%{
#include <memory>
+#include <libxml/globals.h>
#include "../c++11/xml-template.h"
char *str = Z_STRVAL_P(obj);
return new Replace(str);
}
+ case IS_LONG: {
+ char str[256];
+ snprintf(str, sizeof(str), "%ld", Z_LVAL_P(obj));
+ return new Replace(str);
+ }
+ case IS_DOUBLE: {
+ char str[256];
+ snprintf(str, sizeof(str), "%f", Z_DVAL_P(obj));
+ return new Replace(str);
+ }
case IS_RESOURCE: {
XmlDocPtrWrapper *doc;
if (SWIG_ConvertPtr(obj, (void **)&doc, SWIGTYPE_p_XmlDocPtrWrapper, 0) < 0 || doc == NULL) {
return NULL;
}
- return new ReplaceInclude(xmlCopyDoc(doc->get()->ptr, 1));
+ return new ReplaceInclude(xmlCopyDoc((*doc)->ptr, 1));
}
case IS_NULL:
- return NULL;
+ return new Replace { "" };
default:
printf("WARNING: Unknown type %d!\n", Z_TYPE_P(obj));
break;
return NULL;
}
-XmlDocPtrWrapper XML_Template_process_file(const char *input_filename, Directive *root_directive, bool clean)
+XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean)
{
xmlDocPtr ret = process_file(input_filename, root_directive, clean);
delete root_directive;
return XmlDocPtrWrapper(new XmlDocWrapper { ret });
}
+void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean)
+{
+ root_directive->process(xmlDocGetRootElement(doc->ptr), clean);
+ delete root_directive;
+}
+
namespace {
int write_to_string(void *context, const char *buffer, int len)
} // namespace
-std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc)
+std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint)
{
+ xmlIndentTreeOutput = prettyprint;
std::string ret;
xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(write_to_string, close_string, &ret, NULL);
- xmlSaveFileTo(buf, doc->ptr, NULL);
+ xmlSaveFormatFileTo(buf, doc->ptr, "UTF-8", prettyprint);
return ret;
}
-
+
+namespace {
+
+// Remove document fragments (ie. move their content up in the parent node)
+// and combine neighboring text nodes into one.
+void normalize_node(xmlNodePtr node)
+{
+ xmlNode *next_child;
+ for (xmlNode *child = node->children; child != NULL; child = next_child) {
+ next_child = child->next;
+ if (child->type == XML_DOCUMENT_FRAG_NODE) {
+ while (child->children != NULL) {
+ xmlAddPrevSibling(child, child->children);
+ }
+
+ xmlUnlinkNode(child);
+ xmlFreeNode(child);
+ }
+ }
+
+ // xmlAddPrevSibling merges adjacent text nodes, but many other things
+ // (including xmlUnlinkNode) do not, so make an extra pass.
+ for (xmlNode *child = node->children; child != NULL; child = child->next) {
+ while (child->type == XML_TEXT_NODE && (child->next != NULL && child->next->type == XML_TEXT_NODE)) {
+ xmlNode *next_child = child->next;
+
+ xmlChar *content = xmlNodeGetContent(next_child);
+ xmlNodeAddContent(child, content);
+ xmlFree(content);
+
+ xmlUnlinkNode(next_child);
+ xmlFreeNode(next_child);
+ }
+ normalize_node(child);
+ }
+}
+
+// Clean the page of non-necessary whitespace. Leaves whitespace alone if and
+// only if xml:space="preserve" on the element. (IOW, it doesn't parse the DTDs,
+// nor the CSS.)
+void clean_node(xmlNodePtr node, bool preserve_whitespace, bool aggressive)
+{
+ if (node->type == XML_TEXT_NODE) {
+ std::string content = reinterpret_cast<const char *>(xmlNodeGetContent(node));
+ if (!preserve_whitespace) {
+ unsigned dstpos = 0;
+ for (unsigned srcpos = 0; srcpos < content.size(); ++srcpos, ++dstpos) {
+ if (content[srcpos] == '\n' ||
+ content[srcpos] == '\t' ||
+ content[srcpos] == ' ') {
+ content[dstpos] = ' ';
+
+ // compress double spaces
+ if (dstpos > 0 && content[dstpos - 1] == ' ') {
+ --dstpos;
+ }
+ } else {
+ content[dstpos] = content[srcpos];
+ }
+ }
+ content.resize(dstpos);
+ }
+ if (content.empty() || (aggressive && content == " ")) {
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ } else {
+ xmlNodeSetContentLen(node, reinterpret_cast<const xmlChar *>(content.data()), content.size());
+ }
+ } else {
+ if (node->type == XML_ELEMENT_NODE) {
+ xmlChar *space = xmlGetProp(node, reinterpret_cast<const xmlChar *>("xml:space"));
+ preserve_whitespace = (space != NULL && strcmp(reinterpret_cast<const char *>(space), "preserve") == 0);
+ }
+
+ xmlNode *next_child;
+ for (xmlNode *child = node->children; child != NULL; child = next_child) {
+ next_child = child->next;
+ clean_node(child, preserve_whitespace, aggressive);
+ }
+
+ if (node->type == XML_ELEMENT_NODE && node->children == NULL) {
+ std::string tag = reinterpret_cast<const char *>(node->name);
+
+ // These are the only elements allowed in XHTML to be EMPTY,
+ // so insert dummy nodes to prevent the output from using
+ // the <foo/> syntax where not appropriate.
+ if (tag != "base" && tag != "meta" && tag != "link" && tag != "hr" &&
+ tag != "br" && tag != "param" && tag != "img" && tag != "area" &&
+ tag != "input" && tag != "col") {
+ xmlNode *text = xmlNewText(reinterpret_cast<const xmlChar *>(""));
+ xmlAddChild(node, text);
+ }
+ }
+ }
+}
+
+} // namespace
+
+void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive)
+{
+ normalize_node(xmlDocGetRootElement(doc->ptr));
+ clean_node(xmlDocGetRootElement(doc->ptr), false, aggressive);
+}
+
%}
%typemap(in) Directive* {
$1 = convert_php_objects_to_directive(*$input);
}
-XmlDocPtrWrapper XML_Template_process_file(const char *input_filename, Directive *root_directive, bool clean);
-std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc);
+XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean);
+void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean);
+void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive);
+std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint);