Книга
Книга
Компьютерная типография
CSLI Lecture Notes Number 78
Computer Typesetting
D. E. Knuth
CSLI Publications Stanford California
1999
Б
в”
Д
ТЕХНОЛОГИЙ к?
Ас к и X
Д. Э. Кнут
Компьютернаятипография
Перевод с английского под редакциейИ. А. Маховой
ИЗДАТЕЛЬСТВО
«Мир»
«АСТ»
Москва 2003
УДК 681.322
ББК 32.97
К53
Переводчики: Кузнец Р. М. — гл. 4, 13-15, 17, 33, 34;
Маховая О. А. — предисловие, гл. 1-3 (с. 116-166), 18, 21-23, 31, 32;| Третьяков Н. В.~|— гл. 3 (с. 79-116), 5-9;
Тюменцев Ю. В. — гл. 10-12, 16, 19, 20, 24-30.
Кнут Д. Э.
К53 Компьютерная типография: Пер. с англ.— М.: Мир, ООО «Издательство АСТ», 2003.— 668 с., ил.
ISBN 5-03-003361-0 («Мир»)
ISBN 5-17-019775-6 («АСТ»)
Сборник работ Д. Кнута, написанных им за время работы над созданием всемирно известных систем Т^Х и metafont, в который вошли также статьи последних лет по этой тематике. Книга состоит из 34 глав, разделенных условно на три части: Т^Х и относящиеся к нему темы; metafont и родственные вопросы; TpX и metafont в историческом аспекте. Представлен богатый иллюстративный материал и приводятся листинги программ.
Как и во всех книгах Д. Кнута, весьма серьезные вопросы излагаются просто и увлекательно, что, учитывая междисциплинарную направленность книги, делает ее доступной для специалистов в разных областях науки.
Книга представляет интерес для научных работников всех специальностей, самостоятельно готовящих свои работы к публикации, для специалистов в области информатики и издательских систем, а также для математиков, интересующихся нестандартными приложениями.
УДК 681.322
ББК 32.97
Издание осуществлено при поддержке
Российского фонда фундаментальных исследований по проекту
№ 00-07-95002
Редакция автоматизации издательских процессов
ISBN 5-03-003361-0 («Мир»)ISBN 1-57586-010-4 (англ.)ISBN 5-17-019775-6 («АСТ»)ISBN 5-03-003361-0 («Мир»)ISBN 1-57586-010-4 (англ.)ISBN 5-17-019775-6 («АСТ»)© by CSLI Publications, Leland Stanford Junior University. Translated and published by arrangement with CSLI Publications. All rights reserved. Перевод и издание согласованы с CSLI Publications. Все права защищены, 1999.
ISBN 5-03-003361-0 («Мир»)
ISBN 1-57586-010-4 (англ.)
ISBN 5-17-019775-6 («АСТ»)
ISBN 5-03-003361-0 («Мир»)
ISBN 1-57586-010-4 (англ.)
ISBN 5-17-019775-6 («АСТ»)
© перевод на русский язык, оформление, «Мир», 2003
Предисловие редактора перевода
Дональд Эрвин Кнут —имя, окруженное ореолом славы. Раскрыв эту книгу читатель из первых рук узнает о множестве интересных фактов, сопровождавших период создания всемирно известных систем Т^Х и METAFONT. В этом предисловии также представлено несколько «русских страниц» из жизни великого ученого.
Свой монументальный труд The Art of Computer Science Д. Кнут задумывает в двадцатичетырехлетнем возрасте и пишет по сей день, ни на миллиметр не снижая планку, установленную в 1962 г. За эти годы вышло в свет 3 тома (многократно переизданных с изменениями и дополнениями автора) из запланированных семи, идет интенсивная работа над 4-м томом (который распался на 3 книги), и еще 3 тома впереди. При этом автор постоянно отвлекается на вещи, как бы непосредственно не связанные с его грандиозным проектом. Появляется не одна серия самых разных книг: о том, как писать математические работы, о документированном программировании, об алгоритмах, и проч., и проч., всего более 20-ти. Но не только это: издав три первых тома своей Библии для программистов, он с головой уходит в изучение полиграфической премудрости и перевод ее на язык программирования, тратит на это почти 10 лет и создает издательскую систему. Вот так, неожиданно для себя, Д. Кнут, по сути, совершает цифровую революцию в печатном деле.
Казалось бы, зачем внедряться в неведомые сферы: ведь можно было вместо этого написать еще один том своего многотомника? Но он считает, что негоже издавать книги о высоких достижениях компьютерной науки и пользоваться при этом технологией, насчитывающей пять столетий. Творец ощутил несоответствие содержания и формы и не мог работать дальше, пока не будет инструмента, достойного его произведений. Он не стал сокрушаться по поводу этой дисгармонии и проклинать отсталую полиграфию, а просто взялся за работу и выполнил ее блестяще: Дональд Кнут одержим стремлением к Идеалу.
Надеясь потратить на издательскую систему год и вновь вернуться к делу своей жизни, он работает над проектом почти целое десятилетие. Автор говорит, что всегда недооценивал сложность стоящей перед ним задачи, однако в
Рис. 1. Идеальное в природе.
данном случае установил личный рекорд! Сначала он просто хотел облегчить жизнь себе и своему секретарю, но, отдав свое детище в общее пользование (статус программ — public domain), получил поток писем с вопросами и сообщениями о найденных ошибках. Такова плата за свободу распространения и — путь к достижению идеала. Более того, Кнут объявил, что за каждую найденную ошибку в его программах он заплатит 2.56 доллара, после чего поток превратился в шквал. Так что теперь вряд ли можно найти другой столь же тщательно отлаженный программный продукт.
Вот уже 25 лет эти две программы продолжают держать первенство среди программных средств, предназначенных для подготовки к изданию научнотехнической литературы. Их распространение и поддержку взяли на себя общественные организации — группы пользователей ТцХ’а, которые имеются практически во всех странах, во главе с международной группой TUG. Подобная организация CyrTUG была в России (до 1999 г.), и Дональд Кнут был ее почетным членом. На рис. 2 Дональд Кнут запечатлен во время заполнения анкеты, а на рис. 3 представлена анкета под номером 314, собственноручно заполненная по-русски автором TgX’a. Д. Кнут выучил русский язык в объеме, достаточном для того, чтобы читать работы русских математиков в подлиннике. Еще один штрих к портрету. Кстати, первым не-латинским шрифтом, которым был дополнен Computer Modern, была кириллица.
В «почетные пользователи» собственной издательской системы и ее кириллической версии Дональд Кнут был принят в мае 1994 г., когда по приглашению Санкт-Петербургского университета посетил Санкт-Петербург для получения звания honoris causa СПбУ. На сс. 8-9 приводится текст его выступления.
Рис. 2. Джил и Дональд Кнут во время посещения Санкт-Петербурга.Рис. 2. Джил и Дональд Кнут во время посещения Санкт-Петербурга.АНКЕТА пользователя ТУС (Просьба заполнить на русском и английском языках) Кпут—ф.и.о.Т) о н А /? ь п ^ РКи н о"6 И ЧАНКЕТА пользователя ТУС (Просьба заполнить на русском и английском языках) Кпут—ф.и.о.Т) о н А /? ь п ^ РКи н о"6 И ЧОЗУ4ОЗУ4—Место работы (название, адрес, тел., телекс, факс, e-mail’« Пецсцон р Р—Место работы (название, адрес, тел., телекс, факс, e-mail’« Пецсцон р Р—должность!—ZZZZZZZZjij^ZZ^^^—Образование, степеньIT LI С? UX ^-^ ^г 1л Г^ Г МатХ^—должность!—ZZZZZZZZjij^ZZ^^^—Образование, степеньIT LI С? UX ^-^ ^г 1л Г^ Г МатХ^—Как давно знакомы с ТуСом—Как давно знакомы с ТуСом—Оборудование, на котором работаете входное устройство выходное устройство .—Операционная система—Версия ТуСа—Оборудование, на котором работаете входное устройство выходное устройство .—Операционная система—Версия ТуСа1-7771-777Solans MiaSolans Mia—Сколько сотрудников Вашего учреждения работают в ТуСе•* ее ' >г <2. п 2- 7—Для какой пели намерены использовать ТуС и METAFONT .■ Р^^ •ОУИГ^ TMCbM\, ь^уРР^ ита—Какое направление разработок в ТУСе или METAFONTe представляют для Вас особыйинтересЧ^ I О^ Ч Н^ РСТА _—Сколько сотрудников Вашего учреждения работают в ТуСе•* ее ' >г <2. п 2- 7—Для какой пели намерены использовать ТуС и METAFONT .■ Р^^ •ОУИГ^ TMCbM\, ь^уРР^ ита—Какое направление разработок в ТУСе или METAFONTe представляют для Вас особыйинтересЧ^ I О^ Ч Н^ РСТА _—Какую работу хотели бы вести в CYRTUG—Какую работу хотели бы вести в CYRTUGсо ^у^е-с-т^.со ^у^е-с-т^.—Ваши предложения по структуре CYRTUG и др.—Ваши предложения по структуре CYRTUG и др.Дата заполнения: Подпись:Дата заполнения: Подпись:
Рис. 2. Джил и Дональд Кнут во время посещения Санкт-Петербурга.
Рис. 2. Джил и Дональд Кнут во время посещения Санкт-Петербурга.
АНКЕТА пользователя ТУС (Просьба заполнить на русском и английском языках) Кпут
—ф.и.о.Т) о н А /? ь п ^ РКи н о"6 И Ч
АНКЕТА пользователя ТУС (Просьба заполнить на русском и английском языках) Кпут
—ф.и.о.Т) о н А /? ь п ^ РКи н о"6 И Ч
ОЗУ4
ОЗУ4
—Место работы (название, адрес, тел., телекс, факс, e-mail’
« Пецсцон р Р
—Место работы (название, адрес, тел., телекс, факс, e-mail’
« Пецсцон р Р
—должность!—ZZZZZZZZjij^ZZ^^^
—Образование, степеньIT LI С? UX ^-^ ^г 1л Г^ Г МатХ^
—должность!—ZZZZZZZZjij^ZZ^^^
—Образование, степеньIT LI С? UX ^-^ ^г 1л Г^ Г МатХ^
—Как давно знакомы с ТуСом
—Как давно знакомы с ТуСом
—Оборудование, на котором работаете входное устройство выходное устройство .
—Операционная система
—Версия ТуСа
—Оборудование, на котором работаете входное устройство выходное устройство .
—Операционная система
—Версия ТуСа
1-777
1-777
Solans Mia
Solans Mia
—Сколько сотрудников Вашего учреждения работают в ТуСе•* ее ' >г <2. п 2- 7
—Для какой пели намерены использовать ТуС и METAFONT .■ Р^^ •
ОУИГ^ TMCbM\, ь^уРР^ ита
—Какое направление разработок в ТУСе или METAFONTe представляют для Вас особый
интересЧ^ I О^ Ч Н^ РСТА _
—Сколько сотрудников Вашего учреждения работают в ТуСе•* ее ' >г <2. п 2- 7
—Для какой пели намерены использовать ТуС и METAFONT .■ Р^^ •
ОУИГ^ TMCbM\, ь^уРР^ ита
—Какое направление разработок в ТУСе или METAFONTe представляют для Вас особый
интересЧ^ I О^ Ч Н^ РСТА _
—Какую работу хотели бы вести в CYRTUG
—Какую работу хотели бы вести в CYRTUG
со ^у^е-с-т^.
со ^у^е-с-т^.
—Ваши предложения по структуре CYRTUG и др.
—Ваши предложения по структуре CYRTUG и др.
Дата заполнения: Подпись:
Дата заполнения: Подпись:
Рис. 3. Анкета почетного члена ассопиапии CvrTUG Лональла Кнута.
Почетный доктор Санкт- Петербургского университета Дональд Кнут.Почетный доктор Санкт- Петербургского университета Дональд Кнут.
Почетный доктор Санкт- Петербургского университета Дональд Кнут.
Почетный доктор Санкт- Петербургского университета Дональд Кнут.
Речь Дональда Кнута, прочитанная им на торжественной церемонии вручения отличительных знаков почетного доктора Санкт-Петербургского университета
К своему величайшему изумлению я узнал, что Санкт-Петербургский университет вроде бы присуждает мне докторскую степень. По правде говоря, сначала я не поверил этой новости, поскольку впервые меня известили об этом по e-mail— почему-то электронная почта не заслуживает такого же доверия, как старомодный эпистолярный жанр! Однако известие о докторской степени явилось, на самом деле, правдой, и теперь я удостоен большой чести сравняться по рангу со многими выдающимися питомцами вашего университета.
Мне особенно приятно принять эту награду в ее связи с молодой областью науки — информатикой (computer science). Тот факт, что старейший российский университет впервые отмечает такого рода знаком отличия одного из информатиков — хорошая новость для всех информатиков, поскольку это означает, что мы постепенно перерастаем из возраста научного детства в пору юношеской зрелости.
Теперь все больше и больше научных работников осознают, что информатика является не только отраслью науки, которая помогает физикам заниматься физикой, биологам — биологией, врачам — практиковать в медицине, матема
тикам — развивать математику, историкам — писать историю, а музыкантам — музыку: информатика также помогает информатикам совершенствовать информатику. Другими словами, информатика — это область науки, которая тесно связана со всеми направлениями человеческих устремлений; при этом она имеет дело с новыми задачами, которые представляют интерес для ее собствен
ных нужд.
Мне также очень приятно быть здесь, в Петербурге, поскольку так много людей, которые оказали наиболее существенное влияние на мою собственную жизнь, были тесно связаны с этим городом. Прежде всего, я сильно взволнован тем, что нахожусь в том месте, где Леонард Эйлер создал большинство своих трудов, проводя коренную перестройку математики в XVIII веке. Эйлер приехал сюда в 1727 году, когда ему было всего лишь 20 лет от роду —вскоре после основания вашего университета, когда на улицы еще забегали волки. Математика—это наука примеров, а Эйлер открыл сотни из наиболее значительных примеров, которые нам известны. В наши дни мы считаем большинство этих фантастических открытий само собой разумеющимся, но в доэйлерово время никто не отваживался полагать, что математика окажется столь обогащенной
и захватывающей. Эйлер также коренным образом изменил математические обозначения — те символы, которые мы используем для описания абстрактных величин. Он предложил печатникам Санкт-Петербурга выпускать математические книги, отвечающие новым, более высоким типографским стандартам. И, возможно, еще более важно то, что он принципиально перестроил преподавание и изложение математики —в этом он был большой мастер. Собрание его сочинений, которое насчитывает более 60 томов, оказало влияние на несравненно большее число математиков по сравнению с написанным любым другим.
Я также ощущаю сокровенную связь с Санкт-Петербургом благодаря сочинениям великих музыкантов, таких, как Бородин, Чайковский и Прокофьев. Эти композиторы сами были мастерами создания математических и комбинаторных- образов — они доставили мне много мгновений приятных переживаний.
Информатика располагает своим собственным разнообразием примеров, дополняющих весьма отличные от них примеры математики и музыки. Моя собственная деятельность в значительной степени посвящена эстетическим аспектам этих примеров — созданию компьютерных программ, которые столь же прекрасны, как и произведения искусства. Я полагаю, что к компьютерным программам правильнее всего относиться как к литературным трудам, предназначенным в первую очередь для чтения людьми, а не выполнения компьютерами. Любая программа может быть вдвойне восхитительна — функционально и эстетически,— точно так же, как Петровский зал, в котором мы находимся, восхитителен, поскольку он предоставляет нам приют и кров и поскольку он доставляет наслаждение нашим чувствам и мыслям. Любой программист может получить двойное удовлетворение — от решения задачи как таковой и от ее решения элегантным способом.
Одной из самых удивительных сторон компьютеров является то, что они становятся все лучше и лучше, в то время как все остальное становится все хуже и хуже. Даже в наше время серьезных экономических проблем количество вычислений, которое может быть выполнено в пересчете на падающий рубль, продолжает возрастать. Это вселяет в меня надежду, что компьютеры окажут большую помощь в решении тех проблем, с которыми сейчас столкнулся наш мир. Потенциал их велик, но и задачи трудны. Благодаря существенно облегченным способам общения, информатики из Санкт-Петербурга объединяют свои усилия с информатиками всего мира для того, чтобы достойно встретить вызов времени.
[Перевод Б. Б. Походзея. Опубликовано в газете «Санкт-Петербургский университет», № 15 (3366), 1994 г.]
Замечательные системы Т^Х и METAFONT в этом году отмечают свой серебряный юбилей. Дональд Кнут не остался равнодушным к этому событию (хотя обычно, как истинный программист, отмечает только даты, представляющие собой «2 в степени 2 в степени 2 и т. д.»). Только что на его сайте появилась следующая информация:
Вскоре будет сделано первое с 1995г. обновление TgX'a и первое с 1998 г.—METAFONT'а, т. е. появятся версии TgX 3.141592 и METAFONT 2.71828.
Я, конечно, обещал сделать эти программы неизменными и никогда не вводить новых возможностей. Но когда обнаруживаются ошибки, я считаю для себя лучшим выходом подправить программы и довести их до состояния безукоризненности.
Мораль: те, кто занимается распространением систем на базе ТЁК'а, должны как можно скорее подготовиться к обновлению своих дистрибутивов. Обычные пользователи могут спокойно дождаться момента, когда им придется обновлять свое оборудование (например, купить новый компьютер), и только тогда заменить свое ТЁХ'овское программное обеспечение, если, конечно, для них описанные выше изменения не являются крайне необходимыми.
Найдено лишь четыре недочета в T^X’e, одно —в METAFONT’e и сделаны усовершенствования рисунков нескольких букв семейства Computer Modern. Только и всего — по прошествии соответственно 9-ти и 5-ти лет!
Книга, которую вы держите в руках, позволит вам заглянуть в творческую мастерскую автора и проследить за процессом создания этих уникальных программных продуктов. Это собрание работ трудно отнести к какому-то конкретному жанру: здесь представлены как строго научные работы, посвященные серьезным математическим и компьютерным проблемам, так и задачки развлекательного толка, способные скрасить досуг математика, программиста или шрифтового дизайнера (в случае автора — это одно и то же лицо). Несколько выступлений профессора Кнута перед научной аудиторией и пользователями систем Т^Х и METAFONT представляют собой образцы живого общения и могли бы быть отнесены к жанру мемуаров, если бы там порой не затрагивались весьма специальные вопросы. Короче говоря, данное издание вполне отражает многогранность дарования автора, широту его интересов и способность излагать весьма серьезные научные результаты на высокохудожественном уровне.
Понятно, что донести до русскоязычного читателя это многообразие жанров во всем его великолепии — задача для переводчиков не из легких. Мы отдаем себе отчет, что несмотря на все приложенные старания нам вряд ли удалось достичь того же уровня совершенства. Тем не менее, хочется выразить надежду, что предлагаемая вниманию читателей книга будет встречена с большим интересом всеми, кто уже знаком с творчеством Дональда Кнута, а те, кому еще не доводилось сталкиваться с его произведениями, примкнут к армии его почитателей.
И. А. Маховая Январь, 2003 г.
Предисловие
Моему отцу,
Эрвину Кнуту (1912-1974), верному служебному долгу
В этой книге собраны более 30 моих статей и заметок по компьютерной типографии, в обиходе называемой «настольные издательские системы». Мне посчастливилось жить в то время, когда происходила решительная революция в способе представления в печатных документах слов, символов и изображений: на смену аналоговым методам пришли цифровые, предназначенные для компьютерной обработки.
По-видимому, в моих венах течет типографская краска: когда я впервые узнал о возможностях компьютерной технологии в печатном деле, я не мог воспротивиться желанию потратить всю оставшуюся жизнь на то, чтобы попытаться адаптировать типографскую премудрость предыдущих столетий к возможностям сегодняшнего дня. Надеюсь, что и читателя охватит то же волнение, которое испытывал я в течение десятилетий, ушедших на разработку способов получения при помощи компьютеров красиво оформленных книг.
У Леонардо да Винчи в его записях есть одно безапелляционное высказывание: «Пусть никто, кроме математиков, не читает моих работ». На самом деле, он сказал это дважды, так что, вероятно, говорил всерьез. Но, к счастью, большинство не последовало его воле; не-математики благополучно справляются с математическими понятиями, когда описания не перегружены специальным жаргоном. Так что я хотел бы высказаться противоположно изречению Леонардо: «Пусть любой, кто не является математиком, читает мои работы». (Математикам, вообще говоря, это также не возбраняется.)
Каждый автор, разумеется, хочет быть читаемым; я процитировал изречение Леонардо как своего рода оправдание того факта, что некоторые главы этой книги изначально были адресованы профессиональным математикам, тогда как другие — художникам-дизайнерам или людям других профессий. Я лелею надежду, что, используя профессиональный жаргон в минимальном объеме, смог выразить основные идеи, пронизывающие многие специальные области. Ведь книгопечатание такое же междисциплинарное направление, каким может быть любой другой предмет.
Глава 1 представляет собой обзор моей деятельности: взгляд в прошлое с высот 1997 г. В гл. 2 дается «взгляд в будущее» той же деятельности; это было написано в 1977 г., когда я только начинал. Поводом для написания гл. 2 было весьма знаменательное событие в моей жизни: меня пригласили выступить в качестве лектора на Гиббсовских чтениях — ежегодном мероприятии Американского математического общества. В свое время Гиббсовскими лекторами были такие выдающиеся математики, как Г. X. Харди, Альберт Эйнштейн и Джон фон Нейман, так что мне, разумеется, было не легко тягаться с ними. Пригласившие меня ожидали, что я буду воспевать информатику, но вместо этого я решил рассказать о своей новой работе, начатой полгода назад, о которой еще не успел поведать никому, кроме нескольких коллег: я говорил о типографии! При этом я, разумеется, не хотел ниспровергать высокие традиции Гиббсовских чтений, а посему дал некое математическое обоснование, что, по крайней мере, слегка усложнило изложение.
Однако в основном я хотел подчеркнуть, что не следует ограничивать математические идеи традиционными приложениями и что я нахожу весьма заманчивым иметь возможность применять математику к полиграфии. К счастью, я не мог предвидеть, что мои замечания окажутся как нельзя более своевременными и найдут очень дружественную аудиторию, потому что многие из присутствующих сталкивались с проблемами публикации и могли извлечь для себя большую пользу из такого рода исследования. В результате я получил массу предложений о помощи от экспертов в самых разных областях, а Американское математическое общество предоставило моему проекту серьезную финансовую поддержку.
Из гл. 1 и 2 видно, что моя работа по компьютерной типографии имела два основных направления, соответствующих двум компьютерным программам, известным как ТеХ и METAFONT. Первая — ТеХ — имеет дело с расположением литер и изображений на страница, а вторая —METAFONT —непосредственно с созданием этих литер и изображений.
Родственные ТЕХ’у темы обсуждаются в гл. 3-12. Один из наиболее важных аспектов верстки полосы набора —это проблема разбивки абзаца на отдельные строки. Первой мотивацией, побудившей меня приступить к разработке системы ТеХ, был тот факт, что компьютеры справляются с этой задачей лучше, чем большинство специалистов ручного набора: в гл. 3 описывается история разбивки на строки, а также усовершенствующие этот процесс алгоритмы, основанные на многолетних экспериментах с прототипами программы ТеХ. Затем в гл. 4 обсуждаются дополнительные проблемы, возникающие при смешивании языков, использующих противоположные направления при написании: например, иврит или арабский, имеющие написание справа-налево, и европейская письменность. Несколько легковесные главы 5, 6, 7 и 8 представляют собой краткое описание подручных Технических средств, которые я счел удобными для набора кулинарных рецептов, получения логотипа ТеХ, распечатки отдельных страниц из объемистого тома и оформления путевых заметок
моей жены. В гл. 9 приводится одна короткая головоломка для ТЁХпертов, а в гл. 10 содержится подборка упражнений для тех, кто хочет углубиться в предмет и узнать, как работает сам ТЁХ. Компьютерная программа для ТЁ%’а была первым широкомасштабным применением методологии, которую я назвал «документированное программирование»; ценность публикации документации к такой программе весьма повысилась благодаря гипретекстно-подобным миниуказателям, использование которых описывается в гл. 11. Наконец, в гл. 12, обсуждается взаимодействие между ТЁ^’ом и другими системами посредством гибких спецификаций, называемых «виртуальные шрифты».
Относящиеся к METAFONT’y вещи являются темой гл. 13-23. Прежде всего, в гл. 13 описывается интересная математическая задача, возникающая при попытке обучить компьютер рисовать букву «8». В гл. 14 рассказывается, как меня пригласили нарисовать с помощью METAFONT’a одну букву из языка хинди, которую я до того никогда не видел. И в той, и в другой главе иллюстрируется важная концепция «меташрифта», а именно идея, что многие разные, но родственные варианты графических форм букв используются для объединения шрифтов в семейства. В гл. 15 обсуждаются меташрифты и параметрические вариации вообще. Глава 16, которая изначально была основным докладом на международной рабочей конференции дизайнеров шрифта, представляет собой ретроспективу того, что я изучил в течение первых шести лет моих экспериментов с METAFONT-подобными системами. Новое семейство шрифтов для набора математических формул, разработанное по заказу Американского математического общества Германом Цапфом и реализованное при помощи METAFONT’a, представлено в гл. 17. В гл. 18 рассказывается, как я адаптировал текстовые шрифты в математических книгах таким образом, чтобы они по насыщенности гармонировали с математическими символами Цапфа. В гл. 19 подводятся итоги обучения METAFONT’y и основам шрифтового дизайна группы студентов Станфордского университета. В гл. 20 описывается новое семейство шрифтов под названием Punk, созданное забавы ради за один вечер. Главы 21 и 22 посвящены увлекательным задачам, возникающим при преобразовании фотографий и других тоновых иллюстраций в дискретные точечные изображения при выводе на печать. Наконец, гл. 23 имеет дело с границами дискретных изображений, которые предназначены для имитации отрезков прямых в тупых и острых углах.
В остальных главах обсуждаются ТЁХ и МЕТА FONT в историческом аспекте: в прошлом, настоящем и будущем. Меня всегда интересовало происхождение программных систем на уровне идей и дальнейшее их развитие, в связи с чем в гл. 24 и 25 воспроизведены самые первые черновые наброски программы ТЁХ- Эти описания, взятые из компьютерного архива и опубликованные здесь впервые, в основном предназначались для меня и моих ассистентов как руководство по первым реализациям 1977 и 1978 гг.; из них ясно, как повлияла на дальнейшее эта работа, и они выявляют значительное отличие между моими первоначальными представлениями и существующей
в настоящее время системой Т^Х. В гл. 26 описываются самые первые книги, изготовленные в разных частях света при помощи ТвХ’а и METAFONT’a. Затем в гл. 27 мы попадаем в более близкие времена: здесь отражено влияние графического пользовательского интерфейса на компьютерные операционные системы, благодаря чему они стали более визуализированы; именно, представлены иконки, символизирующие различного рода файлы, ассоциированные с вводом и выводом ТЁХ’а и METAFONT’a. Глава 28 представляет собой текст доклада, сделанного в 1986 г., когда я впервые поверил, что привел свою работу по компьютерной типографии к успешному завершению; в гл. 29 написано, что я сказал в 1989 г., когда понял, что в окончательные версии TgX’a и METAFONT’a необходимо внести изменения, чтобы они могли оперировать с большим количеством языков. В гл. 30 содержится то, что я сказал в 1990 г., когда убедился, что эти изменения действительно последние. В гл. 31-33 содержится запись живых бесед с пользователями ТЁХ’а и METAFONT’a в виде вопросов и ответов, которые проводились в Соединенных Штатах (1995 г.), Чешской республике (1996 г.) и Нидерландах (1996 г.). Наконец, гл. 34 представляет собой только что написанное продолжение моей статьи «Ошибки ТЁХ’а».
Многие из глав этой книги были написаны тогда, когда современная технология печатания еще только быстро набирала обороты, используя экспериментальные системы, которые выталкивали текущее состояние этого искусства на предел его возможностей. Чтобы воспроизвести это теперь я должен был воссоздать те экспериментальные системы и воскресить десятки давно утерянных шрифтов, моделируя их готовыми версиями Т£Х’а и METAFONT’a, дабы не исказить исторический контекст. Многие главы были изначально написаны в соответствии с форматом иных изданий, так что мне пришлось адаптировать их к размеру полосы набора данной книги и соблюсти другие принятые здесь соглашения. Отдельные слова и выражения я изменял, но основной материал остался в сущности тем же, каким он был при первых публикациях; исключение составляют гл. 21 и 22, которые претерпели существенную переработку, чтобы соответствовать сегодняшнему положению дел. Списки литературы были адаптированы к требуемому формату; в нужных местах были расставлены дополнительные ссылки и замечания; десятки иллюстраций были существенно улучшены.
Некоторые иллюстрации в тексте должны были выглядеть не лучшим образом, для контраста с другими, которые я предложил в качестве более подходящих альтернатив. Но в принтерах заложена масса приемов для улучшения материала низкого качества; они сработали, в связи с чем некоторые мои «плохие примеры» оказались отретушированными и выглядят лучше, чем я предполагал. Я приношу извинение за любые непрошенные улучшения, которые не смог проконтролировать.
Я весьма признателен CSLI — Станфордскому центру изучения языка и информации —за предоставленную мне возможность опубликовать книгу и за профессиональную подготовку всего того, что я хотел представить в ней. В
частности, Уильям Э. МакМекан и Уильям Дж. Крофт подготовили электронные варианты многих файлов, которые предварительно были набраны моим секретарем Филлис Уинклер; Тони Ги собрал и выстроил материал самым подобающим образом; Копенгавер Кампстон сделал обложку книги. Инициатором и руководителем всего проекта был Дикран Карагезян. Архив Стан- фордского университета предоставил эффективный доступ к своей коллекции по ТЁХ’у и METAFONT’y (SC 97, ящики 12-25). Мартин Фрост помог мне реконструировать многочисленные файлы, изначально подготовленные мною на легендарном Станфордском компьютере me SAIL (списанном в 1990 г.). Барбара Битон из Американского математического общества нашла многие электронные файлы статей, первоначально опубликованных в журнале TUGboat, издаваемом Т^Х Users Group, который она так тщательно редактирует уже почти 20 лет. Фирмы Sun Microsystems и Apple Computer любезно предоставили мне компьютеры, на которых я имел возможность отшлифовать и отредактировать окончательный вариант книги.
Это третий в серии сборников моих работ, написанных за прежние годы, которую планирует выпустить издательство CSLI. Первый том —Literate Programming—появился в 1992 г., второй — Selected Papers on Computer Science —в 1996 г. Еще пять томов находятся в стадии подготовки. В них будут представлены избранные работы по анализу алгоритмов, языкам программирования, построению алгоритмов, дискретной математике и играм и развлечениям.
Дональд Э. Кнут Станфорд, Калифорния Август 1998 г.
Ссылки на источники
«Mathematical Typography» (Математическая типография) первоначально опубликовано в Bulletin of the American Mathematical Society (новая серия) 1 (March 1979), pp. 337-372. Copyright ©1979 by the American Mathematical Society. Перепечатано с разрешения.
«Breaking Paragraphs Into Lines» (Верстка абзацев) первоначально опубликовано в Software — Practice and Experience 11 (1981), pp. 1119-1184. Copyright John Wiley & Sons Limited. Перепечатано с разрешения.
«Mini-Indexes for Literate Programs» (Мини-указатели для самодокументи- рованных программ) первоначально опубликовано в Software — Concepts and Tools 15 (1994), pp. 2-11. Copyright ©1994 by Springer-Verlag GmbH & Co. KG. Перепечатано с разрешения.
«The Letter S» (Буква S) первоначально опубликовано в The Mathematical Intelligencer 2 (1980), pp. 114-122. Copyright ©1980 by Springer-Verlag GmbH & Co. KG. Перепечатано с разрешения.
«Му First Experience with Indian Scripts» (Мое первое знакомство с индийской письменностью) первоначально опубликовано в буклете CALTIS-84, р. 49. Copyright ©1984 by ITR Graphic Systems Pvt. Ltd. Перепечатано с разрешения.
«The Concept of a Meta-Font» (Концепция меташрифта) первоначально опубликовано в Visible Language 16 (1982), pp. 3-27.
«Lessons Learned from METAFONT» (Уроки METAFONT’a) первоначально опубликовано в Visible Language 19 (1985), pp. 35-53. Copyright by Illinois Institute of Technology — Institute of Design. Перепечатано с разрешения.
«AMS Euler —A New Typeface for Mathematics» (AMS Euler —новый математический шрифт) первоначально опубликовано в Scholarly Publishing 20 (1989), pp. 131-15 7. Copyright ©1989 University of Toronto Press Incorporated. Перепечатано с разрешения University of Toronto Press Incorporated.
«Digital Halftones by Dot Diffusion» (Оцифровка полутонов посредством диффузии точки) первоначально опубликовано в ACM Transactions on Graphics 6 (1987), pp. 245-273. Copyright ©1987 by ACM Press, a Division of the Association for Computing Machinery, Inc. (ACM). Перепечатано с разрешения.
«А note on digitized angles» (Заметки об оцифровке углов) первоначально опубликовано в Electronic Publishing — Origination, Dissemination, and Design 3 (1990), pp. 99-104. Copyright ©1990 by Penn Well Publishing Co. Перепечатано с разрешения.
«ТЁХ Incunabula» (ТЁХ’овские инкунабулы) первоначально опубликовано в TUGboat 5 (1984), рр. 4-11.
«А Course on METAFONT Programming» (Из опыта преподавания программирования на METAFONT’e) первоначально опубликовано в TUGboat 5 (1984), рр. 105-118.
«Recipes and Fractions» (Рецепты и дроби) первоначально опубликовано в TUGboat 6 (1985), рр. 36-38.
«Computers and Typesetting» (Компьютеры и книгоиздание) первоначально опубликовано в TUGboat 7 (1986), рр. 95-98.
«The ТЁХ Logo in Various Fonts» (Логотип системы ТЁХ в различных шрифтах) первоначально опубликовано в TUGboat 7 (1986), р. 101.
«Mixing Right-to-Left Texts with Left-to-Right Texts» (Сочетание текстов, читаемых справа налево и слева направо) первоначально опубликовано в TUGboat 8 (1987), рр. 14-25.
«Problem for a Saturday Morning» (Задача на субботнее утро) первоначально опубликовано в TUGboat 8 (1987), рр. 73, 210.
«Fonts for Digital Halftones» (Шрифты для дискретных полутонов) первоначально опубликовано в TUGboat 8 (1987), рр. 135-160.
«Printing Out Selected Pages» (Генерирование избранных страниц) первоначально опубликовано в TUGboat 8 (1987), р. 217.
«Macros for Jill» (Макро, написанные для Джил) первоначально опубликовано в TUGboat 8 (1987), рр. 309-314.
«А Punk Мeta-Font» (Меташрифт Punk) первоначально опубликовано в TUGboat 9 (1988), рр. 152-168.
«Typesetting Concrete Mathematics» (Набор книги Concrete Mathematics) первоначально опубликовано в TUGboat 10 (1989), рр. 31-36, 342.
«The New Versions of ТЁХ and METAFONT» (Новые версии ТЁХ’а и METAFONT’a) первоначально опубликовано в TUGboat 10 (1989), рр. 325-328.
«Virtual Fonts: More Fun for Grand Wizards» (Виртуальные шрифты. Развлечение для настоящих мастеров) первоначально опубликовано в TUGboat 11 (1990), рр. 13-23.
«Exercises for ТЁХ: The Program» (Упражнения к книге ТЁХ: The Program) первоначально опубликовано в TUGboat 11 (1990), рр. 165-170, 499-511.
«The Future of ТЁХ and METAFONT» (Будущее ТЁХ’а и METAFONT’a) первоначально опубликовано в TUGboat 11 (1990), р. 489.
«Icons for ТЁХ and METAFONT» (Пиктограммы для ТЁХ’а и METAFONT’a) первоначально опубликовано в TUGboat 14 (1993), рр. 387-389.
«Questions and Answers, I» (Вопросы и ответы, I) первоначально опубликовано в TUGboat 17 (1996), рр. 7-22.
«Questions and Answers, II» (Вопросы и ответы, II) первоначально опубликовано в TUGboat 17 (1996), рр. 355-367. Copyright ТЁХ Users Group. Перепечатано с разрешения.
«Questions and Answers, III» (Вопросы и ответы, III) первоначально опубликовано в MAPS (Minutes and APpendiceS) 16 (1996), рр. 38-49. Перепечатано с разрешения NTG, the Dutch-language-oriented ТЁХ Users Group.
Компьютерная типография
[Лекция, прочитанная 11 ноября 1996 г. в связи с присуждением премии Киото- 1996 в современных технологиях, учрежденной фондом Inamori Foundation of Kyoto, Япония.]
[Слайд 0 демонстрировался во время представления лектора публике]
Слайд 0.
С тех пор как я себя помню, я находился в атмосфере любви к книгам. Прежде всего, мои родители много мне читали —для Америки необычная практика в те времена: в 40-е годы расхожая «мудрость» гласила, что ребенок, в раннем возрасте отведавший интеллектуальную пищу, станет скучать в школе. Благодаря моим родителям в четырехлетием возрасте я стал самым юным членом Клуба любителей книги при Миллуокской публичной библиотеке [слайд 1].
Я полагаю, что именно раннее знакомство с книгами и есть причина того, что я не могу припомнить ни одного дня, чтобы мне было скучно во время учебы. В самом деле, современное общество все смешало воедино в своей концепции «скуки»: люди часто говорят друг другу, что они скучают, но для меня это почти шокирующее, позорное признание. Почему это кто-то должен нас развлекать? Люди, не находящие интереса в том, что они делают, постоянно нуждающиеся во внешних раздражителях и источниках удовольствия, упускают большую часть земных радостей.
Что касается меня, то со мной происходило прямо противоположное: я совершал крен в другую сторону. Я зачастую находил столько интересного в
Too young io read books himself, Donald Knuth, 4, of 2(m>1 N, i$th st. is nevertheless the youngest member of the public library's Ancient Order of Book Worms. His father reads storyhooks to him. Wednesday he gave oral reports on his “reading" in the young people’s room of the library. Some of the stories he reported on were “Country Bunny," “Pokey Bear." “When the Root Children Wake Up" and “Babar the King."
Слайд 2.Слайд 2.Слайд 1.
Слайд 2.
Слайд 2.
первой главе изучаемой книги, что у меня не хватало времени прочесть заключительные главы.
Однажды, когда мне было 5 лет, родители разрешили мне самому поехать на трамвае в библиотеку. Там я был совершенно зачарован детскими книгами. Так как я не вернулся домой в назначенное время, обеспокоенные родители позвонили в библиотеку. Ночной сторож пошел меня искать и нашел среди полок, упивающегося чтением: у меня и мысли не возникло, что библиотека закрыта и пора идти домой! Даже теперь моя жена знает, что если я пошел в библиотеку, то обратно меня рано ждать не приходится.
Слайд 3.Слайд 3.Слайд 4.Слайд 4.На самом деле я не только всегда любил книги, я также обожал отдельные буквы в книгах. Вот страничка из моей первой азбуки [слайд 2]. Удивительно, но я отметил маленькими крестиками все засечки у букв и сосчитал эти засечки: у буквы К их было 7; у буквы Р [СЛАЙД 3] 4 засечки; у буквы О [СЛАЙД 4] их не было вовсе.
Слайд 3.
Слайд 3.
Слайд 4.
Слайд 4.
Из этого вы можете заключить, что цифры мне нравились не меньше букв. Со временем, став преподавателем Станфордского университета и поняв, что мои основные таланты связаны с программированием, я начал писать свои собственные книги. Первая моя книга —первый том The Art of Computer Programming—вышла в свет в 1968 г.; второй том был готов годом позже [слайд 5].
Я испытывал такое волнение при виде этих томов не только потому, что был доволен их содержанием, но и потому, что они были великолепно типографски оформлены. Эти книги были выполнены в лучших проверенных временем традициях представления научно-технических текстов. Именно в таком классическом стиле были оформлены мои любимые учебники в колледже. Так что смотреть на эти тома было не менее приятно, чем читать их.
Их готовили по технологии 19-го столетия «монотип», в которой были задействованы два вида машин. Прежде всего, имелась сложная пневматическая клавиатура с 284 клавишами [слайд 6]. Эта машина производила перфорированную бумажную ленту, нечто вроде нотных списков пианистов; вы можете видеть ее вверху картинки. Эта лента затем использовалась в качестве управляющего механизма в специальной отливочной машине [слайд 7], производящей отдельные фрагменты наборного материала из горячего расплавленного свинца.
Слайд 8.
22Процесс набора математических формул на такой машине был чрезвычайно сложным; вот типичная формула из тома 2 [СЛАЙД 8]. Наборщик, специализирующийся именно на таких вещах, воспроизводил большую часть этой формулы в два прохода: сначала он набирал буквы и символы основного уровня и их верхние индексы (это
2
2
I det (а )| <
в данном случае); затем вторым проходом вставлялись нижние индексы (это повторенные здесь дважды литеры ^\ Оператор монотипа должен был знать
ширину каждой литеры, чтобы оставлять достаточно места для последующего заполнения индексами. После того как формула была отлита в металле, другой специалист вручную вставлял оставшиеся большие символы (большие скобки и символы типаПи£).
Всего лишь несколько десятков человек в мире умели набирать формулы на монотипе. Я имел удовольствие быть знакомым с наборщиком Эриком, который набирал все формулы в 1-ом и 2-ом томах. Я с удивлением обнаружил, что он говорил с сильным акцентом, присущим жителям предместий Лондона, хотя жил в Америке и ему поручали делать самые сложные книги по математике.
Program A (Addition, rubtraction, and normalization). The following program is a subroutine for Algorithm A, and it is also designed so that the normalization portion can be used by other subroutines which appear later in this section. In this program and in many other programs throughout this chapter, OFLO stands for a subroutine which prints out a message to the effect that Mix’s overflow toggle was unexpectedly found to be “on.1*
01 EXP EQU 1:1 Definition of exponent field.
Ot FSUB STA TEMP Floating-point subtraction subroutine: 03 LOAN TEMP Change sign of operand.
Слайд 9.
Книги по компьютерным наукам добавили новые проблемы к тем, что были при наборе математики: программистам понадобился шрифт особого начертания typewriter type, чтобы представлять тексты программ, с которыми имели дело машины. На слайде [слайд 9], например, представлен еще один фрагмент из 2-го тома — часть компьютерной программы. Мне нужно было объединить шрифт typewriter, которым набрано слово ‘OFLO’ например, с обычным шрифтом. Прежде всего мне сообщили, что невозможно добавить в монотип еще один алфавит, поскольку традиционные математические шрифты уже исчерпали предел возможностей монотипа. Однако позже Эрик и его мастер придумали, как это осуществить. Обратите внимание, что в этом шрифте мне нужна была буква 0 нового, менее округлого рисунка, чтобы различие между 0 (буква о) и 0 (нуль) было более очевидным.
В 1960 гг. металлический набор типа «монотип» начал вытесняться фотонабором. Новые машины воспроизводили страницы путем по-литерной экспозиции фотоформ, используя остроумную комбинацию из вращающихся дисков и линз для помещения каждой литеры в соответствующую позицию. Вскоре после опубликования 3-го тома мой издатель продал монотипы и Эрик был вынужден искать другую работу. Новые допечатки 1-го и 2-го томов появились в 1975 г. с исправленными опечатками, рбнаруженными читателями. Эти исправления набирались в Европе, где монотипы еще продолжали существовать.
Я уже подготовил 2-е издание 2-го тома, которое предполагалось набирать заново. Мои издатели сочли, что готовить книгу в 1976 г. по технологии 1969 г. слишком дорогое удовольствие. Кроме того, шрифт в фотонаборных машинах не совпадал по рисунку со шрифтом, которым были набраны оригиналы. Я вылетел из Калифорнии в Массачусетс для решающей встречи. Издатели согласились, что качеством набора жертвовать нельзя, и через несколько месяцев
Program A (Addition, subtraction, and normalization). The following program is a subroutine for Algorithm A, and it is also designed so that the normalization portion can be used by other subroutines which appear later in this section. In this program and in many other programs throughout this chapter, OFLO stands for a subroutine which prints out a message to the effect that Mix’s overflow toggle was unexpectedly found to be “on.” The byte size b is assumed to be a multiple of 4. The normalization routine WORM assumes that rl2 — * and rAX = /, where rA = 0 implies rX = 0 and rI2 < b.
02 FSUB0302 FSUB03EQU STA LOANEQU STA LOANDefinition of exponent field.Floating-point «ub* tact ion subroutine Change sign of operand.Definition of exponent field.Floating-point «ub* tact ion subroutine Change sign of operand.Слайд 10.Слайд 10.Слайд 11.Слайд 11.
02 FSUB
03
02 FSUB
03
EQU STA LOAN
EQU STA LOAN
Definition of exponent field.
Floating-point «ub* tact ion subroutine Change sign of operand.
Definition of exponent field.
Floating-point «ub* tact ion subroutine Change sign of operand.
Слайд 10.
Слайд 10.
Слайд 11.
Слайд 11.
были предприняты большие усилия, чтобы получить новые шрифты, совпадающие по начертанию со старыми.
Результат, однако, оказался удручающим. Вот, например [слайд 10], некий набор из второй, «отрегулированной» версии новых шрифтов. По сравнению с первой попыткой эта выглядела много лучше, но все еще была неприемлема. Буква «N» в «NORM» была слегка наклонена; лигатура «ff» в «effect» оказалась слишком темной; буквы «ip» в «multiple» очень близко расположились друг к другу и т. д.
Я не знал, что делать. На написание этих книг я потратил 15 лет, но если они были сделаны, чтобы выглядеть так ужасно, я больше ничего не хотел писать. Эта продукция не делала мне чести.
Возможный выход из создавшейся дилеммы представился сам собой несколькими месяцами позже, когда я познакомился с другим радикальным нововведением в технологии печатания. Новейшие машины помещали изображения на пленку дискретными средствами вместо аналоговых — нечто вроде того различия, которое есть между телевизионными и настоящими фильмами. Формы букв были теперь составлены из крошечных точек на основе электронных импульсов, которые были либо в состоянии ВКЛЮЧЕН, либо в состоянии ВЫКЛЮЧЕН [слайд 11]. Так-так! Такого рода вещи я мог понять! Это очень просто, вроде лампочек на табло стадиона.
Металлургия и горячие отливки для меня всегда были тайной за семью печатями; я также ничего не понимал ни в оптике, ни в механических устройствах выравнивания. Но буквы, состоящие из маленьких точек — это объект из компьютерных наук! Это всего лишь биты, бинарные разряды, нули и единицы! Ставим единицу в том месте, где должна быть краска, и нуль в том, где ее быть не должно — и можно печатать книжную страницу!
Раньше я видел формы букв в дискретном представлении, но только на машинах, дающих грубые оттиски. Программисты многие годы экспериментировали на устройствах Xerox Graphics Printer, которые были изобретены в Англии примерно в 1961 г., но до 70-х гг. компьютерами не управлялись. Эти устройства делали буквы из точек, но точки не были достаточно маленькими. Один дюйм содержал только 180 точек, так что буквы состояли из ребристых линий. Поиграть на Xerox Graphics Printer было очень приятно, но я никогда не думал, что на подобной машине можно будет печатать настоящие книги. Мне они казались весьма примитивными, способными только производить дешевые имитации — разница здесь была такая же, как между электронным синтезатором и подлинными пианино или виолончелью.
Но в феврале 1977 г. мне на глаза впервые попалась распечатка с электронной машинки высокого качества с разрешением 1000 точек на дюйм ... и это выглядело великолепно, даже немного лучше, чем самый совершенный металлический набор, который я когда-либо видел. И вдруг я понял, что точки, если они достаточно малы, согласно законам физики образуют как бы гладкие кривые. Тогда я вспомнил, что глаз человека имеет дискретную природу, так как состоит из отдельных палочек и колбочек. Таким образом, я впервые убедился, что дискретная наборная машина в самом деле способна производить книги самого высокого полиграфического качества.
Цифровые (дискретные) камеры не решают всех острых вопросов традиционной фотографии. Телевизионное изображение высокого разрешения не может идти ни в какое сравнение с кинофильмом. Но для типографской краски на бумаге дискретный подход столь же хорош, как и любой другой.
Иначе говоря, проблема высококачественного книгоиздания перестала быть задачей металлургов и оптиков, а стала задачей программистов. Тот факт, что Гутенберг создавал книги, используя металлический набор, вдруг превратился всего лишь в 500-летний исторический эпизод. Новые машины сделали старый механический подход совершенно бесполезным: будущее типографии зависело от людей, которые хорошо понимали, как создавать образы из нулей и единиц. Оно теперь зависело от математиков и программистов.
Осознав это в полной мере, я не мог не поддаться искушению решить данную полиграфическую проблему самостоятельно. Забросив все начатые дела (тогда я завершал работу над первыми 100 страницами 4-го тома), я решил написать программу, генерирующую страницы в виде образов из нулей и единиц, которые так были нужны мне и моим издателям для нового издания 2-го тома.
Я поначалу думал, что это будет совсем нетрудно, и отвел на такую деятельность всего несколько месяцев. Своим издателям в марте 1977 г. я написал, что в июле они получат первую корректуру. О, как далек я был от истины! Всю свою жизнь я недооценивал сложность проектов, за которые брался, но это был мой личный рекорд в оптимистическом прогнозировании.
Прежде всего надо было учесть, что почти никто из компьютерных специалистов не занимался проблемами такого рода, в связи с чем получить финансовую поддержку оказалось довольно трудно. Наборное устройство было слишком дорогим удовольствием, совершенно не вписывающимся в наш уни-
верситетский бюджет. Более того, машина была рассчитана на круглосуточную работу нескольких операторов, а я был всего лишь одинокий одержимый математическими идеями чудак без какого-либо опыта в типографском деле. И еще я полагал, что получив работающую компьютерную программу, я смогу выкроить время на некую цифровую наборную машину.
Это была проблема яйца и курицы. Я не мог установить правила набора, не имея соответствующих шрифтов для текста и математических символов: нужные мне шрифты не существовали в цифровом виде. И я не мог создать шрифты, не установив правил набора. Мне нужно было то и другое одновременно. Существовали другие оцифрованные шрифты, но я был полон решимости создать свои собственные, описываемые математическими формулами, которые я выберу сам. В этом случае мне не пришлось бы столкнуться с такой ситуацией, когда любое изменение технологии снова смешало бы все карты. Имея собственную программу, управляющую всеми аспектами поведения нулей и единиц на страницах, я имел бы возможность определять внешний вид своих книг раз и навсегда.
Мои издатели снабдили меня копиями рисунков шрифтов Monotype, использованных при наборе первого издания 1-го тома. Поэтому мне казалось, что найти математические формулы для описания рисунка букв не составит особого труда. Я встретился с Джоном Уорноком, делавшим аналогичные вещи для исследовательского центра Xerox в Пало Альто, и спросил его, могу ли я использовать возможности лаборатории Xerox для создания своих шрифтов. Ответ был утвердительным, но оставалась одна загвоздка: Xerox настаивал на передаче авторских прав на все шрифты, которые я разработаю на их оборудовании. Конечно, они со своей стороны имели основание это требовать, но такое положение дел совершенно не устраивало меня: математическая формула не может быть ничьей «собственностью»! Математика принадлежит Богу.
Таким образом, я пришел, наконец, в лабораторию искусственного интеллекта Станфордского университета, в которой была телекамера. Я использовал ее для увеличения букв и их оцифровки. К сожалению, телекамера не давала истинной картины — изображения получались искаженными. Еще ужаснее было то, что совсем незначительные колебания освещенности в помещении приводили к разительным изменениям телевизионного изображения. Не было никакой возможности извлечь устойчивые данные для каждой буквы. Шрифты, полученные про помощи телекамеры, выглядели гораздо хуже шрифтов, порожденных нецифровым устройством.
Я попытался сфотографировать страницы и увеличить их, спроецировав на стену у себя дома. Увеличенные контуры букв я обвел карандашом на бумаге. Но это тоже не дало результата.
Наконец, мне пришла простая мысль: ведь эти буквы созданы людьми. Если бы я смог понять, чем они руководствовались, когда рисовали буквы, я смог бы дать компьютеру задание реализовать те же идеи. Теперь моей новой целью было воспроизвести не рисунок букв, а лежащий в его основе интеллект. Я решил обучиться мастерству шрифтовых дизайнеров и научить этому компьютер.
Эта цепочка умозаключений привела меня к компьютерной системе под названием METAFONT, которую я сейчас попытаюсь вам продемонстрировать. [Переключается на слайды на дисплее компьютера.] Здесь представлен способ создания букв при помощи компьютерной программы, к которому я, наконец, пришел. В качестве примера взята буква А. Все ключевые (базовые) точки буквы находятся на показанной здесь сетке, хотя сама сетка в действительности, разумеется, невидима.
На основе этой сетки и заданной спецификации (параметров) текстового шрифта светлого начертания компьютер сначала нарисует основной штрих:
Часть этого штриха затем будет стерта, потому что штрих слишком широк вверху.
Затем пририсовывается левый диагональный штрих
и перекладина.
Теперь нужно добавить засечку внизу слева
11
1
1
и слегка ее подчистить, чтобы она зрительно не утяжеляла букву.
(Такая подчистка едва уловима: нужно внимательно всмотреться!) Аналогично пририсовывается засечка внизу справа:
Буква А готова.
Та же самая программа может нарисовать бесконечно много букв А, если менять спецификации (параметры). Вот, например, более темный или полужирный вариант:
А вот буква А поменьше, подходящая для высококачественной печати:
Ш1ММ1НШ1ММ1ННимНим■■■■■■■■■Шши■Шшидоядоя■■■■
Ш1ММ1Н
Ш1ММ1Н
Ним
Ним
■■
■■
■■
■■
■Шши
■Шши
доя
доя
■■
■■
Простое уменьшение оригинала А на 50% не привело бы к получению правильной буквы мелкого кегля; мы бы получили «а» вместо «а». По правилам полиграфии буквы мелкого кегля по начертанию должны слегка отличаться от их более «взрослых» сестер.
Даже машинописная буква А может быть нарисована той же программой. На этот раз в спецификации указывается, что толстые и тонкие штрихи идентичны, а углы и засечки закруглены.
Получилась буква А из моего первого машинописного шрифта. Позже я узнал, что эта А была чуть-чуть жирнее, чем нужно. Чтобы исправить положение, я слегка раздвинул диагональные штрихи и «подтесал» внутри, так что внутри буквы стало попросторнее.
Вот такая изящная машинописная А используется в последней версии шрифта. С подобными приемами я познакомился несколько лет спустя, когда начал изучать шрифтовой дизайн.
Program A (Addition, aubtraction, and normalization). The following program is a subroutine for Algorithm A, and it is also designed so that the normalization portion can be used by other subroutines that appear later in this section. In this program and in many other programs throughout this chapter, OFLO stands for a subroutine that prints out a message to the effect that Mix's overflow toggle was unexpectedly found to be “on." The byte size b is assumed to be a multiple of 4. The normalization routine NORM assumes that rI2 ■ e and rAX ■■ /, where rA ■ 0 implies rX ■■ 0 and rI2 < b.
00BYTEEQU1(4:4)01EXPEQU1:106FSUBSTATEMP03LOANTEMP00BYTEEQU1(4:4)01EXPEQU1:106FSUBSTATEMP03LOANTEMPByte size b
00BYTEEQU1(4:4)
01EXPEQU1:1
06FSUBSTATEMP
03LOANTEMP
00BYTEEQU1(4:4)
01EXPEQU1:1
06FSUBSTATEMP
03LOANTEMP
Definition of exponent field Floating-point subtraction subroutine: Change sign of operand.
Слайд 12.
А вот образчик того, как выглядел мой первый черновой вариант шрифта в распечатке на принтере Xerox Graphics после полутора лет работы над типографским проектом [слайд 12]. Двумя годами позже при финансовой поддержке моих издателей я, наконец, получил доступ к цифровому устройству с высоким разрешением и смог напечатать 2-й том в новой редакции. Распечатка этой книги выглядела много лучше, чем полученная на принтере Xerox Graphics, с которым я работал прежде, и я уже решил было, что поставленная цель —высокий полиграфический уровень —достигнута.
Но когда я получил сигнальный экземпляр нового издания 2-го тома в знакомом переплете и открыл его, я был страшно разочарован. Книга совсем не была похожа на то, что я ожидал увидеть. После четырех лет тяжелого труда я все еще не представлял себе, как генерировать образы из нулей и единиц, отвечающие требованиям высококачественной печати. Типографский экземпляр второго издания [слайд 13] выглядел ненамного лучше того варианта, который я отверг, приступая к своему типографскому проекту.
Program A (Addition, subtraction, and normalization). The following program is a subroutine for Algorithm A, and it is also designed so that the normalization portion can be used by other subroutines that appear later in this section. In this program and in many others throughout this chapter, OFLO stands for a subroutine that prints out a message to the effect that Mix’s overflow toggle was unexpectedly found to be “on." The byte size b is assumed to be a multiple of 4. The normalisation routine NORM assumes that rI2 = e and rAX = /, where rA = 0 implies rX = 0 and rI2 < b.
00 BYTE EQU 1(4:4)01 EXP EQU 1:102 FSUB STA TEMP03 LDAN TEMP00 BYTE EQU 1(4:4)01 EXP EQU 1:102 FSUB STA TEMP03 LDAN TEMPByte size 6
00 BYTE EQU 1(4:4)
01 EXP EQU 1:1
02 FSUB STA TEMP
03 LDAN TEMP
00 BYTE EQU 1(4:4)
01 EXP EQU 1:1
02 FSUB STA TEMP
03 LDAN TEMP
Definition of exponent field
Floating point subtraction subroutine: Change sign of operand.
Слайд 13.
Между тем мне посчастливилось познакомиться с несколькими ведущими шрифтовыми дизайнерами, которые своими деликатными критическими замечаниями и советами весьма помогли мне в процессе дальнейшего совершенствования. Так что по прошествии еще пяти лет я, наконец, умею делать книги, которыми могу гордиться.
Мне бы не хотелось, чтобы у вас сложилось впечатление, что эти 9 лет я провел исключительно в нудной изнурительной работе. (Как я уже говорил, мне редко бывает скучно.) Шрифтовой дизайн на самом деле — чрезвычайно увлекательное занятие, особенно по части делания ошибок. Компьютер имеет тенденцию рисовать отчетливо созданные изображения, которые человеку даже и не приснятся во сне. Я назвал это «МЕТА-ляп». Например [слайд 14],
вот лигатура ffi, в которой левое f простирается вправо над всей комбинацией вплоть до точки над i. А вот другая ffi, причудливой формы [СЛАЙД 15]: я назвал это «the ffilling station».
При одной из первых попыток нарисовать прописную машинописную букву Y я ошибочно поместил верхнюю правую засечку посередине [слайд 16]. Клянусь, что в тот момент вовсе не думал о йенах!
Слайд 17.Слайд 17.Слайд 18.Слайд 18.
Слайд 17.
Слайд 17.
Слайд 18.
Слайд 18.
Работает ли МЕТА FONT столь же хорошо с японскими буквами, как и с латинскими? Думаю, да, но сам не способен в совершенстве различать азиатские литеры. Мой студент Джон Хобби провел многообещающий эксперимент со специалистом из Shanghai Printing Company Гу Го-Анем, и я с удовольствием представляю образчик того, что они делали. Сначала они написали 13 программ для основных штрихов. Вот пример двух «слезинок», полученных при помощи этих программ [слайд 17]. Шрифтовой дизайнер указывает в спецификации верхнюю точку, нижнюю точку и контур капли; остальное делает
компьютер. Здесь [слайд 18] представлено еще несколько примеров слезинок и варианты трех других основных штрихов.
Хобби и Гу нарисовали при помощи своих программ для штрихов 128 китайских иероглифов. Эти программы позволяют получать иероглифы в трех разных начертаниях, используя три разные версии 13 основных штрихов. Вот [слайд 19] пять иероглифов начертания Song, Long Song и Bold. А вот [слайд 20] примеры 13 основных штрихов для всех трех начертаний.
Слайд 19.Слайд 19.Слайд 20.Слайд 20.
Слайд 19.
Слайд 19.
Слайд 20.
Слайд 20.
Имея систему METAFONT шрифтового дизайна и систему ТЁК правильного размещения букв и символов на странице, любой желающий может теперь самостоятельно красиво оформлять свои книги при разумном объеме усилий с его стороны. Эти системы позволяют авторам осуществлять общий контроль над образами из нулей и единиц, которые нужны для определения страниц. Я приложил особые усилия со своей стороны к тому, чтобы ТЁХ и METAFONT давали один и тот же результат на всех компьютерах и чтобы тот же результат получался и по прошествии 50-ти лет. Более того, я опубликовал эти системы е подробным их описанием и предоставил их для общественного использования, так что никто не должен ничего платить за них. Разумеется, отдельные люди, предоставляющие дополнительный сервис, будут взимать плату за свои усилия, но самое главное здесь то, что с такими свободно распространяемыми системами автор получает возможность самостоятельно готовить книги, что раньше стоило ему чрезвычайно дорого.
Не могу отказать себе в удовольствии показать вам отрывки из книг, присланных мне недавно их авторами. Вот отрывок из книги чешского автора [СЛАЙД 21], набранный другим шрифтом, сделанным при помощи METAFONT. А этот образец из Эфиопии [слайд 22], в котором на амарском языке рассказывается, как пользоваться системой ТЁХ-
KapitolaXII2епё sluncem od£n£ drak sedmihla- vy syna seZrati cht^l, 7. ale Michal archandgl, draka pfemohl. 10. Nad ntmZ affkoli ufastnici Berdnkovi svitS* zili, 11.1 Zena uletSla. 16. a zem5 Геки vyplla. 17. vSak ten drak proti ostatkfim svatych bojovatl nepfestdvd.I ukdzal se div veliky na nebi: Zena od£- nd sluncem, pod jejimif nohama byl m6 sic, a na jejif hlavg byla koruna dvandcti hv^zd. 2 A jsouci t^hotnd, kfifela, pracu- jici ku porodu a trdpgci se, aby porodila. 3 I vidin jest jiny div na nebi. Nebo aj, drakСлайд 21.KapitolaXII2епё sluncem od£n£ drak sedmihla- vy syna seZrati cht^l, 7. ale Michal archandgl, draka pfemohl. 10. Nad ntmZ affkoli ufastnici Berdnkovi svitS* zili, 11.1 Zena uletSla. 16. a zem5 Геки vyplla. 17. vSak ten drak proti ostatkfim svatych bojovatl nepfestdvd.I ukdzal se div veliky na nebi: Zena od£- nd sluncem, pod jejimif nohama byl m6 sic, a na jejif hlavg byla koruna dvandcti hv^zd. 2 A jsouci t^hotnd, kfifela, pracu- jici ku porodu a trdpgci se, aby porodila. 3 I vidin jest jiny div na nebi. Nebo aj, drakСлайд 21.4.8.2 тАП7°tmMC АТ ГУЛА ЬАА тЛЛ fHWfr е-71* *е-ОААА » Mtfhft l>* ГтМГ hAA -МО4^. >»• и АП* тАЛУ hAA ®-ЛТ ГУТЛЭП®- л» )®- II ПЛАТ Л1Р1С ГлзАЛУ —Г*С ЛУЗ? Л—АЛА «4.8.2 тАП7°tmMC АТ ГУЛА ЬАА тЛЛ fHWfr е-71* *е-ОААА » Mtfhft l>* ГтМГ hAA -МО4^. >»• и АП* тАЛУ hAA ®-ЛТ ГУТЛЭП®- л» )®- II ПЛАТ Л1Р1С ГлзАЛУ —Г*С ЛУЗ? Л—АЛА «\КУС{тАЛУ) [tbhp]Л3тг\к\ M.U АЗЛА ::\-1Л-х{ <ГЛ3т<:\-н\ АУ M.U ЛЮА>}\ЛЛУ(*АС)\яьСЛ(тАЛУ}\КУС{тАЛУ) [tbhp]Л3тг\к\ M.U АЗЛА ::\-1Л-х{ <ГЛ3т<:\-н\ АУ M.U ЛЮА>}\ЛЛУ(*АС)\яьСЛ(тАЛУ}Пе-Ad* Г МАЛ- е-ГФС ИУЛА АШУ == \-3A-K ЛЗЛУАА МАА тААУЗ ФА ФТСТ АУ Ае-АГУ Л^-ЧА » М3» ПАЛ \-3A-K 03* тААУ МАА ®-ЛТ —Ь*Ф Г-ГАФЛ >®- Л ЛА Л г AAMlH *dA » ПС оз -глог »»г/ амп -ос* тз** itz/t m*i*®- /«-тка ==Пе-Ad* Г МАЛ- е-ГФС ИУЛА АШУ == \-3A-K ЛЗЛУАА МАА тААУЗ ФА ФТСТ АУ Ае-АГУ Л^-ЧА » М3» ПАЛ \-3A-K 03* тААУ МАА ®-ЛТ —Ь*Ф Г-ГАФЛ >®- Л ЛА Л г AAMlH *dA » ПС оз -глог »»г/ амп -ос* тз** itz/t m*i*®- /«-тка ==Слайд 22.Слайд 22.я возвращается e горизонтальную моду для продолжения абзаца (Формула, которая выделяется, должна оканчиваться $$ ) Например, предположим вычисло $$\pi Харргок 3.1415926536$$ является валныж.TfcX между двумя S3 переходит в выделенную математическую моду и результат. который вы получите, утверждает, что числоя*3 1415926536является важнымКогда ТуС находится в вертикальной или внутренней вертикальной моде, он игнорирует пробелы и пустые строки (или командные последовательности \раг). так что вам не надо беспокоиться, что такие вещи могут хзменить моду или повлиять на создаваемый документ Командный пробел (\и) будет, однако, рассматриваться как начало абзаца; абзац начнется с вробела после отступа.Обычно л/чше окончить всю работу, поставив в конце рукописи ТуС'а \Ъув, что является сокращением для \vtill\ojoct\ond. Командная последовательность \vf ill переводит Т£Х в вертикальную моду и вставляетСлайд 23.я возвращается e горизонтальную моду для продолжения абзаца (Формула, которая выделяется, должна оканчиваться $$ ) Например, предположим вычисло $$\pi Харргок 3.1415926536$$ является валныж.TfcX между двумя S3 переходит в выделенную математическую моду и результат. который вы получите, утверждает, что числоя*3 1415926536является важнымКогда ТуС находится в вертикальной или внутренней вертикальной моде, он игнорирует пробелы и пустые строки (или командные последовательности \раг). так что вам не надо беспокоиться, что такие вещи могут хзменить моду или повлиять на создаваемый документ Командный пробел (\и) будет, однако, рассматриваться как начало абзаца; абзац начнется с вробела после отступа.Обычно л/чше окончить всю работу, поставив в конце рукописи ТуС'а \Ъув, что является сокращением для \vtill\ojoct\ond. Командная последовательность \vf ill переводит Т£Х в вертикальную моду и вставляетСлайд 23.ояаоН') шягб/сАклт-е- учйыпхиъ ^чл^и их«ь ахи, »»-C«b^xv-4HHH4 ^4v4. *tt«. K.<Dt ilCAAti к.the nunher $$\pi \approx 3.14159265363$ is inportant.туси, зз -свзпАйхяаягьга. тч яуь-faxt- к<«*). ж«х 5 <, Г the numberя =3 1415926536ояаоН') шягб/сАклт-е- учйыпхиъ ^чл^и их«ь ахи, »»-C«b^xv-4HHH4 ^4v4. *tt«. K.<Dt ilCAAti к.the nunher $$\pi \approx 3.14159265363$ is inportant.туси, зз -свзпАйхяаягьга. тч яуь-faxt- к<«*). ж«х 5 <, Г the numberя =3 1415926536is important J к Я i НХШЛ 4 Н 6.тус <:$ДХ- К. $*НЯЯ»ЙХ- V^k ЗИ. 5Й4-ЭН (i/tH \раг » 5) L/td-oX. 564-Sfi^t- Ft-eei k.S, Ерв1гпЬ4<к:Р1<.ЖЯ<:»Ь»>ГИ»и<.4<.веФЬ.£.$И»у. L<-L. ЗУ У п-iU • X^-X (\u) И. ЙЯ«46 3 9t*»iHXLi5. tOUgOS ЖИ. /УГУ Ь^Н-эЛ». $ЙХ161^ХУЬС 4<»Ь.is important J к Я i НХШЛ 4 Н 6.тус <:$ДХ- К. $*НЯЯ»ЙХ- V^k ЗИ. 5Й4-ЭН (i/tH \раг » 5) L/td-oX. 564-Sfi^t- Ft-eei k.S, Ерв1гпЬ4<к:Р1<.ЖЯ<:»Ь»>ГИ»и<.4<.веФЬ.£.$И»у. L<-L. ЗУ У п-iU • X^-X (\u) И. ЙЯ«46 3 9t*»iHXLi5. tOUgOS ЖИ. /УГУ Ь^Н-эЛ». $ЙХ161^ХУЬС 4<»Ь.тус <оекш<окъ9х-и. \ьув к Kt)ix-fr'<xtvms*bm;.-ieithx-*>Ь. \Ьув »»И plain тус <$»4 HXfc 9. \vfill\ojoct\snd ««ИЖГ*тус <оекш<окъ9х-и. \ьув к Kt)ix-fr'<xtvms*bm;.-ieithx-*>Ь. \Ьув »»И plain тус <$»4 HXfc 9. \vfill\ojoct\snd ««ИЖГ*Слайд 24.Слайд 24.
KapitolaXII
2епё sluncem od£n£ drak sedmihla- vy syna seZrati cht^l, 7. ale Michal archandgl, draka pfemohl. 10. Nad ntmZ affkoli ufastnici Berdnkovi svitS* zili, 11.1 Zena uletSla. 16. a zem5 Геки vyplla. 17. vSak ten drak proti ostatkfim svatych bojovatl nepfestdvd.
I ukdzal se div veliky na nebi: Zena od£- nd sluncem, pod jejimif nohama byl m6 sic, a na jejif hlavg byla koruna dvandcti hv^zd. 2 A jsouci t^hotnd, kfifela, pracu- jici ku porodu a trdpgci se, aby porodila. 3 I vidin jest jiny div na nebi. Nebo aj, drak
Слайд 21.
KapitolaXII
2епё sluncem od£n£ drak sedmihla- vy syna seZrati cht^l, 7. ale Michal archandgl, draka pfemohl. 10. Nad ntmZ affkoli ufastnici Berdnkovi svitS* zili, 11.1 Zena uletSla. 16. a zem5 Геки vyplla. 17. vSak ten drak proti ostatkfim svatych bojovatl nepfestdvd.
I ukdzal se div veliky na nebi: Zena od£- nd sluncem, pod jejimif nohama byl m6 sic, a na jejif hlavg byla koruna dvandcti hv^zd. 2 A jsouci t^hotnd, kfifela, pracu- jici ku porodu a trdpgci se, aby porodila. 3 I vidin jest jiny div na nebi. Nebo aj, drak
Слайд 21.
4.8.2 тАП7°
tmMC АТ ГУЛА ЬАА тЛЛ fHWfr е-71* *е-ОААА » Mtfhft l>* ГтМГ hAA -МО4^. >»• и АП* тАЛУ hAA ®-ЛТ ГУТЛЭП®- л» )®- II ПЛАТ Л1Р1С ГлзАЛУ —Г*С ЛУЗ? Л—АЛА «
4.8.2 тАП7°
tmMC АТ ГУЛА ЬАА тЛЛ fHWfr е-71* *е-ОААА » Mtfhft l>* ГтМГ hAA -МО4^. >»• и АП* тАЛУ hAA ®-ЛТ ГУТЛЭП®- л» )®- II ПЛАТ Л1Р1С ГлзАЛУ —Г*С ЛУЗ? Л—АЛА «
\КУС{тАЛУ) [tbhp]
Л3тг\к\ M.U АЗЛА ::
\-1Л-х{ <ГЛ3т<:\-н\ АУ M.U ЛЮА>}
\ЛЛУ(*АС)
\яьСЛ(тАЛУ}
\КУС{тАЛУ) [tbhp]
Л3тг\к\ M.U АЗЛА ::
\-1Л-х{ <ГЛ3т<:\-н\ АУ M.U ЛЮА>}
\ЛЛУ(*АС)
\яьСЛ(тАЛУ}
Пе-Ad* Г МАЛ- е-ГФС ИУЛА АШУ == \-3A-K ЛЗЛУАА МАА тААУЗ ФА ФТСТ АУ Ае-АГУ Л^-ЧА » М3» ПАЛ \-3A-K 03* тААУ МАА ®-ЛТ —Ь*Ф Г-ГАФЛ >®- Л ЛА Л г AAMlH *dA » ПС оз -глог »»г/ амп -ос* тз** itz/t m*i*®- /«-тка ==
Пе-Ad* Г МАЛ- е-ГФС ИУЛА АШУ == \-3A-K ЛЗЛУАА МАА тААУЗ ФА ФТСТ АУ Ае-АГУ Л^-ЧА » М3» ПАЛ \-3A-K 03* тААУ МАА ®-ЛТ —Ь*Ф Г-ГАФЛ >®- Л ЛА Л г AAMlH *dA » ПС оз -глог »»г/ амп -ос* тз** itz/t m*i*®- /«-тка ==
Слайд 22.
Слайд 22.
я возвращается e горизонтальную моду для продолжения абзаца (Формула, которая выделяется, должна оканчиваться $$ ) Например, предположим вы
число $$\pi Харргок 3.1415926536$$ является валныж.
TfcX между двумя S3 переходит в выделенную математическую моду и результат. который вы получите, утверждает, что число
я*3 1415926536
является важным
Когда ТуС находится в вертикальной или внутренней вертикальной моде, он игнорирует пробелы и пустые строки (или командные последовательности \раг). так что вам не надо беспокоиться, что такие вещи могут хзменить моду или повлиять на создаваемый документ Командный пробел (\и) будет, однако, рассматриваться как начало абзаца; абзац начнется с вробела после отступа.
Обычно л/чше окончить всю работу, поставив в конце рукописи ТуС'а \Ъув, что является сокращением для \vtill\ojoct\ond. Командная последовательность \vf ill переводит Т£Х в вертикальную моду и вставляет
Слайд 23.
я возвращается e горизонтальную моду для продолжения абзаца (Формула, которая выделяется, должна оканчиваться $$ ) Например, предположим вы
число $$\pi Харргок 3.1415926536$$ является валныж.
TfcX между двумя S3 переходит в выделенную математическую моду и результат. который вы получите, утверждает, что число
я*3 1415926536
является важным
Когда ТуС находится в вертикальной или внутренней вертикальной моде, он игнорирует пробелы и пустые строки (или командные последовательности \раг). так что вам не надо беспокоиться, что такие вещи могут хзменить моду или повлиять на создаваемый документ Командный пробел (\и) будет, однако, рассматриваться как начало абзаца; абзац начнется с вробела после отступа.
Обычно л/чше окончить всю работу, поставив в конце рукописи ТуС'а \Ъув, что является сокращением для \vtill\ojoct\ond. Командная последовательность \vf ill переводит Т£Х в вертикальную моду и вставляет
Слайд 23.
ояаоН') шягб/сАклт-е- учйыпхиъ ^чл^и их«ь ахи, »»-C«b^xv-4HHH4 ^4v4. *tt«. K.<Dt ilCAAti к.
the nunher $$\pi \approx 3.14159265363$ is inportant.
туси, зз -свзпАйхяаягьга. тч яуь-faxt- к<«*). ж«х 5 <, Г the number
я =3 1415926536
ояаоН') шягб/сАклт-е- учйыпхиъ ^чл^и их«ь ахи, »»-C«b^xv-4HHH4 ^4v4. *tt«. K.<Dt ilCAAti к.
the nunher $$\pi \approx 3.14159265363$ is inportant.
туси, зз -свзпАйхяаягьга. тч яуь-faxt- к<«*). ж«х 5 <, Г the number
я =3 1415926536
is important J к Я i НХШЛ 4 Н 6.
тус <:$ДХ- К. $*НЯЯ»ЙХ- V^k ЗИ. 5Й4-ЭН (i/tH \раг » 5) L/td-oX. 564-Sfi^t- Ft-eei k.S, Ерв1гпЬ4<к:Р1<.ЖЯ<:»Ь»>ГИ»и<.4<.веФЬ.£.$И»у. L<-L. ЗУ У п-iU • X^-X (\u) И. ЙЯ«46 3 9t*»iHXLi5. tOUgOS ЖИ. /УГУ Ь^Н-эЛ». $ЙХ161^ХУЬС 4<»Ь.
is important J к Я i НХШЛ 4 Н 6.
тус <:$ДХ- К. $*НЯЯ»ЙХ- V^k ЗИ. 5Й4-ЭН (i/tH \раг » 5) L/td-oX. 564-Sfi^t- Ft-eei k.S, Ерв1гпЬ4<к:Р1<.ЖЯ<:»Ь»>ГИ»и<.4<.веФЬ.£.$И»у. L<-L. ЗУ У п-iU • X^-X (\u) И. ЙЯ«46 3 9t*»iHXLi5. tOUgOS ЖИ. /УГУ Ь^Н-эЛ». $ЙХ161^ХУЬС 4<»Ь.
тус <оекш<окъ9х-и. \ьув к Kt)ix-fr'<xtvms*bm;.-ieithx-*>
Ь. \Ьув »»И plain тус <$»4 HXfc 9. \vfill\ojoct\snd ««ИЖГ*
тус <оекш<окъ9х-и. \ьув к Kt)ix-fr'<xtvms*bm;.-ieithx-*>
Ь. \Ьув »»И plain тус <$»4 HXfc 9. \vfill\ojoct\snd ««ИЖГ*
Слайд 24.
Слайд 24.
Здесь вы видите [слайд 23] фрагмент русского перевода моей книги по системе ТЁК, а здесь [слайд 24] тот же отрывок в переводе на японский. Тем не менее, если бы я жил в Японии, уверен, что никогда бы не пришел к мысли о создании ТЁ^’а или METAFONT’a, потому что не чувствовал в этом необходимости: типографские стандарты в этой стране никогда не приходили в такой упадок как в Америке и Европе. Тем не менее я чрезвычайно рад, что ТЁХ теперь широко используется и для японских изданий.
Разные люди шлют мне много замечательных книг, которые, вероятно, никогда бы не были изданы без ТЁХ’а и METAFONT’a. Мои любимые примеры — это школьные пособия, как вот этот двуязычный текст народной сказки эскимосов [слайд 25]. Здесь двуязычные примечания на греческом и латинском из одной критической работы [слайд 26]; это тексты на арабском [слайд 27] и санскрите [слайд 28].
Начав работу над ТЁХ’ом в 1977 г., я регистрировал все найденные ошибки, крупные и мелкие, которые были найдены и удалены из программы с помощью добровольных помощников со всего света. Этот список на сегодняшний день состоит из 1 276 записей. Возможно поэтому Т^Х стал одной из наиболее тщательно проверенных программ, которые когда бы то ни было были написаны. Я бы хотел завершить свое повествование цитатой из моего любимого сборника стихотворений датского поэта Пита Хейна. Он называет свои стихи «grook»—датский вариант хайку. Нам с женой это произведение настолько
14. Aklaq iniqtuqami kayuqtuqsklsqiniqtuq-(n)smikayuqtuqbearbe tired-Jr.COND/Rfox14. Aklaq iniqtuqami kayuqtuqsklsqiniqtuq-(n)smikayuqtuqbearbe tired-Jr.COND/Rfoxapiqsrugaluagaqnigaaqakuguapiq«ruq-(k)aluaq-(t)aq-niq -(k)aa qakuguask .alr/aher.H*BEVlD.3s3s.lND whea/Uapiqsrugaluagaqnigaaqakuguapiq«ruq-(k)aluaq-(t)aq-niq -(k)aa qakuguask .alr/aher.H*BEVlD.3s3s.lND whea/U16 “Maatnugumaatnugu16 “Maatnugumaatnuguqalukpauragukkuvich qaluk-(q)pauraq-(»)uk -(k)uvich fish very big .DESID.2s.COND/Uqalukpauragukkuvich qaluk-(q)pauraq-(»)uk -(k)uvich fish very big .DESID.2s.COND/UСлайд 25.Слайд 25.freeze /11. bear / he let him lit down / at the hole / he dipping it / bis tail / 12. bear / he began to sit / that evening / 13. fox / he started to watch him / he not wanting him to pull it out too soon / his tail /14. bear / when he was tired / fox / he asked him repeatedly / when / (when) he was going to pull it out / his tail / 15. fox / he answered him often / 16. wait!freeze /11. bear / he let him lit down / at the hole / he dipping it / bis tail / 12. bear / he began to sit / that evening / 13. fox / he started to watch him / he not wanting him to pull it out too soon / his tail /14. bear / when he was tired / fox / he asked him repeatedly / when / (when) he was going to pull it out / his tail / 15. fox / he answered him often / 16. wait!out ad ipsum in unoyuo^ue dipne mtelliyuntw, | R, 264' | sicut ipsa rtuelant; ФПЕ, ПТР, HNETMA (hoc est lux, ignis, spiritus).Haec, ut dixi, ab Epifanio tradita, ut quisquis interrogatus quae tria et quid unum in sancta trinitate debeat credere, Sana fide | J, Г ] respondere ualeat, aut ad fidem accedens sic erudiatur. Et mihi uide- 743* tur spiritum pro calore posuisse, quasi dixisset in similitudine: lux, ignis, color. Haec enim tria unius essentiae sunt. Sed cur lucem primo dixit, non est minim. Nam et pater lux est et ignis et calor; et filius est lux, ignis, calor; et spiritus sanctus lux, ignis, calor. Illuminat enim pater, illuminat filius, illuminat spiritus sanctus: ex ipsis enim 30 omnis scientia et sapientia donatur.11-19 Maith 11, 27 22 EPIPHANIVS. Ancorotw 17, PG 43, 137C-14OA; CCS 25, p. 82. 2-12out ad ipsum in unoyuo^ue dipne mtelliyuntw, | R, 264' | sicut ipsa rtuelant; ФПЕ, ПТР, HNETMA (hoc est lux, ignis, spiritus).Haec, ut dixi, ab Epifanio tradita, ut quisquis interrogatus quae tria et quid unum in sancta trinitate debeat credere, Sana fide | J, Г ] respondere ualeat, aut ad fidem accedens sic erudiatur. Et mihi uide- 743* tur spiritum pro calore posuisse, quasi dixisset in similitudine: lux, ignis, color. Haec enim tria unius essentiae sunt. Sed cur lucem primo dixit, non est minim. Nam et pater lux est et ignis et calor; et filius est lux, ignis, calor; et spiritus sanctus lux, ignis, calor. Illuminat enim pater, illuminat filius, illuminat spiritus sanctus: ex ipsis enim 30 omnis scientia et sapientia donatur.11-19 Maith 11, 27 22 EPIPHANIVS. Ancorotw 17, PG 43, 137C-14OA; CCS 25, p. 82. 2-121 incipit... ПЕР1*ТЕЕПН| rm R, incipit quartur M 2 ANAKE*AA1OEIE| FJP, kg* Avaxcqakaiwviq 2 physiologies | phiriologise P, phyreologiee R 3 quod| p. nature tramp MR 3 ТПЕРОТЕ1АДЕЕ | codd. Vtrum unep. ouoiwlqq (hoc eat luperersentiahs) nature cum CM* (g.UO) an bMpouoilrqq (hoc «I supereMsntialis netura) cum Pforr (PL 1S8.741C) mUliig*n4um nt, emhgitur 8 OMOTEIOE| codd., kg* Ipoowiioq 9 et| R'. от. RPСлайд 26.1 incipit... ПЕР1*ТЕЕПН| rm R, incipit quartur M 2 ANAKE*AA1OEIE| FJP, kg* Avaxcqakaiwviq 2 physiologies | phiriologise P, phyreologiee R 3 quod| p. nature tramp MR 3 ТПЕРОТЕ1АДЕЕ | codd. Vtrum unep. ouoiwlqq (hoc eat luperersentiahs) nature cum CM* (g.UO) an bMpouoilrqq (hoc «I supereMsntialis netura) cum Pforr (PL 1S8.741C) mUliig*n4um nt, emhgitur 8 OMOTEIOE| codd., kg* Ipoowiioq 9 et| R'. от. RPСлайд 26.• From V, iobOT ^J ^^ Ы1Г ^ J ^.LnT vtd* I Tunis 1409 И, ISM AD p. 199 It The text is reprudvced twice ее pp. 199-200 **d pp 2OI«2O2, ie iUfhtly different гегаима.• From V, iobOT ^J ^^ Ы1Г ^ J ^.LnT vtd* I Tunis 1409 И, ISM AD p. 199 It The text is reprudvced twice ее pp. 199-200 **d pp 2OI«2O2, ie iUfhtly different гегаима.« eiiprytfTvrd' simmbtr: i зтяФгйетхтч’ x nfewf^r i so anhrg^ чрп^ит^г fvr^ Hfir 4£XiT wdfh РгЛпгпЬ а<4НччГ< ч^ч^чГчГа ч ^тт sfbrnrr чЧч^у чч1к i х я нпгрчхг^г^тчтг MNvr^rrv fan М fa i мнг хтчптт 4VT ЯПгГI tTfe^hr чГч^Ч I X Ч|^*Ч^ I яуГч 4»qd fnnfr uiylfa । x mid fir i ч№Т i (tk rRr xx ftrftr^nt I fx^Tt Ч" чГ«Га I ss ччгрКггсрчг s^t (хтпгг^чг w чч^г i йч дтчт x 4Rr«rRr i« eiiprytfTvrd' simmbtr: i зтяФгйетхтч’ x nfewf^r i so anhrg^ чрп^ит^г fvr^ Hfir 4£XiT wdfh РгЛпгпЬ а<4НччГ< ч^ч^чГчГа ч ^тт sfbrnrr чЧч^у чч1к i х я нпгрчхг^г^тчтг MNvr^rrv fan М fa i мнг хтчптт 4VT ЯПгГI tTfe^hr чГч^Ч I X Ч|^*Ч^ I яуГч 4»qd fnnfr uiylfa । x mid fir i ч№Т i (tk rRr xx ftrftr^nt I fx^Tt Ч" чГ«Га I ss ччгрКггсрчг s^t (хтпгг^чг w чч^г i йч дтчт x 4Rr«rRr ii yi^ji! oCtn, mi ,p; t^ it.^l /1,;^ ;i, *i . e,',^i«/ <^, >1 « Дь>И1 l jipr 101 1 J^I : j^ О *• • • »#£• .| »y3b Ml 1.2*J ,| .. ^«-i..i yi^ji! oCtn, mi ,p; t^ it.^l /1,;^ ;i, *i . e,',^i«/ <^, >1 « Дь>И1 l jipr 101 1 J^I : j^ О *• • • »#£• .| »y3b Ml 1.2*J ,| .. ^«-i..23 gfriT om. fl | Tjpr J (an ему confusion in Stradi) Ц ЯПЦТ* Pi a c. 21 wantwrifa 2T NTITN#om. о || tmi (.4.) lit» В . VnrfWT'WfV» Pl : WTgr* J (ie. OOI. wfv) II ‘SrfXT P, a.c. I |<fir NT P| fl) I few : friT codd. (a very ему confusion in Sired*) : fc# Pi a c. || few* : TVW* codd 28 1(TX '• Ч^Х Pi P (e and T are not alike in Stradl, hut Г and T arc. So if an original Г were traMliterated м T and the Mndhi then regularised, the MS readinp would be accounted for) : ЧТТ J 29 «Igynt, В, Pi a c. II wfgNglfrtT J l^wWnr) II ru t • : рпт* J II •Nmmftfir Ahhyanxar23 gfriT om. fl | Tjpr J (an ему confusion in Stradi) Ц ЯПЦТ* Pi a c. 21 wantwrifa 2T NTITN#om. о || tmi (.4.) lit» В . VnrfWT'WfV» Pl : WTgr* J (ie. OOI. wfv) II ‘SrfXT P, a.c. I |<fir NT P| fl) I few : friT codd. (a very ему confusion in Sired*) : fc# Pi a c. || few* : TVW* codd 28 1(TX '• Ч^Х Pi P (e and T are not alike in Stradl, hut Г and T arc. So if an original Г were traMliterated м T and the Mndhi then regularised, the MS readinp would be accounted for) : ЧТТ J 29 «Igynt, В, Pi a c. II wfgNglfrtT J l^wWnr) II ru t • : рпт* J II •Nmmftfir AhhyanxarСлайд 28.Слайд 28.Слайд 27.
14. Aklaq iniqtuqami kayuqtuqsklsqiniqtuq-(n)smikayuqtuq
bearbe tired-Jr.COND/Rfox
14. Aklaq iniqtuqami kayuqtuqsklsqiniqtuq-(n)smikayuqtuq
bearbe tired-Jr.COND/Rfox
apiqsrugaluagaqnigaaqakugu
apiq«ruq-(k)aluaq-(t)aq-niq -(k)aa qakuguask .alr/aher.H*BEVlD.3s3s.lND whea/U
apiqsrugaluagaqnigaaqakugu
apiq«ruq-(k)aluaq-(t)aq-niq -(k)aa qakuguask .alr/aher.H*BEVlD.3s3s.lND whea/U
16 “Maatnugumaatnugu
16 “Maatnugumaatnugu
qalukpauragukkuvich qaluk-(q)pauraq-(»)uk -(k)uvich fish very big .DESID.2s.COND/U
qalukpauragukkuvich qaluk-(q)pauraq-(»)uk -(k)uvich fish very big .DESID.2s.COND/U
Слайд 25.
Слайд 25.
freeze /11. bear / he let him lit down / at the hole / he dipping it / bis tail / 12. bear / he began to sit / that evening / 13. fox / he started to watch him / he not wanting him to pull it out too soon / his tail /
14. bear / when he was tired / fox / he asked him repeatedly / when / (when) he was going to pull it out / his tail / 15. fox / he answered him often / 16. wait!
freeze /11. bear / he let him lit down / at the hole / he dipping it / bis tail / 12. bear / he began to sit / that evening / 13. fox / he started to watch him / he not wanting him to pull it out too soon / his tail /
14. bear / when he was tired / fox / he asked him repeatedly / when / (when) he was going to pull it out / his tail / 15. fox / he answered him often / 16. wait!
out ad ipsum in unoyuo^ue dipne mtelliyuntw, | R, 264' | sicut ipsa rtuelant; ФПЕ, ПТР, HNETMA (hoc est lux, ignis, spiritus).
Haec, ut dixi, ab Epifanio tradita, ut quisquis interrogatus quae tria et quid unum in sancta trinitate debeat credere, Sana fide | J, Г ] respondere ualeat, aut ad fidem accedens sic erudiatur. Et mihi uide- 743* tur spiritum pro calore posuisse, quasi dixisset in similitudine: lux, ignis, color. Haec enim tria unius essentiae sunt. Sed cur lucem primo dixit, non est minim. Nam et pater lux est et ignis et calor; et filius est lux, ignis, calor; et spiritus sanctus lux, ignis, calor. Illuminat enim pater, illuminat filius, illuminat spiritus sanctus: ex ipsis enim 30 omnis scientia et sapientia donatur.
11-19 Maith 11, 27 22 EPIPHANIVS. Ancorotw 17, PG 43, 137C-14OA; CCS 25, p. 82. 2-12
out ad ipsum in unoyuo^ue dipne mtelliyuntw, | R, 264' | sicut ipsa rtuelant; ФПЕ, ПТР, HNETMA (hoc est lux, ignis, spiritus).
Haec, ut dixi, ab Epifanio tradita, ut quisquis interrogatus quae tria et quid unum in sancta trinitate debeat credere, Sana fide | J, Г ] respondere ualeat, aut ad fidem accedens sic erudiatur. Et mihi uide- 743* tur spiritum pro calore posuisse, quasi dixisset in similitudine: lux, ignis, color. Haec enim tria unius essentiae sunt. Sed cur lucem primo dixit, non est minim. Nam et pater lux est et ignis et calor; et filius est lux, ignis, calor; et spiritus sanctus lux, ignis, calor. Illuminat enim pater, illuminat filius, illuminat spiritus sanctus: ex ipsis enim 30 omnis scientia et sapientia donatur.
11-19 Maith 11, 27 22 EPIPHANIVS. Ancorotw 17, PG 43, 137C-14OA; CCS 25, p. 82. 2-12
1 incipit... ПЕР1*ТЕЕПН| rm R, incipit quartur M 2 ANAKE*AA1OEIE| FJP, kg* Avaxcqakaiwviq 2 physiologies | phiriologise P, phyreologiee R 3 quod| p. nature tramp MR 3 ТПЕРОТЕ1АДЕЕ | codd. Vtrum unep. ouoiwlqq (hoc eat luperersentiahs) nature cum CM* (g.UO) an bMpouoilrqq (hoc «I supereMsntialis netura) cum Pforr (PL 1S8.741C) mUliig*n4um nt, emhgitur 8 OMOTEIOE| codd., kg* Ipoowiioq 9 et| R'. от. RP
Слайд 26.
1 incipit... ПЕР1*ТЕЕПН| rm R, incipit quartur M 2 ANAKE*AA1OEIE| FJP, kg* Avaxcqakaiwviq 2 physiologies | phiriologise P, phyreologiee R 3 quod| p. nature tramp MR 3 ТПЕРОТЕ1АДЕЕ | codd. Vtrum unep. ouoiwlqq (hoc eat luperersentiahs) nature cum CM* (g.UO) an bMpouoilrqq (hoc «I supereMsntialis netura) cum Pforr (PL 1S8.741C) mUliig*n4um nt, emhgitur 8 OMOTEIOE| codd., kg* Ipoowiioq 9 et| R'. от. RP
Слайд 26.
• From V, iobOT ^J ^^ Ы1Г ^ J ^.LnT vtd* I Tunis 1409 И, ISM AD p. 199 I
t The text is reprudvced twice ее pp. 199-200 **d pp 2OI«2O2, ie iUfhtly different гегаима.
• From V, iobOT ^J ^^ Ы1Г ^ J ^.LnT vtd* I Tunis 1409 И, ISM AD p. 199 I
t The text is reprudvced twice ее pp. 199-200 **d pp 2OI«2O2, ie iUfhtly different гегаима.
« eiiprytfTvrd' simmbtr: i зтяФгйетхтч’ x nfewf^r i so anhrg^ чрп^ит^г fvr^ Hfir 4£XiT wdfh РгЛпгпЬ а<4НччГ< ч^ч^чГчГа ч ^тт sfbrnrr чЧч^у чч1к i х я нпгрчхг^г^тчтг MNvr^rrv fan М fa i мнг хтчптт 4VT ЯПгГI tTfe^hr чГч^Ч I X Ч|^*Ч^ I яуГч 4»qd fnnfr uiylfa । x mid fir i ч№Т i (tk rRr xx ftrftr^nt I fx^Tt Ч" чГ«Га I ss ччгрКггсрчг s^t (хтпгг^чг w чч^г i йч дтчт x 4Rr«rRr i
« eiiprytfTvrd' simmbtr: i зтяФгйетхтч’ x nfewf^r i so anhrg^ чрп^ит^г fvr^ Hfir 4£XiT wdfh РгЛпгпЬ а<4НччГ< ч^ч^чГчГа ч ^тт sfbrnrr чЧч^у чч1к i х я нпгрчхг^г^тчтг MNvr^rrv fan М fa i мнг хтчптт 4VT ЯПгГI tTfe^hr чГч^Ч I X Ч|^*Ч^ I яуГч 4»qd fnnfr uiylfa । x mid fir i ч№Т i (tk rRr xx ftrftr^nt I fx^Tt Ч" чГ«Га I ss ччгрКггсрчг s^t (хтпгг^чг w чч^г i йч дтчт x 4Rr«rRr i
i yi^ji! oCtn, mi ,p; t^ it.^l /1,;^ ;i, *i . e,',^i«/ <^, >1 « Дь>И1 l jipr 101 1 J^I : j^ О *• • • »#£• .| »y3b Ml 1.2*J ,| .. ^«-i..
i yi^ji! oCtn, mi ,p; t^ it.^l /1,;^ ;i, *i . e,',^i«/ <^, >1 « Дь>И1 l jipr 101 1 J^I : j^ О *• • • »#£• .| »y3b Ml 1.2*J ,| .. ^«-i..
23 gfriT om. fl | Tjpr J (an ему confusion in Stradi) Ц ЯПЦТ* Pi a c. 21 wantwrifa 2T NTITN#om. о || tmi (.4.) lit» В . VnrfWT'WfV» Pl : WTgr* J (ie. OOI. wfv) II ‘SrfXT P, a.c. I |<fir NT P| fl) I few : friT codd. (a very ему confusion in Sired*) : fc# Pi a c. || few* : TVW* codd 28 1(TX '• Ч^Х Pi P (e and T are not alike in Stradl, hut Г and T arc. So if an original Г were traMliterated м T and the Mndhi then regularised, the MS readinp would be accounted for) : ЧТТ J 29 «Igynt, В, Pi a c. II wfgNglfrtT J l^wWnr) II ru t • : рпт* J II •Nmmftfir Ahhyanxar
23 gfriT om. fl | Tjpr J (an ему confusion in Stradi) Ц ЯПЦТ* Pi a c. 21 wantwrifa 2T NTITN#om. о || tmi (.4.) lit» В . VnrfWT'WfV» Pl : WTgr* J (ie. OOI. wfv) II ‘SrfXT P, a.c. I |<fir NT P| fl) I few : friT codd. (a very ему confusion in Sired*) : fc# Pi a c. || few* : TVW* codd 28 1(TX '• Ч^Х Pi P (e and T are not alike in Stradl, hut Г and T arc. So if an original Г were traMliterated м T and the Mndhi then regularised, the MS readinp would be accounted for) : ЧТТ J 29 «Igynt, В, Pi a c. II wfgNglfrtT J l^wWnr) II ru t • : рпт* J II •Nmmftfir Ahhyanxar
Слайд 28.
Слайд 28.
нравится, что мы заказали выгравировать его на шиферной доске и повесили у ворот дома [слайд 29]. Это выглядит так:
Совсем простые вехи
Путь к мудрости содержит: Огрехи да огрехи, но реже, реже, реже.
Пит Хейн
Слайд 29.
Источники иллюстраций
Слайд 0 дал мне один незнакомец примерно в 1980 г.; это нарисовано кем-то с инициалами «М. S.». Слайд 1 представляет собой газетную вырезку из The Milwaukee Journal (2 August 1942), page II-l. Слайды 2, 3 и 4 взяты из The Brimful Book, ред. Watty Piper, илл. G. & D. Hauman (New York: Platt and Munk, 1927). Для слайдов 6 и 7 были использованы
иллюстрации III и IV из книги Chaundy, Barrett, Batey The Printing of Mathematics (London: Oxford University Press, 1954). Слайд 9 представляет собой фрагмент с. 185 первого издания второго тома книги The Art of Computer Programming (Reading, Massachusetts: Addison- Wesley, 1969); слайд 13 —тот же фрагмент с. 202 второго издания (1981). Слайды 10 и 12 взяты из материалов SC 97 архива Станфордского университета. Слайды 14-18 представляют собой иллюстрации статьи «Lessons Learned from METAFONT», рис. 13h, 13i, 13f, 8 и 9 соответственно; см. гл. 17 настоящего издания. Слайды 19 и 20 изготовлены на основе рисунков статьи John D. Hobby, Gu Guoan «А Chinese Meta-Font», TUGboat 5 (1984), ПЭ- 136, рис. 7, 9. Слайд 21 взят из книги Albrecht Durer, Apokalypsa, перев. Michaela Hajkova (Prague: Volvox Globator, 1993), page 20. Слайд 22 —из книги ЛИП ПЛ^ ЛЛ^Я) [Abass Belay Alamnehe] ^ди^ X^^h: fn>^ H"!^^ <д><д>^Т [Book of elKfyC Document Preparation Guidelines] (Houston, Texas: EthiO Systems, 1993), pages 70-71. Слайд 23 —фрагмент с. 107 книги Дональд Е. Кнут. Все про Т^Х, перев. М. В. Лисина (Протвино: АО RDT^X, 1993); слайд 24 —тот же фрагмент с.123 книги Donald Е. Knuth Т^ХУ'у^ [The Tj^Xbook], перев. [Nobuo Saito] и ЛК^ЙРШ [Yoshiteru Sagiya] (Tokyo: ASCII Corporation, 1989). Слайд 25 взят из книги Jonathan Kew, Stephen McConnel Formatting Interlinear Text (Dallas, Texas: Summer Institute of Linguistics, 1990), page 71. Слайды 26-28 взяты из работы John Lavagnino, Dominik Wujastyk Critical Edition Typesetting: The EDMAC Format for Plain T^X (UK TEX Users Group, 1996), pages 94, 100, 101. И, наконец, слайд 29 основан на рисунке из работы David Kindersley, Lida Lopes Cardozois Letters Slate Cut: A Sequel, Plate 15 (Cambridge: Cardozo Kindersley Editions, 1990).
Математическая типография
[Гиббсовская лекция (Josiah Willard Gibbs Lecture), состоявшаяся при содействии Американского математического общества 4 января 1978 г Посвящена Дьёрдю Пойа по случаю его 90-летия. Первоначально опубликована в Bulletin of the American Mathematical Society (новая серия) 1 (March 1979), 337-372.]
Аннотация
Математические книги и журналы больше не выглядят так красиво, как это соответствовало бы их предназначению. Это объясняется не тем, что их содержание стало неудовлетворительного качества, а скорее тем, что соблюдение прежних хорошо отработанных типографских традиций теперь слишком дорого обходится. К счастью, выяснилось, что сама математика может решить эту проблему.
Первым шагом на этом пути является создание метода строгого описания математических рукописей таким образом, чтобы они легко обрабатывались машинами. Такой аккуратно построенный язык может быть быстро освоен авторами и их машинистками; имея рукопись, подготовленную таким образом, вы будете получать непосредственно с принтера высококачественную распечатку. При этом вмешательства человека не требуется вовсе или оно незначительно.
Второй шаг в решении проблемы состоит в применении классической математики для рисования самих букв и символов. Можно дать строгое определение формы буквы ‘а’, например, таким образом, чтобы из этого одного определения путем изменения лишь нескольких параметров можно было получать бесконечно много стилизаций ее начертания — полужирное, расширенное, рубленое, наклонное и т. д. Сделав то же самое для других букв и символов, мы получим математическое определение наборных шрифтов, которое можно будет использовать на всех машинах как сейчас, так и в будущем. Основное преимущество этого подхода состоит в том, что легко может быть добавлен любой новый символ, причем он автоматически будет соответствовать уже существующим.
X = ± VIS = ± V^add'^add"') there correspond two quadric forms each containii rameters. So much Hilbert states. In order to as known systems it will be convenient to use a mental cubic, due to Hesse.*Referred to an inflexional triangle, the equatio (3) a’ = a?, 4- z’ 4- ж’ 4- бтх,^ All conic polars accordingly have the form :(4) aa^ = (у,*? 4- y^ 4- y^ + ^(Уг^з “X = ± VIS = ± V^add'^add"') there correspond two quadric forms each containii rameters. So much Hilbert states. In order to as known systems it will be convenient to use a mental cubic, due to Hesse.*Referred to an inflexional triangle, the equatio (3) a’ = a?, 4- z’ 4- ж’ 4- бтх,^ All conic polars accordingly have the form :(4) aa^ = (у,*? 4- y^ 4- y^ + ^(Уг^з “Consider the functions F, — va (a = 1,2, • • • According to the theorem a polynomial P (v, analogous to those of P (x : yn), and P (0, x : since the latter is unique.The series of power series P (F ,x : yn) may be of x, y, and it can readily be seen that its coefl those of P (x : y„). It must, however, be f not, a set of numerical coefficients could be sei which P (F, x : yn) would not be identically Weierstrass’ theorem concerning the sum of an ir when the functions F and P (v, x : yn) are convConsider the functions F, — va (a = 1,2, • • • According to the theorem a polynomial P (v, analogous to those of P (x : yn), and P (0, x : since the latter is unique.The series of power series P (F ,x : yn) may be of x, y, and it can readily be seen that its coefl those of P (x : y„). It must, however, be f not, a set of numerical coefficients could be sei which P (F, x : yn) would not be identically Weierstrass’ theorem concerning the sum of an ir when the functions F and P (v, x : yn) are conv; I call this ineffective part of xt “innocuous"■ validate the fundamental proposition:Г(^)*(c) 1 which was proved above (P. 4) for effective vah ;ineffective part of xe is innocuous is clear: it, as:that the variation of xt does not take place in itD. 3. But this consideration leads to the defin-. , of x. By this I mean the collection of values wh; I call this ineffective part of xt “innocuous"■ validate the fundamental proposition:Г(^)*(c) 1 which was proved above (P. 4) for effective vah ;ineffective part of xe is innocuous is clear: it, as:that the variation of xt does not take place in itD. 3. But this consideration leads to the defin-. , of x. By this I mean the collection of values whsix planes yt-Vyk = 0, each counted three tim- 1 type У\ Уа — УзУ^ = 0, each counted twice..We have seen that any point on the line yx + : image in (X) the whole line X, +Х» = 0, X3 : in (y) meets the line in one point, its image sg co : the system sg has also the three lines of this typ i (d)12. Algebraic procedure. The plane cor ;and the vertex (1, 0, 0, 0) has the equation :pat а'а "VPia «з А*Раз эс4ISince (y) and (y') both satisfy this equation we ;six planes yt-Vyk = 0, each counted three tim- 1 type У\ Уа — УзУ^ = 0, each counted twice..We have seen that any point on the line yx + : image in (X) the whole line X, +Х» = 0, X3 : in (y) meets the line in one point, its image sg co : the system sg has also the three lines of this typ i (d)12. Algebraic procedure. The plane cor ;and the vertex (1, 0, 0, 0) has the equation :pat а'а "VPia «з А*Раз эс4ISince (y) and (y') both satisfy this equation we ;
X = ± VIS = ± V^add'^add"') there correspond two quadric forms each containii rameters. So much Hilbert states. In order to as known systems it will be convenient to use a mental cubic, due to Hesse.*
Referred to an inflexional triangle, the equatio (3) a’ = a?, 4- z’ 4- ж’ 4- бтх,^ All conic polars accordingly have the form :
(4) aa^ = (у,*? 4- y^ 4- y^ + ^(Уг^з “
X = ± VIS = ± V^add'^add"') there correspond two quadric forms each containii rameters. So much Hilbert states. In order to as known systems it will be convenient to use a mental cubic, due to Hesse.*
Referred to an inflexional triangle, the equatio (3) a’ = a?, 4- z’ 4- ж’ 4- бтх,^ All conic polars accordingly have the form :
(4) aa^ = (у,*? 4- y^ 4- y^ + ^(Уг^з “
Consider the functions F, — va (a = 1,2, • • • According to the theorem a polynomial P (v, analogous to those of P (x : yn), and P (0, x : since the latter is unique.
The series of power series P (F ,x : yn) may be of x, y, and it can readily be seen that its coefl those of P (x : y„). It must, however, be f not, a set of numerical coefficients could be sei which P (F, x : yn) would not be identically Weierstrass’ theorem concerning the sum of an ir when the functions F and P (v, x : yn) are conv
Consider the functions F, — va (a = 1,2, • • • According to the theorem a polynomial P (v, analogous to those of P (x : yn), and P (0, x : since the latter is unique.
The series of power series P (F ,x : yn) may be of x, y, and it can readily be seen that its coefl those of P (x : y„). It must, however, be f not, a set of numerical coefficients could be sei which P (F, x : yn) would not be identically Weierstrass’ theorem concerning the sum of an ir when the functions F and P (v, x : yn) are conv
; I call this ineffective part of xt “innocuous"
■ validate the fundamental proposition
:Г(^)*
(c) 1 which was proved above (P. 4) for effective vah ;ineffective part of xe is innocuous is clear: it, as
:that the variation of xt does not take place in it
D. 3. But this consideration leads to the defin-. , of x. By this I mean the collection of values wh
; I call this ineffective part of xt “innocuous"
■ validate the fundamental proposition
:Г(^)*
(c) 1 which was proved above (P. 4) for effective vah ;ineffective part of xe is innocuous is clear: it, as
:that the variation of xt does not take place in it
D. 3. But this consideration leads to the defin-. , of x. By this I mean the collection of values wh
six planes yt-Vyk = 0, each counted three tim- 1 type У\ Уа — УзУ^ = 0, each counted twice..
We have seen that any point on the line yx + : image in (X) the whole line X, +Х» = 0, X3 : in (y) meets the line in one point, its image sg co : the system sg has also the three lines of this typ i (d)
12. Algebraic procedure. The plane cor ;
and the vertex (1, 0, 0, 0) has the equation :
pat а'а "VPia «з А*Раз эс4I
Since (y) and (y') both satisfy this equation we ;
six planes yt-Vyk = 0, each counted three tim- 1 type У\ Уа — УзУ^ = 0, each counted twice..
We have seen that any point on the line yx + : image in (X) the whole line X, +Х» = 0, X3 : in (y) meets the line in one point, its image sg co : the system sg has also the three lines of this typ i (d)
12. Algebraic procedure. The plane cor ;
and the vertex (1, 0, 0, 0) has the equation :
pat а'а "VPia «з А*Раз эс4I
Since (y) and (y') both satisfy this equation we ;
Рис. 1. Стили полиграфического оформления журнала AMS Transactions; (а) 1 (1900), 2; (b) 13 (1912), 138; (с) 2 3 (1922), 216; (d) 2 5 (1923), 10.
Необходимо, разумеется, чтобы математически описанные буквы были красивы с точки зрения общепринятых эстетических норм. Какая самая изящная кривая может соединить заданную последовательность точек на плоскости? Этот вопрос приводит к интересной математической задаче и одно из ее решений основывается на новом семействе сплайнов: эти кривые позволяют построить превосходные типографские шрифты, с которыми автор предварительно экспериментировал. Приходим к выводу, что математический подход к шрифтовому дизайну не исключает художников, которые многие годы занимаются этим делом, а напротив, предоставляет им новый эффективный инструмент для работы.
Введение
Я буду рассказывать о проводимой мною работе, а не о завершенном исследовании; у меня не было специального намерения именно так построить свою сегодняшнюю лекцию, причиной послужило то обстоятельство, что я не смог к настоящему моменту получить работающую программу. И это замечательно, что у меня нет окончательного продукта, который я мог бы здесь продемонстрировать, потому что сам процесс математических исследований значительно интереснее, чем их результат. Я постараюсь объяснить в своей лекции, почему именно я так захвачен проектом, над которым в настоящее время работаю.
Моя лекция состоит из двух частей, что соответствует двум разным значениям, заложенным в ее название. Сначала я буду говорить о математической полиграфии в смысле полиграфии на службе у математики: целью здесь является предоставить математике эффективное средство коммуникации в виде возможности публикации математических статей и книг высокого полиграфического качества. Затем речь пойдет о математической полиграфии в смысле математики на службе у полиграфии. В этом случае мы увидим, как математические идеи могут продвинуть возможности искусства печати.
Предварительные примеры
Чтобы подготовить аудиторию для предстоящего разговора, я бы хотел сначала продемонстрировать несколько примеров, на которых вы могли бы соответствующим образом «настроить зрение», чтобы посмотреть на математику глазами полиграфиста. Эти примеры взяты из трудов Transactions of the American Mathematical Society, которые начали публиковаться в 1900 г.; к настоящему моменту вышло более 230 томов. Тома эти я взял в библиотеке и распределил их по классам эквивалентности в соответствии с различными стилями полиграфического оформления: два тома помещались в один и тот же класс тогда и только тогда, когда они были оформлены в одном и том же полиграфическом стиле. Оказалось, что можно выделить двенадцать различных стилей, и нам будет полезно кратко с ними ознакомиться.
Первый пример (рис. 1а) взят со с. 2 первого тома Transactions; я привожу совсем маленький фрагмент, чтобы дать вам возможность увидеть отдельные буквы и их расположение, а не отвлекаться на математическое содержание. Этот набор выглядит старомодным в основном потому, что прописные буквы и высокие строчные, наподобие h и к, почти вдвое выше других строчных букв, а этого почти не бывает в современном наборе. Обратите внимание, что в курсивной букве х два соседних штриха имеют в центре общий отрезок. Подстрочные и надстрочные индексы набраны слишком мелким размером шрифта.
Этот стиль использовался для томов Transactions с 1-го по 12-й, а также в первой 21 странице из т. 13. Затем, начиная с 22-й страницы т. 13 был введен обновленный шрифт (рис. 1Ь). В этом примере подстрочные индексы все еще слишком малы, а греческая а, к сожалению, почти неотличима от латинской курсивной а. Обратите внимание, что наборщик вставлял больший пробел до и после скобок, чем это принято теперь. В течение последующих нескольких лет пробелы внутри формул постепенно менялись, но сам набор по существу оставался тем же вплоть до т. 24 с одним исключением.
Этим исключением являлся т. 23, изданный в 1922 г. (рис. 1с), который по моему мнению имеет самый элегантный вид из всех трудов Transactions. Этот набор менее плотный, благодаря чему более удобочитаем. Курсивные буквы также были видоизменены, но не столь удачно, — ‘ж’, например, прежде была лучше, а нижняя часть буквы V’ имеет тенденцию непропечатывать- ся — но в основном у читателя, листающего том, складывается благоприятное впечатление. Такое качество, однако, обошлось не даром. Согласно текущему
• of systems of division algebras. The next syste ; of order ^q1 over F with the basal units iojbke ( :■ with an irreducible equation of degree f>q. three । rational functions 9^ and ^(f) with coefficie • iterative 9q^ of 9(1) is i, and likewise >pp(i') = i.(°) ; byi еЧИОМ'ИО] (4-0,1,?-lI The complete multiplication table of the uni > associative law from-r’ = g ,kp = y , kj-ajk , ;i =• of systems of division algebras. The next syste ; of order ^q1 over F with the basal units iojbke ( :■ with an irreducible equation of degree f>q. three । rational functions 9^ and ^(f) with coefficie • iterative 9q^ of 9(1) is i, and likewise >pp(i') = i.(°) ; byi еЧИОМ'ИО] (4-0,1,?-lI The complete multiplication table of the uni > associative law from-r’ = g ,kp = y , kj-ajk , ;i =z=e'ez° = (ewz®,...,ewz®)»0 ^ 0 ^ 2л,с C" is called a Reinhardt circular set if along w. e E also the set{zllzd = lz2|> k = l,2,...,n)bounded closed subset of C", unisolvent with respect The function b(z) being defined and lower semic<hw = ^.....hV^.Vo-C^,^.,-!z=e'ez° = (ewz®,...,ewz®)»0 ^ 0 ^ 2л,с C" is called a Reinhardt circular set if along w. e E also the set{zllzd = lz2|> k = l,2,...,n)bounded closed subset of C", unisolvent with respect The function b(z) being defined and lower semic<hw = ^.....hV^.Vo-C^,^.,-!(k)(k)0 = r*x(2r,a.) - (£/,a,)xr* - £ 1< This element is of lower length. It follows there X j i — 1, • • -, k. Hence, (a) yields that r. = Х.гь Xi ;; Now r^O, by the minimality of k, and 52x,< ) \ which we deduce that 2\«« = 0- But the a. a { . which is impossible since in particular X* = 1. ;Theorem 7. Let R be a dense ring of linear t. ; ■ F be a maximal commutative subfield D. If Rf ; ; tion of finite rank over F, then R contains als I0 = r*x(2r,a.) - (£/,a,)xr* - £ 1< This element is of lower length. It follows there X j i — 1, • • -, k. Hence, (a) yields that r. = Х.гь Xi ;; Now r^O, by the minimality of k, and 52x,< ) \ which we deduce that 2\«« = 0- But the a. a { . which is impossible since in particular X* = 1. ;Theorem 7. Let R be a dense ring of linear t. ; ■ F be a maximal commutative subfield D. If Rf ; ; tion of finite rank over F, then R contains als IThe set Nx is nowhere dense in Zx and thus N=pFor each £ e Y—N we must prove that /( satisfi be the unique projection in [Fd | d e D) such that ; the algebra (ErfEyPQ is finite and homogeneot . onal abelian projections Ex, E2,..., En such that (1 <J, k^n) be partial isometric operators in ^Es/ " (h)UjkUlm = 8mjUlk, where 8 is the Kronecker d<U* = Uki; andUh = Ehfor all 1 ^j, kj.m^ n. For each A in (Ej^E) • Po, t in 3TxP0 such thatThe set Nx is nowhere dense in Zx and thus N=pFor each £ e Y—N we must prove that /( satisfi be the unique projection in [Fd | d e D) such that ; the algebra (ErfEyPQ is finite and homogeneot . onal abelian projections Ex, E2,..., En such that (1 <J, k^n) be partial isometric operators in ^Es/ " (h)UjkUlm = 8mjUlk, where 8 is the Kronecker d<U* = Uki; andUh = Ehfor all 1 ^j, kj.m^ n. For each A in (Ej^E) • Po, t in 3TxP0 such thatThe algebra P is nearly simple if and only if theN is spanned by a, ■ ‘ ■ , an~X~ x, b^, - • - i, j = 1, • • • , k.Either n - к = char F with к even or nProof. By Theorem 5.5, there are elements a a. • • • , an-k~ 1, b^, • • • , b^. Furthermore, ab.= 1 for all i, j where each a^ X.. is in F. From th space of the space spanned by an-k,~ 1, b^, - - • ,Assume P is nearly simple. Then there is a show that each b. is in M. To do this, it is nec<The algebra P is nearly simple if and only if theN is spanned by a, ■ ‘ ■ , an~X~ x, b^, - • - i, j = 1, • • • , k.Either n - к = char F with к even or nProof. By Theorem 5.5, there are elements a a. • • • , an-k~ 1, b^, • • • , b^. Furthermore, ab.= 1 for all i, j where each a^ X.. is in F. From th space of the space spanned by an-k,~ 1, b^, - - • ,Assume P is nearly simple. Then there is a show that each b. is in M. To do this, it is nec<unctions in СЦ^Г) and haB, a, P E I as coordinate irmined by the respective bases chosen above. If a, .e function of /V is the minor of |gy-| determined by the columns /?(!),..., P(pY The coordinate ring of he haP together with l/det|Aa3|, while that of GL(W') ’,y together with l/det|gy|. The coordinate functions gu, so to show A' is a morphism it suffices to show nomial in gv and l/det|gy|. For this, the followingil character of GLfhK) is an integral power of theunctions in СЦ^Г) and haB, a, P E I as coordinate irmined by the respective bases chosen above. If a, .e function of /V is the minor of |gy-| determined by the columns /?(!),..., P(pY The coordinate ring of he haP together with l/det|Aa3|, while that of GL(W') ’,y together with l/det|gy|. The coordinate functions gu, so to show A' is a morphism it suffices to show nomial in gv and l/det|gy|. For this, the followingil character of GLfhK) is an integral power of the)f Q, i.e.y) = 0 for every x E A for which x(Q) = 0).'or тл is equivalent to the one induced by the)f Q, i.e.y) = 0 for every x E A for which x(Q) = 0).'or тл is equivalent to the one induced by thevhich Xj is the (last) minimum of Ух, let Y^.i > of Yx, and Tx the interjump times for Yx. So i such that Y^x = Tx == oo. Notice that Yq is fini t as e -> 0, Yq converges to Iх = inf,^. Let A, —oo, oo). Then, for example, if / > 1vhich Xj is the (last) minimum of Ух, let Y^.i > of Yx, and Tx the interjump times for Yx. So i such that Y^x = Tx == oo. Notice that Yq is fini t as e -> 0, Yq converges to Iх = inf,^. Let A, —oo, oo). Then, for example, if / > 1(|x(z)|: x €Л, Ы < 1 and x(w) = 0}.(|x(z)|: x €Л, Ы < 1 and x(w) = 0}.| B.Y^k-Y^eC,TQKtkeD.N>Q>i^| B.Y^k-Y^eC,TQKtkeD.N>Q>i^(1)(1); ipresent the open unit disk in the complex plane, C, . t polydisk in «-dimensional complex space C". Tn; -oundary of Dn, i.e.; ipresent the open unit disk in the complex plane, C, . t polydisk in «-dimensional complex space C". Tn; -oundary of Dn, i.e.[ T^i € B,Y^k - Y^ E C.T^ E D.N > Q = I[ T^i € B,Y^k - Y^ E C.T^ E D.N > Q = Ioo, a typical term in the summation of (3.5) may Ioo, a typical term in the summation of (3.5) may I
• of systems of division algebras. The next syste ; of order ^q1 over F with the basal units iojbke ( :■ with an irreducible equation of degree f>q. three । rational functions 9^ and ^(f) with coefficie • iterative 9q^ of 9(1) is i, and likewise >pp(i') = i.
(°) ; by
i еЧИОМ'ИО] (4-0,1,?-l
I The complete multiplication table of the uni > associative law from
-r’ = g ,kp = y , kj-ajk , ;i =
• of systems of division algebras. The next syste ; of order ^q1 over F with the basal units iojbke ( :■ with an irreducible equation of degree f>q. three । rational functions 9^ and ^(f) with coefficie • iterative 9q^ of 9(1) is i, and likewise >pp(i') = i.
(°) ; by
i еЧИОМ'ИО] (4-0,1,?-l
I The complete multiplication table of the uni > associative law from
-r’ = g ,kp = y , kj-ajk , ;i =
z=e'ez° = (ewz®,...,ewz®)»0 ^ 0 ^ 2л,
с C" is called a Reinhardt circular set if along w. e E also the set
{zllzd = lz2|> k = l,2,...,n)
bounded closed subset of C", unisolvent with respect The function b(z) being defined and lower semic<
hw = ^.....hV^.Vo-C^,^.,-!
z=e'ez° = (ewz®,...,ewz®)»0 ^ 0 ^ 2л,
с C" is called a Reinhardt circular set if along w. e E also the set
{zllzd = lz2|> k = l,2,...,n)
bounded closed subset of C", unisolvent with respect The function b(z) being defined and lower semic<
hw = ^.....hV^.Vo-C^,^.,-!
(k)
(k)
0 = r*x(2r,a.) - (£/,a,)xr* - £ 1
< This element is of lower length. It follows there X j i — 1, • • -, k. Hence, (a) yields that r. = Х.гь Xi ;
; Now r^O, by the minimality of k, and 52x,< ) \ which we deduce that 2\«« = 0- But the a. a { . which is impossible since in particular X* = 1. ;
Theorem 7. Let R be a dense ring of linear t. ; ■ F be a maximal commutative subfield D. If Rf ; ; tion of finite rank over F, then R contains als I
0 = r*x(2r,a.) - (£/,a,)xr* - £ 1
< This element is of lower length. It follows there X j i — 1, • • -, k. Hence, (a) yields that r. = Х.гь Xi ;
; Now r^O, by the minimality of k, and 52x,< ) \ which we deduce that 2\«« = 0- But the a. a { . which is impossible since in particular X* = 1. ;
Theorem 7. Let R be a dense ring of linear t. ; ■ F be a maximal commutative subfield D. If Rf ; ; tion of finite rank over F, then R contains als I
The set Nx is nowhere dense in Zx and thus N=p
For each £ e Y—N we must prove that /( satisfi be the unique projection in [Fd | d e D) such that ; the algebra (ErfEyPQ is finite and homogeneot . onal abelian projections Ex, E2,..., En such that (1 <J, k^n) be partial isometric operators in ^Es/ " (h)
UjkUlm = 8mjUlk, where 8 is the Kronecker d<
U* = Uki; and
Uh = Eh
for all 1 ^j, kj.m^ n. For each A in (Ej^E) • Po, t in 3TxP0 such that
The set Nx is nowhere dense in Zx and thus N=p
For each £ e Y—N we must prove that /( satisfi be the unique projection in [Fd | d e D) such that ; the algebra (ErfEyPQ is finite and homogeneot . onal abelian projections Ex, E2,..., En such that (1 <J, k^n) be partial isometric operators in ^Es/ " (h)
UjkUlm = 8mjUlk, where 8 is the Kronecker d<
U* = Uki; and
Uh = Eh
for all 1 ^j, kj.m^ n. For each A in (Ej^E) • Po, t in 3TxP0 such that
The algebra P is nearly simple if and only if the
N is spanned by a, ■ ‘ ■ , an~X~ x, b^, - • - i, j = 1, • • • , k.
Either n - к = char F with к even or n
Proof. By Theorem 5.5, there are elements a a. • • • , an-k~ 1, b^, • • • , b^. Furthermore, ab.= 1 for all i, j where each a^ X.. is in F. From th space of the space spanned by an-k,~ 1, b^, - - • ,
Assume P is nearly simple. Then there is a show that each b. is in M. To do this, it is nec<
The algebra P is nearly simple if and only if the
N is spanned by a, ■ ‘ ■ , an~X~ x, b^, - • - i, j = 1, • • • , k.
Either n - к = char F with к even or n
Proof. By Theorem 5.5, there are elements a a. • • • , an-k~ 1, b^, • • • , b^. Furthermore, ab.= 1 for all i, j where each a^ X.. is in F. From th space of the space spanned by an-k,~ 1, b^, - - • ,
Assume P is nearly simple. Then there is a show that each b. is in M. To do this, it is nec<
unctions in СЦ^Г) and haB, a, P E I as coordinate irmined by the respective bases chosen above. If a, .e function of /V is the minor of |gy-| determined by the columns /?(!),..., P(pY The coordinate ring of he haP together with l/det|Aa3|, while that of GL(W') ’,y together with l/det|gy|. The coordinate functions gu, so to show A' is a morphism it suffices to show nomial in gv and l/det|gy|. For this, the following
il character of GLfhK) is an integral power of the
unctions in СЦ^Г) and haB, a, P E I as coordinate irmined by the respective bases chosen above. If a, .e function of /V is the minor of |gy-| determined by the columns /?(!),..., P(pY The coordinate ring of he haP together with l/det|Aa3|, while that of GL(W') ’,y together with l/det|gy|. The coordinate functions gu, so to show A' is a morphism it suffices to show nomial in gv and l/det|gy|. For this, the following
il character of GLfhK) is an integral power of the
)f Q, i.e.
y) = 0 for every x E A for which x(Q) = 0).
'or тл is equivalent to the one induced by the
)f Q, i.e.
y) = 0 for every x E A for which x(Q) = 0).
'or тл is equivalent to the one induced by the
vhich Xj is the (last) minimum of Ух, let Y^.i > of Yx, and Tx the interjump times for Yx. So i such that Y^x = Tx == oo. Notice that Yq is fini t as e -> 0, Yq converges to Iх = inf,^. Let A, —oo, oo). Then, for example, if / > 1
vhich Xj is the (last) minimum of Ух, let Y^.i > of Yx, and Tx the interjump times for Yx. So i such that Y^x = Tx == oo. Notice that Yq is fini t as e -> 0, Yq converges to Iх = inf,^. Let A, —oo, oo). Then, for example, if / > 1
(|x(z)|: x €Л, Ы < 1 and x(w) = 0}.
(|x(z)|: x €Л, Ы < 1 and x(w) = 0}.
| B.Y^k-Y^eC,TQKtkeD.N>Q>i^
| B.Y^k-Y^eC,TQKtkeD.N>Q>i^
(1)
(1)
; ipresent the open unit disk in the complex plane, C, . t polydisk in «-dimensional complex space C". Tn
; -oundary of Dn, i.e.
; ipresent the open unit disk in the complex plane, C, . t polydisk in «-dimensional complex space C". Tn
; -oundary of Dn, i.e.
[ T^i € B,Y^k - Y^ E C.T^ E D.N > Q = I
[ T^i € B,Y^k - Y^ E C.T^ E D.N > Q = I
oo, a typical term in the summation of (3.5) may I
oo, a typical term in the summation of (3.5) may I
Рис. 1 [продолжение]. Выдержки из журналов Transactions of the American Mathematical Society, (e) 28 (1926), 207; (f) 105 (1962), 340; (g) 114 (1965), 216; (h) 125 (1966), 38; (i) 169 (1972), 232; (j) 179 (1973), 314; (k) 199 (1974), 370; (1) 226 (1977), 372.
отчету Американского математического общества в Bulletin [46, с. 100], этот том Transactions вышел в свет позже запланированного срока на 18 месяцев! Возможно поэтому матобщество решило подыскать другой способ печати.
Чтобы оценить последующие изменения, посмотрим на два фрагмента из Bulletin, касающиеся самой первой Гиббсовской лекции (рис. 2). Предварительное объявление, помещенное в 1923 г., набрано шрифтом, представленным на рис. lb, но ко времени самой лекции в 1924 г. рисунок букв изменился на более узкий и вытянутый вверх. Прописные буквы остались, грубо говоря, теми же, но строчные стали совершенно другими. Мы также можем заметить чрезмерно большие междусловные пробелы во многих строках, тогда как есть строки, где текст слишком зажат.
В том же стиле был оформлен т. 25 Transactions (рис. Id), который, между прочим, был набран в Германии для снижения полиграфических расходов. Буквы полужирного и курсивного начертания в этом примере очень хороши, — возврат к добротному старому рисунку х — так что математические формулы выглядят великолепно, тогда как шрифт сопровождающего их текста очень узок. К счастью, так оформлены всего три тома.
Новая эра Transactions началась в 1926 г., когда печать их осуществлялась в типографии Collegiate Press в Менаша, шт. Висконсин. Тома с 28-го по 104-й имеют одинаковое полиграфическое оформление (рис. 1е), что покрывает отрезок времени в 36 лет: с 1926 по 1961 гг. включительно; так же оформлялся и ежемесячник American Mathematical Monthly. Вообще говоря, шрифты были вполне приличными, но имелась и курьезная аномалия: подстрочные и надстрочные индексы в математических формулах набирались другим шрифтом, отличающимся по рисунку от основного! Обратите внимание, например, на буквы к в первой выключной формуле на рис. 1е: большая из них имеет петлю, что топологически отличает ее от меньшей. Аналогичная история с буквой р; р в кр совершенно отличается от р в р2. В этом примере отсутствуют буквы х, но если бы вы взглянули на другие страницы, то обнаружили бы х моего любимого рисунка только в индексах. Я не понимаю, почему на протяжении стольких лет допускалось такое несоответствие.
Другой период полиграфической неряшливости в Transactions начался с т. 105 в 1962 г. Этот том, набранный в Израиле, ввел переход на шрифт Times Roman (рис. If); простой способ быстро распознать отличие — взглянуть на букву о, поскольку ее штрихи (контур) изменили толщину в несколько наклоненном виде (О —вариант О); в ранее использованных шрифтах эта буква была симметрична относительно вертикальной оси, как если бы была нарисована пером, ориентированным по горизонтали, а в шрифте Times Roman явно виден наклонный нажим, как если бы она была нарисована пером с наклоном вправо. Обратите внимание, что три к топологически такие же, как и во второй выключной формуле, но по непонятной причине в индексах они имеют разный размер. Многие курсивные буквы из Times Italic несколько отличаются от тех, к которым привыкли читатели Transactions, и я лично думаю, что в этом шрифте была предпринята попытка сделать формульный шрифт более узким. Модификация шрифтов Times Roman и Times Italic не была вполне за-
THE JOSIAH WILLARD GIBBS LECTURESHIP
The Council of the Society has sanctioned the establishment of an honorary lectureship to be known as the Josiah Willard Gibbs Lectureship. The lectures are to be of a popular nature on topics in mathematics or its applications, and are to be given by invitation under the auspices of the Society. They will be held annually or at such intervals as the Council may direct. It is expected that the first lecture will be delivered in New York City during the winter of 1923-24, and a committee has been authorized to inaugurate the lectures by choosing the first speaker and making the necessary arrangements.
R. G. D. Richardson,
Secretary.
THE FIRST JOSIAH WILLARD GIBBS LECTURE
The first Josiah Willard Gibbs Lecture was delivered under the auspices of this Society on February 29, 1924, by Professor M. I. Pupin, of Columbia University, in the auditorium of the Engineering Societies’ Building, New York City. A large and distinguished audience was present, including, besides members of the Society, many physicists, chemists, and engineers who had been invited to attend.
In introducing the speaker, President Veblen spoke as follows:
“In instituting the Willard Gibbs Lectures, the American Mathematical Society has recognized the dual character of mathematics. On the one hand, mathematics is one of the essential emanations of the human spirit,—a thing to be valued in and for itself, like art or poetry. Gibbs made notable contributions to this side of mathematics in his
Рис. 2. Переходный период [из Bulletin of the American Mathematical Society 29 (1923), 385; 30 (1924), 289].
вершенной; курсивная буква д имела знакомые очертания, возможно потому, что новый ее вид показался бы математикам слишком экзотическим.
В том же стиле были выполнены все издания ст. 105 по т. 124, за исключением краткого перерыва: в тт. 114, 115 и 116 нажим в букве о имел симметрию относительно вертикали и буква к имела петлю (рис. 1g). Другое полиграфическое оформление использовалось в тт. 125-168 (рис. lh): Times Roman и Times Italic снова стали правилом даже для буквы g, за исключением верхних и нижних индексов (которые остались в милом моему сердцу стиле); например, сравните буквы j и к. Эти тома были набраны в Великобритании.
Резкое увеличение объемов публикаций, сопровождаемое повышением заработной платы высокопрофессионального персонала, сделало чрезвычайно невыгодными традиционные методы набора, и матобщество стало время от времени прибегать к экстравагантному способу: оригинал-макет готовился на пишущей машинке с последующим фотовоспроизведением его в типографии. Эти тяжелые обстоятельства и привели к тому, что тт. 169-198 Transactions стали выглядеть так, как на рис. li, за исключением тт. 179, 185, 189, 192, 194 и 198, которые были выполнены в значительно лучшем (хотя и не вполне удовлетворительном) стиле, что можно рассмотреть на рис. lj, глядя на курсивные g и на ‘ffi’. Текст на рис. lj был набран на компьютере при помощи разработанной Лауэллом Хокинсоном и Ричардом МакКиллином системы. Это был один из плодов исследовательского проекта Американского математического общества, поддержанного Национальным научным фондом [2-6].
Компьютерный набор математики несколько опережал тогда свое время, однако, так что другого рода «холодная копия» была задействована в тт. 199— 224: было использовано наборное устройство IBM Compositor для всех эти томов, за исключением тт. 208 и 211, для набора которых вновь обратились к устройству типа Веритайпер с рис. li. Новый алфавит на вид был несколько вытянутым вверх и некоторые слова были даже уже, чем другие (см.рис. 1k). По этой причине я, к моему сожалению, перестал посылать статьи в Американское математическое общество, так как окончательный продукт выглядел столь убого, что я не мог на него глядеть. Аналогичные колебания полиграфического качества вскоре проявились во всех точных науках, особенно в физике, где ситуация была еще тяжелее. (История издания трудов Американского общества инженеров гражданского строительства обсуждалась в интересной и информативной статье П. Париси [45].)
Сейчас дела, к счастью, пошли лучше. Начиная с т. 225, опубликованного в прошлом году, Transactions теперь выглядят как на рис. 11; как и на рис. lj, это компьютерный набор, в котором шрифт Times Roman стал теперь несколько крупнее. Я еще не занимался особым рисунком курсивных букв и здесь имеются некоторые шероховатости, которые надо будет сгладить, такие, как наложение строк, показанное в этом примере, но понятно, что положение улучшается и, возможно, настанет день, когда мы сможем вернуться к полиграфическому качеству тт. 23 и 24.
Набор при помощи компьютера
Возможно, основная причина улучшения ситуации кроется в том, что теперь компьютеры способны работать с текстом и преобразовывать его в вид, соответствующий полиграфическим требованиям. Опытные образцы такого рода систем использовались уже в начале 60-х годов (см. книгу Барнетта [10]), и теперь наступает пора их зрелости. В течение следующих примерно десяти лет, мне думается, типичную офисную пишущую машинку сменит телеэкран с клавиатурой и компактным процессором. Очень легко станет вносить исправления в рукопись, заменяя все вхождения одного выражения другим и делая другие коррективы, а затем можно будет перенести этот текст либо на экран, либо на принтер, либо на другой компьютер. Такие системы уже используются в большинстве газет, а в новых экспериментальных офисных системах для оформления текста имеются различные шрифты [26]. Должно пройти еще некоторое время и такие машины вытеснят традиционную технологию подготовки рукописей в университетах и технических лабораториях.
При обработке математических текстов уровень сложности, конечно, повышается. Полиграфисты относят математические работы к каторжным, и один из известнейших американских типографов Т. Л. Де Винн писал [17, с. 171], что «[даже] при самых благоприятных условиях алгебра будет причинять беспокойство». Проблема была в том, что в двухуровневых формулах приходилось особым образом исхитряться, чтобы вставить отдельные элементы металлического шрифта, но теперь эта задача существенно упростилась и двухуровневые формулы представляются линейными записями в виде последовательностей компьютерных команд.
Языки, использующие строковые выражения для представления математических формул, теперь хорошо известны: например, язык программирования FORTRAN, но чтобы можно было рассмотреть проблему полиграфического представления математики во всем объеме, нужен несколько иной подход. Для того чтобы вы в полной мере могли оценить достоинства языков для набора математики, я кратко ознакомлю вас с тремя известными мне достаточно разумными подходами. Первая система, которую я назову Туре С, представляет собой типичный коммерческий продукт, используемый в настоящее время для набора математических журналов (см. [12]). Второй, который я обозначу Туре В, был разработан в лаборатории Белла и использовался для подготовки нескольких книг и статей, в том числе и работы, посвященные самой системе [27]. Третий, который я обозначу Туре Т, представляет собой тот продукт, которым я в настоящее время занимаюсь в рамках общей системы под названием тех [29] J
На рис. 3 показано, как записываются три простые формулы в этих трех языках. Язык Туре С использует $f ... $s ... $t для дробей, *g для указа-
Т^Х не имеет никакого отношения ни к системе с похожим названием, анонсированной фирмой Honeywell Information Systems, ни к другой, разработанной Digital Research. В моем названии буквы Т, Е и X не латинские, а греческие, поэтому лого Т^Х произносится как «тех», созвучно греческому слову, обозначающему как искусство, так и технологию.
Формула
Туре С
Туре В
Type T
1
2
$fl$s2$t
1 over 2
1 \over 2
62
*gq"2
theta sup 2
\thetat2
Vf^
$rf(xi)$t
sqrt{f(x sub i )}
\sqrt{f(xli)}
Рис. 3. Три способа представления формул.
ния, что «следующая буква греческая», q для греческой буквы тета, $г... $t для квадратных корней, 11 для верхних индексов и 1 для нижних индексов. Язык Туре В более мнемоничен, он использует over (над), theta (тета), sup (верхний), sqrt (квадрат) и sub (нижний) вместе с фигурными скобками для группировки в случае необходимости. Аналогично устроен и язык Туре Т, но он не использует зарезервированных слов, вместо этого у него зарезервирована специальная литера \, которая проставляется перед любым нестандартным текстом. Это позволяет игнорировать пробелы, тогда как в языке Туре В их обязательно надо помещать справа; в приведенном примере, например, пробел после Ч’ важен, в противном случае ^(xiY превратится в ‘/(^г)’ в соответствии с правилами Туре В. Другая причина, по которой в Туре Т используется \, состоит в том, что процессору не нужно каждый раз сверять текст со словарем зарезервированных слов и убеждаться, что sup не означает supremum. Специальные литеры \ { } Т 4 в языке Туре Т при необходимости можно заменить любыми другими; эти пять значков не являются общепринятыми для наборщиков и машинисток, но они хорошо знакомы всем операторам вычислительных машин.
Порой компьютерный набор приносит нам и хорошие новости: мы теперь можем с легкостью получать квадратные корни в традиционном представлении со знаками радикала и подкоренным выражением. Теперь не придется писать ж1/2, если нам этого не нужно1.
Ни один из этих языков не позволяет читать сложные формулы так же легко, как при многоуровневом их расположении, но практика показывает, что необученный персонал довольно быстро «набивает руку» в этом деле. Согласно [12], «всего за несколько часов (в худшем случае — дней) машинистку, не имеющую никаких познаний ни в математике, ни в наборном деле, можно натренировать в наборе даже более сложных уравнений». А авторы языка Туре В в [27] говорят, что «период обучения короток. Через несколько минут уже можно получить общее представление, а работы с одной или двумя страницами обычно достаточно, чтобы разобраться в большей части особенностей набора». Таким образом, и машинисткам, и математикам не нужно прилагать особых усилий к изучению системы, чтобы научиться подготавливать статьи
на таком языке. Действительные трудности возникают только при оформлении таблиц или слишком вычурном выравнивании формул.
Как только подобные системы станут широкодоступными, авторы смогут, готовя свои статьи, сразу видеть их в таком же точно виде, как они выглядят при подготовке в типографии. Все авторы математических статей знают, что их намерения часто остаются неверно понятыми наборщиками, а правка гранок несет в себе существенную положительную вероятность внесения дополнительных ошибок. Таким образом, согласно мнению трех первых пользователей системы лаборатории Белла [1], «мораль ясна: если кто-то другой набирает ваше произведение, то у вас нет возможности контролировать появление ошибок; если же вы выполняете эту работу самостоятельно, то можете винить только себя». Что касается меня, то я не могу подобрать слов, чтобы выразить испытываемые мною чувства, когда вношу изменения в свою рукопись, хранящуюся в машине в Станфорде, зная, что эти изменения немедленно будут отражены и при этом нет никакого промежуточного звена, где бы мои намерения могли быть искажены.
Возможно, настанет день, когда языки программирования для набора станут стандартом и статьи будут приниматься на компьютер Американского математического общества с компьютера автора посредством телефонных линий. Не будут нужны гранки, а рецензенты и (или) редакторы будут отправлять автору предлагаемую правку; авторы смогут вставить необходимые исправления в рукопись снова по телефонным проводам.
Я, несомненно, хочу надеяться, что языком, который будет выбран в качестве стандарта, станет мой Т^Х. Ну ..., возможно, я небеспристрастен, и знаю, что Т^Х предоставляет только чуть большие удобства, чем другие системы. Всего несколько десятков таких мелких удобств свелись к тому, что важно для меня, и я думаю, что эти мелочи докажут свою важность и для других. Поэтому я хотел бы отвести несколько следующих минут на более подробный рассказ о TgX’e.
ТеХ как язык описания ввода
Т^Х должен уметь обращаться с «обычным» текстом так же хорошо, как и с математическим, и должен быть так сконструирован, чтобы быть универсальной системой, в которой математические свойства сочетаются с возможностями программ обработки текстов, вместо того чтобы быть «привязанным» к условному языку набора. В основе Т^Х’а лежит идея построения объектов из неких элементов, которые я назвал боксы. Типографская литера сама по себе представляет собой бокс — сплошной черный прямоугольник; мы будем использовать эти «атомы» для построения более сложных боксов, называемых по аналогии «молекулами», формируя горизонтальные или вертикальные последовательности боксов. Окончательные страницы текста представляют собой боксы, составленные из последовательностей боксов, составленных из последовательностей боксов и т. д. вплоть до отдельных литер и черных прямоугольников, которые не разлагаются далее. Например, типичная страница книги
представляет собой бокс, образованный из вертикальной последовательности боксов, представляющих собой строки набора, и эти строки набора являются боксами, составленными из горизонтальных последовательностей боксов, представляющих собой отдельные литеры. Математическая формула естественным образом разлагается на боксы; например, числитель и знаменатель дроби суть боксы, равно как и черта дроби между ними (поскольку это узкий прямоугольник, залитый черной краской). Элементы матриц также являются боксами и т. д.
Отдельные боксы в горизонтальной или вертикальной последовательности отделяются друг от друга особой эластичной субстанцией, которую я назвал клей. Клей между двумя боксами состоит из трех компонент (х, у, г), выражаемых в единицах длины:
компонента х — пробел — идеальный или нормальный пробел, который необходим между этими боксами;
компонента у — растяжение—размер дополнительного пробела, который допустим;
компонента z — сжатие — размер пробела, который при необходимости может быть удален.
Предположим, последовательность состоит из п + 1 боксов Bq, Bi,...,Bn, разделенных п порциями клея, имеющего спецификации (^i, t/i, ^i),..., (^п) Ут z-nY Когда эта последовательность становится боксом, мы закладываем клей в соответствии с окончательным размером этого бокса. Если этот окончательный бокс предполагается быть большим, чем мы получили бы при нормальных пробелах xi + • • • + хп, мы увеличиваем эти пробелы пропорционально значениям у так, чтобы реальные пробелы и между боксами стали
a;i -Vty^ ..., хп -Vtyn
для некоторого подходящего t > 0. С другой стороны, если требуемый окончательный размер должен быть меньше, мы сужаем пробел до
Ху - ^1, . . . , Хп - tzn,
пропорционально отдельным сжатиям zt. В последнем случае t не разрешается быть больше 1; клей никогда не должен быть меньше, чем х — z^ хотя иногда он может становиться больше, чем х + у. Как только клей заложен, бокс становится жестким и никогда больше не меняет свой размер.
Рассмотрим, например, обычную строку текста, которая представляет собой последовательность отдельных боксов литер. Пусть клей между буквами в слове имеет компоненты х = у = z = 0, что означает, что буквы в слове плотно пригнаны друг к другу; но клей между словами может иметь компоненту х^ равную ширине буквы ‘е’, причем у — х^ z — х]^ т. е. пробелы между словами могут растягиваться или сжиматься. Пробелы после знаков препинания вроде точек и запятых можно растягивать с большей скоростью, но сжатие должно происходить медленнее.
Важный частный случай понятия клея возникает, когда мы имеем дело с «бесконечной» растяжимостью. Предположим, что компоненты х и z равны нулю, а компонента у чрезмерно велика, скажем, длиной в километр. Если такой элемент клея поместить слева от последовательности боксов, то в результате все существенные растяжения будут слева от последовательности боксов, следовательно, эти боксы будут иметь равнение вправо, так что их правые края совпадут с полями. Аналогично, если мы поместим такой бесконечно растяжимый клей с двух концов, то в результате получим равнение по центру строки. Таким образом, эти общепринятые полиграфические эффекты являются простыми частными случаями основополагающей идеи гуттаперчевого клея и компьютер элегантно справляется с этой работой, поскольку она сводится к относительно примитивным операциям. Вы можете заметить из этого примера, что клею позволено появляться и в концах последовательностей, а не только между боксами. Можно помещать клей после клея и боксы после боксов, так что последовательность боксов на самом деле представляет собой последовательность боксов и клея, перемешанных между самой любым образом. Я не упомянул об этом ранее, потому что мне казалось, что сначала эту идею лучше объяснить на примере боксов, перемежающихся клеем.
Те же принципы применимы и к вертикальным последовательностям. Например, клей, который появляется над и под выключной формулой имеет тенденцию к растяжению и сжатию, но клей между строками текста должен быть строго вычислен, чтобы в случае необходимости строки можно было равномерно разогнать по странице. Вы можете также позволить себе при помощи идеи клея самым естественным образом выполнять специальные трюки, вроде эффекта наложения букв (взяв отрицательное значение ж).
Разбиение на строки
Одной из самых интересных особенностей систем, подобных ТЁХ’у, является способность разбивать абзац на отдельные строки примерно нужной длины. Традиционный способ, еще использующийся и сегодня в компьютерных системах, состоит в том, чтобы наилучшим образом оборвать строку, когда вы доходите до правого поля, но как она будет выглядеть на печати, вы уже никогда не рассматриваете; следующую строку вы начинаете, уже не помня, что было ранее. На самом деле, зачастую было бы лучше переместить короткое слово с данной строки на следующую, но проблема в том, что вы не знаете, насколько выиграет весь абзац, если вы улучшите вид одной строки.
В системе Т^Х вводится новый подход к проблеме разбиения на строки, при котором завершающая строка абзаца оказывает влияние на способ разбиения первых строк; в результате появляются дополнительные пробелы и новые переносы слов. Вот как это работает: сначала мы сводим разбиение на строки к строго поставленной математической задаче с использованием субстанции ТЁХ’а «клей» для введения понятия «плохость». Когда горизонтальная последовательность боксов имеет некоторую естественную ширину w (основанную
на ширинах ее боксов и пробелов, заполненных клеем), с примененным к ней некоторым растяжением у (равным сумме компонент растяжения) и некоторым сжатием z (равным сумме компонент сжатия), плохость распределения клея, приведшая к ширине W этого бокса, определяется как 1 +100£3 в наших предыдущих обозначениях, или точнее:
если W = w ,
1 + 1001 + 100/TT-wV\ У / ’/TT-wV\ У / ’если W > w ,если W > w ,1 + 1001 + 100если w — z < W < w,если w — z < W < w,
1 + 100
1 + 100
/TT-wV
\ У / ’
/TT-wV
\ У / ’
если W > w ,
если W > w ,
1 + 100
1 + 100
если w — z < W < w,
если w — z < W < w,
бесконечность,бесконечность,если W < w — z.если W < w — z.
бесконечность,
бесконечность,
если W < w — z.
если W < w — z.
Таким образом, если искомая ширина W почти равна естественной ширине w, либо если было много растяжений или сжатий, то коэффициент плохости очень мал; но если W значительно больше w и растяжений не так уж много, то имеем плохую ситуацию. Кроме того, мы добавляем штрафные очки к коэффициенту плохости, если строка оканчивается на сравнительно нежелательном месте; например, когда слово нужно разорвать на переносе,- плохость достигает 50, и еще больший штраф назначается за разрыв математической формулы.
Теперь задачу разбиения на строки можно сформулировать следующим образом: «при заданном тексте абзаца и при расстановке всех допустимых мест разбиения его на отдельные строки, найти точки разбиения, минимизирующие сумму квадратов коэффициентов плохости полученных в результате строк». Это определение, разумеется, довольно произвольное, но, кажется, оно работает. Предварительные эксперименты показали, что почти всегда делается тот же выбор точек разбиения при простой минимизации сумм отдельных плохостей, равно как и при минимизации сумм их квадратов, но из предосторожности мы будем все же минимизировать суммы квадратов, поскольку это также будет способствовать минимизации максимальной плохости.
Одна лишь математическая формулировка проблемы разбиения на строки еще, разумеется, ее не решает; необходим хороший способ отыскания нужных точек разбиения. Если существует п разрешенных мест разбиения (включая все пробелы между словами и все возможные переносы слов), то имеется 2П возможных способов поделить абзац на строки, и у нас не хватит времени рассмотреть их все. К счастью, существует методика, позволяющая свести число шагов вычислительной процедуры к порядку величины п2, вместо 2П; это специальный случай того, что Ричард Беллман назвал «динамическим программированием». Пусть f(j) есть минимальная сумма квадратов плохости для всех способов разбиения исходного текста абзаца до точки разбиения j включительно, и пусть 6(i,j) есть плохость строк, которые находятся между точкой разбиения г и точкой разбиения j. Начало абзаца обозначим через 0, а заключительную точку разбиения в конце этого абзаца — через п + 1, причем
перед ней может быть помещен бесконечно растяжимый клей. Тогда
/(0) = 0;
/О') = Qmin (f^ + Ь(м)2) для 1 < j < п + 1.
Найти значения /(1), ..., /(n + 1) можно примерно за п2 шагов, и /(n + 1) будет минимальной из возможных сумм квадратов коэффициентов плохости. Припомнив значение г, в котором возникал минимум для каждого j, можно найти точки разбиения, приводящие к наилучшему разбиению на строки, как и требовалось.
На практике нет необходимости проверять маловероятные точки разбиения; например, очень редко есть основание переносить самое первое слово в абзаце. Таким образом, оказывается, что этот метод динамического программирования может быть сведен к другому алгоритму, время работы которого почти всегда будет порядка п, а не п2, и нужно будет проверить сравнительно немного переносов. Иногда сами по себе переносы приводят к некоторым интересным математическим задачам, но здесь у нас нет времени отвлекаться на это (см. статью [42] и цитируемую в ней литературу).
Идея коэффициентов плохости может быть с успехом применена и к вертикальным последовательностям боксов; в этом случае мы хотим избежать плохих разбиений колонок или страниц. Например, штрафные очки даются за разрыв абзаца между страницами на переносе, или за разрыв его таким образом, что на странице остается только одна строка от него (так называемая «висячая строка»). Размещение иллюстраций, таблиц и примечаний также регламентируется соответствующими правилами, формулируемыми в терминах коэффициентов плохости.
Можно еще много рассказать о Т£Х’е, в том числе о средствах верстки сложных таблиц без предварительного вычисления ширины колонки, но я думаю, у вас уже создалось представление о самых важных принципах этой системы. В течение нескольких следующих месяцев я намерен написать такую программу для Т^’а, в которой работа каждого алгоритма была бы предельно ясна и система могла бы работать на разных вычислительных платформах без особых для этого усилий. Тогда я опубликую эти программы в виде отдельной книги, и каждый, кто захочет их использовать, сможет сделать это.
Антракт
В начале лекции я сказал, что она будет состоять из двух частей: первая о том, как полиграфия может помочь математике, и вторая — о том, как математика может помочь полиграфии. До сих пор вы имели возможность услышать и об одном, и о другом, но математика здесь была совсем тривиальная. В оставшейся части своей лекции я постараюсь рассказать о направлении, где вклад математики наиболее значительный, именно об описании рисунков самих литер. Более строго о двух частях моей лекции следовало бы сказать так:
первая часть посвящена системе ТЁК, которая позволяет создавать рукописи, указывая в каком именно месте должна быть размещена каждая литера, а вторая часть посвящена системе МЕТА FONT, которая генерирует сами шрифты, чтобы использовать их в той части типографского дела, которая относится к шрифтовому дизайну.
Прежде чем приступить ко второй части своей лекции, я должен осветить некоторые новые достижения в технологии печати. Самый реальный способ печатать математические книги высокого качества в течение нескольких последних десятилетий состоял в использовании технологии монотипа, при которой буквы отливаются в металле с последующей ручной обработкой формул со сложной структурой. Когда несколько лет тому назад я увидел применение этой технологии к моим книгам, меня поразило, что металлический набор используется для создания только одного экземпляра; этот эталонный экземпляр затем фотографировался и сама печать происходила уже с фотоформ. Эта несколько неуклюжая последовательность шагов была оправданна, поскольку получаемый результат был хорошим. В течение 60-х годов, однако, горячий набор был заменен многоцелевыми устройствами типа машины Photon, на которой отпечатаны программы сегодняшней лекции; здесь целиком задействован фотопроцесс, поскольку рисунки букв сохраняются на ротационном диске в виде маленьких негативов, а необходимые для печати формы получаются при экспозиции пленки после преобразования литер в нужные размеры и расположения их на нужном месте посредством зеркал и линз (см. [10]). Такие машины имеют ограничения в скорости и возможности расширения шрифтовых касс.
«Третье поколение» наборного оборудования
В более современных машинах, вроде той, что использовалась для подготовки последних томов Transactions, аналоговые процессы заменены цифровыми. Новизна идеи состоит в том, что страницы или фотонегативы разбиваются на миллионы крошечных прямоугольников, вроде миллиметровки или телевизионного изображения, но с более высокой разрешающей способностью около 1000 единиц на дюйм. Для каждого из крошечных «пикселей» при такой растеризации — около миллиона квадратных пикселей в одном квадратном дюйме — наборная машина решала, будет ли он белым или черным, и черный пиксель воспроизводился на фотоформе посредством очень точного электронного или лазерного луча. Поскольку в таких машинах было несколько подвижных частей и почти не требовались механические действия, они могли работать с очень большой скоростью несмотря на то, что в каждый момент экспонировался лишь крошечный участок пленки.
Установив свой иной подход, это новое полиграфическое оборудование в сущности трактовало каждую страницу как огромную матрицу из нулей и единиц, зачерненную в тех местах, где были единицы, и оставленную белой там, где были нули. Это вроде электронных табло на стадионе, но со значи-
тельно большим количеством ячеек. Теперь общая задача такой системы, как ТЁК, сводилась к преобразованию авторской рукописи к гигантской матрице из двоичных разрядов.
Вот первый вопрос, который мы должны, разумеется, задать: «А что происходит с качеством?». Понятно, что телевизионное изображение не подходит для фотовоспроизведения и цифровые наборные машины были бы совершенно неудовлетворительными, если бы они давали вывод, который смотрелся хуже, чем результат металлического набора. К вещам, подобным этим, я проявляю педантизм и взыскательность; например, я отказываюсь есть маргарин вместо масла и никогда не слушаю электронный орган, который по красоте звучания даже отдаленно не сравним с настоящим органом из труб. Так что я был чрезвычайно скептически настроен по отношению к цифровой типографии, пока не увидел подлинный образец того, что было сделано на машине с высоким качеством воспроизведения и не стал рассматривать это через лупу: невозможно было поверить, что буквы получены посредством дискретного растра! Причина кроется не в том, что наши глаза не могут различить более 1000 точек на дюйм: в подходящих обстоятельствах могут. Дело в том, что частицы типографской краски не могут различить такие мелкие детали — вы не сможете напечатать линию из 1000 зигзагов вдоль диагонали квадратного дюйма, потому что краска на ребрах будет давать закругления. На самом деле критическое число, кажется, ближе к 500, а не к 1000. Таким образом, физические свойства краски приводят к тому, что страницы выглядят так, как если бы растра не было вовсе.
Сегодня стало понятно, что дискретное полиграфическое оборудование, использующее растеризацию, вскоре вытеснит другие машины почти на всех процессах подготовки издания. Таким образом, в будущем, тот факт, что Гутенберг и другие мастера изобрели подвижный шрифт, не будет рассматриваться как особенно замечательный, а станет считаться просто неким историческим курьезом, который влиял на печатное дело около 500 лет. Поистине замечательной вещью окажется математика: математика матриц из нулей и единиц!
Полуфилософские заметки
Следующую часть своего выступления я бы хотел изложить, исходя из собственной точки зрения. Как представитель комбинаторики, я идентифицирую реальность с матрицами из 0 и 1, так что когда прошлой весной я узнал о таких печатных машинах, то уже не мог продолжать работать над тем, чем тогда занимался; я только хотел улучить момент, чтобы испытать возможности нового оборудования. Меня также вдохновляла идея остановить падение качества, которое я мог наблюдать в технических журналах. Кроме того, издатели моих книг по искусству программирования прилагали героические, но безуспешные усилия к выпуску второго издания 2-го тома с сохранением стиля первого издания, но без использования уже исчезающего к тому времени горячего набора. Это означало, что мои книги будут скоро выглядеть так же плохо,
как и журналы. Когда я понял, что все эти проблемы могут быть решены при помощи программирования, мне ничего не оставалось, как попытаться найти решение самостоятельно.
Одним из самых сильных побудительных моментов было понимание того, что эта проблема м'огла быть решена раз и навсегда, если бы я смог найти чисто математический способ определения рисунка букв и построить соответствующие растровые изображения. Хотя новые способы печати были предназначены для использования в будущем, возможно еще до того, как я закончу работу над 7-м томом книг, которые пишу, любые новые машины почти наверняка будут основаны на растре с высоким разрешением, и хотя разрешение может меняться, формы букв могут оставаться теми же всегда, поскольку они описаны в машинно-независимом виде. Итак, моей целью было дать точное описание рисунков всех символов, которые мне были нужны.
Я видел, как делались оцифрованные типографские шрифты в разных частях света; в основном, при помощи сложного фотооборудования и компьютерных программ копировались уже существующие шрифты и затем они редактировались вручную. Но интуитивно я почувствовал, что для меня это не годится отчасти потому, что сложное оборудование пока для нашей лаборатории в Станфорде недоступно, отчасти из-за сомнительности копирования шрифтов, защищенных знаком копирайта, но в основном из-за того, что сама идея копирования мне не представляется проникающей в суть проблемы. Мне вспоминается однажды слышанный анекдот о логарифмических линейках в Японии. Согласно этой истории на первой логарифмической линейке, попавшей на Восток, было грязное пятно, и в течение многих лет на всех японских логарифмических линейках было бессмысленное темное пятно на том же месте! История эта, возможно, вымышленная, но суть ее в том, что надо копировать идею, а не форму. Мне кажется, правильным будет не задать вопрос «Как скопировать этот типографский шрифт?», а задуматься вот о чем: «Если бы великие шрифтовые дизайнеры прошлого были бы живы, то как бы они создавали свои шрифты для нового оборудования?». Я не надеялся, разумеется, что смогу найти точный ответ на это, но чувствовал, что выйду на правильный путь, поэтому начал изучать историю шрифтового дизайна.
Да, это чрезвычайно увлекательный предмет, но за неимением времени, я не смогу долго о нем рассказывать. Две первые работы, которые я прочитал, были автобиографиями двух знаменитых шрифтовиков 20-го столетия: Германа Цапфа [51] и Фредерика Гауди [20], и особенно меня заинтересовали заметки Цапфа:
С начала шестидесятых ... я был охвачен этим новым направлением [фотонабором] .... Шрифтовой дизайнер — или лучше будем называть его дизайнером алфавита — должен воспринимать свою миссию и свою ответственность как более высокую, чем раньше, сочетая традиционный подход к разработке форм литер с практическими целями и возможностями современного оборудования .... Новые фотонаборные системы, использующие экраны с электронно-лучевыми трубками (ЭЛТ) или запись алфавита в
оцифрованном виде, принесли с собой абсолютно новые технические проблемы, значительно более сложные, чем прежде. [51, с. 71].
У меня создалось впечатление, что Гауди не очень симпатизировал новому оборудованию, но в его книге также содержались полезные мысли.
Математический дизайн шрифта
К счастью, Станфордская библиотека имела великолепную коллекцию книг по печатному делу и мне представился случай прочесть много редких первоисточников. Я узнал, к своему удивлению, что идея математического описания букв уже возникала; она появилась в 15-м столетии и стала активно разрабатываться в начале 16-го. Это было время, когда деятели эпохи Возрождения применяли математические исследования к реальному миру, и одной из выдающихся идей было построение прописных букв при помощи линейки и циркуля. Первым, кто это проделал, был, видимо, Феличе Феличиано (примерно 1460 г.), чей рукописный манускрипт из библиотеки Ватикана был опубликован 500 лет спустя [19]. Феличиано был блестящим художником, который хотел перевести принципы построения форм литер на математический язык. Его современники — резчики из Вероны — использовали в своих работах его рисунки шрифта. Аналогичные эксперименты проводили еще несколько авторов 15-го века — критический обзор этих ранних разработок содержится в [8] — но самая замечательная работа такого рода была проведена в начале 16-го века.
Рис. 4. Построения эпохи Возрождения буквы В, выполненные при помощи линейки и циркуля (а) Феличиано [19], (Ь) Пачиоли [43], (с) Торниелло [34] и (d) Палатино [44].
Итальянский математик Лука Пачиоли, автор самой популярной в те времена книги по алгебре (это была одна из самых ранних опубликованных книг по алгебре), в другую свою книгу по геометрии и «золотому сечению» Divina Proportioned изданную в 1509 г., включил приложение об алфавитах. Еще одной великолепной итальянской работой по этому предмету была книга Франческо Торниелло, опубликованная в 1517 г. [34]; на рис. 4 показана буква В, выполненная Феличиано, Пачиоли и Торниелло, а также Джованбаттиста Палатино [44]. Палатино был одним из лучших каллиграфов того времени, свою работу он сделал примерно в 1550 г. Аналогичные работы проводились в Германии и Франции. Вероятно, самой знаменитой немецкой книгой, повлиявшей на дальнейшее развитие шрифта, была книга Альбрехта Дюрера Underweysung
der Messung [18] — учебник по геометрическим построениям для художников эпохи Возрождения. Весьма популярной было также и французское издание Champ Fleury Джофруа Тори [49] —первого королевского художника Франции, который ввел во французскую типографию акцентированные буквы. На рис. 5 показаны два варианта буквы В, предложенных Тори. Из всех этих книг я отдаю предпочтение труду Торниелло, потому что только у него построения отличаются ясностью и четкостью.
Рис. 5. Еще две буквы В руки Тори [49].
По-видимому никто больше на протяжении более сотни лет не работал в этом направлении и не занимался ни строчными буквами, ни цифрами, ни курсивным начертанием и никакими иными символами, пока Джозеф Моксон не дал подробного описания отдельных изящных букв, сконструированных в Голландии [39]. Предел в улучшении этого математического подхода был достигнут несколько позже, когда французский король Людовик XIV распорядился создать Королевский алфавит. Примерно с 1690 г. более десяти лет над проектом Людовика работала группа художников и типографов; образец того, что они сконструировали, показан на рис. 6 [24].
Итак, ясно, что математическое описание форм литер имеет длинную историю. Я должен, однако, также сказать, что почти все современные крупные типографы пришли к общему заключению о том, что усилия эти были напрасными. Хуже того, буквы, созданные циркулем и линейкой, были названы «уродливыми», в лучшем случае говорили, что они «лишены каллиграфического изящества» [8]. Французские дизайнеры не стали истинными последователями ни Филлипа Гранжана, который фактически выгравировал шрифт Людовика XIV, ни кого бы то ни было еще из тех времен. Реакцией Гауди на это было: «Слава Богу!» [20, с. 139]. Столь строгие геометрические формы литер подверглись критике на самом деле уже в 16-м столетии. Джованни Кресчи, выдающийся переписчик библиотеки Ватикана и Сикстинской капеллы писал в 1560 г.:
Я пришел к выводу, что если бы Евклид, король геометрии, вернулся сегодня в наш мир, то он никогда бы не стал утверждать, что кривые в буквах можно изобразить окружностями, построенными циркулем. [16]
Рис. 6. Буквы прямого и курсивного начертания, разработанные для французского короля Людовика XIV [24].
Кресчи, несомненно, был прав. Но, к счастью, за последние 400 лет математика несколько обогатилась и теперь у нас в запасе есть и другие приемы, кроме прямых линий и окружностей. В самом деле, теперь можно вывести формулы, описывающие тонкие нюансы рисунка руки лучших шрифтовых дизайнеров, и, не исключено, что одаренный дизайнер, вооруженный соответствующими математическими средствами, будет способен создать нечто еще лучшее, чем то, что мы имеем на сегодняшний день.
Определение новых кривых
Рассмотрим следующую математическую задачу: пусть даны п точек ^i, ^2,..., zn на плоскости; какая замкнутая кривая, проходящая через эти точки в заданном порядке ^i, ^2,..., гп и затем возвращающаяся в zy, будет самой приятной? Чтобы исключить вырожденные случаи, будем предполагать, что ri не меньше 4. Эта задача в сущности напоминает детскую головоломку «От точки к точке».
Разумеется, это не является строго поставленной математической задачей, потому что я не сказал, что означает понятие «самая приятная кривая». Приведем сначала несколько аксиом, которым должны удовлетворять самые приятные кривые.
Свойство 1 (инвариантность). Если данные точки повернуть, перенести или расширить, то самая приятная кривая (СПК) будет повернута, перенесена или расширена точно так же. [Аналитическое выражение: СПК(а^1 + 6,..., azn + Ь) = aCHK(^i,..., zn) + 5.]
Свойство 2 (симметрия). Циклическая перестановка данных точек не меняет решения. [Таким образом, имеем СПК(^1,^2,..., ^п) = СПК^з,...,^,^).]
Свойство 3 (расширяемость). Добавление новой точки к уже существующим на самой приятной кривой не меняет решения. [Если г есть любая точка между Zk и Zk-^-i на СПК(^1,... ,zn), то имеем СПК(^,...,^, г, ^+1,... ,гп) = СПК(^,...,^, ZkA-i....^ny\
В справедливости этих свойств очень легко убедиться на интуитивном уровне. Например, свойство расширяемости говорит о том, что дополнительная информация не должна приводить к более слабому решению.
Следующее свойство не столь очевидно, но я считаю его важным для тех приложений, которые имеются ввиду.
Свойство 4 (локальность). Каждый сегмент самой приятной кривой, заключенный между двумя данными точками, зависит только от этих точек и от непосредственно примыкающих к ним (предыдущей и следующей). [СПК(^1, ^2,..., гп) состоит из СПК(гп, ^i, ^2, ^з) от ^i до ^2, затем СПК(^1, г2,г3, z4) от г2 до г3, ..., затем СПК(гп_1, zni zi, z2) от zn до ^.] Согласно свойству локальности изменение одной части образа не должно влиять на другие его части. Это упрощает наше исследование самой приятной-кривой, поскольку нужно решать задачу для случая четырех данных точек; опыт показывает, что локальность также чрезвычайно упрощает процесс дизайна литер, поскольку в каждый момент можно иметь дело с отдельной порцией штрихов. Между прочим, свойство 4 влечет за собой свойство 2 (циклическая симметрия).
Один из способов удовлетворить всем четырем свойствам состоит просто в том, чтобы составить самую приятную кривую из отрезков прямых. Но многоугольники не вызывают приятного ощущения, поэтому мы постулируем
Свойство 5 (гладкость). У самой приятной кривой нет острых углов. [СПК(^1,..., zn^ дифференцируема при некоторой параметризации.]
Иначе говоря, в каждой точке кривой имеется единственная касательная.
Из свойств расширяемости, локальности и гладкости вместе взятых вытекает фактически, что направление касательной в точке Zk зависит только от Zk-i, 2k и Zk-vi. Поскольку это касательная для двух кривых, одна —от Zk-i до Zk и другая —от Zk до Zkvu мы знаем, что она зависит только от
(zk-2, Zk-i, Zk, Zk+i) И только от (zk-1, Zk, Zk+1, Zk+2). в силу свойства расширяемости можно предположить, что п по крайней мере равно 5; следовательно, ^-2 отлична от ^+2 и касательная должна не зависеть от них обеих. На самом деле по этой причине мы будем использовать только очень слабую форму расширяемости.
Если мы применим свойство расширяемости в его полной сильной форме, то получим значительно более сильное следствие, весьма неудачное: Не существует хорошего способа удовлетворить свойствам 1-5\ Предположим, к примеру, что мы добавим еще одну аксиому, которая почти необходима в любых разумных определениях приятных кривых:
Свойство 6 (скругляемость). Если ^i, ^2, г3, Z4 суть последовательные точки кривой, то самая приятная кривая, проходящая через них, есть окружность.
Это свойство (вместе с нашим предыдущим наблюдением относительно того, что касательная зависит только от трех точек) определяет касательную в каждой нашей заданной точке. Именно, касательная в точке Zk есть касательная к окружности, проходящей через точки ^_i, Zk и Zk-vv (На время отбросим возможность принадлежности всех трех точек одной прямой.) Далее, свойство расширяемости гласит, что если z— произвольная точка между z-v и z«i на самой приятной кривой, проходящей через точки z\^... ^z^ мы знаем направления касательной в точке г, если только эта точка z не принадлежит отрезку прямой от z\ до z^. Но существует единственная кривая, начинающаяся в произвольной точке z вне этой прямой и имеющая заданные касательные в каждой из этих точек, именно дуга окружности от z до z^ проходящая через ^i. Неважно, где именно вне прямой мы начинаем, мы способны нарисовать только одну кривую, имеющую правильные касательные. Отсюда следует, что касательная в точке z«i зависит только от zi, Z2 и от касательной в точке zi, что невозможно.
По причине, изложенной в предыдущем абзаце, нет способа удовлетворить свойствам 3, 4, 5 и 6. Будет показано, что по аналогичной причине нет никакой разумной замены для свойства 6, поскольку касательные, зависящие от всех z между zy и 22, будут определять векторное поле, в котором существуют уникальные кривые через практически все точки г, однако требуется некое двухпараметрическое семейство кривых между zy и Z2 > чтобы позволить удовлетворить здесь условию гибкости в производных.
Итак, одно из этих свойств можно исключить. Свойство локальности кажется самым сомнительным, но я уже говорил, что его я удалять не хочу. Следовательно, исключим свойство расширяемости. Это означает, что если мы возьмем самую приятную кривую, проходящую через точки z^..., zn, и если мы зададим еще одну точку z на этой кривой между точками Zk и Zkvu то самая приятная кривая, проходящая через эти n + 1 точек, может оказаться другой. Возможное преимущество состоит в том, что нет необходимости задавать так много точек, возможный недостаток заключается в том, что мы не в состоянии получить ту кривую, какую хотим.
Практическая аппроксимация
Вернувшись к вопросу шрифтового дизайна, можно сказать, что наша цель состоит в том, чтобы задать несколько точек Zk и получить математическое выражение, описывающее приятную кривую, проходящую через эти точки. Такие кривые будут использоваться для определения форм литер создаваемого шрифта. В идеале, это должно также облегчить вычисление кривых. Я решил использовать для этой цели кубические выражения
z^ = а0 + otyt + ct2t2 + c^3 , где &о> ^1, 0:2, аз суть комплексные числа, a t — действительный параметр. Кривые, которые я ввел, являются кубическими сплайнами, именно кусочными кубическими соотношениями, поскольку в каждом интервале между двумя заданными точками будут использоваться разные кубические выражения. Однако способ, которым я определяю коэффициенты для этих кубических выражений, отличается от методов, известных мне по опыту общения с обширной литературой по сплайнам. Возможно, мой метод выбора коэффициентов менее удобен по сравнению с общепринятыми, но он дал хорошие результаты, так что мне не стыдно обнародовать свой «доморощенный» способ.
Во-первых, я решил, что кубическое уравнение между z\ и z^ должно вполне определяться точками z-y и z^ и направлениями касательных в точках zy и ^2. Мы уже видели, что эти касательные существенно предопределены, если свойства 4, 5 и 6 справедливы, и я также обнаружил, что в дизайне шрифтов часто бывает нужно задать, чтобы некая касательная была горизонтальной или вертикальной. Таким образом, мой метод вычисления приятной кривой, проходящей через заданную последовательность точек, таков: сначала вычисляются направления касательных в каждой точке, а затем кубические выражения для интервалов, основанные только на конечных точках этих интервалов и на направлении касательных в них. Применив поворот, перемещение и масштабирование согласно свойству 1, мы можем предположить, что задача поставлена на комплексной плоскости от 0 до 1 с заданными направлениями и конечными точками. Кубическое уравнение в самом общем виде выглядит так:
z(t) = 3t2 - 2t3 + rei9t(l - ty - зе~^2(1 - t), остается определить положительные числа г и s как подходящие функции от 9 и ф.
Во вторых, я понимал, что невозможно, имея кубические сплайны, удовлетворить свойству 6, потому что вы не можете нарисовать окружность как кубическую функцию от t. Но я хотел иметь возможность получать кривые, которые были бы почти как окружности насколько это возможно при условии, что четыре заданные последовательные точки лежат на окружности. Эти кривые должны не отличаться от окружностей настолько, насколько позволяет человеческий глаз. Таким образом, при 9 = ф я решил взять т — з, так что z(^ точно соответствовала окружности, если предположить, что эта кривая между 0 и | и между | и 1 не слишком меняет направление. Ну вот, это позволило выполнить работу великолепно: небольшие вычисления, проведенные
на компьютере* показали, что максимальное отклонение от истинной кривой возникает в точке t = (3± V3)/6 и относительная ошибка пренебрежимо мала. Если, к примеру, взять четыре точки на равном расстоянии друг от друга и на расстоянии 1 от некоторого центра, определяемый описанным выше образом сплайн для этих точек окажется между расстоянием 1 и расстоянием
У(71/6- Т8)/3 < 1.000273
от центра; ошибка меньше, чем 0.0003, а если взять п точек, то максимальная ошибка стремится к нулю как l/nQ.
(Несколько изменив обозначения, положим
z(t) = l + (ei9-l)(3Z2-2i3)
+ 4г<(1 — i)(l — t — ег9/) (sin^) / (1 + cos^
и /(0 = Ц(<)|2. Тогда
т=8т=8max \z(tV = z o<t<i11max \z(tV = z o<t<i11Sin -2Sin -2(t- 1) t (22 - 1) (6£2 — 6t + 1)(t- 1) t (22 - 1) (6£2 — 6t + 1)= 1 += 1 +969691091055296 + 106168320 + “ ‘55296 + 106168320 + “ ‘
т=8
т=8
max \z(tV = z o<t<i11
max \z(tV = z o<t<i11
Sin -
2
Sin -
2
(t- 1) t (22 - 1) (6£2 — 6t + 1)
(t- 1) t (22 - 1) (6£2 — 6t + 1)
= 1 +
= 1 +
96
96
910
910
55296 + 106168320 + “ ‘
55296 + 106168320 + “ ‘
как только mino<t<i |^(0l = z(0) = z(|) = ^(1) = 1- «Двухточечная окружность» имеет max|z(t)| = л/28/27 « 1.01835, тогда как трехточечная окружность имеет шах |z(£)| ~ у325/324 = 1.001542, а восьмиточечная — max |z(t)| « 1.0000042455. Эти почти круглые параметрические сплайны были введены Дж. Р. Маннингом [33], который заметил, что визуально они почти неотличимы от настоящих окружностей.)
В другом случае, когда предполагается естественный способ выбора г и s, имеем 9 + ф = 90°; тогда кривая z^ должна быть очень похожа на эллипс с конечными точками на его осях. (Это сводится к требованию, чтобы (3£2 — 2£3 — (s/cosy?)^2(l — 0 — I)2 + (3£2 — 2t3 + (г/cos6)^(1 — f)2)2 приблизительно равнялось 1.) Итак, теперь я знал, что мне нужно:
1 + cos 9 ’2cos01 + cos 9 ’2cos0s =s =1 + cos ф ’1 + cos ф ’когда 9 = ф\когда 9 = ф\2 cos ф2 cos ф(1+cos 45°) cos 45° ’ Я попробовал формулы(1+cos 45°) cos 45° ’ Я попробовал формулы(1+ cos 45°) cos 45° ’(1+ cos 45°) cos 45° ’когда 9+ф = 90°.когда 9+ф = 90°.2cos02cos02 cos ф2 cos ф
1 + cos 9 ’
2cos0
1 + cos 9 ’
2cos0
s =
s =
1 + cos ф ’
1 + cos ф ’
когда 9 = ф\
когда 9 = ф\
2 cos ф
2 cos ф
(1+cos 45°) cos 45° ’ Я попробовал формулы
(1+cos 45°) cos 45° ’ Я попробовал формулы
(1+ cos 45°) cos 45° ’
(1+ cos 45°) cos 45° ’
когда 9+ф = 90°.
когда 9+ф = 90°.
2cos0
2cos0
2 cos ф
2 cos ф
(1 4- COS ^) COS ^ ’(1 + COS ф^ cos ^ ’
Я признателен разработчикам системы компьютерной алгебры MACSYMA в M.I.T. и сети ARPA, которая сделала эту систему доступной для исследовательской работы.
Рис. 7.Сплайны с параметрами 9 = 0° (5°) 120° и ^ = 60°.Рис. 7.Сплайны с параметрами 9 = 0° (5°) 120° и ^ = 60°.
Рис. 7.
Сплайны с параметрами 9 = 0° (5°) 120° и ^ = 60°.
Рис. 7.
Сплайны с параметрами 9 = 0° (5°) 120° и ^ = 60°.
Рис. 8.Подобно рис. 7, но скорректировано так, что г' = тах(1/2, г) и s' = тах(1/2, s).Рис. 8.Подобно рис. 7, но скорректировано так, что г' = тах(1/2, г) и s' = тах(1/2, s).
Рис. 8.
Подобно рис. 7, но скорректировано так, что г' = тах(1/2, г) и s' = тах(1/2, s).
Рис. 8.
Подобно рис. 7, но скорректировано так, что г' = тах(1/2, г) и s' = тах(1/2, s).
которые соответствовали обоим случаям, где ^ = (9 + у>)/2. Но это не давало удовлетворительных результатов, особенно когда ф приближалось к 90°. Моей второй попыткой было
_ 2 sin ф 2 sin 9(1 + cos^) sin^ ’ (1 + cos-0) sint^
и это работало очень хорошо. На рис. 7 показаны сплайны, полученные в результате такого подхода, когда ф = 60° и 9 меняется от 0° до 120° с шагом 5°.
Можно доказать, что если 6 и ф неотрицательны и меньше 180°, то только что определенная кубическая кривая z^ никогда не пересечет прямые в углах 6 и ф, которые имеют концевые точки 0 и 1 соответственно. Это очень ценное свойство для шрифтового дизайна, поскольку оно гарантирует, что кривая не выйдет за пределы. Тем не менее я обнаружил, что это дает также неудовлетворительные кривые, если один из параметров 9 или ф очень мал, а другой — нет, потому что это означает, что кривая z^ будет очень близка к прямой, пока она не повернет к этой прямой извне под очень острым углом. В самом деле, угол 9 не так уж редко бывает нулевым, и это приводит к прямой линии и к острому углу в правой конечной точке. Поэтому я изменил формулы, чтобы быть уверенным, что и г и s всегда равны | или больше, если особо не оговорено иное; более того, я никогда не брал г или s больше 4. На рис. 8 приводятся сплайны, полученные при тех же условиях, что и на рис. 7, но при г и (или) s равных |, если формула применяется для любого меньшего значения.
Используя эту технику, получаем систему для рисования приемлемо красивых кривых, если не для самых приятных, и особенно хорошо это работает для окружностей. Если этот метод дает неправильное направление касательной в некоторой точке, мы можем этим управлять, задав две очень плотно расположенные к друг другу точки, имеющие нужный наклон. Я также включил другой способ модификации стандартных направлений касательных, приводящий к тому, что эта система одинаково хорошо рисует как эллипсы, так и окружности. Прежде чем вычислить сплайны, я сначала сжимал всю фигуру по вертикали, умножив все координаты у на заданное соотношение ширины и
высоты (обычно 1); затем вычислял сплайны и полученную в результате сжатую кривую снова растягивал, поделив координаты у на соотношение ширины и высоты.
Приложение к шрифтовому дизайну
Давайте теперь более пристально посмотрим на то, что мы можем нарисовать при помощи математической системы вроде только что описанной. Я думаю, самым естественным было бы продемонстрировать это вам на буквах от А до Z; но, поскольку эта лекция математическая, лучше рассмотрим цифры от 0 до 9 (см. рис. 9). Кстати, способ, которым я упорядочил эти цифры, иллюстрирует фундаментальное различие между математиками и полиграфистами: математик всегда расположит 0 рядом с 1, а полиграфист — рядом с 9.
0123456789
Рис. 9. Цифры от 0 до 9, нарисованные прототипом программы МЕТА FONT.
Большинство этих цифр были нарисованы на основе другой идеи, уходящей в исторические истоки типографии, именно посредством имитации переписчиков, пользовавшихся пером и чернилами. Рассмотрим сначала цифру ‘3’, например. Программа, рисовавшая этот символ на рис. 9, может быть перефразирована так: «Сначала рисуем точку, левая граница которой составляет | пути от ее левого края до правого, а нижняя граница равна | пути от верхушки до основания требуемой окончательной формы. Затем берем перо для волосяных линий и ведем его слева вверх вдоль дуги некого эллипса. Когда достигаем верхушки, перо начинает давать более жирную линию и это превращается в другой эллипс таким образом, что максимальная толщина достигается на осях этого эллипса с правой границей пера, равной | пути от левого до правого края рисуемой литеры. Затем толщина пера начинает уменьшаться до своего исходного размера как если бы перо проходило по другому эллипсу, находящемуся на 48% ниже пути от верхушки до основания требуемой окончательной формы ... ».
Обратите внимание, что вместо описания границы литеры, как это делали геометры эпохи Возрождения, моя система МЕТА FONT описывает кривую, проводимую пером относительно центра^ причем допускается изменение как формы пера, так и его траектории. Основное преимущество этого подхода состоит в том, что одно и то же определение успешно работает для семейства из бесконечного количества шрифтов, при этом каждый шрифт внутренне согласован. Изменением размера пера руководят кубические сплайны, равно как и движением центра пера. Для того чтобы определить примерно 20 различных шрифтов, использованных в разных местах моих книг, мне нужно было по
большей части пользоваться всего тремя видами перьев, именно (i) круглым пером, например для рисования точек и основания цифры ‘7’; (ii) горизонтальным пером в виде эллипса, ширина которого меняется, но высота остается равной перу для волосяных линий; (iii) вертикальным пером, аналогичным горизонтальному, которое используется, например, для рисования штрихов в основании цифры ‘2’ и вверху цифр ‘5’ и ‘7’. Горизонтальное перо используется чаще всех, им, в частности, нарисована вся цифра ‘3’ за исключением точек. Для шрифта, который мне требовался, мне не понадобилось наклонное перо (в виде эллипса, скошенного в одну сторону), за исключением акцента в виде тильды над испанской буквой п; но если бы я создавал шрифт типа Times Roman, наклонное перо безусловно потребовалось бы. Если эту систему расширять для китайской или японской письменности, я уверен, что следовало бы добавить дополнительную степень свободы для перемещений пера, позволявшую бы эллиптическому перу вращаться и изменять свою ширину.
Цифра ‘4’ демонстрирует еще одну сторону системы МЕТА FONT. Хотя эта литера довольно простая и целиком состоит из прямых, толстая прямая вверху срезана под углом. Для этого у системы METAFONT наравне с перьями имеются ластики. Сначала компьютер рисует толстую прямую целиком снизу доверху как в прописной букве ‘Г; затем он берет ластик стирает все, что находится слева, двигаясь по диагональному штриху вниз, затем он берет волосяное перо и завершает диагональный штрих. Аналогичным образом ластик используется для верхушки цифры ‘1’, основания цифры ‘2’, и т. д.
Иногда кажется, что простой сплайн не может адекватно описать нужное увеличение толщины пера, так что в отдельных случаях я прибегал к описанию левого и правого краев пера как отдельных кривых, чтобы потом их заполнить. Это происходит, например, в основном штрихе цифры ‘2’, края которого определяются двумя сплайнами, имеющими заданную касательную внизу и имеющими вертикальный наклон справа от кривой.
Вооруженный этой методикой я обнаружил, что способен определить полный шрифт вполне приличного качества, содержащий 128 литер, примерно
0ABCDEFGHIJKLMN
OPQRSTUVWXYZ[“]
‘abcdefghijklmno
pqrstuvwxyzfffiflffiffl aelfkoe^ELfACE
0123456789:;<=>?
ГЧС%^Х)*Ц-г./ ГДеЛЕПЕГФПц
Рис. 10. Шрифт из 128 литер, определяемый METAFONT’om при помощи пера для 5-пунктового кегля. (Литеры акцентов должны разместиться сверху над другими буквами точно в центре благодаря использованию TgX’a.)
за два месяца, хотя, конечно, мне пришлось сделать более тонкую доработку, когда речь зашла о большем, чем просто несколько пробных страниц набора (см. рис. 10). Наибольшие трудности, по крайней мере для меня, вызвала буква S (и цифра 8, для которой использовалась та же процедура). Три дня и
три бессонные ночи я потратил на то, чт.обы получить правильный рисунок буквы S, прежде чем, наконец, получил его. Было мгновенье, когда я даже подумал, не проще ли было бы переписать все мои книги без этой S! После первого дня неудачных попыток я показал свое изделие жене и она сказала: «А почему ты не захотел сделать это в виде S?»
(d)(d)Рис. 11. Буква S в виде, определенном (а) Феличиано [19]; (Ь) Пачиоли [43]; (с) Тор- ниелло [34]; (d) Палатино [44]; (е) французской комиссией [24].Рис. 11. Буква S в виде, определенном (а) Феличиано [19]; (Ь) Пачиоли [43]; (с) Тор- ниелло [34]; (d) Палатино [44]; (е) французской комиссией [24].
(d)
(d)
Рис. 11. Буква S в виде, определенном (а) Феличиано [19]; (Ь) Пачиоли [43]; (с) Тор- ниелло [34]; (d) Палатино [44]; (е) французской комиссией [24].
Рис. 11. Буква S в виде, определенном (а) Феличиано [19]; (Ь) Пачиоли [43]; (с) Тор- ниелло [34]; (d) Палатино [44]; (е) французской комиссией [24].
На рис. 11 показано, как эту проблему решали Феличиано, Пачиоли, Тор- ниелло, Палатино и французские академики, но их буквы не выглядят так, как современная S. Кроме того, я думаю, гравер французской S слегка слукавил при скруглении кривых в центре — возможно, он пользовался некой французской кривой. С помощью жены я, наконец, получил удовлетворительный результат, отдаленно напоминающий букву шестнадцатого века, но для ее получения я использовал эллипсы. Каждая граница каждой дуги моей буквы S состояла из эллипсов и прямых, определяемых (i) положением начальной и конечной точек, (И) наклоном прямой и (ш) требуемым левым краем этой кривой. Для вывода необходимых формул мне понадобилось три часа, и думаю, что Ньютон и Лейбниц получили бы удовольствие от работы над этой пробле-
мой. На рис. 12 приведены несколько вариантов буквы S с разными наклонами в центре, полученных по этой схеме, и я надеюсь, вам больше понравится средняя, потому что ее я в настоящее время и использую.
QQQQQQQ
Рис. 12. Различные варианты S, полученные при разных наклонах в центре, соответствующие коэффициентам 1/2, 2/3, 3/4, 1, 4/3, 3/2 и 2 относительно «правильного» наклона.
Семейства шрифтов
Для расширения системы METAFONT нужно, в сущности, написать программу, определяющую каждую литеру, на специальном дизайнерском языке описания движения пера и ластика. Мой коллега Р. У. Госпер заметил, что мы получили, таким образом, полную противоположность улице Сезам: вместо «эта программа была получена для вас буквой S» имеем «эта буква S была получена для вас программой». В программе задействовано около 20 параметров, управляющих толщиной волосяного пера, определяющих ширину прямых и кривых, которые должны быть нарисованы, и пропорции разных частей литер (высота х, высота верхних и нижних акцентов, ширина М, длина засечек и т. д.). Меняя эти параметры, мы получаем бесконечно много разных видов начертаний шрифтов, при этом они все родственны и гармонируют друг с другом.
На рис. 13, например, демонстрируется ряд этих возможностей. На рис. 13а приводится светлый прямой шрифт «modern» в традициях Bodoni and Bell и Scotch Roman. На рис. 13b дается соответствующее полужирное начертание, в котором волосяные линии несколько толще, а основные штрих соответственно чуть шире. Сделав волосяные линии и штрихи одной толщины и положив длину засечек равной нулю, получим шрифт без засечек (рубленый), который представлен на рис. 13с. Все эти примеры получены одной и той же программой, определяющей форму литер; варьировались лишь параметры. На самом деле шрифт, показанный на рис. 13с, должен иметь букву g иного рисунка (именно ‘д’), потому что в этом шрифте нижние выносные элементы очень короткие, но я оставил такое ‘g’, чтобы продемонстрировать параметрическое разнообразие. На рис. 13d приводится полужирный рубленый шрифт, в котором перо имело овальную форму, шире своей высоты. Я нахожу это начертание особенно привлекательным, в частности потому, что это вышло случайно —я писал программу так, чтобы два или три разных шрифта получались правильно, следовательно, вид всех остальных может быть каким угодно. Я не представляю, почему это начертание получилось таким удачным.
При подходящей установке параметров можно имитировать пишущую машинку с фиксированной шириной литер, как это показано на рис. 13е. Имеется
(a) Mathematical Mathematical ю Typography Typography
ы MathematicalMathematical ад
Typography Typography
и Mathematical Mathematical inTypographyTypography
(e) Mathematical MATHematieai ад TypographyTypography
Mathamthotl (,)
Typography
Рис. 13. Различные начертания шрифта, полученные изменением параметров программы МЕТА FONT: (a) Computer modern прямой; (b) Computer modern полужирный; (с) Computer modern рубленый; (d) Computer modern полужирный рубленый; (е) Computer modern типа пишущей машинки; (f) Computer modern наклонный; (g) Computer modern прямой капитель; (h) Computer modern прямой с капителью и co «строчной капителью»; (i) Computer modern забавный.
возможность наклонить литеры как на рис. 13f; здесь положение пера меняется, но сама форма пера не является наклонной, так что круглые точки остаются круглыми.
Другая установка параметров приводит к капители: вместо строчных букв используются прописные более мелкого размера; это показано на рис. 13g; капитель рисуется теми же перьями той же высоты, что и для строчных букв, но управляется программами для прописных букв. На рис. 13h показано то, чего никогда раньше не было на печати: это получается, если мы рисуем строчные буквы в виде капители. Это можно бы назвать «строчной капителью». Он неожиданно получился одним из наиболее приятных, только точки оказались великоватыми.
И наконец, на рис. 13i продемонстрирован вариант, полученный своеобразной установкой параметров.
Когда я работал ассистентом в фирме Caltech, секретариат отделения математики иногда направлял ко мне «чокнутых» визитеров и как-то раз один посетитель спросил, когда же, наконец, число тг будет вычислено «до самого конца». Я пытался ему объяснить, что тг число иррациональное, но до него это не доходило. Наконец, я показал ему листинг с вычислениями тг до 100000
десятичного знака и сказал, что эти вычисления еще не завершены. Если бы в то время уже была готова моя издательская система, я бы смог показать ему рис. 14!
3.141592 6 5 358970333
Рис. 14. Варьирование высотой, шириной и размером пера.
Рис. 14 иллюстрирует другой аспект шрифтового дизайна, именно, что различные размеры шрифта одного начертания нельзя получить одно из другого обычными оптическими преобразованиями. Высоты и ширины и толщина пера меняются с разной скоростью и хороший резчик шрифта создает каждый размер шрифта (кегль) отдельно. Я не претендую на то, что рис. 14 лучше всего демонстрирует изменение пропорций; нужно будет еще немало поэкспериментировать, прежде чем у меня выкристаллизуется мысль, что же все-таки требуется. Я просто хотел подчеркнуть, что изменение размера шрифта в нижних выносных элементах и тому подобных вещах, не такое простое дело, как это может показаться на первый взгляд, а система наподобие METAFONT’a способна довольно легко менять эти параметры и можно быстро провести визуальные эксперименты с установкой различных параметров. Дизайнеры шрифта тратили месяцы на то, чтобы их рисунки были перенесены на металлические носители, прежде чем они могли увидеть пробный оттиск. Одно из следствий этого состояло в том, что у них попросту не было времени уделить достаточно внимания всем этим математическим символам, греческим буквам и т. д., равно как и более общеупотребительным символам, так что наборщикам не оставалось ничего иного, как устраивать мешанину из имеющихся в их распоряжении символов разного размера. (Например, зачастую они были вынуждены использовать в индексах буквы иного начертания, с чем вы сталкивались.) При подходе, который я рекомендую, мы автоматически получаем соответствие всем символам, как только изменяем параметры.
От непрерывности к дискретности
Система МЕТА FONT должна была не только определять литеры на континууме заданной плоскости, но и выражать их в терминах дискретного растра. Такое представление литер квадратиками на миллиметровой бумаге имеет длинную историю, еще задолго до появления вычислительной техники и телевизоров. Например, нам всем хорошо знакомы образцы 19-го века вышивки крестом. Та же идея тонкого масштабирования многие века использовалась в гобеленах. Моя жена нашла в нашей домашней библиотеке образец, представленный на рис. 15, который был выткан на севере Норвегии примерно в 1500 г; изображено имя святого Томаса в стиле, имитирующем каллиграфию тех времен, и я уверен, что подобные примеры, предвосхитившие печатный пресс, можно найти еще где-нибудь.
Рис. 15. Письменный эквивалент этого растрового изображения представлен на норвежском гобелене из церкви Gildeskaal, выткано около 1500 г. [22, с. 116].
На рис. 16 показано, как МЕТА FONT производит одни и те же буквы с одними и теми же параметрами, но с разной разрешающей способностью растра. Сам по себе процесс оцифровки значительно более сложен, чем это может показаться на первый взгляд, и, прежде чем я смог получить удовлетворительные результаты, понадобилось обратиться к некоторым нетривиальным математическим понятиям. Очевидный подход состоит просто в рисовании или воображаемом рисовании литеры с бесконечной точностью и затем в ее «скруглении» посредством зачернения всех квадратов на миллиметровке, которые достаточно черны в исходном изображении, однако это абсолютно безуспешно.
Одна из причин безуспешности кроется в том, что три штриха, скажем, буквы ‘ш’, могут занять разное положение относительно сетки, так что первый штрих может оказаться, например, ширины три единицы, а второй — четыре. Это совершенно недопустимо, потому что наши глаза с легкостью уловят такое различие в толщине. Но МЕТА FONT все это пропустит, поскольку сначала оцифровывается само перо и затем это же оцифрованное перо используется для всех штрихов. Другая проблема состоит в том, что эти три штриха должны иметь одинаковые просветы, так что £ш’ будет плохо смотреться, если, к примеру, имеется 7 единиц между двумя первыми штрихами и 8 единиц между двумя последними. Таким образом, понадобилась программа, которая округляла бы эти точки так, чтобы подобное не случалось.
Я чрезвычайно доволен рис. 16, но на нем с очевидностью проступают несколько нерешенных проблем, которыми я собираюсь заняться, когда вернусь домой. В среднем примере предполагалось, что ширины кривых округляются до 2 при значении несколько большем, чем 1.5, тогда как ширина штриха округлялась до 1 при значении, лишь чуть меньшем 1.5. Поэтому кривые букв 'а’, ке’ и ‘с’ гораздо темнее, чем должно быть. Хвостик справа внизу у буквы ‘а’ должен быть удален в пятом примере, но при таком низком разрешении нет автоматического метода, который был бы вполне удовлетворительным.
Процесс оцифровки пера тоже далеко не тривиален. Предположим, например, что нам нужно круглое перо ширины 2 растровые единицы; понятно, что подходящим пером будет квадрат 2x2, который ближе всего к кругу при данном низком разрешении. Но заметьте, что мы не можем центрировать квадрат 2 х 2 на любом отдельном квадрате растра, поскольку ни один из этих
Рис. 16.Проблема корректировки букв при грубом растре. (Разрешения соотвественно 120, 60, 40, 30 и 24 пикселей на дюйм.)Рис. 16.Проблема корректировки букв при грубом растре. (Разрешения соотвественно 120, 60, 40, 30 и 24 пикселей на дюйм.)mathematics mathematics mathematics mathematics mathematics
Рис. 16.
Проблема корректировки букв при грубом растре. (Разрешения соотвественно 120, 60, 40, 30 и 24 пикселей на дюйм.)
Рис. 16.
Проблема корректировки букв при грубом растре. (Разрешения соотвественно 120, 60, 40, 30 и 24 пикселей на дюйм.)
четырех квадратов не находится в центре. Та же проблема возникает в случае пера, имеющего четную размерность. Один из способов решить эту проблему состоит в том, чтобы работать только с нечетными числами, но это было бы слишком большим ограничением. Поэтому METAFONT использует специальное правило округления для положения центра пера. Обобщая, предположим, что перо является эллипсом целой ширины w и целой высоты h; тогда, если перо имеет действительные координаты (х^уЬ его местом расположения на дискретной сетке будет
(L^-5(w)J,
где |jrJ обозначает наибольшее целое, меньшее или равное ж, а 5(четное) = |, 5(нечетное) = 0. Само перо, так позиционированное относительно начала координат, будет состоять из всех пар (ж, ?/) целых чисел, удовлетворяющих
2(Ж - 5(w)) V /2(у - 5(h)) у/25(w) 25(h)
w /\ h /\ w h
Рис. 17.Дискретные «эллиптические» перья для малых целых значений ширины и высоты.Рис. 17.Дискретные «эллиптические» перья для малых целых значений ширины и высоты.
Рис. 17.
Дискретные «эллиптические» перья для малых целых значений ширины и высоты.
Рис. 17.
Дискретные «эллиптические» перья для малых целых значений ширины и высоты.
ЖЖ(а)(Ь)(с)(а)(Ь)(с)Рис. 18.Трудности округления правильной дуги. (Три полуокружности радиуса 10, нарисованные пером 1x3.)Рис. 18.Трудности округления правильной дуги. (Три полуокружности радиуса 10, нарисованные пером 1x3.)
Ж
Ж
(а)(Ь)(с)
(а)(Ь)(с)
Рис. 18.
Трудности округления правильной дуги. (Три полуокружности радиуса 10, нарисованные пером 1x3.)
Рис. 18.
Трудности округления правильной дуги. (Три полуокружности радиуса 10, нарисованные пером 1x3.)
Эта формула — между прочим, не первая, которую я попробовал, — утверждает, что дискретное перо действительно будет ширины w единиц и высоты й, если w суть положительные целые. На рис. 17 показаны перья, полученные для малых значений w и h.
Еще одна проблема возникает, когда нам нужно, чтобы кривые после оцифровки выглядели прилично. На рис. 18а представлена полуокружность радиуса 10 единиц, нарисованная пером высоты 1 и ширины 3, когда правая кромка пера попадает точно в некоторую целую точку; тогда, перо будет твердо стоять па одном месте. С другой стороны, если эта правая кромка попадет точно на выпад некоторой целой точки, мы получим кривую как на рис. 18b, которая выглядит слишком плоской. Идеальный вариант показан на с. 18с, где правая кромка приходится точно на середину между целыми числами. Следовательно, программа МЕТА FONT корректирует положение кривых на растровой сетке до того, как приступить к рисованию этих кривых, форсируя благоприятную ситуацию рис. 18с; Настоящая форма букв слегка меняется, чтобы она адаптировалась к требуемому размеру растра наиболее благоприятным образом.
Еще одна проблема возникает тогда, когда размер пера увеличивается так, чтобы ребра кривой, которую оно рисует, были монотонными, если перо было нарисовано с бесконечной точностью, хотя независимое округление положения пера и его ширины приводит к тому, что эта монотонность исчезает. Эта проблема возникает весьма редко, по, когда это случается, глаз не может этого не заметить. Рассмотрим, к примеру, вполне линейную ситуацию на рис. 19, где каждое уменьшение у на одну единицу сопровождается возрастанием я; на .3 единицы и увеличением ширины w.nepa на .2 единицы. Имеется в виду, что высота пера —очень малая постоянная, но в дискретном случае высота пера берется равной 1. Слегка затененные участки на рис. 19 показывают истинную предполагаемую форму, но более темные квадраты соответствуют оцифровке, приводящей к немонотонной левой границе. МЕТА FONT компенсирует такого рода проблему посредством поддержания следа искомой границы, когда ширина пера меняется, рисуя точки дважды (например, й (х^у^ и (х — 1,?/)), когда нужно проследить за правильностью границы. Иначе говоря, иногда от идеи округления положения пера и его ширины независимо друг от друга эффективнее отказаться.
И последняя проблема оцифровки, которую мне пришлось решать, состояла в том, чтобы сделать левую половину цифры ‘0’ зеркальным отражением ее правой половины, открывающую левую скобку — зеркальным отражением ле-
(3.5,
0.5,10.5)
(3, 0,10)
(3.7,
0.8, 9.5)
(3, 0, 9)
(3.9,
1.1, 8.5)
(3, 1, 8)
(4.1,
1.4, 7.5)
(4, 0, 7)
(4.3,
1.7, 6.5)
(4, 1, 6)
(4.5,
2.0, 5.5)
(4, 1, 5)
(4.7,
2.3, 4.5)
(4, 1, 4)
(4.9,
2.6, 3.5)
(4, 2, 3)
(5.1,
2.9, 2.5)
(5, 2, 2)
(5.3,
3.2, 1.5)
(5, 3, 1)
(5.5,
3.5, 0.5)
(5, 3, 0)
Ширина пера и
Округленные ширина
его положение
и положение пера
Рис. 19. Потеря монотонности при независимом округлении,(округление делается из (ш^у) в ([wj, [ж — 5(LWJ)J> Lx/J)-)
вой закрывающей скобки и т. п. Это было достигнуто благодаря специальным программам METAFONT’a, выбирающим в таких случаях центральную точку, которая либо в точности равна целому числу, либо целому плюс |, и введением правила двойного округления, которое обладает свойством полной симметрии.
Иные подходы
Как я уже говорил, я думаю, что система МЕТА FONT успешно справляется с определением букв и других символов, но, возможно, благодаря новым разработкам можно будет построить еще лучшие процедуры. Некоторые ограничения моих кубических сплайнов приведены на рис. 20. В части (а) этого рисунка изображена пятиконечная звезда и слово «mathematics», написанное (в некотором приближении) моей рукой; приближение сделано при помощи сегментов прямых, так что вы можете отчетливо видеть, какие точки были предложены в виде исходных данных моей подпрограмме вычисления сплайнов. В части (Ь) показано, как, возможно, преобразится мой почерк, когда я стану старше; это получилось просто установкой г = s = 2 во всех сегментах сплайна, из чего с очевидностью заключаем, что тангенсы углов предопределены системой. В части (с) изображено нечто более стройное; это было получено установкой везде г = s = 1/2. Рисунок 20d подобен рис. 20с, но он был нарисован посредством комбинации пера и ластика. Такая комбинация может привести к интересным эффектам, так что изображенная здесь звезда —мой запоздалый дар к двухсотлетию Америки.
Когда используется выведенная ранее общая формула для параметров т и s, мы получаем то, что изображено на рис. 20е, в котором звезда превратилась в замечательную аппроксимацию круга (как я предполагал). Для этого рисунка использовалось более толстое перо со слегка наклонным нажимом. Хотя мой почерк чрезвычайно некрасив, все же некоторые дефекты на рис. 20е можно было бы, вероятно, сгладить, если бы был применен другой подход.
Рис. 20.Примеры применения кубических сплайнов к небрежному почерку.Рис. 20.Примеры применения кубических сплайнов к небрежному почерку.
Рис. 20.
Примеры применения кубических сплайнов к небрежному почерку.
Рис. 20.
Примеры применения кубических сплайнов к небрежному почерку.
Самым интересным подходом с математической точки зрения могла бы быть задача нахождения кривой данной длины, минимизирующей интеграл от квадрата кривизны относительно длины дуги. Этот интеграл пропорционален энергии натяжения механического сплайна данной длины (иными словами, энергии в толстой перекладине или балке), проходящего через данные точки, так что, по-видимому, это подходящая величина для минимизации. В работе [32] Майкл Малкольм приводит свой взгляд на эту вариационную задачу. Норвежский математик Ивен Мехлум [37] показал, что если мы зададим дугу фиксированной длины между’последовательным и точками, оптимальная кривая будет линейно менять кривизну как ax-Vb'y-Vc в точке (ж, у),ион предложил выбирать константы, взяв Ъ/а = (т/2 — Z/iVC^ — ^1) между (^ц, тд) и (а;2, 7/2)1 и потребовав, чтобы наклон и кривизна были непрерывными в конечных точках. Такой подход, наверное, потребует значительно больше вычислений, чем рекомендованные здесь кубические сплайны, но он может привести к лучшим кривым, удовлетворяющим, возможно, свойству расширяемости.
Другим интересным подходом к рисованию кривых, который особенно может быть полезен при имитации почерков, является метод «фильтрации», недавно предложенный Майклом Патерсоном из Уорвикского университета (неопубликовано). Для получения гладкой кривой, проходящей через точки z^, предположим, что эти точки примерно равномерно расположены на искомой кривой и что г к = ^пА-к для всех целых к. Тогда можно просто написать
- к) / £(-i)fc/(z - fc)к к
где /(^) есть нечетная функция порядка t-1 при t —* 0, быстро уменьшающаяся при удалении от нуля. Например, мы можем взять
/№ = csch t = 2/(е* - е ^ .
У меня не было времени поэкспериментировать с методом Патерсона или попробовать увязать его с рисованием букв. Легко видеть, что производная т&) = /(1)(^+1 - Zk-1) - /(2)(гк+2 - Zk-^ нимеет направление z^i-zk_x.
Рандомизация
В заключение, я хотел бы рассказать о небольшом эксперименте, который я провел со случайными числами. Кто-то мог бы выразить недовольство по тому поводу, что созданные мною буквы слишком совершенны, слишком похожи на компьютерное изделие и лишены «характера». Чтобы нейтрализовать это, мы можем заложить некоторую долю случайности при выборе того, где поместить перо, когда мы рисуем каждую литеру, и на рис. 21 показано, что при этом происходит. Координаты ключевых положений пера были выбраны независимо согласно нормальному распределению и с возрастающим стандартным отклонением, так что в третьем примере стандартное отклонение было вдвое больше, чем во втором, в четвертом — в три раза больше и т. д. Обратите внимание, что две буквы m в каждой строке разные (за исключением первой строки), то же самое можно сказать относительно букв а и t, поскольку каждая буква была нарисована случайным образом.
При достаточно большом отклонении результаты выглядят чрезвычайно нелепо. Я не хочу, чтобы люди говорили, что я завершил эту лекцию пародией
Рис. 21.Возрастающее случайное отклонение положения пера; ст = 0,1, 2,... .Рис. 21.Возрастающее случайное отклонение положения пера; ст = 0,1, 2,... .mathematics mathematics mathematics mathematics mathematics mathematics mathematics mathematics TCiath erratics mathematics mathematics mathematics
Рис. 21.
Возрастающее случайное отклонение положения пера; ст = 0,1, 2,... .
Рис. 21.
Возрастающее случайное отклонение положения пера; ст = 0,1, 2,... .
mathematicsmathematicsmathematicsmathamtiUQSmathematicsmathematicsmathematicsmathamtiUQSРис. 22.Небольшой уровень рандомизации, введенный в разные начертания шрифта.Рис. 22.Небольшой уровень рандомизации, введенный в разные начертания шрифта.
mathematicsmathematicsmathematicsmathamtiUQS
mathematicsmathematicsmathematicsmathamtiUQS
Рис. 22.
Небольшой уровень рандомизации, введенный в разные начертания шрифта.
Рис. 22.
Небольшой уровень рандомизации, введенный в разные начертания шрифта.
на математику. Так что лучше в заключение взглянем на рис. 22, в котором представлены разные шрифты, когда уровень рандомизации управляем. Думаю, можно сказать, что от этого завершающего примера веет теплом и обаянием, особенно если учесть тот факт, что на самом деле они были сгенерированы компьютером по строгим математическим законам. Возможно, в старые добрые времена математические издания были такими красивыми потому, что типографский шрифт был несовершенным и нелогичным.
Заключение
В заключение я бы хотел вывести мораль из этого длинного повествования. Мои эксперименты на протяжении последних нескольких месяцев живо иллюстрируют тот факт, что многочисленные математические задачи все еще ждут своего решения почти везде, где бы они ни возникли, особенно в тех областях реальной жизни, где математика редко раньше применялась. Математики могут предоставить решения этих проблем и «убить двух зайцев» — получить удовольствие от работы за гранью математики и заслужить благодарность людей, которые смогут воспользоваться этими решениями. Так что давайте будем идти дальше и применять математику в новых направлениях.
Благодарности
Я хотел бы поблагодарить мою жену Джил за многие важные предложения, которые она внесла на стадии критического обсуждения этого исследования, а также Лео Гуиба и Лили Рамшоу за оказанную ими помощь в изготовлении иллюстраций в Исследовательском центре Xerox в Пало Альто. Лестера Ене- ста, Майкла Фишера, Фрэнка Лианга, Тома Лича, Алберта Мейера, Майкла Патерсона, Майкла Плэсса, Боба Спрулла, Джина Тейлора, Ганса Вольфа я благодарю за полезные идеи, обсуждения и письма, касающиеся этого предмета. Я также признателен Гордону Уолкеру за проверку моих предположений относительно полиграфического прошлого Transactions и обеспечение дополнительной информацией, а также профессору Дирку Сифкесу за его помощь при отыскании рис. 4с и 11с и библиотеке Kunstbibliothek Berlin der Staatlichen Museen Preussischer Kulturbesitz — за разрешение их опубликовать, а Андре Жамму —за разрешение опубликовать рис. би Не.
Литература, не упомянутая в тексте
В приведенный ниже список литературы включено несколько статей, не упомянутых в основном тексте, именно обсуждение публикаций Американского института физики [38]; отдельные эксперименты по набору физических журналов при помощи системы, разработанной в Лаборатории Белла [7, 31]; использование компьютера для верстки и редактирования технических журналов, снабженное кратким предложением по стандартному языку набора [11]; отчет о ранних компьютерных программах для генерации литер и набора математических текстов [23, 30, 35, 36, 41, 47]; описание правил математического набора, которые должен был знать печатник [9]; три стандартных издания по набору математических текстов [14, 48, 50]; некоторые типографские шрифты и спецзнаки, разработанные в Американском математическом обществе [40]; новый и очень важный подход к математическому описанию традиционных полиграфических правил на основе конических сечений и одномерных сплайнов [15]; предложение нового способа управления пробелами между литерами, основанного на неких математических идеях [28]; и две чисто математические работы, навеянные полиграфией [13, 21].
Это исследование было частично финансировано фондом National Science Foundation по гранту MCS72-03752 АОЗ и министерством Office of Naval Research по контракту N00014- 76-С-0330.
Литература
А. V. Aho, S. С. Johnson, and J. D. Ullman, “Typesetting by ACM considered harmful,” Communications of the ACM 18 (1975), 740.
American Mathematical Society, Development of the Photon for Efficient Mathematical Composition, Final Report (10 May 1965), National Science Foundation Grant G-21913; NTIS No. PB168627.
American Mathematical Society, Development of Computer Aids for TapeControl of Photocomposing Machines, Report No. 2 (July 1967), “Extension of the system of preparing a computer-processed tape to include the setting of multiple line equations,” National Science Foundation Grant GN-533; NTIS No. PB175939.
American Mathematical Society, Development of Computer Aids for TapeControl of Photo composing Machines, Final Report, Section В (August 1968), “A system for computer-processed tape composition to include the setting of multiple line equations,” National Science Foundation Grant GN-533; NTIS No. PB179418.
American Mathematical Society, Development of Computer Aids for TapeControl of Photocomposing Machines, Final Report, Section C (January 1969), “Implementation, hardware, and other systems,” National Science Foundation Grant GN-533; NTIS No. PB182088.
American Mathematical Society, To Complete the Study of Computer Aids for Tape-Control of Composing Machines by Developing an Operating System,
Final Report No. AMATHS-C AIDS-71-0 (April 1971), National Science Foundation Grant GN-690; NTIS No. PB200892.
American Physical Society, “APS tests computer system for publishing operations,” Physics Today 30,12 (December 1977), 75.
Donald M. Anderson, “Cresci and his capital alphabets,” Visible Language 4 (1971), 331-352.
J. Woodard Auble, Arithmetic for Printers, 2nd edition (Peoria, Illinois: Bennett, 1954).
Michael P. Barnett, Computer Typesetting: Experiments and Prospects (Cambridge, Massachusetts: M.I.T. Press, 1965).
Robert W. Berner and A. Richard Shriver, “Integrating computer text processing with photocomposition,” IEEE Transactions on Professional Communication PC-16 (1973), 92-96. Эта статья представляет собой перепечатку другой работы о наборе и верстке из Robert W. Berner, “The role of a computer in the publication of a primary journal,” Proceedings of the AFIPS National Computing Conference 42, Part II (1973), M16-M20.
Peter J. Boehm, “Software and hardware considerations for a technical typesetting system,” IEEE Transactions on Professional Communication PC- 19 (1976), 15-19.
J. A. Bondy, “The ‘graph theory’ of the Greek alphabet,” in Graph Theory and Applications, edited by Y. Alavi et al. (Berlin: Springer Verlag, 1972), 43-54.
T. W. Chaundy, P. R. Barrett, and Charles Batey, The Printing of Mathematics (Oxford: Oxford University Press, 1954).
P. J. M. Coueignoux, Generation of Roman Printed Fonts, Ph.D. thesis, Dept, of Electrical Engineering, Massachusetts Institute of Technology (June 1975).
Giovanni Francesco Cresci Milanese, Essemplare de Piv Sorti Lettere (Rome: 1560). См. также издание в переводе и под редакцией Arthur Sidney Osley (London: 1968).
T. L. De Vinne, The Practice of Typography: Modern Methods of Book Composition (New York: Oswald, 1914).
Albrecht Durer, Underweysung der Messung mit dem Zirckel und Richtscheyt (Nuremberg: 1525). Английский перевод раздела об алфавитах опубликован в книге Albrecht Diirer, Of the Shaping of Letters, перевод R. T. Nichol (Dover, 1965).
Felice Feliciano Veronese, Alphabetum Romanum, edited by Giovanni Mardersteig and translated by R. H. Boothroyd (Verona: Officina Bodoni, 1960).•
Frederic W. Goudy, Typologia: Studies in Type Design & Type Making with Comments on the Invention of Typography, the First Types, Legibility & Fine Printing (Berkeley, California: University of California Press, 1940).
F. Harary, “Typographs,” Visible Language 7 (1973), 199-208.
Roar Hauglid, Randi Asker, Helen Engelstad, and Gunvor Traetteberg, Native Art of Norway (Oslo: Dreyer, 1965).
A. V. Hershey, “Calligraphy for computers,” NWL Report No. 2101 (Dahlgren, Virginia: U.S. Naval Weapons Laboratory, August 1967); NTIS No. AD662398.
Andre Jammes, La Reforme de la Typographic Royale sous Louis XIV (Paris: Paul Jammes, 1961).
Paul E. Justus, “There is more to typesetting than setting type,” IEEE Transactions on Professional Communication PC-15 (1972), 13-16, 18.
Alan C. Kay, “Microelectronics and the personal computer,” Scientific American 237,3 (September 1977), 230-244.
Brian W. Kernighan and Lorinda L. Cherry, “A system for typesetting mathematics,” Communications of the ACM 18 (1975), 151-157.
David Kindersley, Optical Letter Spacing for New Printing Systems (London: Wynkyn de Worde Society, 1976).
Donald E. Knuth, “Tau Epsilon Chi, a system for technical text,” Stanford Computer Science Report CS675 (September 1978). Исправленное издание, T^X, A System for Technical Text (Providence, Rhode Island: American Mathematical Society, 1979); опубликовано также как часть 2 работы Т^Х and METAFONT: New Directions in Typesetting (Bedford, Massachusetts: Digital Press, 1979).
Dorothy K. Korbuly, “A new approach to coding displayed mathematics for photocomposition,” IEEE Transactions on Professional Communication PC- 18 (1975), 283-287.
M. E. Lesk and B. W. Kernighan, “Computer typesetting of technical journals on UNIX,” Computer Science Technical Report 44 (Murray Hill, New Jersey: Bell Laboratories, June 1976).
Michael A. Malcolm, “On the computation of nonlinear spline functions,” SIAM Journal on Numerical Analysis 14 (1977), 254-282.
J. R. Manning, “Continuity conditions for spline curves,” The Computer Journal 17 (1974), 181-186.
Giovanni Mardersteig, The Alphabet of Francesco Torniello da Novara [1517] Followed by a Comparison with the Alphabet of Fra Luca Pacioli (Verona: Officina Bodoni, 1971).
M. V. Mathews and Joan E. Miller, “Computer editing, typesetting, and image generation,” Proceedings of the AFIPS Fall Joint Computer Conference 27 (1965), 389-398.
M. V. Mathews, Carol Lochbaum, and Judith A. Moss, “Three fonts of computer drawn letters,” Communications of the ACM 10 (1967), 627-630.
Even Mehlum, “Nonlinear splines,” in Computer Aided Geometric Design, edited by Robert E. Barnhill and Richard F. Riesenfeld (New York: Academic Press, 1974), 173-207.
A. W. Kenneth Metzner, “Multiple use and other benefits of computerized publishing,” IEEE Transactions on Professional Communication PC-18 (1975), 274-278.
Joseph Moxon, Regulae Trium Ordinum Literarum Typographicarum: or the Rules of the Three Orders of Print Letters: viz. The {Roman, Italick, English} Capitals and Small. Shewing how they arc compounded of Gcomctrick Figures, and mostly made by Rule and Compass (London: Joseph Moxon, 1676).
Phoebe J. Murdock, “New alphabets and symbols for typesetting mathematics,” Scholarly Publishing 8 (1976), 44-53. Перепечатано в Notices of the American Mathematical Society 2 4 (1977), 63-67.
Nicholas Negroponte, “Raster scan approaches to computer graphics,” Computers and Graphics 2 (1977), 179-193.
Wolfgang A. Ocker, “A program to hyphenate English words,” IEEE Transactions on Engineering Writing and Speech EWS-14 (1971), 53-59.
Luca Pacioli, Diuina proportione, Opera a tutti glingegni perspicaci e curiosi necessaria Oue ciascun studioso di Philosophia: Propectiua Pictura Sculptura: Architectural Musica: e altre Mathematice: suauissima: sottile: e admirable doctrina consequira: e delectarassi: co uarie questione de secretissima scientia (Venice: 1509).
Giovanbattista Palatino Cittadino Romano, Libro Primo del le Lettere Maiuscolc Antiche Romane (неопубликовано), Berlin Kunstbibliothek, MS OS5280. Отдельные статьи датированы 1543, 1546, 1549, 1574 или 1575 г. См. James Wardrop, “Givis Romanus Sum: Giovanbattista Palatino and his circle,” Signature 14 (1952), 3-39.
Paul A. Parisi, “Composition innovations of the American Society of Civil Engineers,” IEEE Transactions on Professional Communication PC-18 (1975), 244-273.
R. G. D. Richardson, “The twenty-ninth annual meeting of the Society,” Bulletin of the American Mathematical Society 29 (1923), 97-116. (См. также 28 (1922), 234-235, 378 относительно комментариев в специальном томе Transactions и 28 (1922), 2-3 относительно обсуждения дефицита в бюджете, вызванного ростом расходов на печать.)
Glenn Е. Roudabush, Charles R. Т. Bacon, R. Bruce Briggs, James A. Fierst, Dale W. Isner, and Hiroshi A. Noguni, “The left hand of scholarship: Computer experiments with recorded text as a communication media,” Proceedings of the AFIPS Fall Joint Computer Conference 2 7 (1965), 399-411.
Ellen E. Swanson, Mathematics into Type (Providence, Rhode Island: American Mathematical Society, 1971).
Geofroy Tory, Champs Fleury (Paris: 1529). Also translated into English and annotated by George B. Ives (New York: Grolier Club, 1927).
Karel Wick, Rules for Typesetting Mathematics, перевод V. Boublik and M. Hejlova (The Hague: Mouton, 1965).
Hermann Zapf, About Alphabets: Some Marginal Notes on Type Design (Cambridge, Massachusetts: M.I.T. Press, 1970).
Добавление
После прочтения этой лекции я с удовлетворением обнаружил по состоявшимся впоследствии обсуждениям и переписке, что нс существует такой непреодолимой пропасти между математиками и художниками, как это себе воображают многие. Например, Дьёрдь Пойа говорил мне в 1979 г., что он был хорошо знаком с работой Дюрера [18].
Журнал Transactions of the American Mathematical Society начал с тома 302 (1987) предлагать авторам представлять свои статьи в формате ТЁХ. Все дальнейшие тома Transactions вплоть до тома 311 (1989) были подготовлены в ТЁХ’е.
Разумеется, с тех пор, как была написана данная работа, я очень много узнал о полиграфии. Положенные в основу ТЁХ’а понятия боксов и клея остались в сущности теми же, но основанный на методах рисования кривых METAFONT изменился полностью. Новый METAFONT, завершенный к 1984 г., все еще использует кубические сплайны, но они управляются более интуитивными «контрольными точками», предложенными Полем Кастейжо и Пьером Безье в 1959 —начале 1960-х годов. Алгоритмы вычерчивания кривых значительно выигрывают в скорости при переходе от порядка т? к порядку тц когда имеется п пикселей на дюйм. Варьирование шириной как на рис. 19 и прием с одновременным использованием пера и ластика (рис. 20d) больше не поддерживается, но их на самом деле никогда не собирались использовать.
Значительно лучшие правила округления для пера с интересными связями с теорией чисел и геометрией были разработаны Джоном Хобби в его докторской диссертации Digitized Brush Trajectories (Stanford University, 1985). Хобби также придумал новые правила получения «самой приятной кривой», проходящей через последовательность данных точек. Превосходство этих методов было подтверждено многочисленными экспериментами, но текущее решение средствами METAFONT’a задачи «математического почерка» (рис. 20) остается, надо признать, весьма эксцентричным: вывод, который теперь дает METAFONT при задании параметров, приводивших в 1977 г. к рис. 20е, выглядит так:
От точек, столь неуклюже заданных мной на рис. 20а, на самом деле и не следовало ожидать превращения в изящные буквы.
Верстка абзацев
[В соавторстве с Майклом Ф. Плэссом. Впервые опубликовано в Software — Practice and Experience 11 (1981), 1119-1184.]
В данной работе рассматривается задача о наилучшей верстке абзаца, т. е. о разбиении текста, образующего абзац, на строки равной или почти равной длины. Особенность предлагаемого подхода состоит в том, чтобы не просто формировать строки последовательно одну за другой, а оценивать качество принимаемых решений для всего абзаца в целом — в этом случае окончательный вид каждой строки зависит не только от того, как были сформированы предыдущие строки, но и от последующих строк. Построенный общий алгоритм поиска оптимальных точек разбиения текста на строки вполне удовлетворительно решает задачу верстки для весьма широкого круга полиграфических приложений. В его основе лежат три базовых понятия: «боксы», «клей» и «штрафы». Благодаря рациональному применению схемы динамического программирования алгоритм позволяет избежать так называемого бэктрекинга (перебора с возвратами). Накопленный практический опыт свидетельствует о том, что алгоритм позволяет осуществлять качественную верстку текста при относительно небольших вычислительных затратах. В конце статьи приведена краткая история методов абзацной верстки, а в приложении описан упрощенный вариант алгоритма, требующий меньших компьютерных ресурсов.
Введение
Чтобы вывести текст на печать или на экран, его требуется специальным образом подготовить, в частности, необходимо так или иначе разбить абзацы на строки, или, как говорят в таких случаях, сверстать абзацы. Качество выполнения этой операции чрезвычайно важно. При хорошем разбиении абзацев на строки читатель практически не замечает, что слова, составляющие текст, оказались в некоторых местах оторванными друг от друга, а образовавшиеся куски помещены в стесняющую их и, в общем-то, неестественную прямоугольную рамку. Если же верстка выполнена неудачно, внимание читателей отвле-
кается на некрасивый вид строк и мешает следить за ходом авторской мысли. Выбрать хорошее место для завершения строки бывает совсем не просто. Например, при верстке газетных полос, когда текст должен быть напечатан в виде нескольких довольно узких столбцов, возможных мест для разрыва строк оказывается очень мало. Наличие в тексте математических формул может при любой ширине текстового поля порождать проблемы с выбором точки перехода на новую строку. Впрочем, и в относительно простых случаях типа беллетристического повествования хорошая верстка делает книгу более красивой и потому более привлекательной. Известно, что многие авторы более тщательно работают над своими произведениями, если нм гарантируют первоклассный внешний вид будущей книги.
Неформальное английское название процедуры верстки абзацев — «justification». Это то, что скрывается за буквой J в аббревиатуре Н & J (hyphenation and justification), используемой в современных текстовых процессорах и коммерческих системах компьютерной верстки. Однако применительно к абзацам этот термин нельзя признать удачным, поскольку в традиционной полиграфии «justification» относится к процессу набора отдельной строки и означает корректировку междусловных пробелов для придания строке требуемой длины. Даже в том случае, когда текст набирается с «рваным» правым краем, т. е. в невыровненном (unjustified) виде, его все равно требуется разбивать на строки примерно равной длины. При традиционном наборе на основе металлических шрифтов подобрать междусловные пробелы так, чтобы не только левая, но и правая граница текста выглядела как прямая линия —дело довольно-таки трудоемкое. Поэтому технологии верстки абзацев, применявшиеся в прошлом веке, в основе своей были процессами простого выравнивания. При верстке на компьютере расставить каким угодно образом пробелы между словами не составляет труда, поэтому в наше время главным содержанием процесса верстки стало разбиение абзацев на строки. В результате происшедшего изменения сути процесса верстки соответствующим образом изменился и смысл слова «выравнивание». Тем не менее в настоящей работе мы пользуемся термином разбиение на строки^ чтобы подчеркнуть, что главной проблемой в нашем случае является нахождение удачных точек перехода на новую строку. *
Традиционный способ разбиения текста на строки аналогичен тому, как производится переход на новую строку при работе на пишущей машинке: при приближении к правому полю звенит звоночек (нс обязательно в буквальном смысле слова), и по этому сигналу мы выбираем лучшее, на наш взгляд, место для завершения строки, не думая о том, где в результате этого действия может закончиться следующая строка, и подавно не заботясь о последующих строках. После того как каретка переместилась в крайнее левое положение, мы начинаем все заново, не стремясь помнить что-нибудь из предыстории, помимо точки начала текущей строки. При таком способе действий нс требуется
запоминать много информации; этот простейший алгоритм идеально подходит для человека и может быть легко запрограммирован для компьютера.
При типографском наборе междусловные пробелы имеют переменную величину— в этом состоит его важнейшее отличие от печатания на пишущей машинке. На практике обычно задают минимум и максимум допустимой величины пробела, а также его нормальное значение — то, чему в идеале должны быть равны все пробелы. Стандартный алгоритм разбиения текста на строки (см., например, [4, с. 55]) состоит в следующем. Слова, разделенные нормальным пробелом, последовательно вносятся в текущую строку до тех пор, пока не окажется, что очередное слово в ней уже не умещается. Переход на новую строку производится после этого слова, если его удается уместить в строке при уменьшении всех пробелов в допустимых пределах, т. е. при значении пробелов, нс меньшем установленного минимума. В противном случае делается попытка перехода на новую строку перед тем же самым словом, для чего все пробелы, возможно, увеличиваются, но так, чтобы их общее значение не превышало установленного максимума. Если и это не удается, злополучное слово разбивается на две части согласно правилам переноса, причем в текущей строке оставляют по возможности большую его часть. Наконец, если подходящего переноса в данном слове не существует, строку обрывают все-таки перед рассматриваемым словом, приняв в качестве результата «жидкую» строку, где пробелы больше заданного максимума.
При компьютерной верстке нецелесообразно ограничиваться столь примитивной процедурой, поскольку, как правило, в памяти компьютера вполне умещается информация о целом абзаце. Вычислительные эксперименты показали, что качество верстки значительно улучшается, если при разбиении абзаца на строки учитывать не только вид очередной строки, но и те последствия, которые может иметь для последующих строк абзаца принятие того или иного решения по любой строке. Используя компьютерные алгоритмы прогностического типа, удается не только избежать «жидких» строк, но и заметно сократить число переносов в словах. Тем самым алгоритмы верстки абзацев дают еще один пример эффективности принципа «динамического связывания». ■ Одним из главных стимулов к использованию компьютеров в полиграфии является относительная дешевизна компьютерного набора. При этом, однако, хочется, чтобы издания, подготовленные на компьютере, выглядели не хуже (и не дешевле) тех, что создаются при помощи традиционной технологии. На самом деле хорошо составленная компьютерная программа должна решать задачу верстки абзацев лучше, чем квалифицированный наборщик (если только последнему не будет разрешено ради повышения качества набора менять порядок слов в предложениях). Сошлемся, например, на работу Дункана [14], в которой приведен результат анализа 958 строк текста, набранного вручную в «одном из наиболее уважаемых издательств», название которого не уточ-
няется. Оказалось, что почти 5% из этих строк—«жидкие»: междусловные пробелы в них превышают 10 единиц (т. е. 10/18 величины еш), а в двух строках пробелы больше, чем 13 единиц. Как мы увидим в дальнейшем, хороший компьютерный алгоритм верстки позволяет получить лучшие результаты.
Компьютерный алгоритм не только позволяет избежать «жидких» строк и лишних переносов в словах, но и дает возможность сделать так, чтобы междусловные пробелы были в основном близки к нормальному значению, а их приближение к верхней или нижней границе промежутка допустимых значений происходило редко. Кроме того, можно попытаться добиться, чтобы пробелы в любых двух соседних строках не сильно отличались друг от друга, чтобы переносы не встречались в двух подряд идущих строках, чтобы предпоследняя строка абзаца не заканчивалась переносом, чтобы пустое пространство в последней строке не было слишком большим, и т. д. Для этого достаточно математически связать с каждым разбиением абзаца на строки некоторую количественную оценку качества этого разбиения и затем попросить компьютер найти то разбиение, которое доставляет экстремум построенной функции.
Но можно ли в действительности эффективно решить поставленную задачу на компьютере? Ведь если абзац имеет п точек возможного перехода на новую строку, то число возможных способов разбиения абзаца на строки равно 2^ — даже самый быстродействующий компьютер не сможет перебрать все эти способы за разумное время. Заметим, что задача о разбиении абзаца на строки с как можно менее отличающимися друг от друга длинами подозрительно напоминает пресловутую задачу о рюкзаке, которая, как известно, является NP-сложной [16]. Выручает то обстоятельство, что соседние строки состоят из смежных кусков информации, составляющей абзац. В силу этого к задаче о разбиении абзаца на строки применимы методы дискретного динамического программирования [6, 20], т. е. действительно имеется достаточно эффективный способ ее решения. Мы увидим, что объем вычислений, требуемых для практического нахождения оптимального разбиения абзаца, всего лишь примерно вдвое превышает объем вычислений, выполняемых при использовании традиционного алгоритма. Иногда новый метод оказывается даже более быстрым, чем старый, если учитывать время, сэкономленное на том, что отпадает потребность в частых переносах. Кроме того, новый алгоритм позволяет добиваться и других полезных эффектов, например, укорачивать или удлинять абзац на строку, что бывает очень полезно при верстке страниц.
Формулировка задачи
Приведем строгую математическую формулировку задачи о разбиении абзаца на строки. Будем использовать основные понятия и термины системы TJ% [26] (хотя и в несколько упрощенном виде, с тем чтобы тонкости полиграфического набора не мешали пониманию основных принципов абзацной верстки)
Для целей настоящей работы абзац удобно отождествлять с последовательностью xix«2...xm из т элементов, в которой каждый элемент х^ есть либо бокс, либо клей, либо штраф.
Бокс —это нечто, что должно быть напечатано: либо символ, взятый из некоторого шрифта, либо черный прямоугольник (сюда относятся горизонтальные и вертикальные линии), либо объект, построенный из нескольких символов (например, буква с диакритическим знаком — акцентом, или математическая формула). Содержимое бокса может быть чрезвычайно сложным, или же, наоборот, совсем простым; алгоритм разбиения на строки не заглядывает внутрь бокса — его не интересует, что там находится. Поэтому боксы можно считать запертыми и герметичными. Для нас важна только одна характеристика бокса — его ширина. Если элемент 2^ абзаца представляет собой бокс, то ширина этого бокса ин — вещественное число, равное величине места, которое бокс занимает в строке. Ширина бокса может быть нулевой; более того, она может быть и отрицательной, хотя отрицательными длинами следует пользоваться вдумчиво и с осторожностью, строго соблюдая приведенные ниже правила.
Под клеем понимается пустое пространство, которое может определенным образом изменять свою протяженность в строке, своего рода эластичный герметик, используемый для заполнения горизонтальных промежутков между боксами. С точки зрения алгоритма разбиения абзаца на строки элемент Жг, являющийся клеем, характеризуется тройкой вещественных чисел (Wi, Pi, ^\.
ин — «идеальная», или «нормальная», величина пробела; pt —«растяжимость»;
Zi — «сжимаемость».
Так, например, промежутки между словами в строке часто определяются значениями ин = |em, pi = ^em, Zi = |еш, где em — некоторое стандартное для используемого шрифта расстояние (в классических шрифтовых наборах приблизительно равное ширине прописной буквы ‘М’). Точные значения междусловных пробелов, заполняемых клеем, определяются при окончательном выравнивании строки по правому краю. Если нормальная ■ величина пробела слишком мала, его увеличивают пропорционально р^ а если слишком велика, — уменьшают пропорционально Zi. При соблюдении некоторых естественных ограничений, смысл которых разъясняется ниже, числа w^, р^ Zi могут быть отрицательными. Например, отрицательное значение w^ означает возврат на один шаг («возврат каретки на одну позицию»). При pi = Zi = 0 клей имеет фиксированную протяженность Wi. Кстати говоря, «клей», возможно, не самый удачный термин, поскольку ассоциируется с чем-то жидким и пачкающимся. По-видимому, лучше было бы использовать слово «пружина», так как именно металлические пружины способны заполнять пространство растягиваясь и сжимаясь, т. с. обладают в точности теми свойствами, которые требуются в нашей модели. Мы, однако, будем и дальше использовать термин «клей», поскольку он был введен в употребление еще в 1977 г., на ранней стадии развития TgX’a, к нему привыкли и многим он нравится. Пользователи Т^Х’а часто
называют комочек клея просто пробелом (skip), поэтому, наверное, лучше было бы говорить не о боксах и пружинах и не о боксах и клее, а о боксах и пробелах. Но в конечном счете дело не в названии, и конкретным выражением обсуждаемого понятия в любом случае остается тройка чисел (wj, щ, z^.
Штрафы задают в абзаце потенциально возможные места перехода от очередной строки к следующей и указывают степень «желательности» или «нежелательности» этого перехода в терминах некоторой «эстетической оценочной функции». С каждым элементом т^, представляющим собой штраф, связано некоторое число рг) также называемое штрафом, которое показывает, стоит ли в текущей точке переходить на новую строку. Остановимся на этом подробнее. Говоря в самых общих чертах, большое положительное pi означает, что в данной точке переход на новую строку нежелателен, а отрицательное pi — что место для перехода хорошее. Штраф рг может также принимать значения +оо и — оо, где 'оо’ означает большое число, которое на практике можно отождествить с бесконечностью, хотя оно, разумеется, конечно. В версии Т£Х’а от 1978 г. любой штраф > 1000 трактовался как оо, а любой штраф < —1000 —как —оо. Если pi = +оо, то переход на новую строку категорически запрещен; если pi = —оо, переход обязателен. В набор характеристик элемента-штрафа входит также ширина Wi, имеющая следующий смысл. Если в данной точке абзаца произвести переход на новую строку, то к текущей строке непосредственно перед точкой перехода нужно будет добавить некоторый текст ширины Wi. Допустим, например, что мы находимся внутри слова, в точке, где возможен перенос. Тогда в качестве потенциально возможного места для перехода на новую строку данная точка будет характеризоваться числом Pi — штрафом за соответствующий перенос и числом wt — шириной знака переноса. Элементы-штрафы бывают двух видов: с флажком и без флажка , что обозначается соответственно как /г = 1 и Д = 0. Рассматриваемый алгоритм верстки абзаца старается избегать двух следующих один за другим переходов на новую строку по штрафам с флажком (в частности, двух переносов подряд).
Итак, элемент-бокс определяется одним числом шг, элемент-клей — тройкой чисел (wi, pi, Zi\ элемент-штраф — тройкой чисел (шг, р^ z%\ Однако удобнее считать, что абзац х\ ... хт определяется следующими шестью последовательностями:
^1 ... ^^1 ... ^Wi . . . Wm,Pl • • • PmiWi . . . Wm,Pl • • • Pmi21 . . . Zmi21 . . . Zmiгде ti — тип элемента Xi со значением либо ‘бокс’, либо ‘клей’, либо ‘штраф’;где Wi—ширина, соответствующая элементу тг;где pi—растяжимость, соответствующая элементу хг. если ti = ‘клей’, и pi = 0 в противном случае, где Zi — сжимаемость, соответствующая элементу тг, если ti = ‘клей’, и 2г = 0 в противном случае;где ti — тип элемента Xi со значением либо ‘бокс’, либо ‘клей’, либо ‘штраф’;где Wi—ширина, соответствующая элементу тг;где pi—растяжимость, соответствующая элементу хг. если ti = ‘клей’, и pi = 0 в противном случае, где Zi — сжимаемость, соответствующая элементу тг, если ti = ‘клей’, и 2г = 0 в противном случае;
^1 ... ^
^1 ... ^
Wi . . . Wm,
Pl • • • Pmi
Wi . . . Wm,
Pl • • • Pmi
21 . . . Zmi
21 . . . Zmi
где ti — тип элемента Xi со значением либо ‘бокс’, либо ‘клей’, либо ‘штраф’;
где Wi—ширина, соответствующая элементу тг;
где pi—растяжимость, соответствующая элементу хг. если ti = ‘клей’, и pi = 0 в противном случае, где Zi — сжимаемость, соответствующая элементу тг, если ti = ‘клей’, и 2г = 0 в противном случае;
где ti — тип элемента Xi со значением либо ‘бокс’, либо ‘клей’, либо ‘штраф’;
где Wi—ширина, соответствующая элементу тг;
где pi—растяжимость, соответствующая элементу хг. если ti = ‘клей’, и pi = 0 в противном случае, где Zi — сжимаемость, соответствующая элементу тг, если ti = ‘клей’, и 2г = 0 в противном случае;
Pi • • -Ртч где pt — штраф, соответствующий элементу х^
если ti = ‘штраф’, и pi = 0 в противном случае;
/1 • • • /mi где fi = 1, если Xi — штраф с флажком, и fi = О в противном случае.
Для задания величин ин, yi, Zi могут использоваться любые единицы измерения. Те% использует так называемый типографский пункт, величина которого несколько меньше, чем ^ дюйма. В этой статье для измерения ширин будет использоваться машинная, единица, равная 1/18 еш. При таком выборе единицы измерения и некотором специальной размере шрифта используемые ширины часто оказываются целыми числами. В частности, все числа в рассматриваемых далее примерах приобретают предельно простой вид.
У читателя может сложиться впечатление, что мы занимаемся громоздкими математическими построениями применительно к совсем простым вещам. Но в действительности в Т^Х’овском алгоритме верстки абзацев все введенные выше понятия так или иначе используются, и точное определение всех параметров существенно даже в том относительно простом случае, когда верстается обычный текст. В дальнейшем мы убедимся, что в общем-то простые понятия боксов, клея и штрафов позволяют единообразно охватить поразительно много разнообразных задач верстки, а внимательное отношение к некоторым деталям процесса позволяет заодно решить еще целый ряд проблем.
Однако для начала лучше обратиться к простому случаю, а именно к верстке абзацев в обычном тексте, например, в газетной статье или в рассказе, чтобы усвоить реальный смысл абстрактных понятий, представляемых числами ин, yi и т. д. В системах компьютерного набора типа Т^Х’а преобразование реального абзаца в искомую абстрактную форму происходит следующим образом:
Если абзац начинается с отступа, первым элементом абзаца Ху должен быть пустой бокс, ширина которого ин равна величине отступа.
Каждое слово в абзаце превращается в последовательность боксов, соответствующих отдельным буквам, а также относящихся к данному слову знакам препинания. Ширины боксов ин определяются используемым шрифтом. Внутри слова отыскиваются все места, где при необходимости можно будет сделать перенос (т. е. перенести часть слова на следующую строку), и между соответствующими боксами вставляются элементы- штрафы с флажками. (В дальнейшем мы увидим, что на самом деле отыскивать точки возможного переноса следует только тогда, когда в них действительно возникает необходимость, однако пока для простоты мы будем предполагать, что заранее определяются все такие точки.)
Между словами вставляется клей в соответствии с правилами формирования промежутков, рекомендуемыми для используемого шрифта. Клей зависит также и от контекста: например, клей, вставляемый Т^Х’ом после знаков препинания, несколько отличается от обычного междусловного клея.
In olden times when wishing still helped one, .85? there lived a king whose daughters were all beaUjtif -.750 ful; and the youngest was so beautiful that the sun -.824 itself, which has seen so much, was astonished i.oe? whenever it shone in her face. Close by the king’s -.235 castle lay a great dark forest, and u^der an old .607 lime-tree in the forest was a well, and when the .500 day was very warm, the king’s child went out into -.500 the forest and sat down by the side of the cool .700 fountain; and when she was bored she took a i.aeo golden ball, and threw it up on high and caught it; -.650 and this ball was her favorite plaything. .001
Рис. 1. Пример абзаца, сверстанного методом «простой верстки». Маленькими треугольниками отмечены возможные места переноса слов. Корректировки междуслов- ных пробелов в относительных единицах приведены справа от соответствующих строк.
После имеющихся в тексте дефисов и тирс вставляются элементы-штрафы с флажками, имеющие нулевую ширину. Тем самым после дефисов и тирс разрешается переход на новую строку. В некоторых стилях переход на новую строку разрешен и перед длинным тире (em-dash) — в этом случае перед таким тире появится штраф нулевой ширины без флажка.
В самом конце абзаца добавляются три элемента, обеспечивающие правильное оформление последней строки. Первым вводится элемент-штраф жш-2 с Рт-2 = оо. Затем добавляется элемент-клей жш-1, формирующий пустое место, которым может заканчиваться последняя строка. Наконец, добавляется элемент-штраф тт с рт = — оо, осуществляющий принудительный переход на следующую строку. Обычно Т^Х использует «завершающий клей» с wm_i = ^m-i = 0 и pm-i = оо (в действительности ут-1 = 100000 пунктов; это значение хотя и конечно, но настолько велико, что ведет себя как оо). Таким образом, промежуток, добавляемый в конце абзаца, изначально имеет нулевую протяженность, но может очень сильно растягиваться. Действие этого последнего элемента сводится к тому, что другие промежутки в последней строке будут поджаты, если эта строка окажется шире, чем полагается, а в противном случае все промежутки, кроме последнего, сохранят нормальную ширину, поскольку всю работу по заполнению строки выполнит именно последний промежуток. Возможны и более тонкие способы задания завершающего клея жш-1 (о них речь пойдет позже).
В качестве примера рассмотрим абзац, приведенный на рис. 1 (текст взят из сказок Братьев Гримм [18]). Сформулированные выше пять правил превращают данный текст в последовательность, состоящую из 601 элемента (см. табл. 1). Абзац сверстан при помощи традиционного построчного алгоритма (см. выше); ширина строк — ровно 390 единиц.
21 = пустой бокс для абзацного отступа
wi = 18
Х2 = бокс для ‘Г
W2 = 6
Хз = бокс для ‘и’
шз = 10
ха = клей для междусловного пробела
W4 = 6,
2/4 = 3,
^4 = 2
Х5 = бокс ДЛЯ ‘о’
W5 = 9
2309 = бокс для Т
Ш309 = 5
2зю = бокс для Ч’
W310 = 5
язи = бокс для ‘т’
W311 = 15
Я312 = бокс для ‘е’
W312 = 8
Я313 = бокс для ‘-’
W313 = 6
2314 = штраф для дефиса
W314 = о,
Р314 = 50,
/314 = 1
Я315 = бокс для Ч’
W315 = 7
2590 = бокс для ‘а’
W590 = 9
2591 = бокс ДЛЯ ‘у’
W591 = 10
2592 = штраф для возможного переноса
W592 = 6,
Р592 = 50,
/б92 = 1
2593 = бокс ДЛЯ Ч’
W593 = 7
2594 = бокс ДЛЯ ‘Ь’
W594 = 10
2595 = бокс ДЛЯ Ч’
W595 = 5
2596 = бокс ДЛЯ ‘и’
W596 = Ю
2597 = бокс ДЛЯ ‘g’
W597 = 9
2598 = бокс ДЛЯ V
W598 = 5
2599 = запрет перехода на новую строку
W599 = 0,
Р599 = СЮ,
/б99 = 0
Ябоо = завершающий клей
W600 = 0,
2/600 = СЮ,
^600 = 0
Я601 = принудительный переход на новую строку
W601 = 0,
Р601 = —сю,
/б01 = 1
Таблица 1. Последовательность элементов (боксов, клея, штрафов), построенная ТЁХ’ом для абзаца, приведенного на рис. 1. Для целей настоящего примера предполагается, что каждая буква имеет ту ширину, которая традиционно использовалась при монотипном наборе, а именно, буквы от ‘а’ до ‘z’ имеют ширину соответственно (9,10,8,10,8,6, 9,10, 5,6,10, 5,15,10, 9,10,10, 7, 7,7,10, 9,13,10,10, 8) единиц, а буквы ‘С’, ‘Г и символ ‘-’ имеют соответственно ширину 13, 6 и 6 единиц. Ширина запятой, точки с запятой и апострофа одна и та же и равняется 5 единицам. Клей между словами определяется тройкой чисел (w,y, г) = (6,3,2), однако после запятой эта тройка чисел равна (6,4, 2), после точки с запятой — (6,4,1), а после точки — (8, 6,1). На каждую строку, заканчивающуюся переносом, накладывается штраф, равный 50.
На рис. 1 места возможных переносов отмечены крошечными треугольниками. Согласно классическим справочным пособиям1 перенос разрешается, если только ему в слове предшествует нс менее двух букв и после него идет нс мснсс трех букв. Нельзя также, чтобы слог, следующий непосредственно за переносом, содержал немое ‘е’ —тем самым запрещаются переносы типа ‘syllable’. Более того, для облегчения чтения рекомендуется, чтобы часть слова, предшествующая переносу, была достаточно длинной: в таком случае читающий оказывается в состоянии правильно и однозначно воспринять все слово
еще до того, как он увидит его заключительный фрагмент в следующей строке. Пример неудачного переноса, способного вызвать некоторую заминку, — ‘process’. Именно по этой причине в предпоследнем слове на рис. 1 нс разрешен перенос ‘fa-vorite’, который в принципе является допустимым: слог ‘fa-’ вполне мог бы оказаться началом слова ‘fa-ther’, где тот же самый слог произносится совершенно иначе.
Выбор правильных мест для переносов — проблема важная, но сложная и выходящая за рамки данной статьи. На самом деле все, что нам нужно в дальнейшем, сводится к следующему: (а) там, где это требуется, мы считаем, что наш алгоритм абзацной верстки знает места возможных переносов; (Ь) мы предпочитаем обходиться без переносов, если это нс сильно портит величину междусловных пробелов.
Хотя на интуитивном уровне используемые правила верстки абзацев становятся понятными из рассмотренного примера, их все же надо сформулировать явным образом. Будем предполагать, что каждый абзац заканчивается принудительным переходом на новую строку— элементом тт (элементом с величиной штрафа — оо). Допустимая точка перехода на новую строку в абзаце—это такой номер 6, что либо (i) хь — штраф с рь < оо, либо (ii) хь — клей, а хь-i— бокс. Иначе говоря, переход на новую строку можно делать либо на элементе-штрафе при условии, что величина штрафа не равна оо, либо на элементе-клее при условии, что последний следует непосредственно за элементом-боксом. Этими двумя случаями исчерпываются все возможные места перехода на следующую строку. В частности, если где-либо в абзаце имеются несколько подряд идущих пробелов, переход на новую строку можно делать только на первом из них и только в том случае, если он нс следует непосредственно за штрафом. Штраф величины оо может быть вставлен перед клеем, чтобы сделать соответствующий пробел неразрывным. Например, элемент Ж599 в табл. 1 обеспечивает невозможность перехода на новую строку на клее, завершающем абзац и заполняющем его последнюю строку.
Верстка абзаца сводится к выбору допустимых точек перехода на новую строку 61 < • • • < bk, определяющих концы строк в абзаце (при этом, естественно, абзац оказывается состоящим из к строк). Каждый элемент-штраф xt со значением штрафа^ = —оо обязан присутствовать среди выбранных точек, в частности, последняя точка перехода Ьк должна равняться т. Полагая для удобства 6q = 0, введем в рассмотрение величины ai < • • • < а^ ассоциируемые с началами строк абзаца. Именно, пусть значением Qj служит наименьшее целое число г, заключенное между bj_i и bj и такое, что xt—элемент-бокс. Если ни для одного г из интервала bj—^ < г < bj соответствующий элемент xt не является боксом, положим aj = bj. Тогда j-я строка состоит из всех элементов xt с aj < i < bj^ а также элемента хьр если последний является штрафом. Другими словами, строки абзаца получаются путем разрезания его на части по выбранным точкам перехода и последующего удаления клея и штрафов из начала каждой из строк, полученных при этом разрезании.
Критерий желательности
Если абзац имеет п допустимых точек перехода на новую строку, не являющихся принудительными переносами, то в соответствии с данным выше определением существует 2П способов сверстать абзац. Так, например, в абзаце, приведенном на рис. 1, имеется 129 точек перехода на новую строку, помимо a?60i; соответственно, число вариантов верстки абзаца (разбиения его на строки) равно 2129 — это больше, чем 1038. Но, конечно же, большинство этих вариантов абсурдны, поэтому нужен критерий, позволяющий отделять разумные варианты верстки от совершенно бессмысленных. Чтобы построить такой критерий, нужно знать, во-первых, желательную длину строки и, во-вторых, длины строк, получающихся при каждом варианте верстки с учетом их растяжимости и сжимаемости. Зная и то, и другое, можно сравнивать реально получающиеся длины строк с желательной длиной.
Будем предполагать, что задан список желательных длин строк Zi, Z2, /3, ... . Чаще всего все эти длины равны между собой, однако иногда они могут и различаться, как, например, в случае текста, обтекающего рисунок. После того как точки перехода на новую строку в абзаце выбраны, фактическая длина j-й строки Lj вычисляется следующим образом. Суммируются ширины всех боксов и всех клеев в интервале aj < г < bj; в том случае, если хь- — элемент-штраф, к полученной сумме прибавляется Wb3. Суммарная растяжимость j-й строки Yj и ее суммарная сжимаемость Zj находятся соответственно суммированием всех величин yt и z^ характеризующих те элементы-клеи, которые содержатся в интервале aj < г < bj. Произведя эти вычисления, можно сравнить действительную длину j-й строки Lj с ее желательной длиной lj и выяснить, позволяют ли найденные растяжимость и сжимаемость строки преобразовать Lj в lj. Назовем коэффициентом выравнивания j-й строки число Tj, определяемое следующим образом:
Если Lj = lj (идеальная длина строки), то Tj = 0.
Если Lj < lj (короткая строка), то Tj = (lj — Lj}/Yj при условии, что Yj > 0; если же в рассматриваемом случае Yj < 0, то значение Tj не определено.
Если Lj > lj (длинная строка), то Tj = (Zj — Lj^/Zj при условии, что Zj > 0; если же в рассматриваемом случае Zj < 0, то значение Tj не определено. Так, например, Tj = 1/3, если суммарная растяжимость строки j в три раза больше того значения, которое требуется, чтобы растянуть строку от ее естественной длины Lj до идеальной длины lj.
Согласно данному определению, для выравнивания j-й строки достаточно для каждого элемента-клея хг в этой строке сделать его ширину равной
Wi + т3уг, если Tj > 0;
и^ + TjZt, если Tj < 0 .
В самом деле, подсчитав суммарную длину строки с так преобразованными ширинами, мы, в зависимости от знака Tj, получим либо Lj + Tj Yj = lj, либо Lj+Tj Zj = lj. Указанное преобразование делает в точности то, что требуется, а именно распределяет необходимое растяжение или сжатие строки равномерно между компонентами yt либо Zi отдельных элементов-клеев.
Числа, напечатанные мелким шрифтом справа от текста на рис. 1, представляют собой значения коэффициентов выравнивания rj соответствующих строк. Отрицательные значения (например, —.824 в третьей строке) означают, что пробелы в строке меньше своей идеальной величины, причем для сжатия пробелов до такого состояния пришлось использовать более 82% их потенциальной сжимаемости. Напротив, относительно большие положительные значения (например, 1.360 в третьей от конца строке) указывают на то, что строка получается очень разреженной.
Хотя абзац, приведенный на рис. 1, может быть разбит на строки 2129 способами, оказывается, что среди всего этого множества разбиений только у 12 все коэффициенты выравнивания не превосходят по абсолютной величине 1. Последнее условие означает, что получающиеся в результате выравнивания междусловные пробелы лежат на отрезках с границами Wi — Zi и w; + у v Заметим, что решение, полученное с помощью традиционного алгоритма верстки, этим свойством не обладает.
Наша основная цель —так сверстать абзац, чтобы слова во всех строках не отстояли друг от друга слишком далеко и не находились друг от друга слишком близко, так как подобные «плохие» строки отвлекают внимание читателя и затрудняют процесс чтения. Можно сказать, что задача верстки абзаца состоит в отыскании такого разбиения абзаца на строки, что |rj | < 1 для любой строки, и при соблюдении этого условия число переносов в абзаце минимально. Подобный подход использовался в начале 1960-х годов Дунканом с соавторами [13] и позволил получить довольно хорошие результаты. Однако критерий |г?| < 1 зависит только от величин wt — z^ Wi + yi и не зависит от w^ а значит, не использует всех степеней свободы, содержащихся в исходных данных. Кроме того, это условие является весьма жестким, и его соблюдения не всегда можно добиться. Например, если бы в приведенном на рис. 1 абзаце длина строки равнялась не 390 единиц, а 400, то при любом варианте верстки абзац содержал бы либо слишком плотно заполненную (rj < — 1), либо слишком разреженную (tj > — 1) строку.
Лучших результатов можно добиться, используя в качестве критерия качества верстки не «релейную» функцию от условия [rj < 1 (функцию типа да/нет), а некоторую непрерывную функцию. С этой целью можно ввести количественную оценку плохости j-й строки, построив тем или иным образом функцию, принимающую значения, близкие к нулю, когда [rj мало, и быстро возрастающую, когда значение |rj | становится больше 1. Как показали многочисленные эксперименты с системой Те^, хорошие результаты получаются, если плохость j-и строки определить формулой
{
оо , если Tj не определено или Tj < — 1; [100|г^|3 + .5J в противном случае.
Так, например, строки абзаца на рис. 1 характеризуются значениями плохости 63, 42, 56, 128, 1, 22, 13, 13, 34, 252, 27, 0 соответственно. Согласно приведенной выше формуле для Д-, строка считается «бесконечно плохой», если Tj < —1.
Основанием для этого служит то обстоятельство, что при соблюдении последнего условия клей не может быть сжат меньше чем до величины Wi — Zi. В то же самое время значениям т^ превышающим единицу, соответствует конечная плохость, так что соответствующие строки попадают в число допустимых и могут быть использованы в окончательном варианте верстки, если им не найдется лучшей альтернативы.
Небольшое усовершенствование метода, с помощью которого был получен абзац на рис. 1, позволяет получить вариант верстки того же абзаца, показанный на рис. 2. Как и раньше, выбор точек перехода на новую строку производится без учета качества следующих строк и без ревизии уже принятых решений, однако на этот раз точка перехода на новую строку выбирается так, чтобы минимизировать «плохость плюс штраф» для текущей строки. Иначе говоря, в методе, отвечающем рис. 2, при уже выбранной точке перехода с Q — 1)-й строки на j-ю в качестве точки перехода с j-й строки на (j + 1)-ю выбирается та, для которой сумма Д'+тг^ принимает наименьшее значение. (Здесь Pj — плохость, определенная выше, a Hj — значение штрафа рьр если j-я строка заканчивается на элементе-клее, и ttj = 0 в противном случае.) Улучшение качества верстки на рис. 2 по сравнению с рис. 1 происходит за счет переноса в следующую строку слов или слогов, содержавшихся в строках 2, 3, 11.
Алгоритм, с помощью которого абзац был сверстан так, как показано на рис. 1, естественно называть «простой версткой» (ПВ), а алгоритм, соответствующий варианту, приведенному на рис. 2, — «улучшенной версткой (УВ)». Однако, хотя для данного конкретного примера улучшенная верстка оказалась лучше простой, единственный пример не является доказательством превосходства того или иного метода в общем случае, и в принципе могут найтись абзацы, для которых лучший результат даст не улучшенная, а простая верстка. Чтобы произвести объективное сравнение рассматриваемых алгоритмов, нужно собрать статистику их «типичного» поведения. С этой целью над абзацем, приведенным на рис. 1 и рис. 2, было проведено 300 экспериментов, в которых сам текст не менялся, а длина строки варьировалась от 350 до 649 единиц. Оказалось, что изменение длины строки может сильно изменить результаты-алгоритмы верстки весьма чувствительны к размерам. В каждом случае записывались самая «плотная» и самая «жидкая» строки в абзаце, а также число сделанных переносов. Полученные результаты приведены в следующей табличке:
min Tj
max Tj
Число переносов
ПВ
< УВ
68%
40%
13%
ПВ
= УВ
26%
45%
79%
ПВ
> УВ
6%
15%
9%
In olden times when wishing still helped one, .857 there lived a king whose daughters were all beaiif .000 tijful; and the youngest was so beau^i^ul that the .280 sun itself, which has seen so much, was astonished -.235 whenever it shone in her face. Close by the king’s -.235 castle lay a great dark forest, and un/der an old .607 lime-tree in the forest was a well, and when the .500 day was very warm, the king’s child went out into -.500 the forest and sat down by the side of the cool .700 fountain; and when she was bored she took а 1.З60 golden ball, and threw it up on high and caught .357 it; and this ball was her favorite plaything. .000
Рис. 2. Абзац, получающийся вместо приведенного на рис. 1 при использовании улучшенной верстки.
Итак, в 68% случаев минимальное значение коэффициента выравнивания rj в абзаце, полученном простой версткой, меньше соответствующей величины для абзаца, полученного улучшенной версткой; максимальное значение коэффициента выравнивания в абзаце, полученном простой версткой, меньше соответствующей величины для абзаца, полученного улучшенной версткой, примерно в 40% случаев, и т. д. Резюмируя приведенные результаты, можно сказать, что простая верстка обычно дает по крайней мере одну строку с не меньшей (т. е. такой же или большей) плотностью, чем у самой плотной строки из числа полученных улучшенной версткой; точно так же простая верстка обычно дает строку с такой же или большей разреженностью, чем у самой разреженной из строк, полученных улучшенной версткой. Число переносов для обоих методов примерно одинаково, хотя, если увеличить штраф за переносы, при улучшенной верстке их будет получаться меньше.
В действительности оба рассмотренных метода можно улучшить, построив «оптимальный» алгоритм верстки. В частности, как показано на рис. 3, можно улучшить варианты верстки, приведенные на рис. 1 и рис. 2, сделав перенос в строке 4, который в итоге позволяет избежать разреженности в строке 10. Абзац на рис. 3 получен методом «оптимальной верстки», который будет рассмотрен ниже. Соответствующий вариант разбиения абзаца на строки является глобально оптимальным в том смысле, что доставляет величине суммарной дефектности (demerits) минимум относительно всех возможных вариантов выбора точек перехода с текущей строки на новую. При этом дефектность J-й строки вычисляется по формуле
( (1 + А + ^зУ + Oj > 5j = < (1 + ft)2 - 7Tj + «з ,I (1 + Р3У + ctj ,( (1 + А + ^зУ + Oj > 5j = < (1 + ft)2 - 7Tj + «з ,I (1 + Р3У + ctj ,если 7Tj > 0 ;если 7Tj > 0 ;если —оо < 7гj < 0 ;если —оо < 7гj < 0 ;если iTj = — 00 .если iTj = — 00 .
( (1 + А + ^зУ + Oj > 5j = < (1 + ft)2 - 7Tj + «з ,
I (1 + Р3У + ctj ,
( (1 + А + ^зУ + Oj > 5j = < (1 + ft)2 - 7Tj + «з ,
I (1 + Р3У + ctj ,
если 7Tj > 0 ;
если 7Tj > 0 ;
если —оо < 7гj < 0 ;
если —оо < 7гj < 0 ;
если iTj = — 00 .
если iTj = — 00 .
Здесь, как и раньше, Pj и л? — соответственно показатель плохости и штраф, а CYj отлично от нуля лишь тогда, когда и j-я, и (J — 1)-я строки заканчиваются
на элементе-штрафе с флажком, причем в этом случае aj равно дополнительному штрафу, установленному для последовательных строк, заканчивающихся переносами (например, 3000). Будем говорить, что абзац сверстан оптимально, если сумма значений 5j по всем j достигает минимума.
Так же как и ранее приведенное выражение для Р^ формула, определяющая 6^ выбрана достаточно произвольно. Тем не менее, на практике эта формула работает хорошо, что связано со следующими ее полезными свойствами.
Минимизация суммы квадратов плохостей не только автоматически обеспечивает минимизацию максимума плохостей по строкам, но и гарантирует «оптимизацию второго порядка». Это означает, например, что если в абзаце с неизбежностью оказывается строка с большой плохостью, алгоритм обеспечивает наилучшую верстку остальных строк.
Дефектность ф является монотонно возрастающей функцией тг?. Нарушение монотонности имеет место лишь при и^ = — оо, но в этом случае и не требуется учитывать штраф, поскольку переход на новую строку является принудительным.
Используя не саму величину р^ а 1 + р^ мы в тех случаях, когда плохость строк близка к нулю, фактически минимизируем число строк в абзаце.
Рассмотрим в качестве примера следующую табличку, в столбцах которой записаны дефектности, отвечающие строкам абзаца на рис. 1, 2 и 3 соответственно:
Простая верстка
Улучшенная верстка
Оптимальная верстка
4096
4096
4096
8649
2601
2601
3249
9
9
16641
196
22801
4
4
1
529
529
9
196
196
256
196
196
1
1225
1225
1225
64009
64009
4
784
36
36
1
1
99579
73098
31040
Можно заметить, что при использовании простой или улучшенной верстки значения дефектности строк распределены по абзацу случайным образом, тогда как при оптимальной верстке самые плохие строки сосредоточены в начале абзаца. Это неудивительно, поскольку для начальных строк число вариантов перехода на новую строку остается относительно небольшим, и алгоритм верстки обладает меньшей гибкостью.
На рис. 4 представлены результаты применения тех же трех алгоритмов верстки при большей ширине текстового поля, а именно при длине строки, рав-
In olden times when wishing still helped one, .857 there lived a king whose daughters were all beaup .000 tijful; and the youngest was so beautiful that the .280 sun itself, which has seen so much, was aston^ 1.000 ished whenever it shone in her face. Close by the .067 king’s castle lay a great dark forest, and un/der an -.278 old lime-tree in the forest was a well, and when .535 the day was very warm, the king’s child went out -.167 into the forest and sat down by the side of the .700 cool fountain; and when she was bored she took a -.176 golden ball, and threw it up on high and caught .357 it; and this ball was her favorite plaything..000
Рис. 3. Наилучший вариант верстки абзаца, фигурирующего на рис. 1 и 2, в смысле минимума суммы дефектностей строк.
Простая In olden times when wishing still helped one, there lived a king -.727 верстка: whose daughters were all beautiful; and the youngest was so .821 beautiful that the sun itself, which has seen so much, was aston^ -.455 ished whenever it shone in her face. Close by the king’s castle lay -.870 a great dark forest, and un^der an old lime-tree in the forest was -.208 a well, and when the day was very warm, the king’s child went .000 out into the forest and sat down by the side of the cool fountain; -.577 and when she was bored she took a golden ball, and threw it up -.231 on high and caught it; and this ball was her favorite plaything. .000
Улучшенная In olden times when wishing still helped one, there lived a king -.727 верстка: whose daughters were all bea^ti^ul; and the youngest was so .821 beautiful that the sun itself, which has seen so much, was aston^ -.455 ished whenever it shone in her face. Close by the king’s castle .278 lay a great dark forest, and un^der an old lime-|tree in the forest .000 was a well, and when the day was very warm, the king’s child .237 went out into the forest and sat down by the side of the cool .462 fountain; and when she was bored she took a golden ball, and .343 threw it up on high and caught it; and this ball was her favorite -.320 plaything..004
Оптимальная In olden times when wishing still helped one, there lived a .774 верстка: king whose daughters were all beautiful; and the youngest was .179 so beautiful that the sun itself, which has seen so much, was .629 astonished whenever it shone in her face. Close by the king’s .545 castle lay a great dark forest, and un^der an old lime-tree in the .000 forest was a well, and when the day was very warm, the king’s .079 child went out into the forest and sat down by the side of the .282 cool fountain; and when she was bored she took a golden ball, .294 and threw it up on high and caught it; and this ball was her .575 favorite plaything..004
Рис. 4. Верстка того же абзаца при большей длине строки.
ной 500 единиц. В данном случае алгоритм оптимальной верстки, благодаря своим прогностическим возможностям, позволил обойтись вообще без переносов слов. Два других алгоритма, в которых решение принимается по каждой строке в отдельности, проходят мимо этого варианта, так как не замечают, что
за счет небольшого ухудшения первой строки удается избавиться от многих проблем в последующих строках. Дефектности строк для всех трех вариантов верстки, представленных на рис. 4, выглядят следующим образом:
Простая
Улучшенная
Оптимальная
верстка
верстка
верстка
1521
1521
2209
3136
3136
4
3600
3600
676
4489
9
2916
4
1
1
1
4
1
400
121
9
4
25
16
1
16
400
1_
1.
13156
8434
6233
При простой и улучшенной верстке дефектность третьей строки, равная 3600, возникает, главным образом, из-за штрафа со значением 50 за сделанный пет ренос.
; In the meantime it ' knocked a second ; time, and cried, ; “Princess, youngest ; princess, open the j door for me. Do you ; not know what you ; said to me yesterday ; by the cool waters of : the well? Princess, * youngest princess, i open the door for me!»; In the meantime it ' knocked a second ; time, and cried, ; “Princess, youngest ; princess, open the j door for me. Do you ; not know what you ; said to me yesterday ; by the cool waters of : the well? Princess, * youngest princess, i open the door for me!»При простой верстке абзац на рис. 4 записывается всего в 9 строк, тогда как при оптимальной верстке этот абзац оказывается разбитым на 10 строк. Издатели, стремящиеся сэкономить бумагу, могут предпочесть простую верстку при том, что получаемый с ее помощью текст, в общем-то, выглядит вполне прилично. Для таких случаев можно предложить целый ряд модификаций параметров оптимальной верстки, приводящих к тому, что алгоритм будет отдавать предпочтение вариантам с меньшим числом строк. Например, можно уменьшить растяжимость клея в последней строке абзаца с очень большого ее значения в стандартном варианте метода до значения, примерно равного длине строки, —в результате этого алгоритм будет отдавать предпочтение строкам с высокой степенью заполненности. Можно также в выражении для дефектности 5j заменить единицу большим числом. В действительности алгоритм оптимальной верстки можно настроить так, чтобы получаемое оптимальное решение имело минимальное возможное число строк.
; In the meantime it ' knocked a second ; time, and cried, ; “Princess, youngest ; princess, open the j door for me. Do you ; not know what you ; said to me yesterday ; by the cool waters of : the well? Princess, * youngest princess, i open the door for me!»
; In the meantime it ' knocked a second ; time, and cried, ; “Princess, youngest ; princess, open the j door for me. Do you ; not know what you ; said to me yesterday ; by the cool waters of : the well? Princess, * youngest princess, i open the door for me!»
Рис. 5. Результат применения алгоритма улучшенной верстки при очень маленькой длине строки. Абзац не выровнен по правому краю, так как иначе он выглядел бы ужасно. Здесь в третьей строке всего два пробела, а в третьей от конца строке — вообще один. В первой строке имеется «выброс» вправо — явление, часто встречающееся при верстке текста без выравнивания.
In olden times when wishing still helped one, there lived a king whose daughters were all beautiful; and the youngest was so beautiful that the sun itself, which has seen so much, was astonished whenever it shone in her face. Close by the king’s castle lay a great dark forest, and under an old lime-tree in the forest was a well, and when the day was very warm, the king’s child went out into the forest and sat down by the side of the cool fountain; and when she was bored she took a golden ball, and threw it up on high and caught it; and this ball was her favorite plaything.Now it so happened that on one occasion the princess’s golden ball did not fall into the little hand that she was holding up for it, but on to the ground beyond, and it rolled straight into the water. The king’s daughter followed it with her eyes, but it vanished, and the well was deep, so deep that the bottom could not be seen. At this she began to cry, and cried louder and louder, and could not be comforted. And as she thus lamented someone said to her, “What ails you, king’s daughter? You weep so that even a stone would show pity.”She looked round to the side from whence the voice came, and saw a frog stretching forth its big, ugly head fromIn olden times when wishing still helped one, there lived a king whose daughters were all beautiful; and the youngest was so beautiful that the sun itself, which has seen so much, was astonished whenever it shone in her face. Close by the king’s castle lay a great dark forest, and under an old lime-tree in the forest was a well, and when the day was very warm, the king’s child went out into the forest and sat down by the side of the cool fountain; and when she was bored she took a golden ball, and threw it up on high and caught it; and this ball was her favorite plaything.Now it so happened that on one occasion the princess’s golden ball did not fall into the little hand that she was holding up for it, but on to the ground beyond, and it rolled straight into the water. The king’s daughter followed it with her eyes, but it vanished, and the well was deep, so deep that the bottom could not be seen. At this she began to cry, and cried louder and louder, and could not be comforted. And as she thus lamented someone said to her, “What ails you, king’s daughter? You weep so that even a stone would show pity.”She looked round to the side from whence the voice came, and saw a frog stretching forth its big, ugly head fromthe water. “Ah, old water-splasher, is it you?” said she; “I am weeping for my golden ball, which has fallen into the well.” “Be quiet, and do not weep,” answered the frog. “I can help you; but what will you give me if I bring your plaything up again?” “Whatever you will have, dear frog,” said she; “my clothes, my pearls and jewels, and even the golden crown that I am wearing.” The frog answered, “I do not care for your clothes, your pearls and jewels, nor for your golden crown; but if you will love me and let me be your companion and play-fellow, and sit by you at your little table, and eat off your little golden plate, and drink out of your little cup, and sleep in your little bed—if you will promise me this I will go down below, and bring you your golden ball up again.”“Oh yes,” said she, “I promise you all you wish, if you will but bring me my ball back again.” But she thought, “How the silly frog does talk! All he does is sit in the water with the other frogs, and croak. He can be no companion to any human being.”But the frog, when he had received this promise, put his head into the water and sank down; and in a short while he came swimming up again with the ball in his mouth, and threw it on the grass. The king’s daughter wasthe water. “Ah, old water-splasher, is it you?” said she; “I am weeping for my golden ball, which has fallen into the well.” “Be quiet, and do not weep,” answered the frog. “I can help you; but what will you give me if I bring your plaything up again?” “Whatever you will have, dear frog,” said she; “my clothes, my pearls and jewels, and even the golden crown that I am wearing.” The frog answered, “I do not care for your clothes, your pearls and jewels, nor for your golden crown; but if you will love me and let me be your companion and play-fellow, and sit by you at your little table, and eat off your little golden plate, and drink out of your little cup, and sleep in your little bed—if you will promise me this I will go down below, and bring you your golden ball up again.”“Oh yes,” said she, “I promise you all you wish, if you will but bring me my ball back again.” But she thought, “How the silly frog does talk! All he does is sit in the water with the other frogs, and croak. He can be no companion to any human being.”But the frog, when he had received this promise, put his head into the water and sank down; and in a short while he came swimming up again with the ball in his mouth, and threw it on the grass. The king’s daughter wasdelighted to see her pretty plaything once more, and she picked it up and ran away with it. “Wait, wait,” said the frog. “Take me with you. I can’t run as you can.” But what did it avail him to scream his croak, croak, after her, as loudly as he could? She did not listen to it, but ran home and soon forgot the poor frog, who was forced to go back into his well again.The next day when she had seated herself at table with the king and all the courtiers, and was eating from her little golden plate, something came creeping splish splash, splish splash, up the marble staircase; and when it had got to the top, it knocked at the door and cried, “Princess, youngest princess, open the door for me.” She ran to see who was outside, but when she opened the door, there sat the frog in front of it. Then she slammed the door to, in great haste, sat down to dinner again, and was quite frightened. The king saw plainly that her heart was beating violently, and said, “My child, what are you so afraid of? Is there perchance a giant outside who wants to carry you away?” “Ah, no,” replied she. “It is no giant, it is a disgusting frog.”“What does a frog want with you?” “Ah, dear father, yesterday as I was in the forestdelighted to see her pretty plaything once more, and she picked it up and ran away with it. “Wait, wait,” said the frog. “Take me with you. I can’t run as you can.” But what did it avail him to scream his croak, croak, after her, as loudly as he could? She did not listen to it, but ran home and soon forgot the poor frog, who was forced to go back into his well again.The next day when she had seated herself at table with the king and all the courtiers, and was eating from her little golden plate, something came creeping splish splash, splish splash, up the marble staircase; and when it had got to the top, it knocked at the door and cried, “Princess, youngest princess, open the door for me.” She ran to see who was outside, but when she opened the door, there sat the frog in front of it. Then she slammed the door to, in great haste, sat down to dinner again, and was quite frightened. The king saw plainly that her heart was beating violently, and said, “My child, what are you so afraid of? Is there perchance a giant outside who wants to carry you away?” “Ah, no,” replied she. “It is no giant, it is a disgusting frog.”“What does a frog want with you?” “Ah, dear father, yesterday as I was in the forest
In olden times when wishing still helped one, there lived a king whose daughters were all beautiful; and the youngest was so beautiful that the sun itself, which has seen so much, was astonished whenever it shone in her face. Close by the king’s castle lay a great dark forest, and under an old lime-tree in the forest was a well, and when the day was very warm, the king’s child went out into the forest and sat down by the side of the cool fountain; and when she was bored she took a golden ball, and threw it up on high and caught it; and this ball was her favorite plaything.
Now it so happened that on one occasion the princess’s golden ball did not fall into the little hand that she was holding up for it, but on to the ground beyond, and it rolled straight into the water. The king’s daughter followed it with her eyes, but it vanished, and the well was deep, so deep that the bottom could not be seen. At this she began to cry, and cried louder and louder, and could not be comforted. And as she thus lamented someone said to her, “What ails you, king’s daughter? You weep so that even a stone would show pity.”
She looked round to the side from whence the voice came, and saw a frog stretching forth its big, ugly head from
In olden times when wishing still helped one, there lived a king whose daughters were all beautiful; and the youngest was so beautiful that the sun itself, which has seen so much, was astonished whenever it shone in her face. Close by the king’s castle lay a great dark forest, and under an old lime-tree in the forest was a well, and when the day was very warm, the king’s child went out into the forest and sat down by the side of the cool fountain; and when she was bored she took a golden ball, and threw it up on high and caught it; and this ball was her favorite plaything.
Now it so happened that on one occasion the princess’s golden ball did not fall into the little hand that she was holding up for it, but on to the ground beyond, and it rolled straight into the water. The king’s daughter followed it with her eyes, but it vanished, and the well was deep, so deep that the bottom could not be seen. At this she began to cry, and cried louder and louder, and could not be comforted. And as she thus lamented someone said to her, “What ails you, king’s daughter? You weep so that even a stone would show pity.”
She looked round to the side from whence the voice came, and saw a frog stretching forth its big, ugly head from
the water. “Ah, old water-splasher, is it you?” said she; “I am weeping for my golden ball, which has fallen into the well.” “Be quiet, and do not weep,” answered the frog. “I can help you; but what will you give me if I bring your plaything up again?” “Whatever you will have, dear frog,” said she; “my clothes, my pearls and jewels, and even the golden crown that I am wearing.” The frog answered, “I do not care for your clothes, your pearls and jewels, nor for your golden crown; but if you will love me and let me be your companion and play-fellow, and sit by you at your little table, and eat off your little golden plate, and drink out of your little cup, and sleep in your little bed—if you will promise me this I will go down below, and bring you your golden ball up again.”
“Oh yes,” said she, “I promise you all you wish, if you will but bring me my ball back again.” But she thought, “How the silly frog does talk! All he does is sit in the water with the other frogs, and croak. He can be no companion to any human being.”
But the frog, when he had received this promise, put his head into the water and sank down; and in a short while he came swimming up again with the ball in his mouth, and threw it on the grass. The king’s daughter was
the water. “Ah, old water-splasher, is it you?” said she; “I am weeping for my golden ball, which has fallen into the well.” “Be quiet, and do not weep,” answered the frog. “I can help you; but what will you give me if I bring your plaything up again?” “Whatever you will have, dear frog,” said she; “my clothes, my pearls and jewels, and even the golden crown that I am wearing.” The frog answered, “I do not care for your clothes, your pearls and jewels, nor for your golden crown; but if you will love me and let me be your companion and play-fellow, and sit by you at your little table, and eat off your little golden plate, and drink out of your little cup, and sleep in your little bed—if you will promise me this I will go down below, and bring you your golden ball up again.”
“Oh yes,” said she, “I promise you all you wish, if you will but bring me my ball back again.” But she thought, “How the silly frog does talk! All he does is sit in the water with the other frogs, and croak. He can be no companion to any human being.”
But the frog, when he had received this promise, put his head into the water and sank down; and in a short while he came swimming up again with the ball in his mouth, and threw it on the grass. The king’s daughter was
delighted to see her pretty plaything once more, and she picked it up and ran away with it. “Wait, wait,” said the frog. “Take me with you. I can’t run as you can.” But what did it avail him to scream his croak, croak, after her, as loudly as he could? She did not listen to it, but ran home and soon forgot the poor frog, who was forced to go back into his well again.
The next day when she had seated herself at table with the king and all the courtiers, and was eating from her little golden plate, something came creeping splish splash, splish splash, up the marble staircase; and when it had got to the top, it knocked at the door and cried, “Princess, youngest princess, open the door for me.” She ran to see who was outside, but when she opened the door, there sat the frog in front of it. Then she slammed the door to, in great haste, sat down to dinner again, and was quite frightened. The king saw plainly that her heart was beating violently, and said, “My child, what are you so afraid of? Is there perchance a giant outside who wants to carry you away?” “Ah, no,” replied she. “It is no giant, it is a disgusting frog.”
“What does a frog want with you?” “Ah, dear father, yesterday as I was in the forest
delighted to see her pretty plaything once more, and she picked it up and ran away with it. “Wait, wait,” said the frog. “Take me with you. I can’t run as you can.” But what did it avail him to scream his croak, croak, after her, as loudly as he could? She did not listen to it, but ran home and soon forgot the poor frog, who was forced to go back into his well again.
The next day when she had seated herself at table with the king and all the courtiers, and was eating from her little golden plate, something came creeping splish splash, splish splash, up the marble staircase; and when it had got to the top, it knocked at the door and cried, “Princess, youngest princess, open the door for me.” She ran to see who was outside, but when she opened the door, there sat the frog in front of it. Then she slammed the door to, in great haste, sat down to dinner again, and was quite frightened. The king saw plainly that her heart was beating violently, and said, “My child, what are you so afraid of? Is there perchance a giant outside who wants to carry you away?” “Ah, no,” replied she. “It is no giant, it is a disgusting frog.”
“What does a frog want with you?” “Ah, dear father, yesterday as I was in the forest
Рис. 6. Сказка о Короле-лягушонке, сверстанная в виде узких колонок с рваным правым краем. Точки перехода на новую строку были определены оптимальным образом для случая верстки с выравниванием.
sitting by the well, playing, my golden ball fell into the water. And because I cried so, the frog brought it out again for me; and because he so insisted, I promised him he should be my companion, but I never thought he would be able to come out of his water. And now he is outside there, and wants to come in to see me.”In the meantime it knocked a second time, and cried, “Princess, youngest princess, open the door for me. Do you not know what you said to me yesterday by the cool waters of the well? Princess, youngest princess, open the door for me!”Then said the king, “That which you have promised must you perform. Go and let him in.” She went and opened the door, and the frog hopped in and followed her, step by step, to her chair. There he sat and cried, “Lift me up beside you.” She delayed, until at last the king commanded her to do it. Once the frog was on the chair he wanted to be on the table, and when he was on the table he said, “Now, push your little golden plate nearer to me, that we may eat together.” She did this, but it was easy to see that she did not do it willingly. The frog enjoyed what he ate, but almost every mouthful she took choked her. At length he said,sitting by the well, playing, my golden ball fell into the water. And because I cried so, the frog brought it out again for me; and because he so insisted, I promised him he should be my companion, but I never thought he would be able to come out of his water. And now he is outside there, and wants to come in to see me.”In the meantime it knocked a second time, and cried, “Princess, youngest princess, open the door for me. Do you not know what you said to me yesterday by the cool waters of the well? Princess, youngest princess, open the door for me!”Then said the king, “That which you have promised must you perform. Go and let him in.” She went and opened the door, and the frog hopped in and followed her, step by step, to her chair. There he sat and cried, “Lift me up beside you.” She delayed, until at last the king commanded her to do it. Once the frog was on the chair he wanted to be on the table, and when he was on the table he said, “Now, push your little golden plate nearer to me, that we may eat together.” She did this, but it was easy to see that she did not do it willingly. The frog enjoyed what he ate, but almost every mouthful she took choked her. At length he said,“I have eaten and am satisfied, now I am tired; carry me into your little room and make your little silken bed ready, and we will both lie down and go to sleep.”The king’s daughter began to cry, for she was afraid of the cold frog, which she did not like to touch, and which was now to sleep in her pretty, clean little bed. But the king grew angry and said, “He who helped you when you were in trouble ought not afterwards to be despised by you.” So she took hold of the frog with two fingers, carried him upstairs, and put him in a corner. But when she was in bed he crept to her and said, “I am tired, I want to sleep as well as you; lift me up or I will tell your father.” At this she was terribly angry, and took him up and threw him with all her might against the wall. “Now, will you be quiet, odious frog?” said she. But when he fell down he was no frog but a king’s son with kind and beautiful eyes. He by her father’s will was now her dear companion and husband. Then he told her how he had been bewitched by a wicked witch, and how no one could have delivered him from the well but herself, and that tomorrow they would go together into his kingdom.Then they went to sleep, and next morning when the sun“I have eaten and am satisfied, now I am tired; carry me into your little room and make your little silken bed ready, and we will both lie down and go to sleep.”The king’s daughter began to cry, for she was afraid of the cold frog, which she did not like to touch, and which was now to sleep in her pretty, clean little bed. But the king grew angry and said, “He who helped you when you were in trouble ought not afterwards to be despised by you.” So she took hold of the frog with two fingers, carried him upstairs, and put him in a corner. But when she was in bed he crept to her and said, “I am tired, I want to sleep as well as you; lift me up or I will tell your father.” At this she was terribly angry, and took him up and threw him with all her might against the wall. “Now, will you be quiet, odious frog?” said she. But when he fell down he was no frog but a king’s son with kind and beautiful eyes. He by her father’s will was now her dear companion and husband. Then he told her how he had been bewitched by a wicked witch, and how no one could have delivered him from the well but herself, and that tomorrow they would go together into his kingdom.Then they went to sleep, and next morning when the sunawoke them, a carriage came driving up with eight white horses, which had white ostrich feathers on their heads, and were harnessed with golden chains; and behind stood the young king’s servant Faithful Henry. Faithful Henry had been so unhappy when his master was changed into a frog, that he had caused three iron bands to be laid round his heart, lest it should burst with grief and sadness. The carriage was to conduct the young king into his kingdom. Faithful Henry helped them both in, and placed himself behind again, and was full of joy because of this deliverance. And when they had driven a part of the way, the king’s son heard a cracking behind him as if something had broken. So he turned round and cried, “Henry, the carriage is breaking.”“No, master, it is not the carriage. It is a band from my heart, that was put there in my great pain when you were a frog and imprisoned in the well.” Again and once again while they were on their way something cracked, and each time the king’s son thought the carriage was breaking; but it was only the bands that were springing from the heart of Faithful Henry because his master was set free and was so happy.awoke them, a carriage came driving up with eight white horses, which had white ostrich feathers on their heads, and were harnessed with golden chains; and behind stood the young king’s servant Faithful Henry. Faithful Henry had been so unhappy when his master was changed into a frog, that he had caused three iron bands to be laid round his heart, lest it should burst with grief and sadness. The carriage was to conduct the young king into his kingdom. Faithful Henry helped them both in, and placed himself behind again, and was full of joy because of this deliverance. And when they had driven a part of the way, the king’s son heard a cracking behind him as if something had broken. So he turned round and cried, “Henry, the carriage is breaking.”“No, master, it is not the carriage. It is a band from my heart, that was put there in my great pain when you were a frog and imprisoned in the well.” Again and once again while they were on their way something cracked, and each time the king’s son thought the carriage was breaking; but it was only the bands that were springing from the heart of Faithful Henry because his master was set free and was so happy.
sitting by the well, playing, my golden ball fell into the water. And because I cried so, the frog brought it out again for me; and because he so insisted, I promised him he should be my companion, but I never thought he would be able to come out of his water. And now he is outside there, and wants to come in to see me.”
In the meantime it knocked a second time, and cried, “Princess, youngest princess, open the door for me. Do you not know what you said to me yesterday by the cool waters of the well? Princess, youngest princess, open the door for me!”
Then said the king, “That which you have promised must you perform. Go and let him in.” She went and opened the door, and the frog hopped in and followed her, step by step, to her chair. There he sat and cried, “Lift me up beside you.” She delayed, until at last the king commanded her to do it. Once the frog was on the chair he wanted to be on the table, and when he was on the table he said, “Now, push your little golden plate nearer to me, that we may eat together.” She did this, but it was easy to see that she did not do it willingly. The frog enjoyed what he ate, but almost every mouthful she took choked her. At length he said,
sitting by the well, playing, my golden ball fell into the water. And because I cried so, the frog brought it out again for me; and because he so insisted, I promised him he should be my companion, but I never thought he would be able to come out of his water. And now he is outside there, and wants to come in to see me.”
In the meantime it knocked a second time, and cried, “Princess, youngest princess, open the door for me. Do you not know what you said to me yesterday by the cool waters of the well? Princess, youngest princess, open the door for me!”
Then said the king, “That which you have promised must you perform. Go and let him in.” She went and opened the door, and the frog hopped in and followed her, step by step, to her chair. There he sat and cried, “Lift me up beside you.” She delayed, until at last the king commanded her to do it. Once the frog was on the chair he wanted to be on the table, and when he was on the table he said, “Now, push your little golden plate nearer to me, that we may eat together.” She did this, but it was easy to see that she did not do it willingly. The frog enjoyed what he ate, but almost every mouthful she took choked her. At length he said,
“I have eaten and am satisfied, now I am tired; carry me into your little room and make your little silken bed ready, and we will both lie down and go to sleep.”
The king’s daughter began to cry, for she was afraid of the cold frog, which she did not like to touch, and which was now to sleep in her pretty, clean little bed. But the king grew angry and said, “He who helped you when you were in trouble ought not afterwards to be despised by you.” So she took hold of the frog with two fingers, carried him upstairs, and put him in a corner. But when she was in bed he crept to her and said, “I am tired, I want to sleep as well as you; lift me up or I will tell your father.” At this she was terribly angry, and took him up and threw him with all her might against the wall. “Now, will you be quiet, odious frog?” said she. But when he fell down he was no frog but a king’s son with kind and beautiful eyes. He by her father’s will was now her dear companion and husband. Then he told her how he had been bewitched by a wicked witch, and how no one could have delivered him from the well but herself, and that tomorrow they would go together into his kingdom.
Then they went to sleep, and next morning when the sun
“I have eaten and am satisfied, now I am tired; carry me into your little room and make your little silken bed ready, and we will both lie down and go to sleep.”
The king’s daughter began to cry, for she was afraid of the cold frog, which she did not like to touch, and which was now to sleep in her pretty, clean little bed. But the king grew angry and said, “He who helped you when you were in trouble ought not afterwards to be despised by you.” So she took hold of the frog with two fingers, carried him upstairs, and put him in a corner. But when she was in bed he crept to her and said, “I am tired, I want to sleep as well as you; lift me up or I will tell your father.” At this she was terribly angry, and took him up and threw him with all her might against the wall. “Now, will you be quiet, odious frog?” said she. But when he fell down he was no frog but a king’s son with kind and beautiful eyes. He by her father’s will was now her dear companion and husband. Then he told her how he had been bewitched by a wicked witch, and how no one could have delivered him from the well but herself, and that tomorrow they would go together into his kingdom.
Then they went to sleep, and next morning when the sun
awoke them, a carriage came driving up with eight white horses, which had white ostrich feathers on their heads, and were harnessed with golden chains; and behind stood the young king’s servant Faithful Henry. Faithful Henry had been so unhappy when his master was changed into a frog, that he had caused three iron bands to be laid round his heart, lest it should burst with grief and sadness. The carriage was to conduct the young king into his kingdom. Faithful Henry helped them both in, and placed himself behind again, and was full of joy because of this deliverance. And when they had driven a part of the way, the king’s son heard a cracking behind him as if something had broken. So he turned round and cried, “Henry, the carriage is breaking.”
“No, master, it is not the carriage. It is a band from my heart, that was put there in my great pain when you were a frog and imprisoned in the well.” Again and once again while they were on their way something cracked, and each time the king’s son thought the carriage was breaking; but it was only the bands that were springing from the heart of Faithful Henry because his master was set free and was so happy.
awoke them, a carriage came driving up with eight white horses, which had white ostrich feathers on their heads, and were harnessed with golden chains; and behind stood the young king’s servant Faithful Henry. Faithful Henry had been so unhappy when his master was changed into a frog, that he had caused three iron bands to be laid round his heart, lest it should burst with grief and sadness. The carriage was to conduct the young king into his kingdom. Faithful Henry helped them both in, and placed himself behind again, and was full of joy because of this deliverance. And when they had driven a part of the way, the king’s son heard a cracking behind him as if something had broken. So he turned round and cried, “Henry, the carriage is breaking.”
“No, master, it is not the carriage. It is a band from my heart, that was put there in my great pain when you were a frog and imprisoned in the well.” Again and once again while they were on their way something cracked, and each time the king’s son thought the carriage was breaking; but it was only the bands that were springing from the heart of Faithful Henry because his master was set free and was so happy.
Рис. 6 (продолэюениеУ Несмотря на то, что для верстки без выравнивания правильнее было бы использовать несколько иной критерий оптимальности, строки получились примерно одинаковой длины.
Король-лягушонок, или Железный Генрих
В стародавние времена, когда заклятья еще помогали, жил-был на свете король; все дочери были у него красавицы, но самая младшая была так прекрасна, что даже солнце, много видавшее на своем веку, и то удивлялось, сияя на ее лице.
Вблизи королевского замка раскинулся большой дремучий лес, и был в том лесу под старою липой колодец; и вот в жаркие дни младшая королевна выходила в лес, садилась на край студеного колодца, и когда становилось ей скучно, она брала золотой мяч, подбрасывала его вверх и ловила, — это было ее самой любимой игрой.
Но вот однажды, подбросив свой золотой мяч, она поймать его не успела, он упал наземь и покатился прямо в колодец. Королевна глаз не спускала с золотого мяча, но он исчез, а колодец был такой глубокий, такой глубокий, что и дна было не видать. Заплакала тогда королевна, и стала плакать все сильней и сильней, и никак не могла утешиться.
Вот горюет она о своем мяче и вдруг слышит — кто-то ей говорит:
— Что с тобой, королевна? Ты так плачешь, что и камень разжалобить можешь.
Она оглянулась, чтоб узнать откуда это голос, вдруг видит — лягушонок высунул из воды свою толстую, уродливую голову.
— A-а, это ты, старый квакун, — сказала она, — я плачу о своем золотом мяче, что упал в колодец.
— Успокойся, чего плакать, — говорит лягушонок, —я тебе помогу. А что ты мне дашь, если я найду твою игрушку?
— Все, что захочешь, милый лягушонок, — ответила королевна. — Мои платья, жемчуга, драгоценные камни и в придачу золотую корону, которую я ношу.
Говорит ей лягушонок:
— Не надо мне ни твоих платьев, ни жемчугов, ни драгоценных камней, и твоей золотой короны я не хочу; а вот если б ты меня полюбила да со мной подружилась, и мы играли бы вместе, и сидел бы я рядом с тобою за столиком,
ел из твоей золотой тарелочки, пил из твоего маленького кубка и спал с тобой вместе в постельке, — если ты мне пообещаешь все это, я мигом прыгну вниз и достану тебе твой золотой мяч.
— Да, да, обещаю тебе все, что хочешь, только достань мне мой мяч! —А сама про себя подумала: «Что там глупый лягушонок болтает? Сидит он в воде среди лягушек да квакает, — где уж ему быть человеку товарищем!»
Получив с нее обещанье, лягушонок нырнул в воду, опустился на самое дно, быстро выплыл наверх, держа во рту мяч, и бросил его на траву. Увидав опять свою красивую игрушку, королевна очень обрадовалась, подняла ее с земли и убежала.
— Постой, постой! — крикнул лягушонок. — Возьми и меня с собой, ведь мне за тобой не угнаться!
Но что с того, что он громко кричал ей вслед свое «ква-ква»? Она и слушать его не хотела, поспешая домой. А потом и совсем позабыла про бедного лягушонка, и пришлось ему опять спуститься в свой колодец.
На другой день она села с королем и придворными за стол и стала кушать из своей тарелочки. Вдруг —топ-шлеп- шлеп — взбирается кто-то по мраморной лестнице и, взобравшись наверх, стучится в дверь и говорит:
— Молодая королевна, отвори мне дверь! Опа побежала поглядеть, кто бы это мог к ней постучаться. Открывает дверь, видит — сидит перед ней лягушонок. Мигом захлопнула она дверь и уселась опять за стол, но сделалось ей так страшно-страшно. Заметил король, как сильно бьется у нее сердечко, и говорит:
— Дитя мое, чего ты так испугалась? Уж не великан ли какой спрятался за дверью и хочет тебя похитить?
— Ах, нет, — сказала королевна, — это вовсе не великан, а мерзкий лягушонок.
— А что ему от тебя надо?
— Ах, милый батюшка, да вот сидела я вчера в лесу у колодца и играла, и упал в воду мой золотой мяч. Я горько заплакала, а лягушонок достал мне его и стал
требовать, чтоб я взяла его в товарищи, а я и пообещала ему, — но никогда я не думала, чтобы он мог выбраться из воды. А вот теперь он явился и хочет сюда войти.
А тем временем лягушонок постучался опять и кликнул:
Здравствуй, королевна, Дверь открой!
Неужель забыла,
Что всегда сулила, Помнишь, у колодца?
Здравствуй, королевна, Дверь открой!
Тогда король сказал:
— Ты свое обещание должна выполнить. Ступай и открой ему дверь.
Она пошла, открыла дверь, и вот лягушонок прыгнул в комнату, поскакал вслед за ней, доскакал до ее стула, сел и говорит:
— Возьми и посади меня рядом с собой.
Она не решалась, но король велел ей исполнить его желанье. Она усадила лягушонка на стул, а он стал на стол проситься; посадила она его на стол, а он говорит:
— А теперь придвинь мне поближе свою золотую тарелочку, будем есть с тобой вместе.
Хотя она и исполнила это, но было видно, что очень неохотно.
Принялся лягушонок за еду, а королевне и кусок в горло не лезет. Наконец он говорит:
— Я наелся досыта и устал, — теперь отнеси меня в свою спаленку, постели мне свою шелковую постельку, и ляжем с тобой вместе спать.
Как заплакала тут королевна, — страшно ей стало холодного лягушонка,боится до него и дотронуться, а он еще в прекрасной, чистой постельке спать с ней собирается. Разгневался король и говорит:
— Кто тебе в беде помог, тем пренебрегать не годится.
Взяла она тогда лягушонка двумя пальцами, понесла его к себе в спаленку, посадила в углу, а сама улеглась в постельку. А он прыгнул и говорит:
— Я устал, мне тоже спать хочется,— возьми меня к себе, а не то я твоему отцу пожалуюсь.
Рассердилась тут королевна и ударила его изо всех сил об стену.
— Ну, уж теперь, мерзкий лягушонок, ты успокоишься!
Но только упал он наземь, как вдруг обернулся королевичем с прекрасными, ласковыми глазами. И стал с той поры, по воле ее отца, ее милым другом и мужем. Он рассказал ей, что его околдовала злая ведьма, и никто бы не мог освободить его из колодца, кроме нее одной, и что завтра они отправятся в его королевство.
Вот легли они спать и уснули. А на другое утро, только разбудило их солнышко, подъехала ко дворцу карета с восьмериком белых коней, и были у них белые султаны на голове, а сбруя из золотых цепей, и стоял на запятках слуга королевича, а был то верный Генрих. Когда его хозяин был обращен в лягушонка, верный Генрих так горевал и печалился, что велел оковать себе сердце тремя железными обручами, чтоб не разорвалось оно от горя и печали.
И должен был в этой карете ехать молодой король в свое королевство. Усадил верный Генрих молодых в карету, а сам стал на запятках и радовался, что хозяин его избавился от злого заклятья.
Вот проехали они часть дороги, вдруг королевич слышит — сзади что-то треснуло. Обернулся он и крикнул:
— Генрих, треснула карета!
— Дело, сударь, тут не в этом, Это обруч с сердца спал, Что тоской меня сжимал, Когда вы в колодце жили И с лягушками дружили.
Вот опять и опять затрещало что-то в пути, королевич думал, что это треснула карета, но были то обручи, что слетели с сердца верного Генриха, потому что хозяин его избавился от злого заклятья и стал снова счастливым.
(Перевод Г. Петникова. Текст приведен по изданию Братья Гримм. Сказки. М., 1949.)
В приведенных примерах использован вполне обычный текст при достаточно большой ширине полосы, поэтому в процессе верстки нам не пришлось столкнуться с какими-либо серьезными трудностями или необычными явлениями. Тем не менее мы можем констатировать, что оптимизационный алгоритм позволяет получить значительно лучшие результаты даже в простых случаях. По всей видимости, эффективность предлагаемого алгоритма будет еще выше в более сложных ситуациях, например, когда текст содержит математические формулы или когда длина строки мала, скажем при газетной верстке.
Те, кого беспокоит судьба прекрасной принцессы, упоминающейся в тексте на рис. 1-4, могут обратиться к рис. 6 на с. 96-97, где сказка приводится целиком. Колонки на рис. 6 чрезвычайно узкие: в строке умещается всего 21 или 22 литеры, тогда как обычный газетный формат допускает порядка 35 литер в строке, а колонки в журналах зачастую вдвое шире, чем в данном примере. Алгоритмы построчной верстки не справляются со столь жесткими ограничениями, а алгоритм оптимальной верстки, как видно из рис. 6, успешно разбивает абзацы на строки более или менее одинаковой длины. При этом, несмотря на то что величина штрафа за перенос была в данном случае увеличена с 50 до 5000, в тексте оказывается довольно много переносов. Это вполне естественно, поскольку при использовании переносов увеличивается число междуслов- ных пробелов, приходящихся в среднем на строку, и, как следствие, удается несколько подровнять длину строк.
Хотя наша техника верстки разрабатывалась в расчете на текст с выровненным правым краем, в примере на рис. 6 оптимизационый алгоритм использован для верстки текста с рваным правым краем. Для этого применен простейший прием: на последнем этапе, когда все точки разрыва строк уже выбраны, выравнивание текста по правому краю отменяется. На самом деле для отыскания наилучших точек перехода на новую строку в случае рваного правого края нужно использовать иную меру плохости, основанную только лишь на разности между желательной длиной строки lj и действительной ее длиной Lj^ причем должно быть разрешено только растягивать междусловные пробелы, а сжимать — запрещено, и Lj никогда не должно превосходить lj. Кроме того, при наборе текста с рваным правым краем не следует допускать «выброса» слов вправо, т. е. нельзя, чтобы слово начиналось правее конца следующей строки. Например, слово ‘it’ на рис. 5 в действительности следует перенести из первой строки во вторую.
Приведенные примеры показывают, что алгоритм качественной верстки текста с рваным правым краем —вещь несколько более сложная, чем такой же алгоритм для текста с выравниванием, вопреки широко распространенному убеждению, что выравнивание —более трудный случай. С другой стороны, как видно из примера, приведенного на рис. 6, если в алгоритме верстки, ориентированном на выровненный правый край, отказаться от самого выравнивания, сохранив разбиение абзаца на строки, результат получается вполне приемлемый.
Интересной иллюстрацией к проблемам, возникающим при наборе узких столбцов, служит фраза
“Now, push your little golden plate nearer ...”
входящая в четвертый от конца абзац на рис. 6. По причинам, упоминавшимся ранее, перенос в любом из слов этой фразы нежелателен. При этом оказывается, что среди четырех возможных ее фрагментов, состоящих каждый из четырех слов и содержащих слово ‘little’, а именно
“Now, push your littlepush your little goldenyour little golden platelittle golden plate nearerни один не умещается в одну строку. Поэтому вне зависимости от того, каковыбыли предшествующие строки, слово ‘little’ с необходимостью будет распола-гаться в строке, состоящей только из трех слов и, следовательно, содержащейтолько два пробела.
and were harpessed злзт with golden chains; 3.277
and
behind stood
5.740
the young king’s sen-
.783
vant
Faithful Henry.
1.971
Faithful Henry had
3.474
been
so unfiappy
6.616
when
his master was
.940
changed into a frog,
1.612
Проблемы, которые могут возникать при верстке узких столбцов, наглядно проявились в последних абзацах текста на рис. 6. Эти проблемы связаны со сложным взаимовлиянием строк в больших абзацах, из-за чего при верстке с выравниванием иногда просто нельзя избежать большой разреженности. На рис. 7 показано, что будет с текстом на рис. 6, если его все-таки выровнять по правому краю. Приведенный фрагмент оказался самой трудной частью во всем тексте: одну из строк, полученных при оптимальной верстке, для выравнивания по правому краю приходится растягивать с фантастически большим коэффициентом 6.616. Единственная возможность сверстать данный абзац без таких огромных пробелов —это отказаться от выравнивания (если, конечно, мы не имеем возможности изменить либо сам текст, либо длину строки, либо минимальную допустимую величину пробела).
Рис. 7. Если попытаться выровнять по правому краю текст на рис. 6, хуже всего дело будет обстоять с этим фрагментом — здесь даже оптимальная верстка дает очень большие пробелы.
Другие приложения
Прежде чем приступить к обсуждению деталей оптимизационного алгоритма, рассмотрим еще несколько ситуаций, когда задачи, связанные с набором текста, удается решить, используя базовые понятия (примитивы) боксов, клея и штрафов. Некоторые из рассматриваемых ниже применений этих понятий связаны с идеями, обсуждавшимися в связи с рис. 1-4, тогда как другие, на первый взгляд, не имеют к верстке никакого отношения.
В том случае, когда желательные длины строк Ц не одинаковы, часто требуется сверстать два абзаца таким образом, чтобы второй начинался в списке длин там, где заканчивается первый. Это несложно сделать, если обрабатывать два абзаца как один, т. е. добавлять боксы/клей/штраф второго абзаца к первому, считая, что каждый абзац начинается с отступа и кончается завершающим клеем и принудительным разрывом (см. выше).
«Заплатки»
Предположим, что в некой книге абзац начинается на странице 100 и продолжается на странице 101. Допустим, что нам нужно что-то изменить в первой части абзаца. Мы хотим быть уверены в том, что последняя строка новой страницы 100 закончится в точности перед тем словом, с которого начинается страница 101, так чтобы нам не пришлось переверстывать страницу 101. В терминах используемых нами понятий это условие формализуется очень просто: в нужном месте вводится принудительный разрыв строки (путем введения штрафа — оо), а последующий текст отбрасывается. Благодаря своему прогностическому характеру, алгоритм оптимальной верстки найдет приемлемый способ переверстать страницу 100 (поставить на этой странице «заплатку»), если, конечно, такой способ вообще существует.
Кроме того, с помощью нижеследующего приема можно добиться, чтобы измененная часть абзаца состояла из заданного числа строк к. В качестве ^+1—желательной длины (к + 1)-й строки — возьмем число У, отличное от длины любой другой строки. В этом случае пустой бокс шириной У, который появляется между двумя штрафами при принудительном разрыве, должен быть помещен в строку к + 1.
Висячая пунктуация
Некоторые авторы предпочитают, чтобы точки, запятые и другие знаки препинания, включая переносы, находились за пределами правого края строки. Например, так часто делается в современной рекламе. Добиться такого расположения знаков переноса несложно: для этого нужно положить ширину соответствующего элемента-штрафа равной нулю. Почти так же просто сделать это для точек и других знаков препинания: в этом случае нужно поместить каждый такой знак в бокс с шириной, равной нулю, и добавить фактическую ширину символа к клею, который следует за знаком. Если на этом клее не происходит разрыв строки, то полученная ширина будет такой же, как раньше, а если разрыв произойдет, то строка будет выровнена по правому краю так, как если бы знак препинания (точка или другой знак) отсутствовал.
Как избежать «психологически плохих» разрывов
Поскольку компьютеры нс умеют думать (пока, во всяком случае), возникает вопрос, допускает ли компьютер такие разрывы строки, которые отверг бы
человек, т. е. разрывы, которые плохо смотрятся. Эта проблема возникает не очень часто, когда речь идет об обычном тексте, какой встречается в газетах или романах, но она типична для технической литературы. Например, психологически плохим будет разрыв перед ;х' или 6 у’ в следующем предложении:
Функция переменной х — это правило, согласно которому каждому значению х ставится в соответствие некоторое значение ?/.
Компьютер в этой ситуации не имеет никаких ограничений относительно разбиения на строки, если только ему их не задать. Но человек будет избегать плохих разрывов, может быть даже бессознательно.
Непросто сформулировать, что такое психологически плохие разрывы. Мы просто знаем, что они плохие. Когда мы переводим взгляд с конца одной строки на начало другой, то если строки разбиты неудачно, слово, с которого начинается вторая строка, часто кажется изолированным от контекста. Представьте себе, что предложение, которое начинается на одной странице и заканчивается на другой, разрывается между словами «Глава» и «8». Вы, естественно, подумаете, что наборщик не должен был разбивать текст таким нелогичным образом.
В первые годы работы с ТЁК’ом авторы этой статьи стали замечать, что некоторые разрывы строк смотрятся плохо, хотя проблема не казалась настолько серьезной, чтобы предпринимать какие-либо действия. Однако в конце концов мы стали сомневаться в справедливости нашего представления о том, что ТЁК обладает лучшим в мире алгоритмом верстки, поскольку компьютер периодически допускал досадные с точки зрения семантики разрывы. Например, в первом руководстве по ТЁХ’у [26] было немало таких разрывов, а его предварительные версии были еще хуже.
Со временем психологически плохие разрывы стали раздражать авторов все больше и больше, и это касалось не только книг, подготовленных в ТЁХ’е, но и других изданий. Пришло время выяснить, действительно ли это вина компьютеров. В связи с этим было проведено исследование, которое заключалось в сравнении первой тысячи разрывов строк в журнале ACM Journal 1960 года (который был подготовлен вручную на монотипе) с первой тысячей разрывов строк в журнале ACM Journal 1980 года (который был набран при помощи лучшей коммерческой компьютерной системы для набора математических текстов, разработанной компанией Penta Systems International). Последние строки абзацев и строки, предшествующие выключным формулам, не рассматривались, так как разрывы в них были принудительными. Использовался текст только самих статей, но не библиографий. Если читатель захочет повторить этот эксперимент, то он обнаружит, что тысячный разрыв в 1960 году появился на с. 67, тогда как в 1980 году —на с. 64. Результаты нашего (конечно же, субъективного) анализа выглядят следующим образом:
13 плохих разрывов в 1960 г.,55 плохих разрывов в 1980 г.
Иными словами, количество плохих разрывов возросло более чем в 4 раза, с 1 до 5.5%, а это уже ощутимо! Конечно, нельзя считать этот опыт абсо-
лютно убедительным, поскольку стиль статей, помещенных в журнале АСМ Journal, менялся, однако очевидно, что когда компьютер выбирает разрывы, руководствуясь исключительно визуальным критерием, это не идет на пользу смысловому восприятию текста.
Как только стало ясно, в чем заключается проблема, была сделана попытка убрать все такие разрывы из второго издания книги Seminumerical Algorithms [28] —первой большой книги, подготовленной в T£X’e. Заставить алгоритм верстки избегать определенных разрывов несложно, для этого надо просто ставить перед клеем штраф величины, скажем рг = 999, и тогда плохой разрыв будет выбран только в случае крайней необходимости, если нет другого подходящего способа сверстать абзац. Для того чтобы облегчить работу наборщика, можно также ввести специальный символ (например, ‘~’), который используется вместо обычного пробела между теми словами, где разрыв нежелателен. Хотя эта проблема редко обсуждалась в литературе, авторы впоследствии обнаружили, что у некоторых полиграфистов есть термин для обозначения таких пробелов —они называют их вспомогательными. Значит, эта проблема многим знакома.
Условимся называть такие пробелы связками. Имеет смысл перечислить основные случаи использования связок в книге Seminumerical Algorithms, поскольку круг рассматриваемых там технических вопросов достаточно широк. Следующие правила наверняка помогут тем, кто набирает на компьютере технические рукописи.
Пользуйтесь связками в перекрестных ссылках:
Теорема~А АлгоритмовГлава~3
Таблица~4 Программы Е и~Е
В последнем примере после слова ‘Программы’ связка не нужна, так как если строка начнется с ‘Е и F’, то это будет выглядеть нормально.
Пользуйтесь связками при наборе имен и фамилий, состоящих из нескольких частей:
Dr.~I.~J. MatrixLuis~I. Trabb~Pardo
Peter van~Emde~Boas
Возникшая в последнее время тенденция не делать пробелов между инициалами — это протест против обычного компьютерного алгоритма верстки! Следует иметь в виду, что лучше уж сделать перенос в одной из частей имени, чем допустить разрыв между его частями. Так, например, ‘Don-’ и ‘aid Е. Knuth’ выглядит лучше, чем ‘Donald’ и ‘Е. Knuth’. В некотором смысле первое правило является частным случаем второго, поскольку ‘Теорема~А’ может рассматриваться как имя. Другой пример —‘регистр~Х’.
Пользуйтесь связками при наборе существительных с дополнительными символами:
основание^измерение^
функция~/(ж)ряд~5 длины~/
Но: ‘строковая переменная"з длины /"или большей’.
Пользуйтесь связками при наборе элементов перечислений: 1,~2 или~3 а~Ь и~с 1/2, ... ~п
Пользуйтесь связками при наборе символов, являющихся строго связанными объектами препозиции:
от"жот 0 до"1
увеличить х на"1одновременно с"т
Это правило не относится к сложным объектам; ср., например, ‘от и~и~г)\
Пользуйтесь связками для того, чтобы избегать неудачных разрывов в предложениях, содержащих математические выражения: равно~пменьше~бmod~2
(для данного~Х)когда ^возрастаетесли Сравно ...
по модулю "ре
Для сравнения: ‘равно" 15’, но ‘в 15~раз больше’; ‘для всех болыпйх~п’, но ‘для всех п,"больших, чем"по’.
Пользуйтесь связками при нумерации под случаев:
(б)"Покажите, что /(ж) (1)"непрерывна; (2)"ограниченна.
Было бы неплохо свести эти семь правил к одному или двум, а еще лучше— автоматизировать их использование, чтобы не думать о них при наборе текста, однако во многих примерах нужно учитывать тонкие семантические соображения. Большинство психологически плохих разрывов возникает тогда, когда символ или небольшая группа символов появляется непосредственно перед разрывом или сразу после него. При автоматической расстановке связок разрывы перед короткими сочетаниями символов, не являющимися словами, должны ассоциироваться с большими штрафами, а разрывы после коротких сочетаний —со штрафами средней величины. Под короткими сочетаниями мы понимаем последовательности символов, которые, хотя и являются не очень длинными, но все же охватывают выражения, типа ‘упражнение"15(Ь)’, ‘длина"235’, ‘порядок" п/2’, с последующими знаками препинания; при этом выражения, состоящие только из одного или двух символов, не рассматриваются. С другой стороны, не возбраняется разрывать строку перед длинным сочетанием символов или после него; например, в сочетании ‘упражнение 4.3.2-15’ после слова ‘упражнение’ связка не нужна.
Во многих руководствах по набору содержатся рекомендации не разрывать строку непосредственно перед последним словом абзаца, особенно, если это слово короткое. Конечно же, этого легко добиться, поставив связку перед последним словом, причем компьютер может вставлять эту связку автоматически. Некоторые пособия дают рекомендации, аналогичные вышеприведенному правилу 2, которые заключаются в том, что наборщики не должны разрывать строку в середине имени человека. Но, по всей видимости, есть только одна книга, в которой рассматриваются и другие случаи психологически пло-
хих разрывов. Это французское руководство XIX века, составленное А. Фри с. 110], где упомянуты следующие примеры нежелательных разрывов:
АнриЧУ М. Жолин 1~сентябряart/2520~fr.
Видимо, настало время возродить старые полиграфические традиции.
Опыт авторов свидетельствует о том, что при наборе рукописей на компьютере проставление связок нс воспринимается как лишняя обременительная работа. На самом деле если, набирая текст, аккуратно проставлять такие «неразрываемые» пробелы, то можно получить более полное удовлетворение от работы, поскольку сравнительно небольшой труд приводит к заметному улучшению качества оригинал-макета. Иногда приятно сознавать, что машина нуждается в твоей помощи.
Строки с информацией об авторах
Большинство рецензий, публикуемых в Mathematical Reviews, подписываются фамилией автора с его адресом, и эта информация сдвигается вправо, точнее — выравнивается по правому краю. Если на последней строке абзаца достаточно места для размещения этой информации, то издатель может сэкономить бумагу и результат будет выглядеть лучше, так как можно будет избежать странных коридоров на странице. Программное обеспечение, используемое в последние годы Американским математическим обществом, не позволяло автоматически выполнять эту операцию, но количество экономящейся при этом бумаги сделало экономически выгодным платить наемному работнику за то, чтобы он вручную, где возможно, перемещал этот текст вверх на компьютерной распечатке при помощи ножниц и (настоящего) клея.
Это случай, когда фамилия и адрес автора удачно вписываютсяв текст рецензии.A. Reviewer (Ann Arbor, Mich.)
Но иногда бывает нужно добавить новую строку.
N. Bourbaki (Paris)
Рис. 8. Задача MR.
Назовем «задачей MR» выравнивание содержимого данного бокса в правый край в конце данного абзаца с пробелом не менее w между абзацем и этим боксом, если они находятся на одной строке. Эта задача может быть решена полностью в терминах базовых понятий бокс/клей/штраф следующим образом:
(текст данного абзаца)
штраф(0, оо, 0)
клей(0, 100000, 0)
штраф(0, 50, 0)
клей(ги, 0, 0)
бокс(О)
штраф(0, оо, 0)
клей(0, 100000, 0) * (данный бокс) штраф(0, —оо, 0)
Последний штраф — оо вынуждает разбить последнюю строку с последующей выключкой данного бокса вправо; два штрафа +оо используются для предотвращения разбиения в местах последующего клея. Таким образом, приведенная выше последовательность сводится к двум случаям: разбивается строка со штрафом 50 или нет. Если разбиение происходит, то ‘клей^, 0, 0)’ исчезает согласно нашему правилу, гласящему, что каждая строка начинается с бокса; за текстом абзаца, предшествующим штрафу 50, будет помещен *клей(0, 100000, 0)’, который растянет строку так, как если бы абзац заканчивался обычным образом, а данному боксу на последней строке также будет предшествовать ‘клей(0, 100000, 0)’ для получения промежутка слева. С другой стороны, если при штрафе 50 разбиения не произойдет, то, в результате все элементы клея сложатся, произведя (текст данного абзаца) клей(ги, 200000, 0) (данный бокс)
так чтобы пространство между абзацем и боксом было w или больше. Независимо от того, произойдет разбиение или нет, плохость последней строки окажется существенно нулевой, потому что будет слишком большое растяжение. Таким образом, относительная стоимость разделения на две альтернативы почти целиком вызвана штрафом 50. Алгоритм оптимального разбиения (оптимальной верстки) выберет лучшую из альтернатив, основываясь на тех возможностях, которые заложены в данном абзаце; он даже может несколько уплотнить абзац, если это приведет к лучшему результату.
Рваный правый край
Как мы уже видели на рис. 6, алгоритм оптимальной верстки, предназначенный для выравнивания текста, прекрасно справляется со своей работой, выдавая строки почти одинаковой длины, даже когда их впоследствии не нужно выравнивать. Можно, однако, легко построить примеры, в которых метод, предназначенный для выравнивания, дает плохое решение, поскольку величина отклонений в длине строки целиком зависит от величины имеющих место сжатий или растяжений. Строка, состоящая из большого количества слов, а, следовательно, содержащая много междусловных пробелов, не вызывает проблем с точки зрения критерия выравнивания, даже если эта строка более короткая или более длинная, потому что имеется достаточно клея для того, чтобы вполне корректно выполнить ее растяжение или сжатие. И напротив, когда строка состоит всего из нескольких слов, алгоритм будет прилагать все усилия к тому, чтобы избежать относительно малых отклонений. Это проиллюстрировано на рис. 5, который на самом деле чи-
тается лучше, чем соответствующий абзац на рис. 6 (за исключением «выбившегося» слова в первой строке); в этом абзаце на рис. 6 были выполнены переносы, чтобы получить больше междусловных пробелов для выравнивания.
Хотя модель бокс/клей/штраф на первый взгляд кажется ориентированной исключительно на выравнивание текста, на самом деле она достаточно мощная, чтобы подходить для работы с аналогичной проблемой при наборе с рваным краем. Если промежутки между словами оформлены правильно, можно так подобрать эти вещи, что в каждой строке будет одинаковая величина растяжения, независимо от того, сколько в ней слов. Идея состоит в том, чтобы междусловные интервалы были представлены последовательностью
клей(0, 18, 0) штраф(0, 0, 0) клей(6, —18, 0)
а не ‘клей(6, 3, 2)’, который мы использовали для выровненного набора. Можно предположить, что в точке ‘клей(0, 18, 0)’ разбиения не будет, потому что алгоритм не может сделать плохого разбиения со штрафом ‘штраф(0, 0, 0)’, когда растяжение представлено 18 единицами. Если разбиение с таким штрафом произойдет, то на строке будет растяжение в 18 единиц и ‘клей(6, —18, 0)’ будет выброшен за ненадобностью, поскольку следующая строка должна быть прижата к левому краю. С другой стороны, если разбиение есть, то в результате имеем клей(6, 0,0), представляющий собой обычный пробел без растяжения или сжатия.
Растяжение —18 во втором элементе клея не имеет физического смысла, и благополучно взаимно уничтожается с растяжением 4-18 в первом элементе клея. Отрицательное растяжение имеет несколько интересных приложений, так что читателю следует досконально изучить этот пример, прежде чем приступать к более сложным конструкциям, приводимым ниже.
Необязательные переносы в невыровненном тексте могут быть описаны аналогичным образом; вместо записи ‘штраф(6, 50, 1)’ для необязательного 6-элементного переноса, имеющего штраф 50, можно использовать последовательность
штраф(0, оо, 0) клей(0, 18, 0) штраф(6, 500, 1) клей(0, —18, 0).
Здесь штраф возрастает от 50 до 500, так как переносы в невыровненном тексте менее желательны. После того как точки разбиения выбраны при помощи приведенной выше последовательности для пробелов и необязательных переносов, отдельные строки не должны выравниваться; в противном случае перенос, вставленный посредством ‘штраф(6, 500, 1)’, оказался бы в правом поле.
Нетрудно доказать, что такой подход к набору с рваным правым краем никогда не приведет к «выбившимся» словам, как в первой строке на рис. 5;
суммарный дефект уменьшается, как только выбившееся слово перемещается на следующую строку.
Центрированный текст
Иногда бывает нужно разбить не помещающийся в одну строку текст на приблизительно равные части так, чтобы каждая часть разместилась по центру отдельной строки. Такая необходимость часто возникает в случае заголовков или подрисуночных подписей, но можно так же поступить и с обычным абзацем как показано на рис. 9.
Боксы, клей и штрафы в подобных случаях представлены следующим образом: (а) в начале абзаца используем ‘клей(0, 18, 0)’ вместо равнения вправо; (Ь) для каждого междусловного пробела в абзаце используем последовательность
клей(0, 18, 0)
штраф(0, 0, 0) клей(6, —36, 0) бокс(О) штраф(0, оо, 0) клей(0, 18, 0),
(с) в конце абзаца —последовательность
клей(0, 18, 0) штраф(0, —оо, 0).
Весь фокус метода состоит в части (Ь), в которой подразумевается, что необязательное разбиение со штрафом ‘штраф(0, 0, 0)’ приводит к растяжению в 18 единиц в конце одной строки и в начале следующей. Если разбиения не происходит, сухим остатком будет клей(0, 18, 0) + клей(6, —36, 0) + клей(0, 18, 0) = клей(6, 0, 0), т. е. фиксированный пробел в 6 единиц. Ъокс(0)’ не содержит текста и не занимает никакого пространства; его назначение состоит в том, чтобы предотвратить исчезание элемента ‘клей(0, 18, 0)’ в начале строки.
In olden times when wishing still helped one, there lived a king whose daughterswere all beautiful; and the youngest was so beautiful that the sun itself, which hasseen so much, was astonished whenever it shone in her face. Close by the king’scastle lay a great dark forest, and under an old lime-tree in the forest was a well,and when the day was very warm, the king’s child went out into the forest and satdown by the side of the cool fountain; and when she was bored she took a goldenball, and threw it up on high and caught it; and this ball was her favorite plaything.
Рис. 9. «Центрированный» текст: алгоритм оптимальной верстки выдает специальные эффекты, подобно представленным здесь, когда для междусловных пробелов используются соответствующие элементы бокс/клей/штраф.
‘Штрафу, 0, 0)’ может быть заменен другими штрафами, представляющими точку разбиения более или менее сносно. Эта техника, однако, не может быть использована вместе с необязательным переносом, поскольку наша модель
бокс/клей/штраф не способна вставлять переносы повсюду, за исключением правого края в случае выровненных строк.
Использованная здесь конструкция существенно минимизирует величину максимального расстояния между полями и текстом в любой строке; подверженная минимизации величина существенно минимизирует максимальное расстояние в оставшихся строках, и т. д. Причина кроется в том, что наши определения плохости и дефектности сводятся в этом случае к тому, что сумма дефектов при любом выборе точек разбиения приблизительно пропорциональна сумме шестых степеней отдельных расстояний. •
АЛГОЛо-подобные языки
Одна из наиболее сложных задач набора технических текстов состоит в том, чтобы получать хорошо оформленные тексты компьютерных программ. Мало того, что мы имеем дело со сложными математическими формулами, разнообразием шрифтов и соглашений о пробелах, мы еще хотим так расположить строки, чтобы отобразить вложенность структуры компьютерной программы. Иногда единое утверждение бывает разбито на несколько отдельных строк; иногда короткие утверждения должны группироваться на единой строке. Программисты и специалисты в области информатики, предпринимавшие попытки публиковаться в журналах, не имевших дело с такими материалами, вскоре обнаруживали, что лишь небольшое число типографий имеют достаточно высокий уровень для представления ALGOLo-подобных языков удовлетворительным образом.
Снова па помощь приходит концепция боксов, клея и штрафов: это значит, что наш метод разбиения на строки, разработанный для обычного текста, может быть использован без изменений для набора программ на алголо-подобных языках. На рис. 10, например, приводится типичная программа, взятая из руководства [23] по Паскалю, которая должна была быть набрана в две колонки различной ширины. Хотя эти два фрагмента программ не кажутся очень похожими, они оба были произведены одним и тем же исходным кодом и определены в терминах боксов, клея и штрафов; единственным отличием была длина строки. (Исходный текст этого примера был подготовлен при помощи программы Blaise [27], которая оттранслировала некий исходный текст на Паскале в Т^Х-файл для последующего объединения с другими документами.)
Спецификации бокс/клей/штраф, которые приводят к рис. 10, включают в себя конструкции, аналогичные тем, что мы видели ранее, но с некоторыми новыми особенностями; для наших целей достаточно вкратце очертить идеи, не погружаясь в подробности. Один из ключевых моментов состоит в том, что мы выбираем точки разбиения согласно критерию минимизации дефектности, который мы обсуждали выше, но строки впоследствии не выравниваются (т. е. клей фактически не сжимается и не растягивается). Причина кроется в том, что отношения и присваивания обрабатываются Т^’ом в «математическом режиме», который допускает разбиения строк в различных местах, но без каких бы то ни было специальных конструкций, так что выравнивание происходит с
const 11 = 10000;
var sieve, primes :
set of 2 .. n;
next,] : integer*, begin { инициализировать } sieve := [2.. n];
primes := [];
next 2;
repeat { найти следующее простое }
while not (next in
sieve) do
next :=
Рис. 10. Эти два фрагмента программы на Паскале взяты из ввода, оформленного как модель бокс/клей/штраф; В первом случае строки были ограничены длиной в 10 ет, а во втором —25 ет. Все разбиения строк и втяжки производились автоматически посредством алгоритма оптимальной верстки, который не имел специальных знаний в языке Паскаль. Процесс компиляции исходного текста программы на Паскале в боксы, клей и штрафы провела компьютерная программ под названием Blaise.Рис. 10. Эти два фрагмента программы на Паскале взяты из ввода, оформленного как модель бокс/клей/штраф; В первом случае строки были ограничены длиной в 10 ет, а во втором —25 ет. Все разбиения строк и втяжки производились автоматически посредством алгоритма оптимальной верстки, который не имел специальных знаний в языке Паскаль. Процесс компиляции исходного текста программы на Паскале в боксы, клей и штрафы провела компьютерная программ под названием Blaise.succ(next);
Рис. 10. Эти два фрагмента программы на Паскале взяты из ввода, оформленного как модель бокс/клей/штраф; В первом случае строки были ограничены длиной в 10 ет, а во втором —25 ет. Все разбиения строк и втяжки производились автоматически посредством алгоритма оптимальной верстки, который не имел специальных знаний в языке Паскаль. Процесс компиляции исходного текста программы на Паскале в боксы, клей и штрафы провела компьютерная программ под названием Blaise.
Рис. 10. Эти два фрагмента программы на Паскале взяты из ввода, оформленного как модель бокс/клей/штраф; В первом случае строки были ограничены длиной в 10 ет, а во втором —25 ет. Все разбиения строк и втяжки производились автоматически посредством алгоритма оптимальной верстки, который не имел специальных знаний в языке Паскаль. Процесс компиляции исходного текста программы на Паскале в боксы, клей и штрафы провела компьютерная программ под названием Blaise.
primes :=
primes + [next];
j :— next;
while j < — n do
{ удалить }
begin sieve := sieve - [j];
3 :-.-_- j + next
end
until sieve = [] end.
const n = 10000;
var sieve, primes : set of 2 .. n;
' next,j : integer;
begin { инициализировать }
sieve := [2.. n]; primes := []; next := 2;
repeat { найти следующее простое }
while not (next in sieve) do next := succ^ext);
primes := primes 4- [nej;^; j :— next;
while j <= n do { удалить }
begin sieve := sieve — [j]; 3 \— 3 -V next
end
until sieve = []
end.
нежелательным эффектом размещения всех таких разбиений в правом поле. Тот факт, что такое выравнивание подавляется, в данном случае оказывается преимуществом, поскольку означает, что мы можем вставить растяжимый клей где хотим внутри строки, если это повлияет на формулу «плохости» соответствующим образом.
Каждая строка в более широком отрывке на рис. 10 фактически является «абзацем» сама по себе, так что только в узком отрывке продемонстрирована работа механизма разбиения строк. Каждый абзац имеет определенную втяжку первой строки, соответствующую его положению в программе, выраженную t единицами «табуляций». Сам абзац также имеет заданный размер втяжки, выраженный t + 2 единицами табуляций; это означает, что все строки после первой будут уже первой на 2 единицы табуляции и сдвинуты относительно нее на две единицы вправо. В некоторых случаях (например, когда строки начинаются с var’ или с ‘while’) втяжка равна трем единицам табуляции вместо двух.
Этот абзац начинается элементом ‘клей(0, 100000, 0)’, который доставляет достаточное растяжение для того, чтобы алгоритм разбиения строк не содрогался слишком часто от того, что разбиения не дают совершенного квадрата у правого поля, по крайней мере в первой строке. Специальные разбиения вставляются в тех местах, где ТеХ не может нормально разбить строку в математическом режиме, например, последовательность
штраф(0, оо, 0) клей(0, 100000, 0) штраф(0, 50, 0) клей(0, —100000, 0) бокс(0) штраф(0, оо, 0) клей(0, 100000, 0)
должна быть вставлена непосредственно перед ‘primes' в декларации var. Эта последовательность позволяет сделать разбиение со штрафом 50 и перейти па следующую строку, которая начинается с многочисленных растяжений. Аналогичная конструкция используется между присваиваниями, например, между ‘siere := [2 . . п];’ и 1 primes := [ ]’, где последовательность выглядит так:
штраф(0, оо, 0) клей(0, 100000, 0) штраф(0, 0, 0) клей(6 + 2w, —100000, 0) бокс(0) штраф(0, оо, 0) клей(—2w, 100000,0);
Здесь w— размер единицы табуляции. Если разбиение произошло, то следующая строка начинается с ‘клей(—2w, 100000, 0)’, который подавляет эффект навешивания втяжки и эффективно восстанавливает положение начала абза-
ца. Если разбиения не происходит, то окончательный результат представлен как ‘клей(6, 100000, 0)’, обычный пробел.
Автоматическая система не может рассчитывать на обнаружение наилучших точек разбиения программ, так как только разбираясь в семантике можно указать, где некоторые разбиения улучшат восприятие программы и обнаружат скрытую симметрию. Десятки экспериментов с самыми разнообразными исходными текстами на Паскале показали, однако, что описанный подход оказался на удивление эффективным: авторами программ были изменены менее 1% разбиений строк для улучшения их восприятия.
Сложный указатель
Последнее приложение разбиений на строки, которое мы рассмотрим, представляет собой наиболее трудный материал из всех, ранее нам встречавшихся. С этим вопросом удалось справиться лишь после того, как мы приобрели более чем двухлетний опыт решения простых задач разбиения, так как вся
ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers *1858
ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976) 1879,4813,
5414, 6918, 6936, 6937, 6946, 6951, 6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869
Software See*1858
ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers ... *1858ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976) 1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, 6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869Software See *1858ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers ... *1858ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976) 1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, 6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869Software See *1858ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers *1858
ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers ... *1858
ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976) 1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, 6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869
Software See *1858
ACM Symposium on Principles of Programming Languages, Third (Atlanta, Ga., 1976), selected papers ... *1858
ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976) 1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, 6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869
Software See *1858
ACM Symposium on Theory of Computing, Eighth Annual (Hershey, Pa., 1976)
1879, 4813, 5414, 6918, 6936, 6937,
6946, 6951, 6970, 7619, 9605, 10148,
11676, 11687, 11692, 11710, 13869
Software See*1858
Рис. 11. Эти три выдержки из указателя ключевых слов были получены из одного исходного файла и набраны с разными ширинами колонок: 22.5, 17.5 и 12.5 ет соответственно. Обратите внимание на комбинацию рваных правого и левого краев и на «отточия».
мощь методики бокс/клей/штраф не была столь очевидна сразу. Эта задача иллюстрируется на рис. 11, в котором представлены выдержки из указателя ключевых слов журнала Mathematical Reviews. Подобным указателем теперь снабжен в конце каждый том, равно как и именным указателем в аналогичном формате.
Как и на рис. 10 примеры на рис. 11 были получены при помощи одного и того же исходного кода, но набирались они с разной длиной строки, чтобы проиллюстрировать разнообразие возможностей разбиения. Каждый термин указателя состоит из двух частей: собственно термин и ссылка, причем обе части могут оказаться слишком длинными и нс уместиться в одну строку. Если разбиение строки происходит в собственно термине, отдельные строки оформляются с рваным правым краем, но если разбиение происходит в ссылках, то получаются строки с рваным левым краем. Эти две части отделяются друг от друга отточием — строкой из точек и пробелов между ними; отточия вводятся при помощи некоторого обобщения понятия клея, которое помещает набор в данном боксе в данный промежуток, вместо того чтобы оставить этот промежуток пустым. Навешивание абзацного отступа применяется ко всем строкам, кроме первой, поэтому первая строка каждого термина легко идентифицируется. Одна из целей разбиения таких элементов на строки состоит в минимизации пустого пространства, которое возникает в строках с рваными правым и левым краями. Вспомогательная цель состоит в минимизации количества строк, содержащих ссылки на страницы. Например, если возможно вместить все ссылки в одну строку, алгоритм разбиения на строки это и сделает. Последнее может означать, что разбиение произошло после отточия, и ссылки начались с новой строки. В таком случае отточия должны прерваться на фиксированном расстоянии wi от правого края. Более того, строки с рваным правым краем должны отстоять от правого поля по крайней мере на фиксированное расстояние w-2, так что нет никакой возможности перепутать собственно термин с ссылками на страницы. Отдельные боксы должны быть повторены в отточии с шириной бокса W3.
Эти основополагающие правила иллюстрируются на рис. 11, где абзацный отступ равен 27 единицам, Wi = 45, w2 = 9, W3 = 7.2; цифры имеют ширину 9 единиц, а ширина соответствующих колонок суть 405, 315 и 225 единиц. Термин ‘Theory of Computing’ демонстрирует три варианта отточий: они могут находиться на одной строке с окончанием собственно термина и началом ссылок, либо они могут завершать строку до того, как начнутся ссылки, либо начинать строку после собственно термина.
Здесь показано, как все это может быть закодировано при помощи боксов, клея и штрафов: (а) каждый пробел в собственно термине представлен последовательностью
штраф(0, оо, 0)
клей^з, 18, 0) штраф(0, 0, 0) клей(6 — w2, —18, 2),
которая сохраняет рваный правый край и пробелы, которые могут сжиматься при необходимости от 6 единиц до 4. (Ь) Переход от собственно термина к ссылкам на страницы представлен последовательностью (а), за которой следует
бокс(О)
штраф(0, оо, 0)
otto4hh(3w3, 100000, Згиз)
клей^, 0, 0)
штраф(0, 0, 0)
клей(—wi, —18, 0)
бокс(О)
штраф(0, оо, 0) клей(0, 18, 0).
(с) Каждый пробел в ссылках представлен последовательностью
штраф(0, 999, 0)
клей(6, —18, 2)
бокс(0)
штраф(0, оо, 0) клей(0, 18, 0),
которая поддерживает рваный левый край и пробелы от 6 до 4 единиц.
Части (а) и (с) этих конструкций аналогичны тем вещам, которые мы видели ранее; штраф 999 в (с) стремится минимизировать общее количество строк, занимаемых ссылками. Самый интересный аспект конструкции — это последовательность перехода (Ь), где представлены три или четыре варианта: если разбиения строки не происходит в (Ь), то окончательный результат такой:
(собственно термин) клей(6, 0, 2) (отточие) (ссылка), что позволяет отточию появиться между собственно термином и ссылками на страницы в текущей строке. Если разбиение происходит до отточия, окончательным результатом будет
(собственно термин) клей^г, 18, 0)
(отточие) (ссылка),
так что мы получим разбиение, очень похожее на то, которое возникает после пробела в собственно термине, и отточие начнется со следующей строки. Если разбиение строки происходит после отточия, окончательный результат такой:
(собственно термин) клей(6, 0, 2) (отточие) клей(-Ш1, 0, 0) клей(0, 18, 0) (ссылка),
так что мы имеем разбиение, очень похожее на то, которое появляется после пробела в ссылках, но без штрафа 999; отточие отстоит от правого края на wi единиц. Наконец, если разбиения возникают и до и после отточий в (Ь), мы
имеем ситуацию, в которой всегда больше дефектности, чем в альтернативной, когда разбиение происходит только после отточия.
Когда выбор точек разбиения оставляет в отточиях пространство по крайней мере в 3w3 единиц, мы с уверенностью будем иметь по крайней мере 2 точки, но можем не получить 3 точки, поскольку отточия на разных строках выровнены по точкам. Клей в других пробелах на строке с отточием будет сжиматься, если до отточия останется расстояние менее чем Згиз, и будет тенденция к тому, чтобы отточие не исчезло вовсе; однако в худшем случае пространство для отточия сожмется до нуля, так что не будет видно ни одной точки. Нужно иметь возможность быть уверенным, что все отточия содержат по крайней мере 2 точки, просто установив компоненту сжатия отточия в (Ь) равной нулю. Это могло бы улучшить вид результата, но, к сожалению, это только удлинит именной указатель примерно на 15 процентов и такие затраты, вероятно, будут неоправданными.
Предварительный вариант этой конструкции использовался с пакетом Т^Х для подготовки указателей в Mathematical Reviews, начиная с ноября 1979 г. Однако элементы ‘бокс(О) штраф(0, оо, 0)’ были опущены в (Ь) для совместимости с более ранними указателями, которые готовились в другом программном продукте; это значит, что отточия исчезали полностью везде, где происходило разбиение непосредственно перед ними, в результате в указателях оказалось неоправданно много пустых коридоров, что портило внешний вид.
Алгебраический подход
Из примеров, которые мы только что рассмотрели, видно, что боксы, клей и штрафы представляют собой чрезвычайно гибкие элементы, позволяющие пользователю получать большое разнообразие эффектов, не прибегая к расширению основных операций, применяющихся при обычном наборе. Отдельные конструкции, однако, могут показаться чем-то магическим: они работают, но непонятно даже, как они были задуманы. Мы теперь приступим к систематическому изучению действия этих элементов, чтобы в полной мере оценить их потенциальные возможности. Это краткое обсуждение не зависит от остального материала статьи и может быть опущено.
Во-первых, понятно, что
6okc(w) 6okc(w') = 6okc(w + w') ,
если мы игнорируем содержимое боксов и рассматриваем только ширины; в критерии разбиения на строки входят только ширины. Эта формула означает, что любые два последовательных бокса могут быть заменены одним, независимо от выбора точек разбиения, так как разбиения не возникают в боксах. Точно так же легко убедиться, что
клей(ги, у, г) клей(г(/, у', z') — клей(ги + w', у + у', z + z'} ,
так как разбиения в элементе клей(ц/, р', г') не будет и поскольку разбиение в клей(ги, р, г) эквивалентно разбиению в клей(ги + w', у + у', г + г').
При определенных обстоятельствах мы можем также скомбинировать два смежных штрафа в один; например, если —оо < р, р' < +оо, мы имеем
штрафу, р, /) штрафу, р', /) = штрафу, min(p, р'), /)
относительно любого оптимального выбора точек разбиения, так как с меньшими штрафами связаны меньшие дефекты. Однако общую последовательность ‘штрафу, р, /) штраф(г(/, р', /')’ мы не всегда можем заменить на один штраф.
Нельзя предположить без потери общности, что за всеми боксами немедленно следуют пары элементов вида ‘штраф(0, оо, 0) клей(ги, р, г)’. Потому что если за боксом следует другой бокс, мы их можем скомбинировать; если за ним следует штраф с р < оо, мы можем вставить ‘штраф(0, оо, 0) клей(0, 0, 0)’; если за ним следует ‘штрафу, оо, /)’, мы можем предположить, что w — / = 0 и следующим элементом является клей; а если за этим боксом следует клей, то мы можем вставить ‘штраф(0, оо, 0) клей(0, 0, 0) штраф(0, 0, 0)’. Более того, мы можем удалить любой штраф, имеющий р = оо, если ему непосредственно не предшествует бокс.
Таким образом, любая последовательность из элементов бокс/клей/штраф может быть преобразована в обычный вид, где за каждым боксом следует штраф оо, за каждым штрафом — клей, за каждым элементом клея следует либо бокс, либо штраф < оо. Предположим, что имеется только один штраф — оо, и что это завершающий элемент, поскольку вынуждаемое им разбиение строки эффективно разделяет более длинную последовательность на независимые части. Из этого заключаем, что представленные в нормальном виде последовательности могут быть переписаны как
Х^Хз . . . Хп штрафу, —оо, /),
где каждое Хг представляет собой последовательность элементов вида
6okc(w) штраф(0, оо, 0) клей(ги/, у, г)
или вида
штраф(г, р, /) клей(ги, у, г) .
Для первой из этих двух форм используем обозначение 6iuk(w +w', у, г), означающее, что это функция от w + w', а не отдельно от w и w', и запишем X во второй форме в виде шк(г, р, /, w, у, z\ Мы можем предположить, что последовательность величин X не содержит двух элементов бшк в одной строке, так как
бшк(ги, у, z) 6hik(W, р', г') = 6uik(w + w', у + у', z + г') .
Близкое знакомство с алгеброй боксов, клея и штрафов чрезвычайно облегчает введение конструкций для специальных приложений, подобных описанным выше, как только такие конструкции возможны. Рассмотрим, например, обобщение задач, возникающих для текста с рваным правым краем, с рваным левым краем и центрированным: мы хотим так определить необязательное разбиение строки между словами, что если разбиения не происходит, то мы будем иметь последовательность
(конец текста^ клей^, pi, Zi) (начало текста^)
на одной строке, а если происходит, то
(конец текста^ клей(-ш2, Р2, ^2) штраф (wo, р, /) клей^з, рз, гз) (начало текста2)
на двух строках. Рассмотрение нормальных форм показывает, что наиболее общий способ сделать это состоит во вставке последовательности
6uik(w, р, г) niK(wo, р, /, w', р', г') 6niK(w", р", z”^
между текст1 и текст2, где никакой дополнительный текст нс связан с двумя вставляемыми элементами бшк. Таким образом, вся работа сводится к определению подходящих значений w, р, г, w', р', z', wM, у" и z” \ их можно получить немедленно, решив уравнения
W + It/ + Ui" = W1 ,у + у' + у" = Р1 ,z + z' + z" = Zy ,
W = W2 ,у = p2 ,Z2 .
™" = W3 ,y" = Уз ,z" = Z3 ■
Как только конструкция найдена таким образом, ее можно упростить, отменив процесс, который мы использовали для вывода нормальных форм, и применяя другие свойства алгебры бокс/клей/штраф. Например, мы всегда можем удалить штраф со в последовательности вида
штраф(0, оо, 0) клей(0, р, z) штраф(0, р, 0) ,
если у > 0, z > 0 и р < 0, поскольку разбиение па клее всегда хуже, чем разбиение на штрафе р.
Введение в алгоритм
Вероятно, лучше понять общую идею алгоритма оптимальной верстки поможет рассмотрение примера. На рис. 12 повторен абзац рис. 4(c) и включены крохотные вертикальные пометки для указания «точек допустимого разбиения», найденных алгоритмом. Точка допустимого разбиения имеет место, если текст абзаца от начала до этой точки может быть разбит на две строки, коэффициент корректировки которых не превышает данный допуск; в случае,
1 In olden times when wishing still helped one, there lived a1 .774 king1 whose daughters were all beautiful; and the youngest was' .179 so1 beaujti/ul that the sun itself, which has seen so much, was1 .629 astonished whenever it shone in her face. Close by the king’s1 .545 castle1 lay1 a great dark forest, and un/ler an old lime-tree in1 the1 .000 foljest1 was1 a1 well, and when the day was very warm, the1 king’s1 .079 child1 went1 out1 into the forest and sat down by the side1 of the1 .282 cool1 founjtain;1 and1 when she was bored she took a golden1 ball,1 .294 and1 threw1 it1 up1 on1 high and caught it; and this ball was1 her* .575 favorite1 plaything. 1 .004
Рис. 12. Тонкие вертикальные пометки показывают «точки допустимого разбиения», где строки могут заканчиваться, не вынуждая никакие предыдущие пробелы растягиваться больше, чем их заданное растяжение.
представленном на рис. 12, этот допуск был взят равным единице. Так, например, в строке 7 после слова ‘went’ имеется пометка, потому что способ установить абзац до этой точки со словом ‘went’ в конце 6-й строки и без строк от 1-й до 6-й имеет плохость, превышающую 100 (см. рис. 4(a)).
Алгоритм возобновляет свою работу, локализуя все точки допустимого разбиения и запоминая лучший способ получения каждой в смысле наименьшего общего дефекта. Это осуществляется посредством поддержания списка активних точек разбиения, состоящий из всех точек, которые могут быть кандидатами для будущих разбиений. Когда встречается потенциальная точка разбиения Ь, алгоритм проверяет, существуют ли активная точка а, такая, что строка от а до b имеет некий подходящий коэффициент корректировки. Если существует, то b является вероятной точкой разбиения и она заносится в список активных точек. Алгоритм также запоминает опознавательный код точки разбиения а, минимизирующей общий дефект, когда последний вычисляется от начала абзаца до точки b через точку а. Когда встречается активная точка а, для которой строка от а до b имеет коэффициент корректировки, меньший чем — 1 (т. е. когда строка не может быть сжата до необходимой длины), то точка разбиения а удаляется из списка активных точек. Поскольку размер активного списка по существу ограничен максимальным количеством слов в строке, время работы алгоритма ограничено этим же количеством (обычно малым), умноженным на число потенциальных точек разбиения.
Например, когда алгоритм начинает работать с абзацем на рис. 12, имеется только одна активная точка разбиения, представленная началом первой строки. Довольно неудачный вариант, когда строка начинается отсюда и заканчивается на ‘In’ или ‘olden’, ..., или ‘lived’, потому что клей между словами не аккумулируется в достаточных количествах для растяжения в таких коротких отрезках текста; но удачная точка разбиения есть и она находится после следующего слова ‘а’. Теперь имеется две активные точки разбиения: одна исходная и одна новая. После следующего слова ‘king’ образуются уже три активные точки; но после слова ‘whose’ алгоритм видит, что невозможно втиснуть весь текст от начала до слова ‘whose’ в одну строку, так что начальная точка перестает быть активной и остаются только две активные точки.
Забегая вперед, посмотрим, что происходит, когда алгоритм рассматривает потенциальную точку разбиения после слова ‘fountain;’. На этом шаге имеется восемь активных точек, следующие соответственно за текстовыми боксами для ‘child’, ‘went’, ‘out’, ‘side’, ‘of’, ‘the’, ‘cool’ и ‘foun-’. Строка, начинающаяся после ‘child’ и заканчивающаяся ‘fountain;’, будет, по-видимому, слишком длинной, так что ‘child’ перестает быть активным. Допустимые строки находятся между ‘went’ или ‘out’ и ‘fountain;’ а дефектность этих строк равна соответственно 400 и 144; строка, начинающаяся ‘went’, фактически более предпочтительна, потому что в сущности общая дефектность строк от начала абзаца до ‘went’ меньше, чем от начала до ‘out’. Таким образом, ‘fountain;’ становится новой активной точкой разбиения. Алгоритм сохраняет указатель в обратном направлении от ‘fountain;’ к ‘went’, что означает, что лучший способ получить разбиение после ‘fountain;’ состоит в том, чтобы начать с лучшего способа получить разбиение после ‘went’.
Вычисления алгоритма можно представить графически в виде схемы на рис. 13, на которой изображены все допустимые точки разбиения вместе с числом дефектности, соответствующим каждой допустимой строке между ними. Цель алгоритма состоит в вычислении наикратчайшего пути от вершины до основания рис. 13; при этом числа дефектности используются в качестве «расстояний», соответствующих отдельным отрезкам пути. В этом смысле работа по оптимизации точек разбиения строк сводится в сущности к особому случаю задачи нахождения наикратчайших путей в ациклической сети; алгоритм разбиения на строки лишь чуть более сложен только потому, что должен строить сеть одновременно с нахождением кратчайшего пути.
Обратите внимание, что алгоритм оптимальной верстки можно легко описать в терминах сети, наподобие рис. 13: это «жадный алгоритм», который просто выбирает кратчайшее продолжение на каждом шаге (хотя использует в качестве критерия плохость-и-штраф вместо дефектности). Алгоритм простой верстки может быть охарактеризован как метод, всегда выбирающий самую левую ветвь с отрицательным коэффициентом корректировки (не приводящим к переносу, в случае которого будет выбрана самая правая ветвь без переносов, если таковая будет допустимой). Из этих рассмотрений мы можем с очевидностью понять, почему алгоритм оптимальной верстки стремится делать значительно лучшую работу.
Иногда нельзя продолжить работу алгоритма от одной допустимой точки разбиения к какой-либо другой. Такая ситуация не возникает на рис. 13, но это могло бы быть ниже слова ‘so’, если бы мы не разрешили перенос в слове ‘astonished’. В таких случаях алгоритмы простой и улучшенной верстки должны пересортировать допустимые строки, тогда как алгоритм оптимальной верстки может обычно найти другой путь через лабиринт.
С другой стороны, некоторые абзацы бывают трудными по сути, и их невозможно разбить на допустимые строки. В таких случаях описанный ранее алгоритм обнаружит, что его активный список сокращается до тех пор, пока активность совсем на потеряется. Как поступить в такой ситуации? По- видимому, можно начать снова с более толерантным взглядом на недопусти-
Рис. 13. Эта диаграмма показывает допустимые точки разбиения и требуемое число дефектности, когда алгоритм проходит от одной точки разбиения к другой. Самый короткий путь от вершины до основания соответствует лучшему способу представления абзаца, если рассматривать дефектности как расстояния.
мость (с большим пороговым значением для коэффициентов корректировки). ТЁК придерживается той точки зрения, что в случае отсутствия конкретного критерия пользователь захочет произвести разбиение вручную. Поэтому активный список насильственным образом защищен от того, чтобы стать пустым: просто декларируется допустимость точки разбиения, если в противном случае активный список может стать пустым. В результате строка переполнится и будет выдано соответствующее сообщение об ошибке, призывающее пользователя провести корректирующие действия.
На рис. 14 показано, что происходит, когда алгоритм допускает совсем жидкие строки; в этом случае строка рассматривается как допустимая, только
если ее коэффициент корректировки превосходит 10 (так что можно иметь междусловный пробел, превышающий 2m). Подобные коэффициенты толерантности могут использоваться людьми, которые не хотят вручную разбивать на строки абзац, вызывающий трудности. На этой иллюстрации тонкие черточки, указывающие точки допустимого разбиения, имеют разную длину: более длинные указывают места, в которые можно попасть лучшими путями; тонкий пунктир используется для точек разбиения, которые вряд ли допустимы. Заметим, что на рис. 14 отмечены все потенциальные точки разбиения, за исключением нескольких в первых двух строках; так что здесь значительно больше допустимых точек, чем на рис. 12, и соответствующая диаграмма будет гораздо пространнее, чем на рис. 13. Существует 806137512 допустимых путей получить абзац при таком широком диапазоне толерантности, тогда как для рис. 12 таких путей всего 50. Количество активных узлов в этом случае, однако, не будет существенно больше, чем для рис. 12, поскольку их число ограничено длиной строки. Поэтому алгоритм будет работать не намного медленнее, несмотря на то, что толерантность увеличилась, а число возможных вариантов абзаца невероятно возросло. Например, после слова ‘fountain;’ теперь имеется 17 активных точек разбиения вместо прежних 8, так что обработка будет всего примерно вдвое длиннее, хотя появилось громадное число дополнительных возможностей.
Если порог допустимых пробелов достаточно велик, алгоритм почти наверняка найдет допустимое решение и сообщит пользователю об отсутствии ошибок, несмотря на то, что неизбежно останутся очень жидкие строки. Те пользователи, которые хотели бы получать сообщения о подобных ошибках, должны установить коэффициент толерантности ниже: это не только информирует о необходимости корректирующих действий, но и повысит эффективность алгоритма.
Один из важнейших моментов в рис. 14, на который следует обратить внимание, состоит в том, что точки разбиения могут становиться допустимыми совершенно разными путями, руководствуясь разным количеством строк до точки разбиения. Например, слово ‘seen’ допускается как в конце строки 3:
‘In olden ... lived/a... young-/est... seen’,
так и в конце строки 4:
‘In olden... helped/one... were/all... beau-/tiful... seen’,
хотя на рис. 12 разбиение после ‘seen’ вообще не допускалось. Разбиения после слова ‘seen’ в конце строки 3 имеет существенно меньшую дефектность, чем в конце строки 4 (1533 770 против 12 516 097 962), так что алгоритм запомнит только предшествующую возможность. Это является приложением «принципа оптимальности» динамического программирования, который отвечает за эффективность нашего алгоритма [6]: точки оптимального разбиения абзаца всегда являются оптимальными для абзаца, который они образуют. Но здесь
In olden times when wishing still helped one,' there' lived1 a1 .774 king1 whose daughters were all beaujtijful;' and1 the1 youngjest1 was1 .179 so1 beaujti/ul that' the* sun' itself,' which' has1 seen1 so1 much,1 was1 .629 astonished' whenever it1 shone' in' her' face.1 Close1 by1 the1 king’s1 .545 castle1 lay1 a great' dark' forest,' and1 unjder' an1 old1 lime-jtree' in1 the1 .000 forest1 was1 a' well,' and' when' the1 day1 was' very1 warm,' the1 king’s1 .079 child1 went1 out1 into' the' forest' and1 sat1 down1 by1 the1 side1 of the1 .282 cool1 fountain;1 and1 when1 she1 was1 bored she1 took1 a1 golden1 ball,1 .294 and1 threw1 it1 up1 on1 high1 and1 caught1 it;1 and1 this1 ball1 was1 her1 .575 favorite1 plaything. 1 .004
Рис. 14. Когда коэффициент толерантности достигает десятикратного растяжения, появляется большее количество допустимых точек разбиения и большее количество вариантов абзаца.
интересен тот факт, что экономии памяти не будет, если последующие строки нс окажутся одинаковой длины, поскольку различные длины строк могут просто означать, что лучше было бы поместить ‘seen’ в конце строки 4; например, мы уже упоминали один прием, вынуждающий алгоритм произвести заданное количество строк. Следовательно, в случае переменной длины строк алгоритм будет вынужден иметь два отдельных списка активных точек разбиения после слова ‘seen’. Компьютер нс может просто запомнить один с наименьшими общими дефектностями; это лишило бы законной силы принцип оптимальности динамического программирования.
На рис. 15 представлен пример разбиения на строки, когда все строки абзаца имеют разную длину. В подобных случаях нужно привязать номера строк к точкам разбиения, которые могут послужить причиной того, что количество активных точек будет заведомо больше, чем максимальное количество слов в строке, если установлено высокое значение толерантности. Следовательно, мы хотим иметь низкое значение толерантности. Но если оно слишком низкое, у нас может не оказаться возможности разбить абзац на строки, имеющие заданный вид. К счастью, всегда есть замечательный способ, при котором алгоритм оказывается достаточно гибким, чтобы найти хорошее решение, не требуя при этом много памяти и времени. Данные на рис. 16 демонстрируют, например, что алгоритму придется не так уж много работать, чтобы отыскать оптимальное решение для заметок Галилея об окружностях, когда коэффициент корректировки для каждой допустимой строки назначался равным 2 или меньше; было все еще достаточно гибкости, чтобы сделать допустимое решение возможным.
Хороший метод разбиения на строки особенно важен для набора научнотехнических работ, потому что математические формулы в тексте по возможности не должны переноситься на другую строку. Ряд таких наиболее трудных случаев можно видеть в журнале Mathematical Reviews или на страницах The Art of Computer Programming, содержащих ответы, потому что материал такого рода публикаций зачастую переполнен формулами. На рис. 17 приведен типичный пример, заимствованный из ответов к упражнениям тома Seminumerical Algorithms [28], в котором отмечены точки возможных разбие-
The areaof a circle is a meanproportional between any tworegular and similar polygons ofwhich one circumscribes it and theother is isoperimetric with it. In addition,the area of the circle is less than that of anycircumscribed polygon and greater than thatof any isoperimetric polygon. And further,of these circumscribed polygons, the onethat has the greater number of sides hasa smaller area than the one that has alesser number; but, on the other hand,the isoperimetric polygon thathas the greater number ofsides is the larger.
— Галилео Галилей (1638)
Iturn, in thefollowing treatises, tovarious uses of those triangleswhose generator is unity. But I leave outmany more than I include; it is extraordinary howfertile in properties this triangle is. Everyone can try his hand.
— Блез Паскаль (1654)
Рис. 15. Пример разбиения на строки разной длины.
Рис. 16.Детали допустимых точек разбиения в первом примере на рис. 15, показывающие, как было найдено оптимальное решение.Рис. 16.Детали допустимых точек разбиения в первом примере на рис. 15, показывающие, как было найдено оптимальное решение.The area of а1 .375circle is a mean proper-1 .828tional between any two regular1 .406and similar polygons of which one1 1.098circumscribes it and the other is iso-1 1.268peri*metric with it. In addition, the area1 .574of the1 circle is less than that of any cir-1 i.mcum’scribed1 polygon and greater than that1 .931of any1 iso*peri*metric polygon. And further,1 .584of these* circumscribed polygons, the ond i.seithat1 has1 the1 greater number of sides had .703a1 smaller1 area1 than the one that had 1.437a1 lesser number;1 but, on the other1 1.240hand,1 the1 iso*peri*metric polygon1 rosethat has the1 greater num-1 .974ber* of sides is1 the1 .479larger.1 .000
Рис. 16.
Детали допустимых точек разбиения в первом примере на рис. 15, показывающие, как было найдено оптимальное решение.
Рис. 16.
Детали допустимых точек разбиения в первом примере на рис. 15, показывающие, как было найдено оптимальное решение.
'15. (This procedure maintains four integers (А, В, С, В) with the invariant meaning1 -.409 that “our remaining job is to output the continued fraction for (Ay 4- В)/{Су ^B),1 - .057 where у is the input yet to come.”) Initially set j к «— О, (А, В, С, В) <—1 (а, b, с, й\1 -.788 then input xj and set (А, В, С, B) <— {Axj 4- B, A, Cxj 4- D,CY j <—1 j 4-11,1 one1 or1 .207 more1 times until С-V D has the same sign as C. (When j > I1 and1 the1 input1 has1 not1 -.282 terminated, we know that 1 < у < 00; and when C 4- B1 has1 the1 same1 sign1 as C1 we1 .124 know therefore that {Ay 4- В)/{Су 4- В) lies between1 {A +] B)/{C -V D/ and1 A/C./ .192 Now1 comes the general step: If no integer lies strictly1 between1 {A 4-1 B}/{C 4-1 B)1 .582 and1 A/C' output Xk *- |A/CJ, and set (A,B,C,B) *-' (C, D, A -'XkC, В ^XkD)i -.098 к <—! /с 4-11;1 otherwise input Xj and set {A, B,C, D) <—1 {Axj +l B, A,Cxj -^D^C)! .479 j <—1 j 4-11.1 The1 general step is repeated ad infinitum.1 However,1 if1 at1 any* time1 the1 .266 final x/ is1 input,1 the1 algorithm immediately switches1 gears: It1 outputs1 the1 continued1 -.325 fraction1 for1 {Axj -V B)/{Cxj 4- B), using Euclid’s1 algorithm,1 and1 terminates.1 .000
Рис. 17. Пример найденных алгоритмом допустимых точек разбиения в абзаце, перенасыщенном математическими формулами.
ний, когда коэффициенты корректировки не должны превышать 1. Несмотря на то что некоторые допустимые точки разбиения появляются внутри формул, с ними связаны штрафы, которые делают их сравнительно нежелательными, так что алгоритм способен сохранить все формулы в данном абзаце невредимыми.
Дальнейшие подробности
Сформулированная выше задача оптимизации состоит в нахождении таких точек разбиения, при которых суммарная дефектность была бы минимальной, где дефектность отдельной строки зависит от ее плохости (т. е. от того, насколько растягивается или сжимается клей) и от возможного штрафа, связанного с окончательной точкой разбиения; когда две соседние строки заканчиваются переносом (т. е. штрафом с / = 1), добавляется также дополнительная дефектность. Двухлетний опыт работы с подобной моделью этой задачи дал блестящие результаты, а несколько приведенных выше абзацев удалось впоследствии улучшить.
. Первые две строки в двух верхних абзацах на рис. 4 иллюстрируют потенциальный источник видимого беспорядка, который не рассматривался в обсуждавшейся выше модели. Эти абзацы начинаются с плотной строки (имеющей г = —.727), за которой сразу следует жидкая строка (имеющая г = 4-.821). Хотя эти две строки сами по себе не так ужасны, контраст между плотной и жидкой строкой дает довольно неприятный эффект. Таким образом, новый ТЁХ’овский алгоритм разбиения на строки распознает четыре вида строк:
Класс 0 (плотные строки), где — 1 < г < —.5;
Класс 1 (обычные строки), где —.5 < г < .5;
Класс 2 (жидкие строки), где .5 < г < 1;
Class 3 (очень жидкие строки), где г > 1.
Дополнительная дефектность добавляется, когда соседние строки не того же :амого или соседнего класса, т. е. когда до или после строки класса 0 находится
строка класса 2 или класса 3, либо до или после строки класса 1 находится строка класса 3.
Это на первый взгляд простое расширение на самом деле вынуждает алгоритм работать интенсивнее, потому что возможная точка разбиения теперь может входить в список активных точек до четырех раз, дабы удовлетворить принципу оптимальности динамического программирования. Например, если строка, окончившаяся в некоторой точке, может быть как строкой класса О, так и строкой класса 2, мы должны запомнить обе эти возможности, даже учитывая, что класс 0 имеет большую дефектность, потому что мы можем захотеть поместить после этой точки разбиения плотную строку. С другой стороны, нам не нужно запоминать возможность разбиения в классе 0, если его общая дефектность превышает дефектность при разбиении на строке класса 2 плюс дефектность при контрастных строках, поскольку разбиение на строке класса 0 в таком случае никогда не приведет к оптимуму.
Чтобы понять, действительно ли дополнительные вычисления, требуемые таким расширением алгоритма, приведут к ощутимому улучшению, нужно провести еще ряд экспериментов. Пользователю удобно знать, что алгоритм разбиения на строки принимает во внимание такие улучшения, но у него нет основания проводить дополнительную работу, если результат вряд ли когда- либо улучшится.
Другое расширение алгоритма нужно для того, чтобы поднять его до высот профессионального ручного набора. Иногда мы хотим, чтобы абзац стал на одну строку длиннее или короче его оптимального размера, потому что нужно избежать одинокой «висячей строки» вверху или внизу страницы, или потому что нам нужно получить четное количество строк для последующей разбивки на две колонки. Хотя вид самого абзаца не оптимален, страница целиком будет смотреться лучше и абзац будет сверстан настолько хорошо, насколько это возможно при данных ограничениях. Например, два абзаца из сказки на рис. 6 получились на строку короче своей оптимальной длины, зато все шесть колонок оказались одинаковой длины.
Алгоритм разбиения на строки, который мы далее будем обсуждать, имеет параметр «разреженности», что проиллюстрировано на рис. 18. «Разреженность» представляет собой целое число q. такое, что общее количество строк, полученное для этого абзаца, близко, насколько возможно, к оптимальному количеству плюс число q\ при этом условия допустимости не нарушаются. На рис. 18 показано, что происходит с примером абзаца на рис. 14, когда \q\ < 1. Значения q < — 1 могли бы быть теми же самыми, что и q = — 1, поскольку этот абзац не может быть сжат больше; значения q > 1 возможны, но редко бывают нужны, потому что они требуют чрезвычайно широких пробелов. Пользователь может получить оптимальное решение, имея наименьшее возможное число строк, положив q равным большому отрицательному значению, например —100. Когда q ^ 0, допустимые точки соответствуют различным номерам строк, но их все надо запомнить, даже если каждая строка имеет одну и ту же длину.
In olden times when wishing still helped one, there lived a king -.727 whose daughters were all ЬеацйДй; and the youngest was so .821 ЬеацЦГп! that the sun itself, which has seen so much, was astonp -.455 ished whenever it shone in her face. Close by the king’s castle lay -.870 a great dark forest, and urijder an old lime-tree in the forest was -.208 a well, and when the day was very warm, the king’s child went .000 out into the forest and sat down by the side of the cool fountain; -.577 and when she was bored she took a golden ball, and threw it up -.231 on high and caught it; and this ball was her favorite plaything. -.883
In olden times when wishing still helped one, there lived a .774 king whose daughters were all beautiful; and the youngest was .179 so beaujti/ul that the sun itself, which has seen so much, was .629 astonished whenever it shone in her face. Close by the king’s .545 castle lay a great dark forest, and u^der an old lime-tree in the .000 forest was a well, and when the day was very warm, the king’s .079 child went out into the forest and sat down by the side of the .282 cool fountain; and when she was bored she took a golden ball, .294 and threw it up on high and caught it; and this ball was her .575 favorite plaything. .557
In olden times when wishing still helped one, there lived 1.393 a king whose daughters were all beautiful; and the youn^- 1.464 est was so beau^i/ul that the sun itself, which has seen so 1.412 much, was astonished whenever it shone in her face. Close 1.226 by the king’s castle lay a great dark forest, and urijder an 1.412 old lime-tree in the forest was a well, and when the day 1.735 was very warm, the king’s child went out into the forest 1.774 and sat down by the side of the cool fountain; and when 1.559 she was bored she took a golden ball, and threw it up on 1.37s high and caught it; and this ball was her favorite playf 2.129 thing. ' .862
Рис. 18. Абзацы, полученные при параметре «разреженности» —1, 0 и +1. Такие значения иногда бывают необходимы, чтобы сбалансировать вид всей страницы, но, разумеется, она будет выглядеть некрасиво, если впадать в крайности.
Если строки абзаца довольно жидкие, мы не захотим, чтобы последняя строка значительно отличалась от них, так что мы должны вернуться к нашему первоначальному предположению о том, что «завершающий клей» абзаца имеет почти бесконечное растяжение. Штраф для соседних строк из контрастирующих классов кажется работает лучше в случае жидкой строки, если завершающий клей в конце абзаца установлен так, чтобы получился нормальный пробел, равный примерно трети общей длины строки, растягивающийся до полной ее длины и сжимающийся до нуля.
Алгоритм
Перейдем теперь к сути дела и обсудим подробно алгоритм оптимального разбиения на строки. Пусть имеется абзац х\...хт1 описываемый элементами xt = (ti,Wi^yi, Zi,pi, fi) как объяснялось выше, где Xi представляет собой бокс, а хт — штраф, устанавливаемый за вынужденное разбиение (рт = — оо). Пусть
также имеется потенциально бесконечная последовательность положительных длин строк Zi, ^ • • • • К дефектности добавляется параметр а, когда возникают две последовательные точки разбиения при /$ = 1, а параметр 7 —когда две последовательные строки принадлежат несовместимым классам соответствия. Порог толерантности р представляет собой верхнюю грань коэффициентов корректировки. Кроме того задан параметр «разреженности» q.
Допустимая последовательность точек разбиения (bi,..., b^ является допустимым выбором точек разбиения, таких, что каждая из к результирующих строк имеет коэффициент корректировки 77 < р. Если q = 0, то работа алгоритма сводится к нахождению допустимой последовательности точек, имеющих наименьшую общую дефектность. Если q / 0, то работа алгоритма несколько усложняется и не поддается точному описанию. Попробуем сформулировать это так: пусть к — число строк, которые алгоритм производит при q = 0. Тогда алгоритм находит допустимую последовательность из к + q точек разбиения, имеющих наименьшую общую дефектность. Если это, однако, невозможно, то значение q увеличивается на 1 (если q < 0) или уменьшается на 1 (если q > 0) до тех пор, пока не будет найдено допустимое решение. Иногда допустимое решение найти невозможно даже при q = 0; мы обсудим эту ситуацию позже, когда выясним, как ведет себя алгоритм в обычном случае.
Мы увидим, что иногда полезно разрешить боксам, клею и штрафам иметь отрицательные ширины и даже отрицательное растяжение, но абсолютно ничем не ограниченное использование отрицательных значений приводит к неприятным осложнениям. По соображениям эффективности целесообразно ввести два ограничения на обрабатываемые абзацы:
Ограничение 1. Пусть Мь — длина самой короткой строки от начала абзаца до точки разбиения Ь, а именно сумма всех Wj — Zj, взятая по всем боксам и элементам х^ клея при 1 < г < Ь, плюс w^ если элемент ось представляет собой штраф. Абзац должен иметь Ма < Мь^ если только а и b суть допустимые точки разбиения, причем а < Ь.
Ограничение 2. Пусть а и Ь — допустимые точки разбиения, а < Ь, и предположим, что нет такого х^ в диапазоне а < г < Ь, который был бы элементом-боксом или вынужденным разбиением (штраф р^ = — оо). Тогда либо b = т, либо жь+i является боксом или штрафом p^+i < 00 •
Оба эти ограничения вполне разумные, поскольку они встречаются во всех известных практических приложениях. Ограничение 2 на первый взгляд может показаться странным, но мы скоро поймем, чем оно полезно.
Наш алгоритм в общих чертах работает следующим образом:
(Создание активного узла, представляющего собой начальную точку);
for b := 1 to т do (if b — допустимая точка разбиения) then begin (Установка допустимых точек разбиения b равными нулю); (for каждый активный узел a) do
begin (Вычисление коэффициента корректировки г от а до 6);
if г < — 1 or (b является вынужденным разбиением) then (Деактивация узла а);
if — 1 < г < р then (Запись допустимого разбиения от а до 6); end;
(if имеется допустимое разбиение в 6) then
(Присоединение лучших таких разбиений в качестве активных узлов);
end;
(Выбор активного узла с наименьшей общей дефектностью);
if q ^ 0 then (Выбор подходящего активного узла);
(Использование выбранного узла для определения оптимального разбиения).
Смысл используемого здесь Алголо-подобного языка должен быть самоочевиден. Активный узел в этом описании ссылается на запись, в которой содержится информация о точке разбиения, ее принадлежности тому или иному классу соответствия и номеру строки, которая на ней оканчивается.
Мы хотим иметь структуру данных, которая делает этот алгоритм эффективным, и такую вполне приемлемую структуру не так трудно построить. Здесь следует руководствоваться двумя основополагающими соображениями: вычисление коэффициента корректировки, переводящего данный активный узел а в данную допустимую точку разбиения, должно быть предельно простым; должен быть простой способ определения того, какие из допустимых разбиений в b должны быть сохранены как активные узлы.
Прежде всего, коэффициент корректировки зависит от общей ширины, общего растяжения и общего сжатия, вычисленных из первого бокса после первой точки разбиения и перед следующей точкой разбиения, и нам не хотелось бы снова и снова вычислять эти суммы. Чтобы этого избежать, можно просто вычислить эту сумму от начала абзаца до текущей точки и затем вычесть две такие суммы, чтобы получить общую, которая находится между ними. Пусть (Sw)b, (Еу^ь и (^г)ь обозначают соответственно суммы всех wt, yt и Хг в элементах бокса и клея ж^ при 1 < г < Ь. Тогда если а и b являются допустимыми точками с а < 6, то ширина Lab строки от а до b и ее растяжение ¥аь и сжатие Zab могут быть вычислены следующим образом:
Lab = (Sw)b - (Sw)n0CJie(a) + (wb если tb = ‘штраф’);
¥ab = (^b (^^после^) j Zab — (^^)b (^^)после(а) •
Здесь «после(а)» есть индекс г > а, такой, что либо г > т^ либо хг есть бокс, либо Xi есть штраф, который вынуждает сделать разбиение (рг = — оо). Эти формулы выполняются даже в вырожденном случае, когда после(а) > 6, в силу ограничения 2; в самом деле, в ограничении 2 особо оговорено, что соотношение после(а) > b влечет за собой (Sw)b = (Sw)n0CJie(a), ^y")b = (Z.y)nocne^ и (^Х^ь — (^^)после(а) •
Из сказанного выше мы можем заключить, что узел а в структуре данных должен содержать следующие поля:
позиция(а) = индекс точки разбиения, представленной этим узлом; строка(а) = номер строки, оканчивающейся в этой точке разбиения;
класс-соответствия(а) = класс соответствия строки, заканчивающейся в этой точке разбиения;
общая_ширина(а) = (^^)после(а), используемая для вычисления коэффициентов корректировки;
общее_растяжение(а) = (Sy)ПОсле(а)> используемая для вычисления коэффициентов корректировки;
общее_сжатие(а) = (Ш)после(а), используемая для вычисления коэффициентов корректировки;
общая_дефектность(а) = минимальная общая дефектность до этой точки разбиения;
предшествующий (а) = указатель лучшего узла для предыдущей точки, разбиения;
ссылка(а) = указатель следующего узла, в этом списке.
Узлы становятся активными, когда они впервые созданы, и пассивными —когда они деактивированы. Этот алгоритм поддерживает глобальные переменные Л и Р, которые указывают соответственно на первый узел в активном списке и первый узел в пассивном списке. Первый шаг, следовательно, может превратиться в:
(Создание активного узла, представляющего собой начальную точку) = begin Л := new узел (позиция = 0, строка = 0, класс-соответствия = 1 общая_ширина = 0, общее-растяжение = 0, общее-сжатие = 0, общаящефектность = 0, предшествующий = Л, ссылка = Л);
Р := Л; end.
Мы также вводим глобальные переменные ШУ, У У и SZ для представления (Ew)b, (£у)ь и (Ег)ь в основном цикле алгоритма, так что операция ‘for b := 1 to т do (if 6 есть допустимая точка разбиения) then (основной цикл)’ принимает следующий вид:
SW := 'EY := EZ := 0;
for b : = 1 to m do
if tb = ‘бокс’ then ШУ := ШУ + Wb
else if tb = ‘клей’ then
begin if tb-i = ‘бокс’ then (основной цикл);
ШУ := ШУ + wb] EY:=EY + yb] EZ:=EZ + zb] end
else if рь / +oo then (основной цикл).
Непосредственно в основном цикле операция (Вычисление коэффициента корректировки г от а до 6) может теперь быть представлена просто как
L := ШУ — общаялнирина(а);
• if tb = ‘штраф’ then L := L + Wb]
3 := строка(а) + 1;
if L < lj then
begin Y := SV — общее_растяжение(а);
if Y > 0 then r := (lj — L^JY else r := oo; end
else if L > lj then
begin Z := EZ — общее_сжатие(а);
if Z > 0 then т := (lj — L)/Z else r := oo; end
else т := 0.
Другая неочевидная проблема, с которой нам придется, возможно, иметь дело, проистекает из того факта, что несколько узлов могут соответствовать одной и той же точке разбиения. Мы никогда не будем создавать два узла, имеющих одни и те же значения полей (позиция, строка, классюоответствия), поскольку весь смысл подхода динамического программирования заключается в том, что нужно помнить только лучший из возможных путей получения каждой допустимой позиции разбиения, имеющей данный номер строки и данный класс соответствия. Но далеко нс сразу очевидно, как отслеживать лучшие пути, приводящие к данной позиции, когда такая позиция может возникнуть при разных номерах строк; мы можем, к примеру, поддерживать таблицу хеширования с полями (строка, класс соответствия) в качестве ключа, но это приведет к ненужным осложнениям. Решение заключается в поддержании активного списка, отсортированного по номерам строк. Просмотрев все активные узлы для строки j, мы можем вставить новые активные узлы для строки j + 1 в этот список непосредственно перед любыми активными узлами для строк > j + 1, которые мы собираемся рассматривать в следующую очередь.
Дополнительная трудность состоит в том, что мы не хотим создавать активные узлы для разных номеров строк, когда все длины строк равны, за исключением q / 0, поскольку это будет нежелательным образом замедлять работу алгоритма; сложности общего случая не должны загромождать простые ситуации, которые чаще всего и возникают. Таким образом, мы предполагаем, что известен индекс jo, такой, что все разбиения строк с номерами > jo можно рассматривать как эквивалентные. Индекс jo определяется следующим образом: если q / 0, то jo = оо; в противном случае jo настолько мал, насколько возможно, причем lj = Zj+i при всех j > jo. Например, если q = 0 и li = 12 = 1з ^ U = U = • • •) положим jo = 3, поскольку нет необходимости отличать точку разбиения, которой оканчивается строка 3, от точки разбиения в конце строки 4 в той же самой позиция, до тех пор, пока затрагиваются последовательные строки.
Для каждой позиции b и строки с номером j удобно запоминать лучшую допустимую точку разбиения, имеющую класс соответствия 0, 1, 2, 3, поддерживаемый для четырех значений Do, Di, D%, Da, где Dc есть наименьшая известная общая дефектность, которая приводит к разбиению в позиции b и строке j и классе с. Другая переменная D = min (Do, Di, D^, D3) также должна быть удобной, и мы полагаем Ас указывающим на активный узел а, который приводит к наилучшему значению Dc. В результате основной цикл принимает
следующий несколько измененный вид для каждой допустимой точки разбиения Ь:
begin а := Л; preva := Л;
loop: Do := Dy := D2 := D3 := D := +00;
loop: nexta := ссылка(а);
(Вычисление j и коэффициента корректировки г от а до Ь);
if г < — 1 or ръ = —оо then (Деактивация узла а)
else рте-иа := а;
if — 1 < г < р then
begin (Вычисление d и класса соответствия с);
if d < Dc then
begin Dc := d\ Ac := a; if d < D then D := d\ end;
end;
a := nexta; if a = Л then exit loop;
if строка(а) > j and j < jo then exit loop;
repeat;
if D < 00 then
(Вставка новых активных узлов для разбиений от Ас до 6);
if а = Л then exit loop;
repeat;
if A = Л then
(Некие неординарные действия, поскольку нет допустимого решения); end.
Для некоторой данной позиции b внутренний цикл алгоритма рассматривает все узлы а как имеющие эквивалентные номера строк, в то время как внешний цикл обрабатывает все номера строк, которые не являются эквивалентными.
Не составляет труда точно раскодировать коды операций, которые имели в этих циклах только аббревиатуры:
(Вычисление дефектности d и класса соответствия с) =
begin if ръ > 0 then d := (1 + 100|r|3 + ръУ else if ръ / —00 then d := (1 + 100|r|3)2 — p^ else d := (1 + 100|r|3)2;
d i= d + Q * fb * /позиция(а);
if r < —.5 then c := 0
else if r < .5 then c := 1
else if r < 1 then c := 2 else c := 3;
if |c — класс_соответствия(а)| > 1 then d := d + 7;
d := d + общая_дефектность(а);
end.
(Вставка новых активных узлов для разбиений от Ас до 6) = begin (Вычисление tw = (2ш)После(Ь), ty = (2т/)после(ь), tz — (^-^)после(Ь)))
for с := 0 to 3 do if Dc < D + 7 then
begin s := new узел (позиция = 6, строка = строка( Лс) + 1, класс-соответствия = с, общаялиирина = tw, общее-растяжение = ty, общее_сжатие = tz, общая_дефектность = Dc, предшествующий = Лс, ссылка = а);
if рте-иа = Л then А := s else ссылка(рге^а) := s;
рте-иа := s;
end;
end.
(Вычисление tw = (^^)после(Ь)? ty = (^^после^) > ^Z = (^^после^)) = begin tw := SW; ty := SK; tz := SZ; г := 6;
loop: if г > m then exit loop;
if ti = ‘бокс’ then exit loop;
if ti = ‘клей’ then
begin tw := tw + w^ ty := ty + уг, tz := tz + ^;
end
else if pi = —00 and г > b then exit loop;
г := г + 1;
repeat;
end.
(Дезактивация узла а) =
begin if рте-иа = Л then A := nexta else ссылка(рге^а) := nexta; ссылка(а) := P; P := a;
end.
После того как основной цикл выполнит свою работу, активный список будет содержать только узлы с позицией = т, поскольку хт представляет собой вынужденное разбиение. Таким образом, мы можем написать
(Выбор активного узла с наименьшей общей дефектностью) = begin а := b := Л; d := общая_дефектность(а);
loop: a := ссылка(а);
if а = Л then exit loop;
if общая дефектность (a) < d then begin d := общая_дефектность(а); b := a; end;
repeat;
к := строка(б);
end.
Теперь узел b выбран, к есть номер его строки. Последующая обработка для q ^ 0 столь же элементарна:
(Выбор соответствующего активного узла) =
begin а := Л; s := 0;
loop: 5 := строка(а) — к.
if q < 5 < s or s < 5 < q then
begin s := 5; d := общая_дефектность(а); b := a;
end
else if 5 = s and общая_дефектность(п) < d then begin d := общая_дефектность(а); b := a;
end;
a := ссылка(п); if a = Л then exit loop;
repeat;
к := строка(б);
end.
Теперь искомая последовательность из к точек разбиения доступна из узла Ь:
(Использование выбранного узла для определения оптимальных точек разбиения) =
for j := к down to 1 do
begin bj := позиция(6); b := предшествующий(6); end.
(Иной путь завершить обработку, заключающийся в том, чтобы строки рассматривать в прямом порядке от 1 до к, а не от к до 1, рассматривается ниже в приложении.) Если не производится сбора мусора, то алгоритм завершается перераспределением всех узлов в списках А и Р.
Ограничение 1 делает законной деактивацию узла, когда обнаруживается, что г < —1, поскольку г < — 1 эквивалентно Ц < Ьаь — Zab, так что последующие точки разбиения br > b будут иметь Ъаь« — Zav > Ьаь — Zab. Таким образом, нетрудно убедиться, что алгоритм действительно находит оптимальное решение: задав некую последовательность допустимых точек разбиения by < • • • < bk^ можно доказать индукцией по J, что алгоритм конструирует узел для допустимой точки разбиения в bj с подходящими номерами строк и классами соответствия, такой, что его дефектность не превышает дефектности данной последовательности.
В алгоритме остается только один свободный конец, а именно операция (Некие неординарные действия, поскольку нет допустимого решения). Как говорилось выше, Т^Х предполагает, что у пользователя есть возможность выбрать толерантный порог р таким образом, чтобы требовалось вмешательство человека, когда этот порог не может быть достигнут. Другой вариант состоит в том, чтобы иметь два пороговых значения и пытаться сначала достичь р0, который ниже, чем р, так что алгоритм будет генерировать относительно немного активных узлов; если нет пути достичь толерантности ро, алгоритм может просто вернуть все узлы в свободную память и пытаться снова работать с настоящим пороговым значением р. Этот двухпороговый метод не будет всегда находить строго оптимальное допустимое решение, поскольку в необычных обстоятельствах возможно, что в оптимальное решение будет включена строка, чей коэффициент корректировки превосходит р^, тогда как существует неоптимальное допустимое решение с толерантностью ро^ для практических целей, однако, это различие несущественно.
На самом деле Т^Х использует разного рода двухпороговые методы. Поскольку задача разбиения слов нетривиальна, Т^Х сначала пытается разбить абзац на строки без каких бы то ни было жестких переносов, за исключением тех, которые уже имелись в данном тексте, используя порог толерантности Pi. Если алгоритм терпит неудачу в процессе поиска решения или допустимое решение с q ^ 0 существует, но требуемая свобода не может быть достигнута (5 ^ q^ то все узлы возвращаются в свободную память и Т^Х начинает сначала, уже используя другую толерантность р^. Во время этого второго прохода все слова из пяти букв или более подвергаются воздействию ТЁХ’овского алгоритма переносов, прежде чем они будут обработаны алгоритмом разбиения на строки. Таким образом, пользователь берет pi, чтобы ограничить толерантность для абзацев, которые могут быть полностью разбиты без переносов, а Р2~для ограничения толерантности, когда переносы должны быть опробованы; возможно, pi будет несколько больше, чем р^ но может оказаться и меньше, если перенос не выглядит слишком нелепо. Как правило, р^ и р^. практически равны друг другу, или же /?1 почти равно 1, а р^ несколько больше; и напротив, можно взять р2 = О, чтобы эффективно запретить перенос.
Если оба прохода безуспешны, Т^Х продолжает работу, снова активируя узел, который был дезактивирован самым последним, и трактует его как допустимое разбиение строки, приводящее к 6. С этой ситуацией на самом деле имеет дело подпрограмма (Дезактивация узла а), сразу после того как последний активный узел становится пассивным:
if А = Л and secondpass and D = oo and r < — 1 then r := — 1.
В итоге получаем сообщение о переполнении «overfull box», свидетельствующее о выталкивании текста в правое поле, невзирая на то, что нет последовательности допустимых точек разбиения. Как обсуждалось выше, неизбежны некоторого рода ошибки индикации, поскольку пользователь полагает иметь р равным такому значению, что дальнейшее растяжение нетолерантно и требуется вмешательство человека. В силу особенностей алгоритма, легче получить переполненные строки, чем незаполненные. И это весьма удачно: установка переполненного бокса будет настолько плотной, насколько это возможно, и, следовательно, пользователь сразу увидит, какие корректировки надо внести: например, сделать вынужденное разбиение или поставить перенос в слове.
Вычислительный эксперимент
Алгоритм, описанный в предшествующем разделе, довольно сложен, поскольку он настроен па применение в широком диапазоне ситуаций, возникающих в наборном процессе. Значительно более простая процедура годится для специальных случаев, возникающих при обработке текстов и газетной верстке; в приложении к данной статье приведено подробное описание такого вырожденного варианта. И напротив, алгоритм в Т£Х’е даже еще более сложный, чем только что описанный, потому что Те% должен иметь дело с колонтитулами,
подстрочными примечаниями, пометками о разбиении, привязанными к строкам, с пробелами как внутри математических формул, так и рядом с ними. Пробелы, окружающие формулы, несколько отличаются от клея, потому что исчезают, когда за формулой идет разбиение строки, но это не представляет собой допустимую точку разбиения. (Полное описание ТеХ’овского алгоритма дано в [29].) Эксперимент показал, что общий алгоритм на практике весьма эффективен, несмотря на все непростые моменты, с которыми он должен справиться.
Представлено настолько много параметров, что вряд ли кому то придется в действительности экспериментировать с большей частью этих возможностей. Пользователь может варьировать междусловными пробелами и штрафами за вставленные переносы, жесткие переносы, смежные флаговые строки и смежные строки с несовместимыми классами соответствия; порог толерантности р также может меняться, не говоря уже о длинах строк и параметре «разреженности» q. Таким образом, вычислительный эксперимент может длиться годами и экспериментатор не получит никакого представления о поведении алгоритма. Даже при фиксированных параметрах возможны значительные отклонения, зависящие от вида печатных материалов: например, насыщенный формулами материал представляет особую проблему. Интересный сравнительный анализ разбиения строк сделал Дункан с соавторами [13], который рассматривал образцы стихотворных текстов из Decline and Fall Гиббона, взятых из Salar the Salmon*, как и ожидалось, словарь Гиббона неизбежно привел к большему количеству строк с переносами.
С другой стороны, мы видели, что алгоритм оптимизации приводит к еще лучшим разбиениям строк в детских рассказах, где слова короткие и простые, как в сказках братьев Гримм. Было бы замечательно интуитивно ощущать качество: сколько нужно дополнительных вычислений для получения этого улучшения качества. Грубо говоря, время вычислений пропорционально количеству слов в абзаце, умноженному на среднее число слов в строке, поскольку основной цикл вычислений проходит через временно активные узлы и поскольку среднее число слов в строке разумно оценивается числом активных узлов во всех строках, кроме нескольких первых строк абзаца (см. рис. 12 и 14). С другой стороны, имеется относительно немного узлов в первых строках абзаца, так что исполнение на самом деле окажется быстрее, чем указывает грубая оценка. Более того, алгоритм для специальных случаев, представленный в приложении, требует для работы почти линейное время независимо от длины строки, поскольку он не должен проходить через все активные узлы.
Подробная статистика была собрана, когда с использованием этой процедуры был набран первый большой Т^’овский продукт Seminumerical Algorithms [28]. В этой 702-страничной книге оказалось 5526 «абзацев» в основном тексте и в ответах на упражнения, если рассматривать выключные формулы как разделители между независимыми абзацами. Эти 5526 абзацев были разбиты на 21057 строк, из которых 550 (около 2.6%) заканчиваются на переносе. Эти строки были обычно длиной 29 pica, что соответству-
ет 626.4 машинным единицам при 10-пунктовом наборе и около 677.19 машинным единицам —при 9-пунктовом, грубо говоря, 12 или 13 слов в строке. Пороговые значения /?1 и рз были обычно оба равны ^2 ~ 1.26, так что промежутки между словами пробегали значения от минимума в 4 единицы до максимума в 6 4- У2 « 9.78 единиц. Штраф за разбиение после переноса был 50; последовательные переносы и смежно несовместимые дефектности были а = 7 = 3000. Второй (по поводу переносов) проход затронул только 279 абзацев, иначе говоря, около 5% времени; допустимое решение без переносов было найдено в оставшихся 5247 случаях. Второй проход представлял собой попытку переносить только слова, набранные пятью или более строчными буквами, содержащими акценты, лигатуры или дефисы, и это привело к 6700 словам, которые были подвергнуты процедуре переносов. Таким образом, среднее число попыток переноса в абзаце было приблизительно 1.2, лишь чуть больше, чем нужно по согласованным неоптимизирующим алгоритмам, и перенос оказался незначимым фактором во времени прогона.
Основной вклад во время работы алгоритма привносит, разумеется, основной цикл, который выполнялся 274102 раза (около 50 раз на абзац, включая оба прохода, взятые вместе, когда требуется второй проход). Общее количество созданных узлов разбиения составляет 64003 (около 12 на абзац), включая разнообразие для сравнительно редких случаев, когда нужно различать разные классы соответствия или номера строк для одной и той же точки разбиения. Таким образом, около 23% допустимых точек разбиения снова превращаются в допустимые, давая относительно низкие значения ру и р^. Внутренний вычислительный цикл выполнялся 880 677 раз; это есть общее число активных узлов, рассматриваемых тогда, когда обрабатывается каждая допустимая точка, суммируемых по всем допустимым точкам разбиения. Обратите внимание, что это соответствует приблизительно 160 рассмотрениям активных узлов на абзац и 3.2 на точку разбиения, так что внутренний цикл определенно доминирует во времени прогона. Если предположить, что слова состоят примерно из пяти букв, так что допустимое разбиение возникает на каждой шестой литере исходного текста, включая пробелы между словами, то алгоритм обходится примерно в половину внутреннего цикла на литеру исходного текста плюс время прохода этой литеры в самом удаленном от центра цикле.
Эти исходные данные были также использованы для установления важности необязательной проверки ‘if Dc < D-V^\ предшествующей созданию нового узла; без этой проверки алгоритму потребовалось бы примерно на 25% больше времени выполнения внутреннего цикла, поскольку создавалось бы слишком много ненужных узлов.
А как обстоят дела с выводом? На рис. 19 приводится реальное распределение коэффициента корректировки г в 15 531 строках тома Seminumerical Algorithms, не считая 5526 строк с концами абзацев, для которых г « 0. Была также одна строка с г « 1.8 и одна с г « 2.2 (т. е. с позорным пробелом в 12 единиц); возможно, кто-нибудь из читателей самостоятельно обнаружил одну
или обе эти аномалии. Среднее значение г, вычисленное по всем 21057 строкам, было равно 0.08, а стандартное отклонение — всего лишь 0.403; примерно в 67% строк междусловный интервал варьировался в интервале от 5 до 7 единиц. Более того, авторы считают, что виртуальное отсутствие 15 531 разбиений строк «психологически плохо» в упомянутом выше смысле.
Любой, кто экспериментировал с типичными английскими текстами знает, что подобная статистика не только великолепна, она фактически слишком хороша, чтобы быть достоверной; но алгоритм разбиения на строки может достичь своего звездного часа без всякого вмешательства автора, который замечает, что небольшие изменения в словах приведут к лучшим разбиениям строк. В самом деле, этот феномен представляет собой другой источник улучшения качества, когда автору предоставляется средство верстки вроде того, которым владеет ТЁК, потому что профессиональный наборщик не отважится производить беспорядок в словах, когда организует их в абзац, тогда как автор будет счастлив сделать улучшающие изменения, особенно когда такие изменения пренебрежимо малы по сравнению с другими, которые сочтет необходимыми технический редактор, читающий верстку. Авторы знают, что есть масса возможностей выразить свою мысль, так что это не такая уж проблема для них внести небольшие коррективы в слова.
Теодор Л. Де Винн, один из виднейших американских полиграфистов начала ХХ-го века, писал [11, с. 138], что «когда автор протестует против [переноса], его следует попросить добавить, либо удалить, либо заменить одно или несколько слов, чтобы избежать нежелательного разбиения строк.... Авторы, которые всегда настаивают на пробелах, заметно разделяющих слова, совсем не представляют себе всю строгость типографских правил».
-1.00 < г <-0.75 -0.75 < г <-0.50 -0.50 < г <-0.25 — 0.25 < г < 0.00
0.00 < г <+0.25 +0.25 < г <+0.50 +0.50 < г <+0.75 +0.75 < г < + 1.00 + 1.00 < г < + 1.25 + 1.25 < т < + 1.50 + 1.50 < г < + 1.75 + 1.75 < г < + 2.00
+2.00 < г <+оо
Рис. 20. Распределение междусловных интервалов, найденное методом «лучшая строка за раз», по сравнению с методом «лучший абзац за раз», когда сложный математический текст набирается без вмешательства человека.
Другой интересный комментарий принадлежит Дж. Б. Шоу [39]: «Где бы он [Уилльям Моррис] ни обнаруживал в своих работах неудачно выровненную строку, он варьировал исключительно междусловными пробелами ради достижения зрительного восприятия при печати. Когда мне присылали корректуру с двумя или тремя строками столь безжалостно разреженными, что на странице появлялся серый коридор, я часто переписывал отрывок, чтобы строки лучше заполнились; но с прискорбием должен констатировать, что мое предложение обычно бывало плохо понято и наборщик калечил еще и весь хвост абзаца, вместо того чтобы исправить свою плохую работу».
Смещение, вызванное настройкой Кнута своей рукописи под конкретную длину строки, сделало статистику рис. 19 неприменимой к той ситуации, когда данный текст должен быть распечатан в том виде, как он был набран. Поэтому был предпринят другой эксперимент, в котором материал разд. 3.5 из Seminumerical Algorithms был помещен в строки длиной 25 pica вместо 29 pica. Раздел 3.5, посвященный вопросу «Что такое случайная последовательность?», был выбран потому, что этот раздел больше всего соответствует типичной математической статье, содержащей теоремы, доказательства, леммы и т. п. В этом эксперименте алгоритм оптимальной верстки работает более интенсивно, чем в случае строк длиной 29 pica, в основном потому, что второй проход нужен в три раза чаще (49 раз на 273 абзаца, вместо 16 раз); более того, второй проход был более толерантен к ширине пробелов (р2 = 10 вместо v^), чтобы гарантировать набор каждого абзаца без ручного вмешательства. Здесь требовалось около 6 обследований активных узлов на допустимую точку разбиения вместо прежних примерно 3, так что сухим остатком от этого изменения в параметрах было увеличение почти вдвое времени работы алгоритма разбиения на строки. Причина такого противоречия состояла прежде всего в комбинации трудностей воспроизведения математики и более узкой полосе набора, а не в «авторской настройке», потому что когда тот же самый текст располагается на ширину полосы в 35 pica, второй проход был нужен только 8 раз.
Интересно рассмотреть качество пробелов, полученных в этом эксперименте с длиной строки в 25 pica, поскольку это указывает на то, насколько хорошо работает метод оптимальной верстки без вмешательства человека. На рис. 20 показано то, что получилось, вместе с соответствующей статистикой для метода улучшенной верстки, когда он применяется к тем же самым данным. В каждом случае фигурировало около 800 разбиений на строки, не считая завершающих строк абзацев. Основное различие состояло в том, что алгоритм оптимальной верстки стремится поместить больше строк в интервале .5 < г < 1, тогда как алгоритм улучшенной верстки производит значительно больше строк, которые чрезвычайно разрежены. Стандартное отклонение пробелов было равно 0.53 (при оптимальной верстке) против 0.64 (при улучшенной); 24 строки при улучшенной верстке имели пробелы, превышающие 12 единиц, тогда как при оптимальной верстке таких плохих строк было только 7. Изучение этих семи проблематичных случаев показало, что три из них получились из-за длинных неразрывных формул в тексте, три были порождены правилом, что Т^Х не должен пытаться переносить слова, набранные прописными буквами, и еще один получился из-за неспособности Т^’а перенести слово ‘reasonable’. Поверхностный анализ выведенного текста показал, что основное различие между алгоритмами улучшенной и оптимальной верстки с точки зрения обычного читателя, состоит в том, что улучшенный алгоритм не только пересортировывает случайные широкие пробелы, но также стремится завершить существенно больше строк переносами: 119 по сравнению с 80. Автор, заботящийся о пробелах и, следовательно, редактирующий рукопись с целью улучшения его внешнего вида, произведет значительный объем дополнительной работы при алгоритме улучшенной верстки, а вывод при алгоритме оптимальной верстки получит свой требуемый вид всего лишь при помощи нескольких авторских изменений.
Исторический обзор
Мы обсудили большинство ситуаций, возникающих при разбиении строк, и было бы интересно сравнить новомодные подходы с теми, с которыми печатники имели дело на протяжении многих лет. Средневековые переписчики, изготавливающие великолепные рукописные книги до эпохи книгопечатания, обычно очень трепетно относились к разбиению строк, добиваясь почти ровного правого края, и эту практику сохранили первопечатники. На самом деле наборщики стараются расставить буквы так, чтобы в каждой строке были равномерные интервалы, так что отдельные буквы не выдаются из общего строя, производя должное впечатление; они могли вставлять дополнительные пробелы между словами почти с той же легкостью, как и размещать эти пробелы в концах строк.
Одним из наиболее серьезных вызовов за многие годы, брошенных печатникам, был набор «Многязычной Библии», т. е. таких изданий Библии, в которых язык оригинала соседствовал с разнообразными переводами, по-
скольку для синхронизации различных версий на разных языках нужно было предпринять особые усилия. Кроме того, тот факт, что на каждой странице нужно было представить разные языки, обусловил тенденцию к сужению колонок; это вкупе с тем обстоятельством, что никто не мог отважиться изменить священный текст, сделало проблему разбиения строк особенно трудной. Можно получить весьма четкое представление о том, как первопечатники решали проблемы разбиения строк, внимательно изучив их многоязычную Библию.
1№рпарюьсгеа Л. uirdcus сооосоэоосо "cclum'&'terra.6 Terra autem соосооооооооооо 'ега^шатз'&уасиа:1"^ tenebre erant"fup°fade pabyffi: q& fpiritusr def ’ferebatur'fuper ooocoo "aquas.r Dixitcy’deus. "Fiat oooooooooccooooooo blux.cEt facta c’lux. eEt Viditfdeus4ucem coooco *$ сПе^Ьола: '& dirnfit CC0000000COOO00O00OOO900O °lucempa qtenebris: rap pelhuftCR O0O30CO3O0OC0O Tlucem"die: "Sftenebras ’noctem. ссоосоооздоооо “Factum^ eftb vefpec& °mane'dicsfvnus.coocoo "Dixit quoq^Mcus.'Fiat "firmamcntii1 in medio maquarum:n8ta diuidat qaquas oowoooxoooooow rab aquis.6 Et ГеаМеиз "firmamentum, ccosoco1№рпарюьсгеа Л. uirdcus сооосоэоосо "cclum'&'terra.6 Terra autem соосооооооооооо 'ега^шатз'&уасиа:1"^ tenebre erant"fup°fade pabyffi: q& fpiritusr def ’ferebatur'fuper ooocoo "aquas.r Dixitcy’deus. "Fiat oooooooooccooooooo blux.cEt facta c’lux. eEt Viditfdeus4ucem coooco *$ сПе^Ьола: '& dirnfit CC0000000COOO00O00OOO900O °lucempa qtenebris: rap pelhuftCR O0O30CO3O0OC0O Tlucem"die: "Sftenebras ’noctem. ссоосоооздоооо “Factum^ eftb vefpec& °mane'dicsfvnus.coocoo "Dixit quoq^Mcus.'Fiat "firmamcntii1 in medio maquarum:n8ta diuidat qaquas oowoooxoooooow rab aquis.6 Et ГеаМеиз "firmamentum, ccosoco> > n^D^^X^^n^XI^J, ‘
1№рпарюьсгеа Л. uirdcus сооосоэоосо "cclum'&'terra.6 Terra autem соосооооооооооо 'ега^шатз'&уасиа:1"^ tenebre erant"fup°fade pabyffi: q& fpiritusr def ’ferebatur'fuper ooocoo "aquas.r Dixitcy’deus. "Fiat oooooooooccooooooo blux.cEt facta c’lux. eEt Viditfdeus4ucem coooco *$ сПе^Ьола: '& dirnfit CC0000000COOO00O00OOO900O °lucempa qtenebris: rap pelhuftCR O0O30CO3O0OC0O Tlucem"die: "Sftenebras ’noctem. ссоосоооздоооо “Factum^ eftb vefpec& °mane'dicsfvnus.coocoo "Dixit quoq^Mcus.'Fiat "firmamcntii1 in medio maquarum:n8ta diuidat qaquas oowoooxoooooow rab aquis.6 Et ГеаМеиз "firmamentum, ccosoco
1№рпарюьсгеа Л. uirdcus сооосоэоосо "cclum'&'terra.6 Terra autem соосооооооооооо 'ега^шатз'&уасиа:1"^ tenebre erant"fup°fade pabyffi: q& fpiritusr def ’ferebatur'fuper ooocoo "aquas.r Dixitcy’deus. "Fiat oooooooooccooooooo blux.cEt facta c’lux. eEt Viditfdeus4ucem coooco *$ сПе^Ьола: '& dirnfit CC0000000COOO00O00OOO900O °lucempa qtenebris: rap pelhuftCR O0O30CO3O0OC0O Tlucem"die: "Sftenebras ’noctem. ссоосоооздоооо “Factum^ eftb vefpec& °mane'dicsfvnus.coocoo "Dixit quoq^Mcus.'Fiat "firmamcntii1 in medio maquarum:n8ta diuidat qaquas oowoooxoooooow rab aquis.6 Et ГеаМеиз "firmamentum, ccosoco
> V^oV’y^’^f °?p^‘
^6^g”i$hfA^^■
> > rrfcrnW w^D^j^sT
рз"в^х^;н^^^
> \п^У^npr:|^bУAвt>,*^.
* > > nWi;^
>>>> □>Э^^Ч^°^Ула^Г1т
> ГО^^О^^^га^
Рис. 21. Стихи, открывающие Книгу Бытия в Complutensian Polyglot Bible. Здесь латинским словам ставятся в соответствие еврейские и для заполнения строк используется пунктир, дабы предотвратить выключку вправо и влево. На той же странице помещены греческие и арамейские переводы этого текста.
Первая многоязычная Библия [10, 19, 24] была издана в Испании знаменитым кардиналом Хименесом Сиснеросом, который прославился тем, что потратил 50 000 золотых дукатов на поддержку этого проекта. Обычно это издание называют «Complutensian Polyglot», поскольку его подготовили в Алкал я де Энарес, городе в предместье Мадрида, ранее носившего название Complutus. О печатнике Арнао Гильене де Брокаре, посвятившем 1514-1517 гг. изданию этого шеститомного труда, говорили, что нарисованные им для этих книг еврейские и греческие шрифты были лучшими из когда бы то ни было созданных. Его подход к выравниванию строк был весьма интересным и необычным, как
видно из рис. 21: вместо того чтобы выравнивать строки, увеличивая межд- условные пробелы, он вставлял отточия, чтобы получить жесткие блоки материала с ровными краями.
Эти отточия появляются справа в латинском тексте и слева — в еврейском. Обретя навык, он несколько изменил своему приему: примерно с 46-й главы Книги Бытия еврейский текст выравнивался посредством междусловных пробелов, хотя в латинском тексте отточия продолжали появляться. Понятно, что ровные края в те времена выдерживались неукоснительно.
Способ Гильена де Брокара разбиения строк был по существу методом предварительной верстки, примененным к еврейскому тексту; соответствующий латинский перевод размещался чрезвычайно просто, поскольку две строки латинского варианта соответствовали одной строке еврейского текста и точно помещались в отведенное для них место. В отдельных случаях, когда греческий текст был значительно длиннее соответствующего еврейского (например Exodus 38), он размещал еврейский очень плотно, что с очевидностью демонстрирует его особое отношение к разбиению строк.
В то же самое время Агостино Джустиниаии из Генуи [17] совершенно безвозмездно подготовил многоязычную версию Книги Псалмов. Это в сущности была первая появившаяся в печати многоязычная книга, в которой для каждого языка был использован свой шрифт, хотя обычно считается, что все более поздние многоязычные издания были инспирированы рукописью Ш-го столетия Hexapla, написанной О rigen’ом. В Псалтыри Джустиниаии было 8 колонок: (1) древнееврейский оригинал; (2) его литературный латинский пересказ; (3) популярное латинское (вульгата) толкование; (4) греческая (Sep- tuagint) версия; (5) арабская версия; (6) халдейская версия; (7) ее литературный латинский перевод; (8) примечания. Поскольку Псалмы представлены в стихотворной форме, все колонки, за исключением последней, имели рваные края, причем для строк, слишком длинных, чтобы разместиться в колонке, использовалось следующее соглашение: в конце разбиваемой строки помещалась открывающая скобка и остаток строки, предваряемый другой открывающей скобкой, выравнивался по краю предыдущей или последующей строки, как удобнее.
Выровненной была только колонка (8), причем она была очень узкой, порядка 21 знака в строке. Пристально изучив ее вид, мы можем заключить, что Джустиниаии не прилагал больших усилий к тому, чтобы получить равномерные пробелы, поиграв словами. Например, на рис. 22, на котором представлен фрагмент примечаний к Псалму 6, видно, что между двумя очень плотными строками находится весьма жидкая строка: ‘scriptum est ... quod qui’. Если бы Джустиниаии захотел уравновесить пробелы, он бы мог сделать перенос: ‘cog-nosces’; другим потенциальным решением — перенести kad’ на предыдущую строку — воспользоваться нельзя, потому что для этого слова там мало места. Обратите внимание, что в те времена был и другой прием, способствующий разбиению строк в латинском тексте: замена т или п на тильду, проставляемую над предшествующей гласной (например, ‘premiu’ для premium и ‘mudo’ для mundo); алгебру бокс/клей/штраф следовало бы рас-
Рис. 22. Фрагмент комментария Джустиниани к Псалмам. Наличие жидкой строки между двумя очень плотными говорит о том, что наборщик, столкнувшись с проблемой, не возвращался назад для переиабора предшествующих строк.Рис. 22. Фрагмент комментария Джустиниани к Псалмам. Наличие жидкой строки между двумя очень плотными говорит о том, что наборщик, столкнувшись с проблемой, не возвращался назад для переиабора предшествующих строк.qui arboribus pknus ? eft,fecerunc fui noti> tiam,& acceperuntin telledumab co,К ui^ ciflim cumeorumd(V • mino fecognouerunt, ; facies cum facte,& o^ culuscumoculo^hu ius rei gratia merue^ runt premia in futuro : mudo,& hoc eft quod j fcriptumefty&cogno^ fees hodie reuerfus i adcortuum,quod qui dac efle ipfeeft Deus : in cell's deiuper^qd: in terra deorfumnon
Рис. 22. Фрагмент комментария Джустиниани к Псалмам. Наличие жидкой строки между двумя очень плотными говорит о том, что наборщик, столкнувшись с проблемой, не возвращался назад для переиабора предшествующих строк.
Рис. 22. Фрагмент комментария Джустиниани к Псалмам. Наличие жидкой строки между двумя очень плотными говорит о том, что наборщик, столкнувшись с проблемой, не возвращался назад для переиабора предшествующих строк.
! fitpreter cum*
ширить, включив такие опции в ТеХ’овский алгоритм разбиения на строки. Непонятно, почему Джустиниани не поместил ‘acceperut’ в третьей строке, чтобы сэкономить пространств, ведь у него не осталось места для знака переноса в слове ‘in-tellectum’; возможно, в его шрифтовой кассе не было достаточного количества литер п.
На рис. 23 показан выровненный текст из Complutensian Polyglot —выдержка из латинского перевода раннего арамейского перевода древнееврей-
H
1R pzindpio creauit beus сеШ t terra* Ca.U ^erra out erat beferta г vacua:? tenebze fup factemabpdi:t (pus bet infufflabat tup faciS aqua?.fit biritbeus.Sitlur:? fuit lur*?viditbeus
lucS ф eflet bona. fit biuifit beus inter luce г inter te nebzas.appeUauitcy bens lucSbie:? tenebzas voca^ uitnocte.fitfuitvefpeTfuitmanebiesvnue*£t bi- ritoeus.Sitfirmamentu in medio aqua?: zbiuidat inter aquas т aqs.fit fecit bens firmamentuzet biul fit iter aquas q erant fubter firmamdtU5:t infer aqs q erant fug firmamentuz? fuit ita* fit vocauit be? fir mamentu celut* fit fuit vefge? fuit mane biesfrfs* fit oidt be?* ft ogregetur aque q fub celo funt in loefi vnumz? appareat arida*fit fuit ita* fit vocauit beus arida terra: ? 1осй cogregationis aquaru appellauit maria, fit vidit bev q6 diet bonfi. fit birit beus* 6er minet terra germinations berbecuivfiliusfemStis
feminatiarboz&B fructifera fadente fructus fm ge^ nus fuu: cuius filius femStis in ipfo fit fup terra* fit fuit ita.fit Д) dur it terra germs berbe cuius filius fe mStis feminat fm genus futtret arboze fadentSfrus ctuszcuiv filius fcmetis i ipfo fm genus fuiufit vidit
Рис. 23. Для ранних образцов печатных латинских текстов характерны плотные пробелы, получаемые за счет частого использования сокращений и разбиения слов. Образец взят с той же страницы, что и фрагмент рис. 21.
ского оригинала. Наборщику совершенно непостижимым образом удалось выдержать во всем томе равномерно плотный набор, используя сокращения и частые переносы. В пяти местах, как и па рис. 22, знаки переноса при разрыве слов отсутствуют, когда для них нет места; например, слово ‘diuisit5 было перенесено без соответствующего знака.
Следующим грандиозным изданием многоязычной Библии было «Royal Polyglot of Antwerp» [1], созданное в течение 1568-1572 гг. выдающимся бельгийским печатником Кристофом Плантеном. К сожалению, многочисленные копии Сотр1и1етг8гатг Polyglot утонули в море, поэтому король Филипп II организовал подготовку нового издания, что позволило также воспользоваться новыми технологическими достижениями. Плантен был набожным человеком, действовавшим в пацифистских религиозных кругах и очень стремившийся заняться этой работой. Но полностью завершив работу, он описал ее как «невыразимо тяжкую, нудную и изнурительную». В письме одному из своих друзей от 9 июня 1572 г. Плантен пишет: «Не перестаю удивляться себе: как мог я взвалить на себя такое! Никогда в жизни я больше не возьмусь за подобное, даже если меня одарят 12 000 крон». Но его работа была по крайней мере достойно встречена ценителями: Люка из Брюга писал в 1577 г., что «никогда искусство печатника нс поднималось до столь величественных высот и не производило ничего более великолепного».
Многоязычная Библия Плантена была по большей части выровнена внутри довольно широких колонок примерно с 42 литерами в строке, и это не представляло особого труда с точки зрения разбиения строк. Но изучив тексты Апокрифа, набранного в более узких колонках с 27 литерами в строке, можно получить некоторое представление о его методах работы. Он так расположил текст, что в каждой колонке на одной странице было примерно равное количество строк, несмотря на то, что сами колонки были на разных языках.
* 1 *Et ftatim perrexcrunt ad eoS) & conftitueruncaduer- fus cos proelium in die fab-. batorum , & dixerunt ad . cos.
Рис. 24. Латинская версия Маккабея 2 : 32 из Антверпенской Royal Polyglot Плантена, из которой видно, как получена дополнительная строка благодаря пробелам во второй строке снизу.
На рис. 24 приведен пример отрывка, в котором латинский текст был несколько разрежен, так что абзацы на этой странице оказались довольно жидкими. Очевидно, сначала была набрана вся страница, а затем были сделаны корректировки, поскольку латинские колонки оказались слишком короткими. В данном случае, слово ‘eos’ было перенесено вниз, чтобы получить новую строку, а предыдущая строка была разрежена. Наборщик Плантена не искал приключений на свою голову и не передвинул вниз ‘sab-’ на эту строку, хотя такое перемещение позволило бы избежать переноса и последующих плохих пробе-
лов. Оптимальным было бы убрать этот перенос и разбить предшествующую строку после ‘ad-’, достигнув таким образом равномерных пробелов всюду.
Самым аккуратным и полным из всех многоязычных изданий Библии было «London Polyglot» [41], напечатанное Томасом Ройкрофтом и другими во времена правления Кромвеля с 1653 по 1657 гг. Эта объемистая 8-томная работа содержала тексты на древнееврейском, греческом, арамейском, сирийском, эфиопском, самаритянском языках и фарси, и все они сопровождались латинскими переводами. Этот труд был признан «вершиной типографского мастерства семнадцатого столетия». Как и в работе Плантена, приведенной на рис. 24, подвергавшийся разреживанию абзац зачастую заканчивался неоправданно плотной строкой, за которой следовала жидкая, а за ней —строка из одного слова. Из этого очевидно, что у наборщика Ройкрофта не было времени выполнить сложную корректировку разбивки строк.
В то время к переносам явно не придирались, поскольку 40% всех строк в London Polyglot оканчиваются переносами, независимо от ширины колонки. Не составляет труда найти страницы, в которых строк с переносами больше, чем строк без оных. В латинском переводе арамейской версии Книги Бытия 4:15 перенесено даже двухбуквенное слово ‘е-o’! Подобная практика не была общепринятой; например, в гамбургской Polyglot Bible от 1596 [42] более 50% переносов находится в правом поле. Как многоязычная Библия Плантена, так и заметки Джустиниани к Псалтыри содержат порядка 40% переносов, и примерно так же обстояло дело со многими средневековыми рукописями. Таким образом, считалось, что лучше иметь ровные поля и плотный набор, чем избегать разбиения слов.
Одна из первых особенностей, которую подмечает современный взгляд при изучении этих изданий Библии, является некоторая вольность в обращении со знаками препинания. Обратите внимание, например, что на рис. 22 после запятых нет пробелов, а на рис. 24 пробел стоит как до, так и после одной из запятых. В каждом из перечисленных выше изданий Библии вы можете обнаружить все четыре варианта: «пробел до/отсутствие пробела до» и «пробел после/отсутствие пробела после» по отношению к следующим знакам препинания: запятая, точка, двоеточие, точка с запятой и вопросительный знак. При этом никаких явных предпочтений одному из четырех вариантов выбора не отдавалось, за исключением того, что пробел перед точкой делался довольно редко. Джустиниани и Плантен иногда могли оставить пробел перед точкой, но Ройкрофт этого не делал никогда. Около 1700 г. запятые в этом смысле были приравнены к точкам, но двоеточие и точка с запятой вплоть до 19-го столетия окружались пробелами с двух сторон. Такие дополнительные пробелы помогали, разумеется, в выравнивании текста, и печатники с очевидным удовольствием изничтожали все пробелы после знаков препинания. Ройкрофт на самом деле удалял пробелы между словами в случае необходимости, если следующее слово было с прописной буквы (например, ‘dixitDeus’); очевидно, что основной целью печатников было сохранить текст в виде, поддающемся однозначной расшифровке, тогда как легкость его восприятия была задачей второстепенной важности.
Знания в области такого производства, как книгопечатание, распространялись от мастеров к подмастерьям и не являлись достоянием широкой публики, так что мы можем только догадываться, чем руководствовались печатники тех времен, считая свое изделие завершенным. Тенденция публиковать свои производственные секреты сформировалась, однако, в течение 17-го столетия [21], и, наконец, была написана книга о том, как делать книги: Mechanick Exercises Джозефа Моксона [30], опубликованная в 1683 г., оставалась на протяжении сорока лет самым востребованным руководством по печати на всех языках. Хотя Моксон не обсуждает правил переносов и пунктуации, он приводит интересную информацию о разбиении строк и выравнивании текста.
«Если наборщик решительно не настроен придерживаться строгих правил Высокого Мастерства, то он будет вынужден лататъ дыры», т. е. делать плохие разбиения строк, согласно Моксону. Обыкновенная «толстая шпация», разделяющая слова, когда строка начинает верстаться, равна одной четверти того, что Моксон называет «body size» (кегельной); он также говорит о «тонких шпациях», которые равны одной седьмой от кегельной; таким образом, печатник, следующий этой практике, будет преимущественно иметь дело с пробелами в 4.5 единиц и 2.57 единиц, хотя такие меры весьма приблизительны в силу примитивности средств, используемых в то время. Процедура Моксона выравнивания текста, естественная ширина которого достаточно узкая, состояла в том, чтобы заложить толстые шпации между одним или более слов для «заполнения данного размера весьма плотно» и, если необходимо, вернуться в начало строки и проделать все снова. «Строгие правила Высокого Мастерства не допускают пробелы более [чем исходный пробел плюс две толстые шпации], за исключением тех случаев, когда Размер строки слишком мал, из-за чего в Строке помещается всего несколько Слов и это вынуждает Наборгцика сделать больше Пробелов между Словами. ... Эти широкие Промеоюртки, оставляемые (самым неприличным образом) Наборщиком, называются Дырами... И как бывают Строки слишком Разреженными, также бывают они и слишком Плотными». [30, сс. 214-215]
Обратите внимание, что процедура выравнивания Моксона обычно приводит к неравномерным пробелам между словами в строке, поскольку он вставляет толстые шпации одну за другой. В самом деле, такая несбалансированность была нормой в ранних образцах печатной продукции, которые несколько напоминают сегодняшние попытки выравнивания на пишущей машинке или компьютере с фиксированной величиной пробела. Например, отношение величин пробелов в третьей строке текста Плантена с рис. 24 составляет приблизительно 8:12:5:9:4, ав пятой строке текста Джустиниани с рис. 22 — приблизительно 3 : 2 : 1. В книге же самого Моксона (см. рис. 25) продемонстрировано чрезвычайное разнообразие пробелов, зачастую в нарушение правил, им декларированных относительно максимальных и минимальных расстояний между словами.
Было бы заманчиво утверждать, что Моксон описывает некий алгоритм разбиения строк, наподобие метода простой или улучшенной верстки, но на самом деле ни он и никто из его последователей до наступления компьютерной
If there be a long word or more left out, he;cannot expedt to Get that in into that Line. where-fore he mu ft now Over-run*. that is, hemuft put fo! much of the fore-part of the Line into the Line■ above it, or fo much of the hinder part of the Lineinto the next Line under it, as will make room forwhat is Left out*. Therefore he confiders how Wide; he has Set. that fo by Over-runing the fewer Lines■ backwards or forwards, or both, (as he finds his help)he may take out fo many Spaces. or other Whites; as will amount to the Thicknefs of what he has Leftout : Thus if he have Set wide. he may perhaps Geta fmall Word or a Syllable into the foregoing Line^rA' perhaps another fmall Word от Syllable in the follow- ,■ ing Line, which if his Leaving out is not much, may: Get it in : But if he Left out much, he mu ft Over-run^many Lines, either backwards or forwards, or bothtill he come to a Break : And if when he comes ata Break it be not Gotten in ; he Drives out a Line.In this cafe if he cannot Get in a Line, by Gettingin the Words of that Break (as I juft now ihew’d you
Рис. 25. Фрагмент co c. 245 издания Mechanick Exercises^ t. 2, Джозефа Моксона, первой книги о том, как делать книги. Здесь Моксон описывает процесс корректировки страниц, которые уже были набраны; неравномерные пробелы в его собственной книге служат, вероятно, одним из подтверждений того факта, что такая корректировка была необходима. (Да, эти строки тоже далеки от совершенства.)
эры никогда не имели ввиду какой бы то ни было конкретной процедуры. В этом нет ничего удивительного, поскольку люди обычно предпочитают руководствоваться здравым смыслом, а не подчиняться строгим правилам. Многие из разбиений строк на рис. 25 могут быть, однако, объяснены предположениями, фигурирующими в алгоритме простой верстки. Например, жидкие строки 1, 4 и 8 вызваны, вероятно, длинными словами в строках 2, 5 и 9, поскольку эти длинные слова не помещаются в предыдущих строках, если их не перенести. С другой стороны, появление чрезвычайно плотной строки 13 лучше всего можно объяснить тем предположением, что для исправления ошибки были вставлены одно или более слов уже после того, как страница была сверстана. Мы не можем удовлетворительно воспроизвести процедуру набора, исходя только из окончательного варианта: для этого нам нужно было бы посмотреть на первую корректуру. Тем не менее можно с уверенностью заключить, что было очень мало попыток вернуться назад и пересмотреть уже сверстанные строки, кроме тех случаев, когда это было крайне необходимо. Так, например, абзац на рис. 25 выглядел бы много лучше, если бы первая строка заканчивалась на ‘сап-’, а вторая —на ‘wherefore’.
Наборщик Моксона, однако, был сориентирован на то, чтобы смотреть вперед: «Когда при Наборе он подходит к Разбиению [т. е. в конце абзаца], за несколько Строк до этого он должен понять, будет ли это Разбиение заканчиваться Пробелом разумного размера; если это так, то и прекрасно, но если
окажется, что всего единственное Слово имеется при его Разбиении, он должен либо Добавить пробелы и перенести одно или два слова на Разбиваемую строку, либо еще Сузить пробелы, чтобы поместить маленькое Слово, потому что Строка с единственным словом выглядит почти как Пустая строка, которая будучи расположена в неправильном месте не радует глаз». [30, с. 226]
Рис. 26.Печатники не всегда практикуют то, что проповедуют.Рис. 26.Печатники не всегда практикуют то, что проповедуют.they may be all exactly the same length, it will almost always happen that the line will either have to be brought out by putting in additional spaces between the words, or contracted by substituting thinner spaces than those used in setting up the lines. If the line by that alteration is not quite tight, an additional thin space may be inserted between such words as begin with j or end with f, and also after all the points, but they must, to look well, be put as near equally as possible between each word in the line, and after each sentence an em space is used.
Рис. 26.
Печатники не всегда практикуют то, что проповедуют.
Рис. 26.
Печатники не всегда практикуют то, что проповедуют.
Другая выдержка из лондонского руководства для наборщиков [7] приводится на рис. 26; это уже 1864 г., а не 1683. Хотя автор здесь говорит о том, что при выравнивании текста нужно делать как можно более равномерные пробелы, наборщик его книги не следует содержащимся в ней инструкциям! Только одна замечательная книга, о которой речь шла выше, именно Complutensian Polyglot, имеет практически одинаковые пробелы. На самом деле печатникам лишь изредка удавалось достичь по настоящему равномерных пробелов, пока, к концу девятнадцатого столетия, эту задачу нс облегчили наборные машины монотип и линотип. Эти новые машины с их установкой на скорость изменили философию выравнивания текста настолько, что качество разбиения строк падало, когда пробелы становились равномерными: наборщики не могли себе позволить вернуться и пересмотреть предыдущие точки разбиения строк абзаца, когда их ждало впереди еще столько новых пробелов в час.
Разбиение строк на рис. 26 выполнено вполне неплохо в смысле неравномерных пробелов, учитывая, что наборщик хотел избегать переносов и психологически плохого разбиения в сочетании ‘with j’. Тем не менее, слово ‘but’ лучше было передвинуть на 9-ю строку.
По-видимому, самым изящным образом пробелы были расставлены в книге Самюэля Бартелса The Art of Spacing [5], вышедшей в 1926 г. Эту книгу автор набрал самостоятельно, в каждой строке было около 50 знаков. Не было ни жидких строк, ни переносов. Заключительные строки абзацев всегда были заполнены по крайней мере на 65% ширины колонки и отстояли от правого поля по крайней мере на одну круглую (em). Чтобы достичь такого результата, Бартелс менял свой исходный текст много раз; автор, будучи наборщиком, явно мог улучшить внешний вид своей книги.
Во Франции Жорж Бафур, Андре Бланшар и Франсуа Раймон использовали для набора компьютеры общего назначения и в 1954 г. предприняли попытку запатентовать свое изобретение (В 1955 г. они получили патенты во Франции и Германии, а в 1956 г.— в США [2, 3].) В их системе особое внима-
' Le bon sens est la chose du mondela mieux partagee: car chacun penseen etre si bien pourvu que ceux meme1 qui sont les plus difficiles a contenter •en toute autre chose n’ont point cou-tume d’en desirer plus qu’ils en ont. En ;. quoi il n’est pas vraisemblable que tous
Рис. 27. Образец вывода, сделанного в 1958 г. на первой наборной системе, управляемой вычислительным устройством. Все точки разбиения строк выбирались автоматически.
ние уделялось переносам, и эти авторы были, вероятно, первыми, кто строго сформулировал алгоритм разбиения строк. На рис. 27 показан образчик их вывода, который демонстрировался на выставке Imprimerie Nationale в 1958 г. В этом примере слово ‘еп’ не было включено во вторую строку, потому что в их схеме предпочтение отдавалось жидким строкам: в каждой строке могло содержаться столько литер, сколько возможно для соблюдения условия, что строка допустима, но следующие дополнительные К литер уже недопустимы; здесь К было постоянным, а их метод основывался на /f-шаговом просмотре.
Свои опыты с компьютерным набором Майкл Барнетт начал в Массачус- сетском технологическом институте (МТИ) в 1961 г., а работе его группы в Объединенной вычислительной лаборатории было предначертано стать самой авторитетной в США. Например, система troff [31], с которой сейчас работают во многих вычислительных центрах, восходит к системе Барнетта РС6 [4]; промежуточными были системы RUNOFF и NROFF. Наследники по другой линии представлены системами PAGE-1, PAGE-2 и PAGE-3, которые также широко использовались в полиграфии [22, 25, 34]. Во всех этих программах применялся метод простой верстки, описанный выше.
Примерно в то же время Барнетт начал в МТИ изучение компьютерного набора, другого важного университетского исследовательского проекта с аналогичными целями, который был начат в Вычислительной лаборатории Университета Ньюкасл-он-Тайн. Разбиение строк было одной из первостепенных задач, усиленно исследуемой этой группой, и они разработали программу, которая находила бы допустимые способы набора абзаца без переносов, если существует последовательность допустимых точек разбиения, дающая минимальные или максимальные значения для междусловных интервалов. Их программа работала по существу методом перебора всех возможностей с возвратом, обрабатывая их в обратном лексикографическом порядке (т. е. начиная с первой точки разбиения bi, настолько большой, насколько возможно, и используя рекурсивно тот же метод, чтобы найти допустимые точки разбиения (62, 63,...) в оставшейся части абзаца, затем уменьшив bi и повторив процесс при необходимости). Таким образом было нетрудно либо найти лексикографически наибольшую последовательность точек разбиения, либо заключить, что таковой нет. В последнем случае допускался перенос. Это была первая система-
тическая последовательность экспериментов, которая имела дело с разбиением строк в абзаце как в таковом, а не работала с каждой строкой по-отдельности.
В этих ранних экспериментах не делалось никаких различий между последовательностями допустимых точек разбиения; критерием было только одно: могут или не могут все междусловные интервалы быть заключены в некотором диапазоне без переноса слов. Дункан обнаружил, что когда строки имеют длину 603 единицы, то теоретически можно избежать всех переносов, если размеру пробелов позволено меняться в диапазоне от 3 до 12 единиц; Если, однако, строки имеют длину 405 единиц, то переносы будут нужны примерно в 3% случаев при сохранении тех же ограничений, а если длина строки уменьшится до 288 единиц, то процент необходимых переносов повысится до 12% или 16% в зависимости от вида набираемого материала. При более узких границах, скажем, от 4 до 9 единиц, использовавшихся в большинстве рассмотренных выше примеров, для строк длиной 603 единицы было обнаружено более 4% переносов и от 30% до 40% — для строк в 288 единиц. Тем не менее этот процентный уровень выше, чем необходимо, потому что ньюкасл овская программа не искала самых лучших мест для переносов: как только не было допустимого способа набрать более к строк, в (/с + 1)-й строке просто ставился перенос и процесс начинался снова. Один перенос, полученный таким способом, имел тенденцию к размножению в этом абзаце, поскольку первая строка абзаца или искусственно возобновленный абзац скорее всего потребуют переноса. Примеры такого рода можно видеть в самой статье, где метод был описан [13]; в первых шести страницах использовались пробелы от 4 до 15 единиц , а для остальных —от 4 до 12 единиц, так же как и в обзоре Дункана [14]. В этих статьях также обсуждаются возможные усовершенствования метода: одна идея касалась того, чтобы избегать появления жидких строк непосредственно за плотными, другая состояла в испытании метода сначала со строгими интервалами, а затем увеличивать допустимый интервал, прежде чем пересортировывать переносы.
Такие усовершенствования были сделаны значительно позже П. И. Купером в фирме Elliott Automation, который разработал сложную экспериментальную систему для обработки всего абзаца целиком [9]. Система Купера работала не только с минимальными или максимальными размерами пробелов; она также делила разрешенные междусловные интервалы на различные секторы, которым назначались разные «уровни штрафа». Он сопоставлял штрафы интервалам на отдельных строках и назначал дополнительные штрафы в соответствии с секторами интервалов двух последующих строк. Целью было минимизировать общий штраф, причитающийся за набор данного абзаца. Таким образом, его модель была очень похожа на ту, что использовалась в пакете Т^Х, за исключением того, что все интервалы были эквивалентны друг другу и не надо иметь дела со специальными вопросами типа переносов.
Купер говорил, что в основу его программы положен «математический аппарат, известный как ‘динамическое программирование’», для выбора оптимального вида набора. Он, однако, опустил подробности, но из заключенных в памяти компьютера ограничений понятно, что его алгоритм имел лишь отдаленное сходство с настоящим динамическим программированием, в котором
Глава 3. Верстка абзацев 151 поддерживается только одна оптимальная сумма штрафов для каждой точки разбиения, а не для каждой пары (точка разбиения, сектор). Таким образом, его алгоритм был, вероятно, похож на метод, который описан ниже в приложении.
К сожалению, метод Купера опередил свое время; в 1966 г. все считали, что столько дополнительного времени вычисления и памяти компьютера неоправданно дороги. Кроме того, его метод оценивался только с точки зрения того, как много переносов удастся сэкономить, а не с тех позиций, насколько лучшие пробелы он предоставляет в строках без переносов. Например, в обзоре Дж. Л. Долби [12] процедура Купера сравнивается с процедурой Дункана в невыгодном для первой свете, поскольку ньюкасловский метод удаляет то же количество переносов, что и появляется, будучи при этом менее сложной программой. На самом деле сам Купер недооценивал свою схему и говорил о ней с необыкновенной скромностью и осторожностью: «это исследование не поддерживает ту точку зрения, что [мой подход] должен рассматриваться как общий и усиленно рекомендуемый. ... Следует признать ..., что, вообще говоря, эстетическое усовершенствование не может быть ни предсказано, ни измерено». Вскоре его метод был забыт.
Ретроспективный взгляд позволяет выявить один дефект в методе Купера, во всех других отношениях великолепном: не делалось точного различия между строками с переносами и допустимыми строками без переносов, так что после каждого вновь вставляемого переноса метод должен был начинать работу снова. Таким образом, переносы имели тенденцию группироваться как и в экспериментах Дункана.
Еще один подход к разбиению строк недавно исследовала Э. М. Прингль из Кембриджского университета, которая предложила процедуру Juggle [36] (жонглирование). Этот алгоритм использует метод простой верстки без переносов, пока не будет достигнута строка, которую невозможно разместить; тогда вызывается рекурсивная процедура pushback (обратное втискивание), которая пытается втиснуть слово из проблемной строки в предыдущий текст. Если процедура pushback не приводит к успеху, происходит обращение к другой рекурсивной программе pullon (вытягивание), которая пытается вытянуть слово из предыдущего текста. Перенос проставляется только тогда, когда и pullon терпит неудачу. Таким образом, Juggle представляет собой попытку моделирования методических действий мастера ручного набора как в добрые старые времена. Рекурсивный перебор с возвратом может, тем не менее, потратить значительно больше времени, чем подход динамического программирования, при этом оптимальное выравнивание строк вообще может быть не достигнуто. Например, вместо рис. 3 мог бы получиться рис. 2. Более того, бывают экзотические случаи, когда решения существуют, но Juggle их не находит: например, есть возможность втиснуть два слова, а не одно.
Анан Саме в своей недавней работе [38] предложил другую меру оптимальности. Поскольку все методы верстки абзаца с данным количеством строк содержат одинаковый общий объем пробелов, он обратил внимание на то, что средний междусловный интервал в абзаце существенно зависит от точек раз-
биения (если мы не обращаем внимания на переносы и на тот факт, что завершающая строка отличается от других). Он, таким образом, пришел к выводу, что дисперсия междусловных пробелов должна быть минимизирована и построил алгоритм «downhill» (скоростного спуска), который сдвигает слова из одной строки в другую до тех пор, пока больше нет никакого такого локального преобразования, которое снизило бы дисперсию.
Первым журналом, который стал готовиться при помощи компьютера, был Time Inc. of New York City, способ разбиения строк которого стал весьма популярным в 1967 г. Согласно комментариям X. Д. Паркса [32], разбиение строк делалось построчно, при помощи разновидности алгоритма простой верстки, который мы можем сегодня назвать «плотной версткой»; он позволял поместить в строке максимально много слов, а переносы делались только в случае необходимости. Это эквивалентно методу простой верстки, если обычные интервалы сделать равными минимальным. Метод плотной верстки имел своего предшественника, который использовался в наборной системе IBM 1620 и демонстрировался в 1963 г. (см. Дункан [14, сс. 159-160]), и вполне разумно предположить, что по существу тем же методом воспользовалась группа журнала Time, когда они задействовали для целей набора две машины IBM 360/40 [33].
Поскольку до получения своего окончательного вида текст в журнале Time не раз редактировался и разбиения строк подвергались в последнюю минуту ручному вмешательству, невозможно точно сказать, просто рассматривая напечатанную статью, каким алгоритмом тогда пользовались, хотя есть соблазн утверждать, что алгоритм оптимальной верстки мог бы улучшить внешний вид таких публикаций. На рис. 28 показан интересный пример, основанный на с. 22 из журнала Time от 23-го июня 1980 г.; вариант А демонстрирует пробелы, которые были в этом издании, а вариант В —то, что дал новый алгоритм в тех же обстоятельствах. Все буквы этого текста были заменены буквой 1п’ соответствующего размера, так что был снят вопрос авторского права и появилась возможность сконцентрироваться исключительно на пробелах. Тем не менее этот прием сделал плохие пробелы менее раздражающим, так как читатель не так расстраивается, когда представленный текст не несет смысловой нагрузки.
Наиболее интересный момент, замеченный на рис. 28, состоит в том, что завершающая строка первого абзаца была растянута до правого края, чтобы точно обрамить вставленную фотографию; эта фотография помещена целиком в правой колонке. В варианте А такой эффект достигается посредством растяжения трех последних строк; при этом остаются большие дыры, безусловно раздражающие глаз многих читателей. В варианте В показано, как оптимизационный алгоритм, чудесным образом заглядывая вперед, безукоризненно печатает такие вещи. Возможно, даже более важен тот факт, что в версии В не понадобилась дополнительная разрядка, что имеет место в строках 6, 9, 10, 23 и 32 версии А.
Разрядку — вставку крошечных пробелов между буквами слова, чтобы сделать менее заметными большие междусловные интервалы — можно без труда вписать в модель бокс/клей/штраф, но это порицается полиграфиста-
nn Nninn ni Nnnini Nnninini Nnnnnnni и nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nn- nnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn 111П1ППП1 nnn n ninuni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnnunnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—inn- ninin inn inn 1ШП1 ni inn Ninun Nnnnn nnn inn inn- nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nn- innni nnn Nninnninni, Nnini, Nnnnnn nnn nnnniinii Nnin- unn uinni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn,nn Nninn ni Nnnini Nnninini Nnnnnnni и nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nn- nnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn 111П1ППП1 nnn n ninuni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnnunnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—inn- ninin inn inn 1ШП1 ni inn Ninun Nnnnn nnn inn inn- nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nn- innni nnn Nninnninni, Nnini, Nnnnnn nnn nnnniinii Nnin- unn uinni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn,ВВnn Nninn ni Nnnini Nnninnu Nnnnnnni ii nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nnnnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn inninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- unnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—innninin inn inn mini ni inn Ninun Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnnnn Nninnnn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninun. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nm-nn Nninn ni Nnnini Nnninnu Nnnnnnni ii nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nnnnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn inninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- unnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—innninin inn inn mini ni inn Ninun Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnnnn Nninnnn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninun. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nm-AArun 1П11111 ni nnnnnin nnninn in inn ini nnnn.Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn umrun 1П11111 ni nnnnnin nnninn in inn ini nnnn.Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn umni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn umni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn um
nn Nninn ni Nnnini Nnninini Nnnnnnni и nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nn- nnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn 111П1ППП1 nnn n ninuni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnnunnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—inn- ninin inn inn 1ШП1 ni inn Ninun Nnnnn nnn inn inn- nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nn- innni nnn Nninnninni, Nnini, Nnnnnn nnn nnnniinii Nnin- unn uinni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn,
nn Nninn ni Nnnini Nnninini Nnnnnnni и nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nn- nnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn 111П1ППП1 nnn n ninuni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnnunnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—inn- ninin inn inn 1ШП1 ni inn Ninun Nnnnn nnn inn inn- nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nn- innni nnn Nninnninni, Nnini, Nnnnnn nnn nnnniinii Nnin- unn uinni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn,
В
В
nn Nninn ni Nnnini Nnninnu Nnnnnnni ii nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nnnnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn inninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- unnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—innninin inn inn mini ni inn Ninun Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnnnn Nninnnn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninun. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nm-
nn Nninn ni Nnnini Nnninnu Nnnnnnni ii nni inn n nnnnnn, nni nn nnnnn—inn ininnu, nnn ninnn- nin inn inn, in nninin. Nnnnnninn 15% ni inn nnun’i innn inunnn, inn Nnnini Nninn inninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- unnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn nninnnn nnn Nnn Nnin in Nnn Ninnnnnn—innninin inn inn mini ni inn Ninun Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnnnn Nninnnn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninun. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nm-
A
A
run 1П11111 ni nnnnnin nnninn in inn ini nnnn.
Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn um
run 1П11111 ni nnnnnin nnninn in inn ini nnnn.
Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn um
ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.
Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn um
ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.
Nninin nnn nnnnn nninininn nnnn nnnnnnn inn Nnnini Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnmnnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnnunn um
Рис. 28. Этот пример основан на тексте последнего номера журнала Time, но все буквы заменены на букву ‘и’ разной ширины. Если бы текст был осмысленным, то разбиения строк в варианте В (которые выбирались согласно алгоритму Т^Х’а оптимальной верстки) казались бы менее раздражающими, чем в варианте А. Кроме того, вариант В не нуждается в пробелах между буквами.
ми почти поголовно. Например, Де Винн [11, с. 206] говорил, что разрядка недопустима, даже когда колонки настолько узкие, что в некоторых строках может поместиться только одно слово; Брюс Роже [37, с. 88] сказал, что «предпочтительно размещать все лишние пробелы между словами, пусть даже получающиеся при этом 'дыры’ режут глаз». Даже одна четвертая единичного пробела, вставленная между буквами, значительно меняет вид слов. Полиграфические правила США Congressional Record [40] гласят, что «Вообще говоря, операторы должны избегать широких пробелов. Тем не менее, разрядка слов недопустима». Алгоритм оптимальной верстки позволяет, таким образом, обращаться с существующими законами более вольно.
Идея применить динамическое программирование к разбиению строк возникла у Д. Э. Кнута в 1976 г., когда профессор Л. Смит из музыкального
nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnn, nnn ninnn- mn inn inn, in nninin. Nnnnnn- inn 15% ni inn nnnn’i innn 1П1- innn, inn Nnnmi Nninn mninnni nnn n ninnni ni nninni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnniinnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—innni- nin inn inn nnini ni inn Nin- un Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn П11- nnn ni n 19in nnninin Nninnn ninnin. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnn, nnn ninnn- mn inn inn, in nninin. Nnnnnn- inn 15% ni inn nnnn’i innn 1П1- innn, inn Nnnmi Nninn mninnni nnn n ninnni ni nninni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnniinnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—innni- nin inn inn nnini ni inn Nin- un Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn П11- nnn ni n 19in nnninin Nninnn ninnin. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.ввnn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnii, nnn ninnn- nin inn inn, in ninnin. Nnnnnninn 15% ni inn nnnn’i innn inimnn, inn Nnnmi Nninn nninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- iinnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—nnninin inn inn nnini ni inn Ninnn Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn nninnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn innn. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nni-nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnii, nnn ninnn- nin inn inn, in ninnin. Nnnnnninn 15% ni inn nnnn’i innn inimnn, inn Nnnmi Nninn nninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- iinnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—nnninin inn inn nnini ni inn Ninnn Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn nninnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn innn. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nni-AArun 1П11111 ni nnnnnin nnnnn in inn ini nnnn.Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn innrun 1П11111 ni nnnnnin nnnnn in inn ini nnnn.Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn innNninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn innNninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn inn
nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnn, nnn ninnn- mn inn inn, in nninin. Nnnnnn- inn 15% ni inn nnnn’i innn 1П1- innn, inn Nnnmi Nninn mninnni nnn n ninnni ni nninni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnniinnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—innni- nin inn inn nnini ni inn Nin- un Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn П11- nnn ni n 19in nnninin Nninnn ninnin. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.
nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnn, nnn ninnn- mn inn inn, in nninin. Nnnnnn- inn 15% ni inn nnnn’i innn 1П1- innn, inn Nnnmi Nninn mninnni nnn n ninnni ni nninni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnniinnnu nnn ninnnn nnn nnnni; nnin innn 3,000 mini— innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—innni- nin inn inn nnini ni inn Nin- un Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn niunnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn ninni. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn П11- nnn ni n 19in nnninin Nninnn ninnin. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nninin 1П11111 ni nnnnnin nnninn in inn ini nnnn.
в
в
nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnii, nnn ninnn- nin inn inn, in ninnin. Nnnnnninn 15% ni inn nnnn’i innn inimnn, inn Nnnmi Nninn nninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- iinnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—nnninin inn inn nnini ni inn Ninnn Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn nninnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn innn. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nni-
nn Nninn ni Nnnmi Nnninnii Nnnnnnni 11 nni inn n nnnnnn, nni nn nnnnn—inn ininnii, nnn ninnn- nin inn inn, in ninnin. Nnnnnninn 15% ni inn nnnn’i innn inimnn, inn Nnnmi Nninn nninnni nnn n ninnni ni ninnni nninnini in Nnnnin Nninnn in inn Nnnnn Nnnn nn inn nn ni Nininn, nninn inn nnn- iinnnn nnn ninnnn nnn nnnni; nnin innn 3,000 mini—innnnin inn ninnnnn nnn Nnn Nnin in Nnn Ninnnnnn—nnninin inn inn nnini ni inn Ninnn Nnnnn nnn inn inn-nninnnn Nnin Nnn Nninn. Nnn 262.4 nnnnn nninnni ni inn N.N.N.N. nninnn in nnin innn 100 ninnin ninnni nnn ninin nninnni nnn Nninnninni, Nnini, Nnnnnn nnn nnnnunn Nniniinn innn. Nnnn nnnninnnni ninnnnni in innn, in Nninnn, nnnni inn inninnn nnnnn ni n 19in nnninin Nninnn ninnni. Nnnn, nnnnnni, innnn in nnin innn 100 innnnni nnn nnnnin Nnnnnnn, Nnn, Nnnnnn, Nnnn, ni inn nni-
A
A
run 1П11111 ni nnnnnin nnnnn in inn ini nnnn.
Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn inn
run 1П11111 ni nnnnnin nnnnn in inn ini nnnn.
Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn inn
Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn inn
Nninin nnn nnnnn nnininuin nnnn nnnnnnn inn Nnnmi Nninn nnn nnnnn nnn nnnni. Nnn ninmninni nnninnnnnnu ni inn N.N.N.N. in nininn, nniinnnnin nnn nnninn ninnnniinn inn
Рис. 29. В представленной на рис. 28 «проблеме журнала Time» Т^Х ведет себя даже лучше, когда позволено выбирать оптимальное число строк. Вариант В на рис. 28 произведен с was produced with параметром looseness 1, чтобы вынудить первый абзац закончиться точно по нижнему краю прямоугольного рисунка. При параметре looseness 0, текст в узкой колонке лучше подогнан.
отделения Станфордского университета задал вопрос о верстке музыкальных партитур. В течение последующего обсуждения этой проблемы со студентами на семинаре (см. Клэнси и Кнут [8]), кто-то заметил, что в сущности одна и та же идея может быть применена как к словам абзаца, так и к музыкальным тактам. Модель бокс/клей/штраф была разработана Кнутом в апреле 1977 г., когда планировался первоначальный проект TgX’a, хотя тогда он еще не знал, достаточно ли эффективным будет общий алгоритм оптимизации на практике. Кнут оставался в счастливом неведении относительно неудачных экспериментов Купера с динамическим программированием, иначе он бы мог отказаться от самой идеи прежде чем заняться ею.
Летом 1977 г. М. Ф. Плэсс ввел в исходный алгоритм Кнута понятие допустимых точек разбиения, чтобы ограничить число активных возможностей
и все еще искать оптимальное решение, при том что оптимум мог быть чрезвычайно плохим. Этот алгоритм был применен в первой полной версии Т^Х’а (март 1978 г.) и проявил себя хорошо. Неожиданная мощь модели бокс/клей/ штраф постепенно прояснялась в течение двух следующих лет экспериментов с ТеХ’ом; и, когда было обнаружено несколько беспорядочное использование отрицательных параметров (как в Паскале и примерах из Math Reviews на рис. 10 и 11), авторы должны были выискивать коварные ошибки в исходной разработке.
Наконец, стало необходимо добавить больше возможностей к процедуре TgX’a разбиения строк, в частности, возможность более гибко менять длину строк, чем просто навешивая отступы. В этом смысле в 1978 г. стал очевиден более глубокий дефект, именно, поддержание одного наиболее активного узла для каждой точки разбиения независимо от того факта, что единственная точка разбиения может оказаться допустимой в разных строках; это означает, что алгоритм может пропустить допустимый способ верстки абзаца при наличии достаточно длинного отступа. Затем весной 1980 г. был разработан новый алгоритм, заменивший предыдущий Т^Х’овский метод. В то же время были введены усовершенствования, касающиеся коэффициента looseness и несовпадения смежных строк, так что ТЁК теперь использует, в сущности, алгоритм оптимальной верстки, который мы подробно обсуждали выше.
Проблемы и усовершенствования
Одно неприятное ограничение в TgX’e еЩе остается, хотя это не свойственно модели бокс/клей/штраф: когда разбиение возникает в середине лигатуры (например, если ‘efficient’ превращается в ‘ef-ficient’), вычисление ширины литеры усложняется. Мы должны учитывать не только тот факт, что знак переноса имеет некую длину, но и то, что буква ‘Г перед ‘fi’ шире, чем та, что входит в лигатуру ‘ffi’. Та же проблема возникает, когда верстается немецкий текст, в котором некоторые сложные слова, будучи перенесенными, меняют свое написание (например, Ъаскеп’ превращается в Ъак-кеп’, a ‘Bettuch’ — в ‘Bett-tuch’). В настоящее время (в 1980 г.) ТЁХ не предоставляет разных вариантов написания слов, имеется только возможность вставить знак переноса между другими неизменяемыми литерами. В редких случаях, когда нельзя получить более сложных разбиений строк, следует прибегнуть к непосредственному вмешательству человека.
Интересно разобраться, как обобщить алгоритм оптимальной верстки, чтобы он мог обрабатывать случаи, подобные опущенным буквам m и п на рис. 22. Функция плохости строки могла бы тогда зависеть не только от таких естественных параметров, как длина, растяжение и сжатие; это могло бы также зависеть и от количества букв ш и п в этой строке. Аналогичная техника была использована при наборе библейских текстов на древнееврейском, которые никогда не переносили: для священных текстов в древнееврейском шрифте имелись широкие варианты некоторых букв, так что отдельные буквы в строке можно было заменить их более широкими эквивалентами, чтобы избежать
широких пробелов между словами. Например, кроме обычного имеется сверхширокий алеф. Соответствующая функция плохости для строк такого абзаца должна была учитывать число представленных в них букв двойной ширины.
Одна из серьезных непредвиденных проблем, возникших в Т£Х’овской процедуре разбиения строк, заключалась в том, что при первоначальных ее применениях для вычисления плохости, дефектности и т. п. использовалась арифметика с плавающей точкой. Это приводило к тому, что на разных вычислительных платформах получались разные результаты, поскольку, во-первых, в существующем оборудовании, основанном на арифметике с плавающей точкой, имеется очень много различий и, во-вторых, зачастую есть две возможности выбрать точку разбиения с почти одинаковой общей дефектностью. Важно иметь гарантию того, что все версии ТкХ’а верстают абзацы идентично, потому что это очень существенно при редактировании, внесении исправлений и печати на разных устройствах. Таким образом, в «стандартной» версии TgX’a, которая должна появиться в 1982 г., во всех вычислениях будет применяться арифметика с фиксированной точкой.
В книгах по издательскому делу часто обсуждается вопрос, который является наиболее серьезным следствием жидких строк: это коридоры из белого пространства, похожие на реки или ползущих ящериц. Столь безобразные создания, которые прорезают последовательности строк и раздражают читателя, не могут быть удалены простым эффективным методом типа динамического программирования. Однако, к счастью, при использовании алгоритма оптимальной верстки такая проблема почти никогда не возникает, потому что компьютер, вообще говоря, способен сверстать абзац с достаточно плотными пробелами. Коридоры начинают превалировать только тогда, когда по некоторым причинам должен быть установлен более высокий порог толерантности р, как, например, на рис. 7, где выравнивается необычно узкая колонка. Другая причина появления коридоров может возникнуть, когда текст абзаца распадается на чисто механические элементы, например, при перечислении в газете многочисленных гостей торжественного мероприятия. Многие эксперименты, проведенные с Т^Х’ом, показали, однако, что после использования алгоритма оптимальной верстки не потребуется удаления коридоров вручную.
Модель бокс/клей/штраф, примененная по вертикали, столь же успешна, как и при применении по горизонтали, поэтому Т^Х способен принимать весьма разумные решения относительно того, где начинать каждую новую страницу. Приемы для получения таких вещей, как равнение по правому краю, уже обсуждавшиеся выше, аналогичны приемам для получения таких вещей, как верстка с «равнением по нижнему краю». Однако в текущей версии Т^Х хранит в памяти каждую страницу до тех пор, пока ее не распечатает, так что Т^Х не может хранить весь документ и находить строго оптимальное разбиение на страницы, используя тот же алгоритм, что и для разбиения строк. Следовательно, алгоритм оптимальной верстки используется для вывода по одной странице. В настоящее время продолжаются опыты с версией ТЁХ’а, обнаруживающей глобальный оптимум разбиения страниц за два прохода. Эти экспериментальные системы позволяют также располагать рисунки как можно
ближе к тому месту, где имеется упоминание о них, принимая также во внимание тот факт, что некоторые страницы могут образовывать «разворот». Многие из этих вопросов решаются обобщением метода динамического программирования и использованием модели бокс/клей/штраф, описанных в этой статье, но для решения других проблем должна быть показана их NP-полнота [35].
Приложение: алгоритм расщепления на части
Во многих приложениях разбиения строк (например, в текстовых процессорах или в газетах) нет никакой необходимости автоматизировать общий алгоритм оптимизации, описанный выше, поэтому есть возможность значительно упростить общую процедуру, одновременно экономя требуемые пространство и время. При этом мы должны будем упростить спецификации задачи и установить более низкий порог толерантности, чем в оптимальном случае, когда необходим перенос. Представленная ниже программа «почти оптимальной верстки» достаточно хорошо справляется с разбиением строк на рис. 3 или рис. 4(c), но более сложные примеры нс обрабатывает. Точнее говоря, в программе расщепления на части предполагается следующее:
Вместо общей модели бокс/клей/штраф ввод снабжается спецификацией в виде последовательности wi ... wn неотрицательных ширин боксов, представляющих слова абзаца с присоединенными к ним знаками препинания, а также последовательностью малых целых чисел gi... дт которые соответствуют виду междусловного интервала. Например, ^ = 1 означает обычный междусловный интервал, следующий за боксом ширины w^ тогда как дк = 2 означает отсутствие пробела, когда бокс к заканчивается явным переносом, а дк = 3 отвечает боксу к в конце абзаца. Иной тип кодов может быть использован после знаков препинания. Каждому типу соответствует тройка неотрицательных чисел (хд^Уд, zgY означающих нормальный пробел, растяжение и сжатие того или иного типа пробела. Например, если типы 1, 2 и 3 используются в только что определенном виде, мы можем иметь
(a:i, 2/ь ^) = (6,3,2) (^2, 2/2, *2) = (0, 0, 0)(жз, 2/з, ^з) = (0, оо, 0)(a:i, 2/ь ^) = (6,3,2) (^2, 2/2, *2) = (0, 0, 0)(жз, 2/з, ^з) = (0, оо, 0)между словами
(a:i, 2/ь ^) = (6,3,2) (^2, 2/2, *2) = (0, 0, 0)
(жз, 2/з, ^з) = (0, оо, 0)
(a:i, 2/ь ^) = (6,3,2) (^2, 2/2, *2) = (0, 0, 0)
(жз, 2/з, ^з) = (0, оо, 0)
после явных переносов или дефисов для заполнения завершающей строки
в единицах измерения ^еш, где оо означает некоторое большое число. Ширина w первого бокса должна состоять из пробела, необходимого для отступа в начале абзаца; таким образом, фрагмент сказки братьев Гримм на рис. 1 был бы представлен так:
wi,...,wn = 34,42,..., 24,39,30,..., 60,80
дъ...,дп= 1, 1,..., 1, 2, 1,..., 1, 3
что соответствует при использовании ширин из табл. 1
‘□In’, ‘olden’,..., ‘old’, ‘lime-’, ‘tree’,..., ‘favorite’, ‘plaything.’
Общие последовательности wi ... wn и д\... дп, описывающие вывод, можно выразить в модели бокс/клей/штраф посредством эквивалентной спецификации
6okc(wi) клей^,?/^,^) ... 6oKc(wn) клей(^п, ygni z9n\
за которой следует ‘штраф(0, —оо,0)’ для завершающей строки абзаца.
Все строки должны иметь одинаковую ширину I и каждое Wk должно быть меньше L
никакое слово не должно быть перенесено, если только нет способа сверстать абзац, не нарушая минимальных и максимальных ограничений на ширину пробела. Минимум для пробелов типа д равен.
zg ™ жд — %gi а максимум —
Уд = Хд + РУд , где р есть положительный коэффициент толерантности, который может быть изменен пользователем. Например, если р = 2, то максимум для пробела типа д равен хд + 2уд1 т. е. нормальный пробел плюс удвоенное растяжение.
Перенос осуществляется только в точке, где допустимое разбиение строки становится невозможным, даже если лучше было бы сделать перенос в одном из предыдущих слов. Таким образом, общий алгоритм оптимальной верстки текста будет давать значительно лучшие результаты, кода требуется вывод высокого качества и часто нужны переносы.
Не назначается штраф за плотную строку, следующую за жидкой строкой, или за несколько переносов подряд, и алгоритм не производит абзацев длиннее или короче оптимальной длины (иначе говоря, в общем алгоритме а = ^ = q = 0).
При этих ограничениях оптимальные точки разбиения могут быть найдены весьма эффективно.*
Почти оптимальный алгоритм работает с двумя массивами:
SqS} . . . Syi-i-i , где sк обозначает минимальную сумму дефектностей, при которой происходит разбиение после бокса к, либо Sk = оо, если здесь нет допустимого способа разбиения; и
Pi ...Рп+1,
где рк значимо, только если Sk < оо, и тогда лучший вариант завершить строку на боксе к состоит в том, чтобы начать ее с бокса рк 4-1. Предположим также, что
Wn+1 = о;
последнее представляет собой невидимый бокс в конце абзаца в завершающей его строке.
Помимо 4п + 4 ячеек памяти для массивов wi... wn+i, д±... дп, sq ... sn+i и р1...рпАл и памяти, необходимой для поддержания параметров Z, р и ^хд, у^ zfg^ для каждого типа д, алгоритму расщепления на части нужно всего несколько разных переменных:
а = начало абзаца (обычно 0, после переноса меняется);
к = текущая рассматриваемая точка;
3 = точка разбиения, рассматриваемая как предшественник точки к;
г = самая левая точка разбиения, которая допускается как предшественник точки к;
т = число активных точек разбиения (т. е. индексы j > г при Sj < оо);
S = нормальная ширина строки от г до к;
Smax = максимально допустимая ширина строки от г до к;
Smin = минимально допустимая ширина строки от г до к;
S' = нормальная ширина строки от j до к;
^тах = максимально допустимая ширина строки от j до к;
^min = минимально допустимая ширина строки от j до к;
г = коэффициент корректировки от j до к;
d = общая дефектность от а до • • • до j до к;
d' = минимальная известная общая дефектность от а до • • • до к;
з' = предшественник точки к, приводящий к общей дефектности d', если d' < оо.
Все эти переменные суть целые числа, за исключением г, которое представляет собой дробь в диапазоне — 1 < г < р. Читатель может проверить обоснованность алгоритма, убедившись в том, что установленные интерпретации переменных при выполнении программы остаются инвариантными относительно ключевых точек.
Теперь приведем программу всю целиком:
а := 0;
1 loop: г := а; з^ := 0; т := 1; к := г + 1;
S :— Smax :— Smin :— w^;
loop: while Smjn > I do (Продвижение по г через 1);
(Рассмотрение всех допустимых строк, заканчивающихся в к\, Sk := d'* if d' < оо then
begin m := m + 1; pk := /;
end;
if m = 0 or к > n then exit loop;
S := S + wa;+i +^J
^max '= ^max 4~ ^/c+l 4“ Удк\ ^тт •“ ^min 4“ ^/c+1 4“ ^gk 5
к := к 4-1;
repeat;
if к > n then
begin output(a, n + 1); exit loop;
end
else begin (Попытка перенести бокс к^ затем вывод от а до этого разбиения);
а := к — 1;
end;
repeat.
Операция (Продвижение по г через 1) выполняется, только когда Sm-m > I, и это не может произойти при к = г + 1, поскольку в этом случае Smjn = W < I. Следовательно, весь цикл завершен; имеем
(Продвижение по г через 1) =
begin if st < оо then т := т — 1;
г := г 4-1;
S := S — Wi — xgi\
^max •“ ^max ^г Уди ^min •“ Sm'm ^г ^дй end.
Внутренний цикл почти оптимальной программы проще и быстрее, чем соответствующий цикл общего алгоритма оптимальной верстки, потому что он не рассматривает активные точки вблизи к^ а рассматривает только те, которые отстоят приблизительно на ширину одной строки:
(Рассмотрение всех допустимых строк, заканчивающихся в к^ = begin j := г; S' := S; S^ax *= ^maxi ^min *= ^mini ^ •— OO> while S^ax ^ ^ do
begin if Sj < oo then
(Рассмотрение разбиения от а до • • • до j до /с);
j := j + 1;
S' := S' - Wj - xgj;
^max * ^max “ ^3 Удз' ^min ‘ ^min “ ^3 Zgj^ end.
Снова можем заключить, что весь цикл должен окончиться, поскольку он не должен выполняться при к = j + 1. Легко воспроизвести самый внутренний цикл:
(Рассмотрение разбиения от а до • • • до j до /с) = begin if S' < I then r := p • (Z - S')/(S^ax - S') else if S' > Z then r := (Z - S')/(S' - S^J else r := 0;
с?:=5, + (1 + 100|г13)2;
if d < d/ then
begin d' := d; / := j;
end;
end.
Когда необходим перенос, алгоритм впадает в паническое состояние, сначала отыскивая последнее допустимое значение г, а затем пытаясь расколоть
слово к. К этому моменту вся строка от г до к — 1 слишком коротка, а от г до к слишком длинна, так что есть надежда, что перенос будет сделан.
(Попытка перенести бокс к, затем вывод от а до этой точки разбиения) = begin loop: S :— S + Wi + xgi;
^max •— ^max "Г ^г "Г Уд^ ^min •“ ^min 4“ ^г 4“ ^i > г := г — 1;
if st < oo then exit loop;
repeat;
output (а, г^
(Раскалывание бокса к в наилучшем месте);
(Вывод строки до наилучшего раскалывания и корректировка Шк для продолжения);
end.
Предположим, что имеется hk способов расколоть бокс к на два куска, где ширины таких кусков при j-om расколе равны соответственно иУ^ и w^-; здесь в w^- включена ширина знака переноса. Предполагается, что дополнительный алгоритм переносов способен вычислять hk и ширины этих кусков, если требуется; этот алгоритм вызывается только тогда, когда мы достигаем программы (Раскалывание бокса к в наилучшем месте). Если перенос не нужен, можем просто положить hk = 0 и приведенная ниже программа значительно упрощается. Имеется hk 4-1 вариантов, включая отсутствие раскалывания вообще, и выбор из них происходит следующим образом:.
(Раскалывание бокса к в наилучшем месте) =
begin (Вызов алгоритма переносов для вычисления hk и ширин кусков);
З' := 0; d' := оо;
for з := 1 to hk do if Emin + wkj — wk<l then , begin S' := S + w'kj - wk;
if S' < I
then d := lOOOOp ■ (Z - S')/ (100(Smax - S) + 1);
else d := 10000 • (S' - Z)/ (100(S - Smin) + 1);
if d < df then
begin d' := d; jf := j;
end;
end;
end.
Окончательная операция (Вывод строки до наилучшего раскалывания и корректировка Wk для продолжения) здесь будет только неформально очерчена, поскольку не хочется вводить массу дополнительных обозначений для объяснения такого элементарного понятия. Если з* ^ 0, т. е. нужен перенос, программа выводит строку от бокса г + 1 до бокса к включительно, но при этом бокс к заменяется куском со знаком переноса ширины w^-,; затем Wk заменяется шириной другого фрагмента, именно w^-,. В другом случае, ко-
гда У = 0, программа просто выводит строку от бокса г + 1 до бокса к — 1 включительно.
Наконец, еще один жидкий конец нужно уплотнить: процедура output^a, i) просто проходит через таблицу р, определяющую лучшие разбиения строки от а до г и формирует соответствующие строки. Один из способов (не требующий дополнительного пространства памяти) сделать это состоит в обращении соответствующих элементов p-таблицы таким образом, чтобы они указывали на последователей, а не на предшественников:
procedure output (integer а, г) =
begin integer q, r, s; q := г; s := 0;
while q ^ a do
begin r := pq; pq := s; s := q; q := 71;
end;
while q ^ i do
begin (Вывод строки от бокса q + 1 до бокса s включительно);
q := <s; s := pq;
end;
end.
Для применения этого алгоритма на практике мы располагаем лишь ограниченным объемом памяти, а произвольно длинные абзацы можно обрабатывать, только если сделать незначительное изменение, предложенное Купером [9]: когда число слов в данном абзаце превышает некое максимальное число nmaX} применять метод к первым nmax словам; затем вывести все, кроме завершающей строки, и возобновить применение метода, начиная с копии, перенесенной из той строки, что не была выведена.
Благодарности
Мы весьма признательны Барбаре Битон из Американского математического общества за многочисленные обсуждения «реальных» приложений; мы очень благодарны Джеймсу Иву из Университета Ньюкасл-он-Тайн и Нейлу Вайсману из Кембриджского университета за помощь в получении литературы, недоступной в Калифорнии, а также библиотекарям отделов редких книг Колумбийского и Станфордского университетов за разрешение ознакомиться с многоязычными Библиями и сфотографировать отдельные фрагменты из них.
Это исследование частично финансировалось Национальным научным фондом по грантам IST-7921977 и MCS-7723738, управлением морских исследований по гранту N00014-76- С-0330, корпорацией IBM и издательством Эддисон-Уэсли.
Литература
Benedictus Arias Montanus, editor, Biblia Sacra Hebraice, Chaldaice, Graece, & Latine (Antwerp: Christoph. Plantinus, 1569-1573).
G. P. Bafour, A. R. Blanchard, and F. H. Raymond, “Automatic Composing Machine,” U.S. Patent 2762485 (11 September 1956). (См. также британский патент 771551 и французский патент 1103000.)
G. Bafour, “A new method for text composition — The BBR System,” Printing Technology 5,2 (1961), 65-75.
Michael P. Barnett, Computer Typesetting: Experiments and Prospects (Cambridge, Massachusetts: M.I.T. Press, 1965).
Samuel A. Bartels, The Art of Spacing (Chicago: The Inland Printer, 1926).
Richard Bellman, Dynamic Programming (Princeton, New Jersey: Princeton University Press, 1957).
D. G. Berri, The Art of Printing (London: 1864).
Michael J. Clancy and Donald E. Knuth, “A programming and problem-solving seminar,” report STAN-CS-77-606, Computer Science Department, Stanford University (April 1977), 85-88.
P. I. Cooper, “The influence of program parameters on hyphenation frequency in a sophisticated justification program,” Advances in Computer Typesetting (London: The Institute of Printing, 1967), 176-178, 211-212.
T. H. Darlow and H. F. Moule, Historical Catalogue of the Printed Editions of Holy Scripture in the Library of the British and Foreign Bible Society (London: The Bible House, 1911).
Theodore Low De Vinne, Correct Composition, Volume 2 of The Practice of Typography (New York: Century, 1901).
James L. Dolby, “Theme C: Software and hardware,” в буклете с аннотациями, распространяемом 18 июля 1966 г. на закрытии International Computer Typesetting Conference,.University of Sussex (London: The Institute of Printing, 1966). Несколько более доброжелательный отзыв Долби о работе Купера содержится в трудах конференции, опубликованных в следующем году: см. Advances in Computer Typesetting (London: The Institute of Printing, 1967), 292.
C. J. Duncan, J. Eve, L. Molyneux, E. S. Page, and Margaret G. Robson, “Computer typesetting: An evaluation of the problems,” Printing Technology 7 (1963), 133-151.
C. J. Duncan, “Look! No hands!” The Penrose Annual 57 (1964), 121-168.
A. Frey, Manuel Nouveau de Typographic (Paris: 1835), 2 volumes.
Michael R. Garey and David S. Johnson, Computers and Intractability (San Francisco: W. H. Freeman, 1979).
Aug. Giustiniani, Psalterium (Genoa: 1516).
Jakob Ludwig Karl Grimm and Wilhelm Karl Grimm, “Der Froschkonig (The Frog King),” в Kinder- und Hausmarchen (Berlin: 1912). Относительно истории этой сказки см. Heinz Rolleke, Die Alteste Marchensammlung der Bruder Grimm (Cologny-Geneve: Fondation Martin Bodmer, 1975), 144-153.
Basil Hall, The Great Polyglot Bibles (San Francisco: The Book Club of California, 1966).
M. Held and R. M. Karp, “The construction of discrete dynamic programming algorithms,” IBM Systems Journal 4 (1965), 136-147.
Walter E. Houghton, Jr., “The history of trades: Its relation to seventeenth century thought,” в Roots of Scientific Thought, edited by Philip P. Wiener and Aaron Noland (New York: Basic Books, 1957), 354-381.
Information International, Inc., PAGE-3 Composition Language, распространяется по частным каналам. Первое издание: October 31, 1975; второе издание: October 20, 1976. Этот язык иногда называли “PAGE-IH” по аббревиатуре компании, его создавшей.
Kathleen Jensen and Niklaus Wirth, PASCAL User Manual and Report, second edition (Heidelberg: Springer-Verlag, 1975).
Francisco Jimenez de Cisneros, sponsor, Uetus testamentum multiplici lingua nunc primo impressum (Alcala de Henares: Industria Arnaldi Guillelmi de Brocario in Academia Complutensi, 1522). Издание было готово в 1517 г., но из-за отсутствия разрешения Папы выход в свет задержался на несколько лет.
Paul Е. Justus, “There is more to typesetting than setting type,” IEEE Transactions on Professional Communication PC-15 (1972), 13-16, 18.
Donald E. Knuth, T^K and METAFONT: New Directions in Typesetting (Bedford, Massachusetts: Digital Press and American Mathematical Society, 1979).
Donald E. Knuth, “BLAISE, a preprocessor for PASCAL,” файл BLAISE.DEK[UP,DOC] в SU-AI в сети ARPA network (March 1979). Сама программа находится в файле BLAISE. SAI [ТЕХ,DEK].
Donald E. Knuth, Seminumerical Algorithms, Volume 2 of The Art of Computer Programming, second edition (Reading, Massachusetts: Addison- Wesley, 1981).[Имеется русский перевод первого издания: Кнут Д. Искусство программирования для ЭВМ. Т. 2. Получисленные алгоритмы.—М.: Мир, 1977; второго издания: Кнут Д. Е. Искусство программирования. Т. 2. Получисленные алгоритмы.— Киев: Диалектика, 1999.]
Donald Е. Knuth, ТЁК: The Program, Volume В of Computers & Typesetting (Reading, Massachusetts: Addison-Wesley, 1986).
Joseph Moxon, Mechanick Exercises: Or, the Doctrine of Handy-Works. Applied to the Art of Printing (London: J. Moxon, 1683-1684). Воспроизведено в Typothetae, New York, 1896, с замечаниями T. L. De Vinne; также воспроизведено в Oxford University Press, 1958, с замечаниями Herbert Davis
и Harry Carter; но эта перепечатка не сохранила в полной мере дух оригинала, представляющего собой великолепный образец мастерства 17-го столетия.
Joseph F. Ossanna, Nroff/Troff User’s Manual. Bell Telephone Laboratories internal memorandum (Murray Hill, New Jersey: 1974). Переработанная версия в UNIX Programmer’s Manual 2, Section 22 (January 1979).
Herman D. Parks, “Computerized processing of editorial copy,” Advances in Computer Typesetting (London: The Institute of Printing, 1967), 119-121, 157-158.
Herman Parks, выступления при обсуждении, Proceedings of the ASIS Workshop on Computer Composition (American Society for Information Science, 1971), 143-145, 151, 180-182.
John Pierson, Computer Composition Using PAGE-1 (New York: Wiley- Interscience, 1972).
Michael F. Plass, Optimal Pagination Techniques for Automatic Typesetting Systems, Ph.D. thesis, Stanford University (1981). Опубликовано также как Xerox Palo Alto Research Center report ISL-81-1 (Palo Alto, California: August 1981).
Alison M. Pringle, “Justification with fewer hyphens,” The Computer Journal 24 (1981), 320-323.
Bruce Rogers, Paragraphs on Printing (New York: William E. Rudge’s Sons, 1943).
Hanan Samet, “Heuristics for the строка division problem in computer justified text,” Communications of the ACM 25 (1982), 564-571.
George Bernard Shaw, “On modern typography,” The Dolphin 4 (1940), 80-81.
U.S. Government Printing Office, Style Manual (Washington, D.C.: 1973). Цитата взята из правила 22.
Brianus Waltonus, editor, Biblia Sacra Polyglotta (London: Thomas Roycroft, 1657).
David Wolder, Biblia Sacra Graece, Latine &; Germanice (Hamburg: Jacobus Lucius Juni., 1596).'
Добавление
Майкл Плэсс подготовил короткую версию этой статьи для книги Document Preparation Systems под редакцией Jurg Nievergelt, Giovanni Coray, Jean-Daniel Nicoud, and Alan C. Shaw (Amsterdam: North-Holland, 1982), 221-242. В его версии модель бокс/клей/штраф была обобщена и упрощена посредством введения понятия зарубка, которая состоит из трех последовательностей из боксов и клея для вариантов до разбиения, после разбиения и отсутствие разбиения
вместе со штрафом р и флагом /; точки разбиения появляются только в зарубках.
В стандартной версии 'ТЁХ’а, появившейся спустя примерно два года после написания статьи, модель разбиения строк обобщается иным образом: вводится клей leftskip и rightskip слева и справа от каждой строки. Это упрощает многие конструкции, такие, как ровнение по правому краю и ровнение по центру. Теперь ТЁК использует несколько улучшенную формулу для дефектностей,
именно:именно:5j = <5j = <(Z + PjV + 7Г? + Otj , (Z + /З,)2 - 7Г? + Otj , (l + ^+aj,(Z + PjV + 7Г? + Otj , (Z + /З,)2 - 7Г? + Otj , (l + ^+aj,если 7Tj > 0;если —оо < Hj < 0;если iTj = — оо;если 7Tj > 0;если —оо < Hj < 0;если iTj = — оо;
именно:
именно:
5j = <
5j = <
(Z + PjV + 7Г? + Otj , (Z + /З,)2 - 7Г? + Otj , (l + ^+aj,
(Z + PjV + 7Г? + Otj , (Z + /З,)2 - 7Г? + Otj , (l + ^+aj,
если 7Tj > 0;
если —оо < Hj < 0;
если iTj = — оо;
если 7Tj > 0;
если —оо < Hj < 0;
если iTj = — оо;
здесь I есть параметр, называемый штраф строки, обычно равный 10. См. D. Е. Knuth, Literate Programming (1991), 272-274, относительно того, почему было важно заменить (I + Pj + тг^)2 на (Z + Р^У + тг2 в случае Hj > 0.
Люди, активно пользующиеся ТЁХ’ом, должно быть знают, что «бесконечный» штраф теперь полагается равным 10000 вместо 1000 и что клей ТЁХ’а теперь имеет три уровня бесконечности, называемые fil, fill и filll. В версии Т^Х’а 3.0 был введен необязательный третий проход алгоритма разбиения строк, если пользователь указывал emergency stretch (непредвиденное растягивание) [см. изменение 885 в протоколе ошибок ТЁ%’а Literate Programming (1992), 338]. Если нет допустимого способа набрать абзац с коэффициентами толерантности ру или р^, экстренное растягивание добавляется к растяжению всех строк, когда вычисляются коэффициенты плохости и дефектности; это предоставляет ТЁХ’у вполне приличный способ различать, в какой степени неудачны чрезвычайно плохие разбиения в наиболее сложных случаях.
Сочетание текстов, читаемых справа налево и слева направо
[Написано в соавторстве с Пьером Маккеем. Первоначально опубликовано в TUGboat 8 (1987), 14-25.]
ТЁК создавался для печати документов, читаемых слева направо и сверху вниз, в соответствии с правилами английского и других западных языков. Если такие документы повернуть на 90°, их можно также читать сверху вниз и справа налево, как в Японии. Поворот еще на 90° или 180° даст документы, читаемые справа налево и снизу вверх или снизу вверх и слева направо, если это зачем-то понадобится. Как бы то ни было, Т^Х сам по себе не приспособлен для языков типа арабского или иврита, которые читаются справа налево и сверху вниз.
Несложно использовать ТЁК для печати документов полностью на арабском языке или полностью на иврите, просто выдавая зеркальное отражение любого требуемого текста, Растровое печатающее устройство легко может быть запрограммировано так, чтобы отражать биты справа налево при выводе их на страницы. (Иногда это называют «режимом футболки», потому что его можно использовать для нанесения надписей на футболку путем аппликации на материю английского текста, напечатанного в зеркальном отражении.)
Сложности, тем не менее, возникают, когда правила чтения слева направо и справа налево соединяются внутри одного документа. Возьмите арабско- английский словарь, или комментарии Библии с цитатами на иврите, или ближневосточную энциклопедию, в которой западные имена пишутся латинскими буквами; такие документы, и многие другие, должны читаться обоими способами.
Цель этой статьи — прояснить вопросы, связанные с печатью смешанных документов, рассматривая их с точки зрения западного автора, или читателя, или создателя программного обеспечения. Мы также рассмотрим изменения ТЁХ’а, превращающие его в двунаправленную форматирующую систему.
Термины и правила
Для удобства будем говорить, что L-текст — это текстовой материал, который следует читать слева направо, а Л-текст — текстовой материал, который следует читать справа налево. Точно так же можно сказать, что английский и испанский — L-языки, в то время как арабский язык и иврит — R-языки.
Чтобы сделать эту статью понятной для европейца, не знакомого с R- языками, мы будем использовать «перевернутый английский», т. е. riailgnS, в качестве R-языка. Все тексты на перевернутом английском языке будут набираться шрифтом ЬэЬпэ^хЭЕ ЫоЯ птэЬоМ loinqmoD, представляющим собой перевернутую версию шрифта Computer Modern Bold Extended [широкого полужирного Computer Modern]. Для перевода с английского на riailgnSE и обратно требуется всего лишь обратить порядок чтения. И English, и riailgnS произносятся одинаково, только НгИзпЭЕ нужно произносить громче и/или ниже тоном так, чтобы слушатель мог их различать.
Простейший случай
Нетрудно набирать одиночные слова R-языка в L-текстовом документе. T£X будет прекрасно работать, если вам не придется иметь дело с R-текстами длиной более одного слова; все, что требуется сделать, —это написать макро, переворачивающий отдельные слова.
Предположим, мы хотим набрать ‘the I English I script’ [английский шрифт], чтобы Т^Х напечатал ‘the riailgnSE script’. Все, что нам нужно, —это шрифт для riailgnS, называемый, скажем, xbmclO, и следующие макро:
\font\rеvrm=xbmc10\hyphenchar\rеvrm=-1
\catcode‘\I=\active
\defI#1|«\revrm\reflect#l\empty\tcelfer}}
\def\reflect#l#2\tcelfer{\ifx#l\empty
\else\reflect#2\tcelfer#l\fi}
(Символы xbmclO можно получить аналогично символам cmbxlO, если добавить еще одно предложение METAFONT’a
extra_endchar := extra_endchar & "currentpicture:=currentpicture
reflectedabout((.5[l,r],0),(.5[l,r],1));"
в файл параметров. Оба шрифта имеют одинаковую ширину символов, но таблицы лигатур-кернов у них разные; к примеру, ‘i’, за которой следует ‘1’ дает ‘й’.)
Тексты с чередованием
Но этот простой метод не работает, когда внутрь L-текста вставляются R-фразы из многих слов, например гэгвтНд ixsi-Я biowiilum, из-за возможных переводов строки, например 1о эгпбээс! гЯвэтс! эш11о Yiilidiggoq orlt. Возьмем, к примеру, следующий абзац и посмотрим, как его можно напечатать:
Leonardo da Vinci made a sweeping statement
in his notebooks: |(<Let no one
who is not a mathematician read my works.’’I
In fact, he said it twice, so he probably meant it.
[У Леонардо да Винчи в его записях есть одно безапелляционное высказывание: «Пусть никто, кроме математиков, не читает моих работ». На самом деле, он сказал это дважды, так что, вероятно, говорил всерьез.]
Вот примеры правильной распечатки этого абзаца, отличающиеся шириной колонки:
Leonardo da Vinci made a sweeping statement in his
notebooks: пб1эНбшэгНбш б ion ai oriw эпо on ioJ^
^.a^iow уш Ьбэт In fact, he said it twice, so he probably meant it.
Leonardo da Vinci made a sweeping statement in his notebooks: ioJ^ -iismoriism б ion ai oriw эпо on “.ariiow уш Ьбэт пб!э In fact, he said it twice, so he probably meant it.
Обратите внимание, что R-текст в каждой строке перевернут; в частности, знак переноса, добавленный справа от R-фрагмента, появится слева от него.
Как заставить Т^Х сделать это? Вероятно, лучше всего расширить программы драйверов, выводящих на печать DVI-файлы, производимые ТёХ’ом, а не пускаться на всевозможные ухищрения с макрокомандами Т£Х’а. Тогда самому ТЁХ’у нужно просто вставлять в конечные DVI-файлы специальные коды, указывающие DVI-IVQ-драйверам, что делать.
Например, одна из почти работающих идей состоит в том, чтобы вставлять ‘\special{R}’ непосредственно перед началом R-текста и ‘\special{L}’ сразу после его окончания. Другими словами, мы можем переписать макро ‘ I ’ из предыдущего примера в следующей простой форме:
\defI#11{{\revrm\special{R}#l\special{L}}}
что на самом деле не переворачивает символы; мы можем также оставить параметру ‘\hyphenchar’ в шрифте \revrm его обычное значение, так что R- тексты будут переноситься. Разбивка на строки будет происходить как обычно, а программа DVI-IVQ-драйвера возьмет на себя переворот каждого фрагмента, который она обнаружит между R и L.
Переворачивать можно произвольные комбинации символов, линеек, акцентов, кернов и т. д.; к примеру, R-текст может быть г^вряв-й [перевернутым французским] или даже быть про XgfT!
Один из путей реализации
Для того чтобы понять, как DVI-IVQ-программы должны выполнять поставленную задачу, нужно посмотреть, какую информацию Т^Х записывает в DVI- файл. Основная идея заключается в том, что каждый раз, когда Т^Х выдает горизонтальный или вертикальный бокс (hbox или vbox), в DVI-файл записывается команда 1ризК\ за которой следуют различные команды для печати содержимого бокса (box), а затем команда ‘рор\ Поэтому мы можем попробовать следующую стратегию:
Как только в DVI-файле встретилось ‘\special{R}’, запомним текущие горизонтальную координату ho и вертикальную координату dq; запомним также текущее положение ро в DVI-файле. Положим с <— 0. Затем следующие DVI-инструкции будем просматривать, вместо использования их собственно для печати; но при этом будем как обычно следить за изменением горизонтальной и вертикальной координат на странице.
Когда в DVI-файле встретилось ‘\specialtL}’, перестаем просматривать инструкции. Затем выдаем на печать все инструкции между ро и текущим положением в зеркальном режиме, как описано ниже.
Если во время просмотра инструкций встречается 'push', увеличиваем с на 1.
Если во время просмотра инструкций встречается ‘pop', то возможны два варианта. Если с > 0, уменьшаем с на 1. (Эта команда ‘pop' соответствует встреченной ранее при просмотре команде ‘push'.^ Но если с = 0, вставляем в этом месте ‘\specialtL}’ и ‘\specialtR}’ —после следующей команды ‘push'.
Зеркальный режим для DVI-команд, располагающихся между положениями ро и pi в DVI-файле, начинающий печатать в точке (/г-о^о) и заканчивающий в точке (hi,vi), работает следующим образом. Символ ширины w, бокс которого в нормальном режиме располагался бы на базовой линии между (h, v) и (Л + w, v), нужно расположить так, чтобы этот бокс в зеркальном режиме располагался на базовой линии между (h/ — w,v) и (h^o^ где h' определяется из уравнения
h — ho — h^ — hf.
Аналогично, линейка ширины w, нижняя сторона которой в нормальном режиме соединяет (h^^ и (h + w,v), в зеркальном режиме должна соединять (Ы — w, v) и (Л', Vy
Ликвидируем недочеты
Выше мы утверждали, что только что описанный метод будет «почти» работать. Но он может не сработать в трех ситуациях, если использовать весь спектр возможностей Т^’а. Во-первых, можно делать вставки «между строк» с помощью команд \vadjust; эти вставки могут ошибочно обрабатываться как R-текст. Во-вторых, предложенный механизм не всегда правильно находит левый край переворачиваемого фрагмента, потому что переворот не всегда должен начинаться с самого левого края печатной строки; он должен начинаться после Meftskip-клея и перед другими начальными отступами, сделанными для расстановки акцентов и т. п. В-третьих, с помощью некоторых трюков с использованием \unhbox, можно заставить целые строки исчезать из DVI-файла; впрочем, эта проблема не столь важна, как первые две, потому что этими трюками злоупотреблять не стоит.
Гораздо более заслуживающая доверия и устойчивая схема получается, если создать специально расширенную версию ТвХ’а, которая добавляет парные команды \special в каждую строку, в которой присутствует перевернутый текст. Добавить к уже имеющемуся в Т^’е механизму разбиения на строки еще и эти действия несложно; подробности описаны ниже в приложении. После того как эти изменения произведены, можно опустить пункты (с) и (d) алгоритма просмотра для DVI-IVG.
L-шовинизм
До сих пор мы обсуждали смешанные документы так, как будто они всегда состоят из R-фрагментов, вставляемых в L-окружение; но люди, пишущие на родном языке справа налево, естественно думают о смешанных текстах как об L-вставках в R-окружение. На самом деле, каждую напечатанную страницу можно читать двумя способами: начиная пробегать каждую строку глазами слева и начиная пробегать каждую строку глазами справа.
Приведенный выше пример из Леонардо принадлежит первому типу, мы будем называть его L-документом. Чтобы прочесть данную строку L- документа, нужно начинать слева и читать любой L-текст, находящийся в строке. Как только ваши глаза наталкиваются на R-символ, нужно пробежать глазами текст до конца следующего R-фрагмента (т. е. до ближайшего L-символа или до конца строки, смотря что встретится раньше); затем нужно прочитать R-фрагмент справа налево и продолжить, как вначале. Подобным же образом следует читать R-документы, только нужно поменять местами правое и левое.
Обычно L-документ можно отличить от R-документа по отступу в первой строке абзаца и/или пустому пространству в последней строке. Например, R-
документы, соответствующие двум вариантам L-документов, полученных из абзаца про Леонардо, будут выглядеть так:
Leonardo da Vinci made a sweeping statement in his nfiiaiisnorfism в ion ai odw эпо on ioJ“ notebooks: In fact, he said it twice, so he probably “.a>how ym Ьбэт meant it.
Leonardo da Vinci made a sweep-
ioJ^ ing statement in his notebooks:
-ИбшэгВбш в ion ai odw эпо on
In fact, he ".adiow ym Ьбэт пв!э
said it twice, so he probably meant it.
Мы можем вообразить себе, что эти R-документы были набраны на R- терминале и XgT обрабатывал следующий э!й inqni [входной файл):
*1п9Ш91в1а §niq99W2 б эЬбш ioniV бЬ оЬтблоэЛ!
эпо on 19J< 1 | : 2dood9Jon aid ni
1 1 . adiow уш Ьбэт nsioi^бшэгЦ-бш б Jon at oiiw I .Ji Jns9ni yldsdoiq 9ii oa t9oiwJ Ji bisa 9ii t Job! nil
В данном случае внутри | заключен L-текст, а не R-текст. (Читателю придется внимательно изучить этот пример; в нем присутствует boriiom [система]!)
Надо полагать, поэт мог бы сконструировать интересные стихотворения, обладающие как L-смыслом, так и R-смыслом, в зависимости от того, читаются они как L-документы или как R-документы.
Обратите внимание, что в наших примерах из Леонардо мы использовали полужирные кавычки (т. е. кавычки ИгНдпЯ), так что кавычки принадлежат цитируемому тексту. Это может показаться неправильным; по на самом деле в тех документах, где не оказывается предпочтения ни L-читателям, ни R-читателям, это соглашение необходимо, потому что оно обеспечивает близость кавычек к переворачиваемому тексту. (См. образцы современной печатной продукции в конце этой статьи.) Если бы кавычки были английскими, а не riailgnS, показанные ранее R-документы действительно выглядели бы очень странно:
Leonardo da Vinci made a sweeping statement in his пб1эИбшэгМбш б ion ai odw эпо on ioJnotebooks: “ ” In fact, he said it twice, so he probably. a>how ym Ьбэт meant it.
Leonardo da Vinci made a sweep-
ioJing statement in his notebooks: “
-ИбшэШбш б ion ai odw эпо on
” In fact, he.adiow ym Ьбэт пб!э
said it twice, so he probably meant it.
Многоуровневое сочетание
Проблемы сочетания R- и L-печати этим не исчерпываются, потому что L- текст может оказаться внутри R-текста внутри L-текста. Например, мы могли бы захотеть напечатать абзац, исходный ТеХ’овский файл которбго выглядит так:
\R{Alice} said, \R{‘‘You think English is
\L{‘English written backwards’}; but to me, \L{English} is English written backwards. I’m sure \L{Knuth} and \L{MacKay} will both agree with me.’’} And she was right.
[\Н{Алиса} сказала:
\Н{<<Вы думаете, что aнглийcкий\dashэто \L{‘перевернутый английский’}; но для меня \L{aнглийcкий}\dashэто перевернутый английский. Я уверена, что \Ь{Кнут} и \Ь{Маккей} оба со мной согласятся>>.}
И она была права.]
Образованный в обоих направлениях читатель захочет, чтобы это было напечатано, как R-доку мент внутри L-доку мента. Другими словами, для такого читателя естественно будет некоторые строки читать, начиная слева, а некоторые — справа. Вот примеры желаемого результата, различающиеся шириной строки (просмотрите их внимательно):
ээИА said, ai НгИдпЯ dnirli uoY^ tom oi ind ;‘English written backwards’ ,abiBW>bBd noiiiiw dailgnSE ai English rfiod Iliw MacKay Ьпв Knuth этпг ml ".эш diiw ээтдв And she was right.
ээИА said, ‘En- ai ЯгИдпЯ dnirli uoY“ English {9m oi ind ;glish written backwards’ этпг ml .abiBw>l9Bd noiiiiw dailgnH ai diiw ээтдв diod Iliw MacKay Ьпв Knuth ",9m And she was right.
Многоуровневые документы обязательно несут в себе неоднозначность. Например, второй вариант примера ээИА можно интерпретировать как результат, полученный из
... \R{... I’m sure and \L{MacKay} will both agree with}
Knuth \R{me.’’} And she was right.
А первый вариант также мог быть получен из такого исходного файла(!):
\R{‘‘You think English is \L{said,} Alice
\L{‘English}; but to me,} written backwards’
XMwritten backwards.} \R{\L{English} is English}
will both} MacKay \R{and} Knuth \R{I’m sure
\L{And she} agree with me.’’} was right.
Небольшие различия заключались бы только в расстановке пробелов по причине ТЁХ’овского «коэффициента пробела» («space factor») для знаков препинания.
Вообще, \M\L{a}\L{b}} = ba, поэтому теоретически возможна любая перестановка символов в каждой строке. Читателю приходится догадываться, какой из различных способов просмотра каждой строки имеет больший смысл. Тем не менее, в странах Ближнего Востока существует единодушное мнение, что сочетать L-документы и R-документы предпочтительней, чем настаивать на абсолютно однозначном последовательном L-прочтении или R-прочтении всего текста, — потому что это вполне естественно и потому что настоящая неоднозначность на практике встречается редко. В предшествующем примере кавычки позволяют восстановить невидимые \R и \L; таким образом автор может сотрудничать с грамотным читателем в стремлении к ясности изложения.
Многоуровневые тексты возникают не только, когда цитаты присутствуют внутри цитат или когда сноски или иллюстрации, оформленные как R- документ, вставляются в L-документ; они возникают также, когда в R-текст вставляются математические выражения. Рассмотрим, например, следующий исходный ТуСовский файл:
The \R{English} version of ‘the famous identity
$e~{i\pi}+l=0$ due to Euler’ is
\R{‘the famous identity $e~{i\pi}+l=0$ due to Euler’}.
[\R{Английская} версия фразы -известное тождество
$e~{i\pi}+l=0$, идущее от Эйлера’ выглядит так:
\R{‘известное тождество $e~{i\pi}+l=0$, идущее от Эйлера’}.]
Печатать это нужно следующим образом:
The НгНзпЗЕ version of ‘the famous identity ег7Г + 1 = 0 due to Euler’ is гиошв! эИР 'тэТиЭЕ oi эпЬ ег7Г 4-1 = 0 yiiinobi.
Расширение ТЁХ’а, названное TgX-XjgfT, описанное в приложении, правильно обрабатывает многоуровневые сочетания, включающие математику, а также более простые случаи чередования R-текстов и L-текстов.
Заключение
Когда в одном документе сочетаются тексты, читаемые справа налево и слева направо, могут возникнуть проблемы, более тонкие, чем можно предположить из простых примеров. Трудности можно преодолеть, расширив ТЁК до ТЕХ-ХдТ’а и расширив DVI-драйверы до DVI-IVQ-драйверов. Ни то, ни другое расширение не является чрезмерно сложным.
Приложение
Описанные здесь расширения ТЁХ’а записывают в DVI-файл до сих пор неопределенные однобайтные коды 250 (‘begin-reflection^ и 251 ^ end-Teftection'Y вместо ‘\special{R}’ и ‘\special{L}’, как указано выше, потому что печать текстов чередующегося направления достаточно важна и заслуживает эффективного DVI-кодирования. Получающиеся в результате этого файлы называются DVI-I VQ-файлами.
Язык ТЁ^’а расширяется следующими четырьмя примитивными операциями (primitive operations):
\beginL \endL \beginR \endR
которые должны вставляться наподобие скобок в каждый абзац и каждый горизонтальный бокс (hbox). Но \endL и \endR следует опускать в конце абзаца, если они должны сработать после \parf illskip-клея. (Так, к примеру,
\everypar{\kern-\parindent\beginR\indent}
можно использовать в начале серии последовательных абзацев, в каждом из которых действуют правила R-документа. Последняя строка каждого из этих абзацев будет прижата к правому полю с заполнением влево; в первой строке отступ будет сделан справа.)
Каждая из четырех новых операций добавляет новый сорт «узла whatsit» в текущий горизонтальный список; это новые виды (горизонтальных команд) ((horizontal command)), в соответствии с объяснениями в руководстве по ТЁК’у [2]. Макро \L и \R в нашем многоуровневом примере про ээИА можно определить следующим образом:
\def\L{\afterassignment\moreL \let\next= }
\def\moreL{\bracetest \aftergroup\endL \beginL \rm}
\def\R{\afterassignment\moreR \let\next= }
\def\moreR{\bracetest \aftergroup\endR \beginR \revrm}
\def\bracetest{\ifcat\next{\else\ifcat\next}\fi \errmessage{Missing left brace has been substituted}\fi \bgroup}
Оставшаяся часть этого приложения дает полное и подробное описание изменений, которые нужно внести в стандартную программу ТЁХ’а [3], чтобы превратить ее в расширенную систему T^X-XjaT. Эти изменения удобно
перечислять по номерам тех WEB-разделов в [3], которые этими изменениями затронуты.
Здесь следует изменить последний вступительный абзац; новый вариант будет объяснять, что данная программа на самом деле ‘ТДО-ХдТ’, а не ‘ТЁХЛ Соответственно, строка banner переопределяется:
define banner = 'ТЫзи1зиТеХ~ХеТ,иУегз1опиЗ. 1415'
{ выводится при запуске TQX-XgT’a }
Изменяется pooLname^ так чтобы ТЁХ-XjgT мог прекрасно сосуществовать с ТЁХ’ОМ-
pooLname = TeXformats :ТЕХХЕТ.РООЬииииииииииииииииии ;
{ строка длины flle-namesize; говорит, где хранится запас сообщений}
В этом месте в программу вставляются дополнительные подпрограммы, которые будут определены позже.
(Declare functions needed for special kinds of nodes 1381)
[Объявить функции, необходимые для узлов специальных видов]
В конец старого списка добавляется новый командный код; поэтому заключительное определение заменяется на следующие два:
define LR =71 { направление текста ( \beginL , \beginR , \endL , \endR ) } define max_non_preflxed_command = 71
{ максимальный командный код, который не может быть \global}
Нужно добавить 1 к правым частям всех этих определений.
define toks.register = 72 {регистр списка токенов ( \toks ) }
define set-interaction = 101
{ определение уровня взаимодействия с пользователем ( \batchmode и т. д. ) }
define max_command = 100 { наибольший код команды, видимый при bigswitch }
К описанию DVI-команд в конце прибавляется две новых: begin-reflect 250. Начать (возможно вложенный) перевернутый фрагмент. end-reflect 251. Закончить (возможно вложенный) перевернутый фрагмент.
В обычных DVI-файлах команды 250-255 не определены, по 250 и 251 разрешены в специальных ‘DVI-IVQ’-файлах, производимых этой версией Tg%’a-
Когда DVI-IVQ-драйвер встречает команду begin-reflect, он должен просматривать последующие команды (как описано ранее), пока не найдет парную end-reflect; они будут правильно располагаться друг относительно друга и относительно push и pop. После того как в результате просмотра определился фрагмент текста, который нужно перевернуть, этот фрагмент пробегается заново и обрабатывается в зеркальном режиме, как описано ранее. Переворачиваемый фрагмент может содержать вложенные пары begin-reflect / end-reflect, которые нужно еще раз переворачивать.
Необходимы два новых определения:
define begin_reflect = 250 { начать перевернутый фрагмент (используется только в DVI-IVQ-файлах) }
define end_reflect = 251 {закончить перевернутый фрагмент (используется только в DVI-IVQ-файлах) }
В начале ship, out мы инициализируем стек действующих на данный момент инструкций \beginL и \beginR; он называется LR-стеком и поддерживается при помощи двух глобальных переменных, называемых LR-ptr и LR-tmp, их мы определим позднее. Вставляемые здесь инструкции (непосредственно перед проверкой условия tracing .output > 0) говорят, что на самом внешнем уровне мы печатаем в режиме слева направо. Начальный ‘begin’ заменяется на
begin LR_ptr 4— get-avail; info (LR-ptr) 4—0; { begin_L-Code на внешнем уровне}
В конце ship_out мы хотим очистить LR-стек. Поэтому происходит замена ‘flush.node.list (рУ на
flush-node-list (р); (Flush the LR stack 1385);
[Очистить LR-стек]
Программа hpack изменяется, чтобы поддерживать LR-стек при формировании горизонтального списка таким образом, чтобы ошибки в парах \beginL. . . \endL и \beginR. . .\endR находились и исправлялись. Изменения требуется внести в начало процедуры и в конец.
function hpack(р : pointer; w : scaled; m : small-number); pointer;
hd; eight-bits; {индексы высоты и глубины для символа}
LR-ptr, LR-tmp: pointer; { для поддержки LR-стека }
LR-problems: integer; {подсчитывает недостающие begin и end}
begin LR-ptr 4— null; LR-problems 4— 0;
r 4— get-node (box-nodesize);
common-ending; (Finish issuing a diagnostic message for an overfull or underfull hbox 663);
[Закончить выдавать диагностическое сообщение для слишком широкого или недостаточно широкого горизонтального бокса]
exit; (Check for LR anomalies at the end of hpack 1390);
[Проверить LR-несоответствия в конце hpack^
hpack 4— r;
end;
Аналогично, программа post.line-break должна поддерживать LR-стек, гак чтобы она могла выдавать инструкции \endL или \endR в конце строк и инструкции \beginL или \beginR в начале строк. Изменения затрагивают зачало и конец этой процедуры:
procedure post-line-break (fin aLwidow .penalty : integer);
cur-line: halfword; {текущий номер строки}
LR-ptr, LR-tmp: pointer; { для поддержки LR-стека }
begin LR-ptr null;
(Reverse the links of the relevant passive nodes, setting cur.p to the first breakpoint 878);
[Обратить ссылки релевантных пассивных узлов, выставляя сит.р на первую точку разрыва]
prev-graf best-line — 1; (Flush the LR stack 1385);
[Очистить LR-стек]
end;
Новые действия, выполняемые при формировании разбитого на строки текста, завершаются тремя новыми шагами, добавляемыми к этому разделу программы.
(Justify the line ending at breakpoint cur.p, and append it to the current vertical list, together with associated penalties and other insertions 880) =
[Установить конец строки в точке разрыва сиг_р и добавить ее в текущий вертикальный список вместе с полагающимися штрафами и другими вставками]
(Insert LR nodes at the beginning of the current line 1386);
[Вставить LR-узлы в начало текущей строки]
(Adjust the LR stack based on LR nodes in this line 1387);
[Изменить LR-стек в соответствии с LR-узлами в этой строке]
(Modify the end of the line to reflect the nature of the break and to include \rightskip; also set the proper value of disc-break 881);
[Модифицировать конец строки в соответствии с природой разрыва и добавить \rightskip; также установить правильное значение disc-break^
(Insert LR nodes at the end of the current line 1388);
[Вставить LR-узлы в конец текущей строки]
(Put the \leftskip glue at the left and detach this line 887);
[Добавить Meftskip-клей слева и отделить эту строку]
1090. Здесь мы добавляем ^mode 4- LR1 в качестве нового подслучая после ^mode 4- no-boundary ’. Это означает, что новые примитивные операции станут тем, что в книге [2] называется (горизонтальными командами).
1196. Математика внутри текста будет форматироваться слева направо, потому что в этот раздел кода вставляются две новые инструкции ‘append’.
(Finish math in text 1196) =
[Закончить математику внутри текста]
begin taiLappend (new.math (mathsurround, before));
(Append a begin.L to the tail of the current list 1383);
[Добавить begin_L в хвост текущего списка]
cur.mlist 4— р; curstyle 4— text-style;
mlist-penalties 4— (mode > 0);
mlist-to_hlist;■
link (tail) 4— link (temp-head);
while link (tail) null do tail 4— link (tail);
(Append an end-L to the tail of the current list 1384);
[Добавить end.L в хвост текущего списка]
taiLappend(new-math(mathsurround, after)); space-factor 4— 1000; unsave;
end
1341. Новые примитивные операции добавляют новые типы узлов whatsit в горизонтальные списки. Поэтому здесь дополнительно требуются два новых определения:
define LR-node =4 { subtype в whatsit’ax, представляющих \beginL и т. д. }
define LR_type(U) = mem[# + 1]лп£ {sub-subtype}
1344. Это место, где устанавливаются новые примитивы.
define immediate-code =4 { команда-модификатор для \immediate } define set-language-code = 5 { команда-модификатор для \setlcmguage } define begin-L-code = 0 { команда-модификатор для \beginL }
define begin-R-Code = 1 { команда-модификатор для \beginR }
define end.L-code = 2 { команда-модификатор для \endL }
define end-R-Code = 3 { команда-модификатор для \endR }
define begin_LR(U) = (LR_type(U) < end-L-code)
define begin-LR-type(#) = (LR_type(U) — end-L-code)
(Put each of T)3X’s primitives into the hash table 226) += [Вставить каждый из примитивов ТЩХ’а в хэш-таблицу] primitive("beginL", LR, begin-L-code);
pnmi£we("beginR", LR, begin-R-code);
primitive (" endL11, LR, end-L-code);
primitive(uendRu, LR, end-R-Code);
primitive("openout", extension, open_node);
1346. Здесь новые примитивы требуют добавления нового случая при разборе случаев.
LR: case chr-code of
begin-L.code: print-esc("beginL");
begin-R-code: print-esc ("beginR");
end-L-code: print-esc ("endL");
othercases print-esc ("endR") endcases;
1356. Нам также нужно уметь выводить на экран новомодные whatsit’bi.
LR-node: case LR_type(p) of
begin-L-code: print-esc ("beginL");
begin-R-Code: print-esc ("beginR");
end_L-Code: print-esc ("endL");
othercases print.es c("endR") endcases;
1357, 1358. Копирование и уничтожение новых узлов не составляет труда, так как с ними можно поступать, как с уже имеющимися узлами \closeout. В этих двух разделах мы просто заменяем ‘close-node1 на ‘close-node, LR-node1.
1360. Раньше мы здесь выполняли do-nothing [ничего не делать], но теперь мы должны выполнить do-something [кое-что сделать]:
(Incorporate a whatsit node into an hbox 1360) =
[Включить узел whatsit в горизонтальный бокс]
if subtype (р) = LR.node then (Adjust the LR stack for the hpack routine 1389) [Подготовить LR-стек для программы hpack\
Эта часть кода используется в разделе 651.
1366.( Output' the whatsit node p in an hlist 1366 ) =
[Выдать узел whatsit p в горизонтальный список]
if subtype (p) ^ LR.node then out.what(p).
else (Output a reflection instruction if the direction has changed 1391) [Выдать инструкцию переворота, если изменилось направление]
Эта часть кода используется в разделе 622.
Большинство изменений были оставлены напоследок, так что номера разделов Т^Х’а в [3] можно оставить без изменений. Теперь мы дошли до самых внутренностей этого расширения для текстов с чередованием направления.
Во-первых, мы допускаем появление новых примитивов в горизонтальном режиме, но не в математическом:
(Cases of main.control that build boxes and lists 1056) +=
[Случаи main.control, которые конструируют боксы и списки]
hmode + LR: begin new.whatsit(LR.node, small.node.size); LR.type(tail) <— cur.chr; end;
mmode + LR: report _illegal_case;
Некоторые программы используют стек, состоящий из однословных узлов, поля info которых содержат либо begin-L-Code, либо begin-R-Code. Указателем на вершину стека является LR.ptr, а вспомогательная переменная LR-tmp используется для манипуляций со стеком.
(Global variables 13) +=
[Глобальные переменные]
LR.ptr, LR.tmp: pointer; { стек LR-кодов и temp для манипуляций}
(Declare functions needed for special kinds of nodes 1381) =
[Объявить функции, необходимые для узлов специальных видов]
function new-LR(s : small-number): pointer;
var р: pointer; {новый узел }
begin р <— get-node(smalLnodesize); type(p) <— whatsiLnode; subtype (p) LR.node;
LR_type(p) <— s; new.LR <— p;
end;
См. также раздел 1382.
Эта часть кода используется в разделе 161.
(Declare functions needed for special kinds of nodes 1381) +=
[Объявить функции, необходимые для узлов специальных видов]
function safe-info(р : pointer): integer;
begin if p = null then safe-info <1 else safe-info <— info(p);
end;
(Append a begin-L to the tail of the current list 1383) =
[Добавить begin-L в хвост текущего списка]
taiLappend (new-LR (begin-L-code))
Эта часть кода используется в разделе 1196.
(Append an end_L to the tail of the current list 1384) =
[Добавить end-L в хвост текущего списка]
taiLappend (new-LR (end-L .code))
Эта часть кода используется в разделе 1196.
Когда ниже используются макро для работы со стеком из этого раздела, переменные LR-ptr и LR_tmp могут быть объявленными ранее глобальными переменными или они могут быть локальными в hpack или post Jin e-break.
define push_LR(tt) =
begin LR-tmp <— get-avail; info(LR-tmp) <— LR-type (#);
link (LR-tmp) <— LR-ptr; LR-ptr <— LR-tmp;
end
define pop-LR =
begin LR-tmp <— LR-ptr; LR-ptr <— link(LR-tmp); free_avail (LR-tmp);
end
(Flush the LR stack 1385) =
[Очистить LR-стек]
while LR-ptr ^ null do pop-LR
Эта часть кода используется в разделах 639 и 877.
(Insert LR nodes at the beginning of the current line 1386) =
[Вставить LR-узлы в начало текущей строки]
while LR-ptr ^ null do
begin LR-tmpnew_LR(info (LR_ptr)); link (LR-tmp)link (temp-head);
link (temp-head)LR-tmp; pop-LR;
end
Эта часть кода используется в разделе 880.
(Adjust the LR stack based on LR nodes in this line 1387) =
[Изменить LR-стек в соответствии с LR-узлами в этой строке] q <— link (temp-head У,
while q ^ cur -break (cur _p) do
begin if ^is-char_node(q^ then
if type(q) = whatsit-node then
if subtype (q) = LR-node then
if begin_LR(q) then push-LR(q)
else if LR-ptr ^ null then
if info(LR-ptr) = begin-LR_type(q) then pop-LR\
q <— Hnk(qY
end
Эта часть кода используется в разделе 880.
Мы пользуемся тем, что q теперь указывает на узел с \rightskip- клеем.
(Insert LR nodes at the end of the current line 1388) =
[Вставить LR-узлы в конец текущей строки]
if LR-ptr ^ null then
begin s <— temp-head; r <— link(sy
while r ^ q do
begin s <— r; r <— link(sy
end;
r <— LR-ptr;
while r + null do
begin LR-tmp <— new-LR(info(r) + end-L-code); link(s) LR-tmp;
s <— LR-tmp; r <— link(r);
end;
link(s) <— q;
end
Эта часть кода используется в разделе 880.
(Adjust the LR stack for the hpack routine 1389) =
[Подготовить LR-стек для программы hpack\
if begin_LR(p) then push_LR(p)
else if safe.info(LR-ptr) = begin-LR-type(p) then pop-LR
else begin incr (LR-problems);
while link(q) ^ p do q <— link(q);
link(q) <— Ипк(рУ free_node(p, small-nodesizeY p q;
end
Эта часть кода используется в разделе 1360.
(Check for LR anomalies at the end of hpack 1390) =
[Проверить LR-несоответствия в конце hpack\
if LR-ptr + null then
begin while link(q) ^ null do q Hnk(qY
repeat link(q)new-LR(info(LR-ptr) + end-L-code)-, q <— Unk(qY
LR-problems LR-problems + 10000; pop-LR\
until LR-ptr = null;
end;
if LR-problems > 0 then
begin print-ln; print-nl ("\endLuoru\endRuproblemu ( ");
print.int ^LR-problems div 10000); pnnZ (" uinis sing, u");
printjint(LR_problems mod 10000); print("uextra");
LR.problems <—0; goto common-ending;
end
Эта часть кода используется в разделе 649.
( Output a reflection instruction if the direction has changed 1391) =
[Выдать переворачивающую инструкцию, если изменилось направление] if begin-LR^ then
begin if safe-info(LR-ptr) ^ LR_type(p) then
begin synch-h; synch-v; duLout^beginjreflectY
end;
push-LR (p);
end
else if safe-info(LR-ptr) = begin-LR-type(p) then
begin pop-LR;
if info(LR-ptr) + end-L-code ^ LR-type(p^ then begin synch-h; synch-v; dvi_out(end_reflect);
end;
end
else confusion ("LR")
Эта часть кода используется в разделе 1366.
Заключительные важные замечания
Только что описанные расширения Т^Х’а являются «надстройками» над стандартным Т^’ом, в том смысле что обычные программы для ТЁХ’а по-прежнему будут правильно (хотя и более медленно) обрабатываться ТеХ-ХдТ’ом. Тем не менее, ТЁ^-^ЭГ нельзя называть новой версией системы ‘ТЁК’, несмотря на то что он обрабатывает все программы для ТЁХ’а; причина этого, безусловно, в том, что ТЁХ не будет обрабатывать все программы для ТЁХ-ХдТ’а.
Изменение названия необходимо для того, чтобы различать программы, которые не соответствуют настоящему ТЁХ’у в точности. Каждый, запускающий программу под названием ‘ТЁХ’, должен ожидать, что он получит одинаковый результат при всех ее реализациях.
Литература
Joseph D. Becker, “Arabic word processing,” Communications of the ACM 30 (1987), 600-610.
Donald E. Knuth, The T^Kbook, Volume A of Computers &; Typesetting (Reading, Massachusetts: Addison-Wesley, 1986). [Имеется русский перевод: Дональд Е. Кнут. Все про Т^Х. — Протвино: АО RDT^X, 1993.]
Donald Е. Knuth, Т^К: The Program, Volume В of Computers &: Typesetting, fifth printing (Reading, Massachusetts: Addison-Wesley, 1993). [Более ранние издания соответствуют более ранним версиям TgX’a, когда изменения для ТЁХ-Х^Г’а были аналогичными, но несколько отличались.]
Pierre MacKay, “Setting Arabic with a computer,” Scholarly Publishing 8,2 (January 1977), 142-150.
Pierre MacKay, “Typesetting problem scripts,” Byte 11,2 (February 1986), 201-218.
Примеры из типографской практики
Из Textus 5 (1966), с. 12; Magnes Press, Hebrew University of Jerusalem. (Обратите внимание на ивритские кавычки, окружающие название на иврите в сноске 6.)
ters adhered,10 and which may have been similar to that adopted, by normative
Jewry presumably somewhat later, during the period of the Second Temple.10
Frag. E. Yadin correctly states: “Sanders* cautious indication *103 (7 104)* can now be eliminated'* (ib., p. 5).
6 Sanders* editio princeps of Ps. 151 already has been discussed by various scholars. The . present author deals with the text of Ps. 151, and its literary genre in: D^riX'H птото* . -JlODIpD тпзуп pv^D, Tarbiz 35 (1966) 214-228.
Выдержки из третьего издания классического труда девятнадцатого столетия по арабской грамматике Уильяма Райта, том 2, сс. 295-297. (Обратите внимание на переход на следующую страницу посередине текста, читаемого справа налево, и некоторые L-текстовые скобки.)
296296Part Third.—Syntax.Part Third.—Syntax.[§152[§152the brightness of the intellect is obscured (or
296
296
Part Third.—Syntax.
Part Third.—Syntax.
[§152
[§152
= eclipsed) by obeying lust. As the above examples show, this agreement
§152] Sentence and its Parts.—Concord of Predicate & Subject. 297 .
verb is placed after a collective subject (see § 148); as ^^1 0^3
: 61^4^' but the greatest part of mankind are thankless;
: ^UJI j*A* c>O^ a Parl °f them are afraid of men; [1у£у!:
\^^i U J^JI let the Turks alone as long as they let you alone; . ",5 6,.
l>£Lb aA^ ^ because his army had perished].\
Co c. 233 той же книги. Здесь R-тексты приравниваются знаками =; сначала следует читать левые части каждого равенства.
'# 1о*Л > t ## Lo* # 5 «• Э 1 ## й «•>15#;
; understood; ^j’jl jm = ^j^^ AaL-JI 5>^> te. As UJI»jJUaJI
' J&1 (see § 77). Similarly, some grammarians consider ^tjAB 4uU-
■ = ^J.^^*! 1 U^^’ *T*N,^*> ^*^^^1 Jb#«x< — ^#otc^J I U^^^ JUK«mmO
or ^«UUl C-JjJI дх^#, eU*»JI AJUl^ = fIa^aJ I A^aUt aLu, and
i 5^*.^ I jb = 5j*J*jl SUaLjl jb*. Here too the constructions J^b
Из Bulletin of the Iranian Mathematical Society 8 (Tehran, 1978), c. 78L. (Математика, читаемая слева направо, в тексте, читаемом справа налево.)
:^уАе* Cree^jej ^j 1*5> Jy*j ^ Че -^ **S •
;nzi + 2(n2m^■
;2m Зпг4гтг:
е=2.71828 оЪ-’^с*-’ 1/(1-а) + (1оде(1-а)] /а ^L-'l^^
(j 1 -U »j С р f*xy^’ll**^ I# Jj >>• *# d' l«j£ q jjf 4> L» I 0< l^< Lm • X~> L^f* :
University, 1942), с. 38. (Номера страниц ‘96-90’, потому что ‘90’ и ‘96’ ивритские числа.)
! П m В hv торя ВВ®8Л ПК 51ПЭ^ t5U .Л’ЗЗКПЗЗЗрЛ 3®1в3 13®ЭП®ЛЗ : : ПЛИЗ ) 10 Ь ’ 1 ВВ®а ПК1 ,ар=а (mod. р) : ЛЧЗХЗ !
;1 • 2 • 3 • • • (р—1) =—1 (mod. р).;
‘.тп флк = modulus ,в'клв = congruens лгвтт о^л ^п л
• Journal f. d. Л to 116 Л рзз 1896 nwa М. HAMBURGER jnw ЛПЭ1ЛЭ }"У .2
: ’лтл 1’лклл пк пуззрл Л802 лит diki to лпмЛ (96-90 ту) reine u. ang. Mathematik ;
6. На с. 200 той же книги демонстрируется различие между многоточиями ‘ • • • ’ в формулах и в тексте. Нигде в этой книге математика внутри текста не разбивается на строки.
: B’4DBat рЧВПП п ВК Л’УЗЛ ПК ЛШВ^ 1®ВХ® (3 ?31Г’К ЛВОП t® УО^З i ^5 t’3®3 ПЧПВЗ Л’УЗЛ® ’КЗПЗ .п = р1>р1рк В’31® В”31®КТ
: П311ПКЛ ЛЗУВЛ ПК 1КЗЗ® рз рВПОЗ .п=рк .... .п=рг .п = рх В’5Л)?Л ■
! пптуз ni33t IBZDKr В'КПрЛ 5П^ 'К1113 угг .л =15 = 3 5 ЛВЗПЬ 0П’3!
' :плк р»Ь) [1] лкзуал 'in» ^э .п=р .лт ллраз» р’П’ ^>э впр тпл» рзаз "В”3’В’а’лв. в'опту взл (it влв ,хр=1 пхзгал 'trie? ts ,л = р К1ВК \Т .р t ЧТ (1,2, ... , р - 1) ЛПОП 11ПП к py ts 'ПЛ® ; 199 'ВУЗ t® л'®ч® Ьэ ^_Ч... ,$2,$ гл* 1К «[1] лк1®ал t® зл®Ьэ ®т® 51 .лт’П’л ’®ч® в'кпрз л^кл в’®п®л .‘[1] лк1®ал
Рецепты и дроби
[Первоначально опубликовано в TUGboat 6 (1985), 36-38.]
На сс. 277, 281 и 282 книги The T^Xbook содержатся примеры выравнивания, взятые из довольно известной книги Julia Child et al. Mastering the Art of French Cooking. Некоторые меры в этих примерах содержат дроби типа ‘ j’, что привело к неприятному наложению выровненных строк, когда я просматривал первую корректуру таблиц на сс. 281-282. На самом деле дроби на разных строках не касались друг друга, но они располагались настолько тесно, что раздражали глаз. Вот почему в этих примерах я увеличил расстояние между базовыми линиями шрифта на 2 pt.
Поскольку при написании The T^Xbook мне несколько раз пришлось набирать рецепты, я узнал о том, что мне следовало уже давно реализовать: в подобных контекстах удобнее работать с типографской ^/«Т, чем с математической ‘|’. Поэтому я недавно добавил новое упражнение 11.6 в свою книгу The T^Xbook, объясняющее, как получать дроби вида ^/2’, если их нет в готовом виде в шрифте. Я также изменил примеры на сс. 277, 281 и 282, так что и в них использована эта идея (относительно деталей см. текущий список опечаток или более новое издание.)
' В прошлом декабре мы с женой сделали подарок Ассоциации университетских библиотек Станфорда: испекли пирог к их ежегодному Рождественскому чаю по рецепту «Stollen» моей бабушки и, кроме того, мы изготовили копии рецептов как образцы компьютерной типографии. Я был доволен тем, что этим книголюбам понравился не только пирог, но и качество набора, хотя это произвел компьютер. Уверен, что если бы не был использован соответствующий вид дробей, нам бы не получить такой благоприятный отзыв.
Далее приводится копия типографской части нашего подарка и произведший ее ТеХ-код на случай, если читателю интересно взглянуть на еще один небольшой, но полный пример использования ТвХ’а (основанный только на макро plain Tg^’a). Окончательный вывод был сверстан и напечатан таким
образом, что мы легко могли сложить эти две страницы, сделав, в сущности открытку размера 3" х 5", которую можно было заполнить другими рецептами. Поскольку рецепт довольно короткий, я не пользовался никакими замысловатыми макро, для того чтобы получить список ингредиентов в две колонки.
У. Рецепт рождественского пирога Stollen
\hsize=4.5in У, ширина полосы набора \vsize=2.3in У. высота полосы набора \nopagenumbers
\font\ninerm=cmr9
\def \frac#l/#2{\leavevmode\kern.lem
\raise.5ex\hbox{\the\scriptfontO #l}\kern-.lem
/\kern-.15em\lower.25ex\hbox{\the\scriptfontO #2}}
\parskip=3pt У, отбивка между абзацами \parindent=Opt У, без отступа
{\bf Christmas Stollen} \medskip
\tabskip=10pt plus ifil \halign to \hsize{&#\hfil\cr 1 pint milk, scalded and cooledfe \fracl/2 teaspoon nutmeg\cr
ounce compressed or dry yeast& l\fracl/2 teaspoons salt\cr
cup butterfe
cups flour\cr
cup sugar&
pound mixed candied fruit\cr
4 eggs&
\frac3/4 pound candied cherries\cr
grated rind of 1 lemon&
cup nuts\cr }
\smallskip
Dissolve yeast in scalded, cooled milk. Add 1 cup of the flour. Let it rise \fracl/2~hour.
Cream butter and sugar. Beat in eggs, one at a time.
Stir in yeast mixture. Add lemon rind, nutmeg and salt.
Dredge the fruit in a little flour to keep the pieces from sticking together. Add the rest of the flour to the dough, and finally stir in the fruit and nuts. Knead the dough until smooth. Put in a warm place in a covered bowl and let rise until doubled in bulk.
\ (Because the fruit makes the dough heavy, it may take two or three hours to rise.) \ Divide the dough into three parts. Roll each portion out to about l~inch thick, then fold over in thirds to form a long, loaf shape. Place on a greased cookie sheet, cover and let rise until doubled"again.
Bake at $325~\circ\,$F. for 45 minutes.
Stollen is traditionally frosted with thin powdered-sugar-and-butter icing. Decorate each loaf with red and green candied cherries.
Vary the fruit and nuts to suit your taste.
You may use cherries alone, mixed fruit, and/or dates; almonds, pecans, walnuts, or no nuts at all.
\medskip \ninerm \baselineskip=llpt
This is the recipe that was used each Christmas by Don’s grandmother, Pauline Ehlert~Bohning, Cleveland, Ohio. Don’s mother, Louise Bohning~Knuth, still makes more than 20 loaves each year, and when we were married she passed the recipe on to us. We hope you enjoy it.
\vskip-\baselineskip
\rightline{Don and Jill Knuth, Stanford, 1984} \eject \end
Christmas Stollen
1 pint milk, scalded and cooled1 ounce compressed or dry yeastcup buttercup sugareggsgrated rind of 1 lemon1 pint milk, scalded and cooled1 ounce compressed or dry yeastcup buttercup sugareggsgrated rind of 1 lemonV2 teaspoon nutmeg 1^2 teaspoons salt 8 cups flour1 pound mixed candied fruit 3/4 pound candied cherries 1 cup nutsV2 teaspoon nutmeg 1^2 teaspoons salt 8 cups flour1 pound mixed candied fruit 3/4 pound candied cherries 1 cup nuts
1 pint milk, scalded and cooled
1 ounce compressed or dry yeast
cup butter
cup sugar
eggs
grated rind of 1 lemon
1 pint milk, scalded and cooled
1 ounce compressed or dry yeast
cup butter
cup sugar
eggs
grated rind of 1 lemon
V2 teaspoon nutmeg 1^2 teaspoons salt 8 cups flour
1 pound mixed candied fruit 3/4 pound candied cherries 1 cup nuts
V2 teaspoon nutmeg 1^2 teaspoons salt 8 cups flour
1 pound mixed candied fruit 3/4 pound candied cherries 1 cup nuts
Dissolve yeast in scalded, cooled milk. Add 1 cup of the flour. Let it rise У2 hour. Cream butter and sugar. Beat in eggs, one at a time. Stir in yeast mixture. Add lemon rind, nutmeg and salt. Dredge the fruit in a little flour to keep the pieces from sticking together. Add the rest of the flour to the dough, and finally stir in the fruit and nuts. Knead the dough until smooth. Put in a warm place in a covered bowl and let rise until doubled in bulk.
(Because the fruit makes the dough heavy, it may take two or three hours to rise.) Divide the dough into three parts. Roll each portion out to about 1 inch thick, then fold over in thirds to form a long, loaf shape. Place on a greased cookie sheet, cover and let rise until doubled again. Bake at 325° F. for 45 minutes.
Stollen is traditionally frosted with thin powdered-sugar-and-butter icing. Decorate each loaf with red and green candied cherries.
Vary the fruit and nuts to suit your taste. You may use cherries alone, mixed fruit, and/or dates; almonds, pecans, walnuts, or no nuts at all.
This is the recipe that was used each Christmas by Don’s grandmother, Pauline Ehlert Bohning, Cleveland, Ohio. Don’s mother, Louise Bohning Knuth, still makes more than 20 loaves each year, and when we were married she passed the recipe on to us. We hope you enjoy it. Don and Jill Knuth, Stanford, 1984
Рождественский пирог Stollen
пинта молока, вскипяченного и охлажденногоунция спрессованных или сухих дрожжейстакан масластакан сахара4 яйцапинта молока, вскипяченного и охлажденногоунция спрессованных или сухих дрожжейстакан масластакан сахара4 яйцацедра 1 лимона^2 чайной ложки мускатного ореха l1^ чайной ложки соли8 стаканов мукифунт засахаренных фруктов3Д фунта засахаренной вишнистакан ореховцедра 1 лимона^2 чайной ложки мускатного ореха l1^ чайной ложки соли8 стаканов мукифунт засахаренных фруктов3Д фунта засахаренной вишнистакан орехов
пинта молока, вскипяченного и охлажденного
унция спрессованных или сухих дрожжей
стакан масла
стакан сахара
4 яйца
пинта молока, вскипяченного и охлажденного
унция спрессованных или сухих дрожжей
стакан масла
стакан сахара
4 яйца
цедра 1 лимона
^2 чайной ложки мускатного ореха l1^ чайной ложки соли
8 стаканов муки
фунт засахаренных фруктов
3Д фунта засахаренной вишни
стакан орехов
цедра 1 лимона
^2 чайной ложки мускатного ореха l1^ чайной ложки соли
8 стаканов муки
фунт засахаренных фруктов
3Д фунта засахаренной вишни
стакан орехов
Растворите дрожжи во вскипяченном и охлажденном молоке. Добавьте стакан муки. Оставьте на подъем на Уг часа. Взбейте масло с сахаром. Добавляйте яйца по одному. Смешайте с дрожжевой смесью. Добавьте цедру лимона, мускатный орех и соль. Посыпьте фрукты небольшим количеством муки, чтобы кусочки не слипались. Добавьте остальную муку к тесту и смешайте все с фруктами и орехами. Вымешивайте тесто до гладкости. Поставьте в теплое место, накрыв миской, и оставьте до увеличения объема вдвое. (Поскольку фрукты утяжеляют тесто, процесс может
длиться от двух до трех часов.) Разделите тесто на три части. Раскатайте каждую часть примерно до толщины в 1 дюйм, затем сложите втрое, придав форму длинного пирога. Разместите все на противне, накройте и снова дайте подняться до увеличения объема вдвое. Выпекайте в духовке 45 мин при температуре 325° F.
Stollen обычно покрывают тонким слоем глазури из сахарной пудры и масла. Каждый пирог украшают красными и зелеными свечами, окруженными вишнями.
Состав фруктов и орехов можно менять по своему усмотрению. Можно брать только вишню или фруктовую смесь и (или) финики; фундук и (или) грецкие орехи и (или) миндаль или вовсе обойтись без орехов.
По этому рецепту каждое Рождество пекла бабушка Дона Полина Элерт Бонинг (Кливленд, Огайо). Мать Дона —Луиза Бонинг Кнут — все еще выпекает по 20 пирогов ежегодно; когда мы поженились, она передала рецепт нам. Надеемся, он вам понравится. Дон и Джил Кнут, Станфорд, 1984
Логотип системы Т^Х в различных шрифтах
[Первоначально опубликовано в TUGboat 7 (1986), 101.]
«Официальное» определение логотипа системы ТеХ, которое содержится в макропакете plain ТеХ (cm. The T^Xbook^ выглядит следующим образом:
\def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX}
Однако макрокоманды plain TEX’a создавались в расчете на семейство шрифтов Computer Modern и учитывают специфику именно этого семейства. При использовании других гарнитур может потребоваться изменить величины «отрицательных пробелов», фигурирующих в определении, с тем чтобы сохранить эстетику данного логотипа.
На самом деле приведенное выше определение вполне годится для подавляющего большинства основных шрифтов с засечками, входящих в семейство Computer Modern, а именно для всех кеглей шрифтов cmr, cmsl, cmti и cmbx. Что же касается рубленых шрифтов, с ними ситуация действительно не такая простая. На с. 418, 419 (^22) книги The ТвКЬоок даны два альтернативных определения команды ‘\ТеХ’: одно для шрифта cmssdclO, приведенного к размеру 40pt, которым набираются заголовки глав (см. с. 36 U6)), другое —для шрифта cmssq, используемого для оформления цитат в конце глав (см. с. 337 иоПУ
Цель настоящей заметки—дать общую запись для различных версий команды ‘\ТеХ’, использовавшихся при наборе книг серии Computers & Typesetting. Эта запись позволяет без труда имитировать стиль той или иной книги.
Во всех версиях команды буква ‘Е’ опущена вниз на одну и ту же величину . 5ех (на половину х-высоты), и отличие версий друг от друга состоит только в величинах «отрицательных пробелов», определяемых командой \kern. Таким образом, в общем виде команду для набора ТеХ’овского логотипа можно записать как
\def \TeX{T\kernaem\lower. 5ex\hbox{E}\kern/?emX}
при некоторых а и (3. В следующей таблице приведены пары значений (а,^), реально использовавшиеся при наборе уже опубликованных томов серии:
семейство шрифтова
cmr—.1667
cmsl—.1667
0
-.125
-.125
cmti—.1667
cmbx—.1667
cmssdc—.2
-.125
-.125
-.06
cmssq cmssqi cmss
-.20
.-.20
v-.150
cmssi— .2О
cmssbx— .1О
(Три последних шрифта использовались только для текста на суперобложке, а не «настоящего» текста внутри книги. Подбор надлежащих значений пробелов потребовал довольно кропотливой работы.)
Я не очень много экспериментировал с другими шрифтами, но похоже, что с ними дело обстоит так же. Например, моя статья «Literate Programming» (см. The Computer Journal 27 (1984), 97-111), была набрана одной из разновидностей шрифта Times Roman, и стандартная макрокоманда \ТеХ давала прекрасный результат. В той же статье подрисуночные подписи и список литературы были набраны рубленым шрифтом Univers; для него, как и для cmssq, использовались значения (а,/?) = (—.2,0).
Генерирование избранных страниц
[Первоначально опубликовано в TUGboat 8 (1987), 217.]
В TUGboat Vol. 7, No. 3, Хелен Хорстман задала следующий вопрос: «Есть ли способ заставить Т^Х генерировать только определенные страницы?»
Недавно я добавил несколько новых строк в макропакет manmac (этот макропакет приведен в приложении Е и использовался при наборе томов А и С), которые позволяют включать в DVI-файл только избранные страницы. Соответствующие команды работают с практически любым макропакетом, будучи помещены в его конец, или, если угодно, в начало исходника основного документа.
Идея метода состоит в следующем. Первым делом TgX’y предлагается найти файл с именем pages .tex. Если выясняется, что такого файла нет, компиляция осуществляется как обычно. Изменения происходят, если файл pages.tex существует — в этом случае он должен содержать список номеров страниц, которые требуется сгенерировать, причем каждый номер должен быть записан в отдельной строке и номера должны следовать друг за другом в том порядке, в каком должны быть сгенерированы страницы. Все страницы с номерами большими, чем последний номер в списке, также будут сгенерированы. Так, если нужно скомпилировать страницу 123, а также все страницы, начиная с 300-й, файл pages.tex должен состоять из строк
123
300
Если же требуется сгенерировать только страницы 123 и 300, строки файла можно задать, например, вот так:
123
300
-999999999 % недопустимый номер
В этом случае номер, заданный последним, никогда не совпадет с текущим номером страницы, и конец списка никогда не будет достигнут.
По завершении процесса файл pages.tex следует переименовать, чтобы в дальнейшем он не влиял на формирование DVI-файла.
При работе под управлением UNIX рекомендуется инсталлировать Т^Х таким образом, чтобы поиск файлов, не найденных в текущем каталоге, производился затем в родительском каталоге. Тогда можно поместить файл pages.tex в специальный подкаталог pages и в случае, когда требуется сгенерировать только страницы, перечисленные в pages.tex, выполнять команду ‘cd pages’ непосредственно перед запуском ТвХ’а. Преимущество такого способа действий в том, что ни один из нужных файлов, находящихся в родительском каталоге, не будет по ошибке испорчен. В частности, не пострадают вспомогательные файлы, созданные с помощью команды \write в процессе генерирования предметного указателя, списка литературы или оглавления.
При выполнении рассматриваемых команд Т^ уведомляет пользователя, что работает в особом режиме.
Макрокоманды для генерирования избранных страниц
, Чтобы сгенерировать только часть страниц документа,
, запишите номера требуемых страниц в отдельных строках
, файла с именем pages.tex
\let\Shipout=\shipout
\newread\pages \newcount\nextpage
\openin\pages=pages
\def\getnextpage{\ifeof\pages\else {\endlinechar=-l\read\pages to\next \ifx\next\empty % в этом случае здесь должен быть
У, символ конца файла eof \else\global\nextpage=\next\fi}\fi}
\ifeof\pages % если файл pages.tex не найден,
У, не делать ничего
\else\message{OK, РИ ship only the requested pages!} \getnextpage\fi
\def\shipout{\ifeof\pages\let\next=\Shipout \else\ifnum\pageno=\nextpage
\getnextpage\let\next=\Shipout
\else\let\next=\Tosspage\fi\fi \next}
\newbox\garbage
\def\Tosspage{\deadcycles=0\setbox\garbage=}
Макро, написанные для Джил
[Первоначально опубликовано в TUGboat 8 (1987), 309-314.]
На конференции TUG в июле 1986 года я как-то упомянул в разговоре, что одна из моих обязанностей по дому —писать макро для моей жены Джил, которая незадолго до того установила Т^Х на свой компьютер. Когда вслед за тем Джил появилась на банкете, многие стали обращаться к ней с просьбой разрешить переписать макро. Это в конце концов навело меня на мысль, что макро нужно опубликовать в журнале TUGboat. Предлагаю вашему вниманию эти макро в слегка отредактированном виде.
Первая из задач, которые Джил поставила передо мной, была, пожалуй, наиболее интересной. Джил как раз тогда начала вести электронный дневник и хотела иметь его печатный вариант, хорошо оформленный. Формат, в котором она задумала печатать дневник, был не совсем обычным: она хотела, чтобы по ходу любого абзаца можно было создавать заметки на полях, причем так, чтобы на левой странице разворота эти заметки появлялись на левом поле, а на правой странице — на правом поле.
Эта задача для ТЕХ’а довольно трудна, так как ТеХ генерирует абзацы, не зная, на какие страницы они попадут. В самом деле, решение о том, что помещать на страницу 100, нельзя принять до того, как ТеХ сгенерирует значительную часть страницы 101.
Один из путей решения этой задачи — жульнический — состоит в том, чтобы помещать каждую заметку на оба поля, а затем маскировать ненужный экземпляр. Джил такой подход не понравился.
Честное решение задачи можно получить, заставив ТеХ обрабатывать исходный файл в два прохода. На первом проходе создается вспомогательный файл, в котором заметкам поставлены в соответствие их номера. На втором проходе этот файл считывается, и каждая заметка помещается на надлежащее поле. .
Хотя второй путь не так уж сложен, я решил использовать третий подход, который получился удивительно простым. Нетрудно запрограммировать ТЕХ так, чтобы все заметки записывались на левом поле или же чтобы все они записывались на правом поле. Используя это, мы в первом случае просим
ТЁК генерировать только левые страницы, во втором — только правые. За два прохода мы получаем то, что требуется.
В дневнике Джил текст заметок на полях вводился с помощью обозначения, основанного на конструкции, предложенной в приложении Е книги The ТвХЬоок, где похожее обозначение используется для задания элементов предметного указателя. Именно, запись *{заметка} при наборе дает на печати ‘заметка’ на полях и ‘заметка’ в основном тексте, а **{заметка} дает только ‘заметка’ на полях.
Вот текст соответствующего макро jmac.tex:
, Формат для дневника Джил
, пример исходного файла:
, \input jmac
, \title Новая глава на новой странице
.
, \date 32 мартобря
.
, Сегодня утром, проснувшись, я решила с помощью \ТеХ’а
, сделать из этого дневника книжку. Я люблю
, делать *{заметки} на поляхэто позволяет
, потом легко ориентироваться в тексте. Используя
, специальный прием, который придумал мой муж**{Дон},
% можно сделать так, чтобы на левых страницах
, эти заметки попадали на левое поле, а на правых
, страницахна правое.
.
, Для этого файл компилируется \ТеХ’ом
, {\it дважды\/}! **{двукратный запуск \ТеХ’а} При
, первом прогоне генерируются все страницы с
, нечетными номерами, при второмвсе
, страницы с четными номерами. Общее время, нужное
, для получения распечатки, увеличивается от этого не
, сильно, так как все равно дольше всего происходит
, вывод результата на принтер.
.
, В моем дневнике {\it *{нет формул}}.
.
. \Ьуе
, Каждый прогон \ТеХ’а начинается со следующего
, небольшого диалога:
\newif\ifleft
\def\lefthand{l }o
\message{************ Which pages do you want (1 or r)? } \read-l to\next% ответ пользователя (1 or r)
\ifx\next\lefthand\lefttrue\else\leftfalse\fi
\message{OK, I’ll produce only the \ifleft left\else right\fi-hand pages. }
% Команды, задающие формат печатной страницы
\frenchspacing
% междусловные пробелы после знаков У, препинания не увеличиваются
\hsize=5.25in
\baselineskip=14pt
, длина строки5.25 дюйма
, интерлиньяж (расстояние между
, базовыми линиями)14 пунктов
\parindent=Opt
\parskip=\baselineskip
, абзацный отступ не делается
, между абзацами вставляется пустая
, строка
\topskip=5\baselineskip
У, на каждой страницеспуск, % равный четырем строкам
\vsize=40\baselineskip
У, высота страницысорок строк
\setbox\strutbox=\hbox{\vrule height.75\baselineskip depth.25\baselineskip widthOpt} У, этострат ("распорка") на
У, высоту строки
\newdimen\titleoffset \titleoffset=l.5in \newdimen\notespace \notespace=.375in \newdimen\maxnote
\maxnote=2in
% заголовки выдвигаются на поля
У, расстояние между заметками
У, максимальная длина заметки
, Шрифты
\font\titlefont=cmbxlO scaled\magstep2У, для заголовков страниц
\font\datefont=cmbxlO scaled\magstephalf У, для дат на полях
\font\notefont=cmbxlOУ, для заметок на полях
\font\textrm=cmrlO scaled\magstephalfУ, для основного текста
\font\textit=cmtilO scaled\magstephalfУ, для выделений в тексте
\font\foliofont=cmbxlO scaled\magstephalf У, для номеров страниц
\let\rm=\textrm \rm
\let\it=\textit
\textfont2=\nullfont
, шрифт по умолчаниюпрямой (\гш)
, любой фрагмент текста либо \гш, либо \it
, математический режим не поддерживается
. Символы * и ** заменяются на \mnote, которая может быть видимой
, (visible) или невидимой (invisible)
\newif\ifvisible
\catcode ‘ V=\active
\def*{\futurelet\next\testdoublehat}
\def\testdoublehat{\ifx\next*\let\next=\silentnote
\else\visibletrue\let\next=\mnote\fi \next}
\def\silentnote*{\visiblefalse\mnote}
\ifleft\ifleftУе используется только в режиме вывода на
\ifleft
\ifleft
У, левое поле
\def\title#l\par{\vfill\eject\message{#l:}
\null\vskip-4\baselineskip
\moveleft\titleoffset\hbox{\titlefont\uppercase{#l}}
\vskip\baselineskip}
\def\date#l\par{\vskip\parskip
\moveleft\notespace
\llap{\hbox to\maxnote{\hfil\datefont#l\unskip}}
\nobreak\vskip-\baselineskip\vskip-\parskip}
\def\mnote#l{\strut\vadjust{\kern-\dp\strutbox
\vtop to\dp\strutbox{\vss \baselineskip=\dp\strutbox
\moveleft\notespace
\llap{\hbox to\maxnote{\hf il\notef ont#l}}\null}}e/e
\ifvisible#l\fi}
\hoffset=\titleoffset
\elseУ, используется только в режиме вывода на
У, правое поле
\def\title#l\par{\vfill\eject\message{#l:}
\null\vskip-4\baselineskip
\moveright\titleof f set\rightline{e/e
\titlefont\uppercase{#l}}
\vskip\baselineskip}
\def\date#l\par{\vskip\parskip
\moveright\notespace\rightline{6/e
\rlap{\hbox to\maxnote{\datefont#l\unskip\hfil}}}
\nobreak\vskip-\baselineskip\vskip-\parskip}
\def\mnote#l{\strut\vadjust{\kern-\dp\strutbox
\vtop to\dp\strutbox{\vss \baselineskip=\dp\strutbox
\moveright\notespace\rightline{6/e
\rlap{\hbox to\maxnote{\notefont#l\hf il}}}\null}}e/e
\ifvisible#l\fi}
\fi У, постраничная разбивка TeKCTa\dashодна и та же
У, в обоих режимах
У, Выводятся либо только левые, либо только правые страницы \output{\ifleft
\ifodd\pageno\discard\else
\shipout\vbox{\box255 \baselineskip=30pt
\hbox{\foliofont\folio}}\fi
\else\ifodd\pageno
\shipout\vbox{\box255 \baselineskip=30pt
\rightline{\foliofout\folio}}
\else\discard\fi\fi
\advancepageno}
\newbox\voidbox
\def\discard{\global\setbox255=\box\voidbox}
\outer\def\bye{\vfill\eject\deadcycles=O\end}
Дополним пример, приведенный в начальных строках макро jmac.tex, следующими командами, подчиняющими результат, получаемый на печати, формату настоящей книги:
В таком случае после двукратной компиляции файла-примера получим на печати следующий двустраничный документ:
\hsize=3.2in
\baselineskip=12pt
\vsize=12\baselineskip
\font\titlefont=cmbx!2
\font\datefont=cmbxl0
\font\notefont=cmbx9
\font\textrm=cmrlO
\font\textit=cmtilO
\font\foliofont=cmbxl0_
НОВАЯ ГЛАВА НА НОВОЙ СТРАНИЦЕ
32 мартобрязаметкиДон32 мартобрязаметкиДонСегодня утром, проснувшись, я решила с помощью Т£Х’а сделать из этого дневника книжку. Я люблю делать заметки на полях —это позволяет потом легко ориентироваться в тексте. Используя специальный прием, который придумал мой муж, можно сделать так, чтобы на левых страницах эти заметки попадали на левое поле, а на правых страницах— на правое.
32 мартобря
заметки
Дон
32 мартобря
заметки
Дон
1
двукратный запуск
Для этого файл компилируется ТЩХ’ом двасисдиХ При первом прогоне генерируются все страницы с нечетными номерами, при втором —все страницы с четными номерами. Общее время, требуемое для получения распечатки, увеличивается от этого не сильно, так как все равно дольше всего происходит вывод результата на принтер.
нет формул
В моем дневнике нет формул.
Вторая задача имела мало общего с первой и была связана с распечаткой рецептов домашней кухни. Дело в том, что в нашей семье такие рецепты обычно записывались на клочках бумаги, которые так в измятом виде и хранились, причем нередко терялись. Джил решила ввести рецепты в компьютер, с тем чтобы потом распечатать их на карточках. Тем самым мы привели бы свою коллекцию в порядок и смогли бы создавать специальные подборки рецептов для наших сына и дочери.
Для удобства и быстроты ввода информации Джил разработала специальную систему кодов. Для меня наибольший интерес представляла возможность реализовать эти коды с помощью «активных» ТеХ’овских символов. Хитрость состояла в том, чтобы определить макро в самом начале, до того как начнутся игры с активными символами — при этом старые значения символов не путались бы с их новыми значениями.
Следующий ниже файл rmac.tex почти не требует пояснений:
% формат для кулинарных рецептов
, пример исходного файла:
, \input гшас
. #ПРИПРАВЫ
, >Клюквенная приправа на День Благодарения
, <Уайлда Бэйтс Картер
, $3 стакана
, I оставить на ночь в холодильнике
. *
, @1 фунт свежей клюквы
, 2 апельсина очистить от кожуры и зернышек
, кожуру от одного апельсина измельчить
. 1 *1/2 ст. сахара
. *
, !Крупно перемолоть клюкву с апельсинами. Добавить цедру
, и сахар. Оставить в холодильнике на ночь.
. =
. #ХЛЕБЦЫ
. >Сырные сухарики
, [охлаждать не меньше 2-х часов, выпекать 20—25 минут
. У,300\0 F
, $60 штук
. *
, @1 баночка острого плавленого сыра (5 унций)
, *1/2 ст. масла
, *1/4 чайной ложки соли
, поперчить
, 1 *1/2 ст. муки
. *
. !Сбить вместе сыр и масло. Добавить остальные ингредиенты
, и растереть. Из полученного теста скатать два валика
, толщиной 1~*1/4 дюйма. Завернуть и охлаждать не меньше
, 2~часов. Нарезать ломтики по *1/4 дюйма, уложить их на
% несмазанный противень и выпекать 20—25 минут при
, температуре 300\0~F до легкого подрумянивания.
. =
. \Ьуе
\hsize=4.25in
\vsize=7in
\parindent=0pt
\font\classfont=cmbxl0 scaled\magstep2
\font\titlefont=cmbxl0 scaled\magstep2
\font\specfont=cmsll0 scaled\magstephalf ’/время, темп-pa, кол-во
\font\ingredfont=cmr7 scaled\magstep2
\font\normalfont=cmrl0
\newdimen\specbaseline
\specbaseline=14pt ’/ межстрочное расстояние для времени,
’/ темп-ры, кол-ва
\output{\shipout\vbox{\vbox to .75in{
\rightline{\classfont\currentclass\hskip-.25in}\vss}
\nointerlineskip\box255}
\advancepageno \global\let\currentdonor=\empty}
\let\currentdonor=\empty
\def\0{$*\circ$}’/ градусы
\obeylines
\def\class#l
{\gdef\currentclass{#l}}
\def\title#l
{{\message{#l}\titlefont#l\par}}
\def\donor#l
{\gdef\currentdonor{#l}}.
\def\time#l
{{\baselineskip=\specbaseline \rightline{\specfont#l\/}}}
\def\temp#l\0 F
{{\baselineskip=\specbaseline’/e
\rightline{\specfont#l\/\0 F\/}}}
\let\quantity=\time
\def\ingredients{\ingredf ont\everypar{\hangindent=20pt}}
\def\method{\let**M=\space \normalfont \everypar{}}
\def \endit{\par\vf ill’/
\ if x\current donor\empty’/
\else\rightline{\dash\currentdonor}\fi%
\еject\obeylines}
\def \frac#l/#2{\leavevmodee/e
\raise. 5ex\hbox{\the\scriptfontO #1}’/
\kern-. lem/\kern-. 15em’/e
Mower.25ex\hbox{\the\scriptfontO #2}}
\catcode‘\"=14" символ комментария
\catcode‘\#=\active \let#=\class " тип блюда, например, СУП \catcode‘\>=\active \let>=\title" название рецепта
\catcode<\<=\active \let<=\donor" источник рецепта
\catcode'\I=\active \let|=\time" время приготовления
\catcode'\7o=\active \let7o=\temp" температура приготовления
\catcode<\$=\active \let$=\quantity " количество получаемого " блюда
\catcode‘\@=\active \letQ=\ingredients " начало списка используемых продуктов
\catcode‘\!=\active \let!=\method " начало алгоритма приготовления \catcode'\*=\active \let*=\medskip" отбивка
(пробел)
\catcode‘V=\active \let~=\frac " числитель дроби \catcode'\==\active \let=\endit" конец рецепта
Обратите внимание на применение команды \ obey lines: используемые ингредиенты записываются по-строчно, а «метод приготовления» — в виде обычного текста, состоящего из одного-двух абзацев. По этой причине команда \method преобразует символы конца строки в пробелы. За методом следует символ «=», который завершает карточку и восстанавливает режим \obeylines.
При уменьшении \vsize до 2.4 дюйма, приведенный файл порождает на печати две карточки, показанные на следующей странице.
Поскольку ввод рецептов в компьютер был нами осуществлен в июле, пользоваться ими пришлось довольно-таки часто. Формат оказался очень удобным: рецепты легко читаются в процессе готовки, а то, что требуемые количества и способ приготовления выделены, помогает при планировании будущих меню.
Разумеется, в качестве следующего шага нужно было бы связать компьютер напрямую с кухонным оборудованием, с тем чтобы вся готовка производилась автоматически. Но, пожалуй, я всё-таки сперва закончу работу над The Art of Computer Programming.
Клюквенная приправа на День БлагодаренияКлюквенная приправа на День БлагодаренияПРИПРАВЫ
Клюквенная приправа на День Благодарения
Клюквенная приправа на День Благодарения
3 стакана оставить на ночь в холодильнике
фунт свежей клюквы
апельсина очистить от кожуры и зернышек кожуру от одного апельсина измельчить
^2 ст. сахара
Крупно перемолоть клюкву с апельсинами. Добавить цедру и сахар. Оставить в холодильнике на ночь.
—Уайлда Бэйтс Картер
ХЛЕБЦЫ
Сырные сухарики
охлаждать не меньше 2-х часов, выпекать 20-25 минут 300° F 60 штук 1 баночка острого плавленого сыра (5 унций)
У2 ст. масла
т/а чайной ложки соли поперчить
1 У2 СТ. муки
Сбить вместе сыр и масло. Добавить остальные ингредиенты и растереть. Из полученного теста скатать два валика толщиной 1 У4 дюйма. Завернуть и охлаждать не меньше 2 часов. Нарезать ломтики по V4 дюйма, уложить их на несмазанный противень и выпекать 20-25 минут при температуре 300° F до легкого подрумянивания.
Задача на субботнее утро
[Первоначально опубликовано в TUGboat 8 (1987), 73, 210.)
Эту головоломку предложил мне Сапе Муллендер из Центра математики и информатики в Амстердаме. Он сказал, что не сомневается в том, что «общий дизайн, предоставляемый Т^Х’ом, лучше дизайна, предоставляемого программой troff, но настоя- щий гуру может заставить troff делать вещи, которые вы никогда не ? сможете сделать в TgX’e». В качестве примера он показал мне стра- ницу, на которой troff вставил картинку в середину абзаца, окружив ее текстом. «Это получилось не очень изящно, но все же получилось, что нельзя сбросить со счетов», — сказал он. Что ж, должен отметить, что не задумывался над этим простым решением, пока не наступило утро следующей субботы, и я решил эту задачку еще до наступления полудня. Вы догадываетесь, как я набрал абзац, который сейчас читаете? (Ответ приведен ниже, но не заглядывайте в него, пока не решите эту задачку самостоятельно. Это не демонстрация превосходства T£X’a над troff ом, а представление неких интересных и полезных вещей.)
??????????????????????????????????????????????????????????????????????? nnimmnmmnnmnninmnnnmnnmmnimmnnnmnnimimnnnimnmmmim штппп
\font\bigfont=cmbxlO scaled \magstep5 \newbox\qmark \setbox\qmark=
\hbox{\raise6pt\hbox{\bigfont\thinspace?\thinspace}} \newdimen\leftedge \newdimen\rightedge
\leftedge=\hsize \advance\leftedge by-\wd\qmark \divide\leftedge by 2
\rightedge=\leftedge \advance\rightedge by\wd\qmark \parshape 10 Opt\hsize Opt\hsize Opt\hsize
Opt\leftedge \rightedge\leftedge
Opt\leftedge \rightedge\leftedge
Opt\leftedge \rightedge\leftedge Opt\hsize \newbox\partpage \newcount\n
\newdimen\savedprevdepth \savedprevdepth=\prevdepth \newdimen\savedvsize \savedvsize=\vsize
\begingroup \clubpenalty=0 \brokenpenalty=0
\output={\global\setbox\partpage=\vbox{\unvbox255\unskip}} \vfill\break
\topskip=\ht\strutbox \vsize=\topskip
\n=200 % we will store nine lines of text in boxes 201—209 \output={\global\advance\n by 1
\ifnum\n<210 \global\setbox\n=\box255
\else \unvbox\partpage \prevdepth=\savedprevdepth \vskip\parskip \box201 \box202 \box203 \box204 \vskip-\baselineskip \box205 \box206 \vskip-\baselineskip \box207 \box208 \vskip-\baselineskip \box209 \vskip-\baselineskip
\moveright\leftedge\hbox{\smash{\box\qmark}} \box255 \global\vsize=\maxdimen \fi}
\noindent Эту головоломку предложил мне Сапе Муллендер из Центра математики и информатики в Амстердаме. Он сказал, что не сомневается в том, что <<общий дизайн, предоставляемый \ТеХ'ом, лучше дизайна, предоставляемого программой {\it troff}, но настоящий гуру может заставить {\it troff\/} делать вещи, которые вы никогда не сможете сделать в \ТеХ'е>>. В качестве примера он показал мне страницу, на которой {\it troff\/} вставил картинку в середину абзаца, окружив ее текстом. <<Это получилось не очень изящно, но все же получилось, что нельзя сбросить со счетов>>, \cdash сказал он. Что ж, должен отметить, что не задумывался над этим простым решением, пока не наступило утро следующей субботы, и я решил эту задачку еще до наступления полудня. Вы догадываетесь, как я набрал абзац, который сейчас читаете?
(Ответ приведен ниже, но не заглядывайте в него, пока не решите эту задачку самостоятельно. Это не демонстрация превосходства \ТеХ'а над {\it troffРом, а представление неких интересных и полезных вещей.)
{\parfillskip=Opt\par} \global\savedprevdepth=\prevdepth \output{\global\setbox\partpage=\vbox{\unvbox255\unskip}} \vfill\break \endgroup \vsize=\savedvsize \unvbox\partpage \prevdepth=\savedprevdepth
% Более изящные решения приветствуются!
^Замечание: Алан Хёниг независимо получил значительно более общее, основанное на \vsplit решение, которое представлено в TUGboat 8 (1987), 211— 215.]
Упражнения к книге Т^Х: The Program
[Первоначально опубликовано в TUGboat 11 (1990), 165-170, 499-511.]
Весной 1987 г. я читал курс, в котором в качестве учебного пособия по исходному ТвХ’овскому тексту служил том В серии Computers &; Typesetting. Поскольку эта книга изначально писалась как справочник, а не как учебник, мне пришлось дополнить материал, содержащийся в ней, упражнениями для домашних работ, а также экзаменационными задачами.
Задачи эти оказались в итоге достаточно интересными и занимательными для людей, склонных к занятиям подобного рода, они могут оказаться также полезными для тех, кто изучает материал книги «ТеК: The Program» самостоятельно, а не в рамках соответствующего предмета, преподаваемого в колледже. Исходя из этих соображений, я отобрал 32 задачи из упомянутых выше и снабдил их ответами, которые, на мой взгляд, можно считать верными.
Последняя из предлагаемого списка задач, относящаяся к типографскому набору текстов на языках с большим количеством используемых в них литер, заслуживает особого внимания, поскольку речь идет о расширении Т^рСа, которое может оказаться полезным в азиатских странах.
Формулировки некоторых задач изменились по сравнению с первоначальными. Я отредактировал их таким образом, чтобы они оставались корректными и применительно к следующим изданиям рассматриваемой книги (для версий ТуК’а от 3.0 и выше); тем же, кто располагает изданием 1986 г., надо ознакомиться со списком исправлений, прежде чем изучать приведенные ниже ответы.
Задачи
Здесь представлены формулировки задач в том порядке, который мне кажется целесообразным. Хотя первые задачи из данного списка носят вводный характер, я не советовал бы их пропускать, даже если они покажутся слишком простыми —зачастую они содержат в себе некоторые тонкости. Некоторые задачи могут оставить впечатление фрагментарности, однако они нацелены на разъяснение важных сторон изучаемого предмета. Чтобы извлечь из решения
предлагаемых задач наибольшую пользу, надо самым серьезным образом пытаться довести решение каждой из них до конца самостоятельно и только после этого сверяться с ответом.
(Упражнение, связанное с чтением WEB-текста.) Что предшествует непосредственно строке ‘PROCEDURE INITIALIZE’ в паскалевской программе, описываемой в моей книге? (Конечно, это точка с запятой, однако есть еще несколько элементов, находящихся перед этой точкой с запятой, употребление которых вы должны понимать.)
Найдите необязательный макро в §15.
Предположим, что вместо строки "m2d5c215x2v5i" в начале процедуры print_roman_int стоит строка "m2d5c212q5v5i". Что будет печататься для входного значения 69? Для входного значения 9999?
Почему счетчик error-count имеет нижнюю границу значений, равную — 1?
Что печатается на пользовательском терминале после ввода ‘q’ в ответ на сообщение об ошибке? Почему?
Дайте примеры аварийного поведения ТЁХ’а в следующих случаях:
если в §108 исключить проверку 7 < 7230584’;
если в §108 убрать проверку ‘s > 1663497’;
если в §127 вместо условия ‘г > р + Г использовать ‘г > р’;
если в §127 исключить условие ^rlink^ ^Р’)
если из §125 убрать условие Чо_тет_тах4-2 < mem-bot+max-halfword ’.
Цель этой задачи — показать, что данные в тет могли бы породить следующий вывод из процедуры show.node-list:
\hbox(10.0+0.0)xl00.0, glue set lO.Ofill
100
.\discretionary replacing 1
200
. .\kern 10.0
300
.I\large U
10000
J Marge(ligature ff)
400,10001,10002
.\large !
10003
.\penalty 5000
500
.\glue 0.0 plus 1.Ofill
600
.\vbox(5.0x0.5)xl0.0, shifted -5.0
700
..\hbox(5.0x0.0)xl0.0
800
...\small d
10004
...\small a
10005
..\rule(0.5+0.0)x*
900
Пусть \large —шрифт с номером 1, а \small —шрифт с номером 2. Пусть также узлы, используемые в нижней (переменной по размеру) части mem, начинаются в позициях 100, 200 и т. д., как это показано выше; узлы, используемые в верхней (однословной) части mem, должны начинаться в позициях
10000, 10001 и т. д. Постройте диаграмму, которая показывала бы точное числовое содержимое каждого имеющегося слова mem, если min-quarterword = min-halfword = 0.’
Что будет напечатано программой short-display, если в предыдущей задаче внутри большого блока \hbox задать горизонтальный список, считая при этом, что первоначальное значение переменной font-in-short-display равно нулю?
Пусть сразу после инициализации INITEX исполняется следующий набор команд:
incr (preu-depth)-, deer (mode-line); incr(prev-graf); show_actwities;
Что будет отображено?
Что выдаст программа ‘show.eqtb^nt-base 4-17)’ после завершения процесса инициализации INITEX?
Пусть ТЁК’у даны следующие определения:
\def\a{\advance\day by l\relax} \def\g{\global\a}
Внутри ТЁХ’а это приведет при обработке командной последовательности \а к появлению вызовов процедуры
eq_word_define(p1 eqtb[p].int + 1),
и вызовов процедуры geq-word-define(p, eqtb\pYint + 1), где р = int-base 4- day-code, при обработке команды \g. Рассмотрим теперь следующие команды:
\day=0 \g\a{\a\g\a{\g\a\g}\a{\a}\a}
Каждый символ Ч’ вызывает процедуру newsaue-leuel (simple.group) а символ Ч’ —процедуру unsafe.
Объясните, что именно помещается в стек sauestack и извлекается из него, а также что запоминается в eqtb(p] и xeqJevel [р] в процессе выполнения приведенных выше команд. Каким будет окончательное значение командной последовательности \day? (См. The ТЁКЬоок\ упр. 15.9 и с. 360.)
Используйте обозначения с нижней части с. 122 книги ТЁК: The Program для описания содержимого списка лексем, отвечающего команде \! после ввода определения
\def\!!1#2![{!#]#!!2}
считая, что [, ] и ’ имеют соответствующие им коды (номера) категорий, равные 1, 2 и 6, точно так же, как и символы {, }, #. (См. упр. 20.7 в книге The T^Kbook.)
Каким будет значение абсолютного максимума для числа символов, напечатанных программой show-eqtb(every_par_loc\ если текущее значение переменной \everypar не содержит никаких управляющих последовательностей?
1 Здесь и далее страницы указаны по русскому переводу: Кнут Д. Е. Всё про Т^Х. — Протвино: АО RDT^X, 1993. — Прим, перев.
(Подсказка: значение ответа превышает 50. Вы можете проверить этот факт, запустив ТеХ для подходящего примера, относящегося к наихудшему случаю, а затем ввести следующие команды
\tracingrestores=l \tracingonline=l {\everypar{}}
поскольку в этом случае при перезаписи \ ever урат вызывается show.eqtb.)
Что сделает INITEX с входной строкой следующего вида (посмотрите на нее повнимательнее):
\catcode“=7 V< ‘(“)‘“!
Объясните причину появления сообщения об ошибке, если в plain TgX’e ввести
\endlinechar=‘! \error
Впишите пропущенное макроопределение так, чтобы для входного файла \catcode1?=\active
\def\answer{...}
\answer
при обработке его plain ТеХ’ом было в точности выдано следующее сообщение об ошибке:
! Undefined control sequence.
<recently read> How did this happen?
1.3 \answer
7
(Эта каверзная задача намного труднее, чем все предыдущие, но для ее решения существует по крайней мере три способа!)
Проанализируйте, что будет делать ТеХ при обработке следующего текста:
{\def\t{\gdef\a##}\catcode1d=12\tld#2#3{#2}}
\hfuzz=100P\ifdiml2pt=lP\expandafter\a
\expandafter\else\romannumeral888\relax\fi
\showthe\hfuzz \showlists
(Считайте, что здесь используются plain ТеХ’овскис коды категорий.)
Определите, в какие моменты при чтении данного текста будут вызываться сканирующие программы scan-keyword, scanJ/nt и scan-dimen^ объясните в общих чертах, что за результат будет выдан этими программами.
Есть ли разница в интерпретации Т^Х’ом команд
\thickmuskip=-\thickmuskip
\thickmuskip=-\the\thickmuskip
и в чем эта разница заключается? (Считайте, что здесь используется plain ТЁК.)
Каким образом изменится поведение ТЕХ’а, если оператор присваивания в конце §508 заменить на ‘5 (р = nuliy ?
Первоначальная реализация ТЁХ’а (ТеХ82) содержала значительно более простой вариант процедуры вместо той, что приведена в §601:
procedure dvLpop;
begin if dvi.ptr > 0 then
if dvi.buf [dvi.ptr — 1] = push then deer ^dvLptr^
else duLout^pop^
else dvi-Out^popY end;
(В параметре l необходимости не было.) Почему автору однажды стало стыдно и он заменил данный вариант на используемый сейчас?
Расставьте индексы d, у и z в последовательности целых чисел
2718281828459045
используя процедуру, набросок которой приведен в §604. (Это просто.)
Сформируйте короткий Т^’овский входной файл, который заставит подпрограмму print-mode напечатать ‘no mode’. (Предположение о том, что предварительно загружены plain Т^’овские коды категорий и макро, не использовать.) Дополнительные баллы получит тот, кто построит самый короткий файл, т. е. содержащий наименьшее число лексем, среди всех предложенных вариантов.
В §78 книги «ТУС: The Program» говорится, что процедура error может быть вызвана из нее самой в процессе вызова errorно рекурсия не может быть более глубокой, чем эта.
Постройте сценарий, в котором error вызывается трижды до того, как она будет завершена.
J.H.Quick (студент) думал, что он обнаружил ошибку в §671 и получит $327.68 при обработке входных текстов наподобие такого:
\vbox{\moveright lpt\hbox to 2pt{} \xleaders\lastbox\vskip 3pt}
(Он обнаружил, что T^X формирует этот vbox шириной 2 pt, но думал, что правильное значение ширины здесь равно 3 pt.) Однако, когда он ввел \showlists, то увидел, что элементы-заполнители пробелов (лидеры) определены просто как
\xleaders 3.0
.\hbox(0.0+0.0)х2.0
и обнаружил с сожалением оператор ‘shift-amount {cur-box) <— 0’ в §1081.
Объясните, каким образом следовало бы исправить §671, если бы параметр shift-amount блока, содержащего элементы-заполнители, имел бы ненулевое значение.
Когда ваш преподаватель готовил эту задачу, он ввел команду ‘\hbadness=-l’ с тем, чтобы Т^Х показывал способ, которым он будет разрывать каждую из строк. (У него (преподавателя) возникает иногда потребность проверить разрывы строк, не формируя графический образ соответствующей
страницы, например, когда работа с ТеХ’ом ведется с помощью терминала, не поддерживающего графический вывод.) Это вызывает появление на терминале следующего текста, выдаваемого ТеХ’ом:
Tight \hbox (badness 0) in paragraph at lines 297—301 []\tenrm When your in-struc-tor made up this prob-lem, h e gave the com-mand
Loose \hbox (badness 3) in paragraph at lines 297—301 \tenrm ^tentt \hbadness=-l\tenrm ’ so that T[] would pr int out the way each line of this
Tight \hbox (badness 0) in paragraph at lines 297—301 \tenrm para-graph was bro-ken. (He some-times wants to c heck line breaks with-
Loose \hbox (badness 14) in paragraph at lines 297—301 \tenrm out look-ing at ac-tual out-put, when he’s us-ing a ter-mi-nal that has no
Почему ничего не показано для последней строки рассматриваемого абзаца?
В чем заключались бы отличия в информации, выводимой Т^Х’ом, если изменить процедуру rebox путем удаления из нее оператора ‘if type^ = vlist_node then b hpackfb, natural^ 7 Каким был бы результат, если удалить условный оператор ‘if (is_char_Tiode(p^ ... ’? (Заметим, что блок b можно сформировать с помощью процедуры char-box.^
Какие пробелы вставит Т^Х между символами, когда будет набирать формулы $х==1$, $х++1$, and $х,,1$? Найдите те места в программе, где осуществляется принятие решений относительно формирования пробелов.
Когда ваш преподаватель готовил эту задачу, он ввел команду ‘\tracingparagraphs=l’ с тем, чтобы полученный файл протокола (transcript file) разъяснял ТЕ^’овские решения относительно разрыва строк применительно к этому абзацу. Он также ввел ‘\pretolerance=-l’, чтобы попытки переноса предпринимались немедленно. Полученный выход показан на следующей странице. Используйте его, чтобы определить, какие разрывы строк могли быть найдены с помощью упрощенного алгоритма, обрабатывающего текст по одной строке. (Этот упрощенный алгоритм находит такую точку разбиения, чтобы минимизировать дефекты первой строки, затем фиксирует ее и стартует снова.)
This is the paragraph-trace output for Problem 28: []\tenrm When your in-struc-tor made up this prob-lem, he gave the com- 6\discretionary via 660 b=145 p=50 d=36525 Ml: line 1.0- t=36525 -> MO mand
6 via MO b=0 p=0 d=100 662: line 1.2 t=100 -> 660 ‘\tentt \tracingparagraphs=l\tenrm * so that his -bran-script file would ex- 6\discretionary via 661 b=179 p=50 d=78221*
663: line 2.0- t=114746 -> 661u
^via 661 b=l p=0 d=10121 664: line 2.2 t=46646 -> 661
Т[]’8.
6 via 662 b=4 p=0 d=196 665: line 2.2 t=296 -> 662 line-breaking de-ci-sions for this para-graph. He also said 6 via 663 b=89 p=0 d=9801 666: line 3.1 t=124547 -> 663 ‘\tentt \pretolerance=-l\tenrm ’ so that hy-phen-ation would be tried im-me-di-ately. 6 via 666 b=ll p=0 d=441 667: line 4.2 t=124988 -> 666
The out-put is shown on the next page; use it to de-ter-mine what 6 via 667 b=318 p=0 d=117584 668: line 5.0 t=242572 -> 667 line 6 via 667 b=14 p=0 d=576 669: line 5.1 t=125564 -> 667 breaks would have been found by a sim-pler al-go-rithm that breaks 6 via 668 b=2 p=0 d=10144 6 via 669 b=295 p=0 d=93025 6610: line 6.0 t=218589 -> 669 off 6 via 668 b=31 p=0 d=11681 6 via 669 b=15 p=0 d=625 6611: line 6.1 t=126189 -> 669
6 via 669 b=26 p=0 d=11296 6612: line 6.3 t=136860 -> 669 line at a time. (The sim-pler al-go-rithm finds the break- 6\discretionary via 6610 b=607 p=50 d=383189 6613: line 7.0- t=601778 -> 6610 point
6 via 6610 b=80 p=0 d=8100
6 via 6611 b=503 p=0 d=263169 6614: line 7.1 t=226689 -> 6610 that
6 via 6610 b=0 p=0 d=10100
6 via 6611 b=20 p=0 d=900
6 via 6612 b=369 p=0 d=153641 6615: line 7.1 t=127089 -> 6611 yields
6 via 6612 b=0 p=0 d=100 6616: line 7.2 t=136960 -> 6612 fewest de-mer-its on the first line, then chooses it 6 via 6613 b=293 p=0 d=91809 6617: line 8.0 t=693587 -> 6613
6 via 6613 b=5 p=0 d=10225
6 via 6614 b=571 p=0 d=337561 6618: line 8.0 t=564250 -> 6614 starts
6 via 6614 b=2 p=0 d=144
6 via 6615 b=308 p=0 d=101124 6619: line 8.0 t=228213 -> 6615 6620: line 8.2 t=226833 -> 6614 over again.) 6\par via 6616 b=0 p=-10000 d=100 6\par via 6617 b=0 p=-10000 d=10100 6\par via 6618 b=0 p=-10000 d=10100 6\par via 6619 b=0 p=-10000 d=10100 6\par via 6620 b=0 p=-10000 d=100 6621: line 8.2- t=137060 -> 6616
Выполните прогон алгоритмов в частях 42 и 43, чтобы найти значения переменных trie-op, trie_char, trie-link^ hyf-distance, hyf_num и hyf_next после выполнения оператора
\patterns{albc 2bcd3 ablcd bcldd}
Затем исполните алгоритм из §923, чтобы увидеть, как ТЕХ использует эту эффективную TRIE-структуру при определении значений переменной hyf, когда осуществляется перенос слова aabcd. [Когда начинается §923, значение параметра hn будет равно 5, а значения элементов массива hc\l.. 5] будут равны (97,97,98,99,100), соответственно. Предполагается, что min-quarterword = Lhyf = r_hyf = 0.]
В момент завершения работы ТЕХ’а стек sauestack обычно пуст. Однако если, например, введенный пользователем текст содержит лишнюю скобку Ч’ (или пропущенную скобку ‘У), ТеХ выдаст сообщение
(\end occurred inside a group at level 1)
(см. §1335).
Объясните подробно, каким образом надо изменить ТеХ, чтобы такого рода сообщения были более информативными. Например, если исходный текст имеет несбалансированную скобку Ч’ в строке 2 и несбалансированную команду ‘\begingroup’ в строке 9, модифицированный вами ТеХ должен выдать такие два предупреждающих сообщения:
(\end occurred when \begingroup on line 9 was incomplete)
(\end occurred when { on line 2 was incomplete)
Вы можете считать, что в момент перехода к §1335 в стеке sa-uestack находятся только коды групп simple-group и semisimple-group. Если же обнаружены также какие-либо другие коды групп, следует вызвать процедуру confusion.
(Задача, излагаемая в данном пункте, является наиболее трудной из всех представленных здесь, но одновременно и наиболее важной из них. Это была основная задача для домашней проработки при подготовке к завершающему экзамену.)
Цель решения задачи 31 — так расширить T^jX, чтобы его легче было распространять в Китае, Японии и Корее. Такая расширенная программа, именуемая ТеХХ, позволяет работать со шрифтами, в которые входит до 65536 символов. Каждый символ из такого рода увеличенной таблицы (будем именовать его далее расширенным символом) представляется с помощью пары значений: «расширения» ос и «кода» с, причем оба эти параметра, х и с, принимают значения из диапазона от 0 до 255 включительно. Литерам с одним и тем же значением параметра ‘с’, но с различными значениями параметра ‘У отвечают различные графические образы, однако все они имеют одну и ту же ширину, высоту, глубину и поправку на курсив.
ТеХХ идентичен TEX’y, за исключением одной новой команды-примитива \xchar, включенной в его состав. Если команда \xchar встречается, когда ТеХХ находится в вертикальном режиме, начинается новый абзац; т. е. она работает в этом случае как «горизонтальная команда» со страницы 335 книги The Т^КИоок. Если же \xchar появляется в горизонтальном режиме, за ней должно следовать число (number) от 0 до 65535; это число может быть преобразовано в форму 256а; + с, где 0 < а;, с < 256. Из текущего шрифта будет взят соответствующий расширенный символ и присоединен к текущему горизонтальному списку, а показатель величины интервала (space factor) будет установлен равным 1000. (Если х = 0, воздействие \xchar аналогично таковому для команды \char, за исключением того, что \xchar отключает лигатуры и кернинг, а также не производит каких-либо специфических действий с показателем величины интервала. Кроме того, не добавляется никакого штрафа, даже если \xchar окажется символом переноса (\hyphenchar) для текущего шрифта.) Слово, содержащее расширенный символ, не должно переноситься. Команду \xchar нельзя использовать в математическом режиме.
Внутри TgXX’a расширенный символ (ж, с) из шрифта / представляется парой р и q последовательных элементов типа char .node, где принято font^ — null-font, character^ = qi{x\ linkup) = q, character{q) = qi^c), font(q) = f. Такое двухэлементное представление используется даже в том случае, когда х = 0.
Т^ХХ набирает некоторый расширенный символ, задавая номер символа 256а; + с в DVI-файле (см. команду set2 в §585.)
Если ТеХХ исполняется с макроопределениями plain ТЕХ’а и при этом пользователь введет ‘\tracingall \xchar600 \showlists’, выход ТЕХХ’а будет включать следующий текст
{\xchar}
{horizontal mode: \xchar}
{\showlists}
### horizontal mode entered at line 0
\hbox(0.0+0.0)x20.0
\tenrm \xchar"258
spacefactor 1000
(так как 600 есть "258 в шестнадцатеричной системе счисления).
Ваша задача — объяснить детально все изменения, которые надо внести в ТеХ, чтобы преобразовать его в Т^ХХ.
[Примечание: правильно построенное расширение ТЕ^’а должно включать также команды-примитивы \xchardef (аналогично \ char def) и \mathdef, поскольку язык должен быть «ортогонально завершенным». Однако это дополнительное расширение не включено в задачу 31 как ее составная часть, поскольку никаких специфических трудностей при этом не возникает. Если кто-то захочет понять, каким образом реализовать \xchar, можно заодно рассмотреть и реализацию \xchardef.]
В первом издании книги Т^К: The Program предполагалось, что расширенные символы могли бы быть представлены на основе следующего соглашения: первый из двух последовательных элементов char-node должен был содержать код шрифта и код символа, размеры которого должны были вычисляться обычным образом; второй из этих элементов, который должен был иметь тип halfword (полуслово), давал фактический номер печатаемого символа. Шрифты подразделялись на два типа, основываясь на характеристиках их TFM-заголовков; «восточные» (oriental) шрифты всегда использовали представление из двух слов, тогда как для всех других шрифтов применялась однословное представление.
Объясните, почему метод, предложенный в задаче 31, лучше чем этот. (Здесь имеются по крайней мере две причины.)
Ответы к задачам
Согласно указателю, процедура initialize объявлена в §4. Ей предшествует там (Global variables 13), а в §13 говорится, что окончательно эта глобальная переменная определяется в §1345. Обращаясь к §1345, находим там lwrite_loc: pointer V и комментарий. Этот комментарий не входит в паскалевский код. Мини-указатель в нижней части страницы 541 показывает, что 1 pointer1 — это макроопределение, объявленное в §115. Изучаемый нами вопрос почти исчерпан, поскольку в §115 сказано, что pointer развертывается в полуслово halfword, являющееся частью программы на Паскале. На странице xi говорится, что малые (строчные) буквы в WEB-программе заменяются соответствующими большими (прописными) в программе на Паскале; на странице xii сказано, что символ подчеркивания в ^writeJoc1 убирается. Следовательно, можно заключить, что словам ‘PROCEDURE INITIALIZE’ в программе на Паскале будут предшествовать непосредственно слова ‘WRITELOC: HALFWORD;’.
Однако это утверждение не совсем точно! Цитируемая книга кое-чего не договаривает. Если запустить программу TANGLE для обработки ТЕХ.WEB (не меняя файла), мы увидим, что словам ‘PROCEDURE INITIALIZE’ фактически предшествует строка вида
{1345:}WRITELOC:HALFWORD;{:1345}
поскольку программа TANGLE вставляет в формируемый текст комментарии, чтобы показать начало каждого из блоков программного кода (программы). Код здесь на самом деле берется из §1345.
Если обратиться к указателю, то можно увидеть, что не используется никогда макро doneG. (Он включен только для тех, кто захочет реализовать системно-зависимые изменения и/или расширения.)
Введенная новая строка заменяет «четвертаки» («quarters») q (имеющие значение 25) «гривенниками» («dimes») х (имеющими значение 10). Если «проиграть» код из §69, можно увидеть, что 69 теперь будет представлено как Ivvviv, а 9999 —как
ттттпппттт с m q с v q i v.
(Первые 9 символов m здесь дают 9000, затем cm дает еще 900, qc —75, vq — 20, a iv — оставшиеся 4).
Так сделано потому, что значение error-count может быть уменьшено на 1 в §1293, будучи увеличенным до того на 1 в §82 (Код в §1293 уменьшает значение счетчика error-count из-за того, что при исполнении команды \show используется подпрограмма error, хотя на самом деле ошибки и не было).
Вводимый символ q есть сокращение для команды «quiet» (отменить вывод), а не «quit» (завершение работы), для которой в §83 вводится сокращение Q. Этот символ q вызывает печать из §86 сообщения ‘OK, entering \batchmode’, после которого переменная selector уменьшается таким образом, чтобы ‘. . . ’ и (return) не печатались на терминале! (Он появится только в протокольной файле, если тот был открыт). Этот способ служит ТЁХ’у для выдачи подтверждения о том, что действительно включен режим \batchmode.
(а) При вычислении £*297 может произойти арифметическое переполнение, поскольку
7230585 х 297 = 231 + 97.
Требуется определенного рода проверка, чтобы предотвратить деление на нуль, когда s имеет значение положительное, но меньшее 297. Если s < 1663497, то
s div 297 < 5601,
и 7230585/5600 чуть больше, чем 1291, так что в данном случае можно записать г > 1290. Следовательно, пороговое значение должно быть выбрано таким образом, чтобы обезопаситься от деления на нуль в максимально возможной степени. (Один студент предложил заменить оператор ‘г <— £’ на ‘г <— 1291’. Это изменение может ускорить или не ускорить вычисления, в зависимости от используемого компьютера и компилятора для языка Паскаль. В машинном языке можно было бы использовать оператор goto, задающий badness <— inf-bad^ но для Паскаля это недопустимо.)
Если войти в§128сг=р+1, будет предпринята попытка создать узел размерности 1, но тогда не будет места для поля nodesize.
Если войти в §129 с единственным доступным узлом, мы потеряем всё и значение rover будет неправильным (Более старые версии ТвХ’а содержат более сложные проверки в §127, которые предотвратили бы переход к §129 в случае, если бы были доступны два узла. Эта предосторожность оказалась излишней.).
Это тонкий вопрос. Нельзя, чтобы «нижняя» часть памяти росла в такой степени, чтобы значение параметра nodesize превысило значение max-halfword, когда в §127 производится объединение (слияние) узлов.
100:
0
0
0
type (hlist_node), , link
101:
6553600
width (100 pt)
102:
0
depth
103:
655360
height (10 pt)
104:
0
shift-amount
105:
1
2
200
gluesign (stretching), glue_order (fill), list-ptr
106:
10
.0
glueset (type real)
200:
7
1
10003
type (disc-node), replacesount, link
201:
300
10000
pre-break, post-break
300:
11
1
0
type (kern-node), subtype (explicit), link
301:
655360
width (10 pt)
400:
6
0
0
type (ligature-node), , link
401:
1
11
10001
font, character, lig.ptr
500:
12
0
600
type (penalty-node), , link
501:
5000
penalty
600:
10
0
700
type (glue-node), subtype (normal), link
601:
8
0
glue-ptr (filLglue), leader.ptr
700:
1
0
0
type (vlist-node), , link
701:
655360
width (10 pt)
702:
32768
depth (0.5 pt)
703:
327680
height (5 pt)
704:
-327680
shift-amount (—5 pt)
705:
0
1 01
|800
gluesign (normal), glue-order (normal), list-ptr
706:
0.
.0
glueset (type real)
800:
0 1
1 0
|900
type (hlist.node), , link
801:
655360
width (10 pt)
802:
0
depth
803:
327680
height (5 pt)
804:
0
shift-amount
805:
° 1
1 0
|10004
gluesign (normal), glue-order (normal), list-ptr
806:
0.
.0
glueset (type real)
900:
2 I
1 0
10
type (rule_node), , link
901:
-1073741824
width (null-flag)
902:
0
depth
903:
32768
height (0.5 pt)
10000:
1
85
400
font, character ("U"), link
10001:
1
102
10002
fimt, character ("f”), link
10002:
1
102
0
font, character ("f"), link
10003:
1
33
500
font, character ("!"), link
10004:
2
100
10005
font, character ("d"), link
10005:
2
96
0
font, character ("a"), link
(Американцы норвежского происхождения узнают здесь шутку «Uff da».) Выходом процедуры short-display будет:
\large Uff []
поскольку short-display показывает доразрывную и послеразрывную последовательности символов в команде \discretionary, но не замещающий текст. Если, однако, этот бокс будет выводиться процедурой hlist-out, такого рода управление разрывами строк не будет эффективным; в качестве результата получим бокс шириной 100pt, начинающийся с большого знака Т и заканчивающийся маленькими символами ‘da’, которые будут иметь размер 5 pt и подчеркнуты линейкой шириной 0.5 pt.
Так как значением для аргумента prev-depth первоначально будет ignore_depth, получим
### vertical mode entered at line 1 (\output routine) prevdepth -999.99998, prevgraf 1 line
Согласно §236, в int-base + 17 запоминается значение mag. (Одно из определений, замененное многоточием на с. 101, есть определение тад; вы можете проверить этот факт, анализируя указатель!) Начальное значение для тад устанавливается в §240. Следовательно, show-eqtb произведет условный переход (ветвление) к §242 и напечатает ‘\mag=1000’.
В приводимой ниже диаграмме ‘(з)’ есть значение на уровне три, а символ ‘—’ показывает границу уровня:
(2)
9
(1) (1)
66(1)(1)(1)(1)(1)
88888
(1) (1) (1)(1)(1)(1)(1)(1)(1)(1)(1)
44444444444
(1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1)2222222222222
save stack;
xeq_level\pY (i) (i) (i) (i) (2) (i) (2) (2) (1) (3) (1) (1) (2) (2) (3) (2) (2) (i)eqtb\p\int; 01223455678899 10 9 10 8day=0 \g \a { \a \g \a { \g \a \g } \a { \a } \a }
Окончательным значением будет, следовательно, \day=8.
(reference count), match !, match #, left-brace [, end-match, left-brace {, mac-param #, right-brace ], mac-param !, out-param 2, left-brace [. Обратите внимание, что left-brace перед end-match повторяется в конце замещающего текста, исходя из условия согласованности (и, следовательно, удаляется из входного текста).
Согласно §233, процедура show.eqtb (every _pardoc) вызывает программу show_tokenJist с ограничением I = 32. В соответствии с §292, нам необходим список лексем, содержащий лексему, которая печатает возможно большее число символов при tally = 31; значение tally увеличивается при каждом обращении к print-char (§58). Изучая случаи, перечисленные в §294, можно заключить, что наихудший случай будет иметь место тогда, когда печатается тас-рагат, и когда литера с фактически печатается как четыре литеры. Оператор ‘pnnLesc("ETC.")’ в §292 напечатает восемь добавочных литер, если текущее значение escape-char представляет собой другую четверку. (Более длинные примеры возможны только в случаях, когда Т^Х содержит ошибку, которая запустит выходы ‘\CLOBBERED. ’ или ‘\BAD. ’ в §293, однако этого здесь не может произойти.)
Другими словами, пример, соответствующий наихудшему случаю, в частности, такой, как
\escapechar=<\"df \catcode‘V~d0=6
\everypar{1234567890123456789012345678901~~d0etc.}
в совокупности с предлагаемой текстовой строкой вызовет выдачу на печать вида
{restoring
~~dfeverypar=1234567890123456789012345678901~~d0~~d0~~dfETC.}
подтверждая этим, что show.eqtb (every _раг_1ос) может напечатать 60 литер.
Здесь надо обратить внимание на процедуру get-next, которая сканирует buffer несколько странным образом, если обнаруживаются два идентичных символа категории 7 (sup_markY После того как код категории \ cat code открывающей кавычки устанавливается равным 7, процедура geLnext начинает сканировать управляющую последовательность в §354, которая переходит в §355 и находит пробел после ‘ ‘. Так как пробел имеет код '40, он изменяется на 'ЦО, а содержимое буфера сдвигается влево на 2. По странному совпадению, 'ЦО — опять литера «открывающая кавычка», так что мы вернемся опять в §355, который изменит ‘ ‘ ( на h и вернется к startles в третий раз. Теперь мы перейдем к §356 и затем назад, к §355 и к starLcs, изменив “) на i. Аналогичным образом, в четвертом цикле, литерная цепочка ‘ ‘ ‘ будет заменена на пробел, а пятый цикл завершит выполнение управляющей последовательности.
Если мы попытаемся ввести указанную в условии задачи строку, INITEX остановится с выдачей сообщения
! Undefined control sequence.
<*> \catcode<<=7 \hi
!\error
Это подтверждает, что buffer в данный момент содержит \hi !.
Сообщение об ошибке, которое появится в данном случае, имеет вид
! Undefined control sequence.
<*> \endlinechar=<! \error
и наша задача состоит в том, чтобы объяснить появление в нем цепочки символов "М. Стандартное значение, устанавливаемое для \endlinechar, есть carriage-return (согласно §240); в соответствии с §22, этому отвечает восьмеричное значение '15, а '15 есть **М в коде ASCII. Таким образом, в конец каждой строки, когда она читается в buffer, помещается символ carriage-return (см. §360). Этот символ не печатается обычно в сообщении об ошибке, поскольку совпадает с символом end_line_char (см. §18). В приведенном выше сообщении он появился вследствие того, что было изменено значение символа end_line-char.
Между прочим, если бы входная строка имела вид
\endlinechar=<’\error
(без пробела после литеры ’), мы не увидели бы литерной цепочки "М в выданном сообщении об ошибке. Почему? А потому, что ТЁК вызывает процедуру get_next, когда видит необязательный пробел после AS СП-константы ‘! (см.§442-443), поэтому неопределенная управляющая последовательность \error будет обнаружена до того, как произойдет изменение значения символа end_line_char\
Одна из проблем здесь состоит в том, чтобы выявить, какая именно управляющая последовательность здесь будет неопределенной; судя по всему, это ‘?’, поскольку данный символ был сделан активным. Возможная зацепка обнаруживается при просмотре §312 и §314, из которых следует, что сообщение ‘<recently read>’ может быть выдано на печать, только если base_ptr = input.ptr, state = token-list, token-type = backed-up, and loc = null. Список лексем типа backed-up обычно содержит единственный элемент; в этом случае имя управляющей последовательности должно быть ‘How did this happen?’, и мы имеем дело с задачей помещения некоторого активного символа в имя управляющей последовательности.
Однако список лексем типа backed-up, обладающий произвольной длиной, может быть порожден и операцией \lowercase (см. §1288). В этом случае, однако, правая фигурная скобка, завершающая операцию \lowercase, почти всегда все еще присутствует в ТуСовском входном состоянии, что может вызвать появление сообщения об ошибке. (Процедура back-list из §323 не очищает стек от завершенного обработкой списка лексем.) Необходимо заставить ТЁК очищать его стек перед сканированием символа }.
С этого места рассматриваемое упражнение начинает напоминать задачу «retrograde chess». Имеется одно решение, показанное ниже в виде двух строк текста, разбитых таким образом, чтобы помещаться на страницах данной книги, хотя на самом деле, если строго следовать условиям задачи, необходимо построить эквивалентное однострочное представление в качестве строки с номером 2 фактического входного файла:
\def\answer{\let~\expandafter\lccode‘! = ‘He/fl [line broken] ~\lowercase~{~!~o~w~ ~d~i~d~ ~t~h~i~s~ ~h~a~p~p~e~n~?}}
(Символ ‘H’ есть представление для ‘!’ «в нижнем регистре»; цепочка \expandaf ter используется для удаления правой фигурной скобки из стека.)
В другом подходе используется \csname и генерируется ? из !:
\def\answer{\def \a##l{{\global\let##l?\aftergroup##l}}e/, \escapechar ‘ H\lccode ‘ j ?\lowercase{\expandaf ter\a'/,
\csname ow did this happen!\endcsname}}
Имеется, однако, и некий «жульнический» способ решения, значительно более короткий — переделать неотображаемый символ возврата каретки, следующий за \answer, в правую фигурную скобку(!):
\def\answer{\catcodel3=2\lccode‘ ! = ‘Не/, \lowercase\bgroup!ow did this happen?}
(Оказалось, что решение этой задачи объяснить в классе намного труднее, чем я ожидал, поэтому для решения ее студентами она получилась труднее, чем предполагалось. После первой моей попытки объяснить данный ответ, я решил сделать специальную версию Т^Х’а, которая помогла бы в объяснении процессов работы программ сканирования. Эта специальная программа, получившая наименование DemoT^X, почти в точности соответствует обычному ТЁ^’у, за исключением того, что при \tracingstats>2 пользователь имеет возможность наблюдать работу ТкХ’овских синтаксических программ в «замедленном воспроизведении». Необходимые изменения, преобразующие ТеХ в DemoTEX, разъясняются в приложении, приводимом ниже. С помощью DemoTEjX’a вначале был предпринят ряд попыток решения простых примеров, вроде ‘\hf uzz=l. 5pt’ и ‘\catcode ‘ а=1Г, прежде чем погрузиться в задачу 17, в которой встречается сразу несколько различных случаев. Кстати, когда мы обсуждали входные стеки, было обнаружено, что полезно рассмотрение поведения Т£Х’а при обработке следующего входного текста:
\output{\botmark}
\def\a{\error}
\mark{
\everyvbox{
\everypar{
\everydisplay{
\everyhbox{
\everymath{\noexpand\a}
$\relax}
\hbox\bgroup\relax}
$$\relax}
\noindent\relax}
\vbox\bgroup\relax}
\hbox{}\vfill\penalty-10000
Здесь \penalty производит переключение в команде \botmark, определяющей \everyvbox и начинающей исполнение команды \vbox, которая определяет \everypar и начинает выполнение \раг, определяющей, в свою очередь, \everydisplay и начинающей исполнение \display, и т. д.)
Вернемся опять к задаче 17. Первая строка здесь имеет вид
\gdef\a#ld#2#3{#2}
где второй символ d имеет категорию кода 12 ^оШет_сИат\ следовательно, это второе d будет соответствовать символу d, который порождается командой \romannumeral. В этой строке процедура scan-int будет вызвана только для сканирования ‘d и числа 12.
При обработке второй строки вызывается процедура scan-dimen, которая производит просмотр правой части оператора присваивания в команде \hf uzz. После того как scan-dimen для чтения литерной цепочки ‘100’ использует процедуру scan-int^ она вызовет процедуру scan-keyword, чтобы определить вид используемой единицы измерения. Однако до того, как для единиц станет известен их вид (‘pt’ или ‘рс’), должно быть произведено раскрытие \ifdim. Здесь нам придется вызвать scan-dimen рекурсивно (дважды), эта процедура обнаружит значение 12 pt в левой части выражения, причем ее исполнение будет опять прервано, когда процедура scan-keyword попытается определить единицы измерения в правой части выражения. Теперь литерная цепочка из \expandafter станет причиной того, что \romannumeral888 будет развернуто в dccclxxxviii, и затем мы получим грамматический разбор
\a\else dccclxxxviii
за которым следует \relax\f i. Здесь аргумент #1 будет иметь значение \else, аргументы #2 и #3 —значение с; тогда раскрытие дает cclxxxviii\relax\f i. Первое ‘с’ завершает второе ‘Рс’, а проверка \if dim дает значение \true. Следовательно, второе ‘с’ может завершать первое ‘Рс’, а значение параметра \hfuzz устанавливается равным 1200pt. Литеры Ixxxviii теперь стоят в начале абзаца. Команда \f i удаляет \if dim из ТЁХ’овского стека условий.
Дополнительная информация по данной теме содержится в даваемом ниже приложении. Примеры, подобные этому, проливают свет на некоторые странные вещи, которые могут быть найдены в тесте TRIP, представляющем собой достаточно запутанный образец необычного кода, используемый для проверки корректности реализаций TgX’a.
Если, например, \thickmuskip имеет значение 5mn plus 5mu, как оно устанавливается plain Т^Х’ом, первая из приводимых команд изменит это значение на -5mu plus -5mu, поскольку scan-glue в §461 вызовет процедуру scansomething-internal со вторым аргументом, имеющим значение true; это приведет к тому, что все три компоненты клея будут сделаны отрицательными (см. §413, §430, §431).
Вторая команда, в отличие от первой, приказывает TgX’y развернуть ‘\the\thickmuskip’ в последовательность литер, так чтобы она была эквивалентна присваиванию
\thickmuskip=-5mu plus 5mu
Знак «минус» не войдет в растяжимую компоненту клея, так как §461 применяет операцию negate (сделать отрицательным) только к первой найденной размерности.
Эта задача иллюстрирует хорошо известную опасность, заключающуюся в любой системе развертывания макроопределений путем текстовых подстановок.
Мы получим забавный результат, состоящий в том, что тексты двух макро можно было бы рассматривать как один общий текст, объединение которого из двух исходных осуществляется посредством команды \if х, если первый из этих текстов (он запускается при q, когда начинается исполнение §508). не является соответствующим префиксом для второго из рассматриваемых текстов. (Обратите внимание на оператор 1р <— null? внутри цикла while.)
Это произошло из-за того, что байт в dvi-buf [dvLptr — 1] обычно не будет являться кодом операции, а это может случиться, когда значение данного байта равно значению параметра push.
2У 7d Id 8Z 2^ 8Z Id 8Z 2^ 8Z 4^ 5Z 9d 0d 4^ 5Z.
TgX будет находиться в режиме ‘no mode’ только при обработке операторов \write, а вид (наименование) режима будет печататься во время выполнения \ write только в случае, когда tracing-commands > 1 при выполнении процедуры expand. Можно было бы подумать, что потребуются операции \catcode, с тем, чтобы существовали левая и правая фигурные скобки для \ write; однако имеется возможность заставить работать в этой ситуации ТЁХ’овский механизм обработки ошибок! Следовательно, наиболее короткой программой, удовлетворяющей предъявленным требованиям, будет, по-видимому, следующая, основанная на идее Ronaldo Ama, который предложил поместить в файл такую строку
\batchmode\tracingcommands2\immediate\write! \nomode
(Семь лексем в общей сложности).
Когда процедура error вызывает get-token, а это произойдет в случае, если пользователь запрашивает удаление лексем (см.§88), возможен второй уровень вызова error, но больше удаления уже не допускаются (см.§336 и §346). Вставки, однако, разрешаются по-прежнему, что может привести к третьему уровню вызова для error, когда overflow вызывает процедуру succumb.
Пусть, например, max_in_open = 6. Тогда можно ввести текст ‘\catcode'?=15 \х’ и ответить на появляющееся сообщение об ошибке (Undefined control sequence.), вводя шесть раз строку ‘i\x??’. Это приведет к вызову процедуры error, в котором появляются шесть уровней c<insert>’; отсюда имеем, что гп.ореп = 6, еще одна вставка будет требуемой «последней каплей». В данной точке процесса введем ‘1’; это переведет error на второй уровень, из которого ‘i’ вызовет вход в error в третий раз. (Стек периода исполнения содержит теперь процедуру main-control, из нее вызывается get-X-token, из get-X-token — expand, из expand — error, из error — get-token, из get-token — get-next, из get-next — error, из error — begin-flle_reading, из begin_flle_reading — overflow и из overflow — error .^
Надо заменить 1 width (дУ на 1 width (д) 4- shift-amount(дУ (дважды). Аналогичные изменения требуется произвести в §656. (Однако блок не должен обладать возможностью сохранять значение параметра shift-amount для него; эта величина является элементом списка вовлеченных блоков, а не описания блока как такового.)
Завершающая строка имеет бесконечную растяжимость, так как plain ТЕХ устанавливает значения \parf illskip=Opt plus Ifil. Сообщения о блоках свободных или тесных, с недозаполнением или с переполнением никогда не будут выдаваться, пока в §658 и §664 принято о = normal.
Если некоторый блок, сформированный в вертикальном режиме (vbox), переупаковывается как блок, формируемый в горизонтальном режиме (hbox), мы в самом деле получим некие странные результаты, поскольку элементы, относительно которых ожидается, что они занесены в стек как упорядоченные вертикально, оказываются размещенными горизонтально. Второе изменение будет существенно менее заметным, за исключением символов, подобных У, где вводится большая поправка на курсив; подобного рода символы будут центрироваться, не принимая во внимание поправок на курсив, установленных для них. (Соответствующая поправка на курсив в математическом режиме представляет собой разность между горизонтальной позицией нижних и верхних индексов в формулах вроде V22-)
Интервалы могут быть найдены, если ввести
$х==1$ $х++1$ $х,,1$ \tracingall\showlists.
Большая часть решений принимается в §766 при помощи таблицы интервалов из §764. Однако в случае со знаком операции + ситуация будет более запутанной, поскольку любой элемент в bin.noad должен быть с обеих сторон окружен элементами соответствующего класса. В рассматриваемой формуле $х++1$ статус второго знака + будет изменен с bin.noad на ord.noad в §728. Это обернется вставкой широких (thick) пробелов после х и перед 1 в строке lx == 1’; средней величины (medium) пробелов перед каждым из знаков + в строке 1х 4- 4-1’; узких (thin) пробелов после каждой запятой в строке ‘а;,, Г. (Запятая в математическом режиме появляется в том месте, где она размещается в знаке «точка с запятой» математического курсивного шрифта; см. с. 502 книги The TEXbook.)
Поведение более простого алгоритма, который будем именовать Brand X, может быть выведено из значений «показателя дефектности») (demerit value) (‘d=’) в трассировке ТЁХ’овского выходного текста. Наилучший выбор для первой строки равен, очевидно, @@2 (значение показателя дефектности равно только 100 единицам, против 36525 для @@1); затем @@4 для следующих строк. Однако при таком выборе возникает ситуация, когда мы должны допустить наличие очень разреженной строки, которая кончается при @@6. Отсюда следует перейти к @@7; после чего Brand X решает, что лучше всего перейти к @@9, затем к @@11 (лучший из трех возможных вариантов), затем к @@15,
после чего к @@19, и, наконец, к @@21. В результате абзац, сформированный и набранный при помощи Brand X, будет выглядеть наподобие следующего (ужасного) текста:
: 28. When your instructor made up this problem, he gave the command
• ‘\tracingparagraphs=r so that his transcript file would explain TeX’s \ line-breaking decisions for this paragraph. He also said :‘\pretolerance=-r so that hyphenation would be tried immediately.1 ^The output is shown on the next page; use it to determine what line: breaks would have been found by a simpler algorithm that breaks off one line at a time. (The simpler algorithm finds the breakpoint that yields fewest demerits on the first line, then chooses it and starts . over again.)
Это упражнение требует определенного времени на его выполнение, однако анализируемые структуры данных весьма интересны, ТеХ’овский алгоритм переноса слов представляет собой прекрасный небольшой фрагмент изучаемой нами программы, который может исследоваться независимо от остальных ее частей.
Формируются следующие таблицы:
op
char link
[1]
[2]
[3]
trie [1]
0
0
2
hyf-distance
2
0
3
trie [99]
0
97
3
hyf.num
1
3
2
Me [100]
0
98
7
hyf.next
0
0
2
trie [101]
0
98
4
trie [103]
1
99
5
Me [105]
1
100
0
Me [106]
0
99
8
Me [108]
3
100
5
Задавая слово aabcd, интересно пронаблюдать, как в §923 из этой TRIE- структуры порождаются в данном случае требуемые индексы переносов ‘oaoa2bicod3’.
Идея решения состоит в том, чтобы в стек сохранения заносить номера строк. Скотт Дуглас обнаружил, что хотя ТеХ и фиксирует самым тщательным образом всю последовательность значений переменной cur-boundary, ничего существенного с этими значениями никогда не делается. Отсюда вытекает, что поле save-index в словах описания границ уровней не является необходимым, так что в нашем распоряжении имеется дополнительное полуслово! (Существующая структура данных содержит «ископаемые» элементы, оставшиеся от прошлых «инкарнаций» ТЁХ’а). Номера строк, однако, могут иметь длину большую, чем полуслово; представляется более целесообразным использовать для их сохранения целые числа длиной в полное слово.
Эта задача требует внесения изменений в три фрагмента рассматриваемой нами программы. Во-первых, надо дополнить §1063 следующим образом:
(Cases of main-control that build boxes and lists 1056) +=
[Случаи main-control, которые создают боксы и списки]
поп-math (left-brace): begin saved(0) *— line; incr(save_ptr);
new_save_level(simple_group);
end; { номер строки сохраняется для возможного использования в предупреждающих сообщениях }
any_mode(begin-group): begin saved(ti) *— line; inerts ave.ptr);
nevj-save-level(semisimple-group);
end;
any-mode (end-group): if cur.group = semi_simple_group then begin unsave; decr(save.ptr);
{ выталкивание неиспользуемого номера строки из стека } end
else offsave;
Аналогичные изменения требуется внести в §1068, где производится обработка применительно к первому из случаев, перечисленных в §1063.
simple-group: begin unsave; decr(save_ptr);
{ выталкивание неиспользуемого номера строки из стека } end;
И, наконец, заменим шесть строк в §1335 аналогичными строками, но с текстами сообщений, соответствующими рассматриваемой задаче:
while cur-level > level-one do
begin print_nl(" ("); print-esc("enduoccurreduwhenu");
case cur-group of
simple-group: print.char ("{");
semisimple-group: print-esc ("begingroup");
othercases confusion ("endgroup")
endcases;
print("uonulineu"); unsave; decr(save_ptr); print-int(saved(0));
print ("uwasuincomplete) ");
end;
while cond-ptr / null do
begin print_nl(" ("); print-esc("enduoccurreduwhenu");
print_cmd_chr (if-test, cur-if);
Во-первых, §2 дает новый абзац, объясняющий, что представляет собой ТЁХХ, а заголовочная строка, печатающаяся при запуске ТЁК’а, заменяется следующей:
define banner = 'ThisuisuTeXX,uVersionu2.2' {печатается при запуске ТЁ^’а}
Затем добавляем два новых определения в §134:
define is-xchar-node(#) = (font(tt) = font-base) {это расширенная char-node? } define bypass-xchar (#) ='
if is-xchar-node (#) then # link(tt)
(Надо сказать, что здесь вместо null-font используется font-base, поскольку идентификатор null-font до того момента не был определен.)
Программа short-display из §174 может трактовать \xchar подобно обычному символу (литере), поскольку со стороны процедуры print-ASCII никаких
ограничений не накладывается. Ниже показан один из способов, которым можно осуществить требуемое изменение:
procedure short-display(р : integer); {печатает выделения в списке р} label done;
var n: integer; {для замены номеров}
ext: integer; { величина, добавляемая к коду литеры посредством xchar }
begin ext 0;
while p > mem_min do
begin if is_char_node(p) then
begin if p < mem_end then
begin if is .xchar .node (p) then
begin ext *— 256 * (qo (character (p))); goto done;
end;
if font(p) ^ font-inshort-display then
begin if (font(p) < font-base) V (font(p) > font-max) then print-char ("♦")
else (Print the font identifier for font(p) 267);
[Печать идентификатора шрифта для font (р)] print-char ("и"); font-inshort-display font(p);
end;
print-ASCII(ext 4- qo(character(p))); ext 0;
end;
end
else (Print a short indication of the contents of node p 175);
[Печать краткого указания на содержимое узла р]
done \ р Ипк(р);
end;
end;
Похожим образом вносятся изменения и в §176:
procedure print-font_and_char(p : integer); {печать данных char-node } label reswitch;
var ext: integer;
{величина, добавляемая к коду литеры посредством xchar, или — 1} begin ext <1;
reswitch: if р > mem-end then print-esc("CLOBBERED.") else begin if is_xchar_node (p) then
begin ext *— qo (character (p)); p link(p); goto reswitch; end;
if (font(p) < font-base) V (font(p) > font-max) then print-char ("*")
else (Print the font identifier for font(p) 267);
[Печать идентификатора шрифта для font(p)\ print-char ("и”);
if ext < 0 then print-ASСП(qo(character(p)))
else begin print-esc ("xchar"); print-hex (ext * 256+ qo (character (p)));
end;
end;
end;
(Эти программы должны обладать сверхустойчивостью.) Первая строка программного кода в §183 будет иметь теперь следующий вид:
if is.char.node (p) then begin print.font.and.char (p); bypass.xchar (p);.
end
В §208 вводится новый код операции
define xchar.num = 17 {расширенная литера ( \xchar ) }
Каждый код операции, который следует за указанными в §208 и §209, от math.char.num до max-command, необходимо увеличить на 1. Надо также добавить в §265 и §226, соответственно, строки кода вида:
primitive (11 xchar ", xchar.num, 0);
xchar.num: print.esс("xchar");
Они вводят новые команды в список команд TgXX’a.
Следующее, о чем надо позаботиться — это предусмотреть действия на случай, когда во входном файле появится \xchar. Удобно добавить в §435 процедуру с именем scan .xchar.пит, которая будет парной (companion procedure) для процедуры scan.char.num:
procedure scan.xchar.num;
begin scan.int;
if (cur.val < 0) V (cur.val > 65535) then begin print.err("Baducharacterucode"); help2 ("Anu\xcharunumberumustubeubetweenLI0uandlj255. ") ("Iuchangeduthisuoneutouzero."); int.error(cur.val); cur.val <— 0; end;
end;
Аналогичным образом в §582 добавляется функция new.xchar, парная к функции new .character*.
function new_xchar(f : internal.font.number; c : integer): pointer;
var p, q: pointer; { вновь размещенные узлы } begin qnew .character (f, c mod 256);
if q = null then new.xchar *— null-
else begin p *— get.avail; font(p) *— font-base; character (p) *— qi((cdiv 256)); link(p) 4— q;
new-xchar p;
end;
end;
Расширенные символы будут выводиться подобающим образом, если заменить начальные строки программного кода в §620 на следующие:
reswitch: if is.char.node(р) then begin synch.h; synch.v; repeat if is.xchar.node (p) then begin / 4— font (link (p));
if character (p) = <p(0) then p link(p);
{ пренебрежение нулевым расширением } end
else / 4— font (p);
c 4— character (p);
if / т^ dvi.f then (Change font dvi.f to / 621);,.
[Замена шрифта dvi.f на /]
if is.xchar.node (p) then
begin dvi.out (setl 4- 1); dvi.out (qo(c))] p *— linkup); c *— character (p); end
else if c > дг (128) then dvi.out (setl);
dvi.out (qo^c))]
Многие из обрабатывающих программ включают оператор вида V <— font(#)’, который требуется исполнять только в том случае, если не принимается во внимание (bypassing) первая половина расширенного кода символа. Этого можно достичь, если вставить следующие операторы:
bypass.xchar (р)в§654;
bypass.xchar(s)в§842;
bypass.xchar (сиг.р) в§867;
bypass.xchar (s)в§871;
bypass.xchar (р)в§1147.
В §841 необходимо делать несколько больше, чем просто игнорировать часть кода символа:
if is.char.node (v) then
begin if is.xchar.node (v) then
begin v <— UnkQuy decr(ty {xchar считается за две литеры } end;
Для того, чтобы отключить переносы в словах, содержащих расширенные символы, необходимо внести следующие два изменения. Во-первых, вставим if hf = font-base then goto donel; { is_xchar_node (s) } после lhf <— font(sy. в §896. Затем заменим ‘endcases;’ в §899 на endcases
else if is.xchar.node (s) then goto donel;
Если \xchar появится в математическом режиме, для обработки ошибки в список соответствующих случаев в §1046 необходимо включить mmode 4- xchar.num. Если же \xchar появляется в вертикальном режиме, надо, чтобы начинался новый абзац, что достигается включением vmode 4- xchar.num во второй список вариантов в §1090.
Но что произойдет при появлении \xchar в горизонтальном режиме? Для обработки данной ситуации требуется переписать §1122.
Нам потребуются два дополнительных элемента, обеспечивающие, в совокупности с имеющимися, работу в горизонтальном режиме, а именно, примитивы \xchar и \accent.
(Cases of main.control that build boxes and lists 1056 ) +=
[Случаи main.control которые создают боксы и списки]
hmode 4- xchar.num: begin scan_xchar_num;
link(tail) 4— new_xchar(cur.fontcur.vaiy
if link (tail) / null then tail 4— link (link (tail));
space-factor <— 1000;
end;
hmode + accent: make.accent;
Необходимо, далее, изменить make-accent таким образом, чтобы можно было печатать акцентированные расширенные символы. (В условии задачи 31 явным образом этого не говорится, однако ТЁХХ, разумеется, должен обеспечивать такую возможность.) Это означает, что в §1124 следует добавить новый вариант:
else if cur.cmd = xchar.num then
begin scan-xchar-num; q *— new-xchar(f, cur-val);
end
а также произвести изменения в начале и конце §1125.
(Append the accent with appropriate kerns, then set p *— q 1125) = [Присоединение акцента с подходящими кернами с последующей установкой p*—q\ begin tslant(f)/float-constant (65536);
if is-xchar-node(q) then г <— char-info(f)(character(link(q)))
else г <— char.info (f)(character (q));
w char-width (f)(i);
subtype (tail) acc-kern; link(p) tail;
if is-xchar-node(q) then {в этом случае мы хотим пренебречь xchar }
begin taiLappend(q); p <— link(q);
end
else p *— q;
end
Основная причина, по которой подход, предложенный в решении задачи 31, является более предпочтительным, состоит в том, что операция введения поправки на курсив (§1113) была бы чрезвычайно затрудненной при другой схеме. Среди других достоинств можно указать следующие, (а) Деление на 256 потребуется только один раз; главный цикл TgXX’a останется быстрым. (Ь) Требуется внесение в Т^Х сравнительно небольшого числа изменений, так что аналогичные идеи могут быть внесены без особых затруднений и в другие варианты ТЁХ’а. (с) Поскольку не требуется разделения шрифтов на «восточные» и «западные», команда \xchar имеет широкую применимость. Например, она дает пользователю способ подавить лигатуры и кернинг; она позволяет также работать с большими шрифтовыми наборами с 256-символьными подмножествами общеупотребительных знаков, (d) Соглашения, введенные в T£XX’e, сопрягаются с задействованными в GF-файлах, порождаемых METAFONT’om.
Единственным недостатком, присущим подходу, реализуемому в Т£ХХ’е, является то, что для всех символов, коды которых кратны 256, требуется иметь одинаковый размер бокса. Это, однако, не слишком значительное затруднение.
Приложение
Решение задачи 17 отсылает читателя к специальной версии Т^К’а, называемой DemoT^X, которая позволяет получать подробную информацию о процессе сканирования. DemoT^X получен путем внесения ряда изменений в части 24-26 ТЕХ’а.
Во-первых, в §341 между строками со словами kexit*? и ‘end’ надо поместить следующий текст:
if tracing stats > 2 then
begin к 4— trace-depth;
print.nl ("");
while к > 0 do
begin print("u");
decr(k);
end;
print (" I");
print.char ('V);
if cur.cs > 0 then
begin print.cs (cur-cs);
print.char ("=");
end;
print.cmd.chr (cur.cmd cur.chr);
end;
(В соответствующем месте должна быть объявлена новая глобальная переменная, именуемая trace-depth и инициализируемая нулевым значением. Она используется для формирования отступов, выделяющих строки вывода DemoTEX’a с учетом уровня вложенности подпрограмм.)
В начале подпрограммы expand (в §366) помещаем операторы
incr (trace-depth);
if tracingstats > 2 then
print ("u<x");
этот фрагмент печатает ‘<x’, когда процедура expand начинает выполнять действия по развертыванию какого-либо макроопределения. Аналогичные операторы вставляются в начало процедур scan.int (§440), scan-dimen (§448) и scan-glue (§461). Разница состоит в том, что scan.int будет печатать ‘<i’, scan.dimen — ‘<d’, a scan.glue — ‘<g’. (Понимаете?) Вставим также добавочный код в конец каждой из упомянутых процедур:
deer (trace-depth);
if tracingstats > 2 then print.char (">");
что дает возможность ясно видеть, когда работает каждая из составных частей сканирующей программы.
Аналогичным образом теперь следует доработать процедуру scan-keyword, однако в ней явным образом указывается, какое ключевое слово является предметом поиска. Код
incr (trace_depth);
if tracing stats > 2 then
begin print("u<'");
print (s);
print-char ("'");
end;
вставляется в начало §407, а фрагмент
if tracingstats > 2 then print-char ("♦");
exit; deer (trace-depth)',
if tracingstats > 2 then
print _char (">'')*,
end;
заменяет код в его конце. (Здесь ‘*’ обозначает ‘успех’, т. е. отмечает тот факт, что требуемое ключевое слово найдено.)
В качестве примера ниже приводится начало того текста, что выводится ВетоТЁХ’ом при сканировании правой части (после знака равенства) выражения для \hfuzz в задаче 17:
I the character = <d
I the character 1 <i
I the character 1
I the character 0
I the character 0
I the letter P>
I the letter P <,em’
ItheletterP><’ex’
ItheletterP><’true’
ItheletterP><’pt’
ItheletterP
I \ifdim =\ifdim <x <d
I the character 1 <i
I the character 1
I the character 2
I the letter p>
I the letter p <,em’
Itheletterp><’ex’
Itheletterp><’true’
Itheletterp><’pt’
Itheletterp
I the letter t*>
I the character =>
После обнаружения символа ‘=’ T^jX вызывает процедуру scan-dimen. Следующим после знака равенства символом будет ‘1’; scan_dimen возвращает эту литеру назад и вызывает процедуру scan.mt, которая обнаруживает последовательность литер ‘100’ и т. д. Этот фрагмент листинга демонстрирует тот факт, что ТЁК часто использует процедуру back-input для того, чтобы повторно прочитать символ в ситуациях, когда система еще не вполне готова иметь дело с этим символом.
Благодарности
Я хотел бы поблагодарить прекрасных студентов моего экспериментального класса за то, что они побуждали меня думать над этими вопросами, приставали ко мне, когда предлагаемые вопросы невозможно было понять, а также за многочисленные улучшения, предложенные к первоначально дававшимся ответам.
Мини-указатели для самодокументированных программ
[Первоначально опубликовано в Software — Concepts and Tools 15 (1994), 2-11.]
В этой главе описывается подход к документированию программ, облегчающий чтение и понимание больших программ и наборов программ путем организации локальных предметных указателей для всех идентификаторов, присутствующих на каждом двухстраничном развороте. Подход подробно поясняется на примере программы, которая отыскивает все гамильтоновы контуры в неориентированном графе.
Введение
Пользователи систем, подобных WEB [4], поддерживающей процессы создания структурированной документации и самодокументированного программирования [7], получают автоматически отпечатанный указатель, размещенный в конце программы и показывающий, где определяется и используется каждый из идентификаторов. Такого рода указатели в высшей степени полезны, однако они могут оказаться слишком громоздкими, особенно для длинных программ. Убедительным примером такого рода может служить листинг Т^Х’овской программы [5], в котором подробный указатель вхождений идентификаторов занимает 32 страницы, набранные мелким шрифтом.
Читатели книги [5] могут, однако, достаточно быстро находить требуемые места в программе благодаря тому, что
... правые страницы каждого разворота этой книги содержат миниуказатели, что позволяет не слишком часто обращаться к большому (общему) указателю. Каждый из идентификаторов, используемый где-либо в пределах двух страниц рассматриваемого разворота, дается в сноске, размещаемой на правой странице разворота, если только он не определен или объявлен в явном виде где-либо на левой или правой странице данного разворота. Эти имена в сносках указываются вместе с типами объектов, которым они принадлежат (процедура, макро, булевская переменная и т. д.) [5; 7, с. 183]
Аналогичная идея используется иногда в изданиях литературных текстов для студентов, изучающих иностранный язык. В такого рода изданиях формируются мини-словари необычных слов, размещаемые на каждой странице [12]. Это позволяет студенту экономить много времени, поскольку отпадает необходимость рыться в больших словарях.
Идея мини-указателя впервые была подсказана автору Джо Уинингом. Он подготовил небольшой макет, показывающий, какой вид мог бы иметь такой указатель [14]. Его предложение привлекло меня и я решил реализовать его в виде отдельной программы, названной TWILL — это имя подчеркивало тот факт, что данная программа представляла собой двухпроходный вариант программы WEAVE. Программа TWILL была использована в сентябре 1985 г. при подготовке книг 7^: The Program [5] и METAFONT: The Program [б].
Первоначально система WEB основывалась на комбинации из ТЁХ’а и языка Паскаль. Однако к настоящему времени я предпочитаю использовать систему CWEB [8], которая объединяет ТЁК и язык С. (На самом деле, CWEB в версии 3.0 полностью совместима с С-н-, хотя я обычно ограничиваю себя подмножеством этого языка, которое можно было бы назвать С —.) Одно из достоинств CWEB состоит в том, что обеспечивается работа с наборами небольших программных модулей и библиотеками, которые можно комбинировать самыми разными способами. Единичный исходный CWEB-файл foo.w может порождать несколько выходных файлов в дополнение к С-программе foo.c; например, foo.w может породить заголовочный файл foo.h для использования другими модулями, которые будут загружены объектным кодом foo.o, а также может сформировать тестовую программу testf оо. с, предназначенную для проверки переносимости полученной программы.
Система CWEB использовалась при разработке Stanford GraphBase —набора из примерно трех с половиной десятков свободно распространяемых программ, полезных при изучении комбинаторных алгоритмов [13]. Эти программы были недавно опубликованы в виде книги, опять же с мини-указателями в ней [9]. Мини-указатели в данном случае были подготовлены с помощью программы CTWILL [10], представляющей собой двухпроходный вариант программы CWEAVE.
Цель данной главы —объяснить, как работает программа TWILL и созданная на ее основе программа CTWILL. Усвоение предлагаемых идей можно существенно упростить, если объяснять их, рассматривая подходящий, достаточно подробный иллюстративный пример, реализованный в виде полностью завершенной CWEB-программы. Такого рода пример описывается в следующем разделе данной работы. Затем в двух разделах рассматривается процесс обработки этого примера программой CTWILL и ТЁХ’ом, после чего в завершающем разделе излагаются некоторые заключительные комментарии.
Пример
CWEB-программа, для которой формируются примеры мини-указателей, была написана специально для данной работы и именуется НАМ. Она перебирает все гамильтоновы контуры графа, т. е. все его неориентированные циклы, включающие каждую вершину графа ровно один раз. К примеру, данная программа позволяет определить, что существует ровно 9862 ходов конем на шахматной доске размера б х б клеток, если пренебречь симметриями доски; на рабочей станции SPARC station 2 решение такой задачи займет около 2.3 с. Поскольку программа НАМ представляет и самостоятельный интерес, она приведена полностью в приложении к данной главе (см. с. 241-245).
Прежде чем читать дальше, рекомендую вам бегло просмотреть программу НАМ. Эта программа представлена на пяти страницах с использованием двухколонного набора. Каждая из страниц именуется «разворотом», по аналогии с двухстраничными разворотами в книгах [5], [б], и [9]. Такое разделение материала дает нам возможность сформировать пять мини-указателей вместо двух при обычном наборе, что делает НАМ вполне подходящим примером несмотря на относительно небольшие размеры данной программы. Для более короткой программы указатель может не потребоваться вообще, а более длинная программа будет вызывать затруднения при ее чтении.
НАМ предназначена для использования совместно с библиотекой программ, поставляемых в составе пакета Stanford GraphBase. По этой причине в §1 данной программы вызывается препроцессор языка С, чтобы подключить заголовочные файлы gb_graph,h и gb_save.h. Эти заголовочные файлы определяют внешние функции и типы данных, необходимые для использования библиотеки GraphBase.
Краткого введения в структуры данных пакета GraphBase будет вполне достаточно для интересующегося читателя, чтобы в деталях понять работу программы НАМ. Задаваемый граф представляется тремя видами записей struct, именуемых Graph (Граф), Vertex (Вершина) и Аге (Дуга). Если г указывает на запись Vertex, то v-name будет представлять собой строку, именующую вершину, представленную посредством о, a v-arcs указывает на представление первой из дуг, выходящей из этой вершины^ Если а указывает на некоторую запись Аге, соответствующую дуге, соединяющей некоторую вершину о с некоторой другой вершиной и, то строка a-tip указывает на запись Vertex, которая представляет и; таким же образом a-next указывает на представление следующей дуги, выходящей из о, или a-next = Л (т. е., NULL), если а —последняя из дуг, исходящих из v. Тогда имена всех вершин, смежных с вершиной о, можно отпечатать с помощью следующего цикла:
for (а = о-arcs; а; а = a-next)printf (”7.s\n”, a-tip-name)*,Произвольное неориентированное ребро между вершинами и и и представля-ется посредством двух дуг: из вершины и в вершину и и из и в и. Наконец,
‘ггщате’ на самом деле вводится с клавиатуры как cv->name’ в программе на С или CWEB; применяемое типографское ухищрение облегчает чтение отпечатанного текста программы.
если д указывает на запись Graph, то д-п представляет собой число вершин в соответствующем графе, а записи Vertex, представляющие эти вершины, расположены в местах, задаваемых выражением g-^vertices + fc, для 0 < к < д-п.
Запись Vertex содержит также «служебные поля» (utility fields), которые могут использоваться различными способами при работе различных алгоритмов. Фактически С-декларация (описание) этих полей, позаимствованная из §8 и §9 программы GB_ GRAPH [9], имеет следующий вид:
typedef union {
struct vertexstruct *V;
/* указатель на Vertex */
struct arcstruct *A;
/* указатель на Arc */
struct graphstruct *G;
/* указатель на Graph */
char *5;
/* указатель на строку */
long /;
/* целое */
} util;
typedef struct 'vertexstruct { struct arcstruct *arcs;
/ * связный список дуг, исходящих из данной вершины * / char *пате;
/* строка, символически обозначающая эту вершину */ util u, v, w, х, у, г;
/* многофункциональные поля */
} Vertex;
Программа НАМ для повышения эффективности работы использует первые четыре из списка служебных полей. Поле и, например, трактуется как целое число типа long, представляющее собой степень рассматриваемой вершины. Обратите внимание на определение deg как макро в §2; оно дает возможность ссылаться на степень вершины v как v-deg, вместо менее удобоваримого варианта ‘и-гиГ, с которым фактически имеет дело С-компилятор. Аналогичные макро для служебных полей v, w и х можно найти в §4 и §6.
Первый из мини-указателей для программы НАМ, отвечающий программному коду из §2, размещенному на первом развороте, дает перекрестные ссылки для всех идентификаторов, появляющихся в §1 или §2, но не определяемых в них. Например, идентификатор restore-graph, упоминается в одном из комментариев из §1, мини-указатель сообщает нам, что это функция, возвращающая значение типа Graph *, и что она определяется в §4 другой CWEB-программы с именем GB.SAVE. Данный мини-указатель упоминает также о том, что определения для Vertex и arcs содержатся в §9 программы GB_ GRAPH (из которой мы и позаимствовали определения, упоминавшиеся выше), а также что поля next и tip записей Аге определяются также в GB_ GRAPH, в ее §10, и т. д.
В этом первом мини-указателе содержится одна тонкость и она заключается в том, как описывается вхождение идентификатора и, относительно которого сказано, что он отвечает служебному полю, определенному в §9 программы GB. GRAPH. Идентификатор и фактически в §2 появляется дважды, в первый раз в определении для deg, и второй —как переменная типа Vertex *. Мини-указатель ссылается только на первое из этих двух определений, поскольку второе из них дается в §2. Мини-указатель не содержит упоминаний для идентификаторов, определяемых на том же развороте, для которого формировался данный указатель.
Второй мини-указатель, следующий за §5 программы НАМ, аналогичен рассмотренному выше первому. Отметим, что в нем содержатся два вхождения для о, поскольку этот идентификатор используется в двух смыслах —как служебное поле (в определении для taken^ и как некая переменная (во всех остальных местах). С-компилятор поймет, что делать с конструкциями наподобие ^-ч).! = О’, которые С-препроцессор раскроет в виде lv-taken = О’, однако для живого читателя здесь будет иметь место определенное затруднение.
Обратим внимание на элемент указателя для идентификатора deg во втором мини-указателе. В нем вместо двоеточия используется знак равенства, показывая, что deg представляет собой макро, а не переменную. Аналогичное обозначение было использовано и в первом мини-указателе при записи перекрестных ссылок па идентификаторы определений типов, подобные Vertex. Взгляните также па элемент для not-taken в четвертом мини-указателе, где обозначение 1 not-taken = macro ()’ показывает, что not-taken представляет собой макроопределение с аргументами.
Работа программы CTWILL
Было бы хорошо, если бы можно было утверждать, что программа CTWILL формирует мини-указатели для НАМ в полностью автоматическом режиме, подобно тому как программа CWEAVE делает это при формировании обычных указателей. Это, однако, было бы неправдой. Истина же состоит в том, что автоматически CTWILL выполняет примерно 99% объема работы, пользователю в сложных случаях приходится помогать ей.
Почему же так получается? Во-первых, CTWILL недостаточно сообразительна, чтобы прийти к заключению, что ‘и’ в определении deg в §2 не то же самое, что V, объявленное как register Vertex * в том же самом разделе. Потребовалось бы встроить в CTWILL достаточно развитые средства искусственного интеллекта, чтобы она могла формировать подобного рода умозаключения.
Во-вторых, CTWILL не знает, каким образом ей сформировать элемент указателя для идентификатора к, появляющегося в §6. Переменная к не объявляется нигде! Пользователи, которые пишут комментарии, привлекая при этом выражения вроде 1ДхУ, могут ссылаться или не ссылаться на идентификаторы / и/или х в своих программах; поэтому они должны дать знать CTWILL, когда употребляют подобного рода «проходные» обозначения, не требующие индексирования (т. е. внесения в указатель). При работе с программой CWEAVE
такого рода затруднений не возникает, так как она для однобуквенных идентификаторов индексирует только определения, а не факты использования таких идентификаторов.
В-третьих, программа CTWILL не распознает автоматически ситуацию, когда параметр uert в определении для not-taken из §4 не имеет ничего общего с макро uert, определенным в §6.
Четвертое осложнение, которое не возникло при обработке НАМ, но случавшееся в [5] и [9], заключается в том, что разделы WEB- или CWEB-программ могут использоваться неоднократно. Вследствие этого, отдельный идентификатор может фактически ссылаться на несколько переменных одновременно. (См., например, §652 в [5].)
В общем случае, когда идентификатор определяется или объявляется в точности один раз и используется только в связи с этим его уникальным определением, осложнений при использовании CTWILL не должно возникать. Однако, если некоторый идентификатор имеет более одного определения, в явной или неявной форме, CTWILL может только предполагать, какое из этих определений подразумевается в том или ином случае. Некоторые из идентификаторов, в особенности однобуквенные, вроде х и у, играют слишком значительную роль в больших наборах программ, чтобы ограничивать возможные их применения каким-либо одним вариантом. В связи с этим, CTWILL разрабатывалась так, чтобы в тех случаях, когда необходимо сделать выбор, давать пользователю соответствующие подсказки.
Наиболее важной чертой подхода, реализованного при разработке CTWILL, является стремление к возможно большей предсказуемости действий, выполняемых данной программой по умолчанию. Чем «интеллектуальнее» мы попытаемся сделать систему, тем труднее будет управлять ее работой. В силу этого, CTWILL имеет очень простые правила, позволяющие решать, что следует поместить в мини-указатель.
Каждый идентификатор имеет единственное текущее значение (current meaning), состоящее из трех частей: типа этого значения, имени программы и номера раздела в ней, где данный идентификатор определяется. В начале процесса исполнения CTWILL читает некоторое количество файлов, содержащих начальные определения для текущих значений. Всякий раз после этого, когда CTWILL обнаруживает С-конструкцию, приводящую к изменению значения — макроопределение, объявление переменной, определение типа, объявление функции или обнаруживает метку, за которой идет двоеточие — производится присваивание нового текущего значения так, как это задается семантикой языка С. Например, когда CTWILL видит строку ‘Graph *д’ в §2 программы НАМ, текущее значение для идентификатора д изменяется на ‘Graph *, НАМ §2’. Рассматриваемые изменения будут осуществляться в порядке, задаваемом чтением исходного CWEB-файла, а не в том «запутанном» порядке, которому следует на самом деле С-компилятор. Поэтому CTWILL не будет пытаться строить вложенные определения, отвечающие блочной структуре анализируемой программы, все делается в строго последовательном духе.
Будет считаться, что некоторая переменная, объявленная в §5 и §10, получила значение в §5 и сохраняет его таковым в §6, §7, §8, §9.
Как только CTWILL меняет текущее значение какой-либо переменной, производится выдача соответствующей записи об этом действии во вспомогательный файл. Для CWEB-программы ham.w имя этого вспомогательного файла будет ham. aux. Первые несколько строк-элементов в файле ham. aux имеют вид
@$deg {ham}2 =macro@> @$argc {ham}2 \&{int}©> @$argv {ham}2 \&{char} ${*H\,]$@>
а последняя строка выглядит так:
@$d {ham}8 \&{register} \&{int}@>
В общем случае элементы данного вспомогательного файла имеют вид: trident lname")-nn type@>
где ident — идентификатор; пате и пп — соответственно имя программы и номер раздела в ней, где содержится определение для ident; type— строка из ТЁХ’овских команд, указывающая тип идентификатора. Вместо фрагмента ^пате'Упп элемент аих-файла может содержать символьную строку ("string"); в этом случае имя программы и номер раздела в формируемом элементе будут заменены данной строкой. (Результатом применения этого средства будет, например, появление имени заголовочного файла <stdio.h> в элементах мини-указателя для программы НАМ, соответствующих идентификатору print/ ?) В ряде случаев значением поля type будет ‘\zip’. Эта ситуация не возникает при обработке программы НАМ, не слишком она была характерной и для [9], однако она случается, например, если препроцессорное макроимя определяется как внешнее, как это имеет место в Makefile, или же в случаях очень сложных типов, вроде типа FILE в <stdio.h>. В таких случаях в миниуказатель будет внесена запись наподобие ‘FILE, <stdio .h>’ без двоеточия или знака равенства.
Пользователь имеет возможность явным образом изменить требуемое текущее значение, записав ^ident ^пате"Упп type®> в любом месте CWEB- программы. Это значит, что механизм умолчания, применяемый CTWILL, будет просто обойден.
Когда CTWILL начинает обрабатывать некую программу foo.w, она ищет первым делом файл с именем fоо.aux, который мог быть сформирован при предыдущем запуске программы CTWILL. Если файл foo.aux уже есть, CTWILL читает его и команды @$. . . @>, содержащиеся в нем, устанавливают текущие значения для всех идентификаторов, определенных в foo.w. Вследствие этого, CTWILL может располагать значением некоторого идентификатора еще до того, как он был объявлен —предполагая, что CTWILL успешно обработала foo.w до этого момента хотя бы один раз, а также считая, что окончательное определение рассматриваемого идентификатора то же самое, что и сформированное в начале программы.
CTWILL просматривает также еще один вспомогательный файл, именуемый foo.bux. Этот файл не перезаписывается при каждом прогоне CTWILL, и может модифицироваться пользователем. Цель применения файла foo. bux — присвоить начальные значения идентификаторам, которые не определены в foo.aux. Например, файл ham.bux содержит две строки
Qi gb_graph.hux @i gb_save.hux
которые сообщают программе CTWILL о том, что необходимо прочитать файлы gb_graph.hux и gb_save.hux. Эти файлы содержат определения для идентификаторов, появляющихся в заголовочных файлах gb_graph.h и gb_save.h, которые программа НАМ подключает к §1. Например, одна из строк в файле gb_graph.hux имеет следующий вид:
@$Vertex {GB\_\,GRAPH}9 =\&{struct}@>
Эта строка имеется также в файле gb_graph. aux; она была скопирована вручную, с использованием текстового редактора, в файл gb_graph.hux, поскольку Vertex —один из идентификаторов, определенных в gb_graph.h.
CTWILL читает также файл с именем system.bux, если таковой имеется. Этот файл содержит глобальную информацию, являющуюся одной из важных составных частей текущей среды, в которой исполняется программа. Одна из строк в файле system.bux имеет, в частности, следующий вид:
@$printf ”<stdio.h>” \&{int} (\,)@>
После того как файлы system.bux, ham.aux и ham.bux прочитаны, CTWILL становятся известными почти все идентификаторы, присутствующие в программе НАМ. Единственным исключением будет идентификатор к, обнаруженный в §6, текущим значением которого будет \uninitialized, и если пользователь не предпримет соответствующих корректирующих действий, отвечающий этому идентификатору элемент мини-указателя будет выглядеть как
к: ???, §0.
Заметим, что идентификатор d объявлен в §4 программы НАМ, а потом еще и в §8. Оба этих описания породят элементы, помещаемые в файл ham. aux. Поскольку CTWILL читает файл ham. aux еще до того, как просматривает исходный файл ham.w, а также из-за того, что ham.aux читается последовательно, к началу обработки ham.w текущее значение для d будет определяться согласно §8. Это не создает никаких проблем, так как d не используется в НАМ нигде, кроме разделов, где этот идентификатор объявлен, в силу чего в мини-указатель информация о данном идентификаторе не попадет.
Когда CTWILL обрабатывает каждый из разделов программы, она создает список всех идентификаторов, используемых в данном разделе, исключая зарезервированные слова (языка программирования). В конце соответствующего раздела она осуществляет «мини-вывод» (т. е. вывод в мини-указатель) текущих значений каждого из идентификаторов, входящих в полученный список,
до тех пор пока текущее значение отвечает текущему разделу рассматриваемой программы, или же пока не вмешается пользователь.
В распоряжении пользователя есть два способа изменить мини-выходы: подавить значения, устанавливаемые по умолчанию, или же вставить заменяющие элементы. При реализации первого из этих двух вариантов используется явная команда вида
Q-identQ>
которая приказывает CTWILL не вырабатывать стандартный мини-выход для идентификатора ident в текущем разделе обрабатываемой программы. Во втором случае пользователь может задать одно или несколько временных значений (temporary meanings) для некоторого идентификатора, причем все они будут включены в мини-выход в конце обрабатываемого раздела. Временные значения не влияют никак на текущее значение идентификатора. Всякий раз, когда хотя бы одно временное значение появляется в мини-выходе, соответствующее текущее значение подавляется так, как если бы была выдана команда @-. . . @>. Временные значения задаются с помощью операции @У„ которая меняет состояние переключателя, влияющего на работу команды @$. . . @>. В начале раздела данный переключатель находится в положении «постоянное» («permanent») и команда @$. . . @> изменит текущее значение идентификатора так, как это было описано выше. Появление @У, каждый раз меняет состояние с «постоянного» на «временное» («temporary») или наоборот; во «временном» состоянии команда @$... @> задает временное значение, помещаемое в минивыход, но не оказывающее никакого влияния на постоянное (текущее) значение рассматриваемого идентификатора.
Перед тем как продемонстрировать примеры действия указанных выше соглашений, следует сказать еще об одном виде взаимодействия команд @- и @$ в CTWILL. Если бы CTWILL присвоила новое текущее значение идентификатору ident нормальным образом, основываясь на семантике языка С, и если команда ^-ident<^> уже появилась в текущем разделе, CTWILL не будет перезаписывать имеющееся текущее значение, однако она выведет это текущее значение в aux-файл. В частности, пользователь имеет право задать необходимое текущее значение с помощью команды ^ ident.. . @>; такого рода подход позволяет пользователю контролировать то, что выводится в аих-файл.
В качестве примера ниже приводится полный перечень всех команд, использованных автором для корректировки или улучшения мини-указателей для НАМ, формируемых программой CTWILL с использованием значений по умолчанию:
В начале §2,
@-deg@>
@$deg {ham}2 =\|u.\H@>
@%@$u {GB\_\,GRAPH}9 \&{util}@>
чтобы в определении для deg читалось 1и.Г вместо ‘macro’, а также для того, чтобы мини-указатель ссылался на и как на служебное поле.
В начале §4, @-taken@> @-vert@> @$taken {ham}4 =\|v.\|I@> @%@$v {GB\_\,GRAPH}9 \&{util}@> @$v {ham}2 \&{register} \&{Vertex} $*$@>
по аналогичным соображениям, а также для того, чтобы предотвратить индексирование для идентификатора vert. Здесь в мини-указатель будут занесены два «временных» значения для щ одно из которых случайно совпадает с его постоянным значением.
В начале §6,
@-k@> @-t@> @-vert@> @-ark@> @$vert {ham}6 =\|w.\|V@> @$ark {ham}6 =\Ix.\IA@> Q7Mw {GB\_\,GRAPH}9 \&{util}@>
@$x {GB\_\,GRAPH}9 \&{util}@>
по аналогичным соображениям. И это все.
Эти команды не вносились в программный файл ham.w, они были записаны в другой файл с именем ham.ch и введены при помощи средства «изменить файл» (change file) CWEB-программы [8]. Данное средство позволяет весьма просто модифицировать действительное (исполняемое) содержание основного файла, не изменяя этого файла напрямую.
Обработка ТеХ’ом
CTWILL формирует ТуХ’овский файл, включающий в конце каждого раздела мини-выход. Например, после §10 программы НАМ будет помещен следующий мини-выход:
\]{GB\_\,GRAPH}10 \\{next} \&{Arc} $*$ \[7 \\{advance} label
\[6 \\{ark} =\|x.\|A
\[2 \|{t} \&{register} \&{Vertex} $*$ \[4 \\{not\_taken} =macro (\,)
\HGB\_\, GRAPH} 10 \\{tip} \&{Vertex} $*$ \[2 \|{v} \&{register} \&{Vertex} $*$ \[2 \|{a} \&{register} \&{Arc} $*$
Здесь управляющая последовательность \] вводит внешнюю ссылку на некоторую другую программу; \[ задает внутреннюю ссылку на другой раздел программы НАМ; \\ набирает идентификатор обычным (текстовым) курсивом; \ I задает набор идентификатора математическим курсивом; \& определяет набор полужирным шрифтом.
Имеется специальный отладочный режим, в котором ТЁК просто набирает мини-выход в конце каждого раздела обрабатываемой программы вместо того,
чтобы формировать требуемые мини-указатели. Это упрощает пользователю проверку информации, фактически выдаваемой программой CTWILL, и сравнение ее с тем, что он хотел бы получить. Следует заметить, что ошибки в выходе CTWILL совсем не обязательно приводят к ошибкам в мини-указателях; например, неверная ссылка в §6 на идентификатор, определенный в §5, не появится в мини-указателе на развороте, включающем §5. Лучше всего сначала убедиться в правильности результатов, выводимых CTWILL, а уже потом формировать и проверять требуемые мини-указатели. Тогда не будет неприятных сюрпризов, случающихся, когда раздел обрабатываемой программы перемещается с одного разворота на другой.
Когда ТЁ^’У поручается набрать создаваемые мини-указатели в их окончательном виде, ему приходится основательно потрудиться. Вот как это происходит. Главная задача ТЁХ’а, после того как отформатированы строки комментария и программного кода на языке С, состоит в том, чтобы определить, попадает ли текущий раздел программы на текущий разворот, и (если попадает) модифицировать мини-указатель, объединяя все элементы указателя для этого разворота.
Рассмотрим в качестве примера, что будет происходить, когда ТЦХ набирает §10 программы НАМ. Рассматриваемый текущий разворот начинается с §8 и ТЁХ уже знает, что §8 и §9 будут размещаться вместе, на одной странице. После того как содержимое §10 набрано, ТеХ просматривает элементы миниуказателя. Если какой-либо из них ссылается на §8 или §9, ТеХ временно проигнорирует такие ссылки, поскольку эти разделы уже являются составными частями рассматриваемого текущего разворота. (Такая ситуация в §10 на самом деле не возникает, но когда ТеХ обрабатывал §7, он подавил элементы указателя для vert и атк, поскольку они ссылались на §6.) Т^Х также на время отбрасывает элементы мини-указателя, которые отвечают другим элементам, уже спланированным для текущего разворота. (В этом случае отбрасывается все, за исключением элементов для идентификаторов advance и атк; остальные — next, t, not.taken, v и a — представляют собой дубликаты элементов в мини-выходах для §8 или §9.) Наконец, ТеХ пробует отбросить ранее сформированные элементы, ссылающиеся на данный текущий раздел. (В рассматриваемом случае не произойдет ничего, поскольку элементы из §8 или §9, ссылающиеся на §10, отсутствуют.)
После такого рода вычислений ТеХ’у известно количество п элементов мини-указателя, которые потребуются, если §10 будет размещен на том же развороте, что и §8 с §9. ТеХ делит это число п на количество колонок в формируемом мини-указателе (в рассматриваемом нами примере этот делитель будет равен 1, однако в [5] и [9] он был принят равным 3), умножает результат на расстояние между базовыми линиями в мини-указателе (в рассматриваемом примере оно принято равным 9 pt) и добавляет полученный результат к полной высоте набираемого текста для формируемого текущего разворота (в данном случае это суммарная высота §8 +§9 +§10). Теперь, с незначительными изменениями, связанными с корректировкой интервалов между разделами и добавлением линейки, отделяющей мини-указатель от остального текста, ТеХ
может оценить, какое пространство необходимо для размещения набираемого разворота. В нашем примере все помещается на одной странице, так что ТЁХ присоединит §10 к развороту, уже содержащему §8 и §9. Затем, после того как тем же самым образом будет обработан §11, ТЁХ обнаружит, что все вместе §§8-11 на одном развороте не помещаются, и решит начать новый разворот, где первым будет идти §11.
Средства реализации описанного выше процесса не встроены, разумеется, в ТЁХ. Все необходимые для него действия выполняются под управлением набора макроопределений, называемого ctwimac.tex [10]. Первое же сообщение, передаваемое ТЁХ’у от CTWILL, содержит требование загрузить данный набор.
ТЁХ создавался для набора текстов, не для программирования, однако его лучше все-таки рассматривать как язык программирования. Но задача индексирования при построении мини-указателей — твердый орешек с точки зрения программирования. Детальный анализ набора макро ctwimac слишком сложен, чтобы его можно было представить здесь, однако ТЁХ’овские хакеры оценят некоторые из не слишком очевидных идей, использованных при написании упомянутых макро. (Оставшаяся часть этого длинного абзаца предназначена только ТЁХникам.) ТЁХ читает мини-выходы из CTWILL дважды, с отличающимися для каждого из прогонов определениями для \[ и \]. Пусть мы в настоящий момент обрабатываем раздел s, и пусть также формируемый текущий разворот начинается с раздела г. Тогда ТЁХ’овские лексемные регистры (token registers) 200, 201, ..., 219 содержат все элементы мини-указателя из разделов
г, г 4-1, ..., s — 1
для идентификаторов, определенных соответственно в разделах г, г 4- 1, ..., г 4- 19 CWEB-программы. (Нам нет необходимости хранить отдельные таблицы для более чем 20 последовательно идущих разделов, начинающихся с базового номера г в текущем развороте, поскольку ни один из разворотов не будет содержать более 20 разделов.) Подобным же образом, лексемный регистр с номером 199 содержит элементы указателя, ссылающиеся на разделы, предшествующие г, а лексемный регистр 220 содержит элементы, указывающие на разделы г 4- 20 и далее. Лексемный регистр 221 включает элементы мини-указателя для идентификаторов, определенных в других программах. Регистр-счетчик (count register) к хранит число элементов в лексемном регистре с номером к, 199 < к < 221. Если регистр-счетчик к имеет значение, равное j, содержимым регистра лексем к будет последовательность из 2j лексем:
\lmda\cs1\lmda\cs2 ...\lmda\csj
где каждая из команд Xcs^ представляет собой управляющую последовательность, единственным образом характеризующую элемент мини-указателя, определенный через \csname. . .\endcsname. ТЁХ может сообщить, согласуется ли вновь вводимый элемент мини-указателя с другими элементами, уже заданными для данного текущего разворота. Это осуществляется простой проверкой того, определена ли уже соответствующая управляющая последовательность. Замещающий текст для \csi представляет собой соответствующий элемент
мини-указателя, а определение для \lmda имеет вид:
\def\lmda#l{#l\global\let#l\relax}
Следовательно, когда Т^Х «исполняет» содержимое определенного лексемного регистра, он осуществляет набор всех требуемых элементов мини-указателя и переводит в разряд неопределенных все соответствующие управляющие последовательности. В качестве альтернативного варианта можно было бы записать
\def\lmda#l{\global\let#l\relax} '
если просто требуется стереть все элементы указателя, представленные в некотором лексемном регистре. В конце разворота, содержащего р разделов, генерируется мини-указатель, что осуществляется путем обработки содержимого («исполнения») лексемных регистров 199 и с 200 +р по 221, используя введенное выше первое из определений для \lmda, а также обрабатывая лексемные регистры с 200 по 200 4- р — 1, используя второе из введенных определений. Все работает как по мановению волшебной палочки.
Некорректность, которая имела место в первоначальных ТеХ’овских макро для TWILL, привела к появлению неприятной ошибки в первом издании (1986 г.) книг [5] и [6]. Управляющие последовательности в лексемных регистрах, соответствующих разделам рассматриваемого текущего разворота, не стирались; другими словами, содержимое этих лексемных регистров попросту отбрасывалось и не обрабатывалось со вторым определением для \lmda. В результате ТЕХ считал, что некоторые управляющие последовательности все еще определены, следовательно, для макро соответствующие элементы мини-указателя продолжали существовать, такого рода элементы были, следовательно, ошибочно пропущены. Затронутыми оказались всего лишь около 3% элементов миниуказателя, так что данная ошибка не бросилась сразу в глаза и оставалась незамеченной до момента выхода упомянутых книг из печати, когда они попали в руки к читателям. Единственным светлым пятном во всей этой истории был тот факт, что подтвердилась высокая эффективность мини-указателей: то, что упомянутые элементы не присутствовали в мини-указателях, было с сожалением отмечено читателями, их наличие обещало быть действительно полезным.
«Жадный» метод формирования текста наибольшей длины, с помощью которого ТЕХ’овские макро программы CTWILL размещают разделы на страницах, ведет к минимизации числа страниц, хотя такой результат и не гарантирован. Например, можно представить себе необычные сценарии, в которых разделы §100 и §101, скажем, не помещаются на одном развороте, тогда как три раздела (§100, §101, §102) поместятся. Это могло бы произойти в случае, если §100 и §101 содержат достаточно много ссылок на переменные, объявленные в §102. Аналогично, можно было бы разместить §100 вместе с §101, если бы §99, «отложенный» при формировании предыдущего разворота, перешел на текущий. Однако такого рода ситуации в высшей степени маловероятны и нет оснований особенно беспокоиться о них. Стратегия вида «один разворот в каждый момент времени», реализуемая макрокомандами ctwimac, является оптимальной и экономящей объем во всех практически значимых случаях.
С другой стороны, имеющийся опыт показывает, что иногда получаются неудачные разрывы страниц, отделяющие развороты один от другого, что заставляет пользователя прибегать к подстройке этих разрывов. Пусть, например, текст §7 в программе НАМ длиннее на одну строку. Тогда §7 не будет уже сочетаться с §6 и нам пришлось бы примириться с разворотом, содержащим совсем крошечный §6 и много пустого пространства. Это выглядело бы совершенно ужасно. В такой ситуации целесообразно два оператора
t-^ark = Л; v = т/;
записывать в одной строке вместо двух отдельных строк. Здесь неудачный разрыв страниц между разворотами ликвидируется путем связывания вручную этих двух операторов вместе, используя CWEB-команду @+.
Еще одна проблема, требующая обсуждения, состоит в том, что миниуказатели должны быть отсортированы в алфавитном порядке. Средств TgX’a вполне достаточно для определения разрывов между разворотами (и, соответственно, для определения содержимого этих разворотов), но TgX — неподходящая среда для выполнения сортировки. Решение указанной проблемы заключается в том, чтобы пропустить выход программы CTWILL через Т^Х дважды, подключив программу сортировки между этими двумя прогонами. Когда Т^Х обрабатывает файл ham.tex, макро из ctwimac заставляют его просмотреть первым делом файл с именем ham. sref. Если такого файла нет, будет записан файл ham.ref, содержащий в неотсортированном виде все элементы миниуказателей для каждого из разворотов. Т^Х при этом осуществляет набор текста как обычно, со всеми мини-указателями (но не отсортированными), на положенных им местах, при этом пользователь получает возможность проконтролировать и откорректировать, если в этом возникнет необходимость, разрывы страниц, отделяющие развороты один от другого. Как только расстановка разрывов страниц будет сочтена удовлетворительной, вызывается отдельная программа с названием REFSORT, которая преобразует файл ham.ref в его отсортированный вариант ham.sref. Теперь Т^Х при втором запуске найдет файл ham.sref и сможет использовать отсортированные данные для получения великолепного окончательного варианта распечатки программы НАМ.
Файл ham.ref может выглядеть, к примеру, следующим образом:
•241
+ \]{GB\_\,SAVE}4 \\{restore\_graph} \&{Graph} $*(\,)$
+ \]{GB\_\,GRAPH}9 \|{u} \&{util}
+ \]{GB\_\,GRAPH}8 \|{I} \&{long} •242
+ \] "<stdio.h>" Wprintf} \&{int} (\,)
А соответствующий файл ham. sref примет при этом следующий вид:
\]{GB\_\,GRAPH}10 \&{Arc} =\&{struct}
\]{GB\_\,GRAPH}9 \|{u} \&{util}
\]{GB\_\,GRAPH}9 \&{Vertex} =\&{struct} \donewithpage241
\[2 \ I {a} \&{register} \&{Arc} $*$
\HGB\_\,GRAPH}20 Wvertices} \&{Vertex} $*$
\donewithpage245
Каждый из этих двух файлов содержит по одной строке для каждого элемента мини-указателя, а также по одной строке, отмечающей начало (в ham.ref) или конец (в ham.sref) каждого из разворотов.
Заключительные замечания
Система CTWILL для формирования мини-указателей может рассматриваться как дальнейшее развитие средств визуализации программных структур. Она продолжает длинный ряд исследовательских работ, начало которым было положено У. Хансеном, создавшим структуризованный текстовый редактор Emily [2, 3].
Хотя система CTWILL и не является полностью автоматическим средством, она самым существенным образом улучшает читаемость больших наборов программ. Автор, потративший целый год на составление программ и подготовку их к публикации, едва ли будет возражать против того, чтобы потратить еще неделю на улучшение предметного указателя для нее. И в самом деле, немного дополнительного времени, потраченного на индексирование, в общем случае приводит к существенному улучшению текста книги, которую автор в процессе индексирования получает шанс увидеть в новом свете.
Избавиться от всех доработок указателей вручную невозможно, поскольку компьютер не может знать подходящих ссылок для каждого из идентификаторов, появляющихся в комментариях к программе. Однако опыт, полученный при использовании механизма изменения файлов программы CTWILL, показывает, что корректные мини-указатели для больших и сложных программ могут быть получены очень быстро, со скоростью примерно 100 книжных страниц в день. Например, создание файлов замены (change files) для 460 страниц программного текста в [9] заняло 5 дней, в течение которых дополнительно проводились отладка и совершенствование программы CTWILL.
Мини-указатели представляют собой прекрасное дополнение к печатным изданиям, однако в более дальней перспективе можно ожидать замены во многих случаях печатной продукции на объекты гипертекстового характера. Достаточно просто представить себе некую систему для просмотра CWEB- программ, в которой найти значение любого из идентификаторов можно было бы простым «щелчком» кнопки мыши на нем. Системы будущего позволят, по-видимому, обозревать программу в целом, обеспечивая простую навигацию среди хитросплетений их кода. (См. [1], где обсуждаются некоторые шаги в данном направлении.)
Такого рода системы, которые появятся в будущем, войдут в определенное противоречие с CTWILL, формирующей мини-указатели. Автор, решивший создать полезный программный гипертекст, который смогут читать и другие,
захочет дать подсказки относительно значений идентификаторов, роль которых невозможно или очень трудно оценить механически, дедуктивным путем. Некоторые из уроков, полученных при создании и применении CTWILL, окажутся, скорее всего, полезными для любого, кто попытается разрабатывать самодокументированные программные системы, которые заменят книги в той форме, к какой мы привыкли сейчас.
Литература
М. Brown & В. Czejdo, “A hypertext for literate programming,” Lecture Notes in Computer Science 468 (1990), 250-259.
Wilfred J. Hansen, Creation of Hierarchic Text With a Computer Display, Ph.D. thesis, Stanford University (1971). Опубликовано также как Argonne National Laboratory report ANL-7818 (Argonne, Illinois: 1971).
Wilfred J. Hansen, “User engineering principles for interactive systems,” AFIPS Fall Joint Computer Conference 39 (1971), 523-532.
Donald E. Knuth, “Literate programming,” The Computer Journal 27 (1984), 97-111.
Donald E. Knuth, TeK: The Program, Volume В of Computers & Typesetting (Reading, Massachusetts: Addison-Wesley, 1986).
Donald E. Knuth, METAFONT: The Program, Volume D of Computers & Typesetting (Reading, Massachusetts: Addison-Wesley, 1986).
Donald E. Knuth, Literate Programming, CSLI Lecture Notes 27 (Stanford, California: Center for the Study of Language and Information, 1992). Distributed by Cambridge University Press.
Donald E. Knuth & S. Levy, The CWEB System of Structured Documentation, Version 3.0 (Reading, Massachusetts: Addison-Wesley, 1994). Самая последняя версия имеется в Интернете и доступна по анонимному ftp на labrea. stanford.edu в директории ~ftp/pub/cweb.
Donald Е. Knuth, The Stanford GraphBase: A Platform for Combinatorial Computing (New York: ACM Press, 1993).
Donald E. Knuth, “CTWILL” (1993). Доступно по анонимному ftp на labrea. stanford.edu в директории ~ftp/pub/ctwill.
Kasper 0sterbye, “Literate Smalltalk programming using hypertext,” IEE Transactions on Software Engineering SE-21 (1995), 138-145.
Clyde Pharr, Virgil’s JEneid, Books I-VI (Boston: D. C. Heath, 1930).
Stanford University Computer Science Department, “The Stanford GraphBase.” Имеется в Интернете и доступно по анонимному ftp на labrea.stanford.edu в директории ~ftp/pub/sgb.
J. S. Weening, Личное сообщение. Хранится в архивах проектов TgX’a в библиотеке Станфордского университета в отделе Special Collections, SC 97, series II, box 18, folder 7.6.
Приложение: НАМ
Гамильтоновы контуры.
Эта программа отыскивает все гамильтоновы контуры (ГК) неориентированного графа.
Если ввести ‘ham foo.gb’, в распечатке результатов работы НАМ будут перечислены все ГК графа f оо, описанного в файле foo.gb с использованием формата из Stanford GraphBase. В конце распечатки дается общее число найденных решений.
Необязательный второй параметр задает шаг выдачи результатов. Например, при запуске НАМ с помощью команды ‘ham foo.gb 1000’ будет выводиться только каждый тысячный из найденных ГК. Если этот параметр равен нулю, выводится лишь общее число найденных ГК.
^include <stdio.h>
/* стандартные функции ввода/вывода языка С */
#include "gb.graph. h"
/* структуры данных GraphBase
#include "gb.save.h"
/* программа restore-graph */
В поле deg записываются степени вершин графа.
#define deg и.I
/* текущее число входящих и исходящих дуг для данной вершины */
int main (int argc, char * argv [ ])
{ Graph *g;
/* граф для анализа */ register Vertex *t1 *u, *u;
/* основные вершины */
Vertex *x, *y, *z;/* менее
употребит, вершины */
register Arc *a, *aa;
/* часто использ. дуги */
Arc *b, *bb;
/* менее часто использ. дуги */ int count = 0;
/* уже найденные решения */ int interval = 1;
/* шаг выдачи решений */
(Сканировать аргументы командной строки и ввести 9 з);
(Подготовить д для отката, найти вершину х с минимальной степенью 4);
(Прекратить выполнение, если неверно задано д или x-deg <2 5);
for (b = x-arcs; b-mext; b = b-next) for (bb = b-mext; bb;
bb = bb-next) {
v = b-*tip;
z = bb-tip;
(Найти все простые пути длины д-т — 2 из v в z^ избегая х 7);
}
printf (" AltogetheruXduSolut \ ions.\n", count);
return 0;
/* нормальный выход */
Arc = struct, GB-GRAPH §10.
arcs: Arc *, GB_GRAPH§9.
Graph = struct, gb_graph§20.
long, GB_GRAPH§8.
long, GB-GRAPH §20.
next: Arc *, GB_GRAPH§10.
printf: int (), <stdio.h>.
restore.graph: Graph *(), gb_SAVE§4.
tip: Vertex *, gb_graph§10.
util, GB_GRAPH§9.
Vertex = struct, GB.GRAPH§9.
(Сканировать аргументы командной строки и ввести 9 3) =
if (аг^с > 2 Л sscanf (аг^[2], "7.d", ^interval) = 1) {
argc —;
if (interval < 0)
interval = — interval;
else if (interval = 0) interval = —10000;
/* подавить печать, если параметр равен 0 */
}
if (argc / 2) {
printf ("Usage: u7.Suf oo. gbu [i \nterval] \n", ar^v[0]);
return — 1;
}
g = restore.graph (argv\l\)\
Этот код используется в разделе 2.
Вершины, уже внесенные в путь, именуются «отмеченными» («taken»), а поле taken для них будет ненулевым. Начальное значение всех этих полей устанавливается равным нулю.
#define taken v.I
/* Есть ли уже эта вершина в текущем пути? */ #define not.taken(vert) ((vert)-*taken = 0)
(Подготовить д для отката, найти вершину х с минимальной степенью 4) =
if (д) { int dmin = g-m; for (v = д~* vertices;
v < g-^vertices + д~п; v++) { register int d = 0;
/* the degree ofv */
mtaken = 0;
for (a = mares; a; a = a-next) d++;
mdeg = d;
if (d < dmin) dmin = d^x = v;
}
}
Этот код используется в разделе 2.
Вершина, имеющая менее двух соседних, не может входить в ГК и должна быть пропущена.
(Прекратить выполнение, если неверно задано д или x-deg < 2 5) =
if (-у) {
printf ("GraphuXsuiSumalf orm\ edu(errorucodeu7.1d) ! \n", ar^v[l], panic-code);
return —2;
}
if (x-deg < 2) {
printf ("Nousolutionsu (verte \
Xu7.Suhasudegreeu7.ld) . \n", x^name^ mdeg);
return —3;
}
Этот код используется в разделе 2.
a: register Arc *, §2.
arcs; Arc *, gb_graph§9.
argc; int, §2.
argv; char *[], §2.
deg — u.I, §2.
g; Graph *, §2.
I; long, GB_GRAPH§8.
interval; int, §2.
n; long, gb_graph§20.
name; char *, gb.graph§9.
next; Arc *, gb_graph§10.
panic_code; long, GB-GRAPH §5.
printf; int (), <stdio.h>.
restore.graph; Graph *(), gb_save§4. sscanf; int (), <stdio.h>.
v; register Vertex *, §2.
v; util, GB-GRAPH§9.
vertices; Vertex *, gb_graph§20.
x; Vertex *, §2.
Алгоритм. Неэффективные ветви дерева поиска отсекаются по простому правилу: если следующая вершина, в которую можно перейти, смежна только с единственной другой неиспользованной вершиной, то надо перейти в нее.
Переходы фиксируются в массиве вершин д. Точнее, к-я вершина рассматриваемого пути будет t-vert, если t — к-я вершина графа. Если переход не задается, t-^ark будет указывать на запись Аге, отвечающую дуге из invert в (t 4- Invert; в противном случае t-^ark будет равно Л. #define vert w.V /* вершина на текущем пути */
# define ark
х.А /* дуга к текущей следующей вершине */
Эта программа — типичный пример применения метода поиска с возвращениями (backtracking), т. е. на дереве всех возможных решений осуществляется поиск в глубину (depth-first search). Автор принадлежит к «старой школе» программирования и ему удобнее писать программы с использованием меток и операторов goto, а не с помощью циклов while.
(Найти все простые пути длины д-*п — 2 из v в z, избегая х 7) =
{ Vertex *tmax;
t = д-vertices; tmax = t + g^n — 1;
x-taken = 1;
t-^vert = x\ t-ark = A;
advance: (Увеличить t, корректируя структуры данных, чтобы
показать, что вершина и уже «выбрана» и установить г/, задающим переход, если он есть, но goto backtrack, если нет возможных переходов 8);
if (з/) {/* переход задан */
t-ark = A; v = д; goto advance;
}
а = v-arcs;
search:
(Проверить дугу а и следующие за ней, перейти, если найден корректный переход ю);
restore: аа = Л;
restore_to_aa: (Вернуть структуры данных к состоянию, в которых они были, когда был достигнут уровень t^ остановиться на дуге аа 9);
backtrack: (Если возможно, уменьшить t и искать другую возможность 11);
}
Этот код используется в разделе 2.
a: register Arc *, §2.
A: Arc *, gb_graph§8.
аа: register Arc *, §2.
Arc = struct, GB_GRAPH§10. arcs: Arc *, GB_GRAPH§9.
Graph *, §2.
long, gb_graph§20.
register Vertex *, §2.
taken = v.I, §4.
register Vertex *, §2.
V: Vertex *, GB_GRAPH§8.
Vertex = struct, GB.GRAPH§9. vertices: Vertex *, GB_GRAPH§20.
util, GB_GRAPH§9.
Vertex *, §2.
util, gb_GRAPH§9.
Vertex *, §2.
z: Vertex *, §2.
После того как вершина выбрана, попытаться удалить ее из графа. (Увеличить i, корректируя структуры данных, чтобы показать, что вершина и уже «выбрана» и установить г/, задающим переход, если он есть, но goto backtrack, если нет возможных переходов 8) =
t-H-;
invert = щ
v-^taken = 1;
if (и = z) {
if (t = tmax)
(Записать решение 12 );
goto backtrack;
}
for (aa = гг^агсз^у = A; aa;
aa = aa-next) {
register int d;
и = aa-tip;
d = u-deg — 1;
if (d = 1 A not-taken (u)) {/*
надо перейти теперь к и */ if (z/) goto restore-to.aa;
/* два заданных перехода одновременно выполнить нельзя */
У = Щ
u-^deg = d; /* из и больше нельзя перейти к v */
}
Этот код используется в разделе 7.
На уровне t граф существенно не меняется, все что делается с ним — уменьшаются степени вершин, достижимых из t-^vert. Это позволяет простым образом восстанавливать предыдущие состояния при откатах.
(Вернуть структуры данных к состоянию, в которых они были, когда был достигнут уровень t, остановиться на дуге аа 9) = for (а = t-vert-arcs; а / аа;
а = а-next) a^tip-deg ++;
Этот код используется в разделе 7.
(Проверить дугу а и следующие за ней, перейти, если найден корректный переход 10) = while (а) {
г? = a-tip;
if (not-taken (г?)) { t-ark = а;
goto advance;
/* move to г? */
}
a = arm ext;
}
Этот код используется в разделе 7.
a: register Arc *, §2.
аа: register Arc *, §2.
advance: label, §7.
arcs: Arc *, GB_GRAPH§9.
ark = x.A, §6.
backtrack: label, §7.
deg = u.I, §2.
next: Arc *, gb_graph§10.
not.taken = macro (), §4.
restore-to.aa: label, §7.
t: register Vertex *, §2.
taken = v.I, §4.
tip: Vertex *, gb_graph§10. tmax: Vertex *, §7.
n: register Vertex *, §2.
v: register Vertex *, §2.
vert = w.V, §6.
g: Vertex *, §2.
z: Vertex *, §2.
(Если возможно, уменьшить t и искать другую возможность 11) =
t-vert-taken = 0;
if (t-ark) {
а = t-ark-next;
goto search;
if (t / g-vertices) goto restore; /* задан переход, поэтому пропустить search */
Этот код используется в разделе 7.
Вывод решения на печать простым перечислением имен вершин выдаваемого текущего пути.
(Записать решение 12) =
count-Н-;
if (count % interval = ^Mnterval > 0) {
print/ ("Xd: u", count);
for (u = g-vertices; и < tmax; u-H-)
print/ ("%Su", u-vert-name^; print/ ("\n");
}
}
Этот код используется в разделе 8.
register Arc *, §2.ark = х.А, §6.count: int, §2.Graph *, §2.interval: int, §2.name: char *, GB.GRAPH §9.next: Arc *, GB_GRAPH§10.print/: int (), <stdio.h>.restore: label, §7.search: label, §7.register Vertex *, §2.taken = у.Ц §4.tmax: Vertex *, §7.register Vertex *, §2.vert = w.V, §6.vertices: Vertex *, gb.graph §20.register Arc *, §2.ark = х.А, §6.count: int, §2.Graph *, §2.interval: int, §2.name: char *, GB.GRAPH §9.next: Arc *, GB_GRAPH§10.print/: int (), <stdio.h>.restore: label, §7.search: label, §7.register Vertex *, §2.taken = у.Ц §4.tmax: Vertex *, §7.register Vertex *, §2.vert = w.V, §6.vertices: Vertex *, gb.graph §20.
register Arc *, §2.
ark = х.А, §6.
count: int, §2.
Graph *, §2.
interval: int, §2.
name: char *, GB.GRAPH §9.
next: Arc *, GB_GRAPH§10.
print/: int (), <stdio.h>.
restore: label, §7.
search: label, §7.
register Vertex *, §2.
taken = у.Ц §4.
tmax: Vertex *, §7.
register Vertex *, §2.
vert = w.V, §6.
vertices: Vertex *, gb.graph §20.
register Arc *, §2.
ark = х.А, §6.
count: int, §2.
Graph *, §2.
interval: int, §2.
name: char *, GB.GRAPH §9.
next: Arc *, GB_GRAPH§10.
print/: int (), <stdio.h>.
restore: label, §7.
search: label, §7.
register Vertex *, §2.
taken = у.Ц §4.
tmax: Vertex *, §7.
register Vertex *, §2.
vert = w.V, §6.
vertices: Vertex *, gb.graph §20.
Виртуальные шрифты. Развлечение
для настоящих мастеров
[Первоначально опубликовано в TUGboat 11 (1990), 13-23.]
Значительная доля участников обсуждений, проводившихся в рамках группы новостей ТуХНах в течение примерно последнего года, прилагала усилия для того, чтобы решить проблемы, связанные со взаимодействием шрифтов, подчиняющихся различным наборам соглашений. К примеру, оживленной была переписка относительно смешивания цифр, изображаемых в старинном стиле, с алфавитом, содержащим прописные буквы и капитель. Некоторых участников обсуждения волновали возможности работы со шрифтами, созданными фирмами Autologic, Compugraphic, Monotype и др., тогда как другие опасались лишиться ограниченных возможностей акцентировки, присущих семейству шрифтов Computer Modern, для шрифтов, включающих уже индивидуально акцентированные буквы, поскольку такого рода шрифты не пригодны к непосредственному употреблению в форме, которую воспринимает текущая версия ТЁХ’овского программного обеспечения.
Существует, однако, подход к решению подобного рода проблем, намного более эффективный, чем средства, предлагаемые участниками обсуждений в ТуХНах. Этот подход был первоначально реализован Дэвидом Фуксом в 1983 г., когда он дополнил соответствующими средствами программу DVI-to- APS, имевшуюся в Станфорде (которая также была разработана им и предназначалась для коммерческого распространения через фирму ArborText). Мы с Дэвидом использовали данную программу, например, при наборе моей статьи Literate Programming для журнала The Computer Journal «родными» шрифтами фирмы Autologic, принятыми в этом журнале в качестве базовых.
Я думал тогда, что стратегия, предложенная Дэвидом, станет широко известной и употребительной. Но увы —и это, пожалуй, единственное серьезное разочарование, связанное с распространением TgX’a по всему миру —ни в одном из других драйверов типа DVI-to-X идеи Дэвида не оказались востребованными, а участники обсуждений в Т^ХНах извели и изводят галлоны электронных чернил, отыскивая ответы на неправильно поставленные вопросы.
Правильное направление поисков очевидно и мы его уже видим (хотя в 1983 г. оно еще не было столь очевидным): все, что нам требуется — это располагать хорошим способом отображения из ТеХ’овской нотации для литеры
из некоторого шрифта в представление, отвечающее требуемому печатающему устройству. Такого рода отображение получило наименование «виртуальный шрифт» («virtual font»), предложенное докладчиками из Американского математического общества (AMS) на конференции TUG в августе этого года. На этой конференции я кратко выступил по данной теме и выразил надежду, что все DVI-драйверы будут в течение года модифицированы так, чтобы реализовывались возможности работы с виртуальными шрифтами. Дейв Роджерс из ArborText объявил, что его фирма решила сделать разработанные ею WEB- программы для проектирования виртуальных шрифтов свободно доступными, а я пообещал отредактировать эти программы таким образом, чтобы они были согласованы с другими программами в стандартном комплекте поставки ТЁХ’овских средств.
Подготовка TgK’a в версии 3 и METAFONT’a в версии 2 заняла существенно больше времени, чем я ожидал, но в конце концов у меня появилась возможность более внимательно ознакомиться с концепцией виртуальных шрифтов. Потребность в таких шрифтах стала сейчас еще более насущной, чем совсем недавно, поскольку новые ТЁХ’овские средства многоязыковой поддержки будут действительно эффективными только тогда, когда будут доступны соответствующие шрифты. Виртуальные шрифты, удовлетворящие этим потребностям, могут быть созданы достаточно просто и легко.
После тщательного ознакомления с первоначальной разработкой Дэвида Фукса, я решил разработать полностью новый формат файла, развивающий его идеи и обеспечивающий полную аппаратную независимость механизма виртуальных шрифтов, в то время как программная реализация, выполненная Дэвидом, ориентировалась на АР S-устройства. Далее, я решил расширить предложенную им нотацию так, чтобы частью виртуального шрифта можно было сделать произвольные DVI-команды, включая команды special. Новый файловый формат, разработанный мною, получил название VF; DVI-драйверы не будут иметь сложностей с чтением VF-файлов, поскольку формат VF аналогичен форматам РК и DVI, с которыми DVI-драйверы имеют дело сейчас.
Результатом проведенных работ стали две новые системные программы, названные VFtoVP и VPtoVF. Эти программы представляют собой расширенные варианты старых программ TFtoPL и PLtoTF; имеется также язык описания списков свойств VPL, расширяющий обычный формат PL в таком стиле, чтобы создание виртуальных шрифтов не составляло труда.
Помимо реализации упомянутых выше программ, я проанализировал заложенные в них идеи, убедившись, что виртуальные шрифты могли бы без особых сложностей быть встроены в систему DVIPS, созданную Томом Рокиц- ки. Я написал программу на языке С (ее можно получить у Тома), которая преобразует AFM-файлы фирмы Adobe в виртуальные шрифты для ТЁХ’а; эти виртуальные шрифты включают почти все характеристики текстовых шрифтов семейства Computer Modern (опущены только заглавные греческие буквы и «бесточечная» буква j), а также включают все дополнительные литеры, введенные фирмой Adobe. Полученные виртуальные шрифты включают даже все «составные знаки», перечисленные в исходном AFM-файле, от ‘Aacute’ до
‘zcaron’; такого рода знаки доступны как лигатуры. Например, чтобы получить ‘Aacute’, печатается сначала ‘acute’ (это литера 19 = *8 в шрифтовой раскладке для Computer Modern; это может быть также литера 194 = Meta-B, если с новым Т^Х’ом используется 8-битовая клавиатура), за которым следует ‘А’. Используя такие шрифты, мне теперь проще набирать тексты на европейских языках шрифтами Times Roman, Helvetica и Palatino, а не Computer Modern! (Требуется менее часа работы, чтобы получить виртуальный шрифт для Computer Modern, который позволит делать все то же самое, у меня, однако, до этого все не доходят руки.)
Прекрасная схема формирования лигатур, пригодная для десятков европейских языков, была опубликована Янисом Хараламбусом в TUGboat 10 (1989), 342-345. Он использовал только ASCII-знаки, вводя ‘Aacute’ с помощью комбинации <А. Я вполне готов присоединить эту схему к моей, добавив несколько строк в написанные мною VPL-файлы. Конечно, различные соглашения могут поддерживаться и одновременно, но я не рекомендовал бы этого делать.
Виртуальные шрифты упрощают переход от DVI-файлов к шрифтовым раскладкам различных разработчиков или поставщиков шрифтов. Они также (хотя мне и жаль об этом говорить) упрощают процедуру изменения апрошей для тех, кому приходится прибегать к использованию этого многократно осужденного элемента набора со свободной разгонкой текста.
К тому же, виртуальные шрифты решают проблему печати гранок (корректур) с помощью экранных шрифтов или лазерных шрифтов с низким разрешением, поскольку можно иметь несколько виртуальных шрифтов, пользующихся одним и тем же TFM-файлом. Предположим, например, что оригинал- макет издания надо распечатывать на наборной машине APS, используя шрифты Uni vers, однако требуется еще и уметь просматривать гранки на экране монитора, а также распечатывать их на лазерном принтере. Пусть, далее, в вашем распоряжении нет шрифтов Univers для лазерного принтера, наиболее похожее из того, что имеется —это шрифт Helvetica. И предположим также, что для вывода гранок на экран монитора у вас нет даже и шрифта Helvetica, имеется только cmsslO. И вот что вы можете сделать в сложившейся ситуации. Первым делом требуется создать VPL-файл univers-aps.vpl, содержащий виртуальный список свойств (Virtual Property List) и описывающий высококачественный шрифт, который понадобится для окончательного вывода требуемого оригинал-макета. Затем после редактирования из этого файла получается файл univers-laser.vpl, включающий идентичную информацию о метрике шрифта, но символы его отображаются в шрифт Helvetica; аналогично готовится и файл univers-screen. vpl, отображающий информацию о метрике шрифта в шрифт cmsslO. Теперь каждый из трех полученных VPL-файлов обрабатывается программой VPtoVF. Она создает три идентичных TFM-файла с именем univers.tfm, один из которых должен быть перемещен в каталог, читаемый Т^Х’ом. Будут получены также три различных VF-файла с одинаковым именем univers.vf. Их надо поместить в три различных каталога, где
размещаются программы обработки DVI-файлов при выводе, соответственно, на APS-машину, лазерный принтер и экран монитора. Voila!
Итак, виртуальные шрифты — вещь, несомненно, вполне добродетельная. Но если рассмотреть эти шрифты более детально, что же они собой все-таки представляют? Для ответа на эти вопросы ниже приводятся фрагменты файлов VFtoVP.web и VPtoVF.web, в которых дается полное описание форматов VF и VPL.
Я с полным основанием ожидаю, что все те, кто занимается реализацией DVI-драйверов, сразу же оценят большие потенциальные возможности виртуальных шрифтов и не смогут противиться введению VF-средств в создаваемое ими программное обеспечение, причем сделают это уже в первые же месяцы 1990 г. Идея здесь такова: для каждого шрифта, заданного в DVI-файле, программа производит сначала просмотр некоторой специальной таблицы, чтобы узнать, является ли данный шрифт резидентным для требуемого устройства (в таком случае загружается TFM-файл, чтобы получить информацию о ширине знаков, входящих в шрифт); если такого рода поиск не увенчался успехом, производится поиск соответствующего GF- или РК-файла; если и этот поиск оказался безуспешным, просматривается VF-файл, что, в свою очередь, может привести к просмотру других реальных или виртуальных файлов. Последние из упомянутых файлов не должны загружаться немедленно, но только по запросу, поскольку реализуемый процесс имеет рекурсивный характер. Если же не найдены ни резидентный файл, ни GF- или РК-файлы, ни VF-файл, в качестве последнего средства следует загрузить TFM-файл, тогда на месте ненайденных символов будут оставлены пробелы соответствующей ширины.
Фрагменты файла VFtoVP. web
Виртуальные шрифты. В основе VF-файлов заложена идея общего интерфейса для переключения между мириадами шрифтовых раскладок, поддерживаемых поставщиками различного наборного оборудования. Если такой механизм отсутствует, пользователям придется писать объемистые и заумные макро всякий раз, когда у них возникнет потребность в применении соглашений о наборе, основанных на одной шрифтовой раскладке, в сочетании с реальными шрифтами, базирующимися на другой раскладке. Это ложится дополнительным бременем на наборные системы; положение еще усугубляется необходимостью проведения ряда дополнительных операций (вроде кернинга, переносов, формирования лигатур).
Эти трудности устраняются, если у нас есть «виртуальный шрифт», т.е. шрифт, который существует в смысле логическом, но не физическом. Система набора текстов, подобная ТЁХ’у, может выполнять свою работу, вовсе не зная, откуда берутся реальные литеры; драйвер устройства вывода может тогда решать свою задачу, посылая запрос к VF-файлу, чтобы выяснить, какие реальные литеры соответствуют тем, что ТУХ пытается изобразить в настоящий момент. Реальные литеры могут быть сдвинуты и/или увеличены и/или скомбинированы с другими литерами из многих различных шрифтов. Виртуальный шрифт может даже обеспечить использование знаков из виртуальных шрифтов, включая самого себя.
Виртуальные шрифты обеспечивают также удобную возможность подстановки литер (символов) для целей черновой печати (печать гранок), в ситуациях, когда шрифты, разработанные для одного печатающего устройства, недоступны на другом.
VF-файл организован как поток, состоящий из 8-разрядных двоичных слов (байтов), на основе использования соглашений, заимствованных из форматов DVI-'и РК-файлов. Поэтому драйвер устройства, располагающий информацией о форматах DVI- и РК-файлов, уже содержит большинство средств, необходимых для обработки VF-файлов. Будем считать, что формат DVI-файла известен; здесь принимаются соглашения, изложенные в документации по DVI (см., например, TjgX: The Program, part 31), и на их основе определяется VF-формат.
Каждый VF-файл начинается преамбулой, за которой следуют определения литер, а после них — заключительная часть. Точнее, самый первый байт каждого VF- файла должен быть первым байтом следующей «преамбульной команды»:
рте 247 г[1] А:[1] т[/с] cs[4] ds [4]. Здесь г — идентификационный байт VF, в настоящий момент 202. Строка х есть просто комментарий, содержащий обычно сведения о происхождении данного VF-файла. Параметры cs и ds представляют собой, соответственно, контрольную сумму и кегель рассматриваемого виртуального шрифта; значения этих параметров должны совпадать со значениями двух первых слов в заголовке соответствующего TFM-файла, как это будет показано ниже.
После команды рте текст преамбулы продолжается благодаря перечислению определений шрифтов. В них с помощью команд set_char определяется каждый из шрифтов, требуемых далее для создания «реальных» литер. Определения шрифтов в VF-файлах в точности такие же, как и в DVI-файлах, за исключением того, что масштабируемый размер s является относительным, а кегель — абсолютным:
fnt.defl 243 A:[l]с[4]s[4]d[4]а[1]Z[l]п[а 4- Z].Определитьшрифт к, где 0 < к < 256.
fnt_def2 244 А:[2]с[4]s[4]d[4]а[1]Z[l]п[а 4- Z].Определитьшрифт к, где 0 < к < 216.
fnt-defS 245 А:[3]с[4]s[4]d[4]а[1]Z[l]п[а 4- /].Определитьшрифт к, где 0 < к < 224.
fnt_def4 246 А:[4]с[4]s[4]d[4]а[1] Z[l] п[а-Н]. Определить шрифт к, где —231 < к < 231.
Эти номера шрифтов к являются «локальными», они не имеют никакого отношения к номерам шрифтов в DVI-файле, использующем данный виртуальный шрифт. Параметр s, представляющий собой масштабированный размер определяемого локального шрифта, есть слово типа fix_word, его значение определяется относительно кегля виртуального шрифта. Тогда, если локальный шрифт, который надо использовать, того же самого размера, что и кегель рассматриваемого виртуального шрифта, s будет иметь целочисленное значение, равное 220. Значение параметра s должно быть положительным и меньшим, чем 224 (т. е. быть меньше 16, если s рассматривать как fix-wordY Параметр d размера также типа fix^word, его значение задается в единицах разрешающей способности печатающего устройства, следовательно, он идентичен кеглю, содержащемуся в соответствующем TFM-файле.
За преамбулой следует нуль или более символьных блоков, причем каждый из этих блоков начинается с байта, имеющего значение < 243. Имеется два формата символьных блоков — длинный и короткий:
long_char 242 pZ[4] сс[4] tfm[4] dm [pl]. Этот длинный формат задает некоторый виртуальный символ в общем виде.
short-charO .. short_char241 Р^[1] сс[1] (/тп[3] dm^piy Этот короткий формат задает некоторый виртуальный символ в случае, когда 0 < pl < 242 и 0 < сс < 256 и О < tfm < 224.
Здесь pl показывает длину блока, следуя значению tfm; сс есть код символа; tfm представляет собой ширину символа, скопированную из соответствующего TFM-файла для данного виртуального шрифта. Для любого заданного сс-кода должен быть определен по крайней мере один символьный блок.
В определениях символьных блоков dvi-байты представляют собой последовательности завершенных DVI-команд, имеющие вложенную структуру в соответствии со значениями push и pop. Разрешается использовать все DVI-операции, за исключением bop сор и команд с кодами операций > 243. Команды выбора шрифтов (с fnt_numO по fnt4) должны ссылаться на шрифты, определенные в преамбуле рассматриваемого VF-файла.
Размерные параметры, появляющиеся в DVI-инструкциях, аналогичны числам fix-word, т. е. они представляют собой целочисленные множители для 2-20 на кегель виртуального шрифта. Например, если виртуальный шрифт имеет кегель 10 pt, DVI- команда, уменьшающая его до 5 pt , представляет собой инструкцию down с параметром 219. Виртуальный шрифт сам по себе может использоваться в разных размерах, скажем, 12 pt; в таком случае инструкция down уменьшала бы размер шрифта до 6 pt. Каждый из размеров должен быть меньше 224 по абсолютному значению.
Драйверы устройств, обрабатывающие VF-файлы, воспринимают последовательности dvi-байтов как подпрограммы или макрокоманды, неявно вкладывая их в push и pop. Каждая подпрограмма начинается с выполнения операторов w = x = ,y = z = 0, и с текущего шрифта /, соответствующего номеру шрифта, определенного первым в преамбуле данного VF-файла (/ будет неопределенным, если нет ни одного такого шрифта). Как только сйп-команды будут выполнены, позиционные регистры к и v в DVI-формате и текущий шрифт / будут восстановлены в их предыдущих значениях; тогда, если рассматриваемая подпрограмма была вызвана командой set_char или set, к увеличивается на TFM-ширину (соответственно промасштабированную) — совсем так, как если бы набирался обычный простой символ.
Вслед за символьными блоками идет тривиальная завершающая (заключительная) часть VF-файла, состоящая из одного или большего количества байтов, причем все они имеют значение post (248). Общее число байтов в рассматриваемом VF-файле должно быть кратно 4.
А здесь — фрагменты файла VPtoVF.web
Описание списка свойств в данных о метрике шрифта. Идея, лежащая в основе формата VPL-файлов, возникла из того обстоятельства, что подготовку точного и детального описания шрифтов, необходимого программам набора текстов, подобных СЩХ’у, иногда приходится осуществлять вручную. Формат для представления вложенных друг в друга списков свойств дает возможность проделывать это достаточно удобным способом.
Для грамматического разбора и обработки VPL-файлов требуется довольно значительный объем работы. Заставлять ТеХ проделывать эту работу всякий раз, когда он загружает шрифт — явно нерационально. Т^Х имеет дело только с компактными описаниями метрических данных шрифтов; эти данные содержатся в TFM-файлах.
Однако представлены они настолько компактно, что никому кроме компьютера разобраться в них невозможно.
Драйверам устройств также необходим компактный способ описания отображений Т^Х’овских представлений о шрифтах в реальные литеры, которые будут порождаться соответствующими устройствами. Если имеется упакованная последовательность байтов, именуемая VF-файлом, то они смогут эффективно выполнять эти действия.
Цель программы VPtoVF состоит в преобразовании текстового файла, удобного для восприятия пользователем, в двоичные файлы, ориентированные на восприятие их компьютером. У VPtoVF есть «парная» программа с именем VFtoVP, реализующая обратную операцию.
VPL-файл подобен PL-файлу, однако по сравнению с ним содержит некоторую добавочную информацию. Поэтому вначале рассмотрим, каким образом формируются PL-файлы. Материал, представленный в следующих нескольких разделах, скопирован из программы PLtoTF.
Рассматриваемый PL-файл представляет собой список элементов вида
(PROPERTYNAME VALUE)
где «имя свойства» (PROPERTY NAME) — одно из конечного множества имен, понимаемых рассматриваемой программой, а «значение» (VALUE) может опять быть некоторым списком свойств. Лучше всего обсуждаемые идеи можно понять на примере, так что давайте рассмотрим фрагмент PL-файла для некоторого гипотетического шрифта.
(FAMILY NOVA) (FACE F MIE) (CODINGSCHEME ASCII) (DESIGNSIZE D 10) (DESIGNUNITS D 18) (COMMENT A COMMENT IS IGNORED) (COMMENT (EXCEPT THIS ONE ISN’T)) (COMMENT (ACTUALLY IT IS, EVEN THOUGH IT SAYS IT ISN’T))
(FONTDIMEN
(SLANT R -.25)
(SPACE D 6) (SHRINK D 2) (STRETCH D 3) (XHEIGHT R 10.55) (QUAD D 18) )
(LIGTABLE (LABEL C f) (LIG C f 0 200) (SKIP D 1)
(LABEL 0 200)
(LIG С i О 201)
(KRN 0 51 R 1.5)
(/LIG С ? С f)
(STOP) )
(CHARACTER С f
(CHARWD D 6)
(CHARHT R 13.5) (CHARIC R 1.5) )
Из этого примера видно, что шрифт, для которого представлена метрическая информация, принадлежит к гипотетическому семейству (FAMILY) NOVA; код (MIE), характеризующий его начертание (FACE), показывает, что данный шрифт имеет нормальную жирность (Medium), курсивное начертание (Italic) и что он расширенный (Extended); литеры рассматриваемого шрифта используют ASCII-кодировку (CODINGSCHEME). Кегель (DESIGNSIZE) данного шрифта составляет 10 pt, а все остальные размеры в этом PL-файле даны в единицах (units) таких, что 18 единиц равны кеглю. Описываемый шрифт является наклонным (SLANT) со значением параметра наклона, равным —.25 (т. е. буквы из этого шрифта имеют фактически обратный наклон — возможно именно поэтому семейству шрифтов и было дано имя NOVA). Нормальное расстояние между словами (SPACE) составляет 6 единиц (т. е. одна треть от кегля в 18 единиц), с клеем, который сжимается (SHRINK) на 2 единицы или растягивается (STRETCH) на 3 единицы. Буквы, для которых не требуется из-за акцентов увеличивать или уменьшать размер, имеют высоту (XHEIGHT) 10.55 единиц и интервал в 1 еш, равный 18 единицам.
Таблица лигатур (LIGATURE) в рассматриваемом примере несколько более запутанна. Она определяет, что если за буквой f следует другая буква f, то ее код меняется на '200, если за кодом '200 идет буква i, то он меняется на код '201; считается, что коды '200 и '201 представляют лигатуры ‘ff’ и ‘ffi’. Более того, в обоих случаях (буква f и код '200), если следующая литера имеет код '51 (это правая скобка), перед '51 следует вставить дополнительный промежуток размером в 1.5 единицы. (Строка ‘SKIP D 1’ пропускает одну команду LIG или KRN, которая в данном случае будет второй командой LIG; таким способом организуется работа двух различных программ формирования лигатуры/кернинга.) Еще один пример—хотя и довольно-таки странный—это «умные лигатуры» («smart ligatures»), введенные в T^X’e версии 3.0. Если за буквой f или кодом '200 следует литера «знак вопроса», то знак вопроса заменяется буквой f и запускается программа формирования лигатур. Это значит, что пара литер ‘f ?’ станет лигатурой ‘ff’, а тройки литер ‘ff?’ или ‘f?f ’ — лигатурой ‘fff’. Если вместо команды /LIG содержится команда /LIG>, перезапуск будет пропущен; ‘f?’ станет ‘ff’, a ‘f?f’ станет ‘fff’.)
В рассматриваемом примере литера f имеет собственную ширину в 6 единиц, а высоту —13.5 единиц. Глубина ее равна нулю (так как не задано значение CHARDP), а ее поправка на курсив составляет 1.5 единицы.
Приведенный выше пример иллюстрирует большую часть характерных особенностей PL-файлов. Отметим, что ряд свойств, в частности, с именами FAMILY или COMMENT, в качестве значения имеют символьную строку, признаком завершения этой строки является первая несбалансированная правая скобка. Однако большинство свойств, например, с именами DESIGNSIZE, SLANT и LABEL, имеют числовые значе-
ния. Соответствующее число может быть выражено несколькими способами. Вид используемого способа задается соответствующей префиксной буквой: D — для десятичного представления числа, Н —для шестнадцатеричного, 0 —для восьмеричного, R —для действительных чисел, С —для литер, F —для значения типа «face». Некоторые другие свойства (LIG, например) характеризуются двумя числами в качестве их значений. Есть еще и такие свойства, как FONTDIMEN, LIGTABLE и CHARACTER, значения которых имеют более сложную структуру, включающую в качестве составного элемента и списки свойств.
Имя соответствующего свойства может появляться только в определенных, допустимых для него списках свойств. Например, имя CHARWD не может появиться на внешнем уровне или внутри FONTDIMEN.
Индивидуальные пары «свойство-значение» (property-and-value) в списке свойств могут следовать.в любом порядке. В частности, в рассмотренном выше примере, свойство ‘SHRINK’ предшествует свойству ‘STRETCH’, хотя в TFM-файле всегда первым записывается значение для параметра растяжимости. Можно было бы дать даже информацию о литерах наподобие ‘f ’ еще до того, как задано число единиц (units) в кегле, или до того, как сформирована таблица лигатур или кернинга. Свойство LIGTABLE, однако, является исключением из этого правила. Индивидуальные элементы из списка свойств LIGTABLE могут быть переупорядочены только в пределах некоторого фрагмента этого списка и так, чтобы при этом значение таблицы не менялось.
Если пары «свойство-значение» опущены, используются значения свойств по умолчанию. В частности, выше уже отмечалось, что значение по умолчанию для свойства CHARDP равно нулю. Фактически для каэюдого из свойств, характеризующихся числовыми значениями, в качестве значения по умолчанию принято нулевое, если не оговорено другое.
Если одно и то же имя свойства используется более чем один раз, программа VPtoVF не заметит этого противоречия, она будет просто использовать последнее из встретившихся значений. И опять, список свойств LIGTABLE представляет собой исключение из общего правила. Программа VPtoVF выдаст соответствующее диагностическое сообщение, если обнаружит, что для некоторой литеры имеется более чем одна метка. И конечно многие из элементов в списке свойств LIGTABLE имеют одинаковые имена.
VPL-файл включает также информацию о том, как создать каждую из литер, путем ли выбора литер из других шрифтов, путем ли рисования линий и т. д. Такого рода информация является значением свойства ‘МАР’. Это свойство может быть проиллюстрировано следующим примером:
(MAPFONT D О (FONTNAME Times-Roman))
(MAPFONT D 1 (FONTNAME Symbol))
(MAPFONT D 2 (FONTNAME cmrlO)(FONTAT D 20))
(CHARACTER 0 0 (MAP (SELECTFONT D 1)(SETCHAR C G)))
(CHARACTER 0 76 (MAP (SETCHAR 0 277)))
(CHARACTER D 197 (MAP
(PUSH)(SETCHAR C A)(POP)
(MOVEUP R 0.937)(MOVERIGHT R 1.5)(SETCHAR 0 312)))
(CHARACTER 0 200 (MAP (MOVEDOWN R 2.1)(SETRULE R 1 R 8)))
(CHARACTER 0 201 (МАР
(SPECIAL ps: /SaveGray currentgray def .5 setgray)
(SELECTFONT D 2)(SETCHAR C A)
(SPECIAL ps: SaveGray setgray)))
(Эти спецификации записываются в дополнение к обычной информации, содержащейся в PL-файле. Атрибут МАР может комбинироваться с другими атрибутами вроде CHARWD, или же его можно дать отдельно).
В данном примере формируемый виртуальный шрифт составляется из литер, которые берутся из трех реальных шрифтов: ‘Times-Roman’, ‘Symbol’ и ‘cmrlO at 20\u’ (где \u — единичный размер (unit size) в данном VPL-файле). Литера с восьмеричным номером '0 набирается как ‘G’ из шрифта Symbol, литера '76 —как символ '277 из обычного шрифта Times. (Если не выбран какой-либо другой шрифт, то по умолчанию будет использован шрифт с номером 0. Если значение атрибута МАР не задано, будет использована литера с тем же самым номером из шрифта по умолчанию.)
Более интересен знак с десятичным номером 197. Сначала набирается А (в шрифте Times, который используется по умолчанию), при этом команда выбора символа заключается внутрь пары команд PUSH и POP, чтобы была возможность восстановить первоначальную позицию. Затем, после передвижения вверх на .937 единиц и вправо на 1.5 единицы, набирается знак акцента '312.
Чтобы напечатать в рассматриваемом виртуальном шрифте литеру с восьмеричным номером '200, перемещаемся вниз на 2.1 единицы, а затем набираем линейку.
Наконец, чтобы набрать символ '201, надо проделать ряд действий, требующих умения интерпретировать команды языка PostScript. В данном примере значение PostScript-параметра «color» (цвет) устанавливается равным 50% серого, а затем в этом «цвете» набирается ‘А’ из шрифта cmrlO at 20\u.
В общем случае, атрибут МАР для некоторого виртуального символа может иметь любую последовательность команд набора, которые могут появиться на странице некоторого DVI-файла. Можно даже на целую страницу отобразить единственный символ.
Теперь, закончив анализ этого гипотетического примера, рассмотрим полную грамматику VPL-файлов, начиная с (оставшихся неизменными) грамматических правил для PL-файлов. На самом высоком (outermost) уровне в любом PL-файле имеют силу свойства со следующими именами:
CHECKSUM (четырехбайтовое значение). Значение этого свойства, которое должно быть неотрицательным целым числом, меньшим чем 232, используется для идентификации конкретного варианта шрифта; оно должно совпадать со значением контрольной суммы, хранящимся в самом шрифте. Если значение этого свойства в явном виде задано равным нулю, то это означает требование не выполнять проверку совпадения указанных контрольных сумм. Если в VPL-файле контрольная сумма не задана, программа VPtoVF вычислит такое ее значение, которое было бы получено METAFONT’om для этих же самых данных.
DESIGNSIZE (числовое значение, по умолчанию равное 10). Значение этого свойства, которое должно быть действительным числом из диапазона 1.0 < х < 2048, используется по умолчанию для масштабирования всех других числовых параметров, если только шрифт не загружается со спецификатором ‘at’. Например, если записать Т^Х’овское предложение вида ‘\f ont\A=cmrl0 at 15pt’, то кегель, заданный в соответствующем TFM-файле, будет заменен на величину, равную 15
pt; однако же если записать просто ‘\f ont\A=cmrlO’, будет использован установленный выше размер. Этот параметр задается всегда в единицах разрешающей способности печатающего устройства.
DESIGNUNITS (числовое значение, по умолчанию равное 1). Значение этого свойства должно быть положительным действительным числом. Оно устанавливает, чему в единицах (units) равняется кегель (или же возможный ‘at’-размер, если шрифт подвергается масштабированию). Пусть, например, имеется шрифт, оцифрованный с 600 пикселями на 1 еш, а кегель его равен 1 еш. В этом случае можно было бы записать ‘(DESIGNUNITS R 600)’, если требуется, чтобы все измерения записывались в пикселях.
CODINGSCHEME (строчное значение, по умолчанию равное ‘UNSPECIFIED’). Символьная строка, являющаяся значением данного свойства, не должна содержать скобок, ее длина должна быть меньше 10. Она устанавливает соответствие между числовыми кодами и литерами (символами) шрифта. (1ЁХ эту информацию не использует, но она может потребоваться другим программам.)
FAMILY (строчное значение, по умолчанию равное ‘UNSPECIFIED’). Символьная строка, являющаяся значением данного свойства, не должна содержать скобок, ее длина должна быть меньше 20. Она задает имя семейства, к которому относится данный шрифт, например, ‘HELVETICA’. (ТЁХ эту информацию пропускает, но она потребуется, например, если будет выполняться преобразование DVI-файлов в PRESS-файлы для оборудования Xerox.)
FACE (однобайтовое значение). Это число, значение которого должно лежать в диапазоне от 0 до 255 включительно, служит для дополнительной идентификации рассматриваемого шрифта в пределах семейства, к которому он относится. Например, полужирные суженные курсивные шрифты (bold italic condensed fonts) могут иметь одно и то же имя семейства со светлыми расширенными шрифтами прямого начертания (light roman extended fonts), различать их будет только значение параметра (байта) FACE. (Т£Х эту информацию не использует, но она потребуется, например, если будет выполняться преобразование DVI-файлов в PRESS-файлы для оборудования Xerox.)
SEVENBITSAFEFLAG (строчное значение, по умолчанию равное ‘FALSE’). Начальное значение этого свойства должно быть либо ‘Т’ (true), либо ‘F’ (false). Если оно равно «true», коды символов, меньшие 128, никогда не приведут к кодам 128 и больше через лигатуры, списки символов или расширяемые символы. (ТЁХ82 не нуждается в этом флаге, но более старые версии ТЁХ’а допускают только TFM-файлы в семибитовой кодировке. Программа VPtoVF вычисляет правильное значение этого флага и выдает сообщение об ошибке, только если заданное значение «true» некорректно.
HEADER (однобайтовое значение, за которым следует четырехбайтовое значение). Однобайтовая часть значения этого свойства должна лежать между 18 и максимумом, величина которого может увеличиваться или уменьшаться в зависимости от значения параметра периода компиляции maxJieader_bytes. Четырехбайтовая часть входит в заголовочное слово, индексом которого является однобайтовое значение; например, чтобы установить header[18] <— 1, можно записать ‘(HEADER D 18 0 1)’. Эта нотация используется для заголовочной информации, которая в настоящее время является неименованной. (ТЁХ не использует ее.)
FONTDIMEN (значением является список свойств). См. ниже для тех имен, использование которых допускается в данном списке свойств.
LIGTABLE (значением является список свойств). См. ниже относительно правил для этого специализированного списка свойств.
BOUNDARYCHAR (однобайтовое значение). Если этот ограничивающий символ появится в команде LIGTABLE, он задает «конец слова», символизируя его собой. Если никакого ограничивающего символа не задано, и внутри LIGTABLE нет LABEL BOUNDARYCHAR, границы слов никак не влияют на формирование лигатур и кернинг.
CHARACTER. Значение этого свойства состоит из однобайтового целого числа, за которым следует список свойств. Целое число представляет собой номер символа, имеющегося в шрифте; список свойств для некоторого произвольного символа определяется ни^е. Значением этого свойства по умолчанию является пустой список свойств.
Числовые значения для элементов списка свойств могут задаваться в различной форме, которая определяется префиксной буквой.
С обозначает ASCII-символ, который должен быть стандартным видимым (отображаемым) знаком, но не скобкой. Числовое значение его поэтому будет лежать между '41 и '176 (кроме '50 и '51\
D обозначает десятичное целое число без знака, которое должно быть меньше 232, т. е. наибольшая его величина ‘D 4294967295’.
F обозначает трехбуквенный код Xerox (face code) для указания вида шрифта; допустимыми кодами являются MRR, MIR, BRR, BIR, LRR, LIR, MRC, MIC, BRC, BIC, LRC, LIC, MRE, MIE, BRE, В IE, LRE и LIE, обозначающие целые числа от 0 до 17 соответственно.
О обозначает восьмеричное целое число без знака, которое должно быть меньше 232, т. е. наибольшее допустимое значение его ‘0 37777777777’.
Н обозначает шестнадцатеричное целое число без знака, которое должно быть меньше 232, т. е. наибольшее допустимое значение его ‘Н FFFFFFFF’.
R обозначает действительное число в десятичной записи, которому, возможно, предшествует знак ‘+’ или ‘-’, и включающее, возможно, десятичную точку. Абсолютное значение этого числа должно быть меньше 2048.
Имена свойств, которые могут включаться в список свойств FONTDIMEN, соответствуют различным ТуХ’овским параметрам, каждый из которых имеет значение в виде (действительного) числа. Все эти параметры, кроме SLANT, измеряются в проектных единицах. В список допустимых включаются следующие имена: SLANT, SPACE, STRETCH, SHRINK, XHEIGHT, QUAD, EXTRASPACE, NUM1, NUM2, NUM3, DENOMI, DEN0M2, SUP1, $>UP2, SUP3, SUB1, SUB2, SUPDROP, SUBDROP, DELIM1, DELIM2 и AXISHEIGHT, для параметров от 1 до 22. Для параметров с номерами от 8 до 13 могут также использоваться альтернативные имена DEFAULTRULETHICKNESS, BIGOPSPACING1, BIG0PSPACING2, BIG0PSPACING3, BIG0PSPACING4 и BIG0PSPACING5.
Обозначение вида ‘PARAMETER п’ представляет собой другой способ задания п-го параметра; например, ‘(PARAMETER D 1 R -.25)’ —альтернативный способ указать, что свойство SLANT имеет значение, равное —0.25. Значение п здесь должно быть положительным и меньшим, чем значение параметра max^aram-words.
Элементы списка свойств CHARACTER могут принадлежать к одному из следующих шести типов.
CHARWD (значение — действительное число) обозначает ширину литеры в проектных единицах.
CHARHT (значение — действительное число) обозначает высоту литеры в проектных единицах.
CHARDP (значение — действительное число) обозначает глубину литеры в проектных единицах.
CHARIC (значение — действительное число) обозначает поправку на курсив литеры в проектных единицах.
NEXTLARGER (однобайтовое значение) задает литеру, которая идет следующей за рассматриваемой в «списке литер» («charlist»). Это значение должно быть номером литеры в шрифте, причем не должно быть циклов, бесконечных по номеру литеры.
VARCHAR (значение — список свойств) задает некоторый расширяемый (extensible) символ (литеру). Этот элемент и NEXTLARGER являются взаимно исключающими, т. е. их нельзя использовать совместно в рамках одного и того же списка CHARACTER.
Элементами списка свойств VARCHAR могут быть TOP, MID, ВОТ или REP; все они имеют целочисленные значения, равные либо нулю, либо номеру требуемой литеры в рассматриваемом шрифте. Нулевая величина для TOP, MID или ВОТ означает, что соответствующий фрагмент рассматриваемого расширяемого символа отсутствует. Ненулевые значения этих параметров, или нулевое значение для REP, обозначает код символа, используемого для верхнего, среднего, нижнего или повторяемого фрагмента расширяемого символа.
Список свойств LIGTABLE содержит элементы четырех видов, задающие некоторую программу на простом командном языке, который используется Т^Х’ом для формирования лигатур и кернинга. Если имеется несколько списков LIGTABLE, производится их объединение (путем сцепления) в единый список.
LABEL (однобайтовое значение) показывает, что здесь начинается программа для установленного значения символа. Номером символа в шрифте должно быть целое число; список свойств CHARACTER для данного символа не должен содержать полей NEXTLARGER или VARCHAR. За элементом LABEL должен следовать по крайней мере один элемент LIG или KRN.
LABEL BOUNDARYCHAR означает, что здесь начинается программа для лигатур в начале слова.
LIG (два однобайтовых значения). Инструкция вида ‘(LIG с г)’ означает, что «если следующим символом является с, то вставить символ г и, возможно, удалить текущий символ с; в противном случае перейти к следующей инструкции». Символы тис должны содержаться в рассматриваемом шрифте. Непосредственно перед LIG и/или сразу после нее можно ставить слэш (V’), а затем могут следовать знаки > , которых должно быть не больше, чем число используемых знаков Ч*. Соблюдение этих правил дает восемь возможных форм записи для LIG:
LIG /LIG /LIG> LIG/ LIG/> /LIG/ /LIG/> /LIG/»
Слэши Ч' задают сохранение левого или правого символа, присутствующего в первоначальной записи, а символ > задает передачу результатов дальше, без продолжения процесса обработки лигатур.
KRN (однобайтовое значение и значение в виде действительного числа). Инструкция ‘(KRN с г)’ говорит о том, что «если следующим символом является с, то вставить пробел ширины г между текущим символом и с; в противном случае перейти к следующей инструкции». Значение параметра г, задаваемое в проектных единицах, часто бывает отрицательным. Символ с должен присутствовать в рассматриваемом шрифте.
STOP (не имеет значения). Эта инструкция завершает программу формирования лигатур/кернинга. Она должна следовать за инструкцией LIG или KRN, но не LABEL, STOP, или SKIP.
SKIP (значение в диапазоне 0 .. 127). Эта инструкция задает продолжение программы формирования лигатур/кернинга после пропуска определенного числа шагов LIG или KRN. Число идущих подряд инструкций LIG и KRN должно, следовательно, превышать величину, задаваемую рассматриваемой инструкцией.
В дополнение ко всем этим возможностям, в любом списке свойств допускается использование свойства с именем COMMENT (комментарий). Комментарии, включенные в списки свойств, при обработке пропускаются.
Свойства, описанные выше, содержатся в PL-файле. В VPL-файле распознаются также еще и дополнительные свойства; следующие два из них действительны на самом верхнем уровне:
VTITLE (строчное значение, по умолчанию — пустая строка). Значение этого свойства воспроизводится в начале VF-файла (и печатается на терминале программой VFtoVP, когда она просматривает данный файл).
MAPFONT. Значением этого свойства является неотрицательное целое число, за которым следует список свойств. Целочисленная часть значения представляет собой идентификационный номер для шрифтов, используемых в атрибутах МАР. Список свойств, который задает шрифт и относительный размер, определяется ниже.
И одно дополнительное «виртуальное свойство», имеющее место внутри списка CHARACTER:
МАР. Значением его является список свойств, содержащий команды набора. Значением по умолчанию, будет единственная команда SETCHAR с, где с —номер текущего рассматриваемого символа.
Элементы списка свойств MAPFONT могут быть одного из следующих типов.
FONTNAME (строчное значение, по умолчанию —NULL). Это имя, идентифицирующее шрифт.
FONTAREA (строчное значение, по умолчанию — пустая строка). Если шрифт, согласно принятым местным правилам, содержится в нестандартном каталоге, то в данном свойстве задается соответствующее имя каталога. (Это свойство является системно-зависимым, подобно тому как это имеет место для DVI-файлов.)
FONTCHECKSUM (четырехбайтовое значение, по умолчанию равное нулю). Значение этого свойства, которое должно быть неотрицательным целым числом, меньшим, чем 232, может использоваться для проверки соответствия упоминаемого шрифта и загружаемого шрифта. Если данное свойство имеет ненулевое значение, требуется, чтобы оно совпадало со значением параметра CHECKSUM в подключаемом ‘ шрифте.
FONTAT (числовое значение, по умолчанию равно значению DESIGNUNITS в рассматриваемом виртуальном шрифте). Значение этого свойства устанавливается относительно проектных единиц для рассматриваемого виртуального шрифта, следовательно, оно будет меняться, если данный виртуальный шрифт увеличивается или уменьшается. Оно представляет собой значение, которым будет заменен кегель рассматриваемого шрифта, так что все символы будут промасштабированы соответствующим образом.
FONTDSIZE (числовое значение, по умолчанию равно 10). Значение этого свойства является абсолютным и измеряется в единицах разрешающей способности принтера). Оно должно быть равно значению параметра DESIGNSIZE для рассматриваемого шрифта.
Если в каком-либо из строчных значений содержатся скобки, то они должны быть сбалансированы, т. е. число открывающихся и закрывающихся скобок должно совпадать. Головные пробелы в таких строках удаляются, тогда как завершающие — оставляются неизменными.
Наконец, элементы списка свойств МАР представляют собой упорядоченную последовательность команд набора, выбранных из следующего перечня:
SELECTFONT (четырехбайтовое целое значение). Значением этого свойства должен быть номер из ранее определенного свойства MAPFONT. Этот шрифт (или, более точно, последний по порядку следования шрифт, который отображается в данную кодовую страницу, если случится так, что имеются два свойства MAPFONT, задающих один и тот же код) будет использоваться в следующих друг за другом инструкциях SETCHAR до тех пор, пока не будет заменен с помощью другой инструкции SELECTFONT. До того момента, как выполнена первая инструкция SELECTFONT, для отображения каждого из символов неявно выбирается первое по порядку следования определение для MAPFONT.
SETCHAR (однобайтовое целое значение). В текущем выбранном шрифте должен быть символ с этим номером. (Программа VPtoVF не проверяет допустимость данного символа, в то время как программа VFtoVP делает это.) Этот символ набирается в данной текущей позиции, после чего «головка наборного устройства» перемещается вправо на расстояние, определяемое для рассматриваемого символа параметром CHARWD в TFM-файле для соответствующего шрифта.
SETRULE (два значения, представляющие собой действительные числа). Первое из значений, связанное с данным свойством, представляет собой высоту линейки, а второе — ее ширину, оба — в проектных единицах. Если значения и высоты, и ширины положительны, набирается сплошной черный прямоугольник, левый нижний угол которого находится в текущей позиции набора. Затем наборное устройство смещается вправо на заданную ширину.
MOVERIGHT, MOVELEFT, MOVEUP, MOVEDOWN (значения — действительные числа). Наборное устройство изменяет свою текущую позицию в соответствующем направлении (вправо, влево, вверх и вниз соответственно) на заданное число проектных единиц.
PUSH. Запоминается текущая позиция наборного устройства, которая может быть восстановлена с помощью следующей далее команды POP.
POP. Восстанавливается текущая позиция наборного устройства соответственно самой последней по порядку позиции, сохраненной с помощью команды PUSH. Команды
PUSH и POP в любом свойстве МАР должны быть расположены соответствующим образом, наподобие сбалансированных скобок.
SPECIAL (строчное значение). Последовательность символов (литер), начиная с первого символа, отличного от пробела, заканчивающаяся перед первым символом ‘)’, для которого нет парного ему символа ‘С. Она интерпретируется в соответствии с некоторыми локальными соглашениями, с тем же самым системно-зависимым значением, что и команда ‘special’ (rrr) в DVI-файле.
SPECIALHEX (шестнадцатеричное строчное значение). Последовательность символов, отличных от пробела, продолжающаяся до следующего символа ‘)’, должна состоять целиком из шестнадцатеричных цифр и включать четное число таких цифр. Каждая пара шестнадцатеричных цифр задает байт и эта строка из байтов интерпретируется как значение для SPECIAL. (Это соглашение позволяет представлять произвольные байтовые строки в обычном текстовом файле.)
Отображение виртуального шрифта представляет собой рекурсивный процесс, наподобие макрорасширения (т. е. развертывания макроопределения). Поэтому MAPFONT может задать другой виртуальный шрифт, символы которого опять отображаются в другие шрифты. В качестве примера, иллюстрирующего такого рода возможность, рассмотрим следующий любопытный файл с именем recurse.vpl, в котором описывается виртуальный шрифт, являющийся самодостаточным и ссылающимся рекурсивно только на самого себя:
(VTITLE Example of recursion)
(MAPFONT D 0 (FONTNAME recurse)(FONTAT D 2))
(CHARACTER C A
(CHARWD D 1)(CHARHT D 1)(MAP (SETRULE DIDI)))
(CHARACTER С В
(CHARWD D 2)(CHARHT D 2)(MAP (SETCHAR C A)))
(CHARACTER С C
(CHARWD D 4)(CHARHT D 4)(MAP (SETCHAR С B)))
Кегель этого шрифта равен 10 pt (значение по умолчанию), следовательно, литера А в шрифте recurse представляет собой черный квадрат размера 10 х 10 pt. Литера В набирается как литера А из шрифта recurse scaled 2000, т. е. это — черный квадрат размера 20 х 20 pt. И, наконец, литера С набирается как литера В из шрифта recurse scaled 2000, т. е. ее размер будет 40 х 40 pt.
Разработчики виртуальных шрифтов должны проявлять осмотрительность, чтобы избежать бесконечной рекурсии.
Новости из издательства St. Anford Press
[Для выпуска 24 августа 1988 г.]
Мы предлагаем шрифтовую гарнитуру San Serifo, названную в честь Сан Се- рифо, знаменитого мученика, чья жизнь была описана отцом Фонтом в 1776 г. Японские пользователи имеют возможность отдать предпочтение варианту этой гарнитуры с именем Serifu-San.
Дизайнер этих шрифтов Б. О. Дони утверждает, что на ее смелое решение повлияли, в первую очередь, недавние брошюры из словолитен, в которых термин «sans serif» пишется как sans s, т. е. «без s». Она в настоящее время работает над шрифтом sans-stem, т. е. «без штриха», который должен быть выпущен в свет в следующем году.
Шрифты в гарнитурах San Serifo и Serifu-San доступны в различной жирности (weights), от «сверхлегкой» (ultralite) до «тучной» (obese), а также во всех основных цифровых форматах, включая FalseTypeDM для изготовления фальшивых немецких марок.
БукваS
[Первоначально опубликовано в The Mathematical Intelligencer 2 (1980), 114-122.]
Несколько лет назад, когда я начал задумываться над проблемой создания алфавитов, приспособленных для современного типографского оборудования, я обнаружил, что справиться с 25 буквами сравнительно легко. Оставалась буква S* Три дня и три ночи я мучился, пытаясь понять, как же определить правильную форму буквы S. Решение, к которому я в конце концов пришел, требует привлечения некоторых небезынтересных математических методов, и мне кажется, что людям, изучающим математический анализ и аналитическую геометрию, эта проблема будет так же интересна, как и мне в свое время. В этой статье я хочу объяснить, что сейчас считаю «правильной» математикой, описывающей печатные буквы S, а также показать пример языка МЕТА FONT, разработкой которого я в последнее время занимался? (Полное описание METAFONT’a, который представляет собой компьютерную систему и язык, призванные помочь в создании начертаний различных букв, можно найти в [3, часть 3].)
Перед тем как углубиться в технические детали, наверное, стоит упомянуть, почему я вообще стал беспокоиться о таких вещах. Главная причина в том, что современная полиграфическая технология большей частью основана на использовании дискретной математики и информатики, а не на особенностях металлов или отдельных литер. Сейчас задача создания печатной формы для страницы в основном сводится к построению огромной матрицы из 0 и 1, где 0 обозначают пустые места, а 1 — чернильные точки. Я хотел, чтобы второе издание одной из моих книг было таким же, как первое, хотя это последнее и было напечатано с использованием старой технологии отливки свинцовых форм; и когда я обнаружил, что эта задача может быть решена при помощи
В английском алфавите 26 букв. — Прим, иерее. *
Все буквы и символы в этой статье были созданы математическими методами при помощи METAFONT’a и набраны с использованием экспериментального программного обеспечения, принадлежащего автору.
соответствующих методов дискретной математики и информатики, я не смог устоять перед соблазном решить ее самому.
Подробнее подоплека моей работы раскрыта в [2], там же обсуждается история первых попыток подойти к начертанию букв математически. В частности, там рассказывается о нескольких, предложенных в XVI и XVII вв. способах строить буквы S геометрически при помощи циркуля и линейки.
Франческо Торниелло в 1517 г. опубликовал геометрический алфавит, типичный для этих ранних попыток. Рассмотрим его метод построения буквы S (рис. 1, взятый из [4], с. 45), чтобы почувствовать, с какими проблемами нам придется столкнуться. Используя современную математическую терминологию, можно так перефразировать его слова:
Будем чертить букву S в квадрате 9x9, который в декартовых координатах можно задать как множество точек (ж, т/), где 0<т<9и0<т/<9. Мы определим 14 точек на границе буквы, обозначив их (ач,7/1), (яг, 2/2),..., (244,2/14). Точка 1 имеет координаты (4.5,9), и из этой точки проводится дуга окружности с центром (4.5, 5.5) радиуса 3.5 в точку 2, где Х2 = 6. [Таким образом, т/2 = 5.5 + х/10 ~ 8.66.] Проводим маленькую дугу радиуса 0.5 с центром (6.5,9) из точки 3 = (6.5,8.5) в (7,9). Из точки 4 = (6,7) проводим прямую линию в точку касания с этой маленькой дугой; назовем ее точкой 5. [Далее мы увидим, что точка 5 имеет координаты (бЦ,8||); интересно поразмышлять, насколько такой результат осчастливил бы Торниелло.] Теперь опускаем дугу радиуса 2 с центром (4, 7) из точки 6 = (4, 9) в точку 7, где я? = 3, а 7/7 < 7 [таким образом, 7/7 = 7 — V3 ~ 5.27]. Проводим прямую линию из точки 7 в точку 8 = (5,4). Теперь проводим дугу с центром (4.5, 7|) из точки 4 в точку 9 = (3.5, 6), продолжая ее прямой линией до точки 10 = (6,4.5). Из этой точки в точку 11 = (3, 0.5) проводится полуокружность радиуса 2.5 с центром (4.5, 2.5). Проводим еще одну маленькую дугу радиуса 1 с центром (2.5, т/), соединяющую точку 11 с точкой 12, где Z12 = 1| [таким образом, т/ = (1 — V3)/2 « —0.37, а т/12 = (У39 + 4 — 4-\/3)/8 ~ 0.41]. Проводим дуги радиуса 2: одну— из точки 8 в точку 13, при этом абсцисса центра дуги равна 4, а тхз = 4.5 [таким образом, центр имеет координаты (4,4 — V3) ~ (4,2.27), а 7/13 = 4 — V3 — V3.75 ~ 0.33]; и другую —из точки 13 в точку 14, при этом абсцисса центра дуги равна 4.5, а т/14 = 2 [таким образом, центр имеет координаты (4.5, 6—V3 —V3.75) ~ (4.5, 2.33), а
zu = 4.5 - ^4-(4-V3-V3J5)2,
что приблизительно равно 2.53]. Наконец, прямая линия соединяет точку 14 с точкой 12.
Для читателя будет интересно взять лист разграфленной бумаги и воспроизвести конструкцию того времени, перед тем как двинуться дальше. Вообще-то описание Торниелло было не таким точным, как данное выше: я постарался почерпнуть из его текста как можно больше информации; кажется, у него с буквами S возникло не меньше сложностей, чем у меня, потому что все остальные буквы у него определены гораздо четче. Главное, что я позволил себе подредактировать, это координаты центра дуги, соединяющей точки 4 и 9: вместо указанных Торниелло (4.5, 7|) я передвинул центр в расположенную
Рис. 1. Так Франческо Торниелло «вписывал букву S в квадрат» в 1517 г.
рядом точку (4.5, 7|), при этом я оставил радиус неопределенным (он утверждал, что радиус должен равняться 1.5, но на самом деле он равен V145/8, что немного больше), причина этого изменения в том, что точка (4.5, 7|) не равноудалена от точек 4 и 9.
Заметьте, что дуга окружности, соединяющая точки 10 и 11, касается линии шрифта в точке (4.5,0) и имеет вертикальную касательную в точке (7,2.5); это дает прекрасный результат, так как
З2 +42 = 52,
и я думаю, Торниелло достаточно хорошо знал математику, чтобы воспользоваться этим приятным совпадением в своем замысле. Он так и не указал, какие кривые должны соединять точки 1 и б или точки 2 и 3; очевидно, через точки 1 и б нужно провести прямолинейный отрезок, а вторая кривая может быть нарисована как угодно, лишь бы это хорошо смотрелось.
Нахождение точки 5 представляет собой элементарное, но поучительное упражнение в аналитической геометрии: пусть даны положительные числа h и т, требуется найти точку (х,у) в верхней правой части окружности радиуса т с центром в начале координат, такую, что прямая, проведенная из точки (—r,h) в точку (х,у), касается окружности в точке (х,у) (рис. 2). Имеем: х2 +т/2 = г2 и у/х = tg 0 = (х 4- t^IQi — у), откуда х2 4- тх 4- у2 — yh = 0 и rx = hy - г2. Это
Рис. 2.
Задача, возникающая в конструкции Торниелло: найти х и у при заданных г и h.
приводит к уравнению (hy - r^hy 4- т^у^у - А) = тх^та + г2) 4- т2у(у - h) = О, откуда y(h2y — hr2 4- г2у — hr2) = 0, и вскоре мы приходим к искомому решению
_ h2r — г3 _ 2hr2
Х ~ № +~ /г2 +г2 '
Решение представляет собой рациональную функцию от h и г (т. е. извлечение квадратных корней не требуется), так как другая точка касания —это (—г,0); эта вторая точка также удовлетворяет данным уравнениям. Рене Декарту несомненно понравилась бы такая демонстрация мощи его координатного метода.
Конструкцию Торниелло без особого труда можно выразить на языке МЕТА FONT, языке, который я в последнее время разрабатывал для задания формы символов в виде, удобном для компьютерной обработки. Хотя в построениях при помощи циркуля и линейки на самом деле используется не так уж и много возможностей METAFONT’a, но кое-что о нем мы можем узнать уже из этого первого примера.
Опорные точки конкретного чертежа задаются в METAFONT’obckom языке уравнениями на их абсциссы и ординаты; затем можно сказать ‘draw г..у\ чтобы провести прямую линию из точки г в точку j. Можно также сказать ‘draw г{а,у5} . .j^bSy, чтобы провести кривую, выходящую из точки г в направлении вектора (а, Р) и приходящую в точку j по направлению (7,5). Эта кривая будет дугой окружности, если существует окружность, проходящая через точки г и у в указанных направлениях, и при условии, что дуга составляет не более половины окружности. Таким образом, конструкция Торниелло в точности воспроизводится следующей программой для METAFONT’a:
Xi = 4.5u; yi = 9и;
а?2 = би; у2 - 5.5и = sqrt ((3.5и)(3.5и) - (х2-4.5и)(а?2-4.5и));
draw 1{t/i — 5.5и,4.5и — a?i} .. 2^у2 — 5.5и,4.5и — х2};
хз = б.5и; уз = 8.5и;
Х4 = би; у4 = 7и;
ж5 = (6 + 1б/17)щ т/5 = (8 + 13/17)щ
draw 3{9u — уз, хз - 6.5u} .. 5{9u — уь,х^ - 6.5u};
draw 4.. 5;
a?6 = 4u; т/e = 9tz;
x^ = 3tz; 7u — т/т = sqrt ((2tz)(2tz) — (x? — 47z)(a?7 — 4u));
draw 6{7tz — ye, xq — 4u} .. 7{7u — 7/7, X7 — 4u};
xs = 5tz; т/g = 4tz; draw 7.. 8;
Xg = 3.5tz; 7/9 = 6tz;
^15 = 4.5tz; 7/15 - 7.125tz =
sqrt ((д:9-4.5и)(:г9-4.5и) 4- (7/9-7.125tz)(7/9-7.125tz));
draw 4{7.125tz — 7/4, X4 — 4.5u} ..15.. 9{7.125u — yg, xg — 4.5u};
a?io = 6tz; ую = 4.5u; draw 9.. 10;
Хц = 3tz; T/ц = .5tz;
draw 10{т/ю - 2.5tz,4.5tz - а?ю} .. H{?/ii — 2.5tz,4.5tz - a?n};
$16 = 2.5u; t/n -t/16 = sqrt((u)(u) - ($n-$i6)(xn-$i6));
$12 = 1.875u; t/12 -t/16 = sqrt((u)(u) - ($i2-zieX^n-$ie))i
draw ll{yi6 -уп.Хц — $ie) • • 12{i/i6 -yi2,xi2 -$ie};
^13 = 4.5tz; a?i7 = 4u;
Уз — У17 = sqrt((2u)(2iz) - ($8-Х17)(х8-$17));
У17 -1/13 = sqrt((2u)(2u) - ($i3-$i7)(xi3-$i7));
draw 8{y8 - ym x17 -xs}.. 13{yis - 1/17, $17 - $13};
Xis = 4.5u; t/is - 1/13 = sqrt((2u)(2u) - ($18-$1з)($18-$1з));
1/14 = 2u; $18 — $14 = sqrt((2u)(2u) - (У18-У14КУ18-У14У);
draw 13{i/i3 - yi8,2:1s - $13} .. 14{t/i4 - i/i8, 2?i8 - $14};
draw 14.. 12.
Здесь «7z» — произвольная единица длины, которая может быть использована в качестве масштабирующего множителя для регулировки общего размера чертежа. Эта программа на первый взгляд выглядит несколько громоздко, но на самом деле ее нетрудно понять, если сравнить с неформальным описанием, данным выше. В программе введены еще несколько точек, обозначенных 15, 16, 17 и 18; точка 15 нужна, чтобы уговорить METAFONT провести дугу окружности, большую чем полуокружность, а остальные три точки — центры окружностей, проводимых в построении. Основное соотношение, использованное в программе, состоит в том, что дуга окружности с центром ^к^Ук\ проходящая по часовой стрелке через точку (xt,yi), идет в направлении ^yi — у к, Хк — ^г}, проходящая же против часовой стрелки — в направлении {i/fc -yi,Xi -$fc}.
На рис. 3 изображено то, что METAFONT нарисует по заданному описанию. METAFONT также завершит чертеж подходящими кривыми (уже не дугами окружностей), если добавить команды:
draw 1.. 6;
draw 2{т/2 - 5.5tz,4.5tz - х2} .. 3{9u - уз,хз - б.5и}.
Направления этих новых кривых выбраны так, чтобы касательные к ним совпадали с касательными к нарисованным ранее кривым в точках их соприкос-
6 1
15 "
ч 1
»2 3 J
5
i
4
7
8
10
14
17
, *18
12*
II1
13^
• 16
Рис. 3. Программа METAFONT’a в тексте даст это изображение торниелловской буквы S.
Рис. 4.
Кривая, изображенная на рис. 3, после завершения и заливки.
новения. Если METAFONT попросить выполнить заливку пространства внутри ограничивающих кривых, то получится фигура, изображенная на рис. 4.
Дуга окружности, соединяющая точку б с точкой 7, в этой последней имеет направление {7и — ут^т — 4и} = {\/Зщ —^}, но ее продолжение — прямая линия из точки 7 в точку 8 —резко меняет направление на {а?8 — ж?, Уз — Ут^ =
{2u, (V3 — 3)u}. Этот перегиб на рис. 4 почти незаметен, но с математической точки зрения это недопустимо. Такие же перегибы имеются в точках 8, 9, 10, 11 и 13, особенно явно они видны в точках 9 и 13; чтобы устранить эти недостатки, рисунки в книге Торниелло, вероятно, были немного подправлены (о чем Торниелло не упомянул). По-видимому, в XVI столетии стандарты были еще не так строги, но сейчас мы не хотим, чтобы компьютеры рисовали нам такие угловатые линии.
Рис. 5.
Немного видоизменив рис. 4, мы сделали соединения кривых более гладкими.
Так как у METAFONT’a нет обязательной привязки к дугам окружностей, он, как и художник Торниелло, автоматически сделает поправки, достаточно лишь указать во всех опорных точках совпадающие направления. На рис. 5 изображен результат, если в качестве касательных в точках 7, 8, 9 и 10 взяты прямолинейные отрезки, а касательная в точке 13 горизонтальна. Направление кривой в точке 11 соответствует дуге окружности, проведенной из точки 10. Далее, точка б была смещена относительно точки 1 так, чтобы избежать неудачного плоского участка вверху. Кривые, проходящие через эти точки, теперь уже не являются окружностями, но достаточно похожи на них, чтобы обмануть большинство людей, и вряд ли Торниелло обиделся бы на такое приближение.
Буква « S » времен Возрождения для современного читателя выглядит тонковатой. Можно попросить METAFONT сделать ее пожирней, увеличив все абсциссы на 20%, а ординаты оставив без изменения; на рис. 6 изображен результат. Заметьте, что такое растяжение превращает окружности в эллипсы. Если бы Торниелло попробовал описать эту фигуру, используя только дуги окружностей, он столкнулся бы с серьезными трудностями; можно вспомнить древних астрономов, которые находили весьма обременительным использование окружностей вместо эллипсов в качестве моделей для планетарных орбит.
Рис. 6. Этот рисунок получается растяжением рис. 5 по горизонтали на 20%; окружности превратились в эллипсы.
На этом примере можно получить некоторое представление о сложностях, возникающих при определении правильной формы букКы S. Но на самом деле я искал решение более общей проблемы, чем та, с которой столкнулся Торниел- ло: вместо того чтобы описать одну конкретную S, мне нужно было множество различных вариантов, включая полужирные S, которые намного темнее обычного текста. Недавно я обсуждал это с Эланом Перлисом, который обратил внимание на то, что главная сложность, возникающая каждый раз, когда мы хотим что-либо автоматизировать, —это «искусство превращения постоянных в переменные», как он это называет. В случае рисования букв мы хотим не просто взять конкретный чертеж и описать его математически, в действительности мы хотим обнаружить законы, по которым этот чертеж построен, так чтобы можно было сконструировать функцию от подходящих параметров, генерирующую бесконечно много чертежей (включая и исходный). Моей целью было создать целые алфавиты, зависящие от дюжины или от двух параметров, так чтобы при их варьировании все буквы изменялись сходным образом.
Рассмотрев эти конструкции времен Возрождения и многие современные варианты начертания буквы S, я пришел к заключению, что основные черты S-образного изгиба, которые я искал, должны быть подобны изображенным на рис. б. Каждая ограничивающая кривая должна быть эллипсом, переходящим в прямую линию, затем вновь превращающуюся в эллипс. Это привело меня к постановке следующей задачи: как найти эллипс, самая верхняя точка которого имеет координаты (xt,yt), а самая левая точка — координаты (xi,yi), yi произвольно, при этом эллипс должен касаться прямой с угловым коэффи-
циентом а, проходящей через точку ^хС1ус\ при заданных Xt, yt^ хц а, хс и ус ? (Большая и малая оси искомого эллипса должны быть параллельны осям координат; иными словами, эллипс должен быть симметричен относительно горизонтали и вертикали. См. рис. 7 на следующей странице.) Причины постановки такой задачи должны быть ясны из предшествующего обсуждения: мы знаем точку, которая должна быть верхней точкой S-образного изгиба, знаем также, как далеко влево продолжается этот изгиб, кроме того, мы помним про прямую, соединяющую верхнюю и нижнюю части изгиба.
Задача, сформулированная в предыдущем абзаце, интересна для меня по нескольким причинам. Во-первых, у нее, как мы увидим, красивый ответ. Во- вторых, этот ответ действительно приводит к приемлемым S-образным изгибам. В-третьих, этот ответ не так уж и тривиален; в течение около двух лет я четырежды натыкался на эту задачу и каждый раз не мог найти свои записи с ее решением, так что я проводил несколько часов, выводя эти формулы снова и снова каждый раз, когда они мне требовались. Наконец, я решил написать эту статью, чтобы больше мне не пришлось выводить ответ заново.
Точка (xtl yi) — центр искомого эллипса. Пусть (х, у) — точка касания этого эллипса с прямой, имеющей угловой коэффициент а и проходящей через точку (^о Ус), как показано на рис. 7. Наша задача сводится к решению системы трех уравнений с тремя неизвестными х, у и ур
x-xtV / У - Щ Уxi-xt) yyt-yj
Ус-ухс — X
_ / yt-yi\ x-Xt\Xi-XtJ y-yi
Первое из этих уравнений — стандартное уравнение эллипса, второе —стандартное уравнение для углового коэффициента; третье получается, если продифференцировать первое:
= 0,= 0,2^^ + Му^/<
= 0,
= 0,
W - xty(yt - У1Г
и положить dy/dx равным a.
Перед тем как попытаться решить систему уравнений (*), мне хотелось бы ввести обозначение, которое весьма помогало мне в моей работе по математическому описанию шрифтов. Будем использовать а [я, у] как сокращение для
х + ot(y — ж),
что можно воспринимать как «а часть пути от ос до у». Таким образом, 0[^, у] = х\ 1[ж,у] = у; ^[х, у] — это середина отрезка с концами х и у; | [яхточка, расположенная на пол пути между этой серединой и точкой у; а 2[х,у]
Рис. 7.
Задача: найти х, у и. уi при заданных х^ yt, х^ а^ хс 'а ус.
расположена на том же расстоянии от у, что и ж, но с другой стороны. Легко выводятся тождества типа а[д:,д:] = х и а:[а;,7/] = (1 — а)[т/,д:]. Производя различные геометрические построения, часто приходится обращаться к таким объектам, как точка в трети пути от А до В; именно это и обозначает запись |[А, BY Я называю это «связующей».
Одно из применений записи с квадратными скобками состоит в нахождении пересечения (х, т/) двух данных прямых, одна из которых проходит через точки (xi,yi) и (х2)Уз\ а другая —через ^^Уг) и (х4^у4\ Это пересечение можно найти, заметив, что существуют некоторое число а, такое, что
х = a[xi ,х2] у = а[1/1, у2], и некоторое число 0, такое, что
X = j3[x3,x4] У = Р[уз,У4]-
Систему этих четырех линейных уравнений легко решить относительно х, у, а и 3\ и на самом деле METAFONT автоматически решает системы линейных уравнений, что позволяет в программах МЕТА FONT’а легко находить пересечение прямых.
Существует также любопытное применение записи с квадратными скобками к эллипсам. В общем уравнении эллипса
)
2/\2
\ Утах — У0 /
МОЖНО ввести обозначения X = Q^O) ^тах] И у = /3[l/0> 2/тах]> сведя его к гораздо более простому уравнению
а2 + /З2 = 1.
Возвращаясь к нашей задаче про эллипс, положим
х = a[xt,xz], X = х — xt, а = xi — xt,х = a[xt,xz], X = х — xt, а = xi — xt,у = Plyь у А,
х = a[xt,xz], X = х — xt, а = xi — xt,
х = a[xt,xz], X = х — xt, а = xi — xt,
v = yi-yt, b=(yc- охс^ - (yt - <rxt).
Теперь систему (*) можно переписать в следующем виде:
а2 + /?2 = 1;
(**)(**)b + сХ = (1 - ^Y;
(**)
(**)
otY = ааР;
X = аа.
Мы получили систему четырех уравнений с четырьмя неизвестными (а, /3, X, У), так что может показаться, что мы усложнили задачу; но эти уравнения имеют гораздо более простой вид. Можно освободиться от а, вернувшись к трем неизвестным:
X2 + а2р2 = а2 ;(1)
Ь + аХ = (1 —/?)У;(2)
XY^a2ap.(3)
Умножив (3) на (1 — /3) и подставив (2), получим
X^b-VaX^a2aP^-PV
и, применив сюда (1), чудесным образом получаем
ЬХ = а2аЦЗ-\у(4)
Отсюда следует, что ^а2а^р — I))2 4- а2Ъ2Р2 = а2Ь2, т. е.
а2(р - 1)(а2а2(р - 1) + Ь2ЦЗ + 1)) = 0.(5)
При а = О уравнение вырождено, оно имеет бесконечно много решений (X,Y) = (0,6/(1 — /3)), где — 1 < Р < 1. Другой вырожденный вариант получается при b = 0, тогда решений нет, за исключением случая аа = 0, когда имеется бесконечно много решений с произвольным Y и (X, а,Р) = (0,0,1). В остальных случаях нетрудно убедиться, что /3^1, поэтому (5) однозначно определяет величину /3, используя это вкупе с уравнением (4), получаем полное решение:
а = —2aba/(a?a2 4- Ь2);
/3 = (aV-62)/^
X = — 2а2Ъа/{а2а2 4- 62);
Y = (Ь2 -а2а2)/2Ь.
С удивлением я обнаружил, что система квадратных уравнений (**) имеет корни, выражающиеся рациональными функциями. Между этим решением и ответом к задаче рисунка 2 наблюдается любопытное сходство.
Переходя от (6) обратно к формулировке исходной задачи (рис. 7), пусть (^t)Ут) —точка на прямой с угловым коэффициентом сг, проходящей через (^о^/с), так что ут = ус 4- o*(xt — хс\ Тогда единственное решение выглядит следующим образом:
= 2<j(rcZ -XtY^t - Ут)
Х Xt-V^yt-УтУ '
2<T2(xt -XtV(yt -Ут), .
(7>
(yt -УтУ -<T2(xt -XtYyi = yt77х,
2(j/t Ут)
кроме вырожденных случаев xi = xt и ут = yt-
Как-то раз я попробовал решить эту задачу, используя возможности автоматического решения уравнений компьютерной алгебраической системы MACSYMA [5, 7], чтобы понять, как скоро компьютеры заменят математиков при проведении таких расчетов. MACSYMA правильно нашла решение (X, У, (3) системы уравнений (1), (2), (3) примерно за 17 секунд, но ничего не сказала о вырожденных решениях, появляющихся при ab = 0. На решение системы четырех уравнений (**) MACSYMA потратила ровно столько же времени, что и на систему (1), (2), (3). Но когда я попросил систему MACSYMA решить исходные три уравнения (*) относительно х, у и уц после примерно минуты и двадцати секунд появилось сообщение о том, что памяти компьютера не хватает, несмотря на то что я упростил (*), заменив (хС1ус) на (х^ут). Так что я успокоился, поняв, что уравнения (*) не совсем уж тривиальны и преобразование к виду (**) было важным шагом.
Рис. 8. Хорошая буква S получается, если провести два незавершенных эллипса, воспользовавшись методом, изображенным на рис. 7, и затем осуществить заливку пространства между ними, используя перо с диаметром, равным толщине «соединительных штрихов» требуемой буквы.
Это решение задачи об эллипсе немедленно приводит к искомым S- образным изгибам, так как можно осуществить заливку пространства между двумя кривыми, состоящими из прямых и эллиптических участков: одной, проходящей через точки (a;t,7/t), (х^,^), (х^у') и (xc;,yfcY и другой, проведенной через (xt,ytY (x^y^Y ^"^у") и (ж^УсУ ПРИ этом расстояние между x't и х" выбирается в соответствии с требуемой толщиной штриха в левой части, а расстояние между т/' и у" — в соответствии с требуемой толщиной штриха в центральной части. (См. рис. 8. На самом деле S-образный изгиб рисуется круглым пером малого, но положительного радиуса, центр которого следует по проведенным кривым, поэтому в действительности граница не является идеальным эллипсом.) С правой нижней частью S поступают, конечно, так же, как и с левой верхней.
Q Q Q Q Q Q С
000)0 >0 >0 10
Рис. 9. Можно исследовать различные возможности, варьируя параметры. Здесь угловой коэффициент изменяется, а остальные характеристики зафиксированы. Угловые коэффициенты на рисунках составляют соответственно 2/5, 1/2, 2/3, 1, 3/2, 2 и 5/2 от «правильного» значения углового коэффициента на среднем рисунке.
Применив этот метод, варьируя угловой коэффициент сг, но оставляя все остальные параметры без изменения, получим различные варианты S- образного изгиба, изображенные на рис. 9. На рис. 10 изображена буква S с таким же угловым коэффициентом, как у средней буквы на рис. 9, но с утолщенными вертикальными участками в левой верхней и правой нижней частях буквы. Одно из главных преимуществ математического, параметрического метода в том, что он позволяет без особого труда проводить множество экспериментов до тех пор, пока не найдутся те значения параметров, которые полностью вас удовлетворят. Программу METAFONT’a для букв S, изображенных на рис. 9 и 10, зависящую от соответствующих параметров, можно найти в приложении в конце статьи.
Больше двух лет я радостно использовал этот метод для создания букв S, но однажды я попросил METAFONT нарисовать очень большую S и результат получился неожиданно уродливым. Взглянув еще раз на якобы замечательные буквы S, нарисованные ранее, я стал замечать кое-где дефект, который для используемых мною небольших масштабов был сравнительно безобиден. При большом увеличении этот дефект начинал бросаться в глаза, и я понял что история еще не закончена.
Рисунок 11 иллюстрирует это новое затруднение в несколько преувеличенном виде. Пользуясь обозначения рис. 8, я расположил точку xft недостаточно далеко справа от х^; из-за этого два эллипса, проходящие через (xft1 у^ и ^x^y^Y пересеклись. Получилось, что предполагаемая внутренняя граница стала внешней и наоборот, — довольно-таки неприятный результат, потому что я не собирался использовать здесь такие каллиграфические изыски.
Рис. 10.
У этой S утолщены штрихи в левой верхней и правой нижней частях буквы, а в остальном все параметры такие же, как у средней S на рис. 9.
Рис. 11.
Ужасный результат может получиться, если толщина штрихов в левой верхней и правой нижней частях буквы недостаточна.
Проблема на рис. 11 исчезает, если х^ достаточно велико, но хотелось бы, конечно, знать, какие значения допустимы. Мы пришли к третьей (и последней) задаче про эллипсы: в ситуации, изображенной на рис. 8, найти условия, необходимые и достаточные для того, чтобы дуга эллипса, проведенная через точки ^х'{, у”) и (xt, yt), оставалась выше дуги эллипса, проведенной через точки ^,3$ и (xt, yt). (Предполагается, что xf{ <x,l <xtn у" <у\< yt, а также, что оси обоих эллипсов параллельны осям координат.) Оказывается, что ответ в этой задаче может быть записан очень просто: кривые не пересекаются тогда
Q Q Q Q Qк) О О lO О
Рис. 12. Такие S можно получить, варьируя толщину штриха в середине буквы, при этом толщина в левой верхней и правой нижней частях выбирается возможно меньше, с условием, чтобы не происходило «перекручивания», как на рис. 11.
и только тогда, когда
yt-Vi > tit - Vi/ох
Попытавшись найти правильный ответ, поначалу я увяз в путанице обозначений, но в конце концов набрел на следующее довольно простое решение: пусть
a = xt-xV b = yt-y'i, A = xt-x", B = yt-y".
Используя эти обозначения и перевернув кривые вверх ногами, мы получаем, что функция b — b^l — (х/а)2 (описывающая правую нижнюю четверть эллипса, соединяющую точки (0,0) и (а, Ь)) не должна превосходить соответствующей функции B — BaJi — (х/А)2 для всех |гс| < а при заданных 0 < а < А и 0 < b < В. Раскладывая в степенной ряд, получаем
b — Ь\/1-b — Ь\/1-Х\2Х\2.2.2.4.4аа-6№ + 8^ + '" +-6№ + 8^ + '" +Л+1— +а2кЛ+1— +а2к
b — Ь\/1-
b — Ь\/1-
Х\2
Х\2
.2
.2
.4
.4
а
а
-6№ + 8^ + '" +
-6№ + 8^ + '" +
Л+1— +
а2к
Л+1— +
а2к
гдегде
где
где
1/2 V(2fc-2)!
к J j 22к"ЧЦк-1у.
положительно для всех к > 0 и при |гс| < а степенной ряд сходится. Если Ъ,а? < В/А2, то аналогичный степенной ряд
при малых х растет быстрее, и кривые пересекутся. Но если b/а2 > В/А2, то при всех к > 0 мы получим Ъ/а2к > В/А\ так что каждый член первого ряда не меньше соответствующего члена второго. ЧТД
Согласно разработанной ранее теории, получаем
(9)(9)yt-yi = Уг-Утст2
(9)
(9)
(xt - х^2 2(xt - xty 2(yt - Ут) *
Таким образом, можно добиться того, чтобы (й-^)/(^-2:!)2 было в точности равно (yt — y")/(xt — ж/)2, если начать с требуемых значений xt1 yt1 х’^ у’т и у'^х сначала определим у^ затем х'г и наконец yrv
Узнав, как рисовать S с математической аккуратностью, я обнаружил, что те же самые идеи можно применить ко многим другим символам, входящим в полный набор математических шрифтов. На самом деле, все символы, изображенные на рис. 13, используют одну и ту же подпрограмму METAFONT’a, впервые разработанную мной для буквы S (или двойственную ей подпрограмму, полученную перестановкой координат х и у). Без разработанной в этой статье теории мне пришлось бы либо отказаться от поставленной цели —делать книги математически,—либо перестать использовать все эти символы.
§8£Э»е?г?
Рис. 13. Метод, использованный для рисования буквы S, также используется в качестве подпрограммы, рисующей части многих других символов, включая приведенные здесь.
Конечно, это всего лишь первые шаги; созданные мною буквы далеки от совершенства, и десятки новых экспериментов напрашиваются сами собой. Сейчас я мечтаю о том, что в следующие несколько лет мы увидим, как математики объединяются с опытными создателями символов для проектирования новых, по-настоящему красивых шрифтов. Это будет действительно самым заметным приложением математики!
В заключение позвольте задать вопрос читателю. Эллипсы изучаются уже тысячи лет, поэтому логично предположить, что все их интересные свойства были давным-давно открыты. Тем не менее, по своему опыту я знаю, что, когда находится новое поле для применения математики, это часто дает почву для новых «чисто математических» вопросов, обогащающих и саму математику. Поэтому мне очень любопытно узнать: изучались ли ранее, может быть в другом обличье, те вопросы, с которыми я столкнулся, пытаясь рисовать эллипсы для S, или же новые приложения математики к типографскому делу привели к более глубокому проникновению в природу даже такого хорошо изученного объекта, как эллипс?
Приложение
Ниже приведена программа на языке METAFONT, описанная в [3, часть 3], она нарисует букву S, изображенную на рис. 14 (и бесконечно много других), если задать следующие параметры:
h, высота символа;
о, «свисание» кривых вверху и внизу;
Рис. 14.
Отмеченные точки на этой S соответствуют номерам в программе METAFONT’a, приведенной в приложении.
и, одна десятая ширины символа;
wq, размер круглого пера, используемого для проведения линий;
W4, ширина треугольных засечек до стирания;
ws, толщина в левой верхней и нижней правой частях;
wg, толщина штриха в середине буквы S.
Вертикальные линии на рис. 14 отстоят друг от друга на и. Чтобы стереть ненужные чернила слева и справа от заданного пути, в программе используются 1реп#’ и ‘греп#’; результат этого стирания виден на рисунке, так как кое-где стерты линии разметки.
subroutine scomp (index г)
(index р)
(index j)
(index /с)
(var s):
% начальная точка% точка поворота ^ур нужно определить) % точка перехода (нужно определить) % конечная точка % конечный угловой коэффициент% начальная точка% точка поворота ^ур нужно определить) % точка перехода (нужно определить) % конечная точка % конечный угловой коэффициент% Эта подпрограмма находит такие уР) Xj и у^ что % у к — Уз = s.(xk — х^ и следующая кривая % совпадает с эллипсом:
% начальная точка
% точка поворота ^ур нужно определить) % точка перехода (нужно определить) % конечная точка % конечный угловой коэффициент
% начальная точка
% точка поворота ^ур нужно определить) % точка перехода (нужно определить) % конечная точка % конечный угловой коэффициент
% г{хр - Xi,Q} . .p{Q,yp -yi} ..j{xk -xp,s\xk ~^p)}.
Ук ~yj = s(xk -Xj\
new a, b] a = s(xp — Xi); b = yk — yi — s(xk — Xi);
290
Компьютерная типография
xj — Xi = —2a • b(xp — Xi)/(a • a + b • b);
Ур — Уг = .5(5 • b — a • a)/b.
subroutine sdraw (index г)% начальная точка
(index p)% верхняя точка поворота ^ур нужно определить)
(index к)
% центральная точка
(index q)% нижняя точка поворота Qyq нужно определить)
(index j)
% конечная точка
(index a)% эффективная ширина пера в точках поворота
(index b)% эффективная высота пера в центральной точке
(var s):% угловой коэффициент в центральной точке
cpen; top0T/5 = top^; botoye = bot^;
x5 = Тб = Xk\
rtaxp = rtoTi; lftaZp = lftoa?2;
rt aXq = rtoTg; 1ft a^q = 1ft 0^10’,
У2 = yP; уэ = yq;
call scomp^i, 1, 3, 5, s);
call scomp^i, 2,4, 6, s);
call scomp(J, 9, 7, 5, s);
call scomp^j, 10,8,6, s);
% найти 7/i и точку 3 % найти т/2 и точку 4 % найти т/д и точку 7 % найти т/io и точку 8
wo ddraw i{xi — Xi, 0} .. l{0, yi — yi} •.
3{xq — xp, s(xq — tp)} .. 7{xq — xp, s(xq — Tp)} . .
9{0,t/j -y9}..j{xj -x9,0}, i{x2 - Xi, 0} .. 2{0, y2 - yi} ..
4{xq — Xp, s(xq — Tp)} . . 8{тд — Xp, s(xq — Tp)} ..
Ю{0,у, - Ую}. Як> — Ж10,о}.
% s-образный изгиб
"Буква S";
hpen; top07/i = round(/i + o); botoT/s = —o: хз = 5u; 7/3 = .52/i;
1ft 8^2 = round a; rts^4 = round 9гц
Xi = 4.bu; xs = 5.5u;
1ft o^6 = round a; rto^7 = round 8.5u;
ye = good0|/i - 1; y7 = good0|/i + 1;
botoT/s = 0; y9 = ye; xg = xe; rt4Z6 = rtorrg;
top07/io = h; 7/11=7/7; Т10 = Х7; 1ft4X7 = 1ft0^11;
wo ddraw 6 .. 8, 9 .. 8;
ddraw 7.. 10, 11.. 10;
rpen#; W4 draw 6{0, — 1} .. 5{1,0};
Ipen#; W4 draw 7{0,1} .. 1{—1,0};
hpen; wo draw 6{0, —1} .. 5{1, 0};
% нижняя засечка % верхняя засечка % стереть излишек % то же самое % нижний левый изгиб
draw 7{0,1} .. 1{—1, 0};% верхний правый изгиб
call ' a sdraw(l, 2, 3,4, 5,8, 9, —/i/(50ti)).
% центральный изгиб
Подготовка этой статьи была осуществлена при частичной поддержке National Science Foundation по грантам: MCS-7723738 и IST-7921977, Office of Naval Research по гранту N00014-76-C-0330, а также IBM Corporation. Автор весьма признателен Xerox Palo Alto Research Center за помощь в подготовке нескольких иллюстраций.
Литература
Richard J. Fateman, Essays in Algebraic Simplification, Ph.D. thesis, Harvard University (1971). Также опубликовано как отчет MAC TR-95 (Cambridge, Massachusetts: M.I.T. Laboratory for Computer Science, April 1972).
Donald E. Knuth, “Mathematical typography,” Bulletin of the American Mathematical Society (new series) 1 (1979), 337-372. [Перепечатано с исправлениями в главе 2 этой книги.]
Donald Е. Knuth, TgX and METAFONT: New Directions in Typesetting (Bedford, Massachusetts: Digital Press and American Mathematical Society, 1979).
Giovanni Mardersteig, The Alphabet of Francesco Torniello da Novara [1517] Followed by a Comparison with the Alphabet of Fra Luca Pacioli (Verona: Officina Bodoni, 1971).
The Mathlab Group, MACSYMA Reference Manual, version nine (Cambridge, Massachusetts: M.I.T. Laboratory for Computer Science, 1977). Первичная разработка и реализация оператора SOLVE системы MACSYMA осуществлена Р. Дж. Фейтманом, краткое описание этого можно найти в [1], §3.6.
Н. W. Mergler & Р. М. Vargo, “One approach to computer-assisted letter design,” Journal of Typographic Research 2 (1968), 299-322. [В этой статье описана первая компьютерная система для рисования параметризованных букв; по ставшим теперь понятными причинам авторам не удалось получить удовлетворительную S!]
Joel Moses, “MACSYMA-The Fifth Year,” SIGS AM Bulletin 8,3 (Association for Computing Machinery, 1974), 105-110.
Дополнение
Гораздо более простой способ решения трех уравнений (*) предложил Г. Дж. Ригер [The Mathematical Intelligencer 3 (1981), 94]. Для удобства положим xt = yt = ж с = 0 и ус = ут; тогда уравнения примут вид
ж2 + (у - yiY/Х2 = я2 , У = Ут + <гх, А2х = a^yi - у),
где А = yi/xi. Подставив второе уравнение в третье, получим выражение х = «ДУХ - УтД>? + сг2); и эти выражения для х и у сводят первое уравнение к уравнению (^ — утУ = у[ 4- ^х^, линейному по уь
Эрик Нойвирт из Венского университета 22 сентября 1980 г. прислал мне письмо с прекрасным объяснением замеченного мной «любопытного сходства» между задачей про эллипс на рис. 7 и задачей Торниелло про круг на рис. 2. Задача про эллипс может быть переформулирована следующим образом. Найти прямолинейный эллипс с центром на оси ординат, касающийся трех линий: у — ^,х — —т иу = ax—d, где г = xt— xi, ad = yt—ym - Если касание происходит в точках (0,0), (—г, —с) и (—а, —Ь), где а = xt —х, b = yt— у, а с = yt—yi, то будем говорить, что (г, a, d)-задача имеет решение (а, Ь, с). Основное наблюдение
Нойвирта состоит в том, что растяжение плоскости в направлении оси абсцисс переводит эллипсы в эллипсы, а касательные — в касательные. Поэтому для произвольного коэффициента растяжения а (г, сг, d)-задача имеет решение (а, 6, с) тогда и только тогда, когда (от, ст Jot, (^-задача имеет решение (ota, b, с). Если теперь вернуться к рис. 2 и положить h = —d/ст, мы увидим из решения задачи Торниелло, что (г, ст, Д)-задача имеет решение
(—2o*?2d/(d2 А-ст2?2), 2?d2/(d2 + ст2?2), ?),
если d2 — ст2?2 = 2?d; это случай, когда эллипс является кругом. Поэтому, если d2 — ст2?2 = 2a?d, решением (от, ст/a, б!)-задачи будет
(—2acy?2d/(d2 4- ст2?2), 2a?d2/(d2 4- ст2?2), от).
Но мы можем выбрать a = (d2 — cr2?2)/(2?d) и, применив принцип Нойвирта, установить, что решением общей (т, ст, d)-задачи будет
(—2CT?2d/(d2 + ст2?2), 2a?d2/(d2 4- ст2?2), от).
Это эквивалентно (7). Если бы я читал курс по математическому анализу или аналитической геометрии, я бы с удовольствием посвятил одно из занятий проблемам, связанным с S-образными изгибами, так как наблюдения и Ригера, и Нойвирта весьма поучительны.
Компьютерные алгебраические системы улучшались. Например, Mathematical 2.2 на SPARCstation 2 (модель 1993 г.) решает систему (*) за 141 с без всяких подсказок. Но математик-человек все еще нужен для приведения решения к удобоваримому виду типа (7).
С 1980 г. язык МЕТАРОМТдовольно сильно изменился, и я могу с радостью сказать, что все символы на рис. 13 были значительно улучшены. (См. Computers &: Typesetting, тт. С и Е.) Тем не менее, основные идеи, использованные выше при рисовании S-образных изгибов, остаются в силе.
Мое первое знакомство с индийской письменностью
[Первоначально опубликовано в буклете CALTIS-84, подготовленном к семинару по индийской письменности: каллиграфии, шрифтам и печати (New Delhi: 11-13 February 1984), 49.]
13 февраля 1980 г. несколько шрифтовых дизайнеров из Mergenthaler Linotype Company навестили меня, чтобы взглянуть на странную компьютерную программу под названием МЕТА FONT, которую я разрабатывал. В числе прочего мы провели следующий эксперимент: Мэтью Картер делал эскиз символа, а я должен был написать программу для METAFONT’a, которая бы рисовала то же самое.
Мэтью решил предложить мне символ, которого я раньше не видел, и выбрал значок из деванагари. Это задание было особенно интересно, потому что раньше у меня ни разу не было возможности поближе познакомиться с индийскими буквоформами. С третьей попытки моя программа нарисовала букву на рис. 1, и этот результат, кажется, удовлетворил Мэтью. (Двенадцать опорных узлов изображены тройкой точек каждый; например, узел 5 изображен тремя черными точками, обозначенными 5, 105 и 205.) '
Рис. 1.
Пробная гранка, показывающая опорные узлы.
Я написал программу так, чтобы можно было легко менять толщину проводимых линий. Более того, я решил попробовать нарисовать «случайные» вариации буквы, немного сдвигая некоторые из 12 узлов с их математически выверенных позиций, для создания таких «ошибок» я использовал случайные числа. Результаты изображены на рис. 2: на рис. 2а изображен исходный символ; 2Ь — его полужирная версия; на рис. 2с, 2d и 2е изображены случайные вариации со стандартным сдвигом 5, 10 и 15. (Сдвиг 15 явно был слишком велик, но нам было интересно.)
(а)(Ь)(с)(d)(е)
Рис. 2. Пять результатов работы программы МЕТА FONT.
Затем я создал шрифт, содержащий символы на рис. 2а и 2Ь, и плюс к тому еще 26 различных символов, вроде изображенного на рис. 2с. (В каждом случае стандартный сдвиг составлял 5, но были использованы различные случайные числа.) Мы отпечатали этот шрифт на фотонаборной машине Alphatype CHS; результат в натуральную величину показан на рис. 3 .
По рис. 3 можно понять, что это было моим первым знакомством с индийской письменностью, хотя бы по тому, что я не знал, как пишется деванагари. Если внимательно приглядеться, то можно также заметить, что все 26 букв в третьей строке немного различаются. Мне казалось, что немного случайности сделает символы «человечнее», несмотря на то что созданы они при помощи точных математических формул.
Это было самое начало, и в нашем программном обеспечении все еще встречались ошибки; между двумя символами третьей строки произошел сбой. (Когда демонстрируешь компьютерную программу, всегда что-нибудь идет не так.)
В минувший год нашу лабораторию навестил г-н Пиджуш Гош; он создал гарнитуру, которую назвал NCSD —Простой деванагари для начинающего каллиграфа^ После этих обнадеживающих экспериментов я с радостью понял, что мои идеи можно применять к языкам далеких заморских стран, которые я, возможно, так никогда и не смогу понять. Какое это замечательное ощущение!
Вместе с моими коллегами мы сейчас работаем над созданием новой версии METAFONT’a, которую надеемся выпустить где-то через год. Использовать эту новую версию должно быть намного проще, чем первую, но я боюсь, что
Вариант Деванагари Мэтью Картера:с^^^^^с^^5^505^5^;
Полужирное начертание: ^^^^^^^^^^^^;
Со «случайными» вариациями: ^^^^^^^с^с^^с^^с^^^с^с^^^^^^с^^^с^.
Рис. 3. Первые напечатанные образцы в натуральную величину.
по-настоящему простой она все же не будет; я думаю, лучше всего, если ею будут пользоваться вместе художник и специалист по компьютерам, точно так же, как работали мы с Мэтью Картером. Эта программа проектируется так, чтобы работать на самых разных компьютерах, как больших, так и маленьких. Мы планируем распространять ее бесплатно, так как вся наша работа вдохновляется нашей любовью к печатному делу.
Дополнение
Позднее я узнал, что символ ^ является вариантом буквы L, которая чаще всего пишется К.
Последние пятнадцать лет я с удовольствием наблюдаю за тем, как многие люди творчески применяют МЕТА FONT к созданию индийских символов. Например, недавно я использовал шрифт деванагари, созданный Франсом Ж. Велыюи, чтобы напечатать слово ^g* (kuttaka) в третьем издании моей книги Seminumerical Algorithms (1998).
Концепция меташрифта
[Первоначально опубликовано в Visible Language 16 (1982), 3-27.]
Одно изображение одной буквы приоткроет лишь немногое из того, о чем думал художник, создавая ее. Имея же точные инструкции, как получить это изображение, можно собрать о букве сведения, достаточные для воспроизведения бесконечного многообразия похожих букв, нарисованных по единому замыслу. В таких инструкциях, вместо того чтобы просто описать одну букву, объясняется, как изменится форма этой буквы при изменении остальных параметров замысла. Таким образом, можно задать целый шрифт из букв и других символов, в котором каждый значок соответствующим образом подстраивается к изменяющейся обстановке. Первые опыты с использованием точного языка для описания движений пера склоняют к мысли, что в будущем дизайнер шрифтов будет заниматься не просто созданием отдельных алфавитов; перед ним будет поставлена задача объяснить, как именно каждое его творение должно приспосабливаться к самым различным изменениям первоначального замысла. В этой статье приводятся примеры меташрифта и рассказывается о переменных параметрах, возникающих в процессе его разработки.
Некоторые философские труды Аристотеля называют Метафизикой, потому что согласно общепринятому расположению его трудов они появились после Физики. К двадцатому столетию большинство позабыло первоначальное значение греческих приставок и «мета-» стало употребляться для указания на более абстрактный характер описываемого предмета. У нас теперь имеются метапсихология (наука о связи тела с пребывающим в нем разумом), метаматематика (наука о математических рассуждениях) и металингвистика (наука о связи
языка и культуры;; метаматематик доказывает метатеоремы (теоремы о теоремах), а ученый, занимающийся информатикой, часто работает с метаязыками (языками, предназначенными для описания языков). Нововведенные слова, начинающиеся с «мета-», обычно отражают современное стремление смотреть на вещи снаружи, на более абстрактном уровне, с более зрелым, как нам кажется, пониманием.
В этом смысле «меташрифт»—это схематическое описание того, как рисуется гарнитура, а не просто сами рисунки. Такие описания дают более или менее точные правила для рисования отдельных букв, и лучше всего эти правила выражать через переменные параметры, так чтобы одно и то же описание на самом деле определяло множество различных рисунков. Правила меташрифта определяют, таким образом, много разных конкретных шрифтов, зависящих от значений параметров. Например, среди образдов, изданных American Type Founders в 1923 г., имелись следующие начертания гарнитуры «Caslon»: простое, старомодное, светлое, полужирное, жирное, узкое, светлое узкое, полужирное узкое, очень узкое, полужирное широкое, штриховое и открытое, не говоря уже об American Caslon, New Caslon, Recut Caslon и Caslon Adbold; каждое из них имело варианты примерно для шестнадцати разных кеглей, так что всего прямых начертаний гарнитуры Caslon было около 270. Имелся общий замысел, некоторым образом связывающий все эти шрифты, что и давало возможность распознавать «Caslon» несмотря на то, что изменения кегля и насыщенности сопровождались более или менее заметными изменениями формы букв. Этот общий замысел мы можем считать меташрифтом, определяющим, как будут изменяться буквы в различных ситу а днях: меташрифт управляет метаморфозами.
Конечно, замысел, лежащий в основе всех этих разновидностей гарнитуры Caslon, явно сформулирован не был; он был выражен косвенно при помощи нескольких рисунков, иллюстрирующих некоторые граничные случаи. Умелый работник может сделать соответствующие изменения для средни^ размеров и стилей так же, как умелые аниматоры заполняют «промежуточность» в мультфильмах Уолта Диснея. Хотелось бы, тем не менее, совершенно явно сформулировать этот замысел, чтобы зафиксировать намерения создателя шрифта без всякой двусмысленности; тогда нам не пришлось бы прибегать
к туманному понятию «соответствующие изменения». В идеале намерения создателя должны быть сформулированы столь явно, чтобы их смог воплотить в жизнь кто-нибудь, совершенно ничего не понимающий в формах букв,—даже тупой, неодушевленный, электронный компьютер!
Джордж Форсайт однажды написал: «Вопрос «Что может быть автоматизировано?» — один из самых волнующих философских и практических вопросов современной цивилизации». По опыту мы знаем, что идею понимаешь гораздо лучше, после того как удается объяснить ее кому-то Другому; а после появления компьютеров появилась еще более правильная формулировка: лучший способ понять что-либо — узнать это настолько досконально, чтобы можно было научить этому компьютер. Машины — это последняя проверка, ведь перед ними нельзя «махать руками» и у них нет «здравого смысла», чтобы исправлять расхождения и несообразности, что у нас получается почти бессознательно. На самом деле, исследования в области искусственного интеллекта показали, что компьютеры могут выполнить практически любое задание, традиционно связанное с «мышлением», но большие сложности возникают у машин при выполнении того, что люди и животные делают «помимо мышления». Искусство создания букв не будет познано до конца, пока его не сумеют объяснить компьютеру; и поиски этого объяснения безусловно будут поучительны для всех вовлеченных в этот процесс. Люди часто обнаруживают, что знания, приобретенные во время написания компьютерных программ, гораздо ценнее собственно результата работы компьютера.
Для того чтобы научить машину создавать шрифты, нам требуется некоторый язык, или система записи, описывающий процесс построения буквы. Самих рисунков будет недостаточно, за исключением, разве что, очень простых по замыслу проектов, в которых все начертания гарнитуры отличаются Друг от друга простейшими преобразованиями. Несколько вариантов языков для точного описания формы букв было предложено в последние годы, в том числе разработанный автором в 1977—1979 гг. Эта система называется METAFONT и отличается от всех предшественниц тем, что в ней вместо задания контуров каждой буквы описываются движения центра «пера» или «ластика». В результате, в языке METAFONT легко созда-
вать гарнитуры; к примеру, на создание плохо проработанного, но все же сносного меташрифта, описанного в [5], ушло всего лишь около двух недель.
Еще через шесть месяцев работы, после буквально тысяч усовершенствований, этот меташрифт-прототип был разработан настолько хорошо, что с его помощью оказалось возможным набрать целую 700-страничную книгу [7]. Получившаяся гарнитура получила название Computer Modern, она включает в себя меташрифты прямого светлого и курсивного стилей, кроме того греческий алфавит, кириллицу и прописные буквы каллиграфического начертания, вместе с обширным запасом математических символов. Основная цель, ради которой создавалась эта гарнитура, состояла в том, чтобы воспроизвести дух шрифтов, использованных в первых изданиях книг автора по компьютерному программированию, а именно шрифтов, известных под названием «Monotype Modern Extended 8А», применив для этого выразительные средства МЕТА FONT5 а и предусмотрев возможности самых разных параметрических изменений.
На самом деле возможно столько вариантов, что автор до сих пор продолжает находить новые значения параметров, дающие неожиданно привлекательный результат, не предусмотренный исходным проектом; параметры, обеспечивающие наилучшие удобочитаемость и внешний вид, могут быть так никогда и не найдены, потому что число вариантов бесконечно. С другой стороны, можно попробовать параметризовать множество вещей, которые в текущий момент изменять нельзя; возможности экспериментирования стали почти безграничными теперь, когда появился МЕТА FONT.
В настоящее время (январь 1981 г.) прямой светлый меташрифт гарнитуры Computer Modern имеет 28 параметров, влияющих на форму букв, плюс еще три параметра, помогающие контролировать межбуквенные пробелы; число параметров продолжает расти по мере накопления опыта. Имеется также с полдюжины разнообразных параметров, предназначенных в основном для переключения между разными формами символов и лигатур в том или ином шрифте. К примеру, один из этих последних параметров выбирает, какое из двух начертаний буквы ‘д’ употребить; возможно, читатель уже заметил, что такое начертание буквы g отличается от использованного далее, например, на с. 300. В дальнейшем бу-
дет продемонстрировано еще несколько типографских трюков вроде этого; относительно крупная печать используется для того, чтобы эти эффекты можно было заметить.
Наиболее интересные и важные параметры Computer Modern будут изменяться в следующих абзацах, по одному за раз, чтобы показать, насколько широки возможности варьирования. Конечно, легко найти значения параметров, дающие неудовлетворительный результат, — не стоит надеяться на решение всех мыслимых проблем одним махом; поэтому на примерах мы постараемся показать пограничные ситуации, в которых начинаются неприятности, а также промежуточные области, где и стоит искать шрифты, пригодные к употреблению.
От первой и самой очевидной группы параметров зависят вертикальные размеры букв: высоту строчной буквы и размеры верхних и нижних выносных элементов можно регулировать независимо Друг от друга. The x-height and the heights of ascenders and descenders can be independently specified. [Высота строчной буквы и высоты верхних и нижних выносных элементов могут быть определены независимо.] На самом деле для нижних выносных элементов имеется два независимых параметра: первый контролирует глубину букв 8jpqy> а Другой— глубину остальных символов, вроде запятых и хвостика буквы Q. Высота прописных букв не зависит от высоты строчных букв, да и высоту цифр от 0 до 9 также можно изменять по желанию. Самый необычный параметр, связанный с вертикальными размерами букв, называется высотой средней линии, а именно высотой средней линии строчной е; на сегодняшний момент высота средней линии влияет также и на некоторые другие строчные буквы:
tlLQ расК,
tlLQ раСК,
tire paclc,
tire расК,
tbc расК!
Еще от одной довольно очевидной группы параметров зависят горизонтальные размеры каждого символа
шрифта: можно получать очень широкие или очень узкие шрифты, не изменяя при этом высоты и толщины линий. It is possible to obtain fonts that are extremely extended or eitrcmdy cwdenselvitkoTit changing the heights or widths of the strokes. [Можно получить слишком широкие или слишком узкие шрифты без изменения высоты или ширины штрихов.] Можно также имитировать печатную машинку, расширяя или сужая отдельные символы так, чтобы все они имели одинаковую ширину. Заметьте, что засечки удлиняются или укорачиваются вместе со всей буквой; поэтому в таком стиле засечка у i гораздо длиннее, чем у ш.
Конечно, гораздо лучшая имитация печатной машинки получится, если нивелировать различия между толстыми и тонкими линиями. Этот шрифт кажется похожим на шрифт печатной машинки, даже когда буквы имеют разную ширину.
Согласно замыслу Computer Modern, буквы рисуются перьями, острие которых имеет форму эллипса; например, толстые линии букв h в этом предложении проведены пером, которое при десятикратном увеличении выглядело бы так: 4 ”* \ Оси эллипсов абсолютно горизонтальны, а не наклонены как 6 / ’, потому что у букв должен быть вертикальный наплыв. Для рисования разных частей букв используются разные перья.
Пять параметров управляют размерами этих эллипсовидных перьев: один — шириной тонких соединительных штрихов, другой — толстых прямых основных штрихов, еще один — толстых округлых основных штрихов, еще один — величиной утолщений у букв типа acf.. .у и последний дает толщину по вертикали. Если все пять параметров равны, перья будут абсолютно круглыми.
Выбору параметров, связанных с размерами пера, требуется уделить особое внимание. Например^ нежелательные кляксы появляются, если утолщения слишком велики в ©равнении в основными штрихами; а если округлые основные штрихи значительно шире прямых, шрифт становится раздражающе несообразным. Нельзя сделать шрифт очень жирным и избежать при этом наползания разных частей букв друг на друга. Возможно, будущие меташрифты научатся рассчитывать нужные размеры пера по меньшему набору независимых параметров: ведь правильные размеры неуловимым образом зависят друг от друга; на данный момент, чтобы получить набор
взаимосвязанных размеров пера, приходится использовать метод проб и ошибок, но дальнейшие исследования должны пролить свет на эту зависимость.
Для простоты были упомянуты только пять параметров, определяющих размеры пера, но на самом деле ситуация несколько сложнее. Так перья, используемые для рисования прописных букв, задаются отдельно от тех, которыми рисуют строчные, а цифры рисуются некоторой смесью этих двух видов. Есть также переменный «поправочный множитель», сбрасывающий лишний вес в буквах типа w и т, которые иначе в некоторых стилях выглядели бы слишком темными; настоящее единообразие в толщине линий не ведет к единообразию внешнего вида букв, потому что наши глаза нас обманывают.
Другой не вполне очевидный параметр шрифтов Computer Modern — это так называемое «свисание» округлых и остроконечных элементов вниз за линию шрифта и вверх за линию, проведенную на высоте строчной буквы. Например, в этом предложении свисания у букв нет вообще. А некоторые буквы в этом предложении свисают за ограничивающие линии в три раза больше, чем в следующих предложениях. Все еще приходится экспериментировать, для того чтобы найти свисание, при котором вид букв наиболее стабилен, а при печати на оборудовании с низким разрешением желательно вообще отказаться от свисания; дальнейшее изучение этого параметра в комбинации с остальными обещает быть весьма интересным.
Засечки можно изменять несколькими способами. Например, в буквах ‘р’ этого предложения засечки не имеют «срезов». А в буквах, которые вы видите сейчас, срезы сделаны в три раза больше чем обычно, специально для того чтобы до конца вскрыть понятие среза.
Еще одно понятие, связанное с засечками, —это размер так называемого «скругления»; у засечек в этом предложении нет скругления. А в этом предложении скругления значительно преувеличены. Легче всего понять разницу, если увеличить
по bracketing (Нет скруглений);
Hormal bracketing (Нормальные скругления);
Hoticeable bracketing (ЗаметНые скруглез
Изгиб, начинающийся на конце засечки, касается основного штриха на некотором расстоянии выше или ниже засечки; это вертикальное расстояние и есть параметр «скругления».
Конечно, длину засечек тоже можно регулировать. У ^Укв в этом предложении засечки на 50% короче, чем раньше. А в этом предложении засечки на 50% длиннее, чем раньше, такие длинные, что иногда они достают, куда не стоило бы. Один из способов получить рубленый шрифт состоит в установке нулевой длины засечек (и соответствующем изменении межбуквенных интервалов); но лучше, когда засечки исчезли, перерисовать некоторые буквы, например, используя выпуклые концевые элементы вместо утолщений.
Параметр «наклона» изменяет движение пера, как показано в этом предложении, но форма пера остается неизменной. Угол наклона может быть как положительным, так и отрицательным — если требуется добиться необычного э екта. ^/ИОНХг/ГЫ^ Z^2^.ZZ7Z2^S5Mt^^ ZiTOJZe ВДУОЛ ЛГ ^ОЛГ^ ^ГО ^ДУГ- Z7ZZ ^Z&ZZ&ZfZZZVZtZ ZZO ^Z^r ZZ^?6>J?vlZ^Z^ZZ6> zyz6? штзп&тъ. Воз можно, самый интересное использование параметра наклона состоит в том, что курсивный шрифт гарнитуры Computer Modern создается без всякого наклона: курсивные буквы но стилю отличаются от прямых, и мы настолько привыкли видеть их наклоненными вперед, что они каснсутся наклоненными назад, когда большинство из них на самом деле выпрямлены или наклонены вперед лишь самую малость.
Последний параметр, который мы обсудим, — один из самых интересных; он называется «квадратным корнем из 2». С математической точки зрения, существует, конечно, единственный квадратный корень из 2, но в меташрифтах Computer Modern V2 считается переменным параметром, использующимся для нахождения точек, отстоящих на 45° друг от друга при движении пера по дуге эллипса. В результате при значении, меньшем истинного, эллипс превращается в суперэллипс и овалы расширяются; например, если использовать корень пятой степени из 4 вместо квадратного корня из 2, т. е.
1.31950791 вместо 1.41421356,
то получится известный суперэллипс, введенный Питом Хейном. Большие значения квадратного корня из 2, напротив,
приведут кприведут кпротивоположному эффекту:
приведут к
приведут к
The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”The “square root of 2”in these letters is 1.100. in these letters is 1.200. in these letters is 1.320. in these letters is 1.350. in these letters is 1.380. in these letters is 1.414. in these letters is 1.450. in these letters is 1.500. in these letters is 1.600. in these letters is 1.700.in these letters is 1.100. in these letters is 1.200. in these letters is 1.320. in these letters is 1.350. in these letters is 1.380. in these letters is 1.414. in these letters is 1.450. in these letters is 1.500. in these letters is 1.600. in these letters is 1.700.
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
The “square root of 2”
in these letters is 1.100. in these letters is 1.200. in these letters is 1.320. in these letters is 1.350. in these letters is 1.380. in these letters is 1.414. in these letters is 1.450. in these letters is 1.500. in these letters is 1.600. in these letters is 1.700.
in these letters is 1.100. in these letters is 1.200. in these letters is 1.320. in these letters is 1.350. in these letters is 1.380. in these letters is 1.414. in these letters is 1.450. in these letters is 1.500. in these letters is 1.600. in these letters is 1.700.
Можно варьировать еще несколькими параметрами, кроме уже упомянутых. Например, величиной, на которую раздвинуты острые углы в буквах типа V и М, для того чтобы избежать излишней черноты. От нескольких параметров зависят особенности формы «клювообразных» засечек таких букв, как Е, Т и Z. Но полное описание прямого светлого шрифта гарнитуры Computer Modern выходит за рамки этой статьи.
Мы изучали параметры по одиночке; что же происходит, когда они изменяются все вместе? На рис. 1 показана одна из интересных возможных трансформаций. Вверху шрифт имеет старомодный оттенок, в точности таким стилем до сих пор набирался текст этой статьи: высота буквы h равна 8.4 пункта, высота строчной буквы — 4 пунктам, высота средней линии — 2.3 пункта, а глубина нижних выносных элементов -— 3 пунктам. Толщина соединительных штрихов составляет 0.26 пункта, в сравнении с 1.2 пункта для прямых основных штрихов и 1.34 пункта для округлых основных штрихов; диаметр утолщений— 1.36 пункта, и для рисования соединительных штрихов используются круглые перья. Ширина кегельной площадки в этом стиле равна 12.6 пункта; длина засечек составляет 0.07777 от этой ширины, и они имеют срезы, равные 0.54 пункта, и скругления в 0.8 пункта. Свисание составляет 0.3 пункта, а «квадратный корень из 2» имеет математически правильное значение 1.414214.
^Ihe LORD is my shepherd;
I shall not want.■
He maketh me to lie down
in green pastures: he leadeth me
beside the still waters.
He restoreth my soul: he leadeth me in the paths of righteousness for his name’s sake.
Yea, though I walk through the valley of the shadow of death, I will fear no evil:
for thou art with me;
thy rod and thy staff they comfort me.
Thou preparest a table before me in the presence of mine enemies: thou anointest my head with oil, my cup runneth over.
Surely goodness and mercy shall follow me
'all the days of my life:
and I will dwell
in the house of the Lord for ever.
Рис. 1. Непрерывным изменением параметров можно постепенно преобразовать шрифт, выглядящий старомодно, к современной стилистике. В этом примере высота буквы h не меняется, а ширина кегельной площадки растет с увеличением высоты строчной буквы. Из-за этого в перспективе создается ощущение, как будто слова выходят из прошлого в настоящее на своем пути к будущему.
К концу рис. 1 буквы превращаются в почти ультрасовременный шрифт, который и будет использоваться вплоть до конца этой статьи. Высота буквы h по-прежнему равна 8.4 пункта, но высота строчной буквы увеличилась до 6.4 пункта, а высота средней линии—до 3.2; глубина нижних выносных элементов теперь равна 4 пунктам. Соединительные и основные штрихи: прямые и округлые— теперь все имеют одинаковую толщину в один пункт, а острие пера имеет высоту 0.6 пункта. Так что перо, которым рисуется большинство' букв, после десятикратного увели- , чения выглядит так: '•’. Ширина кегельной площадки теперь равна 21.6 пункта; длина засечек нулевая, так же как срезы и скругления. Свисание составляет 0.1 пункта, а «квадратный корень из 2» имеет значение 1.3.
Каждая из 595 букв, пробелов и знаков препинания на рис. 1 принадлежит новому шрифту, который ' еще на 1/594 ближе к конечному набору параметров. Поэтому, хотя и кажется, что соседние буквы принадлежат одному и тому же шрифту, но в результате накапливающихся изменений получается разительный контраст: это похоже на постепенное изменение наших собственных лиц,
когда мы стареем, только этот шрифт, напротив, становится моложе.
В этой статье использованы сотни
разных шрифтов, тем не менее, все они принадлежат прямому светлому и курсивному меташрифтам гарнитуры Computer Modern. Каждая буква определяется компьютерной программой, написанной на языке METAFONT, и компьютер может нарисовать любой требуемый вариант этой буквы, если ему указать значения всех параметров. Важно помнить, что
все эти соглашения и параметры не встроены в сам METAFONT; METAFONT — многоцелевой язык, предназначенный для того, чтобы облегчить создание меташрифтов, и Computer Modern — всего лишь одна из попыток создать шрифт с помощью такого языка.
Давайте взглянем на программу рисования буквы h, это даст нам
некоторое представление о том, как создаются меташрифты. Каждая прямая светлая h в гарнитуре Computer Modern рисуется в точности следующим образом, если перевести код METAFONT'a на обычный язык:
Ширина очка этой литеры составит 10 единиц, при этом ширина кегельной площадки равна 18 единицам; но после того как символ нарисован, ширину нужно будет подрегулировать, сделав «поправку на засечки», чтобы учесть, длинные они или короткие.
У этой буквы несколько узловых точек, которые определяются следующим образом. Нужно взять эллиптическое перо с высотой, равной высоте соединительных штрихов, и шириной, равной ширине прямых основных
штрихов в строчных буквах. Когда это перо указывает на точку 1, его центр отстоит приблизительно на 2.5 единицы от левого края очка (округление производится так, чтобы центр находился в позиции, удобной для растрирования), а верхний конец пера находится на высоте буквы h для строчных букв. Точка 2 находится прямо под точкой 1; если центр пера расположить в точке 2, то его нижний конец будет точно на линии шрифта. Обе точки 3 и 4 отстоят приблизительно на 2.5 единицы от правого края очка; точка 4 находится правее точки 2 на том же уровне, а точка 3 — на расстоянии 1/3 от высоты средней линии до высоты строчной буквы.
Возьмите перо и проведите прямые основные штрихи, соединяющие точку 1 с точкой 2 и точку 3 с точкой 4. Сделайте срезанную засечку левее точки 1, и добавьте засечки по обеим сторонам точек 2 и 4, используя подпрограммы для засечек (соответствующим образом учитывающие параметры срезания, скругления и длину засечек).
Наконец, перекладина h рисуется так: линию начинают рисовать из точки, расположенной в 1/8 пути от высоты средней линии до высоты строчной буквы, в вертикальном направлении, пером для соединительных штрихов, прижатым правым концом к границе левого из основных штрихов. Это перо для соединительных штрихов описывает четверть эллипса и останавливается в точке, расположенной посередине между правыми границами основных штрихов, и такой, что верхний конец пера находится на высоте строчной буквы плюс половина свисания; назовем ее точкой 5.
Чтобы дорисовать перекладину, нужно из точки 5 провести четверть суперэллипса в точку 3, увеличивая при этом ширину пера: от положенной для соединительных штрихов до ширины прямых основных штрихов; середина этой дуги находится с использованием среднего геометрического числа 1.23114413 и параметра «корень квадратный из 2», а не х/2, как в обычных формулах для эллипсов. (Странная константа 1.23114413, равная 23/10, выбрана так, чтобы для «корня квадратного из 2», равного V^, получался суперэллипс Пита Хейна.)
Похожие программы рисуют тип. В этом описании не упомянуты эффекты, связанные с параметром «наклона», потому что наклон выполняется другой частью компьютерной программы, когда рисунок переносится на бумагу.
"Теперь идея меташрифта должна быть понятна. Но зачем он нужен? Манипулирование многими пара-
Рис. 2.Программа, перефразированная в тексте, могла бы подготовить этот символ для печатного устройства с низким разрешением. Обратите внимание на пять узловых точек, занумерованных 1, 2, 3, 4 и 5; через эти точки движется центр «пера» при рисовании буквы.Рис. 2.Программа, перефразированная в тексте, могла бы подготовить этот символ для печатного устройства с низким разрешением. Обратите внимание на пять узловых точек, занумерованных 1, 2, 3, 4 и 5; через эти точки движется центр «пера» при рисовании буквы.
Рис. 2.
Программа, перефразированная в тексте, могла бы подготовить этот символ для печатного устройства с низким разрешением. Обратите внимание на пять узловых точек, занумерованных 1, 2, 3, 4 и 5; через эти точки движется центр «пера» при рисовании буквы.
Рис. 2.
Программа, перефразированная в тексте, могла бы подготовить этот символ для печатного устройства с низким разрешением. Обратите внимание на пять узловых точек, занумерованных 1, 2, 3, 4 и 5; через эти точки движется центр «пера» при рисовании буквы.
метрами может быть интересно и забавно, но нужен ли кому-нибудь шрифт кегля б1/?, на одну четверть отличающийся от Baskerville в сторону Helvetica?
Можно также провести аналогию с музыкой: нотное письмо было изобретено за столетия до появления способов описания чертежей; все это время серьезной потребности в метасимфониях не ощущалось, так зачем же нам нужны меташрифты?
Что ж, это законные вопросы, дей-
ствительно заслуживающие ответа;
сначала давайте подумаем над музыкальной аналогией. Длинная история нотного письма ясно показывает,
что существование точного языка само по себе еще не приводит к
введению в нем параметров. В самом деле, до самого последнего времени параметры не смогли пробраться в серьезную музыку, даже в самом примитивном виде, за исключением немногих почти забытых вещиц, вроде метавальса Моцарта [11]. Несомненно, было бы очень интересно и поучительно писать метамузыку, которая бы порождала в слушателе переменные степени беспокойства, волнения, грусти, sturm und drang, в зависимости от значений некоторых параметров; но такой музыке нашлось бы мало естественных применений, разве что в качестве музыкального сопровождения к художественным фильмам.
Все аналогии, конечно, неточны, и создание шрифта отличается от сочинения музыки, потому что алфавиты не симфонии; алфавит—«средство», а симфония — «послание». Гораздо лучшая аналогия между шрифтами и музыкой получится, если рассмотреть вместо симфоний фоновую музыку, ведь шрифт служит фоном к зафиксированным на бумаге идеям автора. Многие люди не выносят фоновую музыку: они считают, что музыка либо должна быть в центре внимания, либо вообще не
должна звучать. С другой стороны, по общему мнению, читатель книги не должен обращать внимания на буквы g и к в этой книге. Шрифт должен выглядеть внушительно, но действовать внушающе.
Умение производить параметрические изменения проистекает из нашей потребности в разнообразии. Мы не хотим все жить в одинаковых домах или водить одинаковые машины. Фоновая музыка становится особенно утомительной, когда ее партитура бедна, состоит всего лишь из нескольких мотивов; и пять столетий типографской практики свидетел ьствуют о непрерывном стремлении к новым алфавитам и к большим гарнитурам родственных алфавитов. Поэтому, хотя любой набор конкретных значений параметров меташрифта может выглядеть несколько глупо и казаться лишним, имеется настоятельная необходимость уметь выбирать произвольные значения параметров. Создатели книг и рекламных материалов получат большую свободу, чем когда-либо раньше, если они смогут оперировать несколькими мета ш рифта м и . Любой также сможет легко получить индивидуальные шрифты и шрифты для однократного использования.
Еще одна причина, почему меташрифты и мета-музыка не были как следует развиты давным-давно, состоит в том, что до недавнего времени не существовало компьютеров. Люди находят выполнение расчетов с множеством параметров затруднительным и скучным, в то время как сегодняшние машины делают это с легкостью.
Может быть, самым важным практическим результатом параметрических изменений стала возможность
делать поправки для каждого кегля; сегодняшняя тенденция получать шрифты Т кегля посредством уменьшения до 70% шрифтов 10 кегля привела к прискорбному ухудшению качества. Еще одно преимущество состоит в том, что мета шрифт может
приготовить кривые для правильного воспроизведения на цифровых печатных устройствах, основанных на дискретном растрировании. Это значительно уменьшает необходимость ручной корректировки растровых
шаблонов.
Конечно, создать меташрифт вместо одного шрифта—задача сложная. Создатель хочет контролировать процесс, но огромное многообразие возможных значений параметров приводит к тому, что меташрифт способен сгенерировать бесконечно
много алфавитов, большинство которых человеческие глаза так никогда и не увидят; только немногие из вариантов можно посмотреть и еще намного меньшее число—как следует отрегулировать до того, как описание меташрифта будет завершено. С другой стороны, создатель меташрифта получает в качестве компенсации и преимущества, потому что меташрифт позволяет отложить принятие решений по многим аспектам замысла, превратив их в параметры, вместо того чтобы фиксировать их описание уже на начальном этапе. "Такие вещи, как величину свисания, толщину соединительных штрихов, длину засечек и т. д., не стоит определять раз и навсегда; не составляет труда попросить компьютер провести эксперименты, которые позволят создателю выбрать наилучшие значения для этих неуловимых количеств после просмотра реальных печатных образцов. Такие эксперименты были бы невыполнимы, если бы каждый символ пришлось рисовать по отдельности, т. е. если бы каждый символ скорее принадлежал просто шрифту, чем меташрифту.
В конце концов на первый план должны выдвинуться научные аспекты меташрифта. Возможность непрерывного изменения параметров
позволяет ставить эксперименты о влиянии таких изменений на удобочитаемость текста или его привлекательность для глаза. И даже еще более важными станут знания, точно зафиксированные в описаниях меташрифтов. Автор, например, узнал очень многое о создании шрифтов, совершенствуя алфавиты Computer Modern, и теперь эта информация доступна каждому, кто умеет читать код METAFONT'a. "Так много надежд связано с мыслью, что когда-нибудь профессионалы, действительно знающие предмет, начнут создавать меташрифты в точном языке, таком как METAFONT, и тогда искусство создания шрифтов вознесется на небывалые высоты.
Благодарности и извинения
Автор хочет поблагодарить Чарльза Бигелоу, Мэтью Картера, Дугласа Хофштадтера, Джил Кнут и Майкла Паркера за многочисленные предложения, которые помогли улучшить статью; и он в особом долгу перед Германом Цапфом за десятки бесценных предложений, которые значительно помогли в совершенствовании замысла меташрифтов Computer Modern. Извинения приносятся поборникам чистоты языка, которые возражают против смешивания греческих и латинских основ: греческим эквивалентом fons будет кг)у^, так что слово вроде «metapeg» (метапег) подошло бы лучше, чем «metafont» (меташрифт). Но тем не менее, люди, встретившие такое название в первый раз, вряд ли сразу сообразят, что оно означает, во всяком случае до тех пор, пока наука о создании шрифтов не станет известна как пегология.
Исследования, описанные в этой статье, частично поддерживались фондом National Science Foundation по грантам IST-7921977 и MCS-7723738 и частично —IBM Corporation.
Аннотированный список литературы
Шрифты, использующиеся в этом списке литературы, соответствуют значениям параметров для прямого светлого шрифта гарнитуры Computer Modern, использованным в ходе ее первоначальной разработки и основанным на шрифтах «Monotype Modern 8»; более радикальные значения, использованные для набора предшествующего текста статьи, были подобраны много позже того, как разработка была завершена, для того чтобы проиллюстрировать концепцию меташрифта.
Р. J. М. Coueignoux, Generation of Roman Printed Fonts, Ph.D. thesis, Dept, of Electrical Engineering, Massachusetts Institute of Technology (June 1975). В этой диссертации представлена первая попытка использовать сложные математические кривые для компьютерного описания формы букв. Автор вместе со своими студентами сейчас продолжает эти исследования в Ecole Nationale Superieure des Mines de Saint-Etienne, Франция.
Adrian Frutiger, Type Sign Symbol (Zurich: ABC Verlag, 1980); особенно обратите внимание на сс. 15-21, где описано, «как создавался и развивался Univers». Uni vers был первым настоящим меташрифтом, в том смысле что многообразие размеров букв и толщин линий с самого начала играло в его создании центральную роль. «Фактором, определившим многие новые созидательные возможности, предоставляемые Uni vers’ом, было то, что впервые стало возможно работать с набором начертаний как с законченной системой». На с. 59 этой захватывающей книги можно найти метабукву п, названную «пропорциональной схемой семейства начертаний», она графически изображает желаемые изменения линий при увеличении насыщенности шрифта.
Peter Karow et al., “IKARUS: computer controlled drafting, cutting and scanning of characters and signs. Automatic production of fonts for photo-, CRT and lasercomp machines. Summary” (Hamburg: URW Unternehmensberatung, September 1979). Уже к 1980 г. система IKARUS широко использовалась для того, чтобы по нарисованной художником букве получать ее математическое описание1 [см. Baseline 3 (1981), 6-11]. Компьютерные программы также могут интерполировать степень насыщенности шрифта, хотя число независимых параметров весьма ограниченно; эта возможность была успешно использована Мэтью Картером для разработки шрифтов гарнитуры Galliard нескольких степеней насыщенности, включая Ultra Roman [см. Charles Bigelow, “On type: Galliard,” Fine Print 5 (1979), 27-30].
David Kindersley and Neil Wiseman, “Computer-aided letter design,” Printing World (31 October 1979), 12, 13, 17. Обсуждается разработанная в Кем-
Отечественному читателю можем порекомендовать книгу Каров П. Шрифтовые технологии. Описание и инструментарий. — М.: Мир, 2001. — Прим. ред.
бриджском университете система ELF, в которой воплощен новый метод оптической расстановки пробелов между буквами.
Donald Е. Knuth, “Mathematical typography,” Bulletin of the American Mathematical Society (new series) 1 (1979), 337-372. [Перепечатано с исправлениями в гл. 2 настоящего издания.] Статья, написанная вскоре после того, как автор начал свои исследования по созданию шрифтов; в ней объясняются исходные предпосылки этой работы и приводится экспериментальный образец меташрифта для прямого светлого начертания.
Donald Е. Knuth, “The letter S,” The Mathematical Intelligencer 2 (1980), 114-122. [Перепечатано в гл. 13 настоящего издания.] Обсуждение буквы, включение которой в параметризованный меташрифт представляет наибольшие сложности.
Donald Е. Knuth, Seminumerical Algorithms, Volume 2 of The Art of Computer Programming, second edition (Reading, Massachusetts: Addison- Wesley, 1981) [Имеется русский перевод первого издания: Кнут Д. Искусство программирования для ЭВМ. Т. 2. Получисленные алгоритмы.—М.: Мир, 1977; второго издания: Кнут Д. Е. Искусство программирования. Т. 2. Получисленные алгоритмы.— Киев: Диалектика, 1999.] Эта книга — первая большая работа, целиком набранная при помощи меташрифтов Computer Modern; на самом деле Computer Modern был разработан специально для книг этой серии. Ко времени напечатания этой книги разработка Computer Modern продвинулась почти до состояния, отраженного в первоначальном (1982 г.) издании предшествующей статьи, хотя некоторые символы вроде ‘2’ впоследствии были переделаны, а высота строчной буквы несколько уменьшена. Такие переделки и переосмысления, вероятно, неизбежны, особенно когда благодаря компьютерному представлению меташрифта так легко делать изменения; очень сложно остановиться и сказать: «Больше никаких улучшений не будет!»
Donald Е. Knuth, The Computer Modern Family of Typefaces, Stanford Computer Science Department report STAN-CS-80-780 (January 1980). Сотни важных усовершенствований были сделаны в период между завершением этого отчета и выходом в свет [7], и тысячи новых — когда все было переписано на язык нового METAFONT’a образца 1984 г. Почти окончательную версию Computer Modern можно найти в The Computer Modern Family of Typefaces, Volume E of Computers & Typesetting, fourth printing (Reading, Massachusetts: Addison—Wesley, 1993); несколько позднейших изменений перечислены в файле cm85.bug, являющемся частью архивов CTAN в Интернете. С 1985 г. Computer Modern всего имеет 62 параметра; самая последняя версия (1998 г.) была использована для набора этой главы, при этом выбирались значения параметров, соответствующие использованным в исходной статье 1982 г.
J. R. Manning, “Continuity conditions for spline curves,” The Computer Journal 17 (1974), 181-186. Нужды швейной промышленности бывают сходны с проблемами создателей шрифтов; в этой статье английской ассоциации Shoe and Allied Trades Research Association обсуждается проведение кривых, проходящих через заданные узловые точки, и приводится пример мета-ботинка.
Н. W. Mergler and Р. М. Vargo, “One approach to computer assisted letter design,” Visible Language [nee The Journal of Typographic Research] 2 (1968), 299-322. В этой статье обсуждается ITSYLF, первая компьютерная система для параметрического создания букв; ITSYLF включает меташрифт для прописных букв прямого светлого начертания. Этот подход был неудачен и не привел к успеху, потому что был полностью основан на описании контуров при помощи ограниченного класса кривых и потому что оборудование 1960-х гг. оставляло желать лучшего, но цель авторов похвальна.
W. A. Mozart, Anleitung zum Componiren von Walzern, so viele man will vermittelst zweier Wiirfel, ohne etwas von der Musik oder Composition zu verstehen (Berlin: Simrock, 1796); впервые опубликовано Й. Ю. Гуммелем в Амстердаме и Берлине в 1793 г. Приведено в Kochelverzeichnis 516f Anh. С 30.01. Переиздано как Musikalisches Wiirfelspiel Моцарта, под редакцией Карла Хайнца Тауберта, Edition Schott 4474 (Mainz: В. Schott’s Sohne, 1957); также вместе с предисловием Хью Нордена: (Brighton, Massachusetts: Carousel Publishing, 1973). Эти необычные ноты представляют собой вальс, который может быть сыгран 759 499 667 966 482 различными способами, так как имеется по одиннадцать вариантов для большинства тактов; гармонические принципы были проанализированы Германом Шерхеном в Gravesaner Blatter 4 (May 1956), 3-14. Моцарт также сочинил метаконтрданс, а Британский музей якобы обладает метанотами Гайдна. Примечательный пример метамузыки 20-го столетия можно найти в The Schillinger System of Musical Composition Йозефа Шиллингера, Volumes 1 and 2 (New York: Carl Fischer, 1946).
Edward Rondthaler, “From the rigid to the flexible,” Penrose Annual 53 (1959), xv, 1-9. Одно из ранних описаний вариаций шрифта, получаемых одними только фотографическими трансформациями.
Дополнение
Короткие рецензии на «Концепцию меташрифта» Фернана Бодена, Чарльза Бигелоу, Анри-Поля Бронсара, Эда Фишера-младшего, Дэвида Форда, Гэри Гора, В. П. Ясперта, Альберта Капра, Петера Карова, Александра Несбитта, Эдварда Рондталера, Джона Шапплера, Уолтера Трэйси, Герарда Унгера и Германа Цапфа, вместе с более распространенной рецензией Дугласа Р. Хофштадтера были опубликованы в Visible Language 16 (1982), 309-359, завершающейся следующим ответом автора:
Какая честь для меня, что так много выдающихся людей прочитали мою работу, и как приятно читать их глубокие комментарии! Спасибо вам за шанс добавить еще несколько слов к этому ободряющему набору писем.
Когда я читал разнообразные отклики, я часто ловил себя на том, что я скорее на стороне людей, остро критикующих мои исследования, чем тех, кто бурно их одобряет. Критические комментарии крайне полезны для планирования следующих этапов работы, которую делают люди вроде меня, когда мы ищем подходящие способы использования новых полиграфических технологий.
В нескольких рецензиях была упомянута цитата, взятая мною у Джорджа Форсайта, и ваш читатель, возможно, захочет более детально ознакомиться с его словами. Джордж был одним из первых, кто увидел настоящую ценность науки «computer science» не просто в использовании компьютеров, и процитированное мною замечание взято из вступления к речи о computer science и образовании, которую его в 1968 г. пригласили прочесть на конгрессе International Federation for Information Processing, проводившемся в Эдинбурге. Если бы я мог, я бы с удовольствием процитировал здесь всю статью; лучшее, что мне остается,— предложить людям найти ее в своих библиотеках: [Information Processing 68, Volume 2 (North-Holland, 1969), 1025-1039].
Надеюсь, мне простят еще одну цитату из моей собственной статьи, названной «Программирование на компьютере как искусство» (Computer programming as an art) [Communications of the ACM 17 (1974), 667-673; Llnformatique Nouvelle, no. 64 (June 1975), 20-27]. В этом эссе я пытаюсь показать, что основное различие между наукой и искусством состоит в том, что наука кодифицирована (и в этом смысле «автоматизирована»), в то время как искусство принадлежит к таинственной области человеческой интуиции. Мое главное утверждение состоит в том, что наука никогда не сравняется с искусством, потому что каждое продвижение в науке обязательно сопровождается прорывом в искусстве.
Таким образом, я надеюсь на то, что пришествие компьютеров поможет нам понять, насколько мало мы пока знаем о форме букв. Затем, когда мы попытаемся объяснить законы в такой точной форме, чтобы даже машина смогла по ним работать, мы узнаем много нового об исследуемом предмете, и после этого вместе с последующими поколениями сможем поднять художественный уровень еще выше.
Пока что мой опыт со времени опубликования статьи в Visible Language вполне обнадеживает. Несколько ведущих создателей шрифтов великодушно указали мне на несколько моментов, которые можно улучшить в шрифтах гарнитуры Computer Modern, и весь апрель я провел, проводя масштабные усо-
вершенствования под руководством Ричарда Саутолла. Количество параметров выросло с 28 до 45, но все параметры пока что кажутся осмысленными; и такое внимание к мелочам уже приводит к значительно лучшим результатам. Еще многое нужно сделать, включая дальнейшее развитие математики форм, но теперь у нас есть некоторая уверенность, что разрабатываемый нами инструментарий не окажется неадекватным поставленной задаче. Я надеюсь опубликовать книгу, содержащую все то, чему меня научили эти люди, чтобы эти знания могли быть приняты во внимание, прочувствованы и послужили своему предназначению, а не просто применялись.
Дональд Э. Кнут
Отделение Computer Science Станфордский университет
12 октября 1982 г.
Замысел и символы этого шрифта получили как благоприятную, так и неблагоприятную оценку, но ввиду того что, насколько я знаю, библиографы уже написали об этом на семи языках в шестнадцати странах мира, я чувствую, что наши достижения по крайней мере вызвали интерес.
— DARD HUNTER, Primitive Papermaking (1927)
Уроки METAFONT’a
[Представлено в качестве пленарного доклада (keynote address) 1 августа 1983 г. на Пятом рабочем семинаре по обучению и исследованиям в области создания шрифтов (Fifth Working Seminar in Letterform Education and Research), проведенном под патронажем Международной полиграфической ассоциации (Association Typographique Internationale (ATypI)). Первоначально опубликовано в Visible Language 19 (1985), 35-53.]
Разработчики шрифтов столкнулись сейчас с серьезной проблемой — проблемой формирования растровых (оцифрованных) образов для печати. Основной вопрос здесь звучит так: «Что можно считать правильным подходом к созданию подобных образов?». Или, скорее, «Что будет правильным подходом?» — поскольку речь пойдет в основном об аспектах долгосрочного характера, отличающихся от более конкретных проблем, обсуждаемых на данном совещании. Я попытаюсь изложить ряд соображений, навеянных ходом и результатами исследований, проводимых нами в Станфорде, поскольку, на мой взгляд, это позволит найти ответы хотя бы на часть возникающих вопросов.
Я вовсе не ожидал, что рассматриваемая проблема будет простой для решения. И тем не менее, когда я стал анализировать уроки, извлеченные из ее решения, то понял, что основной урок состоит как раз в том, что данная проблема оказалась в высшей степени непростой. На самом деле, ничего удивительного в этом нет. Более чем за тридцать лет было поставлено большое число компьютеров, причем продавцы их утверждали, что вычислительную технику использовать просто, однако истинное положение дел прямо противоположно этому утверждению. Компьютерное программирование требует внимания к мельчайшим деталям, чего не требуется ни в одном другом виде человеческой деятельности. Что же касается шрифтов, то их разработка —весьма тонкая материя, значительно более сложная, чем это обычно представляется большинству людей, поскольку наши машины и наши глаза взаимодействуют с геометрическими формами достаточно сложным образом. Я уверен, что проектирование цифровых шрифтов представляет собой чрезвычайно интересную, захватывающую задачу, которая вполне достойна внимания наших выдающихся деятелей науки и искусства. Я полагаю, что мир станет лучше, когда мы больше узнаем об этих вещах.
Вот еще одно соображение, которое я хотел бы высказать, прежде чем перейти к изложению подробностей и результатов своих исследований. Я математик, и прекрасно понимаю, что совсем не художник. Мне отнюдь не кажется, что с помощью математических методов можно решить все проблемы полиграфии, но математики могут помочь в их решении. Это трудно представить, но после более чем 2000 лет накопления геометрических знаний у нас все еще нет подходящих средств для разработки алфавитов. Люди, для которых любовь к буквам частично обусловлена их неприязнью (или, скажем так, отсутствием влечения) к алгебре, видят в математике настоящую угрозу. Мне жаль, что существует такой страх перед математикой, но я знаю, что он распространен довольно широко. Я полностью отдаю себе отчет в том, что внедрение математики в области, где она ранее не применялась, нарушает покой лидеров таких областей, поскольку они внезапно обнаруживают, что надо изучать огромный объем нового материала, чтобы оказаться на уровне изменившихся требований к предмету. Я думаю, однако, что нет реальных причин для тревоги; не может быть предмет настолько сложным, чтобы никто в нем не смог разобраться. Наиболее впечатляющим в последних типографских разработках является все более крепнущее содружество между учеными и художниками: мосты, которые они строят между «двумя культурами», о которых говорил Сноу. Я отнюдь не предполагаю, что художники-шрифтовики бросят вдруг их традиционное ремесло и дружно займутся изучением компьютерного программирования; я говорю сейчас о том, чтобы они вступали в такое творческое взаимодействие со специалистами по вычислительной технике и информатике, которое привычно для них в отношении, например, специалистов по изготовлению матриц для печатных машин. С другой стороны, мне нравится видеть студентов, которые все более уверенно чувствуют себя в обоих из этих миров одновременно.
Но что именно интересно на мой взгляд в этой области? В течение последних нескольких лет я разрабатывал компьютерную систему, получившую наименование МЕТА FONT. У этой системы есть три необычных свойства:
МЕТА FONT понимает специальный язык для вычерчивания геометрических форм с помощью имитируемого пера, обладающего варьируемой толщиной. В качестве примера рассмотрим рис. 1, где изображена «сердцевидная» кривая, полученная с помощью слегка меняющегося по ширине пера. Изобразить такую фигуру для METAFONT’a —совсем простая задача, в которой «хребет» кривой определяется всего шестью точками. Фактический контур кривой весьма сложен и описать его — проблема нетривиальная, однако движения пера здесь совсем просты.
Язык METAFONT поощряет также работу с явными параметрами, что дает возможность описывать не единственную форму, а их семейство. Например, на рис. 2 показан один из эскизов, сделанных Мэттью Картером, когда он работал над гарнитурой Galliard; METAFONT нацеливает разработчика на включение изменений (средств варьирования формы) непосредственно в проект. Именно по этой причине и появилась приставка «meta-» в имени METAFONT; подход, реализуемый данной системой, можно трактовать как метаподход в том смысле, что он имеет дело со шрифтами некоторым образом «с
Рис. 2.Рис. 2.
Рис. 2.
Рис. 2.
внешней стороны», на более высоком уровне, аналогично тому, как «метаматематика» есть теория методов математического доказательства. Метаконцепции множатся в наши дни; например, я недавно услышал о новой игре, называемой «метаигрой» [7], в которой первое, что надо сделать —это выбрать игру. (Интересная проблема возникает, когда первый игрок скажет: «Давай сыграем в метаигру!»)
Я уже писал о концепции меташрифта [6], представляющего собой высокоуровневое описание, порождающее индивидуальные шрифты, набор которых задается меташрифтом. Эту концепцию надо отличать от системы МЕТА FONT как таковой, представляющей собой один из подходов к реализации меташрифтов. На рис. 3 (согласно Скотту Киму) показано, как влияют на вид шрифта изменения в его параметрах на примере ранней версии меташрифта, называемого Computer Modern. Для наглядности каждая из варьируемых переменных в этом примере доводилась до предельных значений. На рис. За демонстрируется изменение наклона букв, на рис. ЗЬ — их ширины; в обоих этих случаях ширина пера оставалась одной и той же, менялась только траектория его движения, значит, изменения в изображении не могут быть осуществлены с помощью одних только оптических преобразований.
На рис. Зс показано, что произойдет, если траектория движения пера будет практически одна и та же, но размер пера меняется. Можно также варьировать величину засечек на буквах (рис. 3d). Преобразование, результат действия которого виден на рис. Зе, имеет не совсем обычный характер. Здесь изменения вносятся в «константу», используемую при вычислении кривых, это приводит к изменениям в форме округлых элементов букв. На рис. 3f демонстрируется результат варьирования несколькими параметрами одновременно, предпринимаемого для сохранения удобочитаемости букв при изменении кегля; это обстоятельство является одной из основных причин параметризации шрифтов. Буквы здесь масштабировались таким образом, чтобы их я-высота не менялась, в этом случае более очевидны результаты воздействия других изменений. В каждом из рассматриваемых случаев буквы формировались на основе одного и того же METAFONT’oBCKoro описания; все показанные изменения порождались только варьированием параметров, применяемым для шрифта в целом.
-1/1(а)Typography(b)—,», T j, ЦП rij(мпмИЬмм) 0*МTypography—.-hy« Typography(мпм! МмМ 1/4 *МTypographyM^h,. Typography1/4 *МTypography— Typography1/1 —Typography■^hyu Typography1/1 *МTypography1/1*мntMMhyl ^J^X 0 ^ц1умг№м*ЫЬу 0Typographyw-m TypographyмпкмЫЬу 1/4Typography^-,*(4/s) TypographyмпкмаЫЬу 1/1Typography^-ikv» TypographyмгМшШЬу 1/4Typography»J^2'X Typography(мп-1) нпАмЫЬу 1Typographyс^У52 TypographyMnhmWhylTypographyw-,fd/5) Typography-rif. мжМЬу 4.Typography(d)w-,to Typography (e)-1/1(а)Typography(b)—,», T j, ЦП rij(мпмИЬмм) 0*МTypography—.-hy« Typography(мпм! МмМ 1/4 *МTypographyM^h,. Typography1/4 *МTypography— Typography1/1 —Typography■^hyu Typography1/1 *МTypography1/1*мntMMhyl ^J^X 0 ^ц1умг№м*ЫЬу 0Typographyw-m TypographyмпкмЫЬу 1/4Typography^-,*(4/s) TypographyмпкмаЫЬу 1/1Typography^-ikv» TypographyмгМшШЬу 1/4Typography»J^2'X Typography(мп-1) нпАмЫЬу 1Typographyс^У52 TypographyMnhmWhylTypographyw-,fd/5) Typography-rif. мжМЬу 4.Typography(d)w-,to Typography (e)(с)—— Typography(г<*<) Might-О Typographymw-v4 TypographyMi*.-,/, TypographyMight-V4 Typography(h^M^ht-i TypographyMight-, Typography..^ Typography Typography.^ Typography•^ Typography7hm Typography.^ Typography5H- Typography(f)(с)—— Typography(г<*<) Might-О Typographymw-v4 TypographyMi*.-,/, TypographyMight-V4 Typography(h^M^ht-i TypographyMight-, Typography..^ Typography Typography.^ Typography•^ Typography7hm Typography.^ Typography5H- Typography(f)
-1/1
(а)
Typography
(b)
—,», T j, ЦП rij
(мпмИЬмм) 0*М
Typography
—.-hy« Typography
(мпм! МмМ 1/4 *М
Typography
M^h,. Typography
1/4 *М
Typography
— Typography
1/1 —
Typography
■^hyu Typography
1/1 *М
Typography
1/1*м
ntMMhyl ^J^X 0 ^ц1у
мг№м*ЫЬу 0
Typography
w-m Typography
мпкмЫЬу 1/4
Typography
^-,*(4/s) Typography
мпкмаЫЬу 1/1
Typography
^-ikv» Typography
мгМшШЬу 1/4
Typography
»J^2'X Typography
(мп-1) нпАмЫЬу 1
Typography
с^У52 Typography
MnhmWhyl
Typography
w-,fd/5) Typography
-rif. мжМЬу 4
.Typography
(d)
w-,to Typography (e)
-1/1
(а)
Typography
(b)
—,», T j, ЦП rij
(мпмИЬмм) 0*М
Typography
—.-hy« Typography
(мпм! МмМ 1/4 *М
Typography
M^h,. Typography
1/4 *М
Typography
— Typography
1/1 —
Typography
■^hyu Typography
1/1 *М
Typography
1/1*м
ntMMhyl ^J^X 0 ^ц1у
мг№м*ЫЬу 0
Typography
w-m Typography
мпкмЫЬу 1/4
Typography
^-,*(4/s) Typography
мпкмаЫЬу 1/1
Typography
^-ikv» Typography
мгМшШЬу 1/4
Typography
»J^2'X Typography
(мп-1) нпАмЫЬу 1
Typography
с^У52 Typography
MnhmWhyl
Typography
w-,fd/5) Typography
-rif. мжМЬу 4
.Typography
(d)
w-,to Typography (e)
(с)
—— Typography
(г<*<) Might-О Typography
mw-v4 Typography
Mi*.-,/, Typography
Might-V4 Typography
(h^M^ht-i Typography
Might-, Typography
..^ Typography Typography
.^ Typography
•^ Typography
7hm Typography
.^ Typography
5H- Typography
(f)
(с)
—— Typography
(г<*<) Might-О Typography
mw-v4 Typography
Mi*.-,/, Typography
Might-V4 Typography
(h^M^ht-i Typography
Might-, Typography
..^ Typography Typography
.^ Typography
•^ Typography
7hm Typography
.^ Typography
5H- Typography
(f)
Рис. 3.
Чтобы обеспечить получение характеристик 1 и 2, METAFONT’obckhc описания форм литер строятся как программы. Например, на рис. 4 показаны две такие программы для ранней версии меташрифта с именем CHEL, разрабатывавшегося Томом Хики в 1982 г. (см. [1]). Образцы букв, формируемых этими программами при различных значениях параметров, показаны в верхней части рисунка. Программа для буквы ‘Ь’ такая короткая из-за того, что большая часть работы здесь выполняется подпрограммой — вспомогательной программой, используемой для конструирования повторяющихся частей нескольких различных по форме литер. В данном случае Хики сделал подпрограмму для вычерчивания округлой части буквы, которую и применил при формировании букв ‘d’, ‘р’ и т. п.
METAFONT’obckhc программы совершенно не похожи на обычные компьютерные программы, поскольку они в большей степени «декларативны», чем «императивны». Другими словами, они устанавливают соотношения, которые должны иметь место, но ничего не говорят относительно того, как удовлетворить сформулированным условиям. К примеру, METAFONT’obckoc описание может декларировать, что левый контур должен отстоять на одну единицу от левой границы бокса, включающего литеру; программе не надо задавать, что центр пера следует поместить на одну единицу от левого края бокса плюс половина ширины вертикального штриха литеры, поскольку компьютер может это подсчитать. Аналогично, можно задать, что некоторая точка лежит на пересечении двух линий, но нет необходимости указывать, каким образом найти эту точку пересечения. Таким образом, большинство математических сложностей ложатся на компьютер и выполняются «за сценой».
"The letter Ъ";
call dwbegieCb, 1,.65,рА,0,*/«1*х 'Slant);
Vi = A; vi = 0;
hpenht 1; hpen;
wit draw 1. .1;% item
call "a smaUbowl(leA).
Рис. 4.
Поскольку METAFONT’obckhc программы включают всю информацию относительно того, как изображать каждую из букв для широкого диапазона условий, эти программы могут фиксировать «интеллектуальную составляющую», лежащую в основе проводимой разработки. Я считаю, что это свойство METAFONT’a —его способность «схватывать» намерения разработчика шрифта, а не только фиксировать рисунок, являющийся следствием этих намерений—окажется наиболее важным среди всех остальных его свойств. Способность изображать бесконечно много алфавитов варьированием параметров не просто важная цель сама по себе. Она позволяет объяснить проект в точных терминах, что в высшей степени поучительно как для разработчиков шрифтов, так и для тех, кто читает написанные ими программы. Применение компьютера способствует повышению дисциплины, что помогает пользователям уточнить используемые ими знания; такого рода образовательный опыт —действительно полезный побочный результат.
Вернемся теперь к главной теме моего сообщения —урокам, извлеченным мною из работы с METAFONT’om. Лучше всего, наверное, будет начать рассказ с лета 1977 г., когда я начал эти работы. Тогда у меня и в мыслях не было, что придется разрабатывать язык для описания форм литер: ведь разработкой шрифтов занимались, обыкновенно, художники и полиграфисты. Мне не удалось получить хороших изображений букв, которые я мог бы использовать при наборе текстов, так что пришлось создавать компьютерные методы проектирования шрифтов, причем делать это, начиная с «чистого листа». Мои издатели снабдили меня высококачественными оттисками, сделанными с помощью шрифтов, которые использовались для первого издания моей книги, но зато мне пришлось иметь дело с крайне примитивным оборудованием. Экспе-
рименты с телевизионной камерой, соединенной с компьютером, завершились полной неудачей, поскольку оптика телевизионной камеры вносила значительные искажения, как только мы пытались увеличить маленькие изображения. Кроме того, незначительное изменение в интенсивности студийного освещения приводило к очень большим изменениям в получаемом телевизионном изображении. Наилучшие результаты, которых мне удалось добиться, были получены путем изготовления на 35-мм пленке слайдов с листов гранок, с последующим увеличением изображения с помощью диапроектора, установленного на расстоянии 8 м от стены в моем доме, игравшей роль экрана. Изображения букв высотой около 5 см получались несколько расплывчатыми, с них я и снимал карандашные эскизы.
Мысль о «трех пи» системы MET A FONT —рисование с помощью пера (pens), использование параметров (parameters) через программы (programs) — осенила меня примерно через час после того, как я начал делать эскизы описанным выше способом. На меня внезапно снизошло, что я должен не просто копировать уже имеющиеся формы литер. Изначально человеческие существа рисовали их, и мне захотелось узнать, чего же достигла человеческая мысль на этом поприще, и использовать такого рода знания в компьютерной программе.
Программы, которые я написал в 1977 г., использовали традиционный «императивный» язык программирования SAIL, очень похожий на международный язык программирования ALGOL. Каждый раз, когда я менял что-нибудь в программе для какой-либо из букв, приходилось перекомпилировать эту программу, чтобы внести изменения в рабочую программу в кодах машины. Идеи декларативного, интерпретируемого языка, подобного METAFONT’y, у меня не возникало, пока несколькими месяцами позднее она не была высказана Робертом Филманом. Однако отсутствие такого языка на самом деле в 1977 г. не очень меня сдерживало, основная проблема состояла в том, что я не знал подходящего способа описания геометрических форм.
Чтобы проиллюстрировать эти трудности, я решил показать вам кое-что из того, что я до сих пор не показывал никому —самые первые результаты, которые я получил в 1977 г., когда попытался рисовать арабские цифры. После того как я представил сделанные ранее эскизы в виде компьютерных программ, машина выдала мне то, что представлено на рис. 5, где каждый из показанных фрагментов соответствует разным значениям основных параметров (нормальный, полужирный, капитель, шрифт без засечек, имитация пишущей машинки соответственно). С цифрой ‘8’ возникли особые проблемы, из-за которых она — к счастью — оказалась напечатанной только в одном стиле, однако мои первоначальные ошибки в цифрах ‘2’, ‘5’, ‘6’ и ‘7’ оказались повторенными пятикратно. Я показываю сейчас эти ранние результаты потому, что похожие проблемы могут возникнуть даже и в существующей версии METAFONT’a; не так-то просто описать суть геометрической формы, чтобы машина правильно воспроизвела ее.
Рисунок 5 буквально кишит огрехами, так что будет поучительно рассмотреть его более внимательно. В нескольких случаях я просто ошибся. Например,
2]5Mr2Wi™Рис. 5.2]5Mr2Wi™Рис. 5.2!5|6|7,!2'5j6i7^
2]5Mr2Wi™
Рис. 5.
2]5Mr2Wi™
Рис. 5.
я забыл об использовании пера достаточной толщины, когда начал рисовать диагональный элемент цифры ‘2’. Странный сбой в третьей «двойке» вызван плохой спецификацией угла в нижней части цифры —я задал тот же самый угол для малой капители, что и для нормального размера, несмотря на то, что рисовалось изображение меньшего, чем было до этого, размера. Еще один некорректный угол имеет место в верхней части округлого элемента каждой из цифр ‘5’. Но другие ошибки оказались более серьезными. Неважная форма нижней части цифр ‘5’ еще хуже смотрится в верхней части цифр ‘6’, где головка-наплыв оказалась поднятой слишком высоко и плохо соотносилась с оставшейся частью цифры. Еще хуже оказался результат в нижней части цифры ‘6’, где вообще не срабатывал принятый мною подход и этот элемент пришлось переделывать несколько раз, доводя его экспериментально. Верхняя часть крайней правой цифры ‘7’ показывает проблему, которую мне не удавалось решить удовлетворительно в течение пяти лет, пока я не понял, наконец, что верхний левый фрагмент цифры ‘7’ (и нижний правый цифры ‘2’) можно рассматривать как горизонтальные штрихи, аналогичные соответствующим фрагментам букв типа ‘Т или ‘Е’.
01234567
01234567
01234567
01234567
01234567 Рис. 6.
К концу 1977 г. цифры из моего экспериментального меташрифта приняли вид, показанный на рис. 6. На тот момент они меня устраивали и большую часть 1978 г. я провел в разработках системы ТЁК, а также выполнял ряд других исследований в области вычислительной техники и информатики. В 1979 г. я решил разработать символьный язык для описания форм литер, который отражал бы более высокий уровень понимания данного предмета, чем тот, что был у меня, когда я писал в 1977 г. программы на ALGOL’e. Новый язык стал основой системы МЕТА FONT в ее первоначальном варианте [4]. В 1980 г. пришлось провести большую работу по формированию интерфейса между выходом METAFONT’a и фотонаборным устройством с высокой разрешающей способностью. В течение всего этого времени я был полностью загружен решением проблем, связанных с программным обеспечением, и особенно много заниматься проектированием шрифтов не мог. Потом я, наконец, добился того, что рассчитывал сделать на два года быстрее: завершил подготовку
второго издания своей книги Seminumerical Algorithms [5] — 700-страничного тома, где все, за исключением очень небольшого числа иллюстраций, было сделано исключительно компьютерными методами. В этой книге в общей сложности было использовано 35 шрифтов — прямой (романский) семи размеров, курсивный шести размеров, по три размера для полужирного и наклонного шрифтов, а также шрифта, имитирующего пишущую машинку, причем каждый размер рисовался отдельно (т. е. получался не просто масштабированием из другого шрифта); было также шесть вариантов шрифта без засечек и семь pi-шрифтов, содержащих математические символы. Все они были созданы с помощью первой версии METAFONT’a, и когда страницы книги распечатывались на принтере, выглядели они, по-моему, вполне прилично.
Я не могу, однако, передать того громадного разочарования, которое испытал, когда наконец Seminumerical Algorithms были отпечатаны в начале 1981 г. Впервые я увидел результат всего процесса в целом, включая печать и переплет. До того момента я работал почти исключительно на оборудовании с невысокой разрешающей способностью и, конечно, результаты, получаемые при повышении разрешения, должны были быть много лучше, так что я с нетерпением ожидал увидеть прекрасную книгу. Когда же я получил и раскрыл сигнальный экземпляр, меня охватило жестокое разочарование — все выглядело никуда не годно! Основное потрясение было вызвано тем, что я видел теперь, как выглядят шрифты отпечатанными в книге и, что очень существенно, в переплетенном виде, в обложке темно-желтого цвета, как это было и в первом издании. Тот факт, что новый формат был помещен в старый контекст, подчеркивал недостатки нового формата. Конечно, новый текст вполне можно было читать и я хотя бы немного мог утешаться мыслью, что моя книга получилась все же не настолько плохой как некоторые другие, вышедшие из печати примерно в то же время. Но это было совсем не то, что мне хотелось получить. Шрифт без засечек не получился совсем, соотношение жирности прямого и курсивного шрифтов для цифр было неудачным. Печать с высоким разрешением выявила также недостатки в отдельных литерах, которые ранее не были заметны. У меня возникла стойкая неприязнь к формам цифр, особенно ‘2’ и ‘б’. Когда я пользовался этой книгой для справок и в учебном процессе, то был вынужден смотреть на числа на каждой странице и это сбивало меня с мысли: я хотел думать об элегантной математике, но это было невозможно при таком уродливом полиграфическом исполнении.
Мое глубокое разочарование все-таки до конца меня не обескуражило. Дело в том, что я прочел множество биографических книг и знал о так называемом кризисе среднего возраста. Поскольку в 1978 г. мне исполнилось 40 лет, я был начеку, ожидая, что совершу по крайней мере одну крупную ошибку. Я всегда считал, что надо следовать интуиции, но быть готовым к тому, что она может подвести. Я знал, что METAFONT совершенно не похож на все, что было сделано до него и что делалось в то время, и мне стало казаться, что все мои идеи, может быть, просто вздорны —не удивительно, что никто до сих пор не пытался сделать чего-то похожего. С другой стороны, мне представля-
лось, что основные идеи —перья, параметры и программы — все же верны, а недостатки в моей опубликованной книге связаны не с этими идеями, а с их реализацией. И я решил продолжать упорно добиваться своего.
С тех пор прошло два года. За это время и я, и мои коллеги накопили большой опыт работы с первой версией METAFONT’a. В следующем году я планирую разработать эту систему заново, основываясь на приобретенном опыте. Это позволит избежать в новой системе большей части недостатков, характерных для старой. Так как новый язык будет готов в 1984 г., было бы неудивительно, если бы мы последовали Джорджу Оруэллу и назвали его NEWSPEAK (НОВОЯЗ). Мы планируем сделать METAFONT84 широко доступным и пригодным для использования на всех компьютерах, вплоть до самых маленьких.
Прошу простить меня за то, что я вставил в эту работу так много биографических заметок. Моя основная цель — рассказать об уроках, извлеченных из работы над METAFONT’om, и теперь самое время поговорить о некоторых более конкретных подробностях.
Один из первых важных выводов — компьютер заслуживает того, чтобы относиться к нему как к некоемому новому средству, среде разработки. Начиная решать проблему разработки средств цифрового набора текстов, не следует ожидать, что все будет делаться в точности так же, как и раньше, скорее придется «учить» компьютер, как традиционно учат людей обращаться с кистью и резцом. Используя машину, лучше всего сдерживаться и отказаться от некоторых наших «указаний» — чтобы позволить машине выявить, что работает, а что нет. Идеальный вариант — работать совместно с инструментом; мы задаем важные подробности, но и хотим, чтобы нам помогли в этой работе.
Конечно, все это имеет смысл, только если компьютер будет «хорошей средой», только если кривые, которые он рисует, будут эстетически привлекательными. Рассмотрим в качестве примера рис. 7а, показывающий, что сегодняшний МЕТА FONT вполне может породить эти уродливые формы, если пользователь просто задаст восемь-девять точек и не даст каких-либо дополнительных указаний. Можно быстро научиться преодолевать эти затруднения и с помощью METAFONT79 получать вполне привлекательные геометрические формы, однако новая система в этом отношении значительно лучше. Джон
Хобби получил недавно важные математические результаты, дающие возможность из тех же самых данных, что были исходными для рис. 7а, получить результаты, показанные на рис. 7Ь. Новый подход, предложенный Дж. Хобби, реализован в METAFONT84. Это весьма существенно не только по той причине, что упрощает применение системы и делает ее легче реагирующей на потребности пользователя, но и потому, что когда осуществляется вывод изображений литер на оборудовании с невысокой разрешающей способностью, всегда требуется подгонка кривых. Метод Хобби значительно снижает вероятность возникновения ситуации, когда такого рода подгонка нарушит форму кривых.
Рис. 9.Рис. 9.
Рис. 9.
Рис. 9.
Рисунки 8 и 9 позволяют уточнить смысл, в котором надо понимать утверждение о важности компьютера как изобразительного средства. Формы типа «слезинка», показанные на рис. 8, изображены METAFONT’om с помощью подпрограммы teardrop, для которой надо было задать всего несколько точек (одну вверху, одну внизу и одну горизонтальную координату на границе наплыва); все другие точки найдены путем математических вычислений, проведенных в подпрограмме. Джон Хобби много поработал, чтобы создать эту подпрограмму, но разработчик может теперь эффективно использовать ее, нисколько не беспокоясь о том, какие именно вычисления на самом деле реализуются данной подпрограммой. На рис. 9 показана серия разнообразных штрихов, полученных с помощью подпрограммы teardrop и трех других подпрограмм в ходе ранних экспериментов, проводившихся Хобби и Гу с целью выработки подхода к изображению знаков для восточно-азиатских языков [2]. Дальнейшая работа Хобби и Гу привела к другому набору подпрограмм, отвечающему потребностям получения высококачественных изображений полного комплекта знаков для японского и китайского языков в различных стилях [3].
Второй важный урок, вынесенный из использования METAFONT’a, состоял в том, что правильнее и лучше считать различные элементы проекта взаимодействующими, а не задавать их изолированно друг от друга. Например, лучше сказать, что некоторая точка лежит посередине между двумя другими, чем
mathematics mathematics mathematics mathematics mathematics mathematicsРис. 10.mathematics mathematics mathematics mathematics mathematics mathematicsРис. 10.■■■J■■■JРис. 11.Рис. 11.QQQQQ
mathematics mathematics mathematics mathematics mathematics mathematics
Рис. 10.
mathematics mathematics mathematics mathematics mathematics mathematics
Рис. 10.
■■■J
■■■J
Рис. 11.
Рис. 11.
О io iO io
QQQQQ ioioOiOu
Рис. 12.
дать явные координаты всех трех точек. Один из способов показать это представлен на рис. 10, являющемся результатом моих экспериментов 1977 г. со случайными числами. Я изменил в них мои ранние программы таким образом, чтобы ключевые точки литер не задавались точно; считалось, что компьютер будет как бы «немного навеселе», размещая эти точки. Верхняя строка литер показывает результат, полученный без введения случайных смещений в координатах ключевых точек; вторая строка демонстрирует, что получается, если точки размещаются случайно с величиной среднеквадратического отклонения для них 1%; третья строка —то же самое, но при среднеквадратическом отклонении 2% и т. д. Главный вывод из этого эксперимента — получающиеся буквы кажутся «теплее», если в проект внести небольшой элемент случайности. Но я привел рис. 10 по другой причине —показать, что различные элементы литеры влияют друг на друга и налицо взаимозависимость между ними. Например, если вертикальный штрих перемещается, то вслед за ним перемещается и засечка; координаты индивидуальных точек не будут независимыми друг от друга случайными величинами.
Аналогичная зависимость видна и из рис. 11; я получил эти три шестерки варьированием координат всего одной точки в спецификации данной цифры (точка 6, лежащая на самом верху ее округлого элемента). Координаты многих других точек также будут изменяться при перемещении точки 6, поскольку программа METAFONT работает большей частью с информацией об относительном расположении точек, а не с абсолютными значениями их координат.
Еще один пример взаимозависимости элементов литеры показан на рис. 12. Здесь опять серия изображений буквы получена варьированием всего одного элемента в программе. В верхней строке этих букв я менял наклон касательной к кривой в середине буквы S; в нижней — жирность букв. Как в одном, так и в другом случае возникли изменения в координатах и других точек, поскольку они определялись выражениями, задающими связи между параметрами буквы, а не простым набором чисел.
Наверное лучшим для меня способом выразить основной дух работы над METAFONT’om было бы показать некоторые мои «МЕТА-ляпы» — получавшиеся время от времени совершенно неожиданные результаты. В самом деле, компьютер полон сюрпризов, и это служит временами источником появления различных забавных ситуаций. Например, одна из моих программистских
ошибок привела к тому, что хвостик в букве ‘g’ завернулся весьма неожиданным образом (рис. 13а); а одна из моих попыток нарисовать букву ‘А’ для шрифта без засечек дала результат, очень похожий на рекламу джинсов Levi’s (рис. 13b). Ошибки в формулах привели к появлению необычной буквы ‘М’ (рис. 13с), франтоватой ‘S’ (рис. 13d) и «жестокой» буквы ‘С’ (рис. 13е). Когда я ошибся с размещением засечки в рис. 13f, клянусь, и не думал в тот момент о японской йене, «валютные» совпадения здесь абсолютно случайны!
Рисунок 13g возник вследствие моих попыток обнаружить, почему METAFONT в букве ‘а’ рисует неверную кривую; я рассчитывал увидеть больше деталей, выведя составляющие эту кривую штрихи, поскольку подозревал, что имеет место компьютерная ошибка. В этом случае оказалось, что МЕТА FONT не виноват — это я допустил математическую ошибку, когда задавал угловой коэффициент касательной в критической точке.
Чтобы приводимая коллекция «металяпов» была более полной, на рис. 13h показана лигатура, в которой я нечаянно попросил, чтобы компьютер соединил оба верхних крючка у Т с точкой над Т. А на рис. 13i получилось то, что можно было бы назвать «{filling station» («ббензоколонкой»).
После 1980 г. исследования в рассматриваемой области проходили очень успешно, поскольку такие люди, как Чак Бигелоу, Мэттью Картер, Крис Холмс, Ричард Саутолл и Герман Цапф чрезвычайно помогли в совершенствовании тех грубых средств, с которых я начинал. В частности, Ричард и я провели три недели, интенсивно отрабатывая каждую букву и предварительные результаты, полученные нами, были в высшей степени обнадеживающими. Он преподал мне много важных уроков и я хотел бы сказать кое-что о вещах, которыми мы тогда занимались.
На рис. 14 представлены два варианта нарисованной нами буквы ‘О’. Изображение книзу слегка «утяжеляется» и мы добавили параметр, позволяющий
Рис. 14.Рис. 14.
Рис. 14.
Рис. 14.
внутренний и внешний контуры буквы делать разными без потери свойств меташрифта (метасвойств). Просто нарисовать два суперэллипса с различными для них степенями этой «суперности» не получалось, так как внутренняя кривая иногда подходила слишком близко к внешней или даже пересекала ее; наше решение состояло в том, чтобы брать оба суперэллипса из одного и того же семейства кривых, а затем «сдвигать» внутреннюю кривую по отношению к внешней на некоторое относительное расстояние.
Некоторые из поправок, внесенные Ричардом в то время, когда мы занимались отработкой буквы ‘Р’, показаны на рис. 15. Обратите внимание, например, что мы немного уменьшали жирность вертикального штриха внутри округлой части буквы. Чтобы при выполнении такого рода улучшений дух метаподхода сохранялся, мы ввели параметр «коррекция вертикального штриха», который можно было использовать для подстройки жирности букв (т. е. ширины штрихов) и в других буквах. Иногда жирность вертикального штриха приходилось менять по два-три раза, прежде чем процесс можно было считать завершенным.
Нас порадовало, что METAFONT хорошо справляется со своей работой в теснине диагональных штрихов, даже когда этому элементу и не уделялось специального внимания. Например, внутренняя верхняя часть полужирной рубленой буквы ‘А’ на рис. 16а несколько раздвинута, это обеспечивает достаточную величину просвета, что создает иллюзию прямых толстых штрихов. Наши METAFONT’oBCKne программы разработаны так, чтобы получить этот эффект как при невысоких, так и при больших значениях разрешающей способности. Рисунок 16b показывает, как та же самая идея применена к букве ‘А’, выполненной в стиле шрифта пишущей машинки.
Подводя итог проделанной работе, можно сказать, что мы теперь уделяем большое внимание контурам литер; новая версия METAFONT’a отличается от старой и в этом отношении. Я понимаю теперь, насколько был наивен в 1977 г., когда считал, что проблема контура обводки литер решается сама по себе, если я просто рисую пером, имеющим правильную форму. С другой стороны, нам не следует отказываться от «перьевой метафоры», поскольку «в первом
Рис. 15.Рис. 15.
Рис. 15.
Рис. 15.
приближении» она дает верное представление о том, как рисуются буквы; детали контура обводки становятся существенными, когда мы выполняем «второе приближение», вводя поправки, улучшающие форму литеры, однако основные характеристики буквы при этом остаются неизменными.
Рисунок 17 представляет собой тестовую палитру, которую я подготовил в 1980 г., когда впервые экспериментировал с METAFONT’obckhmh программами, имитирующими широкие перья с переменным нажимом, следуя советам Германа Цапфа. (Фактически это первое, что захотел попробовать Герман, когда познакомился с METAFONT’om.) Хотя все эти конкретные штрихи были нарисованы пером, зафиксированным в определенном положении, в данном случае — под углом 25°, дальнейшие эксперименты показали возможность варьирования и углом установки пера.
Я бы хотел завершить свой рассказ предложением принять вместе со мной участие в некотором мысленном эксперименте. Давайте рассмотрим буквы ‘АТУРГ, которые Самнер Стоун подготовил в качестве эмблемы нашей конференции [8] и попытаемся представить, как можно было бы их реализовать в виде меташрифта. Конечно, мы можем просто отследить очертания этих букв, но это малоинтересно и не дает никакой пищи для размышлений. Попытаемся вместо этого использовать принципы, на которых основывался Самнер,
Рис. 17.Рис. 17.Рис. 18.Рис. 18.
Рис. 17.
Рис. 17.
Рис. 18.
Рис. 18.
для формирования такой спецификации, которая будет позволять порождать множество прекрасных букв.
Займемся вначале буквой ‘А’ (рис. 18а). Она явно составлена из трех штрихов, два из которых тонкие, а один — толстый. Тонкие штрихи рисуются более узким пером по сравнению с используемым для получения толстого штриха. Сразу же мы приходим к необходимости ввести параметры ширины этих двух перьев, узкого и широкого. Штрихи в этих буквах имеют также изящные сужения/расширения, следовательно, надо добавить третий параметр, управляющий величиной такого рода сужения. Варьируя этот параметр, мы можем получать такие буквы, у которых сужение отсутствует вообще, вплоть до таких, где оно чрезмерно.
Перейдем теперь к букве ‘Т’ (рис. 18b). Мы видим здесь, что ее горизонтальный штрих нельзя назвать ни толстым, ни тонким. Можно здесь или ввести еще один параметр, или же использовать промежуточное значение жирности (например, посередине между значениями ширины пера для толстого и тонкого штрихов для буквы ‘А’). Сужение здесь также используется, но оно выражено менее явно, чем в предыдущем случае. И опять, нет особой необходимости вводить новый параметр, если мы решим, например, что вертикальный штрих у буквы ‘Т’ имеет сужение, равное половине от значения сужения, принятого для буквы ‘А’. Еще одним проектным параметром является угол, под которым завершается на линии шрифта вертикальный штрих буквы; заглядывая вперед, мы должны учесть, что аналогичное свойство надо будет обеспечить также для букв ‘Y’ и Т’.
Построить букву ‘Y’ (рис. 18с) будет, по-видимому, не очень просто, поскольку надо будет выработать принципы, лежащие в основе получения весьма сложного слияния трех штрихов в центре данной буквы. Эта часть буквы выглядит простой, если она построена правильно, но я думаю, что придется потратить по крайней мере два-три часа, чтобы построить соответствующую схему, удовлетворительно работающую при варьировании параметров.
Буква ‘Р’ (рис. 18d) имеет интересное маленькое заострение в верхней части ее округлого элемента, однако ее наиболее заметной особенностью является
зазор между нижней частью этого элемента и ножкой буквы. Скорее всего, нам придется ввести параметр «gap» (зазор), который можно будет использовать также и в букве ‘А’.
И наконец, есть еще «тощая» буква ‘Г («hungry I») (рис. 18е), которую я на самом деле не вполне понимаю. Возможно, я смог бы понять ее лучше, если бы попытался реализовать ее как элемент меташрифта, но все равно мне пришлось бы вначале обратиться к Самнеру за дополнительными разъяснениями. Тогда мой METAFONT смог бы отразить истинные намерения автора рассматриваемых букв.
Заглядывая вперед, я не могу поделиться сколько-нибудь убедительными соображениями относительно того, как на самом деле будут разрабатываться шрифты, к примеру, в 2000 г. Я надеюсь, конечно, что ни один из компьютерных методов, используемых нами сейчас, не будет уже применяться. К тому времени мы как раз начнем понимать данный предмет и на основе этого понимания появится множество идей, более эффективных, чем те, которые мы употребляем в настоящее время. Но меня не покидает предчувствие, что МЕТА- ЕОЫТ’овские понятия перьев, параметров и программ найдут в этом мире свое место как часть того, что имеет ценность в области применения компьютеров для разработки цифровых шрифтов.
Приложение
[Я не смог устоять против того, чтобы не поэкспериментировать с лого ATYPI. Надеюсь, что приводимый ниже подробный пример, разработанный уже после лекции, поможет прояснить некоторые из моментов, затрагивавшихся в этой лекции.]
METAFONT может имитировать широкое перо и процесс рисования им, если положение пера задавать тремя точками: левой границей, серединой и правой границей. Средняя точка пера делит расстояние между его крайними точками ровно пополам. В существующей версии METAFONT’a удобно обозначать номера точек следующим образом: средние точки нумеруются подряд, начиная с 1, для левой граничной точки номер получается прибавлением 100, а для правой —200 к номеру средней точки; тогда тройка точек (101,1,201) соответствует перу в положении 1. [В новой версии METAFONT’a я собираюсь вместо приведенной выше записи задавать такую (IL, 1,1R).]
Несложно написать METAFONT’oвcкyю подпрограмму, которая будет рисовать простой штрих такими перьями, обеспечивая возможность получения штрихов переменной ширины. Например, рис. 19 иллюстрирует работу подпрограммы, которой я занимаюсь в настоящее время. Даны две позиции пера — они обозначаются в рассматриваемом случае как (101,1,201) и (102,2,202) — вместе с тремя коэффициентами А, р и а; коэффициенты Аир описывают величину сужения штриха слева и справа, тогда как а задает позицию максимального сужения. Соответствующий штрих рисуется следующим образом. Вначале компьютер конструирует точки (all, al, а21), которые представляют собой значение коэффициента а траектории, проходящей из позиции
Рис. 19.Рис. 19.
Рис. 19.
Рис. 19.
(101,1, 201) в позицию (102, 2,202). [На рис. 19, например, сх равно 0.4; таким образом, прямая линия, проводимая из точки 101 в точку 102, пройдет через all, а расстояние между точками all и 101 составит 0.4 от расстояния между точками 102 и 101. Три точки (all, al, а21), находимые таким способом, будут лежать на одной прямой.] Затем компьютер находит точку а101, перемещаясь на А по траектории из точки all в точку al, а также точку а201, перемещаясь на р из а21 в al; эти операции определяют степень сужения штриха. Наконец, находят контур штриха. Делается это следующим образом. Кривая начинается в точке 101 и продолжается в направлении точки al; она проходит через точку al01 в тот момент, когда она идет в направлении, параллельном прямой линии, соединяющей точки 101 и 102; затем она завершается в точке 102, как если бы она пришла из точки al. Таким образом, сформирована левая граница контура штриха, правая же строится совершенно аналогично.
Изменения ширины и углы в концевых точках, а также варьируя значения коэффициентов А, р и а, можно построить самые разнообразные по форме штрихи. И можно научиться использовать эти штрихи, не вникая особенно в детали геометрических конструкций и построений, лежащих в основе процесса их формирования. Вполне очевидно, что можно разработать и более изощренные подпрограммы формирования штрихов, но в данный момент я знакомлюсь с простой их (штрихов) разновидностью типа рассмотренных выше. В частности, я обнаружил, что не так сложно получить хорошую аппроксимацию для буквы ‘А’, изображенной Самнером. Это можно сделать с помощью всего трех меташтрихов, даже когда все параметризовано, так что эти конструкции сохраняют работоспособность в весьма широком диапазоне условий.
На рис. 20 показана конструкция мета-А, к которой я пришел. Она была построена МЕТАРОЫТ’овской программой, суть которой можно изложить следующим образом: «Литера должна быть шириной 13 единиц; ее высота в 1.1 раза больше, чем высота прописных букв данного шрифта, глубина ее равна нулю. Позиция 1 пера находится на линии шрифта, его левая граничная точка на 0.5 единицы левее литеры в целом. В позиции 4 перо находится на линии шрифта, а его правая граничная точка расположена на 0.5 единицы правее
литеры в целом. Позиция 2 пера лежит на высоте в 1.1 раза большей, чем высота прописных и в средней по горизонтали точке литеры. Позиция 3 пера лежит на высоте прописных на прямой линии между точками 2 и 4. Ширина пера в позиции 1 имеет значение «узкая» («тонкое перо»), в позициях 2 и 4—«широкая» («толстое перо»), а в позиции 3 она равняется ширине тонкого пера плюс 2/3 разности между шириной толстого и тонкого пера. Угол установки (наклона) пера в позициях 3 и 4 на 15 градусов больше, чем нормальное значение параметра «cut angle» (граничный угол установки пера) для вертикальной ножки литеры, а угол в позиции 2 превышает значение «cut angle» на 30 градусов. Линия перекладины для рассматриваемой буквы определяется позициями пера 5 и 6, их верхняя часть расположена на высоте в 3/7 от высоты прописных; угол наклона пера в позиции 5 составляет 45°, а в позиции 6 — 135°; ширина пера в обоих этих случаях есть некоторая доля от ширины «тонкого» пера, определяемая значением параметра «aspect ratio» («относительное удлинение»). Позиция 5 смещена влево от точки, где прямая, проходящая через точки 5 и 6, пересекается с прямой, проходящей через точки 3 и 1; величина требуемого смещения задается значением параметра «gap amount» («величина зазора») плюс половина ширины тонкого пера. Аналогично этому, позиция 6 смещена относительно точки пересечения прямой, проходящей через точки 5 и 6, с прямой, проведенной через точки 2 и 4. Величина смещения в данном случае равняется заданной величине зазора плюс половина ширины толстого пера. Пусть т —значение параметра сужения. Диагональный штрих между позициями 2 и 4 рисуется при А = т2, р^ т и а = 0.45; диагональный штрих между позициями 3 и 1 —при А = т1/2, р = т3^ и а = 0.6. Горизонтальный штрих (перекладина буквы) сужения не имеет».
Чтобы завершить рассматриваемую спецификацию, надо определить значения входящих в нее параметров. Рисунок 20 был получен для единичной ширины 26ж (где х — произвольный масштабный множитель); высота прописных
ААААА
АААА АААА
здесь была принята равной 245а;; ширина тонкого и толстого пера составляла 22а; и 44а; соответственно. Относительное удлинение было принято равным 0.85; угол установки пера в конце штриха равнялся 15 градусам, величина зазора была принята равной одной единице, параметр сужения т = 0.4.
На рис. 21 показаны пять букв ‘А’, нарисованных для одних и тех же значений всех параметров, за исключением единичной ширины, которая здесь равна 17а;, 20а;, 23а;, 26а; и 29а; соответственно. Рисунок 22 демонстрирует влияние роста жирности: здесь пара значений («ширина тонкого пера», «ширина толстого пера») составляет (thin, thick} = (11а;, 33а;), (22а;, 44а;), (33а;, 55а;) и (44а;, 66а;). Наконец, из рис. 23 видно влияние варьирования некоторыми другими параметрами: (а) ширина вертикального штриха (55а;, 55а;); (Ь) увеличение значения параметра сужения до 0.6; (с) уменьшение угла установки пера в конце штриха до 5° и величины зазора до 0.1 единицы; (d) одновременное действие всех изменений из п. (а), (Ь) и (с). Я сомневаюсь, конечно, что Самнер одобрил бы результаты, полученные в этих примерах путем экстраполяции единственного рисунка. Но я думаю, что вместе мы могли бы получить и что-то более подходящее.
Так как этот материал — приложение к основному тексту статьи, я хотел бы завершить его, приведя тексты реальных METAFONT’obckhx программ для пользы тех читателей, кто захочет разобраться в деталях. Я использовал в этих программах следующие обозначения: hh — высота прописных в пикселях; phh — высота прописных в типографских пунктах; г —правый контур литеры; и — единичная ширина в пикселях; charbegin(character-code, unit-width, heightJn.points, depth Jn.points) есть подпрограмма, устанавливающая значения параметров вроде г и и и сообщающая, куда поместить полученный результат в завершенный шрифт. Вторая половина программы, следующая после строки "The letter А", содержит то, что в словесной форме было описано выше как способ построения буквы. Программы, эквивалентные этим по решаемым задачам, в новой версии METAFONT’a, которая появится в следующем году, будут много проще и более удобочитаемы.
minvr 0; minvs 0;fill = 1;subroutine penpos(index г, var angle, var d):Xi = .5[Zi+i00, а?г+2оо]; yi =minvr 0; minvs 0;fill = 1;subroutine penpos(index г, var angle, var d):Xi = .5[Zi+i00, а?г+2оо]; yi =% отключить коррекцию по скорости % ширина пера для заполнения штрихов
minvr 0; minvs 0;
fill = 1;
subroutine penpos(index г, var angle, var d):
Xi = .5[Zi+i00, а?г+2оо]; yi =
minvr 0; minvs 0;
fill = 1;
subroutine penpos(index г, var angle, var d):
Xi = .5[Zi+i00, а?г+2оо]; yi =
Ti-1-200 ^г4-юо d * cosd angle, З/г+200 - З/г+юо = d • sind angle.Ti-1-200 ^г4-юо d * cosd angle, З/г+200 - З/г+юо = d • sind angle.% задать положение пера г % с данным углом установки и шириной .5[7/г+1ОО) 2/г+2Оо]
Ti-1-200 ^г4-юо d * cosd angle, З/г+200 - З/г+юо = d • sind angle.
Ti-1-200 ^г4-юо d * cosd angle, З/г+200 - З/г+юо = d • sind angle.
subroutine stroke (index г, index j,% нарисовать штрих от г до j
var lambda, var rho, % с заданным сужением по левому и правому контуру var alpha);% и положением максимального сужения
а?1 = alpha\xi,Xjy yi = alpha^i, УзУ
хц = а^рЬа^г+юо, ^j+ioo]; ^21 = alpha[a?i4-2oo, ^j+200];
уи = а^Ьа^Уг-^юотУз-^юоУ 2/21 = аАрЬа^у^оо^Уз^ооУ
trioi = Jambda^ii,а?1]; уип = lambda\yn,yiy
Х201 = rho[x2i,xi]; 3/201 = rlio[3/2i,3/1];
cpen; fill ddraw
г 4- 100 {ti — Тг+100, У1 — з/г+юо}
. . 101 {Tj + 100 — Жг+ЮО, ?/j+100 — 2/г+Юо}
• j + 100 {Tj + 100 — ^1,7/^4-100 — 2/1} 3
г + 200 {Т1 — Тг4-200,7/1 — 7/г4-2Оо}
. . 201 {Tj4-200 — а?г4-200, T/j4-200 — 2/г4-2О()}
• j 4- 200 {Tj-|-200 — ^1, T/j+200 — 7/1} .
"The letter A";
call charbegin('A, 13, l.lphh, 0);
yi = 0; я 101 = .5it;
7/4 — 0; X204 = r — .5u;
У2 = l.lhh; X2 = .5r;
7/3 = hh; new aa; 7/3 = аа[т/2,т/4]; а?з = aa[z2,£4];
call penpos(l, —cut — 45, thin);
call penpos(2, cut 4- 25, thick);
call penpos(3, cut 4-15, 2/з[Шп, thick]);
call penpos(4, cut 4-15, thick);
7/205 = 7/206 = ^hh;
call penpos(5,45, aspect • thin);
call penpos(6,135, aspect • thin);
new aa; 7/5 = aa[3/1, т/з]; X5 4- gap • и 4- .5thin = aa[zi, Z3];
new aa; т/о = аа[т/2,2/4]; xq 4- gap • и 4- .5thick = aa[x2, £4];
call ' a stroke (2,4, tau • tan, tau, .45);% правая диагональ
call 'b stroke(3,1, sqrt tau, tau • sqrt tau, .6);% левая диагональ
fill ddraw 105 .. 106, 205 .. 206.% горизонтальный штрих
Для точки 203 придется смириться с тем, что для некоторых значений параметров вертикальный штрих будет слегка выпирать (как это имеет место для некоторых примеров с рис. 22 и 23). Из текста программы видно, что она после проведенной модификации рисует сначала левый диагональный элемент, затем задается
rpen#; thick draw 2 .. 4;
вследствие чего стирается все справа от прямой линии, проходящей через точки 2 и 4. После этого рисуется правая диагональ и горизонтальный штрих.
Это исследование было поддержано частично Национальным научным фондом США (National Science Foundation) по гранту IST-820-1926, а также Фондом развития систем (System Development Foundation).
Добавление
Читатели, интересующиеся развитием этого примера применительно к существующей версии METAFONT’a (она именовалась в 1983 г. как «METAFONTследующего года») могут воспользоваться таким вариантом рассмотренной программы:
% Буква А Самнера Стоуна для лого ATypI, % вырезанная Доном Кнутом при помощи METAFONT84 mode_setup;
it# := 26/збР^;% базисная единица
cap-height* := 245/збР^;% высота прописной литеры
thin* <= 22/збР^’,% жирность тонких штрихов
thick* := 44/збР^;% жирность толстых штрихов
tan := 0.4;% типичная величина сужения
дар := 1;% расстояние между штрихами
cut := 15;% степень поворота контура штриха
aspect := 0.85;% отношение жирностей вертикальная / горизонтальная
define_pixels (it);
define_blacker_pixels(Z/izn, thick);
vardef penpos @#(expr angle, d) =% задать положение пера
z@# = •5[^@#t, z@#r]; z@#r — z@#t = (d, 0) rotated angle;
enddef ;
vardef stroke (suffix $, $$, @)(expr lambda, rho, alpha) =% штрих с сужением
z© = alpha\z$, z$$]; z@i = alpha[z$i, z$$J; z@r = alpha\z%T, z$$rp,
z^v = lambda\z@i, z©\; z^» = rho\z@T, z^Y% задать для сужения
labels(@, @Z, @r, Ш'^т);
4l {^© — ^$t} • • ^©Г {^$$t - ^$t} • • ^Ш {^$$t - ^© } - ‘
^$r{^@ "" ^$$r} • • ^©rz {^$r — ^$$r} • • ^$r{^$r — ^©} ** cycle enddef;
"The letter A";
beginchar("A", 13it#, 1.1 cap-height*, 0);
penpos ^—cut — 45, thin);
penpos 2(cut 4- 25, thick);
penpos 3(cut 4-15, ^j^thin, thick]);
penpos ^cut 4-15, thick);
penpos5(45, aspect * thin);
penpos6(135, aspect * thin);
pi = 0; хц = .5it;
У4 = 0; X4r = w — .5it;
p2 = l.lh; X2 = .5w;
т/з = h; 2:3 = whatever^, Z4\;
уы = ^hh\ ^5 + (gap * 71 4- .bthin, 0) = whatever [zi, 2:3];
у6т = 3/7/г; ^6 + (дар * 71 4- .Sthick, 0) = whatever \z2, 2:4];
fill stroke (3,1,6, sqrt tau, tau * sqrt tau, .6);% левая диагональ
unfill 22 -- 24 -- (24 + (thick, 0)) -- (22 + (thick, 0)) - - cycle;% стереть лишнее
cullit;% нормализовать после стирания
fill stroke (2,4, a, tau * tau, tau, .45);% правая диагональ
fill zu - - zu - - ZQr - - ^5r - - cycle;% горизонтальный штрих
penlabels (range 1 thru 6); endchar;
end.
Литература
Thomas B. Hickey and Georgia К. M. Tobin, The Book of Chels (Dublin, Ohio: 1982), privately printed.
John D. Hobby and Gu Guoan, “Using METAFONT to design Chinese characters,” Computer Processing of Chinese and Oriental Languages 1 (July 1983), 4-23. Предварительная версия опубликована в трудах The 1982 International Conference of the Chinese-Language Computer Society (September 1982), 18-36.
John D. Hobby and Gu Guoan, “A Chinese meta-font,” TUGboat 5 (1984), 119-136. Предварительная версия опубликована в трудах 1СТР’83, the 1983 Internal Conference on Text Processing with a Large Character Set(Tokyo: 17-19 October 1983), 62-67.
Donald E. Knuth, “METAFONT, a system for alphabet design,” Stanford Artificial Intelligence Memo AIM-332 (September 1979). Reprinted as part 3 of T£X and METAFONT: New Directions in Typesetting (Bedford, Massachusetts: Digital Press and American Mathematical Society, 1979).
Donald E. Knuth, Seminumerical Algorithms, Volume 2 of The Art of Computer Programming, second edition (Reading, Massachusetts: Addison- Wesley, 1981). [Имеется русский перевод первого издания: Кнут Д. Искусство программирования для ЭВМ. Т. 2. Получисленные алгоритмы.— М.: Мир, 1977; второго издания: Кнут Д. Е. Искусство программирования. Т. 2. Получисленные алгоритмы. — Киев: Диалектика, 1999]
Donald Е. Knuth, “The concept of a meta-font,” Visible Language 18 (1982), 3-27. [Переработанный вариант представлен в качестве гл. 15 настоящего издания.]
Raymond Smullyan, “Miscellanea: Metagame,” American Mathematical Monthly 90 (1983), 390.
Sumner Stone, “The ATypI logotype: A digital design process,” presented at Fifth ATypI Working Seminar, Stanford, California (August 1983).
AMS Euler — новый математический шрифт
[Написано в соавторстве с Германом Цапфом. Первоначально опубликовано в Scholarly Publishing 20 (1989), 131-157.]
Сотрудничество ученых и художников способствует дальнейшему проникновению красоты на страницы математических журналов и учебников.
Печатать математические тексты стало быстрее, проще и дешевле, чем когда- либо ранее благодаря последним технологическим разработкам. Страницы теперь состоят из миллионов крошечных чернильных точек, расположением которых заведует компьютер. Системы вроде T£X’a [14] можно использовать для указания, где на странице должны располагаться буквы и символы; сопутствующие системы вроде METAFONT’a [16]—для задания чернильных точек, эти буквы и символы формирующих. Т^Х и METAFONT, завершенные в 1986 г., уже используются десятками миллионов людей на более чем ста различных типах компьютеров, от персональных (PC и Мас) до гигантских CRAY-машин. Эти системы спроектированы таким образом, чтобы результат их работы для всех видов процессоров и всех цифровых устройств вывода был одинаков, хотя качество, естественно, зависит от качества оборудования, используемого для печати. Более того, Т^Х и METAFONT спроектированы как полностью сохраняющие архивность, в том смысле, что рукописи, сегодня записанные в электронном виде в качестве файлов с текстом, и через несколько поколений, можно надеяться, будут давать все тот же результат на печати.
Эти тенденции можно было проследить еще десять лет назад [12], но ушло довольно много времени на то, чтобы (в самом прямом смысле) расставить все точки над i, необходимые для получения достаточно гибких и совершенных систем. Американское математическое общество (American Mathematical Society — AMS), одно из крупнейших в мире издательств математической литературы, в 1979 г. образовало комитет по шрифтам для планирования того, как можно использовать преимущества появляющихся технологий. Первоначально в этом комитете состояли: Ричард Пале (председатель), профессор математики в университете Бранде; Барбара Битон, редактор из штаб-квартиры AMS;
Петер Ренц, редактор математической литературы из W. Н. Freeman & Со.; и два автора данной статьи (ДЭК и ГЦ).
В то время ДЭК и ГЦ знали друг друга только по публикациям. Втайне мы оба желали встретиться, но, зная, как сильно занят другой, никто из нас не решался предложить. К счастью, в роли свахи выступило AMS, положив начало плодотворному сотрудничеству между математиком и художником, которое, как мы надеемся, окажет благотворное воздействие на научную прессу. Нашей целью в этой статье было зафиксировать на бумаге некоторые аспекты нашего сотрудничества, которые мы считаем заслуживающими особого внимания, потому что они освещают вопросы публикации математических текстов, редко обсуждаемые в печати.
• По-видимому, лучше всего будет рассказать эту историю, приводя выдержки из писем, которые мы в то время писали друг другу и получали от других людей. (Полностью эта переписка со всеми сопутствующими рисунками хранится в архиве Станфордского университета, коллекция SC 362.)
Ричард Пале замечательно описал первоначальные цели нашей работы в первом пригласительном письме:
11 сентября 1979 г.
ГЦ от Р. Пале
Дорогой профессор Цапф!
[Вступительные замечания ... ] Поэтому A MS теперь имеет возможность создать гарнитуру, содержащую исчерпывающий набор взаимосвязанных алфавитных и символьных шрифтов, сделанную на основе ТуХ’а и предназначенную не только для публикации журналов и книг AMS, но и для всего математического и научного сообщества в целом. Очевидно, что это проект, рассчитанный на долгий срок, и для его осуществления потребуются планирование, аккуратность и советы лучших специалистов.
Примерно в это же время ДЭК написал ГЦ, приглашая его заехать в Станфорд в феврале 1980 г., для того чтобы ознакомиться с METAFONT’om. Зародыш будущего проекта уже начал оформляться:
7 октября 1979 г.
ДЭК от ГЦ
Дорогой д-р Кнут!'
... нужно тщательно разработать, используя МЕТА FONT, основные черты стандартного научного алфавита, нейтрального по форме и хорошо подходящего для всех видов печатающих устройств: и для коммерческой офсетной печати, и для печати на офисном оборудовании низкого качества, и для последующего ксерокопирования с отпечатанного оригинала и т. д., и т. д.
... Я бы предпочел, чтобы базовый научный алфавит был прямым, потому что в этом случае возникает не так много проблем, как с наклонными формами. Ведь в результате мы хотим получить по-настоящему хороший алфавит с возможностью использовать самые разные специальные символы для всевозможных научных публикаций. Алфавит со всеми необходимыми символами и дополнительными формами, которые бы вместе идеально вписывались в общий замысел.
25 октября 1979 г.
ГЦ от ДЭК
Дорогой проф. Цапф!
... я должен пояснить, что книгоиздание не главное мое занятие; в первую очередь я — преподаватель, проводящий собственные исследования и руководящий исследованиями в computer science и математике, а еще я пишу книги, пытаясь внести некоторое единообразие в эти области. Между делом, решая проблему, связанную с изданием этих книг, я, кажется, натолкнулся на некоторые идеи, представляющие ценность для печатной индустрии, поэтому я хочу убедиться, что правильно разъяснил эти идеи и выпустил их в жизнь. Если в этих идеях есть зерно истины, то они пробьют себе дорогу и без моей помощи; если же нет, они так или иначе канут в Лету.
... Теперь что касается семейства шрифтов для A MS. В настоящее время они используют прямое светлое начертание шрифта Times New для текста, курсивное начертание Times—для формул и выделенных фрагментов. Использование курсивного Times для формул по моему мнению (и я не одинок!) очень неудачно, потому что формулы становятся перенасыщенными; собственно, единственная когда-либо существовавшая причина для использования курсивного Times состоит в том, что это был модный шрифт для работы с текстом когда AMS меняло наборщиков.
Естественно будет поступить следующим образом: взять существующий текст и курсивное начертание для текста и выделенного текста и создать подходящее новое начертание для математических формул, встречающихся в тексте. Например, начать можно с прямого светлого и курсивного Times; но, конечно, мы должны использовать и что-то из Zapf Book, или Optima, или какого-нибудь другого замечательного шрифта из тех, что вы подарили миру.
Новый математический шрифт должен быть легко отличим по начертанию от обычного текста, притом на подсознательном уровне; далее, вместе с латинскими буквами и цифрами, он должен содержать греческий, готический и рукописный алфавиты. Разные буквы и символы должны быть легко отличимы (‘а’ — от альфа, ‘v’ — от ню, ноль — от О и т. д.), но не обязательно различать греческие буквы и точно соответствующие им латинские (прописные А, В, Е, Н, I, К, О, Р, Т, U, X, Z; строчная о и, возможно, и).
Ваша идея создать это начертание без наклона очень интересна, и мне кажется, эта идея хороша, даже несмотря на давнюю традицию использования наклонных символов, — в особенности, из-за того что некоторые математики любят нагромождать акценты, например а.
Должен ли новый шрифт отличаться в первую очередь наличием или отсутствием засечек? Или насыщенностью? Традиционно буквы в формулах несколько светлее, чем в тексте; например, в математическом курсивном начертании Computer Modern основные штрихи букв примерно в 3Д раза тоньше, чем в прямом светлом и наклонном светлом текстовых шрифтах. Полужирные символы для математиков имеют особое значение, так что я думаю, лучше всего сохранить эту традицию и оставить нежирные символы чуть светлее, чем в тексте.
Еще одна характеристика, которая может оказаться важной при продумывании внешнего вида, такова: математики воспринимают формулы, как нечто, что они пишут на доске или на листе бумаги, в то время как текст — это что-то напечатанное. Поэтому, возможно, разница между текстом и математической
частью должна состоять в том, что текст более механичен, а математическая часть — более каллиграфична.
Внешний вид должен быть психологически приемлемым для математиков с первого взгляда, если это возможно; математическое значение в идеале будет восприниматься без участия сознания. Таким образом, новый шрифт должен быть в согласии с исторически сложившимися традициями даже тогда, когда создается нечто абсолютно новое. ...
Р. S. Еще одна мысль: некоторые математические формулы кажутся мне некрасивыми просто по природе своей, независимо от того как их напечатать! Нам надо выбирать тестовые примеры из хорошо отредактированных книг.
Насколько известно авторам, подобные попытки создания набора взаимосвязанных шрифтов и математических символов предпринимались всего лишь дважды. Почтенная словолитня Job. Enschede en Zonen в Харлеме, Голландия, в конце 1920-х годов попросила выполнить эту работу Яна ван Кримпена. Профессор X. А. Лоренц, нобелевский лауреат по физике (1902), жил в Харлеме и согласился участвовать в проекте. Но Лоренц умер в 1928 г., а «работы оказалось даже больше, чем предполагалось, да и сложность ее была недооценена» [4]; «вследствие этого проект был заброшен» [23, с. 32]. Единственный результат этого проекта —греческий шрифт, названный Antigone, который, по словам ван Кримпена, был создан им быстро по образцу греческих текстовых шрифтов [23; с. 35]. Начертание Antigone вертикально, в отличие от наклонных греческих шрифтов, традиционно используемых в математике; так что и он, оказывается, намеревался сделать научный алфавит выпрямленным.
Американским математическим обществом в 1962 г. была предпринята не столь бесплодная попытка. Здесь было решено сделать все математические алфавиты наклонными для соответствия курсивному Times. Это оказалось не очень удачным в случае готического шрифта, потому что «к прискорбию, многие готические буквы, будучи наклоненными на 18°, теряли свои достоинство и вес» [20]. Прописной рукописный шрифт AMS, созданный в ходе этого проекта, заслуживает особого упоминания, потому что это первый рукописный шрифт, предназначенный именно для математики. Трудность состояла в том, чтобы создать нечто, «определенно являющееся рукописным алфавитом, но с минимумом украшений» [20], потому что вычурные завитушки рукописных шрифтов традиционного начертания имеют привычку исчезать при малом кегле, в частности в нижних и верхних индексах.
ГЦ провел две недели в Станфорде в гостях у ДЭК в феврале 1980 года, две чрезвычайно захватывающие недели для нас обоих. Мы с наслаждением наблюдали, как легко люди «двух культур» (по выражению Ч. П. Сноу) могут работать вместе; и мы действительно интенсивно работали все это время. Мы изучали классический труд по набору математических текстов [2], заметив, что даже Oxford University Press не имело шрифтов, в которых все необходимые символы легко различимы при малом кегле. Большая часть нашего времени
Первый опыт сотрудничества между ДЭК (сидит) и ГЦ (стоит), 14 февраля 1980 г. [Фотография Stanford News Service сделана Чаком Пейнтером. ]
была посвящена объяснениям ДЭК по поводу METAFONT’a и ГЦ —о создании шрифтов и каллиграфии. ГЦ объяснял, как добиться специальных эффектов при помощи изменения «нажима пера», а ДЭК находил способ моделировать такие эффекты на компьютере. У нас осталось совсем немного времени на то, чтобы приступить к созданию нового шрифта, но нам все-таки удалось сделать на пробу несколько символов. Вот копия того единственного пробного оттиска, который мы успели сделать на фотонаборной машине:
Consider the formula 66Е — 77g.
Also consider the formula 66E — 77g > aJC.
(Конечно, эта рукописная X нам не понравилась; мы работали над ней всего лишь несколько минут в конце дня.)
24 февраля 1980 г.
ДЭК от ГЦ
Дорогой Дон!
Я очень ценю оказанный мне сердечный прием. ... Отдельным письмом я посылаю два плаката на тот случай, если у Вас еще осталось свободное место на стене. Цитаты из Оппенгеймера [22, с. 119; 25, с. 220; 26, с. 133] точно описывают мои собственные мысли об искусстве и науке: левая часть кругов (цветная) изображает мир художника, правая часть (из одних линий) — мир ученого. Вообще-то
(как показано во внешнем круге) они соединены в людях, но кроме этого везде и всегда они пытаются прийти к согласию, как к недостижимому идеалу.
Будем считать эти две недели в Станфорде началом.
17 марта 1980 г.
ГЦ от ДЭК
Дорогой Герман!
... сейчас мне кажется, что подходящим именем для шрифта будет «Euler»; или же мы можем выбрать скучный безличный стиль и назвать его «AMS Mathematics».
На случай если «Euler» встретит одобрение, я попытался найти образцы его почерка ...
Название Euler было предложено во время заседания комитета AMS по шрифтам (AMS Font Committee) 23 февраля, дело в том что швейцарский математик Леонард Эйлер (1707-1783) был одним из величайших и популярнейших математиков всех времен [9]. Эйлер несомненно оценил бы достижения современной технологии, потому что печатные станки того времени не справлялись с его огромной продуктивностью. (На самом деле Петербургская академия наук Продолжала регулярно публиковать рукописи Эйлера в течение более чем тридцати лет после его смерти.)
Леонард Эйлер был тем, кто впервые использовал многие алфавиты, которые теперь можно часто встретить в математических изданиях. Джон Валлис в формулах своей «Алгебры» [24] иногда одновременно использовал греческие и курсивные буквы, но Эйлер развил эту идею гораздо дальше и использовал также готический алфавит. Вот, к примеру, выдержка из его статьи, опубликованной в 1765 г. [6, §33]:
Ind. v , а , b
ГгаЛ. g , а , ь~, <•vbt $l=v,5B=«5l-hi;€=^234-5(et а — I, b =s 0-b О; С —^ Ь н-О
В учебнике Эйлера по интегральному исчислению (1769) имеются формулы, объединяющие прямой светлый и курсивный, готический и греческий алфавиты одновременно [7, §1130]:
-fxcof^ ^-t-^x^xx)co£.^^fе 6((а + ЬДГН-СЛ^Гш/^^^^
Если бы он использовал в своих публикациях кириллические буквы, современные математики, возможно, знали бы кириллицу не хуже греческого алфавита!
Но иногда Эйлер прибегал к новшествам в обозначениях, не пережившим его. Вот формула, в которой он объединил астрономические символы Солнца, Сатурна, Юпитера и Марса [8, §6]:
0= 2^4-2^ + ^ = ^
К марту 1980 г. ДЭК и ГЦ решили, что новый шрифт должен иметь привкус «рукописности». Поэтому был шанс, что образцы почерка самого Эйлера могут подсказать некоторые особенности будущего шрифта. И действительно, обнаружилось, что Эйлер часто делал верхнюю часть нуля заостренной, а не круглой [3]:
::аа,в — ей^/г^- ^^^ now, ^^ s-.d. е*^hstczObuiyoi,^^^^
Однако это — распространенная особенность рукописных текстов вообще, и не оказало прямого влияния на создание AMS Euler; ГЦ уже экспериментировал с заостренными нулями еще до того, как увидел записи Эйлера —после просмотра математических рукописей Эйнштейна, Ньютона, Рамануджана и ДЭК. Эти заострения «заостряют» внимание на нулях, с тем чтобы естественным образом отличать их от букв О.
20 марта 1980 г.
ДЭК от ГЦ
Дорогой Дон!
Я прилагаю несколько набросков разных знаков и символов вместе с вариантами для некоторых из них, чтобы узнать Ваше мнение и выслушать Ваши замечания перед тем, как сделать наброски для AMS.
Е-тпс2х'-^М-осх gfAv+pv)f (Ap+|j.q) =A2p2lp+2A|i2-a,0
SLDFMXDaTcsgssvwxyz.Ty=0.577216 (A+^)
[Эти рисунки уменьшены на 50% относительно оригинала. Было приложено еще много набросков, не приведенных здесь.]
4 апреля 1980 г.
ГЦ от ДЭК
Дорогой Герман!
Образцы, посланные Вами, содержат много изумительных находок, и я с большим энтузиазмом смотрю на перспективы этого проекта. Но, чтобы письмо не стало слишком объемным, я ограничусь замечаниями по тем вопросам и вещам, которые мне не понравились, вместо того чтобы восклицать о том, что очень мне понравилось; из-за этого письмо может показаться чересчур негативным по настроению. Я заранее приношу свои извинения за это и надеюсь, что Вы сможете восстановить правильное соотношение, если вообразите, что я написал намного более длинное письмо, в котором горячо хвалю все, не упомянутое ниже. ...
/ Хвостики на окончаниях строчных латинских букв должны быть более округлыми, чтобы они были свободнее и полностью отличались от готических букв. Пусть резкий изгиб будет «торговой маркой» готического шрифта, а латинские буквы пусть текут плавными линиями.
/ В рукописной математической записи я предпочитаю ‘у’ с округлым полуовалом, роднящим ее скорее с ‘и’, чем с V). • •.
/ Мне не понравилась рукописная К, но я не вполне понимаю, как заставить ее не выглядеть смешной и в то же время сделать совершенно отличной от рукописных Н и X. Я прошу Вас приложить все усилия к нахождению красивой К, ведь для меня эта буква очень важна (как Z для Вас)! ...
/ Каплевидный нуль очень удачен, и я действительно буду рад увидеть его в этом шрифте. ...
/ Левый верхний штрих 5 должен быть строго вертикальным, а не чуть-чуть наклонным, как у Вас. В противном случае это будет ужасно выглядеть при оцифровке с использованием грубого растра, а у многих в лабораториях стоит именно такой.
Были необходимы семь взаимосвязанных алфавитов: строчный прямой, прописной прямой, прописной рукописный, строчный греческий, прописной греческий, строчный готический и прописной готический. (Рукописные алфавиты как разновидность курсива были введены бельгийским пуансонистом Жак-Франсуа Розаром) в 1753 г. [5]; но во времена Эйлера они, видимо, еще не дошли до Берлина и Санкт-Петербурга, потому что Эйлер их никогда не использовал. Математики взяли прописные рукописные буквы на вооружение много позже, в XX столетии. Строчные рукописные буквы в математике широко не использовались никогда, за исключением буквы Т.)
23 апреля 1980 года
ДЭК от ГЦ
Дорогой Дон!
Позвольте мне особо поблагодарить Вас за труд, вложенный Вами в Ваши замечания; это отняло у Вас много свободного времени. Именно так я и хотел бы работать, когда дело касается настолько важного нового шрифта. Не стоит думать, что я буду расстраиваться из-за искренней и конструктивной критики или замечаний, касающихся специальных или сложных символов.
К письму прилагаются те предложения, которые д-р ле Век хотел получить к майскому заседанию совета Правления AMS.
UemPidGroMagKvy AufVtlDxhJcKM 7 Я 3 ХЛ ^ЦЯ-ПЯЛУ 5 3 ££S793C%DXV'yXW33MO2,TC 1752 04 8 63913725490 = + «-[{ ( Х^каусер^Лпшхсгфбтр 6т] £р £,-9° ПГД 0ТП ЕАФЧ^ЛОЕТФИ С,^ Sfa бре 9Hpf ^15 Тиа фгр 2B6q 3^£CZFSLNQBW1XKYHTI E=mc2
Уильям Ле Век, исполнительный директор Американского математического общества, внимательно следил за нашей деятельностью. (Он даже приехал в Станфорд в январе 1981 г., и научился создавать несколько кириллических символов при помощи METAFONT’a.)
9 мая 1980 г.
ГЦ от У. Дж. Ле Века Дорогой профессор Цапф! Я с большим удовольствием сообщаю Вам, что Правление на заседании 3 мая решило одобрить Ваши предложения.
... Я думаю, будет мудро с самого начала отдавать себе отчет в следующем. Математики весьма склонны, в целом, к эстетическому консерватизму, им нравится то, к чему они привыкли. Этот факт нужно учитывать, если Общество или другие математические издательства должны принять новый шрифт Euler. Поэтому Правление хочет быть уверенным, что весь комитет по шрифтам, и еще несколько математиков, в данный момент не входящих в состав комитета, будут иметь возможность высказывать свое отношение к Вашему проекту до того, как он будет окончательно завершен.
... Мы все с нетерпением ожидаем того момента, когда приступим к совместной работе и в результате ваших усилий, мы знаем, получим замечательную коллекцию шрифтов.
Дата: 09 мая 1980 года 0819-PDT
От: Дон Кнут <ДЭК в SU-AI>
Кому:Пале в MIT-MC
Благодарю за Ваше сообщение о результатах голосования Правления.
Что касается дальнейшего, вот мои предложения: (1) Вы посылаете мне письмо, во всех деталях описывающее все критические замечания комитета на данный момент, чтобы я мог передать их Цапфу. (2) Вы пишете Цапфу короткую записку, в которой сообщаете, что рекомендации комитета были отправлены мне и я вскоре с ним свяжусь. (3) Когда я напишу ему, он сделает наброски в размере, нужном нам для разработок в METAFONT’e. Я их отдам Скотту Киму, он согласился заниматься МЕТАРОПТингом в качестве подготовки к диссертации. Периодически я буду пересекаться со Скоттом и наблюдать за тем, что он делает. (4) Как только мы со Скоттом что-то сделаем, мы пошлем результаты Цапфу, а также любому из AMS, кто хочет участвовать в обсуждении. Этот заключительный этап редактирования может проходить в несколько
итераций, но мы сможем распечатывать много образцов и легко вносить изменения, поскольку все хранится в виде METAFONT’a.
26 мая 1980 г.
ДЭК от ГЦ
Дорогой Дон!
... Возможно, у членов комитета по шрифтам возникнут разногласия по поводу вида некоторых специальных символов. Я бы предложил разрешать эти разногласия до начала собственно разработки, чтобы понимать, чего мы хотим от заключительных эскизов, и избежать постоянной перерисовки. Лучше всего, если бы этим занялись Вы.
27 мая 1980 г.
ГЦ от ДЭК
Дорогой Герман!
Я был рад услышать, что Правление AMS официально решило поручить Вам работу над этим важным шрифтом. Теперь остается «всего лишь» довести проект до успешного завершения!
Благодарю за прекрасные образцы, приложенные к Вашему письму. Большинство из них в точности таковы, как нужно, и я поражен тем, как Вы справились с проблемами, казавшимися мне почти неразрешимыми, вроде рукописной Q.
Остается еще одна беспокоящая меня вещь, и мне бы хотелось еще раз вернуться к этому, потому что я действительно серьезно настроен против. (Как бы то ни было, если Вы со мной не согласны, безусловно хозяин положения Вы!) Мне кажется, начальные и конечные хвостики строчных латинских букв все еще загибаются слишком резко, и я прилагаю набросок того, как они должны выглядеть по моему мнению.
Нынешний вид:Я бы хотел сделать их более округлыми:
Это не только позволит еще легче отличать их от готического шрифта, но также создаст более плавное, текучее ощущение. Вы ухватили эту замечательную текучесть в строчных ‘g’ и ‘х’; я бы хотел, чтобы ‘ш’ и ‘у’ и т. д. вызывали то же, совершенно безыскусное ощущение.
Теперь более детальные комментарии по отдельным буквам. ... Рукописная Т’ не подойдет; буква, нарисованная Вами, прекрасно выглядит рядом с ‘J’, но в математических текстах эта ‘Г будет стоять сама по себе и лишь немногие поймут, что это за буква. ... Хвостик у строчной сигмы сейчас слишком длинен; будет очень сложно набрать формулу ‘|сг|’ так, чтобы она выглядела симметрично. ... Вопросительный знак выглядит замечательно, но в этих шрифтах он не понадобится: математики используют знаки восклицания, но не вопросительные! ... Вторые варианты строчных дзета и кси лучше первых, но основные горизонтальные штрихи внизу слишком длинны, и математики привыкли к большим хвостикам под линией шрифта.
Как нам работать дальше? Я много об этом раздумывал, а Ваши последние образцы полужирных букв прояснили один момент, о котором я должен был
подумать давным-давно. А именно, что лучший способ подготовить эскизы для METAFONT’a будет несколько отличаться от обычного стиля работы. Во-первых, Вам не обязательно делать рисунки самого превосходного качества пером и чернилами; будет достаточно карандашных набросков. Но мне совершенно необходимы два наброска для каждой буквы: один со светлым вариантом, а другой —с полужирным (на самом деле, может быть даже еще более жирным). Эта информация насущно необходима для того, чтобы объяснить METAFONT’y, как нужно варьировать набросок при изменении параметров. Я бы не смог предугадать внешний вид замечательных полужирных прописных греческих букв, присланных Вами, только по виду светлых; поэтому необходимы оба варианта.
Вам также следует делать по два таких наброска для каждой готической буквы, хотя стандартные шрифты, возможно, будут содержать только один готический алфавит — почти-но-не-совсем полужирный. Это даст нам возможность экспериментировать с различными степенями насыщенности, не меняя эскиза.
Я думаю, Вам лучше будет делать все алфавиты целиком, а не пробовать сделать несколько букв, посылать их в Калифорнию, потом делать еще несколько ит. д.; согласованность будет очень важна. Скотт Ким активно работает над завершением своей книги о «ДИЗАИНатурах» [11], и я ожидаю, что он сможет начать МЕТАЕОМТинг Вашей работы в сентябре или октябре.
Пока же, чтобы приступить к работе, мы бы хотели получить примеры набросков большого размера, скажем, для пяти символов: прописной А, строчной i, греческой сигма, цифры ноль и готической R. Пожалуйста, пришлите по два рисунка каждой: один полужирный и один светлый. Я проверю, дают ли Ваши наброски всю информацию, необходимую нам со Скоттом для подготовки установок METAFONT’a. После этого я дам Вам «добро», и Вы приступите к основной части проектирования. ... Как Вам такой план?
16 августа 1980 г.
ДЭК от ГЦ
Дорогой Дон!
... 27 июля я обедал в Бостоне с Пале и Ле Веком, но нам всем не хватало Вас.
К концу следующей недели я закончу два алфавита (рукописный латинский и греческий), и я должен буду послать их прямо в Род-Айленд, чтобы там их сфотографировали и разослали всем участникам проекта для одобрения.
Пока же Вы можете протестировать приложенные большие рисунки в Станфорде и как можно быстрее сообщить мне, годятся ли они для Скотта.
После получения от Вас комментариев и замечаний по поводу этих двух алфавитов, комментариев, соединяющих и сплавляющих воедино идеи всех наших друзей из AMS, я смогу быстро нарисовать окончательный вариант.
5 сентября 1980 г.
Получатель: комитет по шрифтам Отправитель: У. Дж. Ле Век
К письму прикладываются фотокопии первых завершенных Германом Цапфом алфавитов ... Эти начертания математических символов должны заменить традиционно используемый сейчас курсивный шрифт, например х в sin х. Вы обнаружите, что эти символы выпрямлены, это намного облегчит постановку диакритических знаков над, под или рядом с ними.
[Эти рисунки, приложенные к письму ГЦ от 16 августа 1980 года, были уменьшены до 33% от первоначального размера.]
Теперь мы ждем от вас критических замечаний и предложений, присылайте их возможно скорее. Дон Кнут посетит Провиденс 24 сентября, и было бы очень кстати, если бы вы возвратили мне приложенные листы до этого времени.
abcdefgh.ijklmnop qrstuv WTyz123456789074fj ABCDEGHIJLMNOP QR STUVWXYZKKFT а^убеСт] 0 LKA|iv^07Tpcrc, tv фхф ш 12345678 g о ф ГД0ЛЕП£ФФ£2ЛЕ"Ф
[Были разосланы увеличенные копии этих символов, снабженные идентификационными номерами от 1 (для ‘а’) до 119 (для ‘Ф’). Каждый член комитета получил анкету на пяти страницах, в которой требовалось расставить 119 оценок: Отлично/ Удовлетворительно/Неудовлетворительно/Комментарии.]
8 сентября 1980 года
ГЦ от ДЭК
Дорогой Герман!
Благодарю за Ваше письмо от 16 августа и за образцы шести букв (средней насыщенности и полужирных) для тестирования в METAFONT’e. Я немедленно отдаю их Скотту, хотя именно сейчас он завершает последние штрихи своей
книги, так что, возможно, пройдет несколько недель, прежде чем он сможет в достаточной степени сосредоточиться на тестировании, как оно того заслуживает. Нам, наверное, придется пережить горячие деньки, чтобы поспеть за Вашей продуктивностью!
Образцы алфавита, посланные Вами в AMS, сейчас циркулируют в комитете по шрифтам, и я приложу все усилия, чтобы использовать преимущества коллективной мудрости комитета, но избежать недостатков «планирования всем комитетом». Каждый член комитета независимо заполняет анкету; это даст бесценный пример вйдения математиков, особенно если большинство из них независимо приходит к одним и тем же выводам. Может также случиться, что по некоторым вопросам в комитете возникнут серьезные разногласия (кошмар!), но даже в этом случае, я думаю, их наблюдения будут очень ценны, хоть нам, наверное, и не удастся понравиться всем.
P.S. Начальные и конечные хвостики букв теперь полностью меня удовлетворяют. Огромное спасибо!
Анкеты были заполнены Уильямом Дж. Ле Веком, Ричардом Пале, Барбарой Битон, Петером Ренцем, ДЭК и новыми членами комитета: Ральфом Ф. Боасом (из Северо-западного университета), Линкольном К. Дэрстом, Фебе Мэрдоком, Эллен Свонсон, Сэмюелем Б. Видденом (все из AMS) и Уильямом Б. Вулфом (из Mathematical Reviews). Вот выдержка из типичного ответа:
16 сентября 1980 г.
У. Дж. Ле Веку от Ральфа Ф. Боаса
Дорогой Билл!
... многие, включая и меня, уже 50 лет борются за то, чтобы типографы отличали € от е. Мне казалось, что битва уже выиграна, — а теперь AMS одним росчерком пера художника может ее проиграть. Почему это важно? Потому что 1е € 5’ — вполне возможная комбинация. ... Я действительно считаю, что новый алфавит по замыслу весьма привлекателен. Тем не менее, мне кажется, что форма ‘а’ лучше согласуется с остальными буквами, чем ‘а’.
Итоги были подведены в длинном (13-страничном) послании:
9 октября 1980 г.
ГЦ от ДЭК
Дорогой Герман!
Надеюсь, Вы наслаждаетесь осенней порой. У нас только что началась учеба и появилось много замечательных новых студентов. Деревья разукрасились в разные цвета, белки собирают орешки; птички весело напевают.
Но теперь вернемся к работе! Недавно я заходил в штаб-квартиру AMS, и у нас состоялось долгое и плодотворное обсуждение шрифтов Euler. В этом письме я ставлю перед собой цель сжато изложить мнение комитета, с тем чтобы сколь возможно облегчить Вам работу. Этот комитет представляет тип математиков, заботящихся о качестве своих статей, и я знаю, что Вы цените такое внимание к деталям; поэтому про каждый символ я напишу небольшой рассказ, основываясь на их мнениях.
Общее мнение, конечно, весьма благоприятно, но мы обнаружили, что существует опасность спутать математический шрифт с обычным текстовым; большая часть комитета выразила озабоченность этой проблемой. Добавив немного больше «рукописности» или «избыточности» в форму букв, которые сейчас рисуются при помощи одних только прямых штрихов, мы бы достигли весьма серьезных улучшений. Общее настроение склоняется к тому, что лучше иметь немного менее красивые буквы, если они лучше отличимы от обычного прямого светлого начертания, даже когда нам вполне нравится, как оно выглядит.
Перехожу собственно к «коротким рассказам», они пронумерованы в соответствии с кодами на приложенной копии образцов рисунков. Но перед тем как прочесть их, заметьте, что по-моему Вам пока не стоит делать рисунки для МЕТА FONT’а, до тех пор пока мы со Скоттом не закончим работу с тестовыми символами, которые Вы для нас уже нарисовали. Сейчас просто быстро просмотрите комментарии, чтобы иметь представление о настроениях комитета; а затем, я думаю, надо подготовить образцы рукописного и готического шрифтов, аналогичные латинскому и греческому.
/ Всем нравится, как выглядит эта ‘а’, но мы действительно чувствуем, что вместо этого нужно использовать другой стиль. Именно курсивную ‘а’ математики всегда пишут на доске, и она лучше сочетается с другими буквами шрифта Euler. Я приношу свои извинения, что не упомянул об этом раньше; я просто не обратил на это внимания.
, з, 4, 5 / Они нравятся всем, и они достаточно отличаются от обычных латинских букв.
/ Здесь вопрос в том, выбрать (6) или (39). Поначалу мнения комитета о том, что выбрать, разделились поровну, потому что (6) очень изящна. Но после того, как кто-то заметил, что (6) плохо выглядит в том распространенном случае, когда после ‘f ’ идет левая скобка, голоса единодушно были отданы в пользу (39), а не (6).
/ Прекрасно, единодушное одобрение.
119/ Следует использовать (119) вместо (115), но добавить две засечки внизу, как у ‘Г (48) и ‘Ф’ (114).
Но прежде всего я хочу подчеркнуть мнение комитета, что Вы должны принимать собственные решения о том, что пойдет на пользу для замысла в целом. Наша цель — дать Вам как можно больше фактического материала о впечатлениях математиков и о том, какие моменты ощущаются особенно остро, но нашим отдельным мнениям нельзя позволить внести разброд в весь проект. Пока что общее мнение: «Все вместе —какая замечательная работа!»
22 октября 1980 г.
ДЭК от Р. Пале
Дорогой Дон!
От лица всех нас я хочу поблагодарить Вас за те усердие и тщательность, с которыми Вы подошли к сравнению п х 119 мнений о символах шрифта Цапфа. По моему мнению, Ваше письмо просто шедевр. Ле Век был весьма впечатлен. Я думаю, сначала он боялся, что Вы позволите восторжествовать Вашим собственным вкусам, но сейчас он полон энтузиазма по поводу проекта и изобретенной
Вами стратегии достижения лучшего из миров путем соединения мудрости комитета с индивидуальной творческой энергией. Если этот проект создания шрифта увенчается успехом — а я верю, что так оно и будет, — я думаю, этим он будет столь же обязан Вашему терпению и эстетическому чувству равновесия, как и замечательному вкусу и мастерству Цапфа.
25 октября 1980
ДЭК от ГЦ
Дорогой Дон!
Для меня вся история с шрифтом Euler — идеальный пример командной работы. Я согласен со всеми поправками, сделанными Вами и Вашими друзьями. И я очень рад, что новые нули были приняты.
5 декабря 1980 года
Комитету по шрифтам от У. Дж. Ле Века
Дорогие коллеги!
Прилагается лист II с рисунками Германа Цапфа, а также листы для комментариев. Дополнительно к готическому и рукописному алфавитам, он представил новые версии букв с листа I, которые потребовали серьезной переделки из-за разнообразной критики. Как и ранее, просьба присылать ваши комментарии мне.
abcbefgfjijHmnopqtstutitor
9Ла1Йф£ЯНеТЯ®2ПЖ2)ЗЯСВ CD £J93<3 91XEMXOTQRSrtlWI
avwzxz 7 Q1 VWyEiS то £ К
декабря 1980 г.
ГЦ от Р. Ф. Боаса
Дорогой профессор Цапф!
... Новая гамма привлекательна, и я не расстроюсь, если петля при воспроизведении исчезнет: в конце концов, многие греческие шрифты обходятся без петли.
Я только недавно читал русскую статью, в которой действительно используется, и не раз, е G Е (спасибо, что не е € 8), это указывает на важность различий между буквой е и символом £.
5 февраля 1981 г.
ГЦ от ДЭК
Дорогой Герман!
Готовы ли Вы к еще одному длинному письму о письменах? Снова я попытаюсь кратко изложить независимые мнения членов комитета по шрифтам. В целом люди чувствуют, что работа продвигается хорошо. Все (кроме меня) высказали озабоченность тем, что соединительные штрихи будут в малых кеглях выпадать; мне следовало объяснить им, что МЕТА FONT позаботится о соединительных штрихах и что на этой стадии нам не следует беспокоиться о таких вещах.
Перехожу к отдельным буквам по их номерам, как и раньше.
/ Было одно замечание о том, что юго-западная часть овала должна загибаться под несколько более острым углом (как в Ь), чтобы сделать букву еще больше похожей на готическую.
, з / Единодушное одобрение: браво, браво!
/ Слишком похоже на букву дельта. Я нашел готическую D в двух математических справочниках, и в обоих случаях диагональный штрих вверху шел вниз более круто (более горизонтально).
юб / Всем понравилась эта алеф; один человек поинтересовался, не слишком ли она жирна, но это мы всегда сможем подправить позднее.
Необходимо еще одно важное изменение: я со многими людьми обсуждал мое предложение сделать цифры в одном из наборов разной ширины ... Нам следует отменить наше решение и сделать все десять цифр одинаковой ширины и в выровненном, и в старинном стиле.
Итак, очередная порция комментариев завершена. Шрифты Euler очевидно приобретают все более приятные очертания, и следующим критическим шагом станет METAFONThhf.
Скотт Ким, аспирант в computer science, работающий под руководством ДЭК, вызвался закодировать чертежи Euler в системе METAFONT в качестве подготовки к собственной диссертации, которая будет затрагивать вопросы о связи между компьютерами и визуальным мышлением. Его первые попытки были вполне успешны:
17 февраля 1981 г.
ГЦ от ДЭК
Дорогой Герман!
Вот первые пять образцов букв шрифта Euler, в том виде как они были нарисованы METAFONT’om. Скотт проделал огромную работу, и притом первоклассную, разрабатывая новый стиль программирования METAFONT’a, моделирующий вращение и нажим пера, и мы постепенно учимся пользоваться этими приемами. Это непросто, но зато интересно и поучительно.
Во время подготовки этих образцов мы много раз жалели, что вас нет рядом с нами и Вы не можете нам подсказать! С другой стороны, тот факт, что мы общаемся письменно очень полезен, потому что это заставляет нас быть точнее и яснее формулировать свои мысли.
Достаточно ли приложенные буквы близки к вашим рисункам, чтобы мы могли переходить к следующей стадии? Если нет, прошу вас так честно и сказать
и прислать подробный комментарий о том, какие усовершенствования нам еще нужно освоить, прежде чем мы будем готовы приступить к настоящим буквам AMS Euler.
Пользователи METAFONT’a пишут для каждой буквы «программу», выражающую их намерения на специальном языке. Программа говорит компьютеру, как рисовать эту букву, в зависимости от набора параметров, таких как высота и ширина литеры, а также толщина различных типов штрихов. Программа использует эти параметры, чтобы определить опорные точки. Например, горизонтальный штрих на рисунке, изображенном здесь, идет от опорных точек 110-310 к опорным точкам 111-311.
Такие программы дают «меташрифт» [13], который можно использовать для создания самых разнообразных конкретных шрифтов, соответствующим образом выбирая параметры. Вот, например, шесть вариантов пяти тестовых символов, упомянутых в письме ДЭК от 17 февраля 1981 г.:
AiZOf AiZOf AiZOf AiZOf AiZOf AiZOf
Второй и пятый примеры соответствуют рисункам ГЦ от 16 августа 1980 г.; средние два получены интерполяцией; крайние два — экстраполяцией (что чревато большими неприятностями).
Меташрифт позволяет создателю исследовать множество различных возможностей. Например, высоту или ширину символа можно изменять, оставляя толщину штрихов неизменной:
оооООО АААААА
23 февраля 1981 г.
Скотту Киму от У. Дж. Ле Века
Дорогой Скотт!
Я был очень рад услышать, что Вы надеетесь закончить работу в METAFONT’e над латинским, греческим, готическим и рукописным алфавитами этой весной.
Да, это было время эйфории; казалось, все идет превосходно. Но затем пришло отрезвление, грозящее подвергнуть опасности весь проект. Метапроектирование сложнее обычного проектирования, и эта концепция еще недостаточно понята (в особенности не для таких шрифтов, как этот). ДЭК забыл, что ему потребовалось более года на то, чтобы изучить принципы метапроектирования. Кроме того, требовалось обработать более 200 символов и каждый нарисовать в двух вариантах разной насыщенности. Нельзя было ожидать, что ГЦ будет абсолютно последователен при изменении каждого штриха каждого символа от средней насыщенности к полужирному варианту; следовательно, ни от одного произведенного компьютером меташрифта нельзя было ожидать точного совпадения с рисунками ГЦ. Для того чтобы отличать действительно решающие детали рисунков от тех, которые ГЦ и сам бы изменил, будь он метадизайнером, необходимо очень хорошо разбираться в вопросе. Задача Кима была, таким образом, гораздо более сложной, чем думали ДЭК и ГЦ в то время.
ДЭК спросил, пойдет ли ГЦ на то, чтобы дать им со Скоттом свободу в следующем: «Если МЕТА FONT трудно заставить сделать верную копию Вашего рисунка и если есть вариант, почти такой же, как Ваш рисунок, который легок для METAFONT’a и кажется нам нормальным, можем ли мы умышленно изменить Ваш чертеж, приведя его к виду, наиболее естественному для METAFONT’a? (Конечно, результаты в конце будут представлены для Вашего одобрения.)» ГЦ ответил утвердительно: «Я доверяю вам. В некоторых символах будет необходимо пойти на компромисс между моими рисунками и структурными особенностями METAFONT’a. Не слишком беспокойтесь об этом; я знаю, что вы оба сделаете все возможное».
26 мая 1981 г.
ГЦ от ДЭК
Дорогой Герман!
На прошлой неделе почтальон доставил три вкуснейших блюда от Вас! Теперь мы получили полный комплект цифр вместе с прописными и строчными латинскими и греческими буквами, сразу и светлой, и полужирной насыщенности.
Теперь у Скотта полно работы. Он хочет этим летом сосредоточиться на шрифте Euler. ‘
Как бы то ни было, к сожалению, лето увидело завершение лишь 26 строчных латинских букв. Не существовало простого способа превращать рисунки в компьютерный код; каждая буква требовала дня на обдумывание, дня на утомительные измерения и прорисовывания, еще одного дня на борьбу с МЕТА- FONT’om. Выяснилось, что первая система МЕТА FONT совсем не была приспособлена для проектов вроде этого; потребовалась совершенно новая система [15]. Но ДЭК уже занялся другими проектами, отнимавшими у него более 24 часов в сутки. Мы постепенно осознавали, что на всю необходимую работу уйдет несколько лет.
19 апреля 1982 г.
ГЦ от ДЭК
Дорогой Герман!
... у меня нет никаких сомнений, что шрифт Euler однажды воплотит все наши ожидания, но этот день гораздо дальше, чем я раньше предполагал.
Спасением стала станфордская новая программа по компьютерной типографии (Digital Typography), начатая осенью 1982 г. под руководством проф. Чарльза А. Бигелоу. В новых студентах: Джоне Хобби, Дэне Миллсе, Дэвиде Зигеле и Кэрол Туомбли —соединилось множество умений, необходимых для преобразования рисунков ГЦ в цифровой формат, в то время как ДЭК в сентябре 1985 г. заканчивал абсолютно новую систему METAFONT. Вся сага, описывающая эти поучительные разработки, изложена в хорошо иллюстрированном буклете Дэвида Зигеля [21].
Группа произвела на свет 484 символа менее чем за 484 рабочих дня, включая все программирование. Проект в целом потребовал 80 миллионов байтов дискового пространства, хранящихся на двух компьютерах. ... Формулы, набранные шрифтом Euler, должны сохраняться при размазывании слишком обильно нанесенных на пресс чернил по самой наитончайшей бумаге. Они не должны расплываться ни на барабане лазерного принтера, ни под не очень ярким светом древних фотокопировальных машин в математической библиотеке. Буквы не должны терять четкость при самом тусклом свете около полки с периодикой через самые темные трифокальные очки; они должны ясно просвечивать сквозь лес накарябанного как курица лапой поверх гранок. Шрифт Euler готов ко всем этим испытаниям в AMS.
Даже после оцифровки шрифтов работа не была завершена. Требовалось определить соответствующие размеры отступов слева, справа, сверху и снизу каждого символа. Требовалось предоставить информацию, необходимую для расположения акцентов и индексов. (Первоначальная надежда математиков
на то, что это окажется ненужным для выпрямленного шрифта, не сбылась; истинные его преимущества оказались более неуловимыми, а именно выпрямленные буквы лучше сочетаются со скобками, плюсами и другими математическими символами.) Требовалось написать новые «макро», чтобы авторы и их компьютеры могли без труда использовать новые шрифты. И —самое главное —для тестирования и отладки нового проекта в самых разных математических ситуациях требовался опыт. Нам нужно было сжиться с новыми соглашениями и изучить их главные особенности, прежде чем предлагать их широкой общественности.
Следующий очевидный шаг заключался в том, чтобы сделать готические шрифты широко доступными в дополнение к существующим начертаниям, потому что имелась насущная необходимость в математическом готическом шрифте. Поэтому эта часть AMS Euler была использована в настоящих публикациях первой, вначале в экспериментальной книге, отпечатанной в Станфорде [19], и затем во многих статьях журнала AMS.
AMS Euler наконец-то достиг целей, поставленных перед проектом в самом начале, когда все объединенные в нем алфавиты были использованы для набора математических формул в серьезном учебнике. Эта книга под названием Concrete Mathematics [10] была во многих отношениях идеальной стартовой площадкой для новых соглашений. Во-первых, в ней обсуждалось много различных разделов математики во всех ее разнообразнейших аспектах. Во- вторых, ДЭК был одновременно и наборщиком, и соавтором этой книги, так что он мог внимательно изучать формулы и делать любые необходимые поправки. И, наконец, предмет Concrete Mathematics как нельзя лучше подходил к названию AMS Euler, потому что ДЭК и его соавторы к тому времени уже решили посвятить свою книгу Леонарду Эйлеру! «... дух Леонарда Эйлера живет поистине на каждой ее странице: конкретная математика — это эйлерова математика!» [10, с. 12].
Когда художник книги, Рой Говард Браун, впервые увидел шрифты Euler, он заметил, что они немного темнее традиционных математических шрифтов, поэтому для них требовалось немного более жирное начертание текста, чем обычно. На основе этих рекомендаций ДЭК представил новый шрифт, названный Concrete, прямой светлый и курсивный, для основного текста, сделанный при помощи его меташрифта Computer Modern [17] с несколько «египетскими» значениями параметров. Комбинация AMS Euler и прямого светлого Concrete хорошо зарекомендовала себя на всех 640 страницах Concrete Mathematics, так что шрифты Euler теперь действительно ожидает самое светлое будущее.
Неожиданным оказался один аспект, связанный с переходом от существующих соглашений к AMS Euler: теперь имеется четыре разных набора цифр, а именно: «выровненный стиль» (где все цифры расположены над линией шрифта и имеют одинаковую высоту: 0123456789) и «старинный стиль» (0123456789), каждый из них в текстовом и математическом начертаниях. Поэтому авторам впервые пришлось различать числа, являющиеся частью текста (например, ‘1988’), и числа, появляющиеся в математических формулах (например, ‘3,1416’). Это различие оказалось довольно эффективным, и его легко
осуществить при помощи ТЁК’а. Выровненные цифры текстового начертания использовались в предложениях типа ‘Глава 2’; старомодные цифры текстового начертания — для номеров уравнений. Выровненные цифры AMS Euler использовались в математических формулах, а старомодные цифры AMS Euler подошли бы для числовых таблиц (в этой книге не понадобившихся).
Использование AMS Euler в Concrete Mathematics было бы совсем рутинным, если бы не один отрывок на с. 167-168, где авторам хотелось изобразить последовательность все более сложно выглядящих q (потому что математика двигалась от q «первого уровня» ко второму, третьему и более высоким уровням). В первом приближении последовательность выглядела так: строчная прямая светлая, затем прописная прямая светлая, затем рукописная, затем полужирная рукописная: q Q Q Q. Эта последовательность не подошла, потому что рукописная Q в AMS Euler на самом деле меньше по размеру и не столь внушительна, как прямая светлая Q. Переходя от повторного использования рукописного шрифта к прямому полужирному, за которым следует прописной готический, мы получили последовательность с требуемым настроением: q Q Q й (Конечно, рукописные буквы оказались полезными в других случаях; задача рукописного AMS Euler всегда состояла в том, чтобы избежать излишеств коммерческих шрифтов, создаваемых, как правило, для свадебных приглашений, а не для математических текстов.)
За то время, что ДЭК набирал книгу в 1987-1988 г., он быстро привык к новому «выпрямленному» внешнему виду и заметил, что привычный знак интеграла теперь не подходит. Поэтому он предложил вертикальный знак интеграла в шрифтах дополнительных математических символов: J превратился в J. Он также внес завершающее изменение в алфавиты Euler (с одобрения ГЦ), превратив
£ в £
(для этого были использованы возможности трансформации нового МЕТА- FONT’a). Теперь все математические формулы создавали одинаковое ощущение выпрямленности; идеальное единообразие замысла наконец-то было достигнуто!
Если эти новые шрифты обретут поддержку в математическом сообществе в целом, потребуется проделать еще некоторую работу. В настоящий момент AMS Euler не является действительно меташрифтом; его символы были просто оцифрованы для двух степеней насыщенности по исходным рисункам ГЦ. Из- за этого шрифты нельзя ни с легкостью сделать шире для удобочитаемости при маленьком кегле, ни быстро приспособить к наборным устройствам, дающим более темные или более светлые изображения, чем нужно. Новый METAFONT, сам во многом появившийся благодаря первым опытам с AMS Euler, должен оказаться подходящим инструментом для создания меташрифта Euler; так что ДЭК и ГЦ оба надеются, что кто-нибудь примет этот вызов. Мы можем гарантировать, что кто бы ни попробовал взяться за это, он приобретет в высшей степени плодотворный и поучительный опыт.
Здесь приведены основные символы шрифта Euler, набранные 12-м кеглем с прямым светлым Concrete в качестве сопровождающего текста.
Цифры: 0123456789 : 0123456789
& 0123456789 : 01Z3456789
Прописные латинские: |А| + |В| + |С| + |D| + |Е| + |F| + |G| + |Н| + |I| + III + |К| + |L| + |М| + |N| + |О1 + |Р| + IQI + |R| + |S| + |Т| + |U| + |V| + |W| + IX| + |Y| + |Z| + |A| + |B| + |C| + |D| + |E| + |E| + |G| + |H| + |I| + |J| + |K| + |L| + |M| + |N| + |O| + |P| + |Q| + |R| + |S| + |T| + |U| + |V| + |W| + |X| + |Y| + |Z|
Строчные латинские: |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| + |a| + |b| + |c| + |d| + |e| + |-f| + |g| + |h| + |i| + |i| + |k| + |l| + |m| + |n| + |o| + |p| + |q| + |r| + |s| + |t| + |u| + |v| + |w| + |x| + |y| + |z|
Прописные греческие: |Г| +|Д| +|О| + |Л| + |Z| + |П| +1£| + |Т| + |Ф| + |V| + |Q| + |r| + |A| + |0| + |A| + |S| + |n| + |Z| + |Y| + |O| + |W| + |Q|
Строчные греческие: |а| + ||3| + |у| + |5| + |е| + |(| + |г|| + |0| + |t| + И + 1Л| + 1Щ +М + |Е| + |л| + |р| + |ст| + |т| + Щ| + |ф| + |х| + |Ф| + Ы + l«l + |Р| + IyI + 151 + lel + К + Ini + |0| + М + |к| + |Л| + 1^1 + |v| + |Е| + |я| + | pl + |ст| + |т| + |и| + |ф| + |х| + |ф| + |си|
Прописные готические: |2l| + |D3| + |С| + |$)| +|£| +|$| +|(5| +|^| + Р1 + 131 + |Я| + |£| + |9Л| +|<П| + |£)| + №1 + |О| + |К| + |6| + |Т| + |U| + |У| + |2В| + |Х| + |2)| + |3| + |21| + |23| + |€| + |Т>| + |С| + |5| +10| + р| +|3| +13| +1^| +KI + |9Л| + |?t| + |D| + №| + 1О| + 1$Н| + |0| + |Т| + |М| + |2J| + |2В| + |Х| + |2)| + 131
Строчные готические: |a| + |b| + |с| + |D| + |е| + |f| + |g| + |b| + N + 111 + |е| + |1| + |пг| + |п| + |о| + |р| + |Ч| + |г| + |5| + |1| + |и| + |О| + |п)| + Н + Ы + |3| + |a| + |b| + |c| + |b| + |e| + |f| + l0l + lb| + |i| + |i| + |e| + iq + |tn| + [nl + lol + lpl + Iql + Irl + lsl + ltl + lul + lvl + ltol + lyl + lDl + lsl
Прописные рукописные: |А| +1$| + |С| + |D| +18| +1^1 + |S| +1^| + |3| + |а| + |ЗС| + |^| + |М| + |N| + |О| + |Т| + |Q| + |IR| + |S| + |Т| + |U| + IVI + 1W| + |X| + 1У1 + |Z| + |A| + |S| + |C| + |D| + |£| + |У| + 131 +1^1 + PI + PI +13<| + KI + |M| + |N| +101 + |T| + |Q| + |;R| + |S| + |T| + |U| + |V| + |W| + |X| + 1УI + KI
Дополнительные буквы: |К| + |£| + |р| + |г| + |)| + |е| + |$| + |со| + |<р| + |Х| +1£| + |р| + |i| + bl +1^1 + |б| + |ш| + |<р|
(Особая р в нижней строке заслуживает внимания как созданная выдающимся математиком девятнадцатого столетия Карлом Вейерштрассом, который в юности изучал «Schonschrift» [1].)
В завершение посмотрим, как представленные выше три набора формул будут выглядеть в новом наряде от Эйлера:
Ind.Ind.Fract.Fract.where andwhere andV, а, bО’ а’ b ’ с21 = v; «В = а21 + 1; С = Ъ«В + 21 а = 1; b = aa + 0; c=bb+aV, а, bО’ а’ b ’ с21 = v; «В = а21 + 1; С = Ъ«В + 21 а = 1; b = aa + 0; c=bb+aее£ее£
Ind.
Ind.
Fract.
Fract.
where and
where and
V, а, b
О’ а’ b ’ с
21 = v; «В = а21 + 1; С = Ъ«В + 21 а = 1; b = aa + 0; c=bb+a
V, а, b
О’ а’ b ’ с
21 = v; «В = а21 + 1; С = Ъ«В + 21 а = 1; b = aa + 0; c=bb+a
ее£
ее£
Недавнее появление настольных издательских систем повысило внимание математиков к качеству печати. Поэтому авторам был очень приятен благосклонный прием, оказанный этому новому шрифту в первые месяцы после его представления на суд общественности; у истории AMS Euler действительно счастливый конец:
Дата: срд, 28 дек 88 12:59:07 CST
От: thistedQgalton.uchicago.edu (Рональд А. Фистед)
Кому: dekQsail.stanford.edu
Тема: Конкретная математика
Дон!
Я недавно увидел вашу новую книгу в нашем книжном магазине и что-то толкнуло меня купить один экземпляр (наверное, подарок самому себе на Рождество). ...
Между прочим, я обнаружил, что в результате работы оформителей и художников получилась самая удобочитаемая техническая книга из тех, что я видел за долгое время. Обычно после прочтения нескольких страниц большинства книг я уже утомляюсь, но в этом случае мне удалось прочесть всю главу 1, ни разу не отвлекшись.
На рисунки и символы AMS Euler, показанные в этой статье, распространяются авторские права Американского математического общества и они использованы здесь с его разрешения. Эти исследования частично поддерживались грантами System Development Foundation и National Science Foundation.
Литература
Kurt-R. Biermann, “Karl WeierstraB,” Journal fur die reine und angewandte Mathematik 223 (1966), 191-220.
T. W. Chaundy, P. R. Barrett, and Charles Batey, The Printing of Mathematics (Oxford: Oxford University Press, 1954).
A. L. Crelle, editor, Journal fur die reine und angewandte Mathematik 23 (1842), факсимильная вставка между cc. 104 и 105. (Еще с дюжину примеров нулей Эйлера можно найти в книге 9 на с. 96.)
John Dreyfus, The Work of Jan van Krimpen (London: Sylvan Press, Museum House, 1952).
Charles Enschede, Typefounders in the Netherlands; перевод на английский с исправлениями и замечаниями Гарри Картера (Haarlem: Stichting Museum Enschede, 1978), 261-267.
Leonhard Euler, “De usu novi algorithm! in problemati Pelliano solvendo,” Novi commentarii academiae scientiarum Petropolitanae 11 (1765; издано в 1767), 28-66.
Leonhard Euler, Institutionum Calculi Integralis, Volume 2 (St. Petersburg: Academiae Imperials Scientiarum, 1769).
Leonhard Euler, “De novo genere serierum rationalium et valde convergentium quibus ratio peripheriae ad diametrum exprimi potest,” Nova acta academiae scientiarum Petropolitanae 11 (1793; издано в 1798), 150-154.
Leonhard Euler 1707-1783: Beitrage zu Leben und Werk (Basel: Birkhauser Verlag, 1983).
Ronald L. Graham, Donald E. Knuth, and Oren Patashnik, Concrete Mathematics (Reading, Massachusetts: Addison—Wesley, 1989). [Имеется русский перевод: P. Л. Грэхем, Д. Э. Кнут, О. Паташник. Конкретная математика. — М.: Мир, 1998, 1999.]
Scott Kim, Inversions (Peterborough, New Hampshire: Byte Books, 1981).
Donald E. Knuth, “Mathematical typography,” Bulletin of the American Mathematical Society (new series) 1 (1979), 337-372. [Представлено с исправлениями в данном издании как гл. 2.]
Donald Е. Knuth, “The concept of a meta-font,” Visible Language, 16 (1982), 3-27. [Представлено в данном издании как гл. 15.]
Donald Е. Knuth, The T^Xbook, Volume A of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1984). [Имеется русский перевод: Дональд Е. Кнут. Все про ТЁК. — Протвино: АО RDT^X, 1993.]
Donald Е. Knuth, “Lessons learned from METAFONT,” Visible Language, 19 (1985), 35-53. [Представлено в данном издании как гл. 16.]
Donald Е. Knuth, The METAFONTbook, Volume C of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American ' Mathematical Society, 1986).
Donald E. Knuth, Computer Modern Typefaces, Volume E of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley, 1986).
Donald E. Knuth, “Typesetting concrete mathematics,” TUGboat 10 (1989), 31-36, 342. [Представлено в данном издании как гл. 18.]
Ernst Kunz, Introduction to Commutative Algebra and Algebraic Geometry (Boston, Massachusetts: Birkhauser Boston, 1985).
Phoebe J. Murdock, “New alphabets and symbols for typesetting mathematics,” Scholarly Publishing 8 (October 1976), 44-53.
David R. Siegel, The Euler Project at Stanford (Stanford, California: Computer Science Department, Stanford University, 1985).
Technische Hochschule Darmstadt, Hermann Zapf: Ein Arbeitsbericht (Hamburg: Maximilian-Gesellschaft, 1984).
Jan van Krimpen, On Designing and Devising Type (New York: Typophile Chap Books, 1957).
John Wallis, A Treatise of Algebra (Oxford: 1685).
Hermann Zapf and His Design Philosophy (Chicago: Society of Typographic Arts, 1987).
Sammlung Hermann Zapf (Wolfenbiittel: Herzog August Bibliothek, 1993).
Дополнение
Более короткая версия текста этой статьи была опубликована в ABC-XYZapf: Fifty Years in Alphabet Design, под редакцией Джона Дрейфуса и Кнута Эриксона (London: The Wynkyn de Worde Society, and Offenbach: Bund Deutscher Buchkiinstler, 1989), cc. 171-179. Эта версия содержит несколько дополнительных рисунков, относящихся к позднейшим стадиям работы.
Во втором издании [10], вышедшем в 1994 г., METAFONT использовали для улучшения нижних и верхних индексов, сделав их чуть более широкими.
Набор книги
Concrete Mathematics
[Первоначально опубликовано в TUGboat 10 (1989), 31-36, 342.]
В течение 1987-1988 гг. я работал над учебником Concrete Mathematics [1], написанным в соавторстве с Роном Грэхемом и Ореном Паташником. Я прилагал все усилия со своей стороны, чтобы сделать книгу интересной с математической точки зрения, но я также знал, что она должна быть и полиграфически интересной, потому что в ней впервые использовались новые шрифты Германа Цапфа, заказанные ему Американским математическим обществом. Эти шрифты, названные AMS Euler, были тщательно оцифрованы и переведены в формат METAFONT студентами кафедры компьютерной типографии Стан- фордского университета [9], но для реальных приложений они еще не были вполне «отшлифованными». Моя новая книги могла послужить идеальным тестом, потому что (1) книга содержала большое разнообразие математических формул; (2) у меня было серьезное намерение сделать эту книгу удобочитаемой и радующей глаз; (3) на опытах с «отшлифовкой» шрифтов Computer Modern я получил навык установки таинственных параметров, используя Т^Х в математическом режиме; и (4) книга была посвящена великому математику Леонарду Эйлеру, в честь которого и были названы шрифты.
В основу дизайна шрифтов Euler Цапф заложил ту идею, что все символы должны быть наполнены математическим ароматом, как если бы они были написаны математиком с великолепным почерком. Например, одним из типичных знаков шрифта AMS Euler может служить нуль ‘О’, который слегка заострен сверху, потому что чрезвычайно редко рукописный нуль замыкается гладко, когда кривая возвращается в начальную точку. Для математики рукописный рисунок буквы подходит больше, чем механический, потому что обычно математики пишут свои работы ручкой, карандашом или мелом. Буквы Euler прямого начертания, а не курсивного, так что они в общем сочетаются с математическими символами, такими, как плюсы или скобки, поэтому формулы выстраиваются весьма гармонично. Семейство AMS Euler включает семь алфавитов: прямые прописные (от АВС до XYZ), прямые строчные(от abc до xyz), греческие прописные (от АВГ до Х^П), греческие строчные (от а|3у до Хфси), готические прописные (от 2VBC до £2)3)> готические строчные (от abc
до эдз) и рукописные прописные (от АТС до ХУХ). Включены также два вида начертании цифр: (0123456789 и 0123456789), равно как спецзнаки типа р, К и некоторые знаки препинания. Подробности дизайна обсуждаются в статье [7].
Для того чтобы усовершенствовать процесс оцифровки литер математического назначения, я начал с исправления способа, по которому они выстраивались в своих «боксах» с точки зрения ТуС’а. С этой целью я использовал тесты \math стандартной подпрограммы testfont [5, Appendix Н]; эти тесты проводили литеры через их основные положения посредством набора формул, таких, как |A| + |B| + |C| + --- + |Z|, а2+ b2+ с2 + • • •+ z2, ai + bz + czd+32, hA + S + C + '- ' + Z. Среди прочего я заметил, что готические буквы размещены слишком высоко над линией шрифта, а для литер в размере верхних и нижних индексов нужно дополнительное пространство слева и справа. Кроме этих проблем я обнаружил, что нужно установить поправку на курсив, чтобы верхние и нижние индексы заняли свое правильное место. Затем понадобилось определить подходящий размер кернинга посредством \skewchar, чтобы акценты зрительно заняли место по центру буквы. Семейство AMS Euler содержит более 400 литер, и Герман разработал их великолепно. Моя задача состояла в правильной корректировке разнообразных пробелов, чтобы буквы гармонировали с математическими формулами.
Следующий шаг состоял в создании макрокоманд TgX’a для удобства использования шрифтов AMS Euler в математическом режиме. Это означало добавление новых «блоков» соглашений к тем, что уже были определены для предыдущих форматов. Plain TgX набирает математические выражения, используя четыре основных семейства шрифтов (\famO для текста, \faml для курсива в формулах, \f am2 для символов и \f атЗ для больших ограничителей) плюс несколько других, которые используются реже (\f am4 для курсива в тексте, \f am5 для наклонного в тексте, \f атб для прямого полужирного и \fam7 для имитации пишущей машинки). Я добавил \fam8 для рукописного AMS Euler Script и \fam9 для готического AMS Euler Fraktur; греческий и прямой AMS Euler Greek и Roman заняли прежние места для курсива в математике \f ami. Герман нарисовал новые фигурные и квадратные скобки, которые органично вписались в готический шрифт. Таким образом, в файле моих макрокоманд соглашения plain TgX’a заменились посредством ввода определений типа \mathcode' (="4928 и \delcode'(="928300. Аналогично, в семействе Euler был символ ° в качестве альтернативы символу 1<\ запакованный в рукописном алфавите. Для того чтобы TgX распознал эту замену, я ввел \mathchardef \leq="3814 \let\le=\leq.
С таким образом расширенным plain ТеХ’ом я мог набирать формулы вроде $\tan(x+y)$ как обычно и получать в результате не «tan(a; 4- т/)», а «tan(x 4- у)». Было, однако, одно существенное отличие между набором рукописей Concrete Mathematics и The Art of Computer Programming, связанное с тем фактом, что рисунок цифр в шрифте Euler 0123456789 совсем не был похож на рисунок цифр 0123456789 в обычном тексте. Раньше я старался «оп-
тимизировать» свою работу по набору, например так:
$х$ есть либо 1, либо $-1$,
т. е. я не помещал математические константы в знаки $, за исключением случаев, когда нужно было получить минус, а не дефис. Я рассуждал так: все эти дополнительные доллары только усложняют работу в Tg^’e, а результат выглядит таким же; разве я поступаю не логично? Но при наборе Concrete Mathematics мне приходилось набирать так:
$х$ есть либо $1$, либо $-1$,
чтобы х принимал значение ‘либо 1, либо —Г. Первые черновики моей рукописи были подготовлены в прежней манере, так что мне пришлось потратить несколько часов, скрупулезно выискивая и исправляя все места, где было нужно учесть новые соглашения. Это опыт был не напрасным, поскольку он подсказал мне, что полезно и разумно различать цифры в тексте и цифры в математических выражениях. Текстовые цифры следует использовать в таких случаях, как «1776 г.», «глава 5» и «41 способ», когда числа служат составной частью обычной литературной фразы; математические цифры, напротив, должны использоваться в контексте типа «наибольшим делителем чисел 12 и 18 является 6», когда числа являются составной частью математического выражения. (Авторы технических текстов, пишущих, например, на японском языке, в котором принято использовать арабские цифры только в формулах, но не в обычном тексте, всегда хорошо чувствовали это отличие; теперь и у меня появился шанс испытать это на себе.)
В то время как я с упорством зубрилы продирался через AMS Euler, мои издатели показали предварительный вариант рукописи Concrete Mathematics книжному дизайнеру Рою Говарду Брауну. Я переслал Рою экземпляр первого отчета по шрифтам Euler [9], чтобы он мог увидеть образцы шрифта, который предполагалось использовать для набора математических выражений. Нашим первым намерением, основанным на замысле Цапфа 1980 г., когда он приступил к разработке шрифта, было использовать для текста шрифт Computer Modern Roman, а для математики —AMS Euler. Но Рой обнаружил, что AMS Euler был несколько «чернее», чем традиционный математический курсив, поэтому он решил, что и шрифт для текста должен быть чуть-чуть утяжелен. Он прислал мне несколько образцов шрифта более подходящей насыщенности, чтобы я смог изготовить специальный шрифт, сочетающийся с AMS Euler. (Одна из основных моих посылок, когда я разрабатывал семейство Computer Modern, состояла в том, что я должен быть в состоянии адаптировать шрифт к новым условиям, подобным тем, что были сформулированы теперь.) Когда я увидел образцы Роя, я решил добиться чего-нибудь вроде того, что хотел осуществить в течение долгих лет, ожидая подходящего момента: а именно найти установку таких параметров в Computer Modern, чтобы получить некий «египетский» стиль (с квадратными засечками).
На переплетах пятитомника Computers & Typesetting, тт. А-Е, на парах букв Аа, Bb, Сс, Dd и Ее продемонстрирована трансформация стиля от Computer Modern Roman до египетского. Я создавал оформление этих переплетов исключительно ради собственного удовольствия и выполняя просьбу Маршалла Хенрике а, но у меня никогда не было достаточно времени поэкспериментировать в таком ключе со всем шрифтом целиком. Теперь мне представился прекрасный случаи удовлетворить свою прихоть, и, посвятив послеполуденный досуг сладостному экспериментаторству, я выявил комбинацию параметров, приводящих к весьма приемлемому качеству, по крайней мере судя по тому, что произвел наш лазерный принтер. (Увеличение шрифта и взгляд на него с достаточно большого расстояния нивелировали эффект разрешения 300 точек на дюйм.) Затем я более тщательно подготовил образец текста и распечатал его на нашем станфордском фотонаборном автомате APS, чтобы убедиться, что новый шрифт сможет выдержать экзамен. Некоторые буквы нуждались в корректировке —например, буква ‘w’ оказалась слишком темной,— но я был очень доволен результатом и Рой тоже.
Я решил назвать полученный шрифт Concrete Roman, потому что он выглядел крепким^ и еще потому, что впервые использовался в книге Concrete Mathematics. (Возможно вы до сих пор не заметили, но текст, который вы читаете, набран именно шрифтом Concrete Roman?) В этой книге в качестве выделительного шрифта используется Concrete Italic. Если нужен ЕЩЕ ОДИН ВЫДЕЛИТЕЛЬНЫЙ ШРИФТ, ТО ИНОГДА ИСПОЛЬЗУЕТСЯ CONCRETE Roman Caps и Small Caps font. Любой, имеющий METAFONT-исходники шрифта Computer Modern, может элементарно получить шрифт Concrete, подготовив файлы параметров типа ccrlO.mf по аналогии с cmrlO.mf; нужно будет всего лишь изменить некоторые значения параметров как показано в соответствующей таблице.
Вот образцы, набранные шрифтом Concrete Roman размерами 9 пунктов, 8 пунктов, 7 пунктов, 6 пунктов И 5 пунктов!
Mathematics books and journals do not look as beautiful as they used to. It is not that their mathematical content is unsatisfactory, rather that the old and well-developed traditions of typesetting have become too expensive. Fortunately, it now appears that mathematics itself can be used to solve this problem.
Математические книги и журналы больше не выглядят так красиво, как это соответствовало бы их предназначению. Это объясняется не тем, что их содержание стало неудовлетворительного качества, а вызвано скорее тем, что соблюдение прежних хорошо отработанных типографских традиций теперь слишком дорого обходится. К счастью, выяснилось, что сама математика может решить эту проблему. A first step in the solution is to devise a method for unambiguously specifying mathematical manuscripts in such a way that they can easily be manipulated by machines. Such languages,
when properly designed, can be learned quickly by authors and their typists; yet manuscripts in this form will lead directly to high quality plates for the printer with little or no human intervention.
Первым шагом на этом пути является создание метода строгого описания математических рукописей таким образом, чтобы они легко обрабатывались машинами. Такой аккуратно построенный язык может быть быстро освоен авторами и их машинистками; имея рукопись, подготовленную таким образом, вы будете получать непосредственно с принтера высококачественную распечатку. При этом вмешательства человека не требуется вовсе или оно незначительно.
A second step in the solution makes use of classical mathematics to design the shapes of the letters and symbols themselves. It is possible to give a rigorous definition of the exact shape of the letter ‘a’, for example, in such a way that infinitely many styles — bold, extended, sans-serif, italic, etc. — are obtained from a single definition by changing only a few parameters. When the same is done for the other letters and symbols, we obtain a mathematical definition of type fonts, a definition that can be used on all machines both now and in the future. The main significance of this approach is that new symbols can readily be added in such a way that they are automatically consistent with the old ones.
Второй шаг в решении проблемы состоит в применении классической математики для рисования самих букв и символов. Можно дать строгое определение формы буквы ‘а’, например, таким образом, чтобы из этого одного определения путем изменения лишь нескольких параметров можно было получать бесконечно много стилизаций ее начертания — полужирное, расширенное, рубленое, наклонное и т. д. Сделав то же самое для других букв и символов, мы получим математическое определение наборных шрифтов, которое можно будет использовать на всех машинах как сейчас, так и в будущем. Основное преимущество этого подхода состоит в том, что легко может быть добавлен любой новый символ, причем он автоматически будет соответствовать уже существующим.
Of course it is necessary that the mathematically-defined letters be beautiful according to traditional notions of aesthetics. Given a sequence of points in the plane, what is the most pleasing curve that connects them? This question leads to interesting mathematics, and one solution based on a novel family of spline curves has produced excellent [sic] fonts of type in the author's preliminary experiments.
Необходимо, разумеется, чтобы математически описанные буквы были красивы с точки зрения общепринятых эстетических норм. Какая самая изящная кривая может соединить заданную последовательность точек на плоскости? Этот вопрос приводит к интересной математической задаче и одно из ее решений основывается на новом семействе сплайнов: эти кривые позволяют построить превосходные типографские шрифты, с которыми автор предварительно экспериментировал.
We may conclude that a mathematical approach to the design of alphabets does not eliminate the artists who have been doing the job for so many years; on the contrary, it gives them an exciting new medium to work with. [2, page 337]
Приходим к выводу, что математический подход к шрифтовому дизайну не исключает художников, которые многие годы занимаются этим делом, а напротив, предоставляет им новый эффективный инструмент для работы. [2, с. 377]
Таким образом утяжеленный шрифт больше подходил для ксерокопирования и его было лучше различать в скудно освещенных библиотечных залах, так что он мог решить ряд проблем удобочитаемости, с которыми все мы хорошо знакомы. Но и слишком жирный шрифт может превратить чтение книги в изнурительное занятие. Чтобы избежать этого, Рой решил использовать интерлиньяж \baselineskip 13 пунктов при 10-пунктовом шрифте. Это дает дополнительное преимущество для математических текстов, поскольку предотвращает наложение строк, содержащих формулы вроде ‘5ZOk<n ak’- Обычно в математических изданиях используют интерлиньяж 12 пунктов, что зачастую дает раздражающе плотные строки. Разумеется, такое увеличение межстрочного интервала приводит соответственно и к увеличению количества страниц примерно на 8%, но мне это кажется разумной платой за повышение удобочитаемости.
Так ли уж было необходимо делать шрифт Concrete Roman менее тонким, чтобы он сочетался с шрифтом AMS Euler? Чтобы читатель смог ответить на этот вопрос самостоятельно, приведем небольшой пример использования обычного cmrlO в качестве шрифта для текста:
Таблица значений параметров для шрифтов Concrete
название
cmrlO
ссгЮ
ссг9
ccr8
ccr7
font-identifier
CMR
CCR
CCR
CCR
CCR
serif-fit
0
1
1
1
1
cap serif-fit
5
3
2.8
2.6
2.4
x-height
155
165
148.5
132
115.5
bar-height
87
92
78.3
69.6
60.9
tiny
8
11
10
9
8
fine
7
6
6
6
6
thin.join
7
17
17
15
13
hair
9
21
20
19
17
stem
25
25
24
22
20
curve
30
27
26
24
21.5
ess
27
25
24
22
20
flare
33
29
26
24
23
cap-hair
И
21
20
19
17
capstem
32
27
26
24
21.5
cap-curve
37
28
27
25
22.5
cap.ess
35
27
24
22
21.5
bracket
20
5
5
4
4
jut
28
30
27
24
21
cap-jut
37
32
29
26
23
vair
8
21
20
19
17
notch_cut
10
5/6
3/4
2/3
7/12
Ьаги t. n.*
11
21
20
19
17
cap-notch_cut
10
1
.9
.8
.7
serif_drop
4
5
3.6
3.2
2.8
0
8
4
4
3
3
apex-0
8
3
3
3
3
beak-darkness
11/30
4/30
4/30
4/30
4/30
другие значения из
cmrlO
cmr9
cmr8
cmr7
* Значения для bar применимы также г Во всех шрифтах Concrete dish = 0, и superpull = 1/15, только у шрифта
[ для slab, cap-bar и cap.band. fudge = .95, superness = 8/11 ccslc9 fudge = 1. Параметры,
здесь не упомянутые, следует взять из соответствующих шрифтов ст,
(В этой таблице используются соглашения, приведенные на сс. 12-31 книги [б].)
ссгб
ссгб
ccslc9
cctilO
ccmilO
cccsclO
меньшие
CCR
CCR
CCSLC
CCTI
CCMI
CCCSC
1
1
0
1
1
1
2.2
2
2
3
3
3
2
99
82.5
155
165
165
155
116
52.2
43.5
85
92
92
87
65
7
6
9
11
11
11
6
5
6
6
6
6
12
И
13
17
17
17
15
14
16
21
21
21
18
16
22
24
25
25
23
19
17
23
26
27
27
17
12
25
24
25
25
20
18
28
28
29
29
22
15
14
16
21
21
21
21
19
17
23
26
27
27
24
20
18
24
27
28
28
26
19
14
23
26
27
27
24
3
3
5
5
5
5
19
17
15
30
30
30
20
18
16
32
32
32
24
15
14
15
21
21
21
1/2
5/12
3/4
5/6
5/6
5/6
15
14
15
21
21
21
21
.6
.5
.9
1
1
1
3/4
2.4
2
3.6
5
5
5
3
3
4
4
4
4
3
3
2
3
3
3
3
3
4/30
4/30
5/30
4/30
4/30
4/30
стгб
стгб
cms!9
cmtilO
cmmilO
cmcsclO
за исключением следующих: cccsclO имеет lower.fudge = .93; cctilO имеет значение и, равное 20, а не 18.4, и значение crispt равное 11, а не 8; ccmilO имеет значение crisp, равное 0, а не 8, и его math-fitting соответствует false. В шрифте ccsllO все так же, как и в ссгЮ, за исключением строки font .identifier и того факта, что slant равен 1/6.
The set S is, by definition, all points that can be written as J2ki Qk(i— 1)k, for an infinite sequence U], 02, аз, ... of zeros and ones. Figure 1 shows that S can be decomposed into 256 pieces congruent to y^S; notice that if the diagram is rotated counterclockwise by 135°, we obtain two adjacent sets congruent to (1/V2)S, since (i — 1)S = S U (S + 1). [3, page 190]
А теперь повторим этот же пример, но с шрифтом Concrete Roman и \baselineskip=13pt:
The set S is, by definition, all points that can be written as J^ki Qk(i — 1 )k> for an infinite sequence a], 02, аз, ... of zeros and ones. Figure 1 shows that S can be decomposed into 256 pieces congruent to y^S; notice that if the diagram is rotated counterclockwise by 135°, we obtain two adjacent sets congruent to (1/V2)S, since (i — 1)S = S U (S + 1). [3, page 190]
Цифры для нумерации формул представлялись Рою и мне одним из самых запутанных вопросов дизайна. Должны ли эти цифры быть набраны шрифтом Euler или их следует изобразить шрифтом Concrete? После ряда экспериментов мы пришли к выводу, который теперь, при ретроспективном взгляде, кажется единственно верным: мы решили нумеровать формулы в «старинном стиле» из шрифта Concrete Roman, используя цифры ‘0123456789*. Результат—например, ‘(3.14)* —оказался на удивление именно таким, какого мы и ожидали.
После того как я многие месяцы применял AMS Euler и был вполне удовлетворен видом получавшейся «правильной математикой», я приступил к работе над главой книги, где часто встречались интегралы. Мне неожиданно бросилось в глаза, что традиционные знаки интеграла плохо сочетаются с шрифтом AMS Euler, потому что они имеют наклон подобно курсивным буквам, тогда как во всех остальных формульных буквах и знаках наклон был полностью исключен. Поэтому я решил сделать новый знак интеграла —прямой, — чтобы он соответствовал духу новых шрифтов. Теперь я мог набирать
f9 И dz
2т J|z|=r zn
и J^ cosx dx, вместо прежних
b« i 1 /9(z) dz
f x dx - — ф ——27П J|Z|=T zn
и J^. cos x2 dx. Новые знаки интеграла были включены в новый шрифт, названный euexlO, который потом превратился в \famlO для математического
режима. Я велел ТуСу брать интегралы из нового шрифта, указав
\mathchardef\intop="1А52
\mathchardef\ointop="1А48
в моем файле макрокоманд. Позже я обратил внимание на то, что знак бесконечности ‘оо’ в Computer Modern слишком светлый и поэтому плохо вписывается в шрифт Euler. Я слегка утяжелил знак ‘оо* и поместил его в шрифт euexlO вместе с новыми знаками интеграла.
Все это время мне помогал Герман Цапф. Он, например, одобрил первоначальный вариант гл. 1, исполненный в шрифтах Concrete Roman и AMS Euler, тогда как я все еще продолжал отшлифовывать некоторые вещи. Позже, получив экземпляр первого издания настоящей книги, он в первый раз увидел гл. 2 и остальной текст. Тут ему пришлось сделать несколько улучшающих замечаний, которые не могли возникнуть на материале одной первой главы.
Глава 2 посвящена суммированию, в ней я использовал знак суммы 52 из шрифта cmexlO семейства Computer Modern и парный ему знак для выключ- ных формул
к=0
для набора сотен математических выражений, содержащих суммирование. Герман обратил внимание на то, что заглавная буква \Sigma из шрифта Euler выглядит совершенно иначе —это ‘Е* без засечек, так что он предложил мне соответствующим образом заменить и мои знаки суммы, чтобы они больше походили на L из шрифта Euler. Я сделал это при второй допечатке тиража, используя в формулах внутри текста знак У > а в выключных формулах —
Е^К).
k=0
Герман попросил также сделать знак произведения чуть шире, более похожим на букву ‘П’ из шрифта Euler; поэтому я заменил
П и JJ на П и П
Кроме того, он предложил удлинить и сделать несколько более жирными кончики стрел: ‘—V вместо ‘—>’, а фигурные скобки сделать более светлыми:
превратились впревратились в
превратились в
превратились в
Все эти новые знаки было нетрудно сделать, удовлетворяя при этом соглашениям относительно шрифта Computer Modern [б], поэтому я добавил их в шрифт euexlO и применил при второй допечатке тиража.
Читатели книги Concrete Mathematics не могли не заметить одно нововведение: на полях большинства страниц имеются «граффити». Мои соавторы
This 9 pt typeface has worked out very nicely for marginal graffiti, where it is typeset ragged right, 6 picas wide, with 10 pt between baselines.This 9 pt typeface has worked out very nicely for marginal graffiti, where it is typeset ragged right, 6 picas wide, with 10 pt between baselines.и я попросили студентов, выверявших черновики предварительного варианта книги, оставить на полях неформальные комментарии, которые можно было бы напечатать вместе с основным текстом, благодаря чему изложение напоминало бы дружескую беседу сверстников. Мы тогда еще не знали, каким образом будут набраны «отклики галерки», но мы точно знали, что хотим их включить: таких откликов набралось почти 500. Рой высказал идею разместить их на
This 9 pt typeface has worked out very nicely for marginal graffiti, where it is typeset ragged right, 6 picas wide, with 10 pt between baselines.
This 9 pt typeface has worked out very nicely for marginal graffiti, where it is typeset ragged right, 6 picas wide, with 10 pt between baselines.
внутреннем поле (у корешка), где они не так бы выделялись. Он также
прислал мне образчик неформального шрифта, на основе которого я смодели
ровал начертание «Concrete Roman Slanted Condensed».
Для того чтобы набирать граффити, я ввел в свой форматный ТеХ’овский
файл макро \g, так что нужно было просто набрать: ‘\g Text of a graffito. \g’
Этот 9-пунктовый шрифт был очень тщательно разработан для размещения на полях граффити с рваным правым краем, шириной 6 пика и 10-пунктовым интерлиньяжем.Этот 9-пунктовый шрифт был очень тщательно разработан для размещения на полях граффити с рваным правым краем, шириной 6 пика и 10-пунктовым интерлиньяжем.на той строке текста, рядом с которой я хотел начать помещать маргиналии. Вручную приходилось только прослеживать те места, где маргиналии могли наложиться друг на друга, но основную работу за меня выполняла макрокоманда \g. Она, например, решала автоматически, на правом или левом поле надо поместить граффити, используя дополнительный файл ‘grf’, в котором регистрировался выбор, который оказывался подходящим при предыдущем прогоне
Этот 9-пунктовый шрифт был очень тщательно разработан для размещения на полях граффити с рваным правым краем, шириной 6 пика и 10-пунктовым интерлиньяжем.
Этот 9-пунктовый шрифт был очень тщательно разработан для размещения на полях граффити с рваным правым краем, шириной 6 пика и 10-пунктовым интерлиньяжем.
В моих макро для Concrete Mathematics ТуСу давались
указания генерировать не только обычные файлы dvi и log, соответствующие вводу, но и только что упомянутый файл grf, равно как и четыре других:
ans-файл содержит текст всех ответов на упражнения, содержащихся в только что набранном материале; эти ответы будут введены (\input) позже в подходящий момент. (Аналогичная идея обсуждается в книге The TgXbook на с. 493. Единственная разница состоит в том, что для книги Concrete Mathematics я для каждой главы создавал свой файл, тогда как для набора The TgXbook использовался один длинный файл.)
inx-файл содержит строки для подготовки указателя. Когда вся книга готова, я собираю все inx-файлы вместе, сортирую их, а результат редактирую вручную. (См. сс. 494-496 книги The TgKbook, где описаны аналогичные макро для указателя и объясняется, почему я не верю в возможность полностью автоматической подготовки указателя.)
ref-файл содержит символические имена формул, таблиц и упражнений, которые могут понадобиться для перекрестных ссылок, (ref-файл аналогичен aux-файлу в J^TgjX’e.)
bnx-файл, наконец, регистрирует номера страниц с ссылками на литераТУРУ> так что я могу включить такую информацию как своего рода указатель в список литературы. Этот указатель делается автоматически.
Мне бы не хотелось иметь дело с таким количеством дополнительных файлов, если бы я работал с более простой книгой или системой более общего назначения. Но именно для такой книги, как Concrete Mathematics, самым удобным оказался этот многофайловый подход.
Мы с моими соавторами решили использовать нестандартную нумерацию для таблиц, аналогичную принятой в некоторых регионах Америки для нумерации сверхскоростных трасс. Например, ссылка на табл. 244 намекает вам, что искать данную таблицу надо на с. 244. (Эта идея не нам первым пришла в голову, но я не помню, кто именно ее предложил.) Создать удовлетворяющие этому соглашению макро, обновляющие ссылки, когда меняется нумерация страниц, было нетрудно.
Все эти макро, написанные специально для Concrete Mathematics, вошли в файл gkpmac.tex, весьма скудно документированный (о чем я сожалею), поскольку он предназначался для выполнения только одной работы. Возможно, другим создателям макрокоманд захочется взглянуть на этот файл как на источник идей, поэтому я сделал его общедоступным [8]. Но тем, кто попробует применять эти макро, следует иметь ввиду, что я не претендовал на нечто абсолютно полное и универсальное. Я, например, ввел (частично) l^TgX’oBCKoe окружение picture и использовал его для подготовки всех рисунков кроме одного. Но все, что способен делать I^TgX, я вводить не стал. Кроме того, я не пользовался полужирным шрифтом для математических выражений в Concrete Mathematics (за исключением Q на с. 168 при второй допечатке тиража) и поэтому не включил макрокоманды для доступа к полужирным шрифтам AMS Euler в математическом режиме. В этом смысле книга не может служить безукоризненным тестом, поскольку среди знаков Euler почти половину составляют полужирные буквы, а они не были еще проверены. По образу и подобию того, что установлено в gkpmac, нетрудно добавить макрокоманды для полужирной математики, но я вынужден предоставить решение этого вопроса другим и вернуться к своему затянувшемуся проекту: написанию остальных томов The Art of Computer Programming.
Литература
Ronald L. Graham, Donald E. Knuth, and Oren Patashnik, Concrete Mathematics (Reading, Massachusetts: Addison—Wesley, 1989). [Имеется русский перевод второго издания: Грэхем Р., КнутД., Паташник О. Конкретная математика. —М.: Мир, 1998, 1999.] [Хотя в копирайте указан 1989 г., я получил авторский экземпляр первого тиража 29 августа 1988 г. и пользовался им в своей работе в октябре-декабре 1988 г. Второй тираж датирован январем 1989 г. В январе 1994 г. вышло второе издание, в котором сам текст книги подвергся существенной переработке, а полиграфическое
исполнение изменилось лишь незначительно: несколько шире стали знаки шрифта A MS Euler для подстрочных и надстрочных индексов.]
Donald Е. Knuth, “Mathematical typography,” Bulletin of the American Mathematical Society (new series) 1 (1979), 337-372. [Представлено с исправлениями в данном издании как гл. 2.]
Donald Е. Knuth, Seminumerical Algorithms, Volume 2 of The Art of Computer Programming, second edition (Reading, Massachusetts: Addison- Wesley, 1981).[Имеется русский перевод первого издания: Кнут Д. Искусство программирования для ЭВМ. Т. 2. Получисленные алгоритмы.—М.: Мир, 1977; второго издания: Кнут Д. Е. Искусство программирования. Т. 2. Получисленные алгоритмы. — Киев: Диалектика, 1999]
Donald Е. Knuth, The Tg^book, Volume A of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1984). [Имеется русский перевод: Кнут Д. E. Все про Т^Х.— Протвино: АО RDTgX, 1993.]
Donald Е. Knuth, The METAFONTbook, Volume C of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1986).
Donald E. Knuth, Computer Modern Typefaces, Volume E of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley, 1986).
Donald E. Knuth and Hermann Zapf, “AMS Euler —A new typeface for mathematics,” Scholarly Publishing 20 (1989), 131-157. [Представлено в данном издании как гл. 17.]
Donald Е. Knuth, gkpmac.tex, доступно по anonymous ftp в labrea.stanford.edu в директорииpub/concretemath, errata/
(последнее обновление в 1996 г.). Также доступно в Comprehensive TgX Archive Network (СТАМ) в директории systems/knuth/local/lib/.
David R. Siegel, The Euler Project at Stanford (Stanford, California: Computer Science Department, Stanford University, 1985).
Добавление
Шрифты Concrete Roman и AMS Euler теперь используются вместе для набора многих научно-технических изданий. Первой после Concrete Mathematics была, вероятно, книга: Kai Borre Mindste Kvadraters Princip (Aalborg: Borre, 1992), ISJBN 87-984210-1-8. В частности, при переводе Concrete Mathematics на французский, венгерский, итальянский и польский языки были, в сущности, использованы те же соглашения, что и в оригинальном издании. В переводе на русский язык книга «Конкретная математика» набрана шрифтом Concrete Cyrillic, разработанным на основе Concrete Roman Ольгой Лапко и Андреем Ходулёвым.
Из опыта преподавания программирования на METAFONT’e
[Первоначально опубликовано в TUGboat 5 (1984), 105-118.]
Весной 1984 г. около полусотни доблестных студентов составили в Станфорд- ском университете не совсем обычную учебную группу, где занятия вели два бравых профессора и еще один профессор — безрассудный. Эти лекции были посвящены разработке шрифтов вообще и использованию новой версии системы METAFONT в частности. Так получилось, что читаемый курс был в значительной степени импровизированным, поскольку METAFONT, создававшийся в течение достаточно долгого времени, только тогда начинал приобретать черты завершенности. И все же, справедливым было бы утверждать, что упомянутые лекции соотносились между собой весьма хорошо и этот эксперимент себя вполне оправдал.
Большей частью своим успехом курс лекций был обязан тем самым двум бравым профессорам, о которых я упоминал выше, —это были Ричард Саутолл и Чарльз Бигелоу, прочитавшие выдающиеся лекции, время от времени перемежавшиеся лекциями, представленными мною. Лекции Саутолла были посвящены теме «Проектирование шрифтовых семейств», которая включала в себя такие пять разделов:
Определения. В чем разница между шрифтами и шрифтовыми семействами (гарнитурами), между проектированием шрифтов и каллиграфией?
Критерий качества. Как вынести объективное решение, насколько удачными являются текстовые шрифты?
Аспекты задачи. Что должны сделать разработчики шрифта?
Методология. Что можно почерпнуть из традиционных знаний и практики применительно к решению задачи разработки шрифтов?
«Идеальные» проекты. Может ли кто-нибудь сказать, какими именно должны быть начертания литер?
Бигелоу рассказывал об истории начертаний букв от античности до наших дней. Очень поучительно было наблюдать, как менялась форма литер с изменением технологии. Первоначально на проектирование алфавита наклады-
вал отпечаток способ письма, принятый в античные времена — перемещение пишущего элемента (пера, например) вдоль некоторой линии. Затем появилось книгопечатание и определяющим стал процесс гравирования или отливки букв. И наконец в наши дни чаще всего формы литер трактуются как образы растрового типа, получаемые и используемые цифровым путем. Была показана и критически проанализирована работа мастеров (художников- шрифтовиков) всех эпох, после чего Бигелоу подвел итог процесса эволюции шрифтов обсуждением современного состояния дел в области создания коммерческих семейств шрифтов, в том числе шрифтов, используемых для вывода информации на экраны электронно-лучевых трубок. Все лекции, как Саутолла, так и Бигелоу, сопровождались обилием примеров, представлявших собой в большинстве случаев уникальные слайды из частных коллекций.
Моя задача состояла в том, чтобы увязать все это с новым METAFONT’om. Мне определенно повезло, так как новые фрагменты языка заработали буквально за пару дней до того, как эту тему надо было излагать на занятиях. В моем распоряжении был один день, чтобы получить хоть какой-то опыт программирования с помощью упомянутых средств и чтобы самому научиться писать с их помощью хорошие программы, прежде чем пытаться учить этому кого-либо еще. Лекции касались следующих вопросов: (1) координаты, (2) кривые, (3) уравнения, (4) оцифровка, (5) перья, (б) преобразования и (7) синтаксис METAFONT’a.
Студенты получили в качестве домашних работ различные поучительные задачи. Во-первых, им было поручено сделать два задания с трафаретами, чтобы показать существенную разницу между тем «что мы видим» и тем, «что есть на самом деле». Затем настала очередь третьей домашней работы, которая была первым компьютерным заданием. Оно состояло в том, чтобы с помощью METAFONT’a изобразить El Palo Alto (высокое дерево) — символ Станфорда; каждый студент сделал по две ветви дерева, поскольку выполнить все двенадцать было бы слишком трудоемко, я объединил потом предложенные ими решения и в итоге получились следующие результаты:
Все эти деревья получились разными, хотя многие из отдельных ветвей использованы в нескольких деревьях. Цель данного упражнения состояла в том, чтобы помочь студентам освоиться с идеями координат и простых кривых в такой же степени, в какой они знакомы с компьютером и текстовым редактором для него. Очертания дерева оказались очень подходящим объектом в силу его
нетребовательности (огрехи на нем были не так заметны, как на регулярном объекте).
Задача для четвертой домашней работы была намного интереснее, мы назвали ее «Font 1» («Шрифт 1»). Совместными усилиями группа должна была разработать новый шрифт без засечек, напоминающий написанный рукой. Поскольку число слушателей, успешно завершивших третью домашнюю работу, было достаточным, мы организовали выполнение задания так, чтобы каждому участнику досталась разработка пары букв — прописной («большой») и строчной («маленькой»). В качестве примеров, которые должны были помочь выдержать требуемый стиль, я дал прописную букву ‘U’ и строчную ‘1’. Все равно, конечно, у каждого из студентов проявились особенности их индивидуального стиля, что и отразилось на полученных результатах — шрифт в итоге вышел не совсем «однородным». Этот факт был сам по себе поучительным.
Я подготовил два METAFONT’obckhx макро, позволяющих рисовать штрихи и дуги так, как если бы применялось перо. От студентов требовалось выполнить всю необходимую работу с использованием этих двух подпрограмм. Это было существенным ограничением, но помогало за счет такого сужения возможностей сфокусировать внимание. Выполняя данное задание, студенты заодно осваивали и концепции метапроектирования, поскольку разрабатываемые ими программы должны были содержать параметры настройки, позволяющие порождать три различных шрифта: нормальный, полужирный и полужирный расширенный. Это давало возможность каждому обучающемуся ощутить алгебраические возможности METAFONT’a, где компьютер играет критически важную роль в процессе разработки.
Лучший способ показать, что получилось при выполнении четвертой домашней работы — продемонстрировать ее результат, т. е. созданный студентами шрифт, который представлен на следующей странице.
Как я уже сказал, мы не ожидали, что шрифт Font 1 будет обладать какой- либо однородностью, т. е. единством стиля, но я с удовлетворением обнаружил, что многие из индивидуальных символов вышли по-настоящему красивыми, даже когда параметры принимали значения, которые студенты не пробовали.
Задача для пятой и завершающей домашней работы была еще более интересной. Каждый должен был разработать совокупность из восьми знаков, которые можно было бы использовать для формирования рамок. Эти знаки именовались NW, NM, NE, ME, SE, SM, SW и MW по часовой стрелке, начиная с левого верхнего угла; здесь ‘№ означает North («север»), ‘Е’ — East («восток»), ‘S’ —South («юг»), ‘W’ —West («запад») и ‘М’ — Middle («середина»). Высота каждого символа определялась первой компонентой его имени, а ширина — второй. Например, NW и NM должны иметь одну и ту же высоту, SE и ME — одну и ту же ширину. Следовательно, четыре знака, содержащие букву М в их
In every period there have been better or worse types employed In better or worse ways.
The better types employed In better ways have been used by the educated printer acquainted with standards and history, directed by taste and a sense of the fitness of things, and facing the industrial conditions of his time.
Such men have made of printing an art.
The poorer types and methods have been employed by printers ignorant of standards and caring alone for commercial success.
To these, printing has been simply a trade.
The typography of a nation has been good or bad, as one or other of these classes had the supremacy.
And today any intelligent printer can educate his taste, so to choose types for his work and so to use them, that he will help printing to be an art rather than a trade. There is not, as tne sentimentalist would have us think, a specially devilish spirit now abroad that prevents good work from being done.
The old times were not so very good, nor was human nature then so different, nor is the modem spirit particularly devilish. But It was, and Is, nard to hold to a principle.
The principles of the men of those times seem simple and glorious.
We do not dare to believe that we, too, can go and do likewise.
DANIEL BERKELEY UPDIKE
именах, могли быть использованы как повторяемые модули расширения, позволяющие получить произвольно большие прямоугольники вместе с четырьмя угловыми знаками. Кроме этих мягких ограничений на высоту и ширину, никаких других базовых правил не было, поэтому студентам предоставлялась свобода творчества на поприще получения с помощью METAFONT’a «великих произведения».
Мне было особенно интересно и приятно просматривать результаты выполнения этого задания, которые произвели на меня большое впечатление своей оригинальностью, в том числе и потому, что я убедился, что новая версия METAFONT’a работает, причем даже лучше, чем я смел надеяться. Все еще оставался открытым вопрос о том, насколько METAFONT будет соответствовать своей роли средства создания шрифтовых форм, но то, что это супер-
ABCDEFGHIJKKLMNOPQRSTTUVWXYZabcdefgh ij к I mnopqrstuvwxyzThis font of type, the firstto be produced by the newMETAFONT system, wasdesigned by Neenie Billawala,Jean-Luc Bonnetain,Jim Bratnober,Malcolm Brown,William Burley, Renata Byl,Pavel Curtis, Bruce Fleischer,Kanchi Gopinath,John Hershberger,Dikran Karagueuzian,Don Knuth, Ann Lasko-Harvill,Bruce Leban, Dan Mills,Arnie Olds, Stan Osborne,Kwang-Chun Park, Tuan Pham,Theresa-Marie Rhyne,Lynn Ruggles, Arthur Samuel,New Wave Dave,Alan Spragens, Nori Tokusue,Joey Tuttle, and Ed Williams.
средство для рисования рамок, было уже совершенно ясно! Вот результаты описываемого эксперимента:
|METAFONT|METAFONT
|METAFONT
|METAFONT
Ed Williams
ХЖ«ЖГО^
METAFONT
Neenie BillawalaNeenie BillawalaJean-Luc BonnetainJean-Luc Bonnetain
Neenie Billawala
Neenie Billawala
Jean-Luc Bonnetain
Jean-Luc Bonnetain
ГмЕТАГОМт)
Jim Bratnober
^щп^щйй
S|pclJlJ=i
^ ^ Ш
■Ж Ш .Ж
BlhsjifgJligl
1 METAFONT 1
brilПШ
L—Ji™ If—inZDi ICT! q rajisi ran Lsi
^^^^ *^^^^ ^^^^
J^f "jcl JSf
to Wb
William Burlev
Kwang-Chun Park
о tXJ LXJ LXJ LXJ LX J LXj LX J 0
J METAFONT?
Э fx^ Л^ Л^ fSct f5c^ 05c^ ^зс^ ^эс^ G
Tuan Pham
Lynn Ruggles
I METAFONT |
Arthur Samuel
K/\/\/\/\/\/\/\/\/\A/\/\/\/\/\/\/\/\/\/\/l/
IMETAFONT
Ie
New Wave Dave Siegel
Alan Spragens
(metafont
ШВВВВЯШШШЯВЖВВ
Nori Tokusue
Стоит сделать ряд замечаний относительно нашего опыта, касающегося «высокотехнологичных» групп, типа описываемой здесь. Ни один из компьютеров, обеспечивающих учебный процесс в Станфорде, не имел тогда выхода на дисплеи с высокой разрешающей способностью, обеспечивающие работу в графическом режиме. Чтобы заполнить эту пустоту, нам пришлось заказать рабочие станции SUN. Когда машины наконец-то были получены — за неделю до планируемого начала занятий в группе, — оказалось, что они новой модели, для которой надо было написать новое программное обеспечение, чтобы встроить эти машины в вычислительную сеть нашего университета и соединить их с различными периферийными устройствами. Фирма-разработчик в ответ на нашу просьбу предоставить исходные тексты ее программного обеспечения, необходимые для решения проблемы, заартачилась. Мы обнаружили, что все равно не сможем использовать их версию операционной системы (ОС) UNIX, поскольку она привязывает каждый файл на нашем главном диске к конкретной рабочей станции, что вынуждало бы студентов вводить пароль при каждом обращении к главному диску! Более того, их компилятор для языка Pascal не мог работать с программами таких размеров, которые были у METAFONT’a.
Мы решили использовать ОС своей разработки, которая называлась V- системой (V-System) или V-ядром (V-Kernel) и была разработана профессорами Дэвидом Черитоном и Киттом Лантцем и их студентами. К счастью, один из этих студентов, Пер Ботнер, был участником ТЁХ’овского проекта и написал к тому же свой собственный компилятор для Паскаля. К сожалению, однако, мы не могли использовать ОС V-System без присоединения всех наших рабочих станций SUN к более мощной машине, наподобие VAX, а у нас такого компьютера не было. Дело еще более усугублялось тем, что здание, в котором мы предполагали разместить наши рабочие станции SUN, ремонтировалось; первоначально планировалось, что мы въедем туда еще в январе, однако месяц за месяцем возникала то одна проблема, то другая, все это замедляло ход работ и к концу марта стало ясно, что мы сможем разместить свою технику на положенных ей местах самое раннее в мае!
И опять нас выручил профессор Черитон, который также планировал переоснастить графическими рабочими станциями свою учебную лабораторию, расположенную в другом здании. Поскольку компьютеры для его лаборатории еще не были получены, мы поставили там свою технику с тем, что часть времени будем использовать ее мы, а оставшуюся часть — лаборатория проф. Черитона. Более того, в этой лаборатории был и новый компьютер VAX, находившийся в соседнем помещении.
События тогда развивались так. 24 марта я кончил писать программы для подмножества METAFONT’a, которого, на мой взгляд, было достаточно для проведения запланированных занятий, но отладку этого подмножества еще не начинал. Первого апреля я получил первый успешный вывод результатов при решении небольшой тестовой задачи. Тогда же МЕТА FONT впервые правильно отобразил проектируемую литеру на экране дисплея. (Все это делалось на Зб-битовой машине SAIL, на которой я выполнял все работы по Т^Х’у и МЕТА-
FONT’y.) На следующий день, 2 апреля, я узнал о возможности воспользоваться лабораторией Черитона для проведения наших занятий; помещение было еще без мебели, компьютеров, кондиционеров, но решением этой проблемы занялись Дэвид Фукс вместе с другими сотрудниками. 3 апреля Перу Ботнеру удалось успешно перенести МЕТА FONT с машины SAIL на рабочую станцию SUN, используя ОС V-System. А 4 апреля был уже первый день занятий в нашей группе.
В ОС V-Kernel до того момента работали только хакеры, так что подходящего руководства по ее использованию для новичков не было, более того, никто из нас, кроме Пера, не знал, как этой системой пользоваться. На помощь пришел Артур Сэмюэль и начал составлять краткое руководство по системе V-Kernel. Тем временем мы провели специальное совещание с техническим персоналом Станфордской телевизионной сети, поскольку у нас не было другого подходящего способа показывать работу METAFONT’a в лекционной аудитории, кроме как ретранслировать туда «картинку» с экрана монитора с помощью телевизионных средств, б апреля я начал писать весьма длинную программу GFtoDVI, которая необходима для получения пробных распечаток результатов работы METAFONT’a. Я знал, что для завершения этой программы мне потребуется не менее двух недель. Линн Раглес в то же время разрабатывал другую утилиту, GFtoQMS, которая генерировала шрифты, пригодные для нового лазерного принтера, только что нами полученного; Линн добился уже определенных успехов в своей работе. (Однако этот принтер все еще не был установлен.)
Бигелоу и Саутолл хорошо знали, что будет чудом, если все компьютеры окажутся на своих местах к требуемому времени. Поэтому они приготовились «импровизировать» до тех пор, пока я не буду готов. Я читал свою первую лекцию в пятницу, 13 апреля, и в этот же день Линну удалось получить на нашем лазерном принтере QMS первую распечатку символа, подготовленного METAFONT’om. Первые домашние работы студентов, как это уже отмечалось выше, не требовали использования компьютера, так что вышло вполне естественно, что задания для работы с компьютером не выдавались до 27 апреля.
В мае переживаний у нас тоже хватало: компьютеры часто отказывали из-за неудовлетворительной работы кондиционеров, несколько недель потребовалось на их установку и отладку, в избытке было и проблем с программным обеспечением, особенно с новой версией METAFONT’a, все еще находившегося в работе. Однако студенты все хорошо понимали и с терпением относились к поистине нетерпимым условиям. Я вознаградил их отменой планировавшейся шестой домашней работы. Мои ассистенты Дэн Миллс и Дэйв Зигель исполнили свои обязанности безупречно и сделали все, чтобы занятия шли настолько гладко, насколько это было вообще возможно в течение этих девяти недель.
Наконец наш курс дошел до славного финиша, который мы отметили вылазкой в Сан-Франциско. Мы устроили пикник на Бульваре Шрифтов (Font Boulevard), затем посетили прелестную словолитню МакКензи—Харриса (MacKenzie—Harris) и дизайнерскую студию Бигелоу и Холмса (Bigelow & Holmes). Лучше всего идею празднования этого памятного дня можно выра-
зить такой картиной «курсивного» шрифта, который мы все вместе изобразили сразу после ланча:
Фото Джил Кнут
Второй ряд (слева направо): Пер Ботнер, Брюс Фляйшер, Джон Хобби, Рейчел Хьюсон, Дон Кнут, Нини Биллавала Арни Олдс, Элан Спрэдженс, Брюс Либан, Джо Таттл, Стен Осборн, Дейв Зигель, Дэвид Фукс, Первый ряд: Джон Хершбергер, Жак Дезарменьен, Дикран Карагезян, Нори Токусу, Малкольм Браун, Канчи Гопинат, Павел Кертис, Жан-Люк Бонтен, Рената Бил, Куань-Чун Парк, Ричард Саутолл, Майкл Вайзенберг.
Примечание переводчика
Перевод текста со с. 384.
Шрифты во все времена бывали как лучшего так и худшего качества, равно как и способы их использования. Лучшие шрифты и лучшие способы выбирал для себя образованный печатник, который придерживался стандартов и традиций, руководствовался вкусом и чувством меры, учитывая при этом технические достижения своего времени. Такой человек превращал печатное дело в искусство.
Примитивные шрифты и способы привлекали печатников, игнорирующих стандарты и обеспокоенных исключительно коммерческой выгодой. Эти превращали печатное дело в простое ремесло
Национальная полиграфия бывала хорошей или плохой в зависимости от того, какая из описанных категорий специалистов брала верх. И в наши дни любой мыслящий печатник может развить свой вкус, так выбрать шрифты и так научиться ими пользоваться, что в его руках печатное дело станет искусством, а не ремеслом.
Это не означает, как мог бы подумать человек сентиментальный, что некий демонический дух теперь витает повсюду и препятствует тому, чтобы работа выполнялась хорошо. Прежние времена не были так уж хороши, человеческая природа с тех пор мало изменилась, да и современность не стоит особенно демонизировать. Но всегда было и остается упорство в следовании принципам. Принципы человека тех времен кажутся простыми и овеянными славой. И нам не верится, что мы тоже способны на нечто подобное.
Меташрифт Punk
[Первоначально опубликовано в TUGboat 9 (1988), 152-168.]
В феврале 1985 г. Герард и Мариан Унгер в течение девяти вечеров прочитали в Станфорде цикл лекций, где давался обзор эволюции стилей в искусстве, архитектуре, одежде, проектировании промышленных образцов и издательском деле на протяжении последних 75 лет. Эти лекции были особенно интересны тем, что в них изменения в полиграфическом оформлении увязывались с аналогичными и современными им веяниями в других областях. Унгерами был показан такой замечательный факт: издательское дело в части стилей отстает от изменений в других областях примерно на десять лет.
Когда я проснулся утром в день их последней лекции, то внезапно осознал, что имеется очевидное следствие из того, что было рассказано в предыдущие восемь вечеров. Как раз подошло время разработать шрифтовую гарнитуру, отражающую идеи, возникшие в конце 70-х годов! Более того, мне стало совершенно ясно, на каких принципах могло бы быть основано проектирование такого шрифта, поскольку лекторы указали на значительное сходство между «панковской» графикой, показанной в Лондоне, и некоторыми кружевными узорами из линий и точек на обивке «панковской» мебели из Италии.
«Линейно-точечные кружева» для реализации их с помощью METAFONT’a тривиально просты, поэтому я решил сделать новое семейство «сверхмодных» шрифтов, названное Р U ЦК. Во второй половине дня я провел несколько приятных часов за компьютерным терминалом и к вечеру, к моменту начала лекции, подготовил подарок-сувенир собравшимся — фрагмент лекций Унгеров, отпечатанный на лазерном принтере шрифтами FUЦ1^4Л и РUЦК26.
Идея шрифтовой гарнитуры Fb^k состояла в том, чтобы взять в качестве начального приближения более или менее традиционные по форме буквы, но дать задание METAFONT’y сместить ключевые точки букв на случайно заданные расстояния, с тем чтобы внести в буквы элементы «хаотичности». Для примера ниже приведен ряд текстовых фрагментов, набранных с использованием нескольких разновидностей шрифтов Р U ЦК:
АМРЕЕбН1ЛС1Щ1б!’фК5‘[11УПП А&воеРвни^имно^взтмит 612$ ^т? U^bfe^nHSl д^аи''46в"_. ..х-.И^'^т^
ЧимгнР said Artie. His FAee was red and не was certainly flustered. 'Jr ’d ье a dead lucky thins if some more reorie around the sHOf ’d chanse a little. They could n’t ье any bunker ’n they are now.' [1]
AbCP£f5HlJKlMN6f^5TUVVXYZ AbcDEf«никиммгФРзть vmxxx шит дог^аипишй лв^жи'^’- •
г
A Forked vein besAw to swell in Stade’s forehead. ... His voice ьесаме tersIjasive awn. 'Listen, Qutman, Wfi’VE AbSOLtJTELY SOT TO 6JVE THEM A VICTIM. THERE5» no way опт of it. (et’s «ive them the гьнк/ Ke NODDED TLEA3ANTLY AT THE bOY IN THE DOORWAY. [2]
AbCPEFSHUKWBf^STUVWni AUueF SHIJKLMNOF^RSTUVWXY^ ШШЬШ МАОЕТтм^и,'-'и-' 1й112П2221£!М^1^ЖА1№[ Pistol. VbineA This funk is owe of Cupid's caRRisRs. Clap on mors sails, puRsufi; up WITH YOUR FISHTS; Give FiRe! Sue is my FRiLe, oR oceAN wueLM гнем all! (fz/r.) и
/i^WKWWSTUmn Аьсбе?
eHijKLMNOrsRSTUvwxvx И2345Л8У/f/
Винк rock is тне setigRic теки por THS UTgST MUSICAL SARBA65 BRgD BY OUR TROUBLSB CULTURE, BRITISH AUD ^MgRlcAH. ...
Johnny Rottsn and ths Sex fisroLs ARe FUNKS. TveY SINS 'Anarchy in тне UK/ which eNDs with a seReAM: 'PesTRov/ Clash and Pamnbd arc
OTHeR SANDS.
... fuNK will ^ADfi. Its AtOt.O<8t8T8 ARC LUDICROUS. TweRe arc ways to гротевт Аьоит тне rtiTRtv FAoes of ьотн ror and sooieTY without ReiAtSlNS INTO bARbARtSM. fUNK IS ANTl-UFB, ANTI-HUMANITY, w
ABCDEf6HUK(MN0r<lRSTUVVXYZ
AbODeF«HUKLMNor<5R8TUVWXYZ 612845^789 ГОГДдД=ПКТИгр
’•'^SHRft—* 0ПШ/<«>’•'^SHRft—* 0ПШ/<«>PUNKBX20PUNKBX20
’•'^SHRft—* 0ПШ/<«>
’•'^SHRft—* 0ПШ/<«>
PUNKBX20
PUNKBX20
В то время, когда я «спроектировал» эти шрифты, я как раз начал работу над окончательной версией семейства шрифтов Computer Modern на основе приведения версии Almost Computer Modern в соответствие с соглашениями, принятыми в METAFONT84. В тот момент отлаживались буквы ‘А’, ‘В’, ‘С’ и ‘D’, скоро я планировал заняться буквой ‘Е’, но чувствовал, что надо устроить себе передышку. И я сделал наброски некоторых «панковских» форм, вроде
этих:этих:
этих:
этих:
Я использовал большой лист бумаги для графиков («миллиметровку»); приведенные иллюстрации уменьшены до 25% от их первоначального размера. Честно говоря, я не знал, что с этими набросками делать дальше, для чего они могут пригодиться.
В час дня я включил компьютер и стал составлять простой базовый файл. Сделать надо было не так уж и много, поскольку plain МЕТА FONT уже имел в своем составе большинство основных программ, так что через двадцать минут я уже получил первую пробную распечатку, которая выглядела так:
METAFONT output 1985.02.28:1320 Pagel Character 65METAFONT output 1985.02.28:1320 Pagel Character 65(40% исходного размера)(40% исходного размера)
METAFONT output 1985.02.28:1320 Pagel Character 65
METAFONT output 1985.02.28:1320 Pagel Character 65
(40% исходного размера)
(40% исходного размера)
Похоже, что букву ‘А’ можно было считать разработанной, и я перешел к подготовке METAFONT’obckhx программ для остальных букв, от ‘в’ до 7’. Я решил сначала ввести все эти программы, а уже потом делать пробные распечатки: я просто перевел подготовленные эскизы букв в METAFONT’obckhc конструкции, выполняя всю работу сразу с клавиатуры, без промежуточной записи на бумаге. Производить точные измерения необходимости не было, поскольку все равно координаты узловых точек букв потом подвергались воздействию случайных возмущений. Таким путем я быстро получил набор соглашений для создаваемого шрифта и дальше темп его разработки ограничивался только моей скоростью ввода символов с клавиатуры. Эскизы букв от ‘V’ до ‘Z’ мне даже и не понадобились, так как алгебраические формулировки для них представить было довольно просто после того, как я проделал аналогичную работу для предшествовавшей 21 буквы. В 3 ч 04 мин ввод был завершен и я смог запустить METAFONT и получить пробные распечатки всех 26 букв в заглавном (прописном) варианте. Сделал я также и новые знаки препинания (точку, запятую, открывающую и закрывающую одинарную и двойную кавычки).
Разумеется, во введенном программном коде встречались и ошибки. Например, первые несколько букв в начальной распечатке выглядели так:
(А первый вариант буквы ‘G’ я даже и не рискую вам показывать.) Но к 4 часам дня я уже был готов отпечатать первый пробный текстовый фрагмент:
W'r^ovwMNWtP^ MAPJAiT1^ Ш1№ ШТ ад, Ш РП1ШР
В этот момент возникло неожиданное «осложнение», несколько замедлившее работу. Буквы этого шрифта имели необычные значения некоторых характеристик, не возникавшие до сих пор в GF-файлах. Из-за этого программа перевода МЕТАРОЫТ’овского кода в формат нашего лазерного принтера выдала ошибку, когда я попытался сделать тестовую распечатку шрифтом кегля 40 pt. Я сделал копии файлов, вызвавших возникновение данной ситуации, чтобы разобраться с ними позднее, а пока просто обошел эту ошибку выбором другого стартового значения для случайной последовательности и повторной генерации шрифта. Потратив еще примерно полчаса на подгонку разных параметров шрифта (и снизив степень случайного разброса), я мог пойти домой пообедать.
Во время обеда мне пришла мысль, что в памятном сувенире для этого
вечера надо бы отпечатать и дату его проведения. Поэтому я побыстрее закончил обед, набросал эскизы для набора цифр и побежал опять в свой кабинет.
Скоро у меня уже был готов шрифт из 43 литер —26 букв, 10 цифр и 7 знаков препинания. Прекрасно! К семи часам вечера я успел сделать по листку-сувениру для всех присутствующих, раскалив при этом добела нашу копировальную машину.
Примерно через год я попал в Бостонский музей изящных искусств и наткнулся там на рисунок, сделанный Пикассо в 1924 г. [5] (см. иллюстрацию справа). Я с изумлением обнаружил, что идея, положенная в основу шрифтов F^Hk) вовсе не десятилетней давности, а ше-
стидеслтилетней. Еще одно замечательное подтверждение жизненности по крайней мере части проекта РUНК я получил в октябре 1986 г., когда увидел в Paris Metro(!) рекламное объявление в таком вот типографском исполнении:
В феврале 1987 г. я решил довести первоначальный 43-символьный набор до полного ТеХ’овского комплекта символов. Дополнительная работа по программированию недостающих символов не заняла много времени, поскольку строчные символы я формировал как «малую капитель», так что для ввода в компьютер необходимых инструкций по каждому из символов нужно было всего одну-две минуты. Около трети из вновь введенных символов пришлось подправить, после того, как я увидел, что получается на пробных распечатках — некоторые из них оказались слишком «панковскими», а некоторые наоборот — недостаточно «панковскими». Треть исправленных в первой итерации символов пришлось править опять и так несколько раз. И вот примерно после шести часов дополнительной работы 128-символьный меташрифт Р U Н 1< можно было считать завершенным, его уже можно было использовать, если бы у кого-то возникла такая потребность.
В оставшейся части данной работы я даю подробную информацию по МЕТАРОЬГГ’овскому коду гарнитуры FU НК, поскольку его можно рассматривать как кратчайший нетривиальный пример комплекта METAFONT’obckhx программ, порождающих все 128 символов, используемых plain Т^Х’ом.
Как объясняется в приложении Е книги [4], рассматриваемые программы содержатся в файлах нескольких видов. К их числу относятся: файлы параметров, задающие требуемое шрифтовое семейство; файлы-драйверы, управляющие большей частью процесса порождения шрифта; программные файлы, содержащие код для отдельных символов. Базовый файл мне не понадобился, поскольку все потребовавшиеся макро для этих шрифтов оказалось возможным включить в файл-драйвер.
Вот какой вид имеет типичный файл параметров, именуемый PUNK20.MF:
% Шрифт PUNK кегля 20 pt:
designsize := 20pt*; font „identifier := "PUNK";
ht* := 14pt*\
% высота литер
и* := ^/gpt*\
% единичная ширина
s* := 2pt*\
% расстояние от крайней точки буквы до границы бокса
рх* := 3pt*;
% толщина пера по горизонтали
ру* := .6р^;
% толщина пера по вертикали
dot* := 2.7pt*;
% диаметр точек
de-u* := .bpt*;
% среднеквадратич. отклонение «панковских» точек
slant := 0;
% наклон
seed := 2.71828;
% стартовое значение для генератора случайных чисел
input PUNK
% переключение на файл-драйвер
Назначение данного файла — конкретизировать метапроект применительно к выбранным размерам и жирностям. Параметры для пяти шрифтов, использованных в примерах, приведенных выше, имеют следующие значения:
PUNK1O
PUNK12
PUNK20
PUNKSL20
PUNKBX20
designsize
lOpt*
12pt*
20pt*
20pt*
20pt*
font „identifier
"PUNK"
"PUNK"
"PUNK"
"PUNKSL"
"PUNKBX"
ht*
7pt#
3.4pt*
14pt*
14pt#
14pt*
и*
3pt*
УэР&
4/gpt*
3pt*
s*
1.2pt*
1.4pt*
2pt*
2pt#
2.2pt*
рх*
.6pt#
.75pt*
3pt*
.8pt#
2pt*
РУ*
,5pi*
32pt*
3pt*
,6pt*
13pt*
dot*
1.3pi#
l.Gpt*
2.7pt*
2.7pt#
33pt*
dev*
.3pt*
36pt*
3pt*
,5pf*
3pt*
slant
0
0
0
Vs
0
seed
sqrt 2
sqrt 3
2.71828
3.14159
0.57722
Наиболее сложным для написания был файл-драйвер PUNK.MF, поскольку в нем содержится суть проекта. Различные части этого файла росли от шага к шагу проекта. Например, последние параметры макро Ъeginpunkchar’ были добавлены после того, как я обнаружил, что некоторые символы недостаточно толерантны к случайным отклонениям в их точках, они вели себя не так, как
положено обычным символам; слишком большие перемещения приводили к тому, что эти символы переставали быть распознаваемыми:
% это-гьцк,
% меташрифт, создание которого вдохновлено лекциями
% Герарда и Мариан Унгеров, февраль 1985 г.
mode_setup;
randomseed := seed;
define_pixels(u, dev);
define_blacker_pixels(pr, pp, dot);
define _whole_pixels (s);
xoffset := s;
pickup pencircle xscaled px yscaled py; punk-pen :— savepen;
pickup pencircle scaled dot; def .pen .path _;
path dot _pen .path; dot .pen .path := currentpen .path;
currenttransform := identity slanted slant yscaled aspect .ratio;
def beginpunkchar(expr c, n, h, v) =% код с; ширина равна n единицам
hdev := h* dev;% модифицировать горизонтальную и
vdev := v * dev;% вертикальную составляющие отклонения
beginchar (c, п * и*, ht*, 0);
italcorr ht* * slant; pickup punk .pen enddef;
extra.endchar := extra.endchar &, "w:=w+2s;charwd:=charwd+2s#";
def | = transformed currenttransform enddef;
def makebox (text rule) =
for у = 0, h:
rule((—s, y)^ (w — s, y)]); endfor% горизонтали
for x = —s, 0, w — 2s, w — s: rule((x, 0)T, (z, h)^); endfor% вертикали
enddef;
rulepen := pensquare;
vardef pp expr z = z + (hdev * normaldeviate, vdev * normaldeviate) enddef; def pd expr z = addto-currentpicture contour
dot-pen.path shifted Zt_ withpen penspeck enddef;% рисование точки
input PUNKL% прописные буквы
input PUNKAE% прописные Ж, (E, 0
input PUNKG% прописные греческие буквы
input PUNKP% знаки препинания
input PUNKD% цифры
input PUNKA% акценты
ht* := .6ht*; dev := .7dev;
input PUNKSL% строчные спецзнаки
extra-beginchar := extra-beginchar k, "charcode: =charcode+32;11; input PUNKL% строчные буквы
extra-beginchar := extra-beginchar &: "charcode: =charcode-35;"; input PUNKAE% строчные ae, oe, 0
font „slant := slant;
font „quad := IBu* + 2 s*;
font_normal_space := 9u* + 2s*;
font_normal_stretch := 6it*;
font_normal_shrink := 4 it*;
font_x-height := ht*\
font_coding_scheme := "TeX text without f-ligatures"; end
Указанные 128 символов, формируемые драйвером PUNK.MF, размещались в кодовой таблице в тех же самых позициях, что и в шрифтах наподобие сшгб и cmcsclO, в которых нет f-лигатур. Ниже для примера дается раскладка для шрифта PUNKZ20, похожего на шрифт PUNK20, за тем исключением, что для него dev = 0 (т. е. в нем нет случайности):
'0
'1
'2
'3
4
'5
'6
7
'00х
6
к
T
"Ox
'01 х
(
1
Г
p
:
5
'02х
I
J
V
**
V
"lx
'ОЗх
д
(В
0
A
я
'04х
г
#
< t
b
J
"2x
'05х
1
*
/
'06х
6
1
1
"3x
'07х
8
•
<
•J
?
'Юх
А
5
с
)
6
"4x
'Их
M
6
'12х
)
5
и
V
”5x
'13х
X
у
Z
5
A
•
'Цх
t
А
в
С
D
e
f
5
"6x
'15х
н
I
J
К
L
M
N
0
'16х
г
Q
R
S
T
и
V
W
"7x
Ч7х
X
Y
Z
tr
"8
"9
"А
"В
"C
"D
"E
"F
Давайте взглянем теперь на программные файлы. Первым я написал файл PUNKL.MF, в котором содержатся определения для всех букв, от А до Z:
% «Панковские» буквы
beginpunkchar("A", 13, 1, 2);
^i = рр(1-5и, 0); 2:2 = (.5ш, 1.1/г); 2:3 = pp(w — 1.5u, 0);
pd zi; Pd 2:3; draw 2:1 - - 2:2 - - 2:3;% левая и правая диагонали
za = pp.3[zi, z2); z5 = pp.3[z3, z2];
Pd 2:4; pd2:5; draw24--2s;
endchar;
% горизонтальный штрих
beginpunkchar("B", 12, 1, 1);
^i = РР(2гг, 0); 2:2 = pp(2u, .6/г); 2:3 = pp(2u, /г); pd 2:1; pd 2:3; draw 2^1 -- 2:3;
^1.5 = pp(w - гг, .5p2); 2:2.5 = pp(w - гг, .5[p2, рз]);
draw 2^22^2.52:3;
draw flex(2;2, 2^1.5, zi);
endchar;
% основной штрих
% верхний овал % нижний овал
beginpunkchar("C", 13, 1, 2);
zi = pp(w — 2u, .8/г); Z2 = pp(.6w, /г); 2:3 = pp(u, .5/г);
Z4 = (.6w, 0); Z5 = (w — 2u, .2/г);
pd 2:1; pd 2:5; draw 2^1 .. 2^2 .. 2:3 .. 2:4 .. 2:5;
endchar;
% дуга
beginpunkchar("D", 14, 1, 2);
zi = PP(2it, 0); Z2 = pp(2u, /г); 2:3 = pp(w — u, .6/г);
pd 2^1; Pd Z2\ draw flex(zi, 2:3, 2:2); draw 2^1Z2\
endchar;
% овал
% основной штрих
beginpunkchar("E", 12, .5, 1);
zi = pp(2u, 0); 2:2 = рр(2гг, /г); 2:3 = pp(w — 2.5гг, /г); Z4 = pp(w — 2u, 0);
Pd 2:3; pd Z4\ draw 2:4 -- 2:1 -- 2:2 -- 2:3;% основной штрих и горизонтали
^5 = РР(2гг, .6/г); z^ = pp(w — Згг, .6/г); pd 2^5; pd Z6; draw Z5 - - 2^6;
endchar;
% средняя горизонталь
beginpunkchar("F", 12, .5, 2);
^1 = PP(2w, 0); 2:2 = pp(2u, /г); 2:3 = pp(w — 2u, /г);
Pd zi; pd 2:3; draw z\ -- 2^2 -- 2:3;% основной штрих и верхняя горизонталь
2:5 = PP(2u, .6/г); 2^6 = pp(w — Згг, .6/г); 2:4 = рр.5^6, zg] — (0, Л/г);
ножки и диaгoнaлиpd 2:5; pd 2:5; draw flex(2;5, 2:4, 2:5);
endchar;
% средняя горизонталь
beginpunkchar("G", 13, .5, .5);
zi = pp(w — 2гг, .8/г); 2:2 = рр(.бш, /г); 2:3 = рр^и, .5/г);
^4 = рр(.6ш, 0); 2:5 = (ш — 2гг, 0);
pd zi; draw z\ .. Z2 .. z^ .. Z4 — z^;
% дуга
^ = рр(.5[гг, Ж5], .4/г); pd ^g; pd z5; draw z6--(рр(т5, ре))--^s;% «шпора»
endchar;
beginpunkchar("H", 14, 1, .5);
zi = pp(2u, 0); 2:2 = рр(2гг, /г); 2:3 = pp^w — 2u, 0); Z4 = pp(w — 2гг, /г);
25 = рр(2гц .6/t); z6 = pp(w — 2u, .6/t);
pd Zi; pd 22; Pd 23; pd ^4;
draw zi -- 22; draw flex(23, z^ Z4);% основные штрихи
pd Zb; draw Zb -- z^% горизонтальный штрих
endchar;
beginpunkchar("I", 5, 1, 2);
21 = pp(.5w, 0); Z2 = (.5w, Уз/г); ^3 = (.5w, 2/з/г); ^4 = (.5w, /t);
Pd 21; pd Z4; draw flex(2i, 22, z3, 24);% основной штрих
endchar;
beginpunkchar(" J", 9, 1, 2);
21 = pp(w — 2гц h); Z2 = pp(w — 2гц —Ah); zs = рр^гц 0);
Pd 21; Pd zs; draw z\ - - Z2 - - zs;% дуга
endchar;
beginpunkchar("K", 14, 1,2);
21 = PP(2u, 0); Z2 = pp(2u, h); ^з = рр(2гц 7з^); ^4 = pp(w — 1.5u, /t);
Pd 21; Pd Z2; draw z\ - - Z2;% основной штрих
pd 23; pd Z4; draw zs -- Z4;% верхняя диагональ
2g = pp(w — it, 0); Zb = Уз^з, 2:4];
Pd 2g; draw flex(2s, .8[2i, 2(sV^31 zg]], 2^);% нижняя диагональ
endchar;
beginpunkchar("L", 11, 1, 2);
21 = PP(2u, /t); Z2 = pp(2it, 0); zs = pp(w — 1.5it, 0);
Pd 21; pd zs; draw z\ - - Z2 - - 2:3;% основной и горизонтальный штрихи
endchar;
beginpunkchar("M", 17, .5, 2);
21 = PP(2u, 0); Z2 = pp(2u, /t); zs = pp(.5w, 0);
Z4 = pp(w — 2u, /t); Z5 = pp(w — 2it, 0);
Pd 21; Pd Zb; draw zi - - Z2 - - 23 - - Z4 - - £5;% основной штрих и диагонали
endchar;
beginpunkchar("N", 13, .75, 2);
zi = pp(2it, 0); Z2 = pp(2it, /t); 2:3 = pp(w — 2it, 0); Z4 = pp(w — 2it, /t);
Pd 2^1; Pd 2:4; draw zi -- Z2 -- Z3 -- Z4;% основной штрих и диагонали
endchar;
beginpunkchar("0", 12, .5, 2);
^1 = pp(-5w, /г); Z2 = pp(u, .55/t); zs = pp(.5w, 0); Z4 = pp(w — u, .55/t);
Pd zi; draw zi{Ze/^} .. Z2 .. zs .. Z4 .. z\\% овал
endchar;
beginpunkchar("P", 13, 1, 2);
^1 = PP(2u, 0); Z2 = pp(2it, 1.1/t); zs = pp(2it, .5/t); Z4 = pp(w, .6[рз, P2]);
Pd zi\ Pd zs; draw z\ -- Z2 -- ^4 -- zs;% основной штрих и овал
endchar;
beginpunkchar("Q", 14, .5, 2);
^1 = pp(.5w, /г); Z2 = pp(u, .55/t); zs = pp(.5w, 0); Z4 = pp(w — u, .55/t);
Pd zi; draw Z\{curl 2} .. Z2 .. zs .. 24 .. 2^1;% овал
^5 = pp(.4w, .2/t); Zb = pp(w — it, —.1/t); 2:7 = pp(.5[t5, tg], — .2h);
Pd 2^5; pd Zb; draw Zb -- Z7 -- Zb;% «хвостик»
endchar;
beginpunkchar("R", 16, 1, 2);
^i = PP(^i 0); ^2 = РР^и, hy Z3 = pp(w — u, .6[t/2, 2/4]);
Z4 = РР^гц .bh); Zb = pp(w — 1.5u, 0);
pd zi; pd 2^2; pd Z5;
draw zi -- flex(^2, 2:3, 2:4) -- 2:5;% основной штрих, овал и диагональ
endchar;
beginpunkchar("S", 11, .3, 1);
zi = pp(w — 2u, .9/i); Z2 = pp(.5w, /г); Z3 = PP^^ -7h)\ Z4 = .б^, Z2];
^5 = pp(w — u, .35/i); z6 = pp(.5w, u); Z7 = pp(u, .2h?y
Pd Zi; Pd Z7; draw zi - - Z2 ... Z3 .. Z4 .. Z5 ^6 - - Z7;% штрих
endchar;
beginpunkchar("T", 13, .75, 2);
^1 = PP^i M; Z2 = pp(w — u, hy Z3 = pp(.5w, 0);
pd z\; pd Z2\ pd Z3\ draw zi Z2\
draw .5[zi, 2:2]Z3;
endchar;
% горизонтальный штрих
% основной штрих
beginpunkchar("U", 13, .3, 2);
^1 = PP(2w, h); Z2 = pp(2u, .2h?y Z3 = pp(.5w, 0);
Z4 = pp(w — 2u, .2h)} Z5 = pp(w — 2u, h);
pd 2:1; pd Z5; draw z\Z2 .. • Z3^4 — Z2} ... £4^5;
endchar;
% штрих
beginpunkchar("V", 13, 1, 2);
^1 = pp(l-5u, h); Z2 = pp(.5w, 0); Z3 = pp(w — 1.5u, h);
Pd 2:1; Pd Z3; draw z\ - - Z2 - - 2:3;
endchar;
% диагонали
beginpunkchar("W", 18, 1, 2);
^1 = pp(1.5u, hy, Z2 = pp(.5[Ti, T3], 0); Z3 = pp(.5w, .8/1);
Z4 = рр(.5[тз, T5], 0); Zb = pp(w — 1.5u, h);
Pd ^i? pd Z5; draw zy - - Z2 - - Z3 - - ^4 - - Zb;
endchar;
% диагонали
beginpunkchar("X", 13, 1, 1);
21 = pp(l-5u, /г); Z2 = pp(w — 1.5u, 0); z^ = pp(1.5u, 0);
^4 = pp(w — 2.5it, /г);
Pd z\; pd Z2\ draw z\ -- Z2\% главная диагональ
pd 23; pd Z4\ draw Z3 -- Z4\% пересекающая диагональ
endchar;
beginpunkchar("Y", 13, 1, 2);
^1 = pp(1.5u, hy Z2 = pp(w — 1.5u, h?y Z3 = pp(.5w, .5/i); Z4 = pp(.5w, 0);
pd zi; Pd ^2; Pd Z4\ draw zi -- Z3 -- ^4\% основной штрих и левая диагональ
draw Z2 - - 2:3;% правая диагональ
endchar;
beginpunkchar("Z", 11, 1, 2);
zi = pp(l-5u, hy Z2 = pp(w — 2.5u, /г); Z3 = pp(1.5u, 0);
Z4 = pp^w — 1.5u, 0);
Pd z\; pd Z4\ draw zi -- Z2 -- ^3 -- Z4;% диагонали
endchar;
(Конечно, добавление в строки программы поясняющих их комментариев несколько замедляет работу по вводу текста программы в компьютер. Но если приходится пересматривать тексты программ, то такие комментарии становятся поистине бесценными. По этой причине я с самого начала решил, что комментарии в программах должны быть обязательно.)
Три буквы из рассматриваемого шрифта были помещены в специальный файл, получивший наименование PUNKAE.MF. Причиной этому послужило то, что коды символов этих букв в прописном варианте связаны с соответствующими строчными буквами нестандартным образом:
beginpunkchar (oct 11035", 16, 1, 2);% ТЕ
2i = рр(1-5и, 0); 22 = рр(.6ш, /г); 23 = pp(w — 1.5u, /г);
pd 21; Pd 23; draw 21 - - 22 - - 23;% левая диагональ и верх, горизонталь
Z4 = pp.3[zi, 22]; 25 = рр(.6ш, 0); 26 = pp(w — 2п, .3/г);
Pd 24; pd 2е; draw 24 -- 2е;% средняя горизонталь
27 = pp(w — и, 0); pd 22; pd 27;
draw 22 -- 25 -- 27;% основной штрих и нижняя горизонталь
endchar;
beginpunkchar(oct "036", 18, 1, 2);% (Е
21 = pp(.5w, /г); 22 = pp(ti, .4/г); 23 = рр(.5ш, 0);
Pd zr, draw 21 .. 22 .. {ггрА^з;% овал
24 = pp(w — 1.5u, /г); 25 = pp(w — 2u, Ah); ze = pp^w — u, 0);
Pd 24; pd zg; draw Z4 -- zi -- zs -- ze;% горизонтали и основной штрих
Pd Z5; draw Z5 -- A[z3, 21];% средняя горизонталь
endchar;
beginpunkchar(oct "037", 14, 1, 1);% 0
21 = pp(.5w, h); Z2 = pp(u, .5h); Z3 = pp(.5w, 0); Z4 = pp(w — u, .5h);
25 = pp(w — 2u, l.l/i); ze = pp(2u, — .17г);
Pd 21; Pd ze; draw 21 .. 22 .. 23 .. 24 .. 25 - - 26;% овал и диагональ
endchar;
Имеется также еще один специальный файл с именем PUNKSL.MF, содержащий строчные буквы, для которых нет отвечающих им прописных букв:
beginpunkchar(oct "020", 5, 1, 2);% «бесточечная» I
z\ = рр(.5ш, 0); Z2 = (.5ш, Уз^)) ^з = (.5ш, 2/з7г); Z4 = (.5ш, /г);
Pd 21; Pd24; draw flex(zi, 22,23, 24);% основной штрих
endchar;
beginpunkchar(oct "021", 9, 1, 2);% «бесточечная» J
21 = PP(w — 2u, h); 22 = pp(w — 2u, — Ah); 23 = pp(u, 0);
pd 2i; Pd 23; draw 21 - - 22 - - 23;% дуга
endchar;
beginpunkchar(oct "031", 18, .3, 1);% немецкая «сдвоенная» S
2i = pp(.5w — u, .9h); 22 = pp(73W, h);23 = pp(u, .7h); 24=.6[2б, 22];
25 = pp(.5w, .35/i); 2g = pp(l/aw1 u); 27= pp(u, .2h);
Pd 2i; pd.27; draw 2i -- 22 ... 23 .. 24.. 25 ... 2g -- 27;% левыйштрих
for г = 1 upto 7: z\i + 10] = pp(z[i] shifted (.5w — u, 0)); endfor pd 2ц; pd 217;
406406Компьютерная типография draw 2ц -- 212 ... 213 .. 2ц .. 215 ... 2ц -- Z17; % правый штрих endchar;
406
406
Прописные греческие буквы в файле PUNKG. MF могут несколько отличаться по стилю от тех, что содержатся в файле PUNKL, поскольку написал я их двумя годами позднее. Насколько заметна эта разница?
beginpunkchar(oct "ООО", 11, 1, 2);
2i = рр(2гг, 0); 22 = рр(2гг, /г); 23 = pp(w — 1.5гг, /г);
% Г
pd Z1; Pd 23; draw 21 -- 22 -- 23;% основной и горизонтальный штрихи
endchar;
beginpunkchar(oct "OOI", 15, 1, 2);
% A
zi = pp(u, 0); Z2 = pp(.5w, /г); zs = pp(w — u, 0);
pd 2i; draw 21 - - 22 .. tension 5 .. 23 .. tension 5 .. 21;
% треугольник
endchar;
beginpunkchar(oct "002", 15, .5, 2);
%O
zi = pp(-5w, /г); 22 = рр(гг, .6/г); 23 = pp(.5w, 0); 24 = pp(w
— гг, .6/г);
pd 2i; draw 21 .. tension .8 .. 22 .. 23 .. 24 .. tension .8 .. 21;
% овал
Z5 = рр(Ж2 + 2u, .4/l); Ze = pp(x4 — 2ti, .4/l);
pd 25; pd 2e; draw Z5 -- 26;
endchar;
% средняя горизонталь
beginpunkchar(oct "003", 12, 1, 2);
zi = PP(^) 0); 22 = pp(.5w, /г); 23 = pp(w — гг, 0);
% Л
Pd zi; pd 23; draw zi - - 22 - - 23; endchar;
% диагонали
beginpunkchar(oct "004", 12, 1, 1);
% 5
^1 = PP(^> ^); ^2 = pp(w — us /г); pd Zi; Pd 22\ draw 21 -- 22\
% верхняя горизонталь
^3 = РР(2гг, .55/г); Z4 = pp(w — 2гг, .55/г);
Pd ^3‘, pd 24; draw Z3 -- 24;
% средняя горизонталь
^5 = рр(гг, 0); 25 = pp(w — и, 0);
Pd 25; pd ^6*, draw 25 -- 25;
endchar;
% нижняя горизонталь
beginpunkchar(oct "005", 13, 1, .5);
^1 = pp(l-5u, 0); 22 = рр(1.5гг, /г); 23 = pp(w — 1.5гг, /г);
24 = pp(w — 1.5u, 0);
%П
Pd ^1; Pd 2:4; draw zi -- 22 -- 23 -- 24\% основные и горизонтальный штрихи
endchar;
beginpunkchar(oct "006", 13, 1, 1);
^1 — PP(w — гг, /г); 22 = рр(гг, /г); гз = pp(.5w — гг, .5/г);
24 = рр(гг, 0); 25 = pp(w — гг, 0);
% Е
pd -^i i Pd Z5; draw 2i -- 22{.5[^4, 25] — 22} .. 23 --24 -- 25;
% горизонтали % и диагонали
endchar;
beginpunkchar(oct "007", 15, 1, .5);
% Т
21 = рр(гг, .8/г); 22 = pp(.3w, /г); 23 = рр(.5ш, .5/г); 24 = рр(.5ш, 0);
pd ^i i Pd 24\ draw zi .. 22 .. tension ‘I .. 23 — 24; % левая дуга и основной штрих
2:5 = pp(w — гг, .8/г); 25 = pp(.7w, /г);
pd гь; draw 2:5 .. 2^ .. tension 2 .. {2:4 — гз}гз;% правая дуга
endchar;
beginpunkchar(oct "010", 13, 1, 2);% Ф
zi = pp(.5w, /1); 2:2 = pp(.5w, 0); pd zi; pd 2:2; draw zi - - Z2;% основной штрих
^з = pp(.5w, 2/3/1); ^4 = pp(u, ShY Zb = pp(.5w, V4^); ^6 = pp(w — u, .5/i);
Pd 2:3; draw Z3 .. Z4 .. Z5 .. zg .. £3;% овал
endchar;
beginpunkchar(oct "011", 14, 1, 1);% Ф
zi = pp(-5w, h); 2:2 = pp(.5w, 0); pd zi; pd Z2; draw Zi -- Z2;% основной штрих
Z3 = рр(л, .8/1); Z4 = pp(.5w, .2/i); 2:5 = pp(w — u, .8/1);
pd Z3; pd Z5;
draw Z3{.4[zi, Z2\ — Z3} .. Z4 {right} .. {zb — .4[zi, Z2]}zb;% штрих
endchar;
beginpunkchar(oct "012", 13, 1, 2);% Q
zi = PP^^ 0); Z2 = pp(V3W, 0); Z3 = pp(u, 2/3/i); ^4 = pp(.5w, /г);
2:5 = pp(w — u, 2/3/1); ze = pp^/aw, 0); Z7 = pp(w — u, 0);
Pd ^1; Pd ^7;
draw zi -- Z2{up} .. Z3 .. Z4 • • Z5 .. {down}z6 -- Z7;% овал и горизонтали
endchar;
Следующий программный файл, PUNKD.MF, определяет десять «панковских» цифр. Я очень спешил, когда вводил с клавиатуры текст данного файла, поэтому комментарии в конце строк получились весьма лаконичными:
beginpunkchar("0", 9, .5, 1);
^1 = рр(.5ш, /г); Z2 = рр^^ -55Н); Z3 = рр(.5ш, 0); Z4 = pp(w — и, .55/г);
Pd zi; draw zi{curl 2} .. Z2 .. Z3 .. Z4 .. zi;% овал
endchar;
beginpunkchar("l", 9, .3, 1);
^1 = PP(2u, .7/i); Z2 = pp(.6w, /г); Z3 = pp(.6w, 0);
pd 2^1; pd Z3; draw zi -- Z2 -- Z3;% засечка и основной штрих
endchar;
beginpunkchar("2", 9, 1, 1);
^1 = PP(2u, .7/г); Z2 = pp(.5w, /г); Z3 = pp(w — u, .6/1);
^4 = pp(u, 0); Z5 = pp(w — 2u, 0);
Pd ^1; Pd Zb; draw zi .. Z2 .. Z3 .. Z4 - - Zb;% штрих
endchar;
beginpunkchar("3", 9, .5, .5);
^1 = PP(2u, .7/г); Z2 = pp(.5w, /г); Z3 = pp(w — u, .5[p2, 2/4]);
Z4 = pp(.5w — u, .55/i); Zb = pp(w — u, .5[p4, рб]);
2^6 = pp(.5w, 0); Z7 = pp(1.5u, .2/г);
pd ^1; pd Z7\ draw z\ .. Z2 .. Z3 .. Z4 & Z4 .. Zb .. Ze .. Z7;% дуги
endchar;
beginpunkchar("4", 9, 1, 1);
^1 = PP(w — u, .3/i); Z2 = pp(u, .3/i); Z3 = pp(2/3W, /г); Z4 = pp(2/3W, 0);
Pd ^1; Pd 2:4; draw Zi -- Z2 -- Z3 -- Z4\% основной штрих и диагонали
endchar;
beginpunkchar ("5", 9, .5, .5);zi = pp(w — 2и, h); 2:2 = рр(2и, /i); гз = рр(^, -7/г);^4 = pp(w — и, .5[рз, Ps]); ^5 = рр(.5ш, 0); Ze = рр(и, .2/г);pd zi; pd ze; draw z\ -- Z2 -- Z3 .. z^ .. ze .. ze;endchar;beginpunkchar ("6", 9, 1, 1);zi = PP^^w, h); Z2 = pp(u, .37г); Z3 = pp(.5w, 0);^4 = pp(w — u, .3h); ze = pp(.6w, .6/i); ze = pp.z 2;Pd Zi; Pd zq ; draw z\ .. Z2 .. Z3 .. ^4 .. ze - - ze ;endchar;beginpunkchar ("7", 9, .5, 1);zi = pp(2u, /г); ^2 = pp(w — .5u, h); Z3 = pp(.4w, 0);Pd zi; Pd 2:3; draw zi -- Z2 8г Z2 .. Z3{down};endchar;beginpunkchar ("8", 9, .5, .5);z\ = pp(.5w, /г); Z2 = pp(u, .5[pi, рз]); ^з = pp(.5w, .6/2,);z<x. = pp(w - и, .5[рз, ps]); ^5 = pp(.5w, 0);ze = pp(u, .5[p5, рз]); z7 = pp(w - u, .5[pi, p3]);Pd zi; draw z\{curl 8} .. Z2 .. Z3 .. Z4 .. Ze .. Ze .. Z3 .. z7 .. zi; endchar;beginpunkchar("9", 9, 1, 1);zi = pp( Vaw, 0); Z2 = pp(w — u, .lh}; Z3 = pp(.5w, /г);Z4 = pp(u, .7h); Z5 = pp(.5w, .4/i);Pd 2:1; Pd Z5; draw zi .. Z2 .. Z3 .. z^ .. Ze;endchar;beginpunkchar ("5", 9, .5, .5);zi = pp(w — 2и, h); 2:2 = рр(2и, /i); гз = рр(^, -7/г);^4 = pp(w — и, .5[рз, Ps]); ^5 = рр(.5ш, 0); Ze = рр(и, .2/г);pd zi; pd ze; draw z\ -- Z2 -- Z3 .. z^ .. ze .. ze;endchar;beginpunkchar ("6", 9, 1, 1);zi = PP^^w, h); Z2 = pp(u, .37г); Z3 = pp(.5w, 0);^4 = pp(w — u, .3h); ze = pp(.6w, .6/i); ze = pp.z 2;Pd Zi; Pd zq ; draw z\ .. Z2 .. Z3 .. ^4 .. ze - - ze ;endchar;beginpunkchar ("7", 9, .5, 1);zi = pp(2u, /г); ^2 = pp(w — .5u, h); Z3 = pp(.4w, 0);Pd zi; Pd 2:3; draw zi -- Z2 8г Z2 .. Z3{down};endchar;beginpunkchar ("8", 9, .5, .5);z\ = pp(.5w, /г); Z2 = pp(u, .5[pi, рз]); ^з = pp(.5w, .6/2,);z<x. = pp(w - и, .5[рз, ps]); ^5 = pp(.5w, 0);ze = pp(u, .5[p5, рз]); z7 = pp(w - u, .5[pi, p3]);Pd zi; draw z\{curl 8} .. Z2 .. Z3 .. Z4 .. Ze .. Ze .. Z3 .. z7 .. zi; endchar;beginpunkchar("9", 9, 1, 1);zi = pp( Vaw, 0); Z2 = pp(w — u, .lh}; Z3 = pp(.5w, /г);Z4 = pp(u, .7h); Z5 = pp(.5w, .4/i);Pd 2:1; Pd Z5; draw zi .. Z2 .. Z3 .. z^ .. Ze;endchar;% штрих% штрих% штрих% штрих% штрих% штрих% штрих% штрих% штрих% штрих
beginpunkchar ("5", 9, .5, .5);
zi = pp(w — 2и, h); 2:2 = рр(2и, /i); гз = рр(^, -7/г);
^4 = pp(w — и, .5[рз, Ps]); ^5 = рр(.5ш, 0); Ze = рр(и, .2/г);
pd zi; pd ze; draw z\ -- Z2 -- Z3 .. z^ .. ze .. ze;
endchar;
beginpunkchar ("6", 9, 1, 1);
zi = PP^^w, h); Z2 = pp(u, .37г); Z3 = pp(.5w, 0);
^4 = pp(w — u, .3h); ze = pp(.6w, .6/i); ze = pp.z 2;
Pd Zi; Pd zq ; draw z\ .. Z2 .. Z3 .. ^4 .. ze - - ze ;
endchar;
beginpunkchar ("7", 9, .5, 1);
zi = pp(2u, /г); ^2 = pp(w — .5u, h); Z3 = pp(.4w, 0);
Pd zi; Pd 2:3; draw zi -- Z2 8г Z2 .. Z3{down};
endchar;
beginpunkchar ("8", 9, .5, .5);
z\ = pp(.5w, /г); Z2 = pp(u, .5[pi, рз]); ^з = pp(.5w, .6/2,);
z<x. = pp(w - и, .5[рз, ps]); ^5 = pp(.5w, 0);
ze = pp(u, .5[p5, рз]); z7 = pp(w - u, .5[pi, p3]);
Pd zi; draw z\{curl 8} .. Z2 .. Z3 .. Z4 .. Ze .. Ze .. Z3 .. z7 .. zi; endchar;
beginpunkchar("9", 9, 1, 1);
zi = pp( Vaw, 0); Z2 = pp(w — u, .lh}; Z3 = pp(.5w, /г);
Z4 = pp(u, .7h); Z5 = pp(.5w, .4/i);
Pd 2:1; Pd Z5; draw zi .. Z2 .. Z3 .. z^ .. Ze;
endchar;
beginpunkchar ("5", 9, .5, .5);
zi = pp(w — 2и, h); 2:2 = рр(2и, /i); гз = рр(^, -7/г);
^4 = pp(w — и, .5[рз, Ps]); ^5 = рр(.5ш, 0); Ze = рр(и, .2/г);
pd zi; pd ze; draw z\ -- Z2 -- Z3 .. z^ .. ze .. ze;
endchar;
beginpunkchar ("6", 9, 1, 1);
zi = PP^^w, h); Z2 = pp(u, .37г); Z3 = pp(.5w, 0);
^4 = pp(w — u, .3h); ze = pp(.6w, .6/i); ze = pp.z 2;
Pd Zi; Pd zq ; draw z\ .. Z2 .. Z3 .. ^4 .. ze - - ze ;
endchar;
beginpunkchar ("7", 9, .5, 1);
zi = pp(2u, /г); ^2 = pp(w — .5u, h); Z3 = pp(.4w, 0);
Pd zi; Pd 2:3; draw zi -- Z2 8г Z2 .. Z3{down};
endchar;
beginpunkchar ("8", 9, .5, .5);
z\ = pp(.5w, /г); Z2 = pp(u, .5[pi, рз]); ^з = pp(.5w, .6/2,);
z<x. = pp(w - и, .5[рз, ps]); ^5 = pp(.5w, 0);
ze = pp(u, .5[p5, рз]); z7 = pp(w - u, .5[pi, p3]);
Pd zi; draw z\{curl 8} .. Z2 .. Z3 .. Z4 .. Ze .. Ze .. Z3 .. z7 .. zi; endchar;
beginpunkchar("9", 9, 1, 1);
zi = pp( Vaw, 0); Z2 = pp(w — u, .lh}; Z3 = pp(.5w, /г);
Z4 = pp(u, .7h); Z5 = pp(.5w, .4/i);
Pd 2:1; Pd Z5; draw zi .. Z2 .. Z3 .. z^ .. Ze;
endchar;
% штрих
% штрих
% штрих
% штрих
% штрих
% штрих
% штрих
% штрих
% штрих
% штрих
Программный файл PUNKP.MF содержит определения «панковских» символов и знаков препинания. Написать эти определения было едва ли не труднее всего, несмотря на то, что большинство из этих знаков очень просты —но зато их ужасно (darn) много.
beginpunkchar(" . ", 5, 1, 2);
pd pp(.5w, 0); endchar;
% точка
beginpunkchar(" , ", 5, .5, .5);
zi = pp(.5w, 0); Z2 = pp(w — u, — .l/i);
pd zi; pd Z3; draw z\ -- Z2 -- Z3;
endchar;
Z3 = pp(.5w, — .3h);
% штрих
beginpunkchar(":", 5, 1, .5); pd pp(.5w, 0); pd pp(.5w, .4/i); endchar;
% точки
beginpunkchar(";", 5, .5, .5);
z\ = pp(.5w, 0); Z2 = pp(w — u, — .l/i);
pd zi; pd Z3; draw z\ -- Z2 -- Z3;
pd pp(.5w, Ah);
Z3 = pp(.5w, — .3h);
% штрих
% точка
endchar;
beginpunkchar(" ! ", 5, .5, .5);
pd pp(.5w, 0);% точка
zi = pp(.5w, 1.05/i); 2:2 = pp(.5w, .3/i);
Pd ^i; pd Z2; draw z\ * * Z2\% основной штрих
endchar;
ligtable "!": "f" =: oct "016"; beginpunkchar(oct "016", 5, .5, .5); pd pp(.5w, .9/i);
% испанский перевернутый !
% точка
zi = pp(.5w, — .l/i); 2:2 = pp(.5w, .6/1);
pd 2^1; pd Z2; draw Zi -- Z2\ endchar;
% основной штрих
beginpunkchar("?", 9, 1, .5);
zi = pp(l-5u, .87г); 2:2 = pp(.5w, /г); 2:3 = pp(w — u, .87г); 2:4 = pp(.5w, .3/i);
pd 2^1; Pd 2:4; draw z\ .. 2:2 .. 2:3 .. {down}^;
pd pp(.5w, 0);
endchar;
% дуга и основной штрих % точка
ligtable "?": "f" =: oct "017";
beginpunkchar(oct "017", 9, 1, .5);% испанский перевернутый ?
zi = pp(l-5u, .17г); 2:2 = pp(.5w, —.17г);
zs = pp(w — u, .17г); 2:4 = pp(.5w, .6/1);
Pd 2^i; Pd 2:4; draw 2^1 .. 2^2 .. 2:3 .. {мр}^;
pd pp(.5w, .9/i);
endchar;
% дуга и основной штрих % точка
beginpunkchar("&", 14, .5, .5);
zi = PP(w — 2u, /г); Z2 = pp(n, /г); 2:3 = pp(3u, 0);
Z5 = pp^w — u, .67г); ze = pp^w — Zu, 0);
pd 2:1; pd 2:5; draw z\ -- ^2 --2:3 --2:5;
draw zi -- .5^2) 2:3]; pd 2^6; draw zg -- .6[z3, 2:5];
endchar;
% горизонтали и ножка % диагонали
beginpunkchar("$", 12, .5, .5);
zi = PP(w — 1.5u, .7/i); Z2 = pp(.5w, /г); 2:3 = pp(u, .7/i); 2:4 = .5[z3, 2:5];
2:5 = pp(w — и, .37г); 2^6 = pp(.5w, 0); 2:7 = pp(u, .37г);
pd zi; pd ^7; draw 2^1 . . 2^2 . . 2^3 • • Z^ • • Z5 • • 2^6 .. £7;% штрих
2:8 = 2^2 + (0, Л/l); pd 2^8; draw Z8 -- zg;% основной штрих
endchar;
beginpunkchar("7.", 18, .5, .5);
^i = pp(3.5u, l.l/i); 2:2 = pp(u, .8/1); 2:3 = pp(3.5u, .5/i);
^5 = pp(w — 3.5it, .5/i); 2^6 = pp(w — 6it, .2/i);
z7 = pp^w — 3.5u, —.l/i); 2^8 = pp(w — u, .2/i);
Pd z\; draw z\ .. Z2 * * 2:3 .. 2:4 .. 2^1;
pd 2:5; draw 2:5 .. 2^6 .. 2:7 .. 2^8 .. 2:5;
2:9 = pp(w — 3u, l.l/i); 2:0 = pp(3u, — .17г);
pd zo; draw 2:9--2:0;
draw 2^1 {2:5 — 2:1} .. 2:9;
endchar;
^4 = PP(6u, .8/2,);
% верхний овал
% нижний овал
% диагональ
% «связка»
beginpunkchar("Q", 18, 1, .5);
zi = pp(2u, 0); 2:2 = Pp(V3W, -^^Y ^з = pp(w — 6it, 0);
^4 = pp(w, .3/г); Zb = ppG/aw, hY zq = pp(u, .ЬНУ zi = .T^, 2:3];
pd zi; Pd ^7)
draw zx -- Z2 -- 2*{прЛО .. Z4 .. Zb .. z^ .. 27;% диагонали и штрих
endchar;
beginpunkchar ("-", 7, .5, .5);
zi = PP(^) -^У ^2 = pp(w — u, .5hy pd zx; pd ^2; draw Zx -- Z2; %горизонтальный
% штрих
endchar;
ligtable "-": "-" =: oct "173";
%- % горизонтальный % штрих%- % горизонтальный % штрихbeginpunkchar(oct "173", 9, .5, .5);
%- % горизонтальный % штрих
%- % горизонтальный % штрих
zi = PP(0, -5/i); Z2 = pp(w, Ahy pd zx\ Pd Z2; draw zx - - Z2;
endchar;
ligtable oct "173": "-" =: oct "174";
beginpunkchar(oct "174", 18, .5, .5);% —
zi = PP(0, ^h); Z2 = pp(w, Ahy pd zx; pd Z2\ draw zx -- Z2\% горизонтальный
% штрих endchar;
beginpunkchar (" + ", 9, .5, 1);
zi = PP(0, .bhy Z2 = pp(w, .bhy pd z^ pd Z2\ draw zx -- Z2\% горизонтальный
% штрих 2:3 = pp(.5w, Ahy z^ = pp(.5w, .^hy pd 2:3; pd 2:4; draw 2:3 -- 2:4;% основной
% штрих
endchar;
beginpunkchar("♦", 13, .5, 1);
zo = pp(.5w, l.l/i); zx = pp(u, .^hy Z2 = pp(2u, .3/i);
2:3 = pp(w — u, .3hy Z4 = pp(w — u, .9/i);
Pd zo; draw 2:0--2:2 .. V3[-5[^2, 2:4], 2:0] .. 2:4--2:1 --2:3--2:0;% звездочка
endchar;
beginpunkchar(",", 5, .3, .5);
% штрих%”% штрих% штрих% “% штрих% штрих%”% штрих% штрих% “% штрих^1 = pp(l-5u, НУ Z2 = pp(w — u, .85/i); 2:3 = pp(w, ^зНУ^
% штрих
%”
% штрих
% штрих
% “
% штрих
% штрих
%”
% штрих
% штрих
% “
% штрих
Pd 2:1; pd z^; draw z\ - - Z2 - - 2:3;
endchar;
ligtable ">": ",H =; oct "042";
beginpunkchar(oct "042", 9, .3, .5);
^1 = pp(.5w — .5u, H^\ Z2 = pp(w, .^H^\ z^ = pp(w — u, .95hy
pd zi\ pd 2:3; draw Z\ -- Z2 --2:3;
endchar;
beginpunkchar(" f", 5, .3, .5);
^1 = pp(w — 1.5u, Hy Z2 = pp(w, .85/i); 2:3 = pp(w — и, 2/зН)\ pd ^1^ pd Z3; draw z\ - - Z2 - - 2:3;
endchar;
ligtable "f": "f" =: oct "134";
beginpunkchar(oct "134", 9, .3, .5);
zx = pp(.5w + .5u, H); Z2 = pp^w — u, .6hy Z3 = pp(w, .95/i);
pd z\\ pd Z3; draw z\ -- Z2 --2:3;
endchar;
beginpunkchar (oct "015", 9, .3, .5);% *
^i = pp(-5w, /г); 2:2 = pp(.5w, .6/г); pd z\; pd 2:2; draw z\ -- Z2; % основной штрих endchar;
beginpunkchar ("(", 7, .5, .5);
zi = pp(w — u, /г); Z2 = PP(w, -5/г); za = pp(w — u, 0);
pd zi; pd 2:3; draw 2^1 .. 2:2 .. 2:3;% штрих
endchar;
beginpunkchar(")", 7, .5, .5);
zi = PP(^) ^); ^2 = pp(w — u, .5h); za = pp(u, 0);
Pd ^r> Pd za; draw z\ . . Z2 . . 2a;% штрих
endchar;
beginpunkchar(" [", 8, .5, .5);
^1 = pp(w — u, /г); Z2 = pp(.5w, /г); za = pp(.5w, 0); za = pp(w — u, 0);
Pd zi; Pd 2a; draw 21 -- Z2 -- 2a -- 2a;% горизонтальные и основной штрихи
endchar;
beginpunkchar("] ", 8, .5, .5);
zi = pp(u, /г); Z2 = pp(.5w, /г); za = pp(.5w, 0); za = pp(u, 0);
Pd Zi; Pd za; draw zi -- Z2 -- 2a -- za; % горизонтальные и основной штрихи endchar;
beginpunkchar ("<", 9, .5, .5);
^1 = PP(w — и, .9/г); Z2 = pp(u, .5/г); za = pp(w — и, Л/г);
Pd zi; pd za; draw z\ - - Z2 - - 2a;% диагонали
endchar;
beginpunkchar (">", 9, .5, .5);
^1 = PP(^) -9/i); ^2 = pp(w — гц .5h); za = pp(u, .l/i);
Pd zi; Pd za; draw z\ - - 22 - - za;% диагонали
endchar;
beginpunkchar("=", 9, .5, .5);
% верхний горизонтальный штрих% нижний горизонтальный штрих— Зи, /г); za = pp(.5w, 0);% диагонали (связанные)% верхний горизонтальный штрих% нижний горизонтальный штрих% верхний горизонтальный штрих% нижний горизонтальный штрих— Зи, /г); za = pp(.5w, 0);% диагонали (связанные)% верхний горизонтальный штрих% нижний горизонтальный штрих^5 = ppku^ 2A^); ^6 = pp(w — u, 2/з^);
% верхний горизонтальный штрих
% нижний горизонтальный штрих
— Зи, /г); za = pp(.5w, 0);
% диагонали (связанные)
% верхний горизонтальный штрих
% нижний горизонтальный штрих
% верхний горизонтальный штрих
% нижний горизонтальный штрих
— Зи, /г); za = pp(.5w, 0);
% диагонали (связанные)
% верхний горизонтальный штрих
% нижний горизонтальный штрих
Pd 25; pd ze; draw za --ze;
^7 = pp(u, Уз/г); 28 = pp(w — u^ г/^\
Pd ^?; pd zs; draw z7 -- z8,;
endchar;
beginpunkchar("#", 15, .5, .5);
^1 = pp(.5w, /г); 2:2 = pp(3u, 0); za = pp(w
Pd 22; Pd 2a;
draw za -- 21 - - 22; draw za --2a - - 22;
^5 = PPP'LL, 2/з/г); 26 = pp(w — U, 2/з/г);
Pd 25; pd z8; draw za -- 26;
27 = pp(w, Уз/г); 2s = pp(w — u, Va^);
Pd ^7; pd zs; draw z7 -- zs;
endchar;
beginpunkchar("/", 9, 1, 1);
^1 = pp(1.5u, —.05/г); Z2 = pp(w — 1.5u, 1.05/i);
pd Zi; Pd Z2; draw z\ -- Z2;% диагональ
endchar;
beginpunkchar(oct "013", 12, .5, .5);% t
^i = PP^i -7Ну 2^2 = pp(.5w, h); za = pp(w — u, .7/i); Z4 = pp(.5w, 0);
pd ^i; pd Zs\ pd Z4;
draw z\ - - Z2 -- Z4\ draw zs - - Z2;% основной штрих и диагонали
endchar;
beginpunkchar(oct "014", 12, .5, .5);% 1
^i = pp(u, -37г); Z2 = pp(.5w, 0); zs = pp(w — u, .37г); Z4 = pp(.5w, h);
Pd ^i; pd 2:3; pd 24;
draw z\ - - Z2 - - Z4; draw za - - ^2;% основной штрих и диагонали
endchar;
Завершающий рассмотрение программный файл PUNKA. MF определяет акценты в той форме, которая предпочтительнее для ТЁХ’а. ТЁХ’овский ввод \def\AA{\accent’27А} {\AA}ngel\aa\ Beatrice Claire Diana VErica Fran\c{c}oise Ginette H\’el\‘ene Iris Jackie K\=aren {\L}au\.ra Mar{\’\i}a N\H{a}ta{\l}{\u\i}e {\0}ctave Pauline QuVeneau Roxanne Sabine T\~a{V\j}a Ur\v{s}ula'Vivian Wendy Xanthippe Yv{\o}nne Z\”azilie
вызывает расстановку акцентов (для шрифта PUNKSL20), которая будет выглядеть следующим образом:
In set А XtATRicg Et-Ai^e Viana
Erica Fran^ois^ Gwem lilt Ens Iris J a cRie Karsn
I AU RA MarIa EaTAiU ЙТА7<
I A UL INS 5ugN6A U ROXANNg Saeins TxRa IIr§ula Vivian HgNE v XANTHirre IvsXNNg lAZILie
(Отметим, что необходимо переопределить макро \АА, но остальные акценты, имеющиеся в plain Tg^’e, работают без всяких изменений в них.) Прорисовка акцентов выполняется следующим образом:
beginpunkchar(oct "022", 9, 1, 1);% '
% диагональ% диагональ^1 = pp(2.5u, h,y Z2 = pp(.6w, .8/г);
% диагональ
% диагональ
Pd 2:1; pd Z2\ draw z\ -- Z2\ endchar:
Глава 20. Меташрифт Punk413
beginpunkchar(oct "023", 9, 1, 1);% '
2t — PP(w — 2.5u, /г); 2^2 = pp(.4w, .8/i);
pd^i; pd 2:2; draw zi 23;% диагональ
endchar;
beginpunkchar(oct "136", 13, 1, 1);% *
zi = pp(2.5u, .8/1); 2:2 = pp(.5w, /г); 2:3 = (w — 2.5u, .87г);
pd zi; pd Z3; draw z\ - - Z2 * * 23;% диагонали
endchar;
beginpunkchar(oct "024", 13, 1, 1);% *
zi = pp(2.5u, .97г); Z2 = pp(.5w, .77г); Z3 = pp(w — 2.5u, .97г);
pd^i; pd Z3; draw 2:1 --2:2--2:3;% диагонали
end char;
beginpunkchar(oct "025", 11, 1, 1);*% v
zi = PP(2n, h); Z2 = pp(.5w, .75/i); Z3 = pp(w — 2u, /г);
pd^i; pd Z3; draw flex(zi) Z2, 23);% штрих
endchar;
beginpunkchar(oct "026", 12, 1, 1);% "
2:1 = pp(u, .87г); Z2 = pp(w — u, .87г); pd z\\ pd Z2; draw z\ -- Z2;%горизонтальный
% штрих
endchar;
beginpunkchar(oct "137", 5, 1, 1);% ‘
pd pp(.5w, .97г);% точка
endchar;
beginpunkchar(oct "177", 13, 1, 1);% “
pd pp^/sw, .97г); pd pp(4/sw, .97г);% точки
endchar;
beginpunkchar(oct "176", 13, 1, 1);% ~
zi = pp(w, .757г); Z2 = pp(w — u, .97г);
Pd Z\; pd Z2\ draw zi{up} .. {np}^;% штрих
endchar;
beginpunkchar(oct "175", 13, 1, 1);% "
zi = pp(4u, /г); Z2 = pp(2.5u, .7/i);
^з = pp(w — 2u, /г); Z4 = pp(w — 3.5u, .7/i);
pd zi; pd Z3; draw zi -- Z2 -- 24 -- 23;% диагонали (связанные)
endchar;
beginpunkchar(oct "027", 13, 0, 0);% акцент-кружок для Айа
zo = (.5w, .66/1);% точка Z2 для строчной буквы A
zi = (.5w, .9/i);
draw zo{zq — (1.5u, 0)} .. zy .. {(w — 1.5u, 0) — zq}zq;% кружок
endchar;
beginpunkchar(oct "030", 13, .5, .5);% цедилла для g
z\ = (.6w, 0); Z2 = pp(.6w, —Ah); Z3 = pp(2.5u, —Ah);
Pd Z3; draw Zi -- Z2 -- Z3;% штрих
endchar;
beginpunkchar(oct "040", 11, .5, .5);% «польское перечеркивание» для L и 1
^i = PP^i -25/i); Z2 = pp(4u, Ah);
% диагональ% диагональpd Zl*, Pd ^2; draw Zi --22;
% диагональ
% диагональ
endchar;
ligtable oct 11040": "I" kern — charwd, 11L" kern — charwd]
Литература
George Ade, Artie: A Story of the Streets and Town (Chicago: H. S. Stone, 1896), Chapter 19.
Dashiell Hammett, The Maltese Falcon (New York: A. A. Knopf, 1930), Chapter 18.
Derek Jewell, music review in the Sunday Times (28 November 1976), 37.
Donald E. Knuth, The METAFONTbook, Volume C of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1986).
Pablo Picasso, рисунок карандашом из Sketchbook 86. (Этот рисунок был позднее использован в качестве иллюстрации в великолепном издании: Vollard. Le Chef-d’(Euvre Inconnu by Honore de Balzac, 1931.)
William Shakespeare, The Merry Wives of Windsor, Act 2, Scene 2, lines 135-137. (The First Folio has the spelling ‘Puncke’.)
Добавление
Еще одно доказательство того, что идея «панковского» набора символов носилась тогда в воздухе, было представлено Дж. Дэниэлом Смитом из Мичиганского государственного университета, приславшим мне такой пример, обнаруженный им в газете State News 83,152 (25 октября 1988 г.), издаваемой данным университетом:
Шрифты
для дискретных полутонов
[Переработанный вариант статьи, первоначально опубликованной в TUGboat 8 (1987), 135-160.]
Небольшие фотографии можно «набирать» на растровых устройствах таким же образом, как это происходит на экране, чтобы потом использовать эти растровые изображения для печати великолепных книг по фотографии. Цель этой статьи обсудить эксперименты по созданию посредством METAFONT’a таких шрифтов, из которых полутоновые изображения легко сгенерировать на лазерном принтере. При низком разрешении невозможно добиться высокого качества, так что для представления больших фотографий с высоким разрешением будет нехватать памяти Т^Х’а; кроме того, начиная с 1990 г. широко доступны эффективные способы непосредственной обработки полутоновых иллюстраций. Тем не менее обсуждаемые ниже шрифты оказались полезными в ряде приложений и при их построении возникало немало интересных вопросов, все еще актуальных.
Я приступил к этому исследованию в начале 1985 г., когда 15 старшекурсников Станфордского университета работали над проектом создания «высокотехничных автопортретов» (см. [4, сс. 88-103]). Студенты самыми изощренными способами манипулировали цифровыми графическими изображениями, но в те времена в Станфорде не было соответствующего устройства вывода, на котором можно было преобразовать эти цифровые изображения в твердую копию. Поэтому я решил создать шрифт, из которого средствами Т^’а можно было бы получать полутона.
Такой шрифт неизбежно был бы зависимым от платформы. Например, лазерный принтер с разрешением 300 пикселей на дюйм не может повторить поведение другого принтера с разрешением 240 пикселей на дюйм, если мы пытаемся контролировать качество изображений, составленных из пикселей. Я решил использовать наше устройство вывода Imagen laserprinter с разрешением 300 (известное также как Canon LBP-CX), потому что оно обеспечивало лучший контроль качества пикселей, чем любая другая машина, имеющаяся в распоряжении студентов.
Казалось, что лучше всего сначала создать шрифт, «литерами» которого были бы крошечные квадраты размера 8x8, составленные из пикселей. Целью
416416Компьютерная типография было получить 65 литер для 65 различных градаций яркости: при 0 < к < 64 существовала бы одна литера точно с к черными пикселями и с 64 — к белыми пикселями.
416
416
На самом деле лучше всего было бы найти некоторую перестановку р из 64 пикселей в квадрате размера 8x8, так чтобы черных пикселей в литере к было pci Ph • • •) Рк-1- Первым моим побуждением было попытаться поддерживать позиции ро, pi, р2, ... как можно дальше друг от друга. Так что моя первая МЕТАЕОЫТ’овская программа рисовала черные пиксели, упорядочивая позиции следующим образом:
45
29
34
18
46
30
33
17
13
61
2
50
14
62
1
49
39
23
40
24
36
20
43
27
7
55
8
56
4
52
11
59
47
31
32
16
44
28
35
19
15
63
0
48
12
60
3
51
37
21
42
26
38
22
41
25
5
53
10
58
6
54
9
57
(В сущности, это матрица «упорядоченного размывания» Байера; см. [5].) Оказалось, что при помощи METAFONT’a довольно просто создать такой шрифт:
X Шрифт полутонов с 65 градациями серого, полученный посредством"
X "упорядоченного размывания" с литерами от "О" (белое) до "р" (черное)
pair р[]; X пиксели упорядочены
X (сперва становится черным рО, затем pl и т.\,д.)
pair d[] ; У, контроль размывания
d[0]=(0,0); d[l] = (l,l); d[2] = (0,l); d[3]=(l,0);
def wrap(expr z)=(xpart z mod 8,ypart z mod 8) enddef;
for i=0 upto 3: for j=0 upto 3: for k=0 upto 3:
p[16 i+4j+k]=wr ap(4d[k]+2d[j] +d[i] + (2,2) ) ;
endfor endfor endfor
w#:=8/pt; X это 8 пикселей
font_quad:=w#; designsize:=w#;
picture prevchar; X до сих пор все пиксели зачернены prevchar=nullpicture;
for i=0 upto 64:
beginchar(i+ASCII"O",w#,w#,0); currentpicture:=prevchar;
if i>0:
addto currentpicture also unitpixel shifted p[i-l];
fi
prevchar:=currentpicture; endchar;
endfor
Этот файл получил имя odith.mf; я использовал его для создания шрифта odithSOO, применив обычным образом МЕТА FONT к следующему файлу под названием odithSOO.mf, что связано с зависимостью шрифта от платформы:
% Шрифт полутонов для Imagen, полученный упорядоченным размыванием mode_setup;
if (pixels_per_inch<>300) or (magOl):
errmessage "Sorry, this font is only for resolution 300";
errmessage "Abort the run now or you’ll " &
"clobber the TFM file";
forever: endfor% переход в бесконечный цикл
else: input odith fi
end.
Набирать фотографии при помощи файла odithSOO довольно легко, если в ТЁХ’овский документ ввести файл макрокоманд ht.tex:
, Макрокоманды для набора полутонов
, Пример использования:
% \input ht% ввод этого файла
, \font\htfont=<your favorite halftone font> % загрузка шрифта
% \beginhalftone
% литеры для верхней строки фотографии.
. литеры для второй строки фотографии.
.
, литеры для нижней строки фотографии.
% \endhalftone
. Теперь фотография заключена в бокс подходящего размера.
. Вы можете также написать \setboxO=\beginhalftone...\endhalftone.
\chardef\other=12
\def\beginhalftone{\vbox\bgroup\offinterlineskip\htfont \catcode‘\\=\other \catcode‘V=\other \catcode‘\_=\other \catcode'\.=\active \starthalftone}
{\catcode‘\.=\active \catcode‘\/=0 \catcode‘\\=\other
/gdef/starthalftone#l\endhalftone{/let.=/endhalftoneline
/beginhalftoneline#l/endhalftone/ignorespaces}}
\def\beginhalftoneline{\hbox\bgroup\ignorespaces}
\def\endhalftoneline{\egroup\beginhalftoneline}
\def\endhalftone{\egroup\setboxO=\lastbox\unskip\egroup}
(В этих макро применено небольшое ухищрение: поскольку ‘V —один из легальных символов в odithSOO, но ‘t’ в \endhalf tone таковым не является, мы должны временно вернуть бэкслешам статус обычных символов.)
К сожалению, результат, полученный при помощи odithSOO, нельзя было назвать хорошим; изображения несли на себе явный отпечаток своего происхождения от бинарной рекурсии, они были чересчур «компьютерными». Вот,
например, три типичные фотографии в натуральную величину, полученные на нашем принтере:
Квадратные литеры размера 8x8 слишком бросаются в глаза.
Кроме того, лазерный принтер делал странные вещи, когда при помощи odithSOO обрабатывал растровые изображения такого рода:*
Хотя в литере с номером к были более темные пиксели, чем в литере с номером к — 1, их чернота в нашем эксперименте не росла монотонно. Литера номер б казалась темнее литеры номер 7, но это оптический обман. Литера номер 32 была темнее многих за ней следующих литер, но в данном случае это нельзя считать просто иллюзией: изучение под линзой с большим увеличением показало, что принтер распределяет тонер весьма причудливым образом.
Другой дефект подхода упорядоченного размывания состоял в том, что большинство литер получались слишком темными. Замеры при помощи денситометра показали, что примерно на литере номер 16 уже была достигнута плотность 50%. Следовательно, odithSOO слишком выделяет светлые тона.
Моим следующим побуждением было взглянуть на фотографии в книгах и газетах, чтобы понять, как они достигают хорошего качества. Так вот в чем дело! Они делают черные точки все больше и больше по размеру, иначе говоря, упорядочивая пиксели ро, Pi, • • •, они располагали черные пиксели тесно к друг другу, а не в противоположных концах. Кроме того, точки обычно наносились на сетку, повернутую под углом 45°, поскольку при таком повороте человеческий глаз не так отчетливо видит точечную природу изображения,
как в случае прямоугольной сетки. Поэтому я решил зачернить пиксели в сле
дующем порядке:
(2)(2)
(2)
(2)
Здесь я решил отойти от квадрата размера 8x8; это неквадратное множество позиций пикселей все еще в стиле Эшера «покрывает черепицей» плоскость, если мы будем повторять его в 8-пиксельных интервалах. Как и ранее, литеры считаются имеющими ширину 8 пикселей и толщину 8 пикселей, но теперь они не заключены в бокс размера 8x8. Точка отсчета находится в нижнем левом углу позиции 24.
Приведенная выше матрица действительно лучше той, с которой я начинал, но я не учел то ценное, что в ней было. Джон Хобби обратил мое внимание на это и предложил такую альтернативу потому, что хотел, чтобы образ чер- них пикселей в литере номер к был в сущности тем же, что и образ белих пикселей в литере номер 64 — к. (Коммерческие схемы полутонов начинают с маленьких черных точек на белом фоне; затем точки увеличиваются до тех пор, пока они не образуют шахматную доску из белых и черных квадратов; затем белые квадраты начинают сжиматься до маленьких точек на черном фоне.) Матрица (2) обладает этим свойством симметрии, потому что сумма элементов в позициях (ж, у) и (ж + 4, у) равна 63 для всех х и у, если вы рассматриваете «обвивающие» вычисления индексов по модулю 8. Представлена
еще и другая интересная симметрия: если изучить относительные позиции ячеек с номерами 0-7, 8-15, 16-23, 24-31, 32-39, 40-47, 48-55 и 56-63, то видно, что каждая группа восьмерок образована, в сущности, одинаково.
Мы с Джоном использовали это новое упорядочение позиций пикселей, чтобы образовать шрифт «размывания точки» под названием ddithSOO, аналогичный шрифту odithSOO. Он производил следующую последовательность шкал серого тона:*
Теперь получилась радующая глаз равномерная шкала, за исключением одной аномалии с литерой вблизи 62, что было неизбежно на принтере с «идеологией» ксерокса. Плотность достигала 50% вблизи литеры с номером 22, и мы могли
бы компенсировать это посредством предварительной обработки данных перед выводом на печать (используя «transfer function»).
Три представленных выше изображения, полученных при помощи odithSOO, после подстановки ddithSOO выглядели примерно так:*
Поскольку мои студенты успешно справлялись с получением подходящих увеличенных изображений посредством ddithSOO, я прервал работу над полутонами и вернулся к своей привычной деятельности.
Позже, однако, я осознал, что ddithSOO легко можно улучшить, потому что каждая из этих литер состояла из двух точек примерно одинакового размера. Не было никаких оснований именно таким образом объединять в пары точки полутоновых изображений. Приложив совсем немного усилий, мы сможем набирать каждую точку независимо.
Итак, я создал шрифт sdithSOO для «одноточечного размывания» с 33 литерами (а не с 65, как ранее), используя матрицу
для управлением порядком зачернения пикселей. (Эта матрица соответствует ровно одной из двух точек в большей матрице, приведенной выше, если мы разделим каждый элемент на 2 и отбросим остаток.) Литеры все еще рассматриваются как литеры шириной 8 пикселей, но теперь они только 4-пиксельные. Когда набирается фотография, строки с нечетными строкам должны быть компенсированы по горизонтали посредством 4 пикселей.
Ниже представлен METAFONT-файл sdith.mf, который использовался для генерации одноточечного шрифта. Обратите внимание, что регулярность изображения (3) позволяет нам избежать перечисления всех 32 элементов матрицы в явном виде:
% Шрифт полутонов с 33 градациями серого, полученный
% 11 одноточечным размыванием11 с литерами от “О” (белое) до “Р” (черное)
pair р [] ; 7. пиксели идут в таком порядке:
% (сначала рО становится черным, затем pl и т.д.)
р0=(1,1); р4=(2,0); р8=(1,0); р12=(0,0);
р16=(3,-1); р20=(2,-1); р24=(1,-1); р28=(2,-2);
transform г; r=identity rotatedaround ((1.5,1.5),90);
for i=0 step 4 until 28:
p[i+l]=p[i] transformed r;
pCi+3]=p[i+l] transformed r;
p[i+2]=p[i+3] transformed r;
endfor
w#:=8/pt; 7. это 8 пикселей
font_quad:=w#; designsize:=w#;
picture prevchar; 7. пиксели зачерняются до сих пор
prevchar=nullpicture;
for i=0 upto 32:
beginchar (i+ASCIP'O11 ,w#, .5w#,0) ;
currentpicture:=prevchar;
if i>0:
addto currentpicture also unitpixel shifted p[i-l];
f i
prevchar:=currentpicture; endchar;
endfor
(Для обеспечения зависимости от устройства здесь имеется файл sdith300.mf, аналогичный файлу odith300.mf, рассмотренному ранее.)
Далее показано, как выглядят три примера изображений, когда они переведены в шрифт sdith300 — улучшение очевидно:*
Представленные ранее макро TgX’a ht.tex должны быть заменены другим множеством altht. tex, где использовались независимые точки:
, Другие макро для набора полутонов
% Пример использования:
, \input althtУ. ввод этого файла
. \font\althtfont=<alternative halftone font> % загрузка шрифта
, \beginalthalftone
, Литеры для верхней полустроки фотографии.
. (сдвинуто наполовину вправо)
. литеры для второй полустроки фотографии, (не сдвинуто вправо)
. .
. литеры для нижней полу строки фотографии, (возможно, сдвинуто)
, \endhalftone
. Теперь фотография заключена в бокс подходящего размера.
. Можно также установить \setboxO=\beginalthalftone...\endhalftone. \chardef\other=12
\newif\ifshifted \newdimen\hfm
\def\beginalthalftone{\vbox\bgroup\offinterlineskip \shiftedtrue \althtfont \hfm=.5em
\catcode'\.=\active \moveright\hfm\hbox\bgroup} {\catcode'\.=\active \gdef.{\ifshifted\kern-\hfm\egroup
\shiftedfalse\else\egroup\shiftedtrue\moveright\hfm\fi \hbox\bgroup\ignorespaces}}
\def\endhalftone{\egroup\setboxO=\lastbox\unskip\egroup}
Эти макро значительно проще тех, что использовались в ht. tex, потому что 33 ASCII-литеры от "О" до "Р" не имеют специального назначения в plain T^X’e.
В 1987 г. я обнаружил, что похожая, но более изощренная техника уже была разработана Робертом Л. Гардом [3] в 1976 г. Методы, которые мы до сих пор рассматривали, основывались либо на (а) шрифте полутонов с 65 градациями серого, в котором каждая литера размера 8 х 8 в сущности дает на фотографии две точки, либо на (Ь) шрифте полутонов с 33 градациями серого, в котором каждая литера размера 4x8 дает на фотографии одну точку. Метод Гарда основан на (с) шрифте полутонов с 17 градациями серого, в котором каждая литера размера 4x4 дает пол точки (фактически две четверти) на фотографии.
В полуточечной схеме Гарда /с-я градация серого получается зачернением ячеек от 0 до к — 1 в массиве
15101437812139621511401510143781213962151140илиили14105112873269130411151410511287326913041115(4)(4)
1
5
10
14
3
7
8
12
13
9
6
2
15
11
4
0
1
5
10
14
3
7
8
12
13
9
6
2
15
11
4
0
или
или
14
10
5
1
12
8
7
3
2
6
9
13
0
4
11
15
14
10
5
1
12
8
7
3
2
6
9
13
0
4
11
15
(4)
(4)
(Мы фактически создаем два множества литер как зеркала друг друга, и перемещаемся из одного в другое в процессе набора фотографии.) Следующий METAFONT-файл hdith.mf будет так генерировать шрифт hdith300; в сущ-
ности, тем же способом ранее были получены другие шрифты —ddith300 и sdith300:
, Шрифт полутонов с 17 градациями серого, полученный
"полуточечным размыванием"
с использованием литер от "А" (белое) до "Q" (черное) наравне
с зеркально отраженными версиями от "а" (белое) до "q" (черное) pair р [] ; 7. пиксели идут в таком порядке:
7. (сначала рО становится черным, затем pl и т.д.) р0=(3,0); р4=(2,0); р8=(2,2); р12=(3,2);
transform г; r=identity rotatedaround ((1.5,1.5),180);
for i=0 step 4 until 12:
p[i+l]=p[i] transformed r;
p[i+2]=p[i] shifted (0,1);
p[i+3]=p[i+2] transformed r; endfor
w#:=4/pt; 7. that’s 4 pixels font_quad:=w#; designsize:=w#;
r:=identity reflectedabout ((2,0),(2,3));
picture prevchar; 7. пиксели зачерняются до сих пор prevchar=nullpicture;
for i=0 upto 16:
beginchar(i+ASGII"A",w#,w#,0); currentpicture:=prevchar; if i>0:
addto currentpicture also unitpixel shifted p[i-l] ; f i prevchar:=currentpicture; endchar; - beginchar(i+ASCII"a",w#,w#,0);
currentpicture:=prevchar transformed r; endchar; endfor
Для набора таким шрифтом мы можем, например, дать следующие команды:
\input ht'
\font\htfont=hdith300
\beginhalftone
iljJjKkJjJkJkKkKkJjJjJiliJkJkJjJjJ jIhHhJjIj HjIgHhliliGhF hGilhGhHhljJjJjJjKkKkKkKkKkKkKkKiljJkKkLkKlKlLlLlLlLlL.
IjJj КjJjJjJkKkKkKjJjJkHiHiJkJkJjJjJiHhHililgHhGgGglgGgFe
FfGfGhHhHhlhliJkKkKkKlKkKkKkKkKkKkJkJkKkKkLkKkLlLlKkLl.
iljJiKjJjKkJkKkKkJkJjJjJjJjJiJjJjZiHhlhlhHiGgFgFfGfGiIjК
jliHgGfGhHhJjIjKkKkJkLkLkKkLkKlKkKjKkKkKlKkKkKkLILILIL.
HhliJjJj КjJkJjKkKkKkJj JjJjJjliJjJjJililhlhlhGfFeFkMnOoOo
OoOnNlJhGgHiljJjJjJjKkLIKkKkKkKkJkKkKkLkKlKkKkLkKlKkLl. iJililhljKjJiJjKkKjKjJjJjJjljJjJjliliHhlililfFgLnOoOoOoO
oOoPoOoNkGfGhHiJjJjJlKkKkKkKkLjKkKjКjKkKkKkJjKkKkKILkL. IjlilhHiJjKiJjJjKjKjJjJjIjIjJjIiJiHhIiHhIjIgGkOoOoOoOoOo
OpOoOpPpOoJgFgljJjIjJkKkJkJjJkKkKjJjJjKkKkJjJjKkKILkKI.
\endhalftone
Прописные и строчные литеры чередуются в шахматном порядке, так что зеркально отраженные литеры будут появляться в нужных местах. Если необходимо, строки могут быть разорваны, потому что литеры пробелов в шрифте hdithSOO не имеют ширины. В случае половинных точек макро \beginhalftone тот же самый, что и в случае двойных; отличаются только название шрифта и схема кодировки.
Ниже для сравнения приведены девять фотографий, демонстрирующих применение всех трех методов точечного размывания ко всем трем изображениям:*
двухточечноедвухточечноеодноточечноеодноточечноеполуточечноеполуточечное
двухточечное
двухточечное
одноточечное
одноточечное
полуточечное
полуточечное
Метод половинной точки Гарда очевидным образом улучшает качество одноточечных фотографий, несмотря на то, что он ограничивается 17 градациями серого; у этого метода есть еще и то преимущество, что литеры в нем квадрат-
ные, а не ромбовидные, что облегчает процесс вычислений. Но это означает, что ТеХ должен будет удваивать набор при увеличении количества данных. В самом деле, показанные здесь фотографии с двухточечными изображениями были набраны в виде 64 строк с 55 литерами в каждой, одноточечные изображения превратились в 128 строк с 55 литерами в каждой; фотографии с половинными точками состоят из 128 строк со 110 литерами в каждой. Малые версии ТЁХ’а способны обрабатывать не более 50 000 литер на странице.
(Макро \beginhalf tone читает изображение в память Т^’а целиком, прежде чем наберет первую строку, и ТЁК не сбрасывает эту информацию до тех пор, пока не возникнет необходимость использовать ее в каком-нибудь сообщении об ошибке. Сухой остаток таков: необходимо удваивать объем памяти ТЁХ’а, пока фотография не конвертируется в бокс. В случае половинных точек, однако, вполне достаточно упрощенного варианта \beginhalf tone, потому что в шрифте с 17 градациями вполне достаточно букв алфавита.)
Для принтера с высоким разрешением, скажем 635 точек на дюйм (что эквивалентно 25 точкам на миллиметр), можно было бы создать аналогичные шрифты ddith635, sdith635 и hdith635. Тогда наши 9 фотографий выглядели бы примерно так:
Теперь фотографии уменьшились, потому что ширина литер все еще 8 пикселей, а сами пиксели стали меньше. При таком разрешении полутона могли бы выглядеть так же хорошо, как при распечатке на профессиональных устройствах, если бы не тенденция к излишнему затемнению из-за растискивания краски; эта проблема решается установкой подходящей плотности программой- препроцессором .
Приведенные выше образцы были подготовлены на основании данных с 256 градациями при помощи специальной подпрограммы-препроцессора, которая приведена в приложении 1. Препроцессор опирается на обобщенный алгоритм Флойда—Стейнберга [2], сглаживающий эффекты ограниченности градаций серого. Действительно, если мы попробуем провести тот же самый
эксперимент без ошибки диффузии, положив параметр препроцессора демпфирование равным нулю, то девять примеров фотографий будут выглядеть примерно так:*
Диффузия незначительно отражается на портрете Моны Лизы или на деталях высокой частоты в изображениях Линкольна и Статуи свободы, но она весьма существенна для фона, который меняется только постепенно. Без диффузии ошибки сбивающий с толку «цифровой» эффект живописи может легко стать слишком заметным, особенно когда доступны всего 17 градаций серого.
Какое нужно разрешение? Традиционно качество растра определяется подсчетом точек в отрезке, равном одному дюйму в соответствующей прямой сетке, и это легко выявить при помощи увеличительного стекла. Фотографии
Глава 21. Шрифты для дискретных полутонов 427 в газете вроде International Herald Tribune используют сетку с 72 линиями, повернутую на 45°, что приблизительно соответствует разрешению 50 V2, которое мы могли бы получить при помощи шрифта типа sdith400 на лазерном принтере с разрешением 400 пикселей на дюйм. (Наши 300-точечные шрифты ddith3OO, sdith300 и hdith300 дают поворот сетки только на 37.5V^ ~ 53 линий на дюйм.) Фотографии на переплетах томов Computers & Typesetting сделаны с разрешением сетки 133 линии на дюйм, повернутых на 45°; это несколько лучше, чем ранее приведенные примеры с разрешением 635 точек на дюйм, которые соответствуют примерно 112 Ipi. Но 133 отнюдь не верхний предел: в книге с репродукциями высокого качества, как например [1], сделанными дуплексом, использовался растр с разрешением около 270 Ipi.
Обратимся теперь к другой проблеме. Предположим, что имеется изображение, для которого нужно получить наилучшее из возможных воспроизведений на лазерном принтере среднего разрешения, поскольку мы собираемся использовать это изображение многократно —например, в качестве шапки официального письма. В подобных случаях оптимальный результат получается, если создать специальный шрифт для этого отдельного изображения, потому что мы бы хотели управлять каждым пикселем, а не пользоваться шрифтом для полутонов общего назначения. Тогда необходимое изображение можно будет напечатать таким специальным шрифтом из «литер», представляющих собой прямоугольные участки целого.
Приведенные выше примеры были сначала напечатаны на принтере в виде 64 строк по 55 столбцов в строке с 8 пикселями в каждой строке и в каждом столбце. Чтобы получить эквивалентную фотографию с каждым пикселем, выбранным индивидуально, мы можем сделать шрифт, который имеет, скажем 80 литер, с 64 пикселями в высоту и 44 пикселями в ширину каждая. Набрав 8 строк по 10 литер, мы получим требуемое изображение. Именно так была выполнена следующая фотография:*
ТЁХ станет набирать такую фотографию, если ему дать команду \gioconda, предварительно задав такие определения:
\font\lisa=lisa300
\newcount\m \newcount\n
\def\gioconda{\vbox{\lisa \offinterlineskip \n=0
\loop \hbox{\m=0 \loop \char\n \global\advance\n by 1
\advance\m by 1 \ifnum\m<10 \repeat}
\ifnum\n<80 \repeat}}
Как только у нас появились отдельные фрагменты, мы можем комбинировать их, получая необычные эффекты:*
МИЫВИ1МИЫВИ1вавмеввмвавмеввмВ1МВИВЙ8В1МВИВЙ8вавк:1вавававк:1вава■ввввввввв■вввввввввввввввввввввввввввввввиввввввввивввввв
МИЫВИ1
МИЫВИ1
вавмеввм
вавмеввм
В1МВИВЙ8
В1МВИВЙ8
вавк:1вава
вавк:1вава
■ввввввввв
■ввввввввв
вввввввввв
вввввввввв
ввивввввв
ввивввввв
Показанный выше шрифт lisa300 был сгенерирован из файла lisa.mf. который начинался примерно так:
row 1; data "ffffffffffffffffffffffffffffffffffffffffffff" & "fffffffffffffffffffffffffffffffffffffff" & "fffffffffffffffffffffffffff";
row 2; data "80808000900010008000209012101010201000009000"
& "9000001090802000001010a0000002291000002"
& "092009001008900805092080011";
row 3; data "8010f0d000d050f0a0910080a0a0a04080c0805000b0"
& "8080c08020200050808040a0bl9090a040a050a"
& "0405100b050003010404021b0c3";
row 4; data "a8d00120b891308080a0a93100el2190a8005120a080"
& "2060945080a0a024a060a0284060204029a5a0c"
& "89020508134a850al59c8b065d9";
... и т. д. вплоть до 512-й строки; этот файл был получен программой из приложения 2, использующей метод под названием диффузия точки [7]. Параметром файла lisaSOO.mf был
% Мона Лиза для Imagen 300 mode_setup;
if (pixels_per_inch<>300) or (magOl):
...(error messages as before)
else: input piefont
width:=44; height:=64; n:=10; filename:="lisa" ;
do.it; fi end.
а драйвером файла pief ont .mf был
string filename; picture pic[];
picO=nullpicture; pic8=unitpixel;
for j=0 upto 1: pic[4+8j]=pic[8j];
addto pic [4+8j] also unitpixel shifted (1,0); endfor
for j=0 upto 3: pic[2+4j]=pic [4j];
addto pic[2+4j] also unitpixel shifted (2,0); endfor for j=0 upto 7: pic[l+2j]=pic [2j];
addto pic[l+2j] also unitpixel shifted (3,0); endfor
def do_it= ww:=width/4;
for j=0 upto n-1: jj:=ww*j;
scantokens("input "&filename); endfor enddef;
vardef row expr x =
cc:=(x-l)div height; rr:=height-l-((x-l)mod height);
if rr=height-l:
beginchar(cc*n+jj/ww,width/pt,height/pt,0); fi enddef;
vardef data expr s =
for k=0 upto ww-1: addto currentpicture also
pic[hex substring(jj+k,jj+k+1) of s] shifted (4(jj+k),rr); endfor
if rr=0: xoffset:=-4jj; endchar; fi enddef;
Нельзя сказать, что это очень эффективно, но зато интересно и все-таки работает. Весь фокус здесь заключается, разумеется, в том, чтобы избежать переполнения памяти METAFONT’a, пожертвовав временем ради пространства. (Немного цифр: файл lisa.mf был размером 65428 байт; METAFONT производит файл lisa300.300gf общего шрифта размером 103088 байт, который сжимается в файл lisa300.300pk упакованного шрифта размером 29044 байт. Процесс генерации занимает всего 11256 слов памяти METAFONT’a и использует пространство всего для 466 строковых литер.)
Кен Ноултон и Леон Хармон показали, что с фотографиями, подвергнутыми оцифровке, можно получать удивительные эффекты (см. [6]). Следуя
этой традиции, я обнаружил, что массу удовольствия может доставить комбинация приведенных выше макро T£X’a с новыми шрифтами, и тут очевидным образом проявляется их цифровая сущность. Не всегда нужно пытаться конкурировать с коммерческими растрами!
Мы, например, можем использовать ht.tex с шрифтом ‘negdot’, что дает негативы вне квадратных точек:
METAFONT-файл negdot.mf, генерирующий этот шрифт, весьма прост:
шрифт псевдополутонов для негативов: 65 размеров квадратных точек,
использующий литеры от "О" (большой) до "р" (маленький)
mode_setup;
w#:=8/300in#; font_quad:=w#; designsize:=w#;
for i=0 upto 64: beginchar(i+ASCII”O”,w#,w#,0);
r#:=sqrt(.9w#*(l-i/80)); define_pixels(r);
fill unitsquare scaled r shifted(.5w,.5h); endchar; endfor end.
В отличие от только что рассмотренных, этот шрифт не зависит от устройства (и работает во всех разрешениях). Его «шкалой серого» является
О <М ^ «5 00О <М ^ «5 00OM^yoCOCWyOOON^OCOON^roOOOlM^COOOON^OM^yoCOCWyOOON^OCOON^roOOOlM^COOOON^
О <М ^ «5 00
О <М ^ «5 00
OM^yoCOCWyOOON^OCOON^roOOOlM^COOOON^
OM^yoCOCWyOOON^OCOON^roOOOlM^COOOON^
Мы можем на самом деле воспринимать изображения, даже когда каждая литера шрифта полутонов имеет одно и то оюе количество черных пикселей. Вот, например, что происходит, когда три приведенных выше изображения набираются шрифтом, в котором каждая литера состоит из вертикальной и горизонтальной строки. Когда пиксели становятся темнее, строки сдвигаются вверх и вправо, но они остаются равномерной толщины. Мы воспринимаем
более светлые и более темные участки только благодаря тому, что строки либо дальше отстоят друг от друга, либо плотнее сжимаются.
METAFONT-файл lines .mf для этого не зависящего от устройства шрифта выглядит так:
, шрифт псевдополутонов: 65 строк, перемещаемых вправо и вверх;
, используются литеры от "О" (левый/нижний) до ”р" (правый/верхний)
mode_setup; pickup pencircle scaled . 3pt; q:=savepen; w#:=8/300in#; font„quad:=w#; designsize:=w#;
for i=0 upto 64: beginchar(i+ASCII”O”,w#,w#,0);
pickup q; draw (0,h*i/64)—(w,h*i/64);
draw(w*i/64,0)—(w*i/64,h); endchar; endfor end.
Еще одна возможность получения шрифтов состоит в использовании angles.mf; здесь каждая литера представляет собой отрезок фиксированного размера, поворачивающийся от горизонтального до вертикального положения в соответствии с возрастанием плотности:
. шрифт псевдополутонов: 65 отрезков, которые меняют направление;
, используются литеры от "О" (горизонтальный) до ”р” (вертикальный)
mode_setup; pickup pencircle scaled .3pt; q:=savepen; w#:=8/300in#; font_quad:=w#; designsize:=w#;
for i=0 upto 64:
beginchar (i+ASCH"0" ,w# ,w# ,0) ; pickup q;
draw ((0,0)—(w,0)) rotated (90*i/64); endchar; endfor end.
Получающиеся при этом изображения все еще удивительно легко идентифицировать:
(Можно представлять себе это в виде громадного массива солнечных часов, стрелки которых указывают уровни локального освещения.) Забавно рассматривать эти изображения, наклоняя страницу до тех пор, пока ваши глаза не станут почти параллельны бумаге.
В качестве последнего примера рассмотрим шрифт из 33 литер, который предназначен для использования с файлом altht.tex вместо ht.tex. Читателей, имеющих склонность ломать голову, приглашаем попробовать догадаться, что будет делать этот МЕТА FONT-код, прежде чем посмотреть на портрет Моны Лизы, набранный соответствующим шрифтом. ^Подсказка: название МЕТА- FONT-файла hex.mf.]
чередующийся псевдополутоновой шрифт: 33 секретных образа;
используются литеры от "О" до "Р"
mode_setup; q:=savepen;
w#:=6pt#; font_quad:=w#; designsize:=w#;
for i=0 upto 32: beginchar (i+ASCII”O,,,w#, .5w#,0);
pickup q; alpha:=.5-i/72; z0=(.5w,.5h);
zl=alpha[(5/6w,.5h),z0]; z2=alpha[(2/3w,-.5h), zO];
z0=.5[z2,z5] =.5 [z3,z6] =.5 [zl,z4]; x2=x6; y5=y6;
draw zl—z2; draw .5[zl,z2]—zO;
draw z3—z4; draw .5[z3,z4]—zO;
draw z5—z6; draw .5[z5,z6]—zO; endchar; endfor end.
Ответ на эту головоломку в виде иллюстрации помещен в самом конце главы (после приложений и литературы).
Приложение 1..
Препроцессор для изобразительного материала
Следующая программа на CWEB иллюстрирует, как конвертировать цифровое изображение в тот вид, который требуется для описанных выше шрифтов и макрокоманд. (Исходный файл halftone. w и некоторые файлы данных можно скачать с http://www-cs-faculty.stanford.edu/~knuth/programs.html.)
Введение. Эта программа подготавливает данные для изображений в виде описанных выше шрифтов и макрокоманд. Предполагается, что входной файл (stdin) представляет собой EPS-файл, полученный в результате работы программы Adobe Photoshop™ на машине Macintosh с бинарной EPS-опцией, имеющей разрешение 72 пикселя на дюйм. Этот файл имеет либо т строк по п столбцов в каждой, либо т 4- п — 1 строк по т 4- п — 1 столбцов в каждой, либо 2 m строк по 2п столбцов в каждой; во втором случае изображение должно быть повернуто на 45° против часовой стрелки. (Такие изображения получаются, если начать с заданного кт х Am-изображения, возможно повернутого на 45°, и затем использовать операцию Photoshop’a Image Size, чтобы сократить размер до требуемого числа пикселей. В моих экспериментах я положил к = 8, так что мог также использовать метод диффузии точки из приложения 2; но А: не должно быть целым. При больших значениях к имеется тенденция к более четким уменьшенным изображениям, чем при малых значениях.)
Файл вывода (stdout) представляет собой последовательность литер ASCII, которую можно поместить в ТЁХ-файлы, что приведет к получению на выходе изображений размера 8m х 8п, использующих шрифты, вроде описанных в данной работе. В первом случае мы получим т строк из пикселей с 65 градациями. Во втором случае (с поворотом) мы получим 2m срок из пикселей с 33 градациями. В третьем случае мы получим 2m строк из пикселей с 17 градациями.
# define т 64# define п 55# define г 64# define т 64# define п 55# define г 64/* базовое число строк */
# define т 64
# define п 55
# define г 64
# define т 64
# define п 55
# define г 64
/* базовое число столбцов */
/* тах(т,п) */
#include <stdio.h>
float а\т 4- т 4- 2][п 4- г];/* зачерненность: 0.0 (белое), 1.0 (черное) */
(Глобальные переменные 4);
void main(int arg с, char *ar^v[])
{ register int i, j, к, I, p\
int levels, trash, ii, jj;
float dampening = 1.0, brightness = 1.0;
(Проверка для нестандартных множителей dampening и brightness 2);
(Определение шрифта на входе посредством просмотра ограничивающего бокса з);
/print/ (stderr, "Makinguy.dulineSuOfuyed-leveludataXn" ,
(levels < 65 ? m-Vm; m\ levels);
print/ ("\\begin*/.shalftone\n", levels = 33 ? "alt" : "");
(Ввод графических данных 5);
(Трансляция ввода в вывод 12);
}
Необязательные аргументы строковых команд позволяют пользователю умножить постоянные диффузии на dampening и (или) умножить уровни яркости на множитель brightness.
(Проверка для нестандартных множителей dampening и brightness 2) =
if (argc > 1 Л sscanf (аг^[1], "*/.g"^dampening) = 1) {
/print/(stderr, "Usingudampeningufactoruy.gXn", dampening);
if (argc > 2 A sscan/ (argv\2^ "Xg", ^brightness) = 1)
/print/(stderr, "uuandubrightnessufactoruXg\n", brightness);
}
Эта программа использовалась в разд. 1.
Для Macintosh’a принято указывать окончание строки литерой ASCII (возврат каретки) (т. е. control-M, aka \г); но библиотека С проводит работу лучше с новыми строками (т. е. control-J, aka \п). Поскольку нас не беспокоит эффективность, мы просто вводим одну литеру за раз. В этой программе приняты соглашения для Macintosh’a.
Работа здесь заключается в просмотре последовательности ‘Box: ’ на входе, за которой следует 0, 0, количество столбцов и количество строк.
#define panic(s) { /print/(stderr, s); exit(—1);}
(Определение шрифта на входе посредством просмотра ограничивающего бокса з) =
А: = 0;
scan:
if (к-Н- > 1000) panzc("Couldn,tuf indutheuboundinguboxuinfo! \n" );
if (getchar() / ’B*) goto scan;
if (getchar() / ’o’) goto scan;
if (getchar() / ’x*) goto scan;
if (getchar() ^ 1 .-Q goto scan;
if (scan/ ("ZduZduZduZd", &ZZz, &lly, ^t,urx &ury) 7^ 4 V Их ^ 0 V Uy 7^ 0) panic ("Baduboundinguboxudata! \n");
if (urx = n A ury = m) levels = 65;
else if (urx = n + n A ury = m 4- m) levels = 17;
else if (urx = m 4- n — 1 A ury = urx) levels = 33;
else panic ("Boundinguboxudoesn, tumatchutheuformatSuIuknowiXn");
Эта программа использовалась в разд. 1.
(Глобальные переменные 4) =
int Их, Uy, игх, игу; /* параметры ограничивающего бокса */
см. также разд. 8.
Эта программа использовалась в разд. 1.
После того как мы осмотрели ограничивающий бокс, поищем строку литер ^eginimageXr’; за ней должны следовать пиксельные данные, по одной литере на байт.
(Ввод графических данных 5) = к = 0;
skan;
if (fc-H- > 10000)
panic (" Goul dn'tuf indutheupixeludat a! \n");
if (getchar^) ’b*) goto skan;
if (getchar^ / ’e’) goto skan;
if (getchar^ / ’g’) goto skan;
if (getchar^ / *i’) goto skan;
if (getchar() / ’п’) goto skan;
if (getchar^ / *i?) goto skan;
if (getchar() / ’m’) goto skan;
if (getchar() / ’a’) goto skan;
if (getchar() ^ ’g’) goto skan;
if (getchar() / ’e’) goto skan;
if (getchar^ / Лг*) goto skan;
if (levels = 33) (Ввод повернутых писельных данных 7)
else (Ввод прямоугольных пиксельных данных 6); if (getchar() / Лг*)
panic ("Wronguamountuofupixeludata! \n");
Эта программа использовалась в разд. 1.
Пакет Photoshop построен на основе соглашений, принятых в среде фотографов, которые рассматривают 0 как черное и 1 как белое, но мы будем следовать соглашениям, принятым у программистов, имеющих тенденцию рассматривать 0 как отсутствие краски (белое), а 1 как полностью закрашенное (черное).
Мы используем тот факт, что глобальные массивы изначально нулевые, чтобы допустить возможность существования выше и ниже вводимых данных полностью белых строк в прямоугольном случае.
(Ввод прямоугольных пиксельных данных 6) =
for (г = 1; г < ату; г++)
for (j = 0; з < итх; j-H-)
аИЬ1 = 1-0 — brightness * getchar( )/255.0;’
Эта программа использовалась в разд. 5.
В случае поворота мы транспонируем ввод и частично его сдвигаем, так что г-я строка может оказаться в позиции a[z][j 4- LV^J] при 0 < j < п. Столь неочевидное размещение превращается в большое удобство на фазе вывода благодаря применению к этому транспонированному выводу алгоритма диффузии ошибки.
Пусть, например, т = 5 и п = 3; вводом будет массив размера 7x7, который можно представить в виде
/0
0
0
а
А
1
°\
0
0
6
В
F
J
к
0
с
С
G
К
О
S
d
D
н
L
Р
Т
3
е
I
м
Q
и
г
0
е
N
R
V
h
0
0
\0
/
W
д
0
0
0/
На практике ограничивающие значения a, b, с, d, е, ^ д, h, i, j, к, I очень малы, так что они в сущности «белые» и лишь слегка тронуты краской. На этом шаге мы трансформируем ввод к конфигурации
(1
к
0
0
0
0
0\
А
J
S
0
0
0
(
)
а
F
О
3
0
0
(
)
0
В
К
т
0
0
(
)
0
b
G
р
г
0
(
)
0
0
С
L
и
0
(
)
0
0
с
Н
Q
h
(
)
0
0
0
D
м
V
(
)
0
0
0
d
I
R
(
7
\0
0
0
0
Е
N
V
v)
а позже получим
/
А
1
J
к
S
0
\
F
О
j
В
G
К
Р
Т
г
С
Н
L
Q
и
h
D
I
М
R
V
д
\Е
N
/
(Ввод повернутых пиксельных данных 7) =
{ for (г = 0; г < игу\ г-Н-) for (j = 0; j < пгя; j'44-) {
ii = т 4- г — j; 33 = г 4- j 4-1 — т\
if (гг > 0 Л ii <m-Vm ^зз >0Лзз < п 4- тг) а[гг][г] = 1.0 — brightness * getchar ()/255.0;
else trash = getchar^;
}
а[0][п — 1] = 1.0 — brightness /* снова запомнить «потерянное значение» */ }
Эта программа использовалась в разд. 5.
Диффузия ошибки. Мы конвертируем значения зачерненности к 65, 33 или 17 градациям, обобщая алгоритм Флойда—Стейнберга для адаптивных уровней серого [2]. Идея состоит в том, чтобы охватить все изображение по одному пикселю за раз и найти наилучшее значение плотности в текущей рассматриваемой позиции и диффузию ошибки в соседних еще не обработанных пикселях.
Для данного шрифта с к черными точками в литере к при 0 < к < I, мы можем предположить, что видимая плотность fc-й литеры должна быть к/1. Но физические свойства устройств вывода делают действительную плотность нелинейной. Следующая таблица основана на измерениях, полученных при наблюдении за шрифтом ddith300 на лазерном принтере Canon LBP-CX, и они должны быть достаточно точными для практических целей на аналогичных устройствах. Но на самом деле эти измерения не могут быть чересчур точными, потому что считывания не были строго монотонными и потому что тонер распределялся неравномерно между верхней и нижней кромками страницы. Пользователи могут сделать свои собственные измерения, прежде чем адаптировать эту подпрограмму к другим устройствам.
(Глобальные переменые 4) +=
float d[65] = {
0.000,0.060,0.114,0.162,0.205,0.243,0.276,0.306,0.332,0.355,
0.375,0.393,0.408,0.422,0.435,0.446,0.456,0.465,0.474,0.482,
0.490,0.498,0.505,0.512,0.520,0.527,0.535,0.543,0.551,0.559,
0.568,0.577,0.586,0.596,0.605,0.615,0.625,0.635,0.646,0.656,
0.667,0.677,0.688,0.699,0.710,0.720,0.731,0.742,0.753,0.764, 0.775,0.787,0.798,0.810,0.822,0.835,0.849,0.863,0.878,0.894, 0.912,0.931,0.952,0.975,1.000};
В основном цикле мы будем стремиться найти наилучшую аппроксимацию для а[г][Д на основе доступных плотностей d[0], d\p\, d[2p], d[3p], ..., где p есть 1, 2 или 4. Для этой цели хорошо подходит прямая аппроксимация бинарного поиска:
(Найти Z, такое, что АЩ находится как можно ближе к a[i][j] 9) =
if (a[i][j] < 0.0) I = 0;
else if (a[i][j] >1.0) Z = 64;
else { register int lod = 0, hid = 64;
while (hid — lo.l > p) { register int midd = (ZoJ + hid) )£> 1;
/* hid — lod каждый раз делится пополам, так что midd кратно р */
if (a[i][j] > d[mzcLZ]) lod = midd;
else hid = midd;
}
if («ИЫ - d(M < dVhi-i"\ - аЙЫ) i =ZoJ;
else I = hid;
}
Эта программа использовалась в разд. 10 и 11.
Поскольку прямоугольный случай самый простой, рассмотрим его первым. Наша стратегия состоит в том, чтобы пройти каждый столбец сверху вниз, начиная слева, и распространить ошибку на 4 необработанных соседа.
^define alpha 0.4375/* 7/16, диффузия ошибки на соседа S ♦/
^define beta 0.1875/* 3/16, диффузия ошибки на соседа NE */
#define gamma 0.3125/* 5/16, диффузия ошибки на соседа Е */
#define delta 0.0625/* 1/16, диффузия ошибки на соседа SE */
(Обработка a[i][j] в прямоугольном случае 10) =
{ register float err;
if (г = 0 V г > игу) I = 0;
/* вне области вывода следует использовать белое */
else (Найти Z, такое, что d[Z] находится как можно ближе к a[i][j] э);
err = a[i][j] — d[Z];
aHl7] = (float) (Z/p);
/* от сих nop a[i][J] является тем уровнем, когда плотность отсутствует */ if (г < игу) а[г 4- l][j] += alpha * dampening * err;
if (j < urx — 1) {
if (г > 0) а[г — l][j 4-1] 4-= beta * dampening * err;
аИЬ + 1] += gamma * dampening * err;
if (г < игу) а[г 4- l][j 4-1] 4-= delta * dampening * err;
}
}
Эта программа использовалась в разд. 12.'
Случай поворота, в сущности, тот же самый, но необработанными соседями a[z][j] теперь являются a\i 4- 1][J], ^ИЬ 4-1], a\i 4- 1] [J 4-1] и а[г 4- 2][7 4-1]. (Например, восемь соседей литеры К в матрицах из п. 7 суть В, F, J, О, Т, Р, L, G.)
Часть вычислений на этом шаге опущены, поскольку известно, что эти значения равны нулю.
(Обработка а[г][у] в случае поворота и) =
{ register float err;
if ((г > 1) < j - n V (г > 1) > j) Z = 0;
/* вне области вывода следует использовать белое */
else (Найти Z, такое, то d[Z] находится как можно ближе к a[i][j] 9);
err = a[i][j] — d[Z];
«ИЬ! = (float) (Z/p);
/* от сих пор а[г][у] является тем уровнем, когда нет плотности */
if (г < m 4- m — 1) а [г 4- 1] [j] 4-= alpha * dampening * err;
if (j < m 4- n — 2) {
« + 1] 4-= beta * dampening * err;
if (i < m 4- m — 1) a[i 4- l][j 4-1] 4-= gamma * dampening * err;
if (г < m 4- m — 2) a [г 4- 2][j 4-1] 4-= delta * dampening * err;
}
}
Эта программа использовалась в разд. 12.
Наконец, мы готовы все объединить.
(Трансляция ввода в вывод 12) =
р = 64/(leuels — 1);
if (р/2) {
for (j = 0; j < urx; j++)
for (г = 0; i < ury 4-1; г 44-) (Обработка a[i][j] в прямоугольном случае ю);
for (г = 1; г < игу; г++) {
for (j = 0; j < итх; j-H-)
printf ( "%c", (p = 1 ? ’O’ : ((г + j) &; 1) ? ’a’ : * A’) 4- (int) a[i][j]);
printf (" An");
}
}
else {
for (j = 0; j < m 4- n — 1; j-H-)
for (г = 0; г < m 4- m; i-H-) (Обработка a[i][j] в случае поворота 11);
for (г = 0; г < ттг 4- ттг; г44-) {
for (j = 0; j < тг; j-H-) printf ("7tc", *0* 4- (int) a[i][J 4- (г ^> 1)]);
printf (" An");
printf ("\\endhalftone\n");
Эта программа использовалась в разд. 1.
Приложение 2. Пиксельная оптимизация
Здесь приводится другая короткая программа на CWEB. Она использовалась для создания специального шрифта для портрета Моны Лизы.
Введение. Эта программа создает METAFONT-файл для особого шрифта, предназначенного исключительно для аппроксимации данной фотографии. Предполагается, что входной файл (stdin) является EPS-файлом, полученным программой Adobe Photoshop™ на платформе Macintosh с бинарной опцией EPS, содержащей тп строк в каждом из п столбцов. В терминологии Photoshop это значит, что изображение высотой тп пикселей и шириной п пикселей в шкале серого с разрешением 72 пикселя на дюйм. Файл вывода (stdout) должен быть последовательностью из тп строк типа
row 4; data "e8...d9”;
это означает, что пиксельные данные для строки 4 представляют собой цепочку из п битов 11101000... 11011001, закодированную как шестнадцатеричная строка длины п/4.
Для простоты предположим, что т = 512ип = 440.
#define ттг 512/* много строк */
#define п 440/* много столбцов */
#include <stdio.h>
float a\m 4- 2][n 4- 2];/* зачерненность: 0.0 белое, 1.0 черное */
(Глобальные переменные 4)
(Подпрограммы 12)
void main (int argc, char *ar^v[])
{ register int г, j, к, I, ii, jj, w;
register float err;
float zeta = 0.2, sharpening = 0.9;
(Проверка нестандартных множителей zeta и sharpening 2);
(Проверка начальных строк входного файла з);
(Ввод графических данных 5);
(Трансляция ввода в вывод 15);
(Выдача ответов 17);
}
2—6. [Пункты 2-6 этой программы в целом идентичны соответствующим пунктам программы из приложения 1, но несколько проще; вырежем конец п. 6, где начинаются интересные различия.]
Мы используем тот факт, что глобальные массивы изначально нулевые, полагая, что полностью белые строки из нулей располагаются выше, ниже, слева и справа от вводимых данных.
(Ввод прямоугольных пиксельных данных 6) =
for (г = 1; г < игу; i-H-)
for (j = 1; j < urx\ J44-) a[i][j] = 1.0 — getchar ()/255.0;
Эта программа использовалась в разд. 5.
Диффузия точки. Наша задача состоит в том, чтобы от 8-битовых пикселей перейти к 1-битовым, т. е. от 256 оттенков серого к примерно такому положению, когда используются только белое и черное. Используемый при этом метод называется диффузией точки (см. [7]); это работает следующим образом. Пиксели разделяются на 64 класса, пронумерованных от 0 до 63. Мы конвертируем значения пикселей в нули и единицы, назначив сначала всем пикселям класс 0, затем всем пикселям класс 1 и т. д. Ошибки, получившиеся на каждом шаге, распределяются между соседями более высокого класса. Это осуществляется посредством предварительного вычисления таблиц class-row, class.col, start, deLi, deLj и alpha, функции которых легко понять из следующего фрагмента программы:
(Выбор пиксельных ошибок и распространение (диффундирование) ошибок в буфере т) =nt
for (А: = 0; к < 64; А; 44-)
for (г = class-row [А:]; 4 < т; г += 8)
for (j = class-coiy^; j < n; j += 8) {
(Решение о цвете пикселя [г, j] и результирующая ошибка err 9);
for (Z = sZarZ[A;]; I < start\k 4-1]; /44-)
а[г 4- deZ_i[Z]][j 4- cZeZ-j[Z]] 4-= err * alpha\T\;
}
Эта программа использовалась в разд. 15.
Мы будем использовать следующую модель для оценивания эффекта данного битового изображения в выводе: если пиксель черный, его зачерненность
равна 1.0; если он белый, но по крайней мере один из четырех соседей черный, его зачерненность равна zeta; если он белый и его четыре соседа тоже белые, то его зачерненность равна 0.0. Лазерные принтеры 1980-х годов имели тенденцию таким образом распылять тонер, чтобы в первом приближении иметь в этой модели zeta = 0.2. Значение zeta должно находиться между —0.25 и +1.0.
Для того чтобы облегчить процесс вычислений в этой модели, в дополнительном массиве аа поддерживаются значения кодов white, gray или black. Все ячейки изначально белые (white); но как только мы решили сделать пиксель черным (black), мы меняем его белых (white) соседей, если таковые имеются, на серых (gray).
#define white 0/* код для белого пикселя со всеми белыми соседями */
#define gray 1/* белый пиксель с 1, 2, 3 или 4 черными соседями */
^define black, 2/* код для черного пикселя */
(Глобальные переменные 4) +=
char аа[т + 2][п + 2];
/* статус white, gray или black для окончательных пикселей */
На этом шаге определяется текущее однобитовое значение окончательных пикселей. Теперь пиксель либо белый (white), либо серый (gray); мы либо оставляем его в том виде как есть, либо зачерняем, а белых соседей делаем серыми, в зависимости от того, что приведет к минимизации величины ошибки.
Потенциально серые значения gray только что выбранных пикселей несколько усложняют эти вычисления. [Пояснение: первые две мои попытки оказались неудачными.] Обратите внимание, например, что при создании самого первого черного пикселя локальная зачерненность изображения увеличится примерно на 1 + 4zeta. Предположим, что исходное изображение полностью черное, так что a[z][j] равно 1.0 при 1<г<ти1<д<п. Если пиксель класса 0 установлен в white, то ошибка (т.е. зачерненность, которая необходима для диффундирования соседей более высокого класса) равна 1.0; но если он установлен в black, то ошибка равна —4zeta. Этот алгоритм будет выбирать black, пока не будет zeta > .25.
(Решение о цвете пикселя [z,j] и результирующая ошибка err 9) =
if (aa[i][j] = white) err = a[z][j] — 1.0 — 4 * zeta;
else {/* aa[z][j] = gray */
err = а[г][Д — 1.0 + zeta;
if (аа[г — lj[j] = white) err —= zeta;
if (аа[г + l][j] = white) err — = zeta;
if (aa[i][j — 1] = white) err —= zeta;
if (aa[z][j + 1] = white) err — = zeta;
/* черное лучше *//* черное лучше */aa[i - l][j] = gray; aa[i + l][j] = gray; aa[i][j - 1] = gray; aa[i][j + 1] = gray;aa[i - l][j] = gray; aa[i + l][j] = gray; aa[i][j - 1] = gray; aa[i][j + 1] = gray;}
/* черное лучше */
/* черное лучше */
aa[i - l][j] = gray; aa[i + l][j] = gray; aa[i][j - 1] = gray; aa[i][j + 1] = gray;
aa[i - l][j] = gray; aa[i + l][j] = gray; aa[i][j - 1] = gray; aa[i][j + 1] = gray;
ааИЬ1 = black;ааИЬ1 = black;if (err + a[i][j] > 0) {
ааИЬ1 = black;
ааИЬ1 = black;
if if if ifif if if if(аа[г — l][j] = white) (аа[г + l][j] = white) (aa[z][j — 1] = white) (aaHl7 + 1] — white)
if if if if
if if if if
}
else err = ^Hb]j /* сохранить это белым или серым */
Эта программа использовалась в разд. 7.
Вычисление таблиц диффузии. Таблицы для диффузии точки могут быть специфицированы большим количеством нудных операторов присваивания, но значительно приятнее вычислять их неким методом, приподнимающим завесу таинственности структуры, заложенной в основу.
(Инициализация таблиц диффузии 10) =
(Инициализация матрицы номеров класса 13);
(Компиляция «инструкций» для операций диффузии 14);
Эта программа использовалась в разд. 15.
(Глобальные переменные 4)4-=
char class.row [64], class-col [64];
/* первые строка и столбец для данного класса */
char cZas$_num6er [10] [10];/* номер данной позиции */
int кк = 0;/* сколько классов было до сих пор */
int start [65];/* первая инструкция для данного класса */
int deLi[256], deL;[256];/* относительное положение соседа */
float афДа[256];/* коэффициент диффузии для соседа */
Использованный здесь порядок классов — это порядок, в котором могут быть зачернены пиксели в шрифте для полутонов, основанном на точках в сетке, повернутой на 45°. На самом деле это в точности то же самое изображение, которое использовалось в матрице (2) для шрифта ddith300, обсуждавшегося ранее в данной работе.
(Подпрограмма 12) =
void store (int г, int j)
{
if (г < 1) г += 8; else if (г > 8) г —= 8;
if У < 1) j += 8; else if (j > 8) j -= 8;
class-number [i][j] = kk\
class_row[kk] = г; class_col\kk^ = j;
kk-H-\
}
void store, eight (int г, int j)
{
store(i J); store (i — 4, j 4- 4); store(1 — j, г — 4); store(5 — j, г);
store(j, 5 — г); store (4 4- j, 1 — г); store(5 — г, 5 — j); store (1 — i,l — j);
}
Эта программа использовалась в разд. 1.
(Инициализация матрицы номеров классов 13) =
store_eight (7, 2); store-eight (8,3); store-eight (8,2); st ore. eight (8,1);
store.eight(l, 4); store-eight(1, 3); store-eight (1,2); store-eight(2,3);
for (i = 1; i < 8; г44-)
class-number [г][0] = cZass_num6er[i][8], cZass_num6er[i][9] = cZass_num6er[i][l];
for (,j = 0; j < 9; J-H-)
cZass_num6er[0][j] = cZass-numier[8][j], cZass-num6er[9][j] = cZass-numZ>er[l][j];
Эта программа использовалась в разд. 10.
«Компиляция» на этом шаге очень медленно моделирует процесс диффузии, регистрируя выполняемые при этом действия. Позже все эти действия могут быть выполнены с более высокой скоростью.
(Компиляция «инструкций» для операций диффузии 14) =
for (А; = 0,I = 0; к < 64; Aj-H-) {
start [А;] = Z; /* I есть число инструкций, скомпилированных до сих пор */
г = classjrow[A:]; j = class_col^k\; w = 0;
for (гг = г — 1; гг < г 4-1; гг++)
for Уз = j - 1; зз < j + 1; 33 -Н)
if (cZass_num6er[u][7j] > к) {
deLi\l^ = И — г; deLj\l^ = 33 — j\
if (гг / г ^зз ^ 3) w-H-;/* соседи по диагонали получают вес 1 */
else ш += 2;/* ортогональные соседи получают вес 2 ♦/
}
for Уз = start [А:]; 33 < Z; jj++)
if (deLi^ / 0 Л deLj^ + 0) alpha\jj\ = 1.0/ш;
else alpha\jj^ = 2.0/ш;
}
start [64] = Z;/* с этого места l будет 256 */
Эта программа использовалась в разд. 10.
Синтез. Теперь мы готовы объединить все фрагменты воедино.
(Трансляция ввода в вывод 15) =
(Инициализация таблиц диффузии 10);
if (sharpening") (Четкая обрисовка ввода 16);
(Выбор значений пикселей и диффундирование ошибки в буфере 7);
Эта программа использовалась в разд. 1.
Опыт показывает, что диффузия точки часто лучше справляется с заданием, если применить операцию фильтрования, которая преувеличивает различия между яркостью пикселя и его соседей:
dij CXQ^j
- a
где.
i +1+1
= a 52 52
У 5=—1 e=—1
есть среднее значение величины atj и ее восьми соседей (См. обсуждение в [7]. Параметр сх является значением sharpening, которое преимущественно меньше 1.0.)
Можно вместо применения этого преобразования использовать схему буф- феризации, но легче сохранить это новое значение а^ в a^-i^j-i) и затем
впоследствии сдвинуть все обратно. Значения а^ и a^j не должны снова перезаписываться в нуль после этого шага, потому что они не будут снова изучаться. (Четкая обрисовка ввода 16) =
{
for (г = 1; г < т-, г++) for (j 1; j < п; j++)
{ float abar-,
abar = (a[i - l][j - 1] + а[г - l][j] + а[г - l][j + 1] + a[z][j - 1] + a[i][j] + °И1/ + 1] + а[» + !]Ь' ~ 1] + a[i + ИВ] + °[i + HL? + 1])/9-0;
а[г — l][j — 1] = (a[i][j] — sharpening * abar)/(1.0 — sharpening^-,
}
for (г = m; i > 0; г—) for (j = n; j > 0; j —)
«ИЬ1 = № - Ш - 1] < 0.0 ? 0.0 : а[г - 1][; - 1] > 1.0 ? 1.0 : а[г - l][j - 1]);
}
Эта программа использовалась в разд. 15.
Здесь предполагается, что п кратно четырем.
(Выдача ответов 17) =
for (г = 1; г < т; г-Н-) {
printf ("гоии7.с1;иаагаи\"" , г);
for (j = 1; 3 < п; j += 4) {
for (к = 0, w = 0; к < 4; к-Н-) w = w + w + (aa[i][j + A:] = black ? 1 : 0);
printf ("7.x", w);
}
printf ("\" ;\n");
}
Эта программа использовалась в разд. 1.
Из следующих примеров видно действие параметров zeta и sharpening на этот алгоритм:*
нет четкой обрисовки,С = .2нет четкой обрисовки,С = .2четкая обрисовка,четкая обрисовка,четкая обрисовка,четкая обрисовка,с = .2с = .2С = оС = о
нет четкой обрисовки,С = .2
нет четкой обрисовки,С = .2
четкая обрисовка,
четкая обрисовка,
четкая обрисовка,
четкая обрисовка,
с = .2
с = .2
С = о
С = о
Описанное в этой работе исследование частично финансировалось фондом System Development Foundation, а частично — фондом National Science Foundation по грантам 1ST- 8201926, MCS-8300984 и DCR-8308109.
Литература
Carolyn Caddes, Portraits of Success (Portola Valley: Tioga Press, 1986).
Robert W. Floyd and Louis Steinberg, “An adaptive algorithm for spatial greyscale,” Proceedings of the Society for Information Display 17 (1976), 75-77. Предыдущий вариант опубликован в SID 75 Digest (1975), 36-37.
Robert L. Gard, “Digital picture processing techniques for the publishing industry,” Computer Graphics and Image Processing 5 (1976), 151-171.
Ramsey W. Haddad and Donald E. Knuth, A Programming and ProblemSolving Seminar, Stanford Computer Science Department report STAN-CS-85-1055 (Stanford, California: June 1985). Этот семинар был записан также на видеокассету под названием “Problem solving with Donald Knuth,” The Stanford Video Journal 1 (Stanford, California: Stanford Instructional Television Network, 1985).
J. F. Jarvis, C. N. Judice, and W. H. Ninke, “A survey of techniques for the display of continuous tone pictures on bilevel displays,” Computer Graphics and Image Processing 5 (1976), 13-40.
Ken Knowlton and Leon Harmon, “Computer-produced grey scales,” Computer Graphics and Image Processing 1 (1972), 1-20.
Donald E. Knuth, “Digital halftones by dot diffusion,” ACM Transactions on Graphics 6 (1987), 245-273. [Обновленный вариант этой статьи опубликован в настоящем издании в виде гл. 22.]
д
т АтАтАтАтАтАтАтАтАтАтАтАтАтАтА * А?А*A*Ai "X*X*xAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAW *х*х*х*х*х*х*х*х*х*х*х*х*х”хАтАтАт*тГп¥ *х*х*х*х*х*х*х”х*х”х”г”г”х*гАт*т*тАтгпУ т*т*т*Т*т*т*т*т*т*т*т*т*т*т*т*т*т*ТГГПТ1 *Т*Т*Т*Т*т*т*т*т*Т*т*т*т*т*т*т*т*ТГЛТтг
V у A vVI vVI *VI у W то vVI vVI ♦ W vVI vVI *VI *VI *VI *VI ♦ W vVI vVIto vo vWvWvWtWtWtWtWtWtWrHi r*r*T*T*T*r*r*T*T*T*T*T*T*T^ $##|8#№Ш™
УтАтАтАт А тАтптАтто1ОТО!О^^ТАт Ат АтГЛ’П.тптптптп.тУУтПч.......................„
!«*»Ш|^^
^X*X*XVXVX*XYX*X*XVXVX&*^^^
®
УА
№^Ах*х*х*х*х*х^хА^п8
Ут*т*т*т*т*т*т*Т*тГтТ1 С”Х"Х л”х”х”х”х”х iVW 'xnx”x"x”x”x”xnxwxnx’’j Lwx"xwxwx"xwxwxwxwxwx* АДтАДУАДда^Аулу
Vt*T*T*t* т*РГТПТ*Т*ТГГ1
Г^^пПрдАУ ^АхАдАт A.m ^a*a*a*a^a'i,a*a*a*a*a'i Пт”*Т1 •VI •VI'vXtTvIvTI-VlvTl уП] Ь”х*х”х*х^0Х*х*х*х2П
Й№?Ад*дйетЙ88
[УХ*Х*Х*Х*ХЙ1*t*x*x*x*x™
ЖШ^®®^®
Г*тУ'тУ'Т”Х”х".т."х”х”тУ*х”т”тЛт."х"х”хРт.*
X-X”Jj х"х"х"х”х”х”х”х iX”a
Пш1ПшП1ПХ^’^”л”хпх”л”х”л’*х”х**Х”х ГтпТТТуТГтТТУТГРТу.Ту.Т”т~тпт”.тАт.”тЛт.утПТ"
МмдЖдШ^
Уа
Уа
Уа
Уа
к
Уа
г*
ттпИппУтТттет™^^
ШПШШ/Ч^Х^Х^Х^Х^Х^Х^Х^Х^Х^ХТЗ СТШцТПх ^х^х^х^х "Х^Х nX wXnXwXwjy ПшШрхп хп хп хп xw хп xw хп хп хп х^хч] ПТПтГТ*х*т*т*т*т*т*т*т*т*т*тПТ*1 ОЙт*х*х*х*х*х*х*х*х*х*х*х”х*Л 1ш00Й1пх’*х’*х**х’*х’*х’*х’*х’*хпх,,*ху*Х’Я 00W№wAwAwAwAwA wwAwAwAwA wwAww уУрЧ^^Д Ад А^Ад A^ А^Ад A^ A^ A^ A^ A^ AW г.АтАхАхАт.АхАхАхАхАхАхА^ At AtATwS х'” х'” X” Х^’Х^’х’’х’’ х’’ -I. -Т-ЧТУП
LSfx^x^x^x^x^x^x^x^x^x^x^x^x^xtfiQi ЗТшАтАтАтА,А,А,А,А,А,А,АтАт]УД YlinfTWTW тттттт • W • W • W • W • W • W • W т*Т1пЛПпП ЙЙЩ
гАтАтА?АУА?а
ж
8&Йа
Г*
УА
Г*
РА
РА
РА
WW'a^a^aVa^aVa*
W
Х*х*т*т*т*тАтДДДДДДАтАтАтАтАтАтАтА
ШШОЙ XWXWX*X*X*X*XWX”XWXWXWXWXWXWXWXWXWX4L4L4 *А*А*А*А*А*А*А*А*А*А*А*А*А*А*А*А*А*А*ААА
Оцифровка полутонов посредством диффузии точки
[Переработка статьи, первоначально опубликованной в ACM Transactions on Graphics 6 (1987), 245-273.]
В этой работе описывается метод аппроксимации пикселей, принимающих действительные значения, двоичными пикселями. Новый метод, называемый диффузией точки, вводится для того чтобы преодолеть недостатки других общепринятых методов. Для него требуется примерно такое же общее число арифметических операций, что и для метода Флойда—Стейнберга адаптивной градации серого цвета, и он так же хорошо подходит для параллельных вычислений. Однако в случае последовательных вычислений на машине с ограниченным объемом памяти нужно больше буферов и более сложная программная логика, чем в других методах. Для использования на принтерах с высоким разрешением можно доказать «гладкий» вариант этого метода.
Для заданного массива А размера т х п действительных значений, заключенных между 0 и 1, мы хотим построить массив В размера т х п из нулей и единиц, такой, что среднее его элементов В[г, J] при (i,j), находящихся вблизи (io, Jo), приблизительно равно A[io,Jo]- В приложениях А представляет собой интенсивность света в изображении при сканировании некоторым фотоустройством, В —бинарную аппроксимацию изображения при выводе на печать.
Диффузия ошибки
Одно интересное решение этой задачи было предложено Флойдом и Стейнбергом [7], которые вычисляли В из А следующим образом:
for г := 1 to т do for j := 1 to n do
begin if Afi, J] < 0.5 then B[i, J] := 0 else B\i, J] := 1;
err := Afi, J] — B\i, J];
Afi, j + 1] := A\iJ + 1] + err * alpha;
Afi + 1, j — 1] := А\г + 1, j — 1] + err * beta;
A\i + 1,J] := A\i + 1, J] + err * gamma;
A[i + 1, j + 1] := A\i + 1, j + 1] + err * delta;
end.
Здесь а, /3, 7 и 5 суть некоторые константы, выбранные так, чтобы диффундировать (рассеять) ошибку; они прямо пропорциональны близлежащим элементам, значения В которых еще только должны быть найдены. Флойд и Стейнберг предлагали взять (си,/?,7,5) = (7,3,5,1)/16. Аналогичный, но более сложный метод был опубликован ранее Манфредом Шрёдером [23].
Метод Флойда—Стейнберга часто дает блестящие результаты, но имеет свои недостатки. Прежде всего —это настоящий последовательный метод: значения B[m,n] зависят от всех тп элементов массива А. Более того, иногда он помещает «тени» в иллюстрацию; например, когда таким способом обрабатываются лица, посреди лбов могут оказаться тени от волосков. Ниже мы обсудим и другие возникающие проблемы.
Проблема теней может быть слегка смягчена выбором таких (а, /?, 7,5), что их сумма меньше единицы; тогда влияние A[i,j] на отдаленные элементы будет снижаться экспоненциально. Тем не менее добиться полного уничтожения теней таким способом не удастся. Предположим, например, что A[2,j] имеет постоянное значение а для всех г и j и пусть У = (/? + 7 + 5)/(1 — си) < 1. Если а очень мало, то все элементы B[2,j] для малых значений г будут равны нулю, а элементы A[2,j] для больших значений j будут постепенно приближаться к предельному значению
а(1 + У + • • • + У^ 1)(1 + а + си2+ •••)= а(1 + 0+ ••• +^ 1)/(1 —а)
для больших j. Если выбрать а так, чтобы его значение было только чуть меньше 1/2, то в (г + 1)-й строке массива В неожиданно многие значения установятся в 1, а во всех предыдущих строках они станут нулевыми.
Флойд [8] обнаружил, что тени исчезают, если интенсивности A[z,j] масштабировать. Например, каждую интенсивность A[z,j] можно заменить на 0.1 + 0.8Л[г, J]. Это работает, потому что человеческий глаз более чувствителен к контрасту, чем к уровням абсолютного сигнала.
Упорядоченное размывание
Другой подход к проблеме дает интересная техника упорядоченного размывания [14, 17, 18]. Здесь мы делим множество всех позиций (г, j) на, скажем, 64 класса, упорядоченных от 0 до 63 на основании значений г и j по модулю 8 как показано в табл. 1. Если (г,^) принадлежит классу к, то бит B[2,j] устанавливается в 1 тогда и только тогда, когда A[i,j] > (к + .5)/64. Иными словами, каждый пиксель представляет собой порог, основанный на значении в соответствующей позиции матрицы размывания. Обратите внимание, что если A[2,j] имеет постоянное значение а, то этот метод будет включать t пикселей в каждую подматрицу размера 8x8 массива В, где | 2/64 — а | < 1/128.
Диффузия точки
Техника упорядоченного размывания представляет собой абсолютно параллельную процедуру, свободную от теней, но она имеет тенденцию к нерезким изображениям. Так что очень хорошо было бы найти такое решение, которое
45
29
34
18
46
30
33
17
13
61
2
50
14
62
1
49
39
23
40
24
36
20
43
27
7
55
8
56
4
52
11
59
47
31
32
16
44
28
35
19
15
63
0
48
12
60
3
51
37
21
42
26
38
22
41
25
5
53
10
58
6
54
9
57
Таблица 1. Номера классов для упорядоченного размывания.
объединяло резкость метода Флойда—Стейнберга и параллелизм упорядоченного размывания.
Следующая техника, по-видимому, обладает требуемыми свойствами. Пусть мы разбили позиции (г, у) на 64 класса по г и у по модулю 8 как и ранее, но вместо матрицы из табл. 1 воспользуемся матрицей из табл. 2 на следующей странице. (Интуитивный подход получения табл. 2 будет объяснен позже, а пока будем просто считать, что это обычная перестановка чисел {0,1,..., 63}.)
Для любой такой матрицы мы можем выполнить следующий диффузионный алгоритм:
for к := 0 to 63 do
for all (г,у) of класс к do
begin if A[i, j] < 0.5 then B[i, j] := 0 else В[г,у] := 1;
err := А[г,у] — B[i, J];
(Распределение ошибки err среди соседей (г,у), номера класссов которых превосходят /с);
end.
Пиксели класса 0 вычисляются самыми первыми, затем вычисляются пиксели класса 1 и т. д.; ошибки переходят в соседние элементы до тех пор, пока они не будут вычислены.
Каждая позиция (г,у) имеет четыре ортогональных соседа (u,v), таких, что (и — г)2 + (и — у)2 = 1, и четыре диагональных соседа (и, и), таких, что (и — г)2 + (и — зУ = 2. Один из возможных способов распределить ошибку в диффузионном алгоритме выглядит так:
(Распределение ошибки err среди соседей позиции (г,у), номера классов которых превосходят к^ = w := 0;
for all соседи (u,v) позиции (г,у) do
if класс(и, и) > к then w := w + вес(и — г, и — у);
for all соседи (u,v) позиции (г,у) do
A[u, v] := A[u, v] + err * вес(и — г, v — j)/w.
35
48
40
32
28
15
23
31
43
59
56
52
20
4
7
11
51
62
60
44
12
1
3
19
38
46
54
36
25
17
9
27
29
14
22
30
34
49
41
33
21
5
6
10
42
58
57
53
13
0
2
18
50
63
61
45
24
16
8
26
39
47
55
37
Таблица 2. Номера классов для диффузии точки.
Мы можем выбрать весовую функцию так, чтобы вес(я,?/) = 3 — х2 — у2\ она присваивает ортогональным соседям вес вдвое больший, чем диагональным соседям. Более эффективно веса и списки соответствующих соседей вычислять заранее, раз и навсегда, поскольку номера классов не зависят от значений Л.
Применение этого метода подробно описывается в работе [14]. Программа в этой статье рассматривает также некое обобщение, в котором предполагается, что белые пиксели, расположенные ортогонально черным пикселям, привносят некоторый оттенок серого в общую зачерненность; это аппроксимирует характеристики растаскивания растровой точки в некоторых устройствах вывода.
Границы ошибки
Будем говорить, что позиция (г, у) является бароном, если она имеет соседей только более низкого класса. Бароны не распознаются в диффузионном алгоритме, так как они впитывают все локальные ошибки. На самом деле позиции «почти барон», имеющие только одного соседа более высокого класса, также относительно нераспознаваемы, так как они направляют все ошибки в одно и то же место. Структура класса матрицы из табл. 2 имеет только двух баронов (62 и 63) и только двух почти баронов (60 и 61). Напротив, структура класса матрицы упорядоченного размывания из табл. 1 более или менее удачна для диффузии, так как имеет 16 баронов (с 48 по 63).
Средняя ошибка на пиксель в методе диффузии точки обычно будет меньше, чем количество баронов, деленное на удвоенное число классов, если усреднение проводится по области, содержащей по одному пикселю в каждом классе. Например, мы ожидаем, что поглощается самое большее 2/128 единиц интенсивности на один пиксель в произвольной области размера 8x8, если используется приведенная выше матрица, поскольку ошибка, зафиксированная в каждом пикселе, компенсируется еще где-то, за исключением двух баронов, где ошибка, как правило, не больше 1/2.
Можно, однако, построить плохие примеры, в которых элементы матриц становятся отрицательными или больше 1. Следовательно, максимальная
ошибка не просто зависит от количества баронов. Наихудший случай можно ограничить следующим образом:
for к := 0 to 62 do
for I :— к + 1 to 63 do
граница[/] := граница[/] + otki * max(0.5, граница[/с]). '
Здесь cxki представляет собой постоянную диффузию ошибки от класса к к классу I, как определялось выше, либо нуль, если класс I не является соседом класса к. Из этого следует, что граница[/]— это максимальная ошибка, которая может быть передана позициям класса I из позиций нижних классов. Максимальная общая ошибка в области, содержащей по одной позиции каждого класса, равна сумме величин шах(0.5, граница[/с]) по всем баронам классов к. Это эквивалентно тому, что она равна сумме всех классов к величин шах(0,0.5 — граница[/с]). (Доказательство. Имеем
^2 max (0,0.5 — граница[/с]) к
= У2 тах(граница[/с], 0.5) — S к
= ^2тах(граница[/с], 0.5) (^к есть барон] + 52 ^^^ — ® к1>к
= У2 тах(граница[А:], 0.5) + ^2 граница^] — S к есть баронI
где S = УД граница [/с].) Для матрицы из табл. 2 имеем граница[62] = граница[63] « 4.3365; следовательно, средняя ошибка на пиксель всегда меньше, чем 8.674/64 < 0.136.
Исходные данные должны выбираться опасным противником, если ошибка собирается стать настолько плохой. (Множествами-противниками являются A[z,j] = 0.5+граница[/с], если (ij^ принадлежит классу к, где граница[/с] < 0.5; в противном случае А[г,Д = 0.) С другой стороны, противник, который хочет разрушить алгоритм упорядоченного размывания, может сделать это, зафиксировав ошибки величины 0.5 на пиксель в каждом блоке размера 8x8. (Если (ij) принадлежит классу к, а Д[г,^] есть (к + .5)/64, то В[г, J] = 1 для всех г из, но каждое А имеет среднюю плотность 0.5.)
Метод Флойда—Стейнберга имеет по этому критерию ошибку, близкую к нулю, поскольку все ошибки возникают на границе, площадь которой пренебрежимо мала.
Матрица из табл. 3 имеет только одного барона и одного почти барона; кроме того, это приводит к граница[63] « 7.1457. Следовательно, для диффузии точки это лучший вариант, чем матрица из табл. 2. Тем не менее бароны в большом изображении, основанном на табл. 3, могли бы все выстроиться в одну прямую, что могло привести к более серьезному визуальному проявлению структуры. Человеческий глаз имеет тенденцию замечать прямолинейные
25
21
13
39
47
57
53
45
48
32
29
43
55
63
61
56
40
30
35
51
59
62
60
52
36
14
22
26
46
54
58
44
16
6
10
18
38
42
50
24
8
0
2
7
15
31
34
20
4
1
3
11
23
33
28
12
17
9
5
19
27
49
41
37
Таблица 3. Упорядочение с единственным бароном.
участки из точек, но этот феномен не проявляется столь существенно при наложении сетки полутонов, когда изображение повернуто на 45° (см. [13]). Если все элементы матрицы А суть приблизительно 1/64, то в табл. 2 упорядочение производят два пикселя с В — 1 в каждой подматрице размера 8x8, тогда как в табл. 3 — только один. Один — правильное число, хотя табл. 3 дает менее удовлетворительную структуру.
На самом деле, табл. 2 давала точечное представление, которое используется для полутоновых решеток в коммерческих приложениях. Если мы сначала представим себе полностью белую матрицу, в которой мы успешно зачерняем все позиции классов 0,1,..., то получим повернутые на 45° решетки из черных точек, которые постепенно растут все больше и больше. Когда все классы < 32 станут черными, мы получим шахматную доску; начиная отсюда процесс зачернения по существу производит повернутые на 45° решетки из белых точек, которые постепенно становятся все меньше и меньше. Поскольку номер класса позиции (ij) плюс номер класса позиции (IJ + 4) всегда дает 63, изображение решетки из 63 — к белых точек после к шагов будет в точности таким же, как изображение решетки из 63 — к черных точек после 63 — к шагов, сдвинутое вправо на 4. Такая связь изображений из точек с диффундированным изображением делает разумным название нового метода диффузия точки.
Диффузию точки можно также испытать при меньшем масштабе с матрицей классов размера 4x4:
14
13
1
2
4
6
11
9
0
3
15
12
10
8
5
7
Ее можно применять к точкам с по разному выровненными углами, используя образцы, аналогичные представленным в работе Холладея [10], или с точками, имеющими эллиптическую форму, а не круглую.
Выделение краев
Ярвис и Робертс [11, 12] обнаружили, что упорядоченное размывание может быть последовательно улучшено, если выделить края исходного изображения. Суть идеи состоит в том, чтобы заменить А[г, J] на
А'М =А'М =1 — а1 — а
А'М =
А'М =
1 — а
1 — а
где ^[г,^] есть среднее значение для Л[г, j] и его восьми соседей:
1г+1J+1
^>^ = 9 52 52u=i—1 v=j—1
Новые значения А'[г J] имеют ту же среднюю интенсивность, что и старые, но когда а > О они увеличивают разность между A[i,J] и соседними пикселями. Если а = 0.9, то эти формулы упрощаются до хорошо известного соотношения (см. [19], ф-ла. 12.4-3):
Л'[г J] = 9Л[гJ] -^Л[и,у].
0<(и—г)2 + (г>—ji)2<3
Суммирование здесь ведется по всем восьми соседям точки (г, j).
Ярвис и Робертс сформулировали этот метод «ограниченного среднего» по другому, по своей сути очень близко к методу упорядоченного размывания. Однако из приведенных выше соотношений очевидно, что выделение краев можно использовать с любой полутоновой методикой.
На самом деле нам нужно также скорректировать A^i^j^ чтобы убедиться, что это значение находится между 0 и 1, подровняв его до 0 или 1 там, где оно выходит за эти крайние значения. В противном случае нужно будет диффундировать слишком много «ошибок».
Примеры
Иллюстрации на следующих страницах демонстрируют то, что происходит, когда три обсуждавшихся выше метода применяются к двум различным изображениям с выделенными краями и без выделенных краев. Множества данных преднамеренно очень малы, так что можно изучить все детали. Каждое изображение состоит из 360 строк по 250 пекселей в каждой.
Первое изображение представляет собой оцифрованный портрет Моны Лизы, который имеется в свободном доступе как часть станфордской GraphBase [15]; ее функция lisa(m, п, 255,0,0,0,0,0,0, workplace) обращается к массиву размера 360 х 250 байт, в котором каждый пиксель соответствует однобайтовому значению от 0 (черный) до 255 (белый). Мы хотим, чтобы черное было представлено единицей, так что можем заменить значение г на дробь (255.5 — г)/256. Но изображение из GraphBase в свою очередь будет слишком темным из-за того, что старая живопись Леонардо покрыта лаком; лучший результат для наших целей получится, если для высветления плотность возвести в квадрат. Таким образом, элементы данных А[г, J] первого изображения
все имеют вид ((255.5 — г)/25б)2, где г есть байт из GraphBase для пикселя в строке г и столбце j.
Второе изображение вычисление искусственным образом по формулам: для заданных г и j в диапазонах 1 < г < 360 и 1 < j < 250 положим х = (г — 120)/111.5 и у = (j — 120)/111.5. Если х2 + у2 < 1, то значение А[г, j] есть (9Л-х — 4у — Вл/1 — х2 — у2 )/18; в противном случае А[г,7] является просто (1500г + j2)/1000000.
Данные с выделенными краями A^iJl были получены из исходных значений применением простой формулы, приведенной выше, для случая а = 0.9. Обратите внимание, что это на удивление эффективно при выявлении деталей портрета Моны Лизы, если иметь ввиду, что мы работали со сравнительно низким разрешением; можно заметить арки отдаленного акведука на фоне справа от портрета и завитки прически Моны Лизы. Однако ее загадочная улыбка при таком преобразовании стала грубее.
При применении диффузии точки к данным портрета Моны Лизы с выделенными краями (рис. 10) величина ошибки, концентрирующейся в позициях баронов, была в среднем 704.1/2835 « 0.248. Значения Д[г,Д при этом оставались между —0.65 и 1.96. Хотя мы могли наблюдать, что барон диффузии точки может приклеиваться примерно на 4.34 единицы ошибки в худшем случае, только 26 из 2835 позиций баронов были «плохими» в том смысле, что они концентрировали ошибку, величина которой была больше 0.5; это охватывает случаи, когда Л^Д достигает экстремальных значений —0.65 и 1.96. Кроме того, ошибка барона 24.14 единиц на пиксель особенно проявляется у границ; это сумма |А[г,Д| для г = 0, или г = т + 1, или j = 0, или j = тг + 1 после того, как диффузионный алгоритм завершил работу. Таким образом, общая недиффундированная ошибка становится равной 704.10 + 24.14 = 728.24 на 360 х 250 = 90000 пикселей, т. е. около 0.008 на пиксель.
Аналогичные результаты имеют место на рис. 9, 11 и 12; статистические
данные здесь
такие:
Рис.
9
min А[г,з]
-0.575
max А [г, j] 1.640
плохие бароны
22
общая недиффундированная ошибка
707.65 + 72.78 = 780.43
Рис.
10
-0.649
1.957
26
704.10 + 24.14 = 728.24
Рис.
11
-0.630
1.656
13
719.27 + 62.79 = 782.06
Рис.
12
-0.773
1.913
22
725.08 + 41.44 = 766.52
Алгоритм Флойда—Стейнберга имел недиффундированные ошибки только на границах:
min А [г, J]
Рис. 1-0.276
Рис. 2-0.331
Рис. 3-0.246
Рис. 4-0.324
общая недиффундированная max А[г, J]ошибка
1.163116.53
1.40566.14
1.03986.64
1.48348.20
Рис. 1 (без выделения краев).
Рис. 2 (с выделением краев).
Рис. 3 (без выделения краев).Рис. 4 (с выделением краев).
Рис. 5 (без выделения краев).Рис. 6 (с выделением краев).
Рис. 8 (с выделением краев).Рис. 8 (с выделением краев).
Рис. 8 (с выделением краев).
Рис. 8 (с выделением краев).
Рис. 7 (без выделения краев).
Рис. 9 (без выделения краев).Рис. 9 (без выделения краев).
Рис. 9 (без выделения краев).
Рис. 9 (без выделения краев).
Рис. 10 (с выделением краев).
Рис. 11 (без выделения краев).Рис. 12 (с выделением краев).
Напротив, алгоритм упорядоченного размывания диффундирует ошибки только в том смысле, что он старается сделать каждый блок размера 8x8 приблизительно корректным. Связанные с этим методом статистические данные таковы:
[360/81 [250/81 7 7
52 52 52 52 ^i8* _ к' &з- - 5i8i -к' ; г=1 j=l к=0/=0
эти суммы сводятся соответственно к (1000.72,1655.19,608.62,678.94) для рис. 5, 6, 7 и 8. Таким образом, упорядоченное размывание, грубо говоря, вдвое хуже диффузии точки в случае Моны Лизы, тогда как в случае шара оно повело себя несколько аккуратнее. Но абсолютная ошибка превышала 1.0 соответственно в (325,710,103,150) из 1440 блоков.
Рисунки 1-12 должны были быть показаны с относительно большими квадратными пикселями с стороной 0.22 мм, чтобы читатель мог явно различать включенные или выключенные элементы. Нам пришлось уменьшить эти иллюстрации в 6 раз, чтобы получить эквивалент коммерческого изображения среднего качества для полутоновых фотографий.
Проблемы
Метод упорядоченного размывания производит двоично-рекурсивную текстуру, которая не годится для большинства издательских приложений; такие «холодные» образы бывают полезными, вероятно, только когда нарочито подчеркивается заложенная в их основу технология. Результат, полученный методом Флойда—Стейнберга, обычно больше радует глаз, но тоже иногда страдает изъянами, когда невольно привлекают внимание змеевидные образования. Метод диффузии точки точно так же вводит свою собственную шероховатую текстуру. Таким образом, ни один из этих подходов не является полностью удовлетворительным в том смысле, что зритель, которому представлены иллюстрации такого размера, на интуитивном уровне может признать их приятными. Никакой иной полутоновой метод не даст лучшего результата при таком масштабе. Мы должны будем отойти на несколько метров и прищуриться, только тогда будет достигнут эффект непрерывного перехода тонов. Когда проводились эксперименты, примеры, подготовленные при помощи метода Флойда—Стейнберга, дали лучшие показатели, чем другие.
Но картина меняется, когда мы рассматриваем приложения для печати. Автор экспериментировал с различными вариантами этих изображений на лазерном принтере с разрешением 300 точек на дюйм (округленно 38% уменьшение по сравнению с представленными здесь размерами иллюстраций), и результаты методов Флойда—Стейнберга и упорядоченного размывания были абсолютно неудовлетворительными. Нелинейные эффекты процесса ксерографии приводили к появлению больших темных пятен в тех местах, где белые пиксели были чрезвычайно редкими; здесь получался резкий скачок между областями серого и черного. В экспериментах автора самый лучший отпечаток Моны Лизы на лазерном принтере получился с применением диффузии точ-
ки (см. [14]); все другие испробованные методы дали результаты значительно худшего качества.
Разумеется, лазерные принтеры всего лишь грубая аппроксимация устройств с высоким разрешением, используемых для высококачественных полиграфических оттисков. Современные цифровые фотоавтоматы с размером пикселя примерно 20 /ип (1270 пикселей на дюйм), могут производить великолепные полутона, моделируя аналоговый растровый метод, который использовался на оборудовании прошлых лет. В самом деле, метод упорядоченного размывания — но с матрицей диффузии точки размера 8 х 8 из табл. 2 вместо двоично-рекурсивной матрицы размера 8 х 8 из табл. 1 —в сущности моделирует традиционный подход к полутонам.
Естественно предположить, что мы способны выполнить работу даже лучше, чем раньше, если только сможем задуматься о том, как разумно использовать новую технику, потому что сейчас можно сделать столько, о чем даже нельзя было мечтать при возможностях аналоговой техники. К примеру, можно надеяться лучше воспроизвести фотографии Анселя Адамса методом Флойда-Стейнберга (при достаточно высоком разрешении), чем это было возможно любым из методов печати прошлого.
Но по кратком размышлении становится понятно, что подход Флойда- Стейнберга не годится для использования с высоким разрешением в силу физических ограничений. Крошечные капли краски просто не в состоянии образовать изображения типа представленных на рис. 1-4. Худший случай, вероятно, возникает, когда A[i,j] = 1/2 для всех г и j; затем алгоритм Флойда—Стейнберга выдаст шахматную доску с чередованием черных и белых квадратов, а типичный принтер конвертирует это в грязное пятно наподобие такого: ■ Упорядоченное размывание как на рис. 5-8 потерпит крах по той же причине. Оба этих метода предпочитают изолированные черные или белые пиксели.
Будет ли полезна описанная выше диффузия точки при высоком разрешении? Изучим это на примере уменьшенных на 27% рис. 9-12:
В такой сжатой форме каждый пиксель занимает .06 мм, т. е. примерно 423 пикселя на дюйм. Если мы повернем на 45° и разделим на 4д/2 (что является расстоянием между «баронами»), то увидим, что результат аналогичен 75-строчному растру, который дает качество как в газетном отпечатке. Эти изображения очень малы, а на устройствах с высоким разрешением они получатся еще меньше. На коммерческих растрах среднего качества разрешение равно 130 растровым точкам на дюйм или около 735 не подвергнутых повороту двухуровневым пикселям на дюйм. Реальная иллюстрация Моны Лизы будет, следовательно, иметь в сотни раз большее количество данных; мы должны помнить об этом масштабирующем множителе.
Мона Лиза в этом примере выглядит неплохо, но в репродукции шара есть серьезные огрехи. Наши глаза не замечают регулярных образований из точек, если точки достаточно малы, но мы быстро реагируем на изменения в текстуре. Оттенки серого в фоне позади шара должны были изменяться с очень четкой градацией, но заметны непрошенные контуры, потому что незначительные изменения интенсивности приводят к большим изменениям в изображениях, полученных диффузией точки. (Аналогичные, но менее заметные ненужные контуры можно разглядеть в фоне, полученном методом Флойда—Стейнберга (рис. 3 и 4) особенно там, где интенсивности А[г, j] близки к 1/2.)
Методы диффузии ошибки хорошо подходят для привлечения внимания к деталям картины, но удачный метод должен также «утаивать» те места, где данные демонстрируют некоторую активность. Изготовитель дискретных полутонов должен одинаково хорошо сочинять фоновую мелодию и партии изредка звучащих фанфар. Этот пример демонстрирует, что диффузия точки в том виде, как она определена выше, не годится для общего использования при высоком разрешении.
На самом деле есть другая серьезная причина, почему диффузия точки терпит фиаско. Можно показать, что если A[i, j] равно 1/2 при всех (г,^, то сформулированный выше алгоритм диффузии точки произведет идеальную шахматную доску с чередованием черного и белого. (Небольшие фрагменты шахматной доски проступают в отдельных частях рис. 11 и 12.)
На основании этих наблюдений приходим к заключению, что наш исходный критерий, гласящий, что среднее элементов B[i,j] вблизи (г'о,^о) должно быть приблизительно равно А[г*о, Jo], не достаточно хорош в смысле его математической привлекательности. В действительности мы хотим иметь критерий, принимающий во внимание искажения, производимые в процессе печати, равно как и последующие искажения и зрительный обман, производимые нашими оптическими рецепторами. Иными словами, восприятие человеком элемента B[i,j] вблизи (г"о, Jo) после распечатки должно быть примерно тем же, что и восприятие человеком элемента A[i,j] вблизи (г’о,/о)- Некоторые шаги в этом направлении были предприняты Аллебахом и Дальтоном [2, 6], которые включили в свой экспериментальный алгоритм визуальную модель.
Гладкая диффузия ошибки
Из обсуждения, проведенного в предыдущем разделе, по-видимому понятно, что методы, основанные на диффузии ошибки, обречены, равно как и приложения, имеющие дело с принтерами с высоким разрешением. Но мы не рассмотрели диффузию ошибки во всей ее мощи. Биллот-Хоффманн и Брюнгдал [5] сделали важное открытие, состоящее в том, что метод Флойда—Стейнберга репродуцирует градации серого, даже если постоянную ‘0.5’ заменить любым другим значением! Например, если мы положим B[i,j] := 1 только когда A[i,j] > 0.6, то будем устанавливать верхний левый угловой элемент В[1,1] равным нулю значительно чаще, чем раньше; но если мы так поступим, то станем распространителями большей ошибки, так что соседние пиксели
с большей вероятностью будут становиться равными 1. Биллот-Хоффманн и Брингдал обнаружили, что получающаяся в результате текстура улучшается, если пороги незначительно варьировать как функцию от г и j.
Рассмотрим теперь параллельный алгоритм более общего вида, чем диффузия точки. Все позиции (г, j) пикселей прдразделяются на г классов, нумеруемых от 0 до г — 1, а далее происходит следующее:
for к := 0 to г — 1 do for all (jj) класса к do
begin if A[x,J] < 6k then B[x, j] 0 else B[i, J] := 1;
err := A[x, j] — B[x,j];
for I : = к 4- 1 to r — 1 do
begin пусть (щу) ближайшее к (jj\ такое, что класс(щ-и) = I;
А[и, v] := А[щ v] 4- err * сад
end;
end.
Диффузионный алгоритм, который использовался при высоком разрешении, должен иметь некоторого рода свойство гладкости. На интуитивном уровне это значит, что малые изменения в данных пиксельных значениях A[x, J] должны приводить к незначительным изменениям в результирующих бинарных значениях В[г,Д. Например, если все А возрастают, то было бы замечательно, если бы все В оставались теми же самыми или возрастали. Можно задаться вопросом: существует ли последовательность значений параметров 6к и aki при 0 < к < I < г, такая, что изложенный выше общий алгоритм диффузии имеет следующее свойство?
Если изначально A[z, J] = а для всех г и j,
и если (т — .5)/г < а < (т 4- .5)/г для некого целого т, то приведенный выше алгоритм должен установить
и fl, если 0 < класс(г, у) < т;
L J [ 0, если т < классах, j) < г.
Это условие означает, что диффузионный алгоритм должен действовать подобно алгоритму размывания, когда эти данные постоянные.
Существует удивительно простое решение этих нелинейных ограничений. Можно взять
6к = ,5/(г — /с), Qki = 1/(г — к — 1) для всех 0 < к < I < г.
В частности, этот порог 6к строго меньше 1/2, до тех пор пока мы не достигнем барона класса к = г — 1. Для всех меньших классов ошибка распределена поровну между соседями высшего класса; иначе говоря, она не зависит от I.
Приведем доказательство. Предположим, что А[г,Д имеет начальное значение а = ао и что (т — 0.5)/г < а^ < (т 4- 0.5)/г. Если т > 0, алгоритм устанавливает все В[м] класса 0 в 1, а всем А[г,Д классов > 0 присваивает значения а\ = а0 4- (ао — l)/(r — 1) = (г°о — 1)/(г — !)• Теперь мы имеем
(m — 1.5)/(r — 1) < ai < (т — 0.5)/(r — 1). Если m > 1, алгоритм устанавливает все B\i,j"\ класса 1 в 1, а всем А[г,7] классов > 1 присваивает значения ^2 = oi + (ai — 1)/(г—2). Следовательно, (т —2.5)/(г—2) < о^ < (т—1.5)/(г—2); этот процесс продолжается до тех пор, пока мы не достигнем класса т с А[г,7] — ош и —0.5/(r — m) < ат < 0.5/(г — т). Алгоритм теперь устанавливает все B[i,j] класса т в 0, а всем А[г,7] более высоких классов присваивает значения am+i = ат + ат/^ — m — 1). Отсюда мы имеем —0.5/(г — т — 1) < Om+i < 0.5/(г — т — 1), следовательно, приведенное условие выполнено. Ч.Т. Д.
Хотя эти пороговые значения Ок расположены весьма несимметрично относительно 0 и 1, установленное свойство гладкости симметрично. Следовательно, метод не имеет смещения в сторону B[i,j] = 1, как это могло казаться. Тем не менее небольшое смещение имеется. Можно показать, например, что если г = 2 и если непрерывные значения А для классов 0 и 1 выбраны случайным образом независимо и равномерно, то полученные в результате двоичные значения В должны быть (00,01,10,11) с относительными вероятностями (3, 5,20,4)/32; следовательно, полный ожидаемый двоичный вес составит (5 + 20 + 8)/32 = 33/32, чуть больше единицы. Если г = 3, то вероятности для восьми возможных выводов В = 000, 001, 010, 011, 100, 101, 110, 111 при независимых и равномерно распределенных случайных значениях А составят соответственно (57,123,536,148,1423,557,2232,108)/5184; ожидаемое число единиц равно 115/72 « 1.597, т. е. чуть больше г/2. Вообще говоря, вероятность того, что г значений В установятся в 1, равна 1/(2гг!), а вероятность того, что они все установятся в 0, равна
2гг! шД \2/ } V V Т ) )
Но, разумеется, предположение о независимости случайных значений А не является хорошей моделью для действительных изображений.
Мы можем применить данный метод к случаю г = 32, используя матрицу классов из табл. 2, в которой все числа класса разделены на 2 (отбрасывая остатки). В самом деле, обычный метод диффузии точки, обсуждавшийся выше, дал бы точно такие же результаты, если бы мы рассматривали вместо метода с 64 классами метод с 32 классами. Но теперь мы хотим расширить соседа каждого пикселя от размера 9 до размера 32; мы рассматриваем ячейку (ij) как имеющую 32 соседа (iz, v), которые образуют структуру алмаза, определяемую соотношением
—3+|v"7l < и — г < 4 — |v — 7|;
эти 32 соседа (включая сам (г, 7)) содержат по одному элементу каждого класса. Соотношение, связывающее соседей, несимметрично — например, (4,1) является соседом (1,2), но (1,2) не является соседом (4,1); это не составляет проблемы.
Назовем полученный алгоритм гладкой диффузией точки. Описанный ранее метод диффузии точки требовал не больше арифметических операций,
чем обычный алгоритм Флойда—Стейнберга, поскольку для получения блока размера 8x8 требуется 256 сложений и 256 умножений. Напротив, алгоритм гладкой диффузии точки требует всего 62 деления для блока размера 8x8, поскольку ошибка распределена равномерно, но это выполняется посредством 992 сложений, что обходится несколько дороже.
Максимальное значение А[г,Д, получаемое за время работы алгоритма с г классами, появляется, когда А[г, J] настолько велико, насколько возможно при условии, что В\г,з\ устанавливается в 0 для всех классов < г — 1; это позволяет барону подойти произвольно близко к его верхнему пределу 1.5 — 0.5/г.
Труднее описать минимальное из возможных значений A[i,j]. При г = 32 оно возникает, когда значения А берутся малыми насколько возможно, так что В = 1 для классов от 0 до 19 и В = О для классов от 20 до 30. Этот крайний случай будет давать значение барона — 6(^ 4- ^ + п Н ~ —11.776. Пусть при произвольном г минимальное значение к таково, что гармоническая сумма 1/(г — к) 4Н 1/(г — 1) превосходит 1 — 0.5/г; тогда к = г — г/е 4- 0(1) и наименьшее значение, которое любое А[г,Д может принять, есть
k-r ( 122221\г
2 \т—кг—к—1г—к—2г—2г—1гуе
Поскольку гладкая диффузия точки имеет дело с чрезвычайно большим количеством соседей, ошибка может оказаться в позициях, весьма удаленных от источника. Например, существует путь, простирающийся от класса 7 к 9, к 14, к 16, к 18, к 20, к 23, к 27, к 29, к 30, к 32, к 36, к 42, к 45, к 48, к 51, к 53, к 55 в матрице из табл. 2 (до тех пор пока количество классов не будет поделено пополам); это передвижение затрагивает 30 строк ниже начальной точки! Ошибка, однако, умножается на малые постоянные, так что пока она достигнет конца своего путешествия, она значительно демпфируется. Другие пути могут вести наверх, затрагивая до 11 строк (например от 16 к 18, к 22, к 25, к 28, к 39, к 42, к 49). Самым важным последствием такого длинного пути является тот факт, что оптимальное последовательное выполнение, хранящее одновременно в памяти лишь несколько последовательных строк, нуждается в буфере из 16 строк, а обычному методу диффузии точки требуется всего 7 строк.
Сравнение с другими методами
Пол Ротлинг [21, 22] разработал некий похожий параллельный алгоритм для полутонов высокого разрешения. Его метод ARIES, расшифровываемый как Alias-Reducing Image-Enhancing Screener (экранирование, размывающее изображение с выделенными краями), представляет собой, в сущности, модифицированную схему размывания, в которой пороговые уровни устанавливаются для каждой точки. ARIES сначала формирует множество всех значений А[г,Д — к/т^ вносящих вклад в единственную точку, положение которой (z,j) соответствует уровню к в матрице размывания и г есть общее количество пикселей на точку; затем ARIES устанавливает В[г,Д := 1 в m позициях (ij) с
наиболее высоким количеством очков (рейтингом) по этому критерию, где т выбирается равным средней интенсивности этой точки.
На рис. 13 и 14 приведены результаты применения ARIES к нашим двум образцам. Здесь были использованы 32-пиксельные точки и снова за основу была взята матрица классов из табл. 2, в которой все номера классов поделены на 2. (Если (г’о, Jo) есть пиксель класса 0, то 32 пикселя точки суть ее 32 соседа, как это определено выше, именно (го + 5, Jo + с), где — 3 + |Д| < 6 < 4 — |5|.) На рис. 15 и 16 соответственно показаны результаты применения гладкой диффузии ошибки. Исчезли вызывающие проблемы искусственные контуры, которые были на рис. 12, но появился незначительный оставшийся необъясненным эффект, поскольку каждая точка имеет только 32 пикселя и может представлять всего 33 тени серого. Каждый алгоритм производит в результате изображения, которые представлены великолепными полутонами; при разрешении 423 пикселя на дюйм они выглядят примерно так:
Результаты применения гладкой диффузии точки возможно несколько более резко очерчены, чем полученные посредством ARIES; они также несколько темнее (хотя, как ни парадоксально, рис. 16 выглядит немного светлее, чем рис. 12, полученный на устройстве с низким разрешением).
Ошибка метода ARIES гарантированно не превысит 0.5 в каждой позиции точки и в среднем будет составлять 0.25 на одну точку. Наши образцы изображений включают 2943 точки (или части точки); с достаточной долей вероятности можно заключить, что общая ошибка метода ARIES была 723.42 на рис. 13 и 721.49 на рис. 14.
На рис. 15 и 16 значения A[z][J] остаются меньше 1.0007 во все время работы алгоритма гладкой диффузии. Но отрицательные ошибки имеют тенденцию накапливаться, достигая самого низкого уровня —4.555 в портрете Моны Лизы и —3.703 в изображении шара (оба значения достигаются в позициях барона). В 2835 баронах скапливается 2337.15 единиц ошибок в случае портрета Моны Лизы и 977.23 в случае шара, по большей части отрицательных, когда алгоритм пытается сделать изображение чуть светлее. В общей сложности «плохими» были 1349 баронов на рис. 15 и 595 на рис. 16, в каждом случае поглощались ошибки, более отрицательные, чем —0.5. Соседи, составленные из 32 ячеек, также вызывали по существу большее рассеяние на границах: 2072.49 и огромное 4150.88. Таким образом, общие объемы ошибок на пиксель на рис. 15 и 16 становятся 0.049 и 0.05 7 соответственно в сравнении с 0.008 для ARIES. Ошибки на границах пренебрежимо малы при достаточно больших размерах изображения.
Рис. 15 (оцифровано посредством гладкой диффузии точки) Рис. 16
Алжи [1] предложил обобщение метода ARIES в виде функции ранга
А\г, j] — ак,
где си — настраиваемый параметр. Схема ARIES представляет собой специальный случай а = 1/г; другая схема под названием «структурированные элементы изображения» Приора, Синка и Рубинштайна [20] представляет собой специальный случай а = 0. Если мы положим а —» ею, то получим методы, в которых каждая точка выбирается из заданного репертуара шаров. Удовлетворительные результаты по таким схемам получил Роберт Л. Гард [9], который использовал чередующиеся образы из половин точек. Другой родственный алгоритм описан Анастассиу и Пенингтоном [3].
Обычно принтеры отличают «полутоновые» рисунки от «штриховых»: с теми и другими обращаются по-разному. Но когда используется цифровое печатное оборудование с высоким разрешением, мы не должны делать никаких различий. Например, фотография может содержать текстовую информацию, скажем факсимиле подписи. Зачем нужно растрировать этот текст, если его можно представить более четко? Методы наподобие ARIES и гладкой диффузии точки в состоянии адаптировать изображение к тому виду, какой требуется.
Мы можем, разумеется, сконструировать такие входные данные, для которых метод диффузии точки даст на печати совершенно неудовлетворительный результат. Например, если каждое значение А[г,Д уже есть 0 или 1, гладкая диффузия точки будет просто устанавливать В[г,Д := A[i,j] для всех (t^j); мы можем столкнуться со значениями A[i,j], которые не годятся для печати, вроде шахматной доски. Но мы выдвигаем предположение, что такие данные на практике не встречаются. Подобные шумы могут быть отфильтрованы до начала процесса оцифровки.
К настоящему моменту автору неизвестны методы, дающие на цифровом фотонаборном устройстве с высоким разрешением изображения лучшего качества, чем производит алгоритм гладкой диффузии точки. Однако было бы заведомо необдуманным выдвигать экстравагантные претензии по отношению к этому новому методу: до сих пор проведено весьма ограниченное количество экспериментов. Если гладкая диффузия точки ведет себя согласно поданным ею надеждам, то реализация на компьютерном оборудовании — возможно вместе со сканером — может оказаться притягательной возможностью.
Благодарности
Большую помощь оказали референты, указавшие мне литературу, с которой я не был знаком. Благодаря этому я постоянно совершенствовал методы и менял их описание в данной статье. Я также признателен Б. К. Р. Хорну за то, что он предоставил в мое распоряжение результаты своих многочисленных экспериментов с полутонами, произведенными компьютером.
Работа над этой статьей частично финансировалась фондом NSF по гранту CCR-8610181.
Литература-
Stephen Н. Algie, “Resolution and tonal continuity in bilevel printed picture quality,” Computer Vision, Graphics, and Image Processing 24 (1983), 329346.
J. P. Allebach, “Visual model-based algorithms for halftoning images,” Proceedings of SPIE, the Society of Photo-Optical Instrumentation Engineers 310 (1981), 151-157.
Dimitris Anastassiou and Keith S. Pennington, “Digital halftoning of images,” IBM Journal Research and Development 26 (1982), 687-697.
В. E. Bayer, “An optimum method for two-level rendition of continuous- tone pictures,” Conference Record of the IEEE International Conference on Communications 1 (1973), (26-ll)-(26-15).
C. Billotet-Hoffmann and 0. Bryngdahl, “On the error diffusion technique for electronic halftoning,” Proceedings of the Society for Information Display 24 (1983), 253-258.
J. C. Dalton, Visual model based image halftoning using Markov random field error diffusion, thesis, University of Delaware (December 1983). См. также J. C. Dalton, G. R. Arce, and J. P. Allebach, “Error diffusion using random field models,” Proceedings of SPIE, the International Society for Optical Engineering 432 (1983), 333-339; John Dalton, “Perception of binary texture and the generation of stochastic halftone screens,” Proceedings of SPIE, the International Society for Optical Engineering 2411 (1995), 207-220.
Robert W. Floyd and Louis Steinberg, “An adaptive algorithm for spatial greyscale,” Proceedings of the Society for Information Display 17 (1976), 7577. Более ранняя версия опубликована в SID 75 Digest (1975), 36-37.
R. W. Floyd, личная беседа (21 мая 1987 г.).
Robert L. Gard, “Digital picture processing techniques for the publishing industry,” Computer Graphics and Image Processing 5 (1976), 151-171.
Thomas M. Holladay, “An optimum algorithm for halftone generation for displays and hard copies,” Proceedings of the Society for Information Display 21 (1980), 185-192.
J. F. Jarvis and C. S. Roberts, “A new technique for displaying continuous tone images on a bilevel display,” IEEE Transactions on Communications COM-24 (1976), 891-898.
J. F. Jarvis, C. N. Judice, and W. H. Ninke, “A survey of techniques for the display of continuous tone pictures on bilevel displays,” Computer Graphics and Image Processing 5 (1976), 13-40.
R. J. Klensch, Dietrich Meyerhofer, and J. J. Walsh, “Electronically generated halftone pictures,” RCA Review 31 (1970), 517-533.
Donald E. Knuth, “Fonts for digital halftones,” TUGboat 8 (1987), 135-160. [Переработанный вариант этой статьи представлен в гл. 21 настоящего издания.]
Donald Е. Knuth, The Stanford GraphBase (New York: ACM Press, 1993). Портрет Моны Лизы представлен на с. 28.
Kurt Е. Knuth, James М. Berry, and Gary В. Ollendick, “An inkjet facsimile recorder,” IEEE Transactions on Industrial Applications IA-14 (1978), 156161.
J. 0. Limb, “Design of dither waveforms for quantized visual signals,” Bell System Technical Journal 48 (1969), 2555-2582.
Bernard Lippel and Marvin Kurland, “The effect of dither on luminance quantization of pictures,” IEEE Transactions on Communication Technology COM-19 (1971), 879-888.
William K. Pratt, Digital Image Processing (New York: Wiley, 1978).
R. W. Pryor, G. M. Cinque, and A. Rubenstein, “Bilevel image displays —a new approach,” Proceedings of the Society for Information Display 19 (1978), 127-131.
Paul G. Roetling, “Halftone method with edge enhancement and Moire suppression,” Journal of the Optical Society of America 66 (1976), 985-989.
Paul G. Roetling, “Binary approximation of continuous tone images,” Photographic Science and Engineering 21 (1977), 60-65.
M. R. Schroeder, “Images from computers,” IEEE Spectrum 6,3 (March 1969), 66-78. См. также Communications of the ACM 12 (1969), 95-101.
Заметки об оцифровке углов
[Первоначально опубликовано в Electronic Publishing —Origination, Dissemination, and Design 3 (1990), 99-104.]
Изучаются конфигурации пикселей, возникающие в месте соединения двух оцифрованных прямых. Вычисляется точное число различных конфигураций, когда тангенс угла, составленного этими прямыми, выралсен рациональным числом. Эта теория помогает объяснить эмпирически выявленный феномен, заключающийся в том, что две «половинки» наконечника стрелы выглядят по-разному.
Около десяти лет тому назад я был референтом докторской диссертации Кристофера Ван Вика [4], в которой вводился язык IDEAL для описания рисунков [5]. На двух иллюстрациях, выбранных им в качестве примеров, были изображены стрелки, составленные примерно из таких прямых:
Когда я на них взглянул, я решил, что либо в языке IDEAL, либо в процессоре troff, на котором был представлен вывод с языка IDEAL, есть какой-то изъян, потому что древки стрел не строго делят угол, образованный двумя короткими отрезками наконечников. Казалось, что древки были нарисованы либо слишком высоко, либо слишком низко. Крис вместе с Брайаном Керниганом потратил немало часов, стараясь понять, что было неправильно сделано, но никаких ошибок обнаружить не удалось. В конце концов его диссертацию перепечатали на фотоавтомате с высоким разрешением и погрешности стали не так заметны по сравнению с тем, как это выглядело на распечатке с лазерного принтера. Все еще оставались шероховатости, но я решил, что не стоит из-за неправильно расположенного пикселя портить Крису карьеру.
Я вспомнил об этом инциденте в конце 1983 г., когда собирался писать новую версию системы METAFONT для цифрового дизайна [3]. Я вовсе не стремился к тому, чтобы в моей системе был такой же изъян. Но каково было мое
удивление, когда я понял, что подобная проблема при растровом выводе просто неизбеснсна: оцифрованные углы почти никогда нельзя разделить точно пополам, за исключением очень специальных случаев. Две «половинки» угла обязательно хоть в чем-то будут отличаться друг от друга, если только разрешение не будет чрезвычайно высоким. Так что Ван Вик и Керниган были реабилитированы. Аналогичные проблемы обязательно возникают в MacDraw и во всех других графических пакетах.
Я, например, обратил внимание на такой курьезный факт. Рассмотрим угол 45°, который образуется отрезком прямой, направленным вверх к точке (^0,2/о) под углом с тангенсом 2 и затем опускающимся вниз по другой прямой с тангенсом угла наклона — 3:
До.Уо)
(х0 -t,yo- 2t)(х0 -t,yo- 2t)(хо + и, у0 - Зи)(хо + и, у0 - Зи)
(х0 -t,yo- 2t)
(х0 -t,yo- 2t)
(хо + и, у0 - Зи)
(хо + и, у0 - Зи)
Если мы оцифруем этот путь в виде угла, то верхний контур сможет принять одно из пяти различных очертаний, в зависимости от значения координат точки пересечения (j?q, у^^ которые не обязательно должны быть целыми. Вот эти варианты:
Теперь предположим, что этот угол в 45° представляет собой левую половину наконечника стрелы. Правая часть наконечника будет тогда углом в 45°, образованным прямой с тангенсом угла наклона — 3, пересекающим прямую с тангенсом угла наклона — 1/2:
(^1,У1)(^1,У1)Д1 + 2и, t/т - и)Д1 + 2и, t/т - и)($i +1, У1 - 3t)($i +1, У1 - 3t)
(^1,У1)
(^1,У1)
Д1 + 2и, t/т - и)
Д1 + 2и, t/т - и)
($i +1, У1 - 3t)
($i +1, У1 - 3t)
Для этого угла также имеется пять вариантов, получающихся после оцифровки, именно
Чтобы получить сам наконечник, мы должны совместить левый угол Pi с соответствующим правым углом Qj. Но ни один из углов Q не имеет такой же формы, как хотя бы один из углов Р. И вот в этом все и дело: человеческий глаз имеет тенденцию оценивать величину угла по тому, как угол выглядит в вершине. По этому критерию некоторые углы кажутся чуть больше других (за исключением случаев, когда эти углы изображены с высоким разрешением).
Следовательно, нет ничего удивительного в том, что правильно нарисованный угол типа Р может казаться неравным правильно нарисованному углу типа Q, даже хотя оба угла в самом деле окажутся равными 45° при высоком разрешении. (Именно белые пиксели, а не черные, и является источником недоразумений.) Здесь, например, имеется четыре абсолютно точно оцифрованные стрелы с увеличивающейся толщиной их древк:
Нам было бы интересно узнать, какова вероятность того, что оцифрованный результат окажется именно вида Рк, когда вершина (ящ З/о) выбирается на плоскости случайным образом. Имеет ли один из рисунков большую вероятность, чем другой? Когда используется самый естественный метод оцифровки, ответ будет «нет»: каждое Рк будет получено с вероятностью 1/5. В свою очередь все формы Qk также равновероятны при переменных значениях (^i,t/i).
Основная цель этих заметок — доказать, что только что установленные факты представляют собой частные случаи одного общего явления:
Теорема. Когда прямая с тангенсом угла наклона a/b пересекает прямую с тангенсом угла наклона c/d в точке (хо,уо), число разных оцифрованных форм, которые могут быть получены при различных (хо,уо), равно \ad — be|. Кроме того, каждая из этих форм появляется с одной и той же вероятностью, если точки (хо, у о) распределены на этой плоскости равномерно.
Предположим, что a/b и c/d суть рациональные числа. Две оцифрованные формы считаются равными, если они идентичны друг другу после параллельного переноса; поворот и отражение не допускаются.
Прежде чем мы докажем эту теорему, надо точно определить, что значит «оцифровать кривую». Мы будем следовать общей идее, которая излагается, например, в [3, гл. 24]. Рассмотрим плоскость, покрытую пикселями в виде квадратов, углы которых имеют целые координаты. Наша цель состоит в модификации заданной кривой так, чтобы ее путь целиком лежал на границах между пикселями. Если кривая задана в параметрической форме функцией z(t) = ^x(t),y(t^ при переменном I, ее оцифровка почти всюду определяется формулой
round z(t) = (round x(t), round y(t^
при переменном t, где round(aj) есть ближайшее к а целое число.
Следует быть особенно аккуратным, разумеется, в тех случаях, когда округляются значения, находящиеся посередине между целыми, так как в подобных случаях значение round(aj) неопределено. Для удобства будем предполагать, что путь z(t) ни в одном из пикселей не проходит через центр; иными словами, предположим, что z(t) никогда не становится равным (m + |, п 4- |)
для целых тип. (Точные попадания в центр пикселя происходят с вероятностью нуль, так что в теореме, которую мы доказываем, ими можно пренебречь. Чтобы вообще избежать центров пикселей, можно использовать бесконечный сдвиг пути, избегая, таким образом, неопределенностей, на которые было указано в интересной работе Брисенхама [1]; но нам нет необходимости вдаваться в подобные тонкости.) При упомянутом выше предположении, что если x(t) = т + |, то ‘round x(ty неопределено, значение y(t) = п будет неопределено и мы можем включить в оцифрованный путь целиком отрезок от (т,п) до (т + 1,п). Аналогично, когда t достигает такого значения, что round x(t) = т, но round ?/(£) = п или п + 1, мы включаем целиком отрезок от (т, п) до (т, п + 1). Это соглашение определяет искомый оцифрованный путь round z(tY
Когда оцифрованный путь z(t) возвращается в исходную точку либо начинается или заканчивается в бесконечности без самопересечений, это определяет область на плоскости. Соответствующий оцифрованный путь round z(t) также определяет область, а у этой оцифрованной области оказывается есть простое описание, если применить стандартные математические соглашения о «числе витков»: Пиксель с углами в точках (т,п), (т + 1,п), (т,п + 1), (т + 1, п + 1) принадлежит оцифрованной области, определяемой значениями round г (t) тогда и только тогда, когда его центр (т 4- ^.п + |) принадлежит неоцифрованной области, определяемой значениями z(t\ (Это красивое свойство оцифрованных кривых очень легко проверяется в простых случаях, но точное доказательство провести непросто, поскольку оно опирается на такие сложные вещи, как теорема Жордана о кривых. Необходимые подробности можно найти в приложении диссертации Джона Хобби [2], теорема А.4.1.)
Теперь мы готовы начать доказательство требуемого результата. Область, определяемая углом, выходящим из точки (яо^з/о), со сторонами, имеющими наклон a)b и c/d, можно описать неравенствами
а(х - Яо) - Ь(у - у0) > 0; с(х - х^ - d(y - у0) > 0.
(Нам может понадобиться обратить знаки, в зависимости от которых определяются четыре области, описываемые двумя прямыми, проходящими через точку (хо.уоУ которые предположительно определяются заданным угловым путем. Это можно сделать, заменив (а, 6) на (—а, —6) и (или) (с, d) на (—с, —d).) Установленная область содержит пиксели с нижним левым углом (т. п) тогда и только тогда, когда
а(т + | - rr0) - b(n + | - t/o) > 0 ; с(т + | - я0) - d(n + | - t/o) > 0.
Можно упростить обозначения, объединив несколько констант и положив а = “(^о - |) - Ь(уо - j) и ^ = с(х0 - |) - d(y0 - I):
ат — bn> а; ст — dn> Р.
Пусть R(a,P) есть оцифрованная область, составленная из всех целых пар (m,n), удовлетворяющих этому условию; такие пиксели находятся в оцифрованном угле, соответствующем точке (хо,уо).
Как указывалось выше, сохраняется предположение, что центры пикселей не находятся точно на прямых, образующих угол. Таким образом, мы вправе выдвинуть условие, что ат — Ьп ^ а и ст — dn ^ Р для всех пар целых чисел (m,n). Однако, если имеет место равенство, можно также определить оцифрованную область R(ct,P), обобщив неравенства ат — Ьп > а и ст — dn> Р, как установлено, вместо того чтобы трактовать это обстоятельство как особый случай. Обратите внимание, что R(a, 0) равно -R(fa|, [/?!); следовательно, мы можем в дальнейшем обсуждении предположить, что а и Р суть целые числа.
Другая вершина угла (xfQ,yfQ) приведет к параметрам (о/,/?'), определяющим другую область R(a',pf) таким же образом. Эти две области R(a,P) и R(af,Pf) имеют одну и ту же форму тогда и только тогда, когда одна есть параллельный перенос другой, т. е. R(a, Р) = R(a\Pf) тогда и только тогда, когда существуют целые (/с, Z), такие, что
(m, n) е R(a, Р) 4=> (т - к, п - I) е R(a\ Р').
Наша основная цель состоит в том, чтобы доказать, что число различных форм областей, отличающихся одна от другой, согласно приведенному выше соотношению эквивалентности в точности равно \ad — bc\.
Лемма. Пусть а, Р, af, Р’ суть целые числа. Тогда тождество R(a,P) = R(af,Pf) относительно тангенсов a/b и c/d выполняется тогда и только тогда, когда a — a' = ka — lb и Р — Р' = kc — Id при некоторых целых (k, Z).
Доказательство. Предположим, что R(a,P) = R(a',P') относительно a/b и c/d, и пусть (к, Г) есть соответствующее значение параллельного переноса. Тогда имеем
ат — Ьп> а^ ( а(т — к) — b(n — 1) > а'cm — dn> Р ) ( с(т — к) — d(n — I) > Pf
для всех целых пар (т,п). Пусть а" = af + ka — lb и Р” = Р' 4- к с — Id, такие, что
am — bn> а} ( ат — Ьп> а"
ст — dn > Р ) [ ст — dn> Рп
для всех целых пар (m,n). Это влечет за собой, что а = ап и Р = Р”. Так что если, скажем, мы имеем а < а", то можно найти целые тип, такие, что ат — Ьп = а и ст — dn > Р, поскольку а и Ь относительно просты; это могло бы удовлетворять правым неравенствам, но не левым. (Более точно, мы могли бы использовать алгоритм Евклида для нахождения целых а' и Ь’, таких, что аа’ — bb’ = 1. Тогда значения (m,n) = (aaf 4- bx, abf 4- ax) удовлетворяли бы левым неравенствам, но не правым для бесконечно многих целых х, потому что ad — be / 0.)
Таким образом, R(a, 0) = R(af,Pf) влечет за собой, что а — а! = ка — lb и Р — Р' = кс — Id. Обратное утверждение тривиально. □
Пусть к и I—целые, такие, что а = ка — lb. Лемма утверждает, что R(c^P) = R(0,P — кс + Id); следовательно, каждая оцифрованная область R(a^P) имеет ту же форму, что и оцифрованная область R^a^P^^ в которой а' = 0.
Осталось подсчитать количество отличающихся друг от друга областей R^P), когда Р есть целое число. Согласно лемме мы имеем R(^P) = Л(0, /3') тогда и только тогда, когда существуют целые (/с, Z), такие, что 0 = ка — lb и Р — Pf — кс — Id. Но ка = lb тогда и только тогда, когда к = Ьх и I = ах для некоторого целого х; следовательно это условие сводится к р — Р’ = bxc—axd = x(bc—ad). Другими словами, R(^ Р) = 7?(0, Р’) тогда и только тогда, когда Р-Р' есть кратное от ad — be. Число отличающихся друг от друга областей равно, следовательно, \ad — 6с |, как и утверждалось.
Чтобы полностью завершить доказательство теоремы, нужно убедиться, что каждый класс эквивалентности с равной вероятностью будет классом оцифрованных угловых областей, когда точка пересечения (х^уо) выбирается на плоскости случайным образом. Изменение обозначений с (х^уо) на (а^Р) переводит равные области в равные же области; так что мы хотим доказать, что классы эквивалентности R(c^P) равномерно распределены среди |ad — Ьс\ возможностей, когда действительные числа (а, Р) выбраны случайным образом. Случайный выбор действительных чисел (а^Р) приводит к равномерно распределенным парам целых чисел (М, \РД). А если [а] имеет некоторое фиксированное значение и Р/3] пробегает множество целых чисел, класс эквивалентности R(a,P) циклически пробегает все \ad — bc\ возможностей. □
Пристальное изучение приведенного выше доказательства показывает, что мы можем вывести явные формулы для этих множеств точек пересечения (^О)!/о)) которые производят эквивалентные формы областей. Пусть D = |ad — Ьс\ и пусть Rj обозначает форму, соответствующую области R(0,j) в доказательстве, где 0 < j < D. Оцифрованный угол будет иметь форму Rj тогда и только тогда, когда (j?q, уо) лежит в параллелограмме, вершины которого суть (1/2 — bj/D, 1/2 — aj/D) плюс
((6 - d)/D, (а - c)/D) , (-d/D,-c/D), (6/D, a/D), (0,0), или в параллелограмме, полученном параллельным переносом указанного на целую величину.
В особом случае а/b = 2/1 и c/d = (—3)/1 формы Rj суть то, что мы выше обозначили Pj; в особом случае а/6 = 3/(—1) и c/d = (—1)/2 формы Rj суть то, что мы называли Qj. Формы, которые возникают при оцифровке углов, зависят от значений (^0 mod 1, yQ mod 1) в единичном квадрате согласно
Обратите внимание, что параллелограммы, «окутывающие» модуль 1, занимают каждый по 1/5 области. Половина от Pj соответствует половине от Qb-F все другие половины соответствуют половине от Q7-3.
Если наклон любой прямой, образующей угол, выражается рациональным числом, число возможных форм бесконечно (на самом деле несчетно). Но мы все еще можем изучать такие оцифровки, исследуя форму только в непосредственной близости к точке пересечения, во всяком случае, эти пиксели наиболее критичны для восприятия человеком. Например, в упр. 24.7-24.9 книги The METAFONTbook [3] обсуждается правильный способ подгонки вершин к равностороннему углу так, чтобы он хорошо оцифровывался.
Мораль сей сказки —а сказки должны иметь мораль — вероятно такова: если вы хотите поделить угол пополам таким способом, чтобы обе половины визуально казались равными, то делящая прямая должна быть такой, что отражение относительно этой прямой всегда переводит пиксель в пиксель. Таким образом, делящая пополам прямая должна быть либо горизонтальной, либо вертикальной, либо проходить по диагонали под углом в 45° через вершины пикселей и/или их центры. Более того, ваш алгоритм превращения в прямую должен выдавать результаты, симметричные относительно этой отражающей прямой (см. [1]).
Теперь этот результат может успешно применяться в будущих исследованиях.
Благодарности
Я хочу поблагодарить референтов и редакторов за их замечания. В частности, представленное здесь доказательство теоремы предложил один из референтов. Мой вариант доказательства был гораздо сложнее.
Эти заметки подготовлены при частичной финансовой поддержке фонда National Science Foundation по гранту CCR-8610181.
Литература
Jack Е. Bresenham, “Ambiguities in incremental line rastering,” IEEE Computer Graphics and Applications 7,5 (May 1987), 31-43.
John Douglas Hobby, Digitized Brush Trajectories, Ph.D. thesis, Stanford University (1985). Опубликовано также как отчет Stanford Computer Science Department report STAN-CS-85-1070 (April 1985).
Donald E. Knuth, The METAFONTbook, Volume C of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1986).
Christopher John Van Wyk, A Language For Typesetting Graphics, Ph.D. thesis, Stanford University, (1980). Опубликовано также как отчет Stanford Computer Science Department report STAN-CS-80-803 (June 1980). Изображения «стрел» помещены в этой работе на с. 20.
Christopher J. Van Wyk, “A graphics typesetting language,” SIGPLAN Notices 16,6 (June 1981), 99-107.
TEXDR.AFT
Эта и следующая главы рассчитаны на читателей, разделяющих вместе со мной очарованность документом в его первоначальной, исходной, форме. Ничего из приводимых ниже материалов не предназначалось в момент их написания для какого-либо публичного использования. Тем не менее, чтение их сейчас дает хорошее представление о том, как зарождались идеи, составляющие основу TgX’a и METAFONT’a. ,
По-видимому, лучший способ показать, в какой обстановке появились и развивались Т^Х и METAFONT — воспроизвести некоторые записи из моего дневника за 1977 г^
7 Feb:
М w» ^р^', А»^—^ *^ ^"^ ""Г Г^*^
FMsA^wu* *Ow"iK
Ве/ е»Лу■
8 Feb:
^2cA(/n»C <м^ сАа** бдк^г^у
^^^’^«алМг^ ^ Mix"
^iiCMUt^. б£ orw^W -|^аМм| vaVL ^Л |V wy U»L«
^^ ТЬниА чАий cklir
Значительная часть моего рабочего времени в течение зимы 1977 г. была посвящена чтению вводного курса по структурам данных. На первой лекции, состоявшейся 5 января, было 170 студентов, однако только 80 из них действительно прослушали весь курс до конца (он завершился 18 марта) и выполнили все требуемые задания. Кроме того, много времени занимало исполнение административных обязанностей — я возглавлял кафедральную комиссию по со
ставлению экзаменационных вопросов и задач, руководил исследовательскими проектами, посвященными проблемам анализа алгоритмов, состоял в Комитете по кадровым перемещениям Станфордского университета. Кроме того, я писал фрагменты моей книги The Art of Computer Programming — в течение
Перевод дневниковых записей см. в примечании 3 переводчика в конце главы. — Прим, ред.
рассматриваемого периода времени я подготовил новый материал, касающийся билинейных форм для раздела 4.6.4 из тома 2, а также новый материал относительно побитовых (поразрядных) операций над данными для раздела 7.1 из тома 4. Однако 7 февраля, составляя справку для нашей комиссии по экзаменационным вопросам и задачам, я просмотрел корректуры новых учебных пособий и впервые получил возможность увидеть, как выглядит результат применения цифровых типографских технологий с высокой разрешающей способностью. Приобретенный таким способом опыт изменил мою жизнь. Уже 8 февраля я начал обсуждать с коллегами возможность использования этих технологий для печатания моих собственных книг.
Мой дневник за 1977 г. не говорит ничего о типографских делах до 30 марта, когда «жребий был брошен».
30 Mar:
Gtfby рюоб ’Gr wU X f\^l(y t*^ */&y look W^l x.' T AcMt T км -fo soIvL-Al р*>Ий~ ^ulf.^^ffo^v^lvtc^Hy
Seder* S^pv- at* cfuwtA^ {s a brijkt ^poT 'in МЧ u^ttk.
Мне пришлось посвятить весь апрель завершению других начатых мною проектов. Однако к началу мая я решил, что надо написать программу под названием ‘ТЕХ’ и стал быстро продвигаться навстречу новому приключению.
S*^^^ 7.1 4м>М ^i^t «f \:Од ц.ил.,-— Hurvry I
3 May:
4 May:4 May:l^f^^jUH^, "^un^ <h/f old б®^^^ М*4дР^1й^
4 May:
4 May:
С/вДАщЛ. v^0 ^Ff?^ jot* 7» I all р/Ь ^^t
(Д/avl ^r^L *fid 30 g*lkyf ***M &t^t' tjfiwrih^ e^d^ иим(м'к^
5 Мау:
Ayof e&S^* o^ "TVX j'Ax</<^ ♦
6 Мау:
Tex
7 Мау:
и^оД* Ъ wovzIqS k^foCf 77 ^ C^A^u^jj^ (Цъ о^с^^л_^
8 Мау:
Ho^ajA ^^. Tvo)g ^'^ 4d b™wcKc^AL ^hl^n^ ^^^i"
9 Мау:
&y^t up (ДА c^hVtS^iM^4^ S^W^ k^. ^ KovaIoS
10 Мау:
Mt/ w^ *T/H (^ <^u^* »€ A*Mj/^ еЛг. “11*|М~ аЬиЛ" ТЕХ -
И Мау:
^V>H^ luMcAl#^ $*V*A>M*
12 Мау:
\лМ< /ncff t^f^ ^ Tt^j s\>^eA vy Ы( \"^M* fy^ ^ ^°
- Предварительный вариант отчета, подготовленный мною в первой трети мая, был озаглавлен TEXDR.AFT, поскольку компьютер, которым я пользовался, не позволял создать файл с именем ТЕХ.DRAFT. Ниже приводится текст, написанный ночью 12 и 13 мая и предназначавшийся, в основном, в качестве памятной записки для самого себя.
Черновой вариант предварительного описания ТЕХ’а
Д.Кнут, 13 мая 1977 года
В этой записке я попытаюсь объяснить, что такое система ТЕХ для подготовки документов полиграфического качества, а также пути реализации данной системы. Хотя я и сам еще не понимаю в достаточной степени всех этих вопросов, думаю, что лучший способ разобраться в них — это попытаться объяснить существо дела кому-либо еще.
ТЕХ ориентирован на технические тексты. "Посвященные" произносят букву X в ТЕХ как греческое "хи" (ср. также шотландский звук 'ch’ в слове 'Loch Ness’), поскольку слово 'technical’ происходит от греческого корня, обозначающего искусство, а также технику. Я занимаюсь сейчас разработкой этой системы для того, чтобы использовать ее в подготовке к изданию моих книг из серии The Art of Computer Programming, т.е. первоначально система будет ориентирована и настроена таким образом, чтобы соответствовать стилю моих книг, однако не должно быть трудностей с приспособлением ее и для других аналогичных целей, если кому-то это понадобится.
Входом для ТЕХ’а является текстовый файл в формате, например, редактора TVeditor, используемого в Станфордской лаборатории искусственного интеллекта. Результат работы ТЕХ’а -- последовательность страниц, подготавливаемая "за один проход", пригодная для распечатывания на различных устройствах. Этот отчет представляет собой попытку объяснить, каким образом можно из входа получить нечто на выходе. Основная идея состоит в том, что требуемый процесс рассматривается как некая последовательность операций над двумерными "боксами"; упрощенно говоря, входом является длинная строка символов, взятых из различных шрифтов, причем каждый из символов может интерпретироваться как нечто, занимающее небольшую прямоугольную область, а выход получается "склеиванием" этих областей друг с другом, по горизонтали или вертикали, с соблюдением при этом различных соглашений относительно центрирования и выравнивания. Конечным результатом такого рода процесса будет совокупность больших прямоугольных областей, которые и будут представлять собой требуемые страницы. Если LISP работает с одномерными символьными строками, ТЕХ имеет дело с двумерными структурами, собранными из прямоугольных боксов; продолжая аналогию с LISP’ом, можно сказать, что ТЕХ располагает как горизонтальными, так и вертикальными операциями типа операции cons. К тому же, ТЕХ базируется еще на одной важной концепции -- понятии эластичного клея между боксами, представляющего собой некий "строительный раствор", который может растягиваться или сжиматься в той или иной степени, так что конструкции из боксов могут объединяться друг с другом гибким образом.
Чтобы объяснить принципы работы ТЕХ’а более подробно, я буду излагать их на двух уровнях: давать низкоуровневое описание того, как именно выполняется обработка в том или ином случае, а также высокоуровневое описание того, как реализуются те или иные сложные действия.
Для начала, имея в виду низкоуровневое описание, надо понимать, что вход ТЕХ’а -- это на самом деле не последовательность (строка) боксов, а файл из литер в 7-битовой кодировке. Этот файл именуется "внешний входной файл". Первое, что делает ТЕХ, — преобразует этот файл во "внутренний входной файл", следуя при этом таким правилам:
Удалить, если таковая имеется, первую управляющую страницу, сформированую редактором TVeditor.
Удалить первую (заглавную) строку с каждой из оставшихся страниц, заменить индикаторы конца страницы (символ 44) символами возврата каретки (45). Удалить все символы протяжки бумаги на одну строку (42). Удалить все знаки 7, и последовательности символов, следующих за ними, вплоть до очередного символа возврата каретки (но не включая его).
Удалить все пробелы, следующие за символом возврата каретки.
Если два или более символа возврата каретки следуют подряд, заменить их все кроме первого символами вертикальной табуляции (43). Они используются для обозначения конца абзаца, другими словами, пользователь задает конец абзаца либо двукратным нажатием клавиши возврата каретки, либо переводом страницы, за которым следует нажатие клавиши возврата каретки.
Заменить все остающиеся символы возврата каретки пробелами.
Если в строке следуют подряд два пробела, заменить их одним пробелом.
Добавить бесконечно много символов } справа.
Смысл применения правила 7 заключается в том, что ТЕХ использует символы { и } для группирования и завершающие текст символы } будут соответствовать символам { , для которых пользователь забыл написать парные им символы завершения группирования во внешнем входном файле. Следуя приведенным выше правилам, ТЕХ получает внутренний входной файл, не содержащий символов возврата каретки, перевода строки, знаков 7. , перевода страниц, а также сдвоенных пробелов. Управление пробелами в выходном документе осуществляется ТЕХ’ом с помощью других средств (на самом деле ТЕХ во внутреннем представлении документа заменяет литеры { и } на символы с кодами 44 и 45, но это никак не сказывается на работе пользователя, так что везде далее в этом отчете я буду по-прежнему пользоваться литерами { и }).
Теперь перейдем к высокоуровневому описанию и посмотрим, каким образом пользователь сообщает ТЕХ’у о своих требованиях относительно сложных объектов. Приводимый ниже пример довольно велик и его лучше бегло просмотреть, не изучая детально. Он задуман в качестве тестовой задачи для начальной отработки создаваемой системы. Этот пример основан на материале начальных страниц моей книги "Seminumerical Algorithms", где опущены фрагменты, не представляющие особого интереса с издательской точки зрения и с точки зрения требований, предъявляемых к реализуемой системе. Изучение этого примера принесет читателю наибольшую пользу, если он будет иметь под рукой оригинал упомянутой книги, чтобы можно было сравнивать входной ТЕХ’овский текст с требуемым результатом его обработки. Номера, проставленные в начале каждой из строк, не являются элементами входного ТЕХ’овского текста, они включены только для справочных целей, чтобы можно было ссылаться адресно на тот или иной фрагмент данного текста.
7,Example ТЕХ input related to Seminumerical Algorithms using ACPdefs
ACPpages starting at page 1{
titlepage 7,This tells the page format routine not to put page number at top
sectionnumber 3
runninglefthead{RANDOM NUMBERS}
hexpand 11 pt {fnt cmgll CHAPTER hskip 10 pt THREE}
vskip 1 cm plus 30 pt minus 10 pt
rjustline {fnt cmgb20 RANDOM NUMBERS}
vskip .5 cm plus 1 pc minus 5 pt
quoteformat {Anyone who considers arithmetical \cr_
methods of producing random digits \cr is, of course,
in a state of sin. \cr} author {JOHN VON NEUMANN (1951)}
quoteformat {Round numbers are always false.}
author{SAMUEL JOHNSON (c. 1750)}
vskip 1 cm plus 1 pc minus 5 pt
sectionnumber 3.1
sectionbegin {3.1. INTRODUCTION}
runningrighthead {INTRODUCTION}
Numbers which are ((chosen at random” are useful in a wide variety of
applications. For example:
7.This blank line specifies end of paragraph
yskip7.This means an extra space between paragraphs
textindent{a)}{\sl Simulation.}xskip When a computer is used to simulate natural
phenomena, random numbers are required to make things realistic. Simulation
covers many fields, from the study of nuclear physics (where particles are
subject to random collisions) to systems engineering (where people come into,
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
say, a bank at random intervals). \par
yskip textindent{b)}{\sl Sampling.} xskip It is often impractical to examine all cases, but a random sample will provide insight into what constitutes ((typical11 behavior.
yskip It is not easy to invent a foolproof random-number generator. This fact was convincingly impressed upon the author several years ago, when he attempted to create a fantastically good random-number generator using the following peculiar method:
yskip yskip noindent {\bf Algorithm K} xskip({\sl “Super-random” number generator}), xskip Given a 10-digit decimal number $X$, this algorithm may be used to change $X$ to the number which should come next in a supposedly random sequence. \par
algstep KI.[Choose number of iterations.]Set $Y«-lfloor X/10 sup 9 rfloor$, i.e., the most significant digit of $X$. (We will execute steps K2 through K13 $Y+1$ times; that is, we will randomize the digits a {\sl random} number of times.)
algstep K10. [99999 modify.] If $X<10 sup 5$, set $X«-X sup 2 + 99999$;
otherwise set $X <- X - 99999$. xskip blackslug
boxinsert{ctrline{fnt cmgblO Table 10}
ctrline{fnt cm9 A COLOSSAL COINCIDENCE: THE NUMBER 6065038420}
ctrline{fnt cm9 IS TRANSFORMED INTO ITSELF BY ALGORITHM K.}
blankline 3 pt vskip ruled
hjust to 12 pc{blankline vskip 6 pt
tabalign{#®quad#®quad$#$\cr
Step®ctr{$X$ (after)}\cr
vskip 10 pt plus 10 pt minus 5 pt
Kl®6065038420\cr K12®1905867781®Y=5\cr
vskip 10 pt plus 10 pt minus 5 pt blankline}
hskip 3 pc ruled align top
hjust to 12 pc{blankline vskip 6 pt
tabalign{#®quad #®quad $#$\cr
Step®ctr{$X$ (after)}\cr
vskip 10 pt plus 10 pt minus 5 pt
K10®1620063735\cr Kll®1620063735\cr K12®6065038420®Y=0\cr
vskip 10 pt plus 10 pt minus 5 pt blankline}
vskip ruled blankline} 7,end of the boxinsert
yskip yskip The moral to this story is that {\sl random numbers should not be generated with a method chosen at random}. Some theory should be used.
exbegin
exno tr 1. [20] Suppose that you wish to obtain a decimal digit at random, not using a computer. Shifting to exercise 16, let $f(x,y)$ be a function such that if $0<x,y<m$, then $0<f(x,y)<m$. The sequence is constructed by selecting
$X sub 0$ and $X sub 1$ arbitrarily, and then letting$$
X sub{n+l} = f(X sub n, X sub {n-1}) quad for quad n>0.$$
What is the maximum period conceivably attainable in this case?
exno 17. [10] Generalize the situation in the previous exercise so that $X sub {n+l}$ depends on the preceding $k$ values of the sequence. \ff
8080sectionnumber 3.2 sectionbegin{3.2. GENERATING UNIFORM RANDOM NUMBERS}
80
80
runningrighthead {GENERATING UNIFORM RANDOM NUMBERS}
In this section we shall consider methods for generating a sequence of random
fractions, i.e., random {\sl real numbers )U sub n), uniformly distributed
between zero and one}. Since a computer can represent a real number with only
finite accuracy, we shall actually be generating integers )X sub n) between
zero and some number )m); the fraction))U sub n=X sub n/m eqno 1)) will
then lie between zero and one.
88
89 vskip .4 in plus .2 in minus .2 in
90 sectionnumber 3.2.1 sectionbegin{3.2.1. The Linear Congruential Method}
runningrighthead{THE LINEAR CONGRUENTIAL METHOD}
By far the most successful random number generators known today are special
cases of the following scheme, introduced by D. H. Lehmer in 1948. [See
94 {\sl Annals Harvard Comp. Lab.} {\bf 26} (1951), 141-146.] We choose four
((magic numbers”:))
tabalign{rjust#®quad mathoff # quad®rjust #®ljust #\cr
X sub 0,®the starting value;®X sub 0®>0. \cr
m,®the modulus;®m®>X sub 0, quad m>a, quad m>c. \cr} eqno 1))
The desired sequence of random numbers )langle X sub n rangle) is then obtained
100 by setting ))X sub{n+l}=(aX sub n+c)mod m,quad n>0.eqno 2))This is called
101 a {\sl linear congruential sequence}.
102
103 Let )w) be the computer’s word size. The following program computes )(aX+c)
104 mod(w+l)) efficiently:))tabalign{\it#quad®hjust to 50 pt{\tt#}\cr
105 01®LDAN®X\cr
106 02®MUL®A\cr
107 05®JANN®*+3\cr
108 07®ADD®=W-l= quad blackslug\cr} eqno 2))
109 {\sl Proof.} xskip We have )x=l+qp sup e) for some integer )q) which is not a
110 multiple of )p). By the binomial formula))
111 eqalign{x sup p®=l+{p choose l}qp sup e + cdots +{p choose p-l}q sup
112 {p-l}p sup{(p-l)e}+q sup p p sup {pe}\cr
113 ®=l+qp sup{e+l} big(){l + 1 over p {p choose 2} q p sup e + 1 over p
114 {p choose 3} q sup 2 p sup {2e} + cdots + 1 over p {p choose p} q sup {p-1}
115 p sup {(p-l)e}.\cr)) By repeated application of Lemma P, we find that))
116 eqalign{(a sup p sup g - l)/(a-l)®equiv 0f(modulo p sup g), \cr
117 (a sup p sup g - l)/(a-l)®inequiv 0f(modulo p sup {g+1}). \cr}eqno 6))
118 If )Kk<p), )p choose k) is divisible by )p). {biglpren}{\sl Note:} xskip A
119 generalization of this result appears in exercise 3.2.2-ll(a).{bigrpren} By
120 Euler’s theorem (exercise 1.2.4-48), )a sup{varphi(p sup{e-f})} equiv Г
121 (modulo p sup{e-f}); hence )lambda) is a divisor of))
122 lambda(p sub 1 sup e sub 1 Idots p sub t sup e sub t)=lcm paren{lambda(p sub 1
123 sup e sub 1), Idots, lambda(p sub t sup e sub t)}. eqno 9))
124
125 This algorithm in MIX is simply))
126 tabalign{hjust to 50 pt{\tt#}®{\tt#}} quad® \cr
127 J6NN®*+2®underline{\it Al. j<0?}\cr
128 STA®Z®quad quad )->Z).\cr} eqno 7))
129 That was on page 26. If we skip to page 49, )Y sub 1 + cdots + Y sub k) will
130 equal )n) with probability))
131 sum over{y sub 1 + cdots + у sub к = n}atop{y sub 1, Idots, у sub к > 0}
132 prod over {l<s<k}
133 {e sup{-np sub s}(np sub s)sup у sub s}over у sub s!
134 ={e sup -n n sup n}over n!.))
135 This is not hard to express in terms of $n$-dimensional integrals$$ 136 textsize{int from alpha sub n to n dY sub n int from alpha sub{n-l} to 137 Y sub n dY sub{n-l} Idots int from alpha sub 1 to Y sub 2 dY sub 1} over 138 textsize{int from 0 to n dY sub n int from 0 to Y sub n dY sub{n-l} Idots 139 int from 0 to Y sub 2 dY sub 1}, quad {\rm where} quad alpha sub j = 140 max(j-t,0). eqno 24$$
141 This together with (25) implies that$$ def rtn{sqrt n} 142 lim over {n-»inf} s over rtn
sum over{rtn s<k<n}{n choose k} big(){k over n - s over rtn} sup к 144 big(){s over rtn + 1 - к over n} sup{n-k-l} = e sup {-2s} sup 2, quad s>0, 145 eqno 27$$ a limiting relationship which appears to be quite difficult to 146 prove directly.
147
148 exbegin exno 17. [HM26] Let $t$ be a fixed real number. For $0<k<n$, let$$ 149 P sub nk(x)=int from{n-t}to x dx sub n int from{n-l-t} to x sub n dx sub 150 {n-l}ldots int from {k+l-t} to x sub{k+2} dx sub{k+l} int from 0 to 151 x sub {k+1} dx sub к Idots int from 0 to x sub 2 dx sub 1;$$ 152 Eq. (24) is equal to$$def sumk{sum over{l<k<n}}
sumk X prime sub к Y prime sub к big/{sqrt{sumk X prime sub к sup 2}
sqrt{sumk Y prime sub к sup 2}}.$$
155 sectionnumber 3.3.3.3 subsectionbegin{3.3.3.3. This subsection doesn’t exist} 156 runningrighthead{A BIG MATRIX DISPLAY} Finally, look at page 91.$$ 157 def diagdots{raise . by 10 pt hskip 4 pt raise . by 5 pt hskip 4 pt .} 158 eqalign{U®=big(){tabalign{ctr#®ctr#®ctr#®ctr#®ctr#®ctr#®ctr#\cr 159 l\cr®diagdots\cr®®l\cr
c sub l®ldots®c sub{k-l}®l®c sub{k+l}®c sub n\cr
®®®®l\cr®®®®®diagdots\cr®®®®®®l\cr}},\cr
U sup{-l}®=big(){tabalign{ctr#®ctr#®ctr#®ctr#®ctr#®ctr#®ctr#\cr
l\cr®diagdots\cr®®l\cr
-c sub l®ldots®-c sub{k-l}®l®-c sub{k+l}®-c sub n\cr
®®®®l\cr®®®®®diagdots\cr®®®®®®l\cr}}$$
This ends the test data, fortunately TEX is working fine.}
Первое, что надо сказать относительно приведенной выше задачи, — она описана с использованием средств не базового языка, а некоторого расширения ТЕХ’а. Например, "ACPpages" во второй строке представляет собой имя специальной программы, которая вызывает основную ТЕХ’овскую программу формирования страниц, но использует ее для подготовки страниц в формате книги "The Art of Computer Programming". Слова titlepage, sectionnumber, runninglefthead, quoteformat, author, sectionbegin, runningrighthead, xskip, yskip, textindent, algstep, exbegin, exno и subsectionbegin являются специфическими для моих книг и должны быть определены в терминах низкоуровневых ТЕХ’овских примитивов способом, поясняемым ниже. Далее, большинство шрифтов встроены в эти определения; например, в определении sectionbegin указывается, что текст раздела по умолчанию набирается шрифтом кегля 10 pt, в то время как определение exbegin для набора текста упражнений задает шрифт кегля 9 pt. Ключевые слова типа приведенных выше выбирались таким образом, чтобы они не совпадали в точности с английскими словами, поскольку они будут перемежаться со словами набираемого текста. Например, еще одно определение задает, что слово MIX всегда должно набираться шрифтом типа "шрифт пишущей машинки"; скрытое определение def MIX|«\tt \{MIX}}}
обеспечит автоматическое выполнение этого требования при обработке строки 125. Механизм макроопределений ТЕХ’а мы рассмотрим позднее; три простых примера имеются в строках с номерами 141, 152 и 157 нашей тестовой программы, где для
экономии места вводятся сокращенные обозначения для некоторых фрагментов ТЕХ^овского текста. Так, конструкция \{string} используется для передачи строки без интерпретации входящих в нее слов согласно существующему набору определений; это средство используется в приведенном выше определении ключевого слова MIX. Для любознательных читателей, кому было бы интересно увидеть более сложную конструкцию, даем определение ключевого слова quoteformat:
def quoteformat #1 author #2 {lineskip 3 pt plus .5 pt minus 1 pt vskip 6 pt plus 5 pt minus 2 pt def \rm {fnt cmg8} def \sl {fnt cmgi8} {\sl tabalign{rjust##\cr #1}} vskip 6 pt plus 5 pt minus 2 pt \rm rjustline #2 vskip 11 pt plus 5 pt minus 2 pt}
Пожалуйста, не пытайтесь понять это сообщение сейчас — ТЕХ на самом деле весьма прост, поверьте мне. Слово <f author^, встречающееся в определении quoteformat, имеет специальную интерпретацию только в пределах данного определения, в других же случаях оно трактуется как обычно. Именно поэтому я не стал вводить специальное неанглийское ключевое слово вроде f'quoteauthor'' в строках с номерами 12 и 14 изучаемого нами примера.
Спецификации ключевых слов sectionnumber и runninglefthead в строках 4 и 5 предназначены для использования во время формирования самых верхних строк при верстке страниц книги АСР. Строка 6 содержит первый фрагмент текста, который появится на создаваемой странице, однако обратите внимание сначала на строку с номером 8, которая попроще. Строка 8 предписывает использовать шрифт cmgb20 (что означает "Computer Modern Gothic Bold 20 point", т.е. полужирный шрифт семейства Computer Modern, кегля 20 pt, который я предполагаю разработать в скором времени) для набора слов RANDOM NUMBERS (СЛУЧАЙНЫЕ ЧИСЛА), а также выровнять эти слова по правому краю строки (rjustline). Строка 6 аналогична строке 8, но задает использование шрифта cmgll кегля И pt; текст “hskip 10 pt” требует сформировать горизонтальный пробел размером 10 pt, а текст “hexpand И pt” обозначает, что надо взять строку и растянуть ее на И pt шире, чем это должно иметь место в норме. Отметим, что шрифтовые определения, задаваемые внутри фигурных скобок {...}, теряют силу вне этих скобок. Это же правило имеет место и для других определений.
Теперь пришло время объяснить ТЕХ^овский механизм растяжения и сжатия последовательностей боксов. Размеры в ТЕХ^е могут задаваться в типографских пунктах, пиках, дюймах, сантиметрах или миллиметрах, что обозначается соответственно аббревиатурами pt, pc, in, cm, mm. Эти обозначения имеют смысл только в таких контекстах, где используются физические длины, и одно из таких обозначений в такого рода контексте должно присутствовать обязательно, даже если фактическое значение соответствующей величины равно нулю (один дюйм равен 6 пикам или 72 пунктам, или 2.54 сантиметрам, или 25.4 миллиметрам). Клей между боксами имеет три составляющих: фиксированная составляющая, х добавляемая (plus) составляющая, у убавляемая (minus) составляющая, z
Составляющая х представляет собой "нормальное" значение величины пробела. В случаях, когда надо бывает растянуть последовательность боксов, как это имеет место при обработке строки 6, каждая х-составляющая в клее увеличивается путем добавления нескольких у-составляющих. Если же строку надо сжать, тогда каждая х-составляющая уменьшается путем вычитания нескольких z-составляющих.
Например, пусть даны четыре бокса, соединенных друг с другом клеем со спецификацией вида
(xl,yl,zl), (x2,y2,z2), (x3,y3,z3), тогда расширение строки на величину и достигается использованием интервалов xl + у1 и’, х2 + у2 в*, хЗ + уЗ и’, где w’ = w/(yl+y2+y3). Такого рода расширение невозможно, если я>0 и у1+у2+уЗ=0. Создаваемая система будет настойчиво пытаться уменьшить число растяжении строк, которые будут плохо смотреться, посредством довольно интеллектуальной программы выравнивания строк (описывается ниже). При сжатии строки максимальное значение величины ее сокращения равняется сумме всех z-составляющих, не будет предприниматься попыток ужать строку еще больше. Таким образом, величину z-с оставляющих надо задавать так, чтобы x-z представляло собой минимальное значение диапазона допустимых значений пробела. Для у-составляющей подходящим будет такое значение, что х+у было бы допустимым, но х+Зу лежало бы на пределе допустимости. Значения параметров у и z должны быть неотрицательными, однако х может быть и отрицательным (для стирания пробелов (backspacing) и перекрытия фрагментов строк (overlapping)), если соблюдать при этом определенную осторожность.
Обозначение “hskip 10 pt“ в строке 6 означает, что х = 10 pt, у = 0 и z = 0 в горизонтальном клее между словами CHAPTER THREE. Если бы эта команда hskip здесь не присутствовала, использовались бы обычные соглашения для данной ситуации. А именно, клей между CHAPTER и THREE включал бы тогда х-составляющую, равную обычному междусловному интервалу для данного шрифта, а также у = х, z = х/2. Для клея между буквами будет (например) х = 0, у = 1/10 обычного междусловного интервала для данного шрифта, z = 0; таким образом, растяжение строки без использования hskip приведет главным образом к увеличению промежутка между словами CHAPTER и THREE, использование же данной команды в виде, показанном выше, обеспечит введение дополнительных промежутков еще и между отдельными буквами. Шрифты, предназначенные для использования их ТЕХ'ом, должны обладать такого рода средствами управления промежутками, понимаемыми как некоторые дополнительные свойства соответствующего шрифта; ТЕХ должен также уметь обнаруживать ситуации типа конца предложений, слегка увеличивая величину пробелов после знаков препинания там, где это необходимо. Символы вроде + и = принято окружать пробелами, тогда как пары символов и : = должны стоять вплотную друг к другу; символы + и = не выделяются пробелами в случае, когда они появляются в индексах. Аналогичным образом должны обрабатываться ситуации с лигатурами. Наличие в шрифтах, используемых ТЕХ'ом, такого рода средств делает эти шрифты несколько более ’’интеллектуальными”; более подробно данный подход будет рассмотрен ниже.
Большинство ТЕХ^овских средств симметрично по отношению к горизонтальному и вертикальному режимам, хотя основная идея, заключающаяся в формировании сначала строк, а уже потом столбцов, асимметрична по своей природе и встроена в стандартные программы (в соответствии с тем способом, который мы обычно применяем при письме). Спецификация “vskip“ в строке 7 задает вертикальный клей аналогично горизонтальному клею. Когда программа выравнивания страниц пытается заполнить первую страницу при обработке рассматриваемого нами тестового примера (путем введения разрыва страниц точно в конце абзаца, или в таком месте текста, чтобы на каждой из двух смежных страниц были размещены хотя бы по две строки из разрываемого между страницами абзаца), этот клей будет растягиваться или сжиматься вдоль вертикального измерения, используя значения х = 1 см, у = 30 pt, z = 10 pt. Дополнительные характеристики клея вводятся в определении для ключевого слова quoteformat -- это параметр lineskip, задающий величину междустрочного интервала, а также параметр vskips,
который задает значение специального дополнительного интервала между цитатой и строкой с именем ее автора. Применительно к основному тексту инструкция для принтера имеет вид "10 на 12", что означает требование пользоваться для печати шрифтом с кеглем 10 pt при значении интервала между соответствующими частями смежных строк, равном 12 pt. ТЕХ будет использовать шрифт кегля 10 pt со спецификацией
lineskip 2 pt plus .5 pt minus .25 pt
так что междустрочный интервал будет обладать определенной эластичностью. Дополнительное управление величиной междустрочного интервала задается спецификацией
parskip 0 pt plus 1 pt minus 0 pt
Кроме того, существуют также интервалы между упражнениями, шаги в алгоритмах и т.д. Определение
def уskip {vskip 3 pt plus 2 pt minus 2 pt} используется для формирования специального вертикального интервала, например, в строках 22 и 37. В строках 23, 37 и т.д. используется горизонтальный интервал вида
def xskip {hskip 6 pt plus 10 pt minus 3.5 pt}
который вводится в ситуации, когда психологически оправданно наличие интервала большего, чем это определено в качестве нормы. Для этих ситуаций эластичный клей особенно полезен. Пример использования большего по размеру горизонтального отступа, называемого “quad”, содержится в строке 74; он задает интервал величины "два еш", где еш — единица измерения, часто применяемая при типографском наборе математических текстов.
Другая привлекательная черта эластичного клея выявляется при рассмотрении примера def hfill {hskip 0 cm plus 1000 cm minus 0 cm}.
Можно сказать, что у здесь имеет практически бесконечное значение (длина, равная 10 м). Когда этот символ появляется в качестве некоторого hskip в начале строки, он обеспечивает выравнивание данной строки по правому краю; когда он имеется как в начале строки, так и в ее конце, он обеспечивает центрирование строки; в случае, когда он находится в середине строки, он аккуратно делит строку на части и т.д. Эти свойства эластичного клея обеспечивают средствам ТЕХ^а выравнивания строк превосходство над средствами других систем, поскольку новые свойства вводятся достаточно простым способом и в то же самое время уменьшают необходимость использования устаревших средств применительно к общему случаю.
После того как произведено выравнивание списка боксов, все значения параметров клея фиксируются и полученный объединенный бокс становится неизменяемым ("затвердевает"). Выравнивание осуществляется как по горизонтали, так и по вертикали. Горизонтальное выравнивание длинных строк включает автоматическое выполнение операций переноса; дополнительные сведения относительно операций выравнивания будут приведены в данном отчете несколько позднее.
Снова обратимся к примеру. Строки с номерами 21, 31, 36 и т.д. в нем
— это пустые строки, обозначающие конец абзаца. Другой способ показать конец абзаца состоит в использовании команды “Храг”; пример такого использования можно видеть в строке 27. Строка 124 задает конец абзаца, завершающегося выключной формулой. Абзацы начинаются обычным образом; одна из ТЕХ^овских команд, которая будет исполняться после того как обработана строка 17, содержащая "sectionbegin" ("начало раздела"), это команда parindent 20 pt
задающая абзацный отступ размера 20 pt в первой строке каждого абзаца до тех пор, пока не появится команда "noindent" ("подавить абзацный отступ"), как это имеет место, например, в строке 37. Программа "sectionbegin" также задает
"noindent“ для самого первого абзаца данного раздела, поскольку в моих книгах такой стиль является стандартным. В строке 23 указывается ”textindent{a)}", что обозначает необходимость создать бокс ширины parindent, содержащий пару символов а) , за которыми следует пробел, причем эта тройка символов выравнивается по правой границе данного бокса.
В строке 23 "\sl" обозначает "наклонный шрифт". Я решил для выделения слов в тексте пока заменить курсив наклонным вариантом обычного "прямого" шрифта, тогда как имена переменных в математических формулах будут, как это принято, набираться курсивом. Надо будет еще поэкспериментировать с этим, однако чутье подсказывает мне, что набор так будет выглядеть намного привлекательнее, поскольку буквы из типичных курсивных шрифтов смотрятся не очень красиво среди текста, набранного обычным шрифтом. В любом случае я буду делать различие между наклонными и курсивными шрифтами, поскольку возможны ситуации, когда целесообразно использовать два различных шрифта для выделений, отличающихся по стилю (по каким-либо причинам) друг от друга. Обозначение "\bf" в строке 37 задает набор полужирным шрифтом. Все перечисленные шрифты определяются в команде начала раздела (sectionbegin); определения эти здесь не показаны.
Все математические формулы во входном ТЕХ'овском файле содержатся либо между парой символов $...$, как это имеет место, например, в строках 38 и 41, либо между парами $$...$$ (так записываются выключные формулы). Формулы записываются с использованием специального синтаксиса, основывающегося на предложениях Кернигана и Черри (Kernighan and Cherry, Comm. ACM 18, March 1975, 151-157). Например, запись “sup 9” в строке 41 обозначает верхний индекс 9, а запись ”sub{n+l}" в строке 74 — нижний индекс п+1. Ключевые слова наподобие sup и sub активны только в пределах, охватываемых парами $...$ (или $$...$$); то же самое относится к именам греческих букв, вроде lambda (строка 122) и varphi ("variant phi", "скругленный" вариант буквы phi, появляющейся в верхнем индексе в строке 120), а также к словам типа lim (строка 142), max (строка 140), 1cm (строка 122). Все буквы в формулах набираются курсивным шрифтом, за исключением тех случаев, когда они относятся к одному из распознаваемых слов или же лежат в пределах фигурных скобок в командах "mathoff{...}" или ”{\rm ...}".\
Расстановка всех пробелов в формулах выполняется ТЕХ’ом. Для случаев, когда с точки зрения автора ТЕХ справился с решением этой задачи не совсем удачно, имеется возможность указать дополнительный пробел с помощью символа ( (см., например, строку 117). В строке 117 дополнительный пробел перед "(modulo...)" желателен в силу того, что обычно пробел перед скобками опускается, но здесь это будет смотреться неудачно. В завершающих фрагментах рассматриваемого примера много сложных формул, которые будут выглядеть так, как это имеет место в соответствующих местах книги АСР2, откуда взят данный пример. Обработка текста "eqno 24" (строка 140) приведет к тому, что будет сформирован номер формулы и проставлен в виде "(24)" на правом поле страницы, причем он будет отцентрирован по вертикали по высоте формулы. Так будет сделано, если справа от формулы достаточно места для размещения там номера, если же этого места недостаточно, номер "(24)" будет набран на следующей строке и выключен вправо.
Ключевое слово "algstep", используемое в строках 41 и 45, обозначает формат записи шага алгоритма:
def algstep #1. [#2] {vskip 3 pt plus 1 pt minus 1 pt noindent rjust in 20 pt{#l.} [#2] xskip hangindent 20 pt} Сначала в этом определении устанавливается значение клея для вертикальных интервалов; эта операция выполняется еще до того, как начинается текст шага алгоритма. Устанавливается также подходящее значение для текстового
отступа (textindent) и т.д. В первый раз здесь появляется такое средство, как отрицательный абзацный отступ (hanging indent). Использование этого средства оставляет первую строку абзаца несмещенной (без абзацного отступа), а все остальные строки данного абзаца смещаются вправо на величину абзацного отступа.
Ключевое слово ”ехпо“ (упражнение с номером ... ), используемое в строках 70, 77 и т.д., имеет определение в чем-то похожее на определение шага алгоритма; такие определения форматов для элементов, часто используемых в моих книгах, помогают обеспечить единообразие стиля представления этих элементов на протяжении всей книги. Элемент ”tr” в строке 70 позволяет изобразить треугольник на левом поле страницы. Чтобы этот треугольник был размещен левее крайней левой позиции строки, содержащей номер упражнения, в определении для ”tr” используется клей с отрицательным значением.
В строке 48 начинается выполнение операции вставки бокса (boxinsert) — одного из важных элементов, необходимых при верстке полосы набора. Рассматриваемый бокс, определенный внутри пары фигурных скобок {...}, идущих вслед за ключевым словом ’’boxinsert”, формируется как отдельный элемент, который должен быть размещен в верхней части либо текущей страницы, либо следующей за ней. За этим боксом следует вертикальный клей, задаваемый выражением boxskip 20 pt plus 10 pt minus 5 pt, Это еще один из элементов, подключаемых при обнаружении ТЕХ’ом ключевого слова "sectionbegin" в тексте обрабатываемого документа. Это средство используется для размещения рисунков и таблиц, в нашем случае — для таблицы.
Заголовок данной таблицы описывается в строках 48-50, сама же таблица (см. с.6 книги АСР2), достаточно сложна, поэтому отложим пока обсуждение содержимого строк 52-65 и рассмотрим сначала более простой пример формирования таблицы (tabalign), содержащейся в строках 96-98.
В общем случае команда tabalign задает форму вида tabalign{ ul#vl ® ... ® un#vn \сг xll ® ... ® xln \сг
xml ® ... ® xmn \сг}
Символ ® соответствует <ТАВ> на клавиатуре и ее коду во входном файле.
Здесь эти символы показаны в явном виде, как если бы они были отображаемым символом, чтобы упростить изложение. В этом примере ”\сг” обозначает не carriage-return (возврат каретки), а представляет собой последовательность из трех символов \, с, г. Тем самым задается формирование mn боксов ul{xll}vl ... un{xln}vn
ul{xml}vl ... un{xmn}vn
и для каждого к определение максимальной ширины бокса uk{xik}vk для
i = l,...,m. Затем все боксы uk{xik}vk выравниваются по ширине максимального (самого широкого) бокса, после чего каждая из строк xil®...®xin\cr заменяется горизонтально сцепленной последовательностью модифицированных (выровненных по ширине) боксов; эти строки разделяются междустрочным интервалом tabskip 0 pt plus 1 pt minus 0 pt.
Если в какой-либо из строк таблицы содержится менее п элементов, недостающие элементы ("клетки’’ таблицы) оставляются пустыми.
В примере использования команды tabalign, содержащемся в строках 96-98, число п столбцов задано как п=4. Первый из столбцов должен быть выровнен по правому краю, второй трактуется как "mathoff" (нематематические символы внутри формулы) и окружается пробелами ширины в quad, третий опять выравнивается по правому
краю, четвертый -- выравнивается по левому краю. Полученный при выполнении этой команды результат можно видеть на с.9 книги АСР2; сформированная таблица трактуется здесь как формула (1) и номер ее соответственно центрируется по высоте формулы.
Примечание: Со временем я введу в ТЕХ команду "omittab", позволяющую формировать при необходимости "клетки" таблицы, шириной в несколько столбцов.
Вернемся теперь к строкам 52-65 и сравним их с таблицей 1 на с.6 книги АСР2. С помощью команды tabalign формируется бокс ширины 12 рс, размещаемый рядом с другим таким же боксом. Слова "ruled", модифицирующие выражения для вертикального (vskip) и горизонтального (hskip) промежутков, обозначают, что клей между боксами появляется в сочетании с горизонтальной или вертикальной линейкой по его центру.
Средство "eqalign" (строки 111, 116) используется для того, чтобы выравнивать выражения, содержащиеся в выключных формулах, расположенных в нескольких строках. На самом деле эта команда представляет собой частный случай команды tabalign:
def eqalign #1 {tabalign{rjust##®ljust##\cr #1}}.
Обратите внимание — строка ИЗ начинается символом табуляции <ТАВ>.
Команды формирования "больших" математических знаков и скобок (“big(){...}’’ в строках ИЗ, 143 и т.д.; “big/C..}” в строке 152) используются для того, чтобы сформировать в "высоких" формулах круглые скобки (Ч.. .У’ и знаки типа "наклонная черта" “/..J* таких размеров, которые соответствовали бы размерам формулы по вертикали; размеры упомянутых элементов определяются высотой соответствующих боксов (например, бокса, заключенного в скобки). Операции такого рода доступны также для квадратных и угловых скобок ([] и О), а также для пары знаков "вертикальная черта" (II), при этом левая и правая квадратные скобки увеличиваются командами \[ и \] до размера, который ТЕХ сочтет наиболее подходящим в данной конкретной ситуации. Скобки перечисленных выше видов (включая пару вертикальных линий) могут быть сделаны произвольно большими по размеру, однако на знак "наклонная черта" это не распространяется, по крайней мере в текущей реализуемой версии ТЕХ’а. Пример, в котором будут сформированы очень большие круглые скобки при построении большой матрицы, содержится в строках 158-165.
Команды формирования больших круглых скобок f^biglpren’1 и (‘bigrpren’1 в строках 118-119 на самом деле дадут не такой уж и "большой" результат — они просто формируют круглые скобки размера 12 pt вместо положенного здесь по умолчанию размера 10 pt, использующиеся для выделения текстов вроде "(а)", заключенных в круглые скобки.
Знаки суммы, формируемые командой “sum over...” в строках 131, 143, будут большого размера, поскольку здесь они входят в состав выключных формул, однако, когда формула набирается как часть текстовой строки (т.е. она ограничивается парой символов $...$), величина знака суммы в ней будет меньшей, а пределы суммирования будут набраны как индексы у знака суммы. Аналогично этому будет изменяться и величина знака интеграла, а также дробей “...over...” и биномиальных коэффициентов (см., например, "$р choose k$" в строке 118). Ключевое слово “textsize” в строках 136 и 138 указывает, что в данной формуле следует использовать маленькие знаки интегралов, несмотря на то, что она выключная.
Команда \ff в строке 79 означает переход к новой странице (текст завершаемой таким образом страницы будет выключен по вертикали вверх). Здесь опять мы имеем дело с одним из элементов стиля оформления моих книг — основные разделы всегда должны начинаться с новой страницы.
Я полагаю, что приведенные выше замечания по примеру набора текста позволяют уловить дух ТЕХ’овского подхода. Этот пример подбирался так, чтобы показать применение разнообразных средств при наборе достаточно сложного текста. В общем же случае набор большинства книг будет представлять собой существенно более рутинное занятие, где только время от времени возникнет потребность в чем-то необычной формуле или таблице. В рассматриваемом примере акцент делался именно на такого рода "необычных" элементах.
Следующий шаг в понимании работы ТЕХ’а состоит в том, чтобы вернуться к низкоуровневому описанию процессов в нем и посмотреть, что происходит с внутренним входным файлом при его чтении. Оказывается, что данный файл просматривается и преобразуется в последовательность лексем, представляющих собой отдельные литеры или последовательности литер (слова). В общем случае, смежные буквы и цифры группируются, образуя слова, при этом точки и запятые, за которыми сразу следуют цифры, трактуются как буквы; то же относится и к знакам \ , перед которыми нет буквы или цифры.
Точнее, процесс формирования лексем определяется следующим конечным автоматом. Состояние 0. Пусть х — следующая литера во внутреннем входном файле.
Если х есть \ или одна из 52 букв или 10 цифр, задать и«-х и перейти к Состоянию 1.
Если х есть точка или запятая, задать у«-х, w<-null, и перейти к Состоянию 2.
В противном случае вывести х как следующую лексему.
Состояние 1. Пусть х — следующая литера во внутреннем входном файле. Если х есть буква или цифра, задать w«-wftx (за и следует х).
Если х есть точка или запятая, задать у«-х и перейти к Состоянию 2. Если х есть \ , то вывести и, задать и«-х.
В противном случае вывести у как очередную лексему, затем вывести х как следующую лексему, после чего перейти к Состоянию 0.
Состояние 2. Пусть х — следующая литера во внутреннем входном файле. Если х есть цифра, задать v<-v&y&x, перейти к Состоянию 1.
В противном случае, если и не пусто, вывести и как следующую лексему, после чего перейти к Состоянию 0, но без переустановки х на начало набора инструкций для этого состояния.
Например, последовательность литер вида
Abc\de 5,000 plus-or-minus ff2.5 \per cent,, аге ... here, будет преобразована в последовательность из 28 лексем:
Abe\de<space>5,000<space>plus-or-
minus<space>ff2.5\per<space>cent11
<space>are<space>...<space>here.
ТЕХ работает с последовательностями лексем. Если вводится определение для некоторой лексемы (с использованием команды "def"), пробелы (<space>) перед этой лексемой удаляются. Оставшаяся часть данного определения "встраивается" в комплекс уже действующих в ТЕХ’е определений. Аналогичный процесс повторяется для результирующей последовательности лексем. Определения заканчиваются фигурной скобкой }, символами $ или $$, завершающими группу, где содержится определение; они также недействительны между \{ и парной ей скобкой }. Если ключевое слово MIX определить как def MIX«\tt MIX}}
вместо
def MIX{{\tt\{MIX}}}, ТЕХ войдет в бесконечный цикл (зациклится), выдавая {\tt {\tt {\tt {\tt ....
Читатель теперь, наверное, понимает либо все, либо ничего относительно процесса определения; я, тем не менее, попытаюсь изложить правила этого процесса более подробно. В общем случае можно записать
def token stringO #1 stringl #2 string2 ... #k stringk {right-hand side} где "token" — любая лексема, за исключением { или }, и где stringO ... stringk есть строки из нуля или более лексем, не включая {, }, # или I; пробелы в таких строках игнорируются. Я описываю сейчас общий случай; приведенное выше определение для MIX — это простейший пример реализации общего подхода, когда к = 0, а строка stringO — пустая.
Когда обнаруживается определяемая лексема, запускается процесс сопоставления (matching), продолжающийся до тех пор, пока не будут найдены соответствия для всех элементов в левой части определения: лексемы в stringO...stringk должны точно соотноситься с соответствующими лексемами во входном тексте (с удаленными пробелами); если в процессе сопоставления обнаружится ошибка, будет выдано диагностическое сообщение. Символы # в рассматриваемом определении сопоставляются с лексемами или с группами {...}. Никакого развертывания групп {...} не выполняется, они трактуются просто как неинтерпретируемая последовательность лексем. В каждый момент времени имеется по крайней мере одно определение, активное для данной лексемы. По завершении процесса сопоставления, ТЕХ ищет те элементы ("ингредиенты"), которые должны быть подставлены вместо #1,...,#к в правой части определения. Найденные элементы просто копируются в позиции, занимаемые #1 ... #к в правой части определения. И еще одно изменение будет сделано: из любой последовательности, содержащей два или более символа #, идущих подряд, удаляется первый символ #. Это делается для того, чтобы можно было записывать определения в правой части без риска перепутать их с текущим определением.
Например, рассмотрим что произойдет, если активным будет определение вида: def А #1 В С {def Е ##!{##! #1 #1}}.
Если внутренний входной файл содержит А {Х-у} BCD то результирующая последовательность после раскрытия определения примет вид def Е #1{#1{Х-у}{Х-у}} D
(обратите внимание на расстановку пробелов).
Если непосредственно перед символом { появляется знак |, вводящий правую часть определения, то пробел, если таковой будет находиться вслед за последней лексемой, не будет удален. Таким образом, если приведенный выше пример переписать в виде def А #1 В C|{def Е ##!{##! #1 #1}} то будет получен результат такого вида def Е #l{#l{X-y}{X-y}}D.
Это средство было использовано в нашем определении для MIX на предыдущей странице, поскольку оно позволяет сохранить пробел, следующий за MIX, когда эта команда используется в середине предложения. С другой стороны, в большинстве определений (например, для xskip) это средство использовать нет необходимости.
Изложенное выше не представляет собой наиболее общего подхода к формированию макроопределений -- я здесь предпочитаю следовать принципу "бритвы Оккама". Средства много более общего характера доступны через аппарат ТЕХ^овских "программ", о которых речь пойдет ниже; этот аппарат рассчитан на достаточно подготовленных и опытных пользователей. Если теперь читатель вернется несколько назад и посмотрит на определения, сформированные для ключевых слов quoteformat, algstep и eqalign, то увидит, что все они и в самом деле просты.
Некоторые из лексем не определяются в качестве макро, но задают действия, которые следует выполнить. Например, "fnt" означает, что следующая лексема, отличная от пробела, представляет собой имя текущего используемого шрифта, a "def" значит, что ТЕХ должен ввести новое определение.
Можно сказать, что имеется четыре взаимно исключающих вида лексем: определяемые лексемы лексемы-действия { и } прочие лексемы.
Символы { и } указывают на группирование и различные уровни контекста; определения и действия-присваивания, выполненные внутри {...}, не оказывают никакого влияния вне этой пары скобок (группы).
Действия-присваивания влияют на характер ТЕХ^овских операций с входными данными. К их числу относятся:
fnt token hsize length vsize length hmargin length
изменить текущий шрифт
нормальная ширина формируемых строк текста нормальная высота формируемых строк текста расстояние от левого края бумажного листа до начала формируемых строк
vmargin length
расстояние от верхнего края бумажного листа до начала формируемой страницы
lineskip glue parskip glue
интервал между формируемыми строками
дополнительный интервал между абзацами (добавляется к lineskip)
dispskip glue boxskip glue noteskip glue tabskip glue parindent length
дополнительный интервал перед и после выключной формулы дополнительный интервал под вставляемым боксом дополнительный интервал над подстрочными примечаниями горизонтальный промежуток между табулируемыми столбцами отступ для первой строки абзаца
hangindent length отступ для всех строк абзаца, кроме первой (для
hangindent устанавливается нулевое значение после каждого абзаца).
Все эти числовые параметры имеют значения по умолчанию, которые будут выбраны, исходя из опыта использования ТЕХ'а; эти значения будут активными до тех пор, пока не будут в явном виде переопределены. В приведенном выше перечне “length”
("длина") имеет вид token unit
или- token unit
где “token” ("лексема") — строка цифр, содержащая или не содержащая точку (десятичную точку), a “unit” ("единица измерения") может принимать значения pt, pc, in, стили шт. Далее, “glue” ("клей") определяется выражением
length plus length minus length
где фрагменты plus и minus не могут быть отрицательными; любая из трех перечисленных частей-длин может быть опущена или быть равной нулю.
Например, в соответствии со стандартными соглашениями, принятыми для принтера XGP, вывод производится на страницу размера 8 1/2 на 11 дюймов с левыми и правыми полями по 1 дюйму, с междустрочным расстоянием величиной в 4 пикселя, все эти требования можно описать так:
hsize 6.5 in vsize 9 in hmargin 1 in vmargin 1 in lineskip .02 in
На самом верхнем уровне в неявной форме ТЕХ^овскому вводу будет предшествовать ввод строки вида "TEXpublish{", где TEXpublish -- программа, периодически вызывающая программу формирования страниц, которая, в свою очередь, периодически вызывает программу формирования абзацев, а она читает входные данные и выдает
отформатированные строки текста. Программа TEXpublish просто печатает каждую из страниц, которую она получает; в нашем текстовом примере используется более совершенная программа ACPpages.
В первой реализации ТЕХ^а программы, подобные ACPpages, будут написаны на языке SAIL и загружены с помощью ТЕХ’а. У меня была идея добавить к ТЕХ’у некую интерпретирующую систему и мини-язык программирования, чтобы подобного рода расширения можно было бы реализовывать достаточно просто и не затрагивать при этом ядра ТЕХ’овских программ. Однако по некотором размышлении мне показалось, что будет намного лучше считать, что ТЕХ’овский код (не только ядро) не надо трогать совсем, а расширения выполнять с помощью языка SAIL. Это даст возможность сэкономить много времени при реализации системы, а также повысить ее быстродействие.
Продолжим рассмотрение особенностей работы ТЕХ'а, составив набросок программы ACPpages на языке SAIL. Следует помнить, что это не базовая ТЕХ^овская программа, а код, имитирующий внутреннюю ТЕХ’овскую программу. В настоящий момент это только первое приближение к реальной SAIL-программе, поскольку мне еще надо получше обдумать вопрос о том, как будет выглядеть ТЕХ^овская "начинка", прежде чем можно будет говорить обо всем этом более конкретно.
if scan("starting") then
begin scanreqd("at"); scanreqd("page"); spage<-nextnonspacetoken; end else spage*-" 1";
{Здесь "scan" -- ТЕХ^овская программа, которая просматривает следующую лексему, не являющуюся пробелом, находящуюся во вводе (входном потоке лексем); если она совпадает с предписанной лексемой, программа scan выдает ответ true (истина) и отбрасывает данную лексему; в противном случае текущая входная лексема пропускается и выдается ответ false (ложь). Программа "scanreqd" во многом аналогична программе scan, но вместо ответа false она выдает сообщение об ошибке типа "Syntax error, I will insert missing #" ("Синтаксическая ошибка, я вставлю пропущенный символ #"). Когда фрагмент программы, приведенный выше, отработает, переменной spage будет присвоено предписанное значение-строка, задающая номер начальной страницы, если же такого предписания нет, будет использовано значение по умолчанию, равное "1".} if spage = "г" then
begin rnumeral*-true; lop(spage); end;
pageno*-int scan (spage, brchar);
{Римские цифры для нумерации страниц обозначаются как ri, г2 и т.д. Параметр pageno теперь имеет целочисленное значение, равное требуемому номеру первой формируемой страницы. Значение параметра rnumeral будет равно true тогда и только тогда, когда требуется, чтобы страницы нумеровались римскими цифрами.} scanreqd(leftbrace);
put_on_stack_something_to.make_matching_right_brace_end_the_input; while true do
begin cpage*-nextpage; if not cpage then done;
{Здесь cpage — указатель на некоторый бокс, или указатель на пустую запись, null-указатель, если процесс ввода прерван.}
if rnumeral then spage*-cvr от (pageno) else spage*-cvs(pageno);
if omithead then {this would be set by "titlepage" routine} begin omithead*-! alse;
output_cpage_with_blanks_for_top_headline_and_with_spage_in_ 9_point_type_centered_as_a_new_line_at_the_bottom;
end
else begin if pageno land 1 then {right-hand page} begin texta<-the„running_right_head; textb«-attr(sectno, cpage);
{texta и textb — указатели записи на последовательности лексем; "attr" получает атрибут (sectno — "номер раздела") параметра cpage для бокса, как объясняется ниже}
line<-pointer_to_box_vhich_TEX_would_make_from_the_input "{textb} hfill {texta} rjust to .375 in {spage}" end else begin texta<-the_running_left„head;
textb<-attr (sectno,first(cpage));
line<-pointer_to_box_which_TEX_would_make_from_the_input "Ijust to .375 in {spage} texta hfill textb" end;
place_line_above_cpage_with_14_pt_glue_and_output_the_result; end;
pageno<-pageno+l; end;
{Другими словами, для страниц с нечетными номерами берется значение атрибута sectionnumber из cpage; для них формируется колонтитул, состоящий из номера раздела, заглавия раздела, за которым идет номер страницы, выровненный по правому краю. У страниц с четными номерами в колонтитуле сначала идет номер страницы, выровненный по левому краю, затем текст колонтитула, затем номер раздела из первой компоненты, содержащейся в cpage. Следует отметить, что на левой странице будет указываться номер раздела, а на правой — номер текущего подраздела в нем.}
Приведенный выше пример показывает необходимость обсуждения еще одного базового ТЕХ^овского средства, а именно "атрибутов", которые могут быть приписаны боксам. Один из таких атрибутов — номер раздела, другой -- высота, еще один — ширина. Ряд атрибутов используется в математических формулах, они связаны с пробелами, доступными внутри соответствующего бокса, в частности, их можно использовать при формировании верхних и нижних индексов. Применительно к каждому из атрибутов имеется две программы, показывающие, как определять значение атрибута бокса, формируемого из двух боксов, размещаемых друг относительно друга по горизонтали или по вертикали. Например, имеющийся атрибут высоты заменяется значением, определяемым выражением max(hl,h2), если два бокса объединяются по горизонтали, или выражением hl+h2, если они объединяются по вертикали. Атрибут "номер-раздела" (section-number) примет значение si, если s2=null, в противном случае он будет равен s2, и т.д.
Теперь рассмотрим программу формирования страниц, именуемую "nextpage". Это даст возможность изучить ТЕХ’овский процесс выравнивания по вертикали (vertical justification), вводящий ряд концепций, необходимых при реализации более сложной программы, используемой для выравнивания (строк, боксов) по горизонтали (horizontal justification).
Первая из идей, реализуемая в программе "nextpage" — это концепция "плохости" ("badness"). Она представляет собой число, определяемое на. основе совокупности растяжений и сжатий, необходимых при установке значения клея. Пусть имеется список из п боксов (горизонтальный или вертикальный список), разделенных п-1 спецификацями клея. Пусть w — требуемая полная длина списка (т.е. требуемая ширина или высота, в зависимости от того, как выполняется выравнивание — по горизонтали или по вертикали); пусть х — фактическая общая длина боксов и клея, и пусть y,z —
общее число параметров клея для растяжения и сжатия. Плохость в этой ситуации будет такой:
бесконечная, если х > w - z + €, где € -- малый допуск (small tolerance) для компенсации ошибок округления в операциях с плавающей точкой;
100((х-w)/z)~3, если w-z + €>x>w;
О, если х = п; 100((я-х)/Зу)~3, если я > х; плюс штрафы, накладываемые за нарушение границ в нежелательных местах.
Согласно этим формулам, растяжение на у имеет показатель плохости, равный 100/27, или примерно 3.7; растяжение на 2у повышает это значение примерно до 30; растяжение на Зу — до 100 единиц плохости, и столько же при сжатии на максимальную величину, задаваемую параметром z. Я планирую установить величину штрафа примерно в 80 единиц для такого разрыва абзаца или выключной формулы, когда на одной из страниц остается единственная строка абзаца (формулы). Тогда, к примеру, пятистрочный абзац даст 80 единиц штрафа, если разрыв будет произведен после первой или четвертой его строки, но штраф будет нулевым, если разрыв устанавливается после второй или третьей строки. Эти формулы надо будет еще, конечно, "подрегулировать", чтобы результат, даваемый ими, соответствовал моим эстетическим принципам, определяющим "плохой" вывод (bad layout). Пользователь будет иметь возможность задавать свои собственные дополнительные штрафные точки (penalty points) для предотвращения нежелательных разрывов между определенными строками (например, в программе MIX для предотвращения разрыва перед командой, ссылающейся на *-1). Будет установлен также штраф за разрыв по вертикальному клею с линейкой в нем.
Программа nextpage ("следующая-страница") формирует список строк (и связывающий их вертикальный клей), получаемых ею от программы nextparagraph; программа nextparagraph ("следующий-абзац") возвращает последовательность gl hl ... gk hk перемежающихся элементов (вертикального клея gl ... gk и боксов hl ... hk), представляющих собой выровненные по горизонтали (т.е. по ширине страницы) строки текста. Отдельные элементы hi не разрываются и не анализируются программой nextpage, за исключением просмотра значений их атрибутов. Программе nextpage от программы nextparagraph передаются также команды \ff и коды "конец-ввода" (end-of-input).
Программа nextpage накапливает строки до тех пор, пока уже ранее накопленные ею строки в совокупности со вновь введенным абзацем будут иметь высоту, равную предписанной высоте страницы (vsize), или же превысят это значение. Затем данная программа производит разрыв абзаца, введенного последним, после j-й строки, для 0<j<k, при этом значение j должно минимизировать величину показателя плохости; если искомый минимум достигается более, чем при одном значении j, то берется наибольшее из них. После этого клей g(j+l) отбрасывается, а оставшиеся k-j строк переносятся на следующую страницу. Они тут же проверяются — не вызовут ли переполнения новой страницы, и если да, то производится их разрыв точно таким же образом, как это было описано выше.
Этот довольно простой процесс может прерываться процедурой boxinsert, вставляющей боксы. Вычисление данного вставляемого бокса осуществляется автономно, вне процессов формирования абзацев и страниц, после чего предпринимается попытка разместить его на текущей собираемой странице. Если удается сделать это с оценкой "отлично" или "хорошо", вставляемый бокс оставляется на данной странице. Если не удается -- вставляемый бокс переносится на следующую страницу и помещается перед всем тем, что было перенесено с текущей страницы. Если требуется вставить еще добавочные
боксы, то они будут размещаться ниже ранее размещенного первого бокса; эти действия будут выполняться вполне естественным образом, объяснить который, однако, не очень просто.
Подстрочные примечания: на протяжении более чем 2000 страниц книги The Art of Computer Programming я сделал лишь три подстрочных примечания. Мое личное мнение -- использование сносок должно быть редким исключением, поэтому я не предполагаю разрабатывать достаточно изощренный механизм формирования подстрочных примечаний (в частности, разделение длинных сносок между несколькими страницами, или маркировку первой сноски на данной странице звездочкой, а второй — крестиком и т.п.). Тем не менее, сносками можно вполне удовлетворительно управлять путем присоединения атрибута сноски к строке, ссылающейся на некоторую сноску, а также потребовав от программы nextpage разместить данную сноску на той же странице, что и строка, ссылающаяся на нее. Такой подход, в сочетании с оценками плохости, позволяет решать проблему, резервируя место, необходимое для размещения текста подстрочного примечания, в нижней части страницы. Пользователь может получить возможность формировать и более причудливые сноски, если только он не против того, чтобы переписать несколько ТЕХ’овских программ.
При получении команды \ff или кода "конец-ввода" программа nextpage моделирует выполнение команды "vfill blankline" и очищает свои страничные буферы, если они непусты. После обработки кода "конец-ввода" она возвращает null-страницу, что сигнализирует о прекращении ее деятельности, после же команды \ff программа nextpage запрашивает у программы nextparagraph следующий абзац.
Программа nextparagraph собирает строки ширины hsize, делая приостановки для передачи их
в конце абзаца (43);
в начале выключной формулы ($$); с) в конце выключной формулы ($$); d) непосредственно перед операцией vskip;
непосредственно перед и сразу после операций blankline, ctrline, Ijustline, rjustline, hjustline;
при обработке команды \ff или кода "конец-ввода". Операции, перечисленные в пункте (е), порождают независимые строки шириной hsize, не являющиеся частью абзаца, поэтому такие строки передаются без каких-либо изменений в них. Выключные формулы также передаются в неизменном виде, за исключением добавления перед ними и после них клея подходящей величины. (Клей над выключной формулой уменьшается с dispskip до lineskip, если текст в предшествующей строке не "накрывает" отображаемую формулу после ее центрирования.)
Если это удается осуществить, то для некоторого абзаца формируется его текст, или же фрагмент абзаца, начинающегося и/или заканчивающегося парой символов $$; полученный результат передается в программу hjust. Эта программа пытается разбить текст на строки длины hsize наименее плохим путем, привлекая требуемое количество hangindent. Здесь "наименее плохой" способ — это такой, который минимизирует максимальную плохость для данного абзаца на совокупности возможных разрывов строк. В этой точке рассматриваемого процесса обрабатываемый текст состоит из последовательности боксов (это может быть достаточно длинная последовательность), разделенных клеем, причем программа hjust изменять эти боксы не может. Программа hjust, помимо используемой ею идеи клея с переменным весом, реализует еще одно новое средство, благодаря которому она превосходит другие существующие системы выравнивания текста. А именно, hjust умеет эффективно "смотреть вперед", что важно, поскольку ситуация в последующих строках абзаца может оказывать влияние на разрывы строк в его предыдущих строках.
Я обнаружил целый ряд ситуаций, где такого рода программа выравнивания текста обеспечивает получение существенно более высокого качества выходного текста, чем другие программы. Указанный просмотр вперед осуществляяется путем применения принципов "динамического программирования", которые я попытаюсь объяснить ниже.
Попробуем вначале понять, на что обычно похожи боксы, обрабатываемые программой hjust. Они вполне могут быть большими сложными боксами, которые следует трактовать как "черные ящики", как, по-существу, программа nextpage и обходится с их строками. Однако большинство индивидуальных боксов в данной точке процесса представляет собой просто отдельные буквы из одного или нескольких шрифтов. Когда обнаруживается некоторая текстовая лексема, к ней присоединяется индикатор текущего шрифта (так что из 7-битного кода мы получим 12-битный), после чего лексема разбивается на отдельные литеры, составляющие ее. Анализируются пары смежных литер (слева направо) при помощи таблиц, связанных с данным шрифтом. Полученная информация служит основой для вычисления изменений в значениях клея между литерами, для формирования лигатур, если они обнаружены, и т.п. К примеру, для большинства шрифтов литерная последовательность (< означает, что надо использовать специальное "узкое" значение клея между этими двумя литерами. Мы получаем лигатуры заменой
f f
на
<ff>
f i
на
<fi>
f 1
на
<fl>
<ff> i
на
<ffi>
<ff> 1
на
<ffl>.
Подобного рода
процесс
формирования лигатур и подстройки параметров клея имеет
место, когда символьные (литерные) боксы присоединяются к формируемой строке,
до поиска переносов в словах и т.п.; это означает, что при таком подходе в слове "shuffling" теряется шанс получить разрыв в виде "shuff-ling", но чем-то приходится жертвовать.
Лексема <space> ("пробел") преобразуется в клей между боксами, причем принимается у=х и z=x/2, как это уже было объяснено ранее. Действия hskip и quad также порождают переменный клей (variable glue). Всякий раз, когда этот клей имеет значение х или у, большее или равное ширине межлитерного пробела для используемого шрифта, найденное таким образом место будет допустимой точкой разрыва строки без штрафа. Еще одно такое допустимое место — после явного знака переноса слова. (Некоторые команды hskip, используемые для уменьшения пробела (backspacing), имеют отрицательное значение х; они, конечно, недопустимы для организации разрыва строки.) ТЕХ дает двойной у-клей первому пробелу, который идет после точки или двоеточия, восклицательного или вопросительного знака, если только перед этим пробелом не вклинятся буквы или цифры. Точка с запятой и запятая обрабатываются аналогичным способом, только значения компоненты у клея для них устанавливаются равными 1.5 и 1.25 соответственно.
Программа формирования математических формул, обрабатывающая последовательность литер, заключенную внутри пары символов $...$, порождает последовательность боксов в формате, совместимом с форматом текстовых строк вне формул. Допустимыми для разрыва местами в формулах являются позиции сразу после бинарных операторов и отношений. Такого рода разрывы штрафуются; мне предстоит произвести подстройки соответствующих параметров, но наилучшей здесь представляется следующая идея: штраф для отношений типа =, <, = и т.д. должен быть невелик, для операций вроде + , -, х, / — больше по величине, причем для оператора - ("минус") больше, чем для остальных операторов. Верхние и нижние индексы, аргументы функций и тому подобные элементы должны присоединяться к своим боксам неразрывно.
Имеется три вида символов типа "discretionary", используемых для того, чтобы обеспечить или запретить разрывы:
\- данное слово можно перенести в этом месте;
\+ перенос в этом месте не выполнять;
\* в этом месте можно выполнить перенос, но Вместо знака
переноса вставить знак умножения.
Последний из показанных выше трех вариантов может понадобиться для длинных произведении наподобие $(n+l)\*(n+2)\*(n+3)\*(n+4)$.
Помимо использования разрешенных разрывов строк, ТЕХ предпринимает также попытки переноса слов. Он будет проделывать это только с последовательностями, состоящими из букв в нижнем регистре (строчных букв), за ними может следовать (и им может предшествовать) символ, отличный от буквы, цифры, знаков -, \- или \+. Заметим что, например, составные слова с дефисом не будут разрываться. Если обнаружен разрыв, допустимый с точки зрения переноса слова, будет назначен штраф примерно в 30 единиц плохости, однако такой вариант может оказаться более предпочтительным, чем без использования переноса вообще. В рассматриваемом случае будут добавлены еще 20 единиц плохости, если переносимое слово или формула находятся в последней строке абзаца.
Нет способа найти все возможные варианты переносов. Насколько трудна эта задача, можно увидеть на примере слова "record", для которого предполагается, что оно будет разбито как "rec-ord", если оно является существительным, но "re-cord" — если оно будет глаголом. В качестве еще одного примера рассмотрим само слово "hyphenation" ("перенос"), которое скорее будет исключением (which is rather an exception):
hy-phen-a-tion vs. co-or-di-na-tion
Почему в одном из этих двух случаев буква п идет вместе с буквой а, а во втором — нет? Если начать с буквы "а" в словаре и попытаться найти четкие правила переноса слов, не опираясь при этом на слишком большой объем знаний, получим такие пары слов, как a-part vs. ap-er-ture, aph-o-rism vs. a-pha-sia, и т.д. После всех этих примеров становится ясно, что едва ли нас устроит программа переносов, точная, но громоздкая и медленная в работе, занимающая много памяти и процессорного времени. Лучше вместо этого располагать набором правил переноса, которые были бы:
простыми;
почти всегда безопасными;
достаточно мощными для того, чтобы найти, к примеру, порядка 80% переносов из тех, что есть в имеющемся издании книги The Art of Computer Programming.
Анализируя условия, предъявляемые пунктом (с), я обнаружил, что имеют место примерно 2 слова с переносами в них на страницу указанных книг. При этом оказалось, что подготовленные правила переноса лишь в очень редких случаях приводят к действительно плохому разрыву. Время, которое необходимо затратить, чтобы указать способ обработки обнаруженных исключений из правил (т.е. слов, не подчиняющихся имеющимся правилам), совершенно несущественно в сопоставлении с общим временем, затрачиваемым на чтение гранок.
Итак, приведем предлагаемые правила переноса.
Рассматривать только разрывы, в которых обе части слова содержат гласную букву, отличную от концевой е. (Гласные буквы: a,e,i,o,u,y.)
Учитывая рекомендацию правила 1, разрывать слово перед следующими "безопасными" суффиксами: -cions -gion -ly -ment -mial -nary -ness -nomial -sion -tion -ture -vious, а также -tive, перед которым идет гласная, -ed с предшествующей буквой d или t. Разрыв перед -ing (за исключением -bling -pling
или когда этому суффиксу предшествует сдвоенная буква, кроме 11, ss или zz. Для остальных двойных букв — разрыв между ними. Если слово оканчивается на s или 1у, отделить эти окончания и применить перечисленные правила вновь. Суффиксы, распознаваемые с помощью указанных правил, не должны далее разрываться, за исключением vi-ous.
Согласно правилам 1 и 2, выполнять разрывы после следующих "безопасных" приставок (префиксов): algo- equi- ex- hyper- ini- intro- lex- lexi- math- mathe- max- maxi- mini- multi- out- over- pseudo- semi- some- sub- super- there- under- Также для be-, за которой идет буква c,h,s,w; dis-, за которой идет любая буква, кроме h,y; trans-, за которой идет буква a,f,g,l,m; tri-, за которой идет буква a,f,p,u.
Согласно правилам 1 и 2, объединять h с предыдущей буквой, если это согласная, рассматривая полученную комбинацию как одну согласную; теперь разрешается разрывать слово между двумя согласными, если имеет место образец вида vc-cv (v — гласная, с -- согласная), за исключением таких пар согласных как bl ck cl cr dr ght gl gr lk 11 nd ng pl rch rd rm rt sch sp ss st thr zz
(Этот список, возможно, придется еще уточнить).
Будут иметь место некоторые исключения (например, equivocate, minister, somersault, triphammer), однако встречаться они будут настолько редко, что можно считать это обстоятельство не слишком важным. Просмотрев ряд страниц тома 3, я обнаружил 48 переносов, причем предложенные выше правила успешно сработали в 45 случаях, исключениями оказались слова de-gree hap-hazard re-placement.
Конечно, на этих правилах лежит отпечаток моей лексики и моего стиля, но некоторое их расширение позволит получить правила, работающие достаточно хорошо почти у каждого автора.
Теперь об алгоритме динамического программирования, минимизирующего максимальную плохость разрывов. Формула плохости остается той же самой, что и была; тогда для каждого разрыва после данной точки можно вычислить результирующую плохость. Таким способом для каждой допустимой позиции разрыва минимизируется максимальная плохость, которая получится, считая этот и предыдущий разрывы.
Время исполнения данного алгоритма зависит от числа возможных разрывов примерно линейно.
Такой процесс, однако, будет слишком медленным, поскольку предпринимаются попытки разрыва всех слов, тогда как на самом деле надо было бы рассмотреть разрывы лишь для небольшого числа с лов-кандидатов. Следующий приближенный метод должен работать примерно в десять раз быстрее: даны три лучших способа разбить k-ю строку; использовать их для нахождения трех лучших способов разрыва для (к+1)-й строки. Найденные разрывы будут наверняка почти оптимальными, если только не будет слишком плохой сложившаяся ситуация.
Чтобы завершить этот меморандум, я должен был бы объяснить, как ТЕХ выполняет работу с математическими формулами. Мне, однако, надо более подробно проработать набросок требуемого программного кода, поскольку в данный момент я его еще не представляю достаточно четко. В мои планы входит использовать подход, основанный на нисходящем анализе с предшествованием, введенный Воганом Праттом в его статье 1973 POPL, и успешно примененный им в реализации CGOL (к примеру). Выполняется дальнейшая работа по детализации, я надеюсь, что удастся осуществлять вставку боксов за один проход, но может потребоваться строить сначала дерево синтаксического анализа, как это делают Кернитан и Черри.
(конец файла TEXDR.AFT)
13 May:
14 May:
15 May:
16 May:
17 May:
18 May:
19 May:
20 May:
21 May:
22 May:
P№p^K ^or 8/f FcX^j I wb^.
^O yVt_ Immat |to^z< «*'6^ P^Ml^'
Ww+i® w^m* ftch<v<. jk едм*^ *l^> k*oV)c чЛ^и» ^. t-^j of* ^i**.
Ul^(cwCl. дЯе^уЬ 4> ^kot x^b^$ Vy ''^wMts'' ^<ct^ ^и^^
T^L xli'c&s of olO ^ &*~ wlr ( иг.
btHiVK *m%v l^t y<^ д^ SU»kirJl. 6^ ^ovly
^h^ jf^ ^ии^ ^ ^^ "^ Iv^K Oxpt v^nrks, t\p ^Im^ ^~ i/Vt^kt" С^\^\И1Л1^ 'to VwIZ. ^ fw/^twbtvs /^ -Gm^,
МсУС £m£ Vwl^kj 3 <11- ^ аиа, luJl viaaiU (^{'fi of Я.сч vj( и-с.
Cfw^it^ Mitt 6A $"k*.A4. "fr^, p/y<M^ C<M<^H^
&«*Л й*иЬft A< HM^C rOUVA кй#у/И^
13 Jun:
14 Jun:
M*kA cwt of o^x *t л4ю/- 5аМ'^ ^i!
^)l^ "jo vvUVltS ^ N^aW/Ic ^aZ l^f^}
S#m б&кддгу ^ 4»vvk/
n^ info $иу Xdf kut^ vM* Mftg^M 'k glV? ^e <^'voW$.
15 Jun:15 Jun:16 Jun:16 Jun:17 Jun:17 Jun:18 Jun:18 Jun:20 Jun:20 Jun:4 #*^ pUhn ^^(^twt tflct ^fj V^ll AnA< oP И^ »n C/^G(t»< &cA p^V th^<лкг 1еиу>/<PtxycZ 4^7wu(4 T^i^le 4 ^^ ^°^ ^ ~7Ji^4 #*^ pUhn ^^(^twt tflct ^fj V^ll AnA< oP И^ »n C/^G(t»< &cA p^V th^<лкг 1еиу>/<PtxycZ 4^7wu(4 T^i^le 4 ^^ ^°^ ^ ~7Ji^t^xp^w^Ax <M*i^ $иа11йг «^ wmHx#< i^tjyfh'^ М1Д мл^ "t^Vf)t\X .W^ t*f> T^ CAanwInk |k Ut (v {^4" «.yjMVMArVt^xp^w^Ax <M*i^ $иа11йг «^ wmHx#< i^tjyfh'^ М1Д мл^ "t^Vf)t\X .W^ t*f> T^ CAanwInk |k Ut (v {^4" «.yjMVMArV
15 Jun:
15 Jun:
16 Jun:
16 Jun:
17 Jun:
17 Jun:
18 Jun:
18 Jun:
20 Jun:
20 Jun:
4 #*^ pUhn ^^
(^twt tflct ^fj V^ll AnA< oP И^ »n C/^G(t»< &cA p^V th
^<лкг 1еиу>/<
PtxycZ 4^7wu(4 T^i^le 4 ^^ ^°^ ^ ~7Ji^
4 #*^ pUhn ^^
(^twt tflct ^fj V^ll AnA< oP И^ »n C/^G(t»< &cA p^V th
^<лкг 1еиу>/<
PtxycZ 4^7wu(4 T^i^le 4 ^^ ^°^ ^ ~7Ji^
t^xp^w^Ax <M*i^ $иа11йг «^ wmHx#< i^tjyfh'^ М1Д мл^ "t^Vf)t\X .
W^ t*f> T^ CAanwInk |k Ut (v {^4" «.yjMVMArV
t^xp^w^Ax <M*i^ $иа11йг «^ wmHx#< i^tjyfh'^ М1Д мл^ "t^Vf)t\X .
W^ t*f> T^ CAanwInk |k Ut (v {^4" «.yjMVMArV
Л|( J^V,^ ^A H^t ^7 * je6lu- <**1'**^ -(
b^M^ ««JL »W |*~ : w ^^ ^ ^^ ^J •
At Ai I«1 ЛЦ^ ^^^<»~> «^ W- J* jr//> ^^ (T.f'W sub ,f lit ^f* Oa +^ ^t felM -/ ^ Л ^л ^tat T vhm^)
x^t-A* io UZ *f V^
|4a*^y\y i^c M*v ждо^иаА fX <4wj/^ /ztom М<м(я wm^ HpktUMtA^fs ^ ^It^-tvvd* уюуат
(^ttW мцК #к4и1» "йг ^vmmwvJ> Ся^Ш^ p^P^IL C^°^ ^ ^)
СкМ/ Л*^ k M* ^fl*^ 0a4 if I it it /лЬ U^n^*CcMyt/erf park
19 Jun:
21 Jun:
Примечания переводчика
Дадим перечень соответствий между номерами строк в примере на сс. 481-484 и отвечающими им фрагментами книги: Knuth D. Е. The Art of Computer Programming. Volume 2. Seminumerical Algorithms. — Reading a.o.: Wesley Publ. Co., 1969 (далее будем для краткости ссылаться на нее как на АСР2). Через косую черту указаны соответствующие фрагменты из русского перевода данной книги: Кнут Д. Искусство программирования для ЭВМ. Том 2. Получисленные алгоритмы. — М.: Мир, 1977.
Соответствия: строки 5-30 примера —р. 1 оригинала (р. —page; рр. —pages) книги АСР2 / с.13 ее перевода; строки 32-46 —рр. 4-5 / с. 17-18; строки 48-65 —р. 6 / с. 19; строки 66-67 —р. 5 / с. 18; строки 69-75 —р. 6 / с. 19; строки 77-78 —р. 8 / с. 21; строки 80-101 — р. 9 / с. 22; строки 103-108 —р. И / с. 24-25; строки 109-115 — р. 16 / с. 29-30; строки 116-117 —р. 17 / с. 32; строки 118-123 —р. 18 / с. 33; строки 125-128 —р. 26 / с. 42; строки 129-134 —р. 49 / с. 69; строки 135-146 —рр. 51-52 /
с. 71-72; строки 148-154 — р. 54 / с. 74; строки 155-166 не имеют соответствия в книге АСР2 и ее переводе.
Примечание к с. 490; средство «eqalign». Выравнивание выполняется по какому- либо знаку, например, по знаку равенства, как в рассматриваемом случае — формула (6) (р. 16 / с. 30; р. 17 / с. 32).
Перевод дневниковых записей.
февраля:
Спал не очень хорошо. Некоторое время занимался корреспонденцией. Комитет по кадровым перемещениям Станфордского университета одобрил повышение Тайана. Закончил программу квалификационного экзамена и пояснительную записку к следующему заседанию комитета. Спать лег рано..
февраля:
Репетиция с chunkee-group. Отладка средств оценки булевских выражений в MIX- программе. Обсуждение компьютерного типографского набора с использованием шрифтов из моей книги. Чтение диссертации Рейсера. Церковный хор.
30 марта:
Получены, наконец, гранки второго тома, выглядят они в смысле полиграфического исполнения ужасно. Думаю, что мне придется самому заняться решением этой проблемы. Вечернее собрание seder supper в церкви — хоть какой-то светлый эпизод за всю эту унылую неделю.
мая:
Сегодня к часу ночи завершил раздел 7.1 — Ура!
мая:
В Сан Хосе с Джил, посмотрели церковные записи из городка вблизи Гейдельберга: познакомились со старонемецким рукописным текстом.
мая:
Прибрался в офисе, отделался, наконец, от раздела 7.1. Просмотрел первые 30 страниц корректуры, пока понял, как работает белфастская издательская система.
мая:
Читал об издательской системе Белловских лабораторий. Начата основная работа по ТЕХ’у.
мая:
Продолжение работ по ТЕХ’у.
мая:
Работал над длинной тестовой программой для ТЕХ’а. Ходил в кино на фильмы «Airport 77» и «Earthquake».
мая:
День Матери. Повел Джил на выставку брошюровочного оборудования. Встречал Комлосов в аэропорту. Отдыхал от технической работы, спать лег рано.
мая:
Работал с корреспонденцией. Семинар Комлоса.
мая:
Работал с Джил над драпировками и т. п. Размышлял над ТЕХ’ом.
11 мая:
Днем семинар Комлоса. Делал наброски ТЕХ’овских программ.
12 мая:
Писал черновой вариант отчета по ТЕХ’у, не ложился до 5 часов утра, вгоняя его в машину.
13 мая:
Готовился к лекции Bit Fiddling. Спать лег рано.
14 мая:
В Сакраменто с Джил (поездка по линии Библиотечной ассоциации). Видел образцы прекрасной печати.
15 мая:
Завершение поездки. Закончил подготовку к завтрашнему выступлению.
16 мая:
Получил из библиотеки книги по издательскому делу. Начал недельный курс занятий по письменности. Прочел также часовую лекцию по «Bit Fiddling». Вечером ходил на лекцию по органной музыке, а также в кино, на фильм «Anna & King of Sian».
17 мая:
Безуспешная попытка заменить сплайны «сквинсами» (кусочно-квадратичными кривыми).
18 мая:
Взял слайды 20 страниц из томов 1 и 2. Делал наброски программ для генерации шрифтов. Обед с Каем Юнем и Шахидом. Спать лег рано.
19 мая:
Используя слайды, понял, как работают шрифты в нижнем регистре. Всю ночь продолжал формировать информацию по шрифтам.
20 мая:
Продолжал работу по формированию шрифтов, к 5 часам утра получил набросок строчных и прописных букв прямого и курсивного начертаний и цифр 0-9.
21 мая:
Завершил заметки относительно стандартных шрифтов, подготовил компьютерные программы вычерчивания сплайнов.
22 мая:
Пилил бамбук для драпировок в музыкальный салон. Идем на обед в церковь, а потом лекция по математике.
13 июня:
Перемещался из офиса в школу: начинается ежегодный отпуск! Ходили в кино (Network и Lenny).
14 июня:
Работа по отладке шрифтов, Прогон через ^nag не дал понимания, какие значения у производных.
июня:
Весь день и всю ночь пытался найти правильный подход к определению производных—прорыв наступил примерно в час ночи: окончательное решение получилось очень красивым (4 часа утра). Лег в 7 утра.
июня:
С 11 утра отлаживал в лаборатории AI новую сплайновую программу, после были с Джил на ланче у ее дяди Дона. Вылавливал «ненормальности» в этой программе (смотри первые записи и тогда поймешь, что я имею в виду). Отправился спать в 4 часа утра.
июня:
Развешивали новые макраме в музыкальном салоне. Сделал серьезные улучшения в программе формирования шрифтов на основе сплайнов.
июня:
Репетиция с оркестром для завтрашнего исполнения кантаты (Бах 103). Вносил изменения в шрифтовую программу, а также разделил ее на две независимо компилируемые части.
июня:
День Счастливого отца. Прогулка на велосипедах (все четверо) по Канадской дороге к храму на воде. Играл с Джоном и Дженни в «Бермудский треугольник».
июня:
Разгребал корреспонденцию. Говорил с Лили Рамшоу о сплайнах. Китайский банкет, устроенный Симпозиумом по численному анализу. Потом пошел в лабораторию AI и работал с шрифтовой программой до 4 часов утра.
июня:
Эксперименты со все уменьшающимися разрешениями обнаружили новые моменты, требующие учета. Установка в лаборатории телекамеры для экспериментов со шрифтами.
ТЕХ.ONE
Когда мы покинули нашего героя в конце главы 24, он изо всех сил сражался с проблемой генерации шрифтов вычислительными методами, после того как более или менее толково изложил проблему типографского набора в памятной записке TEXDR.AFT. В мае он попросил прочитать эту записку нескольких своих коллег; сейчас приближается июль и позади месячная поездка в Юго-Восточную Азию. Настало время переработать черновой вариант меморандума от 13 мая с тем, чтобы Майкл Плэсс и Фрэнк Лианг, ассистенты по исследовательским проектам, могли подготовить реализацию опытного варианта (прототипа) создаваемой системы1.
30 Jun:
*лр ЦК vkjilw&M) 1и'еЛ mcmw ^ ^4gHl Amv) "k ’A'ngA 1^1 Д/bf- ftdi*j ^ ^/ WII wt go 0* w)j w/ of* gwfy $JwMt
^^4 ;щси<£ 0И ess O&r 4 w. caIcmU^s Mach wUl Ad Whr.
GdV ^(tiLk. сЦегру зЬЬ ^ б&^КУ'
( К; 3° &* ^<^j
2 Jul:
CU^mJ vy *0*4* f^ff^l «4М f^4^f j^ ft'^^ Sl<yt <5v*k dlept
6^4 f^i vtMvJL лЛ 331 ^^лЬ
1 Перевод дневниковых записей, следующих ниже, см. в примечании 3 переводчика в конце главы. — Прим. ред.
4 Jul:4 Jul:5 Jul:6 Jul:5 Jul:6 Jul:7 Jul:8 Jul:9 Jul:10 Jul:11 Jul:12 Jul:7 Jul:8 Jul:9 Jul:10 Jul:11 Jul:12 Jul:№W T^.OUC Jn^- »^Ьйа <^ы #**yor»V*enl*it№W T^.OUC Jn^- »^Ьйа <^ы #**yor»V*enl*it
4 Jul:
4 Jul:
5 Jul:
6 Jul:
5 Jul:
6 Jul:
7 Jul:
8 Jul:
9 Jul:
10 Jul:
11 Jul:
12 Jul:
7 Jul:
8 Jul:
9 Jul:
10 Jul:
11 Jul:
12 Jul:
№W T^.OUC Jn^- »^Ьйа <^ы #**yor»V*enl*it
№W T^.OUC Jn^- »^Ьйа <^ы #**yor»V*enl*it
*k ^i^r^ol^j ^ ^i^ ^Jl^ pAy 4^ v/^r<8^ ^VW*v<L( at \)r*(iv MA6Hj/(hz< A Aay of гилеМ ^!
51^ Ыс, tU^ tL^kt aL^ Л^ Avtdwti -^ 'TCX
^(niw p/у <4" VM»n Hiiuiw, «d* v^WSk^ f&fk.
&>t -м^ ъ( “fE^S SaY^ ^efu^s y^'^e^;
ГсЛи^^Л *Их к^ч^Я- ew^ КлА^к. V4 ^ ^ cAept" /Cf£ vlo^ ^ff^j ^^ ^^
Mo^ Te^ vuh+i^
FlnoM \>Л»+^ ^ Ч^0*^ , ^i 4? Al lab * ^ fV {*jf Cp^
^°^ ^Ц; in b^!/ U^ah *H sHr ^л^и, <^ Ф^ krk
^nifl^ Vji?^ il^,o^ *+ 2~а**
Twv -wak? Gnftif*Jl*6— ^оФ UZt «dw/Zy, -R^W i* +** ^^«-Uu^
^си\ tv mik pW^HM^ fM- AwM^tr BdM' fR*** *> ^^^ЧЁХ
L^ ^^ *M MitLo^Z Fr»*k к J^xc^ pk»v Ar TM /^/«^ЪЬви /t 'flu IaH^iU^C
А теперь — файл ТЕХ. ONE:
Д.Кнут, 12 июля 1977 г.Д.Кнут, 12 июля 1977 г.Предварительное описание TEX’а
Д.Кнут, 12 июля 1977 г.
Д.Кнут, 12 июля 1977 г.
В этой записке я попытаюсь объяснить, что такое система ТЕХ для подготовки документов полиграфического качества. Часть правил, закладываемых в систему ТЕХ, пока еще не устоялась, однако значительная доля данной памятной записки определяет систему ТЕХ в том виде, как она сейчас реализуется, что будет полезно тем, кто принимает участие в ее реализации. [Замечание: Если вы уже прочитали черновой вариант данного предварительного описания, забудьте, пожалуйста, все, что было в том документе, и попытайтесь вообще забыть . о его существовании. Произошли большие изменения, вызванные ценными откликами и замечаниями коллег по черновому варианту данного документа, надо теперь рассмотреть состояние проекта в том виде, каков он сейчас на самом деле.]
ТЕХ ориентирован на технические тексты. "Посвященные" произносят букву X в ТЕХ как греческое "хи" (ср. также шотландский звук 'ch' в слове ‘Loch Ness'),
поскольку слово ‘technical’ происходит от греческого корня, обозначающего искусство, а также технику. Я занимаюсь сейчас разработкой этой системы для того, чтобы использовать ее в подготовке к изданию моих книг из серии The Art of Computer Programming, т.е. первоначально система будет ориентирована и настроена таким образом, чтобы соответствовать стилю моих книг, однако не должно быть трудностей с приспособлением ее и для других аналогичных целей, если кому-то это понадобится.
Входом для ТЕХ’а является текстовый файл в формате, например, редактора TVeditor, используемого в Стамфордской лаборатории искусственного интеллекта. Результат работы ТЕХ’а -- последовательность страниц, подготавливаемая "за один проход", пригодная для распечатывания на различных устройствах. Этот отчет представляет собой попытку объяснить, каким образом можно из входа получить нечто на выходе. Основная идея состоит в том, что требуемый процесс рассматривается как некая последовательность операций над двумерными "боксами"; упрощенно говоря, входом является длинная строка символов, взятых из различных шрифтов, причем каждый из символов может интерпретироваться как нечто, занимающее небольшую прямоугольную область, а выход получается "склеиванием" этих областей друг с другом по горизонтали или вертикали, с соблюдением при этом различных соглашений относительно центрирования и выравнивания. Конечным результатом такого рода процесса будет совокупность больших прямоугольных областей, которые и будут представлять собой требуемые страницы.
Если LISP работает с одномерными символьными строками, ТЕХ имеет дело с двумерными структурами, собранными из прямоугольных боксов; продолжая аналогию с LISP’ом, можно сказать, что ТЕХ располагает как горизонтальными, так и вертикальными операциями типа операции cons. К тому же, ТЕХ базируется еще на одной важной концепции -- понятии эластичного клея между боксами, представляющего собой некий "строительный раствор", который может растягиваться или сжиматься в той или иной степени, так что конструкции из боксов могут объединяться друг с другом гибким образом.
Мне следовало бы везде на протяжении данного документа вместо термина ггклейгг ("glue") использовать термин "строительный раствор" ("mortar"); меня здесь смущает только то, что из-за дополнительного слога слово "mortar" несколько труднее в произношении, чем "glue" и, кроме того, для его набора требуется чуть больше времени. Может быть, в руководстве пользователя будет последовательно использоваться слово "mortar"; данный документ ни в коем случае НЕ является руководством пользователя.
Чтобы объяснить принципы работы ТЕХ’а более подробно, я буду излагать их на двух уровнях: давать низкоуровневое описание того, как именно выполняется обработка в том или ином случае, а также высокоуровневое описание того, как реализуются те или иные сложные действия.
Для начала, имея в виду низкоуровневое описание, надо понимать, что вход ТЕХ’а — это на самом деле не последовательность (строка) боксов, а файл из литер в 7-битовой кодировке. Этот файл именуется "внешний входной файл". Семь символов (литер) из числа отображаемых при печати будут иметь в таких файлах специальное назначение; на протяжении всего этого меморандума в данном качестве выступают следующие литеры: \{}$®%# . Можно, однако, использовать и семерку других символов, проведя соответствующие переназначения. В число этих семи основных ограничителей входят:
\ escape-символ, используемый для указания на переключение из текстового режима в командный
{ начало группы } конец группы $ начало и окончание математических формул ® символ табуляции (выравнивания) X начало комментария # макропараметр
Первое, что делает, ТЕХ — преобразует этот файл во "внутренний входной файл", следуя при этом таким правилам:
Удалить, если таковая имеется, первую управляющую страницу, сформированую редактором TVeditor.
На каждой из оставшихся страниц заменить признак конца страницы (44) на код возврата каретки (45). Удалить все символы перевода строки (42), нуль-символы ООО), коды удаления (477) и символы вертикальной табуляции (43). Заменить все символы (коды) горизонтальной табуляции (41) пробелами (40).
Удалить все пробелы, следующие за символом возврата каретки.
Если два или более символа возврата каретки следуют подряд, заменить их все кроме первого символами вертикальной табуляции (43). Они используются для обозначения конца абзаца; другими словами, пользователь задает конец абзаца либо двукратным нажатием клавиши возврата каретки, либо переводом страницы, за которым следует нажатие клавиши возврата каретки.
Заменить все остающиеся символы возврата каретки пробелами.
Если в строке следуют подряд два пробела, заменить их одним пробелом.
Заменить \ на ’00, ® на 41, $ на 42, {на 44, } на 45, # на 477 (в предположении, что основные символы-ограничители определены так, как это упоминалось выше).
Добавить бесконечно много символов 45 справа.
Смысл применения правила 8 заключается в том, что ТЕХ использует символы { и } для группирования и завершающие текст символы 45 (они эквивалентны символам }) будут соответствовать символам { , для которых пользователь забыл написать парные им символы завершения группирования во внешнем входном файле. Следуя приведенным выше правилам, ТЕХ получает внутренний входной файл, не содержащий символов типа семи перечисленных выше ограничителей, а также сдвоенных пробелов. Управление пробелами в выходном документе осуществляется ТЕХ’ом с помощью других средств, а семь упомянутых выше основных символов-ограничителей могут быть вставлены в текст, если в этом возникнет необходимость, с использованием командных последовательностей вроде \ascii473 для символа {.
[На самом деле основных символов-ограничителей девять; не названные еще два таких символа — это t и I для верхних и нижних индексов соответственно, но использовать их можно только в математических формулах. Из-за нестыковок между различными вариантами ASCII-кодов, станфордские коды не являются универсальными. В частности, код 43 в варианте, принятом в Массачуссетском технологическом институте (MIT), представляет собой символ t. Существует способ задать t как один из девяти основных символов-ограничителей, даже в MIT, и ТЕХ будет интерпретировать его соответственно — не удалит в правиле 2 и не перепутает с символом, добавляемым в правиле 4. ТЕХ на самом деле не применяет правил 1-8 в том виде, как они представлены выше, он использует некий эффективный алгоритм, который по производимым им результатам эквивалентен данному набору правил.]
Теперь перейдем к высокоуровневому описанию и посмотрим, каким образом пользователь сообщает ТЕХ’у свои требования относительно сложных объектов. Приводимый ниже пример довольно велик и его лучше бегло просмотреть, не изучая детально. Он задуман в качестве тестовой задачи для начальной отработки создаваемой системы. Подробно анализировать его сейчас не надо, просто просмотрите его бегло и переходите к чтению следующей части данного меморандума. (Примечание: Этот пример основан на материале начальных страниц моей книги "Seminumerical Algorithms", где опущены фрагменты, не представляющие особого интереса с издательской точки зрения и с точки зрения требований, предъявляемых к реализуемой системе. Итак, предлагаемый пример посвящен большей частью сложным конструкциям, в этом смысле его нельзя считать типичным.
Читателю, который хотел бы разобраться в тонкостях данного примера более основательно, полезно было бы иметь под рукой оригинал упомянутой книги, чтобы можно было сравнивать входной ТЕХ’овский текст с требуемым результатом его обработки.)
\require ACPhdrУ.1
У,Example ТЕХ input related to Seminumerical AlgorithmsУ.2
\ACPpages starting at page 1:%3
\titlepage ‘/.This tells the page format routine not to puta page number on top %4
\ruiininglefthead{RANDOM NUMBERS}X5
\ljustline{\hexpand 11 pt {\:p CHAPTER \hskip 10 pt THREE}}7.6
\vskip 1 cm plus 30 pt minus 10 ptX7
\rjustline{\:q RANDOM NUMBERS}X8
\vskip .5 cm plus 1 pc minus 5 ptX9
\quoteformat{Anyone who considers arithmetical \crX10
methods of producing random digits \cr is, of course,X11
in a state of sin. \cr} author{J0HN VON NEUMANN (1951)}X12
\quot eformat {Round numbers are always false.\cr}X13
author {SAMUEL JOHNSON (c. 1750)}X14
\vskip 1 cm plus 1 pc minus 5 ptX15
\runningrighthead{INTR0DUCTI0N} section{3.1}X16
\sectionbegin{3.1.X17
INTRODUCTION}X18
Numbers which are "chosen at random" are useful in a wide variety ofX19
applications. For example:X20
X This blank line specifies end of paragraphX21
\yskip X This means a bit of extra space between paragraphsX22
\textindent{a)}{\sl Simulation.}\xskip When a computer is used to simulateX23
natural phenomena, random numbers are required to make things realistic.X24
Simulation covers many fields, from the study of nuclear physics (whereX25
particles are subject to random collisions) to systems engineering (whereX26
people come into, say, a bank at random intervals) ДрагX27
\yskip\textindent{b)}{\sl Sampling.}\xskip It is often impractical to examine X28
all cases, but a random sample will provide insight into what constitutesX29
"typical" behavior.X30
31
\yskip It is not easy to invent a foolproof random-number generator. This fact X32
was convincingly impressed upon the author several years ago, when he attempted X33
to create a fantastically good random-number generator using the followingX34
peculiar method:X35
36
\yskip\yskip\noindent{\bf Algorithm K}\xskip(\sl" Super-random" numberX37
generator.}).\xskip Given a 10-digit decimal number $X$, this algorithm may be */. 38 used to change $X$ to the number which should come next in a supposedly random % 39 sequence.\par % 40 \algstep KI. [Choose number of iterations.] Set $Y<-\lfloor X/10t9 \rfloor$, % 41 i.e., the most significant digit of $X$. (We will execute steps K2 through K13 */. 42 $Y+1$ times; that is, we will randomize the digits a {\sl random} number of % 43 times. \par % 44 \algstep K10. [99999 modify.] If $X<10t5$, set $X<-Xt2 + 99999$; % 45 otherwise set $X«-X-99999$. \xskip\blackslug % 46
47 \topinsert{\ctrline{\:r Table 1} % 48 \ctrline{\:d A COLOSSAL COINCIDENCE: THE NUMBER 6065038420} У. 49 \ctrline{\:d IS TRANSFORMED INTO ITSELF BY ALGORITHM К.} У. 50 \vskip 3 pt \hrule У. 51 \ctrline{\valign{\vskip 6pt\top{#}®\vskip 6pt\top{#}\cr У. 52 \halign{\left{#}\quad®\ctr{#}®\left{#}\cr У. 53 Step®\$X$ (after)\cr У. 54 \vskip 10 pt plus 10 pt minus 5 pt У. 55 Kl®6065038420\cr K12®190586778®Y=5\cr} ’/end of \halign on line 53 У. 56 \vskip 10 pt plus 10 pt minus 5pt \cr %end of first column to be \valigned У, 57 \vrule У,vertical rule between columns У. 58 \halign{\left{#}\quad®\ctr{#}®\left{#}\cr У. 59 Step®$X$ (after)\cr У. 60 \vskip 10 pt plus 10 pt minus 5 pt X 61 K10®1620063735\cr % 62 Kll®1620063735\cr K12®6065038420®Y=0\cr}y.end of \halign on line 59 У. 63 \vskip 10 pt plus 10 pt minus 5pt \cr}} ‘/end of 2nd \valigned column,\ctrline У, 64 \hrule} Zend of the \topinsert on line 48 У, 65 \yskip\yskip The moral to this story is that {\sl random numbers should not be У, 66 generated with a method chosen at random.} Some theory should be used. У. 67
68 \exbegin У. 69 \tr\exno 1. [20] Suppose that you wish to obtain a decimal digit at random, not У, 70 using a computer. Shifting to exercise 16, let $f(x,y)$ be a function such that У, 71 if $0<x,y<m$, then $0<f(x,y)<m$. The sequence is constructed by selecting % 72 $XI0$ and $X44$ arbitrarily, and then letting $$ У. 73 X4{n+1} = f(Xln,XI{n-l}) \qquad {\rm for} \qquad n>0.$$ У. 74 What is the maximum period conceivably attainable in this case? У. 75
76 \exno 17. [10] Generalize the situation in the previous exercise so that У, 77 $XI{n+l}$ depends on the preceding $k$ values of the sequence. % 78 \par\vskip plus 100 cm\eject У. 79 \runningrighthead{GENERATING UNIFORM RANDOM NUMBERS} section{3.2} % 80 \sectionbegin{3.2. GENERATING UNIFORM RANDOM NUMBERS} У. 81 In this section we shall consider methods for generating a sequence of random У. 82 fractions, i.e., random {\sl real numbers $Uln$, uniformly distributed У. 83 between zero and one.} Since a computer can represent a real number with only У, 84 finite accuracy, we shall actually be generating integers $Xln$ between У. 85 zero and some number $m$; the fraction$$U4n = Xln/m \eqno(l)$$ will У. 86 then lie between zero and one. У. 87
88 \vskip.4in plus.2in minus.2in У. 89 \runningrighthead{THE LINEAR CONGRUENTIAL METHOD} section{3.2.1} У. 90 \sectionbegin{3.2.1. The Linear Congruential Method} У. 91
By far the most successful random number generators known today are specialX92
cases of the following scheme, introduced by D. H. Lehmer in 1948. [SeeX93
{\sl Annals Harvard Comp. Lab.} {\bf 26}(1951), 141-146.] We choose fourX94
((magic numbers’’:$$X95
\halign{\right{#}®\left{\quad\rm{#}\qquad}®\right{#}®\left{#}\crX96
XI0,®the starting value;®XI0®>0.\crX97
m,®the modulus;®m®>X4,\quad m>a,\quad m>c.\cr}\eqno(l)$$X98
The desired sequence of random numbers $\langle Xin \rangle$ is thenX 99
obtained by setting$$X4{n+l}=(aX4n+c)\mod m,\qquad n>0.\eqno(2)$$This isУ.100
called a {\sl linear congruential sequence.}X101
7.102
Let $w$ be the computer’s word size. The following program computes $(aX+c)X103
\mod(w+l)$ efficiently:$$\halign{{\it#}\qquad®\hjustto 25pt{\left{#}}®X104
\left{\tt#}\crX105
01®LDAN®X\crX106
02®MUL®A\cr 05®JANN®*+3\crX107
07®ADD®=W-l=\qquad\blackslug\cr}\eqno(2) $$X108
{\sl Proof.}\xskip We have $x=l+qpte$ for some integer $q$ which is not а X109 multiple of $p$. By the binomial formula$$X110
\eqalign{xtp®=l+{p\choose l}qpte+\cdots+{p\choose{p-l}}qt{p-l} 'У.Ш
pt{(p-l)e}+qtp pt{pe}\cr7.112
®=l+qpt{e+l}\group(){l+l\over p{p\choose 2}qpte+ l\overpX113
{p\choose 3}qt2 pt{2e}+\cdots+l\over p{p\choosep}qt{p-l}X114
pt{(p-l)e}.\cr}$$ By repeated application of Lemma P, we find thatX115
\def\mlo#l{\ ({\rm modulo}\ #l)}$$\eqalign{(atptg - l)/(a-l)®= 0 \mloX116
{ptg},\cr(atptg-l)/(a-l)®\neqv 0 \mlo{pt{g+l}}.\cr}\eqno(6)$$X117
If $Kk<p$, $p\choose k$ is divisible by $p$. \biglpren{\sl Note: }\xskip AX118
generalization of this result appears in exercise 3.2.2-ll(a).\bigrpren\ ByX119
Euler’s theorem (exercise 1.2.4-48), $at{\varphi(pt{e-f})}= 1 \mloX120
{pt{e-f}}; hence $X$ is a divisor of$$X121
Х(рШ{е41} \ldots pltt{elt} = {\rm lcm}\group()X122
{X(p44t{e44},\ldots,X(pltt{elt})}.\eqno(9)$$X123
X124
This algorithm in \MIX\ is simply$$X125
\halign{\hjust to 25pt{\left{\tt#}}®\left{\tt#\qquad}®\left{#}\crX126
J6NN®*+2®\underline{\it Al. }j<0?}\crX127
STA®Z®\qquad\qquad-»Z. \cr}\eqno (7) $$7.128
That was on page 26. If we skip to page 49, $Y44 +\cdots+ Ylk$ willX129
equal $n$ with probability$$7.130
\suml{{yllAcdots+ylk=n}\atop{y44 ,\ldots ,ylk>0}}7131
\prodl{l<s<k}X132
{et{-npls}\group(){npls}t{yls}}\over{yls!}7133
={et{-n}ntn}\over{n!}.$$7134
This is not hard to express in terms of $n$-dimensional integrals,$$7135
{\inti{ain}tn dYin \intl{al{n-l}}t{Yln}7136
dYI{n<}\ldots\intl{all}t{YI2}dYll} \over7.137
{\intl0tn dYIn \int!0t{Yln} dYI{n-l}\ldots7.138
\int40t{Y42}dY41},\qquad{\rm where}\qquad aij=7139
\max(j-t,0).\eqno(24)$$7140
This together with (25) implies that $$\def\rtn{\sqrt n}7141
\mathop{lim}4{n-»m} s \over \rtn7142
\sumi{\rtn s<k<n}{n\choose k}\group(){k\over n - s\over rtn}tk7143
\group(){s\over\rtn + 1 - к \over n}t{n-k-l} = et{-2s}t2,\qquad s>0,7144
\eqno(27)$$ a limiting relationship which appears to be quite difficult to7145
prove directly. 7,146 7.147
\exbegin\exno 17. [HM26] Let $t$ be a fixed real number. For $0<k<n$, let$$ 7.148 Pl{nk}(x)=\inttxl{n-t}dxln\intl{n-l-t}t{xln}dxl{n-l} 7.149 \ldots\intl{k+l-t}t{xHk+2}}dxl{k+l}\intl0t{xl{k+l}} 7.150 dxlk\ldots\int!0t{xl2}dx44; $$ 7.151 Eq. (24) is equal to$$\def\sumk{\sumi{l<k<n}} 7.152 \sumk X\primeik Y\primelk \group/.{\sqrt{sumk{X\primeik}t2} 7.153 \sqrt{\sumk{Y\primelk}t2}}. $$\par 7.154 \runningrighthead{A BIG MATRIX DISPLAY} section{3.3.3.3} 7.155 \subsectionbegin{3.3.3.3. This subsection doesn’t exist}Finally, look at page 7.156 91.$$\def\diagdots{\raise lOpt .\hskip 4pt \raise 5pt .\hskip 4pt .} 7.157 \eqalign{U®=\group(){\halign{\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}® 7.158 \ctr{#}\cr l\cr ®\diagdots\cr ®®l\cr 7.159 cll®\ldots®cl{k-l}®l®cl{k+l}®cln\cr 7.160 ®®®®l\cr®®®®®\diagdots\cr®®®®®®l\cr}},\cr 7.161 Ut{-l}®=\group(){\halign{\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}®\ctr{#}® 7.162 \ctr{#}\cr l\cr ®\diagdots\cr ®®l\cr 7.163 -c41®\ldots®-c4{k-l}®l®-c4{k+l}®-cin\cr 7.164 ®®®®l\cr®®®®®\diagdots\cr®®®®®®l\cr}}}$$ 7165 This ends the test data, fortunately TEX is working fine. 7.166
Прежде всего относительно приведенной выше задачи надо сказать, что она намного сложнее обычного ТЕХ’овского текста по причинам, упоминавшимся выше. Затем следует подчеркнуть, что она описана с использованием средств не базового языка, а некоторого расширения ТЕХ’а. Например, "\ACPpages" во второй строке представляет собой имя специальной программы, которая осуществляет подготовку страниц в формате книги "The Art of Computer Programming". Кодовые слова \ACPpages, \titlepage, \runninglefthead, \runningrighthead, \quoteformat, \sectionbegin, \xskip, \yskip, \textindent, \algstep, \exbegin, \exno и \subsectionbegin являются специфическими для моих книг и должны быть определены в терминах низкоуровневых ТЕХ’овских примитивов способом, поясняемым ниже. Далее, большинство шрифтов встроены в эти определения, например, в определении "\sectionbegin" указывается, что текст раздела по умолчанию набирается шрифтом кегля 10 pt, в то время как определение "\exbegin" для набора текста упражнений задает шрифт кегля 9 pt. Еще одно определение задает, что слово MIX обычно должно набираться шрифтом типа "шрифт пишущей машинки"; скрытое определение
\def\MIX«\tt MIX}}
обеспечит автоматическое выполнение этого требования при обработке строки 125. Механизм макроопределений ТЕХ’а мы рассмотрим позднее; три простых примера * имеются в строках с номерами 141, 152 и 157 нашей тестовой программы, где для экономии места вводятся сокращенные обозначения для некоторых фрагментов ТЕХ’овского текста. Для любознательных читателей, кому было бы интересно увидеть более сложную конструкцию, даем определение кодового слова \quoteformat:
\def\quoteformat#! author#2{\lineskip И pt plus .5 pt minus 1 pt \vskip 6 pt plus 2 pt minus 2 pt \def\rm{\:s} \def\sl{\:t} {\sl \halign{\right{##}\cr#l}} \vskip 6 pt plus 2 pt minus 2 pt \rm \rjustline{#2} \vskip 11 pt plus 4 pt minus 2 pt}
Слову "author" ("автор"), которое встречается в данном определении, не предшествует управляющий символ \ (escape character), поскольку оно
просматривается только в составе определения макро \quoteformat. Пожалуйста, не пытайтесь понять это сообщение сейчас, ТЕХ на самом деле весьма прост, поверьте мне.
И в самом деле, забудем ненадолго все ТЕХ’овские сложности и попробуем представить себе ТЕХ’овский документ в его простейшей форме. В качестве такого документа, в определенном смысле представляющего собой альтернативу рассмотренным выше примерам, можно указать следующий. Пусть имеется некий текстовый файл, не имеющий вхождений символа “\” ; этому файлу предшествует код вида "\deffnt a METS".
В этом случае ТЕХ осуществит вывод данного файла, используя шрифт с именем METS, причем будет произведено выравнивание всех абзацев выводимого текста.
Самый первый отличный от пробела символ во внешнем входном файле воспринимается ТЕХ’ом как пользовательский командный символ (escape character). С точки зрения пользователя данный символ может считаться в известной степени аналогом "управляющих" клавиш текстового редактора, поскольку этот символ предшествует инструкциям системы. Обычно внешний входной файл начинается строкой вида \require FILENAME
где в файле FILENAME содержатся пользовательские значения по умолчанию для ряда параметров; это и было сделано в строке 1 нашего "большого примера".
Файл ACPhdr, упоминаемый в этой строке, начинается такой последовательностью команд:
\chcode) 173<-2. Uhcode’176«-3. \chcode44<-4. Xchcode^Gt-S. \chcode45<-6. \chcode43<-7. ^hccde'136*-8. Xchcode’l«-9.
которая, в Станфорде, определяет символы 0$®%# в качестве основных ограничителей с номерами 2,3,4,5,б,7,8 и 9 соответственно. Конечно, большинство пользователей ни в коем случае не захотят связываться с такого рода низкоуровневыми мелочами, поэтому они будут применять команду \require для подключения каких-либо других файлов. Файл, задаваемый командой \ACPhdr, определяет еще различные кодовые слова наподобие \quoteformat, а также определяет другие соглашения, принятые при наборе книги The Art of Computer Programming.
Войдем теперь в пример после его первой строки и посмотрим, что и как здесь можно понять. Начало любой главы с полиграфической точки зрения всегда связано с определенными сложностями. Строки 3-18 нашего примера относятся к преодолению именно этих трудностей, сама же глава фактически начинается только в строке 19. Теперь соберемся с духом и сразимся со строками 3-18.
Спецификация \runninglefthead ("левый колонтитул") в строке 5 содержит текстовую строку, которая появится в самом верху левых страниц разворотов книги. Строка 6 содержит первый фрагмент текста, который появится на создаваемой странице, однако обратите внимание сначала на строку с номером 8, которая попроще.
Строка 8 предписывает использовать шрифт q (который в файле ACPhdr определяется как "Computer Modern Gothic Bold 20 point", т.е. полужирный шрифт семейства Computer Modern кегля 20 pt; это шрифт, разработкой которого я занимаюсь в настоящее время) для набора слов RANDOM NUMBERS (СЛУЧАЙНЫЕ ЧИСЛА), а также выровнять эти слова по правому краю строки (\rjustline). Строка 6 аналогична строке 8, но задает использование другого шрифта, шрифта р (при этом "выключается" шрифт Computer Modern Gothic И pt, использовавшийся до момента переключения на шрифт р); пользователь ТЕХ’а может применять до 32 шрифтов, именуемых как @, А или а, В или b,..., Z или z, [, I или <, ], t или *• соответственно (сравните это с ASCII-кодами, где можно использовать любой символ и он будет определяться своими пятью младшими разрядами).
Шрифт @ является специальным, это единственный шрифт, для символов которого
допускается иметь различные высоту и интерлиньяж.
Для всех остальных шрифтов величины интерлиньяж и высот боксов, содержащих литеры, являются фиксированными. Более того, ТЕХ считает, что некоторые из его математических символов также принадлежат шрифту @.
Если продолжить анализ строки 6 примера, то можно увидеть здесь команду "\hskip 10 pt", задающую горизонтальный пробел величиной в 10 pt, а также команду "\hexpand И pt", означающую, что надо взять рассматриваемую строку и растянуть ее на И pt шире, чем это должно быть в норме. Отметим также, что определения шрифтов, заданные внутри группы {...}, теряют свою силу вне данной пары фигурных скобок. Аналогично обстоит дело и с другими определениями.
Теперь пришло время объяснить ТЕХ^овский механизм растяжения и сжатия последовательностей боксов. Размеры в ТЕХ’е могут задаваться в типографских пунктах, пиках, дюймах, сантиметрах или миллиметрах, что обозначается соответственно, аббревиатурами pt, pc, in, cm, mm. Эти обозначения имеют смысл только в таких контекстах, где используются физические длины, и одно из таких обозначений в такого рода контексте должно присутствовать обязательно, даже если фактическое значение соответствующей величины равно нулю (один дюйм равен 6 пикам или 72 пунктам, или 2.54 сантиметрам, или 25.4 миллиметрам). Клей между боксами имеет три составляющих:
фиксированная составляющая, х добавляемая (plus) составляющая, у убавляемая (minus) составляющая, z Составляющая х представляет собой "нормальное" значение величины пробела, которая используется в случае, когда боксы скрепляются друг с другом без всяких модификаций, как это имеет место при обработке строки 6; каждая х-составляющая в клее увеличивается путем добавления нескольких у-составляющих. Если же строку надо сжать, тогда каждая х-составляющая уменьшается путем вычитания нескольких z-составляющих.
Например, пусть даны четыре бокса, соединенных друг с другом клеем со спецификацией вида
(xl,yl,zl), (x2,y2,z2), (x3,y3,z3),
тогда расширение строки на величину и достигается использованием интервалов xl + у1 я\ х2 + у2 я\ хЗ + уЗ я\
где и’ = w/(yl+y2+y3). Такого рода расширение невозможно, если и>0 и у1+у2+уЗ=0. Создаваемая система будет настойчиво пытаться уменьшить число растяжений строк, которые будут плохо смотреться, за счет использования довольно интеллектуальной программы выравнивания строк (описывается ниже). При сжатии строки максимальное значение величины ее сокращения равняется сумме всех z-составляющих, попыток * ужать строку еще больше предприниматься не будет. Таким образом, величину z-составляющих надо задавать таким образом, чтобы x-z представляло бы собой минимальное значение диапазона допустимых значений интервала. Для у-составляющей подходящим значением будет такое, чтобы х+у было допустимым, но х+Зу лежало на пределе допустимости. Значения параметров у и z должны быть неотрицательными, однако х может быть и отрицательным (для стирания пробелов (backspacing) и перекрытия фрагментов строк (overlapping)), если соблюдать при этом определенную осторожность.
Обозначение "\hskip 10 pt" в строке 6 означает, что в горизонтальном клее между словами CHAPTER THREE х = 10 pt, у = 0 и z = 0. Если бы эта команда \hskip здесь не присутствовала, использовались бы обычные соглашения для данной ситуации. А именно, клей между CHAPTER и THREE включал бы тогда
х-составляющую, x=w (нормальное значение междусловного пробела для данного шрифта), и у = w/8, z = w/2. Для клея между буквами будет (например) х = 0, у = я/18 и z = w/б; таким образом, растяжение строки без использования \hskip приведет главным образом к увеличению промежутка между словами CHAPTER и THREE, использование же данной команды в виде, показанном выше, обеспечит введение дополнительных промежутков еще и между отдельными буквами. Шрифты, предназначенные для использования их ТЕХ’ом, должны обладать такого рода средствами управления промежутками, понимаемыми как некоторые дополнительные свойства соответствующего шрифта; ТЕХ должен также уметь обнаруживать ситуации типа конца предложений, слегка увеличивая величину пробелов после знаков препинания там, где это необходимо. Символы вроде + и = принято окружать пробелами, тогда как пары символов '' и : = должны стоять вплотную друг к другу; символы + и = не выделяются пробелами в случае, когда они появляются в индексах. Аналогичным образом должны обрабатываться ситуации с лигатурами. Наличие в шрифтах, используемых ТЕХ’ом, такого рода средств делает эти шрифты несколько более "интеллектуальными"; более подробно данный подход будет рассмотрен ниже.
Большинство ТЕХ’овских средств симметрично по отношению к горизонтальному и вертикальному режимам, хотя основная идея, заключающаяся в формировании сначала строк, а уже потом столбцов асимметрична по своей природе и встроена в стандартные программы (в соответствии с тем способом, который мы обычно применяем при письме). Спецификация "\vskip" в строке 7 задает вертикальный клей аналогично горизонтальному клею. Когда программа выравнивания страниц пытается заполнить первую страницу при обработке рассматриваемого нами тестового примера (путем введения разрыва страниц точно в конце абзаца, или в таком месте текста, чтобы на каждой из двух смежных страниц были размещены хотя бы по две строки из разрываемого между страницами абзаца), этот клей будет растягиваться или сжиматься вдоль вертикального измерения, используя значения х = 1 см, у = 30 pt, z = 10 pt. Дополнительные характеристики клея вводятся в определении для ключевого слова \quoteformat — это параметр \lineskip, задающий величину междустрочного интервала, а также параметр \vskips, который задает значение специального дополнительного интервала между цитатой и строкой с именем ее автора. Применительно к основному тексту инструкция для принтера имеет вид "10 на 12", что означает требование пользоваться для печати шрифтом с кеглем 10 pt при значении интервала между соответствующими частями смежных строк, равном 12 pt. ТЕХ будет использовать шрифт кегля 10 pt со спецификацией
\lineskip 12 pt plus .25 pt minus .25 pt так что интерлиньяж будет обладать определенной эластичностью. Дополнительное управление величиной интерлиньяжа задается спецификацией
\parskip 0 pt plus 1 pt minus 0 pt
Кроме того, существуют также интервалы между упражнениями, шаги в алгоритмах и т.д. Определение
\def\yskip {\vskip 3 pt plus 2 pt minus 2 pt} используется для формирования специального вертикального интервала, например, в строках 22 и 37. В строках 23, 37 и т.д. используется горизонтальный интервал вида
\def\xskip {\hskip 6 pt plus 3.5 pt minus 3.5 pt} который вводится в ситуации, когда психологически оправданно наличие интервала большего, чем это определено в качестве нормы. Для этих ситуаций эластичный клей особенно полезен. Пример использования более значительных по размеру горизонтальных отступов, называемых \quad и \qquad, содержится в строке 96; они задают интервал величины "один еш" и "два еш" соответственно, где еш — единица измерения, часто применяемая при типографском наборе математических текстов.
Общность эластичного клея можно оценить, если рассмотреть следующее гипотетическое определение
\def\hfill {\hskip 0 cm plus 1000 cm minus 0 cm}.
В этом случае у имеет практически бесконечное значение (длина, равная 10 м). Когда код вроде \hfill появляется в начале строки, он обеспечивает выравнивание данной строки по правому краю; когда он имеется как в начале строки, так и в ее конце, он обеспечивает центрирование строки; в случае, когда он находится в середине строки, он аккуратно делит строку на части и т.д. Эти свойства эластичного клея обеспечивают средствам ТЕХ’а выравнивания строк превосходство над средствами других систем, поскольку новые свойства вводятся достаточно простым способом и в то же самое время уменьшают необходимость использования устаревших средств применительно к общему случаю.
После того как произведено выравнивание списка боксов, все значения параметров клея фиксируются и полученный объединенный бокс становится неизменяемым ("затвердевает"). Выравнивание осуществляется как по горизонтали, так и по вертикали. Горизонтальное выравнивание длинных строк включает автоматическое выполнение операций переноса; дополнительные сведения относительно операций выравнивания будут приведены в данном отчете несколько позднее.
Снова обратимся к примеру. Строки с номерами 21, 31, 36 и т.д. в нем — это пустые строки, обозначающие конец абзаца. Другой способ показать конец абзаца состоит в использовании команды “\раг”; пример такого использования можно видеть в строке 27. Строка 124 задает конец абзаца, завершающегося выключной формулой. Абзацы начинаются обычным образом; одна из ТЕХ’овских команд, которая будет исполняться после того как обработана строка 17, содержащая "\sectionbegin" ("начало раздела"), это команда \parindent 20 pt задающая абзацный отступ размера 20 pt в первой строке каждого абзаца до тех пор, пока не появится команда "\noindent" ("подавить абзацный отступ"), как это имеет место, например, в строке 37. Программа "\sectionbegin" также задает "\noindent" для самого первого абзаца данного раздела, поскольку в моих книгах такой стиль является стандартным. В строке 23 указывается "\textindent{a)}", что обозначает необходимость создать бокс ширины \parindent, содержащий пару символов а) , за которыми следует пробел, причем эта тройка символов выравнивается по правой границе данного бокса.
В строке 23 "\sl" обозначает "наклонный шрифт". Я решил для выделения слов в тексте пока заменить курсив наклонным вариантом обычного "прямого" шрифта, тогда как имена переменных в математических формулах будут, как это принято, . набираться курсивом. Надо будет еще поэкспериментировать с этим, однако чутье подсказывает мне, что набор так будет выглядеть намного привлекательнее, поскольку буквы из типичных курсивных шрифтов смотрятся не очень красиво среди текста, набранного обычным шрифтом. В любом случае я буду делать различие между наклонными и курсивными шрифтами, поскольку возможны ситуации, когда целесообразно использовать два различных шрифта для выделений, отличающихся по стилю (по каким-либо причинам) друг от друга. Обозначение "\bf" в строке 37 задает набор полужирным шрифтом. Все перечисленные шрифты определяются в команде начала раздела \sectionbegin; определения эти здесь не показаны.
Точки в текстах внутри фигурных скобок в строках 84 и 101 будут выполнены "наклонными"; это позволяет разместить их подходящим образом по отношению к буквам, вслед за которыми они идут, поскольку при переключении с наклонного шрифта на обычный вводится небольшой дополнительный пробел.
Все математические формулы во входном ТЕХ'овском файле содержатся либо между парой символов $...$, как это имеет место, например, в строках 38 и 41, либо между парами $$...$$ (так записываются выключные формулы). Формулы записываются с использованием специального синтаксиса, основывающегося на предложениях Кернигана и Черри (Kernighan and Cherry, Comm. ACM 18, March 1975, 151-157). Например, запись "t9" в строке 41 обозначает верхний индекс 9, а запись “1{п+1}“ в строке 74 -- нижний индекс п+1. Операторы, подобные t и I, задающие структуру математического выражения, активны только в пределах, охватываемых парами $...$. Все буквы в формулах набираются курсивным шрифтом, за исключением тех случаев, когда они относятся к одному из распознаваемых слов или же лежат в пределах фигурных скобок в командах "{\rm ...}", "\hjust..." и т.д. Цифры и знаки препинания, подобные точке с запятой, скобкам и т.п. в математическом режиме берутся из прямого шрифта, если только не задано {\it...}, как это имеет место в строке 127. Символ "Г1 в этой строке будет иметь курсивное начертание, также как и "j", тогда как символы "О" и "?" — обычное прямое начертание.
Расстановка всех пробелов в формулах выполняется ТЕХ’ом, хотя есть возможность добавлять пробелы в случаях, когда с точки зрения автора ТЕХ справился с решением этой задачи не совсем удачно. В строке 116, например, задаются дополнительные пробелы перед "(modulo" и после него при помощи кода "\ "; пробел перед скобками обычно опускается, но в данном случае делать этого нельзя. В завершающих фрагментах рассматриваемого примера много сложных формул, которые будут выглядеть так, как это имеет место в соответствующих местах книги АСР2, откуда взят данный пример. Обработка кода "\eqno 24" (строка 140) приведет к тому, что будет сформирован номер формулы и проставлен в виде "(24)" на правом поле страницы, причем он будет отцентрирован по вертикали по высоте формулы. Так будет сделано, если справа от формулы достаточно места для размещения там номера, если же этого места недостаточно, будет предпринята попытка сдвинуть формулу левее ее первоначально отцентрированного положения, чтобы вставить номер формулы "(24)", в противном случае номер "(24)" будет набран на следующей строке и выключен вправо.
Ключевое слово "\algstep", используемое в строках 41 и 45, обозначает формат записи шага алгоритма:
\def\algstep #1. [#2] {\vskip 3 pt plus 1 pt minus 1 pt \noindent \hjust to 20 pt{\right{#!.}} [#2]\xskip \hangindent 20 pt}
Сначала в этом определении устанавливается значение клея для вертикальных интервалов; эта операция выполняется еще до того, как начинается текст шага алгоритма. Устанавливается также подходящее значение для текстового отступа (textindent) и т.д. В первый раз здесь появляется такое средство, как отрицательный абзацный отступ (hanging indent). Использование этого средства оставляет первую строку абзаца несмещенной (без абзацного отступа), а все остальные строки данного абзаца смещаются вправо на величину абзацного отступа.
Ключевое слово "\exno" (упражнение с номером ... ), используемое в строках 70, 77 и т.д., имеет определение в чем-то похожее на определение шага алгоритма; такие определения форматов для элементов, часто используемых в моих книгах, помогают обеспечить единообразие стиля представления этих элементов на протяжении всей книги. Элемент "\tr" в строке 70 позволяет изобразить треугольник на левом поле страницы. Чтобы этот треугольник был размещен левее крайней левой позиции строки, содержащей номер упражнения, в определении для "\tr" используется клей с отрицательным значением.
В строке 48 начинается выполнение операции вставки бокса 11 \topinsert11 — одного из важных элементов, необходимых при верстке полосы набора. Рассматриваемый бокс, определенный внутри пары фигурных скобок {...}, идущих вслед за ключевым словом \topinsert, формируется как отдельный элемент, который должен быть размещен в верхней части либо текущей страницы, либо следующей за ней. За этим боксом следует вертикальный клей, задаваемый выражением
\topskip 20 pt plus 10 pt minus 5 pt
Это еще один из элементов, подключаемых при обнаружении ТЕХ’ом ключевого слова "\sectionbegin" в тексте обрабатываемого документа. Вставка боксов используется для размещения рисунков и таблиц, в нашем случае — для таблицы.
Заголовок данной таблицы описывается в строках 48-50, сама же таблица (см. с.6 книги АСР2), достаточно сложна, поэтому отложим пока обсуждение содержимого строк 52-65 и рассмотрим сначала более простой пример формирования таблицы (\halign), содержащейся в строках 96-98.
В общем случае команда \halign задает форму вида \halign{ ul#vl ® ... ® un#vn \сг xll ® ... ® xln \сг
xml ® ... ® хшп \сг}
(После команд \сг допускается использовать команды \vskip;s, \hrule, а также команду \eqno из режима набора выключных формул.) В этом примере "\сг" обозначает не carriage-return (возврат каретки), а представляет собой последовательность из трех символов \, с, г. Символы и и v здесь обозначают любые литерные цепочки, не включающие символов #, ® или \сг. Тем самым задается формирование mn горизонтальных списков ("hlists") из боксов
ul{xll}vl ... un{xln}vn
ul{xml}vl ... un{xmn}vn и для каждого к определение максимальной ширины списка hlist uk{xik}vk для i = l,...,m. Затем все боксы uk{xik}vk выравниваются по ширине максимального (самого широкого) бокса, после чего каждая из строк xil®...®xin\cr заменяется горизонтально сцепленной последовательностью модифицированных (выровненных по ширине) боксов; эти строки разделяются горизонтальным клеем, определяемым как \htabskip 0 pt plus 1 pt minus 0 pt.
Если в какой-либо из строк таблицы до появления в ней команды \сг содержится менее п элементов, недостающие элементы ("клетки" таблицы) оставляются пустыми. Когда команда \halign появляется внутри группы, задаваемой парой символов $, каждый из индивидуальных горизонтальных списков uk{xik}vk рассматривается как отдельная независимая формула.
В примере использования команды \halign, содержащемся в строках 96-98, число п столбцов задано как п=4. Первый из столбцов должен быть выровнен по правому краю, второй трактуется как "\rm" (нематематические символы внутри формулы) и окружается пробелами ширины quad, после чего выравнивается по левому краю, третий опять выравнивается по правому краю, четвертый — выравнивается по левому краю. Полученный при выполнении этой команды результат можно видеть на с.9 книги АСР2; сформированная таблица трактуется здесь как формула (1) и номер ее соответственно центрируется по высоте формулы.
Примечание: Со временем я введу в ТЕХ команду "\omittab", позволяющую формировать при необходимости "клетки" таблицы, шириной в несколько столбцов.
Вернемся теперь к строкам 52-65 и сравним их с таблицей 1 на с.6 книги АСР2. При помощи команды \halign и ее "двойника" для вертикального режима \valign здесь формируются два бокса.
Средство "\eqalign" (строки 111, 116) используется для того, чтобы выравнивать выражения, содержащиеся в выключных формулах, расположенных в нескольких строках. На самом деле эта команда представляет собой частный случай команды \halign:
\def\eqalign #l{\halign{right{##}®left{##}\cr#l}}.
Обратите внимание -- строка 113 начинается символом табуляции ® (<ТАВ>).
Команды формирования "больших" математических знаков и скобок ("\group(){...}" в строках ИЗ, 143 и т.д.; "\group/{...}" в строке 153) используются для того, чтобы сформировать в "высоких" формулах круглые скобки "(...)" и знаки типа "наклонная черта" "/..." таких размеров, которые соответствовали бы размерам формулы по вертикали; размеры упомянутых элементов определяются высотой соответствующих боксов (например, бокса, заключенного в скобки). Операции такого рода доступны также для квадратных и угловых скобок ([] и <>), а также для пары знаков "вертикальная черта" (II), при этом левая и правая квадратные скобки увеличиваются командами \[ и \] до размера, который ТЕХ сочтет наиболее подходящим в данной конкретной ситуации. Скобки перечисленных выше видов (включая пару вертикальных линий) могут быть сделаны произвольно большими по размеру, однако на знак "наклонная черта" это не распространяется, по крайней мере в текущей реализуемой версии ТЕХ’а. Пример, в котором будут сформированы очень большие круглые скобки при построении большой матрицы, содержится в строках 158-165.
Команды формирования больших круглых скобок "\biglpren" и "\bigrpren" в строках 118-119 на самом деле дадут не такой уж и "большой" результат, но они будут больше по величине, чем это установлено для обычного их использования, пример которого на основе применения операции \group() дается в строках 122-123.
Знаки суммы, формируемые командой "\sum ..." в строках 131, 143, будут большого размера, поскольку здесь они входят в состав выключных формул, однако, когда формула набирается как часть текстовой строки (т.е. она ограничивается парой символов $...$), величина знака суммы в ней будет меньшей, а пределы суммирования будут набраны как индексы у знака суммы. Аналогично этому будет изменяться и величина знака интеграла, а также дробей "...\over..." и биномиальных коэффициентов (см., например, "$р \choose к$" в строке 118). Подробнее об этом будет сказано ниже.
Команда \eject в строке 79 означает переход к новой странице (текст завершаемой таким образом страницы будет выключен по вертикали вверх). Здесь опять мы имеем дело с одним из элементов стиля оформления моих книг — основные разделы всегда должны начинаться с новой страницы.
Я полагаю, что приведенные выше замечания по примеру набора текста позволяют уловить дух ТЕХ’овского подхода. Этот пример подбирался так, чтобы показать применение разнообразных средств при наборе достаточно сложного текста. В общем же случае набор большинства книг будет представлять собой существенно более рутинное занятие, где только время от времени возникнет потребность в чем-то необычной формуле или таблице. В рассматриваемом примере акцент делался именно на такого рода "необычных" элементах.
Следующий шаг в понимании работы ТЕХ’а состоит в том, чтобы вернуться к низкоуровневому описанию процессов в нем и посмотреть, что происходит с внутренним входным файлом при его чтении. Оказывается, что данный файл
просматривается и преобразуется в последовательность лексем ("tokens"), представляющих собой отдельные литеры или "командные последовательности" ("control sequences"), побуждающие ТЕХ выполнять ту или иную работу. Командная последовательность представляет собой либо символ \ , за которым идет другой символ, не являющийся буквой или ограничителем, либо символ \ , за которым следуют одна или несколько букв (в этом случае командная последовательность завершается с появлением первого же символа, не являющегося буквой.) Например, "\vsize" и "\]" — это командные последовательности; действие, связанное со сменой шрифта, требует последовательности из двух лексем "\:а", состоящей из командной последовательности \: , за которой идет символ а; строка "\ascii,147" состоит из пяти лексем — командной последовательности \ascii, за которой идут символы ’,1,4,7. Если литера, следующая за символом \ , представляет собой букву, и если признаком завершения командной последовательности является пробел, этот пробел не принимается во внимание (будет незначимым) и удаляется из внутреннего входного файла, поскольку единственное его назначение в данном случае — служить признаком завершения командной последовательности. Из этого вытекает, например, что выражения "\yskip \yskip" и "\yskip\yskip" будут эквивалентны друг другу. В строке 125 мне пришлось написать "\М1Х\ " вместо более простого варианта "\М1Х ", чтобы получить две лексемы, \М1Х и "\ "; при такой записи вторая лексема становится уже реальным пробелом. ТЕХ игнорирует также пробел, следующий за идентификационным символом шрифта, введенный ради повышения удобочитаемости ТЕХ’овского текста; например, запись "\:aNow is the time" эквивалентна с точки зрения получаемого результата записи "\:а Now is the time".
Если командная последовательность состоит из \ и какого-либо еще одного отображаемого символа, учитываются различия между этими символами, но если такая последовательность включает помимо символа \ также и цепочку (строку) из букв, то в этом случае не делается различия между буквами в верхнем и нижнем регистре (т.е. между "прописными" и "строчными" буквами), за исключением самой первой буквы в этой строке. Это значит, что командные последовательности "\GAMMA" и "\Gamma" будут считаться идентичными, но не совпадающими с командой "\gamma". Более того, буквенные последовательности считаются разными, если они различаются по первым семи буквам, входящим в них (по шести буквам, если ТЕХ реализован на машине с 32-разрядными двоичными словами), или же если они имеют различное значение параметра length mod 8 (остаток от деления длины последовательности на 8). Например, обе командные последовательности "\qUotefOmmmm" и "\quOTEfoxxxxxxxxxxxx" эквивалентны команде "\quoteformat". Вследствие этого, общее число различных командных последовательностей, которые можно определить, составляет примерно 128-14 + 2*(26~2 + 264 + 264 + 264 + 264 + 8*264)
и этого явно должно быть достаточно.•
Командная последовательность будет, разумеется, считаться неверной до тех пор, пока ТЕХ’у не станет известно ее определение. В ТЕХ встроены некоторые командные последовательности-примитивы, наподобие "\vsize", "\ " и "\def", а также средства определения макрокоманд (это команда \def), позволяющие научить ТЕХ новым командным последовательностям (или заставить его забыть какие-либо из существующих команд), вроде "\М1Х" и "\quoteformat".
Дадим теперь точные правила, которым следует ТЕХ, преобразуя внутренний входной файл в "чистый ввод", состоящий из лексем. В "чистом вводе" каждая командная последовательность является примитивом, отличным от "\def" и "\require". Выше уже упоминалось, что команда \require просто вводит часть входного текста из некоторого другого файла. Для команды \def в общем случае можно записать
\def <ctrl-seq><stringO>#Kstringl> ... #k<stringk>{<right-hand side>}
где <stringO> ... <stringk> -- последовательности из нуля или более литер, кроме литер { , } или #; пробелы в этих строках являются значимыми. Исключением будет случай первого символа после определенной командной последовательности, который игнорируется, если он представляет собой пробел-ограничитель, за которым следует буквенная строка. Фрагмент <right-hand side> приведенного выше определения представляет собой любую последовательность из символов, включающую сбалансированные пары скобок { и }; пробелы здесь опять будут значимыми. Я описываю сейчас общий случай; в простых ситуациях наподобие определения командной последовательности \rtn в строке 141, к равняется нулю, a <stringO> — пустая строка. Значение параметра к должно быть <9.
Когда обнаруживается командная последовательность с именем <ctrl-seq>, позднее, при обработке ввода, запускается процесс сопоставления, продолжающийся до тех пор, пока не будут найдены соответствия для всех элементов из левой части определения: символы в строках stringO...stringk должны точно соотноситься с соответствующими символами во входном тексте; если в процессе сопоставления обнаружится ошибка, будет выдано диагностическое сообщение. По завершении процесса сопоставления ТЕХ ищет те элементы ("ингредиенты"), которые должны быть подставлены вместо параметров #1,...,#к в правой части определения. Это выполняется следующим образом. Если строка <stringj> пуста, значением для параметра #j будет следующий одиночный входной символ, или (если этим символом окажется "{") следующая группа символов вплоть до соответствующей закрывающей скобки "}". Если же строка <stringj> не пустая, значением параметра #j будет О или более символов или группы {...} вплоть до следующего входного символа, равного первому символу в строке <stringj>. Никаких операций макрорасширения во время данного процесса сопоставления левой и правой частей определения не выполняется, точно так же, как не делается никаких попыток восстановления, когда обнаруживается ошибка; последующие символы входной строки должны отвечать оставшимся символам строки <stringj>. Если параметр #j включает изолированную группу {...}, внешние скобки { и } из данной группы удаляются. Отметим, что процесс сопоставления имеет дело с символами из внутреннего входного файла, не с лексемами; вполне возможно, например, что значением параметра #j станет единственный символ-ограничитель вроде "\".
После того, как путем применения описанных выше правил найдены значения для параметров #1 ... #к, они просто копируются в позиции, занимаемые этими параметрами в правой части определения. И еще одно изменение будет сделано в правой части определения: из любой последовательности, содержащей два или более символа #, идущих подряд, удаляется первый символ #. Это делается для того, чтобы можно было записывать определения в правой части без риска перепутать их с текущим определением.
В качестве примера рассмотрим, что произойдет, если активным будет определение вида:
\def\A#l ВС {\def\E ##1{##1#1 #1}}.
Если внутренний входной файл содержит \А {Х-у} ВС D то результирующая последовательность после раскрытия определения примет вид \def\E #1{#1Х-у X-y}D
(обратите внимание на расстановку пробелов).
Изложенное выше не представляет собой наиболее общего подхода к формированию макроопределений -- я здесь предпочитаю следовать принципу "бритвы Оккама". Если теперь читатель вернется несколько назад и посмотрит на определения, сформированные для ключевых слов \quoteformat, \algstep и \eqalign, то увидит, что все они и в самом деле просты.
Действия присваивания. Я упоминал уже, что "чистый ввод" содержит коды для действий-примитивов, которые могут быть выполнены, а также символы, передаваемые в формируемый итоговый документ. Часть из этих действий — это просто действия присваивания, которые устанавливают значения параметров, сообщающих ТЕХ’у, каким образом следует осуществлять преобразования следующего за ними входного текста. Подобно макроопределениям, действия присваивания имеют силу только внутри текущей группы, задаваемой парой фигурных скобок {...}, парами символов $...$ или $$...$$, или же до момента их явного переопределения.
Дадим список ТЕХ’овских действий присваивания: \chcode,<octal><-<number>определяет основной ограничитель
\deffnt <charXfilename>фактическое имя шрифта, соответствующее
его псевдониму (nickname)
\:<char>
текущий используемый шрифт
\mathrm
шрифт, который следует использовать для набора имен математических функций типа log
\mathit
шрифт, который следует использовать для набора переменных типа х
\mathsy
шрифт, который следует использовать для набора математических символов типа \mu
\ragged или \justifiedвид правого поля
\hsize <length>
нормальная ширина строк формируемого текста
\vsize <length>
нормальная высота страниц формируемого текста
\parindent <glue>
величина отступа для первой строки абзаца
\hangindent <glue> величина отступа для всех строк абзаца, кроме . первой (этот параметр принимает нулевое значение при каждом выходе из абзаца)
Mine skip <glue>
вертикальный промежуток между линиями шрифта формируемых строк текста
\parskip <glue>
дополнительный промежуток между абзацами (добавляется к lineskip)
\dispskip <glue>
дополнительный промежуток перед выключной формулой и после нее
\topskip <glue>
дополнительный интервал снизу для бокса, вставляемого в верхней части страницы
\botskip <glue>
дополнительный интервал сверху для бокса, вставляемого в нижней части страницы
\htabskip <glue>
горизонтальный промежуток между столбцами, табулируемыми с помощью команды \haligned
\vtabskip <glue>
вертикальный промежуток между столбцами, выравниваемыми с помощью команды \valigned
\output <routine>
то, что делается с заполненными страницами*
Все эти числовые параметры имеют значения по умолчанию, которые будут выбраны, исходя из опыта использования ТЕХ’а; эти значения будут активными до тех пор пока не будут в явном виде переопределены. В приведенном выше перечне <length> ("длина") имеет вид <number><unit>или-<number><unit>
где <number> ("число") — строка цифр, содержащая или не содержащая точку (десятичную точку), a <unit> ("единица измерения") может принимать значения pt, pc, in, cm или mm. Далее, <glue> ("клей") определяется выражением <length> plus <length> minus <length>
где фрагменты plus и minus не могут быть отрицательными; любая из трех перечисленных частей-длин может быть опущена или быть равной нулю, до тех пор пока хотя бы одна из трех имеется в наличии. Пробел после <number> или <unit> из ввода удаляется.
Например, в соответствии со стандартными соглашениями, принятыми для принтера XGP, вывод производится на страницу размера 8 1/2 на И дюймов с левыми и правыми полями по 1 дюйму, междустрочное расстояние 4 пикселя; для шрифта высотой в 30 пикселей все эти требования можно описать так:
\hsize 6.5 in \vsize 9 in \lineskip .17 in
Это можно было бы это записать также следующим образом: \def\hmargin{l in}\def \vmargin{l in}
чтобы воспользоваться преимуществами, которые по умолчанию дает ТЕХ’овская программа \output. (Объяснения, связанные с этой программой я приведу ниже.)
Когда я буду расширять ТЕХ, то включу в него дополнительное действие присваивания \tempmeas <length> next <number> lines
с тем, чтобы ТЕХ получил возможность устанавливать сжатый набор (narrow measure), необходимый для включения небольших иллюстраций (см. том 1 книги АСР, с.52).
Чтобы избежать конфликтов между программами формирования страниц и абзацев, были внесены изменения в команды \hsize и \vsize, которые проявятся только в случае, если ТЕХ размещает первую строку абзаца с новой страницы или же первый элемент текстового фрагмента с нового абзаца.
На действия присваивания \chcode и \deffnt не распространяются "правила видимости" команд, оба они имеют "глобальный" эффект.
Структура управления. Теперь самое подходящее время рассмотреть ТЕХ’овские механизмы формирования абзацев и страниц, а также ряд других вопросов, связанных с ТЕХ’овской структурой управления. На самом деле следовало уже давно это сделать, тогда и вы и я смогли бы избежать большого числа недоразумений.
Внутренние механизмы работы ТЕХ’а имеют дело с боксами и объектами, именуемыми "hlists" (которые представляют собой горизонтальные списки боксов, разделенных горизонтальным клеем) и "vlists" (вертикальные списки боксов, разделенных вертикальным клеем). Списки этих двух видов нельзя смешивать и ТЕХ в каждый момент времени должен знать, формирует ли он список типа hlist или список типа vlist.
Мы говорим, что ТЕХ находится в "горизонтальном режиме" ("horizontal mode"), если он работает с объектом типа hlist — интуитивно, когда он находится в середине строки — в противном случае он будет в "вертикальном режиме" ("vertical mode"). Подходя более формально, будем писать "h..." для корректного ТЕХ^овского ввода, начинающегося в горизонтальном режиме, и "v..." для корректного ТЕХ'овского ввода, начинающегося в вертикальном режиме. Действия присваивания не оказывают влияния на вид режима, поэтому в данном обсуждении мы их упоминать не будем.
Когда ТЕХ находится в вертикальном режиме, в качестве следующей лексемы из "чистого ввода" (не считая действий присваивания) должна использоваться одна из перечисленных ниже: v... =
\vskip <glue> v...
(вертикальный клей, присоединенный к текущему списку vlist) \ljustline{h...}v...,\ctrline{h...}v..., или \rjustline{h.. .}v...
(присоединить бокс ширины \hsize к текущему списку vlist) \hrule [height <length>] [width <length>]
(присоединить горизонтальную линейку к текущему списку vlist, она напоминает целиком закрашенный черным прямоугольник, для
которого по умолчанию высота (height) равна 0.5 pt, а ширина (width) равна окончательной ширине данного списка vlist, а именно, от левой границы его самого левого бокса до правой границы его самого правого бокса)
\moveleft <length> v... или \moveright <length> v...
(следующий бокс в текущем списке vlist, который надо сдвинуть влево или вправо на заданную величину, действует только на один бокс)
\topinsert{v...}v... или \botinsert{v...}v...
(вставляет список vlist в текущую страницу, или перемещает его на следующую страницу, если он не может быть размещен на текущей)
\halign{...}v...
(возвращает вертикальный список vlist из ш боксов, сформированный из mn горизонтальных списков hlist как это было описано выше; список vlist присоединяется к текущему вертикальному списку)
\tор{v...}v..., \mid{v...}v..., или \bot{v...}v...
(используется в основном в команде \valign, возвращает список vlist, выровненный с помощью команды \vjust по верхнему краю, отцентрированный по вертикали или выровненный по нижнему краю страницы, соответственно)
*\mark{... }v...
(связывает метки-заголовки со следующими строками текста)
\penalty <number>v...
(добавочные единицы плохости, если разрыв страницы будет произведен в этом месте)
<blank space>v... или \ v... или \par v...
(игнорируется)
\noindent h...
(начинает абзац с первой строкой без отступа)
<box> h...
(начинает абзац с отступом в его первой строке, начальным боксом абзаца будет указанный <Ъох>)
\eject v...
(завершает текущую страницу, если только она не пустая)
*\eqno(<number>)V...
(добавляет справа от выключной формулы соответствующий ей номер уравнения)
Здесь звездочка * обозначает опции, которые будут правомерными, только если текущий список vlist обрабатывается подпрограммой формирования страниц, а двойная звездочка ** -- опцию, допустимую только в режиме формирования выключных формул. Подпрограмма формирования страниц будет активной в начале работы программы в целом, но не внутри других программ (например, программы, реализующей команду \topinsert), связанных с формированием списков vlist.
Спецификация бокса может иметь одну из следующих форм:
<Ъох> =•
<nonblank character>
(бокс, состоящий из этого символа, отличного от пробела (nonblank character), взятого из текущего шрифта)
\ascii'<octal>
(эквивалентно символу, который может быть очень затруднительно ввести каким-либо другим способом)
\hjust to <length>{h...}
(список hlist преобразуется в бокс заданной длины <length>, причем, если это потребуется, то с разрывом его на несколько строк, как при формировании абзаца)
\hjust{h...} или \hexpand <length>{h...}
(список hlist преобразуется в бокс с его естественной длиной, плюс заданная длина.. .здесь действие команды \hjust аналогично действию команды \hexpand 0 pt)
\vjust to <length>{v...}
(список vlist преобразуется в бокс заданной длины, в этом случае, к сожалению, без возможности разбиения его на части)
\vjust{v...} или \vexpand <length>{v...}
•(список vlist преобразуется в бокс с его естественной высотой
плюс заданное значение ... здесь команда \vjust действует аналогично команде \vexpand 0 pt) \page
(данная страница полностью завершена; разрешается использовать только в программе \output)
\boxO, \boxl, ... \box9
("глобальный" бокс самым последним по порядку сохраненный командами \saveO,...,\save9)
Бокс при его чтении командами \page и \boxk разрушается, не копируется, поэтому следующая попытка его прочитать вызовет появление сообщения об ошибке. Программы построения боксов и программы \output имеют право использовать команду с именем \savek (0<к<9), чтобы сохранить некоторый бокс в одной из десяти глобальных областей сохранения; и опять, этот бокс не копируется, он формируется из текущего списка hlist или vlist, после чего текущий список опустошается.
Когда ТЕХ находится в горизонтальном режиме, в качестве следующей лексемы из "чистого ввода" (не считая действий присваивания), должна использоваться одна из перечисленных ниже: h... =
\hskip <glue> h...
(горизонтальный клей, присоединенный к текущему списку hlist) \tjustcol{v...}h...,\midcol{v...}h..., или \bjustcol{v...}h...
(присоединить бокс высоты \vsize к текущему списку hlist) \vrule [height <length>] [width <length>]
(присоединить вертикальную линейку к текущему списку hlist, это аналогично команде \hrule, но по умолчанию ширина (width) для нее равна 0.5 pt, а высота (height) равна высоте списка hlist; если значение высоты задано, линейка выполняется соответствующего размера, при этом она начинается от линии шрифта)
\raise <length> h... или \lower <length> h...
(следующий бокс или линейку в текущем списке hlist надо сдвинуть вверх или вниз относительно линии шрифта на заданную величину, действует только на один бокс)
\topinsert{v...}h... or \botinsert{v...}h...
(вставляет список vlist в точности на ту страницу, которая содержит предыдущий бокс в текущем списке hlist, это может быть, например, некоторое примечание)
\valign{...}h...
(возвращает горизонтальный список hlist из ш боксов, сформированный из mn вертикальных списков vlist как это было
описано выше; список hlist присоединяется к текущему горизонтальному списку)
\left{h...}h..., \ctr{h...}h..., или \right{h...}h...
(используется в основном в команде \halign, возвращает список hlist, выровненный с помощью команды \hjust по левому краю, отцентрированный по горизонтали или выровненный соответственно по правому краю страницы,)
\penalty <number>h...
(добавочные единицы плохости, если разрыв строки будет произведен в этом месте)
<blank space>h...
(переменный клей для междусловных промежутков в данном текущем шрифте; в математическом режиме игнорируется)
\ h...
(то же самое, что и пробел (blank space), но не игнорируется в математическом режиме)
<box> h...
(в частности, символ, отличный от пробела...данный бокс присоединяется к текущему списку hlist)
$...$ h...
(список hlist, определенный в математическом режиме, присоединяется к текущему списку hlist)
*$$...$$ h...
(разрывает текущий абзац, обрабатываемый программой формирования страниц, но значение параметра \hangindent не обнуляется...абзац продолжается после завершения фрагмента, ограниченного парами символов $$... внутри этого фрагмента содержится математическая формула или набор таких формул, задаваемых командами \eqalign или \halign, они будут отцентрированы и присоединены к списку vlist, обрабатываемому программой формирования страниц согласно условиям, принятым для выключных формул...этой программе передается также соответствующее значение вертикального клея...дополнительный клей над выключным уравнением (\dispskip) не добавляется, если текст предшествующей строки абзаца не "накрывает" (overhang) первую из выключных формул в их рассматриваемом наборе после центрирования)
\par v... или <два последовательных символа возврата каретки во внешнем входном файле> v...
(конец абзаца, текущий список hlist разбивается на строки способом, рассматриваемым ниже; полученные строки присоединяются к списку vlist, обрабатываемому программой формирования страниц)
\eject h...
(завершает текущий "абзац" и текущую страницу, но последняя строка текущего "абзаца" выравнивается, как если бы она была в середине абзаца; текст продолжается в новом "абзаце" без абзацного отступа в его первой строкеи без обнуления параметра \hangindent)
Здесь звездочка * обозначает опции, которые будут правомерными, только если текущий список hlist обрабатывается подпрограммой формирования абзацев, вызываемой из программы формирования страниц так, как это было описано выше.
Боксы имеют точку привязки на их левой границе, используемую при склеивании двух боксов. Если бокс представляет собой просто некий символ, взятый из некоторого
шрифта, тогда точка привязки для него будет находиться на пересечении левой границы бокса, содержащего символ, с линией шрифта (т.е. в нижней части букв, подобных х, но не у; в последнем случае бокс будет продолжен вниз, под линию шрифта, чтобы разместить нижнюю часть буквы). Когда боксы сцепляются по горизонтали, их линии шрифта выстраиваются в одну прямую линию (если только не используются команды \raise или \lower, требующие нарушения этого правила). Запоминаются также значения максимальной высоты над линией шрифта и максимальной глубины под ней, чтобы определить высоту получаемого бокса. Если боксы сцепляются по вертикали, в одну прямую линию выстраиваются их левые границы (если только не используются команды \moveright или \moveleft, требующие нарушения этого правила). Расстояние между линиями шрифта в их последовательности задается параметром \lineskip в сочетании с вертикальным клеем, определяемым командами \vskip, \parskip и т.д., пока это не приводит к перекрытию (взаимному наложению) боксов; в последнем случае боксы будут помещены друг на друга с нулевым клеем. В качестве линии шрифта результирующего бокса будет принята линия шрифта самого нижнего бокса в нем. Максимальное расстояние до правой стороны линии привязки определяется шириной результирующего бокса.
Значение числового параметра \lineskip перед и после команды \hrule не принимается во внимание. Тогда, например, можно записать \vskip 3pt \hrule \vskip 2pt \hrule \vskip 3pt
чтобы получить двойную горизонтальную линейку с промежутком между линейками, равным 2 pt, интервалом между линейкой и соседней строкой, равным 3 pt, независимо от текущего значения параметра \lineskip.
Если два стоящих рядом элемента списка hlist — просто символы, взятые из одного и того же шрифта, ТЕХ просматривает таблицу, связанную с данным шрифтом, чтобы определить, не имеет ли место случай, когда пару смежных символов надо заменить на специальный символ. Например, для некоторых из моих стандартных шрифтов, определены следующие подстановки:
ff
•4
<ff>
fi
■4
<fi>
fl
•4
<fl>
<ff>i
■4
<ffi>
<ff>l
•4
<ffl>
•4
<<(>
•4
<„>
—
•4
<en-dash>
<en-dash>-
•4
<em-dash>
Для первых семи комбинаций я буду использовать коды 41, 42, 43, 44, 45, 475, 477, поскольку на данном этапе ТЕХ не перепутает их с базовыми ограничителями. (Другие возможные варианты такого рода комбинаций — это := -» <:=> и, для эксклюзивных подарочных изданий, где используются символы "под старину" — лигатуры для ct и st.)
Обратим внимание на такие элементы приведенной выше таблицы подстановок, как en-dash (короткое тире) и em-dash (длинное тире); на самом деле в математических публикациях используются знаки подобного рода четырех различных видов: дефис (для обозначения разрыва слов при переносе), короткое тире (для диапазонов типа "13—20"), знак минуса (для обозначения операции вычитания), длинное тире (для обозначения тире как такового).
Эти знаки в ТЕХ’овском наборе соответственно имеют вид -, —, - (только внутри фрагментов, ограниченных парами символов $), —.
Приведенные выше правила для v... и h... суммируют сведения для большей части ТЕХ’овских команд, за исключением действий присваивания, рассмотренных ранее, а также операций, связанных с выводом страниц и с работой в математическом режиме.
Рассмотрим теперь текст программы \ACPpages, осуществляющей вывод сложных страниц. Этот код использует "переменные" \tpage и \rhead, не являющиеся частью ТЕХ’а. Я занимаюсь сейчас реализацией ТЕХ’овских макросредств, которые давали бы возможность "присваивать" значения этим символам. Читателей, не привыкших к подобного рода "фокусам", они, наверное, позабавят, но я не считаю их такими уж ужасно неэффективными, поскольку страницы все-таки выводятся сравнительно редко.
\def \titlepage {\def \tpage{T}} 7. присваивает \tpage значение Т ("true") \def \runninglefthead#l {\def \rhead{{\:m#l}}} \def \runningrighthead#l section#2 {\mark
{\ifeven{\hjust to .375 in {\left{\cpage}}\left{\rhead}#2} \else{#2\right{\:m#l}\hjust to .375 in {\right{\cpage}}}} \def \ACPpages starting at page #1: {\setcpage #17, устанавливает номер текущей страницы для
следующей страницы
\output(Mineskip 12 pt 7. запускает программу output, сбрасывает \lineskip \vskip \vmargin7» пропускает верхнее поле (\vmargin определяется
пользователем)
\ifT \tpage7. следующий текст используется, если значение \tpage
равно Т
{\def \tpage{F} 7. возвращает переменную \tpage в исходное состояние \topline 7. специальная строка, задаваемая пользователем, для
вывода в верхней части титульных страниц \moveright \hmargin 7. скорректировать левое поле \ljustline{\page} \vskip 3 pt \moveright \hmargin
\ljustline{\hjust to 29 pc{\:c \ctr{\cpage}}}} 7. центрировать номер
7. в нижней части страницы \else {\moveright \hmargin 7. этот формат используется, если \tpage > Т \ljustline{\hjust to 29 рс{\:а \ifeven{\topmark}\else{\botmark}}} \vskip 12 pt \moveright \hmargin \ljustline{\page}}
\advcpage}7» увеличить номер текущей страницы на 1
Всякий раз когда программа формирования страниц завершает подготовку очередной * страницы, активизируется подпрограмма \output. ТЕХ в данном случае находится в вертикальном режиме, значения параметров \hsize, \lineskip и т.п. непредсказуемы, так что надо вернуть их в исходное состояние ("сбросить"). Бокс, определяемый вертикальным списком vlist, сформированным программой \output, и будет представлять собой требуемый вывод, если только он не будет пустым (например, если он был уже сохранен с помощью команды \save).
К числу ТЕХ’овских действий, использовавшихся в приведенном выше тексте программы и пока еще не получивших объяснения, относятся:
\setcpage <number> Устанавливает значение номера текущей страницы равным заданному целому числу <number>; если это число отрицательно, то номер набирается римскими цифрами.
\advcpage
Увеличивает абсолютное значение номера текущей страницы на единицу.
\cpage
Символьная строка, отвечающая значению номера текущей страницы, которая вставляется в ТЕХ’овский ввод как десятичное число с удаленными головными нулями (leading zeroes) или как число, записанное римскими цифрами (в нижнем регистре);
\ifeven{a}\else{p}
Использует а, если номер текущей страницы четный (even), в противном случае использует р (ТЕХ’овская сканирующая программа за каждый такт обрабатывает один символ).
\ifT <char> {a}\else{p} Использует a, если <char> равно T, в противном
\topmark, \botmark
случае использует р.
Операция \mark связывает неинтерпретируемую символьную строку (маркер) с набором последующих строк текста формируемого документа, полученным программой формирования страниц, это связывание будет действительным до следующего маркера; команда "\topmark" вставляет в ТЕХ’овский ввод маркер, связанный с первой строкой текущей страницы \page, а "\botmark" -связывает с последней строкой, не считая любых строк текста, добавленных командами \topinserted или \botinserted.
Я предлагаю использовать следующие ниже команды в качестве значении по умолчанию в ТЕХ'овской программе output. В этих командах используются дополнительные пять действий, а именно, \day, \month, \year, \time и \file, отвечающие среде, из которой вызывается ТЕХ.
\def \hfill{\hskip plus 100 cm} \lineskip 0 pt
‘/"бесконечная" растяжимость
‘/обнулить ("сбросить") промежуток между ‘/строками
\:\font
‘/вернуться к символу, принятому по ‘/умолчанию
\vskip \vmargin
\ifT \notitle {} \else{
‘/пропустить верхнее поле
‘/пропустить заголовок, если выполнено ‘/условие
\ljustline{\hjust to 7.5 in{
‘/заглавная строка, содержит
‘/номер страницы, отстоящий на 1 дюйм ‘/от правой границы страницы
\hskip\hmargin
\day\ \month\ \year\hfill
\time\hfill
\file\hfill
\cpage}}
\vskip 12 pt}
\moveright \hmargin \ljustline{\cpage}
‘/пропустить левое поле
•/дата
‘/время начала (стартовое время)
‘/имя основного входного файла
‘/номер страницы
‘/сдвиг на 1 пику после заголовка
‘/вставить тело страницы, пропуская
‘/левое поле
\advcpage
‘/увеличить номер страницы
Значения параметров \hmargin, \vmargin, \font и \notitle могут устанавливаться пользователем (или SNAIL), все они также имеют значения по умолчанию.
Рассмотрим теперь более подробно программу формирования страниц. Это даст возможность изучить ТЕХ’овский процесс выравнивания по вертикали, вводящий ряд концепций, необходимых при реализации более сложной программы, используемой для выравнивания последовательностей боксов по горизонтали.
Первая из реализуемых идей — это концепция "плохости" (badness). Она представляет собой число, определяемое на основе совокупности растяжений и сжатий, необходимых при установке значения клея. Пусть имеется список из п боксов (горизонтальный или вертикальный список), разделенных п-1 спецификациями клея. Пусть я — требуемая полная длина списка (т.е. требуемая ширина или высота, в зависимости от того, как выполняется выравнивание -- по горизонтали или по вертикали); пусть х -- фактическая общая длина боксов и клея, и пусть y,z — общее число параметров клея для растяжения и сжатия. Плохость в этой ситуации будет такой: бесконечная, если х > w - z + Е, где Е -- малый допуск (tolerance) для компенсации ошибок округления в операциях с плавающей точкой;
100((x-w)/z)~3, если w-z+E>x>w; О,если х = w;
100((w-x)/Зу)^3, если я > х; плюс штрафы, накладываемые за нарушение границ в нежелательных местах.
Согласно этим формулам, растяжение на у имеет показатель плохости, равный 100/27, или примерно 3.7; растяжение на 2у повышает это значение примерно до 30; растяжение на Зу — до 100 единиц плохости, и столько же при сжатии на максимальную величину, задаваемую параметром z. Я планирую установить величину штрафа примерно в 80 единиц для такого разрыва абзаца или последовательности выключных формул, когда на одной из страниц остается единственная строка абзаца (формулы). Тогда, к примеру, пятистрочный абзац даст 80 единиц штрафа, если разрыв будет произведен после первой или четвертой его строки, но штраф будет нулевым, если разрыв устанавливается после второй или третьей строки. Эти формулы надо будет еще, конечно, "подрегулировать", чтобы результат, даваемый ими, соответствовал моим эстетическим принципам, определяющим плохую верстку. Пользователь будет иметь возможность задавать свои собственные дополнительные штрафные точки для предотвращения нежелательных разрывов между определенными строками (например, в программе MIX для предотвращения разрыва перед командой, ссылающейся на *-1).
Не допускаются разрывы страницы перед линейкой и после нее.
Программа формирования страниц строит вертикальный список vlist так, как это . было описано выше, накапливая строки текста документа и вертикальный клей до тех пор, пока суммарная высота ранее накопленных строк и клея и к вновь введенных строк станет равной предписанной высоте страницы (\vsize), или же превысят это значение. Затем данная программа производит разрыв абзаца, введенного последним, после j-й строки, для 0<j<k, при этом значение j должно минимизировать величину показателя плохости; если искомый минимум достигается более чем при одном значении j, то берется наибольшее из них. После этого клей g(j+l) отбрасывается, а оставшиеся k-j строк переносятся на следующую страницу. Они тут же проверяются -не вызовут ли переполнения новой страницы, и если да, то производится их разрыв точно таким же образом, как это было описано выше. Программа \output вызывается независимо от того, сформирована уже целиком заполненная страница или нет.
Этот довольно простой процесс может прерываться процедурой \topinsert или \boxinsert, вставляющей боксы. Вычисление данного вставляемого бокса
осуществляется автономно, вне процессов формирования абзацев и страниц, после чего предпринимается попытка разместить его на текущей собираемой странице. Если удается сделать это с оценкой "отлично" или "хорошо", вставляемый бокс оставляется на данной странице. Если не удается — вставляемый бокс переносится на следующую страницу; эти действия будут выполняться вполне естественным образом, объяснить который, однако, не очень просто. Эти действия выполняются до тех пор, пока не будет удовлетворено требование о размещении вставляемого бокса на той же самой странице, что и заданная строка (т.е. осуществляется вставка бокса в горизонтальном режиме). Затем используется наименее плохое из имеющихся допустимых решений.
Подстрочные примечания (сноски): на протяжении более чем 2000 страниц книги The Art of Computer Programming я сделал лишь три подстрочных примечания. Мое личное мнение -- использование сносок должно быть редким исключением, поэтому я не предполагаю разрабатывать достаточно изощренный механизм формирования подстрочных примечаний (в частности, разделение длинных сносок между несколькими страницами, или маркировку первой сноски на данной странице звездочкой, а второй -- крестиком и т.п.). Тем не менее, сносками можно вполне удовлетворительно управлять с помощью команды \botinsert, как определено здесь. Пользователь может получить возможность формировать и более причудливые сноски, если только он не против того, чтобы переписать несколько ТЕХ’овских программ.
Программа формирования абзацев производит сборку горизонтального списка hlist способом, описанным выше. Она должна разделить этот список на строки шириной \hsize для передачи их в программу формирования страниц. (Примечание: имеется лишь единственная программа формирования страниц, несмотря на то, что ТЕХ большей частью работает рекурсивно, так же, как и единственная программа формирования абзацев. Однако в активном состоянии одновременно может быть произвольно много программ \hjust, осуществляющих горизонтальное выравнивание до длины <length>, это в значительной мере аналогично тому, что имеет место для программы формирования абзацев, поскольку она также должна разбивать списки hlist на строки текста формируемого документа. Обсуждение механизма разрыва строк имеет отношение также и к этим программам, но для удобства я буду писать об этом процессе так, как если бы за него отвечала только программа формирования абзацев.)
Элементы списка hlist, с которыми работает программа формирования абзацев, обычно представляют собой последовательности текстовых символов или фрагментов математических формул, однако возможен также случай, когда они будут неделимыми боксами, созданными ТЕХ^овскими высокоуровневыми операторами работы с боксами. В моих шрифтах предусмотрено небольшое количество переменного клея между отдельными текстовыми символами (между буквами а и Ь, например, можно было бы использовать клей, полученный как сумма "правого клея" для а и "левого клея" для Ъ, как это указано в шрифтовых таблицах); более того, пробелы между словами имеют более эластичный клей, как это уже объяснялось ранее. ТЕХ дает двойной у-клей первому пробелу (однако не будет изменять х-клей), который идет после точки или двоеточия, восклицательного или вопросительного знака, если только перед этим пробелом не вклинятся буквы, цифры, запятые, точки с запятой или боксы. Точка с запятой и запятая обрабатываются аналогичным способом, только значения компоненты у клея для них устанавливаются равными 1.5 и 1.25 соответственно.
Основная задача, которую решает программа формирования абзацев, состоит в том, чтобы определить, каким образом разделить длинный список hlist. И опять здесь ТЕХ опирается на концепцию "плохости", обсуждавшуюся выше применительно к программе формирования страниц, но здесь данная концепция используется в несколько усовершенствованном виде за счет введения средства "просмотра вперед"
(lookahead), с помощью которого ситуация в последующих строках абзаца может оказывать влияние на разрывы строк в его предыдущих строках. На практике это часто приводит к получению существенно более высокого качества выходного текста, чем это могут обеспечить другие программы.
Перед тем как начать обсуждение средств просмотра вперед, надо определить все места допустимых переносов слов. Каждая команда \hskip, для которой компоненты х или у клея превышают ширину пробела для текущего шрифта, является допустимым местом для разрыва строки (и для того, чтобы удалить горизонтальный клей) без наложения штрафа. Смежные команды \hskips объединяются, в данном случае -- путем добавления трех компонент клея. Еще одно такое допустимое место для разрыва без штрафа — после явного знака переноса слова или тире. (Некоторые команды \hskip, используемые для уменьшения пробела (backspacing), имеют отрицательное значение х; они, конечно, недопустимы для организации разрыва строки.) Программа формирования математических формул, обрабатывающая последовательность литер, заключенную внутри пары символов $...$, разрешает разрывы строк сразу после знаков бинарных операторов и отношений на верхнем уровне; для знаков отношений типа =, <, = и т.п. устанавливается совсем небольшой штраф, например, 10; для операторов типа +,-,x,/,mod этот штраф будет несколько больше, причем для - и mod он будет больше, чем для остальных из этого перечня (например, 30, 70, 30, 40, 80 соответственно). Верхние и нижние индексы, аргументы функций и тому подобные элементы должны присоединяться к своим боксам неразрывно.
Имеется четыре вида символов типа "discretionary", используемых для того, чтобы обеспечить или запретить разрывы. Первый из них — это команда \penalty <number>, которая указывает, что разрыв строки будет допустимым, если не будет превышено установленное значение штрафа. Три других вида этих символов включают:
\- данное слово можно перенести в этом месте (штраф 30 единиц); \+ перенос в этом месте не выполнять;
\* в этом месте можно выполнить перенос (штраф 30 единиц), но вместо знака переноса вставить знак умножения.
Последний из показанных выше трех вариантов может понадобиться для длинных произведений наподобие $(n+l)\*(n+2)\*(n+3)\*(n+4)$.
Теперь я расскажу о подходе, принятом в ТЕХ^е для выполнения автоматической расстановки переносов. Но перед этим представим на минуту, что все подходящие места для разбиения строк уже известны; каким образом тогда сделать так, чтобы расстановка переносов была наилучшей с точки зрения абзаца в целом? Я думаю, что наиболее приемлемым определением понятия "наилучший" здесь будет такое, при котором минимизируется сумма квадратов плохостей всех выполненных индивидуальных разбиений. Это должно привести к минимизации максимальной плохости, а также к аналогичному поведению плохостей порядка второго, третьего и т.д. Как и до сих * пор, понятие плохости базируется здесь на количественной мере растяжения или сжатия строки, плюс штрафные "очки".
Чтобы найти наилучшие точки разбиения по этому критерию, нам не надо решать задачу экспоненциальной сложности. Для нахождения абсолютно наилучшего способа разбиения за время 0(п~2), где п -- число разрешенных точек разбиения строки, можно использовать алгоритм динамического программирования. А именно, пусть f(m) -минимум суммы квадратов плохостей для абзаца вплоть до точки разбиения т, тогда f(m) -- минимальная при всех k<m при f(k) плюс квадрат плохости разбиения строк текста из диапазона (k,m].
Для этого квадратичного алгоритма можно построить удовлетворительную аппроксимацию, близкую к линейной. Пусть даны три лучшие точки разбиения для k-й строки абзаца. Используем их для нахождения трех лучших точек разбиения для
(к+1)-й строки. После того как достигнут конец абзаца, или же если абзац настолько длинный, что не хватает объема буфера для его размещения (например, если он будет длиной более 15 строк), очистим буферные области памяти с помощью операции backtracking посредством вычисления f(m), чтобы найти лучшую из известных последовательностей разбиения. В режиме \ragged (выравнивание по правому краю выключено) строки не растягивают и не сжимают, чтобы подогнать их под размер \hsize, но в режиме \justified (выравнивание по правому краю включено) эти операции проводиться будут.
Встроенные переносы.
Помимо использования разрешенных разбиений строк, ТЕХ предпринимает также попытки переноса слов. Он будет проделывать это только с последовательностями, состоящими из букв в нижнем регистре (строчных букв), за ними может следовать (и им может предшествовать) символ, отличный от буквы, цифры, знака - или символ типа discretionary. Заметим что, например, составные слова с дефисом не будут разрываться. Если обнаружено разбиение, допустимое с точки зрения переноса слова, будет назначен штраф примерно в 30 единиц плохости, однако такой вариант может оказаться более предпочтительным, чем без использования переноса вообще. В рассматриваемом случае будут добавлены еще 20 единиц плохости, если переносимое слово или формула находятся в последней строке абзаца (точнее, в строке, за которой остается меньше, чем 0.5(\hsize) строк абзаца).
Нет способа найти все возможные варианты переносов. Насколько трудна эта задача, можно увидеть на примере слова "record", для которого предполагается, что оно будет разбито как "rec-ord", если оно является существительным, но "re-cord" — если оно будет глаголом. В качестве еще одного примера рассмотрим само слово "hyphenation", которое скорее будет исключением: hy-phen-a-tion vs. co-or-di-na-tion
Почему в одном из этих двух случаев буква п идет вместе с буквой а, а во втором -- нет? Если начать с буквы "а" в словаре и попытаться найти четкие правила переноса слов, не опираясь при этом на слишком большой объем знаний, получим такие пары слов, как a-part vs. ap-er-ture, aph-o-rism vs. a-pha-sia, и т.д. После всех этих примеров становится ясно, что едва ли нас устроит программа переносов, точная, но громоздкая и медленная в работе, занимающая много памяти и процессорного времени. Лучше вместо этого располагать набором правил переноса, которые были бы: а) простыми;
почти всегда безопасными;
достаточно мощными для того, чтобы найти, к примеру, порядка 80е/, переносов из тех, что есть в имеющемся издании книги The Art of Computer Programming.
Анализируя условия, предъявляемые пунктом (с), я обнаружил, что имеют место примерно 2 слова с переносами в них на страницу указанных книг. При этом оказалось, что подготовленные правила переноса лишь в очень редких случаях приводят к действительно плохому разрыву. Время, которое необходимо затратить, чтобы указать способ обработки обнаруженных исключений из правил (т.е. слов, не подчиняющихся имеющимся правилам), совершенно несущественно в сопоставлении с общим временем, затрачиваемым на чтение гранок.
(В этом месте файла ТЕХ. ONE было пять страниц текста, посвяиценного правилам расстановки переносов для текстов на английском языке. Эти страницы опугцены, поскольку не представляют особого интереса и, кроме того, могут быть найдены в руководстве пользователя по TgX’y издания 1979 г. Впоследствии Фрэнк
Л и ан г построил существенно более эффективную процедуру решения данной задачи, нашедшую применение и в других язиках помимо английского.)
Чтобы завершить этот меморандум, следовало бы объяснить, как ТЕХ обращается с математическими формулами. Сейчас я могу дать по крайней мере набросок этого материала.
Основные операторы, которые надо обсудить, это 4, t, \over, \groupxy и \sqrt; остальные с незначительными изменениями сводятся к перечисленным (например, команды \int и \surn преобразуются в нечто подобное командам I и t, команда \atop представляет собой вариант команды \over без линейки, команда \underline подобна команде \group, а команда \vinc (формирование горизонтальной черты над формулой) напоминает команду \sqrt). Для каждой из математических формул вначале строится дерево синтаксического анализа; фактически формула представляет собой модифицированный горизонтальный список hlist, который я буду называть далее списком tlist (от tree-list, т.е. древовидный список). Список tlist представляет собой список деревьев, возможно разделенных горизонтальным клеем, а дерево определяется как объект в одной из следующих форм:
некоторый бокс (если это не символьный бокс, то он был построен при выключенном математическом режиме);
узел \sub с деревом в качестве левого потомка и списком tlist в качестве правого потомка;
узел \sup с деревом в качестве левого потомка и списком tlist в качестве правого потомка;
узел \subsup с деревом в качестве левого потомка и списком tlist в качестве среднего и правого потомков;
узел \over со списком tlist в качестве левого и правого потомков; узел \sqrt со списком tlist в качестве единственного потомка; узел \group с символами квадратных скобок в качестве левого и среднего потомков, списком tlist в качестве правого потомка;
Наилучшие результаты будут получены, если использовать семейство из трех шрифтов изменяющихся размеров. Определение \fntfam <char><char><char>
задает такое семейство, упорядоченное по убыванию размера шрифта. Например, ТЕХ первоначально настроен на работу со следующим набором шрифтовых операций:
\deffnt
a cmlO
\deffnt g cmilO
\deffnt u cmathlO
\deffnt
b ст9
\deffnt h cmi9
\deffnt v cmath9
\deffnt
с ст8
\deffnt i cmi8
\deffnt w cmath8
\deffnt
d cm7
\deffnt j cmi7
\deffnt x cmath7
\deffnt
е сшб
\deffnt k cmi6
\deffnt у cmath6
\deffnt
f сш5
\deffnt 1 cmi5
\deffnt z cmath5
\fntfam adf \fntfam bef \fntfam gjl \fntfam hkl \fntfam uxz \fntfam vyz \mathrm a\mathit g\mathsy u (в тексте)
\mathrm b\mathit h\mathsy v (в упражнениях)
Это шрифты семейств "Computer Modern" и "Computer Modern Italic" размера от 10 pt до 5 pt; шрифт размера 8 pt фактически в формулах не используется, только в нижней части титульных страниц и в указателе.
Если текущий шрифт появляется как первый (самый большой) в некотором объявленном семействе шрифтов, то символы, используемые в математических формулах, будут скорректированы так, чтобы использовать подходящий для каждого конкретного случая шрифт из данного семейства; если же имеется только единственный шрифт, то он будет трактоваться как "семейство" из трех идентичных шрифтов (т.е. одни и те же размеры будут использоваться в нижних индексах и других элементах формул).
После того как выполнен синтаксический разбор математической формулы и представление ее в виде списка tlist, ТЕХ идет по этому списку сверху вниз, присваивая каждому из индивидуальных деревьев пометку режима его обработки: Арежим выключныхформул
Втекстовый режим
Стекстовый режимс уменьшенными верхними индексами
Dрежим индекса
Ережим индекса уиндекса
При этом в режимах АВС будет использоваться размер первого шрифта из семейства, в то время как в режимах D и Е — размеры второго и третьего шрифтов соответственно. В следующей ниже таблице показано, как ТЕХ определяет режимы для потомков корневого узла дерева в зависимости от того, в каком режиме находится корневой узел (father):
father
\sub
\sup
\subsup
\over
\sqrt
\group
А
AD
AD
ADD
ВС
C
A
В ‘
BD
BD
BDD
DD
C
В
С
CD
CD
CDD
DD
C
C
D
DE
DE
DEE
EE
D
D
Е
EE
EE
EEE
EE
E
E
Большие знаки
суммы, интеграла
и т.д.
используются
только в режиме A
После того как завершено приписывание режимов деревьям, ТЕХ делает по дереву tlist проход снизу вверх, преобразуя все деревья в боксы путем добавления клея везде, кроме списка tlist самого верхнего уровня, который становится горизонтальным списком hlist (передаваемым в программу формирования абзацев или куда-либо еще). Теперь, если вы хотите понять, почему ТЕХ делает сначала проход сверху вниз, а затем снизу вверх, заметьте, что, например, для числителя в дроби \over поначалу нельзя сказать, что это именно числитель; рассмотрим конструкцию 111 \over 1", где для "Г1 предполагается, что этому элементу приписан режим D. Более того, узлы \subsup могут порождаться из записи ...1...t... или ...t...1...
поскольку я обнаружил, что некоторые наборщики ставят первыми нижние индексы, а другие наборщики -- верхние индексы. В данном случае, когда ТЕХ выполняет синтаксический разбор формулы, операции 1 и t имеют наивысшее старшинство, затем \sqrt, затем \over;
х 1 у 1 zих t у t z
трактуется как
х 4{у 4 z}их t{y t z},
тогда конструкции типа х 4 у t z 4 я будут недопустимыми.
Первый из шрифтов семейства должен обладать таблицами, которые сообщают ТЕХ'у, насколько надо поднять линию шрифта для размещения верхнего индекса, насколько опустить ее для размещения нижнего индекса, позиции различных линий шрифта в конструкции \over. Все эти данные должны формулироваться как функция от режима и узла. Например, в шрифтах, которые я сейчас разрабатываю, верхний индекс, набираемый шрифтом кегля 7 pt для бокса размера 10 pt без нижнего индекса, будет иметь линию шрифта на высоте 11/3 pt в режиме В и 26/9 pt в режиме С; нижний индекс для такого же бокса без верхнего индекса будет иметь линию шрифта на глубине 3/2 pt как в режиме В, так и в режиме С; когда же у бокса имеются оба индекса, нижний и верхний, нижний индекс будет иметь линию шрифта на глубине 11/4 pt, а верхний индекс — на высоте 11/3 pt или 26/9 pt (или больше, если это окажется необходимым для размещения над сложным нижним индексом). Нижние и верхние индексы для более сложных боксов (например, для групп)
позиционируются относительно нижней и верхней границы рассматриваемого бокса, соответственно.
Выключные формулы никогда не разбиваются ТЕХ’ом на отдельные строки;
предполагается, что пользователь сам подберет место для разрыва формулы так, чтобы она смотрелась наиболее приемлемым образом. Поскольку ТЕХ умеет работать с отрицательными компонентами клея, вполне возможно ужать длинноватые формулы так, чтобы втиснуть их в строку. Если с помощью команд \halign или \eqalign формируется набор из нескольких выключных формул, их надо отделять друг от друга с помощью команды \сг.
(конец файла ТЕХ. ONE)
И с тех пор жили все они счастливо...
Примечания переводчика
Перечень соответствий между номерами строк в примере на сс. 510-513 и отвечающими им фрагментами книги АСР 2 и ее перевода см. в примечании 1 переводчика на с. 502.)
Примечание к с. 520. См. примечание 2 переводчика на с. 503.
Перевод дневниковых записей.
30 июня:
В среду до поздней ночи в течении восьми часов опять пытался закончить букву «s». Самочувствие неважное, не надо больше работать по такому дурацкому графику.
июля:
После 4 часов вычислений добился, наконец, успеха с «ess» (в 4:30 утра). Почувствовал себя лучше. Сделал прививки от тифа и холеры для поездки в Китай.
июля:
Вычищал шрифтовую программу, добавил знаки препинания, после чего отложил ее пока в сторону. Спал и спал.
июля:
Читал руководства по системам PUB и III.
июля:
К Видерхольдам на прощальный ужин с Виртом. Потом смотрел фейерверк у особняка Дрейпера. День такого необходимого отдыха!
июля:
Спал допоздна, потом размышлял о структурах данных для ТЕХ’а.
июля:
Сформирован способ обработки формул. Прием в особняке Вридена, встретил интересных людей.
июля:
Сделал много по спецификациям ТЕХ’овских структур данных. Переделал язык и на 3/4 переписал черновик MS.
июля:
Ходили с Джил за покупками. Работал над отчетом о ТЕХ’е.
июля:
Закончил писать ТЕХ.ONE, ходил в лабораторию AI, чтобы отпечатать первые 8 страниц.
июля:
Джон выступал в команде лучших игроков лиги юниоров, забил два гола. К двум часам ночи завершил набор файла ТЕХ.ONE.
июля:
Корреспонденция за две недели — не так уж и плохо, справился за два-три часа. Начал писать программу преобразования документов из белфастского формата в формат, близкий к ТЕХ’овскому.
июля:
Долгое совещание с Майком и Фрэнком, обсуждали планы реализации ТЕХ’а. Редактировал черновик ТЕХ.ONE, вносил различные усовершенствования в язык.
Т^Х’овские инкунабулы
[Первоначально опубликовано в TUGboat 5 (1984), 4-11.]
Меня постоянно спрашивают о списке «первых» книг, набранных с помощью T^X’a. Библиофилы, наверное, были бы довольны, если бы им была предоставлена возможность проследить раннюю историю данного вида книгопечатания. Вот почему я решил попытаться составить список таких публикаций, известных мне, пока еще не изгладилась память об этих волнующих событиях. Предлагаемый ниже перечень ограничивается только действительно вышедшими в свет (опубликованными) работами, хотя среди моих файлов содержится также еще и несколько десятков концертных и церковных программ, информационных бюллетеней и тому подобных вещей, которые мы с женой готовили, даже еще когда не было полноценного рабочего варианта ТуСа.
Первое издание руководства по Т£Х’у является сейчас уже раритетом, хотя, как я полагаю, и было отпечатано несколько сотен его экземпляров. Оно называлось «Тау Эпсилон Хи, система для подготовки технических текстов» и было выпущено как технический отчет «Таи Epsilon Chi, a system for technical text», Stanford Computer Science Report STAN-CS-78-675 = Artificial Intelligence Laboratory Memo AIM-317 (September 1978), 198 pp. В июне 1979 г. Американским математическим обществом AMS было выпущено исправленное издание этого руководства. Моя жена Джил разработала для него оформление обложки; было продано порядка 1000 экземпляров данной книги. Если у вас имеется достаточно «чистый» экземпляр этого издания, то на его обложке можно разглядеть легкий рисунок ТЁХтуры, очень похожей на ту, что приведена в упр. 21.8 книги The TgXbook.
Значительное число пользователей знакомилось с возможностями экспериментальной версии (прототипа) ТЁХ’а, используя третье издание вышеупомянутого руководства, которое было выпущено в качестве части 2 книги « Т^Х and METAFONT», подготовленной совместно Американским математическим обществом и издательством Digital Press в конце 1979 г. Тираж этой книги составил около 15000 экземпляров.
Печать всех этих трех изданий была выполнена с помощью экспериментального некоммерческого оборудования с невысокой разрешающей способно-
LENA BERNICE
Her Christmas in Wood County, 1895
Lena Bernice was our grandmother. She told us about her first Christmas tree. She told us many things while the snow fell.
Elizabeth Ann and Jill Christmas 1978
Рис. 1. Титульный лист первой ТЁХ’овской книги
стью. При подготовке первого издания использовался графический принтер Xerox Graphics Printer (XGP) фирмы Xerox, подаренный Лаборатории искусственного интеллекта Станфордского университета; второе издание печаталось на уникальном принтере «Colorado» на фирме Xerox Electro-Optical Systems в Пасадене, шт.Калифорния; наконец, для третьего издания был использован принтер «Penguin» Исследовательской группы фирмы Xerox по перспективным системам в Пало Альто (Advanced Systems Development Group). Эти устройства имели переменную разрешающую способность, установленную равной 200 пикселей на дюйм у принтера XGP и 384 пикселей на дюйм у остальных двух принтеров. Техническую поддержку печати второго и третьего изданий осуществляли Дейл Грин и Лео Гуиба.
Руководство по METAFONT’y имеет сходную историю его публикации. Первоначально оно было распечатано на принтере XGP под названием «МЕТА-
33Elizabeth Ann wrote the story for this book, and Jill designed the pages and drew the pictures. The book was typeset by Don Knuth using the Т^% system, and printed by Homer Weathers at THE RAINSHINE PRESS.
3
3
28
Рис. 2. Выходные данные в конце первой TgX’опекой книги
FONT, a system for alphabet design», Stanford Computer Science Report STAN- CS-79-762 = Artificial Intelligence Laboratory Memo AIM-332 (September 1979), 105 pp.); затем данное руководство было перепечатано с небольшими изменениями на принтере Penguin как часть упоминавшейся выше книги, подготовленной издательством Digital Press.
Конечно, руководства пользователей вряд ли можно считать существенными достижениями в области книгопечатания. По моему мнению первой настоящей книгой, отпечатанной с помощью Т^^’а, можно считать 28-страничное сувенирное издание, подготовленное для родственников моей жены к Рождеству 1978 г. В этой книге было 18 оригинальных линогравюр с впечатанным с помощью принтера XGP текстом, набранным с использованием расширенного варианта опытного шрифта Computer Modern, размера 14pt. Чтобы компенсировать ограниченную разрешающую способность принтера
he snow began around three o’clock. Aunt Sally had the little ones turn to the window and recite their lesson.
“See the snow softly fall
over barns and churches tall.”
Gussie was trying to teach Horace at home, so she copied it down. She wanted Horace to be a member of the state legislature, like Ben James who was her uncle and the greatest orator in Wood County.
Lena Bernice thought about little Jimmy Reed in the lesson book and how he wondered if the snow tasted of sugar. She thought about the brave dog, Caesar, who had protected his mistress during the blizzard in Old Kentucky. She thought about the layer of ice on Rock Pond.
Aunt Sally took up the Illustrated Geography and showed The Entire Class a picture of the Alps and of the dear Saint Bernards who saved many a folk from certain death.
2
Рис. 3. Начальные страницы первой ТеХ’овской книги
XGP, подготавливался увеличенный вариант требуемой страницы, которая затем печаталась с уменьшением до 70% от первоначального размера; эффективная разрешающая способность при этом составляла 286 пикселей на дюйм. Титульный лист, первые страницы и страница с выходными данными книги приведены здесь в качестве иллюстраций (уменьшение до 61% от реального размера). Было отпечатано около 100 экземпляров книги, из которых примерно 25 было продано, а остальные 75 использовались в качестве подарков. Полное библиографическое описание данной книги имеет вид: «Lena Bernice: Her Christmas in Wood County, 1895. By Elizabeth Ann James, with illustrations by Jill Carter Knuth. Columbus, Ohio: Rainshine Press, 1978».
Довольно необычным способом T^X и МЕТА FONT были использованы Дэвидом Уоллом при подготовке диссертации (Ph. D. thesis, «Mechanisms for
3
broadcast and selective broadcast», Stanford Computer Science Report STAN- CS-82-919 = Stanford Computer Systems Laboratory Technical Report No. 190 (June 1980), 120 pp.) Он рассматривал каждую иллюстрацию как некий «символ» некоторого нового «начертания», каждый из этих больших символов был нарисован с помощью METAFONT’a, после чего с помощью TgX’a производилось наложение одних текстовых фрагментов на другие. Такой подход позволил бы «победить» существовавшую программную реализацию METAFONT’a, если бы рисунки выполнялись с достаточно высоким разрешением, однако он не оправдал себя при использовании принтера XGP. (См. приводимые ниже примеры, уменьшенные до 65% от их исходного размера.) Дэвид сказал: «Я боялся, что открыл ящик Пандоры, ведь это не совсем то, для чего создавался METAFONT».
On the other hand, if w looks at its own fragment state and finds a better edge than the one и selected, then w must know about a vertex in the fragment of v and w that v did not know was present. Thus as soon as w merged its fragment state with that of и it had a larger fragment state than v. Its fragment state may even have been larger than v’s beforehand.
We observe that z precedes у on this path p. For if z = у then this single vertex is on both path Qi and path 92, which are represented by edges without a common endpoint. This violates Lemma 4. Similarly, if z follows у then у is on the portion of path p from m to z and so by Lemma 2 it must also be on the portion of path 91 from m to z. Thus у is on both 91 and 92, which again contradicts Lemma 4. So z precedes y.
Endpoint Disagrees with vEndpoint Disagrees with vReplacing (m, n} with pReplacing (m, n} with pРис.4. Фрагменты диссертации Дэвида Уолла.
Endpoint Disagrees with v
Endpoint Disagrees with v
Replacing (m, n} with p
Replacing (m, n} with p
В течение всего описываемого периода у нас не было возможности воспользоваться фотонаборным устройством с высокой разрешающей способностью, но после нескольких месяцев работы в течение зимы и весны 1980 г. Дэвид Фукс и я разработали интерфейс для фотонаборной машины Alphatype CRS. (Большая часть наших усилий была направлена на то, чтобы произвести полный пересмотр микрокода, используемого в CRS, поскольку память для хранения шрифтов, имеющаяся в данном устройстве, была весьма ограниченной.) Из моих записей видно, что первые вполне приличные по качеству текстовые страницы были отпечатаны 1 апреля 1980 г., а первой отпечатанной страницей, предназначавшейся для публикации, была моя одностраничная поэма, озаглавленная «Disappearances» (Исчезновения), помещенная на с. 264 книги The Mathematical Gardner, edited by David Klarner (Belmont, California: Wadsworth, 1981). В мае я отправил более длинную статью «The Letter S» в журнал The Mathematical Intelligencer 2 (1980), 114-122. Эта статья воспроизводится в качестве гл. 13 настоящей книги.
Большую часть своего времени в апреле, мае, июне и июле 1980 г. я потратил на подготовку окончательной редакции большой книги, представлявшей собой ТЁК’овский raison d’etre, той самой книги, которая и побудила меня первоначально заняться ТЩК’ом и METAFONT’om. Текст этого тома был уже набран в 1976 г. в издательстве The Universities Press, Belfast, с использованием Monophoto-систем типа Cora и Maths, результаты, однако, были неудовлетво-
рительными. Я получил бумажные перфоленты из Белфаста и конвертировал их в псевдо-Т^Х так, чтобы упростить повторный набор текста. (На самом деле совсем не очевидно, сэкономил ли я какое-то время с помощью такого маневра!) Все 700 страниц данной книги в конце концов встали на свои места и оригинал-макет книги Seminumerical Algorithms, Volume 2 of The Art of Computer Programming, second edition; Reading, Massachusetts: Addison- Wesley, 1981, был завершен в 2 часа ночи, во вторник 29 июня 1980 г. 22 октября я переделал страницу iv (она содержит информацию Библиотеки Конгресса США). Сигнальный экземпляр этой книги я держал в руках уже 4 января 1981 г. Представляется, что именно с фактом появления этой книги наиболее уместно связывать дату первого использования ТЁХ’а в реальном книгоиздании. Мои издатели подготовили ограниченный тираж данной книги (256 экземпляров) в кожаном переплете ручной работы. Я думаю, что обладателями этих специальных экземпляров стало совсем немного людей, поскольку у компьютерных специалистов едва ли возникло желание заплатить за кожаный переплет, тогда как ценители типографского искусства не питали особо нежных чувств к компьютерам. В то же время, в течение 1981 г. было продано около 11 000 экземпляров «Seminumerical Algorithms» в обычном переплете.
А тем временем и другие люди в Станфорде при подготовке книг к публикации стали использовать ТЁК и Alphatype в нашей лаборатории. Первой из них была закончена книга KAREL the ROBOT: A Gentle Introduction to the Art of Programming by Richard E. Pattis; New York: Wiley, 1981,120 pp. Многие иллюстрации в этой книге были набраны при участии Дэвида Уолла с использованием специальных символов METAFONT’a. Следующей книгой такого рода стала Practical Optimization by Philip E. Gill, Walter Murray, and Margaret H. Wright; New York: Academic Press, 1981, 417 pp. Говоря об оптимизации, следует упомянуть еще статью: Bengt Aspvall and Yossi Shiloach, «А polynomial time algorithm for solving systems of linear inequalities with two variables per inequality», SIAM Journal of Computing 9 (1980), 827-845, которая также была отпечатана с помощью устройства Alphatype в нашей лаборатории в этом же году.
Скотт Ким был первым, кто использовал наш Alphatype вместе с ТЁХ’ом, чтобы выполнить печать с помощью шрифтов, полученных помимо METAFONT’a (речь идет о его книге Inversions: A Catalog of Calligraphic Cartwheels by Scott Kim; Peterborough, New Hampshire: Byte Books, 1981, 124 pp.) Впоследствии он подготовил с помощью METAFONT’a ряд специальных символов, использованных при издании книги по программированию на Паскале (Arthur Keller. A First Course in Computer Programming using PASCAL; New York: McGraw-Hill, 1982, 319 pp.) Интересно отметить, что итальянский перевод этой книги (Programmare in PASCAL; Bologna: Zanichelli, 1983, 303 pp.) был одним из первых в Италии, для набора которого использовался ТЁХ. Итальянские переводчики (G. Canzii, A. Pilenga, A. Consolandi) работали независимо от американских авторов и подготовили оригинал-макет книги в Милане с помощью наборной машины Versatec — к сожалению, без символов, подготовленных Скоттом.
Терри Виноград был одним из первых пользователей ТцХ’а и, соответственно, одним из первых стал жаловаться на ограничения, присущие его первоначальному варианту. Например, именно по его просьбе от 28 ноября 1978 г. я добавил команду \xdef. Он начинал писать книгу, используя систему PUB [Larry Tesler, «PUB, The Document Compiler», Stanford Artificial Intelligence Project Operating Note 70 (March 1973), 84 pp.], затем все имеющиеся файлы конвертировал в формат системы BRAVO [Butler W. Lampson, «Bravo Manual», in Alto User’s Handbook, Xerox Palo Alto Research Center (1978), 32-62], после чего опять преобразовал их, на этот раз в ТцХ’овский формат. Виноград разработал макрокоманды индексации для первого выпуска журнала TUGboat, а его борьба с ранней версией ТцХ’а увенчалась в конце концов книгой Language as a Cognitive Process, Volume 1: Syntax; Reading, Massachusetts: Addison- Wesley, 1983, 654 pp. Он пользовался шрифтами семейства Computer Modern, но вместо применявшихся мною в то время (ужасных) шрифтов без засечек (sans-serifs) подставлял шрифт Optima.
Гио Видерхольд модифицировал файлы АСМЕ, созданные им при подготовке первого издания его книги по базам данных, так что второе ее издание он смог подготовить уже с помощью ТцХ’а. Как он говорил, процесс окончательного форматирования текста второго издания занял около шести месяцев (включая печать пробного экземпляра, чтобы убедиться, что все разрывы страниц находятся на положенных им местах). Том, получившийся в итоге, поставил рекорд того времени по объему книги, когда-либо печатавшейся в нашей лаборатории: Database Design, second edition; New York: McGraw-Hill, 1983, 767 pp.
Еще один мой коллега-преподаватель, Джеффри Ульман, преобразовал имевшиеся у него troff-файлы первого издания его книги в ТцХ’овские файлы, на основе которых было получено второе издание этой книги (Principles of Database Systems, second edition, Rockville, Maryland: Computer Science Press, 1982, 491 pp.). После этого он воспользовался ТцХ’ом уже напрямую для написания книги Computational Aspects of VLSI; Rockville, Maryland: Computer Science Press, 1984, 505 pp. Питер, 13-летний сын Джеффа, помог ему в создании с помощью METAFONT’a специальных шрифтов для набора шаблонов из повторяющихся элементов, используемых при разработке СБИС.
Мой соавтор Дэниел Грин перевел в ТЁХ’овскую среду нашу книгу Mathematics for the Analysis of Algorithms; Boston: Birkhauser, 1981, 107 рр»? Интересно сравнить первое издание со вторым (1982 г., 123 с.), поскольку в течение года, прошедшего между первым и вторым изданием, были существенно откорректированы шрифты, использовавшиеся в наборе. [Третье и «окончательное» издание,(1990 г., 132 с.) демонстрирует дальнейшее улучшение полиграфического уровня издания].
Книги и статьи, упоминавшиеся выше, были все подготовлены к изданию их авторами, как было принято на кафедре Computer Science. Но бы-
Имеется русский перевод второго издания: Грин Д.., Кнут Д. Математические методы анализа алгоритмов. — М.: Мир, 1987, но тогда «русский» Т^Х еще только зарождался.— Прим. ред.
ло и несколько случаев, когда работа организовывалась более традиционным образом, при котором Т^’овскую подготовку текста осуществляли люди, привычные к клавиатуре, но не слишком знакомые с предметом, о котором шла речь в подготавливаемом издании. Мне кажется, первыми из книг, подготовленными в нашей лаборатории по этой технологии, были Handbook of Artificial Intelligence, Volume 2, edited by Avron Barr and Edward A. Feigenbaum; Los Altos, California: William Kaufman, 1982, 441 pp.; Handbook of Artificial Intelligence, Volume 3, edited by Paul R. Cohen and Edward A. Feigenbaum; Los Altos, California: William Kaufman, 1982, 652 pp.; Introduction to Arithmetic for Digital Systems Designers by Shlomo Waser and Michael J. Flynn; New York: Holt, Rinehart and Winston, 1982, 326 pp.; Introduction to Stochastic Integration by Kai Lai Chung and Ruth J. Williams; Boston: Birkhauser, 1983, 204 pp.; серия практических руководств по языку программирования Basic для компьютеров различных типов: Hands-on Basic: For the IBM Personal Computer, by Herbert Peckham, New York: McGraw-Hill, 1983, 320 pp.; Hands-on Basic: For the Apple II, by Herbert Peckham with Wade Ellis, Jr., and Ed Lodi, New York: McGraw-Hill, 1983, 332 pp.; Hands-on Basic: For the TRS-80 Color Computer, by Herbert Peckham with Wade Ellis, Jr., and Ed Lodi, New York: McGraw-Hill, 1983, 354 pp.; Hands-on Basic: For the Atari 400/800/1200XL, by Herbert Peckham with Wade Ellis, Jr., and Ed Lodi, New York: McGraw- Hill, 1983, 319 pp.; Probability in Social Science by Samuel Goldberg, Boston: Birkhauser, 1983, 131 pp. Между прочим, набор последней из перечисленных книг был выполнен моим сыном Джоном летом 1982 г., еще до того, как он узнал хоть что-нибудь о компьютерах. Я помог ему с несколькими конструкциями \halign, но в остальном он работал вполне самостоятельно. В это время он был старшеклассником; я знал еще по крайней мере троих ребят из их школы, которые также занимались набором книг в ТЁХ’е. ^Introduction to Commutative Algebra and Algebraic Geometry by Ernst Kunz (Boston: Birkhauser, 1985), 237 pp., набрано Ами и Майклом Вангами; Social Dynamics by Nancy Brandon Tuma and Michael T. Hannan (Orlando, Florida: Academic Press, 1984), 578 pp., набрано Кэти Тума.]
Небезынтересно отметить, что первый том Handbook of Artificial Intelligence, 1981, был реализован с помощью ранней версии шрифтов Computer Modern на нашей наборной машине Alphatype, однако подготовка текста осуществлялась не в ТЁХ’е, а с помощью пакета PUB. В частности, все переносы слов в этой книге были расставлены вручную.
Сотрудники Лаборатории космического пространства, телекоммуникаций и радионаук (Space, Telecommunications and Radioscience Laboratory) Стан- фордского университета начали использовать Т^Х для набора статей, которые они печатали на Alphatype и публиковали затем в журналах и в сборниках трудов конференций. Я думаю, первыми из таких статей были следующие: S.B. Mende, Р. М. Banks, «Photographic observations of earth’s airglow from space»; R. Nobles, О. K. Garriott, and J. Hoffman, Geophysical Research Letters 10 (1983), 1108-1111; C. Robert Clauer, Robert L. McPherron, and Craig Searls, «Solar wind control of the low-latitude asymmetric magnetic disturbance field», Journal of Geophysical Research 88 (1983), 2123-2130; Robert A. Helliwell, «VLF wave
injections from the ground», in Active Experiments in Space (Paris: European Space Agency, 1983), 3-9; P. M. Banks, P. R. Williamson, J. Raitt, S.D. Shawhan, and G. Murphy, «Electron beam experiments aboard the space shuttle», ibid., 171-175. Несколько десятков других статей к настоящему времени (1983 г.) находятся в печати.
Тем студентам, кто был уже достаточно подготовлен, разрешалось использовать наш Alphatype по их усмотрению, не ставя меня об этом в известность. Например, помню, как был удивлен однажды вечером, увидев свои шрифты Computer Modern в рекламе «Earth Shoes», в программе музыкальной комедии, поставленной станфордскими студентами. Разумеется, наиболее поразительной вещью, вышедшей из нашей лаборатории, а в дальнейшем и наиболее значительной в финансовом отношении, была аппаратная часть первой рабочей станции Sun (или, по крайней мере, некоей ключевой частью ее): Андреас фон Бехтольсхайм «набрал текст» печатной платы для первого изделия фирмы Sun Microsystems и вывел его на наш Alphatype в 1981 г., используя специализированные шрифты, созданные с помощью METAFONT’a.
Программное обеспечение, которое мы разработали для обеспечения интерфейса между Т^Х’ом и машиной Alphatype CRS, использовалось также еще в трех местах: в Американском математическом обществе (Провиденс, Род Айленд), в Королевском технологическом институте (Стокгольм, Швеция) и на фирме Bell Northern Research (Маунтин Вью, шт. Калифорния). Я располагаю только отрывочной информацией о том, какие книги были подготовлены с помощью ТЩХ’а в этих и других местах. Тем не менее, я привожу список, заведомо неполный, чтобы те, кто был причастен к соответствующим работам, дополнили и уточнили его.
Первым примером применения Т^Х’а и Alphatype в Американском математическом обществе (AMS), имеющимся в моей коллекции, является статья «1980 Wiener & Steele Prizes Awarded», Notices of the American Mathematical Society 27 (1980), 528-533. С тех пор с помощью IfeX’a готовится все большая доля материалов, появляющаяся в «Notices...». Объединенный список членов обществ SIAM, AMS и МАА за 1981-82 гг. (SIAM-AMS-MAA Combined Membership List for 1981-1982) представляет собой еще один из ранних примеров использования Т^Х’а, так же как и Каталог публикаций AMS за 1981-82 гг. (AMS Catalog of Publications for 1981-1982.) Американское математическое общество впервые испытало математические способности ТЁК’а, подготовив черновой вариант предварительной редакции книги (Michael Spivak. The Joy of T^X, 134 pp., распространявшийся среди участников конференции AMS в Сан-Франциско в январе 1981 г. Имеется много случаев использования TgX’a в «Трудах AMS» (Proceedings [Volume 85 (1982), pp. 141-488, 567-595, 643-665, 673-674; Volume 86 (1982), pp. 12-14,19-86,103-125,133-142,148-150,153-183, 186-188, 253-274, 305-306, 321-327, 363-374, 391, 459-490, 511-524, 574-598, 609-624, 632-637, 641-648, 679-684]). В Memoirs of the American Mathematical Society 33, number 247, September 1981, 54 pp. была опубликована диссерта-
ция Дэвида Экка (David J. Eck. «Gauge-natural bundles and generalized gauge theories»); этот выпуск «Memoirs... » содержит интересное предисловие Ричарда Пале, из которого следует, что Дэвид имел удовольствие быть «подопытным кроликом», впервые испытавшим при наборе своей диссертации Лд^-ТЁХ.
До того как издательская группа AMS стала применять машину Alphatype, она подготовила ряд вещей, используя выход наборной машины Varian, уменьшенный фотоспособом. Указатели к отдельным выпускам журнала Mathematical Reviews готовились с помощью ТЁХ’а после ноября 1979 г. (Volume 58, #5); объединенный список членства Combined Membership List за 1980-81 гг. также был получен с помощью машины Varian.
На оборудовании AMS был подготовлен также ряд книг, написанных в других местах. Среди них: Oregon Software’s PASCAL-2: Version 2.0 for RSX-11 (1981), 186 pp.; Turtle Geometry by Harold Abelson and Andrea diSessa (Cambridge, Massachusetts: M. I. T. Press, 1981), 497 pp.; and History of Ophthalmology by George Gorin (Wilmington, Delaware: Publish or Perish, 1982), 646 pp.
В Bell Northern, как я думаю, ТЁХ использовался большей частью для подготовки внутрифирменных отчетов. Однако мне доводилось видеть несколько прекрасных публикаций из Шведского Королевского технологического института, в частности, 46-страничную монографию Non-linear Inverse Problems by Gerd Eriksson, Report TR17A-NA-8209, 1983, —и мне хотелось бы знать больше относительно опыта, полученного при использовании ТЁХ’а в этой организации.
Издательство Висконсинского университета (The University of Wisconsin Press) прислало мне экземпляр их каталога на осень 1981 г. (1981 Fall Catalog), который, по их словам, был подготовлен с помощью ТЁХ’а. Я не знаю, правда, помог ли ТЁХ подготовить какую-либо из книг, перечисленных в этом каталоге.
Когда шрифты Computer Modern еще не применялись, сказать, использовался или нет ТЁХ при подготовке той или иной публикации, было весьма непросто. Однако я точно знаю, что книга Guide to International Commerce Law by Paul H. Vishny; Colorado Springs, Colorado: Shepard’s/McGraw— Hill, 1981, 782 pp., была целиком набрана с помощью ТЁХ’а и распечатана на IBM 370/3081 с использованием фотонаборной машины APS 5.
Некоторые из книг, изданных в Станфорде, печатались напрямую с помощью устройства Xerox Dover (с разрешающей способностью 384 точки на дюйм). В частности, первоначальное издание в твердом переплете книги Joseph Deken. The Electronic Cottage; New York: William Morrow, 1982, 334 pp., было осуществлено именно таким способом, вследствие очень жестких сроков публикации. Аналогично обстояло дело и с книгами Arithmetic and Geometry: Papers Dedicated to I. R. Shafarevich on the Occasion of His Sixtieth Birthday, edited by Michael Artin and John Tate (Boston: Birkhauser, 1983); Volume 1, 359 pp., Volume 2, 481 pp.)
Макс Дьяс выполнил работы по установке ТЁХ’а в Институте прикладных математических исследований Автономного национального университета в Мехико (Instituto de Investigaciones en Matematicas Aplicadas у en Sistemas of the Universidad Nacional Autonoma de Mexico (IIMAS-UNAM)). Первой книгой, подготовка которой в ТЁХ’е была целиком осуществлена в Мехико, была
книга Nonlinear Phenomena, edited by Kurt Bernardo Wolf, Lecture Notes in Physics 189, 1983, 464 pp. Основой этой работы, которая была получена с использованием фотоуменьшения выхода устройства с невысокой разрешающей способностью, были, разумеется, подготовленные Максом ТвХ’овские макро Facil VTUGboat 2, 2 (July 1981), А-1-А-91].
Я начал работу над ТЁХ’ом вскоре после того, как увидел гранки книги Р. Winston Artificial Intelligence; Reading, Massachusetts: Addison—Wesley, 1977, 444 pp., которая была первой научно-технической книгой, набранной с помощью фотонаборной машины с высокой разрешающей способностью. Только недавно я узнал, что новое издание этой книги (Patrick Henry Winston, Artificial Intelligence, second edition (Reading, Massachusetts: Addison—Wesley, 1984), 527 pp.) готовится с помощью ТЁ^’а, так что круг замкнулся.
Журнал AI Magazine (официальный орган Американской ассоциации искусственного интеллекта) набирается в нашей лаборатории с помощью Т^Х’а после второго номера из тома 3 (весна 1982 г.). Я ничего не знал об этом до 16 июня 1983 г., когда получил следующее довольно неожиданное письмо от главного редактора Claudia С. Mazzetti: «Время подготовки нашего журнала сократилось почти наполовину при использовании ТЁХ’а. Мы хотели бы выразить нашу признательность за создание такой замечательной системы!»
Сейчас, к 1983 г. я никак не могу понять, почему некоторые думают, что старая версия ТЁХ’а была простой в использовании, ведь я потратил два года на устранение сотен недостатков в ней. (Все публикации, о которых говорилось выше, были подготовлены с помощью старого «прото-ТЁХ’а»). Более того, я до сих пор не удовлетворен полностью качеством шрифтов Computer Modern, хотя версия «Almost Computer Modern» от июля 1983 г. уже много лучше, чем шрифты, которые мы использовали в 1980 г. Я предполагаю сделать дальнейшие усовершенствования в шрифтах в течение следующих двух лет, когда будут завершены мои исследования по издательскому делу. Моя цель — разработать новый METAFONT к 1984 г. и новые шрифты Computer Modern к 1985 г. Тем временем, в 1983 г. мы получили новую постоянную редакцию ТЁХ’а, и я завершаю этот список упоминанием трех первых публикаций, подготовленных с помощью этого нового ТЁХ’а и нашего нового фотонаборного устройства APS Micro-5: книга The T^Kbook (Reading, Massachusetts: Addison—Wesley, 1983), 496 ppJ была первой из них, она была отправлена издателю 12 октября. Книга Coordinated Computing: Tools and Techniques for Distributed Software by Robert E. Filman and Daniel P. Friedman (New York: McGraw-Hill, 1984), 390 pp., была второй, а моя статья «Literate Programming» (15 pp.)—третьей. В этой статье, в которой обсуждается WEB, вместе со шрифтами Computer Modern используются также шрифты Times Roman и Univers. Она будет опубликована в томе 27 журнала The Computer Journal.
Мне хочется думать, что использование ТЁХ’а не только позволяет готовить отлично отформатированные книги. Как представляется, ТЁХ также
1 Имеется русский перевод: Кнут Д. Е. Все про Т^Х. — Протвино: RDTLX, 1993, —еще одно из первых русскоязычных Т^Х’овских изданий. — Прим, перев.
помогает писать книги, которые и по содержанию лучше тех, что готовятся по-старинке. Часть этих возможностей обусловлена просто преимуществами использования текстовых процессоров и компьютерного редактирования, что существенно облегчает работу. Однако другая часть предоставляемых возможностей вытекает из того факта, что авторы теперь могут выбирать систему обозначений и форму представления материала такими, какие они считают наиболее подходящими; теперь они свободны от тревог, связанных со взаимодействием через несколько уровней с людьми, для которых обозначения, примененные автором, незнакомы и непривычны. Мне кажется, что значительное число книг, перечисленных выше, демонстрируют такого рода улучшения в части изложения материалов научного характера. К примеру, мои собственные книги существенно улучшились, так как я теперь в состоянии управлять процессом набора. Я с большим уважением относился и отношусь к советам профессиональных редакторов и оформителей книг, однако теперь в значительно большей степени, чем раньше, я могу быть уверен в конечном результате, поскольку осталось сравнительно немного источников возможных недоразумений между участниками издательского процесса, порождающих различные ошибки. Таков, на мой взгляд, некий «итог», основной практический результат моих работ по ТЁХ’у.
Добавление
Дополнительная информация, относящаяся к ранним этапам использования ТЁХ’а в Королевском технологическом институте (Royal Institute of Technology) была опубликована уже после опубликования данной статьи в TUGboat: Hans Riesel, «Report on experience with T£X80», TUGboat 6 (1985), 76-79.
Пиктограммы для TJ%’a и METAFONT’a
[Первоначально опубликовано в TUGboat 14 (1993), 387-389.]
Пользователи компьютеров Macintosh уже давно привыкли видеть их файлы отображаемыми в оглавлениях графически, в «пиктограммной» (iconic) форме. Я получил недавно в свое распоряжение рабочую станцию с графической многооконной оболочкой и системой управления файлами (диспетчером файлов), которые предоставляют аналогичные возможности по визуализации моих собственных файлов для системы UNIX. Естественно, мне захотелось, чтобы ТЁХ’овский материал представляли отвечающие ему по духу пиктограммы. Цель этих заметок — предоставить разработанные мною пиктограммы в распоряжение других пользователей: может быть они захотят ими воспользоваться и/или улучшить их.
Диспетчер файлов моей новой машины (Sun SPARCstation) имеет в своем составе «средства классификации», которые анализируют имя файла и/или его содержимое, чтобы решить, к какому виду принадлежит данный файл. Каждому из видов файлов поставлено в соответствие растровое изображение размера 32 х 32 точки (пикселя), называемое •пиктограммой, совместно с другим растровым изображением (также размера 32 х 32 пикселя), именуемым маской для данной пиктограммы. В точке пиктограммы, где бит маски принимает значение 1, диспетчер файлов отображает один или два пиксельных цвета, называемых цветом переднего плана и цветом фона, в зависимости от того, имеет ли бит пиктограммы в этой позиции значение 1 или 0. (Цвета переднего плана и фона могут быть различными для каждого из типов файлов.) В прочих позициях растрового изображения, где биты маски имеют значение 0, диспетчер файлов отображает его собственный цвет фона.
ИЛИИЛИНапример, каждому из файлов, имена которых заканчиваются на .tex или .mf, на моей машине сейчас поставлены в соответствие следующие растровые изображения
ИЛИ
ИЛИ
соответственно. Это совместимо с существующей схемой, согласно которой у исходных файлов с текстами для программ на языке С имена будут с расширением .с, а у заголовочных файлов для них —с расширением .h, что отображается в виде таких пиктограмм:
Аналогично этому, файлам с расширением *. Itx соответствует пиктограмма
Все, что я должен был сделать —это указать классифицирующей программе, как идентифицировать ТЁХ’овские и METAFONT’obckhc файлы, а также подготовить растровые изображения для пиктограмм и их масок. В каждом из упомянутых выше случаев в качестве соответствующей маски, требуемой для формирования пиктограммы, указывалась встроенная маска, используемая диспетчером файлов, для которой установлено имя Generic_Doc_glyph_mask:
токольных файлов, выдаваемых ТеХ’ом и METAFONT’om. Проблема здесь заключалась в том, что оба этих протокольных файла в моей системе имеют одно и то же расширение *. log, так что просто по имени различить их нельзя. Я решил, что любой файл, первые 12 байтов которого содержат ASCII-символытокольных файлов, выдаваемых ТеХ’ом и METAFONT’om. Проблема здесь заключалась в том, что оба этих протокольных файла в моей системе имеют одно и то же расширение *. log, так что просто по имени различить их нельзя. Я решил, что любой файл, первые 12 байтов которого содержат ASCII-символы
токольных файлов, выдаваемых ТеХ’ом и METAFONT’om. Проблема здесь заключалась в том, что оба этих протокольных файла в моей системе имеют одно и то же расширение *. log, так что просто по имени различить их нельзя. Я решил, что любой файл, первые 12 байтов которого содержат ASCII-символы
токольных файлов, выдаваемых ТеХ’ом и METAFONT’om. Проблема здесь заключалась в том, что оба этих протокольных файла в моей системе имеют одно и то же расширение *. log, так что просто по имени различить их нельзя. Я решил, что любой файл, первые 12 байтов которого содержат ASCII-символы
‘ThisuisuTeX’, должен рассматриваться как ТЁХ’овский протокольный файл, а файл, начинающийся со строки ‘ThisuisuMETAFONT’— как МЕТАРОЫТ’овский. Одно удовольствие было подготовить соответствующие пиктограммы; я сделал их на основе иллюстраций Дуэйна Бибби из руководства пользователя по ТЁХ’У и METAFONT’y:
Маски, отвечающие этим пиктограммам для Т^Х’а и METAFONT’a, соответственно имеют вид:
Основным выходным файлом T£X’a является, безусловно, аппаратнонезависимый (DeVice-Independent) DVI-файл, а для METAFONT’a —типовой шрифт в виде GF-файла. Я решил, что пиктограммы для этих двух видов файлов должны иметь такой вид:
соответственно, поскольку они аналогичны, в известной степени, фотографическим «негативам», которые требуют «проявки» их другими программами. Когда GF-файл упаковывается в РК-файл, его пиктограмма меняется на такую:
Аналогичным образом представляются файлы виртуальных шрифтов
Эти файлы вполне идентифицируются их именами (расширения *.dvi, *gf, *pk, *. vf), но их можно также опознавать и по их содержимому. Первый байт в них всегда имеет десятичное значение 247 (восьмеричное 367), тогда как следующий байт принимает значение 2, 131, 89, 202 (восьмеричное 002, 203, 131, 312) для dvi-, gf-, pk-, or vf-файлов, соответственно.
Еще один важный выходной файл METAFONT’a —это метрический файл шрифта, который можно узнать по расширению .tfm в его имени. Этим файлам я поставил в соответствие такие пиктограмму и ее маску:
Все программирование я выполняю с помощью языка CWEB [1, 2, 3, 4], поэтому у меня накопилось много файлов двух дополнительных видов. Исходный
текст CWEB-программ хранится в файлах с расширением . w, CWEB-файлы изменений — в файлах с расширением . ch; были сформированы соответствующие пиктограммы
хорошо соотносящиеся с существующими соглашениями относительно файлов исходного текста (.с) и заголовочных файлов (.h) для языка С.
Какие цвета переднего плана и фона должны быть присвоены этим пиктограммам? Я этого точно не знаю. В то время, когда я делал эти пиктограммы, в моем распоряжении был монохромный монитор, а не цветной, так что у меня просто нет достаточного опыта, чтобы рекомендовать какой-либо выбор. Прекрасно работает установка всех цветов переднего плана равными основному черному (RGB-значения для него (0,0,0)), но я не хотел бы устанавливать их все равными базовому белому (RGB-значения (255,255,255)). Пока я временно, использую чисто белый цвет в качестве фонового для «негативных» пиктограмм (dvi, gf, pk, vf), и не совсем белый (off-white) цвет (RGB (230,230,230)) в качестве фонового для пиктограмм протокольных файлов. Пиктограммы для Т^’овских и МЕТАРОМТ’овских файлов исходного текста имеют фоновые RGB-значения (200,200,255), в цветном варианте отвечающие светло-голубому; пиктограммы для шрифтовых метрических файлов и для ГАТ^Х’овских файлов с исходным текстом — фоновые RGB-значения (255,200,200), т. е. соответствующие светло-красному при цветном воспроизведении. Для МЕТАРОМТ’овских файлов с исходным текстом мне придется, наверное, выбрать один из оттенков оранжевого цвета, что будет соответствовать обложке книги The METAFONTbook. На моем полутоновом монохромном мониторе мне пришлось несколько осветлить фоновый цвет, присвоенный системным программным обеспечением объектным файлам языка С, а также файлам дампа ядра (*.о и core*); кроме того, на моем полутоновом мониторе невозможно разглядеть детали системных пиктограмм
По-видимому, другим пользователям придется подстроить цвета переднего плана и фона, чтобы они гармонировали с общим стилем оформления их «рабочего стола».
Первый опыт общения с персональной графической станцией я приобрел в 1989 г., после чего сразу же решил сделать растровые пиктограммы размера 64x64 бита для Т^Х’аи METAFONT’a —для самих этих систем, не для файлов, с которыми они работают. Однако мне всегда казалось более удобным запускать Т^Х и METAFONT из командного процессора операционной системы UNIX, так что эти ранние пиктограммы так никогда и не использовались. Вот они, все еще ожидающие подходящего оправдания своему raison d’etre1:
1 Смысл существования (франц.). — Прим, перев.
Все пиктограммы, показанные выше, за исключением уже имеющихся в каталоге /usr/openwin/share/include/images дистрибутива системы OpenWindows фирмы Sun Microsystems, можно получить с анонимного ftp- сервера из каталога ~ftp/pub/tex/icons в labrea.stanford.edu по Интернету. Этот каталог содержит также файл с именем cetex. ascii, который можно использовать для установки рассматриваемых пиктограмм в OpenWindows. Данная операция может быть выполнена, если исполнить команду ‘ce_db_merge system -from_ascii cetex. ascii’.
Литература
CWEB, свободно распростаняем, доступен по анонимному ftp в директории ~ftр/pub/cweb at labrea.stanford.edu.
Donald E. Knuth, Literate Programming, CSLI Lecture Notes 27 (Stanford, California: Center for the Study of Language and Information, 1992). Distributed by Cambridge University Press.
Donald E. Knuth, The Stanford GraphBase: A Platform for Combinatorial Computing (New York: ACM Press, 1993).
Donald E. Knuth and Silvio Levy, The CWEB System of Structured Documentation, Version 3.0 (Reading, Massachusetts: Addison—Wesley, 1993). Обновленная версия доступна в [1].
Добавление
Пиктограммы, разработанные для OpenWindows, усилиями ряда добровольных помощников к настоящему времени адаптированы и для других «оконных систем» (window systems). Их можно найти, например, в каталоге support/icons/ архива CTAN.
Компьютеры и книгоиздание
[Заметки, представленные 21 мая 1986 г. Музею компьютеров в Бостоне, шт. Массачуссетс, как часть «выпускного вечера» в честь завершения Т^’а. Первоначально опубликовано в TUGboat 7 (1986), 95-98.]
Заглавие книг, послуживших поводом для сегодняшнего торжества, — Computers &; Typesetting (Компьютеры и книгоиздание). И поскольку наша встреча происходит в Музее компьютеров, вполне уместно вспомнить, мне думается, что компьютеры были непосредственно связаны с типографским набором с самого их зарождения.
Всякий, кто начинает изучать историю компьютеров, очень скоро обнаруживает, что многие ключевые идеи в этой области восходят к Англии XIX в., где Чарльз Беббидж разработал так называемую «разностную машину» и занимался разработкой «аналитической машины». Машины Беббиджа так никогда и нс были построены, однако шведский писатель и издатель Георг Шойц, прочитав об этих работах, настолько заинтересовался ими, что вместе со своим сыном изготовил действующий образец разностной машины. Именно этот прибор был первым настоящим вычислительным устройством, построенным в Швеции. Здесь самое интересное, для меня по крайней мере, что машина Шойца выдавала результат не в виде перфокарт или чего-нибудь еще в этом роде, а изготавливала фактически печатные формы для стереотипии, с которых можно было печатать книги!
И в самом деле, при помощи этой ранней шведской машины было напечатано несколько книг. Они демонстрировались на Всемирной выставке в Париже в 1856 г. Сувенирный альбом этой выставки содержал такой пассаж: «Эта по
чти интеллектуальная машина не только может в течение секунд выполнить вычисления, на которые обычно нужны часы, она печатает также полученные результаты, соединяя достоинства четкой каллиграфии с преимуществами безошибочных вычислений»* Я скопировал небольшую часть страницы из самой первой книги, подготовленной с помощью компьютера — отпечатанной в 1857 г. — и можно увидеть воочию, как далеко мы ушли с тех пор:2
2405
38112
2406
38130
2407
38148
2408
38166
2409
38184
Насколько я знаю, таблицы в этой книге представляют собой первый дошедший до наших дней образец вывода результатов из автоматического вычислительного устройства?
Leon Brisse, Alburn de PExposition Universelle (Paris, 1857), 194. (Цитируется в Uta C. Merzbach, Georg Scheutz and the First Printing Calculator [Washington: Smithsonian Institution Press, 1977].)
George and Edward Scheutz, Specimens of Tables, Calculated, Sterecrnoulded, and Printed by Machinery (London: Longman, Brown, Green, Longmans, & Roberts, 1857), 50 pp. Пример, воспроизводимый здесь, взят из главной таблицы на с. 13-42, которая содержит логарифмы целых чисел от 1 до 10000. В книгу включены также еще 14 таблиц, чтобы подтвердить разносторонний характер возможностей такого рода машин, выдающих результаты расчетов со скоростью 120 чисел в час. Публичная библиотека в Бостоне располагает двумя экземплярами данной книги, посланной первоначально Эдвардом Шойцем Бенджамину Гоулду — датировано 1 мая 1857 г., и в библиотеку Nathaniel Bowditch Library (датировано 11 мая).
Это не так. Позднее я обнаружил, что Георг Шойц опубликовал до того момента короткую таблицу, которая была рассчитана и отпечатана стереотипическим способом на первой машине, которую они с сыном сделали совместно. Страница 74 его книги Nytt och Enkelt Satt att Losa Nummereqvationer af Hogre och Lagre Grader (Stockholm: J. L. Brudins, 1849), содержит значения полинома т3 — Зт2 +90037.Т для 1 < х < 25; начало этой таблицы выглядит следующим образом:
1 90035
2 180070
3 270111
4 360164
5 450235
Самый первый пример автоматически отпечатанной таблицы был предложен вниманию историков вычислительной техники Майклом Линдгреном, книга которого Glory and Failure (Stockholm: Royal Institute of Technology Library, 1987) содержит очаровательные подробности, касающиеся всех аспектов машин Шойца. В частности, на с. 150 книги Линдгрена приведена фотография подлинной стереотипной пластины, которая использовалась для печати
Я бы хотел также сказать несколько слов об истории моей собственной работы в области применения компьютеров для выполнения допечатных операций издательского процесса. На прошлой неделе я просматривал свой дневник за 1977 г. В записи за 5 мая (это был четверг) сказано: «Начата разработка ТЕХ’а». Из моего дневника следует, что в четверг, пятницу и субботу я интенсивно работал над проектом ТЁХ’а, потом для разрядки сходил в кино. В записи за следующий четверг говорится: «Писал черновой вариант отчета по ТЕХ’у, до 5 часов утра вгонял его в машину». В выходные на той неделе мы с женой отправились в поездку в район Сакраменто по линии Станфордской библиотечной ассоциации. Во время этой поездки нам удалось увидеть много примеров прекрасной печати, именно этот опыт побудил меня просмотреть на следующей неделе гору книг, посвященных разработке шрифтов. Запись в моем дневнике за субботу, 21 мая 1977 г.— ровно за девять лет до сегодняшнего дня — говорит, что к пяти часам утра я завершил «предварительную разработку строчных и прописных латинских букв в прямом и курсивном начертаниях, а также цифр 0-9». После нескольких часов сна я провел остаток субботы за написанием компьютерных программ для растрового вычерчивания кривых. О, как мало я знал тогда про то, насколько трудно будет завершить работу, основные контуры которой я набросал в упомянутые две недели!
Почему я начал работать над Т^Х’ом в 1977 году? Вся эта история началась намного раньше, связана она была с моими книгами The Art of Computer Programming. Я подготовил второе издание-тома 2, но когда я получил его гранки, они выглядели ужасающе — из-за того что за прошедшие со времени выхода первого издания годы технология книгопечатания изменилась радикально. Книги готовились теперь с помощью фотонабора, вместо машин Monotype с горячей отливкой строк; и (увы!) они теперь набирались не вручную, а с помощью компьютера. Результатом была совершенно неудовлетворительная расстановка пробелов, особенно в математических формулах, а шрифты, использовавшиеся для набора, по сравнению с тем, что было раньше, выглядели просто кошмарно. Я был совершенно обескуражен этим и не знал, что предпринять. Издательство Addison—Wesley предлагало перенабрать все заново с использованием старого метода, на котором основана машина Monotype. Я, однако, понимал, что старый подход долго не проживет, т. е. к моменту, когда я должен буду завершить работу над томом 4, эта же проблема возникнет опять, но у меня не было никакого желания писать книгу, которая будет выглядеть так, как те самые гранки, о которых я уже упоминал.
И тут произошло одно замечательное событие. Я состоял в комиссии по пересмотру набора экзаменационных вопросов и задач по профилю нашей кафедры. Одно из дел, которые надо было выполнить в ходе работы комиссии,
демонстрационной таблицы в 1849 г.; головные нули в этой таблице удалялись (стирались) вручную! (В 1857 г. уже было не так). Отметим, что индивидуальные цифры в распечатке Шойца 1849 г. выровнены гораздо лучше, чем в распечатке 1857 г. Можно предположить, что с короткой демонстрационной таблицей машина справлялась намного успешнее, чем с длинной таблицей, требующей многочасовых расчетов.
заключалось в оценке книги Пэта Уинстона по искусственному интеллекту. Мы получили гранки этой книги и суть произошедшего события заключалась в том, что эти гранки были подготовлены с помощью новой машины (в Южной Калифорнии, на фирме Information Internation, Inc.) с высокой разрешающей способностью. Похоже, что один из студентов Уинстона в М. I. Т. слетал в Лос- Анджелес, имея при себе текст книги на магнитной ленте, и результатом были как раз те самые гранки, которые попали к нам в руки. Я, конечно, имел значительный опыт растровой печати, но только на устройствах с невысокой разрешающей способностью. Поэтому мне казалось, что печать с помощью растровых представлений была чем-то вроде забавной аппроксимации «настоящего» типографского набора. Когда же я увидел гранки книги Уинстона, то был поражен высоким качеством печати: «цифровая» (дискретная) природа ее была совершенно неразличима. Фактически, цифровой набор выглядел несравненно лучше, чем то, что я мог наблюдать в гранках моей собственной книги.
Цифровой типографский набор означает работу с объектами, состоящими из нулей и единиц, и вообще вся вычислительная наука может трактоваться как наука об изучении такого рода объектов, элементами которых являются только 0 и 1. И здесь я впервые понял, что как специалист в области программирования и информатики вполне в состоянии помочь решить проблему высококачественной печати, так сильно беспокоившую меня. Мне не надо знать металлургию, оптику,.химию или еще что-нибудь такое же «страшное»; все, что мне надо сделать, — сконструировать правильный объект из нулей и единиц, после чего послать его на исполнение на цифровую печатающую машину с высокой разрешающей способностью, наподобие той машины из Южной Калифорнии. Тогда я смогу получать свои книги в таком виде, в каком захочу. Другими словами, проблема высококачественной печати свелась к проблеме манипулирования с нулями и единицами. Значит, для специалиста по вычислительным наукам вроде меня это почти что долг — тщательно изучить данную проблему.
В течение недели с того момента, как я увидел гранки книги Уинстона, я решил бросить все остальное и работать над проблемой цифровой типографии. К сожалению, профессор Уинстон не смог присутствовать здесь сегодня, но я должен сказать: Пэт, я не знаю как тебя и благодарить за то, что ты написал ту книгу!
ТЁХ’овский исследовательский проект сразу же после его начала в 1977 году преследовал две основные цели. Первой из этих целей было качество: мы хотели получить документы не просто высокого, но высшего качества. Были времена, когда компьютеры имели дело только с числами; несколько лет прошло, прежде чем к цифрам прибавились прописные (заглавные) буквы; через какое-то время им стали доступны также и строчные (малые) буквы; затем компьютеры освоили буквы переменной ширины (пропорциональные шрифты) и к 1977 году уже существовал ряд систем, позволявших получать радующие глаз документы. Моей целью было сделать завершающий шаг на пути к формированию печатных документов наивысшего качества.
Оказалось, что достичь этого уровня качества в смысле форматирования текста не так уж и сложно: сделать это удалось после двух лет работы. К примеру, мы экспериментировали с журналом Time, пытаясь показать, что Time мог бы выглядеть намного лучше, если бы набирался с помощью ТЁХ’а. Но оказалось также, что разработка подходящих семейств шрифтов существенно сложнее, чем я ожидал, —прошло семь лет, прежде чем удалось получить формы букв, удовлетворявшие меня.
Вторая главная цель проекта — «архивная»: она состояла в том, чтобы создать комплекс систем, которые были бы в максимальной степени независимыми от изменений в технологии книгопечатания. Когда придет следующее поколение печатающих устройств, я хотел бы быть в состоянии удержать достигнутый уровень качества, вместо того чтобы решать все проблемы заново. Я хотел создать нечто такое, что будет пригодно к употреблению и через 100 лет. Другими словами, моя цель была так поставить дело, чтобы по спецификациям книги, зафиксированным сейчас, наши потомки смогли бы отпечатать точно такую же книгу и в 2086 году. Хотя я и ожидаю непрерывного развития средств «препроцессорного характера» для Т^Х’а и METAFONT’a, равно как и средств «постпроцессорного характера» или драйверов устройств, работающих с выходом этих систем, сами системы Т^Х и METAFONT не должны изменяться вообще — они должны быть островками стабильности между теми и другими, которым можно с полным основанием доверять и строить на их основе требуемые системы. .
Сегодня я уже вполне могу похвастаться и сказать, что цели высшего качества и технологической независимости, на мой взгляд, достигнуты, а тома А, В, С, D, Е рассказывают всем о том, как это было сделано. Сегодня я вижу эти книги впервые и счастлив, что все вы здесь собрались, чтобы отметить со мной это событие. Эти книги не совсем обычны —они описывают самих себя, рассказывают в точности о том, как они были набраны. Все форматирование было выполнено системой Т^Х, описанной в томах А и В. Далее, каждая буква и каждый символ, появляющиеся в этих пяти томах, а также на обложках и суперобложках, были подготовлены с помощью системы METAFONT, описанной в томах С и D. Том Е повествует, как я, образно говоря, «расставил все точки над i и нарисовал все палочки у t». Если бы экземпляры этих книг послать на Марс, марсиане смогли бы использовать их, чтобы воссоздать объекты из нулей и единиц, использовавшиеся при издании книги. В этих книгах содержится все существенное, чему я научился за последние девять лет.
Все методы, описанные в этих книгах, относятся к классу общедоступных (public domain), т. е. идеями, изложенными в них, может воспользоваться любой, у кого возникнет такая потребность. Единственное, что я оставил за собой — контроль за использованием имен Т^Х и METAFONT: программные продукты, выходящие под любым из этих имен, обязаны отвечать соответствующему стандарту. Я не буду возражать против любых изменений, если только измененные системы не будут именоваться Т^Х или METAFONT.
Тома А и С представляют собой руководства пользователя рассматриваемых систем. Я пытался так написать эти руководства, чтобы они отвечали потребностям пользователя на всех ступенях его роста по мере освоения систем.
И я очень тщательно отбирал цитаты из работ других авторов, содержащиеся в конце каждой главы.
Тома В и D содержат полные листинги ТЁХ’а и METAFONT’a. Эти книги ориентированы, в основном, на профессиональных программистов, а не на случайных пользователей, и я особенно доволен тем, что эти книги все-таки появились, поскольку они представляют собой «побочный продукт» моих исследований. Я и не помышлял о нем, когда девять лет назад начинал свою работу. Приступив к написанию программ для ТЁХ’а и METAFONT’a, я захотел разработать системы, которые отвечали бы современному уровню развития компьютерного программирования. Попытки достичь эту цель привели к созданию системы WEB, реализующей концепцию структурированного документирования. Я думаю, что WEB может оказаться наиболее важным достижением всего проекта — более важным в долгосрочной перспективе, чем ТЁХ и METAFONT сами по себе, — поскольку WEB реализует новый подход к созданию программного обеспечения, и я полагаю, что этот путь и в самом деле лучше любого другого. Использование WEB дало возможность писать программы настолько удобочитаемые, что есть уже довольно много людей, понимающих намного лучше внутренние механизмы ТЁХ’а, чем других программных систем сопоставимых размеров. Более того, мне кажется, что имеются все основания утверждать — мобильность, удобство сопровождения, надежность ТЁХ’а и METAFONT’a обеспечены именно применением WEB-подхода, причем на таком уровне, какого нет сейчас ни у какого другого программного комплекса. Программы ТЁХ’а и METAFONT’a работают и дают практически идентичные результаты почти на всех больших компьютерах; есть уже тысячи пользователей, и несмотря на столь массовое «тестирование», число обнаруживаемых ошибок не превышает одной в два года. Я думаю, что по крайней мере еще-то одна ошибка в ТЁХ’е есть, и намерен заплатить $20.48 первому из лиц, обнаруживших ее. (В следующем году вознаграждение будет удвоено и составит $40.96 и т. д.)
Тома В и D содержат также и другие новшества, улучшающие ранее созданную систему WEB. Каждый разворот книги снабжен мини-указателем, размещенным на правой странице разворота. Наличие таких мини-указателей позволяет упростить работу с перекрестными ссылками на другие страницы книги, это экономит много времени, которое пришлось бы потратить в противном случае, обращаясь к главному указателю в конце книги.
В последние годы я развиваю точку зрения на программы, как на своеобразный вид литературы. Хотя Пулитцеровскую премию за наилучшую программу года все еще не присуждают, я попытался написать тома В и D таким образом, как если бы ее в самом деле уже давали программистам! Говоря более серьезно, я намеревался построить эти книги так, чтобы они были полезны специалистам в области информатики и программирования для самостоятельного изучения, а также для использования их в качестве учебных пособий на
семинарских (практических) занятиях в колледжах. В частности, в томе D имеется хороший материал для групп сильных студентов.
Пятый том, том Е, является наиболее привлекательным и интересным из всех томов рассматриваемой серии. Я надеюсь, что все вы просматривали эти тома, так что понимаете, что я имею в виду. METAFONT — это компьютерный язык, не очень похожий на все остальные языки этого класса. По этой причине мне пришлось подготовить и дать большое число примеров, поясняющих, как можно использовать METAFONT для получения высококачественных шрифтов. В томе Е содержится свыше 500 примеров, поясняющих практически все буквы, цифры, знаки препинания и другие символы, использовавшиеся при печатании книг рассматриваемой серии.
Шрифты, порождаемые этими программами, получили общее наименование «Computer Modern». Мой коллега Чарльз Бигелоу написал для тома Е введение, рассказывающее о шрифтах вида Modern вообще. В этой книге объясняется, как можно вносить изменения в указанные шрифты, которые были разработаны с большим числом варьируемых параметров, так что возможности менять их практически безграничны. В конце книги даны страницы, показывающие, как выглядят образцы литер из 75 стандартных шрифтовых раскладок семейства Computer Modern. Можно осуществить и еще многие тысячи дополнительных изменений, если возникнет такая необходимость, причем делается это очень просто.
Если даже вы не будете читать METAFONT’obckhx программ в этой книге (томе Е), я думаю, что она все равно привлечет ваше внимание иллюстрациями, показывающими разработанные шрифты} а также осознанием того, что программа на странице, посвященной какой-либо из букв, и есть именно та, с помощью которой данная буква была «нарисована». В какой-то степени это дает чувство удовлетворения от завершенности и упорядоченности полученных результатов.
Еще одно важное обстоятельство, о котором я хотел бы сказать в это утро — ПОМОЩЬ. Мне очень и очень много помогали — буквально сотни людей, добровольно участвовавших в этом проекте самыми разнообразными способами—начиная с Ганса Вольфа из издательства Addison—Wesley, обучившего меня тонкостям работы с системами Monotype, которые использовались при подготовке первого издания книги The Art of Computer Programming в 60-х гг. Мне особенно повезло в работе над шрифтами, где я имел весьма ощутимую поддержку от таких признанных авторитетов в этой области, как Герман Цапф и Мэтью Картер.
Важнейшую роль сыграло и то, что в реализации проекта принимали участие такие выдающиеся научные сотрудники, как Дэвид Фукс, Джон Хобби и ряд других. Далее, мой исследовательский проект, реализовывавшийся в Станфорде, имел солидную финансовую поддержку. Наиболее заметна она была от Национального научного фонда США (National Science Foundation) и Фонда
Иллюстрации, о которых идет речь, формировались отдельно от текста примеров и затем вставлялись в них. Для одновременного формирования растровых изображений и текста возможностей вычислительных машин, существовавших в то время (1985 г.) было недостаточно. — Прим. ред. английского издания.
развития систем (System Development Foundation). С такой внушительной поддержкой провалить проект было просто невозможно. Очень большую помощь и поддержку я получил также от моей жены Джил. (В следующем месяце мы празднуем 25-летие со дня нашей свадьбы!)
И еще одно заключительное замечание. Меня часто спрашивают, почему в качестве символов для ТЩХ’а и METAFONT’a выбраны, соответственно, лев и львица. Когда художник Дуэйн Бибби в первый раз пришел ко мне с «львиной идеей», я инстинктивно почувствовал, что идея эта правильная, хотя и не мог понять точно, почему же именно, пока спустя месяц не оказался в Бостонской публичной библиотеке. Я прошел мимо величественных каменных львов, сидящих* у главной лестницы, и подумал: «Вот оно! Т^Х и METAFONT попытаются стать, подобно этим львам, «опорой» великой библиотеки? Я люблю книги, и львы символизируют книги!» Не удивительно, что я счастлив от того, что Т^Х и METAFONT уже приняли участие в подготовке нескольких десятков книг великолепного качества. Мне чрезвычайно приятно ощущать, что этот исследовательский проект, по-видимому, еще не раз будет способствовать созданию в грядущем многих и многих прекрасных книг.
1 Можно также упомянуть львов, величественно стерегущих вход в Нью-Йоркскую публичную библиотеку, которая на той же неделе отмечала свое 75-летие. — Прим. ред. английского издания.
Новые версии
Т^Х’аи METAFONT’a
[Первоначально опубликовано в TUGboat 10 (1989), 325-328; 11 (1990), 12.]
Более пяти лет я пребывал в убеждении, что стабильная система —это много лучше, чем система, продолжающая развиваться. Однако во время конференции TUG в Станфорде (в августе 1989 г.) я был поставлен перед необходимостью внести в эти системы последнюю серию изменений, чтобы довести Т^Х и METAFONT до степени завершенности, отвечающей заявленным для них философии и целям.
Основной побудительной причиной, заставлявшей меня пойти на доработки, был тот факт, что я неверно оценил сравнительные перспективы 7-битовых и 8-битовых наборов символов. Я считал, что стандартный текстовый ввод будет по-прежнему основан на использовании максимум 128 символов, поскольку клавиатура с 256 различными выводами едва ли будет действительно эффективной. Нечего и говорить, что я оказался неправ, особенно для разработок в Европе и Азии. Как только я понял, что программы форматирования текста с 7-битовым вводом быстро станут казаться архаичными, как до того уже было с 6-битовыми, то пришел к выводу, что основательной переработки не избежать.
Однако «7-битовая догма» распространилась повсюду, так что пришлось взять существующие программы и заняться тщательной их переработкой в 8-битовом стиле. Это привело Т^Х «под нож» на мой «операционный стол» впервые после 1984 г., и я получил последнюю возможность включить в‘него несколько нововведений, которые пришли мне в голову, либо были подсказаны пользователями после указанной даты.
Требуемые расширения должны были вводиться в ТЁК и METAFONT таким образом, чтобы сохранилась совместимость старой и новой версий снизу вверх (с несколькими небольшими исключениями, о которых упоминается ниже). Это значит, что свободный от ошибок входной файл для старых ТЕХ’а и МЕТА- FONT’a таковым же должен оставаться и для их новых вариантов, причем получаемые результаты в обоих случаях должны быть идентичными.
В то же время любой, кто отважится использовать вновь введенные расширения ТУХ’а и METAFONT’a, теряет возможность получить требуемые результаты с помощью старых версий этих систем. По этой причине я настоя-
тельно прошу ТЁХ’овское сообщество обновить все имеющиеся в употреблении системы возможно быстрее. Надо выкорчевать и уничтожить их устаревшие 7-битовые версии, несмотря на то, что мы успели сделать с их помощью много прекрасных вещей.
В этих заметках я собираюсь обсудить последовательно те изменения, которые были внесены в ТЁХ и МЕТА FONT. После этого я опишу имеющиеся исключения и совместимость версий систем снизу вверх.
Набор символов
Для записи входных файлов теперь разрешается использовать символы, взятые из набора, в который могут входить до 256 различных символов. Коды символов, которые ранее ограничивались диапазоном 0.. 127, теперь можно брать из диапазона 0.. 255. Все символы равноправны; вы можете свободно использовать в ТуСе любой символ для любой цели, задавая для него соответствующие значения \catcode, \mathcode, \lccode, \uccode, \sfcode и \delcode. Plain ТЁК инициализирует эти кодовые значения для символов с кодами больше 127 точно таким же образом, как и коды для обыкновенных знаков препинания наподобие ‘Р.
Введены новые соглашения для ввода произвольного 8-битового символа в ТЁК, когда нет возможности задать его с клавиатуры непосредственно. Четыре последовательно идущих символа **а;/3, где а и /3 —любые «шестнадцатеричные цифры в нижнем регистре» (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, с, d, е, f), интерпретируются ТЁХ’ом при чтении входного файла так, как если бы они были единым символом с кодом, задаваемым данными цифрами. Например, **80 дает десятичный код символа 128; весь символьный набор лежит в диапазоне кодов от **00 до **ff. Согласно старым соглашениям, обсуждаемым в приложении С, символ 0 кодируется как **@, символ 1 (control-А) — как **А, ..., а символ 127 —как **?. Эти соглашения по-прежнему остаются в силе для первых 128 символьных кодов, за исключением того, что символ, следующий за **, не должен быть шестнадцатеричной цифрой в нижнем регистре, если сразу же идущий за ней символ опять представляет собой такого же рода шестнадцатеричную цифру.
На METAFONT существование 8-битовых символов оказало меньшее воздействие, чем на ТЁК, поскольку METAFONT’obckhc символьные классы являются встроенными для каждого конкретного экземпляра системы. Нормальный набор из 95 печатаемых символов, описанный на с. 51 книги The METAFONTbook, может быть дополнен расширенными символами так, как это описано на с. 282, однако делается это весьма редко, поскольку приводит к возникновению ряда проблем с мобильностью текстов, использующих эти символы. МЕТАЕОЫТ’овский оператор char теперь переопределен, чтобы работать с modulo 256 вместо modulo 128.
Таблицы переносов
В ТЁХ’е теперь допускается до 256 различных наборов правил переноса. Введен новый целочисленный параметр \language, текущее значение которого однозначно определяет используемую таблицу переносов. Если параметр \language имеет значение отрицательное или большее 255, Т^Х действует так, как если бы данный параметр был равным нулю.
Когда вы перечисляете с помощью ТЁХ’овской команды-примитива \hyphenation исключения из правил переноса, то эти исключения относятся только к текущему языку. Аналогично, примитив \patterns сообщает ТЁХ’у, что надо запомнить новые образцы переносов для текущего языка; выполнение этой операции допускается только в специальной программе «инициализации», именуемой INITEX. Исключения из правил переноса могут быть добавлены в любой момент, однако новые образцы после того, как таблица переносов уже сформирована, добавить нельзя.
Когда ТЁК читает текст абзаца, он автоматически вставляет «whatsit nodes» (узлы типа «что такое») в горизонтальный список для данного абзаца во всех случаях, когда для обрабатываемого символа значение параметра \language будет не таким, как у его предшественника. Таким способом ТЁК получает возможность знать, какие правила переноса следует применять для каждого из слов данного абзаца, даже если вы часто выполняете переключения между большим количеством различных языков.
Специальные узлы типа whatsit автоматически вставляются в неограниченном горизонтальном режиме (т. е. когда вы задаете абзац, а не когда задаете содержимое блока hbox). Вы можете вручную вставить специальный узел whatsit в ограниченном горизонтальном режиме, если воспользуетесь командой \set language (number). Это потребуется только в том случае, если вы пытаетесь делать нечто замысловатое, вроде вывода из состава блока некоторых элементов абзаца.
Управление уровнем фрагментации слов при переносах
ТЁК имеет два новых параметра \lefthyphenmin и \righthyphenmin, задающих длину наименьшего фрагмента слова в его начале (\lefthyphenmin) и конце (\righthyphenmin), которые можно оставлять на предыдущей строке или переносить на следующую строку, соответственно. Ранее значения этих параметров \lefthyphenmin=2 и \righthyphenmin=3 были жестко встроены в 1ЁХ и изменить их было нельзя. Теперь plain Т^Х поддерживает указанные старые значения, которые по-прежнему рекомендуются для большинства публикаций в США, однако вы можете увеличить число переносов путем уменьшения значений упомянутых выше параметров, точно так же как и сократить число переносов увеличением значений этих параметров. Если сумма значений параметров \lef thyphenmin и \righthyphenmin равна 63 или более, все переносы подавляются. (Вы можете также отключить переносы, используя шрифт
с \hyphenchar=-l или путем переключения с помощью команды \language на язык, где не заданы образцы переносов или исключения из правил переноса.)
Большее разнообразие лигатур
Теперь о наиболее значительном изменении. В предыдущих версиях ТЦК’а был только один вид лигатур, для которого два символа, подобных ‘Г и ‘i’, преобразовывались в один символ наподобие ‘fi’, если указанные символы шли в тексте один за другим. Новый ТЦК понимает значительно более сложные конструкции, с помощью которых, например, есть возможность изменить символ ‘j’, следующий за ‘f’, на «бесточечный» символ ‘j’ и получить (при оставшемся неизменным символе Т) символ ‘fj’.
Как и прежде, лигатуры можно вводить только тогда, когда для них есть соответствия в используемом шрифте. Перед тем как рассмотреть новые средства METAFONT’a, позволяющие создавать улучшенные лигатуры, обратимся к старым соглашениям. Программист, работающий с METAFONT’om, всегда имеет возможность задать «программу лигатуры/кернинга» для любого символа из создаваемого шрифта. Если, например, комбинация ‘fi’ появляется в шрифтовой позиции 12, замена ‘f’ и ‘Г на ‘fi’ задается включением следующего оператора
=: 12
в программе лигатуры/кернинга для "f "; это — существующее соглашение, принятое в METAFONT’e.
Новые лигатуры позволяют сохранять один или оба исходных символа, а также вставить новый символ. Вместо =: можно теперь написать | = :, если требуется сохранить левый символ, или =: | — если правый, или | = : | — если надо сохранить оба символа. Например, если бесточечная буква ‘j’ содержится в позиции 17 соответствующего шрифта, можно получить результат типа описанного выше, поместив оператор
| = : 17
в программу лигатуры/кернинга для символа ‘Г.
Имеются еще четыре дополнительных оператора
1 = :>,=:1>,1=:1>,1 = : I»,
где каждый из символов > сообщает ТЁХ’у, что надо сдвинуть его «фокус внимания» на одну позицию вправо. Например, после замены f и j на f и бесточеч- ную j, как это было описано выше, ТЦК опять начинает исполнять программу лигатуры/кернинга, вставляя, возможно, кернинг перед бесточечной буквой j, или, может быть, заменяя f совсем на другой символ и т. п. Но если вместо
этого будет выдана инструкция
| = :> 17
Т^Х немедленно перейдет к программе лигатуры/кернинга для символов, следующих за символом 17 (бесточечная буква j); никаких изменений больше не будет вноситься между f и j, даже если в шрифте и определено что-то специфическое именно для этой комбинации.
Граничные лигатуры
Каждая строка из последовательно расположенных «символов», читаемых ТЁХ’ом в горизонтальном режиме (после развертывания текста макрокоманд) может быть названа «словом». В техническом плане в данном определении «символом» мы считаем символ, для которого код категории \ cat code соответствует буквам или «прочим символам», или командную последовательность, которая устанавливается (с помощью команды \let) равной такого рода символам, или командную последовательность, определяемую с помощью команды \chardef, или конструкцию вида \ char (number). Новый Tf^X теперь полагает, что имеется некий невидимый «левый граничный символ» непосредственно перед таким словом, а также «правый граничный символ» сразу после него. Эти граничные символы будут как-то влиять на ситуацию, если разработчик шрифта определил лигатуры и/или кернинг между ними и смежными с ними буквами. Это значит, что теперь имеется возможность автоматически воздействовать на начертание первого или последнего символа в слове.
Программа лигатуры/кернинга для левого граничного символа задается в METAFONT’e с помощью специальной метки I I : в команде ligtable. Лигатура или кернинг с правым граничным символом определяется присваиванием значения новому МЕТАРОЫТ’овскому внутреннему параметру boundary char, а также заданием лигатуры или кернинга для сочетаний с этим символом. Граничный символ может существовать, а может и не существовать как реально имеющийся символ в данном шрифте.
Например, пусть требуется изменить первую букву в слове с Т’ на ‘ff’, если мы занимаемся подготовкой некоторого текста на староанглийском языке. При разработке шрифта с помощью METAFONT’a можно было бы записать
ligtable ||: "F" | = : 11
если символ с десятичным номером 11 есть ‘ff’. Точно такая же инструкция ligtable должна появиться в программах для символов типа (, ‘, “ и -, которые могут предшествовать буквенным строкам; тогда вместо ‘Bassington-French’ получим ‘Bassington-ffrench’.
Если буква ‘s’ в нашем шрифте —это s из текстов до начала XIX века, которая выглядит подобно искалеченной ‘Г (а именно, ‘f’), и если в позиции 128 у нас современная буква ‘s’, в окончательном варианте можно преобразовать
конечные буквы s так, как это сделал Бен Франклин, вводя такие инструкции для лигатуры
boundarychar := 255;
ligtable "s": 255 = : I 128, =:| 128, "," =:I 128, ")" =:I 128, "’" =:I 128,
и т/д. (Настоящий шрифт для текстов в старинном стиле будет также содержать лигатуры для ss, si, si, ssi, ssl, sh и, возможно, для st; было бы интересно разработать шрифт Computer Modern Oldftyle, может быть, для рекламных профпектов к Рождефтву (Chriftmas newfletter.))
Неявный левый граничный символ опускается ТЁХ’ом, если непосредственно перед словом поставить команду \noboundary; точно так же можно убрать неявный (подразумеваемый) правый граничный символ, "записав эту команду сразу после соответствующего слова.
Более компактные таблицы лигатур
Один и тот же общий код может теперь совместно использоваться двумя или более таблицами лигатур. Чтобы воспользоваться этим в METAFONT’e, надо сначала записать ‘skipto (п)’ в конце текста одной команды ligtable, а потом указать ^пу. :’ внутри другой такой команды. Такие локальные метки могут быть повторно используемыми, например, можно написать skipto 1 снова после того, как появилась метка 1::, что вызовет пропуск следующего появления этой метки 1: :. Имеется 256 локальных меток, перенумерованных от 0 до 255. Ограничение: Между инструкцией skipto и отвечающей ей меткой может быть помещено самое большее 128 команд лигатуры/кернинга.
Формат TFM-файла расширен (с учетом требования совместимости снизу вверх) таким образом, чтобы допускать более 32500 команд лигатуры/кернинга на каждый шрифт. (Ранее действовавшее ограничение составляло 256 команд.)
Улучшение управления видом абзацев
Теперь имеется более эффективный, чем это было раньше, способ, позволяющий предотвращать появление переполненных боксов. Он рассчитан на тех авторов, которые не хотят тщательно просматривать полученный документ и исправлять вручную неудачные разбиения на строки. Подобные авторы раньше пытались добиться этого установкой значения параметра \tolerance=10000, но результат получался ужасный, поскольку ТЁК в этой ситуации будет стараться объединить все плохости в одной действительно кошмарной строке текста. (ТЁК считает все плохости > 10000 бесконечно плохими, причем все эти «бесконечности» равны между собой).
Более приемлемый способ дает использование нового параметра-размера \ emergency stretch. Если значение этого параметра положительно, и если у ТуХ’а не получается набрать абзац, не выходя за границы установленных допусков, будет предпринята повторная обработка данного абзаца, в ходе которой ТУХ попытается каждой строке дать дополнительную растяжимость, равную значению параметра \ emergency st г etch. Эта мера позволяет уменьшить все плохости до уровня, когда ранее бесконечные значения станут конечными. ТУХ будет искать оптимальное решение задачи уменьшенного размера и оно обычно будет достаточно хорошим с практической точки зрения. Избыточная растяжимость здесь реально присутствовать не будет; следовательно, боксы с недо- заполнением («жидкие боксы») будут вызывать появление предупреждающих сообщений до тех пор, пока не будет увеличено значение параметра \hbadness.
Возможность проверить плохость
ТУХ получил новый целочисленный параметр с именем \badness, в котором содержится значение плохости для бокса, сформированного последним. Если бокс оказался переполненным, значение данного параметра для него будет 1000000; в противном случае параметр \badness будет иметь значение между 0 и 10000.
Возможность проверить номер строки
ТУХ получил также новый внутренний целочисленный параметр с именем \inputlineno, значением которого является номер строки, которую ТУХ показал бы в сообщении об ошибке, если бы какая-либо ошибка произошла сейчас. Этот параметр и параметр \badness имеют статус «только для чтения» аналогично параметру \lastpenalty: вы можете использовать его в соответствующем контексте как проверяемое число, например в выражении ‘ ifnum\inputlineno>\badness . . . \f i’ или ‘\the\inputlineno’, но устанавливать им новые значения нельзя.
Возможность управления объемом контекста, выдаваемого в сообщениях об ошибках
Имеется новый параметр с именем \errorcontextlines, имеющий целочисленное значение и задающий максимальное число пар строк контекста, выдаваемых ТуХ’овскими сообщениями об ошибке (в дополнение к верхней и нижней строкам, которые обычно появляются). В plain ТуХ’е сейчас принято \errorcontextlines=5, но форматирующие пакеты более высокого уровня могут предпочесть значение этого параметра \errorcontextlines=l или даже \errorcontextlines=0. В последнем случае ошибка, которая вызывала ранее выдачу двух или трех пар строк контекста, будет теперь выдаваться в следу-
ющей форме:
! Error.
<somewhere> The \top
line
1.123 \The bottom line.
Если \errorcontextlines<0, вы не увидите здесь даже и строки ‘ . . . ’.
Возможность
повторного использования страниц
Еще один новый целочисленный параметр, получивший наименование \holdinginserts, завершает рассматриваемый набор вновь введенных параметров. Если \holdinginserts>0, то ТЕХ помещает текущую страницу в бокс \box255, который будет использоваться программой \output. Никакие из вставляемых узлов ТЕХ перемещать в соответствующие боксы не будет, все такие узлы останутся на своих местах. Разработчики программ вывода могут использовать это средство, когда им понадобится поместить содержимое бокса 255 опять на текущую страницу, чтобы ее восстановить (так как могло потребоваться изменить вертикальный размер страницы \vsize или что-нибудь еще в этом роде).
Исключения из совместимости снизу вверх
Вследствие новых свойств, появившихся у ТЕХ’а и METAFONT’a, некоторые средства этих систем работают несколько по-другому по сравнению с тем, что было раньше. Я попробую перечислить здесь все случаи подобного рода (за исключением случаев, когда ТЕХ или МЕТА FONT раньше работали некорректно вследствие тех или иных ошибок, присутствовавших в них). Мне неизвестны какие-либо случаи реального влияния этих изменений на деятельность пользователей, поскольку все они, пожалуй, таковы, что понятны лишь посвященным.
ТЕХ использовал преобразование символьных строк **0, **1, ..., **9, **а, **b, **с, **d, **е, "Ч в соответствующие одиночные символы р, q, ..., у, !,",#,$, %, &. Эта операция больше не выполняется, если следующий символ представляет собой одну из шестнадцатеричных цифр 0123456789abcdef.
ТЕХ не вставлял никакого символа в конце входной строки, если \endlinechar>127. Теперь он выполняет эту операцию до тех пор, пока не станет истинным условие \endlinechar>255. Как и раньше, значение \ endline char < 0 подавляет символ-конца-строки (end-of-line character). Обычно это символ с десятичным номером 13 == ASCII control-M = carriage
return (символ возврата каретки).
Некоторые из ТЁХ’овских диагностических сообщений использовали обозначения ["80] .. ["FF], когда ссылались на символы с кодами в диапазоне 128 .. 255 (например, когда отображали содержимое переполнившегося блока, использовались шрифты с такими символами). Вместо этого теперь используется обозначение **80 .. **ff.
Выражения char 128 и char 0 использовались в METAFONT’e как эквивалентные; взамен этого теперь char определен как modulo 256. Следовательно, char — 1 = char 255 и т. д.
INITEX забывал все предыдущие образцы переносов каждый раз, когда выдавалась команда \patterns. Теперь все образцы переносов используются кумулятивно, т. е. их спецификации накапливаются и уже нельзя использовать \patterns после того, как абзац обработан INITEX’om с точки зрения расстановки переносов.
ТеХ действовал немного по-разному при попытках набрать несуществующие символы шрифта. Пропущенный символ рассматривается теперь как граница слова, так что вы получите диагностическое сообщение немного другое, чем в случае, когда \tracingcommands>0.
ТЁХ и МЕТА FONT будут выдавать различную статистику в конце прогона, поскольку они теперь имеют разное число примитивов.
Программы, использующие средства работы с буфером строк системы TANGLE, без внесения в них изменений теперь работать не будут, так как новый вариант TANGLE начинает нумеровать многосимвольные строки с 256 вместо 128.
Чтобы программы INITEX вели себя аналогично тому, как это было в старой версии TgX’a, надо теперь устанавливать значения \lefthyphenmin=2 и \righthyphenmin=3.
Будущее T^X’s и METAFONT’a
[Первоначально опубликовано в TUGboat 11 (1990), 489.]
Моя работа по созданию ТЁХ’а, METAFONT’a и семейства шрифтов Computer Modern подходит к концу. Я не буду вносить в них больше никаких изменений, кроме случаев, когда обнаружатся особо серьезные ошибки.
Я сделал эти системы общедоступными с тем, чтобы люди в любой точке мира могли свободно воспользоваться ими, если пожелают. Я потратил также тысячи часов, чтобы обеспечить возможность получения одного и того же результата на любых компьютерах. Я очень верю в то, что стабильная система, не подвергающаяся изменениям, представляет собой большую ценность, хотя, конечно, любую сложную систему можно улучшить. По этой причине я полагаю, что было бы неразумным вносить дальнейшие «усовершенствования» в системы, именуемые Т^Х и METAFONT. Давайте будем рассматривать эти системы как некие зафиксированные сущности, которые и через 100 лет должны выдавать тот же самый результат, что они дают и сейчас.
Номером текущей версии для Т£Х’а является 3.1, а для METAFONT’a —2.7. Если потребуется внесение каких-либо изменений, следующие версии TgX’a должны иметь номера 3.14, потом 3.141, затем 3.1415, ..., т.е. они должны сходиться к значению отношения длины окружности к ее диаметру. Для METAFONT’a соответствующая последовательность будет иметь такой вид: 2.71, 2.718, ..., т.е. она должна сходиться к основанию натуральных логарифмов. Я намереваюсь нести полную ответственность за все изменения, вносимые в эти системы, до конца моих дней. Я буду периодически изучать отчеты о выявленных ошибках и по результатам этого анализа буду решать, требуется ли осуществить какие-либо изменения. Тем, кто первыми обнаружит те или иные действительно существующие на мой взгляд ошибки, получат вознаграждение, однако я не имею более возможности удваивать ежегодно сумму этого вознаграждения. При разработке любой новой версии я буду передавать ее в главный официальный ТЁХ’овский архив, который в настоящий момент размещается в Станфордском университете. Мой замысел состоит в том, чтобы после моей смерти существующие на тот момент текущие версии Т£Х’а и METAFONT’a были заморожены и оставались бы с тех пор и навсегда неизменными
за исключением того, что номера версий систем, в заголовочных строках соответствующих программ должны принять вид:
ТеХ, Version $\pi$ и
METAFONT, Version $е$
соответственно. С этого момента все «ошибки» в системах следует считать их неотъемлемыми «свойствами».
Как следует из страниц томов В, D и Е, содержащих информацию об авторских правах, любой может свободно использовать мои программы по своему усмотрению, однако аналогичным образом пользоваться именами Т^Х, METAFONT или Computer Modern нельзя. В частности, любое лицо или группа лиц, кто захочет создать программу, которая будет превосходить то, что создано мной, имеет полное право сделать это. Никто, однако, не имеет права назвать вновь разработанную систему ТЁК или METAFONT, если только она не согласуется на 100% с моими собственными программами, как я определил эту согласованность в руководствах по соответствующим системам через тесты TRIP и TRAP. Никому также не разрешается использовать имена шрифтов семейства Computer Modern из тома Е для любых других шрифтов, не порождающих идентичных tfm-файлов. Этот запрет распространяется на всех людей и машины, которые действуют как в рамках TUG, так и любых других организаций. У меня нет намерения передавать ответственность за эксплуатацию ТЁХ’а, METAFONT’a или шрифтов семейства Computer Modern кому бы то ни было.
Конечно, я не утверждаю, что мне удалось найти наилучшие решения для каждой из проблем. Я считаю лишь, что это большое преимущество — располагать в качестве строительного блока некоторым зафиксированным результатом. Со «входной стороны» можно добавлять улучшенные макропакеты, с «выходной стороны» — усовершенствованные драйверы устройств. Я приветствую продолжение исследований, направленных на создание альтернативных систем, которые смогут решать задачи типографского набора и верстки лучше, чем это умеет делать Т^Х. Но авторы таких систем должны подумать и о других именах для них.
Это все, о чем я прошу, после того, как посвятил существенную часть моей жизни созданию этих систем и тому, чтобы сделать их доступными всем. Я искренне надеюсь, что члены TUG помогут мне провести в жизнь высказанные пожелания, оказывая жесткое давление на любое лицо или группу лиц, которые выпустили бы системы, несовместимые с TgX’oM, METAFONT’om или шрифты, несовместимые с семейством Computer Modern, но воспользовались соответствующими именами. При этом совершенно не имеет значения, насколько мала или велика эта несовместимость.
Вопросы и ответы, I
[Заседание ежегодной конференции TUG 25 июля 1995 года в Санкт- Петербурге, Флорида. Впервые опубликовано в TUGboat 17, (1996), 7-22, под редакцией Кристины Тиле на основе магнитофонных записей, сделанных Келвином У. Джексоном и Джереми Гиббонсом. Заседание открыла Барбара Битон.]
Барбара: Я имею удовольствие знать Дона довольно давно. Хочу начать с первого вопроса ... очевидного, а нс о том, что написано на футболке. Как идет работа над четвертым томом? [ Смех. ]
Дон Кнут: Большое спасибо, Барбара. Ты сказала, что большинство присутствующих находятся здесь из-за меня. Я думаю, что й, так же как и любой другой, нахожусь здесь в большой мере из-за Барбары. Она проделала замечательную работу за те годы, когда была редактором журнала TUGboat и координатором многих других дел.
Как вы знаете, я приехал на эту конференцию, потому что после десятой конференции TUG пообещал, что приеду на шестнадцатую, так как это самое
важное число для ученого-компьютерщика. 16 —это не только степень числа 2, это 2 в степени числа 2:
16 = 222.
[ Смех. ] Примерно так можно получить бинарное представление числа, вплоть до 65 536. Числа играют в моей жизни большую роль. Так что это важная встреча для всего проекта ТЁХ’а.
Я задумался о том, что я делал ровно 16 лет назад в этот день. Оказывается, помимо всего прочего, я работал с Барбарой Битон, которая приехала в Станфорд на 2 или 3 недели как представитель Американского математического общества. Она и несколько ее коллег продемонстрировали мне трудности, с которыми они столкнулись при подготовке указателя к Math Reviews. Поэтому 25 июля 1979 г. мы с Барбарой пытались понять, как делать указатель к Math Reviews. Наша работа привела к созданию более мощных средств для отточий и подобных вещей, так как в указателе к Math Reviews немало проблем связано с расположением точек определенным образом в зависимости от количества ссылок. Это было очень интересно, потому что в тот день я нашел две ошибки в ТуСе —под номерами 413 и 414 за всю историю развития ТвХ’а. Порой, работая над тем или иным сообщением об ошибке, которое вы получаете в конце файла, вы можете обнаружить нечто в середине ... Думаю, это была такого рода ошибка. Это было ровно 16 лет назад.
Барбара спросила, почему я надел эту футболку. На футболке написано:
хп + уп = zn ... Not!
Это математическая формула, и я могу показать, как набирать ее в TgX’e, если вас это интересует ... [ Смех. ]
Я ношу эту майку, потому что месяц назад столкнулся с сенсацией. Меня постоянно волнует то, какую пользу люди всего мира извлекают из T^X’a. Очень волнует. Одним из самых главных событий прошлого месяца было то, что я пошел в библиотеку и увидел решение последней теоремы Ферма, сделанное Эндрю Уайлсом. Думаю, многие из вас знают, что это была новость первой полосы} Некоторое время были небольшие сомнения относительно того, нет ли в доказательстве какой-нибудь погрешности, но затем этот результат был зафиксирован. Это самый выдающийся математический результат. Точно так же, как люди помнят, где они находились в тот момент, когда услышали об убийстве президента Кеннеди, я знаю, что все математики могут вспомнить, где они были, когда впервые услышали о том, что большая теорема Ферма доказана. Статья появилась в прошлом месяце в журнале Annals of Mathematics; журнал пришел в нашу библиотеку, и, сидя там, я ознакомился со статьей, и она казалась мне замечательной, потому что была сделана в ТЩК’е и выглядела блестяще! [ Смех. ] Я чувствовал себя так, как будто сам помог доказать
эту теорему! [ Смех усиливается.] Я также рад сообщить, что на этой неделе здесь присутствуют люди, которые за это отвечали. В последнем ряду сидит редактор Annals of Mathematics, а также наборщица Джеральдин Печт. Это тоже радость для меня. Давайте пожмем им руки. [Аплодисменты.]
Но мне бы не хотелось сегодня говорить на заранее подготовленные темы, я хочу отвечать на вопросы.
Итак, Барбара задала первый вопрос: «Что слышно о четвертом томе The Art of Computer Programming?» Обычно я отвечаю на этот вопрос только в особых случаях. [ Смех. ] В настоящий момент я все время пишу и очень усердно работаю над этой книгой. Минуточку, я посмотрю, смогу ли я найти свою записку ... [ идет к проектору, отображающему экран с редактором Emacs с компьютера, который работает в Станфорде и связан с Флоридой через Интернет. ] Это чтобы я вспомнил, о чем мне говорить ...
В настоящее время для сокращения названия The Art of Computer Programming принята аббревиатура АСР. Но кто-то предложил сокращение ТАОСР. Поэтому теперь используется новая аббревиатура—ТА О СР. Это работа всей моей жизни, это то, за что я взялся в 1962 г., и я думаю, что мне понадобится еще 20 лет для работы, а когда я закончу, мне уже будет 77. Теперь вы понимаете, почему я рано ушел на пенсию: чтобы иметь возможность посвятить себя этой работе.
Барт Чайлдс: Здесь имеется в виду ТАОСР? (на экране опечатка— ‘ТОАСР’) Дон: Да. Вы хотите, чтобы я увеличил шрифт? У меня только три или четыре экранных шрифта ... О! [Замечает опечатку.]
Барт: Значит ли это, что я получу чек на сумму пять долларов и двенадцать центов? [ Смех. ]
Дон: Ни в коем случае! [ Смех. ] ... Я сделал это нарочно, чтобы проверить, смотрит ли кто-нибудь на экран. [ Смех. ] Ну ладно ... Итак, эта другая книга, над которой я работаю, называется The Art of Computer Programming [ смех. ] ... Ax, да ... Надо же, я волнуюсь. [ Смех. ]
Итак, чтобы закончить этот проект, я должен работать очень напряженно, потому что ученые-компьютерщики продолжают открывать новые возможности. Первоначально моя идея состояла в том, чтобы подытожить все, что происходит в области компьютерной науки, но сейчас я должен сказать, что мне придется приложить много усилий, чтобы подытожить хотя бы все то, что уже стало классикой. Особенно я работаю над тем, чтобы все было верно с исторической точки зрения и чтобы заложить правильную основу для специальных вещей. Но я не могу дать исчерпывающее представление, как я мог это в шестидесятых годах, когда приступал к работе. Думаю, что в целом я работал над ТЁХ’ом около десяти лет, и я надеюсь, что эти десять лет сэкономят мне 6 или 7 лет работы над ТАОСР, так как теперь я могу работать более эффективно.
The Art of Computer Programming— это самая значительная вещь, которую я могу сделать в жизни. Сейчас я чувствую себя очень здоровым и счастли-
вым, и мне кажется, что я успеваю за неделю сделать больше, чем когда-либо. Надеюсь, что так будет продолжаться достаточно долго. Но я знаю, что мне потребуется много времени. Поэтому я ушел на пенсию и все свое время отдаю этой работе.
Прошлый год я посвятил разработке основ проекта, что означало создание больших компьютерных файлов из того, что накопилось у меня дома. Так что теперь у меня тысячи и тысячи пунктов, которые я упорядочил и расставил по местам, и теперь я знаю, как искать информацию.
Сейчас мой текущий план состоит в том, чтобы закончить отвечать на почту, касающуюся The Art of Computer Programmings которая приходила в тот период, когда я работал над ТЩХ’ом. Как вы знаете, каждому, кто находил ошибки в книге The T^Xbooks я в итоге отвечал, платил за ошибки и т. д. Люди также получали вознаграждение за найденные ошибки в The Art of Computer Programming. Но на самом деле последний раз я выписал чек в июле 1981 г. [ Смех. ] В августе 1981 г. моя секретарша начала рассылать стандартную форму, выполненную в ТЁХ’е, где говорилось: «Я вам скоро отвечу». [Смех.] Я начал складывать эти письма в маленькую кучку. Потом эта кучка стала расти, перемешалась с другими документами, которые я получал, и в результате выросла до 260 дюймов в высоту. Если перевести это в сантиметры ... ну, в общем, это много! [ Смех. ] Там было около семи—восьми метров материала. Я погрузился в это и теперь отвечаю на эти письма. Количество писем, на которые я еще не ответил, меньше 500 —примерно 450 писем —и теперь я отвечаю на них и надеюсь, что чеки дойдут до людей по тем адресам, с которых они посылали мне свои комментарии.
von Neumann, John [=Margit tai Neumann Janos], 18, 225, 456.
Wadler, Philip Lee, 594. Wall, Hubert Stanley, 481. Wallis, John, product. 50, 112, 480. Wang, Hao (i®), 382-384.
Wang, Paul Shyh-Horng Дтк SLY
436, 631.
Watson, Dan Caldwell, 248.
Wedderburn, Joseph Henry Maclagan, 583.
Wegbreit, Eliot Ben, 603.
Weierstrass, Karl Theodor Wilhelm, 381.
Wiles, Andrew John, 465.
Wilf, Herbert Saul, 92, 483.*
Windley, Peter F., 518.
Wise, David Stephen, 420, 434, 595.
Wiseman, Neil Ernest, 420.
Yao, Andrew Chi-chih (Jyt538.
Young Tanner, Rosalind Cecilia
Hildegard, 75.
Zave, Derek Alan, 90, 603.
Zeilberger, Doron, 64.
Рис. 1. Фрагмент последней страницы списка ошибок к тому 1.
Я покажу вам эти ошибки, потому что я сейчас над ними работаю. Вот только один пример (рис. 1), так что вы поймете, о чем я говорю. [На дисплее
вместо Emacs появляется вывод в xdvi.] Это часть ошибок к указателю к тому 1J На самом деле это шрифт 8-го кегля, здесь слегка увеличенный. Я хочу показать вам одну из тех вещей, над которыми сейчас работаю ... Для всех авторов, упоминаемых в The Art of Computer Programming, если их фамилии не западноевропейские, я построил большую базу данных из фамилий, написанных на их родном языке, например, китайском или японском. (Я еще не поместил туда индийские фамилии, но работаю со специалистами из Индии, чтобы решить и этот вопрос.) ... Только что я практически обновил всю китайскую часть базы .. .У меня есть растровые изображения всех литер из Unicode —и китайских иероглифов в частности — так что теперь у меня, надеюсь, образовалась неплохая база таких вещей. Через несколько лет программный продукт Unicode будет готов и доступен, и тогда я смогу правильно набирать любые фамилии.
Я создал несколько интересных макро для Emacs, при помощи которых обращаюсь с литерами Unicode, несмотря на то что пока не имею соответствующего программного обеспечения. Я могу набирать в шестнадцатеричной системе и затем написать в Emacs М-х uni с; как по мановению волшебной палочки шестнадцатеричные числа превращаются в растровые изображения китайских иероглифов и вставляются в документ. [ Показывает, набирая 59daM-x uni с.] Вот как выглядит фамилия Энди Яо на китайском языке:
\Unil.08:24:24:-1:20% Unicode char "59da
<lc077018066018066018066018066019466effb66c339e78% 331670330660330660330660630e7066366c66e666764666% 6e06600c06600e0c601b0c6219986231b06360607f80803e>%
Пока у меня есть только растровые шрифты размера 24 на 24, но для корректур этого достаточно. Мой компьютер настроен должным образом, поэтому получить эти растровые изображения очень легко. Макро uni с дает команду Emacs запустить небольшую программу, которая ищет шестнадцатеричный код в файле, находит растровое изображение и вставляет его в Т^Х-файл. То, что находится между угловыми скобками, посылается с помощью Т£Х’а в PostScript при помощи макро
\def\Uni#l:#2:#3:#4:#5<#6>%
% \Uni ems:cols:rows:-hoff:rows+voff<hexbitmap> {\leavevmode \hbox to#l\unicodeptsize
{\special{" 0 0 moveto currentpoint translate \unic@deptsize \unic@deptsize scale #2 #3 true [24 0 0 -24 #4 #5] {<#6>} imagema^k}\hss}}
В конце года я планирую опубликовать этот список ошибок, который к тому времени будет завершен. Сейчас у меня набралось около 180 страниц из
Файлы этих ошибок в формате PostScript сейчас находятся где-то внизу Web-страницы http://www-cs-faculty.stanford.edu/~knuth/taocp.html. (Позже я неожиданно обнаружил, что фамилия Paul Wang -относится к ошибкам 2-го тома.)
всех трех томов, и я продолжаю пополнять этот список, отвечая на старые письма.
Мои издатели собираются издавать 4-й том отдельными выпусками, в каждом должно быть около 128 страниц. Предполагается делать это примерно дважды в год в течение ближайших десяти лет. Я считаю, что буду готовить около 256 страниц в год. Мы планируем иметь в наличии 3-4 выпуска, прежде чем начать издание. Один из четырех первых выпусков будет большего объема из-за обновленного списка ошибок, содержащихся в томах 1, 2 и 3; во втором выпуске будет представлен гипотетический компьютер под названием MMIX, который придет на смену компьютеру MIX. MMIX —это машина RISC, очень похожая на компьютеры, которыми мы все сейчас пользуемся. Это 64-битный RISC-компьютер, который я когда-нибудь даже смогу иметь, если найдется кто-то, кто захочет его сконструировать. В его разработке мне помогают несколько экспертов в этой области. Дик Сайтс, создатель процессора Alpha и один из моих студентов, пообещал работать над всей заключительной частью разработки. Мне также помогает Джон Хеннеси, который создал процессор MIPS, и несколько человек из SPARC. Поэтому MMIX должен быть хорошим RISC-компьютером, и он поможет нам в экспериментах с алгоритмами, цель которых — понять, насколько хорошо различные запасные схемы управления работают с различными алгоритмами сортировки и т. д.
Второй выпуск заменит MIX на MMIX, и я надеюсь, что всякий раз, когда в The Art of Computer Programming встретится программа MIX, она будет заменена программой MMIX. Я не собираюсь делать это, пока не закончу тома 4 и 5. Но надеюсь, что многие к тому моменту уже проделают эту работу. Некоторые уже пообещали мне, что в следующем году у них будет С-компилятор для MMIX, и мы пытаемся получить написанные для него операционные системы.
Что сказать о двух других начальных выпусках? Один из них будет представлять собой первую часть 4-го тома, где говорится о вещах, не поддающихся систематизации. В этом разделе обсуждаются технологии, почерпнутые, в основном, из фольклора об эффективных методах для компьютеров при проведении побитовых логических операций, таких как исключение и дополнение, а также сложение, вычитание, умножение и деление, для достижения наилучших результатов. Этот материал уже написан. На самом деле, это то, что я запланировал еще до того, как начал работать над Т£Х’ом, — я написал первый план для не вошедшего в первые три тома материала в начале 1977 г. *
Затем я погружаюсь в изучение грубых перечислительных методов. Четвертый том посвящен комбинаторным алгоритмам, т. е. методам, которые были разработаны для разрешения проблем, где возможно несметное количество вариантов. Для ускорения процедуры были выдвинуты самые разнообразные идеи наравне с очевидными методами обработки важных комбинаторных случаев. Я начал с рассказа о вещах, не поддающихся систематизации, далее говорится о быстрых методах составления списков всех перестановок, всех подмножеств множества и тому подобных вещах. По этому вопросу есть большое количество литературы. Как ни странно, о создании перестановок написано гораздо больше, чем о сортировке. Сортировка — это расположение по порядку,
тогда как создание перестановок — это нечто вроде беспорядочного расположения. Авторы чаще объясняют, как располагать вещи в беспорядке, а не как упорядочить их. [ Смех. ] Большая часть этих трудов содержит повторы и общие места и не так интересна, как то, что написано о сортировке, так что основная трудность для меня заключается в том, чтобы ознакомиться с этой литературой и собрать ее вместе. Большинство тех, кто писал о перестановках, не знало о том, что много других людей работало над той же самой темой.
Ну ладно. Я думаю, что этого более чем достаточно для ответа на ваш вопрос о томе 4. Я стараюсь делать одну страницу в день и думаю, что буду работать в том же темпе.
Page xi replacement for exercise 3 25 Mar 1995 3. [5^] Leonhard Euler conjectured in 1772 that the equation w4 + x4 + y4 = z4 has no solution in positive integers, but Noam Elkies proved in 1987 that infinitely many solutions exist [see Math. Comp. 51 (1988), 825-835]. Find all integer solutions such that ^ <w < x <y < z < 106.
[M50] Prove that when n is an integer, n > 4, the equation wn + xn + yn = zn has no solution in positive integers w, x^ y, z.
Рис. 2. Фрагмент списка ошибок для введения из тома 2.
Пока я пытаюсь вывести это на экран ... давайте перелистаем пару страниц ... Я хочу взглянуть на это уравнение (рис. 2). Обычно в начале книги я помещаю теорему Ферма как исследовательскую проблему. [ Смех. ] В списке ошибок теперь говорится: «Докажите, что если п — целое число, большее 4, то уравнение wn + хп + уп = zn не имеет решений для положительных целых чисел. Таким образом, я просто добавил другой вариант этого уравнения, и мы получили другую хорошую исследовательскую проблему. Оказывается, для п = 4 существует много решений. Доказательство теоремы Ферма стало для меня личным кризисом, но таким вот способом я из него вышел. [ Смех. ]
(((01)2)((3(45))((67)(89))))
Рис. 3. Иллюстрация на METAPOST’e к списку ошибок тома 1.
Я также хотел бы взглянуть на с. 61 списка ошибок как на пример нового материала (рис. 3). На этой странице показаны очень симпатичные конструкции, которые получаются при изучении деревьев. Я привел эту иллюстрацию в качестве примера: все иллюстрации для книги сделаны в METAPOST’e, и на этой странице показан один такой рисунок, который попал в список ошибок. Мне нравится METAPOST, и сегодня утром Джон [Хобби] покажет вам свою систему. Она выполняет технические иллюстрации гораздо лучше других систем. С моей точки зрения, огромное преимущество METAPOST’a состоит в том, что если мне нужно модифицировать иллюстрации позже, например, через год, то в коде МЕТА РОЕТ я могу увидеть точно, что я имел в виду при создании оригинала. METAPOST —это декларативный язык, с помощью которого вы устанавливаете характеристики для вашей иллюстрации, а затем рисуете диаграмму. В списке ошибок сейчас содержится пять-шесть примеров того, как я либо перерисовывал старый рисунок, либо, как в этом случае, создавал новый. Многие другие примеры, которые я подготовил при работе над книгой Stanford GraphBase убедили меня в том, что METAPOST действительно идеально подходит для технических иллюстраций. Вряд ли можно придумать что-нибудь лучше этого.
METAPOST не годится для таких видов иллюстраций как, например, реклама. Но когда вы пишете техническую книгу (вчера Себастиан [Ратц] говорил то же самое в отношении PSTricks), вам нужно сделать диаграмму, удовлетворяющую определенным математическим правилам. Программа Super MacDraw- type едва ли предоставит вам такую возможность, тогда как METAPOST делает это с легкостью, и при этом все выполнено правильно с математической точки зрения. Это позор, что до недавнего времени ни в одной книге по математическому анализу не было хорошего изображения кардиоиды. Вчера на экране мы увидели настоящую кардиоиду [слайд Дени Жиру]. Никто даже не знал, как выглядит кардиоида, потому что рисунки были сделаны художниками, пытавшимися скопировать работы своих предшественников и никогда не видевшими самого предмета.
Ну ладно, я слишком много говорю об этом. Задавайте следующий вопрос.
Роберт Макгаффи: Считаете ли вы, что алгоритм RSA будет взломан?
Дон: Думаю ли я, что алгоритм RSA когда-нибудь будет взломан? Алгоритм RSA —это схема Райвеста—Шамира—Адлемана для шифрования. Уже найден ключ к подлинному шифру, разработанному в 1977 г. Это было число из 140 разрядов, которое являлось очень слабой версией их общей идеи. Но Райвест вместе с остальными сказал: «Вот наше секретное сообщение. Можете ли вы расшифровать его?» К тому моменту, когда кто-то в прошлом году сумел это сделать, Рон [Райвест] потерял свой ответ и забыл, о чем шла речь. Оказалось, что после расшифровки сообщение выглядело так: «Секретное слово—squeamish ossifrage (привередливая скопа)». Таким было решение. Чтобы разгадать этот шифр, пришлось разложить на множители число из 140 разрядов, и в прошлом году на это были затрачены тысячи часов компьютерного времени. Дело в том, что если вместо числа из 140 разрядов взять число из 141
разряда, то проблема значительно возрастет. Поэтому если говорить о числе из 300 разрядов, то, насколько мне известно, работающие компьютеры всего мира не смогут решить эту задачу. Однако могут быть преимущества при разложении на множители, и сам Райвест считает, что кодирование с помощью числа из 300 разрядов займет около 30 лет. А что касается числа из 500 разрядов, то тут понадобится сто лет — он с уверенностью заявляет, что это правда.
Мы сомневаемся в том, что в ближайшее время кто-нибудь откроет волшебный способ разложения чисел на множители. Однако большая проблема заключается в том, что использовать RSA на полную мощность незаконно. Я хочу сказать, что правительство хочет быть уверено в том, что оно сможет при* необходимости прочитать секретные материалы, так как не желает, чтобы у мафии были секреты. Таким образом, сложилась своеобразная ситуация, когда противозаконно решать с помощью компьютера какое-нибудь математическое равенство или формулу. Ладно, я не люблю конфронтаций. [ Смех. ] Мне не нравится вести секретный образ жизни .... Я имею в виду, что я не засекреченный человек. Я целый год занимался криптанализом и работал со многими замечательными людьми, но понял, что эта жизнь не для меня. Я хочу быть профессором колледжа и рассказывать о том, что знаю. Поэтому я плохой консультант по этому вопросу. Однако можно отправить надежную информацию о том, что никто не умеет раскладывать числа на множители. Это либо благодеяние, либо угроза, в зависимости от вашей точки зрения. Что касается меня, то я могу смотреть на это с обеих позиций.
Кто еще?
Майкл Софка: Справедливо ли равенство Р = NP, и если нет, то насколько далеки мы от доказательства?
Дон: Равенство Р = NP —это самая большая нерешенная задача в компьютерной науке, подобная теореме Ферма, хотя этой задаче всего около 25-30 лет. В контексте комбинаторных алгоритмов она звучит так: сможем ли мы решать задачи, которые требуют применения 2П случаев? Можем ли мы сделать это в п10, если бы мы знали лучший способ? Если Р = NP, то мы должны ответить «да»: мы можем преобразовать все эти экспоненциальные задачи в задачи с многочленами. Если равенство неверно, то следует ответить «нет», мы никогда не сможем их преобразовать.
Я подозреваю, что можно решить эту задачу следующим способом. Кто- нибудь докажет, что Р равно NP, потому что количество препятствий для того, чтобы Р не равнялось NP, конечно. [ Смех. ] В результате окажется, что существует некий многочлен, и с его помощью мы сможем решить все задачи NP. Однако мы не узнаем, что это за многочлен, мы только будем знать, что он существует. Таким образом, сложность может заключаться в том, будет ли п равняться триллиону или чему-нибудь еще в этом роде, но это будет многочлен. В таком случае мы никогда не сможем произвести вычисления, так как потребуется слишком много времени, чтобы узнать, что это за многочлен. Но он должен существовать. И это означает, что постановка вопроса Р = NP была неверной. [Смех.] Получится что-то в этом роде. Даже когда вы используете
метод, требующий 2П шагов, и сравниваете его с методом, требующим п100 шагов, то в случае 2П степень п должна быть не больше 20-30. Но воспользоваться методом с п100 вы не можете даже для п = 2. Поэтому очень важна степень многочлена. Существует так много алгоритмов, что задача доказать отсутствие алгоритмов для многочленов является очень сложной. Я действительно полагаю, что теорема Ферма — аналогичное явление, когда более важно сформулировать задачу, а не решить ее. Поэтому относительно теоремы Уайлса я думаю, что он проделал огромную замечательную работу, но я желаю ему решить какую-нибудь другую задачу. [ Смех. ]
Многие думают, что если задача относится к классу NP, то ее можно не решать, так как это означает, что ее нельзя решить с помощью многочленов. Но пока мы не стали изучать NP, мы имели дело с неразрешимыми задачами, т. е. с такими, для которых вообще не существует никаких алгоритмов. И неважно, сколько вы работали, —вы никогда не могли решить задачу. Учитывая, что данная машина Тьюринга когда-нибудь остановится, эту задачу нельзя решить при помощи каких-либо алгоритмов, независимо от того, сколько времени вы этому посвятили. Пока NP не стал популярным, никто не брался за решение подобных задач, так как было доказано, что их нельзя решить в принципе. Но это был неправильный подход, потому что почти все задачи, которые мы когда-либо решали, являются специфическим случаем какой-нибудь задачи, не имеющей решений.
Возьмем, например, дифференциальное исчисление — задачу, в которой берется формула, функция от п^ и ставится вопрос: «Равен ли нулю предел при п, стремящемся к бесконечности?» Это неразрешимая задача. Однако это не означает, что мы не должны изучать дифференциальное исчисление. Пределы многих функций стремятся к нулю, и это позволило развить дифференциальное исчисление. Но в общем виде эту задачу решить нельзя. Я хочу сказать, что вы можете определить / от п~ потребуется всего несколько строчек для формулы, равной нулю, если данная машина Тьюринга остановлена в момент п, и она будет равна 1, если машина Тьюринга все еще будет работать в момент п. Таким образом, предел равен нулю тогда и только тогда, когда машина Тьюринга прекращает работу. Это неразрешимая задача. •
Подобные вещи происходят с NP. Есть Множество задач, которые трудно эффективно решить с помощью NP; если мы знаем, что какие-то задачи не подходят для NP, то это еще не значит, что нужно отказаться от их решения или перестать думать над хорошими эвристическими методами.
А не хотите ли вы задать вопросы о lyX’e?! [ Смех. ] А?
Т. В. Раман: Одним из преимуществ Т^Х’а является то, что это гибкая система, позволяющая авторам определять макро, которые классифицируют шифровку семантики. Таким образом, если вы пишете статью о перестановках, вы можете ввести определение \permute и использовать его затем в своем документе. Я часто прибегаю к этому в своей системе А^ГеД. Я хотел бы знать —
Дон: Простите, вы потом будете выступать ... ?
Раман: Да, я буду выступать завтра днем.
Дон: Значит, в вашей системе используются возможности ТЁХ’а как часть семантики документа, тогда как автор думает, в основном, об удобстве в написании.
Раман: Да. На самом деле, все идет на пользу дела. Поэтому чем больше семантики в разметке, тем лучше. Обычно в документах можно использовать либо основной уровень разметки, когда автор просто пишет ‘x\over у’, если ему нужно изобразить дробь ‘^’, либо авторское определение, например, \inference — макро, который берет 2 символа и затем помещает х над -у и ставит между ними горизонтальную линейку. В последнем случае я выигрываю, а в первом проигрываю. У меня такое ощущение, что если вы посмотрите на большую книгу, то вы увидите, что она выглядит как большая компьютерная программа, и мне кажется, что из соображений здравого смысла лучше читать ее таким образом.
Дон; Все ли поняли, в чем заключался вопрос? Он говорит, что если вы слепой или неполноценный, вы можете изучить Т^’овский исходник. Если он сделан должным образом, это может быть даже лучше, чем иметь печатную копию, потому что должен быть написан по схеме очень логичной разметки. В тексте книги мы стараемся показать структуру с помощью полиграфических средств. Но на самом деле мы знаем даже больше о структуре, когда создаем наши исходные файлы. Поэтому в исходных файлах такого автора будет больше информации, чем в том, что вы увидите впоследствии.
Когда вы пишете, не забывайте о тех, для кого это предназначается. Если вы заглянете в любую книгу о том, как нужно писать, или прослушаете любой курс на эту тему, вы поймете, что правило номер один всегда гласит: Помните о своем читателе. Если авторы понимают, что они пишут гипертекст, они построят свои объяснения таким образом, чтобы извлечь максимум из гипертекста. Когда я написал свою первую статью на иностранном языке, она была опубликована в Канаде, где слово «color» пишется через «и». [Смех.] Моя вторая статья на иностранном языке была написана по-норвежски, поэтому когда я говорил о вариантах обозначения «left» и «right», я использовал буквы ‘и’ и lh' вместо Т и 1т\ Я хочу сказать, что обдумывая то, что вы будете писать, вы делаете это для читателя. Это очень простые примеры. Поэтому если вы предполагаете, что кто-то будет смотреть ваш исходный ТеХ’овский файл или сможет посмотреть часть вашего документа, вы должны построить весь процесс объяснений по-другому, и у вас будет больше читателей. Поэтому я попытаюсь... Я могу показать вам макро, сделанные для The Art of Computer Programming в новом стиле, но...
Раман: Я хотел бы взглянуть на них, потому что следующее, что я собираюсь сделать, — это запустить систему ...
Дон: ^демонстрирует на экране; см. рис. 4.] Давайте посмотрим на файл. Он все еще в незавершенном состоянии, да? [ Смех.] Я загрузил различные файлы
У. Macros .for The Art of Computer Programming
7. (STILL UNDER CONSTRUCTION!
У. I started with manmac.tex and am letting this evolve)
\input epsf
\input rotate
\input picmac
\input unicode
\catcode<Q=ll % borrow the private macros of PLAIN (with care) \font\ninerm=cmr9
Рис. 4. Начало файла acpmac.tex.
с макро: файл ‘epsf’--для того, чтобы получить рисунки METAPOST; файл ‘rotate’ позволяет PostScript’y вращать изображение; файл ‘picmac’ —это моя подсистема той части ГАТ^Х’а, которая делает рисунки; файл ‘unicode’ —это то, о чем я вам говорил, когда рассказывал о наборе китайских фамилий. Итак, давайте посмотрим на некоторые моменты форматирования... [показывает различные коды для форматирования. ]
Вот мои составные макро (рис. 5) ... как видите, я поместил сюда несколько исключений, касающихся переносов. Уравнения нумеруются цифрами старого стиля. Управляющая последовательность \star применяется для обозначения раздела, помеченного звездочкой; \slug используется в конце доказательства— мне нужно изменить его, чтобы он не был таким черным, а то люди видят так много боксов при переполнении, что не хотят больше видеть черных «марашек». [Смех.] Я переопределяю точку с бэкслэшем — \: вместо \. для шрифта пишущей машинки в середине математического режима. Это важная система обозначения согласно Иверсону, где вы можете заключить любую формулу в квадратные скобки, что позволит вычислить ее в 0 или 1 —вы можете поместить это в середину уравнения, это очень полезно. Вот макро для обозначения A.D. или В.С. А вот то, что я использую для специальных выделений. Я не пользуюсь этим для того, чтобы выделить курсив, для этого существует команда \it. А \ет#1: — это специальный формат, который я часто применяю для примечаний или чего-нибудь в этом роде. [ Просматривает макро, комментируя наиболее интересные из них. ] ... Это числа Эйлера ... с угловыми скобками вместо круглых скобок, используемых для двучленных коэффициентов.
Раман: Есть ли здесь макро \euler?
Дон: Да, вот здесь числа Эйлера. ... \Euler — это похожее обозначение, где используются 2 ограничителя и есть достаточное количество отрицательного пространства между ними, что позволяет поместить 2 угловые скобки после каждой из них; точно так же \Choose заключает двучленные коэффициенты
Composition macros
\hyphenation{logical Mac-Mahon hyper-geo-metric
hyper-geo-met-rics Ber-noulli Greg-ory dis-trib-uted}
{\obeyspaces\gdef {\ }}
\def\hang{\hangindent\parindent}
\def\dash {\ifdim\lastskip>Opt\unskip\fi\ \cdash }
\def\eq(#l){{\rm({\oldsty#l})}}
,\let\EQN0=\eqno \def\eqno(#1){\EQN0\hbox{\eq(#D}}
\def\star{\llap{*}}'
\def\slug{\hbox{\kernl.5pt\vrule width2.5pt
height6pt depthl.5pt\kernl.5pt}}
\let\:=\. 7. preserve a way to get the dot accent
\def\.#l{\leavevmode\hbox{\tt#l}}
\def \[#1]{[\hbox{$\mskiplmu\thickmuskip =
\thinmuskip#l\mskiplmu$}]} 7. Iverson brackets
\def\bigi [#1]{\bigl[\begingroup\mskiplmu
\thickmuskip=\thinmuskip #l\mskiplmu\endgroup\bigr]}
\def\AD{{\adbcfont A.D.}}
\def\BC{{\adbcfont B.C.}}
\def \og#l{\leavevmode\vtop{\baselineskip\z(8skip
\lineskip-. 2ex \lineskiplimit\z(8
\ialign{##\cr\relax#l\cr
\hidewidth\kern.3em\shQft{40}1\hidewidth\cr}
\kern-lex}} 7. ogonek
\def\em#l:{{\it#l:\/}} 7. \em Hint: or \em Caution: etc
\def\euler{\atopwithdelims<>}
\def\Euler#l#2{\mathchoice{\biggl<\mskip-7mu
{#l\euler#2}\mskip-7mu\biggr>}7e
{\left<\’{#l\euler#2}\!\right>}{}{}}
\def\Choose#l#2{\mathchoice{\biggl(\mskip-7mu
{#l\choose#2}\mskip-7mu\biggr)}7.
{\left(\!{#l\choose#2}\!\right)}{}{}}
\def\smsum{\mathop{\vtenter{\hbox{\tenrm\char6}}}}
small summation sign
\def\phihat{{\mkern5mu\hat{\vrule widthOpt
height1.2ex\smash{\mkern-5mu\phi}}}}
\def\umod{\nonscript\mskip-\medmuskip\mkern5mu
\mathbin{.\underline{\rm mod}}\penalty900\mkern5mu
\nonscript\mskip-\medmuskip} 7. least remainder
в круглые скобки. А это \smsum для маленького знака суммы —не помню, где я им пользовался. А вот специальный символ \phihat — для буквы ^, которая справа должна иметь «шляпку», потому что (^’ —это то, с чем вы всегда сталкиваетесь, изучая числа Фибоначчи. ... А здесь у меня формат для алгоритма... целая куча макро для набора программ на Ассемблере, подчеркивания текста в комментариях и т. д. [ остальные примеры сюда не включены. ]
Мои ТЁХ’овские файлы списка ошибок частично отражают структуру, принятую в ТАОСР. Как я уже сказал, я ежедневно работаю над новыми исправлениями. Есть 4 типа ошибок: первый называется исправлением, что означает нечто новое, о чем мы не знали раньше; второй называется неисправностью и должен быть отлажен; третий называется планом и означает, что детали нуждаются в дальнейшей доработке, но я хочу отметить в файле, что помню о том, что нужно внести изменения; и наконец, четвертый называется улучшением, и это достаточно тривиально, но пока я над этим размышляю и хочу использовать, когда мы прйдем к завершению книги.
\amendpage 1.189 insert quotation before the exercises (95.07.13) {\quotef ormat
\vskip-3pt
Up to a point it is better to let the snags [bugs]
be there than to spend such time in design that there are none (how many decades would this course take?).
\author A. M. TURING, Proposals for ACE (1945)
X pl8, quoted in Comp J 20(1977)273 }
\endchange
Рис. 6. Последние дополнения к списку ошибок.
Например (рис. 6), вот цитата из Тьюринга, которая мне нравится; она относится к 1945 г., когда компьютеры еще не были изобретены, но он долго думал о компьютерах: «Лучше оставить все огрехи как есть, чем тратить время на такую переделку, чтобы их не осталось совсем. Представляете, сколько недель на это уйдет?» Эта цитата исправлена 12 дней назад в томе 1. е
\amendpage 2.ix replacement for exercise 3 (95.03.25)
\ехЗ. [34] Leonhard Euler conjectured in 1772 that the equation $w"4+x"4+y''4=z''4$ has no solution in positive integers,
\improvepage 2.27 line 2 (81.08.13).
random, they \becomes random; they
\endchange
Рис. 7. Другие примеры ошибок.
А вот здесь (рис. 7) мы видим ТЁХ’овский исходный файл, с помощью которого получен рис. 2. В этом примере я поменял запятую на точку с запятой. Я сделал это, потому что обычные улучшения, как правило, не перечислены в печатном экземпляре, если только вы не работаете сверх меры. Здесь представлен особый способ получения всех \improvepage, но обычно они только появляются в файле.
Вот чем я занимаюсь. Между прочим, улучшение было сделано в 1981 г. В течение 20 лет я просматривал экземпляры The Art of Computer Programming, сидя в своем кабинете, и делал пометки на полях, отмечая то, что нуждалось в улучшении. Все это вы теперь видите в этих файлах.
Еще вопросы?
Сильвио Леви: Как получилось, что вы не пользуетесь МуСом? [Смех.] Дон: Как получилось, что я не пользуюсь ВКЩХ’ом? [ Смех. ] Я боюсь больших систем! [ Смех усиливается. ] Барт?
Барт Чайлдс; Ваша статья «The Errors of Т^Х» — это великий труд. Не собираетесь ли вы написать статью «Mistakes of Т^Х»?
Дон: «The Mistakes of TgX»?? [Смех.]
Барт: Я имею в виду те изменения, которые вы сделали, когда пришли к 3 версии Т£Х’а. 7 и 8 битов и то, что могло считаться ошибкой (?). Есть ли еще что-нибудь в этом роде?
Дон: Думаю, что в статье «The Errors of TgX» я перечислил все, что можно считать ошибкой. Думаю, что я заметил бы неправильное предвычисление of 7-bit versus 8-bit input(?), если бы написал статью на год позже, но я написал се до выхода версии Т^ХЗ. Я пообещал поместить этот результат в статье, когда все успокоится, и вы будете знать, что последняя ошибка ТуХ’а найдена. [ Смех. ] Итак, вот что мы имеем на сегодняшний день. [ на экране появляется файл errorlog.tex; см. рис. 8.]
Последнее изменение было сделано 19 марта. Хотя нет, это день, когда была замечена ошибка. Ее обнаружил Петер Брайтенлонер, который сегодня находится здесь, и звучит она так: «Исключаются ложные подсчеты ссылок в форматных файлах». Это вызвало некоторые проблемы. Он обнаружил, что можно нарушить работу 1ЁХ’а, если сохранять форматные файлы и потом опять загружать, потом опять сохранять и опять загружать, и так сотни раз; вы исчерпаете память, потому что подсчет ссылок станет больше, чем общий объем памяти. Эта ошибка была зафиксирована, и он получил за нес $327.68 — 215 пенни.
* 26 June 1993
R928V668. Avoid potential future bug (Peter Breitenlohner). @628,637 * 17 December 1993
S929\>881. Boundary character representation shouldn't depend on font memory size (Berthold Horn). @549,1323
* 10 March 1994
R930. Huge font parameter number may exceed array bound (GET). @549
* 4 September 1994
F931\>926. Math kerns are explicit (Walter Carlip). @717
R932. Avoid overflow on huge real-to-integer conversion. @625,634 * 19 March 1995
R933. Avoid spurious reference counts in format files (PB). @1335 \relax \bye
Рис. 8. Конец файла errorlog.tex.
Каковы другие самые последние изменения? Номер 932: «Исключаются переполнения при преобразовании действительных чисел в целые». Номер 931: «Математические керны являются явными» — это была ошибка, порожденная изменением 926. Номер 930: «При большом числе параметров шрифта границы массива могут быть нарушены» — это то место, где не было полной ясности. Номер 929: «Границы представления символов не должны зависеть от размера памяти шрифта»—это очень серьезное изменение, которое было сделано вскоре после предыдущего обновления в 1993 г.
Перечисленные здесь даты этих ошибок — это даты их обнаружения. Я зафиксировал их в марте этого года [1995]. Я планирую вернуться к ним опять в 1997 году, затем в 2000, затем в 2004 и 2009, надеясь каждый раз потратить на это один день. Множество добровольцев, которых сейчас здесь нет, обрабатывает и исследует эти ошибки. Вчера, когда мы тестировали программы в Станфорде, кто-то заметил, что мы до сих пор пользуемся очень старой версией ТуСа, а проблем вроде бы и не возникает. [ Смех.] Надеюсь, что ошибки начинают сходить на нет. Оставшиеся ошибки могут разрушить систему; но только если вы очень постараетесь.
Есть одна серьезная ошибка в дизайне, которая должна остаться как характерная, и она возникает, когда речь идет о наборе на нескольких языках. Кажется, я не поместил ее в файл errorlog, но она есть в конце другого файла под названием tex82.bug (рис. 9). Взгляните сюда ... Этот файл содержит все подробности о каждом изменении, начиная с 1982 года. Видите вот это? Это «самое последнее изменение в ТЁХ’е, которое должно быть сделано после моей смерти». Номером версии станет тг. Это нечто вроде моей последней воли и завещания. [Смех.] Таким образом, я никогда не узнаю, что это изменение сделано. В ТЁХ’е версии тг ошибок не будет. Все, что носит имя ТЕХ, должно быть полностью совместимым друг с другом.
The absolutely final change (to be made after my death) Qx module 2
Qd banner=='This is TeX, Version 3.14159 {printed when \TeX\ starts} Qy
Qd banner=='This is TeX, Version $\pi$' {printed when \TeX\ starts} Qz
When this change is made, the corresponding line should be changed in Volume B, and also on page 23 of The TeXbook. My last will and testament for TeX is that no further changes be made under any circumstances. Improved systems should not be called simply fTeX'; that name, unqualified, should refer only to the program for which I have taken personal responsibility. — Don Knuth
Possibly nice ideas that will not be implemented . classes of marks analogous to classes of insertions
Design errors that are too late to fix
. additional parameters should be in symbol fonts to govern the space between rules and text in \over, \sqrt, etc.
. multilingual typesetting doesn't work properly when the \lccode changes within a paragraph
Bad ideas that will not be implemented
. several people want to be able to remove arbitrary elements of lists, but that must never be done because some of those elements (e.g. kerns for accents) depend on floating point arithmetic
. if anybody wants letter spacing desperately they should put it in their own private version (e.g. generalize the hpack routine) and NOT call it TeX.
Рис. 9. Конец файла tex82.bug.-
После последнего изменения в этом файле перечислены «неплохие идеи, которые я хотел бы осуществить», а затем две «ошибки дизайна, которые слишком поздно фиксировать». Самая серьезная касается случая многоязычных текстов. Если вы используете несколько языков в одном абзаце, я забыл сохранить часть информации —я не помню точно, что там используется1, но это серьезная оплошность, и я должен над ней подумать. Сейчас это делать слишком поздно; это единственная оставшаяся помеха.
Единственное, над чем я хотел бы поработать усерднее, — это расположение квадратных корней и линеек в дробях. У меня нет достаточного количества параметров, чтобы контролировать величину пробела между линейкой и текстом. Я совершил ошибку, решив проблему, требующую двух параметров, с помощью только одного параметра: я получил величину пробела, подсчитав ее как кратную толщине линейки, тогда как пробел нужно рассматривать как
независимый параметр. Теперь я обнаружил это, так как пишу статью и вижу, что квадратный корень выглядит неправильно. Поэтому я должен помещать в показатель степени скрытые распор ди, чтобы пробел был больше. Я хотел бы, чтобы это было сделано лучше, но учитывая неизбежные компромиссы моей большой системы, я доволен тем, как все происходит.
Когда я читаю статьи, набранные в ТЁХ’е, меня больше всего беспокоит (не считая набора квадратных корней) то, что люди не используют обновленные и улучшенные шрифты Computer Modern, которые я выложил 3-4 года назад* До сих пор применяются старые шрифты, и я не знаю, сколько еще времени должно пройти, прежде чем люди их поменяют. Примерно 2-3 месяца назад Эберхард [Маттес] внес изменения в свою широко распространенную систему emT£X.
Изменение шрифтов особенно хорошо видно на примере греческой строчной дельты. Около четырех или пяти лет назад, работая над статьей, я обнаружил, что избегаю использования буквы 5. Я стал думать, почему я стараюсь обходиться без этой буквы, и понял, что я ее подсознательно ненавижу, потому что мне не нравится, как она выглядит. Но в этой статье мне действительно была необходима дельта, поэтому я взял выходной и переделал эту букву, в результате чего получилась по-настоящему красивая дельта. Я думаю, что теперь у нас есть самая лучшая дельта из всех, которые когда-либо видело человечество. [Смех.] Это было давным-давно. И теперь, когда я вижу, что люди пользуются старой дельтой, меня коробит.
Я также изменил несколько других букв, например, некоторые рукописные прописные буквы. Я выдвинул горизонтальный штрих буквы ^Н\ а кроме того мне не нравилось основание буквы Т\ и я поменял эту букву на 1Т\ Я вижу, что голландская группа использует в своем логотипе старую букву Т ... однако я надеюсь, что все будут пользоваться шрифтами cmr. Файлы ТЕМ не изменились. Да, я еще сделал все стрёлки более мощными (‘—Г превратилась в ‘->’). При ксерокопировании стрелки пропадали, подтому теперь острие стало больше и ярче.
Камерон Смит: А на какую дату или номер версии мы должны ориентироваться?
Дон: Исходный файл должен быть датирован 93 или последующими годами, и тогда все будет нормально.
Сильвио; Нет! Нужно поменять ст на dm! Пока вы этого не сделаете, никакого обновления не произойдет.
Дон: Да, это происходит в большинстве случаев, но это те пакеты, где не обновлены старые файлы. Если вы используете DVIPS, то все, что вам нужно сделать,— это удалить файлы РК, и новые шрифты установятся автоматически.
Сильвио: Если у вас есть новая версия исходников.
Дон: Все исходники здесь.
Сильвио: Да, но очень трудно ...
Дон: Пожалуйста, найдите способ решить эту проблему, а то меня это расстраивает. Каждый раз, когда я получаю письмо от кого-то, кто пользуется старой дельтой, я пишу ему, что надо попросить программистов обновить шрифты, и тогда он присылает мне новый вариант статьи и говорит, что да, теперь все нормально. [ Смех.] Поскольку люди знают об этом. Не так уж много нужно менять. Если мы сообщим об этом дистрибьюторам и математическим журналам, то изменения не займут много времени.
Робин Фэрбэрнз: Можно я это прокомментирую? Эберхард Маттес является автором версии, которой пользуется наибольшее число людей, и он уже все обновил, а месяц назад разработал новый комплект шрифтов. В списке рассылки мы сталкиваемся с постоянным нытьем типа «нам не нужны проблемы с обновлением наших шрифтовых файлов». Я не устаю повторять: «Но вам действительно необходимо это сделать». Несмотря на это, люди говорят, что компьютерное время на их PC стоит дорого.
Дон: Через 5 лет у них будут новые PC. [Смех.] Все равно это произойдет. Я только надеюсь, что ждать придется не очень долго.
Сильвио; Я послал копию в Австралию. Хочу сказать вам, что ради дельты никто не будет делать обновлений. А вот если вы выпустите новую версию с собственным номером и новым названием и если вы сделаете ее обязательной, тогда ...
Дон: От того, что она будет обязательной, ничего не изменится.
Джереми Гиббонс: Если вы измените название, старые файлы DVI не будут работать при печати, так как будет много недостающих шрифтов.
Дон: Правильно. Но это не самое главное. Видимо, я большой педант: я просто рассказываю вам о том, что меня раздражает, но, очевидно, это не раздражает остальных. [ Смех. ]
Сильвио: Меня это тоже раздражает!
Дон: Ну хорошо. Как бы там ни было, я не хочу менять названия моих шрифтов, [пауза.] Нельсон?
Нельсон Бийби: Дон, с 1978 года мир сильно изменился —
Дон: Да, и это подтверждает великолепная цитата Билла Гейтса, которую я привел в этом году в начале списка ошибок ... (рис. 10).
Нельсон: Если бы вы были на 25 лет моложе и приступили бы к разработке ТЁХ’а сейчас, когда рынок полон процессоров, лазерных принтеров PostScript и прочих вещей, что бы вы сделали по-другому?
Дон: Думаю, что я все сделал бы так же. Я бы оставил все, что вам не нравится! [ Смех.] Так уж я устроен ...
Камерон: Я хотел задать вам один вопрос, связанный вот с чем. Вы проделали большую работу при создании алгоритма разбиения текста на строки внутри абзаца, и кажется, что существует масса возможностей для оптимального разбиения. Но в книге The ТвХЬоок я прочел, что памяти компьютера
\vfill
{\quot eformat
Things have changed in the past two decades.
\author BILL GATES (1995)
% "You, too, can start a software firm"
7, International Herald Tribune 5 Jan 1995, pages 9 and 11 \bigskip
In addition to the errors listed here,
about half of the occurrences of * which’ in volumes one and three should be changed to ‘that’.
\author DONALD E. KNUTH ({\sl The Art of Computer Programming %
Errata et Addenda}, 1981)
\eject
}
Рис. 10. Фрагмент из списка ошибок ТАОСР.
недостаточно для того чтобы разбить подобным образом несколько страниц текста. Выходит, что между алгоритмом разбиения на строки и алгоритмом разбиения на страницы нет достаточной связи. Если, например, вы пишете письмо, и на первой странице у вас есть шапка, то у вас должна быть другая ширина страницы и вам может понадобиться другое разбиение строк в середине абзацев. Это можно было бы упростить, если бы была возможность работать с частью страницы и если связь между алгоритмами разбиения на строки и на страницы была бы лучше. Собираетесь ли вы переделывать что- нибудь сейчас, когда компьютеры обладают мощной памятью?
Дон: Да, конечно, возможности памяти сейчас совсем другие. Удивительно, как сильно изменилась память, — больше, чем что-либо другое. Мы долго накапливали опыт и теперь мы понимаем многое из того, что не понимали раньше. Когда я работал над ТЩХ’ом, многие вещи были на уровне эксперимента, и это давало возможность учиться. Когда вы создаете систему нового поколения, то оказывается, что вы поняли систему предыдущего поколения и привели ее в порядок, а затем вы будете ставить новые эксперименты, которые будут приведены в порядок следующим поколением. Так обычно растет уровень понимания, и так устроен мир.
Сейчас начинает проясняться то, какая связь лучше. Основная идея TgX’a заключается (и я думаю, всегда будет заключаться) в том, чтобы найти наименьшее количество примитивов для создания большинства главных вещей. В результате 99% работы выполняется с помощью этих примитивов, а остальная часть делается вручную. Я не ставил цели создать систему, которая выполняла бы 100% работы, но я ожидал, что ручная работа сведется к минимуму. Наведение лоска занимает ничтожный процент времени, потраченного мною на другие части работы. Конечно, в понятие минимума разные люди вкладывают разный смысл.
Йапример, я работал над книгой о Библии, где помещено много иллюстраций. На каждую я потратил 6-7 часов: я возился с ними, занимался цветоделением, чистил, высветлял, в общем, всячески их совершенствовал. С моей точки зрения, это работа на уровне помех, потому что на написание главы с иллюстрациями я уже потратил 40 часов. Так что эти 6 часов — небольшой процент работы.
Но если я нахожусь в коммерческом учреждении, где нужно заниматься графикой, и эта работа оплачивается, и если я должен сделать 60 иллюстраций и потратить на каждую по 6 часов, то это совсем другое дело. Поэтому я думаю, что у разных пользователей есть разные представления о том, что нул&ю автоматизировать. Автору сравнительно легко потратить немного лишнего времени на какие-то детали, потому что он уже потратил гораздо больше времени на написание книги. Но те люди, которые работают с авторами, наверняка захотят, чтобы все это было автоматизировано и чтобы это было частью волшебной системы, на изучение которой авторы не желают тратить времени.
Итак, если я должен выполнить некую полиграфическую работу в 2 или 3 прохода, я просто пробую пару раз, прогоняю ее через машину, смотрю через визуализатор и привожу все в порядок. Неделю или две назад мы с женой выпустили информационные бюллетени для нашей семьи. Мы делаем это ежегодно; дело в том, что у наших четырех бабушек и дедушек есть куча родственников, примерно по 60 или 80 человек. Мы пишем им письма с просьбой прислать новости за последний год, а затем собираем всю информацию в маленькие буклеты и рассылаем их по четырем направлениям. Каждый год я вожусь с этим материалом 6 или 8 часов, как редактор информационного бюллетеня, чтобы все выглядело красиво. ... Вы затрачиваете какое-то время на то, чтобы навести глянец. Те инструменты, которые есть в вашем распоряжении, всегда меняют ваши ожидания.
Я считаю, что следующее поколение систем будет обладать множеством более сложных механизмов для выполнения общих задач, т. е. тех, которые я причислил к 99%. В процессе набора книги могут возникнуть любые осложнения — возможно, вам потребуется в качестве точки отсчета взять середину знака, а не левое поле, когда вы двигаетесь налево и направо, используете при этом цвет, повррот или несколько столбцов и т. д. Теперь мы лучше понимаем, как создать такие общие механизмы.
Что касается памяти ... я думаю, что разбивка на страницы не так уж сильно ограничена памятью — вам потребуется сделать это в 2 прохода, но машины теперь обладают достаточной скоростью. В общем, люди будут работать на компьютерах следующего поколения с учетом опыта работы на предыдущих системах.
Пожалуйста, Пьер.
Пьер Макей: Я хотел бы вернуться к вопросу обновления шрифтов. Я думаю, что нужно нечто такое, что не приводило бы к сбоям. Чтобы иденти-
фицировать версию шрифтов, вы вставляете в METAFONT команду special. Так как люди обновляют свои драйверы гораздо чаще, чем шрифты, нужно только, чтобы драйвер распознал команду special и сказал: «Нет-нет! Вы не можете пользоваться этим шрифтом!» [Смех.]
Фред Бартлетт: Я думаю, что все, что выступавший ранее хочет сделать с разбивкой на строки и на страницы, можно было бы достаточно легко осуществить с помощью умеренно сложных макро и новой программы вывода, если бы была возможность сохранять элементы, которые выбрасываются в конце каждой страницы. Вы хотите сохранить эти выброшенные элементы, когда ТУХ вызывает программу вывода. Меня интересует, почему, когда вы писали ТУХ, вы сделали так, что все это выбрасывается, и нет возможности это сохранить?
Дон; Программа вывода может поместить их в бокс — скопируйте их в бокс — Фред: —но нельзя сохранить выброшенный пробел между строками.
Дон: Разве выброшенный пробел не является значением одного из параметров, которые сохраняются в программе вывода?
Петер Брайтенлонер: Штраф сохраняется, но пробел выбрасывается после того как страница напечатана. А если ничего не возвращается, то он не выбрасывается.
Дон: Где-то в книге The T^Xbook приводится нулевая программа вывода, которая предполагает, что все сохраняется. Разве она не работает?
Питер: Работает. Потому что в этом случае пробел находится не наверху.
Фред: Но вы не можете упрятать страницу, вы не можете поместить страницу в бокс и вернуться назад, создать новую страницу и поместить ее во второй бокс, как при расположении текста в 2 колонки, а затем соединить оба бокса вместе, чтобы все выглядело гладко, поскольку пробел между ними будет потерян. А это значит, что если вы хотите расположить сложный текст в 2 колонки (я делал пару таких книг), то вы столкнетесь с проблемами при создании таблиц, рисунков и тому подобных вещей. Я бился над этим 6 или 7 лет, и лучшее, что я смог сделать, —это добиться, чтобы Тух выдавал мне предупреждение, когда он приступает к выравниванию колонок, начиная с правой, потому что у меня нет там нужного пробела. Почти все в ТуХ’е вписывается в параметры: вы можете делать что-то, сохранять, просматривать результат, тестировать, — все, кроме элементов и пробелов, которые уничтожаются. Я просто хочу знать, почему это так.
Дон: Видимо, потому что я не подумал об этом. [ Смех.] Программа вывода была в наибольшей степени экспериментальной частью ТуХ’а. У нас не было моделей, которым мы могли бы следовать. Нам нужно было разрешить 4 или 5 проблем, и мы пытались сделать это с помощью минимального количества примитивов. Настал момент, когда мы смогли внести ясность в решение этих проблем. Но мы знали, что все это было на уровне эксперимента. Жаль, что ваша проблема не возникла до появления версии ТуХ 3.0, потому что она могла бы меня соблазнить ... [ Смех. ]
Фред: Вы, кажется, сказали, что предполагаете, что люди будут шире использовать Т^Х, чем раньше.
Дон: Да, безусловно. Я думаю, что Т^Х будет использоваться в специальных изданиях, таких как Encyclopedia Britanica или арабско-китайский словарь, либо в крупных проектах. Я никогда не считал, что какой-нибудь экзотический проект можно выполнить с помощью одного инструмента. Поэтому я встроил в код множество специальных приемов (hooks), и благодаря этому специалист за неделю сможет достаточно легко установить новую программу для особых случаев. Таков был мой план. Но я не думаю, что люди будут этим очень часто пользоваться.
Мне обязательно нужно это сделать. Если бы я выпускал Библию или что-нибудь в этом роде, или если бы я был издателем, стремящимся выполнить какую-то работу особенно хорошо, то мне был бы нужен специальный инструмент для набора. Переписать систему набора достаточно легко. [ Смех. ]
Я полагаю, что многие люди не делают этого, потому что они боятся что- нибудь нарушить. Думаю, они и не станут этого делать. Мне кажется, это лишняя предосторожность. Я пытался показать, как это делать, с помощью некоторых особенностей Дц^’а, как если бы они были добавлены потом. Но это их не убедило. Множество людей продолжает работать в TgX’e на уровне макро. Конечно, большое преимущество заключается в том, что вы можете совместить вашу программу вывода с другими — ваши исходники будут работать на любых системах. Однако я считаю, что специальные проекты требуют большого количества пользовательских версий программы. Но это пока не случилось.
Джереми: В связи с этим такой вопрос ... Если разобрать вертикальный бокс на составные части, поиграть с ними и снова его собрать, то есть одна вещь, которая у вас не получится. Если бокс сдвинуть влево или вправо, когда вы его разбираете, то вы потеряете эту информацию. [Дон: Ну да?] Вы можете задать команду \lastbox и получить бокс, но эта команда не скажет вам, был ли он перемещен. По-моему, я видел в вашем списке плохих идей, с которыми непонятно что делать, что-то связанное с разбиранием на части. Это так?
Дон: Я не уверен что действительно упоминал о чем-то подобном. Нельзя быть уверенным в том, что ни у одного пользователя не будет доступа к результатам с округлением погрешностей, которые на разных машинах разные. Поэтому мне надо быть очень осторожным с ДеХ’ом, чтобы он оставался мобильным — каждый раз, когда производится подсчет клея. Хотя я не думаю, что это произойдет при сдвиге влево и вправо. Одним из последних изменений в TgX’e в недавнем прошлом было ... не помню что. Может быть, кто-нибудь вспомнит ... например, Петер [Брайтенлонер]. По-моему, это именно вы предложили: было что-то в подпрограммах hlist-out и vlist_out, что контролировало величину сдвига бокса и ... ?
Петер: Это было в отточиях. Для боксов отточий нужно следить за величиной сдвига, но в структуре данных это значение всегда было равно нулю ...
Т^Х брал величину сдвига, прибавлял ее и снова вычитал или что-то в этом роде.
Дон: Да, дело в том, что у меня там есть некий код —это не ошибка, потому что от него никогда никакого вреда не бывает — но я всегда добавлял к чему- нибудь ноль. Мы убрали это, чтобы никого не смущать. Величина, на которую сдвигается бокс, заложена в боксе. Если текст расположен вертикально, то это величина сдвига вправо, а если горизонтально, то это величина сдвига вверх и вниз. Величина сдвига в боксах отточий никогда не может быть ненулевой. В общем, это значение не установлено должным образом, возможно, это ошибка и в 1997 году вы получите за нее большие деньги. [ Смех. ]
Джереми: Я не считаю это ошибкой, я думаю, что это просто проблема, с которой вы не смогли справиться, и она касается разбирания и собирания на части .... Ведь это было сделано не нарочно?
Дон: Вообще-то количество подобных вещей было слишком большим, чтобы все их предвидеть, и я рад, что их не оказалось еще больше. Простите, если что не так.
Камерон: Здесь задавалось много вопросов типа «почему вы сделали то-то и то-то?», но я думаю, что на самом деле надо спросить вот о чем: если бы вы делали это снова, как это пытаются сделать некоторые люди, могли бы вы что-то этим людям посоветовать? Важно не то, почему вы сделали это 20 лет назад именно так, а не иначе. Но если кто-то займется этим снова, скажете ли вы им, о чем нужно подумать в первую очередь?
Дон: Я просто рекомендую обратить особое внимание на дизайн и тестирование, а также привлечь для помощи большое количество пользователей, чтобы они показали вам, с какими проблемами они столкнулись, и посмотрели как можно больше примеров. Конечно, это уже делается. Нужно расставить все точки над i, и для этого придется много работать в течение длительного времени. Чем больше вы делаете открытий или осознаете, как много вам предстоит решить, тем больше времени уходит на полное решение всех проблем.
Самыми тяжелыми являются усилия, направленные на конвергенцию вместо дивергенции. Вам требуется загрузить множество разных исходников, но при этом нужно избежать синдрома коллектива ... вам понадобится небольшое количество людей, которые могут сделать так, что все сходится. Иначе во всех коллективных проектах вы столкнетесь с большой проблемой, которая заключается в том, что каждый член коллектива доволен только своей частью конечного результата. Кроме того, у вас будет много несовместимого материала, в основном, по политическим причинам. Очень трудно произвести детальную проверку как можно большего количества вещей на предмет согласованности и конвергенции. Проблема управления была самым тяжелым аспектом развития TgX’a.
Если вы изучите статью «Ошибки Т^Х’а», вы увидите, как шел процесс. Вначале существовал один пользователь, и я потратил много времени, чтобы удовлетворить свои запросы. Затем у меня появилось 10 пользователей, и возник целый уровень новых трудностей. Затем появилось 100 пользователей,
а с ними и другой уровень проблем. Потом было 1000 пользователей, потом 10 000, и все это были необходимые специальные фазы развития. Я не мог заниматься с 10 000 человек, пока я нс разобрался с тысячью. Но каждый раз, когда проходила новая волна изменений, возникала идея улучшить Т^К, а не разнообразить его для выполнения новых задач. Поэтому когда я говорил, что в основном я все делаю так же, как и раньше, я имел в виду, что у меня по-прежнему есть горизонтальные и вертикальные списки, боксы, клей и т. д. Эти основные структурные принципы позволяют максимально использовать небольшое количество концепций и иметь дело с разными видами набора. Но я не говорил, что поступил бы точно так же с величиной сдвига. Все эти вещи чрезвычайно важны, но я буду сохранять ту же основную структуру.
Барбара: Нам пора заканчивать. Я хочу поблагодарить Дона за то, что он нашел время ответить на все вопросы. Позволю себе задать последний вопрос: сможете ли вы сделать это снова в 2011 году, через 16 лет?
Дон: Вполне вероятно. [ Смех.] 32 — это не так захватывающе, но тоже ничего. Большое спасибо. [Бурные аплодисменты.]
Рис. 11. Президенты TUG’a, присутствовавшие на 16 ежегодной конференции: Пьер Макей (1983-1985), Барт Чайлдс (1985-1989), Дон Кнут (1977-1980), Нельсон Бийби (1989-1991), Кристина Тиле (1993-1995), Мишель Гуссенс (1995-1997). Фото Люции Дитше.
Примечания переводчика
Перевод текста на рис. 2:
Страница xi замена для упражнения 3 25 марта 1995
[5^] Леонард Эйлер предположил в 1772 г., что уравнение ш4 + ж4 + г/4 = г4 не имеет решений для положительных целых чисел, но в 1987 г. Ноам Элкис доказал, что существует бесконечно много решений [см. Math. Comp. 51 (1988), 825-835]. Найдите все целые решения, такие, что 0<w<x<y<z< 106.
[М50] Докажите, что если п — целое число ип>4, уравнение wn + 2П + т/п = zn не имеет решений для положительных целых чисел ш, ж, у, z.
Перевод текста на рис. 9 (завещание Д. Кнута):
Самое последнее изменение (должно быть сделано после моей смерти) Фх module 2
Sd banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts} Qy
Sd banner=='This is TeX, Version $\pi$' {printed when \TeX\ starts} Sz
Когда это изменение будет сделано, должны быть изменены и соответствующие строку в томе В, а также на с. 23 The TeXbook. Моей последней волей по отношению к ТеХ'у будет запрет на любые изменения при каких бы то ни было обстоятельствах.
Усовершенствованные системы не должны носить имя ТеХ; это имя должно относиться только к моей программе, за которую я несу персональную ответственность. — Дон Кнут
* Возможно неплохие идеи, которые не будут внедрены: . классы пометок, аналогичные классам вставок
* Ошибки дизайна, обнаруженные слишком поздно и потому не зафиксированные:
. в символьных шрифтах должны быть дополнительные параметры для управления расстоянием между линейками и текстом в \over, \sqrt, и т.п.
. когда внутри абзаца меняется \lccode, многоязычный набор работает некорректно
* Плохие идеи, которые не будут внедрены:
. некоторые хотят иметь возможность удалять произвольные элементы списков, но это никогда не будет работать, потому что некоторые из таких элементов (керны или акценты, например) зависят от арифметики с пл мающей точкой
. если кто-то остро нуждается в разрядке, он может сделать это в своей собственной версии (например, обобщить подпрограмму hpack), но НЕ называть это ТеХ.
Вопросы и ответы, II
[Заседание, посвященное ответам на вопросы в Карловом университете в Праге 9 марта 1996 г. Впервые опубликовано в журнале TUGboat 17, (1996, 355-367) под редакцией Кристины Тиле и Барбары Битон.]
Карел Горак: [Вступительное слово на чешском, затем на английском языке.]
Я очень рад, что у меня есть возможность представить вас, профессор Кнут, нашей аудитории, которая в основном состоит из членов чехословацкой группы пользователей T^X’a CsTUG, а также из нескольких академиков из Праги, так как это заседание организовано группой CsTUG и математическим факультетом Карлова университета. Мы очень рады видеть вас здесь, и я буду счастлив вручить вам от имени Карлова университета специальную медаль. [ Бурные аплодисменты. ]
Дон Кнут: [Удивленно.] Большое спасибо.
Профессор Иван Нетука: Профессор Кнут, дорогие коллеги, дорогие друзья, леди и джентльмены. Я горжусь тем, что мне выпала честь приветствовать профессора Дональда Кнута, так же, как большинство из вас гордится тем, что находится в этом зале, благодаря декану физико-математического факультета Карлова университета профессору Бедрику Седляку.
Насколько я знаю, профессор Кнут приехал в Прагу первый раз. Несмотря на это, он здесь очень известен, и не только среди всех математиков и всех специалистов по информатике, но и среди многих физиков и даже среди тех, кто не имеет отношения к этим наукам. Здесь понимают значение монографии Дональда Кнута The Art of Computer Programming. Многие из нас имели возможность прочитать прекрасную брошюру Surreal Numbers. Мы знаем, что любимый способ Дональда Кнута описать информатику — это сказать, что она является изучением алгоритмов. Мы разделяем точку зрения, что изучение алгоритмов открыло множество новых интересных математических проблем и что оно дает стимул многим областям математики, страдающим от недостатка новых идей.
Мой личный опыт — опыт математика — показывает, что для каждого математика существует такая личность, вклад которой в его область чрезвычайно
Вогнутая сторона памятной медали (в натуральную величину).
велик. Здесь мы видим редкий случай, когда в этом утверждении порядок может быть обратным: существует такая личность, вклад которой для каждого математика велик, чрезвычайно велик. И вот мое краткое доказательство: Дональд Кнут — Т^Х.
Профессор Кнут, в знак признания ваших достижений в информатике, математике, а также в компьютерной полиграфии, которая дала обществу отличный инструмент для представления научных результатов, факультет математики и физики Карлова университета постановил наградить вас памятной медалью факультета. Я счастлив вручить ее вам. [Бурные продолжительные аплодисменты. ]
Дон: Ну что же, очень красивая медаль и приятный сюрприз. Думаю, что все могут подойти и посмотреть на нее. •
«Universitas Carolina Pragensis» — значит, тут все знают латынь, наверное, и мне придется сегодня говорить на латыни. [ Смех. ]
Мои познания, касающиеся чешского языка, невелики, но кое-что я пытался выучить. На этой неделе на многих дверях я видел слово «Sem». [Смех.] А когда я сегодня пришел в эту аудиторию, я видел много надписей «ТУХ». [ Смех.] Поэтому я подумал, что надо бы создать особо мощную версию ТуСа [пишет на доске «SemT^X».]; [смех усиливается.] но кто знает, не опасно ли это ...
Выпуклая сторона с изображением печати Карлова университета в центре.
Сегодня я не подготовил лекции, но хочу поговорить о том, что вам интересно, т. е. ответить на ваши вопросы. Это традиция, которой я придерживался в Калифорнии: самое последнее занятие в Станфорде я посвящал ответам на вопросы. Я говорил студентам, что они могут не приходить на это занятие, но если придут, то я отвечу на любые их вопросы. Эту традицию я перенял у профессора [Ричарда] Фейнмана в Caltech. И сегодня я решил поступить так же. Это очень хорошая идея, и я рекомендую всем преподавателям отводить последние занятия на вопросы и ответы.
Недавно я сделал в Интернете несколько страничек, которые вы можете посмотреть по адресу http://www-cs-faculty.stanford.edu/~knuth. Там я поместил ответы на все часто задаваемые вопросы. Но сегодня вы можете задавать мне нечасто задаваемые вопросы. [ Смех. ] Кстати, расскажу вам еще одну шутку, а потом мы начнем. Вы знаете, как выглядит сайт 0J — О. J. Simpson-—в США? Вот так: «http colon slash slash slash backslash slash escape». [Смех.]
А теперь задавайте, пожалуйста, вопросы, [пауза.]
Ну что же, если вопросов больше нет ... [ Смех. ] Вы можете говорить по-чешски, а кто-нибудь потом переведет.
X: Может быть, начнем с такого вопроса? Я тщательно изучил ТУС и столкнулся с проблемой, когда меня попросили взять интеграл с тильдой. Я обнаружил, что, видимо, в ТЁХ’е такого нет, потому что мы не можем определить поправку на курсив к боксам.
Дон: Поправка на курсив — это ... С каждым символом связано ограниченное количество информации, относящейся к структуре символа; [рисует на доске] у нас есть высота, глубина, ширина и поправка на курсив.
Вот и все предусмотренные числа. А в математическом режиме поправка на курсив используется по-другому. В математическом режиме поправка на курсив часто используется для нижних индексов. Это та величина, на которую вы должны сдвинуть нижний индекс влево. Без поправки на курсив Т^Х напечатает «Р sub п» (Рп) вот так: Рп.'
Поправка на курсив к знаку интеграла —это, видимо, другой случай, потому что в больших операторах поправка на курсив используется для того, чтобы компенсировать смещение между нижним и верхним пределами. В любом случае, здесь есть только одно число. Если вам нужна особая конструкция, требующая большего количества чисел, то единственный известный мне способ — создать для этого специальный макро. Я бы поместил информацию где-нибудь на уровне T£X’a, но не внутри и не в символе. Вам потребуется создать такую структуру, где будет присутствовать эта информация. Я не знаю сейчас общего решения, но, безусловно, если вы нацишете ... я д^же не могу сейчас вспомнить названия ... в общем, это тот же самый механизм, с помо- def щью которого можно поместить что-либо над знаком равенства, например = . Это определено в plain Т£Х’е с помощью макро ... Я бы построил это вне таких примитивов, но если у вас другие интегралы, вам нужно, чтобы тот, кто разработал шрифты ...
X: Я нашел решение, но не с помощью ТЁК’а. Для создания специальных символов я использовал МЕТА FONT ...
Дон: Да, использование МЕТА FONT — это идеальный способ достичь нужного результата, но тогда всем придется иметь ваш код METAFONT’a и компилировать ваш шрифт. Просто комбинируя боксы и клей, вы можете должнцм образом расставлять имеющиеся символы. Вы можете просто создать \vbox или \vcenter чего-нибудь, а затем поместить какой-нибудь \hbox с керном, затем тильду и т. д. Я не знаю другого ^простого способа все точно выполнить, кроме балансировки, так как в случае со знаками интеграла все усложняется видимыми пропорциями — очень сложно учесть все случаи.
Моя философия относительно ТЁХ’а заключалась в том, что я хотел получить систему, которая с легкостью справлялась бы с 99% всех случаев, и я знал, что всегда что-то еще останется. Но я понимал, что этот остаток понадобится только тем, кто очень заботится о своих статьях, и если они потратят на это только 1% своего времени, то смогут радоваться своему вкладу и их будет согревать мысль, что они смогли добавить что-то свое. Поэтому я не пытался все автоматизировать. Я считаю, что следует подумать о том, что еще можно автоматизировать, но я не верю, что когда-нибудь будет автоматизировано абсолютно все.
Карел Горак: Мне очень интересен ход ваших мыслей: когда вы начали думать о создании ТЁХ’а и системы для набора книг, то в какой момент вы поняли, что вот также понадобятся и некоторые буквы, т. е. нужен не только ТЁХ, но и МЕТА FONT? Я не слишком много знаю о всех шрифтах, используемых в компьютерном наборе, но думаю, что не так уж много шрифтов подошло би для ТЁХ’а. Так может быть, вы с самого начала стали думать о METAFONT’e?
Дон: Именно так.
Давайте вернемся в май 1977 года. Я сел перед компьютером и начал писать для себя меморандум, в котором формулировал свои представления о хорошем языке для набора. Через две недели я начал работать над шрифтами. У меня намечался годичный отпуск для научной работы, и с конца 1977 по начало 1978 года я не должен был вести занятия. Я думал, что напишу систему для набора только для себя и своего секретаря. [ Смех. ] Я не предполагал когда-нибудь увидеть ТЁХ на щитах на трамвайных остановках в Брно [смех] или на афишах в знаменитых соборах. ТЁХ предназначался для моих личных целей, и у меня был год на его создание. И я думал, что это будет легко. Итак, в мае 1977 года я отправился в Xerox PARC, место, где зародились и внедрялись идеи мышей, окон, интерфейсов и тому подобных вещей. Я знал, что там имеют дело со сплайнами и графической формой букв. Я увидел за компьютером Батлера Лэмпсона, который подбирал сплайны для контуров увеличенных букв, и подумал: «Пожалуй, я заключу на этот год трудовое соглашение с фирмой Xerox PARC и буду пользоваться их техникой для создания шрифтов».
Я с самого начала знал, что хочу иметь шрифт, облеченный в чисто математическую форму; я хотел иметь нечто такое, что могло бы быть приспособлено к технологии по мере ее изменения, и поэтому мне нужно было иметь математическое описание букв. К сожалению, в фирме Xerox мне сказали: «Пользуйтесь, пожалуйста, нашим оборудованием, но учтите, что все ваши разработки станут собственностью нашей фирмы». Я не хотел, чтобы какая- то часть моей работы стала чьей-то собственностью, и не хотел, чтобы люди платили деньги за пользование моей системой ... Математическая формула — это просто числа, так почему кто-то должен владеть этими числами?
Поэтому я работал только в Станфорде в лаборатории искусственного интеллекта, где было очень примитивное оборудование. У нас были телекамеры, и мое издательство Addison—Wesley очень мне помогло: оттуда мне прислали
контрольные оттиски моей книги The Art of Computer Programming. Этот процесс 60-х годов, которому я собирался подражать, был весьма любопытным: один экземпляр печатался на монотипе на хорошей бумаге, затем этот экземпляр фотографировали и делали распечатки уже с фотографии. Издательство Addison—Wesley предоставило мне тот самый экземпляр, с которого делались фотографии. Я наводил на него телекамеру и шел от телекамеры к экрану компьютера, чтобы скопировать формы буквы. В то же время мы могли соединить наши дисплеи с телевизором и подключаться к телефильмам; люди смотрели на названия фильмов, брали кадры из фильмов и затем делали шрифт. Нужно было ждать большего количества серий фильма Star Trek или чего-нибудь в этом роде, чтобы получить весь алфавит; в конце концов нам встретилось название с буквой «х». Так в то время мы пытались получить шрифт с помощью телевидения.
Я думал, что это будет легко, но я сразу же заметил, что стоит мне чуть- чуть убавить яркость, как буквы становятся более жирными. Существовало огромное количество вариантов, и я видел на своем экране, что между буквами, которые я сделал в понедельник и во вторник, нет абсолютно никакого сходства. Одна буква была толстой, другая тонкой, а между тем это была одна и та же буква, просто яркость была сильно увеличена. Это и сейчас так: если вы сканируете страницу текста и меняете порог между черным и белым, то даже небольшие изменения совершенно преображают букву. Поэтому я не смог использовать телевидение.
Следующая попытка состояла в том, что моя жена сфотографировала страницы, а затем мы принесли домой наш проектор и спроецировали страницы в длинном коридоре. Я пытался скопировать буквы со стены. Но тут я понял, что разработчики этих шрифтов руководствовались вполне определенными идеями. Между буквами была некая логическая связь. Возьмем, например, буквы ‘m’, ‘n’, Ч’ и ‘1’. Я заметил, что ширина ‘т’ равна 15 единицам, ширина ‘п’ —10 единицам, а ширина Ч’ —5 единицам. Ага! Существует какой-то шаблон! Ширина Ч’ равнялась 5 единицам, ширина Т —6 единицам, а ширина лигатуры ‘fi’ —10 единицам. Итак, если срезать верхушки некоторых букв, то можно увидеть четкую ритмичность 5 единиц между основными штрихами букв. Здорово —в дизайне были правила! И тогда мне пришло в голову, что, может быть, нужно не копировать формы буквы, а попытаться понять логику. И я смогу сделать полужирный шрифт по тем же правилам, что и светлый прямой шрифт.
Таким образом, правда заключалась в том, что вначале я не знал, что делать со шрифтами. В конце мая 1977 года я начал думать о создании МЕТА- FONT’a.
Я провел лето 1977 года в Китае, оставив моих студентов в Калифорнии и велев им за время моего отсутствия разработать Т^Х. [ Смех. ] Я думал, что это будет очень легко: к моему возвращению Т^Х будет готов, и я смогу взяться за шрифты. Но когда я вернулся, то понял, что задал им неразрешимую задачу. Они, несомненно, много потрудились над тем, чтобы Т^Х печатал по одному символу на одной странице, и это было великим достижением, поскольку мои
инструкции были весьма туманными} Я считал инструкции четкими, однако никто не понимал, насколько они расплывчаты, пока не была сделана попытка разъяснить их компьютеру. И написать программу.
В тот период, когда я не был в Китае, т. е. в июне, в первой половине июля, в сентябре, октябре и ноябре, я большую часть времени занимался созданием шрифтов. И я должен был это делать, потому что тогда не было способа получить шрифт, который выглядел бы точно так же на другой технике. Существовало много хороших шрифтов, но каждый из них был разработан для какого-то конкретного устройства. Не было такого шрифта, который подходил бы двум устройствам. А сотрудники фирмы Xerox PARC, в первую очередь, Джон Уорнок, все еще разрабатывали свои идеи; в конце концов, в 1982 году они основали фирму Adobe Systems. И теперь с помощью многих выдающихся разработчиков они создали множество красивых шрифтов. Но это произошло позже, более чем через 5 лет после того, как мне потребовались не зависящие от платформы шрифты.
Моя лекция в Американском математическом обществе была запланирована на январь 1978 года. В стенограмме этой лекции отражена работа над шрифтами, которую я проделал в 1977 году. Эта работа потребовала гораздо больше времени, чем я мог себе представить. Я думал, что будет несложно получить нечто привлекательное, но это было лет за шесть до того как я получил то, что выглядело удовлетворительно.
Итак, первая важная идея состояла в том, чтобы с помощью математических определений разработать шрифты, которые могли бы использоваться на разных компьютерах и наборных машинах, включая будущие устройства, к тому моменту еще не изобретенные. Вторая идея заключалась в постижении замысла. Я не просто копировал рисунок букв, но пытался постичь их логику. Моя цель заключалась в том, чтобы понять замысел разработчика, а не просто скопировать результат этого замысла.
До мая 1978 года я не работал в ТЁХ’е; сначала я сделал шрифты. Для статьи «Математическая типография», в которой было отражено мое выступление в Американском математическом обществе в январе 1978 года, я сделал специальные буквы высотой около 4 см. Каждую из них я поместил на большой лист бумаги и сфотографировал.
Вот такой длинный ответ. Надеюсь, я ответил на ваш вопрос.
Карел: У меня еще один вопрос относительно вашей системы: когда вы начали изучать полиграфию, у вас уже были какие-то знания или вы овладевали ими постепенно в процессе изучения? У меня сложилось впечатление, что в книге The ТвХЬоок очень много написано о типографском наборе. Вы сумели очень глубоко изучить полиграфию, гораздо глубже, чем некоторые профессиональные наборщики, использующие системы с окнами и мышками. По книгам, которые прилагаются к этим системам, никогда не изучишь полиграфию на таком уровне.
Дон: Спасибо. Итак, каким был мой багаж знаний до 1977 года? Когда я учился в средней школе —это такой американский вариант европейской гимназии—я подрабатывал набором на мимеографической машине. Я не знаю, с чем это можно сравнить. На мимеографе у вас есть нечто вроде синего желатинового материала. Литера печатной машинки бьет по нему и получается отверстие. Я также использовал освещенный стол и специальные карандаши и пытался писать ноты и делать рисунки на трафаретке мимеографа. Летом я печатал, а потом использовал мой резец для вырезания картинок на геле. Поэтому у меня есть некоторые сведения о полиграфии. Конечно, качество печати было не бог весть каким, все выглядело очень непрофессионально, однако я понял, что могу постичь этот процесс. После изготовления трафаретов я работал на мимеографе, резал бумагу и т. д. Я делал это в качестве ученика.
Позже в подвале нашего дома появился печатный станок, на котором мой отец выполнял работу для нескольких школ Милуоки; делалось это в целях экономии денег, поскольку школы не могли позволить себе иметь профессионального печатника. Он также делал работу для некоторых наших друзей- архитекторов. Школам были нужны программки концертов или выпускных вечеров, билеты на футбольные матчи, и мой отец все это делал в нашем подвале. Он начал с мимеографа, затем стал пользоваться машиной под названием VariTyper, которая была чудесной, потому что на ней получались пропорциональные пробелы —одни буквы были шире других. Шрифты были ужасными, но у нас была эта машина, и я научился с ней обращаться.
Еще позже я начал писать The Art Of Computer Programming. Поэтому к 1977 году я прочитал тысячи гранок. Безусловно, я обращал внимание на типографский набор. Можно сказать, что типографская краска была у меня в крови.
Но я также знаю, что инженеры часто делают ошибку, не обращая внимания на традиции прошлого. Они думают, что начинают все на голом месте, и я считаю ужасным такое отношение к делу. Поэтому в мае 1977 года, когда я только собирался уйти в отпуск на год и посвятить его созданию системы для набора книг, я совершил поездку вместе с членами Станфордской библиотеки — группой любителей книг из Станфорда* Мы посетили Сакраменто и Калифорнию, где жили другие библиофилы. Мы остановились в музее типографского дела, где была страница из Библии Гутенберга и тому подобные экспонаты. У одного человека, живущего в горах, был собственный ручной станок, и я имел возможность попробовать поработать на нем. Всюду, где мы путешествовали, я внимательно смотрел на буквы, представляя себе, как компьютер сможет их нарисовать. И я также видел коллекции самых лучших образцов печати.
В Станфордской библиотеке есть замечательная коллекция типографского материала, переданная в дар Морганом и Элин Гунст, которые всю жизнь собирали образцы красивой печати. Как только я вернулся из поездки, я узнал о коллекции Гунстов, поэтому провел май и июнь за чтением работ Гауди и
1 См. записи, завершающие гл. 24 данной книги.
Цапфа, а также других книг по истории предмета, которые я смог найти. Это было замечательно, но я хотел быть уверенным в том, что смогу отразить в компьютерной форме опыт предыдущих поколений.
Главная моя идея тогда заключалась в следующем. Во времена моей молодости компьютеры могли иметь дело только с числами. Потом появились компьютеры, которые помимо чисел, имели дело с прописными буквами. При этом возможности самовыражения сильно возросли. Но даже при подготовке 1-го тома The Art of Computer Programming, в котором я описал мой компьютер MIX я не ожидал, что компьютеры смогут обрабатывать строчные буквы. [ Смех. ] Язык Pascal был разработан в 1970 году, и там использовались только прописные буквы, а также скобки, запятые, цифры, всего 64 символа.
Позже, в начале 70-х годов появились и строчные буквы, и появилась возможность готовить на компьютере документы, выглядевшие почти так же, как те, которые были набраны типографским способом. Затем появилось программное обеспечение, такое как юниксовская система eqn, с помощью которой можно было готовить документы к печати. Вы, вероятно, знаете, что системы troff и eqn, предназначенные для математиков, были разработаны в лабораториях Bell. Фактически эти системы были разработаны как расширение программы, которая началась в M.I.T. в 1961 году. Она прошла около 5 стадий усовершенствования, и в 1975 году появилась система eqn.
Таким образом, я знал, что есть возможность готовить на компьютерах документы все более высокого качества, которые выглядели бы почти как книги. Обдумывая ТЁК, я сказал: «Пришло время пройти весь этот путь. Не надо пытаться приблизиться к лучшим книгам, а надо пройти весь путь до конца и сделать эти книги!» Моя цель заключалась в создании системы, которая бы делала лучшие книги из всех, которые когда-либо были выпущены, за исключением, конечно, дополнений, сделанных вручную, вроде золотого листика и тому подобных вещей. [ Смех. ] А почему бы и нет? Пора было найти стандартное решение для всех проблем и получить самый лучший результат, а не просто пытаться делать лучше и лучше то, что уже есть. Вот почему я прочитал все другие работы, какие только смог найти —я хотел, чтобы от меня не ускользнула ни одна мысль. Когда я читал книги из коллекции Гунстов, чтобы увидеть, что эти книги могут рассказать мне о типографском наборе и очертаниях букв, я пытался понять, как все это соотносится с сегодняшним днем и как я могу обучить этому компьютер. Конечно, я не во всем преуспел, но я попытался найти мощные примитивы для воплощения большинства идей, которые формировались на протяжении сотен лет.
Безусловно, сейчас у нас есть многолетний опыт, и мы знаем, что возможностей тонких усовершенствований гораздо больше, чем я мог предположить в 1980 году. Работа над проектом заняла больше года, и в результате пользователей стало больше одного. Последующая эволюция описана в моей статье «The errors of ТЁК», в ней же рассказано обо всем, что было после 1978 года*
Software — Practice and Experience 19 (1989), 607-681. Перепечатано с дополнениями в Literate Programming (1992), 243-339.
В 1980 году мне посчастливилось встретиться со многими мировыми лидерами полиграфии. Они смогли многому меня научить, заполнить пробелы в моих познаниях. Мастера обычно не записывают то, что они знают. Они просто делают это. Поэтому в книгах нельзя найти все, и мне пришлось учиться у разных людей. Что касается шрифтов, то интересным было то, что там было 2 уровня: существовал дизайнер шрифтов, который рисовал форму знака, и прансонист, который вырезал шрифт. Дизайнер мог написать книгу, но пуан- сонист этого не делал. Я узнал об оптическом обмане — наш глаз видит не то, что на самом деле есть на странице. Поэтому пуансонист не следовал рисунку строго, а искажал его таким образом, что после того, как все было напечатано и вы смотрели на букву в правильном размере, вы видели то, что нарисовал дизайнер. Пуансонист знал, как делать правильные искажения.
Некоторые из этих трюков уже не применяются на наших лазерных принтерах, поскольку они предназначались только для старых видов шрифтов. Но другие трюки по-прежнему необходимы, так как они позволяют избегать клякс и тому подобных вещей. После того как я проделал первоначальную работу над METAFONT’om, я пригласил Ричарда Саутолла в Станфорд; он работал в университете в Рединге с людьми, которые, по существу, являются пуансонистами наших дней. От него я получил необходимые мне дополнительные знания. Например, когда штрихи должны выглядеть совершенно одинаково, некоторые из них на самом деле чуть тоньше, как внутренняя часть буквы Р — вы хотите, чтобы она была не такая толстая, а немного тоньше; затем, когда у вас будет остальная часть буквы, тонкий штрих будет выглядеть как правильный. Ричард рассказал мне о таком способе улучшения. Подобным вещам я научился у Чака Бигелоу, Мэтью Картера, Криса Холмса, Герарда Унгера, Германа Цапфа и других.
Однако в те времена у нас было очень примитивное оборудование, поэтому шрифты, которые мы могли генерировать с низким разрешением, выглядели непрофессионально. Они были всего лишь дешевым подобием хороших шрифтов. Тогда Станфорд не мог позволить себе иметь дорогие наборные машины, с помощью которых мы могли бы осуществить свои замыслы. И теперь я очень счастлив, что у нас есть такие машины, как LaserJet 4, с помощью которых мой шрифт выглядит так, как мне всегда хотелось, на недорогой машине.
X: Теперь, когда PostScript начинает так широко использоваться, считаете ли вы, что он может успешно заменить МЕТА FONT? Сейчас мы можем использовать Т£Х и PostScript ...
Дон: Вопрос заключается в том, является ли PostScript достаточно хорошей заменой METAFONT’y? Я думаю, что у имеющихся PostScript-шрифтов отличное качество, несмотря на то, что они не используют всех улучшений в METAFONT’e. Они отличаются дизайном высокого качества. Шрифты Multiple Master имеют только 2 или 3 параметра, тогда как шрифты Computer Modern имеют более 60 параметров, но даже только с двумя или тремя шрифты Myriad и Minion выглядят отлично.
Сейчас я работаю с сотрудниками фирмы Adobe, поэтому стало проще заменить шрифты Multiple Master на шрифты из Т^Х’овских документов. Цель заключается в том, чтобы сделать файлы PDF меньше. В системе Acrobat файлы PDF раз в десять больше файлов DVI, но если вам не надо было скачивать шрифты, то они были бы больше только в три раза. В формате Acrobat можно искать команды и достаточно хорошие электронные документы. Поэтому я пытаюсь упростить процесс замены шрифтов Multiple Master. Пока еще они носят не вполне общий характер, хотя, безусловно, мне нравится их качество.
Авторы шрифтов из фирмы Adobe, такие как Кэрол Твамбли и Роберт Слимбах, — это мастера высокого класса; я же был просто любителем. Дизайн моих шрифтов вполне устраивает меня в том отношении, что я могу их использовать в своих собственных книгах, но я бы не осмелился предложить их остальным. Мне очень нравятся шрифты, которые делают другие дизайнеры.
Просить художника шрифтов стать до такой степени математиком, чтобы создать шрифт с 60 параметрами, — это уж чересчур. Специалисты в области информатики понимают, что такое параметры, остальная же часть человечества не имеет об этом понятия. Лет пять назад большинство людей не знало даже самого слова «параметры», и до сих пор это слово остается загадочным. Для человека, занимающегося информатикой, при автоматизации чего-нибудь наиболее естественным является попытаться показать, как будет изменена программа в соответствии с разными спецификациями. Однако для большинства людей это вовсе не является естественным. Большинство привыкло в работе отталкиваться от поставленных задач и давать ответ на конкретную дизайнерскую проблему. Они не хотят давать ответ на все возможные дизайнерские задачи и объяснять, как приспособить решение к каждой задаче. С другой стороны, специалисту в области информатики легко понять связь между вариацией параметров и вариацией программ.
Кто еще?
Ладя Льготна: У меня к вам вопрос: как вы решили написать программу для T£X’a и документировать ее? *
Дон: Я разговаривал с Тони Хоаром, который был редактором серии книг, издававшихся в Oxford University Press. Наша беседа состоялась примерно в ... 1980 году; я пытаюсь вспомнить точно, может быть, в 1979 году, да, наверное, в 1979, когда я был в Ньюкасле. Не помню точно. Он сказал мне, что я должен опубликовать мою программу для T£X’aJ
В процессе написания Т^Х’а я второй раз в жизни пользовался системой под названием «структурное программирование», которая произвела революционный переворот в способе программирования, принятом в середине 70-х годов. Я занимался преподаванием и знал, что люди пользуются структурным программированием, но с 1971 года я не писал больших компьютерных программ. В 1976 году я написал свою первую структурную программу, которая
Впоследствии я посмотрел запись и обнаружил, что моя память сильно меня подвела. До Хоара дошли слухи о моей работе, и он написал мне в Станфорд письмо с предложением об издании. Я ответил на это письмо 16 ноября 1977 года —гораздо раньше, чем я .думал.
была довольно большого размера —что-то около 50000 строк кода, точно не знаю. (Это уже другая история, и когда-нибудь я могу вам ее рассказать.) Это дало мне некоторый опыт написания легко читаемых программ. Несколько позже, когда я начал писать ТуХ (я начал работу над ТЁХ’ом в октябре 1977 года и закончил в мае 78), я сознательно использовал идеи структурного программирования.
Профессор Хоар искал примеры программ достаточно большого размера, которые можно прочесть. Вообще-то для профессора информатики это очень рискованно —показать кому-нибудь большую программу. В лучшем случае он может опубликовать маленькие подпрограммы в качестве примера того, как писать программы. И мы можем наводить лоск, пока ... ну, в общем, каждый пример в литературе о таких программах содержит ошибки. Тони Хоар был пионером в области испытания программ на корректность. Но если бы вы посмотрели на детали ... читая некоторые статьи, я обнаружил, что могу найти три ошибки в программе, которая была признана правильной. [Смех.] А ведь это были маленькие программы. И вот он говорит: «Возьми мою большую программу и обнародуй ее со всеми се рискованными местами». Конечно, я разработал Т^Х таким образом, что он мог продолжить многовековую историю разных идей. Там должны были быть погрешности. Поэтому перспектива показать кому-нибудь свою программу меня испугала. Но потом я осознал всю необходимость примеров больших программ, которые можно рассматривать как образцы хорошей практики, а не просто маленькие программы.
Некоторые ценные идеи я почерпнул у одного бельгийца, чья система описана в моей статье по документированному программированию. Он прислал мне отчет о своей системе на 150 страницах, на который, по-видимому, его вдохновил труд «The spirit in the machine». Первые 99 страниц этого отчета представляли собой философский трактат, а сотая страница начиналась с примера. И этот пример открыл мне глаза на то, что программа должна выглядеть как гипертекст (как мы видели сегодня). Он предложил разбивать сложную программу на маленькие части. Чтобы понять сложное в целом, нужно просто понять маленькие части и связь между каждой частью и соседней с ней.
В феврале 1979 года я разработал систему под названием D0C и UND0C ... нечто вроде системы WEB, которая появилась позже. D0C была похожа на WEAVE, a UND0C — на TANGLE. Я поиграл с DOC и UND0C и сделал а макет из маленькой части ТуХ’а. Я не использовал DOC целиком, а взял внутреннюю часть, которая называется getnext, самую сложную часть Т^Х’овской программы загрузки, и конвертировал ее в D0C. Получилась маленькая 20-страничная программа, которая показывала ТЁХ’овскую часть getnext, написанную в DOC’e. Я показал ее Тони Хоару и некоторым другим, а главное —Луи Трабб Пардо, и получил от них кое-какой материал, связанный с идеями и форматом.
Потом у нас в Станфорде был студент Забала — испанец с двумя фамилиями. Мы звали его Иньяки, а вообще его звали Игнасио. Он взял весь ТЦХ, который я написал на языке под названием SAIL (Stanford Artificial Intelligence
Language — Станфордский Язык Искусственного Интеллекта), и конвертировал его в Pascal в формате DOC. Эта версия TgX’a распространилась по миру к 1981 году. Позже, в 1982 году, когда я писал ТЦК82, я смог использовать его опыт и все материалы, которые он получил от пользователей, и создал систему, которая стала системой WEB. Это был двухнедельный период, когда мы подбирали разные названия для DOC и UND0C, и победителями стали TANGLE и WEAVE. В те времена в Станфорде каждую пятницу собиралось около 25 человек для обсуждения компьютерной полиграфии. У нас был для обсуждения целый ворох идей, и в этом, в основном, заключается успех Т^’а и METAFONT’a.
Другая программа, которую я писал в то время, называлась Blaise, потому что это был препроцессор к Pascal. [ Смех. ]
Петр Ольшак: У меня два вопроса. Что вы думаете о lAT^X’e с точки зрения расширения ТЁХ’а на уровне макрокоманд? Я думаю, что ТЁХ был создан в соответствии с философией plain TgX’a, что означает, что пользователь прочитал книгу The ТвХЬоок ... [ Смех.], тогда как 1АТЁХ создан с помощью макрокоманд на основе plain ТЩХ’а. И второй вопрос: почему ТЁХ не используется в коммерческих фирмах? Там пользуются только мышкой и программами, построенными по принципу WYSIWYG.
Дон: Сначала вы спросили, что я думаю о ТАТЁХ’е. Мне всегда хотелось, чтобы было много разных пакетов макрокоманд, предназначенных для разных групп пользователей, а 1АТЁХ, безусловно, является лучшим примером такого пакета макрокоманд. Раньше было и много других. Но Лесли Лэмпорт лучше всех понял, как сделать эту работу хорошо. Существует также Дд^5-ТЁХ, и многие математики используют макро Макса Дьяса, известные первоначально как МахТЁХ и позже официально названные Facil ТЁХ —это было до появления ТАТЁХ’а. Майкл Спивак и Лесли Лэмпорт предоставили мне очень важные материалы, связанные с тем, как я могу улучшить ТЁХ, чтобы поддерживать такие пакеты. Я не хотел форсировать ... мне близка идея системы макро, которую можно приспособить к специальным случаям. Сам я не использую 1АТЁХ, потому что у меня нет времени на чтение руководства. [ Смех.] В ТАТЁХ’е больше возможностей, чем это нужно мне. И кроме того, я, конечно, достаточно хорошо понимаю ТЁХ, и для меня легче не пользоваться конструкциями высокого уровня, если это вне моего контроля.
Однако для многих людей 1АТЁХ является более простой системой, в нем автоматизированы те вещи, которые люди всегда хотят автоматизировать. На мой взгляд, автоматизация касается в основном тех случаев, которые я считаю небольшой частью работы в целом. Задача ручной настройки библиографии не угнетает меня, однако других это очень раздражает. Я могу понять, почему многие люди предпочитают такой стиль работы.
Кроме того, когда вы пишете в такой системе, как 1АТЁХ, вам проще следовать правилам, которые позволяют другим программам понять структуру вашего документа. Если вы работаете в plain ТЁХ’е, вас не связывает никакая структура и вы можете вмешаться в любой процесс автоматизации библиографии и тому подобных вещей в вашем документе. Если вы ограничите себя
одним видом основной структуры, тогда станут возможными другие процессы. Таким образом, дисциплинированное использование Т£Х’а может быть вполне ценным. Оно допускает перевод в другие структуры, на другие языки и т. д.
Но я пользуюсь гЩХ’ом для большого количества разных случаев, где гораздо труднее запастись впрок программами. lAT^X находится на более высоком уровне, и приспособить его к новым задачам непросто. Очень часто я просыпаюсь утром и думаю, как сделать те или иные вещи ... или моя жена приходит ко мне и спрашивает: «Дон, не мог бы ты сделать для меня то-то и то-то?» Поэтому я создал 10 строк ТеХ’овских макро, и вдруг получился новый язык специально для такого рода документов. Многие из моих электронных документов выглядят так, как будто в них нет никакой разметки.
Теперь, что касается вашего второго вопроса о том, почему TgX недостаточно используется в коммерческих публикациях. На самом деле я был приятно удивлен, когда увидел, как много коммерческих изданий в Чехии сделано в T^X’e. В четверг вечером я видел три или четыре чешско-английских словаря, подготовленных в ТуСе, и, как вы знаете, Т^Х используется для издания новой чешской энциклопедии. А Петр Сойка показал мне авангардный роман, изданный в Т^Х’е, с некоторыми симпатичными фокусами, касающимися новаторского вывода страниц. В Америке Т^Х используется исключительно в легальных изданиях, а за кулисами —во многих крупных проектах.
У меня никогда не было намерения создать систему, которая была бы универсальной и использовалась бы всеми. Я всегда хотел написать систему, которая использовалась бы только для самых лучших книг. [ Смех. ] Я имею в виду издания со сложным набором, либо такие, авторы которых согласны были затратить дополнительные усилия для достижения отличного качества полиграфии. Я никогда не рассчитывал на то, что Т^Х будет конкурировать с системами, созданными для массового пользователя.
В сущности, дух конкуренции мне чужд. Я чувствовал себя очень счастливым от того, что работал над системой, предназначенной прежде всего математикам. Насколько я знаю, ни один человек на свете не обиделся на меня за то, что я упростил набор математических текстов. Наборщики считали математические книги сущим наказанием и брались за них крайне неохотно. Они воспринимали как штраф ужасную дополнительную работу по набору математических текстов. Я никогда не ждал, что TgX вытеснит системы, используемые в газетном издательстве и тому подобных учреждениях. Так получилось, что после того, как Т^Х стал использоваться, мы обнаружили, что можно кое-что улучшить и в тех текстах, которые не связаны с математикой. Например, в качестве эксперимента мы набрали заново страницу из журнала Time, чтобы показать, насколько лучше она будет выглядеть, если применять хороший алгоритм разбиения на строки} Но я вовсе не рассчитывал на то, что такие журналы когда-нибудь будут пользоваться моей системой, потому что это была индустрия, связанная с миллиардами долларов, и я не хотел оставить кого-нибудь без работы или причинить другие неприятности.
Поэтому мне было очень неприятно, когда я в начале 80-х годов обнаружил, что существует человек, который очень расстроен тем, что я изобрел ТЕХ. Он много трудился над созданием коммерческой системы набора математических текстов, и теперь стал терять клиентов. Поэтому он написал в фонд National Science Foundation письмо, в котором говорилось: «Я являюсь налогоплательщиком, а вы пользуетесь выплаченными мною налогами, чтобы помешать моему бизнесу». Это сильно меня огорчило. Я думал, что все, что я делаю, направлено на общее благо, а оказалось, что есть человек, которому я определенно навредил. Но я также подумал, что я все равно должен сделать Т^Х доступным всем, несмотря на то, что он был разработан с некоторой помощью правительства. Я не думаю, что правительство должно было давать финансовую поддержку только чисто академическим и бесполезным проектам.
Пожалуйста, ваш вопрос.
X: Мой вопрос связан с применением ваших типографских программ в коммерческих учреждениях вроде мастерских DTP. Я бы хотел узнать об использовании частей ТеХ’овского исходника. Вы объяснили, что программисты могли свободно включать части Т^Х’овского исходника в свои собственные программы. Не могли бы вы привести несколько ярких примеров?
Дон: Этот же вопрос мне задавали прошлым летом на заседании TUG во Флориде, которое было посвящено вопросам и ответам^ Я думал, что будет вполне обычным делом иметь специальные версии Т^Х’а. Я разработал Т^Х таким образом, что у него внутри имеется много специальных приемов, и вы можете писать дополнения и получать более мощную систему, которая уже приспособлена к работе.
По-видимому, я думал, что в каждом издательстве, пользующемся ТёХ’ом, будет свой программист, который будет разрабатывать специальную версию Т^Х’а, если понадобится издать Библию или арабско-китайский словарь или еще что-нибудь специфическое. Если они будут издавать энциклопедию, у них будет собственная версия Т^^’а, применяемая для этой цели.
Макроязык полон по Тьюрингу —он может сделать все, но зачем нам пытаться делать все на языке высокого уровня, если некоторые вещи можно легко сделать на более низком уровне? Поэтому я встроил в ТцХ специальные приемы и сделал эти части ТцХ’а демонстрационными, чтобы человек, который читает код, мог видеть, как расширить TgX для других целей. Мы предусмотрели некоторого рода приемы для химических формул или для замены горизонтальных штрихов, которые должны были быть сделаны в машинном языке для специальных приложений.
Конечно, если бы я сам занимался издательским бизнесом, я бы имел десять разных версий Т^^’а для десяти разных сложных проектов. Все они выглядели бы почти так же, как Т^Х, но больше ни у кого не было бы точно такой же программы — это было бы не нужно, поскольку никто бы не выпускал книги точно так же, как мое издательство.
1 См. гл. 1 данного издания, слайд 21.
Я считал, что появится много таких специальных версий. И конечно, в середине 80-х годов настал такой момент, когда более тысячи человек поняли устройство TgX’a. Они достаточно хорошо разобрались в сложностях программы, они прочитали ее и если бы захотели, то могли бы создать много видов дополнений. Но сейчас я должен сказать, что количество людей с рабочим знанием устройства ТЁХ’а, больше ста, разумеется, но меньше тысячи. Моя идея не получила такого развития, какого я ожидал.
Одним из наиболее крупных исправлений является то, что я видел на этой неделе в Брно —студент по имени Тан создал систему, которая практически выводит текст в формате PDF вместо DVI. Если вы введете \PDFon, то вывод будет представлен в виде файла, который тут же может быть прочитан системой Acrobat Reader. Десять лет назад я также считал, что пользователи будут обращаться непосредственно к PostScript’y, но насколько я знаю, этого до сих пор не произошло.
Никто пока еще не выпустил в T^X’e специальное издание Библии в том виде, как я это предполагал. Некоторые расширения были сделаны в Исландии. Я не помню, делали ли они это на более высоком уровне, но думаю, что в основном они работали на уровне макрокоманд, а может быть, только в них.
В любом случае, я сделал возможным набор очень сложных вещей. Я всегда считал, что если вы имеете дело со специальным приложением, то захотите иметь специально настроенную программу, потому что так легче всего делать подобные сложные вещи.
X: Я хочу узнать, какие особенности TgX’a существовали в первой версии. Например, были ли в первой версии разбиение на строки, переносы и использование макрокоманд?
Дон: Самая первая версия была разработана в апреле 1977 года. Там были макро и алгоритм разбиения на строки. Он был еще недоработан; в то время у меня еще не было всех этих штучек вроде \parshape, но с самого начала, с 1977 года, я знал, что буду обрабатывать абзац целиком, а не просто строчку за строчкой. Алгоритм переносов у нас тогда был не тот, который мы используем сейчас. Он был основан на перемещении приставок и суффиксов; это был очень своеобразный метод, но он охватывал около 80% случаев. Я работал над ним, просто заглядывая в словарь. Например, если слово начинается с «anti-», то после «1» ставится перенос; те же правила действуют в конце слова. -Или если в середине слова появляется определенная комбинация букв, то там будут переносы. Этот подход мне нравился больше, чем метод troff, опубликованный ранее. Старый ТЁХ’овский алгоритм переносов описан в старом руководстве по ТЁХ’у, которое можно найти в библиотеках? *
Итак, вы спросили о разбиении на строки, переносах, макро и тому подобных вещах. Я разработал язык макрокоманд следующим образом. Я просмотрел 2-й том The Art of Computer Programming и выбрал из него наиболее
характерные части. Я сделал макет примерно из 5 страниц этой книги и спросил себя, как бы мне хотелось, чтобы это выглядело в компьютерном файле. И это был полный исходник дизайна?
Однажды вечером я задержался и сделал Т^Х. Я прошелся по 2-му тому и придумал инструкции, которые выглядели естественно — «Здесь я скажу: ‘begin an algorithm’, а затем —‘Algorithm К’, а затем —‘algstep КГ». Так получился небольшой файл, в котором представлен способ, которым я бы хотел вводить текст книги The Art of Computer Programming. Файл также содержал некоторые математические формулы. Формулы были созданы на основе идей eqn; язык troff демонстрировал способ представить математику так, чтобы секретарши могли легко это выучить. В этом заключался дизайн. Затем для поддержки этих возможностей я должен был создать макроязык.
Макроязык разрабатывался в течение 1978 года под влиянием Терри Винограда. Терри писал книгу по лингвистике, по грамматике английского языка? Он хотел использовать макро гораздо интенсивнее, чем я, поэтому я добавил для него \xdef и экзотические параметры.
Алгоритм переносов, которым мы сейчас пользуемся, появился благодаря исследованиям Фрэнка Лианга. Мы работали вместе с ним над методом переносов, и его опыт позволил ему открыть лучший путь, который можно приспособить ко всем языкам, т. е. ко всем западным языкам, в которых есть переносы.
Чтобы разработать правила для правильных пробелов в математике, я выбрал 3 стандарта качественного набора математических книг. Во-первых, это были книги, выпущенные издательством Addison—Wesley, в частности, The Art of Computer Programming. Сотрудники Addison—Wesley, особенно Ганс Вольф, (именно он подавал идеи внутреннего оформления), создали стиль, который в учебниках мне всегда нравился больше всех, когда я был студентом колледжа. Во-вторых, я взял Acta Mathematica выпуска примерно 1910 года; это такой шведский журнал ... его редактором был Миттаг-Леффлер. Его жена была очень богата, и у них были большие возможности для качественной печати математических текстов, поэтому в Acta Mathematica полиграфия особенно хорошая. И третьим источником был экземпляр голландского журнала Indagationes Mathematicae. В Нидерландах существует давняя традиция качественной печати, и я выбрал выпуск 1950 года или около того, в котором опять-таки особенно хорошо были выполнены математические тексты.
Я взял эти три стандарта и внимательно изучил все математические формулы. Я измерил их с помощью телевизионной камеры Станфордского университета, чтобы понять, насколько опущены подстрочные и подняты надстрочные индексы, какие использованы шрифты, как сбалансированы дроби и т. д. Я произвел подробные измерения и спросил себя, каким должно быть наименьшее количество правил, чтобы я смог сделать все то, что сделали они. Я
См. этот отрывок в гл. 24 данного издания.
Language as a Cognitive Process, Volume 1: Syntax (Reading, Massachusetts: Addison- Wesley, 1983).
понял, что могу свести все к рекурсивной конструкции, которая использует только 7 типов объектов в формулах.
Я рад сообщить вам, что 3 года назад Acta Mathematica стала использовать ТЁХ. Таким образом, круг замкнулся. Addison—Wesley, безусловно, тоже пользуется ТуХ’ом, вот только не знаю насчет голландцев —я собираюсь к ним на следующей неделе. [ Смех. ] Но в любом случае я надеюсь продолжить хорошие полиграфические традиции.
Я должен дать слово тем, кто еще не выступал. Джордж?
Иржи Весели: У меня такой вопрос. Вас все время просят внимательно рассматривать все предложения, касающиеся всяких усовершенствований. Однажды меня спросили, есть ли возможность поместить в книге список всех слов, в которых были сделаны переносы. В вашей книге я не смог найти способа сделать это. Я бы хотел узнать вашу точку зрения относительно того, что должно быть включено, а что нет. Что должно быть в специальном пакете, а что в самом ТвХ’е?
Дон: Вопрос заключается в том, что, по моему мнению, должно быть основной частью ТЁХ’а, а что — специальной или что-то в этом роде. Конечно, все эти решения являются произвольными. Я думаю, было важно, чтобы все эти решения были сделаны одним человеком, хотя ... Безусловно, я делаю много ошибок. Я обращался к лучшим образцам, чтобы получить данные из многих источников, но в конце концов я взял всю ответственность на себя, чтобы сохранить некое единство. Всякий раз, когда вы работаете с группой людей над созданием системы, каждый член группы вправе гордиться своим вкладом в конечный продукт. Но потом вы получите гораздо менее унифицированный результат, потому что в нем нашли отражение определенные веши, удовлетворяющие вкусу того или иного человека. Я хотел угодить как можно большему количеству людей, но при этом сохранить единство. Поэтому на протяжении многих лет у нас было каждую пятницу двухчасовое заседание, на котором присутствовали гости со всего мира. Я слушал их комментарии и пытался включить в Т^Х лучшие идеи.
Теперь что касается вашего вопроса о том, почему нет простого способа составить список всех переносов, сделанных в документе. Это очень интересное предложение, и я не помню, чтобы кто-то задавал этот вопрос во время наших еженедельных заседаний. Нас интересуют слова, которые действительно перенесены. Решение сделать это осуществляется программой Краску являющейся частью алгоритма разбиения на строки. Но тот факт, что переносы сделаны программой Краску не означает, что это появится в последнем документе, потому что вы могли выбросить бокс, в котором был сделан этот перенос.
В TgX’e очень легко набрать что-нибудь несколько раз, а потом выбрать только один вариант для вывода текста. Поэтому, чтобы получить определенные характерные примеры переносов, вам нужно зафиксировать их в программе вывода по вашему усмотрению. Сейчас это несложно сделать в модуле, специально написанном для TEX’a. Я бы сказал, что на самом деле вы сейчас можете получить почти в точности то, что хотите, написав фильтр, который
приказывает ТЁК’у включить все отслеживающие опции, которые помещают что-либо в оглавление. Затем маленькая программа-фильтр будет отслеживать информацию через UNIX и выдаст вам слова с переносами. Чтобы написать такую программу, понадобится один вечер, ну может быть, два ... и одно утро. [ Смех. ] Вы можете получить ее сейчас, но я не помню, обсуждали ли мы во время наших еженедельных дискуссий по ТЁК’у, должен ли я сделать такую программу.
Моя статья «The errors of 1ЁХ» содержит полную запись всех изменений, которые были сделаны с 1979 года, с датами и ссылками на код, когда точно появилось каждое изменение. И вы можете видеть, каким путем шла эволюция. Изменения часто появлялись в процессе написания книги The T^book, когда я понимал, что мне очень трудно объяснить некоторые вещи. Я менял язык, чтобы было проще объяснить, как им пользоваться. Это было тогда, когда наши встречи с пользователями и другими подававшими идеи людьми были наиболее массовыми; часть языка, о которой я писал, была частью, которая менялась мгновенно.
В течение 1978 года я собственноручно набирал 2-й том, и это способствовало усовершенствованиям, поскольку я имел дело с клавиатурой. На самом деле, усовершенствования появлялись четко через 500 страниц. После каждых четырех страниц у меня возникала очередная идея, как сделать ТЁК немножко лучше. Но количество способов улучшить любую сложную систему бесконечно, и аксиома заключается в том, что нет системы, которую нельзя было бы усовершенствовать. Поэтому в конце концов я понял, что лучшее, что я могу сделать, —это перестать совершенствовать систему. Это лучше, чем иметь систему, которая все время совершенствуется.
Позвольте мне объяснить. Когда я занимался разработкой ТЁХ’а в лаборатории искусственного интеллекта Станфордского университета, там была операционная система под названием WAITS, которую я считаю лучшей в мире. Четыре программиста в течение всего рабочего дня совершенствовали эту систему. Каждый день операционная система становилась все лучше и лучше. И каждый день она ломалась, и сю долго нельзя было пользоваться.
На самом деле я написал первые наброски книги The ТуХЬоок исключительно во время этих простоев в работе. По утрам я приносил в лабораторию искусственного интеллекта большую порцию бумаги и сидел за компьютером столько, сколько мог. Затем машина ломалась, и я садился писать очередную главу. Потом машину налаживали, и я опять мог немного поработать. Потом она опять зависала, и наступало время писать следующую главу. Наша операционная система все время становилась лучше, но при этом я не мог долго работать за машиной.
Через некоторое время деньги иссякли, и трое из четырех программистов перешли в лабораторию Лоренса Ливермора и стали работать там над новой операционной системой. Остался только один сотрудник, который поддерживал систему, но больше нс совершенствовал ее. И это было великолепно! [ Смех.] В тот год моя работа была продуктивной, как никогда.
Поэтому я знал, что в конце концов надо будет где-то поставить точку и больше не совершенствовать ТЁК. Это будет устойчивая и надежная система, и пользователи поймут, какие у нее есть недостатки и чего она не сможет сделать.
Я по-прежнему считаю, что лучше иметь систему, которая не является движущейся мишенью. Людям в какой-то момент потребуется нечто стабильное и не меняющееся. Конечно, если вдруг окажется, что может случиться нечто катастрофическое, а мы не хотим, чтобы это случилось, то я внесу изменения в ТЁК, чтобы избежать потенциального бедствия. Но я больше не внедряю в него новые заманчивые идеи.
Другие люди работают над расширениями ТЁХ’а, которые пригодятся следующему поколению. И я бы им тоже настоятельно посоветовал в какой-то момент сказать себе: «Вот здесь мы остановимся и больше ничего в нашей системе менять не будем». Потом такая возможность будет у тех, кто придет им на смену.
Карел: Я бы хотел спросить вас об идее курсивного шрифта в математических текстах. Я никогда не видел в других учебниках, чтобы в текстах и в формулах использовались бы разные курсивные шрифты, поэтому мне хочется знать, принадлежит ли эта идея вам или вы позаимствовали ее из этих трех источников? -
Дон; Ни в каких других книгах я не сталкивался с идеей курсива для текстов и математического курсива. Мне хотелось, чтобы математический курсив выглядел как можно лучше, поэтому я начал с него. Но затем я обнаружил, что текстовый курсив не так хорош, как математический, а поскольку у меня был МЕТА FONT, то мне ничего не стоило получить курсивный шрифт, который выглядел бы лучше. Если бы я улучшил текстовый курсив, то подстрочные и надстрочные индексы не могли бы быть расставлены так же.
Частично это объясняется тем, о чем я говорил раньше, —в ТЁХ’е с каждым символом имеют дело только 4 числа. У принтеров раньше было только 3 числа, и не было поправки на курсив. Поэтому они не могли автоматически воспроизводить ТЁХ’овские пробелы; лучшие принтеры допускали расстановку математических пробелов вручную. Однако сейчас курсивные шрифты всех дизайнеров гораздо лучше, чем были раньше. За последние 10-15 лет курсивные шрифты значительно усовершенствовались. Читая старые книги, вы иногда думаете: «Неужели кто-то может прочесть этот курсив?» или «Почему тут стоят такие странные пробелы?» Старые шрифты были основаны на ограничениях типографского сплава. Понятие поправки на курсив в книгах отсутствовало, но я считал необходимым получить нужный пробел.
Когда я показываю математические формулы дизайнерам шрифтов, они не могут понять, для чего нужен математикам курсивный шрифт в уравнениях. Например, нужно скомбинировать прямую двойку с курсивным ос. И они спрашивают: «А не станет ли этот процесс гораздо проще, если в математике будет шрифт без наклона?» Дизайнер шрифтов Ян ван Кримпен однажды работал в нидерландском городе Харлеме с известным физиком; я не помню,
как его звали. Кажется, это был второй человек, получивший нобелевскую премию по физике. Он умер в двадцатых годах. В общем, они с ван Крим- пеном собирались создать в Нидерландах новый математический шрифт, при этом не предполагалось создавать математический курсив. Они хотели унифицировать греческие буквы и другие математические символы. Но работа остановилась из-за смерти физика, а ван Кримпен закончил только греческие буквы, которые стали пользоваться заслуженной популярностью.
Несколько других дизайнеров шрифтов посетили Станфорд. Увидев математический шрифт, они спросили: «А почему вы не используете шрифт без наклона?» Герман Цапф предложил Американскому математическому обществу создать новый математический шрифт, в который войдут готический шрифт, специальные символы, греческие, рукописные и обычные буквы. Ключевая идея состояла в том, чтобы не использовать наклонных символов, и таким образом, х мог несколько смещаться вниз и вверх. Тогда было бы проще расставлять символы и достигать равновесия. Герман создал серию разработок, и комитет, состоящий из большого числа математиков, изучал эти разработки, вносил свои замечания и занимался их настройкой?
Однако для математиков этот шрифт оказался слишком радикальным изменением. Я знаю математиков, которые в своих работах пишут х с двойным наклоном, чтобы он выглядел очень курсивным — только тогда они воспринимают его как математический символ. Так что после того как в течение 300 лет математика печаталась курсивом, многие люди не могут от этого отвыкнуть. Существует, наверное, всего два десятка книг, ну может быть, больше, около сотни, где вместо курсива используется шрифт AMS Euler, но большинство математиков считают, что он слишком отличается от курсива. С другой стороны, я сейчас вижу, что шрифт Euler Fraktur используется практически всеми.
В Брно я видел, что шрифт Euler Roman используется в замечательной книге — чешском переводе Дюреровского «Апокалипсиса»? Я также видел Euler Roman несколько дней назад в некоторых учебных записях. Однажды, когда я был в Норвегии, я заметил, что на каждом компьютере был наклеен ярлык с названием компьютера, набранным шрифтом AMS Euler, так как он нравился людям. Это красивый шрифт, но им набрано не так уж много математических книг.
Карел: Если вопросов больше нет, давайте поблагодарим профессора Кнута за эту встречу. [ Бурные продолжительные аплодисменты. ]
Дон: Спасибо всем за прекрасные вопросы.
Карел: [ Заключительное слово на чешском языке. ]
Вопросы и ответы, III
[6 января 1996 года Кес ван дер Лан оповестил NTG, голландскоговорящую Группу пользователей ТЦХ’а о том, что Дональд Кнут в марте будет в Голландии. Центр математики Mathematisch Centrum (МС, теперь он называется Centrum voor Wiskunde en Informatica, CWI) пригласил Кнута произнести речь на пятидесятилетии CWL Группа NTG посчитала это весьма удачной возможностью организовать специальное собрание для всех голландских пользователей ТЦХ’а и МЕТА FONT а, кто хотел бы встретиться с самим Великим гуру. К счастью, Кнут принял приглашение NTG, и встреча была организована 13 марта в «De Rode Hoed» в Амстердаме. В этом событии приняли участие около 35 человек со всей страны и даже из Бельгии. Все было записано на видео- и аудиопленку Герардом ван Несом. Кристина Тиле добровольно вызвалась сделать расшифровку, текст которой первоначально был опубликован в журнале NTG: MAPS (Minutes and APpendiceS) 16 (1996), 38-49.]
Эрик Фрамбах: Приветствую всех на этом особом собрании по случаю визита г-на Дональда Кнута в Голландию. NTG посчитала, что было бы замечательно воспользоваться предоставившимся шансом и попросить г-на Кнута ответить на вопросы о ТЁХ’е, METAFONT’e и многом другом, связанном с использованием Дц^’а. К счастью, он согласился. Итак, мы рады приветствовать здесь г-на Дональда Кнута — спасибо за ваш приезд.
Сегодня у нас есть время задать ему все те вопросы, которыми мы давно хотели его озадачить. [Смех.] Я уверен, что у каждого из вас имеется несметное множество вопросов, мнение Великого гуру о которых вы хотели бы узнать. Итак, давайте приступим к ним.
Дон Кнут: Я тоже начинаю задавать вопросы! [ Смех.]
В прошлую субботу я был в Праге и там было похожее собрание чешских и словацких пользователей Т^Х’а. Вам будет приятно узнать, что на той встрече я видел довольно много экземпляров 4ТЦХ CDROM’ob.
Это путешествие не первый мой визит в Амстердам: я был в Амстердаме в 1961 году. Так что прошло всего лишь 35 лет, и, возможно, следующего раза придется ждать меньше 35 лет. Я думаю, люди записывают на пленку эти вопросы и ответы, чтобы я оставался честным, потому что в Праге они делали то же самое. Так что если тот же вопрос будет задан снова, вам нужно будет взять среднее от двух ответов. [ Смех. ]
Вице Дол: А вы знаете, что это из-за Барбары Битон? Она посылает тебе письмо и говорит: «Записывайте все на пленку».
Дон: Да, в Праге они говорили то же самое! [ Смех. ] Я думаю, у нее отчаянная нехватка материалов для публикации или, может быть, у нее просто много вопросов. Но перед тем как вы начнете задавать вопросы, позвольте сказать, что один из самых интересных вопросов, заданных мне в Праге, прозвучал уже после собрания, и мне бы хотелось, чтобы он тоже был записан. Вот этот вопрос: как я встретился с Дуэйном Бибби, который иллюстрировал The ТвХЬоок и The METAFONTbook? Мне почему-то всегда хотелось, чтобы люди узнали об этом.
Вот как это было. У меня была идея после многолетнего издания математических книг сделать книгу с более причудливыми — ну, в любом случае, с другими— иллюстрациями. А тут я писал книгу про книги, а в книгах бывают иллюстрации, так почему бы и мне не обзавестись иллюстрациями? Поэтому я написал художнику по имени Эдвард Гори. Кто-нибудь знает ...
Франс Годдейн: Да. Amphigorey. Замечательно.
Дон: Да, Эдвард Гори. Amphigorey. Его рисунки отличаются болезненным воображением, но и прекрасным чувством юмора. Некоторые из его книг я
давал своим детям. Мне казалось, что он бы вполне подошел. Я написал ему два письма, но он так и не ответил. Потом я написал японскому художнику Анно, Мацумасе Анно, который на самом деле является логическим последователем Эшера. Анно делает то же, что делал Эшер, только в цвете; поэтому я спросил, не сможет ли он проиллюстрировать мою книгу. В ответ он прислал очень милое письмо со словами: «К сожалению, у меня недостаточно времени из-за других моих многочисленных занятий, но вот пять моих книг, полных рисунков, и если вы хотите использовать какие-нибудь из них, то не стесняйтесь». Очень мило, но я хотел, чтобы рисунки были сделаны специально для меня.
Потом я пошел на вечеринку в Станфорде, где встретил даму, пробовавшую свои силы в издательском бизнесе. Она только недавно познакомилась с блестящим молодым художником и начала с ним работать. Я пригласил его к себе домой, и мы провели вместе некоторое время —он замечательный человек. Дуэйн сейчас живет в северной Калифорнии, примерно в четырех часах езды от моего дома, так что я только один раз выбрался туда навестить его. Он иногда бывает в районе Сан-Франциско по делам. Сначала мы обсудили книгу, а потом он прислал мне ворох рисунков и самых разных своих эскизов. Изначально предполагалось, что TgX будет римским гражданином, и Дуэйн нарисовал этого человека в тоге с оливковыми ветвями на голове — вот почему сейчас оливковые ветви украшают льва. Но вдруг ни с того ни с сего он начал делать эскизы своего кота, что вроде бы вполне подошло, и вскорости он набросал все 37 рисунков, изобразив на них льва. Большинство из них в конце концов вошли в книгу, а. еще с полдюжины мы переделали. Когда я приехал к нему в гости, я встретил кота Т^Х’а, очень похожего на изображенного в книге. Вот и вся история про Дуэйна Бибби.
Эрик: Спасибо. Кто хотел бы задать первый вопрос? Пожалуйста, задавая вопрос, представляйтесь.
Пит ван Острюм: Меня зовут Пит ван Острюм. У вас есть замечательный лев в книге The T^Xbook, львица —в The METAFONTbook. А как насчет львят? Дон: А, я понимаю ... [смех.] Дуэйн все еще делает иллюстрации в особых случаях. Он сделал новые иллюстрации к японским переводам The T^Kbook и The METAFONTbook, где Т^Х и МЕТА одеты в японскую одежду. Так что теперь, если откуда-нибудь появится потомство, я полагаю, он с радостью поможет его нарисовать. Но возможно, это будет в какой-то степени незаконнорожденный ребенок, с моей точки зрения. [Смех.] Я имею в виду, что не взял бы на себя ответственность за все, что делают эти персонажи. [ Смех.] Пит: Так что вы думаете о потомках Т^Х’а и METAFONT’a?
Дон: Что ж, мне кажется, независимо от имеющейся у вас системы, будет способ ее улучшить. Если кто-то захочет потратить время на серьезную, ак-
куратную работу, то постольку, поскольку мы больше узнаем о типографском деле, обязательно появится нечто новое. Лично я надеюсь, что мне не придет
ся тратить время на изучение новой системы, потому что мне для моих нужд достаточно и старой. Но я, безусловно, никогда не намеревался сделать мою систему единственным инструментом, который когда-либо может понадобиться для книгопечатания. Я пытался сделать ее насколько возможно общей, но с
тем, чтобы размер программы нс превосходил некоторых разумных пределов, и с учетом того, что мы тогда знали о книгопечатании. Поэтому разные дру
гие проекты —я не считаю, что они мне угрожают или что-то в этом роде. Я надеюсь, что будет некоторая совместимость, так чтобы — я хочу сказать, мне бы -хотелось обрести бессмертие — так чтобы написанные мною книги все еще можно было напечатать через 50 лет, не залезая для этого в файлы и не редактируя содержимое. Мне особенно нравятся в ТЁХ’е архивность и независимость от платформы, я попытался разработать модель, минимальный стандарт ка
чества, которому могли бы следовать другие люди.
Ханс Хаген: Но если посмотреть в будущее, ... вы считаете, что сегодня программирование для многих людей —это искусство, что ж, многие произведения искусства только через сотни лет были признаны таковыми. Через сто лет компьютеры будут довольно сильно отличаться от нынешних, языки программирования изменятся, носители, на которые мы записываем все эти вещи, изменятся. Собственно программы и все, к ним
относящееся,— будет ли у них шанс на бессмертие, как кажется вам?
Дон: А вы назвали ваше имя? [ Смех. ] Ханс: Меня зовут Ханс Хаген.
Дон: Вы говорите: это довольно самонадеянно —предполагать, что наши сегодняшние творения проживут хоть сколько-нибудь долго. Технология меняется так быстро, что мы не имеем ни малейшего понятия о том, что люди выдумают завтра. Сто лет назад физики говорили, что в физике уже все сделано, осталось только получить еще один десятичный знак —пятый десятичный знак после запятой в фундаментальных константах, — и это станет завершением физики. Конечно, такие вещи невозможно предугадать. Но я действительно верю, что, как только мы переводим вещи в электронную форму и у нас имеются их зеркала, это дает достаточную долю бессмертия — в то время как бумага горит.
Вы слышали о проекте под названием «The Clock», разрабатываемом Стьюартом Брэндом и его коллегами? Он опубликовал каталог Whole Earth Catalog. У них группа людей размышляет о том, можно ли построить что- нибудь, что простоит тысячу лет ... Я не хочу углубляться в этот вопрос. Я действительно надеюсь, что стабильность Т^Х’а позволит впоследствии воспроизводить вещи, которые мы делаем сейчас. А так как сделать это довольно легко, я думаю, это и произойдет — разве что наступит ядерный холокост. Некоторые математики дискутируют о взглядах Платона ... существуют ли
все математические объекты, а мы просто открываем их, или мы на самом деле их создаем? В некотором смысле, когда что-то переводят в биты, это становится математическим объектом и поэтому будет существовать вечно, даже если человеческая раса вымрет —оно останется, и что из этого?
Эрик: Кто следующий?
Марк ван Леувен: Мне хотелось бы продолжить затронутую предыдущими вопросами тему. Стабильность самого ТЁХ’а, в моем представлении, может быть камнем преткновения для разработки новых вещей именно потому, что он столь стабилен и все уже им пользуются. Так что если появится что-то, что хоть чуточку лучше, то люди не станут этим пользоваться, потому что оно не везде доступно и нет никакого резона отказываться от старого.
Дон: Мне кажется, во Флориде я рассказывал, что люди все еще пытаются использовать старые шрифты, которые я стараюсь искоренить. Четыре
года назад я переделал греческую строчную дельта и сделал концы стрелок темнее. Я не менял ничего, могущего повлиять на работу Т^^’а: все размеры, высота и ширина всех символов остались неизменными. Я всего лишь подправил некоторые символы. И до сих пор многие математические журналы все еще пользуются старыми символами четырехлетней давности, и я получаю от людей письма и препринты со старыми дельтами. Я внес эти изменения, потому что больше смотреть не мог на старые варианты. [ Смех. ] Теперь у меня есть свой Web-сайт — если у меня появляются какие- нибудь исправления к ТЁХ’у или другие новости, я выкладываю их туда: http://www-cs-faculty.stanford.edu/~knuth. Этот адрес приведет на мой Web-сайт; там есть ссылка, озаглавленная «Важные замечания для всех пользователей ТвХ’а», а в ней сказано: «Посмотри на строчную дельта и, если у тебя не такая, тебя ожидает смерть!» [Смех.]
Я понимаю, что люди неохотно отказываются от вещей, к которым они успели привыкнуть. Я знаю про две основных надстройки над ТЁХ’ом: одна — это е-ТЁХ? а другая —A/jS. е-ТЁХ должен быть, очевидно, на 100% совместим с ТЁК’ом, так что если не пользоваться возможностями, не поддерживаемыми исходной системой, то новая будет работать точно так же, как старая. Это позволит осуществить постепенную замену. Конечно, потребуется дополнительное место на компьютере, но в наше время это не проблема. Люди, работающие над е/ЩХ’ом, всегда присылали мне очень точные комментарии про ТЁХ, когда находили ошибки в моих программах, так что, как мне кажется, они будут работать аккуратно. Таким образом, одна из этих вещей будет поставляться вместе с произвольной установкой UNIX’а или еще чего-нибудь, и там по умолчанию есть е-ТЁХ и по-прежнему имеется ТЁХ. Тогда можно также использовать некоторые другие возможности, которые могут оказаться очень полезными для конкретных нужд.
Йоханнес Брамс: Вы упомянули е-ТЁХ и M^S. X знаете ли вы также о проекте Омега?
Дон: Конечно, проект П! Да, я сам собираюсь использовать его для имен и фамилий авторов в The Art of Computer Programming. Я собираю китайских, японских, индийских, еврейских, греческих, русских, арабских авторов и хочу напечатать их имена правильно, [смех] а нс просто в виде транслитерации. У меня есть некое рудиментарное оборудование, которое можно использовать для черновых распечаток, для поддержания моей базы данных и для написания людям писем с вопросом: «Это ваше имя?» Система П, я надеюсь, будет укомплектована хорошими шрифтами, которые позволят мне делать все это без особых затрат. Сейчас, чтобы получить арабские имена, мне приходится использовать АгаЬТцХ; чтобы получить еврейские имена ... Две недели назад я попал в ужасное положение, пытаясь найти шрифты для иврита в CTAN — если вы хотите, я могу рассказать всю историю ... Я тыкался в разные места, а там были ссылки на несуществующие файлы и README-файлы, противоречивые и устаревшие на четыре года; мне так и нс удалось найти ни одного шрифта для иврита. Может быть он есть у вас на CD ...
Йоханнес: Я заведомо могу указать кое-кого, кто может помочь со шрифтами для иврита: я знаю человека в Израиле, который пытается осуществить поддержку иврита в рамках системы Babel. И в Израиле тоже печатают книги, используя ТУС.
Дон: Мой израильский друг — книгоиздатель Ден Берри, но он, к сожалению, сильно привязан к troff’y. [ Смех.] Я уверен, что могу получить хороший иврит через Янниса [Хараламбуса] и П. Я действительно надеюсь, что Unicode появится скорее раньше, чем позже; он намного лучше всех альтернатив, во многом по причинам, упомянутым Марком [ван Лсувеном].
Я нс увидел большого энтузиазма в отношении Unicode в Японии, потому что у них есть работающая система, которая им прекрасно подходит, так зачем что-то менять. Каждый раз, когда я спрашивал японцев об их именах в Unicode, они говорили: «Что такое Unicode? Вот мое имя в JIS». Но символы JIS включают не все китайские коды, и, на самом деле, мое собственное имя — у меня есть китайское имя —нельзя написать при помощи JIS, не изменив его немного. Имеются два разных символа Unicode: один для японской версии и один —для китайской.
Кто-то позади? Кес?
Кес ван дер Лан: У меня, конечно, много вопросов. Но начать мне хотелось бы с некоторых вопросов про METAFONT. Первый из них: как получилось, что написания макро в ТуС’е и METAFONT’e настолько различаются?
Дон: Почему макро в ТуС’е и METAFONT’e так различаются? Я нс решился сделать Ту< столь экстремальным, как METAFONT. Эти языки имеют совершенно различную структуру. METAFONT в некоторых отношениях невероятный язык программирования — он базируется на объектно-ориентированных макро. Макро могут встретиться посередине структуры данных.
Довольно просто объяснить, как я создавал эти языки. Возьмем ТУС Однажды ночью я записал то, что, по моему мнению, было бы хорошим исходником для The Art of Computer Programming. Я взглянул на второй том, который
должен был напечатать. Начал я с первой страницы, и, когда встречал любой фрагмент, достаточно похожий на то, что я уже делал, то пропускал его. В результате я получил примеры всех видов правил набора текста, которые присутствуют во втором томе. Всего получилось пять напечатанных страниц; вы даже можете увидеть эти страницы — в точности так выглядела моя первоначальная тестовая программа—в нашей с Дейвидом Фуксом статье, где мы обсуждаем оптимальное кэширование для шрифтов^ Там мы привели пример и показываем эти пять страниц, иллюстрирующих то, чему я хотел научить Т^К. Я записал то, что, как мне казалось, мне бы хотелось набрать —как должен выглядеть мой электронный файл. И потом я сказал: «О’кей, это у меня на входе, а вот что будет на выходе — как осуществить переход? А для этого, что ж, для этого, кажется, мне нужны макро». [ Смех.]
То же самое и с METAFONT’om. Я просмотрел мои первые наброски всех тех шрифтов, которые потом стали семейством Computer Modern. На самом деле я писал на SAIL’e, языке-компиляторе типа ALGOL’a; но в SAIL’e предусматривается возможность написания макро, так что я разработал несколько примитивных макро, с помощью которых можно было сказать: «взять широкое перо», «соединить точку 1 с точкой 2» и т. п. Компилятор SAIL’а переводил эти макро в машинный код, который затем рисовал буквы. Я прошелся по всему алфавиту, и к концу года у меня было около 300 небольших программок, каждая из которых рисовала одну букву. Затем я понял, какого типа языком мне хотелось бы пользоваться для описания букв. Так что однажды, во время туристической поездки с семьей — яс женой и детьми был в Гранд-Каньоне — я на час отвлекся от всего, сел под дерево и написал программу для буквы А на языке, который, как мне казалось, будет хорошим алгебраическим языком, на высоком уровне отражающим то, что я делал при помощи довольно примитивных низкоуровневых инструкций в моих программах на SAIL’e. Я сделал также букву В; прописные А и В. И потом я возвратился к туристической поездке. Эти листки с моими первыми программами сейчас находятся в архивах Станфорда — программа для буквы В была напечатана в издании Станфордской
библиотеки в прошлом году. Библиотекарь, занимающаяся в Станфорде редкими книгами и собраниями рукописей, серьезно интересуется METAFONT’om, поэтому она написала небольшую статью о том, что у них есть*
Та программа снова предполагала, что я хочу использовать какие-то макро. Но они должны были иметь гораздо более четкую структуру, чем макро ТУХ’а. Когда я говорил zl ’, это на самом деле должно было быть эквивалентно (xl ’,у1’); и я хотел иметь возможность писать zl ’ без всяких ограничителей. Оказалось, что, если я хотел получить язык высокого уровня, который был бы для меня естественным при написании программ, он должен был быть совершенно отличен от ТЁХ’а. Поэтому у ТЁХ’а и METAFONT’a одинаковый формат сообщений об ошибках и некоторые общие структуры данных внутри, но в остальном это совершенно разные системы. В хорошем языке высокого уровня для шрифтов я не хотел тратить время на написание круглых и квадратных скобок, запятых и других ограничителей.
Кес: Это хорошее вступление к моему второму вопросу: [смех] относительно будущего системы МЕТА РОЕТ, которая позволяет делать рисунки с конечным результатом в виде инкапсулированного PostScript’а: что вы думаете о данных с большим числом измерений для МЕТАРОЕТ’а и METAFONT’a? Например, о добавлении тройки по аналогии со структурой данных pair?
Дон: В МЕТАРОЕТ’е уже есть структура данных для троек из-за color. Так что выражения RGB на самом деле тройки чисел.
Кес: Да, но тройка для хранения точки в пространстве?
Дон: А, я понял. Я действительно писал METAFONT таким образом, чтобы оставались специальные средства (hooks) и его можно было легко расширить; например, вы можете захотеть рисовать трехмерные картинки с использованием аксонометрии и проективной геометрии, вместо аффинной геометрии. Сама программа для METAFONT’a была написана так, чтобы люди, желающие иметь систему, идущую дальше основ, могли легко ее изменить. Я всегда хотел, чтобы системы, которые я делаю для всеобщего употребления, были способны справиться с 99% всех задач, известных мне. Но я всегда чувствовал, что должны быть особые задачи, для выполнения которых будет легче поменять программу, чем написать макро.
Я пытался писать программы так, чтобы у них была логическая структура и было легко добавлять в них новые возможности. Это происходит не так часто, как я думал, потому что люди оказались более заинтересованы, видимо, в переносимости того, что они делают; если у тебя своя собственная программа, то у других ее уже нет. Все же, если бы я был крупным издателем и мне нужно было осуществить особый проект: что-нибудь вроде энциклопедии или нового издания Библии и т. п., я бы непременно посчитал, что правильней всего будет нанять хорошего программиста и сделать специальную компьютерную систему именно под этот проект. По крайней мере, таково мое видение того, как это стоит делать. Кажется, это случается нечасто, хотя в Брно я встретил студента, который довольно далеко продвинулся, пытаясь получать формат Acrobat’a
Robin Е. Rider, «Back to the future: High-tech history», Imprint 14,1 (Fall 1994), 9-18.
прямо из ТЁ^’а, путем прямого изменения кода. А система Q, которую упомянул Йоханнес, это 150 000 строк измененных файлов. [ Смех. ] Я встроил спецсредства так, что каждый раз, когда Т^Х выдает страницу, он может наткнуться на whatsit, a whatsit в разных версиях ТЁХ’а может выражать самые разные вещи. Поэтому, когда основная программа видит whatsit, она вызывает специальную подпрограмму, спрашивая: «Как мне печатать этот whatsit?» Эта специальная подпрограмма смотрит на подтип (subtype), а подтип может оказаться другим подтипом, вставленным для демонстрации, или совершенно новым подтипом.
Похожие спецсредтва имеются в программе МЕТА FONT. Если у людей есть лишнее время, когда они не бродят в Интернете [смех], я рекомендую замечательное развлечение — читать программу METAFONT’a. Некоторые ее части перевариваются с трудом, и я надеюсь, никто и никогда не найдет там ошибку, потому что я не переживу, если мне придется туда еще раз заглядывать. [Смех.] Но это — подпрограммы растеризации, то, что собственно заполняет пиксели. В этой программе много других вещей: блок для решения линейных уравнений и механизмы для структур данных ... там много чудесных алгоритмов: найти квадратный корень в фиксированной точке, найти пересечение двух кривых и т. д. МЕТА FONT полон маленьких программок, которые писались с огромным удовольствием и которые кажутся мне полезными и интересными сами по себе. Я думаю, когда Джон Хобби писал METAPOST, он тоже получил удовольствие от того, что мог добавить свои собственные замечательные программки к тем, что уже имелись.
Я —большой поклонник МЕТАРОЕТ’а для технических иллюстраций. Я не знаю ни одной другой системы, которая хотя бы приближалась к нему, поэтому все рисунки к The Art of Computer Programming я делаю в METAPOST’e. Также технические статьи, написанные мною, Cambridge University Press собирается издать в серии из восьми томов, и все рисунки, кроме фотографий будут переведены в METAPOST. Первым из этих восьми томов была книга Literate Programming; второй том должен выйти этим летом и должен называться Selected Papers in Computer Science. В нем перепечатаны 12 или 15 статей, которые я написал для широкой публики, для неспециалистов в информатике и программировании: статьи в журналах Scientific American или Science и т. п. Третий том будет о компьютерной типографии, и в нем будут перепечатаны все мои статьи в TUGboat и вещи, касающиеся Т^Х’а. Как, кстати, вы думаете, стоит мне публиковать в этом третьем томе памятку, которую я себе написал в первую ночь, когда создавал Т^Х? Я записал ее в компьютерный файл, и она хранится в архивах Станфорда, но я никогда и никому ее не показывал. [Крики «конечно!», «обязательно» и смех аудитории.] Может быть, это увеличит продажи. [ Снова смех.]
Франс: Вам нужно положить ее на ваш Web-сайт, и тогда мы сможем решить —
Дон: Нет, нет. Так мы никогда не продадим книги. [Смех.] Не то, чтобы я был очень корыстолюбив, конечно. Она находится в файле под названием
tex.one—«текс точка уан», на самом деле. Должен признаться, что с месяц или два я произносил название как «текс»: я имел в виду «технические тексты». Файл назывался tex.one, и когда-нибудь его, возможно, будет интересно почитать.
А ваше имя?
Ян Карман: В этой компании я, может быть, задам весьма еретический вопрос, но немного ереси добавит интереса — разговор идет о METAFONT’e. Сейчас, вероятно, многие словолитни производят много высококачественных шрифтов и таблиц кернинга. Неясно, выживет ли PostScript или True Туре. Думаете ли вы, что METAFONT для текстовых шрифтов выживет? Не касаясь математических шрифтов.
Дон: Я не думаю, что дополнительные возможности METAFONT’a оказались необходимыми для создания шрифтов хорошего качества, хотя я все же думаю, что с его помощью можно делать шрифты лучшего качества. Специалисту по шрифтам сложно думать так, как думает компьютерщик, в том смысле, что, когда люди, занимающиеся компьютерами, что-то автоматизируют, пытаясь заставить компьютер сделать то или иное, для нас естественно ввести параметры и сказать, что мы хотим решать не одну задачу, а сразу несколько. Мы пытаемся решить целый спектр задач, зависящих от параметров, задаваемых людьми. Но было бы гораздо проще, если бы люди требовали от нас решения только одной задачи с одним набором значений параметров, тогда мы смогли бы заставить компьютер сделать в точности то, что требуется. Компьютерщики давно привыкли размышлять о том, как бы изменилось поведение при изменении параметров, но для шрифтовиков это довольно непривычно. Они чувствуют себя гораздо лучше, если сначала босс говорит: «Сделай мне прямой шрифт средней насыщенности», а через месяц: «Сделай мне полужирный прямой шрифт». Намного сложнее, если босс говорит: «Покажи мне, как бы ты рисовал прямой шрифт, независимо от того, какой насыщенности я хочу сделать буквы». METAFONT позволяет решить эту проблему и рисовать символы, задаваемые параметрически, но далеко не каждый специалист по шрифтам может свободно оперировать такими понятиями. Они могут сделать шаблоны, начертив несколько рисунков, а затем отождествляя точки на разных рисунках и заставляя компьютер интерполировать. Шаблоны в PostScript’е позволяют ввести до четырех параметров, а у большинства из них —только один или два параметра. У большинства из известных мне — два; возможно, у других число параметров достигает даже четырех. Но тогда им приходится делать рисунки для всех граничных значений этих параметров.
Несмотря на такое ограниченное использование параметров, то, что сейчас продается, обеспечивает вполне приличную удобочитаемость текста, хотя все-таки не дает того качества, которое у вас в Нидерландах было в XVII веке. Как имя того человека, великого пуансониста из Харлема — он сделал шрифты кеглей 4.5, 5 и дальше до 16, и для каждого размера каждая буква делалась отдельно, и его шрифты отличались замечательной общностью начертания. С современными шрифтами Туре! ситуация совсем другая. Были два парня, ну-
ансонисты, которые делали большую часть работы для Энсхеде и остальных в XVIII веке: один из них, Флайшман, был гением по части очень красивых букв; другой, Розар, просто хорошо делал очень много разных букв* [Смех.] Они были замечательны. Розар делал самые разные каллиграфические алфавиты и т. п. У меня есть такая большая книжка с образцами всех шрифтов Энсхеде, переведенная на английский язык отцом Мэтью Картера. Посмотрев на все шрифты в этой книге, Typefoundries in the Netherlands, вам остается лишь обливаться горючими слезами?
Тем не менее, на лазерных принтерах мы сейчас получаем достаточно хорошие шрифты, и поэтому, наверное, не так много специалистов по шрифтам будут пользоваться METAFONT’om. Pandora была хорошим метапроектом настоящего художника? Оказалось, что METAFONT замечательно подходит для создания контурных рисунков и специальных вещей, связанных с геометрией. Сейчас в Польше есть очень изящная система, в которой Т^Х и METAFONT связаны воедино: ТрХ выдает что-либо, и затем METAFONT рисует символ, и, если он не подходит, Т^Х говорит: «возвратись назад и попробуй еще раз». Яцковский и Рыцько понимают Т^Х и METAFONT, а программы хорошо документированы и могут это делать. Таким образом, METAFONT не исчезнет; но его и никогда не будут изучать в средней школе.
Франс: Меня зовут Франс Годдейн, и у меня есть один вопрос с некоторыми подвопросами. [ Смех. ] Сначала я бы хотел задать подвопросы. Меня интересует —и об этом, возможно, уже много раз спрашивали раньше, — считаете ли вы, оглядываясь назад, ваше творение — [это] произведение искусства или инструмент? А причина такого вопроса — когда я слышу, как вы с такой страстью говорите о шрифтах и прекрасных алгоритмах, примененных вами в METAFONT’e, на которые вы
хотели бы указать людям, и одобрение со стороны тех людей, которые это понимают,—но есть подавляющее большинство пользователей, которые просто скачали TgX с какого-то сервера, так и не узнали, кто его создал, и используют его для набора не всегда очень хороших текстов. [Смех.] Они делают это достаточно грубо, и ничуть этим не озабочены. Вы заморозили Т^Х на определенном уровне, позволив другим людям делать над ним надстройки. Меня интересовало, что при этом должен испытывать отец — отец ли вы произведения искусства, которое другие люди используют как инструмент, или это ребенок, замороженный вами в развитии, который никогда не вырастет ... вопросов так много ... если бы вы просто вернулись к идее искусство или инструмент, и вашим мыслям на этот счет.
1 Иоганн Михель Флайшман, 1701-1768; Жак-Франсуа Розар, 1714-1777.
Typefoundries in the Netherlands from the Fifteenth to the Nineteenth Centuries, by Charles Enschede, translated by Harry Carter (Haarlem: Stichting Museum Enschede, 1978), 477 pp. Эта замечательная книга была набрана вручную и напечатана высокой печатью, в честь 275- летия Йоханнеса Энсхеде эн Зонена.
Neenie Billawala, Metamarks: Preliminary Studies for a Pandora’s Box of Shapes, Stanford Computer Science report STAN-CS-89-1256 (Stanford, California: July 1989), 132 pp.
Дон: Конечно, если я пишу что-либо достаточно мощное, чтобы делать много разных вещей, то можно будет заставить его делать и отвратительные вещи. Я только недавно был в Рейксмюсеуме, там есть выставка под названием «Эпоха уродства», представляющая целый набор фантастических серебряных сосудов конца XIX столетия... Когда вы говорите «искусство», я не уверен, что правильно понимаю, что вы имеете в виду. Для меня «искусство» употребляется
в двух совершенно различных смыслах: сегодня чаще всего в смысле высокого искусства, в то время как когда-то искусство (изначально K-unst) означало все, не являющееся натуральным — поэтому у нас есть слово искусственний^ сделанный людьми, а не природой. Греческое слово — те/^- [Смех.] Но затем вы описываете инструмент, как нечто, что, возможно, просто приспособление для быстрейшего получения нужного из имеющегося, но, может быть, при этом вы не заботитесь об изяществе... Мне кажется, говоря об искусстве, люди имеют в виду эстетику — нечто, связанное с красотой и несущее в себе частицу любви. Касательно Т^Х’а, я хотел дать возможность людям делать работу, которой
можно гордиться; я полагал, что люди могут получить настоящее удовольствие, затрачивая немного больше времени на то, чтобы улучшить результат. Я неможно гордиться; я полагал, что люди могут получить настоящее удовольствие, затрачивая немного больше времени на то, чтобы улучшить результат. Я неожидал, что этим будет заниматься весь мир.[ Смех. ]
можно гордиться; я полагал, что люди могут получить настоящее удовольствие, затрачивая немного больше времени на то, чтобы улучшить результат. Я не
можно гордиться; я полагал, что люди могут получить настоящее удовольствие, затрачивая немного больше времени на то, чтобы улучшить результат. Я не
Между прочим, я не могу понять, о чем думают люди, рисующие граффити на красивом здании, хотя могу понять, почему им нравится рисовать. Зачем что-то выцарапывать — по-моему, за это может быть ответствен какой-то животный инстинкт — пометить территорию, но для меня совершенно невозможно постичь эти действия.
Когда дело касается эстетики, нельзя навязывать свой вкус. Нельзя сказать, что твое понимание красоты будет соответствовать пониманию красоты у всех остальных. Но я действительно хотел иметь инструмент, помогающий нам достигать высших ступеней красоты согласно нашим собственным вкусам. Я сделал изменение межбуквенных пробелов не очень простым, но все остальное я постарался сделать доступным для всех. [ Смех. ] .... Конечно, изначально я делал TgX только для себя, для The Art of Computer Programming; я думал, что мой секретарь и я будем единственными пользователями. И далеко не сразу меня убедили, что мне нужно сделать Т^Х более общим и т. д. Но мне нужен был такой инструмент, с помощью которого я мог бы делать книги, вызывающие у меня чувство удовлетворения после того, как почти всю жизнь я эти книги писал.
Я начал писать The Art of Computer Programming в 24 года и мне еще предстоит лет 20 работы. Это довольно много времени. Я не хочу писать книги, которые будут отвратительно выглядеть. Я хотел найти способ научиться делать высококачественные в полиграфическом отношении книги. Вначале, когда компьютеры только появились, они понимали только числа, цифры. Компьютеры XIX века могли печатать таблицы. Потом появились компьютеры, которые могли работать с числами и буквами, но только на телетайпе; так что были некоторые прописные буквы и всего было 32 символа. Но затем, когда я закончил колледж, у нас появились ... дайте подумать, это было, наверное, через десять лет после колледжа, когда мы смогли работать со строчными буквами на компьютере. Вы знаете, язык Pascal, когда он появился, использовал только прописные буквы: никто никогда не рассчитывал, что в репертуаре компьютера будет более 64 символов. Наконец в середине 70-х годов мы начали понимать, что компьютеры действительно могут работать и со строчными буквами, и делать что-то, что можно было как-то читать, что-то, похожее на книги. Здорово! [ Смех.]
Одновременно происходило развитие типографских программ, начавшееся в M.I.T. в 1961 году и прошедшее через четыре или пять поколений, приведших к системам troff и eqn, с помощью которых можно было печатать даже математику. В результате в 1977 году я знал теорему существования: печатать что-то, выглядящее почти как хорошая математика, возможно. Полученные при помощи системы eqn тексты появлялись в физических журналах, и опыт показывал, что секретари могли научиться их набирать. Поэтому я подумал: «Почему бы не пройти весь путь до конца, до предела?» С Т^Сом я хотел не улучшить немного troff и другие системы; я говорил себе: «Попробуем добиться лучшего типографского качества за всю историю человечества». Не считая золоченного тиснения с цветными рисунками, я хотел —ио крайней мере что касается черно-белой печати —я хотел достигнуть наилучшего возможного качества. Компьютерная типография прошла такой долгий путь развития, понемногу становясь все лучше и лучше. Пришло время сказать: «О’кей, давайте перейдем к пределу». Конечно, я не думал, что этим захотят заниматься все. Но было достаточно людей, которые хотели бы попытаться достичь наилучшего возможного качества, которые могли бы — что ж, поэтому я в конце концов и сделал Т^Х более доступным. Главы Американского матобщества были первыми, почти первыми, убедившими меня научить систему делать больше, чем я планировал вначале.
Ленстра: Почему же вы не начали с troff ’а? Он совсем не подходил?
Дон: Да-да. Вы понимаете, troff был «заплатой» на вершине ... Я имею в виду, это была целая система, это было пятое поколение, каждое из которых было «заплатой» на предыдущем. Так что пришло время сдать все на слом и начать все заново: «Вот каким должен быть язык, так что давайте придумаем для него подходящие структуры данных». А не «Давайте попробуем добиться совместимости». У меня было преимущество — я работал не в Bell Labs, так что я не задевал ничьих чувств, если бы сказал: «Давайте все это выбросим».
[ Смех. ] Для людей в Bell Labs сделать такое было, естественно, невозможно—это было бы некрасиво. Но мне пришло в голову, что теперь у нас есть доказательство достижимости более высоких целей, так что пришло время начать заново и перепридумать, как получить конечный результат из исходного файла. Таким образом программа могла быть гораздо более единообразной, более компактной, а также могла бы работать. Я имею в виду, что troff все время зависал. Многие самые первые пользователи TgX’a были разочарованы troj^’oM, падающим от раза к разу, потому что он стал неуклюжим. Но troff также доказал, что есть свет в конце туннеля.
Знаете ли вы, что мне также пришлось выбросить на слом ТЁХ и начать заново; после пяти лет я решил, что лучше всего вернуться назад и переделать программу. Но мне было бы очень сложно сделать это, если бы ее написал мой друг из соседнего отдела. [ Смех. ] Итак, я просто придерживался той философии, что всегда найдутся люди, более заинтересованные в качестве, чем остальные, и я хотел сделать ТЁХ удобным для них.
Я не знаю ни одного хорошего способа, как сделать невозможным создание плохого документа, если только в вашей системе набор опций существенно не сужен. Маленького набора опций, конечно, вполне достаточно для большого класса пользователей — сделать систему такой простой, чтобы в ней невозможно было сделать ничего уродливого.
Эрик: Я думаю, пришло время для перерыва на кофе — он займет пять- десять минут.
Дон: Йоханнес, у вас был вопрос, который вы должны были задать, так что давайте покончим с этим. [ Смех. ]
Йоханнес: Он про набор текста. Что вы думаете о применении при наборе модели плавающего горизонта? В ТЁХ’е говорят о боксах: каждая буква находится внутри бокса, и мы склеиваем боксы вместе, чтобы получить строку, и каждая строка рассматривается как бокс, и боксы прилаживают друг к другу, формируя абзац. В модели плавающего горизонта пытаются пойти немного дальше строгого следования боксам и строкам, и пытаются учитывать, что некоторые нижние выносные элементы верхней и выступающие части нижней строк не перекрываются, так что на самом деле можно размещать строки ближе друг к другу — особенные преимущества это может дать при наборе математических формул.
Дон: Хммм, насколько я понимаю, вы говорите об общих принципах компьютерной графики, когда на картинке присутствуют произвольные прямоугольники, а не только прямоугольники, правильно расположенные внутри других прямоугольников. ... Это, безусловно, привело бы к серьезным изменениям во всех структурах данных ТЁХ’а. Можно обратиться к квадратным деревьям или чему-то в этом роде. Все, что применяется для решения проблемы невидимых линий и вывода изображений, для определения, что находится впереди, и все алгоритмы, которые используются в производстве мультфильмов вро-
де Toy Story. В моем представлении, это будет очень полезно при обработке необычных случаев в математических формулах.
У меня имеется два соображения на этот счет. Первое состоит в том, что мне нравится наблюдать, как люди расширяют круг проблем, решаемых компьютерами автоматически. Люди многому учатся, когда пытаются это сделать. Вся область искусственного интеллекта была одним из разделов, оказавших огромное влияние на компьютерные науки, потому что они пытались решать очень трудные проблемы. Особенно в первое время у них возникали методы, оказавшиеся полезными во многих других разделах компьютерных наук. Так что по-моему, когда люди ставят себе более претенциозные задачи, они разрабатывают мощные методы, у которых часто находятся самые неожиданные и весьма полезные применения. Даже в том случае, когда задача уже решена, они будут думать о чем-то другом, о еще одном усовершенствовании и т. д. — у них никогда не возникнет ситуации, в которой они будут создавать самый лучший документ автоматически. Придет время, когда вы посмотрите на получившийся результат и увидите, что его все еще можно улучшить. Создателям большинства автоматических систем можно дать хороший совет: оставить пользователям по крайней мере возможность двигать что-нибудь вверх и вниз и обжуливать автоматический алгоритм.
Философия, которой я придерживался, создавая Т^Х, заключалась в том, чтобы попытаться сделать систему, которая на 99% все делает автоматически; затем я бы посмотрел на то, что получилось, и подчистил остаток. Но «подчистить» —только один способ описания этого подхода; это можно выразить по-другому: «привести все в порядок» или «расставить все точки над i». Я чувствую, что неавтоматическая часть дает мне лишний повод гордиться, потому что я знаю, что я придал продукту окончательную форму. Если этой дополнительной работы слишком много, это неудобно, и я теряю время. Но если мне действительно удается ограничить ее 1 % —если я потратил 30 часов на написание статьи, а на ее подчистку у меня уйдет всего лишь 15 лишних минут,— тогда я с радостью потрачу в конце еще 15 минут. Этот небольшой добавок дает мне возможность отметить тот факт, что я закончил статью.
Пробелы, которые Т^Х сейчас делает хуже всего —это, на мой взгляд, слишком маленькие пробелы около знаков квадратного корня: или операнд слишком близок к знаку радикала, или слишком близок к горизонтальной черте, или и то и другое; чаще всего мне хочется поиграть с этим. В книге Concrete Math, а также теперь в The Art of Computer Programming, я принял правило, согласно которому я вставляю знак @ в математическую формулу там, где хочу сделать дополнительный пробел в одну математическую единицу. Знак © в таком случае определяется как имеющий математический код, равный в шестнадцатеричной системе 8000, и я ввел определение
{\catcode'\@=\active \gdef@{\mkernlmu}};
в результате, в математическом режиме © считается макро, добавляющим пробел в одну математическую единицу. Например, я набираю
\sqrt{@\log л}
потому что иначе пробел перед буквой ‘Г немного маловат. [ Смех.] Теперь, может быть, эта модель плавающего горизонта не будет знать, что эта ‘Г слишком близко, а может быть, будет. Но в случаях вроде этого ...
На самом деле, самый распространенный случай, где требуется подправить пробелы, — это места вроде ^х2^3\ с простым индексом сверху, за которым следует косая черта, а затем знаменатель. Почти всегда перед косой чертой слишком большой пробел. И, как я обнаружил, то же относится ко всем книгам, которые я привык считать идеально набранными вручную [смех], но теперь я чувствителен к таким вещам. В последнее время я просматриваю мои книги и статьи, как правило при помощи Emacs’a, и ищу все места, где встречается односимвольная степень с последующей косой чертой, и большинство этих мест лучше выглядят с отрицательным тонким пробелом перед косой чертой: $хЛ{2\! /3}$ дает х2^3. Было бы приятней, если бы мне нс приходилось этого делать. Но тем не менее, для меня это мелочь.
Сможет ли мне в этом помочь модель с плавающим горизонтом? Иногда я попадаю в ситуации, когда я добавляю лишнее слово в ответ к упражнению, чтобы избежать наползания строк друг на друга. На самом деле, строки не расходятся слишком далеко, а расположены так близко друг к другу, что нижний предел вроде ‘к <п’ наползает на скобку в следующей строке. И мне совсем не хочется, чтобы там печать была очень плотной. Теперь, если бы я был умнее, я бы сделал мой знак < с диагональной чертой под знаком <, вместо горизонтальной, а именно ;к ^ п\ и я бы избавился от этих наползаний — сейчас для этого слишком поздно. [ Смех. ]
Кес?
Кес ван дер Лан: Позвольте вас спросить о вашем отношении к разметке вообще? Мне бы хотелось проиллюстрировать этот вопрос, рассказав сначала следующую историю. Когда мы начали использовать Т^Х и т. д., на самом деле имеется в виду, мы начали использовать lATgX — я хочу сказать, такова ситуация в Голландии. И тогда я посмотрел, что получается в результате разметки, и мне это не понравилось. И тогда я задумался, как вы к этому относитесь. С сожалением признаю,
что я пролистал исходный файл texbook.tex книги The T^Xbook и взглянул на то, что есть там, и тогда подумал: «Ну, я немного понимаю, что вы думаете про разметку». А когда вы объясняли про METAFONT и те вещи, которых там нет, которые вы сделали неявными, — ошибусь ли я, если суммируя, скажу, что вы придерживаетесь чего-то вроде минимальной разметки?
Дон: Да. Например, если я читаю книгу Эдсгера Дейкстры, каждый раз, когда я дохожу до раздела, где сказано «Конец замечания», это бросается мне в глаза своей избыточностью. И я всегда думаю: «А, да, это в стиле Эдсгера». Когда я писал статью к его 60-летию, я сказал в конце: «Благодарности, я хочу поблагодарить Эдсгера за то-то и то-то» и «Конец благодарностей». [Смех.] Но это был единственный раз в моей жизни, больше я так никогда делать не
буду. Может быть, я не логичен; но очевидно половина людей, пользующихся сейчас HTML, набирают только <р> в начале абзаца, а другая половина — только </р> в конце абзаца. [ Смех. ] Как донесла разведка, редко кто-нибудь использует и то и другое. И я не знаю, способны ли эти системы вообще хоть что-то делать с не заключенным в скобки материалом.
Когда я пишу на HTML, я делаю разметку скрупулезно. Загляните на мой Web-сайт —я заплачу вам $2.56, если вы хоть где-нибудь обнаружите, что я открыл что-то и не закрыл его меткой справа. Я старался быть к этому очень внимательным, расставлять везде отступы и т. п. Но для меня это оказалось весьма неприятно, потому что думаю я по-другому.
Язык высокого уровня, по-моему, должен каким-то образом визуально отражать свою структуру, но не обязательно явно; так что, если я знаком с правилами, можно кое-что опускать. Скобки —одно из таких правил, и математика далеко продвинулась, когда были изобретены другие способы записи вроде предшествующих операторов, которые позволяют нам видеть структуру, не выписывая ее чересчур подробно. Математик много времени проводит, выбирая тот или иной способ записи, и одна из вещей, которых мы в математике стараемся избегать, это двойные нижние индексы. Я читал одну французскую кандидатскую диссертацию, в которой автор дошел до пятого уровня нижних индексов [ смех. ] — он последовательно загонял себя в угол. Начал он с множества {#i,..., j;n}, поэтому, когда ему понадобилось подмножество, оно должно было выглядеть как {j;^ ,..., xtm }, а потом он захотел взять его подмножество; в конце концов он дошел до теоремы, использующей kXi5 . • • хц V [ Смех.] Я стараюсь выбирать способы записи, позволяющие мне экономно мыслить на высоком уровне.
Возможно поэтому я не придавал большого значения обилию разметки в The ТуХЬоок. Я бы начинал раздел, имитирующий пишущую машинку, и заканчивал его, говоря \begintt и \endtt. Я бы также отделял строки, представляющие части макрокоманд plain ТЁХ’а, говоря \beginlines и \endlines —эти макро присутствуют в файле, потому что мне очень важно видеть структуру. Но в остальных случаях я оставил вещи сколь возможно простыми, настолько, чтобы я мог отчетливо представлять себе, где здесь начало и где конец.
Похожая вещь получалась, когда я выполнял административную работу в Станфорде. Иногда, решив проблему, я переставал о ней беспокоиться, так что забывал воплотить решение в жизнь! Я всегда был очень плохим председателем комитета потому, я полагаю, что никогда не умел заканчивать эту последнюю строку. Тем не менее, что касается HTML, документы были небольшими и я решил, что мой Web-сайт будет просматриваться самыми разными типами браузеров, так что лучше соблюдать строгость.
Когда я разрабатывал ТЁК, я посетил одно из заседаний комитета, создавшего SGML, и у меня была очень интересная дискуссия с Чарли Голдфарбом и другими членами комитета — только это заседание было рядом со Станфордом. Конечно, я принимаю во внимание то, что благодаря строгой разметке становится возможным надстраивать другие программы вокруг той, что есть у тебя. Чем больше документ структурирован, тем легче сделать базу данных,
содержащую сведения о нем и отслеживающую изменения. Я никогда не возражал против SGML; я просто всегда чувствовал, что для максимизации моей эффективности я не хотел возиться с полной разметкой, если без этого можно обойтись.
X: SGML позволяет минимизацию; вот почему концы абзаца не являются необходимыми. Так что это —одна из причин, почему он иногда так труден. Нужно минимизировать формализацию.
Дон: Но lATgX этого не позволяет.
Йоханнес; У нас, тем не менее, имеются некие спецприемы, разрешающие опускать закрывающие метки, в ГАТ^ХЗ, но это еще далеко до завершения.
Дон: Ну что ж, Кесу стоит поговорить с Йоханнесом. [ Смех. ]
Так случилось, что я не ощущаю нужды в специальном редакторе для написания исходников HTML —люди рекламируют чудные вещи, когда можно нажать на инструмент (tool), и будут вставлены начальная и конечная метки вместе. Но когда я писал свои файлы, я в Emacs’e написал небольшой макро, работающий по нажатию клавиши, который берет любую только что набранную мной метку и создает конечную метку. Все, что этот макро должен был сделать, это найти предыдущий знак меньше, и затем сделать вторую копию той строки, и добавить перед ней слэш; так что я все время этим пользовался — это было нетрудно.
Йоханнес: Теперь вопрос совершенно из другой области, от человека, который хотел бы быть здесь, он пишет, цитирую: «Почему высота знака минус в символьном шрифте cm такая же, как высота знака плюс из стг?»
Дон: A-а. Этому многие удивляются. Там, где у вас встречается 1а — с’, или ^х_\ или что-то в этом роде, почему высота и глубина больше реальных размеров знака минус? На самом деле, здесь не только + и - , тоже самое относится и к ±, =р, ф, ф, ®, 0, х и 4—если вы посмотрите на их METAFONT-код, там есть макро beginarithchar, начинающий любой арифметический символ шрифта и обеспечивающий одинаковый размер для них всех.
Йоханнес: Но это не объясняет почему.
Дон: Это правда —это не объясняет почему. А причина в том, что в самом начале я хотел, чтобы некоторые вещи располагались одинаково. Например, в формуле
+ у + - у
я хотел, чтобы знаки квадратного корня были одинаковыми. Иначе вы бы получили
V^ + y+ V^-У-
И во множестве других случаев в одной части формулы есть плюс, в другой — минус; чтобы пробелы были одинаковыми, эти части должны выглядеть симметрично. Я охотно признаю, что во многих других случаях есть только минус безо всякого дублирующего плюса, и вы удивляетесь, почему там сделан такой большой отступ. Поэтому я говорю ‘\smash-’ [размазать] [смех.] в таких случаях.
Йоханнес: Конкретный случай, из-за которого этот вопрос был задан — Майкл Даунз из Американского математического общества—
Дон: Да, Майкл Даунз, у него больше опыта, чем у кого бы то ни было из присутствующих; он заведует выпуском в печать большей части мировой математики.
Йоханнес: У него проблема с надписью над \rightarrowf ill ...
Дон; Команда \rightarrowf ill? О’кей ... Команда \rightarrowf ill — это штука, которая делает стрелку вправо любой нужной длины, а потом он хочет сделать над ней надпись. Какой макро нужен для этой конструкции? Я не пользовался этой страницей уже давно ... [Смех.] \rightarrowfill сделан из минусов, так что, может быть, знай я о том, что Майкл ... знай я о сложностях Майкла в то время, я бы изменил макро plain T^X’a так, чтобы высота минуса не использовалась в операторе \rightarrowfill? Как бы то ни было, я теперь объяснил вам причины, почему это нужно в других случаях.
Йоханнес: Еще один вопрос, он про поддержку многих языков. Возникают проблемы, когда в одном абзаце присутствуют разные языки.
Дон: Да, изменения \lccode. Это ...
Йоханнес: И мне говорили, что внутри одного абзаца можно использовать только одну таблицу переносов, а именно ту, которая активна в конце абзаца. Так что переключение таблиц переносов становится проблемой. Предположим, например, что у вас есть абзац с английским текстом, внутри которого имеется цитата на немецком языке длиной в несколько строк.
Дон: Нет, я знаю, что ТеХ правильно отслеживает, какую таблицу переносов использовать. Сбой, ошибка, которой я не предусмотрел, появляется, только если у двух языков разные функции \1 с code —так что в каждом из них по- разному решается вопрос, какие буквы строчные. Когда вы делаете перенос, вы должны переносить слово из прописных букв так же, как слово из строчных букв, поэтому ТеХ использует \lccode символа для перевода каждой буквы в такую же строчную букву. Я не предусмотрел, что в разных языках могут быть разные функции перехода от прописных букв к строчным. Именно эта функция в конце абзаца применяется ко всем языкам в этом абзаце. Но в остальном ТеХ аккуратно отслеживает, какой язык является текущим в каждый конкретный момент.,
Кстати, есть файл, называющийся tex82.bug. Зайдите в архивы CTAN и найдите поддиректорию systems/, а в ней knuth/, а в ней errata/, именно там лежит tex82.bug. В конце tex82 .bug эта конкретная ошибка, касающаяся \lccode упомянута в качестве недосмотра, который слишком поздно исправлять.
Марк ван Леувен: Почему слишком поздно исправлять? Это войдет в конфликт с чем-то другим?
Дон: Да. Люди уже используют эти вещи во многих документах, и это очень сложно изменить. На самом деле, я не вижу, как это можно исправить. [ Смех. ] Я бы сказал, что, когда вы сталкиваетесь с ситуацией, в которой вам требуется много разных языков с разными вариантами \lccode, это хорошая причина написать свою собственную версию T^X’a.
Андрис Ленстра: Могу я задать вопрос? К счастью, я не первым упомянул здесь lAT^X, так что я могу упомянуть его и сейчас. Часто, когда люди пытаются написать кандидатскую диссертацию, возникает ситуация, в которой им бы хотелось изменить код ГАТ^Х’а, потому что им кажется, они лучше знают, что такое красота или как правильно печатать текст, но, к большому сожалению, они не эксперты в JATgX’e, поэтому они не достигают успеха или достигают весьма относительного. Во
обще, люди, сведущие в полиграфии, не могут написать хороший ГАТ^Х-код или другие виды кода, и наоборот —люди, знающие, как писать эти виды кода, не смыслят в типографии. Что вы думаете о прошлых попытках совместить оба мира, так, например, как Виктор Эйкаут попытался сделать при помощи своего формата lollipop, механизма для создания других форматов. Я полагал, что это должно было стать серьезным успехом, но кажется случилось обратное. Что вы думаете об этом?
Дон: Я не знаком с системой lollipop близко. Я полагаю, она основана на известной цитате из Элана Перлиса, сказавшего: «Если кто-то говорит, что ему нужен язык программирования, который все будет делать только правильно, дайте ему леденец (lollipop)».
Андрис: Да.
Дон; Я уверен, что опыт с системой lollipop был поучительным и стоил затраченных усилий, но я не знаю подробностей, так что не могу подробно ответить на вопрос. Может быть, создателям шрифтов показалось, что язык слишком сложно выучить. Я все-таки думаю, что сейчас, с каждым месяцем, общение между людьми, сведущими в шрифтах, и людьми, сведущими в макро, становится все активнее и активнее. Это всего лишь вопрос времени, а пока эти волны продолжают двигаться — мы еще очень далеко от предельного состояния, когда Т^Х достигнет своих естественных границ и создатели шрифтов достигнут своих естественных границ. Границы все еще сближаются. Я не думаю, что получится как в гиперболической геометрии, где они никогда не сойдутся.
Главная сложность, конечно, в том, что Т^Х распространяется бесплатно, и поэтому многие люди скажут: «Что же может быть в нем хорошего, если вы не получаете за это денег?» Многие из сообщества шрифтовиков будут работать только над тем, за чем стоят деньги; деньги для них доказательство, что с людьми стоит разговаривать. Так что должно пройти некоторое время, прежде
чем они увидят какие-нибудь удачные примеры и станут более расположены к общению. И это все время происходит в разных странах.
В Чешской Республике я был очень обрадован, узнав, что новая чешская энциклопедия, первая за много лет, делается в ТЁХ’е. И помимо этого у нее очень высокий бюджет. Издатели пришли к такому решению, потому что они перепробовали все остальные системы и те им не понравились. Т^Х же их вполне удовлетворил. Многие другие коммерческие издательства тоже используют его, потому что общаются со своими друзьями из больших издательских домов. Я думаю, этот вопрос со временем решится. А продукты вроде системы lollipop пока что очень ценны для упрощения этого процесса. На то, чтобы объединить два сообщества, требуется время. Я думаю, финансовый фактор является определяющим для многих людей.
Пит ван Острюм: Я не знаю, смотрели ли вы, как устроен код lAT^X’a изнутри, но если вы заглянете туда, у вас возникнет впечатление, что *ТЁХ не самый подходящий язык программирования для создания таких больших систем. Думали ли вы когда-либо, что Т^Х будет использован для этого, и, если нет, не приходило ли вам в голову придумать для этого язык получше?
Дон: В каком-то смысле, многие возможности ТЁК’а, связанные с программированием, появились только после долгих дебатов; поэтому я попробую описать подоплеку. Я знаю, как Лесли [Лэмпорт] работал над IAT^X’om — сначала он записывал алгоритмы на языке программирования высокого уровня, с циклами while и ветвлениями if-then и пр.; потом он довольно механически переводил высокоуровневый код в макро TJ%’a. Если бы я предвидел, что это будет самым распространенным способом использования ТЁХ’а, я бы, возможно, в те дни серьезно волновался об эффективности. Теперь компьютеры стали настолько мощными, что я уже не беспокоюсь так сильно о времени работы, потому что программа по-прежнему кажется работающей мгновенно!
В 70-е годы у меня сложилось отрицательное отношение к программам, которые пытались делать все для всех. Каждая из виденных мной систем имела свою собственную, каким-то образом встроенную машину Тьюринга, и каждая такая машина немного отличалась от любой другой машины. Поэтому я думал: «Ну, я-то не собираюсь создавать язык программирования; мне нужен просто язык для печати текстов». Мало-помалу, тем не менее, мне требовались все новые возможности, и поэтому программистские конструкции множились. Гай Стил начал заблаговременно проталкивать еще больше возможностей, и многие такие вещи я включил во вторую версию ТЁК’a — ТЁХ82 — по его настоянию. Это позволило находить простые числа, а также делать сложные вещи, связанные с версткой страниц и расположением рисунков. Но причина, по которой я не включил возможности программирования с самого начала, заключалась в том, что как программист я устал от необходимости учить еще один почти-такой-же язык программирования для каждой системы, которая меня интересовала; я хотел попытаться избежать этого. Потом я понял, что это в некотором роде неизбежно, но старался оставаться по возможности верным образцу ТЁХ’а как посимвольного макроязыка. Как я уже сказал раньше,
я считал, что самые хитрые приложения будут делаться за счет изменения скомпилированного кода. Но люди этого делать не стали; они хотели делать низкоуровневые вещи на высоком уровне.
Пит: Что вы думаете, например, о том, чтобы в некотором смысле встроить язык программирования, который с точки зрения разработки программного обеспечения будет легче использовать?
Дон: Было бы неплохо, если бы существовал всем понятный стандарт внутреннего языка-интерпретатора для произвольного приложения. Возьмите регулярные выражения — UNIX я определяю как «30 определений регулярных выражений, живущих под одной крышей». [Смех.] В каждой части UNIX’a немного другие регулярные выражения. Теперь, если бы существовал универсальный простой язык-интерпретатор, общий с другими системами, я бы, естественно, немедленно на него перешел.
Пит: The Free Software Foundation пытается сделать его, и Sun пытается сделать его, и Microsoft пытается ...
Дон: The Free Software Foundation на самом деле пытается включить также решения Sun и Microsoft. Другими словами, заставить, насколько это возможно, работать все правила вместе. А это не согласуется с моим собственным стилем, для которого характерно достигнуть скорее единообразия, а не разнообразия ... я не стремился предоставить возможность делать одну и ту же вещь десятью способами. То же самое с C++ — каждый раз, когда члены комитета по C++ говорили: «Ну, это можно сделать так или вот так», они делали и так и так. В своих системах я не пошел по этому пути, потому что он ведет к полной неразберихе. Но я согласен, что эта неразбериха —лучшее из того, что на данный момент может быть реализовано на практике.
Марк: У меня вопрос о документированном программировании. Я знаю, оно должно вам очень нравиться, если я правильно понимаю ваши интервью — Дон: Да, оно мне так нравится, что я мог бы ... ну ... О’кей. [ Смех. ] Вы знаете, мне действительно очень нравится документированное программирование, это одна из величайших радостей в моей жизни —просто им заниматься.
Марк; Мой вопрос заключался в том, что, очевидно, оно не так популярно, как ТЁК, и кроме того в мире документированного программирования не хватает согласованности. Используется около дюжины разных систем —одни люди предпочитают этот путь, другие предпочитают другой, — и это меня несколько беспокоит. Мне тоже очень нравится этот стиль программирования, но мне бы хотелось, чтобы им чаще пользовались.
Дон: Документированное программирование настолько превосходит все другие стили программирования, что трудно понять, почему им уже не пользуется весь мир. Но мне кажется, Ион Бентли попал в точку, когда сказал, что причина состоит примерно вот в чем: в мире не так много хороших программистов и в мире не так много хороших писателей, а здесь мы ожидаем, что человек окажется и тем и другим. Это некоторое преувеличение, но оно вскрывает суть
вопроса. Я думаю, каждый, кто занимался документированным программированием, согласится, что это действительно стоящая вещь, но они не уверены, что оно доступно обычному студенту. Некоторые эксперименты в Texas А&М доказывают обратное, и у меня был подобный опыт в меньшем масштабе в Станфорде. Это путь программирования через гипертекст; и я полагаю, что с лучшим гипертекстом, вроде имеющегося теперь, и с людьми, все более и более привыкающими к Сети, мы получим набор новых несовместимых систем, поддерживающих документированное программирование. Надеюсь, кто- нибудь, обладающий временем, и талантом, и вкусом, соберет воедино систему документированного программирования, настолько очаровательную, что она привлечет множество народа. Я верю, что там есть потенциал, он ждет только нужного человека, который бы его раскрыл.
Марк: Я думаю, одна из проблем заключается в том, что, если вы сравните ваши программы со средними программами из тех, что пишутся другими людьми, в средней программе и близко нет стольких интересных алгоритмов, так что документированное программирование не много добавляет к программе, которая сама по себе скучна.
Дон: Что ж, спасибо за ваше замечание. Но, может быть, иногда я делаю неинтересный алгоритм интересным, просто вставляя туда шутку. Я взял, например, программы, полученные от Sun Microsystems, и в качестве упражнения потратил день на перевод их в документированную форму. Там не было никаких сногсшибательных алгоритмов, но, тем не менее, можно посмотреть на получившуюся программу, и она была лучше — у нее была лучше диагностика ошибок, она была лучше структурирована, в ней были исправлены некоторые недочеты. У меня нет времени пойти в Sun, показать им это и сказать: «Почему бы вам не переделать вашу операционную систему?» [ Смех.] Но я знаю, что она стала бы намного лучше. Так что все, что я опубликовал, это очень простая переделка программы wc подсчета слов в UNIX’e. Ничего особенного в этом алгоритме нет, но это демонстрация того, как можно сделать качественное системное программирование приятным занятием.
Мой подход к документированному программированию, конечно, не единственный. В недавно вышедшей книге группы авторов из Принстона А Retargetable С Compiler Крис Фрейзер и Хансон использовали разные документированные программы для описания их компилятора С. Выходят другие книги, использующие другие особенности документированного программирования. Я разговаривал с человеком из Microsoft, и он сказал, что, по его мнению, документированное программирование на подъеме, а я сказал: «Значит ли это, что следующая версия Windows будет сделана при помощи документированного программирования?» ... «Ну, нет, не то чтобы». [Смех.] Люди, вкусившие документированного программирования, никогда не вернутся к старому, и возможно, они постепенно приобретут влияние. Компании, использующие его, будут продавать больше продукции, чем их конкуренты, так
что довольно скоро это случится. Я полагаю, что пользователей документированного программирования около десяти тысяч, а пользователей TgX’a около миллиона, так что соотношение 1 к 100.
Марк: Не думаете ли Вы, что ему еще надо развиваться? У меня такое впечатление, что, со столькими инструментами вокруг, оно еще не созрело. Идея уже созрела, но воплощение еще нужно ...
Дон: Да, это правда. Имеется огромная потребность в программных средах, основанных на этой идее. Это вовсе не просто — создать эти среды и иметь достаточно влияния для их распространения и, может быть, средств, чтобы сделать их установку не слишком дорогой и не слишком трудной. Идеально было бы, если бы the Free Software Foundation приняла эту идею, или кто-то вроде них, или люди, с ними работающие. На самом деле, [Ричард] Столлман [из the Free Software Foundation] создал для себя вариант документированного программирования, и у него оно хорошо интегрировано с ТЦК’ом, в его собственной манере. Не слишком много программ, в которые он его вставил, но он к этому идет. Это одна из вещей, которым, как Вы говорите, нужно еще дозреть.
Марк: Считаете ли вы, что документированное программирование должно развиваться в сторону интегрированных систем, где у вас действительно имеются все необходимые возможности в одной системе? Потому что, мне кажется, современная тенденция больше склоняется к самым минимальным системам, которые не позволяют сделать красивую распечатку, потому что это приводит к слишком большим неприятностям, когда вы переключаетесь между языками программирования. Так что в реальности это выливается во что-то очень гибкое, но не очень удобное в использовании.
Дон; Для меня лично одного языка программирования вполне достаточно, так что я не тот, кого стоит об этом спрашивать. Что касается моих собственных потребностей, я собираюсь писать The Art of Computer Programming в течение следующих двадцати лет, и вполне уверен, что CWEB будет полностью удовлетворять всем моим запросам. Я буду писать программы для системы Mathematica™ и буду иногда писать программы для МЕТАРОЕТ’а; я мог бы разработать или использовать документированное программирование для этих систем, но не думаю, что осуществлю это. Я пишу не так много строк кода, чтобы получить серьезный выигрыш ... хотя впоследствии я бы получил лучшие программы. Если только кто-нибудь не подарит мне уже сейчас хорошую систему для них, я не буду пользоваться MathWeb’oM или MPWeb’oM. Но на CWEB’e в обозримом будущем я собираюсь писать в среднем пять программ в неделю, моя продуктивность бесконечно больше, когда я делаю это, используя документированное программирование.
Еще одна мысль промелькнула у меня в голове во время моих предыдущих слов ... Я написал статью, это было, по-моему, в прошлом году, о мини-указателях для документированных программ, и пытался представить,
какого сорта программная среда мне бы помогла. В листингах в ТеХ: The Program и METAFONT: The Program, а также в The Stanford GraphBase справа на каждом двухстраничном развороте имеется предметный указатель всех идентификаторов, встречающихся на этих двух страницах, с указанием места их объявления. В моей статье рассказывается о системе, использованной мною для получения этих указателей, и такого типа деятельность потребуется также в любой гипертекстовой системе. Такие минимальные системы привлекательны в первую очередь тем, что хороший программист может написать их за пару дней, понимать и использовать их и извлекать из них много пользы. Как только будет написана хорошая гипертекстовая система для документированного программирования, я думаю это привлечет очень многих. Нам нужна крепкая система, которая не падает и имеет знакомый пользовательский интерфейс, потому что похожа на другие гипертекстовые системы, которыми мы уже пользуемся. Время для этого наступит примерно через два года.
Эрик: Сейчас уже половина десятого, и я думаю, нам стоит остановиться на этом. Я хочу поблагодарить нашего особого гостя, Дональда Кнута, за эту встречу. Я думаю, мы все многое узнали. Мы очень рады, что вы смогли присутствовать здесь. Благодарим вас.
Дон: Я очень ценю всю проделанную вами работу по подготовке помещения для этой встречи за довольно короткий срок. [ Аплодисменты. ]
Эрик: Также благодарим издательство Elsevier Science, оказавшее помощь в лице Саймона Пеппинга, и нашего английского коллегу Себастиана Ратца, которого здесь нет, хотя я его и ждал. Но он оплатил кофе и чай, так что спасибо. У нас, конечно, есть небольшой подарок для вас. Надеюсь, он вам понравится! [ Он дарит книгу о голландском искусстве, называющуюся De StijR. ]
Дон; А, да ... голландский создатель шрифтов, Герард Унгер, приезжал в Станфорд на три недели, он и его жена Марьян, и они говорили о подобных вещах с нашими создателями шрифтов. Они также связывали моду в одежде, и мебели, и архитектуре еще и со стилями печати. Это замечательная книга. Она сделана вТЁХ’е?
Эрик: Я не думаю ... поскольку мы сейчас в Голландии ... [ смех. ] [ Он дарит также пару деревянных тюльпанов. ]
Дон: Прекрасный подарок моей жене.
Эрик: И, конечно, экземпляр трудов конференции EutoTeX’OS? [ он дарит труды конференции. ]
Carel Blotkamp et al., De Stijl: The Formative Years (M.I.T. Press, 1986); перевод книги De Beginjaren van De Stijl (Utrecht: Uitgeverij Reflex, 1982).
Wietse Dol, editor, Proceedings of the Ninth European T£X Conference, September 4-8, 1995, Arnhem, The Netherlands, 441 pp.
Дон: О!! Я думал, вы никогда ... [смех]. Да, я видел их на прошлой неделе в Чешской Республике, так что спасибо всем.
Эрик: Что вы думаете об использованных шрифтах?
Дон; Я думаю ... о, вы использовали Computer Modern Brights. Да, единственная жалоба, которая у меня была, — кернинг в самом слове ‘Т^Х’ можно было немного подправить? В целом печать вполне привлекательна — большое спасибо.
1 The ТЁХ logo in various fonts, TUGboat 7 (1986), 101; перепечатано в главе 6 данной
книги.
Последние ошибки ТвХ’а
[Эта глава была написана в августе 1998 года.]
Когда я завершил мою статью про «Ошибки Т^К’а» в сентябре 1988 года [2], я сказал, что «собираюсь опубликовать через десять лет короткую заметку, которая окончательно завершит список». Вот эта обещанная короткая заметка.
В сентябре 1988 года журнал ошибок Т^Х’а заканчивался пунктом номер 865. Шестнадцать пунктов добавились в журнал с мая 1987 года, так что у меня не было никаких причин считать, что Т^Х вскоре достигнет абсолютной стабильности. Впрочем с уверенностью могу сказать, что ожидал в конечном итоге иметь менее 900 ошибок.
Но эти надежды были перечеркнуты в 1989 году, когда я понял, что изменения в технологии свели на нет одно из моих ранних предположений. Я считал, что ввод 7-битных символов будет по-прежнему оставаться нормой; но быстрое развитие компьютерных систем, основанных на байтах из 8 битов, и рабочих станций, использующих 8-битные символы для букв с акцентами, ясно показало, что пристойную систему Т^Х, предназначенную для всемирного сообщества, придется снабдить новыми средствами для более адекватной обработки неанглийских языков. Поэтому после серьезных обсуждений, пик которых пришелся на 10-е ежегодное собрание сообщества пользователей TgX’a, я решил встроить несколько существенных новых возможностей в версию 3 TgX’a и версию 2 METAFONT’a [4].
Естественно, новые расширения вызвали всплеск активности в журнале ошибок ТЁХ’а, достигшем 900 пунктов уже в январе 1990 года,. Сводка всех записей журнала на конец 1991 года была опубликована в [3], составив в сумме 916 пунктов. И это всё еще был не конец истории; запись в моем дневнике от 9 января 1992 года говорит: «Увы, плохие новости от ТЁХогетов: три новые ошибки в ТуСе, одна —в METAFONT’e».
Сегодня, как бы то ни было, я рад сообщить, что программа ТЦХ’а сохраняла стабильность последние три года, так что, может быть, процесс наконец- то сошелся. Оставшиеся записи журнала, с 1992 года по настоящий момент, показаны на рис. 1; полностью подробное описание всех изменений в форме листингов строчек кода, этими изменениями затронутых, в состоянии до и
10 January 1992
881 н
917
Also avoid producing a double kern at boundary (GET).§897
S
S
S
918
919
Disallow \setbox where it doesn’t work (Robert Hunt).§1241,1270
Robustify \mskip and \mkern in presence of negative quad (WGS).§716, 717
679 н
920
Defend against ‘X’ in \read (Michael Downes).§483
S
798 н
921
Save string memory if font occurs repeatedly (Boguslaw Jackowski).§1260
E
784 н
922
Don’t let \newlinechar interrupt unprintable expansion (Bernd Raichle). §59,60
7 February 1992
S
881 ь
■+ 923
Restore cur_l properly when boundary character doesn’t exist (Mattes and Raichle).§1036
17 July 1992
D
892 н
924
Use current language at beginning of horizontal mode (Rainer Schopf and GET).§1091,1200
17 December 1992
C
879 н
925
Avoid (harmless) range errors (Philip Taylor and GET).§934,960
25 February 1993
R
881 н
926
Protect kerns inserted by boundary characters (William Baxter).§837,866
C
917 н
927
Don’t let boundary kern disappear after hyphenation.§897
26 June 1993
S
668 ь
928
Avoid potential future bug (Peter Breitenlohner).§628,637
17 December 1993
R
881 н
929
Boundary character representation shouldn’t depend on the font memory size (Berthold Horn).§549,1323
10 March 1994
S
930
Huge font parameter number may exceed array bound (GET).§549
4 September 1994
R
926 н
-► 931
Math kerns are explicit (Walter Car lip).§717
F
932
Avoid overflow on huge real-to-integer conversion.§625,634
19 March 1995
R
933
Avoid spurious reference counts in format files (PB).§1335
R
Рис. 1.Журнал ошибок TJjX’a (1992-1998).
после внесения изменений можно найти в файле, называющемся tex82.bug, являющемся частью архивов CTAN в Интернете.
Гистограмма на рис. 2 показывает, с какой интенсивностью журнал ошибок пополнялся 68 пунктами после сентября 1988 года, пик интенсивности приходится на первую половину 1989 года, а впоследствии она падает, за исключением случайных всплесков активности, когда новые типы пользователей наталкивались на новые типы ошибок. Затененные прямоугольники обозначают пункты, порожденные предыдущими записями в журнале; если бы я на должном уровне сделал предыдущие исправления, эти 34 пункта не понадобились бы.
Некоторые записи в журнале представляют собой серьезные, коренные изменения в программе. Например, изменение номер 878 — приводящее программы 7-битного ввода/вывода к новым 8-битным правилам — заменило 195 строк WEB-Паскаля на 213 новых строк кода. Изменение 881, вводящее «умные лигатуры», потребовало замены 78 строк на 856 новых строк. А изменения 879-880, необходимые для многоязыкового переноса, заменили 155 строк на 329.
Но такие коренные изменения возникали только при добавлении основных новых возможностей версии 3.0. На самом деле, около 57% всех изменений с
Рис. 2. Когда делались изменения.
1988 года потребовали замены менее 10 строк кода на Паскале менее чем 10 новыми строками.
Если использовать пятнадцать категорий из [2] для классификации 68 записей в журнале за последнее десятилетие, мы получим следующие суммы:
Тип А (алгоритмические аномалии), 0.
Тип В (бывший бедлам или брак), 0.
Тип С (чистка для членораздельности или четкости), 4.
Тип D (дезорганизация структуры данных), 6.
Тип Е (эскалация эффективности), 2.
Тип F (факультативная функциональность), 3.
Тип G (генерализация или глобализация), 11.
Тип I (интерактивная интенсификация), 2.
Тип L (лаконичность лексикона), 0.
Тип М (модуленесовместимость), 1.
Тип Р (продвижение переносимости), 2.
Тип Q (копилка качества), 1.
Тип R (укрепление усиления), 10.
Тип S (ситуация-сюрприз), 24.
Тип Т (тривиальный тип), 0.
Ни для кого не сюрприз, что в списке доминируют сюрпризы.
Были ли какие-нибудь из этих изменений особенно поучительными или заслуживающими особого внимания? Большинство уроков, извлеченных после 1988 года, были сходны с уже перечисленными в [2], только их стало больше. Опыт еще десяти лет подтверждает, что, если мы хотим полностью избавить от ошибок сложную программу, следует быть готовыми приложить к этому значительные усилия.
Последняя ошибка?
Самая интересная из недавних ошибок вполне может оказаться и последней, номер 933 —я действительно надеюсь, что она окажется «исторической» последней ошибкой в ТуХ’е. Эта ошибка, обнаруженная в 1995 году Петером Брайтенлонером, довольно эзотерична и в обычной работе никак не проявля-
ется; всё же мне кажется, он честно заслужил награду в $327.68, которую я выплатил ему 20 марта 1995 года.
Ситуация такова: Т^Х борется с накоплением мусора, используя счетчики ссылок в ключевых местах структур данных. Предполагается, что для хранения счетчика ссылок требуется не больше памяти, чем для указателя, потому что число ссылок на узел не может превышать общего числа узлов, если соблюдаются правила TgX’a.
Брайтенлонер обнаружил пробел в этой непогрешимой логике, используя специальную версию TgX’a под названием INITEX. Системные мастера установки инсталлируют макропакеты для INITEX’а, вызывая команду \dump [сбросить], которая сохраняет текущее содержимое структур данных в так называемом форматном файле; \dump запрещена в обычных версиях "ЩК’а. Всё время используемые шрифты и макро могут быть быстро подгружены Т^Х’ом или INITEX’ом, если до этого они были сброшены в форматный файл.
Когда команда \dump возникает в процессе раскрытия макро и/или посередине условного выражения, структуры данных, контролирующие состояние ввода INITEX’а, не сохраняются как часть форматного файла, потому что такие вещи безразличны для конечного пользователя. К несчастью, тем не менее, эти структуры данных могут ссылаться на объекты, которые сохраняются — и тут-то вся загвоздка. До того как было сделано изменение номер 933, INITEX мог сбрасывать счетчики ссылок, включающие ссылки из узлов, которые впоследствии не будут загружены. Поэтому пользователь с преднамеренным злым умыслом мог всё время просить INITEX загружать такие файлы и сбрасывать новые, еще худшие; счетчики ссылок могли расти до тех пор, пока они не станут сколь угодно велики.
Изменение 933 добавило инструкции к программе ТвХ’а finaLcleanup, так чтобы все ссылки из невосстановленных указателей удалялись перед сбрасыванием. Аналогичное изменение, конечно, было сделано в METAFONT’e.
Не ошибки, но близко к этому
Я зарезервировал для себя правило решать относительно любого подозрительного места, является ли это ошибкой ТЁК’а, которую надо исправить.
Некоторые люди считают, что TgX некорректен, только потому, что пользователи легко могут заставить его зациклиться. Но на самом деле ТЁХ и должен зацикливаться, если ему даются команды, требующие бесконечной деятельности. Например,
\def\x{\x}\x
приводит к бесконечному раскрытию макро. Более интересный пример \let\par=\relax \noindent\vfill
говорит ТЁХ’у, чтобы он продолжал выполнять \relax, пока не перейдет в вертикальный режим —а этого никогда не произойдет! (См. правила на с. 286 в книге The Т^КБоок [1]*)
См. также в русском издании «Всё про ТЁХ» на сс. 340-341. — Прим, перев.
Обобщая, я бы сказал, что ТЁХ не должен выдавать сообщение об ошибке «Это не может произойти», если только ранее он не выдал какого-нибудь другого сообщения об ошибке. Но это определение ошибочного поведения тоже имеет исключения, потому что программа ТЁХ’а явно признает, что определенные типы ошибок при выполнении возможны, но тем не менее абсурдны (так как они не возникнут при нормальном использовании).
Например, §798 программы вызывает фатальное сообщение «это не может произойти», когда пользователь пытается сделать \span [выделение] 256 или более колонок таблицы. Программа §798 содержит комментарий
{это может произойти, но не произойдет}
— ясно признающее тот факт, что сообщение ТЁХ’а в этом отдаленно возможном случае ложно. Вы можете называть это примером моего извращенного чувства юмора; или вы можете подумать, что я не хотел выдавать ошибку «переполнения», потому что максимальное число выделяемых колонок не так легко увеличить, как другие величины, перечисленные на с. 300 в книге The T^book и выдаваемые по команде \tracingstats; но нельзя назвать это ошибкой, заслуживающей $327.68. Разработчики, не согласные с этим, вольны изменять поведение ТЁХ’а в этом случае, так как тест TRIP не имеет предписаний, как нужно действовать при фатальных ошибках. Например, они могли бы заменить сообщение по аналогии со случаем «дискреционный список слишком велик» в §1120, если они действительно считают, что честный пользователь будет введен в заблуждение при такой обработке ТЁХ’ом попыток создания слишком больших таблиц. Но я не думаю, что это выход. Между прочим, в 1996 году Дейвид Каструп нашел потрясающе хитрый способ добиться от ТЁХ’а такого лживого поведения с помощью следующей замечательно короткой программы:
\def\x#l{\if#lm\span\expandafter\x\fi}
\halign{&#\cr\expandafter\x\romannumeral256001\cr}
Еще один пример аномального поведения, про которое точно известно, что с ним можно столкнуться в экстремальных условиях, возникает, когда числа становятся достаточно большими, чтобы вызвать арифметическое переполнение. ТЁХ пытается отловить целочисленное переполнение до того, как оно произойдет, в ситуациях, которые предположительно будут возникать на практике (см., например, §104, §445 и §1236); но в §104 указывается, что я не взял на себя труд сделать программу совершенно непробиваемой для вражеских атак.
Например, ТЁХ’овский код
\hbox{\romannumeral\maxdimen}
создает блок, содержащий 1073 741 букву т, за которыми следует ‘dcccxxiii’. В обычном шрифте cmrlO этот блок будет иметь ширину, в точности равную 586 410 016 845 пунктам; это около 1.954 мили (3.145 километра). Что- то да произойдет. В этом случае строка сообщений ТЁХ’а, где формируются римские числа, будет переполнена, но в меньших примерах вместо
этого даст сбой целочисленная арифметика. Хотя флаги компилятора в §9 указывают, что целочисленное переполнение должно отлавливаться аппаратными средствами, современные реализации Т^Х’а часто пренебрегают этой рекомендацией; например, типичная для UNIX’a установка сообщит, что \hbox{\romannumeral3932000} имеет ширину 32766.7666 пункта, в то время как ширина в пунктах \hbox{\romannumeral3932001} составляет -32766.45555!
Целочисленное переполнение можно получить многими способами, если приложить достаточные усилия для преодоления имеющихся установок. Например, можно состряпать абзацы, имеющие более 231 дефектов, при некоторых выполнимых последовательностях разрывов строк. Но я не считаю такие конструкции доказательством ошибок в ТЁХ’е, если только меня не убедят, что разумный пользователь может столкнуться с таким поведением. (Или если, как с ошибкой 933, не нарушено одно из фундаментальных предположений в замысле Т£Х’а.)
По причинам, связанным с независимостью от платформы, я не считаю, ни что максимальное число выделяемых колонок в таблице нужно увеличить, сделав больше 255, ни что максимальный размер целочисленной константы на компьютерах с 64-битной арифметикой должен превышать 231 — 1 = 2147483647. В конце концов, I^X —это язык для печати текстов. Кому нужны такие гигантские числа?
Просчеты проекта
Проект Т^Х’а был заморожен с 1990 года; поэтому больше никто не может сказать, что в замысле Т^Х’а есть какая-нибудь ошибка. В настоящее время я всё же знаю три вещи, которые я бы изменил, если бы задумался о них перед тем, как решил заморозить проект в окончательной форме:
Дополнительные параметры в символьных шрифтах могли бы влиять на минимальное расстояние между линейками в дробях, \sqrt, \overline и \ under line; в настоящее время это минимальное расстояние зависит только от толщины линии.
Только один набор значений внутренних кодов, переводящих прописные буквы в строчные, используется в абзаце для переноса, даже если в абзаце присутствуют несколько языков с разными переводящими функциями. В расширениях ТЁХ’а, в которых эту проблему захочется преодолеть, возможно нужно будет предусмотреть новые типы whatsit’oB, записывающих изменения значений \lccode.
Проблем можно избежать, не расширяя ТЁХ, если использовать следующее окружение: макро \hyphenate, определяемый как
\newbox\hyfbox \def\hyphenate#l{{\everyvbox{}\setboxO= \vbox{\pretolerance=-l\parfillskip=Opt\hsize=\maxdimen
\rightskip=0pt\hbadness=10000\everypar{} \noindent\hskip-\leftskip #l\endgraf \global\setbox\hyfbox=\lastbox}}\unhbox\hyfbox}
перенесет всё, что ему будет поступать, используя правила переносов из текущего языка, возвращая горизонтальный список, включающий мягкие переносы.
Правило 12 в приложении G в книге The TgXbook не позволяет в математическом режиме ставить большие акценты (скажем, из шрифта cmrl7) близко к маленьким буквам из-за формулы 5 <— min(/i(a;), х) • Эта формула не позволяет опустить акцент ниже высоты блока под ним, поэтому акцент не опустится ниже своего первоначального положения в шрифте.
На проблему (3) мое внимание обратил Воан Пратт в марте 1998 года; он спросил, как напечатать ‘г’ (с акцентом, большим нормального ‘г’, полученным из $\check г$), и мое лучшее решение в plain ТЁХ’е было некрасивым:
\font\bigacc=cmrl7 \textfont"F=\bigacc \def\CHECK{\mathaccent"7F14 }
\setbox2=\hbox{$r$}\setbox4=\hbox{\raise3pt\copy2}\ht4=\ht2 \def\CHECKr{\lower3pt\hbox{$\CHECK{\copy4}$}}
Я не вполне уверен, что бы я сделал, если бы он задал мне тот же вопрос в 1988 году; в то время я бы посчитал это ошибкой в TEX’e. Теперь это лишь неудачное свойство.
Заключение
Любую сложную систему можно улучшить; поэтому достичь абсолютного совершенства и оптимальности невозможно. Всё же я думаю, можно сказать, что ТЁК достиг вполне удовлетворительного состояния, при условии, что стабильность сама по себе тоже очень важна. Я глубоко признателен многим добровольцам по всему миру, помогавшим мне формулировать многие изменения, приведшие TgX к его нынешнему состоянию, известному как «версия 3.14159».
Литература.
Donald Е. Knuth, The TfiXbook, Volume A of Computers & Typesetting (Reading, Massachusetts: Addison—Wesley and American Mathematical Society, 1984). [Имеется русский перевод: Дональд Е. Кнут. Всё про ТрХ. — Протвино: АО RDT^X, 1993.]
Donald Е. Knuth, “The errors of ТЁХ,” Software—Practice and Experience 19 (1989), 607-685; перепечатано с дополнениями и исправлениями в главе 10 книги Literate Programming. См. также [3].
Donald Е. Knuth, “The error log of ТЁХ (1978-1991),” глава И книги Literate Programming, CSLI Lecture Notes 27 (Stanford, California: Center for the Study of Language and Information, 1992), 293-339.
Donald Е. Knuth, “The new versions of 1ЁХ and METAFONT,” TUGboat 10 (1989), 325-328; 11 (1990), 12. Перепечатано в Die TKKnische Komodie 2,1 (March 1990), 16-22. Перевод на французский Алена Куске (Alain Cousquer), “ТЁК 3.0 on le ТЁХ nouveau va arriver,” Cahiers GUTenberg, № 4 (December 1989), 39-45. Также перепечатано в главе 29 этой книги.
Добавление (декабрь 2002 г.)
В 1998 г. TgX стал успешно печатать текущий год с 4 цифрами вместо 2, таким образом решив «проблему 2000 года».
В 1999 г. была обнаружена погрешность в \xleaders, в 2001 г. устранена отмена выравнивания ТЁ^’а; округление клея было улучшено в 2002 г. Но, оставаясь оптимистом, я продолжаю думать, что больше никаких изменений не понадобится.
Примечание переводчика
Перевод текста, представленного на рис. 1.
881 н
10 января 1992 года
-*917 Избежать также двойного керна на границе (СЕТ).§897 S
918 Запретить \setbox там, где он не работает (Роберт Хант).§1241,1270 S
679 н
798 н
919 Усилить \mskip и \mkern в присутствии негативного quad (WGS).§716,717 S
-* 920 Защититься от ‘И’ в \read (Майкл Даунз).§483 S
-* 921 Сохранять строковую память, если шрифт появляется неоднократно (Богуслав
Яцковский).§1260 Е
784 н
-* 922 Запретить команде \newlinechar прерывать не отражаемое на печати раскрытие
(Бернд Райхле).§59,60 S
881 н
7 февраля 1992 года•
-* 923 Правильно восстанавливать сиг_1, когда граничного символа нет (Маттес и Райхле).§1036 D
17 июля 1992 года
892 н
-* 924 Использовать текущий язык в начале горизонтального режима (Райнер Шёпф и СЕТ).§1091,1200 С
879 н
17 декабря 1992 года
-* 925 Избежать (безобидных) ошибок диапазона (Филип Тейлор и СЕТ).§934,960 R
881 н
917*-
25 февраля 1993 года
-* 926 Защитить керны, вставляемые граничными символами (Уильям Бакстер).§837,866 С
-*927 Предотвратить исчезание граничного керна после переноса.§897 S
668 н
26 июня 1993 года
-* 928 Избежать потенциальной будущей ошибки (Петер Брайтенлонер).§628,637 R
881 н
17 декабря 1993 года
-* 929 Представление граничного символа не должно зависеть от размера памяти шрифта (Бертольд Хорн).§549,1323 S
10 марта 1994 года
930 Большое значение параметра шрифта может превысить размер массива (СЕТ). §549 R
926 к
4 сентября 1994 года
-*931 Математические керны точно определены (Уолтер Карлип).§717 F
932 Избежать переполнения при переводе больших действительных чисел в целые. §625,634 R
19 марта 1995 года
933 Избежать фальсификации счетчиков ссылок в форматных файлах (ПБ).§1335 R
Именной указатель
Адлеман (Adleman, Leonard Max) 585
Алжи (Algie, Stephen H.) 466, 467
Анастассиу (Anastasiou, Dimitris) 466
Аристотель (Aristotle of Stagira) 296
Бакстер (Baxter, William Erik) 658
Барнетт (Barnett, Michael Peter) 43, 75, 149, 163
Бартелс (Bartels, Samuel A.) 148, 163
Бартлетт (Bartlett, Fred) 599
Бафур (Bafour, Georges P.) 148, 163
Беббидж (Babbage, Charles) 559
Безье (Bezier, Pierre Etienne) 78
Бентли (Bentley, Jon Louis) 646
Берри (Berry, Daniel Martin) 630
Бехтольсхайм, фон (Bechtolsheim, Andreas von) 548
Бибби (Bibby, Duane Robert) 554, 566, 626, 627
Бигелоу (Bigelow, Charles Andrew) 314, 315, 318, 333, 362, 381, 391, 565, 613
Бийби (Beebe, Nelson H. F.) 596, 602
Бил Дарт (Byl Dart, Renata Maria) 392
Биллавала (Billawala, Nazneen Noorudin = Neenie) 392, 635
Биллот-Хоффманн (Billotet-Hoffmann, Claudia) 460, 467
Битон (Beeton, Barbara Ann Neuhaus Friend Smith) 162, 344, 356, 5 78, 604, 626
Бланшар (Blanchard, Andre R.)) 148, 163
Боас (Boas, Ralph Philip) 356, 358
Боден (Baudin, Fernand) 318
Бонтен (Bonnetain, Jean-Luc) 392
Ботнер (Bothner, Per Magnus Alfred) 390, 392
Брайтенлонер (Breitenlohner, Peter) 600, 653, 658
Брамс (Braams, Johannes Laurens) 629
Браун, Малкольм (Brown, Malcolm) 392
Браун, Рой Говард (Brown, Roy Howard) 363, 371
Брокар, де (Brocar, Arnao Guillen, de) 141
Бронсар (Bronsard, Henri-Paul) 318
Брэнд (Brand, Stewart) 628
Брюнгдал (Bryngdahl, Olof) 460,
467
Вайзенберг (Weisenberg, Michael) 392'
Вайсман (Wiseman, Neil Ernest) 162,
315
Валлис (Wallis, John) 349, 368
Ван Вик (Van Wyk, Christopher John) 469, 476
Ванг, Ами (Wang, Amy Ming-te) 547
Ванг, Майкл (Wang, Michael Ming-kai) 547
Вейерштрасс (Weierstrass, Karl Theodor Wilhelm) 366
Вельтюи (Velthuis, Frans Jozef) 295
Весели (Vesely, Jin = George) 621
Видден (Whidden, Samuel Blackwell) 356
Видерхольд (Wiederhold, Giovanni Corrado Melchiore = Gio) 546
Виноград (Winograd, Terry Allen) 546, 620
Винчи, Леонардо, да (Vinci, Leonardo da) 169, 171, 172
Вольф (Wolf, Hans) 73, 565, 620
Вулф (Woolf, William Blauvelt) 356
Гайдн (Haydn, Franz Joseph) 317
Галилей, Галилео (Galilei, Galileo) 124
Гард (Gard, Robert L.) 445, 466, 467
Гауди (Goudy, Frederic William) 52-54, 75, 612
Гиббонс (Gibbons, Jeremy) 578, 596
Го-Ань, Гу (Guoan, Gu, ЙШ$) 31, 32, 35, 330, 343
Годцейн (Goddijn, Franciscus Theodorus Hendrik) 626, 635
Голдфарб (Goldfarb, Charles Frederick) 641
Гопинат (Gopinath, Kanchi) 392
Гор (Gore, Gary) 318
Горак (Horak, Karel) 604, 608
Гори (Gorey, Edward Saint John) 626
Госпер (Gosper, Ralph William, Jr.) 64
Гоулд (Gould, Benjamin Apthorp) 560
Гош (Ghosh, Pijush Kanti fn^jfw ?*t) 294
Гранжан (Grandjean, Phillipe) 54
Гримм, Вильгельм Карл (Grimm, Wilhelm Karl) 86, 136, 157, 164
Гримм, Якоб Людвиг Карл (Grimm, Jakob Ludwig Karl) 86, 136, 157, 164
Грин, Дейл (Green, L. Dale) 540
Грин, Дэниел (Greene, Daniel Hill) 546
Грэхем (Graham, Ronald Lewis) 367, 369, 379
Гуиба (Guibas, Leonidas John) 73, 540
Гуммель (Hummel, Johann Julius) 317
Гунст, Морган (Gunst, Morgan Arthur) 611, 612
Гунст, Элин (Gunst, Aline Dreyfus) 611, 612
Гунтерманн (Guntermann, Klaus)
647
Гуссенс (Goossens, Michel) 602
Гутенберг (Gutenberg, Johannes =
Johann Gensfleish) 24, 51
Даунз (Downes, Michael John) 643, 658
Де Винн (De Vinne, Theodore Low) 43,
75, 138, 153, 163, 164
Дезарменьен (Desarmenien, Jacques
Robert Jean) 392
Дейкстра (Dijkstra, Edsger Wijbe)
640
Декарт (Descartes, Rene) 276
Джексон (Jackson, Calvin William, Jr.) 578
Джустиниани (Giustiniani, Agostino)
142, 143, 145, 146, 163
Дисней (Disney, Walter Elias) 297
Дитше (Dietsche, Luzia) 602
Дол (Dol, Wietse) 626, 649
Долби (Dolby, James Louis) 151, 163
Дрейфус (Dreyfus, John) 367, 368,
624
Дуглас (Douglass, Scott Alan) 226
Дункан (Duncan, Christopher John) 81,
90, 136, 150-152, 163
Дьяс (Diaz de la Pena, Maximiliano
Antonio Temfstocles) 549, 616
Дэрст (Durst, Lincoln Kearny) 356
Дюрер (Diirer, Albrecht) 35, 53, 75, 78, 624
Енест (Earnest, Lester Donald) 73
Жамм (Jammes, Andre)) 73, 76
Жиру (Girou, Denis) 585
Зигель (Siegel, David Rudolph = New
Wave Dave) 362, 368, 380, 391, 392
Зонен (Zonen. Joh. Enschede en) 635
Ив (Eve, James) 162, 163
Капр (Карг, Albert) 318
Карагезян (Karagueuzian, Dikran = ^шршЩоцйшЬ, SJiqpmh) 392
Карлип (Carlip (= C3arlip), Walter Bruce) 658
Карман (Karman, Willem Jan) 634
Каров (Karow, Peter) 318
Картер, Гарри (Carter, Harry Graham) 165, 367, 635
Картер, Мэтью (Carter, Matthew) 293, 295, 314, 315, 322, 333, 565, 613, 635
Картер, Уайлда Бэйтс (Carter, Wilda Ernestine, урожденная Bates) 201
Кастейжо (Casteljau, Paul de Faget de) 78
Каструп (Kastrup, David Friedrich) 655
Кеннеди (Kennedy, John Fitzgerald) 579
Керниган (Kernighan, Brian Wilson) 76, 469, 500
Кертис (Curtis, Pavel) 392
Ким (Kim, Scott Edward) 323, 352, 354, 357, 359-361, 367, 545
Кнут, Джил (Knuth, Nancy Jill, урожденная Carter) 7, 73, 196, 314, 392, 539, 566
Кнут, Дональд (Knuth, Donald Ervin a^^) 7, 76, 163, 164, 184, 250, 291, 316, 343, 367, 368, 379, 380, 392, 414, 445, 467, 468, 476, 558, 657, 658
Кресчи (Cresci Milanese, Giovanni Francesco) 54, 55, 75
Кримпен, ван (Krimpen, Jan van) 347, 367, 368, 623, 624
Купер (Cooper, P. I.) 150, 151, 154, 162, 163
Лан, ван дер (Laan, Cornelis Gerardus van der = Kees) 625, 630, 640
Лантц (Lantz, Kieth Allen) 390
Лапко Ольга Георгиевна 380
Ле Век (LeVeque, William Judson) 352, 354, 356-358, 360
Леви (Levy, Silvio Vieira Ferreira) 592
Лейбниц, фон (Leibniz, Gottfried Wilhelm, Freiherr von) 63
Ленстра (Lenstra, Andries Johannes) 637, 644
Леувен, ван (Leeuwen, Marcus Aurelius
Augustinus van) 629, 630, 643
Лианг (Liang, Franklin Mark) 73, 506, 535, 620
Либан (Leban, Bruce Philip) 392
Ливермор (Livermore, Lawrence) 622
Линдгрен (Lindgren, Michael) 560
Лисина Марина Владиленовна 35
Лич (Lyche, Tom John Wiberg) 73
Лоренц (Lorentz, Hendrik Antoon) 347, 624
Льготка (Lhotka, Lada) 614
Лэмпорт (Lamport, Leslie B.) 616, 645
Лэмпсон (Lampson, Butler Wright) 608
МакКиллин (McQuillin, Richard) 42
Макгаффи (McGaffey, Robert) 585
Маккей (MacKay, Pierre Anthony) 167, 184, 598, 602
Малкольм (Malcolm, Michael Alexander) 71, 76
Маннинг (Manning, J. R.) 59, 76,
317
Маттес (Mattes, Eberhard) 595, 596, 658
Маховая Ирина Анатольевна 10
Мейер (Meyer, Albert Ronald da Silva)
73
Мехлум (Mehlum, Even) 71, 76
Миллс (Mills, Daniel Bradford) 362,
391
Миттаг-Леффлер (Mittag-Leffler)
620
Мицумаса (Mitsumasa, Anno % S 7*6 %) 627
Моксон (Moxon, Joseph) 54, 77, 146, 147, 164
Моцарт (Mozart, Johann Chrysostom Wolfgang Gottlieb) 310, 317
Мэрдок (Murdock, Phoebe/Phoebe
James) 356
Мюллер (Muller, Johann Helfrich)
559
Нес, ван (Nes, Gerard van) 625
Несбитт (Nesbitt, Alexander) 318
Нетука (Netuka, Ivan) 604
Нойвирт (Neuwirth, Erich) 29lr 292
Норден (Norden, Hugo Svan) 317
Ньютон (Newton, Isaac) 63, 350
Олдс (Olds, Arnold Elbert) 392
Ольшак (Olsak, Petr) 616
Оппенгеймер (Oppenheimer, Julius
Rqbert) 348
Оруэлл (Orwell, George = Blair, Eric
Arthur) 329
Осборн (Osborne, Stanley) 392
Острюм (Oostrum, Pieter van) 627, 645
Палатино (Palatino, Giovanbattista) 53, 63, 77
Пале (Palais, Richard Sheldon) 344, 345,
352, 354, 356, 357, 549
Париси (Parisi, Paul A.) 42, 77
Парк Куань-Чун (Park, Kwang-Chun) 392
Паркер (Parker, Michael Russell) 314
Паркс (Parks, Herman D.) 152, 165
Паскаль (Pascal, Blaise) 124
Паташник (Patashnik, Oren) 367, 369, 379
Патерсон (Paterson, Michael Stewart) 71-73
Пачиоли (Pacioli, Fra Luca) 53, 63, 76, 77, 291
Пейнтер (Painter, Charles) 348
Пенингтон (Penington, Keith S.) 466
Пеппинг (Pepping, Simon) 649
Перлис (Perlis, Alan Jay) 280, 644
Печт (Pecht, Geraldine) 580
Пикассо (Picasso, Pablo Ruiz y) 398, 414
Плантен (Plantin, Christophe = Plantijn, Christoffel) 144-146
Плэсс (Plass, Michael Frederick) 73, 79, 154, 165, 506
Пойа (Polya, Gyorgy = George) 36,
78
Пратт (Pratt, Vaughan Ronald) 468, 500,
657
Прингль (Pringle, Alison M.) 151,
165
Приор (Pryor, Roger Welton) 466, 468
Раглес (Ruggles, Lynn Elizabeth)
391
Райвест (Rivest, Ronald Linn) 585,
586
Раймон (Raymond, Francois H.) 148,
163
Райт (Wright, William) 184
Райхле (Raichle, Bernd) 658
Раман (Raman, Thiruvilwamalai Venkatraman) 587
Рамануджан (Ramanujan Iyengar, Srinivasa) 350
Рамшоу (Ramshaw, Lyle Harold) 73,
505
Ратц (Rahtz, Sebastian Patrick Quintus) 585, 649
Ренц (Renz, Peter Lewis) 345, 356
Ригер (Rieger, Georg Johann) 291,
292
Робертс (Roberts, Charles Sheldon) 453,
467
Роджерс (Rodgers, David) 257
Роже (Rogers, Bruce) 153, 165
Розар (Rosart, Jacques-Francois) 351, 635
Ройкрофт (Roycroft, Thomas) 145, 165
Рокицки (Rokicki, Tom) 257
Рондталер (Rondthaler, Edward)
318
Рубинштайн (Rubenstein, Arthur) 466, 468
Рыцько (Rycko, Marek) 635
Сайтс (Sites, Richard Lee) 583
Саме (Samet, Hanan) 151, 165
Саутолл (Southall, Richard Francis) 319,
333, 381, 391, 392, 613
Свонсон (Swanson, Ellen Esther) 77,
356
Седляк (Sedlak, Bedrich) 604
Синк (Cinque, Gregory M.) 466, 468
Сиснерос, де (Ximenes = Jimenez de
Cisneros, Francisco) 141, 164
Сифкес (Siefkes, Dirk) 73
Слимбах (Slimbach, Robert) 614
Смит, Джон Дэниэл (Smith, John
Daniel) 414
Смит, Камерон (Smith, Cameron)) 595
Смит, Леланд (Smith, Leland Clayton) 153
Сноу (Snow, Charles Percy) 322, 347
Софка (Sofka, Michael) 586
Спивак (Spivak, Michael David) 548, 616
Спрулл (Sproull, Robert Fletcher) 73
Спрэдженс (Spragens, Alan) 392
Стейнберг (Steinberg, Louis Ira) 445, 447, 467
Стил (Steele, Guy Lewis, Jr.) 645
Столлман (Stallman, Richard Matthew) 648
Стоун (Stone, Sumner Robert) 335, 342, 343
Сэмюэль (Samuel, Arthur Lee) 391
Тан (Thanh, Han The) 619
Таттл (Tuttle, Joey) 392
Тауберт (Taubert, Karl Heinz) 317
Тейлор, Джин (Taylor, Jean Ellen) 73
Тейлор, Филип (Taylor, Philip) 658
Тиле (Thiele, Christina Anna Louisa)
578, 602, 604, 625
Токусу (Tokusue, Nori) 392
Тори (Tory, Geofroy) 54, 77
Торниелло да Новара (Torniello da Novara, Francesco) 53, 54, 63, 76, 274-276, 278-280, 291, 292
Трабб Пардо (Trabb Pardo, Luis Isidoro) 104, 615
Трэйси (Tracy, Walter) 318
Тума (Tuma, Kathryn) 547
Туомбли (Twombly, Carol) 362, 614
Тьюринг (Turing, Alan Mathison) 587, 591, 618, 645
Уайлс (Wiles, Andrew John) 579
Уининг (Weening, Joseph Simon) 236, 250
Уинстон (Winston, Patrick Henry) 550, 562
Ульман (Ullman, Jeffrey David) 74, 546
Унгер (Unger, Gerard)) 318, 393, 400, 613, 649
Унгер (Unger, Marjan) 393, 400
Уолкер (Walker, Gordon Loftis) 73
Уолл (Wall, David Wayne) 542
Уорнок (Warnock, John Edward) 25, 610
Фейнман (Feynman, Richard Phillips) 606
Феличиано Веронезе (Feliciano Veronese, Felice) 53, 63, 75
Ферма, де (Fermat, Pierre de) 579, 584, 586, 587
Филман (Filman, Robert Elliot) 326, 550
Фистед (Thisted, Ronald Aaron) 366
Фишер, Майкл (Fischer, Michael John) 73
Фишер, мл., Эд (Fisher, Ed, Jr.) 318
Флайшман (Fleischman, Johann Michael) 635
Флойд (Floyd, Robert W.) 445, 447, 467
Фляйшер (Fleischer, Bruce) 392
Форд (Ford, David) 318
Форсайт (Forsythe, George Elmer) 298, 318
Фрамбах (Frambach, Erik Hubertus Martinus) 625
Франкел (Fraenkel, Abraham Adolf) 185
Франклин (Franklin, Benjamin) 572
Фрейзер (Fraser, Christopher Warwick) 647.
Фри (Frey, A.) 106, 163
Фукс (Fuchs, David Raymond) 391, 392,
544, 565, 631
Фэрбэрнз (Fairbairns, Robin) 596
Хаген (Hagen, Johannes) 628
Хансен (Hansen, Wilfred James) 249, 250
Хансон (Hanson, David Roy) 647
Хант (Hunt, Robert) 658
Хараламбус (Haralambous, Yannis) 258, 630
Хейн (Hein, Piet) 33, 303, 308
Хеннеси (Hennessy, John LeRoy) 583
Хенрике (Henrichs, Marshall) 372
Хершбергер (Hershberger, John Edward) 392
Хики (Hickey, Thomas Butler) 324, 343
Xoap (Hoare, Charles Antony Richard) 614, 615
Хобби (Hobby, John Douglas) 31, 32, 35, 78, 330, 343, 362, 392, 419, 472, 476, 565, 585, 633
Ходулёв Андрей Борисович 380
Хокинсон (Hawkinson, Lowell) 42
Холладей (Holladay, Thomas Melvin) 452, 467
Холмс (Holmes, Kris Ann) 333, 613
Хорн (Horn, Berthold Klaus Paul) 466, 658
Хорстман (Horstman, Helen) 194
Хофштадтер (Hofstadter, Douglas Richard) 314, 318
Хьюсон (Hewson, Rachel) 392
Хёниг (Hoenig, Alan) 206
Цапф (Zapf, Hermann) 52, 78, 314, 318, 333, 335, 344-346, 348-359, 361, 362, 368, 369, 371, 377, 380, 565, 612, 613, 624
Чайлдс (Childs, Selma Bart) 580, 592, 602
Черитон (Cheriton, David Ross) 390
Черри (Cherry, Lorinda Landgraf) 76, 500
Шамир (Shamir, Adi) 585
Шапплер (Schappler, John) 318
Шёпф (Schopf, Rainer Maria) 658
Шерхен (Scherchen, Hermann) 317
Шиллингер (Schillinger, Joseph) 317
Шойц (Scheutz, Edvard Georg Raphael) 559, 560
Шоу (Shaw, George Bernard) 139, 165
Шрёдер (Schroeder, Manfred Robert) 448, 468
Шрод (Schrod, Joachim) 647
Шупски (Schupsky, Maureen) 580
Эйкаут (Eijkhout, Victor Lambert) 644
Эйлер (Euler, Leonhard) 349, 351, 363, 367, 369
Эйнштейн (Einstein, Albert) 350
Энсхеде (Enschede, Charles) 635
Эриксон (Erichson, Knut) 368
Эшер (Escher, Maurits Cornelis) 627
Яо (Yao, Andrew Chi-Chih) 582
Ярвис (Jarvis, John Frederick) 445, 453, 467
Ясперт (Jaspert, W. Pincus) 318
Яцковский (Jackowski, Boguslaw) 635, 658
Оглавление
Предисловие редактора перевода 5
Предисловие 11
Ссылки на источники Ссылки на источники 1616
Ссылки на источники
Ссылки на источники
16
16
Глава1.Компьютерная типография 19
Источники иллюстраций 34
Глава2.Математическая типография 36
Аннотация 36
Введение 37
Предварительные примеры 38
Набор при помощи компьютера 43
Т^Х как язык описания ввода 45
Разбиение на строки 47
Антракт 49
«Третье поколение» наборного оборудования 50
Полу философские заметки 51
Математический дизайн шрифта 53
Определение новых кривых 55
Практическая аппроксимация 58
Приложение к шрифтовому дизайну 61
Семейства шрифтов 64
От непрерывности к дискретности 66
Иные подходы 70
Рандомизация 72
Заключение 73
Благодарности 73
Литература, не упомянутая в тексте 73
Литература 74
Добавление 78
Глава 3. Верстка абзацев 79
Введение 79
Формулировка задачи 82
Критерий желательности 89
Король-лягушонок, или Железный Генрих 98
Другие приложения 101
Состыковка абзацев 102
«Заплатки» 102
Висячая пунктуация 102
Как избежать «психологически плохих» разрывов 102
Строки с информацией об авторах 106
Рваный правый край 107
Центрированный текст 109
АЛГОЛо-подобные языки 110
Сложный указатель 113
Алгебраический подход 116
Введение в алгоритм 118
Дальнейшие подробности 125
Алгоритм 127
Вычислительный эксперимент 135
Исторический обзор 140
Проблемы и усовершенствования 155
Приложение: алгоритм расщепления на части 157
Благодарности 162
Литература 163
Добавление 165
Глава 4. Сочетание текстов, читаемых справа налево и слева направо 167
Термины и правила 168
Простейший случай 168
Тексты с чередованием 169
Один из путей реализации 170
Ликвидируем недочеты 171
L-шовинизм 171
Многоуровневое сочетание 173
Заключение 175
Приложение 175
Заключительные важные замечания 183
Литература 184
Примеры из типографской практики 184
Глава 5. Рецепты и дроби 187
Глава 6. Логотип системы Т^Х в различных шрифтах 192
Глава 7. Генерирование избранных страниц 194
Макрокоманды для генерирования избранных страниц 195
Глава 8. Макро, написанные для Джил 196
Глава 9. Задача на субботнее утро 205
Глава 10. Упражнения к книге ТеХ: The Program 207
Задачи 207
Ответы к задачам 216
Приложение 232
Благодарности 234
Глава 11. Мини-указатели для самодокумонтированных программ 235
Введение 235
Пример 237
Работа программы CTWILL 239
Обработка ТуСом 244
Заключительные замечания 249
Литература 250
Глава 12. Виртуальные шрифты. Развлечение для настоящих мастеров 256
Фрагменты файла VFtoVP.web 259
А здесь—фрагменты файла VPtоVF.web 261
Новости из издательства St. Anford Press 272
Глава 13. Буква S 273
Приложение 288
Литература 291
Дополнение 291
Глава 14. Мое первое знакомство с индийской письменностью 293
Дополнение 295
Глава 15. Концепция меташрифта 296
Благодарности и извинения 314
Аннотированный список литературы 315
Дополнение 318
Глава 16. Уроки METAFONT’a 321
Приложение 337
Добавление 342
Литература 343
Глава 17. AMS Euler—новый математический шрифт 344
Литература 367
Дополнение 368
Глава 18. Набор книги Concrete Mathematics , 369
Литература 379
Добавление 380
Глава 19. Из опыта преподавания программирования на METAFONT’e 381
Примечание переводчика 392
Глава 20. Меташрифт Punk 393
Литература 414
Добавление 414
Глава 21. Шрифты для дискретных полутонов 415
Приложение 1. Препроцессор-для изобразительного материала ... 433
Приложение 2. Пиксельная оптимизация 439
Литература 445
Глава 22. Оцифровка полутонов посредством диффузии точки 447
Диффузия ошибки 447
Упорядоченное размывание 448
Диффузия точки 448
Границы ошибки 450
Выделение краев 453
Примеры 453
Проблемы 458
Гладкая диффузия ошибки 460
Сравнение с другими методами 463
ь Благодарности 466
Литература 467
Глава 23.Заметки об оцифровке углов 469
Благодарности 475
Литература 476
Глава 24.TEXDR.AFT 477
Примечания переводчика 502
Глава 25.ТЕХ.ONE 506
Примечания переводчика 537
Глава 26.ТуХ’овские инкунабулы 539
Добавление 551
Глава 27.Пиктограммы для Т£Х’а и METAFONT’a 552
Литература 558
Добавление 558
Глава 28.Компьютеры и книгоиздание 559
Глава 29.Новые версии Т^Х’а и METAFONT’a 567
Набор символов 568
Таблицы переносов 569
Управление уровнем фрагментации слов при переносах 569
Большее разнообразие лигатур 570
Граничные лигатуры 571
Более компактные таблицы лигатур 572
Улучшение управления видом абзацев 572
Возможность проверить плохость 573
Возможность проверить номер строки 573
Возможность управления объемом контекста, выдаваемого в сообщениях об ошибках 573
Возможность повторного использования страниц 574
Исключения из совместимости снизувверх 574
Глава 30.Будущее Т^Х’а и METAFONT’a 576
Глава 31.Вопросы и ответы, I 578
Примечания переводчика 602
Глава 32.Вопросы иответы, II 604
Глава 33.Вопросы иответы, III 625
Глава 34.Последниеошибки ТуСа 651
Последняяошибка? 653
Не ошибки, но близко к этому 654
Просчеты проекта 656
Заключение 657
Литература 657
Добавление 658
Примечание переводчика 658
Предметный указатель 659
Научное издание
Дональд Эрвин КнутКОМПЬЮТЕРНАЯ ТИПОГРАФИЯ
Ведущий редактор И. А. МаховаяХудожник И. И. Куликова
Оригинал-макет подготовлен О. Г. Лапко в пакете Ту<с использованием семейства шрифтов Computer Modernс кириллическим расширением LH
Санитарно- эпидемиологическое заключение№ 77.99.02.953.Д.008286.12.02 от 09.12.2002 г.
Лицензия ЛР Я» 010174 от 20.05.97 г.
Подписано к печати с готовых диапозитивов 23.02.03.Формат 70 х 1001/1в. Печать офсетная. Объем 21,00 бум. л.Усл. печ, л. 54,60. Уч.-изд. л. 51,79. Изд. № 20/9724.Тираж 5 000 экз. (1-й завод 3 000 экз.). Заказ № 122.
Издательство «Мир»
Министерства РФ по делам печати,телерадиовещания и средств массовых коммуникаций107996, ГСП-6, Москва, 1-й Рижский пер., 2.
ООО «Издательство АСТ»
667000, Республика Тыва, г. Кызыл, ул. Кочетова, д. 28Наши электронные адреса:
www.ast.ru
E-mail: astpub@aha.ru
Диапозитивы изготовлены в издательстве «Мир»Отпечатано с готовых диапозитивов в
ОАО «Санкт-Петербургская типография № 6».191144, Санкт-Петербург, ул. Моисеенко, 10.
Телефон отдела маркетинга 271-35-42.
5. Из npWXPTVO^ МНЮ [Введение в математику, произносится бЯНбшэгИбМ э! оубМ], написанного Авраамом А. Франкелом, том 1 (Jerusalem: Hebrew
2 Novice Calligrapher’s Simple Devanagari. — Прим, перев.
2 Concrete (букв.) — бетонный. — Прим, перев.
3 Точнее, его модификацией с кириллическим расширением Cyrillic Concrete. Этот шрифт имеется в свободном доступе в архивах CTAN и на диске Т^Х Live (версия 6).—- Прим. ред.
1 Уайлс доказал, что уравнение Tn + i/n = zn не имеет целочисленных решений, если п > 2 и x-yz > 0.
1 См. гл. 25 данной книги.
2 TeX and META FONT: New Directions in Typesetting (Bedford, Massachusetts: Digital Press, 1979). *
2 AMS Euler—A new typeface for mathematics, Scholarly Publishing 20 (1989), 131-157; вошло в данное издание как глава 17.
3 См. гл. 1 данного издания.
1 «Optimal prepaging and font caching», ACM Transactions on Programming Languages and Systems 7 (1985), 62-79. См. также cc. 481-484 этой книги, где имеется первый набросок исходного кода T^X’a.
2 На самом деле, теперь, в plain.tex версии 3.14159 (март 1995 года), <и ►
действительно пренебрегают высотой и глубиной минуса.