## Summary **What is the goal of this PR?** Consolidated repeated logic to fall back to REPLACEMENT_GLYPH. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_
98 lines
2.7 KiB
C++
98 lines
2.7 KiB
C++
#include "EpdFont.h"
|
|
|
|
#include <Utf8.h>
|
|
|
|
#include <algorithm>
|
|
|
|
void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX,
|
|
int* maxY) const {
|
|
*minX = startX;
|
|
*minY = startY;
|
|
*maxX = startX;
|
|
*maxY = startY;
|
|
|
|
if (*string == '\0') {
|
|
return;
|
|
}
|
|
|
|
int cursorX = startX;
|
|
const int cursorY = startY;
|
|
int lastBaseX = startX;
|
|
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**>(&string)))) {
|
|
const EpdGlyph* glyph = getGlyph(cp);
|
|
if (!glyph) {
|
|
// TODO: Better handle this?
|
|
continue;
|
|
}
|
|
|
|
const bool isCombining = utf8IsCombiningMark(cp);
|
|
int raiseBy = 0;
|
|
if (isCombining && hasBaseGlyph) {
|
|
const int currentGap = glyph->top - glyph->height - lastBaseTop;
|
|
if (currentGap < MIN_COMBINING_GAP_PX) {
|
|
raiseBy = MIN_COMBINING_GAP_PX - currentGap;
|
|
}
|
|
}
|
|
|
|
const int glyphBaseX = (isCombining && hasBaseGlyph) ? (lastBaseX + lastBaseAdvance / 2) : cursorX;
|
|
const int glyphBaseY = cursorY - raiseBy;
|
|
|
|
*minX = std::min(*minX, glyphBaseX + glyph->left);
|
|
*maxX = std::max(*maxX, glyphBaseX + glyph->left + glyph->width);
|
|
*minY = std::min(*minY, glyphBaseY + glyph->top - glyph->height);
|
|
*maxY = std::max(*maxY, glyphBaseY + glyph->top);
|
|
|
|
if (!isCombining) {
|
|
lastBaseX = cursorX;
|
|
lastBaseAdvance = glyph->advanceX;
|
|
lastBaseTop = glyph->top;
|
|
hasBaseGlyph = true;
|
|
cursorX += glyph->advanceX;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EpdFont::getTextDimensions(const char* string, int* w, int* h) const {
|
|
int minX = 0, minY = 0, maxX = 0, maxY = 0;
|
|
|
|
getTextBounds(string, 0, 0, &minX, &minY, &maxX, &maxY);
|
|
|
|
*w = maxX - minX;
|
|
*h = maxY - minY;
|
|
}
|
|
|
|
const EpdGlyph* EpdFont::getGlyph(const uint32_t cp) const {
|
|
const EpdUnicodeInterval* intervals = data->intervals;
|
|
const int count = data->intervalCount;
|
|
|
|
if (count == 0) return nullptr;
|
|
|
|
// Binary search for O(log n) lookup instead of O(n)
|
|
// Critical for Korean fonts with many unicode intervals
|
|
int left = 0;
|
|
int right = count - 1;
|
|
|
|
while (left <= right) {
|
|
const int mid = left + (right - left) / 2;
|
|
const EpdUnicodeInterval* interval = &intervals[mid];
|
|
|
|
if (cp < interval->first) {
|
|
right = mid - 1;
|
|
} else if (cp > interval->last) {
|
|
left = mid + 1;
|
|
} else {
|
|
// Found: cp >= interval->first && cp <= interval->last
|
|
return &data->glyph[interval->offset + (cp - interval->first)];
|
|
}
|
|
}
|
|
if (cp != REPLACEMENT_GLYPH) {
|
|
return getGlyph(REPLACEMENT_GLYPH);
|
|
}
|
|
return nullptr;
|
|
}
|