Определить ориентацию страницы в Postscript - PullRequest
1 голос
/ 13 апреля 2020

Мне нужно добавить белый прямоугольник и текст в нижний левый угол каждой страницы документа PDF с помощью Ghostscript. Для этого я создал следующий скрипт Postscript:

<<
   /EndPage
   {
     2 eq { pop false }
     {
        newpath
        0 0 moveto
        0 20 lineto
        200 20 lineto
        200 0 lineto
        closepath
        %%gsave
        1 setgray
        fill
        %%grestore
        1 setlinewidth
        0 setgray
        stroke

        gsave
        /Times-Roman 9 selectfont              
        30 5 moveto                            
        (My text) show

        grestore
        true
     } ifelse
   } bind
>> setpagedevice

Это хорошо работает в сочетании с командой Ghostscript:

gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=output.pdf my_script.ps input.pdf

Однако, если input.pdf находится в альбомном режиме , затем белый прямоугольник и текст печатаются в верхнем левом углу, а не в левом нижнем. Я могу заставить его работать, добавив:

90 rotate 0 -595 translate

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

/orient currentpagedevice /Orientation get def

Я застрял с этим некоторое время. Любая помощь очень ценится!

(версия Ghostscript - 9.25)

[ОБНОВЛЕНИЕ]

Чтобы проиллюстрировать, как ширина страницы меньше высоты в ландшафтном режиме, вот script.ps, который я использую: https://gist.github.com/irinkaa/9faadf30b3a5a381a0b621d72b712020

А вот input.pdf и output.pdf . Как вы можете видеть, 612.0 - 792.0 печатается внутри выходного файла, показывая, что ширина (612) <высота (792). </p>

Когда я повторно запускаю ту же команду в выходном файле, она печатает ту же Значения ширины и высоты, но затем поле правильно размещается в левом нижнем углу.

Когда я добавляю в скрипт следующее:

/orient currentpagedevice /Orientation get def

Я получаю ошибку, указывающую на ориентацию isn ' установить (если я правильно понимаю):

Error: /undefined in --get--
Operand stack:
   orient   --dict:212/312(ro)(L)--   Orientation
Execution stack:
   %interp_exit   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   false   1   %stopped_push   1999   1   3   %oparray_pop   1998   1   3   %oparray_pop   1982   1   3   %oparray_pop   1868   1   3   %oparray_pop   --nostringval--   %errorexec_pop   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--
Dictionary stack:
   --dict:977/1684(ro)(G)--   --dict:0/20(G)--   --dict:80/200(L)--
Current allocation mode is local
Current file position is 151
GPL Ghostscript 9.25: Unrecoverable error, exit code 1

Ответы [ 2 ]

1 голос
/ 13 апреля 2020

Сначала вы должны обновить свою версию Ghostscript. 9.25 устарела и имеет уязвимости в безопасности.

Во-вторых, вам нужно просмотреть записи / Orientation и / PageSize в словаре устройства страницы. Не только это, но вы должны использовать PageSize, чтобы определить перевод, который вы используете для своей «корректировки». Если вы не находитесь в фиксированном рабочем процессе (и это кажется маловероятным, если вы получаете файлы со смешанной ориентацией), то не следует полагать, что носитель имеет формат A4.

Интерпретатор PDF Ghostscript просматривает MediaBox на каждой странице PDF файл и сбрасывает / PageSize в словаре устройства страницы, чтобы соответствовать MediaBox для страницы. Он (IIR C) никогда не установит / Orientation, если на странице PDF есть запись / Rotate, которая применяется к MediaBox и содержимому страницы.

Так что вам действительно нужно просто посмотреть на ширину и высоту запрошенного носителя, который задается массивом / PageSize в словаре устройства страницы.

Теперь, сказав это ....

Вы говорите, что «даже для страницы в ландшафтном режиме ширина меньше высоты '. Это кажется мне маловероятным, но в отсутствие примера трудно сказать. Кроме того, кому-то сложно давать какие-либо советы.

Я бы посоветовал вам загрузить пример где-нибудь и опубликовать здесь URL, чтобы мы могли посмотреть файл.

О и я действительно рекомендую, чтобы вы не отправляли выходной файл на стандартный вывод. Это может быть удобно для вас, но есть уже определенные функции устройства pdfwrite, которые просто не будут работать, если вы сделаете это (для них требуется, чтобы выходной файл был доступен для поиска), и в будущем может быть больше случаев.

[править]

Ваша проблема в порядке исполнения. Программа в script.ps запускается до интерпретации PDF-файла, затем интерпретируется PDF-файл.

Когда все, что делает ваша программа, это установка процедуры EndPage в словаре устройства страницы, который не проблема, изменения в словаре устройства страницы являются консервативными, они накапливаются, если не перезаписаны.

Так что тот факт, что в ходе интерпретации файла PDF происходят изменения в словаре устройства страницы, не имеет значения (если которые каким-то образом изменили процедуру EndPage).

