feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
#include "CssParser.h"
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
#include <Arduino.h>
|
2026-02-13 12:16:39 +01:00
|
|
|
#include <Logging.h>
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
2026-02-15 12:22:42 -05:00
|
|
|
#include <array>
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
#include <cctype>
|
2026-02-15 12:22:42 -05:00
|
|
|
#include <string_view>
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Stack-allocated string buffer to avoid heap reallocations during parsing
|
|
|
|
|
// Provides string-like interface with fixed capacity
|
|
|
|
|
struct StackBuffer {
|
|
|
|
|
static constexpr size_t CAPACITY = 1024;
|
|
|
|
|
char data[CAPACITY];
|
|
|
|
|
size_t len = 0;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
void push_back(char c) {
|
|
|
|
|
if (len < CAPACITY - 1) {
|
|
|
|
|
data[len++] = c;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
void clear() { len = 0; }
|
|
|
|
|
bool empty() const { return len == 0; }
|
|
|
|
|
size_t size() const { return len; }
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Get string view of current content (zero-copy)
|
|
|
|
|
std::string_view view() const { return std::string_view(data, len); }
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Convert to string for passing to functions (single allocation)
|
|
|
|
|
std::string str() const { return std::string(data, len); }
|
|
|
|
|
};
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Buffer size for reading CSS files
|
|
|
|
|
constexpr size_t READ_BUFFER_SIZE = 512;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Maximum number of CSS rules to store in the selector map
|
|
|
|
|
// Prevents unbounded memory growth from pathological CSS files
|
|
|
|
|
constexpr size_t MAX_RULES = 1500;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Minimum free heap required to apply CSS during rendering
|
|
|
|
|
// If below this threshold, we skip CSS to avoid display artifacts.
|
|
|
|
|
constexpr size_t MIN_FREE_HEAP_FOR_CSS = 48 * 1024;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Maximum length for a single selector string
|
|
|
|
|
// Prevents parsing of extremely long or malformed selectors
|
|
|
|
|
constexpr size_t MAX_SELECTOR_LENGTH = 256;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Check if character is CSS whitespace
|
|
|
|
|
bool isCssWhitespace(const char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; }
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
// String utilities implementation
|
|
|
|
|
|
|
|
|
|
std::string CssParser::normalized(const std::string& s) {
|
|
|
|
|
std::string result;
|
|
|
|
|
result.reserve(s.size());
|
|
|
|
|
|
|
|
|
|
bool inSpace = true; // Start true to skip leading space
|
|
|
|
|
for (const char c : s) {
|
|
|
|
|
if (isCssWhitespace(c)) {
|
|
|
|
|
if (!inSpace) {
|
|
|
|
|
result.push_back(' ');
|
|
|
|
|
inSpace = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
result.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
|
|
|
|
|
inSpace = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove trailing space
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
while (!result.empty() && (result.back() == ' ' || result.back() == '\n')) {
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
result.pop_back();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
void CssParser::normalizedInto(const std::string& s, std::string& out) {
|
|
|
|
|
out.clear();
|
|
|
|
|
out.reserve(s.size());
|
|
|
|
|
|
|
|
|
|
bool inSpace = true; // Start true to skip leading space
|
|
|
|
|
for (const char c : s) {
|
|
|
|
|
if (isCssWhitespace(c)) {
|
|
|
|
|
if (!inSpace) {
|
|
|
|
|
out.push_back(' ');
|
|
|
|
|
inSpace = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
out.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c))));
|
|
|
|
|
inSpace = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!out.empty() && out.back() == ' ') {
|
|
|
|
|
out.pop_back();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
std::vector<std::string> CssParser::splitOnChar(const std::string& s, const char delimiter) {
|
|
|
|
|
std::vector<std::string> parts;
|
|
|
|
|
size_t start = 0;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i <= s.size(); ++i) {
|
|
|
|
|
if (i == s.size() || s[i] == delimiter) {
|
|
|
|
|
std::string part = s.substr(start, i - start);
|
|
|
|
|
std::string trimmed = normalized(part);
|
|
|
|
|
if (!trimmed.empty()) {
|
|
|
|
|
parts.push_back(trimmed);
|
|
|
|
|
}
|
|
|
|
|
start = i + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> CssParser::splitWhitespace(const std::string& s) {
|
|
|
|
|
std::vector<std::string> parts;
|
|
|
|
|
size_t start = 0;
|
|
|
|
|
bool inWord = false;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i <= s.size(); ++i) {
|
|
|
|
|
const bool isSpace = i == s.size() || isCssWhitespace(s[i]);
|
|
|
|
|
if (isSpace && inWord) {
|
|
|
|
|
parts.push_back(s.substr(start, i - start));
|
|
|
|
|
inWord = false;
|
|
|
|
|
} else if (!isSpace && !inWord) {
|
|
|
|
|
start = i;
|
|
|
|
|
inWord = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return parts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Property value interpreters
|
|
|
|
|
|
|
|
|
|
CssTextAlign CssParser::interpretAlignment(const std::string& val) {
|
|
|
|
|
const std::string v = normalized(val);
|
|
|
|
|
|
|
|
|
|
if (v == "left" || v == "start") return CssTextAlign::Left;
|
|
|
|
|
if (v == "right" || v == "end") return CssTextAlign::Right;
|
|
|
|
|
if (v == "center") return CssTextAlign::Center;
|
|
|
|
|
if (v == "justify") return CssTextAlign::Justify;
|
|
|
|
|
|
|
|
|
|
return CssTextAlign::Left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CssFontStyle CssParser::interpretFontStyle(const std::string& val) {
|
|
|
|
|
const std::string v = normalized(val);
|
|
|
|
|
|
|
|
|
|
if (v == "italic" || v == "oblique") return CssFontStyle::Italic;
|
|
|
|
|
return CssFontStyle::Normal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CssFontWeight CssParser::interpretFontWeight(const std::string& val) {
|
|
|
|
|
const std::string v = normalized(val);
|
|
|
|
|
|
|
|
|
|
// Named values
|
|
|
|
|
if (v == "bold" || v == "bolder") return CssFontWeight::Bold;
|
|
|
|
|
if (v == "normal" || v == "lighter") return CssFontWeight::Normal;
|
|
|
|
|
|
|
|
|
|
// Numeric values: 100-900
|
|
|
|
|
// CSS spec: 400 = normal, 700 = bold
|
|
|
|
|
// We use: 0-400 = normal, 700+ = bold, 500-600 = normal (conservative)
|
|
|
|
|
char* endPtr = nullptr;
|
|
|
|
|
const long numericWeight = std::strtol(v.c_str(), &endPtr, 10);
|
|
|
|
|
|
|
|
|
|
// If we parsed a number and consumed the whole string
|
|
|
|
|
if (endPtr != v.c_str() && *endPtr == '\0') {
|
|
|
|
|
return numericWeight >= 700 ? CssFontWeight::Bold : CssFontWeight::Normal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CssFontWeight::Normal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CssTextDecoration CssParser::interpretDecoration(const std::string& val) {
|
|
|
|
|
const std::string v = normalized(val);
|
|
|
|
|
|
|
|
|
|
// text-decoration can have multiple space-separated values
|
|
|
|
|
if (v.find("underline") != std::string::npos) {
|
|
|
|
|
return CssTextDecoration::Underline;
|
|
|
|
|
}
|
|
|
|
|
return CssTextDecoration::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CssLength CssParser::interpretLength(const std::string& val) {
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
CssLength result;
|
|
|
|
|
tryInterpretLength(val, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CssParser::tryInterpretLength(const std::string& val, CssLength& out) {
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
const std::string v = normalized(val);
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
if (v.empty()) {
|
|
|
|
|
out = CssLength{};
|
|
|
|
|
return false;
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
size_t unitStart = v.size();
|
|
|
|
|
for (size_t i = 0; i < v.size(); ++i) {
|
|
|
|
|
const char c = v[i];
|
|
|
|
|
if (!std::isdigit(c) && c != '.' && c != '-' && c != '+') {
|
|
|
|
|
unitStart = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string numPart = v.substr(0, unitStart);
|
|
|
|
|
const std::string unitPart = v.substr(unitStart);
|
|
|
|
|
|
|
|
|
|
char* endPtr = nullptr;
|
|
|
|
|
const float numericValue = std::strtof(numPart.c_str(), &endPtr);
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
if (endPtr == numPart.c_str()) {
|
|
|
|
|
out = CssLength{};
|
|
|
|
|
return false; // No number parsed (e.g. auto, inherit, initial)
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
auto unit = CssUnit::Pixels;
|
|
|
|
|
if (unitPart == "em") {
|
|
|
|
|
unit = CssUnit::Em;
|
|
|
|
|
} else if (unitPart == "rem") {
|
|
|
|
|
unit = CssUnit::Rem;
|
|
|
|
|
} else if (unitPart == "pt") {
|
|
|
|
|
unit = CssUnit::Points;
|
2026-02-08 16:31:52 -05:00
|
|
|
} else if (unitPart == "%") {
|
|
|
|
|
unit = CssUnit::Percent;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
out = CssLength{numericValue, unit};
|
|
|
|
|
return true;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
2026-02-15 12:22:42 -05:00
|
|
|
// Declaration parsing
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
void CssParser::parseDeclarationIntoStyle(const std::string& decl, CssStyle& style, std::string& propNameBuf,
|
|
|
|
|
std::string& propValueBuf) {
|
|
|
|
|
const size_t colonPos = decl.find(':');
|
|
|
|
|
if (colonPos == std::string::npos || colonPos == 0) return;
|
|
|
|
|
|
|
|
|
|
normalizedInto(decl.substr(0, colonPos), propNameBuf);
|
|
|
|
|
normalizedInto(decl.substr(colonPos + 1), propValueBuf);
|
|
|
|
|
|
|
|
|
|
if (propNameBuf.empty() || propValueBuf.empty()) return;
|
|
|
|
|
|
|
|
|
|
if (propNameBuf == "text-align") {
|
|
|
|
|
style.textAlign = interpretAlignment(propValueBuf);
|
|
|
|
|
style.defined.textAlign = 1;
|
|
|
|
|
} else if (propNameBuf == "font-style") {
|
|
|
|
|
style.fontStyle = interpretFontStyle(propValueBuf);
|
|
|
|
|
style.defined.fontStyle = 1;
|
|
|
|
|
} else if (propNameBuf == "font-weight") {
|
|
|
|
|
style.fontWeight = interpretFontWeight(propValueBuf);
|
|
|
|
|
style.defined.fontWeight = 1;
|
|
|
|
|
} else if (propNameBuf == "text-decoration" || propNameBuf == "text-decoration-line") {
|
|
|
|
|
style.textDecoration = interpretDecoration(propValueBuf);
|
|
|
|
|
style.defined.textDecoration = 1;
|
|
|
|
|
} else if (propNameBuf == "text-indent") {
|
|
|
|
|
style.textIndent = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.textIndent = 1;
|
|
|
|
|
} else if (propNameBuf == "margin-top") {
|
|
|
|
|
style.marginTop = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.marginTop = 1;
|
|
|
|
|
} else if (propNameBuf == "margin-bottom") {
|
|
|
|
|
style.marginBottom = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.marginBottom = 1;
|
|
|
|
|
} else if (propNameBuf == "margin-left") {
|
|
|
|
|
style.marginLeft = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.marginLeft = 1;
|
|
|
|
|
} else if (propNameBuf == "margin-right") {
|
|
|
|
|
style.marginRight = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.marginRight = 1;
|
|
|
|
|
} else if (propNameBuf == "margin") {
|
|
|
|
|
const auto values = splitWhitespace(propValueBuf);
|
|
|
|
|
if (!values.empty()) {
|
|
|
|
|
style.marginTop = interpretLength(values[0]);
|
|
|
|
|
style.marginRight = values.size() >= 2 ? interpretLength(values[1]) : style.marginTop;
|
|
|
|
|
style.marginBottom = values.size() >= 3 ? interpretLength(values[2]) : style.marginTop;
|
|
|
|
|
style.marginLeft = values.size() >= 4 ? interpretLength(values[3]) : style.marginRight;
|
|
|
|
|
style.defined.marginTop = style.defined.marginRight = style.defined.marginBottom = style.defined.marginLeft = 1;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
2026-02-15 12:22:42 -05:00
|
|
|
} else if (propNameBuf == "padding-top") {
|
|
|
|
|
style.paddingTop = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.paddingTop = 1;
|
|
|
|
|
} else if (propNameBuf == "padding-bottom") {
|
|
|
|
|
style.paddingBottom = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.paddingBottom = 1;
|
|
|
|
|
} else if (propNameBuf == "padding-left") {
|
|
|
|
|
style.paddingLeft = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.paddingLeft = 1;
|
|
|
|
|
} else if (propNameBuf == "padding-right") {
|
|
|
|
|
style.paddingRight = interpretLength(propValueBuf);
|
|
|
|
|
style.defined.paddingRight = 1;
|
|
|
|
|
} else if (propNameBuf == "padding") {
|
|
|
|
|
const auto values = splitWhitespace(propValueBuf);
|
|
|
|
|
if (!values.empty()) {
|
|
|
|
|
style.paddingTop = interpretLength(values[0]);
|
|
|
|
|
style.paddingRight = values.size() >= 2 ? interpretLength(values[1]) : style.paddingTop;
|
|
|
|
|
style.paddingBottom = values.size() >= 3 ? interpretLength(values[2]) : style.paddingTop;
|
|
|
|
|
style.paddingLeft = values.size() >= 4 ? interpretLength(values[3]) : style.paddingRight;
|
|
|
|
|
style.defined.paddingTop = style.defined.paddingRight = style.defined.paddingBottom = style.defined.paddingLeft =
|
|
|
|
|
1;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
2026-02-19 14:21:31 -05:00
|
|
|
} else if (propNameBuf == "height") {
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
CssLength len;
|
|
|
|
|
if (tryInterpretLength(propValueBuf, len)) {
|
|
|
|
|
style.imageHeight = len;
|
|
|
|
|
style.defined.imageHeight = 1;
|
|
|
|
|
}
|
2026-02-15 20:36:48 -05:00
|
|
|
} else if (propNameBuf == "width") {
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
CssLength len;
|
|
|
|
|
if (tryInterpretLength(propValueBuf, len)) {
|
|
|
|
|
style.width = len;
|
|
|
|
|
style.defined.width = 1;
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CssStyle CssParser::parseDeclarations(const std::string& declBlock) {
|
|
|
|
|
CssStyle style;
|
2026-02-15 12:22:42 -05:00
|
|
|
std::string propNameBuf;
|
|
|
|
|
std::string propValueBuf;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
size_t start = 0;
|
|
|
|
|
for (size_t i = 0; i <= declBlock.size(); ++i) {
|
|
|
|
|
if (i == declBlock.size() || declBlock[i] == ';') {
|
|
|
|
|
if (i > start) {
|
|
|
|
|
const size_t len = i - start;
|
|
|
|
|
std::string decl = declBlock.substr(start, len);
|
|
|
|
|
if (!decl.empty()) {
|
|
|
|
|
parseDeclarationIntoStyle(decl, style, propNameBuf, propValueBuf);
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
2026-02-15 12:22:42 -05:00
|
|
|
start = i + 1;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rule processing
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
void CssParser::processRuleBlockWithStyle(const std::string& selectorGroup, const CssStyle& style) {
|
|
|
|
|
// Check if we've reached the rule limit before processing
|
|
|
|
|
if (rulesBySelector_.size() >= MAX_RULES) {
|
|
|
|
|
LOG_DBG("CSS", "Reached max rules limit (%zu), stopping CSS parsing", MAX_RULES);
|
|
|
|
|
return;
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
// Handle comma-separated selectors
|
|
|
|
|
const auto selectors = splitOnChar(selectorGroup, ',');
|
|
|
|
|
|
|
|
|
|
for (const auto& sel : selectors) {
|
2026-02-15 12:22:42 -05:00
|
|
|
// Validate selector length before processing
|
|
|
|
|
if (sel.size() > MAX_SELECTOR_LENGTH) {
|
|
|
|
|
LOG_DBG("CSS", "Selector too long (%zu > %zu), skipping", sel.size(), MAX_SELECTOR_LENGTH);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
// Normalize the selector
|
|
|
|
|
std::string key = normalized(sel);
|
|
|
|
|
if (key.empty()) continue;
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
// Skip unsupported selector types to reduce memory usage.
|
|
|
|
|
// We only match: tag, tag.class, .class
|
|
|
|
|
if (key.find('+') != std::string::npos) continue; // adjacent sibling
|
|
|
|
|
if (key.find('>') != std::string::npos) continue; // child combinator
|
|
|
|
|
if (key.find('[') != std::string::npos) continue; // attribute selector
|
|
|
|
|
if (key.find(':') != std::string::npos) continue; // pseudo selector
|
|
|
|
|
if (key.find('#') != std::string::npos) continue; // ID selector
|
|
|
|
|
if (key.find('~') != std::string::npos) continue; // general sibling
|
|
|
|
|
if (key.find('*') != std::string::npos) continue; // wildcard
|
|
|
|
|
if (key.find(' ') != std::string::npos) continue; // descendant combinator
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
// Skip if this would exceed the rule limit
|
|
|
|
|
if (rulesBySelector_.size() >= MAX_RULES) {
|
|
|
|
|
LOG_DBG("CSS", "Reached max rules limit, stopping selector processing");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
// Store or merge with existing
|
|
|
|
|
auto it = rulesBySelector_.find(key);
|
|
|
|
|
if (it != rulesBySelector_.end()) {
|
|
|
|
|
it->second.applyOver(style);
|
|
|
|
|
} else {
|
|
|
|
|
rulesBySelector_[key] = style;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Main parsing entry point
|
|
|
|
|
|
|
|
|
|
bool CssParser::loadFromStream(FsFile& source) {
|
|
|
|
|
if (!source) {
|
2026-02-13 12:16:39 +01:00
|
|
|
LOG_ERR("CSS", "Cannot read from invalid file");
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
size_t totalRead = 0;
|
|
|
|
|
|
|
|
|
|
// Use stack-allocated buffers for parsing to avoid heap reallocations
|
|
|
|
|
StackBuffer selector;
|
|
|
|
|
StackBuffer declBuffer;
|
|
|
|
|
// Keep these as std::string since they're passed by reference to parseDeclarationIntoStyle
|
|
|
|
|
std::string propNameBuf;
|
|
|
|
|
std::string propValueBuf;
|
|
|
|
|
|
|
|
|
|
bool inComment = false;
|
|
|
|
|
bool maybeSlash = false;
|
|
|
|
|
bool prevStar = false;
|
|
|
|
|
|
|
|
|
|
bool inAtRule = false;
|
|
|
|
|
int atDepth = 0;
|
|
|
|
|
|
|
|
|
|
int bodyDepth = 0;
|
|
|
|
|
bool skippingRule = false;
|
|
|
|
|
CssStyle currentStyle;
|
|
|
|
|
|
|
|
|
|
auto handleChar = [&](const char c) {
|
|
|
|
|
if (inAtRule) {
|
|
|
|
|
if (c == '{') {
|
|
|
|
|
++atDepth;
|
|
|
|
|
} else if (c == '}') {
|
|
|
|
|
if (atDepth > 0) --atDepth;
|
|
|
|
|
if (atDepth == 0) inAtRule = false;
|
|
|
|
|
} else if (c == ';' && atDepth == 0) {
|
|
|
|
|
inAtRule = false;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bodyDepth == 0) {
|
|
|
|
|
if (selector.empty() && isCssWhitespace(c)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (c == '@' && selector.empty()) {
|
|
|
|
|
inAtRule = true;
|
|
|
|
|
atDepth = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (c == '{') {
|
|
|
|
|
bodyDepth = 1;
|
|
|
|
|
currentStyle = CssStyle{};
|
|
|
|
|
declBuffer.clear();
|
|
|
|
|
if (selector.size() > MAX_SELECTOR_LENGTH * 4) {
|
|
|
|
|
skippingRule = true;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
selector.push_back(c);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bodyDepth > 0
|
|
|
|
|
if (c == '{') {
|
|
|
|
|
++bodyDepth;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (c == '}') {
|
|
|
|
|
--bodyDepth;
|
|
|
|
|
if (bodyDepth == 0) {
|
|
|
|
|
if (!skippingRule && !declBuffer.empty()) {
|
|
|
|
|
parseDeclarationIntoStyle(declBuffer.str(), currentStyle, propNameBuf, propValueBuf);
|
|
|
|
|
}
|
|
|
|
|
if (!skippingRule) {
|
|
|
|
|
processRuleBlockWithStyle(selector.str(), currentStyle);
|
|
|
|
|
}
|
|
|
|
|
selector.clear();
|
|
|
|
|
declBuffer.clear();
|
|
|
|
|
skippingRule = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (bodyDepth > 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!skippingRule) {
|
|
|
|
|
if (c == ';') {
|
|
|
|
|
if (!declBuffer.empty()) {
|
|
|
|
|
parseDeclarationIntoStyle(declBuffer.str(), currentStyle, propNameBuf, propValueBuf);
|
|
|
|
|
declBuffer.clear();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
declBuffer.push_back(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
char buffer[READ_BUFFER_SIZE];
|
|
|
|
|
while (source.available()) {
|
|
|
|
|
int bytesRead = source.read(buffer, sizeof(buffer));
|
|
|
|
|
if (bytesRead <= 0) break;
|
|
|
|
|
|
|
|
|
|
totalRead += static_cast<size_t>(bytesRead);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < bytesRead; ++i) {
|
|
|
|
|
const char c = buffer[i];
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
if (inComment) {
|
|
|
|
|
if (prevStar && c == '/') {
|
|
|
|
|
inComment = false;
|
|
|
|
|
prevStar = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
prevStar = c == '*';
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maybeSlash) {
|
|
|
|
|
if (c == '*') {
|
|
|
|
|
inComment = true;
|
|
|
|
|
maybeSlash = false;
|
|
|
|
|
prevStar = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
handleChar('/');
|
|
|
|
|
maybeSlash = false;
|
|
|
|
|
// fall through to process current char
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '/') {
|
|
|
|
|
maybeSlash = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
handleChar(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
if (maybeSlash) {
|
|
|
|
|
handleChar('/');
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
LOG_DBG("CSS", "Parsed %zu rules from %zu bytes", rulesBySelector_.size(), totalRead);
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Style resolution
|
|
|
|
|
|
|
|
|
|
CssStyle CssParser::resolveStyle(const std::string& tagName, const std::string& classAttr) const {
|
2026-02-15 12:22:42 -05:00
|
|
|
static bool lowHeapWarningLogged = false;
|
|
|
|
|
if (ESP.getFreeHeap() < MIN_FREE_HEAP_FOR_CSS) {
|
|
|
|
|
if (!lowHeapWarningLogged) {
|
|
|
|
|
lowHeapWarningLogged = true;
|
|
|
|
|
LOG_DBG("CSS", "Warning: low heap (%u bytes) below MIN_FREE_HEAP_FOR_CSS (%u), returning empty style",
|
|
|
|
|
ESP.getFreeHeap(), static_cast<unsigned>(MIN_FREE_HEAP_FOR_CSS));
|
|
|
|
|
}
|
|
|
|
|
return CssStyle{};
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
CssStyle result;
|
|
|
|
|
const std::string tag = normalized(tagName);
|
|
|
|
|
|
|
|
|
|
// 1. Apply element-level style (lowest priority)
|
|
|
|
|
const auto tagIt = rulesBySelector_.find(tag);
|
|
|
|
|
if (tagIt != rulesBySelector_.end()) {
|
|
|
|
|
result.applyOver(tagIt->second);
|
|
|
|
|
}
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
// TODO: Support combinations of classes (e.g. style on .class1.class2)
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
// 2. Apply class styles (medium priority)
|
|
|
|
|
if (!classAttr.empty()) {
|
|
|
|
|
const auto classes = splitWhitespace(classAttr);
|
|
|
|
|
|
|
|
|
|
for (const auto& cls : classes) {
|
|
|
|
|
std::string classKey = "." + normalized(cls);
|
|
|
|
|
|
|
|
|
|
auto classIt = rulesBySelector_.find(classKey);
|
|
|
|
|
if (classIt != rulesBySelector_.end()) {
|
|
|
|
|
result.applyOver(classIt->second);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
// TODO: Support combinations of classes (e.g. style on p.class1.class2)
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
// 3. Apply element.class styles (higher priority)
|
|
|
|
|
for (const auto& cls : classes) {
|
|
|
|
|
std::string combinedKey = tag + "." + normalized(cls);
|
|
|
|
|
|
|
|
|
|
auto combinedIt = rulesBySelector_.find(combinedKey);
|
|
|
|
|
if (combinedIt != rulesBySelector_.end()) {
|
|
|
|
|
result.applyOver(combinedIt->second);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Inline style parsing (static - doesn't need rule database)
|
|
|
|
|
|
|
|
|
|
CssStyle CssParser::parseInlineStyle(const std::string& styleValue) { return parseDeclarations(styleValue); }
|
|
|
|
|
|
|
|
|
|
// Cache serialization
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
// Cache file name (version is CssParser::CSS_CACHE_VERSION)
|
2026-02-15 12:22:42 -05:00
|
|
|
constexpr char rulesCache[] = "/css_rules.cache";
|
|
|
|
|
|
|
|
|
|
bool CssParser::hasCache() const { return Storage.exists((cachePath + rulesCache).c_str()); }
|
|
|
|
|
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
void CssParser::deleteCache() const {
|
|
|
|
|
if (hasCache()) Storage.remove((cachePath + rulesCache).c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
bool CssParser::saveToCache() const {
|
|
|
|
|
if (cachePath.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
FsFile file;
|
|
|
|
|
if (!Storage.openFileForWrite("CSS", cachePath + rulesCache, file)) {
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write version
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
file.write(CssParser::CSS_CACHE_VERSION);
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
// Write rule count
|
|
|
|
|
const auto ruleCount = static_cast<uint16_t>(rulesBySelector_.size());
|
|
|
|
|
file.write(reinterpret_cast<const uint8_t*>(&ruleCount), sizeof(ruleCount));
|
|
|
|
|
|
|
|
|
|
// Write each rule: selector string + CssStyle fields
|
|
|
|
|
for (const auto& pair : rulesBySelector_) {
|
|
|
|
|
// Write selector string (length-prefixed)
|
|
|
|
|
const auto selectorLen = static_cast<uint16_t>(pair.first.size());
|
|
|
|
|
file.write(reinterpret_cast<const uint8_t*>(&selectorLen), sizeof(selectorLen));
|
|
|
|
|
file.write(reinterpret_cast<const uint8_t*>(pair.first.data()), selectorLen);
|
|
|
|
|
|
|
|
|
|
// Write CssStyle fields (all are POD types)
|
|
|
|
|
const CssStyle& style = pair.second;
|
|
|
|
|
file.write(static_cast<uint8_t>(style.textAlign));
|
|
|
|
|
file.write(static_cast<uint8_t>(style.fontStyle));
|
|
|
|
|
file.write(static_cast<uint8_t>(style.fontWeight));
|
|
|
|
|
file.write(static_cast<uint8_t>(style.textDecoration));
|
|
|
|
|
|
|
|
|
|
// Write CssLength fields (value + unit)
|
|
|
|
|
auto writeLength = [&file](const CssLength& len) {
|
|
|
|
|
file.write(reinterpret_cast<const uint8_t*>(&len.value), sizeof(len.value));
|
|
|
|
|
file.write(static_cast<uint8_t>(len.unit));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
writeLength(style.textIndent);
|
|
|
|
|
writeLength(style.marginTop);
|
|
|
|
|
writeLength(style.marginBottom);
|
|
|
|
|
writeLength(style.marginLeft);
|
|
|
|
|
writeLength(style.marginRight);
|
|
|
|
|
writeLength(style.paddingTop);
|
|
|
|
|
writeLength(style.paddingBottom);
|
|
|
|
|
writeLength(style.paddingLeft);
|
|
|
|
|
writeLength(style.paddingRight);
|
2026-02-19 14:21:31 -05:00
|
|
|
writeLength(style.imageHeight);
|
|
|
|
|
writeLength(style.width);
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
// Write defined flags as uint16_t
|
|
|
|
|
uint16_t definedBits = 0;
|
|
|
|
|
if (style.defined.textAlign) definedBits |= 1 << 0;
|
|
|
|
|
if (style.defined.fontStyle) definedBits |= 1 << 1;
|
|
|
|
|
if (style.defined.fontWeight) definedBits |= 1 << 2;
|
|
|
|
|
if (style.defined.textDecoration) definedBits |= 1 << 3;
|
|
|
|
|
if (style.defined.textIndent) definedBits |= 1 << 4;
|
|
|
|
|
if (style.defined.marginTop) definedBits |= 1 << 5;
|
|
|
|
|
if (style.defined.marginBottom) definedBits |= 1 << 6;
|
|
|
|
|
if (style.defined.marginLeft) definedBits |= 1 << 7;
|
|
|
|
|
if (style.defined.marginRight) definedBits |= 1 << 8;
|
|
|
|
|
if (style.defined.paddingTop) definedBits |= 1 << 9;
|
|
|
|
|
if (style.defined.paddingBottom) definedBits |= 1 << 10;
|
|
|
|
|
if (style.defined.paddingLeft) definedBits |= 1 << 11;
|
|
|
|
|
if (style.defined.paddingRight) definedBits |= 1 << 12;
|
2026-02-19 14:21:31 -05:00
|
|
|
if (style.defined.width) definedBits |= 1 << 13;
|
|
|
|
|
if (style.defined.imageHeight) definedBits |= 1 << 14;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
file.write(reinterpret_cast<const uint8_t*>(&definedBits), sizeof(definedBits));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 12:16:39 +01:00
|
|
|
LOG_DBG("CSS", "Saved %u rules to cache", ruleCount);
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:22:42 -05:00
|
|
|
bool CssParser::loadFromCache() {
|
|
|
|
|
if (cachePath.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FsFile file;
|
|
|
|
|
if (!Storage.openFileForRead("CSS", cachePath + rulesCache, file)) {
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear existing rules
|
|
|
|
|
clear();
|
|
|
|
|
|
|
|
|
|
// Read and verify version
|
|
|
|
|
uint8_t version = 0;
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
if (file.read(&version, 1) != 1 || version != CssParser::CSS_CACHE_VERSION) {
|
|
|
|
|
LOG_DBG("CSS", "Cache version mismatch (got %u, expected %u), removing stale cache for rebuild", version,
|
|
|
|
|
CssParser::CSS_CACHE_VERSION);
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
fix: Port upstream 1.1.0-rc PRs #1014, #1018, #990 and align #1002
Port three new upstream commits and align the existing #1002 port:
- PR #1014: Strip unused CSS rules by filtering unsupported selector
types (+, >, [, :, #, ~, *, descendants) in processRuleBlockWithStyle.
Fix normalized() trailing whitespace to also strip newlines.
- PR #1018: Add deleteCache() to CssParser, move CSS_CACHE_VERSION to
static class member, remove stale cache on version mismatch, invalidate
section caches (Storage.removeDir) when CSS is rebuilt. Refactor
parseCssFiles() to early-return when cache exists.
- PR #990: Adapt classic theme continue-reading card width to cover
aspect ratio (clamped to 90% screen width), increase homeTopPadding
20->40, fix centering with rect.x offset for boxX/continueBoxX.
- #1002 alignment: Add tryInterpretLength() to skip non-numeric CSS
values (auto, inherit), add "both width and height set" image sizing
branch in ChapterHtmlSlimParser.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 15:52:30 -05:00
|
|
|
Storage.remove((cachePath + rulesCache).c_str());
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read rule count
|
|
|
|
|
uint16_t ruleCount = 0;
|
|
|
|
|
if (file.read(&ruleCount, sizeof(ruleCount)) != sizeof(ruleCount)) {
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read each rule
|
|
|
|
|
for (uint16_t i = 0; i < ruleCount; ++i) {
|
|
|
|
|
// Read selector string
|
|
|
|
|
uint16_t selectorLen = 0;
|
|
|
|
|
if (file.read(&selectorLen, sizeof(selectorLen)) != sizeof(selectorLen)) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string selector;
|
|
|
|
|
selector.resize(selectorLen);
|
|
|
|
|
if (file.read(&selector[0], selectorLen) != selectorLen) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read CssStyle fields
|
|
|
|
|
CssStyle style;
|
|
|
|
|
uint8_t enumVal;
|
|
|
|
|
|
|
|
|
|
if (file.read(&enumVal, 1) != 1) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
style.textAlign = static_cast<CssTextAlign>(enumVal);
|
|
|
|
|
|
|
|
|
|
if (file.read(&enumVal, 1) != 1) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
style.fontStyle = static_cast<CssFontStyle>(enumVal);
|
|
|
|
|
|
|
|
|
|
if (file.read(&enumVal, 1) != 1) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
style.fontWeight = static_cast<CssFontWeight>(enumVal);
|
|
|
|
|
|
|
|
|
|
if (file.read(&enumVal, 1) != 1) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
style.textDecoration = static_cast<CssTextDecoration>(enumVal);
|
|
|
|
|
|
|
|
|
|
// Read CssLength fields
|
|
|
|
|
auto readLength = [&file](CssLength& len) -> bool {
|
|
|
|
|
if (file.read(&len.value, sizeof(len.value)) != sizeof(len.value)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
uint8_t unitVal;
|
|
|
|
|
if (file.read(&unitVal, 1) != 1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
len.unit = static_cast<CssUnit>(unitVal);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!readLength(style.textIndent) || !readLength(style.marginTop) || !readLength(style.marginBottom) ||
|
|
|
|
|
!readLength(style.marginLeft) || !readLength(style.marginRight) || !readLength(style.paddingTop) ||
|
2026-02-19 14:21:31 -05:00
|
|
|
!readLength(style.paddingBottom) || !readLength(style.paddingLeft) || !readLength(style.paddingRight) ||
|
|
|
|
|
!readLength(style.imageHeight) || !readLength(style.width)) {
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read defined flags
|
|
|
|
|
uint16_t definedBits = 0;
|
|
|
|
|
if (file.read(&definedBits, sizeof(definedBits)) != sizeof(definedBits)) {
|
|
|
|
|
rulesBySelector_.clear();
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
style.defined.textAlign = (definedBits & 1 << 0) != 0;
|
|
|
|
|
style.defined.fontStyle = (definedBits & 1 << 1) != 0;
|
|
|
|
|
style.defined.fontWeight = (definedBits & 1 << 2) != 0;
|
|
|
|
|
style.defined.textDecoration = (definedBits & 1 << 3) != 0;
|
|
|
|
|
style.defined.textIndent = (definedBits & 1 << 4) != 0;
|
|
|
|
|
style.defined.marginTop = (definedBits & 1 << 5) != 0;
|
|
|
|
|
style.defined.marginBottom = (definedBits & 1 << 6) != 0;
|
|
|
|
|
style.defined.marginLeft = (definedBits & 1 << 7) != 0;
|
|
|
|
|
style.defined.marginRight = (definedBits & 1 << 8) != 0;
|
|
|
|
|
style.defined.paddingTop = (definedBits & 1 << 9) != 0;
|
|
|
|
|
style.defined.paddingBottom = (definedBits & 1 << 10) != 0;
|
|
|
|
|
style.defined.paddingLeft = (definedBits & 1 << 11) != 0;
|
|
|
|
|
style.defined.paddingRight = (definedBits & 1 << 12) != 0;
|
2026-02-19 14:21:31 -05:00
|
|
|
style.defined.width = (definedBits & 1 << 13) != 0;
|
|
|
|
|
style.defined.imageHeight = (definedBits & 1 << 14) != 0;
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
|
|
|
|
|
rulesBySelector_[selector] = style;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 12:16:39 +01:00
|
|
|
LOG_DBG("CSS", "Loaded %u rules from cache", ruleCount);
|
2026-02-15 12:22:42 -05:00
|
|
|
file.close();
|
feat: Add CSS parsing and CSS support in EPUBs (#411)
## Summary
* **What is the goal of this PR?**
- Adds basic CSS parsing to EPUBs and determine the CSS rules when
rendering to the screen so that text is styled correctly. Currently
supports bold, underline, italics, margin, padding, and text alignment
## Additional Context
- My main reason for wanting this is that the book I'm currently
reading, Carl's Doomsday Scenario (2nd in the Dungeon Crawler Carl
series), relies _a lot_ on styled text for telling parts of the story.
When text is bolded, it's supposed to be a message that's rendered
"on-screen" in the story. When characters are "chatting" with each
other, the text is bolded and their names are underlined. Plus, normal
emphasis is provided with italicizing words here and there. So, this
greatly improves my experience reading this book on the Xteink, and I
figured it was useful enough for others too.
- For transparency: I'm a software engineer, but I'm mostly frontend and
TypeScript/JavaScript. It's been _years_ since I did any C/C++, so I
would not be surprised if I'm doing something dumb along the way in this
code. Please don't hesitate to ask for changes if something looks off. I
heavily relied on Claude Code for help, and I had a lot of inspiration
from how [microreader](https://github.com/CidVonHighwind/microreader)
achieves their CSS parsing and styling. I did give this as good of a
code review as I could and went through everything, and _it works on my
machine_ 😄
### Before


### After


---
### AI Usage
Did you use AI tools to help write this code? **YES**, Claude Code
2026-02-05 05:28:10 -05:00
|
|
|
return true;
|
|
|
|
|
}
|