Skip to content

Commit d46e1b6

Browse files
committed
Use HTML to print
Allows multiple tabs at once and lifts responsibility for print rendering from TextAreaPainter. Fixes #50, closes #213.
1 parent d1e20da commit d46e1b6

3 files changed

Lines changed: 81 additions & 79 deletions

File tree

app/src/processing/app/syntax/JEditTextArea.java

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,6 @@ public final TextAreaPainter getPainter() {
258258
}
259259

260260

261-
public final Printable getPrintable() {
262-
return painter.getPrintable();
263-
}
264-
265-
266261
/**
267262
* Returns the input handler.
268263
*/
@@ -1724,16 +1719,38 @@ public void copy() {
17241719
* specific to any language or version of the PDE.
17251720
*/
17261721
public void copyAsHTML() {
1727-
StringBuilder cf = new StringBuilder("<html><body><pre>\n");
1722+
HtmlSelection formatted = new HtmlSelection("<html><body><pre>\n"
1723+
+ getTextAsHtml(null) + "\n</pre></body></html>");
1724+
1725+
Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard();
1726+
clipboard.setContents(formatted, new ClipboardOwner() {
1727+
public void lostOwnership(Clipboard clipboard, Transferable contents) {
1728+
// I don't care about ownership
1729+
}
1730+
});
1731+
}
1732+
1733+
1734+
/**
1735+
* Guts of copyAsHTML, minus the pre, body, and html blocks surrounding.
1736+
* @param doc If null, read only the selection if any, and use the active
1737+
* document. Otherwise, the whole of doc is used.
1738+
*/
1739+
public String getTextAsHtml(SyntaxDocument doc) {
1740+
StringBuilder cf = new StringBuilder();
17281741

17291742
int selStart = getSelectionStart();
17301743
int selStop = getSelectionStop();
17311744

17321745
int startLine = getSelectionStartLine();
17331746
int stopLine = getSelectionStopLine();
17341747

1748+
if (doc != null) {
1749+
startLine = 0;
1750+
stopLine = doc.getDefaultRootElement().getElementCount() - 1;
1751+
}
17351752
// If no selection, convert all the lines
1736-
if (selStart == selStop) {
1753+
else if (selStart == selStop) {
17371754
startLine = 0;
17381755
stopLine = getLineCount() - 1;
17391756
} else {
@@ -1742,66 +1759,54 @@ public void copyAsHTML() {
17421759
stopLine--;
17431760
}
17441761
}
1762+
if (doc == null) {
1763+
doc = getDocument();
1764+
}
17451765

17461766
// Read the code line by line
17471767
for (int i = startLine; i <= stopLine; i++) {
1748-
emitAsHTML(cf, i);
1768+
emitAsHTML(cf, i, doc);
17491769
}
17501770

1751-
cf.append("\n</pre></body></html>");
1752-
1753-
HtmlSelection formatted = new HtmlSelection(cf.toString());
1754-
1755-
Clipboard clipboard = processing.app.ui.Toolkit.getSystemClipboard();
1756-
clipboard.setContents(formatted, new ClipboardOwner() {
1757-
public void lostOwnership(Clipboard clipboard, Transferable contents) {
1758-
// i don't care about ownership
1759-
}
1760-
});
1771+
return cf.toString();
17611772
}
17621773

17631774

1764-
private void emitAsHTML(StringBuilder cf, int line) {
1775+
private void emitAsHTML(StringBuilder cf, int line, SyntaxDocument doc) {
1776+
// Almost static; only needs the painter for a color scheme.
17651777
Segment segment = new Segment();
1766-
getLineText(line, segment);
1778+
try {
1779+
Element element = doc.getDefaultRootElement().getElement(line);
1780+
int start = element.getStartOffset();
1781+
int stop = element.getEndOffset();
1782+
doc.getText(start, stop - start - 1, segment);
1783+
} catch (BadLocationException e) { return; }
17671784

17681785
char[] segmentArray = segment.array;
17691786
int limit = segment.getEndIndex();
17701787
int segmentOffset = segment.offset;
17711788
int segmentCount = segment.count;
17721789

1773-
TokenMarker tokenMarker = getTokenMarker();
1790+
TokenMarker tokenMarker = doc.getTokenMarker();
17741791
// If syntax coloring is disabled, do simple translation
17751792
if (tokenMarker == null) {
17761793
for (int j = 0; j < segmentCount; j++) {
17771794
char c = segmentArray[j + segmentOffset];
1778-
//cf = cf.append(c);
17791795
appendAsHTML(cf, c);
17801796
}
17811797
} else {
17821798
// If syntax coloring is enabled, we have to do this
17831799
// because tokens can vary in width
1784-
Token tokens;
1785-
if ((painter.getCurrentLineIndex() == line) &&
1786-
(painter.getCurrentLineTokens() != null)) {
1787-
tokens = painter.getCurrentLineTokens();
1788-
1789-
} else {
1790-
painter.setCurrentLineIndex(line);
1791-
painter.setCurrentLineTokens(tokenMarker.markTokens(segment, line));
1792-
tokens = painter.getCurrentLineTokens();
1793-
}
1800+
Token tokens = tokenMarker.markTokens(segment, line);
17941801

17951802
int offset = 0;
17961803
SyntaxStyle[] styles = painter.getStyles();
17971804

17981805
for (;;) {
17991806
byte id = tokens.id;
18001807
if (id == Token.END) {
1801-
char c = segmentArray[segmentOffset + offset];
18021808
if (segmentOffset + offset < limit) {
1803-
//cf.append(c);
1804-
appendAsHTML(cf, c);
1809+
appendAsHTML(cf, segmentArray[segmentOffset + offset]);
18051810
} else {
18061811
cf.append('\n');
18071812
}
@@ -1824,7 +1829,6 @@ private void emitAsHTML(StringBuilder cf, int line) {
18241829
cf.append("&nbsp;");
18251830
} else {
18261831
appendAsHTML(cf, c);
1827-
//cf.append(c);
18281832
}
18291833
// Place close tags [/]
18301834
if (j == (length - 1) && id != Token.NULL && styles[id].isBold())

app/src/processing/app/syntax/TextAreaPainter.java

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import java.awt.event.MouseEvent;
1414
import java.awt.*;
15-
import java.awt.print.*;
1615

1716
import javax.swing.ToolTipManager;
1817
import javax.swing.text.*;
@@ -28,9 +27,6 @@
2827
* @author Slava Pestov
2928
*/
3029
public class TextAreaPainter extends JComponent implements TabExpander {
31-
/** True if inside printing, will handle disabling the highlight */
32-
boolean printing;
33-
3430
/** A specific painter composed by the InputMethod.*/
3531
protected CompositionTextPainter compositionTextPainter;
3632

@@ -502,38 +498,6 @@ public void paint(Graphics gfx) {
502498
}
503499

504500

505-
public Printable getPrintable() {
506-
return new Printable() {
507-
508-
@Override
509-
public int print(Graphics graphics, PageFormat pageFormat,
510-
int pageIndex) throws PrinterException {
511-
int lineHeight = fm.getHeight();
512-
int linesPerPage = (int) (pageFormat.getImageableHeight() / lineHeight);
513-
int lineCount = textArea.getLineCount();
514-
int lastPage = lineCount / linesPerPage;
515-
516-
if (pageIndex > lastPage) {
517-
return NO_SUCH_PAGE;
518-
519-
} else {
520-
Graphics2D g2 = (Graphics2D) graphics;
521-
TokenMarker tokenMarker = textArea.getDocument().getTokenMarker();
522-
int firstLine = pageIndex*linesPerPage;
523-
g2.translate(Math.max(54, pageFormat.getImageableX()),
524-
pageFormat.getImageableY() - firstLine*lineHeight);
525-
printing = true;
526-
for (int line = firstLine; line < firstLine + linesPerPage; line++) {
527-
paintLine(g2, line, 0, tokenMarker);
528-
}
529-
printing = false;
530-
return PAGE_EXISTS;
531-
}
532-
}
533-
};
534-
}
535-
536-
537501
/**
538502
* Marks a line as needing a repaint.
539503
* @param line The line to invalidate
@@ -661,9 +625,7 @@ protected void paintLine(Graphics gfx, int line, int x,
661625
// protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
662626
// Color defaultColor, int x, int y) {
663627
protected void paintPlainLine(Graphics gfx, int line, int x, int y) {
664-
if (!printing) {
665-
paintHighlight(gfx,line,y);
666-
}
628+
paintHighlight(gfx,line,y);
667629
textArea.getLineText(line, currentLine);
668630

669631
// gfx.setFont(plainFont);
@@ -786,8 +748,7 @@ protected int paintSyntaxLine(Graphics gfx, Segment line, int x, int y,
786748
}
787749

788750

789-
protected void paintHighlight(Graphics gfx, int line, int y) {//, boolean printing) {
790-
// if (!printing) {
751+
protected void paintHighlight(Graphics gfx, int line, int y) {
791752
if (line >= textArea.getSelectionStartLine() &&
792753
line <= textArea.getSelectionStopLine()) {
793754
paintLineHighlight(gfx, line, y);

app/src/processing/app/ui/Editor.java

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import javax.swing.event.*;
6565
import javax.swing.plaf.basic.*;
6666
import javax.swing.text.*;
67+
import javax.swing.text.html.*;
6768
import javax.swing.undo.*;
6869

6970

@@ -2610,15 +2611,51 @@ public void handlePageSetup() {
26102611
*/
26112612
public void handlePrint() {
26122613
statusNotice(Language.text("editor.status.printing"));
2614+
2615+
StringBuilder html = new StringBuilder("<html><body>");
2616+
for (SketchCode tab : sketch.getCode()) {
2617+
html.append("<b>" + tab.getPrettyName() + "</b><br>");
2618+
html.append(textarea.getTextAsHtml((SyntaxDocument)tab.getDocument()));
2619+
html.append("<br>");
2620+
}
2621+
html.setLength(html.length() - 4); // Don't want last <br>.
2622+
html.append("</body></html>");
2623+
JTextPane jtp = new JTextPane();
2624+
// Needed for good line wrapping; otherwise one very long word breaks
2625+
// wrapping for the whole document.
2626+
jtp.setEditorKit(new HTMLEditorKit() {
2627+
public ViewFactory getViewFactory() {
2628+
return new HTMLFactory() {
2629+
public View create(Element e) {
2630+
View v = super.create(e);
2631+
if (!(v instanceof javax.swing.text.html.ParagraphView))
2632+
return v;
2633+
else
2634+
return new javax.swing.text.html.ParagraphView(e) {
2635+
protected SizeRequirements calculateMinorAxisRequirements(
2636+
int axis, SizeRequirements r) {
2637+
r = super.calculateMinorAxisRequirements(axis, r);
2638+
r.minimum = 1;
2639+
return r;
2640+
}
2641+
};
2642+
}
2643+
};
2644+
}
2645+
});
2646+
jtp.setFont(new Font(Preferences.get("editor.font.family"), Font.PLAIN, 10));
2647+
jtp.setText(html.toString().replace("\n", "<br>") // Not in a <pre>.
2648+
.replaceAll("(?<!&nbsp;)&nbsp;", " ")); // Allow line wrap.
2649+
26132650
//printerJob = null;
26142651
if (printerJob == null) {
26152652
printerJob = PrinterJob.getPrinterJob();
26162653
}
26172654
if (pageFormat != null) {
26182655
//System.out.println("setting page format " + pageFormat);
2619-
printerJob.setPrintable(textarea.getPrintable(), pageFormat);
2656+
printerJob.setPrintable(jtp.getPrintable(null, null), pageFormat);
26202657
} else {
2621-
printerJob.setPrintable(textarea.getPrintable());
2658+
printerJob.setPrintable(jtp.getPrintable(null, null));
26222659
}
26232660
// set the name of the job to the code name
26242661
printerJob.setJobName(sketch.getCurrentCode().getPrettyName());

0 commit comments

Comments
 (0)