org.eclipse.swt.custom.RTFWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.swt.gtk.linux.aarch64 Show documentation
Show all versions of org.eclipse.swt.gtk.linux.aarch64 Show documentation
Standard Widget Toolkit for GTK on aarch64
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2022 IBM Corporation 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.custom;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
/**
* The {@code RTFWriter} class is used to write styled content as rich text.
* The implementation complies with the RTF specification version 1.5.
*
* {@code toString()} is guaranteed to return a valid formatted string only after
* {@code close()} has been called.
*
* Whole and partial lines and line breaks can be written. Lines will be
* formatted using the styles queried from the {@link LineStyleListener},
* if set, or those set directly in the widget. All styles are applied to
* the stream like they are rendered by the widget. In addition, the
* widget font name and size is used for the whole text.
*/
class RTFWriter extends StyledTextWriterBase {
static final int DEFAULT_FOREGROUND = 0;
static final int DEFAULT_BACKGROUND = 1;
List colorTable;
List fontTable;
public RTFWriter(StyledText styledText, int start, int length) {
super(styledText, start, length);
colorTable = new ArrayList<>();
fontTable = new ArrayList<>();
colorTable.add(this.styledText.getForeground());
colorTable.add(this.styledText.getBackground());
fontTable.add(this.styledText.getFont());
}
@Override
public void close() {
if (!isClosed()) {
writeHeader();
write("\n}}\0");
super.close();
}
}
/**
* Writes the RTF header including font table and color table.
*/
@Override
void writeHeader() {
StringBuilder header = new StringBuilder();
FontData fontData = styledText.getFont().getFontData()[0];
header.append("{\\rtf1\\ansi");
// specify code page, necessary for copy to work in bidi
// systems that don't support Unicode RTF.
String cpg = System.getProperty("file.encoding").toLowerCase();
if (cpg.startsWith("cp") || cpg.startsWith("ms")) {
cpg = cpg.substring(2, cpg.length());
header.append("\\ansicpg");
header.append(cpg);
}
header.append("\\uc1\\deff0{\\fonttbl{\\f0\\fnil ");
header.append(fontData.getName());
header.append(";");
for (int i = 1; i < fontTable.size(); i++) {
header.append("\\f");
header.append(i);
header.append(" ");
FontData fd = fontTable.get(i).getFontData()[0];
header.append(fd.getName());
header.append(";");
}
header.append("}}\n{\\colortbl");
for (Color color : colorTable) {
header.append("\\red");
header.append(color.getRed());
header.append("\\green");
header.append(color.getGreen());
header.append("\\blue");
header.append(color.getBlue());
header.append(";");
}
// some RTF readers ignore the deff0 font tag. Explicitly
// set the font for the whole document to work around this.
header.append("}\n{\\f0\\fs");
// font size is specified in half points
header.append(fontData.getHeight() * 2);
header.append(" ");
write(header.toString(), 0);
}
@Override
public void writeLineDelimiter(String lineDelimiter) {
if (isClosed()) {
SWT.error(SWT.ERROR_IO);
}
write(lineDelimiter);
write("\\par ");
}
@Override
String writeLineStart(Color lineBackground, int indent, int verticalIndent, int alignment, boolean justify) {
write("\\fi");
write(indent);
switch (alignment) {
case SWT.LEFT: write("\\ql"); break;
case SWT.CENTER: write("\\qc"); break;
case SWT.RIGHT: write("\\qr"); break;
}
if (justify) {
write("\\qj");
}
write(" ");
if (lineBackground != null) {
// Background colors are written using the {@code \chshdng0\chcbpat} tag (vs. the {@code \cb} tag).
write("{\\chshdng0\\chcbpat");
write(getColorIndex(lineBackground, DEFAULT_BACKGROUND));
write(" ");
}
// This is what will be used to close the line
return lineBackground == null ? "" : "}";
}
@Override
void writeEmptyLine() {
// Do nothing. RTF does not need special treatment for empty paragraph.
}
@Override
String writeSpanStart(StyleRange style) {
write("{\\cf");
write(getColorIndex(style.foreground, DEFAULT_FOREGROUND));
int colorIndex = getColorIndex(style.background, DEFAULT_BACKGROUND);
if (colorIndex != DEFAULT_BACKGROUND) {
write("\\chshdng0\\chcbpat");
write(colorIndex);
}
int fontStyle = style.fontStyle;
Font font = style.font;
if (font != null) {
int fontIndex = getFontIndex(font);
write("\\f");
write(fontIndex);
FontData fontData = font.getFontData()[0];
write("\\fs");
write(fontData.getHeight() * 2);
fontStyle = fontData.getStyle();
}
if ((fontStyle & SWT.BOLD) != 0) {
write("\\b");
}
if ((fontStyle & SWT.ITALIC) != 0) {
write("\\i");
}
if (style.underline) {
write("\\ul");
}
if (style.strikeout) {
write("\\strike");
}
write(" ");
// This is what will be used to close the span
StringBuilder toCloseSpan = new StringBuilder();
if ((fontStyle & SWT.BOLD) != 0) {
toCloseSpan.append("\\b0");
}
if ((style.fontStyle & SWT.ITALIC) != 0) {
toCloseSpan.append("\\i0");
}
if (style.underline) {
toCloseSpan.append("\\ul0");
}
if (style.strikeout) {
toCloseSpan.append("\\strike0");
}
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 -> {
if (ch > 0x7F) {
result.append("\\u");
result.append(Integer.toString((short) ch));
result.append('?'); // ANSI representation (1 byte long, \\uc1)
} else if (ch == '}' || ch == '{' || ch == '\\') {
result.append('\\');
result.append((char) ch);
} else {
// Fixes bug 21698.
result.append((char) ch);
}
});
return result.toString();
}
/**
* Returns the index of the specified color in the RTF color table.
*
* @param color the color
* @param defaultIndex return value if color is null
* @return the index of the specified color in the RTF color table
* or "defaultIndex" if "color" is null.
*/
private int getColorIndex(Color color, int defaultIndex) {
if (color == null) return defaultIndex;
int index = colorTable.indexOf(color);
if (index == -1) {
index = colorTable.size();
colorTable.add(color);
}
return index;
}
/**
* Returns the index of the specified color in the RTF color table.
*
* @param color the color
* @param defaultIndex return value if color is null
* @return the index of the specified color in the RTF color table
* or "defaultIndex" if "color" is null.
*/
private int getFontIndex(Font font) {
int index = fontTable.indexOf(font);
if (index == -1) {
index = fontTable.size();
fontTable.add(font);
}
return index;
}
}