(require 'cl)
(eval-when-compile
(defvar unresolved)
(if (string-match "XEmacs" emacs-version)
(byte-compiler-options
(warnings (- unresolved))))
(defvar font-lock-auto-fontify)
(defvar font-lock-support-mode)
(defvar global-font-lock-mode))
(defconst htmlize-version "1.45")
(defgroup htmlize nil
"Convert buffer text and faces to HTML."
:group 'hypermedia)
(defcustom htmlize-head-tags ""
"Additional tags to insert within HEAD of the generated document."
:type 'string
:group 'htmlize)
(defcustom htmlize-output-type 'css
"Output type of generated HTML, one of `css', `inline-css', or `font'.
When set to `css' (the default), htmlize will generate a style sheet
with description of faces, and use it in the HTML document, specifying
the faces in the actual text with <span class=\"FACE\">.
When set to `inline-css', the style will be generated as above, but
placed directly in the STYLE attribute of the span ELEMENT: <span
style=\"STYLE\">. This makes it easier to paste the resulting HTML to
other documents.
When set to `font', the properties will be set using layout tags
<font>, <b>, <i>, <u>, and <strike>.
`css' output is normally preferred, but `font' is still useful for
supporting old, pre-CSS browsers, and both `inline-css' and `font' for
easier embedding of colorized text in foreign HTML documents (no style
sheet to carry around)."
:type '(choice (const css) (const inline-css) (const font))
:group 'htmlize)
(defcustom htmlize-use-images t
"Whether htmlize generates `img' for images attached to buffer contents."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-force-inline-images nil
"Non-nil means generate all images inline using data URLs.
Normally htmlize converts image descriptors with :file properties to
relative URIs, and those with :data properties to data URIs. With this
flag set, the images specified as a file name are loaded into memory and
embedded in the HTML as data URIs."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-max-alt-text 100
"Maximum size of text to use as ALT text in images.
Normally when htmlize encounters text covered by the `display' property
that specifies an image, it generates an `alt' attribute containing the
original text. If the text is larger than `htmlize-max-alt-text' characters,
this will not be done.")
(defcustom htmlize-transform-image 'htmlize-default-transform-image
"Function called to modify the image descriptor.
The function is called with the image descriptor found in the buffer and
the text the image is supposed to replace. It should return a (possibly
different) image descriptor property list or a replacement string to use
instead of of the original buffer text.
Returning nil is the same as returning the original text."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-generate-hyperlinks t
"Non-nil means auto-generate the links from URLs and mail addresses in buffer.
This is on by default; set it to nil if you don't want htmlize to
autogenerate such links. Note that this option only turns off automatic
search for contents that looks like URLs and converting them to links.
It has no effect on whether htmlize respects the `htmlize-link' property."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-hyperlink-style "
a {
color: inherit;
background-color: inherit;
font: inherit;
text-decoration: inherit;
}
a:hover {
text-decoration: underline;
}
"
"The CSS style used for hyperlinks when in CSS mode."
:type 'string
:group 'htmlize)
(defcustom htmlize-replace-form-feeds t
"Non-nil means replace form feeds in source code with HTML separators.
Form feeds are the ^L characters at line beginnings that are sometimes
used to separate sections of source code. If this variable is set to
`t', form feed characters are replaced with the <hr> separator. If this
is a string, it specifies the replacement to use. Note that <pre> is
temporarily closed before the separator is inserted, so the default
replacement is effectively \"</pre><hr /><pre>\". If you specify
another replacement, don't forget to close and reopen the <pre> if you
want the output to remain valid HTML.
If you need more elaborate processing, set this to nil and use
htmlize-after-hook."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-html-charset nil
"The charset declared by the resulting HTML documents.
When non-nil, causes htmlize to insert the following in the HEAD section
of the generated HTML:
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=CHARSET\">
where CHARSET is the value you've set for htmlize-html-charset. Valid
charsets are defined by MIME and include strings like \"iso-8859-1\",
\"iso-8859-15\", \"utf-8\", etc.
If you are using non-Latin-1 charsets, you might need to set this for
your documents to render correctly. Also, the W3C validator requires
submitted HTML documents to declare a charset. So if you care about
validation, you can use this to prevent the validator from bitching.
Needless to say, if you set this, you should actually make sure that
the buffer is in the encoding you're claiming it is in. (This is
normally achieved by using the correct file coding system for the
buffer.) If you don't understand what that means, you should probably
leave this option in its default setting."
:type '(choice (const :tag "Unset" nil)
string)
:group 'htmlize)
(defcustom htmlize-convert-nonascii-to-entities t
"Whether non-ASCII characters should be converted to HTML entities.
When this is non-nil, characters with codes in the 128-255 range will be
considered Latin 1 and rewritten as \"&#CODE;\". Characters with codes
above 255 will be converted to \"&#UCS;\", where UCS denotes the Unicode
code point of the character. If the code point cannot be determined,
the character will be copied unchanged, as would be the case if the
option were nil.
When the option is nil, the non-ASCII characters are copied to HTML
without modification. In that case, the web server and/or the browser
must be set to understand the encoding that was used when saving the
buffer. (You might also want to specify it by setting
`htmlize-html-charset'.)
Note that in an HTML entity \"&#CODE;\", CODE is always a UCS code point,
which has nothing to do with the charset the page is in. For example,
\"©\" *always* refers to the copyright symbol, regardless of charset
specified by the META tag or the charset sent by the HTTP server. In
other words, \"©\" is exactly equivalent to \"©\".
For most people htmlize will work fine with this option left at the
default setting; don't change it unless you know what you're doing."
:type 'sexp
:group 'htmlize)
(defcustom htmlize-ignore-face-size 'absolute
"Whether face size should be ignored when generating HTML.
If this is nil, face sizes are used. If set to t, sizes are ignored
If set to `absolute', only absolute size specifications are ignored.
Please note that font sizes only work with CSS-based output types."
:type '(choice (const :tag "Don't ignore" nil)
(const :tag "Ignore all" t)
(const :tag "Ignore absolute" absolute))
:group 'htmlize)
(defcustom htmlize-css-name-prefix ""
"The prefix used for CSS names.
The CSS names that htmlize generates from face names are often too
generic for CSS files; for example, `font-lock-type-face' is transformed
to `type'. Use this variable to add a prefix to the generated names.
The string \"htmlize-\" is an example of a reasonable prefix."
:type 'string
:group 'htmlize)
(defcustom htmlize-use-rgb-txt t
"Whether `rgb.txt' should be used to convert color names to RGB.
This conversion means determining, for instance, that the color
\"IndianRed\" corresponds to the (205, 92, 92) RGB triple. `rgb.txt'
is the X color database that maps hundreds of color names to such RGB
triples. When this variable is non-nil, `htmlize' uses `rgb.txt' to
look up color names.
If this variable is nil, htmlize queries Emacs for RGB components of
colors using `color-instance-rgb-components' and `color-values'.
This can yield incorrect results on non-true-color displays.
If the `rgb.txt' file is not found (which will be the case if you're
running Emacs on non-X11 systems), this option is ignored."
:type 'boolean
:group 'htmlize)
(defcustom htmlize-html-major-mode nil
"The mode the newly created HTML buffer will be put in.
Set this to nil if you prefer the default (fundamental) mode."
:type '(radio (const :tag "No mode (fundamental)" nil)
(function-item html-mode)
(function :tag "User-defined major mode"))
:group 'htmlize)
(defvar htmlize-before-hook nil
"Hook run before htmlizing a buffer.
The hook functions are run in the source buffer (not the resulting HTML
buffer).")
(defvar htmlize-after-hook nil
"Hook run after htmlizing a buffer.
Unlike `htmlize-before-hook', these functions are run in the generated
HTML buffer. You may use them to modify the outlook of the final HTML
output.")
(defvar htmlize-file-hook nil
"Hook run by `htmlize-file' after htmlizing a file, but before saving it.")
(defvar htmlize-buffer-places)
(defconst htmlize-running-xemacs (string-match "XEmacs" emacs-version))
(cond
(htmlize-running-xemacs
(defun htmlize-next-change (pos prop &optional limit)
(if prop
(next-single-char-property-change pos prop nil (or limit (point-max)))
(next-property-change pos nil (or limit (point-max)))))
(defun htmlize-next-face-change (pos &optional limit)
(htmlize-next-change pos 'face limit)))
((fboundp 'next-single-char-property-change)
(defun htmlize-next-change (pos prop &optional limit)
(if prop
(next-single-char-property-change pos prop nil limit)
(next-char-property-change pos limit)))
(defun htmlize-overlay-faces-at (pos)
(delq nil (mapcar (lambda (o) (overlay-get o 'face)) (overlays-at pos))))
(defun htmlize-next-face-change (pos &optional limit)
(or limit
(setq limit (point-max)))
(let ((next-prop (next-single-property-change pos 'face nil limit))
(overlay-faces (htmlize-overlay-faces-at pos)))
(while (progn
(setq pos (next-overlay-change pos))
(and (< pos next-prop)
(equal overlay-faces (htmlize-overlay-faces-at pos)))))
(setq pos (min pos next-prop))
(when (get-char-property pos 'display)
(setq pos (next-single-char-property-change pos 'display nil limit)))
pos)))
(t
(error "htmlize requires next-single-property-change or \
next-single-char-property-change")))
(defmacro htmlize-lexlet (&rest letforms)
(declare (indent 1) (debug let))
(if (and (boundp 'lexical-binding)
lexical-binding)
`(let ,@letforms)
`(lexical-let ,@letforms)))
(cond
(htmlize-running-xemacs
(defalias 'htmlize-make-overlay 'make-extent)
(defalias 'htmlize-overlay-put 'set-extent-property)
(defalias 'htmlize-overlay-get 'extent-property)
(defun htmlize-overlays-in (beg end) (extent-list nil beg end))
(defalias 'htmlize-delete-overlay 'detach-extent))
(t
(defalias 'htmlize-make-overlay 'make-overlay)
(defalias 'htmlize-overlay-put 'overlay-put)
(defalias 'htmlize-overlay-get 'overlay-get)
(defalias 'htmlize-overlays-in 'overlays-in)
(defalias 'htmlize-delete-overlay 'delete-overlay)))
(defvar htmlize-basic-character-table
(let ((table (make-vector 128 ?\0)))
(dotimes (i 128)
(setf (aref table i) (if (and (>= i 32) (<= i 126))
(char-to-string i)
(format "&#%d;" i))))
(setf
(aref table ?\n) "\n"
(aref table ?\r) "\r"
(aref table ?\t) "\t"
(aref table ?&) "&"
(aref table ?<) "<"
(aref table ?>) ">"
)
table))
(defvar htmlize-extended-character-cache (make-hash-table :test 'eq))
(defun htmlize-protect-string (string)
"HTML-protect string, escaping HTML metacharacters and I18N chars."
(if (not (string-match "[^\r\n\t -%'-;=?-~]" string))
string
(mapconcat (lambda (char)
(cond
((< char 128)
(aref htmlize-basic-character-table char))
((gethash char htmlize-extended-character-cache)
)
((not htmlize-convert-nonascii-to-entities)
(setf (gethash char htmlize-extended-character-cache)
(char-to-string char)))
((< char 256)
(setf (gethash char htmlize-extended-character-cache)
(format "&#%d;" char)))
((encode-char char 'ucs)
(setf (gethash char htmlize-extended-character-cache)
(format "&#%d;" (encode-char char 'ucs))))
(t
(setf (gethash char htmlize-extended-character-cache)
(char-to-string char)))))
string "")))
(defun htmlize-attr-escape (string)
(setq string (htmlize-protect-string string))
(if (not (string-match "\"" string))
string
(mapconcat (lambda (char)
(if (eql char ?\")
"""
(char-to-string char)))
string "")))
(defsubst htmlize-concat (list)
(if (and (consp list) (null (cdr list)))
(car list)
(apply #'concat list)))
(defun htmlize-format-link (linkprops text)
(let ((uri (if (stringp linkprops)
linkprops
(plist-get linkprops :uri)))
(escaped-text (htmlize-protect-string text)))
(if uri
(format "<a href=\"%s\">%s</a>" (htmlize-attr-escape uri) escaped-text)
escaped-text)))
(defun htmlize-escape-or-link (string)
(let ((pos 0) (end (length string)) outlist)
(while (< pos end)
(let* ((link (get-char-property pos 'htmlize-link string))
(next-link-change (next-single-property-change
pos 'htmlize-link string end))
(chunk (substring string pos next-link-change)))
(push
(cond (link
(htmlize-format-link link chunk))
((get-char-property 0 'htmlize-literal chunk)
chunk)
(t
(htmlize-protect-string chunk)))
outlist)
(setq pos next-link-change)))
(htmlize-concat (nreverse outlist))))
(defun htmlize-display-prop-to-html (display text)
(let (desc)
(cond ((stringp display)
(htmlize-escape-or-link display))
((not (eq (car-safe display) 'image))
(htmlize-protect-string text))
((null (setq desc (funcall htmlize-transform-image
(cdr display) text)))
(htmlize-escape-or-link text))
((stringp desc)
(htmlize-escape-or-link desc))
(t
(htmlize-generate-image desc text)))))
(defun htmlize-string-to-html (string)
(let ((pos 0) (end (length string)) outlist)
(while (< pos end)
(let* ((display (get-char-property pos 'display string))
(next-display-change (next-single-property-change
pos 'display string end))
(chunk (substring string pos next-display-change)))
(push
(if display
(htmlize-display-prop-to-html display chunk)
(htmlize-escape-or-link chunk))
outlist)
(setq pos next-display-change)))
(htmlize-concat (nreverse outlist))))
(defun htmlize-default-transform-image (imgprops _text)
"Default transformation of image descriptor to something usable in HTML.
If `htmlize-use-images' is nil, the function always returns nil, meaning
use original text. Otherwise, it tries to find the image for images that
specify a file name. If `htmlize-force-inline-images' is non-nil, it also
converts the :file attribute to :data and returns the modified property
list."
(when htmlize-use-images
(when (plist-get imgprops :file)
(let ((location (plist-get (cdr (find-image (list imgprops))) :file)))
(when location
(setq imgprops (plist-put (copy-list imgprops) :file location)))))
(if htmlize-force-inline-images
(let ((location (plist-get imgprops :file))
data)
(when location
(with-temp-buffer
(condition-case nil
(progn
(insert-file-contents-literally location)
(setq data (buffer-string)))
(error nil))))
(and data
(plist-put (plist-put imgprops :file nil)
:data data)))
imgprops)))
(defun htmlize-alt-text (_imgprops origtext)
(and (/= (length origtext) 0)
(<= (length origtext) htmlize-max-alt-text)
(not (string-match "[\0-\x1f]" origtext))
origtext))
(defun htmlize-generate-image (imgprops origtext)
(let* ((alt-text (htmlize-alt-text imgprops origtext))
(alt-attr (if alt-text
(format " alt=\"%s\"" (htmlize-attr-escape alt-text))
"")))
(cond ((plist-get imgprops :file)
(let* ((found-props (cdr (find-image (list imgprops))))
(file (or (plist-get found-props :file)
(plist-get imgprops :file))))
(format "<img src=\"%s\"%s />"
(htmlize-attr-escape (file-relative-name file))
alt-attr)))
((plist-get imgprops :data)
(format "<img src=\"data:image/%s;base64,%s\"%s />"
(or (plist-get imgprops :type) "")
(base64-encode-string (plist-get imgprops :data))
alt-attr)))))
(defconst htmlize-ellipsis "...")
(put-text-property 0 (length htmlize-ellipsis) 'htmlize-ellipsis t htmlize-ellipsis)
(defun htmlize-match-inv-spec (inv)
(member* inv buffer-invisibility-spec
:key (lambda (i)
(if (symbolp i) i (car i)))))
(defun htmlize-decode-invisibility-spec (invisible)
(if (not (listp buffer-invisibility-spec))
(not invisible)
(let ((match (if (symbolp invisible)
(htmlize-match-inv-spec invisible)
(some #'htmlize-match-inv-spec invisible))))
(cond ((null match) t)
((cdr-safe (car match)) 'ellipsis)
(t nil)))))
(defun htmlize-add-before-after-strings (beg end text)
(let (additions)
(dolist (overlay (overlays-in beg end))
(let ((before (overlay-get overlay 'before-string))
(after (overlay-get overlay 'after-string)))
(when after
(push (cons (- (overlay-end overlay) beg)
after)
additions))
(when before
(push (cons (- (overlay-start overlay) beg)
before)
additions))))
(if additions
(let ((textlist nil)
(strpos 0))
(dolist (add (stable-sort additions #'< :key #'car))
(let ((addpos (car add))
(addtext (cdr add)))
(push (substring text strpos addpos) textlist)
(push addtext textlist)
(setq strpos addpos)))
(push (substring text strpos) textlist)
(apply #'concat (nreverse textlist)))
text)))
(defun htmlize-copy-prop (prop beg end string)
(let ((pos beg))
(while (< pos end)
(let ((value (get-char-property pos prop))
(next-change (htmlize-next-change pos prop end)))
(when value
(put-text-property (- pos beg) (- next-change beg)
prop value string))
(setq pos next-change)))))
(defun htmlize-get-text-with-display (beg end)
(let ((text (buffer-substring-no-properties beg end)))
(htmlize-copy-prop 'display beg end text)
(htmlize-copy-prop 'htmlize-link beg end text)
(unless htmlize-running-xemacs
(setq text (htmlize-add-before-after-strings beg end text)))
text))
(defun htmlize-buffer-substring-no-invisible (beg end)
(let ((pos beg)
visible-list invisible show last-show next-change)
(while (< pos end)
(setq invisible (get-char-property pos 'invisible)
next-change (htmlize-next-change pos 'invisible end)
show (htmlize-decode-invisibility-spec invisible))
(cond ((eq show t)
(push (htmlize-get-text-with-display pos next-change)
visible-list))
((and (eq show 'ellipsis)
(not (eq last-show 'ellipsis))
(push htmlize-ellipsis visible-list))))
(setq pos next-change last-show show))
(htmlize-concat (nreverse visible-list))))
(defun htmlize-trim-ellipsis (text)
(if (get-text-property 0 'htmlize-ellipsis text)
(substring text (length htmlize-ellipsis))
text))
(defconst htmlize-tab-spaces
(let ((v (make-vector 32 nil)))
(dotimes (i (length v))
(setf (aref v i) (make-string i ?\ )))
v))
(defun htmlize-untabify (text start-column)
"Untabify TEXT, assuming it starts at START-COLUMN."
(let ((column start-column)
(last-match 0)
(chunk-start 0)
chunks match-pos tab-size)
(while (string-match "[\t\n]" text last-match)
(setq match-pos (match-beginning 0))
(cond ((eq (aref text match-pos) ?\t)
(push (substring text chunk-start match-pos) chunks)
(incf column (- match-pos last-match))
(setq tab-size (- tab-width (% column tab-width)))
(let ((display (get-text-property match-pos 'display text))
(expanded-tab (aref htmlize-tab-spaces tab-size)))
(when display
(put-text-property 0 tab-size 'display display expanded-tab))
(push expanded-tab chunks))
(incf column tab-size)
(setq chunk-start (1+ match-pos)))
(t
(setq column 0)))
(setq last-match (1+ match-pos)))
(if (null chunks)
text
(when (< chunk-start (length text))
(push (substring text chunk-start) chunks))
(htmlize-concat (nreverse chunks)))))
(defun htmlize-extract-text (beg end trailing-ellipsis)
(let ((text (htmlize-buffer-substring-no-invisible beg end)))
(when trailing-ellipsis
(setq text (htmlize-trim-ellipsis text)))
(when (> (length text) 0)
(setq trailing-ellipsis
(get-text-property (1- (length text))
'htmlize-ellipsis text)))
(setq text (htmlize-untabify text (current-column)))
(setq text (htmlize-string-to-html text))
(values text trailing-ellipsis)))
(defun htmlize-despam-address (string)
"Replace every occurrence of '@' in STRING with %40.
This is used to protect mailto links without modifying their meaning."
(while (string-match "@" string)
(setq string (replace-match "%40" nil t string)))
string)
(defun htmlize-make-tmp-overlay (beg end props)
(let ((overlay (htmlize-make-overlay beg end)))
(htmlize-overlay-put overlay 'htmlize-tmp-overlay t)
(while props
(htmlize-overlay-put overlay (pop props) (pop props)))
overlay))
(defun htmlize-delete-tmp-overlays ()
(dolist (overlay (htmlize-overlays-in (point-min) (point-max)))
(when (htmlize-overlay-get overlay 'htmlize-tmp-overlay)
(htmlize-delete-overlay overlay))))
(defun htmlize-make-link-overlay (beg end uri)
(htmlize-make-tmp-overlay beg end `(htmlize-link (:uri ,uri))))
(defun htmlize-create-auto-links ()
"Add `htmlize-link' property to all mailto links in the buffer."
(save-excursion
(goto-char (point-min))
(while (re-search-forward
"<\\(\\(mailto:\\)?\\([-=+_.a-zA-Z0-9]+@[-_.a-zA-Z0-9]+\\)\\)>"
nil t)
(let* ((address (match-string 3))
(beg (match-beginning 0)) (end (match-end 0))
(uri (concat "mailto:" (htmlize-despam-address address))))
(htmlize-make-link-overlay beg end uri)))
(goto-char (point-min))
(while (re-search-forward "<\\(\\(URL:\\)?\\([a-zA-Z]+://[^;]+\\)\\)>"
nil t)
(htmlize-make-link-overlay
(match-beginning 0) (match-end 0) (match-string 3)))))
(defun htmlize-shadow-form-feeds ()
(let ((s "\n<hr />"))
(put-text-property 0 (length s) 'htmlize-literal t s)
(let ((disp `(display ,s)))
(while (re-search-forward "\n\^L" nil t)
(htmlize-make-tmp-overlay (match-beginning 0) (match-end 0) disp)))))
(defun htmlize-defang-local-variables ()
(goto-char (point-min))
(while (search-forward "Local Variables:" nil t)
(replace-match "Local Variables:" nil t)))
(defvar htmlize-x-library-search-path
`(,data-directory
"/etc/X11/rgb.txt"
"/usr/share/X11/rgb.txt"
"/usr/X11R6/lib/X11/"
"/usr/X11R5/lib/X11/"
"/usr/lib/X11R6/X11/"
"/usr/lib/X11R5/X11/"
"/usr/local/X11R6/lib/X11/"
"/usr/local/X11R5/lib/X11/"
"/usr/local/lib/X11R6/X11/"
"/usr/local/lib/X11R5/X11/"
"/usr/X11/lib/X11/"
"/usr/lib/X11/"
"/usr/local/lib/X11/"
"/usr/X386/lib/X11/"
"/usr/x386/lib/X11/"
"/usr/XFree86/lib/X11/"
"/usr/unsupported/lib/X11/"
"/usr/athena/lib/X11/"
"/usr/local/x11r5/lib/X11/"
"/usr/lpp/Xamples/lib/X11/"
"/usr/openwin/lib/X11/"
"/usr/openwin/share/lib/X11/"))
(defun htmlize-get-color-rgb-hash (&optional rgb-file)
"Return a hash table mapping X color names to RGB values.
The keys in the hash table are X11 color names, and the values are the
#rrggbb RGB specifications, extracted from `rgb.txt'.
If RGB-FILE is nil, the function will try hard to find a suitable file
in the system directories.
If no rgb.txt file is found, return nil."
(let ((rgb-file (or rgb-file (locate-file
"rgb.txt"
htmlize-x-library-search-path)))
(hash nil))
(when rgb-file
(with-temp-buffer
(insert-file-contents rgb-file)
(setq hash (make-hash-table :test 'equal))
(while (not (eobp))
(cond ((looking-at "^\\s-*\\([!#]\\|$\\)")
)
((looking-at
"[ \t]*\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\(.*\\)")
(setf (gethash (downcase (match-string 4)) hash)
(format "#%02x%02x%02x"
(string-to-number (match-string 1))
(string-to-number (match-string 2))
(string-to-number (match-string 3)))))
(t
(error
"Unrecognized line in %s: %s"
rgb-file
(buffer-substring (point) (progn (end-of-line) (point))))))
(forward-line 1))))
hash))
(defvar htmlize-color-rgb-hash (htmlize-get-color-rgb-hash))
(defun htmlize-face-specifies-property (face prop)
(or (eq face 'default)
(assq 'global (specifier-spec-list (face-property face prop)))))
(defun htmlize-face-color-internal (face fg)
(let* ((function (if fg #'face-foreground #'face-background))
color)
(if (>= emacs-major-version 22)
(setq color (funcall function face nil t))
(setq color (funcall function face))
(when (and (null color)
(fboundp 'face-attribute)
(face-attribute face :inherit)
(not (eq (face-attribute face :inherit) 'unspecified)))
(setq color (htmlize-face-color-internal
(face-attribute face :inherit) fg))))
(when (and (eq face 'default) (null color))
(setq color (cdr (assq (if fg 'foreground-color 'background-color)
(frame-parameters)))))
(when (or (eq color 'unspecified)
(equal color "unspecified-fg")
(equal color "unspecified-bg"))
(setq color nil))
(when (and (eq face 'default)
(null color))
(setq color (if fg "black" "white")))
color))
(defun htmlize-face-foreground (face)
(cond (htmlize-running-xemacs
(and (htmlize-face-specifies-property face 'foreground)
(color-instance-name (face-foreground-instance face))))
(t
(htmlize-face-color-internal face t))))
(defun htmlize-face-background (face)
(cond (htmlize-running-xemacs
(and (htmlize-face-specifies-property face 'background)
(color-instance-name (face-background-instance face))))
(t
(htmlize-face-color-internal face nil))))
(defun htmlize-color-to-rgb (color)
(let ((rgb-string nil))
(cond ((null color)
)
((string-match "\\`#" color)
(setq rgb-string color))
((and htmlize-use-rgb-txt
htmlize-color-rgb-hash)
(setq rgb-string (gethash (downcase color) htmlize-color-rgb-hash)))
(t
(let ((rgb
(if (fboundp 'color-instance-rgb-components)
(mapcar (lambda (arg)
(/ arg 256))
(color-instance-rgb-components
(make-color-instance color)))
(mapcar (lambda (arg)
(/ arg 256))
(color-values color)))))
(when rgb
(setq rgb-string (apply #'format "#%02x%02x%02x" rgb))))))
(or rgb-string color)))
(defstruct htmlize-fstruct
foreground background size boldp italicp underlinep overlinep strikep css-name )
(defun htmlize-face-emacs21-attr (fstruct attr value)
(case attr
(:foreground
(setf (htmlize-fstruct-foreground fstruct) (htmlize-color-to-rgb value)))
(:background
(setf (htmlize-fstruct-background fstruct) (htmlize-color-to-rgb value)))
(:height
(setf (htmlize-fstruct-size fstruct) value))
(:weight
(when (string-match (symbol-name value) "bold")
(setf (htmlize-fstruct-boldp fstruct) t)))
(:slant
(setf (htmlize-fstruct-italicp fstruct) (or (eq value 'italic)
(eq value 'oblique))))
(:bold
(setf (htmlize-fstruct-boldp fstruct) value))
(:italic
(setf (htmlize-fstruct-italicp fstruct) value))
(:underline
(setf (htmlize-fstruct-underlinep fstruct) value))
(:overline
(setf (htmlize-fstruct-overlinep fstruct) value))
(:strike-through
(setf (htmlize-fstruct-strikep fstruct) value))))
(defun htmlize-face-size (face)
(let ((size-list
(loop
for f = face then (face-attribute f :inherit)
until (or (not f) (eq f 'unspecified))
for h = (face-attribute f :height)
collect (if (eq h 'unspecified) nil h))))
(reduce 'htmlize-merge-size (cons nil size-list))))
(defun htmlize-face-css-name (face)
(let ((name (downcase (symbol-name face))))
(when (string-match "\\`font-lock-" name)
(setq name (replace-match "" t t name)))
(when (string-match "-face\\'" name)
(setq name (replace-match "" t t name)))
(while (string-match "[^-a-zA-Z0-9]" name)
(setq name (replace-match "X" t t name)))
(when (string-match "\\`[-0-9]" name)
(setq name (concat "X" name)))
(when (equal name "")
(setq name "face"))
(concat htmlize-css-name-prefix name)))
(defun htmlize-face-to-fstruct (face)
"Convert Emacs face FACE to fstruct."
(let ((fstruct (make-htmlize-fstruct
:foreground (htmlize-color-to-rgb
(htmlize-face-foreground face))
:background (htmlize-color-to-rgb
(htmlize-face-background face)))))
(if htmlize-running-xemacs
(let* ((font-instance (face-font-instance face))
(props (font-instance-properties font-instance)))
(when (equalp (cdr (assq 'WEIGHT_NAME props)) "bold")
(setf (htmlize-fstruct-boldp fstruct) t))
(when (or (equalp (cdr (assq 'SLANT props)) "i")
(equalp (cdr (assq 'SLANT props)) "o"))
(setf (htmlize-fstruct-italicp fstruct) t))
(setf (htmlize-fstruct-strikep fstruct)
(face-strikethru-p face))
(setf (htmlize-fstruct-underlinep fstruct)
(face-underline-p face)))
(dolist (attr '(:weight :slant :underline :overline :strike-through))
(let ((value (if (>= emacs-major-version 22)
(face-attribute face attr nil t)
(let ((face face))
(while (and (eq (face-attribute face attr)
'unspecified)
(not (eq (face-attribute face :inherit)
'unspecified)))
(setq face (face-attribute face :inherit)))
(face-attribute face attr)))))
(when (and value (not (eq value 'unspecified)))
(htmlize-face-emacs21-attr fstruct attr value))))
(let ((size (htmlize-face-size face)))
(unless (eql size 1.0) (setf (htmlize-fstruct-size fstruct) size))))
(setf (htmlize-fstruct-css-name fstruct) (htmlize-face-css-name face))
fstruct))
(defmacro htmlize-copy-attr-if-set (attr-list dest source)
(cons 'progn
(loop for attr in attr-list
for attr-sym = (intern (format "htmlize-fstruct-%s" attr))
collect `(when (,attr-sym ,source)
(setf (,attr-sym ,dest) (,attr-sym ,source))))))
(defun htmlize-merge-size (merged next)
(cond ((null merged) next)
((integerp next) next)
((null next) merged)
((floatp merged) (* merged next))
((integerp merged) (round (* merged next)))))
(defun htmlize-merge-two-faces (merged next)
(htmlize-copy-attr-if-set
(foreground background boldp italicp underlinep overlinep strikep)
merged next)
(setf (htmlize-fstruct-size merged)
(htmlize-merge-size (htmlize-fstruct-size merged)
(htmlize-fstruct-size next)))
merged)
(defun htmlize-merge-faces (fstruct-list)
(cond ((null fstruct-list)
(make-htmlize-fstruct))
((null (cdr fstruct-list))
(car fstruct-list))
(t
(reduce #'htmlize-merge-two-faces
(cons (make-htmlize-fstruct) fstruct-list)))))
(defun htmlize-attrlist-to-fstruct (attrlist)
(let ((fstruct (make-htmlize-fstruct)))
(cond ((eq (car attrlist) 'foreground-color)
(setf (htmlize-fstruct-foreground fstruct)
(htmlize-color-to-rgb (cdr attrlist))))
((eq (car attrlist) 'background-color)
(setf (htmlize-fstruct-background fstruct)
(htmlize-color-to-rgb (cdr attrlist))))
(t
(while attrlist
(let ((attr (pop attrlist))
(value (pop attrlist)))
(when (and value (not (eq value 'unspecified)))
(htmlize-face-emacs21-attr fstruct attr value))))))
(setf (htmlize-fstruct-css-name fstruct) "ATTRLIST")
fstruct))
(defun htmlize-decode-face-prop (prop)
"Turn face property PROP into a list of face-like objects."
(cond ((null prop)
nil)
((symbolp prop)
(and (facep prop)
(list prop)))
((stringp prop)
(and (facep (intern-soft prop))
(list prop)))
((atom prop)
nil)
((and (symbolp (car prop))
(eq ?: (aref (symbol-name (car prop)) 0)))
(list prop))
((or (eq (car prop) 'foreground-color)
(eq (car prop) 'background-color))
(list prop))
(t
(apply #'nconc (mapcar #'htmlize-decode-face-prop prop)))))
(defun htmlize-make-face-map (faces)
(let ((face-map (make-hash-table :test 'equal))
css-names)
(dolist (face faces)
(unless (gethash face face-map)
(let ((fstruct (if (symbolp face)
(htmlize-face-to-fstruct face)
(htmlize-attrlist-to-fstruct face))))
(setf (gethash face face-map) fstruct)
(let* ((css-name (htmlize-fstruct-css-name fstruct))
(new-name css-name)
(i 0))
(while (member new-name css-names)
(setq new-name (format "%s-%s" css-name (incf i))))
(unless (equal new-name css-name)
(setf (htmlize-fstruct-css-name fstruct) new-name))
(push new-name css-names)))))
face-map))
(defun htmlize-unstringify-face (face)
"If FACE is a string, return it interned, otherwise return it unchanged."
(if (stringp face)
(intern face)
face))
(defun htmlize-faces-in-buffer ()
"Return a list of faces used in the current buffer.
Under XEmacs, this returns the set of faces specified by the extents
with the `face' property. (This covers text properties as well.) Under
GNU Emacs, it returns the set of faces specified by the `face' text
property and by buffer overlays that specify `face'."
(let (faces)
(if htmlize-running-xemacs
(let (face-prop)
(map-extents (lambda (extent ignored)
(setq face-prop (extent-face extent)
faces (if (listp face-prop)
(union face-prop faces)
(adjoin face-prop faces)))
nil)
nil
(point-min) (point-max) nil nil 'face))
(let ((pos (point-min)) face-prop next)
(while (< pos (point-max))
(setq face-prop (get-text-property pos 'face)
next (or (next-single-property-change pos 'face) (point-max)))
(setq faces (nunion (htmlize-decode-face-prop face-prop)
faces :test 'equal))
(setq pos next)))
(dolist (overlay (overlays-in (point-min) (point-max)))
(let ((face-prop (overlay-get overlay 'face)))
(setq faces (nunion (htmlize-decode-face-prop face-prop)
faces :test 'equal)))))
faces))
(cond (htmlize-running-xemacs
(defun htmlize-faces-at-point ()
(let (extent extent-list face-list face-prop)
(while (setq extent (extent-at (point) nil 'face extent))
(push extent extent-list))
(setq extent-list (stable-sort extent-list #'<
:key #'extent-priority))
(dolist (extent extent-list)
(setq face-prop (extent-face extent))
(setq face-list (if (listp face-prop)
(append face-prop face-list)
(cons face-prop face-list))))
(nreverse face-list))))
(t
(defun htmlize-faces-at-point ()
(let (all-faces)
(let ((face-prop (get-text-property (point) 'face)))
(setq all-faces (nreverse (htmlize-decode-face-prop face-prop))))
(let ((overlays
(delete-if-not (lambda (o)
(overlay-get o 'face))
(overlays-at (point))))
list face-prop)
(setq overlays (sort* overlays
#'<
:key (lambda (o)
(- (overlay-end o)
(overlay-start o)))))
(setq overlays (stable-sort
overlays
#'<
:key (lambda (o)
(or (overlay-get o 'priority) 0))))
(dolist (overlay overlays)
(setq face-prop (overlay-get overlay 'face)
list (nconc (htmlize-decode-face-prop face-prop) list)))
(setq all-faces (nconc all-faces list)))
all-faces))))
(defmacro htmlize-method (method &rest args)
`(funcall (htmlize-method-function ',method) ,@args))
(defun htmlize-method-function (method)
(let ((sym (intern (format "htmlize-%s-%s" htmlize-output-type method))))
(indirect-function (if (fboundp sym)
sym
(let ((default (intern (concat "htmlize-default-"
(symbol-name method)))))
(if (fboundp default)
default
'ignore))))))
(defvar htmlize-memoization-table (make-hash-table :test 'equal))
(defmacro htmlize-memoize (key generator)
"Return the value of GENERATOR, memoized as KEY.
That means that GENERATOR will be evaluated and returned the first time
it's called with the same value of KEY. All other times, the cached
\(memoized) value will be returned."
(let ((value (gensym)))
`(let ((,value (gethash ,key htmlize-memoization-table)))
(unless ,value
(setq ,value ,generator)
(setf (gethash ,key htmlize-memoization-table) ,value))
,value)))
(defun htmlize-default-doctype ()
nil "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">"
)
(defun htmlize-default-body-tag (face-map)
nil face-map "<body>")
(defun htmlize-css-specs (fstruct)
(let (result)
(when (htmlize-fstruct-foreground fstruct)
(push (format "color: %s;" (htmlize-fstruct-foreground fstruct))
result))
(when (htmlize-fstruct-background fstruct)
(push (format "background-color: %s;"
(htmlize-fstruct-background fstruct))
result))
(let ((size (htmlize-fstruct-size fstruct)))
(when (and size (not (eq htmlize-ignore-face-size t)))
(cond ((floatp size)
(push (format "font-size: %d%%;" (* 100 size)) result))
((not (eq htmlize-ignore-face-size 'absolute))
(push (format "font-size: %spt;" (/ size 10.0)) result)))))
(when (htmlize-fstruct-boldp fstruct)
(push "font-weight: bold;" result))
(when (htmlize-fstruct-italicp fstruct)
(push "font-style: italic;" result))
(when (htmlize-fstruct-underlinep fstruct)
(push "text-decoration: underline;" result))
(when (htmlize-fstruct-overlinep fstruct)
(push "text-decoration: overline;" result))
(when (htmlize-fstruct-strikep fstruct)
(push "text-decoration: line-through;" result))
(nreverse result)))
(defun htmlize-css-insert-head (buffer-faces face-map)
(insert " <style type=\"text/css\">\n <!--\n")
(insert " body {\n "
(mapconcat #'identity
(htmlize-css-specs (gethash 'default face-map))
"\n ")
"\n }\n")
(dolist (face (sort* (copy-list buffer-faces) #'string-lessp
:key (lambda (f)
(htmlize-fstruct-css-name (gethash f face-map)))))
(let* ((fstruct (gethash face face-map))
(cleaned-up-face-name
(let ((s
(prin1-to-string face)))
(while (string-match "--" s)
(setq s (replace-match "-" t t s)))
(while (string-match "\\*/" s)
(setq s (replace-match "XX" t t s)))
s))
(specs (htmlize-css-specs fstruct)))
(insert " ." (htmlize-fstruct-css-name fstruct))
(if (null specs)
(insert " {")
(insert " {\n /* " cleaned-up-face-name " */\n "
(mapconcat #'identity specs "\n ")))
(insert "\n }\n")))
(insert htmlize-hyperlink-style
" -->\n </style>\n"))
(defun htmlize-css-text-markup (fstruct-list buffer)
(dolist (fstruct fstruct-list)
(princ "<span class=\"" buffer)
(princ (htmlize-fstruct-css-name fstruct) buffer)
(princ "\">" buffer))
(htmlize-lexlet ((fstruct-list fstruct-list) (buffer buffer))
(lambda ()
(dolist (fstruct fstruct-list)
(ignore fstruct) (princ "</span>" buffer)))))
(defun htmlize-inline-css-body-tag (face-map)
(format "<body style=\"%s\">"
(mapconcat #'identity (htmlize-css-specs (gethash 'default face-map))
" ")))
(defun htmlize-inline-css-text-markup (fstruct-list buffer)
(let* ((merged (htmlize-merge-faces fstruct-list))
(style (htmlize-memoize
merged
(let ((specs (htmlize-css-specs merged)))
(and specs
(mapconcat #'identity (htmlize-css-specs merged) " "))))))
(when style
(princ "<span style=\"" buffer)
(princ style buffer)
(princ "\">" buffer))
(htmlize-lexlet ((style style) (buffer buffer))
(lambda ()
(when style
(princ "</span>" buffer))))))
(defun htmlize-font-body-tag (face-map)
(let ((fstruct (gethash 'default face-map)))
(format "<body text=\"%s\" bgcolor=\"%s\">"
(htmlize-fstruct-foreground fstruct)
(htmlize-fstruct-background fstruct))))
(defun htmlize-font-text-markup (fstruct-list buffer)
(let* ((merged (htmlize-merge-faces fstruct-list))
(markup (htmlize-memoize
merged
(cons (concat
(and (htmlize-fstruct-foreground merged)
(format "<font color=\"%s\">" (htmlize-fstruct-foreground merged)))
(and (htmlize-fstruct-boldp merged) "<b>")
(and (htmlize-fstruct-italicp merged) "<i>")
(and (htmlize-fstruct-underlinep merged) "<u>")
(and (htmlize-fstruct-strikep merged) "<strike>"))
(concat
(and (htmlize-fstruct-strikep merged) "</strike>")
(and (htmlize-fstruct-underlinep merged) "</u>")
(and (htmlize-fstruct-italicp merged) "</i>")
(and (htmlize-fstruct-boldp merged) "</b>")
(and (htmlize-fstruct-foreground merged) "</font>"))))))
(princ (car markup) buffer)
(htmlize-lexlet ((markup markup) (buffer buffer))
(lambda ()
(princ (cdr markup) buffer)))))
(defun htmlize-buffer-1 ()
(save-excursion
(save-excursion
(run-hooks 'htmlize-before-hook))
(htmlize-ensure-fontified)
(clrhash htmlize-extended-character-cache)
(clrhash htmlize-memoization-table)
(let ((htmlbuf (generate-new-buffer (if (buffer-file-name)
(htmlize-make-file-name
(file-name-nondirectory
(buffer-file-name)))
"*html*")))
(completed nil))
(unwind-protect
(let* ((buffer-faces (htmlize-faces-in-buffer))
(face-map (htmlize-make-face-map (adjoin 'default buffer-faces)))
(places (gensym))
(title (if (buffer-file-name)
(file-name-nondirectory (buffer-file-name))
(buffer-name))))
(when htmlize-generate-hyperlinks
(htmlize-create-auto-links))
(when htmlize-replace-form-feeds
(htmlize-shadow-form-feeds))
(with-current-buffer htmlbuf
(buffer-disable-undo)
(insert (htmlize-method doctype) ?\n
(format "<!-- Created by htmlize-%s in %s mode. -->\n"
htmlize-version htmlize-output-type)
"<html>\n ")
(put places 'head-start (point-marker))
(insert "<head>\n"
" <title>" (htmlize-protect-string title) "</title>\n"
(if htmlize-html-charset
(format (concat " <meta http-equiv=\"Content-Type\" "
"content=\"text/html; charset=%s\">\n")
htmlize-html-charset)
"")
htmlize-head-tags)
(htmlize-method insert-head buffer-faces face-map)
(insert " </head>")
(put places 'head-end (point-marker))
(insert "\n ")
(put places 'body-start (point-marker))
(insert (htmlize-method body-tag face-map)
"\n ")
(put places 'content-start (point-marker))
(insert "<pre>\n"))
(let ((text-markup
(htmlize-method-function 'text-markup))
next-change text face-list trailing-ellipsis
fstruct-list last-fstruct-list
(close-markup (lambda ())))
(goto-char (point-min))
(while (not (eobp))
(setq next-change (htmlize-next-face-change (point)))
(setq face-list (htmlize-faces-at-point)
fstruct-list (delq nil (mapcar (lambda (f)
(gethash f face-map))
face-list)))
(multiple-value-setq (text trailing-ellipsis)
(htmlize-extract-text (point) next-change trailing-ellipsis))
(when (> (length text) 0)
(when (not (equalp fstruct-list last-fstruct-list))
(funcall close-markup)
(setq last-fstruct-list fstruct-list
close-markup (funcall text-markup fstruct-list htmlbuf)))
(princ text htmlbuf))
(goto-char next-change))
(funcall close-markup))
(with-current-buffer htmlbuf
(insert "</pre>")
(put places 'content-end (point-marker))
(insert "\n </body>")
(put places 'body-end (point-marker))
(insert "\n</html>\n")
(htmlize-defang-local-variables)
(goto-char (point-min))
(when htmlize-html-major-mode
(funcall htmlize-html-major-mode))
(set (make-local-variable 'htmlize-buffer-places)
(symbol-plist places))
(run-hooks 'htmlize-after-hook)
(buffer-enable-undo))
(setq completed t)
htmlbuf)
(when (not completed)
(kill-buffer htmlbuf))
(htmlize-delete-tmp-overlays)))))
(defmacro htmlize-with-fontify-message (&rest body)
`(progn
(if (> (buffer-size) 65536)
(message "Forcing fontification of %s..."
(buffer-name (current-buffer))))
,@body
(if (> (buffer-size) 65536)
(message "Forcing fontification of %s...done"
(buffer-name (current-buffer))))))
(defun htmlize-ensure-fontified ()
(when (and (boundp 'font-lock-mode)
font-lock-mode)
(cond
((and (boundp 'jit-lock-mode)
(symbol-value 'jit-lock-mode))
(htmlize-with-fontify-message
(jit-lock-fontify-now (point-min) (point-max))))
((and (boundp 'lazy-lock-mode)
(symbol-value 'lazy-lock-mode))
(htmlize-with-fontify-message
(lazy-lock-fontify-region (point-min) (point-max))))
((and (boundp 'lazy-shot-mode)
(symbol-value 'lazy-shot-mode))
(htmlize-with-fontify-message
(lazy-shot-fontify-region (point-min) (point-max))))
)))
(defun htmlize-buffer (&optional buffer)
"Convert BUFFER to HTML, preserving colors and decorations.
The generated HTML is available in a new buffer, which is returned.
When invoked interactively, the new buffer is selected in the current
window. The title of the generated document will be set to the buffer's
file name or, if that's not available, to the buffer's name.
Note that htmlize doesn't fontify your buffers, it only uses the
decorations that are already present. If you don't set up font-lock or
something else to fontify your buffers, the resulting HTML will be
plain. Likewise, if you don't like the choice of colors, fix the mode
that created them, or simply alter the faces it uses."
(interactive)
(let ((htmlbuf (with-current-buffer (or buffer (current-buffer))
(htmlize-buffer-1))))
(when (interactive-p)
(switch-to-buffer htmlbuf))
htmlbuf))
(defun htmlize-region (beg end)
"Convert the region to HTML, preserving colors and decorations.
See `htmlize-buffer' for details."
(interactive "r")
(when (fboundp 'zmacs-deactivate-region)
(zmacs-deactivate-region))
(let ((htmlbuf (save-restriction
(narrow-to-region beg end)
(htmlize-buffer-1))))
(when (interactive-p)
(switch-to-buffer htmlbuf))
htmlbuf))
(defun htmlize-region-for-paste (beg end)
"Htmlize the region and return just the HTML as a string.
This forces the `inline-css' style and only returns the HTML body,
but without the BODY tag. This should make it useful for inserting
the text to another HTML buffer."
(let* ((htmlize-output-type 'inline-css)
(htmlbuf (htmlize-region beg end)))
(unwind-protect
(with-current-buffer htmlbuf
(buffer-substring (plist-get htmlize-buffer-places 'content-start)
(plist-get htmlize-buffer-places 'content-end)))
(kill-buffer htmlbuf))))
(defun htmlize-make-file-name (file)
"Make an HTML file name from FILE.
In its default implementation, this simply appends `.html' to FILE.
This function is called by htmlize to create the buffer file name, and
by `htmlize-file' to create the target file name.
More elaborate transformations are conceivable, such as changing FILE's
extension to `.html' (\"file.c\" -> \"file.html\"). If you want them,
overload this function to do it and htmlize will comply."
(concat file ".html"))
(defun htmlize-file (file &optional target)
"Load FILE, fontify it, convert it to HTML, and save the result.
Contents of FILE are inserted into a temporary buffer, whose major mode
is set with `normal-mode' as appropriate for the file type. The buffer
is subsequently fontified with `font-lock' and converted to HTML. Note
that, unlike `htmlize-buffer', this function explicitly turns on
font-lock. If a form of highlighting other than font-lock is desired,
please use `htmlize-buffer' directly on buffers so highlighted.
Buffers currently visiting FILE are unaffected by this function. The
function does not change current buffer or move the point.
If TARGET is specified and names a directory, the resulting file will be
saved there instead of to FILE's directory. If TARGET is specified and
does not name a directory, it will be used as output file name."
(interactive (list (read-file-name
"HTML-ize file: "
nil nil nil (and (buffer-file-name)
(file-name-nondirectory
(buffer-file-name))))))
(let ((output-file (if (and target (not (file-directory-p target)))
target
(expand-file-name
(htmlize-make-file-name (file-name-nondirectory file))
(or target (file-name-directory file)))))
(font-lock-mode nil)
(font-lock-auto-fontify nil)
(global-font-lock-mode nil)
(font-lock-maximum-size nil)
(font-lock-support-mode nil))
(with-temp-buffer
(insert-file-contents file)
(let ((buffer-file-name file))
(normal-mode)
(font-lock-mode 1)
(unless font-lock-mode
(font-lock-fontify-buffer))
(with-current-buffer (htmlize-buffer-1)
(unwind-protect
(progn
(run-hooks 'htmlize-file-hook)
(write-region (point-min) (point-max) output-file))
(kill-buffer (current-buffer)))))))
nil)
(defun htmlize-many-files (files &optional target-directory)
"Convert FILES to HTML and save the corresponding HTML versions.
FILES should be a list of file names to convert. This function calls
`htmlize-file' on each file; see that function for details. When
invoked interactively, you are prompted for a list of files to convert,
terminated with RET.
If TARGET-DIRECTORY is specified, the HTML files will be saved to that
directory. Normally, each HTML file is saved to the directory of the
corresponding source file."
(interactive
(list
(let (list file)
(while (not (equal (setq file (read-file-name
"HTML-ize file (RET to finish): "
(and list (file-name-directory
(car list)))
"" t))
""))
(push file list))
(nreverse list))))
(and target-directory
(not (file-directory-p target-directory))
(error "target-directory must name a directory: %s" target-directory))
(dolist (file files)
(htmlize-file file target-directory)))
(defun htmlize-many-files-dired (arg &optional target-directory)
"HTMLize dired-marked files."
(interactive "P")
(htmlize-many-files (dired-get-marked-files nil arg) target-directory))
(provide 'htmlize)