2012.11.15, Автор: Михаил Борисов3632 прочтений

Сделай сам, или Автоматизация для бережливых

Теги: Adobe Magazine

Некоторые задачи, для которых предлагаются коммерческие расширения к Adobe InDesign, вполне по силам решить самостоятельно. Это будет полезно и для совершенствования навыков написания скриптов.

Publish неоднократно поднимал тему автоматизации рабочих процессов в InDesign, поскольку люди с большим удовольствием используют программу для задач, не связанных с дизайном и творчеством. Ранее публиковались материалы, касающиеся аспектов вёрстки, на этот раз рассмотрим оформление изображений. Для верстающих журналы добавление подписей об авторстве иллюстраций (credits) — неотъемлемая часть рабочего процесса. Некоторые разработчики ПО под InDesign, держа нос по ветру, предлагают коммерческие решения. Мы же сделаем своё, ничуть не уступающее по функционалу, зато совершенно бесплатное.

Первый способ

Большинство современных цифровых камер, кроме изображения, записывают в файл метаданные — информацию о съёмке. Она может быть чисто технической, необходимой для воссоздания условий съёмки (выдержка, экспозиция и пр.), и касающейся других сфер применения (информация об авторстве, время и координаты съёмки и т. п.). Для удобства их использования разработано несколько стандартов: для технической информации — EXIF, для остальной — IPTC. Оба поддерживаются большинством программ для просмотра изображений и наиболее полно — в Adobe Bridge, которая агрегирует данные, с помощью собственного механизма Adobe XMP. Поскольку серьёзные фотографы всегда внедряют метаданные в свои снимки, в процессе вёрстки издания достаточно считать необходимую информацию из файла изображения, чтобы использовать в подписях.

В Adobe Creative Suite все составляющие взаимодействуют через Adobe Bridge, однако автор считает ненужным распылять внимание читателей на объектную модель последнего, потому попробуем найти более лёгкий способ выуживания нужной информации, используя только InDesign.

Анализ его объектной модели показал, что среди свойств изображений имеются несколько подходящих для нашего случая — Author (авторство, информация берётся из поля IPTC Creator) и documentTitle (описание, из поля IPTC Title). Прочие для нас не актуальны. Доступ к этим полям непосредственно из InDesign — большое подспорье для нас: реализация подобного, например, в Illustrator потребовала бы гораздо больше сил (пришлось бы использовать механизм Bridge). Автор столкнулся с этим при написании скрипта, который выполняет в Illustrator допечатную проверку макета (сродни Preflight в InDesign).

Итак, нам повезло с прямым доступом к метаданным. Следовательно, достаточно считывать свойства Author и documentTitle и вставлять в отведённый для этого блок текста, предварительно отформатировав.

Что для этого нужно? Скрипт должен знать, из какого изображения брать информацию, какую именно и где размещать, способ форматирования — не только стилевое оформление, но и вводное слово, если это предусмотрено (например, «Автор:»).

Проще всего задавать изображения, выделяя мышкой (в объектной модели InDesign есть объект Selection). Если выделено несколько, скрипт будет обрабатывать все (Selection в общем случае — набор объектов, выделенных на полосе).

К заданию полей для считывания есть два подхода: выводить диалог, в котором каждый раз задавать требуемые параметры, либо записать их в отдельном файле, откуда они будут считываться при запуске скрипта. На мой взгляд, удобнее второе, поскольку параметры для издания задаются, как правило, единожды и меняются редко. Если же такое произойдёт, можно этот файл настроек открыть и подкорректировать. Его примерное содержание:

Поле: Author

Вводное слово: Автор:

Положение: (удобно использовать принцип часовой стрелки: cверху — 1, справа — 2, снизу — 3, слева — 4).

Стиль оформления: Caption

Положение текста во фрейме, как и оформление, удобно задавать через стиль абзаца (Caption).

Однако я предлагаю вместо файла настроек использовать окно диалога. Люди хотят получать готовые решения при минимальных усилиях, что и достигается через диалог. В противном случае нужно создать файл с настройками, а потом не забыть, где он находится; кроме того, можно случайно ввести не тот символ (например, кириллическую «с» вместо латинской «c», на что скрипт будет реагировать сообщением об ошибке). Ещё один потенциальный плюс такого подхода — гибкость в задании положения информационного блока для каждого изображения в отдельности.

Итак, приступим. Сразу даём сокращённые названия часто употребляемым значениям (документу и хранящимся в нём стилям абзацев):

aD = app.activeDocument;

styles = aD.paragraphStyles;

После запуска скрипта в окне диалога должен отобразиться список всех доступных в документе стилей, из которого выберем необходимый. Чтобы список получить, «пробегаем» по стилям и запоминаем их названия (первые два исключаем — это технические стили InDesign, в данном случае не актуальны).

allStyles = new Array();

for(i=2; i<styles.length; i++)

allStyles.push( styles[i].name );

Теперь всё готово для отображения окна диалога Creating Caption:

myDialog = app.dialogs.add({name:»Creating Caption»});

with(myDialog){

  dialogColumns.add().staticTexts.add({staticLabel:»Position:»});

  myPosition = dialogColumns.add().dropdowns.add ({

  stringList:[«LeftSide», «Bottom», «RightSide»], selectedIndex: 2});

  dialogColumns.add().staticTexts.add({staticLabel:»Style:»});

  myStyle = dialogColumns.add().dropdowns.add ({

  stringList: allStyles, selectedIndex: 0} );

  dialogColumns.add().staticTexts.add({staticLabel:»Separator:»});

  myText = dialogColumns.add().textEditboxes.add({

  editContents:», «, minWidth:20});

}

Принцип построения окна диалога следующий: окно рассматривается как обычная таблица, в колонках расположены все её элементы (текстовые подписи, поля для выбора, радиокнопки, флажки и т. п.). В нашем диалоге используем только 3 типа элементов пользовательского интерфейса: неизменяемый текст (staticText, сама подпись задаётся в его свойстве staticLabel), выпадающий список (dropdown, для вывода списка абзацев) и поле для ввода произвольного значения (textEditbox, задаёт вводное слово, если таковое требуется, через свойство editContents).

Работа со списками строится так: скрипт использует не названия элементов, а их порядковые номера в списке; selectedIndex — номер, который будет выбран изначально (2), соответствует расположению блока на правой стороне. Где именно — вверху или внизу, — будет определять выбранный стиль абзаца (например, свойство Justification, выключка), список всех существующих стилей allStyles выводится в другом всплывающем списке.

По умолчанию новое окно невидимо. Метод show() делает его видимым, после чего считываем все введённые значения:

if( d.show() ){

myPosition = myPosition.selectedIndex;

myStyle = myStyle.selectedIndex;

myText = myText.editContents };

Теперь вся необходимая информация собрана, доступ к выделенным объектам получим через объект selection. Для простоты далее рассматривается ситуация с единственным выделенным объектом. Если не выбрано ничего или же выделенное контейнером не является изображением, скрипт сообщает об ошибке и прекращает действия (свойство constructor определяет тип объекта, length — количество объектов). Для удобства доступ к первому (и единственному) элементу типа selection:

mySelection = aD.selection;

if(mySelection.length==0 &&

mySelection[0].images.length==0)

  alert(«Selection is not valid»), exit();

mySelection = mySelection[0];

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

gB = mySelection. geometricBounds;

top = gB[0], left = gB[1],

bottom = gB[2], right = gB[3];

Далее по названию находим тот стиль абзаца, который будет применяться к содержимому текстового блока (captionStyle). Сразу же вычислим необходимую высоту этого блока (blockHeight): в свойствах стиля абзаца находим размер текста (pointSize), немного его увеличиваем, чтобы текст гарантированно уместился с учётом разных выступающих элементов (+20% вполне достаточно). Учитываем, что в качестве единиц измерения в документе обычно выступают миллиметры, а размер текста задаётся в типографских пунктах (point), поэтому делаем пересчёт (pt2mm):

