Proper support of superscript/subscript
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
#include "FsHelpers.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t BOOK_CACHE_VERSION = 8;
|
||||
constexpr uint8_t BOOK_CACHE_VERSION = 9;
|
||||
constexpr char bookBinFile[] = "/book.bin";
|
||||
constexpr char tmpSpineBinFile[] = "/spine.bin.tmp";
|
||||
constexpr char tmpTocBinFile[] = "/toc.bin.tmp";
|
||||
|
||||
@@ -12,10 +12,21 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int
|
||||
return;
|
||||
}
|
||||
|
||||
const int ascender = renderer.getFontAscenderSize(fontId);
|
||||
for (size_t i = 0; i < words.size(); i++) {
|
||||
const int wordX = wordXpos[i] + x;
|
||||
const EpdFontFamily::Style currentStyle = wordStyles[i];
|
||||
renderer.drawText(fontId, wordX, y, words[i].c_str(), true, currentStyle);
|
||||
// SUP/SUB shift the baseline passed to drawText; the glyph is also scaled 50% inside
|
||||
// drawText, so these offsets are chosen relative to the full-size ascender:
|
||||
// SUP: raise by 40% of ascender — sits clearly above the cap-height
|
||||
// SUB: lower by 25% of ascender — descends below baseline without clashing with ascenders below
|
||||
int wordY = y;
|
||||
if ((currentStyle & EpdFontFamily::SUP) != 0) {
|
||||
wordY -= ascender * 2 / 5;
|
||||
} else if ((currentStyle & EpdFontFamily::SUB) != 0) {
|
||||
wordY += ascender / 4;
|
||||
}
|
||||
renderer.drawText(fontId, wordX, wordY, words[i].c_str(), true, currentStyle);
|
||||
|
||||
const std::string& w = words[i];
|
||||
const bool hasEmSpacePrefix = w.size() >= 3 && static_cast<uint8_t>(w[0]) == 0xE2 &&
|
||||
|
||||
@@ -205,6 +205,8 @@ void ChapterHtmlSlimParser::updateEffectiveInlineStyle() {
|
||||
effectiveStrikethrough =
|
||||
currentCssStyle.hasTextDecoration() && (static_cast<uint8_t>(currentCssStyle.textDecoration) &
|
||||
static_cast<uint8_t>(CssTextDecoration::LineThrough)) != 0;
|
||||
effectiveSup = false;
|
||||
effectiveSub = false;
|
||||
|
||||
// Apply inline style stack in order
|
||||
for (const auto& entry : inlineStyleStack) {
|
||||
@@ -220,6 +222,14 @@ void ChapterHtmlSlimParser::updateEffectiveInlineStyle() {
|
||||
if (entry.hasStrikethrough) {
|
||||
effectiveStrikethrough = entry.strikethrough;
|
||||
}
|
||||
if (entry.hasSup) {
|
||||
effectiveSup = entry.sup;
|
||||
if (entry.sup) effectiveSub = false;
|
||||
}
|
||||
if (entry.hasSub) {
|
||||
effectiveSub = entry.sub;
|
||||
if (entry.sub) effectiveSup = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +290,11 @@ bool ChapterHtmlSlimParser::flushPartWordBuffer() {
|
||||
if (isStrikethrough) {
|
||||
fontStyle = static_cast<EpdFontFamily::Style>(fontStyle | EpdFontFamily::STRIKETHROUGH);
|
||||
}
|
||||
if (effectiveSup) {
|
||||
fontStyle = static_cast<EpdFontFamily::Style>(fontStyle | EpdFontFamily::SUP);
|
||||
} else if (effectiveSub) {
|
||||
fontStyle = static_cast<EpdFontFamily::Style>(fontStyle | EpdFontFamily::SUB);
|
||||
}
|
||||
|
||||
// flush the buffer — route to table cell text when inside a <td>/<th>
|
||||
partWordBuffer[partWordBufferIndex] = '\0';
|
||||
@@ -1168,12 +1183,17 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
||||
if (!self->flushPartWordBuffer()) return;
|
||||
self->nextWordContinues = true;
|
||||
}
|
||||
const char* prefix = (strcmp(name, "sup") == 0) ? "^" : "_";
|
||||
self->currentTextBlock->addWord(prefix, EpdFontFamily::REGULAR, false, true);
|
||||
self->nextWordContinues = true;
|
||||
StyleStackEntry entry;
|
||||
entry.depth = self->depth;
|
||||
if (strcmp(name, "sup") == 0) {
|
||||
entry.hasSup = true;
|
||||
entry.sup = true;
|
||||
} else {
|
||||
entry.hasSub = true;
|
||||
entry.sub = true;
|
||||
}
|
||||
self->inlineStyleStack.push_back(entry);
|
||||
self->updateEffectiveInlineStyle();
|
||||
} else if (strcmp(name, "span") == 0 || !isHeaderOrBlock(name)) {
|
||||
// Handle span and other inline elements for CSS styling
|
||||
if (cssStyle.hasFontWeight() || cssStyle.hasFontStyle() || cssStyle.hasTextDecoration()) {
|
||||
|
||||
@@ -66,6 +66,8 @@ class ChapterHtmlSlimParser final : public Print {
|
||||
bool hasItalic = false, italic = false;
|
||||
bool hasUnderline = false, underline = false;
|
||||
bool hasStrikethrough = false, strikethrough = false;
|
||||
bool hasSup = false, sup = false;
|
||||
bool hasSub = false, sub = false;
|
||||
};
|
||||
std::vector<StyleStackEntry> inlineStyleStack;
|
||||
CssStyle currentCssStyle;
|
||||
@@ -73,6 +75,8 @@ class ChapterHtmlSlimParser final : public Print {
|
||||
bool effectiveItalic = false;
|
||||
bool effectiveUnderline = false;
|
||||
bool effectiveStrikethrough = false;
|
||||
bool effectiveSup = false;
|
||||
bool effectiveSub = false;
|
||||
// Buffered table model — populated while inside <table>, emitted on </table>
|
||||
struct BufferedTableCell {
|
||||
std::unique_ptr<ParsedText> text;
|
||||
|
||||
Reference in New Issue
Block a user