fix(translators): ameliorer le respect des formats de fichiers pour Excel, PowerPoint et PDF
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m33s

- Excel: elargissement intelligent des colonnes pour le texte etendu et alignement RTL des cellules

- PowerPoint: retour a la ligne et auto-fit anti-debordement, colonnes de tableaux en RTL

- PDF: protection multi-colonnes pour eviter les chevauchements et formatage hebreu

- Tests: validation de non-regression pour tous les formats
This commit is contained in:
2026-09-02 21:35:31 +02:00
parent cae09fc512
commit 6f5b6acbdc
6 changed files with 257 additions and 16 deletions

View File

@@ -786,3 +786,58 @@ class TestMultipleSheets:
assert wb_out.worksheets[0]["A1"].value == "Feuille1Texte"
assert wb_out.worksheets[1]["A1"].value == "Feuille2Texte"
class TestColumnWidthAndAlignment:
"""Tests for intelligent column width adjustment and RTL alignment."""
def test_column_width_widened_for_long_translation(self, tmp_path):
"""Test that narrow columns are widened when translated text expands."""
mock_provider = MockTranslationProvider(
{
"Hi": "This is a much longer translated sentence that requires more width",
}
)
translator = ExcelTranslator(provider=mock_provider)
wb = Workbook()
ws = wb.active
ws["A1"] = "Hi"
ws.column_dimensions["A"].width = 8.0
input_file = tmp_path / "narrow.xlsx"
output_file = tmp_path / "widened.xlsx"
wb.save(input_file)
translator.translate_file(input_file, output_file, "fr")
wb_out = load_workbook(output_file)
new_width = wb_out.active.column_dimensions["A"].width
assert new_width is not None
assert new_width > 15.0
def test_cell_alignment_flipped_for_rtl(self, tmp_path):
"""Test that explicit left alignment is converted to right alignment in RTL."""
from openpyxl.styles import Alignment
mock_provider = MockTranslationProvider(
{
"Hello": "سلام",
}
)
translator = ExcelTranslator(provider=mock_provider)
wb = Workbook()
ws = wb.active
ws["A1"] = "Hello"
ws["A1"].alignment = Alignment(horizontal="left")
input_file = tmp_path / "ltr_align.xlsx"
output_file = tmp_path / "rtl_align.xlsx"
wb.save(input_file)
translator.translate_file(input_file, output_file, "fa")
wb_out = load_workbook(output_file)
cell_align = wb_out.active["A1"].alignment
assert cell_align.horizontal == "right"

View File

@@ -968,3 +968,53 @@ class TestRunMerging:
assert "Ligne un" in para.runs[0].text
assert "Ligne deux" in para.runs[0].text
assert "\n" in para.runs[0].text
class TestPptxTableRtlAndAutoFit:
"""Tests for table RTL and text-frame word wrap / auto-fit."""
def test_table_rtl_attribute_applied(self, tmp_path):
"""Table should receive <a:tblPr rtl='1'> for RTL target."""
translator = PowerPointTranslator(
provider=MockTranslationProvider({"Item": "مورد"})
)
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
shape = slide.shapes.add_table(1, 1, 100, 100, 200, 100)
shape.table.cell(0, 0).text = "Item"
input_file = tmp_path / "input_tbl.pptx"
output_file = tmp_path / "output_tbl.pptx"
prs.save(input_file)
translator.translate_file(input_file, output_file, "fa")
prs_out = Presentation(output_file)
tbl_out = prs_out.slides[0].shapes[0]
tbl_el = tbl_out._element.find(".//{http://schemas.openxmlformats.org/drawingml/2006/main}tbl")
tblPr = tbl_el.find("{http://schemas.openxmlformats.org/drawingml/2006/main}tblPr")
assert tblPr is not None
assert tblPr.get("rtl") == "1"
def test_word_wrap_and_autofit_configured(self, tmp_path):
"""Text frames should have word_wrap and auto_size enabled."""
translator = PowerPointTranslator(
provider=MockTranslationProvider({"Hello": "Bonjour"})
)
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
shape = slide.shapes.add_textbox(100, 100, 200, 50)
shape.text_frame.text = "Hello"
shape.text_frame.word_wrap = False
input_file = tmp_path / "input_wrap.pptx"
output_file = tmp_path / "output_wrap.pptx"
prs.save(input_file)
translator.translate_file(input_file, output_file, "fr")
prs_out = Presentation(output_file)
tf_out = prs_out.slides[0].shapes[0].text_frame
assert tf_out.word_wrap is True

View File

@@ -1225,3 +1225,42 @@ class TestLtrNormalization:
assert "1." in lines[0] or "1" in lines[0]
# Must not contain overflow marker
assert "[translation overflow]" not in " ".join(lines)
def test_pdf_multicolumn_protection(self, tmp_path):
"""Tier 1 expansion on a left column must not expand past the right column."""
import fitz
src = tmp_path / "src_cols.pdf"
doc = fitz.open()
page = doc.new_page(width=600, height=400)
# Left column at x=50..250, right column at x=300..500
page.insert_textbox(fitz.Rect(50, 50, 250, 100), "Titre gauche", fontsize=14, fontname="helv")
page.insert_textbox(fitz.Rect(300, 50, 500, 100), "Titre droite", fontsize=14, fontname="helv")
doc.save(str(src))
doc.close()
translator = PDFTranslator(
provider=MockTranslationProvider({
"Titre gauche": "A very long left heading translation that requires more width",
"Titre droite": "Right heading",
})
)
out = tmp_path / "out_cols.pdf"
translator.translate_file(src, out, "en")
doc2 = fitz.open(str(out))
blocks = doc2[0].get_text("blocks")
doc2.close()
left_blocks = [b for b in blocks if "left heading" in b[4]]
if left_blocks:
# The left block must NOT cross the start of the right column (x=300)
assert left_blocks[0][2] <= 300.0
def test_pdf_hebrew_formatting(self):
"""Hebrew text must be visually formatted without Arabic cursive reshaping."""
from translators.pdf_translator import _shape_rtl_multiline
hebrew_text = "שלום עולם"
res = _shape_rtl_multiline(hebrew_text, max_width=200, fontsize=12)
assert res is not None
assert len(res) > 0