Skip to content

fix(slides): improve text width overflow detection#2056

Open
ethan-zhx wants to merge 7 commits into
mainfrom
fix/slides_text_width_detect
Open

fix(slides): improve text width overflow detection#2056
ethan-zhx wants to merge 7 commits into
mainfrom
fix/slides_text_width_detect

Conversation

@ethan-zhx

@ethan-zhx ethan-zhx commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • account for letter spacing in text-width and wrapping estimates
  • report significant text overflow as errors while retaining background-decoration findings as informational
  • limit canvas-overflow checks to supported shape, table, and chart types

Root cause

The lint estimator ignored letter spacing and applied uniform severity and canvas geometry checks to elements that do not need them, causing missed or noisy overflow findings.

Validation

Not run (not requested).

Summary by CodeRabbit

  • Bug Fixes
    • Improved text overflow/width calculations by incorporating letterSpacing end-to-end, including paragraph-specific values and shaped text content.
    • Refined overflow severity and diagnostics, downgrading “decorative background text” cases to informational messages while preserving true errors.
    • Reduced false positives in canvas out-of-bounds detection by narrowing which element types are evaluated and by adjusting rotation-related bounds handling.
    • Updated lint reporting for info issues and tightened behavior for auto-wrapping and wrapping-line overflow scenarios.

Text-shape overflow was always reported as a warning, which let clearly
broken pages pass the lint gate. Overflow > 10px now upgrades to error;
smaller overflows stay as warning to avoid flagging near-fit cases.
Extract letterSpacing from content/paragraph attrs and factor it into
width and line-count estimates, and stop short-circuiting the shape
overflow check for autoFit shapes so that letterSpacing-heavy captions
under normal-auto-fit no longer escape detection.
Large low-alpha text underneath other text shapes is typically a
background design element; treat text_may_overflow_shape as info in
that case instead of warning/error.
chartParsedValues is a server-injected roundtrip child tag under
chartField, not an attribute. Move it from ROUNDTRIP_SXSD_ATTRS to a
new ROUNDTRIP_SXSD_TAGS set and skip the tag (and its subtree) in the
SXSD tag whitelist check.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Text overflow linting now accounts for paragraph and shape letter spacing, font attributes, padding, and decorative background text. Canvas validation now uses restricted element types and selective rotation bounds. Lint results preserve informational issues with document- and slide-level counts and lists.

Changes

Lint behavior

Layer / File(s) Summary
Text measurement and overflow classification
skills/lark-slides/scripts/xml_text_overlap_lint.py, skills/lark-slides/scripts/xml_text_overlap_lint_test.py
Text parsing and width estimation incorporate letter spacing, font family, boldness, and padding. Overflow checks include auto-fit content, paragraph-specific spacing, decorative-background downgrades, and expanded diagnostic tests.
Canvas bounds filtering and validation
skills/lark-slides/scripts/xml_text_overlap_lint.py, skills/lark-slides/scripts/xml_text_overlap_lint_test.py
Rotation-aware bounds and out-of-canvas checks are limited to supported element types; image and line expectations, rotated rectangles, and overlap measurements are updated.
Informational result aggregation
skills/lark-slides/scripts/xml_text_overlap_lint.py, skills/lark-slides/scripts/xml_text_overlap_lint_test.py
Info-level issues are preserved and reported through document and slide infos lists and info_count, with table and overlap CLI assertions updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant XMLContent
  participant TextEstimator
  participant OverflowDetector
  participant LintResult
  XMLContent->>TextEstimator: paragraph and shape text attributes
  TextEstimator->>OverflowDetector: estimated widths and line counts
  OverflowDetector->>LintResult: severity and diagnostic fields
  LintResult->>LintResult: aggregate errors, warnings, and infos
Loading

Possibly related PRs

  • larksuite/cli#1950: Updates the same canvas bounding-box and out-of-canvas detection logic.
  • larksuite/cli#1963: Modifies the same text overflow estimation and detection paths.
  • larksuite/cli#2022: Changes the same canvas out-of-bounds logic and structured diagnostic handling.

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary, root cause, and validation, but it omits the required Changes, Test Plan checklist, and Related Issues sections. Add the missing Changes, Test Plan, and Related Issues sections, and use the repository's template format with checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving text width overflow detection in slides.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/slides_text_width_detect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jul 25, 2026
@ethan-zhx
ethan-zhx marked this pull request as ready for review July 25, 2026 17:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
skills/lark-slides/scripts/xml_text_overlap_lint.py (1)

