Think.JS №01

  • Для начинающих: Скрипт «Hello, world!» — 2
  • Начальное ускорение: Боец невидимого фронта
  • Учим матчасть: Пользовательский объект timer
  • Полезный скрипт: Цветопроба

Содержание

Для начинающих: Скрипт «Hello, world!» — 2

Начальное ускорение: Боец невидимого фронта

Учим матчасть: Пользовательский объект timer

Полезный скрипт: Цветопроба

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

Скрипт «Hello, world!» — 2

Редкий скрипт может быть самодостаточным, обходясь без получения данных от пользователя. К примеру, скрипт helloWorld.jsx, написанный нами для учебных целей всегда выводит в документ одну и туже фразу, и потому не может быть использован в целях практических. Но если дать ему возможность получать данные от пользователя, то в некоторых случаях он может пригодиться.

Есть несколько методов передачи данных в скрипт – диалоговые окна JavaScript, диалоговые окна InDesign и текстовые файлы (кроме них существует возможность использования аргументов при вызове скрипта и использование содержимого буфера обмена, но это скорее экзотика, чем общеупотребимый метод ). Самый простой способ – воспользоваться встроенными диалоговыми окнами языка JavaScript. Есть три стандартных функции, отвечающих за связь с пользователем – alert(), confirm() и prompt().

Функция alert(myString) выводит на экран предупреждение – значение текстовой переменной myString, или любое текстовое значение. Функция часто применяется для быстрой отладки скрипта, когда нужно проконтролировать значение переменной в какой-то момент времени. Также часто функция используется для сообщения пользователю об ошибках или успешном выполнении скрипта. После нажатия кнопки Ok скрипт продолжает свою работу. Пример использования:
alert(“Работа завершена!”).

Функция confirm(myString) выводит на экран диалоговое окно, содержащее значение текстовой переменной (вопрос) и две кнопки – «Да» и «Нет». Функция используется для подтверждения пользователем некоторых действий, например, для удаления файлов. В зависимости от того, какую кнопку нажал пользователь, функция возвращает логическое значение true (если пользователь нажал «Да») или false («Нет»). Пример использования:
var myResult = confirm(“Документ не сохранен. Сохранить его сейчас?”).

Функция prompt(myString, myDefValue) выводит на экран диалоговое окно с сообщением (текстовая переменная myString – любое текстовое значение) и полем ввода, в котором может находиться значение по умолчанию, определенное в текстовой переменной myDefValue. Кроме того, диалоговое окно содержит две кнопки – «Да» и «Отмена». Если пользователь выбирает отмену, то функция возвращает специальное значение null (отсутствие значения). Это помогает узнать, какую кнопку нажал пользователь, поскольку если он даже и не ввел значение, но нажал кнопку «Да», то в скрипт будет передана пустая строка, а не null. Пример использования:
var myFileName = prompt(“Имя файла для сохранения:”, “Document 1”).

Перед тем, как перейти к использованию указанных функций в новой версии скрипта, узнаем больше об использовании свойства geometricBounds объекта textFrame. Это свойство мы уже использовали в первой версии скрипта, но без подробного объяснения. Значением свойства всегда является массив из 4 чисел – геометрических координат двух точек объекта относительно нулевой точки. Нагляднее всего geometricBounds у непрямоугольных объектов – например у круга. Контейнер круга – это собственно и есть его geometricBounds, но только в том случае, если круг не был развернут. Если круг был развернут, то geometricBounds будет иметь значения координат крайних точек контейнера.

Координаты в InDesign вычисляются несколько не так, как в школьной программе. В первую очередь следует запомнить, что geometricBounds любого объекта, имеющего это свойство, определяется двумя точками контейнера – верхней левой и нижней правой. Именно в такой последовательности. Это обязательно надо запомнить, поскольку это поможет избежать немалого количества ошибок на начальном этапе обучения скриптам, пока такое положение не будет казаться естественным. Еще нужно запомнить, что сперва указывается значение координаты y (если брать в качестве основы привычную школьную систему координат), то есть значение по вертикали (сверху вниз). Вторые число в координате точки – это значение по горизонтали (справа налево). И третье – обязательно следует запомнить, что начало координат в InDesign находится в верхнем левом углу по умолчанию, поэтому чем ниже на странице расположен объект, тем его координата y будет больше. Эти три правила нужно обязательно запомнить.

В дальнейшем нам не раз пригодится метод move(), который перемещает объект, для которого был вызван в точку, указанную в качестве параметра. Хотелось бы объяснить и его свойства, поскольку при схожести по использованию с манипулированием свойством geometricBounds у метода move() есть отличия, которые могут привести к появлению ошибок, которые чрезвычайно трудно отыскиваются. Дело в том, что, изменяя свойство geometricBounds, объект можно «перемещать» по странице. Например, если получить значения координат, к каждой прибавить десять, а затем передать эти значения назад в свойство, то объект сместится на 10 единиц измерения вниз и на столько же вправо. Но использовать такое решение не рекомендуется потому, что содержимое объекта, например, фотография, при этом как правило остается на месте. Поэтому для перемещения объектов лучше использовать метод move().

