is the same as
for us
case GUMBO_TAG_P: {
// cursor.insertBlock();
break;
}
case GUMBO_TAG_H1: {
// cursor.insertBlock();
cursor.setBlockFormat(text_style.heading_format);
cursor.setCharFormat(text_style.standard_h1);
break;
}
case GUMBO_TAG_H2: {
// cursor.insertBlock();
cursor.setBlockFormat(text_style.heading_format);
cursor.setCharFormat(text_style.standard_h2);
break;
}
case GUMBO_TAG_H3: {
// cursor.insertBlock();
cursor.setBlockFormat(text_style.heading_format);
cursor.setCharFormat(text_style.standard_h3);
break;
}
case GUMBO_TAG_PRE: {
// cursor.insertBlock();
cursor.setBlockFormat(text_style.preformatted_format);
cursor.setCharFormat(text_style.preformatted);
break;
}
case GUMBO_TAG_OL:
case GUMBO_TAG_UL: {
// cursor.insertBlock();
if(element.tag == GUMBO_TAG_OL) {
auto fmt = text_style.list_format;
fmt.setStyle(QTextListFormat::ListDecimal);
fmt.setNumberPrefix("");
fmt.setNumberSuffix(".");
cursor.createList(fmt);
}
else {
cursor.createList(text_style.list_format);
}
break;
}
case GUMBO_TAG_LI: {
break;
}
case GUMBO_TAG_BLOCKQUOTE: {
QTextTable *table = cursor.insertTable(1, 1,text_style.blockquote_tableformat);
cursor.setBlockFormat(text_style.blockquote_format);
QTextTableCell cell = table->cellAt(0, 0);
cell.setFormat(text_style.blockquote);
cursor.setCharFormat(text_style.blockquote);
break;
}
// Text modification elements:
case GUMBO_TAG_SPAN: {
// This usually has a style change, but we ignore that completly
break;
}
case GUMBO_TAG_BR: {
cursor.insertText("\n");
break;
}
case GUMBO_TAG_I: {
auto fmt = cursor.charFormat();
fmt.setFontItalic(true);
cursor.setCharFormat(fmt);
break;
}
case GUMBO_TAG_B: {
auto fmt = cursor.charFormat();
fmt.setFontWeight(QFont::Bold);
cursor.setCharFormat(fmt);
break;
}
case GUMBO_TAG_U: {
auto fmt = cursor.charFormat();
fmt.setFontUnderline(true);
cursor.setCharFormat(fmt);
break;
}
case GUMBO_TAG_A: {
char const * anchor = getAttribute(element, "href");
if(anchor == nullptr) {
anchor = "#";
}
auto fmt = text_style.standard_link;
fmt.setAnchor(true);
fmt.setAnchorHref(QString::fromUtf8(anchor));
cursor.setCharFormat(fmt);
break;
}
default:
qDebug() << "unhandled tag:" << gumbo_normalized_tagname(element.tag);
break;
}
for (size_t i = 0; i < element.children.length; ++i) {
GumboNode* child = (GumboNode*)element.children.data[i];
renderRecursive(state, *child, nesting + 1);
}
switch(element.tag) {
// case GUMBO_TAG_PRE: {
// // Set the last line of the preformatted block to have
// // standard line height.
// QTextBlockFormat fmt = cursor.blockFormat();
// fmt.setLineHeight(state.style->line_height_p, QTextBlockFormat::LineDistanceHeight);
// cursor.movePosition(QTextCursor::PreviousBlock);
// cursor.setBlockFormat(fmt);
// cursor.movePosition(QTextCursor::NextBlock);
// break;
// }
// Requires closing block
case GUMBO_TAG_PRE:
case GUMBO_TAG_P:
case GUMBO_TAG_DIV:
case GUMBO_TAG_H1:
case GUMBO_TAG_H2:
case GUMBO_TAG_H3:
cursor.insertBlock();
break;
case GUMBO_TAG_OL:
case GUMBO_TAG_UL:
// cursor.insertBlock();
break;
case GUMBO_TAG_LI:
// Terminate the
by pressing "enter"
cursor.insertBlock();
break;
case GUMBO_TAG_BLOCKQUOTE:
cursor.deletePreviousChar();
cursor.movePosition(QTextCursor::NextBlock);
break;
default: break;
}
// qDebug() << "end node(" << gumbo_normalized_tagname(element.tag) << ")";
break;
}
/** Text node. v will be a GumboText. */
case GUMBO_NODE_TEXT: {
auto const & text = node.v.text;
auto contents = QString::fromUtf8(text.text);
// qDebug() << contents;
QRegularExpression regex { "\\s+", QRegularExpression::DotMatchesEverythingOption };
// TODO: This is not quite right, but QTextCursor::inserText
// will insert spurious blocks when a "\n" is encountered.
state.cursor.insertText(contents.replace(regex, " "));
break;
}
/** CDATA node. v will be a GumboText. */
case GUMBO_NODE_CDATA: {
auto const & text = node.v.text;
auto const contents = QString::fromUtf8(text.text);
// TODO: This is not quite right, but QTextCursor::inserText
// will insert spurious blocks when a "\n" is encountered.
state.cursor.insertText(contents.trimmed());
break;
}
/** Comment node. v will be a GumboText, excluding comment delimiters. */
case GUMBO_NODE_COMMENT: {
// qDebug() << "comment(" << ")";
break;
}
/** Text node, where all contents is whitespace. v will be a GumboText. */
case GUMBO_NODE_WHITESPACE: {
// qDebug() << "whitespace(" << ")";
break;
}
/** Template node. This is separate from GUMBO_NODE_ELEMENT because many
* client libraries will want to ignore the contents of template nodes, as
* the spec suggests. Recursing on GUMBO_NODE_ELEMENT will do the right thing
* here, while clients that want to include template contents should also
* check for GUMBO_NODE_TEMPLATE. v will be a GumboElement. */
case GUMBO_NODE_TEMPLATE: {
qDebug() << "template(" << "???" << ")";
break;
}
}
}
std::unique_ptr HtmlRenderer::render(
QByteArray const &input,
QUrl const & root_url,
DocumentStyle const & style,
DocumentOutlineModel & outline,
QString & page_title)
{
std::unique_ptr gumbo_output {
gumbo_parse_with_options(&gumbo_options, input.data(), input.length()),
&destroyGumboOutput,
};
if(gumbo_output->errors.length > 0) {
qDebug() << "Parsing the html document yielded" << gumbo_output->errors.length << "errors!";
}
if(gumbo_output->root->type != GUMBO_NODE_ELEMENT) {
qWarning() << "html document has no proper root node!";
return nullptr;
}
auto doc = std::make_unique();
renderhelpers::setPageMargins(doc.get(), style.margin_h, style.margin_v);
doc->setIndentWidth(style.indent_size);
outline.beginBuild();
// Find page title
{
const char* title = find_title(gumbo_output->root);
if(title != nullptr) {
page_title = QString::fromUtf8(title);
}
}
{
GumboVector const * const root_children = &gumbo_output->root->v.element.children;
GumboNode* body = nullptr;
for (size_t i = 0; i < root_children->length; ++i) {
GumboNode* child = (GumboNode*)root_children->data[i];
if (child->type == GUMBO_NODE_ELEMENT and child->v.element.tag == GUMBO_TAG_BODY) {
body = child;
break;
}
}
if(body != nullptr)
{
RenderState state {
QTextCursor { doc.get() },
TextStyleInstance { style },
root_url,
&style,
&outline,
};
state.cursor.setBlockFormat(state.text_style.standard_format);
state.cursor.setCharFormat(state.text_style.standard);
renderRecursive(state, *body);
}
}
outline.endBuild();
return doc;
}