Skip to content

Table extraction rework with RTL support (v1.4.0) - #9

Merged
NX1X merged 2 commits into
mainfrom
feat/converter-quality-rtl
Jul 25, 2026
Merged

Table extraction rework with RTL support (v1.4.0)#9
NX1X merged 2 commits into
mainfrom
feat/converter-quality-rtl

Conversation

@NX1X

@NX1X NX1X commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

Rework of the PDF table extraction engine plus a set of correctness and UX fixes. Bumps the app to 1.4.0.

Extraction quality

  • Page-wide column detection using a coverage histogram: a row with an empty cell keeps its remaining cells in the correct columns instead of shifting left, and title or total lines can no longer erase column separators
  • Row grouping merges nearby baselines instead of fixed-grid rounding (no more split rows from wobbly baselines)
  • Uses real rendered item widths from PDF.js, clamped against producer-inflated widths
  • Overlaid fragments are folded back into their host: "1 20" + overlaid "." extracts as "1.20"
  • Structured mode keeps key names: "Name: John" becomes two cells instead of dropping the key

RTL support

  • Page direction auto-detected from content (Hebrew/Arabic vs Latin character counts)
  • RTL tables emit column 1 as the rightmost (first logical) column
  • Mixed Hebrew/English cells join in reading order
  • Excel exports of RTL documents open right-to-left; preview table lays out right-to-left
  • "Hebrew/Arabic RTL Support" enabled by default; only activates on RTL-detected documents, LTR documents are unaffected

Fixes

  • Cleanup options no longer destroy data (re-derived from raw extraction on every toggle)
  • "Include Headers" adds an Excel autofilter on the header row
  • Formula-injection guard scoped to CSV only; xlsx cells are string-typed and never evaluated, so their text is no longer prefixed with an apostrophe
  • CSV quotes cells containing carriage returns
  • Filename stripping only removes the final .pdf extension, case-insensitive
  • Dropped files with an empty MIME type accepted by .pdf extension
  • Previous PDF document destroyed before loading a new one
  • Service worker precaches style.css, app.js, manifest.json, and icons (offline now works); cache bumped to v1.4.0
  • Turnstile verification once per session instead of once per file
  • Status messages, page count, and column labels are bilingual

Testing

  • Node harness over the extraction functions (unit level) plus end-to-end runs with pdfjs-dist 3.11.174 against two generated PDFs (LTR invoice and RTL Hebrew invoice with mixed-direction cells, empty cells, and spanning lines): all modes verified
  • Manual browser test on a local server

Summary by CodeRabbit

  • New Features

    • Added automatic RTL detection with Hebrew and Arabic support enabled by default.
    • Improved RTL table extraction, reading order, previews, and Excel exports.
    • Added bilingual status messages and translated preview labels.
    • Added an “Include Headers” option for Excel autofilters.
    • Added session-based verification and improved offline availability.
  • Bug Fixes

    • Improved PDF uploads, table extraction, row grouping, and column detection.
    • Corrected CSV quoting, Excel cell handling, PDF filename cleanup, and memory usage.
    • Cleanup options can now be changed without re-extracting the PDF.
  • Chores

    • Updated the application version to 1.4.0.

- Page-wide column detection via coverage histogram: empty cells stay
  aligned, title/total lines no longer break detection
- Full RTL support: auto-detected page direction, RTL column order,
  reading-order joins for mixed Hebrew/English cells, RTL Excel sheet
  view and RTL preview layout
- Merge overlaid fragments (decimal points) back into their numbers
- Non-destructive cleanup options (re-derive from raw extraction)
- Excel autofilter on header row when Include Headers is checked
- Formula-injection guard scoped to CSV only; xlsx string cells are
  never evaluated as formulas, so cell text stays unmangled
- Bilingual status messages, page count, and column labels
- Turnstile verification once per session instead of once per file
- Fix filename extension stripping, empty-MIME drops, CSV CR quoting,
  PDF memory release, spread-based stack overflow risk
- Service worker precaches all local assets for working offline mode
- Bump cache and app version to 1.4.0, update changelog
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Version 1.4.0 adds RTL-aware PDF extraction, bilingual status messaging, updated Excel/CSV export behavior, improved PDF upload lifecycle handling, default RTL support, and expanded service-worker precaching.

Changes

RTL extraction and export release

