Статья посвящена автоматизации InDesign на платформе Mac OS X средствами AppleScript. В качестве примера рассматривается полезный сценарий «Каталог изображений». Он прекрасно иллюстрирует создание и манипуляции текстовыми и графическими объектами в InDesign.
Объектная модель
Написание сценария начинается с изучения объектной модели приложения. В общем случае, сперва надо изучить иерархию объектов, их свойства и команды, с которыми будущий сценарий будет работать. Вся подробная информация о них находится в редакторе сценариев (словарь интересующей программы).
На вершине иерархии – объект application. В InDesign, как и во многих программах, в его свойствах определяются характеристики самого приложения. Там же содержатся и некоторые параметры, например: file path, name, version и др. Такие свойства обычно предназначены только для чтения. Для нашего будущего сценария интересны свойства document preferences (параметры документа), view preferences (вид), margin preferences («настройки полей»).
Чтобы интерпретатор AppleScript (далее – просто AppleScript) понял, что мы обращаемся именно к свойствам объекта application программы Adobe InDesign, надо указать на этот объект. Есть два способа, наверняка знакомые тем, кто уже писал сценарии. Первый – с помощью ключевого слова of.
set AppName to name of application "Adobe InDesign"
Второй – с помощью операторных скобок tell... end tell.
tell application "Adobe InDesign" set AppName to name end tell
Elements в словаре приложения – это объекты, стоящие ниже в иерархической структуре. Следующий уровень на рис. 1 выделен жёлтым цветом: document, window, swatch, color, printer style и др. Более других нас интересует первый. В программе может быть открыто несколько документов, и для AppleScript важно знать, с каким придётся работать. Для этого после названия объекта указывается параметр, однозначно определяющий его среди таких же объектов: индекс (порядковый номер), имя, уникальный номер (ID) и определение по условию (before, after, front и др). В словаре программы в разделе elements есть подобная строка: document by numeric index, by name, satisfying a test. Зачастую сценарии пишутся для работы с активным документом, окно которого находится поверх остальных, поэтому первые строки сценария будут содержать выражение tell document 1.
Сиреневым цветом выделены объекты, дочерние для document. Внимательный читатель уже заметил, что объекты разных уровней содержат одинаковые дочерние. У объекта application и document есть font, а у document и page item – rectangle. Это не ошибка. Забегая вперёд, приведу следующее сравнение: вспомните, как вы находите изображение в документе? Сначала открываете его, переходите на нужную страницу или разворот, визуально находите нужное среди других изображений и текста, только после этого совершая над ним какие-либо манипуляции. То же самое в сценарии выглядит так:
tell page item 1 of page 5 of document 1 get link 1 of image 1 end tell
Можно и по-другому – открыть документ, выбрать в палитре Links строку с именем изображения и нажать кнопку Go To Link. В сценарии:
tell page item 8 of document 1 get link 1 of image 1 end tell
То есть объект doсument содержит всю коллекцию объектов page item со всех страниц, а page – только тех, что принадлежат данной странице. Выражения page item 1 of page 5 of document 1 и page item 1 of document 1 могут ссылаться на различные объекты и оказаться совершенно разными элементами оформления. Например, нарисованным овалом и текстом, при этом второе выражение ссылается на текст некой страницы, номер которой заранее неизвестен. На рис. 1 представлена схема в сильно сокращённом виде. Document и объект page item имеют схожие объекты в группах, выделенных сиреневым и розовым цветом. Подобные дочерние объекты имеют также page и spread. Но только в page item есть объекты image, EPS и PDF, и только объект text frame обладает дочерним объектом text. Схожая ситуация и в группе объектов, выделенных голубым цветом. Очевидно, что character будет дочерним объектом для text, line, paragraph, word, а word – для text, line, paragraph и т. д. На этом мы закончим изучение объектной модели и перейдём к написанию сценария.
Сценарий
Постараемся определить все параметры, влияющие на создаваемый документ в начале сценария. Поскольку он не будет обладать пользовательским интерфейсом, их легко откорректировать в редакторе, задавая как свойства сценария. Они аналогичны глобальным переменным – доступны из любого места сценария и сохраняют значение при каждом его запуске.
Допустимые для помещения в каталоги типы файлов и их расширения:
property ApprovedFileTypes : {"EPSF", "TIFF", "JPEG"} property ApprovedFileExtensions : {"eps", "tif", "jpg"}
Типы и расширения заданы списком строковых значений. Далее зададим параметры создаваемого документа InDesign (мм):
property PageWidth : 210 - ширина property PageHeight : 297 - высота property Margins : 10 - поля
Теперь опишем дополнительные параметры: количество колонок, строк и расстояние между колонками.
property ColumnsCount : 3 property RowsCount : 4 property Gutters : 5
Подписи к изображениям будут набраны шрифтом Minion Pro Regular (8 пунктов).
property FontFace : "Minion Pro Regular" property FontSize : 8
Определив внешний вид документа для каталога, проведём некоторые вычисления, результаты которых будут использоваться в ходе сценария, чтобы максимально сократить количество предварительно задаваемых параметров, а расчёт остальных предоставить сценарию.
Подсчитаем ширину колонки документа и высоту строки каталога: должны поместиться изображение и подпись к нему. Для наглядности начертим схему (рис. 2). Вычитаем из ширины полосы поля слева, справа и межколоночное расстояние. Остаток делим на количество колонок. Аналогично рассчитываем высоту строки, за исключением промежутка между строками.
2. Чертёж, поясняющий математические расчёты |
set ColumnWidth to (PageWidth - 2 * Margins - (Gutters * (ColumnsCount - 1))) / ColumnsCount set RowHeight to (PageHeight - 2 * Margins) / RowsCount
Для создания каталога необходимы файлы. Ими заведует Finder, к которому мы обращаемся командой tell. Заметьте, что команда activate уже не нуждается в указании объекта активации, потому что выражение tell application "Finder" задаёт объект, используемый по умолчанию для всех команд, следующих до появления в сценарии выражения end tell.
tell application "Finder" activate
Чтобы не вводить вручную пути к каталогизируемым файлам, используем команду из стандартных дополнений (Standart Additions): choose folder. У неё один-единственный параметр, вводимый после ключевых слов with prompt. С его помощью меняется строка текста, отображаемая сверху стандартного диалога выбора папки. Значение класса alias, возвращаемое командой, присваиваем переменной Cataloging Folder.
set CatalogingFolder to (choose folder with prompt "Выберите папку для каталогизации")
Готовим переменную для хранения списка файлов каталога.
set FilesForCatalog to {}
.и, перебирая выбранную ранее папку в поиске файлов с определённым типом или расширением имени, помещаем ссылки на них в список FilesForCatalog:
repeat with x in folder CatalogingFolder if kind of x is not "folder" or kind of x is not "alias" then if file type of x is in ApprovedFileTypes or name extension of x is in ApprovedFileExtensions then set end of FilesForCatalog to (x as alias) end if end repeat
Оператор управления repeat получает в качестве параметра список объектов, находящихся в выбранной папке. Сколько их будет, столько раз повторятся выражения, записанные между repeat и end repeat. При этом переменной x при каждом повторе присваивается ссылка на следующий объект в списке (это могут быть файлы, папки и ярлыки). А если в папке попадутся файлы, неподходящие для нашего каталога (текстовые документы или иных программ, звуковые файлы)? Условным оператором if сначала отсеем папки и ярлыки. При помощи второго условного оператора выберем файлы только подходящего типа. Это выражение можно прочитать так: если тип файла x есть в списке «допустимых типов файла» или расширение имени файла x есть в списке «допустимых расширений», то добавить в конец списка «файлы для каталога» текущее значение х как класс alias. (В Mac OS 9.2.2 нет свойства name extension, возвращающей расширение имени файла. Она появилась только в Mac OS X.)
Завершаем работу с Finder:
end tell
И переходим, наконец, к работе с InDesign:
tell application "InDesign 2.0.1 CE"
Не стоит обращать внимание, что имя версии программы не соответствует установленной на вашей машине. При открытии или компиляции сценария AppleScript автоматически заменит имя приложения на корректное.
Поскольку ранее мы активировали Finder, вызовем на передний план InDesign (предполагается, что он запущен до сценария).
activate
Сразу изменим установки создаваемого вновь документа (параметры, поля и вид) на более соответствующие задаче нашего сценария.
set properties of document preferences to {page height:PageHeight, page width:PageWidth, pages per spread:1} set properties of margin preferences to {margin top:Margins, margin bottom:Margins, margin left:Margins, margin right:Margins, column count:ColumnsCount, column gutter:Gutters} set properties of view preferences to {horizontal measurement units:millimeters, vertical measurement units:millimeters, ruler origin:page origin}
В InDesign все установки представлены в виде особых классов, собранных в пакет InDesign Preferences в словаре. У каждого из классов разные наборы свойств, но практически все имеют особое свойство properties (запись из всех свойств класса). Напомню, что в AppleScript записью называется список из значений с метками. Сначала идёт метка, потом значение; пары разделяются запятыми. В трёх описанных выше выражениях присваиваем записи properties другую запись: изменяются только те значения, имена меток которых совпадают справа и слева от ключевого слова to оператора присваивания.
В ходе сценария при создании текстовых фреймов, текста и фреймов для изображений понадобятся «шрифты» и «цвета» для заливки фона или обводки. Но объекты font и swatch недоступны из объектов text frame, или page, или spread, а лишь из application или document. Чтобы избежать в последующем прямых отсылок к объектам и длинных of-последовательностей, воспользуемся случаем и сохраним ссылки на объекты font и swatch, которые будем использовать в сценарии.
set FontForText to font FontFace set NoneSwatch to swatch "None"
Хотите лучше понять, как работают операторные скобки tell . end tell? Посмотрите в окно Event Log при работе сценария. Вы увидите, что строка swatch "None" заменяется на get swatch "None" of application "Untitled-1".
Создаём новый документ и ссылку на него присваиваем переменной CatalogDoc. Если в InDesign уже открыто один или несколько документов, указываем, что надо создать документ поверх остальных – at front.
set CatalogDoc to make new document at front
Сделаем документ CatalogDoc для команд AppleScript документом по умолчанию.
tell CatalogDoc
Теперь создадим три переменные и присвоим им значения, равные единице, – они будут использоваться в основном цикле сценария и постоянно меняться в зависимости от заполнения полос документа InDesign изображениями. Будущий каталог делится на страницы, те – на условные ячейки, заполняющие страницу в несколько колонок и строк (рис. 3). Сначала – первая страница: первое изображение попадает в ячейку первой строки первой колонки, второе помещается на вторую строку и т. д. После заполнения первой колонки сценарий переходит на вторую, третью, потом опять на первую колонку, но следующей страницы.
3. Схема каталога |
Переменные CurrentCellPage, CurrentColumn, CurrentRow указывают, на какой странице, колонке и строке находится ячейка, в которую станут очередное изображение и подпись к нему.
set CurrentCellPage to 1 set CurrentColumn to 1 set CurrentRow to 1
Мы подошли к основному циклу сценария – при каждой итерации переменной SomeFile будет присваиваться один из элементов собранного ранее списка файлов FilesForCatalog.
repeat with SomeFile in FilesForCatalog tell page CurrentCellPage
Чтобы поместить в документ InDesign изображение, надо создать для него контейнер. В QuarkXPress им для изображения был бокс (picture box), а в InDesign – прямоугольник (rectangle). Рассчитываем координаты верхнего левого (X1, Y1) и нижнего правого (X2, Y2) углов контейнера для изображения (рис. 2). При расчёте Y2 учтена высота текстового фрейма подписи. Под подпись оставляем три строки кеглем FontSize, учитываем интерлиньяж (120%) и переводим всё из пунктов в миллиметры, умножая на 0,353.
set Y1 to Margins + (RowHeight * (CurrentRow - 1)) set X1 to Margins + ((ColumnWidth + 5) * (CurrentColumn - 1)) set TextBox_Y2 to TextBox_Y1 + (FontSize * 3 * 1.2 * 0.353) set X2 to X1 + ColumnWidth
Теперь у нас есть всё для создания контейнера и вставки в него изображения. Синтаксис команды make практически во всех программах одинаков. После слова make следует new и имя создаваемого объекта. Далее указываем, где его создать – пишем at end, что в AppleScript расшифровывается примерно так: «в конце списка других прямоугольников страницы 1 документа "Untitled-1"». Начальные свойства объекта можно задать при его создании в виде записи после слов with properties. Для этого как раз и служит специальное свойство properties, о котором уже говорилось ранее (оно присутствует у всех объектов). Среди свойств можно увидеть, как мы используем ссылку на цвет NoneSwatch для задания цвета линии прямоугольника.
set ImageContainer to make new rectangle at end with properties {label:SomeFile as string, geometric bounds:{Y1, X1, Y2, X2}, content type:graphic, stroke weight:0, stroke color:NoneSwatch} set PlacedImage to place SomeFile on ImageContainer without showing options
Пытливый глаз заметит, что в словаре для команды place требуется значение класса file specification, а мы в самом начале сценария собирали список файлов из значений класса alias. Зачастую заботиться о точности совпадения классов не нужно: AppleScript преобразует значение сам.
Если размер изображения больше контейнера, чтобы оно было показано в каталоге полностью, его надо масштабировать, пропорционально изменив масштаб командой fit.
set ImageBounds to geometric bounds of PlacedImage set LowRightCornerY to item 3 of ImageBounds set LowRightCornerX to item 4 of ImageBounds if LowRightCornerY > Y2 or LowRightCornerX > X2 then fit PlacedImage given proportionally
Той же командой отцентруем изображение в контейнере.
fit PlacedImage given center content
Геометрические параметры текстового фрейма (для удобства назовём его TextBox) мы отчасти рассчитали ранее и теперь только присвоим их значения другим переменным. При расчёте положения по высоте (Y2) нижнего правого угла используем формулу, аналогичную той, что применялась при расчёте прямоугольника изображения, только теперь со знаком плюс.
set TextBox_Y1 to Y2 set TextBox_X1 to X1 set TextBox_Y2 to TextBox_Y1 + (FontSize * 3 * 1.2 * 0.353) set TextBox_X2 to X2
Создаём текстовый фрейм.
set TextBox to make new text frame at end with properties {label:SomeFile as string, geometric bounds:{TextBox_Y1, TextBox_X1, TextBox_Y2, TextBox_X2}, content type:text type, stroke weight:0} tell TextBox
Заполняем текстовый бокс информацией. Для её получения используются две небольшие подпрограммы. При их вызове необходима ссылка на объект-сценарий my (синоним of me), чтобы дать понять AppleScript – команды GetFileName и GetDimensions-String принадлежат именно нашему сценарию, а не объекту text frame документа InDesign.
set text 1 to my GetFileName(PlacedImage) & return & my GetDimensionsString(ImageBounds)
Задаём различные параметры для фрейма и текста.
set inset spacing of text frame preferences to 0.353 set point size of text 1 to FontSize set applied font of text 1 to FontForText set font style of paragraph 1 to "Bold" set justification of text 1 to center end tell
Создаём прямоугольник, который будет служить рамкой вокруг изображения и подписи. Размеры прямоугольника в данном случае равны размерам нашей условной ячейки. Ширина линии задаётся в пунктах.
set Y2_Border to Y1 + RowHeight make new rectangle at end with properties {label:"Border", geometric bounds:{Y1, X1, Y2_Border, X2}, stroke weight:1, content type:none} end tell
Все работы по заполнению ячейки закончены, а сценарий надо направить на следующую ячейку. Изменяем сначала номер текущей строки; если окажется, что он превысил количество строк, корректируем номер колонки, а для строки задаём 1. Условное перекрестие строки и колонки однозначно определяет ячейку (рис. 3). Если номер колонки превысит их количество, создадим новую страницу в конце документа и установим значение переменной CurrentCellPage на 1.
set CurrentRow to CurrentRow + 1 if CurrentRow > RowsCount then set CurrentRow to 1 set CurrentColumn to CurrentColumn + 1 if CurrentColumn > ColumnsCount then set CurrentColumn to 1 set CurrentCellPage to CurrentCellPage + 1 if not (exists page CurrentCellPage) then make new page at end end if end if end repeat end tell end tell
Первая подпрограмма, как видно из названия, служит для определения имени файла изображения.
В качестве параметра она должна получить ссылку на объект image. Благодаря усилиям разработчиков InDesign нет нужды заниматься строковыми преобразованиями пути файла – достаточно использовать свойство name объекта item link. Результат подпрограммы в виде значения класса string возвращается командой return.
on GetFileName(ImageReference) tell application "InDesign 2.0.1 CE" set FileName to name of item link of ImageReference return FileName end tell end GetFileName
Подпрограмма GetDimensionsString используется для создания строки текста из геометрических координат изображения. В качестве параметра берётся класс fixed rectangle, похожий на список из четырёх значений, стоящих в таком порядке: {Y1, X1, Y2, X2}. Значение возвращается в формате «ШИРИНА х ВЫСОТА».
on GetDimensionsString(ImageBounds) set ImageBounds to ImageBounds as list set ImageWidth to (item 4 of ImageBounds) - (item 2 of ImageBounds) set ImageHeight to (item 3 of ImageBounds) - (item 1 of ImageBounds) set ImageWidth to RoundReal(ImageWidth, 3) set ImageHeight to RoundReal(ImageHeight, 3) return (ImageWidth as string) & "x" & (ImageHeight as string) end GetDimensionsString
Подпрограмма RoundReal округлит действительные числа с заданной точностью. Количество знаков после запятой сократится до указанного в параметре Precision.
on RoundReal(RealNumber, Precision) set Multiplicator to 1 repeat Precision times set Multiplicator to Multiplicator * 10 end repeat return (round (RealNumber * Multiplicator)) / Multiplicator end RoundReal
4. Результат работы сценария |
Ограничения
В MacOS X для AppleScript по-прежнему не решена проблема с символами кириллицы в путях файлов. Второе ограничение для нашего сценария несущественно: при вставке русского текста в документ InDesign средствами AppleScript, текст вводится как набор ASCII-символов, а не как принято в InDesign – в виде последовательности Unicode-символов "<0410><0411><0412><0413><0414>". Это ограничение не столь критично, как первое. Для перекодировки используйте специальные сценарии (см. сайт автора www.yezhe.ru/applescript/). Есть надежда, что к выходу этой статьи будут готовы дополнения для перекодировки текста в InDezign и исправления проблемы с русскими именами файлов (ищите там же).
Палитра Script Label
5. Палитра Script Label |
В сценарии при создании прямоугольника для изображения и текстовой подписи мы устанавливали в качестве значения одного свойства label строку пути к файлу. При выделении этого прямоугольника в InDesign палитра Script Label изменится, как показано на рис. 5. Для чего это надо? Чтобы палитра была полезной, удобной и сочеталась с описанным сценарием. С её помощью можно искать изображения в документе, созданном нашим сценарием, почти так же, как и во многих популярных каталогизаторах.
tell application "InDesign 2.0.1 CE" display dialog "Enter file name to find image" default answer "" set WhatToFind to text returned of result tell document 1 set FoundRectangles to (rectangles whose label contains WhatToFind) set FirstItem to item 1 of FoundRectangles if exists image 1 of FirstItem then show item link of image 1 of FirstItem if exists EPS 1 of FirstItem then show item link of EPS 1 of FirstItem if exists PDF 1 of FirstItem then show item link of PDF 1 of FirstItem select FoundRectangles end tell end tell
Заключение
Надеюсь, статья будет полезна тем пользователям настольных издательских систем, кто не любит монотонной работы и стремится самостоятельно создавать функции, отсутствующие в стандартных поставках программ или в списках дополнительных инструментов на versiontracker.
Подразумевается, что читатель, добравшийся до конца, уже знает немного AppleScript или основные приёмы программирования. Здесь демонстрируется, как AppleScript работает с InDesign, какие особенности надо учитывать при написании сценариев для него. И хотя пример невелик, он в полной мере объясняет методы работы с изображениями и текстом.
Закончив статью, я обнаружил, что мне и самому пригодится этот сценарий (после смены формата на 120х120 мм) – для создания обложки CD с изображениями изделий коллекции. Жаль, что его не было у меня полгода назад, когда пришлось готовить PDF, печатая из одного популярного каталогизатора изображений, а потом вставлять файл в InDesign, потому что каталогизатор был недостаточно гибок в настройках печати. Пишите сценарии и получайте удовольствие от настоящей, творческой работы!
Об авторе: Кирилл Корчагин (kirkor@yezhe.ru, www.yezhe.ru/applescript), независимый автор.
Подсказки
Быстрый набор
Конец операторных скобок полностью можно не набирать – достаточно только слова end. При компиляции AppleScript сам добавит конец оператора. Например, наберите:
repeat end
и нажмите кнопку «Check Syntax». Слово repeat будет добавлено после слова end автоматически.
Русские идентификаторы
Если вам лень писать длинные идентификаторы (имена переменных) из английских слов, сложно разбирать их сокращения в написанном сценарии или не нравится транслит, подберите в AppleScript для идентификаторов любое сочетание символов, включая кириллицу и пробелы. Для этого заключите идентификатор в скобки из символа «|» (вертикальная линия). Примеры:
set |Моё имя| to "Кирилл" set |(*Б‰?*)| to "Смайлик"
Синонимы
Используйте синонимы языка для быстрого набора сценария или для его удобочитаемости. Оператор неравенства записывается как is not equal или символ «?». Хоть неравенство в один символ написать быстрее, при передаче текста сценария по e-mail подобный символ недопустим.