1 ;;; muse-book.el --- publish entries into a compilation
3 ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
4 ;; Free Software Foundation, Inc.
6 ;; This file is part of Emacs Muse. It is not part of GNU Emacs.
8 ;; Emacs Muse is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published
10 ;; by the Free Software Foundation; either version 3, or (at your
11 ;; option) any later version.
13 ;; Emacs Muse is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with Emacs Muse; see the file COPYING. If not, write to the
20 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 ;; Boston, MA 02110-1301, USA.
29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;; Muse Book Publishing
33 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
35 (require 'muse-publish)
36 (require 'muse-project)
38 (require 'muse-regexps)
40 (defgroup muse-book nil
41 "Module for publishing a series of Muse pages as a complete book.
42 Each page will become a separate chapter in the book, unless the
43 style keyword :nochapters is used, in which case they are all run
44 together as if one giant chapter."
47 (defcustom muse-book-before-publish-hook nil
48 "A hook run in the book buffer before it is marked up."
52 (defcustom muse-book-after-publish-hook nil
53 "A hook run in the book buffer after it is marked up."
57 (defcustom muse-book-latex-header
58 "\\documentclass{book}
60 \\usepackage[english]{babel}
61 \\usepackage[latin1]{inputenc}
62 \\usepackage[T1]{fontenc}
66 \\title{<lisp>(muse-publishing-directive \"title\")</lisp>}
67 \\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
68 \\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
73 "Header used for publishing books to LaTeX. This may be text or a filename."
77 (defcustom muse-book-latex-footer
78 "<lisp>(muse-latex-bibliography)</lisp>
80 "Footer used for publishing books to LaTeX. This may be text or a filename."
84 (defun muse-book-publish-chapter (title entry style &optional nochapters)
85 "Publish the chapter TITLE for the file ENTRY using STYLE.
86 TITLE is a string, ENTRY is a cons of the form (PAGE-NAME .
87 FILE), and STYLE is a Muse style list.
89 This routine does the same basic work as `muse-publish-markup-buffer',
90 but treating the page as if it were a single chapter within a book."
91 (let ((muse-publishing-directives (list (cons "title" title)))
92 (muse-publishing-current-file (cdr entry))
94 (muse-insert-file-contents (cdr entry))
95 (setq end (copy-marker (point-max) t))
96 (muse-publish-markup-region beg end (car entry) style)
98 (unless (or nochapters
99 (muse-style-element :nochapters style))
101 (muse-insert-markup (muse-markup-text 'chapter))
102 (insert (let ((chap (muse-publishing-directive "title")))
103 (if (string= chap title)
106 (muse-insert-markup (muse-markup-text 'chapter-end))
109 (narrow-to-region beg end)
110 (muse-publish-markup (or title "")
111 '((100 "<\\(lisp\\)>" 0
112 muse-publish-markup-tag)))
113 (muse-style-run-hooks :after style))
116 (defun muse-book-publish-p (project target)
117 "Determine whether the book in PROJECT is out-of-date."
118 (let ((pats (cadr project)))
121 (if (symbolp (car pats))
122 (if (eq :book-end (car pats))
124 ;; skip past symbol-value pair
125 (setq pats (cddr pats)))
126 (dolist (entry (muse-project-file-entries (car pats)))
127 (when (and (not (muse-project-private-p (cdr entry)))
128 (file-newer-than-file-p (cdr entry) target))
130 (setq pats (cdr pats)))))))
132 (defun muse-book-get-directives (file)
133 "Interpret any publishing directives contained in FILE.
134 This is meant to be called in a temp buffer that will later be
135 used for publishing."
137 (narrow-to-region (point) (point))
140 (muse-insert-file-contents file)
143 `(;; Remove leading and trailing whitespace from the file
144 (100 "\\(\\`\n+\\|\n+\\'\\)" 0 "")
145 ;; Remove trailing whitespace from all lines
146 (200 ,(concat "[" muse-regexp-blank "]+$") 0 "")
147 ;; Handle any leading #directives
148 (300 "\\`#\\([a-zA-Z-]+\\)\\s-+\\(.+\\)\n+"
149 0 muse-publish-markup-directive))))
150 (delete-region (point-min) (point-max)))))
152 (defun muse-book-publish-project
153 (project book title style &optional output-dir force)
154 "Publish PROJECT under the name BOOK with the given TITLE and STYLE.
155 BOOK should be a page name, i.e., letting the style determine the
156 prefix and/or suffix. The book is published to OUTPUT-DIR. If FORCE
157 is nil, the book is only published if at least one of its component
158 pages has changed since it was last published."
160 (let ((project (muse-read-project "Publish project as book: " nil t)))
161 (append (list project
162 (read-string "Basename of book (without extension): ")
163 (read-string "Title of book: "))
164 (muse-publish-get-info))))
165 (setq project (muse-project project))
166 (let ((muse-current-project project))
167 ;; See if any of the project's files need saving first
168 (muse-project-save-buffers project)
170 (muse-book-publish book style output-dir force title)))
172 (defun muse-book-publish (file style &optional output-dir force title)
173 "Publish FILE as a book with the given TITLE and STYLE.
174 The book is published to OUTPUT-DIR. If FORCE is nil, the book
175 is only published if at least one of its component pages has
176 changed since it was last published."
177 ;; Cleanup some of the arguments
178 (let ((style-name style))
179 (setq style (muse-style style))
181 (error "There is no style '%s' defined" style-name)))
182 ;; Publish each page in the project as a chapter in one large book
183 (let* ((output-path (muse-publish-output-file file output-dir style))
184 (output-suffix (muse-style-element :osuffix style))
186 (project muse-current-project)
189 (setq target (concat (muse-path-sans-extension target)
191 ;; Unless force is non-nil, determine if the book needs publishing
193 (not (muse-book-publish-p project target)))
194 (message "The book \"%s\" is up-to-date." file)
195 ;; Create the book from all its component parts
196 (muse-with-temp-buffer
197 (let ((style-final (muse-style-element :final style t))
198 (style-header (muse-style-element :header style))
199 (style-footer (muse-style-element :footer style))
200 (muse-publishing-current-style style)
201 (muse-publishing-directives
202 (list (cons "title" (or title (muse-page-name file)))
203 (cons "date" (format-time-string "%B %e, %Y"))))
204 (muse-publishing-p t)
205 (muse-current-project project)
206 (pats (cadr project))
208 (run-hooks 'muse-before-book-publish-hook)
209 (let ((style-final style-final)
210 (style-header style-header)
211 (style-footer style-footer))
213 (muse-book-get-directives file)
214 (setq title (muse-publishing-directive "title")))
216 (if (symbolp (car pats))
218 ((eq :book-part (car pats))
220 (muse-insert-markup (muse-markup-text 'part))
222 (muse-insert-markup (muse-markup-text 'part-end))
224 (setq pats (cddr pats)))
225 ((eq :book-chapter (car pats))
227 (muse-insert-markup (muse-markup-text 'chapter))
229 (muse-insert-markup (muse-markup-text 'chapter-end))
231 (setq pats (cddr pats)))
232 ((eq :nochapters (car pats))
235 ((eq :book-style (car pats))
236 (setq style (muse-style (cadr pats)))
237 (setq style-final (muse-style-element :final style t)
238 style-header (muse-style-element :header style)
239 style-footer (muse-style-element :footer style)
240 muse-publishing-current-style style)
241 (setq pats (cddr pats)))
242 ((eq :book-funcall (car pats))
243 (funcall (cadr pats))
244 (setq pats (cddr pats)))
245 ((eq :book-end (car pats))
248 (setq pats (cddr pats))))
249 (let ((entries (muse-project-file-entries (car pats))))
250 (while (and entries (car entries) (caar entries))
251 (unless (muse-project-private-p (cdar entries))
252 (muse-book-publish-chapter title (car entries)
255 (setq entries (cdr entries))))
256 (setq pats (cdr pats)))))
257 (goto-char (point-min))
258 (if style-header (muse-insert-file-or-string style-header file))
259 (goto-char (point-max))
260 (if style-footer (muse-insert-file-or-string style-footer file))
261 (run-hooks 'muse-after-book-publish-hook)
262 (if (muse-write-file output-path)
264 (funcall style-final file output-path target))
265 (setq published nil)))))
267 (message "The book \"%s\" has been published." file))
270 ;;; Register the Muse BOOK Publishers
272 (muse-derive-style "book-latex" "latex"
273 :header 'muse-book-latex-header
274 :footer 'muse-book-latex-footer
275 :publish 'muse-book-publish)
277 (muse-derive-style "book-pdf" "pdf"
278 :header 'muse-book-latex-header
279 :footer 'muse-book-latex-footer
280 :publish 'muse-book-publish)
284 ;;; muse-book.el ends here