/* * Copyright (C) 2011 Laurent Caillette * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.novelang.rendering; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; /** * Just write HTML with entities verbatim. * Attempts to make {@link org.dom4j.io.HTMLWriter} eat entites as produced * by XSL transformation were unsuccessful, while debugger showed that * correct characters were passed (though unescaped). * * @author Laurent Caillette */ public class HtmlSink implements ContentHandler { private final PrintWriter writer ; public HtmlSink( final OutputStream outputStream, final Charset charset ) { final OutputStreamWriter outputStreamWriter = new OutputStreamWriter( outputStream, charset ) ; this.writer = new PrintWriter( outputStreamWriter, true ) ; } private static boolean isElementIWorthALineBreak( final String elementName ) { final String upperCaseName = elementName.toUpperCase() ; return upperCaseName.startsWith( "P" ) || upperCaseName.startsWith( "BLOCKQUOTE" ) || upperCaseName.startsWith( "H" ) || upperCaseName.startsWith( "META" ) ; } // ================================== // Interesting ContentHandler methods // ================================== @Override public void startDocument() throws SAXException { writer.println( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"" ) ; writer.println( "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" ) ; } @Override public void endDocument() throws SAXException { writer.flush() ; } @Override public void startElement( final String uri, final String localName, final String qName, final Attributes atts ) throws SAXException { writer.append( "<" ).append( localName ) ; for( int i = 0 ; i < atts.getLength() ; i++ ) { writer.append( " " ).append( atts.getLocalName( i ) ). append( "=\"" ).append( atts.getValue( i )).append( "\"" ) ; } writer.append( " >" ) ; if( isElementIWorthALineBreak( localName ) ) { writer.println() ; } } @Override public void endElement( final String uri, final String localName, final String qName ) throws SAXException { if( isElementIWorthALineBreak( localName ) ) { writer.println() ; writer.append( "</" ).append( localName ).append( ">" ) ; writer.println() ; } else { writer.append( "</" ).append( localName ).append( ">" ) ; } } @Override public void characters( final char[] chars, final int start, final int length ) throws SAXException { writer.write( chars, start, length ); } @Override public void ignorableWhitespace( final char[] chars, final int start, final int length ) throws SAXException { writer.write( chars, start, length ); } // ==================================== // Uninteresting ContentHandler methods // ==================================== @Override public void setDocumentLocator( final Locator locator ) { } @Override public void startPrefixMapping( final String prefix, final String uri ) throws SAXException { } @Override public void endPrefixMapping( final String prefix ) throws SAXException { } @Override public void processingInstruction( final String target, final String data ) throws SAXException { } @Override public void skippedEntity( final String name ) throws SAXException { } }