Настройте демонстрационный .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.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
в строке меню.
Решение Б
Если вы намерены часто реструктурировать XML-дерево, то есть вы намерены часто перемещать различные дочерние элементы в новый родительский элемент, то я бы рассмотрел возможность использования вспомогательной функции, такой как функция childElementsToNewParent
, показанная ниже. Делая это, вы можете предоставить более простой интерфейс для выполнения этой задачи.
пример-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 после" раздела "Решение 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
- A reference to the document that we want to change its XML structure.
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
.
person
RobC
schedule
10.02.2020