Обзор подходов к редактированию docx как xml

18.04.25

Разработка - Инструментарий разработчика

Работа с файлами Word без COM (Component Object Model), т. е. без необходимости иметь установленное приложение MS-Word на сервере или рабочем месте пользователя, вызывает постоянный интерес у разработчиков. Об этом свидетельствуют систематически публикуемые на Инфостарте материалы с тем или иным подходом к обработке файлов docx. Статья представляет собой обзор работ на данную тематику, опубликованных разными авторами. Указаны особенности, преимущества и недостатки каждого подхода.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Примеры работы с файлами docx
.epf 77,67Kb
1
1 Скачать (1 SM) Купить за 1 850 руб.

Оглавление:

Об устройстве docx как XML

XML как текст, наивный поиск

Использование DOM

Использование именованных полей (свойств документа)

Подход из Документооборота

XML-инъекция

Чтение XML в память

Вызовы БСП

Источники

 

 

Об устройстве docx как XML

Кратко повторю основные постулаты об устройстве файлов docx. Любой файл docx является архивом zip, достаточно изменить расширение файла, чтобы в этом убедиться. В архиве содержится несколько папок и файлов. Чаще всего разработчиков интересует файл word\document.xml, в котором записано основное текстовое содержимое документа. Картинки, содержимое колонтитулов и примечания записаны в других файлах. На самом деле, стандарт предполагает, что основное содержимое документа не обязательно располагается в файле word\document.xml. Следуя стандарту, нужно сначала прочитать из файла [Content_Types].xml, который всегда располагается в корне zip-архива, элемент "Override" с атрибутом "ContentType" равным "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" и из атрибута "PartName" этого элемента прочитать путь к файлу с основным содержимым документа. Однако, во всех существующих версиях Ворда это всегда будет файл word\document.xml, поэтому большинство авторов рассматриваемых здесь работ пропускают этап с определением расположения требуемой части документа и сразу обращаются к файлу по фиксированному расположению.

Азы работы с содержимым файлов docx изложены в статье [1] и книге [2], электронный вариант которой легко гуглится. Ответы на любые вопросы, традиционно, содержаться в стандарте [3], доступном без ограничений на официальном сайте.

Значительная часть подходов к редактированию docx сводится к размещению в тексте документа специальных текстовых фрагментов, которые затем будут использоваться для определения точек внесения правок в документ. В самом простом случае эти текстовые фрагменты заменяются на новый текст, например "ПодписантФИО" заменяется на "Пупкин В.И.". Однако, текстовые фрагменты можно использовать не только для простой замены текста, они могут указывать место для размещения картинки в тексте, указывать начало или конец большой области текста, которую требуется удалить или переместить в другое место документа, могут указывать строку таблицы, которую нужно продублировать и заполнить значениями и т.д. Общепринятого термина для таких текстовых фрагментов не существует, далее я буду называть текстовые фрагменты, размещенные в документе для целей его автоматического редактирования якорями. Подход с использованием якорей для автоматического редактирования текстовых документов не нов и применялся еще в эпоху COM[4].

 

XML как текст, наивный поиск

Наиболее ранняя статья по данной теме найденная на Инфостарте [5] опирается на самый простой подход к редактированию документа. Суть подхода в том, что любой файл  XML это в первую очередь текст. И в этом тексте присутствуют все якоря, поэтому можно средствами работы с текстовыми файлами просто заменить текст якорей на требуемые значения. Подход вполне рабочий и для каких-то простых задач может подойти. Сразу очевидны и недостатки такого подхода. Поскольку чтения и разбора тегов XML не происходит, в документ невозможно внесение каких-либо более-менее сложных изменений - ни изменить шрифт, цвет, заливку, ни обработать таблицы. По сути, возможна только простая замена одних текстовых строк на другие.

Однако, даже в рамках такого простого подхода возникают нюансы, которые требуется обсудить подробнее. Для этого погрузимся в детали записи документа в файл docx. Совсем упрощено структура файла, в котором записан текст "Hello world!", будет иметь вид:

 

