fix: unit tests and pdf bullet rendering
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
06e68c343b
commit
dce41a01ac
3 changed files with 32 additions and 12 deletions
|
|
@ -828,7 +828,15 @@ class RendererPdf(BaseRenderer):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _renderJsonBulletList(self, list_data: Dict[str, Any], styles: Dict[str, Any]) -> List[Any]:
|
def _renderJsonBulletList(self, list_data: Dict[str, Any], styles: Dict[str, Any]) -> List[Any]:
|
||||||
"""Render a JSON bullet list to PDF elements."""
|
"""Render a JSON bullet list to PDF elements.
|
||||||
|
|
||||||
|
Uses ReportLab's built-in ``bulletText`` parameter for proper hanging
|
||||||
|
indent: the bullet/number is drawn at ``bulletIndent`` while all text
|
||||||
|
lines (including continuation) start at ``leftIndent``. This avoids
|
||||||
|
the previous approach of prepending the bullet character to the text
|
||||||
|
which caused misaligned wrap lines when the character width did not
|
||||||
|
match the indent value.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
content = list_data.get("content", {})
|
content = list_data.get("content", {})
|
||||||
if not isinstance(content, dict):
|
if not isinstance(content, dict):
|
||||||
|
|
@ -836,27 +844,39 @@ class RendererPdf(BaseRenderer):
|
||||||
items = content.get("items", [])
|
items = content.get("items", [])
|
||||||
bulletStyleDef = styles.get("bullet_list", {})
|
bulletStyleDef = styles.get("bullet_list", {})
|
||||||
indent = bulletStyleDef.get("indent", 18)
|
indent = bulletStyleDef.get("indent", 18)
|
||||||
|
fs = bulletStyleDef.get("font_size", 11)
|
||||||
|
|
||||||
|
us = getattr(self, '_unifiedStyle', None)
|
||||||
|
primaryFont = us["fonts"]["primary"] if us else "Calibri"
|
||||||
|
fontName = _resolveFontFamily(primaryFont, False)
|
||||||
|
|
||||||
|
isNumbered = content.get("list_type") == "numbered"
|
||||||
|
bulletChar = bulletStyleDef.get("bullet_char", "\u2022")
|
||||||
|
|
||||||
bulletStyle = ParagraphStyle(
|
bulletStyle = ParagraphStyle(
|
||||||
"BulletItem",
|
"BulletItem",
|
||||||
fontSize=bulletStyleDef.get("font_size", 11),
|
fontName=fontName,
|
||||||
|
fontSize=fs,
|
||||||
textColor=self._hexToColor(bulletStyleDef.get("color", styles.get("colors", {}).get("primary", "#24292e"))),
|
textColor=self._hexToColor(bulletStyleDef.get("color", styles.get("colors", {}).get("primary", "#24292e"))),
|
||||||
leftIndent=indent,
|
leftIndent=indent,
|
||||||
firstLineIndent=-indent,
|
bulletIndent=0,
|
||||||
|
bulletFontName=fontName,
|
||||||
|
bulletFontSize=fs,
|
||||||
spaceAfter=2,
|
spaceAfter=2,
|
||||||
leading=bulletStyleDef.get("font_size", 11) * 1.25,
|
leading=fs * 1.25,
|
||||||
)
|
)
|
||||||
|
|
||||||
bulletChar = bulletStyleDef.get("bullet_char", "\u2022")
|
|
||||||
elements = []
|
elements = []
|
||||||
for item in items:
|
for idx, item in enumerate(items):
|
||||||
|
marker = f"{idx + 1}." if isNumbered else bulletChar
|
||||||
runs = self._inlineRunsForListItem(item)
|
runs = self._inlineRunsForListItem(item)
|
||||||
if isinstance(item, list):
|
if isinstance(item, list):
|
||||||
xml = self._renderInlineRunsToPdfXml(runs)
|
xml = self._renderInlineRunsToPdfXml(runs)
|
||||||
elements.append(Paragraph(f"{bulletChar} {_wrapEmojiSpansInXml(xml)}", bulletStyle))
|
elements.append(Paragraph(_wrapEmojiSpansInXml(xml), bulletStyle, bulletText=marker))
|
||||||
elif isinstance(item, str):
|
elif isinstance(item, str):
|
||||||
elements.append(Paragraph(f"{bulletChar} {self._markdownInlineToReportlabXml(item)}", bulletStyle))
|
elements.append(Paragraph(self._markdownInlineToReportlabXml(item), bulletStyle, bulletText=marker))
|
||||||
elif isinstance(item, dict) and "text" in item:
|
elif isinstance(item, dict) and "text" in item:
|
||||||
elements.append(Paragraph(f"{bulletChar} {self._markdownInlineToReportlabXml(item['text'])}", bulletStyle))
|
elements.append(Paragraph(self._markdownInlineToReportlabXml(item['text']), bulletStyle, bulletText=marker))
|
||||||
|
|
||||||
if elements:
|
if elements:
|
||||||
elements.append(Spacer(1, bulletStyleDef.get("space_after", 3)))
|
elements.append(Spacer(1, bulletStyleDef.get("space_after", 3)))
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ def test_presentation_envelopes_preserves_data_slot_order_text_image_text():
|
||||||
)
|
)
|
||||||
|
|
||||||
class _Svc:
|
class _Svc:
|
||||||
interfaceDbComponent = _Mgmt()
|
chat = _Mgmt()
|
||||||
|
|
||||||
pres = {
|
pres = {
|
||||||
"kind": PRESENTATION_KIND,
|
"kind": PRESENTATION_KIND,
|
||||||
|
|
@ -666,7 +666,7 @@ def test_presentation_envelopes_to_document_json_image_slot():
|
||||||
return b"\x89PNG\r\n\x1a\n" + b"\x00" * 16
|
return b"\x89PNG\r\n\x1a\n" + b"\x00" * 16
|
||||||
|
|
||||||
class _Svc:
|
class _Svc:
|
||||||
interfaceDbComponent = _Mgmt()
|
chat = _Mgmt()
|
||||||
|
|
||||||
out = presentation_envelopes_to_document_json(
|
out = presentation_envelopes_to_document_json(
|
||||||
pres,
|
pres,
|
||||||
|
|
|
||||||
|
|
@ -596,7 +596,7 @@ def test_extract_image_slot_carries_file_id_and_mime():
|
||||||
|
|
||||||
class _Services:
|
class _Services:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.interfaceDbComponent = _MgmtStub()
|
self.chat = _MgmtStub()
|
||||||
|
|
||||||
envelope = {
|
envelope = {
|
||||||
"schemaVersion": PRESENTATION_SCHEMA_VERSION,
|
"schemaVersion": PRESENTATION_SCHEMA_VERSION,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue