001: /*
002: * BufferPrintable.java - Printable implementation
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2001, 2003 Slava Pestov
007: * Portions copyright (C) 2002 Thomas Dilts
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; either version 2
012: * of the License, or any later version.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: * GNU General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with this program; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
022: */
023:
024: package org.gjt.sp.jedit.print;
025:
026: //{{{ Imports
027: import javax.swing.text.TabExpander;
028: import javax.swing.SwingUtilities;
029: import java.awt.font.*;
030: import java.awt.geom.*;
031: import java.awt.print.*;
032: import java.awt.*;
033: import java.lang.reflect.Method;
034: import java.util.*;
035: import java.util.List;
036:
037: import org.gjt.sp.jedit.syntax.*;
038: import org.gjt.sp.jedit.*;
039: import org.gjt.sp.util.*;
040:
041: //}}}
042:
043: /**
044: * @version $Id: BufferPrintable.java 8258 2006-12-25 21:39:27Z kpouer $
045: */
046: class BufferPrintable implements Printable {
047: //{{{ BufferPrintable constructor
048: BufferPrintable(PrinterJob job, Object format, View view,
049: Buffer buffer, Font font, boolean header, boolean footer,
050: boolean lineNumbers, boolean color) {
051: this .job = job;
052: this .format = format;
053: this .view = view;
054: this .buffer = buffer;
055: this .font = font;
056: this .header = header;
057: this .footer = footer;
058: this .lineNumbers = lineNumbers;
059:
060: styles = GUIUtilities.loadStyles(jEdit
061: .getProperty("print.font"), jEdit.getIntegerProperty(
062: "print.fontsize", 10), color);
063: styles[Token.NULL] = new SyntaxStyle(textColor, null, font);
064:
065: // Change any white text to black
066: for (int i = 0; i < styles.length; i++) {
067: SyntaxStyle s = styles[i];
068: if (s.getForegroundColor().equals(Color.WHITE)
069: && s.getBackgroundColor() == null) {
070: styles[i] = new SyntaxStyle(Color.BLACK, styles[i]
071: .getBackgroundColor(), styles[i].getFont());
072: }
073: }
074:
075: lineList = new ArrayList<Chunk>();
076:
077: tokenHandler = new DisplayTokenHandler();
078: } //}}}
079:
080: //{{{ print() method
081: public void print() {
082: try {
083: //buffer.readLock();
084:
085: if (format == null)
086: job.print();
087: else {
088: Method method = PrinterJob.class
089: .getMethod(
090: "print",
091: new Class[] { Class
092: .forName("javax.print.attribute.PrintRequestAttributeSet") });
093: method.invoke(job, new Object[] { format });
094: }
095: } catch (PrinterAbortException ae) {
096: Log.log(Log.DEBUG, this , ae);
097: } catch (Exception e) {
098: Log.log(Log.ERROR, this , e);
099: final String[] args = { e.toString() };
100: SwingUtilities.invokeLater(new Runnable() {
101: public void run() {
102: GUIUtilities.error(view, "print-error", args);
103: }
104: });
105: } finally {
106: //buffer.readUnlock();
107: }
108: } //}}}
109:
110: //{{{ print() method
111: public int print(Graphics _gfx, PageFormat pageFormat, int pageIndex)
112: throws PrinterException {
113: // we keep the first non-null frc we get, since sometimes
114: // we get invalid ones on subsequent pages on Windows
115: if (frc == null) {
116: frc = ((Graphics2D) _gfx).getFontRenderContext();
117: Log.log(Log.DEBUG, this , "Font render context is " + frc);
118: }
119:
120: Log.log(Log.DEBUG, this , "Asked to print page " + pageIndex);
121: Log.log(Log.DEBUG, this , "Current page is " + currentPage);
122:
123: if (pageIndex > currentPage) {
124: for (int i = currentPage; i < pageIndex; i++) {
125: Log.log(Log.DEBUG, this ,
126: "Current physical line is now "
127: + currentPageStart);
128: currentPhysicalLine = currentPageStart;
129: printPage(_gfx, pageFormat, i, true);
130: }
131:
132: currentPage = pageIndex - 1;
133: Log.log(Log.DEBUG, this , "Current page is now "
134: + currentPage);
135: }
136:
137: if (pageIndex == currentPage + 1) {
138: if (end) {
139: Log.log(Log.DEBUG, this , "The end");
140: return NO_SUCH_PAGE;
141: }
142:
143: currentPageStart = currentPhysicalLine;
144: Log.log(Log.DEBUG, this ,
145: "#2 - Current physical line is now "
146: + currentPageStart);
147: currentPage = pageIndex;
148: Log.log(Log.DEBUG, this , "#2 - Current page is now "
149: + currentPage);
150: } else if (pageIndex == currentPage) {
151: currentPhysicalLine = currentPageStart;
152: Log.log(Log.DEBUG, this ,
153: "#3 - Current physical line is now "
154: + currentPageStart);
155: }
156:
157: printPage(_gfx, pageFormat, pageIndex, true);
158:
159: return PAGE_EXISTS;
160: } //}}}
161:
162: //{{{ Private members
163:
164: //{{{ Static variables
165: private static Color headerColor = Color.lightGray;
166: private static Color headerTextColor = Color.black;
167: private static Color footerColor = Color.lightGray;
168: private static Color footerTextColor = Color.black;
169: private static Color lineNumberColor = Color.gray;
170: private static Color textColor = Color.black;
171: //}}}
172:
173: //{{{ Instance variables
174: private PrinterJob job;
175: private Object format;
176:
177: private View view;
178: private Buffer buffer;
179: private Font font;
180: private SyntaxStyle[] styles;
181: private boolean header;
182: private boolean footer;
183: private boolean lineNumbers;
184:
185: private int currentPage;
186: private int currentPageStart;
187: private int currentPhysicalLine;
188: private boolean end;
189:
190: private LineMetrics lm;
191: private final List<Chunk> lineList;
192:
193: private FontRenderContext frc;
194:
195: private DisplayTokenHandler tokenHandler;
196:
197: //}}}
198:
199: //{{{ printPage() method
200: private void printPage(Graphics _gfx, PageFormat pageFormat,
201: int pageIndex, boolean actuallyPaint) {
202: Log.log(Log.DEBUG, this , "printPage(" + pageIndex + ','
203: + actuallyPaint + ')');
204: Graphics2D gfx = (Graphics2D) _gfx;
205: gfx.setFont(font);
206:
207: double pageX = pageFormat.getImageableX();
208: double pageY = pageFormat.getImageableY();
209: double pageWidth = pageFormat.getImageableWidth();
210: double pageHeight = pageFormat.getImageableHeight();
211:
212: Log.log(Log.DEBUG, this , "#1 - Page dimensions: " + pageWidth
213: + 'x' + pageHeight);
214:
215: if (header) {
216: double headerHeight = paintHeader(gfx, pageX, pageY,
217: pageWidth, actuallyPaint);
218: pageY += headerHeight;
219: pageHeight -= headerHeight;
220: }
221:
222: if (footer) {
223: double footerHeight = paintFooter(gfx, pageX, pageY,
224: pageWidth, pageHeight, pageIndex, actuallyPaint);
225: pageHeight -= footerHeight;
226: }
227:
228: boolean glyphVector = jEdit
229: .getBooleanProperty("print.glyphVector");
230: double lineNumberWidth;
231:
232: //{{{ determine line number width
233: if (lineNumbers) {
234: // the +1's ensure that 99 gets 3 digits, 103 gets 4 digits,
235: // and so on.
236: int lineNumberDigits = (int) Math.ceil(Math.log(buffer
237: .getLineCount() + 1)
238: / Math.log(10)) + 1;
239:
240: // now that we know how many chars there are, get the width.
241: char[] chars = new char[lineNumberDigits];
242: for (int i = 0; i < chars.length; i++)
243: chars[i] = ' ';
244: lineNumberWidth = font.getStringBounds(chars, 0,
245: lineNumberDigits, frc).getWidth();
246: } else
247: lineNumberWidth = 0.0;
248: //}}}
249:
250: Log.log(Log.DEBUG, this , "#2 - Page dimensions: "
251: + (pageWidth - lineNumberWidth) + 'x' + pageHeight);
252:
253: //{{{ calculate tab size
254: int tabSize = jEdit.getIntegerProperty("print.tabSize", 8);
255: char[] chars = new char[tabSize];
256: for (int i = 0; i < chars.length; i++)
257: chars[i] = ' ';
258: double tabWidth = font.getStringBounds(chars, 0, tabSize, frc)
259: .getWidth();
260: PrintTabExpander e = new PrintTabExpander(tabWidth);
261: //}}}
262:
263: lm = font.getLineMetrics("gGyYX", frc);
264: Log.log(Log.DEBUG, this , "Line height is " + lm.getHeight());
265:
266: double y = 0.0;
267: print_loop: for (;;) {
268: if (currentPhysicalLine == buffer.getLineCount()) {
269: Log.log(Log.DEBUG, this , "Finished buffer");
270: end = true;
271: break print_loop;
272: }
273: if (!jEdit.getBooleanProperty("print.folds", true)
274: && !view.getTextArea().getDisplayManager()
275: .isLineVisible(currentPhysicalLine)) {
276:
277: Log.log(Log.DEBUG, this , "Skipping invisible line");
278: currentPhysicalLine++;
279: continue;
280: }
281:
282: lineList.clear();
283:
284: tokenHandler.init(styles, frc, e, lineList,
285: (float) (pageWidth - lineNumberWidth));
286:
287: buffer.markTokens(currentPhysicalLine, tokenHandler);
288: if (lineList.isEmpty())
289: lineList.add(null);
290:
291: if (y + (lm.getHeight() * lineList.size()) >= pageHeight) {
292: Log.log(Log.DEBUG, this , "Finished page before line "
293: + currentPhysicalLine);
294: break print_loop;
295: }
296:
297: if (lineNumbers && actuallyPaint) {
298: gfx.setFont(font);
299: gfx.setColor(lineNumberColor);
300: gfx.drawString(String.valueOf(currentPhysicalLine + 1),
301: (float) pageX, (float) (pageY + y + lm
302: .getHeight()));
303: }
304:
305: for (int i = 0; i < lineList.size(); i++) {
306: y += lm.getHeight();
307: Chunk chunks = lineList.get(i);
308: if (chunks != null && actuallyPaint) {
309: Chunk.paintChunkBackgrounds(chunks, gfx,
310: (float) (pageX + lineNumberWidth),
311: (float) (pageY + y));
312: Chunk.paintChunkList(chunks, gfx,
313: (float) (pageX + lineNumberWidth),
314: (float) (pageY + y), glyphVector);
315: }
316: }
317:
318: currentPhysicalLine++;
319: }
320: } //}}}
321:
322: //{{{ paintHeader() method
323: private double paintHeader(Graphics2D gfx, double pageX,
324: double pageY, double pageWidth, boolean actuallyPaint) {
325: String headerText = jEdit.getProperty("print.headerText",
326: new String[] { buffer.getName() });
327: FontRenderContext frc = gfx.getFontRenderContext();
328: lm = font.getLineMetrics(headerText, frc);
329:
330: Rectangle2D bounds = font.getStringBounds(headerText, frc);
331: Rectangle2D headerBounds = new Rectangle2D.Double(pageX, pageY,
332: pageWidth, bounds.getHeight());
333:
334: if (actuallyPaint) {
335: gfx.setColor(headerColor);
336: gfx.fill(headerBounds);
337: gfx.setColor(headerTextColor);
338: gfx
339: .drawString(headerText,
340: (float) (pageX + (pageWidth - bounds
341: .getWidth()) / 2),
342: (float) (pageY + lm.getAscent()));
343: }
344:
345: return headerBounds.getHeight();
346: }
347:
348: //}}}
349:
350: //{{{ paintFooter() method
351: private double paintFooter(Graphics2D gfx, double pageX,
352: double pageY, double pageWidth, double pageHeight,
353: int pageIndex, boolean actuallyPaint) {
354: String footerText = jEdit.getProperty("print.footerText",
355: new Object[] { new Date(),
356: Integer.valueOf(pageIndex + 1) });
357: FontRenderContext frc = gfx.getFontRenderContext();
358: lm = font.getLineMetrics(footerText, frc);
359:
360: Rectangle2D bounds = font.getStringBounds(footerText, frc);
361: Rectangle2D footerBounds = new Rectangle2D.Double(pageX, pageY
362: + pageHeight - bounds.getHeight(), pageWidth, bounds
363: .getHeight());
364:
365: if (actuallyPaint) {
366: gfx.setColor(footerColor);
367: gfx.fill(footerBounds);
368: gfx.setColor(footerTextColor);
369: gfx
370: .drawString(footerText,
371: (float) (pageX + (pageWidth - bounds
372: .getWidth()) / 2),
373: (float) (pageY + pageHeight
374: - bounds.getHeight() + lm
375: .getAscent()));
376: }
377:
378: return footerBounds.getHeight();
379: } //}}}
380:
381: //}}}
382:
383: //{{{ PrintTabExpander class
384: static class PrintTabExpander implements TabExpander {
385: private double tabWidth;
386:
387: //{{{ PrintTabExpander constructor
388: PrintTabExpander(double tabWidth) {
389: this .tabWidth = tabWidth;
390: } //}}}
391:
392: //{{{ nextTabStop() method
393: public float nextTabStop(float x, int tabOffset) {
394: int ntabs = (int) ((x + 1) / tabWidth);
395: return (float) ((ntabs + 1) * tabWidth);
396: } //}}}
397: } //}}}
398: }
|