Add experimental version of muse-blosxom.el to project
[muse-el.git] / muse-book.el
blob38f7e3a996c90bb519365e8dd80311d1c27e1b59
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;
3 ;; Muse Book Publishing
4 ;;
5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 (require 'muse-publish)
8 (require 'muse-project)
9 (require 'muse-latex)
11 (defgroup muse-book nil
12 "Module for publishing a series of Muse pages as a complete book.
13 Each page will become a separate chapter in the book, unless the
14 style keyword :nochapters is used, in which case they are all run
15 together as if one giant chapter."
16 :group 'muse-publish)
18 (defcustom muse-before-book-publish-hook nil
19 "A hook run in the book buffer before it is marked up."
20 :type 'hook
21 :group 'muse-book)
23 (defcustom muse-after-book-publish-hook nil
24 "A hook run in the book buffer after it is marked up."
25 :type 'hook
26 :group 'muse-book)
28 (defcustom muse-book-latex-header
29 "\\documentclass{book}
31 \\usepackage[english]{babel}
32 \\usepackage[latin1]{inputenc}
33 \\usepackage[T1]{fontenc}
35 \\begin{document}
37 \\title{<lisp>(muse-publishing-directive \"title\")</lisp>}
38 \\author{<lisp>(muse-publishing-directive \"author\")</lisp>}
39 \\date{<lisp>(muse-publishing-directive \"date\")</lisp>}
41 \\maketitle
43 \\tableofcontents\n"
44 "Header used for publishing books to LaTeX."
45 :type '(choice string file)
46 :group 'muse-book)
48 (defcustom muse-book-latex-footer "\n\\end{document}"
49 "Footer used for publishing books to LaTeX."
50 :type '(choice string file)
51 :group 'muse-book)
53 (defun muse-book-publish-chapter (title entry style &optional nochapters)
54 "Publish the chapter TITLE for the file ENTRY using STYLE.
55 TITLE is a string, ENTRY is a cons of the form (PAGE-NAME .
56 FILE), and STYLE is a Muse style list.
58 This routine does the same basic work as `muse-publish-markup-buffer',
59 but treating the page as if it were a single chapter within a book."
60 (let ((muse-publishing-directives (list (cons "title" title)))
61 (muse-publishing-current-file (cdr entry))
62 (beg (point)) end)
63 (insert-file-contents (cdr entry))
64 (setq end (copy-marker (point-max) t))
65 (muse-publish-markup-region beg end (car entry) style)
66 (goto-char beg)
67 (unless (or nochapters
68 (muse-style-element :nochapters style))
69 (insert "\n" (muse-markup-text 'chapter)
70 (let ((chap (muse-publishing-directive "title")))
71 (if (string= chap title)
72 (car entry)
73 chap))
74 (muse-markup-text 'chapter-end) "\n\n"))
75 (save-restriction
76 (narrow-to-region beg end)
77 (muse-publish-markup (or title "")
78 '((100 "<\\(lisp\\)>" 0
79 muse-publish-markup-tag)))
80 (muse-style-run-hooks :after style))
81 (goto-char end)))
83 (defun muse-book-publish-project
84 (project book title style &optional output-dir force)
85 "Publish PROJECT under the name BOOK with the given TITLE and STYLE.
86 BOOK should be a page name, i.e., letting the style determine the
87 prefix and/or suffix. The book is published to OUTPUT-DIR. If FORCE
88 is nil, the book is only published if at least one of its component
89 pages has changed since it was last published."
90 (interactive
91 (let ((project (muse-read-project "Publish project as book: " nil t)))
92 (append (list project
93 (read-string "Basename of book (without extension): ")
94 (read-string "Title of book: "))
95 (muse-publish-get-info))))
96 ;; Cleanup some of the arguments
97 (setq project (muse-project project)
98 style (muse-style style))
99 ;; See if any of the project's files need saving first
100 (muse-project-save-buffers project)
101 ;; Publish each page in the project as a chapter in one large book
102 (let* ((output-path (muse-publish-output-file book output-dir style))
103 (output-suffix (muse-style-element :osuffix style))
104 (target output-path)
105 (pats (cadr project))
106 (publish force) published)
107 (when output-suffix
108 (setq target (concat (file-name-sans-extension target)
109 output-suffix)))
110 ;; Unless force is non-nil, determine if the book needs publishing
111 (unless force
112 (while pats
113 (if (symbolp (car pats))
114 (if (eq :book-end (car pats))
115 (setq pats nil)
116 (setq pats (cddr pats)))
117 (let ((entries (muse-project-file-entries (car pats))))
118 (while entries
119 (if (and (not (muse-project-private-p (cdar entries)))
120 (file-newer-than-file-p (cdar entries) target))
121 (setq publish t entries nil)
122 (setq entries (cdr entries)))))
123 (setq pats (cdr pats)))))
124 ;; Create the book from all its component parts
125 (if (not publish)
126 (message "The book \"%s\" is up-to-date." book)
127 (with-temp-buffer
128 (let ((style-final (muse-style-element :final style t))
129 (style-header (muse-style-element :header style))
130 (style-footer (muse-style-element :footer style))
131 (muse-publishing-current-style style)
132 (muse-publishing-directives
133 (list (cons "title" title)
134 (cons "date" (format-time-string "%B %e, %Y"))))
135 (muse-publishing-p t)
136 (muse-current-project project)
137 nochapters)
138 (run-hooks 'muse-before-book-publish-hook)
139 (setq pats (cadr project))
140 (let ((style-final style-final)
141 (style-header style-header)
142 (style-footer style-footer))
143 (while pats
144 (if (symbolp (car pats))
145 (cond
146 ((eq :book-part (car pats))
147 (insert "\n" (muse-markup-text 'part) (cadr pats)
148 (muse-markup-text 'part-end) "\n")
149 (setq pats (cddr pats)))
150 ((eq :book-chapter (car pats))
151 (insert "\n" (muse-markup-text 'chapter) (cadr pats)
152 (muse-markup-text 'chapter-end) "\n")
153 (setq pats (cddr pats)))
154 ((eq :nochapters (car pats))
155 (setq nochapters t
156 pats (cddr pats)))
157 ((eq :book-style (car pats))
158 (setq style (muse-style (cadr pats)))
159 (setq style-final (muse-style-element :final style t)
160 style-header (muse-style-element :header style)
161 style-footer (muse-style-element :footer style)
162 muse-publishing-current-style style)
163 (setq pats (cddr pats)))
164 ((eq :book-funcall (car pats))
165 (funcall (cadr pats))
166 (setq pats (cddr pats)))
167 ((eq :book-end (car pats))
168 (setq pats nil))
170 (setq pats (cddr pats))))
171 (let ((entries (muse-project-file-entries (car pats))))
172 (while (and entries (car entries) (caar entries))
173 (unless (muse-project-private-p (cdar entries))
174 (muse-book-publish-chapter title (car entries)
175 style nochapters)
176 (setq published t))
177 (setq entries (cdr entries))))
178 (setq pats (cdr pats)))))
179 (goto-char (point-min))
180 (if style-header (muse-insert-file-or-string style-header book))
181 (goto-char (point-max))
182 (if style-footer (muse-insert-file-or-string style-footer book))
183 (run-hooks 'muse-after-book-publish-hook)
184 (let ((backup-inhibited t))
185 (write-file output-path))
186 (if style-final
187 (funcall style-final book output-path target)))))
188 (if published
189 (message "The book \"%s\" has been published." book))
190 published))
192 (unless (assoc "book-latex" muse-publishing-styles)
193 (muse-derive-style "book-latex" "latex"
194 :header 'muse-book-latex-header
195 :footer 'muse-book-latex-footer)
197 (muse-derive-style "book-pdf" "pdf"
198 :header 'muse-book-latex-header
199 :footer 'muse-book-latex-footer))
201 (provide 'muse-book)
203 ;;; muse-book.el ends here