All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.java.textilej.parser.builder.HtmlDocumentBuilder Maven / Gradle / Ivy

The newest version!
package net.java.textilej.parser.builder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import net.java.textilej.parser.Attributes;
import net.java.textilej.parser.ImageAttributes;
import net.java.textilej.parser.LinkAttributes;
import net.java.textilej.parser.ListAttributes;
import net.java.textilej.parser.QuoteAttributes;
import net.java.textilej.parser.TableAttributes;
import net.java.textilej.parser.TableCellAttributes;
import net.java.textilej.parser.TableRowAttributes;
import net.java.textilej.parser.ImageAttributes.Align;
import net.java.textilej.util.FormattingXMLStreamWriter;
import net.java.textilej.util.XmlStreamWriter;

/**
 * A builder that produces XHTML output. 
 * 
 * @author dgreen
 */
public class HtmlDocumentBuilder extends AbstractXmlDocumentBuilder {

	
	private static final Map spanTypeToElementName = new HashMap();
	static {
		spanTypeToElementName.put(SpanType.BOLD,"b");
		spanTypeToElementName.put(SpanType.CITATION,"cite");
		spanTypeToElementName.put(SpanType.ITALIC,"i");
		spanTypeToElementName.put(SpanType.EMPHASIS,"em");
		spanTypeToElementName.put(SpanType.STRONG,"strong");
		spanTypeToElementName.put(SpanType.DELETED,"del");
		spanTypeToElementName.put(SpanType.INSERTED,"ins");
		spanTypeToElementName.put(SpanType.UNDERLINED,"u");
		spanTypeToElementName.put(SpanType.SUPERSCRIPT,"sup");
		spanTypeToElementName.put(SpanType.SUBSCRIPT,"sub");
		spanTypeToElementName.put(SpanType.SPAN,"span");
		spanTypeToElementName.put(SpanType.CODE,"code");
		spanTypeToElementName.put(SpanType.MONOSPACE,"tt");
	}

	private static final Map blockTypeToElementInfo = new HashMap();
	static {
		blockTypeToElementInfo.put(BlockType.BULLETED_LIST,new ElementInfo("ul"));
		blockTypeToElementInfo.put(BlockType.CODE,new ElementInfo("code" ));
		blockTypeToElementInfo.put(BlockType.FOOTNOTE,new ElementInfo("footnote" ));
		blockTypeToElementInfo.put(BlockType.LIST_ITEM,new ElementInfo("li"));
		blockTypeToElementInfo.put(BlockType.NUMERIC_LIST,new ElementInfo("ol"));
		blockTypeToElementInfo.put(BlockType.DEFINITION_LIST,new ElementInfo("dl"));
		blockTypeToElementInfo.put(BlockType.DEFINITION_TERM,new ElementInfo("dt"));
		blockTypeToElementInfo.put(BlockType.DEFINITION_ITEM,new ElementInfo("dd"));
		blockTypeToElementInfo.put(BlockType.PARAGRAPH,new ElementInfo("p" ));
		blockTypeToElementInfo.put(BlockType.PREFORMATTED,new ElementInfo("pre" ));
		blockTypeToElementInfo.put(BlockType.QUOTE,new ElementInfo("blockquote" ));
		blockTypeToElementInfo.put(BlockType.TABLE,new ElementInfo("table" ));
		blockTypeToElementInfo.put(BlockType.TABLE_CELL_HEADER,new ElementInfo("th" ));
		blockTypeToElementInfo.put(BlockType.TABLE_CELL_NORMAL,new ElementInfo("td" ));
		blockTypeToElementInfo.put(BlockType.TABLE_ROW,new ElementInfo("tr"));
		blockTypeToElementInfo.put(BlockType.TIP,new ElementInfo("div","tip","border: 1px solid #090;background-color: #dfd;margin: 20px;padding: 0px 6px 0px 6px;"));
		blockTypeToElementInfo.put(BlockType.WARNING,new ElementInfo("div","warning","border: 1px solid #c00;background-color: #fcc;margin: 20px;padding: 0px 6px 0px 6px;"));
		blockTypeToElementInfo.put(BlockType.INFORMATION,new ElementInfo("div","info","border: 1px solid #3c78b5;background-color: #D8E4F1;margin: 20px;padding: 0px 6px 0px 6px;"));
		blockTypeToElementInfo.put(BlockType.NOTE,new ElementInfo("div","note","border: 1px solid #F0C000;background-color: #FFFFCE;margin: 20px;padding: 0px 6px 0px 6px;"));
		blockTypeToElementInfo.put(BlockType.PANEL,new ElementInfo("div","panel","border: 1px solid #ccc;background-color: #FFFFCE;margin: 10px;padding: 0px 6px 0px 6px;"));
		
	}
	
	
	private String htmlNsUri = "http://www.w3.org/1999/xhtml";
	private String htmlDtd = "";

	private boolean xhtmlStrict = false;
	private boolean emitAsDocument = true;
	private boolean emitDtd = false;
	private String title;

	private String defaultAbsoluteLinkTarget;
	
	private List stylesheets = null;
	private boolean useInlineStyles = true;
	private boolean suppressBuiltInStyles = false;

	private String linkRel;
	
	public HtmlDocumentBuilder(Writer out) {
		super(out);
	}
	
	public HtmlDocumentBuilder(XmlStreamWriter writer) {
		super(writer);

	}
	
	protected XmlStreamWriter createFormattingXmlStreamWriter(Writer out) {
		XmlStreamWriter writer = super.createXmlStreamWriter(out);
		return new FormattingXMLStreamWriter(writer) {
			@Override
			protected boolean preserveWhitespace(String elementName) {
				return elementName.equals("pre") || elementName.equals("code");
			}
		};
	}
	
	/**
	 * The XML Namespace URI of the HTML elements, only used if {@link #isEmitAsDocument()}.
	 * The default value is "http://www.w3.org/1999/xhtml".
	 */
	public String getHtmlNsUri() {
		return htmlNsUri;
	}

	/**
	 * The XML Namespace URI of the HTML elements, only used if {@link #isEmitAsDocument()}.
	 * The default value is "http://www.w3.org/1999/xhtml".
	 */
	public void setHtmlNsUri(String htmlNsUri) {
		this.htmlNsUri = htmlNsUri;
	}

	/**
	 * The DTD to emit, if {@link #isEmitDtd()} and {@link #isEmitAsDocument()}.
	 * The default value is <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
	 */
	public String getHtmlDtd() {
		return htmlDtd;
	}

	/**
	 * The DTD to emit, if {@link #isEmitDtd()} and {@link #isEmitAsDocument()}.
	 * @see #getHtmlDtd()
	 */
	public void setHtmlDtd(String htmlDtd) {
		this.htmlDtd = htmlDtd;
	}

	/**
	 * Indicate if the resulting HTML should be emitted as a document.  If false, the html and body tags are not included
	 * in the output.
	 * Default value is true.
	 */
	public boolean isEmitAsDocument() {
		return emitAsDocument;
	}

	/**
	 * Indicate if the resulting HTML should be emitted as a document.  If false, the html and body tags are not included
	 * in the output.
	 * Default value is true.
	 */
	public void setEmitAsDocument(boolean emitAsDocument) {
		this.emitAsDocument = emitAsDocument;
	}

	/**
	 * Indicate if the resulting HTML should include a DTD.  Ignored unless {@link #isEmitAsDocument()}.
	 * Default value is false.
	 */
	public boolean isEmitDtd() {
		return emitDtd;
	}

	/**
	 * Indicate if the resulting HTML should include a DTD.  Ignored unless {@link #isEmitAsDocument()}.
	 * Default value is false.
	 */
	public void setEmitDtd(boolean emitDtd) {
		this.emitDtd = emitDtd;
	}

	/**
	 * indicate if the builder should attempt to conform to strict XHTML rules.  The default is false.
	 */
	public boolean isXhtmlStrict() {
		return xhtmlStrict;
	}

	/**
	 * indicate if the builder should attempt to conform to strict XHTML rules.  The default is false.
	 */
	public void setXhtmlStrict(boolean xhtmlStrict) {
		this.xhtmlStrict = xhtmlStrict;
	}

	/**
	 * Set the document title, which will be emitted into the <title> element.
	 * Ignored unless {@link #isEmitAsDocument()}
	 * 
	 * @return the title or null if there is none
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * Set the document title, which will be emitted into the <title> element.
	 * Ignored unless {@link #isEmitAsDocument()}
	 * 
	 * @param title the title or null if there is none
	 */
	public void setTitle(String title) {
		this.title = title;
	}
	
	/**
	 * A default target attribute for links that have absolute (not relative) urls.
	 * By default this value is null.  Setting this value will cause all HTML anchors
	 * to have their target attribute set if it's not explicitly specified in a {@link LinkAttributes}.
	 */
	public String getDefaultAbsoluteLinkTarget() {
		return defaultAbsoluteLinkTarget;
	}

	/**
	 * A default target attribute for links that have absolute (not relative) urls.
	 * By default this value is null.  Setting this value will cause all HTML anchors
	 * to have their target attribute set if it's not explicitly specified in a {@link LinkAttributes}.
	 */
	public void setDefaultAbsoluteLinkTarget(String defaultAbsoluteLinkTarget) {
		this.defaultAbsoluteLinkTarget = defaultAbsoluteLinkTarget;
	}

	/**
	 * Add a CSS stylesheet to the output document as an URL, where the CSS stylesheet
	 * is referenced as an HTML link.
	 * Calling this method after {@link #beginDocument() starting the document}
	 * has no effect.
	 * 
	 * Generates code similar to the following: 
	 * 
	 *   <link type="text/css" rel="stylesheet" href="url"/>
	 * 
	 * 
	 * @param url the CSS url to use, which may be relative or absolute
	 * 
	 * @see #addCssStylesheet(File)
	 */
	public void addCssStylesheet(String url) {
		addStylesheet(new Stylesheet(url,false));
	}
	
