Python docx перенос строки

Автоматизированная сборка документа «Текст программы» по ЕСПД с помощью python-docx

Статья не имеет цели доказать важность и необходимость существования документа «Текст программы» и, тем более, необходимость его разработки в виде документа, которым кто-то когда-либо воспользуется. Цель статьи показать основы автоматизации обработки документов формата *.doc (*.docx) с использованием скриптов, написанных на языке Python.

Документ «Текст программы» (далее – Документ) входит в состав комплекта программных документов, перечень которых определен ГОСТ 19.101-77. ГОСТ, бесспорно, достаточно старый, однако его требования до сих пор востребованы при разработке программной продукции, соответственно требуется и разработка рассматриваемого Документа.

Содержание Документа определено ГОСТ 19.401-78 и должно включать в себя либо символическую запись на исходном языке, либо символическую запись на промежуточных языках, либо символическое представление машинных кодов (и т.п.).

Учитывая объемы современных веб приложений, разрабатываемых с использованием нескольких фреймворков, подготовить данный документ без использования автоматизации достаточно сложно.

Возможно два варианта создания документа:

  1. разработчики собирают содержание всех файлов проекта в один текстовый файл и передают техническому писателю на обработку;
  2. разработчики передают техническому писателю архив с файлами проекта, которые технический писатель должен обработать сам.

Первый вариант зависим от загруженности разработчиков. Чтобы сформировать текстовый файл с кодом программы, кто-то из команды разработчиков должен отвлечься, выгрузить код проекта из репозитория, написать программу, которая обработает выгруженный код и выдаст текстовый файл. Файл может быть размером как в пару мегабайт, так и в пару сотен мегабайт, его нужно вставить в Документ и как-то оформить. Вставка такого объема информации в файл Microsoft Word может занять как 30 минут, так и несколько часов. При этом, когда вставка будет завершена, Документ будет не структурирован и не читаем.

Читайте также:  Питон плетение браслет женское золото

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

Автоматизация рассматриваемого Документа заключается в том, чтобы перенести текст кода программы в документ с заданным форматированием и структурой, чтобы в дальнейшем им можно было воспользоваться (маловероятный, но имеющий право на жизнь вариант использования).

Файл формата Microsoft Word (*.doc, *.docx) представляет собой набор данных в разметке xml. Файл имеет такие атрибуты, как параграф, рисунок, таблица, стиль. Для доступа к файлу и его атрибутам Microsoft в свое время была разработана технология OLE (Object Linking and Embedding). OLE позволяет передавать часть работы от одной программы редактирования к другой и возвращать результаты назад.

Библиотеки, осуществляющие взаимодействие с файлами офисного пакета Microsoft Office с использованием технологии OLE, есть практически для всех языков программирования. Для Python это библиотека python-docx. Описание библиотеки доступно по ссылке. Установить библиотеку можно командой:

Общий алгоритм разработки Документа представляет собой последовательность шагов:

  1. Подготовка шаблона документа;
  2. Подготовка скрипта сборки на Python;
  3. Обновление содержания и числа страниц;
  4. Сохранение в PDF.

Шаг 1

Конечный Документ проще сформировать на основе заранее подготовленного шаблона (например файл с именем template.docx). Форматирование и атрибуты (наименование программы, разработчик, децимальный номер, аннотация, текст основной части) шаблона Документа должны соответствовать ГОСТ 19.104-78.

Кроме требований ГОСТ шаблон Документа должен удовлетворять следующим требованиям (последовательность шагов описана для Microsoft Word 2019):

  • иметь поле автоматического подсчета числа страниц в строке «Листов …»:

Вставка→Экспресс-блоки→Поле→NumPages→OK

  • иметь предопределенные стили для заголовков уровня 1, 2, 3, а также для кода:

Стили→Создать стиль→Дать имя→Выбрать созданный стиль правой кнопкой мыши→Формат→Абзац→Уровень

  • иметь поле автоматической сборки содержания:

Ссылки→Оглавление→Настраиваемое оглавление

  • заголовки уровня 1, 2, 3 должны переноситься в содержание (Ссылки→Оглавление→Настраиваемое оглавление→Параметры→Выбрать созданные стили и присвоить им уровень);
  • шаблон документа должен содержать фразу, на место которой будет вставлен код программы, выполненную в необходимом стиле, например «».

Шаг 2

