Что такое словари PostScript и как их можно получить (через Ghostscript)?

Обычно я смотрю ghostscript как инструмент командной строки; однако я никогда не перестаю удивляться огромному количеству настроек и вариантов присутствия там, что связано с тем, что ghostscript - это полный интерпретатор языка PostScript (который я часто забываю).

Например, в Запрос Ghostscript для параметров/настроек по умолчанию для устройства вывода (например, "pdfwrite" или "tiffg4" ); вы узнаете, как извлекать параметры по умолчанию для данного устройства вывода. Однако то, что я хотел бы знать, - это эти параметры, связанные с так называемыми словарями PostScript?

Или, другими словами, что такое словари PostScript; и какие средства ghostscript имеют, чтобы запросить (и возможно) изменить эти данные?

Ответ 1

Проще говоря: В PostScript словарь представляет собой список пар ключей (name) + value. Словари позволяют интерпретатору PostScript искать, существует ли ключ и извлекать его чтобы использовать его в любой процедуре. Интерпретатор также может создавать ключи, сохранять или изменять значения и даже создавать полные пользовательские словари (продиктованные его кодом PostScript). Ключи обычно имеют имя типа (но они могут быть любого другого типа, кроме исключения).

Два из этих словарей всегда должны присутствовать для любой реализации интерпретатора PostScript:

  • systemdict В нем содержатся предопределенные операторы PostScript (и реализации, чтобы заставить их делать то, что ожидает от них спецификация PostScript).

  • userdict В нем содержатся переменные и процедуры программы PostScript (подумайте о "процедурах" как о функциях или подпрограммах, которые создаются комбинацией определяемых языком операторов и определяемых программой значений и параметров).

Одно слово об именах: имена - это то, что для других языков программирования являются идентификаторами uniq (и они чувствительны к регистру). Эти идентификаторы могут быть именами переменных или процедур. Они могут состоять из любой комбинации из 256 символов ASCII (но они не являются строками).

Как вам известно, PostScript - это ориентированный на стек язык. Он использует несколько стеков:

  • стек операнда Этот стек содержит каждый отдельный операнд и каждый результат промежуточных операций (временный переход последнего результата в самый верхний элемент стека операндов).

  • стек словаря Как сказано в названии: в этом стеке есть только словари. Таким образом, стек определяет текущий контекст для поиска ключей/имен.

  • исполняемый стек В этом есть исполняемые объекты, то есть в основном процедуры и файлы, которые в настоящее время исполняются. Если интерпретатор прерывает выполнение текущего объекта, он помещает прерванный объект в этот стек. После того, как объект был полностью выполнен, он удаляется из стека, и выполнение продолжается с той, которая теперь самая верхняя.

  • стек состояния графики Этот стек содержит текущий контекст для извлечения графических элементов: текущая настройка ширины линии, текущий шрифт, текущий цвет или значение оттенков серого, текущий путь... Текущие графические состояния могут быть сохранены (gsave) и восстановлено ( grestore) позже. Самое высокое графическое состояние всегда является текущим графическим состоянием.

Все эти стеки независимы друг от друга. Однако стеки операнда, словаря и графического состояния находятся под управлением программы PostScript (то есть ею может управляться). Выполняющий стек является единственным свойством интерпретатора.

Для каждого стека существуют определенные ограничения (как для количества элементов, которые могут быть сохранены на нем, и т.д.). PostScript знает операторы, которые могут манипулировать стеками: поместить новый элемент в стек, удалить самый верхний элемент (pop), дублировать самый верхний элемент ( dup), перетасуйте порядок элементов в стеке ( roll), поменяйте два элемента верхнего уровня ( exch) и еще несколько ( хорошим вступлением в программирование PostScript является "Bluebook" от Adobe).

Как я уже сказал, словари имеют свой собственный стек, который содержит все словари, которые может использовать интерпретатор PostScript.

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

systemdict всегда самый нижний; выше это userdict. Эти два не могут быть удалены из стека словаря, поскольку все остальные могут быть подчинены любому оператору манипуляции стеком (например, pop, который удаляет самый верхний элемент из стека).

Всякий раз, когда интерпретатор ищет имя, он ищет словари для этого имени, начиная с самого верхнего словаря. Следовательно, userdict выполняется поиск до systemdict. Как только имя будет найдено (ключ), интерпретатор прекратит поиск и использует этот ключ (вернее, значение, которое он имеет). Следствием этой архитектуры является то, что программист PostScript может перезаписать любой оператор PostScript, который предварительно определен в systemdict с его собственным вариантом.

Кроме того, некоторые словари могут быть для программы PS 'private' (без доступа, например, словари шрифтов) или "только для чтения".


