3

I use only lualatex, so a compiler-specific answer is OK.

Consider this pseudocode-MWE. The included PDF is always one page:

\documentclass{article}
\usepackage{pdfpages}
\def\includepdfifgood#1{
  % This is where I test the pdfproducer string in #1.
  % If wrong pdfproducer, compile halts with error message. Otherwise:
  \includepdf{#1}
}
\begin{document}
\includepdfifgood{something.pdf}
\end{document}

The included page must be the output of a previous lualatex compile. In that earlier compile, I can (for example) set pdfproducer to a custom string. I know how to do that.

So, what I need to do is inspect pdfproducer and accept or reject the file.

Why I need to do this: PDF/X (and PDF/A) have stringent requirements. If a properly pre-processed PDF page is included in such a document, it can still be PDF/X or PDF/A. Otherwise, compliance may fail (and TeX does not know that it fails). I know how to pre-process. Now I need to ensure that only a pre-processed page is allowed into another PDF.

EDIT2: When I asked this question, I had the PDF Producer string in mind. It turns out that the Producer string is not the best one to use, because it may change when the Producer changes version number (for example). It is better to use something that the user can directly set, such as Title, Author, Subject, or Keywords. Fortunately, the accepted code works for any standard info dictionary entry. There is no need to modify it, since within the luacode, producer is just a local variable name, which works even if you are looking for Author, or whatever.

EDIT: In an earlier question, I received a magnificent answer in luacode, which I am already using. My rationale for asking is the same, but the circumstances are different. Alas, I do not know enough Lua to edit the linked answer, or I would have done it.

EDIT2: The accepted answer has been put to good use! In version 1.50 of novel package, the procedure for creating CMYK 240% PDF/X-1a:2001 color cover artwork has been automated by batch/bash script. First, an RGB image (or PDF) is converted to CMYK 240% PDF (FlateDecode) without TeX, using ImageMagick and Ghostscript. This PDF is not PDF/X. However, using the pdfmark capability of Ghostscript, it has a custom tag in the PDF info dictionary. Then, LuaLaTeX (with novel) processes that file into PDF/X-1a:2001. It inspects for the custom tag, and will only perform the process if the tag is detected. That's how it knows the image is in the correct color space, since no profile is embedded. The results have been validated using Adobe Acrobat Pro (which, I believe, is the only kind of program that could do it, until now).

2
  • didn't you already asked something similar? Commented Mar 7, 2018 at 19:15
  • @UlrikeFischer Yes, I did. In that case, I wanted to know if the color space (DeviceCMYK) for an image can be read. I was provided with a good luacode answer, which I am already using. This question is more general. I looked at the earlier luacode, but I don't know enough to edit it myself.
    – user139954
    Commented Mar 7, 2018 at 19:22

1 Answer 1

8

You can use the following code to verify the entries in the Info dictionary:

\documentclass{scrartcl}
\usepackage{luacode,pdfpages}
\begin{luacode*}
  function utf16to8(u16str)
    local result = ""
    local i = 1
    if #u16str % 2 == 1 then
      print("ERROR")
      return
    end
    while i < #u16str do
      local high, low = u16str:byte(i, i + 1)
      i = i + 2
      local current = bit32.replace(low, high, 8, 8)
      if bit32.band(high, 0xFC) == 0xD8 then
        current = bit32.replace(0, current, 10, 10)
        if i > #u16str then
          print("ERROR")
          return
        end
        high, low = u16str:byte(i, i + 1)
        i = i + 2
        current = bit32.replace(current, bit32.replace(low, high, 8, 8), 0, 10) + 0x10000
      elseif bit32.band(high, 0xFC) == 0xDC then
        print("ERROR")
        return
      end
      result = result .. unicode.utf8.char(current)
    end
    return result
  end
  function normalize_string(str)
    if str:sub(1,2) == "\xFE\xFF" then
      return utf16to8(str:sub(3))
    else
      return str
    end
  end
  function check_pdf_info(name, field, expected)
    local doc = epdf.open(name);
    if doc == nil then
      tex.sprint(luatexbase.catcodetables['latex-package'],
          "\\errmessage{Could not open " .. name .. "}{}{}\\@gobbletwo")
    else
      local producer = doc:getDocInfo():dictLookup(field)
      if not producer:isNull() and normalize_string(producer:getString()) == expected then
        tex.sprint(luatexbase.catcodetables['latex-package'], '\\@firstoftwo')
      else
        tex.sprint(luatexbase.catcodetables['latex-package'], '\\@secondoftwo')
      end
    end
  end
\end{luacode*}
\newcommand\PDFVerifyInfoFieldTF[3]{\directlua{check_pdf_info("\luaescapestring{#1}", "\luaescapestring{#2}", "\luaescapestring{#3}")}}
\begin{document}
\PDFVerifyInfoFieldTF{some_file.pdf}{Producer}{pdfTeX-1.40.8}{%
  \includepdf[pages=-]{some_file.pdf}%
}{%
  \errmessage{some_file.tex has the wrong producer}%
}
\end{document}

Possible fields:

  • Producer: For example pdfTeX-1.40.18 or LuaTeX-1.0.4.
  • Creator: For example TeX or LaTeX with hyperref package.
  • Author
  • Title
  • Subject
  • Keywords
  • PTEX.Fullbanner: For example This is LuaTeX, Version 1.0.4 (TeX Live 2017/Arch Linux))
  • CreationDate or ModDate These two are probably not very useful in this context.
8
  • Looks good. Will test and report back. Answers lots of good things, not just what I asked.
    – user139954
    Commented Mar 7, 2018 at 19:42
  • Works, with exception: Apparently the producer program writes some metadata in UTF-16, even when it could be ASCII. So, screening for producer (or title) did not work. But screening for CreationDate does work. What I need to do is check the metadata in either ASCII or UTF-16. Seems do-able, even with my limited knowledge.
    – user139954
    Commented Mar 7, 2018 at 22:56
  • @RobtAll I added some code to convert UTF16 if necessary. The new code should only fail if non ASCII characters are used without UTF-16 encoding. Commented Mar 8, 2018 at 0:47
  • Excellent! Will test. If you hear no more, that means success. In the last few hours: I could not figure out how to deal with UTF-16, but I did figure out how to cheat, by writing a cryptic producer string. Since it will always be the same for each approved file, it does not need to make sense. But actual UTF-16 is more versatile.
    – user139954
    Commented Mar 8, 2018 at 3:33
  • 1
    This sounds like a bug in the program producing the PDF file. If the parentheses end with 00 00, then the Producer ends with a NULL, which is ^^@ in TeX. So the code provides the right (but still unexpected) answer. Commented Mar 8, 2018 at 6:54

You must log in to answer this question.