Лишний пустой абзац в начале ODT (из шаблона) #42

Closed
opened 2026-06-30 08:03:37 +07:00 by artem.kokos · 0 comments
Owner

Симптом

При открытии сгенерированного ODT-файла в LibreOffice перед заголовком появляется пустая строка.

Причина

Шаблон redmine_reporter/templates/template.odt в своём content.xml содержит пустой абзац как первый элемент после структурных. Это артефакт редактирования шаблона в LibreOffice — когда-то первой строкой документа была пустая.

При загрузке шаблона в odt.py:

# odt.py строка 32
with resources.files(...).joinpath("templates/template.odt").open("rb") as f:
    doc = load(f)  # ← пустой <text:p/> из шаблона попадает в doc.text

Весь контент добавляется после элементов шаблона:

# odt.py строки 42-43 — добавляются в хвост doc.text, а не заменяют его
doc.text.addElement(P(stylename=para_style_name, text=header_text))
doc.text.addElement(P(stylename=para_style_name, text=""))

Вот что в итоге в content.xml:

<office:text>
  <forms/>                    <!-- из шаблона, структурный -->
  <sequence-decls>...</>      <!-- из шаблона, структурный -->
  <text:p/>                   <!-- ← ← ← ПУСТОЙ АБЗАЦ ИЗ ШАБЛОНА, видимый баг -->
  <text:p>Заголовок отчёта</text:p>   <!-- добавлен odt.py -->
  <text:p/>                   <!-- намеренный разделитель -->
  <table:table>...</table:table>
  ...
</office:text>

Варианты исправления

Вариант A: почистить шаблон

Открыть template.odt в LibreOffice, удалить пустую первую строку, пересохранить. Минус: проблема может вернуться при любом редактировании шаблона.

Вариант B: чистить doc.text в коде (рекомендуется)

После load(f) удалить все text:p из doc.text, оставив только структурные элементы (forms, sequence-decls). Тогда содержимое шаблона не будет влиять на вывод.

Примерный подход:

# Удаляем все <text:p> из шаблона, оставляем только структурные элементы
for child in list(doc.text.childNodes):
    if child.tagName == 'text:p':
        doc.text.removeChild(child)

Этот вариант предпочтительнее, потому что делает код устойчивым к случайным артефактам шаблона в будущем.

Связанные задачи

Не зависит от других задач. Можно чинить независимо.

Критерии приёмки

  • Сгенерированный ODT не содержит пустого абзаца перед заголовком.
  • Код не зависит от содержимого template.odt в части текстовых параграфов.
  • Добавлен тест: первый text:p в выводе — это заголовок, а не пустая строка.
## Симптом При открытии сгенерированного ODT-файла в LibreOffice перед заголовком появляется пустая строка. ## Причина Шаблон `redmine_reporter/templates/template.odt` в своём `content.xml` содержит пустой абзац как первый элемент после структурных. Это артефакт редактирования шаблона в LibreOffice — когда-то первой строкой документа была пустая. При загрузке шаблона в `odt.py`: ```python # odt.py строка 32 with resources.files(...).joinpath("templates/template.odt").open("rb") as f: doc = load(f) # ← пустой <text:p/> из шаблона попадает в doc.text ``` Весь контент добавляется **после** элементов шаблона: ```python # odt.py строки 42-43 — добавляются в хвост doc.text, а не заменяют его doc.text.addElement(P(stylename=para_style_name, text=header_text)) doc.text.addElement(P(stylename=para_style_name, text="")) ``` Вот что в итоге в `content.xml`: ```xml <office:text> <forms/> <!-- из шаблона, структурный --> <sequence-decls>...</> <!-- из шаблона, структурный --> <text:p/> <!-- ← ← ← ПУСТОЙ АБЗАЦ ИЗ ШАБЛОНА, видимый баг --> <text:p>Заголовок отчёта</text:p> <!-- добавлен odt.py --> <text:p/> <!-- намеренный разделитель --> <table:table>...</table:table> ... </office:text> ``` ## Варианты исправления ### Вариант A: почистить шаблон Открыть `template.odt` в LibreOffice, удалить пустую первую строку, пересохранить. Минус: проблема может вернуться при любом редактировании шаблона. ### Вариант B: чистить `doc.text` в коде (рекомендуется) После `load(f)` удалить все `text:p` из `doc.text`, оставив только структурные элементы (`forms`, `sequence-decls`). Тогда содержимое шаблона не будет влиять на вывод. Примерный подход: ```python # Удаляем все <text:p> из шаблона, оставляем только структурные элементы for child in list(doc.text.childNodes): if child.tagName == 'text:p': doc.text.removeChild(child) ``` Этот вариант предпочтительнее, потому что делает код устойчивым к случайным артефактам шаблона в будущем. ## Связанные задачи Не зависит от других задач. Можно чинить независимо. ## Критерии приёмки - [ ] Сгенерированный ODT не содержит пустого абзаца перед заголовком. - [ ] Код не зависит от содержимого `template.odt` в части текстовых параграфов. - [ ] Добавлен тест: первый `text:p` в выводе — это заголовок, а не пустая строка.
artem.kokos changed title from Лишняя строчка в ODT to Лишний пустой абзац в начале ODT (из шаблона) 2026-06-30 09:41:13 +07:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: artem.kokos/redmine-reporter#42