910-932: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Advice is contradictory now that autoFit="normal-auto-fit" shapes are no longer skipped.

Removing the auto-fit early-continue means shapes that already declare wrap="true" autoFit="normal-auto-fit" get an error telling them to set exactly that. Suppress that clause when the element already has those values.

🐛 Suggested tweak
-        message = (
-            f'text shape {element["id"]} may overflow its own content box '
-            f'(estimated {estimated_height:g}px, available {available_height:g}px); '
-            'consider setting content wrap="true" autoFit="normal-auto-fit"'
-        )
+        already_auto_fit = element.get("autoFit") == "normal-auto-fit"
+        message = (
+            f'text shape {element["id"]} may overflow its own content box '
+            f'(estimated {estimated_height:g}px, available {available_height:g}px)'
+        )
+        if not already_auto_fit:
+            message += '; consider setting content wrap="true" autoFit="normal-auto-fit"'
+        else:
+            message += '; autoFit="normal-auto-fit" will shrink the text, verify the rendered size'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 910 - 932,
The overflow warning in the message construction should avoid recommending wrap
and normal-auto-fit when the element already declares both settings. Update the
logic around the existing message and element attributes in the text-overflow
lint path, using an alternative hint for shapes that already have those values
while preserving the current advice for all others.
🧹 Nitpick comments (6)
skills/lark-slides/scripts/xml_text_overlap_lint.py (3)

1269-1274: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The whitelist makes the extra_elements canvas pass dead code.

lint_xml calls detect_elements_out_of_canvas(extra_elements, ...) with only icon/polyline/line kinds, none of which pass this filter, so extra_overflow_issues is now always empty. Drop that call (or add the intended kinds to the whitelist) so the intent is explicit rather than silently no-op.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 1269 -
1274, The whitelist in the element iteration excludes all kinds passed to the
extra_elements canvas check, making lint_xml’s extra_overflow_issues path
ineffective. Update lint_xml and the related element filtering so the intended
extra_elements kinds are processed, or remove the now-dead
detect_elements_out_of_canvas(extra_elements, ...) call; keep the chosen
behavior explicit and ensure extra_overflow_issues is not silently always empty.

938-956: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the magic thresholds into module constants.

96, 0.5 here and the overflow > 10 cutoff at line 909 are tuning knobs; this module already keeps such values as named constants (e.g. CANVAS_OVERFLOW_TOLERANCE), which makes them discoverable and testable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 938 - 956,
Define module-level named constants for the decorative-text font-size threshold,
alpha threshold, and the overflow cutoff currently using 96, 0.5, and 10. Update
is_background_decorative_text and the overflow check near the existing tolerance
constants to reference those constants, preserving the current threshold
behavior.

809-815: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

estimate_text_max_line_width still ignores paragraph-level letterSpacing.

It splits element["text"] on newlines and resolves spacing element-only, so a <p letterSpacing="4"> override is invisible to horizontal-overflow and visual-bbox estimates even though line counting now honors it. Iterating element["paragraphs"] (falling back to the split when absent) would keep both paths consistent.

♻️ Sketch
 def estimate_text_max_line_width(element: dict[str, Any]) -> int | float:
     font_size = element["fontSize"] if isinstance(element["fontSize"], (int, float)) else 16
-    letter_spacing = resolve_letter_spacing(element)
-    paragraphs = [paragraph for paragraph in re.split(r"\n+", element["text"]) if paragraph]
-    return max(
-        [estimate_text_width(paragraph, font_size, letter_spacing) for paragraph in paragraphs] or [1]
-    )
+    records = element.get("paragraphs") or [{"text": element["text"]}]
+    widths = [
+        estimate_text_width(line, font_size, resolve_letter_spacing(element, record))
+        for record in records
+        for line in re.split(r"\n+", record.get("text") or "")
+        if line
+    ]
+    return max(widths or [1])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 809 - 815,
Update estimate_text_max_line_width to iterate element["paragraphs"] when
available, resolving each paragraph’s letterSpacing override through the
existing spacing-resolution logic; fall back to the current newline-split text
when paragraphs are absent. Preserve the existing width calculation and default
minimum width so horizontal-overflow and visual-bbox estimates match
paragraph-level line counting.
skills/lark-slides/scripts/xml_text_overlap_lint_test.py (3)

689-689: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale test name.

test_lint_xml_warns_when_text_may_overflow_its_own_shape now asserts two errors; rename to reflect the error classification (and that auto-fit shapes are included).