Папка с проектом содержит значительное число файлов, часть которых не являются кодом. К ним относятся графические файлы интерфейса программы, файлы фреймворков, а также файлы программ, используемых в процессе разработки и оставляющих после себя следы (GIT, SVN, Docker). Все эти файлы необходимо отфильтровать.

Функция, которая будет отфильтровывать только файлы, удовлетворяющие условию, выглядит следующим образом:

def check(string): result = False if string[-3:] == '.js': result = True if string[-4:] == '.vue': result = True if string[-5:] == '.json': result = True if string[-4:] == '.css': result = True return result

Чтобы получить список файлов, удовлетворяющих условиям фильтра, сначала необходимо получить список всех каталогов в директории проекта:

folder = [] for i in os.walk(folder_name): folder.append(i)

Из полученного списка каталогов читаем имена всех файлов во всех директориях проекта, и те, которые удовлетворяют условиям фильтра, складываем в отдельный список:

paths = [] for address, dirs, files in folder: for file in files: if check(file): paths.append(address+'\\'+file)

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

def read_file(filename): f = codecs.open(filename, "r", "utf_8_sig" ) file = [] for line in f: file.append(line) file = ''.join(file) f.close() return file

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

for i, path in enumerate(paths): if i == 0: catalog = path[folder_name_len:].split('\')[1] total_code_file.append('Каталог '+catalog+'\n') if path[folder_name_len:].split('\')[1] != catalog: catalog = path[folder_name_len:].split('\')[1] total_code_file.append('Каталог '+catalog+'\n') total_code_file.append('Файл '+path[folder_name_len:]+'\n') total_code_file.append(read_file(path)) total_code_file += '\n'

Для переноса полученного кода программы подключаемся к документу, ищем контрольную фразу, заменяем ее на пустую строку и начинаем вставлять полученный код. Если код содержит слово «Каталог», форматируем его в стиле заголовка 2 уровня, если содержит слово «Файл» – в стиле заголовка 3 уровня, остальной текст форматируем в стиле кода программы:

doc = Document(sample_file) for p in doc.paragraphs: if '' in p.text: p.text = '' for line in total_code_file: if line.rstrip() > '' and line.split()[0] == 'Каталог': p.insert_paragraph_before(line.rstrip(), 'ЗАГ_2') elif line.rstrip() > '' and line.split()[0] == 'Файл': p.insert_paragraph_before(line.rstrip(), 'ЗАГ_3') else: p.insert_paragraph_before(line.rstrip(), 'КОД')

По завершении сохраняем документ. Программная обработка документа завершена.

Шаг 3

После вставки текста программы в Документ необходимо открыть его в Microsoft Word, выделить все (Ctrl+A) и обновить автозаполняемые поля (F9). Данную операцию необходимо выполнить дважды, так как поля обновляются последовательно, и после формирования содержания итоговое число страниц изменится.

Данная операция занимает время, так как Word выполняет расчет страниц, последовательно обрабатывая документ до конца.

Шаг 4

После завершения обновления автозаполняемых полей необходимо сохранить Документ в формате *.pdf средствами Word. Это необходимо, чтобы зафиксировать форматирование и исключить дальнейшую работу с файлом в Word, так как Word будет выполнять пересчет числа страниц при каждом открытии файла или его изменении. С *.pdf такой проблемы не будет.

*.pdf имеет больший размер, но легко открывается любой подходящей программой. Ссылки в содержании после сохранения работают.

Полный код проекта доступен по ссылке.

Описанный вариант автоматизации не обрабатывает ошибки, связанные с разными кодировками файлов, и имеет варианты развития:

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

Источник

Breaks¶

Word supports a variety of breaks that interrupt the flow of text in the document:

  • line break
  • page break
  • column break
  • section break (new page, even page, odd page)

In addition, a page break can be forced by formatting a paragraph with the “page break before” setting.

This analysis is limited to line, page, and column breaks. A section break is implemented using a completely different set of elements and is covered separately.

Candidate protocol – run.add_break()¶

The following interactive session demonstrates the protocol for adding a page break:

>>> run = p.add_run() >>> run.breaks [] >>> run.add_break() # by default adds WD_BREAK.LINE >>> run.breaks [] >>> run.breaks[0].type.__name__ WD_BREAK.LINE >>> run.add_break(WD_BREAK.LINE) >>> run.breaks [, ] >>> run.add_break(WD_BREAK.PAGE) >>> run.add_break(WD_BREAK.COLUMN) >>> run.add_break(WD_BREAK.LINE_CLEAR_LEFT) >>> run.add_break(WD_BREAK.LINE_CLEAR_RIGHT) >>> run.add_break(WD_BREAK.TEXT_WRAPPING) 

Enumeration – WD_BREAK_TYPE¶

  • WD_BREAK.LINE
  • WD_BREAK.LINE_CLEAR_LEFT
  • WD_BREAK.LINE_CLEAR_RIGHT
  • WD_BREAK.TEXT_WRAPPING (e.g. LINE_CLEAR_ALL)
  • WD_BREAK.PAGE
  • WD_BREAK.COLUMN
  • WD_BREAK.SECTION_NEXT_PAGE
  • WD_BREAK.SECTION_CONTINUOUS
  • WD_BREAK.SECTION_EVEN_PAGE
  • WD_BREAK.SECTION_ODD_PAGE

Specimen XML¶

Line break¶

This XML is produced by Word after inserting a line feed with Shift-Enter:

   Text before   and after line break   

Word loads this more straightforward generation just fine, although it changes it back on next save. I’m not sure of the advantage in creating a fresh run such that the element is the first child:

   Text before  and after line break   

Page break¶

   Before inserting a page break, the cursor was here >     This was the following paragraph, the last in the document   

… this XML is produced by Word on inserting a hard page:

   Before inserting a page break, the cursor was here >     w:type="page"/>    w:id="0" w:name="_GoBack"/>  w:id="0"/>    This was the following paragraph, the last in the document   

Word loads the following simplified form fine …

   Text before an intra-run page break  w:type="page"/> Text after an intra-run page break     following paragraph   

… although on saving it converts it to this:

   Text before an intra-run page break   w:type="page"/>   Text after an intra-run page break     following paragraph   

Schema excerpt¶

 name="CT_R">  ref="EG_RPr" minOccurs="0"/>  ref="EG_RunInnerContent" minOccurs="0" maxOccurs="unbounded"/>   name="rsidRPr" type="ST_LongHexNumber"/>  name="rsidDel" type="ST_LongHexNumber"/>  name="rsidR" type="ST_LongHexNumber"/>   name="EG_RunInnerContent">  name="br" type="CT_Br"/>  name="t" type="CT_Text"/>  name="contentPart" type="CT_Rel"/>  name="delText" type="CT_Text"/>  name="instrText" type="CT_Text"/>  name="delInstrText" type="CT_Text"/>  name="noBreakHyphen" type="CT_Empty"/>  name="softHyphen" type="CT_Empty"/>  name="dayShort" type="CT_Empty"/>  name="monthShort" type="CT_Empty"/>  name="yearShort" type="CT_Empty"/>  name="dayLong" type="CT_Empty"/>  name="monthLong" type="CT_Empty"/>  name="yearLong" type="CT_Empty"/>  name="annotationRef" type="CT_Empty"/>  name="footnoteRef" type="CT_Empty"/>  name="endnoteRef" type="CT_Empty"/>  name="separator" type="CT_Empty"/>  name="continuationSeparator" type="CT_Empty"/>  name="sym" type="CT_Sym"/>  name="pgNum" type="CT_Empty"/>  name="cr" type="CT_Empty"/>  name="tab" type="CT_Empty"/>  name="object" type="CT_Object"/>  name="pict" type="CT_Picture"/>  name="fldChar" type="CT_FldChar"/>  name="ruby" type="CT_Ruby"/>  name="footnoteReference" type="CT_FtnEdnRef"/>  name="endnoteReference" type="CT_FtnEdnRef"/>  name="commentReference" type="CT_Markup"/>  name="drawing" type="CT_Drawing"/>  name="ptab" type="CT_PTab"/>  name="lastRenderedPageBreak" type="CT_Empty"/>    name="CT_Br">  name="type" type="ST_BrType"/>  name="clear" type="ST_BrClear"/>   name="ST_BrType">  base="xsd:string">  value="page"/>  value="column"/>  value="textWrapping"/>    name="ST_BrClear">  base="xsd:string">  value="none"/>  value="left"/>  value="right"/>  value="all"/>   

Resources¶

Relevant sections in the ISO Spec¶

Источник

Оцените статью