Обновление - Дополнительные ответы:

Ответ 2

Другие ответы уже рассмотрели "Что такое словари?" часть вашего вопроса. Теперь позвольте мне обратиться к "Как Ghostscript может их получить?"

Может быть, вопрос скорее должен быть: "Как я могу (мощный пользователь, разработчик, выродка...) получить к ним доступ?"

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

Для этого вам нужно знать только название соответствующего словаря.

Посмотрим на один интересный такой внутренний словарь Ghostscript, называемый .distillersettings:

gs \
 -dNODISPLAY \
 -c ".distillersettings {exch ==only ( ) print ==} forall quit"

Результат:

/default -dict-
/prepress -dict-
/PSL2Printer -dict-
/ebook -dict-
/screen -dict-
/printer -dict-

Это может не сказать вам многого на первый взгляд. Но вы можете распознать некоторые ключевые имена в этом словаре: /prepress, /printer, /screen, /ebook...

Все это можно использовать в командной строке Ghostscript, чтобы запросить заданный набор параметров, когда вы хотите, чтобы результат был сделан с помощью -sDEVICE=pdfwrite (функция Ghostscript 'Distiller'-alike). Чтобы запросить такой набор настроек, просто добавьте -dPDFSETTINGS=/printer в командную строку.

На втором взгляде теперь вы увидите, что содержание словаря .distillersettings по существу представляет собой набор из 6 дополнительных словарей. Это словарь словарей.

Содержание словаря не распечатывается по умолчанию (не с кодом PostScript выше). Но если вы хотите их, вы можете использовать процедуру, описанную в Ghostscript, под названием === вместо стандартного оператора языка PostScript == в приведенной выше команде. Эта процедура ведет себя так же, как == execpt, что она также расширяет словари и печатает все пары ключей: значения, содержащиеся в них.

Будьте осторожны с процедурой ===: -dict-, которую вы пытаетесь развернуть, может быть долгое время veeeeeery и может привести к тому, что вы потеряете свое зрение.:-)

В нашем текущем случае, однако, он все еще управляем:

gs \
 -dNODISPLAY \
 -c ".distillersettings {exch ==only ( ) print ===} forall quit"

Выход теперь:

 /default << /Optimize false /DoThumbnails false /PreserveEPSInfo true /ColorConversionStrategy /LeaveColorUnchanged /DownsampleMonoImages false /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments true /GrayACSImageDict << /HSamples [2 1 1 2] /VSamples [2 1 1 2] /QFactor 0.9 /Blend 1 >> /DownsampleColorImages false /PreserveOverprintSettings true /CreateJobTicket false /AutoRotatePages /PageByPage /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /HSamples [2 1 1 2] /VSamples [2 1 1 2] /QFactor 0.9 /Blend 1 >> /DownsampleGrayImages false /UCRandBGInfo /Preserve >>
 /prepress << /DoThumbnails true /MonoImageResolution 1200 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo true /ColorConversionStrategy /LeaveColorUnchanged /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Error /PreserveOPIComments true /GrayImageResolution 300 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 300 /PreserveOverprintSettings true /CreateJobTicket true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /NeverEmbed [] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Preserve >>
 /PSL2Printer << /DoThumbnails false /CompatibilityLevel 1.2 /TransferFunctionInfo /Preserve /MonoImageResolution 1200 /PreserveEPSInfo true /CompressFonts true /ColorImageDownsampleType /Bicubic /GrayImageDownsampleType /Bicubic /ColorConversionStrategy /LeaveColorUnchanged /EmbedAllFonts true /ColorACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CannotEmbedFontPolicy /Error /PreserveOPIComments true /CompressPages true /GrayImageResolution 600 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 600 /PreserveOverprintSettings true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /ASCII85EncodePages true /MaxViewerMemorySize 8000000 /NeverEmbed [] /PreserveHalftoneInfo true /UCRandBGInfo /Preserve >>
 /ebook << /DoThumbnails false /MonoImageResolution 300 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo false /ColorConversionStrategy /sRGB /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments false /GrayImageResolution 150 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /ColorImageResolution 150 /PreserveOverprintSettings false /CreateJobTicket false /AutoRotatePages /All /MonoImageDownsampleType /Bicubic /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Remove >>
 /screen << /DoThumbnails false /MonoImageResolution 300 /ColorImageDownsampleType /Average /PreserveEPSInfo false /ColorConversionStrategy /sRGB /GrayImageDownsampleType /Average /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments false /GrayImageResolution 72 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /ColorImageResolution 72 /PreserveOverprintSettings false /CreateJobTicket false /AutoRotatePages /PageByPage /MonoImageDownsampleType /Average /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /CompatibilityLevel 1.3 /UCRandBGInfo /Remove >>
 /printer << /DoThumbnails false /MonoImageResolution 1200 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo true /ColorConversionStrategy /UseDeviceIndependentColor /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments true /GrayImageResolution 300 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.4 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 300 /PreserveOverprintSettings true /CreateJobTicket true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /NeverEmbed [] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.4 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Preserve >>

