Как определить начало страниц в DOCX-файле через его XML-структуру?

Программист IT и автоматизация бизнеса
← На главную

При работе с файлами формата docx на низком уровне, например, для анализа или программной модификации, часто возникает задача — определить, где в документе заканчивается одна страница и начинается другая. Распаковав docx-архив и открыв основной файл document.xml, мы видим всю структуру текста, абзацев и форматирования. Но как среди этих тегов найти четкие границы страниц? Разберемся в этом вопросе подробно.

Основная сложность заключается в том, что разбиение на страницы в формате docx — это динамический процесс. В отличие от форматов с жесткой версткой, таких как PDF, в docx нет строго зафиксированных границ страниц. Финальное отображение документа и его разбивка на страницы происходят в момент открытия файла в текстовом процессоре (например, Microsoft Word) и зависят от множества факторов:

Таким образом, попытка найти "мягкие" разрывы страниц (те, которые получаются автоматически при заполнении страницы текстом) в XML-файле — это почти невыполнимая задача. Для этого потребовалось бы создать полноценный движок рендеринга, который бы в точности повторял логику MS Word. Однако в XML-структуре можно найти следы принудительных, или явных, разрывов, которые вставил сам пользователь.

Способ 1: Поиск явного разрыва страницы

Самый надежный способ найти начало новой страницы — это обнаружить тег, который соответствует действию пользователя "Вставка -> Разрыв страницы" (или нажатию Ctrl+Enter). В XML-структуре это представлено тегом <w:br> с атрибутом w:type="page". Он находится внутри элемента абзаца <w:p> и элемента пробега <w:r>.

Проанализируем пример такой структуры в document.xml:


<w:p>
  <w:r>
    <w:t>Текст на первой странице.</w:t>
  </w:r>
</w:p>
<!-- Этот абзац содержит принудительный разрыв страницы -->
<w:p>
  <w:r>
    <w:br w:type="page"/>
  </w:r>
</w:p>
<w:p>
  <w:r>
    <w:t>Этот текст гарантированно начнется с новой страницы.</w:t>
  </w:r>
</w:p>

Обнаружение такого тега дает стопроцентную уверенность, что в этом месте начинается новая страница, независимо от настроек рендеринга.

Способ 2: Поиск свойства абзаца "с новой страницы"

Еще один способ принудительно начать абзац с новой страницы — это задать ему соответствующее свойство в настройках форматирования. В XML за это отвечает тег <w:pageBreakBefore/>, который размещается внутри свойств абзаца <w:pPr>.

Посмотрим на пример:


<w:p>
  <w:r>
    <w:t>Текст на предыдущей странице.</w:t>
  </w:r>
</w:p>
<!-- Следующий абзац начнется с новой страницы из-за его свойства -->
<w:p>
  <w:pPr>
    <w:pageBreakBefore/>
  </w:pPr>
  <w:r>
    <w:t>Этот абзац начинается с новой страницы.</w:t>
  </w:r>
</w:p>

Этот метод, как и предыдущий, является надежным индикатором начала новой страницы, так как он задан пользователем явно.

Способ 3: Разрывы разделов

Новая страница также может начинаться из-за вставки разрыва раздела (например, "со следующей страницы", "с четной/нечетной страницы"). Информация о свойствах раздела хранится в теге <w:sectPr>. Если в свойствах раздела указан тип разрыва, который предполагает начало новой страницы, это также будет точкой отсчета.

Ненадежный метод: тег последнего рендеринга

В некоторых случаях текстовый процессор может оставлять в документе "подсказки" о том, где проходила граница страницы во время последнего сохранения. За это отвечает тег <w:lastRenderedPageBreak/>.

Пример его использования:


<w:r>
  <w:t>...конец текста на странице.</w:t>
  <w:lastRenderedPageBreak/>
  <w:t>Начало текста на новой странице.</w:t>
</w:r>

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

  1. Может отсутствовать. Если документ был создан программно или ни разу не открывался в режиме, где происходит разбивка на страницы (paginated view), этих тегов в нем не будет.
  2. Информация устаревает. Если после последнего сохранения с рендерингом в документ были внесены изменения (например, изменили размер шрифта), но он не был заново пересчитан и сохранен, теги <w:lastRenderedPageBreak/> будут указывать на старые, неактуальные границы.
  3. Историческая ненадежность. Разные версии Word могут по-разному обрабатывать или вовсе игнорировать этот тег.

Использовать этот тег можно лишь для приблизительной оценки, но не для точного анализа.

Выводы и альтернативные подходы

Подведем итог. Программно определить границы всех страниц в документе docx путем анализа только document.xml невозможно из-за динамической природы верстки.

Что можно сделать:

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

← На главную