Здесь элемент <w:p> соответствует абзацу, элемент <w:r> соответствует области текста, имеющей одинаковое форматирование (шрифт, цвет, размер). Внутри элемента <w:p> может находится любое количество элементов <w:r>. В свою очередь, элементы <w:r> могут содержать один или несколько элементов <w:t>, содержимое которых и есть текстовое содержимое документа. В приведенном примере один абзац содержит единственную область, которая содержит единственный текстовый элемент. 

В реальных файлах, которые сохраняет приложение MS Word, каждый из элементов <w:r> содержит по единственному элементу <w:t>. А вот элементы <w:r> MS-Word создает весьма щедро. В некоторых случаях логика их создания очевидна. К примеру, если мы покрасим каждую букву текста новым цветом Hello world!, то при записи такого текста будет создано столько элементов <w:r>, сколько символов в тексте, у каждого элемента <w:r> будет не менее двух подчиненных элементов. Один элемент устанавливающий свойства шрифта, для простоты этот элемент я не указывал в примере. И текстовый элемент <w:t>, содержащий единственный символ текста.

Но, даже если мы не будем менять свойства текста, тот же файл может сохраниться вот так:

 

Это полностью корректный файл, внутренняя логика MS Word решила его сохранить именно так и не иначе, никакого нарушения стандарта в таком сохранении нет. Более того, это как раз типичное поведение MS Word. Если вы откроете document.xml любого файла Ворд, то не увидите там больших блоков осмысленного текста, текст будет разбит на небольшие фрагменты.

А теперь представьте, вы пытаетесь в файле document.xml заменить подстроку "Hello" на подстроку "Привет". Очевидно, если файл сохранился так, как в последнем примере, такая замена не будет выполнена, т.к. подстроки "Hello" в файле нет. Полноценное решение, основанное на поиске текстовых фрагментов, должно анализировать структуру файла и последовательно искать в значениях элементов <w:t>, учитывая, что каждый из элементов соответствует любому фрагменту текста, от одного символа до нескольких слов.

Однако, не все обсуждаемые здесь решения будут учитывать эту особенность. Часть решений, аналогично обсуждаемой статье, будут исходить из предположения, что ни один из якорей не разорван на разные элементы. Т.е. каждый якорь полностью находится в одном из элементов <w:t>. Такой поиск якорей далее я буду называть наивным.

Как правило, авторы подходов, основанных на наивном поиске, знают о проблеме. И либо оговаривают её в конце статьи, либо обсуждают в комментариях к статье. Для обхода проблемы есть две группы решений, назовем их условно "костыли" и "таблетки".

В качестве костылей предлагаются следующие действия:

- Проверить шаблон, например, выполнив код его заполнения. Если в ходе проверки был выявлен проблемный якорь, то нужно открыть файл шаблона в редакторе MS Word, удалить проблемный якорь, затем скопировать якорь целиком в буфер (из другого файла) и вставить его. После этого, не совершая каких-либо действий, сразу сохранить файл шаблона. Вероятность, что такой якорь будет разорван крайне низкая.

- Не использовать в якорях спецсимволы []$#@ и т.д. Вероятность, что текст будет разорван по спецсимволу, крайне высока.

- После создания шаблона, распаковать файл, открыть document.xml в текстовом редакторе, проконтролировать целостность якорей, при необходимости вручную исправить файл, объединив проблемные якоря в один элемент <w:t>.

Под таблеткой я понимаю автоматическое, т.е. с помощью программы, исправление разрывов якорей в файле. Пример такой таблетки можно найти в комментариях к обсуждаемой статье [6]. Забавно, таблетка использует разбор файла через DOM (Document Object Model), т.е. более технологична, чем применяемое решение по заполнению шаблона.

Для работы таблетки предполагается, что якоря должны быть обрамлены слева и справа каким-то специальным символом. Автор таблетки использует значки меньше и больше ("<", ">"). 

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