Also applies to: 714-725

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` at line 689, Rename
test_lint_xml_warns_when_text_may_overflow_its_own_shape to describe that it
asserts both error classifications and includes auto-fit shapes, updating any
related references consistently.

799-861: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider parameterizing these three near-identical cases.

The alpha / no-foreground / order variants differ only in the shape XML; a single subTest loop over (name, shapes, expected element id) would cut the duplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` around lines 799 -
861, Parameterize the three near-identical tests for text overflow severity into
one test method using a subTest loop over each XML shape variant and expected
element ID. Preserve the existing assertions that the matching
text_may_overflow_shape issue has level "error", including the alpha,
missing-foreground, and ordering cases.

1101-1143: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fixtures rely on short-circuit evaluation to avoid KeyError: "type".

The table, chart and image dicts omit type; the test only passes because the or/and ordering in the filter (and in element_canvas_bbox) never reaches element["type"] for those kinds. Adding "type" to each fixture makes the test robust against a harmless reordering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` around lines 1101 -
1143, Add the appropriate type field to the table, chart, and image fixtures in
test_detect_elements_out_of_canvas_limits_detection_to_whitelist, so each
element dictionary is complete and the test no longer depends on short-circuit
evaluation avoiding element["type"].
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint.py`:
- Around line 905-909: The background-decoration classification in
`xml_text_overlap_lint.py` must match normalization behavior: either update
`normalize_issue`/`build_result` so `info` is preserved and counted correctly,
or change the classification near `is_background_decorative_text` to emit
`warning`. In `skills/lark-slides/scripts/xml_text_overlap_lint.py` lines
905-909, apply the chosen contract; in
`skills/lark-slides/scripts/xml_text_overlap_lint_test.py` lines 796-797, assert
the resulting level, updating the expectation to `warning` unless normalization
is changed to preserve `info`.

---

Outside diff comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint.py`:
- Around line 910-932: The overflow warning in the message construction should
avoid recommending wrap and normal-auto-fit when the element already declares
both settings. Update the logic around the existing message and element
attributes in the text-overflow lint path, using an alternative hint for shapes
that already have those values while preserving the current advice for all
others.

---

Nitpick comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py`:
- Line 689: Rename test_lint_xml_warns_when_text_may_overflow_its_own_shape to
describe that it asserts both error classifications and includes auto-fit
shapes, updating any related references consistently.
- Around line 799-861: Parameterize the three near-identical tests for text
overflow severity into one test method using a subTest loop over each XML shape
variant and expected element ID. Preserve the existing assertions that the
matching text_may_overflow_shape issue has level "error", including the alpha,
missing-foreground, and ordering cases.
- Around line 1101-1143: Add the appropriate type field to the table, chart, and
image fixtures in
test_detect_elements_out_of_canvas_limits_detection_to_whitelist, so each
element dictionary is complete and the test no longer depends on short-circuit
evaluation avoiding element["type"].

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py`:
- Around line 1269-1274: The whitelist in the element iteration excludes all
kinds passed to the extra_elements canvas check, making lint_xml’s
extra_overflow_issues path ineffective. Update lint_xml and the related element
filtering so the intended extra_elements kinds are processed, or remove the
now-dead detect_elements_out_of_canvas(extra_elements, ...) call; keep the
chosen behavior explicit and ensure extra_overflow_issues is not silently always
empty.
- Around line 938-956: Define module-level named constants for the
decorative-text font-size threshold, alpha threshold, and the overflow cutoff
currently using 96, 0.5, and 10. Update is_background_decorative_text and the
overflow check near the existing tolerance constants to reference those
constants, preserving the current threshold behavior.
- Around line 809-815: Update estimate_text_max_line_width to iterate
element["paragraphs"] when available, resolving each paragraph’s letterSpacing
override through the existing spacing-resolution logic; fall back to the current
newline-split text when paragraphs are absent. Preserve the existing width
calculation and default minimum width so horizontal-overflow and visual-bbox
estimates match paragraph-level line counting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: da340127-da0d-44be-9b2e-bf1a05ef28a5

📥 Commits

Reviewing files that changed from the base of the PR and between a7865cd and bbb1311.

📒 Files selected for processing (2)
  • skills/lark-slides/scripts/xml_text_overlap_lint.py
  • skills/lark-slides/scripts/xml_text_overlap_lint_test.py

Comment thread skills/lark-slides/scripts/xml_text_overlap_lint.py
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@ba422b07443875c4843f7bf8f099cf86ad83a660

🧩 Skill update

npx skills add larksuite/cli#fix/slides_text_width_detect -y -g

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.14%. Comparing base (a7865cd) to head (ba422b0).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2056   +/-   ##
=======================================
  Coverage   75.14%   75.14%           
=======================================
  Files         911      911           
  Lines       96322    96322           
=======================================
  Hits        72385    72385           
  Misses      18370    18370           
  Partials     5567     5567           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Replace the uniform 0.55em half-width coefficient with per-character-type
coefficients, add font-family awareness (sans/serif), bold multiplier,
letter-spacing support, and fix padding-aware line wrapping.

- Split half-width chars into uppercase (0.57), lowercase (0.51 sans / 0.53
  serif), digits (0.58), and punctuation (0.50)
- Add classify_font_family() to apply slightly wider lowercase widths for
  serif fonts (Georgia, Source Han Serif/思源宋体, Times, etc.)
- Add 5% width multiplier for bold text; detect <strong>/<b>/<i>/<em> tags
  and span-level bold/italic attributes in addition to content attrs
- Fix estimate_text_line_count_for_text to subtract paddingLeft/paddingRight
  from available width before computing wrap lines
- Add resolve_letter_spacing and wire letterSpacing through estimate_text_width
- Extract fontFamily/bold/italic/letterSpacing into element dict during parse

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
skills/lark-slides/scripts/xml_text_overlap_lint.py (1)

717-750: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Bold/italic detection logic looks correct; consider adding direct unit coverage for all 4 fallback paths.

The cascading checks (content attr → shape attr → inline <strong>/<b>/<i>/<em> tag → <span bold/italic> attr) are logically sound. However, the visible test file only appears to exercise the content-attribute path (e.g. bold="true" on <content>); I don't see a dedicated test for the shape-level attribute, inline-tag, or span-attribute fallbacks. Since this is new, multi-branch logic feeding directly into width/line estimation, a regression in any of these paths would silently affect overflow accuracy.

Do you want me to draft extract_elements-focused unit tests covering each fallback (shape bold/italic attr, <strong>/<em> tags, <span bold="true">) ?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py` around lines 717 - 750,
Add extract_elements-focused unit coverage for bold and italic detection through
the shape-level attributes, inline strong/b/em tags, and span boolean
attributes, in addition to the existing content-attribute cases. Assert each
fallback sets the corresponding element flags so width and line estimation
receive the correct formatting state.
skills/lark-slides/scripts/xml_text_overlap_lint_test.py (1)

