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
21 "<style type=\"text/css\">
23 background: white; color: black;
24 margin-left: 3%; margin-right: 7%;
28 p.verse { margin-left: 3% }
30 .example { margin-left: 3% }
36 h3 { margin-bottom: 0px; }
38 "Store your stylesheet definitions here.
39 This is used in `muse-html-header'.
40 You can put raw CSS in here or a <link> tag to an external stylesheet.
41 This text may contain <lisp> markup tags.
43 An example of using <link> is as follows.
45 <link rel=\"stylesheet\" type=\"text/css\" charset=\"utf-8\" media=\"all\" href=\"/default.css\">"
49 (defcustom muse-html-header
50 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\">
54 (concat (muse-publishing-directive \"title\")
55 (let ((author (muse-publishing-directive \"author\")))
56 (if (not (string= author (user-full-name)))
57 (concat \" (by \" author \")\"))))</lisp></title>
58 <meta name=\"generator\" content=\"muse.el\">
59 <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\"
60 content=\"<lisp>muse-html-meta-content-type</lisp>\">
62 (let ((maintainer (muse-style-element :maintainer)))
64 (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\")))
66 <lisp>muse-html-style-sheet</lisp>
70 (concat (muse-publishing-directive \"title\")
71 (let ((author (muse-publishing-directive \"author\")))
72 (if (not (string= author (user-full-name)))
73 (concat \" (by \" author \")\"))))</lisp></h1>
74 <!-- Page published by Emacs Muse begins here -->\n"
75 "Header used for publishing HTML files."
76 :type
'(choice string file
)
79 (defcustom muse-html-footer
"
80 <!-- Page published by Emacs Muse ends here -->
83 "Footer used for publishing HTML files."
84 :type
'(choice string file
)
87 (defcustom muse-html-anchor-on-word nil
88 "When true, anchors surround the closest word. This allows you
89 to select them in a browser (ie, for pasting), but has the
90 side-effect of marking up headers in multiple colours if your
91 header style is different from your link style."
95 (defcustom muse-html-table-attributes
96 "class=\"muse-table\" border=\"2\" cellpadding=\"5\""
97 "The attribute to be used with HTML <table> tags.
98 Note that since Muse supports direct insertion of HTML tags, you
99 can easily create any kind of table you want, as long as each
100 line begins at column 0 (to prevent it from being blockquote'd).
101 To make such a table, use this idiom:
105 [... contents of my table, in raw HTML ...]
108 It may look strange to have the tags out of sequence, but this is
109 because the Muse verbatim tag is handled during a different pass
110 than the HTML table tag."
114 (defcustom muse-html-markup-regexps
115 `(;; join together the parts of a list or table
116 (10000 "</\\([oud]l\\)>\\s-*<\\1>\\s-*" 0 "")
117 (10100 ,(concat " </t\\(body\\|head\\|foot\\)>\\s-*</table>\\s-*"
118 "<table[^>]*>\\s-*<t\\1>\n") 0 "")
119 (10200 "</table>\\s-*<table[^>]*>\n" 0 "")
121 ;; the beginning of the buffer begins the first paragraph
122 (10300 "\\`\n*\\([^<-]\\|<\\(em\\|strong\\|code\\)>\\|<a \\)" 0
123 "<p class=\"first\">\\1")
124 ;; plain paragraph separator
125 (10400 ,(concat "\\(\n</\\(blockquote\\|center\\)>\\)?\n"
128 "]*\n\\)+\\(<\\(blockquote\\|center\\)>\n\\)?")
129 0 muse-html-markup-paragraph
)
130 (10500 ,(concat "\\([^>"
134 "List of markup rules for publishing a Muse page to HTML.
135 For more on the structure of this list, see `muse-publish-markup-regexps'."
136 :type
'(repeat (choice
137 (list :tag
"Markup rule"
139 (choice regexp symbol
)
141 (choice string function symbol
))
145 (defcustom muse-html-markup-functions
146 '((anchor . muse-html-markup-anchor
)
147 (table . muse-html-markup-table
)
148 (footnote . muse-html-markup-footnote
))
149 "An alist of style types to custom functions for that kind of text.
150 For more on the structure of this list, see
151 `muse-publish-markup-functions'."
152 :type
'(alist :key-type symbol
:value-type function
)
155 (defcustom muse-html-markup-strings
156 '((image-with-desc .
"<img src=\"%s\" alt=\"%s\">")
157 (image-link .
"<img src=\"%s\">")
158 (url-with-image .
"<a class=\"image-link\" href=\"%s\"><img src=\"%s\"></a>")
159 (url-link .
"<a href=\"%s\">%s</a>")
160 (email-addr .
"<a href=\"mailto:%s\">%s</a>")
161 (emdash .
" — ")
167 (section-end .
"</h2>")
168 (subsection .
"<h3>")
169 (subsection-end .
"</h3>")
170 (subsubsection .
"<h4>")
171 (subsubsection-end .
"</h4>")
172 (begin-underline .
"<u>")
173 (end-underline .
"</u>")
174 (begin-literal .
"<code>")
175 (end-literal .
"</code>")
176 (begin-emph .
"<em>")
178 (begin-more-emph .
"<strong>")
179 (end-more-emph .
"</strong>")
180 (begin-most-emph .
"<strong><em>")
181 (end-most-emph .
"</em></strong>")
182 (begin-verse .
"<p class=\"verse\">\n")
183 (verse-space .
" ")
184 (end-verse-line .
"<br>")
185 (last-stanza-end .
"<br>")
186 (empty-verse-line .
"<br>")
188 (begin-example .
"<pre class=\"example\">")
189 (end-example .
"</pre>")
190 (begin-center .
"<center>\n")
191 (end-center .
"\n</center>")
192 (begin-quote .
"<blockquote>\n")
193 (end-quote .
"\n</blockquote>")
194 (begin-uli .
"<ul>\n<li>")
195 (end-uli .
"</li>\n</ul>")
196 (begin-oli .
"<ol>\n<li>")
197 (end-oli .
"</li>\n</ol>")
198 (begin-ddt .
"<dl>\n<dt><strong>")
199 (start-dde .
"</strong></dt>\n<dd>")
200 (end-ddt .
"</dd>\n</dl>"))
201 "Strings used for marking up text.
202 These cover the most basic kinds of markup, the handling of which
203 differs little between the various styles."
204 :type
'(alist :key-type symbol
:value-type string
)
207 (defcustom muse-html-markup-tags
208 '(("class" t t muse-html-class-tag
))
209 "A list of tag specifications, for specially marking up HTML."
210 :type
'(repeat (list (string :tag
"Markup tag")
211 (boolean :tag
"Expect closing tag" :value t
)
212 (boolean :tag
"Parse attributes" :value nil
)
216 (defcustom muse-html-markup-specials
221 "A table of characters which must be represented specially."
222 :type
'(alist :key-type character
:value-type string
)
225 (defcustom muse-html-meta-http-equiv
"Content-Type"
226 "The http-equiv attribute used for the HTML <meta> tag."
230 (defcustom muse-html-meta-content-type
"text/html"
231 "The content type used for the HTML <meta> tag."
235 (defcustom muse-html-meta-content-encoding
(if (featurep 'mule
)
238 "If set to the symbol 'detect, use `muse-coding-map' to try
239 and determine the HTML charset from emacs's coding. If set to a string, this
240 string will be used to force a particular charset"
241 :type
'(choice string symbol
)
244 (defcustom muse-html-charset-default
"iso-8859-1"
245 "The default HTML meta charset to use if no translation is found in
250 (defcustom muse-html-encoding-default
'iso-8859-1
251 "The default emacs coding use if no special characters are found"
255 (defcustom muse-html-encoding-map
256 '((iso-2022-jp .
"iso-2022-jp")
258 (japanese-iso-8bit .
"euc-jp")
259 (chinese-big5 .
"big5")
260 (mule-utf-8 .
"utf-8")
261 (chinese-iso-8bit .
"gb2312")
262 (chinese-gbk .
"gbk"))
263 "An alist mapping emacs coding systems to appropriate HTML charsets.
264 Use the base name of the coding system (ie, without the -unix)"
265 :type
'(alist :key-type coding-system
:value-type string
)
268 (defun muse-html-transform-content-type (content-type)
269 "Using `muse-html-encoding-map', try and resolve an emacs coding
270 system to an associated HTML coding system. If no match is found,
271 `muse-html-charset-default' is used instead."
272 (let ((match (assoc (coding-system-base content-type
)
273 muse-html-encoding-map
)))
276 muse-html-charset-default
)))
278 (defun muse-html-insert-anchor (anchor)
279 "Insert an anchor, either around the word at point, or within a tag."
280 (skip-chars-forward muse-regexp-space
)
281 (if (looking-at "<\\([^ />]+\\)>")
282 (let ((tag (match-string 1)))
283 (goto-char (match-end 0))
284 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
285 (when muse-html-anchor-on-word
286 (or (and (search-forward (format "</%s>" tag
)
287 (line-end-position) t
)
288 (goto-char (match-beginning 0)))
291 (insert "<a name=\"" anchor
"\" id=\"" anchor
"\">")
292 (when muse-html-anchor-on-word
296 (unless (fboundp 'looking-back
)
297 (defun looking-back (regexp &optional limit
)
299 (re-search-backward (concat "\\(?:" regexp
"\\)\\=") limit t
))))
301 (defun muse-html-markup-paragraph ()
302 (let ((end (copy-marker (match-end 0) t
)))
303 (goto-char (match-beginning 0))
304 (unless (eq (char-before) ?\
>) (insert "</p>"))
306 (unless (and (eq (char-after) ?\
<)
307 (not (or (looking-at "<\\(em\\|strong\\|code\\)>")
308 (and (looking-at "<a ")
309 (not (looking-at "<a[^>]+><img"))))))
311 ((looking-back "\\(</h[1-4]>\\|<hr>\\)\n\n")
312 (insert "<p class=\"first\">"))
313 ((looking-back "<\\(blockquote\\|center\\)>\n")
314 (insert "<p class=\"quoted\">"))
318 (defun muse-html-markup-anchor ()
320 (muse-html-insert-anchor (match-string 1))) "")
322 (defun muse-html-escape-string (str)
323 "Convert to character entities any non-alphanumeric characters
324 outside a few punctuation symbols, that risk being misinterpreted
329 (while (setq pos
(string-match (concat "[^-"
333 (setq code
(int-to-string (aref str pos
))
335 str
(replace-match (concat "&#" code
";") nil nil str
)
339 (defun muse-html-markup-footnote ()
340 (if (/= (line-beginning-position) (match-beginning 0))
341 "<sup><a name=\"fnr.\\1\" href=\"#fn.\\1\">\\1</a></sup>"
343 "<p class=\"footnote\"><a name=\"fn.\\1\" href=\"#fnr.\\1\">\\1.</a>"
346 (let* ((beg (goto-char (match-end 0)))
347 (end (and (search-forward "\n\n" nil t
)
349 (copy-marker (match-beginning 0))
351 (while (re-search-forward (concat "^["
355 (replace-match "\\1" t
))))))))
357 (defun muse-html-markup-table ()
360 (delete-region (match-beginning 0) (match-end 0))))
361 (fields (split-string str
"\\s-*|+\\s-*"))
362 (type (and (string-match "\\s-*\\(|+\\)\\s-*" str
)
363 (length (match-string 1 str
))))
364 (part (cond ((= type
1) "tbody")
366 ((= type
3) "tfoot")))
367 (col (cond ((= type
1) "td")
371 (insert "<table " muse-html-table-attributes
">\n"
374 (dolist (field fields
)
375 (insert " <" col
">" field
"</" col
">\n"))
380 ;; Handling of tags for HTML
382 (defun muse-html-insert-contents (depth)
383 (let ((max-depth (or depth
2))
387 (goto-char (point-min))
388 (search-forward "Page published by Emacs Muse begins here" nil t
)
390 (while (re-search-forward "^<h\\([0-9]+\\)>\\(.+?\\)</h\\1>" nil t
)
391 (unless (get-text-property (point) 'read-only
)
392 (setq l
(1- (string-to-int (match-string 1))))
397 (when (<= l max-depth
)
398 (setq contents
(cons (cons l
(match-string-no-properties 2))
400 (goto-char (match-beginning 2))
401 (muse-html-insert-anchor (concat "sec" (int-to-string index
)))
402 (setq index
(1+ index
)))))))
403 (setq index
1 contents
(reverse contents
))
404 (let ((depth 1) (sub-open 0) (p (point)))
405 (insert "<dl class=\"contents\">\n")
407 (insert "<dt class=\"contents\">\n")
408 (insert "<a href=\"#sec" (int-to-string index
) "\">"
409 (muse-publish-strip-tags (cdar contents
))
411 (setq index
(1+ index
))
413 (setq depth
(caar contents
)
414 contents
(cdr contents
))
417 ((< (caar contents
) depth
)
418 (let ((idx (caar contents
)))
420 (insert "</dl>\n</dd>\n")
421 (setq sub-open
(1- sub-open
)
423 ((> (caar contents
) depth
) ; can't jump more than one ahead
424 (insert "<dd>\n<dl class=\"contents\">\n")
425 (setq sub-open
(1+ sub-open
))))))
426 (while (> sub-open
0)
427 (insert "</dl>\n</dd>\n")
428 (setq sub-open
(1- sub-open
)))
430 (muse-publish-mark-read-only p
(point)))))
432 (defun muse-html-class-tag (beg end attrs
)
434 (insert "<span class=\"" (cdr (assoc "name" attrs
)) "\">")
438 ;; Register the Muse HTML Publisher
440 (defun muse-html-browse-file (file)
441 (browse-url (concat "file:" file
)))
443 (defun muse-html-encoding ()
444 (if (stringp muse-html-meta-content-encoding
)
445 muse-html-meta-content-encoding
446 (muse-html-transform-content-type
447 (or buffer-file-coding-system
448 muse-html-encoding-default
))))
450 (defun muse-html-prepare-buffer ()
451 (set (make-local-variable 'muse-publish-url-transforms
)
452 (cons 'muse-html-escape-string muse-publish-url-transforms
))
453 (make-local-variable 'muse-html-meta-http-equiv
)
454 (set (make-local-variable 'muse-html-meta-content-type
)
455 (concat muse-html-meta-content-type
"; charset="
456 (muse-html-encoding))))
458 (defun muse-html-finalize-buffer ()
459 (when muse-publish-generate-contents
460 (goto-char (car muse-publish-generate-contents
))
461 (muse-html-insert-contents (cdr muse-publish-generate-contents
)))
462 (when (memq buffer-file-coding-system
'(no-conversion undecided-unix
))
463 ;; make it agree with the default charset
464 (setq buffer-file-coding-system muse-html-encoding-default
)))
466 (unless (assoc "html" muse-publishing-styles
)
467 (muse-define-style "html"
468 :suffix
'muse-html-extension
469 :regexps
'muse-html-markup-regexps
470 :functions
'muse-html-markup-functions
471 :strings
'muse-html-markup-strings
472 :tags
'muse-html-markup-tags
473 :specials
'muse-html-markup-specials
474 :before
'muse-html-prepare-buffer
475 :after
'muse-html-finalize-buffer
476 :header
'muse-html-header
477 :footer
'muse-html-footer
478 :browser
'muse-html-browse-file
))
482 ;;; muse-html.el ends here