всего книг 1544
новинок 161
бестселлеров 239

Издание: Эффективная работа: UNIX

Глава 13

Язык PostScript

  • Язык описания страниц PostScript
  • Шрифты и вывод текста
  • Задания для практической работы

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

    Язык описания страниц PostScript

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

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

    Координаты точек на странице задаются вещественными числами в полиграфических единицах длины — пунктах.

    ПРИМЕЧАНИЕ. Используемая в полиграфии в Великобритании и США единица длины «пункт» равна 1/72 дюйма, то есть примерно 0,353 мм. Именно эта единица применяется в языке PostScript, разработанном в США. В континентальной Европе (включая Россию) полиграфический пункт равен примерно 0,376 мм.

    Язык PostScript был создан в середине 80-х годов прошлого века под руководством Дж. Ванока и Ч. Гешке, основателей компании Adobe Systems Inc. Название PostScript является зарегистрированной торговой маркой фирмы Adobe Systems.

    Виртуальная ЭВМ и интерпретатор языка

    В языке PostScript предполагается существование виртуальной ЭВМ, на которой работает интерпретатор языка и к которой подключено устройство вывода. Виртуальная ЭВМ имеет определенные ресурсы, важнейшим из которых является оперативная память. Интерпретатор последовательно считывает из PostScript-файла команды и формирует изображение страницы в памяти виртуальной ЭВМ. Команда showpage выводит готовое изображение на устройство вывода. Устройством вывода обычно является принтер.

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

    Комментарии в программах на PostScript

    Комментарии в языке PostScript играют важную роль и на то есть две причины:

    1. По комментарию, расположенному в первой строке файла, ОС UNIX и прикладные программы определяют его тип.

    2. Структурные комментарии используются для выделения элементов логической структуры PostScript-файла и интерпретируются программами просмотра/преобразования.

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

    Комментарий начинается символом % и продолжается до конца строки, например:

    %!PS-Adobe-1.0
    %%BoundingBox: 18 18 594 774
    /Times-Roman findfont % найти прямой светлый шрифт Times
    

    Для того чтобы подсистема печати или прикладная программа смогли правильно определить тип файла, его первая строка должна начинаться комментарием:

    %!

    Это «магическое число» PostScript-файла. Данный комментарий является обязательным. После символов %! в той же строке может следовать текст PS–Adobe–N.M. Цифры N.M (например, 1.0) определяют версию соглашения о структурных комментариях (это специальный документ, содержащий правила оформления структурных комментариев).

    Структурные комментарии начинаются символами %%. Если программа содержит такие комментарии, она соответствует договоренностям о структуре PostScript-файлов. После %% без пробела следуют зарезервированные (ключевые) слова. Регистр букв в них имеет значение. При необходимости с ключевым словом может быть указано значение, которое отделяется от ключевого слова двоеточием и пробелом.

    PostScript-файл состоит из трех частей: пролога, тела и эпилога. В прологе содержатся описания подпрограмм и данные, необходимые для печати документа. В теле PostScript-файла содержатся только команды, формирующие страницы документа. Эпилог не содержит никаких команд.

    Структурные комментарии делятся на три группы:

    1. Комментарии в заголовке программы (перед прологом).

    2. Комментарии в теле программы.

    3. Завершающие комментарии (в эпилоге).

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

    Таблица 13.1. Структурные комментарии версии 1.0

    КомментарийОписание
    Комментарии в заголовке программы
    %!PS–Adobe–1.0 Начало файла (обязателен)
    %%DocumentFonts: шрифт… Перечень всех шрифтов, используемых в документе. Названия
    %%DocumentFonts: (atend)шрифтов в списке разделяются пробелами (обязателен)
    %%Pages: число Общее число страниц в документе (обязателен)
    %%Pages: (attend)
    %%BoundingBox: x0 y0 x1 y1 Координаты границ рисунка (обязателен)
    %%BoundingBox: (atend)
    %%Title: текстНазвание документа
    %%Creator: текстАвтор документа
    %%EndComments Конец комментариев в заголовке программы
    Комментарии в теле программы
    %%EndProlog Конец пролога и начало тела файла (обязателен)
    %%Page: метка числоНомер страницы (обязателен). Находится в начале описания страницы
    %%PageFonts: шрифт…Перечень шрифтов, используемых на данной странице
    %%TrailerКонец последней страницы
    Завершающие комментарии
    %%DocumentFonts: шрифт… Перечень шрифтов, используемых в документе. Названия шрифтов в списке разделяются пробелами
    %%Pages: числоЧисло печатных страниц
    %%BoundingBox: x0 y0 x1 y1Координаты границ рисунка

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

    Комментарий %%BoundingBox определяет положение изображения на странице. Четыре целых числа — это координаты левого нижнего и правого верхнего углов прямоугольника, содержащего изображение. В многостраничном документе прямоугольник должен охватывать все изображения. Этот комментарий важен для программ, которые включают изображение из одного файла в другой файл. Если точно определить размер изображения невозможно, следует указать заведомо большие значения, например соответствующие размеру листа бумаги формата А4:

    %%BoundingBox: 0 0 595 842

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

    Комментарием %%EndProlog завершается раздел описаний PostScript-файла и начинается тело документа. Для того чтобы PostScript-файлы с дополнительными шрифтами, включаемыми из файлов, удовлетворяли соглашению о структурных комментариях, необходимо имена файлов .pfa включить перед комментарием %%EndProlog.

    Комментарий %%Page открывает ту часть файла, которая описывает печать очередной страницы. У него два аргумента — метка страницы и ее номер. Метка представляет собой последовательность любых символов, кроме пробела, и на печать не выводится. Номер страницы задается числом начиная с 1, например:

    %%Page: Title 1
    %%Page: i-ii 2
    %%Page: iii-iv 3

    Комментарий %%PageFonts, если он имеется, должен идти сразу после %%Page. Очередной комментарий %%Page обозначает конец предыдущей страницы и начало следующей. Конец последней страницы отмечается комментарием %%Trailer.

    В листинге 13.1 приведен пример PostScript-файла, удовлетворяющего соглашению о структурных комментариях.

    Листинг 13.1. Пример PostScript-файла

    %!PS-Adobe-1.0
    %%Title: Пример комментариев
    %%Creator: А. В. Комолкин
    %%Pages: (atend)
    %%DocumentFonts: Courier TimesNRCyrMT-Inclined TimesNRCyrMT
    %%BoundingBox: 10 310 535 460
    %%EndComments
      % Начало пролога. Здесь определяются многократно используемые переменные:
      %  - со шрифтами:
      /TimesNRCyrMT findfont 40 scalefont /tr40 exch def % кириллический шрифт
      /TimesNRCyrMT-Inclined findfont 30 scalefont /ti30 exch def
      %  - с часто используемыми строками текста:
      /title (Эффективная работа в ОС UNIX) def
      /authors (А.В.Комолкин, С.А.Немнюгин, М.П.Чаунин) def
      /section (\24713   PostScript Language) def
      %  - с процедурами:
      /frame {                   % Начало первой процедуры
               gsave             % сохранение параметров принтера
               newpath           % начало графического пути
                  15 315 moveto  % Форматировать текст следует
                 530 315 lineto  % так, чтобы была видна структура
                 530 455 lineto  % программы и чтобы было удобно ее
                  15 455 lineto  % читать
               closepath         % замыкание графического пути
               1 setlinewidth    % установка толщины линии
               [3 3] 0 setdash   % установка типа пунктирной линии
               stroke            % построение линии вдоль пути
               grestore          % восстановление параметров принтера
             } def    % Конец первой процедуры
      /cm {72 mul 2.54 div} def  % Вторая процедура целиком уместилась в строке
      % Можно проводить вычисления, использовать переменные и команды:
      tr40 setfont        % - устанавливать шрифт
      title stringwidth   % - вычислять длину строк
      pop                 % - работать со стеком
      ti30 setfont        % - устанавливать другой шрифт
      authors stringwidth % - вычислять длину строк, печатаемых другим шрифтом
      pop div             % - проводить арифметические вычисления
      /rel exch def       % - определять переменные с вычисленными значениями
    %%EndProlog
    %%Page: Title 1
      % Начало построения изображения на титульной странице.
      % В самом начале надо использовать:
      gsave           % - команду сохранения установок принтера
      % Далее можно использовать:
      frame           % - вызовы процедур
      20 400 moveto   % - команды перемещения
      tr40 setfont    % - переменные, хранящие шрифты; команды установки шрифта
      title show      % - переменные, хранящие строки; команды печати строки
      20 340 moveto
      rel 1 scale     % - числовые переменные; команды изменения масштаба
      ti30 setfont
      authors show
      % После того, как изображение построено, указывается:
      grestore        % - одна из команд восстановления установок принтера
      showpage        % - команда вывода изображения
    %%Page: 1 2
      % Начало построения изображения на странице 1.
      % В самом начале надо указать:
      save            % - одну из команд сохранения параметров принтера
      % Далее может выполняться:
      frame           % - вызов процедур
      gsave           % - сохранение текущих установок принтера
        20 400 moveto       % Форматирование текста можно выполнять так,
        tr40 setfont        % чтобы его было легче читать и исправлять.
        title stringwidth   % - определение длины строки на бумаге
        rmoveto             % - относительное перемещение
        -1 1 scale          % - изменение направления оси X
        title false charpath% - добавление контуров букв к графическому пути
        0.1 setlinewidth    % - установка толщины линии
        stroke              % - вывод линии вдоль контуров символов
      grestore        % - восстановление установок принтера
      20 340 moveto
      ti30 setfont
      gsave
        rel 2.0 scale     % - произвольное сжатие и растяжение осей координат
        authors show
      grestore
      gsave
        0 -1 rmoveto
        rel -1.0 scale
        0.5 setgray         % - изменение цвета выводимых объектов
        (А. В. ) stringwidth rmoveto  % Использование новых строк
        (Комолкин) show
        (, С. А. ) stringwidth rmoveto
        (Немнюгин) show
        (, М. П. ) stringwidth rmoveto
        (Чаунин) show
      grestore
      restore  % Восстановление параметров принтера
      showpage % печать изображения на бумаге
    %%Page: 2 3
    %%PageFonts: Courier
      gsave
      frame
      20 400 moveto
      /Courier findfont 33.7 scalefont setfont % Определение нового шрифта
      section show
      20 340 moveto
      (Printing standard in UNIX) show
      grestore
      showpage
    %%Trailer
    %%Pages: 3
    Документ в листинге 13.1 содержит 3 страницы с названиями Title, 1 и 2. Текст печатается шрифтами Courier и TimesNRCyrMT в прямоугольной области шириной 525 пунктов и высотой 150 пунктов (18,5 см ґ 5,3 см). На последней странице используется только шрифт Courier. Для печати документа необходимо загрузить в принтер шрифты из файлов times8.pfa и timesi8.pfa, что можно сделать командой:
    # cat times8.pfa timesi8.pfa listing-13.1.ps | lpr
    

    Страницы этого документа приведены на рис. 13.1, 13.2 и 13.3.

    ПРИМЕЧАНИЕ. Некоторые принтеры имеют аппаратные ограничения размера печатаемой страницы. Поля в этом случае могут достигать 18 пунктов, а в нашем примере рамка печатается с отступом 15 пунктов от левого края. Если левая часть рамки не печатается, можно передвинуть все изображение на несколько пунктов правее. Это делается командами изменения системы координат.


    Рис. 13.1. Титульная страница примера из листинга 13.1


    Рис. 13.2. Первая страница примера из листинга 13.1


    Рис. 13.3. Вторая страница примера из листинга 13.1

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

    При печати страниц документа следует избегать побочных эффектов, связанных с изменением значений переменных, которые используются при описании следующих страниц. Сделаем опыт. Пусть в нашем примере необходимо напечатать только страницу 2. Выделим из файла пролог и команды печати последней страницы и запишем их в другой файл. При этом следует поменять общее число страниц в комментарии %%Pages в конце файла на 1. В результате получим:

    %!PS-Adobe-1.0
    … % здесь должен быть текст пролога
    %%EndProlog
    %%Page: 2 1
    … % здесь должно быть описание 2 страницы
    %%Trailer
    %%Pages: 1
    

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

    Элементы языка

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

    В словах кроме обычных, алфавитно-цифровых символов можно использовать специальные символы. Специальными символами являются скобки трех видов (, ), [, ], { и }, символы <, >, / и %.

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

    В PostScript используется обратная (польская) запись, при которой сначала указываются операнды, а затем оператор, выполняющий с ними действия. Например, запись:

    4 5 add
    представляет собой команду сложения чисел 4 и 5.

    Стеки

    Использование польской записи объясняется тем, что все операции в PostScript выполняются над операндами в стеке. Стек представляет собой специальную временную область памяти, организованную по принципу «последний вошел — первый вышел». Если в пустой стек последовательно записываются три числа 1, 2 и 3, число 1 будет находиться в вершине стека, а 3 — в его нижней части. Значение, находящееся в самом низу стека, будем называть текущим или последним значением в стеке. Условимся записывать значения, находящиеся в стеке, слева направо. Самое левое значение находится в вершине стека, а самое правое — текущее:

    1 2 3

    Команда выбирает последнее значение, удаляя его из стека. После этого текущим становится значение, бывшее ранее предпоследним. Таким образом, находящиеся в стеке значения извлекаются из него в обратной последовательности: 3, 2 и 1.

    Интерпретатор PostScript последовательно считывает файл, интерпретируя очередное слово. Если слово представляет собой команду, она немедленно выполняется, а если это некоторый объект данных, он заносится в стек. Таким образом, приведенный пример интерпретируется так:

    1. Слово 4 является константой, поэтому она помещается в стек.

    2. Слово 5 является константой, поэтому она помещается в стек.

    3. Слово add является командой. Она выбирает из стека два числа, складывает их, и результат помещает в стек.

    4. В стеке после выполнения команды остается число 9.

    В PostScript для хранения данных разных типов используются четыре стека:

  • стек операндов — содержит все объекты данных: числа, строки, массивы, процедуры, имена и т. д. Большинство операций выполняется над данными из этого стека;
  • стек графических контекстов — используется при работе с графическими объектами;
  • стек исполнения — рассматривается ниже;
  • стек словарей — содержит словари, в том числе словари со шрифтами. Обязательно содержит словари ошибок, системный и пользовательский. Подробнее о словарях рассказывается ниже.

    Типы данных

    Основные типы данных языка PostScript:

  • целое число со знаком;
  • вещественное число;
  • логическое значение;
  • строка символов;
  • массив произвольных объектов, в том числе и разнотипных;
  • процедура;
  • словарь.

    ПРИМЕЧАНИЕ. В PostScript сложные объекты данных (массивы, строки, процедуры, словари и некоторые другие) состоят из двух частей — тела объекта и ссылки (указателя) на него. Тело объекта хранится в специальной области памяти, не в стеке и не в переменной. Операции со сложными объектами выполняются с использованием ссылок. Если говорят, что в стек записывается массив, на самом деле в стек записывается ссылка на него. Команды создания строки ( ), массива [ ] и процедуры { } создают объекты в специальной области памяти, а в стек заносят только ссылку на объекты. Команда def записывает в переменную только ссылку на объект.

    Целые константы задаются в десятичном формате (25, +17, –5) или в формате без знака с указанием основания системы счисления (8#377 — восьмеричное, 2#101 — двоичное). Шестнадцатеричные константы записываются в угловых скобках: <01FF>.

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

    2.0, +10.5, –4.12, 1., –.1
    без указателя порядка и
    1e3, 1.2E-2
    с указателем порядка (1ґ103 и 1,2ґ10-2 соответственно).

    Логические константы принимают только два значения: true и false («истина» и «ложь»).

    Строка записывается в круглых скобках, например:
    (это строка)
    Скобки играют роль ограничителей. Строка может содержать пробелы, специальный символ конца строки, любые другие символы, а также экранированные последовательности, начинающиеся символом \. Обратная косая черта записывается в виде \\, а открывающая и закрывающая скобки — в виде \( и \). Вместо символов можно использовать их восьмеричные коды: \xxx, где xxx принимает значения от 000 до 3778. В прологе программы из листинга 13.1 в 16-й строке запись \24713 обозначает восьмеричный код \247 символа § в стандартной кодировке PostScript-шрифтов, а также цифры 1 и 3, то есть §13. Строковое значение можно перенести на следующую строку PostScript-файла, при этом специальный символ конца строки будет включен в значение строкового объекта. В этом случае необходимо применить экранирование обратной косой чертой:
    (Post\Enter
    Script)
    Русские буквы можно включать в текст PostScript-файла как в виде символов кириллицы, так и в виде их восьмеричных кодов. В последнем случае исходный текст будет состоять только из символов ASCII. Это упрощает передачу файла по компьютерной сети, например его пересылку по электронной почте, однако затрудняет чтение и модификацию файла. Вот примеры двух одинаковых строк в кодировке КОИ-8:
    (КОИ–8)
    и
    (\353\357\351–8)
    Массивом в программировании называют совокупность однотипных данных, которой присвоено общее имя. Массив в PostScript может состоять и из разнотипных объектов. Значения элементов массива записываются в квадратных скобках:
    [ 1 (String) 1.0 ]
    Символы квадратных скобок являются командами. Команда [ записывает в стек операндов специальную метку, а команда ] создает массив, содержащий элементы этого стека от текущего значения до ближайшей метки. Элемент, находящийся в стеке сразу после метки, имеет нулевой индекс. Между командами [ и ] можно исполнять любые команды со значениями, записанными в этот стек. Так, например, следующая запись создаст массив из одного элемента со значением 9:
    [ 4 5 add ]
    Здесь после исполнения команд [ 4 и 5 стек операндов будет содержать три значения:
    метка 4 5
    После выполнения операции сложения (команда add) останутся два элемента:
    метка 9
    Команда ] создаст массив из одного элемента и поместит его в стек:
    [9]

    Процедура — это группа команд, которая решает какую-то частную задачу или используется достаточно часто. В PostScript у процедуры нет ни имени, ни параметров. Процедура имеет тело и ссылку на него, этим она похожа на объекты других сложных типов. Текст процедуры содержится в фигурных скобках. При интерпретации процедуры ее операторы записываются в тело процедуры, а ссылки на нее — в стек операндов. В дальнейшем мы будем говорить, что в стек записывается процедура. Процедуру, записанную в стек, можно исполнить или записать в переменную, которая в таком случае будет указывать на тело процедуры, играя роль ее имени.

    Приведем пример. Пусть имеется процедура { 72.0 mul }. При ее исполнении в стек записывается значение 72,0, а затем выполняется умножение двух последних чисел из стека. Результат заносится в стек. Процедура записывает в стек операндов одно число, а использует для вычислений два. Первое число должно перед исполнением процедуры находиться в стеке операндов, оно играет роль аргумента. После выполнения процедуры в стеке операндов остается вещественное значение, оно и возвращается. Процедура может записать в стек несколько значений разных типов.

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

    Словарь является сложным типом данных. Мы рассмотрим только применение словаря для хранения шрифта и изменения его кодировки.

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

    Команды PostScript хранятся в специальных словарях, находящихся в стеке словарей с самого начала, которые не могут быть удалены. Это словарь ошибок, системный и пользовательский словари. Данные в системном словаре и словаре ошибок нельзя изменить, эти словари закрыты для записи. Пользовательский словарь изначально является текущим. Можно создавать новые словари, помещать их в стек словарей и удалять из него. Текущим является последний словарь, записанный в этот стек.

    Подробнее работа со словарями и шрифтами будет описана на стр. 606.

    Имя в PostScript является аналогом имени переменной в обычных языках программирования. Именем может быть любая последовательность символов, не содержащая специальных символов и не определяющая данные вышеперечисленных типов, например add, min1, Times–Roman, 1x, 777–, @midnight.

    Каждому объекту можно сопоставить имя. Делается это командой def, которая играет роль оператора присваивания. Интерпретатор просматривает текущий словарь и либо создает там новую запись, либо изменяет значение старого имени. Определяемая пользователем переменная всегда записывается в текущий словарь. Если объект простой (например, число), в словарь заносится сам объект. Если объект сложный, в словарь заносится ссылка на него. Занесение новой переменной в текущий словарь позволяет переопределять системные команды и стандартные реакции на ошибки, так как поиск любого имени осуществляется во всех словарях в стеке словарей начиная с текущего. Если имя найдено в одном из словарей, поиск прекращается, значение выбирается и обрабатывается.

    Если первый символ имени не /, оно ищется в словарях и «исполняется», то есть найденное значение переменной либо записывается в стек операндов, либо исполняется в зависимости от его типа. Если значением является процедура, исполняется она. Если имя не найдено, возникает ошибка и интерпретация завершается.

    Если первым символом имени является /, оно заносится в стек операндов, а поиск его значения не производится. Примеры имен:

    /Times–Roman, /1x, /add

    Приведем примеры двух операторов и результат их выполнения (состояние стека операндов):
    4 5 add % стек: 9 % операция суммирования
    4 5 /add % стек: 4 5 /add % в стек занесено имя

    Обзор универсальных команд

    Здесь мы познакомимся с универсальными командами PostScript.

    Обозначения. Для описания команд будем использовать следующую нотацию:
    аргумент1 аргумент2 … команда ® результат1 результат2 … % комментарий

    Имя команды выделяется специальным шрифтом. Слева от имени описывается состояние стека до исполнения команды, а справа от стрелки — после ее исполнения. Минус вместо аргумента или результата обозначает отсутствие значения. Данные обозначаются словами, производными от названий типов или математических обозначений, характеризующих операцию. Такая запись приближена к записи текста программы. Например, описание операции сложения выглядит так:
    p q add ® p+q
    а запись команды в тексте PostScript-файла может быть такой:
    4.0 5.0 add
    или
    17 10 add

    Целые числа обозначаются буквами i, j, n, вещественные числа — x, y, z, логические (булевы) значения — буквой b, координаты — x, y. Буквами p, q, r, s обозначаются любые числа, как целые, так и вещественные, если их тип не имеет значения. Строки, массивы и процедуры указываются вместе со скобками, в которых они определяются.

    Создание объектов. Объекты, встретившиеся в тексте программы, записываются в стек операндов. Имеются также команды создания «пустых», то есть не содержащих значений, объектов. Эти команды описаны в табл. 13.2.

    Таблица 13.2. Команды создания объектов

    Тип данных Описание командыПримечание
    Число – число ® p Число целого или вещественного типа
    Логическое - true ® true - false ® falseЛогическая константа
    Массив – [ объект1 … объектn ] ® [массив]Массив из n объекто n array ® [массив] Пустой массив из n объектов
    Строка - (строка) ® (строка) Строка текстаn string ® (буфер) Пустая строка длиной n символов
    Имя - /имя ® имя Запись имени в стек
    Процедура - { команды… } ® {процедура} Определение процедуры — команды не исполняются, а записываются в тело процедуры
    Словарьn dict ® словарьПустой словарь для хранения n словарных записей

    Пустые объекты можно использовать в дальнейшем для записи в них значений.

    Арифметические и логические операции, функции, преобразование типов. Эти операции приведены в табл. 13.3.

    Таблица 13.3. Арифметические и логические операции, функции, отношения

    Описание команды Примечание
    Арифметические операции
    p q add ® p+q Результат целый, только если оба аргумента целые, иначе вещественный
    p q sub ® p-q
    p q mul ® p*q
    p q div ® p/q Результат всегда вещественный
    i j idiv ® [i/j] Целая часть от деления. Аргументы только целого типа
    i j mod ® imodj Остаток от деления. Аргументы только целого типа
    p neg ® -p Изменение знака
    p abs ® |p| Абсолютное значение (модуль числа)
    Округление и преобразование типа операнда
    p round ® q Округление числа. Тип результата совпадает с типом аргумента
    p truncate ® q Отбрасывание дробной части. Тип результата совпадает с типом аргумента
    p floor ® q Наибольшее целое, значение которого не превосходит значение аргумента. Тип результата совпадает с типом аргумента
    p ceiling ® q Наименьшее целое, значение которого больше или равно значению аргумента. Тип результата совпадает с типом аргумента
    p cvi ® I Преобразование к целому типу. У вещественного числа отбрасывается дробная часть
    p cvr ® x Преобразование к вещественному типу
    Преобразование значения в текст и обратно
    объект (буфер) cvs ®(строка) Преобразование данных целого, вещественного или логического типа в символьное представление
    i j (буфер) cvrs ® (строка)Преобразование целого числа i по основанию j в символьное представление
    (строка) cvi ® iПреобразование символьного представления числа в целое значение. У вещественного числа отбрасывается дробная часть
    (строка) cvr ® xПреобразование символьного представления числа в вещественное значение
    Функции
    a° cos ® cos(a°) Угол задается в градусах
    a° sin ® sin(a°)
    x y atan ® Q° Угол задается в градусах и отсчитывается по часовой стрелке от оси OY к вектору (x,y)
    p sqrt ® Цp % p>0.0 Квадратный корень
    p ln ® ln(p) % p>0.0 Натуральный логарифм
    p log ® lg(p) % p>0.0 Десятичный логарифм
    x y exp ® xy % x>0.0 Возведение в степень
    p n exp ®pn % если n<0, то x№0
    - rand ® I Генератор псевдослучайных чисел в диапазоне от 0 до 231–1
    Отношения
    p q eq ® p=q В стек помещается логическое значение true или false
    p q ne ® p№q
    p q le ® pЈq
    p q lt ® p
    p q ge ® pіq
    p q gt ® p>q
    Логические операции
    b1 b2 and ® b1Щb2 Логическое И
    b1 b2 or ® b1Ъb2 Логическое ИЛИ
    b1 b2 xor ® b1=b2 Исключающее ИЛИ
    b not ® Шb Логическое отрицание

    Команда округления и другие, подобные ей, не изменяют тип значения. Если аргумент вещественный, результатом будет вещественное число, но без дробной части. Для получения целого значения следует применить команду cvi. В табл. 13.4 приведены примеры выполнения операций округления с положительными и отрицательными аргументами.

    Таблица 13.4. Примеры преобразования вещественного числа в целое

    АргументЗначение функции
    round truncate floor ceiling cvi
    1.1 1.0 1.0 1.0 2.0 1
    0.9 1.0 0.0 0.0 1.0 0
    0.5 1.0 0.0 0.0 1.0 0
    0.1 0.0 0.0 0.0 1.0 0
    –0.1 0.0 0.0 –1.0 0.0 0
    –0.5 0.0 0.0 –1.0 0.0 0
    –0.9 –1.0 0.0 –1.0 0.0 0
    –1.1 –1.0 –1.0 –2.0 –1.0 –1

    Преобразования между целыми и вещественными значениями выполняются автоматически, однако есть несколько операций преобразования типов. Операция cvi преобразует вещественное значение в целое путем отбрасывания дробной части:

    1.9 cvi ® 1
    -1.9 cvi ® -1

    Операции cvi и cvr могут выполнять преобразование строки в число:

    (1.92e1) cvi ® 19
    (1.92e1) cvr ® 19.2

    Обратное преобразование выполняется командами cvs и cvrs:

    1.9 (буфер) cvs ® (1.9)
    true (буфер) cvs ® (true)
    5 2 (буфер) cvrs ® (101) % двоичное представление числа 5

    Аргумент (буфер) может быть пустой строкой, однако его длина должна быть достаточной для символьного представления числа:

    1.9 % стек: 1.9
    10 string % стек: 1.9 (строка) % строка имеет длину 10 символов
    cvs % стек: (1.9) % строка содержит только 3 символа

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

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

    Аргументы тригонометрических функций задаются в градусах. Функция atan определяет угол по значению тангенса. Угол отсчитывается от вертикали по часовой стрелке. Это значение будет совпадать с общепринятым определением, если поменять аргументы местами:

    y x atan ® a°
    0 1 atan ® 0.0
    1.5 0 atan ® 90.0
    0.0 -10 atan ® 180.0
    -1 0 atan ® 270.0
    Exp — операция возведения числа x в степень y. У нее два аргумента. Возводить в целую положительную степень можно любые числа, а в целую отрицательную — все, кроме нуля. В вещественную степень можно возводить только неотрицательные числа. Результат данной операции всегда вещественный:
    3 2 exp ® 9.0
    10 1.5 exp ® 31.62278
    -0.1 –1 exp ® -10.0
    

    Приведем примеры математических выражений. Сумма трех чисел x + y + z может быть вычислена двумя способами:

    x y add z add % первый способ
    или
    x y z add add % второй способ
    

    Состояние стека и результат этих операций сравниваются в табл. 13.5. Результаты для целых аргументов совпадают, а для вещественных могут отличаться из-за конечной точности представления вещественных чисел в ЭВМ.

    Таблица 13.5. Состояние стека и сравнение результатов вычисления суммы трех чисел двумя способами

    Первый способВторой способ
    Команда Состояние стекаКомандаСостояние стека
    xxxx
    yxyy x y
    add (x+y)zx y z
    z(x+y)zaddx (y+z)
    add (x+y)+zaddx+(y+z)

    Перевести обычные единицы измерения в пункты можно, например, командами:

    x 72 mul % из дюймов в пункты
    x 2.54 div 72 mul % из сантиметров в пункты

    PostScript имеет команды общего назначения для работы с данными (присваивание, работа со стеком), операторы вызова подпрограмм, ветвления и циклы. Операторов безусловного перехода и меток нет. Имеются команды ввода/вывода, но при печати на принтере они теряют смысл, так как PostScript-файл передается в принтер и обрабатывается в нем автономно, без связи с компьютером. Универсальные команды языка PostScript приведены в табл. 13.6.

    Таблица 13.6. Универсальные команды языка PostScript

    Описание командПримечание
    Работа со стеком
    X dup ® X X Скопировать в стек текущий объект
    Xn-1 … X0 n copy ® Xn-1 … X0 Xn-1 … X0 Скопировать в стек n последних объектов
    Xn … X0 n index ® Xn … X0 Xn Скопировать в стек n-й элемент из стека (начиная с нулевого)
    X pop ® - Удалить текущий объект из стека
    X Y exch ® Y X Поменять местами два последних объекта в стеке
    Xn-1 … X0 n i roll ® Xi-1 … X0 Xn-1 … Xi Циклический сдвиг n элементов стека сверху вниз i раз или снизу вверх –i раз, если i отрицательное- mark ® метка Записать в стек метку. Эквивалентна команде [
    метка Xn-1 … X0 cleartomark ® - Удалить все объекты из стека до ближайшей метки
    метка Xn-1 … X0 counttomark ® метка Xn-1 … X0 n Записать в стек количество объектов, расположенных до ближайшей метки
    Весь-стек clear ® - Полностью очистить стек
    Весь-стек count ® Весь-стек n Определить количество объектов в стеке
    Работа с массивами, строками и словарями
    [массив] length ® n Определить количество элементов
    (строка) length ® n в объекте (длина массива, длина строки, словарь length ® n число записей в словаре)
    [массив] i объект put ® - Записать объект в i-й элемент массива,
    (строка) i код put ® - букву с указанным кодом в строку вместо
    словарь /имя значение put ® - i-й буквы или занести в словарь пару/имя-значение
    [массив] i get ® объект Занести в стек i-й элемент массива, код
    (строка) i get ® код i-й буквы в строке или значение записи словарь /имя get ® значение из словаря
    /имя X def ® - Присвоить значение объекта переменной «имя»/имя {процедура} def ® - Связать с процедурой имя- имя ® объект Занести значение переменной в стек, если это не процедура
    объекты’… имя ® объекты»… Если значением переменной является процедура, исполнить процедуру. Состояние стека операндов до и после выполнения определяется исполняемыми командами процедуры
    /имя load ® объект Занести значение переменной в стек операндов независимо от ее типа
    - quit ® - Завершение интерпретации PostScript-файла
    Операторы управления
    b {проц-then} if ® - Условные операторы, аналогичные
    b {проц-then} {проц-else} ifelse ® - if…then…endif и if…then…else…endifpнач qшаг sкон {проц} for ® - Цикл с параметром (целым или вещественным)
    n {проц} repeat ® - n-кратный цикл без параметра{проц} loop ® - Бесконечный цикл
    [массив] {проц} forall ® - Выполнить процедуру для каждого
    (строка) {проц} forall ® - элемента массива, каждой буквы в строке
    словарь {проц} forall ® - или каждой записи в словаре
    - exit ® - Завершение цикла

    При интерпретации команды интерпретатор PostScript ищет ее имя в стеке словарей. Если имя найдено, соответствующее значение либо заносится в стек операндов (если это не процедура), либо исполняется (если это процедура). Если имя не найдено ни в одном из словарей, возникает ошибка и интерпретация PostScript-файла завершается. Имена стандартных команд представляют собой имена процедур, записанные в системном словаре. При необходимости эти имена можно переопределить. Команда load выполняет поиск указанного имени, но в любом случае заносит найденное значение в стек. Эту команду можно применять для того, чтобы использовать описанную ранее процедуру в качестве тела цикла или условного оператора.

    Некоторые аспекты определения и использования переменных рассматриваются на стр. 606.

    Приведем несколько примеров определения объектов и их использования.

    1. Применение символьных обозначений для констант и определение переменных в программе:

    /e 2.71828 def % число e
    /pi 3.1415927 def % число p
    /x 0.94 def % пользовательская переменная
    e x exp % стек: ex % в обычных языках программирования это exp(x)
    /y exch % стек: /y ex % стек подготовлен для выполнения присваивания
    def % стек: - % определение пользовательской переменой y со значением ex
    y x atan % стек: a° % угол в градусах между осью OX и вектором (x,y)
    180 div pi mul % стек: a % тот же угол в радианах
    /alpha exch def % запись значения угла в переменную alpha
    

    2. Определение процедур перевода единиц длины в пункты:

    /cm { 2.54 div 72 mul } def % стек: сантиметры ® пункты
    /mm { 25.4 div 72 mul } def % стек: миллиметры ® пункты
    3.	Использование процедур задания размеров объектов и координат точек:
    /x-len 210 mm def % эта строка эквивалентна строке:
    /x-len 210 25.4 div 72 mul def % переменная x-len получит значение 595,28
    x-len 2.5 cm sub /t-len exch def % сначала длина 2,5 см будет переведена
    % в пункты, затем она вычитается из длины x-len. 
    % Переменная t-len будет равна 524,41
    1 cm 10 mm atan % стек: 45.0 % угол в градусах
    

    ВНИМАНИЕ. Переменная хранит ссылку на сложный объект, поэтому можно создать несколько переменных, ссылающихся на один и тот же объект, например: /a (Line) def /b a def. После такого определения переменные a и b будут ссылаться на один и тот же объект. Если изменить букву в строке a, изменится и строка b. А после команд: /a (Line) def /b (Line) def будет создано два разных объекта с одинаковым содержанием. Переменные будут ссылаться на разные объекты. В этом случае любое изменение объекта a не повлияет на объект b.

    Условный оператор if удаляет из стека операндов процедуру и логическое значение, после чего либо исполняет процедуру, либо нет. Процедура может использовать значения в стеке операндов, записанные до того, как туда было записано логическое значение. Условный оператор ifelse удаляет из стека операндов обе процедуры и логическое значение, после чего исполняет одну из процедур. В этом случае обе процедуры должны одинаково использовать старые значения в стеке и оставлять там одинаковое количество новых значений. Примеры использования условных операторов для управления работой программы приведены ниже.

    1. Определение процедур и использование их в качестве аргумента команды if:

    /body-then { (Numbers are equal) show } def
    /body-else { (Numbers are different) show } def
    10 15 eq
    /body-then load
    /body-else load
    ifelse
    

    2. Вычисление абсолютного значения числа:

    x dup % стек: x x
    0 lt % стек: x x<0
    {neg} % стек: x x<0 {проц}
    if % стек: |x| % выполняет команду neg, только если x отрицательно
    sqrt % стек: Ц|x| % корень вычисляется из положительного числа
    

    3. Короткий вариант вычисления абсолютного значения числа:

    x abs sqrt % стек: Ц|x|

    4. Процедура нахождения максимального значения из двух:

    /max {       % стек: a b ® max(a,b)
    2 copy       % стек: a b a b
    lt {exch} if % стек: a b % если aіb
                 % стек: b a % если b>a
    pop          % удаляем нижнее число из стека, остается максимальное из двух
    } def        % конец определения процедуры max
    3 4 max % стек: 4
    10.0 –1 max % стек: 10.0
    

    5. Завершение программы, если координата x вышла за пределы печатного листа (используется ранее определенная команда mm):

    /test-x { % стек: x ® -
       dup    % стек: x x
       0 lt   % стек: x x<0
       exch   % стек: x<0 x
       210 mm % стек: x<0 x 595,28
       gt     % стек: x<0 x>595,28
       or     % стек: (x<0)Ъ(x>595,28)
       {quit} % процедура содержит команду останова
       if     % стек: -
    } def
    a1 test-x % если программа не остановилась, то обе координаты a1 и a2
    a2 test-x % находятся в диапазоне 0…210 мм
    

    Оператор цикла for удаляет из стека операндов все свои 4 аргумента и устанавливает значение параметра цикла равным pнач. После этого он циклически выполняет следующие действия: проверяет, не вышло ли текущее значение параметра за допустимую границу sкон, помещает значение параметра цикла в стек операндов, выполняет процедуру, увеличивает значение параметра на величину qшаг. Шаг может быть как положительным, так и отрицательным. Если начальное значение больше конечного при положительном шаге или меньше конечного при отрицательном шаге, процедура не выполняется. Процедура может использовать предыдущие значения в стеке, а также значение параметра цикла. После окончания цикла стек не восстанавливает автоматически свое прежнее состояние, что может привести к его переполнению, поэтому, в частности, необходимо удалять из стека операндов очередное значение параметра цикла.

    1. Запись чисел в стек и очистка стека:

    x y mark      % стек: x y метка
    5 –1 1 {} for % стек: x y метка 5 4 3 2 1
                  % пустая процедура {} не изменяет стек
    cleartomark   % стек: x y
    add           % стек: x+y
    

    2. Программа для вычисления наибольшего общего делителя (НОД) двух чисел (см. листинг 13.2). В этой программе используется процедура gcd2, переведенная с языка Паскаль.1 Здесь используется рекурсия. Процедура должна использовать только значения, хранящиеся в стеке, так как в языке отсутствуют локальные переменные процедур. Это пример вычислительной программы для принтера, включающей вывод числовых значений на печать. Результат работы программы показан на рис. 13.4.

    Листинг 13.2. Вычислительная программа для принтера — нахождение НОД двух чисел и печать результата на бумаге

    %!PS-Adobe-1.0
    %%Title: Наибольший общий делитель (НОД)
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%DocumentFonts: Courier
    %%BoundingBox: 25 55 175 105
    %%EndComments
    /gcd2 { % стек: m n ® НОД(m,n)
       dup    % стек: m n n
       0 eq   % стек: m n n=0
       {        % начало 1 процедуры % стек процедуры: m n ® m
         pop      % стек: m
       }        % конец  1 процедуры
       {        % начало 2 процедуры % стек процедуры: m n ® m
         2 copy   % стек: m n m n
         lt       % стек: m n m<n
         {        % начало 1 процедуры % стек процедуры: m n ® m
           exch     % стек: n m
           gcd2     % стек: n m ® НОД(n,m) % Рекурсивный вызов
         }        % конец  1 процедуры
         {        % начало 2 процедуры % стек процедуры: m n ® m
           dup      % стек: m n n
           3 1 roll % стек: n m n
           mod      % стек: n m\mod/n
           gcd2     % стек: n m\mod/n ® НОД(n,m\mod/n) % Рекурсивный вызов
         }        % конец  2 процедуры
         ifelse   % использование одной из двух процедур
       }        % конец  2 процедуры
       ifelse   % использование одной из двух процедур
    } def
    %%EndProlog
    %%Page: 1 1
    gsave
    /Courier findfont 12 scalefont setfont % выбор шрифта
    30 90 moveto                 %-- выбор точки для печати текста
      (GCD\(29008,36386\)=) show % печать условия
      29008 36386 gcd2           % вычисление НОД(29008,36386)
      10 string cvs              % перевод числа в строку
      show                       % печать результата
    30 75 moveto                 %-- выбор точки для печати текста
      (GCD\(29008,36385\)=) show % печать условия
      29008 36385 gcd2           % вычисление НОД(29008,36385)
      10 string cvs              % перевод числа в строку
      show                       % печать результата
    30 60 moveto                 %-- выбор точки для печати текста
      (GCD\(29008,36384\)=) show % печать условия
      29008 36384 gcd2           % вычисление НОД(29008,36384)
      10 string cvs              % перевод числа в строку
      show                       % печать результата
    grestore
    showpage
    %%Trailer
    


    Рис. 13.4. Результат вычисления наибольшего общего делителя трех пар чисел

    3. Вывод графика функции sin(x). В листинге 13.3 приведена программа, которая строит 4 периода синусоиды, соединяя точки, расположенные с интервалом 5 градусов, отрезками прямых. Размер поля рисования 10 см ґ 4 см, график расположен в центре листа и обведен рамкой. Результат показан на рис. 13.5.

    Листинг 13.3. Программа вывода графика функции sin(x)

    %!PS-Adobe-1.0
    %%Title: График синусоиды
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    %%EndComments
      /cm { 72.0 mul 2.54 div } def % Перевод из сантиметров в пункты
      /x0 21.0 2 div 5.0 sub cm def % Координаты нижнего левого
      /y0 29.7 2 div 2.0 sub cm def % угла графика
    %%EndProlog
    %%Page: 1 1
    gsave
    x0 y0 translate % сдвигаем начало координат к рамке
    newpath
    0 0 moveto      % обводим рамку размером 10см ґ 4см
    10 cm 0 rlineto
    0 4 cm rlineto
    -10 cm 0 rlineto
    closepath       % четвертая сторона
    .3 setlinewidth % толщина рамки 0,3 пункта
    stroke          % рисуем линию вдоль сторон рамки
    newpath
    0 5 1440        % заголовок цикла: от 0° с шагом 5° до 4ґ360° (4 периода)
    { % Процедура. Стек: a° ® -
      /a exch def   % локальная переменная 
      a 144.0 div   % стек: a/144 % это число в диапазоне 0…10
      cm            % стек: x     % координата x лежит в диапазоне 0…10 см
      a sin         % стек: x sin(a°)
      1.0 add       % стек: x sin(a°)+1
      2.0 mul       % стек: x (sin(a°)+1)*2
      cm            % стек: x y % координата y лежит в интервале 0…4 см
      % команды построения линий:
      %   стек обеих процедур: x y ® -
      %   в первую точку (a=0) переходим без проведения линии
      %   в последующие точки проводим линии
      a 0 eq       % стек: x y a=0
        { moveto } % переход в первую точку % стек: x y ® -
        { lineto } % проведение линии из предыдущей точки % стек: x y ® -
      ifelse
      % стек: -
    } for          % оператор цикла
    1 setlinewidth % график выводится жирной линией (1 пункт)
    stroke         % именно эта команда рисует линию вдоль кривой
    grestore
    showpage
    %%Trailer
    


    Рис. 13.5. График функции y=sin(x)

    Оператор repeat повторяет процедуру n раз. Перед исполнением он удаляет из стека оба аргумента, после чего процедура может использовать предыдущие значения в стеке. Если n равно нулю, процедура не выполняется ни разу. Этот оператор используется для «размножения» объектов в стеке или, наоборот, для удаления их из стека:

    3 {dup} repeat % стек: x ® x x x x
    4 {pop} repeat % стек: x x x x ® -

    Бесконечный цикл loop можно прервать только выполнением оператора exit. Используйте этот оператор осторожно, так как принтер может «зациклиться» и его придется выключить. Подсистема печати ОС UNIX может повторно передать этот файл на принтер, если его печать закончилась неудачно, например, после выключения принтера.

    Оператор цикла forall выполняет процедуру для каждого элемента составного объекта, указанного в качестве аргумента. Аргумент перед выполнением цикла удаляется из стека операндов. Перед выполнением процедуры в стек заносится очередной элемент составного объекта.

    При обработке элементов массива перед исполнением процедуры в стек помещается очередной элемент массива:

    0 [ 1 3 2 -5 4 ] {add} forall % стек: 5 % сумма элементов массива

    Элементы одного массива могут содержать данные разных типов.

    При обработке строки в стек помещается код очередного символа. Пример программы, определяющей количество пробелов в строке:

    /n 0 def   % определение переменной
    (в этой строке четыре пробела) % строка – первый аргумент forall
    { % процедура - второй аргумент forall
      % стек процедуры: код ® -
      8#40 eq % стек: код=32 % пробел имеет код 3210=408
      { % процедура – второй аргумент if
        % стек процедуры: - ® -
        n 1 add % стек: n+1
        /n exch % стек: /n n+1
        def     % определяем новое значение переменной n
      } if % если код символа равен 32 (код пробела)
    } forall % цикл по всем символам строки
    % n содержит число пробелов в строке, которая была первым аргументом forall
    

    При обработке словаря в стек заносятся два объекта — имя и значение.

    Оператор exit досрочно завершает выполнение любого цикла.

    Операторы put, get и length применяются для работы с массивами, строками и словарями. Оператор put удаляет из стека тот составной объект, в который заносится значение, поэтому нельзя использовать только что созданный объект — он потеряется. Для правильной работы оператора put необходимо составной объект присвоить переменной, а затем использовать эту переменную:

    /st (строка) def % все буквы в строке маленькие
    st 0 8#362 put % st=(Строка), 3628 – код буквы «С» в КОИ-8

    Можно продублировать составной объект в стеке (точнее, дублируется ссылка, а тело объекта остается в единственном экземпляре):

    (строка) dup % стек: (строка) (строка) % это два одинаковых указателя
    0 8#362 put % стек: (Строка) % второй указатель теряется
    Приведем несколько команд для работы с массивами:
    /ma 3 array def % пустой массив ma=[пусто, пусто, пусто]
    ma 1 (KOI8) put % ma=[пусто, (KOI8), пусто] % определен только один элемент
    ma length % стек: 3 % размер массива, даже если не все элементы определены

    Работа с графическими объектами

    В языке PostScript команды работы с графическими объектами принадлежат одной из следующих категорий:

    1. Сохранение/восстановление графического контекста.

    2. Изменение графического контекста.

    3. Создание графического пути.

    4. Построение фигуры на листе.

    5. Вывод текста и работа со шрифтами.

    В этом параграфе мы рассмотрим команды первых четырех категорий. Работа со шрифтами и вывод текста рассматриваются в следующем параграфе. Команды остальных категорий представлены в табл. 13.7.

    Таблица 13.7. Команды PostScript для работы с графическими объектами

    Описание команд Примечание
    Сохранение и восстановление графического контекста
    - gsave ® - Запись текущего графического контекста в стек графических контекстов, стек операндов при этом не изменяется
    - grestore ® - Восстановление текущего графического контекста из сохраненного командой gsave контекста
    - grestoreall ® - Восстановление первого графического контекста, сохраненного командой gsave или последней командой save
    - save ® образ-памяти метка Сохранение текущего графического контекста в стеке графических контекстов и запись в стек операндов образа памяти виртуальной ЭВМ, содержащего имена, их значения (кроме строк текста) и метку
    образ-памяти restore ® - Восстановление образа памяти и графического контекста, сохраненных командой save
    Изменение системы координат
    x y translate ® - Перенос начала координат
    a° rotate ® - Поворот координатных осей
    Sx Sy scale ® - Изменение масштаба осей координат
    Создание графического пути
    - newpath ® - Инициализация нового графического пути. Текущая точка становится неопределенной
    - closepath ® - Замыкание участка графического пути из текущей точки в начальную точку пути
    x y moveto ® - Установка текущей точки без проведения линии
    Dx Dy rmoveto ® - Сдвиг текущей точки без проведения линии
    - currentpoint ® x y Определение координат текущей точки
    x y lineto ® - Добавление к текущему пути отрезка прямой линии
    Dx Dy rlineto ® - Добавление отрезка прямой линии
    x y R a0 a1 arc ® - Добавление части окружности в направлении против часовой стрелки
    x y R a0 a1 arcn ® - То же, но по часовой стрелке
    x1 y1 x2 y2 R arcto ® xнач yнач xкон yкон Закругление угла, соединяющего две прямые
    x1 y1 x2 y2 x3 y3 curveto ® - Добавление части кривой Безье
    Dx1 Dy1 Dx2 Dy2 Dx3 Dy3 rcurveto ® - Добавление части кривой Безье
    Использование графического пути для вывода фигуры
    - stroke ® - Построение линии вдоль графического пути
    - fill ® - - eofill ® - Закрашивание текущим цветом фигуры, ограниченной замкнутым графическим путем
    - clip ® - - eoclip ® - Ограничение текущей области рисования фигурой, определенной замкнутым графическим путем
    - clippath ® - Создание нового графического пути вокруг всех областей, ограничивающих область рисования
    - pathbbox ® x0 y0 x1 y1 Определение координат левого нижнего и правого верхнего углов прямоугольника, охватывающего текущий путь
    - flattenpath ® - Вспомогательная команда, позволяющая точнее определить границы текущего графического пути

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

    Среди них команды, которые заносят в специальный стек графических контекстов текущий графический контекст и могут восстановить ранее записанное состояние. Это позволяет изменять параметры вывода одной части рисунка и возвращаться к предыдущему состоянию перед выводом другой его части. Команда gsave выполняет запись в стек графических контекстов, а команда grestore считывает из этого стека графический контекст, делая его текущим. Таким образом, все изменения параметров рисования, произведенные с момента сохранения контекста, перестают действовать на последующие команды вывода графических объектов. Команда grestoreall восстанавливает самое первое состояние, записанное в стек командой gsave.

    Более «мощные» команды save и restore кроме сохранения графического контекста сохраняют еще и область памяти виртуальной ЭВМ, содержащую все переменные, и восстанавливают как графический контекст, так и значения переменных. Эти команды можно использовать для предотвращения побочных эффектов при печати страниц, если среди команд печати встречаются команды изменения каких-либо объектов. Команда grestoreall восстанавливает последний графический контекст, сохраненный командой save, или, если таких команд не было, самый первый (верхний в стеке), сохраненный командой gsave. Образ памяти, создаваемый командой save, рекомендуется хранить в стеке операндов и использовать команду restore, предварительно убрав из стека все записанные туда позже операнды.

    Команды save/restore рекомендуется использовать в начале и в конце описания каждой страницы:

    %%Page: II 2
    save        % стек: образ-памяти
    % здесь идут команды печати страницы…
    restore     % стек: -
    showpage    % печать изображения
    %%Page: III 3
    Такой стиль программирования позволит в начале каждой страницы получать тот графический контекст и переменные, которые были определены в прологе программы. Использование в начале/конце описания страниц команд gsave/grestore позволяет сохранять графический контекст, но не освобождает память от определенных, но уже не используемых значений. Эти команды выполняются быстрее, чем save/restore, поэтому при печати больших файлов они могут оказаться более эффективными:
    %%Page: IV 4
    gsave    % сохранение графического контекста
    % здесь идут команды печати страницы…
    grestore % восстановление графического контекста
    showpage % печать изображения
    %%Page: V 5
    В этой книге мы придерживаемся данных рекомендаций, даже если документ содержит только одну страницу.
    

    ПРИМЕЧАНИЕ. Все определяемые в PostScript-файле строки текста, подпрограммы или массивы находятся в памяти принтера, даже если они больше не нужны. Это может привести к переполнению памяти при печати больших многостраничных документов. Восстановление образа памяти позволяет удалить ненужные переменные.

    Система координат. В PostScript используется обычная прямоугольная система координат. Начало отсчета располагается в нижнем левом углу листа бумаги. Ориентация листа бумаги по умолчанию выбирается книжной. Масштабы по осям OX и OY совпадают, а единица длины — 1 пункт. Возможен перенос начала координат в любую точку, даже за пределами листа бумаги, поворот системы на произвольный угол, изменение масштаба по каждой оси, а также изменение направления оси на противоположное.

    Если на принтере не печатается левая часть рамки, в программе 13.1, перед формированием каждой страницы (после команды gsave или save) следует сместить всю систему координат на несколько пунктов вправо командой:

    15 0 translate % сдвиг начала координат на 15 пунктов вправо

    Изображение в результате этого сместится на 15 пунктов вправо.

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

    newpath
    0 0 moveto      % переходим в начало текущей системы координат
    100 0 translate % сдвигаем систему координат в другую точку
    0 0 lineto      % проводим линию в начало новой системы координат
    stroke
    в нижней части листа бумаги строится горизонтальная линия длиной 100 пунктов. Линия не попадет в область печати на любом принтере, поэтому для проверки программы надо заменить координаты (0, 0) на большие, например (30, 30).
    

    В программе из листинга 13.4, для того чтобы две страницы формата А4 исходного документа напечатать на одном листе бумаги, перед выводом изображения использованы команды, которые изменяют ориентацию листа бумаги формата А4 с книжной на альбомную и уменьшают масштаб в Ц2 раз. Эта программа составлена так, что в ее текст можно включить другую программу целиком, вместе со структурными комментариями. Предлагаем читателю вместо соответствующей строки самостоятельно вставить фрагмент, приведенный в листинге 13.3.

    Комментарии %%BeginDocument и %%EndDocument начинают и завершают файл с самостоятельной программой, «экранируя» структурные комментарии от интерпретации большинством программ обработки. Весь текст файла заключен между командами save и restore, что важно для правильной печати. Все изменения в памяти виртуальной машины, выполненные после save, будут потеряны после восстановления состояния командой restore. На этом основан прием с переопределением стандартной процедуры showpage, которая должна выводить построенное изображение на печать. После переопределения она уже не будет выводить изображение, то есть во включенном документе не приведет к завершению набора и к печати текущей страницы. В результате выполнения restore новое (пустое) значение этой процедуры будет утеряно, использоваться будет стандартное значение, то есть вывод изображения. Если развернуть напечатанный программой 13.3 лист на 90°, в правой его половине будет находиться уменьшенная копия листа, напечатанного программой 13.2.

    Листинг 13.4. Изменение ориентации листа и масштаба перед выводом синусоиды

    %!PS-Adobe-3.0
    %%Title: Синусоида на половине листа
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    %%EndProlog
    %%Page: 1 1
    gsave               % сохраним графический контекст
    21 cm 0 translate   % переведем начало координат в нижний правый угол
    90 rotate           % направим ось OX вверх листа, а OY – влево
    0.7071 0.7071 scale % уменьшим масштаб в Ц2 раз
    save                % сохраним полностью образ памяти виртуальной машины
    /showpage {} def    % заменим команду showpage пустой процедурой
    %%BeginDocument     % начало документа
    ВКЛЮЧИТЕ СЮДА ФАЙЛ (листинг 13.3) С ПРОГРАММОЙ РИСОВАНИЯ ГРАФИКА СИНУСОИДЫ
    %%EndDocument       % конец включенного файла
    restore             % восстановим образ памяти виртуальной машины
    grestore            % вернем систему координат на прежнее место
    showpage            % печать изображения страницы
    %%Trailer
    

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

    После того как путь построен, можно:

    1. Провести вдоль пути линию независимо от того, замкнут он или нет.

    2. Закрасить обведенную фигуру цветом (если путь замкнут).

    3. Ограничить область рисования построенной фигурой (если путь замкнут).

    Проведение графического пути начинается командой newpath. Старый путь при этом удаляется из графического контекста. Текущая точка становится неопределенной, поэтому первой командой после создания нового пути должна быть moveto, arc или arcn. Аргументами этих команд являются координаты начальной точки. Остальные команды используют текущую точку в качестве начальной. Для них текущая точка обязательно должна быть определена. После определения текущей точки можно использовать команды добавления линий к графическому пути. Замыкание участка пути прямой линией из текущей точки в начальную точку участка выполняется командой closepath. Без этой команды путь останется разомкнутым, даже если координаты последней и начальной точки совпали (см. листинг 13.6). После замыкания одного участка пути можно строить другой участок и замыкать его. Участки могут пересекаться.

    Если путь создан, можно провести вдоль него линию. Это делается командой stroke, которая после исполнения неявно вызывает команду newpath, поэтому текущая точка становится неопределенной.

    Если путь замкнутый или состоит из замкнутых участков, полученную фигуру можно заполнить цветом командой fill или eofill. Эти команды также вызывают команду newpath после своего завершения.

    Графические объекты или их фрагменты, которые не помещаются внутри текущей области рисования, не отображаются на листе бумаги. Команды clip и eoclip используют текущий замкнутый графический путь для ограничения области рисования. Новой областью рисования является область пересечения текущей области рисования с текущим путем. Изначально область рисования ограничена размером листа бумаги или чуть меньшим, если принтер не может печатать изображение вплотную к краю бумаги. Команды clip и eoclip всегда сужают область рисования. Поскольку расширить область рисования нельзя, перед использованием этих команд необходимо сохранить графический контекст, а позже восстановить его. Команды clip и eoclip не вызывают команду newpath, поэтому перед построением нового пути ее следует выполнить явно. Приведем пример ограничения области рисования и восстановления прежней области:

    %!PS-Adobe-3.0
    %%Title: Область рисования
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    %%EndProlog
    %%Page: 1 1
    gsave        % сохраняем начальный графический контекст
    newpath      % создаем новый путь
    50 50 35 0 360 arc % обводим окружность с центром (50,50) и радиусом 35
    closepath    % замыкаем путь
    clip         % ограничиваем область рисования построенной окружностью
    newpath      % создаем новый путь
    30 30 moveto % один из углов квадрата
    30 90 lineto % проводим три стороны квадрата
    90 90 lineto
    90 30 lineto
    closepath    % замыкаем четвертую сторону прямой линией
    fill         % закрашиваем ту часть квадрата,
                 % которая попала внутрь окружности
    % здесь область рисования всё ещё ограничена окружностью!
    grestore     % восстанавливаем контекст (включая прежнюю область рисования)
    showpage     % печатаем страницу
    %%Trailer
    

    Результат действия этой программы показан на рис. 13.6. Серыми линиями обведены круг (область рисования) и квадрат (фигура, выводимая на печать).

    Рис. 13.6. Результат ограничения области рисования кругом — квадрат закрашен не полностью

    Различие команд clip и eoclip, а также fill и eofill заключается в следующем. Если путь простой, легко определить, какая точка лежит внутри него. Примером простого пути является любая выпуклая фигура. В случае вложенных или пересекающихся путей в PostScript предусмотрены два правила определения принадлежности точки внутренней области. Оба правила предусматривают проведение воображаемого луча из искомой точки в каком-либо направлении и подсчет числа пересекаемых этим лучом участков пути.

    Первое правило, используемое по умолчанию, предусматривает учет направления пути при подсчете числа пересечений. Если число участков, пересекающих луч справа налево, равно числу участков, пересекающих луч слева направо, точка расположена вне фигуры. Это «правило нулевого числа витков». Построим две вложенные окружности в одном направлении, против часовой стрелки, с помощью двух команд arc:

    newpath
    50 50 35 0 360 arc % проводим наружную окружность
    closepath          % замыкаем первую часть пути
    70 50 moveto       % разрываем путь, переходим в начало следующей окружности
    50 50 20 0 360 arc % вложенная окружность
    closepath          % замыкаем вторую часть пути
    fill               % закрашиваем фигуру
    Точка, расположенная внутри центральной окружности, находится внутри сложной фигуры, так как луч, проведенный из нее, дважды пересекается линиями окружности, но каждый раз в одном направлении. Теперь построим окружности в противоположных направлениях командами arc и arcn:
    newpath
    50 50 35 0 360 arc  % проводим наружную окружность
    closepath           % замыкаем первую часть пути
    70 50 moveto        % разрываем путь, переходим в начало следующей окружности
    50 50 20 360 0 arcn % вложенная окружность, проведенная по часовой стрелке
    closepath           % замыкаем вторую часть пути
    fill                % закрашиваем фигуру
    

    Луч, проведенный из той же точки, пересекается двумя границами области в разных направлениях, и данная точка расположена вне фигуры. Этот способ определения принадлежности точки фигуре реализован в командах clip и fill.

    Второе правило используется в командах eoclip и eofill и основано на четности числа пересечений луча с участками пути. Это «правило четности». Если число пересечений нечетное, точка находится внутри фигуры, если четное, — снаружи. Все три случая представлены на рис. 13.7. Для простой выпуклой фигуры оба правила дают одинаковый результат.


    Рис. 13.7. Правила определения внутренней области фигуры: а и б — правило нулевого
    числа витков: а — оба витка обходят центральную точку в одном направлении; б — внешний
    и внутренний витки проложены в противоположные стороны; в — правило четности.
    Показаны направления, по которым окружности пересекают лучи в каждом случае

    Команда clippath строит графический путь вокруг всех участков текущей области рисования. Этот путь замкнут и пригоден только для заполнения цветом командами fill и eofill. Вдоль него нельзя построить линию командой stroke.

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

    1. Определение границ области рисования. Если команды clippath и pathbbox расположены в начале программы или после команды showpage, определяется максимальная область печати на данном принтере:

    %!PS-Adobe-1.0
    %%Title: Доступная область печати
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    /s 20 string def
    %%EndProlog
    %%Page: 1 1
    clippath      % первоначальная область, определенная принтером
    pathbbox      % определяем ее размеры
    /y1 exch def  % сохраняем
    /x1 exch def
    /y0 exch def
    /x0 exch def
    newpath       % новый путь для рисования
    x0 y0 moveto  % нижний левый угол области рисования
    x1 y1 lineto  % диагональ
    x0 y1 moveto  % нижний правый угол
    x1 y0 lineto  % диагональ
    stroke
    /Courier findfont 10 scalefont setfont % шрифт для печати сообщения
    x0 y0 moveto % переходим в нижний левый угол области рисования
    15 5 rmoveto % сдвигаемся внутрь области рисования
    (%%BoundingBox: ) show           % печатаем строку «%%BoundingBox »
    x0 round cvi            % округляем x0 и преобразуем к целому
    s cvs                   % преобразуем в строку
    show ( ) show           % печатаем значение x0 и пробел
    y0 round cvi s cvs show ( ) show % печатаем y0 и пробел
    x1 round cvi s cvs show ( ) show % печатаем x1 и пробел
    y1 round cvi s cvs show          % печатаем y1
    showpage
    %%Trailer
    

    2. Определение границ текущего пути (фрагмент программы):

    newpath
    … % команды построения пути
    … % путь можно не замыкать
    flattenpath
    pathbbox
    /y1 exch def
    /x1 exch def
    /y0 exch def
    /x0 exch def
    stroke % выводим линию вдоль построенного пути
    newpath
    x0 y0 moveto % рисуем прямоугольник вокруг
    x0 y1 lineto % предыдущего пути
    x1 y1 lineto
    x1 y0 lineto
    closepath
    stroke
    

    Команды печати линий. Команда перемещения в новую точку без построения линии — moveto. Ее аргументами являются координаты точки. Первой в стек помещается координата x, затем y. Обе координаты задаются в единицах длины текущей системы координат. Масштаб изменяется командой scale. После установки текущей точки можно строить линии. Все команды после добавления линии к пути перемещают текущую точку в конец построенной линии. В командах добавления линий с именами, начинающимися на r (см. табл. 13.7), аргументом является смещение относительно текущей точки. Покажем, как используются эти команды для построения двух одинаковых квадратов со стороной 20 пунктов:

    1. В относительных координатах:

    newpath
    30  30 moveto  % угол квадрата – абсолютные координаты
     0  20 rlineto % стороны квадрата – смещение от текущей точки
    20   0 rlineto
     0 –20 rlineto
    closepath      % четвертая сторона просто закрывает путь
    

    2. В абсолютных координатах:

    newpath
    30  30 moveto  % угол квадрата – абсолютные координаты
    30  50 lineto  % стороны квадрата – абсолютные координаты точки
    50  50 lineto
    50  30 lineto
    closepath      % четвертая сторона замыкает путь
    В командах рисования окружностей углы задаются в градусах. В команде построения дуги arc угол a0 должен быть меньше a1, а в команде arcn наоборот. Углы отсчитываются от оси x в сторону оси y. Таким образом, команды:
    50 50 35 0 90 arc
    и
    50 50 35 90 0 arcn
    построят одинаковые дуги в первой четверти координатной плоскости, но в разных направлениях. Первая — из точки (85, 50) в точку (50, 85), а вторая — наоборот.
    

    Команда arcto используется для закругления соединений отрезков. Отрезок строится из текущей точки в точку (x1, y1). Не доводя линию до конечной точки, команда закругляет линию и подводит ее к отрезку прямой, соединяющему точки (x1, y1) и (x2, y2). Закругление представляет собой дугу радиусом R, которая соединяется с отрезками по касательным. Точка соединения со вторым отрезком становится текущей, и исполнение команды на этом завершается. Приведем пример использования этой команды:

    newpath
    30 30 moveto            % начальная точка (30, 30)
    120 70 30 120 20 arcto  % ведем отрезок в точку (120, 70),
                            % а затем в (30, 120)
    4 {pop} repeat          % удаляем возвращаемые значения из стека
    stroke                  % строим линию
    showpage                % печатаем рисунок
    

    Команда arcto заносит в стек координаты (xнач, yнач) начала и (xкон, yкон) конца дуги. Последние являются и координатами текущей точки. Продолжить построение второго отрезка из текущей точки можно командой 30 120 lineto. Результат представлен на рис. 13.8.


    Рис. 13.8. Проведение линий с плавным соединением. Серыми точками отмечены места соединения дуги с прямыми линиями

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

    newpath     % начинаем новый путь
    …           % строим путь
    closepath   % замыкаем этот путь
    gsave       % сохраняем текущий графический контекст, содержащий путь
    0.5 setgray % серый цвет (команда изменения графического контекста)
    fill        % закрашиваем область, графический путь стирается
    grestore    % восстанавливаем контекст и графический путь
    1 setgray   % черный цвет (команда изменения графического контекста)
    stroke      % строим линию вдоль графического пути
    

    Здесь важен порядок действий — сначала надо заполнить фигуру цветом, а потом обвести контур. При выполнении действий в обратном порядке фрагменты линий, которые попадут внутрь контура, будут закрашены. Покажем это на примере квадрата, обведенного толстой линией (рис. 13.9). При закраске фигуры поверх обведенного контура половина толстой линии пропала. Графический путь обозначен тонким белым пунктиром.


    Рис. 13.9. Результат построения контура после заполнения фигуры (1)
    и заполнения фигуры после построения контура (2)

    Для того чтобы при исполнении команд stroke и fill не потерять текущую точку, следует воспользоваться командой currentpoint, которая записывает в стек координаты текущей точки:

    newpath      % новый путь
    … команды создания пути
    currentpoint % занесение координат текущей точки в стек
    stroke       % неявный вызов newpath и потеря текущей точки
    moveto       % считывание координат из стека и получение текущей точки
    

    Приведем программу (листинг 13.5) построения линии, толщина которой меняется по периодическому закону. Линия состоит из отрезков длиной 0,1 мм. Каждый отрезок выводится линией постоянной толщины. В результате вывода коротких отрезков создается иллюзия непрерывного изменения толщины линии (рис. 13.10). Перед изменением толщины линии приходится выводить отрезок, что приводит к потере текущей точки. Для ее восстановления использована приведенная выше последовательность команд.

    Листинг 13.5. Программа построения линии переменной толщины

    %!PS-Adobe-1.0
    %%Title: Толстая линия
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 325 40
      /mm { 72 mul 25.4 div } def
    %%EndProlog
    %%Page: 1 1
    gsave        % сохраняем графический контекст
    20 20 moveto % координаты начала линии
    0 1 1000     % параметры цикла – 1000 точек по 0,1 мм (полная длина 10 см)
    { % стек процедуры: n ® -
      1.8 mul           % угол a от 0° до 1800°, 5 периодов синусоиды
      cos               % cos(a°) – от –1 до +1
      1 add             % cos(a°)+1 – от 0 до 2
      10 mul            % 10*(cos(a°)+1) – от 0 до 20
      setlinewidth      % установка толщины линии (команда
                        % изменения графического контекста)
      0.1 mm 0 rlineto  % проведение линии длиной 0,1 мм от текущей точки
      currentpoint      % запись координат текущей точки в стек
      stroke            % вывод отрезка и вызов newpath
      moveto            % восстановление текущей точки на прежнем месте
    }
    for
    grestore     % восстанавливаем графический контекст
    showpage     % печатаем страницу
    %%Trailer
    


    Рис. 13.10. Линия переменной толщины

    Изменение параметров графических объектов

    Команды установки параметров графических объектов и получения текущих значений приведены в табл. 13.8.

    Таблица 13.8. Команды изменения параметров графических объектов и получения текущих значений

    Установка параметров Получение текущих значений Описание
    Параметры линии
    x setlinewidth ® - - currentlinewidth ® x Толщина выводимой линии
    [массив] n setdah ® - - currentdah ® [массив] n Образец для вывода пунктирной линии
    n setlinecap ® - - currentlinecap ® n Способ рисования конца отрезков
    n setlinejoin ® - - currentlinejoin ® n Способ соединения отрезков прямых
    x setmiterlimit ® - - currentmiterlimit ® x Предел выноса линии при соединении в ус
    Текущий шрифт
    шрифт setfont ® - - currentfont ® шрифт Установка текущего шрифта
    Цвет объектов
    x setgray ® - - currentgray ® x Уровень серого тона для монохромных изображений
    к з с setrgbcolor ® - - currentrgbcolor ® к з с Цвет в системе RGB
    г п ж ч setcmykcolor ® - - currentcmykcolor ® г п ж ч Цвет в системе CMYK
    т н я sethsbcolor ® - - currenthsbcolor ® т н я Цвет в системе HSB
    Коррекция цветопередачи
    {проц} settransfer ® - - currenttransfer ® {проц} «Аппаратная» функция передачи сигнала яркости
    {к} {з} {с} {моно} - currentcolortransfer ® «Аппаратная» функция передачи
    setcolortransfer ® - {к} {з} {с} {моно} сигналов яркости в системе RGB для цветного и монохромного устройств

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

    Параметры линии. Эти параметры используются командой stroke. Толщина линии (setlinewidth) определяется в текущем масштабе по каждой из координат. Это важно, если масштабы по осям X и Y разные. На рис. 13.11 показаны окружности, построенные программой из листинга 13.6 в системе координат с разными масштабами по осям координат. Обратите внимание на то, что окружности следует замыкать. Эффект разомкнутой окружности будет виден на правом рисунке, если из программы исключить команды closepath. На левом рисунке толщина линии окружности меняется в зависимости от направления ее построения, так как в момент вывода линии (команда stroke) масштаб по осям различный. На правом рисунке графический путь построен после установки разных масштабов по осям, а линия проведена вдоль этого пути после восстановления одинакового масштаба по обеим осям, поэтому окружность получилась вытянутой, а линия — равномерной.

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

    Листинг 13.6. Влияние текущего масштаба на графический путь и толщину линии

    %!PS-Adobe-1.0
    %%Title: Линии и масштаб
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 70 395 130
    %%EndProlog
    %%Page: 1 1
    gsave             % ЛЕВЫЙ РИСУНОК
    100 100 translate % центр «окружности»
    2.0 0.5 scale     % разный масштаб по осям координат
    newpath
    0 0 40 0 360 arc  % прокладываем графический путь по окружности
    closepath         % замыкаем окружность
    15 setlinewidth   % толщина линии
    stroke            % выводим линию
    grestore
    gsave             % ПРАВЫЙ РИСУНОК
    300 100 translate % центр окружности
    2.0 0.5 scale     % разный масштаб по осям координат
    newpath
    0 0 40 0 360 arc  % прокладываем графический путь по окружности
    closepath         % замыкаем окружность
    15 setlinewidth   % толщина линии
    0.5 2.0 scale     % восстанавливаем одинаковый масштаб по осям
    stroke            % выводим линию
    grestore
    showpage
    %%Trailer
    


    Рис. 13.11. Линии «одинаковой» толщины в системе координат
    с разными масштабами по осям X и Y

    Вид линии задается командой setdash. Если размер массива равен нулю, выводится сплошная линия. В массиве содержатся длины чередующихся видимых и пропущенных отрезков. Начальный отрезок — видимый. Длина задается в текущих единицах длины. Вдоль каждого непрерывного участка графического пути выводится одна пунктирная линия независимо от числа построенных прямых или кривых участков и углов, под которыми они соединяются. Второй аргумент задает смещение «узора» линии в ее начальной точке. Пусть, например, имеется команда [10 5] 0 setdash. В этом случае 10 единиц длины закрашено, затем 5 единиц пропущены, потом снова 10 единиц закрашено и т. д. При выводе линии командой [10 5] 3 setdash в начале линии 7 единиц длины закрашено, потом идет чередование незакрашенных участков длиной 5 единиц и закрашенных участков длиной 10 единиц. Пример построения сложного пути, вдоль которого выводятся пунктирные линии с различными образцами, приводится в листинге 13.7, а соответствующее изображение — на рис. 13.12.

    Листинг 13.7. Примеры различных пунктиров

    %!PS-Adobe-1.0
    %%Title: Пунктир
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 20 10 340 160
    /linia { % стек процедуры: x y ® - % построение сложного пути
             gsave                 % сохраняем текущее состояние
             translate             % используем координаты из стека
             newpath
             0 0 moveto            % начало линии
             100 0 150 50 50 arcto % соединение с закруглением
             pop pop pop pop       % убираем из стека 4 координаты
             150  50 lineto        % соединение под углом
             175  50 lineto        % соединение под углом
             225 0 300 0 50 arcto  % соединение с закруглением
             pop pop pop pop       % убираем из стека 4 координаты
             300 0 lineto          % конец линии
             stroke                % выводим линию вдоль всего пути
             grestore              % восстанавливаем старую систему координат
           } def
    %%EndProlog
    %%Page: 1 1
    gsave
    7 setlinewidth                      % толщина всех линий
    [] 0 setdash           30 100 linia % 1) сплошная
    [7] 0 setdash          30  80 linia % 2) пунктир из квадратов
    1 setlinecap                        % вид концов отрезка - круглый
    [0 14] 0 setdash       30  60 linia % 3) пунктир из точек
    0 setlinecap                        % вид концов отрезка - прямой
    [21 7 7 7]  0 setdash  30  40 linia % 4) штрих-пунктир
    [21 7 7 7] 10 setdash  30  20 linia % 5) штрих-пунктир со смещением «узора»
    grestore
    showpage
    %%Trailer
    


    Рис. 13.12. Проведение пунктирных линий вдоль сложного пути

    Окончания линий срезаются перпендикулярно направлению линии в точках начала и конца. В точках изгиба линии соединяются «в ус», то есть наружные границы линии продлеваются до их пересечения. Изменить окончания линий и тип их соединений можно командами setlinecap и setlinejoin. Значения аргумента: 0, 1 и 2.

    На рис. 13.13 приведены окончания линий. Центром закругления или квадрата является конец линии, а диаметр или сторона равны ее толщине. По этой причине видимая длина линии увеличивается по сравнению с длиной графического пути на половину толщины линии на каждом конце. Это видно на рис. 13.12 (средняя пунктирная линия). Здесь длина отрезка, переходящего в окружность, равна 0, а интервал между отрезками составляет 14 единиц, то есть в 2 раза больше толщины линии. Сравните внешний вид этой линии с предыдущей.

    На рис. 13.14 показаны виды соединений линий в PostScript. При соединении линий с фацетом наружные края каждого из отрезков, срезанных в точке сочленения, соединяются прямой линией (см. рис. 13.15). При соединении линий в ус длина уса может составить большую величину, если линии соединяются под малым углом. Длина уса отсчитывается от точки соединения внутренних сторон линий до самой дальней точки соединения (рис. 13.15) и может быть вычислена по формуле l = w / sin(a / 2), где l — длина уса, w — ширина линии, a — угол между отрезками. Для ограничения длины уса введен параметр, устанавливаемый командой setmiterlimit, который определяет максимальное значение отношения l / w. Оно должно быть не меньше 1,0. Значение 1,0 задает такое обрезание уса, которое соответствует соединению с фацетом. Если установлено соединение линий в ус, но длина уса больше, чем параметр setmiterlimit, ус обрезается и соединение происходит с фацетом.


    Рис. 13.13. Окончания линий в зависимости от значения параметра команды setlinecap:
    а — срезанные окончания; б — закругленные окончания; в — квадратные окончания. Тонкой
    светлой линией обозначен графический путь, вдоль которого строится толстая линия


    Рис. 13.14. Вид соединений линий в зависимости от значения параметра команды setlinejoin:
    а — соединение в ус; б— соединение с закруглением; в — соединение с фацетом. Тонкой светлой
    линией обозначен графический путь, вдоль которого строится толстая линия


    Рис. 13.15. Определение длины уса и места среза фацета при соединении линий в ус

    Цвет объектов. Язык PostScript позволяет работать как с полутоновыми (черно-белыми), так и с цветными изображениями. При выводе цветных изображений на черно-белый принтер интерпретатор PostScript автоматически преобразует их в полутоновые и учитывает различие восприятия человеком объектов разного цвета, но одинаковой яркости. Цвет используется командами fill/eofill, stroke и некоторыми командами печати текста.

    Яркость полутоновых изображений задается вещественным числом от 0 до 1. При установке оттенка серого цвета 0 соответствует черному цвету, а 1 — белому. На рис. 13.16 приведены градации оттенков серого цвета с соответствующими значениями параметра команды setgray.

    Задавать текущий цвет можно в любой из трех систем:

    1. RGB (Red Green Blue) — цвет задается интенсивностями красного, зеленого и синего цветовых каналов, яркости складываются.

    2. CMYK (Cyan Magenta Yellow blacK, чтобы не путать с Blue) — цвет задается плотностью голубой, пурпурной, желтой и черной красок.

    3. HSB (Hue Saturation Brightness) — определяет цвет, насыщенность и яркость используемого цвета.

    Допустимые значения всех аргументов команд setrgbcolor, sethsbcolor и setcmykcolor — вещественные от 0 до 1. В системе RGB аргументы задают интенсивность каждого цветового канала, как при выводе изображения на экран цветного монитора, причем ноль соответствует отсутствию яркости. Пример передачи 8 основных цветов (трех чистых и их смесей) с соответствующими параметрами команды setrgbcolor приведен на рис. 13.17. В системе HSB чистые цвета передаются значениями: 0 — красный, 1/3 — зеленый, 2/3 — синий, 1 — вновь красный, а промежуточные значения соответствуют смешению ближайших чистых цветов в соответствующей пропорции. Насыщенность — это отношение выбранного цвета к серому, примешиваемому к нему (0 — серый, 1 — чистый цвет без примеси серого). Яркость задает общую интенсивность цвета. Белому цвету соответствует нулевое значение насыщенности (чистый серый тон) и единичное значение яркости при любом значении цвета. Чистые цвета задаются выбранным значением цвета при единичных значениях насыщенности и яркости. В системе CMYK задается количество краски, которое накладывается на бумагу в одной точке, как при фотопечати или при печати на цветном принтере. Здесь большее значение параметра задает более темный цвет. Равное количество всех трех цветных красок задает оттенок серого цвета, поэтому канал черного цвета является избыточным. Принято, однако, полутоновые изображения кодировать только интенсивностью черного цвета, а цветные — смешением только цветных красок.

    Независимо от того, в какой из цветовых систем устанавливается текущий цвет, его можно определить с помощью справочных команд currentrgbcolor, currencmykbcolor или currenthsbcolor.


    Рис. 13.16. Шкала оттенков серого цвета. Приведены аргументы команды setgray


    Рис. 13.17. Передача основных цветов черно-белым принтером. Под квадратами
    приведены аргументы команды setrgbcolor. Цвета расположены
    в следующей последовательности: черный, красный, зеленый, синий, желтый, пурпурный, голубой, белый

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

    Команды settransfer и setcolortransfer определяют текущие функции передачи яркости и цвета. Установленные функции используются командами задания цвета и влияют на цвет графических объектов до момента изменения функции передачи.

    У команды settransfer один аргумент — яркость сигнала (от 0 до 1). Она заносит в стек новую яркость. Эта функция используется для коррекции передачи как полутоновых, так и цветных изображений. В последнем случае одинаково корректируются яркости сигнала в каждом цветовом канале (в системе RGB).

    Приведем несколько примеров задания функции передачи:

    { 0.8 exp } settransfer % гамма-коррекция y=x0.8 (изменение контрастности)
    { neg 1 add } settransfer % инверсия сигнала (негативное изображение)
    { 5 mul round 5 div } settransfer % дискретная функция передачи
    {} settransfer % восстановление нормальной функции передачи яркости
    

    На рис. 13.18 показана линейная шкала оттенков серого цвета, выведенная с разными функциями передачи. Прототип этой шкалы изображен на рис. 13.16. На шкалах 1–7 по-разному скорректирована контрастность (по-разному проведена гамма-коррекция), на 8-й шкале инвертирована интенсивность, а на 9-й шкале изображение сделано значительно более светлым и менее контрастным функцией {0.3 mul 0.7 add}.


    Рис. 13.18. Коррекция аппаратной функции цветопередачи. 1–7 — гамма-коррекция:
    1) g = 0,4, 2) g = 0,6, 3) g = 0,8, 4) g = 1 (яркость не изменяется),
    5) g = 1,2, 6) g = 1,4, 7) g = 1,6; 8) — негативное изображение; 9) — осветленное изображение.
    Под квадратами указаны аргументы команды setgray

    Команда setcolortransfer имеет 4 аргумента. Первые три — это процедуры, которые задают индивидуальные функции передачи по трем цветовым каналам (RGB) и используются только цветными устройствами вывода (цветными принтерами). Четвертая процедура задает передачу общей яркости и используется только черно-белыми принтерами. На конкретном принтере используется только один из методов коррекции — либо по трем цветовым каналам, либо по общей яркости серого цвета.

    Шрифты и вывод текста

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

    Печать текста

    Команды печати можно разделить на две группы — команды подготовки шрифтов и команды вывода текста. Они команды приведены в табл. 13.9.

    Таблица 13.9. Команды подготовки шрифтов и вывода текста

    Команда Описание
    Команды подготовки шрифтов
    /имя findfont ® шрифт Найти шрифт по его имени
    шрифт кегль scalefont ® шрифт Масштабировать шрифт
    шрифт setfont ® - Установить текущий шрифт
    - currentfont ® шрифт Поместить текущий шрифт в стек
    Команды вывода текста
    (строка) stringwidth ® dx dy Определить смещение текущей точки, которое произойдет при выводе строки
    (строка) show ® - Вывести строку
    dx dy (строка) ashow ® - Вывести строку, увеличив ширину символов на (dx, dy)
    cx cy символ (строка) widthshow ® - Вывести строку, увеличив ширину указанного символа на (cx, cy)
    cx cy символ dx dy (строка) awidthshow ® - Комбинация ashow и widthshow
    {проц} (строка) kshow ® - Вывести строку, выполняя указанную процедуру после вывода каждого символа
    (строка) b charpath ® - Добавить контуры символов к текущему пути

    Подготовка шрифта заключается в его отыскании по имени, масштабировании и установке в качестве текущего шрифта:

    /Times-Roman findfont 12 scalefont setfont

    Кегль шрифта, выдаваемого командой findfont, равен 1. Масштабированием можно установить необходимый размер символов. Высотой символов считается расстояние от самой нижней точки символов с выносом под базовую линию до верхней точки самого высокого символа, то есть BoundingBox всех символов.

    При временном изменении шрифта можно записать текущий шрифт в стек, а после использования нового шрифта восстановить текущий. Эти действия выполняются быстрее, чем две команды gsave/grestore.

    Вывод строк выполняется командой show. Начало первого символа располагается в текущей точке, а вывод происходит в направлении, традиционном для используемого языка. В европейских языках — это положительное направление оси OX, поэтому команда stringwidth в этих языках всегда возвращает нулевое значение dy. Вывод осуществляется текущим цветом в текущей системе координат. В листинге 13.1 (рис. 13.1–13.3) было показано, как можно изменить масштаб координатных осей для изменения направления вывода текста.

    Приведем три процедуры, которые осуществляют вывод текста справа и слева от текущей точки и центрируют текст в текущей точке:

    %!PS-Adobe-1.0
    %%Title: Вывод текста
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 25 40 175 102
    % стек процедур: (строка) ® -
    % 1) вывести справа от точки (синоним команды show):
    /r-show { show } def
    % 2) вывести слева от точки (выравнивание по правому краю):
    /l-show { dup stringwidth neg exch neg exch rmoveto show } def
    % 3) центрировать строку в текущей точке:
    /c-show { dup stringwidth -2 div exch -2 div exch rmoveto show } def
    %%EndProlog
    %%Page: 1 1
    gsave
    newpath
    100 100 moveto
    100 42 lineto
    0.1 setlinewidth
    stroke
    /Times-Roman findfont 10 scalefont setfont
    100 80 moveto (Left adjustment) r-show
    100 68 moveto (Right adjustment) l-show
    /Times-Roman findfont 14.4 scalefont setfont
    100 51 moveto (Center adjustment) c-show
    grestore
    showpage
    %%Trailer
    


    Рис. 13.19. Результаты работы процедур выравнивания текста слева, справа и по центру

    В командах widthshow и awidthshow символ задается его кодом. В следующей программе показано использование этих команд для печати вразрядку и со сдвигом вниз на каждом пробеле:

    %!PS-Adobe-1.0
    %%Title: Вывод текста
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 95 30 410 110
    /TimesNRCyrMT findfont 10 scalefont setfont % шрифт из файла «times8.pfa»
    /authors (Авторы: А. В. Комолкин, С. А. Немнюгин и М. П. Чаунин) def
    %%EndProlog
    %%Page: 1 1
    gsave
    newpath
    100 100 moveto
    authors show    % обычная печать
    100 88 moveto
    1 0 authors ashow % печать вразрядку в 1 пункт между каждым символом
    100 76 moveto
    0 -2 8#40 authors widthshow % каждое слово опускается на 2 пункта
    % смещение текущей точки к левому краю на 2 пункта ниже текущей высоты:
    currentpoint exch % стек: y x
    pop 2 sub         % стек: y-2
    100 exch          % стек: 100 y-2
    moveto
    0 -2 8#40 1 0 authors awidthshow % комбинация двух предыдущих видов печати
    grestore
    showpage
    %%Trailer
    


    Рис. 13.20. Примеры печати вразрядку со сдвигом вниз

    Познакомимся с командой kshow. Очередной символ печатается в текущей точке, после чего текущая точка сдвигается к правой границе символа и вызывается процедура, которая получает в качестве аргументов два числа — коды напечатанного и следующего символов. Эта процедура может выполнять любые действия по изменению графического контекста — передвигать текущую точку, изменять масштаб и ориентацию осей координат, изменять шрифт и его кегль и т. д. После выполнения процедуры будет напечатан очередной символ, уже в новом графическом контексте. В Приложении Б приведена программа вывода строки по кругу. Шрифт можно менять во время печати. Пусть необходимо печатать прописные буквы прямым шрифтом, а строчные — курсивом:

    %!PS-Adobe-1.0
    %%Title: Изменение шрифта
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 95 95 360 110
    /rm /Times-Roman findfont 10 scalefont def  % Прямой шрифт
    /it /Times-Italic findfont 10 scalefont def % Курсив
    /authors (Authors: A. V. Komolkin, S. A. Nemnyugin and M. P. Chaunin) def
    %%EndProlog
    %%Page: 1 1
    gsave
    newpath
    100 100 moveto
    rm setfont  % прямой шрифт для первой буквы
    { % стек процедуры: символ1 символ2 ® -
      % выделяем диапазон от 'a' до 'z'
      dup         % стек: символ1 символ2 символ2
      8#172 le    % стек: символ1 символ2 (символ2Ј'z')
      exch        % стек: символ1 (символ2Ј'z') символ2
      8#141 ge    % стек: символ1 (символ2Ј'z') (символ2і'a')
      and         % стек: символ1 (символ2Ј'z')Щ(символ2і'a')
      { it }      % процедура занесения в стек курсивного шрифта, если true
      { rm }      % процедура занесения в стек прямого шрифта, если false
      ifelse      % выбор одного действия из двух
                  % стек: символ1 шрифт
      setfont     % стек: символ1
      pop         % стек: -
    } authors kshow
    grestore
    showpage
    %%Trailer
    


    Рис. 13.21. Пример изменения шрифта во время печати строки

    Контуры символов в векторных шрифтах можно добавить к текущему графическому пути командой charpath. Второй аргумент этой команды показывает, что надо делать с теми шрифтами, в которых символы описываются не контурами, а путями проведения линий (см. параметр PaintType в табл. 13.12 и его описание в следующем параграфе). Если значение этого аргумента false, команда добавляет путь без изменений. Вдоль такого пути можно только провести линию командой stroke. Если значение true, путь обрабатывается так, что к полученному пути можно применять только команды fill или clip.

    Следующая программа показывает все возможные операции с графическим путем, состоящим из контура слова Times:

    %!PS-Adobe-1.0
    %%Title: Команда charpath
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 16 95 203 153
    /Times-Roman findfont 72 scalefont setfont
    %%EndProlog
    %%Page: 1 1
    gsave
    newpath
    20 100 moveto
    (Times) false charpath % Создаем новый графический путь
    gsave          % 1) закрашиваем путь
      0.5 setgray
      fill
    grestore
    gsave          % 2) определяем геометрический центр пути
      flattenpath
      pathbbox
      /y1 exch def % стек: x0 y0 x1
      /x1 exch def % стек: x0 y0
      y1 add 2 div % стек: x0 (y0+y1)/2
      /yc exch def % стек: x0 % переменная yc содержит ординату центра текста
      x1 add 2 div % стек: (x0+x1)/2
      /xc exch def % стек: -  % переменная xc содержит абсциссу центра текста
    grestore
    % здесь определены 4 новых переменных: x1, y1, xc, yc
    gsave          % 3) ограничиваем область рисования и строим окружности
      clip
      0.8 setgray
      0.2 setlinewidth
      2 2 100      % заголовок цикла – от 2 с шагом 2 до 100
      { newpath    % стек: R % параметр цикла - радиус окружности в пунктах
        xc exch    % стек: xc R
        yc exch    % стек: xc yc R
        0 360      % стек: xc yc R 0° 360°
        arc        % стек: - % рисуем окружность
        closepath  % замыкаем окружность
        stroke     % выводим часть окружности, попавшей в область рисования
      } for        % цикл – строим 50 окружностей
    grestore
    gsave          % 4) обводим контур
      0 setgray
      0.75 setlinewidth
      stroke
    grestore
    grestore
    showpage
    %%Trailer
    

    В этой программе графический путь из контуров букв создается только один раз. После этого все операции выполняются между командами gsave/grestore, которые после завершения очередной операции восстанавливают графический контекст. Линия вдоль контура букв выводится в последнюю очередь, для того чтобы светлые окружности были нарисованы только внутри букв и не повредили бы эту линию.


    Рис. 13.22. Пример операций с контурами символов

    Команды работы со словарями и шрифтами

    В PostScript шрифт хранится в словаре. Основные команды работы со словарями, которые работают и с данными других типов, были приведены в табл. 13.6. В табл. 13.10 приведены команды, предназначенные для работы со словарями и шрифтами.

    Таблица 13.10. Команды языка PostScript для работы со словарями и шрифтами

    Команда Описание
    Работа с произвольным словарем
    n dict ® словарь Создание словаря
    словарь length ® n Определение числа записей в словаре
    словарь maxlength ® n Определение емкости словаря
    словарь /имя объект put ® - Занесение записи в словарь
    словарь /имя get ® объект Выбор значения по имени из словаря
    словарь /имя known ® b Проверка наличия записи с указанным именем в словаре
    словарь {проц} forall ® - Циклическое выполнение процедуры для всех записей
    словарь readonly ® словарь Запрещение записи новых словарных записей в словарь
    Работа с текущим словарем
    словарь begin ® - Занесение словаря в стек словарей. Словарь становится текущим
    /имя объект def ® - Занесение словарной записи в текущий словарь
    - end ® - Удаление текущего словаря из стека словарей
    - currentdict ® словарь Занесение текущего словаря в стек операндов. Стек словарей при этом не изменяется
    - userdict ® словарь Занесение пользовательского словаря в стек операндов
    - systemdict ® словарь Занесение системного словаря в стек операндов
    Работа со шрифтами
    /имя словарь definefont ® шрифт Регистрация нового шрифта в каталоге шрифтов
    - FontDirectory ® словарь Занесение каталога шрифтов в стек операндов
    - StandardEncoding ® [массив] Занесение в стек массива со стандартной кодировкой шрифтов

    Команда dict создает пустой словарь с указанной емкостью. Команда length определяет текущее число записей в словаре, а команда maxlength — емкость словаря.

    Запись из словаря нельзя удалить — в языке нет команд, противоположных командам put и def. В случае необходимости использования временных переменных можно создать небольшой словарь, поместить его в стек словарей и использовать для хранения новых переменных. В таких словарях можно переопределять стандартные команды PostScript. После удаления словаря из стека интерпретатор не будет использовать его для поиска переменных.

    Команда readonly делает словарь доступным только для чтения. Запись в такой словарь запрещена, а попытка выполнить команду записи приводит к ошибке и к завершению интерпретации PostScript-файла. Команда readonly применяется не только к словарям, но и к объектам других типов. После регистрации шрифта он автоматически становится защищенным от записи.

    Команда known позволяет узнать, содержится ли в словаре переменная с указанным именем. Команды currentdict, userdict и systemdict записывают в стек операндов ссылки на текущий, пользовательский и системный словари.

    Шрифт представляет собой словарь, в котором хранятся определенные записи. Мы познакомимся только с теми из них, которые важны для изменения кодировки шрифтов. В табл. 13.11 приведены имена и типы соответствующих записей.

    Таблица 13.11. Стандартные имена и типы данных в словаре, хранящем шрифты

    Имя Тип Описание
    /CharStrings Словарь Содержит имена определенных в данном шрифте символов и процедуры их построения
    /Encoding Массив из 256 имен Массив из 256 имен символов, определяющий кодировку шрифта
    /FontMatrix Массив из 6 Матрица преобразования из системы координат, вещественных в которой определены символы, в систему значений координат изображения
    /FontName Имя Имя шрифта. Оно может не совпадать с именем, по которому шрифт ищется в каталоге шрифтов
    /FontInfo Словарь Дополнительная информация о шрифте. Не влияет на вид символов
    /PaintType Целое от 0 до 3 Указывает, как символы должны быть построены
    /FontType Целое от 1 до 3 Тип шрифта
    /UniqueID Целое от 0 до 224-1 Уникальный идентификатор, определяемый разработчиком
    /FID Специальный тип Внутренний идентификатор шрифта. Переменная, устанавливаемая при регистрации шрифта интерпретатором

    Процедуры построения символов содержатся в словаре CharStrings. Каждая процедура имеет в нем уникальное имя, по которому и выполняется ее вызов. Имена процедур (имена символов) определены стандартом фирмы Adobe на названия символов. Часть имен содержится в массиве Encoding. При выводе очередного символа интерпретатор выбирает по его коду имя процедуры из массива Encoding. Таким образом, кодировка шрифта определяется данным массивом. Пользователь может создавать собственные шрифты из уже существующих, изменяя, в частности, массив Encoding с кодировкой шрифта.

    Последовательность действий интерпретатора при печати, например, символа @ такова:

    1. В качестве словаря выбирается текущий шрифт.

    2. Выбирается очередной символ из строки: @.

    3. Его код 8#100 используется как индекс массива Encoding.

    4. Из массива Encoding выбирается имя символа: /at.

    5. По имени /at в словаре CharStrings выбирается процедура.

    6. Выполняется процедура печати символа на бумаге.

    В листинге 13.8 приведена программа, которая выводит все символы, имеющиеся в указанном шрифте, и их названия. Если символов слишком много, очередные столбцы окажутся правее края листа бумаги и пропадут. Например, шрифт Times-Roman в ghostscript содержит 316 символов, в принтере Hewlett-Packard LaserJet 5000 он имеет 601 символ, а минимальный набор символов в этом шрифте, определенный стандартом Adobe, может содержать 210 символов. Читатель может изменить программу так, чтобы она переносила изображение на новый лист бумаги после заполнения текущего листа (см. задание 6 для практической работы).

    Листинг 13.8. Программа печати названий, изображений и кодов всех символов текущего шрифта

    %!PS-Adobe-1.0
    %%Title: Все символы шрифта
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    /fontn
    %-------------------------------%
    % Здесь можно задать имя шрифта %
        /ZapfDingbats
      % /Times-Roman
      % /Symbol
      % /TimesNRCyrMT
    %-------------------------------%
    def
    /cf
      /Times-Italic findfont 10 scalefont % шрифт для печати имен
    def
    /st 50 string def % буфер для команды cvs
    /ct ( ) def       % буфер для печати одного символа
    clippath          % определение размеров видимой области на листе
    pathbbox
    /ymax exch def
    /xmax exch def
    /ymin exch def
    /xmin exch def   % BoundingBox видимой области
    /ymax ymax 40 sub def % положение верхней строки с учетом полей
    /xmin xmin 20 add def % левая граница
    /ymin ymin 30 add def % нижняя граница для строки
    /nextline { % процедура для перехода на новую строку
      currentpoint pop % используем только координату X
      dup x1 gt
        { /x1 exch def }
        { pop }
      ifelse                % x1 = max(X,x1)
      /ypos ypos 11 sub def % положение следующей строки
      ypos ymin lt {        % если строка ниже, чем минимальная высота:
        /ypos ymax def      % переход на верхнюю строку
        /xpos x1 20 add def % следующего столбца
      } if
      xpos ypos moveto      % переходим на новую строку
    } def
    %%EndProlog
    %%Page: 1 1
    gsave
    /x1 0 def        % правая позиция столбца (вычисляется в процессе печати)
    /ypos ymax def   % текущая позиция строки
    /xpos xmin def   % текущая позиция столбца
    fontn findfont 10 scalefont /fn exch def % интересующий нас шрифт
    fn /CharStrings get % выбираем из шрифта словарь CharStrings
    /cs exch def        % сохраняем словарь
    fn /Encoding get    % выбираем из шрифта текущий массив кодировок
    /en exch def        % сохраняем массив
    xpos ypos moveto    % начало печати
    cf setfont
                        % 1) печатаем название шрифта
    (Font ) show        % слово «Шрифт»
    fn /FontName get    % выбираем название из словаря со шрифтом
    st cvs show         % преобразуем имя в строку и печатаем
    nextline            % переход на новую строку
                        % 2) печатаем общее число символов в шрифте
    (Total ) show       % слово «Всего»
    cs length           % определяем длину словаря
    st cvs show         % преобразуем число в строку и печатаем
    ( symbols) show     % слово «символов»
    nextline            % переход на новую строку
    nextline            % пропуск одной строки
    % вывод имен всех символов и самих
    % символов, включенных в текущую кодировку
    cs                  % словарь   — 1-й аргумент команды forall
    {                   % процедура — 2-й аргумент команды forall
      pop               % удаляем второй аргумент (ссылку на процедуру)
      /sym exch def     % записываем в переменную имя символа
      currentpoint      % сохраняем текущую точку (*)
      0 1 255 {         % аргументы цикла for - поиск имени в массиве Encoding
        /kod exch def   % сохраняем параметр цикла
        en kod get      % выбираем название по коду
        sym eq {        % сравниваем с текущим названием
          fn setfont    % устанавливаем шрифт
          ct 0 kod put  % создаем строку из одного символа с этим кодом
          ct show       % печать символа
          exit          % выход из цикла после печати символа
        } if            % if — если имя найдено в массиве Encoding
      } for             % for — цикл по всем кодам (0…255)
      moveto            % переход в сохраненную точку (*) до печати символа
      20 0 rmoveto      % «табуляция» на 20 пунктов вправо
      cf setfont        % установка основного шрифта
      sym st cvs show   % преобразуем имя в строку и печатаем
      nextline          % переход на новую строку
    } forall            % цикл по всем именам из словаря CharStrings
    grestore
    showpage
    %%Trailer
    

    Часть страницы, напечатанной интерпретатором ghostscript в результате выполнения этой программы, показана на рис. 13.23. Интерпретатор ghostscript использует свободно распространяемый шрифт Dingbats, разработанный компанией URW Software. Этот шрифт является аналогом шрифта ZapfDingbats фирмы Adobe и распознается интерпретатором под обоими названиями.


    Рис. 13.23. Названия и вид символов в шрифте Dingbats. Показана
    часть страницы, напечатанной интерпретатором ghostscript

    Научившись определять символы, содержащиеся в конкретном шрифте, мы можем напечатать названия всех шрифтов, содержащихся в каталоге шрифтов принтера. За основу можно принять программу 13.8, оставив из нее алгоритм перехода на новую строку и алгоритм основного цикла. Команда FontDirectory возвращает словарь, содержащий каталог всех известных принтеру шрифтов. Новая программа приведена в листинге 13.9, а часть страницы, напечатанной принтером Hewlett-Packard LaserJet 5000, — на рис. 13.24. Эта программа не может показать все шрифты, известные интерпретатору ghostscript, так как он загружает шрифты из файлов динамически, по мере их использования.

    Листинг 13.9. Программа печати каталога шрифтов принтера

    %!PS-Adobe-1.0
    %%Title: Все шрифты принтера
    %%Creator: А. В. Комолкин
    %%Pages: 1
    %%BoundingBox: 0 0 595 842
    /cs
      FontDirectory
    def                     % словарь с каталогом шрифтов
    /cf
      /Times-Italic findfont 10 scalefont
    def                     % шрифт для печати имен
    /st 50 string def       % буфер для команды cvs
    clippath                % определение размеров видимой области на листе
    pathbbox
    /ymax exch def
    /xmax exch def
    /ymin exch def
    /xmin exch def          % BoundingBox видимой области
    /ymax ymax 40 sub def   % положение верхней строки с учетом полей
    /xmin xmin 20 add def   % левая граница
    /ymin ymin 30 add def   % нижняя граница для строки
    /nextline {             % процедура для перехода на новую строку
      currentpoint pop      % используем только координату X
      dup x1 gt
        { /x1 exch def }
        { pop }
      ifelse                % x1 = max(X,x1)
      /ypos ypos 11 sub def % положение следующей строки
      ypos ymin lt {        % если строка ниже, чем минимальная высота:
        /ypos ymax def      % переход на верхнюю строку
        /xpos x1 20 add def % следующего столбца
      } if
      xpos ypos moveto      % переходим на новую строку
    } def
    %%EndProlog
    %%Page: 1 1
    gsave
    /x1 0 def           % правая позиция столбца (вычисляется в процессе печати)
    /ypos ymax def      % текущая позиция строки
    /xpos xmin def      % текущая позиция столбца
    xpos ypos moveto    % начало печати
    cf setfont
                        % 1) печатаем название принтера
    (Printer ) show     % слово "Принтер"
    product show        % узнаем и печатаем название принтера
    nextline            % переход на новую строку
                        % 2) печатаем общее число шрифтов
    (Total ) show       % слово "Всего"
    cs length           % определяем длину каталога шрифтов
    st cvs show         % преобразуем в строку и печатаем
    ( fonts) show       % слово "шрифтов"
    nextline            % переход на новую строку
    nextline            % пропуск одной строки
    % собственно вывод имен всех символов и самих
    % символов, включенных в текущую кодировку
    cs                  % каталог шрифтов - 1-ый аргумент команды forall
    {                   % процедура       - 2-ой аргумент команды forall
      pop               % удаляем второй аргумент (ссылку на шрифт)
      st cvs show       % преобразуем имя в строку и печатаем
      nextline          % переход на новую строку
    } forall            % цикл по всем именам из словаря CharStrings
    grestore
    showpage
    %%Trailer
    


    Рис. 13.24. Часть списка PostScript-шрифтов принтера Hewlett-Packard LaserJet 5000

    Массив FontMatrix является матрицей преобразования координат (x, y) системы координат символа в координаты (x', y') системы координат изображения и состоит из 6 чисел. Формулы преобразования имеют вид:

    x' = sxx + by + tx
    y' = cx + syy + ty

    Коэффициенты расположены в матрице в таком порядке: [sx, b, c, sy, tx, ty]. Значения tx и ty определяют сдвиг системы координат, sx и sy — масштаб, а b и c — вращение системы координат или изменение угла между осями X и Y. Обычное значение матрицы преобразования — [0,001 0 0 0,001 0 0], то есть 1000 единиц соответствует 1 пункту.

    Переменная FID является внутренней переменной шрифта, определяющей шрифт в данном принтере. Она устанавливается интерпретатором при регистрации шрифта командой definefont. При создании новых шрифтов эту переменную нельзя копировать из старого шрифта в новый. Для нее необходимо зарезервировать место в словаре. В отличие от нее переменная UniqueID определяется разработчиком шрифта и может быть скопирована из старого шрифта. Переменная FontType определяет, как построены шрифты и для каких операций они приспособлены. Большинство шрифтов имеют тип 1. Это означает, что их можно использовать в команде charpath. Тип рисования PaintType определяет, какие операции должен выполнять интерпретатор для вывода изображения символов после выполнения внутренних процедур построения символов: 0 — символы заполняются цветом командой fill, 1 — символы выводятся линиями командой stroke, 2 — символы обводятся по контуру, 3 — процедуры вывода символов сами выполняют необходимые действия по выводу изображения. При построении нового шрифта на основе старого можно изменить значение этой переменной с 0 на 2 и обратно. При других изменениях результат печати текста может быть непредсказуемым.

    Словарь FontInfo содержит информацию о шрифтах, которая может оказаться полезной. Логическая переменная isFixedPitch показывает, имеют ли символы одинаковую ширину (true) или шрифт пропорциональный (false). Число UnderlinePosition представляет собой расстояние от базовой линии строки до линии подчеркивания, а UnderlineThickness — толщину подчеркивающей линии. Эти величины задаются в единицах системы координат символа.

    Стандартные названия символов в PostScript-шрифтах

    В PostScript определены стандартные названия символов. Названия букв английского алфавита совпадают с самими буквами. Другие символы называются английским словом или несколькими словами, возможно, с сокращениями, пишущимися слитно. Например, используемые в русском языке кавычки «и» называются guillemotleft и guillemotright и имеют коды в стандартной кодировке 8#253 и 8#273, а кавычки „ и ” называются quotedblbase (8#271) и quotedblleft (8#252). Все кавычки имеются в стандартных PostScript-шрифтах. Русские буквы получили названия, которые приведены в табл. 13.12. В стандартных шрифтах русских букв нет.

    Таблица 13.12. Названия и восьмеричные коды КОИ-8 русских букв и символа №

    Буква Прописная Строчная Буква Прописная Строчная
    код имя код имя код имя код имя
    А 341 afii10017 301 afii10065 Р 362 afii10034 322 afii10082
    Б 342 afii10018 302 afii10066 С 363 afii10035 323 afii10083
    В 367 afii10019 327 afii10067 Т 364 afii10036 324 afii10084
    Г 347 afii10020 307 afii10068 У 365 afii10037 325 afii10085
    Д 344 afii10021 304 afii10069 Ф 346 afii10038 306 afii10086
    Е 345 afii10022 305 afii10070 Х 350 afii10039 310 afii10087
    Ё 263 afii10023 243 afii10071 Ц 343 afii10040 303 afii10088
    Ж 366 afii10024 326 afii10072 Ч 376 afii10041 336 afii10089
    З 372 afii10025 332 afii10073 Ш 373 afii10042 333 afii10090
    И 351 afii10026 311 afii10074 Щ 375 afii10043 335 afii10091
    Й 352 afii10027 312 afii10075 Ъ 377 afii10044 337 afii10092
    К 353 afii10028 313 afii10076 Ы 371 afii10045 331 afii10093
    Л 354 afii10029 314 afii10077 Ь 370 afii10046 330 afii10094
    М 355 afii10030 315 afii10078 Э 374 afii10047 334 afii10095
    Н 356 afii10031 316 afii10079 Ю 340 afii10048 300 afii10096
    О 357 afii10032 317 afii10080 Я 361 afii10049 321 afii10097
    П 360 afii10033 320 afii10081 нет afii61352

    В стандартной кодировке КОИ-8 не предусмотрено место для символа №, поэтому его чаще всего помещают под номерами 2718 или 2068. Кроме современных русских букв стандартизированы имена старославянских букв и букв других славянских языков.

    Изменение кодировки и названия шрифта

    Рассмотрим методы изменения кодировки шрифтов и изменения их названий. Изменить кодировку можно, создав новый шрифт с соответствующим массивом Encoding. Есть несколько методов решения этой задачи, но алгоритм во всех случаях примерно одинаковый:

    1. Создание пустого словаря необходимой длины для хранения нового шрифта.

    2. Копирование всех записей, кроме /FID, из старого шрифта в новый словарь.

    3. Изменение названия шрифта FontName.

    4. Создание массива с требуемой кодировкой символов.

    5. Запись в словарь нового массива Encoding.

    6. Регистрация нового шрифта командой definefont.

    В листинге 13.10 предлагается еще один вариант процедуры изменения кодировки шрифта. У процедуры три аргумента — имя старого шрифта, имя нового шрифта и вектор кодировки. Выходных значений нет. Действие процедуры заключается в создании нового шрифта и его регистрации в каталоге шрифтов.

    Листинг 13.10. Процедура создания нового шрифта

    % Процедура создания шрифта с новой кодировкой
    /novel-font { % процедура
      % стек процедуры:
      %  /имя-старого-шрифта /имя-нового-шрифта массив-имен-букв ® -
      /new-encoding  exch def % новый массив с кодировкой
      /new-font-name exch def % имя нового шрифта
      findfont                % стек: /имя-старого-шрифта ® шрифт
      dup length              % стек: шрифт длина % длина старого словаря
      dict                    % стек: шрифт словарь % создание пустого словаря
      begin                   % занесение словаря в стек словарей,
                              % новый словарь становится текущим
                              % стек: шрифт % - 1-й аргумент команды forall
        {                     % процедура     – 2-й аргумент команды forall
          exch dup      % стек: объект /имя /имя % очередная переменная
          /FID eq       % стек: объект /имя /имя=/FID % проверяем на имя «FID»
          {             % 1-я процедура ifelse
            pop pop     % Не заносим переменную /FID в новый словарь
          }{            % 2-я процедура ifelse
            exch        % стек: /имя объект
            def         % добавляем запись в текущем (то есть новом) словаре
          } ifelse      % выбор: имя равно или не равно /FID
        } forall        % цикл по всем элементам старого шрифта
                        % стек: -
        /FontName new-font-name def % записываем новое имя шрифта
        /Encoding new-encoding def  % записываем новую кодировку шрифта
        new-font-name   % стек: /имя-нового-шрифта
        currentdict     % стек: /имя-нового-шрифта словарь
      end               % убираем текущий словарь из стека словарей
      definefont        % стек: новый-шрифт
      pop               % стек: -
    } def
    

    Приведем примеры создания разных кодировок. Можно создать массив путем задания всех 256 значений. Если имеется пакет enscript, можно взять файлы с массивами кодировок из системного каталога этой программы. Полное имя каталога указано в электронном справочнике по теме enscript. Файлы имеют имена, совпадающие с названием требуемой кодировки, и суффикс .enc. Например, файл 88595.enc содержит массив кодировки русских букв ISO-8859-5, а файл koi8.enc — массив кодировки КОИ-8. Обратим внимание на то, что имя .notdef находится в тех позициях, которые не кодируют печатаемые символы. Эти символы не печатаются, но при их выводе ошибка не возникает. В листинге 13.11 приведен файл koi8.enc, в котором создается массив encoding_vector с кодировкой КОИ-8. Данный способ может не давать желаемого результата для шрифтов, в которых нет полного набора кириллических символов или символов стандартных шрифтов. Другой способ — заполнение в цикле всего массива именами .notdef с последующим определением командами put только необходимых элементов. В листинге 13.12 показано заполнение вектора кодировки, который определяет только стандартные символы в кодировке КОИ-8, то есть символы ASCII с кодами 040–176 и русские буквы.

    Листинг 13.11. Файл koi8.enc из пакета enscript, создающий массив с кодировкой КОИ-8

    % koi8 encoding vector.
    % Информация об авторских правах и условиях распространения
    % -- code follows this line --
    /encoding_vector [
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /space        	/exclam       	/quotedbl     	/numbersign   	
    /dollar       	/percent      	/ampersand    	/quoteright   	
    /parenleft    	/parenright   	/asterisk     	/plus         	
    /comma        	/hyphen       	/period       	/slash        	
    /zero         	/one          	/two          	/three        	
    /four         	/five         	/six          	/seven        	
    /eight        	/nine         	/colon        	/semicolon    	
    /less         	/equal        	/greater      	/question     	
    /at           	/A            	/B            	/C            	
    /D            	/E            	/F            	/G            	
    /H            	/I            	/J            	/K            	
    /L            	/M            	/N            	/O            	
    /P            	/Q            	/R            	/S            	
    /T            	/U            	/V            	/W            	
    /X            	/Y            	/Z            	/bracketleft  	
    /backslash    	/bracketright 	/asciicircum  	/underscore   	
    /quoteleft    	/a            	/b            	/c            	
    /d            	/e            	/f            	/g            	
    /h            	/i            	/j            	/k            	
    /l            	/m            	/n            	/o            	
    /p            	/q            	/r            	/s            	
    /t            	/u            	/v            	/w            	
    /x            	/y            	/z            	/braceleft    	
    /bar          	/braceright   	/tilde        	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /guillmotleft 	/guillmotright	/afii61352    	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /.notdef      	/.notdef      	/.notdef      	/.notdef      	
    /space        	/exclamdown   	/cent         	/afii10071    	
    /currency     	/yen          	/brokenbar    	/section      	
    /dieresis     	/copyright    	/ordfeminine  	/guillemotleft	
    /logicalnot   	/hyphen       	/registered   	/macron       	
    /degree       	/plusminus    	/twosuperior  	/afii10023    	
    /acute        	/mu           	/paragraph    	/bullet       	
    /cedilla      	/dotlessi     	/ordmasculine 	/guillemotright	
    /onequarter   	/onehalf      	/threequarters	/questiondown 	
    /afii10096    	/afii10065    	/afii10066    	/afii10088    	
    /afii10069    	/afii10070    	/afii10086    	/afii10068    	
    /afii10087    	/afii10074    	/afii10075    	/afii10076    	
    /afii10077    	/afii10078    	/afii10079    	/afii10080    	
    /afii10081    	/afii10097    	/afii10082    	/afii10083    	
    /afii10084    	/afii10085    	/afii10072    	/afii10067    	
    /afii10094    	/afii10093    	/afii10073    	/afii10090    	
    /afii10095    	/afii10091    	/afii10089    	/afii10092    	
    /afii10048    	/afii10017    	/afii10018    	/afii10040    	
    /afii10021    	/afii10022    	/afii10038    	/afii10020    	
    /afii10039    	/afii10026    	/afii10027    	/afii10028    	
    /afii10029    	/afii10030    	/afii10031    	/afii10032    	
    /afii10033    	/afii10049    	/afii10034    	/afii10035    	
    /afii10036    	/afii10037    	/afii10024    	/afii10019    	
    /afii10046    	/afii10045    	/afii10025    	/afii10042    	
    /afii10047    	/afii10043    	/afii10041    	/afii10044    	
    ] def
    

    Листинг 13.12. Заполнение вектора кодировки КОИ-8

    % создаем массив из 256 «неопределенных» символов:
    /koi8-encoding [ 256 {/.notdef} repeat ] def
    % записываем в стек подряд идущие имена символов ASCII с кодами 0408-1768:
    /space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright
    /parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one
    /two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal
    /greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z
    /bracketleft/backslash/bracketright/asciicircum/underscore/quoteleft
    /a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
    /braceleft/bar/braceright/tilde
    % заносим имена в массив (в обратном порядке)
    8#176 -1 8#040 { exch koi8-encoding 3 1 roll put } for
    % буквы «ё» и «Ё»:
    koi8-encoding 8#243 /afii10071 put
    koi8-encoding 8#263 /afii10023 put
    % записываем в стек подряд идущие русские буквы с кодами 3008-3778:
    /afii10096/afii10065/afii10066/afii10088/afii10069/afii10070/afii10086
    /afii10068/afii10087/afii10074/afii10075/afii10076/afii10077/afii10078
    /afii10079/afii10080/afii10081/afii10097/afii10082/afii10083/afii10084
    /afii10085/afii10072/afii10067/afii10094/afii10093/afii10073/afii10090
    /afii10095/afii10091/afii10089/afii10092/afii10048/afii10017/afii10018
    /afii10040/afii10021/afii10022/afii10038/afii10020/afii10039/afii10026
    /afii10027/afii10028/afii10029/afii10030/afii10031/afii10032/afii10033
    /afii10049/afii10034/afii10035/afii10036/afii10037/afii10024/afii10019
    /afii10046/afii10045/afii10025/afii10042/afii10047/afii10043/afii10041
    /afii10044
    % заносим имена в массив (в обратном порядке)
    8#377 -1 8#300 { exch koi8-encoding 3 1 roll put } for
    

    ВНИМАНИЕ. При любых изменениях в шрифтах следует давать новому шрифту новое имя.

    Новые шрифты могут быть использованы для печати текстов и листингов программ с помощью утилиты enscript. В предыдущей главе было рассказано, как могут задаваться названия таких шрифтов. Теперь читатель может использовать законный, не «хакерский» способ построения новых шрифтов с необходимыми названиями.

    Допускается изменение и других параметров шрифтов, но потребность в этом возникает редко.

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

    1. TimesNRCyrMT — прямой светлый пропорциональный с засечками.

    2. ArialCyrMT — прямой светлый пропорциональный рубленый.

    3. CourierCyrPS — прямой светлый с одинаковой шириной символов с засечками.

    4. ERKurierKOI8RRegular — прямой светлый с одинаковой шириной символов с засечками.

    Об этих шрифтах речь шла в предыдущей главе. Русские буквы в них имеют названия не в стандарте PostScript, а расположены в соответствии с кодировкой КОИ-8. При использовании этих шрифтов в ОС UNIX перекодировка не требуется.

    5. OmegaSerifCyrillic — поставляется вместе с пакетом tetex (свободно распространяемая реализация системы подготовки документов TeX/LaTeX/AMSTeX) в файле omsecy.pfb. Кроме русских букв в шрифте имеется много дополнительных кириллических символов, но буквы с диакритическими знаками (ё и й) отсутствуют.

    6. WNCYR10 — прямой светлый пропорциональный с засечками (гарнитура Washington Computer Modern Cyrillic). Этот и следующий шрифты разработаны Американским математическим обществом для системы подготовки документов AMSTeX. Шрифты не содержат английских букв. Русские буквы имеют имена не в стандарте PostScript. Кодировка не совпадает ни с одной из применяемых в различных ОС.

    7. WNCYSS10 — прямой светлый пропорциональный рубленый.

    8. cmcyr10 — прямой светлый пропорциональный с засечками (гарнитура Computer Modern Cyrillic). PostScript-версии шрифтов входят в коллекцию шрифтов ВаКоМа. Распространяются в стандартной поставке системы tetex вместе со всеми дистрибутивами ОС Linux. Шрифты содержат только кириллические символы, расположенные в соответствии с кодировкой КОИ-7. Названия букв соответствуют стандарту PostScript.

    9. cmcss10 — прямой светлый пропорциональный рубленый.

    10. Lazurski — PostScript-версия гарнитуры Лазурского, разработанной по мотивам рисунков итальянских шрифтов эпохи Возрождения. Этот и следующий шрифты распространяются в пакете PSCyr для русификации системы LaTeX. Пакет можно найти в дистрибутивах некоторых российских версий ОС Linux. PostScript-шрифты наряду с русскими содержат латинские буквы и поставляются в кодировке КОИ-8, но в стандартной кодировке отсутствует буква ё.

    11. Textbook — рубленый шрифт. PostScript-версия гарнитуры «букварная», которой в России набираются буквари и учебники для начальной школы.


    Рис. 13.25. Примеры гарнитур некоторых свободно распространяемых PostScript-шрифтов. Звездочкой отмечены шрифты,
    в которых отсутствуют буквы с диакритическими знаками

    Свободно распространяемые русские шрифты можно найти в Интернете. Кроме указанных в предыдущей главе ftp-серверов следует упомянуть сервер CTAN (архив системы TeX, ftp://ftp.tex.ac.uk/tex-archive/). Коллекция шрифтов и набор ссылок на другие источники в Интернете имеются на Web-сервере шрифтового проекта «Веди» (http://vedi.d-s.ru). Приобрести русские (и не только русские) шрифты можно в компании Adobe (http://www.adobe.com), а также в других компаниях, специализирующихся на разработке PostScript-шрифтов.

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

    Задания для практической работы

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

    2. Напишите PostScript-файлы для печати рекурсивных геометрических объектов — ковра Серпинского, кривых Гильберта, Коха, Пеано. Описание алгоритмов и примеры программ на языке Pascal можно найти в книгах Н. Вирта «Алгоритмы + структуры данных = программы» и С. А. Немнюгина «Turbo Pascal. Учебник» (издательство «Питер», 2000 г.).

    3. Напишите PostScript-файл, в котором запрограммировано решение уравнения f(x) = 0 одним из численных методов, например методом половинного деления. Задаются: функция f(x), границы интервала поиска решения a и b, и точность вычисления epsilon. Программа должна напечатать на листе бумаги оси координат и график функции f(x), построенный по нескольким (например, по 200) точкам в диапазоне [a, b]. Под графиком должно печататься значение корня. Напечатайте минимальное и максимальное значения функции.

    4. Перепишите программу из листинга 13.8 так, чтобы рядом с названием символа печатался сам символ. Для тех символов, которые не включены в стандартную кодировку, надо создать отдельный шрифт и включить эти символы в измененную кодировку. Не следует создавать для каждого символа свой шрифт, так как это приведет к переполнению памяти виртуальной машины PostScript. При использовании команд save/restore для освобождения памяти программа получится медленная. Можно разбить словарь на блоки по 256 символов, создать для каждого блока новый шрифт, а вывод символов и названий осуществлять целым блоком.

    5. Если в задании 4 символ имеется в стандартной кодировке шрифта, рядом с ним напечатайте его восьмеричный код.

    6. В задании 4 может оказаться так, что символов в шрифте слишком много и они не помещаются на одной странице. Например, в принтере Hewlett Packard LaserJet 5000 стандартные шрифты содержат 601 символ. Измените программу так, чтобы список символов печатался на нескольких страницах. Новая программа перестанет удовлетворять соглашению о структурных комментариях, так как заранее не известно, сколько страниц будет напечатано. Все команды, связанные с выводом заполненной страницы и переходом на новую страницу, надо включать в процедуру nextline. Целесообразно фиксировать число колонок, печатаемых на одной странице, и ширину колонок.

    7. Напишите программу для печати всей информации о шрифте из словаря FontInfo.

  • Книги
    Компьютеры
    Экономика
    Психология
    Популярная психология
    Юридическая
    Периодика
    Медицина
    Оздоровление
    Образование
    Воспитание
    Домоводство
    Гуманитарная
    Книга
    О книге
    Содержание
    Отрывок
    издательство | каталог | отдел сбыта | обратная связь | webmaster



    Авторские права охраняются.
    Воспроизведение материалов или их частей в любом виде без письменного разрешения запрещено!
    © 1997-2004, Издательский дом "Питер"

    Санкт-Петербург
    пр. Бол. Сампсониевский 29а
    тел. (812)-703-7374
    Москва
    тел. (095)-777-5467
    e-mail: sales@piter.com, postbook@piter.com http://www.piter.com