Basic WYSIWYG printing in GNU Emacs

Unicode text (top) converted to PS file (bottom)

Document printed using HP3545 printer
Unicode RTL text (top) converted to PS file (bottom)

ASCII text in Braille font (top) converted to PS file (bottom)

Unicode CJK text (top) converted to PS file (bottom)
Sheet music using Bravura Text (top) converted to PS file (bottom)
Sudoko grid (top) converted to PS file (bottom)

Text with fancy fonts (top) converted to PS file (bottom)


Text file (left) converted to PS file (right)
Text file (left) converted to PS file (right)

Wecome screen (left) converted to PS file (right)
Welcome screen (left) with SVG image and font variations converted to PS file (right) 
Full resolution JPG in image-mode (left) converted to fit in an A4 page (right)

 

  1. Format a text file in text-mode. Please note that you need to use proportional font (aka variable pitch font) in the frame to have text with varying sizes.  DejaVuSans font is used in the screenshot. Also, be aware that font-lock-mode might interfere with the formatting. If you're unable to change formatting, try turning off font-lock-mode.
  2. Install Ghostscript (# apt install ghostscript). (Only required if you want to visualize the PS document within Emacs.)
  3. Install appropriate printer driver e.g. for HP3545, # apt install hplip
  4. Run M-x ps-print-buffer-with-faces to send output to printer.
  5. Run C-u M-x ps-print-buffer-with-faces to send output to file (Postscript  PS format). Use $ lpr filename.ps  to send PS file filename.ps to printer.
Tip: Did you know PDF is a subset of PS specs? If you want to convert to PDF, you can use ps2pdf on Linux.

Code: https://gitlab.com/atamariya/emacs/tree/dev

Config

Some useful config:

(setq ps-print-header nil) ;; Turn off header

;; Print
;;<print> is prsc
;; Print using printer
(global-set-key (kbd "<print>") 'ps-spool-buffer-with-faces)
;; Print to file 
(global-set-key (kbd "S-<print>") (lambda ()
                                    (interactive)
                                    (let ((current-prefix-arg '(4)))
                                      (call-interactively 'ps-print-buffer-with-faces))))

;; Default font for arabic script in case your choice of font is being overridden
(set-fontset-font t 'arabic "Scheherazade")


Text for testing

You can generate text for testing using following Elisp code:

(let ((light-colors ["#707183" "#7388d6" "#909183" "#709870" "#907373"
                     "#6276ba" "#858580" "#80a880" "#887070"])
      (dark-colors ["grey55" "#93a8c6" "#b0b1a3" "#97b098" "#aebed8"
                    "#b0b0b3" "#90a890" "#a2b6da" "#9cb6ad"])
      (w 0))
  (text-mode)
  (set-frame-font "DejaVu Sans")
  (erase-buffer)
  (dotimes (i 2)
    (setq w (* .10 (+ i 10)))
    (insert (propertize (format "The quick brown fox jumps over the lazy dog (%f)\n" w)
                        'face `(:height ,w :foreground ,(aref dark-colors (% i 8)))))
    ))

 

Deriving font from existing font

PS Type 1 or base fonts are limited to 256 glyphs. You can derive a new PS font from a TrueType font for accessing glyphs beyond 256 glyph range.

/DeriveFont {    % newname charstring encoding fontname  |  font
    findfont dup length dict begin
    { 1 index /FID ne { def } { pop pop } ifelse } forall
        /Encoding exch def
        /CharStrings exch def
    currentdict
    end
    definefont
} bind def

% Sample usage
% Encoding - array of ps names
/E [/c00] def

% Charstrings - dictionary of ps names and glyph code
% In DejaVu font, 207 is the glyph code for ccaron
/C <</c00 207>> def

/F1 C E /DejaVuSans DeriveFont
/F1 findfont 12 scalefont setfont
<00> show

 

Lessons Learnt

  1. PS devices support cubic Bezier curves.
  2. TTF fonts use quadratic Bezier curves.
  3. Since TrueType fonts are treated uniformly by operating system, Emacs and Ghostscript, if you use TrueType fonts in Emacs buffer, you can achieve WYSIWYG printing without any additional configuration.
  4. 1 pt = 1/72 inch; 1 em = width of 'm' character; 1 en = width of 'n' character; 1 em = 2 en (typically)
  5. For variable pitch font, width of space might be less than average character width of the font.
  6. TAB character is used to jump to next tab stop. It is not fixed width.
  7. Font height is typically larger than font size. This includes some space so that text looks fine even without any explicit line spacing. e.g. for a 15pt DejaVu font, font height is 18 pt.
  8. When using show operator, interpreter looks up character code (0-255) in /Encoding array returning a glyph name. Then it looks up the glyph name in /CharStrings map to find a procedure to draw the glyph. You can use glyphshow operator to directly draw the glyphs available in the font.
  9. PS supports only JPG and bitmaps for images. Any other format would require conversion.
  10. Global origin is at bottom left.
  11. Be aware of monotone, RGB and CMYK color profiles of images. They have 1, 3 and 4 samples per pixel respectively.
  12. PS supports 13 fonts - regular, bold, italics and bold-italic fonts for Courier, Times and Helvetica font families and one font for Symbols. If the fonts defined in the document are missing on the system, PS will substitute one of these for display.
  13. For portability, images and fonts must be embedded in the document. Embedding is usually done in hex format (denoted as <20> for space).
  14. To reduce the size of document, define new names or procedures (aka variables and functions in other programming languages) for pieces of code used often. e.g. /f0 /DejaVuSans findfont 15 scalefont setfont def . Also, only include font information for those glyphs which are included in the document. This is called font subsetting.
  15. Ligatures might be just aesthetic in English, but is mandatory in some languages.
  16. Use pstack and == for printing stack and top of stack respectively while debugging.
  17. For Ghostscript to work with CIDFont (typically for CJK support),  the font must be installed in Resource/CIDFont directory. The filename must be the Postscript name of the font without any extension.
  18. Ghostscript can substitute fonts in a document if appropriate entries are added in Init/Fontmap for fonts and Init/cidfmap for CID fonts.
  19. Ghostscript 9.27 doesn't support Opentype fonts (OTF) and collections (OTC).
  20. On Linux, TTF fonts just need to be copied to /usr/share/fonts/truetype/.
  21. Use otc2otf to convert OTC/TTC collections to OTF/TTF font files. Use otf2ttf to convert OTF to TTF.
  22. Use ftview to view the glyphs in a font file.
  23. Use otfinfo or fc-query to view meta information in a font file.
  24. Format-14 cmap subtable partitions the UVSes supported by the font into two categories: “default” and “non-default” UVSes. Given a UVS, if the glyph obtained by looking up the base character of that sequence in the Unicode encoding subtable (i.e. the UCS-4 or the BMP encoding subtable) is the glyph to use for that sequence, then the sequence is a “default” UVS; otherwise it is a “non-default” UVS, and the glyph to use for that sequence is specified in the format 14 subtable itself. (Format 14: Unicode Variation Sequences)

References

Comments

Popular posts from this blog

GNU Emacs as a Comic Book Reader

Data Visualization with GNU Emacs

Mozilla Readability in GNU Emacs