Работа с xml как с простым текстом применяется также в работе [7]. Можно сказать, что эта работа представляет собой две статьи, т.к. состоит из двух не связанных по смыслу частей. В первой части речь идет о работе с шаблонами docx через вызовы подпрограмм БСП. А во второй части изложен способ программной вставки в файл docx закладок. Сейчас рассмотрим только вторую часть. Для её понимания следует ознакомится с закладками поближе.

Идея закладок была реализована в MS Word для удобного и быстрого перехода в нужную точку объемного документа. Сами закладки не видны при печати и не мешают читать и редактировать документ. Побочный эффект закладок - в объектной модели вордовского документа имеется коллекция именованных объектов, каждый из которых привязан к некоторому фрагменту текста. Во времена COM существовал способ заполнения шаблонов с использованием закладок (см. [4]).

Закладка в document.xml схематично будет описана следующим образом:

 

Здесь закладка определена для подстроки "lo wo" текста "Hello world!".

Можно видеть, что используется два элемента, один соответствует точке начала закладки, второй точке окончания закладки. Закладка может начаться в одном абзаце, а закончится в другом, т.е. между элементами начала и завершения закладки может находится произвольное количество элементов <w:p>, <w:r> и <w:t>. Атрибуты w:name и w:id должны быть уникальны в пределах документа.

То, как закладки описываются в document.xml, дает удобный способ использовать их для заполнения шаблонов. Достаточно просто последовательно читать элементы, для элементов w:bookmarkStart проверять значение атрибута w:name, если оно совпадает с искомым, найти соответствующий элемент w:bookmarkEnd. Далее можно просто удалить все элементы внутри закладки, заменив их на <w:r><w:t>новый текст</w:t></w:r>. Этот способ лишен проблем наивного поиска якорей. Действительно, тут мы имеем дело непосредственно с правилами XML, которые не предполагают каких-либо разрывов в значениях атрибутов.

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

Рассмотрим особенности предложенного решения.

Как уже отмечено выше, документ XML открывается как обычный текст, поиск нужных точек вставки осуществляется вызовами функции СтрНайти(), при необходимости указывается направление поиска и точка начала поиска. Вычисляется позиция требуемого символа текста, затем исходная строка XML разрезается в вычисленной точке через вызовы Лев() и Прав() и в финале конкатенируется в новую строку.

Первое наблюдение, которое сразу бросается в глаза, - поиск якорей наивный.

Второе наблюдение - закладка будет вставлена не непосредственно на якорь, а может включать в себя также и некоторый текст, расположенный рядом с якорем. Это связано с особенностями реализации. После нахождения якоря ищется ближайшая подстрока "<w:r" левее найденного якоря и ближайшая подстрока "</w:r>" правее. Именно в найденные точки и вставляются теги соответственно начала и конца закладки. Чтобы понять, к чему это приведет, обратимся к самому первому примеру записи в document.xml текста "Hello world!" (см. выше). Допустим, якорем будет подстрока "Hello". Тогда закладка вставленная между тегами "<w:r>" и "</w:r>" будет включать в себя весь текст "Hello world!". То же самое произойдет, если якорем будет подстрока "world!". Если же вставить закладки сразу для двух этих якорей, мы получим две различные закладки, указывающие на одну и ту же область текста.

 

Использование DOM

От экзотики перейдем к нормальным технологичным способам. Любой файл XML представляет собой иерархическую структуру. Если развернуть эту структуру в памяти, то манипулирование данными сведется к простым и интуитивно понятным операциям, достаточно знать, что и по каким правилам эти данные описывают. Подходы к обработке файлов docx, основанные на разворачивании его составных частей в память в виде структур данных, их программном преобразовании и сохранении результирующих (измененных) структур данных в новый файл видятся наиболее перспективными.

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

DOM реализована в платформе 1С Предприятие. Казалось бы, отлично, живи и радуйся. Но есть нюанс. Ошибка в реализации DOM в платформе 1С не позволяет использовать этот механизм для работы с document.xml. С какими-то другими данными XML работать можно, но document.xml в некоторых случаях будет прочитан в память некорректно.

