Think.JS №10

  • Для начинающих: Путем последовательных трансформаций
  • Учим матчасть: Диалог с файловой системой
  • Полевые испытания: Сортировка как она есть
  • Полезный скрипт: Сортировка абзацев

Содержание

Для начинающих: Путем последовательных трансформаций

Учим матчасть: Диалог с файловой системой

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

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

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

Путем последовательных трансформаций

Для трансформации объектов типа pageItems в InDesign предусмотрены три метода: resize (масштабирование), rotate (вращение) и shear (сдвиг). Напомню, что объекты типа pageItem – это такие объекты, которые можно создать на странице при помощи инструментов из панели Tools.

Метод resize() изменяет размеры объекта pageItem в соответствии с указанными параметрами. Раньше мы меняли размеры объектов, изменяя свойство geometricBounds, но это не всегда удобно по многим причинам, но в первую очередь потому, что при таком методе невозможно одновременно изменить размеры объектов, которые содержатся в исходном (при обработке группы или объекта, содержащего изображения).

resizeObject.jsx
Скрипт демонстрирует изменение размеров объекта при помощи метода resize()
with (app) {
  var myDoc = documents.add();
  var myRectangle = myDoc.pages[0].rectangles.add({geometricBounds: [10, 10, 110, 110]});
  myRectangle.resize(80, 80, AnchorPoint.centerAnchor, true, true, true);
}

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

Рассмотрим параметры подробнее.

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

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

Наименование
Десятичное значение

AnchorPoint.topLeftAnchor 1095660652

AnchorPoint.topCenterAnchor 1095660643

AnchorPoint.topRightAnchor 1095660658

AnchorPoint.leftCenterAnchor 1095658595

AnchorPoint.centerAnchor 1095656308

AnchorPoint.rightCenterAnchor 1095660131

AnchorPoint.bottomLeftAnchor 1095656044

AnchorPoint.bottomCenterAnchor 1095656035

AnchorPoint.bottomRightAnchor 1095656050

Из названий ясно, к какой именно то точке относится каждое значение нумератора. Если нужно, чтобы верхняя левая точка осталась на своем месте (стала точкой трансформации), то следует указать AnchorPoint.topLeftAnchor или десятичное значение 1095660652. Трансформация объектов, как правило, производится относительно значений нумератора.

Учет текущего значения масштабирования используется для указания того, что будет считаться 100-процентными размерами. При значении true будут считаться эталонными текущие параметры масштабирования, при false – начальные размеры объекта. То есть, при масштабировании картинки, которая была уменьшена до 80% от своего первоначального размера, при true за 100% будут приняты текущие размеры (80% от исходного), а при false – исходные (100%). По умолчанию значение равно false.

Масштабирование содержимого позволяет включать и отключать изменение размеров содержимого объектов. Содержимое может быть разного типа: текст, объекты (pageItem или картинки), содержащиеся в масштабируемом объекте и т.д. При значении true масштабирование содержимого включено, при false – выключено.

resizeText.jsx
Скрипт демонстрирует изменение размера текста при масштабировании текстового фрейма
with (app) {
  var myDoc = documents.add();
  var myTextFrame = myDoc.pages[0].textFrames.add({geometricBounds: [10, 10, 60, 60]});
  myTextFrame.contents = "Пробный текст";
  myTextFrame.resize(200, 200, AnchorPoint.topLeftAnchor, false, true, true);
}

Относительность масштабирования указывает, будет ли масштабирование производиться относительно родительского объекта (true) или относительно родительской страницы (false). Назначение этого параметра масштабирования не совсем ясно, поскольку то же самое происходит при использовании учета предыдущего масштабирования.

Метод rotate() имеет очень похожие параметры.

rotateObject.jsx
Скрипт демонстрирует поворот объекта
with (app) {
  var myDoc = documents.add();
  var myRectangle = myDoc.pages[0].rectangles.add({geometricBounds: [10, 10, 110, 110]});  myRectangle.rotate(30, AnchorPoint.centerAnchor, true, true, false);
}

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

Логические параметры примерно соответствуют тем же параметрам метода resize(). Если значение учета текущего поворота true, то объект повернется относительно своего текущего угла поворота. При значении false объект будет повернут на угол, указанный в качестве параметра безотносительно к тому, на какой угол он был повернут до применения метода rotate(). Например, объект был развернут на 32 градуса. После применения метода myObject.rotate(5, AnchorPoint.centerAnchor, true), объект будет повернут на 37 градусов относительно страницы (дальнейшие повороты с теми же параметрами развернут объект на 42, 37, 52 и т.д. градусов). А вот после применения метода myObject.rotate(5, AnchorPoint.centerAnchor, false) объект будет повернут относительно страницы на 5 градусов ровно (положение не изменится при вызове метода с такими параметрами несколько раз). Второй логический параметр означает то же, что и в метода resize(), а именно будет ли произведен поворот содержимого объекта (текст в текстовом фрейме поворачивается при любом значении этого параметра). Третий логический параметр указывает, будет ли проведен поворот относительно родительского объекта (true) или родительской страницы (false). В отличие от метода rotate() в этом методе данный параметр достаточно важен хотя бы потому, что при включенном учете значений текущего поворота, его нельзя устанавливать в true – скрипт завершится по ошибке. Как правило, поворот с использованием myObject.rotate(5, AnchorPoint.centerAnchor, false, true, true) приводит к таким же результатам, что и myObject.rotate(5, AnchorPoint.centerAnchor, true, true, false), однако при работе со «сдвинутыми» объектами результаты могут быть различными.

