fix: drhider PDF→DOCX via pdfplumber (LibreOffice cant do it) v1.12
Deploy contracts-flask / validate (push) Successful in 0s

This commit is contained in:
2026-07-07 16:37:36 +04:00
parent 88b63733b1
commit f68597f680
3 changed files with 61 additions and 55 deletions
+30 -27
View File
@@ -252,8 +252,9 @@ class TwoPassObfuscator:
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
"""Конвертировать PDF в DOCX через pdfplumber. При совпадении имён — _из_pdf."""
import pdfplumber
from docx import Document as DocxDocument
result = []
existing_names = {f[0] for f in files}
for fname, content, ctype in files:
@@ -261,34 +262,36 @@ class TwoPassObfuscator:
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))
doc = DocxDocument()
with pdfplumber.open(io.BytesIO(content)) as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
if table:
rows = [[str(c or "").strip() for c in (row or [])] for row in table]
rows = [r for r in rows if any(r)]
if rows:
t = doc.add_table(rows=len(rows), cols=len(rows[0]))
t.style = 'Table Grid'
for ri, row in enumerate(rows):
for ci, cell_text in enumerate(row):
t.rows[ri].cells[ci].text = cell_text
text = page.extract_text()
if text:
for line in text.split('\n'):
line = line.strip()
if line:
doc.add_paragraph(line)
buf = io.BytesIO()
doc.save(buf)
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, buf.getvalue(), 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]]:
+30 -27
View File
@@ -252,8 +252,9 @@ class TwoPassObfuscator:
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
"""Конвертировать PDF в DOCX через pdfplumber. При совпадении имён — _из_pdf."""
import pdfplumber
from docx import Document as DocxDocument
result = []
existing_names = {f[0] for f in files}
for fname, content, ctype in files:
@@ -261,34 +262,36 @@ class TwoPassObfuscator:
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))
doc = DocxDocument()
with pdfplumber.open(io.BytesIO(content)) as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
if table:
rows = [[str(c or "").strip() for c in (row or [])] for row in table]
rows = [r for r in rows if any(r)]
if rows:
t = doc.add_table(rows=len(rows), cols=len(rows[0]))
t.style = 'Table Grid'
for ri, row in enumerate(rows):
for ci, cell_text in enumerate(row):
t.rows[ri].cells[ci].text = cell_text
text = page.extract_text()
if text:
for line in text.split('\n'):
line = line.strip()
if line:
doc.add_paragraph(line)
buf = io.BytesIO()
doc.save(buf)
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, buf.getvalue(), 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]]:
+1 -1
View File
@@ -84,7 +84,7 @@
<a href="https://contractor.pythonk8s.services.ngcloud.ru/">Сверка договоров</a>
<span class="sep">|</span>
<strong>DrHider</strong>
<span style="font-size:11px;color:var(--muted);">v1.11</span>
<span style="font-size:11px;color:var(--muted);">v1.12</span>
<button class="help-btn" onclick="openModal()" title="О сервисе">?</button>
</header>