From c3b35773cd1c455fdaf3e60b2a87dcae8ef68a15 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Wed, 23 Mar 2016 18:33:36 +0000 Subject: [PATCH] Use HTML to print Allows multiple tabs at once and lifts responsibility for print rendering from TextAreaPainter. Fixes #50, closes #213. --- .../processing/app/syntax/JEditTextArea.java | 76 ++++++++++--------- .../app/syntax/TextAreaPainter.java | 43 +---------- app/src/processing/app/ui/Editor.java | 41 +++++++++- 3 files changed, 81 insertions(+), 79 deletions(-) diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 5fb7392db..544978d76 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -258,11 +258,6 @@ public class JEditTextArea extends JComponent } - public final Printable getPrintable() { - return painter.getPrintable(); - } - - /** * Returns the input handler. */ @@ -1724,7 +1719,25 @@ public class JEditTextArea extends JComponent * specific to any language or version of the PDE. */ public void copyAsHTML() { - StringBuilder cf = new StringBuilder("
\n");
+    HtmlSelection formatted = new HtmlSelection("
\n"
+        + getTextAsHtml(null) + "\n
"); + + Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard(); + clipboard.setContents(formatted, new ClipboardOwner() { + public void lostOwnership(Clipboard clipboard, Transferable contents) { + // I don't care about ownership + } + }); + } + + + /** + * Guts of copyAsHTML, minus the pre, body, and html blocks surrounding. + * @param doc If null, read only the selection if any, and use the active + * document. Otherwise, the whole of doc is used. + */ + public String getTextAsHtml(SyntaxDocument doc) { + StringBuilder cf = new StringBuilder(); int selStart = getSelectionStart(); int selStop = getSelectionStop(); @@ -1732,8 +1745,12 @@ public class JEditTextArea extends JComponent int startLine = getSelectionStartLine(); int stopLine = getSelectionStopLine(); + if (doc != null) { + startLine = 0; + stopLine = doc.getDefaultRootElement().getElementCount() - 1; + } // If no selection, convert all the lines - if (selStart == selStop) { + else if (selStart == selStop) { startLine = 0; stopLine = getLineCount() - 1; } else { @@ -1742,55 +1759,45 @@ public class JEditTextArea extends JComponent stopLine--; } } + if (doc == null) { + doc = getDocument(); + } // Read the code line by line for (int i = startLine; i <= stopLine; i++) { - emitAsHTML(cf, i); + emitAsHTML(cf, i, doc); } - cf.append("\n
"); - - HtmlSelection formatted = new HtmlSelection(cf.toString()); - - Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard(); - clipboard.setContents(formatted, new ClipboardOwner() { - public void lostOwnership(Clipboard clipboard, Transferable contents) { - // i don't care about ownership - } - }); + return cf.toString(); } - private void emitAsHTML(StringBuilder cf, int line) { + private void emitAsHTML(StringBuilder cf, int line, SyntaxDocument doc) { + // Almost static; only needs the painter for a color scheme. Segment segment = new Segment(); - getLineText(line, segment); + try { + Element element = doc.getDefaultRootElement().getElement(line); + int start = element.getStartOffset(); + int stop = element.getEndOffset(); + doc.getText(start, stop - start - 1, segment); + } catch (BadLocationException e) { return; } char[] segmentArray = segment.array; int limit = segment.getEndIndex(); int segmentOffset = segment.offset; int segmentCount = segment.count; - TokenMarker tokenMarker = getTokenMarker(); + TokenMarker tokenMarker = doc.getTokenMarker(); // If syntax coloring is disabled, do simple translation if (tokenMarker == null) { for (int j = 0; j < segmentCount; j++) { char c = segmentArray[j + segmentOffset]; - //cf = cf.append(c); appendAsHTML(cf, c); } } else { // If syntax coloring is enabled, we have to do this // because tokens can vary in width - Token tokens; - if ((painter.getCurrentLineIndex() == line) && - (painter.getCurrentLineTokens() != null)) { - tokens = painter.getCurrentLineTokens(); - - } else { - painter.setCurrentLineIndex(line); - painter.setCurrentLineTokens(tokenMarker.markTokens(segment, line)); - tokens = painter.getCurrentLineTokens(); - } + Token tokens = tokenMarker.markTokens(segment, line); int offset = 0; SyntaxStyle[] styles = painter.getStyles(); @@ -1798,10 +1805,8 @@ public class JEditTextArea extends JComponent for (;;) { byte id = tokens.id; if (id == Token.END) { - char c = segmentArray[segmentOffset + offset]; if (segmentOffset + offset < limit) { - //cf.append(c); - appendAsHTML(cf, c); + appendAsHTML(cf, segmentArray[segmentOffset + offset]); } else { cf.append('\n'); } @@ -1824,7 +1829,6 @@ public class JEditTextArea extends JComponent cf.append(" "); } else { appendAsHTML(cf, c); - //cf.append(c); } // Place close tags [/] if (j == (length - 1) && id != Token.NULL && styles[id].isBold()) diff --git a/app/src/processing/app/syntax/TextAreaPainter.java b/app/src/processing/app/syntax/TextAreaPainter.java index bdfded94d..95482b124 100644 --- a/app/src/processing/app/syntax/TextAreaPainter.java +++ b/app/src/processing/app/syntax/TextAreaPainter.java @@ -12,7 +12,6 @@ package processing.app.syntax; import java.awt.event.MouseEvent; import java.awt.*; -import java.awt.print.*; import javax.swing.ToolTipManager; import javax.swing.text.*; @@ -28,9 +27,6 @@ import processing.app.syntax.im.CompositionTextPainter; * @author Slava Pestov */ public class TextAreaPainter extends JComponent implements TabExpander { - /** True if inside printing, will handle disabling the highlight */ - boolean printing; - /** A specific painter composed by the InputMethod.*/ protected CompositionTextPainter compositionTextPainter; @@ -502,38 +498,6 @@ public class TextAreaPainter extends JComponent implements TabExpander { } - public Printable getPrintable() { - return new Printable() { - - @Override - public int print(Graphics graphics, PageFormat pageFormat, - int pageIndex) throws PrinterException { - int lineHeight = fm.getHeight(); - int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight); - int lineCount = textArea.getLineCount(); - int lastPage = lineCount / linesPerPage; - - if (pageIndex > lastPage) { - return NO_SUCH_PAGE; - - } else { - Graphics2D g2 = (Graphics2D) graphics; - TokenMarker tokenMarker = textArea.getDocument().getTokenMarker(); - int firstLine = pageIndex*linesPerPage; - g2.translate(Math.max(54, pageFormat.getImageableX()), - pageFormat.getImageableY() - firstLine*lineHeight); - printing = true; - for (int line = firstLine; line < firstLine + linesPerPage; line++) { - paintLine(g2, line, 0, tokenMarker); - } - printing = false; - return PAGE_EXISTS; - } - } - }; - } - - /** * Marks a line as needing a repaint. * @param line The line to invalidate @@ -661,9 +625,7 @@ public class TextAreaPainter extends JComponent implements TabExpander { // protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, // Color defaultColor, int x, int y) { protected void paintPlainLine(Graphics gfx, int line, int x, int y) { - if (!printing) { - paintHighlight(gfx,line,y); - } + paintHighlight(gfx,line,y); textArea.getLineText(line, currentLine); // gfx.setFont(plainFont); @@ -786,8 +748,7 @@ public class TextAreaPainter extends JComponent implements TabExpander { } - protected void paintHighlight(Graphics gfx, int line, int y) {//, boolean printing) { -// if (!printing) { + protected void paintHighlight(Graphics gfx, int line, int y) { if (line >= textArea.getSelectionStartLine() && line <= textArea.getSelectionStopLine()) { paintLineHighlight(gfx, line, y); diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 47081b99e..83a134351 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -64,6 +64,7 @@ import javax.swing.*; import javax.swing.event.*; import javax.swing.plaf.basic.*; import javax.swing.text.*; +import javax.swing.text.html.*; import javax.swing.undo.*; @@ -2610,15 +2611,51 @@ public abstract class Editor extends JFrame implements RunnerListener { */ public void handlePrint() { statusNotice(Language.text("editor.status.printing")); + + StringBuilder html = new StringBuilder(""); + for (SketchCode tab : sketch.getCode()) { + html.append("" + tab.getPrettyName() + "
"); + html.append(textarea.getTextAsHtml((SyntaxDocument)tab.getDocument())); + html.append("
"); + } + html.setLength(html.length() - 4); // Don't want last
. + html.append(""); + JTextPane jtp = new JTextPane(); + // Needed for good line wrapping; otherwise one very long word breaks + // wrapping for the whole document. + jtp.setEditorKit(new HTMLEditorKit() { + public ViewFactory getViewFactory() { + return new HTMLFactory() { + public View create(Element e) { + View v = super.create(e); + if (!(v instanceof javax.swing.text.html.ParagraphView)) + return v; + else + return new javax.swing.text.html.ParagraphView(e) { + protected SizeRequirements calculateMinorAxisRequirements( + int axis, SizeRequirements r) { + r = super.calculateMinorAxisRequirements(axis, r); + r.minimum = 1; + return r; + } + }; + } + }; + } + }); + jtp.setFont(new Font(Preferences.get("editor.font.family"), Font.PLAIN, 10)); + jtp.setText(html.toString().replace("\n", "
") // Not in a
.
+        .replaceAll("(?