diff --git a/dokogen/generator.py b/dokogen/generator.py index f37ef42..4e0a314 100644 --- a/dokogen/generator.py +++ b/dokogen/generator.py @@ -30,6 +30,17 @@ VAR_PATTERN = re.compile(r'\{\{[^}]+\}\}') OLD_VAR_PATTERN = re.compile(r'<[A-Za-z0-9_]+>') UNMATCHED_PATTERN = re.compile(r'\{\{[^}]+\}\}|<[A-Za-z0-9_]+>') +# «Переменные», которые на самом деле не переменные, а клавиши в тексте +# инструкций (Ctrl+Alt+Del и т.п.) — их не считаем незаменёнными +IGNORED_OLD_VARS = { + '', '', '', '', '', '', '', + '', '', '', '', '', '', '', + '', '', '', '', '', + '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', + '', '', '', +} + LOOP_START = re.compile(r'(?:<!--|)') LOOP_END = re.compile(r'(?:<!--|)') @@ -61,6 +72,14 @@ OLD_VAR_MAP = { '': '{{document_number}}', '': '{{document_date}}', '': '{{date}}', + # Краткое наименование по падежам (старые шаблоны) + '': '{{company_short_name}}', + '': '{{company_short_name_genitive}}', + '': '{{company_short_name_dative}}', + '': '{{company_short_name_accs}}', + '': '{{company_short_name_ablt}}', + '': '{{company_short_name_loct}}', + '': '{{is_name_1}}', } @@ -342,42 +361,64 @@ def _strip_prooferr(doc): def _highlight_unmatched(doc): - """Подсвечивает незаменённые переменные красным.""" + """Выделяет незаменённые переменные красным маркером (только саму переменную).""" ns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' - def _highlight_run(run): - rpr = run._element.find(f'{{{ns}}}rPr') + def _apply_highlight(run_el): + rpr = run_el.find(f'{{{ns}}}rPr') if rpr is None: rpr = OxmlElement('w:rPr') - run._element.insert(0, rpr) - color = OxmlElement('w:color') - color.set(f'{{{ns}}}val', 'FF0000') - rpr.append(color) + run_el.insert(0, rpr) + hl = OxmlElement('w:highlight') + hl.set(f'{{{ns}}}val', 'red') + rpr.append(hl) + + def _process_para(para): + full = para.text + matches = [m for m in UNMATCHED_PATTERN.finditer(full) + if m.group(0) not in IGNORED_OLD_VARS] + if not matches: + return + + # Пересобираем параграф: обычный текст и переменные отдельными run'ами, + # переменные — с красным маркером. Сохраняем стиль первого run. + first = para.runs[0] if para.runs else None + para.clear() + pos = 0 + for m in matches: + if m.start() > pos: + run = para.add_run(full[pos:m.start()]) + if first is not None: + _copy_run_style(first, run) + run = para.add_run(m.group(0)) + if first is not None: + _copy_run_style(first, run) + _apply_highlight(run._element) + pos = m.end() + if pos < len(full): + run = para.add_run(full[pos:]) + if first is not None: + _copy_run_style(first, run) for para in doc.paragraphs: - for run in para.runs: - if UNMATCHED_PATTERN.search(run.text or ''): - _highlight_run(run) + _process_para(para) for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: - for run in para.runs: - if UNMATCHED_PATTERN.search(run.text or ''): - _highlight_run(run) + _process_para(para) for section in doc.sections: - for hf in [section.header, section.footer]: - if hf: + for hf in [section.header, section.footer, + section.first_page_header, section.first_page_footer]: + if hf is not None: for para in hf.paragraphs: - for run in para.runs: - if UNMATCHED_PATTERN.search(run.text or ''): - _highlight_run(run) + _process_para(para) def _log_unmatched(doc, replacements=None, log_func=None): - """Логирует незаменённые переменные.""" + """Логирует незаменённые переменные (без клавиш-исключений).""" if not log_func: return unmatched = [] @@ -385,6 +426,8 @@ def _log_unmatched(doc, replacements=None, log_func=None): for para in doc.paragraphs: for m in UNMATCHED_PATTERN.finditer(para.text): var = m.group(0) + if var in IGNORED_OLD_VARS: + continue if var not in seen: seen.add(var) unmatched.append(var)