Невидимые маркеры в PDF-файлах с использованием pdflatex

Невидимые маркеры в PDF-файлах с использованием pdflatex

Я использую latexdiffдля отображения изменений, которые я вношу в большой текст (более 200 страниц). Большинство моих изменений затрагивают лишь несколько страниц, поэтому я не хочу, чтобы мои читатели просматривали 200 страниц в поисках редких страниц, где были сделаны отдельные исправления опечаток: только около 30 страниц из 200 будут содержать изменения.

Я хотел бы:

  1. Вставьте скрытые маркеры PDF на страницы, где происходят изменения.
  2. Компилировать с помощью pdflatex
  3. Запустите скрипт, который отфильтрует страницы, не содержащие скрытых маркеров PDF.

    book.tex -latexdiff-> book_diff.tex -pdftex-> 200pp.pdf -script-> 30pp.pdf

Может ли кто-нибудь порекомендовать:

  • пакет tex для вставки «маркеров» определенного рода в PDF-файл
  • пакет Python для чтения PDF-файлов (и поиска указанных маркеров)

решение1

Первой моей мыслью было использовать операции ввода-вывода файлов, например, \writeили \immediate\write, но это столкнулось с известной проблемой с плавающими объектами и их поведением. У меня также была проблема с плавающими объектами с собственным счетчиком, когда не было \globalпрефикса.

Я предпочел бы использовать перекрестные ссылки напрямую, где обеспечивается защищенная запись. Я могу предложить вам это решение с помощью LuaTeX. Обработанные шаги таковы:

  • Мы набираем обычный документ и отмечаем позицию. Для целей этого поста это видимая отметка и это номер страницы. Определение использует обычную \labelкоманду с определенным префиксом, например malipivo. Я назвал файл filter.texи команду \writeme. Команда использует собственный счетчик, и это гарантирует уникальность маркера в \labelкоманде.
  • Файл filter.texобрабатывается несколько раз в зависимости от фактического набора материала, в этом примере это два раза. После первого запуска мы получаем документ с двумя красными вопросительными знаками (строка 1 в предварительном просмотре), после второго запуска мы получаем правильно набранные перекрестные ссылки (строка 2 в предварительном просмотре).

Вот код:

%! *latex filter.tex
%! Run me twice or even more times to get references right.
\def\myprefixis{malipivo}

\documentclass[a4paper]{article}
\usepackage{tikz}
\usepackage[numbers]{kantlipsum}
\newcount\mycounter 
\mycounter=0
% Our own marker...
% Save the reference and mark it...
\def\writeme{%
\global\advance\mycounter by 1%
\label{\myprefixis\the\mycounter}%
\begin{tikzpicture}%
\begin{pgfinterruptboundingbox}%
\node[red,font=\bfseries\Huge,scale=4]{\pageref{\myprefixis\the\mycounter}};%
\end{pgfinterruptboundingbox}%
\end{tikzpicture}%
  }% End of \writeme...

\begin{document}
\section{My experiment}
\kant[1-2]\writeme
\kant[14]\writeme
\kant[15-16]\writeme
\kant[25]\writeme\ There is a typo and\writeme\ one more\writeme
\kant[26-32]\writeme
\kant[46]\writeme
\begin{figure}[p]
\kant[51] I am floating.\writeme
\end{figure}
\kant[52-61]\writeme
\end{document}

filter.pdf после первого запуска filter.pdf после второго запуска

Содержимое файла filter.auxвыглядит так:

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}My experiment}{1}}
\newlabel{malipivo1}{{1}{1}}
\newlabel{malipivo2}{{1}{1}}
\newlabel{malipivo3}{{1}{2}}
\newlabel{malipivo4}{{1}{2}}
\newlabel{malipivo5}{{1}{2}}
\newlabel{malipivo6}{{1}{2}}
\newlabel{malipivo7}{{1}{4}}
\newlabel{malipivo8}{{1}{4}}
\newlabel{malipivo10}{{1}{7}}
\newlabel{malipivo9}{{1}{8}}

