Think.JS №08

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

Содержание

Для начинающих: Поиск текста. Общие сведения

Учим матчасть: Операции с файлами и папками

Полевые испытания: Книга – основа знания

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

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

Поиск текста. Общие сведения

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

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

textReplace.jsx
Скрипт демонстрирует метод поиска-замены текста
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    exit();
  }
  myStory.search("a", false, false, "AB");
}

Параметры метода должны идти в строго определенном порядке. В первую очередь передается текстовая переменная, которая определяет искомый текст. Вторым параметром является логическая переменная, которая определяет, будет ли при поиске учитываться регистр символов (true – да, false — нет). Третий параметр определяет, будет ли искаться целое слово, а не произвольный текст (true – да, false — нет). Четвертый параметр – текстовая переменная, которая определяет, на какой текст будет заменен найденный. Обязательным является только первый параметр, остальные могут быть пропущены, но делать это не рекомендуется, поскольку при неправильном порядке параметров может произойти ошибка выполнения скрипта.

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

searchResult.jsx
Скрипт демонстрирует результат поиска в тексте.
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    exit();
  }
  var myResult = myStory.search("a", false, false);
  alert(myResult);
}

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

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

Для расширения возможностей поиска и замены в InDesign введены два специальных объекта: findPreferences и changePreferences. Эти объекты являются свойствами объекта app и определяют настройки поиска и замены соответственно. Эти настройки хранятся в InDesign между выполнениями скриптов, поэтому перед поиском желательно обнулить их значения, чтобы случайно не воспользоваться настройками, которые были сделаны другим скриптом или пользователем. Окно Find/Change отображает настройки поиска-замены независимо от того, как были сделаны настройки – вручную пользователем или из скрипта.

changeOptions.jsx
Скрипт демонстрирует использование changeOptions
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    exit();
  }
  findPreferences = null;
  changePreferences = null;
  changePreferences.fontStyle = "Bold";
  myStory.search("a", false, false, "a");
}

После выполнения этого скрипта можно посмотреть настройки, сделанные им в окне Find/Change. Чтобы не оставлять «хвостов» желательно обнулять настройки не только до, но и после поиска.

clearChange.jsx
Скрипт демонстрирует обнуление значений findPreferences и changePreferences после поиска
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    exit();
  }
  findPreferences = null;
  changePreferences = null;
  changePreferences.fontStyle = "Italic";
  myStory.search("b", false, false, "b");
  findPreferences = null;
  changePreferences = null;
}

В строке поиска или замены могут использоваться специальные символы InDesign, которые можно узнать в окне Find/Change. Штатными средствами текстовых редакторов невозможно указать в строке поиска символ окончания абзаца, поэтому нужно использовать символ «^p».

paragraphSplit.jsx
Скрипт меняет знаки конца абзаца на пробелы
with (app) {
  try {
    var myDoc = activeDocument;
  } catch (error) {
    exit();
  }
  try {
    var myStory = selection[0].parentStory;
  } catch (error) {
    exit();
  }
  findPreferences = null;
  changePreferences = null;
  myStory.search("^p", false, false, " ");
}

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

Операции с файлами и папками

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

При объявлении нового объекта типа Folder, который указывает на несуществующую папку, свойство exists для него будет равно false. При этом папка автоматически не создается. Для создания папок используется метод create(), который возвращает true в том случае, если удалось создать папку по указанному имени объекта Folder, или false – в том случае, если этого сделать не удалось. Для удаления папки используется метод remove(), который также возвращает true, если удаление папки произошло или false – в обратном случае.

createDeleteFolder.jsx
Скрипт создает временную папку и удаляет ее
with (app) {
  var myFolder = Folder(activeScript.path + "/Temporary Script Folder");
  if (!myFolder.exists) {
    if (myFolder.create()) {
      alert("Папка " + decodeURI(myFolder.name) + " была успешно создана!");
    } else {
      alert("Папку " + decodeURI(myFolder.name) + " создать не удалось!");
    }
    if (myFolder.remove()) {
      alert("Папка " + decodeURI(myFolder.name) + " была успешно удалена!");
    } else {
      alert("Папку " + decodeURI(myFolder.name) + " удалить не удалось!");
    }
  }
}

