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
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:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user