···77#include "builtinFonts/bookerly_italic.h"
8899EpdRenderer::EpdRenderer(XteinkDisplay* display) {
1010+ const auto bookerlyFontFamily = new EpdFontFamily(new EpdFont(&bookerly), new EpdFont(&bookerly_bold),
1111+ new EpdFont(&bookerly_italic), new EpdFont(&bookerly_bold_italic));
1212+ const auto babyblueFontFamily = new EpdFontFamily(new EpdFont(&babyblue));
1313+1014 this->display = display;
1111- this->regularFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly), display);
1212- this->boldFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold), display);
1313- this->italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_italic), display);
1414- this->bold_italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold_italic), display);
1515- this->smallFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&babyblue), display);
1515+ this->regularFontRenderer = new EpdFontRenderer<XteinkDisplay>(bookerlyFontFamily, display);
1616+ this->smallFontRenderer = new EpdFontRenderer<XteinkDisplay>(babyblueFontFamily, display);
16171718 this->marginTop = 11;
1819 this->marginBottom = 30;
···2122 this->lineCompression = 0.95f;
2223}
23242424-EpdFontRenderer<XteinkDisplay>* EpdRenderer::getFontRenderer(const bool bold, const bool italic) const {
2525- if (bold && italic) {
2626- return bold_italicFont;
2727- }
2828- if (bold) {
2929- return boldFont;
3030- }
3131- if (italic) {
3232- return italicFont;
3333- }
3434- return regularFont;
3535-}
3636-3737-int EpdRenderer::getTextWidth(const char* text, const bool bold, const bool italic) const {
2525+int EpdRenderer::getTextWidth(const char* text, const EpdFontStyle style) const {
3826 int w = 0, h = 0;
39274040- getFontRenderer(bold, italic)->font->getTextDimensions(text, &w, &h);
2828+ regularFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
41294230 return w;
4331}
44324545-int EpdRenderer::getSmallTextWidth(const char* text) const {
3333+int EpdRenderer::getSmallTextWidth(const char* text, const EpdFontStyle style) const {
4634 int w = 0, h = 0;
47354848- smallFont->font->getTextDimensions(text, &w, &h);
3636+ smallFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
49375038 return w;
5139}
52405353-void EpdRenderer::drawText(const int x, const int y, const char* text, const bool bold, const bool italic,
5454- const uint16_t color) const {
4141+void EpdRenderer::drawText(const int x, const int y, const char* text, const uint16_t color,
4242+ const EpdFontStyle style) const {
5543 int ypos = y + getLineHeight() + marginTop;
5644 int xpos = x + marginLeft;
5757- getFontRenderer(bold, italic)->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
4545+ regularFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
5846}
59476060-void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color) const {
6161- int ypos = y + smallFont->font->data->advanceY + marginTop;
4848+void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color,
4949+ const EpdFontStyle style) const {
5050+ int ypos = y + smallFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
6251 int xpos = x + marginLeft;
6363- smallFont->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
5252+ smallFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
6453}
65546655void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
6767- const bool bold, const bool italic) const {
5656+ const EpdFontStyle style) const {
6857 const size_t length = text.length();
6958 // fit the text into the box
7059 int start = 0;
···7261 int ypos = 0;
7362 while (true) {
7463 if (end >= length) {
7575- drawText(x, y + ypos, text.substr(start, length - start).c_str(), bold, italic);
6464+ drawText(x, y + ypos, text.substr(start, length - start).c_str(), 1, style);
7665 break;
7766 }
7867···8170 }
82718372 if (text[end - 1] == '\n') {
8484- drawText(x, y + ypos, text.substr(start, end - start).c_str(), bold, italic);
7373+ drawText(x, y + ypos, text.substr(start, end - start).c_str(), 1, style);
8574 ypos += getLineHeight();
8675 start = end;
8776 end = start + 1;
8877 continue;
8978 }
90799191- if (getTextWidth(text.substr(start, end - start).c_str(), bold, italic) > width) {
9292- drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), bold, italic);
8080+ if (getTextWidth(text.substr(start, end - start).c_str(), style) > width) {
8181+ drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), 1, style);
9382 ypos += getLineHeight();
9483 start = end - 1;
9584 continue;
···10897 display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
10998}
11099111111-void EpdRenderer::fillRect(const int x, const int y, const int width, const int height,
112112- const uint16_t color = 0) const {
100100+void EpdRenderer::fillRect(const int x, const int y, const int width, const int height, const uint16_t color) const {
113101 display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
114102}
115103104104+void EpdRenderer::drawCircle(const int x, const int y, const int radius, const uint16_t color) const {
105105+ display->drawCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
106106+}
107107+108108+void EpdRenderer::fillCircle(const int x, const int y, const int radius, const uint16_t color) const {
109109+ display->fillCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
110110+}
111111+112112+void EpdRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
113113+ const bool invert) const {
114114+ display->drawImage(bitmap, x + marginLeft, y + marginTop, width, height, invert);
115115+}
116116+116117void EpdRenderer::clearScreen(const bool black) const {
117118 Serial.println("Clearing screen");
118119 display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
···121122void EpdRenderer::flushDisplay(const bool partialUpdate) const { display->display(partialUpdate); }
122123123124void EpdRenderer::flushArea(const int x, const int y, const int width, const int height) const {
124124- display->displayWindow(x, y, width, height);
125125+ display->displayWindow(x + marginLeft, y + marginTop, width, height);
125126}
126127127128int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; }
128129129130int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; }
130131131131-int EpdRenderer::getSpaceWidth() const { return regularFont->font->getGlyph(' ')->advanceX; }
132132-133133-int EpdRenderer::getLineHeight() const { return regularFont->font->data->advanceY * lineCompression; }
134134-135135-// deep sleep helper - persist any state to disk that may be needed on wake
136136-bool EpdRenderer::dehydrate() {
137137- // TODO: Implement
138138- return false;
139139-};
140140-141141-// deep sleep helper - retrieve any state from disk after wake
142142-bool EpdRenderer::hydrate() {
143143- // TODO: Implement
144144- return false;
145145-};
132132+int EpdRenderer::getSpaceWidth() const { return regularFontRenderer->fontFamily->getGlyph(' ', REGULAR)->advanceX; }
146133147147-// really really clear the screen
148148-void EpdRenderer::reset() {
149149- // TODO: Implement
150150-};
134134+int EpdRenderer::getLineHeight() const {
135135+ return regularFontRenderer->fontFamily->getData(REGULAR)->advanceY * lineCompression;
136136+}
+13-23
lib/EpdRenderer/EpdRenderer.h
···8899class EpdRenderer {
1010 XteinkDisplay* display;
1111- EpdFontRenderer<XteinkDisplay>* regularFont;
1212- EpdFontRenderer<XteinkDisplay>* boldFont;
1313- EpdFontRenderer<XteinkDisplay>* italicFont;
1414- EpdFontRenderer<XteinkDisplay>* bold_italicFont;
1515- EpdFontRenderer<XteinkDisplay>* smallFont;
1111+ EpdFontRenderer<XteinkDisplay>* regularFontRenderer;
1212+ EpdFontRenderer<XteinkDisplay>* smallFontRenderer;
1613 int marginTop;
1714 int marginBottom;
1815 int marginLeft;
1916 int marginRight;
2017 float lineCompression;
2121- EpdFontRenderer<XteinkDisplay>* getFontRenderer(bool bold, bool italic) const;
22182319 public:
2420 explicit EpdRenderer(XteinkDisplay* display);
2521 ~EpdRenderer() = default;
2626- int getTextWidth(const char* text, bool bold = false, bool italic = false) const;
2727- int getSmallTextWidth(const char* text) const;
2828- void drawText(int x, int y, const char* text, bool bold = false, bool italic = false, uint16_t color = 1) const;
2929- void drawSmallText(int x, int y, const char* text, uint16_t color = 1) const;
3030- void drawTextBox(int x, int y, const std::string& text, int width, int height, bool bold = false,
3131- bool italic = false) const;
3232- void drawLine(int x1, int y1, int x2, int y2, uint16_t color) const;
3333- void drawRect(int x, int y, int width, int height, uint16_t color) const;
3434- void fillRect(int x, int y, int width, int height, uint16_t color) const;
2222+ int getTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
2323+ int getSmallTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
2424+ void drawText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
2525+ void drawSmallText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
2626+ void drawTextBox(int x, int y, const std::string& text, int width, int height, EpdFontStyle style = REGULAR) const;
2727+ void drawLine(int x1, int y1, int x2, int y2, uint16_t color = 1) const;
2828+ void drawRect(int x, int y, int width, int height, uint16_t color = 1) const;
2929+ void fillRect(int x, int y, int width, int height, uint16_t color = 1) const;
3030+ void drawCircle(int x, int y, int radius, uint16_t color = 1) const;
3131+ void fillCircle(int x, int y, int radius, uint16_t color = 1) const;
3232+ void drawImage(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false) const;
3533 void clearScreen(bool black = false) const;
3634 void flushDisplay(bool partialUpdate = true) const;
3735 void flushArea(int x, int y, int width, int height) const;
···4543 void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
4644 void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
4745 void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
4848- // deep sleep helper - persist any state to disk that may be needed on wake
4949- bool dehydrate();
5050- // deep sleep helper - retrieve any state from disk after wake
5151- bool hydrate();
5252- // really really clear the screen
5353- void reset();
5454-5555- uint8_t temperature = 20;
5646};
+4-4
lib/Epub/Epub/Section.cpp
···107107 delete p;
108108 } else if (pageCount == 0) {
109109 Serial.println("No pages to render");
110110- const int width = renderer->getTextWidth("Empty chapter", true);
111111- renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", true);
110110+ const int width = renderer->getTextWidth("Empty chapter", BOLD);
111111+ renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", 1, BOLD);
112112 } else {
113113 Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
114114- const int width = renderer->getTextWidth("Out of bounds", true);
115115- renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", true);
114114+ const int width = renderer->getTextWidth("Out of bounds", BOLD);
115115+ renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", 1, BOLD);
116116 }
117117}
+23-2
lib/Epub/Epub/blocks/TextBlock.cpp
···5656 uint16_t wordWidths[totalWordCount];
5757 for (int i = 0; i < words.size(); i++) {
5858 // measure the word
5959- const int width = renderer->getTextWidth(words[i].c_str(), wordStyles[i] & BOLD_SPAN, wordStyles[i] & ITALIC_SPAN);
5959+ EpdFontStyle fontStyle = REGULAR;
6060+ if (wordStyles[i] & BOLD_SPAN) {
6161+ if (wordStyles[i] & ITALIC_SPAN) {
6262+ fontStyle = BOLD_ITALIC;
6363+ } else {
6464+ fontStyle = BOLD;
6565+ }
6666+ } else if (wordStyles[i] & ITALIC_SPAN) {
6767+ fontStyle = ITALIC;
6868+ }
6969+ const int width = renderer->getTextWidth(words[i].c_str(), fontStyle);
6070 wordWidths[i] = width;
6171 }
6272···182192 // get the style
183193 const uint8_t wordStyle = wordStyles[i];
184194 // render the word
185185- renderer->drawText(x + wordXpos[i], y, words[i].c_str(), wordStyle & BOLD_SPAN, wordStyle & ITALIC_SPAN);
195195+ EpdFontStyle fontStyle = REGULAR;
196196+ if (wordStyles[i] & BOLD_SPAN) {
197197+ if (wordStyles[i] & ITALIC_SPAN) {
198198+ fontStyle = BOLD_ITALIC;
199199+ } else {
200200+ fontStyle = BOLD;
201201+ }
202202+203203+ } else if (wordStyles[i] & ITALIC_SPAN) {
204204+ fontStyle = ITALIC;
205205+ }
206206+ renderer->drawText(x + wordXpos[i], y, words[i].c_str(), 1, fontStyle);
186207 }
187208}
188209
+2-2
src/main.cpp
···89899090// Enter deep sleep mode
9191void enterDeepSleep() {
9292- enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", true, false, true));
9292+ enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", BOLD, true));
93939494 Serial.println("Power button released after a long press. Entering deep sleep.");
9595 delay(1000); // Allow Serial buffer to empty and display to update
···137137 display.setTextColor(GxEPD_BLACK);
138138 Serial.println("Display initialized");
139139140140- enterNewScreen(new FullScreenMessageScreen(renderer, "Booting...", true));
140140+ enterNewScreen(new FullScreenMessageScreen(renderer, "Booting...", BOLD));
141141142142 // SD Card Initialization
143143 SD.begin(SD_SPI_CS, SPI, SPI_FQ);
+10-10
src/screens/EpubReaderScreen.cpp
···134134 const int h = renderer->getLineHeight() + margin * 2;
135135 renderer->fillRect(x, y, w, h, 0);
136136 renderer->drawText(x + margin, y + margin, "Indexing...");
137137- renderer->drawRect(x + 5, y + 5, w - 10, h - 10, 1);
138138- renderer->flushArea(x - 20, y - 20, w + 40, h + 40);
137137+ renderer->drawRect(x + 5, y + 5, w - 10, h - 10);
138138+ renderer->flushArea(x, y, w, h);
139139 }
140140141141 section->setupCacheDir();
···199199 constexpr int y = 772;
200200201201 // Top line
202202- renderer->drawLine(x, y, x + batteryWidth - 4, y, 1);
202202+ renderer->drawLine(x, y, x + batteryWidth - 4, y);
203203 // Bottom line
204204- renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1, 1);
204204+ renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
205205 // Left line
206206- renderer->drawLine(x, y, x, y + batteryHeight - 1, 1);
206206+ renderer->drawLine(x, y, x, y + batteryHeight - 1);
207207 // Battery end
208208- renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1, 1);
209209- renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3, 1);
210210- renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3, 1);
211211- renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3, 1);
208208+ renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
209209+ renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3);
210210+ renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3);
211211+ renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
212212213213 // The +1 is to round up, so that we always fill at least one pixel
214214 int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
215215 if (filledWidth > batteryWidth - 5) {
216216 filledWidth = batteryWidth - 5; // Ensure we don't overflow
217217 }
218218- renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2, 1);
218218+ renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
219219220220 // Page width minus existing content with 30px padding on each side
221221 const int leftMargin = 20 + percentageTextWidth + 30;