fix: drhider PDF→DOCX via pdfplumber (LibreOffice cant do it) v1.12
Deploy contracts-flask / validate (push) Successful in 0s
Deploy contracts-flask / validate (push) Successful in 0s
This commit is contained in:
+26
-23
@@ -252,8 +252,9 @@ class TwoPassObfuscator:
|
|||||||
self._sorted_keys.clear()
|
self._sorted_keys.clear()
|
||||||
|
|
||||||
def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
||||||
"""Конвертировать PDF в DOCX через LibreOffice. При совпадении имён — _из_pdf."""
|
"""Конвертировать PDF в DOCX через pdfplumber. При совпадении имён — _из_pdf."""
|
||||||
import subprocess, tempfile
|
import pdfplumber
|
||||||
|
from docx import Document as DocxDocument
|
||||||
result = []
|
result = []
|
||||||
existing_names = {f[0] for f in files}
|
existing_names = {f[0] for f in files}
|
||||||
for fname, content, ctype in files:
|
for fname, content, ctype in files:
|
||||||
@@ -261,34 +262,36 @@ class TwoPassObfuscator:
|
|||||||
result.append((fname, content, ctype))
|
result.append((fname, content, ctype))
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp:
|
doc = DocxDocument()
|
||||||
tmp.write(content)
|
with pdfplumber.open(io.BytesIO(content)) as pdf:
|
||||||
pdf_path = tmp.name
|
for page in pdf.pages:
|
||||||
outdir = tempfile.mkdtemp()
|
tables = page.extract_tables()
|
||||||
subprocess.run(['libreoffice', '--headless', '--convert-to', 'docx',
|
for table in tables:
|
||||||
'--outdir', outdir, pdf_path], timeout=30, capture_output=True)
|
if table:
|
||||||
docx_files = [f for f in os.listdir(outdir) if f.endswith('.docx')]
|
rows = [[str(c or "").strip() for c in (row or [])] for row in table]
|
||||||
if docx_files:
|
rows = [r for r in rows if any(r)]
|
||||||
with open(os.path.join(outdir, docx_files[0]), 'rb') as f:
|
if rows:
|
||||||
docx_content = f.read()
|
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'
|
new_name = fname[:-4] + '.docx'
|
||||||
if new_name in existing_names:
|
if new_name in existing_names:
|
||||||
new_name = fname[:-4] + '_из_pdf.docx'
|
new_name = fname[:-4] + '_из_pdf.docx'
|
||||||
existing_names.add(new_name)
|
existing_names.add(new_name)
|
||||||
result.append((new_name, docx_content, ctype))
|
result.append((new_name, buf.getvalue(), ctype))
|
||||||
else:
|
|
||||||
log.warning("PDF→DOCX failed for %s", fname)
|
|
||||||
result.append((fname, content, ctype))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("PDF→DOCX error for %s: %s", fname, e)
|
log.warning("PDF→DOCX error for %s: %s", fname, e)
|
||||||
result.append((fname, content, ctype))
|
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
|
return result
|
||||||
|
|
||||||
def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
||||||
|
|||||||
+26
-23
@@ -252,8 +252,9 @@ class TwoPassObfuscator:
|
|||||||
self._sorted_keys.clear()
|
self._sorted_keys.clear()
|
||||||
|
|
||||||
def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
def _convert_pdfs_to_docx(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
||||||
"""Конвертировать PDF в DOCX через LibreOffice. При совпадении имён — _из_pdf."""
|
"""Конвертировать PDF в DOCX через pdfplumber. При совпадении имён — _из_pdf."""
|
||||||
import subprocess, tempfile
|
import pdfplumber
|
||||||
|
from docx import Document as DocxDocument
|
||||||
result = []
|
result = []
|
||||||
existing_names = {f[0] for f in files}
|
existing_names = {f[0] for f in files}
|
||||||
for fname, content, ctype in files:
|
for fname, content, ctype in files:
|
||||||
@@ -261,34 +262,36 @@ class TwoPassObfuscator:
|
|||||||
result.append((fname, content, ctype))
|
result.append((fname, content, ctype))
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp:
|
doc = DocxDocument()
|
||||||
tmp.write(content)
|
with pdfplumber.open(io.BytesIO(content)) as pdf:
|
||||||
pdf_path = tmp.name
|
for page in pdf.pages:
|
||||||
outdir = tempfile.mkdtemp()
|
tables = page.extract_tables()
|
||||||
subprocess.run(['libreoffice', '--headless', '--convert-to', 'docx',
|
for table in tables:
|
||||||
'--outdir', outdir, pdf_path], timeout=30, capture_output=True)
|
if table:
|
||||||
docx_files = [f for f in os.listdir(outdir) if f.endswith('.docx')]
|
rows = [[str(c or "").strip() for c in (row or [])] for row in table]
|
||||||
if docx_files:
|
rows = [r for r in rows if any(r)]
|
||||||
with open(os.path.join(outdir, docx_files[0]), 'rb') as f:
|
if rows:
|
||||||
docx_content = f.read()
|
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'
|
new_name = fname[:-4] + '.docx'
|
||||||
if new_name in existing_names:
|
if new_name in existing_names:
|
||||||
new_name = fname[:-4] + '_из_pdf.docx'
|
new_name = fname[:-4] + '_из_pdf.docx'
|
||||||
existing_names.add(new_name)
|
existing_names.add(new_name)
|
||||||
result.append((new_name, docx_content, ctype))
|
result.append((new_name, buf.getvalue(), ctype))
|
||||||
else:
|
|
||||||
log.warning("PDF→DOCX failed for %s", fname)
|
|
||||||
result.append((fname, content, ctype))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning("PDF→DOCX error for %s: %s", fname, e)
|
log.warning("PDF→DOCX error for %s: %s", fname, e)
|
||||||
result.append((fname, content, ctype))
|
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
|
return result
|
||||||
|
|
||||||
def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
def _expand_zips(self, files: List[Tuple[str, bytes, str]]) -> List[Tuple[str, bytes, str]]:
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<a href="https://contractor.pythonk8s.services.ngcloud.ru/">Сверка договоров</a>
|
<a href="https://contractor.pythonk8s.services.ngcloud.ru/">Сверка договоров</a>
|
||||||
<span class="sep">|</span>
|
<span class="sep">|</span>
|
||||||
<strong>DrHider</strong>
|
<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>
|
<button class="help-btn" onclick="openModal()" title="О сервисе">?</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user