#include "htmlrenderer.hpp"
#include "renderhelpers.hpp"
#include "textstyleinstance.hpp"
#include "gumbo.h"
#include "kristall.hpp"
#include
#include
#include
#include
#include
static void* malloc_wrapper(void*, size_t size) { return malloc(size); }
static void free_wrapper(void*, void* ptr) { free(ptr); }
static GumboOptions const gumbo_options = {
&malloc_wrapper, &free_wrapper, // memory management
nullptr, // user pointer
4, // tab width
false, // stop on first error
-1, // maximum numbers of errors (-1 = infinite)
GUMBO_TAG_LAST,
GUMBO_NAMESPACE_HTML
};
static void destroyGumboOutput(GumboOutput * output)
{
gumbo_destroy_output(&gumbo_options, output);
}
static const char* find_title(const GumboNode* root) {
assert(root->type == GUMBO_NODE_ELEMENT);
if(root->v.element.children.length < 2)
return nullptr;
const GumboVector* root_children = &root->v.element.children;
GumboNode* head = 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_HEAD) {
head = child;
break;
}
}
if(head == nullptr)
return nullptr;
GumboVector* head_children = &head->v.element.children;
for (size_t i = 0; i < head_children->length; ++i) {
GumboNode* child = (GumboNode*)head_children->data[i];
if (child->type == GUMBO_NODE_ELEMENT and child->v.element.tag == GUMBO_TAG_TITLE) {
if (child->v.element.children.length != 1) {
return "";
}
GumboNode* title_text = (GumboNode*)child->v.element.children.data[0];
if(title_text->type == GUMBO_NODE_TEXT or title_text->type == GUMBO_NODE_WHITESPACE)
return title_text->v.text.text;
return nullptr;
}
}
return nullptr;
}
struct RenderState
{
QString & stream;
QUrl root_url;
DocumentOutlineModel & outline;
//! when non-null, we're inside a header element and accumulate the text to
//! compute the outline.
QString * header_text;
int header_count;
};
static char const * getAttribute(GumboElement const & element, char const * attrib_name)
{
for(size_t i = 0; i < element.attributes.length; i++)
{
auto const attrib = static_cast(element.attributes.data[i]);
if(strcmp(attrib->name, attrib_name) == 0)
return attrib->value;
}
return nullptr;
}
static void renderRecursive(RenderState & state, GumboNode const & node, int nesting = 0)
{
auto & stream = state.stream;
auto & outline = state.outline;
switch(node.type)
{
/** Document node. v will be a GumboDocument. */
case GUMBO_NODE_DOCUMENT: {
qWarning() << "Detected embedded document";
break;
}
/** Element node.v will be a GumboElement. */
case GUMBO_NODE_ELEMENT: {
auto const & element = node.v.element;
// qDebug() << "begin node(" << gumbo_normalized_tagname(element.tag) << ")";
bool process_header = false;
QString header_text;
switch(element.tag) {
// Stripped tags
case GUMBO_TAG_STYLE:
case GUMBO_TAG_SCRIPT:
case GUMBO_TAG_UNKNOWN:
return;
case GUMBO_TAG_BR:
stream += "
";
return;
case GUMBO_TAG_HR:
// HACK: stream += "
";
stream += "
";
return;
case GUMBO_TAG_NAV: {
if(kristall::globals().options.strip_nav)
return;
stream += "