содержание   назад   вперед


Виктор Кон,   Постскрипт: инструкция к применению

2. Первый уровень программирования

2.1 Предисловие

В отношении к постскрипту, пожалуй, опыт умения программировать на стандартных языках типа Фортрана, C, Паскаля или более современных Java, javascript может оказать отрицательное воздействие, потому что тут все по другому. Чтобы было легче, начнем все же с того, что объединяет. Как и в других языках, постскрипт имеет большое количество команд (или операторов), которые применяются к объектам языка, изменяя его свойства. Объектами языка являются числа, символьные строки, а также массивы указанных элементов. Для тех, кто не имеет опыта программирования я все же поясню, что строка символов - это как текст в книге, а массив, это как отряд солдат на марше. Все они примерно одинаковые, но есть первый, второй и так далее до последнего. У строки символов и у массива есть размер -- это число их элементов. Далее, как и в других языках, совокупность команд можно объединить в процедуру, присвоить ей имя и далее писать в программе имя процедуры вместо всех ее команд. Интерпретатор запоминает все пары имя-процедура и всегда заменяет имя самой процедурой. Это позволяет очень резко сократить текст программы на постскрипте и сделать его более понятным и читабельным.

Соответсвенно разумно предположить, что есть команды преобразования объектов из одного типа в другой тип. Отличия начинаются в том, что различны цели. Если в вычислительных языках необходимо где-то прочитать числа, провести над ними вычисления и новые числа снова куда-то записать, то здесь нужно описать картинку, используя самые разные источники информации. Собственно все современные языки тоже имеют графический пакет подпрограмм, который рисует. Здесь рисуют почти все команды. И нет разницы между ними, так как именно рисующие команды -- главные, а остальные играют вспомогательную роль. Тем не менее, при рисовании часто приходится делать вычисления и "логически" мыслить. В этом смысле язык самодостаточен. Хотя можно было бы и просто рисовать по точкам, а всю остальную работу делать на других языках программирования. Такой подход тоже имеет право на реализацию.

Самое большое различие между стандартными языками и постскриптом состоит в способе запоминания объектов и хранения информации. В обычных языках существуют переменные, строки и массивы, каждый из которых имеет имя и адресуется именно по имени. В постскрипте все объекты безымянные. Они все запоминаются методом вкладывания монет в трубочку. Все монеты в трубочке хранятся, но вытащить можно только ту, которую положили последней. Такая трубочка называется стеком. Соотвественно в языке отсутствует оператор присваивания, который является главным во всех расчетных языках. Там надо писать <имя_объекта> = <значение_объекта>. Здесь просто пишется значение объекта. Если интерпретатор увидит в программе значение объекта, он его сразу засунет в стек последним. И значит потом он его использует первым. Если много значений записано подряд, то надо помнить, что они будут использоваться в обратном порядке -- из конца в начало. Значения в тексте разделяются пробелами. Числа записываются просто числами, например, 1 234 4.56 36.8е-5. Символьные строки записываются в круглых скобках, например, (system) (postscript). Массивы записываются в квадратных скобках, например, [(a) 7.56 (abc)]. Из примера видно, что элементами массива могуть быть объекты разных типов. В простой программе без массивов можно обойтись, а вот числа и строки символов необходимы. В принципе что-то похожее на оператор присваивания все же есть, но это имеет вид определения процедуры. В общем случае определение процедуры записывается следующим образом

/name {процедура как кусок программы} def

Фигурные скобки являются указанием на процедуру. Перед ними ставится имя процедуры и знак деления перед ним указывает, что это имя не используется, а определяется. После них ставится команда def, которая как раз и определяет процедуру. Пробелы до и после фигурных скобок не обязательны. После того, как процедура определена, в тексте программы можно писать вместо нее только ее имя (уже без знака деления). Так как процедура является просто куском программы, то минимальным куском программы можно считать занесение числа в стек. Поэтому конструкция типа /X 45 def тоже является определением процедуры и одновременно она определяет переменную X, которой присвоено значение 45. Если в процедуре только простые операторы, то фигурные скобки можно не ставить. Сразу же полезно сказать про команду bind. Она полезна в том случае, если в теле процедуры есть имена других процедур. Тогда они сразу заменяются на свои процедуры с тем, чтобы ускорить работу программы в последующем. Таким образом для сложных процедур разумнее писать

/name {процедура как кусок программы} bind def

Если же в процедуре все просто, то указанное выше определение все равно ничего не испортит и мы будем часто использовать эту пару операторов.


2.2 Математические вычисления и стек

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

add -- вынимает два числа из стека, складывает их и возвращает их сумму

sub -- вынимает два числа из стека, вычитает их и возвращает их разность

mul -- вынимает два числа из стека, умножает их и возвращает их произведение

div -- вынимает два числа из стека, делит их и возвращает их частное

idiv -- вынимает два целых числа из стека, делит их и возвращает целое частное

mod -- вынимает два числа из стека, делит их и возвращает остаток от деления

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

abs -- абсолютное значение

neg -- изменение знака числа

ceiling -- ближайшее целое сверху

floor -- ближайшее целое снизу

round -- ближайшее целое с меньшим расстоянием

truncate -- дробная часть числа

sqrt -- квадратный корень

atan -- берет два числа из стека и возврашает арктангенс их отношения в градусах

cos -- косинус числа в градусах

sin -- синус числа в градусах

exp -- берет два числа и возвышает предпоследнее в степень последнего

log -- десятичный логарифм

ln -- натуральный логарифм

rand -- ничего не берет но возвращает в стек случайное целое число

srand -- берет из стека целое число и устанавливает как базовое в последовательности случайных чисел

rrand -- ничего не берет и устанавливает базовое число в последовательности случайных чисел

Итак попробуем выполнить вычисления. Пусть нам надо провести вычисления, которые в вычислительном языке записываются так
X=25; Y=45; X=(X-Y)/(X+Y)+35*cos(45); Это уже кусок программы, но на постскрипте такая программа записывается не так наглядно
/X 25 def /Y 45 def /X X Y sub X Y add div 45 cos 35 mul add def
Вопрос не в том, что это менее наглядно и привычно, нужно быть очень аккуратным и постоянно следить за состоянием стека. При определенном навыке все очень просто, размер записи значения не имеет, так как на постскрипте вычисления имеют вспомогательный характер, да и не это самое страшное в программировании. Главное все понимать правильно и не делать ошибок. Итак мы сначала задали переменным X и Y нужные значения, а затем стали переопределять переменную X с помощью цепочки команд. Сначала мы вставили в стек два числа (точнее их обозначения) и провели вычитание. Порядок имеет значение, первым надо ставить вычитаемое и оно будет предпоследним с стеке, затем мы вставили в стек два числа и провели сложение. На этот момент в стеке предпоследним стал числитель, а последним знаменатель, поэтому мы спокойно проводим деление. Затем вставляем в стек аргумент косинуса и вычисляем косинус, добавляем в стек множитель и умножаем и потом сразу складываем. Вычисления закончены, осталось завершить цепочку оператором def. Я напомню, что Постскрипт делает все легким для интерпретатора, но не для программиста. Запись на постскрипте очень легко выполнить, намного проще, чем математическую нотацию.

Легко убедиться, что на постскрипте можно проводить вычисления любой степени сложности. Но есть еще одна проблема - передача данных (чисел, например) в процедуру. Процедура без аргументов не так эффективна и, учитывая стековую природу памяти необходимо что-то еще, чтобы задать много чисел сразу, а затем определить с их помощью переменные. Для этого служит оператор exch. Он просто меняет местами последние два объекта в стеке. С его помощью можно менять порядок задания объектов. Лучше всего это понять на примере. Так 25 /X exch def означает то же самое, что /X 25 def. Более того, 25 35 /Y exch def /X exch def означает то же самое, что /X 25 def /Y 35 def. Вся разница в том, что с помошью оператора exch мы можем все числа определить заранее, а уже потом определять переменные в процедуре. В этом случае числа играют роль аргументов. Используя такую технику можно всю программу записать таким образом, что сначала все числа а затем все команды. Это очень трудно читать, но удобно для программ, которые генерируют постскрипт код автоматически. Так как некоторые комбинации операторов очень часто встречаются в паре или в группе, то удобно сразу ввести для них более сокращенное обозначение (процедуру). Мы постепенно это будем делать, так как именно в создании нового уровня языка постскрипт заключается вторая цель этой инструкции. Итак введем первые процедуры Нового Языка, назовем его vkPS.

