Merge pull request #221 from jpirnay/feat-hr-epub

feat: Add support for horizontal rulers
This commit is contained in:
jpirnay
2026-05-15 11:51:06 +02:00
committed by GitHub
9 changed files with 82 additions and 1 deletions
+23
View File
@@ -49,6 +49,25 @@ std::unique_ptr<PageImage> PageImage::deserialize(FsFile& file) {
return std::unique_ptr<PageImage>(new PageImage(std::move(ib), xPos, yPos));
}
void PageHR::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
renderer.drawLine(xPos + xOffset, yPos + yOffset, xPos + xOffset + width - 1, yPos + yOffset);
}
bool PageHR::serialize(FsFile& file) {
serialization::writePod(file, xPos);
serialization::writePod(file, yPos);
serialization::writePod(file, width);
return true;
}
std::unique_ptr<PageHR> PageHR::deserialize(FsFile& file) {
int16_t xPos, yPos, width;
serialization::readPod(file, xPos);
serialization::readPod(file, yPos);
serialization::readPod(file, width);
return std::unique_ptr<PageHR>(new PageHR(xPos, yPos, width));
}
void PageTableFragment::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
const int drawX = xPos + xOffset;
const int drawY = yPos + yOffset;
@@ -237,6 +256,10 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
auto pt = PageTableFragment::deserialize(file);
if (!pt) return nullptr;
page->elements.push_back(std::move(pt));
} else if (tag == TAG_PageHR) {
auto hr = PageHR::deserialize(file);
if (!hr) return nullptr;
page->elements.push_back(std::move(hr));
} else {
LOG_ERR("PGE", "Deserialization failed: Unknown tag %u", tag);
return nullptr;
+12
View File
@@ -21,6 +21,7 @@ enum PageElementTag : uint8_t {
TAG_PageLine = 1,
TAG_PageImage = 2,
TAG_PageTable = 3,
TAG_PageHR = 4,
};
// represents something that has been added to a page
@@ -63,6 +64,17 @@ class PageImage final : public PageElement {
const ImageBlock& getImageBlock() const { return *imageBlock; }
};
class PageHR final : public PageElement {
int16_t width;
public:
PageHR(const int16_t xPos, const int16_t yPos, const int16_t width) : PageElement(xPos, yPos), width(width) {}
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;
bool serialize(FsFile& file) override;
PageElementTag getTag() const override { return TAG_PageHR; }
static std::unique_ptr<PageHR> deserialize(FsFile& file);
};
struct TableCell {
std::vector<std::shared_ptr<TextBlock>> lines;
bool isHeader = false;
+1 -1
View File
@@ -14,7 +14,7 @@
#include "parsers/ChapterHtmlSlimParser.h"
namespace {
constexpr uint8_t SECTION_FILE_VERSION = 27;
constexpr uint8_t SECTION_FILE_VERSION = 28;
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + // SECTION_FILE_VERSION
sizeof(int) + // fontId
sizeof(float) + // lineCompression
@@ -1049,6 +1049,28 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
self->preUntilDepth = std::min(self->preUntilDepth, self->depth);
}
}
} else if (strcmp(name, "hr") == 0) {
if (self->partWordBufferIndex > 0) {
if (!self->flushPartWordBuffer()) return;
}
self->makePages();
if (!self->currentPage) {
self->currentPage.reset(new Page());
self->currentPageNextY = 0;
}
const int lineHeight = static_cast<int>(self->renderer.getLineHeight(self->fontId) * self->lineCompression + 0.5f);
const int16_t marginV = static_cast<int16_t>(lineHeight / 2);
self->currentPageNextY += marginV;
if (self->currentPageNextY + 1 + marginV > self->viewportHeight) {
self->emitPage(self->lastBodyChildByteOffset);
self->currentPage.reset(new Page());
self->currentPageNextY = 0;
}
self->currentPage->elements.push_back(
std::make_shared<PageHR>(0, self->currentPageNextY, static_cast<int16_t>(self->viewportWidth)));
self->currentPageNextY += 1 + marginV;
BlockStyle emptyStyle;
self->startNewTextBlock(emptyStyle);
} else if (matches(name, UNDERLINE_TAGS, NUM_UNDERLINE_TAGS) ||
matches(name, STRIKETHROUGH_TAGS, NUM_STRIKETHROUGH_TAGS)) {
// Flush buffer before style change so preceding text gets current style