Для создания файла не предусмотрена специальная функция. Если файл не существует, то при использовании метода open() с ключом “w”
(открыть файл для записи) он будет создан. Если создание файла прошло без ошибок, метод вернет true, иначе — false Для закрытия файла используется метод close(), который возвращает true, если файл удалось закрыть (в том случае, если файл не был открыт другим процессом) или false – в обратном случае. Для удаления файла используется метод remove(), который идентичен соответствующему методу Folder.

createDeleteFile.jsx
Скрипт создает и удаляет файл
with (app) {
  var myFile = File(activeScript.path + "/TSF.txt");
  if (!myFile.exists) {
    if (myFile.open("w")) {
      alert("Файл " + decodeURI(myFile.name) + " удалось создать!");
      myFile.close();
    } else {
      alert("Файл " + decodeURI(myFile.name) + " не удалось создать!")
    }
    if (myFile.remove()) {
      alert("Файл " + decodeURI(myFile.name) + " удалось удалить!");
    } else {
      alert("Файл " + decodeURI(myFile.name) + " не удалось удалить!");
    }
  }
}

Для записи текста в файл используются методы write() и writeln(). Они отличаются тем, что второй автоматически добавляет знак окончания строки (Line Feed – в различных операционных системах может быть разным) к тексту, переданному в качестве параметра. Оба метода возвращают true при успешном выполнении и false – в обратном случае.

writeText.jsx
Скрипт демонстрирует возможность записи текста в файл
with (app) {
  var myFile = File(activeScript.path + "/TSF.txt");
  var myString = "Строка текста"
  try {
    myFile.open("w");
    myFile.writeln(myString);
    myFile.close();
    myFile.execute()
  } catch (error) {
    alert(error)
  }
}

Метод execute() запускает файл на выполнение. Если файл ассоциирован с приложением в системе, он откроется (или выполнится, если, например, записать cmd-файл в Windows). Традиционно метод возвращает true в результате успешного выполнения и false – при неуспешном.

Чтение файла выполняется при помощи методов read() и readln().

Метод read() читает указанное в качестве параметра число символов из файла. Если параметр не указан, то будут прочитан весь файл.

readFile.jsx
Скрипт демонстрирует метод чтения файла
with (app) {
  var myFile = File(activeScript.path + "/TSF.txt");
  var myString = "";
  try {
    myFile.open("r");
    myString += myFile.read();
    myFile.close();
  } catch (error) {
    alert(error)
  }
  alert(myString);
}

Метод readln() используется для чтения файла построчно. Для того чтобы прочитать весь файл, используется цикл с проверкой свойства File.eof
(конец файла). После чтения текущая позиция смещается на начало следующей строки. Если в позиции чтения оказывается знак окончания файла, то свойство File.eof будет равно true.

readlnFile.jsx
Скрипт демонстрирует метод чтения файла
with (app) {
  var myFile = File(activeScript.path + "/TSF.txt");
  var myString = "";
  try {
    myFile.open("r");
    while (!myFile.eof) {
      myString += myFile.readln() + "\n";
    }
    myFile.close();
  } catch (error) {
    alert(error);
  }
  alert(myString);
}

В следующий раз мы займемся практической работой с файлами – напишем перекодировщик текстовых файлов и функцию для сохранения настроек диалоговых окон в ini-файл.

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

Книга – основа знания

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

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

createBook.jsx
Скрипт демонстрирует метод создания книги
with (app) {
  var myBookFile = File.saveDialog("Создать книгу:", "InDesign Book: *.indb");
  if (myBookFile != null) {
    var myBook = books.add(myBookFile);
  }
}

Книга не содержит объектов типа document – вместо них используются объекты типа bookContent. В свойстве bookContent.fullName хранится ссылка на имя файла документа. Кроме того, можно узнать дату последнего сохранения документа (date), его размер (size), диапазон страниц документа (documentPageRange) и его статус (status).

Добавление документов в книгу производится при помощи метода book.bookContents.add().