Объясню подробнее. Для этого вернемся к тому, как текст "Hello world!" может быть записан в файле document.xml. В примерах выше мы видели, что любой текст может быть произвольным образом разбит на элементы <w:r>, <w:t>. Приведу еще один пример такого разбиения.

 

Пример очень похож на предыдущие, единственное отличие, текстовое содержимое первого элемента <w:t> заканчивается символом пробела. Во всех случаях, когда текстовое содержимое элемента <w:t> начинается или заканчивается на пробел MS Word добавляет в элемент <w:t> атрибут xml:space со значением preserve, который указывает программам обрабатывающим файл, что начальные и конечные пробелы имеют значение и их надлежит сохранить.

Вот еще один способ записи того же файла:

 

По сути, то же самое. Однако, именно об этот файл и споткнется DOM в том виде, как он реализован в платформе. DOM при чтении любых элементов, содержащих только пробелы, воспринимает их как пустую строку. При сохранении же данных из памяти обратно в файл, DOM сохранит такие элементы именно как пустую строку, т.е. без текстового содержимого. Таким образом, элементы без текстового содержимого, элементы содержащие один пробел и элементы содержащие несколько пробелов, после чтения DOM становятся неотличимы друг от друга - информация теряется и способа восстановить её не существует.

При работе с файлами docx через DOM проблема будет проявляться в спонтанном исчезновении пробелов в произвольных местах документа. Т.е. пользователь отдает шаблон в котором все нормально, а после его заполнения где-то может исчезнуть пробел. Такое даже не сразу и заметишь. Неприятно. И фактически ставит жирный крест на DOM как способе работы с файлами Word.

Приведу код, который демонстрирует наличие описанного эффекта. Проблемный файл открыли в DOM и тут же сохранили - пробел исчезает.

 

Упоминание проблемы встречается в Интернете  [8; 9], а вот решений нигде нет. Проблема не решается никакими настройками ЧтенияXML или ПостроителяDOM. Однако, это не мешает нам рассмотреть описанные решения для файлов docx на основе DOM.

Обычный способ работы с DOM изложен в статье [10]. Порядок действий таков:

- разворачиваем файл docx как zip-архив

- читаем document.xml через ПостроительDOM

- ищем элементы <w:t>, содержащие якорь. В DOM встроен специальный механизм XPath, позволяющий выбрать элементы, соответствующие условию одной командой, без рекурсивного обхода элементов XML. Именно этот механизм и использует автор статьи.

- заменяем в найденном элементе якорь на требуемое значение

- сохраняем результат обратно в document.xml

- архивируем файлы с измененным document.xml в новый файл docx

Т.е. то же, что и с текстовым поиском якорей, только код выглядит более читаемым. Однако, ни XPath, ни рекурсивный обход элементов DOM не дают готового способа искать якорь разорванный на несколько элементов <w:t>. Т.е. в основе данного решения лежит все тот же наивный поиск, со всеми вытекающими последствиями.

В работе [11] мы видим, по большому счету, то же самое. Но есть две особенности.

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

Заливка текста цветом в document.xml записывается с помощью элементов <w:rPr>, находящихся в соответствующих элементах <w:r>:

 

В статье мы можем видеть как автор непринужденно, единственным регулярным выражением запроса XPath, решает вопрос поиска элементов <w:t>, на которые распространяется заливка соответствующим цветом. Однако, заливка никак не влияет на произвольное разбиение текста на элементы <w:t>, и мы имеем все тот же наивный поиск, что и в других работах.