captionStyle = styles[ myStyle + 2 ];

blockHeight = captionStyle.pointSize * 1.2;

pt2mm = 0.353;

Настала очередь «десерта» — для чего и задумывался скрипт — работы с содержимым полей с информацией об авторстве и, возможно, описанием. Эти свойства хранятся в самом файле изображения (доступ через свойство itemLink), а доступ к полям осуществляется через свойства author и documentTitle по собственной технологии описания метаданных Adobe XMP (adobe.com/products/xmp/). Не будь её, поиск нужной информации среди разветвлённой системы метаданных был бы весьма затруднён. Если описание изображения не задано, скрипт просто игнорирует это, а в случае отсутствия обязательного поля (авторства) выдаёт сообщение об ошибке и останавливается (можно предусмотреть выполнение скрипта независимо от ошибок, а в конце выдать список проблемных файлов).

Ещё один момент: когда мы выделяем изображение (инструмент — чёрная стрелка), выделяется контейнер, а не оно само, т. е. не носитель метаданных. Чтобы выбрать именно изображение, нужно переключаться на белую стрелку, что не удобно, поэтому предусмотрим в скрипте, что основным объектом воздействия у нас будет не mySelection, а mySelection.images[0]. В результате получаем:

image = mySelection.images[0];

imageFile = image.itemLink;

metadata = imageFile.linkXmp;

if ( typeof (metadata.author! = undefined) ){

  byLine = metadata.author;

} else

alert(‘Author not specified’), exit();

if ( typeof (metadata.documentTitle! = undefined) )

  titleLine = metadata.documentTitle;

Финальные шаги: создаём по ширине контейнера текстовый блок для подписи изображения и наполняем его содержимым. Необязательное свойство label (пометка) заполняем, чтобы при необходимости собрать статистику использования материалов по всему выпуску (именно так поступает издательская система Woodwing). Текст (обращаемся к нему через texts[0], поскольку в текстовом блоке могут существовать изображения в виде связанной графики и даже таблицы) форматируем выбранным ранее стилем captionStyle:

Block = aD.textFrames.add( {

geometricBounds: [bottom, left, bottom + blockH * pt2mm, right],

contents: byLine + myText + titleLine,

label: byLine} );

Block.texts[0].appliedParagraphStyle = captionStyle;

Скриптинг под InDesign отличается от логики остальных продуктов Adobe. Например, все размеры он выдаёт в единицах измерения, установленных в документе, а внутреннее представление у него — в типографских пунктах (point), причём это касается всех объектов, а не только текста. Если хотим задать размеры, положение на странице, мы должны учитывать эту особенность, переходя от миллиметров к типографским пунктам (pt2mm):

if( pos==0 || pos==2 ) myVerticalTranslate=0;

else myVerticalTranslate =

— (bottom-top — (right-left)) / pt2mm;

if( pos==0 || pos==3 ) myHorizontalTranslate = -blockH;

else myHorizontalTranslate = (right-left) / pt2mm;

В данном блоке мы готовимся задать параметры для перемещения текстового блока. В зависимости от конечного положения задаём то или иное смещение по горизонтали или вертикали в пунктах. Если положение текста боковое (слева или справа), то блок с текстом поворачиваем на 90°.

Тут мы подошли ко второй особенности скриптинга под InDesign: если в Illustrator, Photoshop есть простые операции по трансформации объектов (повернуть, передвинуть и т. п.), то в InDesign (начиная с версии Creative Suite 3) всё сложнее. Сначала создаются матрицы трансформаций (по одной на каждую, в нашем случае это будут матрицы для вращения rotationMx и перемещения moveMx), которые сначала объединяются (resultMx), и лишь затем к ним применяется сложная трансформация. Это несколько усложняет код, но даёт значительное преимущество в быстродействии, поскольку вместо множества последовательных трансформаций выполняется одна комплексная (выигрыш во времени хорошо заметен при обработке большого количества объектов).

transformMx = app.transformationMatrices;

