Настройка демонстрации .indd
Чтобы обеспечить некоторый контекст для этого ответа, а также для облегчения объяснения и демонстрации, сначала импортируйте следующий документ XML в новый документ inDesign:
sample. xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
<EL1>Para 1</EL1>
<EL2>Para 2</EL2>
<EL3>Para 3</EL3>
<EL4>Para 4</EL4>
<EL5>Para 5</EL5>
<EL6>Para 6</EL6>
<EL7>Para 7</EL7>
<EL8>Para 8</EL8>
<EL9>Para 9</EL9>
</Root>
Чтобы импортировать sample.xml
в новый документ, выполните следующие действия:
- Создайте новый документ InDesign.
- Выберите
View
> Structure
> Show Structure
в строке меню, чтобы открыть панель XML Структура . - В структуре XML На панели выберите элемент по умолчанию
Root
XML. - На панели XML Структура выберите
Import XMl
в раскрывающемся меню. - Найти вышеупомянутый
sample.xml
файл и откройте его. - Наконец сохраните документ
Примечание: В этом ответе нам потребуется отменить .indd
верните файл в исходное состояние, поэтому сохраните его.
Дерево документов XML теперь должно иметь структуру следует:
Начальная XML древовидная структура:
Root
├── EL1
├── EL2
├── EL3
├── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9
Как вы можете видеть, это очень похоже на то, что вы описали в своем вопросе, однако есть только несколько больше элементов, а именно; EL6
, EL7
, EL8
, EL9
.
Решение A
example-a.jsx
#target indesign
// 1. Obtain a reference to the active document.
var doc = app.activeDocument;
// 2. Obtain a reference to the root element
var root = doc.xmlElements.item(0);
// 3. Create a new tag
var newParentTag = doc.xmlTags.add("EL");
// 4. Create a new element node
var parentNode = root.xmlElements.add(newParentTag.name);
// 5. Change the position of the newly created element node
parentNode.move(LocationOptions.before, root.xmlElements.item(1));
// 6. Move elements to be children of the newly created parent element.
root.xmlElements.item(4).move(LocationOptions.atBeginning, root.xmlElements.item(1));
root.xmlElements.item(3).move(LocationOptions.atBeginning, root.xmlElements.item(1));
root.xmlElements.item(2).move(LocationOptions.atBeginning, root.xmlElements.item(1));
Если мы запустим код, предоставленный в example-a.jsx
(выше), он будет реструктурировать дерево XML следующим образом:
XML древовидная структура после:
Root
├── EL1
├── EL
│ ├── EL2
│ ├── EL3
│ └── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9
Примечание: Прежде чем перейти к Решение B (ниже), пожалуйста, верните демонстрационный документ inDesign в исходное состояние. Выберите File
> Revert
в строке меню.
Решение B
Если вы намереваетесь часто реструктурировать дерево XML, т.е. вы собираетесь двигаться различные дочерние элементы к новому родительскому элементу часто, тогда я рассмотрел бы использование вспомогательной функции, такой как функция childElementsToNewParent
, показанная ниже. Делая это, вы можете предоставить более простой интерфейс для выполнения этой задачи.
example-b.jsx
#target indesign
$.level=0;
//------------------------------------------------------------------------------
// Usage
//------------------------------------------------------------------------------
// 1. Obtain a reference to the active document.
var doc = app.activeDocument;
// 2. Obtain a reference to the root element
var rootElement = doc.xmlElements.item(0);
// 3. Restructure the XML tree.
childElementsToNewParent(doc, rootElement, 2, 4);
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Moves child element(s) at a given position within a given parent element to
* a new parent element.
*
* @param {Object} doc - A document reference for changing its XML structure.
* @param {Object} parent - The parent XMlElement whose children need to move.
* @param {Number} from - The position of the first child element to move.
* @param {Number} to - The position of the last child element to move.
* @param {Object} options - The configuration options.
* @param {String} [options.tagName=undefined] - A custom name for the newly
* created parent XML element.
*/
function childElementsToNewParent(doc, parent, from, to, options) {
// Default options
var opts = {
tagName: undefined
}
// Override the default opts with any user defined options.
opts = assign(options, opts);
var xPath = '*[position() >= ' + from + ' and position() <= ' + to + ']';
var childrenToMove = parent.evaluateXPathExpression(xPath);
// XMLElements matched by the `evaluateXPathExpression` method are returned
// in any order. We sort each element object in the array by it positional
// index to ensure that when we move them to a new parent element their
// positional order is preserved as-is.
childrenToMove = sortArrayOfObjects(childrenToMove, 'index');
var firstChildToMove = childrenToMove[0];
var firstChildToMoveIndex = firstChildToMove.index;
var xmlTagName = opts.tagName
? opts.tagName
: firstChildToMove.markupTag.name.substring(0, 2);
createXmlTag(doc, xmlTagName);
// Move the newly created parent XMLElement to
// before the first child element to be moved.
parent.xmlElements.add(xmlTagName).move(
LocationOptions.before,
parent.xmlElements.item(firstChildToMoveIndex)
);
// Move each the matched child XMLElement(s) into their new parent XMLElement.
for (var i = 0, max = childrenToMove.length; i < max; i++) {
childrenToMove[i].move(
LocationOptions.atEnd,
parent.xmlElements.item(firstChildToMoveIndex)
);
}
}
/**
* Enumerates own properties of a 'source' object and copies them to a 'target'
* object. Properties in the 'target' object are overwritten by properties in
* the 'source' object if they have the same key.
*
* @param {Object} source - The object containing the properties to apply.
* @param {Object} target - The object to apply the source object properties to.
* @returns {Object} - The target object.
*/
function assign(source, target) {
if (typeof source === 'object') {
for (key in source) {
if (source.hasOwnProperty(key) && target.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
}
/**
* Sorts array of objects by value of property name in ascending order.
*
* @param {Array} arr - The array of objects to sort.
* @param {String} prop - The name of the object property to sort by.
* @returns {Array} - Array of objects sorted by value of property name.
*/
function sortArrayOfObjects(arr, prop) {
return arr.sort(function sortByPropertyValue(a, b) {
if (a[prop] < b[prop]) {
return -1;
}
if (a[prop] > b[prop]) {
return 1;
}
return 0;
});
}
/**
* Creates a new XML tag if the given name does not already exist.
*
* @param {String} tagName - The name of the XML tag.
* @param {Object} doc - A reference to the document to add the XMl tag to.
*/
function createXmlTag(doc, tagName) {
var hasTag = inArray(tagName, doc.xmlTags.everyItem().name);
if (! hasTag) {
doc.xmlTags.add(tagName);
}
}
/**
* Determines whether an array includes a certain value among its elements.
*
* @param {String} valueToFind - The value to search for.
* @param {Array} arrayToSearch - The array to search in.
* @returns {Boolean} true if valueToFind is found within the array.
*/
function inArray(valueToFind, arrayToSearch) {
for (var i = 0, max = arrayToSearch.length; i < max; i++) {
if (arrayToSearch[i] === valueToFind) {
return true;
}
}
return false;
}
Если мы запустим код, указанный в example-a.jsx
(выше) оно реструктурирует дерево XML до той же результирующей структуры, как показано в древовидной структуре "XML после раздела" "Solution A" .
Примечание: Пока не возвращайте документ indd
- оставьте его как есть, чтобы следующий пример использования был актуальным.
Другой пример использования example-b.jsx
Допустим, теперь мы хотим реструктурировать дерево из показанной ранее "XML древовидной структуры после состояния" в следующее состояние:
Следующая желаемая XML древовидная структура:
Root
├── EL1
├── EL
│ ├── EL2
│ └── section
│ ├── EL3
│ └── EL4
├── EL5
├── EL6
├── EL7
├── EL8
└── EL9
Для достижения этой структуры нам необходимо заменить следующую строку:
// 3. Restructure the XML tree.
childElementsToNewParent(doc, rootElement, 2, 4);
... которая в настоящее время определена в example-b.jsx
, то есть в строке, которая вызывает функцию childElementsToNewParent
, вместо нее используются следующие две строки:
var parentElement = rootElement.xmlElements.item(1);
childElementsToNewParent(doc, parentElement, 2, 3, { tagName: 'section' });
На этот раз мы по существу:
Получаем ссылку на элемент EL
(так как это родительский элемент, содержащий дочерние элементы, которые мы хотим переместить) и присваиваем его переменная с именем parentElement
.
вызов функции childElementsToNewParent
со следующими аргументами:
doc
- ссылка на документ, который мы хотим изменить его XML структуру. parentElement
- переменная, значение которой является ссылкой на элемент EL
. 2
- первая позиция дочернего элемента, которая мы хотим переместить. 3
- последняя позиция дочернего элемента, который мы хотим переместить. { tagName: 'section' }
- объект options
, который включает ключ / свойство с именем tagName
со значением section
.
Примечание: На этот раз мы передали необязательный объект options
, в котором указано имя для недавно созданный родительский элемент, а именно section
. При вызове функции childElementsToNewParent
без предоставления значения для tagName
в объекте options
мы выводим имя для нового родительского элемента, используя первые два символа первого дочернего элемента, который необходимо переместить. Согласно вашему комментарию:
... имя родителя - это первые два символа выбранных элементов
Дополнительные примечания
Вы заметили, что в последнем примере мы получили ссылку на элемент EL
(т. Е. Ссылку на родительский элемент, содержащий дочерние элементы, которые мы хотели переместить), используя следующую запись:
var parentElement = rootElement.xmlElements.item(1);
... который может получить довольно долго при желании получить ссылку на глубоко вложенный элемент. В конечном итоге вы сделаете что-то вроде этого:
var parentElement = rootElement.xmlElements.item(1).xmlElements.item(3).xmlElements.item(1) ....
Я предпочитаю вместо этого использовать метод evaluateXPathExpression
, так как это позволяет нам сопоставить элемент с помощью xpath выражение. Например, чтобы получить ссылку на элемент EL
, мы могли бы сделать это вместо
var parentElement = rootElement.evaluateXPathExpression('EL')[0];
Как вы можете видеть, мы также используем метод evaluateXPathExpression
в теле функции childElementsToNewParent
чтобы получить ссылку на дочерние элементы, которые мы хотим переместить:
var xPath = '*[position() >= ' + from + ' and position() <= ' + to + ']';
var childrenToMove = parent.evaluateXPathExpression(xPath);
Это выражение использует функцию XPath position()
для поиска дочернего элемента элементы в заданном позиционном диапазоне. По сути, это позиционный диапазон, который вы определяете при передаче аргументов from
и to
в функцию childElementsToNewParent
.