はじめに
libxml2でHTMLを保存した時にmeta charsetの前にmeta http-equivがつかないようにするパッチを作ったのでメモ。
gnome.orgのBugzillaにBug 728436 – [PATCH] Write meta charset tag instead of meta http-equiv content-typeとして報告済みです。
元々はNokogiriを使っていてこの現象に遭遇しました。additional meta Content-Type is added to HTML5 · Issue #1008 · sparklemotion/nokogiriにイシューが上がっています。
問題
パッチを当てていないlibxml2-2.9.1だと、以下のサンプルプログラム
read_write_html.c
# include <libxml/HTMLparser.h>
# include <libxml/tree.h>
# include <libxml/xmlsave.h>
/*
* cc -Iinclude -L.libs -lxml2.2 -g read_write_html.c
*/
static xmlDocPtr readHtmlFile(const char *filename) {
htmlParserCtxtPtr parserCtxt;
xmlDocPtr doc;
parserCtxt = htmlNewParserCtxt();
if (parserCtxt == NULL) {
fprintf(stderr, "htmlNewParserCtxt failed\n");
return NULL;
}
doc = htmlCtxtReadFile(parserCtxt, "example1.html", NULL, 0);
htmlFreeParserCtxt(parserCtxt);
return doc;
}
static int saveHtmlFile(xmlDocPtr doc, const char *filename) {
int ret;
xmlSaveCtxtPtr saveCtxt;
saveCtxt = xmlSaveToFilename(filename, "UTF-8",
XML_SAVE_FORMAT | XML_SAVE_AS_HTML);
if (saveCtxt == NULL) {
fprintf(stderr, "xmlSaveToFilename failed\n");
return -1;
}
ret = xmlSaveDoc(saveCtxt, doc);
if (ret == -1) {
fprintf(stderr, "xmlSaveDoc failed\n");
return -1;
}
ret = xmlSaveClose(saveCtxt);
if (ret == -1) {
fprintf(stderr, "xmlSaveClose failed\n");
return -1;
}
return 0;
}
int main(int argc, char **argv) {
xmlDocPtr doc;
int ret;
doc = readHtmlFile("example1.html");
if (doc == NULL) {
fprintf(stderr, "readHtmlFile failed\n");
return 1;
}
ret = saveHtmlFile(doc, "example1-out.html");
if (ret == -1) {
fprintf(stderr, "saveHtmlFile failed\n");
xmlFreeDoc(doc);
return 1;
}
xmlFreeDoc(doc);
return 0;
}
で、以下のようにmeta charsetのみを持つhtmlファイルを読み込んで保存すると
example1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HTML5 example</title>
</head>
<body>
<h1>HTML5 save example</h1>
<p>I would like to meta charset tag is used instead of meta http-equiv.</p>
</body>
</html>
以下のようにmeta charsetタグの前にmeta http-equivタグが付けられてしまいます。
example1-out.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>HTML5 example</title>
</head>
<body>
<h1>HTML5 save example</h1>
<p>I would like to meta charset tag is used instead of meta http-equiv.</p>
</body>
</html>
パッチ
libxml2-2.9.1-HTMLtree-meta-charset.patch
--- libxml2-2.9.1/HTMLtree.c.orig 2013-02-27 14:08:48.000000000 +0900
+++ libxml2-2.9.1/HTMLtree.c 2014-04-18 01:05:26.000000000 +0900
@@ -242,6 +242,12 @@
(attr->children->type == XML_TEXT_NODE) &&
(attr->children->next == NULL)) {
value = attr->children->content;
+ if (!xmlStrcasecmp(attr->name, BAD_CAST"charset"))
+ {
+ content = value;
+ http = 1;
+ }
+ else
if ((!xmlStrcasecmp(attr->name, BAD_CAST"http-equiv"))
&& (!xmlStrcasecmp(value, BAD_CAST"Content-Type")))
http = 1;
このパッチを当てたlibxml2では
example1-out.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HTML5 example</title>
</head>
<body>
<h1>HTML5 save example</h1>
<p>I would like to meta charset tag is used instead of meta http-equiv.</p>
</body>
</html>
と余分なmeta http-equivタグが追加されずにmeta charsetタグだけが出力されます。