101 СПОСОБ  ЗАРАБОТАТЬ   НА ПЕЧАТИ

Бой табличной скуке!

  • Михаил Борисов
  • 8 февраля 2008 г.
  • 5580
Автоматизируем форматирование таблиц.

Хотя InDesign CS3 стал поддерживать стили для таблиц, их форматирование с помощью скриптов не утратило актуальности: встроенные функции решают часть проблем, но все не охватывают. Первая причина — переход на версию CS3 произойдёт не сразу, многие ещё долго будут работать в InDesign CS2; вторая — скриптинг предоставляет ни с чем не сравнимую гибкость при решении задач, что позволяет полностью автоматизировать форматирование. Предлагаемый пример (я активно использую этот скрипт) — отправная точка для создания собственных скриптов по форматированию таблиц; кроме того, он решает вопросы оптимизации, т. е. универсален.

При импорте документа Word в InDesign параметры ячеек теряются (рис. 1, вверху). Наша задача — из полуфабриката создать полноценную таблицу (рис. 1, внизу).

Pис. 1. Таблица до и после

Предположим, вёрстка у нас двухколоночная, поэтому предусмотрим, чтобы таблицы были двух видов: узкие (в одну колонку) и широкие (занимающие всю полосу набора). Для заливки строк будем использовать два чередующихся цвета, цвет окантовки у всех ячеек выберем белым. Для текста в шапке таблицы возьмём стиль абзаца Tbl Hdr, для остального текста — Table contents.

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

Перечисляем необходимые параметры:

DEFAULT_SIDE_INSET = DEFAULT_STROKE_WIDTH =
"0.5 mm" ;
VERTICAL_INSET = "1 mm";
narrow = "120 mm";
wide = "174 mm";

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

Как вариант подойдёт способ принудительного переключения в нужные единицы:

app.activeDocument.viewPreferences.horizontal
MeasurementUnits = MeasurementUnits.centimeters;
app.activeDocument.viewPreferences.vertical
MeasurementUnits = MeasurementUnits.centimeters;

Нам придётся использовать цвета для заливки ячеек таблицы. В отличие от названия стиля, который непосредственно в скрипте задать нельзя (как мы его видим в палитре Paragraph Styles, например, «Table»), с цветом ситуация значительно проще. InDesign понимает их названия такими, как мы видим в окне Swatches, и для цвета содержимого таблицы можно записать:

alterFill = "alterFillColor"; для шапки: headerFill = "headerFillColor";

Разумеется, эти цвета перед запуском скрипта должны присутствовать в публикации. Устанавливаем ссылки на объекты, с которыми будем работать:

myDocument = app.activeDocument;

Стандартная строка для работы с любым выделенным объектом:

mySelection = myDocument.selection[0];

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

myTable = mySelection.parent.parent;

Прямой родитель у точки вставки — ячейка, а у неё — таблица, а у той — текстовый фрейм, в котором она находится.

myTextFrame = myTable.parent;

Для использования стилей публикации создаём ссылки на них:

parStyles = myDocument.paragraphStyles;

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

Создадим временное хранилище для них.

parStylesStorage = new Array();//хранилище для индексов стилей
flagStorage = new Array();//здесь будем хранить признаки наличия требуемых стилей.

Запоминаем индексы используемых стилей:

for (i=0; i< parStyles.length; i++){
switch(parStyles[i].name){
case ‘Table Header’: parStylesStorage [‘Table Header’] = i;
flagStorage [0]=true; break;
case ‘Table Footer’: parStylesStorage [‘Table Footer’] = i;
flagStorage [1]=true; break;
case ‘Table contents’: parStylesStorage [‘Table contents’] = i;
flagStorage [2]=true; break;
case ‘Table’: parStylesStorage [‘Table’] = i;
flagStorage [3]=true; break;
case ‘Tbl Hdr’: parStylesStorage [‘Tbl Hdr’] = i;
flagStorage [4]=true; break;
}}

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

