% \iffalse % vim: set expandtab: % vim: set shiftwidth=2: % vim: set tabstop=2: % \fi % \iffalse meta-comment % % Copyright (C) 2026 by Lukas Heindl % --------------------------------------------------------------------------- % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3c or later is part of all distributions of LaTeX % version 2008/05/04 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Lukas Heindl. % % This work consists of all files listed in manifest.txt. % % \fi % % \iffalse %<*driver> \ProvidesFile{hexdumptikz-annotate.dtx} % %\NeedsTeXFormat{LaTeX2e}[2022-06-01] % %<*driver> \begin{document} \DocInput{\jobname.dtx} \PrintChanges \PrintIndex \end{document} % % \changes{v0.0.0}{2026-05-13}{First draft} % % % \fi % % \iffalse %<*package> %<@@=hexdumptikz_annotate> % \fi % % \maketitle % % \begin{abstract} % Functions for annotating a hexdump after it has been drawn. % \end{abstract} % % Identify the package and give the over all version information. % \begin{macrocode} \ProvidesExplPackage {hexdumptikz-annotate} {2026-06-20} {1.0.1} {Annotate hexdumps} % \end{macrocode} % % Load the internal libraries which are used % \begin{macrocode} \RequirePackage { hexdumptikz-addrcalc } % \end{macrocode} % % \begin{var}{\l_@@_start_row_tl} % \begin{var}{\l_@@_start_col_tl} % \begin{var}{\l_@@_end_row_tl} % \begin{var}{\l_@@_end_col_tl} % variables used when labeling a hexdump % \begin{macrocode} \tl_new:N \l_@@_start_row_tl \tl_new:N \l_@@_start_col_tl \tl_new:N \l_@@_end_row_tl \tl_new:N \l_@@_end_col_tl % \end{macrocode} % \end{var} % \end{var} % \end{var} % \end{var} % % \begin{var}{\l_@@_start_row_int} % \begin{var}{\l_@@_end_row_int} % \begin{var}{\l_@@_middle_n_row_tl} % \begin{var}{\l_@@_middle_s_row_tl} % variables used for drawing multi-line spanning annotations % \begin{macrocode} \int_new:N \l_@@_start_row_int \int_new:N \l_@@_end_row_int \tl_new:N \l_@@_middle_n_row_tl \tl_new:N \l_@@_middle_s_row_tl % \end{macrocode} % \end{var} % \end{var} % \end{var} % \end{var} % % \begin{var}{\l_@@_tmpa_tl} % Variable to avoid clobbering the usual scratch variables. % \begin{macrocode} \tl_new:N \l_@@_tmpa_tl % \end{macrocode} % \end{var} % % \begin{fn}{\l_@@_offset_lookup:nnN} % When drawing the hexdump, it stored a mapping from offset to (y-)index. % This function allows a lookup in this mapping allowing to \enquote{calculate} with the offset as it becomes an index. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_int} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & key (base-address / y-component) \\ % 2 & \ain & some context which is printed with the error \\ % 3 & \aout & int variable to store the (y-)index in \\ % - & \ain & \texttt{g\_hexdumptikz\_common\_cur\_offsets\_prop} \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_offset_lookup:nnN #1 #2 #3 { \prop_get:NnNTF \g_hexdumptikz_common_cur_offsets_prop { #1 } \l_@@_tmpa_tl { \int_set:Nn #3 { \l_@@_tmpa_tl } } { \msg_critical:nnnn { hexdumptikz } { unknown-row } { #1 } { #2 } } } \cs_generate_variant:Nn \@@_offset_lookup:nnN { enN } % \end{macrocode} % \end{fn} % % \begin{fn}{\l_@@_label_bytes_init:nnn} % Some common functionalities which are required by multiple annotation functions. % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & pgfkeys for the line \\ % 2 & \ain & start-address \\ % 3 & \ain & end-address \\ % - & \aout & \texttt{l\_@@\_start\_row\_tl} \\ % - & \aout & \texttt{l\_@@\_start\_col\_tl} \\ % - & \aout & \texttt{l\_@@\_end\_row\_tl} \\ % - & \aout & \texttt{l\_@@\_end\_col\_tl} \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_label_bytes_init:nnn #1 #2 #3 { \pgfkeys { /hexdumptikz, #1 } \hexdumptikz_address_to_nodename_components:nNN { #2 } \l_@@_start_row_tl \l_@@_start_col_tl \hexdumptikz_address_to_nodename_components:nNN { #3 } \l_@@_end_row_tl \l_@@_end_col_tl } % \end{macrocode} % \end{fn} % % \begin{fn}{\l_@@_label_bytes:nnn} % Enclose a sequence of bytes with a line. % % This does not list sideeffects as there are many variables affected by this. % On the other hand, the code is enclosed by a fresh group, so changes are local anyhow. % % \begin{args} % 1 & \ain & pgfkeys for the line \\ % 2 & \ain & start-address \\ % 3 & \ain & end-address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_label_bytes:nnn #1 #2 #3 { % \end{macrocode} % grouping needed to avoid pgfkeys leaking options % \begin{macrocode} \group_begin: % \end{macrocode} % % common code across multiple annotation functionalities % \begin{macrocode} \@@_label_bytes_init:nnn { #1 } { #2 } { #3 } % \end{macrocode} % % check if the annotation spans multiple rows/lines $\to$ maybe the drawing can be simplified % \begin{macrocode} \str_if_eq:VVTF \l_@@_start_row_tl \l_@@_end_row_tl { \begin{ scope }[/hexdumptikz/next~scope] \draw (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl .north~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl .south~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103 cycle ; \end{ scope } } { % \end{macrocode} % the annotation spans multiple rows/lines $\to$ obtain the y-indices so calculations are possible % \begin{macrocode} \@@_offset_lookup:enN { 0x \l_@@_start_row_tl } { start } \l_@@_start_row_int \@@_offset_lookup:enN { 0x \l_@@_end_row_tl } { end } \l_@@_end_row_int % \end{macrocode} % % check if there is a \enquote{true} middle row. Else use the start-/end-row as replacement % % Note two middle rows are important: % \begin{enumerate} % \item the topmost middle row \texttt{middle\_n\_row} (\emph{north}) -- the row to which a vertical line from the \emph{end-row} extends to % \item the bottommost middle row \texttt{middle\_s\_row} (\emph{south}) -- the row to which a vertical line from the \emph{begin-row} extends to % \end{enumerate} % % \begin{macrocode} \int_compare:nNnTF { \l_@@_start_row_int + 1 } = { \l_@@_end_row_int } { \tl_set:Nn \l_@@_middle_n_row_tl { \int_to_arabic:n { \l_@@_end_row_int } } \tl_set:Nn \l_@@_middle_s_row_tl { \int_to_arabic:n { \l_@@_start_row_int } } } { \tl_set:Nn \l_@@_middle_n_row_tl { \int_to_arabic:n { \l_@@_start_row_int + 1 } } \tl_set:Nn \l_@@_middle_s_row_tl { \int_to_arabic:n { \l_@@_end_row_int - 1 } } } % \end{macrocode} % % Do the actual drawing (starting at north west, going clockwise) % \begin{macrocode} \begin{ scope }[/hexdumptikz/next~scope] \draw % \end{macrocode} % top left corner to top right corner of the first line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (0,0) -- (1,0); % \node[fill=red,circle] at (0,0) {1}; % \node[fill=red,circle] at (1,0) {2}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl -end .north~east) -- % noqa: S103 % \end{macrocode} % bottom right corner of the lowest fully captured line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (1,-1) -- (0.5,-1); % \node[fill=red,circle] at (1,-1) {3}; % \node[fill=red,circle] at (0.5,-1) {4}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz-out- \tl_use:N \l_@@_middle_s_row_tl -end .south~east) -- % noqa: S103 (hexdumptikz-out- \tl_use:N \l_@@_middle_s_row_tl - \tl_use:N \l_@@_end_col_tl .south~east) -- % noqa: S103 % \end{macrocode} % bottom right to bottom left corner of the last line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (0.5,-1.5) -- (-1,-1.5); % \node[fill=red,circle] at (0.5,-1.5) {5}; % \node[fill=red,circle] at (-1,-1.5) {6}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl .south~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - 0 .south~west) -- % noqa: S103 % \end{macrocode} % top left corner of the highest fully captured line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (-1,-0.5) -- (0,-0.5); % \node[fill=red,circle] at (-1,-0.5) {7}; % \node[fill=red,circle] at (0,-0.5) {8}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz-out- \tl_use:N \l_@@_middle_n_row_tl - 0 .north~west) -- % noqa: S103 (hexdumptikz-out- \tl_use:N \l_@@_middle_n_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103 cycle ; \end{ scope } } \group_end: } % \end{macrocode} % \end{fn} % % \begin{fn}{\l_@@_label_bytes_fallback:nnn} % Enclose a sequence of bytes with a line -- fallback which also works without the lookup. % Thus, this function can also be used once the mapping which was created by printing the hexdump is lost. % Though, the output is not as nice as with that mapping available. % % This does not list sideeffects as there are many variables affected by this. % On the other hand, the code is enclosed by a fresh group, so changes are local anyhow. % % \begin{args} % 1 & \ain & pgfkeys for the line \\ % 2 & \ain & start-address \\ % 3 & \ain & end-address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_label_bytes_fallback:nnn #1 #2 #3 { % \end{macrocode} % grouping needed to avoid pgfkeys leaking options % \begin{macrocode} \group_begin: \@@_label_bytes_init:nnn { #1 } { #2 } { #3 } % \end{macrocode} % % check if the annotation spans multiple rows/lines $\to$ maybe the drawing can be simplified % \begin{macrocode} \str_if_eq:VVTF \l_@@_start_row_tl \l_@@_end_row_tl { \begin{ scope }[/hexdumptikz/next~scope] \draw (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl .north~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_end_col_tl .south~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103 cycle ; \end{ scope } } { % \end{macrocode} % this procedure does not rely on being able to translate addresses to row-indices % Thus, this can serve as fallback if the prop required for the lookup is not available anymore % Though, the spacing of the drawn border is not as nice as with the correct handling of the middle line % draws the line beginning at the top left clockwise % \begin{macrocode} \begin{ scope }[/hexdumptikz/next~scope] \draw % \end{macrocode} % top left corner to top right corner of the first line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (0,0) -- (1,0); % \node[fill=red,circle] at (0,0) {1}; % \node[fill=red,circle] at (1,0) {2}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .north~west) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl -end .north~east) -- % noqa: S103 % \end{macrocode} % bottom right corner of the lowest fully captured line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (1,-1) -- (0.5,-1); % \node[fill=red,circle] at (1,-1) {3}; % \node[fill=red,circle] at (0.5,-1) {4}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl -end.north~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl .north~east) -- % noqa: S103 % \end{macrocode} % bottom right to bottom left corner of the last line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (0.5,-1.5) -- (-1,-1.5); % \node[fill=red,circle] at (0.5,-1.5) {5}; % \node[fill=red,circle] at (-1,-1.5) {6}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - \tl_use:N \l_@@_end_col_tl .south~east) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_end_row_tl - 0 .south~west) -- % noqa: S103 % \end{macrocode} % top left corner of the highest fully captured line % \begin{tikzpicture}[yscale=0.5,font=\sffamily\scriptsize,inner sep=0.5pt] % \draw (0,0) -- (1,0) -- (1,-1) -- (0.5,-1) -- (0.5,-1.5) -- (-1,-1.5) -- (-1,-0.5) -- (0,-0.5) -- cycle; % \draw[red] (-1,-0.5) -- (0,-0.5); % \node[fill=red,circle] at (-1,-0.5) {7}; % \node[fill=red,circle] at (0,-0.5) {8}; % \end{tikzpicture} % \begin{macrocode} (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - 0 .south~west) -- % noqa: S103 (hexdumptikz- 0x \tl_use:N \l_@@_start_row_tl - \tl_use:N \l_@@_start_col_tl .south~west) -- % noqa: S103 cycle ; \end{ scope } } \group_end: } % \end{macrocode} % \end{fn} % \iffalse % % \fi % % \Finale