	/**
	 * Add CSS stylesheet content to the output document.
	 * Calling this method after {@link #beginDocument() starting the document}
	 * has no effect.
	 * 
	 * Generates code similar to the following: 
	 * 

	 *   <style type="text/css">
	 *   ... contents of the file ...
	 *   </style>
	 * 
* * @param url the CSS url to use, which may be relative or absolute * @param append if true, the content is appended, otherwise it is prepended in the stylesheet order * * @see #addCssStylesheet(File) */ public void addCssStylesheetContent(String content) { addStylesheet(new Stylesheet(content,true)); } private void addStylesheet(Stylesheet stylesheet) { if (stylesheets == null) { stylesheets = new ArrayList(); } stylesheets.add(stylesheet); } /** * Add a CSS stylesheet to the output document, where the contents of the CSS stylesheet * are embedded in the HTML. * Calling this method after {@link #beginDocument() starting the document} * has no effect. * * Generates code similar to the following: *

	 *   <style type="text/css">
	 *   ... contents of the file ...
	 *   </style>
	 * 
* * * @param file * * @see #addCssStylesheet(String) */ public void addCssStylesheet(File file) { if (file == null) { throw new IllegalArgumentException(); } if (!file.exists()) { throw new IllegalArgumentException(String.format("File does not exist: %s",file)); } if (!file.isFile()) { throw new IllegalArgumentException(String.format("Not a file: %s",file)); } if (!file.canRead()) { throw new IllegalArgumentException(String.format("File cannot be read: %s",file)); } addStylesheet(new Stylesheet(file)); } /** * Indicate if inline styles should be used when creating output such as text boxes. * When disabled inline styles are suppressed and CSS classes are used instead, with the default styles emitted as a * stylesheet in the document head. If disabled and {@link #isEmitAsDocument()} is false, this option has the same effect as {@link #isSuppressBuiltInStyles()}. * * The default is true. * * @see #isSuppressBuiltInStyles() */ public boolean isUseInlineStyles() { return useInlineStyles; } /** * Indicate if inline styles should be used when creating output such as text boxes. * When disabled inline styles are suppressed and CSS classes are used instead, with the default styles emitted as a * stylesheet in the document head. If disabled and {@link #isEmitAsDocument()} is false, this option has the same effect as {@link #isSuppressBuiltInStyles()}. * * The default is true. */ public void setUseInlineStyles(boolean useInlineStyles) { this.useInlineStyles = useInlineStyles; } /** * indicate if default built-in CSS styles should be suppressed. Built-in styles are styles that are emitted by this builder * to create the desired visual effect when rendering certain types of elements, such as warnings or infos. * the default is false. * * @see #isUseInlineStyles() */ public boolean isSuppressBuiltInStyles() { return suppressBuiltInStyles; } /** * indicate if default built-in CSS styles should be suppressed. Built-in styles are styles that are emitted by this builder * to create the desired visual effect when rendering certain types of elements, such as warnings or infos. * the default is false. */ public void setSuppressBuiltInStyles(boolean suppressBuiltInStyles) { this.suppressBuiltInStyles = suppressBuiltInStyles; } /** * The 'rel' value for HTML links. If specified the value is applied to all links generated by the builder. * The default value is null. * * Setting this value to "nofollow" is recommended for rendering HTML in areas where users may * add links, for example in a blog comment. See * http://en.wikipedia.org/wiki/Nofollow for more information. * * @return the rel or null if there is none. * @see LinkAttributes#getRef() */ public String getLinkRel() { return linkRel; } /** * The 'rel' value for HTML links. If specified the value is applied to all links generated by the builder. * The default value is null. * * Setting this value to "nofollow" is recommended for rendering HTML in areas where users may * add links, for example in a blog comment. See * http://en.wikipedia.org/wiki/Nofollow for more information. * * @param linkRel the rel or null if there is none. * * @see LinkAttributes#getRef() */ public void setLinkRel(String linkRel) { this.linkRel = linkRel; } @Override public void beginDocument() { writer.setDefaultNamespace(htmlNsUri); if (emitAsDocument) { writer.writeStartDocument(); if (emitDtd && htmlDtd != null) { writer.writeDTD(htmlDtd); } writer.writeStartElement(htmlNsUri,"html"); writer.writeDefaultNamespace(htmlNsUri); writer.writeStartElement(htmlNsUri,"head"); if (base != null && baseInHead) { writer.writeEmptyElement(htmlNsUri,"base"); writer.writeAttribute("href", base.toString()); } if (title != null) { writer.writeStartElement(htmlNsUri,"title"); writer.writeCharacters(title); writer.writeEndElement(); // title } if (!useInlineStyles && !suppressBuiltInStyles) { writer.writeStartElement(htmlNsUri,"style"); writer.writeAttribute("type", "text/css"); writer.writeCharacters("\n"); for (Entry ent: new TreeMap(blockTypeToElementInfo).entrySet()) { ElementInfo elementInfo = ent.getValue(); if (elementInfo.cssStyles != null && elementInfo.cssClass != null) { String[] classes = elementInfo.cssClass.split("\\s+"); for (String cssClass: classes) { writer.writeCharacters("."); writer.writeCharacters(cssClass); writer.writeCharacters(" "); } writer.writeCharacters("{"); writer.writeCharacters(elementInfo.cssStyles); writer.writeCharacters("}\n"); } } writer.writeEndElement(); } if (stylesheets != null) { for (Stylesheet stylesheet: stylesheets) { if (stylesheet.url != null) { // writer.writeEmptyElement(htmlNsUri,"link"); writer.writeAttribute("type", "text/css"); writer.writeAttribute("rel", "stylesheet"); writer.writeAttribute("href", makeUrlAbsolute(stylesheet.url)); } else { // writer.writeStartElement(htmlNsUri,"style"); writer.writeAttribute("type", "text/css"); String css; if (stylesheet.content != null) { css = stylesheet.content; } else { try { css = readFully(stylesheet.file); } catch (IOException e) { throw new IllegalStateException(String.format("Cannot read file: %s",stylesheet.file),e); } } writer.writeCharacters(css); writer.writeEndElement(); } } } writer.writeEndElement(); // head writer.writeStartElement(htmlNsUri,"body"); } else { // sanity check if (stylesheets != null && !stylesheets.isEmpty()) { throw new IllegalStateException("CSS stylesheets are specified but the HTML output is not a document"); } } } @Override public void endDocument() { if (emitAsDocument) { writer.writeEndElement(); // body writer.writeEndElement(); // html writer.writeEndDocument(); } writer.close(); } @Override public void entityReference(String entity) { writer.writeEntityRef(entity); } @Override public void acronym(String text, String definition) { writer.writeStartElement(htmlNsUri,"acronym"); writer.writeAttribute("title", definition); writer.writeCharacters(text); writer.writeEndElement(); } @Override public void link(Attributes attributes,String hrefOrHashName, String text) { writer.writeStartElement(htmlNsUri,"a"); writer.writeAttribute("href", makeUrlAbsolute(hrefOrHashName)); applyLinkAttributes(attributes,hrefOrHashName); characters(text); writer.writeEndElement(); // a } @Override public void beginBlock(BlockType type, Attributes attributes) { ElementInfo elementInfo = blockTypeToElementInfo.get(type); if (elementInfo == null) { throw new IllegalStateException(type.name()); } writer.writeStartElement(htmlNsUri,elementInfo.name); if (elementInfo.cssClass != null) { if (attributes.getCssClass() == null) { attributes.setCssClass(elementInfo.cssClass); } else { attributes.setCssClass(elementInfo.cssClass+' '+attributes.getCssClass()); } } if (useInlineStyles && !suppressBuiltInStyles && elementInfo.cssStyles != null) { if (attributes.getCssStyle() == null) { attributes.setCssStyle(elementInfo.cssStyles); } else { attributes.setCssStyle(elementInfo.cssStyles+attributes.getCssStyle()); } } if (type == BlockType.TABLE) { applyTableAttributes(attributes); } else if (type == BlockType.TABLE_ROW) { applyTableRowAttributes(attributes); } else if (type == BlockType.TABLE_CELL_HEADER || type == BlockType.TABLE_CELL_NORMAL) { applyCellAttributes(attributes); } else if (type == BlockType.BULLETED_LIST || type == BlockType.NUMERIC_LIST) { applyListAttributes(attributes); } else if (type == BlockType.QUOTE) { applyQuoteAttributes(attributes); } else { applyAttributes(attributes); // create the titled panel effect if a title is specified if (attributes.getTitle() != null) { beginBlock(BlockType.PARAGRAPH, new Attributes()); beginSpan(SpanType.BOLD, new Attributes()); characters(attributes.getTitle()); endSpan(); endBlock(); } } } @Override public void beginHeading(int level, Attributes attributes) { if (level > 6) { level = 6; } writer.writeStartElement(htmlNsUri,"h"+level); applyAttributes(attributes); } @Override public void beginSpan(SpanType type, Attributes attributes) { String elementName = spanTypeToElementName.get(type); if (elementName == null) { throw new IllegalStateException(type.name()); } writer.writeStartElement(htmlNsUri,elementName); applyAttributes(attributes); } @Override public void endBlock() { writer.writeEndElement(); } @Override public void endHeading() { writer.writeEndElement(); } @Override public void endSpan() { writer.writeEndElement(); } @Override public void image(Attributes attributes, String url) { writer.writeEmptyElement(htmlNsUri,"img"); applyImageAttributes(attributes); writer.writeAttribute("src", makeUrlAbsolute(url)); } private void applyListAttributes(Attributes attributes) { applyAttributes(attributes); if (attributes instanceof ListAttributes) { ListAttributes listAttributes = (ListAttributes) attributes; if (listAttributes.getStart() != null) { writer.writeAttribute("start", listAttributes.getStart()); } } } private void applyQuoteAttributes(Attributes attributes) { applyAttributes(attributes); if (attributes instanceof QuoteAttributes) { QuoteAttributes quoteAttributes = (QuoteAttributes) attributes; if (quoteAttributes.getCitation() != null) { writer.writeAttribute("cite", quoteAttributes.getCitation()); } } } private void applyTableAttributes(Attributes attributes) { applyAttributes(attributes); if (attributes.getTitle() != null) { writer.writeAttribute("title", attributes.getTitle()); } if (attributes instanceof TableAttributes) { TableAttributes tableAttributes = (TableAttributes) attributes; if (tableAttributes.getBgcolor() != null) { writer.writeAttribute("bgcolor", tableAttributes.getBgcolor()); } if (tableAttributes.getBorder() != null) { writer.writeAttribute("border", tableAttributes.getBorder()); } if (tableAttributes.getCellpadding() != null) { writer.writeAttribute("cellpadding", tableAttributes.getCellpadding()); } if (tableAttributes.getCellspacing() != null) { writer.writeAttribute("cellspacing", tableAttributes.getCellspacing()); } if (tableAttributes.getFrame() != null) { writer.writeAttribute("frame", tableAttributes.getFrame()); } if (tableAttributes.getRules() != null) { writer.writeAttribute("rules", tableAttributes.getRules()); } if (tableAttributes.getSummary() != null) { writer.writeAttribute("summary", tableAttributes.getSummary()); } if (tableAttributes.getWidth() != null) { writer.writeAttribute("width", tableAttributes.getWidth()); } } } private void applyTableRowAttributes(Attributes attributes) { applyAttributes(attributes); if (attributes.getTitle() != null) { writer.writeAttribute("title", attributes.getTitle()); } if (attributes instanceof TableRowAttributes) { TableRowAttributes tableRowAttributes = (TableRowAttributes) attributes; if (tableRowAttributes.getBgcolor() != null) { writer.writeAttribute("bgcolor", tableRowAttributes.getBgcolor()); } if (tableRowAttributes.getAlign() != null) { writer.writeAttribute("align", tableRowAttributes.getAlign()); } if (tableRowAttributes.getValign() != null) { writer.writeAttribute("valign", tableRowAttributes.getValign()); } } } private void applyCellAttributes(Attributes attributes) { applyAttributes(attributes); if (attributes.getTitle() != null) { writer.writeAttribute("title", attributes.getTitle()); } if (attributes instanceof TableCellAttributes) { TableCellAttributes tableCellAttributes = (TableCellAttributes) attributes; if (tableCellAttributes.getBgcolor() != null) { writer.writeAttribute("bgcolor", tableCellAttributes.getBgcolor()); } if (tableCellAttributes.getAlign() != null) { writer.writeAttribute("align", tableCellAttributes.getAlign()); } if (tableCellAttributes.getValign() != null) { writer.writeAttribute("valign", tableCellAttributes.getValign()); } if (tableCellAttributes.getRowspan() != null) { writer.writeAttribute("rowspan", tableCellAttributes.getRowspan()); } if (tableCellAttributes.getColspan() != null) { writer.writeAttribute("colspan", tableCellAttributes.getColspan()); } } } private void applyImageAttributes(Attributes attributes) { int border = 0; Align align = null; if (attributes instanceof ImageAttributes) { ImageAttributes imageAttributes = (ImageAttributes) attributes; border = imageAttributes.getBorder(); align = imageAttributes.getAlign(); } if (xhtmlStrict) { String borderStyle = String.format("border-width: %spx;",border); String alignStyle = null; if (align != null) { switch (align) { case Center: case Right: case Left: alignStyle = "text-align: "+align.name().toLowerCase()+";"; break; case Bottom: case Baseline: case Top: case Middle: alignStyle = "vertical-align: "+align.name().toLowerCase()+";"; break; case Texttop: alignStyle = "vertical-align: text-top;"; break; case Absmiddle: alignStyle = "vertical-align: middle;"; break; case Absbottom: alignStyle = "vertical-align: bottom;"; break; } } String additionalStyles = borderStyle; if (alignStyle != null) { additionalStyles += alignStyle; } if (attributes.getCssStyle() == null || attributes.getCssStyle().length() == 0) { attributes.setCssStyle(additionalStyles); } else { attributes.setCssStyle(additionalStyles+attributes.getCssStyle()); } } applyAttributes(attributes); boolean haveAlt = false; if (attributes instanceof ImageAttributes) { ImageAttributes imageAttributes = (ImageAttributes) attributes; if (imageAttributes.getHeight() != -1) { writer.writeAttribute("height", Integer.toString(imageAttributes.getHeight())); } if (imageAttributes.getWidth() != -1) { writer.writeAttribute("width", Integer.toString(imageAttributes.getWidth())); } if (!xhtmlStrict && align != null) { writer.writeAttribute("align", align.name().toLowerCase()); } if (imageAttributes.getAlt() != null) { haveAlt = true; writer.writeAttribute("alt", imageAttributes.getAlt()); } } if (attributes.getTitle() != null) { writer.writeAttribute("title", attributes.getTitle()); if (!haveAlt) { haveAlt = true; writer.writeAttribute("alt", attributes.getTitle()); } } if (xhtmlStrict) { if (!haveAlt) { // XHTML requires img/@alt writer.writeAttribute("alt", ""); } } else { // only specify border attribute if it's not already specified in CSS writer.writeAttribute("border",Integer.toString(border)); } } private void applyLinkAttributes(Attributes attributes, String href) { applyAttributes(attributes); boolean hasTarget = false; String rel = linkRel; if (attributes instanceof LinkAttributes) { LinkAttributes linkAttributes = (LinkAttributes) attributes; if (linkAttributes.getTarget() != null) { hasTarget = true; writer.writeAttribute("target", linkAttributes.getTarget()); } if (linkAttributes.getRel() != null) { rel = rel==null?linkAttributes.getRel():linkAttributes.getRel()+' '+rel; } } if (attributes.getTitle() != null && attributes.getTitle().length() > 0) { writer.writeAttribute("title", attributes.getTitle()); } if (!hasTarget && defaultAbsoluteLinkTarget != null && href != null) { if (isExternalLink(href)) { writer.writeAttribute("target", defaultAbsoluteLinkTarget); } } if (rel != null) { writer.writeAttribute("rel", rel); } } /** * Note: this method does not apply the {@link Attributes#getTitle() title}. */ private void applyAttributes(Attributes attributes) { if (attributes.getId() != null) { writer.writeAttribute("id", attributes.getId()); } if (attributes.getCssClass() != null) { writer.writeAttribute("class", attributes.getCssClass()); } if (attributes.getCssStyle() != null) { writer.writeAttribute("style", attributes.getCssStyle()); } if (attributes.getLanguage() != null) { writer.writeAttribute("lang", attributes.getLanguage()); } } @Override public void imageLink(Attributes linkAttributes, Attributes imageAttributes, String href, String imageUrl) { writer.writeStartElement(htmlNsUri,"a"); writer.writeAttribute("href", makeUrlAbsolute(href)); applyLinkAttributes(linkAttributes,href); writer.writeEmptyElement(htmlNsUri,"img"); applyImageAttributes(imageAttributes); writer.writeAttribute("src", makeUrlAbsolute(imageUrl)); writer.writeEndElement(); // a } @Override public void lineBreak() { writer.writeEmptyElement(htmlNsUri,"br"); } @Override public void charactersUnescaped(String literal) { writer.writeLiteral(literal); } private static final class ElementInfo { final String name; final String cssClass; final String cssStyles; public ElementInfo(String name, String cssClass, String cssStyles) { this.name = name; this.cssClass = cssClass; this.cssStyles = cssStyles != null && !cssStyles.endsWith(";")?cssStyles+';':cssStyles; } public ElementInfo(String name) { this(name,null,null); } } private static class Stylesheet { String content; String url; File file; public Stylesheet(File file) { this.file = file; } public Stylesheet(String urlOrContent,boolean content) { if (content) { this.content = urlOrContent; } else { this.url = urlOrContent; } } } private static String readFully(File inputFile) throws IOException { int length = (int) inputFile.length(); if (length <= 0) { length = 2048; } StringBuilder buf = new StringBuilder(length); Reader reader = new BufferedReader(new FileReader(inputFile)); try { int c; while ((c = reader.read()) != -1) { buf.append((char)c); } } finally { reader.close(); } return buf.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy