% \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-draw.dtx} % %\NeedsTeXFormat{LaTeX2e}[2022-06-01] % %<*driver> \begin{document} \DocInput{\jobname.dtx} \PrintChanges \PrintIndex \end{document} % % \changes{v0.0.0}{2026-05-14}{First draft} % % % \fi % % \iffalse %<*package> %<@@=hexdumptikz_draw> % \fi % % \maketitle % % \begin{abstract} % Draws the hexdump. % For the most part this defines configuration keys regarding the hexdump drawing and provides a callback which then can be passed to the parser. % \end{abstract} % % Identify the package and give the over all version information. % \begin{macrocode} \ProvidesExplPackage {hexdumptikz-draw} {2026-06-20} {1.0.1} {Common functionality for working with hexdumps and (large) addresses} % \end{macrocode} % % Load the requirements. % \begin{macrocode} \RequirePackage { pgfkeys } \RequirePackage { hexdumptikz-common } \RequirePackage { hexdumptikz-selector } \RequirePackage { hexdumptikz-parser } % \end{macrocode} % % \begin{var}{\l_@@_print_selector_ctx_tl} % Initialize / Declare a selector-context which is used for selecting which rows should be shown in the output. % \begin{macrocode} \tl_new:N \l_@@_print_selector_ctx_tl \hexdumptikz_selector_ctx_new:N \l_@@_print_selector_ctx_tl % \end{macrocode} % \end{var} % % \begin{var}{\l_@@_print_styled_ctx_tl} % Initialize / Declare a selector-context which is used for assigning \emph{styles} to the bytes. % \begin{macrocode} \tl_new:N \l_@@_print_styled_ctx_tl \hexdumptikz_selector_ctx_new:N \l_@@_print_styled_ctx_tl % \end{macrocode} % \end{var} % % \subsection{Variables} % % \begin{var}{\g_@@_addr_index_int} % \begin{var}{\g_@@_addr_used_index_int} % \begin{var}{\g_@@_byte_index_int} % Variables for counting the node indices % \begin{macrocode} \int_new:N \g_@@_addr_index_int \int_new:N \g_@@_addr_used_index_int \int_new:N \g_@@_byte_index_int % \end{macrocode} % \end{var} % \end{var} % \end{var} % \begin{var}{\g_@@_last_addr_node_tl} % \begin{var}{\g_@@_last_byte_node_tl} % Store the nodenames of the most recently generated nodes to be able to create \texttt{-end} aliases. % Due to the nesting of tikz-\texttt{scope}s, these variables must be global. % \begin{macrocode} \tl_new:N \g_@@_last_addr_node_tl \tl_new:N \g_@@_last_byte_node_tl % \end{macrocode} % \end{var} % \end{var} % \begin{var}{\l_@@_cur_addr_tl} % \begin{var}{\l_@@_cur_addr_padded_str} % Variables for storing the current (padded) address % \begin{macrocode} \tl_new:N \l_@@_cur_addr_tl \str_new:N \l_@@_cur_addr_padded_str % \end{macrocode} % \end{var} % \end{var} % \begin{var}{\g_@@_first_addr_node_bool} % The first address node gets a special style applied. % This allows e.g. for placing the whole hexdump at a specific position. % \begin{macrocode} \bool_new:N \g_@@_first_addr_node_bool % \end{macrocode} % \end{var} % % \subsection{Constants} % % \begin{var}{\l_@@_tmp_box} % this box has the maximal height and depth a hexadecimal node can have % \begin{macrocode} \box_new:N \l_@@_tmp_box \hbox_set:Nn \l_@@_tmp_box { \ttfamily 0x0123456789abcdef } % \end{macrocode} % \end{var} % \begin{var}{\l_@@_tmp_box} % variables for uniform node heights % \begin{macrocode} \dim_const:Nn \c_@@_hexheight_dim { \box_ht:N \l_@@_tmp_box } \dim_const:Nn \c_@@_hexdepth_dim { \box_dp:N \l_@@_tmp_box } % \end{macrocode} % \end{var} % % % \subsection{Settings / Config Variables} % decisions about address printing % \begin{var}{\l_hexdumptikz_draw_show_addr_padded_bool} % Whether the address which gets shown shall be padded % \begin{macrocode} \bool_new:N \l_hexdumptikz_draw_show_addr_padded_bool % \end{macrocode} % \end{var} % \begin{var}{\l_hexdumptikz_draw_show_addr_bool} % Whether the address shall be shown at all in the output % \begin{macrocode} \bool_new:N \l_hexdumptikz_draw_show_addr_bool % \end{macrocode} % \end{var} % \begin{var}{\l_hexdumptikz_draw_show_addr_base_bool} % Whether the shown address should be prefixed with \texttt{0x} as indicator of the hexadecimal base % \begin{macrocode} \bool_new:N \l_hexdumptikz_draw_show_addr_base_bool % \end{macrocode} % \end{var} % % \begin{var}{\l_hexdumptikz_draw_byte_sep_int} % \begin{var}{\l_hexdumptikz_draw_byte_sep_tl} % insert separating space in the hexdump. Every \texttt{int} columns, insert \texttt{dim} additional space. % \begin{macrocode} \int_new:N \l_hexdumptikz_draw_byte_sep_int \tl_new:N \l_hexdumptikz_draw_byte_sep_tl % \end{macrocode} % \end{var} % \end{var} % % \subsubsection{pgfkeys wiring} % define explicit keys % \begin{macrocode} \pgfkeys { /hexdumptikz, % prefix for the nodenames name~prefix/.code = { \tl_set:Ne \l_hexdumptikz_common_nodename_prefix_tl { #1 } \pgfkeys { /hexdumptikz/next~scope/.append~style = { name~prefix = #1 } } }, name~prefix = { }, name~prefix/.value~required, % insert space after that many bytes bytes~sep~after/.code = { \int_set:Nn \l_hexdumptikz_draw_byte_sep_int { \int_abs:n { #1 } } }, bytes~sep~after = 64, % basically deactivated bytes~sep~after/.default = { 8 }, % size of the gap bytes~sep/.code = { \tl_set:Nn \l_hexdumptikz_draw_byte_sep_tl { #1 } }, bytes~sep = { 2em }, bytes~sep/.value~required, % whether to even show the address nodes % this is a bit more complicated as the address-nodes cannot be simply omitted as they are relevant for the positioning -> inner sep=0, and first byte in each row needs special placement with no 'node distance' show~addr/.code = { \hexdumptikz_common_parse_bool:Nn \l_hexdumptikz_draw_show_addr_bool { #1 } }, show~addr = { true }, show~addr/.default = { true }, % whether to display 0x when showing the addresses show~addr~base/.code = { \hexdumptikz_common_parse_bool:Nn \l_hexdumptikz_draw_show_addr_base_bool { #1 } }, show~addr~base = { true }, show~addr~base/.default = { true }, % whether the shown addresses should be padded (otherwise padding only for ranges, node-names and internals) show~addr~padded/.code = { \hexdumptikz_common_parse_bool:Nn \l_hexdumptikz_draw_show_addr_padded_bool { #1 } }, show~addr~padded = { false }, show~addr~padded/.default = { true }, styled~bytes/.code = { \hexdumptikz_selector_parse:Nn \l_@@_print_styled_ctx_tl { #1 } } } % \end{macrocode} % % Define styles which get applied to the corresponding nodes % \begin{macrocode} \pgfkeys { /hexdumptikz, address~node~first/.style= { }, address~node/.style= { left, on~chain, text~height = \c_@@_hexheight_dim, text~depth = \c_@@_hexdepth_dim, }, byte~node/.style = { on~chain, text~height = \c_@@_hexheight_dim, text~depth = \c_@@_hexdepth_dim, }, } % \end{macrocode} % % \subsection{Actual drawing} % in principle there are the following node-names: % \begin{itemize} % \item one chain going down where every node gets three names: % \begin{enumerate} % \item based on the index in the input % \item based on the index in the output % \item based on the address/offset % \end{enumerate} % \item three chains in each row for every node-name going right where every node just gets the intuitive name % \item every chain comes with an -end node % \end{itemize} % % \subsubsection{Helpers for generating specific nodes} % \begin{fn}{\@@_print_handle_first_node:} % Small helper for handling the \emph{first node} which also modifies the state accordingly. % \begin{macrocode} \prg_new_conditional:Npnn \@@_print_handle_first_node: { TF } { \bool_if:NTF \g_@@_first_addr_node_bool { \bool_gset_false:N \g_@@_first_addr_node_bool \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{fn} % \begin{fn}{\@@_generate_gap_node:n} % Generate an empty node which is intended to be a \emph{gap} node (same placement / category like an address node) % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & number/index of the gap \\ % - & \ain & \texttt{g\_@@\_addr\_index\_int} \\ % - & \ain & \texttt{l\_@@\_cur\_addr\_padded\_str} \\ % - & \aout & \texttt{g\_@@\_last\_addr\_node\_tl} \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_generate_gap_node:n #1 { % \end{macrocode} % Construct the nodename only once so it can be reused later % \begin{macrocode} \tl_gset:Ne \g_@@_last_addr_node_tl { hexdumptikz-in- \int_use:N \g_@@_addr_index_int -gap #1 } % \end{macrocode} % % Draw the node depending on whether it's the first one (in this hexdump) or not % \begin{macrocode} \@@_print_handle_first_node:TF { \node[ /hexdumptikz/address~node, /hexdumptikz/address~node~first, ] ( \tl_use:N \g_@@_last_addr_node_tl ) { } ; } { \node[ /hexdumptikz/address~node, ] ( \tl_use:N \g_@@_last_addr_node_tl ) { } ; } % \end{macrocode} % % Add some node-aliases. This encodes additional information about the node. % \begin{macrocode} \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz- \str_use:N \l_@@_cur_addr_padded_str -gap #1 } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_addr_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-out- \int_use:N \g_@@_addr_used_index_int -gap #1 } % noqa: S103 { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_addr_node_tl } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_generate_addr_node:n} % Generate a node showing the address / offset % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & address which shall be shown \\ % - & \ain & \texttt{g\_@@\_addr\_index\_int} \\ % - & \aout & \texttt{g\_@@\_last\_addr\_node\_tl} \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_generate_addr_node:n #1 { % \end{macrocode} % Construct the nodename only once so it can be reused later % \begin{macrocode} \tl_gset:Ne \g_@@_last_addr_node_tl { hexdumptikz-in- \int_use:N \g_@@_addr_index_int } % \end{macrocode} % % Draw the node depending on whether it's the first one (in this hexdump) or not % \begin{macrocode} \@@_print_handle_first_node:TF { \node[ /hexdumptikz/address~node, /hexdumptikz/address~node~first, ] ( \tl_use:N \g_@@_last_addr_node_tl ) { #1 } ; } { \node[ /hexdumptikz/address~node, ] ( \tl_use:N \g_@@_last_addr_node_tl ) { #1 } ; } } \cs_generate_variant:Nn \@@_generate_addr_node:n { e } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_generate_byte_node:nn} % Generate a node showing a single byte % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & value of the byte \\ % 2 & \ain & style which is to be applied \\ % - & \ain & \texttt{g\_@@\_addr\_index\_int} \\ % - & \ain & \texttt{g\_@@\_byte\_index\_int} \\ % - & \aout & \texttt{g\_@@\_last\_addr\_node\_tl} \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_generate_byte_node:nn #1 #2 { % \end{macrocode} % Construct the nodename only once so it can be reused later % \begin{macrocode} \tl_gset:Ne \g_@@_last_byte_node_tl { hexdumptikz-in- \int_use:N \g_@@_addr_index_int - \int_use:N \g_@@_byte_index_int } % \end{macrocode} % % Draw the node depending on its position: % \begin{itemize} % \item[pos=$0$] needs special positioning % \item[$x \bmod m = 0$] add gap for separation % \item normal node % \end{itemize} % \begin{macrocode} \int_compare:nNnTF { \g_@@_byte_index_int } = { 0 } { \node[right=of~hexdumptikz-in- \int_use:N \g_@@_addr_index_int ,/hexdumptikz/byte~node,#2] % noqa: S103 ( \tl_use:N \g_@@_last_byte_node_tl ) { #1 } ; } { \int_compare:nNnTF { \int_mod:nn { \g_@@_byte_index_int } { \l_hexdumptikz_draw_byte_sep_int } } = { 0 } { \node[ xshift = \tl_use:N \l_hexdumptikz_draw_byte_sep_tl ,/hexdumptikz/byte~node,#2] % noqa: S103 ( \tl_use:N \g_@@_last_byte_node_tl ) { #1 } ; } { \node[/hexdumptikz/byte~node,#2] ( \tl_use:N \g_@@_last_byte_node_tl ) { #1 } ; } } } \cs_generate_variant:Nn \@@_generate_byte_node:nn { ev } % \end{macrocode} % \end{fn} % % \subsubsection{Main drawing functions} % \begin{fn}{\hexdumptikz_draw_init:nn} % Initialize the drawing logic (necessary as it is stateful) % \begin{sideeffects} % \sclobber & \sdir & all kinds of variables, see the source-code\\ % \end{sideeffects} % \begin{args} % 1 & \ain & pgfkeys for the whole hexdump \\ % 2 & \ain & row-selector \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_draw_init:nn #1 #2 { % \end{macrocode} % Clear and initialize the selector contexts % \begin{macrocode} \hexdumptikz_selector_parse:Ne \l_@@_print_selector_ctx_tl { #2 } \hexdumptikz_selector_ctx_clear_iter:N \l_@@_print_selector_ctx_tl \hexdumptikz_selector_ctx_clear_storage:N \l_@@_print_styled_ctx_tl % \end{macrocode} % % Clear the variables with global state. % \begin{macrocode} \bool_gset_true:N \g_@@_first_addr_node_bool \int_gzero:N \g_@@_addr_index_int \int_gzero:N \g_@@_addr_used_index_int % \end{macrocode} % % handle the pgfkeys. % Needs to be after clearing the \texttt{print\_styled} logic. % \begin{macrocode} \pgfkeys { /hexdumptikz, #1 } \hexdumptikz_selector_ctx_clear_iter:N \l_@@_print_styled_ctx_tl % \end{macrocode} % Start the tikz-scope which holds the entire hexdump % \begin{macrocode} \begin{ scope } [ start~chain = hexdumptikz-rows~going~{ below=of~\tikzchainprevious.south~east,anchor=north~east }, % noqa: S103 node~distance = .3ex~and~.2em, font = \ttfamily\small, /hexdumptikz/next~scope, ] } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_draw:NNNN} % Draw-callback which is passed to the parser. % Draws one line / row of the hexdump. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_int} \\ % \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & index of the line (in the input) (int) \\ % 2 & \ain & offset / address (tl) \\ % 3 & \ain & bytes (seq) \\ % 4 & \aout & backchannel for signaling the parser to stop \\ % \end{args} % TODO: \texttt{g\_@@\_addr\_index\_int} is \texttt{\#1} (ok there are differences regarding empty lines) $\to$ simplify % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_draw:NNNN #1 #2 #3 #4 { % \end{macrocode} % Create a working copy of the address, padd it and make a proper string out of it % \begin{macrocode} \tl_set:Ne \l_@@_cur_addr_tl { #2 } \hexdumptikz_common_pad_address:N #2 \str_set:Ne \l_@@_cur_addr_padded_str { \tl_to_str:N #2 } % \end{macrocode} % % Query the user-supplied row selector whether this row should be present in the output % \begin{macrocode} \hexdumptikz_selector_match_rows:NnnN \l_@@_print_selector_ctx_tl { \g_@@_addr_index_int } % y { 0 } % x \l_@@_cur_addr_padded_str % \end{macrocode} % % Only show the row when the selector said so % \begin{macrocode} \bool_if:cT { \hexdumptikz_selector_show_bool:N \l_@@_print_selector_ctx_tl } { % \end{macrocode} % Place the address in a lookup-table which allows translating \texttt{addr$\to$idx}. % This may then later used for annotating the hexdump. % \begin{macrocode} \prop_gput:Nee \g_hexdumptikz_common_cur_offsets_prop { \l_@@_cur_addr_padded_str } { \int_use:N \g_@@_addr_used_index_int } % \end{macrocode} % % The row-selector might have encountered gaps before the current range $\to$ draw them % \begin{macrocode} \int_set_eq:Nc \l_tmpa_int { \hexdumptikz_selector_gap_int:N \l_@@_print_selector_ctx_tl } \int_step_function:nnnN { 1 } { 1 } { \l_tmpa_int } \@@_generate_gap_node:n \int_gzero:c { \hexdumptikz_selector_gap_int:N \l_@@_print_selector_ctx_tl } % \end{macrocode} % % Determine how the address should be presented (padded or unpadded) (avoids having to use more branches later) % \begin{macrocode} \bool_if:NTF \l_hexdumptikz_draw_show_addr_padded_bool { \tl_set:Ne \l_tmpa_tl { \l_@@_cur_addr_padded_str } } { \tl_set:Ne \l_tmpa_tl { \l_@@_cur_addr_tl } } % \end{macrocode} % % Remove the leading \texttt{0x} if the user does not want it to be present in the output % \begin{macrocode} \bool_if:NF \l_hexdumptikz_draw_show_addr_base_bool { \regex_replace_once:NnN \c_hexdumptikz_common_leading_hex_base_regex { } \l_tmpa_tl } \@@_generate_addr_node:e { \l_tmpa_tl } % \end{macrocode} % % Create various node-aliases encoding additional information about the node. % \begin{macrocode} \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz- \str_use:N \l_@@_cur_addr_padded_str } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_addr_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-out- \int_use:N \g_@@_addr_used_index_int } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_addr_node_tl } % \end{macrocode} % % Start a new tikz-\texttt{scope} + chain for the row which started with the address containing the byte values % \begin{macrocode} \begin{ scope } [start~chain=hexdumptikz-row~going~right] \int_zero:N \g_@@_byte_index_int \seq_map_inline:Nn #3 { % \end{macrocode} % % Determine what style to apply to the node % \begin{macrocode} \hexdumptikz_selector_match_styles:NnnN \l_@@_print_styled_ctx_tl { \g_@@_addr_used_index_int } % y { \g_@@_byte_index_int } % x \l_@@_cur_addr_padded_str \@@_generate_byte_node:ev { ##1 } { \hexdumptikz_selector_style_tl:N \l_@@_print_styled_ctx_tl } % \end{macrocode} % % Create various node-aliases encoding additional information about the node. % \begin{macrocode} \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz- \str_use:N \l_@@_cur_addr_padded_str - \int_use:N \g_@@_byte_index_int } % noqa: S103 { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_byte_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-out- \int_use:N \g_@@_addr_used_index_int - \int_use:N \g_@@_byte_index_int } % noqa: S103 { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_byte_node_tl } % \end{macrocode} % % Count the byte nodes for indexing % \begin{macrocode} \int_gincr:N \g_@@_byte_index_int } % \end{macrocode} % % Row finished $\to$ create more node-aliases for easier drawing later % \begin{macrocode} \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-in- \int_use:N \g_@@_addr_index_int -end } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_byte_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-out- \int_use:N \g_@@_addr_used_index_int -end } % noqa: S103 { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_byte_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz- \str_use:N \l_@@_cur_addr_padded_str -end } % noqa: S103 { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \tl_use:N \g_@@_last_byte_node_tl } \end{ scope } % \end{macrocode} % % Count the selected rows for indexing % \begin{macrocode} \int_gincr:N \g_@@_addr_used_index_int } % \end{macrocode} % % Forward the row-selector's decision whether the parser should continue parsing % \begin{macrocode} \bool_set_eq:Nc \l_hexdumptikz_parser_finished_bool { \hexdumptikz_selector_finished_bool:N \l_@@_print_selector_ctx_tl } % \end{macrocode} % % Count the parsed rows for indexing % \begin{macrocode} \int_gincr:N \g_@@_addr_index_int } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_draw_fin:} % Finalize the drawing logic (necessary as it is stateful) % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_draw_fin: { % \end{macrocode} % % Hexdump finished $\to$ create more node-aliases for easier drawing later % \begin{macrocode} \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-end } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \g_@@_last_addr_node_tl } \pgfnodealias { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl hexdumptikz-end-end } { \tl_use:N \l_hexdumptikz_common_nodename_prefix_tl \g_@@_last_byte_node_tl } \end{ scope } \pgfkeys { /hexdumptikz/next~scope/.style = { } } % \end{macrocode} % % Cleanup the created state (additional hexdump might be drawn and the information might leak) % \begin{macrocode} \hexdumptikz_selector_ctx_clear_storage:N \l_@@_print_styled_ctx_tl \hexdumptikz_selector_ctx_clear_storage:N \l_@@_print_selector_ctx_tl } % \end{macrocode} % \end{fn} % % \subsection{Usual entrance point} % \begin{fn}{\hexdumptikz_draw_print:nn} % Usual entry-point handling the complete drawing state logic and the incovation of the selected parser. % \begin{args} % 1 & \ain & pgfkeys for the whole hexdump \\ % 2 & \ain & row-selector (tl) \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_draw_print:nn #1 #2 { % \end{macrocode} % Note, this handling the pgfkeys can still select a different parser. % \begin{macrocode} \hexdumptikz_draw_init:nn { #1 } { #2 } \hexdumptikz_parser_parse:nN { \l_hexdumptikz_common_input_file_str } \hexdumptikz_draw:NNNN \hexdumptikz_draw_fin: } % \end{macrocode} % \end{fn} % \iffalse % % \fi % % \Finale