rotationMx = transformMx.add({ counterclockwiseRotationAngle: 90 });

moveMx = transformMx.add({

horizontalTranslation: myHorizontalTranslate,

verticalTranslation: myVerticalTranslate});

resultMx = rotationMx.catenateMatrix( moveMx );

Block.transform(

CoordinateSpaces.innerCoordinates,

AnchorPoint.topLeftAnchor,

resultMx, undefined, true);

Полное описание метода transform() займёт не одну страницу текста. Интересующиеся могут скачать из Сети руководство Transform Tutorial JavaScript, где подробно разъяснены многие вопросы, но даже оно не раскрывает всех нюансов. Для нашего случая важно, что мы выбрали собственную систему координат текстового блока (innerCoordinates), осью вращения (AnchorPoint) задали его левый верхний угол, а комплексное преобразование — результат слияния вращения с перемещением по обеим осям.

Финальный шаг: если? выбрано боковое расположение блока с текстом или сверху, то выключку для текста меняем на правую:

if(pos==3 || pos==4)

Block.texts[0].justification = Justification.rightJustified;

Вот, собственно и всё. Пользуйтесь на здоровье!

Второй способ размещения надписей

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

Изменения в первом скрипте потребуются минимальные, поскольку другим будет лишь окно диалога: нужно предусмотреть в нём задание диапазона страниц, на которых располагается статья. Их можно получить и автоматически, определив положение начального и конечного текстовых контейнеров текущего материала (Story). Но такой подход не всегда корректен: часто возникают ситуации, когда начало статьи представляет собой изображение на всю страницу плюс заголовок, а сам материал начинается со следующей. Чтобы скрипт корректно работал, понадобится связывать заголовок с содержательной частью статьи, что не всегда воспринимается положительно, поскольку это изменения в уже устоявшемся процессе, а отказ от привычек — процесс гораздо более сложный, чем написание скрипта. Поэтому пойдём самым простым, но стопроцентно надёжным способом — будем указывать страницы вручную (информация доступна через панель Pages либо через всплывающую панель внизу рабочего окна).

А дальше так: просматриваем одну за другой все страницы, через механизм Adobe XMP получаем искомые метаданные (недаром название свойства несёт аббревиатуру XMP), запоминаем их и выводим консолидированную отчётность в виде: «Иванов (2), Петров (10), Сидоров (5)», где цифры в скобках отображают количество опубликованных изображений автора. Скрипт настолько прост, что самая сложная его часть — форматирование полученных данных заданным способом, что к InDesign не относится, — это чистый Java-Script.

Начало традиционное: задаём ссылки на объекты, которые будем использовать. Собираем информацию о доступных в документе стилях и получаем ссылку на текущую страницу (currPage). Для чего? В окне диалога нужно задать диапазон просматриваемых страниц; чтобы не оставлять поля пустыми, подставим в качестве исходной страницы текущую. Да и сидящему за компьютером удобнее — достаточно будет лишь указать длину статьи (например, задать в поле для последней страницы «+3»), и скрипт поймёт, что нужно анализировать кроме текущей ещё три страницы. Если удобнее задавать реальные номера страниц — нет проблем: например, диапазон «25–29» тоже воспримется корректно.

Внутренняя нумерация в InDesign ведётся от первой страницы, вторая получает смещение (свойство offset) = 1 и т. д. Интересно, что родитель у страниц — окно вёрстки (layoutWindow), хотя, казалось бы, им должен быть текущий документ. Согласитесь, весьма оригинальный подход. Сделано это по двум причинам. Во-первых, для большей гибкости при скриптинге — можно даже управлять видимостью макета в окне документа и т. п. Во-вторых, во избежание неоднозначности: документ, в целях удобства работы с ним, может отображаться в нескольких окнах программы, причём с разными видимыми страницами и масштабами отображения.

А с диалогом всё понятно: задаём начальную, конечную страницы и стиль, которым будет форматироваться информация об авторстве.

var aD = app.activeDocument;

var Pages = aD.pages;

var styles = aD.paragraphStyles;

var stylesList = [], byAll=’’;

var currPage = aD.layoutWindows[0].activePage.offset+1;

for(var i=2; i<styles.length; i++)

  stylesList.push(styles[i].name);

var dlg = app.dialogs.add({name:»Specify Info», canCancel:true});

with(dlg){

dialogColumns.add()

. staticTexts.add({staticLabel:»Pages to seek:»});

  var pageStart = dialogColumns.add()

. textEditboxes.add({editContents:currP});

  dialogColumns.add().staticTexts.add({staticLabel:» thru «});

  var pageFinal = dialogColumns.add()

. textEditboxes.add({editContents:currP});

dialogColumns.add()

. staticTexts.add({staticLabel:»Style:»});

  var captionStyle = dialogColumns.add()

. dropdowns.add ({stringList:stylesList, selectedIndex: 0} );

}

Делаем диалог видимым и проверяем содержимое полей. Особенно нас интересует поле, в котором задаётся конечная страница: если перед числом стоит «+», значит, указан не реальный номер страницы, а длина статьи. Например, если она занимает 4 полосы и начинается с 10-й страницы, правильным будет для конечной задать либо «14», либо «+3» (кроме текущей, просмотреть ещё три). Надеюсь, постоянные читатели уже знакомы с регулярными выражениями, поскольку без них при работе с текстом не обойтись никак.

Для остальных напомню: специальный символ «^» указывает на то, что искомое выражение ищется в самом начале текста, «$» — в конце. Поскольку в строке с конечной страницей может использоваться «+», который в регулярных выражениях является специальным символом, для корректной работы скрипта защищаем его (меняем на «\+», и он будет восприниматься как обычно). Метод substring(1) из строки «+3» удаляет первый символ, что даст «3», которое потом добавляется к начальной странице. Метод parseInt используется для преобразования текста (все заполняемые поля диалога — текстовые) в цифру (т. е. «три» переводится в «3»), чтобы полученное число можно было складывать с номером первой страницы.

Учитываем, что номера страниц у программы рассчитываются как смещение относительно первой:

if(dlg.show()){

pageStart = parseInt(pageStart.editContents)-1;

pageFinal = pageFinal.editContents;

if(pageFinal.match(/^\+/)! = null)

  pageFinal = pageStart + parseInt( pageFinal.substring(1));

else pageFinal = parseInt(pageStart)-1;

captionStyle = captionStyle.selectedIndex };

«Пробегаем» по всем страницам и отыскиваем на них изображения. Вариантов тут несколько, всё зависит от наших потребностей. Можно ограничиться растровыми форматами или просматривать все подряд (тогда программа будет пытаться найти авторство и у элементов дизайна, сохранённых в EPS/AI). В первом случае можно ограничиться файлами, имеющими расширение tif и jpg. Чтобы регистр значения не имел, поставим символ «i» (ignoreCase) в регулярном выражении, что позволит включить в поиск расширения TIF и JPG, а если добавить ещё «е?» и «f?», то будут искаться также jpeg, tiff, JPЕG и TIFF (итого 8 разновидностей).

