1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; Muse HTML Publishing
5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 (require 'muse-publish
)
8 (require 'muse-regexps
)
10 (defgroup muse-html nil
11 "Options controlling the behaviour of Muse HTML publishing.
12 See `muse-html' for more information."
15 (defcustom muse-html-extension
".html"
16 "Default file extension for publishing HTML files."
20 (defcustom muse-html-style-sheet
22 background: white; color: black;
23 margin-left: 3%; margin-right: 7%;
27 p.verse { margin-left: 3% }
29 .example { margin-left: 3% }
35 h3 { margin-bottom: 0px; }"
36 "Text to prepend to a Muse mail message being published.
37 This text may contain <lisp> markup tags."
41 (defcustom muse-html-header
42 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">
46 (concat (muse-publishing-directive \"title\")
47 (let ((author (muse-publishing-directive \"author\")))
48 (if (not (string= author (user-full-name)))
49 (concat \" (by \" author \")\"))))</lisp></title>
50 <meta name=\"generator\" content=\"muse.el\">
51 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
52 content=\"<lisp>muse-html-meta-content-type</lisp>\">
54 (let ((maintainer (muse-style-element :maintainer)))
56 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\")))
58 <style type=\"text/css\">
59 <lisp>muse-html-style-sheet</lisp>
64 (concat (muse-publishing-directive \"title\")
65 (let ((author (muse-publishing-directive \"author\")))
66 (if (not (string= author (user-full-name)))
67 (concat \" (by \" author \")\"))))</lisp></h1>
68 <!-- Page published by Emacs Muse begins here -->\n"
69 "Header used for publishing HTML files."
70 :type
'(choice string file
)
73 (defcustom muse-html-footer
"
74 <!-- Page published by Emacs Muse ends here -->
77 "Footer used for publishing HTML files."
78 :type
'(choice string file
)
81 (defcustom muse-html-anchor-on-word nil
82 "When true, anchors surround the closest word. This allows you
83 to select them in a browser (ie, for pasting), but has the
84 side-effect of marking up headers in multiple colours if your
85 header style is different from your link style."
89 (defcustom muse-html-table-attributes
90 "class=\"muse-table\" border=\"2\" cellpadding=\"5\""
91 "The attribute to be used with HTML <table> tags.
92 Note that since Muse supports direct insertion of HTML tags, you
93 can easily create any kind of table you want, as long as each
94 line begins at column 0 (to prevent it from being blockquote'd).
95 To make such a table, use this idiom:
99 [... contents of my table, in raw HTML ...]
102 It may look strange to have the tags out of sequence, but this is
103 because the Muse verbatim tag is handled during a different pass
104 than the HTML table tag."
108 (defcustom muse-html-markup-regexps
109 `(;; join together the parts of a list or table
110 (10000 "</\\([oud]l\\)>\\s-*<\\1>\\s-*" 0 "")
111 (10100 ,(concat " </t\\(body\\|head\\|foot\\)>\\s-*</table>\\s-*"
112 "<table[^>]*>\\s-*<t\\1>\n") 0 "")
113 (10200 "</table>\\s-*<table[^>]*>\n" 0 "")
115 ;; the beginning of the buffer begins the first paragraph
116 (10300 "\\`\n*\\([^<-]\\|<\\(em\\|strong\\|code\\)>\\|<a \\)" 0
117 "<p class=\"first\">\\1")
118 ;; plain paragraph separator
119 (10400 ,(concat "\\(\n</\\(blockquote\\|center\\)>\\)?\n"
122 "]*\n\\)+\\(<\\(blockquote\\|center\\)>\n\\)?")
123 0 muse-html-markup-paragraph
)
124 (10500 ,(concat "\\([^>"
128 "List of markup rules for publishing a Muse page to HTML.
129 For more on the structure of this list, see `muse-publish-markup-regexps'."
130 :type
'(repeat (choice
131 (list :tag
"Markup rule"
133 (choice regexp symbol
)
135 (choice string function symbol
))
139 (defcustom muse-html-markup-functions
140 '((anchor . muse-html-markup-anchor
)
141 (table . muse-html-markup-table
)
142 (footnote . muse-html-markup-footnote
))
143 "An alist of style types to custom functions for that kind of text.
144 For more on the structure of this list, see
145 `muse-publish-markup-functions'."
146 :type
'(alist :key-type symbol
:value-type function
)
149 (defcustom muse-html-markup-strings
150 '((image-with-desc .
"<img src=\"%s\" alt=\"%s\">")
151 (image-link .
"<img src=\"%s\">")
152 (url-with-image .
"<a class=\"image-link\" href=\"%s\"><img src=\"%s\"></a>")
153 (url-link .
"<a href=\"%s\">%s</a>")
154 (email-addr .
"<a href=\"mailto:%s\">%s</a>")
155 (emdash .
" — ")
161 (section-end .
"</h2>")
162 (subsection .
"<h3>")
163 (subsection-end .
"</h3>")
164 (subsubsection .
"<h4>")
165 (subsubsection-end .
"</h4>")
166 (begin-underline .
"<u>")
167 (end-underline .
"</u>")
168 (begin-literal .
"<code>")
169 (end-literal .
"</code>")
170 (begin-emph .
"<em>")
172 (begin-more-emph .
"<strong>")
173 (end-more-emph .
"</strong>")
174 (begin-most-emph .
"<strong><em>")
175 (end-most-emph .
"</em></strong>")
176 (begin-verse .
"<p class=\"verse\">\n")
177 (verse-space .
" ")
178 (end-verse-line .
"<br>")
179 (last-stanza-end .
"<br>")
180 (empty-verse-line .
"<br>")
182 (begin-example .
"<pre class=\"example\">")
183 (end-example .
"</pre>")
184 (begin-center .
"<center>\n")
185 (end-center .
"\n</center>")
186 (begin-quote .
"<blockquote>\n")
187 (end-quote .
"\n</blockquote>")
188 (begin-uli .
"<ul>\n<li>")
189 (end-uli .
"</li>\n</ul>")
190 (begin-oli .
"<ol>\n<li>")
191 (end-oli .
"</li>\n</ol>")
192 (begin-ddt .
"<dl>\n<dt><strong>")
193 (start-dde .
"</strong></dt>\n<dd>")
194 (end-ddt .
"</dd>\n</dl>"))
195 "Strings used for marking up text.
196 These cover the most basic kinds of markup, the handling of which
197 differs little between the various styles."
198 :type
'(alist :key-type symbol
:value-type string
)
201 (defcustom muse-html-markup-tags
202 '(("class" t t muse-html-class-tag
))
203 "A list of tag specifications, for specially marking up HTML."
204 :type
'(repeat (list (string :tag
"Markup tag")
205 (boolean :tag
"Expect closing tag" :value t
)
206 (boolean :tag
"Parse attributes" :value nil
)
210 (defcustom muse-html-markup-specials
215 "A table of characters which must be represented specially."
216 :type
'(alist :key-type character
:value-type string
)
219 (defcustom muse-html-meta-http-equiv
"Content-Type"
220 "The http-equiv attribute used for the HTML <meta> tag."
224 (defcustom muse-html-meta-content-type
"text/html"
225 "The content type used for the HTML <meta> tag."
229 (defcustom muse-html-meta-content-encoding
(if (featurep 'mule
)
232 "If set to the symbol 'detect, use `muse-coding-map' to try
233 and determine the HTML charset from emacs's coding. If set to a string, this
234 string will be used to force a particular charset"
235 :type
'(choice string symbol
)
238 (defcustom muse-html-charset-default
"iso-8859-1"
239 "The default HTML meta charset to use if no translation is found in
244 (defcustom muse-html-encoding-default
'iso-8859-1
245 "The default emacs coding use if no special characters are found"
249 (defcustom muse-html-encoding-map
250 '((iso-2022-jp .
"iso-2022-jp")
252 (japanese-iso-8bit .
"euc-jp")
253 (chinese-big5 .
"big5")
254 (mule-utf-8 .
"utf-8")
255 (chinese-iso-8bit .
"gb2312")
256 (chinese-gbk .
"gbk"))
257 "An alist mapping emacs coding systems to appropriate HTML charsets.
258 Use the base name of the coding system (ie, without the -unix)"
259 :type
'(alist :key-type coding-system
:value-type string
)
262 (defun muse-html-transform-content-type (content-type)
263 "Using `muse-html-encoding-map', try and resolve an emacs coding
264 system to an associated HTML coding system. If no match is found,
265 `muse-html-charset-default' is used instead."
266 (let ((match (assoc (coding-system-base content-type
)
267 muse-html-encoding-map
)))
270 muse-html-charset-default
)))
272 (defun muse-html-insert-anchor (anchor)
273 "Insert an anchor, either around the word at point, or within a tag."
274 (skip-chars-forward muse-regexp-space
)
275 (if (looking-at "<\\([^ />]+\\)>")
276 (let ((tag (match-string 1)))
277 (goto-char (match-end 0))
278 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
279 (when muse-html-anchor-on-word
280 (or (and (search-forward (format "</%s>" tag
)
281 (line-end-position) t
)
282 (goto-char (match-beginning 0)))
285 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
286 (when muse-html-anchor-on-word
290 (unless (fboundp 'looking-back
)
291 (defun looking-back (regexp &optional limit
)
293 (re-search-backward (concat "\\(?:" regexp
"\\)\\=") limit t
))))
295 (defun muse-html-markup-paragraph ()
296 (let ((end (copy-marker (match-end 0) t
)))
297 (goto-char (match-beginning 0))
298 (unless (eq (char-before) ?\
>) (insert "</p>"))
300 (unless (and (eq (char-after) ?\
<)
301 (not (or (looking-at "<\\(em\\|strong\\|code\\)>")
302 (and (looking-at "<a ")
303 (not (looking-at "<a[^>]+><img"))))))
305 ((looking-back "\\(</h[1-4]>\\|<hr>\\)\n\n")
306 (insert "<p class=\"first\">"))
307 ((looking-back "<\\(blockquote\\|center\\)>\n")
308 (insert "<p class=\"quoted\">"))
312 (defun muse-html-markup-anchor ()
314 (muse-html-insert-anchor (match-string 1))) "")
316 (defun muse-html-escape-string (str)
317 "Convert to character entities any non-alphanumeric characters
318 outside a few punctuation symbols, that risk being misinterpreted
323 (while (setq pos
(string-match (concat "[^-"
327 (setq code
(int-to-string (aref str pos
))
329 str
(replace-match (concat "&#" code
";") nil nil str
)
333 (defun muse-html-markup-footnote ()
334 (if (/= (line-beginning-position) (match-beginning 0))
335 "<sup><a name=\"fnr.\\1\" href=\"#fn.\\1\">\\1</a></sup>"
337 "<p class=\"footnote\"><a name=\"fn.\\1\" href=\"#fnr.\\1\">\\1.</a>"
340 (let* ((beg (goto-char (match-end 0)))
341 (end (and (search-forward "\n\n" nil t
)
343 (copy-marker (match-beginning 0))
345 (while (re-search-forward (concat "^["
349 (replace-match "\\1" t
))))))))
351 (defun muse-html-markup-table ()
354 (delete-region (match-beginning 0) (match-end 0))))
355 (fields (split-string str
"\\s-*|+\\s-*"))
356 (type (and (string-match "\\s-*\\(|+\\)\\s-*" str
)
357 (length (match-string 1 str
))))
358 (part (cond ((= type
1) "tbody")
360 ((= type
3) "tfoot")))
361 (col (cond ((= type
1) "td")
365 (insert "<table " muse-html-table-attributes
">\n"
368 (dolist (field fields
)
369 (insert " <" col
">" field
"</" col
">\n"))
374 ;; Handling of tags for HTML
376 (defun muse-html-insert-contents (depth)
377 (let ((max-depth (or depth
2))
381 (goto-char (point-min))
382 (search-forward "Page published by Emacs Muse begins here" nil t
)
384 (while (re-search-forward "^<h\\([0-9]+\\)>\\(.+?\\)</h\\1>" nil t
)
385 (unless (get-text-property (point) 'read-only
)
386 (setq l
(1- (string-to-int (match-string 1))))
391 (when (<= l max-depth
)
392 (setq contents
(cons (cons l
(match-string-no-properties 2))
394 (goto-char (match-beginning 2))
395 (muse-html-insert-anchor (concat "sec" (int-to-string index
)))
396 (setq index
(1+ index
)))))))
397 (setq index
1 contents
(reverse contents
))
398 (let ((depth 1) (sub-open 0) (p (point)))
399 (insert "<dl class=\"contents\">\n")
401 (insert "<dt class=\"contents\">\n")
402 (insert "<a href=\"#sec" (int-to-string index
) "\">"
403 (muse-publish-strip-tags (cdar contents
))
405 (setq index
(1+ index
))
407 (setq depth
(caar contents
)
408 contents
(cdr contents
))
411 ((< (caar contents
) depth
)
412 (let ((idx (caar contents
)))
414 (insert "</dl>\n</dd>\n")
415 (setq sub-open
(1- sub-open
)
417 ((> (caar contents
) depth
) ; can't jump more than one ahead
418 (insert "<dd>\n<dl class=\"contents\">\n")
419 (setq sub-open
(1+ sub-open
))))))
420 (while (> sub-open
0)
421 (insert "</dl>\n</dd>\n")
422 (setq sub-open
(1- sub-open
)))
424 (muse-publish-mark-read-only p
(point)))))
426 (defun muse-html-class-tag (beg end attrs
)
428 (insert "<span class=\"" (cdr (assoc "name" attrs
)) "\">")
432 ;; Register the Muse HTML Publisher
434 (defun muse-html-browse-file (file)
435 (browse-url (concat "file:" file
)))
437 (defun muse-html-encoding ()
438 (if (stringp muse-html-meta-content-encoding
)
439 muse-html-meta-content-encoding
440 (muse-html-transform-content-type
441 (or buffer-file-coding-system
442 muse-html-encoding-default
))))
444 (defun muse-html-prepare-buffer ()
445 (set (make-local-variable 'muse-publish-url-transforms
)
446 (cons 'muse-html-escape-string muse-publish-url-transforms
))
447 (make-local-variable 'muse-html-meta-http-equiv
)
448 (set (make-local-variable 'muse-html-meta-content-type
)
449 (concat muse-html-meta-content-type
"; charset="
450 (muse-html-encoding))))
452 (defun muse-html-finalize-buffer ()
453 (when muse-publish-generate-contents
454 (goto-char (car muse-publish-generate-contents
))
455 (muse-html-insert-contents (cdr muse-publish-generate-contents
)))
456 (when (memq buffer-file-coding-system
'(no-conversion undecided-unix
))
457 ;; make it agree with the default charset
458 (setq buffer-file-coding-system muse-html-encoding-default
)))
460 (unless (assoc "html" muse-publishing-styles
)
461 (muse-define-style "html"
462 :suffix
'muse-html-extension
463 :regexps
'muse-html-markup-regexps
464 :functions
'muse-html-markup-functions
465 :strings
'muse-html-markup-strings
466 :tags
'muse-html-markup-tags
467 :specials
'muse-html-markup-specials
468 :before
'muse-html-prepare-buffer
469 :after
'muse-html-finalize-buffer
470 :header
'muse-html-header
471 :footer
'muse-html-footer
472 :browser
'muse-html-browse-file
))
476 ;;; muse-html.el ends here