diff --git a/deploy/drhider/drhider.py b/deploy/drhider/drhider.py index 0582592..abcbe06 100644 --- a/deploy/drhider/drhider.py +++ b/deploy/drhider/drhider.py @@ -207,6 +207,8 @@ class TwoPassObfuscator: """ # --- Распаковать ZIP-файлы --- files = self._expand_zips(files) + # --- Конвертировать PDF → DOCX --- + files = self._convert_pdfs_to_docx(files) try: # --- Проход 1: сбор сущностей --- @@ -236,10 +238,6 @@ class TwoPassObfuscator: pass elif fname in all_docx: obf_content = self._replace_in_docx(all_docx[fname]) - elif fname.endswith('.pdf'): - txt = all_texts.get(fname, '') - obf_content = self._replace_in_text(txt, fname) - fname = fname[:-4] + '.txt' else: txt = all_texts.get(fname, '') obf_content = self._replace_in_text(txt, fname) @@ -253,6 +251,46 @@ class TwoPassObfuscator: self._regex_replacements.clear() self._sorted_keys.clear() + def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]: + """Конвертировать PDF в DOCX через LibreOffice. При совпадении имён — _из_pdf.""" + import subprocess, tempfile + result = [] + existing_names = {f[0] for f in files} + for fname, content, ctype in files: + if not fname.lower().endswith('.pdf'): + result.append((fname, content, ctype)) + continue + try: + with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp: + tmp.write(content) + pdf_path = tmp.name + outdir = tempfile.mkdtemp() + subprocess.run(['libreoffice', '--headless', '--convert-to', 'docx', + '--outdir', outdir, pdf_path], timeout=30, capture_output=True) + docx_files = [f for f in os.listdir(outdir) if f.endswith('.docx')] + if docx_files: + with open(os.path.join(outdir, docx_files[0]), 'rb') as f: + docx_content = f.read() + new_name = fname[:-4] + '.docx' + if new_name in existing_names: + new_name = fname[:-4] + '_из_pdf.docx' + existing_names.add(new_name) + result.append((new_name, docx_content, ctype)) + else: + log.warning("PDF→DOCX failed for %s", fname) + result.append((fname, content, ctype)) + except Exception as e: + log.warning("PDF→DOCX error for %s: %s", fname, e) + result.append((fname, content, ctype)) + finally: + if os.path.exists(pdf_path): + os.unlink(pdf_path) + if os.path.exists(outdir): + for f in os.listdir(outdir): + os.unlink(os.path.join(outdir, f)) + os.rmdir(outdir) + return result + def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]: """Распаковать ZIP-файлы, заменив их содержимым. Остальные файлы — как есть. Защита от ZIP-бомб: ratio + накопительный размер (как в compare/unzip.py).""" diff --git a/site/services/drhider.py b/site/services/drhider.py index 898f7ef..ebfea7a 100644 --- a/site/services/drhider.py +++ b/site/services/drhider.py @@ -207,6 +207,8 @@ class TwoPassObfuscator: """ # --- Распаковать ZIP-файлы --- files = self._expand_zips(files) + # --- Конвертировать PDF → DOCX --- + files = self._convert_pdfs_to_docx(files) try: # --- Проход 1: сбор сущностей --- @@ -236,10 +238,6 @@ class TwoPassObfuscator: pass elif fname in all_docx: obf_content = self._replace_in_docx(all_docx[fname]) - elif fname.endswith('.pdf'): - txt = all_texts.get(fname, '') - obf_content = self._replace_in_text(txt, fname) - fname = fname[:-4] + '.txt' else: txt = all_texts.get(fname, '') obf_content = self._replace_in_text(txt, fname) @@ -253,6 +251,46 @@ class TwoPassObfuscator: self._regex_replacements.clear() self._sorted_keys.clear() + def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]: + """Конвертировать PDF в DOCX через LibreOffice. При совпадении имён — _из_pdf.""" + import subprocess, tempfile + result = [] + existing_names = {f[0] for f in files} + for fname, content, ctype in files: + if not fname.lower().endswith('.pdf'): + result.append((fname, content, ctype)) + continue + try: + with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp: + tmp.write(content) + pdf_path = tmp.name + outdir = tempfile.mkdtemp() + subprocess.run(['libreoffice', '--headless', '--convert-to', 'docx', + '--outdir', outdir, pdf_path], timeout=30, capture_output=True) + docx_files = [f for f in os.listdir(outdir) if f.endswith('.docx')] + if docx_files: + with open(os.path.join(outdir, docx_files[0]), 'rb') as f: + docx_content = f.read() + new_name = fname[:-4] + '.docx' + if new_name in existing_names: + new_name = fname[:-4] + '_из_pdf.docx' + existing_names.add(new_name) + result.append((new_name, docx_content, ctype)) + else: + log.warning("PDF→DOCX failed for %s", fname) + result.append((fname, content, ctype)) + except Exception as e: + log.warning("PDF→DOCX error for %s: %s", fname, e) + result.append((fname, content, ctype)) + finally: + if os.path.exists(pdf_path): + os.unlink(pdf_path) + if os.path.exists(outdir): + for f in os.listdir(outdir): + os.unlink(os.path.join(outdir, f)) + os.rmdir(outdir) + return result + def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]: """Распаковать ZIP-файлы, заменив их содержимым. Остальные файлы — как есть.""" result = [] diff --git a/site/templates/drhider.html b/site/templates/drhider.html index d788ec3..a99ce36 100644 --- a/site/templates/drhider.html +++ b/site/templates/drhider.html @@ -84,7 +84,7 @@ Сверка договоров | DrHider - v1.8 + v1.9 @@ -123,7 +123,7 @@
.txt. Форматирование, таблицы и графика не сохраняются._из_pdf.