Методика разработки сцен на PostScript Урок 2 | Занятие второеПеред тем как продолжить рассмотрение основ языка, замечу, что для отладки программ на PostScript удобно использовать программу RIP, хотя она и работает под DOS. Так как в разных моделях принтеров реализованы различные версии этого языка, то при выводе PostScript-программы на реальный принтер посмотрите его документацию. В частности иногда перед текстом примера следует поставить знак процента, за которым следует восклицательный знак, т. е. %!. Эта комбинация символов переключает на PostScript принтер, понимающий разные языки описания страниц (обычно ещё имеется PCL5). Итак, продолжим рассмотрение команд перемещения. Как и в других языках описания страниц в PostScript имеется возможность задавать перемещение не в абсолютных координатах, а в приращениях относительно текущей точки. Для этого служат операторы rmoveto и rlineto. Пример из первого занятия можно записать так: newpath 144 72 rmoveto 0 360 rlineto stroke showpage Следующие две строки 144 432 moveto 0 -216 rlineto перемещают текущую точку вверх над сегментом первой линии и добавляют к траектории сегмент линии, проводя ее вертикально вниз (обратите внимание на отрицательный аргумент у) на 216 единиц от текущей точки. Траектория не обязательно должна быть единым связанным друг с другом куском. Она может содержать на текущей странице любой набор прямых линий и кривых. Попробуйте написать программу, рисующую две пересекающиеся прямые (в этом случае текущий путь не будет последовательным). ПрямоугольникНапишем программу, рисующую квадрат со стороной один дюйм, расположенный в центре страницы: newpath 200 300 moveto 0 72 rlineto 0 -72 rlineto -72 0 rlineto 5 setlinewidth stroke showpage Использование приращений позволяет вам поместить квадрат в любое место страницы, исправив только одну строку. В этой программе новой является только предпоследняя строка 5 setlinewidth Оператор setlinewidth позволяет вам установить ширину линии. В этой строке задана ширина 5/72 дюйма. Данный оператор действует на все линии, помещаемые на текущую страницу, пока не встретится другой оператор setlinewidth. Наш квадрат, как вы заметили, имеет выщерблину в левом нижнем углу, так как у линий заметная толщина. Чтобы избежать этого явления, следует использовать новый оператор: closepath. newpath 200 300 moveto 0 72 rlineto 72 0 rlineto 0 -72 rlineto closepath 5 setlinewidth stroke showpage Оператор closepath добавляет к текущему пути линию, соединяющую текущую точку с начальной точкой адресуемой оператором moveto. Научившись строить квадрат посмотрим как его можно заполнить. Для этой цели служит оператор fill, который "заливает" квадрат черными чернилами: newpath 200 300 moveto 0 72 rlineto 0 -72 rlineto -72 0 rlineto сlosepath fill showpage Обратите внимание, что на этот раз вместо перенесения прямых на текущую страницу (stroke), мы вызываем оператор fill, который заполняет некоторым цветом очерченную область. Уровень серого цвета для заполнения фигуры задается аргументом оператора setgray -- числом в интервале от 0 (черный цвет) до 1(белый). newpath 200 300 moveto 0 72 rlineto 0 -72 rlineto -72 0 rlineto сlosepath .6 setgray fill showpage Заданный уровень серого действует до появления следующего оператора setgray. Если setgray не задан, то по умолчанию область заполняется черным цветом. Так как у каждого типа принтера свой способ построения полутонов, то полутона одной и той же PostScript-страницы, выведенной на разных принтерах могут не совпадать. То, что вы получите запустив эти программы может не совпадать по полутонам с рисунками примеров. Перекрывающиеся областиПри рисовании перекрывающихся областей цвет их пересечения определяется цветом, нанесенным на текущую страницу последним. Пример: нарисуем два перекрывающихся прямоугольника. newpath % серый квадрат 200 300 moveto 0 72 rlineto 72 0 rlineto 0 -72 rlineto сlosepath 0.5 setgray fill newpath % светлый квадрат 236 336 moveto 0 72 rlineto 72 0 rlineto 0 -72 rlineto closepath .8 setgray fill showpage % послать на принтер Обратите внимание, что каждый квадрат начинается с оператора moveto. Это связано с тем, что оператор fill очищает текущую траекторию и после него не определена текущая точка, поэтому lineto и rlineto не имеют начальной точки. Оператор stroke также очищает текущий путь. Каждый блок этой программы содержит также комментарий, который начинается со знака % и продолжается до конца строки. Все что следует за знаком % в строке PostScript-программы интерпретатором игнорируется. Процедуры и переменныеСловарь (dictionary) - это таблица, которая связывает между собой пары объектов. Например, словарь терминов по мультимедиа связывает слова с их толкованиями. PostScript-словарь связывает объект, именуемый ключом, с другим объектом - значением этого ключа. Интерпретатор языка PostScript может искать по ключу в словаре и получать его значение, если такой ключ есть в таблице. PostScript всегда имеет два словаря: системный и пользовательский. Системный словарь объединяет имя каждого встроенного в язык оператора с соответствующим ему действием. Словарь пользователя ассоциирует имена с процедурами и переменными, определенными в программе. Когда интерпретатор встречает имя он сначала просматривает словарь пользователя, а затем системный. Если имя в словаре найдено, то выполняются соответствующие ему действия: либо объект помещается в стек, либо выполняются некоторый набор операторов. Если имя не найдено в словаре (например на стадии отладки программы), то выдается сообщение об ошибке. Обсуждаемые выше словари хранятся в стеке словарей: словарь пользователя в верху стека, системный -- внизу. Таким образом слово ищется начиная с вершины стека. Программа может создать новые словари, которые будут размещены в вершине стека словарей. Словарь, находящийся в вершине стека и, следовательно, просматриваемый первым, называется текущим словарем. Определение переменных и процедурПеременные. Чтобы определить переменную в PostScript, ее имя и значение нужно занести в текущий словарь. Это делается с помощью оператора def, как в следующем примере: /ed 47 def Косая черта перед именем переменной показывает, что интерпретатору следует поместить это имя в стек как литерал и не пытаться сразу же искать его в словаре. Вслед за именем в стек заносится число 47. И наконец def берет оба эти объекта из стека и помещает их в текущий словарь. Второй элемент стека (ed) становится ключом, с которым ассоциировано значение первого элемента (47). На первый взгляд это больше похоже на определение константы, чем переменной. Однако определенное с помощью def значение переменной может быть изменено либо новым оператором def, либо другими операторами, например: /ed 52 def Посмотрим на примере, как PostScript работает с новой переменной. Если дальше в программе появится строка: 13 ed add то интерпретатор сделает следующее:
Следующий оператор умножает значение переменной ed на 5: /ed ed 5 mul def Попробуйте, используя приведенные выше правила, определите содержимое стека в каждый момент выполнения этой строки. Процедуры. Процедура в PostScript - это набор операторов сгруппированных под общим именем. Имя процедуры является ключом в словаре, а набор операторов ассоциируется как его значение. Когда имя процедуры появляется в программе, то выполняется связанный с ним набор операторов. Процедуры в PostScript определяются точно так же, как и переменные, с тем только отличием, что набор операторов процедуры должен быть заключен в фигурные скобки. Следующая строка, например, описывает процедуру inch (дюйм), полезную для перевода дюймов в систему единиц, используемую в PostScript по умолчанию. /inch {72 mul} def Любое появление слова inch после этой строки заставит интерпретатор поместить в стек число 72, умножить его на число лежащее в стеке ниже его и поместить в стек вместо двух этих чисел результат их произведения, таким образом следующие две строки эквивалентны: 3 72 mul 3 inch Как известно, использование процедур сокращает длину программы, улучшает ее читабельность, а главное дает возможность расширить средства языка за счет введения в него новых слов. Таким образом обычный процедурный язык программирования (3GL) можно поднять до сверхвысокого уровня. При определении процедуры ничего не было сказано о передаче параметров. Для языка ориентированного на работу со стеком естественный способ передачи параметров -- размещение их в стеке. Перепишем программу рисования двух перекрывающихся областей: /inch {72 mul} def /box { % в стеке: x y newpath moveto 1 inch 0 rlineto 0 1 inch rlineto -1 inch 0 rlineto closepath } def /fillgray { % в стеке: уровень серого цвета setgray fill } def % Основная программа 2 inch 3 inch box .9 fillgray 2.5 inch 3.5 inch box .7 fillgray showpage В такой записи программу уже значительно проще изменять и она более самодокументирована, чем ее первый вариант. За это приходиться платить некоторым увеличением времени выполнения, так как интерпретатору приходится больше работать со словарями, однако быстродействие встроенных микропроцессоров, управляющих внешними устройствами (так в лазерных принтерах обычно используется RISC-процессор Intel 960) настолько велико, что эти микросекунды обычно никого не волнуют. Работа со шрифтамиЯзык PostScript не был бы так популярен, если бы он не предоставлял богатейшие возможности вывода на печать текстов. Текстовые данные представлены в PostScript объектами типа string (строка). Строка может содержать любую последовательность символов, заключенную в круглые скобки. Строка может быть помещена в стек, присвоена переменной или напечатана. Однако перед тем как строка будет помещена на текущей странице интерпретатору PostScript необходимо указать какую гарнитуру и размер шрифта использовать при печати. PostScript-шрифтыШрифт - это набор символов, имеющих единый дизайн. Дизайн конкретного шрифта называется гарнитурой. Набор гарнитур, разработанных для совместного использования, называется семейством гарнитур. Наиболее популярные гарнитуры: Таймс, Курьер, Журнальная и др. Конкретный PostScript-шрифт является реализацией начертания некоторого семейства гарнитур. PostScript-шрифты относятся к классу векторных и, следовательно, масштабируемых шрифтов. Существующие методы описания векторных шрифтов позволяют автоматически менять размер шрифта (кегель) с минимальными искажениями его начертания при преобразовании размера. Чтобы задать шрифт, нужно выполнить следующие действия:
Чтобы посмотреть, как это работает, давайте напечатаем слово PostScript шрифтом Helvetica размером 14 пунктов. /Helvetica findfont 14 scalefont setfont 100 150 moveto (PostScript) show showpage В этом фрагменте используется ряд новых операторов. В первой строке в стек сначала помещается литерал с именем шрифта, а затем вызывается оператор findfont. Этот оператор ищет это имя в словаре с названием FontDictionary и помещает соответствующий словарь шрифта в стек. Данный словарь содержит описания образов символов для шрифта размером в один пункт. Нужный размер устанавливается с помощью оператора scalefont, который берет из стека число и словарь шрифта и возвращает в стек словарь шрифта модифицированный под нужный размер. Так, в нашем примере строка 14 scalefont вернет в стек словарь для шрифта Helvetica с кеглем 14 пунктов. Наконец, оператор setfont переводит словарь шрифта из стека в текущий шрифт, который и будет использован для печати текста. Чтобы что-либо напечатать, нужно установить местоположение текущей точки, поместить в стек заключенную в круглые скобки строку и вызвать оператор show. Этот оператор "печатает" строку из стека на текущей странице, начиная с текущей точки. По мере печати текущая точка перемещается в конец строки. Изменение размера шрифта при печатиСледующий пример показывает, как во время печати текста можно менять шрифт. Определим процедуру устанавливающую шрифт нужного размера. /newsize { % в стеке размер scalefont setfont } def /getfont { /Helvetica findfont } def % Основная программа getfont 8 newsize 72 250 moveto (example) show getfont 10 newsize 72 275 moveto (example) show getfont 12 newsize 72 300 moveto (example) show showpage Эта программа напечатает три раза слово example шрифтом разного размера. Процедуры newsize и getfont можно объединить, если учесть порядок следования аргументов в стеке. (Понятно, что в нем хранится не сам словарь шрифта непосредственно, а ссылка на него). /scaleHelv { % в стеке размер /Helvetica findfont exch % кегель на вершине стека scalefont setfont } def Теперь запись строки программы станет еще компактней: 6 scaleHelv Графика и текстВ PostScript не существует различия между графикой и текстом. Символ текста рассматривается как один из графических объектов, размещаемых на текущей странице. Поэтому для совмещения на ней текста и графики не требуется никаких специальных действий. Графические операторы PostScript выполняют свои действия в пространстве пользователя, или, иначе говоря, в пользовательской системе координат. Как уже говорилось, это пространство независимо от какого-либо физического устройства и результат работы операторов PostScript при печати автоматически преобразуется в систему координат устройства. Однако иногда бывает удобно изменить действующую по умолчанию систему координат. Можно, например, перенести в другое место начало координат, изменить ориентацию осей и масштабы по ним. Перенесение начала координат. Для этой цели служит оператор translate. Он берет из стека два числа, и перемещает пространство пользователя в точку с этими новыми координатами. Например строка: 150 180 translate переместит их начало координат в точку (150, 180). После этого все позиции на текущей странице будут отмеряться от этой точки. Например, нужно треугольник переместить 3 раза: /treangl 0 0 moveto 90 0 lineto x y lineto closepath fill } def treangl 200 250 translate treangl 200 250 translate treangl showpage Обратите внимание, что второй перенос системы координат осуществляется уже относительно новой системы координат, а не относительно исходной. Поворот осейОператор rotate позволяет повернуть систему координат на заданный угол. Он берет из стека число, показывающее угол поворота осей в градусах (угол отсчитывается от вертикальной оси против часовой стрелки) и выполняет разворот. Снова нарисует наши три треугольника, но с поворотом осей. /neworigin { 250 150 translate 60 rotate } def /treangl { 0 0 moveto 90 0 lineto x y lineto closepath fill } def treangl neworigin treangl neworigin treangl showpage Изменение шкал по осям координатОператор scale позволяет изменить размер единицы измерения по каждой из осей координат. Он берет из стека два аргумента - коэффициенты изменения масштаба по осям x и y, соответственно, и с их учетом изменяет размер системных единиц. Например строка: 2 2 scale увеличит размер системной единицы вдвое, т.е. объект будет рисоваться вдвое большего размера, чем до выполнения этой команды. Попробуйте напечатать треугольники из предыдущего примера используя следующие размеры системной единицы:
Сохранение состояния графики PostScriptДо сих пор мы работали с графикой явным образом, меняя с помощью операторов ее состояние. Состояние графики - это набор данных, которые описывают, как операторы будут влиять на текущую страницу. Пара взаимодополняющих операторов, gsave и grestore, позволяют сохранить текущее состояние графики, а затем в нужный момент восстановить его. Это может потребоваться до и после использования оператора fill, который, как вы помните, очищает текущую траекторию. Если нужно продолжить рисование из какой-либо точки заполняемой оттенком серого фигуры, то удобно просто восстановить состояние графики, сохраненное до выполнения fill. Пример вы легко придумаете сами. Оператор gsave сохраняет копию текущего состояния графики в стеке состояния графики. Этот стек может хранить до 32-х состояний графики, включая текущее состояние. Оператор grestore восстанавливает состояние графики, сохраненное в стеке самым последним. Все характеристики текущего графического состояния, включая текущий путь уровень серого цвета, ширину линии и систему пользовательских координат возвращаются в состояние, в котором они были перед выполнением оператора gsave. 16.11.2001 |