Layer / File(s) Summary
Upload and processing lifecycle
public/app.js
PDF validation, session verification reuse, previous-document cleanup, raw extraction state, and translated processing statuses are updated.
RTL extraction and data reconstruction
public/app.js
Text and table reconstruction now handles RTL reading order, RTL-dominant detection, structured labels, and safer column aggregation.
Localized preview and export
public/app.js, public/index.html
Preview direction, bilingual labels, Excel RTL views and autofilters, CSV quoting, and translated export statuses are updated; RTL support is enabled by default.
Release and offline packaging
CHANGELOG.md, README.md, package.json, public/sw.js
Version references are updated to 1.4.0, and service-worker precaching includes additional application assets.

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

Sequence Diagram(s)

sequenceDiagram
  participant PDFFile
  participant PDFjs
  participant extractDataFromPDF
  participant PreviewAndExport
  PDFFile->>PDFjs: Load PDF pages
  PDFjs->>extractDataFromPDF: Provide page text content
  extractDataFromPDF->>extractDataFromPDF: Reconstruct RTL text and tables
  extractDataFromPDF->>PreviewAndExport: Provide cleaned extracted data
  PreviewAndExport->>PreviewAndExport: Render preview and generate exports
Loading

Poem

A rabbit hops through RTL lines,
With tidy sheets and translated signs.
Columns turn and tables glow,
CSV winds softly, rows in tow.
Version four leaves caches bright—
Thump, thump, everything reads right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: a table extraction rework with RTL support and the 1.4.0 release bump.
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

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

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 25, 2026

Copy link
Copy Markdown

Deploying easyconvert-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 143a94a
Status: ✅  Deploy successful!
Preview URL: https://daa4c791.easyconvert-website.pages.dev
Branch Preview URL: https://feat-converter-quality-rtl.easyconvert-website.pages.dev

View logs

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
public/app.js (1)

733-734: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Empty results leave the previous preview on screen.

updatePreview() bails before clearing when extractedData is empty, so if a cleanup-option toggle (e.g. Skip Empty Rows) reduces the data to zero rows, the stale table from the previous state remains visible and downloads stay enabled.