for(var i=pageStart; i<=pageFinal; i++){

var Img = Pages[i].allGraphics;

  for(var j=0; j<Img.length; j++){

var myLink = Img[j].itemLink;

  if(myLink.name.match(/( tiff?|jpe?g )$/i )!=null){

Затем определяем авторство снимка. Если поле не заполнено (авторство по какой-то причине отсутствует), сигнализируем об этом и прекращаем работу, предварительно выделив контейнер с проблемным изображением для оперативного решения вопроса. Контейнер (frame) является родителем (parent) для изображения.

(1) var by = myLink.linkXmp.author;

if(by==’’){

  alert(‘Selected image isn\’t authored’);

  Img[j].parent.select(), exit();

Как быть с графикой, используемой для оформления разворотов? Элементы, повторяющиеся из номера в номер, предварительно можно пометить для игнорирования скриптом: свойство label (Img[j].parent.label) проще всего задать в панели Window/Utilities/Script Label, после чего применять их как элементы библиотеки — это свойство будет сохранено.

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

if(myLink.name.match(/^(_|!|#)+/)! = null) …

Если же авторство указано, запоминаем автора и ведём подсчёт количества принадлежащих ему работ в виде «Иванов (3)», точно так же для остальных. Тут желательно ориентироваться в регулярных выражениях. Если занимались веб-программированием и с «регэкспами» не сталкивались, можно решить задачу любым удобным способом.

} else {

(2) var re = new RegExp( by+’ \\()([0-9]+)’ );

(3) if( byAll.match(re)==null ) byAll += by+’ (1), ‘;

(4) else byAll.replace( re, ‘$1’+parseInt(‘$2’)+1 );

} } } }

Внимательно следите за кавычками — достаточно пропустить одну, и скрипт работать не будет. ‘$1’ подставит выражение, окружённое первой парой скобок в регулярном выражении, ‘$2’ — второй парой. В принципе, если нет желания разбираться с регулярными выражениями, просто скопируйте код — всё заработает без каких-либо вмешательств (он проверен).

После создания списка с авторами и частотой использования их материалов настаёт черёд добавить его в вёрстку. Для этого переходим на то место, где список должен быть размещён, например, на начальную страницу статьи и внизу, где и размещаем блок с собранной информацией (подробности описаны в предыдущей статье — http://www.publish.ru/publish/2012/04/19907300/), после чего тексту присваиваем стиль captionStyle.

var aD.layoutWindows[0].activePage = Pages[pageStart];

var gB = Pages[pageStart].geometricBounds;

blockH = styles[captionStyle+2].pointSize*1.2;

Block = aD.textFrames.add({

geometricBounds:[gB[2]-45, gB[1]+35, gB[2]-45+blockH, gB[3]/2]

, contents:byAll+».»});

Block.texts[0].appliedParagraphStyle= captionStyle;

Всё, задача решена. Для упрощения картины можно ограничиться заданием диапазона страниц по-простому (тогда не понадобится делать дополнительные проверки и усложнять код). Но если вы уже не новичок в программировании, то добавление подобной функциональности не составит никакого труда.

Присмотритесь к полученному скрипту: основная его часть — всего лишь четыре строчки (1–4), получение частоты использования авторских материалов. А диалог и обработка введённых данных — процедуры вспомогательные.

Заключение

Как видите, оба скрипта — относительно простые, при некотором знакомстве с объектной моделью программы их написание занимает час-другой, включая отладку. Надеюсь, мне удалось передать простоту и элегантность программирования под InDesign. Возможно, вам тоже захочется самим написать что-то своими руками. Поверьте, это будет увлекательно, а коллеги наверняка оценят ваш профессионализм. Удачи!

Об авторе: Михаил Борисов (bmike68b@gmail.com), пишет для Publish полезные советы по допечатной подготовке и обзоры ПО.

Меняем размеры страниц без мастера

Совет для тех, у кого установлена последняя версия пакета Creative Suite: в InDesign появился инструмент Page для изменения размеров страницы без использования мастер-страниц. Выбрав его, можно в самой вёрстке (не в панели Pages, это важно) выделить необходимое количество страниц. Программа воспримет выделение как подлежащий просмотру диапазон (знакомый объект Selection, только теперь состоящий из страниц).

 

Архив журналов в свободном доступе.

Купить номер с этой статьей в pdf

На ту же тему:
  • Начинаем с цвета: предварительная работа детектива *

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

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

     

     

  • Adobe Photoshop CC 2015: с юбилеем!
    Программе Adobe Photoshop — 25 лет! Пожалуй, непросто найти подобного долгожителя на рынке ПО. При этом наш долгожитель чувствует себя просто великолепно — посолиднел, помудрел, обзавёлся гаджетами. Но обо всём по порядку…
  • Самые «трогательные» интерфейсы
    Развитие инструментов для дизайна сенсорных интерфейсов.

comments powered by Disqus
301 Moved Permanently

301 Moved Permanently


nginx/1.4.1