Think.JS №18

  • Для начинающих: Направляем направляющие
  • Учим матчасть: JSX – это не JS. Часть I
  • Полевые испытания: Печать. Все красочно.
  • Полезный скрипт: Обозначить границы

Содержание

Для начинающих: Направляем направляющие

Учим матчасть: JSX – это не JS. Часть I

Полевые испытания: Печать. Все красочно.

Полезный скрипт: Обозначить границы

Для начинающих

Направляем направляющие

Направляющие (guides) сами по себе в скриптах используются редко, поскольку предназначены в первую очередь для облегчения работы вручную. Зато с помощью скриптов можно здорово облегчить точное размещение направляющих – занятие, требующее незаурядного терпения.

Наиболее важными свойствами направляющей являются ее координата – location и ориентация – orientation. Вместе два этих свойства однозначно определяют положение направляющей на странице. Координата в качестве значения принимает либо значение относительно начала координат в указанных единицах измерения, либо число (используются текущие единицы измерения). Свойство orientation может принимать одно из двух значений нумератора с интересным названием HorizontalOrVertical, соответственно HorizontalOrVertical.horizontal и HorizontalOrVertical.vertical.

Создаются направляющие при помощи метода guides.add(), который может быть вызван для объектов типа layer (слой), page (страница), spread (разворот). В качестве обязательного параметра метод принимает слой, на котором будет расположена направляющая. Следующий скрипт показывает, как можно автоматически создавать направляющие с одинаковым расстоянием от отступов страницы (очень полезно, если страница, например, ограничена графической рамкой).

marginPlusGuides.jsx
Скрипт создает в активной странице активного документа 4 направляющих на равном расстоянии от отступов страницы
with (app) {
// Получаем активный документ
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
// Определяем дистанцию до направляющей
  var myDistance = 3;
// Определяем активную страницу  
  var myPage = activeWindow.activePage;
// Определяем активный слой
  var myLayer = activeWindow.activeLayer;  
  with (myPage) {
// Создаем и настраиваем верхнюю направляющую    
    var myTop = guides.add(myLayer);
// Устанавливаем ориентацию    
    myTop.orientation = HorizontalOrVertical.horizontal;
// Устанавливаем позицию
    myTop.location = bounds[0] + marginPreferences.top + myDistance;
// Создаем и настраиваем левую направляющую
    var myLeft = guides.add(myLayer);
    myLeft.orientation = HorizontalOrVertical.vertical;
    myLeft.location = bounds[1] + marginPreferences.left + myDistance;
// Создаем и настраиваем нижнюю направляющую
    var myBottom = guides.add(myLayer);
    myBottom.orientation = HorizontalOrVertical.horizontal;
    myBottom.location = bounds[2] - marginPreferences.top - myDistance;    
// Создаем и настраиваем правую направляющую
    var myRight = guides.add(myLayer);
    myRight.orientation = HorizontalOrVertical.vertical;
    myRight.location = bounds[3] - marginPreferences.left - myDistance;
  }
}

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

По своим свойствам направляющие больше похожи на объекты типа pageItem, нежели на границы и колонки объекта marginPreferences, хотя по функциональности они и ближе к последним. Направляющая может быть перемещена при помощи метода move(), может быть выделена при помощи метода select()
(однако, невозможно выделить одновременно направляющую и объект типа pageItem), и удалена при помощи метода remove(). Последняя возможность достаточно полезна при очистке публикации от направляющих, оставшихся от ручной обработки и более не нужных.

removeGuides.jsx
Скрипт удаляет с активной страницы все незакрепленные и не отеленные от мастер-страницы направляющие
with (app) {
// Получаем активный документ
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
// Определяем активную страницу  
  var myPage = activeWindow.activePage;
// Подготавливаем массив для направляющих, которые нужно удалить
  var myGuides = [];
// Для всех направляющих на странице
  for (var counter = 0; counter < myPage.guides.length; counter++) {
// Если напрвляющая не является закрепленной или отделенной от мастер-страницы
    if (!myPage.guides[counter].locked && !myPage.guides[counter].overridden) {
// Помещаем ее в массив
      myGuides.push(myPage.guides[counter]);
    }
  }
// Разворачиваем массив  
  myGuides.reverse();
//Удаляем каждую направляющую, помещенную в массив
  for (var counter = 0; counter < myGuides.length; counter++) {
    myGuides[counter].remove();
  }
}

Свойство locked используется для закрепления отдельной направляющей в публикации. Если это свойство содержит значение true, то такую направляющую перед удалением вручную или скриптом нужно будет открепить (скриптом – передав в свойство значение false, вручную – при помощи соответствующей команды).

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

Учим матчасть

JSX – это не JS. Часть I

Впервые поддержка языка JavaScript была введена в InDesign CS. Нововведение открывало широкие перспективы, поскольку позволяло создавать скрипты, одинаково хорошо (за небольшими исключениями) работающие как на PC, так и на Mac. Кроме того, для разработки скриптов не требовалось никаких специальных средств, кроме самого обычного текстового редактора, который выбирался по вкусу пользователя. Следующим шагом стала унификация средств разработки скриптов ExtendScript для всего пакета Adobe Creative Suite 2.

Самым очевидным нововведением было то, что скрипты теперь могут иметь расширение jsx вместо привычного js. Расширение jsx настоятельно рекомендуется для всех скриптов, написанных на языке ExtendScript потому, что позволяет связать файлы с таким расширением со специально разработанной программой ExtendScript Toolkit. В ближайшее время мы обязательно подробно рассмотрим возможности этого отладчика более подробно, сейчас – только наиболее полезные при разработке скриптов для InDesign.

Неудобство использования расширения js было очевидным – запустить скрипт не из панели скриптов в InDesign было невозможно. Система могла открывать файлы скрипта в соответствующем редакторе или пытаться выполнить их в WSH (Windows Scripting Host), но выполнить их в InDesign – никогда. Теперь, если систему не перенастроить, скрипты с таким расширением открываются в ExtendScript Toolkit. Это более удобно, но все-таки было намного удобнее выполнять скрипты в соответствующей программе, в нашем случае в InDesign. И это вполне возможно.

С версии CS2 ExtendScript поддерживает так называемые директивы препроцессора для управления интерпретатором скриптов. С помощью этих директив можно влиять на свойства интерпретатора, что позволяет, например, автоматически подключать дополнительные библиотеки функций и целые скрипты. Одной из самых полезных директив является директива определения программы, для которой предназначается скрипт. Общий вид директивы: “#target appName”. Все директивы препроцессора начинаются либо со знака # (т.н. стиль Си), либо с комбинации // @ (стиль комментария). Рекомендуется использовать стиль Си, для того, чтобы при прочтении не путать директивы с комментариями. В качестве параметра директиве #target передается имя программы, в которой скрипт должен выполняться. В нашем случае это indesign или, точнее, indesign-4 (поскольку indesign-5 уже на подходе).

Любой скрипт, который будет начинаться с кода #target indesign-4 (до директив препроцессора не желательно использовать любой код кроме комментариев) при запуске из системы, а не из панели скриптов, будет выполнен в InDesign.

targetApp.jsx
Скрипт демонстрирует использование директивы препроцессора #target
#target indesign-4;
alert(app.name);

Если вместо indesign-4 указать, допустим, photoshop-9, то тот же самый скрипт будет запущен в PhotoShop CS2, о чем скрипт сообщит через функцию alert(app.name). Заметим, что если приложение не было запущено (но, естественно, установлено в системе), то система сначала запустит приложение, а затем уже будет выполнен скрипт. Это весьма полезное свойство.

Остальные директивы препроцессора предназначены для подключения различным образом библиотек, скриптов или файлов данных. Мы рассмотрим их отдельно при изучении модульной разработки скриптов.

Кроме директив препроцессора в ExtendScript был добавлен т.н. Dollar Object – глобальный объект, позволяющий скрипту взаимодействовать с обработчиком, передавать текст в консоль JavaScript отладчика, а также, что очень важно, использовать возможности локализации скриптов в зависимости от конкретной локали системы. Обращаться к свойствам и методам объекта Dollar можно следующим образом: $.locale = “ru”;
(изменяет свойство locale, которое определяет текущую локаль приложения), $.about();
(вызывает метод, который отображает информацию About ExtendScript).

Не останавливаясь на подробностях, перейдем к полезному с практической точки зрения методу локализации скриптов.

Для того чтобы получить некое значение, определенное в зависимости от текущей локализации, нужно сначала это значение задать. Делается это путем создания специального объекта-переменной, в котором для каждой используемой локали определяется свое значение. Следующий пример демонстрирует создание такой переменной myAlert и использование функции localize(), которая выбирает из свойств myAlert то, имя которого соответствует текущим установкам локали и возвращает его.

