[css2xslfo] 01/13: Imported Upstream version 1.6.2
Tristan Seligmann
mithrandi at moszumanska.debian.org
Sun Jan 19 14:33:04 UTC 2014
This is an automated email from the git hooks/post-receive script.
mithrandi pushed a commit to branch master
in repository css2xslfo.
commit b2d86986be12d04e248c0d0b690b88162cc7b778
Author: Tristan Seligmann <mithrandi at mithrandi.net>
Date: Sun Jan 19 14:18:14 2014 +0200
Imported Upstream version 1.6.2
---
build.xml | 247 +
doc/manual.xhtml | 5880 ++++++++++++++++++++
doc/preprocess_xhtml.xsl | 126 +
doc/xhtml_report.css | 253 +
dtd/catalog | 90 +
dtd/xhtml1/xhtml-lat1.ent | 196 +
dtd/xhtml1/xhtml-special.ent | 80 +
dtd/xhtml1/xhtml-symbol.ent | 237 +
dtd/xhtml1/xhtml1-frameset.dtd | 1235 ++++
dtd/xhtml1/xhtml1-strict.dtd | 978 ++++
dtd/xhtml1/xhtml1-transitional.dtd | 1201 ++++
lib_src/flute_src.zip | Bin 0 -> 202375 bytes
lib_src/sac_src.zip | Bin 0 -> 146108 bytes
res/LICENCE | 5 +
res/MANIFEST_css2fop.MF | 6 +
res/MANIFEST_css2fopnew.MF | 6 +
res/MANIFEST_css2xep.MF | 7 +
res/MANIFEST_css2xinc.MF | 6 +
res/MANIFEST_css2xsl.MF | 6 +
res/MANIFEST_css2xslfo.MF | 5 +
res/W3C_COPYRIGHT.html | 74 +
res/release_notes.txt | 351 ++
src/META-INF/services/org.w3c.css.sac.parser | 1 +
src/be/re/css/BlockContainerFilter.java | 200 +
src/be/re/css/CSSToFOP.java | 396 ++
src/be/re/css/CSSToFOPNew.java | 403 ++
src/be/re/css/CSSToXEP.java | 415 ++
src/be/re/css/CSSToXSLFO.java | 307 +
src/be/re/css/CSSToXSLFOException.java | 82 +
src/be/re/css/CSSToXSLFOFilter.java | 405 ++
src/be/re/css/CSSToXSLFormatter.java | 216 +
src/be/re/css/CSSToXinc.java | 380 ++
src/be/re/css/CenterFilter.java | 214 +
src/be/re/css/Compiled.java | 621 +++
src/be/re/css/Constants.java | 15 +
src/be/re/css/Context.java | 17 +
src/be/re/css/DisplayNonePropagator.java | 106 +
src/be/re/css/Element.java | 79 +
src/be/re/css/FOMarkerFilter.java | 164 +
src/be/re/css/FirstLetterFilter.java | 376 ++
src/be/re/css/FootnoteFilter.java | 223 +
src/be/re/css/ForeignFilter.java | 129 +
src/be/re/css/InternedElementSelector.java | 52 +
src/be/re/css/InvalidPropertyFilter.java | 374 ++
src/be/re/css/LengthAdjustFilter.java | 163 +
src/be/re/css/LinkFilter.java | 218 +
src/be/re/css/ListImageLabelFilter.java | 115 +
src/be/re/css/MarkerFilter.java | 389 ++
src/be/re/css/Matcher.java | 652 +++
src/be/re/css/NormalizeTableFilter.java | 704 +++
src/be/re/css/PageRule.java | 99 +
src/be/re/css/PageSetupFilter.java | 1276 +++++
src/be/re/css/ProjectorFilter.java | 2264 ++++++++
src/be/re/css/Property.java | 708 +++
src/be/re/css/Rule.java | 413 ++
src/be/re/css/RuleCollector.java | 349 ++
src/be/re/css/RuleComparator.java | 51 +
src/be/re/css/SpaceCorrectionFilter.java | 118 +
src/be/re/css/TestSAC.java | 416 ++
src/be/re/css/Util.java | 1187 ++++
src/be/re/css/WidthAndMarginsFilter.java | 328 ++
src/be/re/css/WrapperFilter.java | 136 +
src/be/re/css/XHTMLAttributeTranslationFilter.java | 837 +++
src/be/re/css/ant/CSSToFOP.java | 81 +
src/be/re/css/ant/CSSToFOPNew.java | 65 +
src/be/re/css/ant/CSSToXEP.java | 71 +
src/be/re/css/ant/CSSToXSLFO.java | 42 +
src/be/re/css/ant/CSSToXSLFormatter.java | 61 +
src/be/re/css/ant/CSSToXinc.java | 42 +
src/be/re/css/ant/TaskBase.java | 164 +
src/be/re/css/res/space_correction.prop | 12 +
src/be/re/css/style/css.xsl | 1043 ++++
src/be/re/css/style/deltaxml.css | 40 +
src/be/re/css/style/fo_setup.xsl | 319 ++
src/be/re/css/style/fop_filter.xsl | 177 +
src/be/re/css/style/ua.css | 3 +
src/be/re/css/style/util.xsl | 106 +
src/be/re/css/style/xhtml.css | 160 +
src/be/re/css/style/xhtml_print.css | 211 +
src/be/re/css/style/xlink.css | 9 +
src/be/re/io/FlushOutputStream.java | 28 +
src/be/re/io/IOException.java | 43 +
src/be/re/io/ReadLineInputStream.java | 96 +
src/be/re/io/StreamConnector.java | 245 +
src/be/re/io/Util.java | 190 +
src/be/re/net/BasicUser.java | 142 +
src/be/re/net/Headers.java | 282 +
src/be/re/net/User.java | 9 +
src/be/re/net/Util.java | 1265 +++++
src/be/re/util/Array.java | 941 ++++
src/be/re/util/DigitalTree.java | 207 +
src/be/re/util/Equal.java | 24 +
src/be/re/util/UUID.java | 194 +
src/be/re/util/Util.java | 1088 ++++
src/be/re/xml/Accumulator.java | 404 ++
src/be/re/xml/CatalogResolver.java | 388 ++
src/be/re/xml/DOMToContentHandler.java | 284 +
src/be/re/xml/ExpandedName.java | 85 +
src/be/re/xml/Util.java | 1276 +++++
src/be/re/xml/sax/BalanceChecker.java | 157 +
src/be/re/xml/sax/ErrorHandler.java | 68 +
src/be/re/xml/sax/FilterOfFilters.java | 343 ++
src/be/re/xml/sax/GobbleDocumentEvents.java | 46 +
src/be/re/xml/sax/ProtectEventHandlerFilter.java | 91 +
src/be/re/xml/sax/Tee.java | 190 +
src/be/re/xml/sax/TransformerHandlerFilter.java | 49 +
src/be/re/xml/sax/Util.java | 267 +
src/catalog | 9 +
src/xhtml1/xhtml-lat1.ent | 196 +
src/xhtml1/xhtml-special.ent | 80 +
src/xhtml1/xhtml-symbol.ent | 237 +
src/xhtml1/xhtml1-frameset.dtd | 1235 ++++
src/xhtml1/xhtml1-strict.dtd | 978 ++++
src/xhtml1/xhtml1-transitional.dtd | 1201 ++++
114 files changed, 41531 insertions(+)
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..c485b7c
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,247 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<project name="CSSToXSLFO" basedir="." default="all">
+
+ <property name="version" value="1_6_2" />
+ <property name="view" value="csstoxslfo_release" />
+
+ <taskdef name="webdav-sync" classname="be.re.webdav.cmd.SyncAntTask" classpath="${user.home}/bin/webdav_sync.jar" />
+
+ <fileset id="compile-classpath" dir="lib">
+ <include name="flute.jar" />
+ <include name="sac.jar" />
+ <include name="stax-api-1.0.1.jar" />
+ </fileset>
+
+ <patternset id="lib-include">
+ <include name="lib/flute.jar" />
+ <include name="lib/sac.jar" />
+ <include name="lib/stax-api-1.0.1.jar" />
+ </patternset>
+
+ <!-- Main targets -->
+ <target name="all" depends="xslfo" />
+
+ <target name="release">
+ <antcall target="clean" />
+ <antcall target="xslfo" />
+ <antcall target="clean" />
+ <antcall target="xep" />
+ <antcall target="clean" />
+ <antcall target="xsl" />
+ <antcall target="clean" />
+ <antcall target="fop" />
+ <antcall target="clean" />
+ <antcall target="fopnew" />
+ <antcall target="clean" />
+ <antcall target="xinc" />
+ <antcall target="pack-source" />
+ <antcall target="clean" />
+ </target>
+
+ <target name="build" depends="compile-css2xslfo,copy-resources" />
+ <target name="build-xep" depends="compile-css2xep,copy-resources" />
+ <target name="build-xsl" depends="compile-css2xsl,copy-resources" />
+ <target name="build-fop" depends="compile-css2fop,copy-resources" />
+ <target name="build-fopnew" depends="compile-css2fopnew,copy-resources" />
+ <target name="build-xinc" depends="compile-css2xinc,copy-resources" />
+
+ <target name="clean">
+ <delete dir="classes" />
+ </target>
+
+ <target name="compile-css2xslfo" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXEP.java" />
+ <exclude name="be/re/css/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/CSSToFOP.java" />
+ <exclude name="be/re/css/CSSToFOPNew.java" />
+ <exclude name="be/re/css/CSSToXinc.java" />
+ <exclude name="be/re/css/ant/CSSToXEP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/ant/CSSToFOP.java" />
+ <exclude name="be/re/css/ant/CSSToFOPNew.java" />
+ <exclude name="be/re/css/ant/CSSToXinc.java" />
+ </javac>
+ </target>
+
+ <target name="compile-css2xep" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ <fileset file="lib/xep.jar" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXSLFO.java" />
+ <exclude name="be/re/css/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/CSSToFOP.java" />
+ <exclude name="be/re/css/CSSToFOPNew.java" />
+ <exclude name="be/re/css/CSSToXinc.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFO.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/ant/CSSToFOP.java" />
+ <exclude name="be/re/css/ant/CSSToFOPNew.java" />
+ <exclude name="be/re/css/ant/CSSToXinc.java" />
+ </javac>
+ </target>
+
+ <target name="compile-css2xsl" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ <fileset file="lib/XfoJavaCtl.jar" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXEP.java" />
+ <exclude name="be/re/css/CSSToXSLFO.java" />
+ <exclude name="be/re/css/CSSToFOP.java" />
+ <exclude name="be/re/css/CSSToFOPNew.java" />
+ <exclude name="be/re/css/CSSToXinc.java" />
+ <exclude name="be/re/css/ant/CSSToXEP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFO.java" />
+ <exclude name="be/re/css/ant/CSSToFOP.java" />
+ <exclude name="be/re/css/ant/CSSToFOPNew.java" />
+ <exclude name="be/re/css/ant/CSSToXinc.java" />
+ </javac>
+ </target>
+
+ <target name="compile-css2fop" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ <fileset file="lib/fop-must-come-after-new-0.20.5.jar" />
+ <fileset file="lib/avalon-framework-4.2.0.jar" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXEP.java" />
+ <exclude name="be/re/css/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/CSSToXSLFO.java" />
+ <exclude name="be/re/css/CSSToFOPNew.java" />
+ <exclude name="be/re/css/CSSToXinc.java" />
+ <exclude name="be/re/css/ant/CSSToXEP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFO.java" />
+ <exclude name="be/re/css/ant/CSSToFOPNew.java" />
+ <exclude name="be/re/css/ant/CSSToXinc.java" />
+ </javac>
+ </target>
+
+ <target name="compile-css2fopnew" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ <fileset file="lib/fop-1.0.jar" />
+ <fileset file="lib/avalon-framework-4.2.0.jar" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXEP.java" />
+ <exclude name="be/re/css/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/CSSToFOP.java" />
+ <exclude name="be/re/css/CSSToXSLFO.java" />
+ <exclude name="be/re/css/CSSToXinc.java" />
+ <exclude name="be/re/css/ant/CSSToXEP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/ant/CSSToFOP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFO.java" />
+ <exclude name="be/re/css/ant/CSSToXinc.java" />
+ </javac>
+ </target>
+
+ <target name="compile-css2xinc" depends="init">
+ <javac debug="on" debuglevel="lines,vars,source" srcdir="src" destdir="classes">
+ <classpath>
+ <fileset refid="compile-classpath" />
+ <fileset file="lib/xinc.jar" />
+ </classpath>
+ <exclude name="be/re/css/CSSToXEP.java" />
+ <exclude name="be/re/css/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/CSSToFOP.java" />
+ <exclude name="be/re/css/CSSToFOPNew.java" />
+ <exclude name="be/re/css/CSSToXSLFO.java" />
+ <exclude name="be/re/css/ant/CSSToXEP.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFormatter.java" />
+ <exclude name="be/re/css/ant/CSSToFOP.java" />
+ <exclude name="be/re/css/ant/CSSToFOPNew.java" />
+ <exclude name="be/re/css/ant/CSSToXSLFO.java" />
+ </javac>
+ </target>
+
+ <target name="copy-resources" depends="init">
+ <copy todir="classes">
+ <fileset dir="src" includes="be/**,catalog,xhtml1/**" excludes="**/*.java" />
+ </copy>
+ </target>
+
+ <target name="init">
+ <tstamp />
+ <mkdir dir="classes" />
+ <mkdir dir="bin" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/java/src/" directory="src" direction="down" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/java/lib/" directory="lib" direction="down" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/java/applications/css2xslfo/doc/" directory="doc" direction="down" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/java/applications/css2xslfo/res/" directory="res" direction="down" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/java/applications/css2xslfo/lib_src/" directory="lib_src" direction="down" />
+ <webdav-sync url="https://werner@re.pincette.net/view/${view}/dtd/" directory="dtd" direction="down" />
+ </target>
+
+ <target name="jar">
+ <jar destfile="bin/${variant}${version}.jar" manifest="res/MANIFEST_${variant}.MF">
+ <fileset dir="classes" />
+ <zipgroupfileset dir=".">
+ <patternset refid="lib-include" />
+ </zipgroupfileset>
+ <fileset dir="src">
+ <include name="META-INF/**" />
+ </fileset>
+ <fileset dir="res">
+ <include name="release_notes.txt" />
+ <include name="LICENCE" />
+ <include name="W3C_COPYRIGHT.html" />
+ </fileset>
+ </jar>
+ </target>
+
+ <target name="xslfo" depends="clean,build">
+ <antcall target="jar">
+ <param name="variant" value="css2xslfo" />
+ </antcall>
+ </target>
+
+ <target name="xep" depends="clean,build-xep">
+ <antcall target="jar">
+ <param name="variant" value="css2xep" />
+ </antcall>
+ </target>
+
+ <target name="xsl" depends="clean,build-xsl">
+ <antcall target="jar">
+ <param name="variant" value="css2xsl" />
+ </antcall>
+ </target>
+
+ <target name="fop" depends="clean,build-fop">
+ <antcall target="jar">
+ <param name="variant" value="css2fop" />
+ </antcall>
+ </target>
+
+ <target name="fopnew" depends="clean,build-fopnew">
+ <antcall target="jar">
+ <param name="variant" value="css2fopnew" />
+ </antcall>
+ </target>
+
+ <target name="xinc" depends="clean,build-xinc">
+ <antcall target="jar">
+ <param name="variant" value="css2xinc" />
+ </antcall>
+ </target>
+
+ <target name="pack-source" depends="clean">
+ <zip destfile="css2xslfo${version}_src.zip">
+ <zipfileset dir="." includes="doc/**,dtd/**,lib_src/**,res/**,src/**,build.xml,build.properties" prefix="css2xslfo" />
+ <zipfileset dir="." prefix="css2xslfo">
+ <patternset refid="lib-include" />
+ </zipfileset>
+ </zip>
+ </target>
+
+</project>
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
new file mode 100644
index 0000000..b0f66c9
--- /dev/null
+++ b/doc/manual.xhtml
@@ -0,0 +1,5880 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
+ <!ENTITY css "<acronym>CSS</acronym>">
+ <!ENTITY css2 "<acronym>CSS2</acronym>">
+ <!ENTITY css3 "<acronym>CSS3</acronym>">
+ <!ENTITY csstoxslfo "<acronym>CSSToXSLFO</acronym>">
+ <!ENTITY fop "<acronym>FOP</acronym>">
+ <!ENTITY html "<acronym>HTML</acronym>">
+ <!ENTITY id "<acronym>ID</acronym>">
+ <!ENTITY jar "<acronym>JAR</acronym>">
+ <!ENTITY names "<acronym>NAMES</acronym>">
+ <!ENTITY pdf "<acronym>PDF</acronym>">
+ <!ENTITY sax "<acronym>SAX</acronym>">
+ <!ENTITY svg "<acronym>SVG</acronym>">
+ <!ENTITY xep "<acronym>XEP</acronym>">
+ <!ENTITY xep4 "<acronym>XEP4</acronym>">
+ <!ENTITY xhtml "<acronym>XHTML</acronym>">
+ <!ENTITY xinc "<acronym>XINC</acronym>">
+ <!ENTITY xml "<acronym>XML</acronym>">
+ <!ENTITY xslfo "<acronym>XSL-FO</acronym>">
+ <!ENTITY xslt "<acronym>XSLT</acronym>">
+ <!ENTITY uri "<acronym>URI</acronym>">
+ <!ENTITY url "<acronym>URL</acronym>">
+]>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>The CSSToXSLFO User Guide</title>
+ <link type="text/css" media="print" rel="stylesheet" href="xhtml_report.css" />
+ <style type="text/css" xml:space="preserve">
+table
+{
+ border-collapse: collapse;
+ table-layout: fixed;
+ width: 100%;
+}
+
+table table
+{
+ margin-bottom: 0;
+ margin-top: 0;
+}
+
+table.property tr > td:first-child
+{
+ font-style: italic;
+}
+
+table.property tr > td
+{
+ padding: 0pt;
+}
+
+td
+{
+ padding: 5pt;
+ vertical-align: top;
+}
+
+th
+{
+ font-style: italic;
+ font-weight: normal;
+ padding: 5pt;
+ text-align: left;
+}
+
+table.specifications td:first-child, table.specifications th:first-child,
+table.ant-tasks td:first-child, table.ant-tasks th:first-child,
+table.task-attributes td:first-child, table.task-attributes th:first-child
+{
+ padding-left: 0pt;
+}
+
+table.specifications td + td + td, table.specifications th + th + th,
+table.ant-tasks td + td, table.ant-tasks th + th,
+table.task-attributes td + td + td, table.task-attributes th + th + th
+{
+ padding-right: 0pt;
+}
+
+div.method
+{
+ font-style: italic;
+ margin-bottom: 1.2em;
+ margin-top: 1.2em;
+ page-break-inside: avoid;
+}
+
+div.method > div.p
+{
+ padding-left: 1em;
+}
+
+ at media screen
+{
+ *:link
+ {
+ color: #982936;
+ }
+
+ *:visited
+ {
+ color: purple;
+ }
+
+ body
+ {
+ background: white;
+ font-family: sans-serif;
+ }
+
+ div.bottom, div.top, div.doc-info, div.title-page, div.copy-right
+ {
+ display: none;
+ }
+
+ div.h1
+ {
+ margin: 0.67em 0;
+ text-align: center;
+ }
+
+ div.h2
+ {
+ margin-bottom: 0.83em;
+ margin-left: 0;
+ margin-right: 0;
+ margin-top: 2em;
+ }
+
+ div.h3
+ {
+ margin-bottom: 1em;
+ margin-left: 0;
+ margin-right: 0;
+ margin-top: 1.8em;
+ }
+
+ div.h1, div.h2, div.h3
+ {
+ border-bottom: solid 0.4pt;
+ color: #982936;
+ width: 100%;
+ }
+
+ div.h2 h2
+ {
+ font-style: italic;
+ font-size: 1.3em;
+ }
+
+ div.h3 h3
+ {
+ font-style: italic;
+ font-size: 1.1em;
+ }
+
+ div.h1 h1, div.h2 h2, div.h3 h3
+ {
+ display: inline;
+ font-weight: normal;
+ color: white;
+ background-color: #982936;
+ margin: 0;
+ padding-left: 5pt;
+ padding-right: 5pt;
+ padding-top: 2pt;
+ }
+
+ div.toc-h1
+ {
+ margin-left: 1em;
+ }
+
+ div.toc-h2
+ {
+ margin-left: 2em;
+ }
+
+ div.toc-h3
+ {
+ margin-left: 3em;
+ }
+
+ div.text
+ {
+ margin-left: 5em;
+ }
+
+ div.title-screen
+ {
+ font-size: 2em;
+ font-weight: bold;
+ }
+
+ hr
+ {
+ border-bottom: none;
+ border-left: none;
+ border-right: none;
+ border-top: 0.2pt solid;
+ }
+
+ table
+ {
+ margin-bottom: 0.83em;
+ margin-top: 0.83em;
+ }
+}
+
+ at media print
+{
+ div.blank
+ {
+ page: blank;
+ }
+
+ div.bottom
+ {
+ height: 3em;
+ padding-top: 2em;
+ region: bottom;
+ }
+
+ div.bottom > span:before
+ {
+ content: counter(page);
+ }
+
+ div.copy-right
+ {
+ page: copy-right;
+ }
+
+ div.first
+ {
+ page: first;
+ }
+
+ div.front
+ {
+ page: front;
+ }
+
+ div.front.left
+ {
+ page: left-front;
+ }
+
+ div.front.right
+ {
+ page: right-front;
+ }
+
+ div.front.bottom > span:before
+ {
+ content: counter(page, lower-roman);
+ }
+
+ div.left
+ {
+ page: left;
+ text-align: left;
+ }
+
+ div.right
+ {
+ page: right;
+ text-align: right;
+ }
+
+ div.title.first
+ {
+ page: first-title;
+ }
+
+ div.copy-right.first
+ {
+ page: first-copy-right;
+ }
+
+ div.top
+ {
+ font-family: serif-title;
+ font-style: italic;
+ height: 4em;
+ region: top;
+ }
+
+ div.top > span:before
+ {
+ content: string(chapter);
+ }
+
+ div.copy-right > p
+ {
+ text-indent: 0pt;
+ }
+
+ div.back, div.main, div.toc-back, div.toc-main
+ {
+ counter-reset: h1;
+ }
+
+ div.title-screen
+ {
+ display: none;
+ }
+
+ div.title-page
+ {
+ page: title;
+ }
+
+ div.title-page > div.doc-info
+ {
+ border-right: solid 0.2pt;
+ display: block;
+ font-size: 12pt;
+ padding-right: 5mm;
+ text-align: right;
+ }
+
+ div.title-page > div.title-area
+ {
+ border-left: solid 0.2pt;
+ padding-left: 5mm;
+ }
+
+ div.title-page > div.title-area > div
+ {
+ font-size: 21pt;
+ }
+
+ div.title-page > div.title-area > div.title-main
+ {
+ font-size: 36pt;
+ font-style: italic;
+ font-weight: normal;
+ margin-bottom: 1em;
+ margin-top: 1em;
+ }
+
+ h1, div.toc-h1
+ {
+ counter-increment: h1;
+ counter-reset: footnote h2;
+ }
+
+ div.back > h1
+ {
+ page: back;
+ }
+
+ div.main > h1
+ {
+ page: main;
+ }
+
+ div.main > h1:first-child
+ {
+ page: main-first;
+ }
+
+ div.main h2:before, div.main h3:before,
+ div.back h2:before, div.back h3:before
+ {
+ padding-right: 1em;
+ }
+
+ div.toc-h1:before, div.toc-h2:before, div.toc-h3:before
+ {
+ display: marker;
+ marker-offset: 12pt;
+ text-align: right;
+ width: 4em;
+ }
+
+ h1
+ {
+ margin-bottom: 2.4em;
+ text-align-last: justify;
+ }
+
+ h1 > span:before
+ {
+ display: leader;
+ }
+
+ h1 > span:after
+ {
+ font-family: serif-title;
+ font-size: 3em; /* Results in 4.8 */
+ font-style: italic;
+ text-transform: uppercase;
+ }
+
+ div.main h1 > span:after, div.toc-main > div.toc-h1:before
+ {
+ content: counter(h1);
+ }
+
+ div.back h1 > span:after, div.toc-back > div.toc-h1:before
+ {
+ content: counter(h1, upper-alpha);
+ }
+
+ h2, div.toc-h2
+ {
+ counter-increment: h2;
+ counter-reset: h3;
+ }
+
+ div.main h2:before, div.toc-main > div.toc-h2:before
+ {
+ content: counter(h1) "." counter(h2);
+ }
+
+ div.main h2.example:before
+ {
+ display: marker;
+ marker-offset: 0.5em;
+ padding-right: 0pt;
+ text-align: right;
+ width: 3em;
+ }
+
+ div.back h2:before, div.toc-back > div.toc-h2:before
+ {
+ content: counter(h1, upper-alpha) "." counter(h2);
+ }
+
+ h3, div.toc-h3
+ {
+ counter-increment: h3;
+ }
+
+ div.main h3:before, div.toc-main > div.toc-h3:before
+ {
+ content: counter(h1) "." counter(h2) "." counter(h3);
+ }
+
+ div.back h3:before, div.toc-back > div.toc-h3:before
+ {
+ content: counter(h1, upper-alpha) "." counter(h2) "." counter(h3);
+ }
+
+ div.toc span.leader
+ {
+ display: none;
+ }
+
+ div.toc span.page-ref
+ {
+ padding-left: 2em;
+ }
+
+ div.toc-h1 > a, div.toc-h2 > a, div.toc-h3 > a
+ {
+ font-family: serif-title;
+ }
+
+ ol.example
+ {
+ counter-reset: list-counter;
+ }
+
+ ol.example li
+ {
+ margin-left: 2em;
+ }
+
+ ol.example li:before
+ {
+ content: counter(list-counter, lower-roman) ".";
+ counter-increment: list-counter;
+ display: marker;
+ marker-offset: 0.5em;
+ text-align: right;
+ width: 2em;
+ }
+
+ p.first-letter-example:first-letter
+ {
+ font-family: serif-swash;
+ font-size: 46pt;
+ font-style: italic;
+ float: left;
+ line-height: 46pt;
+ padding-right: 6pt;
+ margin-bottom: -12pt;
+ vertical-align: 9pt;
+ }
+}
+ </style>
+ </head>
+ <body>
+ <div class="first top" />
+ <div class="title first bottom" />
+ <div class="title first top" />
+ <div class="copy-right first bottom" />
+ <div class="copy-right first top" />
+ <div class="left bottom"><span /></div>
+ <div class="right bottom"><span /></div>
+ <div class="left top"><span /></div>
+ <div class="right top"><span /></div>
+ <div class="front left bottom"><span /></div>
+ <div class="front right bottom"><span /></div>
+ <div class="blank bottom" />
+ <div class="blank top" />
+
+ <div class="title-page">
+ <div class="title-area">
+ <div>The</div>
+ <div class="title-main">CSSToXSLFO</div>
+ <div>User Guide</div>
+ </div>
+
+ <div style="height: 10cm" />
+
+ <div class="doc-info">
+ <div>Version 1.6.2</div>
+ <div>Werner Donné</div>
+ <div>Pincette bvba</div>
+ <div>23 August 2010</div>
+ </div>
+ </div>
+
+ <div class="title-screen">The CSSToXSLFO User Guide</div>
+
+ <div class="copy-right">
+ <p>© 2004-2010 Pincette bvba. All rights granted.</p>
+
+ <p>This software is free and will remain free.</p>
+
+ <p>To use at your own responsibility.</p>
+ </div>
+
+ <div class="front">
+ <div class="toc" />
+ </div>
+
+ <div class="main">
+
+ <h1>Introduction<span /></h1>
+
+ <p>&csstoxslfo; is a tool which converts an &xml; document, combined with a
+&css2; style sheet, into an &xslfo; file. It has some special provisions for
+&xhtml;, which is also an &xml; vocabulary.
+The tool implements a reasonable subset of &css2;. It also
+adds a few extensions for handling page-related issues properly. Note that the
+tool is not a general-purpose printing tool for any kind of &html; pages you
+can find on the Internet.</p>
+
+ <p>The goal of &csstoxslfo; is to provide a rather easy interface to fine
+printing environments that use &xslfo; as their input. It is
+a compromise between the simplicity of style sheet expression and the quality
+of the result. &xslfo; is quite difficult. Writing style
+sheets that produce it are mostly written in &xslt;, which is
+not straightforward to everyone either. &css; on the other hand
+is rather simple and yet it is powerful. In fact it combines element selection
+and formatting specification in one easy-to-learn syntax. The cost is that a
+lot of interesting &xslfo; features are not available.</p>
+
+ <p>An area where the tool can be a plus is the programmatic generation of
+reports within applications. The variety in style for reports is not that
+great. The offered feature set of &csstoxslfo; can be sufficient. Having report
+programmers learn &xslfo; and &xslt; is not always an option, while many know
+&css; and &xhtml; well enough to be productive with it.</p>
+
+ <p>Another use-case for &csstoxslfo; is writing documents in &xml;. One can
+put work in a style sheet once and reuse that through the mark-up paradigm,
+in which content and formatting are separated.
+The formatting features should be sufficient to produce day-to-day documents in
+a typical business environment. Such documents don't tend to be very
+complicated, with respect to layout that is.</p>
+
+ <div class="separator" />
+
+ <h1>In Practice<span /></h1>
+
+ <h2>Specifying Style Sheets</h2>
+
+ <p>The most general way of specifying a style sheet for a document with
+&csstoxslfo; is the proposal in section 2.2 of [<a href="#CSS2" shape="rect">&css2;</a>]. It
+consists of a processing instruction, which precedes the document, looking like
+this:</p>
+
+ <pre xml:space="preserve"> <?xml-stylesheet type="text/css" href="style.css"?></pre>
+
+ <p>For &xhtml; there are a few additional options. You can
+use the <code>link</code> element to link a style sheet (only persistent style
+sheets) to the document or you can embed it with the <code>style</code> element.
+The <code>style</code> attribute is also available as specified in
+[<a href="#XHTML" shape="rect">&xhtml;</a>].</p>
+
+ <h2>Running It</h2>
+
+ <p>There are six packages you can run from the command-line: one that
+produces plain &xslfo;, one that returns the output of &xep;,
+a product from RenderX (<a href="http://www.renderx.com" shape="rect">http://www.renderx.com</a>),
+another that returns the output of XSLFormatter, a product from Antenna
+House
+(<a href="http://www.antennahouse.com" shape="rect">http://www.antennahouse.com</a>),
+yet another that returns the output of Xinc, a product from Lunasil
+<acronym>LTD</acronym> (<a href="http://www.lunasil.com" shape="rect">http://www.lunasil.com</a>)
+and finally, two that run &fop;
+(<a href="http://xml.apache.org/fop/" shape="rect">http://xml.apache.org/fop/</a>).
+One is for version 0.20.5 and the other for version 1.0.
+The 0.20.5 one comes with a filter that removes a few properties, which are not
+supported by &fop;. This makes &fop; complain less.</p>
+
+<p>You need <acronym>JDK1.3</acronym> or higher to run the packages. For 1.3
+you should create a classpath with a namespace-aware &xml; parser and an &xslt;
+processor. The command-lines look as follows for plain &csstoxslfo;:</p>
+
+ <pre xml:space="preserve"> > java -jar css2xslfo.jar url_or_filename <options></pre>
+
+ <p>For <acronym>XEP3</acronym>:</p>
+
+ <pre xml:space="preserve"> > java -Dcom.renderx.xep.ROOT=<XEP location> -jar css2xep.jar
+ url_or_filename <options></pre>
+
+ <p>For &xep4;:</p>
+
+ <pre xml:space="preserve"> > java -Dcom.renderx.xep.CONFIG=<XEP location>/xep.xml
+ -jar <XEP location>/lib/css2xep.jar url_or_filename
+ <options></pre>
+
+ <p>For XSLFormatter:</p>
+
+ <pre xml:space="preserve"> > set dynamic library path to <XSLFormatter location>/lib
+ > set environment variable AH_FONT_CONFIGFILE to
+ <XSLFormatter location>/etc/font-config.xml
+ > java -jar <XSLFormatter location>/lib/css2xsl.jar
+ url_or_filename <options></pre>
+
+ <p>For Xinc:</p>
+
+ <pre xml:space="preserve"> > java -jar css2xinc.jar url_or_filename <options></pre>
+
+ <p>For &fop; 0.20.5:</p>
+
+ <pre xml:space="preserve"> > java -jar css2fop.jar url_or_filename <options></pre>
+
+ <p>For &fop; 0.95:</p>
+
+ <pre xml:space="preserve"> > java -jar css2fopnew.jar url_or_filename <options></pre>
+
+ <p>Additional system properties and/or enviroment variables can be set.
+Please consult the product-specific documentation for this.</p>
+
+ <p>In order for css2xep.jar to work, you should place it in the <XEP
+location>/lib directory and create a link to or a copy of your &xep; &jar; file
+with the name <q>xep.jar</q>. Since &xep4; the link or copy are no longer
+needed, because the &xep; &jar; file has the expected name.
+For css2xsl.jar to work, you should place it in <XSLFormatter location>/lib.
+The css2fop.jar file needs to be next to fop.jar, which should be next
+to the packages is uses. Therefore you should copy fop.jar from the &fop; build
+directory to its lib directory. The same procedure is required for
+css2fopnew.jar. The css2xinc.jar should be in the &xinc; lib directory.</p>
+
+ <p>&csstoxslfo; uses the &xslt;-processor that comes with
+the <acronym>JDK1.4</acronym>, which is
+Xalan from <a href="http://www.apache.org" shape="rect">Apache</a>. For better performance
+you can prepend <a href="http://xml.apache.org/xalan-j/" shape="rect">Xalan 2.6.0+</a> or <a href="http://saxon.sourceforge.net/" shape="rect">Saxon 8.3+</a> to
+your boot classpath as follows (assuming /usr/local as the installation
+directory of Xalan):</p>
+
+ <pre xml:space="preserve"> > java -Xbootclasspath/p:/usr/local/xalan-j_2_6_0/bin/xalan.jar
+ -jar css2xslfo.jar url_or_filename <options></pre>
+
+ <p>You can also use <acronym>JDK1.5</acronym>, which comes with a faster
+&xslt; processor.</p>
+
+ <p>For &xep; there is a special note. You have to specify another &xslt;
+processor, because &xep; uses Saxon 6.5.x, with which it doesn't work. You can
+either prepend another &xslt; processor to the boot classpath or you can simply
+copy saxon8.jar in the &xep; lib directory.</p>
+
+ <h3>Common Options</h3>
+
+ <p>The following options are common to all six variants. The document
+to be processed can be specified with a &url; or filename. If it is omitted,
+stdin will be read.</p>
+
+ <dl>
+ <dt>-baseurl <URL></dt>
+ <dd>Change the base &url; of the input document. By default it is the
+&url; of the document itself.</dd>
+
+ <dt>-c <URL or filename></dt>
+ <dd>Specify a catalog in the format defined by SGML Open Technical
+Resolution <acronym>TR9401:1997</acronym>. Only the <q>PUBLIC</q> and
+<q>SYSTEM</q> keywords are supported.</dd>
+
+ <dt>-h</dt><dd>Display the command-line syntax.</dd>
+
+ <dt>-p <comma-separated list of &url;s or filenames></dt>
+ <dd>A list of pre-processing &xslt; style sheets that are executed on the
+input document, in the specified order, before anything else.</dd>
+
+ <dt>-uacss <URL or filename></dt>
+ <dd>Use another User Agent style sheet than the one built-in.</dd>
+
+ <dt>-v</dt><dd>Turn on &xml; validation of the input document.</dd>
+
+ <dt>parameter=value</dt>
+ <dd>Specify User Agent parameters. Equivalent &css; constructs precede
+these.</dd>
+ </dl>
+
+ <h3>Options Specific To css2xslfo.jar</h3>
+
+ <dl>
+ <dt>-debug</dt>
+ <dd>Produces a number of intermediary files representing the different
+processing steps.</dd>
+
+ <dt>-fo <filename></dt>
+ <dd>The &xslfo; output file. If it is omitted stdout will be written
+instead.</dd>
+ </dl>
+
+ <h3>Options Specific To css2xep.jar</h3>
+
+ <p>One the following options should be specified.</p>
+
+ <dl>
+ <dt>-pdf <filename></dt>
+ <dd>The &pdf; output file. Either this option or the <q>-ps</q> options
+should be present.</dd>
+
+ <dt>-ps <filename></dt>
+ <dd>The PostScript output file. Either this option or the <q>-pdf</q>
+options should be present.</dd>
+
+ <dt>-config <URL or filename></dt>
+ <dd>The &xep; configuration file.</dd>
+
+ <dt>-q</dt>
+ <dd>Makes &xep; silent.</dd>
+ </dl>
+
+ <h3>Options Specific To css2xsl.jar</h3>
+
+ <dl>
+ <dt>-pdf <filename></dt>
+ <dd>The &pdf; output file. This option is mandatory.</dd>
+
+ <dt>-config <URL or filename></dt>
+ <dd>The XSLFormatter configuration file.</dd>
+ </dl>
+
+ <h3>Options Specific To css2fop.jar</h3>
+
+ <dl>
+ <dt>-fc <filename></dt>
+ <dd>A user configuration file.</dd>
+
+ <dt>-pdf <filename></dt>
+ <dd>The &pdf; output file. Either this option or one of the <q>-ps</q>
+and <q>-svg</q> options should be present.</dd>
+
+ <dt>-ps <filename></dt>
+ <dd>The PostScript output file. Either this option or one of the
+<q>-pdf</q> and <q>-svg</q> options should be present.</dd>
+
+ <dt>-q</dt>
+ <dd>Makes &fop; silent.</dd>
+
+ <dt>-svg <filename></dt>
+ <dd>The <acronym>SVG</acronym> output file. Either this option or one of
+the <q>-pdf</q> and <q>-ps</q> options should be present.</dd>
+ </dl>
+
+ <h3>Options Specific To css2fopnew.jar</h3>
+
+ <dl>
+ <dt>-fop <options></dt>
+ <dd>The rest of the command-line will be parsed by FOP. Specifying an
+input file here doesn't work. The <q>-q</q> option will only work if you
+configure the Apache logger environment. The &fop; command-line logger can be
+set by assigning the value
+<code>org.apache.fop.util.CommandLineLogger</code>
+to the system property
+<code>org.apache.commons.logging.Log</code>. This
+option is mandatory.</dd>
+ </dl>
+
+ <h3>Options Specific To css2xinc.jar</h3>
+
+ <p>One the following options should be specified.</p>
+
+ <dl>
+ <dt>-pdf <filename></dt>
+ <dd>The &pdf; output file. This option is mandatory.</dd>
+
+ <dt>-config <URL or filename></dt>
+ <dd>The &xinc; configuration file.</dd>
+ </dl>
+
+ <h3 id="uapar">User Agent Parameters</h3>
+
+ <p>The User Agent parameters are common to all three packages. They have
+no effect if there are @page rules in the style sheet, except for the
+<q>rule-thickness</q> parameter. Furthermore, equivalent &css; constructs, when
+present in the style sheet, always precede.</p>
+
+ <dl>
+ <dt>column-count</dt>
+ <dd>The number of columns on a page. The default is <q>1</q>.</dd>
+
+ <dt>country</dt>
+ <dd>The country code. The default is <q>GB</q>.</dd>
+
+ <dt>font-size</dt>
+ <dd>The point size of the font. The default for paper sizes <q>a5</q>
+and <q>b5</q> is <q>10pt</q>. For all other paper sizes the default is
+<q>11pt</q>. See also the <q>paper-size</q> parameter.</dd>
+
+ <dt>html-header-mark</dt>
+ <dd>An &html; element can be passed here. Its contents will be used as the
+running header. By default there is no mark.</dd>
+
+ <dt>language</dt>
+ <dd>The language code. The default is <q>en</q>.</dd>
+
+ <dt>odd-even-hift</dt>
+ <dd>The amount by which the page contents is shifted in the inline
+progression direction when the paper mode is <q>twosided</q>. The default is
+<q>10mm</q>. See also the <q>paper-mode</q> parameter.</dd>
+
+ <dt>orientation</dt>
+ <dd>The allowed values are <q>portrait</q>, which is the default, and
+<q>landscape</q>.</dd>
+
+ <dt>paper-margin-bottom</dt>
+ <dd>The bottom margin of a page. The default is <q>0mm</q>.</dd>
+
+ <dt>paper-margin-left</dt>
+ <dd>The left margin of a page. The default is <q>25mm</q>.</dd>
+
+ <dt>paper-margin-right</dt>
+ <dd>The right margin of a page. The default is <q>25mm</q>.</dd>
+
+ <dt>paper-margin-top</dt>
+ <dd>The top margin of a page. The default is <q>10mm</q>.</dd>
+
+ <dt>paper-mode</dt>
+ <dd>The allowed values are <q>onesided</q>, which is the default, and
+<q>twosided</q>.</dd>
+
+ <dt>paper-size</dt>
+ <dd>The allowed values are <q>a4</q>, which is the default, <q>a0</q>,
+<q>a1</q>, <q>a2</q>, <q>a3</q>, <q>a5</q>, <q>b5</q>, <q>executive</q>,
+<q>letter</q> and <q>legal</q>.</dd>
+
+ <dt>rule-thickness</dt>
+ <dd>The default thickness for rules when there was no &css; specification
+for it. The default is <q>0.2pt</q>.</dd>
+
+ <dt>writing-mode</dt>
+ <dd>The &xslfo; writing mode. The default is <q>lr-tb</q>. Other possible
+values are <q>rl-tb</q>, <q>tb-rl</q>, <q>lr</q>, <q>rl</q> and <q>tb</q>. See
+also [<a href="#XSLFO" shape="rect">&xslfo;</a>].</dd>
+ </dl>
+
+ <h2>Building CSSToXSLFO</h2>
+
+ <p>The tool comes with an <a href="http://ant.apache.org/" shape="rect"><acronym>ANT</acronym></a> file.
+The default target only builds the css2xslfo.jar file. Then there are also the
+<code>xep</code>, <code>xsl</code>, <code>xinc</code>, <code>fop</code> and
+<code>fopnew</code> targets, which produce css2xep.jar, css2xsl.jar,
+css2xinc.jar, css2fop.jar and css2fopnew.jar respectively.</p>
+
+ <h2>Ant Tasks</h2>
+
+ <p>In each package there is a corresponding Ant task. The following table
+gives the respective class names.</p>
+
+ <table class="ant-tasks" rules="all">
+ <col width="1*" span="1" />
+ <col width="2*" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Package</th>
+ <th rowspan="1" colspan="1">Ant task class name</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">css2xslfo.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToXSLFO</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">css2xep.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToXEP</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">css2xsl.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToXSLFormatter</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">css2fop.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToFOP</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">css2fopnew.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToFOPNew</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">css2xinc.jar</td>
+ <td rowspan="1" colspan="1">be.re.css.ant.CSSToXinc</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Common Options</h3>
+
+ <p>All tasks support the nested elements <code>parameter</code> and
+<code>preprocessor</code>. With the former the parameters described in
+<q><a href="#uapar" shape="rect">User Agent Parameter</a></q> can be specified through its
+<code>name</code> and <code>value</code> attributes. With the latter a sequence
+of pre-processing &xslt; style sheets can be provided. Its
+<code>stylesheet</code> attribute should be set to a filename or &url;.
+The following attributes are common to all tasks.</p>
+
+ <table class="task-attributes" rules="all">
+ <col width="2*" span="1" />
+ <col width="4*" span="1" />
+ <col width="1*" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Attribute</th>
+ <th rowspan="1" colspan="1">Description</th>
+ <th rowspan="1" colspan="1">Required</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">baseurl</td>
+ <td rowspan="1" colspan="1">Change the base &url; of the input document. By default it is the
+&url; of the document itself.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">catalog</td>
+ <td rowspan="1" colspan="1">Specify a catalog in the format defined by SGML Open Technical
+Resolution <acronym>TR9401:1997</acronym>. Only the <q>PUBLIC</q> and
+<q>SYSTEM</q> keywords are supported.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">input</td>
+ <td rowspan="1" colspan="1">The input document as a &url; or filename.</td>
+ <td rowspan="1" colspan="1">Yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">output</td>
+ <td rowspan="1" colspan="1">The output document as a filename. The format is derived from its
+extension.</td>
+ <td rowspan="1" colspan="1">Yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">useragentstylesheet</td>
+ <td rowspan="1" colspan="1">Use another User Agent style sheet than the one built-in.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">validate</td>
+ <td rowspan="1" colspan="1">Turn on &xml; validation of the input document. Defaults to
+<code>false</code>.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Options Specific To css2xep.jar</h3>
+
+ <table class="task-attributes" rules="all">
+ <col width="1*" span="1" />
+ <col width="3*" span="1" />
+ <col width="1*" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Attribute</th>
+ <th rowspan="1" colspan="1">Description</th>
+ <th rowspan="1" colspan="1">Required</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">config</td>
+ <td rowspan="1" colspan="1">The &xep; configuration file. It may be a filename or a
+&url;.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">quiet</td>
+ <td rowspan="1" colspan="1">Makes &xep; silent.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Options Specific To css2fop.jar</h3>
+
+ <table class="task-attributes" rules="all">
+ <col width="1*" span="1" />
+ <col width="3*" span="1" />
+ <col width="1*" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Attribute</th>
+ <th rowspan="1" colspan="1">Description</th>
+ <th rowspan="1" colspan="1">Required</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">config</td>
+ <td rowspan="1" colspan="1">A user configuration file.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">quiet</td>
+ <td rowspan="1" colspan="1">Makes &fop; silent.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Options Specific To css2fopnew.jar</h3>
+
+ <table class="task-attributes" rules="all">
+ <col width="1*" span="1" />
+ <col width="3*" span="1" />
+ <col width="1*" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Attribute</th>
+ <th rowspan="1" colspan="1">Description</th>
+ <th rowspan="1" colspan="1">Required</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">config</td>
+ <td rowspan="1" colspan="1">A user configuration file.</td>
+ <td rowspan="1" colspan="1">No</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="separator" />
+
+ <h1>Compliance With CSS2<span /></h1>
+
+ <h2>Specifications</h2>
+
+ <table class="specifications" rules="all">
+ <col width="2*" span="1" />
+ <col width="1*" span="1" />
+ <col width="2*" align="justify" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Section</th>
+ <th rowspan="1" colspan="1">Implemented</th>
+ <th rowspan="1" colspan="1">Remarks and restrictions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">4.1 Syntax</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1">Thanks to Flute.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">4.2 Rules for handling parsing errors</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Unknown properties will end up in the &xslfo; file and cause
+errors in a &xslfo; processor.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">4.3 Values</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1">Thanks to Flute.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">4.4 &css; document representation</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1">Thanks to Flute.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">5 Selectors</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">All sections but 5.11.2 and 5.11.3. The
+<code>:first-letter</code> pseudo element is implemented with the restriction
+that letter combinations, which are considered as one letter, are not examined.
+As a workaround you can use the ligature Unicode characters instead. The
+<code>vertical-align</code> is also valid when <code>float</code> is
+<code>none</code>. In that case it applies to the inline material which is
+affected by the pseudo element.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">6 Assigning property values, Cascading and Inheritance</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">7 Media types</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1">By design, only types <code>all</code> and <code>print</code>
+are supported.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">8 Box model</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.1.1 The viewport</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.1.2 Containing blocks</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.2.1 Block-level elements and block boxes</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Compact and run-in boxes are not supported.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.2.2 Inline-level elements and inline boxes</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Compact and run-in boxes and inline tables are not
+supported.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.2.3 Compact boxes</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.2.4 Run-in boxes</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.2.5 The 'display' property</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">See property table.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.3 Positioning schemes</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.4 Normal flow</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.5 Floats</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.6 Absolute positioning</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.7 Relationships between 'display', 'position', and 'float'</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.9 Layered presentation</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">9.10 Text direction: the 'direction' and 'unicode-bibi'
+properties</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">10 Visual formatting model details</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">See the property table for the height property.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">11 Visual effects</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.1 The :before and :after pseudo-elements</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.2 The 'content' property</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.3 Interaction of :before and :after with 'compact' and
+'run-in' elements</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.4 Quotation marks</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.5 Automatic counters and numbering</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.6.1 Markers: the 'marker-offset' property</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">12.6.2 Lists: the 'list-style-type', 'list-style-image',
+'list-style-position', and 'list-style' properties</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.2.1 Page margins</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.2.2 Page size: the 'size' property</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.2.3 Crop marks: the 'marks' property</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.2.4 Left, right, and first pages</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.2.5 Content outside the page box</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.3 Page breaks</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Named pages are only supported for block-level and table
+elements, which are not inside of a table and have an ancestor with the
+<code>region</code> property set to <code>body</code>.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">13.4 Cascading in the page context</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">14 Colors and Backgrounds</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">15 Fonts</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">@font-face and descriptors are not supported.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">16 Text</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">17 Tables</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Inline tables are not supported. Anonymous table objects are
+only supported for missing table groups and missing table cells in a row, on
+the condition that there are table column elements. Audio rendering is not
+supported.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">18 User interface</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">19 Aural style sheets</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ </tbody>
+ </table>
+
+ <h2>Properties</h2>
+
+ <table class="specifications" rules="all">
+ <col width="2*" span="1" />
+ <col width="1*" span="1" />
+ <col width="2*" align="justify" span="1" />
+ <thead>
+ <tr>
+ <th rowspan="1" colspan="1">Property</th>
+ <th rowspan="1" colspan="1">Implemented</th>
+ <th rowspan="1" colspan="1">Remarks and restrictions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">azimuth</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background-attachment</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background-image</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background-position</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">background-repeat</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-bottom</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-bottom-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-bottom-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-bottom-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-collapse</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Not for <code>inline-table</code>.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-left</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-left-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-left-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-left-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-right</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-right-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-right-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-right-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-spacing</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Not for <code>inline-table</code>.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-top</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-top-color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-top-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">border-top-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">borded-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">bottom</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">caption-side</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">clear</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">clip</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">color</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">content</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">counter-increment</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">counter-reset</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">cue</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">cue-after</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">cue-before</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">cursor</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">direction</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">display</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">The values <code>run-in</code>, <code>compact</code>
+and <code>inline-table</code> are not supported. The <code>marker</code> value
+is supported with the limiation that the value <code>auto</code> for the
+<code>width</code> property is not. Markers also don't work with floats.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">elevation</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">empty-cells</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">float</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">fonts</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-family</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-size</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-size-adjust</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-stretch</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-style</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-variant</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">font-weight</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">height</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">A percentage value for the height of a block, which is in another
+block with an explicit height, will be treated as <code>auto</code>. This stems
+from the fact that in this case a block has to be split in a fo:block-container
+and a nested fo:block, because there are properties that don't apply to both of
+them. The inner original block will therefore have a parent without an
+explicit height specification. The latter has moved to the surrounding
+fo:block-container.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">left</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">letter-spacing</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">line-height</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">list-style</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">See individual properties.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">list-style-image</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">list-style-position</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">A list should be uniform. Specifying different values for
+different list items will produce undesired results.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">list-style-type</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">
+ <p>Only the values <code>disc</code>, <code>circle</code>,
+<code>square</code>, <code>decimal</code>, <code>lower-roman</code>,
+<code>upper-roman</code>, <code>lower-alpha</code>, <code>lower-latin</code>,
+<code>upper-alpha</code>, <code>upper-latin</code> and <code>none</code> are
+supported. The additional glyphs defined in [<a href="#CSS3L" shape="rect"><acronym>CSS3L</acronym></a>] are also supported. Those are
+<code>box</code>, <code>check</code>, <code>diamond</code> and
+<code>hyphen</code>.</p>
+ <p>A list should be uniform. Specifying different values for
+different list items will produce undesired results.</p>
+ </td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">margin</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">margin-bottom</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">margin-left</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">margin-right</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">margin-top</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">marker-offset</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">marks</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">max-height</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">max-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">min-height</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">min-width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">orphans</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">outline</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">outline-color</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">outline-style</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">outline-width</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">overflow</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">padding</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">padding-bottom</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">padding-left</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">padding-right</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">padding-top</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">page</td>
+ <td rowspan="1" colspan="1">partial</td>
+ <td rowspan="1" colspan="1">Only for block-level and table elements, which are not inside
+of a table and have an ancestor with the <code>region</code> property set to
+<code>body</code>.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">page-break-after</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">page-break-before</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">page-break-inside</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">pause</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">pause-after</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">pause-before</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">pitch</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">play-during</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">play-range</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">position</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">quotes</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">richness</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">right</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">size</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">speak</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">speak-header</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">speak-numeral</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">speak-punctuation</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">speech-rate</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">stress</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">table-layout</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">text-align</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">text-decoration</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">text-indent</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">text-transform</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">top</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">unicode-bibi</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">vertical-align</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">visibility</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">voice-family</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">volume</td>
+ <td rowspan="1" colspan="1">no</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">white-space</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">widows</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">width</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">word-spacing</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">z-index</td>
+ <td rowspan="1" colspan="1">yes</td>
+ <td rowspan="1" colspan="1" />
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="separator" />
+
+ <h1>Extensions<span /></h1>
+
+ <p>The extension features of the tool mostly pertain to page-oriented
+aspects. Care has been taken to not introduce new syntax. There are, however, a
+number of new properties. Those are normally safely ignored by browsers. In the
+case where there would be an impact on the layout produced by browsers, the
+properties can be confined to the <q>print</q> medium through @media rules.</p>
+
+ <h2>Page Regions</h2>
+
+ <p>This extension introduces &xslfo;-compatible page regions. Regions can
+be defined by placing a <code>region</code> property on an element. The allowed
+values are <code>bottom</code>, <code>left</code>, <code>right</code>,
+<code>top</code> and <code>body</code>. At least one element with the
+<code>region</code> property set to <code>body</code> should be present in the
+document.<span class="footnote-reference" /><span class="footnote-body">The
+&xhtml; User Agent style sheet sets this property to the <code>body</code>
+element.</span> Page sequences are only generated for the content of such an
+element. The regions other than the body region must be the first direct
+children of the body region. Otherwise they are ignored. In the case of
+&xhtml;, for example, this means that they should come at the
+beginning of the <code>body</code> element.</p>
+
+ <p>On top of that, either the <code>width</code> property, for left and
+right regions, or the <code>height</code> property, for top and bottom regions,
+should be defined. They will determine the dimensions of the page
+regions. The default value for <code>width</code> is <q>20mm</q>. For
+<code>height</code> it is <q>10mm</q>.</p>
+
+ <p>The extension property <code>precedence</code> is also available for
+the top and bottom regions. Its value can be <code>true</code> or
+<code>false</code>, the latter being the initial value. The property says
+whether the width of the top or bottom region is equal to that of the page
+reference area or if they give way to the left and right regions.</p>
+
+ <p>The regions work together with the @page rules, of which there should be
+at least one. It is possible to specify different regions, which correspond to
+the different page types in the style sheet. This can be achieved by also
+specifying the <code>page</code> property, which is a standard &css2; property.
+Consider the following example:</p>
+
+ <pre xml:space="preserve"> div.bottom-left, div.bottom-right { display: none; }
+
+ @media print
+ {
+ div.bottom-left
+ {
+ height: 15mm;
+ page: left;
+ region: bottom;
+ text-align: left;
+ }
+
+ div.bottom-right
+ {
+ height: 15mm;
+ page: right;
+ region: bottom;
+ text-align: right;
+ }
+
+ span.page:before { content: counter(page); }
+ }</pre>
+
+ <p>This says that on left pages the bottom region is left-aligned, while
+on right pages it is right-aligned. The <code>span</code> element is used in
+the following region definitions:</p>
+
+ <pre xml:space="preserve"> <div class="bottom-left">
+ <p> </p>
+ <div><span class="page"/></div>
+ </div>
+
+ <div class="bottom-right">
+ <p> </p>
+ <div><span class="page"/></div>
+ </div></pre>
+
+ <p>The <code>page</code> property bears a kind of inheritance mechanism.
+For any page the regions with the most specific <code>page</code> property will
+be selected. A region without a <code>page</code> property is the least
+specific. A named page is more specific and the values <code>left</code> and
+<code>right</code> are yet more specific. After this comes the new pseudo page
+<code>blank</code>, which is for blank pages that are generated because of page
+positioning constraints such as left and right. The first page of a chapter,
+for example, is sometimes forced to be a right page. This can produce an extra
+blank page for the previous chapter. In fact, this maps directly to the &xslfo;
+blank pages. There are special values, which are even more specific, such as
+<code>first-right</code>, <code>blank-left</code>,
+<code>left-<page-name></code>, etc. If, for example, there is no bottom
+region for <code>first-right</code>, but there is one for <code>first</code>,
+the latter will be selected if the first page happens to on the right. See
+section <a href="#page-prop" shape="rect"><q><i>page</i></q></a> for the
+precise precedence rules.</p>
+
+ <p>In order for the top, bottom, left and right region elements not to
+interfere with the normal flow it is best to set their display type to
+<code>none</code>.</p>
+
+ <h2>Page Numbering</h2>
+
+ <p>The two special counters <code>page</code> and <code>pages</code> in
+this tool are taken over from the &css3; Paged Media Module (see also [<a href="#CSS3P" shape="rect"><acronym>CSS3P</acronym></a>]). The <code>page</code> can be
+used just like any other counter, except that it is confined to the regions.
+The following example shows a document with a preface and a body. Each reset
+the page count. The preface has a lower Roman numbering style, while the body
+uses the decimal style. If the body page didn't reset the counter,
+numbering would continue from the preface, but with a change of style.</p>
+
+ <pre xml:space="preserve"> <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title></title>
+ <style type="text/css">
+ @page preface
+ {
+ counter-reset: page;
+ margin: 10%;
+ }
+
+ @page body
+ {
+ counter-reset: page;
+ margin: 10%;
+ }
+
+ div.bottom-preface
+ {
+ page: preface;
+ region: bottom;
+ }
+
+ div.bottom-body
+ {
+ page: body;
+ region: bottom;
+ }
+
+ div.bottom-preface > span.page:before
+ {
+ content: counter(page, lower-roman);
+ }
+
+ div.bottom-body > span.page:before
+ {
+ content: counter(page, decimal);
+ }
+
+ div.preface { page: preface; }
+ div.body { page: body; }
+ </style>
+ </head>
+ <body>
+ <div class="bottom-preface"><span class="page"/></div>
+ <div class="bottom-body"><span class="page"/></div>
+ <div class="preface">
+ <p>Text.</p>
+ </div>
+ <div class="body">
+ <p>Text.</p>
+ </div>
+ </body>
+ </html></pre>
+
+ <p>When switching between named pages you can control how the ending named
+page sequence should be terminated with the extension property
+<code>force-page-count</code>. For example, it some page sequence produces five
+pages, you can force the sequence to produce six pages by setting the property
+to <code>even</code>. An extra blank page will then be generated before starting
+the new page sequence. If you don't want such behaviour, you should set the
+property to <code>no-force</code>, since the initial value is
+<code>auto</code>.</p>
+
+ <h2>Page References</h2>
+
+ <p>You sometimes want to write phrases like <q>The diagram on page 19
+...</q>. The &csstoxslfo; tool provides this functionality through the
+<code>page-ref</code> function, which can be used in the <code>content</code>
+property. Its only parameter is the name of an attribute that contains the &id;
+of another element. The function call will be replaced with the number of the
+page that element is on.</p>
+
+ <p>In &xhtml; it is a bit more complicated to achieve the desired result,
+because there aren't many extension attributes available for it. The
+following fragment shows how it can be done:</p>
+
+ <pre xml:space="preserve"> <img id="img1" src="file:///t.png"/>
+ ...
+ <span class="page-ref"><span class="img1"/></span></pre>
+
+ <p>The accompanying style sheet rule would then be:</p>
+
+ <pre xml:space="preserve"> span.page-ref > span:before { content: page-ref(class); }</pre>
+
+ <h2>Leaders</h2>
+
+ <p>It is possible to use &xslfo; leaders through the display type
+<code>leader</code>. The properties defined in section 7.21 of [<a href="#XSLFO" shape="rect">&xslfo;</a>] (<q>Leader and Rule Properties</q>) can be used in
+a &css; style sheet, with the exception that the <code>leader-length</code>
+property cannot have a length range as a value. If you want to create table of
+contents lines or something similar, you also need the &xslfo; property
+<code>text-align-last</code>, described in section 7.15.10 of
+[<a href="#XSLFO" shape="rect">&xslfo;</a>]. The following example shows how a table of
+contents line could be made in &xhtml;.</p>
+
+ <pre xml:space="preserve"> <div class="toc">
+ <a href="#chapter1">Title of Chapter 1</a>
+ <span class="leader"/>
+ <span class="page-ref"><span class="chapter1"/></span>
+ </div></pre>
+
+ <p>The piece of style sheet that goes with it is:</p>
+
+ <pre xml:space="preserve"> div.toc
+ {
+ text-align-last: justify;
+ }
+
+ span.leader
+ {
+ display: leader;
+ leader-pattern: dots;
+ leader-pattern-width: 5pt;
+ }
+
+ span.page-ref > span:before
+ {
+ content: page-ref(class);
+ }</pre>
+
+ <h2>Named Strings</h2>
+
+ <p>Named strings, as described in [<a href="#CSS3G" shape="rect"><acronym>CSS3G</acronym></a>], are supported in &csstoxslfo;.
+This consists of the <code>string-set</code> property,
+with which contents can be captured, and the <code>string()</code> function.
+The latter can occur in the value of the <code>content</code> property. The
+<code>string-set</code> property accepts values which are similar to those of
+the <code>content</code> property. There is an additional keyword
+<code>contents</code>, which is replaced with the string value of the element
+carrying the <code>string-set</code> property.</p>
+
+ <p>The following is a simple &xhtml; example of how you can create a running
+header that refers to the current chapter.</p>
+
+ <pre xml:space="preserve"> <body>
+ <div class="top">
+ <span class="mark"/>
+ </div>
+ ...
+ <h1>Chapter Title</h1>
+ ...
+ </body></pre>
+
+ <p>Here is the bit of style sheet that does it:</p>
+
+ <pre xml:space="preserve"> div.top
+ {
+ region: top;
+ display: none;
+ }
+
+ div.top > span.mark:before { content: string(mark); }
+
+ h1 { string-set: mark contents; }</pre>
+
+ <h2>Hyphenation</h2>
+
+ <p>Text can be hyphenated through the <code>hyphenate</code> property,
+which is inherited. The possible values are <code>true</code> and
+<code>false</code>. Hyphenation is turned off by default.</p>
+
+ <h2>Footnotes</h2>
+
+ <p>It is possible to produce footnotes using the
+<code>footnote-reference</code> and <code>footnote-body</code> display types.
+The former is displayed in the flow, while
+the latter goes to the footnote area at the bottom of the page. When a footnote
+body occurs it must be either immediately preceded by a footnote reference or
+have a <code>:before</code> pseudo element with the display type
+<code>footnote-reference</code>. Otherwise
+it is treated as if the display were <code>none</code>. Whitespace between a
+footnote reference and body is gobbled. A footnote reference can also occur on
+its own.</p>
+
+ <p>The contents of both the footnote reference and body is free. Both
+display types exist to give you complete control over the contents and style.
+Usually some footnote counter is used, as shown in the example below. There is
+an extra counter style <code>footnote</code>, which produces symbols, such as
+an asterix, dagger, etc.</p>
+
+ <pre xml:space="preserve"> h1 { counter-reset: footnote; }
+
+ span.footnote-body
+ {
+ display: footnote-body;
+ font-size: 0.83em;
+ }
+
+ span.footnote-body:before
+ {
+ content: counter(footnote);
+ padding-right: 1em;
+ }
+
+ span.footnote-reference
+ {
+ display: footnote-reference;
+ }
+
+ span.footnote-reference:before
+ {
+ counter-increment: footnote;
+ content: counter(footnote);
+ font-size: 0.83em;
+ vertical-align: super;
+ }</pre>
+
+ <p>In the document a footnote would then look like this:</p>
+
+ <pre xml:space="preserve"> <p>Paragraph text.<span class="footnote-reference"/><span
+ class="footnote-body">Footnote text.</span></p></pre>
+
+ <p>You might find it cumbersome to have to place a footnote reference in
+front of every footnote body. It can be avoided, at the expense of formatting
+control however. You can define a <code>:before</code> pseudo element for the
+footnote body and give it the display type <code>footnote-reference</code>.
+Whatever contents it generates will then be used for the reference in the flow,
+as well as in the footnote body at the bottom of the page. As a consequence,
+the style is constrained by the fact that it must be decent for both contexts.
+The style sheet becomes a bit simpler:</p>
+
+ <pre xml:space="preserve"> h1 { counter-reset: footnote; }
+
+ span.footnote-body
+ {
+ display: footnote-body;
+ font-size: 0.83em;
+ }
+
+ span.footnote-body:before
+ {
+ counter-increment: footnote;
+ content: counter(footnote);
+ display: footnote-reference;
+ font-size: 0.83em;
+ vertical-align: super;
+ }</pre>
+
+ <p>If you want full control over the formatting in both contexts and at the
+same time want to omit the footnote reference elements in the document, the
+solution is to pre-process the document. The transformation is rather
+trivial.</p>
+
+ <h2>Orientation</h2>
+
+ <p>You can rotate text with the <code>orientation</code> property. This
+works only for block, table and table cell elements. The possible values are 0,
+90, 180, 270, -90, -180, -270. They represent the degrees in the
+counter-clockwise direction. The initial value is 0.</p>
+
+ <h2>List Style Types</h2>
+
+ <p>The glyphs for the <code>list-style-type</code> property, as defined in
+[<a href="#CSS3L" shape="rect"><acronym>CSS3L</acronym></a>], are implemented.</p>
+
+ <h2>Multicolumn</h2>
+
+ <p>With the properties <code>column-count</code>, which must be strictly
+positive, and <code>column-gap</code>, which is a length, a multi-column layout
+can be specified for a page. Both properties are allowed in an @page rule. As a
+consequence, if you want to switch between column modes, you have to switch
+pages as well.</p>
+
+ <p>With the <code>column-span</code> property a blocks and tables, that
+are not themselves inside of another table, can be made to span all the columns
+of a multi-column page. The allowed values for the property are <code>all</code>
+and <code>none</code>.</p>
+
+ <h2>Change Bars</h2>
+
+ <p>The change bar properties introduced in
+[<a href="#XSLFO11" shape="rect"><acronym>XSL-FO11</acronym></a>] are available for
+<code>:before</code> and <code>:after</code> pseudo elements. For the latter,
+only the <code>change-bar-class</code> property is relevant.
+The following is a simple example:</p>
+
+ <pre xml:space="preserve"> p.changed:before
+ {
+ change-bar-class: changed;
+ change-bar-style: solid; /* initial value is none */
+ change-bar-width: 0.2pt;
+ }
+
+ p.changed:after { change-bar-class: changed; }</pre>
+
+ <p>Note that this feature only works for &xep4; at the moment.</p>
+
+ <h2>Links</h2>
+
+ <p>The <code>link</code> property can have the name of an attribute as
+its value. The value of that attribute will be used for the generated link, as
+the target &url; or the internal target &id;, if it is an
+<acronym>IDREF</acronym> attribute, which distinguishes it from a relative
+&url;. Likewise, the value of the <code>anchor</code> property can be the name
+of an attribute, the value of which will become the target &id;. This way an
+internal link destination can be created. For example:</p>
+
+ <pre xml:space="preserve"> a[href] { link: href; }
+ a[name] { anchor: name; }</pre>
+
+ <h2>Graphics</h2>
+
+ <p>An external graphic can be included in a document through the display
+type <code>graphic</code>, which is an inline level display type. The elements
+marked with it are <q>replaced elements</q>. As a consequence, the properties
+<code>height</code> and <code>width</code> apply. The &xslfo;
+properties <code>content-height</code>, <code>content-width</code>,
+<code>content-type</code>, <code>scaling</code> and <code>scaling-method</code>
+are also supported. Their definition is in [<a href="#XSLFO" shape="rect">&xslfo;</a>]. The
+property <code>src</code> is interpreted differently. Its value should be the
+name of an attribute that has a &uri; for a value. For the &xhtml; element
+<code>img</code>, for example, the User Agent style sheet contains the
+following:</p>
+
+ <pre xml:space="preserve"> img
+ {
+ content-height: scale-to-fit;
+ content-width: scale-to-fit;
+ display: graphic;
+ scaling: uniform;
+ src: src;
+ }</pre>
+
+ <h2>Column And Row Spanning</h2>
+
+ <p>In &xhtml; one can specify column and row spanning with the
+<code>colspan</code> and <code>rowspan</code> attributes on the <code>td</code>
+and <code>th</code> elements. It is, however, also possible to apply &css; to
+other &xml; vocabularies. Hence, there should be an equivalent feature in &css;
+to express this. The extension properties <code>colspan</code> and
+<code>rowspan</code> serve that purpose. They can be used for elements with the
+display type <code>table-cell</code>.</p>
+
+ <h2>Proportional Column Widths</h2>
+
+ <p>Again in &xhtml; it is possible to say that a column should occupy a
+relative portion of the total table width. It is done by setting the
+<code>width</code> attribute to a number, followed by an asterix. If we have,
+for example, three columns with the widths <q>1*</q>, <q>2*</q> and <q>3*</q>,
+they occupy 1, 2 and 3 sixth of the table width respectively. This is not part
+of the &html; specification, but it is a widely supported feature.</p>
+
+ <p>In order to provide it for other &xml; vocabularies then &xhtml;, the
+unit <code>pcw</code>, which stands for <q>proportional column width</q>, is
+available for the <code>width</code> property of an element with the display
+type <code>table-column</code>.</p>
+
+ <h2>Repeating Table Headers And Footers</h2>
+
+ <p>By default table headers and footers are repeated when a table spans
+several pages. You can suppress this by setting the
+<code>table-omit-header-at-break</code> and
+<code>table-omit-footer-at-break</code> properties to <code>true</code>
+respectively.</p>
+
+ <h2>CSS3 Namespaces</h2>
+
+ <p>Namespaces for selectors, as defined in [<a href="#CSS3S" shape="rect"><acronym>CSS3S</acronym></a>], are implemented. This means you
+can use namespace prefixes in element selectors and attribute conditions. The
+prefixes are separated from the local name with a pipe sign (<q>|</q>).</p>
+
+ <p>The namespaces are declared with the <code>@namespace</code> rule, which
+should always come right after the <code>@import</code> rules if there are any.
+In the following example the &xhtml; namespace has been declared as the default
+namespace. Next to that, the DeltaXML namespace is declared with the prefix
+<q>dx</q>. You also see the use of the <q>attr</q> function with an attribute
+that has a prefix.</p>
+
+ <pre xml:space="preserve"> @namespace url(http://www.w3.org/1999/xhtml);
+ @namespace dx
+ url(http://www.deltaxml.com/ns/well-formed-delta-v1);
+
+ *[dx|delta=add], dx|new
+ {
+ text-decoration: underline;
+ }
+
+ *[dx|delta=delete], dx|old
+ {
+ text-decoration: line-through;
+ }
+
+ p[dx|delta]:before
+ {
+ content: attr(dx|delta);
+ display: marker;
+ marker-offset: 0.5em;
+ text-align: right;
+ }</pre>
+
+ <h2>Wrappers</h2>
+
+ <p>When processing &xml; in general you might encounter elements which
+represent pure structure, i.e. they are not directly related to layout. For such
+elements there shouldn't be any formatting objects in the output. Normally you
+would have to pre-process the document in order to get rid of them in the proper
+way.</p>
+
+ <p>The display type <code>wrapper</code> is introduced to cope with common
+cases. When an element has this display type, it will not contribute any
+formatting objects. However, its inherited properties will be passed on to its
+child elements, according to the property inheritance rules.</p>
+
+ <p>With respect to &xml; processing, a wrapper seems to be
+<q>transparant</q>. Note however that, while a wrapper can occur anywhere, it
+influences &css; selector matching. For instance, it will interfere with
+<q>direct sibling</q> and <q>direct child</q> selectors.</p>
+
+ <h2>Foreign Elements</h2>
+
+ <p>With the display type <code>foreign</code> it is possible to transfer
+part of a document unmodified to an <code>fo:instream-foreign-object</code>
+element. This may be useful for elements that are in another namespace than
+that of the document itself and which are supported by the &xslfo; processor.
+Typical examples are &svg; and MathML.</p>
+
+ <h2 id="property-specs">Property Specifications</h2>
+
+ <h3 id="anchor-prop">anchor</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><identifier> | attr(X)</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block-level and inline-level elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><identifier></dt>
+ <dd>The qualified name of an attribute, the value of which is the target
+&id;. This type of value is <i>deprecated</i>, because it doesn't support
+namespace prefixes.</dd>
+
+ <dt>attr(X)</dt>
+ <dd>This returns the value of the attribute of the subject with the
+qualified name X. The &css3; namespace prefixes are supported. The value is the
+target &id;.</dd>
+ </dl>
+
+ <h3 id="change-bar-class-prop">change-bar-class</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><name></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none, value required</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before and after pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><name></dt>
+ <dd>An NCName, as defined in [<a href="#NAMES" shape="rect">&names;</a>], to allow
+pairing of before and after elements, which don't have to belong to the same
+element. This way a change bar context is created.</dd>
+ </dl>
+
+ <h3 id="change-bar-color-prop">change-bar-color</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><color></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">the value of the <code>color</code> property</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><color></dt>
+ <dd>Specifies the color of the change bar.</dd>
+ </dl>
+
+ <h3 id="change-bar-offset-prop">change-bar-offset</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><length></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">6pt</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><length></dt>
+ <dd>Gives the distance from the edge of the column area containing the
+text that is marked as changed to the center of the generated change bar. A
+positive distance is directed away from the column region and into the margin
+regardless of the <code>change-bar-placement</code> property. Relative lengths
+(i.e., percentage values and lengths with units of <q>em</q>) are not permitted
+for the value of this property.</dd>
+ </dl>
+
+ <h3 id="change-bar-placement-prop">change-bar-placement</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">left | right | inside | outside | alternate</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">start</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>alternate</dt>
+ <dd>When there are exactly two columns, the change bar will be offset
+from the left edge of all column one areas and the right edge of all column two
+areas; when there are any other number of columns, this value is equivalent to
+<q>outside</q>.</dd>
+
+ <dt>inside</dt>
+ <dd>If the page binding edge is on the left-edge, the change bar
+will be offset from the left edge of all column areas. If the binding is the
+right-edge, the change bar will be offset from the right edge of all column
+areas. If the page binding edge is on neither the left-edge nor right-edge, the
+change bar will be offset from the left edge of all column areas.</dd>
+
+ <dt>left</dt>
+ <dd>The change bar will be offset from the left edge of all column
+areas.</dd>
+
+ <dt>outside</dt>
+ <dd>If the page binding edge is on the left-edge, the change bar
+will be offset from the right edge of all column areas. If the binding is the
+right-edge, the change bar will be offset from the left edge of all column
+areas. If the page binding edge is on neither the left-edge nor right-edge, the
+change bar will be offset from the right edge of all column areas.</dd>
+
+ <dt>right</dt>
+ <dd>The change bar will be offset from the right edge of all column
+areas.</dd>
+ </dl>
+
+ <h3 id="change-bar-style-prop">change-bar-style</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><border-style></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3 id="change-bar-width-prop">change-bar-width</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><border-width></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">medium</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">before pseudo elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><border-width></dt>
+ <dd>Relative lengths (i.e., percentage values and lengths with units of
+<q>em</q>) are not permitted for the value of this property.</dd>
+ </dl>
+
+ <h3 id="colspan-prop">colspan</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><integer></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">1</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">table cells</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><integer></dt>
+ <dd>Expresses the number of columns the table cell will span. The value
+must be larger than or equal to 1.</dd>
+ </dl>
+
+ <h3 id="column-count-prop">column-count</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><integer> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">1</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">the page context</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><integer></dt>
+ <dd>The value must be larger than or equal to 1.</dd>
+ </dl>
+
+ <h3 id="column-gap-prop">column-gap</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><length> | <percentage> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">12.0pt</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">the page context</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">refer to the width of the body region</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><length></dt>
+ <dd>This is an unsigned length, If a negative value has been specified a
+value of 0pt will be used.</dd>
+
+ <dt><percentage></dt>
+ <dd>The value is a percentage of the width of the body region.</dd>
+ </dl>
+
+ <h3 id="column-span-prop">column-span</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">none | all | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block elements which are not in table elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>all</dt>
+ <dd>This element spans all columns of a multi-column region.</dd>
+
+ <dt>none</dt>
+ <dd>This element does not span multiple columns of a multi-column
+region.</dd>
+ </dl>
+
+ <h3 id="content-height-prop">content-height</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | scale-to-fit | <length> |
+<percentage> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">intrinsic height</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>The content-height should be the intrinsic content-height.</dd>
+
+ <dt>scale-to-fit</dt>
+ <dd>A size of the content-height equal to the height of the viewport.
+This implies a certain scaling factor to be applied onto the content.</dd>
+
+ <dt><length></dt>
+ <dd>An absolute size for the content-height. This implies a certain
+scaling factor to be applied onto the content.</dd>
+
+ <dt><percentage></dt>
+ <dd>A percentage representing a scaling factor applied to the intrinsic
+height.</dd>
+ </dl>
+
+ <h3 id="content-type-prop">content-type</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | <string></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">intrinsic height</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>No identification of the content-type. The User Agent may determine
+it by <q>sniffing</q> or by other means.</dd>
+
+ <dt><string></dt>
+ <dd>A specification of the content-type in terms of a mime-type, which
+has the form <q>content-type:</q> followed by a mime content-type, e.g.,
+content-type="content-type:image/svg+xml".</dd>
+ </dl>
+
+ <h3 id="content-width-prop">content-width</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | scale-to-fit | <length> |
+<percentage> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">intrinsic width</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>The content-width should be the intrinsic content-width.</dd>
+
+ <dt>scale-to-fit</dt>
+ <dd>A size of the content-width equal to the width of the viewport.
+This implies a certain scaling factor to be applied onto the content.</dd>
+
+ <dt><length></dt>
+ <dd>An absolute size for the content-width. This implies a certain
+scaling factor to be applied onto the content.</dd>
+
+ <dt><percentage></dt>
+ <dd>A percentage representing a scaling factor applied to the intrinsic
+width.</dd>
+ </dl>
+
+ <h3 id="display-prop">display</h3>
+
+ <p>This section specifies additional values for the property.</p>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">footnote-body | footnote-reference | foreign | graphic | leader | wrapper</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">inline</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">all elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>footnote-body</dt>
+ <dd>The contents of the element goes to the footnote area. The element
+must be either immediately preceded by an element of type
+<code>footnote-reference</code> or have a <code>:before</code> pseudo element
+of that type. Otherwise it is treated as if its display type
+were <code>none</code>. Whitespace between a footnote reference and body is
+removed. In case a pseudo element is used, the contents it generates is
+displayed in the flow, as well as in the footnote body.</dd>
+
+ <dt>footnote-reference</dt>
+ <dd>This is an inline variant. Its contents is displayed in the flow. It
+can occur without a following <code>footnote-body</code> element.</dd>
+
+ <dt>foreign</dt>
+ <dd>If an element has this display type, it is placed unmodified in an
+<code>fo:instream-foreign-object</code> element.</dd>
+
+ <dt>graphic</dt>
+ <dd>This display type is used to include external graphics. It is an
+inline level display type. Elements marked with it are replaced elements.</dd>
+
+ <dt>leader</dt>
+ <dd>This display type is used to produce &xslfo; leaders. It is an inline
+level display type.</dd>
+
+ <dt>wrapper</dt>
+ <dd>An element with this display type doesn't contribute any formatting
+objects. Its inherited properties are nevertheless inherited by its
+subtree.</dd>
+ </dl>
+
+ <h3 id="force-page-count-prop">force-page-count</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | even | odd | end-on-even |
+end-on-odd | no-force | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">the page context</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <p>The property is used to impose a constraint on the number of pages
+in a page sequence. In the event that this constraint is not satisfied, an
+additional page will be added to the end of the sequence. This page becomes the
+<q>last</q> page of that sequence.</p>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>Force the last page in this page sequence to be an odd page if the
+initial page number of the next page sequence is even. Force it to be an even
+page if the initial page number of the next page sequence is odd. If there is
+no next page sequence or if the value of its initial page number is <q>auto</q>
+do not force any page.</dd>
+
+ <dt>even</dt>
+ <dd>Force an even number of pages in this page sequence.</dd>
+
+ <dt>odd</dt>
+ <dd>Force an odd number of pages in this page sequence.</dd>
+
+ <dt>end-on-even</dt>
+ <dd>Force the last page in this page sequence to be an even page.</dd>
+
+ <dt>end-on-odd</dt>
+ <dd>Force the last page in this page sequence to be an odd page.</dd>
+
+ <dt>no-force</dt>
+ <dd>Do not force either an even or an odd number of pages in this
+page sequence.</dd>
+ </dl>
+
+ <h3 id="hyphenate-prop">hyphenate</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">false | true | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">false</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block-level and inline-level elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>false</dt>
+ <dd>Hyphenation is not active for the text in this element.</dd>
+
+ <dt>true</dt>
+ <dd>Hyphenation is active for the text in this element.</dd>
+ </dl>
+
+ <h3 id="initial-page-number-prop">initial-page-number</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | auto-odd | auto-even | <integer>
+| inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">the page context</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>The initial number shall be set to 1 if no previous page-sequence
+exists in the document. If a preceding page-sequence exists, the initial number
+will be one greater than the last number for that sequence.</dd>
+
+ <dt>auto-odd</dt>
+ <dd>A value is determined in the same manner as for <q>auto</q>. If that
+value is an even number 1 is added.</dd>
+
+ <dt>auto-even</dt>
+ <dd>A value is determined in the same manner as for <q>auto</q>. If that
+value is an odd number 1 is added.</dd>
+
+ <dt><integer></dt>
+ <dd>A positive integer. If a negative or non-integer value is provided,
+the value will be rounded to the nearest integer value greater than or equal to
+1.</dd>
+ </dl>
+
+ <h3 id="leader-alignment-prop">leader-alignment</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">none | reference-area | page | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <p>Specifies whether leader elements having identical content and property
+values shall have their patterns aligned with each other, with respect to their
+common reference-area or page. For leader elements where the
+<code>leader-pattern</code> property is specified as <code>dots</code> or as
+<code>use-content</code>, this property will be honored. If the leader elements
+is aligned, the left-edge of each cycle of the repeated pattern will be placed
+on the left-edge of the next cycle in the appropriate pattern-alignment
+grid.</p>
+
+ <dl>
+ <dt>none</dt>
+ <dd>Leader-pattern has no special alignment.</dd>
+
+ <dt>page</dt>
+ <dd>Leader-pattern is aligned as if it began on the current page's
+left-edge.</dd>
+
+ <dt>reference-area</dt>
+ <dd>Leader-pattern is aligned as if it began on the current
+reference-area's content-rectangle left-edge.</dd>
+ </dl>
+
+ <h3 id="leader-length-prop">leader-length</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><length> | <percentage> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">12.0pt</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">refer to the width of the content-rectangle of the parent
+area.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><length></dt>
+ <dd>Sets the length of a leader element.</dd>
+
+ <dt><percentage></dt>
+ <dd>Sets the length of a leader element to a percentage of the
+width of the content-rectangle of the parent area.</dd>
+ </dl>
+
+ <h3 id="leader-pattern-prop">leader-pattern</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">space | rule | dots | use-content |
+inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">space</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>dots</dt>
+ <dd>Leader is to be filled with a repeating sequence of dots. The choice
+of dot character is dependent on the user agent.</dd>
+
+ <dt>rule</dt>
+ <dd>Leader is to be filled with a rule. If this choice is selected, the
+<code>rule-thickness</code> and <code>rule-style</code> properties are used to
+set the leader's style.</dd>
+
+ <dt>space</dt>
+ <dd>Leader is to be filled with blank space.</dd>
+
+ <dt>use-content</dt>
+ <dd>Leader is to be filled with a repeating pattern as specified by the
+children of the leader element.</dd>
+ </dl>
+
+ <h3 id="leader-pattern-width-prop">leader-pattern-width</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">use-font-metrics | <length> |
+<percentage> | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">use-font-metrics</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">refer to the width of the content-rectangle of the parent
+area.</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>use-font-metrics</dt>
+ <dd>Use the width of the leader-pattern as determined from its font
+metrics.</dd>
+
+ <dt><length></dt>
+ <dd>Sets the length for leader-pattern-repeating. The leader will have an
+inline-space inserted after each pattern cycle to account for any difference
+between the width of the pattern as determined by the font metrics and the
+width specified in this property. If the length specified is less than the
+value that would be determined via the <code>use-font-metrics</code> choice,
+the value of this property is computed as if <code>use-font-metrics</code>
+choice had been specified.</dd>
+
+ <dt><percentage></dt>
+ <dd>Sets the length for leader-pattern-repeating to a percentage of the
+width of the content-rectangle of the parent area.</dd>
+ </dl>
+
+ <p>For leader elements where the
+<code>leader-pattern</code> property is specified as <code>dots</code> or as
+<code>use-content</code>, this property will be honored.</p>
+
+ <h3 id="link-prop">link</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><identifier> | attr(X)</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block-level and inline-level elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><identifier></dt>
+ <dd>The qualified name of an attribute, the value of which is either a
+target &id; or a &uri;. It is considered as an &id; if the attribute is of type
+<acronym>IDREF</acronym>. This way a distinction is made with a relative &url;.
+Note that the attribute type information should be available. This requires a
+document type definition. This type of value is <i>deprecated</i>, because it
+doesn't support namespace prefixes.</dd>
+
+ <dt>attr(X)</dt>
+ <dd>This returns the value of the attribute of the subject with the
+qualified name X. The &css3; namespace prefixes are supported. The value is
+considered as an &id; if the attribute is of type <acronym>IDREF</acronym>.
+This way a distinction is made with a relative &url;. Note that the attribute
+type information should be available. This requires a document type
+definition.</dd>
+ </dl>
+
+ <h3 id="list-style-type-prop">list-style-type</h3>
+
+ <p>This section specifies additional glyph values for the property.</p>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">box | check | diamond | hyphen</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">disc</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">elements with <q>display: list-item</q></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>box</dt>
+ <dd>A hollow square.</dd>
+
+ <dt>check</dt>
+ <dd>A check mark.</dd>
+
+ <dt>diamond</dt>
+ <dd>A filled diamond.</dd>
+
+ <dt>hyphen</dt>
+ <dd>A hyphen bullet.</dd>
+ </dl>
+
+ <h3 id="orientation-prop">orientation</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">0 | 90 | 180 | 270 | -90 | -180 | -270 |
+inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">0</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block, table and table cell elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>0</dt>
+ <dd>The material in this element is not rotated.</dd>
+
+ <dt>90</dt>
+ <dd>The material in this element is rotated 90 degrees counter-clockwise
+with respect to the containing block element.</dd>
+
+ <dt>180</dt>
+ <dd>The material in this element is rotated 180 degrees counter-clockwise
+with respect to the containing block element.</dd>
+
+ <dt>270</dt>
+ <dd>The material in this element is rotated 270 degrees counter-clockwise
+with respect to the containing block element.</dd>
+
+ <dt>-90</dt>
+ <dd>The material in this element is rotated 90 degrees clockwise
+with respect to the containing block element.</dd>
+
+ <dt>-180</dt>
+ <dd>The material in this element is rotated 180 degrees clockwise
+with respect to the containing block element.</dd>
+
+ <dt>-270</dt>
+ <dd>The material in this element is rotated 270 degrees clockwise
+with respect to the containing block element.</dd>
+ </dl>
+
+ <h3 id="page-prop">page</h3>
+
+ <p>This section specifies the property in the context of static regions. It
+defines the pages to which the static region applies. If more than one static
+region of the same kind (left, right, top or bottom) applies to a page, the
+most specific is selected, i.e. the one for which the most conditions are
+fulfilled. Each property value expresses a number of conditions.</p>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | first | last | left | right |
+blank | first-left | first-right | last-left | last-right |
+blank-left | blank-right | first-<identifier> | last-<identifier> |
+left-<identifier> | right-<identifier> | blank-<identifier> |
+first-left-<identifier> | first-right-<identifier> |
+last-left-<identifier> | last-right-<identifier> |
+blank-left-<identifier> | blank-right-<identifier> |
+<identifier></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">static regions</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>Applies to any page.</dd>
+
+ <dt>blank</dt>
+ <dd>Applies if the page is a blank page. Blank pages can be generated,
+for example, when page breaks are forced to left or right pages.</dd>
+
+ <dt>blank-left</dt>
+ <dd>Applies if the page is a blank and a left page.</dd>
+
+ <dt>blank-right</dt>
+ <dd>Applies if the page is a blank and a right page.</dd>
+
+ <dt>blank-<identifier></dt>
+ <dd>Applies if the page is a blank and a named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>blank-left-<identifier></dt>
+ <dd>Applies if the page is a blank, left and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>blank-right-<identifier></dt>
+ <dd>Applies if the page is a blank, right and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>first</dt>
+ <dd>Applies if the page is a first page.</dd>
+
+ <dt>first-left</dt>
+ <dd>Applies if the page is a first and a left page.</dd>
+
+ <dt>first-right</dt>
+ <dd>Applies if the page is a first and a right page.</dd>
+
+ <dt>first-<identifier></dt>
+ <dd>Applies if the page is a first and a named page, with the name
+set to the specified identifier. When the document switches to a named page
+sequence, using the <code>page</code> property in the regular way, the first
+page of that sequence is a first page.</dd>
+
+ <dt>first-left-<identifier></dt>
+ <dd>Applies if the page is a first, left and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>first-right-<identifier></dt>
+ <dd>Applies if the page is a first, right and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>last</dt>
+ <dd>Applies if the page is a last page.</dd>
+
+ <dt>last-left</dt>
+ <dd>Applies if the page is a last and a left page.</dd>
+
+ <dt>last-right</dt>
+ <dd>Applies if the page is a last and a right page.</dd>
+
+ <dt>last-<identifier></dt>
+ <dd>Applies if the page is a last and a named page, with the name
+set to the specified identifier. When the document switches to a named page
+sequence, using the <code>page</code> property in the regular way, the last
+page of that sequence is a last page.</dd>
+
+ <dt>last-left-<identifier></dt>
+ <dd>Applies if the page is a last, left and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>last-right-<identifier></dt>
+ <dd>Applies if the page is a last, right and named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>left</dt>
+ <dd>Applies if the page is a left page.</dd>
+
+ <dt>left-<identifier></dt>
+ <dd>Applies if the page is a left and a named page, with the name
+set to the specified identifier.</dd>
+
+ <dt>right</dt>
+ <dd>Applies if the page is a right page.</dd>
+
+ <dt>right-<identifier></dt>
+ <dd>Applies if the page is a right and a named page, with the name
+set to the specified identifier.</dd>
+
+ <dt><identifier></dt>
+ <dd>Applies if the page is a named page, with the name
+set to the specified identifier.</dd>
+ </dl>
+
+ <h3 id="precedence-prop">precedence</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">false | true | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">false</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">static top and bottom regions</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>false</dt>
+ <dd>The width of the region is reduced by the incursions of the left and
+right regions.</dd>
+
+ <dt>true</dt>
+ <dd>The height of the left and right regions is reduced by the incursions
+of this region.</dd>
+ </dl>
+
+ <h3 id="region-prop">region</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">body | left | right | top | bottom | none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">all elements, but see prose</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>body</dt>
+ <dd>There should be one element with this value for the property. For the
+contents of this element the page sequences will be generated.</dd>
+
+ <dt>bottom</dt>
+ <dd>This element becomes the bottom static region. The pages for which
+this is the case can be limited through the
+<a href="#page-prop" shape="rect"><code>page</code></a> property.</dd>
+
+ <dt>left</dt>
+ <dd>This element becomes the left static region.</dd>
+
+ <dt>none</dt>
+ <dd>The element is not a region.</dd>
+
+ <dt>right</dt>
+ <dd>This element becomes the right static region.</dd>
+
+ <dt>top</dt>
+ <dd>This element becomes the top static region.</dd>
+ </dl>
+
+ <p>The static region elements should be the first child elements of the
+body region. In other words, they should precede all elements which are not
+static regions, otherwise their <code>region</code> property is ignored. The
+property is also ignored if there are no @page rules. In that case the default
+page set-up is generated.</p>
+
+ <h3 id="rowspan-prop">rowspan</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><integer></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">1</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">table cells</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><integer></dt>
+ <dd>Expresses the number of rows the table cell will span. The value
+must be larger than or equal to 1.</dd>
+ </dl>
+
+ <h3 id="rule-style-prop">rule-style</h3>
+
+ <p>This property applies only if the <code>leader-pattern</code> property is
+specified as <code>rule</code>.</p>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">none | dotted | dashed | solid | double |
+groove | ridge | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">solid</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>dashed</dt>
+ <dd>The rule is a series of short line segments.</dd>
+
+ <dt>dotted</dt>
+ <dd>The rule is a series of dots.</dd>
+
+ <dt>double</dt>
+ <dd>The rule is two solid lines. The sum of the two lines and the space
+between them equals the value of the <code>rule-thickness</code> property.</dd>
+
+ <dt>groove</dt>
+ <dd>The rule looks as though it were carved into the canvas. (Top/left
+half of the rule's thickness is the color specified; the other half is
+white.)</dd>
+
+ <dt>none</dt>
+ <dd>No rule, forces <code>rule-thickness</code> to 0.</dd>
+
+ <dt>ridge</dt>
+ <dd>The opposite of <q>groove</q>, the rule looks as though it were
+coming out of the canvas. (Bottom/right half of the rule's thickness is the
+color specified; the other half is white.)</dd>
+
+ <dt>solid</dt>
+ <dd>The rule is a single line segment.</dd>
+ </dl>
+
+ <h3 id="rule-thickness-prop">rule-thickness</h3>
+
+ <p>This property applies only if the <code>leader-pattern</code> property is
+specified as <code>rule</code>.</p>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><length></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">1.0pt</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">leader elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">yes</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><length></dt>
+ <dd>The rule-thickness is always perpendicular to its length-axis. The
+rule is thickened equally above and below the line's alignment position.</dd>
+ </dl>
+
+ <h3 id="scaling-prop">scaling</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">uniform | non-uniform | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">uniform</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">intrinsic width</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>non-uniform</dt>
+ <dd>Scaling need not preserve the intrinsic aspect ratio.</dd>
+
+ <dt>uniform</dt>
+ <dd>Scaling should preserve the intrinsic aspect ratio.</dd>
+ </dl>
+
+ <h3 id="scaling-method-prop">scaling-method</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">auto | integer-pixels |
+resample-any-method | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">auto</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">intrinsic width</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>auto</dt>
+ <dd>The User Agent is free to choose either resampling, integer scaling,
+or any other scaling method.</dd>
+
+ <dt>integer-pixels</dt>
+ <dd>The User Agent should scale the image such that each pixel in the
+original image is scaled to the nearest integer number of device-pixels that
+yields an image less-then-or-equal-to the image size derived from the
+content-height, content-width, and scaling properties.</dd>
+
+ <dt>resample-any-method</dt>
+ <dd>The User Agent should resample the supplied image to provide an image
+that fills the size derived from the <code>content-height</code>,
+<code>content-width</code>, and <code>scaling</code> properties. The user agent
+may use any sampling method.</dd>
+ </dl>
+
+ <h3 id="src-prop">src</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1"><identifier> | attr(X)</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none, value required</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">graphic elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt><identifier></dt>
+ <dd>The qualified name of an attribute, the value of which is a
+&uri;. This type of value is <i>deprecated</i>, because it doesn't support
+namespace prefixes.</dd>
+
+ <dt>attr(X)</dt>
+ <dd>This returns the value of the attribute of the subject with the
+qualified name X. The &css3; namespace prefixes are supported. The value is a
+&uri;.</dd>
+ </dl>
+
+ <h3 id="string-set-prop">string-set</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">none | <identifier> contents |
+<identifier> <content></td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">none</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">all elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>none</dt>
+ <dd>No named string is set.</dd>
+
+ <dt><identifier> contents</dt>
+ <dd>The string named by the identifier is set to the textual contents of
+the element.</dd>
+
+ <dt><identifier> <content></dt>
+ <dd>The string named by the identifier is set to the result of the
+evaluation of the expression in <content>. The syntax for the expression is
+the same as that for the <code>content</code> property.</dd>
+ </dl>
+
+ <h3 id="text-align-last-prop">text-align-last</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">center | inside | justify | left |
+outside | relative | right | inherit</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">relative</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">block elements</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>center</dt>
+ <dd>Specifies that the contents is to be centered horizontally.</dd>
+
+ <dt>inside</dt>
+ <dd>If the page binding edge is on the left-edge, the alignment will be
+left. If the binding is on the right-edge, the alignment will be right. If
+neither, use left alignment.</dd>
+
+ <dt>justify</dt>
+ <dd>Specifies that the contents is to be expanded to fill the available
+width.</dd>
+
+ <dt>left</dt>
+ <dd>Specifies that the contents is to be aligned on the left-edge.</dd>
+
+ <dt>outside</dt>
+ <dd>If the page binding edge is on the left-edge, the alignment will be
+right. If the binding is on the right-edge, the alignment will be left. If
+neither, use right alignment.</dd>
+
+ <dt>relative</dt>
+ <dd>If <code>text-align</code> is <code>justify</code>, then the
+alignment of the last line, and of any line ending in <span style="font-family: serif-title">U+000A</span>, will be left. If <code>text-align</code> is not
+<code>justify</code>, <code>text-align-last</code> will use the value of
+<code>text-align</code>.</dd>
+
+ <dt>right</dt>
+ <dd>Specifies that the contents is to be aligned on the right-edge.</dd>
+ </dl>
+
+ <h3 id="table-omit-footer-at-break-prop">table-omit-footer-at-break</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">false | true</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">false</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">tables</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>false</dt>
+ <dd>This property specifies that the footer should not be ommitted.</dd>
+
+ <dt>true</dt>
+ <dd>This property specifies that the footer should be ommitted.</dd>
+ </dl>
+
+ <h3 id="table-omit-header-at-break-prop">table-omit-header-at-break</h3>
+
+ <table class="property">
+ <col width="1*" span="1" />
+ <col width="5*" span="1" />
+ <tbody>
+ <tr>
+ <td rowspan="1" colspan="1">Value:</td>
+ <td rowspan="1" colspan="1">false | true</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Initial:</td>
+ <td rowspan="1" colspan="1">false</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Applies to:</td>
+ <td rowspan="1" colspan="1">tables</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Inherited:</td>
+ <td rowspan="1" colspan="1">no</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Percentages:</td>
+ <td rowspan="1" colspan="1">N/A</td>
+ </tr>
+ <tr>
+ <td rowspan="1" colspan="1">Media:</td>
+ <td rowspan="1" colspan="1">print</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <dl>
+ <dt>false</dt>
+ <dd>This property specifies that the header should not be ommitted.</dd>
+
+ <dt>true</dt>
+ <dd>This property specifies that the header should be ommitted.</dd>
+ </dl>
+
+ <h2 id="misc-specs">Miscellaneous Specifications</h2>
+
+ <h3 id="blank-pseudo-class">The :blank Pseudo-class</h3>
+
+ <p>The :blank pseudo-class is available to specify properties in the page
+context for blank pages. Those can be generated, for example, when pages are
+forced to start at the left or right.</p>
+
+ <h3 id="last-pseudo-class">The :last Pseudo-class</h3>
+
+ <p>The :last pseudo-class is available to specify properties in the page
+context for last pages. This is analogous to the :first pseudo-class.</p>
+
+ <h3 id="page-properties">The background, border and padding page properties</h3>
+
+ <p>The background, border and padding properties, as defined in [<a href="#CSS3P" shape="rect"><acronym>CSS3P</acronym></a>], are implemented. They
+are, however, not entirely compatible with that specification. The
+implementation applies the properties to the <code>region-body</code>, because
+in &xslfo; they are not defined at the page master level.</p>
+
+ <h3 id="page-counters">The page And pages Counters</h3>
+
+ <p>The page counter represents the current page number, while the pages
+counter represents the total number of pages in the document. Both can be used
+in static regions only. The page counter may be reset in the page context.</p>
+
+ <h3 id="page-ref-function">The page-ref Function</h3>
+
+ <p>The page-ref function can be used in the <code>content</code>
+property. Its only parameter is either the qualified name of an attribute that
+contains the &id; of another element, or the attr(X) function, where X is the
+qualified name of such an attribute. The former is deprecated, because it
+doesn't support &css3; namespace prefixes, while the latter does. The function
+call will be replaced with the number of the page the target element is on.</p>
+
+ <h3 id="string-function">The string Function</h3>
+
+ <p>The string function produces the string that was saved with a
+<code>string-set</code> property. Its argument is the name used in a
+<code>string-set</code> property. If a named string is set more than once on a
+page, the first occurrence will be returned by the string function.</p>
+
+ <h3 id="footnote-counter">The footnote Counter Style</h3>
+
+ <p>This counter style produces symbols in the following order: *, †,
+‡, §, ||, ¶, #, **, ††,
+‡‡, §§. If the counter value if larger than the
+number of symbols in the preceding list, the * symbol is generated.</p>
+
+ <h3 id="pcw-unit">The pcw Unit</h3>
+
+ <p>This unit is available for the <code>width</code> property of an element
+with display type <code>table-column</code>. It expresses the proportional width
+for a table column. The value should be divided by the sum of all the present
+proportional widths, which itself is equal to the width of the table minus all
+fixed column widths.</p>
+
+ <h3 id="namespaces">The @namespace Rule</h3>
+
+ <p>With the @namespace rule a namespace can be declared, with or without a
+prefix. In the latter case it is the default namespace. The scope of a declared
+namespace is limited to the style sheet entity in which it is declared. The
+ at namespace rule should come right after the @import rules if there are any and
+before all other rules. An @namespace rule has an optional prefix argument,
+which is an identifier, followed by a mandatory &uri; specification. Consult
+[<a href="#CSS3S" shape="rect"><acronym>CSS3S</acronym></a>] to learn how namespaces work
+with selectors.</p>
+
+ <div class="separator" />
+
+ <h1>Embedding In An Application<span /></h1>
+
+ <h2>API Specification</h2>
+
+ <h3 id="CSSToXSLFOFilter">be.re.css.CSSToXSLFOFilter</h3>
+
+ <p>This class extends
+<a href="http://www.saxproject.org/apidoc/org/xml/sax/helpers/XMLFilterImpl.html" shape="rect">org.xml.sax.helpers.XMLFilterImpl</a>.
+The source of the &sax; events that are sent through the filter must be
+namespace aware. If it is an &xml; parser this option must be turned on.</p>
+
+ <h4>Constructors</h4>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+userAgentParameters,</div>
+ <div class="p"><a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">XMLReader
+parent,</a></div>
+ <div class="p">boolean debug</div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <dl>
+ <dt id="baseUrl">baseUrl</dt>
+ <dd>
+ <p>The <code>baseUrl</code> is used to resolve relative &url;s. This
+includes references to style sheets as well as other resources. Because of the
+latter the resulting &xslfo; document is not relocatable. The relative &url;s
+in the input document are transformed into absolute ones using the
+<code>baseUrl</code>. This copes with the case where the input document is an
+anonymous stream that refers to style sheets in a relative way. The set of
+style sheets is then relocatable through the <code>baseUrl</code>.</p>
+
+ <p>If <code>baseUrl</code> is <code>null</code> no resolution of
+relative &url;s will be done.</p>
+ </dd>
+
+ <dt id="userAgentStyleSheet">userAgentStyleSheet</dt>
+ <dd>The default style sheet for the filter. It is used in the way
+described in section 6.4 of <a href="#CSS2" shape="rect">[&css2;]</a>. If the parameter is
+<code>null</code>, a default style sheet for &xhtml; is used.</dd>
+
+ <dt id="userAgentParameters">userAgentParameters</dt>
+ <dd>These are defined in section <a href="#uapar" shape="rect"><q><i>User Agent
+Parameters</i></q></a>.</dd>
+
+ <dt>parent</dt>
+ <dd>The parent filter. It must not be <code>null</code>.</dd>
+
+ <dt id="debug">debug</dt>
+ <dd>If <code>debug</code> is <code>true</code> a number of debug
+files are dumped. They show the results of the internal processing steps.</dd>
+ </dl>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+userAgentParameters,</div>
+ <div class="p"><a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">XMLReader
+parent</a></div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter with <code>debug</code> set to
+<code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">XMLReader
+parent</a></div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter with an empty <code>userAgentParameters</code> map
+and <code>debug</code> set to <code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">XMLReader
+parent</a></div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter with <code>userAgentStyleSheet</code> set to
+<code>null</code>, an empty <code>userAgentParameters</code> map
+and <code>debug</code> set to <code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">XMLReader
+parent</a></div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter with <code>baseUrl</code> and
+<code>userAgentStyleSheet</code> set to
+<code>null</code>, an empty <code>userAgentParameters</code> map
+and <code>debug</code> set to <code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+userAgentParameters,</div>
+ <div class="p">boolean debug</div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter without a parent.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+userAgentParameters</div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter without a parent and <code>debug</code> set to
+<code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet</div>
+ <div>) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter without a parent, an empty
+<code>userAgentParameters</code> map and <code>debug</code> set to
+<code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl) throws <a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter without a parent, <code>userAgentStyleSheet</code>
+set to <code>null</code>, an empty <code>userAgentParameters</code> map and
+<code>debug</code> set to <code>false</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOFilter() throws
+<a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Constructs the filter without a parent, <code>baseUrl</code> and
+<code>userAgentStyleSheet</code> set to <code>null</code>, an empty
+<code>userAgentParameters</code> map and <code>debug</code> set to
+<code>false</code>.</p>
+
+ <h4>Methods</h4>
+
+ <div class="method">
+ <div id="getBaseUrl">public <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+getBaseUrl()</div>
+ </div>
+
+ <p>Returns the base &url; of the filter. It is either set in a constructor
+or with the <a href="#setBaseUrl" shape="rect"><code>setBaseUrl</code></a> method.</p>
+
+ <div class="method">
+ <div id="getParameters">public <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+getParameters()</div>
+ </div>
+
+ <p>Returns the User Agent parameters of the filter. They are either set in
+a constructor or with the <a href="#setUserAgentStyleSheet" shape="rect"><code>setUserAgentStyleSheet</code></a>
+method.</p>
+
+ <div class="method">
+ <div id="getUserAgentStyleSheet">public <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+getUserAgentStyleSheet()</div>
+ </div>
+
+ <p>Returns the User Agent style sheet of the filter. It is either set in a
+constructor or with the <a href="#setUserAgentStyleSheet" shape="rect"><code>setUserAgentStyleSheet</code></a>
+method.</p>
+
+ <div class="method">
+ <div id="setBaseUrl">public void setBaseUrl(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl)</div>
+ </div>
+
+ <p>Sets the base &url; of the filter. The base &url; is used to resolve
+relative &url;s in the input document. See also <a href="#baseUrl" shape="rect"><code>baseUrl</code></a>.</p>
+
+ <div class="method">
+ <div id="setParameters">public void setParameters(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+parameters)</div>
+ </div>
+
+ <p>Sets the User Agent parameters of the filter. See also <a href="#userAgentParameters" shape="rect"><code>userAgentParameters</code></a>.</p>
+
+ <div class="method">
+ <div id="setUserAgentStyleSheet">public void setUserAgentStyleSheet(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet)</div>
+ </div>
+
+ <p>Sets the User Agent style sheet of the filter. See also <a href="#userAgentStyleSheet" shape="rect"><code>userAgentStyleSheet</code></a>.</p>
+
+ <h3 id="CSSToXSLFOException">be.re.css.CSSToXSLFOException</h3>
+
+ <p>This class extends <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Exception.html" shape="rect">java.lang.Exception</a>.</p>
+
+ <h4>Constructors</h4>
+
+ <div class="method">
+ <div>public CSSToXSLFOException(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Exception.html" shape="rect">java.lang.Exception
+e</a>)</div>
+ </div>
+
+ <p>This is just a wrapper around <code>e</code>.</p>
+
+ <div class="method">
+ <div>public CSSToXSLFOException(<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html" shape="rect">java.lang.String
+message</a>)</div>
+ </div>
+
+ <p>The parameter will be returned by the method <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#getMessage()" shape="rect"><code>getMessage</code></a>.</p>
+
+ <h3 id="CSSToXSLFO">be.re.css.CSSToXSLFO</h3>
+
+ <p>This class contains a few convenience methods with which an &xml; filter
+set-up can be avoided, because they do it for you.</p>
+
+ <h4>Methods</h4>
+
+ <div class="method">
+ <div>public static void convert</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.InputStream.html" shape="rect">java.io.InputStream</a>
+in,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.OutputStream.html" shape="rect">java.io.OutputStream</a>
+out,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+baseUrl,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+catalog,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html" shape="rect">java.util.Map</a>
+userAgentParameters,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL[]</a>
+preprocessors,</div>
+ <div class="p">boolean validate,</div>
+ <div class="p">boolean debug</div>
+ <div>) throws <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.IOException.html" shape="rect">java.io.IOException</a>,
+<a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <dl>
+ <dt>in</dt>
+ <dd>The input document. It must not be <code>null</code>.</dd>
+
+ <dt>out</dt>
+ <dd>The output document. It must not be <code>null</code>.</dd>
+
+ <dt>baseUrl</dt>
+ <dd>See <a href="#baseUrl" shape="rect"><code>baseUrl</code></a>.</dd>
+
+ <dt>userAgentStyleSheet</dt>
+ <dd>See <a href="#userAgentStyleSheet" shape="rect"><code>userAgentStyleSheet</code></a>.</dd>
+
+ <dt>catalog</dt>
+ <dd>The catalog used to resolve entities during &xml; parsing. It must
+have the format defined by SGML Open Technical
+Resolution <acronym>TR9401:1997</acronym>. Only the <q>PUBLIC</q> and
+<q>SYSTEM</q> keywords are supported. It may be <code>null</code>.</dd>
+
+ <dt>userAgentParameters</dt>
+ <dd>See <a href="#userAgentParameters" shape="rect"><code>userAgentParameters</code></a>.</dd>
+
+ <dt>preprocessors</dt>
+ <dd>An array of &xslt; style sheets. The input goes through them in the
+specified order and before the
+<a href="#CSSToXSLFOFilter" shape="rect"><code>CSSToXSLFOFilter</code></a>.</dd>
+
+ <dt>validate</dt>
+ <dd>Turns on validation during &xml; parsing of the input document.</dd>
+
+ <dt>debug</dt>
+ <dd>See <a href="#debug" shape="rect"><code>debug</code></a>.</dd>
+ </dl>
+
+ <div class="method">
+ <div>public static void convert</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.InputStream.html" shape="rect">java.io.InputStream</a>
+in,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.OutputStream.html" shape="rect">java.io.OutputStream</a>
+out,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html" shape="rect">java.net.URL</a>
+userAgentStyleSheet</div>
+ <div>) throws <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.IOException.html" shape="rect">java.io.IOException</a>,
+<a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Calls the first variant of <code>convert</code> with
+<code>baseUrl</code>, <code>catalog</code> and <code>preprocessors</code> set
+to <code>null</code>, <code>validate</code> and <code>debug</code> set to false
+and an empty <code>userAgentParameters</code> map.</p>
+
+ <div class="method">
+ <div>public static void convert</div>
+ <div>(</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.InputStream.html" shape="rect">java.io.InputStream</a>
+in,</div>
+ <div class="p"><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.OutputStream.html" shape="rect">java.io.OutputStream</a>
+out</div>
+ <div>) throws <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io.IOException.html" shape="rect">java.io.IOException</a>,
+<a href="#CSSToXSLFOException" shape="rect">CSSToXSLFOException</a></div>
+ </div>
+
+ <p>Calls the first variant of <code>convert</code> with
+<code>baseUrl</code>, <code>userAgentStyleSheet</code>, <code>catalog</code> and
+<code>preprocessors</code> set to <code>null</code>, <code>validate</code> and
+<code>debug</code> set to false and an empty <code>userAgentParameters</code>
+map.</p>
+
+ <h2>Examples</h2>
+
+ <p>Since <a href="#CSSToXSLFOFilter" shape="rect">CSSToXSLFOFilter</a> is derived from
+<a href="http://www.saxproject.org/apidoc/org/xml/sax/helpers/XMLFilterImpl.html" shape="rect">org.xml.sax.helpers.XMLFilterImpl.</a>,
+it implements all &sax; event interfaces, as well as <a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLFilter.html" shape="rect">org.xml.sax.XMLFilter</a>.
+As a consequence, the filter can occur in input and output filter chains.</p>
+
+ <h3>Example 1</h3>
+
+ <p>The most straight-forward scenario is an application that reads the
+input document from a file and that writes an &xslfo; document into another
+file. For this we need an &xml; parser that can produce &sax; events. The
+parser implements the <a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">org.xml.sax.XMLReader</a>
+interface, so we can make it the parent of <a href="#CSSToXSLFOFilter" shape="rect">CSSToXSLFOFilter</a>.</p>
+
+ <p>In order to create a parser, we first have to set up the parser factory
+and make it namespace-aware. This happens at the lines 6 through 8. The filter
+can now be created with the input document as the base &url; (in case any
+relative &url;s need to be resolved) and an &xml; parser as its parent. This is
+done at the lines 9 through 14.</p>
+
+ <p>We now have to prepare the output part. We use an &xslt; transformer
+without a style sheet to copy the &sax; events to the output. The transformer
+must be in a form that accepts &sax; events. This is why a <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/sax/TransformerHandler.html" shape="rect">javax.xml.transform.sax.TransformerHandler</a>
+is created at lines 15 through 19. It implements the <a href="http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html" shape="rect">org.xml.sax.ContentHandler</a>
+interface. By giving it the output file as a result (lines 20 through 26), the
+&sax; events are transformed in the &xml; syntax.</p>
+
+ <p>The input and output parts can now be connected by setting the content
+handler of the filter to the transformer handler (line 27). The whole chain is
+then activated by calling the <code>parse</code> method, passing it the input
+document in the form of a file. The filter will pass this call onto the parser,
+which is its parent. The parser starts producing &sax; events that go through
+the filter and into the transformer handler.</p>
+
+ <pre xml:space="preserve"> 1 public class Example1
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 javax.xml.parsers.SAXParserFactory factory =
+ 7 javax.xml.parsers.SAXParserFactory.newInstance();
+
+ 8 factory.setNamespaceAware(true);
+
+ 9 be.re.css.CSSToXSLFOFilter filter =
+10 new be.re.css.CSSToXSLFOFilter
+11 (
+12 new java.io.File(args[0]).toURL(), // base URL.
+13 factory.newSAXParser().getXMLReader()
+14 );
+
+15 javax.xml.transform.sax.TransformerHandler handler =
+16 (
+17 (javax.xml.transform.sax.SAXTransformerFactory)
+18 javax.xml.transform.TransformerFactory.newInstance()
+19 ).newTransformerHandler();
+
+20 handler.setResult
+21 (
+22 new javax.xml.transform.stream.StreamResult
+23 (
+24 new java.io.File(args[1])
+25 )
+26 );
+
+27 filter.setContentHandler(handler);
+
+28 filter.parse
+29 (
+30 new org.xml.sax.InputSource
+31 (
+32 new java.io.FileInputStream(args[0])
+33 )
+34 );
+35 }
+36 }</pre>
+
+ <h3>Example 2</h3>
+
+ <p>A variation of the previous example is to perform the transformation of
+the &sax; events coming out of the filter to &xml; syntax in another way. In
+the previous example the parser had the control flow and the transformer acted
+as a handler of &sax; events. We can also give the control flow to a
+transformer that reads the input and copies it to the output, because we don't
+give it any style sheet. We need to create a <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/Transformer.html" shape="rect">javax.xml.transform.Transformer</a>.
+It is done at lines 15 through 17. The actual transformation is launched at
+lines 18 through 32. For this to work, we have to wrap our filter in a <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/sax/SAXSource.html" shape="rect">javax.xml.transform.sax.SAXSource</a>.
+For the transformer it is as if it is going to call an &xml; parser.</p>
+
+ <pre xml:space="preserve"> 1 public class Example2
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 javax.xml.parsers.SAXParserFactory factory =
+ 7 javax.xml.parsers.SAXParserFactory.newInstance();
+
+ 8 factory.setNamespaceAware(true);
+
+ 9 be.re.css.CSSToXSLFOFilter filter =
+10 new be.re.css.CSSToXSLFOFilter
+11 (
+12 new java.io.File(args[0]).toURL(), // base URL.
+13 factory.newSAXParser().getXMLReader()
+14 );
+
+15 javax.xml.transform.Transformer transformer =
+16 javax.xml.transform.TransformerFactory.newInstance().
+17 newTransformer();
+
+18 transformer.transform
+19 (
+20 new javax.xml.transform.sax.SAXSource
+21 (
+22 filter, // Acts as the XMLReader.
+23 new org.xml.sax.InputSource
+24 (
+25 new java.io.FileInputStream(args[0])
+26 )
+27 ),
+28 new javax.xml.transform.stream.StreamResult
+29 (
+30 new java.io.File(args[1])
+31 )
+32 );
+33 }
+34 }</pre>
+
+ <h3>Example 3</h3>
+
+ <p>This example shows how a pre-processing step can be added to the filter
+chain. The input document is transformed by the pre-processor and the resulting
+&sax; events go through the conversion filter. The pre-processor is created at
+lines 9 through 13. This one does nothing, i.e. it lets the events go through
+unmodified. In reality you would replace it with a class of your own.</p>
+
+ <p>The pre-processor instead of the filter is now initialised with the
+&xml; parser as its parent. The pre-processor will become the parent of the
+filter, as shown at line 18. When the <code>parse</code> method is called, the
+filter passes the call onto the pre-processor, which in turn passes it onto the
+parser. The &sax; events produced by the parser will then flow through the
+pre-processor, which in turn forwards them, possibly modified, to the
+filter.</p>
+
+ <pre xml:space="preserve"> 1 public class Example3
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 javax.xml.parsers.SAXParserFactory factory =
+ 7 javax.xml.parsers.SAXParserFactory.newInstance();
+
+ 8 factory.setNamespaceAware(true);
+
+ 9 org.xml.sax.helpers.XMLFilterImpl myPreprocessor =
+10 new org.xml.sax.helpers.XMLFilterImpl
+11 (
+12 factory.newSAXParser().getXMLReader()
+13 );
+
+14 be.re.css.CSSToXSLFOFilter filter =
+15 new be.re.css.CSSToXSLFOFilter
+16 (
+17 new java.io.File(args[0]).toURL(), // base URL.
+18 myPreprocessor
+19 );
+
+20 javax.xml.transform.sax.TransformerHandler handler =
+21 (
+22 (javax.xml.transform.sax.SAXTransformerFactory)
+23 javax.xml.transform.TransformerFactory.newInstance()
+24 ).newTransformerHandler();
+
+25 handler.setResult
+26 (
+27 new javax.xml.transform.stream.StreamResult
+28 (
+29 new java.io.File(args[1])
+30 )
+31 );
+
+32 filter.setContentHandler(handler);
+
+33 filter.parse
+34 (
+35 new org.xml.sax.InputSource
+36 (
+37 new java.io.FileInputStream(args[0])
+38 )
+39 );
+40 }
+41 }</pre>
+
+ <h3>Example 4</h3>
+
+ <p>The previous example can be modified in such a way that the
+pre-processor is an &xslt; style sheet. From this style sheet a <a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLFilter.html" shape="rect">org.xml.sax.XMLFilter</a>
+must be made, because it will sit between the &xml; parser and the filter. This
+is shown at lines 9 through 19. The transformer factory is re-used afterwards
+to create also the output handler.</p>
+
+ <pre xml:space="preserve"> 1 public class Example4
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 javax.xml.parsers.SAXParserFactory factory =
+ 7 javax.xml.parsers.SAXParserFactory.newInstance();
+
+ 8 factory.setNamespaceAware(true);
+
+ 9 javax.xml.transform.sax.SAXTransformerFactory trFactory =
+10 (javax.xml.transform.sax.SAXTransformerFactory)
+11 javax.xml.transform.TransformerFactory.newInstance();
+
+12 org.xml.sax.XMLFilter myPreprocessor =
+13 trFactory.newXMLFilter
+14 (
+15 new javax.xml.transform.stream.StreamSource
+16 (
+17 new java.io.File(args[2])
+18 )
+19 );
+
+20 myPreprocessor.setParent
+21 (
+22 factory.newSAXParser().getXMLReader()
+23 );
+
+24 be.re.css.CSSToXSLFOFilter filter =
+25 new be.re.css.CSSToXSLFOFilter
+26 (
+27 new java.io.File(args[0]).toURL(), // base URL.
+28 myPreprocessor
+29 );
+
+30 javax.xml.transform.sax.TransformerHandler handler =
+31 trFactory.newTransformerHandler();
+
+32 handler.setResult
+33 (
+34 new javax.xml.transform.stream.StreamResult
+35 (
+36 new java.io.File(args[1])
+37 )
+38 );
+
+39 filter.setContentHandler(handler);
+
+40 filter.parse
+41 (
+42 new org.xml.sax.InputSource
+43 (
+44 new java.io.FileInputStream(args[0])
+45 )
+46 );
+47 }
+48 }</pre>
+
+ <h3>Example 5</h3>
+
+ <p>In all previous examples we have been parsing an input document. In some
+applications, however, the data might come from somewhere else. It is possible,
+for example, to synthesize the &xml; from data that resides in the database. In
+such a scenario our filter no longer has a parent but becomes the &sax; event
+handler of some system method, <code>generateReport</code> in this example.<span class="footnote-reference" /><span class="footnote-body">Note
+that a real system method would probably need more than just the filter to do
+its work. It would therefore have more parameters.</span>
+This system method has the control flow. It fetches the data and generates the
+&sax; events. In the case the generated &xml; stream is not suitable for &css;
+conversion, a pre-processor may be specified as the parent of the filter.</p>
+
+ <pre xml:space="preserve"> 1 public class Example5
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 be.re.css.CSSToXSLFOFilter filter =
+ 7 new be.re.css.CSSToXSLFOFilter();
+
+ 8 javax.xml.transform.sax.TransformerHandler handler =
+ 9 (
+10 (javax.xml.transform.sax.SAXTransformerFactory)
+11 javax.xml.transform.TransformerFactory.newInstance()
+12 ).newTransformerHandler();
+
+13 handler.setResult
+14 (
+15 new javax.xml.transform.stream.StreamResult
+16 (
+17 new java.io.File(args[0])
+18 )
+19 );
+
+20 filter.setContentHandler(handler);
+21 generateReport(filter);
+22 }
+
+23 private static void
+24 generateReport(org.xml.sax.ContentHandler handler)
+25 {
+26 }
+27 }</pre>
+
+ <h3>Example 6</h3>
+
+ <p>It may be the case that you want to synthesize the &xml; stream in a
+system method, which needs the control flow, but that the interface of your
+&xslfo; formatter is such that it also needs the control flow. In other words,
+the formatter is not available in the form of a &sax; event handler, but has
+some method that must be called to perform the actual formatting. At lines 21
+through 31 there a hypothetical example of such a formatter.</p>
+
+ <p>To solve this control flow conflict you can create an adapter that
+implements the <a href="http://www.saxproject.org/apidoc/org/xml/sax/XMLReader.html" shape="rect">org.xml.sax.XMLReader</a>
+interface. Instead of actually parsing some &xml; you let both
+<code>parse</code> methods call your system method. The parameters the latter
+needs are passed through the constructor of the adapter. When the formatter now
+calls the <code>parse</code> method it really ends up calling the system
+method, which synthesizes the &sax; events.</p>
+
+ <pre xml:space="preserve"> 1 public class Example6
+ 2 {
+ 3 public static void
+ 4 main(String[] args) throws Exception
+ 5 {
+ 6 be.re.css.CSSToXSLFOFilter filter =
+ 7 new be.re.css.CSSToXSLFOFilter
+ 8 (
+ 9 new MyReportGenerator(new Object())
+10 );
+11 MyXSLFOFormatter myFormatter = new MyXSLFOFormatter();
+
+12 myFormatter.format
+13 (
+14 new javax.xml.transform.sax.SAXSource(filter, null),
+15 new java.io.FileOutputStream(args[0]));
+16 }
+
+17 private static void
+18 generateReport(Object context)
+19 {
+20 }
+
+21 public static class MyXSLFOFormatter
+22 {
+23 public void
+24 format
+25 (
+26 javax.xml.transform.Source source,
+27 java.io.OutputStream out
+28 )
+29 {
+30 }
+31 }
+
+32 public static class MyReportGenerator
+33 extends org.xml.sax.helpers.XMLFilterImpl
+34 {
+35 private Object context;
+
+36 public
+37 MyReportGenerator(Object context)
+38 {
+39 this.context = context;
+40 }
+
+41 public void
+42 parse(org.xml.sax.InputSource input)
+43 throws org.xml.sax.SAXException, java.io.IOException
+44 {
+45 generateReport(context);
+46 }
+
+47 public void
+48 parse(String systemId)
+49 throws org.xml.sax.SAXException, java.io.IOException
+50 {
+51 generateReport(context);
+52 }
+53 }
+54 }</pre>
+
+ <div class="separator" />
+
+ <h1>Some Techniques<span /></h1>
+
+ <p>A few practical cases of formatting contructs, which are either more
+advanced or not yet very common, are described in this chapter. Gradually, new
+cases will be added. The chapter is some sort of <q>how to</q> section in the
+user guide. The examples use &xhtml; as the input document language.</p>
+
+ <h2>Customising List Labels With Markers</h2>
+
+ <p>The genaration of the labels of an itemised list is somewhat fixed. It
+depends on the value of the <code>list-style-type</code> property.<span class="footnote-reference" /><span class="footnote-body">The
+<code>list-style-image</code> and <code>list-style-position</code> properties
+are not supported by this tool.</span> Sometimes, however, more control is
+required over how the labels look like. This can be achieved through
+markers.</p>
+
+ <p>Basically, you have to specify a <code>:before</code> pseudo element
+with the display type <code>marker</code> in your style sheet for those
+elements you have given the display type <code>list-item</code>. Strictly
+speaking that display type is not needed, but if you are about to convert your
+existing lists, those elements would have that display type.</p>
+
+ <p>In the pseudo element you have control over the formatting of the label.
+The only exception is that the <code>width</code> property must be fixed. The
+tool doesn't support the automatic calculation of the required width. If your
+style sheet doesn't specify a width, a default value will be used. In order to
+not depend on this value, it is best to specify one.</p>
+
+ <p>The following example is an ordered list with a nested ordered list in
+the second item. We are going to change the numbering as well as the alignment
+of the labels.</p>
+
+ <pre xml:space="preserve"> <ol>
+ <li>Item 1</li>
+ <li>Item 2
+ <ol>
+ <li>Subitem 1</li>
+ <li>Subitem 2</li>
+ <li>Subitem 3</li>
+ </ol>
+ </li>
+ <li>Item 3</li>
+ </ol></pre>
+
+ <p>In the style sheet we say that the <code>:before</code> pseudo element
+of any <code>li</code> under a <code>ol</code>, no matter the level, is a
+marker. In there, we increment the counter that is reset for each level of
+<code>ol</code>. We also display it with the <code>lower-roman</code> counter
+style instead of the default style (<code>decimal</code>). This style will show
+the effect of the right alignment of the text inside the label. The
+<code>marker-offset</code> property provides for a bit of space between the
+label and the list item body.</p>
+
+ <p>The <code>width</code> property deserves special attention. First of all
+it defines the width of the labels. Since markers shouldn't influence the
+positioning of the element they are attached to, the labels would stick out to
+the left by the amount of the value of the <code>width</code> property. In
+order to compensate this, we have to add a <code>margin-left</code> with the
+same value to the list item itself.</p>
+
+ <pre xml:space="preserve"> ol { counter-reset: list-counter; }
+
+ ol li { margin-left: 2em; }
+
+ ol li:before
+ {
+ content: counter(list-counter, lower-roman) ".";
+ counter-increment: list-counter;
+ display: marker;
+ marker-offset: 0.5em;
+ text-align: right;
+ width: 2em;
+ }</pre>
+
+ <p>The rendered result would like this:</p>
+
+ <ol class="example">
+ <li>Item 1</li>
+ <li>Item 2
+ <ol class="example">
+ <li>Subitem 1</li>
+ <li>Subitem 2</li>
+ <li>Subitem 3</li>
+ </ol>
+ </li>
+ <li>Item 3</li>
+ </ol>
+
+ <h2 class="example">Making Section Numbers <q>Stick Out</q></h2>
+
+ <p>Sometimes the text of the section titles must be aligned with the rest of
+the material, at the left side for example. As consequence, if the titles also
+have section numbers, those will stick out at the left side of the title, into
+the margin, just like the title of the current section. This can be obtained by
+specifying a <code>:before</code> pseudo element for the section titles with
+the display type <code>marker</code>. Because markers shouldn't influence the
+positioning of their associated element, the marker content is prepended. This
+is the piece of style sheet you would need:</p>
+
+ <pre xml:space="preserve"> h2:before
+ {
+ display: marker;
+ marker-offset: 0.5em;
+ padding-right: 0pt;
+ text-align: right;
+ width: 3em;
+ }</pre>
+
+ <h2>This Guide's Page Set-up</h2>
+
+ <p>The page set-up of this guide is rather advanced and is therefore an
+interesting practical case. The difficulty lies in specifying the static
+regions if there are many kinds of pages and if for each of those the static
+regions are different. i.e. very specific.</p>
+
+ <p>In order to avoid an explosion of &css; property specifications for all
+those regions, we can work in a sort of multidimensional way. This is possible
+through the combination of two things. First, we have the &css; cascading
+mechanism, which allows us to centralise common property specifications.
+Second, the can make use of the fact that the <code>class</code> attribute is a
+space-separated list of names. Because of this, an element can belong to
+several classes. These are the static regions of this guide:</p>
+
+ <pre xml:space="preserve"> <div class="first top"/>
+ <div class="title first bottom"/>
+ <div class="title first top"/>
+ <div class="copy-right first bottom"/>
+ <div class="copy-right first top"/>
+ <div class="left bottom"><span/></div>
+ <div class="right bottom"><span/></div>
+ <div class="left top"><span/></div>
+ <div class="right top"><span/></div>
+ <div class="front left bottom"><span/></div>
+ <div class="front right bottom"><span/></div>
+ <div class="blank bottom"/>
+ <div class="blank top"/></pre>
+
+ <p>Neither of them has a lot of content. It is all generated using the
+<code>span</code> element as a hook. For the empty <code>div</code> elements no
+content will be generated. You can see that all regions belong to more than one
+class. The more classes they belong to, the more specific is the set of pages
+they apply to. This is one dimension. The other is whether the static region is
+a top or bottom region.</p>
+
+ <p>The first region, for example, is a bottom region that applies only to the
+first page of the named page sequence called <q>title</q>. This is expressed in
+the style sheet as follows:</p>
+
+ <pre xml:space="preserve"> div.title.first
+ {
+ page: first-title;
+ }</pre>
+
+ <p>The rule applies if <code>class</code> is <q>title</q> <i>and</i>
+<q>first</q>. This is more specific then the following page assignment which
+also occurs in the style sheet:</p>
+
+ <pre xml:space="preserve"> div.first
+ {
+ page: first;
+ }</pre>
+
+ <p>Another example is the pair of bottom regions for left and right pages.
+They apply for left and right pages, no matter the page sequence. For the
+<q>front</q> page sequence, however, there is a more specific version. Making
+it more specific is done by the two page assignments in the following style
+sheet part. The style of the bottom region of the front part is a bit
+different. It display the page numbers in lower Roman. The complete style is
+obtained by cascading all the rules for the class <q>bottom</q>. The first two
+rules define the style for all bottom regions and the last one overrides the
+counter style.</p>
+
+ <pre xml:space="preserve"> div.bottom
+ {
+ height: 3em;
+ padding-top: 2em;
+ region: bottom;
+ }
+
+ div.bottom > span:before
+ {
+ content: counter(page);
+ }
+
+ div.front.left
+ {
+ page: left-front;
+ }
+
+ div.front.right
+ {
+ page: right-front;
+ }
+
+ div.front.bottom > span:before
+ {
+ content: counter(page, lower-roman);
+ }</pre>
+
+ <p>For blank pages we want no static regions at all, at least not visually.
+The presence of the top and bottom regions is defined generally through the
+<code>div.top</code> and <code>div.bottom</code> rules. As a consequence, no
+matter the page sequence, those static regions are always generated. All we
+have to do now is making sure they don't contain anything. This is done by the
+two empty regions with the <code>class</code> attribute set to <q>blank top</q>
+and <q>blank bottom</q> respectively. Those regions are assigned to blank pages
+with the following style sheet part:</p>
+
+ <pre xml:space="preserve"> div.blank
+ {
+ page: blank;
+ }</pre>
+
+ <p>There is one more special construct left to discuss: the absence of static
+top regions on the first page of a chapter. As with blank pages they are not
+really absent. They are merely made empty. It is in fact the first region with
+the classes <q>first</q> and <q>top</q>. This region is assigned to the
+<code>first</code> pseudo page. All chapters are however in the named page
+sequence <q>main</q>. If we do nothing only the first page of the first chapter
+will have an empty top region. We therefore should toggle the <code>page</code>
+property without adding extra pages. This can be achieved by inserting an empty
+<code>div</code> element between the chapters with the class <q>separator</q>.
+The page assignment for that class is <q>separator</q>. This named page is not
+used for anything else. Since the element is empty no page sequence is
+generated. The next <q>main</q> element, however, will start a new page
+sequence.</p>
+
+ <p>After the front matter not only the page number style changes to decimal,
+but the page numbering is also reset. We can't just reset the <code>page</code>
+counter in the <q>main</q> page context, because then the numbering would be
+reset for each chapter. Instead, the style sheet defines a
+<code>main-first</code> @page rule, which contains the same definitions as
+<code>main</code> and a <code>page</code> counter reset on top of it. This page
+is assigned to the first chapter only, using the <code>first-child</code>
+pseudo-class on the <code>h1</code> element.</p>
+
+ <h2>A Two-column Article</h2>
+
+ <p>Many articles and papers are formatted in two column mode. The title,
+abstract, authors, etc. are usually displayed across the two columns. With two
+extension properties it is possible to do this. The <code>column-count</code>
+should be set to <q>2</q> in the page context. The title material can be
+wrapped in a block element for which the <code>column-span</code> property is
+set to <q>all</q>.</p>
+
+ <h2>Initial Capitals</h2>
+
+ <p class="first-letter-example">A typographical effect that is often used are
+initial capitals. It consists of making the first letter of an article or
+chapter stand out by rendering it bigger and perhaps in another font and/or
+colour. In &css; this is supported through the <code>:first-letter</code>
+pseudo element, which is described in section 5.12.2 of
+[<a href="#CSS2" shape="rect">&css2;</a>]. In &csstoxslfo; it is implemented with the
+restriction that letter combinations, which are considered as one letter, are
+not examined. In case you need that, you can always use the Unicode ligature
+characters instead.</p>
+
+ <p>The technique was applied to the previous paragraph using the piece of
+style sheet below. Note the second deviation from the specification being the
+usage of the property <code>vertical-align</code> while the <code>float</code>
+property has the value <code>none</code>. It is allowed in &csstoxslfo; because
+otherwise we have no control over the alignment of the first letter with the
+lines next to it. This depends on the font and will always require some trial
+and error in order to get it right. The values for the other properties are
+obtained in the same way. In fact, for this special case, we work around the
+normal way a glyph is layed out in a line.</p>
+
+ <pre xml:space="preserve"> p:first-letter
+ {
+ font-family: serif-swash;
+ font-size: 46pt;
+ font-style: italic;
+ float: left;
+ line-height: 46pt;
+ padding-right: 6pt;
+ margin-bottom: -12pt;
+ vertical-align: 9pt;
+ }</pre>
+
+ </div>
+
+
+ <div class="back">
+
+ <h1>Special Provisions for XHTML<span /></h1>
+
+ <p>While the tool works for any &xml; vocabulary it does a number of
+things for &xhtml; specifically. Other vocabularies may be supported in the same
+way at some later stage. The items are the following:</p>
+
+ <ul>
+ <li>Non-&css; presentational hints are translated to the corresponding
+&css; rules, as prescribed in section 6.4.4 of
+[<a href="#CSS2" shape="rect">&css2;</a>];</li>
+
+ <li>The <code>lang</code> attribute is honored;</li>
+
+ <li>Hyperlinks are recognized and translated in &xslfo; links;</li>
+
+ <li>The <code>link</code> element can be used to specify external style
+sheets;</li>
+
+ <li>Style sheets can be embedded with the <code>style</code>
+element;</li>
+
+ <li>The <code>style</code> attribute is honored;</li>
+
+ <li>The <code>img</code> element is interpreted and processed;</li>
+
+ <li>The <code>html-header-mark</code> user agent parameter is
+available;</li>
+
+ <li>There is a user agent style sheet for &xhtml; that cascades against
+the one in appendix A of [<a href="#CSS2" shape="rect">&css2;</a>].</li>
+ </ul>
+
+ <div class="separator" />
+
+ <h1>The User Agent Style Sheet<span /></h1>
+
+ <h2>XHTML</h2>
+
+ <pre style="page-break-inside: auto" xml:space="preserve">@import "xhtml.css";
+ at namespace url(http://www.w3.org/1999/xhtml);
+
+ at media print
+{
+ a[href]
+ {
+ color: blue;
+ link: attr(href);
+ text-decoration: none;
+ }
+
+ a[name]
+ {
+ anchor: name;
+ }
+
+ blockquote, dl, ol, p, ul
+ {
+ margin: 0.83em 0pt;
+ }
+
+ blockquote
+ {
+ margin-left: 3em;
+ margin-right: 3em;
+ }
+
+ body
+ {
+ font-family: serif;
+ padding: 0pt;
+ region: body;
+ }
+
+ body:lang(da)
+ {
+ quotes: "\00BB" "\00AB";
+ }
+
+ body:lang(de-DE), body:lang(de-AT)
+ {
+ quotes: "\201E" "\201C" "\201A" "\2018"
+ }
+
+ body, body:lang(en), body:lang(es)
+ {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+ }
+
+ body:lang(fr)
+ {
+ quotes: "\00AB " " \00BB" "\2039 " " \203A";
+ }
+
+ body:lang(it)
+ {
+ quotes: "\00AB " " \00BB";
+ }
+
+ body:lang(nl)
+ {
+ quotes: "\201D" "\201D" "\2019" "\2019";
+ }
+
+ body:lang(no), bodylang:(pt), body:lang(de-CH)
+ {
+ quotes: "\00AB" "\00BB" "\2039" "\203A"
+ }
+
+ body:lang(sv)
+ {
+ quotes: "\00BB" "\00BB";
+ }
+
+ caption
+ {
+ margin: 0.5em 0pt;
+ }
+
+ dt
+ {
+ page-break-after: avoid;
+ }
+
+ h1
+ {
+ font-size: 1.6em;
+ margin-bottom: 0.7em;
+ margin-top: 1.4em;
+ }
+
+ h2
+ {
+ font-size: 1.3em;
+ margin-bottom: 0.6em;
+ margin-top: 1.2em;
+ }
+
+ h3
+ {
+ font-size: 1.1em;
+ }
+
+ h3, h4
+ {
+ margin-bottom: 0.5em;
+ margin-top: 1em;
+ }
+
+ h1, h2, h3, h4, h5, h6
+ {
+ hyphenate: false;
+ }
+
+ hr
+ {
+ border: 0.1pt solid;
+ }
+
+ img
+ {
+ content-height: scale-to-fit;
+ content-width: scale-to-fit;
+ display: graphic;
+ scaling: uniform;
+ src: attr(src);
+ }
+
+ li
+ {
+ margin-bottom: 0.8em;
+ margin-top: 0.8em;
+ }
+
+ li p, li blockquote, li dl, li ol, li ul
+ {
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+ }
+
+ li li
+ {
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+ }
+
+ li li p, li li blockquote, li li dl, li li ol, li li ul
+ {
+ margin-bottom: 0.3em;
+ margin-top: 0.3em;
+ }
+
+ li li li
+ {
+ margin-bottom: 0.4em;
+ margin-top: 0.4em;
+ }
+
+ li li li p, li li li blockquote, li li li dl, li li li ol,
+ li li li ul
+ {
+ margin-bottom: 0.3em;
+ margin-top: 0.3em;
+ }
+
+ li, p
+ {
+ text-align: justify;
+ }
+
+ pre
+ {
+ font-size: 0.85em;
+ }
+
+ ul
+ {
+ list-style-type: disc;
+ }
+
+ ol li ul, ul li ul
+ {
+ list-style-type: circle;
+ }
+
+ ol li ol li ul, ol li ul li ul, ul li ol li ul, ul li ul li ul
+ {
+ list-style-type: square;
+ }
+
+ q:after
+ {
+ content: close-quote;
+ }
+
+ q:before
+ {
+ content: open-quote;
+ }
+
+ script
+ {
+ display: none;
+ }
+
+ span.section-number
+ {
+ padding-right: 1em;
+ }
+}</pre>
+
+ <h2 style="page-break-before: always">DeltaXML</h2>
+
+ <pre style="page-break-inside: auto" xml:space="preserve">@namespace deltaxml
+ url(http://www.deltaxml.com/ns/well-formed-delta-v1);
+
+ at media print
+{
+ deltaxml|PCDATAnew, deltaxml|PCDATAold
+ {
+ display: inline;
+ }
+
+ deltaxml|exchange, deltaxml|new, deltaxml|old
+ {
+ display: wrapper;
+ }
+
+ *[deltaxml|delta="add"], deltaxml|PCDATAnew, deltaxml|new
+ {
+ text-decoration: underline;
+ }
+
+ *[deltaxml|delta="delete"], deltaxml|PCDATAold, deltaxml|old
+ {
+ text-decoration: line-through;
+ }
+
+ *[deltaxml|delta="add"]:before, deltaxml|PCDATAnew:before,
+ deltaxml|new, *[deltaxml|delta="delete"]:before,
+ deltaxml|PCDATAold:before, deltaxml|old
+ {
+ change-bar-class: changed;
+ change-bar-placement: alternate;
+ change-bar-style: solid;
+ change-bar-width: 0.2pt;
+ }
+
+ *[deltaxml|delta="add"]:after, deltaxml|PCDATAnew:after,
+ *[deltaxml|delta="delete"]:after, deltaxml|PCDATAold:after
+ {
+ change-bar-class: changed;
+ }
+}</pre>
+
+ <h2 style="page-break-before: always">XLink</h2>
+
+ <pre style="page-break-inside: auto" xml:space="preserve">@namespace xlink url(http://www.w3.org/1999/xlink);
+
+ at media print
+{
+ *[xlink|href]
+ {
+ link: attr(xlink|href);
+ }
+}</pre>
+
+ <div class="separator" />
+
+ <h1>References<span /></h1>
+
+ <dl>
+ <dt id="CSS2">[&css2;]</dt>
+ <dd><q><span class="title">Cascading Style Sheets, level 2, CSS2
+Specification</span></q>, W3C
+Recommendation 12 May 1998, Bert Bos, Håkon Wium Lie, Chris Lilley, Ian
+Jacobs,
+<a href="http://www.w3.org/TR/1998/REC-CSS2-19980512" shape="rect">http://www.w3.org/TR/1998/REC-CSS2-19980512</a>.</dd>
+
+ <dt id="CSS3G">[<acronym>CSS3G</acronym>]</dt>
+ <dd><q><span class="title">CSS3 Generated and Replaced Content
+Module</span></q>, W3C Working Draft 14 May 2003, Ian Hickson,
+<a href="http://www.w3.org/TR/2003/WD-css3-content-20030514" shape="rect">http://www.w3.org/TR/2003/WD-css3-content-20030514</a>.</dd>
+
+ <dt id="CSS3L">[<acronym>CSS3L</acronym>]</dt>
+ <dd><q><span class="title">CSS3 module: Lists</span></q>, W3C Working
+Draft 7 November 2002, Ian Hickson, Tantek Çelik,
+<a href="http://www.w3.org/TR/2002/WD-css3-lists-20021107" shape="rect">http://www.w3.org/TR/2004/WD-css3-lists-20021107</a>.</dd>
+
+ <dt id="CSS3P">[<acronym>CSS3P</acronym>]</dt>
+ <dd><q><span class="title">CSS3 Module: Paged Media</span></q>, W3C
+Working Draft 10 October 2006, Håkon Wium Lie, Melinda Grant,
+<a href="http://www.w3.org/TR/2006/WD-css3-page-20061010" shape="rect">http://www.w3.org/TR/2006/WD-css3-page-20061010</a>.</dd>
+
+ <dt id="CSS3S">[<acronym>CSS3S</acronym>]</dt>
+ <dd><q><span class="title">CSS3 Selectors</span></q>, W3C Working Draft 15
+December 2005, Daniel Glazman, Tantek
+Çelik, Ian Hickson, Peter Linss, John Williams, <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215" shape="rect">http://www.w3.org/TR/2005/WD-css3-selectors-20051215</a>.</dd>
+
+ <dt id="DELTA">[<acronym>DELTA</acronym>]</dt>
+ <dd><q><span class="title">How DeltaXML Represents Changes to XML
+Files</span></q>, DeltaXML Ltd., <a href="http://www.deltaxml.com/library/how-deltaxml-represents-changes.html" shape="rect">http://www.deltaxml.com/library/how-deltaxml-represents-changes.html</a>.</dd>
+
+ <dt id="NAMES">[<acronym>NAMES</acronym>]</dt>
+ <dd><q><span class="title">Namespaces in XML</span></q>, W3C
+Recommendation 14 January 1999, Tim Bray, Dave Hollander, Andrew Layman, <a href="http://www.w3.org/TR/REC-xml-names/" shape="rect">http://www.w3.org/TR/REC-xml-names/</a>.</dd>
+
+ <dt id="XHTML">[&xhtml;]</dt>
+ <dd><q><span class="title">XHTML™ 1.0 The Extensible HyperText
+Markup Language (Second Edition)</span></q>, W3C Recommendation 26 January 2000,
+revised 1 August 2002,
+<a href="http://www.w3.org/TR/2002/REC-xhtml1-20020801" shape="rect">http://www.w3.org/TR/2002/REC-xhtml1-20020801</a>.</dd>
+
+ <dt id="XLINK">[<acronym>XLINK</acronym>]</dt>
+ <dd><q><span class="title">XML Linking Language (XLink) Version
+1.0</span></q>, W3C Recommendation 27 June 2001, Steve DeRose, Eve Maler, David
+Orchard, <a href="http://www.w3.org/TR/2001/REC-xlink-20010627" shape="rect">http://www.w3.org/TR/2001/REC-xlink-20010627</a>.</dd>
+
+ <dt id="XSLFO">[&xslfo;]</dt>
+ <dd><q><span class="title">Extensible Stylesheet Language (XSL), Version
+1.0</span></q>, W3C
+Recommendation 15 October 2001, Sharon Adler, Anders Berglund, Jeff Caruso,
+Stephen Deach, Tony Graham, Paul Grosso, Eduardo Gutentag, Alex Milowski, Scott
+Parnell, Jeremy Richman, Steve Zilles,
+<a href="http://www.w3.org/TR/2001/REC-xsl-20011015/" shape="rect">http://www.w3.org/TR/2001/REC-xsl-20011015/</a>.</dd>
+
+ <dt id="XSLFO11">[<acronym>XSL-FO11</acronym>]</dt>
+ <dd><q><span class="title">Extensible Stylesheet Language (XSL), Version
+1.1</span></q>, W3C Recommendation 05 December 2006, Anders Berglund,
+<a href="http://www.w3.org/TR/2006/REC-xsl11-20061205/" shape="rect">http://www.w3.org/TR/2006/REC-xsl11-20061205/</a>.</dd>
+ </dl>
+
+ </div>
+ </body>
+</html>
diff --git a/doc/preprocess_xhtml.xsl b/doc/preprocess_xhtml.xsl
new file mode 100644
index 0000000..3d93c66
--- /dev/null
+++ b/doc/preprocess_xhtml.xsl
@@ -0,0 +1,126 @@
+<?xml version='1.0'?>
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" xmlns:xh="http://www.w3.org/1999/xhtml" version="1.0" exclude-result-prefixes="xh">
+
+ <xsl:output method="xml" indent="no" version="1.0" encoding="UTF-8" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" omit-xml-declaration="no" />
+
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:caption" mode="tot">
+ <div class="tot-line">
+ <xsl:call-template name="toc-link" />
+ </div>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:div[@class = 'toc' and not(node())]">
+ <xsl:copy>
+ <xsl:apply-templates select="@*" />
+ <h1 />
+ <div class="toc-main">
+ <xsl:apply-templates select="//xh:div[@class = 'main']//xh:h1 | //xh:div[@class = 'main']//xh:h2 | //xh:div[@class = 'main']//xh:h3" mode="toc" />
+ </div>
+ <div class="toc-back">
+ <xsl:apply-templates select="//xh:div[@class = 'back']//xh:h1 | //xh:div[@class = 'back']//xh:h2 | //xh:div[@class = 'back']//xh:h3" mode="toc" />
+ </div>
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:div[@class = 'tof' and not(node())]">
+ <xsl:copy>
+ <xsl:apply-templates select="@*" />
+ <h1 />
+ <div class="tof-main">
+ <xsl:apply-templates select="//xh:div[@class = 'main']//xh:div[@class = 'img-caption']" mode="tof" />
+ </div>
+ <div class="tof-back">
+ <xsl:apply-templates select="//xh:div[@class = 'back']//xh:div[@class = 'img-caption']" mode="tof" />
+ </div>
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:div[@class = 'tot' and not(node())]">
+ <xsl:copy>
+ <xsl:apply-templates select="@*" />
+ <h1 />
+ <div class="tot-main">
+ <xsl:apply-templates select="//xh:div[@class = 'main']//xh:caption" mode="tot" />
+ </div>
+ <div class="tot-back">
+ <xsl:apply-templates select="//xh:div[@class = 'back']//xh:caption" mode="tot" />
+ </div>
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:caption | xh:h1 | xh:h2 | xh:h3 | xh:div[@class = 'img-caption']">
+ <xsl:copy>
+ <xsl:attribute name="id">
+ <xsl:value-of select="generate-id()" />
+ </xsl:attribute>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:h1 | xh:h2 | xh:h3" mode="toc">
+ <div class="{concat('toc-', name())}">
+ <xsl:call-template name="toc-link" />
+ </div>
+ </xsl:template>
+
+
+
+ <xsl:template match="xh:div[@class = 'img-caption']" mode="tof">
+ <div class="tof-line">
+ <xsl:call-template name="toc-link" />
+ </div>
+ </xsl:template>
+
+
+
+ <xsl:template match="@id | @xml:id" mode="href">
+ <xsl:attribute name="href">
+ <xsl:text>#</xsl:text>
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@id | @xml:id" mode="class">
+ <xsl:attribute name="class">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <!-- Named templates. -->
+
+ <xsl:template name="toc-link">
+ <a href="{concat('#', generate-id())}">
+ <xsl:apply-templates select="@id | @xml:id" mode="href" />
+ <xsl:apply-templates select="node()" />
+ </a>
+ <span class="leader" />
+ <span class="page-ref">
+ <span class="{generate-id()}">
+ <xsl:apply-templates select="@id | @xml:id" mode="class" />
+ </span>
+ </span>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/doc/xhtml_report.css b/doc/xhtml_report.css
new file mode 100644
index 0000000..f305aa5
--- /dev/null
+++ b/doc/xhtml_report.css
@@ -0,0 +1,253 @@
+ at page
+{
+ margin-bottom: 25mm;
+ margin-top: 25mm;
+}
+
+/* A page for the back matter. */
+ at page back
+{
+ force-page-count: even;
+}
+
+/* A page for the back of the title page with copy right notice. */
+ at page copy-right
+{
+ margin-top: 35mm;
+}
+
+/* A page for the front matter. */
+ at page front
+{
+ force-page-count: even;
+ counter-reset: page 1;
+}
+
+/* A page for the main matter. */
+ at page main
+{
+ force-page-count: even;
+}
+
+/* A main page to reset the page number. */
+ at page main-first
+{
+ counter-reset: page 1;
+ force-page-count: even;
+}
+
+/* A page to force page switches without generating content. */
+ at page separator
+{
+ margin-top: 35mm;
+}
+
+/* The title page. */
+ at page title
+{
+ margin-left: 25mm;
+ margin-right: 25mm;
+ margin-top: 50mm;
+}
+
+ at page :left
+{
+ margin-left: 40mm;
+ margin-right: 40mm;
+}
+
+ at page :right
+{
+ margin-left: 40mm;
+ margin-right: 40mm;
+}
+
+ at media print
+{
+ a
+ {
+ color: black;
+ }
+
+ acronym
+ {
+ font-family: serif-small-caps;
+ text-transform: lowercase;
+ }
+
+ body
+ {
+ font-family: serif;
+ font-size: 11pt;
+ hyphenate: true;
+ line-height: 1.2;
+ }
+
+ dd
+ {
+ text-align: justify;
+ }
+
+ div.page-reset
+ {
+ page: page-reset;
+ }
+
+ div.separator
+ {
+ page: separator;
+ }
+
+ dl, pre, table
+ {
+ margin-bottom: 1.2em;
+ margin-top: 1.2em;
+ }
+
+ table table
+ {
+ margin-bottom: 0pt;
+ margin-top: 0pt;
+ }
+
+ h1, h2, h3, h4, h5, h6
+ {
+ font-weight: normal;
+ }
+
+ h1
+ {
+ font-size: 1.6em;
+ margin-bottom: 2.88em;
+ margin-top: 0pt;
+ page-break-before: right;
+ string-set: chapter contents;
+ }
+
+ h1, h2
+ {
+ font-family: serif-small-caps;
+ letter-spacing: 0.1em;
+ text-transform: lowercase;
+ }
+
+ h2
+ {
+ font-size: 1.2em;
+ margin-bottom: 1.08em;
+ margin-top: 1.08em;
+ }
+
+ h3, h4, h5, h6
+ {
+ font-family: serif-title;
+ }
+
+ h3
+ {
+ font-style: italic;
+ }
+
+ h3, h4, h5, h6
+ {
+ font-size: 1em;
+ margin-bottom: 1.2em;
+ margin-top: 1.2em;
+ }
+
+ p
+ {
+ margin-bottom: 0pt;
+ margin-top: 0pt;
+ }
+
+ p + p
+ {
+ text-indent: 1.5em;
+ }
+
+ pre
+ {
+ page-break-inside: auto;
+ }
+
+ span.footnote-body
+ {
+ display: footnote-body;
+ font-size: 0.83em;
+ }
+
+ span.footnote-body:before
+ {
+ content: counter(footnote);
+ padding-right: 1em;
+ }
+
+ span.footnote-reference
+ {
+ display: footnote-reference;
+ }
+
+ span.footnote-reference:before
+ {
+ counter-increment: footnote;
+ content: counter(footnote);
+ font-size: 0.83em;
+ vertical-align: super;
+ }
+
+ span.leader
+ {
+ display: leader;
+ }
+
+ span.page-ref > span:before
+ {
+ content: page-ref(attr(class));
+ }
+
+ span.title
+ {
+ font-family: serif-title;
+ }
+
+ sub
+ {
+ font-size: 0.72em;
+ vertical-align: -25%;
+ }
+
+ sup
+ {
+ font-size: 0.72em;
+ vertical-align: 25%;
+ }
+
+ div.toc > h1
+ {
+ string-set: chapter "Table Of Contents";
+ text-align-last: left;
+ }
+
+ div.toc > h1:before
+ {
+ content: "Table Of Contents";
+ }
+
+ div.toc-h1, div.toc-h2, div.toc-h3
+ {
+ text-align: left;
+ }
+
+ div.toc-h1
+ {
+ margin-bottom: 2em;
+ margin-top: 2em;
+ }
+
+ div.toc-h2
+ {
+ margin-bottom: 1em;
+ margin-top: 1em;
+ }
+}
diff --git a/dtd/catalog b/dtd/catalog
new file mode 100644
index 0000000..3db67bb
--- /dev/null
+++ b/dtd/catalog
@@ -0,0 +1,90 @@
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "xhtml1/xhtml1-strict.dtd"
+SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ "xhtml1/xhtml1-strict.dtd"
+
+PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "xhtml1/xhtml1-transitional.dtd"
+SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+ "xhtml1/xhtml1-transitional.dtd"
+
+PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ "xhtml1/xhtml1-frameset.dtd"
+SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
+ "xhtml1/xhtml1-frameset.dtd"
+
+PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "xhtml11/xhtml11.dtd"
+
+SYSTEM "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
+ "xhtml11/xhtml11.dtd"
+
+PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "html4/loose.dtd"
+SYSTEM "http://www.w3.org/TR/html4/loose.dtd"
+ "html4/loose.dtd"
+
+PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "html4/strict.dtd"
+SYSTEM "http://www.w3.org/TR/html4/strict.dtd"
+ "html4/strict.dtd"
+
+PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 8.1.0 EJB//EN"
+ "j2ee/weblogic810-ejb.jar.dtd"
+SYSTEM "http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd"
+ "j2ee/weblogic810-ejb.jar.dtd"
+
+PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN"
+ "j2ee/weblogic-ejb-jar.dtd"
+SYSTEM "http://www.bea.com/servers/wls600/dtd/weblogic-ejb-jar.dtd"
+ "j2ee/weblogic-ejb-jar.dtd"
+
+PUBLIC "-//BEA Systems, Inc.//DTD Web Application 6.1//EN"
+ "j2ee/weblogic-ejb-jar.dtd"
+SYSTEM "http://www.bea.com/servers/wls600/dtd/weblogic-ejb-jar.dtd"
+ "j2ee/weblogic-ejb-jar.dtd"
+
+PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 Connector//EN"
+ "j2ee/weblogic600-ra.dtd"
+SYSTEM "http://www.bea.com/servers/wls600/dtd/weblogic600-ra.dtd"
+ "j2ee/weblogic600-ra.dtd"
+
+PUBLIC "-//BEA Systems, Inc.//DTD WebLogic Application 8.1.0//EN"
+ "j2ee/weblogic-application_2_0.dtd"
+SYSTEM "http://www.bea.com/servers/wls810/dtd/weblogic-application_2_0.dtd"
+ "j2ee/weblogic-application_2_0.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
+ "j2ee/web-app_2_2.dtd"
+SYSTEM "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"
+ "j2ee/web-app_2_2.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "j2ee/web-app_2_3.dtd"
+SYSTEM "http://java.sun.com/dtd/web-app_2_3.dtd"
+ "j2ee/web-app_2_3.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"
+ "j2ee/connector_1_0.dtd"
+SYSTEM "http://java.sun.com/dtd/connector_1_0.dtd"
+ "j2ee/connector_1_0.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
+ "j2ee/web-jsptaglibrary_1_2.dtd"
+SYSTEM "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"
+ "j2ee/web-jsptaglibrary_1_2.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application Client 1.3//EN"
+ "j2ee/application-client_1_3.dtd"
+SYSTEM "http://java.sun.com/dtd/application-client_1_3.dtd"
+ "j2ee/application-client_1_3.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
+ "j2ee/application_1_3.dtd"
+SYSTEM "http://java.sun.com/dtd/application_1_3.dtd"
+ "j2ee/application_1_3.dtd"
+
+PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
+ "j2ee/ejb-jar_2_0.dtd"
+SYSTEM "http://java.sun.com/dtd/ejb-jar_2_0.dtd"
+ "j2ee/ejb-jar_2_0.dtd"
diff --git a/dtd/xhtml1/xhtml-lat1.ent b/dtd/xhtml1/xhtml-lat1.ent
new file mode 100644
index 0000000..ffee223
--- /dev/null
+++ b/dtd/xhtml1/xhtml-lat1.ent
@@ -0,0 +1,196 @@
+<!-- Portions (C) International Organization for Standardization 1986
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
+ %HTMLlat1;
+-->
+
+<!ENTITY nbsp " "> <!-- no-break space = non-breaking space,
+ U+00A0 ISOnum -->
+<!ENTITY iexcl "¡"> <!-- inverted exclamation mark, U+00A1 ISOnum -->
+<!ENTITY cent "¢"> <!-- cent sign, U+00A2 ISOnum -->
+<!ENTITY pound "£"> <!-- pound sign, U+00A3 ISOnum -->
+<!ENTITY curren "¤"> <!-- currency sign, U+00A4 ISOnum -->
+<!ENTITY yen "¥"> <!-- yen sign = yuan sign, U+00A5 ISOnum -->
+<!ENTITY brvbar "¦"> <!-- broken bar = broken vertical bar,
+ U+00A6 ISOnum -->
+<!ENTITY sect "§"> <!-- section sign, U+00A7 ISOnum -->
+<!ENTITY uml "¨"> <!-- diaeresis = spacing diaeresis,
+ U+00A8 ISOdia -->
+<!ENTITY copy "©"> <!-- copyright sign, U+00A9 ISOnum -->
+<!ENTITY ordf "ª"> <!-- feminine ordinal indicator, U+00AA ISOnum -->
+<!ENTITY laquo "«"> <!-- left-pointing double angle quotation mark
+ = left pointing guillemet, U+00AB ISOnum -->
+<!ENTITY not "¬"> <!-- not sign = angled dash,
+ U+00AC ISOnum -->
+<!ENTITY shy ""> <!-- soft hyphen = discretionary hyphen,
+ U+00AD ISOnum -->
+<!ENTITY reg "®"> <!-- registered sign = registered trade mark sign,
+ U+00AE ISOnum -->
+<!ENTITY macr "¯"> <!-- macron = spacing macron = overline
+ = APL overbar, U+00AF ISOdia -->
+<!ENTITY deg "°"> <!-- degree sign, U+00B0 ISOnum -->
+<!ENTITY plusmn "±"> <!-- plus-minus sign = plus-or-minus sign,
+ U+00B1 ISOnum -->
+<!ENTITY sup2 "²"> <!-- superscript two = superscript digit two
+ = squared, U+00B2 ISOnum -->
+<!ENTITY sup3 "³"> <!-- superscript three = superscript digit three
+ = cubed, U+00B3 ISOnum -->
+<!ENTITY acute "´"> <!-- acute accent = spacing acute,
+ U+00B4 ISOdia -->
+<!ENTITY micro "µ"> <!-- micro sign, U+00B5 ISOnum -->
+<!ENTITY para "¶"> <!-- pilcrow sign = paragraph sign,
+ U+00B6 ISOnum -->
+<!ENTITY middot "·"> <!-- middle dot = Georgian comma
+ = Greek middle dot, U+00B7 ISOnum -->
+<!ENTITY cedil "¸"> <!-- cedilla = spacing cedilla, U+00B8 ISOdia -->
+<!ENTITY sup1 "¹"> <!-- superscript one = superscript digit one,
+ U+00B9 ISOnum -->
+<!ENTITY ordm "º"> <!-- masculine ordinal indicator,
+ U+00BA ISOnum -->
+<!ENTITY raquo "»"> <!-- right-pointing double angle quotation mark
+ = right pointing guillemet, U+00BB ISOnum -->
+<!ENTITY frac14 "¼"> <!-- vulgar fraction one quarter
+ = fraction one quarter, U+00BC ISOnum -->
+<!ENTITY frac12 "½"> <!-- vulgar fraction one half
+ = fraction one half, U+00BD ISOnum -->
+<!ENTITY frac34 "¾"> <!-- vulgar fraction three quarters
+ = fraction three quarters, U+00BE ISOnum -->
+<!ENTITY iquest "¿"> <!-- inverted question mark
+ = turned question mark, U+00BF ISOnum -->
+<!ENTITY Agrave "À"> <!-- latin capital letter A with grave
+ = latin capital letter A grave,
+ U+00C0 ISOlat1 -->
+<!ENTITY Aacute "Á"> <!-- latin capital letter A with acute,
+ U+00C1 ISOlat1 -->
+<!ENTITY Acirc "Â"> <!-- latin capital letter A with circumflex,
+ U+00C2 ISOlat1 -->
+<!ENTITY Atilde "Ã"> <!-- latin capital letter A with tilde,
+ U+00C3 ISOlat1 -->
+<!ENTITY Auml "Ä"> <!-- latin capital letter A with diaeresis,
+ U+00C4 ISOlat1 -->
+<!ENTITY Aring "Å"> <!-- latin capital letter A with ring above
+ = latin capital letter A ring,
+ U+00C5 ISOlat1 -->
+<!ENTITY AElig "Æ"> <!-- latin capital letter AE
+ = latin capital ligature AE,
+ U+00C6 ISOlat1 -->
+<!ENTITY Ccedil "Ç"> <!-- latin capital letter C with cedilla,
+ U+00C7 ISOlat1 -->
+<!ENTITY Egrave "È"> <!-- latin capital letter E with grave,
+ U+00C8 ISOlat1 -->
+<!ENTITY Eacute "É"> <!-- latin capital letter E with acute,
+ U+00C9 ISOlat1 -->
+<!ENTITY Ecirc "Ê"> <!-- latin capital letter E with circumflex,
+ U+00CA ISOlat1 -->
+<!ENTITY Euml "Ë"> <!-- latin capital letter E with diaeresis,
+ U+00CB ISOlat1 -->
+<!ENTITY Igrave "Ì"> <!-- latin capital letter I with grave,
+ U+00CC ISOlat1 -->
+<!ENTITY Iacute "Í"> <!-- latin capital letter I with acute,
+ U+00CD ISOlat1 -->
+<!ENTITY Icirc "Î"> <!-- latin capital letter I with circumflex,
+ U+00CE ISOlat1 -->
+<!ENTITY Iuml "Ï"> <!-- latin capital letter I with diaeresis,
+ U+00CF ISOlat1 -->
+<!ENTITY ETH "Ð"> <!-- latin capital letter ETH, U+00D0 ISOlat1 -->
+<!ENTITY Ntilde "Ñ"> <!-- latin capital letter N with tilde,
+ U+00D1 ISOlat1 -->
+<!ENTITY Ograve "Ò"> <!-- latin capital letter O with grave,
+ U+00D2 ISOlat1 -->
+<!ENTITY Oacute "Ó"> <!-- latin capital letter O with acute,
+ U+00D3 ISOlat1 -->
+<!ENTITY Ocirc "Ô"> <!-- latin capital letter O with circumflex,
+ U+00D4 ISOlat1 -->
+<!ENTITY Otilde "Õ"> <!-- latin capital letter O with tilde,
+ U+00D5 ISOlat1 -->
+<!ENTITY Ouml "Ö"> <!-- latin capital letter O with diaeresis,
+ U+00D6 ISOlat1 -->
+<!ENTITY times "×"> <!-- multiplication sign, U+00D7 ISOnum -->
+<!ENTITY Oslash "Ø"> <!-- latin capital letter O with stroke
+ = latin capital letter O slash,
+ U+00D8 ISOlat1 -->
+<!ENTITY Ugrave "Ù"> <!-- latin capital letter U with grave,
+ U+00D9 ISOlat1 -->
+<!ENTITY Uacute "Ú"> <!-- latin capital letter U with acute,
+ U+00DA ISOlat1 -->
+<!ENTITY Ucirc "Û"> <!-- latin capital letter U with circumflex,
+ U+00DB ISOlat1 -->
+<!ENTITY Uuml "Ü"> <!-- latin capital letter U with diaeresis,
+ U+00DC ISOlat1 -->
+<!ENTITY Yacute "Ý"> <!-- latin capital letter Y with acute,
+ U+00DD ISOlat1 -->
+<!ENTITY THORN "Þ"> <!-- latin capital letter THORN,
+ U+00DE ISOlat1 -->
+<!ENTITY szlig "ß"> <!-- latin small letter sharp s = ess-zed,
+ U+00DF ISOlat1 -->
+<!ENTITY agrave "à"> <!-- latin small letter a with grave
+ = latin small letter a grave,
+ U+00E0 ISOlat1 -->
+<!ENTITY aacute "á"> <!-- latin small letter a with acute,
+ U+00E1 ISOlat1 -->
+<!ENTITY acirc "â"> <!-- latin small letter a with circumflex,
+ U+00E2 ISOlat1 -->
+<!ENTITY atilde "ã"> <!-- latin small letter a with tilde,
+ U+00E3 ISOlat1 -->
+<!ENTITY auml "ä"> <!-- latin small letter a with diaeresis,
+ U+00E4 ISOlat1 -->
+<!ENTITY aring "å"> <!-- latin small letter a with ring above
+ = latin small letter a ring,
+ U+00E5 ISOlat1 -->
+<!ENTITY aelig "æ"> <!-- latin small letter ae
+ = latin small ligature ae, U+00E6 ISOlat1 -->
+<!ENTITY ccedil "ç"> <!-- latin small letter c with cedilla,
+ U+00E7 ISOlat1 -->
+<!ENTITY egrave "è"> <!-- latin small letter e with grave,
+ U+00E8 ISOlat1 -->
+<!ENTITY eacute "é"> <!-- latin small letter e with acute,
+ U+00E9 ISOlat1 -->
+<!ENTITY ecirc "ê"> <!-- latin small letter e with circumflex,
+ U+00EA ISOlat1 -->
+<!ENTITY euml "ë"> <!-- latin small letter e with diaeresis,
+ U+00EB ISOlat1 -->
+<!ENTITY igrave "ì"> <!-- latin small letter i with grave,
+ U+00EC ISOlat1 -->
+<!ENTITY iacute "í"> <!-- latin small letter i with acute,
+ U+00ED ISOlat1 -->
+<!ENTITY icirc "î"> <!-- latin small letter i with circumflex,
+ U+00EE ISOlat1 -->
+<!ENTITY iuml "ï"> <!-- latin small letter i with diaeresis,
+ U+00EF ISOlat1 -->
+<!ENTITY eth "ð"> <!-- latin small letter eth, U+00F0 ISOlat1 -->
+<!ENTITY ntilde "ñ"> <!-- latin small letter n with tilde,
+ U+00F1 ISOlat1 -->
+<!ENTITY ograve "ò"> <!-- latin small letter o with grave,
+ U+00F2 ISOlat1 -->
+<!ENTITY oacute "ó"> <!-- latin small letter o with acute,
+ U+00F3 ISOlat1 -->
+<!ENTITY ocirc "ô"> <!-- latin small letter o with circumflex,
+ U+00F4 ISOlat1 -->
+<!ENTITY otilde "õ"> <!-- latin small letter o with tilde,
+ U+00F5 ISOlat1 -->
+<!ENTITY ouml "ö"> <!-- latin small letter o with diaeresis,
+ U+00F6 ISOlat1 -->
+<!ENTITY divide "÷"> <!-- division sign, U+00F7 ISOnum -->
+<!ENTITY oslash "ø"> <!-- latin small letter o with stroke,
+ = latin small letter o slash,
+ U+00F8 ISOlat1 -->
+<!ENTITY ugrave "ù"> <!-- latin small letter u with grave,
+ U+00F9 ISOlat1 -->
+<!ENTITY uacute "ú"> <!-- latin small letter u with acute,
+ U+00FA ISOlat1 -->
+<!ENTITY ucirc "û"> <!-- latin small letter u with circumflex,
+ U+00FB ISOlat1 -->
+<!ENTITY uuml "ü"> <!-- latin small letter u with diaeresis,
+ U+00FC ISOlat1 -->
+<!ENTITY yacute "ý"> <!-- latin small letter y with acute,
+ U+00FD ISOlat1 -->
+<!ENTITY thorn "þ"> <!-- latin small letter thorn,
+ U+00FE ISOlat1 -->
+<!ENTITY yuml "ÿ"> <!-- latin small letter y with diaeresis,
+ U+00FF ISOlat1 -->
diff --git a/dtd/xhtml1/xhtml-special.ent b/dtd/xhtml1/xhtml-special.ent
new file mode 100644
index 0000000..ca358b2
--- /dev/null
+++ b/dtd/xhtml1/xhtml-special.ent
@@ -0,0 +1,80 @@
+<!-- Special characters for XHTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent">
+ %HTMLspecial;
+-->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode names.
+-->
+
+<!-- C0 Controls and Basic Latin -->
+<!ENTITY quot """> <!-- quotation mark, U+0022 ISOnum -->
+<!ENTITY amp "&"> <!-- ampersand, U+0026 ISOnum -->
+<!ENTITY lt "<"> <!-- less-than sign, U+003C ISOnum -->
+<!ENTITY gt ">"> <!-- greater-than sign, U+003E ISOnum -->
+<!ENTITY apos "'"> <!-- apostrophe = APL quote, U+0027 ISOnum -->
+
+<!-- Latin Extended-A -->
+<!ENTITY OElig "Œ"> <!-- latin capital ligature OE,
+ U+0152 ISOlat2 -->
+<!ENTITY oelig "œ"> <!-- latin small ligature oe, U+0153 ISOlat2 -->
+<!-- ligature is a misnomer, this is a separate character in some languages -->
+<!ENTITY Scaron "Š"> <!-- latin capital letter S with caron,
+ U+0160 ISOlat2 -->
+<!ENTITY scaron "š"> <!-- latin small letter s with caron,
+ U+0161 ISOlat2 -->
+<!ENTITY Yuml "Ÿ"> <!-- latin capital letter Y with diaeresis,
+ U+0178 ISOlat2 -->
+
+<!-- Spacing Modifier Letters -->
+<!ENTITY circ "ˆ"> <!-- modifier letter circumflex accent,
+ U+02C6 ISOpub -->
+<!ENTITY tilde "˜"> <!-- small tilde, U+02DC ISOdia -->
+
+<!-- General Punctuation -->
+<!ENTITY ensp " "> <!-- en space, U+2002 ISOpub -->
+<!ENTITY emsp " "> <!-- em space, U+2003 ISOpub -->
+<!ENTITY thinsp " "> <!-- thin space, U+2009 ISOpub -->
+<!ENTITY zwnj ""> <!-- zero width non-joiner,
+ U+200C NEW RFC 2070 -->
+<!ENTITY zwj ""> <!-- zero width joiner, U+200D NEW RFC 2070 -->
+<!ENTITY lrm ""> <!-- left-to-right mark, U+200E NEW RFC 2070 -->
+<!ENTITY rlm ""> <!-- right-to-left mark, U+200F NEW RFC 2070 -->
+<!ENTITY ndash "–"> <!-- en dash, U+2013 ISOpub -->
+<!ENTITY mdash "—"> <!-- em dash, U+2014 ISOpub -->
+<!ENTITY lsquo "‘"> <!-- left single quotation mark,
+ U+2018 ISOnum -->
+<!ENTITY rsquo "’"> <!-- right single quotation mark,
+ U+2019 ISOnum -->
+<!ENTITY sbquo "‚"> <!-- single low-9 quotation mark, U+201A NEW -->
+<!ENTITY ldquo "“"> <!-- left double quotation mark,
+ U+201C ISOnum -->
+<!ENTITY rdquo "”"> <!-- right double quotation mark,
+ U+201D ISOnum -->
+<!ENTITY bdquo "„"> <!-- double low-9 quotation mark, U+201E NEW -->
+<!ENTITY dagger "†"> <!-- dagger, U+2020 ISOpub -->
+<!ENTITY Dagger "‡"> <!-- double dagger, U+2021 ISOpub -->
+<!ENTITY permil "‰"> <!-- per mille sign, U+2030 ISOtech -->
+<!ENTITY lsaquo "‹"> <!-- single left-pointing angle quotation mark,
+ U+2039 ISO proposed -->
+<!-- lsaquo is proposed but not yet ISO standardized -->
+<!ENTITY rsaquo "›"> <!-- single right-pointing angle quotation mark,
+ U+203A ISO proposed -->
+<!-- rsaquo is proposed but not yet ISO standardized -->
+
+<!-- Currency Symbols -->
+<!ENTITY euro "€"> <!-- euro sign, U+20AC NEW -->
diff --git a/dtd/xhtml1/xhtml-symbol.ent b/dtd/xhtml1/xhtml-symbol.ent
new file mode 100644
index 0000000..63c2abf
--- /dev/null
+++ b/dtd/xhtml1/xhtml-symbol.ent
@@ -0,0 +1,237 @@
+<!-- Mathematical, Greek and Symbolic characters for XHTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent">
+ %HTMLsymbol;
+-->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode names.
+-->
+
+<!-- Latin Extended-B -->
+<!ENTITY fnof "ƒ"> <!-- latin small letter f with hook = function
+ = florin, U+0192 ISOtech -->
+
+<!-- Greek -->
+<!ENTITY Alpha "Α"> <!-- greek capital letter alpha, U+0391 -->
+<!ENTITY Beta "Β"> <!-- greek capital letter beta, U+0392 -->
+<!ENTITY Gamma "Γ"> <!-- greek capital letter gamma,
+ U+0393 ISOgrk3 -->
+<!ENTITY Delta "Δ"> <!-- greek capital letter delta,
+ U+0394 ISOgrk3 -->
+<!ENTITY Epsilon "Ε"> <!-- greek capital letter epsilon, U+0395 -->
+<!ENTITY Zeta "Ζ"> <!-- greek capital letter zeta, U+0396 -->
+<!ENTITY Eta "Η"> <!-- greek capital letter eta, U+0397 -->
+<!ENTITY Theta "Θ"> <!-- greek capital letter theta,
+ U+0398 ISOgrk3 -->
+<!ENTITY Iota "Ι"> <!-- greek capital letter iota, U+0399 -->
+<!ENTITY Kappa "Κ"> <!-- greek capital letter kappa, U+039A -->
+<!ENTITY Lambda "Λ"> <!-- greek capital letter lamda,
+ U+039B ISOgrk3 -->
+<!ENTITY Mu "Μ"> <!-- greek capital letter mu, U+039C -->
+<!ENTITY Nu "Ν"> <!-- greek capital letter nu, U+039D -->
+<!ENTITY Xi "Ξ"> <!-- greek capital letter xi, U+039E ISOgrk3 -->
+<!ENTITY Omicron "Ο"> <!-- greek capital letter omicron, U+039F -->
+<!ENTITY Pi "Π"> <!-- greek capital letter pi, U+03A0 ISOgrk3 -->
+<!ENTITY Rho "Ρ"> <!-- greek capital letter rho, U+03A1 -->
+<!-- there is no Sigmaf, and no U+03A2 character either -->
+<!ENTITY Sigma "Σ"> <!-- greek capital letter sigma,
+ U+03A3 ISOgrk3 -->
+<!ENTITY Tau "Τ"> <!-- greek capital letter tau, U+03A4 -->
+<!ENTITY Upsilon "Υ"> <!-- greek capital letter upsilon,
+ U+03A5 ISOgrk3 -->
+<!ENTITY Phi "Φ"> <!-- greek capital letter phi,
+ U+03A6 ISOgrk3 -->
+<!ENTITY Chi "Χ"> <!-- greek capital letter chi, U+03A7 -->
+<!ENTITY Psi "Ψ"> <!-- greek capital letter psi,
+ U+03A8 ISOgrk3 -->
+<!ENTITY Omega "Ω"> <!-- greek capital letter omega,
+ U+03A9 ISOgrk3 -->
+
+<!ENTITY alpha "α"> <!-- greek small letter alpha,
+ U+03B1 ISOgrk3 -->
+<!ENTITY beta "β"> <!-- greek small letter beta, U+03B2 ISOgrk3 -->
+<!ENTITY gamma "γ"> <!-- greek small letter gamma,
+ U+03B3 ISOgrk3 -->
+<!ENTITY delta "δ"> <!-- greek small letter delta,
+ U+03B4 ISOgrk3 -->
+<!ENTITY epsilon "ε"> <!-- greek small letter epsilon,
+ U+03B5 ISOgrk3 -->
+<!ENTITY zeta "ζ"> <!-- greek small letter zeta, U+03B6 ISOgrk3 -->
+<!ENTITY eta "η"> <!-- greek small letter eta, U+03B7 ISOgrk3 -->
+<!ENTITY theta "θ"> <!-- greek small letter theta,
+ U+03B8 ISOgrk3 -->
+<!ENTITY iota "ι"> <!-- greek small letter iota, U+03B9 ISOgrk3 -->
+<!ENTITY kappa "κ"> <!-- greek small letter kappa,
+ U+03BA ISOgrk3 -->
+<!ENTITY lambda "λ"> <!-- greek small letter lamda,
+ U+03BB ISOgrk3 -->
+<!ENTITY mu "μ"> <!-- greek small letter mu, U+03BC ISOgrk3 -->
+<!ENTITY nu "ν"> <!-- greek small letter nu, U+03BD ISOgrk3 -->
+<!ENTITY xi "ξ"> <!-- greek small letter xi, U+03BE ISOgrk3 -->
+<!ENTITY omicron "ο"> <!-- greek small letter omicron, U+03BF NEW -->
+<!ENTITY pi "π"> <!-- greek small letter pi, U+03C0 ISOgrk3 -->
+<!ENTITY rho "ρ"> <!-- greek small letter rho, U+03C1 ISOgrk3 -->
+<!ENTITY sigmaf "ς"> <!-- greek small letter final sigma,
+ U+03C2 ISOgrk3 -->
+<!ENTITY sigma "σ"> <!-- greek small letter sigma,
+ U+03C3 ISOgrk3 -->
+<!ENTITY tau "τ"> <!-- greek small letter tau, U+03C4 ISOgrk3 -->
+<!ENTITY upsilon "υ"> <!-- greek small letter upsilon,
+ U+03C5 ISOgrk3 -->
+<!ENTITY phi "φ"> <!-- greek small letter phi, U+03C6 ISOgrk3 -->
+<!ENTITY chi "χ"> <!-- greek small letter chi, U+03C7 ISOgrk3 -->
+<!ENTITY psi "ψ"> <!-- greek small letter psi, U+03C8 ISOgrk3 -->
+<!ENTITY omega "ω"> <!-- greek small letter omega,
+ U+03C9 ISOgrk3 -->
+<!ENTITY thetasym "ϑ"> <!-- greek theta symbol,
+ U+03D1 NEW -->
+<!ENTITY upsih "ϒ"> <!-- greek upsilon with hook symbol,
+ U+03D2 NEW -->
+<!ENTITY piv "ϖ"> <!-- greek pi symbol, U+03D6 ISOgrk3 -->
+
+<!-- General Punctuation -->
+<!ENTITY bull "•"> <!-- bullet = black small circle,
+ U+2022 ISOpub -->
+<!-- bullet is NOT the same as bullet operator, U+2219 -->
+<!ENTITY hellip "…"> <!-- horizontal ellipsis = three dot leader,
+ U+2026 ISOpub -->
+<!ENTITY prime "′"> <!-- prime = minutes = feet, U+2032 ISOtech -->
+<!ENTITY Prime "″"> <!-- double prime = seconds = inches,
+ U+2033 ISOtech -->
+<!ENTITY oline "‾"> <!-- overline = spacing overscore,
+ U+203E NEW -->
+<!ENTITY frasl "⁄"> <!-- fraction slash, U+2044 NEW -->
+
+<!-- Letterlike Symbols -->
+<!ENTITY weierp "℘"> <!-- script capital P = power set
+ = Weierstrass p, U+2118 ISOamso -->
+<!ENTITY image "ℑ"> <!-- black-letter capital I = imaginary part,
+ U+2111 ISOamso -->
+<!ENTITY real "ℜ"> <!-- black-letter capital R = real part symbol,
+ U+211C ISOamso -->
+<!ENTITY trade "™"> <!-- trade mark sign, U+2122 ISOnum -->
+<!ENTITY alefsym "ℵ"> <!-- alef symbol = first transfinite cardinal,
+ U+2135 NEW -->
+<!-- alef symbol is NOT the same as hebrew letter alef,
+ U+05D0 although the same glyph could be used to depict both characters -->
+
+<!-- Arrows -->
+<!ENTITY larr "←"> <!-- leftwards arrow, U+2190 ISOnum -->
+<!ENTITY uarr "↑"> <!-- upwards arrow, U+2191 ISOnum-->
+<!ENTITY rarr "→"> <!-- rightwards arrow, U+2192 ISOnum -->
+<!ENTITY darr "↓"> <!-- downwards arrow, U+2193 ISOnum -->
+<!ENTITY harr "↔"> <!-- left right arrow, U+2194 ISOamsa -->
+<!ENTITY crarr "↵"> <!-- downwards arrow with corner leftwards
+ = carriage return, U+21B5 NEW -->
+<!ENTITY lArr "⇐"> <!-- leftwards double arrow, U+21D0 ISOtech -->
+<!-- Unicode does not say that lArr is the same as the 'is implied by' arrow
+ but also does not have any other character for that function. So lArr can
+ be used for 'is implied by' as ISOtech suggests -->
+<!ENTITY uArr "⇑"> <!-- upwards double arrow, U+21D1 ISOamsa -->
+<!ENTITY rArr "⇒"> <!-- rightwards double arrow,
+ U+21D2 ISOtech -->
+<!-- Unicode does not say this is the 'implies' character but does not have
+ another character with this function so rArr can be used for 'implies'
+ as ISOtech suggests -->
+<!ENTITY dArr "⇓"> <!-- downwards double arrow, U+21D3 ISOamsa -->
+<!ENTITY hArr "⇔"> <!-- left right double arrow,
+ U+21D4 ISOamsa -->
+
+<!-- Mathematical Operators -->
+<!ENTITY forall "∀"> <!-- for all, U+2200 ISOtech -->
+<!ENTITY part "∂"> <!-- partial differential, U+2202 ISOtech -->
+<!ENTITY exist "∃"> <!-- there exists, U+2203 ISOtech -->
+<!ENTITY empty "∅"> <!-- empty set = null set, U+2205 ISOamso -->
+<!ENTITY nabla "∇"> <!-- nabla = backward difference,
+ U+2207 ISOtech -->
+<!ENTITY isin "∈"> <!-- element of, U+2208 ISOtech -->
+<!ENTITY notin "∉"> <!-- not an element of, U+2209 ISOtech -->
+<!ENTITY ni "∋"> <!-- contains as member, U+220B ISOtech -->
+<!ENTITY prod "∏"> <!-- n-ary product = product sign,
+ U+220F ISOamsb -->
+<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+ the same glyph might be used for both -->
+<!ENTITY sum "∑"> <!-- n-ary summation, U+2211 ISOamsb -->
+<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+ though the same glyph might be used for both -->
+<!ENTITY minus "−"> <!-- minus sign, U+2212 ISOtech -->
+<!ENTITY lowast "∗"> <!-- asterisk operator, U+2217 ISOtech -->
+<!ENTITY radic "√"> <!-- square root = radical sign,
+ U+221A ISOtech -->
+<!ENTITY prop "∝"> <!-- proportional to, U+221D ISOtech -->
+<!ENTITY infin "∞"> <!-- infinity, U+221E ISOtech -->
+<!ENTITY ang "∠"> <!-- angle, U+2220 ISOamso -->
+<!ENTITY and "∧"> <!-- logical and = wedge, U+2227 ISOtech -->
+<!ENTITY or "∨"> <!-- logical or = vee, U+2228 ISOtech -->
+<!ENTITY cap "∩"> <!-- intersection = cap, U+2229 ISOtech -->
+<!ENTITY cup "∪"> <!-- union = cup, U+222A ISOtech -->
+<!ENTITY int "∫"> <!-- integral, U+222B ISOtech -->
+<!ENTITY there4 "∴"> <!-- therefore, U+2234 ISOtech -->
+<!ENTITY sim "∼"> <!-- tilde operator = varies with = similar to,
+ U+223C ISOtech -->
+<!-- tilde operator is NOT the same character as the tilde, U+007E,
+ although the same glyph might be used to represent both -->
+<!ENTITY cong "≅"> <!-- approximately equal to, U+2245 ISOtech -->
+<!ENTITY asymp "≈"> <!-- almost equal to = asymptotic to,
+ U+2248 ISOamsr -->
+<!ENTITY ne "≠"> <!-- not equal to, U+2260 ISOtech -->
+<!ENTITY equiv "≡"> <!-- identical to, U+2261 ISOtech -->
+<!ENTITY le "≤"> <!-- less-than or equal to, U+2264 ISOtech -->
+<!ENTITY ge "≥"> <!-- greater-than or equal to,
+ U+2265 ISOtech -->
+<!ENTITY sub "⊂"> <!-- subset of, U+2282 ISOtech -->
+<!ENTITY sup "⊃"> <!-- superset of, U+2283 ISOtech -->
+<!ENTITY nsub "⊄"> <!-- not a subset of, U+2284 ISOamsn -->
+<!ENTITY sube "⊆"> <!-- subset of or equal to, U+2286 ISOtech -->
+<!ENTITY supe "⊇"> <!-- superset of or equal to,
+ U+2287 ISOtech -->
+<!ENTITY oplus "⊕"> <!-- circled plus = direct sum,
+ U+2295 ISOamsb -->
+<!ENTITY otimes "⊗"> <!-- circled times = vector product,
+ U+2297 ISOamsb -->
+<!ENTITY perp "⊥"> <!-- up tack = orthogonal to = perpendicular,
+ U+22A5 ISOtech -->
+<!ENTITY sdot "⋅"> <!-- dot operator, U+22C5 ISOamsb -->
+<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+
+<!-- Miscellaneous Technical -->
+<!ENTITY lceil "⌈"> <!-- left ceiling = APL upstile,
+ U+2308 ISOamsc -->
+<!ENTITY rceil "⌉"> <!-- right ceiling, U+2309 ISOamsc -->
+<!ENTITY lfloor "⌊"> <!-- left floor = APL downstile,
+ U+230A ISOamsc -->
+<!ENTITY rfloor "⌋"> <!-- right floor, U+230B ISOamsc -->
+<!ENTITY lang "〈"> <!-- left-pointing angle bracket = bra,
+ U+2329 ISOtech -->
+<!-- lang is NOT the same character as U+003C 'less than sign'
+ or U+2039 'single left-pointing angle quotation mark' -->
+<!ENTITY rang "〉"> <!-- right-pointing angle bracket = ket,
+ U+232A ISOtech -->
+<!-- rang is NOT the same character as U+003E 'greater than sign'
+ or U+203A 'single right-pointing angle quotation mark' -->
+
+<!-- Geometric Shapes -->
+<!ENTITY loz "◊"> <!-- lozenge, U+25CA ISOpub -->
+
+<!-- Miscellaneous Symbols -->
+<!ENTITY spades "♠"> <!-- black spade suit, U+2660 ISOpub -->
+<!-- black here seems to mean filled as opposed to hollow -->
+<!ENTITY clubs "♣"> <!-- black club suit = shamrock,
+ U+2663 ISOpub -->
+<!ENTITY hearts "♥"> <!-- black heart suit = valentine,
+ U+2665 ISOpub -->
+<!ENTITY diams "♦"> <!-- black diamond suit, U+2666 ISOpub -->
diff --git a/dtd/xhtml1/xhtml1-frameset.dtd b/dtd/xhtml1/xhtml1-frameset.dtd
new file mode 100644
index 0000000..d128f2e
--- /dev/null
+++ b/dtd/xhtml1/xhtml1-frameset.dtd
@@ -0,0 +1,1235 @@
+<!--
+ Extensible HTML version 1.0 Frameset DTD
+
+ This is the same as HTML 4 Frameset except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
+
+ $Revision: 1.2 $
+ $Date: 2002/08/01 18:37:55 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % FrameTarget "NMTOKEN">
+ <!-- render in this frame -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % MultiLengths "CDATA">
+ <!-- comma-separated list of MultiLength -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!-- a color using sRGB: #RRGGBB as Hex values -->
+<!ENTITY % Color "CDATA">
+
+<!-- There are also 16 widely known color names with their sRGB values:
+
+ Black = #000000 Green = #008000
+ Silver = #C0C0C0 Lime = #00FF00
+ Gray = #808080 Olive = #808000
+ White = #FFFFFF Yellow = #FFFF00
+ Maroon = #800000 Navy = #000080
+ Red = #FF0000 Blue = #0000FF
+ Purple = #800080 Teal = #008080
+ Fuchsia= #FF00FF Aqua = #00FFFF
+-->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!-- text alignment for p, div, h1-h6. The default is
+ align="left" for ltr headings, "right" for rtl -->
+
+<!ENTITY % TextAlign "align (left|center|right|justify) #IMPLIED">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.extra
+ "object | applet | img | map | iframe">
+
+<!ENTITY % special.basic
+ "br | span | bdo">
+
+<!ENTITY % special
+ "%special.basic; | %special.extra;">
+
+<!ENTITY % fontstyle.extra "big | small | font | basefont">
+
+<!ENTITY % fontstyle.basic "tt | i | b | u
+ | s | strike ">
+
+<!ENTITY % fontstyle "%fontstyle.basic; | %fontstyle.extra;">
+
+<!ENTITY % phrase.extra "sub | sup">
+<!ENTITY % phrase.basic "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym">
+
+<!ENTITY % phrase "%phrase.basic; | %phrase.extra;">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl | menu | dir">
+<!ENTITY % blocktext "pre | hr | blockquote | address | center">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | isindex | fieldset | table">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding img, object, applet, big, small,
+ sub, sup, font, or basefont -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %special.basic; | %fontstyle.basic; | %phrase.basic; |
+ %inline.forms; | %misc.inline;)*">
+
+
+<!-- form uses %Flow; excluding form -->
+
+<!ENTITY % form.content "(#PCDATA | %block; | %inline; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form, form controls, iframe -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | br | span | bdo | object | applet | img | map |
+ %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, frameset)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object|isindex)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ id ID #IMPLIED
+ href %URI; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ language CDATA #IMPLIED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Flow;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--======================= Frames =======================================-->
+
+<!-- only one noframes element permitted per document -->
+
+<!ELEMENT frameset (frameset|frame|noframes)*>
+<!ATTLIST frameset
+ %coreattrs;
+ rows %MultiLengths; #IMPLIED
+ cols %MultiLengths; #IMPLIED
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ >
+
+<!-- reserved frame names start with "_" otherwise starts with letter -->
+
+<!-- tiled window within frameset -->
+
+<!ELEMENT frame EMPTY>
+<!ATTLIST frame
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ noresize (noresize) #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ >
+
+<!-- inline subwindow -->
+
+<!ELEMENT iframe %Flow;>
+<!ATTLIST iframe
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ align %ImgAlign; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!-- alternate content container for non frame-based rendering -->
+
+<!ELEMENT noframes (body)>
+<!ATTLIST noframes
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Flow;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ background %URI; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ text %Color; #IMPLIED
+ link %Color; #IMPLIED
+ vlink %Color; #IMPLIED
+ alink %Color; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list bullet styles -->
+
+<!ENTITY % ULStyle "(disc|square|circle)">
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ type %ULStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ >
+
+<!-- Ordered list numbering style
+
+ 1 arabic numbers 1, 2, 3, ...
+ a lower alpha a, b, c, ...
+ A upper alpha A, B, C, ...
+ i lower roman i, ii, iii, ...
+ I upper roman I, II, III, ...
+
+ The style is applied to the sequence number which by default
+ is reset to 1 for the first list item in an ordered list.
+-->
+<!ENTITY % OLStyle "CDATA">
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ type %OLStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ start %Number; #IMPLIED
+ >
+
+<!-- single column list (DEPRECATED) -->
+<!ELEMENT menu (li)+>
+<!ATTLIST menu
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- multiple column list (DEPRECATED) -->
+<!ELEMENT dir (li)+>
+<!ATTLIST dir
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- LIStyle is constrained to: "(%ULStyle;|%OLStyle;)" -->
+<!ENTITY % LIStyle "CDATA">
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ type %LIStyle; #IMPLIED
+ value %Number; #IMPLIED
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address (#PCDATA | %inline; | %misc.inline; | p)*>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ align (left|center|right) #IMPLIED
+ noshade (noshade) #IMPLIED
+ size %Pixels; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding
+ "img|object|applet|big|small|sub|sup|font|basefont" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ width %Number; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Flow;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Text alignment ===================================-->
+
+<!-- center content -->
+<!ELEMENT center %Flow;>
+<!ATTLIST center
+ %attrs;
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ clear (left|all|right|none) "none"
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!ELEMENT u %Inline;> <!-- underline -->
+<!ATTLIST u %attrs;>
+
+<!ELEMENT s %Inline;> <!-- strike-through -->
+<!ATTLIST s %attrs;>
+
+<!ELEMENT strike %Inline;> <!-- strike-through -->
+<!ATTLIST strike %attrs;>
+
+<!ELEMENT basefont EMPTY> <!-- base font size -->
+<!ATTLIST basefont
+ id ID #IMPLIED
+ size CDATA #REQUIRED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!ELEMENT font %Inline;> <!-- local change to font -->
+<!ATTLIST font
+ %coreattrs;
+ %i18n;
+ size CDATA #IMPLIED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form |%inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #REQUIRED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Java applet ==================================-->
+<!--
+ One of code or object attributes must be present.
+ Place param elements before other content.
+-->
+<!ELEMENT applet (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST applet
+ %coreattrs;
+ codebase %URI; #IMPLIED
+ archive CDATA #IMPLIED
+ code CDATA #IMPLIED
+ object CDATA #IMPLIED
+ alt %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ width %Length; #REQUIRED
+ height %Length; #REQUIRED
+ align %ImgAlign; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ name NMTOKEN #IMPLIED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--================ Forms ===============================================-->
+
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ name NMTOKEN #IMPLIED
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ align %LAlign; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form, form controls, iframe
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!-- single-line text input control (DEPRECATED) -->
+<!ELEMENT isindex EMPTY>
+<!ATTLIST isindex
+ %coreattrs;
+ %i18n;
+ prompt %Text; #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=":"
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ align %TAlign; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST caption
+ %attrs;
+ align %CAlign; #IMPLIED
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ bgcolor %Color; #IMPLIED
+ >
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Pixels; #IMPLIED
+ height %Pixels; #IMPLIED
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Pixels; #IMPLIED
+ height %Pixels; #IMPLIED
+ >
+
diff --git a/dtd/xhtml1/xhtml1-strict.dtd b/dtd/xhtml1/xhtml1-strict.dtd
new file mode 100644
index 0000000..2927b9e
--- /dev/null
+++ b/dtd/xhtml1/xhtml1-strict.dtd
@@ -0,0 +1,978 @@
+<!--
+ Extensible HTML version 1.0 Strict DTD
+
+ This is the same as HTML 4 Strict except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+
+ $Revision: 1.1 $
+ $Date: 2002/08/01 13:56:03 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.pre
+ "br | span | bdo | map">
+
+
+<!ENTITY % special
+ "%special.pre; | object | img ">
+
+<!ENTITY % fontstyle "tt | i | b | big | small ">
+
+<!ENTITY % phrase "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym | sub | sup ">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl">
+<!ENTITY % blocktext "pre | hr | blockquote | address">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | fieldset | table">
+
+<!ENTITY % Block "(%block; | form | %misc;)*">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding big, small, sup or sup -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %fontstyle; | %phrase; | %special.pre; | %misc.inline;
+ | %inline.forms;)*">
+
+<!-- form uses %Block; excluding form -->
+
+<!ENTITY % form.content "(%block; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form and form controls -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | %special; | %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, body)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ href %URI; #REQUIRED
+ id ID #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Block;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Block;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ >
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ >
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address %Inline;>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding "img|object|big|small|sub|sup" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Block;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+ Note that in this DTD there is no name attribute. That
+ is only available in the transitional and frameset DTD.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ >
+
+<!--================ Forms ===============================================-->
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form and form controls
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=':'
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ >
+
+<!ATTLIST caption
+ %attrs;
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ >
+
diff --git a/dtd/xhtml1/xhtml1-transitional.dtd b/dtd/xhtml1/xhtml1-transitional.dtd
new file mode 100644
index 0000000..628f27a
--- /dev/null
+++ b/dtd/xhtml1/xhtml1-transitional.dtd
@@ -0,0 +1,1201 @@
+<!--
+ Extensible HTML version 1.0 Transitional DTD
+
+ This is the same as HTML 4 Transitional except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+
+ $Revision: 1.2 $
+ $Date: 2002/08/01 18:37:55 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % FrameTarget "NMTOKEN">
+ <!-- render in this frame -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!-- a color using sRGB: #RRGGBB as Hex values -->
+<!ENTITY % Color "CDATA">
+
+<!-- There are also 16 widely known color names with their sRGB values:
+
+ Black = #000000 Green = #008000
+ Silver = #C0C0C0 Lime = #00FF00
+ Gray = #808080 Olive = #808000
+ White = #FFFFFF Yellow = #FFFF00
+ Maroon = #800000 Navy = #000080
+ Red = #FF0000 Blue = #0000FF
+ Purple = #800080 Teal = #008080
+ Fuchsia= #FF00FF Aqua = #00FFFF
+-->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!-- text alignment for p, div, h1-h6. The default is
+ align="left" for ltr headings, "right" for rtl -->
+
+<!ENTITY % TextAlign "align (left|center|right|justify) #IMPLIED">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.extra
+ "object | applet | img | map | iframe">
+
+<!ENTITY % special.basic
+ "br | span | bdo">
+
+<!ENTITY % special
+ "%special.basic; | %special.extra;">
+
+<!ENTITY % fontstyle.extra "big | small | font | basefont">
+
+<!ENTITY % fontstyle.basic "tt | i | b | u
+ | s | strike ">
+
+<!ENTITY % fontstyle "%fontstyle.basic; | %fontstyle.extra;">
+
+<!ENTITY % phrase.extra "sub | sup">
+<!ENTITY % phrase.basic "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym">
+
+<!ENTITY % phrase "%phrase.basic; | %phrase.extra;">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl | menu | dir">
+<!ENTITY % blocktext "pre | hr | blockquote | address | center | noframes">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | isindex |fieldset | table">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding img, object, applet, big, small,
+ font, or basefont -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %special.basic; | %fontstyle.basic; | %phrase.basic; |
+ %inline.forms; | %misc.inline;)*">
+
+<!-- form uses %Flow; excluding form -->
+
+<!ENTITY % form.content "(#PCDATA | %block; | %inline; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form, form controls, iframe -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | br | span | bdo | object | applet | img | map |
+ %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, body)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object|isindex)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ id ID #IMPLIED
+ href %URI; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ language CDATA #IMPLIED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Flow;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--======================= Frames =======================================-->
+
+<!-- inline subwindow -->
+
+<!ELEMENT iframe %Flow;>
+<!ATTLIST iframe
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ align %ImgAlign; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!-- alternate content container for non frame-based rendering -->
+
+<!ELEMENT noframes %Flow;>
+<!ATTLIST noframes
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Flow;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ background %URI; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ text %Color; #IMPLIED
+ link %Color; #IMPLIED
+ vlink %Color; #IMPLIED
+ alink %Color; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list bullet styles -->
+
+<!ENTITY % ULStyle "(disc|square|circle)">
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ type %ULStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ >
+
+<!-- Ordered list numbering style
+
+ 1 arabic numbers 1, 2, 3, ...
+ a lower alpha a, b, c, ...
+ A upper alpha A, B, C, ...
+ i lower roman i, ii, iii, ...
+ I upper roman I, II, III, ...
+
+ The style is applied to the sequence number which by default
+ is reset to 1 for the first list item in an ordered list.
+-->
+<!ENTITY % OLStyle "CDATA">
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ type %OLStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ start %Number; #IMPLIED
+ >
+
+<!-- single column list (DEPRECATED) -->
+<!ELEMENT menu (li)+>
+<!ATTLIST menu
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- multiple column list (DEPRECATED) -->
+<!ELEMENT dir (li)+>
+<!ATTLIST dir
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- LIStyle is constrained to: "(%ULStyle;|%OLStyle;)" -->
+<!ENTITY % LIStyle "CDATA">
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ type %LIStyle; #IMPLIED
+ value %Number; #IMPLIED
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address (#PCDATA | %inline; | %misc.inline; | p)*>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ align (left|center|right) #IMPLIED
+ noshade (noshade) #IMPLIED
+ size %Pixels; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding
+ "img|object|applet|big|small|sub|sup|font|basefont" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ width %Number; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Flow;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Text alignment ===================================-->
+
+<!-- center content -->
+<!ELEMENT center %Flow;>
+<!ATTLIST center
+ %attrs;
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ clear (left|all|right|none) "none"
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!ELEMENT u %Inline;> <!-- underline -->
+<!ATTLIST u %attrs;>
+
+<!ELEMENT s %Inline;> <!-- strike-through -->
+<!ATTLIST s %attrs;>
+
+<!ELEMENT strike %Inline;> <!-- strike-through -->
+<!ATTLIST strike %attrs;>
+
+<!ELEMENT basefont EMPTY> <!-- base font size -->
+<!ATTLIST basefont
+ id ID #IMPLIED
+ size CDATA #REQUIRED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!ELEMENT font %Inline;> <!-- local change to font -->
+<!ATTLIST font
+ %coreattrs;
+ %i18n;
+ size CDATA #IMPLIED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #REQUIRED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Java applet ==================================-->
+<!--
+ One of code or object attributes must be present.
+ Place param elements before other content.
+-->
+<!ELEMENT applet (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST applet
+ %coreattrs;
+ codebase %URI; #IMPLIED
+ archive CDATA #IMPLIED
+ code CDATA #IMPLIED
+ object CDATA #IMPLIED
+ alt %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ width %Length; #REQUIRED
+ height %Length; #REQUIRED
+ align %ImgAlign; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ name NMTOKEN #IMPLIED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Length; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name CDATA #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--================ Forms ===============================================-->
+
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ name NMTOKEN #IMPLIED
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ align %LAlign; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form, form controls, iframe
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!-- single-line text input control (DEPRECATED) -->
+<!ELEMENT isindex EMPTY>
+<!ATTLIST isindex
+ %coreattrs;
+ %i18n;
+ prompt %Text; #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=':'
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ align %TAlign; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST caption
+ %attrs;
+ align %CAlign; #IMPLIED
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ bgcolor %Color; #IMPLIED
+ >
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Length; #IMPLIED
+ height %Length; #IMPLIED
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Length; #IMPLIED
+ height %Length; #IMPLIED
+ >
+
diff --git a/lib_src/flute_src.zip b/lib_src/flute_src.zip
new file mode 100644
index 0000000..11df86d
Binary files /dev/null and b/lib_src/flute_src.zip differ
diff --git a/lib_src/sac_src.zip b/lib_src/sac_src.zip
new file mode 100644
index 0000000..f19d2a3
Binary files /dev/null and b/lib_src/sac_src.zip differ
diff --git a/res/LICENCE b/res/LICENCE
new file mode 100644
index 0000000..b4a2ac3
--- /dev/null
+++ b/res/LICENCE
@@ -0,0 +1,5 @@
+All rights granted.
+
+This software is free and will remain free.
+
+To use at your own responsibility.
diff --git a/res/MANIFEST_css2fop.MF b/res/MANIFEST_css2fop.MF
new file mode 100644
index 0000000..744d2cd
--- /dev/null
+++ b/res/MANIFEST_css2fop.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToFOP
+Implementation-Title: CSSToFOP
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
+Class-Path: fop.jar
diff --git a/res/MANIFEST_css2fopnew.MF b/res/MANIFEST_css2fopnew.MF
new file mode 100644
index 0000000..41dd1d5
--- /dev/null
+++ b/res/MANIFEST_css2fopnew.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToFOPNew
+Implementation-Title: CSSToFOPNew
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
+Class-Path: fop.jar
diff --git a/res/MANIFEST_css2xep.MF b/res/MANIFEST_css2xep.MF
new file mode 100644
index 0000000..b713c45
--- /dev/null
+++ b/res/MANIFEST_css2xep.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToXEP
+Implementation-Title: CSSToXEP
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
+Class-Path: saxon8.jar xep.jar
+
diff --git a/res/MANIFEST_css2xinc.MF b/res/MANIFEST_css2xinc.MF
new file mode 100644
index 0000000..c31bed6
--- /dev/null
+++ b/res/MANIFEST_css2xinc.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToXinc
+Implementation-Title: CSSToXinc
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
+Class-Path: xinc.jar commons-logging.jar
diff --git a/res/MANIFEST_css2xsl.MF b/res/MANIFEST_css2xsl.MF
new file mode 100644
index 0000000..cddba9d
--- /dev/null
+++ b/res/MANIFEST_css2xsl.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToXSLFormatter
+Implementation-Title: CSSToXSLFormatter
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
+Class-Path: XfoJavaCtl.jar
diff --git a/res/MANIFEST_css2xslfo.MF b/res/MANIFEST_css2xslfo.MF
new file mode 100644
index 0000000..bd09ed6
--- /dev/null
+++ b/res/MANIFEST_css2xslfo.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Main-Class: be.re.css.CSSToXSLFO
+Implementation-Title: CSSToXSLFO
+Implementation-Version: 1.6.2
+Implementation-Vendor: Pincette bvba
diff --git a/res/W3C_COPYRIGHT.html b/res/W3C_COPYRIGHT.html
new file mode 100644
index 0000000..2424d22
--- /dev/null
+++ b/res/W3C_COPYRIGHT.html
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>W3C IPR SOFTWARE NOTICE</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <style type='text/css'>
+ body { color: black; background: white; }
+ </style>
+</head>
+
+<body>
+<h1>W3C IPR SOFTWARE NOTICE</h1>
+
+<h3>Copyright � 2002 World Wide Web Consortium, (Massachusetts Institute of
+Technology, Institut National de Recherche en Informatique et en Automatique,
+Keio University). All Rights Reserved.</h3>
+
+<p><b>Note:</b> The original version of the W3C Software Copyright Notice and
+License could be found at <a
+href="http://www.w3.org/Consortium/Legal/copyright-software-19980720">http://www.w3.org/Consortium/Legal/copyright-software-19980720</a></p>
+
+<h3>Copyright � 1994-2000 <a href="http://www.w3.org/">World Wide Web
+Consortium</a>, (<a href="http://www.lcs.mit.edu/">Massachusetts Institute of
+Technology</a>, <a href="http://www.inria.fr/">Institut National de Recherche
+en Informatique et en Automatique</a>, <a href="http://www.keio.ac.jp/">Keio
+University</a>). All Rights Reserved. http://www.w3.org/Consortium/Legal/</h3>
+
+<p>This W3C work (including software, documents, or other related items) is
+being provided by the copyright holders under the following license. By
+obtaining, using and/or copying this work, you (the licensee) agree that you
+have read, understood, and will comply with the following terms and
+conditions:</p>
+
+<p>Permission to use, copy, and modify this software and its documentation,
+with or without modification,� for any purpose and without fee or royalty is
+hereby granted, provided that you include the following on ALL copies of the
+software and documentation or portions thereof, including modifications, that
+you make:</p>
+<ol>
+ <li>The full text of this NOTICE in a location viewable to users of the
+ redistributed or derivative work.</li>
+ <li>Any pre-existing intellectual property disclaimers, notices, or terms
+ and conditions. If none exist, a short notice of the following form
+ (hypertext is preferred, text is permitted) should be used within the body
+ of any redistributed or derivative code: "Copyright � 2002
+ <a href="http://www.w3.org/">World Wide Web Consortium</a>, (<a
+ href="http://www.lcs.mit.edu/">Massachusetts Institute of Technology</a>,
+ <a href="http://www.inria.fr/">Institut National de Recherche en
+ Informatique et en Automatique</a>, <a href="http://www.keio.ac.jp/">Keio
+ University</a>). All Rights Reserved.
+ http://www.w3.org/Consortium/Legal/"</li>
+ <li>Notice of any changes or modifications to the W3C files, including the
+ date changes were made. (We recommend you provide URIs to the location
+ from which the code is derived.)</li>
+</ol>
+
+<p>THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS
+MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
+PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
+THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.</p>
+
+<p>COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR
+DOCUMENTATION.</p>
+
+<p>The name and trademarks of copyright holders may NOT be used in advertising
+or publicity pertaining to the software without specific, written prior
+permission. Title to copyright in this software and any associated
+documentation will at all times remain with copyright holders.</p>
+</body>
+</html>
diff --git a/res/release_notes.txt b/res/release_notes.txt
new file mode 100644
index 0000000..195c801
--- /dev/null
+++ b/res/release_notes.txt
@@ -0,0 +1,351 @@
+Release Notes CSSToXSLFO 1.6.2
+23 August 2010
+
+1. Requirements
+
+You need JRE 1.5 or a higher version to run the application.
+
+If you run css2xep.jar you also have to specify another XSLT processor, because
+XEP uses Saxon 6.5.x, with which it doesn't work. You can either prepend
+another XSLT processor to the boot classpath or you can simply copy saxon8.jar
+in the XEP lib directory.
+
+2. Installation
+
+You can place the jar file where you want and launch the application as
+follows:
+
+> java -jar css2xslfo.jar options
+
+Consult the manual for details about running the application.
+
+3. Changes
+
+3.1 Changes Since Version 1.6.1
+
+3051512: Recompile for FOP 1.0
+
+Bug fixes
+
+3.2 Changes Since Version 1.6
+
+Bug fixes
+
+2571787: Bug in ProjectorFilter
+2588555: The value "transparent" is not recognized as a color
+2758546: Relative URLs are always resolved to absolute URLs
+
+3.3 Changes Since Version 1.5.2
+
+Bug fixes
+
+1722531: hard coded urls in css file are all set to lowercase
+1911693: xml:lang or lang on document element is ignored
+1984171: The XHTML catalog is no longer included
+1997883: table element with a region property causes program to die
+
+New features
+
+- Add FOP command-line parsing for fopnew (feature request 1989920). With the
+ new "-fop" option any FOP command-line option can be used. Note that the
+ complete command-line syntax is not compatible with the previous version. The
+ "-fc" has been removed, because there now is the FOP equivalent.
+- Add the "-config" command-line option to the Xinc variant.
+- Add the "-config" command-line option to the XSLFormatter variant.
+
+3.4 Changes Since Version 1.5.1
+
+Bug fixes
+
+1807366: Floating point values are locale dependent
+1811419: The text-align-last property is not treated as inherited
+1824076: Splitting shorthand properties four ways uses a wrong order
+1843554: Setting float to "none" still generates an fo:float
+
+3.5 Changes Since Version 1.5
+
+Bug fixes
+
+1802080: A named page sequence with only tables is considered empty
+
+3.6 Changes Since Version 1.4.2
+
+Bug fixes
+
+1716939: Regression because of invalid property bug (1691044)
+1722531: hard coded urls in css file are all set to lowercase
+1722541: underscores in class name
+1723310: Relative URLs in a style sheet are not resolved
+1731447: Inline properties are not applied to "graphic" and "leader"
+1733555: The width and height properties are not applied to "graphic"
+1737664: Floating point values shouldn't use exponential notation
+1767912: Using as filter without calling parse or setParent fails
+1768297: The text-align property is no longer applied to table cells
+1770690: The page-break properties are no longer applied to table-row
+1778499: The fopnew option -fc is broken
+
+New features
+
+- Support last pages, analogous to first pages (feature request 1720073).
+- Implement the "list-style-image" property (feature request 1721782).
+- Implement the background, border and padding page properties that are defined
+ in CSS3 (feature request 1724392). This deviates from that specification in
+ that the properties are applied to the region-body, because in XSL-FO they are
+ not defined at the page master level.
+
+3.7 Changes Since Version 1.4.1
+
+Bug fixes
+
+1701428: The border-width shorthand property is not split
+1715203: Regression because of font-family bug (1679537)
+
+3.8 Changes Since Version 1.4
+
+Bug fixes
+
+1679537: Font family names not quoted when they contain spaces
+1681734: Only block-container should have absolute positioning
+1684177: ID attribute on body is duplicated
+1691044: Unexpected CSS attributes come out as bogus FO attributes
+1691392: Named strings fail when contents is fragmented
+
+3.9 Changes Since Version 1.3.3
+
+Bug fixes
+
+1594025: Css2FopNew user configuration file
+1626094: Testing a style sheet URL is too slow
+
+New features
+
+- New Ant tasks (feature request 1554682).
+- New options "-config" and "-q" for css2xep.jar.
+
+3.10 Changes Since Version 1.3.2
+
+Bug fixes
+
+1496388: Rules with document element don't work when in open document
+1496389: xml:base treatment is not scoped
+1553292: BASE element and xml:base not taken into account for links
+
+New features
+
+Use version 0.92beta instead of 0.91beta for the package for the new FOP branch.
+
+3.11 Changes Since Version 1.3.1
+
+Bug fixes
+
+1359835: When display is "none" on regions, they are no longer shown
+1462265: Regions which are not specific are not inherited
+1482296: Overriding anonymous page context properties doesn't work
+1482644: Drop width conversion for % values when parent has no width
+
+3.12 Changes Since Version 1.3
+
+Bug fixes
+
+1455266: Regression with XSLTC because of prefix mapping events
+
+3.13 Changes Since Version 1.2
+
+Bug fixes
+
+1356430: list-style and its singles are not treated correctly
+1359835: When display is "none" on regions, they are no longer shown
+1361765: No default for page counter reset
+1425336: Invalid flow-name references
+
+New features
+
+- CSS3 namespaces.
+- Implement the "list-style-position" property.
+- Implement the ":first-letter" pseudo element.
+- Add the "wrapper" display type, which prevents an element from contributing
+ formatting objects, but for which property inheritance works.
+- Add DeltaXML (http://www.deltaxml.com) to the User Agent style sheet. It
+ takes care of the difference mark-up in the DeltaXML namespace. Note that the
+ style sheet uses change bars, which work only for XEP4
+ (http://www.renderx.com) at the moment.
+- Add XLink to the User Agent style sheet. This merely consists of supporting
+ the global XLink "href" attribute.
+- Relax the constraint that there should be no whitespace between a footnote
+ reference and a footnote body. Whitespace between them is now gobbled.
+- Add the possibility for a footnote body to have a ":before" pseudo element
+ with the display type "footnote-reference" instead of an explicit footnote
+ reference element. The generated contents will go in the flow as well as in
+ the footnote body.
+- Add support for xml:id.
+- Add the "foreign" display type. Elements having it end up in an
+ fo:instream-foreign-object element.
+
+3.14 Changes Since Version 1.1
+
+Bug fixes
+
+1348367: Fix table normalisation when display type is "none".
+1348374: Fix link processing.
+1348383: A CSS style sheet is not treated as case-insensitive.
+1349014: Correct manual section about running with FOP.
+
+New features
+
+- The special Unicode spaces U+2000 through U+200A are post-processed using
+ fo:leader elements (reminiscent of Jirka Kosek's DocBook style sheet).
+
+3.15 Changes Since Version 1.0
+
+Bug fixes
+
+1281230: The presence of the anonymous @page rule is assumed.
+1285991: Move fo:marker to the next allowed place in the XSL-FO tree.
+1286078: Turn off hyphenation by default for User Agent page set-up.
+1291094: A marker pseudo element shouldn't imply the other is one too.
+1315758: Don't generate empty page sequences.
+
+New features
+
+- Add the "colspan" and "rowspan" properties for table cells.
+- Add the unit "pcw" (proportional column width) for table column widths.
+- Add the "table-omit-footer-at-break" and "table-omit-header-at-break"
+ properties for tables.
+
+3.16 Changes Since Version 1.0rc2
+
+Incompatible changes
+
+- The "footnote" property no longer exists. It is replaced by the new display
+ types "footnote-reference" and "footnote-body". This provides control over
+ contents and style of both the footnote reference and body. The list style
+ type "footnote" has also been added. It generates symbols such as the
+ asterix, dagger, etc.
+- Regions have an incompatible restriction. They must now be the first children
+ of the body region element. For XHTML the latter is the body element.
+- Move "counter-reset" for the "page" counter from the static regions to the
+ @page rule.
+- Hyphenation is turned off by default instead of on. This puts it in line
+ with XSL-FO.
+
+Enhancements
+
+- Add the "precedence" property for the top and bottom regions.
+- Implement the change bar extension. This works only with XEP at the moment.
+- Add the link extension, which covers internal and external links.
+- Make it work with Saxon 8.3 and later.
+- Make it work with JDK 1.5 without having to prepend an XSLT processor in the
+ boot class path.
+- Remove the CSS rules for the XHTML elements "sup" and "sub" from the User
+ Agent style sheet. Set the "line-height-shift-adjustment" property to
+ "disregard-shifts" to compensate this.
+- Show a stack trace only in the main functions when -Dbe.re.stack is on.
+- Generalize the named pages feature. It is no longer bound to a sibling list
+ below the XHTML "body" element or the document element for another XML
+ vocabulary.
+- Enhance performance and reduce memory usage.
+- Add a Xinc package.
+- Add the XSL-FO "force-page-count" property.
+- Implement markers with the limitation that the value "auto" is not supported
+ for the the "width" property. Check out section 12.6 of the CSS2
+ specification to learn how you can have much more control over lists through
+ markers.
+- Implement the "column-span" property for multicolumn pages.
+- Introduce the display type "graphic".
+- Add the pseudo page "blank".
+
+Bug fixes
+
+- Fix the "string-set" property bug. It didn't cascade properly and it didn't
+ work when placed in the XHTML "style" attribute.
+- Fix the CSSToXSLFOFilter constructor. It didn't check the absence of the
+ user agent parameters.
+- Fix the absence of the "any" simple-page-master when there were @page rules
+ without an anonymous @page rule.
+- Fix a page rule cascade bug. The page rules weren't split before sorting them.
+- Do the inheritance of text-align and vertical-align in tables only for XHTML
+ tables and take into account the specificity of UA rules and universal
+ selectors.
+- Fix the bug where properties on pseudo elements were also applied to the
+ elements themselves.
+- Change the processing instruction xml:stylesheet into xml-stylesheet. The
+ XML namespaces specification doesn't allow colons in PI targets. The new
+ target follows the CSS2.1 convention.
+- Fix the combination of pseudo classes and pseudo elements. The specification
+ ":first-child:before" didn't work.
+- Fix the application of the "content" property. It didn't cascade, but
+ combined all matched property values.
+- Fix the specificity calculation in the case where several conditions are
+ combined.
+- Set the "font-selection-strategy" property to "character-by-character", which
+ corresponds to CSS.
+- Implement centering of blocks and tables when "margin-left" and
+ "margin-right" are set to "auto".
+- Report missing command-line options in css2fop.jar.
+- Produce a descent error message when an URL is invalid when trying to parse
+ a CSS style sheet.
+- The value "contents" for the "string-set" property also included the
+ generated content for the element it was set on.
+- Fix the bug where the "userAgentParameters" argument to the constructor can't
+ be "null".
+- Fix the treatment of the "clear" property.
+
+3.17 Changes Since Version 1.0rc1
+
+- Install an error handler on the filter chain for the validating mode,
+ otherwise there are no error messages when Xalan 2.6.0 or higher are used.
+ This is because Xalan's TraXFilter now correctly propagates error handler
+ events instead of throwing an exception.
+- Also add the default unit "px" to the "width" and "height" of the XHTML "img"
+ element if there is no specified unit.
+- Honor the "media" attribute of the XHTML "style" and "link" elements.
+- Fix a selector bug in the case unnamed element selectors are used elsewhere
+ than at the final position.
+- Fix a named pages regression when using a more recent Xalan version.
+- Implement the multicolumn extension.
+
+3.18 Changes Since Version 1.0b3
+
+- Implement positioned elements (position, top, bottom, left and right
+ properties).
+- Implement layered presentation (z-index property).
+- Use a block-container instead of a plain block when one of the following
+ properties is specified on a block-level element: clip, height, min-height,
+ min-width, overflow, width. This bug disabled those properties.
+- Add a FOP package variant with a FOP filter that removes some of the
+ non-supported XSL-FO properties.
+- Implement the additional CSS3 List Module glyphs for the list-style-type
+ property.
+
+3.19 Changes Since Version 1.0b2
+
+- Change the common -p option to accept a comma-separated list of URLs or
+ filenames instead of just one.
+- The anonymous page block elements following a named page block element are
+ now part of the same name page type of the latter.
+- Give lower precedence to the UA style sheet.
+- Fix a bug in the translation of XHTML attributes into CSS properties.
+- Fix a bug in the static region mapping onto pages.
+- Fix a bug in the load of the embedded XHTML catalog.
+- Document the footnote extension.
+- Document the orientation extension.
+- Complete the "Compliance With CSS2" section of the manual.
+
+3.20 Changes Since Version 1.0b1
+
+- Drop XSLTC because it produces wrong results.
+- Add the option "-v" to turn on XML validation explicitly. By default no
+ validation is done.
+- Replace the deprecated XEP interface with the new interface.
+
+4. Known Problems
+
+- The built-in XSLT-processors in JDK1.5.0 and JDK1.6.0 don't treat the
+ "ancestor-or-self" axis as a reverse axis. This causes the "list-style-type"
+ detection to fail for nested lists with different style types. As a
+ workaround you should prepend Saxon or Xalan 2.7.0 to the boot classpath.
+
+5. Contact Information
+
+Comments and bug reports can be sent to support at pincette.biz. Information about
+the application is available at http://www.re.be/css2xslfo/.
diff --git a/src/META-INF/services/org.w3c.css.sac.parser b/src/META-INF/services/org.w3c.css.sac.parser
new file mode 100644
index 0000000..a60a1e9
--- /dev/null
+++ b/src/META-INF/services/org.w3c.css.sac.parser
@@ -0,0 +1 @@
+org.w3c.flute.parser.Parser
diff --git a/src/be/re/css/BlockContainerFilter.java b/src/be/re/css/BlockContainerFilter.java
new file mode 100644
index 0000000..dafaa76
--- /dev/null
+++ b/src/be/re/css/BlockContainerFilter.java
@@ -0,0 +1,200 @@
+package be.re.css;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Checks properties to see if an element has to be wrapped in a
+ * fo:block-container.
+ * @author Werner Donn\u00e9
+ */
+
+class BlockContainerFilter extends XMLFilterImpl
+
+{
+
+ private final static Set containerProperties =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "bottom", "clip", "height", "left", "max-height", "max-width",
+ "min-height", "min-width", "orientation", "overflow", "position",
+ "right", "top", "width", "z-index"
+ }
+ )
+ );
+ private final static Set triggeringBlockProperties =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "clip", "height", "max-height", "max-width", "min-height",
+ "min-width", "orientation", "overflow", "width", "z-index"
+ }
+ )
+ );
+ private Stack stack = new Stack();
+
+
+
+ BlockContainerFilter()
+ {
+ }
+
+
+
+ BlockContainerFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ super.endElement(namespaceURI, localName, qName);
+
+ if (stack.pop().equals(new Boolean(true)))
+ {
+ super.endElement(Constants.XSLFO, "block", "fo:block");
+ super.
+ endElement(Constants.XSLFO, "block-container", "fo:block-container");
+ }
+ }
+
+
+
+ private static boolean
+ isContainerAttribute(Attributes atts, int index)
+ {
+ String name = atts.getLocalName(index);
+
+ return
+ Constants.CSS.equals(atts.getURI(index)) &&
+ (
+ containerProperties.contains(name) || name.startsWith("background-")
+ || name.startsWith("border-") || name.startsWith("margin-") ||
+ name.startsWith("padding-") || name.startsWith("page-break-")
+ );
+ }
+
+
+
+ private static AttributesImpl
+ selectAttributes(Attributes atts, boolean container)
+ {
+ AttributesImpl result = new AttributesImpl();
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if (container == isContainerAttribute(atts, i))
+ {
+ result.addAttribute
+ (
+ atts.getURI(i),
+ atts.getLocalName(i),
+ atts.getQName(i),
+ atts.getType(i),
+ atts.getValue(i)
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private static boolean
+ shouldWrap(Attributes atts)
+ {
+ boolean block =
+ "block".equals(atts.getValue(Constants.CSS, "display"));
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if
+ (
+ Constants.CSS.equals(atts.getURI(i)) &&
+ (
+ (
+ block &&
+ triggeringBlockProperties.contains(atts.getLocalName(i))
+ ) ||
+ (
+ "position".equals(atts.getLocalName(i)) &&
+ (
+ "absolute".equals(atts.getValue(i)) ||
+ "fixed".equals(atts.getValue(i))
+ )
+ )
+ )
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ boolean wrap = shouldWrap(atts);
+
+ if (wrap)
+ {
+ super.startElement
+ (
+ Constants.XSLFO,
+ "block-container",
+ "fo:block-container",
+ selectAttributes(atts, true)
+ );
+
+ super.startElement
+ (
+ Constants.XSLFO,
+ "block",
+ "fo:block",
+ new AttributesImpl()
+ );
+ }
+
+ super.startElement
+ (
+ namespaceURI,
+ localName,
+ qName,
+ wrap ? selectAttributes(atts, false) : atts
+ );
+
+ stack.push(new Boolean(wrap));
+ }
+
+} // BlockContainerFilter
diff --git a/src/be/re/css/CSSToFOP.java b/src/be/re/css/CSSToFOP.java
new file mode 100644
index 0000000..e31be0d
--- /dev/null
+++ b/src/be/re/css/CSSToFOP.java
@@ -0,0 +1,396 @@
+package be.re.css;
+
+import be.re.xml.sax.ProtectEventHandlerFilter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+import org.apache.avalon.framework.logger.ConsoleLogger;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.logger.NullLogger;
+import org.apache.fop.apps.Driver;
+import org.apache.fop.apps.Options;
+import org.apache.fop.messaging.MessageHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to the FOP XSL-FO formatter.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToFOP
+
+{
+
+ public static void
+ convert(URL in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in.openStream(),
+ out,
+ in,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert(in, out, null, format);
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ null,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ int format,
+ boolean quiet,
+ boolean validate
+ ) throws IOException, CSSToXSLFOException
+ {
+ try
+ {
+ XMLReader parser = be.re.xml.sax.Util.getParser(catalog, validate);
+ XMLFilter parent = new ProtectEventHandlerFilter(true, true, parser);
+
+ if (preprocessors != null)
+ {
+ parent = Util.createPreprocessorFilter(preprocessors, parent);
+ }
+
+ XMLFilter filter =
+ new CSSToXSLFOFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ parameters,
+ parent,
+ System.getProperty("be.re.css.debug") != null
+ );
+
+ InputSource source = new InputSource(in);
+
+ if (baseUrl != null)
+ {
+ source.setSystemId(baseUrl.toString());
+ }
+
+ Driver driver = new Driver();
+
+ MessageHandler.setScreenLogger
+ (
+ quiet ?
+ (Logger) new NullLogger() :
+ (Logger) new ConsoleLogger(ConsoleLogger.LEVEL_INFO)
+ );
+
+ driver.setLogger
+ (
+ quiet ?
+ (Logger) new NullLogger() :
+ (Logger) new ConsoleLogger(ConsoleLogger.LEVEL_INFO)
+ );
+
+ driver.setRenderer(format);
+
+ if (out != null)
+ {
+ driver.setOutputStream(out);
+ }
+
+ TransformerHandler handler =
+ be.re.xml.sax.Util.newSAXTransformerFactory().newTransformerHandler
+ (
+ new StreamSource
+ (
+ CSSToFOP.class.getResourceAsStream("style/fop_filter.xsl")
+ )
+ );
+
+ handler.setResult(new SAXResult(driver.getContentHandler()));
+ filter.setContentHandler(handler);
+ filter.parse(source);
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ File configFile = null;
+ int format = -1;
+ String output = null;
+ Map parameters = new HashMap();
+ URL[] preprocessors = null;
+ boolean quiet = false;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-q"))
+ {
+ quiet = true;
+ }
+ else
+ {
+ if (args[i].equals("-pdf"))
+ {
+ if (i == args.length - 1 || format != -1)
+ {
+ usage(1);
+ }
+
+ format = Driver.RENDER_PDF;
+ output = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-ps"))
+ {
+ if (i == args.length - 1 || format != -1)
+ {
+ usage(1);
+ }
+
+ format = Driver.RENDER_PS;
+ output = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-svg"))
+ {
+ if (i == args.length - 1 || format != -1)
+ {
+ usage(1);
+ }
+
+ format = Driver.RENDER_SVG;
+ output = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].equals("-fc"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ configFile = new File(args[++i]);
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (format == -1)
+ {
+ usage(1);
+ }
+
+ if (configFile != null)
+ {
+ new Options(configFile);
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ try
+ {
+ convert
+ (
+ url != null ? url.openStream() : System.in,
+ output != null ? new FileOutputStream(output) : null,
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToFOP.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ format,
+ quiet,
+ validate
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToFOP");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL ");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-config url_or_filename]: extra configuration");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-q]: quiet mode");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, uses stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.
+ println(" (-pdf filename | -ps filename | -svg filename): output file");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+} // CSSToFOP
diff --git a/src/be/re/css/CSSToFOPNew.java b/src/be/re/css/CSSToFOPNew.java
new file mode 100644
index 0000000..4f35ae4
--- /dev/null
+++ b/src/be/re/css/CSSToFOPNew.java
@@ -0,0 +1,403 @@
+package be.re.css;
+
+import be.re.xml.sax.ProtectEventHandlerFilter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.cli.CommandLineOptions;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to the new FOP XSL-FO
+ * formatter.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToFOPNew
+
+{
+
+ public static void
+ convert(URL in, OutputStream out, URL userAgentStyleSheet, String format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in.openStream(),
+ out,
+ in,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ null,
+ false,
+ null
+ );
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, String format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert(in, out, null, format);
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL userAgentStyleSheet,
+ String format
+ ) throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ null,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ null,
+ false,
+ null
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ String format,
+ File configFile,
+ boolean validate
+ ) throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ baseUrl,
+ userAgentStyleSheet,
+ catalog,
+ parameters,
+ preprocessors,
+ format,
+ configFile,
+ validate,
+ null
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ String format,
+ File configFile,
+ boolean validate,
+ FOUserAgent agent
+ ) throws IOException, CSSToXSLFOException
+ {
+ try
+ {
+ XMLReader parser = be.re.xml.sax.Util.getParser(catalog, validate);
+ XMLFilter parent = new ProtectEventHandlerFilter(true, true, parser);
+
+ if (preprocessors != null)
+ {
+ parent = Util.createPreprocessorFilter(preprocessors, parent);
+ }
+
+ XMLFilter filter =
+ new CSSToXSLFOFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ parameters,
+ parent,
+ System.getProperty("be.re.css.debug") != null
+ );
+
+ InputSource source = new InputSource(in);
+
+ if (baseUrl != null)
+ {
+ source.setSystemId(baseUrl.toString());
+ }
+
+ FopFactory factory = FopFactory.newInstance();
+
+ if (configFile != null)
+ {
+ factory.setUserConfig
+ (
+ new DefaultConfigurationBuilder().buildFromFile(configFile)
+ );
+ }
+
+ if (agent == null)
+ {
+ agent = factory.newFOUserAgent();
+ }
+
+ filter.setContentHandler
+ (
+ factory.newFop(format, agent, out).getDefaultHandler()
+ );
+
+ filter.parse(source);
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ String[] fopOptions = null;
+ Map parameters = new HashMap();
+ URL[] preprocessors = null;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-fop"))
+ {
+ fopOptions = new String[args.length - ++i];
+
+ for (int j = 0; i < args.length; ++i, ++j)
+ {
+ fopOptions[j] = args[i];
+ }
+ }
+ else
+ {
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ FOUserAgent agent = null;
+
+ FopCommandLine options = new FopCommandLine();
+
+ options.
+ parse(setDummyInputFile(fopOptions != null ? fopOptions : new String[0]));
+ agent = options.getFOUserAgent();
+
+ try
+ {
+ convert
+ (
+ url != null ? url.openStream() : System.in,
+ new FileOutputStream(options.getOutputFile()),
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToFOPNew.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ options.getOutputFormat(),
+ null,
+ validate,
+ agent
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+ }
+
+
+
+ private static String[]
+ setDummyInputFile(String[] args) throws Exception
+ {
+ String[] result = new String[args.length + 2];
+
+ System.arraycopy(args, 0, result, 0, args.length);
+ result[result.length - 2] = "-fo";
+ result[result.length - 1] =
+ be.re.io.Util.createTempFile("css2fopnew.", null).getAbsolutePath();
+
+ return result;
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToFOPNew");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, uses stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.
+ println(" -fop options: the rest of the command-line is for FOP");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+
+
+ private static class FopCommandLine extends CommandLineOptions
+
+ {
+
+ public FOUserAgent
+ getFOUserAgent()
+ {
+ return super.getFOUserAgent();
+ }
+
+
+
+ public String
+ getOutputFormat() throws FOPException
+ {
+ return super.getOutputFormat();
+ }
+
+ } // FopCommandLine
+
+} // CSSToFOPNew
diff --git a/src/be/re/css/CSSToXEP.java b/src/be/re/css/CSSToXEP.java
new file mode 100644
index 0000000..2b11433
--- /dev/null
+++ b/src/be/re/css/CSSToXEP.java
@@ -0,0 +1,415 @@
+package be.re.css;
+
+import be.re.xml.sax.ProtectEventHandlerFilter;
+import com.renderx.xep.FOTarget;
+import com.renderx.xep.FormatterImpl;
+import com.renderx.xep.lib.DefaultLogger;
+import com.renderx.xep.lib.Logger;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to the XEP XSL-FO formatter.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXEP
+
+{
+
+ public static final int PDF = 0;
+ public static final int POSTSCRIPT = 1;
+
+
+
+ public static void
+ convert(URL in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in.openStream(),
+ out,
+ in,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert(in, out, null, format);
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ null,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ int format,
+ boolean validate
+ ) throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ baseUrl,
+ userAgentStyleSheet,
+ catalog,
+ parameters,
+ preprocessors,
+ format,
+ validate,
+ false,
+ null
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ int format,
+ boolean validate,
+ boolean quiet,
+ URL configuration
+ ) throws IOException, CSSToXSLFOException
+ {
+ if (format != PDF && format != POSTSCRIPT)
+ {
+ throw
+ new IllegalArgumentException
+ (
+ "be.re.css.CSSToXEP.convert takes only be.re.css.CSSToXEP.PDF " +
+ "or be.re.css.CSSToXEP.POSTSCRIPT as format values."
+ );
+ }
+
+ try
+ {
+ XMLReader parser = be.re.xml.sax.Util.getParser(catalog, validate);
+ XMLFilter parent = new ProtectEventHandlerFilter(true, true, parser);
+
+ if (preprocessors != null)
+ {
+ parent = Util.createPreprocessorFilter(preprocessors, parent);
+ }
+
+ XMLFilter filter =
+ new CSSToXSLFOFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ parameters,
+ parent,
+ System.getProperty("be.re.css.debug") != null
+ );
+
+ InputSource source = new InputSource(in);
+
+ if (baseUrl != null)
+ {
+ source.setSystemId(baseUrl.toString());
+ }
+
+ if (configuration != null)
+ {
+ new FormatterImpl
+ (
+ new StreamSource(configuration.toString())
+ ).render
+ (
+ new SAXSource(filter, source),
+ new FOTarget(out, format == PDF ? "PDF" : "PostScript"),
+ quiet ? Logger.NULL_LOGGER : new DefaultLogger()
+ );
+ }
+ else
+ {
+ new FormatterImpl().render
+ (
+ new SAXSource(filter, source),
+ new FOTarget(out, format == PDF ? "PDF" : "PostScript"),
+ quiet ? Logger.NULL_LOGGER : new DefaultLogger()
+ );
+ }
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ URL configuration = null;
+ String pdf = null;
+ Map parameters = new HashMap();
+ String postScript = null;
+ URL[] preprocessors = null;
+ boolean quiet = false;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-pdf"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ pdf = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-ps"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ postScript = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].equals("-config"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ configuration = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-q"))
+ {
+ quiet = true;
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if
+ (
+ (
+ pdf == null &&
+ postScript == null
+ ) ||
+ (
+ pdf != null &&
+ postScript != null
+ )
+ )
+ {
+ usage(1);
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ try
+ {
+ convert
+ (
+ url != null ? url.openStream() : System.in,
+ new FileOutputStream(pdf != null ? pdf : postScript),
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXEP.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ pdf != null ? PDF : POSTSCRIPT,
+ validate,
+ quiet,
+ configuration
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToXEP");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL ");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-config url_or_filename]: extra configuration");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-q]: quiet mode");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, uses stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.println(" (-pdf filename | -ps filename): output file");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+} // CSSToXEP
diff --git a/src/be/re/css/CSSToXSLFO.java b/src/be/re/css/CSSToXSLFO.java
new file mode 100644
index 0000000..6a14d1a
--- /dev/null
+++ b/src/be/re/css/CSSToXSLFO.java
@@ -0,0 +1,307 @@
+package be.re.css;
+
+import be.re.xml.sax.ProtectEventHandlerFilter;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to XSL-FO.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXSLFO
+
+{
+
+ /**
+ * Takes in an XML document and produces an XSL-FO document.
+ */
+
+ public static void
+ convert(URL in, OutputStream out, URL userAgentStyleSheet)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in.openStream(),
+ out,
+ in,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ false,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out)
+ throws IOException, CSSToXSLFOException
+ {
+ convert(in, out, null);
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, URL userAgentStyleSheet)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ null,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ false,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map userAgentParameters,
+ URL[] preprocessors,
+ boolean validate,
+ boolean debug
+ ) throws IOException, CSSToXSLFOException
+ {
+ try
+ {
+ XMLReader parser = be.re.xml.sax.Util.getParser(catalog, validate);
+ XMLFilter parent = new ProtectEventHandlerFilter(true, true, parser);
+
+ if (preprocessors != null)
+ {
+ parent = Util.createPreprocessorFilter(preprocessors, parent);
+ }
+
+ XMLFilter filter =
+ new CSSToXSLFOFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ userAgentParameters,
+ parent,
+ debug
+ );
+
+ InputSource source = new InputSource(in);
+
+ if (baseUrl != null)
+ {
+ source.setSystemId(baseUrl.toString());
+ }
+
+ TransformerHandler handler =
+ be.re.xml.sax.Util.newSAXTransformerFactory().newTransformerHandler();
+
+ handler.setResult(new StreamResult(out));
+ filter.setContentHandler(handler);
+ filter.parse(source);
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ boolean debug = false;
+ String filename = null;
+ Map parameters = new HashMap();
+ URL[] preprocessors = null;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-fo"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ filename = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-debug"))
+ {
+ debug = true;
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ try
+ {
+ convert
+ (
+ url != null ? url.openStream() : System.in,
+ filename != null ?
+ (OutputStream) new FileOutputStream(filename) :
+ (OutputStream) System.out,
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXSLFO.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ validate,
+ debug
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToXSLFO");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL ");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-config url_or_filename]: extra configuration");
+ System.err.println(" [-debug]: debug mode");
+ System.err.println(" [-fo filename]: output file, uses stdout by default");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, use stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+} // CSSToXSLFO
diff --git a/src/be/re/css/CSSToXSLFOException.java b/src/be/re/css/CSSToXSLFOException.java
new file mode 100644
index 0000000..5e713cb
--- /dev/null
+++ b/src/be/re/css/CSSToXSLFOException.java
@@ -0,0 +1,82 @@
+package be.re.css;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+
+
+/**
+ * General exception for the conversion from CSS to XSL-FO.
+ * @author Werner Donne\u00e9
+ */
+
+public class CSSToXSLFOException extends Exception
+
+{
+
+ private Exception exception;
+ private String message;
+
+
+
+ public
+ CSSToXSLFOException(Exception e)
+ {
+ this.exception = e;
+ }
+
+
+
+ public
+ CSSToXSLFOException(String message)
+ {
+ this.message = message;
+ }
+
+
+
+ public Exception
+ getException()
+ {
+ return exception;
+ }
+
+
+
+ public String
+ getMessage()
+ {
+ return
+ message != null ?
+ message : (exception != null ? exception.getMessage() : null);
+ }
+
+
+
+ public void
+ printStackTrace(PrintStream s)
+ {
+ super.printStackTrace(s);
+
+ if (exception != null)
+ {
+ s.println("Caused by:");
+ exception.printStackTrace(s);
+ }
+ }
+
+
+
+ public void
+ printStackTrace(PrintWriter s)
+ {
+ super.printStackTrace(s);
+
+ if (exception != null)
+ {
+ s.println("Caused by:");
+ exception.printStackTrace(s);
+ }
+ }
+
+} // CSSToXSLFOException
diff --git a/src/be/re/css/CSSToXSLFOFilter.java b/src/be/re/css/CSSToXSLFOFilter.java
new file mode 100644
index 0000000..d46cdfa
--- /dev/null
+++ b/src/be/re/css/CSSToXSLFOFilter.java
@@ -0,0 +1,405 @@
+package be.re.css;
+
+import be.re.xml.sax.FilterOfFilters;
+import be.re.xml.sax.TransformerHandlerFilter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * A filter that accepts an XML document and produces an XSL-FO document.
+ * @author Werner Donne\u00e9
+ */
+
+public class CSSToXSLFOFilter extends XMLFilterImpl
+
+{
+
+ private static SAXTransformerFactory factory;
+ private XMLFilterImpl filter;
+ private PageSetupFilter pageSetupFilter;
+ private Util.PostProjectionFilter postProjectionFilter;
+ private ProjectorFilter projectorFilter;
+ private static Templates templates = loadStyleSheet();
+ private Map userAgentParameters;
+
+
+
+ public
+ CSSToXSLFOFilter() throws CSSToXSLFOException
+ {
+ this(null, null, new HashMap(), false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter(URL baseUrl) throws CSSToXSLFOException
+ {
+ this(baseUrl, null, new HashMap(), false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter(URL baseUrl, URL userAgentStyleSheet)
+ throws CSSToXSLFOException
+ {
+ this(baseUrl, userAgentStyleSheet, new HashMap(), false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter
+ (
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ Map userAgentParameters
+ ) throws CSSToXSLFOException
+ {
+ this(baseUrl, userAgentStyleSheet, userAgentParameters, false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter
+ (
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ Map userAgentParameters,
+ boolean debug
+ ) throws CSSToXSLFOException
+ {
+ this.userAgentParameters =
+ userAgentParameters != null ? userAgentParameters : new HashMap();
+
+ try
+ {
+ Context context = new Context();
+
+ projectorFilter =
+ new ProjectorFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ userAgentParameters,
+ context
+ );
+
+ postProjectionFilter =
+ Util.createPostProjectionFilter(baseUrl, userAgentParameters, debug);
+ pageSetupFilter =
+ new PageSetupFilter(context, baseUrl, userAgentParameters, debug);
+
+ filter =
+ new FilterOfFilters
+ (
+ new XMLFilter[]
+ {
+ projectorFilter,
+ new FOMarkerFilter(),
+ postProjectionFilter.getFilter(),
+ pageSetupFilter,
+ new TransformerHandlerFilter
+ (
+ be.re.xml.sax.Util.
+ newTemplatesHandler(templates, userAgentParameters, factory)
+ ),
+ new SpaceCorrectionFilter()
+ },
+ debug
+ );
+
+ super.setContentHandler(filter);
+ super.setDTDHandler(filter);
+ super.setEntityResolver(filter);
+ super.setErrorHandler(filter);
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public
+ CSSToXSLFOFilter(XMLReader parent) throws CSSToXSLFOException
+ {
+ this(null, null, new HashMap(), parent, false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter(URL baseUrl, XMLReader parent) throws CSSToXSLFOException
+ {
+ this(baseUrl, null, new HashMap(), parent, false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter(URL baseUrl, URL userAgentStyleSheet, XMLReader parent)
+ throws CSSToXSLFOException
+ {
+ this(baseUrl, userAgentStyleSheet, new HashMap(), parent, false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter
+ (
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ Map userAgentParameters,
+ XMLReader parent
+ ) throws CSSToXSLFOException
+ {
+ this(baseUrl, userAgentStyleSheet, userAgentParameters, parent, false);
+ }
+
+
+
+ public
+ CSSToXSLFOFilter
+ (
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ Map userAgentParameters,
+ XMLReader parent,
+ boolean debug
+ ) throws CSSToXSLFOException
+ {
+ this(baseUrl, userAgentStyleSheet, userAgentParameters, debug);
+ setParent(parent);
+ }
+
+
+
+ public URL
+ getBaseUrl()
+ {
+ return projectorFilter.getBaseUrl();
+ }
+
+
+
+ public ContentHandler
+ getContentHandler()
+ {
+ return filter.getContentHandler();
+ }
+
+
+
+ public DTDHandler
+ getDTDHandler()
+ {
+ return filter.getDTDHandler();
+ }
+
+
+
+ public EntityResolver
+ getEntityResolver()
+ {
+ return filter.getEntityResolver();
+ }
+
+
+
+ public ErrorHandler
+ getErrorHandler()
+ {
+ return filter.getErrorHandler();
+ }
+
+
+
+ public Map
+ getParameters()
+ {
+ return userAgentParameters;
+ }
+
+
+
+ public URL
+ getUserAgentStyleSheet()
+ {
+ return projectorFilter.getUserAgentStyleSheet();
+ }
+
+
+
+ private static Templates
+ loadStyleSheet()
+ {
+ try
+ {
+ factory = be.re.xml.sax.Util.newSAXTransformerFactory();
+
+ factory.setURIResolver
+ (
+ new URIResolver()
+ {
+ public Source
+ resolve(String href, String base)
+ {
+ try
+ {
+ return
+ new StreamSource
+ (
+ base != null && be.re.net.Util.isUrl(base) ?
+ new URL(new URL(base), href).toString() :
+ new URL
+ (
+ CSSToXSLFOFilter.class.getResource("style/css.xsl"), href
+ ).toString()
+ );
+ }
+
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ }
+ );
+
+ return
+ factory.newTemplates
+ (
+ new StreamSource
+ (
+ CSSToXSLFOFilter.class.getResource("style/css.xsl").toString()
+ )
+ );
+ }
+
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+
+ public void
+ parse(InputSource input) throws IOException, SAXException
+ {
+ if (getBaseUrl() == null && input.getSystemId() != null)
+ {
+ setBaseUrl(new URL(input.getSystemId()));
+ }
+
+ filter.parse(input);
+ }
+
+
+
+ public void
+ parse(String systemId) throws IOException, SAXException
+ {
+ if (getBaseUrl() == null && systemId != null)
+ {
+ setBaseUrl(new URL(systemId));
+ }
+
+ filter.parse(systemId);
+ }
+
+
+
+ public void
+ setBaseUrl(URL baseUrl)
+ {
+ projectorFilter.setBaseUrl(baseUrl);
+ pageSetupFilter.setBaseUrl(baseUrl);
+ postProjectionFilter.setBaseUrl(baseUrl);
+ }
+
+
+
+ public void
+ setContentHandler(ContentHandler handler)
+ {
+ filter.setContentHandler(handler);
+ }
+
+
+
+ public void
+ setDTDHandler(DTDHandler handler)
+ {
+ filter.setDTDHandler(handler);
+ }
+
+
+
+ public void
+ setEntityResolver(EntityResolver resolver)
+ {
+ filter.setEntityResolver(resolver);
+ }
+
+
+
+ public void
+ setErrorHandler(ErrorHandler handler)
+ {
+ filter.setErrorHandler(handler);
+ }
+
+
+
+ public void
+ setParameters(Map userAgentParameters)
+ {
+ this.userAgentParameters = userAgentParameters;
+ }
+
+
+
+ public void
+ setParent(XMLReader parent)
+ {
+ super.setParent(parent);
+ // Some XMLFilterImpl functions seem to use parent directly instead of
+ // getParent.
+ filter.setParent(parent);
+ parent.setContentHandler(filter);
+ }
+
+
+
+ public void
+ setUserAgentStyleSheet(URL userAgentStyleSheet)
+ {
+ projectorFilter.setUserAgentStyleSheet(userAgentStyleSheet);
+ }
+
+} // CSSToXSLFOFilter
diff --git a/src/be/re/css/CSSToXSLFormatter.java b/src/be/re/css/CSSToXSLFormatter.java
new file mode 100644
index 0000000..0fd022e
--- /dev/null
+++ b/src/be/re/css/CSSToXSLFormatter.java
@@ -0,0 +1,216 @@
+package be.re.css;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import jp.co.antenna.XfoJavaCtl.XfoObj;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to the XSLFormatter XSL-FO
+ * formatter.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXSLFormatter
+
+{
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ URL configuration = null;
+ String pdf = null;
+ Map parameters = new HashMap();
+ URL[] preprocessors = null;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-pdf"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ pdf = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].equals("-config"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ configuration = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (pdf == null)
+ {
+ usage(1);
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ File f = File.createTempFile("be.re.css.", "css2xslformatter");
+
+ f.deleteOnExit();
+
+ try
+ {
+ CSSToXSLFO.convert
+ (
+ url != null ? url.openStream() : System.in,
+ new FileOutputStream(f),
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ?
+ catalog : CSSToXSLFormatter.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ validate,
+ System.getProperty("be.re.css.debug") != null
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+
+ XfoObj o = new XfoObj();
+
+ if (configuration != null)
+ {
+ o.addOptionFileURI(configuration.toString());
+ }
+
+ o.render(new FileInputStream(f), new FileOutputStream(pdf));
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToXSLFormatter");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL ");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-config url_or_filename]: extra configuration");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, uses stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.println(" -pdf filename: output file");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+} // CSSToXSLFormatter
diff --git a/src/be/re/css/CSSToXinc.java b/src/be/re/css/CSSToXinc.java
new file mode 100644
index 0000000..f108aba
--- /dev/null
+++ b/src/be/re/css/CSSToXinc.java
@@ -0,0 +1,380 @@
+package be.re.css;
+
+import be.re.xml.sax.ProtectEventHandlerFilter;
+import com.lunasil.xf.XincEngine;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * Convenience class for the conversion from CSS to the Xinc XSL-FO formatter.
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXinc
+
+{
+
+ public static final int PDF = 0;
+
+
+
+ public static void
+ convert(URL in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in.openStream(),
+ out,
+ in,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert(in, out, null, format);
+ }
+
+
+
+ public static void
+ convert(InputStream in, OutputStream out, URL userAgentStyleSheet, int format)
+ throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ null,
+ userAgentStyleSheet,
+ null,
+ new HashMap(),
+ null,
+ format,
+ false
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ int format,
+ boolean validate
+ ) throws IOException, CSSToXSLFOException
+ {
+ convert
+ (
+ in,
+ out,
+ baseUrl,
+ userAgentStyleSheet,
+ catalog,
+ parameters,
+ preprocessors,
+ format,
+ validate,
+ null
+ );
+ }
+
+
+
+ public static void
+ convert
+ (
+ InputStream in,
+ OutputStream out,
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ URL catalog,
+ Map parameters,
+ URL[] preprocessors,
+ int format,
+ boolean validate,
+ URL configuration
+ ) throws IOException, CSSToXSLFOException
+ {
+ if (format != PDF)
+ {
+ throw
+ new IllegalArgumentException
+ (
+ "be.re.css.CSSToXinc.convert takes only be.re.css.CSSToXinc.PDF " +
+ "as a format value."
+ );
+ }
+
+ try
+ {
+ XMLReader parser = be.re.xml.sax.Util.getParser(catalog, validate);
+ XMLFilter parent = new ProtectEventHandlerFilter(true, true, parser);
+
+ if (preprocessors != null)
+ {
+ parent = Util.createPreprocessorFilter(preprocessors, parent);
+ }
+
+ XMLFilter filter =
+ new CSSToXSLFOFilter
+ (
+ baseUrl,
+ userAgentStyleSheet,
+ parameters,
+ parent,
+ System.getProperty("be.re.css.debug") != null
+ );
+
+ InputSource source = new InputSource(in);
+
+ if (baseUrl != null)
+ {
+ source.setSystemId(baseUrl.toString());
+ }
+
+ XincEngine engine = new XincEngine();
+
+ if (configuration != null)
+ {
+ try
+ {
+ engine.setConfiguration
+ (
+ be.re.xml.Util.newDocumentBuilderFactory(false).
+ newDocumentBuilder().parse(configuration.toString())
+ );
+ }
+
+ catch (IOException e)
+ {
+ throw e;
+ }
+
+ catch (Exception e)
+ {
+ throw new be.re.io.IOException(e);
+ }
+ }
+
+ engine.setOutputStream(out);
+ filter.setContentHandler(engine.createContentHandler());
+ filter.parse(source);
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSToXSLFOException(e);
+ }
+ }
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ URL baseUrl = null;
+ URL catalog = null;
+ URL configuration = null;
+ String pdf = null;
+ Map parameters = new HashMap();
+ URL[] preprocessors = null;
+ URL url = null;
+ URL userAgentStyleSheet = null;
+ boolean validate = false;
+
+ for (int i = 0; i < args.length; ++i)
+ {
+ if (args[i].equals("-h"))
+ {
+ usage(0);
+ }
+
+ if (args[i].equals("-baseurl"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ baseUrl = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-uacss"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ userAgentStyleSheet = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-pdf"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ pdf = args[++i];
+ }
+ else
+ {
+ if (args[i].equals("-c"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ catalog = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-p"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ preprocessors = Util.createUrls(args[++i]);
+ }
+ else
+ {
+ if (args[i].equals("-v"))
+ {
+ validate = true;
+ }
+ else
+ {
+ if (args[i].equals("-config"))
+ {
+ if (i == args.length - 1)
+ {
+ usage(1);
+ }
+
+ configuration = Util.createUrl(args[++i]);
+ }
+ else
+ {
+ if (args[i].indexOf('=') != -1)
+ {
+ parameters.put
+ (
+ args[i].substring(0, args[i].indexOf('=')),
+ args[i].indexOf('=') == args[i].length() - 1 ?
+ "" : args[i].substring(args[i].indexOf('=') + 1)
+ );
+ }
+ else
+ {
+ if (url != null)
+ {
+ usage(1);
+ }
+
+ url = Util.createUrl(args[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (pdf == null)
+ {
+ usage(1);
+ }
+
+ parameters.put
+ (
+ "base-url",
+ baseUrl != null ?
+ baseUrl.toString() : (url != null ? url.toString() : "")
+ );
+
+ try
+ {
+ convert
+ (
+ url != null ? url.openStream() : System.in,
+ new FileOutputStream(pdf),
+ baseUrl != null ? baseUrl : url,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXinc.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ PDF,
+ validate,
+ configuration
+ );
+ }
+
+ catch (Throwable e)
+ {
+ System.err.println(e.getMessage());
+ be.re.util.Util.printStackTrace(e);
+ }
+ }
+
+
+
+ private static void
+ usage(int code)
+ {
+ System.err.println("Usage: be.re.css.CSSToXinc");
+ System.err.println(" [-h]: show this help");
+ System.err.println(" [-baseurl url]: base URL ");
+ System.err.println(" [-c url_or_filename]: catalog for entity resolution");
+ System.err.println(" [-config url_or_filename]: extra configuration");
+ System.err.println(" [-p url_or_filename_comma_list]: preprocessors");
+ System.err.println(" [-uacss url_or_filename]: User Agent style sheet");
+ System.err.println(" [-v]: turn on validation");
+ System.err.
+ println(" [url_or_filename]: the input document, use stdin by default");
+ System.err.println(" [parameter=value ...] ");
+ System.err.println(" -pdf filename: output file");
+ System.err.println();
+ Util.printUserAgentParameters(System.err);
+ System.exit(code);
+ }
+
+} // CSSToXinc
diff --git a/src/be/re/css/CenterFilter.java b/src/be/re/css/CenterFilter.java
new file mode 100644
index 0000000..d053978
--- /dev/null
+++ b/src/be/re/css/CenterFilter.java
@@ -0,0 +1,214 @@
+package be.re.css;
+
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Detects centering (right and left margins set to "auto") and wraps tables
+ * and blocks in a three column table.
+ * @author Werner Donn\u00e9
+ */
+
+class CenterFilter extends XMLFilterImpl
+
+{
+
+ private Stack stack = new Stack();
+
+
+
+ CenterFilter()
+ {
+ }
+
+
+
+ CenterFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private void
+ column(String width) throws SAXException
+ {
+ AttributesImpl atts = displayType("table-column");
+
+ if (width != null)
+ {
+ atts.addAttribute(Constants.CSS, "width", "css:width", "CDATA", width);
+ }
+
+ super.startElement(Constants.CSS, "table-column", "css:table-column", atts);
+ super.endElement(Constants.CSS, "table-column", "css:table-column");
+ }
+
+
+
+ private static AttributesImpl
+ displayType(String type)
+ {
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute
+ (
+ Constants.CSS,
+ "display",
+ "css:display",
+ "CDATA",
+ type
+ );
+
+ return atts;
+ }
+
+
+
+ private void
+ emptyCell() throws SAXException
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "table-cell",
+ "css:table-cell",
+ displayType("table-cell")
+ );
+
+ super.endElement(Constants.CSS, "table-cell", "css:table-cell");
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ super.endElement(namespaceURI, localName, qName);
+
+ if (((Boolean) stack.pop()).booleanValue())
+ {
+ super.endElement(Constants.CSS, "table-cell", "css:table-cell");
+ emptyCell();
+ super.endElement(Constants.CSS, "table-row", "css:table-row");
+ super.endElement(Constants.CSS, "table-row-group", "css:table-row-group");
+ super.endElement(Constants.CSS, "table", "css:table");
+ }
+ }
+
+
+
+ private void
+ generateTable(Attributes atts) throws SAXException
+ {
+ AttributesImpl tableAtts = displayType("table");
+
+ Util.copyAttribute(atts, tableAtts, Constants.CSS, "margin-bottom");
+ Util.copyAttribute(atts, tableAtts, Constants.CSS, "margin-top");
+
+ tableAtts.addAttribute
+ (
+ Constants.CSS,
+ "table-layout",
+ "css:table-layout",
+ "CDATA",
+ "fixed"
+ );
+
+ super.startElement(Constants.CSS, "table", "css:table", tableAtts);
+ column("1*");
+ column(atts.getValue(Constants.CSS, "width"));
+ column("1*");
+
+ super.startElement
+ (
+ Constants.CSS,
+ "table-row-group",
+ "css:table-row-group",
+ displayType("table-row-group")
+ );
+
+ super.startElement
+ (
+ Constants.CSS,
+ "table-row",
+ "css:table-row",
+ displayType("table-row")
+ );
+
+ emptyCell();
+
+ super.startElement
+ (
+ Constants.CSS,
+ "table-cell",
+ "css:table-cell",
+ displayType("table-cell")
+ );
+ }
+
+
+
+ private static boolean
+ shouldCenter(Attributes atts)
+ {
+ return
+ "auto".equals(atts.getValue(Constants.CSS, "margin-left")) &&
+ "auto".equals(atts.getValue(Constants.CSS, "margin-right"));
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String display = atts.getValue(Constants.CSS, "display");
+ boolean extra = false;
+
+ if
+ (
+ (
+ "block".equals(display) ||
+ "table".equals(display)
+ ) &&
+ shouldCenter(atts)
+ )
+ {
+ extra = true;
+ generateTable(atts);
+ atts = new AttributesImpl(atts);
+ Util.
+ removeAttribute((AttributesImpl) atts, Constants.CSS, "margin-bottom");
+ Util.removeAttribute((AttributesImpl) atts, Constants.CSS, "margin-top");
+ Util.removeAttribute((AttributesImpl) atts, Constants.CSS, "margin-left");
+ Util.
+ removeAttribute((AttributesImpl) atts, Constants.CSS, "margin-right");
+
+ Util.setAttribute
+ (
+ (AttributesImpl) atts,
+ Constants.CSS,
+ "width",
+ "css:width",
+ "100%"
+ );
+ }
+
+ stack.push(new Boolean(extra));
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+} // CenterFilter
diff --git a/src/be/re/css/Compiled.java b/src/be/re/css/Compiled.java
new file mode 100644
index 0000000..cf1cc80
--- /dev/null
+++ b/src/be/re/css/Compiled.java
@@ -0,0 +1,621 @@
+package be.re.css;
+
+import be.re.util.DigitalTree;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.ConditionalSelector;
+import org.w3c.css.sac.DescendantSelector;
+import org.w3c.css.sac.ElementSelector;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SiblingSelector;
+
+
+
+/**
+ * Represents a CSS style sheet in compiled form.
+ * @author Werner Donn\u00e9
+ */
+
+public class Compiled
+
+{
+
+ static final String ANY_ELEMENT = "*|*".intern();
+ private static final int END_STATE = 1;
+ private static final String EPSILON = "EPSILON".intern();
+ private static final int ERROR_STATE = -1;
+ static final String SIBLING = "SIBLING".intern();
+ private static final int START_STATE = 0;
+
+ private int dfaStateCounter = 0;
+ private int nfaStateCounter = 0;
+ private NFAState[] nfa =
+ new NFAState[]{new NFAState(), new NFAState()};
+ DFAState startState = null;
+ private static final boolean trace =
+ System.getProperty("be.re.css.trace") != null;
+
+
+
+ /**
+ * Adds the rule to the NFA being built using the Thompson construction.
+ * The rule should be split, i.e. it should have exactly one property.
+ */
+
+ void
+ addRule(Rule rule)
+ {
+ NFAState[] states = constructNFA(rule.getSelector());
+
+ if (rule.getPseudoElementName() == null)
+ {
+ states[END_STATE].rules.add(rule);
+ }
+ else
+ {
+ states[END_STATE].pseudoRules.add(rule);
+ }
+
+ nfa[START_STATE].next.add(new Next(EPSILON, states[START_STATE]));
+ states[END_STATE].next.add(new Next(EPSILON, nfa[END_STATE]));
+ }
+
+
+
+ private static Map
+ collectNextSets(SortedSet set)
+ {
+ Map result = new HashMap();
+
+ for (Iterator i = set.iterator(); i.hasNext();)
+ {
+ for (Iterator j = ((NFAState) i.next()).next.iterator(); j.hasNext();)
+ {
+ Next next = (Next) j.next();
+
+ if (next.event != EPSILON)
+ {
+ SortedSet nextSet = (SortedSet) result.get(next.event);
+
+ if (nextSet == null)
+ {
+ nextSet = new TreeSet(set.comparator());
+ result.put(next.event, nextSet);
+ }
+
+ nextSet.add(next.state);
+ }
+ }
+ }
+
+ for (Iterator i = result.values().iterator(); i.hasNext();)
+ {
+ TreeSet nextSet = (TreeSet) i.next();
+ SortedSet copy = (SortedSet) nextSet.clone();
+
+ for (Iterator j = copy.iterator(); j.hasNext();)
+ {
+ epsilonMove(nextSet, (NFAState) j.next());
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private static NFAState[]
+ constructAnd(NFAState[] first, NFAState[] second)
+ {
+ first[END_STATE].next.add(new Next(EPSILON, second[START_STATE]));
+
+ return new NFAState[]{first[START_STATE], second[END_STATE]};
+ }
+
+
+
+ private NFAState[]
+ constructChild(DescendantSelector selector)
+ {
+ return
+ selector.getSimpleSelector().getSelectorType() ==
+ Selector.SAC_PSEUDO_ELEMENT_SELECTOR ?
+ constructNFA(selector.getAncestorSelector()) :
+ constructAnd
+ (
+ constructNFA(selector.getAncestorSelector()),
+ constructNFA(selector.getSimpleSelector())
+ );
+ }
+
+
+
+ private NFAState[]
+ constructConditional(ConditionalSelector selector)
+ {
+ NFAState[] first = constructNFA(selector.getSimpleSelector());
+ NFAState end = new NFAState();
+
+ first[END_STATE].condition = selector.getCondition();
+ first[END_STATE].next.add(new Next(selector.getCondition(), end));
+
+ return new NFAState[]{first[START_STATE], end};
+ }
+
+
+
+ private NFAState[]
+ constructDescendant(DescendantSelector selector)
+ {
+ return
+ constructAnd
+ (
+ constructAnd
+ (
+ constructNFA(selector.getAncestorSelector()),
+ constructKleeneClosure(ANY_ELEMENT)
+ ),
+ constructNFA(selector.getSimpleSelector())
+ );
+ }
+
+
+
+ private NFAState[]
+ constructElement(ElementSelector selector)
+ {
+ NFAState start = new NFAState();
+ NFAState end = new NFAState();
+
+ start.next.add
+ (
+ new Next
+ (
+ (
+ (
+ selector.getNamespaceURI() != null ?
+ selector.getNamespaceURI() : "*"
+ ) + "|" +
+ (
+ selector.getLocalName() != null ?
+ selector.getLocalName().intern() : "*"
+ )
+ ).intern(),
+ end
+ )
+ );
+
+ return new NFAState[]{start, end};
+ }
+
+
+
+ private NFAState[]
+ constructKleeneClosure(Object event)
+ {
+ NFAState start = new NFAState();
+ NFAState end = new NFAState();
+ NFAState from = new NFAState();
+ NFAState to = new NFAState();
+
+ from.next.add(new Next(event, to));
+ start.next.add(new Next(EPSILON, from));
+ start.next.add(new Next(EPSILON, end));
+ to.next.add(new Next(EPSILON, end));
+ end.next.add(new Next(EPSILON, from));
+
+ return new NFAState[]{start, end};
+ }
+
+
+
+ /**
+ * Applies the Thompson construction.
+ */
+
+ private NFAState[]
+ constructNFA(Selector selector)
+ {
+ switch (selector.getSelectorType())
+ {
+ case Selector.SAC_CONDITIONAL_SELECTOR:
+ return constructConditional((ConditionalSelector) selector);
+
+ case Selector.SAC_CHILD_SELECTOR:
+ return constructChild((DescendantSelector) selector);
+
+ case Selector.SAC_DESCENDANT_SELECTOR:
+ return constructDescendant((DescendantSelector) selector);
+
+ case Selector.SAC_DIRECT_ADJACENT_SELECTOR:
+ return constructSibling((SiblingSelector) selector);
+
+ case Selector.SAC_ELEMENT_NODE_SELECTOR:
+ return constructElement((ElementSelector) selector);
+
+ default:
+ return null; // Ignore non-CSS2 selector types.
+ }
+ }
+
+
+
+ private NFAState[]
+ constructSibling(SiblingSelector selector)
+ {
+ return
+ constructAnd
+ (
+ constructAnd
+ (
+ constructNFA(selector.getSelector()),
+ constructSiblingTransition()
+ ),
+ constructNFA(selector.getSiblingSelector())
+ );
+ }
+
+
+
+ private NFAState[]
+ constructSiblingTransition()
+ {
+ NFAState start = new NFAState();
+ NFAState end = new NFAState();
+
+ start.next.add(new Next(SIBLING, end));
+
+ return new NFAState[]{start, end};
+ }
+
+
+
+ void
+ dumpDFA(PrintWriter out)
+ {
+ if (trace)
+ {
+ out.println();
+ out.println("DFA START");
+ out.println();
+ dumpDFA(startState, new HashSet(), out);
+ out.println("DFA END");
+ out.println();
+ out.flush();
+ }
+ }
+
+
+
+ private static void
+ dumpDFA(DFAState state, Set seen, PrintWriter out)
+ {
+ if (seen.contains(new Integer(state.state)))
+ {
+ return;
+ }
+
+ out.println(String.valueOf(state.state) + ":");
+
+ List values = new ArrayList();
+
+ for (Iterator i = state.events.keySet().iterator(); i.hasNext();)
+ {
+ String event = (String) i.next();
+ DFAState nextState = (DFAState) state.events.get(event);
+
+ out.println(" " + event + " -> " + String.valueOf(nextState.state));
+ values.add(nextState);
+ }
+
+ for
+ (
+ Iterator i = state.candidateConditions.keySet().iterator();
+ i.hasNext();
+ )
+ {
+ Condition event = (Condition) i.next();
+ DFAState nextState = (DFAState) state.candidateConditions.get(event);
+
+ out.println
+ (
+ " " + Util.conditionText(event) + " -> " +
+ String.valueOf(nextState.state)
+ );
+
+ values.add(nextState);
+ }
+
+ dumpRules(state.rules, out);
+ dumpRules(state.pseudoRules, out);
+ out.println();
+ seen.add(new Integer(state.state));
+
+ for (Iterator i = values.iterator(); i.hasNext();)
+ {
+ dumpDFA((DFAState) i.next(), seen, out);
+ }
+ }
+
+
+
+ void
+ dumpNFA(PrintWriter out)
+ {
+ if (trace)
+ {
+ out.println();
+ out.println("NFA START");
+ out.println();
+ dumpNFA(nfa[START_STATE], new HashSet(), out);
+ out.println("NFA END");
+ out.println();
+ out.flush();
+ }
+ }
+
+
+
+ private static void
+ dumpNFA(NFAState state, Set seen, PrintWriter out)
+ {
+ if (seen.contains(new Integer(state.state)))
+ {
+ return;
+ }
+
+ out.println(String.valueOf(state.state) + ":");
+
+ for (Iterator i = state.next.iterator(); i.hasNext();)
+ {
+ Next next = (Next) i.next();
+
+ out.println
+ (
+ " " +
+ (
+ next.event instanceof Condition ?
+ Util.conditionText((Condition) next.event) : next.event.toString()
+ ) + " -> " + String.valueOf(next.state.state)
+ );
+ }
+
+ dumpRules(state.rules, out);
+ dumpRules(state.pseudoRules, out);
+ out.println();
+ seen.add(new Integer(state.state));
+
+ for (Iterator i = state.next.iterator(); i.hasNext();)
+ {
+ dumpNFA(((Next) i.next()).state, seen, out);
+ }
+ }
+
+
+
+ private static void
+ dumpRules(List rules, PrintWriter out)
+ {
+ for (Iterator i = rules.iterator(); i.hasNext();)
+ {
+ Rule rule = (Rule) i.next();
+
+ out.println
+ (
+ " " + (rule.getElementName() != null ? rule.getElementName() : "") +
+ (
+ rule.getPseudoElementName() != null ?
+ rule.getPseudoElementName() : ""
+ ) + ": " + rule.getProperty().getName() + ": " +
+ rule.getProperty().getValue()
+ );
+ }
+ }
+
+
+
+ private static void
+ epsilonMove(Set set, NFAState state)
+ {
+ for (Iterator i = state.next.iterator(); i.hasNext();)
+ {
+ Next next = (Next) i.next();
+
+ if (next.event == EPSILON && set.add(next.state))
+ {
+ epsilonMove(set, next.state);
+ }
+ }
+ }
+
+
+
+ void
+ generateDFA()
+ {
+ dumpNFA(new PrintWriter(System.out));
+ startState = generateDFA(nfa);
+ dumpDFA(new PrintWriter(System.out));
+ }
+
+
+
+ /**
+ * Applies the subset construction. Returns the start state.
+ */
+
+ private DFAState
+ generateDFA(NFAState[] nfa)
+ {
+ SortedSet set =
+ new TreeSet // Sorting of the NFA states makes the labels unique.
+ (
+ new Comparator()
+ {
+ public int
+ compare(Object o1, Object o2)
+ {
+ return ((NFAState) o1).state - ((NFAState) o2).state;
+ }
+ }
+ );
+ Map states = new HashMap();
+
+ set.add(nfa[START_STATE]);
+ epsilonMove(set, nfa[START_STATE]);
+
+ DFAState result = new DFAState();
+
+ states.put(label(set), result);
+ generateTransitions(result, set, states);
+
+ return result;
+ }
+
+
+
+ private void
+ generateTransitions(DFAState from, SortedSet set, Map states)
+ {
+ Map nextSets = collectNextSets(set);
+
+ for (Iterator i = nextSets.keySet().iterator(); i.hasNext();)
+ {
+ Object event = i.next();
+ SortedSet nextSet = (SortedSet) nextSets.get(event);
+
+ if (nextSet.size() > 0)
+ {
+ DFAState nextState;
+ String s = label(nextSet);
+ DFAState state = (DFAState) states.get(s);
+
+ if (state == null)
+ {
+ nextState = new DFAState();
+ states.put(s, nextState);
+ }
+ else
+ {
+ nextState = state;
+ }
+
+ if (event instanceof Condition)
+ {
+ from.candidateConditions.put(event, nextState);
+ }
+ else
+ {
+ from.events.put((String) event, nextState);
+ }
+
+ for (Iterator j = nextSet.iterator(); j.hasNext();)
+ {
+ NFAState next = (NFAState) j.next();
+
+ nextState.rules.addAll(next.rules);
+ nextState.pseudoRules.addAll(next.pseudoRules);
+ }
+
+ if (state == null)
+ {
+ generateTransitions(nextState, nextSet, states);
+ }
+ }
+ }
+ }
+
+
+
+ private static String
+ label(SortedSet set)
+ {
+ String result = "";
+
+ for (Iterator i = set.iterator(); i.hasNext();)
+ {
+ result += "#" + String.valueOf(((NFAState) i.next()).state);
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Contains all matching rules sorted from least to most specific.
+ */
+
+ class DFAState
+
+ {
+
+ Map candidateConditions = new HashMap();
+ DigitalTree events = new DigitalTree(trace);
+ List pseudoRules = new ArrayList();
+ List rules = new ArrayList();
+ int state;
+
+
+
+ private
+ DFAState()
+ {
+ state = dfaStateCounter++;
+ }
+
+ } // DFAState
+
+
+
+ private static class Next
+
+ {
+
+ private
+ Next(Object event, NFAState state)
+ {
+ this.event = event;
+ this.state = state;
+ }
+
+
+
+ private Object event;
+ private NFAState state;
+
+ } // Next
+
+
+
+ private class NFAState
+
+ {
+
+ private Condition condition;
+ private List next = new ArrayList();
+ private List pseudoRules = new ArrayList();
+ private List rules = new ArrayList();
+ private int state;
+
+
+
+ private
+ NFAState()
+ {
+ state = nfaStateCounter++;
+ }
+
+ } // NFAState
+
+} // Compiled
diff --git a/src/be/re/css/Constants.java b/src/be/re/css/Constants.java
new file mode 100644
index 0000000..002723e
--- /dev/null
+++ b/src/be/re/css/Constants.java
@@ -0,0 +1,15 @@
+package be.re.css;
+
+public interface Constants
+
+{
+
+ public static final String CSS = "http://www.w3.org/1998/CSS".intern();
+ public static final String SPECIF = "urn:be-re-css:specificity".intern();
+ public static final String XHTML = "http://www.w3.org/1999/xhtml".intern();
+ public static final String XML =
+ "http://www.w3.org/XML/1998/namespace".intern();
+ public static final String XSLFO =
+ "http://www.w3.org/1999/XSL/Format".intern();
+
+} // Constants
diff --git a/src/be/re/css/Context.java b/src/be/re/css/Context.java
new file mode 100644
index 0000000..5699a28
--- /dev/null
+++ b/src/be/re/css/Context.java
@@ -0,0 +1,17 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+
+class Context
+
+{
+
+ List pageRules = new ArrayList();
+ Map regions = new HashMap();
+
+} // Context
diff --git a/src/be/re/css/DisplayNonePropagator.java b/src/be/re/css/DisplayNonePropagator.java
new file mode 100644
index 0000000..a4ea1eb
--- /dev/null
+++ b/src/be/re/css/DisplayNonePropagator.java
@@ -0,0 +1,106 @@
+package be.re.css;
+
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Propagates a "none" display type down the tree, which makes it unnecessary
+ * for subsequent filters to analyse the ancestor chain. Regions can't have
+ * their display set to "none", so in that case the display property it set to
+ * "block".
+ * @author Werner Donn\u00e9
+ */
+
+class DisplayNonePropagator extends XMLFilterImpl
+
+{
+
+ private Stack stack = new Stack();
+
+
+
+ DisplayNonePropagator()
+ {
+ }
+
+
+
+ DisplayNonePropagator(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ super.endElement(namespaceURI, localName, qName);
+ stack.pop();
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ boolean displayNone =
+ (stack.isEmpty() ? false : ((Boolean) stack.peek()).booleanValue()) ||
+ (
+ "none".equals(atts.getValue(Constants.CSS, "display")) &&
+ atts.getValue(Constants.CSS, "region") == null
+ );
+
+ stack.push(new Boolean(displayNone));
+
+ if (displayNone)
+ {
+ atts = new AttributesImpl(atts);
+
+ Util.setAttribute
+ (
+ (AttributesImpl) atts,
+ Constants.CSS,
+ "display",
+ "css:display",
+ "none"
+ );
+ }
+ else
+ {
+ if
+ (
+ atts.getValue(Constants.CSS, "region") != null &&
+ "none".equals(atts.getValue(Constants.CSS, "display"))
+ )
+ {
+ atts = new AttributesImpl(atts);
+
+ Util.setAttribute
+ (
+ (AttributesImpl) atts,
+ Constants.CSS,
+ "display",
+ "css:display",
+ "block"
+ );
+ }
+ }
+
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+} // DisplayNonePropagator
diff --git a/src/be/re/css/Element.java b/src/be/re/css/Element.java
new file mode 100644
index 0000000..3eb7c6b
--- /dev/null
+++ b/src/be/re/css/Element.java
@@ -0,0 +1,79 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.AttributesImpl;
+
+
+
+class Element
+
+{
+
+ static final String BLOCK = "block".intern();
+ static final String COMPACT = "compact".intern();
+ static final String GRAPHIC = "graphic".intern();
+ static final String INLINE = "inline".intern();
+ static final String INLINE_TABLE = "inline-table".intern();
+ static final String LEADER = "leader".intern();
+ static final String LIST_ITEM = "list-item".intern();
+ static final String MARKER = "marker".intern();
+ static final String NONE = "none".intern();
+ static final String RUN_IN = "run-in".intern();
+ static final String TABLE = "table".intern();
+ static final String TABLE_CELL = "table-cell".intern();
+ static final String TABLE_CAPTION = "table-caption".intern();
+ static final String TABLE_COLUMN = "table-column".intern();
+ static final String TABLE_COLUMN_GROUP = "table-column-group".intern();
+ static final String TABLE_FOOTER_GROUP = "table-footer-group".intern();
+ static final String TABLE_HEADER_GROUP = "table-header-group".intern();
+ static final String TABLE_ROW = "table-row".intern();
+ static final String TABLE_ROW_GROUP = "table-row-group".intern();
+
+ Attributes atts;
+ List children;
+ String display; // Interned.
+ Object extra;
+ String localName;
+ String namespaceURI;
+ String qName;
+
+
+
+ Element(String namespaceURI, String localName, String qName, Attributes atts)
+ {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ this.atts = new AttributesImpl(atts); // Copy because parser reuses them.
+ this.display = atts.getValue(Constants.CSS, "display");
+
+ if (display != null)
+ {
+ display = display.intern();
+ }
+ }
+
+
+
+ void
+ addChild(Element child)
+ {
+ if (children == null)
+ {
+ children = new ArrayList(10);
+ }
+
+ children.add(child);
+ }
+
+
+
+ boolean
+ isDisplay(String knownDisplay)
+ {
+ return knownDisplay == display;
+ }
+
+} // Element
diff --git a/src/be/re/css/FOMarkerFilter.java b/src/be/re/css/FOMarkerFilter.java
new file mode 100644
index 0000000..65b3c10
--- /dev/null
+++ b/src/be/re/css/FOMarkerFilter.java
@@ -0,0 +1,164 @@
+package be.re.css;
+
+import be.re.xml.Accumulator;
+import be.re.xml.DOMToContentHandler;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import org.w3c.dom.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Moves FO-markers to the next allowed place.
+ * @author Werner Donn\u00e9
+ */
+
+class FOMarkerFilter extends XMLFilterImpl
+
+{
+
+ private static final Set allowedPlaces =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "block", "inline", "list-item", "table", "table-cell",
+ "table-footer-group", "table-header-group", "table-row-group"
+ }
+ )
+ );
+
+ private List foMarkers = new ArrayList();
+ private Stack stack = new Stack();
+
+
+
+ FOMarkerFilter()
+ {
+ }
+
+
+
+ FOMarkerFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private void
+ accumulateFOMarker
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Accumulator.preAccumulate
+ (
+ namespaceURI,
+ localName,
+ qName,
+ atts,
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(Element element, XMLFilter filter) throws SAXException
+ {
+ foMarkers.add(element);
+ }
+ }
+ );
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ stack.pop();
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+
+
+ private void
+ flushFOMarkers() throws SAXException
+ {
+ if (foMarkers.size() > 0)
+ {
+ for (Iterator i = foMarkers.iterator(); i.hasNext();)
+ {
+ Element element = (Element) i.next();
+
+ DOMToContentHandler.
+ elementToContentHandler(element, getContentHandler());
+ }
+
+ foMarkers.clear();
+ }
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String parentDisplay = stack.isEmpty() ? null : (String) stack.peek();
+
+ if
+ (
+ parentDisplay != null &&
+ Constants.CSS == namespaceURI &&
+ "fo-marker".equals(localName) &&
+ !allowedPlaces.contains(parentDisplay)
+ )
+ {
+ accumulateFOMarker(namespaceURI, localName, qName, atts);
+ }
+ else
+ {
+ super.startElement(namespaceURI, localName, qName, atts);
+
+ String display =
+ parentDisplay != null && parentDisplay.equals("none") ?
+ "none" : atts.getValue(Constants.CSS, "display");
+
+ if
+ (
+ (
+ Constants.CSS != namespaceURI ||
+ !"fo-marker".equals(localName)
+ ) &&
+ allowedPlaces.contains(display)
+ )
+ {
+ flushFOMarkers();
+ }
+
+ stack.push(display);
+ }
+ }
+
+} // FOMarkerFilter
diff --git a/src/be/re/css/FirstLetterFilter.java b/src/be/re/css/FirstLetterFilter.java
new file mode 100644
index 0000000..9dbd13b
--- /dev/null
+++ b/src/be/re/css/FirstLetterFilter.java
@@ -0,0 +1,376 @@
+package be.re.css;
+
+import be.re.xml.Accumulator;
+import be.re.xml.DOMToContentHandler;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+class FirstLetterFilter extends XMLFilterImpl
+
+{
+
+ FirstLetterFilter()
+ {
+ }
+
+
+
+ FirstLetterFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private static Element
+ getFirstLetter(Node node)
+ {
+ return
+ node == null ?
+ null :
+ (
+ Constants.CSS.equals(node.getNamespaceURI()) &&
+ "first-letter".equals(node.getLocalName()) ?
+ (Element) node :
+ getFirstLetter(node.getNextSibling())
+ );
+ }
+
+
+
+ private static Text
+ getFirstTextNode(Node node)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node instanceof Text && ((Text) node).getLength() > 0 ?
+ (Text) node :
+ (
+ node instanceof Element ?
+ (
+ "inline".equals
+ (
+ ((Element) node).getAttributeNS(Constants.CSS, "display")
+ ) ? getFirstTextNode(node.getFirstChild()) : null
+ ) :
+ (
+ node.getNextSibling() != null ?
+ getFirstTextNode(node.getNextSibling()) :
+ getFirstTextNode(node.getParentNode().getNextSibling())
+ )
+ )
+ );
+ }
+
+
+
+ private static Map
+ getOriginalProperties(Element firstLetter)
+ {
+ NamedNodeMap attributes = firstLetter.getAttributes();
+ Map result = new HashMap();
+
+ for (int i = 0; i < attributes.getLength(); ++i)
+ {
+ if (Constants.CSS.equals(attributes.item(i).getNamespaceURI()))
+ {
+ result.put
+ (
+ attributes.item(i).getLocalName(),
+ attributes.item(i).getNodeValue()
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private static boolean
+ isPunctuation(char c)
+ {
+ return
+ Character.getType(c) == Character.END_PUNCTUATION ||
+ Character.getType(c) == Character.START_PUNCTUATION ||
+ Character.getType(c) == Character.INITIAL_QUOTE_PUNCTUATION ||
+ Character.getType(c) == Character.FINAL_QUOTE_PUNCTUATION ||
+ Character.getType(c) == Character.OTHER_PUNCTUATION;
+ }
+
+
+
+ private static void
+ mergeProperties(Element firstLetter, Node text)
+ {
+ for
+ (
+ Node n = text.getParentNode();
+ n.getParentNode() != null;
+ n = n.getParentNode()
+ )
+ {
+ NamedNodeMap attributes = n.getAttributes();
+
+ for (int i = 0; i < attributes.getLength(); ++i)
+ {
+ if
+ (
+ Constants.CSS.equals(attributes.item(i).getNamespaceURI()) &&
+ Util.isInherited(attributes.item(i).getLocalName()) &&
+ firstLetter.getAttributeNS
+ (
+ Constants.CSS,
+ attributes.item(i).getLocalName()
+ ).equals("")
+ )
+ {
+ firstLetter.setAttributeNS
+ (
+ Constants.CSS,
+ "css:" + attributes.item(i).getLocalName(),
+ attributes.item(i).getNodeValue()
+ );
+ }
+ }
+ }
+ }
+
+
+
+ private static void
+ removeOriginalProperties(Element element, Map properties)
+ {
+ for (Iterator i = properties.keySet().iterator(); i.hasNext();)
+ {
+ element.removeAttributeNS(Constants.CSS, (String) i.next());
+ }
+ }
+
+
+
+ private static void
+ setOriginalProperties(Element element, Map properties)
+ {
+ for (Iterator i = properties.keySet().iterator(); i.hasNext();)
+ {
+ String localName = (String) i.next();
+
+ element.setAttributeNS
+ (
+ Constants.CSS,
+ "css:" + localName,
+ (String) properties.get(localName)
+ );
+ }
+ }
+
+
+
+ private static void
+ splitText(Element firstLetter, Text text, int offset)
+ {
+ firstLetter.appendChild
+ (
+ firstLetter.getOwnerDocument().
+ createTextNode(text.getData().substring(0, offset))
+ );
+
+ mergeProperties(firstLetter, text);
+
+ text.getParentNode().insertBefore
+ (
+ text.getOwnerDocument().createTextNode(text.getData().substring(offset)),
+ text
+ );
+
+ text.getParentNode().removeChild(text);
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ if
+ (
+ "block".equals(atts.getValue(Constants.CSS, "display")) &&
+ "1".equals(atts.getValue(Constants.CSS, "has-first-letter"))
+ )
+ {
+ Accumulator.preAccumulate
+ (
+ namespaceURI,
+ localName,
+ qName,
+ atts,
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(Element element, XMLFilter filter) throws SAXException
+ {
+ DOMToContentHandler.elementToContentHandler
+ (
+ transform(element),
+ filter.getContentHandler()
+ );
+ }
+ }
+ );
+ }
+ else
+ {
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+ }
+
+
+
+ private static Element
+ transform(Element element)
+ {
+ element.removeAttributeNS(Constants.CSS, "has-first-letter");
+
+ Element firstLetter = getFirstLetter(element.getFirstChild());
+
+ if (firstLetter == null)
+ {
+ return element;
+ }
+
+ Map originalProperties = getOriginalProperties(firstLetter);
+
+ firstLetter.setAttributeNS(Constants.CSS, "css:display", "inline");
+
+ Text text = getFirstTextNode(firstLetter.getNextSibling());
+
+ if (text == null)
+ {
+ return element;
+ }
+
+ if (isPunctuation(text.getData().charAt(0)))
+ {
+ if (text.getLength() > 1)
+ {
+ splitText(firstLetter, text, 2);
+ }
+ else
+ {
+ Text nextText =
+ getFirstTextNode
+ (
+ text.getNextSibling() != null ?
+ text.getNextSibling() : text.getParentNode().getNextSibling()
+ );
+
+ if (nextText == null)
+ {
+ splitText(firstLetter, text, 1);
+ }
+ else
+ {
+ Element second = (Element) firstLetter.cloneNode(true);
+
+ element.insertBefore(second, firstLetter.getNextSibling());
+ splitText(firstLetter, text, 1);
+ splitText(second, nextText, 1);
+ }
+ }
+ }
+ else
+ {
+ splitText(firstLetter, text, 1);
+ }
+
+ String floatValue = firstLetter.getAttributeNS(Constants.CSS, "float");
+
+ if (!"".equals(floatValue) && !"none".equalsIgnoreCase(floatValue))
+ {
+ wrapInFloat
+ (
+ firstLetter,
+ floatValue,
+ firstLetter.getAttributeNS(Constants.CSS, "clear"),
+ originalProperties
+ );
+ }
+
+ return element;
+ }
+
+
+
+ private static void
+ wrapInFloat
+ (
+ Element firstLetter,
+ String floatValue,
+ String clearValue,
+ Map originalProperties
+ )
+ {
+ Element block =
+ firstLetter.getOwnerDocument().
+ createElementNS(Constants.CSS, "css:block");
+ Element floating =
+ firstLetter.getOwnerDocument().
+ createElementNS(Constants.CSS, "css:float");
+
+ floating.appendChild(block);
+ block.setAttributeNS(Constants.CSS, "css:display", "block");
+ floating.setAttributeNS(Constants.CSS, "css:float", floatValue);
+
+ if (!"".equals(clearValue))
+ {
+ floating.setAttributeNS(Constants.CSS, "css:clear", clearValue);
+ }
+
+ Map blockProperties = new HashMap(originalProperties);
+ Map inlineProperties = new HashMap(originalProperties);
+
+ blockProperties.remove("float");
+ blockProperties.remove("clear");
+ blockProperties.remove("vertical-align");
+ inlineProperties.remove("vertical-align");
+
+ setOriginalProperties(block, blockProperties);
+ firstLetter.getParentNode().insertBefore(floating, firstLetter);
+ removeOriginalProperties(firstLetter, inlineProperties);
+
+ Element second =
+ Constants.CSS.equals(firstLetter.getNextSibling().getNamespaceURI()) &&
+ "first-letter".equals(firstLetter.getNextSibling().getLocalName()) ?
+ (Element) firstLetter.getNextSibling() : null;
+
+ block.appendChild(firstLetter);
+
+ if (second != null)
+ {
+ removeOriginalProperties(second, inlineProperties);
+ block.appendChild(second);
+ }
+ }
+
+} // FirstLetterFilter
diff --git a/src/be/re/css/FootnoteFilter.java b/src/be/re/css/FootnoteFilter.java
new file mode 100644
index 0000000..2798440
--- /dev/null
+++ b/src/be/re/css/FootnoteFilter.java
@@ -0,0 +1,223 @@
+package be.re.css;
+
+import be.re.xml.Accumulator;
+import be.re.xml.DOMToContentHandler;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Processes footnotes.
+ * @author Werner Donn\u00e9
+ */
+
+class FootnoteFilter extends XMLFilterImpl
+
+{
+
+ private Element footnoteReference = null;
+
+
+
+ FootnoteFilter()
+ {
+ }
+
+
+
+ FootnoteFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ if (footnoteReference != null)
+ {
+ if (!Util.isWhitespace(ch, start, length))
+ {
+ flushFootnoteReference();
+ super.characters(ch, start, length);
+ }
+ }
+ else
+ {
+ super.characters(ch, start, length);
+ }
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ flushFootnoteReference();
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+
+
+ private void
+ flushFootnoteReference() throws SAXException
+ {
+ if (footnoteReference != null && getContentHandler() != null)
+ {
+ footnoteReference.setAttributeNS(Constants.CSS, "css:display", "inline");
+ DOMToContentHandler.
+ elementToContentHandler(footnoteReference, getContentHandler());
+ footnoteReference = null;
+ }
+ }
+
+
+
+ private static Element
+ getBeforePseudoElement(Node node)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node instanceof Element &&
+ Constants.CSS.equals(node.getNamespaceURI()) &&
+ "before".equals(node.getLocalName()) &&
+ "footnote-reference".equals
+ (
+ ((Element) node).getAttributeNS(Constants.CSS, "display")
+ ) ?
+ (Element) node :
+ getBeforePseudoElement(node.getNextSibling())
+ );
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String display = atts.getValue(Constants.CSS, "display");
+
+ if ("footnote-reference".equals(display))
+ {
+ flushFootnoteReference();
+
+ Accumulator.preAccumulate
+ (
+ namespaceURI,
+ localName,
+ qName,
+ atts,
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(Element element, XMLFilter filter) throws SAXException
+ {
+ footnoteReference = element;
+ }
+ }
+ );
+ }
+ else
+ {
+ if ("footnote-body".equals(display))
+ {
+ Accumulator.preAccumulate
+ (
+ namespaceURI,
+ localName,
+ qName,
+ atts,
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(Element element, XMLFilter filter) throws SAXException
+ {
+ transform(element);
+ }
+ }
+ );
+ }
+ else
+ {
+ flushFootnoteReference();
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+ }
+ }
+
+
+
+ private void
+ transform(Element element) throws SAXException
+ {
+ Element before = getBeforePseudoElement(element.getFirstChild());
+
+ if (footnoteReference == null && before == null)
+ {
+ return;
+ }
+
+ if (footnoteReference == null)
+ {
+ footnoteReference = before;
+ }
+
+ super.startElement
+ (
+ Constants.CSS,
+ "footnote",
+ "css:footnote",
+ new AttributesImpl()
+ );
+
+ super.startElement
+ (
+ Constants.CSS,
+ "footnote-reference",
+ "css:footnote-reference",
+ new AttributesImpl()
+ );
+
+ flushFootnoteReference();
+
+ super.endElement
+ (
+ Constants.CSS,
+ "footnote-reference",
+ "css:footnote-reference"
+ );
+
+ super.startElement
+ (
+ Constants.CSS,
+ "footnote-body",
+ "css:footnote-body",
+ new AttributesImpl()
+ );
+
+ element.setAttributeNS(Constants.CSS, "css:display", "block");
+ DOMToContentHandler.elementToContentHandler(element, getContentHandler());
+ super.endElement(Constants.CSS, "footnote-body", "css:footnote-body");
+ super.endElement(Constants.CSS, "footnote", "css:footnote");
+ }
+
+} // FootnoteFilter
diff --git a/src/be/re/css/ForeignFilter.java b/src/be/re/css/ForeignFilter.java
new file mode 100644
index 0000000..6797b34
--- /dev/null
+++ b/src/be/re/css/ForeignFilter.java
@@ -0,0 +1,129 @@
+package be.re.css;
+
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Places elements with the display type "foreign" in an
+ * fo:instream-foreign-object element and removes attributes in the CSS
+ * namespace below it.
+ * @author Werner Donn\u00e9
+ */
+
+class ForeignFilter extends XMLFilterImpl
+
+{
+
+ private Stack stack = new Stack();
+
+
+
+ ForeignFilter()
+ {
+ }
+
+
+
+ ForeignFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ boolean foreign = ((Boolean) stack.pop()).booleanValue();
+ boolean foreignParent =
+ !stack.isEmpty() && ((Boolean) stack.peek()).booleanValue();
+
+ super.endElement(namespaceURI, localName, qName);
+
+ if (!foreignParent && foreign)
+ {
+ super.endElement
+ (
+ Constants.XSLFO,
+ "instream-foreign-object",
+ "fo:instream-foreign-object"
+ );
+ }
+ }
+
+
+
+ private static Attributes
+ removeCSS(Attributes atts)
+ {
+ AttributesImpl result = new AttributesImpl();
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if
+ (
+ !Constants.CSS.equals(atts.getURI(i)) &&
+ !Constants.SPECIF.equals(atts.getURI(i))
+ )
+ {
+ result.addAttribute
+ (
+ atts.getURI(i),
+ atts.getLocalName(i),
+ atts.getQName(i),
+ atts.getType(i),
+ atts.getValue(i)
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ boolean foreignParent =
+ !stack.isEmpty() && ((Boolean) stack.peek()).booleanValue();
+ boolean foreign =
+ foreignParent ||
+ "foreign".equals(atts.getValue(Constants.CSS, "display"));
+
+ if (!foreignParent && foreign)
+ {
+ super.startElement
+ (
+ Constants.XSLFO,
+ "instream-foreign-object",
+ "fo:instream-foreign-object",
+ new AttributesImpl()
+ );
+ }
+
+ stack.push(new Boolean(foreign));
+
+ super.startElement
+ (
+ namespaceURI,
+ localName,
+ qName,
+ foreign ? removeCSS(atts) : atts
+ );
+ }
+
+} // ForeignFilter
diff --git a/src/be/re/css/InternedElementSelector.java b/src/be/re/css/InternedElementSelector.java
new file mode 100644
index 0000000..0b2fe70
--- /dev/null
+++ b/src/be/re/css/InternedElementSelector.java
@@ -0,0 +1,52 @@
+package be.re.css;
+
+import org.w3c.css.sac.ElementSelector;
+
+
+
+public class InternedElementSelector implements ElementSelector
+
+{
+
+ private String localName;
+ private String namespaceURI;
+ private short selectorType;
+
+
+
+ public
+ InternedElementSelector(ElementSelector selector)
+ {
+ localName =
+ selector.getLocalName() == null ? null : selector.getLocalName().intern();
+ namespaceURI =
+ selector.getNamespaceURI() == null ?
+ null : selector.getNamespaceURI().intern();
+ selectorType = selector.getSelectorType();
+ }
+
+
+
+ public String
+ getLocalName()
+ {
+ return localName;
+ }
+
+
+
+ public String
+ getNamespaceURI()
+ {
+ return namespaceURI;
+ }
+
+
+
+ public short
+ getSelectorType()
+ {
+ return selectorType;
+ }
+
+} // InternedElementSelector
diff --git a/src/be/re/css/InvalidPropertyFilter.java b/src/be/re/css/InvalidPropertyFilter.java
new file mode 100644
index 0000000..6b7f384
--- /dev/null
+++ b/src/be/re/css/InvalidPropertyFilter.java
@@ -0,0 +1,374 @@
+package be.re.css;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Removes invalid properties after projection, where shorthand properties have
+ * already been split.
+ * @author Werner Donn\u00e9
+ */
+
+class InvalidPropertyFilter extends XMLFilterImpl
+
+{
+
+ private final static Set after =
+ new HashSet(Arrays.asList(new String[]{"change-bar-class", "content"}));
+ private final static Set alwaysValid =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "background-attachment",
+ "background-color",
+ "background-image",
+ "background-repeat",
+ "border-top-color",
+ "border-right-color",
+ "border-bottom-color",
+ "border-left-color",
+ "border-top-style",
+ "border-right-style",
+ "border-bottom-style",
+ "border-left-style",
+ "border-top-width",
+ "border-right-width",
+ "border-bottom-width",
+ "border-left-width",
+ "color",
+ "counter-increment",
+ "counter-reset",
+ "direction",
+ "display",
+ "font",
+ "font-family",
+ "font-size",
+ "font-size-adjust",
+ "font-stretch",
+ "font-style",
+ "font-variant",
+ "font-weight",
+ "letter-spacing",
+ "line-height",
+ "margin-top",
+ "margin-right",
+ "margin-bottom",
+ "margin-left",
+ "padding-top",
+ "padding-right",
+ "padding-bottom",
+ "padding-left",
+ "position",
+ "quotes",
+ "region",
+ "string-set",
+ "text-decoration",
+ "text-shadow",
+ "text-transform",
+ "unicode-bidi",
+ "visibility",
+ "word-spacing",
+ // Internal attributes.
+ "has-first-letter",
+ "has-markers",
+ "list-label-width"
+ }
+ )
+ );
+ private final static Set before =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "change-bar-class", "change-bar-color", "change-bar-offset",
+ "change-bar-placement", "change-bar-style", "change-bar-width",
+ "content"
+ }
+ )
+ );
+ private final static Set blockLevel =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "anchor", "background-position", "clear", "clip", "hyphenate",
+ "link", "orphans", "overflow", "page", "page-break-after",
+ "page-break-before", "page-break-inside", "text-align",
+ "text-align-last", "text-indent", "white-space", "widows"
+ }
+ )
+ );
+ private final static Set blockLevelNotTable =
+ new HashSet(Arrays.asList(new String[]{"column-span"}));
+ private final static Set blockOrTableOrTableCell =
+ new HashSet(Arrays.asList(new String[]{"orientation"}));
+ private final static Set graphic =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "background-position", "content-height", "content-type",
+ "content-width", "height", "max-height", "max-width", "min-height",
+ "min-width", "overflow", "scaling", "scaling-method", "src", "width"
+ }
+ )
+ );
+ private final static Set inline =
+ new HashSet
+ (
+ Arrays.
+ asList(new String[]{"anchor", "hyphenate", "link", "vertical-align"})
+ );
+ private final static Set leader =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "leader-alignment", "leader-length", "leader-pattern",
+ "leader-pattern-width", "rule-thickness"
+ }
+ )
+ );
+ private final static Set listItem =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "list-style", "list-style-image", "list-style-position",
+ "list-style-type"
+ }
+ )
+ );
+ private final static Set marker =
+ new HashSet(Arrays.asList(new String[]{"marker-offset"}));
+ private final static Set notInlineOrTable =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]{"max-height", "max-width", "min-height", "min-width"}
+ )
+ );
+ private final static Set notInlineOrTableColumnOrColumnGroup =
+ new HashSet(Arrays.asList(new String[]{"height"}));
+ private final static Set notInlineOrTableRowOrRowGroup =
+ new HashSet(Arrays.asList(new String[]{"width"}));
+ private final static Set notPositioned =
+ new HashSet(Arrays.asList(new String[]{"float"}));
+ private final static Set positioned =
+ new HashSet
+ (
+ Arrays.asList(new String[]{"bottom", "left", "right", "top", "z-index"})
+ );
+ private final static Set table =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "border-collapse", "border-spacing", "caption-side", "empty-cells",
+ "table-layout", "table-omit-footer-at-break",
+ "table-omit-header-at-break"
+ }
+ )
+ );
+ private final static Set tableCaption =
+ new HashSet(Arrays.asList(new String[]{"caption-side"}));
+ private final static Set tableCell =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]{"colspan", "empty-cells", "rowspan", "vertical-align"}
+ )
+ );
+
+
+
+ InvalidPropertyFilter()
+ {
+ }
+
+
+
+ InvalidPropertyFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private static boolean
+ isBlockLevel(String display)
+ {
+ return
+ display == Element.BLOCK || display == Element.COMPACT ||
+ display == Element.LIST_ITEM || display == Element.RUN_IN ||
+ display == Element.TABLE || display == Element.TABLE_CELL ||
+ display == Element.TABLE_ROW || display == Element.MARKER;
+ }
+
+
+
+ private static boolean
+ isInline(String display)
+ {
+ return
+ display == Element.INLINE || display == Element.GRAPHIC ||
+ display == Element.LEADER;
+ }
+
+
+
+ private static boolean
+ isPositioned(Attributes atts)
+ {
+ String value = atts.getValue(Constants.CSS, "position");
+
+ return value != null && !"static".equals(value);
+ }
+
+
+
+ private static boolean
+ isValid
+ (
+ Attributes atts,
+ int index,
+ String display,
+ boolean isBefore,
+ boolean isAfter,
+ boolean isPositioned
+ )
+ {
+ String localName = atts.getLocalName(index);
+
+ return
+ !Constants.CSS.equals(atts.getURI(index)) ||
+ (
+ alwaysValid.contains(localName) ||
+ Util.isInherited(localName) ||
+ (isBlockLevel(display) && blockLevel.contains(localName)) ||
+ (display == Element.LIST_ITEM && listItem.contains(localName)) ||
+ (
+ (display == Element.TABLE || display == Element.INLINE_TABLE) &&
+ table.contains(localName)
+ ) ||
+ (
+ display == Element.TABLE_CAPTION &&
+ tableCaption.contains(localName)
+ ) ||
+ (display == Element.TABLE_CELL && tableCell.contains(localName)) ||
+ (isInline(display) && inline.contains(localName)) ||
+ (
+ !isInline(display) && display != Element.TABLE &&
+ notInlineOrTable.contains(localName)
+ ) ||
+ (
+ !isInline(display) && display != Element.TABLE_COLUMN &&
+ display != Element.TABLE_COLUMN_GROUP &&
+ notInlineOrTableColumnOrColumnGroup.contains(localName)
+ ) ||
+ (
+ !isInline(display) && display != Element.TABLE_ROW &&
+ display != Element.TABLE_ROW_GROUP &&
+ notInlineOrTableRowOrRowGroup.contains(localName)
+ ) ||
+ (
+ display != Element.TABLE && isBlockLevel(display) &&
+ blockLevelNotTable.contains(localName)
+ ) ||
+ (display == Element.MARKER && marker.contains(localName)) ||
+ (display == Element.GRAPHIC && graphic.contains(localName)) ||
+ (display == Element.LEADER && leader.contains(localName)) ||
+ (
+ (
+ display == Element.BLOCK || display == Element.TABLE ||
+ display == Element.TABLE_CELL
+ ) && blockOrTableOrTableCell.contains(localName)
+ ) ||
+ (isAfter && after.contains(localName)) ||
+ (isBefore && before.contains(localName)) ||
+ (isPositioned && positioned.contains(localName)) ||
+ (!isPositioned && notPositioned.contains(localName))
+ );
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String display = atts.getValue(Constants.CSS, "display");
+
+ if
+ (
+ display == null ||
+ (
+ Constants.CSS.equals(namespaceURI) &&
+ !"after".equals(localName) &&
+ !"before".equals(localName)
+ )
+ )
+ {
+ super.startElement(namespaceURI, localName, qName, atts);
+
+ return;
+ }
+
+ display = display.intern();
+
+ boolean after =
+ Constants.CSS.equals(namespaceURI) && "after".equals(localName);
+ boolean before =
+ Constants.CSS.equals(namespaceURI) && "before".equals(localName);
+ AttributesImpl newAtts = new AttributesImpl();
+ boolean positioned = isPositioned(atts);
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if (isValid(atts, i, display, before, after, positioned))
+ {
+ newAtts.addAttribute
+ (
+ atts.getURI(i),
+ atts.getLocalName(i),
+ atts.getQName(i),
+ atts.getType(i),
+ atts.getValue(i)
+ );
+ }
+ }
+
+ super.startElement(namespaceURI, localName, qName, newAtts);
+ }
+
+} // InvalidPropertyFilter
diff --git a/src/be/re/css/LengthAdjustFilter.java b/src/be/re/css/LengthAdjustFilter.java
new file mode 100644
index 0000000..4530a65
--- /dev/null
+++ b/src/be/re/css/LengthAdjustFilter.java
@@ -0,0 +1,163 @@
+package be.re.css;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Length properties without a unit are given the unit "px".
+ * @author Werner Donn\u00e9
+ */
+
+class LengthAdjustFilter extends XMLFilterImpl
+
+{
+
+ private static final Set ofLengthType =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "background-position",
+ "border-bottom-width",
+ "border-left-width",
+ "border-right-width",
+ "border-spacing",
+ "border-top-width",
+ "bottom",
+ "font-size",
+ "height",
+ "img-height",
+ "img-width",
+ "left",
+ "letter-spacing",
+ "line-height",
+ "margin-bottom",
+ "margin-left",
+ "margin-right",
+ "margin-top",
+ "marker-offset",
+ "max-height",
+ "max-width",
+ "min-height",
+ "min-width",
+ "outline-width",
+ "padding-bottom",
+ "padding-left",
+ "padding-right",
+ "padding-top",
+ "right",
+ "size",
+ "text-indent",
+ "text-shadow",
+ "top",
+ "vertical-align",
+ "width",
+ "word-spacing"
+ }
+ )
+ );
+
+
+
+ LengthAdjustFilter()
+ {
+ }
+
+
+
+ LengthAdjustFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private static Attributes
+ adjustAttributes(Attributes atts)
+ {
+ AttributesImpl result = null;
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if
+ (
+ Constants.CSS.equals(atts.getURI(i)) &&
+ ofLengthType.contains(atts.getLocalName(i))
+ )
+ {
+ boolean changed = false;
+ String newValue = "";
+ StringTokenizer tokenizer = new StringTokenizer(atts.getValue(i), " ");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (mustReplace(token))
+ {
+ changed = true;
+ newValue += (newValue.equals("") ? "" : " " ) + token + "px";
+ }
+ else
+ {
+ newValue += (newValue.equals("") ? "" : " " ) + token;
+ }
+ }
+
+ if (changed)
+ {
+ if (result == null)
+ {
+ result = new AttributesImpl(atts);
+ }
+
+ result.setValue(i, newValue);
+ }
+ }
+ }
+
+ return result != null ? (Attributes) result : atts;
+ }
+
+
+
+ private static boolean
+ mustReplace(String s)
+ {
+ try
+ {
+ return Integer.parseInt(s) > 0;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ super.startElement(namespaceURI, localName, qName, adjustAttributes(atts));
+ }
+
+} // LengthAdjustFilter
diff --git a/src/be/re/css/LinkFilter.java b/src/be/re/css/LinkFilter.java
new file mode 100644
index 0000000..52fd98f
--- /dev/null
+++ b/src/be/re/css/LinkFilter.java
@@ -0,0 +1,218 @@
+package be.re.css;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Processes css:link and css:anchor properties.
+ * @author Werner Donn\u00e9
+ */
+
+class LinkFilter extends XMLFilterImpl
+
+{
+
+ private static final int EXTERNAL_LINK = 0;
+ private static final int INTERNAL_LINK = 1;
+ private static final int NO_LINK = 2;
+
+ private URL baseUrl;
+ private Stack elements = new Stack();
+
+
+
+ LinkFilter(URL baseUrl)
+ {
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ LinkFilter(URL baseUrl, XMLReader parent)
+ {
+ super(parent);
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ super.endElement(namespaceURI, localName, qName);
+
+ int linkType = ((Element) elements.pop()).linkType;
+
+ if (linkType != NO_LINK)
+ {
+ String name =
+ (linkType == INTERNAL_LINK ? "internal-link" : "external-link");
+
+ super.endElement(Constants.CSS, name, "css:" + name);
+ }
+ }
+
+
+
+ private void
+ handleBaseUrl(Attributes atts) throws SAXException
+ {
+ String base = atts.getValue("xml:base");
+
+ try
+ {
+ ((Element) elements.peek()).baseUrl =
+ base != null ?
+ new URL(base) :
+ (
+ elements.size() == 1 ?
+ baseUrl : ((Element) elements.get(elements.size() - 2)).baseUrl
+ );
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+
+
+ private static boolean
+ isUrl(URL baseUrl, String target)
+ {
+ try
+ {
+ new URL(baseUrl != null ? baseUrl : new URL("file:///nowhere"), target);
+
+ return true;
+ }
+
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ private static Attributes
+ resolveAnchor(Attributes atts)
+ {
+ if (atts.getIndex(Constants.CSS, "anchor") == -1)
+ {
+ return atts;
+ }
+
+ AttributesImpl result = new AttributesImpl(atts);
+ int index = result.getIndex(Constants.CSS, "anchor");
+
+ if (result.getValue(index).equalsIgnoreCase("none"))
+ {
+ result.removeAttribute(index);
+ }
+ else
+ {
+ result.setValue(index, Util.getIndirectValue(result, "anchor"));
+ }
+
+ return result;
+ }
+
+
+
+ void
+ setBaseUrl(URL url)
+ {
+ baseUrl = url;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Element element = new Element();
+
+ elements.push(element);
+ handleBaseUrl(atts);
+
+ String link = atts.getValue(Constants.CSS, "link");
+
+ if (link != null)
+ {
+ String target = Util.getIndirectValue(atts, "link");
+ String type = Util.getIndirectType(atts, "link");
+
+ atts = new AttributesImpl(atts);
+ ((AttributesImpl) atts).
+ removeAttribute(atts.getIndex(Constants.CSS, "link"));
+
+ if (!link.equalsIgnoreCase("none") && target != null)
+ {
+ AttributesImpl linkAtts = new AttributesImpl();
+
+ element.linkType =
+ target.startsWith("#") || "IDREF".equals(type) ?
+ INTERNAL_LINK :
+ (isUrl(element.baseUrl, target) ? EXTERNAL_LINK : NO_LINK);
+
+ if (element.linkType != NO_LINK)
+ {
+ String name =
+ element.linkType == INTERNAL_LINK ?
+ "internal-link" : "external-link";
+
+ linkAtts.addAttribute
+ (
+ "",
+ "target",
+ "target",
+ "CDATA",
+ element.linkType == INTERNAL_LINK ?
+ target.substring(target.startsWith("#") ? 1 : 0) : target
+ );
+
+ super.startElement(Constants.CSS, name, "css:" + name, linkAtts);
+ }
+ }
+ else
+ {
+ element.linkType = NO_LINK;
+ }
+ }
+ else
+ {
+ element.linkType = NO_LINK;
+ }
+
+ super.startElement(namespaceURI, localName, qName, resolveAnchor(atts));
+ }
+
+
+
+ private static class Element
+
+ {
+
+ private URL baseUrl;
+ private int linkType;
+
+ } // Element
+
+} // LinkFilter
diff --git a/src/be/re/css/ListImageLabelFilter.java b/src/be/re/css/ListImageLabelFilter.java
new file mode 100644
index 0000000..2105d3a
--- /dev/null
+++ b/src/be/re/css/ListImageLabelFilter.java
@@ -0,0 +1,115 @@
+package be.re.css;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+import javax.imageio.ImageIO;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Fetches images specified in <code>list-style-image</code> properties and
+ * sets their width as the <code>list-label-width</code> property.
+ * @author Werner Donn\u00e9
+ */
+
+class ListImageLabelFilter extends XMLFilterImpl
+
+{
+
+ ListImageLabelFilter()
+ {
+ }
+
+
+
+ ListImageLabelFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private static String
+ decodeUrl(String url)
+ {
+ return
+ url.startsWith("url(") && url.endsWith(")") ?
+ url.substring(4, url.length() - 1) : url;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String url = atts.getValue(Constants.CSS, "list-style-image");
+
+ if (url != null)
+ {
+ if (url.equals("none"))
+ {
+ atts = new AttributesImpl(atts);
+
+ Util.removeAttribute
+ (
+ (AttributesImpl) atts,
+ Constants.CSS,
+ "list-style-image"
+ );
+
+ if (atts.getValue(Constants.CSS, "list-style-type") == null)
+ {
+ ((AttributesImpl) atts).addAttribute
+ (
+ Constants.CSS,
+ "list-style-type",
+ "css:list-style-type",
+ "CDATA",
+ "none"
+ );
+ }
+ }
+ else
+ {
+ try
+ {
+ BufferedImage image = ImageIO.read(new URL(decodeUrl(url)));
+
+ if (image != null)
+ {
+ atts = new AttributesImpl(atts);
+
+ ((AttributesImpl) atts).addAttribute
+ (
+ Constants.CSS,
+ "list-label-width",
+ "css:list-label-width",
+ "CDATA",
+ String.valueOf(image.getWidth() + 5) + "px"
+ );
+ }
+ }
+
+ catch (IOException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+ }
+
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+} // ListImageLabelFilter
diff --git a/src/be/re/css/MarkerFilter.java b/src/be/re/css/MarkerFilter.java
new file mode 100644
index 0000000..18efaa3
--- /dev/null
+++ b/src/be/re/css/MarkerFilter.java
@@ -0,0 +1,389 @@
+package be.re.css;
+
+import be.re.xml.Accumulator;
+import be.re.xml.DOMToContentHandler;
+import be.re.xml.sax.FilterOfFilters;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NamedNodeMap;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Creates table structures to implement the marker display type.
+ * @author Werner Donn\u00e9
+ */
+
+class MarkerFilter extends XMLFilterImpl
+
+{
+
+ private static final String DEFAULT_WIDTH = "2em";
+
+
+
+ MarkerFilter()
+ {
+ }
+
+
+
+ MarkerFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private void
+ accumulate
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Accumulator.preAccumulate
+ (
+ namespaceURI,
+ localName,
+ qName,
+ atts,
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(Element element, XMLFilter filter) throws SAXException
+ {
+ DOMToContentHandler.elementToContentHandler
+ (
+ transform(element),
+ filter.getContentHandler()
+ );
+ }
+ }
+ );
+ }
+
+
+
+ private static void
+ addBody(Element table, Element element, Element before, Element after)
+ {
+ Element body =
+ table.getOwnerDocument().
+ createElementNS(Constants.CSS, "table-row-group");
+
+ body.setAttributeNS(Constants.CSS, "css:display", "table-row-group");
+ table.appendChild(body);
+
+ Element row =
+ table.getOwnerDocument().createElementNS(Constants.CSS, "table-row");
+
+ row.setAttributeNS(Constants.CSS, "css:display", "table-row");
+ body.appendChild(row);
+
+ Element mainCell =
+ table.getOwnerDocument().createElementNS(Constants.CSS, "table-cell");
+
+ mainCell.setAttributeNS(Constants.CSS, "css:display", "table-cell");
+ row.appendChild(mainCell);
+ mainCell.appendChild(element);
+
+ if (before != null)
+ {
+ Element cell =
+ table.getOwnerDocument().createElementNS(Constants.CSS, "table-cell");
+
+ cell.setAttributeNS(Constants.CSS, "css:display", "table-cell");
+ cell.setAttributeNS(Constants.CSS, "css:vertical-align", "top");
+ row.insertBefore(cell, mainCell);
+ addMarker(cell, before, "right");
+ }
+
+ if (after != null)
+ {
+ Element cell =
+ table.getOwnerDocument().createElementNS(Constants.CSS, "table-cell");
+
+ cell.setAttributeNS(Constants.CSS, "css:display", "table-cell");
+ cell.setAttributeNS(Constants.CSS, "css:vertical-align", "bottom");
+ row.appendChild(cell);
+ addMarker(cell, after, "left");
+ }
+ }
+
+
+
+ private static void
+ addColumn(Element table, String width)
+ {
+ Element column =
+ table.getOwnerDocument().createElementNS(Constants.CSS, "table-column");
+
+ column.setAttributeNS(Constants.CSS, "css:display", "table-column");
+
+ column.setAttributeNS
+ (
+ Constants.CSS,
+ "css:width",
+ width.equals("") ? "1*" : width
+ );
+ table.appendChild(column);
+ }
+
+
+
+ private static void
+ addMarker(Element cell, Element marker, String side)
+ {
+ cell.appendChild(marker);
+ marker.removeAttributeNS(Constants.CSS, "width");
+ marker.setAttributeNS(Constants.CSS, "css:display", "block");
+
+ String markerOffset =
+ marker.getAttributeNS(Constants.CSS, "marker-offset");
+
+ if (!markerOffset.equals(""))
+ {
+ cell.setAttributeNS(Constants.CSS, "css:padding-" + side, markerOffset);
+ marker.removeAttributeNS(Constants.CSS, "marker-offset");
+ }
+ }
+
+
+
+ private static Element
+ getAfterPseudoElement(Node node)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node instanceof Element &&
+ Constants.CSS.equals(node.getNamespaceURI()) &&
+ "after".equals(node.getLocalName()) &&
+ "marker".equals
+ (
+ ((Element) node).getAttributeNS(Constants.CSS, "display")
+ ) ?
+ (Element) node :
+ getAfterPseudoElement(node.getPreviousSibling())
+ );
+ }
+
+
+
+ private static Element
+ getBeforePseudoElement(Node node)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node instanceof Element &&
+ Constants.CSS.equals(node.getNamespaceURI()) &&
+ "before".equals(node.getLocalName()) &&
+ "marker".equals
+ (
+ ((Element) node).getAttributeNS(Constants.CSS, "display")
+ ) ?
+ (Element) node :
+ getBeforePseudoElement(node.getNextSibling())
+ );
+ }
+
+
+
+ private static Element
+ handleNestedMarkers(Element element) throws SAXException
+ {
+ Accumulator result = new Accumulator();
+ FilterOfFilters filter =
+ new FilterOfFilters
+ (
+ // The MarkerFilter needs a parent to insert its own accumulator.
+ new XMLFilter[]{new XMLFilterImpl(), new MarkerFilter(), result}
+ );
+
+ filter.startDocument();
+ DOMToContentHandler.elementToContentHandler(element, filter);
+ filter.endDocument();
+
+ return
+ (Element)
+ element.getOwnerDocument().
+ importNode(result.getDocument().getDocumentElement(), true);
+ }
+
+
+
+ private static void
+ moveInheritedProperties(Element element, Element table)
+ {
+ NamedNodeMap attributes = element.getAttributes();
+
+ for (int i = 0; i < attributes.getLength(); ++i)
+ {
+ Attr attribute = (Attr) attributes.item(i);
+
+ if
+ (
+ Constants.CSS.equals(attribute.getNamespaceURI()) &&
+ Util.isInherited(attribute.getLocalName())
+ )
+ {
+ element.removeAttributeNode(attribute);
+ table.setAttributeNodeNS(attribute);
+ --i;
+ }
+ }
+ }
+
+
+
+ private static void
+ moveMargin(Element element, Element table, String side)
+ {
+ Attr margin =
+ element.getAttributeNodeNS(Constants.CSS, "margin-" + side);
+
+ if (margin != null && !margin.getValue().equals(""))
+ {
+ element.removeAttributeNode(margin);
+ table.setAttributeNodeNS(margin);
+ }
+ }
+
+
+
+ private static void
+ moveMargins(Element element, Element table, String beforeWidth)
+ {
+ String margin = element.getAttributeNS(Constants.CSS, "margin-left");
+
+ if (Util.isZeroLength(margin))
+ {
+ margin = "";
+ }
+
+ if (beforeWidth != null || !margin.equals(""))
+ {
+ table.setAttributeNS
+ (
+ Constants.CSS,
+ "css:margin-left",
+ (beforeWidth != null ? ("-" + beforeWidth) : "") +
+ (!margin.equals("") ? ("+" + margin) : "")
+ );
+ }
+
+ if (!margin.equals(""))
+ {
+ element.removeAttributeNS(Constants.CSS, "margin-left");
+ }
+
+ moveMargin(element, table, "right");
+ moveMargin(element, table, "top");
+ moveMargin(element, table, "bottom");
+ }
+
+
+
+ private static void
+ removeMargins(Element marker)
+ {
+ marker.removeAttributeNS(Constants.CSS, "margin-left");
+ marker.removeAttributeNS(Constants.CSS, "margin-right");
+ marker.removeAttributeNS(Constants.CSS, "margin-top");
+ marker.removeAttributeNS(Constants.CSS, "margin-bottom");
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ if ("1".equals(atts.getValue(Constants.CSS, "has-markers")))
+ {
+ accumulate(namespaceURI, localName, qName, atts);
+ }
+ else
+ {
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+ }
+
+
+
+ private static Element
+ transform(Element element) throws SAXException
+ {
+ Element after = getAfterPseudoElement(element.getLastChild());
+ Element before = getBeforePseudoElement(element.getFirstChild());
+ String beforeWidth = null;
+ Element table =
+ element.getOwnerDocument().createElementNS(Constants.CSS, "table");
+
+ table.setAttributeNS(Constants.CSS, "css:display", "table");
+ table.setAttributeNS(Constants.CSS, "css:table-layout", "fixed");
+
+ if (before != null)
+ {
+ beforeWidth = before.getAttributeNS(Constants.CSS, "width");
+
+ if (beforeWidth.equals("") || beforeWidth.equals("auto"))
+ {
+ beforeWidth = DEFAULT_WIDTH;
+ }
+
+ addColumn(table, beforeWidth);
+
+ if ("list-item".equals(element.getAttributeNS(Constants.CSS, "display")))
+ {
+ element.setAttributeNS(Constants.CSS, "css:display", "block");
+ }
+
+ element.removeChild(before);
+ removeMargins(before);
+ }
+
+ addColumn(table, element.getAttributeNS(Constants.CSS, "width"));
+ element.setAttributeNS(Constants.CSS, "css:width", "100%");
+
+ if (after != null)
+ {
+ String width = before.getAttributeNS(Constants.CSS, "width");
+
+ addColumn
+ (
+ table,
+ !width.equals("") && !width.equals("auto") ? width : DEFAULT_WIDTH
+ );
+
+ element.removeChild(after);
+ removeMargins(after);
+ }
+
+ moveMargins(element, table, beforeWidth);
+ moveInheritedProperties(element, table);
+ element.removeAttributeNS(Constants.CSS, "has-markers");
+ addBody(table, handleNestedMarkers(element), before, after);
+
+ return table;
+ }
+
+} // MarkerFilter
diff --git a/src/be/re/css/Matcher.java b/src/be/re/css/Matcher.java
new file mode 100644
index 0000000..10b63db
--- /dev/null
+++ b/src/be/re/css/Matcher.java
@@ -0,0 +1,652 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+import org.w3c.css.sac.AttributeCondition;
+import org.w3c.css.sac.CombinatorCondition;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.DocumentHandler;
+import org.w3c.css.sac.LangCondition;
+import org.w3c.css.sac.NegativeCondition;
+import org.w3c.css.sac.PositionalCondition;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * Finds the matching rules as the document goes through it.
+ * @author Werner Donn\u00e9
+ */
+
+public class Matcher implements ContentHandler
+
+{
+
+ private static final String DEFAULT_LANGUAGE = "en-GB";
+
+ private Stack elements = new Stack();
+ private Compiled.DFAState startState;
+ private static final boolean trace =
+ System.getProperty("be.re.css.trace") != null;
+
+
+
+ public
+ Matcher(Compiled styleSheet)
+ {
+ startState = styleSheet.startState;
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ }
+
+
+
+ private static boolean
+ checkAttributeCondition(Element e, AttributeCondition c, TestAttribute test)
+ {
+ if (c.getNamespaceURI() != null)
+ {
+ int index =
+ DocumentHandler.SAC_NO_URI.equals(c.getNamespaceURI()) ?
+ e.attributes.getIndex(c.getLocalName()) :
+ e.attributes.getIndex(c.getNamespaceURI(), c.getLocalName());
+
+ return index != -1 && test.test(e.attributes, index, c);
+ }
+
+ for (int i = 0; i < e.attributes.getLength(); ++i)
+ {
+ if
+ (
+ e.attributes.getLocalName(i).equals(c.getLocalName()) &&
+ test.test(e.attributes, i, c)
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ private static boolean
+ checkAttributeCondition(Element e, AttributeCondition c)
+ {
+ return
+ checkAttributeCondition
+ (
+ e,
+ c,
+ new TestAttribute()
+ {
+ public boolean
+ test(Attributes atts, int i, AttributeCondition c)
+ {
+ return
+ c.getValue() == null || c.getValue().equals(atts.getValue(i));
+ }
+ }
+ );
+ }
+
+
+
+ private static boolean
+ checkBeginHyphenAttributeCondition(Element e, AttributeCondition c)
+ {
+ return
+ checkAttributeCondition
+ (
+ e,
+ c,
+ new TestAttribute()
+ {
+ public boolean
+ test(Attributes atts, int i, AttributeCondition c)
+ {
+ return
+ atts.getValue(i).startsWith(c.getValue() + "-") ||
+ atts.getValue(i).equals(c.getValue());
+ }
+ }
+ );
+ }
+
+
+
+ private static boolean
+ checkClassCondition(Element e, AttributeCondition c)
+ {
+ String value;
+
+ return
+ (value = e.attributes.getValue("class")) != null &&
+ hasToken(value, c.getValue());
+ }
+
+
+
+ private static boolean
+ checkCondition(Element e, Condition c)
+ {
+ switch (c.getConditionType())
+ {
+ case Condition.SAC_AND_CONDITION:
+ return
+ checkCondition(e, ((CombinatorCondition) c).getFirstCondition()) &&
+ checkCondition(e, ((CombinatorCondition) c).getSecondCondition());
+
+ case Condition.SAC_ATTRIBUTE_CONDITION:
+ return checkAttributeCondition(e, (AttributeCondition) c);
+
+ case Condition.SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION:
+ return checkBeginHyphenAttributeCondition(e, (AttributeCondition) c);
+
+ case Condition.SAC_CLASS_CONDITION:
+ return checkClassCondition(e, (AttributeCondition) c);
+
+ case Condition.SAC_ID_CONDITION:
+ return checkIdCondition(e, (AttributeCondition) c);
+
+ case Condition.SAC_LANG_CONDITION:
+ return checkLangCondition(e, (LangCondition) c);
+
+ case Condition.SAC_NEGATIVE_CONDITION:
+ return !checkCondition(e, ((NegativeCondition) c).getCondition());
+
+ case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION:
+ return checkOneOfAttributeCondition(e, (AttributeCondition) c);
+
+ case Condition.SAC_OR_CONDITION:
+ return
+ checkCondition(e, ((CombinatorCondition) c).getFirstCondition()) ||
+ checkCondition(e, ((CombinatorCondition) c).getSecondCondition());
+
+ case Condition.SAC_POSITIONAL_CONDITION:
+ return
+ checkPositionalCondition
+ (
+ e,
+ ((PositionalCondition) c).getPosition()
+ );
+
+ case Condition.SAC_PSEUDO_CLASS_CONDITION:
+ return checkPseudoClassCondition(e, (AttributeCondition) c);
+
+ default:
+ return false; // Ignore non-CSS2 or irrelevant condition types.
+ }
+ }
+
+
+
+ private static boolean
+ checkIdCondition(Element e, AttributeCondition c)
+ {
+ for (int i = 0; i < e.attributes.getLength(); ++i)
+ {
+ if
+ (
+ "ID".equals(e.attributes.getType(i)) &&
+ c.getValue().equals(e.attributes.getValue(i))
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ private static boolean
+ checkLangCondition(Element e, LangCondition c)
+ {
+ return
+ e.language.startsWith(((LangCondition) c).getLang() + "-") ||
+ e.language.equals(((LangCondition) c).getLang());
+ }
+
+
+
+ private static boolean
+ checkOneOfAttributeCondition(Element e, AttributeCondition c)
+ {
+ return
+ checkAttributeCondition
+ (
+ e,
+ c,
+ new TestAttribute()
+ {
+ public boolean
+ test(Attributes atts, int i, AttributeCondition c)
+ {
+ return hasToken(atts.getValue(i), c.getValue());
+ }
+ }
+ );
+ }
+
+
+
+ private static boolean
+ checkPositionalCondition(Element e, int position)
+ {
+ // The element on the top of the stack is not yet in the child list of its
+ // parent. The preceding sibling is the last element in the parent's child
+ // list.
+
+ return e.parent.children.size() == position;
+ }
+
+
+
+ private static boolean
+ checkPseudoClassCondition(Element e, AttributeCondition c)
+ {
+ return
+ "after".equals(c.getValue()) || "before".equals(c.getValue()) ||
+ ("first-child".equals(c.getValue()) && checkPositionalCondition(e, 0));
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ Element element = (Element) elements.pop();
+
+ ((Element) elements.peek()).children.add(element);
+ element.children = null;
+ }
+
+
+
+ public void
+ endPrefixMapping(String prefix) throws SAXException
+ {
+ }
+
+
+
+ private String
+ getLanguage(String namespaceURI, Attributes attributes, Element parent)
+ {
+ String result = null;
+
+ if (Constants.XHTML == namespaceURI)
+ {
+ result = attributes.getValue("lang");
+ }
+
+ if (result == null)
+ {
+ result = attributes.getValue("xml:lang");
+ }
+
+ if (result == null)
+ {
+ result = parent.language;
+ }
+
+ return result;
+ }
+
+
+
+ private static Set
+ getSiblingStates(Collection states)
+ {
+ Set result = new HashSet();
+
+ for (Iterator i = states.iterator(); i.hasNext();)
+ {
+ Object nextState =
+ ((Compiled.DFAState) i.next()).events.get(Compiled.SIBLING);
+
+ if (nextState != null)
+ {
+ result.add(nextState);
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public void
+ ignorableWhitespace(char[] ch, int start, int length) throws SAXException
+ {
+ }
+
+
+
+ private static boolean
+ hasToken(String s, String token)
+ {
+ int i;
+
+ return
+ (i = s.indexOf(token)) != -1 && (i == 0 || s.charAt(i - 1) == ' ') &&
+ (
+ i == s.length() - token.length() ||
+ s.charAt(i + token.length()) == ' '
+ );
+ }
+
+
+
+ /**
+ * Returns the rules that match a pseudo element sorted from least to most
+ * specific.
+ */
+
+ public Rule[]
+ matchingPseudoRules()
+ {
+ SortedSet result = new TreeSet(new RuleComparator());
+
+ for
+ (
+ Iterator i = ((Element) elements.peek()).states.iterator();
+ i.hasNext();
+ )
+ {
+ result.addAll(((Compiled.DFAState) i.next()).pseudoRules);
+ }
+
+ return (Rule[]) result.toArray(new Rule[0]);
+ }
+
+
+
+ /**
+ * Returns the rules that match a normal element sorted from least to most
+ * specific.
+ */
+
+ public Rule[]
+ matchingRules()
+ {
+ SortedSet result = new TreeSet(new RuleComparator());
+
+ for
+ (
+ Iterator i = ((Element) elements.peek()).states.iterator();
+ i.hasNext();
+ )
+ {
+ result.addAll(((Compiled.DFAState) i.next()).rules);
+ }
+
+ return (Rule[]) result.toArray(new Rule[0]);
+ }
+
+
+
+ public void
+ processingInstruction(String target, String data) throws SAXException
+ {
+ }
+
+
+
+ public void
+ setDocumentLocator(Locator locator)
+ {
+ }
+
+
+
+ public void
+ skippedEntity(String name) throws SAXException
+ {
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ elements.clear();
+
+ Element root = new Element("", "/");
+
+ root.language = DEFAULT_LANGUAGE;
+ elements.push(root);
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Element parent = (Element) elements.peek();
+ Set currentStates = parent.states;
+ Element element = new Element(namespaceURI, localName);
+
+ element.attributes = atts;
+ element.language = getLanguage(namespaceURI, atts, parent);
+ element.parent = parent;
+ elements.push(element);
+
+ traceElement(namespaceURI + "|" + localName, atts);
+ stepStates(parent.states, element);
+
+ if (parent.children.size() > 0)
+ {
+ stepStates
+ (
+ getSiblingStates
+ (
+ ((Element) parent.children.get(parent.children.size() - 1)).states
+ ),
+ element
+ );
+ }
+
+ // At every element new rules can be started, because they are relative.
+
+ step(startState, element);
+ }
+
+
+
+ public void
+ startPrefixMapping(String prefix, String uri) throws SAXException
+ {
+ }
+
+
+
+ /**
+ * More than one state transition can occur because when the candidate
+ * conditions are fullfilled, they constitute an event. The universal
+ * selector transitions are also tried.
+ */
+
+ private static void
+ step(Compiled.DFAState state, Element element)
+ {
+ stepOneEvent
+ (
+ state,
+ element,
+ ("".equals(element.namespaceURI) ? "*" : element.namespaceURI) + "|" +
+ element.localName
+ );
+
+ if (!"".equals(element.namespaceURI))
+ {
+ stepOneEvent(state, element, "*|" + element.localName);
+ stepOneEvent(state, element, element.namespaceURI + "|*");
+ }
+ else
+ {
+ stepOneEvent
+ (
+ state,
+ element,
+ DocumentHandler.SAC_NO_URI + "|" + element.localName
+ );
+ }
+
+ stepOneEvent(state, element, Compiled.ANY_ELEMENT);
+ }
+
+
+
+ private static void
+ stepOneEvent(Compiled.DFAState state, Element element, String name)
+ {
+ Compiled.DFAState nextState = (Compiled.DFAState) state.events.get(name);
+
+ if (nextState != null)
+ {
+ traceTransition(state, nextState, name);
+ element.states.add(nextState);
+ stepThroughConditions(nextState, element);
+ }
+ }
+
+
+
+ private static void
+ stepStates(Collection states, Element element)
+ {
+ for (Iterator i = states.iterator(); i.hasNext();)
+ {
+ step((Compiled.DFAState) i.next(), element);
+ }
+ }
+
+
+
+ private static void
+ stepThroughConditions(Compiled.DFAState state, Element element)
+ {
+ for
+ (
+ Iterator i = state.candidateConditions.keySet().iterator();
+ i.hasNext();
+ )
+ {
+ Condition c = (Condition) i.next();
+ Compiled.DFAState nextState =
+ (Compiled.DFAState) state.candidateConditions.get(c);
+
+ if (nextState != null && checkCondition(element, c))
+ {
+ traceTransition(state, nextState, c);
+ element.states.add(nextState);
+ }
+ }
+ }
+
+
+
+ private static void
+ traceElement(String qName, Attributes atts)
+ {
+ if (trace)
+ {
+ System.out.print(qName + ": ");
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ System.out.print(atts.getQName(i) + "=" + atts.getValue(i) + " ");
+ }
+
+ System.out.println();
+ }
+ }
+
+
+
+ private static void
+ traceTransition(Compiled.DFAState from, Compiled.DFAState to, Object event)
+ {
+ if (trace)
+ {
+ System.out.println
+ (
+ String.valueOf(from.state) + " -> " + String.valueOf(to.state) +
+ ": " +
+ (
+ event instanceof Condition ?
+ Util.conditionText((Condition) event) : event.toString()
+ )
+ );
+ }
+ }
+
+
+
+ private static class Element
+
+ {
+
+ private Attributes attributes;
+ private List children = new ArrayList();
+ private String language;
+ private String localName;
+ private String namespaceURI;
+ private Element parent;
+ private Set states = new HashSet();
+
+
+
+ private
+ Element(String namespaceURI, String localName)
+ {
+ this.namespaceURI = namespaceURI != null ? namespaceURI : "";
+ this.localName = localName;
+ }
+
+ } // Element
+
+
+
+ private interface TestAttribute
+
+ {
+
+ public boolean test (Attributes atts, int i, AttributeCondition c);
+
+ } // TestAttribute
+
+} // Matcher
diff --git a/src/be/re/css/NormalizeTableFilter.java b/src/be/re/css/NormalizeTableFilter.java
new file mode 100644
index 0000000..afc5d61
--- /dev/null
+++ b/src/be/re/css/NormalizeTableFilter.java
@@ -0,0 +1,704 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This filter propagates table-column-group properties to table-column
+ * elements and table-column properties to table-cell elements. It also fills up
+ * rows which are too short with empty table cells.
+ * @author Werner Donn\u00e9
+ */
+
+class NormalizeTableFilter extends XMLFilterImpl
+
+{
+
+ private Stack columnStack = new Stack();
+ private Stack elementStack = new Stack();
+
+
+
+ NormalizeTableFilter()
+ {
+ }
+
+
+
+ NormalizeTableFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private void
+ addColumn(Attributes atts)
+ {
+ List columns = (List) columnStack.peek();
+ int span = getSpan(atts, "span");
+
+ for (int i = 0; i < span; ++i)
+ {
+ columns.add(atts);
+ }
+ }
+
+
+
+ private void
+ addTableCellRowContributions(Element element)
+ {
+ int rowSpan = getSpan(element.atts, "rowspan");
+
+ if (rowSpan > 1)
+ {
+ int colSpan = getSpan(element.atts, "colspan");
+ Element group = getGroup();
+
+ for (int i = 1; i < rowSpan; ++i) // For the next rows.
+ {
+ if (i == ((List) group.extra).size())
+ {
+ ((List) group.extra).add(new Integer(colSpan));
+ }
+ else
+ {
+ ((List) group.extra).set
+ (
+ i,
+ new Integer
+ (
+ ((Integer) ((List) group.extra).get(i)).intValue() + colSpan
+ )
+ );
+ }
+ }
+ }
+ }
+
+
+
+ private void
+ bookKeeping(Element element, Element parent)
+ {
+ if (element.isDisplay(Element.TABLE))
+ {
+ columnStack.push(new ArrayList());
+ }
+ else
+ {
+ if (element.isDisplay(Element.TABLE_COLUMN))
+ {
+ addColumn(new AttributesImpl(element.atts));
+ }
+ else
+ {
+ if (element.isDisplay(Element.TABLE_ROW))
+ {
+ if (isGroup(parent) && parent.extra == null)
+ {
+ parent.extra = new ArrayList(); // Row seen.
+ ((List) parent.extra).add(new Integer(0));
+ // No contributions because there is no previous row.
+ }
+ }
+ else
+ {
+ if (element.isDisplay(Element.TABLE_CELL))
+ {
+ int currentCellCount =
+ parent.extra == null ?
+ 0 : ((Integer) parent.extra).intValue();
+
+ parent.extra =
+ new Integer
+ (
+ currentCellCount +
+ getSpan(element.atts, "colspan")
+ );
+
+ addTableCellRowContributions(element);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ Element element = (Element) elementStack.pop();
+
+ if
+ (
+ element.isDisplay(Element.TABLE_ROW_GROUP) &&
+ Constants.CSS.equals(element.namespaceURI) &&
+ "synthetic".equals(element.localName)
+ )
+ {
+ // Flush pending synthetic group.
+ super.endElement(Constants.CSS, "synthetic", "css:synthetic");
+ endElement(namespaceURI, localName, qName);
+
+ return;
+ }
+
+ Element parent =
+ elementStack.empty() ? null : ((Element) elementStack.peek());
+
+ if (element.isDisplay(Element.TABLE_COLUMN_GROUP))
+ {
+ synthesizeColumns(element);
+ }
+ else
+ {
+ if (element.isDisplay(Element.TABLE_ROW))
+ {
+ synthesizeCells(element, parent);
+ }
+ else
+ {
+ if (element.isDisplay(Element.TABLE))
+ {
+ columnStack.pop();
+ }
+ }
+ }
+
+ if
+ (
+ !element.isDisplay(Element.TABLE_COLUMN_GROUP) &&
+ (
+ !element.isDisplay(Element.TABLE_COLUMN) ||
+ parent == null ||
+ !parent.isDisplay(Element.TABLE_COLUMN_GROUP)
+ )
+ )
+ {
+ super.endElement(namespaceURI, localName, qName);
+ }
+ }
+
+
+
+ /**
+ * Searches down the element stack until an element with a display type in
+ * <code>oneOf</code> is found, but not beyond <code>upTo</code>.
+ */
+
+ private Element
+ getAncestor(String[] oneOf, String[] upTo)
+ {
+ for (int i = elementStack.size() - 1; i >= 0; --i)
+ {
+ Element element = (Element) elementStack.get(i);
+
+ for (int j = 0; j < oneOf.length; ++j)
+ {
+ if (element.isDisplay(oneOf[j]))
+ {
+ return element;
+ }
+ }
+
+ for (int j = 0; j < upTo.length; ++j)
+ {
+ if (element.isDisplay(upTo[j]))
+ {
+ return null;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+
+ private Attributes
+ getColumn()
+ {
+ List columns = (List) columnStack.peek();
+ Integer position = (Integer) ((Element) elementStack.peek()).extra;
+
+ return
+ position == null && columns.size() > 0 ?
+ (Attributes) columns.get(0) :
+ (
+ columns.size() == 0 || position.intValue() >= columns.size() ?
+ null : (Attributes) columns.get(position.intValue())
+ );
+ }
+
+
+
+ private Element
+ getGroup()
+ {
+ return
+ getAncestor
+ (
+ new String[]
+ {
+ Element.TABLE_ROW_GROUP, Element.TABLE_HEADER_GROUP,
+ Element.TABLE_FOOTER_GROUP
+ },
+ new String[] {Element.TABLE}
+ );
+ }
+
+
+
+ private static int
+ getSpan(Attributes atts, String name)
+ {
+ String span = atts.getValue(Constants.CSS, name);
+
+ return span == null ? 1 : Integer.parseInt(span);
+ }
+
+
+
+ private Attributes
+ getTableCellAttributes(Element element, Element parent)
+ {
+ Element table =
+ getAncestor(new String[] {Element.TABLE}, new String[0]);
+
+ if (table == null)
+ {
+ return element.atts;
+ }
+
+ Attributes column = getColumn();
+
+ // While not being general, the XHTML alignment is done here instead of in
+ // XHTMLAttributeTranslationFilter because we have to know to which column
+ // a cell belongs, in order to possibly inherit from it. This is only
+ // possible when the table is normalized.
+
+ Attributes atts =
+ Constants.XHTML.equals(parent.namespaceURI) ?
+ inheritXHTMLAlign(element.atts, column, parent) : element.atts;
+ int index = table.atts.getIndex(Constants.CSS, "border-collapse");
+
+ // The following are redundant and harmless for collapse, but it produces
+ // something useful for XSL-FO processors that treat collapse as separate.
+
+ if (index == -1 || "collapse".equals(table.atts.getValue(index)))
+ {
+ if (parent.isDisplay(Element.TABLE_ROW))
+ {
+ atts =
+ Util.
+ mergeAttributes(parent.atts, atts, new String[] {"border*"}, true);
+ }
+
+ if (column != null)
+ {
+ atts =
+ Util.mergeAttributes(column, atts, new String[] {"border*"}, true);
+ }
+ }
+
+ return atts;
+ }
+
+
+
+ private Attributes
+ getTableRowAttributes(Attributes atts, Attributes parentAtts)
+ {
+ // The following are redundant and harmless for collapse, but it produces
+ // something useful for XSL-FO processors that treat collapse as separate.
+
+ return
+ Util.mergeAttributes
+ (
+ parentAtts,
+ atts,
+ new String[] {"border-before*", "border-top*"},
+ true
+ );
+ }
+
+
+
+ private Attributes
+ inheritXHTMLAlign(Attributes atts, Attributes columnAtts, Element parent)
+ {
+ AttributesImpl result = new AttributesImpl(atts);
+ String textAlign =
+ atts.getIndex(Constants.SPECIF, "text-align") != -1 ?
+ // Can be overridden by XHTML.
+ null : atts.getValue(Constants.CSS, "text-align");
+ String verticalAlign =
+ atts.getIndex(Constants.SPECIF, "vertical-align") != -1 ?
+ // Can be overridden by XHTML.
+ null : atts.getValue(Constants.CSS, "vertical-align");
+
+ if (textAlign == null && columnAtts != null)
+ {
+ textAlign = columnAtts.getValue(Constants.CSS, "text-align");
+ }
+
+ if
+ (
+ (
+ textAlign == null ||
+ verticalAlign == null
+ ) &&
+ parent.isDisplay(Element.TABLE_ROW)
+ )
+ {
+ if (textAlign == null)
+ {
+ textAlign = parent.atts.getValue(Constants.CSS, "text-align");
+ }
+
+ if (verticalAlign == null)
+ {
+ verticalAlign = parent.atts.getValue(Constants.CSS, "vertical-align");
+ }
+
+ if (textAlign == null || verticalAlign == null)
+ {
+ Element group = getGroup();
+
+ if (group != null)
+ {
+ if (textAlign == null)
+ {
+ textAlign = group.atts.getValue(Constants.CSS, "text-align");
+ }
+
+ if (verticalAlign == null)
+ {
+ verticalAlign =
+ group.atts.getValue(Constants.CSS, "vertical-align");
+
+ if (verticalAlign == null && columnAtts != null)
+ {
+ verticalAlign =
+ columnAtts.getValue(Constants.CSS, "vertical-align");
+ }
+ }
+
+ if (textAlign == null || verticalAlign == null)
+ {
+ Element table =
+ getAncestor(new String[] {Element.TABLE}, new String[0]);
+
+ if (table != null)
+ {
+ if (textAlign == null)
+ {
+ textAlign = table.atts.getValue(Constants.CSS, "text-align");
+ }
+
+ if (verticalAlign == null)
+ {
+ verticalAlign =
+ table.atts.getValue(Constants.CSS, "vertical-align");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (textAlign != null)
+ {
+ Util.setAttribute
+ (
+ result,
+ Constants.CSS,
+ "text-align",
+ "css:text-align",
+ textAlign
+ );
+ }
+
+ if (verticalAlign != null)
+ {
+ Util.setAttribute
+ (
+ result,
+ Constants.CSS,
+ "vertical-align",
+ "css:vertical-align",
+ verticalAlign
+ );
+ }
+
+ return result;
+ }
+
+
+
+ private static boolean
+ isGroup(Element element)
+ {
+ return
+ element.isDisplay(Element.TABLE_HEADER_GROUP) ||
+ element.isDisplay(Element.TABLE_FOOTER_GROUP) ||
+ element.isDisplay(Element.TABLE_ROW_GROUP);
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Element element =
+ new Element(namespaceURI, localName, qName, atts);
+
+ if (!element.isDisplay(Element.TABLE_COLUMN_GROUP))
+ {
+ Element parent =
+ elementStack.empty() ? null : (Element) elementStack.peek();
+
+ if
+ (
+ element.isDisplay(Element.TABLE_COLUMN) &&
+ parent.isDisplay(Element.TABLE_COLUMN_GROUP)
+ )
+ {
+ parent.addChild(element);
+ }
+ else
+ {
+ if
+ (
+ element.isDisplay(Element.TABLE_ROW) &&
+ parent.isDisplay(Element.TABLE)
+ )
+ {
+ synthesizeGroup();
+ parent = (Element) elementStack.peek();
+ }
+ else
+ {
+ if (isGroup(element) && parent.isDisplay(Element.TABLE_ROW_GROUP))
+ {
+ // Flush pending synthetic group.
+ elementStack.pop();
+ super.endElement(Constants.CSS, "synthetic", "css:synthetic");
+ }
+ }
+
+ element.atts =
+ element.isDisplay(Element.TABLE_CELL) ?
+ getTableCellAttributes(element, parent) :
+ (
+ element.isDisplay(Element.TABLE_ROW) && isGroup(parent) &&
+ parent.extra == null /* No row seen yet */ ?
+ getTableRowAttributes(atts, parent.atts) : atts
+ );
+
+ super.startElement(namespaceURI, localName, qName, element.atts);
+ bookKeeping(element, parent);
+ }
+ }
+
+ elementStack.push(element);
+ }
+
+
+
+ private void
+ synthesizeCells(Element element, Element parent) throws SAXException
+ {
+ if (element.extra != null)
+ {
+ List columns = (List) columnStack.peek();
+ List rowContributions = (List) parent.extra;
+ // From group parent.
+ int contribution =
+ rowContributions.size() > 0 ?
+ //rowContributions != null && rowContributions.size() > 0 ?
+ ((Integer) rowContributions.get(0)).intValue() : 0;
+
+ if (rowContributions.size() > 0)
+ {
+ rowContributions.remove(0);
+ }
+
+ if (rowContributions.size() == 0) // No contributions left for next row.
+ {
+ rowContributions.add(new Integer(0));
+ }
+
+ if (((Integer) element.extra).intValue() + contribution < columns.size())
+ {
+ for
+ (
+ int i = ((Integer) element.extra).intValue();
+ i < columns.size();
+ ++i
+ )
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "synthetic",
+ "css:synthetic",
+ synthesizeColumnAttributes
+ (
+ (Attributes) columns.get(i),
+ "table-cell",
+ new String[] {"border*"}
+ )
+ );
+
+ super.endElement(Constants.CSS, "synthetic", "css:synthetic");
+ }
+ }
+ }
+ }
+
+
+
+ private static Attributes
+ synthesizeColumnAttributes
+ (
+ Attributes source,
+ String display,
+ String[] include
+ )
+ {
+ AttributesImpl atts = new AttributesImpl();
+
+ for (int i = 0; i < source.getLength(); ++i)
+ {
+ if (Util.inArray(include, source.getLocalName(i)))
+ {
+ atts.addAttribute
+ (
+ source.getURI(i),
+ source.getLocalName(i),
+ source.getQName(i),
+ source.getType(i),
+ source.getValue(i)
+ );
+ }
+ }
+
+ atts.
+ addAttribute(Constants.CSS, "display", "css:display", "CDATA", display);
+
+ return atts;
+ }
+
+
+
+ private void
+ synthesizeColumns(Element element) throws SAXException
+ {
+ if (element.children == null)
+ {
+ int span = getSpan(element.atts, "span");
+
+ for (int i = 0; i < span; ++i)
+ {
+ Attributes atts =
+ synthesizeColumnAttributes
+ (
+ element.atts,
+ "table-column",
+ i == 0 ?
+ // Take over the group border if needed.
+ new String[] {"border-left*", "text-align", "vertical-align"} :
+ (
+ i == span - 1 ?
+ new String[]
+ {"border-right*", "text-align", "vertical-align"} :
+ new String[] {"text-align", "vertical-align"}
+ )
+ );
+
+ addColumn(atts);
+ super.startElement(Constants.CSS, "synthetic", "css:synthetic", atts);
+ super.endElement(Constants.CSS, "synthetic", "css:synthetic");
+ }
+ }
+ else
+ {
+ for (int i = 0; i < element.children.size(); ++i)
+ {
+ Element child = (Element) element.children.get(i);
+ Attributes atts =
+ Util.mergeAttributes
+ (
+ element.atts,
+ child.atts,
+ i == 0 ?
+ // Take over the group border if needed.
+ new String[] {"border-left*", "text-align", "vertical-align"} :
+ (
+ i == element.children.size() - 1 ?
+ new String[]
+ {"border-right*", "text-align", "vertical-align"} :
+ new String[] {"text-align", "vertical-align"}
+ ),
+ true
+ );
+
+ addColumn(atts);
+
+ super.startElement
+ (
+ child.namespaceURI,
+ child.localName,
+ child.qName,
+ atts
+ );
+
+ super.endElement(child.namespaceURI, child.localName, child.qName);
+ }
+ }
+ }
+
+
+
+ private void
+ synthesizeGroup() throws SAXException
+ {
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute
+ (
+ Constants.CSS,
+ "display",
+ "css:display",
+ "CDATA",
+ "table-row-group"
+ );
+
+ super.startElement(Constants.CSS, "synthetic", "css:synthetic", atts);
+
+ elementStack.push
+ (
+ new Element(Constants.CSS, "synthetic", "css:synthetic", atts)
+ );
+ }
+
+} // NormalizeTableFilter
diff --git a/src/be/re/css/PageRule.java b/src/be/re/css/PageRule.java
new file mode 100644
index 0000000..b3f81d2
--- /dev/null
+++ b/src/be/re/css/PageRule.java
@@ -0,0 +1,99 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+ * Represents one CSS2 @page rule.
+ * @author Werner Donn\u00e9
+ */
+
+class PageRule
+
+{
+
+ private int position;
+ private List properties = new ArrayList();
+ private String name;
+
+
+
+ PageRule(String name, int position)
+ {
+ this.name = name;
+ this.position = position;
+ }
+
+
+
+ void
+ addProperty(Property property)
+ {
+ properties.add(property);
+ }
+
+
+
+ String
+ getName()
+ {
+ return name;
+ }
+
+
+
+ int
+ getPosition()
+ {
+ return position;
+ }
+
+
+
+ Property[]
+ getProperties()
+ {
+ return (Property[]) properties.toArray(new Property[properties.size()]);
+ }
+
+
+
+ void
+ setProperty(Property property)
+ {
+ for (Iterator i = properties.iterator(); i.hasNext();)
+ {
+ if (((Property) i.next()).getName().equals(property.getName()))
+ {
+ i.remove();
+ }
+ }
+
+ properties.add(property);
+ }
+
+
+
+ /**
+ * Splits this rule into a set of equivalent rules in which there is only one
+ * property. For each property of this rule there will be a new one.
+ */
+
+ PageRule[]
+ split()
+ {
+ PageRule[] result = new PageRule[getProperties().length];
+
+ for (int i = 0; i < result.length; ++i)
+ {
+ result[i] = new PageRule(getName(), getPosition());
+ result[i].addProperty(getProperties()[i]);
+ }
+
+ return result;
+ }
+
+} // PageRule
diff --git a/src/be/re/css/PageSetupFilter.java b/src/be/re/css/PageSetupFilter.java
new file mode 100644
index 0000000..e49dc2b
--- /dev/null
+++ b/src/be/re/css/PageSetupFilter.java
@@ -0,0 +1,1276 @@
+package be.re.css;
+
+import be.re.xml.DOMToContentHandler;
+import be.re.xml.sax.GobbleDocumentEvents;
+import be.re.xml.sax.FilterOfFilters;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.TreeSet;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This filter produces the page setup, taking into account named pages.
+ * Named page properties will be considered inside a body region element on
+ * blocks and outer tables.
+ * @author Werner Donn\u00e9
+ */
+
+class PageSetupFilter extends XMLFilterImpl
+
+{
+
+ private static final String DEFAULT_COLUMN_COUNT = "1";
+ private static final String DEFAULT_REGION_HEIGHT = "10mm";
+ private static final String DEFAULT_REGION_WIDTH = "20mm";
+
+ private static final String[][] pageInheritanceTable =
+ {
+ {"first-left-named", "unnamed", "left", "first", "named"},
+ {"first-right-named", "unnamed", "right", "first", "named"},
+ {"last-left-named", "unnamed", "left", "last", "named"},
+ {"last-right-named", "unnamed", "right", "last", "named"},
+ {"left-named", "unnamed", "left", "named"},
+ {"right-named", "unnamed", "right", "named"},
+ {"blank-left-named", "unnamed", "left", "blank", "named"},
+ {"blank-right-named", "unnamed", "right", "blank", "named"}
+ };
+ private static final String[] prefixes =
+ new String[]
+ {
+ "first-left-", "first-right-", "blank-left-", "blank-right-", "first-",
+ "blank-", "left-", "right-", "last-left-", "last-right-", "last-"
+ };
+ private static final String[][] regionInheritanceTable =
+ {
+ {
+ "first-left-named", "first-named", "first-left", "first", "left-named",
+ "left", "named", "unnamed"
+ },
+ {
+ "first-right-named", "first-named", "first-right", "first",
+ "right-named", "right", "named", "unnamed"
+ },
+ {
+ "last-left-named", "last-named", "last-left", "last", "left-named",
+ "left", "named", "unnamed"
+ },
+ {
+ "last-right-named", "last-named", "last-right", "last", "right-named",
+ "right", "named", "unnamed"
+ },
+ {"left-named", "left", "named", "unnamed"},
+ {"right-named", "right", "named", "unnamed"},
+ {
+ "blank-left-named", "blank-named", "blank-left", "blank", "left-named",
+ "left", "named", "unnamed"
+ },
+ {
+ "blank-right-named", "blank-named", "blank-right", "blank",
+ "right-named", "right", "named", "unnamed"
+ }
+ };
+
+ private URL baseUrl;
+ private Context context;
+ private boolean debug;
+ private Stack elements = new Stack();
+ private PageRule[] resolvedPageRules;
+ private Map userAgentParameters;
+
+
+
+ PageSetupFilter
+ (
+ Context context,
+ URL baseUrl,
+ Map userAgentParameters,
+ boolean debug
+ )
+ {
+ this.context = context;
+ this.baseUrl = baseUrl;
+ this.userAgentParameters = userAgentParameters;
+ this.debug = debug;
+ }
+
+
+
+ PageSetupFilter
+ (
+ Context context,
+ URL baseUrl,
+ Map userAgentParameters,
+ XMLReader parent,
+ boolean debug
+ )
+ {
+ super(parent);
+ this.context = context;
+ this.baseUrl = baseUrl;
+ this.userAgentParameters = userAgentParameters;
+ this.debug = debug;
+ }
+
+
+
+ private static List
+ addUnnamedPageRule(List pageRules)
+ {
+ boolean unnamedPresent = false;
+
+ for (Iterator i = pageRules.iterator(); i.hasNext() && !unnamedPresent;)
+ {
+ if (((PageRule) i.next()).getName().equals("unnamed"))
+ {
+ unnamedPresent = true;
+ }
+ }
+
+ if (unnamedPresent)
+ {
+ return pageRules;
+ }
+
+ PageRule rule = new PageRule("unnamed", 0);
+
+ rule.addProperty(new Property("size", "portrait", false, null));
+ pageRules.add(rule);
+
+ return pageRules;
+ }
+
+
+
+ private PageRule[]
+ applyPageRules() throws SAXException
+ {
+ if (context.pageRules.size() == 0)
+ {
+ return new PageRule[0];
+ }
+
+ PageRule[] pageRules =
+ recomposePageRules
+ (
+ sortPageRules
+ (
+ (PageRule[])
+ addUnnamedPageRule(context.pageRules).toArray(new PageRule[0])
+ )
+ );
+
+ super.
+ startElement(Constants.CSS, "pages", "css:pages", new AttributesImpl());
+
+ String[] names = getPageRuleNames(pageRules);
+
+ for (int i = 0; i < names.length; ++i)
+ {
+ generatePage
+ (
+ getPageAttributes
+ (
+ pageRules,
+ names[i],
+ getInheritanceTableEntry(pageInheritanceTable, names[i])
+ )
+ );
+ }
+
+ super.endElement(Constants.CSS, "pages", "css:pages");
+
+ return pageRules;
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ if (shouldEmitContents())
+ {
+ super.characters(ch, start, length);
+ }
+ }
+
+
+
+ private boolean
+ closeElementsInBodyRegion() throws SAXException
+ {
+ boolean closed = false;
+
+ for
+ (
+ int i = elements.size() - 1;
+ i >= 0 && ((Element) elements.get(i)).inBodyRegion;
+ --i
+ )
+ {
+ Element element = (Element) elements.get(i);
+
+ super.endElement(element.namespaceURI, element.localName, element.qName);
+ closed = true;
+ }
+
+ return closed;
+ }
+
+
+
+ private void
+ emitRegion(org.w3c.dom.Element region, String flowName)
+ throws SAXException
+ {
+ AttributesImpl atts = new AttributesImpl();
+ XMLFilterImpl filter =
+ new FilterOfFilters
+ (
+ new XMLFilter[]
+ {
+ Util.createPostProjectionFilter
+ (
+ baseUrl,
+ userAgentParameters,
+ debug
+ ).getFilter(),
+ new GobbleDocumentEvents()
+ // Give a chance for initialization, but don't interfere
+ // with the chain.
+ }
+ );
+
+ atts.addAttribute("", "flow-name", "flow-name", "CDATA", flowName);
+ filter.setContentHandler(getContentHandler());
+ filter.startDocument();
+
+ super.startElement
+ (
+ Constants.XSLFO,
+ "static-content",
+ "fo:static-content",
+ atts
+ );
+
+ DOMToContentHandler.
+ elementToContentHandler(removeWidthAndHeight(region), filter);
+ filter.endDocument();
+ super.endElement(Constants.XSLFO, "static-content", "fo:static-content");
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ endPrefixMapping("fo");
+ endPrefixMapping("css");
+ endPrefixMapping("xh");
+ endPrefixMapping("sp");
+ super.endDocument();
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ Element element = (Element) elements.pop();
+
+ if (element.inBodyRegion)
+ {
+ super.endElement(namespaceURI, localName, qName);
+
+ if (elements.isEmpty() || !((Element) elements.peek()).inBodyRegion)
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "last-page-mark",
+ "css:last-page-mark",
+ new AttributesImpl()
+ );
+
+ super.endElement(Constants.CSS, "last-page-mark", "css:last-page-mark");
+ super.endElement(Constants.CSS, "page-sequence", "css:page-sequence");
+ }
+ else
+ {
+ if (element.span && closeElementsInBodyRegion())
+ {
+ reopenElementsInBodyRegion(false);
+ }
+ }
+ }
+
+ if (elements.isEmpty())
+ {
+ super.endElement(Constants.CSS, "root", "css:root");
+ }
+ }
+
+
+
+ private static String
+ extractPseudoPrefix(String pageName)
+ {
+ for (int i = 0; i < prefixes.length; ++i)
+ {
+ if (pageName.startsWith(prefixes[i]))
+ {
+ return prefixes[i];
+ }
+ }
+
+ return "";
+ }
+
+
+
+ private void
+ generateBodyRegionExtent(Attributes pageAtts, AttributesImpl regionAtts)
+ throws SAXException
+ {
+ String columnCount = pageAtts.getValue(Constants.CSS, "column-count");
+ String columnGap = pageAtts.getValue(Constants.CSS, "column-gap");
+
+ if (columnCount == null)
+ {
+ columnCount = (String) userAgentParameters.get("column-count");
+ }
+
+ if (columnCount != null)
+ {
+ regionAtts.
+ addAttribute("", "column-count", "columnt-count", "CDATA", columnCount);
+ }
+
+ if (columnGap != null)
+ {
+ regionAtts.
+ addAttribute("", "column-gap", "columnt-gap", "CDATA", columnGap);
+ }
+
+ super.startElement
+ (
+ Constants.XSLFO,
+ "region-body",
+ "fo:region-body",
+ regionAtts
+ );
+
+ super.endElement(Constants.XSLFO, "region-body", "fo:region-body");
+ }
+
+
+
+ private void
+ generatePage(Attributes attributes) throws SAXException
+ {
+ AttributesImpl atts = new AttributesImpl(attributes);
+ AttributesImpl bodyRegionAttributes = new AttributesImpl();
+
+ moveBodyProperties(atts, bodyRegionAttributes);
+ super.startElement(Constants.CSS, "page", "css:page", atts);
+
+ List extents = new ArrayList();
+ Set generated = new HashSet();
+ String name = atts.getValue(Constants.CSS, "name");
+ String[] inheritedPages =
+ getInheritanceTableEntry(regionInheritanceTable, name);
+ String[] pages = new String[inheritedPages.length + 1];
+ String[] regionNames =
+ new String[]{"top", "bottom", "left", "right"};
+
+ System.arraycopy(inheritedPages, 0, pages, 1, inheritedPages.length);
+ pages[0] = name;
+
+ for (int i = 0; i < pages.length; ++i)
+ {
+ Map regions = (Map) context.regions.get(pages[i]);
+
+ if (regions != null)
+ {
+ for (int j = 0; j < regionNames.length; ++j)
+ {
+ if (!generated.contains(regionNames[j]))
+ {
+ org.w3c.dom.Element region =
+ (org.w3c.dom.Element) regions.get(regionNames[j]);
+
+ if (region != null)
+ {
+ generated.add(regionNames[j]);
+
+ String extent;
+
+ if
+ (
+ "top".equals(regionNames[j]) ||
+ "bottom".equals(regionNames[j])
+ )
+ {
+ extent = region.getAttributeNS(Constants.CSS, "height");
+
+ if (extent.equals(""))
+ {
+ extent = DEFAULT_REGION_HEIGHT;
+ }
+ }
+ else
+ {
+ extent = region.getAttributeNS(Constants.CSS, "width");
+
+ if (extent.equals(""))
+ {
+ extent = DEFAULT_REGION_WIDTH;
+ }
+ }
+
+ bodyRegionAttributes.addAttribute
+ (
+ "",
+ "margin-" + regionNames[j],
+ "margin-" + regionNames[j],
+ "CDATA",
+ extent
+ );
+
+ extents.add
+ (
+ generateRegionExtent(region, name, regionNames[j], extent)
+ );
+ }
+ }
+ }
+ }
+ }
+
+ // The region order matters.
+
+ generateBodyRegionExtent(atts, bodyRegionAttributes);
+
+ for (Iterator j = sortExtents(extents).iterator(); j.hasNext();)
+ {
+ DOMToContentHandler.elementToContentHandler
+ (
+ (org.w3c.dom.Element) j.next(),
+ getContentHandler()
+ );
+ }
+
+ super.endElement(Constants.CSS, "page", "css:page");
+ }
+
+
+
+ private org.w3c.dom.Element
+ generateRegionExtent
+ (
+ org.w3c.dom.Element region,
+ String page,
+ String name,
+ String extent
+ ) throws SAXException
+ {
+ String element =
+ "top".equals(name) ?
+ "region-before" :
+ (
+ "bottom".equals(name) ?
+ "region-after" :
+ ("left".equals(name) ? "region-start" : "region-end")
+ );
+ String precedence =
+ region.getAttributeNS(Constants.CSS, "precedence");
+ org.w3c.dom.Element result =
+ region.getOwnerDocument().
+ createElementNS(Constants.XSLFO, "fo:" + element);
+
+ if (!precedence.equals(""))
+ {
+ result.setAttribute("precedence", precedence);
+ }
+
+ result.setAttribute("region-name", page + "-" + name);
+ result.setAttribute("extent", extent);
+
+ return result;
+ }
+
+
+
+ private void
+ generateRegions(String page) throws SAXException
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "regions",
+ "css:regions",
+ new AttributesImpl()
+ );
+
+ if (context.regions.size() > 0)
+ {
+ Set generated = new HashSet();
+ String[] regionNames = new String[]{"top", "bottom", "left", "right"};
+
+ for (int i = 0; i < regionInheritanceTable.length; ++i)
+ {
+ for (int j = 0; j < regionInheritanceTable[i].length; ++j)
+ {
+ String specificPage =
+ getSpecificPageName(regionInheritanceTable[i][j], page);
+ Map regions = (Map) context.regions.get(specificPage);
+
+ if (regions != null)
+ {
+ for (int k = 0; k < regionNames.length; ++k)
+ {
+ String flowName =
+ getSpecificPageName(regionInheritanceTable[i][0], page) + "-" +
+ regionNames[k];
+
+ if (!generated.contains(flowName))
+ {
+ org.w3c.dom.Element region =
+ (org.w3c.dom.Element) regions.get(regionNames[k]);
+
+ if (region != null)
+ {
+ generated.add(flowName);
+ emitRegion(region, flowName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ super.endElement(Constants.CSS, "regions", "css:regions");
+ }
+
+
+
+ private static String[]
+ getInheritanceTableEntry(String[][] table, String name)
+ {
+ String symbolic = extractPseudoPrefix(name) + "named";
+ String unprefixed = stripPseudoPrefix(name);
+
+ for (int i = 0; i < table.length; ++i)
+ {
+ if (table[i][0].equals(symbolic))
+ {
+ List result = new ArrayList();
+
+ for (int j = 0; j < table[i].length - 1; ++j)
+ {
+ int index =
+ table[i][j + 1].equals("unnamed") ?
+ -1 : table[i][j + 1].indexOf("named");
+
+ if (!unprefixed.equals("unnamed") || !table[i][j + 1].equals("named"))
+ // The named page "unnamed" doesn't exist.
+ {
+ result.add
+ (
+ index != -1 ?
+ (table[i][j + 1].substring(0, index) + unprefixed) :
+ table[i][j + 1]
+ );
+ }
+ }
+
+ return (String[]) result.toArray(new String[0]);
+ }
+ }
+
+ return new String[0];
+ }
+
+
+
+ /**
+ * The page properties are determined in the order of <code>names</code>.
+ * A successor overrides the values of its predecessors. This implements the
+ * cascade.
+ */
+
+ private Attributes
+ getPageAttributes(PageRule[] pageRules, String pageName, String[] names)
+ {
+ AttributesImpl result = new AttributesImpl();
+
+ result.addAttribute(Constants.CSS, "name", "css:name", "CDATA", pageName);
+
+ for (int i = 0; i < names.length; ++i)
+ {
+ for (int j = 0; j < pageRules.length; ++j)
+ {
+ if (names[i].equals(pageRules[j].getName()))
+ {
+ Property[] properties = pageRules[j].getProperties();
+
+ for (int k = 0; k < properties.length; ++k)
+ {
+ Util.setCSSAttribute(result, properties[k], -1);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * The method expands all the style sheet specified page names into first,
+ * last, left, right, blank and any variants, which go in the repeatable page
+ * masters. Only the most precise page names remain.
+ */
+
+ private static String[]
+ getPageRuleNames(PageRule[] rules)
+ {
+ Set result = new HashSet();
+
+ for (int i = 0; i < rules.length; ++i)
+ {
+ String stripped = stripPseudoPrefix(rules[i].getName());
+
+ if (!isPseudoPageName(rules[i].getName()))
+ {
+ result.add("first-left-" + stripped);
+ result.add("first-right-" + stripped);
+ result.add("last-left-" + stripped);
+ result.add("last-right-" + stripped);
+ result.add("blank-left-" + stripped);
+ result.add("blank-right-" + stripped);
+ result.add("left-" + stripped);
+ result.add("right-" + stripped);
+ }
+ }
+
+ result.remove("first");
+ result.remove("last");
+ result.remove("blank");
+ result.remove("left");
+ result.remove("right");
+ result.remove("first-left");
+ result.remove("first-right");
+ result.remove("last-left");
+ result.remove("last-right");
+ result.remove("blank-left");
+ result.remove("blank-right");
+ result.remove("unnamed");
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+
+
+ private static String
+ getSpecificPageName(String symbolicName, String page)
+ {
+ return
+ symbolicName.indexOf("-named") != -1 ?
+ symbolicName.substring(0, symbolicName.indexOf("-named")) + "-" + page :
+ ("named".equals(symbolicName) ? page : symbolicName);
+ }
+
+
+
+ private static boolean
+ isPseudoPageName(String name)
+ {
+ return
+ Util.
+ inArray(new String[]{"first", "last", "left", "right", "blank"}, name);
+ }
+
+
+
+ private static void
+ moveBodyProperties(AttributesImpl pageAtts, AttributesImpl regionAtts)
+ {
+ for (int i = 0; i < pageAtts.getLength(); ++i)
+ {
+ if
+ (
+ Constants.CSS.equals(pageAtts.getURI(i)) &&
+ (
+ pageAtts.getLocalName(i).startsWith("background-") ||
+ pageAtts.getLocalName(i).startsWith("border-") ||
+ pageAtts.getLocalName(i).startsWith("padding-")
+ )
+ )
+ {
+ if (!pageAtts.getLocalName(i).endsWith(".conditionality"))
+ {
+ regionAtts.addAttribute
+ (
+ pageAtts.getURI(i),
+ pageAtts.getLocalName(i),
+ pageAtts.getQName(i),
+ pageAtts.getType(i),
+ pageAtts.getValue(i)
+ );
+ }
+
+ pageAtts.removeAttribute(i--);
+ }
+ }
+ }
+
+
+
+ public void
+ processingInstruction(String target, String data) throws SAXException
+ {
+ if (shouldEmitContents())
+ {
+ super.processingInstruction(target, data);
+ }
+ }
+
+
+
+ /**
+ * This performs the cascade.
+ */
+
+ private static PageRule[]
+ recomposePageRules(PageRule[] pageRules)
+ {
+ Map result = new HashMap();
+
+ for (int i = 0; i < pageRules.length; ++i)
+ {
+ PageRule pageRule = (PageRule) result.get(pageRules[i].getName());
+
+ if (pageRule == null)
+ {
+ pageRule = new PageRule(pageRules[i].getName(), 0);
+ result.put(pageRule.getName(), pageRule);
+ }
+
+ pageRule.setProperty(pageRules[i].getProperties()[0]);
+ }
+
+ return (PageRule[]) result.values().toArray(new PageRule[0]);
+ }
+
+
+
+ private static AttributesImpl
+ removeId(AttributesImpl atts)
+ {
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if ("ID".equals(atts.getType(i)))
+ {
+ atts.removeAttribute(i);
+
+ return atts;
+ }
+ }
+
+ return atts;
+ }
+
+
+
+ private static org.w3c.dom.Element
+ removeWidthAndHeight(org.w3c.dom.Element region)
+ {
+ org.w3c.dom.Element result = (org.w3c.dom.Element) region.cloneNode(true);
+
+ result.removeAttributeNS(Constants.CSS, "width");
+ result.removeAttributeNS(Constants.CSS, "height");
+
+ return result;
+ }
+
+
+
+ private void
+ reopenElementsInBodyRegion(boolean span) throws SAXException
+ {
+ int i;
+
+ for
+ (
+ i = elements.size() - 1;
+ i >= 0 && ((Element) elements.get(i)).inBodyRegion;
+ --i
+ );
+
+ if (((Element) elements.get(i + 1)).inBodyRegion)
+ {
+ for (int j = i + 1; j < elements.size(); ++j)
+ {
+ Element element = (Element) elements.get(j);
+ AttributesImpl atts = removeId(element.atts); // Avoid duplicate IDs.
+
+ if (span && j == i + 1)
+ {
+ atts = new AttributesImpl(atts);
+
+ atts.addAttribute
+ (
+ Constants.CSS,
+ "column-span",
+ "css:column-span",
+ "CDATA",
+ "all"
+ );
+ }
+
+ super.startElement
+ (
+ element.namespaceURI,
+ element.localName,
+ element.qName,
+ atts
+ );
+ }
+ }
+ }
+
+
+
+ void
+ setBaseUrl(URL baseUrl)
+ {
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ private boolean
+ shouldEmitContents()
+ {
+ return ((Element) elements.peek()).inBodyRegion;
+ }
+
+
+
+ private static Collection
+ sortExtents(Collection extents)
+ {
+ Collection result =
+ new TreeSet
+ (
+ new Comparator()
+ {
+ public int
+ compare(Object o1, Object o2)
+ {
+ String[] ordered =
+ new String[]
+ {"region-before", "region-after", "region-start", "region-end"};
+
+ return
+ Util.indexOf(ordered, ((Node) o1).getLocalName()) -
+ Util.indexOf(ordered, ((Node) o2).getLocalName());
+ }
+ }
+ );
+
+ result.addAll(extents);
+
+ return result;
+ }
+
+
+
+ private static PageRule[]
+ sortPageRules(PageRule[] pageRules)
+ {
+ Arrays.sort
+ (
+ pageRules,
+ new Comparator()
+ {
+ public int
+ compare(Object object1, Object object2)
+ {
+ PageRule rule1 = (PageRule) object1;
+ PageRule rule2 = (PageRule) object2;
+ String name1 = rule1.getName();
+ String name2 = rule2.getName();
+ int result =
+ !"unnamed".equals(name1) && "unnamed".equals(name2) ?
+ 1 :
+ ("unnamed".equals(name1) && !"unnamed".equals(name2) ? -1 : 0);
+
+ if (result == 0)
+ {
+ result =
+ "first".equals(name1) && !"first".equals(name2) ?
+ 1 : (!"first".equals(name1) && "first".equals(name2) ? -1 : 0);
+ }
+
+ if (result == 0)
+ {
+ result =
+ "last".equals(name1) && !"last".equals(name2) ?
+ 1 : (!"last".equals(name1) && "last".equals(name2) ? -1 : 0);
+ }
+
+ if (result == 0)
+ {
+ result =
+ "left".equals(name1) && !"left".equals(name2) ?
+ 1 : (!"left".equals(name1) && "left".equals(name2) ? -1 : 0);
+ }
+
+ if (result == 0)
+ {
+ result =
+ "right".equals(name1) && !"right".equals(name2) ?
+ 1 : (!"right".equals(name1) && "right".equals(name2) ? -1 : 0);
+ }
+
+ if (result == 0)
+ {
+ result = rule1.getPosition() - rule2.getPosition();
+ }
+
+ return result;
+ }
+ }
+ );
+
+ return pageRules;
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ super.startDocument();
+ startPrefixMapping("css", Constants.CSS);
+ startPrefixMapping("xh", Constants.XHTML);
+ startPrefixMapping("sp", Constants.SPECIF);
+ startPrefixMapping("fo", Constants.XSLFO);
+ getParent().setContentHandler(new Recorder());
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String display = atts.getValue(Constants.CSS, "display");
+ Element element = new Element(namespaceURI, localName, qName, atts);
+ Element parent = elements.isEmpty() ? null : (Element) elements.peek();
+
+ if (parent == null)
+ {
+ super.startElement(Constants.CSS, "root", "css:root", atts);
+ }
+
+ element.inBodyRegion =
+ (parent != null && parent.inBodyRegion) ||
+ "body".equals(atts.getValue(Constants.CSS, "region"));
+ element.inTable =
+ (parent != null && parent.inTable) || "table".equals(display);
+
+ if
+ (
+ element.inBodyRegion &&
+ (
+ parent == null ||
+ (
+ !parent.inTable &&
+ (
+ element.inTable ||
+ "block".equals(display)
+ )
+ )
+ )
+ )
+ {
+ int span = element.atts.getIndex(Constants.CSS, "column-span");
+
+ if (span != -1)
+ {
+ element.span = "all".equals(element.atts.getValue(span));
+ element.atts.removeAttribute(span);
+ }
+
+ element.pageName = atts.getValue(Constants.CSS, "page");
+
+ if ("auto".equals(element.pageName))
+ {
+ element.pageName = null;
+ }
+
+ if (element.pageName == null && parent != null)
+ {
+ element.pageName = parent.pageName;
+ }
+
+ if (element.pageName == null)
+ {
+ element.pageName = "unnamed";
+ }
+
+ boolean newPage =
+ parent == null || !element.pageName.equals(parent.pageName);
+
+ if (newPage || element.span)
+ {
+ boolean closed = closeElementsInBodyRegion();
+
+ if (newPage)
+ {
+ if (parent != null && parent.pageName != null)
+ // There is an open page sequence.
+ {
+ super.
+ endElement(Constants.CSS, "page-sequence", "css:page-sequence");
+ }
+ else
+ {
+ // Pages go before the first page sequence.
+ resolvedPageRules = applyPageRules();
+ }
+
+ AttributesImpl attributes = new AttributesImpl();
+
+ attributes.addAttribute
+ (
+ Constants.CSS,
+ "page",
+ "css:page",
+ "CDATA",
+ element.pageName
+ );
+
+ super.startElement
+ (
+ Constants.CSS,
+ "page-sequence",
+ "css:page-sequence",
+ attributes
+ );
+
+ generateRegions(element.pageName);
+ }
+
+ if (closed)
+ {
+ reopenElementsInBodyRegion(element.span);
+ }
+
+ if (parent != null)
+ {
+ parent.pageName = element.pageName;
+ }
+ }
+ }
+ else
+ {
+ if (parent != null)
+ {
+ element.pageName = parent.pageName;
+ }
+ }
+
+ if (element.inBodyRegion)
+ {
+ super.startElement(namespaceURI, localName, qName, element.atts);
+ }
+
+ elements.push(element);
+ }
+
+
+
+ private static String
+ stripPseudoPrefix(String pageName)
+ {
+ for (int i = 0; i < prefixes.length; ++i)
+ {
+ if (pageName.startsWith(prefixes[i]))
+ {
+ return pageName.substring(prefixes[i].length());
+ }
+ }
+
+ return pageName;
+ }
+
+
+
+ private static class Element
+
+ {
+
+ private AttributesImpl atts;
+ private boolean inBodyRegion;
+ private boolean inTable;
+ private String localName;
+ private String namespaceURI;
+ private String pageName;
+ private String qName;
+ private boolean span;
+
+
+ private
+ Element
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ )
+ {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ this.atts = new AttributesImpl(atts);
+ }
+
+ } // Element
+
+
+
+ private class Recorder extends XMLFilterImpl
+
+ {
+
+ private List events = new ArrayList();
+ private Stack elements = new Stack();
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ elements.pop();
+ events.add(new Event(namespaceURI, localName, qName, null));
+ }
+
+
+
+ private void
+ replayEvents() throws SAXException
+ {
+ for (int i = 0; i < events.size(); ++i)
+ {
+ Event event = (Event) events.get(i);
+
+ if (event.atts != null)
+ {
+ PageSetupFilter.this.startElement
+ (
+ event.namespaceURI,
+ event.localName,
+ event.qName,
+ event.atts
+ );
+ }
+ else
+ {
+ PageSetupFilter.this.
+ endElement(event.namespaceURI, event.localName, event.qName);
+ }
+ }
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ events.add(new Event(namespaceURI, localName, qName, atts));
+
+ if (!elements.isEmpty() && ((Element) elements.peek()).inBodyRegion)
+ {
+ replayEvents();
+ PageSetupFilter.this.getParent().
+ setContentHandler(PageSetupFilter.this);
+ }
+ else
+ {
+ Element element = new Element(namespaceURI, localName, qName, atts);
+
+ element.inBodyRegion =
+ "body".equals(atts.getValue(Constants.CSS, "region"));
+ elements.push(element);
+ }
+ }
+
+
+
+ private class Event
+
+ {
+
+ private Attributes atts;
+ private String localName;
+ private String namespaceURI;
+ private String qName;
+
+
+
+ private
+ Event
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ )
+ {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ this.atts = (atts == null ? null : new AttributesImpl(atts));
+ }
+
+ } // Event
+
+ } // Recorder
+
+} // PageSetupFilter
diff --git a/src/be/re/css/ProjectorFilter.java b/src/be/re/css/ProjectorFilter.java
new file mode 100644
index 0000000..e5968d8
--- /dev/null
+++ b/src/be/re/css/ProjectorFilter.java
@@ -0,0 +1,2264 @@
+package be.re.css;
+
+import be.re.xml.Accumulator;
+import be.re.xml.DOMToContentHandler;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.Parser;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This class implements the CSS cascading mechanism. It projects the CSS
+ * properties onto the elements in the source document according to the
+ * CSS stylesheet. They are represented as attributes in a distinct namespace.
+ * An external stylesheet can be specified with the processing instruction
+ * which is proposed in section 2.2 of the CSS2 specification.
+ *
+ * Only properties from the "all" and "print" media are considered, besides the
+ * media-neutral ones.
+ *
+ * The filter works for any XML vocabulary, but special measures have been taken
+ * for the XHTML namespace. The style attribute, for example, is recognised
+ * and applied. The XHTML methods of specifying stylesheets, such as the
+ * link and style elements, are also honored.
+ *
+ * All shorthand properties are split into their constituting parts. This makes
+ * it easier to write an XSLT stylesheet which transforms the result of this
+ * class into XSL-FO.
+ * @author Werner Donn\u00e9
+ */
+
+class ProjectorFilter extends XMLFilterImpl
+
+{
+
+ private static final String DEFAULT_CLOSE_QUOTE = "\"";
+ private static final String DEFAULT_OPEN_QUOTE = "\"";
+
+ private static final String AFTER = "after".intern();
+ private static final String BASE = "base".intern();
+ private static final String BEFORE = "before".intern();
+ private static final String BODY = "body".intern();
+ private static final String FIRST_LETTER = "first-letter".intern();
+ private static final String FIRST_LINE = "first-line".intern();
+ private static final String LINK = "link".intern();
+ private static final String STYLE = "style".intern();
+
+ private static final String[][] pageFormatTable =
+ {
+ {"armenian", "ա"},
+ {"decimal", "1"},
+ {"decimal-leading-zero", "01"},
+ {"georgian", "ა"},
+ {"hebrew", "א"},
+ {"hiragana", "あ"},
+ {"hiragana-iroha", "い"},
+ {"katakana", "ア"},
+ {"katakana-iroha", "イ"},
+ {"lower-alpha", "a"},
+ {"lower-greek", "α"},
+ {"lower-latin", "a"},
+ {"lower-roman", "i"},
+ {"upper-alpha", "A"},
+ {"upper-latin", "A"},
+ {"upper-roman", "I"},
+ };
+
+ private URL baseUrl = null;
+ private boolean collectStyleSheet = false;
+ private Compiled compiled = new Compiled();
+ private Context context;
+ private Stack counters = new Stack();
+ private Stack elements = new Stack();
+ private String embeddedStyleSheet = "";
+ private int lastRulePosition = 0;
+ private Matcher matcher = null;
+ private Stack namedStrings = new Stack();
+ private int quoteDepth = 0;
+ // Filter state because quotes can match across the hole document.
+ private Map userAgentParameters;
+ private URL userAgentStyleSheet = null;
+
+
+
+ ProjectorFilter(Context context)
+ {
+ this(null, null, new HashMap(), context);
+ }
+
+
+
+ ProjectorFilter
+ (
+ URL baseUrl,
+ URL userAgentStyleSheet,
+ Map userAgentParameters,
+ Context context
+ )
+ {
+ this.baseUrl = baseUrl;
+ this.userAgentStyleSheet =
+ userAgentStyleSheet != null ?
+ userAgentStyleSheet : getClass().getResource("style/ua.css");
+ this.userAgentParameters = userAgentParameters;
+ this.context = context;
+ }
+
+
+
+ private static void
+ addFOMarker(Node parent, String name, String value)
+ {
+ org.w3c.dom.Element element =
+ parent.getOwnerDocument().createElementNS(Constants.CSS, "css:fo-marker");
+
+ element.appendChild(parent.getOwnerDocument().createTextNode(value));
+ element.setAttributeNS(Constants.CSS, "css:name", name);
+ parent.insertBefore(element, parent.getFirstChild());
+ }
+
+
+
+ private Rule[]
+ appendStyleAttributeRules
+ (
+ Rule[] matchingRules,
+ Attributes atts,
+ String namespaceURI
+ ) throws SAXException
+ {
+ if (Constants.XHTML != namespaceURI)
+ {
+ return matchingRules;
+ }
+
+ String style = atts.getValue("style");
+
+ if (style == null || "".equals(style))
+ {
+ return matchingRules;
+ }
+
+ try
+ {
+ Rule[] rules = getStyleAttributeRules(style);
+ Rule[] result = new Rule[matchingRules.length + rules.length];
+
+ System.arraycopy(matchingRules, 0, result, 0, matchingRules.length);
+ System.arraycopy(rules, 0, result, matchingRules.length, rules.length);
+
+ return result;
+ }
+
+ catch (IOException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+
+
+ private void
+ applyContentProperty(Element element, Rule[] pseudoRules) throws SAXException
+ {
+ boolean seen = false;
+
+ // From most to least specific.
+
+ for (int i = pseudoRules.length - 1; i >= 0 && !seen; --i)
+ {
+ Property[] properties = pseudoRules[i].getProperties();
+
+ for (int j = 0; j < properties.length && !seen; ++j)
+ {
+ if
+ (
+ "content".equals(properties[j].getName()) &&
+ properties[j].getLexicalUnit().getLexicalUnitType() !=
+ LexicalUnit.SAC_INHERIT
+ )
+ {
+ seen = true;
+
+ for
+ (
+ LexicalUnit k = properties[j].getLexicalUnit();
+ k != null;
+ k = k.getNextLexicalUnit()
+ )
+ {
+ switch (k.getLexicalUnitType())
+ {
+ case LexicalUnit.SAC_ATTR:
+ serializeAttrFunction(k, element, properties[j].getPrefixMap());
+ break;
+
+ case LexicalUnit.SAC_COUNTER_FUNCTION:
+ serializeCounterFunction(k);
+ break;
+
+ case LexicalUnit.SAC_COUNTERS_FUNCTION:
+ serializeCountersFunction(k);
+ break;
+
+ case LexicalUnit.SAC_FUNCTION:
+ serializeFunction(k, element, properties[j].getPrefixMap());
+ break;
+
+ case LexicalUnit.SAC_IDENT:
+ serializeQuote(k, element);
+ break;
+
+ case LexicalUnit.SAC_STRING_VALUE:
+ serializeString(k.getStringValue());
+ break;
+
+ case LexicalUnit.SAC_URI:
+ serializeUriFunction(k);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ private void
+ addFirstLetterMarker(Element element)
+ {
+ Rule[] pseudoRules =
+ selectPseudoRules(element.matchingPseudoRules, FIRST_LETTER);
+
+ if (pseudoRules.length > 0)
+ {
+ element.appliedAttributes.addAttribute
+ (
+ Constants.CSS,
+ "has-first-letter",
+ "css:has-first-letter",
+ "CDATA",
+ "1"
+ );
+ }
+ }
+
+
+
+ private void
+ applyPseudoRules(Element element, String name)
+ throws SAXException
+ {
+ Rule[] pseudoRules =
+ selectPseudoRules(element.matchingPseudoRules, name);
+
+ if (pseudoRules.length > 0)
+ {
+ AttributesImpl extra = new AttributesImpl();
+
+ if (AFTER == name || BEFORE == name)
+ {
+ extra.addAttribute
+ (
+ Constants.CSS,
+ "display",
+ "css:display",
+ "CDATA",
+ "inline"
+ );
+ }
+
+ AttributesImpl attributes = setCSSAttributes(pseudoRules, extra);
+ AttributesImpl changeBarAttributes =
+ splitChangeBarAttributes(attributes);
+
+ if (BEFORE == name)
+ {
+ serializeChangeBarBegin(changeBarAttributes);
+ }
+
+ super.startElement(Constants.CSS, name, "css:" + name, attributes);
+
+ if (AFTER == name || BEFORE == name)
+ {
+ serializeFOMarkers(pseudoRules);
+ applyContentProperty(element, pseudoRules);
+ }
+
+ super.endElement(Constants.CSS, name, "css:" + name);
+
+ if (AFTER == name)
+ {
+ serializeChangeBarEnd(changeBarAttributes);
+ }
+ }
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ if (collectStyleSheet)
+ {
+ embeddedStyleSheet += new String(ch, start, length);
+ }
+
+ super.characters(ch, start, length);
+ }
+
+
+
+ private static String
+ convertPageFormat(String listStyle)
+ {
+ for (int i = 0; i < pageFormatTable.length; ++i)
+ {
+ if (listStyle.equals(pageFormatTable[i][0]))
+ {
+ return pageFormatTable[i][1];
+ }
+ }
+
+ return "1"; // Decimal is the default.
+ }
+
+
+
+ private static void
+ detectMarkers(Element element)
+ {
+ boolean hasMarkers = false;
+
+ for (int i = 0; i < element.matchingPseudoRules.length && !hasMarkers; ++i)
+ {
+ hasMarkers =
+ (
+ element.matchingPseudoRules[i].getPseudoElementName() == BEFORE ||
+ element.matchingPseudoRules[i].getPseudoElementName() == AFTER
+ ) &&
+ "display".equals
+ (
+ element.matchingPseudoRules[i].getProperty().getName()
+ ) &&
+ "marker".equals
+ (
+ element.matchingPseudoRules[i].getProperty().getValue()
+ );
+ }
+
+ if (hasMarkers)
+ {
+ element.appliedAttributes.addAttribute
+ (
+ Constants.CSS,
+ "has-markers",
+ "css:has-markers",
+ "CDATA",
+ "1"
+ );
+ }
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ endPrefixMapping("css");
+ endPrefixMapping("sp");
+ super.endDocument();
+ matcher.endDocument();
+ reset();
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ counters.pop();
+ namedStrings.pop();
+ matcher.endElement(namespaceURI, localName, qName);
+
+ if (collectStyleSheet)
+ {
+ collectStyleSheet = false;
+
+ try
+ {
+ parseStyleSheet(new StringReader(embeddedStyleSheet), 0, true);
+ }
+
+ catch (IOException e)
+ {
+ throw new SAXException(e);
+ }
+
+ embeddedStyleSheet = "";
+ }
+
+ Element element = (Element) elements.pop();
+
+ applyPseudoRules(element, AFTER);
+ super.endElement(element.namespaceURI, element.localName, element.qName);
+ element.matchingElementRules = null;
+ element.matchingPseudoRules = null;
+ element.appliedAttributes = null;
+
+ if (element.floating)
+ {
+ super.endElement(Constants.CSS, "float", "css:float");
+ }
+ }
+
+
+
+ private static String
+ evaluateAttrFunction
+ (
+ LexicalUnit function,
+ Attributes attributes,
+ Map prefixMap
+ )
+ {
+ if (function.getStringValue() == null)
+ {
+ return "";
+ }
+
+ String attribute = function.getStringValue();
+ int index = attribute.indexOf('|');
+ String value =
+ index == -1 ?
+ attributes.getValue(attribute) :
+ attributes.getValue
+ (
+ (String) prefixMap.get(attribute.substring(0, index)),
+ attribute.substring(index + 1)
+ );
+
+ return value != null ? value : "";
+ }
+
+
+
+ private String
+ evaluateCounterFunction(LexicalUnit function)
+ {
+ if (function.getParameters() == null)
+ {
+ return "";
+ }
+
+ String counter = function.getParameters().getStringValue();
+
+ if (counter == null)
+ {
+ return "";
+ }
+
+ Integer value =
+ (Integer) findCounterScope(counter).get(counter.toLowerCase());
+ String listStyle = getCounterListStyle(function);
+
+ return
+ value != null && !"none".equalsIgnoreCase(listStyle) &&
+ !"inherit".equalsIgnoreCase(listStyle) ?
+ getCounterString(value.intValue(), listStyle) : "";
+ }
+
+
+
+ private String
+ evaluateCountersFunction(LexicalUnit function) throws SAXException
+ {
+ if (function.getParameters() == null)
+ {
+ return "";
+ }
+
+ String counter = function.getParameters().getStringValue();
+
+ if (counter == null)
+ {
+ return "";
+ }
+
+ LexicalUnit parameter =
+ function.getParameters().getNextLexicalUnit().getNextLexicalUnit();
+
+ if (parameter == null)
+ {
+ return "";
+ }
+
+ String separator = parameter.getStringValue();
+
+ if (separator == null)
+ {
+ return "";
+ }
+
+ String listStyle = getCountersListStyle(parameter);
+
+ return
+ !"none".equalsIgnoreCase(listStyle) &&
+ !"inherit".equalsIgnoreCase(listStyle) ?
+ getCountersString(counter.toLowerCase(), separator, listStyle) : "";
+ }
+
+
+
+ private static String
+ evaluateQuote(LexicalUnit quote, Element element, int[] quoteDepth)
+ {
+ if (quote.getStringValue().equals("open-quote"))
+ {
+ String s = selectOpenQuote(element, quoteDepth[0]);
+
+ ++quoteDepth[0];
+
+ return s;
+ }
+
+ if (quote.getStringValue().equals("close-quote"))
+ {
+ --quoteDepth[0];
+
+ String s = selectCloseQuote(element, quoteDepth[0]);
+
+ return s;
+ }
+
+ if (quote.getStringValue().equals("no-open-quote"))
+ {
+ ++quoteDepth[0];
+ }
+ else
+ {
+ if (quote.getStringValue().equals("no-close-quote"))
+ {
+ --quoteDepth[0];
+ }
+ }
+
+ return "";
+ }
+
+
+
+ private String
+ evaluateStringFunction(LexicalUnit function) throws SAXException
+ {
+ if (function.getParameters() == null)
+ {
+ return "";
+ }
+
+ String name = function.getParameters().getStringValue().toLowerCase();
+ String value = (String) findNamedStringScope(name).get(name);
+
+ return value != null ? value : "";
+ }
+
+
+
+ private Map
+ findCounterScope(String counter)
+ {
+ return findScope(counters, counter, new Integer(0));
+ }
+
+
+
+ private Map
+ findNamedStringScope(String namedString)
+ {
+ return findScope(namedStrings, namedString, "");
+ }
+
+
+
+ private static Map
+ findScope(Stack scopes, String item, Object defaultValue)
+ {
+ for (int i = scopes.size() - 1; i >= 0; --i)
+ {
+ if (((Map) scopes.get(i)).containsKey(item))
+ {
+ return (Map) scopes.get(i);
+ }
+ }
+
+ // The highest scope is the default scope.
+
+ ((Map) scopes.get(0)).put(item, defaultValue);
+
+ return (Map) scopes.get(0);
+ }
+
+
+
+ public URL
+ getBaseUrl()
+ {
+ return baseUrl;
+ }
+
+
+
+ private static String
+ getCounterListStyle(LexicalUnit function)
+ {
+ return
+ function.getParameters().getNextLexicalUnit() != null &&
+ function.getParameters().getNextLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_OPERATOR_COMMA ?
+ function.getParameters().getNextLexicalUnit().getNextLexicalUnit().
+ getStringValue() :
+ "decimal";
+ }
+
+
+
+ private static String
+ getCountersListStyle(LexicalUnit function)
+ {
+ return
+ function.getNextLexicalUnit() != null &&
+ function.getNextLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_OPERATOR_COMMA ?
+ function.getNextLexicalUnit().getNextLexicalUnit().getStringValue() :
+ "decimal";
+ }
+
+
+
+ private static String
+ getCounterString(int value, String listStyle)
+ {
+ if (listStyle.equals("circle"))
+ {
+ return "\u25cb";
+ }
+
+ if (listStyle.equals("disc"))
+ {
+ return "\u2022";
+ }
+
+ if (listStyle.equals("square"))
+ {
+ return "\u25a0";
+ }
+
+ if (listStyle.equals("decimal-leading-zero"))
+ {
+ return (value < 10 ? "0" : "") + String.valueOf(value);
+ }
+
+ if (listStyle.equals("lower-alpha") || listStyle.equals("lower-latin"))
+ {
+ return String.valueOf((char) (value + 96));
+ }
+
+ if (listStyle.equals("upper-alpha") || listStyle.equals("upper-latin"))
+ {
+ return String.valueOf((char) (value + 64));
+ }
+
+ if (listStyle.equals("lower-greek"))
+ {
+ return String.valueOf((char) (value + 944));
+ }
+
+ if (listStyle.equals("lower-roman"))
+ {
+ return Util.toRoman(value).toLowerCase();
+ }
+
+ if (listStyle.equals("upper-roman"))
+ {
+ return Util.toRoman(value);
+ }
+
+ if (listStyle.equals("footnote"))
+ {
+ return Util.toFootnote(value);
+ }
+
+ return String.valueOf(value); // decimal
+ }
+
+
+
+ private String
+ getCountersString(String counter, String separator, String listStyle)
+ {
+ String result = "";
+
+ for (Iterator i = counters.iterator(); i.hasNext();)
+ {
+ Map scope = (Map) i.next();
+ Integer value = (Integer) scope.get(counter);
+
+ if (value != null)
+ {
+ result +=
+ (result.equals("") ? "" : separator) +
+ getCounterString(value.intValue(), listStyle);
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private static String
+ getElementContents(Node node)
+ {
+ return
+ node == null ?
+ "" :
+ (
+ (
+ node instanceof Text ?
+ ((Text) node).getData() :
+ (
+ node instanceof org.w3c.dom.Element &&
+ (
+ !Constants.CSS.equals(node.getNamespaceURI()) ||
+ !(
+ BEFORE.equals(node.getLocalName()) ||
+ AFTER.equals(node.getLocalName())
+ )
+ ) ? getElementContents(node.getFirstChild()) : ""
+ )
+ ) + getElementContents(node.getNextSibling())
+ );
+ }
+
+
+
+ private static LexicalUnit
+ getQuotePair(LexicalUnit unit, int quoteDepth)
+ {
+ for
+ (
+ int i = 0;
+ i < quoteDepth && unit.getNextLexicalUnit().getNextLexicalUnit() != null;
+ ++i
+ )
+ {
+ unit = unit.getNextLexicalUnit().getNextLexicalUnit();
+ }
+
+ return unit;
+ }
+
+
+
+ private static String[]
+ getSetNamedStringNames(Rule[] rules)
+ {
+ Set result = new HashSet();
+
+ for (int i = 0; i < rules.length; ++i)
+ {
+ Property[] properties = rules[i].getProperties();
+
+ for (int j = 0; j < properties.length; ++j)
+ {
+ if
+ (
+ "string-set".equals(properties[j].getName()) &&
+ properties[j].getLexicalUnit() != null &&
+ properties[j].getLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_IDENT &&
+ !"none".equalsIgnoreCase
+ (
+ properties[j].getLexicalUnit().getStringValue()
+ )
+ )
+ {
+ if
+ (
+ !hasContentsIdentifier // Those are serialized differently.
+ (
+ properties[j].getLexicalUnit().getNextLexicalUnit()
+ )
+ )
+ {
+ result.add
+ (
+ properties[j].getLexicalUnit().getStringValue().toLowerCase()
+ );
+ }
+ else
+ {
+ // In case there is a rule earlier in the cascade that doesn't
+ // have "contents" as the value for the named string.
+
+ result.remove
+ (
+ properties[j].getLexicalUnit().getStringValue().toLowerCase()
+ );
+ }
+ }
+ }
+ }
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+
+
+ private Rule[]
+ getStyleAttributeRules(String style) throws IOException, SAXException
+ {
+ final List rules = new ArrayList();
+
+ try
+ {
+ parseStyleSheet
+ (
+ null,
+ new StringReader("dummy{" + style + "}"),
+ new RuleCollector.RuleEmitter()
+ {
+ public void
+ addRule(Rule rule)
+ {
+ rules.add(rule);
+ }
+ },
+ new ArrayList(),
+ 0,
+ false
+ );
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new UndeclaredThrowableException(e); // Would be a bug.
+ }
+
+ return (Rule[]) rules.toArray(new Rule[0]);
+ }
+
+
+
+ public URL
+ getUserAgentStyleSheet()
+ {
+ return userAgentStyleSheet;
+ }
+
+
+
+ private void
+ handleControlInformation
+ (
+ String namespaceURI,
+ String localName,
+ AttributesImpl atts
+ ) throws SAXException
+ {
+ try
+ {
+ if (atts.getValue("xml:base") != null)
+ {
+ ((Element) elements.peek()).baseUrl =
+ new URL(atts.getValue("xml:base"));
+ }
+
+ setXMLIDType(atts);
+
+ if (Constants.XHTML == namespaceURI)
+ {
+ if (BASE == localName)
+ {
+ if (atts.getValue("href") != null)
+ {
+ URL base = new URL(atts.getValue("href"));
+
+ for (int i = 0; i < elements.size(); ++i)
+ {
+ ((Element) elements.get(i)).baseUrl = base;
+ }
+ }
+ }
+ else
+ {
+ if (BODY == localName)
+ {
+ URL base = ((Element) elements.peek()).baseUrl;
+
+ if (base != null)
+ // Make sure the BASE is translated for the rest of the chain.
+ {
+ Util.
+ setAttribute(atts, "", "xml:base", "xml:base", base.toString());
+ }
+ }
+ else
+ {
+ if (LINK == localName)
+ {
+ if (isMatchingStyleSheet(atts) && atts.getValue("href") != null)
+ {
+ parseStyleSheet(atts.getValue("href"), 0, true);
+ }
+ }
+ else
+ {
+ if (STYLE == localName && isMatchingStyleSheet(atts))
+ {
+ collectStyleSheet = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ catch (Exception e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+
+
+ private void
+ handleFloats(Element element)
+ throws SAXException
+ {
+ int floating = element.appliedAttributes.getIndex(Constants.CSS, "float");
+
+ if (floating == -1)
+ {
+ return;
+ }
+
+ if (!"none".equals(element.appliedAttributes.getValue(floating)))
+ {
+ AttributesImpl floatAttributes = new AttributesImpl();
+
+ floatAttributes.addAttribute
+ (
+ Constants.CSS,
+ "float",
+ "css:float",
+ "CDATA",
+ element.appliedAttributes.getValue(floating)
+ );
+
+ super.startElement(Constants.CSS, "float", "css:float", floatAttributes);
+ element.floating = true;
+ }
+
+ element.appliedAttributes.removeAttribute(floating);
+ }
+
+
+
+ private void
+ handleGraphics(Element element) throws SAXException
+ {
+ if
+ (
+ "graphic".
+ equals(element.appliedAttributes.getValue(Constants.CSS, "display"))
+ )
+ {
+ String url = Util.getIndirectValue(element.appliedAttributes, "src");
+
+ if (url != null)
+ {
+ try
+ {
+ element.appliedAttributes.setValue
+ (
+ element.appliedAttributes.getIndex(Constants.CSS, "src"),
+ element.baseUrl != null ?
+ new URL(element.baseUrl, url).toString() : url
+ );
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+ }
+ }
+
+
+
+ private static boolean
+ hasContentsIdentifier(LexicalUnit unit)
+ {
+ return
+ unit == null ?
+ false :
+ (
+ unit.getLexicalUnitType() == LexicalUnit.SAC_IDENT &&
+ "contents".equalsIgnoreCase(unit.getStringValue()) ? true :
+ hasContentsIdentifier(unit.getNextLexicalUnit())
+ );
+ }
+
+
+
+ private void
+ incrementCounter(Property counterIncrement, boolean display)
+ {
+ for
+ (
+ LexicalUnit i = counterIncrement.getLexicalUnit();
+ i != null;
+ i = i.getNextLexicalUnit()
+ )
+ {
+ if (i.getLexicalUnitType() == LexicalUnit.SAC_IDENT)
+ {
+ String counter = i.getStringValue().toLowerCase();
+
+ if (display || "page".equals(counter))
+ {
+ Map scope = findCounterScope(i.getStringValue());
+
+ scope.put
+ (
+ counter,
+ new Integer
+ (
+ ((Integer) scope.get(i.getStringValue())).intValue() +
+ (
+ i.getNextLexicalUnit() != null &&
+ i.getNextLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_INTEGER ?
+ i.getNextLexicalUnit().getIntegerValue() : 1
+ )
+ )
+ );
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * The installed accumulator catches region elements processed by this
+ * filter and saves them.
+ */
+
+ private void
+ installRegionAccumulator() throws SAXException
+ {
+ Accumulator.postAccumulate
+ (
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(org.w3c.dom.Element element, XMLFilter filter)
+ throws SAXException
+ {
+ String pageName =
+ element.getAttributeNS(Constants.CSS, "page");
+
+ if (pageName.equals("") || pageName.equals("auto"))
+ {
+ pageName = "unnamed";
+ }
+
+ element.setAttributeNS(Constants.CSS, "css:page", pageName);
+
+ Map regionsForPage = (Map) context.regions.get(pageName);
+
+ if (regionsForPage == null)
+ {
+ regionsForPage = new HashMap();
+ context.regions.put(pageName, regionsForPage);
+ }
+
+ regionsForPage.
+ put(element.getAttributeNS(Constants.CSS, "region"), element);
+ }
+ }
+ );
+ }
+
+
+
+ private void
+ installStringSetAccumulator
+ (
+ final String name,
+ final String value,
+ final Map scope
+ ) throws SAXException
+ {
+ Accumulator.postAccumulate
+ (
+ this,
+ new Accumulator.ProcessElement()
+ {
+ public void
+ process(org.w3c.dom.Element element, XMLFilter filter)
+ throws SAXException
+ {
+ String result =
+ MessageFormat.format
+ (
+ value,
+ new Object[]{getElementContents(element.getFirstChild())}
+ );
+
+ scope.put(name, result);
+ addFOMarker(element, name, result);
+
+ DOMToContentHandler.elementToContentHandler
+ (
+ element,
+ filter.getContentHandler()
+ );
+ }
+ }
+ );
+ }
+
+
+
+ private static boolean
+ isMatchingStyleSheet(Attributes atts)
+ {
+ if
+ (
+ !"text/css".equals(atts.getValue("type")) ||
+ (
+ atts.getValue("rel") != null &&
+ !"stylesheet".equalsIgnoreCase(atts.getValue("rel").trim())
+ )
+ )
+ {
+ return false;
+ }
+
+ if (atts.getValue("media") == null)
+ {
+ return true;
+ }
+
+ StringTokenizer tokenizer =
+ new StringTokenizer(atts.getValue("media"), ",");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken().trim().toLowerCase();
+
+ if ("all".equals(token) || "print".equals(token))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ private static boolean
+ isStaticRegion(Attributes atts)
+ {
+ String region = atts.getValue(Constants.CSS, "region");
+
+ return
+ region != null && !"body".equalsIgnoreCase(region) &&
+ !"none".equalsIgnoreCase(region);
+ }
+
+
+
+ private void
+ parseStyleSheet(String uri, int offset, boolean resetMatcher)
+ throws CSSException, MalformedURLException, IOException, SAXException
+ {
+ parseStyleSheet(uri, null, offset, resetMatcher);
+ }
+
+
+
+ private void
+ parseStyleSheet(Reader reader, int offset, boolean resetMatcher)
+ throws CSSException, IOException, SAXException
+ {
+ try
+ {
+ parseStyleSheet(null, reader, offset, resetMatcher);
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new UndeclaredThrowableException(e); // Would be a bug.
+ }
+ }
+
+
+
+ private void
+ parseStyleSheet(String uri, Reader reader, int offset, boolean resetMatcher)
+ throws CSSException, MalformedURLException, IOException, SAXException
+ {
+ parseStyleSheet
+ (
+ uri,
+ reader,
+ new RuleCollector.RuleEmitter()
+ {
+ public void
+ addRule(Rule rule)
+ {
+ compiled.addRule(rule);
+ }
+ },
+ context.pageRules,
+ offset,
+ resetMatcher
+ );
+ }
+
+
+
+ private void
+ parseStyleSheet
+ (
+ String uri,
+ Reader reader,
+ RuleCollector.RuleEmitter ruleEmitter,
+ List pageRules,
+ int offset,
+ boolean resetMatcher
+ ) throws CSSException, MalformedURLException, IOException, SAXException
+ {
+ URL base =
+ !elements.isEmpty() && ((Element) elements.peek()).baseUrl != null ?
+ ((Element) elements.peek()).baseUrl : baseUrl;
+ Parser parser = Util.getSacParser();
+ InputSource source =
+ reader != null ? new InputSource(reader) : new InputSource();
+
+ source.setURI
+ (
+ base != null && uri != null ?
+ new URL(base, uri).toString() :
+ (base != null ? base.toString() : uri)
+ );
+
+ RuleCollector collector =
+ new RuleCollector
+ (
+ ruleEmitter,
+ pageRules,
+ source.getURI() != null ? new URL(source.getURI()) : null,
+ lastRulePosition,
+ offset
+ );
+
+ parser.setDocumentHandler(collector);
+ parser.parseStyleSheet(source);
+ lastRulePosition = collector.getCurrentPosition();
+
+ if (resetMatcher)
+ {
+ setMatcher();
+ }
+ }
+
+
+
+ public void
+ processingInstruction(String target, String data) throws SAXException
+ {
+ if ("xml-stylesheet".equalsIgnoreCase(target))
+ {
+ StringTokenizer tokenizer = new StringTokenizer(data, " ");
+ String type = null;
+ String uri = null;
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (token.startsWith("type="))
+ {
+ type = token.substring(token.indexOf('=') + 1);
+ }
+ else
+ {
+ if (token.startsWith("href="))
+ {
+ uri = token.substring(token.indexOf('=') + 1);
+ }
+ }
+ }
+
+ if
+ (
+ uri != null &&
+ uri.length() > 2 &&
+ type != null &&
+ type.length() > 2 &&
+ type.substring(1, type.length() - 1).equals("text/css")
+ )
+ {
+ try
+ {
+ parseStyleSheet(uri.substring(1, uri.length() - 1), 0, true);
+ }
+
+ catch (Exception e)
+ {
+ throw new SAXException(e);
+ }
+ }
+ }
+
+ super.processingInstruction(target, data);
+ }
+
+
+
+ private void
+ repositionMatcher() throws SAXException
+ {
+ matcher.startDocument();
+
+ // The "/" element is of no concern.
+
+ for (int i = 1; i < elements.size() - 1; ++i)
+ {
+ Element element = (Element) elements.get(i);
+
+ matcher.startElement
+ (
+ element.namespaceURI,
+ element.localName,
+ element.qName,
+ element.attributes
+ );
+ }
+ }
+
+
+
+ private void
+ reset()
+ {
+ context.pageRules.clear();
+ compiled = new Compiled();
+ matcher = null;
+ collectStyleSheet = false;
+ embeddedStyleSheet = "";
+ elements.clear();
+ counters.clear();
+ namedStrings.clear();
+ context.regions.clear();
+ }
+
+
+
+ private void
+ resetCounter(Property counterReset, boolean display)
+ {
+ for
+ (
+ LexicalUnit i = counterReset.getLexicalUnit();
+ i != null;
+ i = i.getNextLexicalUnit()
+ )
+ {
+ if (i.getLexicalUnitType() == LexicalUnit.SAC_IDENT)
+ {
+ String counter = i.getStringValue().toLowerCase();
+
+ if (display || "page".equals(counter))
+ {
+ ((Map) counters.peek()).put
+ (
+ counter,
+ new Integer
+ (
+ i.getNextLexicalUnit() != null &&
+ i.getNextLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_INTEGER ?
+ i.getNextLexicalUnit().getIntegerValue() : 0
+ )
+ );
+ }
+ }
+ }
+ }
+
+
+
+ private static String
+ selectCloseQuote(Element element, int quoteDepth)
+ {
+ if (element.quotes == null)
+ {
+ return DEFAULT_CLOSE_QUOTE;
+ }
+
+ return
+ getQuotePair(element.quotes, quoteDepth).getNextLexicalUnit().
+ getStringValue();
+ }
+
+
+
+ private static String
+ selectOpenQuote(Element element, int quoteDepth)
+ {
+ if (element.quotes == null)
+ {
+ return DEFAULT_OPEN_QUOTE;
+ }
+
+ return getQuotePair(element.quotes, quoteDepth).getStringValue();
+ }
+
+
+
+ /**
+ * Maintains the order.
+ */
+
+ private static Rule[]
+ selectPseudoRules(Rule[] rules, String pseudoElementName)
+ {
+ List result = new ArrayList();
+
+ for (int i = 0; i < rules.length; ++i)
+ {
+ if (pseudoElementName == rules[i].getPseudoElementName())
+ {
+ result.add(rules[i]);
+ }
+ }
+
+ return (Rule[]) result.toArray(new Rule[0]);
+ }
+
+
+
+ private void
+ serializeAttrFunction(LexicalUnit unit, Element element, Map prefixMap)
+ throws SAXException
+ {
+ String value =
+ evaluateAttrFunction(unit, element.attributes, prefixMap);
+
+ super.characters(value.toCharArray(), 0, value.length());
+ }
+
+
+
+ private void
+ serializeChangeBarBegin(Attributes attributes) throws SAXException
+ {
+ if (attributes.getIndex("change-bar-class") != -1)
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "change-bar-begin",
+ "css:change-bar-begin",
+ attributes
+ );
+
+ super.
+ endElement(Constants.CSS, "change-bar-begin", "css:change-bar-begin");
+ }
+ }
+
+
+
+ private void
+ serializeChangeBarEnd(Attributes attributes) throws SAXException
+ {
+ int index = attributes.getIndex("change-bar-class");
+
+ if (index != -1)
+ {
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute
+ (
+ attributes.getURI(index),
+ attributes.getLocalName(index),
+ attributes.getQName(index),
+ attributes.getType(index),
+ attributes.getValue(index)
+ );
+
+ super.startElement
+ (
+ Constants.CSS,
+ "change-bar-end",
+ "css:change-bar-end",
+ atts
+ );
+
+ super.
+ endElement(Constants.CSS, "change-bar-end", "css:change-bar-end");
+ }
+ }
+
+
+
+ private void
+ serializeCounterFunction(LexicalUnit unit) throws SAXException
+ {
+ if (unit.getParameters() == null)
+ {
+ return;
+ }
+
+ String counter = unit.getParameters().getStringValue().toLowerCase();
+
+ if ("page".equals(counter)) // Special synthetic counter.
+ {
+ serializePageNumber(getCounterListStyle(unit));
+ }
+ else
+ {
+ if ("pages".equals(counter)) // Special synthetic counter.
+ {
+ serializePagesTotal();
+ }
+ else
+ {
+ String value = evaluateCounterFunction(unit);
+
+ super.characters(value.toCharArray(), 0, value.length());
+ }
+ }
+ }
+
+
+
+ private void
+ serializeCountersFunction(LexicalUnit unit) throws SAXException
+ {
+ String value = evaluateCountersFunction(unit);
+
+ super.characters(value.toCharArray(), 0, value.length());
+ }
+
+
+
+ private void
+ serializeFOMarkers(Rule[] rules) throws SAXException
+ {
+ String[] names = getSetNamedStringNames(rules);
+
+ for (int i = 0; i < names.length; ++i)
+ {
+ String value =
+ (String) findNamedStringScope(names[i]).get(names[i]);
+
+ if (value != null)
+ {
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute(Constants.CSS, "name", "css:name", "CDATA", names[i]);
+ super.startElement(Constants.CSS, "fo-marker", "css:fo-marker", atts);
+ super.characters(value.toCharArray(), 0, value.length());
+ super.endElement(Constants.CSS, "fo-marker", "css:fo-marker");
+ }
+ }
+ }
+
+
+
+ private void
+ serializeFunction(LexicalUnit unit, Element element, Map prefixMap)
+ throws SAXException
+ {
+ if ("string".equalsIgnoreCase(unit.getFunctionName()))
+ {
+ serializeStringFunction(unit);
+ }
+ else
+ {
+ if ("page-ref".equalsIgnoreCase(unit.getFunctionName()))
+ {
+ serializePageRefFunction(unit, element, prefixMap);
+ }
+ }
+ }
+
+
+
+ private void
+ serializeQuote(LexicalUnit unit, Element element) throws SAXException
+ {
+ int[] quoteDepthReference = new int[] {quoteDepth};
+ String value = evaluateQuote(unit, element, quoteDepthReference);
+
+ quoteDepth = quoteDepthReference[0];
+ super.characters(value.toCharArray(), 0, value.length());
+ }
+
+
+
+ private void
+ serializePageNumber(String listStyle) throws SAXException
+ {
+ AttributesImpl attributes = new AttributesImpl();
+
+ attributes.addAttribute
+ (
+ Constants.CSS,
+ "format",
+ "css:format",
+ "CDATA",
+ convertPageFormat(listStyle)
+ );
+
+ if
+ (
+ listStyle.equals("armenian") ||
+ listStyle.equals("georgian") ||
+ listStyle.equals("hebrew")
+ )
+ {
+ attributes.addAttribute
+ (
+ Constants.CSS,
+ "letter-value",
+ "css:letter-value",
+ "CDATA",
+ "traditional"
+ );
+ }
+
+ super.
+ startElement(Constants.CSS, "page-number", "css:page-number", attributes);
+ super.endElement(Constants.CSS, "page-number", "css:page-number");
+ }
+
+
+
+ private void
+ serializePageRefFunction(LexicalUnit unit, Element element, Map prefixMap)
+ throws SAXException
+ {
+ if (unit.getParameters() == null)
+ {
+ return;
+ }
+
+ String value =
+ unit.getParameters().getLexicalUnitType() == LexicalUnit.SAC_ATTR ?
+ evaluateAttrFunction
+ (
+ unit.getParameters(),
+ element.attributes,
+ prefixMap
+ ) : element.attributes.getValue(unit.getParameters().getStringValue());
+
+ if (value == null)
+ {
+ return;
+ }
+
+ AttributesImpl attributes = new AttributesImpl();
+
+ attributes.
+ addAttribute(Constants.CSS, "ref-id", "css:ref-id", "CDATA", value);
+ super.startElement(Constants.CSS, "page-ref", "css:page-ref", attributes);
+ super.endElement(Constants.CSS, "page-ref", "css:page-ref");
+ }
+
+
+
+ private void
+ serializePagesTotal() throws SAXException
+ {
+ super.startElement
+ (
+ Constants.CSS,
+ "pages-total",
+ "css:pages-total",
+ new AttributesImpl()
+ );
+
+ super.endElement(Constants.CSS, "pages-total", "css:pages-total");
+ }
+
+
+
+ private void
+ serializeString(String s) throws SAXException
+ {
+ int position = 0;
+
+ for (int i = s.indexOf('\n'); i != -1; i = s.indexOf(position, '\n'))
+ {
+ super.characters(s.substring(position, i).toCharArray(), 0, i - position);
+
+ super.startElement
+ (
+ Constants.CSS,
+ "newline",
+ "css:newline",
+ new AttributesImpl()
+ );
+
+ super.endElement(Constants.CSS, "newline", "css:newline");
+ position = i + 1;
+ }
+
+ if (position < s.length())
+ {
+ super.characters
+ (
+ s.substring(position).toCharArray(),
+ 0,
+ s.length() - position
+ );
+ }
+ }
+
+
+
+ private void
+ serializeStringFunction(LexicalUnit unit) throws SAXException
+ {
+ if (unit.getParameters() == null)
+ {
+ return;
+ }
+
+ AttributesImpl atts = new AttributesImpl();
+ String name = unit.getParameters().getStringValue();
+
+ if (name == null)
+ {
+ return;
+ }
+
+ atts.addAttribute(Constants.CSS, "name", "css:name", "CDATA", name);
+
+ super.startElement
+ (
+ Constants.CSS,
+ "retrieve-fo-marker",
+ "css:retrieve-fo-marker",
+ atts
+ );
+
+ super.
+ endElement(Constants.CSS, "retrieve-fo-marker", "css:retrieve-fo-marker");
+ }
+
+
+
+ private void
+ serializeUriFunction(LexicalUnit unit) throws SAXException
+ {
+ if (unit.getStringValue() == null)
+ {
+ return;
+ }
+
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute
+ (
+ Constants.CSS,
+ "href",
+ "css:href",
+ "CDATA",
+ "url(" + unit.getStringValue() + ")"
+ );
+
+ super.startElement(Constants.CSS, "external", "css:external", atts);
+ super.endElement(Constants.CSS, "external", "css:external");
+ }
+
+
+
+ public void
+ setBaseUrl(URL baseUrl)
+ {
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ /**
+ * This method produces CSS attributes according to the matching rules. It
+ * also has a side effect in that it adjusts the counters and named strings.
+ * This was done in order to scan the matching rules only once.
+ */
+
+ private AttributesImpl
+ setCSSAttributes(Rule[] matchingRules, Attributes attributes)
+ throws SAXException
+ {
+ Property counterIncrement = null;
+ Property counterReset = null;
+ boolean displayNone = false;
+ AttributesImpl result = new AttributesImpl(attributes);
+ Property stringSet = null;
+
+ // From least to most specific.
+
+ for (int i = 0; i < matchingRules.length; ++i)
+ {
+ Property property = matchingRules[i].getProperties()[0];
+ String propertyName = property.getName();
+
+ if (propertyName.equals("counter-increment"))
+ {
+ counterIncrement = property;
+ }
+ else
+ {
+ if (propertyName.equals("counter-reset"))
+ {
+ counterReset = property;
+ }
+ else
+ {
+ if (propertyName.equals("string-set"))
+ {
+ if (matchingRules[i].getPseudoElementName() == null)
+ {
+ stringSet = property;
+ }
+ }
+ else
+ {
+ Util.setCSSAttribute
+ (
+ result,
+ property,
+ matchingRules[i].getSpecificity()
+ );
+
+ if
+ (
+ propertyName.equals("display") &&
+ "none".equalsIgnoreCase(property.getValue())
+ )
+ {
+ displayNone = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (counterReset != null)
+ {
+ resetCounter(counterReset, !displayNone);
+ }
+
+ if (counterIncrement != null)
+ {
+ incrementCounter(counterIncrement, !displayNone);
+ }
+
+ if (stringSet != null && !displayNone)
+ {
+ setNamedString(stringSet);
+ }
+
+ return result;
+ }
+
+
+
+ private void
+ setMatcher() throws SAXException
+ {
+ compiled.generateDFA();
+ matcher = new Matcher(compiled);
+ repositionMatcher();
+ }
+
+
+
+ private void
+ setNamedString(Property stringSet) throws SAXException
+ {
+ if
+ (
+ stringSet.getLexicalUnit() == null ||
+ stringSet.getLexicalUnit().getLexicalUnitType() !=
+ LexicalUnit.SAC_IDENT ||
+ "none".
+ equalsIgnoreCase(stringSet.getLexicalUnit().getStringValue())
+ )
+ {
+ return;
+ }
+
+ boolean needContents = false;
+ String name =
+ stringSet.getLexicalUnit().getStringValue().toLowerCase();
+ String result = "";
+ Map scope = findNamedStringScope(name);
+
+ for
+ (
+ LexicalUnit i = stringSet.getLexicalUnit().getNextLexicalUnit();
+ i != null;
+ i = i.getNextLexicalUnit()
+ )
+ {
+ switch (i.getLexicalUnitType())
+ {
+ case LexicalUnit.SAC_ATTR:
+ result +=
+ evaluateAttrFunction
+ (
+ i,
+ ((Element) elements.peek()).attributes,
+ stringSet.getPrefixMap()
+ );
+ break;
+
+ case LexicalUnit.SAC_COUNTER_FUNCTION:
+ result += evaluateCounterFunction(i);
+ break;
+
+ case LexicalUnit.SAC_COUNTERS_FUNCTION:
+ result += evaluateCountersFunction(i);
+ break;
+
+ case LexicalUnit.SAC_FUNCTION:
+ if ("string".equalsIgnoreCase(i.getFunctionName()))
+ {
+ result += evaluateStringFunction(i);
+ }
+ break;
+
+ case LexicalUnit.SAC_IDENT:
+ if ("contents".equalsIgnoreCase(i.getStringValue()))
+ {
+ result += "{0}";
+ needContents = true;
+ }
+ else
+ {
+ result += evaluateQuote(i, (Element) elements.peek(), new int[1]);
+ // Local evaluation.
+ }
+ break;
+
+ case LexicalUnit.SAC_STRING_VALUE:
+ result += i.getStringValue();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!needContents)
+ {
+ scope.put(name, result);
+ }
+ else
+ {
+ installStringSetAccumulator(name, result, scope);
+ }
+ }
+
+
+
+ private void
+ setQuotes()
+ {
+ Element element = (Element) elements.peek();
+
+ for (int i = 0; i < element.matchingElementRules.length; ++i)
+ {
+ Property[] properties =
+ element.matchingElementRules[i].getProperties();
+
+ if (properties[0].getName().equals("quotes"))
+ {
+ element.quotes = properties[0].getLexicalUnit();
+ }
+ }
+
+ if
+ (
+ element.quotes == null ||
+ element.quotes.getLexicalUnitType() == LexicalUnit.SAC_INHERIT
+ )
+ {
+ element.quotes = ((Element) elements.get(elements.size() - 2)).quotes;
+ }
+ }
+
+
+
+ public void
+ setUserAgentStyleSheet(URL userAgentStyleSheet)
+ {
+ this.userAgentStyleSheet = userAgentStyleSheet;
+ }
+
+
+
+ private static void
+ setXMLIDType(AttributesImpl atts)
+ {
+ int index = atts.getIndex("xml:id");
+
+ if (index != -1 && !"ID".equals(atts.getType(index)))
+ {
+ atts.setType(index, "ID");
+ }
+ }
+
+
+
+ /**
+ * Removes the change-bar attributes from <code>attributes</code> and returns
+ * those that are in the CSS namespace. They are returned without a
+ * namespace however.
+ */
+
+ private static AttributesImpl
+ splitChangeBarAttributes(AttributesImpl attributes)
+ {
+ AttributesImpl result = new AttributesImpl();
+
+ for (int i = 0; i < attributes.getLength(); ++i)
+ {
+ if (attributes.getLocalName(i).startsWith("change-bar-"))
+ {
+ if (Constants.CSS.equals(attributes.getURI(i)))
+ {
+ result.addAttribute
+ (
+ "",
+ attributes.getLocalName(i),
+ attributes.getLocalName(i),
+ attributes.getType(i),
+ attributes.getValue(i)
+ );
+ }
+
+ attributes.removeAttribute(i--);
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ reset();
+
+ try
+ {
+ parseStyleSheet(new StringReader("*{display: inline}"), -2, true);
+
+ String htmlHeaderMark =
+ (String) userAgentParameters.get("html-header-mark");
+
+ if (htmlHeaderMark != null)
+ {
+ parseStyleSheet
+ (
+ new StringReader(htmlHeaderMark + "{string-set: component contents}"),
+ -2,
+ true
+ );
+ }
+
+ parseStyleSheet(userAgentStyleSheet.toString(), -1, true);
+ }
+
+ catch (Exception e)
+ {
+ throw new SAXException(e);
+ }
+
+ Element root = new Element("", "/", "/");
+
+ root.baseUrl = baseUrl;
+ elements.push(root);
+ counters.push(new HashMap());
+ namedStrings.push(new HashMap());
+ super.startDocument();
+ startPrefixMapping("css", Constants.CSS);
+ startPrefixMapping("sp", Constants.SPECIF);
+ }
+
+
+
+ /**
+ * The string arguments are interned.
+ */
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ if (namespaceURI != null)
+ {
+ namespaceURI = namespaceURI.intern();
+ }
+
+ if (localName != null)
+ {
+ localName = localName.intern();
+ }
+
+ if (qName != null)
+ {
+ qName = qName.intern();
+ }
+
+ Element element = new Element(namespaceURI, localName, qName);
+
+ element.baseUrl = ((Element) elements.peek()).baseUrl;
+ elements.push(element);
+ element.attributes = new AttributesImpl(atts);
+ // Must be copied because atts might be recuperated by the parser.
+ handleControlInformation(namespaceURI, localName, element.attributes);
+ matcher.startElement(namespaceURI, localName, qName, element.attributes);
+
+ element.matchingElementRules =
+ appendStyleAttributeRules(matcher.matchingRules(), atts, namespaceURI);
+ element.matchingPseudoRules = matcher.matchingPseudoRules();
+ setQuotes();
+
+ element.appliedAttributes =
+ setCSSAttributes(element.matchingElementRules, element.attributes);
+ handleFloats(element);
+ handleGraphics(element);
+ detectMarkers(element);
+
+ if (isStaticRegion(element.appliedAttributes))
+ {
+ installRegionAccumulator();
+ }
+
+ addFirstLetterMarker(element);
+ translateId(element.appliedAttributes);
+ super.
+ startElement(namespaceURI, localName, qName, element.appliedAttributes);
+ serializeFOMarkers(element.matchingElementRules);
+ applyPseudoRules(element, FIRST_LETTER);
+ applyPseudoRules(element, BEFORE);
+ applyPseudoRules(element, FIRST_LINE);
+ counters.push(new HashMap());
+ namedStrings.push(new HashMap());
+ }
+
+
+
+ private static void
+ translateId(AttributesImpl atts)
+ {
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if ("ID".equals(atts.getType(i)))
+ {
+ atts.setAttribute
+ (
+ i,
+ Constants.XML,
+ "id",
+ "xml:id",
+ "ID",
+ atts.getValue(i)
+ );
+ }
+ }
+ }
+
+
+
+ private static class Element
+
+ {
+
+ private AttributesImpl appliedAttributes;
+ private AttributesImpl attributes = new AttributesImpl();
+ private URL baseUrl;
+ private boolean floating = false;
+ private String localName;
+ private Rule[] matchingElementRules = null;
+ private Rule[] matchingPseudoRules = null;
+ private String namespaceURI;
+ private String qName;
+ private LexicalUnit quotes = null;
+
+
+
+ private
+ Element(String namespaceURI, String localName, String qName)
+ {
+ this.namespaceURI = namespaceURI;
+ this.localName = localName;
+ this.qName = qName;
+ }
+
+ } // Element
+
+} // ProjectorFilter
diff --git a/src/be/re/css/Property.java b/src/be/re/css/Property.java
new file mode 100644
index 0000000..a5449cd
--- /dev/null
+++ b/src/be/re/css/Property.java
@@ -0,0 +1,708 @@
+package be.re.css;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import org.w3c.css.sac.LexicalUnit;
+
+
+
+/**
+ * Represents a CSS2 property. It also implements splitting of shorthand
+ * properties into basic properties.
+ * @author Werner Donn\u00e9
+ */
+
+public class Property
+
+{
+
+ private static final String[] BACKGROUND_ATTACHMENT = {"fixed", "scroll"};
+ private static final String[] BACKGROUND_REPEAT =
+ {"repeat", "repeat-x", "repeat-y", "no-repeat"};
+ private static final String[] BORDER_STYLE =
+ {
+ "none", "hidden", "dotted", "dashed", "solid", "double", "groove",
+ "ridge", "inset", "outset"
+ };
+ private static final String[] COLORS =
+ {
+ "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon",
+ "navy", "olive", "purple", "red", "silver", "teal", "white", "yellow",
+ "transparent"
+ };
+ private static final String[] FONT_SIZE =
+ {
+ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large",
+ "larger", "smaller"
+ };
+ private static final String[] FONT_STYLE = {"normal", "italic", "oblique"};
+ private static final String[] FONT_VARIANT = {"normal", "small-caps"};
+ private static final String[] FONT_WEIGHT =
+ {"normal", "bold", "bolder", "lighter"};
+ private static final String[] LIST_STYLE_POSITION = {"inside", "outside"};
+ private static final String[] LIST_STYLE_TYPE =
+ {
+ "disc", "circle", "square", "decimal", "decimal-leading-zero",
+ "lower-roman", "upper-roman", "lower-greek", "lower-alpha",
+ "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian",
+ "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
+ "katakana-iroha", "none"
+ };
+ private static final String[] SYSTEM_FONTS =
+ {"caption", "icon", "menu", "message-box", "small-caption", "status-bar"};
+
+ private URL baseUrl;
+ private boolean important;
+ private String name;
+ private Map prefixMap;
+ private LexicalUnit value;
+ private String valueAsString;
+
+
+
+ /**
+ * Is not used for shorthand properties.
+ */
+
+ Property(String name, String value, boolean important, Map prefixMap)
+ {
+ this.name = name;
+ this.valueAsString = value;
+ this.important = important;
+ this.prefixMap = prefixMap;
+ }
+
+
+
+ public
+ Property
+ (
+ String name,
+ LexicalUnit value,
+ boolean important,
+ Map prefixMap,
+ URL baseUrl
+ )
+ {
+ this.name = name;
+ this.value = value;
+ this.valueAsString =
+ Util.lexicalUnitToString(value, !"font-family".equals(name), baseUrl);
+ this.important = important;
+ this.prefixMap = prefixMap;
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ private Property
+ copy(String name, String value)
+ {
+ return new Property(name, value, getImportant(), getPrefixMap());
+ }
+
+
+
+ public boolean
+ getImportant()
+ {
+ return important;
+ }
+
+
+
+ public String
+ getName()
+ {
+ return name;
+ }
+
+
+
+ public LexicalUnit
+ getLexicalUnit()
+ {
+ return value;
+ }
+
+
+
+ public Map
+ getPrefixMap()
+ {
+ return prefixMap;
+ }
+
+
+
+ public String
+ getValue()
+ {
+ return valueAsString;
+ }
+
+
+
+ private boolean
+ isFontSize(LexicalUnit unit)
+ {
+ int type = unit.getLexicalUnitType();
+
+ return
+ type == LexicalUnit.SAC_CENTIMETER ||
+ type == LexicalUnit.SAC_EM ||
+ type == LexicalUnit.SAC_EX ||
+ type == LexicalUnit.SAC_INCH ||
+ type == LexicalUnit.SAC_MILLIMETER ||
+ type == LexicalUnit.SAC_PERCENTAGE ||
+ type == LexicalUnit.SAC_PICA ||
+ type == LexicalUnit.SAC_PIXEL ||
+ type == LexicalUnit.SAC_POINT ||
+ Util.inArray(FONT_SIZE, Util.lexicalUnitAtomLower(unit, baseUrl));
+ }
+
+
+
+ private static boolean
+ isFontWeight(String atom)
+ {
+ try
+ {
+ return
+ Util.inArray(FONT_WEIGHT, atom) ||
+ (Integer.parseInt(atom) >= 100 && Integer.parseInt(atom) <= 900);
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ private Property[]
+ setAtoms(String[] names, String[] atoms)
+ {
+ Property[] result = new Property[names.length];
+
+ for (int i = 0; i < names.length; ++i)
+ {
+ result[i] = copy(names[i], atoms[i]);
+ }
+
+ return result;
+ }
+
+
+
+ public Property[]
+ split()
+ {
+ StringTokenizer tokenizer = new StringTokenizer(getName(), "-");
+ String name = "split";
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ name += token.substring(0, 1).toUpperCase() + token.substring(1);
+ }
+
+ try
+ {
+ return
+ (Property[])
+ getClass().getDeclaredMethod(name, new Class[0]).
+ invoke(this, new Object[0]);
+ }
+
+ catch (IllegalAccessException e)
+ {
+ throw new UndeclaredThrowableException(e);
+ }
+
+ catch (InvocationTargetException e)
+ {
+ throw new UndeclaredThrowableException(e);
+ }
+
+ catch (NoSuchMethodException e)
+ {
+ return new Property[] {this};
+ }
+ }
+
+
+
+ private Property[]
+ splitBackground()
+ {
+ List result = new ArrayList();
+ String remaining = null;
+
+ for (LexicalUnit i = value; i != null; i = i.getNextLexicalUnit())
+ {
+ String atom = Util.lexicalUnitAtomLower(i, baseUrl);
+
+ if (i.getLexicalUnitType() == LexicalUnit.SAC_URI)
+ {
+ result.add(copy("background-image", atom));
+ }
+ else
+ {
+ if
+ (
+ i.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR ||
+ Util.inArray(COLORS, atom)
+ )
+ {
+ result.add(copy("background-color", atom));
+ }
+ else
+ {
+ if (Util.inArray(BACKGROUND_ATTACHMENT, atom))
+ {
+ result.add(copy("background-attachment", atom));
+ }
+ else
+ {
+ if (Util.inArray(BACKGROUND_REPEAT, atom))
+ {
+ result.add(copy("background-repeat", atom));
+ }
+ else
+ {
+ remaining = remaining == null ? atom : (remaining + " " + atom);
+ }
+ }
+ }
+ }
+ }
+
+ if (remaining != null)
+ {
+ result.add(copy("background-position", remaining));
+ }
+
+ return (Property[]) result.toArray(new Property[result.size()]);
+ }
+
+
+
+ private Property[]
+ splitBorder()
+ {
+ List result = new ArrayList();
+ String remaining = null;
+
+ for (LexicalUnit i = value; i != null; i = i.getNextLexicalUnit())
+ {
+ String atom = Util.lexicalUnitAtomLower(i, baseUrl);
+
+ if
+ (
+ i.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR ||
+ Util.inArray(COLORS, atom) ||
+ atom.equals("transparant")
+ )
+ {
+ result.add(copy("border-top-color", atom));
+ result.add(copy("border-right-color", atom));
+ result.add(copy("border-bottom-color", atom));
+ result.add(copy("border-left-color", atom));
+ }
+ else
+ {
+ if (Util.inArray(BORDER_STYLE, atom))
+ {
+ result.add(copy("border-top-style", atom));
+ result.add(copy("border-right-style", atom));
+ result.add(copy("border-bottom-style", atom));
+ result.add(copy("border-left-style", atom));
+ }
+ else
+ {
+ remaining = remaining == null ? atom : (remaining + " " + atom);
+ }
+ }
+ }
+
+ if (remaining != null)
+ {
+ result.add(copy("border-top-width", remaining));
+ result.add(copy("border-bottom-width", remaining));
+ result.add(copy("border-left-width", remaining));
+ result.add(copy("border-right-width", remaining));
+ result.add(copy("border-after-width.conditionality", "retain"));
+ result.add(copy("border-before-width.conditionality", "retain"));
+ }
+
+ return (Property[]) result.toArray(new Property[result.size()]);
+ }
+
+
+
+ private Property[]
+ splitBorderBottom()
+ {
+ return splitBorderSide("bottom");
+ }
+
+
+
+ private Property[]
+ splitBorderBottomWidth()
+ {
+ Property[] result = new Property[2];
+
+ result[0] = copy(getName(), getValue());
+ result[1] = copy("border-after-width.conditionality", "retain");
+
+ return result;
+ }
+
+
+
+ private Property[]
+ splitBorderColor()
+ {
+ return splitFourWays(getName(), getImportant());
+ }
+
+
+
+ private Property[]
+ splitBorderLeft()
+ {
+ return splitBorderSide("left");
+ }
+
+
+
+ private Property[]
+ splitBorderRight()
+ {
+ return splitBorderSide("right");
+ }
+
+
+
+ private Property[]
+ splitBorderStyle()
+ {
+ return splitFourWays(getName(), getImportant());
+ }
+
+
+
+ private Property[]
+ splitBorderTop()
+ {
+ return splitBorderSide("top");
+ }
+
+
+
+ private Property[]
+ splitBorderSide(String side)
+ {
+ List result = new ArrayList();
+ String remaining = null;
+
+ for (LexicalUnit i = value; i != null; i = i.getNextLexicalUnit())
+ {
+ String atom = Util.lexicalUnitAtomLower(i, baseUrl);
+
+ if
+ (
+ i.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR ||
+ Util.inArray(COLORS, atom) ||
+ atom.equals("transparant")
+ )
+ {
+ result.add(copy("border-" + side + "-color", atom));
+ }
+ else
+ {
+ if (Util.inArray(BORDER_STYLE, atom))
+ {
+ result.add(copy("border-" + side + "-style", atom));
+ }
+ else
+ {
+ remaining = remaining == null ? atom : (remaining + " " + atom);
+ }
+ }
+ }
+
+ if (remaining != null)
+ {
+ result.add(copy("border-" + side + "-width", remaining));
+
+ if ("top".equals(side))
+ {
+ result.add(copy("border-before-width.conditionality", "retain"));
+ }
+ else
+ {
+ if ("bottom".equals(side))
+ {
+ result.add(copy("border-after-width.conditionality", "retain"));
+ }
+ }
+ }
+
+ return (Property[]) result.toArray(new Property[result.size()]);
+ }
+
+
+
+ private Property[]
+ splitBorderTopWidth()
+ {
+ Property[] result = new Property[2];
+
+ result[0] = copy(getName(), getValue());
+ result[1] = copy("border-before-width.conditionality", "retain");
+
+ return result;
+ }
+
+
+
+ private Property[]
+ splitBorderWidth()
+ {
+ Property[] values = splitFourWays(getName(), getImportant());
+ Property[] result = new Property[values.length + 2];
+
+ System.arraycopy(values, 0, result, 0, values.length);
+ result[values.length] = copy("border-after-width.conditionality", "retain");
+ result[values.length + 1] =
+ copy("border-before-width.conditionality", "retain");
+
+ return result;
+ }
+
+
+
+ private Property[]
+ splitFont()
+ {
+ List result = new ArrayList();
+ String remaining = null;
+
+ for (LexicalUnit i = value; i != null; i = i.getNextLexicalUnit())
+ {
+ String originalAtom = Util.lexicalUnitAtom(i, baseUrl);
+ String atom = originalAtom.toLowerCase();
+
+ if (Util.inArray(SYSTEM_FONTS, atom))
+ {
+ result.add(copy("font", atom));
+ }
+ else
+ {
+ if (Util.inArray(FONT_STYLE, atom))
+ {
+ result.add(copy("font-style", atom));
+ }
+ else
+ {
+ if (Util.inArray(FONT_VARIANT, atom))
+ {
+ result.add(copy("font-variant", atom));
+ }
+ else
+ {
+ if (isFontWeight(atom))
+ {
+ result.add(copy("font-weight", atom));
+ }
+ else
+ {
+ if
+ (
+ (
+ i.getPreviousLexicalUnit() == null ||
+ i.getPreviousLexicalUnit().getLexicalUnitType() !=
+ LexicalUnit.SAC_OPERATOR_SLASH
+ ) &&
+ isFontSize(i)
+ )
+ {
+ result.add(copy("font-size", atom));
+ }
+ else
+ {
+ if (i.getLexicalUnitType() == LexicalUnit.SAC_OPERATOR_SLASH)
+ {
+ i = i.getNextLexicalUnit();
+
+ result.add
+ (
+ copy("line-height", Util.lexicalUnitAtomLower(i, baseUrl))
+ );
+ }
+ else
+ {
+ remaining =
+ remaining == null ?
+ originalAtom : (remaining + " " + originalAtom);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (remaining != null)
+ {
+ result.add(copy("font-family", remaining));
+ }
+
+ return (Property[]) result.toArray(new Property[result.size()]);
+ }
+
+
+
+ private Property[]
+ splitFourWays(String name, boolean important)
+ {
+ String[] names = splitFourWaysNames(name);
+ LexicalUnit[] units = Util.lexicalUnitArray(value);
+
+ if (units.length == 1)
+ {
+ String atom = Util.lexicalUnitAtomLower(units[0], baseUrl);
+
+ return setAtoms(names, new String[] {atom, atom, atom, atom});
+ }
+
+ if (units.length == 2)
+ {
+ String atom1 = Util.lexicalUnitAtomLower(units[0], baseUrl);
+ String atom2 = Util.lexicalUnitAtomLower(units[1], baseUrl);
+
+ return setAtoms(names, new String[] {atom1, atom2, atom1, atom2});
+ }
+
+ if (units.length == 3)
+ {
+ String atom1 = Util.lexicalUnitAtomLower(units[0], baseUrl);
+ String atom2 = Util.lexicalUnitAtomLower(units[1], baseUrl);
+ String atom3 = Util.lexicalUnitAtomLower(units[2], baseUrl);
+
+ return setAtoms(names, new String[] {atom1, atom2, atom3, atom2});
+ }
+
+ if (units.length == 4)
+ {
+ String atom1 = Util.lexicalUnitAtomLower(units[0], baseUrl);
+ String atom2 = Util.lexicalUnitAtomLower(units[1], baseUrl);
+ String atom3 = Util.lexicalUnitAtomLower(units[2], baseUrl);
+ String atom4 = Util.lexicalUnitAtomLower(units[3], baseUrl);
+
+ return setAtoms(names, new String[] {atom1, atom2, atom3, atom4});
+ }
+
+ return new Property[] {this};
+ }
+
+
+
+ private static String[]
+ splitFourWaysNames(String name)
+ {
+ String[] result = new String[4];
+ StringTokenizer tokenizer = new StringTokenizer(name, "-");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (result[0] == null)
+ {
+ result[0] = token + "-top";
+ result[1] = token + "-right";
+ result[2] = token + "-bottom";
+ result[3] = token + "-left";
+ }
+ else
+ {
+ for (int i = 0; i < result.length; ++i)
+ {
+ result[i] += "-" + token;
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private Property[]
+ splitListStyle()
+ {
+ List result = new ArrayList();
+
+ for (LexicalUnit i = value; i != null; i = i.getNextLexicalUnit())
+ {
+ String atom = Util.lexicalUnitAtomLower(i, baseUrl);
+
+ if (atom.equals("none"))
+ {
+ result.add(copy("list-style-type", atom));
+ result.add(copy("list-style-image", atom));
+ }
+ else
+ {
+ if (i.getLexicalUnitType() == LexicalUnit.SAC_URI)
+ {
+ result.add(copy("list-style-image", atom));
+ }
+ else
+ {
+ if (Util.inArray(LIST_STYLE_POSITION, atom))
+ {
+ result.add(copy("list-style-position", atom));
+ }
+ else
+ {
+ if (Util.inArray(LIST_STYLE_TYPE, atom))
+ {
+ result.add(copy("list-style-type", atom));
+ }
+ }
+ }
+ }
+ }
+
+ return (Property[]) result.toArray(new Property[result.size()]);
+ }
+
+
+
+ private Property[]
+ splitMargin()
+ {
+ return splitFourWays(getName(), getImportant());
+ }
+
+
+
+ private Property[]
+ splitPadding()
+ {
+ return splitFourWays(getName(), getImportant());
+ }
+
+} // Property
diff --git a/src/be/re/css/Rule.java b/src/be/re/css/Rule.java
new file mode 100644
index 0000000..6a043ee
--- /dev/null
+++ b/src/be/re/css/Rule.java
@@ -0,0 +1,413 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.w3c.css.sac.AttributeCondition;
+import org.w3c.css.sac.CombinatorCondition;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.ConditionalSelector;
+import org.w3c.css.sac.DescendantSelector;
+import org.w3c.css.sac.ElementSelector;
+import org.w3c.css.sac.NegativeSelector;
+import org.w3c.css.sac.PositionalCondition;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SiblingSelector;
+
+
+
+/**
+ * Represents one CSS2 rule.
+ * @author Werner Donn\u00e9
+ */
+
+public class Rule
+
+{
+
+ private Property[] cachedArray = null;
+ private String elementName;
+ private int position;
+ private Map properties = new HashMap();
+ private String pseudoElementName;
+ private Selector selector;
+ private Selector[] selectorChain;
+ private int specificity;
+
+
+
+ /**
+ * Use values like -1, 0 and +1 for <code>offset</code>. This will shift the
+ * specificity up or down, which is needed to account for the style sheet
+ * source.
+ */
+
+ Rule(Selector selector, int position, int offset)
+ {
+ this.selector =
+ selector instanceof ElementSelector ?
+ new InternedElementSelector((ElementSelector) selector) : selector;
+ this.position = position;
+ selectorChain = Util.getSelectorChain(selector);
+ specificity = specificity() + offset * 10000000;
+ elementName = getElementName(selectorChain, selectorChain.length - 1);
+ pseudoElementName = getPseudoElementName(selectorChain);
+
+ if (elementName != null)
+ {
+ elementName = elementName.intern();
+ }
+
+ if (pseudoElementName != null)
+ {
+ pseudoElementName = pseudoElementName.intern();
+ }
+ }
+
+
+
+ /**
+ * The created rule physically shares the selector and specificity
+ * information. This makes it possible to match a set of rules resulting
+ * after a split by picking only one of them.
+ */
+
+ private
+ Rule(Rule source, int position)
+ {
+ this.selector = source.selector;
+ this.position = position;
+ this.elementName = source.elementName;
+ this.pseudoElementName = source.pseudoElementName;
+ this.selectorChain = source.selectorChain;
+ this.specificity = source.specificity;
+ }
+
+
+
+ void
+ addProperty(Property property)
+ {
+ properties.put(property.getName(), property);
+ }
+
+
+
+ /**
+ * Returns the interned name of the element this rule applies to. If it
+ * doesn't apply to an element <code>null</code> is returned.
+ */
+
+ public String
+ getElementName()
+ {
+ return elementName;
+ }
+
+
+
+ private static String
+ getElementName(Selector[] selectorChain, int position)
+ {
+ switch (selectorChain[position].getSelectorType())
+ {
+ case Selector.SAC_ELEMENT_NODE_SELECTOR:
+ return
+ ((ElementSelector) selectorChain[position]).
+ getLocalName();
+ case Selector.SAC_PSEUDO_ELEMENT_SELECTOR:
+ return getElementName(selectorChain, position - 1);
+
+ default: return null;
+ }
+ }
+
+
+
+ int
+ getPosition()
+ {
+ return position;
+ }
+
+
+
+ Property[]
+ getProperties()
+ {
+ if (cachedArray == null || cachedArray.length != properties.size())
+ {
+ cachedArray =
+ (Property[])
+ properties.values().toArray(new Property[properties.size()]);
+ }
+
+ return cachedArray;
+ }
+
+
+
+ public Property
+ getProperty()
+ {
+ Property[] result = getProperties();
+
+ if (result.length != 1)
+ {
+ throw new RuntimeException("Unsplit rule");
+ }
+
+ return result[0];
+ }
+
+
+
+ private static void
+ getPseudoClassConditions(Condition c, List result)
+ {
+ if (c.getConditionType() == Condition.SAC_PSEUDO_CLASS_CONDITION)
+ {
+ result.add(((AttributeCondition) c).getValue());
+ }
+ else
+ {
+ if (c.getConditionType() == Condition.SAC_AND_CONDITION)
+ {
+ getPseudoClassConditions
+ (
+ ((CombinatorCondition) c).getFirstCondition(),
+ result
+ );
+
+ getPseudoClassConditions
+ (
+ ((CombinatorCondition) c).getSecondCondition(),
+ result
+ );
+ }
+ }
+ }
+
+
+
+ /**
+ * Returns the interned pseudo element name or <code>null</code> if the rule
+ * doesn't apply to a pseudo element.
+ */
+
+ public String
+ getPseudoElementName()
+ {
+ return pseudoElementName;
+ }
+
+
+
+ private static String
+ getPseudoElementName(Selector[] selectorChain)
+ {
+ if
+ (
+ selectorChain[selectorChain.length - 1].getSelectorType() ==
+ Selector.SAC_PSEUDO_ELEMENT_SELECTOR
+ )
+ {
+ return
+ ((ElementSelector) selectorChain[selectorChain.length - 1]).
+ getLocalName();
+ }
+
+ if
+ (
+ selectorChain.length > 1 &&
+ selectorChain[selectorChain.length - 2].getSelectorType() ==
+ Selector.SAC_CONDITIONAL_SELECTOR
+ )
+ {
+ List conditions = new ArrayList();
+
+ getPseudoClassConditions
+ (
+ ((ConditionalSelector) selectorChain[selectorChain.length - 2]).
+ getCondition(),
+ conditions
+ );
+
+ return
+ conditions.contains("before") ?
+ "before" :
+ (
+ conditions.contains("after") ?
+ "after" :
+ (conditions.contains("first-line") ? "first-line" : null)
+ );
+ }
+
+ return null;
+ }
+
+
+
+ /**
+ * Returns the selector that matches the rule.
+ */
+
+ public Selector
+ getSelector()
+ {
+ return selector;
+ }
+
+
+
+ /**
+ * Flattens the selector expression tree in infix order.
+ */
+
+ Selector[]
+ getSelectorChain()
+ {
+ return selectorChain;
+ }
+
+
+
+ int
+ getSpecificity()
+ {
+ return specificity;
+ }
+
+
+
+ private int
+ specificity()
+ {
+ Specificity s = new Specificity();
+
+ specificity(selector, s);
+
+ return 10000 * s.ids + 100 * s.attributes + s.names;
+ }
+
+
+
+ private static void
+ specificity(Selector selector, Specificity s)
+ {
+ if (selector instanceof ConditionalSelector)
+ {
+ specificity(((ConditionalSelector) selector).getCondition(), s);
+ specificity(((ConditionalSelector) selector).getSimpleSelector(), s);
+ }
+ else
+ {
+ if (selector instanceof DescendantSelector)
+ {
+ specificity(((DescendantSelector) selector).getAncestorSelector(), s);
+ specificity(((DescendantSelector) selector).getSimpleSelector(), s);
+ }
+ else
+ {
+ if (selector instanceof NegativeSelector)
+ {
+ specificity(((NegativeSelector) selector).getSimpleSelector(), s);
+ }
+ else
+ {
+ if (selector instanceof SiblingSelector)
+ {
+ specificity(((SiblingSelector) selector).getSelector(), s);
+ specificity(((SiblingSelector) selector).getSiblingSelector(), s);
+ }
+ else
+ {
+ if
+ (
+ selector.getSelectorType() ==
+ Selector.SAC_ELEMENT_NODE_SELECTOR &&
+ ((ElementSelector) selector).getLocalName() != null
+ // There is no name for "*".
+ )
+ {
+ ++s.names;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ private static void
+ specificity(Condition c, Specificity s)
+ {
+ switch (c.getConditionType())
+ {
+ case Condition.SAC_ID_CONDITION:
+ ++s.ids;
+ break;
+
+ case Condition.SAC_ATTRIBUTE_CONDITION:
+ case Condition.SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION:
+ case Condition.SAC_CLASS_CONDITION:
+ case Condition.SAC_LANG_CONDITION:
+ case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION:
+ case Condition.SAC_PSEUDO_CLASS_CONDITION:
+ ++s.attributes;
+ break;
+
+ case Condition.SAC_AND_CONDITION:
+ case Condition.SAC_OR_CONDITION:
+ specificity(((CombinatorCondition) c).getFirstCondition(), s);
+ specificity(((CombinatorCondition) c).getSecondCondition(), s);
+ break;
+ }
+
+ if
+ (
+ c.getConditionType() == Condition.SAC_POSITIONAL_CONDITION &&
+ ((PositionalCondition) c).getPosition() == 1
+ // first-child pseudo class.
+ )
+ {
+ ++s.attributes;
+ }
+ }
+
+
+
+ /**
+ * Splits this rule into a set of equivalent rules in which there is only one
+ * property. For each property of this rule there will be a new one.
+ */
+
+ Rule[]
+ split()
+ {
+ Rule[] result = new Rule[getProperties().length];
+
+ for (int i = 0; i < result.length; ++i)
+ {
+ result[i] = new Rule(this, getPosition());
+ result[i].addProperty(getProperties()[i]);
+ }
+
+ return result;
+ }
+
+
+
+ private static class Specificity
+
+ {
+
+ private int attributes;
+ private int ids;
+ private int names;
+
+ } // Specificity
+
+} // Rule
diff --git a/src/be/re/css/RuleCollector.java b/src/be/re/css/RuleCollector.java
new file mode 100644
index 0000000..cbb729a
--- /dev/null
+++ b/src/be/re/css/RuleCollector.java
@@ -0,0 +1,349 @@
+package be.re.css;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.DocumentHandler;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.Parser;
+import org.w3c.css.sac.SACMediaList;
+import org.w3c.css.sac.SelectorList;
+
+
+
+/**
+ * This class collects rules and page rules from the "all" and "print" media.
+ * The other media are ignored. Rules without any properties are also ignored.
+ * @author Werner Donn\u00e9
+ */
+
+class RuleCollector implements DocumentHandler
+
+{
+
+ private URL baseUrl;
+ private RuleEmitter ruleEmitter;
+ private PageRule currentPageRule = null;
+ private Rule[] currentRules = null;
+ private boolean ignore = false;
+ private int offset;
+ private List pageRules;
+ private Map prefixMap = new HashMap();
+ private int position;
+
+
+
+ RuleCollector
+ (
+ RuleEmitter ruleEmitter,
+ List pageRules,
+ URL baseUrl,
+ int startPosition,
+ int offset
+ )
+ {
+ this.ruleEmitter = ruleEmitter;
+ this.pageRules = pageRules;
+ this.baseUrl = baseUrl;
+ this.position = startPosition;
+ this.offset = offset;
+ }
+
+
+
+ public void
+ comment(String text) throws CSSException
+ {
+ }
+
+
+
+ public void
+ endDocument(InputSource source) throws CSSException
+ {
+ }
+
+
+
+ public void
+ endFontFace() throws CSSException
+ {
+ }
+
+
+
+ public void
+ endMedia(SACMediaList media) throws CSSException
+ {
+ ignore = false;
+ }
+
+
+
+ public void
+ endPage(String name, String pseudoPage) throws CSSException
+ {
+ if (currentPageRule.getProperties().length > 0)
+ {
+ PageRule[] split = currentPageRule.split();
+
+ for (int i = 0; i < split.length; ++i)
+ {
+ pageRules.add(split[i]);
+ }
+ }
+
+ currentPageRule = null;
+ }
+
+
+
+ public void
+ endSelector(SelectorList selectors) throws CSSException
+ {
+ if (!ignore)
+ {
+ for (int i = 0; i < currentRules.length; ++i)
+ {
+ if (currentRules[i].getProperties().length > 0)
+ {
+ Rule[] split = currentRules[i].split();
+
+ for (int j = 0; j < split.length; ++j)
+ {
+ ruleEmitter.addRule(split[j]);
+ }
+ }
+ }
+
+ currentRules = null;
+ }
+ }
+
+
+
+ int
+ getCurrentPosition()
+ {
+ return position;
+ }
+
+
+
+ private boolean
+ hasOneOfMedia(SACMediaList media, String[] choices)
+ {
+ if (media == null)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < media.getLength(); ++i)
+ {
+ for (int j = 0; j < choices.length; ++j)
+ {
+ if (media.item(i).equals(choices[j]))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+
+ public void
+ ignorableAtRule(String atRule) throws CSSException
+ {
+ }
+
+
+
+ public void
+ importStyle(String uri, SACMediaList media, String defaultNamespaceURI)
+ throws CSSException
+ {
+ if (!ignore)
+ {
+ if
+ (
+ media == null ||
+ hasOneOfMedia(media, new String[] {"all", "print"})
+ )
+ {
+ try
+ {
+ Parser parser = Util.getSacParser();
+ URL url =
+ (baseUrl != null ? new URL(baseUrl, uri) : new URL(uri));
+
+ RuleCollector importCollector =
+ new RuleCollector(ruleEmitter, pageRules, url, position, offset);
+ parser.setDocumentHandler(importCollector);
+ parser.parseStyleSheet(url.toString());
+ position = importCollector.getCurrentPosition();
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSException(e);
+ }
+ }
+ }
+ }
+
+
+
+ public void
+ namespaceDeclaration(String prefix, String uri) throws CSSException
+ {
+ prefixMap.put(prefix, uri);
+ }
+
+
+
+ public void
+ property(String name, LexicalUnit value, boolean important)
+ throws CSSException
+ {
+ if (!ignore)
+ {
+ Property[] properties =
+ new Property(name.toLowerCase(), value, important, prefixMap, baseUrl).
+ split();
+
+ if (currentRules != null)
+ {
+ for (int i = 0; i < currentRules.length; ++i)
+ {
+ for (int j = 0; j < properties.length; ++j)
+ {
+ currentRules[i].addProperty(properties[j]);
+ }
+ }
+ }
+ else
+ {
+ if (currentPageRule != null)
+ {
+ for (int i = 0; i < properties.length; ++i)
+ {
+ LexicalUnit unit = properties[i].getLexicalUnit();
+
+ if
+ (
+ "counter-reset".equals(properties[i].getName()) &&
+ unit.getLexicalUnitType() == LexicalUnit.SAC_IDENT &&
+ "page".equals(unit.getStringValue()) &&
+ (
+ unit.getNextLexicalUnit() == null ||
+ unit.getNextLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_INTEGER
+ )
+ )
+ {
+ properties[i] =
+ unit.getNextLexicalUnit() == null ?
+ new Property
+ (
+ "initial-page-number",
+ "1",
+ properties[i].getImportant(),
+ prefixMap
+ ) :
+ new Property
+ (
+ "initial-page-number",
+ unit.getNextLexicalUnit(),
+ properties[i].getImportant(),
+ prefixMap,
+ baseUrl
+ );
+ }
+
+ currentPageRule.addProperty(properties[i]);
+ }
+ }
+ }
+ }
+ }
+
+
+
+ public void
+ startDocument(InputSource source) throws CSSException
+ {
+ }
+
+
+
+ public void
+ startFontFace() throws CSSException
+ {
+ }
+
+
+
+ public void
+ startMedia(SACMediaList media) throws CSSException
+ {
+ ignore = !hasOneOfMedia(media, new String[] {"all", "print"});
+ }
+
+
+
+ public void
+ startPage(final String name, final String pseudoPage) throws CSSException
+ {
+ if (!ignore)
+ {
+ currentPageRule =
+ new PageRule
+ (
+ name != null && pseudoPage != null ?
+ (pseudoPage + "-" + name ) :
+ (
+ name != null ?
+ name : (pseudoPage != null ? pseudoPage : "unnamed")
+ ),
+ position++
+ );
+ }
+ }
+
+
+
+ public void
+ startSelector(SelectorList selectors) throws CSSException
+ {
+ if (ignore || selectors.getLength() == 0)
+ {
+ currentRules = null;
+
+ return;
+ }
+
+ currentRules = new Rule[selectors.getLength()];
+
+ for (int i = 0; i < currentRules.length; ++i)
+ {
+ currentRules[i] = new Rule(selectors.item(i), position++, offset);
+ }
+ }
+
+
+
+ interface RuleEmitter
+
+ {
+
+ public void addRule (Rule rule);
+
+ } // RuleEmitter
+
+} // RuleCollector
diff --git a/src/be/re/css/RuleComparator.java b/src/be/re/css/RuleComparator.java
new file mode 100644
index 0000000..79cdf83
--- /dev/null
+++ b/src/be/re/css/RuleComparator.java
@@ -0,0 +1,51 @@
+package be.re.css;
+
+import java.util.Comparator;
+
+
+
+/**
+ * A comparator for sorting rules from least to most specific. The rules must
+ * be split, i.e. they should have exactly one property.
+ * @author Werner Donn\u00e9
+ */
+
+class RuleComparator implements Comparator
+
+{
+
+ public int
+ compare(Object object1, Object object2)
+ {
+ Rule rule1 = (Rule) object1;
+ Rule rule2 = (Rule) object2;
+ Property property1 = rule1.getProperty();
+ Property property2 = rule2.getProperty();
+ int result = property1.getName().compareTo(property2.getName());
+
+ if (result == 0)
+ {
+ result =
+ (
+ !property1.getImportant() && property2.getImportant() ?
+ -1 :
+ (
+ property1.getImportant() && !property2.getImportant() ? 1 : 0
+ )
+ );
+ }
+
+ if (result == 0)
+ {
+ result = rule1.getSpecificity() - rule2.getSpecificity();
+ }
+
+ if (result == 0)
+ {
+ result = rule1.getPosition() - rule2.getPosition();
+ }
+
+ return result;
+ }
+
+} // RuleComparator
diff --git a/src/be/re/css/SpaceCorrectionFilter.java b/src/be/re/css/SpaceCorrectionFilter.java
new file mode 100644
index 0000000..dd5afb5
--- /dev/null
+++ b/src/be/re/css/SpaceCorrectionFilter.java
@@ -0,0 +1,118 @@
+package be.re.css;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Properties;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Process space characters as in Jirka's Kosek spaces.xsl DocBook style sheet.
+ * @author Werner Donn\u00e9
+ */
+
+class SpaceCorrectionFilter extends XMLFilterImpl
+
+{
+
+ private static final String[] widths = loadWidths();
+
+
+
+ SpaceCorrectionFilter()
+ {
+ }
+
+
+
+ SpaceCorrectionFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ int position = start;
+
+ for (int i = 0; i < length; ++i)
+ {
+ if
+ (
+ ch[start + i] >= '\u2000' &&
+ ch[start + i] <= '\u200A' &&
+ widths[ch[start + i] - 0x2000].length() > 0
+ )
+ {
+ super.characters(ch, position, start + i - position);
+ position = start + i + 1;
+
+ AttributesImpl atts = new AttributesImpl();
+
+ atts.addAttribute
+ (
+ "",
+ "leader-length",
+ "leader-length",
+ "CDATA",
+ widths[ch[start + i] - 0x2000]
+ );
+
+ super.startElement(Constants.XSLFO, "leader", "fo:leader", atts);
+ super.endElement(Constants.XSLFO, "leader", "fo:leader");
+ }
+ }
+
+ if (position < start + length)
+ {
+ super.characters(ch, position, start + length - position);
+ }
+ }
+
+
+
+ private static String[]
+ loadWidths()
+ {
+ try
+ {
+ Properties properties = new Properties();
+ String[] result = new String[11];
+
+ properties.load
+ (
+ SpaceCorrectionFilter.class.
+ getResourceAsStream("res/space_correction.prop")
+ );
+
+ for (int i = 0; i < result.length; ++i)
+ {
+ String value =
+ properties.
+ getProperty(Integer.toString(0x2000 + i, 16).toLowerCase());
+
+ if (value == null)
+ {
+ value =
+ properties.
+ getProperty(Integer.toString(0x2000 + i, 16).toUpperCase());
+ }
+
+ result[i] = value != null ? value : "";
+ }
+
+ return result;
+ }
+
+ catch (Exception e)
+ {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+} // SpaceCorrectionFilter
diff --git a/src/be/re/css/TestSAC.java b/src/be/re/css/TestSAC.java
new file mode 100644
index 0000000..e29d411
--- /dev/null
+++ b/src/be/re/css/TestSAC.java
@@ -0,0 +1,416 @@
+package be.re.css;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.net.URL;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.CharacterDataSelector;
+import org.w3c.css.sac.ConditionalSelector;
+import org.w3c.css.sac.DescendantSelector;
+import org.w3c.css.sac.DocumentHandler;
+import org.w3c.css.sac.ElementSelector;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.NegativeSelector;
+import org.w3c.css.sac.Parser;
+import org.w3c.css.sac.ProcessingInstructionSelector;
+import org.w3c.css.sac.SACMediaList;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SelectorList;
+import org.w3c.css.sac.SiblingSelector;
+import org.w3c.css.sac.helpers.ParserFactory;
+
+
+
+public class TestSAC
+
+{
+
+ private static final Tuple[] tuples =
+ {
+ new Tuple(LexicalUnit.SAC_OPERATOR_COMMA, "SAC_OPERATOR_COMMA"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_PLUS, "SAC_OPERATOR_PLUS"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_MINUS, "SAC_OPERATOR_MINUS"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_MULTIPLY, "SAC_OPERATOR_MULTIPLY"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_SLASH, "SAC_OPERATOR_SLASH"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_MOD, "SAC_OPERATOR_MOD"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_EXP, "SAC_OPERATOR_EXP"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_LT, "SAC_OPERATOR_LT"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_GT, "SAC_OPERATOR_GT"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_LE, "SAC_OPERATOR_LE"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_GE, "SAC_OPERATOR_GE"),
+ new Tuple(LexicalUnit.SAC_OPERATOR_TILDE, "SAC_OPERATOR_TILDE"),
+ new Tuple(LexicalUnit.SAC_INHERIT, "SAC_INHERIT"),
+ new Tuple(LexicalUnit.SAC_INTEGER, "SAC_INTEGER"),
+ new Tuple(LexicalUnit.SAC_REAL, "SAC_REAL"),
+ new Tuple(LexicalUnit.SAC_EM, "SAC_EM"),
+ new Tuple(LexicalUnit.SAC_EX, "SAC_EX"),
+ new Tuple(LexicalUnit.SAC_PIXEL, "SAC_PIXEL"),
+ new Tuple(LexicalUnit.SAC_INCH, "SAC_INCH"),
+ new Tuple(LexicalUnit.SAC_CENTIMETER, "SAC_CENTIMETER"),
+ new Tuple(LexicalUnit.SAC_MILLIMETER, "SAC_MILLIMETER"),
+ new Tuple(LexicalUnit.SAC_POINT, "SAC_POINT"),
+ new Tuple(LexicalUnit.SAC_PICA, "SAC_PICA"),
+ new Tuple(LexicalUnit.SAC_PERCENTAGE, "SAC_PERCENTAGE"),
+ new Tuple(LexicalUnit.SAC_URI, "SAC_URI"),
+ new Tuple(LexicalUnit.SAC_COUNTER_FUNCTION, "SAC_COUNTER_FUNCTION"),
+ new Tuple(LexicalUnit.SAC_COUNTERS_FUNCTION, "SAC_COUNTERS_FUNCTION"),
+ new Tuple(LexicalUnit.SAC_RGBCOLOR, "SAC_RGBCOLOR"),
+ new Tuple(LexicalUnit.SAC_DEGREE, "SAC_DEGREE"),
+ new Tuple(LexicalUnit.SAC_GRADIAN, "SAC_GRADIAN"),
+ new Tuple(LexicalUnit.SAC_RADIAN, "SAC_RADIAN"),
+ new Tuple(LexicalUnit.SAC_MILLISECOND, "SAC_MILLISECOND"),
+ new Tuple(LexicalUnit.SAC_SECOND, "SAC_SECOND"),
+ new Tuple(LexicalUnit.SAC_HERTZ, "SAC_HERTZ"),
+ new Tuple(LexicalUnit.SAC_KILOHERTZ, "SAC_KILOHERTZ"),
+ new Tuple(LexicalUnit.SAC_IDENT, "SAC_IDENT"),
+ new Tuple(LexicalUnit.SAC_STRING_VALUE, "SAC_STRING_VALUE"),
+ new Tuple(LexicalUnit.SAC_ATTR, "SAC_ATTR"),
+ new Tuple(LexicalUnit.SAC_RECT_FUNCTION, "SAC_RECT_FUNCTION"),
+ new Tuple(LexicalUnit.SAC_UNICODERANGE, "SAC_UNICODERANGE"),
+ new Tuple(LexicalUnit.SAC_SUB_EXPRESSION, "SAC_SUB_EXPRESSION"),
+ new Tuple(LexicalUnit.SAC_FUNCTION, "SAC_FUNCTION"),
+ new Tuple(LexicalUnit.SAC_DIMENSION, "SAC_DIMENSION")
+ };
+
+
+
+ public static void
+ main(String[] args) throws Exception
+ {
+ if (args.length != 1)
+ {
+ System.err.println("Usage: be.re.css.TestSAC url_or_filename");
+ return;
+ }
+
+ Parser parser = new ParserFactory().makeParser();
+ PrintWriter out = new PrintWriter(System.out);
+
+ parser.setDocumentHandler(new SACWriter(out, new File(args[0]).toURL()));
+
+ parser.parseStyleSheet
+ (
+ be.re.net.Util.isUrl(args[0]) ?
+ new URL(args[0]).toString() :
+ be.re.net.Util.fileToUrl(new File(args[0])).toString()
+ );
+
+ out.flush();
+ }
+
+
+
+ private static class SACWriter implements DocumentHandler
+
+ {
+
+ private URL baseUrl;
+ private PrintWriter out;
+
+
+
+ private
+ SACWriter(PrintWriter out, URL baseUrl)
+ {
+ this.out = out;
+ this.baseUrl = baseUrl;
+ }
+
+
+
+ public void
+ comment(String text) throws CSSException
+ {
+ out.println("comment: " + text);
+ }
+
+
+
+ public void
+ endDocument(InputSource source) throws CSSException
+ {
+ out.println("end document");
+ }
+
+
+
+ public void
+ endFontFace() throws CSSException
+ {
+ out.println("end font face");
+ }
+
+
+
+ public void
+ endMedia(SACMediaList media) throws CSSException
+ {
+ out.println("end media");
+ }
+
+
+
+ public void
+ endPage(String name, String pseudePage) throws CSSException
+ {
+ out.println("end page");
+ }
+
+
+
+ public void
+ endSelector(SelectorList selectors) throws CSSException
+ {
+ out.println("end selector");
+ }
+
+
+
+ private static String
+ enumerateUnits(LexicalUnit value)
+ {
+ return
+ lexicalUnitTypeToString(value) +
+ (
+ value.getNextLexicalUnit() != null ?
+ (", " + enumerateUnits(value.getNextLexicalUnit())) : ""
+ );
+ }
+
+
+
+ public void
+ ignorableAtRule(String atRule) throws CSSException
+ {
+ out.println("ignorable at-rule: " + atRule);
+ }
+
+
+
+ public void
+ importStyle(String uri, SACMediaList media, String defaultNamespaceURI)
+ throws CSSException
+ {
+ out.println("import: " + uri);
+
+ try
+ {
+ Parser parser = new ParserFactory().makeParser();
+
+ parser.setDocumentHandler(new SACWriter(out, new URL(baseUrl, uri)));
+ parser.parseStyleSheet(new URL(baseUrl, uri).toString());
+ }
+
+ catch (Exception e)
+ {
+ throw new CSSException(e);
+ }
+ }
+
+
+
+ private static String
+ lexicalUnitTypeToString(LexicalUnit value)
+ {
+ for (int i = 0; i < tuples.length; ++i)
+ {
+ if (tuples[i].id == value.getLexicalUnitType())
+ {
+ return tuples[i].name;
+ }
+ }
+
+ return "unknown";
+ }
+
+
+
+ public void
+ namespaceDeclaration(String prefix, String uri) throws CSSException
+ {
+ out.println("namespace declaration: " + prefix + ", " + uri);
+ }
+
+
+
+ public void
+ property(String name, LexicalUnit value, boolean important)
+ throws CSSException
+ {
+ out.println
+ (
+ "property: " + name + ": " +
+ Util.lexicalUnitToString(value, false, null) + "(" +
+ enumerateUnits(value) + "), " + String.valueOf(important)
+ );
+ }
+
+
+
+ private String
+ selectorText(Selector selector)
+ {
+ switch (selector.getSelectorType())
+ {
+ case Selector.SAC_ANY_NODE_SELECTOR: return "(any)";
+
+ case Selector.SAC_CDATA_SECTION_NODE_SELECTOR:
+ return
+ "(cdata: " + ((CharacterDataSelector) selector).getData() + ")";
+
+ case Selector.SAC_CHILD_SELECTOR:
+ return
+ "(child: " +
+ selectorText
+ (
+ ((DescendantSelector) selector).getAncestorSelector()
+ ) + " " +
+ selectorText(((DescendantSelector) selector).getSimpleSelector())
+ + ")";
+
+ case Selector.SAC_COMMENT_NODE_SELECTOR:
+ return
+ "(comment: " + ((CharacterDataSelector) selector).getData() + ")";
+
+ case Selector.SAC_CONDITIONAL_SELECTOR:
+ return
+ "(conditional: " +
+ Util.conditionText(((ConditionalSelector) selector).
+ getCondition()) +
+ " " +
+ selectorText(((ConditionalSelector) selector).getSimpleSelector())
+ + ")";
+
+ case Selector.SAC_DESCENDANT_SELECTOR:
+ return
+ "(descendant: " +
+ selectorText
+ (
+ ((DescendantSelector) selector).getAncestorSelector()
+ ) + " " +
+ selectorText(((DescendantSelector) selector).getSimpleSelector())
+ + ")";
+
+ case Selector.SAC_DIRECT_ADJACENT_SELECTOR:
+ return
+ "(sibling: " +
+ selectorText(((SiblingSelector) selector).getSelector()) + " " +
+ selectorText(((SiblingSelector) selector).getSiblingSelector())
+ + ")";
+
+ case Selector.SAC_ELEMENT_NODE_SELECTOR:
+ return
+ "(element: " +
+ (
+ ((ElementSelector) selector).getNamespaceURI() != null ?
+ (((ElementSelector) selector).getNamespaceURI() + "#") : ""
+ ) + ((ElementSelector) selector).getLocalName() + ")";
+
+ case Selector.SAC_NEGATIVE_SELECTOR:
+ return
+ "(negative: " +
+ selectorText(((NegativeSelector) selector).getSimpleSelector()) +
+ ")";
+
+ case Selector.SAC_PROCESSING_INSTRUCTION_NODE_SELECTOR:
+ return
+ "(pi: (" + ((ProcessingInstructionSelector) selector).getData() +
+ ") (" + ((ProcessingInstructionSelector) selector).getTarget() +
+ "))";
+
+ case Selector.SAC_PSEUDO_ELEMENT_SELECTOR:
+ return
+ "(pseudo: " +
+ (
+ ((ElementSelector) selector).getNamespaceURI() != null ?
+ (((ElementSelector) selector).getNamespaceURI() + "#") : ""
+ ) + ((ElementSelector) selector).getLocalName() + ")";
+
+ case Selector.SAC_ROOT_NODE_SELECTOR: return "(root)";
+
+ case Selector.SAC_TEXT_NODE_SELECTOR:
+ return
+ "(text: " + ((CharacterDataSelector) selector).getData() + ")";
+
+ default: return "(unknown)";
+ }
+ }
+
+
+
+ public void
+ startDocument(InputSource source) throws CSSException
+ {
+ out.println("start document");
+ }
+
+
+
+ public void
+ startFontFace() throws CSSException
+ {
+ out.println("start font face");
+ }
+
+
+
+ public void
+ startMedia(SACMediaList media) throws CSSException
+ {
+ out.print("start media:");
+
+ for (int i = 0; i < media.getLength(); ++i)
+ {
+ out.print(" " + media.item(i));
+ }
+
+ out.println();
+ }
+
+
+
+ public void
+ startPage(final String name, final String pseudoPage) throws CSSException
+ {
+ out.println("start page: " + name + ", " + pseudoPage);
+ }
+
+
+
+ public void
+ startSelector(SelectorList selectors) throws CSSException
+ {
+ out.print("start selector:");
+
+ for (int i = 0; i < selectors.getLength(); ++i)
+ {
+ out.print(" " + selectorText(selectors.item(i)));
+ }
+
+ out.println();
+ }
+
+ } // SACWriter
+
+
+
+ private static class Tuple
+
+ {
+
+ private int id;
+ private String name;
+
+
+
+ private
+ Tuple(int id, String name)
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ } // Tuple
+
+} // TestSAC
diff --git a/src/be/re/css/Util.java b/src/be/re/css/Util.java
new file mode 100644
index 0000000..7afee0f
--- /dev/null
+++ b/src/be/re/css/Util.java
@@ -0,0 +1,1187 @@
+package be.re.css;
+
+import be.re.xml.sax.FilterOfFilters;
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+import org.w3c.css.sac.AttributeCondition;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.CombinatorCondition;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.ConditionalSelector;
+import org.w3c.css.sac.ContentCondition;
+import org.w3c.css.sac.DescendantSelector;
+import org.w3c.css.sac.ElementSelector;
+import org.w3c.css.sac.LangCondition;
+import org.w3c.css.sac.LexicalUnit;
+import org.w3c.css.sac.NegativeCondition;
+import org.w3c.css.sac.NegativeSelector;
+import org.w3c.css.sac.Parser;
+import org.w3c.css.sac.PositionalCondition;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SiblingSelector;
+import org.xml.sax.Attributes;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * A collection of functions.
+ * @author Werner Donn\u00e9
+ */
+
+class Util
+
+{
+
+ private static final String[] FOOTNOTE_NUMBERS =
+ new String[]
+ {
+ "*",
+ "\u2020",
+ "\u2021",
+ "\u00a7",
+ "\u007c\u007c",
+ "\u00b6",
+ "#",
+ "**",
+ "\u2020\u2020",
+ "\u2021\u2021",
+ "\u00a7\u00a7"
+ };
+ private static final Set INHERITED =
+ new HashSet
+ (
+ Arrays.asList
+ (
+ new String[]
+ {
+ "azimuth",
+ "border-collapse",
+ "border-spacing",
+ "caption-size",
+ "color",
+ "cursor",
+ "direction",
+ "elevation",
+ "empty-cells",
+ "font",
+ "font-family",
+ "font-size",
+ "font-stretch",
+ "font-style",
+ "font-variant",
+ "font-weight",
+ "hyphenate",
+ "leader-alignment",
+ "leader-length",
+ "leader-pattern",
+ "leader-pattern-width",
+ "letter-spacing",
+ "line-height",
+ "list-style",
+ "list-style-image",
+ "list-style-position",
+ "list-style-type",
+ "orientation",
+ "orphans",
+ "page",
+ "page-break-inside",
+ "pitch",
+ "pitch-range",
+ "quotes",
+ "richness",
+ "rule-style",
+ "rule-thickness",
+ "speak",
+ "speak-header",
+ "speak-numeral",
+ "speak-punctuation",
+ "speech-rate",
+ "stress",
+ "text-align",
+ "text-align-last",
+ "text-indent",
+ "text-transform",
+ "voice-family",
+ "volume",
+ "white-space",
+ "widows",
+ "word-spacing"
+ }
+ )
+ );
+
+ private static Class sacParserClass = null;
+
+
+
+ static String
+ conditionText(Condition condition)
+ {
+ switch (condition.getConditionType())
+ {
+ case Condition.SAC_AND_CONDITION:
+ return
+ "(and: " +
+ conditionText
+ (
+ ((CombinatorCondition) condition).getFirstCondition()
+ ) + " " +
+ conditionText
+ (
+ ((CombinatorCondition) condition).getSecondCondition()
+ ) + ")";
+
+ case Condition.SAC_ATTRIBUTE_CONDITION:
+ return
+ "(attribute: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ case Condition.SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION:
+ return
+ "(hyphen: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ case Condition.SAC_CLASS_CONDITION:
+ return
+ "(class: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ case Condition.SAC_CONTENT_CONDITION:
+ return "(content: " + ((ContentCondition) condition).getData() + ")";
+
+ case Condition.SAC_ID_CONDITION:
+ return
+ "(id: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ case Condition.SAC_LANG_CONDITION:
+ return "(lang: " + ((LangCondition) condition).getLang() + ")";
+
+ case Condition.SAC_NEGATIVE_CONDITION:
+ return
+ "(negative: " +
+ conditionText(((NegativeCondition) condition).getCondition()) +
+ ")";
+
+ case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION:
+ return
+ "(one of: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ case Condition.SAC_ONLY_CHILD_CONDITION: return "(only child)";
+
+ case Condition.SAC_ONLY_TYPE_CONDITION: return "(only type)";
+
+ case Condition.SAC_OR_CONDITION:
+ return
+ "(or: " +
+ conditionText
+ (
+ ((CombinatorCondition) condition).getFirstCondition()
+ ) + " " +
+ conditionText
+ (
+ ((CombinatorCondition) condition).getSecondCondition()
+ ) + ")";
+
+ case Condition.SAC_POSITIONAL_CONDITION:
+ return
+ "(positional: (" + ((PositionalCondition) condition).getPosition() +
+ ") (" + ((PositionalCondition) condition).getType() + ") (" +
+ ((PositionalCondition) condition).getTypeNode() + "))";
+
+ case Condition.SAC_PSEUDO_CLASS_CONDITION:
+ return
+ "(pseudo class: (" +
+ ((AttributeCondition) condition).getNamespaceURI() + ") (" +
+ ((AttributeCondition) condition).getLocalName() + ") (" +
+ ((AttributeCondition) condition).getSpecified() + ") (" +
+ ((AttributeCondition) condition).getValue() + "))";
+
+ default: return "(unknown)";
+ }
+ }
+
+
+
+ private static String
+ convertFloat(float value)
+ {
+ return
+ new DecimalFormat
+ (
+ "####0.0####",
+ new DecimalFormatSymbols(Locale.ENGLISH)
+ ).format(value);
+ }
+
+
+
+ static void
+ copyAttribute
+ (
+ Attributes from,
+ AttributesImpl to,
+ String namespaceURI,
+ String localName
+ )
+ {
+ int index = from.getIndex(namespaceURI, localName);
+
+ if (index != -1)
+ {
+ to.addAttribute
+ (
+ namespaceURI,
+ localName,
+ from.getQName(index),
+ from.getType(index),
+ from.getValue(index)
+ );
+ }
+ }
+
+
+
+ static PostProjectionFilter
+ createPostProjectionFilter
+ (
+ URL baseUrl,
+ Map userAgentParameters,
+ boolean debug
+ )
+ {
+ final LinkFilter linkFilter = new LinkFilter(baseUrl);
+ final XMLFilterImpl filter =
+ new FilterOfFilters
+ (
+ new XMLFilter[]
+ {
+ new InvalidPropertyFilter(),
+ new WrapperFilter(),
+ new DisplayNonePropagator(),
+ new ForeignFilter(),
+ new FirstLetterFilter(),
+ userAgentParameters != null &&
+ userAgentParameters.get("rule-thickness") != null ?
+ new XHTMLAttributeTranslationFilter
+ (
+ (String) userAgentParameters.get("rule-thickness")
+ ) : new XHTMLAttributeTranslationFilter(),
+ new NormalizeTableFilter(),
+ new CenterFilter(),
+ new LengthAdjustFilter(),
+ new WidthAndMarginsFilter(),
+ new MarkerFilter(),
+ linkFilter,
+ new FootnoteFilter(),
+ new BlockContainerFilter(),
+ new ListImageLabelFilter()
+ },
+ debug
+ );
+
+ return
+ new PostProjectionFilter()
+ {
+ public XMLFilterImpl
+ getFilter()
+ {
+ return filter;
+ }
+
+ public void
+ setBaseUrl(URL url)
+ {
+ linkFilter.setBaseUrl(url);
+ }
+ };
+ }
+
+
+
+ static XMLFilter
+ createPreprocessorFilter(URL[] preprocessors, XMLFilter parent)
+ throws TransformerConfigurationException
+ {
+ SAXTransformerFactory factory =
+ be.re.xml.sax.Util.newSAXTransformerFactory();
+ XMLFilter result = parent;
+
+ for (int i = 0; i < preprocessors.length; ++i)
+ {
+ XMLFilter transformer =
+ factory.newXMLFilter(new StreamSource(preprocessors[i].toString()));
+
+ transformer.setParent(result);
+ result = transformer;
+ }
+
+ return result;
+ }
+
+
+
+ static URL
+ createUrl(String s) throws MalformedURLException
+ {
+ return
+ be.re.net.Util.isUrl(s) ?
+ new URL(s) : be.re.net.Util.fileToUrl(new File(s));
+ }
+
+
+
+ static URL[]
+ createUrls(String s) throws MalformedURLException
+ {
+ List result = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(s, ",");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ result.add(createUrl(tokenizer.nextToken()));
+ }
+
+ return (URL[]) result.toArray(new URL[0]);
+ }
+
+
+
+ static String
+ getIndirectType(Attributes attributes, String property)
+ {
+ String value = attributes.getValue(Constants.CSS, property);
+ int index = value != null ? value.lastIndexOf('|') : -1;
+
+ return
+ value != null ?
+ (
+ index != -1 ?
+ attributes.
+ getType(value.substring(0, index), value.substring(index + 1)) :
+ attributes.getType(value)
+ ) : null;
+ }
+
+
+
+ static String
+ getIndirectValue(Attributes attributes, String property)
+ {
+ String value = attributes.getValue(Constants.CSS, property);
+ int index = value != null ? value.lastIndexOf('|') : -1;
+
+ return
+ value != null ?
+ (
+ index != -1 ?
+ attributes.
+ getValue(value.substring(0, index), value.substring(index + 1)) :
+ attributes.getValue(value)
+ ) : null;
+ }
+
+
+
+ static Selector
+ getLastSelector(Selector selector)
+ {
+ Selector[] chain = Util.getSelectorChain(selector);
+
+ return chain[chain.length - 1];
+ }
+
+
+
+ static Parser
+ getSacParser() throws CSSException
+ {
+ try
+ {
+ return (Parser) getSacParserClass().newInstance();
+ }
+
+ catch (Exception e)
+ {
+ if (e instanceof CSSException)
+ {
+ throw (CSSException) e;
+ }
+
+ throw new CSSException(e);
+ }
+ }
+
+
+
+ private static Class
+ getSacParserClass() throws Exception
+ {
+ if (sacParserClass == null)
+ {
+ String cls =
+ be.re.util.Util.getSystemProperty("org.w3c.css.sac.parser");
+
+ if (cls == null)
+ {
+ throw new CSSException("No value for org.w3c.css.sac.parser");
+ }
+
+ sacParserClass = Class.forName(cls);
+ }
+
+ return sacParserClass;
+ }
+
+
+
+ /**
+ * Flattens the selector expression tree in infix order.
+ */
+
+ static Selector[]
+ getSelectorChain(Selector selector)
+ {
+ List result = getSelectorChainList(selector);
+
+ return (Selector[]) result.toArray(new Selector[result.size()]);
+ }
+
+
+
+ private static List
+ getSelectorChainList(Selector selector)
+ {
+ List result;
+
+ switch (selector.getSelectorType())
+ {
+ case Selector.SAC_CHILD_SELECTOR:
+ case Selector.SAC_DESCENDANT_SELECTOR:
+ result =
+ getSelectorChainList
+ (
+ ((DescendantSelector) selector).getAncestorSelector()
+ );
+
+ result.add(selector);
+
+ result.addAll
+ (
+ getSelectorChainList
+ (
+ ((DescendantSelector) selector).getSimpleSelector()
+ )
+ );
+
+ break;
+
+ case Selector.SAC_CONDITIONAL_SELECTOR:
+ result = new ArrayList();
+ result.add(selector);
+
+ result.addAll
+ (
+ getSelectorChainList
+ (
+ ((ConditionalSelector) selector).getSimpleSelector()
+ )
+ );
+
+ break;
+
+ case Selector.SAC_DIRECT_ADJACENT_SELECTOR:
+ result =
+ getSelectorChainList(((SiblingSelector) selector).getSelector());
+ result.add(selector);
+
+ result.addAll
+ (
+ getSelectorChainList
+ (
+ ((SiblingSelector) selector).getSiblingSelector()
+ )
+ );
+
+ break;
+
+ case Selector.SAC_NEGATIVE_SELECTOR:
+ result = new ArrayList();
+ result.add(selector);
+
+ result.addAll
+ (
+ getSelectorChainList
+ (
+ ((NegativeSelector) selector).getSimpleSelector()
+ )
+ );
+
+ break;
+
+ default:
+ result = new ArrayList();
+ result.add(selector);
+ }
+
+ return result;
+ }
+
+
+
+ static boolean
+ inArray(String[] array, String object)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if
+ (
+ (
+ array[i].charAt(array[i].length() - 1) == '*' &&
+ object.startsWith(array[i].substring(0, array[i].length() - 1))
+ ) ||
+ array[i].equals(object)
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ static int
+ indexOf(Object[] array, Object object)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i].equals(object))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ static boolean
+ isInherited(String property)
+ {
+ return INHERITED.contains(property);
+ }
+
+
+
+ static boolean
+ isWhitespace(char[] ch, int start, int length)
+ {
+ for (int i = start; i < ch.length && i < start + length; ++i)
+ {
+ if (!Character.isWhitespace(ch[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ static boolean
+ isZeroLength(String value)
+ {
+ return
+ Util.inArray
+ (
+ new String[] {"0", "0pt", "0px", "0pc", "0mm", "0cm", "0in", "0em"},
+ value
+ );
+ }
+
+
+
+ static LexicalUnit[]
+ lexicalUnitArray(LexicalUnit unit)
+ {
+ List result = new ArrayList();
+
+ for (LexicalUnit i = unit; i != null; i = i.getNextLexicalUnit())
+ {
+ result.add(i);
+ }
+
+ return (LexicalUnit[]) result.toArray(new LexicalUnit[result.size()]);
+ }
+
+
+
+ static String
+ lexicalUnitAtom(LexicalUnit unit, URL baseUrl)
+ {
+ return lexicalUnitAtom(unit, false, baseUrl);
+ }
+
+
+
+ private static String
+ lexicalUnitAtom(LexicalUnit unit, boolean identifiersToLower, URL baseUrl)
+ {
+ switch (unit.getLexicalUnitType())
+ {
+ case LexicalUnit.SAC_ATTR:
+ return "attr(" + unit.getStringValue().toLowerCase() + ")";
+
+ case LexicalUnit.SAC_CENTIMETER:
+ case LexicalUnit.SAC_DEGREE:
+ case LexicalUnit.SAC_DIMENSION:
+ case LexicalUnit.SAC_EM:
+ case LexicalUnit.SAC_EX:
+ case LexicalUnit.SAC_GRADIAN:
+ case LexicalUnit.SAC_HERTZ:
+ case LexicalUnit.SAC_INCH:
+ case LexicalUnit.SAC_KILOHERTZ:
+ case LexicalUnit.SAC_MILLIMETER:
+ case LexicalUnit.SAC_MILLISECOND:
+ case LexicalUnit.SAC_PERCENTAGE:
+ case LexicalUnit.SAC_PICA:
+ case LexicalUnit.SAC_PIXEL:
+ case LexicalUnit.SAC_POINT:
+ case LexicalUnit.SAC_RADIAN:
+ return
+ (convertFloat(unit.getFloatValue()) + unit.getDimensionUnitText()).
+ toLowerCase();
+
+ // Flute 1.3 work-around, should be in previous list.
+ case LexicalUnit.SAC_REAL:
+ return convertFloat(unit.getFloatValue());
+
+ case LexicalUnit.SAC_COUNTER_FUNCTION:
+ case LexicalUnit.SAC_COUNTERS_FUNCTION:
+ case LexicalUnit.SAC_FUNCTION:
+ case LexicalUnit.SAC_RECT_FUNCTION:
+ return
+ unit.getFunctionName().toLowerCase() + "(" +
+ (
+ unit.getParameters() != null ?
+ lexicalUnitChain
+ (
+ unit.getParameters(),
+ identifiersToLower,
+ baseUrl
+ ) : ""
+ ) + ")";
+
+ case LexicalUnit.SAC_IDENT:
+ return
+ identifiersToLower ?
+ unit.getStringValue().toLowerCase() : unit.getStringValue();
+
+ case LexicalUnit.SAC_INHERIT: return "inherit";
+
+ case LexicalUnit.SAC_INTEGER:
+ return String.valueOf(unit.getIntegerValue());
+
+ case LexicalUnit.SAC_OPERATOR_COMMA: return ",";
+
+ case LexicalUnit.SAC_OPERATOR_EXP: return "^";
+
+ case LexicalUnit.SAC_OPERATOR_GE: return ">=";
+
+ case LexicalUnit.SAC_OPERATOR_GT: return ">";
+
+ case LexicalUnit.SAC_OPERATOR_LE: return "<=";
+
+ case LexicalUnit.SAC_OPERATOR_LT: return "<";
+
+ case LexicalUnit.SAC_OPERATOR_MINUS: return "-";
+
+ case LexicalUnit.SAC_OPERATOR_MOD: return "%";
+
+ case LexicalUnit.SAC_OPERATOR_MULTIPLY: return "*";
+
+ case LexicalUnit.SAC_OPERATOR_PLUS: return "+";
+
+ case LexicalUnit.SAC_OPERATOR_SLASH: return "/";
+
+ case LexicalUnit.SAC_OPERATOR_TILDE: return "~";
+
+ case LexicalUnit.SAC_RGBCOLOR:
+ return
+ "rgb(" +
+ lexicalUnitChain(unit.getParameters(), identifiersToLower, baseUrl)
+ + ")";
+
+ case LexicalUnit.SAC_STRING_VALUE: return unit.getStringValue();
+
+ case LexicalUnit.SAC_URI:
+ try
+ {
+ return
+ "url(" +
+ (
+ baseUrl != null ?
+ new URL(baseUrl, unit.getStringValue()).toString() :
+ unit.getStringValue()
+ ) + ")";
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ default: return "";
+ }
+ }
+
+
+
+ static String
+ lexicalUnitAtomLower(LexicalUnit unit, URL baseUrl)
+ {
+ return
+ unit.getLexicalUnitType() == LexicalUnit.SAC_URI ?
+ lexicalUnitAtom(unit, baseUrl) :
+ lexicalUnitAtom(unit, baseUrl).toLowerCase();
+ }
+
+
+
+ static String[]
+ lexicalUnitAtoms(LexicalUnit unit, URL baseUrl)
+ {
+ return lexicalUnitAtoms(unit, false, baseUrl);
+ }
+
+
+
+ private static String[]
+ lexicalUnitAtoms(LexicalUnit unit, boolean lower, URL baseUrl)
+ {
+ LexicalUnit[] values = lexicalUnitArray(unit);
+ String[] result = new String[values.length];
+
+ for (int i = 0; i < values.length; ++i)
+ {
+ result[i] =
+ lower ?
+ lexicalUnitAtomLower(values[i], baseUrl) :
+ lexicalUnitAtom(values[i], baseUrl);
+ }
+
+ return result;
+ }
+
+
+
+ static String[]
+ lexicalUnitAtomsLower(LexicalUnit unit, URL baseUrl)
+ {
+ return lexicalUnitAtoms(unit, true, baseUrl);
+ }
+
+
+
+ private static String
+ lexicalUnitChain(LexicalUnit unit, boolean identifiersToLower, URL baseUrl)
+ {
+ return
+ lexicalUnitAtom(unit, identifiersToLower, baseUrl) +
+ (
+ unit.getNextLexicalUnit() != null ?
+ (
+ " " +
+ lexicalUnitChain
+ (
+ unit.getNextLexicalUnit(),
+ identifiersToLower,
+ baseUrl
+ )
+ ) : ""
+ );
+ }
+
+
+
+ static String
+ lexicalUnitToString(LexicalUnit unit, boolean identifiersToLower, URL baseUrl)
+ {
+ return lexicalUnitChain(unit, identifiersToLower, baseUrl);
+ }
+
+
+
+ /**
+ * Adds <code>from</code> attributes to <code>into</code> giving precedence
+ * to the latter.
+ */
+
+ static Attributes
+ mergeAttributes(Attributes from, Attributes into)
+ {
+ return mergeAttributes(from, into, new String[0], false);
+ }
+
+
+
+ /**
+ * Adds <code>from</code> attributes to <code>into</code> giving precedence
+ * to the latter. If <code>include</code> is <code>true</code>, the attribute
+ * in <code>from</code> must be in <code>subset</code> in order for it to be
+ * included. If <code>include</code> is <code>false</code>, the attribute
+ * in <code>from</code> must not be in <code>subset</code> in order for it to
+ * be included.
+ */
+
+ static Attributes
+ mergeAttributes
+ (
+ Attributes from,
+ Attributes into,
+ String[] subset,
+ boolean include
+ )
+ {
+ AttributesImpl result = new AttributesImpl(into);
+
+ for (int i = 0; i < from.getLength(); ++i)
+ {
+ if
+ (
+ into.getIndex(from.getURI(i), from.getLocalName(i)) == -1 &&
+ (
+ (
+ !include &&
+ !inArray(subset, from.getLocalName(i))
+ ) ||
+ (
+ include &&
+ inArray(subset, from.getLocalName(i))
+ )
+ )
+ )
+ {
+ result.addAttribute
+ (
+ from.getURI(i),
+ from.getLocalName(i),
+ from.getQName(i),
+ from.getType(i),
+ from.getValue(i)
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ static void
+ printUserAgentParameters(PrintStream out)
+ {
+ System.err.println("User Agent parameters:");
+ System.err.println(" column-count (default: 1)");
+ System.err.println(" country (default: GB)");
+ System.err.
+ println(" font-size (default: 10pt for a5 and b5, otherwise 11pt)");
+ System.err.println(" html-header-mark: an HTML element (default: none)");
+ System.err.println(" language (default: en)");
+ System.err.println(" odd-even-shift (default: 10mm)");
+ System.err.
+ println(" orientation (default: portrait; other: landscape)");
+ System.err.println(" paper-margin-bottom (default: 0mm)");
+ System.err.println(" paper-margin-left (default: 25mm)");
+ System.err.println(" paper-margin-right (default: 25mm)");
+ System.err.println(" paper-margin-top (default: 10mm)");
+ System.err.println(" paper-mode (default: onesided; other: twosided)");
+
+ System.err.println
+ (
+ " paper-size (default: a4; others: a0, a1, a2, a3, a5, b5, " +
+ "executive, letter and legal)"
+ );
+
+ System.err.println(" rule-thickness (default: 0.2pt)");
+ System.err.println(" writing-mode (default: lr-tb)");
+ }
+
+
+
+ private static String
+ processFontFamily(String value)
+ {
+ value = value.trim();
+
+ if
+ (
+ value.indexOf(' ') == -1 ||
+ (
+ value.charAt(0) == '\'' &&
+ value.charAt(value.length() - 1) == '\''
+ ) ||
+ (
+ value.charAt(0) == '"' &&
+ value.charAt(value.length() - 1) == '"'
+ )
+ )
+ {
+ return value;
+ }
+
+ String result = "";
+ StringTokenizer tokenizer = new StringTokenizer(value, ",");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken().trim();
+
+ result +=
+ (result.equals("") ? "" : ", ") +
+ (token.indexOf(' ') != -1 ? ("'" + token + "'") : token);
+ }
+
+ return result;
+ }
+
+
+
+ static void
+ removeAttribute(AttributesImpl atts, String namespaceURI, String localName)
+ {
+ int index = atts.getIndex(namespaceURI, localName);
+
+ if (index != -1)
+ {
+ atts.removeAttribute(index);
+ }
+ }
+
+
+
+ static void
+ setAttribute
+ (
+ AttributesImpl attributes,
+ String namespaceURI,
+ String localName,
+ String qName,
+ String value
+ )
+ {
+ int index = attributes.getIndex(namespaceURI, localName);
+
+ if (index == -1)
+ {
+ attributes.addAttribute(namespaceURI, localName, qName, "CDATA", value);
+ }
+ else
+ {
+ attributes.setAttribute
+ (
+ index,
+ namespaceURI,
+ localName,
+ qName,
+ "CDATA",
+ value
+ );
+ }
+ }
+
+
+
+ /**
+ * If the value of a property is a call to the "attr" function and if the
+ * property is not "content", the call is replaced by the expanded attribute
+ * name in which the URI is separated from the local name by a |.
+ */
+
+ static void
+ setCSSAttribute(AttributesImpl attributes, Property property, int specificity)
+ {
+ String propertyName = property.getName();
+ String value;
+
+ if
+ (
+ !"content".equals(propertyName) &&
+ property.getLexicalUnit() != null &&
+ property.getLexicalUnit().getLexicalUnitType() ==
+ LexicalUnit.SAC_ATTR
+ )
+ {
+ value = property.getLexicalUnit().getStringValue();
+
+ int index = value.lastIndexOf('|');
+
+ if (index != -1)
+ {
+ value =
+ (String) property.getPrefixMap().get(value.substring(0, index)) +
+ "|" + value.substring(index + 1);
+ }
+ }
+ else
+ {
+ value = property.getValue();
+ }
+
+ Util.setAttribute
+ (
+ attributes,
+ Constants.CSS,
+ propertyName,
+ "css:" + propertyName,
+ "font-family".equals(propertyName) ? processFontFamily(value) : value
+ );
+
+ // XHTML attributes are translated to CSS properties further down the
+ // filter chain. They get a specificity of 0 and a position before the
+ // other rules in the style sheet. Therefore, they can only overwrite
+ // property values selected by the universal selector or comming from the
+ // UA style sheet.
+
+ if (specificity <= 0) // Universal selector is 0, UA rules are < 0.
+ {
+ // Marked as eligible for replacement.
+
+ Util.setAttribute
+ (
+ attributes,
+ Constants.SPECIF,
+ propertyName,
+ "sp:" + propertyName,
+ "1"
+ );
+ }
+ }
+
+
+
+ static String
+ toFootnote(int v)
+ {
+ return v > FOOTNOTE_NUMBERS.length ? "*" : FOOTNOTE_NUMBERS[v - 1];
+ }
+
+
+
+ static String
+ toRoman(int v)
+ {
+ return
+ v < 1 ?
+ "" :
+ (
+ v < 4 ?
+ ("I" + toRoman(v - 1)) :
+ (
+ v < 5 ?
+ "IV" :
+ (
+ v < 9 ?
+ ("V" + toRoman(v - 5)) :
+ (
+ v < 10 ?
+ "IX" :
+ (
+ v < 40 ?
+ ("X" + toRoman(v - 10)) :
+ (
+ v < 50 ?
+ ("XL" + toRoman(v - 40)) :
+ (
+ v < 90 ?
+ ("L" + toRoman(v - 50)) :
+ (
+ v < 100 ?
+ ("XC" + toRoman(v - 90)) :
+ (
+ v < 400 ?
+ ("C" + toRoman(v - 100)) :
+ (
+ v < 500 ?
+ ("CD" + toRoman(v - 400)) :
+ (
+ v < 900 ?
+ (
+ "D" +
+ toRoman(v - 500)
+ ) :
+ (
+ v < 1000 ?
+ (
+ "CM" +
+ toRoman(v - 900)
+ ) :
+ (
+ "M" +
+ toRoman(v - 1000)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ );
+ }
+
+
+
+ interface PostProjectionFilter
+
+ {
+
+ public XMLFilterImpl getFilter ();
+ public void setBaseUrl (URL baseUrl);
+
+ } // BaseUrl
+
+} // Util
diff --git a/src/be/re/css/WidthAndMarginsFilter.java b/src/be/re/css/WidthAndMarginsFilter.java
new file mode 100644
index 0000000..2595367
--- /dev/null
+++ b/src/be/re/css/WidthAndMarginsFilter.java
@@ -0,0 +1,328 @@
+package be.re.css;
+
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Compensates the differences between CSS and XSL-FO with respect to
+ * horizontal margins and width. There should be no shorthand properties
+ * anymore.
+ * @author Werner Donne\u00e9
+ */
+
+class WidthAndMarginsFilter extends XMLFilterImpl
+
+{
+
+ private Stack stack = new Stack();
+
+
+
+ WidthAndMarginsFilter()
+ {
+ }
+
+
+
+ WidthAndMarginsFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ private static String
+ adjustMargin(String margin, Attributes atts, String edge)
+ {
+ String s;
+
+ return
+ "auto".equals(margin) ?
+ margin :
+ (
+ margin +
+ (Util.isZeroLength(s = getBorderWidth(atts, edge)) ? "" : ("+" + s))
+ );
+ }
+
+
+
+ private static String
+ canonicLength(String value)
+ {
+ return Util.isZeroLength(value) ? "0pt" : value;
+ }
+
+
+
+ private static Attributes
+ correctBlock(Attributes atts, Attributes parent)
+ {
+ AttributesImpl result = new AttributesImpl(atts);
+
+ String marginLeft = getImplicitZeroProperty(atts, "margin-left");
+ String marginRight = getImplicitZeroProperty(atts, "margin-right");
+ String width = atts.getValue(Constants.CSS, "width");
+
+ if ("auto".equals(width))
+ {
+ marginLeft = "0pt";
+ marginRight = "0pt";
+ }
+ else
+ {
+ if
+ (
+ width != null &&
+ !width.equals("auto") &&
+ !marginLeft.equals("auto") &&
+ !marginRight.equals("auto")
+ ) // Over-constraint.
+ {
+ String direction = atts.getValue(Constants.CSS, "direction");
+
+ if (direction == null || direction.equals("ltr"))
+ {
+ marginRight = "auto";
+ }
+ else
+ {
+ marginLeft = "auto";
+ }
+ }
+ }
+
+ if (marginLeft.equals("auto") && marginRight.equals("auto"))
+ {
+ marginRight = "0pt";
+ }
+
+ setValue(result, "margin-left", adjustMargin(marginLeft, atts, "left"));
+ setValue(result, "margin-right", adjustMargin(marginRight, atts, "right"));
+
+ if (width != null)
+ {
+ setValue(result, "width", width);
+ }
+
+ return result;
+ }
+
+
+
+ private static Attributes
+ correctInline(Attributes atts)
+ {
+ AttributesImpl result = new AttributesImpl(atts);
+ int index = result.getIndex(Constants.CSS, "width");
+
+ if (index != -1)
+ {
+ result.removeAttribute(index);
+ }
+
+ makeAutoExplicit(result, "margin-left");
+ makeAutoExplicit(result, "margin-right");
+
+ return result;
+ }
+
+
+
+ private static Attributes
+ correctFloat(Attributes atts)
+ {
+ AttributesImpl result = new AttributesImpl(atts);
+
+ // The "width" property is not touched here because that could disable
+ // replaced elements (width="auto" should be set to "0"). We can't
+ // distinguish replaced and non-replaced elements here.
+
+ makeAutoExplicit(result, "margin-left");
+ makeAutoExplicit(result, "margin-right");
+
+ return result;
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ super.endElement(namespaceURI, localName, qName);
+ stack.pop();
+ }
+
+
+
+ private static String
+ getBorderWidth(Attributes atts, String edge)
+ {
+ String value =
+ atts.getValue(Constants.CSS, "border-" + edge + "-width");
+
+ return
+ atts.getValue(Constants.CSS, "border-" + edge + "-style") == null ||
+ "none".equals
+ (
+ atts.getValue(Constants.CSS, "border-" + edge + "-style")
+ ) ?
+ "0pt" :
+ (
+ value == null ?
+ "0.6pt" : // medium
+ (
+ "thin".equals(value) ?
+ "0.2pt" :
+ (
+ "medium".equals(value) ?
+ "0.6pt" :
+ ("thick".equals(value) ? "1pt" : canonicLength(value))
+ )
+ )
+ );
+ }
+
+
+
+ private static String
+ getImplicitZeroProperty(Attributes atts, String property)
+ {
+ String value = atts.getValue(Constants.CSS, property);
+
+ return value == null ? "0pt" : canonicLength(value);
+ }
+
+
+
+ private static String
+ getPadding(Attributes atts, String edge)
+ {
+ String value = atts.getValue(Constants.CSS, "padding-" + edge);
+
+ return value == null ? "0pt" : canonicLength(value);
+ }
+
+
+
+ private static void
+ makeAutoExplicit(AttributesImpl atts, String property)
+ {
+ int index = atts.getIndex(Constants.CSS, property);
+
+ if (index == -1)
+ {
+ atts.addAttribute
+ (
+ Constants.CSS,
+ property,
+ "css:" + property,
+ "CDATA",
+ "0pt"
+ );
+ }
+ else
+ {
+ if ("auto".equals(atts.getValue(index)))
+ {
+ atts.setValue(index, "0pt");
+ }
+ }
+ }
+
+
+
+ private static void
+ setValue(AttributesImpl atts, String name, String value)
+ {
+ int index = atts.getIndex(Constants.CSS, name);
+
+ if (index != -1)
+ {
+ atts.setValue(index, value);
+ }
+ else
+ {
+ atts.addAttribute(Constants.CSS, name, "css:" + name, "CDATA", value);
+ }
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ String display = atts.getValue(Constants.CSS, "display");
+
+ if (display != null)
+ {
+ if (display.equals("inline"))
+ {
+ atts = correctInline(atts);
+ }
+ else
+ {
+ if
+ (
+ atts.getValue(Constants.CSS, "float") != null &&
+ !"none".equals(atts.getValue(Constants.CSS, "float"))
+ )
+ {
+ atts = correctFloat(atts);
+ }
+ else
+ {
+ // Absolute and fixed positioning is left to the following processor
+ // because layout calculation results are needed.
+
+ if
+ (
+ Util.inArray
+ (
+ new String[]
+ {"block", "compact", "list-item", "run-in", "table"},
+ display
+ ) &&
+ !"absolute".equals(atts.getValue(Constants.CSS, "position")) &&
+ !"fixed".equals(atts.getValue(Constants.CSS, "position")) &&
+ (
+ // If the parent is a table-cell we leave it (too complicated).
+ stack.isEmpty() ||
+ !"table-cell".equals
+ (
+ ((Attributes) stack.peek()).
+ getValue(Constants.CSS, "display")
+ )
+ )
+ )
+ {
+ atts =
+ correctBlock
+ (
+ atts,
+ !stack.isEmpty() ?
+ (Attributes) stack.peek() : new AttributesImpl()
+ );
+ }
+ }
+ }
+ }
+
+ stack.push(new AttributesImpl(atts));
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+} // WidthAndMarginsFilter
diff --git a/src/be/re/css/WrapperFilter.java b/src/be/re/css/WrapperFilter.java
new file mode 100644
index 0000000..0a87230
--- /dev/null
+++ b/src/be/re/css/WrapperFilter.java
@@ -0,0 +1,136 @@
+package be.re.css;
+
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This filter removes elements with the display type "wrapper". The inherited
+ * properties on the element are propagated to its children.
+ * @author Werner Donn\u00e9
+ */
+
+class WrapperFilter extends XMLFilterImpl
+
+{
+
+ private Stack elements = new Stack();
+
+
+
+ WrapperFilter()
+ {
+ }
+
+
+
+ WrapperFilter(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ if (!((Element) elements.pop()).remove)
+ {
+ super.endElement(namespaceURI, localName, qName);
+ }
+ }
+
+
+
+ private static Attributes
+ mergeInheritedProperties(Attributes atts, Attributes inheritedProperties)
+ {
+ return
+ inheritedProperties == null ?
+ atts : Util.mergeAttributes(inheritedProperties, atts);
+ }
+
+
+
+ private static Attributes
+ selectInheritedProperties(Attributes atts)
+ {
+ AttributesImpl result = new AttributesImpl();
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if
+ (
+ Constants.CSS.equals(atts.getURI(i)) &&
+ Util.isInherited(atts.getLocalName(i))
+ )
+ {
+ result.addAttribute
+ (
+ atts.getURI(i),
+ atts.getLocalName(i),
+ atts.getQName(i),
+ atts.getType(i),
+ atts.getValue(i)
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Element element = new Element();
+
+ if ("wrapper".equals(atts.getValue(Constants.CSS, "display")))
+ {
+ element.remove = true;
+ element.inheritedProperties = selectInheritedProperties(atts);
+ }
+ else
+ {
+ super.startElement
+ (
+ namespaceURI,
+ localName,
+ qName,
+ mergeInheritedProperties
+ (
+ atts,
+ elements.isEmpty() ?
+ null : ((Element) elements.peek()).inheritedProperties
+ )
+ );
+ }
+
+ elements.push(element);
+ }
+
+
+
+ private static class Element
+
+ {
+
+ private Attributes inheritedProperties;
+ private boolean remove;
+
+ } // Element
+
+} // WrapperFilter
diff --git a/src/be/re/css/XHTMLAttributeTranslationFilter.java b/src/be/re/css/XHTMLAttributeTranslationFilter.java
new file mode 100644
index 0000000..3172b51
--- /dev/null
+++ b/src/be/re/css/XHTMLAttributeTranslationFilter.java
@@ -0,0 +1,837 @@
+package be.re.css;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This filter translates XHTML attributes to the corresponding CSS properties.
+ * @author Werner Donn\u00e9
+ */
+
+class XHTMLAttributeTranslationFilter extends XMLFilterImpl
+
+{
+
+ private static final String COL = "col".intern();
+ private static final String COLGROUP = "colgroup".intern();
+ private static final String TABLE = "table".intern();
+ private static final String TBODY = "tbody".intern();
+ private static final String TFOOT = "tfoot".intern();
+ private static final String THEAD = "thead".intern();
+ private static final String TD = "td".intern();
+ private static final String TH = "th".intern();
+ private static final String TR = "tr".intern();
+
+ private static final Map map =
+ loadTable
+ (
+ new String[][]
+ { // element, attribute, attribute value, CSS property, CSS property value
+ {"applet", "align", "bottom", "vertical-align", "bottom"},
+ {"applet", "align", "left", "text-align", "left"},
+ {"applet", "align", "middle", "vertical-align", "middle"},
+ {"applet", "align", "right", "text-align", "right"},
+ {"applet", "align", "top", "vertical-align", "top"},
+ {"applet", "height", null, "height", null},
+ {"applet", "hspace", null, "margin-left", null},
+ {"applet", "hspace", null, "margin-right", null},
+ {"applet", "vspace", null, "margin-bottom", null},
+ {"applet", "vspace", null, "margin-top", null},
+ {"applet", "width", null, "width", null},
+ {"body", "background", null, "background-image", null},
+ {"body", "text", null, "color", null},
+ {"caption", "align", null, "caption-side", null},
+ {"col", "align", "center", "text-align", "center"},
+ {"col", "align", "char", "text-align", "@char;."},
+ {"col", "align", "justify", "text-align", "justify"},
+ {"col", "align", "left", "text-align", "left"},
+ {"col", "align", "right", "text-align", "right"},
+ {"col", "span", null, "span", null},
+ {"col", "valign", null, "vertical-align", null},
+ {"col", "width", null, "width", null},
+ {"colgroup", "align", "center", "text-align", "center"},
+ {"colgroup", "align", "char", "text-align", "@char;."},
+ {"colgroup", "align", "justify", "text-align", "justify"},
+ {"colgroup", "align", "left", "text-align", "left"},
+ {"colgroup", "align", "right", "text-align", "right"},
+ {"colgroup", "span", null, "span", null},
+ {"colgroup", "valign", null, "vertical-align", null},
+ {"colgroup", "width", null, "width", null},
+ {"div", "align", null, "text-align", null},
+ {"font", "color", null, "color", null},
+ {"font", "face", null, "font-family", null},
+ {"font", "size", null, "font-size", "f:fontSize"},
+ {"h1", "align", null, "text-align", null},
+ {"h2", "align", null, "text-align", null},
+ {"h3", "align", null, "text-align", null},
+ {"h4", "align", null, "text-align", null},
+ {"h5", "align", null, "text-align", null},
+ {"h6", "align", null, "text-align", null},
+ {"hr", "align", null, "text-align", null},
+ {"hr", "noshade", null, "border-bottom-style", "solid"},
+ {"hr", "noshade", null, "border-left-style", "solid"},
+ {"hr", "noshade", null, "border-right-style", "solid"},
+ {"hr", "noshade", null, "border-top-style", "solid"},
+ {"hr", "size", null, "height", null},
+ {"hr", "width", null, "width", null},
+ {"img", "border", null, "border-bottom-width", null},
+ {"img", "border", null, "border-left-width", null},
+ {"img", "border", null, "border-right-width", null},
+ {"img", "border", null, "border-top-width", null},
+ {"img", "border", null, "border-bottom-style", "solid"},
+ {"img", "border", null, "border-left-style", "solid"},
+ {"img", "border", null, "border-right-style", "solid"},
+ {"img", "border", null, "border-top-style", "solid"},
+ {"img", "border", null, "border-after-width.conditionality", "retain"},
+ {"img", "border", null, "border-before-width.conditionality", "retain"},
+ {"img", "height", null, "height", null},
+ {"img", "hspace", null, "margin-left", null},
+ {"img", "hspace", null, "margin-right", null},
+ {"img", "vspace", null, "margin-bottom", null},
+ {"img", "vspace", null, "margin-top", null},
+ {"img", "width", null, "width", null},
+ {"input", "align", null, "text-align", null},
+ {"object", "border", null, "border-bottom-width", null},
+ {"object", "border", null, "border-left-width", null},
+ {"object", "border", null, "border-right-width", null},
+ {"object", "border", null, "border-top-width", null},
+ {"object", "border", null, "border-bottom-style", "solid"},
+ {"object", "border", null, "border-left-style", "solid"},
+ {"object", "border", null, "border-right-style", "solid"},
+ {"object", "border", null, "border-top-style", "solid"},
+ {
+ "object", "border", null, "border-after-width.conditionality",
+ "retain"
+ },
+ {
+ "object", "border", null, "border-before-width.conditionality",
+ "retain"
+ },
+ {"li", "compact", null, "list-style-position", "inside"},
+ {"li", "type", null, "list-style-type", null},
+ {"object", "height", null, "height", null},
+ {"object", "hspace", null, "margin-left", null},
+ {"object", "hspace", null, "margin-right", null},
+ {"object", "vspace", null, "margin-bottom", null},
+ {"object", "vspace", null, "margin-top", null},
+ {"object", "width", null, "width", null},
+ {"ol", "compact", null, "list-style-position", "inside"},
+ {"ol", "type", "1", "list-style-type", "decimal"},
+ {"ol", "type", "a", "list-style-type", "lower-alpha"},
+ {"ol", "type", "A", "list-style-type", "upper-alpha"},
+ {"ol", "type", "i", "list-style-type", "lower-roman"},
+ {"ol", "type", "I", "list-style-type", "upper-roman"},
+ {"p", "align", null, "text-align", null},
+ {"span", "align", null, "text-align", null},
+ {"table", "width", null, "width", null},
+ {"tbody", "align", "center", "text-align", "center"},
+ {"tbody", "align", "char", "text-align", "@char;."},
+ {"tbody", "align", "justify", "text-align", "justify"},
+ {"tbody", "align", "left", "text-align", "left"},
+ {"tbody", "align", "right", "text-align", "right"},
+ {"tbody", "valign", null, "vertical-align", null},
+ {"td", "align", "center", "text-align", "center"},
+ {"td", "align", "char", "text-align", "@char;."},
+ {"td", "align", "justify", "text-align", "justify"},
+ {"td", "align", "left", "text-align", "left"},
+ {"td", "align", "right", "text-align", "right"},
+ {"td", "colspan", null, "colspan", null},
+ {"td", "height", null, "height", null},
+ {"td", "nowrap", null, "white-space", "nowrap"},
+ {"td", "rowspan", null, "rowspan", null},
+ {"td", "valign", null, "vertical-align", null},
+ {"td", "width", null, "width", null},
+ {"tfoot", "align", "center", "text-align", "center"},
+ {"tfoot", "align", "char", "text-align", "@char;."},
+ {"tfoot", "align", "justify", "text-align", "justify"},
+ {"tfoot", "align", "left", "text-align", "left"},
+ {"tfoot", "align", "right", "text-align", "right"},
+ {"tfoot", "valign", null, "vertical-align", null},
+ {"th", "align", "center", "text-align", "center"},
+ {"th", "align", "char", "text-align", "@char;."},
+ {"th", "align", "justify", "text-align", "justify"},
+ {"th", "align", "left", "text-align", "left"},
+ {"th", "align", "right", "text-align", "right"},
+ {"th", "colspan", null, "colspan", null},
+ {"th", "height", null, "height", null},
+ {"th", "nowrap", null, "white-space", "nowrap"},
+ {"th", "rowspan", null, "rowspan", null},
+ {"th", "valign", null, "vertical-align", null},
+ {"th", "width", null, "width", null},
+ {"thead", "align", "center", "text-align", "center"},
+ {"thead", "align", "char", "text-align", "@char;."},
+ {"thead", "align", "justify", "text-align", "justify"},
+ {"thead", "align", "left", "text-align", "left"},
+ {"thead", "align", "right", "text-align", "right"},
+ {"thead", "valign", null, "vertical-align", null},
+ {"tr", "align", "center", "text-align", "center"},
+ {"tr", "align", "char", "text-align", "@char;."},
+ {"tr", "align", "justify", "text-align", "justify"},
+ {"tr", "align", "left", "text-align", "left"},
+ {"tr", "align", "right", "text-align", "right"},
+ {"tr", "bgcolor", null, "background-color", null},
+ {"tr", "valign", null, "vertical-align", null},
+ {"ul", "compact", null, "list-style-position", "inside"},
+ {"ul", "type", null, "list-style-type", null}
+ }
+ );
+
+ private String defaultBorderThickness;
+ private Stack elementStack = new Stack();
+ private Stack tableStack = new Stack();
+
+
+
+ XHTMLAttributeTranslationFilter()
+ {
+ this("0.2pt");
+ }
+
+
+
+ XHTMLAttributeTranslationFilter(String defaultBorderThickness)
+ {
+ this.defaultBorderThickness = defaultBorderThickness;
+ }
+
+
+
+ XHTMLAttributeTranslationFilter(XMLReader parent)
+ {
+ this(parent, "0.2pt");
+ }
+
+
+
+ XHTMLAttributeTranslationFilter
+ (
+ XMLReader parent,
+ String defaultBorderThickness
+ )
+ {
+ super(parent);
+ this.defaultBorderThickness = defaultBorderThickness;
+ }
+
+
+
+ private static String
+ callPropertyResolver
+ (
+ String function,
+ String element,
+ Attributes atts,
+ String attribute,
+ String value
+ )
+ {
+ try
+ {
+ return
+ (String)
+ XHTMLAttributeTranslationFilter.class.getDeclaredMethod
+ (
+ function,
+ new Class[]
+ {String.class, Attributes.class, String.class, String.class}
+ ).invoke(null, new Object[] {element, atts, attribute, value});
+ }
+
+ catch (Exception e)
+ {
+ return value;
+ }
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ if (Constants.XHTML == namespaceURI)
+ {
+ elementStack.pop();
+
+ if (TABLE == localName)
+ {
+ tableStack.pop();
+ }
+ }
+
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+
+
+ private static boolean
+ equivalentSibling(String element1, String element2)
+ {
+ return
+ element1 == element2 ||
+ (
+ (THEAD == element1 || TBODY == element1 || TFOOT == element1) &&
+ (THEAD == element2 || TBODY == element2 || TFOOT == element2)
+ );
+ }
+
+
+
+ private static String
+ fontSize(String element, Attributes atts, String attribute, String value)
+ {
+ try
+ {
+ return
+ value.startsWith("+") ?
+ (
+ String.valueOf
+ (
+ (int)
+ (
+ 100.0 *
+ (100 + 10 * Integer.parseInt(value.substring(1))) / 100
+ )
+ ) + "%"
+ ) :
+ (
+ value.startsWith("-") ?
+ (
+ String.valueOf
+ (
+ (int)
+ (
+ 100.0 *
+ (100 - 10 * Integer.parseInt(value.substring(1))) / 100
+ )
+ ) + "%"
+ ) : (String.valueOf((Integer.parseInt(value) + 7)) + "pt")
+ );
+ }
+
+ catch (Exception e)
+ {
+ return "100%";
+ }
+ }
+
+
+
+ private static Map
+ loadTable(String[][] table)
+ {
+ Map result = new HashMap();
+
+ for (int i = 0; i < table.length; ++i)
+ {
+ String key = table[i][0] + "#" + table[i][1];
+ List tuples = (List) result.get(key);
+
+ if (tuples == null)
+ {
+ tuples = new ArrayList();
+ result.put(key, tuples);
+ }
+
+ tuples.add(new Tuple(table[i][2], table[i][3], table[i][4]));
+ }
+
+ return result;
+ }
+
+
+
+ private static Tuple[]
+ lookup(String element, Attributes atts, String attribute, String value)
+ {
+ List tuples = (List) map.get(element + "#" + attribute);
+
+ if (tuples == null)
+ {
+ return new Tuple[0];
+ }
+
+ List result = new ArrayList();
+
+ for (int i = 0; i < tuples.size(); ++i)
+ {
+ Tuple tuple = (Tuple) tuples.get(i);
+
+ if (tuple.inValue == null || tuple.inValue.equals(value))
+ {
+ String otherAttribute;
+
+ result.add
+ (
+ new Tuple
+ (
+ tuple.inValue,
+ tuple.property,
+ tuple.outValue == null ?
+ value :
+ (
+ tuple.outValue.charAt(0) == '@' ?
+ (
+ (
+ otherAttribute =
+ atts.getValue
+ (
+ tuple.outValue.
+ substring(1, tuple.outValue.indexOf(';'))
+ )
+ ) != null ?
+ otherAttribute :
+ tuple.outValue.substring(tuple.outValue.indexOf(';') + 1)
+ ) :
+ (
+ tuple.outValue.startsWith("f:") ?
+ callPropertyResolver
+ (
+ tuple.outValue.substring(2),
+ element,
+ atts,
+ attribute,
+ value
+ ) : tuple.outValue
+ )
+ )
+ )
+ );
+ }
+ }
+
+ return (Tuple[]) result.toArray(new Tuple[result.size()]);
+ }
+
+
+
+ private static void
+ mergeAttribute
+ (
+ AttributesImpl atts,
+ String originalName,
+ String cssName,
+ String value
+ )
+ {
+ int index1 = atts.getIndex(Constants.CSS, cssName);
+
+ if (index1 == -1)
+ {
+ atts.
+ addAttribute(Constants.CSS, cssName, "css:" + cssName, "CDATA", value);
+ }
+ else
+ {
+ int index2 = atts.getIndex(Constants.SPECIF, cssName);
+
+ if (index2 != -1)
+ {
+ atts.setValue(index1, value);
+ }
+ }
+
+ if (originalName != null)
+ {
+ index1 = atts.getIndex(originalName);
+
+ if (index1 != -1)
+ {
+ atts.removeAttribute(index1);
+ }
+ }
+ }
+
+
+
+ private AttributesImpl
+ prepareTableAttributes(String localName, Attributes atts)
+ {
+ return
+ TD == localName || TH == localName ?
+ preprocessTableCell
+ (
+ localName,
+ atts,
+ new String[] {"all", "cols"},
+ "left"
+ ) :
+ (
+ TR == localName ?
+ preprocessRulesBorder
+ (
+ localName,
+ atts,
+ new String[] {"all", "rows"},
+ "top"
+ ) :
+ (
+ COL == localName ?
+ preprocessRulesBorder
+ (
+ localName,
+ atts,
+ new String[] {"all", "cols"},
+ "left"
+ ) :
+ (
+ TABLE == localName ?
+ preprocessTableBorder(atts) :
+ (
+ THEAD == localName || TFOOT == localName ||
+ TBODY == localName ?
+ preprocessRulesBorder
+ (
+ localName,
+ atts,
+ new String[] {"all", "rows", "groups"},
+ "top"
+ ) :
+ (
+ COLGROUP == localName ?
+ preprocessRulesBorder
+ (
+ localName,
+ atts,
+ new String[] {"groups"},
+ "left"
+ ) : new AttributesImpl(atts)
+ )
+ )
+ )
+ )
+ );
+ }
+
+
+
+ private AttributesImpl
+ preprocessRulesBorder
+ (
+ String localName,
+ Attributes atts,
+ String[] rulesValues,
+ String borderSide
+ )
+ {
+ Element table = (Element) tableStack.peek();
+ String border = table.atts.getValue("border");
+ String rules = table.atts.getValue("rules");
+ AttributesImpl result = new AttributesImpl(atts);
+
+ if ("0".equals(border) || "none".equals(rules))
+ {
+ return result;
+ }
+
+ String borderWidth =
+ border == null ? defaultBorderThickness : (border + "px");
+ Element parent = (Element) elementStack.peek();
+
+ if
+ (
+ (
+ (
+ border != null &&
+ rules == null
+ ) ||
+ Util.inArray(rulesValues, rules)
+ ) &&
+ equivalentSibling(localName, ((Preceding) parent.extra).element) &&
+ ((Preceding) parent.extra).count > 0
+ )
+ {
+ mergeAttribute
+ (
+ result,
+ "border",
+ "border-" + borderSide + "-width",
+ borderWidth
+ );
+
+ mergeAttribute
+ (
+ result,
+ null, // Removed already.
+ "border-" + borderSide + "-style",
+ "solid"
+ );
+
+ if ("bottom".equals(borderSide))
+ {
+ mergeAttribute
+ (
+ result,
+ null, // Removed already.
+ "border-after-width.conditionality",
+ "retain"
+ );
+ }
+
+ if ("top".equals(borderSide))
+ {
+ mergeAttribute
+ (
+ result,
+ null, // Removed already.
+ "border-before-width.conditionality",
+ "retain"
+ );
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private AttributesImpl
+ preprocessTableBorder(Attributes atts)
+ {
+ Element table = (Element) tableStack.peek();
+ String border = table.atts.getValue("border");
+ String frame = table.atts.getValue("frame");
+ AttributesImpl result = new AttributesImpl(atts);
+
+ if ("0".equals(border) || "void".equals(frame))
+ {
+ return result;
+ }
+
+ String borderWidth =
+ border == null ? defaultBorderThickness : (border + "px");
+
+ if
+ (
+ (
+ border != null &&
+ frame == null
+ ) ||
+ Util.inArray(new String[] {"above", "hsides", "box", "border"}, frame)
+ )
+ {
+ mergeAttribute(result, "border", "border-top-width", borderWidth);
+ mergeAttribute(result, null, "border-top-style", "solid");
+
+ mergeAttribute
+ (
+ result,
+ null, // Removed already
+ "border-before-width.conditionality",
+ "retain"
+ );
+ }
+
+ if
+ (
+ (
+ border != null &&
+ frame == null
+ ) ||
+ Util.inArray(new String[] {"below", "hsides", "box", "border"}, frame)
+ )
+ {
+ mergeAttribute(result, "border", "border-bottom-width", borderWidth);
+ mergeAttribute(result, null, "border-bottom-style", "solid");
+
+ mergeAttribute
+ (
+ result,
+ null, // Removed already
+ "border-after-width.conditionality",
+ "retain"
+ );
+ }
+
+ if
+ (
+ (
+ border != null &&
+ frame == null
+ ) ||
+ Util.inArray(new String[] {"lhs", "vsides", "box", "border"}, frame)
+ )
+ {
+ mergeAttribute(result, "border", "border-left-width", borderWidth);
+ mergeAttribute(result, null, "border-left-style", "solid");
+ }
+
+ if
+ (
+ (
+ border != null &&
+ frame == null
+ ) ||
+ Util.inArray(new String[] {"rhs", "vsides", "box", "border"}, frame)
+ )
+ {
+ mergeAttribute(result, "border", "border-right-width", borderWidth);
+ mergeAttribute(result, null, "border-right-style", "solid");
+ }
+
+ return result;
+ }
+
+
+
+ private AttributesImpl
+ preprocessTableCell
+ (
+ String localName,
+ Attributes atts,
+ String[] rulesValues,
+ String borderSide
+ )
+ {
+ // If there are columns the normal column propagation can take place. Else
+ // we should place the borders directly on the cells.
+
+ return
+ preprocessRulesBorder
+ (
+ localName,
+ preprocessTableCellPaddingAndSpacing(atts),
+ rulesValues,
+ borderSide
+ );
+ }
+
+
+
+ private AttributesImpl
+ preprocessTableCellPaddingAndSpacing(Attributes atts)
+ {
+ Element table = (Element) tableStack.peek();
+ String padding = table.atts.getValue("cellpadding");
+ AttributesImpl result = new AttributesImpl(atts);
+ String spacing = table.atts.getValue("cellspacing");
+
+ if (padding != null)
+ {
+ mergeAttribute(result, null, "padding-top", padding);
+ mergeAttribute(result, null, "padding-bottom", padding);
+ mergeAttribute(result, null, "padding-left", padding);
+ mergeAttribute(result, null, "padding-right", padding);
+ }
+
+ if (spacing != null)
+ {
+ mergeAttribute(result, null, "margin-top", spacing);
+ mergeAttribute(result, null, "margin-bottom", spacing);
+ mergeAttribute(result, null, "margin-left", spacing);
+ mergeAttribute(result, null, "margin-right", spacing);
+ }
+
+ return result;
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ if (Constants.XHTML == namespaceURI)
+ {
+ Element element = new Element(namespaceURI, localName, qName, atts);
+
+ if (TABLE == localName)
+ {
+ element.extra = new Boolean(false); // No columns seen yet.
+ tableStack.push(element);
+ }
+
+ AttributesImpl newAtts = prepareTableAttributes(localName, atts);
+
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if (atts.getURI(i).equals(""))
+ {
+ Tuple[] tuples =
+ lookup(localName, atts, atts.getLocalName(i), atts.getValue(i));
+
+ for (int j = 0; j < tuples.length; ++j)
+ {
+ mergeAttribute
+ (
+ newAtts,
+ atts.getLocalName(i),
+ tuples[j].property,
+ tuples[j].outValue
+ );
+ }
+ }
+ }
+
+ Element parent =
+ elementStack.empty() ? null : (Element) elementStack.peek();
+
+ if (parent != null)
+ {
+ ((Preceding) parent.extra).count =
+ equivalentSibling(localName, ((Preceding) parent.extra).element) ?
+ (((Preceding) parent.extra).count + 1) :
+ 1;
+ ((Preceding) parent.extra).element = localName;
+ }
+
+ element.extra = new Preceding();
+ elementStack.push(element);
+
+ super.startElement(namespaceURI, localName, qName, newAtts);
+ }
+ else
+ {
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+ }
+
+
+
+ private static class Preceding
+
+ {
+
+ private int count = 0;
+ private String element;
+
+ } // Preceding
+
+
+
+ private static class Tuple
+
+ {
+
+ private String inValue;
+ private String outValue;
+ private String property;
+
+
+
+ private
+ Tuple(String inValue, String property, String outValue)
+ {
+ this.inValue = inValue;
+ this.property = property;
+ this.outValue = outValue;
+ }
+
+ } // Tuple
+
+} // XHTMLAttributeTranslationFilter
diff --git a/src/be/re/css/ant/CSSToFOP.java b/src/be/re/css/ant/CSSToFOP.java
new file mode 100644
index 0000000..c1755fa
--- /dev/null
+++ b/src/be/re/css/ant/CSSToFOP.java
@@ -0,0 +1,81 @@
+package be.re.css.ant;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import org.apache.fop.apps.Driver;
+import org.apache.fop.apps.Options;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToFOP extends TaskBase
+
+{
+
+ private File configuration = null;
+ private boolean quite = false;
+
+
+
+ public void
+ execute() throws BuildException
+ {
+ try
+ {
+ if (configuration != null)
+ {
+ new Options(configuration);
+ }
+
+ be.re.css.CSSToFOP.convert
+ (
+ input.openStream(),
+ new FileOutputStream(output),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToFOP.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ output.toString().toLowerCase().endsWith(".pdf") ?
+ Driver.RENDER_PDF :
+ (
+ output.toString().toLowerCase().endsWith(".ps") ?
+ Driver.RENDER_PS :
+ (
+ output.toString().toLowerCase().endsWith(".svg") ?
+ Driver.RENDER_SVG : -1
+ )
+ ),
+ true,
+ validate
+ );
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+ }
+
+
+
+ public void
+ setConfig(File value)
+ {
+ configuration = value;
+ }
+
+
+
+ public void
+ setQuite(boolean value)
+ {
+ quite = value;
+ }
+
+} // CSSToFOP
diff --git a/src/be/re/css/ant/CSSToFOPNew.java b/src/be/re/css/ant/CSSToFOPNew.java
new file mode 100644
index 0000000..bba7bcd
--- /dev/null
+++ b/src/be/re/css/ant/CSSToFOPNew.java
@@ -0,0 +1,65 @@
+package be.re.css.ant;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToFOPNew extends TaskBase
+
+{
+
+ private File configuration = null;
+
+
+
+ public void
+ execute() throws BuildException
+ {
+ try
+ {
+ be.re.css.CSSToFOPNew.convert
+ (
+ input.openStream(),
+ new FileOutputStream(output),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToFOPNew.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ output.toString().toLowerCase().endsWith(".pdf") ?
+ "application/pdf" :
+ (
+ output.toString().toLowerCase().endsWith(".ps") ?
+ "application/postscript" :
+ (
+ output.toString().toLowerCase().endsWith(".svg") ?
+ "image/svg+xml" : null
+ )
+ ),
+ configuration,
+ validate
+ );
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+ }
+
+
+
+ public void
+ setConfig(File value)
+ {
+ value = configuration;
+ }
+
+} // CSSToFOPNew
diff --git a/src/be/re/css/ant/CSSToXEP.java b/src/be/re/css/ant/CSSToXEP.java
new file mode 100644
index 0000000..5b0f08a
--- /dev/null
+++ b/src/be/re/css/ant/CSSToXEP.java
@@ -0,0 +1,71 @@
+package be.re.css.ant;
+
+import java.io.FileOutputStream;
+import java.net.URL;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXEP extends TaskBase
+
+{
+
+ private URL configuration = null;
+ private boolean quiet = false;
+
+
+
+ public void
+ execute() throws BuildException
+ {
+ try
+ {
+ be.re.css.CSSToXEP.convert
+ (
+ input.openStream(),
+ new FileOutputStream(output),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXEP.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ output.toString().toLowerCase().endsWith(".pdf") ?
+ be.re.css.CSSToXEP.PDF :
+ (
+ output.toString().toLowerCase().endsWith(".ps") ?
+ be.re.css.CSSToXEP.POSTSCRIPT : -1
+ ),
+ validate,
+ quiet,
+ configuration
+ );
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+ }
+
+
+
+ public void
+ setConfig(String value)
+ {
+ configuration = createUrl(value);
+ }
+
+
+
+ public void
+ setQuiet(boolean value)
+ {
+ quiet = value;
+ }
+
+} // CSSToXEP
diff --git a/src/be/re/css/ant/CSSToXSLFO.java b/src/be/re/css/ant/CSSToXSLFO.java
new file mode 100644
index 0000000..36c5a85
--- /dev/null
+++ b/src/be/re/css/ant/CSSToXSLFO.java
@@ -0,0 +1,42 @@
+package be.re.css.ant;
+
+import java.io.FileOutputStream;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXSLFO extends TaskBase
+
+{
+
+ public void
+ execute() throws BuildException
+ {
+ try
+ {
+ be.re.css.CSSToXSLFO.convert
+ (
+ input.openStream(),
+ new FileOutputStream(output),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXSLFO.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ validate,
+ false
+ );
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+ }
+
+} // CSSToXSLFO
diff --git a/src/be/re/css/ant/CSSToXSLFormatter.java b/src/be/re/css/ant/CSSToXSLFormatter.java
new file mode 100644
index 0000000..ba0b6c1
--- /dev/null
+++ b/src/be/re/css/ant/CSSToXSLFormatter.java
@@ -0,0 +1,61 @@
+package be.re.css.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import jp.co.antenna.XfoJavaCtl.XfoObj;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXSLFormatter extends TaskBase
+
+{
+
+ public void
+ execute() throws BuildException
+ {
+ File f = null;
+
+ try
+ {
+ f = File.createTempFile("be.re.css.", "css2xslformatter");
+ f.deleteOnExit();
+
+ be.re.css.CSSToXSLFO.convert
+ (
+ input.openStream(),
+ new FileOutputStream(f),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ?
+ catalog : CSSToXSLFormatter.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ validate,
+ false
+ );
+
+ new XfoObj().render(new FileInputStream(f), new FileOutputStream(output));
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+
+ finally
+ {
+ if (f != null)
+ {
+ f.delete();
+ }
+ }
+ }
+
+} // CSSToXSLFormatter
diff --git a/src/be/re/css/ant/CSSToXinc.java b/src/be/re/css/ant/CSSToXinc.java
new file mode 100644
index 0000000..d9b8275
--- /dev/null
+++ b/src/be/re/css/ant/CSSToXinc.java
@@ -0,0 +1,42 @@
+package be.re.css.ant;
+
+import java.io.FileOutputStream;
+import org.apache.tools.ant.BuildException;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class CSSToXinc extends TaskBase
+
+{
+
+ public void
+ execute() throws BuildException
+ {
+ try
+ {
+ be.re.css.CSSToXinc.convert
+ (
+ input.openStream(),
+ new FileOutputStream(output),
+ baseUrl != null ? baseUrl : input,
+ userAgentStyleSheet,
+ catalog != null ? catalog : CSSToXinc.class.getResource("/catalog"),
+ parameters,
+ preprocessors,
+ be.re.css.CSSToXinc.PDF,
+ validate
+ );
+ }
+
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new BuildException(e);
+ }
+ }
+
+} // CSSToXinc
diff --git a/src/be/re/css/ant/TaskBase.java b/src/be/re/css/ant/TaskBase.java
new file mode 100644
index 0000000..52eee3a
--- /dev/null
+++ b/src/be/re/css/ant/TaskBase.java
@@ -0,0 +1,164 @@
+package be.re.css.ant;
+
+import be.re.util.Array;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.tools.ant.Task;
+
+
+
+/**
+ * The base class for all CSSToXSLFO tasks.
+ * @author Werner Donn\u00e9
+ */
+
+public abstract class TaskBase extends Task
+
+{
+
+ protected URL baseUrl;
+ protected URL catalog;
+ protected URL input;
+ protected File output;
+ protected Map parameters = new HashMap();
+ protected URL[] preprocessors = new URL[0];
+ protected URL userAgentStyleSheet = null;
+ protected boolean validate = false;
+
+
+
+ public Parameter
+ createParameter()
+ {
+ return new Parameter();
+ }
+
+
+
+ public Preprocessor
+ createPreprocessor()
+ {
+ return new Preprocessor();
+ }
+
+
+
+ protected URL
+ createUrl(String s)
+ {
+ try
+ {
+ return
+ be.re.net.Util.isUrl(s) ?
+ new URL(s) : be.re.net.Util.fileToUrl(new File(s));
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+
+ public void
+ setBaseurl(String value)
+ {
+ baseUrl = createUrl(value);
+ }
+
+
+
+ public void
+ setCatalog(String value)
+ {
+ catalog = createUrl(value);
+ }
+
+
+
+ public void
+ setInput(String value)
+ {
+ input = createUrl(value);
+ }
+
+
+
+ public void
+ setOutput(File value)
+ {
+ output = value;
+ }
+
+
+
+ public void
+ setUseragentstylesheet(String value)
+ {
+ userAgentStyleSheet = createUrl(value);
+ }
+
+
+
+ public void
+ setValidate(boolean value)
+ {
+ validate = value;
+ }
+
+
+
+ public class Parameter
+
+ {
+
+ private String name = null;
+ private String value = null;
+
+
+
+ public void
+ setName(String value)
+ {
+ name = value;
+
+ if (this.value != null)
+ {
+ parameters.put(name, this.value);
+ }
+ }
+
+
+
+ public void
+ setValue(String value)
+ {
+ this.value = value;
+
+ if (name != null)
+ {
+ parameters.put(name, this.value);
+ }
+ }
+
+ } // Parameter
+
+
+
+ public class Preprocessor
+
+ {
+
+ public void
+ setStylesheet(String value)
+ {
+ preprocessors = (URL[]) Array.append(preprocessors, createUrl(value));
+ }
+
+ } // Preprocessor
+
+} // TaskBase
diff --git a/src/be/re/css/res/space_correction.prop b/src/be/re/css/res/space_correction.prop
new file mode 100644
index 0000000..ca5db67
--- /dev/null
+++ b/src/be/re/css/res/space_correction.prop
@@ -0,0 +1,12 @@
+#Should only have entries for U+2000 through U+200A. The rest is ignored.
+2000=0.5em
+2001=1em
+2002=0.5em
+2003=1em
+2004=0.33em
+2005=0.25em
+2006=0.16em
+2007=
+2008=
+2009=0.2em
+200A=0.1em
diff --git a/src/be/re/css/style/css.xsl b/src/be/re/css/style/css.xsl
new file mode 100644
index 0000000..99b2f90
--- /dev/null
+++ b/src/be/re/css/style/css.xsl
@@ -0,0 +1,1043 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:css="http://www.w3.org/1998/CSS" xmlns:rx="http://www.renderx.com/XSL/Extensions" xmlns:exsl="http://exslt.org/common" version="1.0" exclude-result-prefixes="css exsl">
+
+ <xsl:include href="fo_setup.xsl" />
+ <xsl:include href="util.xsl" />
+
+ <xsl:output method="xml" indent="no" version="1.0" encoding="UTF-8" omit-xml-declaration="no" />
+
+
+
+ <!-- User-defined page set-up. -->
+
+ <xsl:template match="/css:root[css:pages]" mode="setup">
+ <fo:root font-selection-strategy="character-by-character" line-height-shift-adjustment="disregard-shifts" font-family="{$font-family}" font-size="{$actual-font-size}" language="{$language}">
+ <!--
+ The document element's properties are put here for inheritance
+ purposes. The display property has no effect because pages are
+ generated for the top-level elements.
+ -->
+ <xsl:apply-templates select="@*" />
+ <xsl:if test="not(@xml:lang) and not(@lang)">
+ <xsl:attribute name="country">
+ <xsl:value-of select="$country" />
+ </xsl:attribute>
+ </xsl:if>
+ <fo:layout-master-set>
+ <xsl:apply-templates select="css:pages/css:page" />
+ <!--
+ If a named page exists then it is always split in six specific
+ versions.
+ -->
+ <xsl:apply-templates select="css:pages/css:page[starts-with(@css:name, 'first-left-')]" mode="master" />
+ </fo:layout-master-set>
+ <xsl:apply-templates select="css:page-sequence" />
+ </fo:root>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page[starts-with(@css:name, 'first-left-')]" mode="master">
+ <xsl:variable name="name" select="substring-after(@css:name, 'first-left-')" />
+ <fo:page-sequence-master master-name="{$name}">
+ <fo:repeatable-page-master-alternatives>
+ <!--
+ The following references are always generated. If the CSS style
+ sheet hasn't specified them they will be equal to the named
+ page definition.
+ -->
+ <fo:conditional-page-master-reference odd-or-even="odd" blank-or-not-blank="not-blank" page-position="first" master-reference="{concat('first-right-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="even" blank-or-not-blank="not-blank" page-position="first" master-reference="{concat('first-left-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="odd" blank-or-not-blank="not-blank" page-position="last" master-reference="{concat('last-right-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="even" blank-or-not-blank="not-blank" page-position="last" master-reference="{concat('last-left-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="odd" blank-or-not-blank="not-blank" page-position="any" master-reference="{concat('right-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="even" blank-or-not-blank="not-blank" page-position="any" master-reference="{concat('left-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="odd" blank-or-not-blank="blank" page-position="any" master-reference="{concat('blank-right-', $name)}" />
+ <fo:conditional-page-master-reference odd-or-even="even" blank-or-not-blank="blank" page-position="any" master-reference="{concat('blank-left-', $name)}" />
+ </fo:repeatable-page-master-alternatives>
+ </fo:page-sequence-master>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page">
+ <fo:simple-page-master master-name="{@css:name}" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left}" margin-right="{$paper-margin-right}">
+ <xsl:apply-templates select="@*[name() != 'css:column-count' and name() != 'css:force-page-count' and name() != 'css:initial-page-number' and name() != 'css:column-gap']" />
+ <xsl:apply-templates select="*" />
+ </fo:simple-page-master>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page/@css:force-page-count" priority="1">
+ <xsl:attribute name="force-page-count">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page/@css:initial-page-number" priority="1">
+ <xsl:attribute name="initial-page-number">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <!-- Don't generate empty page sequences. -->
+
+ <xsl:template match="css:page-sequence[(not(css:regions/following-sibling::*//*[(@css:display = 'block' or @css:display = 'table') and @css:page]/*) and not(css:regions/following-sibling::*//*[@css:display = 'block' and @css:page]/text()) and not(css:regions/following-sibling::*//*[(@css:display = 'block' or @css:display = 'table') and @css:page]/following-sibling::*) and @css:page != 'unnamed') or (not(*[@css:display = 'block' or @css:display = 'table']/*) and not(*[@css: [...]
+
+
+
+ <xsl:template match="css:page-sequence">
+ <xsl:variable name="page-name" select="@css:page" />
+ <fo:page-sequence format="1" master-reference="unnamed">
+ <xsl:apply-templates select="@css:page" />
+ <xsl:apply-templates select="/css:root/css:pages/css:page [@css:name = concat('first-left-', $page-name)]/ @css:initial-page-number" />
+ <xsl:apply-templates select="/css:root/css:pages/css:page [@css:name = concat('first-left-', $page-name)]/ @css:force-page-count" />
+ <xsl:apply-templates select="css:regions//css:page-number" mode="page-number-setup" />
+ <xsl:apply-templates select="css:regions/fo:static-content" />
+ <fo:static-content flow-name="xsl-footnote-separator">
+ <xsl:call-template name="footnote-separator" />
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <xsl:apply-templates select="css:regions/following-sibling::*" />
+ </fo:flow>
+ </fo:page-sequence>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-sequence/@css:page" priority="1">
+ <xsl:attribute name="master-reference">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <!-- Default orientation, i.e. defined from outside. -->
+ <xsl:template match="css:page/@css:size[. = 'auto' or . = 'portrait']" priority="1" />
+
+
+
+ <xsl:template match="css:page/@css:size[. = 'landscape']" priority="1">
+ <xsl:attribute name="page-height">
+ <xsl:value-of select="$paper-width" />
+ </xsl:attribute>
+ <xsl:attribute name="page-width">
+ <xsl:value-of select="$paper-height" />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page/@css:size [. != 'auto' and . != 'landscape' and . != 'portrait' and . != 'inherit']" priority="1">
+ <xsl:variable name="tokens">
+ <xsl:call-template name="count-tokens">
+ <xsl:with-param name="s" select="." />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$tokens = 2">
+ <xsl:attribute name="page-width">
+ <xsl:call-template name="get-token">
+ <xsl:with-param name="position" select="1" />
+ <xsl:with-param name="s" select="." />
+ </xsl:call-template>
+ </xsl:attribute>
+ <xsl:attribute name="page-height">
+ <xsl:call-template name="get-token">
+ <xsl:with-param name="position" select="2" />
+ <xsl:with-param name="s" select="." />
+ </xsl:call-template>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="page-width">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ <xsl:attribute name="page-height">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <!-- General templates. -->
+
+ <xsl:template match="@*" priority="-1" />
+
+
+
+ <xsl:template match="fo:*">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:*/@*[not(starts-with(name(), 'css:'))]">
+ <xsl:copy />
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:instream-foreign-object">
+ <xsl:copy-of select="." />
+ </xsl:template>
+
+
+
+ <xsl:template match="@id | @ID | @xml:id" priority="1">
+ <xsl:attribute name="id">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@xml:lang | @lang">
+ <xsl:choose>
+ <xsl:when test="contains(., '-')">
+ <xsl:attribute name="language">
+ <xsl:value-of select="substring-before(., '-')" />
+ </xsl:attribute>
+ <xsl:attribute name="country">
+ <xsl:value-of select="substring-after(., '-')" />
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="language">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:*">
+ <xsl:attribute name="{local-name()}">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:content" priority="0.5" />
+ <xsl:template match="@css:counter-increment" priority="0.5" />
+ <xsl:template match="@css:counter-reset" priority="0.5" />
+ <xsl:template match="@css:cursor" priority="0.5" />
+ <xsl:template match="@css:display" priority="0.5" />
+ <xsl:template match="@css:force-page-count" priority="0.5" />
+ <xsl:template match="@css:format" priority="0.5" />
+ <xsl:template match="@css:initial-page-number" priority="0.5" />
+ <xsl:template match="@css:letter-value" priority="0.5" />
+ <xsl:template match="@css:list-label-width" priority="0.5" />
+ <xsl:template match="@css:list-style" priority="0.5" />
+ <xsl:template match="@css:list-style-image" priority="0.5" />
+ <xsl:template match="@css:list-style-position" priority="0.5" />
+ <xsl:template match="@css:list-style-type" priority="0.5" />
+ <xsl:template match="@css:marker-offset" priority="0.5" />
+ <xsl:template match="@css:marks" priority="0.5" />
+ <xsl:template match="@css:name" priority="0.5" />
+ <xsl:template match="@css:outline" priority="0.5" />
+ <xsl:template match="@css:outline-color" priority="0.5" />
+ <xsl:template match="@css:outline-style" priority="0.5" />
+ <xsl:template match="@css:outline-width" priority="0.5" />
+ <xsl:template match="@css:page" priority="0.5" />
+ <xsl:template match="@css:precedence" priority="0.5" />
+ <xsl:template match="@css:quotes" priority="0.5" />
+ <xsl:template match="@css:region" priority="0.5" />
+ <xsl:template match="@css:span" priority="0.5" />
+
+
+
+ <xsl:template match="*[@css:display = 'block']">
+ <fo:block margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@* | node()" />
+ </fo:block>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:orientation" priority="1">
+ <xsl:attribute name="reference-orientation">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:position[. = 'relative' or . = 'static']" priority="1">
+ <xsl:attribute name="relative-position">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:position[. = 'absolute' or . = 'fixed']" priority="1">
+ <xsl:attribute name="absolute-position">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'inline']">
+ <fo:inline>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:inline>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'none']" />
+
+
+
+ <xsl:template match="*[@css:display = 'graphic']">
+ <fo:external-graphic>
+ <xsl:apply-templates select="@*[name() != 'css:color' and not(starts-with(name(), 'css:font'))] | node()" />
+ </fo:external-graphic>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:src" priority="1">
+ <xsl:attribute name="src">
+ <xsl:value-of select="concat('url(', ., ')')" />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'list-item']">
+ <xsl:variable name="list-style-type">
+ <xsl:apply-templates select="." mode="list-style-type" />
+ </xsl:variable>
+ <xsl:variable name="list-style-position">
+ <xsl:apply-templates select="." mode="list-style-position" />
+ </xsl:variable>
+ <fo:list-item>
+ <xsl:choose>
+ <xsl:when test="$list-style-type != 'none' and $list-style-position = 'outside'">
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block text-align="end">
+ <xsl:call-template name="list-label">
+ <xsl:with-param name="list-type" select="$list-style-type" />
+ </xsl:call-template>
+ </fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:block>
+ </fo:list-item-body>
+ </xsl:when>
+ <xsl:otherwise>
+ <fo:list-item-label end-indent="0">
+ <fo:block />
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="0">
+ <fo:block>
+ <xsl:apply-templates select="@*" />
+ <xsl:if test="$list-style-type != 'none'">
+ <fo:inline>
+ <xsl:attribute name="width">
+ <xsl:call-template name="get-list-label-width">
+ <xsl:with-param name="list-type" select="$list-style-type" />
+ </xsl:call-template>
+ </xsl:attribute>
+ <xsl:call-template name="list-label">
+ <xsl:with-param name="list-type" select="$list-style-type" />
+ </xsl:call-template>
+ </fo:inline>
+ </xsl:if>
+ <xsl:apply-templates select="node()" />
+ </fo:block>
+ </fo:list-item-body>
+ </xsl:otherwise>
+ </xsl:choose>
+ </fo:list-item>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[*[@css:display = 'list-item']]" priority="1">
+ <xsl:variable name="list-style-type">
+ <xsl:apply-templates select="*[@css:display = 'list-item'][1]" mode="list-style-type" />
+ </xsl:variable>
+ <xsl:variable name="list-style-position">
+ <xsl:apply-templates select="*[@css:display = 'list-item'][1]" mode="list-style-position" />
+ </xsl:variable>
+ <fo:list-block>
+ <xsl:choose>
+ <xsl:when test="$list-style-type != 'none' and $list-style-position = 'outside'">
+ <xsl:attribute name="provisional-label-separation">
+ <xsl:text>6pt</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="provisional-distance-between-starts">
+ <xsl:call-template name="get-list-label-width">
+ <xsl:with-param name="list-type" select="$list-style-type" />
+ </xsl:call-template>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="provisional-label-separation">
+ <xsl:text>0</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="provisional-distance-between-starts">
+ <xsl:text>0</xsl:text>
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:list-block>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'list-item']" mode="list-style-position">
+ <xsl:variable name="list-style-position" select="ancestor-or-self::*[@css:list-style-position != 'inherit'][1]/@css:list-style-position" />
+ <xsl:choose>
+ <xsl:when test="string-length($list-style-position) = 0">
+ <xsl:text>outside</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$list-style-position" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'list-item']" mode="list-style-type">
+ <xsl:variable name="att" select="ancestor-or-self::*[@css:list-style-type != 'inherit' or @css:list-style-image != 'inherit'][1]" />
+ <xsl:choose>
+ <xsl:when test="exsl:node-set($att)/@css:list-style-image">
+ <xsl:text>image</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="list-style-type" select="exsl:node-set($att)/@css:list-style-type" />
+ <xsl:choose>
+ <xsl:when test="string-length($list-style-type) = 0">
+ <xsl:text>disc</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$list-style-type" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table' and *[@css:display = 'table-caption'] and not(*[@css:display = 'table-row'])]">
+ <fo:table-and-caption>
+ <xsl:apply-templates select="*[@css:display = 'table-caption']/@css:caption-side" />
+ <xsl:apply-templates select="@*[starts-with(name(), 'css:margin-')]" />
+ <xsl:apply-templates select="*[@css:display = 'table-caption']" />
+ <fo:table margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@*[not(starts-with(name(), 'css:margin-'))]" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" mode="layout" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" />
+ <xsl:apply-templates select="*[@css:display = 'table-header-group']" />
+ <xsl:apply-templates select="*[@css:display = 'table-footer-group']" />
+ <xsl:apply-templates select="*[@css:display = 'table-row-group']" />
+ </fo:table>
+ </fo:table-and-caption>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table' and not(*[@css:display = 'table-caption']) and not(*[@css:display = 'table-row'])]">
+ <fo:table margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@*" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" mode="layout" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" />
+ <xsl:apply-templates select="*[@css:display = 'table-header-group']" />
+ <xsl:apply-templates select="*[@css:display = 'table-footer-group']" />
+ <xsl:apply-templates select="*[@css:display = 'table-row-group']" />
+ </fo:table>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table' and *[@css:display = 'table-caption'] and *[@css:display = 'table-row']]">
+ <fo:table-and-caption>
+ <xsl:apply-templates select="*[@css:display = 'table-caption']/@css:caption-side" />
+ <xsl:apply-templates select="@*[starts-with(name(), 'css:margin-')]" />
+ <xsl:apply-templates select="*[@css:display = 'table-caption']" />
+ <fo:table margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@*[not(starts-with(name(), 'css:margin-'))]" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" mode="layout" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" />
+ <fo:table-body>
+ <xsl:apply-templates select="*[@css:display = 'table-row']" />
+ </fo:table-body>
+ </fo:table>
+ </fo:table-and-caption>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table' and not(*[@css:display = 'table-caption']) and *[@css:display = 'table-row']]">
+ <fo:table margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@*" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" mode="layout" />
+ <xsl:apply-templates select="*[@css:display = 'table-column']" />
+ <fo:table-body>
+ <xsl:apply-templates select="*[@css:display = 'table-row']" />
+ </fo:table-body>
+ </fo:table>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-caption']">
+ <fo:table-caption margin-left="0pt" margin-right="0pt">
+ <xsl:apply-templates select="@*[name() != 'css:caption-side']" />
+ <fo:block>
+ <xsl:apply-templates select="node()" />
+ </fo:block>
+ </fo:table-caption>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-caption']/@css:caption-side[. = 'bottom']" priority="1">
+ <xsl:attribute name="caption-side">after</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-caption']/@css:caption-side[. = 'left']" priority="1">
+ <xsl:attribute name="caption-side">start</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-caption']/@css:caption-side[. = 'right']" priority="1">
+ <xsl:attribute name="caption-side">end</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-caption']/@css:caption-side[. = 'top']" priority="1">
+ <xsl:attribute name="caption-side">before</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']">
+ <fo:table-cell>
+ <xsl:apply-templates select="@*" />
+ <fo:block>
+ <xsl:apply-templates select="node()" />
+ </fo:block>
+ </fo:table-cell>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']/@css:colspan" priority="1">
+ <xsl:attribute name="number-columns-spanned">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']/@css:rowspan" priority="1">
+ <xsl:attribute name="number-rows-spanned">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']/@css:vertical-align[. = 'bottom']" priority="1">
+ <xsl:attribute name="display-align">after</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']/@css:vertical-align[. = 'middle']" priority="1">
+ <xsl:attribute name="display-align">center</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-cell']/@css:vertical-align[. = 'top']" priority="1">
+ <xsl:attribute name="display-align">before</xsl:attribute>
+ </xsl:template>
+
+
+
+ <!-- Unsupported vertical-align values for table-cells. -->
+
+ <xsl:template match="*[@css:display = 'table-cell']/ @css:vertical-align[. != 'top' and . != 'bottom' and . != 'middle']" priority="1" />
+
+
+
+ <xsl:template match="*[@css:display = 'table-column']">
+ <fo:table-column>
+ <xsl:apply-templates select="@*" />
+ </fo:table-column>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-column' and contains(@css:width, '*')]" mode="layout">
+ <xsl:attribute name="table-layout">fixed</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-column']/@css:span" priority="1">
+ <xsl:attribute name="number-columns-repeated">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <!--
+ The inheritance mechanism of the table normalizer might have propagated
+ these.
+ -->
+
+ <xsl:template match="*[@css:display = 'table-column']/@css:text-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-column']/@css:vertical-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-footer-group']/@css:text-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-footer-group']/@css:vertical-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-header-group']/@css:text-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-header-group']/@css:vertical-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-row']/@css:text-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-row']/@css:vertical-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-row-group']/@css:text-align" priority="1" />
+ <xsl:template match="*[@css:display = 'table-row-group']/@css:vertical-align" priority="1" />
+
+
+
+ <xsl:template match="*[@css:display = 'table-column']/@css:width" priority="1">
+ <xsl:attribute name="column-width">
+ <xsl:choose>
+ <xsl:when test="starts-with(., '*')">
+ <xsl:value-of select="concat('proportional-column-width(1)', substring-after(., '*'))" />
+ </xsl:when>
+ <xsl:when test="contains(., '*')">
+ <xsl:value-of select="concat('proportional-column-width(', substring-before(., '*'), ')', substring-after(., '*'))" />
+ </xsl:when>
+ <xsl:when test="contains(., 'pcw')">
+ <xsl:value-of select="concat('proportional-column-width(', substring-before(., 'pcw'), ')', substring-after(., 'pcw'))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="." />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-footer-group']">
+ <fo:table-footer>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:table-footer>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-header-group']">
+ <fo:table-header>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:table-header>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-row']">
+ <fo:table-row>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:table-row>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'table-row-group']">
+ <fo:table-body>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:table-body>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'block']/@css:margin-bottom[. != 'auto'] | *[@css:display = 'list-item']/@css:margin-bottom[. != 'auto'] | *[@css:display = 'table']/@css:margin-bottom[. != 'auto']" priority="1">
+ <xsl:attribute name="space-after">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'block']/@css:margin-top[. != 'auto'] | *[@css:display = 'list-item']/@css:margin-top[. != 'auto'] | *[@css:display = 'table']/@css:margin-top[. != 'auto']" priority="1">
+ <xsl:attribute name="space-before">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="*[@css:display = 'leader']">
+ <fo:leader>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:leader>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:external-link">
+ <fo:basic-link external-destination="{concat('url(', @target, ')')}">
+ <xsl:apply-templates />
+ </fo:basic-link>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:footnote">
+ <fo:footnote>
+ <xsl:apply-templates select="css:footnote-reference/*" />
+ <fo:footnote-body>
+ <xsl:apply-templates select="css:footnote-body/*" />
+ </fo:footnote-body>
+ </fo:footnote>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:internal-link">
+ <fo:basic-link internal-destination="{@target}">
+ <xsl:apply-templates />
+ </fo:basic-link>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:anchor" priority="1">
+ <xsl:attribute name="id">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:column-span" priority="1">
+ <xsl:attribute name="span">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@css:vlink" priority="1" />
+
+
+
+ <xsl:template match="@css:white-space[. = 'wrap-nocollapse']" priority="1">
+ <xsl:attribute name="linefeed-treatment">preserve</xsl:attribute>
+ <xsl:attribute name="white-space-collapse">false</xsl:attribute>
+ <xsl:attribute name="white-space-treatment">preserve</xsl:attribute>
+ <xsl:attribute name="wrap-option">wrap</xsl:attribute>
+ </xsl:template>
+
+
+
+ <!--
+ The following two templates are temporarily in the RenderX namespace.
+ -->
+
+ <xsl:template match="css:change-bar-begin">
+ <rx:change-bar-begin>
+ <xsl:copy-of select="@*" />
+ </rx:change-bar-begin>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:change-bar-end">
+ <rx:change-bar-end>
+ <xsl:copy-of select="@*" />
+ </rx:change-bar-end>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:external">
+ <fo:external-graphic src="{@css:href}" />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:first-line">
+ <fo:initial-property-set>
+ <xsl:apply-templates select="@*" />
+ </fo:initial-property-set>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:float">
+ <fo:float>
+ <xsl:apply-templates select="@*" />
+ <fo:block>
+ <xsl:apply-templates select="node()" />
+ </fo:block>
+ </fo:float>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:fo-marker">
+ <fo:marker marker-class-name="{@css:name}">
+ <xsl:apply-templates select="node()" />
+ </fo:marker>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:last-page-mark">
+ <fo:block id="last-page" />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:newline">
+ <fo:block />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-number">
+ <fo:page-number />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-number" mode="page-number-setup">
+ <xsl:apply-templates select="@css:format" mode="page-number-setup" />
+ <xsl:apply-templates select="@css:letter-value" mode="page-number-setup" />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-number/@css:format" mode="page-number-setup">
+ <xsl:attribute name="format">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-number/@css:letter-value" mode="page-number-setup">
+ <xsl:attribute name="letter-value">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="css:pages-total">
+ <fo:page-number-citation ref-id="last-page" />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:page-ref">
+ <fo:page-number-citation ref-id="{@css:ref-id}" />
+ </xsl:template>
+
+
+
+ <xsl:template match="css:retrieve-fo-marker">
+ <fo:retrieve-marker retrieve-class-name="{@css:name}" />
+ </xsl:template>
+
+
+
+ <!-- Named templates. -->
+
+ <xsl:template name="get-list-label-width">
+ <xsl:param name="list-type" />
+
+ <xsl:choose>
+ <xsl:when test="$list-type = 'image'">
+ <xsl:value-of select="ancestor-or-self::*[@css:list-label-width != 'inherit'][1]/@css:list-label-width" />
+ </xsl:when>
+ <xsl:when test="$list-type = 'disc' or $list-type = 'circle' or $list-type = 'square' or $list-type = 'box' or $list-type = 'check' or $list-type = 'diamond' or $list-type = 'hyphen'">
+ <xsl:text>1em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="get-num-list-label-width">
+ <xsl:with-param name="items" select="count(*[@css:display = 'list-item'])" />
+ <xsl:with-param name="list-type" select="$list-type" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="get-num-format">
+ <xsl:param name="list-type" />
+
+ <xsl:choose>
+ <xsl:when test="$list-type = 'lower-alpha' or $list-type = 'lower-latin'">
+ <xsl:text>a</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'lower-roman'">
+ <xsl:text>i</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'upper-alpha' or $list-type = 'upper-latin'">
+ <xsl:text>A</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'upper-roman'">
+ <xsl:text>I</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>1</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="get-num-list-label-width">
+ <xsl:param name="items" />
+ <xsl:param name="list-type" />
+
+ <xsl:choose>
+ <xsl:when test="$list-type='lower-alpha' or $list-type = 'lower-latin' or $list-type = 'lower-greek'">
+ <xsl:choose>
+ <xsl:when test="$items < 27">
+ <xsl:text>1.5em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>2em</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="$list-type='upper-alpha' or $list-type = 'upper-latin'">
+ <xsl:choose>
+ <xsl:when test="$items<27">
+ <xsl:text>1.5em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>2.5em</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="$list-type='lower-roman'">
+ <xsl:choose>
+ <xsl:when test="$items<7">
+ <xsl:text>1.5em</xsl:text>
+ </xsl:when>
+ <xsl:when test="$items<17">
+ <xsl:text>2em</xsl:text>
+ </xsl:when>
+ <xsl:when test="$items<27">
+ <xsl:text>2.5em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>3.5em</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="$list-type='upper-roman'">
+ <xsl:choose>
+ <xsl:when test="$items<7">
+ <xsl:text>2em</xsl:text>
+ </xsl:when>
+ <xsl:when test="$items<17">
+ <xsl:text>2.5em</xsl:text>
+ </xsl:when>
+ <xsl:when test="$items<27">
+ <xsl:text>3em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>4em</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="$items<10">
+ <xsl:text>1.5em</xsl:text>
+ </xsl:when>
+ <xsl:when test="$items<100">
+ <xsl:text>2em</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>2.5em</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="list-label">
+ <xsl:param name="list-type" />
+
+ <xsl:choose>
+ <xsl:when test="$list-type = 'image'">
+ <fo:external-graphic>
+ <xsl:attribute name="src">
+ <xsl:value-of select="ancestor-or-self::*[@css:list-style-image != 'inherit'][1]/@css:list-style-image" />
+ </xsl:attribute>
+ </fo:external-graphic>
+ </xsl:when>
+ <xsl:when test="$list-type = 'box'">
+ <xsl:text>□</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'check'">
+ <xsl:text>✓</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'circle'">
+ <xsl:text>◦</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'diamond'">
+ <xsl:text>♦</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'disc'">
+ <xsl:text>•</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'hyphen'">
+ <xsl:text>–</xsl:text>
+ </xsl:when>
+ <xsl:when test="$list-type = 'square'">
+ <xsl:text>■</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="format">
+ <xsl:call-template name="get-num-format">
+ <xsl:with-param name="list-type" select="$list-type" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:number format="{$format}" value="count(preceding-sibling::*[@css:display = 'list-item']) + 1" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/src/be/re/css/style/deltaxml.css b/src/be/re/css/style/deltaxml.css
new file mode 100644
index 0000000..4139253
--- /dev/null
+++ b/src/be/re/css/style/deltaxml.css
@@ -0,0 +1,40 @@
+ at namespace deltaxml url(http://www.deltaxml.com/ns/well-formed-delta-v1);
+
+ at media print
+{
+ deltaxml|PCDATAnew, deltaxml|PCDATAold
+ {
+ display: inline;
+ }
+
+ deltaxml|exchange, deltaxml|new, deltaxml|old
+ {
+ display: wrapper;
+ }
+
+ *[deltaxml|delta="add"], deltaxml|PCDATAnew, deltaxml|new
+ {
+ text-decoration: underline;
+ }
+
+ *[deltaxml|delta="delete"], deltaxml|PCDATAold, deltaxml|old
+ {
+ text-decoration: line-through;
+ }
+
+ *[deltaxml|delta="add"]:before, deltaxml|PCDATAnew:before,
+ deltaxml|new, *[deltaxml|delta="delete"]:before,
+ deltaxml|PCDATAold:before, deltaxml|old
+ {
+ change-bar-class: changed;
+ change-bar-placement: alternate;
+ change-bar-style: solid;
+ change-bar-width: 0.2pt;
+ }
+
+ *[deltaxml|delta="add"]:after, deltaxml|PCDATAnew:after,
+ *[deltaxml|delta="delete"]:after, deltaxml|PCDATAold:after
+ {
+ change-bar-class: changed;
+ }
+}
diff --git a/src/be/re/css/style/fo_setup.xsl b/src/be/re/css/style/fo_setup.xsl
new file mode 100644
index 0000000..2662e17
--- /dev/null
+++ b/src/be/re/css/style/fo_setup.xsl
@@ -0,0 +1,319 @@
+<?xml version='1.0'?>
+<xsl:transform xmlns:css="http://www.w3.org/1998/CSS" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
+
+ <xsl:param name="column-count">1</xsl:param>
+ <xsl:param name="country">GB</xsl:param>
+ <xsl:param name="font-family">serif</xsl:param>
+ <xsl:param name="font-size">none</xsl:param>
+ <xsl:param name="language">en</xsl:param>
+ <xsl:param name="link-color">black</xsl:param>
+ <xsl:param name="odd-even-shift">10mm</xsl:param>
+ <xsl:param name="orientation">portrait</xsl:param>
+ <xsl:param name="paper-margin-bottom">10mm</xsl:param>
+ <xsl:param name="paper-margin-left">25mm</xsl:param>
+ <xsl:param name="paper-margin-right">25mm</xsl:param>
+ <xsl:param name="paper-margin-top">10mm</xsl:param>
+ <xsl:param name="paper-mode">onesided</xsl:param>
+ <!-- Can be "twosided" or "onesided". -->
+ <xsl:param name="paper-size">a4</xsl:param>
+ <xsl:param name="rule-thickness">0.2pt</xsl:param>
+
+
+
+ <xsl:variable name="actual-font-size">
+ <xsl:choose>
+ <xsl:when test="$font-size != 'none'">
+ <xsl:value-of select="$font-size" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="$paper-size = 'a5' or $paper-size = 'b5'">
+ <xsl:text>10pt</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>11pt</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="portrait-paper-height">
+ <xsl:choose>
+ <xsl:when test="$paper-size = 'a0'">
+ <xsl:text>1188mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a1'">
+ <xsl:text>840mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a2'">
+ <xsl:text>594mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a3'">
+ <xsl:text>420mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a4'">
+ <xsl:text>297mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a5'">
+ <xsl:text>210mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'b5'">
+ <xsl:text>250mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'executive'">
+ <xsl:text>11in</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'letter'">
+ <xsl:text>11in</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'legal'">
+ <xsl:text>14in</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="no">Unknown paper-size, taking a4</xsl:message>
+ <xsl:text>297mm</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="portrait-paper-width">
+ <xsl:choose>
+ <xsl:when test="$paper-size = 'a0'">
+ <xsl:text>840mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a1'">
+ <xsl:text>594mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a2'">
+ <xsl:text>420mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a3'">
+ <xsl:text>297mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a4'">
+ <xsl:text>210mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'a5'">
+ <xsl:text>148mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'b5'">
+ <xsl:text>176mm</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'executive'">
+ <xsl:text>7.25in</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'letter'">
+ <xsl:text>8.5in</xsl:text>
+ </xsl:when>
+ <xsl:when test="$paper-size = 'legal'">
+ <xsl:text>8.5in</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>210mm</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="paper-height">
+ <xsl:choose>
+ <xsl:when test="$orientation = 'portrait'">
+ <xsl:value-of select="$portrait-paper-height" />
+ </xsl:when>
+ <xsl:when test="$orientation = 'landscape'">
+ <xsl:value-of select="$portrait-paper-width" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="no">Unknown orientation, taking portrait</xsl:message>
+ <xsl:value-of select="$portrait-paper-height" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="paper-width">
+ <xsl:choose>
+ <xsl:when test="$orientation = 'portrait'">
+ <xsl:value-of select="$portrait-paper-width" />
+ </xsl:when>
+ <xsl:when test="$orientation = 'landscape'">
+ <xsl:value-of select="$portrait-paper-height" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$portrait-paper-width" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="writing-mode">lr-tb</xsl:variable>
+
+
+
+ <!-- Page setup. -->
+
+ <xsl:template match="/">
+ <xsl:apply-templates select="*" mode="setup" />
+ </xsl:template>
+
+
+
+ <xsl:template match="/css:root[not(css:pages)]" mode="setup">
+ <fo:root font-selection-strategy="character-by-character" line-height-shift-adjustment="disregard-shifts" country="{$country}" font-family="{$font-family}" font-size="{$actual-font-size}" language="{$language}">
+ <xsl:apply-templates select="@xml:lang" />
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="blank" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left}" margin-right="{$paper-margin-right}">
+ <fo:region-body margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="even" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left} - {$odd-even-shift}" margin-right="{$paper-margin-right} + {$odd-even-shift}">
+ <xsl:call-template name="region-body" />
+ <xsl:call-template name="region-before">
+ <xsl:with-param name="region-name">region-before-even</xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="region-after" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="odd" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left} + {$odd-even-shift}" margin-right="{$paper-margin-right} - {$odd-even-shift}">
+ <xsl:call-template name="region-body" />
+ <xsl:call-template name="region-before">
+ <xsl:with-param name="region-name">region-before-odd</xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="region-after" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="odd-first" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left} + {$odd-even-shift}" margin-right="{$paper-margin-right} - {$odd-even-shift}">
+ <xsl:call-template name="region-body" />
+ <xsl:call-template name="region-after" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="onesided" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left}" margin-right="{$paper-margin-right}">
+ <xsl:call-template name="region-body" />
+ <xsl:call-template name="region-before">
+ <xsl:with-param name="region-name">region-before-even</xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="region-after" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="onesided-first" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}" margin-left="{$paper-margin-left}" margin-right="{$paper-margin-right}">
+ <xsl:call-template name="region-body" />
+ <xsl:call-template name="region-after" />
+ </fo:simple-page-master>
+
+ <fo:simple-page-master master-name="title" writing-mode="{$writing-mode}" page-height="{$paper-height}" page-width="{$paper-width}" margin-top="{$paper-margin-top}" margin-bottom="{$paper-margin-bottom}">
+ <xsl:if test="$paper-mode='onesided'">
+ <xsl:attribute name="margin-left">
+ <xsl:value-of select="$paper-margin-left" />
+ </xsl:attribute>
+ <xsl:attribute name="margin-right">
+ <xsl:value-of select="$paper-margin-right" />
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="$paper-mode='twosided'">
+ <xsl:attribute name="margin-left">
+ <xsl:value-of select="concat($paper-margin-left, ' + ', $odd-even-shift)" />
+ </xsl:attribute>
+ <xsl:attribute name="margin-right">
+ <xsl:value-of select="concat($paper-margin-left, ' - ', $odd-even-shift)" />
+ </xsl:attribute>
+ </xsl:if>
+ <fo:region-body margin-top="0mm" margin-bottom="0mm" margin-left="0mm" margin-right="0mm" />
+ </fo:simple-page-master>
+
+ <fo:page-sequence-master master-name="document">
+ <fo:repeatable-page-master-alternatives>
+ <xsl:if test="$paper-mode='twosided'">
+ <fo:conditional-page-master-reference odd-or-even="even" master-reference="even" />
+ <fo:conditional-page-master-reference odd-or-even="odd" page-position="first" master-reference="odd-first" />
+ <fo:conditional-page-master-reference odd-or-even="odd" page-position="any" master-reference="odd" />
+ </xsl:if>
+
+ <xsl:if test="$paper-mode='onesided'">
+ <fo:conditional-page-master-reference page-position="first" master-reference="onesided-first" />
+ <fo:conditional-page-master-reference page-position="any" master-reference="onesided" />
+ </xsl:if>
+
+ <fo:conditional-page-master-reference blank-or-not-blank="blank" page-position="any" master-reference="blank" />
+ </fo:repeatable-page-master-alternatives>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+
+ <fo:page-sequence format="1" initial-page-number="1" master-reference="document">
+ <xsl:call-template name="page-style" />
+ <fo:flow flow-name="xsl-region-body">
+ <xsl:apply-templates select="css:page-sequence/*" />
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </xsl:template>
+
+
+
+ <!-- Named templates. -->
+
+ <xsl:template name="footnote-separator">
+ <fo:block>
+ <fo:leader leader-length="41%" leader-pattern="rule" rule-style="solid" rule-thickness="{$rule-thickness}" />
+ </fo:block>
+ </xsl:template>
+
+
+
+ <xsl:template name="page-style">
+ <fo:static-content flow-name="region-before-even">
+ <xsl:call-template name="running-head">
+ <xsl:with-param name="align">start</xsl:with-param>
+ </xsl:call-template>
+ </fo:static-content>
+
+ <fo:static-content flow-name="region-before-odd">
+ <xsl:call-template name="running-head">
+ <xsl:with-param name="align">end</xsl:with-param>
+ </xsl:call-template>
+ </fo:static-content>
+
+ <fo:static-content flow-name="xsl-footnote-separator">
+ <xsl:call-template name="footnote-separator" />
+ </fo:static-content>
+
+ <fo:static-content flow-name="xsl-region-after">
+ <xsl:call-template name="running-foot" />
+ </fo:static-content>
+ </xsl:template>
+
+
+
+ <xsl:template name="region-after">
+ <fo:region-after display-align="before" extent="10mm" />
+ </xsl:template>
+
+
+
+ <xsl:template name="region-before">
+ <xsl:param name="region-name" />
+
+ <fo:region-before display-align="after" extent="10mm" region-name="{$region-name}" />
+ </xsl:template>
+
+
+
+ <xsl:template name="region-body">
+ <fo:region-body column-count="{$column-count}" margin-bottom="15mm" margin-left="0mm" margin-right="0mm" margin-top="15mm" />
+ </xsl:template>
+
+
+
+ <xsl:template name="running-head">
+ <xsl:param name="align" />
+ <fo:block font-style="oblique" text-align="{$align}" text-transform="uppercase">
+ <fo:retrieve-marker retrieve-class-name="component" retrieve-position="first-starting-within-page" retrieve-boundary="document" />
+ </fo:block>
+ </xsl:template>
+
+
+
+ <xsl:template name="running-foot">
+ <fo:block text-align="center">
+ <fo:page-number />
+ </fo:block>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/src/be/re/css/style/fop_filter.xsl b/src/be/re/css/style/fop_filter.xsl
new file mode 100644
index 0000000..a784436
--- /dev/null
+++ b/src/be/re/css/style/fop_filter.xsl
@@ -0,0 +1,177 @@
+<?xml version='1.0'?>
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
+
+ <xsl:output method="xml" indent="no" version="1.0" encoding="UTF-8" omit-xml-declaration="no" />
+
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:list-item-body/fo:block/@space-before" />
+ <xsl:template match="@page-break-after" />
+ <xsl:template match="@page-break-before" />
+ <xsl:template match="@page-break-inside" />
+ <xsl:template match="@text-transform" />
+
+
+
+ <xsl:template match="@font-weight[. = 'bolder']">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@margin-bottom[. = '0']">
+ <xsl:attribute name="space-after">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:block/@margin-bottom | fo:block-container/@margin-bottom | fo:table/@margin-bottom">
+ <xsl:attribute name="space-after">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@margin-left[. = '0']" priority="1">
+ <xsl:attribute name="start-indent">
+ <xsl:text>inherited-property-value(start-indent)</xsl:text>
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:block/@margin-left | fo:block-container/@margin-left | fo:table/@margin-left">
+ <xsl:attribute name="start-indent">
+ <xsl:text>inherited-property-value(start-indent)</xsl:text>
+ <xsl:if test=". != 'auto'">
+ <xsl:text>+</xsl:text>
+ <xsl:value-of select="." />
+ </xsl:if>
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:table-cell">
+ <fo:table-cell start-indent="0pt" end-indent="0pt">
+ <xsl:apply-templates select="@* | node()" />
+ </fo:table-cell>
+ </xsl:template>
+
+
+
+ <xsl:template match="@margin-right[. = '0']" priority="1">
+ <xsl:attribute name="end-indent">
+ <xsl:text>inherited-property-value(end-indent)</xsl:text>
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:block/@margin-right | fo:block-container/@margin-right | fo:table/@margin-right">
+ <xsl:attribute name="end-indent">
+ <xsl:text>inherited-property-value(end-indent)</xsl:text>
+ <xsl:if test=". != 'auto'">
+ <xsl:text>+</xsl:text>
+ <xsl:value-of select="." />
+ </xsl:if>
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@margin-top[. = '0']">
+ <xsl:attribute name="space-before">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:block/@margin-top | fo:block-container/@margin-top | fo:table/@margin-top">
+ <xsl:attribute name="space-before">
+ <xsl:value-of select="." />
+ </xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@padding-bottom[. = '0']">
+ <xsl:attribute name="padding-bottom">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@padding-left[. = '0']">
+ <xsl:attribute name="padding-left">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@padding-right[. = '0']">
+ <xsl:attribute name="padding-right">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@padding-top[. = '0']">
+ <xsl:attribute name="padding-top">0pt</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="@provisional-distance-between-starts">
+ <xsl:attribute name="provisional-distance-between-starts">2em</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:table">
+ <fo:table width="100%">
+ <xsl:apply-templates select="@*" /> <!-- Can override width. -->
+ <xsl:attribute name="table-layout">fixed</xsl:attribute>
+ <xsl:apply-templates select="(.//fo:table-row)[1]/fo:table-cell" mode="table-no-columns" />
+ <xsl:apply-templates select="node()" />
+ </fo:table>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:table/@width[. = 'auto']">
+ <xsl:attribute name="width">100%</xsl:attribute>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:table[not(fo:table-column)]//fo:table-cell" mode="table-no-columns">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ </xsl:template>
+
+
+
+ <xsl:template match="text()" mode="table-no-columns" />
+
+
+
+ <xsl:template match="fo:table-column">
+ <fo:table-column column-width="proportional-column-width(1)">
+ <xsl:apply-templates select="@*" />
+ </fo:table-column>
+ </xsl:template>
+
+
+
+ <xsl:template match="fo:table-cell//fo:block-container">
+ <fo:block>
+ <xsl:apply-templates select="@* | node()" />
+ </fo:block>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/src/be/re/css/style/ua.css b/src/be/re/css/style/ua.css
new file mode 100644
index 0000000..b7d8d89
--- /dev/null
+++ b/src/be/re/css/style/ua.css
@@ -0,0 +1,3 @@
+ at import "xhtml_print.css";
+ at import "deltaxml.css";
+ at import "xlink.css";
diff --git a/src/be/re/css/style/util.xsl b/src/be/re/css/style/util.xsl
new file mode 100644
index 0000000..3f174c9
--- /dev/null
+++ b/src/be/re/css/style/util.xsl
@@ -0,0 +1,106 @@
+<?xml version='1.0'?>
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:template name="count-tokens">
+ <xsl:param name="s" />
+
+ <xsl:variable name="norm" select="normalize-space($s)" />
+
+ <xsl:choose>
+ <xsl:when test="string-length($norm) = 0">
+ <xsl:value-of select="number(0)" />
+ </xsl:when>
+ <xsl:when test="contains($norm, ' ')">
+ <xsl:variable name="value">
+ <xsl:call-template name="count-tokens">
+ <xsl:with-param name="s" select="substring-after($norm, ' ')" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$value + 1" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="number(1)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="get-token">
+ <xsl:param name="current-position" select="number(1)" />
+ <xsl:param name="position" />
+ <xsl:param name="s" />
+
+ <xsl:if test="$position < 1">
+ <xsl:message terminate="yes">Position for get-token must be greater than 0.</xsl:message>
+ </xsl:if>
+
+ <xsl:variable name="norm" select="normalize-space($s)" />
+
+ <xsl:choose>
+ <xsl:when test="$current-position = $position">
+ <xsl:choose>
+ <xsl:when test="contains($norm, ' ')">
+ <xsl:value-of select="substring-before($norm, ' ')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$norm" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="contains($norm, ' ')">
+ <xsl:call-template name="get-token">
+ <xsl:with-param name="current-position" select="$current-position + 1" />
+ <xsl:with-param name="position" select="$position" />
+ <xsl:with-param name="s" select="substring-after($norm, ' ')" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="replace-string">
+ <xsl:param name="s" />
+ <xsl:param name="replace" />
+ <xsl:param name="by" />
+
+ <xsl:choose>
+ <xsl:when test="contains($s, $replace)">
+ <xsl:call-template name="replace-string">
+ <xsl:with-param name="s" select="concat(substring-before($s, $replace), $by, substring-after($s, $replace))" />
+ <xsl:with-param name="replace" select="$replace" />
+ <xsl:with-param name="by" select="$by" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$s" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+ <xsl:template name="uri-last-path-segment">
+ <xsl:param name="uri" />
+
+ <xsl:choose>
+ <xsl:when test="contains($uri, '/')">
+ <xsl:call-template name="uri-last-path-segment">
+ <xsl:with-param name="uri" select="substring-after($uri, '/')" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$uri" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/src/be/re/css/style/xhtml.css b/src/be/re/css/style/xhtml.css
new file mode 100644
index 0000000..15268f5
--- /dev/null
+++ b/src/be/re/css/style/xhtml.css
@@ -0,0 +1,160 @@
+ at namespace url(http://www.w3.org/1999/xhtml);
+
+address, blockquote, body, dd, div, dl, dt, fieldset, form, frame, frameset,
+ h1, h2, h3, h4, h5, h6, iframe, noframes, object, ol, p, ul, applet, center,
+ dir, hr, menu, pre { display: block }
+
+li { display: list-item }
+
+head { display: none }
+
+table { display: table }
+
+tr { display: table-row }
+
+thead { display: table-header-group }
+
+tbody { display: table-row-group }
+
+tfoot { display: table-footer-group }
+
+col { display: table-column }
+
+colgroup { display: table-column-group }
+
+td, th { display: table-cell }
+
+caption { display: table-caption }
+
+th { font-weight: bolder; text-align: center }
+
+caption { text-align: center }
+
+body { padding: 8px; line-height: 1.33 }
+
+h1 { font-size: 2em; margin: .67em 0 }
+
+h2 { font-size: 1.5em; margin: .83em 0 }
+
+h3 { font-size: 1.17em; margin: 1em 0 }
+
+h4, p, blockquote, ul, fieldset, form, ol, dl, dir, menu { margin: 1.33em 0 }
+
+h5 { font-size: .83em; line-height: 1.17em; margin: 1.67em 0 }
+
+h6 { font-size: .67em; margin: 2.33em 0 }
+
+h1, h2, h3, h4, h5, h6, b, strong { font-weight: bolder }
+
+blockquote { margin-left: 40px; margin-right: 40px }
+
+i, cite, em, var, address { font-style: italic }
+
+pre, tt, code, kbd, samp { font-family: monospace }
+
+pre { white-space: pre }
+
+big { font-size: 1.17em }
+
+small, sub, sup { font-size: .83em }
+
+sub { vertical-align: sub }
+
+sup { vertical-align: super }
+
+s, strike, del { text-decoration: line-through }
+
+hr { border: 1px inset }
+
+ol, ul, dir, menu, dd { margin-left: 40px }
+
+ol { list-style-type: decimal }
+
+ol ul, ul ol, ul ul, ol ol { margin-top: 0; margin-bottom: 0 }
+
+u, ins { text-decoration: underline }
+
+center { text-align: center }
+
+br:before { content: "\a" }
+
+/* an example of style for html 4.0 s abbr/acronym elements */
+
+abbr, acronym { font-variant: small-caps; letter-spacing: 0.1em }
+
+a[href] { text-decoration: underline }
+
+:focus { outline: thin dotted invert }
+
+/* begin bidirectionality settings (do not change) */
+
+bdo[dir="ltr"] { direction: ltr; unicode-bidi: bidi-override }
+
+bdo[dir="rtl"] { direction: rtl; unicode-bidi: bidi-override }
+
+*[dir="ltr"] { direction: ltr; unicode-bidi: embed }
+
+*[dir="rtl"] { direction: rtl; unicode-bidi: embed }
+
+/* elements that are block-level in html4 */
+
+address, blockquote, body, dd, div, dl, dt, fieldset, form, frame, frameset, h1,
+ h2, h3, h4, h5, h6, iframe, noscript, noframes, object, ol, p, ul, applet,
+ center, dir, hr, menu, pre, li, table, tr, thead, tbody, tfoot, col, colgroup,
+ td, th, caption { unicode-bidi: embed }
+
+/* end bidi settings */
+
+/*@page { margin: 10% }*/
+
+ at media print
+{
+ h1, h2, h3, h4, h5, h6 { page-break-after: avoid; page-break-inside: avoid }
+
+ blockquote, pre { page-break-inside: avoid }
+
+ ul, ol, dl { page-break-before: avoid }
+}
+
+ at media speech
+{
+ h1, h2, h3, h4, h5, h6 { voice-family: paul, male; stress: 20; richness: 90 }
+
+ h1 { pitch: x-low; pitch-range: 90 }
+
+ h2 { pitch: x-low; pitch-range: 80 }
+
+ h3 { pitch: low; pitch-range: 70 }
+
+ h4 { pitch: medium; pitch-range: 60 }
+
+ h5 { pitch: medium; pitch-range: 50 }
+
+ h6 { pitch: medium; pitch-range: 40 }
+
+ li, dt, dd { pitch: medium; richness: 60 }
+
+ dt { stress: 80 }
+
+ pre, code, tt { pitch: medium; pitch-range: 0; stress: 0; richness: 80 }
+
+ em { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }
+
+ strong { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }
+
+ dfn { pitch: high; pitch-range: 60; stress: 60 }
+
+ s, strike { richness: 0 }
+
+ i { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }
+
+ b { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }
+
+ u { richness: 0 }
+
+ a:link { voice-family: harry, male }
+
+ a:visited { voice-family: betty, female }
+
+ a:active { voice-family: betty, female; pitch-range: 80; pitch: x-high }
+}
diff --git a/src/be/re/css/style/xhtml_print.css b/src/be/re/css/style/xhtml_print.css
new file mode 100644
index 0000000..d7f1aa9
--- /dev/null
+++ b/src/be/re/css/style/xhtml_print.css
@@ -0,0 +1,211 @@
+ at import "xhtml.css";
+ at namespace url(http://www.w3.org/1999/xhtml);
+
+ at media print
+{
+ a[href]
+ {
+ color: blue;
+ link: attr(href);
+ text-decoration: none;
+ }
+
+ a[name]
+ {
+ anchor: name;
+ }
+
+ blockquote, dl, ol, p, ul
+ {
+ margin: 0.83em 0pt;
+ }
+
+ blockquote
+ {
+ margin-left: 3em;
+ margin-right: 3em;
+ }
+
+ body
+ {
+ font-family: serif;
+ padding: 0pt;
+ region: body;
+ }
+
+ body:lang(da)
+ {
+ quotes: "\00BB" "\00AB";
+ }
+
+ body:lang(de-DE), body:lang(de-AT)
+ {
+ quotes: "\201E" "\201C" "\201A" "\2018"
+ }
+
+ body, body:lang(en), body:lang(es)
+ {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+ }
+
+ body:lang(fr)
+ {
+ quotes: "\00AB " " \00BB" "\2039 " " \203A";
+ }
+
+ body:lang(it)
+ {
+ quotes: "\00AB " " \00BB";
+ }
+
+ body:lang(nl)
+ {
+ quotes: "\201D" "\201D" "\2019" "\2019";
+ }
+
+ body:lang(no), bodylang:(pt), body:lang(de-CH)
+ {
+ quotes: "\00AB" "\00BB" "\2039" "\203A"
+ }
+
+ body:lang(sv)
+ {
+ quotes: "\00BB" "\00BB";
+ }
+
+ caption
+ {
+ margin: 0.5em 0pt;
+ }
+
+ dt
+ {
+ page-break-after: avoid;
+ }
+
+ h1
+ {
+ font-size: 1.6em;
+ margin-bottom: 0.7em;
+ margin-top: 1.4em;
+ }
+
+ h2
+ {
+ font-size: 1.3em;
+ margin-bottom: 0.6em;
+ margin-top: 1.2em;
+ }
+
+ h3
+ {
+ font-size: 1.1em;
+ }
+
+ h3, h4
+ {
+ margin-bottom: 0.5em;
+ margin-top: 1em;
+ }
+
+ h1, h2, h3, h4, h5, h6
+ {
+ hyphenate: false;
+ }
+
+ hr
+ {
+ border: 0.1pt solid;
+ }
+
+ img
+ {
+ content-height: scale-to-fit;
+ content-width: scale-to-fit;
+ display: graphic;
+ scaling: uniform;
+ src: attr(src);
+ }
+
+ li
+ {
+ margin-bottom: 0.8em;
+ margin-top: 0.8em;
+ }
+
+ li p, li blockquote, li dl, li ol, li ul
+ {
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+ }
+
+ li li
+ {
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+ }
+
+ li li p, li li blockquote, li li dl, li li ol, li li ul
+ {
+ margin-bottom: 0.3em;
+ margin-top: 0.3em;
+ }
+
+ li li li
+ {
+ margin-bottom: 0.4em;
+ margin-top: 0.4em;
+ }
+
+ li li li p, li li li blockquote, li li li dl, li li li ol,
+ li li li ul
+ {
+ margin-bottom: 0.3em;
+ margin-top: 0.3em;
+ }
+
+ li, p
+ {
+ text-align: justify;
+ }
+
+ pre
+ {
+ font-size: 0.85em;
+ }
+
+ ul
+ {
+ list-style-type: disc;
+ }
+
+ ol li ul, ul li ul
+ {
+ list-style-type: circle;
+ }
+
+ ol li ol li ul, ol li ul li ul, ul li ol li ul, ul li ul li ul
+ {
+ list-style-type: square;
+ }
+
+ q:after
+ {
+ content: close-quote;
+ }
+
+ q:before
+ {
+ content: open-quote;
+ }
+
+ script
+ {
+ display: none;
+ }
+
+ span.section-number
+ {
+ padding-right: 1em;
+ }
+}
diff --git a/src/be/re/css/style/xlink.css b/src/be/re/css/style/xlink.css
new file mode 100644
index 0000000..baf38e1
--- /dev/null
+++ b/src/be/re/css/style/xlink.css
@@ -0,0 +1,9 @@
+ at namespace xlink url(http://www.w3.org/1999/xlink);
+
+ at media print
+{
+ *[xlink|href]
+ {
+ link: attr(xlink|href);
+ }
+}
diff --git a/src/be/re/io/FlushOutputStream.java b/src/be/re/io/FlushOutputStream.java
new file mode 100644
index 0000000..fe4e267
--- /dev/null
+++ b/src/be/re/io/FlushOutputStream.java
@@ -0,0 +1,28 @@
+package be.re.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+
+public class FlushOutputStream extends FilterOutputStream
+
+{
+
+ public
+ FlushOutputStream(OutputStream out)
+ {
+ super(out);
+ }
+
+
+
+ public void
+ write(byte b) throws IOException
+ {
+ out.write(b);
+ out.flush();
+ }
+
+} // FlushOutputStream
diff --git a/src/be/re/io/IOException.java b/src/be/re/io/IOException.java
new file mode 100644
index 0000000..48285ee
--- /dev/null
+++ b/src/be/re/io/IOException.java
@@ -0,0 +1,43 @@
+package be.re.io;
+
+/**
+ * Make it possible to wrap a cause.
+ * @author Werner Donn\u00e9
+ */
+
+public class IOException extends java.io.IOException
+
+{
+
+ public
+ IOException()
+ {
+ }
+
+
+
+ public
+ IOException(String s)
+ {
+ super(s);
+ }
+
+
+
+ public
+ IOException(String message, Throwable cause)
+ {
+ super(message);
+ initCause(cause);
+ }
+
+
+
+ public
+ IOException(Throwable cause)
+ {
+ super();
+ initCause(cause);
+ }
+
+} // IOException
diff --git a/src/be/re/io/ReadLineInputStream.java b/src/be/re/io/ReadLineInputStream.java
new file mode 100644
index 0000000..3f41f9f
--- /dev/null
+++ b/src/be/re/io/ReadLineInputStream.java
@@ -0,0 +1,96 @@
+package be.re.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.util.Arrays;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class ReadLineInputStream extends FilterInputStream
+
+{
+
+ public
+ ReadLineInputStream(InputStream in)
+ {
+ super(new PushbackInputStream(in));
+ }
+
+
+
+ /**
+ * Reads bytes until '\r', '\n' or "\r\n" is encountered and returns them in
+ * an array. The line termination sequence is discarded and not included in
+ * the array. If the end of the stream has been reached <code>null</code> is
+ * returned.
+ */
+
+ public byte[]
+ readLine() throws IOException
+ {
+ return readLine(new byte[1024], 0);
+ }
+
+
+
+ private byte[]
+ readLine(byte[] buffer, int off) throws IOException
+ {
+ Arrays.fill(buffer, off, buffer.length, (byte) 0);
+
+ int i;
+
+ for
+ (
+ i = off;
+ i < buffer.length && (buffer[i] = (byte) in.read()) != '\n' &&
+ buffer[i] != '\r' && buffer[i] != -1;
+ ++i
+ );
+
+ if (i < buffer.length)
+ {
+ if (i == 0 && buffer[i] == -1)
+ {
+ return null;
+ }
+
+ byte[] result = new byte[i];
+
+ System.arraycopy(buffer, off, result, 0, result.length);
+
+ if ((char) buffer[i] == '\r')
+ {
+ byte b = (byte) in.read();
+
+ if (b != '\n')
+ {
+ ((PushbackInputStream) in).unread(b);
+ }
+ }
+
+ return result;
+ }
+
+ return readLine(realloc(buffer), i);
+ }
+
+
+
+ private static byte[]
+ realloc(byte[] buffer)
+ {
+ byte[] result = new byte[buffer.length * 2];
+
+ System.arraycopy(buffer, 0, result, 0, buffer.length);
+
+ return result;
+ }
+
+} // ReadLineInputStream
diff --git a/src/be/re/io/StreamConnector.java b/src/be/re/io/StreamConnector.java
new file mode 100644
index 0000000..5157ee4
--- /dev/null
+++ b/src/be/re/io/StreamConnector.java
@@ -0,0 +1,245 @@
+package be.re.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+
+/**
+ * This class reads data from an input stream and writes it to an output stream
+ * in a separate thread. The thread stops when the input stream is closed
+ * or when an exception occurs.
+ * @author Werner Donn\u00e9
+ */
+
+public class StreamConnector
+
+{
+
+ private IOException exception = null;
+ private Thread thread;
+
+
+
+ public
+ StreamConnector(InputStream in, OutputStream out)
+ {
+ this(in, out, true, true);
+ }
+
+
+
+ public
+ StreamConnector
+ (
+ InputStream in,
+ OutputStream out,
+ boolean closeInput,
+ boolean closeOutput
+ )
+ {
+ this(in, out, 0x10000, closeInput, closeOutput);
+ }
+
+
+
+ public
+ StreamConnector
+ (
+ InputStream in,
+ OutputStream out,
+ boolean closeInput,
+ boolean closeOutput,
+ boolean flush
+ )
+ {
+ this(in, out, 0x10000, closeInput, closeOutput, flush);
+ }
+
+
+
+ public
+ StreamConnector
+ (
+ InputStream in,
+ OutputStream out,
+ int bufferSize,
+ boolean closeInput,
+ boolean closeOutput
+ )
+ {
+ this(in, out, bufferSize, closeInput, closeOutput, true);
+ }
+
+
+
+ public
+ StreamConnector
+ (
+ final InputStream in,
+ final OutputStream out,
+ final int bufferSize,
+ final boolean closeInput,
+ final boolean closeOutput,
+ final boolean flush
+ )
+ {
+ (
+ thread =
+ new Thread
+ (
+ new Runnable()
+ {
+ public void
+ run()
+ {
+ try
+ {
+ copy
+ (
+ in,
+ out,
+ bufferSize,
+ closeInput,
+ closeOutput,
+ flush
+ );
+ }
+
+ catch (IOException e)
+ {
+ exception = e;
+ }
+ }
+ }
+ )
+ ).start();
+ }
+
+
+
+ public static void
+ copy(InputStream in, OutputStream out) throws IOException
+ {
+ copy(in, out, true, true);
+ }
+
+
+
+ public static void
+ copy
+ (
+ InputStream in,
+ OutputStream out,
+ boolean closeInput,
+ boolean closeOutput
+ ) throws IOException
+ {
+ copy(in, out, 0x10000, closeInput, closeOutput);
+ }
+
+
+
+ public static void
+ copy
+ (
+ InputStream in,
+ OutputStream out,
+ boolean closeInput,
+ boolean closeOutput,
+ boolean flush
+ ) throws IOException
+ {
+ copy(in, out, 0x10000, closeInput, closeOutput, flush);
+ }
+
+
+
+ public static void
+ copy
+ (
+ InputStream in,
+ OutputStream out,
+ int bufferSize,
+ boolean closeInput,
+ boolean closeOutput
+ ) throws IOException
+ {
+ copy(in, out, bufferSize, closeInput, closeOutput, true);
+ }
+
+
+
+ public static void
+ copy
+ (
+ InputStream in,
+ OutputStream out,
+ int bufferSize,
+ boolean closeInput,
+ boolean closeOutput,
+ boolean flush
+ ) throws IOException
+ {
+ byte[] buffer = new byte[bufferSize];
+ int len;
+
+ while ((len = in.read(buffer)) != -1)
+ {
+ out.write(buffer, 0, len);
+
+ if (flush)
+ {
+ out.flush();
+ }
+ }
+
+ if (closeInput)
+ {
+ in.close();
+ }
+
+ if (closeOutput)
+ {
+ out.close();
+ }
+ else
+ {
+ if (flush)
+ {
+ out.flush();
+ }
+ }
+ }
+
+
+
+ /**
+ * Breaks the connection. It doesn't touch the streams.
+ */
+
+ public void
+ disconnect()
+ {
+ thread.interrupt();
+ }
+
+
+
+ /**
+ * Joins with the thread implementing the io processing.
+ */
+
+ public void
+ join() throws IOException, InterruptedException
+ {
+ thread.join();
+
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+} // StreamConnector
diff --git a/src/be/re/io/Util.java b/src/be/re/io/Util.java
new file mode 100644
index 0000000..d78143d
--- /dev/null
+++ b/src/be/re/io/Util.java
@@ -0,0 +1,190 @@
+package be.re.io;
+
+import be.re.util.UUID;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+
+
+public class Util
+
+{
+
+ private static ResourceBundle bundle = null;
+ private static boolean deleteOnExitHookAdded = false;
+ private static final Set dirsToDelete = new HashSet();
+
+
+
+ /**
+ * Creates a temporary directory. When the resulting <code>java.io.File</code>
+ * object is garbage collected, the directory will not be deleted along. If
+ * the caller hasn't deleted it the file will be deleted on exit of the VM.
+ */
+
+ public static File
+ createTempDir(String prefix, String suffix, File directory)
+ throws IOException
+ {
+ final File result = createTempFileName(prefix, suffix, directory);
+
+ if (!result.mkdir())
+ {
+ throw
+ new IOException
+ (
+ "Can't create temporary directory \"" + result.getAbsolutePath() +
+ "\"."
+ );
+ }
+
+ deleteDirOnExit(result);
+
+ return
+ new File(result.getAbsolutePath())
+ {
+ public boolean
+ delete()
+ {
+ synchronized (dirsToDelete)
+ {
+ dirsToDelete.remove(result);
+ }
+
+ return result.delete();
+ }
+ };
+ }
+
+
+
+ public static File
+ createTempFile(String prefix, String suffix) throws IOException
+ {
+ return createTempFile(prefix, suffix, null);
+ }
+
+
+
+ /**
+ * Creates a temporary file. When the resulting <code>java.io.File</code>
+ * object is garbage collected, the file will not be deleted along. If the
+ * caller hasn't deleted it the file will be deleted on exit of the VM.
+ */
+
+ public static File
+ createTempFile(String prefix, String suffix, File directory)
+ throws IOException
+ {
+ File result = createTempFileName(prefix, suffix, directory);
+
+ if (!result.createNewFile())
+ {
+ throw
+ new IOException
+ (
+ "Can't create temporary file \"" + result.getAbsolutePath() + "\"."
+ );
+ }
+
+ result.deleteOnExit();
+
+ return result;
+ }
+
+
+
+ public static File
+ createTempFileName(String prefix, String suffix, File directory)
+ {
+ return
+ new File
+ (
+ directory != null ?
+ directory : new File(System.getProperty("java.io.tmpdir")),
+ (prefix != null ? prefix : "") + UUID.generateFormatted() +
+ (suffix != null ? suffix : ".tmp")
+ );
+ }
+
+
+
+ public static boolean
+ deleteDir(File dir)
+ {
+ if (dir.isDirectory())
+ {
+ File[] files = dir.listFiles();
+
+ for (int i = 0; i < files.length; ++i)
+ {
+ deleteDir(files[i]);
+ }
+ }
+
+ return dir.delete();
+ }
+
+
+
+ public static synchronized void
+ deleteDirOnExit(File dir)
+ {
+ if (!deleteOnExitHookAdded)
+ {
+ deleteOnExitHookAdded = true;
+
+ Runtime.getRuntime().addShutdownHook
+ (
+ new Thread
+ (
+ new Runnable()
+ {
+ public void
+ run()
+ {
+ synchronized (dirsToDelete)
+ {
+ for (Iterator i = dirsToDelete.iterator(); i.hasNext();)
+ {
+ deleteDir((File) i.next());
+ }
+ }
+ }
+ }
+ )
+ );
+ }
+
+ synchronized (dirsToDelete)
+ {
+ dirsToDelete.add(dir);
+ }
+ }
+
+
+
+ static String
+ getResource(String key)
+ {
+ if (bundle == null)
+ {
+ bundle = ResourceBundle.getBundle("be.re.io.res.Res");
+ }
+
+ return bundle.getString(key);
+ }
+
+
+
+ public static boolean
+ isSymbolicLink(File file) throws IOException
+ {
+ return !file.getCanonicalPath().equals(file.getAbsolutePath());
+ }
+
+} // Util
diff --git a/src/be/re/net/BasicUser.java b/src/be/re/net/BasicUser.java
new file mode 100644
index 0000000..570a8a0
--- /dev/null
+++ b/src/be/re/net/BasicUser.java
@@ -0,0 +1,142 @@
+package be.re.net;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+
+
+public class BasicUser implements User
+
+{
+
+ private String password;
+ private PropertyChangeSupport propertyChange =
+ new PropertyChangeSupport(this);
+ private String username;
+
+
+
+ public
+ BasicUser()
+ {
+ this(null, null);
+ }
+
+
+
+ public
+ BasicUser(String username, String password)
+ {
+ this.username = username;
+ this.password = password;
+ }
+
+
+
+ public void
+ addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ propertyChange.addPropertyChangeListener(listener);
+ }
+
+
+
+ public void
+ addPropertyChangeListener
+ (
+ String propertyName,
+ PropertyChangeListener listener
+ )
+ {
+ propertyChange.addPropertyChangeListener(propertyName, listener);
+ }
+
+
+
+ public String
+ getPassword()
+ {
+ return password;
+ }
+
+
+
+ public String
+ getUsername()
+ {
+ return username;
+ }
+
+
+
+ public void
+ removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ propertyChange.removePropertyChangeListener(listener);
+ }
+
+
+
+ public void
+ removePropertyChangeListener
+ (
+ String propertyName,
+ PropertyChangeListener listener
+ )
+ {
+ propertyChange.removePropertyChangeListener(propertyName, listener);
+ }
+
+
+
+ public void
+ setPassword(String value)
+ {
+ if
+ (
+ (
+ value == null &&
+ password == null
+ ) ||
+ (
+ value != null &&
+ value.equals(password)
+ )
+ )
+ {
+ return;
+ }
+
+ String old = password;
+
+ password = value;
+ propertyChange.firePropertyChange("password", old, value);
+ }
+
+
+
+ public void
+ setUsername(String value)
+ {
+ if
+ (
+ (
+ value == null &&
+ username == null
+ ) ||
+ (
+ value != null &&
+ value.equals(username)
+ )
+ )
+ {
+ return;
+ }
+
+ String old = username;
+
+ username = value;
+ propertyChange.firePropertyChange("username", old, value);
+ }
+
+} // BasicUser
diff --git a/src/be/re/net/Headers.java b/src/be/re/net/Headers.java
new file mode 100644
index 0000000..d16285e
--- /dev/null
+++ b/src/be/re/net/Headers.java
@@ -0,0 +1,282 @@
+package be.re.net;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+
+
+/**
+ * Several Internet protocols use headers. This class can be used to manage
+ * them. Headers can occur more than once. Even name/value pairs may be
+ * dupplicated.
+ * @author Werner Donn\u00e9
+ */
+
+public class Headers
+
+{
+
+ private List headers = new ArrayList();
+
+
+
+ /**
+ * Adds a name/value pair.
+ */
+
+ public void
+ add(String name, String value)
+ {
+ headers.add(new Header(name, value));
+ }
+
+
+
+ /**
+ * Adds all the headers in <code>headers</code>.
+ */
+
+ public void
+ add(Headers headers)
+ {
+ Header[] all = headers.getAll();
+
+ for (int i = 0; i < all.length; ++i)
+ {
+ add(all[i].getName(), all[i].getValue());
+ }
+ }
+
+
+
+ /**
+ * Removes all headers.
+ */
+
+ public void
+ clear()
+ {
+ headers.clear();
+ }
+
+
+
+ /**
+ * Returns all the values for the header <code>name</code> in the order of
+ * appearance.
+ */
+
+ public String[]
+ get(String name)
+ {
+ List values = new ArrayList();
+
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ if (tuple.name.equalsIgnoreCase(name))
+ {
+ values.add(tuple.value);
+ }
+ }
+
+ return (String[]) values.toArray(new String[values.size()]);
+ }
+
+
+
+ /**
+ * Returns all headers.
+ */
+
+ public Header[]
+ getAll()
+ {
+ return (Header[]) headers.toArray(new Header[headers.size()]);
+ }
+
+
+
+ /**
+ * Returns all the values for the header <code>name</code> in the order of
+ * appearance. If a header value is a comma-separated list, the elements in
+ * the list are added separately in the order of appearance and without
+ * surrounding whitespace.
+ */
+
+ public String[]
+ getValuesFromList(String name)
+ {
+ List values = new ArrayList();
+
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ if (tuple.name.equalsIgnoreCase(name))
+ {
+ StringTokenizer tokenizer = new StringTokenizer(tuple.value, ",");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ values.add(tokenizer.nextToken().trim());
+ }
+ }
+ }
+
+ return (String[]) values.toArray(new String[values.size()]);
+ }
+
+
+
+ /**
+ * Removes all headers with <code>name</code> as their name.
+ */
+
+ public void
+ remove(String name)
+ {
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ if (tuple.name.equalsIgnoreCase(name))
+ {
+ i.remove();
+ }
+ }
+ }
+
+
+
+ /**
+ * Removes one name/value pair.
+ */
+
+ public void
+ remove(String name, String value)
+ {
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ if (tuple.name.equalsIgnoreCase(name) && tuple.value.equals(value))
+ {
+ i.remove();
+ }
+ }
+ }
+
+
+
+ /**
+ * Replaces all the headers with <code>name</code> as their name with the
+ * given name/value pair.
+ */
+
+ public void
+ set(String name, String value)
+ {
+ remove(name);
+ add(name, value);
+ }
+
+
+
+ /**
+ * Replaces one name/value pair.
+ */
+
+ public void
+ set(String name, String oldValue, String newValue)
+ {
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ if (tuple.name.equalsIgnoreCase(name) && tuple.value.equals(oldValue))
+ {
+ tuple.value = newValue;
+ }
+ }
+ }
+
+
+
+ /**
+ * Returns the number of headers.
+ */
+
+ public int
+ size()
+ {
+ return headers.size();
+ }
+
+
+
+ /**
+ * A string representation of all headers. The string can be used in Internet
+ * protocols.
+ */
+
+ public String
+ toString()
+ {
+ String result = "";
+
+ for (Iterator i = headers.iterator(); i.hasNext();)
+ {
+ Header tuple = (Header) i.next();
+
+ result += tuple.name + ":" + tuple.value + "\r\n";
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Represents one header.
+ * @author Werner Donn\u00e9
+ */
+
+ public class Header
+
+ {
+
+ private String name;
+ private String value;
+
+
+
+ public
+ Header(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+
+
+ public String
+ getName()
+ {
+ return name;
+ }
+
+
+
+ public String
+ getValue()
+ {
+ return value;
+ }
+
+ } // Header
+
+} // Headers
diff --git a/src/be/re/net/User.java b/src/be/re/net/User.java
new file mode 100644
index 0000000..b61bdec
--- /dev/null
+++ b/src/be/re/net/User.java
@@ -0,0 +1,9 @@
+package be.re.net;
+
+public interface User
+{
+ public String getPassword ();
+ public String getUsername ();
+ public void setPassword (String password);
+ public void setUsername (String username);
+}
diff --git a/src/be/re/net/Util.java b/src/be/re/net/Util.java
new file mode 100644
index 0000000..12118a5
--- /dev/null
+++ b/src/be/re/net/Util.java
@@ -0,0 +1,1265 @@
+package be.re.net;
+
+import be.re.io.ReadLineInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+
+
+public class Util
+
+{
+
+ private final static char[] MARK =
+ {'-', '_', '.', '!', '~', '*', '\'', '(', ')'};
+ private final static char[] PCHAR_SPECIALS =
+ {':', '@', '&', '=', '+', '$', ','}; // Less than uric_no_slash.
+ private final static char[] RESERVED =
+ {';', '/', '?', ':', '@', '&', '=', '+', '$', ','};
+
+ private final static String[] archiveExtensions =
+ {"ear", "jar", "rar", "war", "zip"};
+ private static ResourceBundle bundle = null;
+ private static Map bundles = new HashMap();
+
+
+
+ private static boolean
+ compareBytes(byte[] b1, byte[] b2, int length)
+ {
+ for (int i = 0; i < length; ++i)
+ {
+ if (b1[i] != b2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ private static String
+ escapeUriPart(String s, TestChar t)
+ {
+ byte[] bytes = null;
+ String result = "";
+
+ try
+ {
+ bytes = s.getBytes("UTF-8");
+ }
+
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e); // Would be a bug.
+ }
+
+ char[] c = new char[bytes.length];
+
+ for (int i = 0; i < bytes.length; ++i)
+ {
+ c[i] = (char) (0xff & bytes[i]);
+ }
+
+ for (int i = 0; i < c.length; ++i)
+ {
+ result +=
+ !t.test(c, i) ?
+ ("%" + (c[i] < 0x10 ? "0" : "") + Integer.toHexString(c[i])) :
+ new String(c, i, 1);
+ }
+
+ return result;
+ }
+
+
+
+ public static String
+ escapeUriPathSegment(String segment)
+ {
+ return escapeUriPathSegment(segment, false);
+ }
+
+
+
+ public static String
+ escapeUriPathSegment(String segment, final boolean noParameters)
+ {
+ return
+ escapeUriPart
+ (
+ segment,
+ new TestChar()
+ {
+ public boolean
+ test(char[] c, int i)
+ {
+ return
+ isPChar(c[i]) || (!noParameters && c[i] == ';') ||
+ isStartOfEscape(c, i);
+ }
+ }
+ );
+ }
+
+
+
+ public static String
+ escapeUriPathSegments(String path)
+ {
+ return escapeUriPathSegments(path, false);
+ }
+
+
+
+ public static String
+ escapeUriPathSegments(String path, boolean noParameters)
+ {
+ String result = "";
+ StringTokenizer tokenizer = new StringTokenizer(path, "/");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ result +=
+ (result.equals("") ? "" : "/") +
+ escapeUriPathSegment(tokenizer.nextToken(), noParameters);
+ }
+
+ return
+ (path.length() > 0 && path.charAt(0) == '/' ? "/" : "") + result +
+ (path.length() > 1 && path.charAt(path.length() - 1) == '/' ? "/" : "");
+ }
+
+
+
+ public static String
+ escapeUriQueryString(String queryString)
+ {
+ return
+ escapeUriPart
+ (
+ queryString,
+ new TestChar()
+ {
+ public boolean
+ test(char[] c, int i)
+ {
+ return (!isReserved(c[i]) || c[i] == '=') && isUriChar(c, i);
+ }
+ }
+ );
+ }
+
+
+
+ public static String
+ escapeUriQueryStrings(String path)
+ {
+ String result = "";
+ StringTokenizer tokenizer = new StringTokenizer(path, "&");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ result +=
+ (result.equals("") ? "" : "&") +
+ escapeUriQueryString(tokenizer.nextToken());
+ }
+
+ return result;
+ }
+
+
+
+ public static String
+ escapeUriReference(String reference)
+ {
+ return
+ escapeUriPart
+ (
+ reference,
+ new TestChar()
+ {
+ public boolean
+ test(char[] c, int i)
+ {
+ return !isReserved(c[i]) && isUriChar(c, i);
+ }
+ }
+ );
+ }
+
+
+
+ private static URL
+ escapedComposedUrl(String url) throws MalformedURLException
+ {
+ return
+ new URL
+ (
+ url.substring(0, url.indexOf(':')) + ":" +
+ escapedUrl(extractSubUrl(url).toString()).toString() + "!/" +
+ escapeUriPathSegments(extractComposedUrlEntry(url)).toString()
+ );
+ }
+
+
+
+ private static File
+ escapedFile(File file)
+ {
+ return
+ new File
+ (
+ escapeUriPathSegments
+ (
+ file.getAbsolutePath().replace(File.separatorChar, '/')
+ )
+ );
+ }
+
+
+
+ public static String
+ escapedRelativeUrl(String url, String protocol)
+ {
+ return escapedRelativeUrl(url, protocol, false);
+ }
+
+
+
+ public static String
+ escapedRelativeUrl(String url, String protocol, boolean noParameters)
+ {
+ int queryStart = -1;
+ int referenceStart = -1;
+
+ if ("http".equals(protocol) || "https".equals(protocol))
+ {
+ queryStart = url.indexOf('?');
+ referenceStart = url.indexOf('#', queryStart != -1 ? queryStart : 0);
+ }
+
+ return
+ escapeUriPathSegments
+ (
+ url.substring
+ (
+ 0,
+ queryStart != -1 ?
+ queryStart : (referenceStart != -1 ? referenceStart : url.length())
+ ),
+ noParameters
+ ) +
+ (
+ queryStart != -1 ?
+ (
+ "?" +
+ escapeUriQueryStrings
+ (
+ url.substring
+ (
+ queryStart + 1,
+ referenceStart != -1 ? referenceStart : url.length()
+ )
+ )
+ ) : ""
+ ) +
+ (
+ referenceStart != -1 ?
+ ("#" + escapeUriReference(url.substring(referenceStart + 1))) : ""
+ );
+ }
+
+
+
+ public static URL
+ escapedUrl(String url) throws MalformedURLException
+ {
+ return escapedUrl(url, false);
+ }
+
+
+
+ public static URL
+ escapedUrl(String url, boolean noParameters) throws MalformedURLException
+ {
+ int colon = url.indexOf(':');
+
+ if (colon != -1 && isComposedUrl(url))
+ {
+ return escapedComposedUrl(url);
+ }
+
+ int pathStart =
+ colon != -1 ?
+ url.indexOf
+ (
+ '/',
+ colon +
+ (
+ url.length() > colon + 2 &&
+ url.substring(colon + 1, colon + 3).equals("//") ? 3 : 1
+ )
+ ) : 0;
+
+ return
+ pathStart == -1 ?
+ new URL(url) :
+ new URL
+ (
+ url.substring(0, pathStart) +
+ escapedRelativeUrl
+ (
+ url.substring(pathStart),
+ colon != -1 ? url.substring(0, colon) : null,
+ noParameters
+ )
+ );
+ }
+
+
+
+ public static String
+ extractComposedUrlEntry(URL url)
+ {
+ return extractComposedUrlEntry(url.getFile());
+ }
+
+
+
+ public static String
+ extractComposedUrlEntry(String url)
+ {
+ int index = url.lastIndexOf("!/");
+
+ return
+ index == -1 || index + 2 == url.length() ? "" : url.substring(index + 2);
+ }
+
+
+
+ public static URL
+ extractSubUrl(URL url) throws MalformedURLException
+ {
+ if (!isComposedUrl(url))
+ {
+ throw new MalformedURLException(url.toString() + ": no sub-URL");
+ }
+
+ return
+ new URL
+ (
+ url.toString().substring
+ (
+ url.getProtocol().length() + 1, url.toString().lastIndexOf("!/")
+ )
+ );
+ }
+
+
+
+ public static URL
+ extractSubUrl(String url) throws MalformedURLException
+ {
+ return extractSubUrl(new URL(url));
+ }
+
+
+
+ public static URL
+ fileToUrl(File file)
+ {
+ try
+ {
+ return
+ escapedFile
+ (
+ new File
+ (
+ file.getAbsolutePath().replace(File.separatorChar, '/')
+ )
+ ).toURL();
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new RuntimeException(e);
+ // We made sure the path can be parsed as an URL by escaping it.
+ }
+ }
+
+
+
+ private static String
+ getExtension(URL url)
+ {
+ return url.toString().substring(url.toString().lastIndexOf(".") + 1);
+ }
+
+
+
+ public static String
+ getFTPFilename(URL url)
+ {
+ // According to RFC 959 the file system conventions of the involved
+ // server must be followed. The applicable information is, however, not
+ // available anymore in the URL, because there the URI rules are followed.
+ // Therefore, we assure to have forward slashes, which are widely accepted.
+
+ return urlToFile(url).getPath().replace('\\', '/');
+ }
+
+
+
+ public static String
+ getHttpReasonPhrase(int statusCode)
+ {
+ return getHttpReasonPhrase(statusCode, new String[0]);
+ }
+
+
+
+ public static String
+ getHttpReasonPhrase(int statusCode, String languageTag)
+ {
+ return getHttpReasonPhrase(statusCode, new String[]{languageTag});
+ }
+
+
+
+ public static String
+ getHttpReasonPhrase(int statusCode, String[] languageTags)
+ {
+ return getResource("http_" + String.valueOf(statusCode), languageTags);
+ }
+
+
+
+ public static String
+ getJarEntry(URL url)
+ {
+ return unescapeUriSpecials(extractComposedUrlEntry(url));
+ }
+
+
+
+ public static String
+ getLastPathSegment(URL url)
+ {
+ return
+ getLastPathSegment
+ (
+ isComposedUrl(url) ?
+ url.getFile().substring(url.getFile().lastIndexOf("!/") + 1) :
+ url.getFile()
+ );
+ }
+
+
+
+ /**
+ * A trailing slash is ignored to determine the last segment, but it is part
+ * of it if it is there.
+ */
+
+ public static String
+ getLastPathSegment(String path)
+ {
+ // We're not interested in the last character if it is a slash.
+
+ path = path.replace('\\', '/');
+
+ return path.substring(path.lastIndexOf('/', path.length() - 2) + 1);
+ }
+
+
+
+ public static Properties
+ getParameters(String queryString)
+ {
+ if (queryString == null)
+ {
+ return new Properties();
+ }
+
+ Properties result = new Properties();
+ StringTokenizer tokenizer = new StringTokenizer(queryString, "&");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+ int index = token.indexOf('=');
+
+ if (index != -1)
+ {
+ result.
+ setProperty(token.substring(0, index), token.substring(index + 1));
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public static String[]
+ getPathSegments(String path)
+ {
+ List result = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(path, "/");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (!"".equals(token))
+ {
+ result.add(token);
+ }
+ }
+
+ return (String[]) result.toArray(new String[0]);
+ }
+
+
+
+ public static String
+ getResource(String key)
+ {
+ if (bundle == null)
+ {
+ bundle = ResourceBundle.getBundle("be.re.net.res.Res");
+ }
+
+ return bundle.getString(key);
+ }
+
+
+
+ public static String
+ getResource(String key, String languageTag)
+ {
+ return getResource(key, new String[]{languageTag});
+ }
+
+
+
+ /**
+ * Try <code>languageTags</code> until a match is found. Return the default
+ * otherwise.
+ */
+
+ public static String
+ getResource(String key, String[] languageTags)
+ {
+ for (int i = 0; i < languageTags.length; ++i)
+ {
+ ResourceBundle bundle =
+ (ResourceBundle)
+ bundles.get(be.re.util.Util.getLocale(languageTags[i]));
+
+ if (bundle == null)
+ {
+ bundle = getResourceBundle(languageTags[i]);
+
+ if (bundle != null)
+ {
+ bundles.put(be.re.util.Util.getLocale(languageTags[i]), bundle);
+ }
+ }
+
+ if (bundle != null)
+ {
+ return bundle.getString(key);
+ }
+ }
+
+ return getResource(key);
+ }
+
+
+
+ private static ResourceBundle
+ getResourceBundle(String language)
+ {
+ try
+ {
+ Locale locale = be.re.util.Util.getLocale(language);
+ ResourceBundle result =
+ ResourceBundle.getBundle("be.re.net.res.Res", locale);
+
+ return result.getLocale().equals(locale) ? result : null;
+ }
+
+ catch (MissingResourceException e)
+ {
+ return null;
+ }
+ }
+
+
+
+ public static boolean
+ hasBody(Headers headers)
+ {
+ return
+ (
+ headers.get("Content-Length").length > 0 &&
+ Integer.parseInt(headers.get("Content-Length")[0]) > 0
+ ) || headers.get("Transfer-Encoding").length > 0;
+ }
+
+
+
+ public static long
+ httpDate(String s)
+ {
+ String[] patterns =
+ new String[]
+ {
+ "EEE, dd MMM yyyy HH:mm:ss Z", // RFC1123-date
+ "EEEE, dd-MMM-yy HH:mm:ss Z", // RFC850-date
+ "EEE MMM dd HH:mm:ss yyyy", // ASCTIME-date
+ "EEE MMM d HH:mm:ss yyyy" // ASCTIME-date
+ };
+
+ for (int i = 0; i < patterns.length; ++i)
+ {
+ try
+ {
+ Date date =
+ new SimpleDateFormat(patterns[i], new Locale("en", "US")).parse(s);
+
+ if (date != null)
+ {
+ return date.getTime();
+ }
+ }
+
+ catch (ParseException e)
+ {
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static String
+ httpDate(long date)
+ {
+ return
+ new SimpleDateFormat
+ (
+ "EEE, dd MMM yyyy HH:mm:ss Z",
+ new Locale("en", "US")
+ ).format(new Date(date));
+ }
+
+
+
+ /**
+ * Extract the status code from the status line or <code>-1</code> if that is
+ * not possible.
+ */
+
+ public static int
+ httpStatusCode(String statusLine)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(statusLine);
+
+ if
+ (
+ tokenizer.countTokens() < 2 ||
+ !tokenizer.nextToken().startsWith("HTTP/")
+ )
+ {
+ return -1;
+ }
+
+ try
+ {
+ return Integer.parseInt(tokenizer.nextToken());
+ }
+
+ catch (NumberFormatException e)
+ {
+ return -1;
+ }
+ }
+
+
+
+ public static boolean
+ isAncestor(URL ancestor, URL child)
+ {
+ try
+ {
+ if (isArchive(ancestor) && "jar".equals(child.getProtocol()))
+ {
+ return isAncestor(new URL("jar:" + ancestor.toString() + "!/"), child);
+ }
+
+ if
+ (
+ "file".equals(ancestor.getProtocol()) &&
+ "jar".equals(child.getProtocol())
+ )
+ {
+ return isAncestor(ancestor, Util.extractSubUrl(child));
+ }
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new RuntimeException(e); // Would be a bug.
+ }
+
+ if
+ (
+ ancestor.getProtocol() == null ||
+ child.getProtocol() == null ||
+ !ancestor.getProtocol().equals(child.getProtocol()) ||
+ !ancestor.getHost().equals(child.getHost()) ||
+ ancestor.getPort() != child.getPort()
+ )
+ {
+ return false;
+ }
+
+ return
+ isAncestor(ancestor.getFile(), child.getFile()) ||
+ (
+ "file".equals(ancestor.getProtocol()) &&
+ "file".equals(child.getProtocol()) &&
+ isAncestor(urlToFile(ancestor), urlToFile(child))
+ );
+ }
+
+
+
+ private static boolean
+ isAncestor(String path1, String path2)
+ {
+ StringTokenizer t1 = new StringTokenizer(path1, "/");
+ StringTokenizer t2 = new StringTokenizer(path2, "/");
+
+ while (t1.hasMoreTokens())
+ {
+ if (!t2.hasMoreTokens() || !t1.nextToken().equals(t2.nextToken()))
+ {
+ return false;
+ }
+ }
+
+ return t2.hasMoreTokens();
+ }
+
+
+
+ public static boolean
+ isAncestor(File file1, File file2)
+ {
+ return
+ file1 != null && file2 != null &&
+ (
+ file1.equals(file2.getParentFile()) ||
+ isAncestor(file1, file2.getParentFile())
+ );
+ }
+
+
+
+ public static boolean
+ isArchive(URL url)
+ {
+ return
+ url.getProtocol() != null && url.getProtocol().equals("file") &&
+ isArchiveExtension(getExtension(url));
+ }
+
+
+
+ private static boolean
+ isArchiveExtension(String s)
+ {
+ for (int i = 0; i < archiveExtensions.length; ++i)
+ {
+ if (archiveExtensions[i].equalsIgnoreCase(s))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ public static boolean
+ isComposedUrl(URL url)
+ {
+ return
+ "jar".equals(url.getProtocol()) && url.toString().lastIndexOf("!/") != -1;
+ }
+
+
+
+ public static boolean
+ isComposedUrl(String url) throws MalformedURLException
+ {
+ return isComposedUrl(new URL(url));
+ }
+
+
+
+ private static boolean
+ isPChar(char c)
+ {
+ return isUnreserved(c) || be.re.util.Array.inArray(PCHAR_SPECIALS, c);
+ }
+
+
+
+ private static boolean
+ isReserved(char c)
+ {
+ return be.re.util.Array.inArray(RESERVED, c);
+ }
+
+
+
+ private static boolean
+ isStartOfEscape(char[] c, int i)
+ {
+ return
+ c[i] == '%' && i < c.length - 2 &&
+ be.re.util.Util.isInteger(new String(c, i + 1, 2), 16);
+ }
+
+
+
+ private static boolean
+ isUnreserved(char c)
+ {
+ return
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || be.re.util.Array.inArray(MARK, c);
+ }
+
+
+
+ private static boolean
+ isUriChar(char[] c, int i)
+ {
+ return isReserved(c[i]) || isUnreserved(c[i]) || isStartOfEscape(c, i);
+ }
+
+
+
+ public static boolean
+ isUrl(String s)
+ {
+ try
+ {
+ return s != null && new URL(s) != null;
+ }
+
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static Headers
+ readHeaders(ReadLineInputStream in) throws IOException
+ {
+ Headers headers = new Headers();
+ byte[] line;
+
+ while ((line = in. readLine()) != null && line.length > 0)
+ {
+ String s = new String(line);
+
+ if (s.indexOf(':') != -1)
+ {
+ headers.add
+ (
+ s.substring(0, s.indexOf(':')).trim(),
+ s.substring(s.indexOf(':') + 1).trim()
+ );
+ }
+ }
+
+ return headers;
+ }
+
+
+
+ public static String
+ resolveFullPath(String basePath, String relativePath)
+ throws MalformedURLException
+ {
+ return
+ resolveFullPath
+ (
+ relativePath.startsWith("/") ?
+ relativePath :
+ (basePath.substring(0, basePath.lastIndexOf('/') + 1) + relativePath)
+ );
+ }
+
+
+
+ /**
+ * Resolves ".." segements and multiple slashes.
+ */
+
+ public static String
+ resolveFullPath(String path) throws MalformedURLException
+ {
+ List segments = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(path, "/");
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (!token.equals("."))
+ {
+ if (token.equals(".."))
+ {
+ if (segments.size() == 0)
+ {
+ throw
+ new MalformedURLException
+ (
+ "URL specification goes outside of its root"
+ );
+ }
+
+ segments.remove(segments.size() - 1);
+ }
+ else
+ {
+ segments.add(token);
+ }
+ }
+ }
+
+ String result = "";
+
+ for
+ (
+ Iterator i = segments.iterator();
+ i.hasNext();
+ result += "/" + (String) i.next()
+ );
+
+ return result.equals("") ? "/" : (result + (path.endsWith("/") ? "/" : ""));
+ }
+
+
+
+ public static URL
+ resolvePath(URL url) throws MalformedURLException
+ {
+ if (isComposedUrl(url))
+ {
+ try
+ {
+ String path = resolveFullPath(extractComposedUrlEntry(url));
+
+ return
+ new URL
+ (
+ url.getProtocol() + ":" +
+ resolvePath(extractSubUrl(url)).toString() + "!" + path
+ );
+ }
+
+ catch (MalformedURLException e)
+ {
+ // This considers a jar URL to be an ordinary directory.
+
+ return
+ resolvePath
+ (
+ new URL
+ (
+ extractSubUrl(url).toString() + "/" + extractComposedUrlEntry(url)
+ )
+ );
+ }
+ }
+
+ String path = resolveFullPath(url.getFile());
+
+ return
+ new URL
+ (
+ new URL
+ (
+ url.getProtocol() + ":" +
+ (url.getAuthority() != null ? "//" + url.getAuthority() : "")
+ ),
+ path + (url.getRef() != null ? ("#" + url.getRef()) : "")
+ );
+ }
+
+
+
+ public static URL
+ setScheme(URL url, String scheme) throws MalformedURLException
+ {
+ try
+ {
+ return
+ escapedUrl
+ (
+ new URI
+ (
+ scheme,
+ unescapeUriSpecials(url.getUserInfo()),
+ // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getHost()),
+ // Avoid escaping escapes by URI.
+ url.getPort(),
+ unescapeUriSpecials(url.getPath()),
+ // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getQuery()),
+ // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getRef())
+ // Avoid escaping escapes by URI.
+ ).toString()
+ );
+ }
+
+ catch (URISyntaxException e)
+ {
+ throw new MalformedURLException(e.getMessage());
+ }
+ }
+
+
+
+ public static URL
+ setUserInfo(URL url, String userInfo) throws MalformedURLException
+ {
+ try
+ {
+ return
+ escapedUrl
+ (
+ new URI
+ (
+ url.getProtocol(),
+ unescapeUriSpecials(userInfo), // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getHost()),
+ // Avoid escaping escapes by URI.
+ url.getPort(),
+ unescapeUriSpecials(url.getPath()),
+ // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getQuery()),
+ // Avoid escaping escapes by URI.
+ unescapeUriSpecials(url.getRef())
+ // Avoid escaping escapes by URI.
+ ).toString()
+ );
+ }
+
+ catch (URISyntaxException e)
+ {
+ throw new MalformedURLException(e.getMessage());
+ }
+ }
+
+
+
+ public static String
+ stripQuery(String url)
+ {
+ int query = url.lastIndexOf('?');
+ int reference = -1;
+
+ return
+ query != -1 ?
+ (
+ url.substring(0, query) +
+ (
+ (reference = url.lastIndexOf('#')) != -1 ?
+ url.substring(reference) : ""
+ )
+ ) : url;
+ }
+
+
+
+ public static URL
+ stripQuery(URL url) throws MalformedURLException
+ {
+ return new URL(stripQuery(url.toString()));
+ }
+
+
+
+ public static URL
+ stripUserInfo(URL url)
+ {
+ try
+ {
+ return
+ new URL
+ (
+ url.getProtocol() + ":" +
+ (url.getHost() != null ? "//" + url.getHost() : "") +
+ (url.getPort() != -1 ? (":" + String.valueOf(url.getPort())) : "")
+ + url.getFile() + (url.getRef() != null ? ("#" + url.getRef()) : "")
+ );
+ }
+
+ catch (MalformedURLException e)
+ {
+ throw new RuntimeException(e); // Would be a bug.
+ }
+ }
+
+
+
+ public static void
+ testUrl(URL url) throws IOException
+ {
+ try
+ {
+ url.openStream().close();
+ }
+
+ catch (Exception e)
+ {
+ throw
+ new be.re.io.IOException
+ (
+ "The URL \"" + url.toString() + "\" can't be opened.",
+ e
+ );
+ }
+ }
+
+
+
+ public static String
+ unescapeUriSpecials(String s)
+ {
+ if (s == null)
+ {
+ return null;
+ }
+
+ int count = 0;
+ byte[] result = new byte[s.length()];
+
+ for (int i = 0; i < s.length(); ++i)
+ {
+ if
+ (
+ s.charAt(i) == '%' &&
+ s.length() - i > 2 &&
+ be.re.util.Util.isInteger(s.substring(i + 1, i + 3), 16)
+ )
+ {
+ result[count++] =
+ (byte) Integer.parseInt(s.substring(i + 1, i + 3), 16);
+ i += 2;
+ }
+ else
+ {
+ result[count++] = (byte) s.charAt(i);
+ }
+ }
+
+ try
+ {
+ return new String(result, 0, count, "UTF-8");
+ }
+
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e); // Would be a bug.
+ }
+ }
+
+
+
+ public static File
+ urlToFile(URL url)
+ {
+ return new File(unescapeUriSpecials(url.getFile()));
+ }
+
+
+
+ public static User
+ userFromUrl(URL url)
+ {
+ User user = new BasicUser();
+ String userInfo = url.getUserInfo();
+
+ if (userInfo == null)
+ {
+ return user;
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(userInfo, ":");
+
+ if (tokenizer.hasMoreTokens())
+ {
+ user.setUsername(tokenizer.nextToken());
+ }
+
+ if (tokenizer.hasMoreTokens())
+ {
+ user.setPassword(unescapeUriSpecials(tokenizer.nextToken()));
+ }
+
+ return user;
+ }
+
+
+
+ private interface TestChar
+
+ {
+
+ /**
+ * Return <code>true</code> if the character should not be escaped and
+ * <code>false</code> otherwise.
+ */
+
+ public boolean test (char[] c, int i);
+
+ } // TestChar
+
+} // Util
diff --git a/src/be/re/util/Array.java b/src/be/re/util/Array.java
new file mode 100644
index 0000000..3c4cffc
--- /dev/null
+++ b/src/be/re/util/Array.java
@@ -0,0 +1,941 @@
+package be.re.util;
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class Array
+
+{
+
+ public static Object[]
+ append(Object[] array, Object object)
+ {
+ Object[] newArray =
+ (Object[]) java.lang.reflect.Array.
+ newInstance(array.getClass().getComponentType(), array.length + 1);
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = object;
+
+ return newArray;
+ }
+
+
+
+ public static byte[]
+ append(byte[] array, byte b)
+ {
+ byte[] newArray = new byte[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = b;
+
+ return newArray;
+ }
+
+
+
+ public static char[]
+ append(char[] array, char c)
+ {
+ char[] newArray = new char[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = c;
+
+ return newArray;
+ }
+
+
+
+ public static short[]
+ append(short[] array, short s)
+ {
+ short[] newArray = new short[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = s;
+
+ return newArray;
+ }
+
+
+
+ public static int[]
+ append(int[] array, int i)
+ {
+ int[] newArray = new int[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = i;
+
+ return newArray;
+ }
+
+
+
+ public static long[]
+ append(long[] array, long l)
+ {
+ long[] newArray = new long[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = l;
+
+ return newArray;
+ }
+
+
+
+ public static float[]
+ append(float[] array, float f)
+ {
+ float[] newArray = new float[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = f;
+
+ return newArray;
+ }
+
+
+
+ public static double[]
+ append(double[] array, double d)
+ {
+ double[] newArray = new double[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ newArray[newArray.length - 1] = d;
+
+ return newArray;
+ }
+
+
+
+ public static Object[]
+ append(Object[] array, Object[] objects)
+ {
+ Object[] newArray =
+ (Object[]) java.lang.reflect.Array.newInstance
+ (
+ array.getClass().getComponentType(),
+ array.length + objects.length
+ );
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(objects, 0, newArray, array.length, objects.length);
+
+ return newArray;
+ }
+
+
+
+ public static byte[]
+ append(byte[] array, byte[] bytes)
+ {
+ byte[] newArray = new byte[array.length + bytes.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(bytes, 0, newArray, array.length, bytes.length);
+
+ return newArray;
+ }
+
+
+
+ public static char[]
+ append(char[] array, char[] chars)
+ {
+ char[] newArray = new char[array.length + chars.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(chars, 0, newArray, array.length, chars.length);
+
+ return newArray;
+ }
+
+
+
+ public static short[]
+ append(short[] array, short[] shorts)
+ {
+ short[] newArray = new short[array.length + shorts.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(shorts, 0, newArray, array.length, shorts.length);
+
+ return newArray;
+ }
+
+
+
+ public static int[]
+ append(int[] array, int[] ints)
+ {
+ int[] newArray = new int[array.length + ints.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(ints, 0, newArray, array.length, ints.length);
+
+ return newArray;
+ }
+
+
+
+ public static long[]
+ append(long[] array, long[] longs)
+ {
+ long[] newArray = new long[array.length + longs.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(longs, 0, newArray, array.length, longs.length);
+
+ return newArray;
+ }
+
+
+
+ public static float[]
+ append(float[] array, float[] floats)
+ {
+ float[] newArray = new float[array.length + floats.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(floats, 0, newArray, array.length, floats.length);
+
+ return newArray;
+ }
+
+
+
+ public static double[]
+ append(double[] array, double[] doubles)
+ {
+ double[] newArray = new double[array.length + doubles.length];
+
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ System.arraycopy(doubles, 0, newArray, array.length, doubles.length);
+
+ return newArray;
+ }
+
+
+
+ public static boolean
+ equal(Object[] array1, Object[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if
+ (
+ (
+ array1[i] == null &&
+ array2[i] != null
+ ) ||
+ (
+ array1[i] != null &&
+ array2[i] == null
+ ) ||
+ !array1[i].equals(array2[i])
+ )
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(byte[] array1, byte[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(char[] array1, char[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(short[] array1, short[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(int[] array1, int[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(long[] array1, long[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(float[] array1, float[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ equal(double[] array1, double[] array2)
+ {
+ if (array1.length != array2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array1.length; ++i)
+ {
+ if (array1[i] != array2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ inArray(Object[] array, Object object)
+ {
+ return indexOf(array, object) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(byte[] array, byte b)
+ {
+ return indexOf(array, b) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(char[] array, char c)
+ {
+ return indexOf(array, c) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(short[] array, short s)
+ {
+ return indexOf(array, s) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(int[] array, int i)
+ {
+ return indexOf(array, i) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(long[] array, long l)
+ {
+ return indexOf(array, l) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(float[] array, float f)
+ {
+ return indexOf(array, f) != -1;
+ }
+
+
+
+ public static boolean
+ inArray(double[] array, double d)
+ {
+ return indexOf(array, d) != -1;
+ }
+
+
+
+ public static int
+ indexOf(Object[] array, Object object)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i].equals(object))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(byte[] array, byte b)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == b)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(char[] array, char c)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == c)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(short[] array, short s)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == s)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(int[] array, int i)
+ {
+ for (int j = 0; j < array.length; ++j)
+ {
+ if (array[j] == i)
+ {
+ return j;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(long[] array, long l)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == l)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(float[] array, float f)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == f)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static int
+ indexOf(double[] array, double d)
+ {
+ for (int i = 0; i < array.length; ++i)
+ {
+ if (array[i] == d)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+
+ public static Object[]
+ insert(Object[] array, int pos, Object object)
+ {
+ Object[] newArray =
+ (Object[]) java.lang.reflect.Array.
+ newInstance(array.getClass().getComponentType(), array.length + 1);
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = object;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static byte[]
+ insert(byte[] array, int pos, byte b)
+ {
+ byte[] newArray = new byte[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = b;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static char[]
+ insert(char[] array, int pos, char c)
+ {
+ char[] newArray = new char[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = c;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static short[]
+ insert(short[] array, int pos, short s)
+ {
+ short[] newArray = new short[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = s;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static int[]
+ insert(int[] array, int pos, int i)
+ {
+ int[] newArray = new int[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = i;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static long[]
+ insert(long[] array, int pos, long l)
+ {
+ long[] newArray = new long[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = l;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static float[]
+ insert(float[] array, int pos, float f)
+ {
+ float[] newArray = new float[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = f;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static double[]
+ insert(double[] array, int pos, double d)
+ {
+ double[] newArray = new double[array.length + 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ newArray[pos] = d;
+ System.arraycopy(array, pos, newArray, pos + 1, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static Object[]
+ insert(Object[] array, int pos, Object[] objects)
+ {
+ Object[] newArray =
+ (Object[]) java.lang.reflect.Array.
+ newInstance
+ (
+ array.getClass().getComponentType(),
+ array.length + objects.length
+ );
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(objects, 0, newArray, pos, objects.length);
+ System.
+ arraycopy(array, pos, newArray, pos + objects.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static byte[]
+ insert(byte[] array, int pos, byte[] bytes)
+ {
+ byte[] newArray = new byte[array.length + bytes.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(bytes, 0, newArray, pos, bytes.length);
+ System.
+ arraycopy(array, pos, newArray, pos + bytes.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static char[]
+ insert(char[] array, int pos, char[] chars)
+ {
+ char[] newArray = new char[array.length + chars.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(chars, 0, newArray, pos, chars.length);
+ System.
+ arraycopy(array, pos, newArray, pos + chars.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static short[]
+ insert(short[] array, int pos, short[] shorts)
+ {
+ short[] newArray = new short[array.length + shorts.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(shorts, 0, newArray, pos, shorts.length);
+ System.
+ arraycopy(array, pos, newArray, pos + shorts.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static int[]
+ insert(int[] array, int pos, int[] ints)
+ {
+ int[] newArray = new int[array.length + ints.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(ints, 0, newArray, pos, ints.length);
+ System.
+ arraycopy(array, pos, newArray, pos + ints.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static long[]
+ insert(long[] array, int pos, long[] longs)
+ {
+ long[] newArray = new long[array.length + longs.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(longs, 0, newArray, pos, longs.length);
+ System.
+ arraycopy(array, pos, newArray, pos + longs.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static float[]
+ insert(float[] array, int pos, float[] floats)
+ {
+ float[] newArray = new float[array.length + floats.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(floats, 0, newArray, pos, floats.length);
+ System.
+ arraycopy(array, pos, newArray, pos + floats.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static double[]
+ insert(double[] array, int pos, double[] doubles)
+ {
+ double[] newArray = new double[array.length + doubles.length];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(doubles, 0, newArray, pos, doubles.length);
+ System.
+ arraycopy(array, pos, newArray, pos + doubles.length, array.length - pos);
+
+ return newArray;
+ }
+
+
+
+ public static Object[]
+ remove(Object[] array, int pos)
+ {
+ Object[] newArray =
+ (Object[]) java.lang.reflect.Array.
+ newInstance(array.getClass().getComponentType(), array.length - 1);
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static byte[]
+ remove(byte[] array, int pos)
+ {
+ byte[] newArray = new byte[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static char[]
+ remove(char[] array, int pos)
+ {
+ char[] newArray = new char[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static short[]
+ remove(short[] array, int pos)
+ {
+ short[] newArray = new short[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static int[]
+ remove(int[] array, int pos)
+ {
+ int[] newArray = new int[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static long[]
+ remove(long[] array, int pos)
+ {
+ long[] newArray = new long[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static float[]
+ remove(float[] array, int pos)
+ {
+ float[] newArray = new float[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+
+
+ public static double[]
+ remove(double[] array, int pos)
+ {
+ double[] newArray = new double[array.length - 1];
+
+ System.arraycopy(array, 0, newArray, 0, pos);
+ System.arraycopy(array, pos + 1, newArray, pos, array.length - pos - 1);
+
+ return newArray;
+ }
+
+} // Array
diff --git a/src/be/re/util/DigitalTree.java b/src/be/re/util/DigitalTree.java
new file mode 100644
index 0000000..5fd07b4
--- /dev/null
+++ b/src/be/re/util/DigitalTree.java
@@ -0,0 +1,207 @@
+package be.re.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+
+public class DigitalTree implements Cloneable
+
+{
+
+ private static final int BITS = 8;
+ private static final int MASK = (0x1 << BITS) - 1;
+ private static final int SIZE = (0x1 << BITS);
+
+ private Set keys;
+ private Node[] root = new Node[SIZE];
+
+
+
+ public
+ DigitalTree()
+ {
+ this(false);
+ }
+
+
+
+ public
+ DigitalTree(boolean saveKeys)
+ {
+ keys = saveKeys ? new HashSet() : null;
+ }
+
+
+
+ /**
+ * Returns a shallow copy.
+ */
+
+ public Object
+ clone()
+ {
+ DigitalTree copy = null;
+
+ try
+ {
+ copy = (DigitalTree) super.clone();
+ }
+
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+
+ copy.root = cloneNodes(root);
+
+ if (keys != null)
+ {
+ copy.keys = (Set) ((HashSet) keys).clone();
+ }
+
+ return copy;
+ }
+
+
+
+ private static Node[]
+ cloneNodes(Node[] nodes)
+ {
+ Node[] result = new Node[nodes.length];
+
+ for (int i = 0; i < nodes.length; ++i)
+ {
+ if (nodes[i] != null)
+ {
+ result[i] = (Node) nodes[i].clone();
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public Object
+ get(String key)
+ {
+ Node[] current = root;
+ int i = 0;
+ int length = key.length();
+ Node node = null;
+
+ for (i = 0; i < length && current != null; ++i)
+ {
+ char c = key.charAt(i);
+
+ for (int j = 0; j < 16 / BITS && current != null; ++j)
+ {
+ node = current[((MASK << (j * BITS)) & c) >>> (j * BITS)];
+ current = node != null ? node.nodes : null;
+ }
+ }
+
+ return i == length && node != null ? node.object : null;
+ }
+
+
+
+ public Set
+ keySet()
+ {
+ return keys;
+ }
+
+
+
+ public void
+ put(String key, Object o)
+ {
+ Node[] current = root;
+ int length = key.length();
+ Node node = null;
+
+ for (int i = 0; i < length; ++i)
+ {
+ char c = key.charAt(i);
+
+ for (int j = 0; j < 16 / BITS; ++j)
+ {
+ int value = ((MASK << (j * BITS)) & c) >>> (j * BITS);
+
+ if (current[value] == null)
+ {
+ current[value] = new Node();
+ }
+
+ if
+ (
+ current[value].nodes == null &&
+ (
+ i < length - 1 ||
+ j < (16 / BITS) - 1
+ )
+ )
+ {
+ current[value].nodes = new Node[SIZE];
+ }
+
+ node = current[value];
+ current = current[value].nodes;
+ }
+ }
+
+ node.object = o;
+
+ if (keys != null)
+ {
+ keys.add(key);
+ }
+ }
+
+
+
+ public void
+ remove(String key)
+ {
+ put(key, null);
+ }
+
+
+
+ private static class Node implements Cloneable
+
+ {
+
+ private Node[] nodes;
+ private Object object;
+
+
+
+ protected Object
+ clone()
+ {
+ Node copy = null;
+
+ try
+ {
+ copy = (Node) super.clone();
+ }
+
+ catch (CloneNotSupportedException e)
+ {
+ return null;
+ }
+
+ if (nodes != null)
+ {
+ copy.nodes = cloneNodes(nodes);
+ }
+
+ return copy;
+ }
+
+ } // Node
+
+} // DigitalTree
diff --git a/src/be/re/util/Equal.java b/src/be/re/util/Equal.java
new file mode 100644
index 0000000..3661395
--- /dev/null
+++ b/src/be/re/util/Equal.java
@@ -0,0 +1,24 @@
+package be.re.util;
+
+/**
+ * The Equal interface is meant for generic algorithms that need to be able to
+ * test two objects for equality. The comparison criterion is kept out of the
+ * algorithm.
+ * It is also kept out of the objects themselves. This would not be the case
+ * if <code> Object.equals </code> were used. That method implies only one
+ * criterion.
+ */
+
+public interface Equal
+{
+ /**
+ * The actual equal method.
+ * @param object the object encountered in the course of the algorithm.
+ * @param refData the reference data the encountered object is compared with.
+ * Normally this data is constant during the algorithm.
+ * @return <code> true </code> if the objects are equal according to the
+ * criterion, <code> false </code> otherwise.
+ */
+
+ public boolean equal (Object object, Object refData);
+}
diff --git a/src/be/re/util/UUID.java b/src/be/re/util/UUID.java
new file mode 100644
index 0000000..b5fe933
--- /dev/null
+++ b/src/be/re/util/UUID.java
@@ -0,0 +1,194 @@
+package be.re.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+
+
+/**
+ * Generates a version 1 UUID according to RFC 4122.
+ * @author Werner Donn\u00e9
+ */
+
+public class UUID
+
+{
+
+ private static final Random random = new SecureRandom();
+ private static final long startGregorianSinceEpoch =
+ -10010304000000L - // Gregorian in milliseconds since NTP (1/1/1900).
+ 2208988800000L; // Epoch in milliseconds since NTP.
+
+ private static long lastTimestamp = 0;
+ private static long timestampCounter = 0;
+
+
+
+ static
+ {
+ random.setSeed(System.currentTimeMillis());
+ }
+
+
+
+ public static String
+ format(byte[] uuid)
+ {
+ if (uuid.length != 16)
+ {
+ throw new IllegalArgumentException("UUID should be 16 bytes.");
+ }
+
+ char[] result = new char[36];
+
+ format(uuid, 0, 4, result, 0); // time_low
+ result[8] = '-';
+ format(uuid, 4, 2, result, 9); // time_mid
+ result[13] = '-';
+ format(uuid, 6, 2, result, 14); // time_hi
+ result[18] = '-';
+ format(uuid, 8, 2, result, 19); // time_hi
+ result[23] = '-';
+ format(uuid, 10, 6, result, 24); // time_hi
+
+ return new String(result);
+ }
+
+
+
+ private static void
+ format(byte[] uuid, int offsetUUID, int len, char[] result, int offsetResult)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ result[offsetResult + 2 * i] =
+ toHex((0x00ff & uuid[i + offsetUUID]) >>> 4);
+ result[offsetResult + 2 * i + 1] =
+ toHex(0x000f & uuid[i + offsetUUID]);
+ }
+ }
+
+
+
+ public static synchronized byte[]
+ generate()
+ {
+ byte[] clockSequence = new byte[2];
+ byte[] node = new byte[6];
+ byte[] result = new byte[16];
+
+ setTimestamp(result);
+ result[7] &= 0x1f; // version
+ random.nextBytes(clockSequence);
+ clockSequence[0] &= 0xbf; // variant
+ System.arraycopy(clockSequence, 0, result, 8, clockSequence.length);
+ random.nextBytes(node);
+ node[0] |= 0x80; // multicast bit
+ System.arraycopy(node, 0, result, 10, node.length);
+
+ return result;
+ }
+
+
+
+ public static String
+ generateFormatted()
+ {
+ return format(generate());
+ }
+
+
+
+ public static boolean
+ isUUID(String s)
+ {
+ if (s.length() != 36)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < 36; ++i)
+ {
+ if (i == 8 || i == 13 || i == 18 || i == 23)
+ {
+ if (s.charAt(i) != '-')
+ {
+ return false;
+ }
+ }
+ else
+ {
+ char c = s.charAt(i);
+
+ if
+ (
+ !(
+ (c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F')
+ )
+ )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static void
+ main(String[] args)
+ {
+ System.out.println(generateFormatted());
+ }
+
+
+
+ private static void
+ setTimestamp(byte[] result)
+ {
+ long time =
+ (System.currentTimeMillis() + startGregorianSinceEpoch) * 10000;
+
+ if (time != lastTimestamp)
+ {
+ lastTimestamp = time;
+ timestampCounter = 0;
+ }
+
+ time += timestampCounter++;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream(8);
+
+ try
+ {
+ new DataOutputStream(out).writeLong(time);
+ }
+
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ byte[] bytes = out.toByteArray();
+
+ System.arraycopy(bytes, 4, result, 0, 4); // time_low
+ System.arraycopy(bytes, 2, result, 4, 2); // time_mid
+ System.arraycopy(bytes, 0, result, 6, 2); // time_hi
+ }
+
+
+
+ private static char
+ toHex(int i)
+ {
+ return (char) (i + (i < 10 ? 48 : 87));
+ }
+
+} // UUID
diff --git a/src/be/re/util/Util.java b/src/be/re/util/Util.java
new file mode 100644
index 0000000..e6e844b
--- /dev/null
+++ b/src/be/re/util/Util.java
@@ -0,0 +1,1088 @@
+package be.re.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+
+
+/**
+ * @author Werner Donn\u00e9
+ */
+
+public class Util
+
+{
+
+ private static final String[][] DATE_FORMATS =
+ {
+ {"yyyy.MM.dd h:mm:ss a", "0"},
+ {"yyyy.MM.dd h:mm a", "0"},
+ {"yyyy.MM.dd HH:mm", "0"},
+ {"yyyy.MM.dd HH:mm:ss", "0"},
+ {"dd.MM.yyyy h:mm a", "0"},
+ {"dd.MM.yyyy h:mm:ss a", "0"},
+ {"dd.MM.yyyy HH:mm", "0"},
+ {"dd.MM.yyyy HH:mm:ss", "0"},
+ {"yyyy/MM/dd h:mm a", "0"},
+ {"yyyy/MM/dd h:mm:ss a", "0"},
+ {"yyyy/MM/dd HH:mm", "0"},
+ {"yyyy/MM/dd HH:mm:ss", "0"},
+ {"dd/MM/yyyy h:mm a", "0"},
+ {"dd/MM/yyyy h:mm:ss a", "0"},
+ {"dd/MM/yyyy HH:mm", "0"},
+ {"dd/MM/yyyy HH:mm:ss", "0"},
+ {"yyyy-MM-dd h:mm a", "0"},
+ {"yyyy-MM-dd h:mm:ss a", "0"},
+ {"yyyy-MM-dd HH:mm", "0"},
+ {"yyyy-MM-dd HH:mm:ss", "0"},
+ {"yyyy-MM-dd'T'h:mm a", "0"},
+ {"yyyy-MM-dd'T'h:mm:ss a", "0"},
+ {"yyyy-MM-dd'T'HH:mm", "0"},
+ {"yyyy-MM-dd'T'HH:mm:ss", "0"},
+ {"yyyy-MM-dd'T'HH:mm:ss.S", "0"},
+ {"yyyy-MM-dd'T'HH:mm:ssZ", "0"},
+ {"yyyy-MM-dd'T'HH:mm:ss.SZ", "0"},
+ {"dd-MM-yyyy h:mm a", "0"},
+ {"dd-MM-yyyy h:mm:ss a", "0"},
+ {"dd-MM-yyyy HH:mm", "0"},
+ {"dd-MM-yyyy HH:mm:ss", "0"},
+ {"h:mm a", "1"},
+ {"h:mm:ss a", "1"},
+ {"HH:mm", "1"},
+ {"HH:mm:ss", "1"},
+ {"yyyy.MM.dd", "2"},
+ {"dd.MM.yyyy", "2"},
+ {"yyyy/MM/dd", "2"},
+ {"dd/MM/yyyy", "2"},
+ {"yyyy-MM-dd", "2"},
+ {"dd-MM-yyyy", "2"}
+ };
+
+ private static Map bundles = new HashMap();
+ private static Set countries =
+ new HashSet(Arrays.asList(Locale.getISOCountries()));
+ private static Set languages =
+ new HashSet(Arrays.asList(Locale.getISOLanguages()));
+ private static Pattern timeZonePattern =
+ Pattern.compile("\\p{Digit}{2}:?\\p{Digit}{2}");
+ private static Pattern timestampPattern =
+ Pattern.compile
+ (
+ "\\p{Digit}{4}(-\\p{Digit}{2}(-\\p{Digit}{2}" +
+ "(T\\p{Digit}{2}:\\p{Digit}{2}(:\\p{Digit}{2}(.(\\p{Digit})+)?)?" +
+ "(Z|((\\+|-)\\p{Digit}{2}:?\\p{Digit}{2}))?)?)?)?"
+ );
+
+
+
+ public static void
+ attemptExitVM(int returnCode)
+ {
+ if
+ (
+ Util.class.getClassLoader() == null ||
+ Util.class.getClassLoader().
+ equals(ClassLoader.getSystemClassLoader())
+ )
+ {
+ try
+ {
+ System.exit(returnCode);
+ }
+
+ catch (Throwable e)
+ {
+ // Only attempt.
+ }
+ }
+ }
+
+
+
+ public static String
+ createDate(long time)
+ {
+ return new SimpleDateFormat("yyyy-MM-dd").format(new Date(time));
+ }
+
+
+
+ public static Object[]
+ createSet(Object[] collection)
+ {
+ Set set = new HashSet();
+
+ for (int i = 0; i < collection.length; ++i)
+ {
+ set.add(collection[i]);
+ }
+
+ return
+ set.toArray
+ (
+ (Object[])
+ Array.newInstance
+ (
+ collection.getClass().getComponentType(),
+ set.size()
+ )
+ );
+ }
+
+
+
+ public static String
+ createTimestamp(long time)
+ {
+ return
+ new SimpleDateFormat
+ (
+ time % 1000 > 0 ?
+ "yyyy-MM-dd'T'HH:mm:ss.SSSZ" : "yyyy-MM-dd'T'HH:mm:ssZ"
+ ).format(new Date(time));
+ }
+
+
+
+ public static String
+ createTimestampUTC(long time)
+ {
+ SimpleDateFormat format =
+ new SimpleDateFormat
+ (
+ time % 1000 > 0 ?
+ "yyyy-MM-dd'T'HH:mm:ss.SSS" : "yyyy-MM-dd'T'HH:mm:ss"
+ );
+
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+ return format.format(new Date(time)) + "Z";
+ }
+
+
+
+ public static String
+ enumerate(String[] s, String separator)
+ {
+ StringBuffer result = new StringBuffer(128);
+
+ for (int i = 0; i < s.length; ++i)
+ {
+ if (i > 0)
+ {
+ result.append(separator);
+ }
+
+ result.append(s[i]);
+ }
+
+ return result.toString();
+ }
+
+
+
+ private static String[]
+ findFormat(String value)
+ {
+ for (int i = 0; i < DATE_FORMATS.length; ++i)
+ {
+ SimpleDateFormat format = new SimpleDateFormat(DATE_FORMATS[i][0]);
+
+ format.setLenient(false);
+
+ try
+ {
+ if (format.parse(value) != null)
+ {
+ return DATE_FORMATS[i];
+ }
+ }
+
+ catch (ParseException e)
+ {
+ }
+ }
+
+ return null;
+ }
+
+
+
+ public static Throwable
+ getCause(Throwable t, Class theClass)
+ {
+ return
+ t == null ?
+ null :
+ (
+ theClass.isAssignableFrom(t.getClass()) ?
+ t : getCause(t.getCause(), theClass)
+ );
+ }
+
+
+
+ public static String
+ getCommonPrefix(String s1, String s2)
+ {
+ for (int i = 0; i < s1.length() && i < s2.length(); ++i)
+ {
+ if (s1.charAt(i) != s2.charAt(i))
+ {
+ return s1.substring(0, i);
+ }
+ }
+
+ return s1.length() <= s2.length() ? s1 : s2;
+ }
+
+
+
+ public static String
+ getCommonPrefix(String[] s)
+ {
+ if (s.length == 0)
+ {
+ return "";
+ }
+
+ String result = s[0];
+
+ for (int i = 1; i < s.length; ++i)
+ {
+ result = getCommonPrefix(result, s[i]);
+ }
+
+ return result;
+ }
+
+
+
+ public static String
+ getLanguageTag(Locale locale)
+ {
+ return
+ locale.getLanguage().toLowerCase() +
+ (
+ "".equals(locale.getCountry()) ?
+ "" : ("-" + locale.getCountry().toUpperCase())
+ );
+ }
+
+
+
+ public static Locale
+ getLocale(String languageTag)
+ {
+ return
+ languageTag.indexOf('-') != -1 ?
+ new Locale
+ (
+ languageTag.substring(0, languageTag.indexOf('-')).toLowerCase(),
+ languageTag.substring(languageTag.indexOf('-') + 1).toUpperCase()
+ ) : new Locale(languageTag.toLowerCase());
+ }
+
+
+
+ public static File
+ getPackageStorage(Class c)
+ {
+ File current = new File(System.getProperty("user.home"));
+ boolean first = true;
+ StringTokenizer tokenizer = new StringTokenizer(c.getName(), ".");
+ int count = tokenizer.countTokens();
+
+ for (int i = 0; i < count - 1; ++i)
+ {
+ current = new File(current, (first ? "." : "") + tokenizer.nextToken());
+ first = false;
+ }
+
+ if (!current.exists())
+ {
+ current.mkdirs();
+ }
+
+ return current;
+ }
+
+
+
+ public static String[]
+ getPathSegments(String path)
+ {
+ return split(path, "/");
+ }
+
+
+
+ public static String
+ getReSystemProperty(String name) throws IOException
+ {
+ InputStream in =
+ Util.class.getClassLoader().
+ getResourceAsStream("META-INF/services/be/re/" + name);
+
+ if (in != null)
+ {
+ try
+ {
+ String[] resource = readLineConfig(in);
+
+ if (resource.length == 1)
+ {
+ return resource[0];
+ }
+ }
+
+ finally
+ {
+ in.close();
+ }
+ }
+
+ return null;
+ }
+
+
+
+ public static String
+ getResource(String name)
+ {
+ return getResource(name, Locale.getDefault());
+ }
+
+
+
+ public static String
+ getResource(String name, Locale locale)
+ {
+ return getResourceBundle(locale).getString(name);
+ }
+
+
+
+ public static ResourceBundle
+ getResourceBundle()
+ {
+ return getResourceBundle(Locale.getDefault());
+ }
+
+
+
+ public static ResourceBundle
+ getResourceBundle(Locale locale)
+ {
+ ResourceBundle bundle = (ResourceBundle) bundles.get(locale);
+
+ if (bundle == null)
+ {
+ bundle = ResourceBundle.getBundle("be.re.util.res.Res", locale);
+ bundles.put(locale, bundle);
+ }
+
+ return bundle;
+ }
+
+
+
+ public static String
+ getStackTrace()
+ {
+ try
+ {
+ throw new Exception("");
+ }
+
+ catch (Exception e)
+ {
+ StringWriter writer = new StringWriter();
+
+ e.printStackTrace(new PrintWriter(writer));
+
+ return writer.toString();
+ }
+ }
+
+
+
+ public static String
+ getSystemProperty(String propertyName) throws IOException
+ {
+ String result = System.getProperty(propertyName);
+
+ if (result == null)
+ {
+ InputStream in =
+ Util.class.getClassLoader().
+ getResourceAsStream("META-INF/services/" + propertyName);
+
+ if (in != null)
+ {
+ String[] resource = readLineConfig(in);
+
+ if (resource.length == 1)
+ {
+ result = resource[0];
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public static TimeZone
+ getTimeZone(String s)
+ {
+ return getTimeZone(s, getTimeZoneOffset(s));
+ }
+
+
+
+ private static TimeZone
+ getTimeZone(String s, int offset)
+ {
+ if (offset == -1)
+ {
+ return TimeZone.getDefault();
+ }
+
+ s = s.substring(offset);
+
+ return
+ TimeZone.getTimeZone
+ (
+ "Z".equals(s) ? "GMT" : (s.startsWith("GMT") ? s : ("GMT" + s))
+ );
+ }
+
+
+
+ private static int
+ getTimeZoneOffset(String s)
+ {
+ int index;
+
+ return
+ s.charAt(s.length() - 1) == 'Z' ?
+ (s.length() - 1) :
+ (
+ (index = s.lastIndexOf("GMT")) != -1 ?
+ index :
+ (
+ (
+ (index = s.lastIndexOf('+')) != -1 ||
+ (index = s.lastIndexOf('-')) != -1
+ ) && timeZonePattern.matcher(s.substring(index + 1)).matches() ?
+ index : -1
+ )
+ );
+ }
+
+
+
+ public static boolean
+ isDateSet(String dateTime)
+ {
+ return isDateSet(findFormat(dateTime));
+ }
+
+
+
+ private static boolean
+ isDateSet(String[] format)
+ {
+ return format != null && !format[1].equals("1");
+ }
+
+
+
+ public static boolean
+ isDateTime(String s)
+ {
+ return findFormat(s) != null;
+ }
+
+
+
+ public static boolean
+ isDouble(String s)
+ {
+ try
+ {
+ Double.parseDouble(s);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isFloat(String s)
+ {
+ try
+ {
+ Float.parseFloat(s);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isInteger(String s)
+ {
+ try
+ {
+ Integer.parseInt(s);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isInteger(String s, int radix)
+ {
+ try
+ {
+ Integer.parseInt(s, radix);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isJCEInstalled()
+ {
+ try
+ {
+ Class.forName("javax.crypto.Cipher").
+ getMethod("getInstance", new Class[] {String.class}).
+ invoke(null, new Object[] {"PBEWithMD5AndDES/CBC/PKCS5Padding"});
+ return true;
+ }
+
+ catch (Throwable e)
+ {
+ return false;
+ }
+ }
+
+
+
+ /**
+ * Supports only the primary and country subtags. (See also RFC 4646.)
+ */
+
+ public static boolean
+ isLanguageTag(String s)
+ {
+ Locale locale = getLocale(s);
+
+ return
+ languages.contains(locale.getLanguage().toLowerCase()) &&
+ (
+ "".equals(locale.getCountry()) ||
+ countries.contains(locale.getCountry().toUpperCase())
+ );
+ }
+
+
+
+ public static boolean
+ isLong(String s)
+ {
+ try
+ {
+ Long.parseLong(s);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isLong(String s, int radix)
+ {
+ try
+ {
+ Long.parseLong(s, radix);
+
+ return true;
+ }
+
+ catch (NumberFormatException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isMac()
+ {
+ return System.getProperty("mrj.version") != null;
+ }
+
+
+
+ public static boolean
+ isTimeSet(long time)
+ {
+ Calendar calendar = Calendar.getInstance();
+
+ calendar.setTimeInMillis(time);
+
+ return
+ calendar.get(Calendar.HOUR_OF_DAY) > 0 ||
+ calendar.get(Calendar.MINUTE) > 0 ||
+ calendar.get(Calendar.SECOND) > 0 ||
+ calendar.get(Calendar.MILLISECOND) > 0;
+ }
+
+
+
+ public static boolean
+ isTimeSet(String dateTime)
+ {
+ return isTimeSet(findFormat(dateTime));
+ }
+
+
+
+ private static boolean
+ isTimeSet(String[] format)
+ {
+ return format != null && !format[1].equals("2");
+ }
+
+
+
+ public static boolean
+ isTimestamp(String s)
+ {
+ try
+ {
+ return parseTimestamp(s) != -1;
+ }
+
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+
+
+ public static boolean
+ isWindows()
+ {
+ return System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
+ }
+
+
+
+ public static String
+ list(String[] list, String separator)
+ {
+ StringBuilder builder = new StringBuilder(256);
+
+ for (int i = 0; i < list.length; ++i)
+ {
+ if (i > 0)
+ {
+ builder.append(separator);
+ }
+
+ builder.append(list[i]);
+ }
+
+ return builder.toString();
+ }
+
+
+
+ /**
+ * If the date is not set the current date will be used. If the time is not
+ * set midnight will be used.
+ */
+
+ public static long
+ parseDateTime(String s)
+ {
+ String[] format = findFormat(s);
+
+ if (format == null)
+ {
+ return -1;
+ }
+
+ long time = parseTimestamp(s, new String[]{format[0]});
+
+ if (isDateSet(format) && isTimeSet(format))
+ {
+ return time;
+ }
+
+ if (!isDateSet(format))
+ {
+ Calendar calendar = Calendar.getInstance();
+ Calendar now = Calendar.getInstance();
+
+ calendar.setTimeInMillis(time);
+ now.setTimeInMillis(System.currentTimeMillis());
+ calendar.set(Calendar.YEAR, now.get(Calendar.YEAR));
+ calendar.set(Calendar.MONTH, now.get(Calendar.MONTH));
+ calendar.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH));
+ time = calendar.getTime().getTime();
+ }
+
+ if (!isTimeSet(format))
+ {
+ time = setTime(time, 0, 0, 0);
+ }
+
+ return time;
+ }
+
+
+
+ /**
+ * Parse an ISO-8601 date. Returns <code>-1</code> if the date couldn't be
+ * parsed.
+ */
+
+ public static long
+ parseTimestamp(String s)
+ {
+ if (!timestampPattern.matcher(s).matches())
+ {
+ return -1;
+ }
+
+ int timeZoneOffset = getTimeZoneOffset(s);
+ Calendar calendar = Calendar.getInstance(getTimeZone(s, timeZoneOffset));
+
+ calendar.setLenient(false);
+ calendar.setTimeInMillis(0);
+ calendar.set(Calendar.YEAR, Integer.parseInt(s.substring(0, 4)));
+
+ if (s.length() > 4)
+ {
+ calendar.set(Calendar.MONTH, Integer.parseInt(s.substring(5, 7)) - 1);
+
+ if (s.length() > 7)
+ {
+ calendar.
+ set(Calendar.DAY_OF_MONTH, Integer.parseInt(s.substring(8, 10)));
+
+ if (s.length() > 10)
+ {
+ calendar.
+ set(Calendar.HOUR_OF_DAY, Integer.parseInt(s.substring(11, 13)));
+ calendar.set(Calendar.MINUTE, Integer.parseInt(s.substring(14, 16)));
+
+ if (s.length() > 16 && s.charAt(16) == ':')
+ {
+ calendar.
+ set(Calendar.SECOND, Integer.parseInt(s.substring(17, 19)));
+
+ if (s.length() > 19 && s.charAt(19) == '.')
+ {
+ calendar.set
+ (
+ Calendar.MILLISECOND,
+ Math.round
+ (
+ Float.parseFloat
+ (
+ "0." +
+ s.substring
+ (
+ 20,
+ timeZoneOffset != -1 ? timeZoneOffset : s.length()
+ )
+ ) * 1000
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+
+ return calendar.getTimeInMillis();
+ }
+
+
+
+ public static long
+ parseTimestamp(String s, String[] patterns)
+ {
+ Date result = null;
+
+ for (int i = 0; i < patterns.length && result == null; ++i)
+ {
+ try
+ {
+ SimpleDateFormat format = new SimpleDateFormat(patterns[i]);
+
+ format.setLenient(false);
+ result = format.parse(s);
+ }
+
+ catch (ParseException e)
+ {
+ }
+ }
+
+ return result == null ? -1 : result.getTime();
+ }
+
+
+
+ public static String
+ patternToRegexp(String pattern)
+ {
+ return
+ pattern.replaceAll("\\+", "\\+").replaceAll("\\.", "\\.").
+ replaceAll("\\$", "\\$").replaceAll("\\*", ".*");
+ }
+
+
+
+ public static void
+ printStackTrace(Throwable e)
+ {
+ if (System.getProperty("be.re.stack") != null)
+ {
+ e.printStackTrace();
+ }
+ }
+
+
+
+ public static String[]
+ readLineConfig(InputStream in) throws IOException
+ {
+ return readLineConfig(new BufferedReader(new InputStreamReader(in)));
+ }
+
+
+
+ public static String[]
+ readLineConfig(BufferedReader in) throws IOException
+ {
+ String line = "";
+ List result = new ArrayList();
+ String s;
+
+ while ((s = in.readLine()) != null)
+ {
+ if (s.indexOf('#') != -1)
+ {
+ s = s.substring(0, s.indexOf('#'));
+ }
+
+ s = s.trim();
+
+ if (s.length() > 0)
+ {
+ if (s.charAt(s.length() - 1) == '\\')
+ {
+ line += s.substring(0, s.length() - 1);
+ }
+ else
+ {
+ line += s;
+ result.add(line);
+ line = "";
+ }
+ }
+ }
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+
+
+ public static long
+ setTime(long date, int hourOfDay, int minute, int second)
+ {
+ Calendar calendar = Calendar.getInstance();
+
+ calendar.setTimeInMillis(date);
+ calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ calendar.set(Calendar.MINUTE, minute);
+ calendar.set(Calendar.SECOND, second);
+ calendar.set(Calendar.MILLISECOND, 0);
+
+ return calendar.getTime().getTime();
+ }
+
+
+
+ public static String[]
+ split(String s, String separator)
+ {
+ List result = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(s, separator);
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+
+ if (!"".equals(token))
+ {
+ result.add(token);
+ }
+ }
+
+ return (String[]) result.toArray(new String[0]);
+ }
+
+
+
+ public static String
+ toRoman(int v)
+ {
+ return
+ v < 1 ?
+ "" :
+ (
+ v < 4 ?
+ ("I" + toRoman(v - 1)) :
+ (
+ v < 5 ?
+ "IV" :
+ (
+ v < 9 ?
+ ("V" + toRoman(v - 5)) :
+ (
+ v < 10 ?
+ "IX" :
+ (
+ v < 40 ?
+ ("X" + toRoman(v - 10)) :
+ (
+ v < 50 ?
+ ("XL" + toRoman(v - 40)) :
+ (
+ v < 90 ?
+ ("L" + toRoman(v - 50)) :
+ (
+ v < 100 ?
+ ("XC" + toRoman(v - 90)) :
+ (
+ v < 400 ?
+ ("C" + toRoman(v - 100)) :
+ (
+ v < 500 ?
+ ("CD" + toRoman(v - 400)) :
+ (
+ v < 900 ?
+ (
+ "D" +
+ toRoman(v - 500)
+ ) :
+ (
+ v < 1000 ?
+ (
+ "CM" +
+ toRoman(v - 900)
+ ) :
+ (
+ "M" +
+ toRoman(v - 1000)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ );
+ }
+
+
+
+ public static int
+ waitFor(Process process)
+ {
+ while (true)
+ {
+ try
+ {
+ return process.waitFor();
+ }
+
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+
+} // Util
diff --git a/src/be/re/xml/Accumulator.java b/src/be/re/xml/Accumulator.java
new file mode 100644
index 0000000..09e6dae
--- /dev/null
+++ b/src/be/re/xml/Accumulator.java
@@ -0,0 +1,404 @@
+package be.re.xml;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * With this class a SAX stream can be accumulated in a DOM document. An
+ * instance of it can be reused. The client must call
+ * <code>startDocument</code> and <code>endDocument</code>.
+ * @author Werner Donn\u00e9
+ */
+
+public class Accumulator extends XMLFilterImpl
+
+{
+
+ private static final DocumentBuilder documentBuilder =
+ createDocumentBuilder();
+
+ private Node currentNode = null;
+ private Document document = null;
+ private Stack prefixMappings = new Stack();
+ private Result result = null;
+
+
+
+ public
+ Accumulator()
+ {
+ this((Document) null, (Result) null);
+ }
+
+
+
+ public
+ Accumulator(XMLReader parent)
+ {
+ this((Document) null, (Result) null, parent);
+ }
+
+
+
+ public
+ Accumulator(Result result)
+ {
+ this((Document) null, result);
+ }
+
+
+
+ public
+ Accumulator(Result result, XMLReader parent)
+ {
+ this((Document) null, result, parent);
+ }
+
+
+
+ public
+ Accumulator(Document document)
+ {
+ this.document = document;
+ }
+
+
+
+ public
+ Accumulator(Document document, XMLReader parent)
+ {
+ super(parent);
+ this.document = document;
+ }
+
+
+
+ public
+ Accumulator(Document document, Result result)
+ {
+ this.document = document;
+ this.result = result;
+ }
+
+
+
+ public
+ Accumulator(Document document, Result result, XMLReader parent)
+ {
+ super(parent);
+ this.document = document;
+ this.result = result;
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ currentNode.
+ appendChild(document.createTextNode(new String(ch, start, length)));
+ }
+
+
+
+ private static DocumentBuilder
+ createDocumentBuilder()
+ {
+ try
+ {
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ }
+
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ prefixMappings.pop();
+ currentNode = null;
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ prefixMappings.pop();
+ currentNode = currentNode.getParentNode();
+
+ if (result != null && currentNode == document)
+ {
+ result.report(this);
+ }
+ }
+
+
+
+ public void
+ endPrefixMapping(String prefix) throws SAXException
+ {
+ ((Map) prefixMappings.peek()).remove(prefix);
+ }
+
+
+
+ public Document
+ getDocument()
+ {
+ return document;
+ }
+
+
+
+ public void
+ ignorableWhitespace(char[] ch, int start, int length) throws SAXException
+ {
+ }
+
+
+
+ /**
+ * This installs an accumulator after <code>filter</code>. You would call it
+ * in <code>startElement</code>, before you let the event go through.
+ */
+
+ public static void
+ postAccumulate(XMLFilter filter, ProcessElement process) throws SAXException
+ {
+ final XMLFilter f = filter;
+ final ContentHandler handler =
+ filter.getContentHandler();
+ final ProcessElement p = process;
+ Accumulator accumulator =
+ new Accumulator
+ (
+ documentBuilder.newDocument(),
+ new Result()
+ {
+ public void
+ report(Accumulator accumulator) throws SAXException
+ {
+ f.setContentHandler
+ (
+ handler != null ? handler : new XMLFilterImpl()
+ );
+
+ p.process(accumulator.getDocument().getDocumentElement(), f);
+ accumulator.endDocument();
+ }
+ }
+ );
+
+ filter.setContentHandler(accumulator);
+ accumulator.setParent(filter);
+ accumulator.startDocument();
+ }
+
+
+
+ /**
+ * This installs an accumulator before <code>filter</code>. You would call it
+ * in <code>startElement</code>, and initialize it with the incoming event,
+ * which you don't let go through.
+ */
+
+ public static void
+ preAccumulate
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts,
+ XMLFilter filter,
+ ProcessElement process
+ ) throws SAXException
+ {
+ if (filter.getParent() == null)
+ {
+ return;
+ }
+
+ final XMLFilter f = filter;
+ final ContentHandler handler =
+ filter.getParent().getContentHandler();
+ final ProcessElement p = process;
+ Accumulator accumulator =
+ new Accumulator
+ (
+ documentBuilder.newDocument(),
+ new Result()
+ {
+ public void
+ report(Accumulator accumulator) throws SAXException
+ {
+ p.process(accumulator.getDocument().getDocumentElement(), f);
+ accumulator.endDocument();
+ f.getParent().setContentHandler(handler);
+ }
+ }
+ );
+
+ filter.getParent().setContentHandler(accumulator);
+ accumulator.setParent(filter.getParent());
+ accumulator.startDocument();
+ accumulator.startElement(namespaceURI, localName, qName, atts);
+ }
+
+
+
+ public void
+ processingInstruction(String target, String data) throws SAXException
+ {
+ currentNode.appendChild(document.createProcessingInstruction(target, data));
+ }
+
+
+
+ public void
+ setDocumentLocator(Locator locator)
+ {
+ }
+
+
+
+ private static void
+ setAttributes(Element element, Attributes atts)
+ {
+ for (int i = 0; i < atts.getLength(); ++i)
+ {
+ if (atts.getURI(i) == null || "".equals(atts.getURI(i)))
+ {
+ element.setAttribute(atts.getQName(i), atts.getValue(i));
+ }
+ else
+ {
+ element.
+ setAttributeNS(atts.getURI(i), atts.getQName(i), atts.getValue(i));
+ }
+ }
+ }
+
+
+
+ private static void
+ setPrefixMappings(Element element, Map mappings)
+ {
+ for (Iterator i = mappings.keySet().iterator(); i.hasNext();)
+ {
+ String prefix = (String) i.next();
+
+ element.setAttribute
+ (
+ "".equals(prefix) ? "xmlns" : ("xmlns:" + prefix),
+ (String) mappings.get(prefix)
+ );
+ }
+ }
+
+
+
+ public void
+ skippedEntity(String name) throws SAXException
+ {
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ if (document == null)
+ {
+ try
+ {
+ document = documentBuilder.newDocument();
+ }
+
+ catch (Exception e)
+ {
+ throw new SAXException(e);
+ }
+ }
+ else
+ {
+ if (document.getDocumentElement() != null)
+ {
+ document.removeChild(document.getDocumentElement());
+ }
+ }
+
+ currentNode = document;
+ prefixMappings.push(new HashMap());
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Element element =
+ document.
+ createElementNS("".equals(namespaceURI) ? null : namespaceURI, qName);
+
+ setAttributes(element, atts);
+ setPrefixMappings(element, (Map) prefixMappings.peek());
+ prefixMappings.push(new HashMap());
+ currentNode.appendChild(element);
+ currentNode = element;
+ }
+
+
+
+ public void
+ startPrefixMapping(String prefix, String uri) throws SAXException
+ {
+ ((Map) prefixMappings.peek()).put(prefix, uri);
+ }
+
+
+
+ public interface ProcessElement
+
+ {
+
+ public void process (Element element, XMLFilter filter) throws SAXException;
+
+ } // ProcessElement
+
+
+
+ public interface Result
+ {
+ public void report (Accumulator accumulator) throws SAXException;
+ }
+
+} // Accumulator
diff --git a/src/be/re/xml/CatalogResolver.java b/src/be/re/xml/CatalogResolver.java
new file mode 100644
index 0000000..d3f63b4
--- /dev/null
+++ b/src/be/re/xml/CatalogResolver.java
@@ -0,0 +1,388 @@
+package be.re.xml;
+
+import be.re.io.StreamConnector;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+import javax.xml.stream.XMLResolver;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.stream.StreamSource;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * This entity resolver uses a catalog as defined by SGML Open Technical
+ * Resolution TR9401:1997. Only PUBLIC and SYSTEM statements are supported at
+ * this time. Relative URLs are resolved using the catalog URL as the base URL.
+ * @author Werner Donn\u00e9
+ */
+
+public class CatalogResolver implements EntityResolver, XMLResolver
+
+{
+
+ // Alphabet.
+
+ final static int SINGLE_QUOTE = 0;
+ final static int DOUBLE_QUOTE = 1;
+ final static int OTHER = 2;
+ final static int SPACE = 3;
+ final static int WHITE = 4;
+ final static int EOF = 5;
+
+ // States.
+
+ final static int TYP = 0;
+ final static int SQ1 = 1;
+ final static int DQ1 = 2;
+ final static int ID1 = 3;
+ final static int SQ2 = 4;
+ final static int DQ2 = 5;
+ final static int ERR = 6;
+
+ final static int[][][] FSM =
+ {
+ {{SQ1, 1}, {DQ1, 1}, {TYP, 0}, {TYP, 0}, {TYP, 0}, {TYP, 0}}, // TYP
+ {{ID1, 1}, {SQ1, 0}, {SQ1, 0}, {SQ1, 0}, {ERR, 0}, {ERR, 0}}, // SQ1
+ {{DQ1, 0}, {ID1, 1}, {DQ1, 0}, {DQ1, 0}, {ERR, 0}, {ERR, 0}}, // DQ1
+ {{SQ2, 1}, {DQ2, 1}, {ERR, 0}, {ID1, 0}, {ID1, 0}, {ERR, 0}}, // ID1
+ {{TYP, 1}, {SQ2, 0}, {SQ2, 0}, {SQ2, 0}, {ERR, 0}, {ERR, 0}}, // SQ2
+ {{DQ2, 0}, {TYP, 1}, {DQ2, 0}, {DQ2, 0}, {ERR, 0}, {ERR, 0}} // DQ2
+ };
+
+ private String catalogSystemId;
+ private Map publicIdentifiers = new HashMap();
+ private Map systemIdentifiers = new HashMap();
+
+
+
+ public
+ CatalogResolver(URL catalogUrl) throws IOException
+ {
+ this(catalogUrl.toString(), null);
+ }
+
+
+
+ public
+ CatalogResolver(String catalogSystemId) throws IOException
+ {
+ this(catalogSystemId, null);
+ }
+
+
+
+ public
+ CatalogResolver(URL catalogUrl, InputStream in) throws IOException
+ {
+ this(catalogUrl.toString(), in);
+ }
+
+
+
+ public
+ CatalogResolver(String catalogSystemId, InputStream in) throws IOException
+ {
+ this.catalogSystemId = catalogSystemId;
+
+ load
+ (
+ in != null ?
+ in :
+ (
+ isUrl(catalogSystemId) ?
+ new URL(catalogSystemId).openStream() :
+ new FileInputStream(catalogSystemId)
+ )
+ );
+ }
+
+
+
+ private static int
+ category(int c)
+ {
+ return
+ c == '\'' ?
+ SINGLE_QUOTE :
+ (
+ c == '\"' ?
+ DOUBLE_QUOTE :
+ (
+ c == ' ' ?
+ SPACE :
+ (
+ c == '\t' || c == '\n' || c == '\r' ?
+ WHITE : OTHER
+ )
+ )
+ );
+ }
+
+
+
+ private static void
+ error(int in, int line) throws IOException
+ {
+ if (in == EOF)
+ {
+ throw
+ new IOException
+ (
+ "Line " + String.valueOf(line) + ": premature end of file"
+ );
+ }
+
+ if (in == WHITE)
+ {
+ throw
+ new IOException
+ (
+ "Line " + String.valueOf(line) +
+ ": \\t, \\n and \\r are not allowed in an identifier"
+ );
+ }
+
+ if (in == OTHER)
+ {
+ throw
+ new IOException
+ (
+ "Line " + String.valueOf(line) + ": white space expected"
+ );
+ }
+ }
+
+
+
+ /**
+ * Returns a map from the public identifiers to the resolved URLs.
+ */
+
+ public Map
+ getPublicIdentifierMappings()
+ {
+ return publicIdentifiers;
+ }
+
+
+
+ /**
+ * Returns a map from the public identifiers to the resolved URLs.
+ */
+
+ public Map
+ getSystemIdentifierMappings()
+ {
+ return systemIdentifiers;
+ }
+
+
+
+ private static String
+ getTypeToken(char[] c, int off, int len, int line) throws IOException
+ {
+ StringTokenizer tokenizer =
+ new StringTokenizer(new String(c, off, len), " \t\n\r");
+
+ if (!tokenizer.hasMoreTokens())
+ {
+ throw
+ new IOException
+ (
+ "Line " + String.valueOf(line) + ": PUBLIC or SYSTEM expected"
+ );
+ }
+
+ String token = tokenizer.nextToken();
+
+ if (!token.equals("PUBLIC") && !token.equals("SYSTEM"))
+ {
+ throw
+ new IOException
+ (
+ "Line " + String.valueOf(line) + ": PUBLIC or SYSTEM expected"
+ );
+ }
+
+ return token;
+ }
+
+
+
+ private static boolean
+ isUrl(String s)
+ {
+ try
+ {
+ return s != null && new URL(s) != null;
+ }
+
+ catch (MalformedURLException e)
+ {
+ return false;
+ }
+ }
+
+
+
+ private void
+ load(InputStream in) throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ StreamConnector.copy(in, out);
+
+ char[] c = new String(out.toByteArray(), "ASCII").toCharArray();
+ String from = null;
+ int line = 1;
+ int position = 0;
+ int state = TYP;
+ String type = null;
+
+ for (int i = 0; i < c.length; ++i)
+ {
+ int[] next = FSM[state][category(c[i])];
+
+ if (next[0] == ERR)
+ {
+ error(category(c[i]), line);
+ }
+
+ if (next[1] == 1)
+ {
+ Map map;
+
+ switch (state)
+ {
+ case TYP:
+ type = getTypeToken(c, position, i - position, line);
+ break;
+
+ case SQ1: case DQ1:
+ from = new String(c, position, i - position);
+ break;
+
+ case SQ2: case DQ2:
+ map = type.equals("PUBLIC") ? publicIdentifiers : systemIdentifiers;
+
+ map.put
+ (
+ from,
+ resolveSystemId
+ (
+ catalogSystemId,
+ new String(c, position, i - position)
+ )
+ );
+
+ break;
+ }
+
+ position = i + 1;
+ }
+
+ state = next[0];
+
+ if (c[i] == '\n')
+ {
+ ++line;
+ }
+ }
+
+ if (FSM[state][EOF][0] == ERR)
+ {
+ error(EOF, line);
+ }
+ }
+
+
+
+ public InputSource
+ resolveEntity(String publicId, String systemId)
+ throws IOException, SAXException
+ {
+ InputSource result =
+ publicId != null && publicIdentifiers.get(publicId) != null ?
+ new InputSource(publicIdentifiers.get(publicId).toString()) :
+ (
+ systemId != null && systemIdentifiers.get(systemId) != null ?
+ new InputSource(systemIdentifiers.get(systemId).toString()) : null
+ );
+
+ if (result != null)
+ {
+ result.setPublicId(publicId);
+ }
+
+ return result;
+ }
+
+
+
+ public Object
+ resolveEntity
+ (
+ String publicId,
+ String systemId,
+ String baseURI,
+ String namespace
+ ) throws XMLStreamException
+ {
+ try
+ {
+ StreamSource result =
+ new StreamSource
+ (
+ publicId != null && publicIdentifiers.get(publicId) != null ?
+ publicIdentifiers.get(publicId).toString() :
+ (
+ systemId != null && systemIdentifiers.get(systemId) != null ?
+ systemIdentifiers.get(systemId).toString() :
+ (
+ baseURI != null && systemId != null ?
+ resolveSystemId(baseURI, systemId) : null
+ )
+ )
+ );
+
+ result.setPublicId(publicId);
+
+ return result;
+ }
+
+ catch (IOException e)
+ {
+ throw new XMLStreamException(e);
+ }
+ }
+
+
+
+ private static String
+ resolveSystemId(String baseURI, String systemId) throws IOException
+ {
+ return
+ isUrl(baseURI) ?
+ new URL(new URL(baseURI), systemId).toString() :
+ (
+ systemId.charAt(0) == '/' ?
+ systemId :
+ (
+ baseURI.charAt(baseURI.length() - 1) == '/' ?
+ (baseURI + systemId) :
+ (baseURI.substring(0, baseURI.lastIndexOf('/') + 1) + systemId)
+ )
+ );
+ }
+
+} // CatalogResolver
diff --git a/src/be/re/xml/DOMToContentHandler.java b/src/be/re/xml/DOMToContentHandler.java
new file mode 100644
index 0000000..fc97c80
--- /dev/null
+++ b/src/be/re/xml/DOMToContentHandler.java
@@ -0,0 +1,284 @@
+package be.re.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * With this class a DOM document can be fed into a SAX chain.
+ * @author Werner Donn\u00e9
+ */
+
+public class DOMToContentHandler
+
+{
+
+ /**
+ * Extracts the attributes of an element for use in a SAX-environment. It
+ * leaves out namespace declarations, which are emitted as events.
+ */
+
+ public static Attributes
+ createAttributes(Element element)
+ {
+ AttributesImpl attributes = new AttributesImpl();
+ NamedNodeMap map = element.getAttributes();
+
+ for (int i = 0; i < map.getLength(); ++i)
+ {
+ Attr attribute = (Attr) map.item(i);
+
+ if (attribute.getNamespaceURI() != null)
+ {
+ attributes.addAttribute
+ (
+ attribute.getNamespaceURI(),
+ attribute.getLocalName(),
+ attribute.getPrefix() != null ?
+ (attribute.getPrefix() + ":" + attribute.getLocalName()) :
+ attribute.getLocalName(),
+ "CDATA",
+ attribute.getValue()
+ );
+ }
+ else
+ {
+ if
+ (
+ !"xmlns".equals(attribute.getName()) &&
+ !attribute.getName().startsWith("xmlns:")
+ )
+ {
+ attributes.addAttribute
+ (
+ "",
+ attribute.getName(),
+ attribute.getName(),
+ "CDATA",
+ attribute.getValue()
+ );
+ }
+ }
+ }
+
+ return attributes;
+ }
+
+
+
+ /**
+ * Runs a complete DOM-document through a <code>ContentHandler</code>.
+ */
+
+ public static void
+ documentToContentHandler(Document document, ContentHandler handler)
+ throws SAXException
+ {
+ XMLFilterImpl h = realHandler(handler);
+
+ h.startDocument();
+ elementToContentHandler(document.getDocumentElement(), h);
+ h.endDocument();
+ }
+
+
+
+ /**
+ * Runs a complete DOM-element through a <code>ContentHandler</code>.
+ */
+
+ public static void
+ elementToContentHandler(Element element, ContentHandler handler)
+ throws SAXException
+ {
+ elementToContentHandler(element, realHandler(handler));
+ }
+
+
+
+ private static void
+ elementToContentHandler(Element element, XMLFilterImpl handler)
+ throws SAXException
+ {
+ String[] prefixes = startPrefixMappings(element, handler);
+
+ startElement(element, handler);
+ siblingsToContentHandler(element.getFirstChild(), handler);
+ endElement(element, handler);
+
+ for (int i = prefixes.length - 1; i >= 0; --i)
+ {
+ handler.endPrefixMapping(prefixes[i]);
+ }
+ }
+
+
+
+ public static void
+ endElement(Element element, ContentHandler handler) throws SAXException
+ {
+ if (element.getNamespaceURI() != null)
+ {
+ handler.endElement
+ (
+ element.getNamespaceURI(),
+ element.getLocalName(),
+ element.getPrefix() != null ?
+ (element.getPrefix() + ":" + element.getLocalName()) :
+ element.getLocalName()
+ );
+ }
+ else
+ {
+ handler.endElement("", element.getTagName(), element.getTagName());
+ }
+ }
+
+
+
+ /**
+ * Puts an <code>XMLFilterImpl</code> in front of <code>handler</code> in
+ * order to provide the possiblity to the latter to insert an
+ * <code>Accumulator</code> dynamically.
+ */
+
+ private static XMLFilterImpl
+ realHandler(ContentHandler handler)
+ {
+ XMLFilterImpl result = new XMLFilterImpl();
+
+ result.setContentHandler(handler);
+
+ if (handler instanceof XMLFilter)
+ {
+ ((XMLFilter) handler).setParent(result);
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Runs a complete sibling list through a <code>ContentHandler</code>
+ * starting with <code>node</code>.
+ */
+
+ public static void
+ siblingsToContentHandler(Node node, ContentHandler handler)
+ throws SAXException
+ {
+ siblingsToContentHandler(node, realHandler(handler));
+ }
+
+
+
+ private static void
+ siblingsToContentHandler(Node node, XMLFilterImpl handler)
+ throws SAXException
+ {
+ if (node == null)
+ {
+ return;
+ }
+
+ switch (node.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ elementToContentHandler((Element) node, handler);
+ break;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ handler.processingInstruction
+ (
+ ((ProcessingInstruction) node).getTarget(),
+ ((ProcessingInstruction) node).getData()
+ );
+
+ break;
+
+ case Node.TEXT_NODE:
+ {
+ char[] chars = ((Text) node).getData().toCharArray();
+
+ handler.characters(chars, 0, chars.length);
+ }
+
+ break;
+ }
+
+ siblingsToContentHandler(node.getNextSibling(), handler);
+ }
+
+
+
+ public static void
+ startElement(Element element, ContentHandler handler) throws SAXException
+ {
+ if (element.getNamespaceURI() != null)
+ {
+ handler.startElement
+ (
+ element.getNamespaceURI(),
+ element.getLocalName(),
+ element.getPrefix() != null ?
+ (element.getPrefix() + ":" + element.getLocalName()) :
+ element.getLocalName(),
+ createAttributes(element)
+ );
+ }
+ else
+ {
+ handler.startElement
+ (
+ "",
+ element.getTagName(),
+ element.getTagName(),
+ createAttributes(element)
+ );
+ }
+ }
+
+
+
+ private static String[]
+ startPrefixMappings(Element element, ContentHandler handler)
+ throws SAXException
+ {
+ NamedNodeMap map = element.getAttributes();
+ List result = new ArrayList();
+
+ for (int i = 0; i < map.getLength(); ++i)
+ {
+ Attr attribute = (Attr) map.item(i);
+ String name = attribute.getName();
+
+ if (name.startsWith("xmlns"))
+ {
+ String prefix =
+ name.startsWith("xmlns:") ?
+ name.substring(name.indexOf(':') + 1) : "";
+
+ result.add(prefix);
+ handler.startPrefixMapping(prefix, attribute.getValue());
+ }
+ }
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+} // DOMToContentHandler
diff --git a/src/be/re/xml/ExpandedName.java b/src/be/re/xml/ExpandedName.java
new file mode 100644
index 0000000..15b2b67
--- /dev/null
+++ b/src/be/re/xml/ExpandedName.java
@@ -0,0 +1,85 @@
+package be.re.xml;
+
+import javax.xml.namespace.QName;
+import org.w3c.dom.Node;
+
+
+
+/**
+ * A struct to represent the expanded name of an element.
+ * @author Werner Donn\u00e9
+ */
+
+public class ExpandedName
+
+{
+
+ public String localName;
+
+ /**
+ * The empty string or <code>null</code> indicates absence of a namespace.
+ */
+
+ public String namespaceURI;
+
+
+
+ public
+ ExpandedName(String namespaceURI, String localName)
+ {
+ this.namespaceURI = namespaceURI == null ? "" : namespaceURI;
+ this.localName = localName;
+ }
+
+
+
+ public
+ ExpandedName(QName name)
+ {
+ this(name.getNamespaceURI(), name.getLocalPart());
+ }
+
+
+
+ public
+ ExpandedName(Node node)
+ {
+ this(node.getNamespaceURI(), node.getLocalName());
+ }
+
+
+
+ public boolean
+ equals(Object o)
+ {
+ return
+ o instanceof ExpandedName &&
+ namespaceURI.equals(((ExpandedName) o).namespaceURI) &&
+ localName.equals(((ExpandedName) o).localName);
+ }
+
+
+
+ public int
+ hashCode()
+ {
+ return toString().hashCode();
+ }
+
+
+
+ public boolean
+ noNamespace()
+ {
+ return namespaceURI == null || namespaceURI.equals("");
+ }
+
+
+
+ public String
+ toString()
+ {
+ return namespaceURI + "#" + localName;
+ }
+
+} // ExpandedName
diff --git a/src/be/re/xml/Util.java b/src/be/re/xml/Util.java
new file mode 100644
index 0000000..6fccf6e
--- /dev/null
+++ b/src/be/re/xml/Util.java
@@ -0,0 +1,1276 @@
+package be.re.xml;
+
+import be.re.xml.sax.ErrorHandler;
+import be.re.util.Equal;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.AttributeList;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributeListImpl;
+
+
+
+/**
+ * This class is a collection of untility functions.
+ * @author Werner Donn\u00e9
+ */
+
+public class Util
+
+{
+
+ private static DocumentBuilderFactory nonValidatingFactory;
+ private static final Equal elementSelector =
+ new Equal()
+ {
+ public boolean
+ equal(Object object, Object refData)
+ {
+ return object instanceof Element;
+ }
+ };
+ private static final Equal nameSelector =
+ new Equal()
+ {
+ public boolean
+ equal(Object object, Object refData)
+ {
+ return ((Node) object).getNodeName().equals((String) refData);
+ }
+ };
+ private static final Equal qnameSelector =
+ new Equal()
+ {
+ public boolean
+ equal(Object object, Object refData)
+ {
+ return
+ (
+ ((ExpandedName) refData).localName == null ||
+ ((ExpandedName) refData).localName.
+ equals(((Node) object).getLocalName())
+ ) &&
+ (
+ ((ExpandedName) refData).namespaceURI.
+ equals(((Node) object).getNamespaceURI()) ||
+ (
+ "".equals(((ExpandedName) refData).namespaceURI) &&
+ ((Node) object).getNamespaceURI() == null
+ )
+ );
+ }
+ };
+ private static TransformerFactory transformerFactory;
+ private static DocumentBuilderFactory validatingFactory;
+
+
+
+ public static Node
+ clearNode(Node node)
+ {
+ while (node.hasChildNodes())
+ {
+ node.removeChild(node.getFirstChild());
+ }
+
+ return node;
+ }
+
+
+
+ private static void
+ collectChild(Node node, List children)
+ {
+ if (node != null)
+ {
+ children.add(node);
+ collectChild(node.getNextSibling(), children);
+ }
+ }
+
+
+
+ /**
+ * Returns all child nodes of a node as an array.
+ */
+
+ public static Node[]
+ collectChildren(Node node)
+ {
+ List children = new ArrayList();
+
+ collectChild(node.getFirstChild(), children);
+
+ return (Node[]) children.toArray(new Node[children.size()]);
+ }
+
+
+
+ public static Node
+ copy(Node node, Document owner)
+ {
+ if (node == null)
+ {
+ return null;
+ }
+
+ Node result = null;
+
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ result = copyAttribute((Attr) node, owner);
+ break;
+
+ case Node.CDATA_SECTION_NODE:
+ result = owner.createCDATASection(((CDATASection) node).getData());
+ break;
+
+ case Node.COMMENT_NODE:
+ result = owner.createComment(((Comment) node).getData());
+ break;
+
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ result = owner.createDocumentFragment();
+ break;
+
+ case Node.ELEMENT_NODE:
+ result = copyElement((Element) node, owner);
+ break;
+
+ case Node.ENTITY_REFERENCE_NODE:
+ result = owner.createEntityReference(node.getNodeName());
+ break;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ result =
+ owner.createProcessingInstruction
+ (
+ ((ProcessingInstruction) node).getTarget(),
+ ((ProcessingInstruction) node).getData()
+ );
+ break;
+
+ case Node.TEXT_NODE:
+ result = owner.createTextNode(((Text) node).getData());
+ break;
+ }
+
+ if (result != null)
+ {
+ for (Node i = node.getFirstChild(); i != null; i = i.getNextSibling())
+ {
+ result.appendChild(copy(i, owner));
+ }
+ }
+
+ return result;
+ }
+
+
+
+ private static Node
+ copyAttribute(Attr attribute, Document owner)
+ {
+ Attr result =
+ attribute.getNamespaceURI() != null ?
+ owner.
+ createAttributeNS(attribute.getNamespaceURI(), attribute.getName()) :
+ owner.createAttribute(attribute.getName());
+
+ result.setValue(attribute.getValue());
+
+ return result;
+ }
+
+
+
+ private static Node
+ copyElement(Element element, Document owner)
+ {
+ NamedNodeMap attributes = element.getAttributes();
+ Element result =
+ element.getNamespaceURI() != null ?
+ owner.createElementNS(element.getNamespaceURI(), element.getTagName()) :
+ owner.createElement(element.getTagName());
+
+ for (int i = 0; i < attributes.getLength(); ++i)
+ {
+ Attr attribute = (Attr) copy(attributes.item(i), owner);
+
+ if (attribute.getNamespaceURI() != null)
+ {
+ result.setAttributeNodeNS(attribute); // Why does this method exist?
+ }
+ else
+ {
+ result.setAttributeNode(attribute);
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Extracts the attributes of an element for use in a SAX-environment.
+ */
+
+ public static AttributeList
+ createAttributeList(Element element)
+ {
+ AttributeListImpl attributes = new AttributeListImpl();
+ NamedNodeMap map = element.getAttributes();
+
+ for (int i = 0; i < map.getLength(); ++i)
+ {
+ Attr attribute = (Attr) map.item(i);
+
+ attributes.
+ addAttribute(attribute.getName(), "CDATA", attribute.getValue());
+ }
+
+ return attributes;
+ }
+
+
+
+ /**
+ * Runs a complete DOM-element through a <code>DocumentHandler</code>.
+ */
+
+ public static void
+ elementToDocumentHandler(Element element, DocumentHandler handler)
+ throws SAXException
+ {
+ handler.startElement(element.getTagName(), createAttributeList(element));
+ elementToDocumentHandlerChild(element.getFirstChild(), handler);
+ handler.endElement(element.getTagName());
+ }
+
+
+
+ private static void
+ elementToDocumentHandlerChild(Node node, DocumentHandler handler)
+ throws SAXException
+ {
+ if (node == null)
+ {
+ return;
+ }
+
+ switch (node.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ elementToDocumentHandler((Element) node, handler);
+ break;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ processingInstructionToDocumentHandler
+ (
+ (ProcessingInstruction) node,
+ handler
+ );
+ break;
+
+ case Node.TEXT_NODE:
+ textToDocumentHandler((Text) node, handler);
+ break;
+ }
+
+ elementToDocumentHandlerChild(node.getNextSibling(), handler);
+ }
+
+
+
+ public static String
+ escapeText(String value)
+ {
+ return escapeText(value, true);
+ }
+
+
+
+ public static String
+ escapeText(String value, boolean minimum)
+ {
+ StringBuffer buffer = new StringBuffer((int) (value.length() * 1.1));
+
+ for (int i = 0; i < value.length(); ++i)
+ {
+ if (value.charAt(i) == '&')
+ {
+ buffer.append("&");
+ }
+ else
+ {
+ if (value.charAt(i) == '\'' && !minimum)
+ {
+ buffer.append("'");
+ }
+ else
+ {
+ if (value.charAt(i) == '>' && !minimum)
+ {
+ buffer.append(">");
+ }
+ else
+ {
+ if (value.charAt(i) == '<')
+ {
+ buffer.append("<");
+ }
+ else
+ {
+ if (value.charAt(i) == '"' && !minimum)
+ {
+ buffer.append(""");
+ }
+ else
+ {
+ buffer.append(value.charAt(i));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return buffer.toString();
+ }
+
+
+
+ /**
+ * Searches the ancestor chain of a node for a node called
+ * <code>name</code>. If no such node is found <code>null</code> is returned.
+ */
+
+ public static Node
+ findAncestor(Node node, String name)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node.getNodeName().equalsIgnoreCase(name) ?
+ node : findAncestor(node.getParentNode(), name)
+ );
+ }
+
+
+
+ /**
+ * Does the same as <code>findAncestor</code> except that the search stops
+ * when a node called <code>limitingElement</code> is encountered.
+ * @see #findAncestor
+ */
+
+ public static Node
+ findAncestorNotBeyond(Node node, String name, String limitingElement)
+ {
+ return
+ node == null ?
+ null :
+ (
+ node.getNodeName().equalsIgnoreCase(name) ?
+ node :
+ (
+ node.getNodeName().equalsIgnoreCase(limitingElement) ?
+ null :
+ findAncestorNotBeyond
+ (
+ node.getParentNode(),
+ name,
+ limitingElement
+ )
+ )
+ );
+ }
+
+
+
+ public static DocumentBuilder
+ getDocumentBuilder(URL catalog, boolean validating)
+ throws IOException, ParserConfigurationException
+ {
+ return getDocumentBuilder(newDocumentBuilderFactory(validating), catalog);
+ }
+
+
+
+ public static DocumentBuilder
+ getDocumentBuilder(DocumentBuilderFactory factory, URL catalog)
+ throws IOException, ParserConfigurationException
+ {
+ DocumentBuilder result = factory.newDocumentBuilder();
+
+ result.setErrorHandler(new ErrorHandler(false));
+
+ if (catalog != null)
+ {
+ result.setEntityResolver(new CatalogResolver(catalog));
+ }
+
+ return result;
+ }
+
+
+
+ public static DocumentBuilderFactory
+ getDocumentBuilderFactory(boolean validating)
+ throws ParserConfigurationException
+ {
+ if (validating && validatingFactory != null)
+ {
+ return validatingFactory;
+ }
+
+ if (!validating && nonValidatingFactory != null)
+ {
+ return nonValidatingFactory;
+ }
+
+ DocumentBuilderFactory factory = newDocumentBuilderFactory(validating);
+
+ if (validating)
+ {
+ validatingFactory = factory;
+ }
+ else
+ {
+ nonValidatingFactory = factory;
+ }
+
+ return factory;
+ }
+
+
+
+ public static Element
+ getNextSiblingElement(Node node, String namespaceURI, String localName)
+ {
+ if (node == null)
+ {
+ return null;
+ }
+
+ return
+ node instanceof Element &&
+ new ExpandedName(node.getNamespaceURI(), node.getLocalName()).
+ equals(new ExpandedName(namespaceURI, localName)) ?
+ (Element) node :
+ getNextSiblingElement(node.getNextSibling(), namespaceURI, localName);
+ }
+
+
+
+ public static Element
+ getPreviousSiblingElement(Node node, String namespaceURI, String localName)
+ {
+ if (node == null)
+ {
+ return null;
+ }
+
+ return
+ node instanceof Element &&
+ new ExpandedName(node.getNamespaceURI(), node.getLocalName()).
+ equals(new ExpandedName(namespaceURI, localName)) ?
+ (Element) node :
+ getPreviousSiblingElement
+ (
+ node.getPreviousSibling(),
+ namespaceURI,
+ localName
+ );
+ }
+
+
+
+ public static QName
+ getQName(Node node)
+ {
+ return
+ node.getPrefix() != null ?
+ new QName
+ (
+ node.getNamespaceURI(),
+ node.getLocalName(),
+ node.getPrefix()
+ ) :
+ (
+ node.getNamespaceURI() != null ?
+ new QName(node.getNamespaceURI(), node.getLocalName()) :
+ new QName(node.getLocalName())
+ );
+ }
+
+
+
+ /**
+ * Assumes pure #PCDATA, no mixed content.
+ */
+
+ public static String
+ getText(Node node)
+ {
+ if (node == null)
+ {
+ return null;
+ }
+
+ NodeList list = node.getChildNodes();
+ int position = 0;
+ int size = getTextSize(list);
+ char[] array = new char[size];
+
+ for (int i = 0; i < list.getLength(); ++i)
+ {
+ if (list.item(i) instanceof Text)
+ {
+ char[] chars = ((Text) list.item(i)).getData().toCharArray();
+
+ System.arraycopy(chars, 0, array, position, chars.length);
+ position += chars.length;
+ }
+ }
+
+ return new String(array);
+ }
+
+
+
+ private static int
+ getTextSize(NodeList list)
+ {
+ int result = 0;
+
+ for (int i = 0; i < list.getLength(); ++i)
+ {
+ if (list.item(i) instanceof Text)
+ {
+ result += ((Text) list.item(i)).getLength();
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public static TransformerFactory
+ getTransformerFactory() throws TransformerConfigurationException
+ {
+ if (transformerFactory == null)
+ {
+ transformerFactory = newTransformerFactory();
+ }
+
+ return transformerFactory;
+ }
+
+
+
+ private static boolean
+ isWhiteSpace(String s)
+ {
+ for (int i = 0; i < s.length(); ++i)
+ {
+ if (!Character.isWhitespace(s.charAt(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static boolean
+ isXml(String mimeType)
+ {
+ mimeType = stripMimeTypeParameters(mimeType).toLowerCase();
+
+ return
+ "text/xml".equals(mimeType) || "application/xml".equals(mimeType) ||
+ mimeType.endsWith("+xml");
+ }
+
+
+
+ public static boolean
+ isXmlChar(char c)
+ {
+ return
+ c == 0x9 || c == 0xa || c == 0xd || (c >= 0x20 && c <= 0xd7ff) ||
+ (c >= 0xe000 && c <= 0xfffd);
+ }
+
+
+
+ public static boolean
+ isXmlText(String s)
+ {
+ return isXmlText(s.toCharArray());
+ }
+
+
+
+ public static boolean
+ isXmlText(char[] c)
+ {
+ for (int i = 0; i < c.length; ++i)
+ {
+ if (!isXmlChar(c[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ public static DocumentBuilderFactory
+ newDocumentBuilderFactory(boolean validating)
+ throws ParserConfigurationException
+ {
+ try
+ {
+ String className =
+ be.re.util.Util.
+ getSystemProperty("javax.xml.parsers.DocumentBuilderFactory");
+ DocumentBuilderFactory factory =
+ className != null ?
+ (DocumentBuilderFactory) Class.forName(className).newInstance() :
+ DocumentBuilderFactory.newInstance();
+
+ factory.setNamespaceAware(true);
+ factory.setValidating(validating);
+
+ return factory;
+ }
+
+ catch (Exception e)
+ {
+ throw new ParserConfigurationException(e.getMessage());
+ }
+ }
+
+
+
+ public static TransformerFactory
+ newTransformerFactory() throws TransformerConfigurationException
+ {
+ try
+ {
+ String className =
+ be.re.util.Util.
+ getSystemProperty("javax.xml.transform.TransformerFactory");
+
+ return
+ className != null ?
+ (TransformerFactory) Class.forName(className).newInstance() :
+ TransformerFactory.newInstance();
+ }
+
+ catch (Exception e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+
+
+
+ /**
+ * Passes a DOM-processing-instruction to a <code>DocumentHandler</code>.
+ */
+
+ public static void
+ processingInstructionToDocumentHandler
+ (
+ ProcessingInstruction processingInstruction,
+ DocumentHandler handler
+ ) throws SAXException
+ {
+ handler.processingInstruction
+ (
+ processingInstruction.getTarget(),
+ processingInstruction.getData()
+ );
+ }
+
+
+
+ public static void
+ removeChildren(Node node)
+ {
+ while (node.hasChildNodes())
+ {
+ node.removeChild(node.getLastChild());
+ }
+ }
+
+
+
+ public static void
+ removeIgnorableSpace(Element element)
+ {
+ element.normalize();
+ removeIgnorableSpace(element.getFirstChild());
+ }
+
+
+
+ private static void
+ removeIgnorableSpace(Node node)
+ {
+ if (node == null)
+ {
+ return;
+ }
+
+ removeIgnorableSpace(node.getNextSibling());
+ removeIgnorableSpace(node.getFirstChild());
+
+ if (node instanceof Text && isWhiteSpace(((Text) node).getData()))
+ {
+ node.getParentNode().removeChild(node);
+ }
+ }
+
+
+
+ private static void
+ selectChild(Node node, Equal equal, Object refData, List children)
+ {
+ if (node != null)
+ {
+ if (equal.equal(node, refData))
+ {
+ children.add(node);
+ }
+
+ selectChild(node.getNextSibling(), equal, refData, children);
+ }
+ }
+
+
+
+ private static void
+ selectChildNotBeyond
+ (
+ Node node,
+ Equal equal,
+ Object refData,
+ Equal equalLimit,
+ Object refDataLimit,
+ List children
+ )
+ {
+ if (node != null)
+ {
+ if (equal.equal(node, refData))
+ {
+ children.add(node);
+ }
+
+ if (!equalLimit.equal(node, refDataLimit))
+ {
+ selectChildNotBeyond
+ (
+ node.getNextSibling(),
+ equal,
+ refData,
+ equalLimit,
+ refDataLimit,
+ children
+ );
+ }
+ }
+ }
+
+
+
+ /**
+ * Returns all direct child nodes of <code>node</code> that meet the
+ * condition expressed by <code>equal</code> and <code>refData</code>.
+ * @see be.re.util.Equal
+ */
+
+ public static Node[]
+ selectChildren(Node node, Equal equal, Object refData)
+ {
+ List children = new ArrayList();
+
+ selectChild(node.getFirstChild(), equal, refData, children);
+
+ return (Node[]) children.toArray(new Node[children.size()]);
+ }
+
+
+
+ /**
+ * Returns all direct child nodes of <code>node</code> called
+ * <code>name</code>.
+ */
+
+ public static Node[]
+ selectChildren(Node node, String name)
+ {
+ return selectChildren(node, nameSelector, name);
+ }
+
+
+
+ /**
+ * Returns all direct child nodes of <code>node</code> with namespace
+ * <code>namespaceURI</code> and local name <code>localName</code>.
+ * <code>localName</code> may be null;
+ */
+
+ public static Node[]
+ selectChildren(Node node, String namespaceURI, String localName)
+ {
+ return
+ selectChildren
+ (
+ node,
+ qnameSelector,
+ new ExpandedName(namespaceURI, localName)
+ );
+ }
+
+
+
+ /**
+ * Returns all direct child nodes of <code>node</code> that meet the
+ * condition expressed by <code>equal</code> and <code>refData</code>. The
+ * search stops if a node meeting the condition expressed by
+ * <code>equalLimit</code> and <code>refDataLimit</code> is encountered.
+ * @see be.re.util.Equal
+ */
+
+ public static Node[]
+ selectChildrenNotBeyond
+ (
+ Node node,
+ Equal equal,
+ Object refData,
+ Equal equalLimit,
+ Object refDataLimit
+ )
+ {
+ List children = new ArrayList();
+
+ selectChildNotBeyond
+ (
+ node.getFirstChild(),
+ equal,
+ refData,
+ equalLimit,
+ refDataLimit,
+ children
+ );
+
+ return (Node[]) children.toArray(new Node[children.size()]);
+ }
+
+
+
+ /**
+ * Returns all direct child nodes of <code>node</code> called
+ * <code>name</code>. The search stops is a node called <code>limit</code> is
+ * encountered.
+ */
+
+ public static Node[]
+ selectChildrenNotBeyond(Node node, String name, String limit)
+ {
+ return
+ selectChildrenNotBeyond(node, nameSelector, name, nameSelector, limit);
+ }
+
+
+
+ /**
+ * Returns an element if there is exactly one that matches and
+ * <code>null</code> otherwise.
+ */
+
+ public static Element
+ selectElement(Node node, ExpandedName[] path)
+ {
+ List result = new ArrayList();
+
+ selectElements(node, path, 0, result);
+
+ return result.size() == 1 ? (Element) result.get(0) : null;
+ }
+
+
+
+ public static Element[]
+ selectElements(Node node)
+ {
+ List children = new ArrayList();
+
+ selectChild(node.getFirstChild(), elementSelector, null, children);
+
+ return (Element[]) children.toArray(new Element[children.size()]);
+ }
+
+
+
+ /**
+ * Returns all elements that match the path.
+ */
+
+ public static Element[]
+ selectElements(Node node, ExpandedName[] path)
+ {
+ List result = new ArrayList();
+
+ selectElements(node, path, 0, result);
+
+ return (Element[]) result.toArray(new Element[0]);
+ }
+
+
+
+ private static void
+ selectElements(Node node, ExpandedName[] path, int position, List result)
+ {
+ if (position == path.length)
+ {
+ return;
+ }
+
+ Node[] children =
+ selectChildren
+ (
+ node,
+ path[position].namespaceURI,
+ path[position].localName
+ );
+
+ for (int i = 0; i < children.length; ++i)
+ {
+ if (position < path.length - 1)
+ {
+ selectElements(children[i], path, position + 1, result);
+ }
+ else
+ {
+ result.add((Element) children[i]);
+ }
+ }
+ }
+
+
+
+ /**
+ * Returns the first direct child of <code>node</code> that meets the
+ * condition expressed by <code>equal</code> and <code>refData</code>.
+ * @see be.re.util.Equal
+ */
+
+ public static Node
+ selectFirstChild(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null : selectNextSiblingSibling(node.getFirstChild(), equal, refData);
+ }
+
+
+
+ /**
+ * Returns the first direct child of <code>node</code> called
+ * <code>name</code>.
+ */
+
+ public static Node
+ selectFirstChild(Node node, String name)
+ {
+ return selectFirstChild(node, nameSelector, name);
+ }
+
+
+
+ /**
+ * Returns the first direct child of <code>node</code> with namespace
+ * <code>namespaceURI</code> and local name <code>localName</code>.
+ */
+
+ public static Node
+ selectFirstChild(Node node, String namespaceURI, String localName)
+ {
+ return
+ selectFirstChild
+ (
+ node,
+ qnameSelector,
+ new ExpandedName(namespaceURI, localName)
+ );
+ }
+
+
+
+ /**
+ * Returns the first direct child element of <code>node</code>.
+ */
+
+ public static Element
+ selectFirstElement(Node node)
+ {
+ return (Element) selectFirstChild(node, elementSelector, null);
+ }
+
+
+
+ /**
+ * Returns the last direct child of <code>node</code> that meets the
+ * condition expressed by <code>equal</code> and <code>refData</code>.
+ * @see be.re.util.Equal
+ */
+
+ public static Node
+ selectLastChild(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null :
+ selectPreviousSiblingSibling(node.getLastChild(), equal, refData);
+ }
+
+
+
+ /**
+ * Returns the last direct child of <code>node</code> called
+ * <code>name</code>.
+ */
+
+ public static Node
+ selectLastChild(Node node, String name)
+ {
+ return selectLastChild(node, nameSelector, name);
+ }
+
+
+
+ /**
+ * Returns the last direct child of <code>node</code> with namespace
+ * <code>namespaceURI</code> and local name <code>localName</code>.
+ */
+
+ public static Node
+ selectLastChild(Node node, String namespaceURI, String localName)
+ {
+ return
+ selectLastChild
+ (
+ node,
+ qnameSelector,
+ new ExpandedName(namespaceURI, localName)
+ );
+ }
+
+
+
+ /**
+ * Returns the next sibling of <code>node</code> that meets the
+ * condition expressed by <code>equal</code> and <code>refData</code>.
+ * @see be.re.util.Equal
+ */
+
+ public static Node
+ selectNextSibling(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null :
+ selectNextSiblingSibling(node.getNextSibling(), equal, refData);
+ }
+
+
+
+ /**
+ * Returns the next sibling of <code>node</code> called <code>name</code>.
+ */
+
+ public static Node
+ selectNextSibling(Node node, String name)
+ {
+ return selectNextSibling(node, nameSelector, name);
+ }
+
+
+
+ /**
+ * Returns the next sibling of <code>node</code> with namspace
+ * <code>namespaceURI</code> and local name <code>localName</code>.
+ */
+
+ public static Node
+ selectNextSibling(Node node, String namespaceURI, String localName)
+ {
+ return
+ selectNextSibling
+ (
+ node,
+ qnameSelector,
+ new ExpandedName(namespaceURI, localName)
+ );
+ }
+
+
+
+ private static Node
+ selectNextSiblingSibling(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null :
+ (
+ equal.equal(node, refData) ?
+ node :
+ selectNextSiblingSibling(node.getNextSibling(), equal, refData)
+ );
+ }
+
+
+
+ /**
+ * Returns the previous sibling of <code>node</code> that meets the
+ * condition expressed by <code>equal</code> and <code>refData</code>.
+ * @see be.re.util.Equal
+ */
+
+ public static Node
+ selectPreviousSibling(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null :
+ selectPreviousSiblingSibling(node.getPreviousSibling(), equal, refData);
+ }
+
+
+
+ /**
+ * Returns the previous sibling of <code>node</code> called <code>name</code>.
+ */
+
+ public static Node
+ selectPreviousSibling(Node node, String name)
+ {
+ return selectPreviousSibling(node, nameSelector, name);
+ }
+
+
+
+ /**
+ * Returns the previous sibling of <code>node</code> with namespace
+ * <code>namespaceURI</code> and local name <code>localName</code>.
+ */
+
+ public static Node
+ selectPreviousSibling(Node node, String namespaceURI, String localName)
+ {
+ return
+ selectPreviousSibling
+ (
+ node,
+ qnameSelector,
+ new ExpandedName(namespaceURI, localName)
+ );
+ }
+
+
+
+ private static Node
+ selectPreviousSiblingSibling(Node node, Equal equal, Object refData)
+ {
+ return
+ node == null ?
+ null :
+ (
+ equal.equal(node, refData) ?
+ node :
+ selectPreviousSiblingSibling
+ (
+ node.getPreviousSibling(),
+ equal,
+ refData
+ )
+ );
+ }
+
+
+
+ public static void
+ setTransformerParameters(Transformer transformer, Map parameters)
+ {
+ for (Iterator i = parameters.entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+
+ transformer.
+ setParameter((String) entry.getKey(), (String) entry.getValue());
+ }
+ }
+
+
+
+ private static String
+ stripMimeTypeParameters(String mimeType)
+ {
+ int index = mimeType.indexOf(';');
+
+ return index != -1 ? mimeType.substring(0, index) : mimeType;
+ }
+
+
+
+ /**
+ * Passes a DOM-text-node to a <code>DocumentHandler</code>.
+ */
+
+ public static void
+ textToDocumentHandler(Text text, DocumentHandler handler) throws SAXException
+ {
+ String data = text.getData();
+ char[] chars = new char[data.length()];
+
+ data.getChars(0, chars.length, chars, 0);
+ handler.characters(chars, 0, chars.length);
+ }
+
+} // Util
diff --git a/src/be/re/xml/sax/BalanceChecker.java b/src/be/re/xml/sax/BalanceChecker.java
new file mode 100644
index 0000000..ff37dae
--- /dev/null
+++ b/src/be/re/xml/sax/BalanceChecker.java
@@ -0,0 +1,157 @@
+package be.re.xml.sax;
+
+import be.re.xml.ExpandedName;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Stack;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+public class BalanceChecker extends XMLFilterImpl
+
+{
+
+ private Stack elements = new Stack();
+ private File file;
+ private int indent = 0;
+ private PrintStream out;
+
+
+
+ public
+ BalanceChecker(File file)
+ {
+ this.file = file;
+ }
+
+
+
+ public
+ BalanceChecker(File file, XMLReader parent)
+ {
+ super(parent);
+ this.file = file;
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ for (int i = elements.size() - 1; i >=0; --i)
+ {
+ write("Element " + elements.get(i).toString() + " is not closed.");
+ }
+
+ out.close();
+ super.endDocument();
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ ExpandedName element = new ExpandedName(namespaceURI, localName);
+
+ if (elements.isEmpty())
+ {
+ write
+ (
+ "Closing " + element.toString() + " while no open elements are left."
+ );
+ }
+ else
+ {
+ ExpandedName name = (ExpandedName) elements.pop();
+
+ if (!name.equals(element))
+ {
+ write
+ (
+ "Closing " + element.toString() + " while expecting " +
+ name.toString() + "."
+ );
+ }
+ }
+
+ indent -= 2;
+ write("</" + element.toString() + ">");
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+
+
+ private void
+ openWriter() throws SAXException
+ {
+ if (out == null)
+ {
+ try
+ {
+ out = new PrintStream(new FileOutputStream(file));
+ }
+
+ catch (IOException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ openWriter();
+ super.startDocument();
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ openWriter();
+
+ ExpandedName name = new ExpandedName(namespaceURI, localName);
+
+ write("<" + name.toString() + ">");
+ indent += 2;
+ elements.push(name);
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+
+
+ private void
+ write(String s)
+ {
+ if (indent > 0)
+ {
+ char[] c = new char[indent];
+
+ Arrays.fill(c, ' ');
+ out.print(c);
+ }
+
+ out.println(s);
+ out.flush();
+ }
+
+} // BalanceChecker
diff --git a/src/be/re/xml/sax/ErrorHandler.java b/src/be/re/xml/sax/ErrorHandler.java
new file mode 100644
index 0000000..464137e
--- /dev/null
+++ b/src/be/re/xml/sax/ErrorHandler.java
@@ -0,0 +1,68 @@
+package be.re.xml.sax;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+
+public class ErrorHandler implements org.xml.sax.ErrorHandler
+
+{
+
+ private boolean warnings;
+
+
+
+ public
+ ErrorHandler()
+ {
+ this(false);
+ }
+
+
+
+ public
+ ErrorHandler(boolean warnings)
+ {
+ this.warnings = warnings;
+ }
+
+
+
+ public void
+ error(SAXParseException exception) throws SAXException
+ {
+ System.err.println("Error: " + getMessage(exception));
+ }
+
+
+
+ public void
+ fatalError(SAXParseException exception) throws SAXException
+ {
+ System.err.println("Fatal error: " + getMessage(exception));
+ }
+
+
+
+ private String
+ getMessage(SAXParseException e)
+ {
+ return
+ (e.getPublicId() != null ? (e.getPublicId() + ": ") : "") +
+ (e.getSystemId() != null ? (e.getSystemId() + ": ") : "") + "Line " +
+ String.valueOf(e.getLineNumber()) + ": " + e.getMessage();
+ }
+
+
+
+ public void
+ warning(SAXParseException exception) throws SAXException
+ {
+ if (warnings)
+ {
+ System.err.println("Warning: " + getMessage(exception));
+ }
+ }
+
+} // ErrorHandler
diff --git a/src/be/re/xml/sax/FilterOfFilters.java b/src/be/re/xml/sax/FilterOfFilters.java
new file mode 100644
index 0000000..0845f4e
--- /dev/null
+++ b/src/be/re/xml/sax/FilterOfFilters.java
@@ -0,0 +1,343 @@
+package be.re.xml.sax;
+
+import be.re.io.FlushOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * The given array of filters is connected into a filter-chain and encapsulated
+ * in this filter, so you can insert the whole in a chain as one filter. The
+ * wiring of the event chains will be interrupted for those filters that don't
+ * implement the corresponding handler interfaces.
+ * @author Werner Donn\u00e9
+ */
+
+public class FilterOfFilters extends XMLFilterImpl
+
+{
+
+ private XMLFilter first;
+ private XMLFilter last;
+
+
+
+ public
+ FilterOfFilters(XMLFilter[] filters)
+ {
+ this(filters, false);
+ }
+
+
+
+ public
+ FilterOfFilters(XMLFilter[] filters, boolean debug)
+ {
+ setupChain(debug ? addDebug(filters) : filters);
+ }
+
+
+
+ public
+ FilterOfFilters(XMLFilter[] filters, XMLReader parent)
+ {
+ this(filters, false, parent);
+ }
+
+
+
+ public
+ FilterOfFilters(XMLFilter[] filters, boolean debug, XMLReader parent)
+ {
+ setupChain(debug ? addDebug(filters) : filters);
+ setParent(parent);
+ }
+
+
+
+ private XMLFilter[]
+ addDebug(XMLFilter[] filters)
+ {
+ XMLFilter[] result = new XMLFilter[filters.length * 2 + 1];
+
+ result[0] =
+ new Tee
+ (
+ new ContentHandler[]
+ {
+ outputHandler(toString() + "_input.xml")
+ }
+ );
+
+ for (int i = 0; i < filters.length; ++i)
+ {
+ result[i * 2 + 1] = filters[i];
+ result[i * 2 + 2] =
+ new Tee
+ (
+ new ContentHandler[]
+ {
+ new BalanceChecker
+ (
+ new File(toString() + "_" + filters[i].toString() + ".balance")
+ ),
+ outputHandler(toString() + "_" + filters[i].toString())
+ }
+ );
+ }
+
+ return result;
+ }
+
+
+
+ public boolean
+ getFeature(String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ return first != null ? first.getFeature(name) : super.getFeature(name);
+ }
+
+
+
+ public XMLReader
+ getParent()
+ {
+ return first != null ? first.getParent() : super.getParent();
+ }
+
+
+
+ public Object
+ getProperty(String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ return first != null ? first.getProperty(name) : super.getProperty(name);
+ }
+
+
+
+ private static ContentHandler
+ outputHandler(String filename)
+ {
+ try
+ {
+ TransformerHandler handler =
+ Util.newSAXTransformerFactory().newTransformerHandler();
+
+ handler.setResult
+ (
+ new StreamResult(new FlushOutputStream(new FileOutputStream(filename)))
+ );
+
+ return handler;
+ }
+
+ catch (Exception e)
+ {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+
+
+ public void
+ parse(InputSource input) throws IOException, SAXException
+ {
+ if (last != null)
+ {
+ last.parse(input);
+ }
+ else
+ {
+ super.parse(input);
+ }
+ }
+
+
+
+ public void
+ setContentHandler(ContentHandler handler)
+ {
+ if (last != null)
+ {
+ last.setContentHandler(handler);
+ }
+ else
+ {
+ super.setContentHandler(handler);
+ }
+ }
+
+
+
+ public void
+ setDTDHandler(DTDHandler handler)
+ {
+ if (last != null)
+ {
+ last.setDTDHandler(handler);
+ }
+ else
+ {
+ super.setDTDHandler(handler);
+ }
+ }
+
+
+
+ public void
+ setEntityResolver(EntityResolver resolver)
+ {
+ if (last != null)
+ {
+ last.setEntityResolver(resolver);
+ }
+ else
+ {
+ super.setEntityResolver(resolver);
+ }
+ }
+
+
+
+ public void
+ setErrorHandler(ErrorHandler handler)
+ {
+ if (last != null)
+ {
+ last.setErrorHandler(handler);
+ }
+ else
+ {
+ super.setErrorHandler(handler);
+ }
+ }
+
+
+
+ public void
+ setFeature(String name, boolean value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if (first != null)
+ {
+ first.setFeature(name, value);
+ }
+ else
+ {
+ super.setFeature(name, value);
+ }
+ }
+
+
+
+ public void
+ setParent(XMLReader parent)
+ {
+ if (first != null)
+ {
+ first.setParent(parent);
+ }
+ else
+ {
+ super.setParent(parent);
+ }
+ }
+
+
+
+ public void
+ setProperty(String name, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if (first != null)
+ {
+ first.setProperty(name, value);
+ }
+ else
+ {
+ super.setProperty(name, value);
+ }
+ }
+
+
+
+ private void
+ setupChain(XMLFilter[] filters)
+ {
+ if (filters.length == 0)
+ {
+ return;
+ }
+
+ for (int i = filters.length - 1; i > 0; --i)
+ {
+ filters[i].setParent(filters[i - 1]);
+
+ // The following connections make it work also when this filter is
+ // inserted in a chain that is already running, i.e. for which parse is
+ // already called.
+
+ if (filters[i] instanceof ContentHandler)
+ {
+ filters[i - 1].setContentHandler((ContentHandler) filters[i]);
+ }
+
+ if (filters[i] instanceof DTDHandler)
+ {
+ filters[i - 1].setDTDHandler((DTDHandler) filters[i]);
+ }
+
+ if (filters[i] instanceof EntityResolver)
+ {
+ filters[i - 1].setEntityResolver((EntityResolver) filters[i]);
+ }
+
+ if (filters[i] instanceof ErrorHandler)
+ {
+ filters[i - 1].setErrorHandler((ErrorHandler) filters[i]);
+ }
+ }
+
+ first = filters[0];
+ last = filters[filters.length - 1];
+
+ if (first instanceof ContentHandler)
+ {
+ super.setContentHandler((ContentHandler) first);
+ }
+
+ if (first instanceof DTDHandler)
+ {
+ super.setDTDHandler((DTDHandler) first);
+ }
+
+ if (first instanceof EntityResolver)
+ {
+ super.setEntityResolver((EntityResolver) first);
+ }
+
+ if (first instanceof ErrorHandler)
+ {
+ super.setErrorHandler((ErrorHandler) first);
+ }
+ }
+
+} // FilterOfFilters
diff --git a/src/be/re/xml/sax/GobbleDocumentEvents.java b/src/be/re/xml/sax/GobbleDocumentEvents.java
new file mode 100644
index 0000000..2fb7ac7
--- /dev/null
+++ b/src/be/re/xml/sax/GobbleDocumentEvents.java
@@ -0,0 +1,46 @@
+package be.re.xml.sax;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Blocks the document events, which is useful for inlining another SAX source
+ * in a SAX result.
+ * @author Werner Donn\u00e9
+ */
+
+public class GobbleDocumentEvents extends XMLFilterImpl
+
+{
+
+ public
+ GobbleDocumentEvents()
+ {
+ }
+
+
+
+ public
+ GobbleDocumentEvents(XMLReader parent)
+ {
+ super(parent);
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ }
+
+} // GobbleDocumentEvents
diff --git a/src/be/re/xml/sax/ProtectEventHandlerFilter.java b/src/be/re/xml/sax/ProtectEventHandlerFilter.java
new file mode 100644
index 0000000..e8660e8
--- /dev/null
+++ b/src/be/re/xml/sax/ProtectEventHandlerFilter.java
@@ -0,0 +1,91 @@
+package be.re.xml.sax;
+
+import java.io.IOException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * If the parent already has an entity resolver on it, it will not be
+ * replaced by this filter.
+ * @author Werner Donn\u00e9
+ */
+
+public class ProtectEventHandlerFilter extends XMLFilterImpl
+
+{
+
+ private boolean entityResolver;
+ private boolean errorHandler;
+
+
+
+ public
+ ProtectEventHandlerFilter(boolean entityResolver, boolean errorHandler)
+ {
+ this.entityResolver = entityResolver;
+ this.errorHandler = errorHandler;
+ }
+
+
+
+ public
+ ProtectEventHandlerFilter
+ (
+ boolean entityResolver,
+ boolean errorHandler,
+ XMLReader parent
+ )
+ {
+ super(parent);
+ this.entityResolver = entityResolver;
+ this.errorHandler = errorHandler;
+ }
+
+
+
+ public void
+ parse(InputSource input) throws IOException, SAXException
+ {
+ if (getParent() != null)
+ {
+ setupParse();
+ getParent().parse(input);
+ }
+ }
+
+
+
+ public void
+ parse(String systemId) throws IOException, SAXException
+ {
+ if (getParent() != null)
+ {
+ setupParse();
+ getParent().parse(systemId);
+ }
+ }
+
+
+
+ private void
+ setupParse()
+ {
+ if (!entityResolver || getParent().getEntityResolver() == null)
+ {
+ getParent().setEntityResolver(this);
+ }
+
+ getParent().setContentHandler(this);
+ getParent().setDTDHandler(this);
+
+ if (!errorHandler || getParent().getErrorHandler() == null)
+ {
+ getParent().setErrorHandler(this);
+ }
+ }
+
+} // ProtectEventHandlerFilter
diff --git a/src/be/re/xml/sax/Tee.java b/src/be/re/xml/sax/Tee.java
new file mode 100644
index 0000000..3e3ec8d
--- /dev/null
+++ b/src/be/re/xml/sax/Tee.java
@@ -0,0 +1,190 @@
+package be.re.xml.sax;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * This XML filter delegates everything to multiple other handlers.
+ * @author Werner Donn\u00e9
+ */
+
+public class Tee extends XMLFilterImpl
+
+{
+
+ private ContentHandler[] tubes;
+
+
+
+ public
+ Tee(ContentHandler[] tubes)
+ {
+ this.tubes = tubes;
+ }
+
+
+
+ public
+ Tee(ContentHandler[] tubes, XMLReader parent)
+ {
+ super(parent);
+ this.tubes = tubes;
+ }
+
+
+
+ public void
+ characters(char[] ch, int start, int length) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].characters(ch, start, length);
+ }
+
+ super.characters(ch, start, length);
+ }
+
+
+
+ public void
+ endDocument() throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].endDocument();
+ }
+
+ super.endDocument();
+ }
+
+
+
+ public void
+ endElement(String namespaceURI, String localName, String qName)
+ throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].endElement(namespaceURI, localName, qName);
+ }
+
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+
+
+ public void
+ endPrefixMapping(String prefix) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].endPrefixMapping(prefix);
+ }
+
+ super.endPrefixMapping(prefix);
+ }
+
+
+
+ public void
+ ignorableWhitespace(char[] ch, int start, int length) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].ignorableWhitespace(ch, start, length);
+ }
+
+ super.ignorableWhitespace(ch, start,length);
+ }
+
+
+
+ public void
+ processingInstruction(String target, String data) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].processingInstruction(target, data);
+ }
+
+ super.processingInstruction(target, data);
+ }
+
+
+
+ public void
+ setDocumentLocator(Locator locator)
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].setDocumentLocator(locator);
+ }
+
+ super.setDocumentLocator(locator);
+ }
+
+
+
+ public void
+ skippedEntity(String name) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].skippedEntity(name);
+ }
+
+ super.skippedEntity(name);
+ }
+
+
+
+ public void
+ startDocument() throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].startDocument();
+ }
+
+ super.startDocument();
+ }
+
+
+
+ public void
+ startElement
+ (
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].startElement(namespaceURI, localName, qName, atts);
+ }
+
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+
+
+ public void
+ startPrefixMapping(String prefix, String uri) throws SAXException
+ {
+ for (int i = 0; i < tubes.length; ++i)
+ {
+ tubes[i].startPrefixMapping(prefix, uri);
+ }
+
+ super.startPrefixMapping(prefix, uri);
+ }
+
+} // Tee
diff --git a/src/be/re/xml/sax/TransformerHandlerFilter.java b/src/be/re/xml/sax/TransformerHandlerFilter.java
new file mode 100644
index 0000000..1fe0e20
--- /dev/null
+++ b/src/be/re/xml/sax/TransformerHandlerFilter.java
@@ -0,0 +1,49 @@
+package be.re.xml.sax;
+
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.TransformerHandler;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+
+
+/**
+ * Wraps a <code>TransformerHandler</code> in a filter.
+ * @author Werner Donn\u00e9
+ */
+
+public class TransformerHandlerFilter extends XMLFilterImpl
+
+{
+
+ private TransformerHandler handler;
+
+
+
+ public
+ TransformerHandlerFilter(TransformerHandler handler)
+ {
+ this.handler = handler;
+ super.setContentHandler(handler);
+ }
+
+
+
+ public
+ TransformerHandlerFilter(TransformerHandler handler, XMLReader parent)
+ {
+ super(parent);
+ this.handler = handler;
+ super.setContentHandler(handler);
+ }
+
+
+
+ public void
+ setContentHandler(ContentHandler value)
+ {
+ handler.setResult(new SAXResult(value));
+ }
+
+} // TransformerHandlerFilter
diff --git a/src/be/re/xml/sax/Util.java b/src/be/re/xml/sax/Util.java
new file mode 100644
index 0000000..0969d2e
--- /dev/null
+++ b/src/be/re/xml/sax/Util.java
@@ -0,0 +1,267 @@
+package be.re.xml.sax;
+
+import be.re.xml.CatalogResolver;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLFilter;
+import org.xml.sax.XMLReader;
+
+
+
+public class Util
+
+{
+
+ public static XMLReader
+ getParser(URL catalog, boolean validating) throws SAXException
+ {
+ try
+ {
+ return getParser(newSAXParserFactory(validating), catalog);
+ }
+
+ catch (ParserConfigurationException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+
+
+ public static XMLReader
+ getParser(SAXParserFactory factory, URL catalog)
+ throws SAXException
+ {
+ try
+ {
+ XMLReader parser = factory.newSAXParser().getXMLReader();
+
+ parser.setErrorHandler(new ErrorHandler(false));
+
+ if (catalog != null)
+ {
+ CatalogResolver resolver = new CatalogResolver(catalog);
+
+ parser.setEntityResolver(resolver);
+ trySchemaLocation(parser, resolver);
+ }
+
+ return parser;
+ }
+
+ catch (SAXException e)
+ {
+ throw e;
+ }
+
+ catch (Exception e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+
+
+ public static SAXParserFactory
+ newSAXParserFactory(boolean validating) throws ParserConfigurationException
+ {
+ try
+ {
+ String className =
+ be.re.util.Util.getSystemProperty("javax.xml.parsers.SAXParserFactory");
+ SAXParserFactory factory =
+ className != null ?
+ (SAXParserFactory) Class.forName(className).newInstance() :
+ SAXParserFactory.newInstance();
+
+ factory.setNamespaceAware(true);
+ factory.setValidating(validating);
+ tryFactoryProperties(factory, validating);
+
+ return factory;
+ }
+
+ catch (Exception e)
+ {
+ throw new ParserConfigurationException(e.getMessage());
+ }
+ }
+
+
+
+ public static SAXTransformerFactory
+ newSAXTransformerFactory() throws TransformerConfigurationException
+ {
+ return (SAXTransformerFactory) be.re.xml.Util.newTransformerFactory();
+ }
+
+
+
+ public static XMLFilter
+ newTemplatesFilter
+ (
+ Templates templates,
+ Map parameters,
+ SAXTransformerFactory factory
+ ) throws TransformerConfigurationException
+ {
+ if
+ (
+ !"net.sf.saxon.PreparedStylesheet".equals(templates.getClass().getName())
+ )
+ {
+ return
+ factory.
+ newXMLFilter(new ParameterizableTemplate(templates, parameters));
+ }
+
+ XMLFilter result = factory.newXMLFilter(templates);
+
+ try
+ {
+ be.re.xml.Util.setTransformerParameters
+ (
+ (Transformer)
+ result.getClass().getMethod("getTransformer", new Class[0]).
+ invoke(result, new Object[0]),
+ parameters
+ );
+ }
+
+ catch (Exception e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+
+ return result;
+ }
+
+
+
+ public static TransformerHandler
+ newTemplatesHandler
+ (
+ Templates templates,
+ Map parameters,
+ SAXTransformerFactory factory
+ ) throws TransformerConfigurationException
+ {
+ TransformerHandler result = factory.newTransformerHandler(templates);
+
+ be.re.xml.Util.
+ setTransformerParameters(result.getTransformer(), parameters);
+
+ return result;
+ }
+
+
+
+ private static void
+ tryFactoryProperties(SAXParserFactory factory, boolean validating)
+ {
+ try
+ {
+ factory.setFeature
+ (
+ "http://apache.org/xml/features/validation/schema",
+ validating
+ );
+
+ factory.setFeature
+ (
+ "http://apache.org/xml/features/validation/schema-full-checking",
+ validating
+ );
+ }
+
+ catch (Exception e)
+ {
+ }
+ }
+
+
+
+ private static void
+ trySchemaLocation(XMLReader parser, CatalogResolver resolver)
+ {
+ try
+ {
+ String schemaLocation = "";
+
+ for
+ (
+ Iterator i = resolver.getSystemIdentifierMappings().keySet().iterator();
+ i.hasNext();
+ )
+ {
+ String key = (String) i.next();
+
+ schemaLocation +=
+ key + " " + key + " ";
+ //key + " " + resolver.getSystemIdentifierMappings().get(key) + " ";
+ // The postman always rings twice.
+ }
+
+ parser.setProperty
+ (
+ "http://apache.org/xml/properties/schema/external-schemaLocation",
+ schemaLocation
+ );
+ }
+
+ catch (Exception e)
+ {
+ }
+ }
+
+
+
+ private static class ParameterizableTemplate implements Templates
+
+ {
+
+ private Templates delegate;
+ private Map parameters;
+
+
+
+ private
+ ParameterizableTemplate(Templates delegate, Map parameters)
+ {
+ this.delegate = delegate;
+ this.parameters = parameters;
+ }
+
+
+
+ public Properties
+ getOutputProperties()
+ {
+ return delegate.getOutputProperties();
+ }
+
+
+
+ public Transformer
+ newTransformer() throws TransformerConfigurationException
+ {
+ Transformer transformer = delegate.newTransformer();
+
+ be.re.xml.Util.setTransformerParameters(transformer, parameters);
+
+ return transformer;
+ }
+
+ } // ParameterizableTemplate
+
+} // Util
diff --git a/src/catalog b/src/catalog
new file mode 100644
index 0000000..898232f
--- /dev/null
+++ b/src/catalog
@@ -0,0 +1,9 @@
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "xhtml1/xhtml1-strict.dtd"
+SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ "xhtml1/xhtml1-strict.dtd"
+
+PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "xhtml1/xhtml1-transitional.dtd"
+SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+ "xhtml1/xhtml1-transitional.dtd"
diff --git a/src/xhtml1/xhtml-lat1.ent b/src/xhtml1/xhtml-lat1.ent
new file mode 100644
index 0000000..ffee223
--- /dev/null
+++ b/src/xhtml1/xhtml-lat1.ent
@@ -0,0 +1,196 @@
+<!-- Portions (C) International Organization for Standardization 1986
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
+ %HTMLlat1;
+-->
+
+<!ENTITY nbsp " "> <!-- no-break space = non-breaking space,
+ U+00A0 ISOnum -->
+<!ENTITY iexcl "¡"> <!-- inverted exclamation mark, U+00A1 ISOnum -->
+<!ENTITY cent "¢"> <!-- cent sign, U+00A2 ISOnum -->
+<!ENTITY pound "£"> <!-- pound sign, U+00A3 ISOnum -->
+<!ENTITY curren "¤"> <!-- currency sign, U+00A4 ISOnum -->
+<!ENTITY yen "¥"> <!-- yen sign = yuan sign, U+00A5 ISOnum -->
+<!ENTITY brvbar "¦"> <!-- broken bar = broken vertical bar,
+ U+00A6 ISOnum -->
+<!ENTITY sect "§"> <!-- section sign, U+00A7 ISOnum -->
+<!ENTITY uml "¨"> <!-- diaeresis = spacing diaeresis,
+ U+00A8 ISOdia -->
+<!ENTITY copy "©"> <!-- copyright sign, U+00A9 ISOnum -->
+<!ENTITY ordf "ª"> <!-- feminine ordinal indicator, U+00AA ISOnum -->
+<!ENTITY laquo "«"> <!-- left-pointing double angle quotation mark
+ = left pointing guillemet, U+00AB ISOnum -->
+<!ENTITY not "¬"> <!-- not sign = angled dash,
+ U+00AC ISOnum -->
+<!ENTITY shy ""> <!-- soft hyphen = discretionary hyphen,
+ U+00AD ISOnum -->
+<!ENTITY reg "®"> <!-- registered sign = registered trade mark sign,
+ U+00AE ISOnum -->
+<!ENTITY macr "¯"> <!-- macron = spacing macron = overline
+ = APL overbar, U+00AF ISOdia -->
+<!ENTITY deg "°"> <!-- degree sign, U+00B0 ISOnum -->
+<!ENTITY plusmn "±"> <!-- plus-minus sign = plus-or-minus sign,
+ U+00B1 ISOnum -->
+<!ENTITY sup2 "²"> <!-- superscript two = superscript digit two
+ = squared, U+00B2 ISOnum -->
+<!ENTITY sup3 "³"> <!-- superscript three = superscript digit three
+ = cubed, U+00B3 ISOnum -->
+<!ENTITY acute "´"> <!-- acute accent = spacing acute,
+ U+00B4 ISOdia -->
+<!ENTITY micro "µ"> <!-- micro sign, U+00B5 ISOnum -->
+<!ENTITY para "¶"> <!-- pilcrow sign = paragraph sign,
+ U+00B6 ISOnum -->
+<!ENTITY middot "·"> <!-- middle dot = Georgian comma
+ = Greek middle dot, U+00B7 ISOnum -->
+<!ENTITY cedil "¸"> <!-- cedilla = spacing cedilla, U+00B8 ISOdia -->
+<!ENTITY sup1 "¹"> <!-- superscript one = superscript digit one,
+ U+00B9 ISOnum -->
+<!ENTITY ordm "º"> <!-- masculine ordinal indicator,
+ U+00BA ISOnum -->
+<!ENTITY raquo "»"> <!-- right-pointing double angle quotation mark
+ = right pointing guillemet, U+00BB ISOnum -->
+<!ENTITY frac14 "¼"> <!-- vulgar fraction one quarter
+ = fraction one quarter, U+00BC ISOnum -->
+<!ENTITY frac12 "½"> <!-- vulgar fraction one half
+ = fraction one half, U+00BD ISOnum -->
+<!ENTITY frac34 "¾"> <!-- vulgar fraction three quarters
+ = fraction three quarters, U+00BE ISOnum -->
+<!ENTITY iquest "¿"> <!-- inverted question mark
+ = turned question mark, U+00BF ISOnum -->
+<!ENTITY Agrave "À"> <!-- latin capital letter A with grave
+ = latin capital letter A grave,
+ U+00C0 ISOlat1 -->
+<!ENTITY Aacute "Á"> <!-- latin capital letter A with acute,
+ U+00C1 ISOlat1 -->
+<!ENTITY Acirc "Â"> <!-- latin capital letter A with circumflex,
+ U+00C2 ISOlat1 -->
+<!ENTITY Atilde "Ã"> <!-- latin capital letter A with tilde,
+ U+00C3 ISOlat1 -->
+<!ENTITY Auml "Ä"> <!-- latin capital letter A with diaeresis,
+ U+00C4 ISOlat1 -->
+<!ENTITY Aring "Å"> <!-- latin capital letter A with ring above
+ = latin capital letter A ring,
+ U+00C5 ISOlat1 -->
+<!ENTITY AElig "Æ"> <!-- latin capital letter AE
+ = latin capital ligature AE,
+ U+00C6 ISOlat1 -->
+<!ENTITY Ccedil "Ç"> <!-- latin capital letter C with cedilla,
+ U+00C7 ISOlat1 -->
+<!ENTITY Egrave "È"> <!-- latin capital letter E with grave,
+ U+00C8 ISOlat1 -->
+<!ENTITY Eacute "É"> <!-- latin capital letter E with acute,
+ U+00C9 ISOlat1 -->
+<!ENTITY Ecirc "Ê"> <!-- latin capital letter E with circumflex,
+ U+00CA ISOlat1 -->
+<!ENTITY Euml "Ë"> <!-- latin capital letter E with diaeresis,
+ U+00CB ISOlat1 -->
+<!ENTITY Igrave "Ì"> <!-- latin capital letter I with grave,
+ U+00CC ISOlat1 -->
+<!ENTITY Iacute "Í"> <!-- latin capital letter I with acute,
+ U+00CD ISOlat1 -->
+<!ENTITY Icirc "Î"> <!-- latin capital letter I with circumflex,
+ U+00CE ISOlat1 -->
+<!ENTITY Iuml "Ï"> <!-- latin capital letter I with diaeresis,
+ U+00CF ISOlat1 -->
+<!ENTITY ETH "Ð"> <!-- latin capital letter ETH, U+00D0 ISOlat1 -->
+<!ENTITY Ntilde "Ñ"> <!-- latin capital letter N with tilde,
+ U+00D1 ISOlat1 -->
+<!ENTITY Ograve "Ò"> <!-- latin capital letter O with grave,
+ U+00D2 ISOlat1 -->
+<!ENTITY Oacute "Ó"> <!-- latin capital letter O with acute,
+ U+00D3 ISOlat1 -->
+<!ENTITY Ocirc "Ô"> <!-- latin capital letter O with circumflex,
+ U+00D4 ISOlat1 -->
+<!ENTITY Otilde "Õ"> <!-- latin capital letter O with tilde,
+ U+00D5 ISOlat1 -->
+<!ENTITY Ouml "Ö"> <!-- latin capital letter O with diaeresis,
+ U+00D6 ISOlat1 -->
+<!ENTITY times "×"> <!-- multiplication sign, U+00D7 ISOnum -->
+<!ENTITY Oslash "Ø"> <!-- latin capital letter O with stroke
+ = latin capital letter O slash,
+ U+00D8 ISOlat1 -->
+<!ENTITY Ugrave "Ù"> <!-- latin capital letter U with grave,
+ U+00D9 ISOlat1 -->
+<!ENTITY Uacute "Ú"> <!-- latin capital letter U with acute,
+ U+00DA ISOlat1 -->
+<!ENTITY Ucirc "Û"> <!-- latin capital letter U with circumflex,
+ U+00DB ISOlat1 -->
+<!ENTITY Uuml "Ü"> <!-- latin capital letter U with diaeresis,
+ U+00DC ISOlat1 -->
+<!ENTITY Yacute "Ý"> <!-- latin capital letter Y with acute,
+ U+00DD ISOlat1 -->
+<!ENTITY THORN "Þ"> <!-- latin capital letter THORN,
+ U+00DE ISOlat1 -->
+<!ENTITY szlig "ß"> <!-- latin small letter sharp s = ess-zed,
+ U+00DF ISOlat1 -->
+<!ENTITY agrave "à"> <!-- latin small letter a with grave
+ = latin small letter a grave,
+ U+00E0 ISOlat1 -->
+<!ENTITY aacute "á"> <!-- latin small letter a with acute,
+ U+00E1 ISOlat1 -->
+<!ENTITY acirc "â"> <!-- latin small letter a with circumflex,
+ U+00E2 ISOlat1 -->
+<!ENTITY atilde "ã"> <!-- latin small letter a with tilde,
+ U+00E3 ISOlat1 -->
+<!ENTITY auml "ä"> <!-- latin small letter a with diaeresis,
+ U+00E4 ISOlat1 -->
+<!ENTITY aring "å"> <!-- latin small letter a with ring above
+ = latin small letter a ring,
+ U+00E5 ISOlat1 -->
+<!ENTITY aelig "æ"> <!-- latin small letter ae
+ = latin small ligature ae, U+00E6 ISOlat1 -->
+<!ENTITY ccedil "ç"> <!-- latin small letter c with cedilla,
+ U+00E7 ISOlat1 -->
+<!ENTITY egrave "è"> <!-- latin small letter e with grave,
+ U+00E8 ISOlat1 -->
+<!ENTITY eacute "é"> <!-- latin small letter e with acute,
+ U+00E9 ISOlat1 -->
+<!ENTITY ecirc "ê"> <!-- latin small letter e with circumflex,
+ U+00EA ISOlat1 -->
+<!ENTITY euml "ë"> <!-- latin small letter e with diaeresis,
+ U+00EB ISOlat1 -->
+<!ENTITY igrave "ì"> <!-- latin small letter i with grave,
+ U+00EC ISOlat1 -->
+<!ENTITY iacute "í"> <!-- latin small letter i with acute,
+ U+00ED ISOlat1 -->
+<!ENTITY icirc "î"> <!-- latin small letter i with circumflex,
+ U+00EE ISOlat1 -->
+<!ENTITY iuml "ï"> <!-- latin small letter i with diaeresis,
+ U+00EF ISOlat1 -->
+<!ENTITY eth "ð"> <!-- latin small letter eth, U+00F0 ISOlat1 -->
+<!ENTITY ntilde "ñ"> <!-- latin small letter n with tilde,
+ U+00F1 ISOlat1 -->
+<!ENTITY ograve "ò"> <!-- latin small letter o with grave,
+ U+00F2 ISOlat1 -->
+<!ENTITY oacute "ó"> <!-- latin small letter o with acute,
+ U+00F3 ISOlat1 -->
+<!ENTITY ocirc "ô"> <!-- latin small letter o with circumflex,
+ U+00F4 ISOlat1 -->
+<!ENTITY otilde "õ"> <!-- latin small letter o with tilde,
+ U+00F5 ISOlat1 -->
+<!ENTITY ouml "ö"> <!-- latin small letter o with diaeresis,
+ U+00F6 ISOlat1 -->
+<!ENTITY divide "÷"> <!-- division sign, U+00F7 ISOnum -->
+<!ENTITY oslash "ø"> <!-- latin small letter o with stroke,
+ = latin small letter o slash,
+ U+00F8 ISOlat1 -->
+<!ENTITY ugrave "ù"> <!-- latin small letter u with grave,
+ U+00F9 ISOlat1 -->
+<!ENTITY uacute "ú"> <!-- latin small letter u with acute,
+ U+00FA ISOlat1 -->
+<!ENTITY ucirc "û"> <!-- latin small letter u with circumflex,
+ U+00FB ISOlat1 -->
+<!ENTITY uuml "ü"> <!-- latin small letter u with diaeresis,
+ U+00FC ISOlat1 -->
+<!ENTITY yacute "ý"> <!-- latin small letter y with acute,
+ U+00FD ISOlat1 -->
+<!ENTITY thorn "þ"> <!-- latin small letter thorn,
+ U+00FE ISOlat1 -->
+<!ENTITY yuml "ÿ"> <!-- latin small letter y with diaeresis,
+ U+00FF ISOlat1 -->
diff --git a/src/xhtml1/xhtml-special.ent b/src/xhtml1/xhtml-special.ent
new file mode 100644
index 0000000..ca358b2
--- /dev/null
+++ b/src/xhtml1/xhtml-special.ent
@@ -0,0 +1,80 @@
+<!-- Special characters for XHTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent">
+ %HTMLspecial;
+-->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode names.
+-->
+
+<!-- C0 Controls and Basic Latin -->
+<!ENTITY quot """> <!-- quotation mark, U+0022 ISOnum -->
+<!ENTITY amp "&"> <!-- ampersand, U+0026 ISOnum -->
+<!ENTITY lt "<"> <!-- less-than sign, U+003C ISOnum -->
+<!ENTITY gt ">"> <!-- greater-than sign, U+003E ISOnum -->
+<!ENTITY apos "'"> <!-- apostrophe = APL quote, U+0027 ISOnum -->
+
+<!-- Latin Extended-A -->
+<!ENTITY OElig "Œ"> <!-- latin capital ligature OE,
+ U+0152 ISOlat2 -->
+<!ENTITY oelig "œ"> <!-- latin small ligature oe, U+0153 ISOlat2 -->
+<!-- ligature is a misnomer, this is a separate character in some languages -->
+<!ENTITY Scaron "Š"> <!-- latin capital letter S with caron,
+ U+0160 ISOlat2 -->
+<!ENTITY scaron "š"> <!-- latin small letter s with caron,
+ U+0161 ISOlat2 -->
+<!ENTITY Yuml "Ÿ"> <!-- latin capital letter Y with diaeresis,
+ U+0178 ISOlat2 -->
+
+<!-- Spacing Modifier Letters -->
+<!ENTITY circ "ˆ"> <!-- modifier letter circumflex accent,
+ U+02C6 ISOpub -->
+<!ENTITY tilde "˜"> <!-- small tilde, U+02DC ISOdia -->
+
+<!-- General Punctuation -->
+<!ENTITY ensp " "> <!-- en space, U+2002 ISOpub -->
+<!ENTITY emsp " "> <!-- em space, U+2003 ISOpub -->
+<!ENTITY thinsp " "> <!-- thin space, U+2009 ISOpub -->
+<!ENTITY zwnj ""> <!-- zero width non-joiner,
+ U+200C NEW RFC 2070 -->
+<!ENTITY zwj ""> <!-- zero width joiner, U+200D NEW RFC 2070 -->
+<!ENTITY lrm ""> <!-- left-to-right mark, U+200E NEW RFC 2070 -->
+<!ENTITY rlm ""> <!-- right-to-left mark, U+200F NEW RFC 2070 -->
+<!ENTITY ndash "–"> <!-- en dash, U+2013 ISOpub -->
+<!ENTITY mdash "—"> <!-- em dash, U+2014 ISOpub -->
+<!ENTITY lsquo "‘"> <!-- left single quotation mark,
+ U+2018 ISOnum -->
+<!ENTITY rsquo "’"> <!-- right single quotation mark,
+ U+2019 ISOnum -->
+<!ENTITY sbquo "‚"> <!-- single low-9 quotation mark, U+201A NEW -->
+<!ENTITY ldquo "“"> <!-- left double quotation mark,
+ U+201C ISOnum -->
+<!ENTITY rdquo "”"> <!-- right double quotation mark,
+ U+201D ISOnum -->
+<!ENTITY bdquo "„"> <!-- double low-9 quotation mark, U+201E NEW -->
+<!ENTITY dagger "†"> <!-- dagger, U+2020 ISOpub -->
+<!ENTITY Dagger "‡"> <!-- double dagger, U+2021 ISOpub -->
+<!ENTITY permil "‰"> <!-- per mille sign, U+2030 ISOtech -->
+<!ENTITY lsaquo "‹"> <!-- single left-pointing angle quotation mark,
+ U+2039 ISO proposed -->
+<!-- lsaquo is proposed but not yet ISO standardized -->
+<!ENTITY rsaquo "›"> <!-- single right-pointing angle quotation mark,
+ U+203A ISO proposed -->
+<!-- rsaquo is proposed but not yet ISO standardized -->
+
+<!-- Currency Symbols -->
+<!ENTITY euro "€"> <!-- euro sign, U+20AC NEW -->
diff --git a/src/xhtml1/xhtml-symbol.ent b/src/xhtml1/xhtml-symbol.ent
new file mode 100644
index 0000000..63c2abf
--- /dev/null
+++ b/src/xhtml1/xhtml-symbol.ent
@@ -0,0 +1,237 @@
+<!-- Mathematical, Greek and Symbolic characters for XHTML -->
+
+<!-- Character entity set. Typical invocation:
+ <!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent">
+ %HTMLsymbol;
+-->
+
+<!-- Portions (C) International Organization for Standardization 1986:
+ Permission to copy in any form is granted for use with
+ conforming SGML systems and applications as defined in
+ ISO 8879, provided this notice is included in all copies.
+-->
+
+<!-- Relevant ISO entity set is given unless names are newly introduced.
+ New names (i.e., not in ISO 8879 list) do not clash with any
+ existing ISO 8879 entity names. ISO 10646 character numbers
+ are given for each character, in hex. values are decimal
+ conversions of the ISO 10646 values and refer to the document
+ character set. Names are Unicode names.
+-->
+
+<!-- Latin Extended-B -->
+<!ENTITY fnof "ƒ"> <!-- latin small letter f with hook = function
+ = florin, U+0192 ISOtech -->
+
+<!-- Greek -->
+<!ENTITY Alpha "Α"> <!-- greek capital letter alpha, U+0391 -->
+<!ENTITY Beta "Β"> <!-- greek capital letter beta, U+0392 -->
+<!ENTITY Gamma "Γ"> <!-- greek capital letter gamma,
+ U+0393 ISOgrk3 -->
+<!ENTITY Delta "Δ"> <!-- greek capital letter delta,
+ U+0394 ISOgrk3 -->
+<!ENTITY Epsilon "Ε"> <!-- greek capital letter epsilon, U+0395 -->
+<!ENTITY Zeta "Ζ"> <!-- greek capital letter zeta, U+0396 -->
+<!ENTITY Eta "Η"> <!-- greek capital letter eta, U+0397 -->
+<!ENTITY Theta "Θ"> <!-- greek capital letter theta,
+ U+0398 ISOgrk3 -->
+<!ENTITY Iota "Ι"> <!-- greek capital letter iota, U+0399 -->
+<!ENTITY Kappa "Κ"> <!-- greek capital letter kappa, U+039A -->
+<!ENTITY Lambda "Λ"> <!-- greek capital letter lamda,
+ U+039B ISOgrk3 -->
+<!ENTITY Mu "Μ"> <!-- greek capital letter mu, U+039C -->
+<!ENTITY Nu "Ν"> <!-- greek capital letter nu, U+039D -->
+<!ENTITY Xi "Ξ"> <!-- greek capital letter xi, U+039E ISOgrk3 -->
+<!ENTITY Omicron "Ο"> <!-- greek capital letter omicron, U+039F -->
+<!ENTITY Pi "Π"> <!-- greek capital letter pi, U+03A0 ISOgrk3 -->
+<!ENTITY Rho "Ρ"> <!-- greek capital letter rho, U+03A1 -->
+<!-- there is no Sigmaf, and no U+03A2 character either -->
+<!ENTITY Sigma "Σ"> <!-- greek capital letter sigma,
+ U+03A3 ISOgrk3 -->
+<!ENTITY Tau "Τ"> <!-- greek capital letter tau, U+03A4 -->
+<!ENTITY Upsilon "Υ"> <!-- greek capital letter upsilon,
+ U+03A5 ISOgrk3 -->
+<!ENTITY Phi "Φ"> <!-- greek capital letter phi,
+ U+03A6 ISOgrk3 -->
+<!ENTITY Chi "Χ"> <!-- greek capital letter chi, U+03A7 -->
+<!ENTITY Psi "Ψ"> <!-- greek capital letter psi,
+ U+03A8 ISOgrk3 -->
+<!ENTITY Omega "Ω"> <!-- greek capital letter omega,
+ U+03A9 ISOgrk3 -->
+
+<!ENTITY alpha "α"> <!-- greek small letter alpha,
+ U+03B1 ISOgrk3 -->
+<!ENTITY beta "β"> <!-- greek small letter beta, U+03B2 ISOgrk3 -->
+<!ENTITY gamma "γ"> <!-- greek small letter gamma,
+ U+03B3 ISOgrk3 -->
+<!ENTITY delta "δ"> <!-- greek small letter delta,
+ U+03B4 ISOgrk3 -->
+<!ENTITY epsilon "ε"> <!-- greek small letter epsilon,
+ U+03B5 ISOgrk3 -->
+<!ENTITY zeta "ζ"> <!-- greek small letter zeta, U+03B6 ISOgrk3 -->
+<!ENTITY eta "η"> <!-- greek small letter eta, U+03B7 ISOgrk3 -->
+<!ENTITY theta "θ"> <!-- greek small letter theta,
+ U+03B8 ISOgrk3 -->
+<!ENTITY iota "ι"> <!-- greek small letter iota, U+03B9 ISOgrk3 -->
+<!ENTITY kappa "κ"> <!-- greek small letter kappa,
+ U+03BA ISOgrk3 -->
+<!ENTITY lambda "λ"> <!-- greek small letter lamda,
+ U+03BB ISOgrk3 -->
+<!ENTITY mu "μ"> <!-- greek small letter mu, U+03BC ISOgrk3 -->
+<!ENTITY nu "ν"> <!-- greek small letter nu, U+03BD ISOgrk3 -->
+<!ENTITY xi "ξ"> <!-- greek small letter xi, U+03BE ISOgrk3 -->
+<!ENTITY omicron "ο"> <!-- greek small letter omicron, U+03BF NEW -->
+<!ENTITY pi "π"> <!-- greek small letter pi, U+03C0 ISOgrk3 -->
+<!ENTITY rho "ρ"> <!-- greek small letter rho, U+03C1 ISOgrk3 -->
+<!ENTITY sigmaf "ς"> <!-- greek small letter final sigma,
+ U+03C2 ISOgrk3 -->
+<!ENTITY sigma "σ"> <!-- greek small letter sigma,
+ U+03C3 ISOgrk3 -->
+<!ENTITY tau "τ"> <!-- greek small letter tau, U+03C4 ISOgrk3 -->
+<!ENTITY upsilon "υ"> <!-- greek small letter upsilon,
+ U+03C5 ISOgrk3 -->
+<!ENTITY phi "φ"> <!-- greek small letter phi, U+03C6 ISOgrk3 -->
+<!ENTITY chi "χ"> <!-- greek small letter chi, U+03C7 ISOgrk3 -->
+<!ENTITY psi "ψ"> <!-- greek small letter psi, U+03C8 ISOgrk3 -->
+<!ENTITY omega "ω"> <!-- greek small letter omega,
+ U+03C9 ISOgrk3 -->
+<!ENTITY thetasym "ϑ"> <!-- greek theta symbol,
+ U+03D1 NEW -->
+<!ENTITY upsih "ϒ"> <!-- greek upsilon with hook symbol,
+ U+03D2 NEW -->
+<!ENTITY piv "ϖ"> <!-- greek pi symbol, U+03D6 ISOgrk3 -->
+
+<!-- General Punctuation -->
+<!ENTITY bull "•"> <!-- bullet = black small circle,
+ U+2022 ISOpub -->
+<!-- bullet is NOT the same as bullet operator, U+2219 -->
+<!ENTITY hellip "…"> <!-- horizontal ellipsis = three dot leader,
+ U+2026 ISOpub -->
+<!ENTITY prime "′"> <!-- prime = minutes = feet, U+2032 ISOtech -->
+<!ENTITY Prime "″"> <!-- double prime = seconds = inches,
+ U+2033 ISOtech -->
+<!ENTITY oline "‾"> <!-- overline = spacing overscore,
+ U+203E NEW -->
+<!ENTITY frasl "⁄"> <!-- fraction slash, U+2044 NEW -->
+
+<!-- Letterlike Symbols -->
+<!ENTITY weierp "℘"> <!-- script capital P = power set
+ = Weierstrass p, U+2118 ISOamso -->
+<!ENTITY image "ℑ"> <!-- black-letter capital I = imaginary part,
+ U+2111 ISOamso -->
+<!ENTITY real "ℜ"> <!-- black-letter capital R = real part symbol,
+ U+211C ISOamso -->
+<!ENTITY trade "™"> <!-- trade mark sign, U+2122 ISOnum -->
+<!ENTITY alefsym "ℵ"> <!-- alef symbol = first transfinite cardinal,
+ U+2135 NEW -->
+<!-- alef symbol is NOT the same as hebrew letter alef,
+ U+05D0 although the same glyph could be used to depict both characters -->
+
+<!-- Arrows -->
+<!ENTITY larr "←"> <!-- leftwards arrow, U+2190 ISOnum -->
+<!ENTITY uarr "↑"> <!-- upwards arrow, U+2191 ISOnum-->
+<!ENTITY rarr "→"> <!-- rightwards arrow, U+2192 ISOnum -->
+<!ENTITY darr "↓"> <!-- downwards arrow, U+2193 ISOnum -->
+<!ENTITY harr "↔"> <!-- left right arrow, U+2194 ISOamsa -->
+<!ENTITY crarr "↵"> <!-- downwards arrow with corner leftwards
+ = carriage return, U+21B5 NEW -->
+<!ENTITY lArr "⇐"> <!-- leftwards double arrow, U+21D0 ISOtech -->
+<!-- Unicode does not say that lArr is the same as the 'is implied by' arrow
+ but also does not have any other character for that function. So lArr can
+ be used for 'is implied by' as ISOtech suggests -->
+<!ENTITY uArr "⇑"> <!-- upwards double arrow, U+21D1 ISOamsa -->
+<!ENTITY rArr "⇒"> <!-- rightwards double arrow,
+ U+21D2 ISOtech -->
+<!-- Unicode does not say this is the 'implies' character but does not have
+ another character with this function so rArr can be used for 'implies'
+ as ISOtech suggests -->
+<!ENTITY dArr "⇓"> <!-- downwards double arrow, U+21D3 ISOamsa -->
+<!ENTITY hArr "⇔"> <!-- left right double arrow,
+ U+21D4 ISOamsa -->
+
+<!-- Mathematical Operators -->
+<!ENTITY forall "∀"> <!-- for all, U+2200 ISOtech -->
+<!ENTITY part "∂"> <!-- partial differential, U+2202 ISOtech -->
+<!ENTITY exist "∃"> <!-- there exists, U+2203 ISOtech -->
+<!ENTITY empty "∅"> <!-- empty set = null set, U+2205 ISOamso -->
+<!ENTITY nabla "∇"> <!-- nabla = backward difference,
+ U+2207 ISOtech -->
+<!ENTITY isin "∈"> <!-- element of, U+2208 ISOtech -->
+<!ENTITY notin "∉"> <!-- not an element of, U+2209 ISOtech -->
+<!ENTITY ni "∋"> <!-- contains as member, U+220B ISOtech -->
+<!ENTITY prod "∏"> <!-- n-ary product = product sign,
+ U+220F ISOamsb -->
+<!-- prod is NOT the same character as U+03A0 'greek capital letter pi' though
+ the same glyph might be used for both -->
+<!ENTITY sum "∑"> <!-- n-ary summation, U+2211 ISOamsb -->
+<!-- sum is NOT the same character as U+03A3 'greek capital letter sigma'
+ though the same glyph might be used for both -->
+<!ENTITY minus "−"> <!-- minus sign, U+2212 ISOtech -->
+<!ENTITY lowast "∗"> <!-- asterisk operator, U+2217 ISOtech -->
+<!ENTITY radic "√"> <!-- square root = radical sign,
+ U+221A ISOtech -->
+<!ENTITY prop "∝"> <!-- proportional to, U+221D ISOtech -->
+<!ENTITY infin "∞"> <!-- infinity, U+221E ISOtech -->
+<!ENTITY ang "∠"> <!-- angle, U+2220 ISOamso -->
+<!ENTITY and "∧"> <!-- logical and = wedge, U+2227 ISOtech -->
+<!ENTITY or "∨"> <!-- logical or = vee, U+2228 ISOtech -->
+<!ENTITY cap "∩"> <!-- intersection = cap, U+2229 ISOtech -->
+<!ENTITY cup "∪"> <!-- union = cup, U+222A ISOtech -->
+<!ENTITY int "∫"> <!-- integral, U+222B ISOtech -->
+<!ENTITY there4 "∴"> <!-- therefore, U+2234 ISOtech -->
+<!ENTITY sim "∼"> <!-- tilde operator = varies with = similar to,
+ U+223C ISOtech -->
+<!-- tilde operator is NOT the same character as the tilde, U+007E,
+ although the same glyph might be used to represent both -->
+<!ENTITY cong "≅"> <!-- approximately equal to, U+2245 ISOtech -->
+<!ENTITY asymp "≈"> <!-- almost equal to = asymptotic to,
+ U+2248 ISOamsr -->
+<!ENTITY ne "≠"> <!-- not equal to, U+2260 ISOtech -->
+<!ENTITY equiv "≡"> <!-- identical to, U+2261 ISOtech -->
+<!ENTITY le "≤"> <!-- less-than or equal to, U+2264 ISOtech -->
+<!ENTITY ge "≥"> <!-- greater-than or equal to,
+ U+2265 ISOtech -->
+<!ENTITY sub "⊂"> <!-- subset of, U+2282 ISOtech -->
+<!ENTITY sup "⊃"> <!-- superset of, U+2283 ISOtech -->
+<!ENTITY nsub "⊄"> <!-- not a subset of, U+2284 ISOamsn -->
+<!ENTITY sube "⊆"> <!-- subset of or equal to, U+2286 ISOtech -->
+<!ENTITY supe "⊇"> <!-- superset of or equal to,
+ U+2287 ISOtech -->
+<!ENTITY oplus "⊕"> <!-- circled plus = direct sum,
+ U+2295 ISOamsb -->
+<!ENTITY otimes "⊗"> <!-- circled times = vector product,
+ U+2297 ISOamsb -->
+<!ENTITY perp "⊥"> <!-- up tack = orthogonal to = perpendicular,
+ U+22A5 ISOtech -->
+<!ENTITY sdot "⋅"> <!-- dot operator, U+22C5 ISOamsb -->
+<!-- dot operator is NOT the same character as U+00B7 middle dot -->
+
+<!-- Miscellaneous Technical -->
+<!ENTITY lceil "⌈"> <!-- left ceiling = APL upstile,
+ U+2308 ISOamsc -->
+<!ENTITY rceil "⌉"> <!-- right ceiling, U+2309 ISOamsc -->
+<!ENTITY lfloor "⌊"> <!-- left floor = APL downstile,
+ U+230A ISOamsc -->
+<!ENTITY rfloor "⌋"> <!-- right floor, U+230B ISOamsc -->
+<!ENTITY lang "〈"> <!-- left-pointing angle bracket = bra,
+ U+2329 ISOtech -->
+<!-- lang is NOT the same character as U+003C 'less than sign'
+ or U+2039 'single left-pointing angle quotation mark' -->
+<!ENTITY rang "〉"> <!-- right-pointing angle bracket = ket,
+ U+232A ISOtech -->
+<!-- rang is NOT the same character as U+003E 'greater than sign'
+ or U+203A 'single right-pointing angle quotation mark' -->
+
+<!-- Geometric Shapes -->
+<!ENTITY loz "◊"> <!-- lozenge, U+25CA ISOpub -->
+
+<!-- Miscellaneous Symbols -->
+<!ENTITY spades "♠"> <!-- black spade suit, U+2660 ISOpub -->
+<!-- black here seems to mean filled as opposed to hollow -->
+<!ENTITY clubs "♣"> <!-- black club suit = shamrock,
+ U+2663 ISOpub -->
+<!ENTITY hearts "♥"> <!-- black heart suit = valentine,
+ U+2665 ISOpub -->
+<!ENTITY diams "♦"> <!-- black diamond suit, U+2666 ISOpub -->
diff --git a/src/xhtml1/xhtml1-frameset.dtd b/src/xhtml1/xhtml1-frameset.dtd
new file mode 100644
index 0000000..d128f2e
--- /dev/null
+++ b/src/xhtml1/xhtml1-frameset.dtd
@@ -0,0 +1,1235 @@
+<!--
+ Extensible HTML version 1.0 Frameset DTD
+
+ This is the same as HTML 4 Frameset except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
+
+ $Revision: 1.2 $
+ $Date: 2002/08/01 18:37:55 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % FrameTarget "NMTOKEN">
+ <!-- render in this frame -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % MultiLengths "CDATA">
+ <!-- comma-separated list of MultiLength -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!-- a color using sRGB: #RRGGBB as Hex values -->
+<!ENTITY % Color "CDATA">
+
+<!-- There are also 16 widely known color names with their sRGB values:
+
+ Black = #000000 Green = #008000
+ Silver = #C0C0C0 Lime = #00FF00
+ Gray = #808080 Olive = #808000
+ White = #FFFFFF Yellow = #FFFF00
+ Maroon = #800000 Navy = #000080
+ Red = #FF0000 Blue = #0000FF
+ Purple = #800080 Teal = #008080
+ Fuchsia= #FF00FF Aqua = #00FFFF
+-->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!-- text alignment for p, div, h1-h6. The default is
+ align="left" for ltr headings, "right" for rtl -->
+
+<!ENTITY % TextAlign "align (left|center|right|justify) #IMPLIED">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.extra
+ "object | applet | img | map | iframe">
+
+<!ENTITY % special.basic
+ "br | span | bdo">
+
+<!ENTITY % special
+ "%special.basic; | %special.extra;">
+
+<!ENTITY % fontstyle.extra "big | small | font | basefont">
+
+<!ENTITY % fontstyle.basic "tt | i | b | u
+ | s | strike ">
+
+<!ENTITY % fontstyle "%fontstyle.basic; | %fontstyle.extra;">
+
+<!ENTITY % phrase.extra "sub | sup">
+<!ENTITY % phrase.basic "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym">
+
+<!ENTITY % phrase "%phrase.basic; | %phrase.extra;">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl | menu | dir">
+<!ENTITY % blocktext "pre | hr | blockquote | address | center">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | isindex | fieldset | table">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding img, object, applet, big, small,
+ sub, sup, font, or basefont -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %special.basic; | %fontstyle.basic; | %phrase.basic; |
+ %inline.forms; | %misc.inline;)*">
+
+
+<!-- form uses %Flow; excluding form -->
+
+<!ENTITY % form.content "(#PCDATA | %block; | %inline; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form, form controls, iframe -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | br | span | bdo | object | applet | img | map |
+ %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, frameset)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object|isindex)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ id ID #IMPLIED
+ href %URI; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ language CDATA #IMPLIED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Flow;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--======================= Frames =======================================-->
+
+<!-- only one noframes element permitted per document -->
+
+<!ELEMENT frameset (frameset|frame|noframes)*>
+<!ATTLIST frameset
+ %coreattrs;
+ rows %MultiLengths; #IMPLIED
+ cols %MultiLengths; #IMPLIED
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ >
+
+<!-- reserved frame names start with "_" otherwise starts with letter -->
+
+<!-- tiled window within frameset -->
+
+<!ELEMENT frame EMPTY>
+<!ATTLIST frame
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ noresize (noresize) #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ >
+
+<!-- inline subwindow -->
+
+<!ELEMENT iframe %Flow;>
+<!ATTLIST iframe
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ align %ImgAlign; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!-- alternate content container for non frame-based rendering -->
+
+<!ELEMENT noframes (body)>
+<!ATTLIST noframes
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Flow;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ background %URI; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ text %Color; #IMPLIED
+ link %Color; #IMPLIED
+ vlink %Color; #IMPLIED
+ alink %Color; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list bullet styles -->
+
+<!ENTITY % ULStyle "(disc|square|circle)">
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ type %ULStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ >
+
+<!-- Ordered list numbering style
+
+ 1 arabic numbers 1, 2, 3, ...
+ a lower alpha a, b, c, ...
+ A upper alpha A, B, C, ...
+ i lower roman i, ii, iii, ...
+ I upper roman I, II, III, ...
+
+ The style is applied to the sequence number which by default
+ is reset to 1 for the first list item in an ordered list.
+-->
+<!ENTITY % OLStyle "CDATA">
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ type %OLStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ start %Number; #IMPLIED
+ >
+
+<!-- single column list (DEPRECATED) -->
+<!ELEMENT menu (li)+>
+<!ATTLIST menu
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- multiple column list (DEPRECATED) -->
+<!ELEMENT dir (li)+>
+<!ATTLIST dir
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- LIStyle is constrained to: "(%ULStyle;|%OLStyle;)" -->
+<!ENTITY % LIStyle "CDATA">
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ type %LIStyle; #IMPLIED
+ value %Number; #IMPLIED
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address (#PCDATA | %inline; | %misc.inline; | p)*>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ align (left|center|right) #IMPLIED
+ noshade (noshade) #IMPLIED
+ size %Pixels; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding
+ "img|object|applet|big|small|sub|sup|font|basefont" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ width %Number; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Flow;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Text alignment ===================================-->
+
+<!-- center content -->
+<!ELEMENT center %Flow;>
+<!ATTLIST center
+ %attrs;
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ clear (left|all|right|none) "none"
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!ELEMENT u %Inline;> <!-- underline -->
+<!ATTLIST u %attrs;>
+
+<!ELEMENT s %Inline;> <!-- strike-through -->
+<!ATTLIST s %attrs;>
+
+<!ELEMENT strike %Inline;> <!-- strike-through -->
+<!ATTLIST strike %attrs;>
+
+<!ELEMENT basefont EMPTY> <!-- base font size -->
+<!ATTLIST basefont
+ id ID #IMPLIED
+ size CDATA #REQUIRED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!ELEMENT font %Inline;> <!-- local change to font -->
+<!ATTLIST font
+ %coreattrs;
+ %i18n;
+ size CDATA #IMPLIED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form |%inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #REQUIRED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Java applet ==================================-->
+<!--
+ One of code or object attributes must be present.
+ Place param elements before other content.
+-->
+<!ELEMENT applet (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST applet
+ %coreattrs;
+ codebase %URI; #IMPLIED
+ archive CDATA #IMPLIED
+ code CDATA #IMPLIED
+ object CDATA #IMPLIED
+ alt %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ width %Length; #REQUIRED
+ height %Length; #REQUIRED
+ align %ImgAlign; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ name NMTOKEN #IMPLIED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--================ Forms ===============================================-->
+
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ name NMTOKEN #IMPLIED
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ align %LAlign; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form, form controls, iframe
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!-- single-line text input control (DEPRECATED) -->
+<!ELEMENT isindex EMPTY>
+<!ATTLIST isindex
+ %coreattrs;
+ %i18n;
+ prompt %Text; #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=":"
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ align %TAlign; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST caption
+ %attrs;
+ align %CAlign; #IMPLIED
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ bgcolor %Color; #IMPLIED
+ >
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Pixels; #IMPLIED
+ height %Pixels; #IMPLIED
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Pixels; #IMPLIED
+ height %Pixels; #IMPLIED
+ >
+
diff --git a/src/xhtml1/xhtml1-strict.dtd b/src/xhtml1/xhtml1-strict.dtd
new file mode 100644
index 0000000..2927b9e
--- /dev/null
+++ b/src/xhtml1/xhtml1-strict.dtd
@@ -0,0 +1,978 @@
+<!--
+ Extensible HTML version 1.0 Strict DTD
+
+ This is the same as HTML 4 Strict except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+
+ $Revision: 1.1 $
+ $Date: 2002/08/01 13:56:03 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.pre
+ "br | span | bdo | map">
+
+
+<!ENTITY % special
+ "%special.pre; | object | img ">
+
+<!ENTITY % fontstyle "tt | i | b | big | small ">
+
+<!ENTITY % phrase "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym | sub | sup ">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl">
+<!ENTITY % blocktext "pre | hr | blockquote | address">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | fieldset | table">
+
+<!ENTITY % Block "(%block; | form | %misc;)*">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding big, small, sup or sup -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %fontstyle; | %phrase; | %special.pre; | %misc.inline;
+ | %inline.forms;)*">
+
+<!-- form uses %Block; excluding form -->
+
+<!ENTITY % form.content "(%block; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form and form controls -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | %special; | %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, body)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ href %URI; #REQUIRED
+ id ID #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Block;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Block;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ >
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ >
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address %Inline;>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding "img|object|big|small|sub|sup" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Block;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+ Note that in this DTD there is no name attribute. That
+ is only available in the transitional and frameset DTD.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ >
+
+<!--================ Forms ===============================================-->
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form and form controls
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=':'
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ >
+
+<!ATTLIST caption
+ %attrs;
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ >
+
diff --git a/src/xhtml1/xhtml1-transitional.dtd b/src/xhtml1/xhtml1-transitional.dtd
new file mode 100644
index 0000000..628f27a
--- /dev/null
+++ b/src/xhtml1/xhtml1-transitional.dtd
@@ -0,0 +1,1201 @@
+<!--
+ Extensible HTML version 1.0 Transitional DTD
+
+ This is the same as HTML 4 Transitional except for
+ changes due to the differences between XML and SGML.
+
+ Namespace = http://www.w3.org/1999/xhtml
+
+ For further information, see: http://www.w3.org/TR/xhtml1
+
+ Copyright (c) 1998-2002 W3C (MIT, INRIA, Keio),
+ All Rights Reserved.
+
+ This DTD module is identified by the PUBLIC and SYSTEM identifiers:
+
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
+
+ $Revision: 1.2 $
+ $Date: 2002/08/01 18:37:55 $
+
+-->
+
+<!--================ Character mnemonic entities =========================-->
+
+<!ENTITY % HTMLlat1 PUBLIC
+ "-//W3C//ENTITIES Latin 1 for XHTML//EN"
+ "xhtml-lat1.ent">
+%HTMLlat1;
+
+<!ENTITY % HTMLsymbol PUBLIC
+ "-//W3C//ENTITIES Symbols for XHTML//EN"
+ "xhtml-symbol.ent">
+%HTMLsymbol;
+
+<!ENTITY % HTMLspecial PUBLIC
+ "-//W3C//ENTITIES Special for XHTML//EN"
+ "xhtml-special.ent">
+%HTMLspecial;
+
+<!--================== Imported Names ====================================-->
+
+<!ENTITY % ContentType "CDATA">
+ <!-- media type, as per [RFC2045] -->
+
+<!ENTITY % ContentTypes "CDATA">
+ <!-- comma-separated list of media types, as per [RFC2045] -->
+
+<!ENTITY % Charset "CDATA">
+ <!-- a character encoding, as per [RFC2045] -->
+
+<!ENTITY % Charsets "CDATA">
+ <!-- a space separated list of character encodings, as per [RFC2045] -->
+
+<!ENTITY % LanguageCode "NMTOKEN">
+ <!-- a language code, as per [RFC3066] -->
+
+<!ENTITY % Character "CDATA">
+ <!-- a single character, as per section 2.2 of [XML] -->
+
+<!ENTITY % Number "CDATA">
+ <!-- one or more digits -->
+
+<!ENTITY % LinkTypes "CDATA">
+ <!-- space-separated list of link types -->
+
+<!ENTITY % MediaDesc "CDATA">
+ <!-- single or comma-separated list of media descriptors -->
+
+<!ENTITY % URI "CDATA">
+ <!-- a Uniform Resource Identifier, see [RFC2396] -->
+
+<!ENTITY % UriList "CDATA">
+ <!-- a space separated list of Uniform Resource Identifiers -->
+
+<!ENTITY % Datetime "CDATA">
+ <!-- date and time information. ISO date format -->
+
+<!ENTITY % Script "CDATA">
+ <!-- script expression -->
+
+<!ENTITY % StyleSheet "CDATA">
+ <!-- style sheet data -->
+
+<!ENTITY % Text "CDATA">
+ <!-- used for titles etc. -->
+
+<!ENTITY % FrameTarget "NMTOKEN">
+ <!-- render in this frame -->
+
+<!ENTITY % Length "CDATA">
+ <!-- nn for pixels or nn% for percentage length -->
+
+<!ENTITY % MultiLength "CDATA">
+ <!-- pixel, percentage, or relative -->
+
+<!ENTITY % Pixels "CDATA">
+ <!-- integer representing length in pixels -->
+
+<!-- these are used for image maps -->
+
+<!ENTITY % Shape "(rect|circle|poly|default)">
+
+<!ENTITY % Coords "CDATA">
+ <!-- comma separated list of lengths -->
+
+<!-- used for object, applet, img, input and iframe -->
+<!ENTITY % ImgAlign "(top|middle|bottom|left|right)">
+
+<!-- a color using sRGB: #RRGGBB as Hex values -->
+<!ENTITY % Color "CDATA">
+
+<!-- There are also 16 widely known color names with their sRGB values:
+
+ Black = #000000 Green = #008000
+ Silver = #C0C0C0 Lime = #00FF00
+ Gray = #808080 Olive = #808000
+ White = #FFFFFF Yellow = #FFFF00
+ Maroon = #800000 Navy = #000080
+ Red = #FF0000 Blue = #0000FF
+ Purple = #800080 Teal = #008080
+ Fuchsia= #FF00FF Aqua = #00FFFF
+-->
+
+<!--=================== Generic Attributes ===============================-->
+
+<!-- core attributes common to most elements
+ id document-wide unique id
+ class space separated list of classes
+ style associated style info
+ title advisory title/amplification
+-->
+<!ENTITY % coreattrs
+ "id ID #IMPLIED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED"
+ >
+
+<!-- internationalization attributes
+ lang language code (backwards compatible)
+ xml:lang language code (as per XML 1.0 spec)
+ dir direction for weak/neutral text
+-->
+<!ENTITY % i18n
+ "lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #IMPLIED"
+ >
+
+<!-- attributes for common UI events
+ onclick a pointer button was clicked
+ ondblclick a pointer button was double clicked
+ onmousedown a pointer button was pressed down
+ onmouseup a pointer button was released
+ onmousemove a pointer was moved onto the element
+ onmouseout a pointer was moved away from the element
+ onkeypress a key was pressed and released
+ onkeydown a key was pressed down
+ onkeyup a key was released
+-->
+<!ENTITY % events
+ "onclick %Script; #IMPLIED
+ ondblclick %Script; #IMPLIED
+ onmousedown %Script; #IMPLIED
+ onmouseup %Script; #IMPLIED
+ onmouseover %Script; #IMPLIED
+ onmousemove %Script; #IMPLIED
+ onmouseout %Script; #IMPLIED
+ onkeypress %Script; #IMPLIED
+ onkeydown %Script; #IMPLIED
+ onkeyup %Script; #IMPLIED"
+ >
+
+<!-- attributes for elements that can get the focus
+ accesskey accessibility key character
+ tabindex position in tabbing order
+ onfocus the element got the focus
+ onblur the element lost the focus
+-->
+<!ENTITY % focus
+ "accesskey %Character; #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED"
+ >
+
+<!ENTITY % attrs "%coreattrs; %i18n; %events;">
+
+<!-- text alignment for p, div, h1-h6. The default is
+ align="left" for ltr headings, "right" for rtl -->
+
+<!ENTITY % TextAlign "align (left|center|right|justify) #IMPLIED">
+
+<!--=================== Text Elements ====================================-->
+
+<!ENTITY % special.extra
+ "object | applet | img | map | iframe">
+
+<!ENTITY % special.basic
+ "br | span | bdo">
+
+<!ENTITY % special
+ "%special.basic; | %special.extra;">
+
+<!ENTITY % fontstyle.extra "big | small | font | basefont">
+
+<!ENTITY % fontstyle.basic "tt | i | b | u
+ | s | strike ">
+
+<!ENTITY % fontstyle "%fontstyle.basic; | %fontstyle.extra;">
+
+<!ENTITY % phrase.extra "sub | sup">
+<!ENTITY % phrase.basic "em | strong | dfn | code | q |
+ samp | kbd | var | cite | abbr | acronym">
+
+<!ENTITY % phrase "%phrase.basic; | %phrase.extra;">
+
+<!ENTITY % inline.forms "input | select | textarea | label | button">
+
+<!-- these can occur at block or inline level -->
+<!ENTITY % misc.inline "ins | del | script">
+
+<!-- these can only occur at block level -->
+<!ENTITY % misc "noscript | %misc.inline;">
+
+<!ENTITY % inline "a | %special; | %fontstyle; | %phrase; | %inline.forms;">
+
+<!-- %Inline; covers inline or "text-level" elements -->
+<!ENTITY % Inline "(#PCDATA | %inline; | %misc.inline;)*">
+
+<!--================== Block level elements ==============================-->
+
+<!ENTITY % heading "h1|h2|h3|h4|h5|h6">
+<!ENTITY % lists "ul | ol | dl | menu | dir">
+<!ENTITY % blocktext "pre | hr | blockquote | address | center | noframes">
+
+<!ENTITY % block
+ "p | %heading; | div | %lists; | %blocktext; | isindex |fieldset | table">
+
+<!-- %Flow; mixes block and inline and is used for list items etc. -->
+<!ENTITY % Flow "(#PCDATA | %block; | form | %inline; | %misc;)*">
+
+<!--================== Content models for exclusions =====================-->
+
+<!-- a elements use %Inline; excluding a -->
+
+<!ENTITY % a.content
+ "(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc.inline;)*">
+
+<!-- pre uses %Inline excluding img, object, applet, big, small,
+ font, or basefont -->
+
+<!ENTITY % pre.content
+ "(#PCDATA | a | %special.basic; | %fontstyle.basic; | %phrase.basic; |
+ %inline.forms; | %misc.inline;)*">
+
+<!-- form uses %Flow; excluding form -->
+
+<!ENTITY % form.content "(#PCDATA | %block; | %inline; | %misc;)*">
+
+<!-- button uses %Flow; but excludes a, form, form controls, iframe -->
+
+<!ENTITY % button.content
+ "(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
+ table | br | span | bdo | object | applet | img | map |
+ %fontstyle; | %phrase; | %misc;)*">
+
+<!--================ Document Structure ==================================-->
+
+<!-- the namespace URI designates the document profile -->
+
+<!ELEMENT html (head, body)>
+<!ATTLIST html
+ %i18n;
+ id ID #IMPLIED
+ xmlns %URI; #FIXED 'http://www.w3.org/1999/xhtml'
+ >
+
+<!--================ Document Head =======================================-->
+
+<!ENTITY % head.misc "(script|style|meta|link|object|isindex)*">
+
+<!-- content model is %head.misc; combined with a single
+ title and an optional base element in any order -->
+
+<!ELEMENT head (%head.misc;,
+ ((title, %head.misc;, (base, %head.misc;)?) |
+ (base, %head.misc;, (title, %head.misc;))))>
+
+<!ATTLIST head
+ %i18n;
+ id ID #IMPLIED
+ profile %URI; #IMPLIED
+ >
+
+<!-- The title element is not considered part of the flow of text.
+ It should be displayed, for example as the page header or
+ window title. Exactly one title is required per document.
+ -->
+<!ELEMENT title (#PCDATA)>
+<!ATTLIST title
+ %i18n;
+ id ID #IMPLIED
+ >
+
+<!-- document base URI -->
+
+<!ELEMENT base EMPTY>
+<!ATTLIST base
+ id ID #IMPLIED
+ href %URI; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- generic metainformation -->
+<!ELEMENT meta EMPTY>
+<!ATTLIST meta
+ %i18n;
+ id ID #IMPLIED
+ http-equiv CDATA #IMPLIED
+ name CDATA #IMPLIED
+ content CDATA #REQUIRED
+ scheme CDATA #IMPLIED
+ >
+
+<!--
+ Relationship values can be used in principle:
+
+ a) for document specific toolbars/menus when used
+ with the link element in document head e.g.
+ start, contents, previous, next, index, end, help
+ b) to link to a separate style sheet (rel="stylesheet")
+ c) to make a link to a script (rel="script")
+ d) by stylesheets to control how collections of
+ html nodes are rendered into printed documents
+ e) to make a link to a printable version of this document
+ e.g. a PostScript or PDF version (rel="alternate" media="print")
+-->
+
+<!ELEMENT link EMPTY>
+<!ATTLIST link
+ %attrs;
+ charset %Charset; #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ type %ContentType; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ media %MediaDesc; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!-- style info, which may include CDATA sections -->
+<!ELEMENT style (#PCDATA)>
+<!ATTLIST style
+ %i18n;
+ id ID #IMPLIED
+ type %ContentType; #REQUIRED
+ media %MediaDesc; #IMPLIED
+ title %Text; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- script statements, which may include CDATA sections -->
+<!ELEMENT script (#PCDATA)>
+<!ATTLIST script
+ id ID #IMPLIED
+ charset %Charset; #IMPLIED
+ type %ContentType; #REQUIRED
+ language CDATA #IMPLIED
+ src %URI; #IMPLIED
+ defer (defer) #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!-- alternate content container for non script-based rendering -->
+
+<!ELEMENT noscript %Flow;>
+<!ATTLIST noscript
+ %attrs;
+ >
+
+<!--======================= Frames =======================================-->
+
+<!-- inline subwindow -->
+
+<!ELEMENT iframe %Flow;>
+<!ATTLIST iframe
+ %coreattrs;
+ longdesc %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ src %URI; #IMPLIED
+ frameborder (1|0) "1"
+ marginwidth %Pixels; #IMPLIED
+ marginheight %Pixels; #IMPLIED
+ scrolling (yes|no|auto) "auto"
+ align %ImgAlign; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!-- alternate content container for non frame-based rendering -->
+
+<!ELEMENT noframes %Flow;>
+<!ATTLIST noframes
+ %attrs;
+ >
+
+<!--=================== Document Body ====================================-->
+
+<!ELEMENT body %Flow;>
+<!ATTLIST body
+ %attrs;
+ onload %Script; #IMPLIED
+ onunload %Script; #IMPLIED
+ background %URI; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ text %Color; #IMPLIED
+ link %Color; #IMPLIED
+ vlink %Color; #IMPLIED
+ alink %Color; #IMPLIED
+ >
+
+<!ELEMENT div %Flow;> <!-- generic language/style container -->
+<!ATTLIST div
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Paragraphs =======================================-->
+
+<!ELEMENT p %Inline;>
+<!ATTLIST p
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Headings =========================================-->
+
+<!--
+ There are six levels of headings from h1 (the most important)
+ to h6 (the least important).
+-->
+
+<!ELEMENT h1 %Inline;>
+<!ATTLIST h1
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h2 %Inline;>
+<!ATTLIST h2
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h3 %Inline;>
+<!ATTLIST h3
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h4 %Inline;>
+<!ATTLIST h4
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h5 %Inline;>
+<!ATTLIST h5
+ %attrs;
+ %TextAlign;
+ >
+
+<!ELEMENT h6 %Inline;>
+<!ATTLIST h6
+ %attrs;
+ %TextAlign;
+ >
+
+<!--=================== Lists ============================================-->
+
+<!-- Unordered list bullet styles -->
+
+<!ENTITY % ULStyle "(disc|square|circle)">
+
+<!-- Unordered list -->
+
+<!ELEMENT ul (li)+>
+<!ATTLIST ul
+ %attrs;
+ type %ULStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ >
+
+<!-- Ordered list numbering style
+
+ 1 arabic numbers 1, 2, 3, ...
+ a lower alpha a, b, c, ...
+ A upper alpha A, B, C, ...
+ i lower roman i, ii, iii, ...
+ I upper roman I, II, III, ...
+
+ The style is applied to the sequence number which by default
+ is reset to 1 for the first list item in an ordered list.
+-->
+<!ENTITY % OLStyle "CDATA">
+
+<!-- Ordered (numbered) list -->
+
+<!ELEMENT ol (li)+>
+<!ATTLIST ol
+ %attrs;
+ type %OLStyle; #IMPLIED
+ compact (compact) #IMPLIED
+ start %Number; #IMPLIED
+ >
+
+<!-- single column list (DEPRECATED) -->
+<!ELEMENT menu (li)+>
+<!ATTLIST menu
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- multiple column list (DEPRECATED) -->
+<!ELEMENT dir (li)+>
+<!ATTLIST dir
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!-- LIStyle is constrained to: "(%ULStyle;|%OLStyle;)" -->
+<!ENTITY % LIStyle "CDATA">
+
+<!-- list item -->
+
+<!ELEMENT li %Flow;>
+<!ATTLIST li
+ %attrs;
+ type %LIStyle; #IMPLIED
+ value %Number; #IMPLIED
+ >
+
+<!-- definition lists - dt for term, dd for its definition -->
+
+<!ELEMENT dl (dt|dd)+>
+<!ATTLIST dl
+ %attrs;
+ compact (compact) #IMPLIED
+ >
+
+<!ELEMENT dt %Inline;>
+<!ATTLIST dt
+ %attrs;
+ >
+
+<!ELEMENT dd %Flow;>
+<!ATTLIST dd
+ %attrs;
+ >
+
+<!--=================== Address ==========================================-->
+
+<!-- information on author -->
+
+<!ELEMENT address (#PCDATA | %inline; | %misc.inline; | p)*>
+<!ATTLIST address
+ %attrs;
+ >
+
+<!--=================== Horizontal Rule ==================================-->
+
+<!ELEMENT hr EMPTY>
+<!ATTLIST hr
+ %attrs;
+ align (left|center|right) #IMPLIED
+ noshade (noshade) #IMPLIED
+ size %Pixels; #IMPLIED
+ width %Length; #IMPLIED
+ >
+
+<!--=================== Preformatted Text ================================-->
+
+<!-- content is %Inline; excluding
+ "img|object|applet|big|small|sub|sup|font|basefont" -->
+
+<!ELEMENT pre %pre.content;>
+<!ATTLIST pre
+ %attrs;
+ width %Number; #IMPLIED
+ xml:space (preserve) #FIXED 'preserve'
+ >
+
+<!--=================== Block-like Quotes ================================-->
+
+<!ELEMENT blockquote %Flow;>
+<!ATTLIST blockquote
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!--=================== Text alignment ===================================-->
+
+<!-- center content -->
+<!ELEMENT center %Flow;>
+<!ATTLIST center
+ %attrs;
+ >
+
+<!--=================== Inserted/Deleted Text ============================-->
+
+<!--
+ ins/del are allowed in block and inline content, but its
+ inappropriate to include block content within an ins element
+ occurring in inline content.
+-->
+<!ELEMENT ins %Flow;>
+<!ATTLIST ins
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!ELEMENT del %Flow;>
+<!ATTLIST del
+ %attrs;
+ cite %URI; #IMPLIED
+ datetime %Datetime; #IMPLIED
+ >
+
+<!--================== The Anchor Element ================================-->
+
+<!-- content is %Inline; except that anchors shouldn't be nested -->
+
+<!ELEMENT a %a.content;>
+<!ATTLIST a
+ %attrs;
+ %focus;
+ charset %Charset; #IMPLIED
+ type %ContentType; #IMPLIED
+ name NMTOKEN #IMPLIED
+ href %URI; #IMPLIED
+ hreflang %LanguageCode; #IMPLIED
+ rel %LinkTypes; #IMPLIED
+ rev %LinkTypes; #IMPLIED
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--===================== Inline Elements ================================-->
+
+<!ELEMENT span %Inline;> <!-- generic language/style container -->
+<!ATTLIST span
+ %attrs;
+ >
+
+<!ELEMENT bdo %Inline;> <!-- I18N BiDi over-ride -->
+<!ATTLIST bdo
+ %coreattrs;
+ %events;
+ lang %LanguageCode; #IMPLIED
+ xml:lang %LanguageCode; #IMPLIED
+ dir (ltr|rtl) #REQUIRED
+ >
+
+<!ELEMENT br EMPTY> <!-- forced line break -->
+<!ATTLIST br
+ %coreattrs;
+ clear (left|all|right|none) "none"
+ >
+
+<!ELEMENT em %Inline;> <!-- emphasis -->
+<!ATTLIST em %attrs;>
+
+<!ELEMENT strong %Inline;> <!-- strong emphasis -->
+<!ATTLIST strong %attrs;>
+
+<!ELEMENT dfn %Inline;> <!-- definitional -->
+<!ATTLIST dfn %attrs;>
+
+<!ELEMENT code %Inline;> <!-- program code -->
+<!ATTLIST code %attrs;>
+
+<!ELEMENT samp %Inline;> <!-- sample -->
+<!ATTLIST samp %attrs;>
+
+<!ELEMENT kbd %Inline;> <!-- something user would type -->
+<!ATTLIST kbd %attrs;>
+
+<!ELEMENT var %Inline;> <!-- variable -->
+<!ATTLIST var %attrs;>
+
+<!ELEMENT cite %Inline;> <!-- citation -->
+<!ATTLIST cite %attrs;>
+
+<!ELEMENT abbr %Inline;> <!-- abbreviation -->
+<!ATTLIST abbr %attrs;>
+
+<!ELEMENT acronym %Inline;> <!-- acronym -->
+<!ATTLIST acronym %attrs;>
+
+<!ELEMENT q %Inline;> <!-- inlined quote -->
+<!ATTLIST q
+ %attrs;
+ cite %URI; #IMPLIED
+ >
+
+<!ELEMENT sub %Inline;> <!-- subscript -->
+<!ATTLIST sub %attrs;>
+
+<!ELEMENT sup %Inline;> <!-- superscript -->
+<!ATTLIST sup %attrs;>
+
+<!ELEMENT tt %Inline;> <!-- fixed pitch font -->
+<!ATTLIST tt %attrs;>
+
+<!ELEMENT i %Inline;> <!-- italic font -->
+<!ATTLIST i %attrs;>
+
+<!ELEMENT b %Inline;> <!-- bold font -->
+<!ATTLIST b %attrs;>
+
+<!ELEMENT big %Inline;> <!-- bigger font -->
+<!ATTLIST big %attrs;>
+
+<!ELEMENT small %Inline;> <!-- smaller font -->
+<!ATTLIST small %attrs;>
+
+<!ELEMENT u %Inline;> <!-- underline -->
+<!ATTLIST u %attrs;>
+
+<!ELEMENT s %Inline;> <!-- strike-through -->
+<!ATTLIST s %attrs;>
+
+<!ELEMENT strike %Inline;> <!-- strike-through -->
+<!ATTLIST strike %attrs;>
+
+<!ELEMENT basefont EMPTY> <!-- base font size -->
+<!ATTLIST basefont
+ id ID #IMPLIED
+ size CDATA #REQUIRED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!ELEMENT font %Inline;> <!-- local change to font -->
+<!ATTLIST font
+ %coreattrs;
+ %i18n;
+ size CDATA #IMPLIED
+ color %Color; #IMPLIED
+ face CDATA #IMPLIED
+ >
+
+<!--==================== Object ======================================-->
+<!--
+ object is used to embed objects as part of HTML pages.
+ param elements should precede other content. Parameters
+ can also be expressed as attribute/value pairs on the
+ object element itself when brevity is desired.
+-->
+
+<!ELEMENT object (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST object
+ %attrs;
+ declare (declare) #IMPLIED
+ classid %URI; #IMPLIED
+ codebase %URI; #IMPLIED
+ data %URI; #IMPLIED
+ type %ContentType; #IMPLIED
+ codetype %ContentType; #IMPLIED
+ archive %UriList; #IMPLIED
+ standby %Text; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ name NMTOKEN #IMPLIED
+ tabindex %Number; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Pixels; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--
+ param is used to supply a named property value.
+ In XML it would seem natural to follow RDF and support an
+ abbreviated syntax where the param elements are replaced
+ by attribute value pairs on the object start tag.
+-->
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+ id ID #IMPLIED
+ name CDATA #REQUIRED
+ value CDATA #IMPLIED
+ valuetype (data|ref|object) "data"
+ type %ContentType; #IMPLIED
+ >
+
+<!--=================== Java applet ==================================-->
+<!--
+ One of code or object attributes must be present.
+ Place param elements before other content.
+-->
+<!ELEMENT applet (#PCDATA | param | %block; | form | %inline; | %misc;)*>
+<!ATTLIST applet
+ %coreattrs;
+ codebase %URI; #IMPLIED
+ archive CDATA #IMPLIED
+ code CDATA #IMPLIED
+ object CDATA #IMPLIED
+ alt %Text; #IMPLIED
+ name NMTOKEN #IMPLIED
+ width %Length; #REQUIRED
+ height %Length; #REQUIRED
+ align %ImgAlign; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!--=================== Images ===========================================-->
+
+<!--
+ To avoid accessibility problems for people who aren't
+ able to see the image, you should provide a text
+ description using the alt and longdesc attributes.
+ In addition, avoid the use of server-side image maps.
+-->
+
+<!ELEMENT img EMPTY>
+<!ATTLIST img
+ %attrs;
+ src %URI; #REQUIRED
+ alt %Text; #REQUIRED
+ name NMTOKEN #IMPLIED
+ longdesc %URI; #IMPLIED
+ height %Length; #IMPLIED
+ width %Length; #IMPLIED
+ usemap %URI; #IMPLIED
+ ismap (ismap) #IMPLIED
+ align %ImgAlign; #IMPLIED
+ border %Length; #IMPLIED
+ hspace %Pixels; #IMPLIED
+ vspace %Pixels; #IMPLIED
+ >
+
+<!-- usemap points to a map element which may be in this document
+ or an external document, although the latter is not widely supported -->
+
+<!--================== Client-side image maps ============================-->
+
+<!-- These can be placed in the same document or grouped in a
+ separate document although this isn't yet widely supported -->
+
+<!ELEMENT map ((%block; | form | %misc;)+ | area+)>
+<!ATTLIST map
+ %i18n;
+ %events;
+ id ID #REQUIRED
+ class CDATA #IMPLIED
+ style %StyleSheet; #IMPLIED
+ title %Text; #IMPLIED
+ name CDATA #IMPLIED
+ >
+
+<!ELEMENT area EMPTY>
+<!ATTLIST area
+ %attrs;
+ %focus;
+ shape %Shape; "rect"
+ coords %Coords; #IMPLIED
+ href %URI; #IMPLIED
+ nohref (nohref) #IMPLIED
+ alt %Text; #REQUIRED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--================ Forms ===============================================-->
+
+<!ELEMENT form %form.content;> <!-- forms shouldn't be nested -->
+
+<!ATTLIST form
+ %attrs;
+ action %URI; #REQUIRED
+ method (get|post) "get"
+ name NMTOKEN #IMPLIED
+ enctype %ContentType; "application/x-www-form-urlencoded"
+ onsubmit %Script; #IMPLIED
+ onreset %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ accept-charset %Charsets; #IMPLIED
+ target %FrameTarget; #IMPLIED
+ >
+
+<!--
+ Each label must not contain more than ONE field
+ Label elements shouldn't be nested.
+-->
+<!ELEMENT label %Inline;>
+<!ATTLIST label
+ %attrs;
+ for IDREF #IMPLIED
+ accesskey %Character; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ >
+
+<!ENTITY % InputType
+ "(text | password | checkbox |
+ radio | submit | reset |
+ file | hidden | image | button)"
+ >
+
+<!-- the name attribute is required for all but submit & reset -->
+
+<!ELEMENT input EMPTY> <!-- form control -->
+<!ATTLIST input
+ %attrs;
+ %focus;
+ type %InputType; "text"
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ checked (checked) #IMPLIED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ size CDATA #IMPLIED
+ maxlength %Number; #IMPLIED
+ src %URI; #IMPLIED
+ alt CDATA #IMPLIED
+ usemap %URI; #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ accept %ContentTypes; #IMPLIED
+ align %ImgAlign; #IMPLIED
+ >
+
+<!ELEMENT select (optgroup|option)+> <!-- option selector -->
+<!ATTLIST select
+ %attrs;
+ name CDATA #IMPLIED
+ size %Number; #IMPLIED
+ multiple (multiple) #IMPLIED
+ disabled (disabled) #IMPLIED
+ tabindex %Number; #IMPLIED
+ onfocus %Script; #IMPLIED
+ onblur %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!ELEMENT optgroup (option)+> <!-- option group -->
+<!ATTLIST optgroup
+ %attrs;
+ disabled (disabled) #IMPLIED
+ label %Text; #REQUIRED
+ >
+
+<!ELEMENT option (#PCDATA)> <!-- selectable choice -->
+<!ATTLIST option
+ %attrs;
+ selected (selected) #IMPLIED
+ disabled (disabled) #IMPLIED
+ label %Text; #IMPLIED
+ value CDATA #IMPLIED
+ >
+
+<!ELEMENT textarea (#PCDATA)> <!-- multi-line text field -->
+<!ATTLIST textarea
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ rows %Number; #REQUIRED
+ cols %Number; #REQUIRED
+ disabled (disabled) #IMPLIED
+ readonly (readonly) #IMPLIED
+ onselect %Script; #IMPLIED
+ onchange %Script; #IMPLIED
+ >
+
+<!--
+ The fieldset element is used to group form fields.
+ Only one legend element should occur in the content
+ and if present should only be preceded by whitespace.
+-->
+<!ELEMENT fieldset (#PCDATA | legend | %block; | form | %inline; | %misc;)*>
+<!ATTLIST fieldset
+ %attrs;
+ >
+
+<!ENTITY % LAlign "(top|bottom|left|right)">
+
+<!ELEMENT legend %Inline;> <!-- fieldset label -->
+<!ATTLIST legend
+ %attrs;
+ accesskey %Character; #IMPLIED
+ align %LAlign; #IMPLIED
+ >
+
+<!--
+ Content is %Flow; excluding a, form, form controls, iframe
+-->
+<!ELEMENT button %button.content;> <!-- push button -->
+<!ATTLIST button
+ %attrs;
+ %focus;
+ name CDATA #IMPLIED
+ value CDATA #IMPLIED
+ type (button|submit|reset) "submit"
+ disabled (disabled) #IMPLIED
+ >
+
+<!-- single-line text input control (DEPRECATED) -->
+<!ELEMENT isindex EMPTY>
+<!ATTLIST isindex
+ %coreattrs;
+ %i18n;
+ prompt %Text; #IMPLIED
+ >
+
+<!--======================= Tables =======================================-->
+
+<!-- Derived from IETF HTML table standard, see [RFC1942] -->
+
+<!--
+ The border attribute sets the thickness of the frame around the
+ table. The default units are screen pixels.
+
+ The frame attribute specifies which parts of the frame around
+ the table should be rendered. The values are not the same as
+ CALS to avoid a name clash with the valign attribute.
+-->
+<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)">
+
+<!--
+ The rules attribute defines which rules to draw between cells:
+
+ If rules is absent then assume:
+ "none" if border is absent or border="0" otherwise "all"
+-->
+
+<!ENTITY % TRules "(none | groups | rows | cols | all)">
+
+<!-- horizontal placement of table relative to document -->
+<!ENTITY % TAlign "(left|center|right)">
+
+<!-- horizontal alignment attributes for cell contents
+
+ char alignment char, e.g. char=':'
+ charoff offset for alignment char
+-->
+<!ENTITY % cellhalign
+ "align (left|center|right|justify|char) #IMPLIED
+ char %Character; #IMPLIED
+ charoff %Length; #IMPLIED"
+ >
+
+<!-- vertical alignment attributes for cell contents -->
+<!ENTITY % cellvalign
+ "valign (top|middle|bottom|baseline) #IMPLIED"
+ >
+
+<!ELEMENT table
+ (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>
+<!ELEMENT caption %Inline;>
+<!ELEMENT thead (tr)+>
+<!ELEMENT tfoot (tr)+>
+<!ELEMENT tbody (tr)+>
+<!ELEMENT colgroup (col)*>
+<!ELEMENT col EMPTY>
+<!ELEMENT tr (th|td)+>
+<!ELEMENT th %Flow;>
+<!ELEMENT td %Flow;>
+
+<!ATTLIST table
+ %attrs;
+ summary %Text; #IMPLIED
+ width %Length; #IMPLIED
+ border %Pixels; #IMPLIED
+ frame %TFrame; #IMPLIED
+ rules %TRules; #IMPLIED
+ cellspacing %Length; #IMPLIED
+ cellpadding %Length; #IMPLIED
+ align %TAlign; #IMPLIED
+ bgcolor %Color; #IMPLIED
+ >
+
+<!ENTITY % CAlign "(top|bottom|left|right)">
+
+<!ATTLIST caption
+ %attrs;
+ align %CAlign; #IMPLIED
+ >
+
+<!--
+colgroup groups a set of col elements. It allows you to group
+several semantically related columns together.
+-->
+<!ATTLIST colgroup
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ col elements define the alignment properties for cells in
+ one or more columns.
+
+ The width attribute specifies the width of the columns, e.g.
+
+ width=64 width in screen pixels
+ width=0.5* relative width of 0.5
+
+ The span attribute causes the attributes of one
+ col element to apply to more than one column.
+-->
+<!ATTLIST col
+ %attrs;
+ span %Number; "1"
+ width %MultiLength; #IMPLIED
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!--
+ Use thead to duplicate headers when breaking table
+ across page boundaries, or for static headers when
+ tbody sections are rendered in scrolling panel.
+
+ Use tfoot to duplicate footers when breaking table
+ across page boundaries, or for static footers when
+ tbody sections are rendered in scrolling panel.
+
+ Use multiple tbody sections when rules are needed
+ between groups of table rows.
+-->
+<!ATTLIST thead
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tfoot
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tbody
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ >
+
+<!ATTLIST tr
+ %attrs;
+ %cellhalign;
+ %cellvalign;
+ bgcolor %Color; #IMPLIED
+ >
+
+<!-- Scope is simpler than headers attribute for common tables -->
+<!ENTITY % Scope "(row|col|rowgroup|colgroup)">
+
+<!-- th is for headers, td for data and for cells acting as both -->
+
+<!ATTLIST th
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Length; #IMPLIED
+ height %Length; #IMPLIED
+ >
+
+<!ATTLIST td
+ %attrs;
+ abbr %Text; #IMPLIED
+ axis CDATA #IMPLIED
+ headers IDREFS #IMPLIED
+ scope %Scope; #IMPLIED
+ rowspan %Number; "1"
+ colspan %Number; "1"
+ %cellhalign;
+ %cellvalign;
+ nowrap (nowrap) #IMPLIED
+ bgcolor %Color; #IMPLIED
+ width %Length; #IMPLIED
+ height %Length; #IMPLIED
+ >
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/css2xslfo.git
More information about the pkg-java-commits
mailing list