Через некоторое время автор статьи столкнулся с проблемой исчезающих пробелов в DOM, в комментарии он сообщает, что в новой версии модуля проблема решена добавлением функции ЗаполнитьПробелы(). Если мы посмотрим на код этой функции, то увидим, что она выполняет очень простую работу - для всех элементов <w:t> с пустым содержимым устанавливает содержимое равное строке из одного пробела. Будем справедливы, на самом деле, функция не решает проблему, а лишь уменьшает вероятность её проявления. Время от времени несколько подряд идущих пробелов, попавших в один элемент <w:t> будут превращаться в единственный пробел, приводя пользователя в недоумение. Повторю сказанное выше, информация о том, сколько пробелов содержал элемент <w:t> была утеряна в момент чтения файла в DOM, способов корректно восстановить её не существует.

 

Использование именованных полей (свойств документа)

Замечу, что среди рассмотренных статей пока не нашлось ни одного работающего решения - ни один из авторов не смог преодолеть особенности сохранения документа Ворд в файл. Реализованный наивный поиск назвать работающим решением нельзя. В этой связи особого внимания заслуживает комментарий [12], который дает нам принципиально новый подход к заполнению шаблонов Ворд. Проблему наивного поиска, оказывается можно не решать, а обойти!

В основе идеи лежит использование свойств документа для заполнения шаблона. Что такое свойства документа и как с ними работать я описывал в [4]. Как верно указано в комментарии [12], информация о свойствах документа не хранится в document.xml, а вынесена в отдельный файл docProps\custom.xml. Точнее, следуя стандарту, нужно прочитать из файла [Content_Types].xml, элемент "Override" с атрибутом "ContentType" равным "application/vnd.openxmlformats-officedocument.custom-properties+xml" и из атрибута "PartName" этого элемента прочитать имя файла со свойствами.

Файл custom.xml очень простой, вот его пример:

 

Причем, если примеры document.xml я искусственно упрощал, то это полноценный файл без изменений, именно в таком виде его и записывает Ворд. Найти нужный элемент с атрибутом name и поместить в его атрибут требуемое значение - тривиальная задача. Решать её можно через DOM, проблеме исчезающих пробелов взяться не от куда.

 

Но это только часть решения задачи. Если мы откроем файл с измененным custom.xml, то увидим, что ничего не изменилось. Для того, чтобы изменения отобразились в тексте документа пользователь должен обновить поля. Для этого можно нажать <Ctrl-A> и <F9>. Но если пользователь забудет обновить поля, последствия могут быть неприятными. Для обновления полей есть более надежный способ. В файле /word/settings.xml есть элемент <w:settings>, если для него добавить дочерний элемент <w:updateFields w:val="true"/>, то при первом открытии файла пользователь увидит запрос

 

 

Для обновления полей достаточно нажать "Да". Забыть об обновлении полей в этом случае невозможно.

Элемент <w:updateFields> можно добавить в шаблон вручную. Но это не наш метод, достаточно добавить в предыдущий пример следующий код и задачу заполнения шаблона можно считать решенной:

 

 

Подход из Документооборота

Сразу оговорюсь, Документооборота у меня нет, где-то его искать и ставить ради посмотреть, как там с docx организована работа, я не стал. Однако, авторы нескольких статей заявляют, что подсмотрели идеи своих решений именно в Документообороте. И у нас нет оснований им не доверять.

В качестве первого примера подхода на основе Документооборота рассмотрим работу [13]. Код в ней закрыт за SM, поэтому не буду его перепубликовывать здесь, только в общих чертах опишу основной механизм работы. Разбор document.xml построен на конечном автомате: инициализируются объекты ЧтениеXML и ЗаписьXML. ЧтениеXML читает очередной узел исходного файла, в зависимости от текущего состояния автомата и прочитанного узла, автомат переходит в новое состояние и может происходить одно из действий:

- прочитанный узел без изменений передается в ЗаписьXML

- прочитанный узел игнорируется

- прочитанный узел модифицируется и передается в ЗаписьXML

- в ЗаписьXML передается один или несколько новых узлов (т.е. узлов, которые не были прочитаны из ЧтениеXML)

Таким образом, на вход конечного автомата подается исходный файл XML, а на выходе получаем измененный файл XML. После завершения работы автомата исходный файл удаляется, а конечный копируется на его место.

