3

EDIT: It turns out that Lua is not needed. Since I call my TeX from script, the necessary investigation can be done directly, using findstr in Windows batch, or the equivalent in BASH. But that's using script prior to running the TeX file. The answers provided below will do it within the TeX file.

Question:

I use only LuaLaTeX, Windows and Linux. For cross-platform compatibility, and for non-admin users, I cannot invoke sed or most other shell-escape programs from within TeX, even if it is available. I can invoke from outside TeX with a non-admin batch or BASH script.

My document class produces PDF/X-1a. Thus, any included image must be DeviceGray or DeviceCMYK. Currently, I avoid potential problems by severely limiting TeX commands that include images or other PDF. I would like to lift the limitations, if possible.

Thanks to this very helpful answer of a related question, I can block using \includepdf if the included file has fonts in it. Now, I would like to block using \includepdf unless the file has DeviceGray or DeviceCMYK written into it.

In all cases, the included PDF is just one page, one image.

If I open a suitable PDF in a hex editor, I see something like this:

10 0 obj
/DeviceCMYK
endobj

The actual image follows as another object (with FlateDecode).

So, to rephrase my question: Is there any way LuaLaTeX can screen a PDF file, perhaps using methods similar to sed or grep, and provide a yes/no answer as to whether string DeviceCMYK or DeviceGray appears?

I myself can simply use Adobe Acrobat Pro, or perhaps ImageMagick. This is for the benefit of more general users, without having to step outside of LuaLaTeX.

  • 1
    I suspect an answer similar to the one at the other question may work (using the Lua epdf library to navigate the PDF structure); the hard part may be in understanding enough of PDF structure (and the epdf library) to express what you want. Maybe the user who gave the other answer also knows how to do this (i.e. leave a message on that answer linking to this question)? – ShreevatsaR Mar 1 '18 at 1:18
  • @ShreevatsaR That's a thought. But I'll leave the question as-is for now, until others have seen it. I'm not a programmer, and have no knowledge of Lua. Once in a while, a question has a relatively obvious answer, from those who know. I think it is important to raise the question. Just now, I Googled for "Lua epdf library" and got no result intul the 11th. This question was itself 13th! – user139954 Mar 1 '18 at 3:22
  • @ShreevatsaR I am going to delete the question, unless someone chooses to answer it within half a day or so. It turns out that Windows command findstr does the job, and BASH has a similar method. Since my TeX file is called from command line, it's easy enough to do what I need in a shell script, without Lua. I wouldn't have guessed, but it works! – user139954 Mar 1 '18 at 3:41
  • 3
    Instead of deleting, why not post your solution as an answer? – A Feldman Mar 1 '18 at 4:48
  • 1
    @AFeldman All right, that makes sense. The rationale for my question is very useful, even though Lua is not the answer. Will do it tomorrow. – user139954 Mar 1 '18 at 5:57
5

A solution with epdf:

\documentclass{scrartcl}
\usepackage{luacode,pdfpages}
\begin{luacode*}
  function check_colorspaces(allowed_colorspace, name)
  local doc = epdf.open(name);
  if doc == nil then
    tex.sprint(luatexbase.catcodetables['latex-package'],
        "\\errmessage{Could not open " .. name .. "}{}{}\\@gobbletwo")
    return;
  else
    for pageno=1,doc:getNumPages() do
      local xobjs= doc:getCatalog():getPage(pageno):getResourceDict():lookup("XObject");
      if not xobjs:isNull() then
        for i=1,xobjs:dictGetLength() do
          xobjDict = xobjs:dictGetVal(i):streamGetDict()
          if xobjDict:lookup('Subtype'):getName() == 'Image' then
            if not allowed_colorspace[xobjDict:lookup('ColorSpace'):getName()] then
              tex.sprint(luatexbase.catcodetables['latex-package'], '\\@firstoftwo')
              return
            end
          end
        end
      end
    end
  end
  tex.sprint(luatexbase.catcodetables['latex-package'], '\\@secondoftwo')
  return;
end
\end{luacode*}
\newcommand\PDFHasDisallowedColorspaceTF[1]{%
  \directlua{check_colorspaces({DeviceCMYK=true, DeviceGray=true},"\luaescapestring{#1}")}%
}
\begin{document}
\PDFHasDisallowedColorspaceTF{image.pdf}{%
  \errmessage{image.pdf contains invalid colorspace!}%
}{%
  \includepdf[pages=-]{image.pdf}%
}
\end{document}

This does not check if the file contains one of the allowed colorspaces, it instead checks that no image in the PDF has an not allowed colorspace. If you have exactly one image, this should result in the same result, but if multiple images are included, this checks every one. If no image is found, this is not considered an error and colorspaces in the text are ignored.

This answer also can handle compressed PDF files.

  • 1
    Will test further, but looks excellent. Bonus for handling compression. Your caveats, about what it does and does not check, will not be a problem in my application. – user139954 Mar 1 '18 at 15:18
  • 1
    Works well, and I am currently using it. Just posted a related question, if you care to look at it. I don't know enough Lua to edit your code myself. Related: tex.stackexchange.com/questions/418970/… – user139954 Mar 7 '18 at 19:36
3

Since the color space will be in the file in plain ASCII you can just search for the string. This might come with some performance penalty compared to epdf.

\documentclass{article}
\pagestyle{empty}
\begin{document}

\directlua{
local filename = "test.pdf"
local color_space_found = false
%
for line in io.lines(filename) do
    if string.find(line,"/DeviceGray") or string.find(line,"/DeviceCMYK") then
        color_space_found = true
        break
    end
end
%
if not color_space_found then
    error(filename .. " does not specify the correct color space")
end
}

\end{document}
  • 1
    This requires the PDF to be uncompressed, i.e. at most PDF 1.4. – Martin Schröder Mar 1 '18 at 8:35
  • 1
    A useful answer, but as noted, the PDF must be uncompressed. When I asked the question, that was true of the PDFs I looked at, but in general they will be compressed. Still, this is useful. For example, if the PDF was produced via ImageMagick, it will probably be uncompressed (just the image data is compressed). – user139954 Mar 1 '18 at 15:17

Your Answer

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