localize.jsx
Скрипт демонстрирует использование функции localize()
// Создаем переменную-объект
var myAlert = {en: "Message", ru: "Сообщение"};
// Сообщаем ее значение через функцию localize
alert(localize(myAlert));
// Сообщаем значение свойства myAlert.en
alert(myAlert.en);

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

Для того чтобы всякий раз при обращении к локализованной переменной не пользоваться функцией localize(), можно включить или выключить локализацию по умолчанию при помощи свойства $.localize, которое принимает логические значения (true – локализация включена, false – выключена).

globalLocalize.jsx
Скрипт демонстрирует использование глобальной локализации при помощи $.localize
// Включаем $.localize
$.localize = true;
// Создаем переменную-объект
var myAlert = {en: "Message", ru: "Сообщение"};
// Сообщаем ее значение
alert(myAlert);
// Выключаем $.localize
$.localize = false;
// Пытаемся сообщить значение myAlert без локализации
alert(myAlert);
// Сообщаем принудительно локализованное значение
alert(localize(myAlert));

Как видим, при включенном свойстве $.localize происходит автоматический выбор соответствующего значения, а при отключенном – переменная myAlert воспринимается как объект типа Object (чем, по сути, и является), хотя возможность доступа к свойствам, например к myAlert.en, сохраняется. При выключенном свойстве можно использовать функцию localize() для получения значения в зависимости от текущей локали.

При помощи свойства $.locale можно управлять значением текущей локали. Для разработчиков это прежде всего возможность проверки скрипта под всеми доступными локалями.

setLocale.jsx
Скрипт демонстрирует возможность управления локалью
// Включаем $.localize
$.localize = true;
// Создаем переменную-объект
var myAlert = {en: "Message", ru: "Сообщение"};
// Сообщаем ее значение
alert(myAlert);
// Устанавливаем значение локали
$.locale = "en";
// Сообщаем значение при новых настройках локали
alert(myAlert);
// Возвращаем значение $.locale в используемое по умолчанию
$.locale = null;
// Сообщаем значение
alert(myAlert);

Для того чтобы вернуть значение текущей локали в исходное – которое используется в системе – нужно передать в свойство $.locale значение null.

Может случиться, что разработчик не предусмотрел всех возможных значений для локализованной переменной, например, задал значения только для английского и русского языка, а скрипт был запущен в системе с немецкой локалью. В этом случае автоматически запрашивается значение свойства en (английский язык считается международным). При отсутствии такого свойства переменная возвращается как объект типа Object, что наверняка нарушит работу скрипта. Поэтому при локализации в обязательном порядке нужно указывать значение переменной в en-локали – для вящей безопасности.

checkLocale.jsx
Скрипт демонстрирует автоматический выбор локали
// Включаем $.localize
$.localize = true;
// Создаем переменную-объект
var myAlert = {en: "Message", ru: "Сообщение"};
// Устанавливаем значение локали
$.locale = "de";
// Сообщаем значение при новых настройках локали
alert(myAlert);
// Возвращаем значение $.locale в используемое по умолчанию
$.locale = null;
// Сообщаем значение
alert(myAlert);

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

useLocaleParameters.jsx
Скрипт демонстрирует использование локали для задания значений разных типов
// Включаем $.localize
$.localize = true;
// Устанавливаем значения переменных для единиц измерения и размеров
var myUnit = {en: MeasurementUnits.points, ru: MeasurementUnits.millimeters};
var mySize = {en: 215, ru: 100};
with (app) {
// Создаем новый документ
  var myDoc = documents.add();
// Устанавливаем единицы измерения
  myDoc.viewPreferences.horizontalMeasurementUnits = Number(myUnit);
  myDoc.viewPreferences.verticalMeasurementUnits = Number(myUnit);
// Создаем прямоугольник с размерами, зависимыми от установленной локали
  var myRectangle = myDoc.pages[0].rectangles.add({geometricBounds: [0,0,Number(mySize),Number(mySize)]});
}

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

Полевые испытания

Печать. Все красочно.

В некоторых случаях требуется напечатать документ не в его «натуральном» виде, а по отдельным краскам – в сепарированном виде. Чистый случай такой задачи – печать каждой сепарации в отдельный файл. Постранично.