🐛 Clear instead of returning early
 function updatePreview() {
-    if (extractedData.length === 0) return;
+    if (extractedData.length === 0) {
+        document.getElementById('previewHeader').innerHTML = '';
+        document.getElementById('previewBody').innerHTML = '';
+        previewSection.style.display = 'none';
+        return;
+    }
🤖 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 `@public/app.js` around lines 733 - 734, Update updatePreview() so it clears
the existing preview and disables download actions before handling an empty
extractedData array, then return without rendering rows. Preserve the current
rendering behavior for non-empty data.
🧹 Nitpick comments (5)
public/app.js (5)

269-272: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Spread-based push contradicts the anti-spread rationale used elsewhere.

Lines 696 and 826 deliberately avoid Math.max(...arr) because spreading large arrays overflows the argument stack; push(...pageData) has the same limit. A loop or a running array concat avoids it, and also lets you release page resources.

♻️ Proposed change
             if (pageData.length > 0) {
-                rawExtractedData.push(...pageData);
+                for (let r = 0; r < pageData.length; r++) rawExtractedData.push(pageData[r]);
             }
+            page.cleanup();
🤖 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 `@public/app.js` around lines 269 - 272, Replace the spread-based append in the
page-processing flow around rawExtractedData with a stack-safe approach that
does not pass pageData as individual arguments, such as iterative insertion or
array concatenation. Preserve the existing empty-page guard and ensure page
resources can be released as each page is processed.

123-129: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider guarding against overlapping re-extractions.

extractDataFromPDF() is async and unawaited here; toggling the checkbox quickly (or toggling while an extraction is already running) lets two runs interleave writes to rawExtractedData/lastExtractionRTL, producing mixed results. A simple in-flight flag (or disabling the option inputs during extraction) removes the hazard.

🤖 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 `@public/app.js` around lines 123 - 129, Guard the rtlSupport change handler
around extractDataFromPDF so a new extraction cannot start while one is already
in flight. Add and manage an in-flight flag (or disable the relevant option
inputs) across the async extraction lifecycle, while preserving updatePreview()
behavior when no PDF is loaded and ensuring the guard is cleared after
completion.

486-493: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Threshold is stricter than the comment claims for small tables.

coverage[b] > Math.max(1, Math.floor(n * 0.34)) requires ≥2 covering rows whenever n <= 5, i.e. 40-100% coverage rather than "more than ~a third". A 2-3 row table needs near-universal coverage or column detection silently falls back to one-cell-per-item. Either relax to >= with a proportional threshold or update the comment to match.

🤖 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 `@public/app.js` around lines 486 - 493, The threshold in the column-detection
loop is stricter than the stated “above ~a third” behavior for small multiRows
collections. Update the threshold calculation and isColumn comparison near
columns so coverage representing more than roughly one-third of rows is
accepted, including the intended small-table boundary cases, while preserving
the existing column grouping and gap logic.

340-360: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Short numeric fragments can be folded into a neighbouring host.

The only guards are text.length <= 2 and horizontal containment (with +1 slack), so a legitimate standalone cell like 12 or % that happens to sit inside an over-wide neighbour's bounds is silently absorbed and its own cell disappears. Since this only ever replaces a space, it's plausible to also require that the fragment be non-alphanumeric punctuation (., ,, :, -) which is the documented motivating case.

♻️ Narrow the trigger
-        if (item.text && item.text.length <= 2) {
+        if (item.text && item.text.length <= 2 && !/[A-Za-z0-9\u0590-\u05FF\u0600-\u06FF]/.test(item.text)) {
🤖 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 `@public/app.js` around lines 340 - 360, In the sorted item folding logic,
narrow the trigger guarded by the item.text length check so only documented
non-alphanumeric punctuation fragments (such as ., ,, :, or -) can be merged
into a host. Preserve the existing host containment, space-selection, and
replacement behavior while preventing numeric or other standalone alphanumeric
cells from being absorbed.

297-298: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer \uXXXX escapes for these character classes.

Raw RTL characters inside a regex literal are hard to review and easy for editors/encoding conversions to mangle (the class boundaries are literally invisible here). Line 777 already uses escapes for the same purpose, so unifying on escapes also removes the duplicate definition. Note the current upper range also swallows U+FEFF (BOM/ZWNBSP), which would count as an RTL character.

♻️ Example
-const RTL_CHARS = /[֐-߿ࡠ-ࣿיִ-﷿ﹰ-]/g;
+const RTL_CHARS = /[\u0590-\u05FF\u0600-\u06FF\u0700-\u07BF\u0860-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFC]/g;
 const LTR_CHARS = /[A-Za-z0-9]/g;
🤖 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 `@public/app.js` around lines 297 - 298, Update the RTL character-class
definition near RTL_CHARS to use explicit Unicode escapes, preserving the
intended ranges while excluding U+FEFF. Reuse the existing escaped definition at
the later occurrence and remove the duplicate declaration; keep LTR_CHARS
unchanged.
🤖 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 `@public/app.js`:
- Around line 1224-1228: Update the parameter substitution loop in the fileLabel
interpolation logic to replace every occurrence of each placeholder and treat
parameter values literally, so dollar sequences such as $&, $', and $1 are
preserved rather than interpreted by String.replace. Use a replacement approach
or callback that avoids replacement-pattern expansion while retaining the
existing behavior for missing or ordinary parameters.
- Around line 254-257: Update the no-pages early-exit branch in the
page-processing function to call hideProgress() before returning, matching the
cleanup performed by the other early exits while preserving the existing error
status.
- Around line 186-199: Update the new-PDF cleanup path around currentPDF
destruction to also clear the previous extractedData, preview, and download
output before assigning the new fileName. Reset the associated extraction
state/UI using the existing state variables and cleanup mechanisms, ensuring
failed extraction or pending verification cannot expose the prior document’s
rows under the new name.
- Around line 393-399: Pass the effective RTL flag from the method-selection
logic to extractStructuredData and extractTextData, just as the table paths use
pageRTL && rtlSupportEnabled(). Update those functions to use the passed flag
when determining pageDirectionRTL and reading order, rather than recomputing RTL
support unconditionally, while preserving existing behavior when RTL support is
enabled.

---

Outside diff comments:
In `@public/app.js`:
- Around line 733-734: Update updatePreview() so it clears the existing preview
and disables download actions before handling an empty extractedData array, then
return without rendering rows. Preserve the current rendering behavior for
non-empty data.

---

Nitpick comments:
In `@public/app.js`:
- Around line 269-272: Replace the spread-based append in the page-processing
flow around rawExtractedData with a stack-safe approach that does not pass
pageData as individual arguments, such as iterative insertion or array
concatenation. Preserve the existing empty-page guard and ensure page resources
can be released as each page is processed.
- Around line 123-129: Guard the rtlSupport change handler around
extractDataFromPDF so a new extraction cannot start while one is already in
flight. Add and manage an in-flight flag (or disable the relevant option inputs)
across the async extraction lifecycle, while preserving updatePreview() behavior
when no PDF is loaded and ensuring the guard is cleared after completion.
- Around line 486-493: The threshold in the column-detection loop is stricter
than the stated “above ~a third” behavior for small multiRows collections.
Update the threshold calculation and isColumn comparison near columns so
coverage representing more than roughly one-third of rows is accepted, including
the intended small-table boundary cases, while preserving the existing column
grouping and gap logic.
- Around line 340-360: In the sorted item folding logic, narrow the trigger
guarded by the item.text length check so only documented non-alphanumeric
punctuation fragments (such as ., ,, :, or -) can be merged into a host.
Preserve the existing host containment, space-selection, and replacement
behavior while preventing numeric or other standalone alphanumeric cells from
being absorbed.
- Around line 297-298: Update the RTL character-class definition near RTL_CHARS
to use explicit Unicode escapes, preserving the intended ranges while excluding
U+FEFF. Reuse the existing escaped definition at the later occurrence and remove
the duplicate declaration; keep LTR_CHARS unchanged.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c04b79ad-36b7-4bdb-9e02-e1caf06dd2f9

📥 Commits

Reviewing files that changed from the base of the PR and between 41e67b1 and fe09cf9.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • README.md
  • package.json
  • public/app.js
  • public/index.html
  • public/sw.js

Comment thread public/app.js
Comment on lines +186 to +199
// Release the previous document before loading a new one
if (currentPDF) {
currentPDF.destroy();
currentPDF = null;
}

fileName = file.name.replace(/\.pdf$/i, '');
showFileInfo(file);

// Already verified this session - skip straight to processing
if (turnstileVerified) {
processPDFAfterVerification(file);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=js '\b(enableDownloads|disableDownloads)\b' -C5 public/

Repository: NX1X/EasyConvert

Length of output: 1068


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline =="
ast-grep outline public/app.js --view expanded 2>/dev/null | sed -n '1,220p' || true

echo "== relevant public/app.js sections =="
sed -n '130,215p' public/app.js
sed -n '250,290p' public/app.js
sed -n '900,940p' public/app.js

echo "== state usages and updatePreview =="
rg -n --type=js '(rawExtractedData|extractedData|lastExtractionRTL|updatePreview|currentPDF|previewSection|downloadExcel|downloadCSV|disableDownloads|enableDownloads)"' public/app.js
rg -n --type=js 'function updatePreview|const previewSection|document.getElementById\(.*preview' public/app.js

Repository: NX1X/EasyConvert

Length of output: 8970


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== public/app.js lines 290-330 =="
sed -n '290,330p' public/app.js

echo "== public/app.js lines 370-395 =="
sed -n '370,395p' public/app.js

echo "== public/app.js lines 718-795 =="
sed -n '718,795p' public/app.js

echo "== public/app.js lines 205-260 =="
sed -n '205,260p' public/app.js

echo "== download filename generation =="
sed -n '780,810p' public/app.js

echo "== exact state/preview/disabled usage =="
rg -n --type=js 'rawExtractedData|extractedData|lastExtractionRTL|previewSection|downloadExcel\.disabled|downloadCSV\.disabled|function downloadFile' public/app.js

Repository: NX1X/EasyConvert

Length of output: 9604


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== public/app.js lines 100-125 =="
sed -n '100,125p' public/app.js

echo "== public/app.js lines 818-880 =="
sed -n '818,880p' public/app.js

Repository: NX1X/EasyConvert

Length of output: 4101


Reset extraction state when loading a new PDF.

Cleaning up currentPDF is not enough: enableDownloads() leaves the old extractedData, preview, and download output in place while fileName already reflects the newly selected file. If extraction fails or verification is still pending, users can download the previous document’s rows under the new file name. Reset these state/UI elements during the cleanup path.

🤖 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 `@public/app.js` around lines 186 - 199, Update the new-PDF cleanup path around
currentPDF destruction to also clear the previous extractedData, preview, and
download output before assigning the new fileName. Reset the associated
extraction state/UI using the existing state variables and cleanup mechanisms,
ensuring failed extraction or pending verification cannot expose the prior
document’s rows under the new name.

Comment thread public/app.js
Comment on lines 254 to 257
if (pagesToProcess.length === 0) {
showStatus('No valid pages to process.', 'error');
showStatus(t('statusNoPages'), 'error');
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Progress bar is left visible on the no-pages path.

Every other early exit in this function calls hideProgress(); this one returns at 60%, leaving a stuck progress indicator.

🐛 Proposed fix
         if (pagesToProcess.length === 0) {
             showStatus(t('statusNoPages'), 'error');
+            hideProgress();
             return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (pagesToProcess.length === 0) {
showStatus('No valid pages to process.', 'error');
showStatus(t('statusNoPages'), 'error');
return;
}
if (pagesToProcess.length === 0) {
showStatus(t('statusNoPages'), 'error');
hideProgress();
return;
}
🤖 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 `@public/app.js` around lines 254 - 257, Update the no-pages early-exit branch
in the page-processing function to call hideProgress() before returning,
matching the cleanup performed by the other early exits while preserving the
existing error status.

Comment thread public/app.js
Comment on lines 393 to 399
if (method === 'table' || method === 'advanced') {
return extractTableData(textContent, viewport, method === 'advanced');
return extractTableData(textContent, viewport, method === 'advanced', pageRTL && rtlSupportEnabled());
} else if (method === 'structured') {
return extractStructuredData(textContent, viewport);
} else {
return extractTextData(textContent);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

RTL checkbox is only honored by the table methods.

rtlSupportEnabled() gates the table/advanced paths, but extractStructuredData (line 595) and extractTextData (line 645) recompute pageDirectionRTL and reorder reading order unconditionally. Unchecking "Hebrew/Arabic RTL Support" therefore has no effect in Text/Structured modes, while the preview dir and Excel RTL view (which do check it) disagree with the emitted ordering. Suggest passing the effective flag down from here.

♻️ Thread the effective flag through
-    if (method === 'table' || method === 'advanced') {
-        return extractTableData(textContent, viewport, method === 'advanced', pageRTL && rtlSupportEnabled());
-    } else if (method === 'structured') {
-        return extractStructuredData(textContent, viewport);
-    } else {
-        return extractTextData(textContent);
-    }
+    const effectiveRTL = pageRTL && rtlSupportEnabled();
+    if (method === 'table' || method === 'advanced') {
+        return extractTableData(textContent, viewport, method === 'advanced', effectiveRTL);
+    } else if (method === 'structured') {
+        return extractStructuredData(textContent, effectiveRTL);
+    } else {
+        return extractTextData(textContent, effectiveRTL);
+    }
🤖 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 `@public/app.js` around lines 393 - 399, Pass the effective RTL flag from the
method-selection logic to extractStructuredData and extractTextData, just as the
table paths use pageRTL && rtlSupportEnabled(). Update those functions to use
the passed flag when determining pageDirectionRTL and reading order, rather than
recomputing RTL support unconditionally, while preserving existing behavior when
RTL support is enabled.

Comment thread public/app.js
Comment on lines +1224 to +1228
if (params) {
Object.keys(params).forEach(function (name) {
str = str.replace('{' + name + '}', params[name]);
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

String.replace with a string replacement mangles $ in params.

fileLabel interpolates a user-supplied file name; if it contains $&, $' or $1, those are treated as replacement patterns and the rendered label is wrong. Also only the first occurrence of each placeholder is substituted.

🐛 Proposed fix
     if (params) {
         Object.keys(params).forEach(function (name) {
-            str = str.replace('{' + name + '}', params[name]);
+            str = str.split('{' + name + '}').join(String(params[name]));
         });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (params) {
Object.keys(params).forEach(function (name) {
str = str.replace('{' + name + '}', params[name]);
});
}
if (params) {
Object.keys(params).forEach(function (name) {
str = str.split('{' + name + '}').join(String(params[name]));
});
}
🤖 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 `@public/app.js` around lines 1224 - 1228, Update the parameter substitution
loop in the fileLabel interpolation logic to replace every occurrence of each
placeholder and treat parameter values literally, so dollar sequences such as
$&, $', and $1 are preserved rather than interpreted by String.replace. Use a
replacement approach or callback that avoids replacement-pattern expansion while
retaining the existing behavior for missing or ordinary parameters.

@NX1X
NX1X enabled auto-merge (squash) July 25, 2026 08:16
@NX1X
NX1X merged commit 18e4ab9 into main Jul 25, 2026
10 checks passed
@NX1X
NX1X deleted the feat/converter-quality-rtl branch July 25, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant