One can grab into a new environment the previous code and declare a couple of commands to create annotations.
So, I created the annotatedtikzpicture as:
\NewDocumentEnvironment{annotatedtikzpicture}{O{width=0.9\textwidth} m}{
\begin{tikzpicture}
\node[anchor=south west,inner sep=0] (image) at (0,0)
{\includegraphics[#1]{#2}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
}{
\end{scope}
\end{tikzpicture}
}
For what concern the commands, both are able to immediately draw the border (with its own options) and the annotated text (with its own options); the differences are:
annote is very tricky for the number of arguments;
xannote is more user-friendly by exploiting keys.
Let's see the definitions:
\NewDocumentCommand{\annote}{o r() r() o r() m}{
\draw[#1] (#2) rectangle (#3);
\node[#4] at (#5) {#6};
}
for \annote, while \xannote and its own keys:
\pgfkeys{/tikz/annotated figure/.cd,
border width/.initial=0,
border width/.get=\bwidth,
border width/.store in=\bwidth,
border height/.initial=0,
border height/.get=\bheight,
border height/.store in=\bheight,
start border pos/.initial={(0,0)},
start border pos/.get=\bpos,
start border pos/.store in=\bpos,
start text pos/.initial={(0,0)},
start text pos/.get=\tpos,
start text pos/.store in=\tpos,
border options/.code={
\tikzset{border style/.style={
#1
}
}
},
text options/.code={
\tikzset{text style/.style={
#1
}
}
},
}
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (x) at \bpos {};
\draw[border style] (x) rectangle ($(x)+(\bwidth,\bheight)$);
\node[text style] at \tpos {#2};
}
Notice that it is also possible to simplify the annotation text position thanks to the key annotation centered set to true. But, to do so, the \xannote command has to be changed into:
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (x) at \bpos {};
\draw[border style] (x) rectangle ($(x)+(\bwidth,\bheight)$);
\ifannotationcentered
\node[text style] at ($(x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at \tpos {#2};
\fi
}
To make things complete, the following features are added:
- an
\annoteset command is provided: its functionalities are identical to \tikzsetand here is used in order to collect into a style similar options to be passed to \xannote;
- an
\yannote command, different from the previous \xannote since it does not draw first a rectangle and then a node, but rather is just a node. Definition shown later on;
- a key to compute directly the width of the annotation as asked in How to compute a length and set a text width with pgf; I didn't post the solution there because it's very far from being perfect (actually it won't work under some hypothesis).
So, how it is defined \yannote? It is simply a node in which the minimum height and width are already given:
\NewDocumentCommand{\yannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[option style,
minimum width=\bwidth,
minimum height=\bheight] at \tpos {#2};
}
The option style is identical to the previous border style and text style and it could be customized by means of the key options. An example:
\yannote[start text pos={(0.5,0.075)},
border height=0.7,
border width=0.3,
options={draw=red,
fill=blue!10
},
]{A Comment}
In this case, the start border pos is completely useless.
In order to group into a style similar options, it is possible to exploit \annoteset; defined just as:
\NewDocumentCommand{\annoteset}{m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
}
can be used for example to group:
\annoteset{my annotation/.style={
border options={red,ultra thick,rounded corners},
annotation centered=true,
automatic text width=true,
text options={fill=blue!10,align=center,text width=2.25cm},
}
}
\xannote[start border pos={(0.68,0.01)},
border height=0.77,
border width=0.29,
my annotation
]{Comment one}
\xannote [start border pos={(0.04,0.08)},
border height=0.43,
border width=0.26,
my annotation
]{zone~2 comment~2 multiline}
Notice the key automatic text width: that's responsible for setting the annotation text width. The width computation is actually performed by the following macro:
% based on Alain's answer:
% https://tex.stackexchange.com/a/38500/13304
% percusse's answer:
% https://tex.stackexchange.com/a/58291/13304
% and the \CalcHeight macro implemented in:
% http://www.ctan.org/pkg/smartdiagram
\makeatletter
\def\CalcWidth(#1,#2)#3{%
\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
\pgfmathsetmacro{\mywidth}{veclen(\pgf@x,\pgf@y)-4\pgflinewidth}
\global\expandafter\edef\csname #3\endcsname{\mywidth}
}
\makeatother
within the new definition of \xannote:
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (annote-x) at \bpos {};
\draw[border style] (annote-x) rectangle ($(annote-x)+(\bwidth,\bheight)$)coordinate(annote-y);
\coordinate(annote-z) at (annote-x.west-|annote-y.east);
\ifannotationcentered
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\fi
\else
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at \tpos {#2};
\else
\node[text style] at \tpos {#2};
\fi
\fi
}
Just an hint on the reasoning: we know that the lower left point of the border is called (annote-x) and we know also the top right point of the border, denoted with (annote-y). To compute the width we basically need to calculate the intersection of the previous coordinates to find the lower right point of the border, (annote-z).
Why I said it's not perfect? Well, just use this approach and set a high text width within text options: it will fail.
A complete example:
\documentclass[11pt,a4paper]{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{calc}
\NewDocumentEnvironment{annotatedtikzpicture}{O{width=0.9\textwidth} m}{
\begin{tikzpicture}
\node[anchor=south west,inner sep=0] (image) at (0,0)
{\includegraphics[#1]{#2}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
\draw(image.south east)rectangle(image.north west);
}{
\end{scope}
\end{tikzpicture}
}
% based on Alain's answer:
% https://tex.stackexchange.com/a/38500/13304
% percusse's answer:
% https://tex.stackexchange.com/a/58291/13304
% and the \CalcHeight macro implemented in:
% http://www.ctan.org/pkg/smartdiagram
\makeatletter
\def\CalcWidth(#1,#2)#3{%
\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
\pgfmathsetmacro{\mywidth}{veclen(\pgf@x,\pgf@y)-4\pgflinewidth}
\global\expandafter\edef\csname #3\endcsname{\mywidth}
}
\makeatother
\NewDocumentCommand{\annote}{o r() r() o r() m}{
\draw[#1] (#2) rectangle (#3);
\node[#4] at (#5) {#6};
}
% predefined styles
\tikzset{border style/.style={draw=none},
text style/.style={draw=none},
option style/.style={draw=none},
}
\newif\ifannotationcentered
\newif\ifautomatictextwidth
\pgfkeys{/tikz/annotated figure/.cd,
border width/.initial=0,
border width/.get=\bwidth,
border width/.store in=\bwidth,
border height/.initial=0,
border height/.get=\bheight,
border height/.store in=\bheight,
start border pos/.initial={(0,0)},
start border pos/.get=\bpos,
start border pos/.store in=\bpos,
start text pos/.initial={(0,0)},
start text pos/.get=\tpos,
start text pos/.store in=\tpos,
border options/.code={
\tikzset{border style/.style={
#1
}
}
},
text options/.code={
\tikzset{text style/.style={
#1
}
}
},
options/.code={
\tikzset{option style/.style={
#1
}
}
},
annotation centered/.is if=annotationcentered,
annotation centered=false,
automatic text width/.is if=automatictextwidth,
automatic text width=false,
}
\NewDocumentCommand{\annoteset}{m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
}
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (annote-x) at \bpos {};
\draw[border style] (annote-x) rectangle ($(annote-x)+(\bwidth,\bheight)$)coordinate(annote-y);
\coordinate(annote-z) at (annote-x.west-|annote-y.east);
\ifannotationcentered
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\fi
\else
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at \tpos {#2};
\else
\node[text style] at \tpos {#2};
\fi
\fi
}
\NewDocumentCommand{\yannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[option style,
minimum width=\bwidth,
minimum height=\bheight] at \tpos {#2};
}
% something for shadings, taken from:
% https://tex.stackexchange.com/questions/104528/tikz-shade-also-the-border-of-a-node/104541#104541
\tikzset{
shrink inner sep/.code={
\pgfkeysgetvalue{/pgf/inner xsep}{\currentinnerxsep}
\pgfkeysgetvalue{/pgf/inner ysep}{\currentinnerysep}
\pgfkeyssetvalue{/pgf/inner xsep}{\currentinnerxsep - 0.5\pgflinewidth}
\pgfkeyssetvalue{/pgf/inner ysep}{\currentinnerysep - 0.5\pgflinewidth}
}
}
\tikzset{horizontal shaded border/.style args={#1 and #2}{
append after command={
\pgfextra{%
\begin{pgfinterruptpath}
\path[rounded corners,left color=#1,right color=#2]
($(\tikzlastnode.south west)+(-\pgflinewidth,-\pgflinewidth)$)
rectangle
($(\tikzlastnode.north east)+(\pgflinewidth,\pgflinewidth)$);
\end{pgfinterruptpath}
}
}
},
vertical shaded border/.style args={#1 and #2}{
append after command={
\pgfextra{%
\begin{pgfinterruptpath}
\path[rounded corners,top color=#1,bottom color=#2]
($(\tikzlastnode.south west)+(-\pgflinewidth,-\pgflinewidth)$)
rectangle
($(\tikzlastnode.north east)+(\pgflinewidth,\pgflinewidth)$);
\end{pgfinterruptpath}
}
}
}
}
\begin{document}
\begin{annotatedtikzpicture}[width=0.8\textwidth]{foto}
\yannote[start text pos={(0.5,0.075)},
border height=0.7,
border width=0.3,
options={ultra thick,
rounded corners,
vertical shaded border=green!50 and violet!50!magenta,
top color=green!10,
bottom color=violet!40!magenta!50
},
]{A Comment}
% a style for grouping similar options
\annoteset{my annotation/.style={
border options={red,ultra thick,rounded corners},
annotation centered=true,
automatic text width=true,
text options={fill=blue!10,align=center,text width=2.25cm},
}
}
\xannote[start border pos={(0.68,0.01)},
border height=0.77,
border width=0.29,
my annotation
]{Comment one}
\xannote [start border pos={(0.04,0.08)},
border height=0.43,
border width=0.26,
my annotation
]{zone~2 comment~2 multiline}
\end{annotatedtikzpicture}
\end{document}
It gives:
