PR #1311: Replace separate spaceWidth + getSpaceKernAdjust() with a single getSpaceAdvance() that combines space glyph advance and kerning in fixed-point before snapping to pixels, eliminating +/-1 px rounding drift in text layout. PR #1322: Add early exit to fillUncompressedSizes() once all target entries are matched, avoiding unnecessary central directory traversal. Also updates tracking docs and verifies PR #1329 (reader utils refactor) matches upstream after merge. Made-with: Cursor
33 KiB
Merged Upstream PRs
Tracking document for upstream PRs ported into this mod.
- PR #1038 — Replace std::list with std::vector in text layout (znelson)
- PR #1045 — Shorten "Forget Wifi" button labels (lukestein)
- PR #1037 — Fix hyphenation and rendering of decomposed characters (jpirnay)
- PR #1019 — Display file extensions in File Browser (CaptainFrito)
- PR #1055 — Byte-level framebuffer writes for fillRect and drawLine (jpirnay)
- PR #1027 — Reduce ParsedText layout time 7–9% via word-width cache and hyphenation early exit (jpirnay)
- PR #1068 — Correct hyphenation of URLs (Uri-Tauber)
- PR #1329 — Refactor shared reader utilities (upstream)
- PR #1143 + #1172 — TOC fragment navigation + multi-spine TOC (upstream)
- PR #1311 — Fix inter-word spacing rounding error (znelson)
- PR #1320 — RAII JPEG resource cleanup (upstream)
- PR #1322 — Early exit on fillUncompressedSizes (jpirnay)
- PR #1325 — Dynamic settings tab label (upstream)
PR #1038: Replace std::list with std::vector in text layout
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1038
- Author: znelson (revision of BlindBat's #802)
- Status in upstream: Open (approved by osteotek, awaiting second review from daveallie)
- Method: Manual port (partial -- incremental fixes only)
Context
The core list-to-vector conversion was already performed in a prior mod sync. This port only brings in two targeted fixes from the PR that were missing locally.
Changes applied
- Erase consumed words in
layoutAndExtractLines(lib/Epub/Epub/ParsedText.cpp): Added.erase()calls after the line extraction loop to remove consumed words fromwords,wordStyles,wordContinues, andforceBreakAftervectors. Without this, the 750-word early flush threshold fires on every subsequentaddWordcall instead of only ~3 times per large paragraph, causing ~1,670 redundant flushes. - Fix
wordContinuesflag inhyphenateWordAtIndex(lib/Epub/Epub/ParsedText.cpp): Corrected the attach-to-previous flag handling when splitting a word at a hyphenation point. The prefix now keeps its original flag and the remainder getsfalse, instead of the previous behavior that cleared the prefix's flag and transferred it to the remainder. This fix was identified by coderabbit review and accepted in commit9bbe994.
Differences from upstream PR
- The
forceBreakAftervector erase was added (mod-only vector not present in upstream) alongside the three upstream vectors. - The upstream PR's full list-to-vector conversion (TextBlock.h/.cpp, ParsedText.h/.cpp) was already done in a prior mod sync. Only the two fixes above were new.
Notable PR discussion
- znelson posted detailed benchmarks comparing list vs vector on ESP32-C3 with "Intermezzo" by Sally Rooney (chapter 14, 2,420-word paragraph): 11% faster chapter parse time, 89% more heap headroom (~50KB saved), 10-14% faster layout for medium paragraphs.
- The erase cost is ~90-133 microseconds per flush (0.1% of total flush time).
PR #1045: Shorten "Forget Wifi" button labels to fit on button
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1045
- Author: lukestein
- Status in upstream: Open (approved by osteotek)
- Resolves: upstream issue #1035
- Method: Manual port (direct string changes)
Changes applied
Updated STR_FORGET_BUTTON in all 9 translation yaml files under lib/I18n/translations/:
| Language | Before | After |
|---|---|---|
| Czech | "Zapomenout na síť" | "Zapomenout" |
| English | "Forget network" | "Forget" |
| French | "Oublier le réseau" | "Oublier" |
| German | "WLAN entfernen" | "Entfernen" |
| Portuguese | "Esquecer rede" | "Esquecer" |
| Romanian | "Uitaţi reţeaua" | "Uitaţi" |
| Russian | "Забыть сеть" | "Забыть" |
| Spanish | "Olvidar la red" | "Olvidar" |
| Swedish | "Glöm nätverk" | "Glöm" |
Prerequisite
Romanian translation file (romanian.yaml) was pulled from upstream/master (merged PR #987 by ariel-lindemann) as it was missing locally.
Differences from upstream PR
None. Changes are identical to the upstream PR.
Notable PR discussion
- ariel-lindemann requested the Romanian shortening be included (was added in a follow-up commit by lukestein).
- Translations were verified via Google Translate (lukestein is not a native speaker of non-English languages).
PR #1037: Fix hyphenation and rendering of decomposed characters
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1037
- Author: jpirnay
- Status in upstream: Open (no approvals yet; coderabbit reviewed with one actionable comment, resolved)
- Related to: upstream issue #998 (Minor hyphenation and text flow issues)
- Method: Manual port (4 files)
Changes applied
**lib/Utf8/Utf8.h**: Addedutf8IsCombiningMark(uint32_t)inline function that identifies Unicode combining diacritical marks (ranges: 0x0300-0x036F, 0x1DC0-0x1DFF, 0x20D0-0x20FF, 0xFE20-0xFE2F).**lib/EpdFont/EpdFont.cpp**: Added combining mark positioning ingetTextBounds. Tracks last base glyph state (lastBaseX,lastBaseAdvance,lastBaseTop,hasBaseGlyph). Combining marks are centered over the base glyph's advance width and raised with a minimum 1px gap. Cursor does not advance for combining marks.**lib/Epub/Epub/hyphenation/HyphenationCommon.cpp**: Added NFC-like precomposition incollectCodepoints(). When a combining diacritic follows a base character and a precomposed Latin-1/Latin-Extended scalar exists (grave, acute, circumflex, tilde, diaeresis, cedilla), the base is replaced with the precomposed form and the combining mark is skipped. This allows Liang hyphenation patterns to match decomposed text correctly.**lib/GfxRenderer/GfxRenderer.cpp**: Added combining mark handling todrawText,drawTextRotated90CW,drawTextRotated90CCW, andgetTextAdvanceX. Each text drawing function tracks base glyph state and renders combining marks at adjusted (raised, centered) positions without advancing the cursor.getTextAdvanceXskips combining marks entirely.
Differences from upstream PR
**drawTextRotated90CCW**: This is a mod-only function (not present in upstream). Combining mark handling was added here following the same pattern as the CW rotation, with coordinate adjustments inverted for CCW direction (+raiseByon X,+lastBaseAdvance/2on Y instead of negatives).- The upstream PR initially had a duplicate local
isCombiningMarkfunction inEpdFont.cpp(anonymous namespace) which was flagged by coderabbit and replaced with the sharedutf8IsCombiningMarkfromUtf8.h. Our port uses the shared function directly throughout.
Notable PR discussion
- jpirnay noted this is not a 100% bullet-proof implementation -- it maps common base+combining sequences rather than implementing full Unicode NFC normalization.
- The render fix is more universal: it properly x-centers the compound glyph over the previous one and uses at least 1pt visual distance in Y.
- lukestein specifically expressed interest in the fix for hyphenation of already-hyphenated words (e.g., "US-Satellitensystem" was exclusively broken at the existing hyphen).
- osteotek approved the approach of breaking already-hyphenated words at additional Liang pattern points.
PR #1019: Display file extensions in File Browser
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1019
- Author: CaptainFrito
- Status in upstream: Open (no approvals; Eloren1 asked about long filename behavior)
- Method: Manual port (1 file)
Changes applied
In src/activities/home/MyLibraryActivity.cpp:
- Added
getFileExtension(std::string)helper function near the existinggetFileName. Returns the file extension including the dot (e.g., ".epub"), or empty string for directories or files without extensions. - Updated the
GUI.drawListcall to pass the extension lambda as therowValueparameter andfalseforhighlightValue. ThedrawListsignature already supported these parameters with defaults.
Differences from upstream PR
None. The implementation is functionally identical.
Notable PR discussion
- CaptainFrito questioned whether the multiple lambda approach (each independently indexing into the files array) is optimal, suggesting a single function returning a struct might be better.
- Eloren1 asked how long filenames look with extensions displayed. This remains an open question in the upstream PR. See mod enhancement below.
- The PR was force-pushed to squash commits.
Mod enhancement: Expandable selected row for long filenames
Addresses Eloren1's concern about long filenames. When the selected row's filename overflows the available text width, the row expands to 2 lines with smart text wrapping. The file extension moves to the bottom-right of the expanded area, baseline-aligned with the last text line. Non-selected rows retain single-line truncation. If the filename still overflows after 2 lines, the second line is truncated with "...".
Files modified:
src/components/themes/BaseTheme.cpp: AddedwrapTextToLinesutility function with 3-tier break logic andtruncateWithEllipsishelper. ModifieddrawListto detect selected-row overflow, draw expanded selection highlight at 2x row height, render wrapped title with row-height line spacing, and position the file extension on line 2 (right-aligned).src/components/themes/lyra/LyraTheme.cpp: Same helpers and analogousdrawListmodifications with Lyra-specific styling (rounded-rect selection highlight, icon aligned with line 1, scroll bar awareness).
Design decisions:
- Only the selected/highlighted row expands; other rows with long filenames continue to truncate normally.
- Expansion triggers when the title overflows the value-reduced text width (i.e., when it would be truncated in single-line mode). For titles that overflow the value area but still fit the full width, the expanded row shows the full title on line 1 with the extension on line 2 below.
- 3-tier text wrapping for natural line breaks:
- Preferred delimiters: breaks at " -- ", " - ", en-dash, or em-dash separators (common "Title - Author" naming convention). Uses last occurrence to maximize line 1 content.
- Word boundaries: breaks at last space or hyphen that fits on line 1.
- Character-level fallback: for long unbroken tokens without spaces or hyphens.
- Each wrapped line is placed at the same Y position as a normal list row (row-height spacing between lines), giving natural visual separation that matches the list's vertical rhythm.
- File extension is always positioned on line 2 of the expanded area (right-aligned), regardless of how many text lines are produced by wrapping.
- Icons in LyraTheme are aligned with line 1 (not centered in the expanded area).
- Pagination uses
effectivePageItems(pageItems - 1 when expanding) for totalPages, scroll indicators, and page boundaries. This ensures hidden items appear on the next page with proper scroll indicators. To prevent items from the previous page "leaking" into view, the page start is clamped to never go before the original (non-expanded) page boundary. Edge case: when the selected item is the last on the original page, the start shifts forward minimally to keep it visible. - Boundary item duplication: when navigating to a "real" page boundary (non-expanding path), if the item just before the page start would need expansion when selected, it is included at the top of the current page (the page start is decremented by 1). This prevents the "bumped" item from vanishing when the user navigates from it to the next page. The guard
selectedIndex < pageStartIndex + pageItems - 1ensures the selected item isn't pushed off the page by this adjustment.
PR #1055: Byte-level framebuffer writes for fillRect and axis-aligned drawLine
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1055
- Author: jpirnay
- Status in upstream: Open (coderabbit reviewed, no approvals yet)
- Method: Manual port (2 files)
Context
Eliminates per-pixel drawPixel calls for solid fills, axis-aligned lines, and dithered fills by writing directly to the framebuffer at byte granularity. Exploits the fact that one logical dimension always maps to a contiguous physical row in the framebuffer, allowing entire spans to be written with byte masking and memset instead of individual read-modify-write cycles per pixel. Measured 232–470x speedups on ESP32-C3 hardware for full-screen operations.
Changes applied
lib/GfxRenderer/GfxRenderer.h: Added two new private methods:fillPhysicalHSpanByte(int phyY, int phyX_start, int phyX_end, uint8_t patternByte)for writing a patterned horizontal span with byte-level edge blending, andfillPhysicalHSpan(int phyY, int phyX_start, int phyX_end, bool state)as a thin solid-fill wrapper.lib/GfxRenderer/GfxRenderer.cpp: Added#include <cstring>formemset. ImplementedfillPhysicalHSpanByte(bounds clamping, MSB-first bit packing, partial-byte masking at edges, memset for aligned middle) andfillPhysicalHSpanwrapper.drawLine(axis-aligned cases): Logical vertical lines in Portrait/PortraitInverted now route throughfillPhysicalHSpaninstead of per-pixel loops. Logical horizontal lines in Landscape orientations similarly usefillPhysicalHSpan. Bresenham diagonal path is unchanged.fillRect: Replaced the per-rowdrawLineloop with orientation-specific fast paths. Each orientation iterates over the logical dimension that maps to a constant physical row, writing the perpendicular extent as a singlefillPhysicalHSpancall.fillRectDither: Replaced per-pixeldrawPixelDitherloops for DarkGray and LightGray with orientation-awarefillPhysicalHSpanBytecalls using pre-computed byte patterns. DarkGray uses checkerboard0xAA/0x55alternating by physical row parity. LightGray uses 1-in-4 pattern with row-skipping optimization for all-white rows.
Differences from upstream PR
fillPolygonlandscape optimization (coderabbit nitpick): The upstream PR's coderabbit review noted thatfillPolygon's horizontal scanline inner loop could benefit fromfillPhysicalHSpanfor Landscape orientations. This was noted as a "future optimization opportunity" in the review and not implemented in the PR. We applied it here: forLandscapeCounterClockwiseandLandscapeClockwise, the scanline fill now usesfillPhysicalHSpanwith appropriate coordinate transforms, falling back to per-pixel for Portrait orientations (where horizontal logical lines map to vertical physical lines).
Notable PR discussion
- jpirnay posted hardware benchmarks:
fillRect(480×800)went from 125,577 µs to 519 µs (242× speedup), verticaldrawLine(800px)from 261 µs to 3 µs (87×),fillRectDitherDarkGray 234× speedup, LightGray 470× speedup. - coderabbit review approved all core changes with 8 LGTM comments; the only nitpick was the
fillPolygonoptimization opportunity (applied in this port). - The
fillRectDitherLightGray change has a subtle semantic difference from upstream: the originaldrawPixelDither<LightGray>only wrote dark pixels (leaving white untouched), while the new span-based approach explicitly writes the full pattern. ForfillRectDither(which fills a complete rectangle) this is semantically equivalent.
PR #1027: Reduce ParsedText layout time 7–9% via word-width cache and hyphenation early exit
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1027
- Author: jpirnay
- Status in upstream: Open (no approvals; osteotek requested separate benchmarks for cache vs early exit)
- Method: Manual port (1 file, adapted for vector-based code)
Context
Reduces CPU time spent in ParsedText::layoutAndExtractLines, the hot path for every page render on the ESP32. Two independent optimizations contribute: a direct-mapped word-width cache and an early exit in the hyphenation breakpoint loop. Benchmarked on device (50 iterations, 474 px justified viewport) showing 5–9% improvement depending on corpus and layout mode.
Changes applied
- Word-width cache (
lib/Epub/Epub/ParsedText.cpp): Added a 128-entry, 4 KB static array (BSS, not heap) that cachesgetTextAdvanceXresults keyed by(word, fontId, style). Lookup is O(1) via FNV-1a hash + bitmask slot selection. Words >= 24 bytes bypass the cache (uncommon, unlikely to repeat). Hyphen-fragment measurements (never repeated) skip the cache entirely.calculateWordWidthsnow callscachedMeasureWordWidthinstead ofmeasureWordWidth. - Hyphenation early exit (
lib/Epub/Epub/ParsedText.cpp): InhyphenateWordAtIndex, when a candidate prefix is wider than the available space, the loop nowbreaks instead ofcontinueing.Hyphenator::breakOffsetsreturns candidates in ascending byte-offset order, so prefix widths are non-decreasing -- all subsequent candidates will also be too wide. A reusablestd::string prefixbuffer replaces per-iterationsubstrallocations. - Reserve hint (
lib/Epub/Epub/ParsedText.cpp): AddedlineBreakIndices.reserve(totalWordCount / 8 + 1)incomputeLineBreaksto avoid repeated reallocation.
Differences from upstream PR
- List-specific optimizations not applicable: The upstream PR includes
std::listsplice optimizations inextractLineand iterator changes (std::advancetostd::next) throughout. Our mod already usesstd::vector(from PR #1038), so these changes don't apply -- vector index access and move iterators are already in place. continuesVecsync removed: The upstream PR updates a separatecontinuesVecpointer inhyphenateWordAtIndex. Our mod modifieswordContinuesdirectly (it's already a vector), so this indirection is unnecessary.- Benchmark infrastructure excluded: The PR's final commit removed
ParsedTextLegacy.h/.cpp,ParsedTextBenchmark.h/.cpp, andmain.cppbenchmark hooks. These were development-only files not part of the deliverable.
Notable PR discussion
- osteotek noted he had previously tried a word-width cache with "almost zero on-device improvements" and requested separate benchmarks for each optimization.
- jpirnay posted individual contribution data: the cache dominates (7–8% for DP layout, 3–4% for greedy) while the early exit contributes 1–2%. The cache saves a
getTextAdvanceXcall on every word incalculateWordWidths(56–61 calls/iteration), whereas the early exit only fires on the handful of words per paragraph that trigger hyphenation. - jpirnay's benchmark table (50 iterations, 474 px justified viewport):
| Scenario | Early exit only | Cache only (derived) | Both combined |
|---|---|---|---|
| German DP | −1% | ~−7% | −8% |
| English DP | −1% | ~−8% | −9% |
| Combined DP | −1% | ~−8% | −9% |
| German greedy | −2% | ~−3% | −5% |
| English greedy | −2% | ~−4% | −6% |
| Combined greedy | −2% | ~−3% | −5% |
PR #1068: Correct hyphenation of URLs
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1068
- Author: Uri-Tauber
- Status in upstream: Open (coderabbit reviewed with one nitpick, no approvals yet)
- Related to: upstream issue #1066 (Some epubs crash Crosspoint — long URLs cause rendering issues)
- Method: Manual port (2 files)
Context
Long URLs in EPUBs could not be line-wrapped because buildExplicitBreakInfos required alphabetic characters on both sides of an explicit hyphen marker. URLs contain /, digits, dots, and other non-alphabetic characters, so path separators were never recognized as break points.
Changes applied
lib/Epub/Epub/hyphenation/HyphenationCommon.cpp: Addedcase '/':toisExplicitHyphen, treating the URL path separator as an explicit hyphen delimiter alongside existing hyphen/dash characters.lib/Epub/Epub/hyphenation/Hyphenator.cpp: Split the single combined filter inbuildExplicitBreakInfosinto a two-stage check. TheisExplicitHyphentest is applied first. Then, for/and-specifically, the strict requirement that both adjacent codepoints must be alphabetic is relaxed — these separators can break next to digits, dots, or other URL characters. All other explicit hyphens retain the original TeX-style alphabetic-surround rule.
Differences from upstream PR
- Repeated-separator guard (mod enhancement): Added a guard from the coderabbit review nitpick (not yet addressed in the upstream PR) that skips break points between consecutive identical separators. This prevents a line break from being inserted between the two slashes in
http://or between double hyphens like--.
Notable PR discussion
- coderabbit approved the
isExplicitHyphenchange and flagged the repeated-separator edge case as a nitpick. The PR author has not yet addressed it. - The PR resolves the URL portion of issue #1066, where MIT Press EPUBs with long URLs caused rendering problems.
PR #1329: Refactor reader utils
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1329
- Author: Uri-Tauber
- Status in upstream: MERGED (commit
cd508d2, 2026-03-08). DROP on next sync. - Method: Manual port (3 files)
Context
Both EpubReaderActivity and TxtReaderActivity contained duplicated logic for orientation switching, page-turn detection, refresh cycling, and grayscale anti-aliasing. This PR extracts shared reader utilities into a new ReaderUtils.h header.
Changes applied
src/activities/reader/ReaderUtils.h(new): Created namespaceReaderUtilscontainingGO_HOME_MS,applyOrientation(),PageTurnResult+detectPageTurn(),displayWithRefreshCycle(), andrenderAntiAliased()(template to avoidstd::functionoverhead). IncludesstoreBwBuffer()null-check from CodeRabbit review.src/activities/reader/EpubReaderActivity.cpp: Replaced localapplyReaderOrientation(),goHomeMs, manual page-turn detection, and refresh cycling withReaderUtils::equivalents.src/activities/reader/TxtReaderActivity.cpp: Same delegation toReaderUtils::, removing duplicated orientation switch, page-turn detection, and anti-aliasing code.
Differences from upstream PR
- CodeRabbit fix applied: Added
storeBwBuffer()return value check inrenderAntiAliased()that upstream's draft doesn't yet include. - Mod's
applyOrientationpreserved: The mod'sEpubReaderActivity::applyOrientation()method (which handles settings persistence and section reset) was kept and internally callsReaderUtils::applyOrientation()for the renderer orientation change.
PR #1143 + #1172: TOC fragment navigation + multi-spine TOC
- URL (1143): https://github.com/crosspoint-reader/crosspoint-reader/pull/1143
- URL (1172): https://github.com/crosspoint-reader/crosspoint-reader/pull/1172
- Author: upstream
- Status in upstream: Both open (drafts)
- Method: Surgical feature extraction (7 files modified)
Context
Many EPUBs have spine files containing multiple TOC chapters (e.g., short story collections, academic texts). The status bar showed incorrect chapter titles, chapter skip jumped entire spine items instead of TOC entries, and cross-spine chapter transitions required full re-indexing. These PRs add TOC-aware boundary tracking and multi-spine section caching.
Changes applied
lib/Epub/Epub/Section.h: AddedTocBoundarystruct,tocBoundariesvector,buildTocBoundaries(),getTocIndexForPage(),getPageForTocIndex(),getPageRangeForTocIndex(),readAnchorMap(),readCachedPageCount().lib/Epub/Epub/Section.cpp: Implemented all new TOC boundary methods. IncrementedSECTION_FILE_VERSIONfrom 18 to 19. IntegratedbuildTocBoundaries()into bothloadSectionFileandcreateSectionFile. Added staticreadAnchorMap()for lightweight section probing.lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h/.cpp: AddedtocAnchorsset andtocAnchorPageMap. Constructor acceptstocAnchorsparameter. Forced page breaks at TOC anchor boundaries instartNewTextBlockso chapters start on fresh pages.src/activities/ActivityResult.h: AddedtocIndextoChapterResult.src/activities/reader/EpubReaderActivity.h/.cpp: AddedpendingTocIndexfor deferred cross-spine TOC navigation. Chapter skip (long-press) now walks TOC entries. Status bar usessection->getTocIndexForPage()for accurate subchapter title. AddedcacheMultiSpineChapter()for proactive indexing of all spines in the same TOC chapter.src/activities/reader/EpubReaderChapterSelectionActivity.h/.cpp: AddedcurrentTocIndexparameter for precise pre-positioning. ReturnstocIndexalongsidespineIndexinChapterResult.
Differences from upstream PRs
- Mod's footnote support preserved: Upstream removed footnote navigation; the mod's
getPageForAnchor(), footnote depth tracking, andEpubReaderFootnotesActivityare all retained. - Mod's image rendering preserved: Upstream removed image rendering options; the mod's
imageRenderingparameter chain is preserved throughout Section, parser, and settings. - Activity base class retained: Upstream adopted
ActivityWithSubactivity; the mod keepsActivityas the base class for reader activities. - No
STR_INDEXING_PROGRESSkey: Instead of adding a new translation key, the progress popup reusestr(STR_INDEXING)withsnprintfto append the numeric(x/y)progress, avoiding changes to 17+ YAML files. - Section file version: Mod increments from v18 to v19 (upstream from v13 to v14). The mod's higher version reflects additional fields from previous mod-exclusive changes.
PR #1320: JPEG resource cleanup
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1320
- Author: upstream
- Status in upstream: Open
- Method: Manual port (1 file)
Context
JpegToBmpConverter::convert() had scattered free()/delete calls across multiple early-return paths, making it prone to resource leaks on error.
Changes applied
lib/JpegToBmpConverter/JpegToBmpConverter.cpp: IntroducedScopedCleanupRAII struct that managesrowBuffer,mcuRowBuffer,atkinsonDitherer,fsDitherer,atkinson1BitDitherer,rowAccum, androwCount. All pointers are initialized tonullptrand freed in the destructor. Removed scattered manualfree()/deletecalls. ChangedrowCountfromuint16_t*touint32_t*to prevent overflow for wide images.
Differences from upstream PR
- None -- clean port. The mod's
JpegToBmpConverter.cpphad the same structure as upstream's pre-PR version.
PR #1325: Settings tab label
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1325
- Author: upstream
- Status in upstream: Open
- Method: Manual port (1 file)
Context
The settings screen's "Confirm" button showed a hardcoded "Toggle" label even when the tab bar was focused, where it should indicate the next category name.
Changes applied
src/activities/settings/SettingsActivity.cpp: WhenselectedSettingIndex == 0(tab bar focused), the confirm button now shows the next category's name usingI18N.get(categoryNames[(selectedCategoryIndex + 1) % categoryCount]). Otherwise, it shows the standardtr(STR_TOGGLE)label.
Differences from upstream PR
- None -- clean port.
PR #1311: Fix inter-word spacing rounding error
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1311
- Author: znelson
- Status in upstream: Open (approved by jdk2pq, awaiting second review from osteotek)
- Method: Manual port (4 files)
Context
Inter-word gap widths were computed as two separately-snapped integers: fp4::toPixel(spaceAdvance) + fp4::toPixel(kernSum). Because fp4::toPixel(a) + fp4::toPixel(b) can differ from fp4::toPixel(a + b) by +/-1 pixel when fractional parts straddle a rounding boundary, each inter-word space could be one pixel wider or narrower than the correct value, affecting line-break width decisions and word-position accumulation.
Changes applied
lib/GfxRenderer/GfxRenderer.h: ReplacedgetSpaceKernAdjust()declaration withgetSpaceAdvance(), which returns the full inter-word space advance (space glyph advance + both flanking kern values) combined in fixed-point before a single pixel snap.lib/GfxRenderer/GfxRenderer.cpp: ReplacedgetSpaceKernAdjust()implementation withgetSpaceAdvance()that retrieves the space glyph advance (12.4 FP), adds kern(leftCp, ' ') and kern(' ', rightCp) (4.4 FP), then snaps the combined sum once viafp4::toPixel().getSpaceWidth()is retained for the single-space-word case inmeasureWordWidth.lib/Epub/Epub/ParsedText.h: RemovedspaceWidthparameter fromcomputeLineBreaks,computeHyphenatedLineBreaks, andextractLine.lib/Epub/Epub/ParsedText.cpp: Updated all four call sites (computeLineBreaks,computeHyphenatedLineBreaks, and both gap-calculation loops inextractLine) to userenderer.getSpaceAdvance()instead ofspaceWidth + renderer.getSpaceKernAdjust(). RemovedspaceWidthpre-computation fromlayoutAndExtractLines.
Differences from upstream PR
- Mod's vector-based ParsedText: The mod's
ParsedText.cppusesstd::vector(from #1038 port) and has additional vectors (forceBreakAfter,wordContinues). The spacing replacement is structurally identical at each call site. - Word-width cache preserved: The mod's word-width cache (from #1027 port) is unaffected by this change since it caches
getTextAdvanceXresults, not space widths.
Notable PR discussion
- jdk2pq tested on device and confirmed working.
- The single-snap pattern matches what
getTextAdvanceXalready uses for intra-word glyph advances, making inter-word spacing consistent with word-width measurement.
PR #1322: Early exit on fillUncompressedSizes
- URL: https://github.com/crosspoint-reader/crosspoint-reader/pull/1322
- Author: jpirnay
- Status in upstream: MERGED (commit
e60ba76, 2026-03-08). DROP on next sync. - Method: Manual port (1 file)
Context
fillUncompressedSizes scanned the entire ZIP central directory even when all requested target entries had already been matched, wasting time on large EPUB files with many entries.
Changes applied
lib/ZipFile/ZipFile.cpp: AddedtargetCountvariable before the scan loop and abreakafter the inner match loop whenmatched >= targetCount, terminating the central-directory scan early.
Differences from upstream PR
- None -- identical to upstream commit
e60ba76.
Notable PR discussion
- CodeRabbit flagged a theoretical duplicate-match overcount issue, but znelson and ngxson approved as-is since duplicate central-directory filenames are not expected in valid EPUB/ZIP files.