addAllDocToBook.jsx
Скрипт создает книгу и добавляет в нее все открытые документы
with (app) {
  var myBookFile = File.saveDialog("Создать книгу:", "InDesign Book: *.indb");
  if (myBookFile != null) {
    var myBook = books.add(myBookFile);
  }
  myBook.automaticPagination = false;
  for (var counter = 0; counter < documents.length; counter++) {
    myBook.bookContents.add(documents[counter].fullName);
  }
}

Автоматическая пагинация (myBook.automaticPagination) в этом случае отключается для того, чтобы не произошло автоматического изменения нумерации в открытых документах.

Документы в книге можно перемещать относительно друг друга при помощи метода move(). При включенной автоматической пагинации при этом происходит переопределение нумерации.

Один из объектов bookContents в книге является источником «эталонных» стилей (абзаца, знака, объектов и пр.). При синхронизации книги методом synchronize() все документы импортируют стили эталонного, который определен в свойстве book.styleSourceDocument.

synchronizeBook.jsx
Скрипт устанавливает эталонный документ и синхронизирует книгу
with (app) {
  var myBookFile = File.openDialog("Открыть книгу:", "InDesign Book: *.indb");
  if (myBookFile != null) {
    var myBook = open(myBookFile);
  }
  if (myBook.bookContents.length > 0) {
    myBook.styleSourceDocument = myBook.bookContents[0].fullName;
    myBook.synchronize();
  }
}

Включить или отключить синхронизацию некоторой категории стилей можно при использовании коллекции synchronizeOptions.

synchronizeBookWOSwatches.jsx
Скрипт устанавливает эталонный документ и синхронизирует книгу без синхронизации swatches
with (app) {
  var myBookFile = File.openDialog("Открыть книгу:", "InDesign Book: *.indb");
  if (myBookFile != null) {
    var myBook = open(myBookFile);
  }
  myBook.synchronizeOptions.item("Swatches").useStyle = false;
  if (myBook.bookContents.length > 0) {
    myBook.styleSourceDocument = myBook.bookContents[0].fullName;
    myBook.synchronize();
  }
}

Список доступных synchronizeOptions не указан в документации по скриптингу, но его легко найти с помощью цикла.

Сохранить книгу можно при помощи метода save(), в качестве необязательного параметра которому передается новое имя файла книги (если параметр не указан, книга сохраняется в файле, переданном при создании). После изменений книги при помощи скрипта ее желательно сохранять. Закрывается книга при помощи метода close(), который имеет два необязательных параметра: saving и savingIn. Параметр saving определяет поведение при закрытии: значение SaveOptions.no означает, что книга будет закрыта без сохранения, SaveOptions.yes – с сохранением, при SaveOptions.ask будет выведено диалоговое окно с вопросом о сохранении книги. Параметр savingIn определяет новое имя файла книги при сохранении.

Некоторые практические методы работы с книгами приведены в сегодняшнем Полезном скрипте.

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

Перейти к странице книги

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

Скрипт gotoBookPage.jsx позволяет выбрать нужную книгу, если в InDesign открыто несколько, и задать нужную страницу. При этом возможно закрытие с сохранением всех открытых документов.

Для удобства программирования, в скрипте создается массив объектов пользовательского типа docRange, который имеет три свойства: document – ссылка на файл документа книги, firstPage – номер первой страницы документа, lastPage – номер последней страницы. Поиск документа, который содержит указанную страницу, осуществляется простым перебором значений. Если в книге нет документа с указанной страницей, скрипт выводит сообщение об этом.