945-1146: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Canvas-scope-restriction tests align with the intended new behavior.

The updated expectations (images/lines ignored, rotated rect uses declared bounds, whitelist test) are internally consistent with the element_canvas_bbox/detect_elements_out_of_canvas changes. See the related comment on xml_text_overlap_lint.py (Lines 1354-1363) about whether icon/polyline exclusion here was fully intentional, since no test in this range explicitly exercises those two kinds against detect_elements_out_of_canvas.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py` around lines 945 -
1146, Review the canvas-scope whitelist in detect_elements_out_of_canvas and
explicitly confirm the intended handling of icon and polyline elements alongside
the tested image and line exclusions. If they should be ignored, preserve that
behavior; otherwise update the whitelist and add focused coverage for both kinds
without changing the existing rotated-rect and supported-element behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@skills/lark-slides/scripts/xml_text_overlap_lint_test.py`:
- Around line 945-1146: Review the canvas-scope whitelist in
detect_elements_out_of_canvas and explicitly confirm the intended handling of
icon and polyline elements alongside the tested image and line exclusions. If
they should be ignored, preserve that behavior; otherwise update the whitelist
and add focused coverage for both kinds without changing the existing
rotated-rect and supported-element behavior.

In `@skills/lark-slides/scripts/xml_text_overlap_lint.py`:
- Around line 717-750: Add extract_elements-focused unit coverage for bold and
italic detection through the shape-level attributes, inline strong/b/em tags,
and span boolean attributes, in addition to the existing content-attribute
cases. Assert each fallback sets the corresponding element flags so width and
line estimation receive the correct formatting state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39e13736-9529-4887-97d9-c68a12bcd582

📥 Commits

Reviewing files that changed from the base of the PR and between bbb1311 and ba422b0.

📒 Files selected for processing (2)
  • skills/lark-slides/scripts/xml_text_overlap_lint.py
  • skills/lark-slides/scripts/xml_text_overlap_lint_test.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant