При работе с файлами формата docx на низком уровне, например, для анализа или программной модификации, часто возникает задача — определить, где в документе заканчивается одна страница и начинается другая. Распаковав docx-архив и открыв основной файл document.xml, мы видим всю структуру текста, абзацев и форматирования. Но как среди этих тегов найти четкие границы страниц? Разберемся в этом вопросе подробно.
Основная сложность заключается в том, что разбиение на страницы в формате docx — это динамический процесс. В отличие от форматов с жесткой версткой, таких как PDF, в docx нет строго зафиксированных границ страниц. Финальное отображение документа и его разбивка на страницы происходят в момент открытия файла в текстовом процессоре (например, Microsoft Word) и зависят от множества факторов:
Таким образом, попытка найти "мягкие" разрывы страниц (те, которые получаются автоматически при заполнении страницы текстом) в XML-файле — это почти невыполнимая задача. Для этого потребовалось бы создать полноценный движок рендеринга, который бы в точности повторял логику MS Word. Однако в XML-структуре можно найти следы принудительных, или явных, разрывов, которые вставил сам пользователь.
Самый надежный способ найти начало новой страницы — это обнаружить тег, который соответствует действию пользователя "Вставка -> Разрыв страницы" (или нажатию 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>
Обнаружение такого тега дает стопроцентную уверенность, что в этом месте начинается новая страница, независимо от настроек рендеринга.
Еще один способ принудительно начать абзац с новой страницы — это задать ему соответствующее свойство в настройках форматирования. В 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>
Этот метод, как и предыдущий, является надежным индикатором начала новой страницы, так как он задан пользователем явно.
Новая страница также может начинаться из-за вставки разрыва раздела (например, "со следующей страницы", "с четной/нечетной страницы"). Информация о свойствах раздела хранится в теге <w:sectPr>. Если в свойствах раздела указан тип разрыва, который предполагает начало новой страницы, это также будет точкой отсчета.
В некоторых случаях текстовый процессор может оставлять в документе "подсказки" о том, где проходила граница страницы во время последнего сохранения. За это отвечает тег <w:lastRenderedPageBreak/>.
Пример его использования:
<w:r>
<w:t>...конец текста на странице.</w:t>
<w:lastRenderedPageBreak/>
<w:t>Начало текста на новой странице.</w:t>
</w:r>
Важно понимать, что на этот тег полагаться нельзя. Он имеет ряд существенных недостатков:
<w:lastRenderedPageBreak/> будут указывать на старые, неактуальные границы.Использовать этот тег можно лишь для приблизительной оценки, но не для точного анализа.
Подведем итог. Программно определить границы всех страниц в документе docx путем анализа только document.xml невозможно из-за динамической природы верстки.
Что можно сделать:
<w:br w:type="page"/> и <w:pageBreakBefore/>.Если ваша конечная цель — вставить некий объект или информацию в начало/конец каждой страницы (например, номер страницы, логотип, служебную информацию), то анализировать XML для поиска границ — неверный подход. Для этого в формате docx существуют специальные механизмы:
header.xml, footer.xml).