На самом деле вы можете видеть, что есть некий плавающий объект, поскольку числа меняются местами с их эквивалентами номеров страниц ( malipivo10<-> malipivo9; 7<-> 8). Нам нужно как-то обработать этот вспомогательный файл и извлечь страницы из filter.pdf. Я использовал LuaTeX и pdfpagesпакет.

  • Сначала я думал, что нужно отсортировать строки, но, похоже, это не нужно, они уже отсортированы по номерам страниц, а не по маркерам.

  • Однако мне нужно было извлечь строки с malipivoвключенным префиксом и пропустить остальные. Я использовал string.findиз LuaTeX. В терминале есть примечание от этого шага, говорящее Testing note on page 1....

  • Затем мне нужно было извлечь номера страниц, чтобы получить 1 1 2 2 2 2 4 4 7 8, я использовал string.gsubфункцию.

  • Мне нужно было убедиться, что каждая страница будет включена только один раз, поэтому 1 2 4 7 8после удаления дубликатов мы должны получить эту последовательность чисел.

  • Следующим моим шагом было добавление запятых, я сделал это во время конкатенации временной строки. Мы добираемся до 1,2,4,7,8.

  • Остальное было относительно просто. Я сделал из этого команду LaTeX, чтобы получить \includepdf[pages={1,2,4,7,8},fitpaper]{filter.pdf}и отправить ее в тело документа.

Я создал простую функцию в LuaTeX с именем testmeс двумя параметрами, первый параметр запрашивает тестируемый файл ( filter), код использует его auxи pdfфайлы, а второй параметр запрашивает префикс, который мы использовали (теоретически префиксов может быть больше), я использовал malipivo. Фактическая верстка выполняется \directlua{testme("filter","malipivo")}.

Я прилагаю код ( filtered.tex), который будет запущен lualatexодин раз, и, наконец, предварительный просмотр выбранных страниц ( filtered.pdf), которые были отмечены в исходном коде ( filter.tex).

%! lualatex filtered.tex
%! run me only once
\documentclass{article}
\usepackage{pdfpages}
\usepackage{luacode}

\begin{document}

\begin{luacode*}
function testme (filtering, thekey)
-- Initializing variables...
local myinput=filtering..".aux" -- Where are the reference?
local myinputpdf=filtering..".pdf" -- Where are the real PDF pages?
local mylines="" -- List of pages to get is?
local mycount=0 -- How many tested reference do we have?
local lastcount="" -- What is the previous reference?
-- The core of the function...
for mylinetemp in io.lines(myinput) do -- Read input file line by line
  test=string.find(mylinetemp,thekey) -- Is a unique key involved? 
  if test~=nil then -- Yes, the key is present there...
    myline=string.gsub(mylinetemp,"\\newlabel{"..thekey..".-}{{.-}{(.-)}}", "%1", 1) -- Find a group with page reference.
    print("Testing note on page "..myline.."...") -- Print information to the terminal
    mycount=mycount+1 -- Increase a number of tested references
    if mycount==1 then -- Save the first value as the starting value
      mylines=myline -- Start the resulting string
      lastcount=myline -- Remember the last occurance
    else -- Add value to the list if not already stored in the list
      if lastcount~=myline then -- But only if last two values differ (aka uniq)
        mylines=mylines..","..myline -- Add comma and value to the string (aka join)
        lastcount=myline -- Remember this occurance as the last one for next testing
      end -- of if not lastcount
    end -- of if mycount
  end -- of if thekey is involved
end -- of myline
-- Print the results and generate PDF via regular typesetting...
local keyresult="\\includepdf[pages={"..mylines.."},fitpaper]{"..myinputpdf.."}"
print(keyresult)
tex.print(keyresult)
end -- of function testme
\end{luacode*}

% The tested file, e.g. filter.{aux,pdf}, and its reference label prefix.
\directlua{testme("filter","malipivo")}

\end{document}

отфильтровано.pdf

решение2

Итак, я написал первый черновик рабочего конвейера.

  • По предложению @ChristianH я использовалpdfкомментарий
  • Для сценария я использовал:
    • pdfminerчитать аннотации PDF
    • pdfrwнаписать выбранные страницы

Код здесь: https://gist.github.com/ivanistheone/219dad11a30efef82b7e

Связанный контент