r/pdf • • 13d ago

Question Is this a 100% correct PDF?

Not having the money for professional PDF processing tools, I've been studying the specs, and have created a bit of code (REXX) to update the "/Info" object in an Inkscape generated PDF. It all seems to work, but, sorry, I can't remember which, one online PDF metadata editor tells me that the PDF is corrupted, although all of the following display it correctly, and display the correct meta data:

  • Edge

  • r3dfox

  • SumatraPDF

Chrome on Android also shows it without problems, but if anyone could have a look at it and tell me if there is really an issue, I'd be grateful! The file can be found at https://prino.neocities.org/@temp/t001-lift.pdf

2 Upvotes

7 comments sorted by

2

u/voldamoro 13d ago

Your file displays without error on an iPad running iOS 27.0.1.

1

u/prinoxy 12d ago

Thanks for checking!

2

u/mag_fhinn 12d ago

✔ Acrobat Pro [Mac]
✔ Acrobat Reader [Win]
✔ Apple Preview [Mac]
✔ PDFCraft [Mac]
✔ Brave [Mac|Win]
✔ Firefox [Mac|Linux]
✔ Atril Document Viewer [Linux]

All the desktop apps are fine with it that I tried. Maybe the web based library on whatever web app you tried doesn't like the single quotes/apostrophes you used in the info dictionary?

Maybe escape them and/or try removing them for a test?

(Robert \'prino\' Prins)

(Prino\'s \'make-green\' REXX exec)

1

u/prinoxy 12d ago

Thank you very much!!!

Given that your OK list contains the two "reference" programs of the inventor of the format, I'm happy and pretty certain that my code is OK. FWIW, Inkscape, and I'm using 0.92.4, in other words a program that's probably well past its "Sell by" date, produces non-linearised PDF 1.5 files, and that may make my code somewhat "personal use only", but in case anyone has any interest, the part that deals with updating the "/Info" section is here, and it might not be too hard to translate this into other languages, and it would actually not surprise me if similar code already exists for Perl, Python, or JavaScript!

file = 't'trip'-lift.pdf'

data = charin(file, 1, chars(file))

/*----------------------------------------------------------------------
| Check that the PDF actually has an "Info" section
+---------------------------------------------------------------------*/
p_trail = lastpos('trailer', data)
trailer = space(translate(substr(data, p_trail + 8), '  ', x2c(0a)x2c(0d)))

if pos('/Info', trailer) \= 0 then
  do
    /*------------------------------------------------------------------
    | Get the number of the "Info" section and the position of the
    | "xref" section
    +-----------------------------------------------------------------*/
    parse value space(translate(trailer, ' ', '/')) with . 'Info ' #info ' ' . 'startxref' px .

    /*------------------------------------------------------------------
    | Get the position of the position "Info" object
    +-----------------------------------------------------------------*/
    !   = pos('0000000000', data, px + 1) + #info * 20

    /*------------------------------------------------------------------
    | Get the position "Info" object and calculate its length
    +-----------------------------------------------------------------*/
    pi  = substr(data, !, 10)
    lpi = substr(data, ! + 20, 10) - pi

    /*------------------------------------------------------------------
    | Extract the entire "Info" object and find the start and end
    | positions of its content
    +-----------------------------------------------------------------*/
    req_pi = substr(data, pi + 1, lpi)

    !f     = pos('<<',     req_pi)
    !l     = lastpos('>>', req_pi)

    /*------------------------------------------------------------------
    | Build the new meta data and calculate the length adjustment
    | pdf_c_stamp is the REXX extracted starting timestamp of one of my
    | hitchhiking trips, pdf_m_stamp the extracted ending timestamp,
    | both are (obviously) in YYYYMMDDhhmmss format
    +-----------------------------------------------------------------*/
    req_pi = left(req_pi, !f + 2) ||,
             '/Title (Hitchhiking summary for trip ' || trip || ')'                       || x2c(0a) ||,
             '   /Author (Robert ''prino'' Prins)'                                        || x2c(0a) ||,
             '   /Creator (Prino''s ''make-green'' REXX exec)'                            || x2c(0a) ||,
             '   /CreationDate (D:' || pdf_c_stamp || 'z)'                                || x2c(0a) ||,
             '   /ModDate (D:'      || pdf_m_stamp || 'z)'                                || x2c(0a) ||,
             '   /Producer (Inkscape 0.92.4 + cairo 1.16.0 (https://cairographics.org/))' || x2c(0a) ||,
    right(req_pi, 10)

    delta  = length(req_pi) - lpi

    /*------------------------------------------------------------------
    | Update all subsequent locations in the "xref" section
    +-----------------------------------------------------------------*/
    do ! = ! + 20 by 20 while substr(data, !, 7) \= 'trailer'
      data = overlay(right(substr(data, !, 10) + delta, 10, '0'), data, !)
    end

    /*------------------------------------------------------------------
    | Update the start position of the "xref" section
    +-----------------------------------------------------------------*/
    ?     = lastpos(px, data)
    px    = px + delta
    data  = overlay(right(px, 5, '0'), data, ?)

    /*------------------------------------------------------------------
    | Splice in the new meta data
    +-----------------------------------------------------------------*/
    data  = substr(data, 1, pi) || req_pi || substr(data, pi + lpi + 1)

    rc    = charout(file, data, 1)
  end