org.fife.ui.rsyntaxtextarea.RtfGenerator Maven / Gradle / Ivy
The newest version!
/*
* 07/28/2008
*
* RtfGenerator.java - Generates RTF via a simple Java API.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.Color;
import java.awt.Font;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.List;
import org.fife.ui.rtextarea.RTextArea;
/**
* Generates RTF text via a simple Java API.
*
* The following RTF features are supported:
*
* - Fonts
*
- Font sizes
*
- Foreground and background colors
*
- Bold, italic, and underline
*
*
* The RTF generated isn't really "optimized," but it will do, especially for
* small amounts of text, such as what's common when copy-and-pasting. It
* tries to be sufficient for the use case of copying syntax highlighted
* code:
*
* - It assumes that tokens changing foreground color often is fairly
* common.
*
- It assumes that background highlighting is fairly uncommon.
*
*
* @author Robert Futrell
* @version 1.0
*/
public class RtfGenerator {
private List fontList;
private List colorList;
private StringBuilder document;
private boolean lastWasControlWord;
private int lastFontIndex;
private int lastFGIndex;
private boolean lastBold;
private boolean lastItalic;
private int lastFontSize;
/**
* Java2D assumes a 72 dpi screen resolution, but on Windows the screen
* resolution is either 96 dpi or 120 dpi, depending on your font display
* settings. This is an attempt to make the RTF generated match the
* size of what's displayed in the RSyntaxTextArea.
*/
private int screenRes;
/**
* The default font size for RTF. This is point size, in half
* points.
*/
private static final int DEFAULT_FONT_SIZE = 12;//24;
/**
* Constructor.
*/
public RtfGenerator() {
fontList = new ArrayList(1); // Usually only 1.
colorList = new ArrayList(1); // Usually only 1.
document = new StringBuilder();
reset();
}
/**
* Adds a newline to the RTF document.
*
* @see #appendToDoc(String, Font, Color, Color)
*/
public void appendNewline() {
document.append("\\par");
document.append('\n'); // Just for ease of reading RTF.
lastWasControlWord = false;
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is null
, the
* default font is used.
* @param fg The foreground of the text. If this is null
,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* null
, the default background color is used.
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg) {
appendToDoc(text, f, fg, bg, false);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is null
, the
* default font is used.
* @param bg The background color of the text. If this is
* null
, the default background color is used.
* @param underline Whether the text should be underlined.
* @see #appendNewline()
*/
public void appendToDocNoFG(String text, Font f, Color bg,
boolean underline) {
appendToDoc(text, f, null, bg, underline, false);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is null
, the
* default font is used.
* @param fg The foreground of the text. If this is null
,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* null
, the default background color is used.
* @param underline Whether the text should be underlined.
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg,
boolean underline) {
appendToDoc(text, f, fg, bg, underline, true);
}
/**
* Appends styled text to the RTF document being generated.
*
* @param text The text to append.
* @param f The font of the text. If this is null
, the
* default font is used.
* @param fg The foreground of the text. If this is null
,
* the default foreground color is used.
* @param bg The background color of the text. If this is
* null
, the default background color is used.
* @param underline Whether the text should be underlined.
* @param setFG Whether the foreground specified by fg
should
* be honored (if it is non-null
).
* @see #appendNewline()
*/
public void appendToDoc(String text, Font f, Color fg, Color bg,
boolean underline, boolean setFG) {
if (text!=null) {
// Set font to use, if different from last addition.
int fontIndex = f==null ? 0 : (getFontIndex(fontList, f)+1);
if (fontIndex!=lastFontIndex) {
document.append("\\f").append(fontIndex);
lastFontIndex = fontIndex;
lastWasControlWord = true;
}
// Set styles to use.
if (f!=null) {
int fontSize = fixFontSize(f.getSize2D()*2); // Half points!
if (fontSize!=lastFontSize) {
document.append("\\fs").append(fontSize);
lastFontSize = fontSize;
lastWasControlWord = true;
}
if (f.isBold()!=lastBold) {
document.append(lastBold ? "\\b0" : "\\b");
lastBold = !lastBold;
lastWasControlWord = true;
}
if (f.isItalic()!=lastItalic) {
document.append(lastItalic ? "\\i0" : "\\i");
lastItalic = !lastItalic;
lastWasControlWord = true;
}
}
else { // No font specified - assume neither bold nor italic.
if (lastFontSize!=DEFAULT_FONT_SIZE) {
document.append("\\fs").append(DEFAULT_FONT_SIZE);
lastFontSize = DEFAULT_FONT_SIZE;
lastWasControlWord = true;
}
if (lastBold) {
document.append("\\b0");
lastBold = false;
lastWasControlWord = true;
}
if (lastItalic) {
document.append("\\i0");
lastItalic = false;
lastWasControlWord = true;
}
}
if (underline) {
document.append("\\ul");
lastWasControlWord = true;
}
// Set the foreground color.
if (setFG) {
int fgIndex = 0;
if (fg!=null) { // null => fg color index 0
fgIndex = getColorIndex(colorList, fg)+1;
}
if (fgIndex!=lastFGIndex) {
document.append("\\cf").append(fgIndex);
lastFGIndex = fgIndex;
lastWasControlWord = true;
}
}
// Set the background color.
if (bg!=null) {
int pos = getColorIndex(colorList, bg);
document.append("\\highlight").append(pos+1);
lastWasControlWord = true;
}
if (lastWasControlWord) {
document.append(' '); // Delimiter
lastWasControlWord = false;
}
escapeAndAdd(document, text);
// Reset everything that was set for this text fragment.
if (bg!=null) {
document.append("\\highlight0");
lastWasControlWord = true;
}
if (underline) {
document.append("\\ul0");
lastWasControlWord = true;
}
}
}
/**
* Appends some text to a buffer, with special care taken for special
* characters as defined by the RTF spec:
*
*
* - All tab characters are replaced with the string
* "
\tab
"
* - '\', '{' and '}' are changed to "\\", "\{" and "\}"
*
*
* @param text The text to append (with tab chars substituted).
* @param sb The buffer to append to.
*/
private final void escapeAndAdd(StringBuilder sb, String text) {
int count = text.length();
for (int i=0; i
*
* Java2D assumes 72 dpi. On systems with larger dpi (Windows, GTK, etc.),
* font rendering will appear too small if we simply return a Java "Font"
* object's getSize() value. We need to adjust it for the screen
* resolution.
*
* @param pointSize A Java Font's point size, as returned from
* getSize2D()
.
* @return The font point size, adjusted for the current screen resolution.
* This will allow other applications to render fonts the same
* size as they appear in the Java application.
*/
private int fixFontSize(float pointSize) {
if (screenRes!=72) { // Java2D assumes 72 dpi
pointSize = Math.round(pointSize*72f/screenRes);
}
return (int)pointSize;
}
/**
* Returns the index of the specified item in a list. If the item
* is not in the list, it is added, and its new index is returned.
*
* @param list The list (possibly) containing the item.
* @param item The item to get the index of.
* @return The index of the item.
*/
private static int getColorIndex(List list, Color item) {
int pos = list.indexOf(item);
if (pos==-1) {
list.add(item);
pos = list.size()-1;
}
return pos;
}
private String getColorTableRtf() {
// Example:
// "{\\colortbl ;\\red255\\green0\\blue0;\\red0\\green0\\blue255; }"
StringBuilder sb = new StringBuilder();
sb.append("{\\colortbl ;");
for (Color c : colorList) {
sb.append("\\red").append(c.getRed());
sb.append("\\green").append(c.getGreen());
sb.append("\\blue").append(c.getBlue());
sb.append(';');
}
sb.append("}");
return sb.toString();
}
/**
* Returns the index of the specified font in a list of fonts. This
* method only checks for a font by its family name; its attributes such
* as bold and italic are ignored.
*
* If the font is not in the list, it is added, and its new index is
* returned.
*
* @param list The list (possibly) containing the font.
* @param font The font to get the index of.
* @return The index of the font.
*/
private static int getFontIndex(List list, Font font) {
String fontName = font.getFamily();
for (int i=0; iString
.
*/
public String getRtf() {
StringBuilder sb = new StringBuilder();
sb.append("{");
// Header
sb.append("\\rtf1\\ansi\\ansicpg1252");
sb.append("\\deff0"); // First font in font table is the default
sb.append("\\deflang1033");
sb.append("\\viewkind4"); // "Normal" view
sb.append("\\uc\\pard\\f0");
sb.append("\\fs20"); // Font size in half-points (default 24)
sb.append(getFontTableRtf()).append('\n');
sb.append(getColorTableRtf()).append('\n');
// Content
sb.append(document);
sb.append("}");
//System.err.println("*** " + sb.length());
return sb.toString();
}
/**
* Resets this generator. All document information and content is
* cleared.
*/
public void reset() {
fontList.clear();
colorList.clear();
document.setLength(0);
lastWasControlWord = false;
lastFontIndex = 0;
lastFGIndex = 0;
lastBold = false;
lastItalic = false;
lastFontSize = DEFAULT_FONT_SIZE;
screenRes = Toolkit.getDefaultToolkit().getScreenResolution();
}
}