feat: Overhaul font format into CrossPoint font

This commit is contained in:
Dave Allie
2026-01-28 23:30:39 +11:00
parent da4d3b5ea5
commit 567fa6e1e2
142 changed files with 254312 additions and 263366 deletions

View File

@@ -30,8 +30,8 @@ void stripSoftHyphensInPlace(std::string& word) {
}
// Returns the rendered width for a word while ignoring soft hyphen glyphs and optionally appending a visible hyphen.
uint16_t measureWordWidth(const GfxRenderer& renderer, const int fontId, const std::string& word,
const EpdFontFamily::Style style, const bool appendHyphen = false) {
uint16_t measureWordWidth(GfxRenderer& renderer, const int fontId, const std::string& word,
const CrossPointFont::Style style, const bool appendHyphen = false) {
const bool hasSoftHyphen = containsSoftHyphen(word);
if (!hasSoftHyphen && !appendHyphen) {
return renderer.getTextWidth(fontId, word.c_str(), style);
@@ -49,7 +49,7 @@ uint16_t measureWordWidth(const GfxRenderer& renderer, const int fontId, const s
} // namespace
void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle) {
void ParsedText::addWord(std::string word, const CrossPointFont::Style fontStyle) {
if (word.empty()) return;
words.push_back(std::move(word));
@@ -57,7 +57,7 @@ void ParsedText::addWord(std::string word, const EpdFontFamily::Style fontStyle)
}
// Consumes data to minimize memory usage
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const uint16_t viewportWidth,
void ParsedText::layoutAndExtractLines(GfxRenderer& renderer, const int fontId, const uint16_t viewportWidth,
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
const bool includeLastLine) {
if (words.empty()) {
@@ -84,7 +84,7 @@ void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fo
}
}
std::vector<uint16_t> ParsedText::calculateWordWidths(const GfxRenderer& renderer, const int fontId) {
std::vector<uint16_t> ParsedText::calculateWordWidths(GfxRenderer& renderer, const int fontId) {
const size_t totalWordCount = words.size();
std::vector<uint16_t> wordWidths;
@@ -103,7 +103,7 @@ std::vector<uint16_t> ParsedText::calculateWordWidths(const GfxRenderer& rendere
return wordWidths;
}
std::vector<size_t> ParsedText::computeLineBreaks(const GfxRenderer& renderer, const int fontId, const int pageWidth,
std::vector<size_t> ParsedText::computeLineBreaks(GfxRenderer& renderer, const int fontId, const int pageWidth,
const int spaceWidth, std::vector<uint16_t>& wordWidths) {
if (words.empty()) {
return {};
@@ -206,7 +206,7 @@ void ParsedText::applyParagraphIndent() {
}
// Builds break indices while opportunistically splitting the word that would overflow the current line.
std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& renderer, const int fontId,
std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(GfxRenderer& renderer, const int fontId,
const int pageWidth, const int spaceWidth,
std::vector<uint16_t>& wordWidths) {
std::vector<size_t> lineBreakIndices;
@@ -257,7 +257,7 @@ std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& r
// Splits words[wordIndex] into prefix (adding a hyphen only when needed) and remainder when a legal breakpoint fits the
// available width.
bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availableWidth, const GfxRenderer& renderer,
bool ParsedText::hyphenateWordAtIndex(const size_t wordIndex, const int availableWidth, GfxRenderer& renderer,
const int fontId, std::vector<uint16_t>& wordWidths,
const bool allowFallbackBreaks) {
// Guard against invalid indices or zero available width before attempting to split.
@@ -375,7 +375,7 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
// *** CRITICAL STEP: CONSUME DATA USING SPLICE ***
std::list<std::string> lineWords;
lineWords.splice(lineWords.begin(), words, words.begin(), wordEndIt);
std::list<EpdFontFamily::Style> lineWordStyles;
std::list<CrossPointFont::Style> lineWordStyles;
lineWordStyles.splice(lineWordStyles.begin(), wordStyles, wordStyles.begin(), wordStyleEndIt);
for (auto& word : lineWords) {
@@ -385,4 +385,4 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
}
processLine(std::make_shared<TextBlock>(std::move(lineWords), std::move(lineXPos), std::move(lineWordStyles), style));
}
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <EpdFontFamily.h>
#include <CrossPointFont.h>
#include <functional>
#include <list>
@@ -14,22 +14,22 @@ class GfxRenderer;
class ParsedText {
std::list<std::string> words;
std::list<EpdFontFamily::Style> wordStyles;
std::list<CrossPointFont::Style> wordStyles;
TextBlock::Style style;
bool extraParagraphSpacing;
bool hyphenationEnabled;
void applyParagraphIndent();
std::vector<size_t> computeLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth, int spaceWidth,
std::vector<size_t> computeLineBreaks(GfxRenderer& renderer, int fontId, int pageWidth, int spaceWidth,
std::vector<uint16_t>& wordWidths);
std::vector<size_t> computeHyphenatedLineBreaks(const GfxRenderer& renderer, int fontId, int pageWidth,
int spaceWidth, std::vector<uint16_t>& wordWidths);
bool hyphenateWordAtIndex(size_t wordIndex, int availableWidth, const GfxRenderer& renderer, int fontId,
std::vector<size_t> computeHyphenatedLineBreaks(GfxRenderer& renderer, int fontId, int pageWidth, int spaceWidth,
std::vector<uint16_t>& wordWidths);
bool hyphenateWordAtIndex(size_t wordIndex, int availableWidth, GfxRenderer& renderer, int fontId,
std::vector<uint16_t>& wordWidths, bool allowFallbackBreaks);
void extractLine(size_t breakIndex, int pageWidth, int spaceWidth, const std::vector<uint16_t>& wordWidths,
const std::vector<size_t>& lineBreakIndices,
const std::function<void(std::shared_ptr<TextBlock>)>& processLine);
std::vector<uint16_t> calculateWordWidths(const GfxRenderer& renderer, int fontId);
std::vector<uint16_t> calculateWordWidths(GfxRenderer& renderer, int fontId);
public:
explicit ParsedText(const TextBlock::Style style, const bool extraParagraphSpacing,
@@ -37,12 +37,12 @@ class ParsedText {
: style(style), extraParagraphSpacing(extraParagraphSpacing), hyphenationEnabled(hyphenationEnabled) {}
~ParsedText() = default;
void addWord(std::string word, EpdFontFamily::Style fontStyle);
void addWord(std::string word, CrossPointFont::Style fontStyle);
void setStyle(const TextBlock::Style style) { this->style = style; }
TextBlock::Style getStyle() const { return style; }
size_t size() const { return words.size(); }
bool isEmpty() const { return words.empty(); }
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
void layoutAndExtractLines(GfxRenderer& renderer, int fontId, uint16_t viewportWidth,
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
bool includeLastLine = true);
};
};

View File

@@ -3,7 +3,7 @@
#include <GfxRenderer.h>
#include <Serialization.h>
void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int x, const int y) const {
void TextBlock::render(GfxRenderer& renderer, const int fontId, const int x, const int y) const {
// Validate iterator bounds before rendering
if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) {
Serial.printf("[%lu] [TXB] Render skipped: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(),
@@ -47,7 +47,7 @@ std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
uint16_t wc;
std::list<std::string> words;
std::list<uint16_t> wordXpos;
std::list<EpdFontFamily::Style> wordStyles;
std::list<CrossPointFont::Style> wordStyles;
Style style;
// Word count

View File

@@ -1,5 +1,5 @@
#pragma once
#include <EpdFontFamily.h>
#include <CrossPointFont.h>
#include <SdFat.h>
#include <list>
@@ -21,12 +21,12 @@ class TextBlock final : public Block {
private:
std::list<std::string> words;
std::list<uint16_t> wordXpos;
std::list<EpdFontFamily::Style> wordStyles;
std::list<CrossPointFont::Style> wordStyles;
Style style;
public:
explicit TextBlock(std::list<std::string> words, std::list<uint16_t> word_xpos,
std::list<EpdFontFamily::Style> word_styles, const Style style)
std::list<CrossPointFont::Style> word_styles, const Style style)
: words(std::move(words)), wordXpos(std::move(word_xpos)), wordStyles(std::move(word_styles)), style(style) {}
~TextBlock() override = default;
void setStyle(const Style style) { this->style = style; }
@@ -34,7 +34,7 @@ class TextBlock final : public Block {
bool isEmpty() override { return words.empty(); }
void layout(GfxRenderer& renderer) override {};
// given a renderer works out where to break the words into lines
void render(const GfxRenderer& renderer, int fontId, int x, int y) const;
void render(GfxRenderer& renderer, int fontId, int x, int y) const;
BlockType getType() override { return TEXT_BLOCK; }
bool serialize(FsFile& file) const;
static std::unique_ptr<TextBlock> deserialize(FsFile& file);

View File

@@ -43,13 +43,13 @@ bool matches(const char* tag_name, const char* possible_tags[], const int possib
// flush the contents of partWordBuffer to currentTextBlock
void ChapterHtmlSlimParser::flushPartWordBuffer() {
// determine font style
EpdFontFamily::Style fontStyle = EpdFontFamily::REGULAR;
CrossPointFont::Style fontStyle = CrossPointFont::REGULAR;
if (boldUntilDepth < depth && italicUntilDepth < depth) {
fontStyle = EpdFontFamily::BOLD_ITALIC;
fontStyle = CrossPointFont::BOLD_ITALIC;
} else if (boldUntilDepth < depth) {
fontStyle = EpdFontFamily::BOLD;
fontStyle = CrossPointFont::BOLD;
} else if (italicUntilDepth < depth) {
fontStyle = EpdFontFamily::ITALIC;
fontStyle = CrossPointFont::ITALIC;
}
// flush the buffer
partWordBuffer[partWordBufferIndex] = '\0';
@@ -161,7 +161,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
self->startNewTextBlock(static_cast<TextBlock::Style>(self->paragraphAlignment));
if (strcmp(name, "li") == 0) {
self->currentTextBlock->addWord("\xe2\x80\xa2", EpdFontFamily::REGULAR);
self->currentTextBlock->addWord("\xe2\x80\xa2", CrossPointFont::REGULAR);
}
self->depth += 1;