Следует учитывать одну особенность при использовании метода rotate(). Если выполнить поворот объекта myRectangle.rotate(90, AnchorPoint.topLeftAnchor), то точка topLeftAnchor после поворота «опустится» ниже topRightAnchor, как бы заменив точку bottomLeftAnchor. Но на самом деле она останется точкой topLeftAnchor – при следующей трансформации относительно ее же может произойти не совсем то, что предполагал автор (если он не учел смещения точки и не указал другую точку поворота).

Метод shear() отличается от метода rotate() тем, что в списке параметров после точки трансформации требует указания значения, которое будет определять, по какому углу будет произведено смещение. При значении 0 смещение будет происходить по горизонтали (при положительном значении угла объект наклонится вправо, при отрицательном — влево), при значении 90 – по вертикали (при положительном значении угла правый край объекта опустится относительно левого, при отрицательном — наоборот). При произвольном значении угла объект будет не только наклонен, но и повернут – происходит довольно сложная трансформация.

shearObject.jsx
Скрипт демонстрирует сдвиг объекта
with (app) {
  var myDoc = documents.add();
  var myRectangle = myDoc.pages[0].rectangles.add({geometricBounds: [10, 10, 110, 110]});
  myRectangle.shear(15, AnchorPoint.centerAnchor, 0, true, true, false);
}

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

Диалог с файловой системой

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

Для выбора папки существуют два метода: Folder.selectDialog() и selectDlg() объекта типа Folder. Разница между ними в том, что selectDialog() является методом типа Folder и вызывается при помощи обращения к типу, а selectDlg() – методом объекта Folder и вызывается при помощи обращения к объекту типа.

selectDialog.jsx
Скрипт демонстрирует использование метода Folder.selectDialog()
with (app) {
  var myFolder = Folder.selectDialog("Выберите папку", "~");
  alert(myFolder);
}

Метод имеет два параметра: строковую переменную, которая используется для сообщения пользователю скрипта некоторой информации, и папку (или имени папки), которая будет выбрана в диалоговом окне при открытии. Метод возвращает либо объект Folder, если была выбрана папка, либо null, если пользователь нажал кнопку отмены или выбрал объект, который не может быть представлен как объект Folder (в Windows таким объектом является Мой компьютер).

Метод selectDlg() отличается тем, что возвращает только объект типа Folder в любом случае, даже если пользователь нажал кнопку отмены – при этом возвращается исходная папка.

selectDlg.jsx
Скрипт демонстрирует использование метода selectDlg()
with (app) {
  var myFolder = Folder("~");
  var myNextFolder = myFolder.selectDlg("Выберите папку:");
  alert(myFolder);
}

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

Какой метод использовать – нужно решать для каждого конкретного случая. Метод Folder.selectDialog() лучше использовать в том случае, если выполнение скрипта должно прекратиться в том случае, если пользователь отказался от выбора папки. После проверки значения можно прекратить выполнение скрипта. Метод selectDlg() удобно использовать в том случае, когда скрипт должен работать независимо от решения пользователя.

Для выбора файлов существуют два разных диалога: для открытия файла – File.openDialog(), для сохранения – File.saveDialog(). Принципиально они ничем не различаются, кроме надписи в диалоговом окне в том случае, если не указан параметр, определяющий сообщение для пользователя. По идее, File.saveDialog() должен запрашивать подтверждение при выборе уже существующего файла, но не делает этого. В принципе, можно написать обработчик для проверки.

checkSaveFile.jsx
Скрипт демонстрирует использование метода File.saveDialog() с проверкой
with (app) {
  var myFile = File.saveDialog("Выберите файл:");
  if (myFile.exists) {
    if (!confirm("Файл существует! Перезаписать?")) {
      myFile == null;
    }
  }
  if (myFile == null) {
    exit();
  }
  alert(myFile);
}

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

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

openTextFiles.jsx
Скрипт демонстрирует использование метода File.openDialog() для выбора файлов определенного типа
with (app) {
  var myFile = File.openDialog("Выберите файл для импорта:", "Текстовые файлы: *.txt;*.rtf;*.doc, Все файлы:*");
  alert(myFile);
}

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

Очень удобно иногда указать для выбора конкретное имя файла (например, в проекте инсталлятора для скриптов предполагается использование такого метода), чтобы пользователь указал к нему путь.

openCfgFile.jsx
Скрипт позволяет выбирать только специально указанный файл
with (app) {
  var myFile = File.openDialog("Выберите файл для импорта:", "Файл конфигурации: info.cfg");
  alert(myFile);
}

В ExtendScript предусмотрены еще два диалога для работы с файлами: openDlg() и saveDlg(), которые являются методами не типа File, а объектов файловой системы – папок и файлов.