if(flagStorage[0] && flagStorage[1] && flagStorage[2] && flagStorage[3] && flagStorage[4]) {

По умолчанию булевы выражения всегда сравниваются с true, поэтому не надо каждый раз использовать конструкцию типа if(flagStorage[…]==true).

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

myTable = mySelection.parent.parent;

потеряет смысл, т. к. жёстко привязано к конкретному типу выделенной области. Удостоверяемся, что выделение допустимо:

if (mySelection.constructor.name == "InsertionPoint" || mySelection.constructor.name == "Paragraph"){

Свойство constructor позволяет определить тип любого объекта и постоянно применяется для проверки корректности условий перед началом работы. Используется в связке со свойством name, позволяющим определять этот тип в удобной форме.

Приступаем к форматированию. Сначала присваиваем тексту в ячейках таблицы стиль Table contents:

for (i=0; i< myTable.cells.length; i++)…

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

При переборе ячеек пользуемся исключительно JavaScript — задаём цикл от самой первой ячейки и до последней. Универсальный подход редко бывает оптимален. Попробуем отыскать в самом InDesign методы, ускоряющие процесс. Диапазон методов, применимых к ячейке (Cell), разнообразием не блещет — типичный набор. А если посмотреть на методы, существующие для ячеек (Cells)? Тут нас ждёт находка: проблему решает метод itemByRange(). Он имеет два параметра, задающих диапазон ячеек, к которым InDesign обращается через свои внутренние механизмы (первый задаёт начальный объект, второй — конечный). Анализ производительности обеими методами (перебор средствами JavaScript и InDesign) дал убедительный результат: в среднем, скорость форматирования поднялась на порядок! Теперь нам по зубам и наисложнейшие таблицы.

Изменим часть скрипта, относящуюся к форматированию ячеек. Введём две переменные — для доступа ко всем ячейкам и ко всем строкам:

with(myTable.cells)cellsTbl = itemByRange(firstItem(), lastItem());
with(myTable.rows) rowsTbl = itemByRange(firstItem(), lastItem());

Для форматирования текста в таблице можем записать:

with(cellsTbl) {
texts[0].applyStyle(pS [pSA [‘Table contents’]], true);
texts[0] используется для обращения к текстовому содержимому ячейки, поскольку применить метод applyStyle() к ячейке нельзя. Первый параметр — индекс стиля (его мы определили раньше), второй параметр (true) говорит о том, что текущее форматирование текста будет очищено. Горизонтальное выравнивание в ячейке — по центру.
verticalJustification = 1667591796;

Следующий шаг — установка высоты для каждой ячейки соответственно её содержимому. В InDesign свойство autoGrow для строк и ячеек избавляет нас от решения задачи собственными методами:

autoGrow = true;

Задаём параметры оформления:

topInset = VERT_INSET;
bottomInset = VERT_INSET;
leftInset = DEF_SIDE_INSET;
rightInset = DEF_SIDE_INSET;}

На данном этапе все основные операции на уровне отдельных ячеек выполнены. Осталось заняться объектами более высокого уровня. Выполняем операции над таблицей:

with(myTable) {
alternatingFills = AlternatingFillsTypes.alternatingRows;//чередовать заливку по строкам

startRowFillCount = 1;
startRowFillColor = alterFill;//первый используемый цвет
endRowFillColor = "None";//второй используемый цвет
startRowFillTint = 100;//плотность цвета
skipFirstAlternatingFillRows = 1;//начальная строка

Следующий шаг — определение необходимой ширины таблицы. Если колонок больше 5, таблица однозначно должна быть широкой. Если меньше и пользователь не указал явно в окне диалога, что таблица должна быть широкой, делаем её узкой:

width = (columns.length < 5 || table_W == narrow) ? narrow : wide;

Настало время задать выключку влево для крайнего левого столбца. Метод itemByRange() воздействует только на те элементы, к которым он применяется, поэтому раньше (когда работали на уровне всех ячеек таблицы) мы не могли получить доступ к столбцам. Используем конструкцию rowsTbl и в каждом ряду воздействуем только на самую первую ячейку (порядок ячеек в InDesign — слева направо, поэтому для рядов cells[0] означает крайнюю левую ячейку):

with(rowsTbl){
cells[0].texts[0].justification = 1818584692;//выключка влево
topEdgeStrokeWeight = bottomEdgeStrokeWeight = 0;
}

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

Следующий шаг — задание ширины первой колонки. Частенько в таблице фигурируют одни цифры, и лишь первая колонка предназначена для текста — под работу именно с такими таблицами в первую очередь и создавался скрипт. Постепенно увеличиваем ширину первой колонки, пока текст не займёт одну строку. Если это невозможно (текста слишком много), ограничиваем макс. ширину колонки значением 5 см. Оптимальная величина шага — 1 мм.

Использовать конструкцию tbl.columns[0].cells.itemByRange()не получится, поскольку тут нам нужна работа не с массивом данных, а непосредственно с каждой ячейкой — ведь количество символов в них может не совпадать.

step = 0.1// единица измерения (сантиметры) установлена заранее
for (i = 0; i < myTable.rows.length; i++) {
with(myTable.rows[i].cells[0]) while(lines.length > 1 && width < 5) width = width + step;
}

Последний аккорд: оформляем разделительную сетку. Она должна быть только вертикальной (рис. 1):

with(myTable) {
for (i = 0; i < columns.length; i++) {
with (columns[i]) {
leftEdgeStrokeColor = aD.swatches[1]; rightEdgeStrokeColor = aD.swatches[1];
leftEdgeStrokeWeight = DEF_STROKE_W + " pt";
//явно указываем единицу измерения, иначе будут использоваться миллиметры
rightEdgeStrokeWeight = DEF_STROKE_W + " pt";

Изменяем ширину всех колонок с учётом новой ширины первой:

if(i>0) width=(table_W-parent.columns[0].width)/(parent.columns.length-1)
}}}

Не используем метод itemByRange (firstItem(), lastItem()), потому что нужно было сделать исключение для крайнего левого столбца; хотя, конечно же, можно было вместо firstItem() использовать nextItem (firstItem()). Присваиваем стили абзацам:

myParagraphs = mySelection.paragraphs;
curr_para = myParagraphs [0];
curr_para.applyStyle(pS [pSA [‘Table’]], true);
myParagraphs.previousItem(curr_para).applyStyle(pS[pSA[‘Table Header’]],true);
myParagraphs.nextItem(curr_para).applyStyle(pS[pSA[‘Table Footer’]],true);
if (table_W == Obj.w.wide)
with(myParagraphs) itemByRange(previousItem(curr_para), nextItem(curr_para)).leftIndent = 0;

Вот и весь скрипт. А как задать шапку? Если она простая (ячейки не объединены ни по вертикали, ни по горизонтали), делается это просто:

rowType = RowTypes.headerRow;

Задача усложняется, если ячейки объединены: при попытке выполнить такую, казалось бы, безобидную операцию InDesign аварийно заканчивает работу. Обходной путь: определяем ряд, в котором находится курсор (свойство ячейки name содержит номер колонки и ряда, в которых она находится, разделённые знаком «:»; разбиваем строку по этому разделителю, получаем массив строк и берём последний элемент, поскольку номер ряда идёт последним):

curr_row_name = sel.parent.name;
curr_row = curr_row_name.split(":")[1];
tbl.rows[0].select();

расширяем область выделения до той строки, в котором находится курсор

for (I = 1; I <= curr_row; i++) tbl.rows[i].select(SelectionOptions.addTo);
mySelection.rowType = RowTypes.headerRow;
mySelection.fillColor = headerFill;//заливка
mySelection.texts[0].applyStyle(pS[pSA[‘Tbl Hdr’]],true);//оформление стилем
myDocument.select(NothingEnum.nothing);//снятие выделения

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

 

Растягивание таблицы

Если в публикации несколько таблиц, часто возникает ситуация, когда нижний край таблицы немного не доходит до низа колонки набора — начинать новый абзац смысла нет, а оставлять «висельника» тоже нехорошо. Надо увеличить высоту таблицы за счёт добавления интервалов между ячейками так, чтобы она чётко вписывалась в оставшееся до низа колонки место.

Pис. 2. Растягиваем таблицу до края полосы набора

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

Устанавливаем знакомые из предыдущего примера ссылки:

mySelection = app.activeDocument.selection[0];
myTable = sel.parent.parent;
myParagraph = mySelection.storyOffset.paragraphs[0];
myTextFrame = sel.parentTextFrames[0];

Определяем реальную высоту таблицы и требуемую; результат делим на количество строк — это и будет то приращение, на которое нужно увеличить высоту строки.

delta = (myTextFrame.geometricBounds[2] — myParagraph.baseline)/ myTable.rows.length;

Дальнейшее имеет два сценария:

  1. Явная установка высоты ячейки (величина приращения нам уже известна).
  2. Изменение отступов сверху и снизу (предпочтительнее, поскольку не зависит от способа выравнивания текста в ячейке по вертикали).

Остановимся на последнем. Естественно, величина приращения для каждого отступа должна быть вдвое меньше, чтобы в итоге мы вышли на то же значение. Осталось предусмотреть вариант, когда строк не так много, а расстояние до низа колонки набора достаточно большое. Задаём порог (thresholdValue) — макс. значение высоты ячейки.

thresholdValue = «1 cm»;
for (i = 0; i < myTable.rows.length; i++) {
if (delta > thresholdValue) delta = thresholdValue;
myTable.rows[i]. topInset += delta / 2;
myTable.rows[i]. bottomInset += delta / 2;
}

Если строк много, вместо for (i = 0; i < myTable.rows.length; i++) можно использовать уже рассматривавшийся метод itemByRange().

Надеюсь, примеры помогут вам в освоении скриптинга. Удачи!

Об авторе: Михаил Борисов (mikebb@ukr.net), пишет для Publish полезные советы и обзоры ПО.


 

Универсальные методы

Для большей гибкости и удобства любая коллекция InDesign поддерживает методы: получить следующий объект (nextItem()), предыдущий (previousItem()), самый первый (firstItem()), последний (lastItem()), расположенный посредине (middleItem). Это позволяет не тратить время на получение конкретного индекса элемента в коллекции (абсолютное значение), а просто задавать относительное, что гораздо проще.

Комбинируя метод itemByRange() с методами, задающими относительное положение объекта, можно эффективно и просто управлять большими массивами объектов.

Обратите внимание, что параметры диапазона, как и itemByRange(), — это тоже методы. А поскольку они принимают параметры, можно обращаться через необходимую комбинацию к любому объекту, например, к предпоследнему: currentStory.paragraphs.previousItem (currentStory.paragraphs.lastItem()) и т. д.

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

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


 

Всегда в резерве

Многие свойства (verticalJustification, horisontalJustification, position и др.) могут иметь только зарезервированные значения. Их допускается задавать двумя способами: либо в текстовом виде (VerticalJustification.centerAlign, Position.Superscript), что увеличивает читабельность кода, либо в цифровом — читабельность приносится в жертву производительности, поскольку InDesign не нужно проводить дополнительного преобразования. Полный список зарезервированных значений (Enumerators) есть в руководстве по написанию скриптов.

Данное число соответствует выбранному нами типу выравнивания. Можно писать и по-другому: verticalJustification = VerticalJustification.centerAlign; Так понятнее, но несколько медленней, поскольку скрипту всё равно придётся выполнять это преобразование.

ПОХОЖИЕ СТАТЬИ
Управление цветом в Adobe Creative Cloud без секретов

Подробное руководство по настройке параметров управления цветом в программах Adobe.

InDesign осваивает ePub

Приёмы построения ePub в Adobe InDesign CC. Советы издательствам, как без лишних хлопот готовить книги для LitRes или Bookmate.


Реклама. Рекламодатель ООО "ТКС"
erid 2SDnjbygPPW


Новый номер

Цифра vs флексо для этикетки. Цифровая печать этикеток водными чернилами. «Этикетка и упаковка. Взгляд 360°». Ricoh Pro C5400S. Боксмейкеры. Vorey расширяет присутствие на рынке. Фабрика фантастических декораций. UPAKEXPO после смены дислокации.



Какой следующий принтер вы купите себе на производство?
Широкий УФ
25%
25 %
Сувенирный УФ
27%
27 %
ДТФ (текстиль)
20%
20 %
УФ ДТФ
20%
20 %
Латекс
7%
7 %
Экосольвент
12%
12 %
На водных чернилах
7%
7 %
Сублимацию
8%
8 %
Для прямой печати по ткани
10%
10 %
ДТГ («футболочный»)
3%
3 %
Проголосовало: 59