# Void Pantograph / microdots

I would like to have a PDF file which I can embed in the foreground of my PDF files which, in turn, will mark analog copies with a visible watermark. Apparently these are called Void Pantographs and some printers can even put them on any page printed - mine can't.

For that, it seems to be the way to go to have two micro-dotted halftone images in which the underlying one has a different frequency then the one that renders the text.

Is there a way, for starters, to generate a postscript file that contains micro dots of a certain size and in a certain distance? I would then import one of them into a vector editor, mask my text and overlay it onto another copy of different halftone frequency, merge the two and add that on top of my to-be-protexted page. Wheew.

In other words, is there a way to LaTeX-programmatically generate a Void Pantograph with adjustable dot-size, dot color and frequency without the above?

Here is a solution with TikZ.

Edit: I added two additional solutions below. Both have the advantage, that they can cover the whole page without memory problems.

Edit 2: added example of usage at the end (on request of lAtExFaN).

Step 1: draw the dots, the straight forward approach.

\foreach \x in {0,...,50}{%
\typeout{line \x}% showing progress
\foreach \y in {0,...,50}{%
\draw (\x*1mm,\y*1mm) circle (0.1mm);
}
}


Here 1mm is the dot distance and 0.1mm is the dot size.

This puts a huge burden on TeXs main memory (100 x 100 ended with a TeX memory exceeded error here) and it will take quite long to compile. It can be optimized by drawing short lines instead of circles. For micro dots this shouldn't matter.

\foreach \x in {0,...,100}{%
\typeout{line \x}% showing progress
\foreach \y in {0,...,100}{%
\draw[line width=0.1m]
(\x*1mm,\y*1mm-0.5*0.1mm) -- (\x*1mm,\y*1mm+0.5*0.1mm);
}
}


The burden on the memory is still huge (but 100 x 100 worked here). So it's probably not possible to cover a whole page, unless TeXs memory is enlarged. But this method has the advantage that the dots can be randomized.

In the picture the red and blue square are just for testing, that stuff in the background doesn't get covered up.

Step 2: randomize the dots.

\foreach \x in {0,...,100}{%
\typeout{line \x}% showing progress
\foreach \y in {0,...,100}{%
\pgfmathparse{rand}
\pgfmathsetmacro{\drandx}{\pgfmathresult}
\pgfmathparse{rand}
\pgfmathsetmacro{\drandy}{\pgfmathresult}
\pgfmathsetlength{\dotx}{(\x+0.25*\drandx)*1mm}
\pgfmathsetlength{\doty}{(\y+0.25*\drandy)*1mm}
\draw[line width=0.1mm] (\dotx,\doty-0.5*0.1mm) -- (\dotx,\doty+0.5*0.1mm);
}
}


Off course, this takes a bit longer to compile.

Making it really fast:

If no randomization is required, drawing the dots can be made much faster by using dash patterns. This also uses only about a tenth of the memory of the previous approach.

\draw[dash pattern=on 0.1mm off 0.9mm,dash phase=0.5*1mm,line width=0.1mm]
foreach \y in {0,...,100}{ (0,\y*1mm) -- (100mm,\y*1mm) };


Here the lengths for on and off should add up to the desired dot distance.

A problem here was, that for a small dot distance, my ancient Acrobat Reader showed lines instead of dots.

Step 3: stamp out the text.

For this, transparency group=knockout is used. This may not show up in some PDF viewers (but Acrobat Reader works). And for printing it depends of course on the used printer / printer driver.

\begin{scope}[transparency group=knockout]
\draw[dash pattern=on 0.1mm off 0.9mm,dash phase=0.5*1mm,line width=0.1mm]
foreach \y in {0,...,100}{ (0,\y*1mm) -- (100mm,\y*1mm) };
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}


Step 4: text made out of dots.

This used the fadings library (with a little help from this answer).

\begin{tikzfadingfrompicture}[name=A]
\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
\end{tikzfadingfrompicture}% Edit: get rid of this space
\begin{tikzpicture}
foreach \y in {-50,...,50}{ (-50mm,\y*1mm) -- (50mm,\y*1mm) };
\end{tikzpicture}


Note that here \y is counted from -50 to 50, because the fading picture is centered to the origin. Since the latter is a separate picture, this has to be done to ensure proper positioning.

Step 5: putting it al together.