Как всегда, перед началом разработки скрипта, желательно посмотреть, как такое действие выполняется вручную. Чтобы получить доступ к управлению печатью красок, нужно в окне Print выбрать закладку Output и в ней выбрать соответствующий тип вывода. В скрипте то же самое делается при помощи редактирования свойства printPreferences. colorOutput, которое может принимать одно из значений нумератора ColorOutputModes. В нашем случае это ColorOutputModes.separations. Для печати отдельных красок в printPreferences определены четыре свойства – printBlack, printCyan, printMagenta, printYellow, – которые имеют логические значения.

printSeparation.jsx
Скрипт демонстрирует печать красок отдельно
with (app) {
// Получаем активный документ
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
// Настраиваем свойства печати
  with (myDoc.printPreferences) {
// Устанавливаем тип вывода
    colorOutput = ColorOutputModes.separations;
// Настраиваем краски
    printBlack = true;
    printCyan = false;
    printMagenta = false;
    printYellow = false;
  }
// Выполняем печать  
  myDoc.print(true);
}

Запускаем скрипт и получаем… ошибку выполнения. Трудно понять логику разработчиков, которые ввели четыре свойства для печати основных красок, не обеспечив возможность их использования. В первую очередь непонятно, почему свойств всего четыре, а красок может быть гораздо больше. Для управления красками в InDesign предусмотрена коллекция объектов типа ink
(чернила или краска). Каждый объект ink имеет свойства для управления настройками соответствующей краски (аналог окну Ink Manager), в том числе логическое свойство printInk, которое определяет, будет ли краска печататься. Вот это свойство и нужно использовать при печати отдельными красками. Обращаться к краскам можно по их именам (в скрипте ниже приведены названия основных красок).

printSeparation.jsx
Скрипт демонстрирует возможность покрасочной печати
with (app) {
// Создаем новый документ
  var myDoc = documents.add();
// Настраиваем печать красок
  myDoc.inks.item("Process Black").printInk = true;
  myDoc.inks.item("Process Cyan").printInk = false;
  myDoc.inks.item("Process Magenta").printInk = false;
  myDoc.inks.item("Process Yellow").printInk = false;
// Настраиваем свойства печати
  with (myDoc.printPreferences) {
// Устанавливаем тип вывода
    colorOutput = ColorOutputModes.separations;
  }
// Выполняем печать  
  myDoc.print(true);
}

Поскольку объекты типа ink собраны в коллекцию inks, обрабатывать их можно обходом в цикле, поочередно включая и выключая.

printSeparationInks.jsx
Скрипт демонстрирует печать всех красок по отдельности
with (app) {
// Создаем новый документ
  var myDoc = documents.add();
  with (myDoc.printPreferences) {
// Устанавливаем тип вывода
    colorOutput = ColorOutputModes.separations;
  }
// Отменяем печать всех красок
  for (var counter = 0; counter < myDoc.inks.length; counter++) {
    myDoc.inks[counter].printInk = false;
  }
  for (var counter = 0; counter < myDoc.inks.length; counter++) {
// Включаем печать текущей краски
    myDoc.inks[counter].printInk = true;
  // Выполняем печать  
    myDoc.print(true);
// Выключаем печать текущей краски
    myDoc.inks[counter].printInk = false;
  }
// Включаем печать всех красок
  for (var counter = 0; counter < myDoc.inks.length; counter++) {
    myDoc.inks[counter].printInk = true;
  }
}

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

Полезный скрипт

Обозначить границы

Иногда бывает полезно создать направляющие по границам какого-либо объекта. Вручную это делать – неправильно. Лучше написать простой скрипт для быстрого и точного выполнения этой работы.

Скрипт достаточно прост как в замысле, так и в реализации. В первую очередь определяются размеры выделенного объекта (если объектов несколько, то они предварительно группируются с последующим разгруппированием). Затем пользователю предлагается выбрать, с каких сторон направляющие будут установлены, а с каких – нет. Одновременно можно выбрать отступ (равный длине или ширине выделенного объекта, для вертикальных и горизонтальных направляющих соответственно). После того, как пользователь произвел все настройки и нажал Ok скрипт последовательно создает все четыре направляющие.