Для хранения состояния автомата автор работы использует несколько логических переменных.

Модификация шаблона основана на поиске закладок (о том, как закладки записываются в document.xml, описывалось выше) и замены их текста.

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

Но автор все-таки ловит ошибку, похожую на наивный поиск. Как я указывал выше, внутри закладки, т.е. между элементами <w:bookmarkStart> и <w:bookmarkEnd> может быть довольно много различных элементов. В том числе несколько элементов <w:t>. Правильно было бы заменить текст первого элемента <w:t> закладки, а все остальные элементы <w:t> удалить. Еще более правильно - удалить также элементы <w:r> и <w:p> не включающие в себя первый из элементов <w:t>. Однако, автор статьи так делать не стал. Код заменяет текст первого элемента <w:t>, а все остальные элементы входящей последовательности из ЧтениеXML без изменений транслируются в ЗаписьXML. Поэтому, как только в шаблоне появиться не идеальная закладка, которую Ворд записал несколькими элементами <w:t> и <w:r>, решение перестанет работать корректно.

В работе [14] можно увидеть схожий подход. Тоже конечный автомат и тоже отсылка к Документообороту. В этот раз задача проще, достаточно только прочитать файл, модифицировать его не требуется. Сам код написан аккуратно, радует взгляд. Идеальное сочетание для того чтобы понять идею конечного автомата. Чтобы было проще вникнуть в решение приведу пример файла comments.xml

 

Текстовое содержимое есть только у элементов <w:t>, именно они и будут записаны в выходной массив. Переключение на новый элемент массива происходит в тот момент, когда на входе появляется элемент <w:comment>. Все остальные элементы входной последовательности игнорируются, т.е. не меняют состояние автомата и не попадают на выход.

Решения на основе конечных автоматов вполне рабочие. Но годятся только для простых задач. В качестве примера приведу код таблетки аналогичной [6], т.е. объединяющий якоря, обозначенные символами "<" и ">" в один элемент <w:t>, работающей не через DOM, а через конечный автомат:

 

Рассмотрим еще одну задачу. Пусть нам нужно удалить в документе пустые строчки, но не все, а только те, которые являются пунктами какого-нибудь номерованного списка. Пустые строчки в документе Ворд это абзацы в которых нет текста, возникают они после нажатия клавиши <Enter>. Если пустой абзац является пунктом номерованного списка, то визуально он будет выглядеть не как пустая строчка, а как строчка с номером. Вот такие абзацы и требуется удалить.

Характерным признаком пустого абзаца является отсутствие среди подчиненных элементов элемента <w:r>. Характерным признаком пункта списка является наличие элемента <w:numPr>, подчиненного элементу свойств абзаца <w:pPr>.

Упрощенно пустой номерованный абзац записывается в document.xml так:

 

На этой несложной задаче мы можем видеть, подход основанный на конечных автоматах имеет ограничения. Прочитав очередной элемент <w:p> невозможно принять решение, нужно ли этот элемент и подчиненные ему элементы записывать в результирующий файл. Для того, чтобы это выяснить, требуется просмотреть все элементы подчиненные <w:p> и дойти до закрывающего тега.

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

Решение задачи с использованием автомата с памятью:

 

 

XML-инъекция

Термин xml-инъекция встречается в некоторых статьях на Инфостарте и комментариях к ним, поэтому было бы неправильным обойти его вниманием. Под xml-инъекцией понимается идея вставить в нужную точку document.xml сравнительно большой фрагмент данных, содержащий описание нескольких элементов. Вставлять фрагмент предполагается как текст на языке XML. Фрагмент готовится заранее, видимо, копированием из document.xml и хранится либо в базе данных, либо в тексте программы. Такой подход мы можем видеть в статье [15].