/bd{bind def}bind def

/ed{exch def}bd

У нас появились две новые команды с очень короткими именами bd и ed. Первая команда определяла фактически саму себя. А в определении второй команды уже присутствует первая команда.


2.3 Задание траектории (контура) по точкам

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

newpath -- открывает новую траекторию как объект. Курсор пока остается на старом месте.

closepath -- закрывает траекторию как объект, при этом дорисовывается линия, соединяющая последнюю точку с первой точкой траектории и траектория становится замкнутой.

moveto -- берет из стека два числа (сначала Y потом X) и переносит курсор в точку с данными координатами. При этом траектория не меняется (перо над бумагой). Эта команда фактически задает первую точку новой траектории.

rmoveto -- то же, что и предыдущая команда, только в стеке не абсолютные координаты X,Y, а смещения DX,DY курсора из текущей точки в новую точки. Если исходно курсор находился в точке с координатами 0,0, то эта команда эквивалентна предыдущей. Однако иногда надо начать новую траекторию на определенном расстоянии от предыдущей траектории. Тогда эта команда упрощает расчеты.

lineto -- берет из стека два числа (сначала Y потом X) и добавляет к траектории отрезок прямой линии из предыдущей точки в точку с данными координатами.

rlineto -- то же, что и предыдущая команда, только в стеке не абсолютные координаты X,Y, а смещения DX,DY курсора из текущей точки в новую точки.

curveto -- берет из стека шесть чисел (Y3,X3,Y2,X2,Y1,X1) и добавляет к траектории отрезок кривой линии из текущей точки в точку X3,Y3 как кубический сплайн. Точки X1,Y1,X2,Y2 на траектории не находятся, они используются для интерполяции. Так прямая из текущей точки в точку X1,Y1 задает начальную ориентацию кривой, а прямая из точки X2,Y2 в точку X3,Y3 - конечную ориентацию кривой. Это достаточно сложная команда, она может быть использована в чертежах для сглаживания острых углов на траектории.

rcurveto -- то же, что и предыдущая команда, только в стеке не абсолютные координаты X,Y, а смещения DX,DY курсора из текущей точки в новые точки.

arc -- берет из стека пять чисел (A2,A1,R,Y,X) и добавляет к траектории отрезок кривой линии как часть окружности (дуги) с центром в точке X,Y и с радиусом R. Углы A1 и A2 в градусах задают начало и конец дуги. Движение против часовой стрелки. Для рисования полной окружности достаточно задать A1=0, A2=360.

acrn -- то же, что и предыдущая команда, только движение по часовой стрелке.

arct -- берет из стека пять чисел (R,Y2,X2,Y1,X1) и добавляет к траектории отрезок кривой линии как часть окружности (дуги) с радиусом R, которая имеет своими касательными отрезки прямых линий из текущей точки в X1,Y1 и из X1,Y1 в X2,Y2. Траектория заканчивается в точке X2,Y2.

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


2.4 Команды создания простых графических объектов

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

setrgbcolor -- берет из стека три числа (B,G,R) и задает текущий цвет для рисования всего как комбинацию трех цветов - красного (R), зеленого (G) и синего (B). Это стандартный способ задания цвета. Важно только помнить, что числа задаются в диапазоне от 0 (отсутствие) до 1 (полная яркость). Так что черный нужно задавать как 0 0 0 а белый как 1 1 1.

setgray -- берет из стека одно число G и задает яркость серого. Эта команда первоначально была создана для черно-белых принтеров, но она остается и сейчас и часто используется. Дело в том, что черно-белые графики еще часто создаются, например в публикациях научных статей. Фактически команда G setgray эквивалентна команде G G G setrgbcolor.

setlinewidth -- берет из стека одно число W и задает толщину рисования линий по контуру.

setlinejoin -- берет из стека одно число N и задает модификацию обработки углов ломаной толстой линии. Есть три способа: N=0 реальный угол как есть, N=1 сглаженный угол по дуге, N=2 обрезаный угол.

setlinecap -- берет из стека одно число N и задает модификацию обработки концов толстой линии. Есть три способа: N=0 укороченный конец (точно по контуру), N=1 сглаженный конец по дуге, N=2 удлиненный конец с учетом толщины.

