]> code.delx.au - gnu-emacs/blob - lisp/ps-print.el
Use cl only at compile time.
[gnu-emacs] / lisp / ps-print.el
1 ;;; ps-print.el --- Jim's Pretty-Good PostScript Generator for Emacs 19.
2
3 ;; Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
4
5 ;; Author: Jim Thompson (was <thompson@wg2.waii.com>)
6 ;; Maintainer: Jacques Duthen <duthen@club-internet.fr>
7 ;; Keywords: print, PostScript
8 ;; Time-stamp: <97/01/09 13:52:08 duthen>
9 ;; Version: 3.04
10
11 (defconst ps-print-version "3.04"
12 "ps-print.el, v 3.04 <97/01/09 duthen>
13
14 Jack's last change version -- this file may have been edited as part of
15 Emacs without changes to the version number. When reporting bugs,
16 please also report the version of Emacs, if any, that ps-print was
17 distributed with.
18
19 Please send all bug fixes and enhancements to
20 Jacques Duthen <duthen@club-internet.fr>>.
21 ")
22
23 ;; This file is part of GNU Emacs.
24
25 ;; GNU Emacs is free software; you can redistribute it and/or modify
26 ;; it under the terms of the GNU General Public License as published by
27 ;; the Free Software Foundation; either version 2, or (at your option)
28 ;; any later version.
29
30 ;; GNU Emacs is distributed in the hope that it will be useful,
31 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
32 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 ;; GNU General Public License for more details.
34
35 ;; You should have received a copy of the GNU General Public License
36 ;; along with GNU Emacs; see the file COPYING. If not, write to the
37 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
38 ;; Boston, MA 02111-1307, USA.
39
40 ;;; Commentary:
41
42 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
43 ;;
44 ;; About ps-print
45 ;; --------------
46 ;;
47 ;; This package provides printing of Emacs buffers on PostScript
48 ;; printers; the buffer's bold and italic text attributes are
49 ;; preserved in the printer output. Ps-print is intended for use with
50 ;; Emacs 19 or Lucid Emacs, together with a fontifying package such as
51 ;; font-lock or hilit.
52 ;;
53 ;;
54 ;; Using ps-print
55 ;; --------------
56 ;;
57 ;; The Commands
58 ;;
59 ;; Ps-print provides eight commands for generating PostScript images
60 ;; of Emacs buffers:
61 ;;
62 ;; ps-print-buffer
63 ;; ps-print-buffer-with-faces
64 ;; ps-print-region
65 ;; ps-print-region-with-faces
66 ;; ps-spool-buffer
67 ;; ps-spool-buffer-with-faces
68 ;; ps-spool-region
69 ;; ps-spool-region-with-faces
70 ;;
71 ;; These commands all perform essentially the same function: they
72 ;; generate PostScript images suitable for printing on a PostScript
73 ;; printer or displaying with GhostScript. These commands are
74 ;; collectively referred to as "ps-print- commands".
75 ;;
76 ;; The word "print" or "spool" in the command name determines when the
77 ;; PostScript image is sent to the printer:
78 ;;
79 ;; print - The PostScript image is immediately sent to the
80 ;; printer;
81 ;;
82 ;; spool - The PostScript image is saved temporarily in an
83 ;; Emacs buffer. Many images may be spooled locally
84 ;; before printing them. To send the spooled images
85 ;; to the printer, use the command `ps-despool'.
86 ;;
87 ;; The spooling mechanism was designed for printing lots of small
88 ;; files (mail messages or netnews articles) to save paper that would
89 ;; otherwise be wasted on banner pages, and to make it easier to find
90 ;; your output at the printer (it's easier to pick up one 50-page
91 ;; printout than to find 50 single-page printouts).
92 ;;
93 ;; Ps-print has a hook in the `kill-emacs-hooks' so that you won't
94 ;; accidentally quit from Emacs while you have unprinted PostScript
95 ;; waiting in the spool buffer. If you do attempt to exit with
96 ;; spooled PostScript, you'll be asked if you want to print it, and if
97 ;; you decline, you'll be asked to confirm the exit; this is modeled
98 ;; on the confirmation that Emacs uses for modified buffers.
99 ;;
100 ;; The word "buffer" or "region" in the command name determines how
101 ;; much of the buffer is printed:
102 ;;
103 ;; buffer - Print the entire buffer.
104 ;;
105 ;; region - Print just the current region.
106 ;;
107 ;; The -with-faces suffix on the command name means that the command
108 ;; will include font, color, and underline information in the
109 ;; PostScript image, so the printed image can look as pretty as the
110 ;; buffer. The ps-print- commands without the -with-faces suffix
111 ;; don't include font, color, or underline information; images printed
112 ;; with these commands aren't as pretty, but are faster to generate.
113 ;;
114 ;; Two ps-print- command examples:
115 ;;
116 ;; ps-print-buffer - print the entire buffer,
117 ;; without font, color, or
118 ;; underline information, and
119 ;; send it immediately to the
120 ;; printer.
121 ;;
122 ;; ps-spool-region-with-faces - print just the current region;
123 ;; include font, color, and
124 ;; underline information, and
125 ;; spool the image in Emacs to
126 ;; send to the printer later.
127 ;;
128 ;;
129 ;; Invoking Ps-Print
130 ;; -----------------
131 ;;
132 ;; To print your buffer, type
133 ;;
134 ;; M-x ps-print-buffer
135 ;;
136 ;; or substitute one of the other seven ps-print- commands. The
137 ;; command will generate the PostScript image and print or spool it as
138 ;; specified. By giving the command a prefix argument
139 ;;
140 ;; C-u M-x ps-print-buffer
141 ;;
142 ;; it will save the PostScript image to a file instead of sending it
143 ;; to the printer; you will be prompted for the name of the file to
144 ;; save the image to. The prefix argument is ignored by the commands
145 ;; that spool their images, but you may save the spooled images to a
146 ;; file by giving a prefix argument to `ps-despool':
147 ;;
148 ;; C-u M-x ps-despool
149 ;;
150 ;; When invoked this way, `ps-despool' will prompt you for the name of
151 ;; the file to save to.
152 ;;
153 ;; Any of the `ps-print-' commands can be bound to keys; I recommend
154 ;; binding `ps-spool-buffer-with-faces', `ps-spool-region-with-faces',
155 ;; and `ps-despool'. Here are the bindings I use on my Sun 4 keyboard:
156 ;;
157 ;; (global-set-key 'f22 'ps-spool-buffer-with-faces) ;f22 is prsc
158 ;; (global-set-key '(shift f22) 'ps-spool-region-with-faces)
159 ;; (global-set-key '(control f22) 'ps-despool)
160 ;;
161 ;;
162 ;; The Printer Interface
163 ;; ---------------------
164 ;;
165 ;; The variables `ps-lpr-command' and `ps-lpr-switches' determine what
166 ;; command is used to send the PostScript images to the printer, and
167 ;; what arguments to give the command. These are analogous to
168 ;; `lpr-command' and `lpr-switches'.
169 ;; Make sure that they contain appropriate values for your system;
170 ;; see the usage notes below and the documentation of these variables.
171 ;;
172 ;; NOTE: `ps-lpr-command' and `ps-lpr-switches' take their initial values
173 ;; from the variables `lpr-command' and `lpr-switches'. If you have
174 ;; `lpr-command' set to invoke a pretty-printer such as `enscript',
175 ;; then ps-print won't work properly. `ps-lpr-command' must name
176 ;; a program that does not format the files it prints.
177 ;;
178 ;;
179 ;; The Page Layout
180 ;; ---------------
181 ;;
182 ;; All dimensions are floats in PostScript points.
183 ;; 1 inch == 2.54 cm == 72 points
184 ;; 1 cm == (/ 1 2.54) inch == (/ 72 2.54) points
185 ;;
186 ;; The variable `ps-paper-type' determines the size of paper ps-print
187 ;; formats for; it should contain one of the symbols:
188 ;; `a4' `a3' `letter' `legal' `letter-small' `tabloid'
189 ;; `ledger' `statement' `executive' `a4small' `b4' `b5'
190 ;;
191 ;; The variable `ps-landscape-mode' determines the orientation
192 ;; of the printing on the page:
193 ;; nil means `portrait' mode, non-nil means `landscape' mode.
194 ;; There is no oblique mode yet, though this is easy to do in ps.
195
196 ;; In landscape mode, the text is NOT scaled: you may print 70 lines
197 ;; in portrait mode and only 50 lignes in landscape mode.
198 ;; The margins represent margins in the printed paper:
199 ;; the top margin is the margin between the top of the page
200 ;; and the printed header, whatever the orientation is.
201 ;;
202 ;; The variable `ps-number-of-columns' determines the number of columns
203 ;; both in landscape and portrait mode.
204 ;; You can use:
205 ;; - (the standard) one column portrait mode
206 ;; - (my favorite) two columns landscape mode (which spares trees)
207 ;; but also
208 ;; - one column landscape mode for files with very long lines.
209 ;; - multi-column portrait or landscape mode
210 ;;
211 ;;
212 ;; Horizontal layout
213 ;; -----------------
214 ;;
215 ;; The horizontal layout is determined by the variables
216 ;; `ps-left-margin' `ps-inter-column' `ps-right-margin'
217 ;; as follows:
218 ;;
219 ;; ------------------------------------------
220 ;; | | | | | | | |
221 ;; | lm | text | ic | text | ic | text | rm |
222 ;; | | | | | | | |
223 ;; ------------------------------------------
224 ;;
225 ;; If `ps-number-of-columns' is 1, `ps-inter-column' is not relevant.
226 ;; Usually, lm = rm > 0 and ic = lm
227 ;; If (ic < 0), the text of adjacent columns can overlap.
228 ;;
229 ;;
230 ;; Vertical layout
231 ;; ---------------
232 ;;
233 ;; The vertical layout is determined by the variables
234 ;; `ps-bottom-margin' `ps-top-margin' `ps-header-offset'
235 ;; as follows:
236 ;;
237 ;; |--------| |--------|
238 ;; | tm | | tm |
239 ;; |--------| |--------|
240 ;; | header | | |
241 ;; |--------| | |
242 ;; | ho | | |
243 ;; |--------| or | text |
244 ;; | | | |
245 ;; | text | | |
246 ;; | | | |
247 ;; |--------| |--------|
248 ;; | bm | | bm |
249 ;; |--------| |--------|
250 ;;
251 ;; If `ps-print-header' is nil, `ps-header-offset' is not relevant.
252 ;; The margins represent margins in the printed paper:
253 ;; the top margin is the margin between the top of the page
254 ;; and the printed header, whatever the orientation is.
255 ;;
256 ;;
257 ;; Headers
258 ;; -------
259 ;;
260 ;; Ps-print can print headers at the top of each column; the default
261 ;; headers contain the following four items: on the left, the name of
262 ;; the buffer and, if the buffer is visiting a file, the file's
263 ;; directory; on the right, the page number and date of printing.
264 ;; The default headers look something like this:
265 ;;
266 ;; ps-print.el 1/21
267 ;; /home/jct/emacs-lisp/ps/new 94/12/31
268 ;;
269 ;; When printing on duplex printers, left and right are reversed so
270 ;; that the page numbers are toward the outside (cf. `ps-spool-duplex').
271 ;;
272 ;; Headers are configurable:
273 ;; To turn them off completely, set `ps-print-header' to nil.
274 ;; To turn off the header's gaudy framing box,
275 ;; set `ps-print-header-frame' to nil.
276 ;;
277 ;; The font family and size of text in the header are determined
278 ;; by the variables `ps-header-font-family', `ps-header-font-size' and
279 ;; `ps-header-title-font-size' (see below).
280 ;;
281 ;; The variable `ps-header-line-pad' determines the portion of a header
282 ;; title line height to insert between the header frame and the text
283 ;; it contains, both in the vertical and horizontal directions:
284 ;; .5 means half a line.
285
286 ;; Page numbers are printed in `n/m' format, indicating page n of m pages;
287 ;; to omit the total page count and just print the page number,
288 ;; set `ps-show-n-of-n' to nil.
289 ;;
290 ;; The amount of information in the header can be changed by changing
291 ;; the number of lines. To show less, set `ps-header-lines' to 1, and
292 ;; the header will show only the buffer name and page number. To show
293 ;; more, set `ps-header-lines' to 3, and the header will show the time of
294 ;; printing below the date.
295 ;;
296 ;; To change the content of the headers, change the variables
297 ;; `ps-left-header' and `ps-right-header'.
298 ;; These variables are lists, specifying top-to-bottom the text
299 ;; to display on the left or right side of the header.
300 ;; Each element of the list should be a string or a symbol.
301 ;; Strings are inserted directly into the PostScript arrays,
302 ;; and should contain the PostScript string delimiters '(' and ')'.
303 ;;
304 ;; Symbols in the header format lists can either represent functions
305 ;; or variables. Functions are called, and should return a string to
306 ;; show in the header. Variables should contain strings to display in
307 ;; the header. In either case, function or variable, the PostScript
308 ;; string delimiters are added by ps-print, and should not be part of
309 ;; the returned value.
310 ;;
311 ;; Here's an example: say we want the left header to display the text
312 ;;
313 ;; Moe
314 ;; Larry
315 ;; Curly
316 ;;
317 ;; where we have a function to return "Moe"
318 ;;
319 ;; (defun moe-func ()
320 ;; "Moe")
321 ;;
322 ;; a variable specifying "Larry"
323 ;;
324 ;; (setq larry-var "Larry")
325 ;;
326 ;; and a literal for "Curly". Here's how `ps-left-header' should be
327 ;; set:
328 ;;
329 ;; (setq ps-left-header (list 'moe-func 'larry-var "(Curly)"))
330 ;;
331 ;; Note that Curly has the PostScript string delimiters inside his
332 ;; quotes -- those aren't misplaced lisp delimiters!
333 ;; Without them, PostScript would attempt to call the undefined
334 ;; function Curly, which would result in a PostScript error.
335 ;; Since most printers don't report PostScript errors except by
336 ;; aborting the print job, this kind of error can be hard to track down.
337 ;; Consider yourself warned!
338 ;;
339 ;;
340 ;; Duplex Printers
341 ;; ---------------
342 ;;
343 ;; If you have a duplex-capable printer (one that prints both sides of
344 ;; the paper), set `ps-spool-duplex' to t.
345 ;; Ps-print will insert blank pages to make sure each buffer starts
346 ;; on the correct side of the paper.
347 ;; Don't forget to set `ps-lpr-switches' to select duplex printing
348 ;; for your printer.
349 ;;
350 ;;
351 ;; Font managing
352 ;; -------------
353 ;;
354 ;; Ps-print now knows rather precisely some fonts:
355 ;; the variable `ps-font-info-database' contains information
356 ;; for a list of font families (currently mainly `Courier' `Helvetica'
357 ;; `Times' `Palatino' `Helvetica-Narrow' `NewCenturySchlbk').
358 ;; Each font family contains the font names for standard, bold, italic
359 ;; and bold-italic characters, a reference size (usually 10) and the
360 ;; corresponding line height, width of a space and average character width.
361 ;;
362 ;; The variable `ps-font-family' determines which font family
363 ;; is to be used for ordinary text.
364 ;; If its value does not correspond to a known font family,
365 ;; an error message is printed into the `*Messages*' buffer,
366 ;; which lists the currently available font families.
367 ;;
368 ;; The variable `ps-font-size' determines the size (in points)
369 ;; of the font for ordinary text, when generating Postscript.
370 ;; Its value is a float.
371 ;;
372 ;; Similarly, the variable `ps-header-font-family' determines
373 ;; which font family is to be used for text in the header.
374 ;; The variable `ps-header-font-size' determines the font size,
375 ;; in points, for text in the header.
376 ;; The variable `ps-header-title-font-size' determines the font size,
377 ;; in points, for the top line of text in the header.
378 ;;
379 ;;
380 ;; Adding a new font family
381 ;; ------------------------
382 ;;
383 ;; To use a new font family, you MUST first teach ps-print
384 ;; this font, ie add its information to `ps-font-info-database',
385 ;; otherwise ps-print cannot correctly place line and page breaks.
386 ;;
387 ;; For example, assuming `Helvetica' is unkown,
388 ;; you first need to do the following ONLY ONCE:
389 ;;
390 ;; - create a new buffer
391 ;; - generate the PostScript image to a file (C-u M-x ps-print-buffer)
392 ;; - open this file and find the line:
393 ;; `% 3 cm 20 cm moveto 10 /Courier ReportFontInfo showpage'
394 ;; - delete the leading `%' (which is the Postscript comment character)
395 ;; - replace in this line `Courier' by the new font (say `Helvetica')
396 ;; to get the line:
397 ;; `3 cm 20 cm moveto 10 /Helvetica ReportFontInfo showpage'
398 ;; - send this file to the printer (or to ghostscript).
399 ;; You should read the following on the output page:
400 ;;
401 ;; For Helvetica 10 point, the line height is 11.56, the space width is 2.78
402 ;; and a crude estimate of average character width is 5.09243
403 ;;
404 ;; - Add these values to the `ps-font-info-database':
405 ;; (setq ps-font-info-database
406 ;; (append
407 ;; '((Helvetica ; the family name
408 ;; "Helvetica" "Helvetica-Bold" "Helvetica-Oblique" "Helvetica-BoldOblique"
409 ;; 10.0 11.56 2.78 5.09243))
410 ;; ps-font-info-database))
411 ;; - Now you can use this font family with any size:
412 ;; (setq ps-font-family 'Helvetica)
413 ;; - if you want to use this family in another emacs session, you must
414 ;; put into your `~/.emacs':
415 ;; (require 'ps-print)
416 ;; (setq ps-font-info-database (append ...)))
417 ;; if you don't want to load ps-print, you have to copy the whole value:
418 ;; (setq ps-font-info-database '(<your stuff> <the standard stuff>))
419 ;; or, if you can wait until the `ps-print-hook' is implemented, do:
420 ;; (add-hook 'ps-print-hook '(setq ps-font-info-database (append ...)))
421 ;; This does not work yet, since there is no `ps-print-hook' yet.
422 ;;
423 ;; You can create new `mixed' font families like:
424 ;; (my-mixed-family
425 ;; "Courier-Bold" "Helvetica"
426 ;; "Zapf-Chancery-MediumItalic" "NewCenturySchlbk-BoldItalic"
427 ;; 10.0 10.55 6.0 6.0)
428 ;; Now you can use your new font family with any size:
429 ;; (setq ps-font-family 'my-mixed-family)
430 ;;
431 ;; You can get information on all the fonts resident in YOUR printer
432 ;; by uncommenting the line:
433 ;; % 3 cm 20 cm moveto ReportAllFontInfo showpage
434 ;;
435 ;; The postscript file should be sent to YOUR postscript printer.
436 ;; If you send it to ghostscript or to another postscript printer,
437 ;; you may get slightly different results.
438 ;; Anyway, as ghostscript fonts are autoload, you won't get
439 ;; much font info.
440 ;;
441 ;;
442 ;; How Ps-Print Deals With Faces
443 ;; -----------------------------
444 ;;
445 ;; The ps-print-*-with-faces commands attempt to determine which faces
446 ;; should be printed in bold or italic, but their guesses aren't
447 ;; always right. For example, you might want to map colors into faces
448 ;; so that blue faces print in bold, and red faces in italic.
449 ;;
450 ;; It is possible to force ps-print to consider specific faces bold or
451 ;; italic, no matter what font they are displayed in, by setting the
452 ;; variables `ps-bold-faces' and `ps-italic-faces'. These variables
453 ;; contain lists of faces that ps-print should consider bold or
454 ;; italic; to set them, put code like the following into your .emacs
455 ;; file:
456 ;;
457 ;; (setq ps-bold-faces '(my-blue-face))
458 ;; (setq ps-italic-faces '(my-red-face))
459 ;;
460 ;; Faces like bold-italic that are both bold and italic should go in
461 ;; *both* lists.
462 ;;
463 ;; Ps-print keeps internal lists of which fonts are bold and which are
464 ;; italic; these lists are built the first time you invoke ps-print.
465 ;; For the sake of efficiency, the lists are built only once; the same
466 ;; lists are referred in later invocations of ps-print.
467 ;;
468 ;; Because these lists are built only once, it's possible for them to
469 ;; get out of sync, if a face changes, or if new faces are added. To
470 ;; get the lists back in sync, you can set the variable
471 ;; `ps-build-face-reference' to t, and the lists will be rebuilt the
472 ;; next time ps-print is invoked.
473 ;;
474 ;;
475 ;; How Ps-Print Deals With Color
476 ;; -----------------------------
477 ;;
478 ;; Ps-print detects faces with foreground and background colors
479 ;; defined and embeds color information in the PostScript image.
480 ;; The default foreground and background colors are defined by the
481 ;; variables `ps-default-fg' and `ps-default-bg'.
482 ;; On black-and-white printers, colors are displayed in grayscale.
483 ;; To turn off color output, set `ps-print-color-p' to nil.
484 ;;
485 ;;
486 ;; Utilities
487 ;; ---------
488 ;;
489 ;; Some tools are provided to help you customize your font setup.
490 ;;
491 ;; `ps-setup' returns (some part of) the current setup.
492 ;;
493 ;; To avoid wrapping too many lines, you may want to adjust the
494 ;; left and right margins and the font size. On UN*X systems, do:
495 ;; pr -t file | awk '{printf "%3d %s\n", length($0), $0}' | sort -r | head
496 ;; to determine the longest lines of your file.
497 ;; Then, the command `ps-line-lengths' will give you the correspondance
498 ;; between a line length (number of characters) and the maximum font
499 ;; size which doesn't wrap such a line with the current ps-print setup.
500 ;;
501 ;; The commands `ps-nb-pages-buffer' and `ps-nb-pages-region' display
502 ;; the correspondance between a number of pages and the maximum font
503 ;; size which allow the number of lines of the current buffer or of
504 ;; its current region to fit in this number of pages.
505 ;; Note: line folding is not taken into account in this process
506 ;; and could change the results.
507 ;;
508 ;;
509 ;; New since version 1.5
510 ;; ---------------------
511 ;;
512 ;; Color output capability.
513 ;; Automatic detection of font attributes (bold, italic).
514 ;; Configurable headers with page numbers.
515 ;; Slightly faster.
516 ;; Support for different paper sizes.
517 ;; Better conformance to PostScript Document Structure Conventions.
518 ;;
519 ;;
520 ;; New since version 2.8
521 ;; ---------------------
522 ;;
523 ;; [jack] 960517 Jacques Duthen <duthen@cegelec-red.fr>
524 ;;
525 ;; Font familiy and float size for text and header.
526 ;; Landscape mode.
527 ;; Multiple columns.
528 ;; Tools for page setup.
529 ;;
530 ;;
531 ;; Known bugs and limitations of ps-print:
532 ;; --------------------------------------
533 ;;
534 ;; Although color printing will work in XEmacs 19.12, it doesn't work
535 ;; well; in particular, bold or italic fonts don't print in the right
536 ;; background color.
537 ;;
538 ;; Invisible properties aren't correctly ignored in XEmacs 19.12.
539 ;;
540 ;; Automatic font-attribute detection doesn't work well, especially
541 ;; with hilit19 and older versions of get-create-face. Users having
542 ;; problems with auto-font detection should use the lists
543 ;; `ps-italic-faces' and `ps-bold-faces' and/or turn off automatic
544 ;; detection by setting `ps-auto-font-detect' to nil.
545 ;;
546 ;; Automatic font-attribute detection doesn't work with XEmacs 19.12
547 ;; in tty mode; use the lists `ps-italic-faces' and `ps-bold-faces'
548 ;; instead.
549 ;;
550 ;; Still too slow; could use some hand-optimization.
551 ;;
552 ;; ASCII Control characters other than tab, linefeed and pagefeed are
553 ;; not handled.
554 ;;
555 ;; Default background color isn't working.
556 ;;
557 ;; Faces are always treated as opaque.
558 ;;
559 ;; Epoch and Emacs 18 not supported. At all.
560 ;;
561 ;; Fixed-pitch fonts work better for line folding, but are not required.
562 ;;
563 ;; `ps-nb-pages-buffer' and `ps-nb-pages-region' don't take care
564 ;; of folding lines.
565 ;;
566 ;;
567 ;; Things to change:
568 ;; ----------------
569 ;;
570 ;; Add `ps-print-hook' (I don't know how to do that (yet!)).
571 ;; Add 4-up capability (really needed?).
572 ;; Add line numbers (should not be too hard).
573 ;; Add `ps-non-bold-faces' and `ps-non-italic-faces' (should be easy).
574 ;; Put one header per page over the columns (easy but needed?).
575 ;; Improve the memory management for big files (hard?).
576 ;; `ps-nb-pages-buffer' and `ps-nb-pages-region' should take care
577 ;; of folding lines.
578 ;;
579 ;;
580 ;; Acknowledgements
581 ;; ----------------
582 ;; Thanks to Jim Thompson <?@?> for the 2.8 version I started from.
583 ;; [jack]
584 ;;
585 ;; Thanks to Kevin Rodgers <kevinr@ihs.com> for adding support for
586 ;; color and the invisible property.
587 ;;
588 ;; Thanks to Avishai Yacobi, avishaiy@mcil.comm.mot.com, for writing
589 ;; the initial port to Emacs 19. His code is no longer part of
590 ;; ps-print, but his work is still appreciated.
591 ;;
592 ;; Thanks to Remi Houdaille and Michel Train, michel@metasoft.fdn.org,
593 ;; for adding underline support. Their code also is no longer part of
594 ;; ps-print, but their efforts are not forgotten.
595 ;;
596 ;; Thanks also to all of you who mailed code to add features to
597 ;; ps-print; although I didn't use your code, I still appreciate your
598 ;; sharing it with me.
599 ;;
600 ;; Thanks to all who mailed comments, encouragement, and criticism.
601 ;; Thanks also to all who responded to my survey; I had too many
602 ;; responses to reply to them all, but I greatly appreciate your
603 ;; interest.
604 ;;
605 ;; Jim
606 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
607
608 ;;; Code:
609
610 (eval-when-compile
611 (require 'cl))
612
613 (unless (featurep 'lisp-float-type)
614 (error "`ps-print' requires floating point support"))
615
616 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 ;; User Variables:
618
619 ;;; Interface to the command system
620
621 (defgroup ps-print nil
622 "Postscript generator for Emacs 19"
623 :prefix "ps-"
624 :group 'wp)
625
626 (defgroup ps-print-horizontal nil
627 "Horizontal page layout"
628 :prefix "ps-"
629 :tag "Horizontal"
630 :group 'ps-print)
631
632 (defgroup ps-print-vertical nil
633 "Vertical page layout"
634 :prefix "ps-"
635 :tag "Vertical"
636 :group 'ps-print)
637
638 (defgroup ps-print-header nil
639 "Headers layout"
640 :prefix "ps-"
641 :tag "Header"
642 :group 'ps-print)
643
644 (defgroup ps-print-font nil
645 "Fonts customization"
646 :prefix "ps-"
647 :tag "Font"
648 :group 'ps-print)
649
650 (defgroup ps-print-color nil
651 "Color customization"
652 :prefix "ps-"
653 :tag "Color"
654 :group 'ps-print)
655
656 (defgroup ps-print-face nil
657 "Faces customization"
658 :prefix "ps-"
659 :tag "PS Faces"
660 :group 'ps-print
661 :group 'faces)
662
663
664 (defcustom ps-lpr-command lpr-command
665 "*The shell command for printing a PostScript file."
666 :type 'string
667 :group 'ps-print)
668
669 (defcustom ps-lpr-switches lpr-switches
670 "*A list of extra switches to pass to `ps-lpr-command'."
671 :type '(repeat string)
672 :group 'ps-print)
673
674 ;;; Page layout
675
676 ;; All page dimensions are in PostScript points.
677 ;; 1 inch == 2.54 cm == 72 points
678 ;; 1 cm == (/ 1 2.54) inch == (/ 72 2.54) points
679
680 ;; Letter 8.5 inch x 11.0 inch
681 ;; Legal 8.5 inch x 14.0 inch
682 ;; A4 8.26 inch x 11.69 inch = 21.0 cm x 29.7 cm
683
684 ;; LetterSmall 7.68 inch x 10.16 inch
685 ;; Tabloid 11.0 inch x 17.0 inch
686 ;; Ledger 17.0 inch x 11.0 inch
687 ;; Statement 5.5 inch x 8.5 inch
688 ;; Executive 7.5 inch x 10.0 inch
689 ;; A3 11.69 inch x 16.5 inch = 29.7 cm x 42.0 cm
690 ;; A4Small 7.47 inch x 10.85 inch
691 ;; B4 10.125 inch x 14.33 inch
692 ;; B5 7.16 inch x 10.125 inch
693
694 (defcustom ps-page-dimensions-database
695 (list (list 'a4 (/ (* 72 21.0) 2.54) (/ (* 72 29.7) 2.54))
696 (list 'a3 (/ (* 72 29.7) 2.54) (/ (* 72 42.0) 2.54))
697 (list 'letter (* 72 8.5) (* 72 11.0))
698 (list 'legal (* 72 8.5) (* 72 14.0))
699 (list 'letter-small (* 72 7.68) (* 72 10.16))
700 (list 'tabloid (* 72 11.0) (* 72 17.0))
701 (list 'ledger (* 72 17.0) (* 72 11.0))
702 (list 'statement (* 72 5.5) (* 72 8.5))
703 (list 'executive (* 72 7.5) (* 72 10.0))
704 (list 'a4small (* 72 7.47) (* 72 10.85))
705 (list 'b4 (* 72 10.125) (* 72 14.33))
706 (list 'b5 (* 72 7.16) (* 72 10.125)))
707 "*List associating a symbolic paper type to its width and height.
708 see `ps-paper-type'."
709 :type '(repeat (list :tag "Paper Type"
710 (symbol :tag "Name")
711 (number :tag "Width")
712 (number :tag "Height")))
713 :group 'ps-print)
714
715 (defcustom ps-paper-type 'letter
716 "*Specifies the size of paper to format for.
717 Should be one of the paper types defined in `ps-page-dimensions-database', for
718 example `letter', `legal' or `a4'."
719 :type '(symbol :validate (lambda (wid)
720 (if (assq (widget-value wid) ps-page-dimensions-database)
721 nil
722 (widget-put wid :error "Unknown paper size")
723 wid)))
724 :group 'ps-print)
725
726 (defcustom ps-landscape-mode 'nil
727 "*Non-nil means print in landscape mode."
728 :type 'boolean
729 :group 'ps-print)
730
731 (defcustom ps-number-of-columns (if ps-landscape-mode 2 1)
732 "*Specifies the number of columns"
733 :type 'integer
734 :group 'ps-print)
735
736 ;;; Horizontal layout
737
738 ;; ------------------------------------------
739 ;; | | | | | | | |
740 ;; | lm | text | ic | text | ic | text | rm |
741 ;; | | | | | | | |
742 ;; ------------------------------------------
743
744 (defcustom ps-left-margin (/ (* 72 2.0) 2.54) ; 2 cm
745 "*Left margin in points (1/72 inch)."
746 :type 'number
747 :group 'ps-print-horizontal)
748
749 (defcustom ps-right-margin (/ (* 72 2.0) 2.54) ; 2 cm
750 "*Right margin in points (1/72 inch)."
751 :type 'number
752 :group 'ps-print-horizontal)
753
754 (defcustom ps-inter-column (/ (* 72 2.0) 2.54) ; 2 cm
755 "*Horizontal space between columns in points (1/72 inch)."
756 :type 'number
757 :group 'ps-print-horizontal)
758
759 ;;; Vertical layout
760
761 ;; |--------|
762 ;; | tm |
763 ;; |--------|
764 ;; | header |
765 ;; |--------|
766 ;; | ho |
767 ;; |--------|
768 ;; | text |
769 ;; |--------|
770 ;; | bm |
771 ;; |--------|
772
773 (defcustom ps-bottom-margin (/ (* 72 1.5) 2.54) ; 1.5 cm
774 "*Bottom margin in points (1/72 inch)."
775 :type 'number
776 :group 'ps-print-vertical)
777
778 (defcustom ps-top-margin (/ (* 72 1.5) 2.54) ; 1.5 cm
779 "*Top margin in points (1/72 inch)."
780 :type 'number
781 :group 'ps-print-vertical)
782
783 (defcustom ps-header-offset (/ (* 72 1.0) 2.54) ; 1.0 cm
784 "*Vertical space in points (1/72 inch) between the main text and the header."
785 :type 'number
786 :group 'ps-print-vertical)
787
788 (defcustom ps-header-line-pad 0.15
789 "*Portion of a header title line height to insert between the header frame
790 and the text it contains, both in the vertical and horizontal directions."
791 :type 'number
792 :group 'ps-print-vertical)
793
794 ;;; Header setup
795
796 (defcustom ps-print-header t
797 "*Non-nil means print a header at the top of each page.
798 By default, the header displays the buffer name, page number, and, if
799 the buffer is visiting a file, the file's directory. Headers are
800 customizable by changing variables `ps-left-header' and
801 `ps-right-header'."
802 :type 'boolean
803 :group 'ps-print-header)
804
805 (defcustom ps-print-header-frame t
806 "*Non-nil means draw a gaudy frame around the header."
807 :type 'boolean
808 :group 'ps-print-header)
809
810 (defcustom ps-header-lines 2
811 "*Number of lines to display in page header, when generating Postscript."
812 :type 'integer
813 :group 'ps-print-header)
814 (make-variable-buffer-local 'ps-header-lines)
815
816 (defcustom ps-show-n-of-n t
817 "*Non-nil means show page numbers as N/M, meaning page N of M.
818 Note: page numbers are displayed as part of headers, see variable
819 `ps-print-headers'."
820 :type 'boolean
821 :group 'ps-print-header)
822
823 (defcustom ps-spool-duplex nil ; Not many people have duplex
824 ; printers, so default to nil.
825 "*Non-nil indicates spooling is for a two-sided printer.
826 For a duplex printer, the `ps-spool-*' commands will insert blank pages
827 as needed between print jobs so that the next buffer printed will
828 start on the right page. Also, if headers are turned on, the headers
829 will be reversed on duplex printers so that the page numbers fall to
830 the left on even-numbered pages."
831 :type 'boolean
832 :group 'ps-print-header)
833
834 ;;; Fonts
835
836 (defcustom ps-font-info-database
837 '((Courier ; the family key
838 "Courier" "Courier-Bold" "Courier-Oblique" "Courier-BoldOblique"
839 10.0 10.55 6.0 6.0)
840 (Helvetica ; the family key
841 "Helvetica" "Helvetica-Bold" "Helvetica-Oblique" "Helvetica-BoldOblique"
842 10.0 11.56 2.78 5.09243)
843 (Times
844 "Times-Roman" "Times-Bold" "Times-Italic" "Times-BoldItalic"
845 10.0 11.0 2.5 4.71432)
846 (Palatino
847 "Palatino-Roman" "Palatino-Bold" "Palatino-Italic" "Palatino-BoldItalic"
848 10.0 12.1 2.5 5.08676)
849 (Helvetica-Narrow
850 "Helvetica-Narrow" "Helvetica-Narrow-Bold"
851 "Helvetica-Narrow-Oblique" "Helvetica-Narrow-BoldOblique"
852 10.0 11.56 2.2796 4.17579)
853 (NewCenturySchlbk
854 "NewCenturySchlbk-Roman" "NewCenturySchlbk-Bold"
855 "NewCenturySchlbk-Italic" "NewCenturySchlbk-BoldItalic"
856 10.0 12.15 2.78 5.31162)
857 ;; got no bold for the next ones
858 (AvantGarde-Book
859 "AvantGarde-Book" "AvantGarde-Book"
860 "AvantGarde-BookOblique" "AvantGarde-BookOblique"
861 10.0 11.77 2.77 5.45189)
862 (AvantGarde-Demi
863 "AvantGarde-Demi" "AvantGarde-Demi"
864 "AvantGarde-DemiOblique" "AvantGarde-DemiOblique"
865 10.0 12.72 2.8 5.51351)
866 (Bookman-Demi
867 "Bookman-Demi" "Bookman-Demi"
868 "Bookman-DemiItalic" "Bookman-DemiItalic"
869 10.0 11.77 3.4 6.05946)
870 (Bookman-Light
871 "Bookman-Light" "Bookman-Light"
872 "Bookman-LightItalic" "Bookman-LightItalic"
873 10.0 11.79 3.2 5.67027)
874 ;; got no bold and no italic for the next ones
875 (Symbol
876 "Symbol" "Symbol" "Symbol" "Symbol"
877 10.0 13.03 2.5 3.24324)
878 (Zapf-Dingbats
879 "Zapf-Dingbats" "Zapf-Dingbats" "Zapf-Dingbats" "Zapf-Dingbats"
880 10.0 9.63 2.78 2.78)
881 (Zapf-Chancery-MediumItalic
882 "Zapf-Chancery-MediumItalic" "Zapf-Chancery-MediumItalic"
883 "Zapf-Chancery-MediumItalic" "Zapf-Chancery-MediumItalic"
884 10.0 11.45 2.2 4.10811)
885 )
886 "*Font info database: font family (the key), name, bold, italic, bold-italic,
887 reference size, line height, space width, average character width.
888 To get the info for another specific font (say Helvetica), do the following:
889 - create a new buffer
890 - generate the PostScript image to a file (C-u M-x ps-print-buffer)
891 - open this file and delete the leading `%' (which is the Postscript
892 comment character) from the line
893 `% 3 cm 20 cm moveto 10 /Courier ReportFontInfo showpage'
894 to get the line
895 `3 cm 20 cm moveto 10 /Helvetica ReportFontInfo showpage'
896 - add the values to `ps-font-info-database'.
897 You can get all the fonts of YOUR printer using `ReportAllFontInfo'."
898 :type '(repeat (list :tag "Font Definition"
899 (symbol :tag "Font")
900 (string :tag "Name")
901 (string :tag "Bold")
902 (string :tag "Italic")
903 (string :tag "Bold-Italic")
904 (number :tag "Reference Size")
905 (number :tag "Line Height")
906 (number :tag "Space Width")
907 (number :tag "Average Character Width")))
908 :group 'ps-print-font)
909
910 (defcustom ps-font-family 'Courier
911 "Font family name for ordinary text, when generating Postscript."
912 :type 'symbol
913 :group 'ps-print-font)
914
915 (defcustom ps-font-size (if ps-landscape-mode 7 8.5)
916 "Font size, in points, for ordinary text, when generating Postscript."
917 :type 'number
918 :group 'ps-print-font)
919
920 (defcustom ps-header-font-family 'Helvetica
921 "Font family name for text in the header, when generating Postscript."
922 :type 'symbol
923 :group 'ps-print-font)
924
925 (defcustom ps-header-font-size (if ps-landscape-mode 10 12)
926 "Font size, in points, for text in the header, when generating Postscript."
927 :type 'number
928 :group 'ps-print-font)
929
930 (defcustom ps-header-title-font-size (if ps-landscape-mode 12 14)
931 "Font size, in points, for the top line of text in the header,
932 when generating Postscript."
933 :type 'number
934 :group 'ps-print-font)
935
936 ;;; Colors
937
938 (defcustom ps-print-color-p (or (fboundp 'x-color-values) ; Emacs
939 (fboundp 'pixel-components)) ; XEmacs
940 ; xemacs
941 ; Printing color requires x-color-values.
942 "*If non-nil, print the buffer's text in color."
943 :type 'boolean
944 :group 'ps-print-color)
945
946 (defcustom ps-default-fg '(0.0 0.0 0.0)
947 "*RGB values of the default foreground color. Defaults to black."
948 :type '(list (number :tag "Red") (number :tag "Green") (number :tag "Blue"))
949 :group 'ps-print-color)
950
951 (defcustom ps-default-bg '(1.0 1.0 1.0)
952 "*RGB values of the default background color. Defaults to white."
953 :type '(list (number :tag "Red") (number :tag "Green") (number :tag "Blue"))
954 :group 'ps-print-color)
955
956 (defcustom ps-auto-font-detect t
957 "*Non-nil means automatically detect bold/italic face attributes.
958 nil means rely solely on the lists `ps-bold-faces', `ps-italic-faces',
959 and `ps-underlined-faces'."
960 :type 'boolean
961 :group 'ps-print-font)
962
963 (defcustom ps-bold-faces
964 (unless ps-print-color-p
965 '(font-lock-function-name-face
966 font-lock-builtin-face
967 font-lock-variable-name-face
968 font-lock-keyword-face
969 font-lock-warning-face))
970 "*A list of the \(non-bold\) faces that should be printed in bold font.
971 This applies to generating Postscript."
972 :type '(repeat face)
973 :group 'ps-print-face)
974
975 (defcustom ps-italic-faces
976 (unless ps-print-color-p
977 '(font-lock-variable-name-face
978 font-lock-string-face
979 font-lock-comment-face
980 font-lock-warning-face))
981 "*A list of the \(non-italic\) faces that should be printed in italic font.
982 This applies to generating Postscript."
983 :type '(repeat face)
984 :group 'ps-print-face)
985
986 (defcustom ps-underlined-faces
987 (unless ps-print-color-p
988 '(font-lock-function-name-face
989 font-lock-type-face
990 font-lock-reference-face
991 font-lock-warning-face))
992 "*A list of the \(non-underlined\) faces that should be printed underlined.
993 This applies to generating Postscript."
994 :type '(repeat face)
995 :group 'ps-print-face)
996
997 (defcustom ps-left-header
998 (list 'ps-get-buffer-name 'ps-header-dirpart)
999 "*The items to display (each on a line) on the left part of the page header.
1000 This applies to generating Postscript.
1001
1002 The value should be a list of strings and symbols, each representing an
1003 entry in the PostScript array HeaderLinesLeft.
1004
1005 Strings are inserted unchanged into the array; those representing
1006 PostScript string literals should be delimited with PostScript string
1007 delimiters '(' and ')'.
1008
1009 For symbols with bound functions, the function is called and should
1010 return a string to be inserted into the array. For symbols with bound
1011 values, the value should be a string to be inserted into the array.
1012 In either case, function or variable, the string value has PostScript
1013 string delimiters added to it."
1014 :type '(repeat (choice string symbol))
1015 :group ps-print-header)
1016 (make-variable-buffer-local 'ps-left-header)
1017
1018 (defcustom ps-right-header
1019 (list "/pagenumberstring load" 'time-stamp-mon-dd-yyyy 'time-stamp-hh:mm:ss)
1020 "*The items to display (each on a line) on the right part of the page header.
1021 This applies to generating Postscript.
1022
1023 See the variable `ps-left-header' for a description of the format of
1024 this variable."
1025 :type '(repeat (choice string symbol))
1026 :group ps-print-header)
1027 (make-variable-buffer-local 'ps-right-header)
1028
1029 (defcustom ps-razzle-dazzle t
1030 "*Non-nil means report progress while formatting buffer."
1031 :type 'boolean
1032 :group 'ps-print)
1033
1034 (defvar ps-adobe-tag "%!PS-Adobe-1.0\n"
1035 "*Contains the header line identifying the output as PostScript.
1036 By default, `ps-adobe-tag' contains the standard identifier. Some
1037 printers require slightly different versions of this line.")
1038
1039 (defcustom ps-build-face-reference t
1040 "*Non-nil means build the reference face lists.
1041
1042 Ps-print sets this value to nil after it builds its internal reference
1043 lists of bold and italic faces. By settings its value back to t, you
1044 can force ps-print to rebuild the lists the next time you invoke one
1045 of the ...-with-faces commands.
1046
1047 You should set this value back to t after you change the attributes of
1048 any face, or create new faces. Most users shouldn't have to worry
1049 about its setting, though."
1050 :type 'boolean
1051 :group 'ps-print-face)
1052
1053 (defcustom ps-always-build-face-reference nil
1054 "*Non-nil means always rebuild the reference face lists.
1055
1056 If this variable is non-nil, ps-print will rebuild its internal
1057 reference lists of bold and italic faces *every* time one of the
1058 -with-faces commands is called. Most users shouldn't need to set this
1059 variable."
1060 :type 'boolean
1061 :group 'ps-print-face)
1062
1063 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1064 ;; User commands
1065
1066 ;;;###autoload
1067 (defun ps-print-buffer (&optional filename)
1068 "Generate and print a PostScript image of the buffer.
1069
1070 When called with a numeric prefix argument (C-u), prompts the user for
1071 the name of a file to save the PostScript image in, instead of sending
1072 it to the printer.
1073
1074 More specifically, the FILENAME argument is treated as follows: if it
1075 is nil, send the image to the printer. If FILENAME is a string, save
1076 the PostScript image in a file with that name. If FILENAME is a
1077 number, prompt the user for the name of the file to save in."
1078
1079 (interactive (list (ps-print-preprint current-prefix-arg)))
1080 (ps-generate (current-buffer) (point-min) (point-max)
1081 'ps-generate-postscript)
1082 (ps-do-despool filename))
1083
1084
1085 ;;;###autoload
1086 (defun ps-print-buffer-with-faces (&optional filename)
1087 "Generate and print a PostScript image of the buffer.
1088 Like `ps-print-buffer', but includes font, color, and underline
1089 information in the generated image. This command works only if you
1090 are using a window system, so it has a way to determine color values."
1091 (interactive (list (ps-print-preprint current-prefix-arg)))
1092 (ps-generate (current-buffer) (point-min) (point-max)
1093 'ps-generate-postscript-with-faces)
1094 (ps-do-despool filename))
1095
1096
1097 ;;;###autoload
1098 (defun ps-print-region (from to &optional filename)
1099 "Generate and print a PostScript image of the region.
1100 Like `ps-print-buffer', but prints just the current region."
1101
1102 (interactive (list (point) (mark) (ps-print-preprint current-prefix-arg)))
1103 (ps-generate (current-buffer) from to
1104 'ps-generate-postscript)
1105 (ps-do-despool filename))
1106
1107
1108 ;;;###autoload
1109 (defun ps-print-region-with-faces (from to &optional filename)
1110 "Generate and print a PostScript image of the region.
1111 Like `ps-print-region', but includes font, color, and underline
1112 information in the generated image. This command works only if you
1113 are using a window system, so it has a way to determine color values."
1114
1115 (interactive (list (point) (mark) (ps-print-preprint current-prefix-arg)))
1116 (ps-generate (current-buffer) from to
1117 'ps-generate-postscript-with-faces)
1118 (ps-do-despool filename))
1119
1120
1121 ;;;###autoload
1122 (defun ps-spool-buffer ()
1123 "Generate and spool a PostScript image of the buffer.
1124 Like `ps-print-buffer' except that the PostScript image is saved in a
1125 local buffer to be sent to the printer later.
1126
1127 Use the command `ps-despool' to send the spooled images to the printer."
1128 (interactive)
1129 (ps-generate (current-buffer) (point-min) (point-max)
1130 'ps-generate-postscript))
1131
1132
1133 ;;;###autoload
1134 (defun ps-spool-buffer-with-faces ()
1135 "Generate and spool a PostScript image of the buffer.
1136 Like `ps-spool-buffer', but includes font, color, and underline
1137 information in the generated image. This command works only if you
1138 are using a window system, so it has a way to determine color values.
1139
1140 Use the command `ps-despool' to send the spooled images to the printer."
1141
1142 (interactive)
1143 (ps-generate (current-buffer) (point-min) (point-max)
1144 'ps-generate-postscript-with-faces))
1145
1146
1147 ;;;###autoload
1148 (defun ps-spool-region (from to)
1149 "Generate a PostScript image of the region and spool locally.
1150 Like `ps-spool-buffer', but spools just the current region.
1151
1152 Use the command `ps-despool' to send the spooled images to the printer."
1153 (interactive "r")
1154 (ps-generate (current-buffer) from to
1155 'ps-generate-postscript))
1156
1157
1158 ;;;###autoload
1159 (defun ps-spool-region-with-faces (from to)
1160 "Generate a PostScript image of the region and spool locally.
1161 Like `ps-spool-region', but includes font, color, and underline
1162 information in the generated image. This command works only if you
1163 are using a window system, so it has a way to determine color values.
1164
1165 Use the command `ps-despool' to send the spooled images to the printer."
1166 (interactive "r")
1167 (ps-generate (current-buffer) from to
1168 'ps-generate-postscript-with-faces))
1169
1170 ;;;###autoload
1171 (defun ps-despool (&optional filename)
1172 "Send the spooled PostScript to the printer.
1173
1174 When called with a numeric prefix argument (C-u), prompt the user for
1175 the name of a file to save the spooled PostScript in, instead of sending
1176 it to the printer.
1177
1178 More specifically, the FILENAME argument is treated as follows: if it
1179 is nil, send the image to the printer. If FILENAME is a string, save
1180 the PostScript image in a file with that name. If FILENAME is a
1181 number, prompt the user for the name of the file to save in."
1182 (interactive (list (ps-print-preprint current-prefix-arg)))
1183 (ps-do-despool filename))
1184
1185 ;;;###autoload
1186 (defun ps-line-lengths ()
1187 "*Display the correspondance between a line length and a font size,
1188 using the current ps-print setup.
1189 Try: pr -t file | awk '{printf \"%3d %s\n\", length($0), $0}' | sort -r | head"
1190 (interactive)
1191 (ps-line-lengths-internal))
1192
1193 ;;;###autoload
1194 (defun ps-nb-pages-buffer (nb-lines)
1195 "*Display an approximate correspondance between a font size and the number
1196 of pages the current buffer would require to print
1197 using the current ps-print setup."
1198 (interactive (list (count-lines (point-min) (point-max))))
1199 (ps-nb-pages nb-lines))
1200
1201 ;;;###autoload
1202 (defun ps-nb-pages-region (nb-lines)
1203 "*Display an approximate correspondance between a font size and the number
1204 of pages the current region would require to print
1205 using the current ps-print setup."
1206 (interactive (list (count-lines (mark) (point))))
1207 (ps-nb-pages nb-lines))
1208
1209 ;;;###autoload
1210 (defun ps-setup ()
1211 "*Return the current setup"
1212 (format "
1213 (setq ps-print-color-p %s
1214 ps-lpr-command \"%s\"
1215 ps-lpr-switches %s
1216
1217 ps-paper-type '%s
1218 ps-landscape-mode %s
1219 ps-number-of-columns %s
1220
1221 ps-left-margin %s
1222 ps-right-margin %s
1223 ps-inter-column %s
1224 ps-bottom-margin %s
1225 ps-top-margin %s
1226 ps-header-offset %s
1227 ps-header-line-pad %s
1228 ps-print-header %s
1229 ps-print-header-frame %s
1230 ps-header-lines %s
1231 ps-show-n-of-n %s
1232 ps-spool-duplex %s
1233
1234 ps-font-family '%s
1235 ps-font-size %s
1236 ps-header-font-family '%s
1237 ps-header-font-size %s
1238 ps-header-title-font-size %s)
1239 "
1240 ps-print-color-p
1241 ps-lpr-command
1242 ps-lpr-switches
1243 ps-paper-type
1244 ps-landscape-mode
1245 ps-number-of-columns
1246 ps-left-margin
1247 ps-right-margin
1248 ps-inter-column
1249 ps-bottom-margin
1250 ps-top-margin
1251 ps-header-offset
1252 ps-header-line-pad
1253 ps-print-header
1254 ps-print-header-frame
1255 ps-header-lines
1256 ps-show-n-of-n
1257 ps-spool-duplex
1258 ps-font-family
1259 ps-font-size
1260 ps-header-font-family
1261 ps-header-font-size
1262 ps-header-title-font-size))
1263
1264 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1265 ;; Utility functions and variables:
1266
1267 (defvar ps-print-emacs-type
1268 (cond ((string-match "XEmacs" emacs-version) 'xemacs)
1269 ((string-match "Lucid" emacs-version) 'lucid)
1270 ((string-match "Epoch" emacs-version) 'epoch)
1271 (t 'emacs)))
1272
1273 (if (or (eq ps-print-emacs-type 'lucid)
1274 (eq ps-print-emacs-type 'xemacs))
1275 (if (< emacs-minor-version 12)
1276 (setq ps-print-color-p nil))
1277 (require 'faces)) ; face-font, face-underline-p,
1278 ; x-font-regexp
1279
1280 (require 'time-stamp)
1281
1282 (defvar ps-font nil
1283 "Font family name for ordinary text, when generating Postscript.")
1284
1285 (defvar ps-font-bold nil
1286 "Font family name for bold text, when generating Postscript.")
1287
1288 (defvar ps-font-italic nil
1289 "Font family name for italic text, when generating Postscript.")
1290
1291 (defvar ps-font-bold-italic nil
1292 "Font family name for bold italic text, when generating Postscript.")
1293
1294 (defvar ps-avg-char-width nil
1295 "The average width, in points, of a character, for generating Postscript.
1296 This is the value that ps-print uses to determine the length,
1297 x-dimension, of the text it has printed, and thus affects the point at
1298 which long lines wrap around.")
1299
1300 (defvar ps-space-width nil
1301 "The width of a space character, for generating Postscript.
1302 This value is used in expanding tab characters.")
1303
1304 (defvar ps-line-height nil
1305 "The height of a line, for generating Postscript.
1306 This is the value that ps-print uses to determine the height,
1307 y-dimension, of the lines of text it has printed, and thus affects the
1308 point at which page-breaks are placed.
1309 The line-height is *not* the same as the point size of the font.")
1310
1311 (defvar ps-print-prologue-1
1312 "% ISOLatin1Encoding stolen from ps_init.ps in GhostScript 2.6.1.4:
1313 /ISOLatin1Encoding where { pop } {
1314 % -- The ISO Latin-1 encoding vector isn't known, so define it.
1315 % -- The first half is the same as the standard encoding,
1316 % -- except for minus instead of hyphen at code 055.
1317 /ISOLatin1Encoding
1318 StandardEncoding 0 45 getinterval aload pop
1319 /minus
1320 StandardEncoding 46 82 getinterval aload pop
1321 %*** NOTE: the following are missing in the Adobe documentation,
1322 %*** but appear in the displayed table:
1323 %*** macron at 0225, dieresis at 0230, cedilla at 0233, space at 0240.
1324 % 0200 (128)
1325 /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
1326 /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
1327 /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
1328 /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron
1329 % 0240 (160)
1330 /space /exclamdown /cent /sterling
1331 /currency /yen /brokenbar /section
1332 /dieresis /copyright /ordfeminine /guillemotleft
1333 /logicalnot /hyphen /registered /macron
1334 /degree /plusminus /twosuperior /threesuperior
1335 /acute /mu /paragraph /periodcentered
1336 /cedilla /onesuperior /ordmasculine /guillemotright
1337 /onequarter /onehalf /threequarters /questiondown
1338 % 0300 (192)
1339 /Agrave /Aacute /Acircumflex /Atilde
1340 /Adieresis /Aring /AE /Ccedilla
1341 /Egrave /Eacute /Ecircumflex /Edieresis
1342 /Igrave /Iacute /Icircumflex /Idieresis
1343 /Eth /Ntilde /Ograve /Oacute
1344 /Ocircumflex /Otilde /Odieresis /multiply
1345 /Oslash /Ugrave /Uacute /Ucircumflex
1346 /Udieresis /Yacute /Thorn /germandbls
1347 % 0340 (224)
1348 /agrave /aacute /acircumflex /atilde
1349 /adieresis /aring /ae /ccedilla
1350 /egrave /eacute /ecircumflex /edieresis
1351 /igrave /iacute /icircumflex /idieresis
1352 /eth /ntilde /ograve /oacute
1353 /ocircumflex /otilde /odieresis /divide
1354 /oslash /ugrave /uacute /ucircumflex
1355 /udieresis /yacute /thorn /ydieresis
1356 256 packedarray def
1357 } ifelse
1358
1359 /reencodeFontISO { %def
1360 dup
1361 length 5 add dict % Make a new font (a new dict the same size
1362 % as the old one) with room for our new symbols.
1363
1364 begin % Make the new font the current dictionary.
1365
1366
1367 { 1 index /FID ne
1368 { def } { pop pop } ifelse
1369 } forall % Copy each of the symbols from the old dictionary
1370 % to the new one except for the font ID.
1371
1372 /Encoding ISOLatin1Encoding def % Override the encoding with
1373 % the ISOLatin1 encoding.
1374
1375 % Use the font's bounding box to determine the ascent, descent,
1376 % and overall height; don't forget that these values have to be
1377 % transformed using the font's matrix.
1378
1379 % ^ (x2 y2)
1380 % | |
1381 % | v
1382 % | +----+ - -
1383 % | | | ^
1384 % | | | | Ascent (usually > 0)
1385 % | | | |
1386 % (0 0) -> +--+----+-------->
1387 % | | |
1388 % | | v Descent (usually < 0)
1389 % (x1 y1) --> +----+ - -
1390
1391 FontBBox % -- x1 y1 x2 y2
1392 FontMatrix transform /Ascent exch def pop
1393 FontMatrix transform /Descent exch def pop
1394 /FontHeight Ascent Descent sub def % use `sub' because descent < 0
1395
1396 % Define these in case they're not in the FontInfo
1397 % (also, here they're easier to get to.
1398 /UnderlinePosition 1 def
1399 /UnderlineThickness 1 def
1400
1401 % Get the underline position and thickness if they're defined.
1402 currentdict /FontInfo known {
1403 FontInfo
1404
1405 dup /UnderlinePosition known {
1406 dup /UnderlinePosition get
1407 0 exch FontMatrix transform exch pop
1408 /UnderlinePosition exch def
1409 } if
1410
1411 dup /UnderlineThickness known {
1412 /UnderlineThickness get
1413 0 exch FontMatrix transform exch pop
1414 /UnderlineThickness exch def
1415 } if
1416
1417 } if
1418
1419 currentdict % Leave the new font on the stack
1420 end % Stop using the font as the current dictionary.
1421 definefont % Put the font into the font dictionary
1422 pop % Discard the returned font.
1423 } bind def
1424
1425 /DefFont { % Font definition
1426 findfont exch scalefont reencodeFontISO
1427 } def
1428
1429 /F { % Font selection
1430 findfont
1431 dup /Ascent get /Ascent exch def
1432 dup /Descent get /Descent exch def
1433 dup /FontHeight get /FontHeight exch def
1434 dup /UnderlinePosition get /UnderlinePosition exch def
1435 dup /UnderlineThickness get /UnderlineThickness exch def
1436 setfont
1437 } def
1438
1439 /FG /setrgbcolor load def
1440
1441 /bg false def
1442 /BG {
1443 dup /bg exch def
1444 { mark 4 1 roll ] /bgcolor exch def } if
1445 } def
1446
1447 % B width C
1448 % +-----------+
1449 % | Ascent (usually > 0)
1450 % A + +
1451 % | Descent (usually < 0)
1452 % +-----------+
1453 % E width D
1454
1455 /dobackground { % width --
1456 currentpoint % -- width x y
1457 gsave
1458 newpath
1459 moveto % A (x y)
1460 0 Ascent rmoveto % B
1461 dup 0 rlineto % C
1462 0 Descent Ascent sub rlineto % D
1463 neg 0 rlineto % E
1464 closepath
1465 bgcolor aload pop setrgbcolor
1466 fill
1467 grestore
1468 } def
1469
1470 /dobackgroundstring { % string --
1471 stringwidth pop
1472 dobackground
1473 } def
1474
1475 /dounderline { % fromx fromy --
1476 currentpoint
1477 gsave
1478 UnderlineThickness setlinewidth
1479 4 2 roll
1480 UnderlinePosition add moveto
1481 UnderlinePosition add lineto
1482 stroke
1483 grestore
1484 } def
1485
1486 /eolbg { % dobackground until right margin
1487 PrintWidth % -- x-eol
1488 currentpoint pop % -- cur-x
1489 sub % -- width until eol
1490 dobackground
1491 } def
1492
1493 /eolul { % idem for underline
1494 PrintWidth % -- x-eol
1495 currentpoint exch pop % -- x-eol cur-y
1496 dounderline
1497 } def
1498
1499 /SL { % Soft Linefeed
1500 bg { eolbg } if
1501 ul { eolul } if
1502 0 currentpoint exch pop LineHeight sub moveto
1503 } def
1504
1505 /HL /SL load def % Hard Linefeed
1506
1507 /sp1 { currentpoint 3 -1 roll } def
1508
1509 % Some debug
1510 /dcp { currentpoint exch 40 string cvs print (, ) print = } def
1511 /dp { print 2 copy
1512 exch 40 string cvs print (, ) print = } def
1513
1514 /S {
1515 bg { dup dobackgroundstring } if
1516 ul { sp1 } if
1517 show
1518 ul { dounderline } if
1519 } def
1520
1521 /W {
1522 ul { sp1 } if
1523 ( ) stringwidth % Get the width of a space in the current font.
1524 pop % Discard the Y component.
1525 mul % Multiply the width of a space
1526 % by the number of spaces to plot
1527 bg { dup dobackground } if
1528 0 rmoveto
1529 ul { dounderline } if
1530 } def
1531
1532 /BeginDoc {
1533 % ---- save the state of the document (useful for ghostscript!)
1534 /docState save def
1535 % ---- [jack] Kludge: my ghostscript window is 21x27.7 instead of 21x29.7
1536 /JackGhostscript where {
1537 pop 1 27.7 29.7 div scale
1538 } if
1539 LandscapeMode {
1540 % ---- translate to bottom-right corner of Portrait page
1541 LandscapePageHeight 0 translate
1542 90 rotate
1543 } if
1544 /ColumnWidth PrintWidth InterColumn add def
1545 % ---- translate to lower left corner of TEXT
1546 LeftMargin BottomMargin translate
1547 % ---- define where printing will start
1548 /f0 F % this installs Ascent
1549 /PrintStartY PrintHeight Ascent sub def
1550 /ColumnIndex 1 def
1551 } def
1552
1553 /EndDoc {
1554 % ---- on last page but not last column, spit out the page
1555 ColumnIndex 1 eq not { showpage } if
1556 % ---- restore the state of the document (useful for ghostscript!)
1557 docState restore
1558 } def
1559
1560 /BeginDSCPage {
1561 % ---- when 1st column, save the state of the page
1562 ColumnIndex 1 eq { /pageState save def } if
1563 % ---- save the state of the column
1564 /columnState save def
1565 } def
1566
1567 /BeginPage {
1568 PrintHeader {
1569 PrintHeaderFrame { HeaderFrame } if
1570 HeaderText
1571 } if
1572 0 PrintStartY moveto % move to where printing will start
1573 } def
1574
1575 /EndPage {
1576 bg { eolbg } if
1577 ul { eolul } if
1578 } def
1579
1580 /EndDSCPage {
1581 ColumnIndex NumberOfColumns eq {
1582 % ---- on last column, spit out the page
1583 showpage
1584 % ---- restore the state of the page
1585 pageState restore
1586 /ColumnIndex 1 def
1587 } { % else
1588 % ---- restore the state of the current column
1589 columnState restore
1590 % ---- and translate to the next column
1591 ColumnWidth 0 translate
1592 /ColumnIndex ColumnIndex 1 add def
1593 } ifelse
1594 } def
1595
1596 /ul false def
1597
1598 /UL { /ul exch def } def
1599
1600 /SetHeaderLines { % nb-lines --
1601 /HeaderLines exch def
1602 % ---- bottom up
1603 HeaderPad
1604 HeaderLines 1 sub HeaderLineHeight mul add
1605 HeaderTitleLineHeight add
1606 HeaderPad add
1607 /HeaderHeight exch def
1608 } def
1609
1610 % |---------|
1611 % | tm |
1612 % |---------|
1613 % | header |
1614 % |-+-------| <-- (x y)
1615 % | ho |
1616 % |---------|
1617 % | text |
1618 % |-+-------| <-- (0 0)
1619 % | bm |
1620 % |---------|
1621
1622 /HeaderFrameStart { % -- x y
1623 0 PrintHeight HeaderOffset add
1624 } def
1625
1626 /HeaderFramePath {
1627 PrintWidth 0 rlineto
1628 0 HeaderHeight rlineto
1629 PrintWidth neg 0 rlineto
1630 0 HeaderHeight neg rlineto
1631 } def
1632
1633 /HeaderFrame {
1634 gsave
1635 0.4 setlinewidth
1636 % ---- fill a black rectangle (the shadow of the next one)
1637 HeaderFrameStart moveto
1638 1 -1 rmoveto
1639 HeaderFramePath
1640 0 setgray fill
1641 % ---- do the next rectangle ...
1642 HeaderFrameStart moveto
1643 HeaderFramePath
1644 gsave 0.9 setgray fill grestore % filled with grey
1645 gsave 0 setgray stroke grestore % drawn with black
1646 grestore
1647 } def
1648
1649 /HeaderStart {
1650 HeaderFrameStart
1651 exch HeaderPad add exch % horizontal pad
1652 % ---- bottom up
1653 HeaderPad add % vertical pad
1654 HeaderDescent sub
1655 HeaderLineHeight HeaderLines 1 sub mul add
1656 } def
1657
1658 /strcat {
1659 dup length 3 -1 roll dup length dup 4 -1 roll add string dup
1660 0 5 -1 roll putinterval
1661 dup 4 2 roll exch putinterval
1662 } def
1663
1664 /pagenumberstring {
1665 PageNumber 32 string cvs
1666 ShowNofN {
1667 (/) strcat
1668 PageCount 32 string cvs strcat
1669 } if
1670 } def
1671
1672 /HeaderText {
1673 HeaderStart moveto
1674
1675 HeaderLinesRight HeaderLinesLeft % -- rightLines leftLines
1676
1677 % ---- hack: `PN 1 and' == `PN 2 modulo'
1678
1679 % ---- if duplex and even page number, then exchange left and right
1680 Duplex PageNumber 1 and 0 eq and { exch } if
1681
1682 { % ---- process the left lines
1683 aload pop
1684 exch F
1685 gsave
1686 dup xcheck { exec } if
1687 show
1688 grestore
1689 0 HeaderLineHeight neg rmoveto
1690 } forall
1691
1692 HeaderStart moveto
1693
1694 { % ---- process the right lines
1695 aload pop
1696 exch F
1697 gsave
1698 dup xcheck { exec } if
1699 dup stringwidth pop
1700 PrintWidth exch sub HeaderPad 2 mul sub 0 rmoveto
1701 show
1702 grestore
1703 0 HeaderLineHeight neg rmoveto
1704 } forall
1705 } def
1706
1707 /ReportFontInfo {
1708 2 copy
1709 /t0 3 1 roll DefFont
1710 /t0 F
1711 /lh FontHeight def
1712 /sw ( ) stringwidth pop def
1713 /aw (01234567890abcdefghijklmnopqrstuvwxyz) dup length exch
1714 stringwidth pop exch div def
1715 /t1 12 /Helvetica-Oblique DefFont
1716 /t1 F
1717 gsave
1718 (For ) show
1719 128 string cvs show
1720 ( ) show
1721 32 string cvs show
1722 ( point, the line height is ) show
1723 lh 32 string cvs show
1724 (, the space width is ) show
1725 sw 32 string cvs show
1726 (,) show
1727 grestore
1728 0 FontHeight neg rmoveto
1729 gsave
1730 (and a crude estimate of average character width is ) show
1731 aw 32 string cvs show
1732 (.) show
1733 grestore
1734 0 FontHeight neg rmoveto
1735 } def
1736
1737 /cm { % cm to point
1738 72 mul 2.54 div
1739 } def
1740
1741 /ReportAllFontInfo {
1742 FontDirectory
1743 { % key = font name value = font dictionary
1744 pop 10 exch ReportFontInfo
1745 } forall
1746 } def
1747
1748 % 3 cm 20 cm moveto 10 /Courier ReportFontInfo showpage
1749 % 3 cm 20 cm moveto ReportAllFontInfo showpage
1750
1751 ")
1752
1753 (defvar ps-print-prologue-2
1754 "
1755 % ---- These lines must be kept together because...
1756
1757 /h0 F
1758 /HeaderTitleLineHeight FontHeight def
1759
1760 /h1 F
1761 /HeaderLineHeight FontHeight def
1762 /HeaderDescent Descent def
1763
1764 % ---- ...because `F' has a side-effect on `FontHeight' and `Descent'
1765
1766 ")
1767
1768 ;; Start Editing Here:
1769
1770 (defvar ps-source-buffer nil)
1771 (defvar ps-spool-buffer-name "*PostScript*")
1772 (defvar ps-spool-buffer nil)
1773
1774 (defvar ps-output-head nil)
1775 (defvar ps-output-tail nil)
1776
1777 (defvar ps-page-count 0)
1778 (defvar ps-showpage-count 0)
1779
1780 (defvar ps-current-font 0)
1781 (defvar ps-current-underline-p nil)
1782 (defvar ps-default-color (if ps-print-color-p ps-default-fg)) ; black
1783 (defvar ps-current-color ps-default-color)
1784 (defvar ps-current-bg nil)
1785
1786 (defvar ps-razchunk 0)
1787
1788 (defvar ps-color-format
1789 (if (eq ps-print-emacs-type 'emacs)
1790
1791 ;;Emacs understands the %f format; we'll
1792 ;;use it to limit color RGB values to
1793 ;;three decimals to cut down some on the
1794 ;;size of the PostScript output.
1795 "%0.3f %0.3f %0.3f"
1796
1797 ;; Lucid emacsen will have to make do with
1798 ;; %s (princ) for floats.
1799 "%s %s %s"))
1800
1801 ;; These values determine how much print-height to deduct when headers
1802 ;; are turned on. This is a pretty clumsy way of handling it, but
1803 ;; it'll do for now.
1804
1805 (defvar ps-header-font)
1806 (defvar ps-header-title-font)
1807
1808 (defvar ps-header-line-height)
1809 (defvar ps-header-title-line-height)
1810 (defvar ps-header-pad 0
1811 "Vertical and horizontal space in points (1/72 inch) between the header frame
1812 and the text it contains.")
1813
1814 ;; Define accessors to the dimensions list.
1815
1816 (defmacro ps-page-dimensions-get-width (dims) `(nth 0 ,dims))
1817 (defmacro ps-page-dimensions-get-height (dims) `(nth 1 ,dims))
1818
1819 (defvar ps-landscape-page-height)
1820
1821 (defvar ps-print-width nil)
1822 (defvar ps-print-height nil)
1823
1824 (defvar ps-height-remaining)
1825 (defvar ps-width-remaining)
1826
1827 (defvar ps-ref-bold-faces nil)
1828 (defvar ps-ref-italic-faces nil)
1829 (defvar ps-ref-underlined-faces nil)
1830
1831 (defvar ps-print-color-scale nil)
1832
1833 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1834 ;; Internal functions
1835
1836 (defun ps-line-lengths-internal ()
1837 "Display the correspondance between a line length and a font size,
1838 using the current ps-print setup.
1839 Try: pr -t file | awk '{printf \"%3d %s\n\", length($0), $0}' | sort -r | head"
1840 (let ((buf (get-buffer-create "*Line-lengths*"))
1841 (ifs ps-font-size) ; initial font size
1842 (icw ps-avg-char-width) ; initial character width
1843 (print-width (progn (ps-get-page-dimensions)
1844 ps-print-width))
1845 (ps-setup (ps-setup)) ; setup for the current buffer
1846 (fs-min 5) ; minimum font size
1847 cw-min ; minimum character width
1848 nb-cpl-max ; maximum nb of characters per line
1849 (fs-max 14) ; maximum font size
1850 cw-max ; maximum character width
1851 nb-cpl-min ; minimum nb of characters per line
1852 fs ; current font size
1853 cw ; current character width
1854 nb-cpl ; current nb of characters per line
1855 )
1856 (setq cw-min (/ (* icw fs-min) ifs)
1857 nb-cpl-max (floor (/ print-width cw-min))
1858 cw-max (/ (* icw fs-max) ifs)
1859 nb-cpl-min (floor (/ print-width cw-max)))
1860 (setq nb-cpl nb-cpl-min)
1861 (set-buffer buf)
1862 (goto-char (point-max))
1863 (if (not (bolp)) (insert "\n"))
1864 (insert ps-setup)
1865 (insert "nb char per line / font size\n")
1866 (while (<= nb-cpl nb-cpl-max)
1867 (setq cw (/ print-width (float nb-cpl))
1868 fs (/ (* ifs cw) icw))
1869 (insert (format "%3s %s\n" nb-cpl fs))
1870 (setq nb-cpl (1+ nb-cpl)))
1871 (insert "\n")
1872 (display-buffer buf 'not-this-window)))
1873
1874 (defun ps-nb-pages (nb-lines)
1875 "Display an approximate correspondance between a font size and the number
1876 of pages the number of lines would require to print
1877 using the current ps-print setup."
1878 (let ((buf (get-buffer-create "*Nb-Pages*"))
1879 (ifs ps-font-size) ; initial font size
1880 (ilh ps-line-height) ; initial line height
1881 (page-height (progn (ps-get-page-dimensions)
1882 ps-print-height))
1883 (ps-setup (ps-setup)) ; setup for the current buffer
1884 (fs-min 4) ; minimum font size
1885 lh-min ; minimum line height
1886 nb-lpp-max ; maximum nb of lines per page
1887 nb-page-min ; minimum nb of pages
1888 (fs-max 14) ; maximum font size
1889 lh-max ; maximum line height
1890 nb-lpp-min ; minimum nb of lines per page
1891 nb-page-max ; maximum nb of pages
1892 fs ; current font size
1893 lh ; current line height
1894 nb-lpp ; current nb of lines per page
1895 nb-page ; current nb of pages
1896 )
1897 (setq lh-min (/ (* ilh fs-min) ifs)
1898 nb-lpp-max (floor (/ page-height lh-min))
1899 nb-page-min (ceiling (/ (float nb-lines) nb-lpp-max))
1900 lh-max (/ (* ilh fs-max) ifs)
1901 nb-lpp-min (floor (/ page-height lh-max))
1902 nb-page-max (ceiling (/ (float nb-lines) nb-lpp-min)))
1903 (setq nb-page nb-page-min)
1904 (set-buffer buf)
1905 (goto-char (point-max))
1906 (if (not (bolp)) (insert "\n"))
1907 (insert ps-setup)
1908 (insert (format "%d lines\n" nb-lines))
1909 (insert "nb page / font size\n")
1910 (while (<= nb-page nb-page-max)
1911 (setq nb-lpp (ceiling (/ nb-lines (float nb-page)))
1912 lh (/ page-height nb-lpp)
1913 fs (/ (* ifs lh) ilh))
1914 (insert (format "%s %s\n" nb-page fs))
1915 (setq nb-page (1+ nb-page)))
1916 (insert "\n")
1917 (display-buffer buf 'not-this-window)))
1918
1919 (defun ps-select-font ()
1920 "Choose the font name and size (scaling data)."
1921 (let ((assoc (assq ps-font-family ps-font-info-database))
1922 l fn fb fi bi sz lh sw aw)
1923 (if (null assoc)
1924 (error "Don't have data to scale font %s. Known fonts families are %s"
1925 ps-font-family
1926 (mapcar 'car ps-font-info-database)))
1927 (setq l (cdr assoc)
1928 fn (prog1 (car l) (setq l (cdr l))) ; need `pop'
1929 fb (prog1 (car l) (setq l (cdr l)))
1930 fi (prog1 (car l) (setq l (cdr l)))
1931 bi (prog1 (car l) (setq l (cdr l)))
1932 sz (prog1 (car l) (setq l (cdr l)))
1933 lh (prog1 (car l) (setq l (cdr l)))
1934 sw (prog1 (car l) (setq l (cdr l)))
1935 aw (prog1 (car l) (setq l (cdr l))))
1936
1937 (setq ps-font fn)
1938 (setq ps-font-bold fb)
1939 (setq ps-font-italic fi)
1940 (setq ps-font-bold-italic bi)
1941 ;; These data just need to be rescaled:
1942 (setq ps-line-height (/ (* lh ps-font-size) sz))
1943 (setq ps-space-width (/ (* sw ps-font-size) sz))
1944 (setq ps-avg-char-width (/ (* aw ps-font-size) sz))
1945 ps-font-family))
1946
1947 (defun ps-select-header-font ()
1948 "Choose the font name and size (scaling data) for the header."
1949 (let ((assoc (assq ps-header-font-family ps-font-info-database))
1950 l fn fb fi bi sz lh sw aw)
1951 (if (null assoc)
1952 (error "Don't have data to scale font %s. Known fonts families are %s"
1953 ps-font-family
1954 (mapcar 'car ps-font-info-database)))
1955 (setq l (cdr assoc)
1956 fn (prog1 (car l) (setq l (cdr l))) ; need `pop'
1957 fb (prog1 (car l) (setq l (cdr l)))
1958 fi (prog1 (car l) (setq l (cdr l)))
1959 bi (prog1 (car l) (setq l (cdr l)))
1960 sz (prog1 (car l) (setq l (cdr l)))
1961 lh (prog1 (car l) (setq l (cdr l)))
1962 sw (prog1 (car l) (setq l (cdr l)))
1963 aw (prog1 (car l) (setq l (cdr l))))
1964
1965 ;; Font name
1966 (setq ps-header-font fn)
1967 (setq ps-header-title-font fb)
1968 ;; Line height: These data just need to be rescaled:
1969 (setq ps-header-title-line-height (/ (* lh ps-header-title-font-size) sz))
1970 (setq ps-header-line-height (/ (* lh ps-header-font-size) sz))
1971 ps-header-font-family))
1972
1973 (defun ps-get-page-dimensions ()
1974 (let ((page-dimensions (cdr (assq ps-paper-type ps-page-dimensions-database)))
1975 page-width page-height)
1976 (cond
1977 ((null page-dimensions)
1978 (error "`ps-paper-type' must be one of:\n%s"
1979 (mapcar 'car ps-page-dimensions-database)))
1980 ((< ps-number-of-columns 1)
1981 (error "The number of columns %d should not be negative")))
1982
1983 (ps-select-font)
1984 (ps-select-header-font)
1985
1986 (setq page-width (ps-page-dimensions-get-width page-dimensions)
1987 page-height (ps-page-dimensions-get-height page-dimensions))
1988
1989 ;; Landscape mode
1990 (if ps-landscape-mode
1991 ;; exchange width and height
1992 (setq page-width (prog1 page-height (setq page-height page-width))))
1993
1994 ;; It is used to get the lower right corner (only in landscape mode)
1995 (setq ps-landscape-page-height page-height)
1996
1997 ;; | lm | text | ic | text | ic | text | rm |
1998 ;; page-width == lm + n * pw + (n - 1) * ic + rm
1999 ;; => pw == (page-width - lm -rm - (n - 1) * ic) / n
2000 (setq ps-print-width
2001 (/ (- page-width
2002 ps-left-margin ps-right-margin
2003 (* (1- ps-number-of-columns) ps-inter-column))
2004 ps-number-of-columns))
2005 (if (<= ps-print-width 0)
2006 (error "Bad horizontal layout:
2007 page-width == %s
2008 ps-left-margin == %s
2009 ps-right-margin == %s
2010 ps-inter-column == %s
2011 ps-number-of-columns == %s
2012 | lm | text | ic | text | ic | text | rm |
2013 page-width == lm + n * print-width + (n - 1) * ic + rm
2014 => print-width == %d !"
2015 page-width
2016 ps-left-margin
2017 ps-right-margin
2018 ps-inter-column
2019 ps-number-of-columns
2020 ps-print-width))
2021
2022 (setq ps-print-height
2023 (- page-height ps-bottom-margin ps-top-margin))
2024 (if (<= ps-print-height 0)
2025 (error "Bad vertical layout:
2026 ps-top-margin == %s
2027 ps-bottom-margin == %s
2028 page-height == bm + print-height + tm
2029 => print-height == %d !"
2030 ps-top-margin
2031 ps-bottom-margin
2032 ps-print-height))
2033 ;; If headers are turned on, deduct the height of the header from
2034 ;; the print height.
2035 (cond
2036 (ps-print-header
2037 (setq ps-header-pad
2038 (* ps-header-line-pad ps-header-title-line-height))
2039 (setq ps-print-height
2040 (- ps-print-height
2041 ps-header-offset
2042 ps-header-pad
2043 ps-header-title-line-height
2044 (* ps-header-line-height (- ps-header-lines 1))
2045 ps-header-pad))))
2046 (if (<= ps-print-height 0)
2047 (error "Bad vertical layout:
2048 ps-top-margin == %s
2049 ps-bottom-margin == %s
2050 ps-header-offset == %s
2051 ps-header-pad == %s
2052 header-height == %s
2053 page-height == bm + print-height + tm - ho - hh
2054 => print-height == %d !"
2055 ps-top-margin
2056 ps-bottom-margin
2057 ps-header-offset
2058 ps-header-pad
2059 (+ ps-header-pad
2060 ps-header-title-line-height
2061 (* ps-header-line-height (- ps-header-lines 1))
2062 ps-header-pad)
2063 ps-print-height))))
2064
2065 (defun ps-print-preprint (&optional filename)
2066 (if (and filename
2067 (or (numberp filename)
2068 (listp filename)))
2069 (let* ((name (concat (buffer-name) ".ps"))
2070 (prompt (format "Save PostScript to file: (default %s) "
2071 name))
2072 (res (read-file-name prompt default-directory name nil)))
2073 (if (file-directory-p res)
2074 (expand-file-name name (file-name-as-directory res))
2075 res))))
2076
2077 ;; The following functions implement a simple list-buffering scheme so
2078 ;; that ps-print doesn't have to repeatedly switch between buffers
2079 ;; while spooling. The functions ps-output and ps-output-string build
2080 ;; up the lists; the function ps-flush-output takes the lists and
2081 ;; insert its contents into the spool buffer (*PostScript*).
2082
2083 (defun ps-output-string-prim (string)
2084 (insert "(") ;insert start-string delimiter
2085 (save-excursion ;insert string
2086 (insert string))
2087
2088 ;; Find and quote special characters as necessary for PS
2089 (while (re-search-forward "[()\\]" nil t)
2090 (save-excursion
2091 (forward-char -1)
2092 (insert "\\")))
2093
2094 (goto-char (point-max))
2095 (insert ")")) ;insert end-string delimiter
2096
2097 (defun ps-init-output-queue ()
2098 (setq ps-output-head (list ""))
2099 (setq ps-output-tail ps-output-head))
2100
2101 (defun ps-output (&rest args)
2102 (setcdr ps-output-tail args)
2103 (while (cdr ps-output-tail)
2104 (setq ps-output-tail (cdr ps-output-tail))))
2105
2106 (defun ps-output-string (string)
2107 (ps-output t string))
2108
2109 (defun ps-flush-output ()
2110 (save-excursion
2111 (set-buffer ps-spool-buffer)
2112 (goto-char (point-max))
2113 (while ps-output-head
2114 (let ((it (car ps-output-head)))
2115 (if (not (eq t it))
2116 (insert it)
2117 (setq ps-output-head (cdr ps-output-head))
2118 (ps-output-string-prim (car ps-output-head))))
2119 (setq ps-output-head (cdr ps-output-head))))
2120 (ps-init-output-queue))
2121
2122 (defun ps-insert-file (fname)
2123 (ps-flush-output)
2124
2125 ;; Check to see that the file exists and is readable; if not, throw
2126 ;; and error.
2127 (if (not (file-readable-p fname))
2128 (error "Could not read file `%s'" fname))
2129
2130 (save-excursion
2131 (set-buffer ps-spool-buffer)
2132 (goto-char (point-max))
2133 (insert-file fname)))
2134
2135 ;; These functions insert the arrays that define the contents of the
2136 ;; headers.
2137
2138 (defun ps-generate-header-line (fonttag &optional content)
2139 (ps-output " [ " fonttag " ")
2140 (cond
2141 ;; Literal strings should be output as is -- the string must
2142 ;; contain its own PS string delimiters, '(' and ')', if necessary.
2143 ((stringp content)
2144 (ps-output content))
2145
2146 ;; Functions are called -- they should return strings; they will be
2147 ;; inserted as strings and the PS string delimiters added.
2148 ((and (symbolp content) (fboundp content))
2149 (ps-output-string (funcall content)))
2150
2151 ;; Variables will have their contents inserted. They should
2152 ;; contain strings, and will be inserted as strings.
2153 ((and (symbolp content) (boundp content))
2154 (ps-output-string (symbol-value content)))
2155
2156 ;; Anything else will get turned into an empty string.
2157 (t
2158 (ps-output-string "")))
2159 (ps-output " ]\n"))
2160
2161 (defun ps-generate-header (name contents)
2162 (ps-output "/" name " [\n")
2163 (if (> ps-header-lines 0)
2164 (let ((count 1))
2165 (ps-generate-header-line "/h0" (car contents))
2166 (while (and (< count ps-header-lines)
2167 (setq contents (cdr contents)))
2168 (ps-generate-header-line "/h1" (car contents))
2169 (setq count (+ count 1)))
2170 (ps-output "] def\n"))))
2171
2172 (defun ps-output-boolean (name bool)
2173 (ps-output (format "/%s %s def\n" name (if bool "true" "false"))))
2174
2175 (defun ps-begin-file ()
2176 (ps-get-page-dimensions)
2177 (setq ps-showpage-count 0)
2178
2179 (ps-output ps-adobe-tag)
2180 (ps-output "%%Title: " (buffer-name) "\n") ;Take job name from name of
2181 ;first buffer printed
2182 (ps-output "%%Creator: " (user-full-name) "\n")
2183 (ps-output "%%CreationDate: "
2184 (time-stamp-hh:mm:ss) " " (time-stamp-mon-dd-yyyy) "\n")
2185 (ps-output "%% DocumentFonts: "
2186 ps-font " " ps-font-bold " " ps-font-italic " "
2187 ps-font-bold-italic " "
2188 ps-header-font " " ps-header-title-font "\n")
2189 (ps-output "%%Pages: (atend)\n")
2190 (ps-output "%%EndComments\n\n")
2191
2192 (ps-output-boolean "LandscapeMode" ps-landscape-mode)
2193 (ps-output (format "/NumberOfColumns %d def\n" ps-number-of-columns))
2194
2195 (ps-output (format "/LandscapePageHeight %s def\n" ps-landscape-page-height))
2196 (ps-output (format "/PrintWidth %s def\n" ps-print-width))
2197 (ps-output (format "/PrintHeight %s def\n" ps-print-height))
2198
2199 (ps-output (format "/LeftMargin %s def\n" ps-left-margin))
2200 (ps-output (format "/RightMargin %s def\n" ps-right-margin)) ; not used
2201 (ps-output (format "/InterColumn %s def\n" ps-inter-column))
2202
2203 (ps-output (format "/BottomMargin %s def\n" ps-bottom-margin))
2204 (ps-output (format "/TopMargin %s def\n" ps-top-margin)) ; not used
2205 (ps-output (format "/HeaderOffset %s def\n" ps-header-offset))
2206 (ps-output (format "/HeaderPad %s def\n" ps-header-pad))
2207
2208 (ps-output-boolean "PrintHeader" ps-print-header)
2209 (ps-output-boolean "PrintHeaderFrame" ps-print-header-frame)
2210 (ps-output-boolean "ShowNofN" ps-show-n-of-n)
2211 (ps-output-boolean "Duplex" ps-spool-duplex)
2212
2213 (ps-output (format "/LineHeight %s def\n" ps-line-height))
2214
2215 (ps-output ps-print-prologue-1)
2216
2217 ;; Header fonts
2218 (ps-output ; /h0 14 /Helvetica-Bold Font
2219 (format "/h0 %s /%s DefFont\n" ps-header-title-font-size ps-header-title-font))
2220 (ps-output ; /h1 12 /Helvetica Font
2221 (format "/h1 %s /%s DefFont\n" ps-header-font-size ps-header-font))
2222
2223 (ps-output ps-print-prologue-2)
2224
2225 ;; Text fonts
2226 (ps-output (format "/f0 %s /%s DefFont\n" ps-font-size ps-font))
2227 (ps-output (format "/f1 %s /%s DefFont\n" ps-font-size ps-font-bold))
2228 (ps-output (format "/f2 %s /%s DefFont\n" ps-font-size ps-font-italic))
2229 (ps-output (format "/f3 %s /%s DefFont\n" ps-font-size ps-font-bold-italic))
2230
2231 (ps-output "\nBeginDoc\n\n")
2232 (ps-output "%%EndPrologue\n"))
2233
2234 (defun ps-header-dirpart ()
2235 (let ((fname (buffer-file-name)))
2236 (if fname
2237 (if (string-equal (buffer-name) (file-name-nondirectory fname))
2238 (file-name-directory fname)
2239 fname)
2240 "")))
2241
2242 (defun ps-get-buffer-name ()
2243 (cond
2244 ;; Indulge Jim this little easter egg:
2245 ((string= (buffer-name) "ps-print.el")
2246 "Hey, Cool! It's ps-print.el!!!")
2247 ;; Indulge Jack this other little easter egg:
2248 ((string= (buffer-name) "sokoban.el")
2249 "Super! C'est sokoban.el!")
2250 (t (buffer-name))))
2251
2252 (defun ps-begin-job ()
2253 (setq ps-page-count 0))
2254
2255 (defun ps-end-file ()
2256 (ps-output "\nEndDoc\n\n")
2257 (ps-output "%%Trailer\n")
2258 (ps-output (format "%%%%Pages: %d\n" (1+ (/ (1- ps-page-count)
2259 ps-number-of-columns)))))
2260
2261 (defun ps-next-page ()
2262 (ps-end-page)
2263 (ps-flush-output)
2264 (ps-begin-page))
2265
2266 (defun ps-begin-page (&optional dummypage)
2267 (ps-get-page-dimensions)
2268 (setq ps-width-remaining ps-print-width)
2269 (setq ps-height-remaining ps-print-height)
2270
2271 ;; Print only when a new real page begins.
2272 (when (zerop (mod ps-page-count ps-number-of-columns))
2273 (ps-output (format "\n%%%%Page: %d %d\n"
2274 (1+ (/ ps-page-count ps-number-of-columns))
2275 (1+ (/ ps-page-count ps-number-of-columns)))))
2276
2277 (ps-output "BeginDSCPage\n")
2278 (ps-output (format "/PageNumber %d def\n" (incf ps-page-count)))
2279 (ps-output "/PageCount 0 def\n")
2280
2281 (when ps-print-header
2282 (ps-generate-header "HeaderLinesLeft" ps-left-header)
2283 (ps-generate-header "HeaderLinesRight" ps-right-header)
2284 (ps-output (format "%d SetHeaderLines\n" ps-header-lines)))
2285
2286 (ps-output "BeginPage\n")
2287 (ps-set-font ps-current-font)
2288 (ps-set-bg ps-current-bg)
2289 (ps-set-color ps-current-color)
2290 (ps-set-underline ps-current-underline-p))
2291
2292 (defun ps-end-page ()
2293 (setq ps-showpage-count (+ 1 ps-showpage-count))
2294 (ps-output "EndPage\n")
2295 (ps-output "EndDSCPage\n"))
2296
2297 (defun ps-dummy-page ()
2298 (setq ps-showpage-count (+ 1 ps-showpage-count))
2299 (ps-output "%%Page: " (format "- %d\n" ps-showpage-count)
2300 "BeginDSCPage
2301 /PrintHeader false def
2302 BeginPage
2303 EndPage
2304 EndDSCPage\n"))
2305
2306 (defun ps-next-line ()
2307 (if (< ps-height-remaining ps-line-height)
2308 (ps-next-page)
2309 (setq ps-width-remaining ps-print-width)
2310 (setq ps-height-remaining (- ps-height-remaining ps-line-height))
2311 (ps-hard-lf)))
2312
2313 (defun ps-continue-line ()
2314 (if (< ps-height-remaining ps-line-height)
2315 (ps-next-page)
2316 (setq ps-width-remaining ps-print-width)
2317 (setq ps-height-remaining (- ps-height-remaining ps-line-height))
2318 (ps-soft-lf)))
2319
2320 ;; [jack] Why hard and soft ?
2321
2322 (defun ps-hard-lf ()
2323 (ps-output "HL\n"))
2324
2325 (defun ps-soft-lf ()
2326 (ps-output "SL\n"))
2327
2328 (defun ps-find-wrappoint (from to char-width)
2329 (let ((avail (truncate (/ ps-width-remaining char-width)))
2330 (todo (- to from)))
2331 (if (< todo avail)
2332 (cons to (* todo char-width))
2333 (cons (+ from avail) ps-width-remaining))))
2334
2335 (defun ps-basic-plot-string (from to &optional bg-color)
2336 (let* ((wrappoint (ps-find-wrappoint from to ps-avg-char-width))
2337 (to (car wrappoint))
2338 (string (buffer-substring from to)))
2339 (ps-output-string string)
2340 (ps-output " S\n")
2341 wrappoint))
2342
2343 (defun ps-basic-plot-whitespace (from to &optional bg-color)
2344 (let* ((wrappoint (ps-find-wrappoint from to ps-space-width))
2345 (to (car wrappoint)))
2346
2347 (ps-output (format "%d W\n" (- to from)))
2348 wrappoint))
2349
2350 (defun ps-plot (plotfunc from to &optional bg-color)
2351 (while (< from to)
2352 (let* ((wrappoint (funcall plotfunc from to bg-color))
2353 (plotted-to (car wrappoint))
2354 (plotted-width (cdr wrappoint)))
2355 (setq from plotted-to)
2356 (setq ps-width-remaining (- ps-width-remaining plotted-width))
2357 (if (< from to)
2358 (ps-continue-line))))
2359 (if ps-razzle-dazzle
2360 (let* ((q-todo (- (point-max) (point-min)))
2361 (q-done (- (point) (point-min)))
2362 (chunkfrac (/ q-todo 8))
2363 (chunksize (if (> chunkfrac 1000) 1000 chunkfrac)))
2364 (if (> (- q-done ps-razchunk) chunksize)
2365 (let (foo)
2366 (setq ps-razchunk q-done)
2367 (setq foo
2368 (if (< q-todo 100)
2369 (/ (* 100 q-done) q-todo)
2370 (/ q-done (/ q-todo 100))))
2371 (message "Formatting...%3d%%" foo))))))
2372
2373 (defun ps-set-font (font)
2374 (setq ps-current-font font)
2375 (ps-output (format "/f%d F\n" ps-current-font)))
2376
2377 (defun ps-set-bg (color)
2378 (if (setq ps-current-bg color)
2379 (ps-output (format ps-color-format (nth 0 color) (nth 1 color)
2380 (nth 2 color))
2381 " true BG\n")
2382 (ps-output "false BG\n")))
2383
2384 (defun ps-set-color (color)
2385 (if (setq ps-current-color color)
2386 nil
2387 (setq ps-current-color ps-default-fg))
2388 (ps-output (format ps-color-format (nth 0 ps-current-color)
2389 (nth 1 ps-current-color) (nth 2 ps-current-color))
2390 " FG\n"))
2391
2392 (defun ps-set-underline (underline-p)
2393 (ps-output (if underline-p "true" "false") " UL\n")
2394 (setq ps-current-underline-p underline-p))
2395
2396 (defun ps-plot-region (from to font fg-color &optional bg-color underline-p)
2397
2398 (if (not (equal font ps-current-font))
2399 (ps-set-font font))
2400
2401 ;; Specify a foreground color only if one's specified and it's
2402 ;; different than the current.
2403 (if (not (equal fg-color ps-current-color))
2404 (ps-set-color fg-color))
2405
2406 (if (not (equal bg-color ps-current-bg))
2407 (ps-set-bg bg-color))
2408
2409 ;; Toggle underlining if different.
2410 (if (not (equal underline-p ps-current-underline-p))
2411 (ps-set-underline underline-p))
2412
2413 ;; Starting at the beginning of the specified region...
2414 (save-excursion
2415 (goto-char from)
2416
2417 ;; ...break the region up into chunks separated by tabs, linefeeds,
2418 ;; and pagefeeds, and plot each chunk.
2419 (while (< from to)
2420 (if (re-search-forward "[\t\n\f]" to t)
2421 (let ((match (char-after (match-beginning 0))))
2422 (cond
2423 ((= match ?\t)
2424 (let ((linestart
2425 (save-excursion (beginning-of-line) (point))))
2426 (ps-plot 'ps-basic-plot-string from (- (point) 1)
2427 bg-color)
2428 (forward-char -1)
2429 (setq from (+ linestart (current-column)))
2430 (if (re-search-forward "[ \t]+" to t)
2431 (ps-plot 'ps-basic-plot-whitespace
2432 from (+ linestart (current-column))
2433 bg-color))))
2434
2435 ((= match ?\n)
2436 (ps-plot 'ps-basic-plot-string from (- (point) 1)
2437 bg-color)
2438 (ps-next-line)
2439 )
2440
2441 ((= match ?\f)
2442 (ps-plot 'ps-basic-plot-string from (- (point) 1)
2443 bg-color)
2444 (ps-next-page)))
2445 (setq from (point)))
2446 (ps-plot 'ps-basic-plot-string from to bg-color)
2447 (setq from to)))))
2448
2449 (defun ps-color-value (x-color-value)
2450 ;; Scale 16-bit X-COLOR-VALUE to PostScript color value in [0, 1] interval.
2451 (/ x-color-value ps-print-color-scale))
2452
2453 (defun ps-color-values (x-color)
2454 (cond ((fboundp 'x-color-values)
2455 (x-color-values x-color))
2456 ((fboundp 'pixel-components)
2457 (pixel-components x-color))
2458 (t (error "No available function to determine X color values."))))
2459
2460 (defun ps-face-attributes (face)
2461 (let ((differs (face-differs-from-default-p face)))
2462 (list (memq face ps-ref-bold-faces)
2463 (memq face ps-ref-italic-faces)
2464 (memq face ps-ref-underlined-faces)
2465 (and differs (face-foreground face))
2466 (and differs (face-background face)))))
2467
2468 (defun ps-face-attribute-list (face-or-list)
2469 (if (listp face-or-list)
2470 (let (bold-p italic-p underline-p foreground background face-attr face)
2471 (while face-or-list
2472 (setq face (car face-or-list))
2473 (setq face-attr (ps-face-attributes face))
2474 (setq bold-p (or bold-p (nth 0 face-attr)))
2475 (setq italic-p (or italic-p (nth 1 face-attr)))
2476 (setq underline-p (or underline-p (nth 2 face-attr)))
2477 (if foreground
2478 nil
2479 (setq foreground (nth 3 face-attr)))
2480 (if background
2481 nil
2482 (setq background (nth 4 face-attr)))
2483 (setq face-or-list (cdr face-or-list)))
2484 (list bold-p italic-p underline-p foreground background))
2485
2486 (ps-face-attributes face-or-list)))
2487
2488 (defun ps-plot-with-face (from to face)
2489 (if face
2490 (let* ((face-attr (ps-face-attribute-list face))
2491 (bold-p (nth 0 face-attr))
2492 (italic-p (nth 1 face-attr))
2493 (underline-p (nth 2 face-attr))
2494 (foreground (nth 3 face-attr))
2495 (background (nth 4 face-attr))
2496 (fg-color (if (and ps-print-color-p foreground)
2497 (mapcar 'ps-color-value
2498 (ps-color-values foreground))
2499 ps-default-color))
2500 (bg-color (if (and ps-print-color-p background)
2501 (mapcar 'ps-color-value
2502 (ps-color-values background)))))
2503 (ps-plot-region from to
2504 (cond ((and bold-p italic-p) 3)
2505 (italic-p 2)
2506 (bold-p 1)
2507 (t 0))
2508 ; (or fg-color '(0.0 0.0 0.0))
2509 fg-color
2510 bg-color underline-p))
2511 (goto-char to)))
2512
2513
2514 (defun ps-emacs-face-kind-p (face kind kind-regex kind-list)
2515 (let ((frame-font (face-font face))
2516 (face-defaults (face-font face t)))
2517 (or
2518 ;; Check FACE defaults:
2519 (and (listp face-defaults)
2520 (memq kind face-defaults))
2521
2522 ;; Check the user's preferences
2523 (memq face kind-list))))
2524
2525 (defun ps-xemacs-face-kind-p (face kind kind-regex kind-list)
2526 (let* ((frame-font (or (face-font face) (face-font 'default)))
2527 (kind-cons (assq kind (x-font-properties frame-font)))
2528 (kind-spec (cdr-safe kind-cons))
2529 (case-fold-search t))
2530
2531 (or (and kind-spec (string-match kind-regex kind-spec))
2532 ;; Kludge-compatible:
2533 (memq face kind-list))))
2534
2535 (defun ps-face-bold-p (face)
2536 (if (eq ps-print-emacs-type 'emacs)
2537 (ps-emacs-face-kind-p face 'bold "-\\(bold\\|demibold\\)-"
2538 ps-bold-faces)
2539 (ps-xemacs-face-kind-p face 'WEIGHT_NAME "bold\\|demibold"
2540 ps-bold-faces)))
2541
2542 (defun ps-face-italic-p (face)
2543 (if (eq ps-print-emacs-type 'emacs)
2544 (ps-emacs-face-kind-p face 'italic "-[io]-" ps-italic-faces)
2545 (or
2546 (ps-xemacs-face-kind-p face 'ANGLE_NAME "i\\|o" ps-italic-faces)
2547 (ps-xemacs-face-kind-p face 'SLANT "i\\|o" ps-italic-faces))))
2548
2549 (defun ps-face-underlined-p (face)
2550 (or (face-underline-p face)
2551 (memq face ps-underlined-faces)))
2552
2553 ;; Ensure that face-list is fbound.
2554 (or (fboundp 'face-list) (defalias 'face-list 'list-faces))
2555
2556 (defun ps-build-reference-face-lists ()
2557 (if ps-auto-font-detect
2558 (let ((faces (face-list))
2559 the-face)
2560 (setq ps-ref-bold-faces nil
2561 ps-ref-italic-faces nil
2562 ps-ref-underlined-faces nil)
2563 (while faces
2564 (setq the-face (car faces))
2565 (if (ps-face-italic-p the-face)
2566 (setq ps-ref-italic-faces
2567 (cons the-face ps-ref-italic-faces)))
2568 (if (ps-face-bold-p the-face)
2569 (setq ps-ref-bold-faces
2570 (cons the-face ps-ref-bold-faces)))
2571 (if (ps-face-underlined-p the-face)
2572 (setq ps-ref-underlined-faces
2573 (cons the-face ps-ref-underlined-faces)))
2574 (setq faces (cdr faces))))
2575 (setq ps-ref-bold-faces ps-bold-faces)
2576 (setq ps-ref-italic-faces ps-italic-faces)
2577 (setq ps-ref-underlined-faces ps-underlined-faces))
2578 (setq ps-build-face-reference nil))
2579
2580 (defun ps-mapper (extent list)
2581 (nconc list (list (list (extent-start-position extent) 'push extent)
2582 (list (extent-end-position extent) 'pull extent)))
2583 nil)
2584
2585 (defun ps-extent-sorter (a b)
2586 (< (extent-priority a) (extent-priority b)))
2587
2588 (defun ps-print-ensure-fontified (start end)
2589 (if (and (boundp 'lazy-lock-mode) lazy-lock-mode)
2590 (if (fboundp 'lazy-lock-fontify-region)
2591 (lazy-lock-fontify-region start end) ; the new
2592 (lazy-lock-fontify-buffer)))) ; the old
2593
2594 (defun ps-generate-postscript-with-faces (from to)
2595 ;; Build the reference lists of faces if necessary.
2596 (if (or ps-always-build-face-reference
2597 ps-build-face-reference)
2598 (progn
2599 (message "Collecting face information...")
2600 (ps-build-reference-face-lists)))
2601 ;; Set the color scale. We do it here instead of in the defvar so
2602 ;; that ps-print can be dumped into emacs. This expression can't be
2603 ;; evaluated at dump-time because X isn't initialized.
2604 (setq ps-print-color-scale
2605 (if ps-print-color-p
2606 (float (car (ps-color-values "white")))
2607 1.0))
2608 ;; Generate some PostScript.
2609 (save-restriction
2610 (narrow-to-region from to)
2611 (let ((face 'default)
2612 (position to))
2613 (ps-print-ensure-fontified from to)
2614 (cond ((or (eq ps-print-emacs-type 'lucid)
2615 (eq ps-print-emacs-type 'xemacs))
2616 ;; Build the list of extents...
2617 (let ((a (cons 'dummy nil))
2618 record type extent extent-list)
2619 (map-extents 'ps-mapper nil from to a)
2620 (setq a (sort (cdr a) 'car-less-than-car))
2621
2622 (setq extent-list nil)
2623
2624 ;; Loop through the extents...
2625 (while a
2626 (setq record (car a))
2627
2628 (setq position (car record))
2629 (setq record (cdr record))
2630
2631 (setq type (car record))
2632 (setq record (cdr record))
2633
2634 (setq extent (car record))
2635
2636 ;; Plot up to this record.
2637 ;; XEmacs 19.12: for some reason, we're getting into a
2638 ;; situation in which some of the records have
2639 ;; positions less than 'from'. Since we've narrowed
2640 ;; the buffer, this'll generate errors. This is a
2641 ;; hack, but don't call ps-plot-with-face unless from >
2642 ;; point-min.
2643 (if (and (>= from (point-min))
2644 (<= position (point-max)))
2645 (ps-plot-with-face from position face))
2646
2647 (cond
2648 ((eq type 'push)
2649 (if (extent-face extent)
2650 (setq extent-list (sort (cons extent extent-list)
2651 'ps-extent-sorter))))
2652
2653 ((eq type 'pull)
2654 (setq extent-list (sort (delq extent extent-list)
2655 'ps-extent-sorter))))
2656
2657 (setq face
2658 (if extent-list
2659 (extent-face (car extent-list))
2660 'default))
2661
2662 (setq from position)
2663 (setq a (cdr a)))))
2664
2665 ((eq ps-print-emacs-type 'emacs)
2666 (let ((property-change from)
2667 (overlay-change from))
2668 (while (< from to)
2669 (if (< property-change to) ; Don't search for property change
2670 ; unless previous search succeeded.
2671 (setq property-change
2672 (next-property-change from nil to)))
2673 (if (< overlay-change to) ; Don't search for overlay change
2674 ; unless previous search succeeded.
2675 (setq overlay-change
2676 (min (next-overlay-change from) to)))
2677 (setq position
2678 (min property-change overlay-change))
2679 ;; The code below is not quite correct,
2680 ;; because a non-nil overlay invisible property
2681 ;; which is inactive according to the current value
2682 ;; of buffer-invisibility-spec nonetheless overrides
2683 ;; a face text property.
2684 (setq face
2685 (cond ((let ((prop (get-text-property from 'invisible)))
2686 ;; Decide whether this invisible property
2687 ;; really makes the text invisible.
2688 (if (eq buffer-invisibility-spec t)
2689 (not (null prop))
2690 (or (memq prop buffer-invisibility-spec)
2691 (assq prop buffer-invisibility-spec))))
2692 nil)
2693 ((get-text-property from 'face))
2694 (t 'default)))
2695 (let ((overlays (overlays-at from))
2696 (face-priority -1)) ; text-property
2697 (while overlays
2698 (let* ((overlay (car overlays))
2699 (overlay-face (overlay-get overlay 'face))
2700 (overlay-invisible (overlay-get overlay 'invisible))
2701 (overlay-priority (or (overlay-get overlay
2702 'priority)
2703 0)))
2704 (if (and (or overlay-invisible overlay-face)
2705 (> overlay-priority face-priority))
2706 (setq face (cond ((if (eq buffer-invisibility-spec t)
2707 (not (null overlay-invisible))
2708 (or (memq overlay-invisible buffer-invisibility-spec)
2709 (assq overlay-invisible buffer-invisibility-spec)))
2710 nil)
2711 ((and face overlay-face)))
2712 face-priority overlay-priority)))
2713 (setq overlays (cdr overlays))))
2714 ;; Plot up to this record.
2715 (ps-plot-with-face from position face)
2716 (setq from position)))))
2717 (ps-plot-with-face from to face))))
2718
2719 (defun ps-generate-postscript (from to)
2720 (ps-plot-region from to 0 nil))
2721
2722 (defun ps-generate (buffer from to genfunc)
2723 (let ((from (min to from))
2724 (to (max to from))
2725 ;; This avoids trouble if chars with read-only properties
2726 ;; are copied into ps-spool-buffer.
2727 (inhibit-read-only t))
2728 (save-restriction
2729 (narrow-to-region from to)
2730 (if ps-razzle-dazzle
2731 (message "Formatting...%3d%%" (setq ps-razchunk 0)))
2732 (set-buffer buffer)
2733 (setq ps-source-buffer buffer)
2734 (setq ps-spool-buffer (get-buffer-create ps-spool-buffer-name))
2735 (ps-init-output-queue)
2736 (let (safe-marker completed-safely needs-begin-file)
2737 (unwind-protect
2738 (progn
2739 (set-buffer ps-spool-buffer)
2740
2741 ;; Get a marker and make it point to the current end of the
2742 ;; buffer, If an error occurs, we'll delete everything from
2743 ;; the end of this marker onwards.
2744 (setq safe-marker (make-marker))
2745 (set-marker safe-marker (point-max))
2746
2747 (goto-char (point-min))
2748 (if (looking-at (regexp-quote "%!PS-Adobe-1.0"))
2749 nil
2750 (setq needs-begin-file t))
2751 (save-excursion
2752 (set-buffer ps-source-buffer)
2753 (if needs-begin-file (ps-begin-file))
2754 (ps-begin-job)
2755 (ps-begin-page))
2756 (set-buffer ps-source-buffer)
2757 (funcall genfunc from to)
2758 (ps-end-page)
2759
2760 (if (and ps-spool-duplex
2761 (= (mod ps-page-count 2) 1))
2762 (ps-dummy-page))
2763 (ps-flush-output)
2764
2765 ;; Back to the PS output buffer to set the page count
2766 (set-buffer ps-spool-buffer)
2767 (goto-char (point-max))
2768 (while (re-search-backward "^/PageCount 0 def$" nil t)
2769 (replace-match (format "/PageCount %d def" ps-page-count) t))
2770
2771 ;; Setting this variable tells the unwind form that the
2772 ;; the postscript was generated without error.
2773 (setq completed-safely t))
2774
2775 ;; Unwind form: If some bad mojo occurred while generating
2776 ;; postscript, delete all the postscript that was generated.
2777 ;; This protects the previously spooled files from getting
2778 ;; corrupted.
2779 (if (and (markerp safe-marker) (not completed-safely))
2780 (progn
2781 (set-buffer ps-spool-buffer)
2782 (delete-region (marker-position safe-marker) (point-max))))))
2783
2784 (if ps-razzle-dazzle
2785 (message "Formatting...done")))))
2786
2787 (defun ps-do-despool (filename)
2788 (if (or (not (boundp 'ps-spool-buffer))
2789 (not (symbol-value 'ps-spool-buffer)))
2790 (message "No spooled PostScript to print")
2791 (ps-end-file)
2792 (ps-flush-output)
2793 (if filename
2794 (save-excursion
2795 (if ps-razzle-dazzle
2796 (message "Saving..."))
2797 (set-buffer ps-spool-buffer)
2798 (setq filename (expand-file-name filename))
2799 (write-region (point-min) (point-max) filename)
2800 (if ps-razzle-dazzle
2801 (message "Wrote %s" filename)))
2802 ;; Else, spool to the printer
2803 (if ps-razzle-dazzle
2804 (message "Printing..."))
2805 (save-excursion
2806 (set-buffer ps-spool-buffer)
2807 (if (and (eq system-type 'ms-dos) (stringp dos-ps-printer))
2808 (write-region (point-min) (point-max) dos-ps-printer t 0)
2809 (let ((binary-process-input t)) ; for MS-DOS
2810 (apply 'call-process-region
2811 (point-min) (point-max) ps-lpr-command nil
2812 (if (fboundp 'start-process) 0 nil)
2813 nil
2814 ps-lpr-switches))))
2815 (if ps-razzle-dazzle
2816 (message "Printing...done")))
2817 (kill-buffer ps-spool-buffer)))
2818
2819 (defun ps-kill-emacs-check ()
2820 (let (ps-buffer)
2821 (if (and (setq ps-buffer (get-buffer ps-spool-buffer-name))
2822 (buffer-modified-p ps-buffer))
2823 (if (y-or-n-p "Unprinted PostScript waiting; print now? ")
2824 (ps-despool)))
2825 (if (and (setq ps-buffer (get-buffer ps-spool-buffer-name))
2826 (buffer-modified-p ps-buffer))
2827 (if (yes-or-no-p "Unprinted PostScript waiting; exit anyway? ")
2828 nil
2829 (error "Unprinted PostScript")))))
2830
2831 (if (fboundp 'add-hook)
2832 (funcall 'add-hook 'kill-emacs-hook 'ps-kill-emacs-check)
2833 (if kill-emacs-hook
2834 (message "Won't override existing kill-emacs-hook")
2835 (setq kill-emacs-hook 'ps-kill-emacs-check)))
2836
2837 ;;; Sample Setup Code:
2838
2839 ;; This stuff is for anybody that's brave enough to look this far,
2840 ;; and able to figure out how to use it. It isn't really part of ps-
2841 ;; print, but I'll leave it here in hopes it might be useful:
2842
2843 ;; WARNING!!! The following code is *sample* code only. Don't use it
2844 ;; unless you understand what it does!
2845
2846 (defmacro ps-prsc () (list 'if (list 'eq 'ps-print-emacs-type ''emacs)
2847 [f22] ''f22))
2848 (defmacro ps-c-prsc () (list 'if (list 'eq 'ps-print-emacs-type ''emacs)
2849 [C-f22]
2850 ''(control f22)))
2851 (defmacro ps-s-prsc () (list 'if (list 'eq 'ps-print-emacs-type ''emacs)
2852 [S-f22]
2853 ''(shift f22)))
2854
2855 ;; Look in an article or mail message for the Subject: line. To be
2856 ;; placed in ps-left-headers.
2857 (defun ps-article-subject ()
2858 (save-excursion
2859 (goto-char (point-min))
2860 (if (re-search-forward "^Subject:[ \t]+\\(.*\\)$" nil t)
2861 (buffer-substring (match-beginning 1) (match-end 1))
2862 "Subject ???")))
2863
2864 ;; Look in an article or mail message for the From: line. Sorta-kinda
2865 ;; understands RFC-822 addresses and can pull the real name out where
2866 ;; it's provided. To be placed in ps-left-headers.
2867 (defun ps-article-author ()
2868 (save-excursion
2869 (goto-char (point-min))
2870 (if (re-search-forward "^From:[ \t]+\\(.*\\)$" nil t)
2871 (let ((fromstring (buffer-substring (match-beginning 1) (match-end 1))))
2872 (cond
2873
2874 ;; Try first to match addresses that look like
2875 ;; thompson@wg2.waii.com (Jim Thompson)
2876 ((string-match ".*[ \t]+(\\(.*\\))" fromstring)
2877 (substring fromstring (match-beginning 1) (match-end 1)))
2878
2879 ;; Next try to match addresses that look like
2880 ;; Jim Thompson <thompson@wg2.waii.com>
2881 ((string-match "\\(.*\\)[ \t]+<.*>" fromstring)
2882 (substring fromstring (match-beginning 1) (match-end 1)))
2883
2884 ;; Couldn't find a real name -- show the address instead.
2885 (t fromstring)))
2886 "From ???")))
2887
2888 ;; A hook to bind to gnus-Article-prepare-hook. This will set the ps-
2889 ;; left-headers specially for gnus articles. Unfortunately, gnus-
2890 ;; article-mode-hook is called only once, the first time the *Article*
2891 ;; buffer enters that mode, so it would only work for the first time
2892 ;; we ran gnus. The second time, this hook wouldn't get set up. The
2893 ;; only alternative is gnus-article-prepare-hook.
2894 (defun ps-gnus-article-prepare-hook ()
2895 (setq ps-header-lines 3)
2896 (setq ps-left-header
2897 ;; The left headers will display the article's subject, its
2898 ;; author, and the newsgroup it was in.
2899 (list 'ps-article-subject 'ps-article-author 'gnus-newsgroup-name)))
2900
2901 ;; A hook to bind to vm-mode-hook to locally bind prsc and set the ps-
2902 ;; left-headers specially for mail messages. This header setup would
2903 ;; also work, I think, for RMAIL.
2904 (defun ps-vm-mode-hook ()
2905 (local-set-key (ps-prsc) 'ps-vm-print-message-from-summary)
2906 (setq ps-header-lines 3)
2907 (setq ps-left-header
2908 ;; The left headers will display the message's subject, its
2909 ;; author, and the name of the folder it was in.
2910 (list 'ps-article-subject 'ps-article-author 'buffer-name)))
2911
2912 ;; Every now and then I forget to switch from the *Summary* buffer to
2913 ;; the *Article* before hitting prsc, and a nicely formatted list of
2914 ;; article subjects shows up at the printer. This function, bound to
2915 ;; prsc for the gnus *Summary* buffer means I don't have to switch
2916 ;; buffers first.
2917 (defun ps-gnus-print-article-from-summary ()
2918 (interactive)
2919 (if (get-buffer "*Article*")
2920 (save-excursion
2921 (set-buffer "*Article*")
2922 (ps-spool-buffer-with-faces))))
2923
2924 ;; See ps-gnus-print-article-from-summary. This function does the
2925 ;; same thing for vm.
2926 (defun ps-vm-print-message-from-summary ()
2927 (interactive)
2928 (if (and (boundp 'vm-mail-buffer) (symbol-value 'vm-mail-buffer))
2929 (save-excursion
2930 (set-buffer (symbol-value 'vm-mail-buffer))
2931 (ps-spool-buffer-with-faces))))
2932
2933 ;; A hook to bind to bind to gnus-summary-setup-buffer to locally bind
2934 ;; prsc.
2935 (defun ps-gnus-summary-setup ()
2936 (local-set-key (ps-prsc) 'ps-gnus-print-article-from-summary))
2937
2938 ;; Look in an article or mail message for the Subject: line. To be
2939 ;; placed in ps-left-headers.
2940 (defun ps-info-file ()
2941 (save-excursion
2942 (goto-char (point-min))
2943 (if (re-search-forward "File:[ \t]+\\([^, \t\n]*\\)" nil t)
2944 (buffer-substring (match-beginning 1) (match-end 1))
2945 "File ???")))
2946
2947 ;; Look in an article or mail message for the Subject: line. To be
2948 ;; placed in ps-left-headers.
2949 (defun ps-info-node ()
2950 (save-excursion
2951 (goto-char (point-min))
2952 (if (re-search-forward "Node:[ \t]+\\([^,\t\n]*\\)" nil t)
2953 (buffer-substring (match-beginning 1) (match-end 1))
2954 "Node ???")))
2955
2956 (defun ps-info-mode-hook ()
2957 (setq ps-left-header
2958 ;; The left headers will display the node name and file name.
2959 (list 'ps-info-node 'ps-info-file)))
2960
2961 ;; WARNING! The following function is a *sample* only, and is *not*
2962 ;; meant to be used as a whole unless you understand what the effects
2963 ;; will be! (In fact, this is a copy of Jim's setup for ps-print -- I'd
2964 ;; be very surprised if it was useful to *anybody*, without
2965 ;; modification.)
2966
2967 (defun ps-jts-ps-setup ()
2968 (global-set-key (ps-prsc) 'ps-spool-buffer-with-faces) ;f22 is prsc
2969 (global-set-key (ps-s-prsc) 'ps-spool-region-with-faces)
2970 (global-set-key (ps-c-prsc) 'ps-despool)
2971 (add-hook 'gnus-article-prepare-hook 'ps-gnus-article-prepare-hook)
2972 (add-hook 'gnus-summary-mode-hook 'ps-gnus-summary-setup)
2973 (add-hook 'vm-mode-hook 'ps-vm-mode-hook)
2974 (add-hook 'vm-mode-hooks 'ps-vm-mode-hook)
2975 (add-hook 'Info-mode-hook 'ps-info-mode-hook)
2976 (setq ps-spool-duplex t)
2977 (setq ps-print-color-p nil)
2978 (setq ps-lpr-command "lpr")
2979 (setq ps-lpr-switches '("-Jjct,duplex_long"))
2980 'ps-jts-ps-setup)
2981
2982 ;; WARNING! The following function is a *sample* only, and is *not*
2983 ;; meant to be used as a whole unless it corresponds to your needs.
2984 ;; (In fact, this is a copy of Jack's setup for ps-print --
2985 ;; I would not be that surprised if it was useful to *anybody*,
2986 ;; without modification.)
2987
2988 (defun ps-jack-setup ()
2989 (setq ps-print-color-p 'nil
2990 ps-lpr-command "lpr"
2991 ps-lpr-switches (list)
2992
2993 ps-paper-type 'a4
2994 ps-landscape-mode 't
2995 ps-number-of-columns 2
2996
2997 ps-left-margin (/ (* 72 1.0) 2.54) ; 1.0 cm
2998 ps-right-margin (/ (* 72 1.0) 2.54) ; 1.0 cm
2999 ps-inter-column (/ (* 72 1.0) 2.54) ; 1.0 cm
3000 ps-bottom-margin (/ (* 72 1.5) 2.54) ; 1.5 cm
3001 ps-top-margin (/ (* 72 1.5) 2.54) ; 1.5 cm
3002 ps-header-offset (/ (* 72 1.0) 2.54) ; 1.0 cm
3003 ps-header-line-pad .15
3004 ps-print-header t
3005 ps-print-header-frame t
3006 ps-header-lines 2
3007 ps-show-n-of-n t
3008 ps-spool-duplex nil
3009
3010 ps-font-family 'Courier
3011 ps-font-size 5.5
3012 ps-header-font-family 'Helvetica
3013 ps-header-font-size 6
3014 ps-header-title-font-size 8)
3015 'ps-jack-setup)
3016
3017 (provide 'ps-print)
3018
3019 ;;; ps-print.el ends here