Сам подход я бы назвал хирургически радикальным. Разрезали-имплантировали-зашили. Недостаток метода очевиден – внедряемые фрагменты XML содержат теги форматирования, т.е. форматирование хранится в двух разных местах - как в шаблоне, так и в базе (или даже в коде). Соответственно, при изменении внешнего вида документа потребуется синхронно править и шаблон и внедряемые блоки XML. Такая правка потребует участия разработчика и традиционно считается плохой практикой.

 

Чтение XML в память

Идея прочитать весь файл document.xml в оперативную память и затем его обрабатывать как обычные данные лежит на поверхности. Разбор файла через DOM на этом и основан. Однако, даже с учетом того, что DOM не работает корректно в 1С, я нашел только одну работу, в которой этот подход используется. В работе [16] автор читает весь файл document.xml, узел за узлом, в ТаблицуЗначений. Затем все манипуляции осуществляются с этой ТаблицейЗначений. По завершении обработки вся ТаблицаЗначений выгружается в файл.

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

Обработка документа в работе [16] сводится к поиску и замене якорей. Поиск якорей не наивный, а полноценный. Реализована также возможность заполнить таблицу в документе.

 

Вызовы БСП

В мире 1С существует весьма мощный API для формирования файлов Ворд на основании пользовательских шаблонов в формате docx. Этот API часть библиотеки стандартных подсистем, а точнее её подсистемы Печать. Сам API состоит всего из десяти подпрограмм, описание которых можно прочитать в официальной документации [17].

Лучше всего описано как пользоваться этим API в статье [18], я лишь повторю основные тезисы.

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

{v8 Область.ИмяОбласти}  {/v8 Область.ИмяОбласти} - всегда парные, каждый из них должен начинаться с новой строки.

{v8 ИмяПараметра} - может располагаться в любом месте, будет заменен на значение соответствующего параметра.

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

Затем вызовами подпрограмм ПрисоединитьОбласть и ПрисоединитьОбластьИЗаполнитьПараметры итоговый документ наполняется содержанием. Вызов ПрисоединитьОбласть можно использовать для областей не содержащих ни одного параметра, например, заголовков таблиц.

Когда итоговый документ наполнен всеми областями, его фиксируют вызовом подпрограммы  СформироватьДокумент.

 

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

 

Источники

1. @eduard93 Как я разбирал docx с помощью XSLT. — URL: https://habr.com/ru/companies/intersystems/articles/321044/.
2. Воутер Ван Вугт OpenXML. Кратко и доступно.
3. ECMA-376 Office Open XML file formats. — URL: https://ecma-international.org/publications-and-standards/standards/ecma-376/.
4. Васильев А. Работа с Ворд через СОМ-Объект. — URL: //infostart.ru/1c/articles/1382473/.
5. (wbazil) Договора из Шаблона MS Word 2007+ без OLE. — URL: //infostart.ru/1c/tools/237032/.
6. WWWolfy комментарий к статье 237032 - удаление разрывов в якорях (через DOM). — URL: https://forum.infostart.ru/forum9/topic98811/#message2426182.
7. Епанчинцев Д. Формирование документа MS Word с использованием БСП и программное добавление закладок. — URL: //infostart.ru/1c/articles/1915944/.
8. сообщение на форуме infostart.ru о пропадающих пробелах в DOM. — URL: https://forum.infostart.ru/forum9/topic301704/.
9. сообщение на форуме mista.ru о пропадающих пробелах в DOM. — URL: https://portal.mista.ru/topic/888632.
10. (nbeliaev) Заполнение документа Word без ComОбъект. — URL: //infostart.ru/1c/articles/590918/.
11. УШЕЛ ИЗ 1С Подключаемые печатные формы с .DOCX макетом без Word’а. — URL: //infostart.ru/1c/tools/873883/.
12. AlX0id комментарий о редактировании файла Ворд через пользовательские поля. — URL: https://forum.infostart.ru/forum9/topic167301/#message2011488.
13. Тамашев А. Заполнение шаблона Word (docx) на сервере без сторонних ПО. — URL: //infostart.ru/1c/tools/1744686/.
14. Игнатьев В. Чтение примечаний из файла с расширением «docx» без применения COM-объекта. — URL: //infostart.ru/1c/articles/2051512/.
15. Кунин А. Документооборот 8: Расширяем возможности автозаполнения шаблонов документов в 1С или XML-инъекция. — URL: //infostart.ru/1c/articles/259510/.
16. Эртель М. Заполнение шаблона Word на сервере, без использования MS Office (docx -> zip -> xml). Предусмотрен вывод табличных частей. — URL: //infostart.ru/1c/tools/675307/.
17. Документация БСП: РаботаСМакетамиОфисныхДокументов. — URL: https://its.1c.ru/db/bsp3110doc#content:1644:hdoc.
18. (PROSTO-1C) Печать в docx методами БСП без COM объекта. — URL: //infostart.ru/1c/articles/2155772/.

 

