[Tex/LaTex] How to add barely visible tracking dots to a b/w document


I would like to add barely visible tracking dots in the style of these micro tracking dots to my b/w document. The tracking dots mentioned in the above look like this:

tracking dots as per (c) EFF, OP link see above

The tracking dots are aligned in a 15×8 matrix and, in the above case, include printer information, infos about the time the document was printed etc.
I specifically do not want to copy the mechanism of encoding timestamps, but rather uniquely be able to identify printed pages (and, as you might have guessed, copies thereof).

I assure you to use this wisely, rarely and make ample notice of using the mechanism on my document.

If anyone has a more pratical, but still rather invisible, way of adding this type of tracking to a document I'd be quite happy to hear from you. Sadly, just adding a watermark in the background (also thanks to the good people of stackexchange 😉 has not solved the issue of tracking single documents for me.

Best Answer

Here are a couple of options:

Method 1: Dot Grid, encoded "christian"

enter image description here

\setgrid[opt arg][opt arg]{letters}


The first optional argument sets the size of a grid cell, default is 10 pt (units aren't entered). The second optional argument determines dot size. The dots are periods scaled via. \scalebox the second argument gives the scale factor, default is 0.3. Each letter in the given word is converted first to a position in the alphabet (a=1 etc.) and then that alphabetic position is converted to a 5 digit binary number. Each such binary numbers is set as a column of vertical dots: 0 = no dot, 1 = dot. Columns are read from top to bottom (the first column is always filled with dots for reference). Thus, in the picture above:

c->3rd letter->00011-> no dot, no dot, no dot, dot, dot.

The \usegrid macro then sets the grid. In the example I put it in the left header. You can put it wherever.


\prop_new:N \g__docmark_alph_num_prop
\seq_new:N \l__docmark_name_bin_seq
\tl_new:N \l__docmark_box_list_tl
\box_new:N \c__dot_box
\box_new:N \c__grid_box

% key = val: key -> letter, val -> binary rep of letter pos in alphabet
\int_step_inline:nnnn {1}{1}{26}
        \tl_set:Nx \l_tmpa_tl {\int_to_binary:n {#1}}
        \int_while_do:nn {\tl_count:N \l_tmpa_tl < 5}
                \tl_put_left:Nn \l_tmpa_tl 0
        \tl_set:Nx \l_tmpb_tl {\int_to_alph:n {#1}}
        \prop_put:NVV \g__docmark_alph_num_prop \l_tmpb_tl \l_tmpa_tl

\NewDocumentCommand{\setgrid}{ O{10} O{.5} m }
        \docmark_name_to_grid:nnn {#1}{#2}{#3}

\cs_generate_variant:Nn \hbox_gset:Nn {NV}
\cs_new:Npn \docmark_name_to_grid:nnn #1#2#3
        % scaled period, aka dot
        \hbox_gset:Nn \c__dot_box {\hbox_to_zero:n {\scalebox{#2}{.}}}

        % map letters of mandatory arg to 5 digit binaries
        \tl_map_inline:nn {#3}
                \seq_put_right:Nx \l__docmark_name_bin_seq {\prop_get:Nn \g__docmark_alph_num_prop {##1}}

        % "bookend" for reference
        \seq_put_left:Nn \l__docmark_name_bin_seq {11111}

        % for each 5 digit binary rep
        \seq_map_variable:NNn \l__docmark_name_bin_seq \l_tmpa_tl
                \int_step_inline:nnnn {1}{1}{5}
                        % for each of the 5 digits in the rep
                        \tl_set:Nx \l_tmpb_tl {\tl_item:Nn \l_tmpa_tl {##1}}

                        % if zero, no dot
                        \tl_if_in:NnF \l_tmpb_tl {0}
                                % if not zero then dot, left/right in list --> up/down in dots
                                \fp_set:Nn \l_tmpa_fp {-(##1-1)*(\tl_item:Nn \l_tmpa_tl {##1})*(#1)}
                                \tl_put_right:Nx \l__docmark_box_list_tl {\box_move_up:nn {\fp_to_dim:N \l_tmpa_fp}{\box_use:N \c__dot_box}}

                % move to next column of dots
                \tl_put_right:Nn \l__docmark_box_list_tl {\hspace{#1pt}}
        % box the dots
        \hbox_gset:NV \c__grid_box \l__docmark_box_list_tl

        \box_use:N \c__grid_box




Hi! I am a lovely sentence.


Method 2: Dots in text, encoded "abba"

enter image description here

This one gives:


The first argument again sets dot size, and the second is letters. The letters a1,...,aj are converted to integers n1..nj as above. In the ith line of text a dot will be placed beneath the nith glyph. The letters can be recovered by counting glyphs (letters, punctuation, etc) from the left until you reach a dot, and then using the corresponding letter of the alphabet. This will occur on every page. Compile with lualatex. Code borrowed liberally from here: https://tex.stackexchange.com/a/58327/14100

local GLYPH_ID = node.id("glyph")

-- adapted from https://tex.stackexchange.com/a/58327/14100
local number_sp_in_a_pdf_point = 65782

function math.round(num)
    return math.floor(num * 1000 + 0.5) / 1000

-- char width, whatsit, char pos, line number
local wd,w,pos,line

-- head is a linked list (next/prev entries pointing to the next node)
function mark_chars(head)
    while head do
      if head.id == 0 then
        -- an hbox
            pos = 0
            line = line + 1
      elseif head.id == GLYPH_ID then
            pos = pos + 1
            if pos == t[line] then
            -- if (character position) = (number of current character in name) 
        w = node.new("whatsit","pdf_literal")
        wd = math.round(head.width  / number_sp_in_a_pdf_point)
                -- cf. http://www.tug.org/TUGboat/tb32-2/tb101isambert.pdf
        w.data = string.format("q 0.2 G %g w %g -2.8 m %g %g l s Q",p,-wd/2,-wd/2,-2.8+p)
        -- insert this new node after the current glyph and move pointer (head) to
        -- the new whatsit node
        w.next = head.next
        w.prev = head
        head.next = w
        head = w
      end -- if
      head = head.next
  end -- while
  return true
end -- function

char_tab = {a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11, l=12, m=13, n=14, o=15, p=16, q=17, r=18, s=19, t=20, u=21, v=22, w=23, x=24, y=25, z=26}

t = {}
function encode(name)
    -- step through name
    for i = 1, #name do
            -- read each char
        local s = string.sub(name, i, i)
            -- store its numeric equiv in table

function mark_page(head)
    line = 0
    return head
    \directlua{luatexbase.add_to_callback('pre_output_filter', mark_page, "mark_page")}%
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tincidunt condimentum quam, quis dignissim felis adipiscing rhoncus. Mauris quis imperdiet ligula. Maecenas quis dolor vel nunc egestas consectetur. Donec porttitor dictum tincidunt. Nunc orci ipsum, mattis vitae lobortis ut, porta eget mi. Quisque adipiscing, lacus quis molestie fermentum, nunc odio consectetur arcu, nec luctus dolor nisi quis erat. Aliquam sit amet venenatis est. Sed ut venenatis elit. Maecenas lacus leo, aliquam nec fringilla eu, lobortis quis augue.

Mauris vestibulum, augue non convallis vestibulum, ante enim aliquam eros, sit amet sollicitudin sapien ante ac diam. Quisque mi purus, vehicula eu feugiat a, blandit facilisis est. Etiam sit amet dui eget purus ultricies volutpat. Integer eu enim leo, eu faucibus neque. Donec ut sollicitudin nunc. Etiam tincidunt justo eu ante consectetur consequat. Suspendisse ultricies faucibus odio. Donec felis risus, lobortis eu pellentesque quis, imperdiet non augue. Sed massa dolor, ultrices at ultricies in, dapibus a ante. Sed sit amet augue felis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.