MCPcopy Create free account
hub / github.com/crosspoint-reader/crosspoint-reader / render

Method render

lib/Epub/Epub/blocks/TextBlock.cpp:10–74  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

8#include <cstring>
9
10void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int x, const int y) const {
11 // Focus annotations are optional: empty vectors mean no word in this block has a split.
12 // When present, they must be sized in lockstep with words[].
13 const bool hasFocus = !wordFocusBoundary.empty();
14 if (words.size() != wordXpos.size() || words.size() != wordStyles.size() ||
15 (hasFocus && (words.size() != wordFocusBoundary.size() || words.size() != wordFocusSuffixX.size()))) {
16 LOG_ERR("TXB", "Render skipped: size mismatch (words=%u, xpos=%u, styles=%u, boundary=%u, suffixX=%u)\n",
17 (uint32_t)words.size(), (uint32_t)wordXpos.size(), (uint32_t)wordStyles.size(),
18 (uint32_t)wordFocusBoundary.size(), (uint32_t)wordFocusSuffixX.size());
19 return;
20 }
21
22 const bool scanning = renderer.isFontCacheScanning();
23 const int ascender = renderer.getFontAscenderSize(fontId);
24 for (size_t i = 0; i < words.size(); i++) {
25 const int wordX = wordXpos[i] + x;
26 const EpdFontFamily::Style currentStyle = wordStyles[i];
27 const auto baseDir = static_cast<BidiUtils::BidiBaseDir>(
28 BidiUtils::detectParagraphLevel(words[i].c_str(), blockStyle.isRtl ? 1 : 0));
29 const uint8_t boundary = hasFocus ? wordFocusBoundary[i] : 0;
30
31 // SUP/SUB shift the baseline passed to drawText; the glyph is also scaled 50% inside
32 // drawText, so these offsets are chosen relative to the full-size ascender:
33 // SUP: raise by 40% of ascender — sits clearly above the cap-height
34 // SUB: lower by 25% of ascender — descends below baseline without clashing with ascenders below
35 int wordY = y;
36 if ((currentStyle & EpdFontFamily::SUP) != 0) {
37 wordY -= ascender * 2 / 5;
38 } else if ((currentStyle & EpdFontFamily::SUB) != 0) {
39 wordY += ascender / 4;
40 }
41
42 if (boundary > 0) {
43 // Focus split: draw bold prefix, then the regular suffix at a pre-computed x offset.
44 // The bold prefix is bounded to 9 codepoints by the clamp on targetBoldChars in
45 // ParsedText::addWord; 9 UTF-8 codepoints occupy at most 9 * 4 = 36 bytes, +1 for null = 37.
46 // suffixX is computed at cache-creation time to avoid font metric lookups at render time.
47 static constexpr size_t MAX_FOCUS_PREFIX_BYTES = 9 * 4 + 1;
48 char boldBuf[40];
49 static_assert(sizeof(boldBuf) >= MAX_FOCUS_PREFIX_BYTES,
50 "boldBuf too small for max focus prefix (9 codepoints * 4 UTF-8 bytes + null)");
51 const auto boldStyle = static_cast<EpdFontFamily::Style>(currentStyle | EpdFontFamily::BOLD);
52 const size_t boldLen = std::min<size_t>({static_cast<size_t>(boundary), words[i].size(), sizeof(boldBuf) - 1});
53 memcpy(boldBuf, words[i].c_str(), boldLen);
54 boldBuf[boldLen] = '\0';
55 renderer.drawText(fontId, wordX, wordY, boldBuf, true, boldStyle, baseDir);
56 const int suffixX = wordX + wordFocusSuffixX[i];
57 renderer.drawText(fontId, suffixX, wordY, words[i].c_str() + boldLen, true, currentStyle, baseDir);
58 } else {
59 renderer.drawText(fontId, wordX, wordY, words[i].c_str(), true, currentStyle, baseDir);
60 }
61
62 if (!scanning && (currentStyle & EpdFontFamily::UNDERLINE) != 0) {
63 const std::string& w = words[i];
64 int underlineWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle, baseDir);
65 const int underlineY = wordY + ascender + 2;
66
67 if ((currentStyle & (EpdFontFamily::SUP | EpdFontFamily::SUB)) != 0) {

Callers

nothing calls this directly

Calls 8

detectParagraphLevelFunction · 0.85
isFontCacheScanningMethod · 0.80
getFontAscenderSizeMethod · 0.80
drawTextMethod · 0.80
getTextWidthMethod · 0.80
drawLineMethod · 0.80
emptyMethod · 0.45
sizeMethod · 0.45

Tested by

no test coverage detected