\begin{tikzfadingfrompicture}[name=A]
\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
\end{tikzfadingfrompicture}% Edit: get rid of this space
\begin{tikzpicture}
\begin{scope}[transparency group=knockout]
\draw[dash pattern=on 0.1mm off 0.9mm,dash phase=0.5*1mm,line width=0.1mm]
foreach \y in {-50,...,50}{ (-50,\y*1mm) -- (50mm,\y*1mm) };
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}
foreach \y in {-35,...,35}{ (-35mm,\y*1mm) -- (35mm,\y*1mm) };
\end{tikzpicture}


For viewers that can't show transparency group=knockout, the dots for the background will show up between the dots for the text, if randomization is used.

In order to save memory and compilation time, it is recommended to make the second dot raster as small as possible, so it just covers the text.

Finally: lets create a few macros with some keys.

\dotpattern draws the dot raster, the slow way.

\dotfast also draws the dot raster, but the fast way.

\dotrandom draws a randomized dot raster.

\dotfastfade, and \dotrandomfade the same as above, but to be used for the text. There is no \dotpatternfade, because \dotfastfade does the same, just much faster.

These macros can take an optional argument with the following keys

dot color to set the color of the dots (e.g. dot color=red, default: black).

dot distance to set the distance between the dots, must be a length (e.g. dot distance=2mm, default: 1mm).

dot size to set the size of the dots, again, must be a length (e.g. dot size=0.2mm, default: 0.1mm).