setdash -- берет из стека одно число D и массив [...] и задает способ рисования линии пунктиром. В массиве может быть разное число элементов в зависимости от сложности пунктира. Проще всего это показать на примерах. Так [] 0 setdash -- это сплошная линия, [3] 0 setdash -- это простая пунктирная линия 3 pt чертим, 3pt не чертим и так далее. Для простоты я буду использовать английский язык, он короче - итак 3 on, 3 off, 3 on, 3 off, etc., [4 2] 0 setdash -- это более фасонная пунктирная линия: 4 on, 2 off, 4 on, 2 off, etc., [3 5] 6 setdash -- это пунктирная линия с начальной фазой. Тут алгоритм следующий. Линия сначала прокручивается вхолостую, пока не достигнет начальной фазы. И с нее начинается рисование. В данном примере начальная фаза 6, проходим 3 on вхолостую так как меньше начальной фазы, проходим 5 off, при этом движении начальная фаза пересекается после 3 off, значит реально будет начерчено 2 off, 3 on, 5 off, etc.. [2 3] 11 setdash -- попробуйте догадаться, что ответ будет таким 1 on, 3 off, 2 on, 3 off, etc..

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

rectstroke -- и он имеет четыре аргумента x, y, w, h, то есть координаты левого нижнего угла прямоугольника, ширина и высота. Естественно из стека он берет эти числа в обратном порядке. Вместо чисел можно задавать массив или строку, которые содержат эти числа. Причем в массиве и строке могут быть описаны не один, а несколько прямоугольников сразу. Есть даже и более сложная форма, в которой после четырех чисел задается матрица CTM (об этом можно будет прочитать позднее). Эта матрица умножается на текущую матрицу перед рисованием.

fill -- эта команда заливает внутреннюю область контура цветом или уровнем серого. Если контур не замкнут, она его замыкает, соединяя последнюю точку с первой. Очень часто приходится заливать цветом прямоугольные области. Для удобства записи введет оператор

rectfill -- для которого прямоугольный контур можно задавать непосредственно перед его использование в виде четырех чисел x, y, w, h, то есть координаты левого нижнего угла прямоугольника, ширину и высоту. Естественно из стека он берет эти числа в обратном порядке. Вместо чисел можно задавать массив или строку, которые содержат эти числа.

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

/np{newpath}bd

/m{np moveto}bd -- два аргумента

/l{lineto}bd -- два аргумента

/rl{rlineto}bd -- два аргумента

/c{curveto}bd -- шесть аргументов

/slw{setlinewidth}bd -- один аргумент

/srgb{setrgbcolor}bd -- три аргумента

/sg{setgray}bd -- один аргумент

/sl{stroke}bd

/ls{m l sl}bd -- четыре аргумента

Использование новых определений команд позволяет намного компактнее записывать программы. Платой за это является просто предварительная запись всех определений в Прологе файла. Правда для человека, не знающего этих определений программа может показаться написанной вовсе не на постскрипте, а на каком-то другом языке. Это свойство всех интерпретируемых языков. Более того, практически каждая программа постскрипт-генератор начинает с того, что вводит свои определения. Поэтому прежде чем разбирать постскрипт файл, созданный какой-то программой надо сначала выучить ее язык. Моя программа PS-Figure начинает с того, что вводит все определения, после чего ими можно смело пользоваться. Вот несколько примеров рисования простых объектов.
Контур красного квадрата: 1 0 0 srgb 2 slw 0 0 m 50 0 rl 0 50 rl -50 0 rl 0 -50 rl sl
Синяя окружность на зеленом треугольнике:
0 1 0 srgb 2 slw 10 10 m 50 0 rl -25 30 rl -25 -30 rl sl 0 0 1 srgb 55 60 m 35 60 20 0 360 arc sl
Указанных команд вполне достаточно, чтобы нарисовать чертеж сколь угодно сложной формы и даже изображение поверхности в трехмерном пространстве с помощью сетки линий на осях координат. Все что требуется - это вычисление координат точек прямолинейных отрезков и дуг. Вот эту работу как раз можно выполнить на любом другом вычислительном языке и просто записать тысячи повторений простых команд типа m l rl sl. Тем не менее для полноценного графика еще нехватает аппарата рисования текстов и преобразования координат. Перейдем к рисованию текстов.


  2.5 Команды изображения текстов

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

