I'd like to add an invisible watermark to each PDF document that I produce so that in case it leaks, I know where it leaked from. Anyone knows what would a good way for an invisible watermark be in a PDF document and how to add one?
[Tex/LaTex] How to add an invisible watermark to PDF documents
drmembeddingsteganographywatermark
Related Solutions
Here's a quick and dirty solution:
\documentclass{article}
\makeatletter
\usepackage{tikz}
\usepackage{everypage}
\pgfmathsetseed{314}
\newlength{\obfobjectsize}
\setlength{\obfobjectsize}{36pt}
\newcommand{\obftext}{obfuscated}
\newcommand{\dontobfuscate}[1]{%
\ifmmode\let\@dollar=$\else\let\@dollar=\relax\fi
\vphantom{#1}\smash{\fboxsep=0pt\colorbox{white}{\@dollar #1\@dollar}}%
}
\newcommand{\setrandomcoordinates}{% Places random coordinates (in pt)
\pgfmathrnd % in \a and \b.
\let\a=\pgfmathresult
\pgfmathmultiply{\a}{\paperwidth}%
\let\a=\pgfmathresult
%
\pgfmathrnd
\let\b=\pgfmathresult
\pgfmathmultiply{\b}{\paperheight}%
\let\b=\pgfmathresult
}
\newcommand{\tkzplacerandomline}{
\setrandomcoordinates
%
\pgfmathrand
\let\c=\pgfmathresult
\pgfmathmultiply{\c}{\obfobjectsize}%
\let\c=\pgfmathresult
%
\pgfmathrand
\let\d=\pgfmathresult
\pgfmathmultiply{\d}{\obfobjectsize}%
\let\d=\pgfmathresult
%
\coordinate[xshift=\a,yshift=\b] (a) at (current page.south west);
\coordinate[xshift=\c,yshift=\d] (b) at (a);
\draw[ultra thick] (a) -- (b);
}
\newcommand{\tkzplacerandomcircle}{
\setrandomcoordinates
%
\pgfmathrnd
\let\c=\pgfmathresult
\pgfmathmultiply{\c}{\obfobjectsize}%
\let\c=\pgfmathresult
%
\coordinate[xshift=\a,yshift=\b] (a) at (current page.south west);
\draw[ultra thick] (a) circle (\c pt);
}
\newcommand{\tkzplacerandomnode}{%
\setrandomcoordinates
%
\pgfmathrandominteger{\c}{30}{330}
%
\coordinate[xshift=\a,yshift=\b] (a) at (current page.south west);
\node[rotate=\c] at (a) {\obftext};
}
\newcommand{\placerandomobjects}[2]{%
\begin{tikzpicture}[overlay,remember picture]
\foreach \n in {1,2,...,#2} { #1 }
\end{tikzpicture}%
}
\AddEverypageHook{
\placerandomobjects{\tkzplacerandomline}{100}
\placerandomobjects{\tkzplacerandomcircle}{100}
\placerandomobjects{\tkzplacerandomnode}{100}
}
\makeatother
\begin{document}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus
elit, vestibulum ut, placerat ac, adipiscing vitae, felis. Curabitur
dictum gravida mauris. Nam arcu libero, nonummy eget, consectetuer id,
vulputate a, magna. Donec vehicula augue eu neque. Pellentesque
habitant morbi tristique senectus et netus et malesuada fames ac
turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. Nulla
et lectus vestibulum urna fringilla ultrices. Phasellus eu tellus sit
amet tortor gravida placerat. Integer sapien est, iaculis in, pretium
quis, viverra ac, nunc. \dontobfuscate{Praesent eget sem vel leo ultrices
bibendum}. Aenean faucibus. Morbi dolor nulla, malesuada eu, pulvinar
at, mollis ac, nulla. Cur- abitur auctor semper nulla. Donec varius
orci eget risus. Duis nibh mi, congue eu, accumsan eleifend, sagittis
quis, diam. Duis eget orci sit amet orci dignissim rutrum.
Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel,
wisi. Morbi auctor lorem non justo. Nam lacus libero, pretium at,
lobortis vitae, ultricies et, tellus. Donec aliquet, tortor sed
accumsan bibendum, erat ligula aliquet magna, vitae ornare odio metus
a mi. Morbi ac orci et nisl hendrerit mollis. Suspendisse ut
massa. Cras nec ante. Pellentesque a nulla. Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Aliquam tincidunt urna.
Nulla ullamcorper vestibulum turpis. Pellentesque cursus luctus
mauris. Nulla malesuada porttitor diam. Donec felis erat, congue non,
volutpat at, tincidunt tristique, libero. Vivamus viverra fermentum
felis. \dontobfuscate{$E=m(a^2+b^2)$} Donec nonummy pellentesque
ante. Phasellus adipiscing semper elit. Proin fermentum massa ac
quam. Sed diam turpis, molestie vitae, placerat a, molestie nec,
leo. Mae- cenas lacinia. Nam ipsum ligula, eleifend at, accumsan nec,
suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc eleifend
consequat lorem. Sed lacinia nulla vitae enim. Pellentesque tincidunt
purus vel magna. Integer non enim. Praesent euismod nunc eu
purus. Donec bibendum quam in tellus. Nullam cur- sus pulvinar
lectus. Donec et mi. Nam vulputate metus eu enim. Vestibulum
pellentesque felis eu massa.
Quisque ullamcorper placerat ipsum. Cras nibh. Morbi vel justo vitae
lacus tincidunt ultrices. Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. In hac habitasse platea dictumst. Integer tempus
convallis augue. Etiam facilisis.
\begin{displaymath}
\dontobfuscate{E=m(a^2+b^2)}
\end{displaymath}
Nunc elementum fermentum
wisi. Aenean placerat. Ut imperdiet, enim sed gravida sollicitudin,
felis odio placerat quam, ac pulvinar elit purus eget enim. Nunc
vitae tortor. Proin tempus nibh sit amet nisl. Vivamus quis tortor
vitae risus porta vehicula.
\end{document}
It is not very elegant, and especially the \dontobfuscate command is really very simple; it will work in horizontal mode (and generate a box, so it will be not breakable and the spaces will have their natural width, which will look ugly unless (a) only individual words are put in it or (b) the text is set ragged right or something similar; it will also work in math mode, but in a very primitive fashion (suitable for e.g. simple symbols). But it works as a proof of concept, and making it more versatile is now a question of some tweaking. Have fun!
PS. Not to mention that the "drm" tag might be considered a bit, say, offensive by some people in this community;).
Edit: as cjorssen mentioned in the comment, this needs two-pass compilation, since it uses the remember picture mechanism of tikz.
Here are a couple of options:
Method 1: Dot Grid, encoded "christian"
\setgrid[opt arg][opt arg]{letters}
\usegrid
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.
\documentclass{article}
\usepackage{xparse}
\usepackage{graphicx}
\usepackage{fancyhdr}
\ExplSyntaxOn
\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
}
\NewDocumentCommand{\usegrid}{}
{
\box_use:N \c__grid_box
}
\ExplSyntaxOff
\pagestyle{fancy}
\renewcommand{\headrulewidth}{0pt}
\lhead{\usegrid}
\begin{document}
\setgrid[5][.3]{christian}
Hi! I am a lovely sentence.
\end{document}
Method 2: Dots in text, encoded "abba"
This one gives:
\markdoc{arg}{letters}
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 ni
th 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
\documentclass{article}
\usepackage{luacode,luatexbase}
\begin{luacode}
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
end
-- 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
mark_chars(head.list)
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
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
t[i]=char_tab[s]
end
end
function mark_page(head)
line = 0
mark_chars(head)
return head
end
\end{luacode}
\newcommand\markdoc[2]{%
\directlua{encode("#2")}%
\directlua{p=#1}
\directlua{luatexbase.add_to_callback('pre_output_filter', mark_page, "mark_page")}%
}
\begin{document}
\markdoc{.3}{abba}
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.
\end{document}
Best Answer
Here a funny idea: Just place some watermark/copyright text on top or below of a dot or other punctuation mark. It is invisible for the naked eye if it uses the same color as the text but can be extracted using copy and paste. You can also give it a dark gray color, so you still have a chance to read it on screen.
Here some principal solution:
The dot will then look like this: (note the hard to see text)