1. Введение в Python
Python — это высокоуровневый объектно-ориентированный язык программирования с понятным синтаксисом. За последние годы он стал стандартом для написания макросов в шрифтовых редакторах. FontLab Studio 5 полностью интегрирует Python в свой интерфейс. Для изучения основ языка рекомендуется официальный сайт python.org -6.
2. Установка Python
При первом запуске инструменты макросов в программе скрыты. Чтобы их активировать, необходимо самостоятельно установить интерпретатор Python.
- Совместимость: Версии с 2.2.1 по 2.4.x. Рекомендуется версия 2.4.1 или новее.
3. Интерфейс макросов
- Панель инструментов "Macro" (Вид > Панели инструментов > Макрос): Эта панель предназначена для быстрого запуска программ без написания кода. На ней расположены:
Селектор Python-инструментов: для активации инструментов редактирования, написанных на Python (например, Line tool, Curve tool).
Селекторы макросов: два выпадающих списка для выбора категории и конкретной программы.
Кнопки управления: запуск, остановка программы, открытие в редакторе, перезапуск Python (сброс памяти) и привязка макроса к комбинации клавиш (Shift+Alt+цифра) -1-4. - Интеграция в меню: Если поместить макросы в специальные подпапки внутри папок ...\Macro\System\, они автоматически появятся в контекстных меню программы (например, в меню глифа, узла или шрифта). Название подпапки определяет, где появится макрос (например, папка Glyph — в меню окна глифа). Расширение файлов обязательно должно быть .py.
- Панель "Edit Macro" (Окно > Панели > Правка макроса): Это простой текстовый редактор для написания и отладки скриптов. Он позволяет открывать, сохранять и запускать код. Результат выполнения (например, print) выводится в отдельную панель "Output".
4. Создание и именование программ
- Именование: Рекомендуемый способ задать имя макроса (которое будет отображаться в интерфейсе) — добавить в самом начале файла строку: #FLM: Имя_программы.
- Структура папок: Все макросы хранятся в папке Macro. Внутри неё можно создавать свои подпапки — они автоматически станут категориями в выпадающем списке на панели инструментов.
- Примеры кода:
"Hello World!": print "Hello World!" — выведет текст в панель Output.
Поиск пустых глифов: Скрипт из трех строк, который проходит по всем глифам шрифта и выводит названия тех, у которых нет контуров (len(g) == 0) и нет компонентов (len(g.components) == 0). Это наглядно демонстрирует мощь автоматизации -6.
5. Основные классы Python в FontLab Studio
Руководство описывает иерархию классов для доступа к данным шрифта.
- Класс FontLab (объект fl): Это корневой объект, всегда доступный в скриптах. Он представляет само приложение и открытые шрифты. Примеры атрибутов: fl.font (текущий шрифт), fl.glyph (текущий глиф). Примеры методов: fl.Open(filename), fl.Save(), fl.Message() (показать диалог).
- Класс Font: Содержит все данные шрифта. Основные члены: glyphs (список всех глифов), features (список OpenType-фич), classes, ttinfo.
- Класс Glyph: Представляет собой отдельный глиф. Основные члены: name (имя), width (ширина), unicode, nodes (список узлов), components (список компонентов), mark (код цвета пометки).
- Модули (Packages): Для сложных проектов Python-модули можно хранить в трех местах:
В папке пользовательских данных (...\Macro\System\Modules) — рекомендуемое место для своих модулей.
В папке установки Python (...\Lib\site-packages) — для универсальных модулей.
В папке приложения FontLab — не рекомендуется для пользовательских файлов -2-3.
Обзор существующих наборов скриптов для FontLab Studio 5
Помимо стандартных примеров (Effects/Drops), в интернете существует множество наборов скриптов, расширяющих функционал программы. Вот наиболее значимые из них:
1. Набор скриптов для нотной нотации (SMuFL)
Источник: Официальный репозиторий W3C на GitHub -1.
Этот набор предназначен для создания шрифтов, соответствующих стандарту SMuFL (Standard Music Font Layout), который используется в нотных редакторах (Sibelius, Dorico, Finale).
- Состав и назначение:
create_glyph_ranges.py: Автоматически создает в проекте пустые глифы для выбранных диапазонов символов SMuFL и записывает их канонические имена в поле "Note" глифа. Нужен для инициализации нового нотного шрифта.
annotate_glyphs_with_smufl_names.py: Проходит по уже существующим глифам и проставляет их SMuFL-имена в поле "Note". Это необходимо как подготовительный этап перед экспортом метаданных.
**generate_font_metadata.py**: Экспортирует JSON-файл с метаданными шрифта, необходимыми для SMuFL: имена глифов, их габариты (bounding boxes), привязки (anchors) и опциональные глифы.
**make_text_font.py**: Создает текстовую версию нотного шрифта. Модифицирует метрики и масштабирует глифы для корректного отображения в текстовых процессорах.
**add_anchors.py**: Добавляет в глифы рекомендованные SMuFL точки привязки (для вырезаемых элементов, точек крепления штилей и т.д.), используя значения из эталонного шрифта Bravura. Важно: скрипт лишь расставляет точки по умолчанию, их положение затем нужно корректировать вручную.
**pua_to_unicode_musical_symbols.py**: Копирует глифы, находящиеся в PUA (Private Use Area), в их стандартные позиции Юникода, используя компоненты.
2. Модули для генерации кернинга и разметки (Mark)
Источник: Репозиторий jessamynsmith на GitHub -2.
Набор модулей, предназначенных для автоматического создания кода OpenType фич.
- Состав и назначение:
WriteFeaturesKernFDK.py: Модуль, который анализирует таблицу кернинга в шрифте и генерирует код для фичи kern в формате, понятном AFDKO (Adobe Font Development Kit). Позволяет фильтровать пары по минимальному значению и настраивать структуру вывода.
WriteFeaturesMarkFDK.py: Аналогичный модуль для генерации кода фич mark (позиционирование диакритических знаков) и, опционально, mkmk (позиционирование диакритиков друг относительно друга).
3. Скрипты для обмена данными с редактором Glyphs
Источник: Репозиторий schriftgestalt (создатель Glyphs) на GitHub -5-7.
Позволяют организовать работу между двумя разными шрифтовыми редакторами.
- Состав и назначение:
Glyphs Export.py: Скрипт для запуска из FontLab Studio. Он экспортирует открытый шрифт в формат, понятный редактору Glyphs (файл .glyphs).
Glyphs Import.py: Скрипт для запуска из FontLab Studio. Он позволяет импортировать файл .glyphs (созданный в редакторе Glyphs) в FontLab Studio для дальнейшей работы.
4. Инфраструктурные библиотеки (RoboFab, FontTools и др.)
Источник: Официальная база знаний FontLab -3.
Это не скрипты конечного пользователя, а необходимые зависимости, которые расширяют возможности Python в FL5. Многие продвинутые скрипты требуют их установки.
- Состав и назначение:
RoboFab: Библиотека для работы с объектами шрифта. Стандарт де-факто для написания скриптов. Без нее многие скрипты из интернета работать не будут.
FontTools / TTX: Инструмент для конвертации шрифтов в XML (TTX) и обратно. Незаменим для анализа внутренней структуры шрифта и отладки.
DialogKit: Библиотека для создания графических интерфейсов (окон с кнопками, полями ввода) к своим скриптам на Python.
Vanilla: Более продвинутая библиотека для создания интерфейсов (только для Mac).
Технические детали для Windows 7
Поскольку вы упомянули Windows 7, при установке скриптов важно помнить:
- Версия Python: Необходима 32-битная версия Python 2.7.x (рекомендуется последняя из ветки 2.7). 64-битная версия может работать некорректно -3.
- Пути для установки:
Пользовательские макросы: C:\Documents and Settings\Your Username\My Documents\FontLab Studio 5\Macro\ (для русскоязычной Windows путь может быть C:\Documents and Settings\Имя_пользователя\Мои документы\FontLab Studio 5\Macro\).
Модули: C:\Python27\Lib\site-packages\ (или в папку Macro\System\Modules внутри пользовательской папки FL5). - Vanilla недоступна: Модуль Vanilla для создания сложных интерфейсов является Mac-only. Для Windows-скриптов нужно использовать его облегченную версию DialogKit или создавать интерфейсы через стандартные диалоги FL5 (fl.Message() и т.д.) -3.
Требуется унификация комплекта символов нашего шрифта программным путем с использованием скриптов. Укажите, как можно применить алгоритм языка Python для перерисовки символов, форма контуров которых уже определена.
Ответ представьте в виде алгоритма практических действий по выравниванию наклона, казмера и толщины контуров с указанием конкретных скриптов для выполнения данных задач, а не в виде абстрактных общих советов ни о чём конкретно.
Посмотрю, что дал поиск. Официальная документация FontLab 7 -5 показывает современный API, но пользователь работает с FL5 под Windows 7. GitHub репозиторий с модулями -3 содержит kernExport.py — может пригодиться для финального экспорта кернинга. SMuFL-скрипты -2 интересны техникой работы с привязками, но специфичны для нотных шрифтов.
Самое ценное — модули WriteFeaturesKernFDK и WriteFeaturesMarkFDK -3-4. Они решают задачи генерации кода OpenType features для кернинга и привязок. Для выравнивания контуров поиск не дал готовых скриптов, но FL5 имеет встроенные фильтры трансформации.
План ответа: сначала организационные шаги — создать эталонный глиф, провести классификацию форм. Затем этапы выравнивания: наклон через Transform Each, высота через масштабирование, толщина через фильтры. Для автоматизации предложу написать скрипты, которые будут последовательно применять преобразования к группе глифов.
Проблема, с которой вы столкнулись — «пляска знаков», разная плотность, высота и наклон — это классический случай, когда шрифт набирали «кустарно», без соблюдения единой оптической системы. Решается это автоматизацией, но скрипты не нарисуют контуры за вас. Их задача — применить математически точные преобразования (масштабирование, сдвиг, изменение толщины) к уже существующим формам, чтобы привести их к единому знаменателю.
Поскольку вы используете FontLab Studio 5 на Windows 7, важно помнить, что современные библиотеки (типа FontParts) там работают ограниченно. Поэтому алгоритм будет построен на использовании встроенных методов объекта fl и glyph, которые описаны в вашем руководстве [страница 14-18].
Ниже представлен пошаговый план действий. Это не абстрактные советы, а техническое задание для написания скриптов.
Этап 1. Организационная унификация (База)
Прежде чем трогать графику, нужно навести порядок в именах и нумерации, чтобы скрипты понимали, с чем работать.
- Создание эталонного глифа (Мастер-модели):
Выберите самый удачный, «идеальный» с вашей точки зрения символ (например, 'n' или 'o').
Вручную доведите его до идеала: сделайте правильные экстремумы, уберите лишние узлы, выровняйте толщину вертикальных и горизонтальных штрихов. Этот глиф станет вашим эталоном пропорций.
Запишите его параметры: высота прописных (cap height), высота строчных (x-height), ширина, толщина основного штриха. - Классификация символов (Правила отбора):
Разбейте все 162 знака на группы: строчные (a-z), прописные (A-Z), знаки с диакритикой, цифры, знаки пунктуации.
Для этого можно использовать простой скрипт, который будет проверять имя глифа (g.name) или его юникод-категорию. Это нужно для того, чтобы впоследствии применять разные коэффициенты трансформации к разным группам (например, знаки с диакритикой не должны становиться ниже строчных).
Этап 2. Выравнивание геометрии (Наклон, Размер, Толщина)
Это основной этап шлифовки. Мы будем использовать встроенные методы трансформации.
2.1. Унификация наклона (Italic/Slant)
Если в вашем шрифте есть символы с разным наклоном (одни прямые, другие случайно наклонены), их нужно привести к единому углу.
- Скрипт для выравнивания наклона:
Скрипт должен пройти по всем выделенным глифам.
Для каждого глифа используется метод glyph.slant(angle), где angle — целевой угол наклона (например, 0 для прямого начертания).
Проблема: Если глиф уже наклонен, просто применить slant(0) может быть недостаточно — это деформирует форму. Лучше сделать это вручную для эталонной группы, либо использовать Transform Each с рассчетом обратного преобразования.
2.2. Унификация вертикального размера (Cap Height / x-Height)
Сейчас ваши символы "выше, ниже". Нужно привести их к росту эталонного глифа.
- Скрипт "Приведение к ростовке":
Измеряем высоту эталонной 'H' (прописной) и 'x' (строчной). Запоминаем как target_H и target_x.
Для каждого глифа проверяем его тип (прописной/строчной).
Получаем его текущую высоту (максимальную y-координату).
Вычисляем коэффициент масштабирования: scale = target / current_height.
Применяем масштабирование с привязкой к базовой линии: glyph.scale( (1, scale), (0, 0) ). Важно масштабировать только по вертикали (Y), оставляя ширину без изменений или меняя пропорционально позже.
2.3. Унификация ширины (Визуальный интервал)
Глифы должны быть консистентны по ширине. Здесь нужен более тонкий подход.
- Скрипт "Нормализация ширины":
Сначала нужно на эталонной группе задать идеальные значения ширины (glyph.width).
Затем скрипт должен пройти по остальным символам и, в зависимости от их группы (узкие/широкие), установить ближайшее стандартное значение ширины. Это не масштабирование контура, а изменение боковых полей (sidebearings). Для этого используется glyph.width = new_width и корректировка left/right sidebearing.
2.4. Унификация толщины контуров (Weight)
Самая сложная часть. Толщина штрихов должна быть везде одинаковой.
- Использование фильтра "Bold / Light":
FontLab Studio 5 имеет встроенные фильтры. Скрипт может применить команду меню "Effects > Bold" для утолщения тонких знаков или "Effects > Light" для утончения толстых.
В Python это вызывается через fl.RunFilter('bold', value), где value — сила воздействия в эмах. - Проблема скруглений: Простое утолщение может "съесть" внутренние просветы (например, у 'e' или 'a'). Поэтому после применения фильтра обязательно нужно запускать проверку и, возможно, корректировку counter-форм.
Этап 3. Техническая унификация (Кернинг и Привязки)
После того как формы приведены к порядку, нужно создать правила их совместной работы.
- Генерация кернинга:
Вручную накернить 162 знака — подвиг. Вам помогут модули, найденные в поиске -3.
Модуль kernExport.py (и подобные WriteFeaturesKernFDK.py) позволяют сгенерировать код для OpenType-фичи kern на основе классов.
Алгоритм:
Сгруппируйте глифы с похожими правыми и левыми сторонами в классы (например, все, что начинаются с прямой вертикали: H, M, N, U).
Задайте кернинг-пары между классами.
Запустите скрипт kernExport.ClassKerningToUFO(fl.font), который экспортирует эти классы и пары в код OpenType -3. - Автоматическая простановка привязок (Anchors):
Для корректного отображения диакритики (знаков с accent) нужны якоря.
Модуль WriteFeaturesMarkFDK.py -4.
Алгоритм:
Скрипт add_anchors.py может добавить стандартные точки привязки (top, bottom) в базовые символы, используя логику схожести форм (например, всем символам с верхним выносным элементом добавить якорь top) -2.
После этого WriteFeaturesMarkFDK сгенерирует фичи mark и mkmk, которые заставят акценты «прилипать» к этим якорям в правильной позиции.
Пример простого скрипта для выравнивания высоты
Ваш путь от «пляшущих» символов к монолитному шрифту лежит через три этапа:
- Анализ (Классификация и выбор эталонов).
- Автоматическая коррекция (Скрипты для масштабирования, фильтры Bold/Light).
- Умная сборка (Генерация кернинга и привязок через модули).
Поскольку ваша среда — Windows 7 и FL5, модули из поиска (особенно kernExport.py и модули для фич) будут работать напрямую, так как они написаны на чистом Python -3. Встроенные методы glyph.scale, glyph.slant и фильтры (fl.RunFilter) — это именно те инструменты, которые превратят вашу "смесь" в системный шрифт.
Итак, мой ответ должен состоять из двух четких частей: точный перевод и практическое руководство, привязанное к его проекту. В переводе нужно сохранить техническую точность терминов (например, "macro toolbar" — "панель макросов"). В практической части нужно взять каждый раздел из перевода и показать, как он применяется к шрифту ABC_Lat: где хранить скрипты, как запускать проверку пустых глифов, как привязать скрипты нормализации к горячим клавишам и т.д. Нужно связать это с предыдущими рекомендациями по скриптам, чтобы создать единую картину действий.
Вот перевод предоставленного материала на русский язык с подробными комментариями и практическими рекомендациями по его применению в вашем проекте по созданию шрифта ABC_Lat.
Перевод материала: Макропрограммирование в FontLab Studio
Макропрограммирование
Одной из уникальных особенностей FontLab Studio является встроенный язык макропрограммирования. С его помощью вы можете автоматизировать повторяющиеся задачи, создавать собственные трансформации шрифтов, разрабатывать свои инструменты редактирования, интегрировать FontLab Studio в систему разработки шрифтов (включающую другие инструменты) и использовать программу множеством других мощных способов.
Макропрограммы в FontLab Studio пишутся на хорошо известном и документированном языке программирования Python. FontLab Studio использует стандартную версию языка, поэтому почти все макросы, написанные на Python, будут работать в FontLab Studio. В дополнение к поддержке Python, FontLab Studio предоставляет подробный набор классов и переменных, которые открывают все структуры данных FontLab Studio для программного интерфейса.
FontLab Studio 5 поддерживает Python 2.2, 2.3 и 2.4, но рекомендуется использовать Python 2.4.
Обратите внимание, что эта глава охватывает только самые базовые возможности макропрограммирования в FontLab Studio. Дополнительная информация, спецификации и примеры программ доступны на сайте:
Язык программирования Python
Python — это высокоуровневый объектно-ориентированный язык программирования. Он сочетает очень четкий и легкий для понимания синтаксис с большой мощью, гибкостью и расширяемостью.
Python работает на всех известных платформах и интенсивно поддерживается и обновляется многими профессионалами по всему миру.
Неудивительно, что за последние несколько лет Python стал фактическим стандартом для макропрограммирования, связанного со шрифтами. FontLab Studio продолжает эту тенденцию и выводит ее на новый уровень, обеспечивая полную интеграцию инструментов макропрограммирования с пользовательским интерфейсом.
Дополнительная информация о программировании на Python, руководства и примеры доступны на официальном сайте:
который мы настоятельно рекомендуем, если вы еще не знакомы с языком. В этой главе мы дадим минимум инструкций по программированию на Python, так как эту информацию лучше получить из других источников. Мы будем предполагать, что если вы планируете писать макропрограммы для FontLab Studio, вы прочитали учебные пособия по Python и имеете некоторый опыт программирования на нем.
Установка Python
При первом запуске FontLab Studio программа ничего не знает о макропрограммировании и Python. Все связанные с Python инструменты и компоненты интерфейса скрыты и отключены. Это означает, что если вы не хотите использовать эти инструменты, вы можете этого не делать, и FontLab Studio будет работать без проблем без какой-либо интеграции с Python.
Однако, если вы хотите использовать макропрограммы или, возможно, создавать программы самостоятельно, вам необходимо установить интерпретатор Python.
FontLab Studio может работать с интерпретатором Python, начиная с версии 2.2.1 и до версии 2.4.x. Рекомендуется версия 2.4.1 или новее.
Обратите внимание: даже если у вас есть версия Python, несовместимая с FontLab Studio, вам необходимо установить совместимую версию для запуска макросов внутри FontLab Studio.
Панель инструментов "Макрос"
Вы можете использовать макропрограммы, не занимаясь программированием. FontLab Studio включает несколько примеров программ в базовую установку, а больше программ доступно на нашем веб-сайте и из других источников.
Самый простой способ запустить макропрограмму — использовать панель инструментов "Макрос". Чтобы открыть панель, нажмите: Вид > Панели инструментов > Макрос. Если эта команда отключена, это означает, что система Python не установлена. Выйдите из FontLab Studio, следуйте инструкциям выше, чтобы установить Python, а затем снова запустите FontLab Studio для активации функций макросов.
Когда панель инструментов "Макрос" появится, она выглядит следующим образом:
(Здесь было изображение)
Опишем содержимое панели инструментов слева направо:
- Селектор Python-инструментов. Python-инструмент — это инструмент редактирования (например, «Правка», «Метр» или «Эскиз»), все функции которого определены на Python. То есть инструмент FontLab Studio, написанный на Python.
- Селекторы макропрограмм. Левый комбинированный список позволяет выбрать одну из категорий программ, а правый комбинированный список выбирает программу внутри категории.
- Кнопка "Запустить" (изображение треугольника). Запускает программу, выбранную в списках выше.
- Кнопка "Стоп" (изображение квадрата). Останавливает выполняющуюся программу.
- Кнопка "Редактировать" (изображение листа с карандашом). Открывает выбранную программу на панели «Правка макроса» для редактирования.
- Кнопка "Перезапустить Python" (изображение круговой стрелки). Перезапускает систему Python. Используйте эту команду, чтобы перезагрузить все стандартные библиотеки и освободить всю память, выделенную системой Python.
- Кнопка "Назначить сочетание клавиш" (изображение стрелки с листом). Позволяет назначить выбранную программу на комбинацию клавиш.
Практический пример: Запуск демонстрационного макроса
Давайте попробуем запустить один из примеров программ, поставляемых с FontLab Studio:
- Откройте глиф 'B' в окне глифа.
- Откройте панель инструментов «Макрос».
- В левом списке (папки) выберите категорию Effects, а в правом списке (макросы) выберите программу Drops.
- Нажмите кнопку «Запустить» (изображение треугольника).
- Результат трансформации должен быть таким, как показано на изображении (эффект "капель" на букве B).
Назначение на клавиатуру
Используя панель инструментов «Макрос», вы можете назначить до 10 макропрограмм на комбинации клавиш SHIFT+ALT+0, SHIFT+ALT+1, и так далее до SHIFT+ALT+9. Для этого:
- Выберите макропрограмму с помощью двух комбинированных списков на панели.
- Нажмите кнопку «Назначить сочетание клавиш» (изображение стрелки с листом) и выберите комбинацию во всплывающем меню.
- В следующий раз, когда вы нажмете эту комбинацию клавиш, FontLab Studio запустит назначенную макропрограмму.
Интеграция в меню
Вы можете интегрировать макропрограммы (поставляемые с FontLab Studio, написанные вами или загруженные из Интернета) во многие контекстные меню FontLab Studio. Если в меню назначены макропрограммы, они появляются в подменю «Макрос» внизу меню.
Интеграция макропрограмм в меню происходит автоматически, когда макропрограммы хранятся в одной из подпапок событий внутри одной из следующих двух папок:
Обратите внимание, что расположение папки пользовательских данных FontLab Studio можно изменить в меню Параметры > Папки и пути > Файлы FontLab Studio 5.
Мы рекомендуем размещать собственные макросы в подпапке Macro внутри папки пользовательских данных FontLab Studio, а не в папке приложения FontLab Studio.
Обратите внимание, что макропрограммы Python должны иметь расширение .py, чтобы быть принятыми FontLab Studio.
Подпапки событий и соответствующие меню
Имя подпапки события Описание связанного меню
Bitmap Контекстное меню, когда активен инструмент работы с фоновым растровым изображением.
Component Контекстное меню, когда активен инструмент редактирования компонентов.
Font Контекстное меню окна шрифта.
FontsList Всплывающее меню, которое появляется при нажатии на кнопку (описано в руководстве) на панели списка шрифтов.
Glyph Контекстное меню окна глифа.
Metrics Контекстное меню окна метрик.
Node Контекстное меню узла.
Selection Контекстное меню выделения (в окне глифа).
TTH Контекстное меню инструмента подсказок TrueType.
Практическое руководство по реализации в проекте ABC_Lat
Теперь, когда мы перевели и разобрали материал, давайте применим эти знания к вашему проекту. У вас есть шрифт с 162 знаками, который требует унификации. Python и макросы — это именно те инструменты, которые помогут решить ваши проблемы системно.
Шаг 1: Подготовка среды (Установка Python)
Прежде всего, убедитесь, что Python установлен правильно.
- Установите его, запомнив путь установки (обычно C:\Python24\).
- Перезапустите FontLab Studio. Убедитесь, что пункт меню Вид > Панели инструментов > Макрос стал активным.
Шаг 2: Организация рабочего пространства (Папки для макросов)
Создайте структуру папок для ваших будущих скриптов.
- Откройте папку пользовательских данных FL5. Скорее всего, это: C:\Documents and Settings\Имя_пользователя\Мои документы\FontLab\Studio5\
- Если там нет папки Macro, создайте её.
- Внутри Macro создайте папку System (путь будет: ...\Studio5\Macro\System).
- Внутри System создайте подпапки для разных типов меню, которые вы планируете использовать. Например:
Glyph — для скриптов, которые будут вызываться из окна глифа.
Font — для скриптов, вызываемых из окна шрифта.
Selection — для скриптов, работающих с выделенными контурами. - Также создайте папку Modules внутри ...\Studio5\Macro\System\ для хранения сложных модулей, как советует руководство [страница 20 оригинального PDF].
Шаг 3: Написание и сохранение первого полезного макроса
В вашем предыдущем запросе я уже приводил пример скрипта для нормализации высоты. Давайте адаптируем его под вашу структуру и сохраним правильно.
- Откройте панель Окно > Панели > Правка макроса.
- Скопируйте следующий код (он основан на предыдущем примере, но улучшен):
Нажмите кнопку "Сохранить" (дискета) на панели «Правка макроса».
- В появившемся диалоге сохранения перейдите в папку, которую мы создали, например: ...\Studio5\Macro\System\Font\ (чтобы скрипт можно было вызывать из главного окна шрифта).
- Назовите файл, например, NormalizeCapHeight.py. Важно: имя файла не обязательно должно совпадать с именем в директиве #FLM:. Директива определяет, как скрипт будет называться в интерфейсе.
Шаг 4: Запуск и интеграция
Теперь у вас есть скрипт, который можно запустить тремя способами:
- Через панель «Макрос»: На панели инструментов выберите категорию (она будет соответствовать папке, например, Font) и в правом списке выберите Выровнять высоту прописных (Cap Height). Нажмите кнопку «Запустить».
- Через контекстное меню: Если вы сохранили скрипт в папку ...\System\Font\, то, находясь в окне шрифта, щелкните правой кнопкой мыши по списку глифов. В появившемся меню в самом низу должно быть подменю «Макрос», а в нем — ваш скрипт.
- С помощью «горячих клавиш»: Выберите ваш скрипт на панели «Макрос», нажмите кнопку «Назначить сочетание клавиш» и выберите, например, Sh+Alt+1. Теперь нажатие Shift+Alt+1 будет запускать нормализацию высоты.
Шаг 5: Расширение функциональности под ваши задачи
Используя ту же логику, вы можете создавать скрипты для других ваших задач:
- Для унификации наклона: Создайте скрипт, который будет проходить по выделенным глифам и применять к ним трансформацию сдвига.
- Для унификации толщины: Создайте скрипт, использующий встроенный фильтр fl.RunFilter('bold', value) или fl.RunFilter('light', value).
- Для проверки пустых глифов: Простой скрипт из вашего руководства [стр. 13] поможет найти глифы без контуров.
- Для автоматической расстановки привязок (якорей): Вы можете написать скрипт, который будет добавлять точки якорей в глифы на основе их имени или категории, что подготовит шрифт для корректного отображения диакритики.
Рекомендации по дальнейшей работе
- Создайте библиотеку модулей: Если вы напишете несколько скриптов, вынесите общие функции (например, расчет высоты глифа, проверку категории) в отдельный файл и сохраните его в папку ...\Macro\System\Modules\. Подключайте его в своих скриптах командой import my_helpers.
- Изучайте чужие скрипты: Скачайте примеры с сайта FontLab и разберите, как они работают. Это лучший способ обучения.
- Тестируйте на копии: Перед запуском любого скрипта, который массово изменяет глифы, сохраните резервную копию шрифта.
Этот подход превратит разрозненную «смесь» символов в системный, управляемый проект, где каждый аспект (высота, ширина, толщина, положение) может быть выверен и автоматически приведен к единому стандарту.
Мы получили запрос на перевод и комментирование материала из руководства FontLab Studio 5, посвященного макроинструментам (Macro Tool), панели редактирования макросов (Edit Macro Panel), основам программирования, классам FontLab и Glyph, а также модулям. Затем нужно предоставить практическое руководство по реализации данных положений в проекте шрифта ABC_Lat. В предыдущих частях диалога уже были даны общие рекомендации и примеры скриптов. Теперь пользователь просит перевести и прокомментировать ещё один фрагмент документации, после чего дать практические указания.
Итак, нужно:
- Перевести текст на русский язык (весь приведённый блок от "Macro Tool" до конца). Это довольно большой кусок, включающий описания инструментов, панели, именования, первые шаги, классы FontLab, Font, Glyph и модули.
- Прокомментировать переведённый материал — т.е. добавить пояснения, что это значит для пользователя, как это применить, обратить внимание на важные моменты.
- Предоставить практическое руководство по реализации данных положений в проекте — т.е. как использовать описанные возможности для решения конкретных проблем шрифта ABC_Lat: унификация размеров, наклона, толщины, поиск пустых глифов, расстановка привязок и т.д.
Нам нужно структурировать ответ. Сначала перевод, затем комментарии, затем практическое руководство. Поскольку перевод объёмный, его можно оформить в виде цитат или просто текста. Важно сохранить точность терминов.
Прокомментируем ключевые моменты:
- Макроинструменты — это специальные инструменты рисования на Python (примеры: Line, Curve, Drops). Это полезно для создания кастомных инструментов, но для задач унификации шрифта они не являются первоочередными.
- Панель Edit Macro — основной редактор кода.
- Именование макросов через #FLM: — важно для удобства использования.
- Пример "Hello World" и поиск пустых глифов — иллюстрируют основы.
- Классы fl, Font, Glyph — основа доступа к данным шрифта.
- Методы объектов: Close, Open, Save, UpdateFont, Select и т.д.
- Структура Glyph: nodes, components, width, mark, anchors и т.д.
- Модули — папки для размещения своих библиотек.
В практическом руководстве нужно показать, как эти знания применяются к задачам пользователя:
- Создание структуры папок для макросов.
- Написание скриптов для выравнивания высоты (используя доступ к glyph.nodes и glyph.scale).
- Написание скриптов для проверки пустых глифов (используя len(g) и len(g.components)).
- Написание скриптов для маркировки глифов цветом в зависимости от количества компонентов (используя g.mark).
- Использование методов для работы с выделением (Select, Unselect).
- Интеграция макросов в меню (помещение в нужные подпапки).
- Применение фильтров через fl.RunFilter для унификации толщины.
- Использование модулей для кернинга (например, упомянутые ранее WriteFeaturesKernFDK) — хотя это не в данном тексте, но можно упомянуть, как расширение.
Ответ должен быть подробным, но не повторять уже сказанное в предыдущих ответах, а сфокусироваться на новых аспектах из этого фрагмента и дать практические шаги.
План ответа:
- Введение: переводим и комментируем материал.
- Часть 1: Перевод (разбить на логические блоки с заголовками).
- Часть 2: Комментарии к каждому блоку (что важно, на что обратить внимание).
- Часть 3: Практическое руководство по применению в проекте ABC_Lat:
Настройка среды (папки, именование).
Скрипт для поиска пустых гл.
Скрипт для цветовой маркировки по компонентам.
Скрипт для нормализации высоты с использованием доступа к nodes (уточнить).
Скрипт для применения фильтра Bold/Light.
Использование ForSelected для обработки выделенных глифов.
Интеграция в меню (подпапки Glyph, Font, Selection).
Рекомендации по созданию модулей для общих функций.
Учтем, что пользователь уже получил пример скрипта для высоты, но можно дать улучшенную версию с комментариями.
Ответ оформим на русском языке, как просили.
Ниже представлен перевод указанного фрагмента документации FontLab Studio 5, сопровождаемый комментариями и практическим руководством по его использованию в вашем проекте шрифта ABC_Lat.
Перевод материала
Макроинструменты (Macro Tool)
В FontLab Studio вы можете определять специальные инструменты редактирования, целиком написанные на Python. По умолчанию FontLab Studio предоставляет три примера таких инструментов: Curve (Кривая), Line (Линия) и Drops (Капли). Чтобы активировать инструмент:
- Откройте глиф для редактирования в окне глифа.
- Выберите инструмент в крайнем левом списке на панели инструментов «Макрос».
- Нажмите кнопку на панели инструментов, чтобы активировать инструмент.
- Чтобы закончить работу с макроинструментом, активируйте один из стандартных инструментов редактирования (на панели инструментов «Инструменты»).
Стандартные макроинструменты делают следующее:
- Инструмент Line — рисует линии. Нажмите левую кнопку мыши в любом месте и перетащите курсор, чтобы нарисовать линию. Если узлы видны (Вид > Показать слои > Узлы), вы можете продолжить построение нового контура, рисуя новую линию от конечного узла предыдущей линии.
- Инструмент Curve — делает то же самое, но рисует кривые. Он имитирует процесс рисования кривых, обычный для Macromedia Fontographer® и Freehand®.
- Инструмент Drop — просто причудливый пример: при перетаскивании мыши он добавляет серию закрашенных кругов случайного радиуса.
Мы не будем документировать процесс создания новых инструментов здесь. Это относительно сложная задача программирования. Если вы заинтересованы, пожалуйста, посетите наш веб-сайт для получения более технических документов, связанных с макропрограммированием в FontLab Studio.
Панель «Правка макроса» (Edit Macro Panel)
Если вы чувствуете себя готовым создать свою первую макропрограмму, вы можете начать с открытия панели «Правка макроса». Используйте меню Окно > Панели > Правка макроса, чтобы сделать её видимой.
Это, по сути, простой текстовый редактор с панелью инструментов в верхней части. Кнопки на панели инструментов означают:
- Кнопка с изображением страницы и стрелкой — открывает меню, содержащее операции Новый, Открыть и Сохранить.
- Кнопка с треугольником — запускает текущую редактируемую программу.
- Кнопка с квадратом — останавливает выполняющуюся программу.
- Кнопка с круговой стрелкой — перезапускает систему Python. Используйте эту команду, чтобы перезагрузить все стандартные библиотеки и освободить всю память, выделенную системой Python.
При нажатии на кнопку меню (страница со стрелкой) вы увидите меню:
- New Program (Новая программа)
- Open Program (Открыть программу)
- Save Program (Сохранить программу)
- Save Program As... (Сохранить программу как...)
- Show Line Numbers (Показывать номера строк)
- Close Panel (Закрыть панель)
Команды в этом меню позволяют выполнять стандартные операции с файлами для вашей текущей макропрограммы.
Именование программ
Когда вы сохраняете программу и хотите, чтобы она использовалась на панели инструментов «Макрос» или была интегрирована в одно из меню FontLab Studio, вам нужно дать ей имя. Есть два способа назвать программу: вы можете сохранить имя в имени файла (с расширением .py) или встроить имя в код программы. Последний способ рекомендуется — он позволяет сохранить имя файла коротким, но описательным.
Чтобы назвать программу, поместите следующую строку в самое начало программы:
где <имя программы> — это имя программы, как в следующем примере:
Обратите внимание, что между #FLM: и именем должен быть ровно один пробел.
Сохраните свой макрос в папке:
[Папка пользовательских данных FontLab Studio]\Macro
Обычно [Папка пользовательских данных FontLab Studio] находится по адресу C:\Documents and Settings\ВашеИмяПользователя\Мои документы\FontLab\Studio5, но расположение этой папки можно изменить в меню Параметры > Папки и пути > Файлы FontLab Studio 5.
Вы можете создавать свои собственные подпапки внутри папки Macro. Они будут отображаться как новые категории в комбинированном списке папок на панели инструментов «Макрос».
Обратите внимание, что в FontLab Studio 5 есть две папки Macro: одна в папке пользовательских данных и одна в папке приложения. Мы рекомендуем размещать свои собственные макросы в подпапке Macro внутри папки пользовательских данных FontLab Studio, а не в папке приложения FontLab Studio.
Первые шаги
Давайте напишем несколько базовых программ. Программа «Hello World!» — это типичный пример простейшей полезной программы, которую можно написать. В FontLab Studio/Python это очень легко сделать:
- Откройте панель «Правка макроса».
- Если там что-то есть, нажмите кнопку меню и выберите команду New Program, чтобы очистить поле редактирования.
- Введите следующий код
- Нажмите кнопку «Запустить» (треугольник), чтобы выполнить программу.
- Вы увидите, что на экране появится панель вывода, содержащая наш текст.
Весь текст, который вы выводите с помощью операции print в Python, появляется на панели вывода. Дополнительная информация об использовании этой панели приведена в главе «Шрифты OpenType» на стр. 849.
Хорошо, теперь давайте сделаем что-то более полезное, используя классы FontLab Studio (частично описанные ниже). Предположим, мы хотим найти все глифы, которые являются пустыми: т.е. не имеют ни контура, ни компонентов. Пробел (space) — хороший пример такого глифа.
Сначала откройте шрифт, который хотите проверить, и откройте и очистите панель «Правка макроса».
Введите следующую простую программу:
python
Запустите программу и проверьте результаты на панели вывода.
Давайте немного поговорим о коде выше:
- for g in fl.font.glyphs:
Эта строка начинает цикл, который присваивает переменную g каждому из глифов, содержащихся в текущем шрифте (на который ссылается fl.font). - if len(g) == 0 and len(g.components) == 0:
Эта строка сравнивает длину контура глифа и длину массива components (который содержит все компоненты) с нулём, и если нет ни контура, ни компонентов, она выполняет следующую строку. - print g.name
Если условие во второй строке истинно, эта строка печатает имя глифа.
Как видите, всего тремя строками кода мы решили задачу, на выполнение которой вручную обычно уходит около 30 минут.
Классы Python в FontLab Studio
В этом разделе мы обсудим базовый набор классов и переменных FontLab Studio. Полная спецификация доступна в виде отдельного документа для загрузки с нашего сайта (www.fontlab.com), и большинство классов также самодокументированы. Например, чтобы распечатать краткую справку по классу Font, введите на панели «Правка макроса»:
И проверьте панель вывода. Обратите внимание на два символа подчёркивания до и после doc.
FontLab
Высшим классом в иерархии FontLab Studio является класс с именем FontLab. Вы не можете создать его явно, но объект этого класса всегда доступен и называется fl.
Мы приведём несколько примеров использования функций, описанных выше, когда будем обсуждать классы Font и Glyph.
Font
Класс Font содержит все данные, относящиеся к шрифту во внутренних структурах данных FontLab Studio. Сам по себе класс Font не взаимодействует с элементами пользовательского интерфейса. Для взаимодействия со шрифтом вам нужно использовать другие классы, такие как объект fl класса FontLab Studio.
Так, например, если вы хотите увидеть ширину (advanced width) глифа с индексом 12, вы напишете:
Здесь fl — объект класса FontLab Studio, представляющий пользовательский интерфейс FontLab Studio; font — текущий активный шрифт (объект класса Font); glyphs — член класса Font, представляющий массив глифов; [12] указывает на 12-й элемент массива глифов; width — член класса Glyph, представляющий ширину глифа.
Более короткий способ доступа к глифам шрифта — обращаться к объекту Font как к массиву:
Первая строка напечатает количество глифов в шрифте. Вторая строка — это более короткая версия примера, описанного выше.
С членом glyphs вы можете выполнять несколько операций, например, добавлять новый глиф:
Этот пример добавит новый глиф в конец шрифта.
Чтобы удалить все глифы из шрифта, используйте этот метод:
Другие члены класса Font представляют данные заголовка шрифта, и вы можете получить их список, выполнив следующую операцию:
Два метода класса Font относятся к внутреннему формату FontLab Studio:
Эти операции не связаны с пользовательским интерфейсом FontLab Studio, поэтому вы можете использовать их со шрифтами, созданными внутри программы Python и не подключенными к системе FontLab Studio.
Glyph
Класс Glyph представляет глиф как структуру данных в FontLab Studio. Наиболее важные члены перечислены в следующей таблице:
Вы можете получить список операций, определённых для класса Glyph, если выполните следующую строку:
Вы можете заменить Glyph на имя любого класса, упомянутого выше, чтобы получить описание его членов и операций.
Вот несколько примеров:
Пометка всех глифов разным цветом в зависимости от количества компонентов.
Некомпозитные глифы не помечаются. Глифы, имеющие ровно 2 компонента, помечаются зелёным, а глифы с другим количеством компонентов или имеющие контур и компоненты помечаются красным:
Выделение всех глифов, не имеющих ни контура, ни компонентов:
Первое, что мы делаем, — снимаем выделение со шрифта. Снять выделение со всех глифов сразу быстрее, чем изменять выделение всех глифов по одному. В нашем примере мы выделяем только те глифы, которые соответствуют условию.
Модули
Более сложные программы на Python могут быть написаны с использованием так называемых модулей (обратитесь к документации Python для получения дополнительной информации). Существует три разные папки, в которых FontLab Studio ищет модули:
- [Папка пользовательских данных FontLab Studio]\Macro\System\Modules
(обычно C:\Documents and Settings\ВашеИмяПользователя\Мои документы\FontLab\Studio5\Macro\System\Modules)
Помещайте сюда свои собственные модули Python, специфичные для FontLab. - [Папка Python]\Lib\site-packages
(обычно C:\Python24\Lib\site-packages)
Помещайте сюда свои модули Python, не специфичные для FontLab, которые вы хотели бы использовать как в автономном Python (вне FontLab Studio), так и в FontLab Studio. - [Папка приложения FontLab Studio]\Macro\System\Modules
(обычно C:\Program Files\FontLab\Studio5\Macro\System\Modules)
Не помещайте сюда свои собственные модули — эта папка зарезервирована для модулей от Fontlab Ltd. и зарегистрированных поставщиков скриптов для FontLab.
Обратите внимание, что расположение папки пользовательских данных FontLab Studio можно изменить в меню Параметры > Папки и пути > Файлы FontLab Studio 5.
Пожалуйста, обращайтесь к нашему веб-сайту (http://www.fontlab.com/python/) для получения дополнительных примеров и информации о макропрограммах. Вы также можете изучить исходный код примеров программ, поставляемых с FontLab Studio.
Комментарии к материалу
- Макроинструменты — это расширение возможностей редактора. Они позволяют создавать собственные режимы рисования, что может быть полезно для специфических задач (например, рисование штрихов определённой формы). Однако для вашей текущей задачи (унификация существующих символов) они вряд ли понадобятся. Основной фокус — на скриптах, которые обрабатывают уже нарисованные глифы.
- Панель «Правка макроса» — это ваша основная рабочая среда. Здесь вы пишете, тестируете и сохраняете скрипты. Важно запомнить сочетания клавиш для запуска (треугольник) и перезапуска Python (круговая стрелка). Перезапуск Python бывает нужен, если вы импортируете модули и хотите перезагрузить их без закрытия программы.
- Именование через #FLM: — критически важная деталь. Без этой директивы макрос будет отображаться в интерфейсе под именем файла (без расширения .py), что не всегда удобно. Используйте #FLM: Моё полезное действие для красивого имени.
- Структура папок в ...\Macro\System\ определяет, в каких меню появится макрос. Например, положив скрипт в папку Glyph, вы сделаете его доступным из контекстного меню окна глифа. Папка Font — для меню окна шрифта, Selection — для меню при работе с выделенными объектами. Папки внутри Macro (но не в System) создают категории на панели инструментов.
- Примеры кода:
print "Hello World!" — простейший способ убедиться, что всё работает, и увидеть вывод.
Поиск пустых глифов — наглядная демонстрация обхода всех глифов и доступа к их свойствам (len(g) для количества узлов, len(g.components) для количества компонентов). Это реально полезный скрипт для ревизии шрифта. - Класс fl (FontLab) — точка входа во все данные. Через него вы получаете текущий шрифт (fl.font), текущий глиф (fl.glyph), количество открытых шрифтов и т.д. Методы Open, Save, UpdateFont, Select позволяют управлять шрифтом и интерфейсом.
- Класс Font — содержит всё, что относится к шрифту: список глифов, классы, фичи, пользовательские данные. Доступ к глифам можно получить как fl.font.glyphs[i] или напрямую как fl.font[i]. Это удобно.
- Класс Glyph — самый важный для вашей задачи. Его члены:
nodes — список узлов контура. Через них можно менять геометрию.
components — список компонентов (если глиф составной).
width — ширина глифа.
mark — цвет пометки (используется для визуальной классификации).
name — имя глифа.
unicode, unicodes — коды символов.
anchors — якоря для диакритики. - Методы для работы с выделением: Select, Unselect, ForSelected позволяют эффективно обрабатывать только нужные глифы. Например, можно применить трансформацию только к выделенным символам.
- Модули — способ организовать код. Свои общие функции (например, расчёт высоты глифа) можно вынести в отдельный файл в папку Modules и импортировать в любой скрипт. Это повышает переиспользуемость.
Практическое руководство по реализации в проекте ABC_Lat
Используя описанные выше возможности, вы можете системно подойти к унификации вашего шрифта. Предлагаю следующий план действий.
1. Настройка рабочей среды
- Создайте в папке пользовательских данных FL5 (...\FontLab\Studio5\) структуру:
Macro\System\Font — для скриптов, вызываемых из окна шрифта.
Macro\System\Glyph — для скриптов из окна глифа.
Macro\System\Selection — для скриптов по выделению.
Macro\System\Modules — для ваших модулей. - Убедитесь, что Python 2.4 установлен и панель «Макрос» активна.
2. Создание библиотеки полезных функций (модуль)
Создайте файл abc_utils.py в папке ...\Macro\System\Modules\ со следующим содержимым (расширяйте по мере необходимости):
3. Скрипт для поиска пустых глифов
Создайте файл FindEmptyGlyphs.py в папке ...\Macro\System\Font\ (чтобы был доступен из окна шрифта):
FLM: Найти пустые глифы
4. Скрипт для цветовой маркировки по сложности
Это поможет визуально оценить структуру шрифта.
5. Скрипт для нормализации высоты прописных
Усовершенствованный вариант с использованием модуля и учётом baseline:
6. Скрипт для применения фильтра Bold/Light к выделенным глифам
7. Скрипт для автоматической расстановки якорей (пример для диакритики)
Это более сложный скрипт, но идея такова: пройти по глифам с диакритикой (например, имена содержат 'acute', 'grave' и т.д.) и добавить якорь top в базовые глифы. Можно начать с простого:
8. Интеграция в меню
После сохранения скриптов в соответствующих подпапках System\... они автоматически появятся в контекстных меню. Для быстрого доступа можно назначить горячие клавиши через панель «Макрос».
Заключение
Предложенные скрипты покрывают основные потребности: инвентаризация, визуальная классификация, коррекция размеров, толщины и подготовка к кернингу. По мере работы вы сможете дополнять модуль abc_utils.py новыми функциями (например, для вычисления среднего наклона, для проверки симметрии) и создавать более тонкие скрипты под конкретные задачи.
Главное — всегда тестировать скрипты на копии шрифта и использовать возможности fl.Message для обратной связи. Успехов в приведении ABC_Lat к единому стандарту!
Теперь опишем каждый скрипт.
Для удобства представим результат в виде структурированного списка с заголовками.
Начнём.
Описание макросов для FontLab Studio 5 (из комплекта поставки и примеров)
Ниже представлен подробный разбор каждого скрипта, входящего в предоставленный набор. Для каждого файла указано его назначение, принцип работы и, где это возможно, рекомендации по применению.
Anchor kerning.py
Назначение: Автоматическая генерация кернинга на основе якорей «left» и «right».
Как работает: Скрипт перебирает все пары глифов в шрифте. Для каждого глифа ищет якорь с именем right (у первого глифа) и left (у второго). Если оба якоря найдены, вычисляется значение кернинга как разница между положением правого якоря и левого якоря с учётом ширины первого глифа. Полученная пара добавляется в таблицу кернинга первого глифа. Процесс отображается с индикатором прогресса.
Применение: Полезно для шрифтов, где уже расставлены смысловые якоря (например, для диакритики), и требуется быстро создать базовый кернинг на их основе.
bevel.py
Назначение: Эффект «скоса» (bevel) для выделенных глифов.
Как работает: Для каждого выделенного глифа создаётся его копия, которая сдвигается на вектор (-8, 8). Затем исходный глиф утолщается (Distance), из него вычитается сдвинутая копия, в результате чего получается эффект объёмного скоса.
Применение: Художественная обработка символов для создания эффекта трёхмерности.
count pairs.py
Назначение: Подсчёт общего количества кернинговых пар в шрифте.
Как работает: Проходит по всем глифам шрифта, суммирует длину списка kerning каждого глифа и выводит итог в панель Output.
Применение: Быстрая инвентаризация объёма кернинга.
Create OpenType.py
Назначение: Автоматическое создание OpenType-фич (smcp, onum, titl, liga) и соответствующих глифов для капители, старых цифр, титульных символов и лигатур.
Как работает:
- Определяет списки букв (прописные, строчные) и цифр.
- Вычисляет коэффициенты масштабирования для капители (на основе высоты строчных и прописных).
- Для фичи onum (старые цифры) создаёт глифы с суффиксом .taboldstyle, масштабирует или сдвигает их в зависимости от цифры.
- Для фичи titl (титульные) создаёт глифы с суффиксом .titling.
- Для фичи smcp (капитель) создаёт глифы с суффиксом .small и масштабирует их.
- Для фичи liga (лигатуры) создаёт стандартные лигатуры ffi, fi, ffl, ff, fl, а также f_f_j и f_j.
- Формирует классы OpenType в строку ot_classes и добавляет фичи в шрифт.
Применение: Значительно ускоряет подготовку шрифта с базовыми OpenType-функциями.
Curve.py
Назначение: Инструмент рисования кривых Безье (макроинструмент).
Как работает: Реализует класс Tool с методами down, up, move. Позволяет рисовать кривую, интерактивно управляя контрольными точками. Поддерживает продолжение контура от конечного узла предыдущей кривой. Эмулирует поведение инструментов FreeHand / Fontographer.
Применение: Замена стандартному инструменту «Кривая» с дополнительными возможностями, заданными в коде.
digital.py
Назначение: Эффект «цифровой» мозаики — преобразование глифа в набор кружков (пикселей) с заданным диаметром и промежутком.
Как работает:
- Выводит диалоговое окно с параметрами: диаметр точки (diameter) и белое пространство (border).
- Для выделенных глифов строит сетку из квадратов, накладывает её на глиф (пересечение), затем определяет, какие ячейки сетки заполнены, и вместо квадратов рисует круги в соответствующих позициях.
Применение: Создание стилизованных растровых изображений из векторных контуров.
done.py
Назначение: Заглушка для кода, выполняемого при закрытии FontLab Studio.
Как работает: Содержит только комментарий. Пользователь может вписать сюда свои действия, которые должны происходить при завершении программы.
Применение: Автоматизация задач при закрытии (например, сохранение лога, очистка временных файлов).
Drop.py
Назначение: Инструмент рисования «капель» (макроинструмент).
Как работает: При перетаскивании мыши добавляет в глиф окружности (капли) случайного радиуса (или зависящего от давления пера, если планшет активен). Каждая капля рисуется как замкнутый контур — кривая Безье, аппроксимирующая окружность. После отпускания кнопки все капли объединяются с исходным глифом (логическая операция сложения).
Применение: Художественный инструмент для создания эффекта разбрызгивания.
drops.py
Назначение: Эффект «капли» — добавление случайных кругов в глиф.
Как работает: Похож на Drop.py, но работает как пакетный скрипт: для каждого выделенного глифа генерирует 80 кругов случайного размера в пределах габаритов шрифта, затем выполняет булевы операции для создания эффекта «вычитания» или «сложения» (судя по коду: сначала утолщает контур, потом вычитает из него круги и затем исходный контур).
Применение: Декоративная обработка символов.
features list.py
Назначение: Вывод списка всех OpenType-фич текущего шрифта.
Как работает: Перебирает fl.font.features и печатает теги фич (например, 'liga', 'smcp') в панель Output.
Применение: Быстрая проверка, какие фичи уже определены в шрифте.
init.py
Назначение: Инициализация макрос-системы при запуске.
Как работает: Импортирует основные модули (FL, fl_cmd, cPickle), настраивает кодировку вывода для корректного отображения Unicode в панели Output. Также определяет функцию Caption (для отображения подписей в окне шрифта?) и константы для работы с графикой (стили перьев, кистей, типы прямоугольников).
Применение: Служебный скрипт, обеспечивающий корректную среду для других макросов.
Invert selection.py
Назначение: Инвертировать выделение глифов в окне шрифта.
Как работает: Проходит по всем индексам глифов и меняет состояние выделения на противоположное с помощью fl.Select(i, not fl.Selected(i)).
Применение: Удобная команда для быстрого изменения набора выделенных символов.
Line.py
Назначение: Инструмент рисования прямых линий (макроинструмент).
Как работает: Класс Tool. При нажатии создаёт узел в начальной точке, при движении мыши обновляет положение конечного узла. Если начали от существующего конечного узла, продолжает контур.
Применение: Альтернативный инструмент для рисования многоугольников.
Mark problems.py
Назначение: Пометка глифов цветом в зависимости от количества проблем, найденных функцией Audit().
Как работает: Для каждого глифа вызывается g.Audit() (проверка на ошибки — пересечения, вырожденные контуры и т.д.). Количество проблем l определяет цвет: если больше 25 — красный (1), иначе оттенок от 0 до 250 с шагом 10.
Применение: Визуализация «качества» глифов для последующей ручной доработки.
mosaic.py
Назначение: Эффект «мозаика» — разбиение глифа на квадратные ячейки и случайное смещение каждой.
Как работает: Накладывает на глиф сетку из квадратов, выполняет пересечение (оставляет только те части, которые попадают в глиф). Затем для каждого полученного фрагмента случайно сдвигает все его узлы, создавая эффект «рассыпания».
Применение: Декоративный эффект для создания стилизованных изображений.
new.py
Назначение: Скрипт, выполняемый после создания нового шрифта.
Как работает: Пустой, содержит комментарий. Пользователь может добавить свои действия (например, установка метрик по умолчанию, добавление служебных глифов).
Применение: Автоматизация начальной настройки нового проекта.
open.py
Назначение: Скрипт, выполняемый после открытия шрифта.
Как работает: Пустой, содержит комментарий. Предназначен для пользовательских действий при открытии.
Применение: Например, автоматическое обновление каких-либо данных или проверка.
placeholder.py
Назначение: Заглушка для макроса, расширяющего работу окна метрик в режиме предпросмотра.
Как работает: Выводит текст в панель Output.
Применение: Демонстрация возможности добавления макросов в окно метрик.
placeholder+.py
Назначение: Заглушка для макроса в окне метрик в режиме редактирования текста.
Как работает: Аналогично placeholder.py.
Применение: То же, но для другого режима.
Print component.py
Назначение: Вывод информации о текущем выделенном компоненте (в окне глифа).
Как работает: Обращается к fl.font[fl.iglyph].components[fl.iobject] и печатает объект компонента.
Применение: Отладка, просмотр свойств компонента.
Print font.py
Назначение: Вывод информации о текущем шрифте.
Как работает: print fl.font — печатает строковое представление объекта шрифта.
Применение: Быстрая проверка, какой шрифт активен.
Print glyph.py / Print Glyph+.py
Назначение: Вывод информации о текущем глифе.
Как работает: print fl.font[fl.iglyph]. Два файла с одинаковым содержимым, вероятно, для разных контекстов меню.
Применение: Отладка, просмотр свойств глифа.
Print node.py
Назначение: Вывод информации о текущем выделенном узле.
Как работает: print fl.font[fl.iglyph][fl.iobject].
Применение: Просмотр координат и типа узла.
Print nodes.py
Назначение: Вывод информации обо всех выделенных узлах текущего глифа.
Как работает: Перебирает все узлы глифа, проверяет флаг selected и печатает те, которые выделены.
Применение: Удобно при отладке, чтобы увидеть, какие именно узлы сейчас активны.
Print program.py
Назначение: Вывод программы TrueType-хинтинга текущего глифа.
Как работает: Создаёт объект TTH для текущего глифа, загружает программу и печатает все инструкции.
Применение: Для анализа хинтинга.
print_glyphs.py
Назначение: Вывести список всех глифов шрифта с индексами.
Как работает: Если шрифт открыт, проходит по всем глифам и печатает индекс и имя.
Применение: Инвентаризация состава шрифта.
print_kerning.py
Назначение: Вывести все кернинговые пары для текущего глифа.
Как работает: Проверяет наличие кернинга у текущего глифа и для каждой пары выводит имена глифов и значение кернинга.
Применение: Просмотр кернинга конкретного символа.
Sample printout.py
Назначение: Пример макроса для окна метрик.
Как работает: Просто печатает строку.
Применение: Демонстрация.
save.py
Назначение: Скрипт, выполняемый перед сохранением шрифта.
Как работает: Пустой, содержит комментарий. Можно добавить свои действия, например, обновление метаданных.
Применение: Автоматические проверки или модификации при сохранении.
Select alphanumeric.py
Назначение: Выделить все буквы латиницы (A-Z, a-z) и цифры (0-9).
Как работает: Сначала снимает выделение, затем по кодам Unicode (0x41–0x5A, 0x61–0x7A, 0x30–0x39) выделяет соответствующие глифы. Устанавливает текущим глиф с индексом 20 (некоторый произвольный).
Применение: Быстрое выделение основного набора символов.
Select composites.py
Назначение: Выделить все составные глифы (имеющие компоненты).
Как работает: Перебирает все глифы, если у глифа есть компоненты (len(cs) > 0), то выделяет его. Также выводит имя первого компонента в панель Output.
Применение: Для работы только с составными символами.
shadow.py
Назначение: Эффект тени для выделенных глифов.
Как работает: Создаёт две копии глифа, одну сдвигает на вектор (40, -40), другую утолщает (Distance). Затем выполняет булевы операции, чтобы получить эффект тени (исходный глиф вычитается из утолщённого и добавляется сдвинутый).
Применение: Создание объёмной тени у символов.
Shadow+.py
Назначение: Упрощённый эффект тени (вероятно, встроенный метод).
Как работает: Вызывает fl.font[fl.iglyph].Shadow() — предположительно, встроенный метод класса Glyph для создания тени.
Применение: Быстрая тень для текущего глифа.
slices.py
Назначение: Эффект «нарезки» — разрезание глифа на полосы и случайный сдвиг каждой.
Как работает: Определяет направление нарезки вектором v, ширину полосы width и величину сдвига move. Для каждого выделенного глифа создаёт множество полос, пересекает их с глифом и сдвигает каждый фрагмент в направлении вектора на случайную величину.
Применение: Создание эффекта «движения» или «искажения».
Testing macro.py
Назначение: Тестовый макрос — выводит количество открытых шрифтов и имя текущего.
Как работает: Использует len(fl) для подсчёта шрифтов и fl[fl.ifontslist].font_name для имени.
Применение: Проверка работы макросов, отладка.
testing.py
Назначение: Аналогично Testing macro.py с небольшими отличиями.
Как работает: Выводит приветствие, количество открытых шрифтов и имя текущего глифа.
Применение: То же.
Заключение
Представленный набор макросов охватывает широкий спектр задач: от создания OpenType-фич и автоматической расстановки кернинга до художественных эффектов и инструментов рисования. Многие скрипты являются примерами, демонстрирующими возможности API FontLab Studio 5, и могут служить основой для создания собственных макросов под конкретные нужды проекта ABC_Lat. Особый интерес представляют Create OpenType.py (автоматизация фич), Anchor kerning.py (кернинг по якорям), Mark problems.py (визуализация ошибок) и скрипты для работы с выделением.
LatCap_A.txt
TXT 873B
LatCap_A-rel.txt
TXT 1.05KB
У меня имеется пустой глиф для заглавной латинской A. Ваша задача разработать алгоритм макроса, который используя средства рисования изобразит в окне глифа FontLab Studio 5 этот символ в пределах установленных границ 600 на 700 пикселей.
Текстовое описание этого символа прилагаю.
Thought for 204 seconds
Мы получили два файла с описанием контуров для заглавной латинской A. Один файл содержит координаты в виде списка списков (contours = [[...], [...]]), второй - анализ. Нужно разработать алгоритм макроса для FontLab Studio 5, который нарисует этот символ в окне глифа в пределах 600x700 пикселей.
Понимание задачи: нужно написать скрипт на Python для FL5, который создаст контуры глифа по заданным координатам. Исходные координаты даны в абсолютных значениях (видимо, в единицах шрифта). Но они имеют min x=44.65, max x=355.35, min y=20.0, max y=380.0. Это примерно ширина 310.7, высота 360.0. Мы должны разместить символ в пределах 600x700. Видимо, нужно масштабировать или просто вписать в заданные границы. Но сказано "в пределах установленных границ 600 на 700 пикселей". Вероятно, речь о размерах глифа в окне редактора, т.е. мы можем задать ширину и высоту глифа (UPM) или просто нарисовать с такими размерами. Однако в FL5 глиф имеет свои метрики (width, height). Но обычно координаты контура могут быть любыми. Вероятно, нужно просто создать глиф с контуром по этим координатам, но при этом чтобы он не выходил за пределы некоего окна? Или нужно масштабировать контур так, чтобы он занимал область примерно 600x700? Исходные размеры 310x360 - можно масштабировать до 600x700, но пропорции разные (0.86 против 0.857). Можно просто масштабировать с сохранением пропорций, чтобы максимальный размер (высота) стала 700, тогда ширина будет 700*310/360 ≈ 602.8, что близко к 600. Или можно масштабировать независимо? Обычно сохраняют пропорции.
Но в задании сказано "изобразит в окне глифа FontLab Studio 5 этот символ в пределах установленных границ 600 на 700 пикселей." Возможно, это означает, что окно глифа имеет размер 600x700 пикселей (область просмотра) и нужно нарисовать символ так, чтобы он полностью помещался. Но в FL5 координаты глифа абстрактны, а окно имеет масштаб. Проще всего: взять исходные координаты и масштабировать их так, чтобы bounding box стал, например, 600 в ширину и 700 в высоту, или вписать в эти габариты с сохранением пропорций. Поскольку пропорции близки, можно сделать так: определить масштаб по высоте: 700 / (ymax - ymin) = 700 / 360 ≈ 1.9444, тогда новая ширина будет 310.7 * 1.9444 ≈ 604, что чуть больше 600. Можно масштабировать по ширине: 600/310.7 ≈ 1.931, тогда высота станет 360*1.931=695, что меньше 700. Чтобы вписать точно в 600x700, нужно либо обрезать, либо делать не uniform масштабирование, что исказит форму. Лучше выбрать масштаб так, чтобы больший размер стал 700, а меньший вписался в 600, но если при этом ширина превысит 600, то придется уменьшить масштаб по ширине, но тогда высота будет меньше 700. В итоге оптимально - масштабировать с сохранением пропорций, чтобы символ целиком помещался в прямоугольник 600x700. Это значит, что масштаб = min(600/width, 700/height). В данном случае width=310.7, height=360, 600/310.7≈1.93, 700/360≈1.944, минимальный =1.93. Тогда новые размеры: ширина=600, высота=360*1.93=694.8, что влезает по высоте. Таким образом, символ займет всю ширину и почти всю высоту.
Итак, алгоритм:
- Получить текущий глиф (fl.glyph) или создать новый? Скорее всего, мы работаем с текущим открытым глифом. Можно очистить его и нарисовать.
- Задать исходные координаты из файла. В файле даны два контура. Первый контур - вероятно, внешний (основная форма буквы A), второй - внутренний (вырез, перекладина? Но во втором много точек - похоже на описание перекладины или детали). Посмотрим содержимое:
contours = [
[96.52, 380.0, 151.47, 276.27, 293.72, 276.27, 309.64, 380.0, 355.35, 380.0, 299.89, 20.0, 241.34, 20.0, 44.65, 380.0, 96.52, 380.0, 96.52, 380.0],
[228.51, 132.47, 230.86, 128.12, 233.14, 123.85, ... и т.д.]
]
Первый список содержит четное количество чисел? Давайте посмотрим: 96.52, 380.0 - это точка, затем 151.47,276.27 и т.д. Всего в первом списке 20 чисел, значит 10 точек. Последовательность: (96.52,380.0), (151.47,276.27), (293.72,276.27), (309.64,380.0), (355.35,380.0), (299.89,20.0), (241.34,20.0), (44.65,380.0), (96.52,380.0), (96.52,380.0). Последняя точка совпадает с первой - замкнутый контур. Это похоже на контур буквы A: две ножки, перекладина? Внешний контур должен быть треугольником с вырезом? Но тут много точек. Возможно, это контур, описывающий форму A с засечками? На самом деле буква A обычно имеет два внешних контура? Но здесь один контур с 10 точками, вероятно, это внешний контур, а второй контур - внутренний (вырез) или перекладина? Второй контур очень длинный, много точек - возможно, это перекладина (горизонтальная черта) с плавными кривыми? Давайте посмотрим: второй список начинается с (228.51,132.47) и затем множество пар, и заканчивается (172.01,239.29) и снова (228.51,132.47) - замыкание. Это похоже на описание перекладины или какой-то детали. Судя по анализу, в файле LatCap_A-rel.txt указано "Контуров: 2", значит так и есть.
Таким образом, в скрипте нужно создать два контура, добавив узлы соответствующих типов. Поскольку координаты даны парами, скорее всего, это точки, соединенные прямыми линиями? Но в первом контуре точки просто перечислены, возможно, это вершины многоугольника. Во втором конторе много точек, возможно, это тоже ломаная. В FL5 узлы могут быть с кривыми, но здесь нет информации о типах узлов (кривая Безье). Предположим, что это просто линейные сегменты (тип узла 1 - прямая). В коде макросов мы видели, что для создания узла используется Node(0x11, point) для move и type=1 для line. Обычно контур начинается с узла типа 0x11 (move) и затем добавляются узлы типа 1 (line) или 0x23 (curve). Здесь все точки, вероятно, линейные. Но во втором контуре очень много точек - может быть, это плавная кривая, но без контрольных точек. Возможно, это просто набор точек для аппроксимации.
Учитывая, что задача стоит "используя средства рисования изобразит в окне глифа", мы можем просто создать полилинии по точкам. Для этого нужно:
- Очистить глиф (удалить все существующие контуры)
- Для каждого контура создать последовательность узлов. Первый узел - тип move (0x11), остальные - line (1), последний - обычно замыкается, но если последняя точка совпадает с первой, можно не добавлять, а просто установить флаг замкнутости? В FL5 контур замыкается автоматически, если последняя точка совпадает с первой? Но в примерах они добавляют все точки. Лучше добавить все точки, включая последнюю, чтобы контур замкнулся. Но тогда будет дублирование точки, что может создать нулевой сегмент. В первом контуре последняя точка равна первой, поэтому мы можем либо не добавлять последнюю, либо добавить и тогда будет замкнуто, но с повторением. В коде FL5 обычно при создании контура добавляют все точки, а замыкание происходит, когда последняя точка соединена с первой. Если последняя точка равна первой, то получится сегмент нулевой длины, что нехорошо. Лучше добавлять все точки, кроме последней, а затем установить контур замкнутым? Но в API FL5 нет явного метода замыкания; контур считается замкнутым, если последняя точка соединена с первой линией. Если мы не добавим последнюю точку, то контур будет открытым. Обычно при рисовании замкнутого контура добавляют все точки, включая последнюю, которая совпадает с первой, и тогда программа автоматически понимает, что это замкнуто? Но тогда создается сегмент от предпоследней до последней и от последней до первой? Если последняя совпадает с первой, то фактически это два сегмента: один от предпоследней до последней (которая равна первой) и затем от последней до первой - нулевой длины. Лучше избегать дублирования. В примере из drops.py они рисуют окружность, добавляя узлы, причем последний узел не дублирует первый. Они создают замкнутый контур, заканчивая там же, где начали, но не добавляя лишний узел. В коде: сначала добавляют move node в точку (p.x - size, p.y), затем 4 кривых узла, каждый добавляет свои точки, и в итоге контур замыкается сам, потому что последняя точка кривой ведет обратно к начальной. В нашем случае, если мы имеем список точек, образующих замкнутый многоугольник, то нужно добавить все точки по порядку, и последняя точка должна быть такой же, как первая? Или мы можем не добавлять последнюю, а просто после добавления всех точек контур будет замкнут, если последняя соединена с первой? Но по умолчанию контур не замкнут, если не соединить последнюю с первой. В FL5, чтобы создать замкнутый контур, нужно, чтобы последний узел имел флаг, указывающий на замыкание? Или это определяется типом узла? Обычно в векторных редакторах контур замыкается автоматически, если последняя точка соединена с первой линией. Но чтобы это произошло, нужно либо явно добавить линию от последней к первой, либо установить флаг closed. В API FL5, судя по примерам, они добавляют узлы, и контур считается замкнутым, если первый узел имеет тип move, а последний - line, и они не соединяются автоматически. В примере с Line.py они добавляют узлы и при рисовании линии просто перемещают последний узел. Контур там не замыкается. В Curve.py они добавляют узлы с типами. Для замкнутого контура, вероятно, нужно добавить узел в начальную точку еще раз? Или использовать специальный метод.
Давайте посмотрим на создание окружности в drops.py: они добавляют move node, затем четыре узла типа curve, каждый из которых задает три точки (контрольные и конечная). В итоге после четырех узлов последняя точка последнего узла - это точка (p.x - size, p.y), т.е. начальная. Таким образом, они получают замкнутый контур без дублирования узла. Значит, чтобы замкнуть, нужно, чтобы последняя добавленная точка совпадала с первой. В нашем случае для многоугольника, если мы имеем список точек p0, p1, ..., pn, где pn == p0, то мы можем добавить p0 как move, затем p1... pn-1 как line, и не добавлять pn, тогда последняя линия будет от pn-1 до p0, но как это сделать? Векторный формат предполагает, что после добавления pn-1 линия идет к p0, если мы укажем это? В API FL5, чтобы добавить линию, нужно создать узел и указать его точку. Линия будет от предыдущей точки к этой. Если мы хотим замкнуть, то после pn-1 нужно добавить узел в p0. Но тогда p0 уже есть как первый узел, получится дублирование. В некоторых системах можно просто не добавлять последнюю точку, а установить флаг замкнутости контура. Но в FL5, судя по документации, контур определяется набором узлов, и замыкание происходит, если последний узел соединен с первым неявно? В объекте Glyph есть метод CloseContour? Не знаем. В предоставленных скриптах не видно явного замыкания. Например, в Line.py при создании линии они добавляют два узла: первый move, второй line, и это открытый контур. В Curve.py они добавляют несколько узлов, и последний узел возвращается в начало, но они не добавляют дополнительный узел, а используют кривые. Так что, видимо, для замкнутого контура нужно, чтобы последняя точка была равна первой. В первом контуре последняя точка (96.52,380) совпадает с первой. Значит, мы можем добавить все точки, включая последнюю, но тогда у нас будет два узла в одной точке - первый и последний. Это создаст нулевой сегмент между предпоследним и последним, и между последним и первым? Но если последний узел имеет координаты, равные первому, то линия от предпоследнего до последнего - нормальная, а от последнего до первого - нулевая, но поскольку последний узел есть, то контур заканчивается на нем, и нет линии к первому. На самом деле контур не замкнется, потому что после последнего узла нет соединения с первым. Чтобы замкнуть, нужно, чтобы после последнего узла был сегмент к первому, но для этого требуется либо добавить еще один узел, либо изменить тип. В большинстве векторных форматов контур замкнут, если флаг closed установлен, и тогда последний сегмент соединяет последнюю точку с первой. В FL5, вероятно, есть понятие замкнутого контура. Посмотрим в документации: класс Glyph имеет метод AddContour? Не знаем. В примерах они используют просто добавление узлов. Возможно, контур автоматически замыкается, если первый узел имеет тип move, а последний - line и они не совпадают? Нет.
Чтобы избежать неопределенности, мы можем создать контур, используя следующий подход: добавить первый узел как move, затем для всех промежуточных точек добавить line, а в конце добавить line в первую точку (что создаст дополнительный узел, но это будет лишняя точка). Но тогда у нас будет два узла в одной точке (первый и последний), и контур будет замкнут с сегментом от предпоследнего до последнего и от последнего до первого? На самом деле после последнего узла нет сегмента к первому, потому что последний узел - это просто точка, и чтобы соединить с первым, нужен еще один сегмент. Но если мы добавим line в первую точку, то мы создадим новый узел с координатами первой точки. Тогда у нас будет два узла с одинаковыми координатами: первый и новый последний. Между ними будет сегмент (от предпоследнего до последнего), но сегмент от последнего до первого не существует, так как после последнего ничего нет. Чтобы замкнуть, нужно, чтобы последний сегмент вел к первому узлу. Это значит, что мы не должны добавлять первый узел как отдельный, а должны начать с move в p0, затем добавлять p1, p2, ..., pn, и после pn контур замкнется автоматически, если pn соединена с p0. Но для этого нужно, чтобы после pn был неявный сегмент. В API FL5, вероятно, есть метод ClosePath для текущего контура. В документации к FL5 есть упоминание о том, что можно добавлять узлы, и контур считается замкнутым, если последний узел имеет флаг, или можно использовать метод CloseContour. Но в предоставленных скриптах я не вижу такого. В скрипте Line.py они добавляют два узла и линия рисуется от первого ко второму, контур открытый. В Curve.py они добавляют несколько узлов, и последний узел (кривой) ведет к начальной точке, но они не добавляют дополнительный узел. Там они сначала добавляют move node в p0, затем добавляют curve node, у которого конечная точка - p0? Давайте посмотрим в Curve.py: в методе down они создают узел n с точкой self.p0 и добавляют его. Затем они создают еще один узел n с типом 0x1023 (?) и устанавливают его точки [1] и [2] равными self.p0, а точку [0] тоже self.p0? Запутанно. На самом деле, в Curve.py они строят кривую от предыдущей точки к текущей. Там есть сложная математика. Проще для нашей задачи использовать простые линейные сегменты.
Учитывая, что это учебная задача, вероятно, можно просто добавить все точки из первого контура как узлы, и последняя точка совпадает с первой, тогда получится замкнутый контур, потому что после добавления последней точки линия от предпоследней до последней будет нарисована, а затем, поскольку последняя точка равна первой, визуально контур будет замкнут, но технически это не замкнутый контур, а просто последняя точка совпадает с первой, и контур не имеет соединения между последней и первой. Однако в графическом представлении это будет выглядеть как разрыв, если не соединить. Чтобы убедиться, посмотрим, как в других программах: обычно, чтобы создать замкнутый многоугольник, нужно либо добавить все вершины, и последняя соединяется с первой автоматически, если флаг closed установлен. В FL5, возможно, есть метод глифа для замыкания контура. Например, можно использовать glyph.CloseContour(index) или что-то подобное. Но в предоставленных скриптах такого нет.
Вместо гадания, я предлагаю следующий подход: создать новый глиф, очистить его, затем для каждого контура:
- Взять список точек.
- Добавить первый узел как move (тип 0x11).
- Для всех остальных точек, кроме последней, добавить line (тип 1).
- После этого добавить еще один line в первую точку, чтобы замкнуть. Это создаст дополнительный узел, но он будет в той же точке, что и первый. Однако это допустимо: у нас будет два узла в одной точке, но контур будет замкнут. В некоторых редакторах это может создать проблему, но обычно допустимо. Альтернативно, можно добавить все точки, включая последнюю, и затем использовать метод CloseContour, если он есть. Я не знаю точно.
Давайте поищем в документации FL5. В классе Glyph есть метод AddNode? Они используют glyph.Add(node). У узла есть тип. Тип 0x11 означает move (начало контура), тип 1 - line. Если мы добавим move, затем несколько line, и в конце не добавим ничего, то контур будет открытым. Чтобы замкнуть, нужно либо добавить line в начальную точку, либо установить специальный флаг. В некоторых системах, если последний узел имеет тип line и его точка совпадает с move, то это замкнуто. Но мы уже имеем move в той же точке, так что если мы добавим line в ту же точку, то получится два узла в одной точке. Это нормально для замкнутого контура? Например, в PostScript так делают: closepath добавляет сегмент от текущей точки к начальной, но не создает нового узла. Здесь же узел создается. Возможно, правильнее использовать метод ClosePath, но его нет.
Я думаю, что для простоты можно создать контур из всех точек, включая последнюю, и затем контур будет иметь разрыв? Нет, если последняя точка добавлена как line, то она соединяется с предыдущей линией, но не с первой. Чтобы соединить с первой, нужно либо добавить еще одну линию, либо использовать специальную команду. В многих векторных форматах (например, SVG) контур замыкается командой Z. В FL5, возможно, есть возможность добавить узел с типом, указывающим на замыкание. Но в примерах из drops.py они обходятся без явного замыкания, так как последняя кривая ведет в начальную точку. Там они не добавляют отдельный узел, а каждый кривой узел имеет три точки: контрольные и конечную. Последний кривой узел имеет конечную точку, равную начальной. Таким образом, после четырех кривых контур замкнут. Значит, если мы хотим замкнуть многоугольник, нам нужно, чтобы последний сегмент вел в первую точку. Это означает, что мы должны добавить line из предпоследней точки в первую, но тогда первая точка уже есть как move, и мы добавляем еще один узел с той же координатой. Так что да, мы получим два узла в одном месте. Это не идеально, но допустимо для простого скрипта.
Второй контур имеет много точек. Возможно, он тоже замкнут. Проверим: в конце второго списка последние две точки: ... 172.01,239.29, затем 228.51,132.47 - это первая точка? Первая точка второго контура: 228.51,132.47. Значит, последняя пара 172.01,239.29, затем 228.51,132.47 - это не совпадает. На самом деле в конце списка после ... 172.01,239.29 идет 228.51,132.47 - это начало. То есть последняя точка списка - 228.51,132.47? Давайте посмотрим на второй список: он начинается с 228.51,132.47 и заканчивается ... 172.01,239.29, 228.51,132.47. То есть последняя точка - 228.51,132.47, что совпадает с первой. Значит, второй контур также замкнут.
Итак, оба контура замкнуты и имеют первую и последнюю точки одинаковые. Значит, в каждом списке первая и последняя пары совпадают. Поэтому мы можем добавить все точки, и тогда получим два узла в начале и в конце с одинаковыми координатами. Это приведет к тому, что контур будет состоять из сегментов от первой до второй, ..., от предпоследней до последней, и последняя точка - это та же, что и первая, но нет сегмента от последней до первой. Контур будет разомкнут. Чтобы замкнуть, нужно либо не добавлять последнюю точку, либо добавить ее и затем добавить еще один сегмент. Лучше не добавлять последнюю точку, а после добавления всех остальных точек добавить line в первую точку. Это создаст один дополнительный узел с координатами первой точки, но тогда у нас будет первый узел (move) и последний (line) с одинаковыми координатами, и между ними есть сегмент от предпоследнего до последнего, но нет сегмента от последнего до первого? Подождите, если мы добавим line в первую точку, то мы создаем новый узел, который является последним. Этот последний узел соединен с предпоследним линией. Но после него нет соединения с первым, так как это последний. Чтобы замкнуть, нужно, чтобы после последнего узла был сегмент к первому, но это невозможно без добавления еще одного. Правильный способ: добавить все точки, кроме первой, как line, а затем не добавлять ничего, но контур замкнется автоматически? Нет, не замкнется. В большинстве графических API есть понятие "замкнуть контур" - это означает, что после последней точки рисуется линия к первой. В векторных редакторах это реализуется либо специальной командой, либо добавлением неявного сегмента. В FL5, возможно, контур замыкается, если первый узел имеет тип move, а последний - line, и при отрисовке они соединяются. Но это не так.
Я предлагаю следующий компромисс: создать контур, добавив все точки из списка, кроме последней (т.е. без дублирования), а затем использовать метод glyph.CloseContour() для текущего контура, если он существует. Однако в API FL5 я не видел такого метода. В классе Glyph есть метод AddContour? Может быть, есть метод ClosePath. Поскольку мы не имеем документации, придется опираться на известные примеры.
Посмотрим на скрипт mosaic.py: там они создают квадрат с помощью четырех узлов: move, затем три line, и это замкнутый квадрат? Они добавляют: node = Node(0x11, Point(x, y)) - move, затем добавляют node.type = 1 с точкой (x, y+dy-border) - line, затем еще line (x+dx-border, y+dy-border), затем line (x+dx-border, y). Это четыре узла. После этого они не добавляют линию обратно в начало. Получится ли замкнутый контур? Визуально это будет незамкнутая ломаная из трех отрезков (от move до первого line, от первого до второго, от второго до третьего), и последняя точка не соединена с начальной. Но они, вероятно, предполагают, что контур замкнется автоматически? Вряд ли. В скрипте digital.py они используют MakeSquare, который добавляет move и три line, и затем этот квадрат используется для булевых операций. Возможно, в булевых операциях контур считается замкнутым, если он замкнут по данным? Но для булевых операций обычно требуются замкнутые контуры. Значит, они полагаются на то, что контур замкнут. Как же они это делают? Секрет в том, что в FL5, если вы добавляете узлы и первый узел имеет тип move, а последующие line, то контур автоматически замыкается, если последняя точка не равна первой? Нет, это нелогично. Давайте посмотрим на код MakeSquare в digital.py:
Здесь они добавляют четыре узла: первый move, затем три line. После этого у них есть точки: p0, p1, p2, p3. Контур состоит из сегментов: p0-p1, p1-p2, p2-p3. Сегмента p3-p0 нет. Это открытая ломаная. Однако они используют этот контур для булевой операции Bintersect. Возможно, в FL5 контур автоматически замыкается, если он не замкнут? Или они предполагают, что квадрат должен быть замкнут, и для этого нужно, чтобы последняя точка была p0? Но у них p3 - это (x+dx-border, y), а p0 - (x, y). Они не равны, так что квадрат не замкнут. Но в реальности квадрат должен быть замкнут. Я подозреваю, что в FL5, когда вы создаете контур, он считается замкнутым, если вы добавили узлы и затем не добавляете ничего, но по умолчанию он незамкнут. Однако, возможно, в их реализации булевых операций они не требуют явного замыкания, а просто работают с множеством точек? Нет, булевы операции требуют замкнутых областей.
Давайте проверим другой скрипт: в drops.py они рисуют окружность, используя четыре кривых узла, и последний узел заканчивается в начальной точке. Там они явно делают так, что последняя точка равна первой. Так что для окружности они замыкают.
Вероятно, правильный способ - сделать так, чтобы последняя добавленная точка была равна первой. Для многоугольника это означает, что нужно добавить все точки, и последняя должна быть первой. Но тогда у нас будет дублирование узла: первый и последний с одинаковыми координатами. Это допустимо, и тогда контур будет состоять из сегментов: от первого до второго, ..., от предпоследнего до последнего, и последний сегмент соединяет последний с... ничем? Нет, последний сегмент - это от предпоследнего до последнего, а последний узел - это конечная точка. Чтобы был сегмент от последнего до первого, нужно добавить еще один узел, равный первому. Тогда мы получим два узла в одной точке: первый и последний, и между ними есть сегмент от предпоследнего до последнего, но сегмента от последнего до первого нет. Чтобы он был, нужно, чтобы последний узел был не конечным, а после него шел еще один сегмент в первый. Это значит, что мы должны добавить после последнего еще один line в первую точку, создав тем самым дополнительный узел. Тогда у нас будет последовательность: p0 (move), p1, p2, ..., pn-1 (последняя точка исходного списка, которая не равна p0), затем pn = p0 (line). В итоге мы имеем p0 дважды: как move и как line в конце. Контур будет состоять из сегментов: p0-p1, p1-p2, ..., pn-2 - pn-1, pn-1 - pn (где pn = p0). Это дает замкнутый контур, но с повторяющейся точкой p0. Это приемлемо.
Таким образом, для каждого контура мы можем:
- Взять список точек (пары координат).
- Создать первый узел как move в первую точку.
- Для всех точек, начиная со второй и до конца (включая последнюю, если она не равна первой? Но в наших данных последняя равна первой, поэтому мы пропустим последнюю, чтобы избежать дублирования? Если пропустить, то у нас будет список от p0 до p_{n-1}, где p_{n-1} - предпоследняя. Затем добавим line в p0. Это даст замкнутый контур без дублирования. Но тогда мы потеряем последнюю точку, которая равна p0, и это нормально. Однако если в контуре есть точки, которые не дублируются, то последняя точка должна быть добавлена как line, а затем еще одна line в первую. В нашем случае последняя точка равна первой, поэтому мы можем просто не включать её в список, а после всех добавить line в первую. Это даст правильный замкнутый контур без лишней точки.
Проверим на первом контуре: список из 10 точек, где последняя совпадает с первой. Значит, у нас есть 9 уникальных точек? На самом деле первая и последняя одинаковы, но остальные могут быть разными. Итак, добавляем move в p0, затем для точек с 1 по 8 (индексы 1..8) добавляем line, затем добавляем line в p0. В итоге получим 9+1=10 узлов? Первый move, затем 8 line (для точек 1-8), затем последний line в p0 - всего 10 узлов. При этом p0 встречается дважды: в начале и в конце. Это нормально. Но можно было бы не добавлять последний line, если бы мы добавили точку 9 (которая p0) как line, тогда было бы: move p0, line p1,..., line p8, line p9 (p0) - тоже 10 узлов, но тогда последний сегмент от p8 до p9, и нет сегмента от p9 до p0. Контур не замкнут. Так что вариант с дополнительным line в p0 после всех - правильный.
Итак, алгоритм:
- Получить текущий глиф (fl.glyph). Если нет активного глифа, выдать сообщение.
- Очистить глиф: удалить все узлы. Можно использовать glyph.Clear() или удалить все контуры. В FL5 есть метод glyph.Delete(0, len(glyph)) для удаления всех узлов.
- Задать масштабирование: исходные координаты имеют bounding box (xmin, ymin) = (44.65, 20.0), (xmax, ymax) = (355.35, 380.0). Ширина = 310.7, высота = 360.0. Целевые размеры: 600x700. Вычисляем масштаб s = min(600/310.7, 700/360) ≈ 1.93. Также нужно сместить, чтобы символ оказался в центре или по базовой линии? Обычно базовые линии шрифта: baseline y=0, а x-height и cap height положительны. В исходных данных ymin=20, ymax=380, так что символ расположен выше baseline на 20. Чтобы он стоял на baseline, нужно сдвинуть вниз на 20. После масштабирования ymin станет 20*s, ymax=380*s. Чтобы baseline была внизу, нужно сдвинуть на -20*s. Но также можно оставить как есть, если мы просто рисуем в окне глифа. Обычно в глифе координаты могут быть любыми. Для удобства можно сделать так, чтобы нижняя точка была на y=0. Для этого после масштабирования вычтем ymin_new = 20*s из всех y. Тогда нижняя точка станет 0, верхняя станет (380-20)*s = 360*s. Аналогично по x: можно центрировать, но не обязательно. Просто оставим как есть, чтобы символ был в пределах положительных x. xmin=44.65, после масштабирования xmin станет 44.65*s, что около 86, это нормально. Можно также сдвинуть влево на xmin, чтобы начать с 0. Но тогда ширина останется 600. Для унификации лучше сдвинуть так, чтобы символ начинался с x=0? Или оставить как есть. Поскольку мы хотим вписать в 600, то после масштабирования ширина будет 600, а xmin будет 44.65*s ≈ 86, так что символ будет от 86 до 686, что выходит за 600? Нет, 86+600=686, это в пределах 686, но у нас целевая ширина 600, значит, мы хотим, чтобы символ занимал ширину 600, т.е. от 0 до 600. Поэтому нужно сдвинуть на -xmin*s.