show -- эта команда берет из стека строку символов, объект в круглых скобках, и рисует ее левым нижним краем на позиции курсора.

Естественно позицию курсора можно изменить командой X Y m. Но нужна еще дополнительная информация о размере символов, форме их написания и т. д. Постскрипт уже относительно старый язык, и когда он создавался, то существовала только система ASCII, то есть таблицы символов размером 256. Уникоды еще не использовались. Так все и осталось. Постскрипт шрифты (font) содержат не более 256 знаков. Более того, есть стандартный набор шрифтов, который обязаны поддерживать все принтеры и все интерпретаторы типа Gstools. И в этом стандартном наборе отсутсвует русский язык. Это большой минус, хотя конечно проблему можно решить, но приходится использовать нестандартные фонты, что увеличивает размер программы и ухудшает переносимость. Так как Постскрипт - язык векторный, то все шрифты тоже векторные, их можно масштабировать и вообще видоизменять произвольным образом. Шрифты описываются специальным сложным образом и находятся в файлах с соответсвующими именами. В программе Gstools, которую я предлагаю на своем сайте, эти файлы находятся в папке (gs5.50/fonts). Соответствие имен файлов тем именам фонтов, которые используются в языке Постскрипт обычно записывается в файл с именем Fontmap в папке (gs5.50). Там однако могут оказаться имена фонтов, которых реально нет в поставке, но которые можно скачать с сайта фирмы Adobe. Я просто перечислю здесь имена стандартных фонтов в порядке их привлекательности

/Palatino-Roman
/Palatino-Italic
/Palatino-Bold
/Palatino-BoldItalic

/Times-Roman
/Times-Italic
/Times-Bold
/Times-BoldItalic

/Helvetica
/Helvetica-Oblique
/Helvetica-Bold
/Helvetica-BoldOblique

/Helvetica-Narrow
/Helvetica-Narrow-Oblique
/Helvetica-Narrow-Bold
/Helvetica-Narrow-BoldOblique

/Symbol

/Courier
/Courier-Oblique
/Courier-Bold
/Courier-BoldOblique

/AvantGarde-Book
/AvantGarde-BookOblique
/AvantGarde-Demi
/AvantGarde-DemiOblique

/Bookman-Demi
/Bookman-DemiItalic
/Bookman-Light
/Bookman-LightItalic

/NewCenturySchlbk-Roman
/NewCenturySchlbk-Italic
/NewCenturySchlbk-Bold
/NewCenturySchlbk-BoldItalic

/ZapfDingbats
/ZapfChancery-MediumItalic

Эти имена можно смело использовать в программе, так как интерпретатор их найдет в своих арсеналах и обработает. Помимо этого существует множество шрифтов самых разных типов, которые можно купить и установить в интерпретатор. Более того, существуют конверторы true type фонтов ОС Виндовс в постскрипт фонты. Наконец каждый может сам разработать себе фонт по определенным правилам и либо установить файл в интерпретатор, либо просто записать определение фонта в Пролог файла. Именно так я сделал с русским фонтом, о чем речь пойдет в последующих главах.

Итак для рисования текста необходимо указать какое-либо имя фонта из указанных выше и затем выполнить команду

findfont -- которая заставляет интерпретатор искать нужный файл, все фонты исходно имеют размер 1 pt. Соотвественно фонт нужно промасштабировать. Это делает команда

scalefont -- она берет из стека число N и масштабирует фонт на этот размер, после этого команда

setfont -- устанавливает данный фонт как текущий для рисования командой show.

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

/s{show}bd

/f{findfont exch scalefont setfont}bd

/PR{/Palatino-Roman f}bd

/PB{/Palatino-Bold f}bd

/PI{/Palatino-Italic f}bd

/Sy{/Symbol f}bd

/He{/Helvetica f}bd

/FS 14 def

/L{FS PR s}bd

/B{FS PB s}bd

/I{FS PI s}bd

/G{FS Sy s}bd

/H{FS He s}bd

Все эти процедуры представляют собой последовательную цепочку сокращений. В конце концов выбрано 5 фонтов стандартного размера 14 pt и эти фонты обозначаются всего одной буквой. Теперь, если вдруг нам захотелось написать букву m пятью разными шрифтами подряд, то это совсем просто 10 10 m (m)L(m)B(m)I(m)G(m)H. Эту программу можно выполнить, для этого надо использовать шаблон структуры EPS файла, описанный в предыдущем разделе и поставить все новые команды в Преамбулу файла, а указанную программу в основную часть. Вот я только что это проделал. Вместо 4-й буквы m стоит греческая буква мю. Здесь используется один нехитрый прием. Размер текста 14 определен первоначально в переменной FS. Если нам вдруг понадобится поменять размер текстов, то достаточно переопределить эту переменную, и все названия стандартных шрифтов будут рисовать тексты другого размера.

Теперь нам в самый раз поговорить о том как интерпретатор рисует тексты. Мы можем указывать в строке любые символы, которые можно набить на клавиатуре. Реально интерпретатор берет ASCII номер этого символа и рисует как раз тот символ, который у него зарезервирован под этим номером. В шрифте Symbol рисуются символы греческого языка. В шрифте могут быть также символы, номера которых вообще невозможно ввести с клавиатуры. Есть и другая проблема: как ввести символы, являющиеся служебными в языке, например, круглые скобки. Для этого предусмотрена конструкция, которая используется во многих языках -- символ можно задать по восьмеричному номеру, перед которым ставится обратная наклонная черта, например \101 = A и так далее. Использование восьмеричных номеров вместо шестнадцатиричных -- это своеобразный запах старины. Когда то программы писались в двоичном коде компьютера, но вместо двоичных чисел очень популярна была именно восьмеричная система. Понятия байта тогда не существовало. Задание символов по номеру решает все проблемы. Но кроме этого есть еще и дополнительные возможности указания непечатных символов \b=\10, \t=\11, \n=\12, \r=\15, \\=\134, \(=\50, \)=\51. Еще одно полезное правило состоит в том, что если строка длинная и не умещается на одной линии, то ее приходится переносить. При этом символ конца линии тоже запишется как символ для печати. Если нужно этого избежать, то в конце линии можно поставить знак "\" и переноса не будет.

Часто бывает необходимо поставить текст на позицию курсора не началом, а концом или серединой, причем заранее длина текста неизвестна. Для этого в языке vkPS введены следующие процедуры:

/sp{stringwidth pop}bd -- определяет длину строки

/sc{dup sp -0.5 mul 0 rm s}bd -- ставит текст серединой

/se{dup sp -1 mul 0 rm s}bd -- ставит текст концом

/Lc{FS PR sc}bd -- ставит текст стандартным фонтом на середину

/Le{FS PR se}bd -- ставит текст стандартным фонтом на конец

Здесь использованы еще не известные нам команды

dup -- помещает в стек копию последнего элемента

pop -- вынимает из стека последний элемент, но ничего с ним не делает

stringwidth -- берет из стека строку символов и вычисляет ее ширину, но возвращает в стек два числа как X,Y смещение точки из начало в конец текста, второе число ноль.

Теперь ясно как это работает. dup - копирует строку в стеке, stringwidth - съедает копию, но возвращает два числа, pop - уничтожает последнее ненужное число. После этого происходит умножение ширины на -0.5 или на -1 и смещение текущей позиции курсора на указанное расстояние. Теперь мы имеем набор команд s,sc,se позволяющих рисовать текст строго справа от линии, в центре линии и слева от линии. Для полноты картины следует указать, что есть еще более сложный оператор рисования текста

ashow -- он берет из стека три объекта (text) Y X и рисует текст таким образом, что каждый следующий символ сдвигается по отношению к предыдущему на заданное смещение X,Y. Иногда это бывает полезно. Для особых случаев есть еще оператор

widthshow -- аргументами которого являются четыре объекта cx cy char (text). Так они должны быть записаны перед его использованием, из стека он их берет в обратном порядке. Он рисует (text), но специальным образом - каждый символ с размерами, которые получаются добавлением к размерам символа char приращений cx и cy по горизонтали и по вертикали. Символ char задается своим ASCII кодом.

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

 


содержание   назад   вперед