В приложенной обработке реализованы те же самые примеры, что и в статье. Также в обработке сохранены файлы docx на которых можно проверить указанные примеры. Пример c БСП выполнится только если открыть обработку в конфигурации с внедренной БСП. Подпрограмма, обращающаяся к БСП, закомментирована, иначе обработка не будет открываться в других конфигурациях, для выполнения примера её необходимо раскомментировать.

 

 

Проверено на следующих конфигурациях и релизах:

  • Управление торговлей, редакция 11, релизы 11.5.11.70

Ворд Word docx xml

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

15500 руб.

02.09.2020    184747    1029    403    

968

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Системный администратор Программист Руководитель проекта Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях (УТ, БП, ERP, УНФ, КА и т.д.). Поддерживаются серверные и файловые базы, управляемые и обычные формы. Может выполнять свертку одновременно в несколько потоков. А так же автоматически, без непосредственного участия пользователя. Решение в Реестре отечественного ПО

8400 руб.

20.08.2024    24745    163    86    

161

Пакетная печать Печатные формы Инструментарий разработчика Программист Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

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

22200 руб.

06.10.2023    20433    52    19    

86

Инструменты администратора БД Инструментарий разработчика Роли и права Программист Платформа 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

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

15000 руб.

10.11.2023    13737    57    33    

77

Инструментарий разработчика Программист Платформа 1С v8.3 Платные (руб)

Инструмент для написания и отладки кода в режиме «1С:Предприятие». Представляет собой консоль кода с возможностью пошаговой отладки, просмотра значений переменных любых типов, использования процедур и функций, просмотра стека вызовов, вычисления произвольных выражений на встроенном языке в контексте точки останова, синтаксического контроля и остановки по ошибке. В консоли используется удобный редактор кода с подсветкой, контекстной подсказкой, возможностью вызова конструкторов запроса и форматной строки.

9360 руб.

17.05.2024    30648    105    48    

148

Инструментарий разработчика Программист 8.3.14 Россия Платные (руб)

Расширение для конфигурации “Конвертация данных 3”. Добавляет подсветку синтаксиса, детальную контекстную подсказку, глобальный поиск по коду.

20000 руб.

07.10.2021    19147    7    32    

43

Инструментарий разработчика Программист Платформа 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

Восстановление партий или взаиморасчетов, расчет зарплаты, пакетное формирование документов или отчетов - теперь все это стало доступнее. * Есть желание повысить скорость работы медленных алгоритмов! Но... * Нет времени думать о реализации многопоточности? * о запуске и остановке потоков? * о поддержании потоков в рабочем состоянии? * о передаче данных в потоки и как получить ответ из потока? * об организации последовательности? Тогда ЭТО - то что надо!!!

5000 руб.

07.02.2018    104944    246    100    

311
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. ixijixi 2005 18.04.25 15:55 Сейчас в теме
Спасибо за разбор. Просто отлично!
2. sergey82vladik 6 18.04.25 16:04 Сейчас в теме
Как говорится, мое почтение! Как- то занимался подробной проблематикой, испробовал почти все способы , описанные в статья. В итоге остановился на DOM
Оставьте свое сообщение