Все еще не так хорошо. Так что попробуй лучше. Способ, которым мы можем это сделать, - изменить наш код PostScript: теперь мы скажем, чтобы он получил доступ к словарю .distillersettings и получил значение одного из его ключей (используйте /screen). Поскольку мы знаем, что значение - это еще один словарь, мы знаем, что мы получим еще один набор ключей: пары значений, которые мы будем форматировать так же, как и раньше:

gs \
 -q \
 -dNODISPLAY \
 -c ".distillersettings /screen get {exch ==only ( ) print ===} forall quit"

Теперь это выглядит лучше, не так ли? Смотрите сами:

/DoThumbnails false
/MonoImageResolution 300
/ColorImageDownsampleType /Average
/PreserveEPSInfo false
/ColorConversionStrategy /sRGB
/GrayImageDownsampleType /Average
/EmbedAllFonts true
/CannotEmbedFontPolicy /Warning
/PreserveOPIComments false
/GrayImageResolution 72
/GrayACSImageDict -dict-
/ColorImageResolution 72
/PreserveOverprintSettings false
/CreateJobTicket false
/AutoRotatePages /PageByPage
/MonoImageDownsampleType /Average
/NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica     /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats]
/ColorACSImageDict -dict-
/CompatibilityLevel 1.3
/UCRandBGInfo /Remove

Как уже заметил ваш острый глаз: некоторые из ключевых значений снова являются словарями. Вы можете снова использовать приведенную выше команду, на этот раз с помощью === вместо второго ==, чтобы разрешить тайны, которые /GrayACSImageDict -dict- и тому подобное могут скрывать...

В любом случае теперь вы знаете, что вы сохраняете при вводе, просто используя -dPDFSETTINGS=/screen вместо перечисления всех отдельных параметров, встроенных в этот словарь /screen...

И вы также знаете, какое единственное значение вам нужно переопределить, если вы хотите получить общее качество экрана, но с той разницей, что шрифты all встроены:

gs \
 -o out.pdf \
 -sDEVICE=pdfwrite \
 -dPDFSETTINGS=/screen \
 -c "<</NeverEmbed [ ] /AlwaysEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats]>> setdistillerparams" \
 -f input.pdf

Вы можете изучить много интересного в этом отношении о внутренних функциях Ghostscript, если только вы знаете название используемых им словарей.: -)

Ответ 3

Уже много хороших ответов, но никто не упоминал об этом:

При вызове ghostscript параметры -d и -s создают исходные определения в systemdict. Это позволяет вам параметризовать вызов вашей программы postscript.

Используйте -dname[=token], чтобы установить значение null, или число (или любой другой токен постскриптума). Используйте -sname=string для установки строкового значения (которое в большинстве контекстов работает так же, как и имя).

И вы можете в любой момент управлять всеми стеками с помощью правильных операторов.

  • token нажмите на стеки операндов из строки или файла (это то, что использует цикл интерпретатора для использования потока программы, так что это то, что вы используете, вводя код через файл или прямо с клавиатуры)
  • pop отказаться от стека операндов
  • begin push to dict stack
  • end pop from dict stack
  • run, exec, %procedure-invocation нажмите на стек exec
  • exit, stop pop или очистить стек exec
  • gsave push gstate в графическом стеке
  • grestore стек поп-графики
  • save нажмите копию всех VM-содержимого (все dicts и массивы, но не строки)
  • restore перемотать память в сохраненное состояние (вернуть все dicts и массивы в предыдущее состояние)

Словари, являющиеся составными объектами, наследуют ряд операторов, общих для всех составных объектов.

  • -typename- создать объект, например dict
  • length сообщить размер объекта
  • put вставить элемент
  • get извлечь элемент
  • copy заполняет объект содержимым из другого объекта
  • forall сделать что-то для каждого элемента
  • *load альтернативный элемент извлечения (для словарей load выполняет поиск с where, а затем get; для массивов aload разливает все содержимое массива в стеке операндов)
  • *store альтернативный элемент вставки (для словарей, store выполняет поиск с where, а затем a put, если найден, или def if not; для массивов astore заполняет массив из объектов в стеке)

