fix: Port upstream PRs #1038, #1037, #1045, #1019

- #1038 (partial): Add .erase() for consumed words in layoutAndExtractLines
  to fix redundant early flush bug; fix wordContinues flag in hyphenateWordAtIndex
- #1037: Add combining mark handling for hyphenation (NFC-like precomposition)
  and rendering (base glyph tracking in EpdFont, GfxRenderer including CCW)
- #1045: Shorten STR_FORGET_BUTTON labels across all 9 translation files
- #1019: Display file extensions in File Browser via getFileExtension helper
- Pull romanian.yaml from upstream/master (merged PR #987)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-02-20 16:27:59 -05:00
parent 55a1fef01a
commit 406c3aeace
17 changed files with 725 additions and 25 deletions

View File

@@ -174,12 +174,14 @@ static void renderCharImpl(const GfxRenderer& renderer, GfxRenderer::RenderMode
}
}
if constexpr (rotation == TextRotation::Rotated90CW) {
*cursorY -= glyph->advanceX;
} else if constexpr (rotation == TextRotation::Rotated90CCW) {
*cursorY += glyph->advanceX;
} else {
*cursorX += glyph->advanceX;
if (!utf8IsCombiningMark(cp)) {
if constexpr (rotation == TextRotation::Rotated90CW) {
*cursorY -= glyph->advanceX;
} else if constexpr (rotation == TextRotation::Rotated90CCW) {
*cursorY += glyph->advanceX;
} else {
*cursorX += glyph->advanceX;
}
}
}
@@ -241,6 +243,11 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
const EpdFontFamily::Style style) const {
int yPos = y + getFontAscenderSize(fontId);
int xpos = x;
int lastBaseX = x;
int lastBaseY = yPos;
int lastBaseAdvance = 0;
int lastBaseTop = 0;
bool hasBaseGlyph = false;
// cannot draw a NULL / empty string
if (text == nullptr || *text == '\0') {
@@ -253,9 +260,43 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
return;
}
const auto& font = fontIt->second;
constexpr int MIN_COMBINING_GAP_PX = 1;
uint32_t cp;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
if (utf8IsCombiningMark(cp) && hasBaseGlyph) {
const EpdGlyph* combiningGlyph = font.getGlyph(cp, style);
if (!combiningGlyph) {
combiningGlyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
int raiseBy = 0;
if (combiningGlyph) {
const int currentGap = combiningGlyph->top - combiningGlyph->height - lastBaseTop;
if (currentGap < MIN_COMBINING_GAP_PX) {
raiseBy = MIN_COMBINING_GAP_PX - currentGap;
}
}
int combiningX = lastBaseX + lastBaseAdvance / 2;
int combiningY = lastBaseY - raiseBy;
renderChar(font, cp, &combiningX, &combiningY, black, style);
continue;
}
const EpdGlyph* glyph = font.getGlyph(cp, style);
if (!glyph) {
glyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
if (!utf8IsCombiningMark(cp)) {
lastBaseX = xpos;
lastBaseY = yPos;
lastBaseAdvance = glyph ? glyph->advanceX : 0;
lastBaseTop = glyph ? glyph->top : 0;
hasBaseGlyph = true;
}
renderChar(font, cp, &xpos, &yPos, black, style);
}
}
@@ -963,6 +1004,9 @@ int GfxRenderer::getTextAdvanceX(const int fontId, const char* text, const EpdFo
int width = 0;
const auto& font = fontIt->second;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
if (utf8IsCombiningMark(cp)) {
continue;
}
const EpdGlyph* glyph = font.getGlyph(cp, style);
if (!glyph) glyph = font.getGlyph(REPLACEMENT_GLYPH, style);
if (glyph) width += glyph->advanceX;
@@ -1016,9 +1060,48 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
int xPos = x;
int yPos = y;
int lastBaseX = x;
int lastBaseY = y;
int lastBaseAdvance = 0;
int lastBaseTop = 0;
bool hasBaseGlyph = false;
constexpr int MIN_COMBINING_GAP_PX = 1;
uint32_t cp;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
if (utf8IsCombiningMark(cp) && hasBaseGlyph) {
const EpdGlyph* combiningGlyph = font.getGlyph(cp, style);
if (!combiningGlyph) {
combiningGlyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
int raiseBy = 0;
if (combiningGlyph) {
const int currentGap = combiningGlyph->top - combiningGlyph->height - lastBaseTop;
if (currentGap < MIN_COMBINING_GAP_PX) {
raiseBy = MIN_COMBINING_GAP_PX - currentGap;
}
}
int combiningX = lastBaseX - raiseBy;
int combiningY = lastBaseY - lastBaseAdvance / 2;
renderCharImpl<TextRotation::Rotated90CW>(*this, renderMode, font, cp, &combiningX, &combiningY, black, style);
continue;
}
const EpdGlyph* glyph = font.getGlyph(cp, style);
if (!glyph) {
glyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
if (!utf8IsCombiningMark(cp)) {
lastBaseX = xPos;
lastBaseY = yPos;
lastBaseAdvance = glyph ? glyph->advanceX : 0;
lastBaseTop = glyph ? glyph->top : 0;
hasBaseGlyph = true;
}
renderCharImpl<TextRotation::Rotated90CW>(*this, renderMode, font, cp, &xPos, &yPos, black, style);
}
}
@@ -1040,9 +1123,48 @@ void GfxRenderer::drawTextRotated90CCW(const int fontId, const int x, const int
int xPos = x;
int yPos = y;
int lastBaseX = x;
int lastBaseY = y;
int lastBaseAdvance = 0;
int lastBaseTop = 0;
bool hasBaseGlyph = false;
constexpr int MIN_COMBINING_GAP_PX = 1;
uint32_t cp;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
if (utf8IsCombiningMark(cp) && hasBaseGlyph) {
const EpdGlyph* combiningGlyph = font.getGlyph(cp, style);
if (!combiningGlyph) {
combiningGlyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
int raiseBy = 0;
if (combiningGlyph) {
const int currentGap = combiningGlyph->top - combiningGlyph->height - lastBaseTop;
if (currentGap < MIN_COMBINING_GAP_PX) {
raiseBy = MIN_COMBINING_GAP_PX - currentGap;
}
}
int combiningX = lastBaseX + raiseBy;
int combiningY = lastBaseY + lastBaseAdvance / 2;
renderCharImpl<TextRotation::Rotated90CCW>(*this, renderMode, font, cp, &combiningX, &combiningY, black, style);
continue;
}
const EpdGlyph* glyph = font.getGlyph(cp, style);
if (!glyph) {
glyph = font.getGlyph(REPLACEMENT_GLYPH, style);
}
if (!utf8IsCombiningMark(cp)) {
lastBaseX = xPos;
lastBaseY = yPos;
lastBaseAdvance = glyph ? glyph->advanceX : 0;
lastBaseTop = glyph ? glyph->top : 0;
hasBaseGlyph = true;
}
renderCharImpl<TextRotation::Rotated90CCW>(*this, renderMode, font, cp, &xPos, &yPos, black, style);
}
}