[Tex/LaTex] How to get element position in LaTeX

boxesoutputpositioning

I have a test with check boxes made in LaTeX for result recognition in queXF. This works fine, but if I change something in source boxes are being relocated and queXF recognition boxes are becoming invalid.

Is it possible to make output of absolute box element position and sizes to external file?

For example, I have the following code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\checkBox}[1]{
    \begin{tikzpicture}[color=black, line width=0.2mm]
        \draw (0,0) rectangle (5mm,5mm);
    \end{tikzpicture}
}
\begin{document}
    Question:\\
    \checkBox{Q1A}~Answer A\\
    \checkBox{Q1B}~Answer B\\
    \checkBox{Q1C}~Answer C\\
    \checkBox{Q1D}~Answer D\\
\end{document}

Output is:

enter image description here

And I want to get information about box positions and sizes (if possible) according to their IDs. Maybe something like this:

\Q1A{3cm}{2cm}{5mm}{5mm}
\Q2B{3cm}{2.5cm}{5mm}{5mm}

Or in any other format that could be parsed.

Any ideas are greatly appreciated.

Best Answer

If you use the remember picture option of tikzpicture the picture origin is written to the auxiliary file (.aux). The used unit is scaled points sp which is 65536sp = 1pt = 1/72.27inch. The origin is IIRC the lower left page corner. You can also save any content in a box register and write its size into the auxiliary file or another one.

Even better is the zref-abspos package which allows you to get the absolute position of any point, also in sp. You need to place a \zsavepos{<name>} at these coordinates and then can get the coordinates after one compilation using \zposx{<name>} and \zposy{<name>}. The manual states that it is only good for relative positions because there is no official reference origin, but in all my tests it was also the lower left page corner.

The following code should demonstrate this:

\documentclass{article}
\usepackage{zref-user}
\usepackage{zref-abspos}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\checkBox}[1]{%
    \begin{tikzpicture}[color=black, line width=0.2mm]
        \draw (0,0)
            node {\zsavepos{#1-ll}}
            rectangle (5mm,5mm)
            node {\zsavepos{#1-ur}};
    \end{tikzpicture}%
    \write\mywrite{#1: \zposx{#1-ll}, \zposy{#1-ll}, \zposx{#1-ur}, \zposy{#1-ur}}%
}
\newwrite\mywrite
\openout\mywrite=\jobname.pos\relax
\begin{document}
    Question:\\
    \checkBox{Q1A}~Answer A\\
    \checkBox{Q1B}~Answer B\\
    \checkBox{Q1C}~Answer C\\
    \checkBox{Q1D}~Answer D\\
\end{document}

It writes a .pos file which holds the positions like this:

Q1A: 9017948, 41890363, 9950287, 42822702
Q1B: 9017948, 40455628, 9950287, 41387967
Q1C: 9017948, 39020893, 9950287, 39953232
Q1D: 9017948, 37586158, 9950287, 38518497

The format can also be changed to get the values in pt or another unit, or the origin plus size instead of the absolute position of the lower left and upper right corner like it is now.

For mm output replace the \write content with:

\write\mywrite{#1: (%
    \dimtomm{\zposx{#1-ll}sp},
    \dimtomm{\zposy{#1-ll}sp}) + (%
    \dimtomm{\zposx{#1-ur}sp-\zposx{#1-ll}sp},
    \dimtomm{\zposy{#1-ur}sp-\zposy{#1-ll}sp})%
}%

and add the following macro:

\makeatletter
\newcommand\dimtomm[1]{%
    \strip@pt\dimexpr 0.351459804\dimexpr#1\relax\relax mm%
}
\makeatother

Then you get:

Q1A: (48.36134mm, 224.64914mm) + (4.99992mm, 4.99992mm)
Q1B: (48.36134mm, 216.95496mm) + (4.99992mm, 4.99992mm)
Q1C: (48.36134mm, 209.26077mm) + (4.99992mm, 4.99992mm)
Q1D: (48.36134mm, 201.5666mm) + (4.99992mm, 4.99992mm)

If you mind the shown precision and like to round the numbers instead have a look at \pgfmathprintnumber of PGF/TikZ. However, if you parse these files anyway you can do it then as well.


If you are using DVI mode (i.e. latex not pdflatex), the tikzpicture is created using PostScript code executed by dvips after the TeX processing. In this case both nodes are not yet placed and get the same coordinates which should be the origin of the picture. Instead I would only save one point and measure the size of the picture to get the other one. Note that I added some PGF/TiKZ code to make sure that \zsavepos doesn't influence the size of the picture.

\documentclass[a4paper]{article}
\usepackage{zref-user}
\usepackage{zref-abspos}
\usepackage{tikz}
\usetikzlibrary{calc}
\newsavebox\mybox
\newcommand{\checkBox}[1]{%
    \begingroup
    \sbox\mybox{%
        \begin{tikzpicture}[color=black, line width=0.2mm]
            % Pace the save pos "whatsit" without influencing the bounding box / size of the picture
            \begin{pgfinterruptboundingbox}
                \node at (0,0) {\zsavepos{#1-ll}};
            \end{pgfinterruptboundingbox}
            \draw (0,0) rectangle (5mm,5mm);
        \end{tikzpicture}%
    }%
    \usebox\mybox
    \immediate\write\mywrite{#1: (%
        \dimtomm{\zposx{#1-ll}sp},
        \dimtomm{\zposy{#1-ll}sp}) + (%
        \dimtomm{\wd\mybox},
        \dimtomm{\ht\mybox+\dp\mybox})%
    }%
    \endgroup
}
\makeatletter
\newcommand\dimtomm[1]{%
    \strip@pt\dimexpr 0.351459804\dimexpr#1\relax\relax mm%
}
\makeatother
\newwrite\mywrite
\immediate\openout\mywrite=\jobname.pos\relax
\begin{document}
    Question:\\
    \checkBox{Q1A}~Answer A\\
    \checkBox{Q1B}~Answer B\\
    \checkBox{Q1C}~Answer C\\
    \checkBox{Q1D}~Answer D\\
\end{document}