dot number x to set the number of dots in x-direction (e.g. dot number x=50', default: 100). The horizontal size of the area covered is determined bydot distancetimesdot number x.

dot number y to set the number of dots in y-direction (e.g. dot number y=50', default: 100). The vertical size of the area covered is determined bydot distancetimesdot number y.

dot random shift ratio sets maximum shift of a dot from its nominal position in x- and y-direction as a fraction of the dot distance (only for \dotrandom and \dotrandomfade, e.g. dot random shift ratio=0.3, default: 0.25).

And here is a complete example:

\documentclass[10pt,a4paper]{article}
\usepackage{tikz}

\newdimen\dotdist
\newdimen\dotsize
\newdimen\dotx
\newdimen\doty

% defaults
\dotdist=1mm
\dotsize=0.1mm
\newcommand*{\dotstartx}{-50}
\newcommand*{\dotstarty}{-50}
\newcommand*{\dotendx}{50}
\newcommand*{\dotendy}{50}
\newcommand*{\dotshiftratio}{0.25}
\newcommand*{\dotcolor}{black}

\tikzset{%
dot distance/.code={\dotdist=#1},
dot distance/.value required,
dot size/.code={\dotsize=#1},
dot size/.value required,
dot number x/.code={\pgfmathparse{int(#1/2)}\edef\dotstartx{-\pgfmathresult}\edef\dotendx{\pgfmathresult}},
dot number x/.value required,
dot number y/.code={\pgfmathparse{int(#1/2)}\edef\dotstarty{-\pgfmathresult}\edef\dotendy{\pgfmathresult}},
dot number y/.value required,
dot random shift ratio/.code={\def\dotshiftratio{#1}},
dot random shift ratio/.value required,
dot color/.code={\def\dotcolor{#1}}
}

\newcommand*{\dotfast}[1][]{%
\tikzset{#1}
\draw[draw=\dotcolor,dash pattern=on \dotsize off \dimexpr\dotdist-\dotsize,dash phase=0.5\dotsize,line width=\dotsize]
foreach \y in {\dotstarty,...,\dotendy}{ (\dotstartx*\dotdist,\y*\dotdist) -- (\dotendx*\dotdist,\y*\dotdist) };
}

\tikzset{#1}
dash pattern=on \dotsize off \dimexpr\dotdist-\dotsize,dash phase=0.5\dotsize,line width=\dotsize]
foreach \y in {\dotstarty,...,\dotendy}{ (\dotstartx*\dotdist,\y*\dotdist) -- (\dotendx*\dotdist,\y*\dotdist) };
}

\newcommand*{\dotpattern}[1][]{%
\tikzset{#1}
\foreach \x in {\dotstartx,...,\dotendx}{%
\typeout{line \x}% showing progress
\foreach \y in {\dotstarty,...,\dotendy}{%
\draw[draw=\dotcolor,line width=\dotsize]
(\x*\dotdist,\y*\dotdist-0.5*\dotsize) -- (\x*\dotdist,\y*\dotdist+0.5*\dotsize);
}
}
}

\newcommand{\dotrandom}[1][]{%
\tikzset{#1}
\foreach \x in {\dotstartx,...,\dotendx}{%
\typeout{line \x}% showing progress
\foreach \y in {\dotstarty,...,\dotendy}{%
\pgfmathparse{rand}
\pgfmathsetmacro{\drandx}{\pgfmathresult}
\pgfmathparse{rand}
\pgfmathsetmacro{\drandy}{\pgfmathresult}
\pgfmathsetlength{\dotx}{(\x+\dotshiftratio*\drandx)*\dotdist}
\pgfmathsetlength{\doty}{(\y+\dotshiftratio*\drandy)*\dotdist}
\draw[draw=\dotcolor,line width=\dotsize] (\dotx,\doty-0.5*\dotsize) -- (\dotx,\doty+0.5*\dotsize);
}
}
}

\tikzset{#1}
\foreach \x in {\dotstartx,...,\dotendx}{%
\typeout{line \x}% showing progress
\foreach \y in {\dotstarty,...,\dotendy}{%
\pgfmathparse{rand}
\pgfmathsetmacro{\drandx}{\pgfmathresult}
\pgfmathparse{rand}
\pgfmathsetmacro{\drandy}{\pgfmathresult}
\pgfmathsetlength{\dotx}{(\x+\dotshiftratio*\drandx)*\dotdist}
\pgfmathsetlength{\doty}{(\y+\dotshiftratio*\drandy)*\dotdist}
(\dotx,\doty-0.5*\dotsize) -- (\dotx,\doty+0.5*\dotsize);
}
}
}

\begin{document}
\section*{Step 1:}
creating the dots, straight forward approach, but takes long and needs a lot of memory

\vspace{1cm}
\noindent
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\dotpattern[dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\end{tikzpicture}

\newpage
\section*{Step 2:}
randomize the dots a little, takes a bit longer

\vspace{1cm}
\noindent
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\dotrandom[dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\end{tikzpicture}

\newpage
\section*{Fast dots:}
use line patterns to create dots, fast, nearly no memory, but can't be randomized

\vspace{1cm}
\noindent
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\dotfast[dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\end{tikzpicture}

\newpage
\section*{Step 3:}
stamp out the text, will not show with all viewers (Acrobat Reader works)

\vspace{1cm}
\noindent
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\begin{scope}[transparency group=knockout]
\dotfast[dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}
\end{tikzpicture}

\newpage
\section*{Step 4:}

\vspace{1cm}
\noindent
\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
\end{tikzfadingfrompicture}% Edit: get rid of this space
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\dotfastfade[dot distance=2mm,dot size=0.4mm,dot number x=50,dot number y=50]
\end{tikzpicture}

\newpage
\section*{Step 5:}
putting it all together

\vspace{1cm}
\noindent
% Edit: this is only needed once in the document
%\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
%\end{tikzfadingfrompicture}% Edit: get rid of this space
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\begin{scope}[transparency group=knockout]
\dotfast[dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}
\dotfastfade[dot distance=2mm,dot size=0.4mm,dot number x=50,dot number y=50]
\end{tikzpicture}

\newpage
\section*{Nice Version:}
all together, with random dots

\vspace{1cm}
\noindent
% Edit: this is only needed once in the document
%\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
%\end{tikzfadingfrompicture}% Edit: get rid of this space
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\begin{scope}[transparency group=knockout]
\dotrandom[dot color=teal,dot distance=2mm,dot size=0.2mm,dot number x=50,dot number y=50]
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}
\dotrandomfade[dot color=magenta,dot size=0.2mm,dot number x=70,dot number y=70,dot random shift ratio=0.3]
\end{tikzpicture}
\end{document}


Edit: Here is a solution using patterns.

The main advantage is, that it can cover the whole page without memory problems (only medium burden). The disadvantages are gap and line artefacts between the tiles of the pattern and of course the pattern repeats. Oh, and the compile time for the first page is quite long. But subsequent pages are compiled much faster.

The construction (using transparency group=knockout and fading) is the same as for the first solution. And therefore the same limitations for viewer and printing apply.

Here the options of the first solution are also available to setup the dot raster.

The code:

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{patterns}

\newdimen\dotdist
\newdimen\dotsize
\newdimen\dotx
\newdimen\doty

% defaults
\dotdist=1mm
\dotsize=0.1mm

\tikzset{%
dot distance/.code={\dotdist=#1},
dot size/.code={\dotsize=#1},
dot number x/.store in=\dotnumx,
dot number y/.store in=\dotnumy,
dot random shift ratio/.store in=\dotshiftratio,
dot color/.store in=\dotcolor,
%
dot distance/.value required,
dot size/.value required,
dot number x/.value required,
dot number y/.value required,
dot random shift ratio/.value required,
% set defaults
dot distance=1mm,
dot size=0.1mm,
dot number x=50,
dot number y=50,
dot random shift ratio=0.25,
dot color=black
}

\pgfdeclarepatternformonly[\dotdist,\dotsize,\dotnumx,\dotnumy,\dotshiftratio,\dotcolor]{dotsrandom}
% slightly overlap, creates line artefacts
%{\pgfpoint{-\dotshiftratio*\dotdist}{-\dotshiftratio*\dotdist}}
%{\pgfpoint{(\dotnumx+\dotshiftratio)*\dotdist}{(\dotnumy+\dotshiftratio)*\dotdist}}
% no overlap, can create gap  and line artefacts, but seems better
{\pgfpointorigin}
{\pgfpoint{\dotnumx*\dotdist}{\dotnumy*\dotdist}}
{\pgfpoint{\dotnumx*\dotdist}{\dotnumy*\dotdist}}
{
\pgfsetlinewidth{\dotsize}
\pgfsetstrokecolor{\dotcolor}
\foreach \x in {0,...,\dotnumx}{%
\typeout{line \x}% showing progress
\foreach \y in {0,...,\dotnumy}{%
\pgfmathparse{rand}
\pgfmathsetmacro{\drandx}{\pgfmathresult}
\pgfmathparse{rand}
\pgfmathsetmacro{\drandy}{\pgfmathresult}
\pgfmathsetlength{\dotx}{(\x+\dotshiftratio*\drandx)*\dotdist}
\pgfmathsetlength{\doty}{(\y+\dotshiftratio*\drandy)*\dotdist}
\pgfpathmoveto{\pgfpoint{\dotx}{\doty-0.5*\dotsize}}
\pgfpathlineto{\pgfpoint{\dotx}{\doty+0.5*\dotsize}}
\pgfusepath{stroke}
}
}
}

\begin{document}
\noindent
\node[scale=15,rotate=45, transparent!0,inner sep=0pt] at (0,0) {\sffamily\bfseries Test};
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\begin{scope}[transparency group=knockout]
\fill[pattern=dotsrandom,dot color=red,dot size=0.2mm] (-0.5\textwidth,-0.5\textheight) rectangle (0.5\textwidth,0.5\textheight);
\node[scale=15, text opacity=0,rotate=45,inner sep=0pt] at (0,0) {\sffamily\bfseries Test};
\end{scope}
(-0.5\textwidth,-0.5\textheight) rectangle (0.5\textwidth,0.5\textheight);
\end{tikzpicture}
\end{document}


And another idea: using nearly transparent filled rectangles.

Again, the construction is the same as for the first solution.

Here there is no control control of dot size and distance. In the viewer the rectangle may be visible, depending on their opacity. And for printing the apperance solely depends on the printer / printer driver. But there are no problems with memory or compile time at all.

The code:

\documentclass[10pt,a4paper]{article}
\usepackage{tikz}

\begin{document}
\section*{With transparent rectangles:}
Disadvantages are: no control over dot size and distance, rectangles visible
on screen, and printout always depends on printer / printer driver.

\vspace{1ex}
\noindent
Advantages: no memory problems, fast compiling.

\vspace{1cm}
\noindent
\node[scale=10,rotate=45, transparent!0] at (0,0) {\sffamily\bfseries Test};
\begin{tikzpicture}
% stuff below, for testing
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
% the dots
\begin{scope}[transparency group=knockout]
\fill[blue,opacity=0.1] (-5,-5) rectangle (5,5);
\node[scale=10, text opacity=0,rotate=45] at (0,0) {\sffamily\bfseries Test};
\end{scope}
\end{tikzpicture}
\end{document}


Edit 2: example of usage.

Here is an example of usage, using the eso-pic package to put the dot raster in the foreground. The dots are deliberately too big here, so they show up on screen. For real use, dot size, distance and color have to be selected so they are (nearly) invisible on screen, but show up on a printout. That probably depends on the used printer / printer driver.

Note: here the definition of \VoidPantograph has to be changed to setup the dots, because the main raster and the one for the text use the same options.

\documentclass[a4paper]{article}
\usepackage{eso-pic}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usepackage{lipsum}

\newdimen\dotdist
\newdimen\dotsize
\newdimen\dotx
\newdimen\doty

% defaults
\dotdist=1mm
\dotsize=0.1mm

\tikzset{%
dot distance/.code={\dotdist=#1},
dot size/.code={\dotsize=#1},
dot number x/.store in=\dotnumx,
dot number y/.store in=\dotnumy,
dot random shift ratio/.store in=\dotshiftratio,
dot color/.store in=\dotcolor,
%
dot distance/.value required,
dot size/.value required,
dot number x/.value required,
dot number y/.value required,
dot random shift ratio/.value required,
% set defaults
dot distance=1mm,
dot size=0.1mm,
dot number x=50,
dot number y=50,
dot random shift ratio=0.25,
dot color=black
}

\pgfdeclarepatternformonly[\dotdist,\dotsize,\dotnumx,\dotnumy,\dotshiftratio,\dotcolor]{dotsrandom}
% slightly overlap, creates line artefacts
%{\pgfpoint{-\dotshiftratio*\dotdist}{-\dotshiftratio*\dotdist}}
%{\pgfpoint{(\dotnumx+\dotshiftratio)*\dotdist}{(\dotnumy+\dotshiftratio)*\dotdist}}
% no overlap, can create gap  and line artefacts, but seems better
{\pgfpointorigin}
{\pgfpoint{\dotnumx*\dotdist}{\dotnumy*\dotdist}}
{\pgfpoint{\dotnumx*\dotdist}{\dotnumy*\dotdist}}
{
\pgfsetlinewidth{\dotsize}
\pgfsetstrokecolor{\dotcolor}
\foreach \x in {0,...,\dotnumx}{%
\typeout{line \x}% showing progress
\foreach \y in {0,...,\dotnumy}{%
\pgfmathparse{rand}
\pgfmathsetmacro{\drandx}{\pgfmathresult}
\pgfmathparse{rand}
\pgfmathsetmacro{\drandy}{\pgfmathresult}
\pgfmathsetlength{\dotx}{(\x+\dotshiftratio*\drandx)*\dotdist}
\pgfmathsetlength{\doty}{(\y+\dotshiftratio*\drandy)*\dotdist}
\pgfpathmoveto{\pgfpoint{\dotx}{\doty-0.5*\dotsize}}
\pgfpathlineto{\pgfpoint{\dotx}{\doty+0.5*\dotsize}}
\pgfusepath{stroke}
}
}
}

% #1: scale for text, optional, default: 15
% #2: text
\newcommand*{\VoidPantograph}[2][15]{%
\node[scale=#1,rotate=45, transparent!0,inner sep=0pt] at (0,0) {#2};
\begin{tikzpicture}
\begin{scope}[transparency group=knockout]
\fill[pattern=dotsrandom,dot color=red,dot size=0.2mm]
(-0.5\paperwidth,-0.5\paperheight) rectangle (0.5\paperwidth,0.5\paperheight);
\node[scale=#1, text opacity=0,rotate=45,inner sep=0pt] at (0,0) {#2};
\end{scope}
(-0.5\paperwidth,-0.5\paperheight) rectangle (0.5\paperwidth,0.5\paperheight);
\end{tikzpicture}
}

\begin{document}
\noindent
\begin{center}
\Huge\sffamily\bfseries
Example of Usage

\vspace{1cm}
\begin{tikzpicture}
\fill[blue!20] (-2,-2) rectangle (0,0);
\fill[red!20] (0,0) rectangle (2,2);
\end{tikzpicture}
\end{center}

\vspace{1cm}

\noindent
The dots here are of course too big. But for this example they should be
visible on screen. The goal is to select dot size, distance and color,
so the raster is nearly invisible on screen, but shows up on a printout.

\section{Some Text}
\lipsum
\end{document}

• I don't know whether I understand the idea behind the question (+1) and the answer (+1) in detail - but both sound and look very fine! Could you please be so kind and show how to apply the answer to a real world example (e.g. a page containing b/w blindtext)? Thx! – lAtExFaN Aug 20 '17 at 10:23
• @lAtExFaN: added an example, see edit 2 at the end. – Mike Aug 20 '17 at 16:31
• @Christian: it would be interesting to know the settings you used, the printer(s) you tested it on, and how well it worked. – Mike Aug 20 '17 at 16:43