Add support for horizontal rulers
This commit is contained in:
@@ -49,6 +49,25 @@ std::unique_ptr<PageImage> PageImage::deserialize(FsFile& file) {
|
|||||||
return std::unique_ptr<PageImage>(new PageImage(std::move(ib), xPos, yPos));
|
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) {
|
void PageTableFragment::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
|
||||||
const int drawX = xPos + xOffset;
|
const int drawX = xPos + xOffset;
|
||||||
const int drawY = yPos + yOffset;
|
const int drawY = yPos + yOffset;
|
||||||
@@ -237,6 +256,10 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
|||||||
auto pt = PageTableFragment::deserialize(file);
|
auto pt = PageTableFragment::deserialize(file);
|
||||||
if (!pt) return nullptr;
|
if (!pt) return nullptr;
|
||||||
page->elements.push_back(std::move(pt));
|
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 {
|
} else {
|
||||||
LOG_ERR("PGE", "Deserialization failed: Unknown tag %u", tag);
|
LOG_ERR("PGE", "Deserialization failed: Unknown tag %u", tag);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ enum PageElementTag : uint8_t {
|
|||||||
TAG_PageLine = 1,
|
TAG_PageLine = 1,
|
||||||
TAG_PageImage = 2,
|
TAG_PageImage = 2,
|
||||||
TAG_PageTable = 3,
|
TAG_PageTable = 3,
|
||||||
|
TAG_PageHR = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
// represents something that has been added to a page
|
// represents something that has been added to a page
|
||||||
@@ -63,6 +64,17 @@ class PageImage final : public PageElement {
|
|||||||
const ImageBlock& getImageBlock() const { return *imageBlock; }
|
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 {
|
struct TableCell {
|
||||||
std::vector<std::shared_ptr<TextBlock>> lines;
|
std::vector<std::shared_ptr<TextBlock>> lines;
|
||||||
bool isHeader = false;
|
bool isHeader = false;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#include "parsers/ChapterHtmlSlimParser.h"
|
#include "parsers/ChapterHtmlSlimParser.h"
|
||||||
|
|
||||||
namespace {
|
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
|
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + // SECTION_FILE_VERSION
|
||||||
sizeof(int) + // fontId
|
sizeof(int) + // fontId
|
||||||
sizeof(float) + // lineCompression
|
sizeof(float) + // lineCompression
|
||||||
|
|||||||
@@ -1049,6 +1049,26 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
|
|||||||
self->preUntilDepth = std::min(self->preUntilDepth, self->depth);
|
self->preUntilDepth = std::min(self->preUntilDepth, self->depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(name, "hr") == 0) {
|
||||||
|
if (self->partWordBufferIndex > 0) self->flushPartWordBuffer();
|
||||||
|
self->makePages();
|
||||||
|
if (!self->currentPage) {
|
||||||
|
self->currentPage.reset(new Page());
|
||||||
|
self->currentPageNextY = 0;
|
||||||
|
}
|
||||||
|
const int lineHeight = self->renderer.getLineHeight(self->fontId) * self->lineCompression;
|
||||||
|
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) ||
|
} else if (matches(name, UNDERLINE_TAGS, NUM_UNDERLINE_TAGS) ||
|
||||||
matches(name, STRIKETHROUGH_TAGS, NUM_STRIKETHROUGH_TAGS)) {
|
matches(name, STRIKETHROUGH_TAGS, NUM_STRIKETHROUGH_TAGS)) {
|
||||||
// Flush buffer before style change so preceding text gets current style
|
// Flush buffer before style change so preceding text gets current style
|
||||||
|
|||||||
@@ -1014,6 +1014,7 @@ def main():
|
|||||||
<li>pre element: intrinsic line breaks preserved</li>
|
<li>pre element: intrinsic line breaks preserved</li>
|
||||||
<li>pre element: leading/trailing blank lines</li>
|
<li>pre element: leading/trailing blank lines</li>
|
||||||
<li>pre with inline code element</li>
|
<li>pre with inline code element</li>
|
||||||
|
<li>horizontal rules between paragraphs</li>
|
||||||
</ul>
|
</ul>
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
@@ -1061,6 +1062,29 @@ Fifth line (blank line above)</pre>
|
|||||||
|
|
||||||
greet("World");</code></pre>
|
greet("World");</code></pre>
|
||||||
<p>Normal paragraph after pre/code block.</p>
|
<p>Normal paragraph after pre/code block.</p>
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"4. Horizontal Rules",
|
||||||
|
make_chapter(
|
||||||
|
"Horizontal Rule Tests",
|
||||||
|
"""
|
||||||
|
<p>A plain <hr> between two paragraphs. A thin line should appear between the two blocks of text.</p>
|
||||||
|
<p>Paragraph before the first rule. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
||||||
|
<hr/>
|
||||||
|
<p>Paragraph after the first rule. The rule above should be a full-width horizontal line.</p>
|
||||||
|
<hr/>
|
||||||
|
<p>Second rule above. Two rules in a row with no text between them:</p>
|
||||||
|
<hr/>
|
||||||
|
<hr/>
|
||||||
|
<p>Two rules appeared above. Now a rule at the very start of a section (right after the heading):</p>
|
||||||
|
<hr/>
|
||||||
|
<p>Rule appeared right after the heading. Finally, a rule near the end of the page to verify it does not cause a spurious page break when there is still room:</p>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
|
<hr/>
|
||||||
|
<p>End of horizontal rule tests.</p>
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
[],
|
[],
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user