Created
May 27, 2013 12:57
-
-
Save hoehrmann/5656932 to your computer and use it in GitHub Desktop.
Annotate XML documents with CSS declarations. Originally http://lists.w3.org/Archives/Public/www-archive/2004Jun/0001.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
cssanno.c -- annotate XML documents with CSS declarations | |
Usage: | |
cssanno file.xml file.css | |
cssanno takes an XML document and a CSS style sheet and | |
annotates the elements with new attributes representing | |
the style declarations for the current element. Example: | |
<?xml version='1.0' encoding='utf-8'?> | |
<foo><bar/><baz/></foo> | |
with a style sheet | |
foo { display: block } | |
bar { color: red } | |
baz { font-family: "Arial Unicode MS" } | |
*:first-child { white-space: normal } | |
would result in an XML document like | |
<?xml version="1.0" encoding="utf-8"?> | |
<foo xmlns:css = "http://example.org/css#" | |
css:display = "block" | |
css:white-space = "normal"> | |
<bar css:color = "red" | |
css:white-space = "normal" /> | |
<baz css:font-family = ""Arial Unicode MS"" /> | |
</foo> | |
Copyright (c) 2004 Bjoern Hoehrmann <[email protected]>. | |
This program is free software; you can redistribute it and/or | |
modify it under the terms of the GNU General Public License | |
as published by the Free Software Foundation; either version 2 | |
of the License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
$Id$ | |
*/ | |
#include <libcroco.h> | |
#include <libxml/tree.h> | |
#include <libxml/xmlsave.h> | |
#include <assert.h> | |
/* namespace for css:property='...' attributes */ | |
#define CSS_NAMESPACE "http://example.org/css#" | |
typedef struct | |
{ | |
xmlDoc* document; /* XML document */ | |
CRStyleSheet* stylesheet; /* style sheet */ | |
CRCascade* cascade; /* cascade abstraction */ | |
CRSelEng* selector; /* selection engine */ | |
} workspace; | |
void add_single_property(xmlNode* element, xmlNs* ns, CRDeclaration* decl) | |
{ | |
CRString* name = decl->property; /* name of the property */ | |
CRTerm* value = decl->value; /* value of the property */ | |
gchar *vstr = cr_term_to_string(value); /* convert to gchar* */ | |
gchar *nstr = cr_string_dup2(name); /* convert to gchar* */ | |
if (!vstr || !nstr) | |
{ | |
fprintf(stderr, "%s\n", "Warning: out of memory when adding attribute"); | |
/* free this far allocated memory */ | |
if (vstr) | |
g_free(vstr); | |
if (nstr) | |
g_free(nstr); | |
return; | |
} | |
/* element.setAttributeNS(...) */ | |
if (!(xmlNewNsProp(element, ns, nstr, vstr))) | |
fprintf(stderr, "%s\n", "Warning: Could not add attribute to element"); | |
/* free the stringified representation of the property value */ | |
g_free(vstr); | |
/* free the stringified representation of the property name */ | |
g_free(nstr); | |
} | |
void add_properties(xmlNode* element, xmlNs* ns, CRPropList *first) | |
{ | |
CRDeclaration *decl = NULL; /* a style declaration, a name/value pair */ | |
CRPropList *current = NULL; /* a list of properties */ | |
/* iterate over all properties in the list */ | |
for (current = first; current; current = cr_prop_list_get_next(current)) | |
{ | |
decl = NULL; | |
/* retrieve the declaration for the current property */ | |
cr_prop_list_get_decl(current, &decl); | |
if (decl) | |
{ | |
/* add the property to the element */ | |
add_single_property(element, ns, decl); | |
} | |
} | |
} | |
void process_element(xmlNode* element, xmlNs* ns, CRCascade *cascade, CRSelEng *selector) | |
{ | |
CRPropList *properties; /* list of properties for the current element */ | |
xmlNode *current = NULL; /* current node */ | |
/* foreach element $current in $doc add properties to element */ | |
for (current = element; current; current = current->next) | |
{ | |
if (current->type == XML_ELEMENT_NODE) | |
{ | |
properties = NULL; | |
/* get all properties for the current element */ | |
cr_sel_eng_get_matched_properties_from_cascade(selector, | |
cascade, | |
current, | |
&properties); | |
/* add the properties as attributes to the element */ | |
add_properties(current, ns, properties); | |
} | |
/* recursively process the children of the element */ | |
process_element(current->children, ns, cascade, selector); | |
} | |
} | |
void free_workspace(workspace* w) | |
{ | |
/* immediately return if there is nothing to clean up */ | |
if (!w) | |
return; | |
/* free the XML document */ | |
if (w->document) | |
xmlFreeDoc(w->document); | |
if (w->cascade) | |
{ | |
/* free the cascade abstraction */ | |
cr_cascade_destroy(w->cascade); | |
} | |
else if (w->stylesheet) | |
{ | |
/* free the style sheet */ | |
cr_stylesheet_destroy(w->stylesheet); | |
} | |
/* free the selector engine */ | |
if (w->selector) | |
cr_sel_eng_destroy(w->selector); | |
} | |
void fatal_error(workspace* w, char* msg) | |
{ | |
/* free allocated workspace, if any */ | |
if (w) | |
free_workspace(w); | |
/* print an error message, if any */ | |
if (msg) | |
fprintf(stderr, "%s\n", msg); | |
/* reclaim parsing related global memory */ | |
xmlCleanupParser(); | |
/* abort */ | |
exit(2); | |
} | |
workspace* new_workspace() | |
{ | |
/* allocate a new workspace */ | |
workspace* w = (workspace*)malloc(sizeof(workspace)); | |
/* fatal error if malloc() fails */ | |
if (!w) | |
fatal_error(w, "Out of memory!"); | |
/* initialize fields */ | |
w->cascade = NULL; | |
w->document = NULL; | |
w->selector = NULL; | |
w->stylesheet = NULL; | |
return w; | |
} | |
int main(int argc, char** argv) | |
{ | |
enum CRStatus status = CR_OK; /* status for libcroco operations */ | |
xmlNode* node = NULL; /* document element */ | |
workspace* w; /* workspace */ | |
char* xmlfile; /* path to xml document */ | |
char* cssfile; /* path to style sheet */ | |
xmlNs* ns; /* namespace for new attributes */ | |
/* check command line parameter */ | |
if (argc != 3) | |
{ | |
fprintf(stderr, "Usage: %s file.xml file.css\n", argv[0]); | |
exit(1); | |
} | |
xmlfile = argv[1]; | |
cssfile = argv[2]; | |
w = new_workspace(); | |
/* read the XML document into memory or stop processing */ | |
if (!(w->document = xmlParseFile(xmlfile))) | |
fatal_error(w, "Unable to process XML document."); | |
/* read the style sheet into memory */ | |
status = cr_om_parser_simply_parse_file(cssfile, CR_ASCII, &w->stylesheet); | |
/* check whether style sheet processing succeeded */ | |
if (status != CR_OK || w->stylesheet == NULL) | |
fatal_error(w, "Unable to process style sheet."); | |
/* retrieve a pointer to the document element */ | |
node = xmlDocGetRootElement(w->document); | |
/* at this point, the document must have a root element */ | |
assert( node != NULL ); | |
/* attempt to bind the "css" prefix to a namespace */ | |
if (!(ns = xmlNewNs(node, CSS_NAMESPACE, "css"))) | |
fatal_error(w, "Unable to declare 'css' prefix."); | |
/* new cascade abstraction */ | |
if (!(w->cascade = cr_cascade_new(w->stylesheet, NULL, NULL))) | |
fatal_error(w, "Unable to create cascade abstraction"); | |
/* new selector engine */ | |
if (!(w->selector = cr_sel_eng_new())) | |
fatal_error(w, "Unable to create selector engine."); | |
/* traverse the tree */ | |
process_element(node, ns, w->cascade, w->selector); | |
/* write the document to stdout */ | |
if (xmlSaveFile("-", w->document) == -1) | |
fatal_error(w, "Could not write XML document to stdout!"); | |
/* free workspace resources */ | |
free_workspace(w); | |
/* reclaim parsing related global memory */ | |
xmlCleanupParser(); | |
return EXIT_SUCCESS; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment