17

I have a problem which is in my opinion quite reasonable and common, still I cannot find an answer anywhere.

I have two .tex files and I just want to check whether pdflatex will produce exactly the same pdf.

It is easy to generate two different .tex files which give - as far as i understand - exactly the same pdf. Just think about - two .tex files with different line breaks - a .tex file the does \newcommand{c1}{c} and calls c1 and another calling directly c - a .tex file doing \input{blabla.tex} and the other containing exactly the same content as blabla.tex

The background is that I have a repository with tex files. Quite often, I find myself doing changes which are not supposed to change the output pdfs at all (e.g. changes like those of the examples above) but I have no way to check.

Things that I already thought of:

  • using a tool to directly compare pdf's: ideally I'd like the thing to be more automatic and radical.
  • using latexdiff: I cannot use perl and again I'd like something more automatic and radical.
  • Using tools like latexpand, flatex, flatten: no go, since this just expands inputs/includes.

An interesting clue I found is this How to prevent different checksums for different typesets but I cannot make it work.

Of course, not being a computer expert, I don't quite know how to precisely define "two pdf's are exactly the same". I would define as in "if you put them one on top of the other on a bright window, they look like just one of them".

If it matters, I m using Win7 and pdflatex (through TeXnicCenter).

  • I've had success in this by compiling the two files to postscript (latex-> dvips) and then running a 'diff' on the two postscript files – cmhughes Nov 27 '17 at 15:24
  • 2
    At least in my case the postscript files have a time stamp. Almost certainly the files will differ here. This raises the question what precisely is meant by `identical'. – user121799 Nov 27 '17 at 15:38
  • You can use perl, for example, to remove the time stamps from the post script files – cmhughes Nov 27 '17 at 17:12
  • 5
    pdftex will make reproducible pdf if you set the enviornment variables SOURCE_DATE_EPOCH enviornment variable to an epoch (seconds since 1970) eg 0 and set SOURCE_DATE_EPOCH_TEX_PRIMITIVES=1 and FORCE_SOURCE_DATE=1 – David Carlisle Nov 27 '17 at 20:28
9

In the spirit of "holding the paper up to the light", we can replicate exactly this effect with a Python script. Read the PDF, the overlay the pages to find out whether each pixel is the same.

import wand.image as w
import itertools
import functools
import sys

def load_pages(filename):
    """ Load the PDF file into a sequence of bytes, with dimensions """
    pdf = w.Image(filename=filename)
    for page in pdf.sequence:
        image = w.Image(page)
        width, height = image.width, image.height
        # Copy raw image data into blob string
        blob = image.make_blob(format='RGB')
        yield (blob, width, height)


def pages_identical(blob1, blob2, width, height):
    """ Check (pixel by pixel) if the pages (blobs) are identical """
    for c in range(0, width*height*3, 3):
        px1 = (blob1[c], blob1[c+1], blob1[c+2])
        px2 = (blob2[c], blob2[c+1], blob2[c+2])
        if px1 != px2:
            return False
    return True


def files_identical(filename1, filename2):
    """ Check if the two PDF files 'look' the same """
    pages1, pages2 = load_pages(filename1), load_pages(filename2)
    for pageno, (page1, page2) in enumerate(itertools.zip_longest(pages1, pages2, fillvalue=None)):
        if None in {page1, page2}:
            print("Files have a different number of pages")
            yield False
            return  # Break out now
        (blob1, width1, height1) = page1
        (blob2, width2, height2) = page2
        if (width1, height1) != (width2, height2):
            print("Pages are different sizes!")
            yield False
            return  # Break out now
        if pages_identical(blob1, blob2, width1, height1):
            yield True
        else:
            print("Page {} differs".format(pageno))
            yield False


fn1 = sys.argv[1]
fn2 = sys.argv[2]

match = functools.reduce(lambda a, b: a and b, files_identical(fn1, fn2), True)
# print(match)

Note that this requires ImageMagick and the Python bindings for it (wand).

3

You can use \tracingoutput to get information about the boxes that LaTeX ships out to the pdf. This should allow you to identify differences that would fail your test of "if you put them one on top of the other on a bright window, they look like just one of them", although possibly changes to the margins/papersize could sneak through.

\documentclass{article}

\tracingoutput=1
\showboxbreadth=1000
\showboxdepth=1000

\def\foo{Hello World}
\def\bar{World Hello}

\begin{document}
%Hello World
%\foo
\bar
\end{document}

After running, you would need to compare the log files and look for differences between things like

Completed box being shipped out [1]
\vbox(633.0+0.0)x407.0
.\glue 16.0
.\vbox(617.0+0.0)x345.0, shifted 62.0
..\vbox(12.0+0.0)x345.0, glue set 12.0fil
...\glue 0.0 plus 1.0fil
...\hbox(0.0+0.0)x345.0
..\glue 25.0
..\glue(\lineskip) 0.0
..\vbox(550.0+0.0)x345.0, glue set 539.94232fil
...\write-{}
...\glue(\topskip) 3.05556
...\hbox(6.94444+0.0)x345.0, glue set 277.47212fil
....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 H
....\OT1/cmr/m/n/10 e
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 o
....\glue 3.33333 plus 1.66666 minus 1.11111
....\OT1/cmr/m/n/10 W
....\kern-0.83334
....\OT1/cmr/m/n/10 o
....\OT1/cmr/m/n/10 r
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 d
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
....\glue(\rightskip) 0.0
...\glue 0.0 plus 1.0fil
...\glue 0.0
...\glue 0.0 plus 0.0001fil
..\glue(\baselineskip) 23.55556
..\hbox(6.44444+0.0)x345.0, glue set 170.0fil
...\glue 0.0 plus 1.0fil
...\OT1/cmr/m/n/10 1
...\glue 0.0 plus 1.0fil

and

Completed box being shipped out [1]
\vbox(633.0+0.0)x407.0
.\glue 16.0
.\vbox(617.0+0.0)x345.0, shifted 62.0
..\vbox(12.0+0.0)x345.0, glue set 12.0fil
...\glue 0.0 plus 1.0fil
...\hbox(0.0+0.0)x345.0
..\glue 25.0
..\glue(\lineskip) 0.0
..\vbox(550.0+0.0)x345.0, glue set 539.94232fil
...\write-{}
...\glue(\topskip) 3.05556
...\hbox(6.94444+0.0)x345.0, glue set 277.47212fil
....\hbox(0.0+0.0)x15.0
....\OT1/cmr/m/n/10 W
....\kern-0.83334
....\OT1/cmr/m/n/10 o
....\OT1/cmr/m/n/10 r
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 d
....\glue 3.33333 plus 1.66666 minus 1.11111
....\OT1/cmr/m/n/10 H
....\OT1/cmr/m/n/10 e
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 l
....\OT1/cmr/m/n/10 o
....\penalty 10000
....\glue(\parfillskip) 0.0 plus 1.0fil
....\glue(\rightskip) 0.0
...\glue 0.0 plus 1.0fil
...\glue 0.0
...\glue 0.0 plus 0.0001fil
..\glue(\baselineskip) 23.55556
..\hbox(6.44444+0.0)x345.0, glue set 170.0fil
...\glue 0.0 plus 1.0fil
...\OT1/cmr/m/n/10 1
...\glue 0.0 plus 1.0fil

You could use a simple diff to find the differences.

2

You can compare two pdf using programs to show the differences between those files. I suggest DiffPDF (version 2.1.3 by Mark Summerfield), that have a "portable" version (PortableApps.com).

  • Thx! Actually as I was saying I dont like this option too much because it does not look very automatizable. For example, I dont think I d be able to put it in a batch. – Giulio Nov 27 '17 at 15:04
  • There is a cl-program available as well, diffpdfc: qtrac.eu/diffpdfc.html – Grimler Nov 27 '17 at 15:10
1

As it's visual differences you're looking for, here's an image-based approach.

I've used a python script in the past to split PDFs into colour and B&W pages
(info on the author's homepage). It needs pdftk and gs or pdftoppm to do the conversions.

The script works by converting each page to PPM and looking for colour pixels. If you passed this over the two PDFs page-by-page and subtracted one PPM from the equivalent for the other document (or hashed the images and compared hashes) this would be equivalent to holding them up to the light. I'll see if I can put something together simply. The script is quite simple python if you've got the tools installed, and I've written something based on it before (to avoid printing blank pages from a scanned document).

  • 1
    This is a bit of a non-answer for now, but more than a comment. I'll either write the script or edit the answer – Chris H Nov 27 '17 at 16:51
1

For reledmac/reledpar developement tools, I have scripten a python script which call conver tool to

  1. Export the pdf to png
  2. Compare the png.

It slower to the tracebox method, but we can't exclude that some boxes have the same size, but different content.

The script is here https://github.com/maieul/ledmac/blob/master/examples/check-example.py.

1

This is not an answer, but I keep trying hard..

If I compile the following

\documentclass{article}

\pdfinfo{
    /CreationDate (D:20000101000000)
    /ModDate (D:20000101000000)
}

\begin{document}
Hello World
\end{document}

twice, name the two outputs test1.pdf and test2.pdf and then run the following C# code

static void Main(string[] args)
{
    byte[] fBytes1 = File.ReadAllBytes(@"C:\test\latex\test1.pdf");
    byte[] fBytes2 = File.ReadAllBytes(@"C:\test\latex\test2.pdf");

    bool equal = true;

    if (fBytes1.Length == fBytes2.Length)
    {
        for (int i = 0; i < fBytes1.Length; ++i)
        {
            if (fBytes1[i] != fBytes2[i])
            {
                equal = false;
                break;
            }
        }
    }
    else
    {
        equal = false;
    }

    int wait = 0;
}

I do not get the same bytes (i.e. equal is false). There are actually just a handful of bytes which are different (out more than 10^4). There are more different bytes if I remove the pdfinfo.

I m quite sure that these differences are coming from the different time stamps: in fact if I look at dateModified in windows explorer, that s different. (If I look at file>properties in acrobat reader, then created and modified dates are what I set through pdfinfo, so yes it s doing something).

In such a case, I guess it would be enough to either hack the remaining metadata in the two files which is causing the diff or doing a smarter bitwise comparison in order to ignore the metadata I'm not interested in and I would be done right?

  • 1
    This was mentioned recently in chat and you may need to use epoch mode, but texlive help also says pdfTeX also has several primitives to support reproducible builds, which are preferable to setting these environment variables – StrongBad Nov 27 '17 at 18:54
  • @StrongBad set SOURCE_DATE_EPOCH enviornment variable to an epoch (seconds since 1970) eg 0 and set SOURCE_DATE_EPOCH_TEX_PRIMITIVES=1 and FORCE_SOURCE_DATE=1 – David Carlisle Nov 27 '17 at 20:26
  • @DavidCarlisle nothing - i tried a bit but i dont manage to compile twice and get the two pdf bitwise identical (ie "equal" in my c# code always ends up being false) – Giulio Nov 29 '17 at 8:27
  • @Giulio should work if you have texlive2017 or miktex of a similar age – David Carlisle Nov 29 '17 at 8:47
  • @DavidCarlisle Very interesting (actually i m getting crazy). You re right. I compile the first time, I rename to test1.pdf, I compile a second time, I rename to test2.pdf, I run my code and see perfect equality. BUT If I open even just one of them and then close it, if I run the code again they re not equal anymore!! I.e. it seems that opening the pdf actually modifies it: this is why before i said that i did not manage - i think the simple reason was that i was opening the pdf before bitwise comparing. I m using Adobe Acrobat Reader DC 2015 Release | Version 2015.006.30355 to open the pdfs. – Giulio Nov 29 '17 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.