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

Method addWord

lib/Epub/Epub/ParsedText.cpp:253–448  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

251} // namespace
252
253void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle, const bool underline,
254 const bool attachToPrevious) {
255 if (word.empty()) return;
256
257 // The device fonts carry no combining-mark positioning, so EPUB text stored in NFD
258 // (a base letter followed by separate combining accents -- common for Vietnamese,
259 // and used for many EPUB <h1> chapter headings) renders with the marks detached or
260 // misplaced. Compose to NFC here, the single funnel every word passes through, so a
261 // precomposed glyph is used instead. This runs once per word at layout time (the
262 // result is cached in the section file) and is a cheap no-op for mark-free text.
263 word = utf8ComposeNfc(word);
264
265 EpdFontFamily::Style baseStyle = fontStyle;
266 if (underline) {
267 baseStyle = static_cast<EpdFontFamily::Style>(baseStyle | EpdFontFamily::UNDERLINE);
268 }
269 const bool wordStartsRtl = !hasRtlWord && mayContainRtlBytes(word.c_str()) &&
270 BidiUtils::startsWithRtl(word.c_str(), RTL_PER_WORD_PROBE_DEPTH);
271
272 const auto pushToken = [&](std::string token, const bool continues, const bool noSpaceBefore,
273 const bool isFocusSuffix) {
274 words.push_back(std::move(token));
275 wordStyles.push_back(baseStyle);
276 wordContinues.push_back(continues);
277 wordNoSpaceBefore.push_back(noSpaceBefore);
278 wordIsFocusSuffix.push_back(isFocusSuffix);
279 };
280
281 bool effectiveAttachToPrevious = attachToPrevious;
282 bool effectiveNoSpaceBefore = false;
283 if (attachToPrevious && !words.empty() &&
284 hasCjkBreakOpportunityBetween(lastCodepoint(words.back()), firstCodepoint(word))) {
285 effectiveAttachToPrevious = false;
286 effectiveNoSpaceBefore = true;
287 }
288
289 if (auto breakOffsets = cjkCharacterBreakByteOffsets(word); !breakOffsets.empty()) {
290 bool firstToken = true;
291 size_t tokenStart = 0;
292 for (const size_t breakOffset : breakOffsets) {
293 if (breakOffset <= tokenStart || breakOffset > word.size()) continue;
294 pushToken(word.substr(tokenStart, breakOffset - tokenStart), firstToken ? effectiveAttachToPrevious : false,
295 firstToken ? effectiveNoSpaceBefore : true, false);
296 firstToken = false;
297 tokenStart = breakOffset;
298 }
299 if (tokenStart < word.size()) {
300 pushToken(word.substr(tokenStart), firstToken ? effectiveAttachToPrevious : false,
301 firstToken ? effectiveNoSpaceBefore : true, false);
302 }
303 if (wordStartsRtl) {
304 hasRtlWord = true;
305 }
306 return;
307 }
308
309 if (containsCjkBreakableCodepoint(word)) {
310 pushToken(std::move(word), effectiveAttachToPrevious, effectiveNoSpaceBefore, false);

Callers 2

flushPartWordBufferMethod · 0.80
startElementMethod · 0.80

Calls 13

utf8ComposeNfcFunction · 0.85
mayContainRtlBytesFunction · 0.85
startsWithRtlFunction · 0.85
lastCodepointFunction · 0.85
firstCodepointFunction · 0.85
utf8NextCodepointFunction · 0.85
isWordCharacterFunction · 0.85
push_backMethod · 0.80
emptyMethod · 0.45

Tested by

no test coverage detected