Но во время работы вашей программы словарь устройства страницы / ключ PageSize имеет ассоциированное значение, которое представляет собой массив, содержащий размер по умолчанию *1032* media size (потому что ничего не произошло, чтобы изменить это еще). Запись PageSize не будет изменена, пока файл PDF не будет интерпретирован. Это означает, что независимо от того, какой размер носителя используется в вашем PDF-файле, ваша программа всегда будет возвращать размер носителя по умолчанию.

Вам необходимо знать фактический размер страницы во время выполнения процедуры EndPage. Поэтому вам нужно исследовать текущий PageSize как часть процедуры EndPage.

Что-то вроде:

<<
   /EndPage
   {
     2 eq { pop false }
     {
       % Get the current page device dictionary and extract the PageSize
       currentpagedevice /PageSize get

       % Load the values from the array onto the stack
       % and discard the array copy returned by the aload operator
       aload pop

       % If width < height (or equal, square page)
       le {
         % Handle a portrait page
       } {
         % Handle a landscape page
       } ifelse
    }ifelse
  } bind
>> setpagedevice

Обратите внимание, что это позволяет избежать создания словарной записи для хранения ширина страницы и высота. Для этого есть несколько причин:

Во-первых, ширина и высота могут быть разными для каждой страницы (особенно в файле PDF).

Во-вторых, вы (в своей программе) не создаете Ваш собственный словарь для хранения этих пар ключ / значение, что означает, что вы используете любой словарь, активный в данный момент. Хотя это в некотором роде приемлемо, как вы это делаете в настоящее время, поскольку userdict будет активен в начале программы, вы не можете узнать, какой словарь находится над стеком словарей при вызове EndPage. Так что просто вставлять значения в любой словарь, который окажется верхним, небезопасно, в итоге вы можете перезаписать ключи с одинаковым именем, что приведет к непредсказуемым побочным эффектам. Аналогично (в соответствии с приведенной ниже ориентацией), если текущий словарь не содержит этих ключей, вы получите неопределенную ошибку. Так что сейчас тебе это удается благодаря удаче.

В-третьих, в PostScript обычно считается лучшей практикой использование стека для временного хранения, а не создание пар ключ / значение в словарях.

По двум последним причинам я настоятельно рекомендую вместо создания ключ с именем stringholder (как в настоящее время делает ваша программа) в любом словаре, который находится на вершине стека словаря в начале программы, и, предполагая, что он будет доступен во время процедуры EndPage, вместо этого вам следует просто создать временную строку вместо этого используйте 10 string.

Например:

/Times-Roman 9 selectfont              
30 5 moveto                            
pagewidth
stringHolder cvs
show

станет:

/Times-Roman 9 selectfont              
30 5 moveto                            
currentpagedevice /PageSize get 0 get
256 string cvs
show

10 цифр, возможно, немного мало, 256 должно быть достаточно для любого и строка будет собирать мусор, поэтому вы не будете терять память или что-то в этом роде.

Что касается ориентации; да, вы правы, и, как я уже говорил, интерпретатор PDF не устанавливает ориентацию в словаре устройства страницы. Если вы попытаетесь get ключ из словаря, который не содержит этот ключ, то вы получите неопределенную ошибку. Если вы не уверены, существует ли ключ в словаре, вам следует сначала проверить его с помощью оператора known.

[EDIT 2]

Как отмечено в комментариях ниже ... Это возможно проверить ориентацию CTM с помощью оператора transform и единичного вектора. Если одна или обе координаты, полученные в результате transform, являются отрицательными, то в CTM участвует вращение, и, изучая знак каждой координаты, мы можем определить, в каком квадранте заканчивается вращение.

Для целей флага / Rotate в PDF этого достаточно, поскольку его можно указывать только с шагом 90 градусов. Вот пример функции, которая определяет вращение, и простой фрагмент PostScript для его осуществления:

%!PS

/R {
1 1 transform

0 ge {
  0 ge {
    (no rotation\n) print
  } {
    (90 degree ccw rotation\n) print
  } ifelse
} {
  0 ge {
    (270 ccw rotation\n) print
  } {
    (180 ccw rotation\n) print
  } ifelse
} ifelse
} bind def

R
gsave
90 rotate R
grestore
gsave
180 rotate R
grestore
gsave
270 rotate R
grestore
gsave
360 rotate R
grestore

Можно использовать эту технику, чтобы решить, был ли повернут исходный файл, и затем выбрать иметь процедура EndPage ведет себя по-другому.

0 голосов
/ 14 апреля 2020

"но я не могу определить, когда страницы находятся в ландшафтном режиме или в портретном режиме"

$ gs -sDEVICE=bbox -dNOPAUSE -dBATCH input.pdf | grep %B 
%%BoundingBox: -1 0 842 596
%%HiResBoundingBox: -0.008930 0.018000 841.988998 595.223982
%%BoundingBox: -1 0 842 596
%%HiResBoundingBox: -0.008930 0.018000 841.988998 595.223982

Тогда вы можете иметь script-portrait.ps и script-landscape.ps в зависимости от ситуации .

РЕДАКТИРОВАТЬ: Я согласен с KenS. Вывод ghostscript pdfwrite создает макет, отличный от исходного pdf, созданного Acrobat Distiller 10.1.1 (Windows). Я обнаружил эту разницу даже без включения скрипта EndPage.

...