К этому набору словари добавляют

  • def помещается в текущий словарь (верхняя часть стека dict)
  • known словарь запросов для элемента
  • where запросить все словари для элемента
  • maxlength уже не интересен после того, как PS Level 2 добавил авторасширяющиеся словари и gc
  • dictstack скопируйте dictstack в массив (возможно, вы хотите искать снизу вверх, вы можете!)
  • имена, не предшествующие косой чертой /, автоматически load ed и, если выполняются, выполняются
  • //, а token создает объект постскриптума. Все имена, которым предшествует двойная слэш, load ed и заменяются в массиве процедур. Это очень мощно, так как вы можете имитировать макросы Lisp.

Изменить: Еще одна вещь. При создании словаря существует компромисс между временем и пространством, когда вы выбираете размер словаря. Словари почти наверняка реализованы как хеш-таблица (во всех, кроме простейших интерпретаторов), и большинство хеш-функций могут избежать столкновений, когда таблица примерно наполовину (Правило большого пальца: Используйте двумерные дикты для скорости). Поскольку уровень 2, конечно, словари будут расти автоматически, когда вы добавите размер + 1 элемент, предположительно, путем размещения нового словаря размера k * (где k, вероятно, 1,5 или 2); но контроль размеров вручную может дать вам ускорение скорости. На уровне 1, если вы не умножаете ссылки на словари, вы можете установить замену для dictfull в errordict, чтобы увеличить dict и повторно выполнить put (или def или что-то еще). Поскольку уровень 2 делает это внутри, он может заменить все ссылки.

Ответ 4

Если вы хотите получить список других словарей, которые содержатся в словарях systemdict и userdict, просто запустите:

for _dict in userdict systemdict; \
   do \
   gs \
     -dNODISPLAY \
     -c "${_dict} {exch ==only ( ) print ==} forall quit"; \
done \
| awk '{print $1, $2}' \
| grep -- -dict- \
| sort

Это приведет к сортированному списку имен словарей, которые вы могли бы исследовать для потенциально "интересных" имен.

Вы найдете такие имена, как Fontmap, localdict, AdobeGlyphList, userparams, .eexec_param_dict, .substitutefamilies, EncodingDirectory, colorspacedict, .distillerparamkeys, devicedict, .symbol_list,...

С каждым из этих имен вы можете найти более или менее интересную информацию и лакомые кусочки о внутренних функциях Ghostscript, запустив f.e.:

gs \
  -q \
  -dNODISPLAY \
  -c "Fontmap {exch ==only ( ) print ==} forall quit"

Как вы можете видеть, даже Fontmap, используемый Ghostscript, хранится в словаре. Вывод моих результатов здесь локально:

[....]
/Arial [/ArialMT]
/Arial,Bold [/Arial-BoldMT]
/AvantGarde-Book [/URWGothicL-Book]
/Bookman-Demi [/URWBookmanL-DemiBold]
/Calligraphic-Hiragana [(fhirw.gsf)]
/Calligraphic-Katakana [(fkarw.gsf)]
/Charter-Bold [/CharterBT-Bold]
/CharterBT-Bold [(bchb.pfa)]
/Courier [/NimbusMonL-Regu]
/Courier-Bold [/NimbusMonL-Bold]
/Courier-BoldOblique [/NimbusMonL-BoldObli]
/Courier-Oblique [/NimbusMonL-ReguObli]
/Helvetica [/NimbusSanL-Regu]
/Helvetica-Bold [/NimbusSanL-Bold]
/NewCenturySchlbk-Bold [/CenturySchL-Bold]
/Palatino-Roman [/URWPalladioL-Roma]
/Symbol [/StandardSymL]
/Times-Bold [/NimbusRomNo9L-Medi]
/TimesNewRoman,Bold [/TimesNewRomanPS-BoldMT]
/Utopia-Regular [(putr.pfa)]
/ZapfDingbats [/Dingbats]
[....]

Примечание: Вышеупомянутый не является фактически файловым форматом, так как вам нужно будет использовать, когда вы хотите использовать файл Fontmap , который должен использовать Ghostscript (в общего или для конкретной работы). Для этого формата прочитайте комментарии в примере файла Fontmap, отправленного Ghostscript. Вышеприведенный список представляет собой представление fontmap как хранилища Ghostscript в его внутреннем словаре.

Ответ 5

Dictionaires в PostScript являются объектом "container", они по сути представляют собой список пар, ключ и значение. Дополнительную информацию см. В Справочном руководстве по языку PostScript, особенно в разделе 3.3.9 в третьем издании.

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

Словари могут иметь разрешения доступа, поэтому можно иметь словари только для чтения, чьи значения могут быть проверены, но не изменены, а словари шрифтов могут быть "без доступа", чтобы предотвратить извлечение данных контура в PostScript.

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