mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
acceleration of function prettyprint
This commit is contained in:
@ -3,9 +3,8 @@
|
||||
|
||||
#include <string>
|
||||
#include "gumbo-parser/src/gumbo.h"
|
||||
#include "../../../DesktopEditor/xml/include/xmlutils.h"
|
||||
#include "../../../DesktopEditor/xml/include/xmlencoding.h"
|
||||
#include "../../../DesktopEditor/common/File.h"
|
||||
#include "../../../DesktopEditor/common/StringBuilder.h"
|
||||
#include "../../../UnicodeConverter/UnicodeConverter.h"
|
||||
|
||||
#ifndef HTMLTOXHTML_USE_DYNAMIC_LIBRARY
|
||||
@ -22,7 +21,7 @@ static std::string special_handling = "|html|body|";
|
||||
static std::string no_entity_sub = "|script|style|";
|
||||
static std::string treat_like_inline = "|p|";
|
||||
|
||||
static std::string prettyprint(GumboNode*, int lvl, const std::string indent_chars);
|
||||
static std::string prettyprint(GumboNode*);
|
||||
|
||||
static std::wstring htmlToXhtml(const std::wstring& sFile)
|
||||
{
|
||||
@ -46,6 +45,7 @@ static std::wstring htmlToXhtml(const std::wstring& sFile)
|
||||
std::string sEncoding = sFileContent.substr(posEncoding, posEnd - posEncoding);
|
||||
if (sEncoding != "utf-8" && sEncoding != "UTF-8")
|
||||
{
|
||||
sFileContent.replace(posEncoding, posEnd - posEncoding, "UTF-8");
|
||||
NSUnicodeConverter::CUnicodeConverter oConverter;
|
||||
sRes = oConverter.toUnicode(sFileContent, sEncoding.c_str());
|
||||
sFileContent = U_TO_UTF8(sRes);
|
||||
@ -56,83 +56,64 @@ static std::wstring htmlToXhtml(const std::wstring& sFile)
|
||||
GumboOutput* output = gumbo_parse_with_options(&options, sFileContent.data(), sFileContent.length());
|
||||
|
||||
// prettyprint
|
||||
std::string indent_chars = " ";
|
||||
std::string sR = prettyprint(output->document, 0, indent_chars);
|
||||
std::string sR = prettyprint(output->document);
|
||||
|
||||
// Вставка кодировки в файл
|
||||
if(sR.length() > 5)
|
||||
{
|
||||
std::string sSub = sR.substr(0, 5);
|
||||
if(sSub != "<?xml")
|
||||
sR = "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" + sR;
|
||||
sR = "<?xml version=\'1.0\' encoding=\'UTF-8\'?>" + sR;
|
||||
}
|
||||
|
||||
// Конвертирование из string utf8 в wstring
|
||||
return UTF8_TO_U(sR);
|
||||
}
|
||||
|
||||
// Убирает в конце бесполезные символы
|
||||
static inline void rtrim(std::string& s)
|
||||
{
|
||||
s.erase(s.find_last_not_of(" \n\r\t") + 1);
|
||||
}
|
||||
|
||||
// Убирает в начале бесполезные символы
|
||||
static inline void ltrim(std::string& s)
|
||||
{
|
||||
s.erase(0,s.find_first_not_of(" \n\r\t"));
|
||||
}
|
||||
|
||||
// Заменяет в строке s все символы s1 на s2
|
||||
static void replace_all(std::string& s, const char* s1, const char* s2)
|
||||
static void replace_all(std::string& s, std::string s1, std::string s2)
|
||||
{
|
||||
std::string t1(s1);
|
||||
size_t len = t1.length();
|
||||
size_t pos = s.find(t1);
|
||||
size_t len = s1.length();
|
||||
size_t pos = s.find(s1);
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
s.replace(pos, len, s2);
|
||||
pos = s.find(t1, pos + len);
|
||||
pos = s.find(s1, pos + len);
|
||||
}
|
||||
}
|
||||
|
||||
// Заменяет сущности &,<,> в text
|
||||
static std::string substitute_xml_entities_into_text(const std::string &text)
|
||||
static void substitute_xml_entities_into_text(std::string &text)
|
||||
{
|
||||
std::string result = text;
|
||||
// replacing & must come first
|
||||
replace_all(result, "&", "&");
|
||||
replace_all(result, "<", "<");
|
||||
replace_all(result, ">", ">");
|
||||
return result;
|
||||
replace_all(text, "&", "&");
|
||||
replace_all(text, "<", "<");
|
||||
replace_all(text, ">", ">");
|
||||
}
|
||||
|
||||
// Заменяет сущности ",' в text
|
||||
static std::string substitute_xml_entities_into_attributes(char quote, const std::string &text)
|
||||
static void substitute_xml_entities_into_attributes(char quote, std::string &text)
|
||||
{
|
||||
std::string result = substitute_xml_entities_into_text(text);
|
||||
substitute_xml_entities_into_text(text);
|
||||
if (quote == '"')
|
||||
replace_all(result,"\"",""");
|
||||
replace_all(text, "\"", """);
|
||||
else if (quote == '\'')
|
||||
replace_all(result,"'","'");
|
||||
return result;
|
||||
replace_all(text, "'", "'");
|
||||
}
|
||||
|
||||
|
||||
static std::string handle_unknown_tag(GumboStringPiece *text)
|
||||
static std::string handle_unknown_tag(GumboStringPiece* text)
|
||||
{
|
||||
std::string tagname = "";
|
||||
if (text->data == NULL) {
|
||||
return tagname;
|
||||
return "";
|
||||
}
|
||||
GumboStringPiece gsp = *text;
|
||||
gumbo_tag_from_original_text(&gsp);
|
||||
tagname = std::string(gsp.data, gsp.length);
|
||||
return tagname;
|
||||
return std::string(gsp.data, gsp.length);
|
||||
}
|
||||
|
||||
|
||||
static std::string get_tag_name(GumboNode *node)
|
||||
static std::string get_tag_name(GumboNode* node)
|
||||
{
|
||||
std::string tagname = (node->type == GUMBO_NODE_DOCUMENT ? "document" : gumbo_normalized_tagname(node->v.element.tag));
|
||||
if (tagname.empty())
|
||||
@ -141,65 +122,59 @@ static std::string get_tag_name(GumboNode *node)
|
||||
}
|
||||
|
||||
|
||||
static std::string build_doctype(GumboNode *node)
|
||||
static void build_doctype(GumboNode* node, NSStringUtils::CStringBuilderA& oBuilder)
|
||||
{
|
||||
std::string results = "";
|
||||
if (node->v.document.has_doctype)
|
||||
{
|
||||
results.append("<!DOCTYPE ");
|
||||
results.append(node->v.document.name);
|
||||
oBuilder.WriteString("<!DOCTYPE ");
|
||||
oBuilder.WriteString(node->v.document.name);
|
||||
std::string pi(node->v.document.public_identifier);
|
||||
if ((node->v.document.public_identifier != NULL) && !pi.empty())
|
||||
{
|
||||
results.append(" PUBLIC \"");
|
||||
results.append(node->v.document.public_identifier);
|
||||
results.append("\" \"");
|
||||
results.append(node->v.document.system_identifier);
|
||||
results.append("\"");
|
||||
oBuilder.WriteString(" PUBLIC \"");
|
||||
oBuilder.WriteString(pi);
|
||||
oBuilder.WriteString("\" \"");
|
||||
oBuilder.WriteString(node->v.document.system_identifier);
|
||||
oBuilder.WriteString("\"");
|
||||
}
|
||||
results.append(">\n");
|
||||
oBuilder.WriteString(">");
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
static std::string build_attributes(GumboAttribute * at, bool no_entities)
|
||||
static void build_attributes(GumboAttribute* at, bool no_entities, NSStringUtils::CStringBuilderA& atts)
|
||||
{
|
||||
std::string atts = "";
|
||||
atts.append(" ");
|
||||
atts.append(at->name);
|
||||
|
||||
// how do we want to handle attributes with empty values
|
||||
// <input type="checkbox" checked /> or <input type="checkbox" checked="" />
|
||||
|
||||
if ((!std::string(at->value).empty()) ||
|
||||
(at->original_value.data[0] == '"') ||
|
||||
(at->original_value.data[0] == '\''))
|
||||
std::string sVal(at->value);
|
||||
char quote = at->original_value.data[0];
|
||||
if ((!sVal.empty()) || (quote == '"') || (quote == '\''))
|
||||
{
|
||||
// determine original quote character used if it exists
|
||||
char quote = at->original_value.data[0];
|
||||
std::string qs = "";
|
||||
if (quote == '\'') qs = std::string("'");
|
||||
if (quote == '"') qs = std::string("\"");
|
||||
atts.WriteString(" ");
|
||||
atts.WriteString(at->name);
|
||||
|
||||
atts.append("=");
|
||||
atts.append(qs);
|
||||
atts.append(no_entities ? at->value : substitute_xml_entities_into_attributes(quote, std::string(at->value)));
|
||||
atts.append(qs);
|
||||
// determine original quote character used if it exists
|
||||
std::string qs = "";
|
||||
if (quote == '\'')
|
||||
qs = "'";
|
||||
else if (quote == '"')
|
||||
qs = "\"";
|
||||
|
||||
atts.WriteString("=");
|
||||
atts.WriteString(qs);
|
||||
if(!no_entities)
|
||||
substitute_xml_entities_into_attributes(quote, sVal);
|
||||
atts.WriteString(sVal);
|
||||
atts.WriteString(qs);
|
||||
}
|
||||
return atts;
|
||||
}
|
||||
|
||||
|
||||
static std::string prettyprint_contents(GumboNode* node, int lvl, const std::string indent_chars)
|
||||
static std::string prettyprint_contents(GumboNode* node)
|
||||
{
|
||||
std::string contents = "";
|
||||
std::string tagname = get_tag_name(node);
|
||||
std::string key = "|" + tagname + "|";
|
||||
NSStringUtils::CStringBuilderA contents;
|
||||
std::string key = "|" + get_tag_name(node) + "|";
|
||||
bool no_entity_substitution = no_entity_sub.find(key) != std::string::npos;
|
||||
bool keep_whitespace = preserve_whitespace.find(key) != std::string::npos;
|
||||
bool is_inline = nonbreaking_inline.find(key) != std::string::npos;
|
||||
bool pp_okay = !is_inline && !keep_whitespace;
|
||||
|
||||
GumboVector* children = &node->v.element.children;
|
||||
|
||||
@ -209,79 +184,58 @@ static std::string prettyprint_contents(GumboNode* node, int lvl, const std::str
|
||||
|
||||
if (child->type == GUMBO_NODE_TEXT)
|
||||
{
|
||||
std::string val = (no_entity_substitution ? std::string(child->v.text.text) : substitute_xml_entities_into_text(std::string(child->v.text.text)));
|
||||
|
||||
if (pp_okay)
|
||||
rtrim(val);
|
||||
if (pp_okay && (contents.length() == 0))
|
||||
{
|
||||
// add required indentation
|
||||
char c = indent_chars.at(0);
|
||||
int n = indent_chars.length();
|
||||
contents.append(std::string((lvl - 1) * n, c));
|
||||
}
|
||||
contents.append(val);
|
||||
std::string val(child->v.text.text);
|
||||
if(!no_entity_substitution)
|
||||
substitute_xml_entities_into_text(val);
|
||||
contents.WriteString(val);
|
||||
}
|
||||
else if ((child->type == GUMBO_NODE_ELEMENT) || (child->type == GUMBO_NODE_TEMPLATE))
|
||||
{
|
||||
std::string val = prettyprint(child, lvl, indent_chars);
|
||||
|
||||
// remove any indentation if this child is inline and not first child
|
||||
std::string childname = get_tag_name(child);
|
||||
std::string childkey = "|" + childname + "|";
|
||||
if ((nonbreaking_inline.find(childkey) != std::string::npos) && (contents.length() > 0))
|
||||
ltrim(val);
|
||||
|
||||
contents.append(val);
|
||||
|
||||
std::string val = prettyprint(child);
|
||||
contents.WriteString(val);
|
||||
}
|
||||
else if (child->type == GUMBO_NODE_WHITESPACE)
|
||||
{
|
||||
if (keep_whitespace || is_inline)
|
||||
contents.append(std::string(child->v.text.text));
|
||||
contents.WriteString(child->v.text.text);
|
||||
}
|
||||
else if (child->type != GUMBO_NODE_COMMENT)
|
||||
{
|
||||
// Сообщение об ошибке
|
||||
// Does this actually exist: (child->type == GUMBO_NODE_CDATA)
|
||||
fprintf(stderr, "unknown element of type: %d\n", child->type);
|
||||
// fprintf(stderr, "unknown element of type: %d\n", child->type);
|
||||
}
|
||||
}
|
||||
|
||||
return contents;
|
||||
return contents.GetData();
|
||||
}
|
||||
|
||||
|
||||
static std::string prettyprint(GumboNode* node, int lvl, const std::string indent_chars)
|
||||
static std::string prettyprint(GumboNode* node)
|
||||
{
|
||||
NSStringUtils::CStringBuilderA oBuilder;
|
||||
// special case the document node
|
||||
if (node->type == GUMBO_NODE_DOCUMENT)
|
||||
{
|
||||
std::string results = build_doctype(node);
|
||||
results.append(prettyprint_contents(node,lvl+1,indent_chars));
|
||||
return results;
|
||||
build_doctype(node, oBuilder);
|
||||
oBuilder.WriteString(prettyprint_contents(node));
|
||||
return oBuilder.GetData();
|
||||
}
|
||||
|
||||
std::string close = "";
|
||||
std::string closeTag = "";
|
||||
std::string atts = "";
|
||||
NSStringUtils::CStringBuilderA atts;
|
||||
std::string tagname = get_tag_name(node);
|
||||
std::string key = "|" + tagname + "|";
|
||||
bool need_special_handling = special_handling.find(key) != std::string::npos;
|
||||
bool is_empty_tag = empty_tags.find(key) != std::string::npos;
|
||||
bool no_entity_substitution = no_entity_sub.find(key) != std::string::npos;
|
||||
bool keep_whitespace = preserve_whitespace.find(key) != std::string::npos;
|
||||
bool is_inline = nonbreaking_inline.find(key) != std::string::npos;
|
||||
bool inline_like = treat_like_inline.find(key) != std::string::npos;
|
||||
bool pp_okay = !is_inline && !keep_whitespace;
|
||||
char c = indent_chars.at(0);
|
||||
int n = indent_chars.length();
|
||||
|
||||
// build attr string
|
||||
const GumboVector * attribs = &node->v.element.attributes;
|
||||
for (int i = 0; i < attribs->length; ++i)
|
||||
{
|
||||
GumboAttribute* at = static_cast<GumboAttribute*>(attribs->data[i]);
|
||||
atts.append(build_attributes(at, no_entity_substitution));
|
||||
build_attributes(at, no_entity_substitution, atts);
|
||||
}
|
||||
|
||||
// determine closing tag type
|
||||
@ -290,40 +244,20 @@ static std::string prettyprint(GumboNode* node, int lvl, const std::string inden
|
||||
else
|
||||
closeTag = "</" + tagname + ">";
|
||||
|
||||
std::string indent_space = std::string((lvl - 1) * n, c);
|
||||
|
||||
// prettyprint your contents
|
||||
std::string contents = prettyprint_contents(node, lvl + 1, indent_chars);
|
||||
|
||||
if (need_special_handling)
|
||||
{
|
||||
rtrim(contents);
|
||||
contents.append("\n");
|
||||
}
|
||||
std::string contents = prettyprint_contents(node);
|
||||
|
||||
char last_char = ' ';
|
||||
if (!contents.empty())
|
||||
last_char = contents.at(contents.length() - 1);
|
||||
|
||||
// build results
|
||||
std::string results;
|
||||
if (pp_okay)
|
||||
results.append(indent_space);
|
||||
results.append("<" + tagname + atts + close + ">");
|
||||
if (pp_okay && !inline_like)
|
||||
results.append("\n");
|
||||
if (inline_like)
|
||||
ltrim(contents);
|
||||
results.append(contents);
|
||||
if (pp_okay && !contents.empty() && (last_char != '\n') && (!inline_like))
|
||||
results.append("\n");
|
||||
if (pp_okay && !inline_like && !closeTag.empty())
|
||||
results.append(indent_space);
|
||||
results.append(closeTag);
|
||||
if (pp_okay && !closeTag.empty())
|
||||
results.append("\n");
|
||||
NSStringUtils::CStringBuilderA results;
|
||||
results.WriteString("<" + tagname + atts.GetData() + close + ">");
|
||||
results.WriteString(contents);
|
||||
results.WriteString(closeTag);
|
||||
|
||||
return results;
|
||||
return results.GetData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
16
HtmlFile2/examples/test3.html
Normal file
16
HtmlFile2/examples/test3.html
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<html xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fb="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Последнее желание</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="page_styles.css" rel="stylesheet" type="text/css"/>
|
||||
</head>
|
||||
<body class="calibre">
|
||||
<h1 id="calibre_toc_2" class="calibre7"><a id="TOC_idm140435370588032" class="calibre9"></a><a class="calibre9" id="TOC_idm140435370587904"></a>ГЛАС РАССУДКА I <br class="calibre8"/>
|
||||
</h1><div class="paragraph">Она пришла под утро.</div>
|
||||
<div class="paragraph">Вошла осторожно, тихо, бесшумно ступая, плывя по комнате, словно призрак, привидение, а единственным звуком, выдававшим ее движение, был шорох накидки, прикасавшейся к голому телу. Однако именно этот исчезающе тихий, едва уловимый шелест разбудил ведьмака, а может, только вырвал из полусна, в котором он мерно колыхался, словно погруженный в бездонную топь, висящий между дном и поверхностью спокойного моря, среди легонько извивающихся нитей водорослей.</div>
|
||||
<div class="paragraph">Он не пошевелился, даже не дрогнул. Девушка подпорхнула ближе, сбросила накидку, медленно, нерешительно оперлась коленом о край ложа. Он наблюдал за ней из-под опущенных ресниц, не выдавая себя. Девушка осторожно поднялась на постель, легла на него, обхватила бедрами. Опираясь на напряженные руки, скользнула по его лицу волосами. Волосы пахли ромашкой. Решительно и как бы нетерпеливо наклонилась, коснулась сосочком его века, щеки, губ. Он улыбнулся, медленно, осторожно, нежно взял ее руки в свои. Она выпрямилась, ускользая от его пальцев, лучистая, подсвеченная и от этого света нечеткая в туманном отблеске зари. Он пошевелился, но она решительным нажимом обеих рук остановила его и легкими, но настойчивыми движениями бедер добилась ответа.</div>
|
||||
<div class="paragraph">Он ответил. Она уже не избегала его рук, откинула голову, встряхнула волосами. Ее кожа была холодной и поразительно гладкой. Глаза, которые он увидел, когда она приблизила свое лицо к его лицу, были огромными и темными, как глаза русалки.</div>
|
||||
<div class="paragraph">Покачиваясь, он утонул в ромашковом море, а оно взбурлило и зашумело, потеряв покой.</div>
|
||||
</body></html>
|
||||
@ -3,6 +3,7 @@
|
||||
#include "../../Common/3dParty/html/htmltoxhtml.h"
|
||||
#include "../../DesktopEditor/common/File.h"
|
||||
#include "../../DesktopEditor/common/Directory.h"
|
||||
#include "../../DesktopEditor/xml/include/xmlutils.h"
|
||||
|
||||
void readFile( XmlUtils::CXmlLiteReader& oLightReader)
|
||||
{
|
||||
@ -14,7 +15,7 @@ void readFile( XmlUtils::CXmlLiteReader& oLightReader)
|
||||
int main()
|
||||
{
|
||||
// Файл, который открываем
|
||||
std::wstring sFile = NSFile::GetProcessDirectory() + L"/../../../examples/test2.html";
|
||||
std::wstring sFile = NSFile::GetProcessDirectory() + L"/../../../examples/test3.html";
|
||||
|
||||
// Директория, где будем создавать xhtml
|
||||
std::wstring sOutputDirectory = NSFile::GetProcessDirectory() + L"/res";
|
||||
@ -34,8 +35,8 @@ int main()
|
||||
bool bRes = oLightReader.FromFile(sOutputDirectory + L"/res.xhtml");
|
||||
if(bRes)
|
||||
{
|
||||
// readFile(oLightReader);
|
||||
std::cout << (oLightReader.ReadTillEnd() ? "Success" : "Failure") << std::endl;
|
||||
readFile(oLightReader);
|
||||
std::cout << "Success" << std::endl;
|
||||
}
|
||||
else
|
||||
std::cout << "Failure" << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user