/
doubley
/
Anton
Обзор
Документация
Войти
/
doubley
/
Anton
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
func.py
377 строк
20 KB
square
F
12 ноя 2025, 16:10
12 ноя 2025, 16:10
dfebf76
Код
Авторство
О чём код?
import os import re from docx import Document from docx.shared import Pt, Cm from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml.ns import qn from docx.oxml import OxmlElement import traceback def format_document(input_file, output_file=None): """ Основная функция для форматирования документа по ГОСТ Args: input_file (str): Путь к входному файлу .docx output_file (str, optional): Путь к выходному файлу. Если None, создается автоматически Returns: bool: True если успешно, False если ошибка """ # Автоматическое создание имени для выходного файла if not output_file: base, ext = os.path.splitext(input_file) output_file = f"{base}_formatted{ext}" # Проверка существования файла if not os.path.exists(input_file): print(f"❌ Ошибка: Файл '{input_file}' не найден!") print("📋 Файлы в текущей директории:") for f in os.listdir('.'): if f.endswith('.docx'): print(f" - {f}") return False try: # 1. ЗАГРУЗКА ДОКУМЕНТА print("🔄 Файл принят, идёт загрузка...") doc = Document(input_file) print("✅ Файл успешно загружен!") # 2. ПРИМЕНЕНИЕ ПАРАМЕТРОВ СТРАНИЦЫ print("📄 Применение параметров страницы...") section = doc.sections[0] section.left_margin = Cm(3) # 30 мм section.right_margin = Cm(1) # 10 мм section.top_margin = Cm(2) # 20 мм section.bottom_margin = Cm(2) # 20 мм # 3. НАСТРОЙКА НУМЕРАЦИИ СТРАНИЦ print("🔢 Настройка нумерации страниц...") for section in doc.sections: footer = section.footer paragraph = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph() paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER run = paragraph.add_run() fld_char = OxmlElement('w:fldChar') fld_char.set(qn('w:fldCharType'), 'begin') instr_text = OxmlElement('w:instrText') instr_text.set(qn('xml:space'), 'preserve') instr_text.text = 'PAGE' fld_char2 = OxmlElement('w:fldChar') fld_char2.set(qn('w:fldCharType'), 'end') run._r.append(fld_char) run._r.append(instr_text) run._r.append(fld_char2) # 4. УДАЛЕНИЕ ИНТЕРВАЛОВ ПЕРЕД И ПОСЛЕ АБЗАЦЕВ print("🗑️ Удаление интервалов перед и после абзацев...") for paragraph in doc.paragraphs: paragraph.paragraph_format.space_before = Pt(0) paragraph.paragraph_format.space_after = Pt(0) # 5. ФОРМАТИРОВАНИЕ ВСЕГО ТЕКСТА (включая списки) print("🔤 Применение настроек шрифта ко всему тексту...") for paragraph in doc.paragraphs: for run in paragraph.runs: run.font.name = 'Times New Roman' run.font.size = Pt(14) # 6. ОБРАБОТКА СПИСКОВ И УБРАТЬ БОЛЬШИЕ ОТСТУПЫ print("🔢 Обработка списков и отступов...") for paragraph in doc.paragraphs: text = paragraph.text.strip() # Определяем элементы списка is_list_item = (re.match(r'^\d+\.\s', text) or # "1. текст" re.match(r'^\d+\)\s', text) or # "1) текст" re.match(r'^[а-я]\.\s', text.lower()) or # "а. текст" re.match(r'^[а-я]\)\s', text.lower()) or # "а) текст" re.match(r'^[a-z]\.\s', text.lower()) or # "a. текст" re.match(r'^[a-z]\)\s', text.lower())) # "a) текст" if is_list_item: # Убираем ВСЕ отступы для списков paragraph.paragraph_format.first_line_indent = Cm(0) paragraph.paragraph_format.left_indent = Cm(0) paragraph.paragraph_format.right_indent = Cm(0) # Выравнивание по ширине для списков paragraph.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # Убираем табуляцию после номера (заменяем на пробел) if len(paragraph.runs) > 0: first_run = paragraph.runs[0] first_run_text = first_run.text # Заменяем табуляции на пробелы в первом run if '\t' in first_run_text: first_run.text = first_run_text.replace('\t', ' ') # Убеждаемся, что после цифры стоит только один пробел cleaned_text = re.sub(r'^(\d+\.)\s+', r'\1 ', first_run.text) cleaned_text = re.sub(r'^(\d+\))\s+', r'\1 ', cleaned_text) cleaned_text = re.sub(r'^([а-я]\.)\s+', r'\1 ', cleaned_text.lower()) cleaned_text = re.sub(r'^([а-я]\))\s+', r'\1 ', cleaned_text) cleaned_text = re.sub(r'^([a-z]\.)\s+', r'\1 ', cleaned_text) cleaned_text = re.sub(r'^([a-z]\))\s+', r'\1 ', cleaned_text) first_run.text = cleaned_text # 7. ФОРМАТИРОВАНИЕ ЗАГОЛОВКОВ print("📝 Форматирование заголовков...") for paragraph in doc.paragraphs: text = paragraph.text.strip().upper() # Заголовки разделов if text in ['ВВЕДЕНИЕ', 'ЗАКЛЮЧЕНИЕ', 'СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ', 'СОДЕРЖАНИЕ', 'ОГЛАВЛЕНИЕ', 'АННОТАЦИЯ', 'РЕФЕРАТ']: for run in paragraph.runs: run.font.bold = True run.font.size = Pt(16) paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER paragraph.paragraph_format.first_line_indent = Cm(0) # Заголовки подразделов elif re.match(r'^\d+\.\d+', paragraph.text): for run in paragraph.runs: run.font.bold = True run.font.size = Pt(14) paragraph.paragraph_format.first_line_indent = Cm(0) # 8. ФОРМАТИРОВАНИЕ ОСНОВНОГО ТЕКСТА ПО ШИРИНЕ print("📏 Форматирование основного текста по ширине...") for paragraph in doc.paragraphs: text = paragraph.text.strip() # Проверяем, не является ли элемент особым случаем is_special = (paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER or re.match(r'^Таблица \d+ –', text) or re.match(r'^Рисунок \d+ –', text) or text.upper() in ['ВВЕДЕНИЕ', 'ЗАКЛЮЧЕНИЕ', 'СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ', 'СОДЕРЖАНИЕ', 'ОГЛАВЛЕНИЕ', 'АННОТАЦИЯ', 'РЕФЕРАТ'] or re.match(r'^\d+\.\d+', text)) if not is_special: paragraph.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # 9. ФОРМАТИРОВАНИЕ АБЗАЦЕВ (красная строка) print("📝 Форматирование абзацев...") for paragraph in doc.paragraphs: paragraph.paragraph_format.line_spacing = 1.5 text = paragraph.text.strip() # Определяем, нужна ли красная строка is_list_item = (re.match(r'^\d+\.\s', text) or re.match(r'^\d+\)\s', text) or re.match(r'^[а-я]\.\s', text.lower()) or re.match(r'^[а-я]\)\s', text.lower()) or re.match(r'^[a-z]\.\s', text.lower()) or re.match(r'^[a-z]\)\s', text.lower())) is_special = (text.upper() in ['ВВЕДЕНИЕ', 'ЗАКЛЮЧЕНИЕ', 'СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ', 'СОДЕРЖАНИЕ', 'ОГЛАВЛЕНИЕ', 'АННОТАЦИЯ', 'РЕФЕРАТ'] or re.match(r'^\d+\.\d+', text) or paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER) # Красная строка только для обычных абзацев if not is_list_item and not is_special and text: paragraph.paragraph_format.first_line_indent = Cm(1.25) else: paragraph.paragraph_format.first_line_indent = Cm(0) # 10. ОБРАБОТКА ТАБЛИЦ И РИСУНКОВ print("🖼️ Обработка таблиц и рисунков...") # Обработка таблиц for i, table in enumerate(doc.tables, 1): # Добавляем подпись к таблице paragraph = doc.add_paragraph() paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER run = paragraph.add_run(f'Таблица {i} – ') run.font.size = Pt(12) run.font.name = 'Times New Roman' # ОБРАБОТКА РИСУНКОВ - ПОИСК РИСУНКОВ В ДОКУМЕНТЕ print("🖼️ Поиск и подпись рисунков...") figure_count = 0 # Создаем список для хранения индексов параграфов с рисунками figure_paragraphs = [] # Проходим по всем параграфам и ищем те, что содержат рисунки for i, paragraph in enumerate(doc.paragraphs): # Проверяем, содержит ли параграф рисунки (graphic) if paragraph._element.xpath('.//w:drawing'): figure_count += 1 print(f"📸 Найден рисунок {figure_count} в параграфе {i}") figure_paragraphs.append(i) # Альтернативный метод поиска рисунков - через blip elif paragraph._element.xpath('.//a:blip'): figure_count += 1 print(f"📸 Найден рисунок {figure_count} в параграфе {i} (альтернативный метод)") figure_paragraphs.append(i) # Если не нашли рисунки через XML, используем эвристический метод if figure_count == 0: print("🔍 Использование эвристического поиска рисунков...") # Ищем параграфы, которые могут содержать рисунки for i, paragraph in enumerate(doc.paragraphs): # Эвристика: пустые или почти пустые центрированные параграфы часто содержат рисунки text_length = len(paragraph.text.strip()) has_runs = len(paragraph.runs) > 0 if (text_length <= 10 and paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER and has_runs and not any(keyword in paragraph.text.strip().lower() for keyword in ['таблица', 'table', 'рисунок', 'рис.'])): figure_count += 1 print(f"📸 Предположительно найден рисунок {figure_count} в параграфе {i} (эвристика)") figure_paragraphs.append(i) # Теперь добавляем подписи ПОСЛЕ рисунков # Проходим в обратном порядке, чтобы индексы не сбивались при вставке for figure_index, para_index in enumerate(sorted(figure_paragraphs, reverse=True), 1): try: # Получаем номер рисунка (в правильном порядке) figure_number = len(figure_paragraphs) - figure_index + 1 # Добавляем подпись ПОСЛЕ рисунка (вставляем после параграфа с рисунком) # Используем insert_paragraph_before для следующего параграфа if para_index + 1 < len(doc.paragraphs): # Если есть следующий параграф, вставляем перед ним caption_paragraph = doc.paragraphs[para_index + 1].insert_paragraph_before() else: # Если рисунок последний в документе, добавляем в конец caption_paragraph = doc.add_paragraph() caption_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER run = caption_paragraph.add_run(f'Рисунок {figure_number}') run.font.size = Pt(12) run.font.name = 'Times New Roman' run.font.bold = True # Добавляем небольшой отступ после подписи caption_paragraph.paragraph_format.space_after = Pt(12) print(f"✅ Добавлена подпись 'Рисунок {figure_number}' после параграфа {para_index}") except Exception as e: print(f"⚠️ Ошибка при добавлении подписи для рисунка в параграфе {para_index}: {e}") continue # 11. ФОРМАТИРОВАНИЕ БИБЛИОГРАФИЧЕСКОГО СПИСКА print("📚 Форматирование библиографического списка...") bibliography_started = False for paragraph in doc.paragraphs: text = paragraph.text.strip().upper() if 'СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ' in text or 'БИБЛИОГРАФИЧЕСКИЙ СПИСОК' in text: bibliography_started = True continue if bibliography_started and paragraph.text.strip(): text = paragraph.text.strip() text = re.sub(r'\s+', ' ', text) paragraph.clear() run = paragraph.add_run(text) run.font.name = 'Times New Roman' run.font.size = Pt(14) paragraph.paragraph_format.first_line_indent = Cm(-1.25) paragraph.paragraph_format.left_indent = Cm(1.25) paragraph.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # 12. ГЕНЕРАЦИЯ ОГЛАВЛЕНИЯ (ТОЛЬКО ЕСЛИ УЖЕ ЕСТЬ В ДОКУМЕНТЕ) print("📑 Проверка оглавления...") # Ищем существующее оглавление в документе toc_found = False for paragraph in doc.paragraphs: if 'СОДЕРЖАНИЕ' in paragraph.text.upper() or 'ОГЛАВЛЕНИЕ' in paragraph.text.upper(): toc_found = True print("📑 Найдено существующее оглавление - форматируем его") # Форматируем существующее оглавление for run in paragraph.runs: run.font.name = 'Times New Roman' run.font.size = Pt(16) run.font.bold = True paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER paragraph.paragraph_format.first_line_indent = Cm(0) break # НЕ добавляем автоматически оглавление, если его нет в документе if not toc_found: print("📑 Оглавление не найдено в документе - не добавляем автоматически") # 13. ФИНАЛЬНАЯ ПРОВЕРКА print("🔍 Финальная проверка...") center_count = 0 justify_count = 0 left_count = 0 list_count = 0 table_count = len(doc.tables) for paragraph in doc.paragraphs: text = paragraph.text.strip() is_list_item = (re.match(r'^\d+\.\s', text) or re.match(r'^\d+\)\s', text) or re.match(r'^[а-я]\.\s', text.lower()) or re.match(r'^[а-я]\)\s', text.lower()) or re.match(r'^[a-z]\.\s', text.lower()) or re.match(r'^[a-z]\)\s', text.lower())) if is_list_item: list_count += 1 elif paragraph.alignment == WD_ALIGN_PARAGRAPH.CENTER: center_count += 1 elif paragraph.alignment == WD_ALIGN_PARAGRAPH.JUSTIFY: justify_count += 1 else: left_count += 1 print(f"📊 Статистика форматирования:") print(f" - По центру: {center_count} параграфов") print(f" - По ширине: {justify_count} параграфов") print(f" - По левому краю: {left_count} параграфов") print(f" - Элементов списка: {list_count}") print(f" - Таблиц: {table_count}") print(f" - Найдено рисунков: {figure_count}") # 14. СОХРАНЕНИЕ РЕЗУЛЬТАТА print("💾 Сохранение документа...") doc.save(output_file) print("✅ Готово! Файл успешно отформатирован.") print(f"🎉 Документ сохранен как: {output_file}") return True except Exception as e: print(f"💥 Произошла ошибка при обработке документа: {str(e)}") print(f"🔍 Детали ошибки: {traceback.format_exc()}") return False # Пример использования функции if __name__ == "__main__": # УКАЖИТЕ ПУТЬ К ВАШЕМУ ФАЙЛУ ЗДЕСЬ input_file = "успешный успех3.docx" # ← ИЗМЕНИТЕ НА ИМЯ ВАШЕГО ФАЙЛ # Вызов основной функции success = format_document(input_file) if success: print("\n🎉 Форматирование завершено успешно!") else: print("\n💥 Форматирование завершено с ошибками!")