В качестве параметра в общем случае этому методу передается массив из двух чисел. Обязательно нужно запомнить, что метод воспринимает только массив, а не просто числа. Вызов myTextFrame.move(10, 30) вызовет ошибку, нужно применять вызов вида myTextFrame.move([10, 30]). В квадратных скобках мы задали массив точно так же, если бы сделали var myArray = new Array(10, 30). Второй важный момент, который нужно запомнить на контрасте с особенностями свойства geometricBounds – это то, что сперва указывается координата x, а затем y. То есть, в нашем примере myTextFrame переместится в точку 10 по горизонтали и в точку 30 по вертикали.

Освоив такую обширную теоретическую базу, вы без труда разберетесь в работе расширенного варианта скрипта helloWorld.jsx.

helloWord.jsx
Расширенный вариант скрипта, демонстрирующий возможность получения данных от пользователя методами JavaScript
with (app) {
  if (documents.length > 0) {
    var myResult = confirm("Создать новый документ?");
  } else {
    myResult = true;
  }
  if (myResult) {
    var myDoc = documents.add();
  } else {
    var myDoc = activeDocument;
  }
  var myText = prompt("Введите текст:", "");
  if (myText == null) {
    exit();
  }
  var myTextFrame = myDoc.pages[0].textFrames.add();
  with (myTextFrame) {
    geometricBounds = [0,0,40, 120];
    contents = myText;
    move([20, 20]);
  }
  alert("Выполнено!");
}

Несколько пояснений относительно оператора if () {} else {} который раньше нам не встречался. Это условный оператор, в котором действия зависят от того, верно ли выражение в круглых скобках. Если верно (в результате решения выражения получается логическое значение true или это значение указывается напрямую), то выполняется блок операторов в первых фигурных скобках. Если значение неверно (false), то выполняется блок операторов во вторых скобках после else.

В строке 10 var myDoc = activeDocument; переменной myDoc присваивается значение – ссылка на документ, который в данный момент активен в InDesign. Это свойство объекта app. Если такой документ отсутствует, то произойдет ошибка, поэтому до того, как обращаться к этому свойству, требуется узнать, есть открытые документы или нет. Такую проверку мы проводим в первом операторе if .. else. Если открытые документы есть, то пользователю предлагается выбор: создать новый документ или использовать текущий. Если открытых документов нет, то новый документ создается автоматически без запроса. Поэтому к моменту обращения хотя бы один документ в InDesign есть.

Остальной текст скрипта не содержит ничего такого, с чем раньше мы не сталкивались.

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

Начальное ускорение

Боец невидимого фронта

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

Очень тормозят отрисовку обработка больших объемов текста и создание большого количества новых объектов, причем процесс в общем виде выглядит так: из скрипта изменяется документ, затем происходит отрисовка, затем еще раз изменение – отрисовка и так далее до тех пор, пока скрипт не закончит работу. В таком случае есть резон попытаться свести количество отрисовок к минимуму, свернув окно обрабатываемого документа. Код выглядит примерно так:

minWindow.jsx
Скрипт демонстрирует методы объекта типа window для ускорения работы
with (app) {
  var myDoc = activeDocument;
  myDoc.windows[0].minimize();
  var myObject = myDoc.rectangles.add();
  with (myObject) {
    geometricBounds = [20,20,70,70];
    fillColor = "Black";
    fillTint = 65;
    strokeWeight = "4pt";
  }
  myDoc.windows[0].restore();
}

Если у документа несколько окон, то неплохо их свернуть в цикле все и восстановить после выполнения скрипта. Сворачивается окно методом minimize(), восстанавливается методом restore();можно также использовать метод maximize(), который разворачивает окно во весь экран.

Если в процессе работы создается новый документ, который и будет обрабатываться, то будет полезен еще один хитрый трюк. Метод documents.add() в качестве первого необязательного параметра принимает логическую переменную, по значению которой устанавливается, будет ли новый документ иметь окно. По умолчанию передается значение true, но если указать явно значение false, то документ будет создан без окна. Он будет как бы невидим, следовательно, все действия в нем не будут вызывать процесс отрисовки. Главное — не забыть создать окно документа по окончании работы скрипта или при возникновении ошибки.

invisibleDoc.jsx
Скрипт создает документ без активного окна
with (app) {
  var myDoc = documents.add(false, documentPresets[0])
  var myObject = myDoc.pages[0].textFrames.add();
  with (myObject) {
    geometricBounds = [10,10,40,40];
    contents = "И опыт - сын ошибок трудных, и гений - парадоксов друг";
  }
  var myWindow = myDoc.windows.add();
  myWindow.maximize();
}

Следует запомнить, что при отсутствии окна у документа будет отсутствовать такое полезное свойство как activePage, которое определяется через activeWindow; кроме того в CS этот метод следует применять осторожно – не работают многие методы и не существуют многие свойства объектов, вплоть до объекта selection.

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

Пользовательский объект timer

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

Объекты в JavaScript создаются и выглядят как функции, причем имя функции будет являться именем конструктора объектов этого типа, а сама функция будет аналогом конструктора объекта (в объектно-ориентированных языках конструктор – последовательность операций, выполняемых при создании объекта). Единственным отличием является использование указателя this, который используется для указания на свойства и методы объекта.

Спланируем структуру нужного нам объекта, она достаточно несложна. Нам нужно определить два свойства – время начала и время завершения работы таймера – и три метода – старт, стоп, и вывод результатов замера времени.

timer.jsx
Определение пользовательского объекта timer, его свойств и методов
function timer() {
  this.startTime = null;
  this.endTime = null;
}
timer.prototype.start = function () {
  this.startTime = new Date();
}
timer.prototype.stop = function () {
  this.endTime = new Date();
}
timer.prototype.alert = function () {
  if (this.startTime == null) {
    alert("Таймер не был запущен!");
    return null;
  }
  if (this.endTime == null) {
    this.stop();
  }
  alert("Выполнено за " + String((this.endTime - this.startTime)/1000) + " секунд.")
}

В главной функции определяются две переменных со значениями null. Заметим, что главная функция не содержит оператора return, а значит, не возвращает никакого значения.

Методы start() и stop() объекта timer тоже просты – в них определяется значение переменных как новых объектов типа Date. При отсутствии параметров при создании объект типа Date содержит значение текущей даты в виде числа миллисекундах, прошедших с полуночи 01.01.1970. По разнице между двумя переменными можно узнать время, потраченное на выполнение измеряемых операций.

Метод alert()
(заметим, что имя метода вполне может быть синонимом уже существующего метода другого объекта) несколько более сложен. Сначала проверяется переменная startTime – если значение равно null, то это означает, что таймер не был запущен. Если переменная endTime равна null, то это значит, что таймер не был остановлен. В этом случае вызывается метод stop. После проверок выдается сообщение о выполнении с указанием времени в секундах.

Применяется объект очень просто. Продемонстрируем работу на практическом примере –попробуем проверить, есть ли выигрыш во времени исполнения скрипта из предыдущей статьи. Добавим определение объекта вне with(app){} и отредактируем скрипт для измерения времени обработки.

checkTime.jsx
Скрипт проверки скорости выполнения операций
with (app) {
  var myTimer = new timer();
  myTimer.start();
  var myDoc = documents.add(false, documentPresets[0])
  var myObject = myDoc.pages[0].textFrames.add();
  with (myObject) {
    geometricBounds = [10,10,40,40];
    contents = "И опыт - сын ошибок трудных, и гений - парадоксов друг";
  }
  myDoc.pages.add();
  myObject.move(myDoc.pages[1]);
  myObject.move([30,50])
  var myWindow = myDoc.windows.add();
  myWindow.maximize();
  myTimer.stop();
  myTimer.alert();
}

У меня получалось, что при использовании ускоренного метода скрипт выполняется за 0,75 секунд, а при обычном методе – за 1,8 секунд. Выигрыш по времени очевиден.

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

Цветопроба

Как перевести все RGB swatches в документе в CMYK? Трудолюбивый человек кропотливо проверяет весь список swathces и вручную исправляет каждый (хорошо, если их десятки, а если сотни?), а ленивый скриптописатель пишет скрипт, который это делает автоматически. Но на практике это оказывается не так просто — объект типа swatch не имеет свойства, указывающего цветовую модель. Вообще, по странной прихоти разработчиков, этот тип имет удивительно куцый список свойств и методов. Но на самом деле он больше, чем кажется на первый взгляд.

У всех объектов в InDesign есть полезное свойство properties, которое очень полезно проверять. Для этого хорошо использовать функцию objectInfo, которая возвращает список свойств объекта с текущими значениями.

getProperties.jsx
Скрипт содержит функцию проверки свойства properties любого объекта
function objectInfo(myObject) {
  var myResult = "";
  for (prop in myObject) {
    myResult += prop + " - " + myObject[prop] + "\n"
  }
  return myResult;
}

Вызываем функция вот так: alert(objectInfo(myDoc.swatches[2].properties)) и видим, что в properties присутствует свойство space, которое определяет цветовую модель, которую нам и нужно поменять. Но дело в том, что все свойства в properties только для чтения – изменить их невозможно. Зато свойство space присутствует у объектов типа color, причем это свойство можно менять. Вообще, свойства объекта типа color совпадают со свойствами properties объекта типа swatch. Есть мнение, что swatch – это ссылка на объект типа color (с удалением некоторых свойств). Для объекта swatch соответствующий color можно найти по общему имени. При изменении свойства space объекта color, автоматически изменится соответствующее свойство у одноименного объекта типа swatch.

swatchesRGB2CMYK.jsx
Скрипт конвертирует все RGB swatches в CMYK
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    alert("Нет открытых документов!");
    exit();
  }
  for (var counter = 0; counter < myDoc.swatches.length; counter++) {
    if (myDoc.swatches[counter].properties.space == ColorSpace.rgb) {
      myDoc.colors.item(myDoc.swatches[counter].name).space = ColorSpace.cmyk
    }
  }
}

Олег Бутрин
THINK.JS выпуск № 1 от 2006-10-30

 

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Connecting to %s