/* * @Author: Vitaly Batushev * @Date: 2017-02-10 15:22:28 * @Last Modified by: Vitaly Batushev!!! * @Last Modified time: 2017-08-28 13:35:40 * @Name: InPoem * @Version: 2.1.0 * @Email: vitaly@batushev.info * @Description: * Скрипт выравнивания строк стихотворения по самой длинной строке в нем * Обходит все текстовые фреймы, относящиеся к материалу (Story) выделенного текстового фрейма, * где ищет абзацы со стилями, указанными в параметрах head_styles (для заголовков стихотворений) * и text_styles (для основного текста стихотворений), которые и попадают под выравнивание */ #target indesign /** * Конфигурация стилей * @head_style — стиль заголовков стихов * @text_style — стиль основного текста стихов * @date_style — стиль даты стихов */ var styles = { head_styles: ["Заголовок стихотворения"], text_styles: ["Стихи"] } var InPoem = (function(){ var config = { head_styles: [], text_styles: [] } var Index = { start: 0, end: 0 } /** * Основная функция объекта InPoem */ var main = function() { app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS; testPublication(); processTextFrames(app.selection[0].parentStory); alert("Стихи обработаны.", "InPoem 2.1"); } /** * Проверка публикации на возможность выполнения скрипта * При невозможности скрипт сообщает о причине отказа */ var testPublication = function() { if (app.documents.length < 1) { alert("Нет открытых публикаций!\nОткрой публикацию и попробуйте снова.", "Ошибка", true); exit(); } if (app.selection.length < 1) { alert("Ничего не выделено!\nВыделите текстовый фрейм и попробуйте снова.", "Ошибка", true); exit(); } if (app.selection[0].constructor.name != "TextFrame") { alert("Выделенный объект не является текстовым фреймов!\nВыделите текстовый фрейм и попробуйте снова.", "Ошибка", true); exit(); } } /** * Обработка текстового фрейма, относящихся к parentStory * @param {Story} story Объект Story, текстовые фреймы которого необходимо обработать */ var processTextFrames = function(story) { var textContainersLength = story.textContainers.length; var progress = new ProgressbarClass(textContainersLength, "Обрабатываются текстовые фреймы", "InPoem 2.0", false); for (var a = 0, l = story.textContainers.length; a < l; a++) { progress.setLabel("Обрабатывается текстовый фрейм " + a + " из " + textContainersLength); var textFrame = story.textContainers[a]; if (textFrame.characters.length == 0) { continue; } var re = new RegExp("([^\u000D]+)|($)", "gim"); var heads = seekRegexp(textFrame, re); Index.start = heads[0][0]; Index.end = 0; for (var i = 0, j = heads.length; i < j; i++) { if (heads[i][1] < 0) { continue; } if (!textFrame.characters[heads[i][1]].isValid) { continue; } var typeStyle = getTypeOfStyle(textFrame.characters[heads[i][1]].appliedParagraphStyle.name); switch(typeStyle) { case "head": if (Index.end > 0) setIndents(textFrame); Index.start = heads[i][1] + 1; break; case "text": Index.end = heads[i][1]; if (i == heads.length - 1 && Index.end > 0) setIndents(textFrame); break; default: if (Index.end > 0) setIndents(textFrame); break; } } progress.increase(); } progress.close(); } /** * Установка отступов * в текстовом фрейме * @param {TextFrame} tf Обрабатываемых текстовый фрейм * @param {Integer} start Индекс первого символа текста * @param {Integer} end Индекс последнего символа текста */ function setIndents(tf) { var text = tf.characters.itemByRange(Index.start, Index.end); var indent = calcIndent(text, tf); indent = Math.round(indent * 10) / 10; for (var a = 0, l = text.lines.length; a < l; a++) { var line = text.lines[a]; if (line.hasOwnProperty("appliedParagraphStyle")) { var type = getTypeOfStyle(line.paragraphs[0].appliedParagraphStyle.name); if (type == 'head' || type == 'text') { line.leftIndent = indent; line.rightIndent = indent; } } } Index.start = 0; Index.end = 0; } /** * Вычисление величины отступа * для данного текста (Text) * @param {Text} text Обрабатываемый текст * @param {TextFrame} tf Текстовый фрейм * @returns {Integer} Величина отступа в пт */ function calcIndent(text, tf) { var values = []; for (var a = 0, l = text.lines.length; a < l; a++) { var paragraph = text.lines[a]; var type = getTypeOfStyle(paragraph.appliedParagraphStyle[0].name); if (type == 'head' || type == 'text') { var startIP = paragraph.insertionPoints.item(0); var endIP = paragraph.insertionPoints.item(paragraph.insertionPoints.length - 2); values.push(endIP.horizontalOffset - startIP.horizontalOffset); } } if (values.length == 0) { return 0; } values.sort(function(a, b) { return a - b; }); width = tf.geometricBounds[3] - tf.geometricBounds[1]; return (width - values[values.length - 1]) / 2; } /** * Определение границ по регулярному выражению * * @param {Text} text Объект Text * @param {RegExp} re Регулярное выражение * @return {Array} Массив из найденных границ начала и конца */ function seekRegexp(tf, re) { var i = 0, result = [], text = tf.contents, found = text.match(re); if (!found) { return result; } for (var a = 0, l = found.length; a < l; a++) { var start = text.indexOf(found[a], i); var end = start + (found[a].length - 1); var type = getTypeOfStyle(tf.characters[start].appliedParagraphStyle.name); if (end > start && (type == 'head' || type == 'text')) { result.push([start, end]); i = end; } } return result; } /** * Получение типа стиля абзаца * * @param {String} styleName Имя стиля * @return {String} "head", "text" или "other" */ function getTypeOfStyle(styleName) { for (var a = 0, l = config.head_styles.length; a < l; a++) { if (config.head_styles[a] == styleName) { return "head"; } } for (var a = 0, l = config.text_styles.length; a < l; a++) { if (config.text_styles[a] == styleName) { return "text"; } } return "other"; } /** * Класс индикатора выполнения * @param {Number} maxValue Максимальное значение индикатора * @param {String} barLabel Текст над индикатором * @param {String} panelTitle Заголовок панели * @param {String} Info Текст под индикатором * * @method close() Закрывает окно индикатора выполнения * @method reset(maxValue, barLabel) Новые значения для максимума и текста над индикатором * @method setVal(barValue) Устанавливает конкретное значение индикатора * @method increase() Увеличивает значение индикатора на единицу * @method setLabel(str) Устанавливает новое значение текста над индикатором * @method setPanelLabel(str) Устанавливает новое значение заголовка панели * @method setInfo(str) Устанавливает новое значение текста под индикатором */ function ProgressbarClass (maxValue, barLabel, panelTitle, viewInfo) { var panelTitle = panelTitle || Locales.ProgressBar.panelTitle; var viewInfo = viewInfo || false; var Info = ""; var win = new Window("palette", "", undefined, { closeButton:true, maximizeButton:false, minimizeButton:false, resizeable:false, borderless: false}); this.windowRef = win; if (viewInfo) { win.pnl = win.add("panel", [10, 10, 440, 210], panelTitle); win.pnl.progBarLabel = win.pnl.add("statictext", [20, 15, 405, 30], barLabel); win.pnl.progBar = win.pnl.add("progressbar", [20, 35, 405, 60], 0, maxValue); win.pnl.log = win.pnl.add("panel", [20, 65, 405, 175], ""); win.pnl.log.info = win.pnl.log.add("statictext", [5, 5, 385, 150], Info, {multiline: true, scrolling: false}); win.pnl.log.info.visible = true; } else { win.pnl = win.add("panel", [10, 10, 440, 100], panelTitle); win.pnl.progBarLabel = win.pnl.add("statictext", [20, 15, 405, 30], barLabel); win.pnl.progBar = win.pnl.add("progressbar", [20, 35, 405, 60], 0, maxValue); } win.cancelBtn = win.add("button", [0, 0, 0, 0], "Отменить"); win.cancelBtn.visible = true; win.cancelElement = win.cancelBtn ; this.terminated = false; win.onCancel = function() { this.isDone = true; return true; } win.center(); win.show(); this.close = function() { win.close(); }; this.reset = function (maxValue, barLabel) { this.windowRef.pnl.progBar.maxvalue = maxValue; this.windowRef.pnl.progBar.value = 0; this.windowRef.pnl.progBarLabel.text = barLabel; }; this.setVal = function (barValue) { this.windowRef.pnl.progBar.value = barValue; }; this.increase = function () { this.windowRef.pnl.progBar.value++; }; this.setLabel = function (str) { this.windowRef.pnl.progBarLabel.text = str; }; this.setPanelLabel = function (str) { this.windowRef.pnl.text = str; }; this.setInfo = function (str) { this.windowRef.pnl.log.info.text = str; }; } return { config: config, run: main } })(); InPoem.config.head_styles = styles.head_styles; InPoem.config.text_styles = styles.text_styles; InPoem.run();