4

I use this function for printing a buffer's content to PDF

(from my .emacs file:)

(defun print-to-pdf ()
  (interactive)
  (ps-spool-buffer-with-faces)
  (switch-to-buffer "*PostScript*")
  (write-file "/tmp/tmp.ps")
  (kill-buffer "tmp.ps")
  (setq cmd (concat "ps2pdf14 /tmp/tmp.ps /home/user/" (buffer-name) ".pdf"))
  (shell-command cmd)
  (shell-command "rm /tmp/tmp.ps")
  (message (concat "Saved to:  /home/user/" (buffer-name) ".pdf"))  
  )

I cannot, however, find a way to enable or apply the visual-line minor mode to the PostScript buffer before it gets written to disk so to enable word wrap in the output.

Werner
  • 261
  • 2
  • 8

1 Answers1

5

The problem with getting visual line mode to be respected is that it inserts "soft newlines" (which get ignored by the PS renderer). A solution is to replace these with hard newlines. The code below does what you want, I think. Note that we call harden-newlines in a temporary buffer so as not to mess up the current document. Also, I've changed the output destination to always land in /tmp/print.pdf. It seems... unwise to overwrite documents in your /home without any sort of warning! You can always move the PDF afterwards.

Anyway, here you go. Is this what you wanted?

(defun harden-newlines ()
  (interactive)
  "Make all the newlines in the buffer hard."
  (save-excursion
    (goto-char (point-min))
    (while (search-forward "\n" nil t)
      (backward-char)
      (put-text-property (point) (1+ (point)) 'hard t)
      (forward-char))))

(defun spool-buffer-given-name (name)
  (load "ps-print")
  (let ((tmp ps-left-header))
    (unwind-protect
        (progn
          (setq ps-left-header
                (list (lambda () name) 'ps-header-dirpart))
          (ps-spool-buffer-with-faces))
      (setf ps-left-header tmp))))

(defun print-to-pdf ()
  "Print the current file to /tmp/print.pdf"
  (interactive)
  (let ((wbuf (generate-new-buffer "*Wrapped*"))
        (sbuf (current-buffer)))
    (jit-lock-fontify-now)
    (save-current-buffer
      (set-buffer wbuf)
      (insert-buffer sbuf)
      (longlines-mode t)
      (harden-newlines)
      (spool-buffer-given-name (buffer-name sbuf))
      (kill-buffer wbuf)
      (switch-to-buffer "*PostScript*")
      (write-file "/tmp/print.ps")
      (kill-buffer (current-buffer)))
    (call-process "ps2pdf14" nil nil nil
                  "/tmp/print.ps" "/tmp/print.pdf")
    (delete-file "/tmp/print.ps")
    (message "PDF saved to /tmp/print.pdf")))
Rupert Swarbrick
  • 2,793
  • 16
  • 26
  • Thanks Rupert. In your solution, however, from page 2 onwards, the font-locking is removed. – Werner Sep 13 '11 at 13:12
  • Also longlines limits a line to 80 characters. Replacing `(longlines-mode t)` with `(visual-line-mode t)` removes wrapping (because the breaks are different?). – Werner Sep 13 '11 at 13:17
  • Yep, that makes sense (about longlines-mode). I don't know what's going on with the font locking though... :-( – Rupert Swarbrick Sep 13 '11 at 15:37
  • Hmm, this is really strange. For the tex file I just tested, the font locking disappears halfway down page 3... – Rupert Swarbrick Sep 13 '11 at 15:40
  • Solved! The problem is the "jit" font lock (which for speed only fontifies the bit of the buffer you can see). You just have to add a call to `(jit-lock-fontify-now)`: I'll edit my answer in a sec. – Rupert Swarbrick Sep 13 '11 at 15:49
  • Ok, I'll leave it alone now... but I also changed the way the header gets calculated: the "*Wrapped* (unsaved)" was bugging me, and I think I'm going to use the resulting code now! :-) – Rupert Swarbrick Sep 13 '11 at 16:05
  • Thanks for the update. This version of your code gives me a `spool-buffer-given-name: Symbol's value as variable is void: ps-left-header` – Werner Sep 13 '11 at 17:00
  • After commentting out `(spool-buffer-given-name (buffer-name sbuf))` the resulting pdf is empty – Werner Sep 13 '11 at 17:04
  • Ah. Bugger. That's because ps-left-header only gets defined when ps-spool-buffer-* first gets called (because of autoloading). Moving this to chat now, and I'll edit the code in a sec. Basically, just add `(load "ps-print")` to the top of the spool buffer and it should work. – Rupert Swarbrick Sep 13 '11 at 17:34
  • This solution works great now. I only feel that `longlines-mode` should be replaced by `visual-lines-mode` as _this mode will likely deprecate LongLines in a future version of Emacs_ (from [link](http://www.emacswiki.org/emacs/VisualLineMode)) As I stated earlier, simply replacing the line in which you set `longlines-mode` breaks the wrapping. Any clue how to fix that? Edit: Also, the header in the resulting PDF now is **HeaderLinesLeft** – Werner Sep 14 '11 at 09:03
  • Yep, that seems sensible. Feel free to change it on your filesystem... As for the header being weird, I'm not sure what can be causing that, sorry. If you can't work out what's going on, maybe post that as a separate question? (These comments are getting looong) – Rupert Swarbrick Sep 14 '11 at 13:32
  • I just wanted to create a comment with two related posts: The first follow-up: http://stackoverflow.com/questions/7442100/formatting-a-header-in-an-emacs-function-to-print-a-buffer-to-pdf-w-line-wrappi And, the second follow-up: http://stackoverflow.com/questions/16779882/save-buffer-as-a-pdf-with-ns-write-file-using-panel-or-similar-option I still need to implement opening the panel, but it almost does what I'm looking for. – lawlist May 28 '13 at 15:14
  • `insert-buffer` results in an byte compilation error. The help says "This function is meant for the user to run interactively. Don't call it from programs: use `insert-buffer-substring` instead!" – Michael Hoffman Aug 01 '14 at 17:51