gotoBookPage.jsx
Скрипт для перехода на указанную страницу в книге
var langAlertNoBook = localize({ru: "Нет открытых книг!"});
var langAlertErrorDoc = localize({ru: "Ошибка при открытии документа!"});
var langAlertErrorPage = localize({ru: "Выбранной страницы в книге нет!"});
var langUISelectBook = localize({ru: "Выберите книгу:"});
var langUISelectPage = localize({ru: "Выберите страницу:"});
var langUISaveClose = localize({ru: "Сохранить и закрыть остальные документы"});
var iniScriptName = "GoTo Book Page";
var iniScriptVersion = "1.0";
Array.prototype.firstItem = function() {
  return this[0];
}
Array.prototype.lastItem = function() {
  return this[this.length - 1];
}
function getNames (myArray, myNum) {
  var myResult = new Array();
  try {
    for (var elementCounter = myNum; elementCounter < myArray.length; elementCounter++) {
      try {
        myResult.push(myArray[elementCounter].name);
      } catch (error) {
        myResult.push(String(myArray[elementCounter]));
      }
    }
  } catch (error) {
    myResult = null;
  }
  return myResult;
}
function getBookContentNames (myBook) {
  var myArray = new Array();
  for (var contCounter = 0; contCounter < myBook.bookContents.length; contCounter++) {
    var myDoc = myBook.bookContents[contCounter];
    myArray.push(new File(myDoc.fullName).name);
  }
  return myArray;
}
function docRange (myBookContent) {
  if (myBookContent == null || (myBookContent.constructor.name != "BookContent")) {
    return null;
  }
  var rangeArray = myBookContent.documentPageRange.split("-");
  this.firstPage = Number(rangeArray.firstItem());
  this.lastPage = Number(rangeArray.lastItem());
  if (isNaN(this.firstPage) || isNaN(this.lastPage)) {
    return null;
  }
  this.document = File(myBookContent.fullName);
}
function docRangeOpen (myPage) {
  try {
    var myDoc = app.open(this.document, false);
  } catch (error) {
    return null;
  }
  app.activeDocument = myDoc;
  app.activeWindow = myDoc.windows.add();
  app.activeWindow.activePage = myDoc.pages[myPage - Number(myDoc.pages.firstItem().name)];
  return true;
}
docRange.prototype.open = docRangeOpen;
with (app) {
  switch (books.length) {
    case 0:
      alert(langAlertNoBook);
      exit();
      break;
    case 1:
      var myBook = books[0];
      break;
    default:
      var bookDialog = dialogs.add({name: langUISelectBook});
      with (bookDialog.dialogColumns.add().borderPanels.add().dialogColumns.add()) {
        var uiBook = dialogRows.add().dropdowns.add({stringList: getNames(books, 0), selectedIndex:0});
      }
      var myResult = bookDialog.show();
      if (!myResult) {
        bookDialog.destroy;
        exit();
      }
      var myBook = books[uiBook.selectedIndex];
      break;
  }
  var myDocRanges = new Array();
  for (var counter = 0; counter < myBook.bookContents.length; counter++) {
    myDocRanges[counter] = new docRange(myBook.bookContents[counter]);
  }
  var pageDialog = dialogs.add({name: iniScriptName + " " + iniScriptVersion});
  with (pageDialog.dialogColumns.add()) {
    with (dialogRows.add().borderPanels.add().dialogColumns.add()) {
      with (dialogRows.add()) {
        with (dialogColumns.add()) {
          dialogRows.add().staticTexts.add({staticLabel: langUISelectPage});
        }
        with (dialogColumns.add()) {
          var userPage = dialogRows.add().integerEditboxes.add({minimumValue: myDocRanges.firstItem().firstPage, maximumValue: myDocRanges.lastItem().lastPage, editValue: myDocRanges.firstItem().firstPage, largeNudge: 10, smallNudge: 1});
        }
      }
      var userSaveClose = dialogRows.add().checkboxControls.add({staticLabel: langUISaveClose, checkedState: false})
    }
    dialogRows.add().staticTexts.add({staticLabel: "\u00A9 Oleg Butrin @ http://toolbox.rudtp.ru"});
  }
  var myResult = pageDialog.show();
  if (!myResult) {
    exit();
  }
  if (userSaveClose.checkedState) {
    while (documents.length > 0) {
      documents[0].close(SaveOptions.yes);
    }
  }
  var currentDocRange = null;
  for (var counter = 0; counter < myDocRanges.length; counter++) {
    if ((userPage.editValue >= myDocRanges[counter].firstPage) && (userPage.editValue <= myDocRanges[counter].lastPage)) {
      var currentDocRange = myDocRanges[counter];
      break;
    }
  }
  if (currentDocRange != null) {
    var myResult = currentDocRange.open(userPage.editValue);
  } else {
    alert(langAlertErrorPage);
    exit();
  }
  if (!myResult) {
    alert(langAlertErrorDoc + " " + currentDocRange.document.name);
    exit();    
  }
}

Олег Бутрин
THINK.JS выпуск № 8 от 2006-12-18

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Connecting to %s