openDlg.jsx
Скрипт демонстрирует работу метода openDlg()
with (app) {
  var myFile = Folder("~").openDlg("Выберите файл для импорта:", "Текстовые файлы:*.txt;*.rtf;*.doc, Все файлы:*");
  alert(myFile)
}

Преимущество этого метода открытия файлов в том, что можно сразу указать папку, которая будет открыта в диалоге. Используя метод File.openDialog() тоже можно добиться того же, изменяя свойство Folder.current, но это менее толковый путь.

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

saveWithName.jsx
Скрипт демонстрирует использование метода saveDlg() с указанием имени файла
with (app) {
  var myFile = Folder("~/fileToSave.txt").saveDlg("Выберите файл для экспорта:", "Текстовые файлы:*.txt");
  alert(myFile);
}

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

Также нужно учесть, что в отличие от метода selectDlg() объекта типа Folder методы openDlg() и saveDlg() могут возвращать null в том случае, если файл не был выбран.

Категорически рекомендую при работе с проектами не пользоваться методами типа File, а использовать методы openDlg() и saveDlg() как более гибкие и надежные в работе.

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

Сортировка как она есть

Сортировать различные объекты в InDesign приходится часто. Это могут быть как данные, полученные от пользователя или из других источников, так и объекты в документе, например, абзацы. Язык JavaScript обладает весьма гибкими и легко настраиваемыми средствами сортировки.

Когда речь заходит о сортировке, в основном имеется в виду сортировка массивов. В JavaScript определен метод sort() для объектов типа Array. Это и есть главный он же единственный метод сортировки. В качестве дополнительного метода используется метод reverse(), который разворачивает массив так, что последний его элемент становится первым и наоборот.

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

sortArray.jsx
Скрипт демонстрирует работу метода sort()
with (app) {
  var myArray = [14,52,3,14,45,36];
  myArray.sort();
  alert(myArray);
}

В результате в отсортированном массиве число 14 оказывается меньше, чем 3. Почему? Дело в том, что по умолчанию метод sort() сортирует элементы массива как строковые переменные (не проводя конвертирование). Если рассмотреть результат сортировки такого же массива, но содержащего текстовые переменные, то сортировка будет правильной.

sortArray.jsx
Скрипт демонстрирует работу метода sort()
with (app) {
  var myArray = ["14","52","3","14","45","36"];
  myArray.sort();
  alert(myArray);
}

Действительно, строка «14» меньше чем строка «3», более того, даже строка «14567890» меньше, чем «3». Возникает вопрос, а как же отсортировать не текстовые данные, а числа?

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

sortNumbers.jsx
Скрипт демонстрирует возможность сортировки массива чисел
function realSort (x, y) {
  if (x < y) {
    return -1;
  }
  if (x > y) {
    return 1;
  }
  return 0;
}
with (app) {
  var myArray = [14,52,3,14,45,36];
  myArray.sort(realSort);
  alert(myArray);
}

Общие правила написания функций правил сортировки таковы. В качестве параметра функция должна принимать два элемента массива. Если второй элемент больше первого, то функция возвращает -1, если элементы равны, то возвращается 0, если первый элемент больше, то 1. Никогда не следует использовать для сравнения условия <= или >=, поскольку это приводит к зависанию приложения.

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

sortLength.jsx
Скрипт сортирует массив строк в зависимости от длины
function lengthSort (x, y) {
  if (x.length < y.length) {
    return -1;
  }
  if (x.length > y.length) {
    return 1;
  }
  return 0;
}
with (app) {
  var myArray = ["У", "попа", "была", "собака", "он", "ее", "любил"];
  myArray.sort(lengthSort);
  alert(myArray);
}

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

Практическая работа с сортировкой объектов InDesign – в сегодняшнем Полезном скрипте.

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

Сортировка абзацев

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

Основная проблема в том, чтобы переместить абзаца на соответствующие им места. Как известно, коллекция абзацев не является массивом, потому каждый абзац придется перемещать при помощи метода move() на соответствующее место. При этом взаимное положение абзацев изменяется, что опять-таки затрудняет работу.

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

sortParagraphs.jsx
Скрипт демонстрирует работу функции сортировки абзацев
function paragraphSort (myParagraphs, myDirection) {
  var myArray = [];
  for (var counter = 0; counter < myParagraphs.length; counter++) {
    myArray[counter] = myParagraphs[counter].contents;
  }
  myArray.sort();
  if (myDirection) {
    myArray.reverse();
  }
  for (var counter = 0; counter < myArray.length; counter++) {
    for (var pcounter = counter; pcounter < myParagraphs.length; pcounter++) {
      if (myArray[counter] == myParagraphs[pcounter].contents) {
        try {
          myParagraphs[pcounter].move(LocationOptions.before, myParagraphs[0]);
          break;
        } catch (error) {

        }
      }
    }
  }
}

with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myContents = selection[0].contents;
  } catch (eror) {
    exit();
  }
  paragraphSort(selection[0].paragraphs, false);
}

Олег Бутрин
THINK.JS выпуск № 10 от 2007-01-08

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Connecting to %s