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

org.eclipse.swt.custom.HTMLWriter Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2022 Mihai Nita and others
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
package org.eclipse.swt.custom;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;

/**
 * The HTMLWriter class is used to write styled content as HTML.
 * The implementation creates a very-very simple HTML, as compatible
 * as possible: simple styles, no JavaScript.
 *
 * 

How the concepts are mapped to HTML:

*
    *
  • The global {@link StyledText} properties (for example {@link StyledText#getBackground()} * are written in a wrapping {@code
    } or {@code } (for line fragments).

    *
  • The line specific properties (for example {@link StyledText#getLineBackground(int)} * are written the {@code

    } elements - if the copied range contains more then a line fragment.

  • *
  • The {@link StyleRange} properties inside the line are written the {@code } elements.
  • *
*/ class HTMLWriter extends StyledTextWriterBase { private String tag; private boolean multiline; public HTMLWriter(StyledText styledText, int start, int length, StyledTextContent content) { super(styledText, start, length); String text = content.getTextRange(start, length); multiline = text.contains("\n"); tag = multiline ? "div" : "span"; writeHeader(); } @Override public void close() { if (!isClosed()) { write(""); write(""); super.close(); } } @Override void writeHeader() { StringBuilder outerDivStyle = new StringBuilder(); StringBuilder innerDivStyle = new StringBuilder(); appendStyle(outerDivStyle, "background-color:", styledText.getMarginColor(), ";"); appendStyle(innerDivStyle, "color:", styledText.getForeground(), ";"); appendStyle(innerDivStyle, "background-color:", styledText.getBackground(), ";"); appendStyle(outerDivStyle, "padding:" + styledText.getTopMargin() + "px " + styledText.getRightMargin() + "px " + styledText.getBottomMargin() + "px " + styledText.getLeftMargin() + "px;"); String language = appendFont(innerDivStyle, styledText.getFont(), 0); int indent = styledText.getIndent(); if (indent != 0) { appendStyle(innerDivStyle, "text-indent:", indent, "px;"); } if (styledText.getWordWrap()) { // Sequences of white space are preserved. // Lines are broken at newline characters, at
, and as necessary to fill line boxes. appendStyle(innerDivStyle, "white-space:pre-wrap;"); } else { // Sequences of white space are preserved. // Lines are only broken at newline characters in the source and at
elements. appendStyle(innerDivStyle, "white-space:pre;"); } appendAlignAndJustify(innerDivStyle, styledText.getAlignment(), styledText.getJustify()); if (styledText.getOrientation() == SWT.RIGHT_TO_LEFT || styledText.getTextDirection() == SWT.RIGHT_TO_LEFT) { appendStyle(innerDivStyle, "direction:rtl;"); } write("<"+ tag+" style='" + outerDivStyle + "'>"); if (language == null || language.isEmpty()) { write("<"+ tag+" style='" + innerDivStyle + "'>"); } else { write("<"+ tag+" lang='" + language + "' style='" + innerDivStyle + "'>"); } } @Override public void writeLineDelimiter(String lineDelimiter) { if (isClosed()) { SWT.error(SWT.ERROR_IO); } // We don't write the newline, otherwise it would be rendered because of the `white-space:pre;`. } @Override String writeLineStart(Color lineBackground, int indent, int verticalIndent, int alignment, boolean justify) { StringBuilder paragraphStyle = new StringBuilder(); appendAlignAndJustify(paragraphStyle, alignment, justify); appendStyle(paragraphStyle, "background-color:", lineBackground, ";"); if (indent != 0) { appendStyle(paragraphStyle, "text-indent:", indent, "px;"); } if (verticalIndent == 0) { appendStyle(paragraphStyle, "margin:0;"); } else { // Set the margin-top to verticalIndent, everything else zero. appendStyle(paragraphStyle, "margin:", verticalIndent, " 0 0 0;"); } if (!multiline) { return ""; } if (paragraphStyle.length() == 0) { write("

"); } else { write("

"); } // This is what will be used to close the line return "

"; } @Override void writeEmptyLine() { write("
"); } @Override String writeSpanStart(StyleRange style) { StringBuilder spanStyle = new StringBuilder(); appendStyle(spanStyle, "color:", style.foreground, ";"); appendStyle(spanStyle, "background-color:", style.background, ";"); appendFont(spanStyle, style.font, style.fontStyle); if (style.rise != 0) { appendStyle(spanStyle, "position:relative;bottom:", style.rise, "pt;"); } String borderColor = style.borderColor != null ? " " + colorToHex(style.borderColor) : ""; switch (style.borderStyle) { case SWT.BORDER: // intentional fall-trough // In Eclipse the default border is solid case SWT.BORDER_SOLID: appendStyle(spanStyle, "border:solid 1pt", borderColor, ";"); break; case SWT.BORDER_DASH: appendStyle(spanStyle, "border:dashed 1pt", borderColor, ";"); break; case SWT.BORDER_DOT: appendStyle(spanStyle, "border:dotted 1pt", borderColor, ";"); break; default: break; } if (style.underline) { appendStyle(spanStyle, "text-decoration:underline;"); appendStyle(spanStyle, "text-decoration-color:", style.underlineColor, ";"); switch (style.underlineStyle) { case SWT.UNDERLINE_SINGLE: appendStyle(spanStyle, "text-decoration-style:solid;"); break; case SWT.UNDERLINE_DOUBLE: appendStyle(spanStyle, "text-decoration-style:double;"); break; case SWT.UNDERLINE_ERROR: appendStyle(spanStyle, "text-decoration-style:wavy;"); break; case SWT.UNDERLINE_SQUIGGLE: appendStyle(spanStyle, "text-decoration-style:wavy;"); break; case SWT.UNDERLINE_LINK: appendStyle(spanStyle, "text-decoration-style:solid;"); if (style.underlineColor == null) { // If the color of the link underline was not explicitly overridden, then is is blue. appendStyle(spanStyle, "text-decoration-color:#0066cc;"); } if (style.foreground == null) { // If the color of the link text was not explicitly overridden, then is is blue. appendStyle(spanStyle, "color:#0066cc;"); } break; default: break; } } // Both underline and line-through use text-decoration-color. // If we want the underline color to be different than the strikeout color we need two spans. StringBuilder spanStyle2; if (style.strikeout) { spanStyle2 = new StringBuilder(); appendStyle(spanStyle2, "text-decoration:line-through;"); appendStyle(spanStyle2, "text-decoration-color:", style.strikeoutColor, ";"); } else { spanStyle2 = null; } if (spanStyle.length() != 0) { write(""); } if (spanStyle2 != null) { write(""); } // This is what will be used to close the span StringBuilder toCloseSpan = new StringBuilder(); if (spanStyle2 != null) { toCloseSpan.append(""); } if (spanStyle.length() != 0) { toCloseSpan.append(""); } return toCloseSpan.toString(); } // ==== Helper methods ==== @Override String escapeText(String string) { // The resulting string is at least as long as the original. StringBuilder result = new StringBuilder(string.length()); string.chars().forEach(ch -> { switch (ch) { case '&': result.append("&"); break; case '"': result.append("""); break; case '<': result.append("<"); break; case '>': result.append(">"); break; default: result.append((char) ch); } }); return result.toString(); } // TODO: do we also want support for alpha? private static String colorToHex(Color color) { if (color == null) { return null; } return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); } private static void appendStyle(StringBuilder buffer, String value) { buffer.append(value); } private static void appendStyle(StringBuilder buffer, String prefix, String value, String postfix) { buffer.append(prefix); buffer.append(value); buffer.append(postfix); } private static void appendStyle(StringBuilder buffer, String prefix, int value, String postfix) { buffer.append(prefix); buffer.append(value); buffer.append(postfix); } private static void appendStyle(StringBuilder buffer, String prefix, Color value, String postfix) { if (value != null) { buffer.append(prefix); buffer.append(colorToHex(value)); buffer.append(postfix); } } // Return a locale id, if one was present in the font private static String appendFont(StringBuilder buffer, Font font, int fontStyle) { String language = null; if (font != null) { FontData[] fd = font.getFontData(); if (fd != null && fd.length > 0) { FontData fdata = fd[0]; String name = fdata.getName(); if (name != null && !name.isEmpty()) { appendStyle(buffer, "font-family:\"", fdata.getName(), "\";"); } appendStyle(buffer, "font-size:", fdata.getHeight(), "pt;"); language = fdata.getLocale(); // The style in the font wins. // Quote from {@link StyleRange#fontStyle}: // "Note: the font style is not used if the font attribute is set" fontStyle = fdata.getStyle(); } } if (fontStyle != 0) { if ((fontStyle & SWT.ITALIC) == SWT.ITALIC) { appendStyle(buffer, "font-style:italic;"); } if ((fontStyle & SWT.BOLD) == SWT.BOLD) { appendStyle(buffer, "font-weight:bold;"); } } return language; } private static void appendAlignAndJustify(StringBuilder buffer, int alignment, boolean justify) { // `text-align` is used for general paragraph alignment, but when it is used for justify // we need to set `text-align-last` to control the behavior of the last line. String textAlignKey; if (justify) { appendStyle(buffer, "text-align:justify;"); textAlignKey = "text-align-last:"; } else { textAlignKey = "text-align:"; } switch (alignment) { case SWT.LEFT: // This is the default, no need to write anything out. break; case SWT.CENTER: appendStyle(buffer, textAlignKey, "center", ";"); break; case SWT.RIGHT: appendStyle(buffer, textAlignKey, "right", ";"); break; default: break; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy