Subject: 24.0.91; htmlfontify.el: Add support for code block fontification on ODT export

From: Jambunathan K <[email-masked]>
Date: Mon, 31 Oct 2011 14:34:48 +0530

I have added support for exporting of Orgmode files to OpenDocument Text
format. See
http://lists.gnu.org/archive/html/bug-gnu-emacs/2011-10/msg01313.html.

As part of the above work, I have enhanced htmlfontify.el so that Org
babel blocks are fontified in the exported document. 

See the attached README.org for details.

The standalone files are bundled in to the htmlfontify.tar.gz for ease
of examination.

Sample test files:

Attachments:

--- a/htmlfontify.el	2011-10-31 12:50:26.812500000 +0530
+++ b/htmlfontify.el	2011-10-31 12:54:04.093750000 +0530
@@ -450,6 +450,12 @@ and so on."
   keep-overlays      : More of a bell (or possibly whistle) than an
                        optimization - If on, preserve overlay highlighting
                        (cf ediff or goo-font-lock) as well as basic faces.\n
+  body-text-only     : Emit only body-text. In concrete terms,
+                       1. Suppress calls to `hfy-page-header'and
+                          `hfy-page-footer'
+                       2. Pretend that `div-wrapper' option above is
+                          turned off
+                       3. Don't enclose output in <pre> </pre> tags
   And the following are planned but not yet available:\n
   kill-context-leak  : Suppress hyperlinking between files highlighted by
                        different modes.\n
@@ -463,7 +469,8 @@ which can never slow you down, but may r
                (const :tag "skip-refontification" skip-refontification)
                (const :tag "kill-context-leak"    kill-context-leak   )
                (const :tag "div-wrapper"          div-wrapper         )
-               (const :tag "keep-overlays"        keep-overlays       ))
+               (const :tag "keep-overlays"        keep-overlays       )
+	       (const :tag "body-text-only"       body-text-only      ))
   :group 'htmlfontify
   :tag   "optimizations")
 
@@ -1107,10 +1114,9 @@ See also `hfy-face-to-style-i', `hfy-fla
 
 ;; construct an assoc of (stripped-name . "{ css-stuff-here }") pairs
 ;; from a face:
-(defun hfy-face-to-css (fn)
-  "Take FN, a font or `defface' specification (cf `face-attr-construct')
-and return a CSS style specification.\n
-See also `hfy-face-to-style'."
+(defun hfy-face-to-css-default (fn)
+  "Default handler for mapping faces to styles.
+See also `hfy-face-to-css'."
   ;;(message "hfy-face-to-css");;DBUG
   (let* ((css-list (hfy-face-to-style fn))
          (seen     nil)
@@ -1124,6 +1130,17 @@ See also `hfy-face-to-style'."
            css-list)))
     (cons (hfy-css-name fn) (format "{%s}" (apply 'concat css-text)))) )
 
+(defvar hfy-face-to-css 'hfy-face-to-css-default
+  "Handler for mapping faces  to styles.
+The signature of the handler is of the form \(lambda (FN) ...\).
+FN is a font or `defface' specification (cf
+`face-attr-construct').  The handler should return a cons cell of
+the form (STYLE-NAME . STYLE-SPEC).
+
+The default handler is `hfy-face-to-css-default'.
+
+See also `hfy-face-to-style'.")
+
 (defalias 'hfy-prop-invisible-p
   (if (fboundp 'invisible-p) #'invisible-p
     (lambda (prop)
@@ -1310,20 +1327,27 @@ The plists are returned in descending pr
 
 ;; construct an assoc of (face-name . (css-name . "{ css-style }")) elements:
 (defun hfy-compile-stylesheet ()
-  "Trawl the current buffer, construct and return a `hfy-sheet-assoc'."
+  "Trawl the current buffer, construct and return a `hfy-sheet-assoc'.
+If `hfy-user-sheet-assoc' is currently bound then use it to
+collect new styles discovered during this run.  Otherwise create
+a new assoc."
   ;;(message "hfy-compile-stylesheet");;DBUG
   (let ((pt (point-min))
         ;; Make the font stack stay:
         ;;(hfy-tmpfont-stack nil)
         (fn         nil)
-        (style      nil))
+        (style      (and (boundp 'hfy-user-sheet-assoc) hfy-user-sheet-assoc)))
     (save-excursion
       (goto-char pt)
       (while (< pt (point-max))
         (if (and (setq fn (hfy-face-at pt)) (not (assoc fn style)))
-            (push (cons fn (hfy-face-to-css fn)) style))
-        (setq pt (next-char-property-change pt))) )
-    (push (cons 'default (hfy-face-to-css 'default)) style)))
+            (push (cons fn (funcall hfy-face-to-css fn)) style))
+        (setq pt (next-char-property-change pt))))
+    (unless (assoc 'default style)
+      (push (cons 'default (funcall hfy-face-to-css 'default)) style))
+    (when (boundp 'hfy-user-sheet-assoc)
+      (setq hfy-user-sheet-assoc style))
+    style))
 
 (defun hfy-fontified-p ()
   "`font-lock' doesn't like to say it's been fontified when in batch
@@ -1424,7 +1448,7 @@ Returns a modified copy of FACE-MAP."
         (setq pt (next-char-property-change pt))
         (setq pt-narrow (+ offset pt)))
       (if (and map (not (eq 'end (cdar map))))
-          (push (cons (- (point-max) (point-min)) 'end) map)))
+          (push (cons (1+ (- (point-max) (point-min))) 'end) map)))
     (if (hfy-opt 'merge-adjacent-tags) (hfy-merge-adjacent-spans map) map)))
 
 (defun hfy-buffer ()
@@ -1546,6 +1570,61 @@ Do not record undo information during ev
       (remove-text-properties (point-min) (point-max)
                               '(hfy-show-trailing-whitespace)))))
 
+(defun hfy-begin-span (style text-block text-id text-begins-block-p)
+  "Default handler to begin a span of text.
+Insert \"<span class=\"STYLE\" ...>\". See
+`hfy-begin-span-handler' for more information."
+  (when text-begins-block-p
+    (insert
+     (format "<span onclick=\"toggle_invis('%s');\">…</span>" text-block)))
+
+  (insert
+   (if text-block
+       (format "<span class=\"%s\" id=\"%s-%d\">" style text-block text-id)
+     (format "<span class=\"%s\">" style))))
+
+(defun hfy-end-span ()
+  "Default handler to end a span of text.
+Insert \"</span>\".  See `hfy-end-span-handler' for more
+information."
+  (insert "</span>"))
+
+(defvar hfy-begin-span-handler 'hfy-begin-span
+  "Handler to begin a span of text.
+The signature of the handler is \(lambda (STYLE TEXT-BLOCK
+TEXT-ID TEXT-BEGINS-BLOCK-P) ...\).  The handler must insert
+appropriate tags to begin a span of text.
+
+STYLE is the name of the style that begins at point.  It is
+derived from the face attributes as part of `hfy-face-to-css'
+callback.  The other arguments TEXT-BLOCK, TEXT-ID,
+TEXT-BEGINS-BLOCK-P are non-nil only if the buffer contains
+invisible text.
+
+TEXT-BLOCK is a string that identifies a single chunk of visible
+or invisible text of which the current position is a part.  For
+visible portions, it's value is \"nil\". For invisible portions,
+it's value is computed as part of `hfy-invisible-name'.
+
+TEXT-ID marks a unique position within a block.  It is set to
+value of `point' at the current buffer position.
+
+TEXT-BEGINS-BLOCK-P is a boolean and is non-nil if the current
+span also begins a invisible portion of text.
+
+An implementation can use TEXT-BLOCK, TEXT-ID,
+TEXT-BEGINS-BLOCK-P to implement fold/unfold-on-mouse-click like
+behaviour.
+
+The default handler is `hfy-begin-span'.")
+
+(defvar hfy-end-span-handler 'hfy-end-span
+  "Handler to end a span of text.
+The signature of the handler is \(lambda () ...\).  The handler
+must insert appropriate tags to end a span of text.
+
+The default handler is `hfy-end-span'.")
+
 (defun hfy-fontify-buffer (&optional srcdir file)
   "Implement the guts of `htmlfontify-buffer'.
 SRCDIR, if set, is the directory being htmlfontified.
@@ -1633,23 +1712,19 @@ FILE, if set, is the file name."
               (or (get-text-property pt 'hfy-linkp)
                   (get-text-property pt 'hfy-endl )))
         (if (eq 'end fn)
-            (insert "</span>")
+	    (funcall hfy-end-span-handler)
           (if (not (and srcdir file))
               nil
             (when move-link
               (remove-text-properties (point) (1+ (point)) '(hfy-endl nil))
               (put-text-property pt (1+ pt) 'hfy-endl t) ))
           ;; if we have invisible blocks, we need to do some extra magic:
-          (if invis-ranges
-              (let ((iname (hfy-invisible-name pt invis-ranges))
-                    (fname (hfy-lookup         fn css-sheet   )))
-                (when (assq pt invis-ranges)
-                  (insert
-                   (format "<span onclick=\"toggle_invis('%s');\">" iname))
-                  (insert "…</span>"))
-                (insert
-                 (format "<span class=\"%s\" id=\"%s-%d\">" fname iname pt)))
-            (insert (format "<span class=\"%s\">" (hfy-lookup fn css-sheet))))
+	  (funcall hfy-begin-span-handler
+		   (hfy-lookup fn css-sheet)
+		   (and invis-ranges
+			(format "%s" (hfy-invisible-name pt invis-ranges)))
+		   (and invis-ranges pt)
+		   (and invis-ranges (assq pt invis-ranges)))
           (if (not move-link) nil
             ;;(message "removing prop2 @ %d" (point))
             (if (remove-text-properties (point) (1+ (point)) '(hfy-endl nil))
@@ -1697,23 +1772,41 @@ FILE, if set, is the file name."
     ;; so we have to do this after we use said properties:
     ;; (message "munging dangerous characters")
     (hfy-html-dekludge-buffer)
+    (unless (hfy-opt 'body-text-only)
     ;; insert the stylesheet at the top:
     (goto-char (point-min))
+
     ;;(message "inserting stylesheet")
     (insert (hfy-sprintf-stylesheet css-sheet file))
+
     (if (hfy-opt 'div-wrapper) (insert "<div class=\"default\">"))
     (insert "\n<pre>")
     (goto-char (point-max))
     (insert "</pre>\n")
     (if (hfy-opt 'div-wrapper) (insert "</div>"))
     ;;(message "inserting footer")
-    (insert (funcall hfy-page-footer file))
+      (insert (funcall hfy-page-footer file)))
     ;; call any post html-generation hooks:
     (run-hooks 'hfy-post-html-hooks)
     ;; return the html buffer
     (set-buffer-modified-p nil)
     html-buffer))
 
+(defun htmlfontify-string (string)
+  "Take a STRING and return a fontified version of it.
+It is assumed that STRING has text properties that allow it to be
+fontified.  This is a simple convenience wrapper around
+`htmlfontify-buffer'."
+  (let* ((hfy-optimisations-1 (copy-list hfy-optimisations))
+	 (hfy-optimisations (add-to-list 'hfy-optimisations-1
+					 'skip-refontification)))
+    (with-temp-buffer
+      (insert string)
+      (htmlfontify-buffer)
+      (prog1 (buffer-string)
+	(setq buffer-modified-p nil)
+	(kill-buffer)))))
+
 (defun hfy-force-fontification ()
   "Try to force font-locking even when it is optimized away."
   (run-hooks 'hfy-init-kludge-hook)
>From b15e66c86024cc5f0350e96d26197c60241954ec Mon Sep 17 00:00:00 2001
From: Jambunathan K <[email-masked]>
Date: Fri, 19 Aug 2011 01:44:19 +0530
Subject: [PATCH 1/5] htmlfontify.el: Add new output option - `body-text-only'

* contrib/lisp/htmlfontify.el (hfy-optimisations): Define new
option `body-text-only'
(hfy-fontify-buffer): Honor above setting.
---
 contrib/lisp/htmlfontify.el |   34 ++++++++++++++++++++++------------
 1 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/contrib/lisp/htmlfontify.el b/contrib/lisp/htmlfontify.el
index c8e9558..5415e58 100755
--- a/contrib/lisp/htmlfontify.el
+++ b/contrib/lisp/htmlfontify.el
@@ -450,6 +450,12 @@ and so on."
   keep-overlays      : More of a bell (or possibly whistle) than an
                        optimization - If on, preserve overlay highlighting
                        (cf ediff or goo-font-lock) as well as basic faces.\n
+  body-text-only     : Emit only body-text. In concrete terms,
+                       1. Suppress calls to `hfy-page-header'and
+                          `hfy-page-footer'
+                       2. Pretend that `div-wrapper' option above is
+                          turned off
+                       3. Don't enclose output in <pre> </pre> tags
   And the following are planned but not yet available:\n
   kill-context-leak  : Suppress hyperlinking between files highlighted by
                        different modes.\n
@@ -463,7 +469,8 @@ which can never slow you down, but may result in incomplete fontification."
                (const :tag "skip-refontification" skip-refontification)
                (const :tag "kill-context-leak"    kill-context-leak   )
                (const :tag "div-wrapper"          div-wrapper         )
-               (const :tag "keep-overlays"        keep-overlays       ))
+               (const :tag "keep-overlays"        keep-overlays       )
+	       (const :tag "body-text-only"       body-text-only      ))
   :group 'htmlfontify
   :tag   "optimizations")
 
@@ -1697,17 +1704,20 @@ FILE, if set, is the file name."
     ;; so we have to do this after we use said properties:
     ;; (message "munging dangerous characters")
     (hfy-html-dekludge-buffer)
-    ;; insert the stylesheet at the top:
-    (goto-char (point-min))
-    ;;(message "inserting stylesheet")
-    (insert (hfy-sprintf-stylesheet css-sheet file))
-    (if (hfy-opt 'div-wrapper) (insert "<div class=\"default\">"))
-    (insert "\n<pre>")
-    (goto-char (point-max))
-    (insert "</pre>\n")
-    (if (hfy-opt 'div-wrapper) (insert "</div>"))
-    ;;(message "inserting footer")
-    (insert (funcall hfy-page-footer file))
+    (unless (hfy-opt 'body-text-only)
+      ;; insert the stylesheet at the top:
+      (goto-char (point-min))
+
+      ;;(message "inserting stylesheet")
+      (insert (hfy-sprintf-stylesheet css-sheet file))
+
+      (if (hfy-opt 'div-wrapper) (insert "<div class=\"default\">"))
+      (insert "\n<pre>")
+      (goto-char (point-max))
+      (insert "</pre>\n")
+      (if (hfy-opt 'div-wrapper) (insert "</div>"))
+      ;;(message "inserting footer")
+      (insert (funcall hfy-page-footer file)))
     ;; call any post html-generation hooks:
     (run-hooks 'hfy-post-html-hooks)
     ;; return the html buffer
-- 
1.7.5.1

>From 6670131285304500156b6e2f557f7728b9ad5e1a Mon Sep 17 00:00:00 2001
From: Jambunathan K <[email-masked]>
Date: Fri, 19 Aug 2011 01:45:59 +0530
Subject: [PATCH 2/5] htmlfontify.el: Allow customization of begin and end
 span markers

* contrib/lisp/htmlfontify.el (hfy-begin-span, hfy-end-span):
New routines factored out form hfy-fontify-buffer.
(hfy-begin-span-handler, hfy-end-span-handler): New variables
that permit insertion of custom tags.
(hfy-fontify-buffer): Use above handlers.
---
 contrib/lisp/htmlfontify.el |   73 ++++++++++++++++++++++++++++++++++++------
 1 files changed, 62 insertions(+), 11 deletions(-)

diff --git a/contrib/lisp/htmlfontify.el b/contrib/lisp/htmlfontify.el
index 5415e58..b4b1af2 100755
--- a/contrib/lisp/htmlfontify.el
+++ b/contrib/lisp/htmlfontify.el
@@ -1553,6 +1553,61 @@ Do not record undo information during evaluation of BODY."
       (remove-text-properties (point-min) (point-max)
                               '(hfy-show-trailing-whitespace)))))
 
+(defun hfy-begin-span (style text-block text-id text-begins-block-p)
+  "Default handler to begin a span of text.
+Insert \"<span class=\"STYLE\" ...>\". See
+`hfy-begin-span-handler' for more information."
+  (when text-begins-block-p
+    (insert
+     (format "<span onclick=\"toggle_invis('%s');\">…</span>" text-block)))
+
+  (insert
+   (if text-block
+       (format "<span class=\"%s\" id=\"%s-%d\">" style text-block text-id)
+     (format "<span class=\"%s\">" style))))
+
+(defun hfy-end-span ()
+  "Default handler to end a span of text.
+Insert \"</span>\".  See `hfy-end-span-handler' for more
+information."
+  (insert "</span>"))
+
+(defvar hfy-begin-span-handler 'hfy-begin-span
+  "Handler to begin a span of text.
+The signature of the handler is \(lambda (STYLE TEXT-BLOCK
+TEXT-ID TEXT-BEGINS-BLOCK-P) ...\).  The handler must insert
+appropriate tags to begin a span of text.
+
+STYLE is the name of the style that begins at point.  It is
+derived from the face attributes as part of `hfy-face-to-css'
+callback.  The other arguments TEXT-BLOCK, TEXT-ID,
+TEXT-BEGINS-BLOCK-P are non-nil only if the buffer contains
+invisible text.
+
+TEXT-BLOCK is a string that identifies a single chunk of visible
+or invisible text of which the current position is a part.  For
+visible portions, it's value is \"nil\". For invisible portions,
+it's value is computed as part of `hfy-invisible-name'.
+
+TEXT-ID marks a unique position within a block.  It is set to
+value of `point' at the current buffer position.
+
+TEXT-BEGINS-BLOCK-P is a boolean and is non-nil if the current
+span also begins a invisible portion of text.
+
+An implementation can use TEXT-BLOCK, TEXT-ID,
+TEXT-BEGINS-BLOCK-P to implement fold/unfold-on-mouse-click like
+behaviour.
+
+The default handler is `hfy-begin-span'.")
+
+(defvar hfy-end-span-handler 'hfy-end-span
+  "Handler to end a span of text.
+The signature of the handler is \(lambda () ...\).  The handler
+must insert appropriate tags to end a span of text.
+
+The default handler is `hfy-end-span'.")
+
 (defun hfy-fontify-buffer (&optional srcdir file)
   "Implement the guts of `htmlfontify-buffer'.
 SRCDIR, if set, is the directory being htmlfontified.
@@ -1640,23 +1695,19 @@ FILE, if set, is the file name."
               (or (get-text-property pt 'hfy-linkp)
                   (get-text-property pt 'hfy-endl )))
         (if (eq 'end fn)
-            (insert "</span>")
+	    (funcall hfy-end-span-handler)
           (if (not (and srcdir file))
               nil
             (when move-link
               (remove-text-properties (point) (1+ (point)) '(hfy-endl nil))
               (put-text-property pt (1+ pt) 'hfy-endl t) ))
           ;; if we have invisible blocks, we need to do some extra magic:
-          (if invis-ranges
-              (let ((iname (hfy-invisible-name pt invis-ranges))
-                    (fname (hfy-lookup         fn css-sheet   )))
-                (when (assq pt invis-ranges)
-                  (insert
-                   (format "<span onclick=\"toggle_invis('%s');\">" iname))
-                  (insert "…</span>"))
-                (insert
-                 (format "<span class=\"%s\" id=\"%s-%d\">" fname iname pt)))
-            (insert (format "<span class=\"%s\">" (hfy-lookup fn css-sheet))))
+	  (funcall hfy-begin-span-handler
+		   (hfy-lookup fn css-sheet)
+		   (and invis-ranges
+			(format "%s" (hfy-invisible-name pt invis-ranges)))
+		   (and invis-ranges pt)
+		   (and invis-ranges (assq pt invis-ranges)))
           (if (not move-link) nil
             ;;(message "removing prop2 @ %d" (point))
             (if (remove-text-properties (point) (1+ (point)) '(hfy-endl nil))
-- 
1.7.5.1

>From 0c74cd517e6ecc41bebf6053f0512d42b13dcb77 Mon Sep 17 00:00:00 2001
From: Jambunathan K <[email-masked]>
Date: Fri, 19 Aug 2011 01:47:48 +0530
Subject: [PATCH 3/5] htmlfontify.el: Allow customization of face to style
 mappings

* contrib/lisp/htmlfontify.el (hfy-face-to-css-default): Same
as the earlier hfy-face-to-css.
(hfy-face-to-css): Re-defined to be a variable.
(hfy-compile-stylesheet): Related changes.
---
 contrib/lisp/htmlfontify.el |   22 ++++++++++++++++------
 1 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/contrib/lisp/htmlfontify.el b/contrib/lisp/htmlfontify.el
index b4b1af2..3f5bdbd 100755
--- a/contrib/lisp/htmlfontify.el
+++ b/contrib/lisp/htmlfontify.el
@@ -1114,10 +1114,9 @@ See also `hfy-face-to-style-i', `hfy-flatten-style'."
 
 ;; construct an assoc of (stripped-name . "{ css-stuff-here }") pairs
 ;; from a face:
-(defun hfy-face-to-css (fn)
-  "Take FN, a font or `defface' specification (cf `face-attr-construct')
-and return a CSS style specification.\n
-See also `hfy-face-to-style'."
+(defun hfy-face-to-css-default (fn)
+  "Default handler for mapping faces to styles.
+See also `hfy-face-to-css'."
   ;;(message "hfy-face-to-css");;DBUG
   (let* ((css-list (hfy-face-to-style fn))
          (seen     nil)
@@ -1131,6 +1130,17 @@ See also `hfy-face-to-style'."
            css-list)))
     (cons (hfy-css-name fn) (format "{%s}" (apply 'concat css-text)))) )
 
+(defvar hfy-face-to-css 'hfy-face-to-css-default
+  "Handler for mapping faces  to styles.
+The signature of the handler is of the form \(lambda (FN) ...\).
+FN is a font or `defface' specification (cf
+`face-attr-construct').  The handler should return a cons cell of
+the form (STYLE-NAME . STYLE-SPEC).
+
+The default handler is `hfy-face-to-css-default'.
+
+See also `hfy-face-to-style'.")
+
 (defalias 'hfy-prop-invisible-p
   (if (fboundp 'invisible-p) #'invisible-p
     (lambda (prop)
@@ -1328,9 +1338,9 @@ The plists are returned in descending priority order."
       (goto-char pt)
       (while (< pt (point-max))
         (if (and (setq fn (hfy-face-at pt)) (not (assoc fn style)))
-            (push (cons fn (hfy-face-to-css fn)) style))
+            (push (cons fn (funcall hfy-face-to-css fn)) style))
         (setq pt (next-char-property-change pt))) )
-    (push (cons 'default (hfy-face-to-css 'default)) style)))
+      (push (cons 'default (funcall hfy-face-to-css 'default)) style)))
 
 (defun hfy-fontified-p ()
   "`font-lock' doesn't like to say it's been fontified when in batch
-- 
1.7.5.1

>From c71fb16dfc24f25a12bf5d84fac2f3d9d427e06a Mon Sep 17 00:00:00 2001
From: Jambunathan K <[email-masked]>
Date: Fri, 19 Aug 2011 01:49:05 +0530
Subject: [PATCH 4/5] htmlfontify.el: Add support for building stylesheet over
 multiple runs

* contrib/lisp/htmlfontify.el (hfy-compile-stylesheet):
Modified. Allow stylesheet to be built over multiple
runs. This is made possible by having the caller let bind a
special variable hfy-user-sheet-assoc.
---
 contrib/lisp/htmlfontify.el |   15 +++++++++++----
 1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/contrib/lisp/htmlfontify.el b/contrib/lisp/htmlfontify.el
index 3f5bdbd..6a4d62f 100755
--- a/contrib/lisp/htmlfontify.el
+++ b/contrib/lisp/htmlfontify.el
@@ -1327,20 +1327,27 @@ The plists are returned in descending priority order."
 
 ;; construct an assoc of (face-name . (css-name . "{ css-style }")) elements:
 (defun hfy-compile-stylesheet ()
-  "Trawl the current buffer, construct and return a `hfy-sheet-assoc'."
+  "Trawl the current buffer, construct and return a `hfy-sheet-assoc'.
+If `hfy-user-sheet-assoc' is currently bound then use it to
+collect new styles discovered during this run.  Otherwise create
+a new assoc."
   ;;(message "hfy-compile-stylesheet");;DBUG
   (let ((pt (point-min))
         ;; Make the font stack stay:
         ;;(hfy-tmpfont-stack nil)
         (fn         nil)
-        (style      nil))
+        (style      (and (boundp 'hfy-user-sheet-assoc) hfy-user-sheet-assoc)))
     (save-excursion
       (goto-char pt)
       (while (< pt (point-max))
         (if (and (setq fn (hfy-face-at pt)) (not (assoc fn style)))
             (push (cons fn (funcall hfy-face-to-css fn)) style))
-        (setq pt (next-char-property-change pt))) )
-      (push (cons 'default (funcall hfy-face-to-css 'default)) style)))
+        (setq pt (next-char-property-change pt))))
+    (unless (assoc 'default style)
+      (push (cons 'default (funcall hfy-face-to-css 'default)) style))
+    (when (boundp 'hfy-user-sheet-assoc)
+      (setq hfy-user-sheet-assoc style))
+    style))
 
 (defun hfy-fontified-p ()
   "`font-lock' doesn't like to say it's been fontified when in batch
-- 
1.7.5.1

>From a81bb6fa6afce64badbaa4c47eb77073fc807ee2 Mon Sep 17 00:00:00 2001
From: Jambunathan K <[email-masked]>
Date: Fri, 19 Aug 2011 01:50:23 +0530
Subject: [PATCH 5/5] htmlfontify.el: Add htmlfontify-string & fix a bug in
 fontification

* htmlfontify.el (htmlfontify-string): New defun.
(hfy-compile-face-map): Make sure that the last char in the
buffer is correctly fontified.
---
 contrib/lisp/htmlfontify.el |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/contrib/lisp/htmlfontify.el b/contrib/lisp/htmlfontify.el
index 6a4d62f..5b86d98 100755
--- a/contrib/lisp/htmlfontify.el
+++ b/contrib/lisp/htmlfontify.el
@@ -1448,7 +1448,7 @@ Returns a modified copy of FACE-MAP."
         (setq pt (next-char-property-change pt))
         (setq pt-narrow (+ offset pt)))
       (if (and map (not (eq 'end (cdar map))))
-          (push (cons (- (point-max) (point-min)) 'end) map)))
+          (push (cons (1+ (- (point-max) (point-min))) 'end) map)))
     (if (hfy-opt 'merge-adjacent-tags) (hfy-merge-adjacent-spans map) map)))
 
 (defun hfy-buffer ()
@@ -1792,6 +1792,21 @@ FILE, if set, is the file name."
     (set-buffer-modified-p nil)
     html-buffer))
 
+(defun htmlfontify-string (string)
+  "Take a STRING and return a fontified version of it.
+It is assumed that STRING has text properties that allow it to be
+fontified.  This is a simple convenience wrapper around
+`htmlfontify-buffer'."
+  (let* ((hfy-optimisations-1 (copy-list hfy-optimisations))
+	 (hfy-optimisations (add-to-list 'hfy-optimisations-1
+					 'skip-refontification)))
+    (with-temp-buffer
+      (insert string)
+      (htmlfontify-buffer)
+      (prog1 (buffer-string)
+	(setq buffer-modified-p nil)
+	(kill-buffer)))))
+
 (defun hfy-force-fontification ()
   "Try to force font-locking even when it is optimized away."
   (run-hooks 'hfy-init-kludge-hook)
-- 
1.7.5.1