createBoundsGuides.jsx
Скрипт для создания направляющих вокруг выделенного объекта
with (app) {
// Получаем активный документ
  try {
    var myDoc = activeDocument;
  } catch (error) {
    alert("Нет открытых документов!");
    exit();
  }
// Проверяем наличие выделенного объекта
  if (selection.length < 1) {
    exit();
  }  
  if (selection.length > 1) {
// Если объектов несколько, пытаемся сгруппировать и получить видимые размеры
    try {
      var myObj = myDoc.groups.add(selection);
      var myBounds = myObj.visibleBounds;
// Разгруппировываем обратно
      myObj.ungroup();
    } catch (error) {
      try {
// При ошибке в группировке пытаемся на всякий случай разгруппировать
        myObj.ungroup();
      } catch (error) {
        exit();
      }
    }
  } else {
    try {
// Если объект один, то просто получаем его размер
      var myBounds = selection[0].visibleBounds;
    } catch (error) {
      exit();
    }  
  }
// Создаем диалог  
  var myDialog = dialogs.add({name: "Create bounds guides"});
  with (myDialog.dialogColumns.add().borderPanels.add().dialogColumns.add()) {
    with (dialogRows.add()) {
      with (dialogColumns.add()) {
// Для каждой стороны создаем свой выключатель
        var userTop = dialogRows.add().checkboxControls.add({staticLabel: "Сверху:", checkedState: true});
        var userLeft = dialogRows.add().checkboxControls.add({staticLabel: "Слева:", checkedState: true});
        var userBottom = dialogRows.add().checkboxControls.add({staticLabel: "Снизу:", checkedState: true});
        var userRight = dialogRows.add().checkboxControls.add({staticLabel: "Справа:", checkedState: true});
      }
// Для каждой стороны создаем виджет для получения отступа
      with (dialogColumns.add()) {
        var userTopParam = dialogRows.add().realEditboxes.add({editValue: 0, minimumValue: -(myBounds[2] - myBounds[0]), maximumValue: (myBounds[2] - myBounds[0]), smallNudge: .1, largeNudge: 1});
        var userLeftParam = dialogRows.add().realEditboxes.add({editValue: 0, minimumValue: -(myBounds[3] - myBounds[1]), maximumValue: (myBounds[3] - myBounds[1]), smallNudge: .1, largeNudge: 1});
        var userBottomParam = dialogRows.add().realEditboxes.add({editValue: 0, minimumValue: -(myBounds[2] - myBounds[0]), maximumValue: (myBounds[2] - myBounds[0]), smallNudge: .1, largeNudge: 1});
        var userRightParam = dialogRows.add().realEditboxes.add({editValue: 0, minimumValue: -(myBounds[3] - myBounds[1]), maximumValue: (myBounds[3] - myBounds[1]), smallNudge: .1, largeNudge: 1});
      }
    }
  }
// Отображаем диалог
  var myResult = myDialog.show();
  if (!myResult) {
    exit();
  }
// Получаем активную страницу и слой  
  var myPage = activeWindow.activePage;
  var myLayer = activeWindow.activeLayer; 
// Для каждой стороны
  if (userTop.checkedState) {
// Создаем направляющую
    var myTop = myPage.guides.add(myLayer);
// Устанавливаем ориентацию
    myTop.orientation = HorizontalOrVertical.horizontal;
// Задаем позицию с учетом отступа
    myTop.location = myBounds[0] - userTopParam.editValue;
// Определяем растяжение по странице    
    myTop.fitToPage = true;
  }
// Далее везде  
  if (userLeft.checkedState) {
    var myLeft = myPage.guides.add(myLayer);
    myLeft.orientation = HorizontalOrVertical.vertical;
    myLeft.location = myBounds[1] - userLeftParam.editValue;
    myLeft.fitToPage = true;
  }
  if (userBottom.checkedState) {
    var myBottom = myPage.guides.add(myLayer);
    myBottom.orientation = HorizontalOrVertical.horizontal;
    myBottom.location = myBounds[2] + userBottomParam.editValue;
    myBottom.fitToPage = true;
  }
  if (userRight.checkedState) {
    var myRight = myPage.guides.add(myLayer);
    myRight.orientation = HorizontalOrVertical.vertical;
    myRight.location = myBounds[3] + userRightParam.editValue;
    myRight.fitToPage = true;
  }
}

Олег Бутрин
THINK.JS выпуск № 18 от 2007-03-05

 

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s