unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Core package offering - engrave-faces.el
@ 2021-07-09 17:39 Timothy
  2021-07-09 21:14 ` Clément Pit-Claudel
  2021-07-16  6:04 ` Timothy
  0 siblings, 2 replies; 13+ messages in thread
From: Timothy @ 2021-07-09 17:39 UTC (permalink / raw)
  To: Emacs developers

[-- Attachment #1: Type: text/plain, Size: 2538 bytes --]

Hi All,

Over the last few months I have worked on a package that I think may be
a good candidate for inclusion into Emacs. It has recently reached what
I consider a sufficient quality for consideration - though I anticipate
that should this be seen as promising there will likely be suggested
improvements.

This has been motivated by a desire to produce a better method of
formatting Org source blocks in PDF export, and inspired by htmlize.

Unlike htmlize, Engrave Faces provides general functionality to
transform a buffer into another format with font-lock information.
This core functionality is currently made us of in
engrave-faces-latex.el, engrave-faces-ansi.el, and engrave-faces-html.el
to provide exporters for LaTeX, ASCII/ANSI, and HTML.

Compared to htmlize and htmlfontify.el, Engrave Faces currently lacks
the ability to treat links specially, or properly handle the text
transformation performed by overlays*. However, this is also much smaller
and hopefully more maintainable.
- engrave-faces.el is 250 sloc
- engrave-faces-latex.el is 100 sloc
- engrave-faces-ansi.el is 140 sloc
- engrave-faces-html.el is 115 sloc
( *there may be other differences, but this is what's apparent to me. )

For comparison, htmlize.el is 1700 sloc and htmlfontify.el is 2200 sloc.

Engrave Faces also possesses the unique capability that it can be given
a list of preset face styles which will override the current attributes
of those faces. These overrides are also passed down with inheritance,
via an internal expansion and merging of face attribute information.
This allows one to save the details for a certain theme, and "engrave"
the buffer with font-lock attributes from that theme regardless of what
the current theme is. For ease of applying this to with your current
theme a function `engrave-faces-generate-preset' is provided which
re-generates the saved face information using the current theme.

I feel that this is likely generally useful functionality, and so would
like to offer it to Emacs to make it more accessible and more visible
for improvement.

I am currently an FSF-assigned contributor to Org mode, and assume that
this should make contributing to Emacs fairly straightforward.

Attached you may find the engrave-faces{,-latex,-ansi,-html}.el as well
as LaTeX, ASCII/ANSI, and HTML versions of engrave-faces.el for sampling.
The code is also available at https://github.com/tecosaur/engrave-faces.

All the best,

--
Timothy

p.s. I am not subscribed to this list, so please include my address in replies.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: engrave-faces.el --]
[-- Type: text/x-emacs-lisp, Size: 14106 bytes --]

;;; engrave-faces.el --- Convert font-lock faces to other formats -*- lexical-binding: t; -*-

;; Copyright (C) 2021 TEC

;; Author: TEC <https://github/tecosaur>
;; Maintainer: TEC <tec@tecosaur.com>
;; Created: January 18, 2021
;; Modified: July 10, 2021
;; Version: 0.1.0
;; Keywords: faces
;; Homepage: https://github.com/tecosaur/engrave-faces
;; Package-Requires: ((emacs "27.1"))

;;; License:

;; This file is part of engrave-faces, which is not part of GNU Emacs.
;;
;; engrave-faces is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; engrave-faces is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with engrave-faces.  If not, see <https://www.gnu.org/licenses/>.
;;
;; SPDX-License-Identifier: GPL-3.0-or-later

;;; Commentary:

;;  Convert font-lock faces to other formats.

;;; Code:

(require 'map)

;;;###autoload
(defvar engrave-faces--backends nil)

;;;###autoload
(defmacro engrave-faces-define-backend (name extension face-transformer &optional standalone-transformer view-setup)
  `(progn (add-to-list 'engrave-faces--backends
                       (list ,name :face-transformer ,face-transformer :extension ,extension))
          (defun ,(intern (concat "engrave-faces-" name "-buffer")) (&optional switch-to-result)
            ,(concat "Convert buffer to " name " formatting.")
            (interactive '(t))
            (let ((buf (engrave-faces-buffer ,name)))
              (when switch-to-result
                (switch-to-buffer buf)
                ,(when view-setup `(funcall ,view-setup)))
              buf))
          ,(when standalone-transformer
             `(defun ,(intern (concat "engrave-faces-" name "-buffer-standalone")) (&optional switch-to-result)
                (interactive '(t))
                ,(concat "Export the current buffer to a standalone " name " buffer.")
                (let ((buf (engrave-faces-buffer ,name)))
                  (with-current-buffer buf
                    (funcall ,standalone-transformer))
                  (when switch-to-result
                    (switch-to-buffer buf)
                    ,(when view-setup `(funcall ,view-setup)))
                  buf)))
          (defvar ,(intern (concat "engrave-faces-" name "-before-hook")) nil)
          (defvar ,(intern (concat "engrave-faces-" name "-after-hook")) nil)))

(defgroup engrave-faces nil
  "Export buffers with font-lock information to other formats."
  :group 'hypermedia)

(defcustom engrave-faces-attributes-of-interest
  '(:foreground :background :slant :weight :height :strike-through)
  "Attributes which sould be paid attention to."
  :type '(repeat symbol)
  :group 'engrave-faces)

(defcustom engrave-faces-before-hook nil
  "Hook run before engraving a buffer.
The hook functions are run in the source buffer (not the resulting buffer)."
  :type 'hook
  :group 'engrave-faces)

(defcustom engrave-faces-after-hook nil
  "Hook run after engraving a buffer.
Unlike `engrave-faces-before-hook', these functions are run in the generated
buffer.  You may use them to modify the outlook of the final output."
  :type 'hook
  :group 'engrave-faces)

(defun engrave-faces-buffer (backend)
  "Export the current buffer with BACKEND and return the created buffer."
  (save-excursion
    ;; Protect against the hook changing the current buffer.
    (save-excursion
      (run-hooks 'engrave-faces-before-hook)
      (run-hooks (intern (concat "engrave-faces-" backend "-before-hook"))))
    ;; Convince font-lock support modes to fontify the entire buffer
    ;; in advance.
    (when (and (boundp 'jit-lock-mode)
               (symbol-value 'jit-lock-mode))
      (jit-lock-fontify-now (point-min) (point-max)))
    (font-lock-ensure)

    ;; It's important that the new buffer inherits default-directory
    ;; from the current buffer.
    (let ((engraved-buf (generate-new-buffer (if (buffer-file-name)
                                                 (concat (file-name-nondirectory (buffer-file-name))
                                                         (plist-get (cdr (assoc backend engrave-faces--backends)) :extension))
                                               (concat "*" backend "*"))))
          (face-transformer (plist-get (cdr (assoc backend engrave-faces--backends)) :face-transformer))
          (completed nil))
      (unwind-protect
          (let (next-change text)
            ;; This loop traverses and reads the source buffer, appending the
            ;; resulting text to the export buffer. This method is fast because:
            ;; 1) it doesn't require examining the text properties char by char
            ;; (engrave-faces-next-face-change is used to move between runs with
            ;; the same face), and 2) it doesn't require frequent buffer
            ;; switches, which are slow because they rebind all buffer-local
            ;; vars.
            (goto-char (point-min))
            (while (not (eobp))
              (setq next-change (engrave-faces-next-face-change (point)))
              (setq text (buffer-substring-no-properties (point) next-change))
              ;; Don't bother writing anything if there's no text (this
              ;; happens in invisible regions).
              (when (> (length text) 0)
                (princ (funcall face-transformer
                                (let ((prop (get-text-property (point) 'face)))
                                  (cond
                                   ((null prop) 'default)
                                   ((and (listp prop)
                                         (eq (car prop) 'quote)) (eval prop))
                                   (t prop)))
                                text)
                       engraved-buf))
              (goto-char next-change)))
        (setq completed t))
      (if (not completed)
          (kill-buffer engraved-buf)
        (with-current-buffer engraved-buf
          (run-hooks 'engrave-faces-after-hook)
          (run-hooks (intern (concat "engrave-faces-" backend "-after-hook"))))
        engraved-buf))))

(defun engrave-faces-merge-attributes (faces &optional attributes)
  "Find the final ATTRIBUTES for text with FACES."
  (setq faces (engrave-faces-explicit-inheritance (if (listp faces) faces (list faces))))
  (mapcan (lambda (attr)
            (list attr (car (engrave-faces-attribute-values faces attr))))
          (or attributes engrave-faces-attributes-of-interest)))

(defun engrave-faces-explicit-inheritance (faces)
  "Expand :inherit for each face in FACES.
I.e. ([facea :inherit faceb] facec) results in (facea faceb facec)"
  (delq nil
        (mapcan
         (lambda (face)
           (if (listp face)
               (let ((inherit (plist-get face :inherit)))
                 (cons (map-delete face :inherit)
                       (engrave-faces-explicit-inheritance inherit)))
             (cons face
                   (let ((inherit (face-attribute face :inherit nil nil)))
                     (when (and inherit (not (eq inherit 'unspecified)))
                       (engrave-faces-explicit-inheritance inherit))))))
         (if (listp faces) faces (list faces)))))

(defun engrave-faces-attribute-values (faces attribute)
  "Fetch all specified instances of ATTRIBUTE for FACES, ignoring inheritence.
To consider inheritence, use `engrave-faces-explicit-inheritance' first."
  (delq nil (delq 'unspecified
                  (mapcar
                   (lambda (face)
                     (or (plist-get (cdr (assoc face engrave-faces-preset-styles)) attribute)
                         (cond
                          ((symbolp face) (face-attribute face attribute nil nil))
                          ((listp face) (plist-get face attribute)))))
                   (delq 'default (if (listp faces) faces (list faces)))))))

(defun engrave-faces-next-face-change (pos &optional limit)
  "Find the next face change from POS up to LIMIT.

This function is lifted from htmlize."
  ;; (engrave-faces-next-change pos 'face limit) would skip over entire
  ;; overlays that specify the `face' property, even when they
  ;; contain smaller text properties that also specify `face'.
  ;; Emacs display engine merges those faces, and so must we.
  (or limit
      (setq limit (point-max)))
  (let ((next-prop (next-single-property-change pos 'face nil limit))
        (overlay-faces (engrave-faces-overlay-faces-at pos)))
    (while (progn
             (setq pos (next-overlay-change pos))
             (and (< pos next-prop)
                  (equal overlay-faces (engrave-faces-overlay-faces-at pos)))))
    (setq pos (min pos next-prop))
    ;; Additionally, we include the entire region that specifies the
    ;; `display' property.
    (when (get-char-property pos 'display)
      (setq pos (next-single-char-property-change pos 'display nil limit)))
    pos))

(defun engrave-faces-overlay-faces-at (pos)
  (delq nil (mapcar (lambda (o) (overlay-get o 'face)) (overlays-at pos))))

;;; Style helpers

(defcustom engrave-faces-preset-styles ; doom-one-light
  '((default                             :short "default"          :slug "D"     :foreground "#383a42")
    (font-lock-keyword-face              :short "keyword"          :slug "k"     :foreground "#e45649")
    (font-lock-doc-face                  :short "doc"              :slug "d"     :foreground "#84888b" :slant italic)
    (font-lock-type-face                 :short "type"             :slug "t"     :foreground "#986801")
    (font-lock-string-face               :short "string"           :slug "s"     :foreground "#50a14f")
    (font-lock-warning-face              :short "warning"          :slug "w"     :foreground "#986801")
    (font-lock-builtin-face              :short "builtin"          :slug "b"     :foreground "#a626a4")
    (font-lock-comment-face              :short "comment"          :slug "ct"    :foreground "#9ca0a4")
    (font-lock-constant-face             :short "constant"         :slug "c"     :foreground "#b751b6")
    (font-lock-preprocessor-face         :short "preprocessor"     :slug "pp"    :foreground "#4078f2" :weight bold)
    (font-lock-negation-char-face        :short "neg-char"         :slug "nc"    :foreground "#4078f2" :weight bold)
    (font-lock-variable-name-face        :short "variable"         :slug "v"     :foreground "#6a1868")
    (font-lock-function-name-face        :short "function"         :slug "f"     :foreground "#a626a4")
    (font-lock-comment-delimiter-face    :short "comment-delim"    :slug "cd"    :foreground "#9ca0a4")
    (font-lock-regexp-grouping-construct :short "regexp"           :slug "rc"    :foreground "#4078f2" :weight bold)
    (font-lock-regexp-grouping-backslash :short "regexp-backslash" :slug "rb"    :foreground "#4078f2" :weight bold)
    (org-block                           :short "org-block"        :slug "ob") ; forcing no background is preferable
    (highlight-numbers-number            :short "number"           :slug "hn"    :foreground "#da8548" :weight bold)
    (highlight-quoted-quote              :short "qquote"           :slug "hq"    :foreground "#4078f2")
    (highlight-quoted-symbol             :short "qsymbol"          :slug "hs"    :foreground "#986801")
    (rainbow-delimiters-depth-1-face     :short "rd1"              :slug "rdi"   :foreground "#4078f2")
    (rainbow-delimiters-depth-2-face     :short "rd2"              :slug "rdii"  :foreground "#a626a4")
    (rainbow-delimiters-depth-3-face     :short "rd3"              :slug "rdiii" :foreground "#50a14f")
    (rainbow-delimiters-depth-4-face     :short "rd4"              :slug "rdiv"  :foreground "#da8548")
    (rainbow-delimiters-depth-5-face     :short "rd5"              :slug "rdv"   :foreground "#b751b6")
    (rainbow-delimiters-depth-6-face     :short "rd6"              :slug "rdvi"  :foreground "#986801")
    (rainbow-delimiters-depth-7-face     :short "rd7"              :slug "rdvii" :foreground "#4db5bd")
    (rainbow-delimiters-depth-8-face     :short "rd8"              :slug "rdiix" :foreground "#80a880")
    (rainbow-delimiters-depth-9-face     :short "rd9"              :slug "rdix"  :foreground "#887070"))
  "Overriding face values.

By setting :foreground, :background, etc. a certain theme can be set for
the faces.  The face attributes here will also be used when calculating
inherited styles.

Faces here will represented more compactly when possible, by using the
:short or :slug parameter to produce a named version styles, wheras other
faces will need to be explicitly styled each time they're used."
  :type '(repeat (repeat (choice symbol string)))
  :group 'engrave-faces)

(defun engrave-faces-check-nondefault (attr value)
  "Return VALUE as long as it is specified, and not the default for ATTR."
  (unless (or (eq value (face-attribute 'default attr nil t))
              (eq value 'unspecified))
    value))

(defun engrave-faces-generate-preset ()
  "Generate `engrave-faces-preset-styles' based on the current theme."
  (mapcar
   (lambda (face-style)
     (apply #'append
            (list (car face-style)
                  :short (plist-get (cdr face-style) :short)
                  :slug (plist-get (cdr face-style) :slug))
            (delq nil
                  (mapcar
                   (lambda (attr)
                     (let ((attr-val (face-attribute (car face-style) attr nil t)))
                       (when (or (engrave-faces-check-nondefault attr attr-val)
                                 (eq (car face-style) 'default))
                         (list attr attr-val))))
                   engrave-faces-attributes-of-interest))))
   engrave-faces-preset-styles))

(provide 'engrave-faces)
;;; engrave-faces.el ends here

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: engrave-faces-latex.el --]
[-- Type: text/x-emacs-lisp, Size: 4933 bytes --]

;;; engrave-faces-latex.el --- Support for engraving buffers to LaTeX -*- lexical-binding: t; -*-

;; This file is part of engrave-faces.
;; SPDX-License-Identifier: GPL-3.0-or-later

;;; Commentary:

;; Support for engraving buffers to LaTeX.

;;; Code:

(require 'engrave-faces)

(defcustom engrave-faces-latex-output-style 'preset
  "How to encode LaTeX style information.
When nil, all face properties are applied via \\colorbox, \\textcolor,
\\textbf, etc. each time.
When preset, short commands are generated for `engrave-faces-preset-styles'."
  :type '(choice nil preset)
  :group 'engrave-faces)

(defun engrave-faces-latex-gen-preamble ()
  "Generate a preamble which provides short commands for the preset styles.
See `engrave-faces-preset-styles' and `engrave-faces-latex-output-style'."
  (mapconcat
   (lambda (face-style)
     (engrave-faces-latex-gen-preamble-line (car face-style) (cdr face-style)))
   engrave-faces-preset-styles
   "\n"))

(defun engrave-faces-latex-gen-preamble-line (face style)
  "Generate a LaTeX preamble line for STYLE representing FACE."
  (let ((short (plist-get style         :slug))
        (fg    (plist-get style         :foreground))
        (bg    (plist-get style         :background))
        (st    (plist-get style         :strike-through))
        (it    (eql (plist-get style    :slant) 'italic))
        (bl    (member (plist-get style :weight) '(bold extra-bold))))
    (concat (when fg (format "\\definecolor{EF%s}{HTML}{%s}\n" short (substring fg 1)))
            (when bg (format "\\definecolor{Ef%s}{HTML}{%s}\n" short (substring bg 1)))
            "\\newcommand{\\EF" short "}[1]{"
            (when bg (concat "\\colorbox{Ef" short "}{"))
            (when fg (concat "\\textcolor{EF" short "}{"))
            (when st "\\sout{") (when bl "\\textbf{") (when it "\\textit{")
            "#1}"
            (when bg "}") (when fg "}") (when st "}") (when bl "}") (when it "}")
            " % " (symbol-name face))))

(defun engrave-faces-latex-face-apply (faces content)
  "Convert each (compatable) parameter of FACES to a LaTeX command apllied to CONTENT."
  (let ((attrs (engrave-faces-merge-attributes faces)))
    (let ((bg (plist-get attrs         :background))
          (fg (plist-get attrs         :foreground))
          (it (eql (plist-get attrs    :slant) 'italic))
          (bl (member (plist-get attrs :weight) '(bold extra-bold)))
          (st (plist-get attrs         :strike-through)))
      (concat
       (when bg (concat "\\colorbox[HTML]{" (substring bg 1) "}{"))
       (when fg (concat "\\textcolor[HTML]{" (substring fg 1) "}{"))
       (when st "\\sout{") (when bl "\\textbf{") (when it "\\textit{")
       content
       (when bg "}") (when fg "}") (when st "}") (when bl "}") (when it "}")))))

(defun engrave-faces-latex-face-mapper (faces content)
  "Create a LaTeX representation of CONTENT With FACES applied."
  (let ((protected-content (replace-regexp-in-string "[\\{}$%&_#]" "\\\\\\&" content))
        (style (unless (eq faces 'default) (assoc faces engrave-faces-preset-styles))))
    (if (string-match-p "\\`[\n[:space:]]+\\'" content)
        protected-content
      (if (and style (eq engrave-faces-latex-output-style 'preset))
          (concat "\\EF" (plist-get (cdr style) :slug) "{" protected-content "}")
        (engrave-faces-latex-face-apply faces protected-content)))))

(defvar engrave-faces-latex-char-replacements
  '(("\\\\" . "\\\\char92{}")
    ("^" . "\\\\char94{}")
    ("~" . "\\\\char126{}")))

(defun engrave-faces-latex-post-processing ()
  (goto-char (point-min))
  (when (eq engrave-faces-latex-output-style 'preset)
    (insert "\\color[HTML]{"
            (substring (plist-get (cdr (assoc 'default engrave-faces-preset-styles))
                                  :foreground) 1)
            "}"))
  (dolist (find-sub engrave-faces-latex-char-replacements)
    (goto-char (point-min))
    (while (search-forward (car find-sub) nil t)
      (replace-match (cdr find-sub))))
  (goto-char (point-min))
  (while (re-search-forward "\n\\([[:space:]]*\\)\\(}+\\)" nil t)
    (replace-match "\\2\n\\1")))

(defun engrave-faces-latex-make-standalone ()
  "Export current buffer to a standalone LaTeX buffer."
  (goto-char (point-min))
  (insert "\\documentclass{article}

\\usepackage{xcolor}
\\usepackage{fvextra}
\\usepackage[margin=1.5cm]{geometry}
\\usepackage{sourcecodepro}
\\pagestype{empty}\n\n"
          (engrave-faces-latex-gen-preamble)
          "
\\begin{document}

\\begin{Verbatim}[breaklines=true, commandchars=\\\\\\{\\}]\n")
  (goto-char (point-max))
  (insert "\\end{Verbatim}
\\end{document}"))

;;;###autoload
(engrave-faces-define-backend "latex" ".tex" #'engrave-faces-latex-face-mapper #'engrave-faces-latex-make-standalone #'latex-mode)
(add-hook 'engrave-faces-latex-after-hook #'engrave-faces-latex-post-processing)

(provide 'engrave-faces-latex)
;;; engrave-faces-latex.el ends here

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: engrave-faces-ansi.el --]
[-- Type: text/x-emacs-lisp, Size: 6954 bytes --]

;;; engrave-faces-ansi.el --- Support for engraving buffers to LaTeX -*- lexical-binding: t; -*-

;; This file is part of engrave-faces.
;; SPDX-License-Identifier: GPL-3.0-or-later

;;; Commentary:

;; Support for engraving buffers to LaTeX.

;;; Code:

(require 'engrave-faces)

(defcustom engrave-faces-ansi-color-mode '8-bit
  "The ansi escape mode set to use.
This accepts both n-bit and m-color forms.
Possible values are:
- `3-bit'  (`8-color')
- `4-bit'  (`16-color')
- `8-bit'  (`256-color')
- `24-bit' (`16m-color')"
  :type '(choice
          (const 3-bit)
          (const 4-bit)
          (const 8-bit)
          (const 24-bit))
  :group 'engrave-faces)

(defcustom engrave-faces-ansi-use-face-colours t
  "Whether to apply face colours"
  :group 'engrave-faces)

(defvar engrave-faces-ansi-face-nesting nil)

(defun engrave-faces-ansi-code (attrs)
  "Genrerate ANSI commands which apply ATTRS to the succeeding text."
  (concat
   (when (member (plist-get attrs :weight) '(bold extra-bold)) "\uE000[1m")
   (when (eq 'italic (plist-get attrs :slant)) "\uE000[3m")
   (when (eq t (plist-get attrs :underline)) "\uE000[4m")
   (when (and engrave-faces-ansi-use-face-colours
              (plist-get attrs :foreground))
     (engrave-faces-ansi-color-to-ansi
      (plist-get attrs :foreground)))
   (when (and engrave-faces-ansi-use-face-colours
              (plist-get attrs :background))
     (engrave-faces-ansi-color-to-ansi
      (plist-get attrs :background) t))))

;;;;; Color conversion

(defun engrave-faces-ansi-color-to-ansi (color &optional background)
  (if (eq color 'unspecified) nil
    (apply (pcase engrave-faces-ansi-color-mode
             ((or '3-bit '8-color) #'engrave-faces-ansi-color-3bit-code)
             ((or '4-bit '16-color) #'engrave-faces-ansi-color-4bit-code)
             ((or '8-bit '256-color) #'engrave-faces-ansi-color-8bit-code)
             ((or '24-bit '16m-color) #'engrave-faces-ansi-color-24bit-code))
           (append (mapcar (lambda (c) (/ c 257)) (color-values color)) (list background)))))

(defun engrave-faces-ansi-color-dist-squared (reference rgb)
  "Squared L2 distance between a REFERENCE and RBG values, each a list of 3 values (r g b)."
  (+ (* (nth 0 reference)
        (nth 0 rgb))
     (* (nth 1 reference)
        (nth 1 rgb))
     (* (nth 2 reference)
        (nth 2 rgb))))

;;;;;; 3-bit / 8-color

(defun engrave-faces-ansi-color-3bit-code (r g b &optional background)
  "Convert the (R G B) colour code to a correspanding 4bit ansi escape sequence."
  (format "\uE000[%sm"
          (% (pcase (nth (engrave-faces-ansi-color-rbg-to-256 r g b)
                         engrave-faces-ansi-256-to-16-map)) 8)))

;;;;;; 4-bit / 16-color

(defvar engrave-faces-ansi-256-to-16-map
  '(0   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
        0   4  4  4 12 12  2  6  4  4 12 12  2  2  6  4
        12 12  2  2  2  6 12 12 10 10 10 10 14 12 10 10
        10 10 10 14  1  5  4  4 12 12  3  8  4  4 12 12
        2   2  6  4 12 12  2  2  2  6 12 12 10 10 10 10
        14 12 10 10 10 10 10 14  1  1  5  4 12 12  1  1
        5   4 12 12  3  3  8  4 12 12  2  2  2  6 12 12
        10 10 10 10 14 12 10 10 10 10 10 14  1  1  1  5
        12 12  1  1  1  5 12 12  1  1  1  5 12 12  3  3
        3   7 12 12 10 10 10 10 14 12 10 10 10 10 10 14
        9   9  9  9 13 12  9  9  9  9 13 12  9  9  9  9
        13 12  9  9  9  9 13 12 11 11 11 11  7 12 10 10
        10 10 10 14  9  9  9  9  9 13  9  9  9  9  9 13
        9   9  9  9  9 13  9  9  9  9  9 13  9  9  9  9
        9  13 11 11 11 11 11 15  0  0  0  0  0  0  8  8
        8   8  8  8  7  7  7  7  7  7 15 15 15 15 15 15))

(defun engrave-faces-ansi-color-4bit-code (r g b &optional background)
  "Convert the (R G B) colour code to a correspanding 4bit ansi escape sequence."
  (format "\uE000[%sm"
          (pcase (nth (engrave-faces-ansi-color-rbg-to-256 r g b)
                      engrave-faces-ansi-256-to-16-map)
            ((and (pred (> 8)) n)
             (+ 30 (if background 10 0) n))
            (n
             (format "1;%d" (+ 22 (if background 10 0) n))))))

;;;;;; 8-bit / 256-color

(defvar engrave-faces-ansi-color-6cube-values '(0 95 135 175 215 255))
(defun engrave-faces-ansi-color-to-6cube (value)
  "Map VALUE to the associated 6x6 colour cube value."
  (pcase value
    ((pred (> 48)) 0)
    ((pred (> 114)) 1)
    (_ (/ (- value 35) 40))))

(defun engrave-faces-ansi-color-8bit-code (r g b &optional background)
  "Convert the (R G B) colour code to a correspanding 8bit ansi escape sequence."
  (format (if background "\uE000[48;5;%dm" "\uE000[38;5;%dm")
          (engrave-faces-ansi-color-rbg-to-256 r g b)))

(defun engrave-faces-ansi-color-rbg-to-256 (r g b &optional background)
  "Convert the (R G B) colour code to the nearest 256-colour."
  (let ((6cube-r (engrave-faces-ansi-color-to-6cube r))
        (6cube-g (engrave-faces-ansi-color-to-6cube g))
        (6cube-b (engrave-faces-ansi-color-to-6cube b)))
    (let ((nearest-r (nth 6cube-r engrave-faces-ansi-color-6cube-values))
          (nearest-g (nth 6cube-g engrave-faces-ansi-color-6cube-values))
          (nearest-b (nth 6cube-b engrave-faces-ansi-color-6cube-values)))
      (if (and (= nearest-r r) (= nearest-g g) (= nearest-b b))
          (+ 16 (* 36 6cube-r) (* 6 6cube-g) 6cube-b)
        (let* ((grey-avg (/ (+ r g b) 3))
               (grey-index (if (> grey-avg 238) 23
                             (/ (- grey-avg 3) 10)))
               (grey (+ 8 (* 10 grey-index))))
          (if (> (engrave-faces-ansi-color-dist-squared (list grey grey grey)
                                                        (list r g b))
                 (engrave-faces-ansi-color-dist-squared (list nearest-r nearest-g nearest-b)
                                                        (list r g b)))
              (+ 232 grey-index)
            (+ 16 (* 36 6cube-r) (* 6 6cube-g) 6cube-b)))))))


;;;;;; 24-bit / 16m-color

(defun engrave-faces-ansi-color-24bit-code (r g b &optional background)
  (format (if background "\uE000[48;2;%d;%d;%dm" "\uE000[38;2;%d;%d;%dm") r g b))

;;; Applying the transformation

(defun engrave-faces-ansi-face-apply (faces content)
  "TODO record faces, and use `engrave-faces-ansi-face-nesting' to diff properties
with parent form more intelligent use of escape codes, and renewing properties which
are collateral damage from \"[0m\"."
  (let* ((face-str (engrave-faces-ansi-code (engrave-faces-merge-attributes faces))))
    (concat face-str content (if (string= face-str "") "" "\uE000[0m"))))

(defun engrave-faces-unescape-escape ()
  (goto-char (point-min))
  (while (re-search-forward "\uE000" nil t)
    (replace-match "\e")))

;;;###autoload
(engrave-faces-define-backend "ansi" ".txt" #'engrave-faces-ansi-face-apply nil
                              (lambda () (ansi-color-apply-on-region (point-min) (point-max) t)))
(add-hook 'engrave-faces-ansi-after-hook #'engrave-faces-unescape-escape)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: engrave-faces-html.el --]
[-- Type: text/x-emacs-lisp, Size: 4554 bytes --]

;;; engrave-faces-html.el --- Support for engraving buffers to HTML -*- lexical-binding: t; -*-

;; This file is part of engrave-faces.
;; SPDX-License-Identifier: GPL-3.0-or-later

;;; Commentary:

;; Support for engraving buffers to HTML.

;;; Code:

(require 'engrave-faces)

(defcustom engrave-faces-html-output-style 'preset
  "How to encode HTML style information.
When nil, all face properties are applied via inline styles.
When preset, CSS classes are generated for `engrave-faces-preset-styles'."
  :type '(choice nil preset)
  :group 'engrave-faces)

(defcustom engrave-faces-html-class-prefix "ef-"
  "Prefix to use when generating CSS class names."
  :type 'string
  :group 'engrave-faces)

(defun engrave-faces-html-gen-stylesheet (&optional indent)
  "Generate a preamble which provides short commands for the preset styles.
See `engrave-faces-preset-styles' and `engrave-faces-html-output-style'."
  (let ((stylesheet
         (mapconcat
          (lambda (face-style)
            (engrave-faces-html-gen-stylesheet-entry (car face-style) (cdr face-style)))
          engrave-faces-preset-styles
          "\n")))
    (if indent
        (mapconcat (lambda (line)
                     (concat indent line))
                   (split-string stylesheet "\n")
                   "\n")
      stylesheet)))

(defun engrave-faces-html-gen-stylesheet-entry (face style)
  "Generate a HTML preamble line for STYLE representing FACE."
  (concat "." engrave-faces-html-class-prefix (plist-get style :slug)
          " {\n  "
          (engrave-faces-html-gen-style-css style "\n  ")
          " }"))

(defun engrave-faces-html-gen-style-css (attrs seperator)
  "Compose the relevant CSS styles to apply compatible ATTRS, seperated by SEPERATOR."
  (let ((fg    (plist-get attrs      :foreground))
        (bg    (plist-get attrs      :background))
        (st    (plist-get attrs      :strike-through))
        (ul    (plist-get attrs      :underline))
        (it    (eql (plist-get attrs :slant) 'italic))
        (wt    (plist-get attrs      :weight)))
    (mapconcat
     #'identity
     (delq nil
           (list
            (when fg (format "color: %s;" fg))
            (when bg (format "background-color: %s;" bg))
            (when st "text-decoration: line-through;")
            (when ul "text-decoration: underline;")
            (when it "text-decoration: italic;")
            (when wt (format "font-weight: %s;" wt))))
     seperator)))

(defun engrave-faces-html-face-apply (faces content)
  (let ((attrs (engrave-faces-merge-attributes faces)))
    (concat "<span style=\"" (engrave-faces-html-gen-style-css attrs " ") "\">"
            content "</span>")))

(defun engrave-faces-html-protect-string (str)
  (replace-regexp-in-string
   "<" "&lt;"
   (replace-regexp-in-string
    ">" "&gt;"
    (replace-regexp-in-string
     "&" "&amp;"
     str))))

(defun engrave-faces-html-face-mapper (faces content)
  "Create a HTML representation of CONTENT With FACES applied."
  (let ((protected-content (engrave-faces-html-protect-string content))
        (style (unless (eq faces 'default) (assoc faces engrave-faces-preset-styles))))
    (if (string-match-p "\\`[\n[:space:]]+\\'" content)
        protected-content
      (if (and style (eq engrave-faces-html-output-style 'preset))
          (concat "<span class=\"" engrave-faces-html-class-prefix
                  (plist-get (cdr style) :slug) "\">"
                  protected-content "</span>")
        (engrave-faces-html-face-apply faces protected-content)))))

(defun engrave-faces-html-make-standalone ()
  "Export current buffer to a standalone LaTeX buffer."
  (goto-char (point-min))
  (insert "<!DOCTYPE html>
<html>
  <head>
    <meta charset=\"utf-8\">
    <title>"
          (engrave-faces-html-protect-string (if (buffer-file-name)
                                                 (file-name-nondirectory (buffer-file-name))
                                               (buffer-name)))
          "</title>
    <style>
      pre {
        font-size: 1rem;
        max-width: min(100rem, 100%);
        width: max-content;
        white-space: pre-wrap;
        margin: auto; }\n"
          (engrave-faces-html-gen-stylesheet "      ")
          "
    </style>
  </head>
  <body>
<pre>\n")
  (goto-char (point-max))
  (insert "
</pre>
  <body>
</html>"))

;;;###autoload
(engrave-faces-define-backend "html" ".html" #'engrave-faces-html-face-mapper #'engrave-faces-html-make-standalone #'html-mode)

(provide 'engrave-faces-html)
;;; engrave-faces-html.el ends here

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: engrave-faces.el.tex --]
[-- Type: text/x-tex, Size: 37383 bytes --]

\documentclass{article}

\usepackage{xcolor}
\usepackage{fvextra}
\usepackage[margin=1.5cm]{geometry}
\usepackage{sourcecodepro}
\pagestype{empty}

\definecolor{EFD}{HTML}{383a42}
\newcommand{\EFD}[1]{\textcolor{EFD}{#1}} % default
\definecolor{EFk}{HTML}{e45649}
\newcommand{\EFk}[1]{\textcolor{EFk}{#1}} % font-lock-keyword-face
\definecolor{EFd}{HTML}{84888b}
\newcommand{\EFd}[1]{\textcolor{EFd}{\textit{#1}}} % font-lock-doc-face
\definecolor{EFt}{HTML}{986801}
\newcommand{\EFt}[1]{\textcolor{EFt}{#1}} % font-lock-type-face
\definecolor{EFs}{HTML}{50a14f}
\newcommand{\EFs}[1]{\textcolor{EFs}{#1}} % font-lock-string-face
\definecolor{EFw}{HTML}{986801}
\newcommand{\EFw}[1]{\textcolor{EFw}{#1}} % font-lock-warning-face
\definecolor{EFb}{HTML}{a626a4}
\newcommand{\EFb}[1]{\textcolor{EFb}{#1}} % font-lock-builtin-face
\definecolor{EFct}{HTML}{9ca0a4}
\newcommand{\EFct}[1]{\textcolor{EFct}{#1}} % font-lock-comment-face
\definecolor{EFc}{HTML}{b751b6}
\newcommand{\EFc}[1]{\textcolor{EFc}{#1}} % font-lock-constant-face
\definecolor{EFpp}{HTML}{4078f2}
\newcommand{\EFpp}[1]{\textcolor{EFpp}{\textbf{#1}}} % font-lock-preprocessor-face
\definecolor{EFnc}{HTML}{4078f2}
\newcommand{\EFnc}[1]{\textcolor{EFnc}{\textbf{#1}}} % font-lock-negation-char-face
\definecolor{EFv}{HTML}{6a1868}
\newcommand{\EFv}[1]{\textcolor{EFv}{#1}} % font-lock-variable-name-face
\definecolor{EFf}{HTML}{a626a4}
\newcommand{\EFf}[1]{\textcolor{EFf}{#1}} % font-lock-function-name-face
\definecolor{EFcd}{HTML}{9ca0a4}
\newcommand{\EFcd}[1]{\textcolor{EFcd}{#1}} % font-lock-comment-delimiter-face
\definecolor{EFrc}{HTML}{4078f2}
\newcommand{\EFrc}[1]{\textcolor{EFrc}{\textbf{#1}}} % font-lock-regexp-grouping-construct
\definecolor{EFrb}{HTML}{4078f2}
\newcommand{\EFrb}[1]{\textcolor{EFrb}{\textbf{#1}}} % font-lock-regexp-grouping-backslash
\newcommand{\EFob}[1]{#1} % org-block
\definecolor{EFhn}{HTML}{da8548}
\newcommand{\EFhn}[1]{\textcolor{EFhn}{\textbf{#1}}} % highlight-numbers-number
\definecolor{EFhq}{HTML}{4078f2}
\newcommand{\EFhq}[1]{\textcolor{EFhq}{#1}} % highlight-quoted-quote
\definecolor{EFhs}{HTML}{986801}
\newcommand{\EFhs}[1]{\textcolor{EFhs}{#1}} % highlight-quoted-symbol
\definecolor{EFrdi}{HTML}{4078f2}
\newcommand{\EFrdi}[1]{\textcolor{EFrdi}{#1}} % rainbow-delimiters-depth-1-face
\definecolor{EFrdii}{HTML}{a626a4}
\newcommand{\EFrdii}[1]{\textcolor{EFrdii}{#1}} % rainbow-delimiters-depth-2-face
\definecolor{EFrdiii}{HTML}{50a14f}
\newcommand{\EFrdiii}[1]{\textcolor{EFrdiii}{#1}} % rainbow-delimiters-depth-3-face
\definecolor{EFrdiv}{HTML}{da8548}
\newcommand{\EFrdiv}[1]{\textcolor{EFrdiv}{#1}} % rainbow-delimiters-depth-4-face
\definecolor{EFrdv}{HTML}{b751b6}
\newcommand{\EFrdv}[1]{\textcolor{EFrdv}{#1}} % rainbow-delimiters-depth-5-face
\definecolor{EFrdvi}{HTML}{986801}
\newcommand{\EFrdvi}[1]{\textcolor{EFrdvi}{#1}} % rainbow-delimiters-depth-6-face
\definecolor{EFrdvii}{HTML}{4db5bd}
\newcommand{\EFrdvii}[1]{\textcolor{EFrdvii}{#1}} % rainbow-delimiters-depth-7-face
\definecolor{EFrdiix}{HTML}{80a880}
\newcommand{\EFrdiix}[1]{\textcolor{EFrdiix}{#1}} % rainbow-delimiters-depth-8-face
\definecolor{EFrdix}{HTML}{887070}
\newcommand{\EFrdix}[1]{\textcolor{EFrdix}{#1}} % rainbow-delimiters-depth-9-face
\begin{document}

\begin{Verbatim}[breaklines=true, commandchars=\\\{\}]
\color[HTML]{383a42}\EFcd{;;; }\EFct{engrave-faces.el --- Convert font-lock faces to other formats -*- lexical-binding: t; -*-}

\EFcd{;; }\EFct{Copyright (C) 2021 TEC}

\EFcd{;; }\EFct{Author: TEC <https://github/tecosaur>}
\EFcd{;; }\EFct{Maintainer: TEC <tec@tecosaur.com>}
\EFcd{;; }\EFct{Created: January 18, 2021}
\EFcd{;; }\EFct{Modified: July 10, 2021}
\EFcd{;; }\EFct{Version: 0.1.0}
\EFcd{;; }\EFct{Keywords: faces}
\EFcd{;; }\EFct{Homepage: https://github.com/tecosaur/engrave-faces}
\EFcd{;; }\EFct{Package-Requires: ((emacs "27.1"))}

\EFcd{;;; }\EFct{License:}

\EFcd{;; }\EFct{This file is part of engrave-faces, which is not part of GNU Emacs.}
\EFcd{;;}
\EFcd{;; }\EFct{engrave-faces is free software: you can redistribute it and/or modify}
\EFcd{;; }\EFct{it under the terms of the GNU General Public License as published by}
\EFcd{;; }\EFct{the Free Software Foundation, either version 3 of the License, or}
\EFcd{;; }\EFct{(at your option) any later version.}
\EFcd{;;}
\EFcd{;; }\EFct{engrave-faces is distributed in the hope that it will be useful,}
\EFcd{;; }\EFct{but WITHOUT ANY WARRANTY; without even the implied warranty of}
\EFcd{;; }\EFct{MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the}
\EFcd{;; }\EFct{GNU General Public License for more details.}
\EFcd{;;}
\EFcd{;; }\EFct{You should have received a copy of the GNU General Public License}
\EFcd{;; }\EFct{along with engrave-faces.  If not, see <https://www.gnu.org/licenses/>.}
\EFcd{;;}
\EFcd{;; }\EFct{SPDX-License-Identifier: GPL-3.0-or-later}

\EFcd{;;; }\EFct{Commentary:}

\EFcd{;;  }\EFct{Convert font-lock faces to other formats.}

\EFcd{;;; }\EFct{Code:}

\textcolor[HTML]{4078f2}{(}\EFc{require} \EFhq{'}\EFc{map}\textcolor[HTML]{4078f2}{)}

\EFcd{;;;}\EFct{\#\#\#}\textcolor[HTML]{986801}{autoload}
\textcolor[HTML]{4078f2}{(}\EFk{defvar} \EFv{engrave-faces--backends} nil\textcolor[HTML]{4078f2}{)}

\EFcd{;;;}\EFct{\#\#\#}\textcolor[HTML]{986801}{autoload}
\textcolor[HTML]{4078f2}{(}\EFk{defmacro} \EFf{engrave-faces-define-backend} \textcolor[HTML]{a626a4}{(}name extension face-transformer \EFt{\&optional} standalone-transformer view-setup\textcolor[HTML]{a626a4}{)}
  \EFhq{`}\textcolor[HTML]{a626a4}{(}\EFk{progn} \textcolor[HTML]{50a14f}{(}\EFc{add-to-list} \EFhq{'}\EFv{engrave-faces--backends}
                       \textcolor[HTML]{da8548}{(}\EFc{list} ,name \EFb{:face-transformer} ,face-transformer \EFb{:extension} ,extension\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
          \textcolor[HTML]{50a14f}{(}\EFk{defun} ,\textcolor[HTML]{da8548}{(}\EFc{intern} \textcolor[HTML]{4078f2}{(}\EFc{concat} \EFs{"engrave-faces-"} name \EFs{"-buffer"}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)} \textcolor[HTML]{da8548}{(}\EFt{\&optional} switch-to-result\textcolor[HTML]{da8548}{)}
            ,\textcolor[HTML]{da8548}{(}\EFc{concat} \EFs{"Convert buffer to "} name \EFs{" formatting."}\textcolor[HTML]{da8548}{)}
            \textcolor[HTML]{da8548}{(}\EFk{interactive} \EFhq{'}\textcolor[HTML]{4078f2}{(}t\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
            \textcolor[HTML]{da8548}{(}\EFk{let} \textcolor[HTML]{4078f2}{(}\textcolor[HTML]{a626a4}{(}buf \textcolor[HTML]{50a14f}{(}\EFf{engrave-faces-buffer} ,name\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
              \textcolor[HTML]{4078f2}{(}\EFk{when} switch-to-result
                \textcolor[HTML]{a626a4}{(}\EFc{switch-to-buffer} buf\textcolor[HTML]{a626a4}{)}
                ,\textcolor[HTML]{a626a4}{(}\EFk{when} view-setup \EFhq{`}\textcolor[HTML]{50a14f}{(}funcall ,view-setup\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
              buf\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
          ,\textcolor[HTML]{50a14f}{(}\EFk{when} standalone-transformer
             \EFhq{`}\textcolor[HTML]{da8548}{(}\EFk{defun} ,\textcolor[HTML]{4078f2}{(}\EFc{intern} \textcolor[HTML]{a626a4}{(}\EFc{concat} \EFs{"engrave-faces-"} name \EFs{"-buffer-standalone"}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\EFt{\&optional} switch-to-result\textcolor[HTML]{4078f2}{)}
                \textcolor[HTML]{4078f2}{(}\EFk{interactive} \EFhq{'}\textcolor[HTML]{a626a4}{(}t\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
                ,\textcolor[HTML]{4078f2}{(}\EFc{concat} \EFs{"Export the current buffer to a standalone "} name \EFs{" buffer."}\textcolor[HTML]{4078f2}{)}
                \textcolor[HTML]{4078f2}{(}\EFk{let} \textcolor[HTML]{a626a4}{(}\textcolor[HTML]{50a14f}{(}buf \textcolor[HTML]{da8548}{(}\EFf{engrave-faces-buffer} ,name\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
                  \textcolor[HTML]{a626a4}{(}\EFk{with-current-buffer} buf
                    \textcolor[HTML]{50a14f}{(}\EFc{funcall} ,standalone-transformer\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
                  \textcolor[HTML]{a626a4}{(}\EFk{when} switch-to-result
                    \textcolor[HTML]{50a14f}{(}\EFc{switch-to-buffer} buf\textcolor[HTML]{50a14f}{)}
                    ,\textcolor[HTML]{50a14f}{(}\EFk{when} view-setup \EFhq{`}\textcolor[HTML]{da8548}{(}funcall ,view-setup\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
                  buf\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
          \textcolor[HTML]{50a14f}{(}\EFk{defvar} ,\textcolor[HTML]{da8548}{(}\EFc{intern} \textcolor[HTML]{4078f2}{(}\EFc{concat} \EFs{"engrave-faces-"} name \EFs{"-before-hook"}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)} nil\textcolor[HTML]{50a14f}{)}
          \textcolor[HTML]{50a14f}{(}\EFk{defvar} ,\textcolor[HTML]{da8548}{(}\EFc{intern} \textcolor[HTML]{4078f2}{(}\EFc{concat} \EFs{"engrave-faces-"} name \EFs{"-after-hook"}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)} nil\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defgroup} \EFt{engrave-faces} nil
  \EFd{"Export buffers with font-lock information to other formats."}
  \EFb{:group} \EFhq{'}\EFhs{hypermedia}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defcustom} \EFv{engrave-faces-attributes-of-interest}
  \EFhq{'}\textcolor[HTML]{a626a4}{(}\EFb{:foreground} \EFb{:background} \EFb{:slant} \EFb{:weight} \EFb{:height} \EFb{:strike-through}\textcolor[HTML]{a626a4}{)}
  \EFd{"Attributes which sould be paid attention to."}
  \EFb{:type} \EFhq{'}\textcolor[HTML]{a626a4}{(}repeat symbol\textcolor[HTML]{a626a4}{)}
  \EFb{:group} \EFhq{'}\EFhs{engrave-faces}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defcustom} \EFv{engrave-faces-before-hook} nil
  \EFd{"Hook run before engraving a buffer.
The hook functions are run in the source buffer (not the resulting buffer)."}
  \EFb{:type} \EFhq{'}\EFhs{hook}
  \EFb{:group} \EFhq{'}\EFhs{engrave-faces}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defcustom} \EFv{engrave-faces-after-hook} nil
  \EFd{"Hook run after engraving a buffer.
Unlike `}\textcolor[HTML]{b751b6}{\textit{engrave-faces-before-hook}}\EFd{', these functions are run in the generated
buffer.  You may use them to modify the outlook of the final output."}
  \EFb{:type} \EFhq{'}\EFhs{hook}
  \EFb{:group} \EFhq{'}\EFhs{engrave-faces}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-buffer} \textcolor[HTML]{a626a4}{(}backend\textcolor[HTML]{a626a4}{)}
  \EFd{"Export the current buffer with BACKEND and return the created buffer."}
  \textcolor[HTML]{a626a4}{(}\EFk{save-excursion}
    \EFcd{;; }\EFct{Protect against the hook changing the current buffer.}
    \textcolor[HTML]{50a14f}{(}\EFk{save-excursion}
      \textcolor[HTML]{da8548}{(}\EFc{run-hooks} \EFhq{'}\EFv{engrave-faces-before-hook}\textcolor[HTML]{da8548}{)}
      \textcolor[HTML]{da8548}{(}\EFc{run-hooks} \textcolor[HTML]{4078f2}{(}\EFc{intern} \textcolor[HTML]{a626a4}{(}\EFc{concat} \EFs{"engrave-faces-"} backend \EFs{"-before-hook"}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    \EFcd{;; }\EFct{Convince font-lock support modes to fontify the entire buffer}
    \EFcd{;; }\EFct{in advance.}
    \textcolor[HTML]{50a14f}{(}\EFk{when} \textcolor[HTML]{da8548}{(}\EFk{and} \textcolor[HTML]{4078f2}{(}\EFc{boundp} \EFhq{'}\EFv{jit-lock-mode}\textcolor[HTML]{4078f2}{)}
               \textcolor[HTML]{4078f2}{(}\EFc{symbol-value} \EFhq{'}\EFv{jit-lock-mode}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
      \textcolor[HTML]{da8548}{(}\EFc{jit-lock-fontify-now} \textcolor[HTML]{4078f2}{(}\EFc{point-min}\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\EFc{point-max}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFc{font-lock-ensure}\textcolor[HTML]{50a14f}{)}

    \EFcd{;; }\EFct{It's important that the new buffer inherits default-directory}
    \EFcd{;; }\EFct{from the current buffer.}
    \textcolor[HTML]{50a14f}{(}\EFk{let} \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}engraved-buf \textcolor[HTML]{a626a4}{(}\EFc{generate-new-buffer} \textcolor[HTML]{50a14f}{(}\EFk{if} \textcolor[HTML]{da8548}{(}\EFv{buffer-file-name}\textcolor[HTML]{da8548}{)}
                                                 \textcolor[HTML]{da8548}{(}\EFc{concat} \textcolor[HTML]{4078f2}{(}\EFc{file-name-nondirectory} \textcolor[HTML]{a626a4}{(}\EFv{buffer-file-name}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
                                                         \textcolor[HTML]{4078f2}{(}\EFc{plist-get} \textcolor[HTML]{a626a4}{(}\EFc{cdr} \textcolor[HTML]{50a14f}{(}\EFc{assoc} backend \EFv{engrave-faces--backends}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)} \EFb{:extension}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
                                               \textcolor[HTML]{da8548}{(}\EFc{concat} \EFs{"*"} backend \EFs{"*"}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
          \textcolor[HTML]{4078f2}{(}face-transformer \textcolor[HTML]{a626a4}{(}\EFc{plist-get} \textcolor[HTML]{50a14f}{(}\EFc{cdr} \textcolor[HTML]{da8548}{(}\EFc{assoc} backend \EFv{engrave-faces--backends}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)} \EFb{:face-transformer}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
          \textcolor[HTML]{4078f2}{(}completed nil\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
      \textcolor[HTML]{da8548}{(}\EFk{unwind-protect}
          \textcolor[HTML]{4078f2}{(}\EFk{let} \textcolor[HTML]{a626a4}{(}next-change text\textcolor[HTML]{a626a4}{)}
            \EFcd{;; }\EFct{This loop traverses and reads the source buffer, appending the}
            \EFcd{;; }\EFct{resulting text to the export buffer. This method is fast because:}
            \EFcd{;; }\EFct{1) it doesn't require examining the text properties char by char}
            \EFcd{;; }\EFct{(engrave-faces-next-face-change is used to move between runs with}
            \EFcd{;; }\EFct{the same face), and 2) it doesn't require frequent buffer}
            \EFcd{;; }\EFct{switches, which are slow because they rebind all buffer-local}
            \EFcd{;; }\EFct{vars.}
            \textcolor[HTML]{a626a4}{(}\EFc{goto-char} \textcolor[HTML]{50a14f}{(}\EFc{point-min}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
            \textcolor[HTML]{a626a4}{(}\EFk{while} \textcolor[HTML]{50a14f}{(}\EFc{not} \textcolor[HTML]{da8548}{(}\EFc{eobp}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
              \textcolor[HTML]{50a14f}{(}\EFk{setq} next-change \textcolor[HTML]{da8548}{(}\EFf{engrave-faces-next-face-change} \textcolor[HTML]{4078f2}{(}\EFc{point}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
              \textcolor[HTML]{50a14f}{(}\EFk{setq} text \textcolor[HTML]{da8548}{(}\EFc{buffer-substring-no-properties} \textcolor[HTML]{4078f2}{(}\EFc{point}\textcolor[HTML]{4078f2}{)} next-change\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
              \EFcd{;; }\EFct{Don't bother writing anything if there's no text (this}
              \EFcd{;; }\EFct{happens in invisible regions).}
              \textcolor[HTML]{50a14f}{(}\EFk{when} \textcolor[HTML]{da8548}{(}\EFc{>} \textcolor[HTML]{4078f2}{(}\EFc{length} text\textcolor[HTML]{4078f2}{)} \EFhn{0}\textcolor[HTML]{da8548}{)}
                \textcolor[HTML]{da8548}{(}\EFc{princ} \textcolor[HTML]{4078f2}{(}\EFc{funcall} face-transformer
                                \textcolor[HTML]{a626a4}{(}\EFk{let} \textcolor[HTML]{50a14f}{(}\textcolor[HTML]{da8548}{(}prop \textcolor[HTML]{4078f2}{(}\EFc{get-text-property} \textcolor[HTML]{a626a4}{(}\EFc{point}\textcolor[HTML]{a626a4}{)} \EFhq{'}\EFhs{face}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
                                  \textcolor[HTML]{50a14f}{(}\EFk{cond}
                                   \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}\EFc{null} prop\textcolor[HTML]{4078f2}{)} \EFhq{'}\EFhs{default}\textcolor[HTML]{da8548}{)}
                                   \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}\EFk{and} \textcolor[HTML]{a626a4}{(}\EFc{listp} prop\textcolor[HTML]{a626a4}{)}
                                         \textcolor[HTML]{a626a4}{(}\EFc{eq} \textcolor[HTML]{50a14f}{(}\EFc{car} prop\textcolor[HTML]{50a14f}{)} \EFhq{'}\EFhs{quote}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\textcolor[HTML]{986801}{eval}\textcolor[HTML]{986801}{ prop}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
                                   \textcolor[HTML]{da8548}{(}t prop\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
                                text\textcolor[HTML]{4078f2}{)}
                       engraved-buf\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
              \textcolor[HTML]{50a14f}{(}\EFc{goto-char} next-change\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
        \textcolor[HTML]{4078f2}{(}\EFk{setq} completed t\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
      \textcolor[HTML]{da8548}{(}\EFk{if} \textcolor[HTML]{4078f2}{(}\EFc{not} completed\textcolor[HTML]{4078f2}{)}
          \textcolor[HTML]{4078f2}{(}\EFc{kill-buffer} engraved-buf\textcolor[HTML]{4078f2}{)}
        \textcolor[HTML]{4078f2}{(}\EFk{with-current-buffer} engraved-buf
          \textcolor[HTML]{a626a4}{(}\EFc{run-hooks} \EFhq{'}\EFv{engrave-faces-after-hook}\textcolor[HTML]{a626a4}{)}
          \textcolor[HTML]{a626a4}{(}\EFc{run-hooks} \textcolor[HTML]{50a14f}{(}\EFc{intern} \textcolor[HTML]{da8548}{(}\EFc{concat} \EFs{"engrave-faces-"} backend \EFs{"-after-hook"}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
        engraved-buf\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-merge-attributes} \textcolor[HTML]{a626a4}{(}faces \EFt{\&optional} attributes\textcolor[HTML]{a626a4}{)}
  \EFd{"Find the final ATTRIBUTES for text with FACES."}
  \textcolor[HTML]{a626a4}{(}\EFk{setq} faces \textcolor[HTML]{50a14f}{(}\EFf{engrave-faces-explicit-inheritance} \textcolor[HTML]{da8548}{(}\EFk{if} \textcolor[HTML]{4078f2}{(}\EFc{listp} faces\textcolor[HTML]{4078f2}{)} faces \textcolor[HTML]{4078f2}{(}\EFc{list} faces\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
  \textcolor[HTML]{a626a4}{(}\EFc{mapcan} \textcolor[HTML]{50a14f}{(}\EFk{lambda} \textcolor[HTML]{da8548}{(}attr\textcolor[HTML]{da8548}{)}
            \textcolor[HTML]{da8548}{(}\EFc{list} attr \textcolor[HTML]{4078f2}{(}\EFc{car} \textcolor[HTML]{a626a4}{(}\EFf{engrave-faces-attribute-values} faces attr\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
          \textcolor[HTML]{50a14f}{(}\EFk{or} attributes \EFv{engrave-faces-attributes-of-interest}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-explicit-inheritance} \textcolor[HTML]{a626a4}{(}faces\textcolor[HTML]{a626a4}{)}
  \EFd{"Expand :inherit for each face in FACES.
I.e. ([facea :inherit faceb] facec) results in (facea faceb facec)"}
  \textcolor[HTML]{a626a4}{(}\EFc{delq} nil
        \textcolor[HTML]{50a14f}{(}\EFc{mapcan}
         \textcolor[HTML]{da8548}{(}\EFk{lambda} \textcolor[HTML]{4078f2}{(}face\textcolor[HTML]{4078f2}{)}
           \textcolor[HTML]{4078f2}{(}\EFk{if} \textcolor[HTML]{a626a4}{(}\EFc{listp} face\textcolor[HTML]{a626a4}{)}
               \textcolor[HTML]{a626a4}{(}\EFk{let} \textcolor[HTML]{50a14f}{(}\textcolor[HTML]{da8548}{(}inherit \textcolor[HTML]{4078f2}{(}\EFc{plist-get} face \EFb{:inherit}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
                 \textcolor[HTML]{50a14f}{(}\EFc{cons} \textcolor[HTML]{da8548}{(}\EFf{map-delete} face \EFb{:inherit}\textcolor[HTML]{da8548}{)}
                       \textcolor[HTML]{da8548}{(}\EFf{engrave-faces-explicit-inheritance} inherit\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
             \textcolor[HTML]{a626a4}{(}\EFc{cons} face
                   \textcolor[HTML]{50a14f}{(}\EFk{let} \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}inherit \textcolor[HTML]{a626a4}{(}\EFc{face-attribute} face \EFb{:inherit} nil nil\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
                     \textcolor[HTML]{da8548}{(}\EFk{when} \textcolor[HTML]{4078f2}{(}\EFk{and} inherit \textcolor[HTML]{a626a4}{(}\EFc{not} \textcolor[HTML]{50a14f}{(}\EFc{eq} inherit \EFhq{'}\EFhs{unspecified}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
                       \textcolor[HTML]{4078f2}{(}\EFf{engrave-faces-explicit-inheritance} inherit\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
         \textcolor[HTML]{da8548}{(}\EFk{if} \textcolor[HTML]{4078f2}{(}\EFc{listp} faces\textcolor[HTML]{4078f2}{)} faces \textcolor[HTML]{4078f2}{(}\EFc{list} faces\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-attribute-values} \textcolor[HTML]{a626a4}{(}faces attribute\textcolor[HTML]{a626a4}{)}
  \EFd{"Fetch all specified instances of ATTRIBUTE for FACES, ignoring inheritence.
To consider inheritence, use `}\textcolor[HTML]{b751b6}{\textit{engrave-faces-explicit-inheritance}}\EFd{' first."}
  \textcolor[HTML]{a626a4}{(}\EFc{delq} nil \textcolor[HTML]{50a14f}{(}\EFc{delq} \EFhq{'}\EFhs{unspecified}
                  \textcolor[HTML]{da8548}{(}\EFc{mapcar}
                   \textcolor[HTML]{4078f2}{(}\EFk{lambda} \textcolor[HTML]{a626a4}{(}face\textcolor[HTML]{a626a4}{)}
                     \textcolor[HTML]{a626a4}{(}\EFk{or} \textcolor[HTML]{50a14f}{(}\EFc{plist-get} \textcolor[HTML]{da8548}{(}\EFc{cdr} \textcolor[HTML]{4078f2}{(}\EFc{assoc} face \EFv{engrave-faces-preset-styles}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)} attribute\textcolor[HTML]{50a14f}{)}
                         \textcolor[HTML]{50a14f}{(}\EFk{cond}
                          \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}\EFc{symbolp} face\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\EFc{face-attribute} face attribute nil nil\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
                          \textcolor[HTML]{da8548}{(}\textcolor[HTML]{4078f2}{(}\EFc{listp} face\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\EFc{plist-get} face attribute\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
                   \textcolor[HTML]{4078f2}{(}\EFc{delq} \EFhq{'}\EFhs{default} \textcolor[HTML]{a626a4}{(}\EFk{if} \textcolor[HTML]{50a14f}{(}\EFc{listp} faces\textcolor[HTML]{50a14f}{)} faces \textcolor[HTML]{50a14f}{(}\EFc{list} faces\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-next-face-change} \textcolor[HTML]{a626a4}{(}pos \EFt{\&optional} limit\textcolor[HTML]{a626a4}{)}
  \EFd{"Find the next face change from POS up to LIMIT.

This function is lifted from htmlize."}
  \EFcd{;; }\EFct{(engrave-faces-next-change pos 'face limit) would skip over entire}
  \EFcd{;; }\EFct{overlays that specify the `}\textcolor[HTML]{b751b6}{face}\EFct{' property, even when they}
  \EFcd{;; }\EFct{contain smaller text properties that also specify `}\textcolor[HTML]{b751b6}{face}\EFct{'.}
  \EFcd{;; }\EFct{Emacs display engine merges those faces, and so must we.}
  \textcolor[HTML]{a626a4}{(}\EFk{or} limit
      \textcolor[HTML]{50a14f}{(}\EFk{setq} limit \textcolor[HTML]{da8548}{(}\EFc{point-max}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
  \textcolor[HTML]{a626a4}{(}\EFk{let} \textcolor[HTML]{50a14f}{(}\textcolor[HTML]{da8548}{(}next-prop \textcolor[HTML]{4078f2}{(}\EFc{next-single-property-change} pos \EFhq{'}\EFhs{face} nil limit\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
        \textcolor[HTML]{da8548}{(}overlay-faces \textcolor[HTML]{4078f2}{(}\EFf{engrave-faces-overlay-faces-at} pos\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFk{while} \textcolor[HTML]{da8548}{(}\EFk{progn}
             \textcolor[HTML]{4078f2}{(}\EFk{setq} pos \textcolor[HTML]{a626a4}{(}\EFc{next-overlay-change} pos\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
             \textcolor[HTML]{4078f2}{(}\EFk{and} \textcolor[HTML]{a626a4}{(}\EFc{<} pos next-prop\textcolor[HTML]{a626a4}{)}
                  \textcolor[HTML]{a626a4}{(}\EFc{equal} overlay-faces \textcolor[HTML]{50a14f}{(}\EFf{engrave-faces-overlay-faces-at} pos\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFk{setq} pos \textcolor[HTML]{da8548}{(}\EFc{min} pos next-prop\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    \EFcd{;; }\EFct{Additionally, we include the entire region that specifies the}
    \EFcd{;; }\EFct{`}\textcolor[HTML]{b751b6}{display}\EFct{' property.}
    \textcolor[HTML]{50a14f}{(}\EFk{when} \textcolor[HTML]{da8548}{(}\EFc{get-char-property} pos \EFhq{'}\EFhs{display}\textcolor[HTML]{da8548}{)}
      \textcolor[HTML]{da8548}{(}\EFk{setq} pos \textcolor[HTML]{4078f2}{(}\EFc{next-single-char-property-change} pos \EFhq{'}\EFhs{display} nil limit\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    pos\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-overlay-faces-at} \textcolor[HTML]{a626a4}{(}pos\textcolor[HTML]{a626a4}{)}
  \textcolor[HTML]{a626a4}{(}\EFc{delq} nil \textcolor[HTML]{50a14f}{(}\EFc{mapcar} \textcolor[HTML]{da8548}{(}\EFk{lambda} \textcolor[HTML]{4078f2}{(}o\textcolor[HTML]{4078f2}{)} \textcolor[HTML]{4078f2}{(}\EFc{overlay-get} o \EFhq{'}\EFhs{face}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)} \textcolor[HTML]{da8548}{(}\EFc{overlays-at} pos\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\EFcd{;;; }\EFct{Style helpers}

\textcolor[HTML]{4078f2}{(}\EFk{defcustom} \EFv{engrave-faces-preset-styles} \EFcd{; }\EFct{doom-one-light}
  \EFhq{'}\textcolor[HTML]{a626a4}{(}\textcolor[HTML]{50a14f}{(}default                             \EFb{:short} \EFs{"default"}          \EFb{:slug} \EFs{"D"}     \EFb{:foreground} \EFs{"\#383a42"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-keyword-face}              \EFb{:short} \EFs{"keyword"}          \EFb{:slug} \EFs{"k"}     \EFb{:foreground} \EFs{"\#e45649"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-doc-face}                  \EFb{:short} \EFs{"doc"}              \EFb{:slug} \EFs{"d"}     \EFb{:foreground} \EFs{"\#84888b"} \EFb{:slant} italic\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-type-face}                 \EFb{:short} \EFs{"type"}             \EFb{:slug} \EFs{"t"}     \EFb{:foreground} \EFs{"\#986801"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-string-face}               \EFb{:short} \EFs{"string"}           \EFb{:slug} \EFs{"s"}     \EFb{:foreground} \EFs{"\#50a14f"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-warning-face}              \EFb{:short} \EFs{"warning"}          \EFb{:slug} \EFs{"w"}     \EFb{:foreground} \EFs{"\#986801"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-builtin-face}              \EFb{:short} \EFs{"builtin"}          \EFb{:slug} \EFs{"b"}     \EFb{:foreground} \EFs{"\#a626a4"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-comment-face}              \EFb{:short} \EFs{"comment"}          \EFb{:slug} \EFs{"ct"}    \EFb{:foreground} \EFs{"\#9ca0a4"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-constant-face}             \EFb{:short} \EFs{"constant"}         \EFb{:slug} \EFs{"c"}     \EFb{:foreground} \EFs{"\#b751b6"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-preprocessor-face}         \EFb{:short} \EFs{"preprocessor"}     \EFb{:slug} \EFs{"pp"}    \EFb{:foreground} \EFs{"\#4078f2"} \EFb{:weight} bold\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-negation-char-face}        \EFb{:short} \EFs{"neg-char"}         \EFb{:slug} \EFs{"nc"}    \EFb{:foreground} \EFs{"\#4078f2"} \EFb{:weight} bold\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-variable-name-face}        \EFb{:short} \EFs{"variable"}         \EFb{:slug} \EFs{"v"}     \EFb{:foreground} \EFs{"\#6a1868"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-function-name-face}        \EFb{:short} \EFs{"function"}         \EFb{:slug} \EFs{"f"}     \EFb{:foreground} \EFs{"\#a626a4"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFv{font-lock-comment-delimiter-face}    \EFb{:short} \EFs{"comment-delim"}    \EFb{:slug} \EFs{"cd"}    \EFb{:foreground} \EFs{"\#9ca0a4"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}font-lock-regexp-grouping-construct \EFb{:short} \EFs{"regexp"}           \EFb{:slug} \EFs{"rc"}    \EFb{:foreground} \EFs{"\#4078f2"} \EFb{:weight} bold\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}font-lock-regexp-grouping-backslash \EFb{:short} \EFs{"regexp-backslash"} \EFb{:slug} \EFs{"rb"}    \EFb{:foreground} \EFs{"\#4078f2"} \EFb{:weight} bold\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}\EFc{org-block}                           \EFb{:short} \EFs{"org-block"}        \EFb{:slug} \EFs{"ob"}\textcolor[HTML]{50a14f}{)} \EFcd{; }\EFct{forcing no background is preferable}
    \textcolor[HTML]{50a14f}{(}highlight-numbers-number            \EFb{:short} \EFs{"number"}           \EFb{:slug} \EFs{"hn"}    \EFb{:foreground} \EFs{"\#da8548"} \EFb{:weight} bold\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}highlight-quoted-quote              \EFb{:short} \EFs{"qquote"}           \EFb{:slug} \EFs{"hq"}    \EFb{:foreground} \EFs{"\#4078f2"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}highlight-quoted-symbol             \EFb{:short} \EFs{"qsymbol"}          \EFb{:slug} \EFs{"hs"}    \EFb{:foreground} \EFs{"\#986801"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-1-face     \EFb{:short} \EFs{"rd1"}              \EFb{:slug} \EFs{"rdi"}   \EFb{:foreground} \EFs{"\#4078f2"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-2-face     \EFb{:short} \EFs{"rd2"}              \EFb{:slug} \EFs{"rdii"}  \EFb{:foreground} \EFs{"\#a626a4"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-3-face     \EFb{:short} \EFs{"rd3"}              \EFb{:slug} \EFs{"rdiii"} \EFb{:foreground} \EFs{"\#50a14f"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-4-face     \EFb{:short} \EFs{"rd4"}              \EFb{:slug} \EFs{"rdiv"}  \EFb{:foreground} \EFs{"\#da8548"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-5-face     \EFb{:short} \EFs{"rd5"}              \EFb{:slug} \EFs{"rdv"}   \EFb{:foreground} \EFs{"\#b751b6"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-6-face     \EFb{:short} \EFs{"rd6"}              \EFb{:slug} \EFs{"rdvi"}  \EFb{:foreground} \EFs{"\#986801"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-7-face     \EFb{:short} \EFs{"rd7"}              \EFb{:slug} \EFs{"rdvii"} \EFb{:foreground} \EFs{"\#4db5bd"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-8-face     \EFb{:short} \EFs{"rd8"}              \EFb{:slug} \EFs{"rdiix"} \EFb{:foreground} \EFs{"\#80a880"}\textcolor[HTML]{50a14f}{)}
    \textcolor[HTML]{50a14f}{(}rainbow-delimiters-depth-9-face     \EFb{:short} \EFs{"rd9"}              \EFb{:slug} \EFs{"rdix"}  \EFb{:foreground} \EFs{"\#887070"}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
  \EFd{"Overriding face values.

By setting :foreground, :background, etc. a certain theme can be set for
the faces.  The face attributes here will also be used when calculating
inherited styles.

Faces here will represented more compactly when possible, by using the
:short or :slug parameter to produce a named version styles, wheras other
faces will need to be explicitly styled each time they're used."}
  \EFb{:type} \EFhq{'}\textcolor[HTML]{a626a4}{(}repeat \textcolor[HTML]{50a14f}{(}\EFf{repeat} \textcolor[HTML]{da8548}{(}choice symbol string\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
  \EFb{:group} \EFhq{'}\EFhs{engrave-faces}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-check-nondefault} \textcolor[HTML]{a626a4}{(}attr value\textcolor[HTML]{a626a4}{)}
  \EFd{"Return VALUE as long as it is specified, and not the default for ATTR."}
  \textcolor[HTML]{a626a4}{(}\EFk{unless} \textcolor[HTML]{50a14f}{(}\EFk{or} \textcolor[HTML]{da8548}{(}\EFc{eq} value \textcolor[HTML]{4078f2}{(}\EFc{face-attribute} \EFhq{'}\EFhs{default} attr nil t\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}
              \textcolor[HTML]{da8548}{(}\EFc{eq} value \EFhq{'}\EFhs{unspecified}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
    value\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFk{defun} \EFf{engrave-faces-generate-preset} \textcolor[HTML]{a626a4}{(}\textcolor[HTML]{a626a4}{)}
  \EFd{"Generate `}\textcolor[HTML]{b751b6}{\textit{engrave-faces-preset-styles}}\EFd{' based on the current theme."}
  \textcolor[HTML]{a626a4}{(}\EFc{mapcar}
   \textcolor[HTML]{50a14f}{(}\EFk{lambda} \textcolor[HTML]{da8548}{(}face-style\textcolor[HTML]{da8548}{)}
     \textcolor[HTML]{da8548}{(}\EFc{apply} \EFhq{\#'}\EFhs{append}
            \textcolor[HTML]{4078f2}{(}\EFc{list} \textcolor[HTML]{a626a4}{(}\EFc{car} face-style\textcolor[HTML]{a626a4}{)}
                  \EFb{:short} \textcolor[HTML]{a626a4}{(}\EFc{plist-get} \textcolor[HTML]{50a14f}{(}\EFc{cdr} face-style\textcolor[HTML]{50a14f}{)} \EFb{:short}\textcolor[HTML]{a626a4}{)}
                  \EFb{:slug} \textcolor[HTML]{a626a4}{(}\EFc{plist-get} \textcolor[HTML]{50a14f}{(}\EFc{cdr} face-style\textcolor[HTML]{50a14f}{)} \EFb{:slug}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
            \textcolor[HTML]{4078f2}{(}\EFc{delq} nil
                  \textcolor[HTML]{a626a4}{(}\EFc{mapcar}
                   \textcolor[HTML]{50a14f}{(}\EFk{lambda} \textcolor[HTML]{da8548}{(}attr\textcolor[HTML]{da8548}{)}
                     \textcolor[HTML]{da8548}{(}\EFk{let} \textcolor[HTML]{4078f2}{(}\textcolor[HTML]{a626a4}{(}attr-val \textcolor[HTML]{50a14f}{(}\EFc{face-attribute} \textcolor[HTML]{da8548}{(}\EFc{car} face-style\textcolor[HTML]{da8548}{)} attr nil t\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}
                       \textcolor[HTML]{4078f2}{(}\EFk{when} \textcolor[HTML]{a626a4}{(}\EFk{or} \textcolor[HTML]{50a14f}{(}\EFf{engrave-faces-check-nondefault} attr attr-val\textcolor[HTML]{50a14f}{)}
                                 \textcolor[HTML]{50a14f}{(}\EFc{eq} \textcolor[HTML]{da8548}{(}\EFc{car} face-style\textcolor[HTML]{da8548}{)} \EFhq{'}\EFhs{default}\textcolor[HTML]{50a14f}{)}\textcolor[HTML]{a626a4}{)}
                         \textcolor[HTML]{a626a4}{(}\EFc{list} attr attr-val\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
                   \EFv{engrave-faces-attributes-of-interest}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}\textcolor[HTML]{da8548}{)}\textcolor[HTML]{50a14f}{)}
   \EFv{engrave-faces-preset-styles}\textcolor[HTML]{a626a4}{)}\textcolor[HTML]{4078f2}{)}

\textcolor[HTML]{4078f2}{(}\EFc{provide} \EFhq{'}\EFc{engrave-faces}\textcolor[HTML]{4078f2}{)}
\EFcd{;;; }\EFct{engrave-faces.el ends here}
\end{Verbatim}
\end{document}

[-- Attachment #7: engrave-faces.el.txt --]
[-- Type: text/plain, Size: 32815 bytes --]

^[[38;5;145m;;; ^[[0m^[[38;5;145mengrave-faces.el --- Convert font-lock faces to other formats -*- lexical-binding: t; -*-
^[[0m
^[[38;5;145m;; ^[[0m^[[38;5;145mCopyright (C) 2021 TEC
^[[0m
^[[38;5;145m;; ^[[0m^[[38;5;145mAuthor: TEC <https://github/tecosaur>
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mMaintainer: TEC <tec@tecosaur.com>
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mCreated: January 18, 2021
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mModified: July 10, 2021
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mVersion: 0.1.0
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mKeywords: faces
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mHomepage: https://github.com/tecosaur/engrave-faces
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mPackage-Requires: ((emacs "27.1"))
^[[0m
^[[38;5;145m;;; ^[[0m^[[38;5;145mLicense:
^[[0m
^[[38;5;145m;; ^[[0m^[[38;5;145mThis file is part of engrave-faces, which is not part of GNU Emacs.
^[[0m^[[38;5;145m;;^[[0m^[[38;5;145m
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mengrave-faces is free software: you can redistribute it and/or modify
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mit under the terms of the GNU General Public License as published by
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mthe Free Software Foundation, either version 3 of the License, or
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145m(at your option) any later version.
^[[0m^[[38;5;145m;;^[[0m^[[38;5;145m
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mengrave-faces is distributed in the hope that it will be useful,
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mbut WITHOUT ANY WARRANTY; without even the implied warranty of
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mGNU General Public License for more details.
^[[0m^[[38;5;145m;;^[[0m^[[38;5;145m
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mYou should have received a copy of the GNU General Public License
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145malong with engrave-faces.  If not, see <https://www.gnu.org/licenses/>.
^[[0m^[[38;5;145m;;^[[0m^[[38;5;145m
^[[0m^[[38;5;145m;; ^[[0m^[[38;5;145mSPDX-License-Identifier: GPL-3.0-or-later
^[[0m
^[[38;5;145m;;; ^[[0m^[[38;5;145mCommentary:
^[[0m
^[[38;5;145m;;  ^[[0m^[[38;5;145mConvert font-lock faces to other formats.
^[[0m
^[[38;5;145m;;; ^[[0m^[[38;5;145mCode:
^[[0m
^[[38;5;69m(^[[0m^[[38;5;133mrequire^[[0m ^[[38;5;69m'^[[0m^[[38;5;133mmap^[[0m^[[38;5;69m)^[[0m

^[[38;5;145m;;;^[[0m^[[38;5;145m###^[[0m^[[38;5;94mautoload^[[0m^[[38;5;145m
^[[0m^[[38;5;69m(^[[0m^[[38;5;167mdefvar^[[0m ^[[38;5;53mengrave-faces--backends^[[0m nil^[[38;5;69m)^[[0m

^[[38;5;145m;;;^[[0m^[[38;5;145m###^[[0m^[[38;5;94mautoload^[[0m^[[38;5;145m
^[[0m^[[38;5;69m(^[[0m^[[38;5;167mdefmacro^[[0m ^[[38;5;127mengrave-faces-define-backend^[[0m ^[[38;5;127m(^[[0mname extension face-transformer ^[[38;5;94m&optional^[[0m standalone-transformer view-setup^[[38;5;127m)^[[0m
  ^[[38;5;69m`^[[0m^[[38;5;127m(^[[0m^[[38;5;167mprogn^[[0m ^[[38;5;71m(^[[0m^[[38;5;133madd-to-list^[[0m ^[[38;5;69m'^[[0m^[[38;5;53mengrave-faces--backends^[[0m
                       ^[[38;5;173m(^[[0m^[[38;5;133mlist^[[0m ,name ^[[38;5;127m:face-transformer^[[0m ,face-transformer ^[[38;5;127m:extension^[[0m ,extension^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
          ^[[38;5;71m(^[[0m^[[38;5;167mdefun^[[0m ,^[[38;5;173m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m name ^[[38;5;71m"-buffer"^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m ^[[38;5;173m(^[[0m^[[38;5;94m&optional^[[0m switch-to-result^[[38;5;173m)^[[0m
            ,^[[38;5;173m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"Convert buffer to "^[[0m name ^[[38;5;71m" formatting."^[[0m^[[38;5;173m)^[[0m
            ^[[38;5;173m(^[[0m^[[38;5;167minteractive^[[0m ^[[38;5;69m'^[[0m^[[38;5;69m(^[[0mt^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
            ^[[38;5;173m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;69m(^[[0m^[[38;5;127m(^[[0mbuf ^[[38;5;71m(^[[0m^[[38;5;127mengrave-faces-buffer^[[0m ,name^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
              ^[[38;5;69m(^[[0m^[[38;5;167mwhen^[[0m switch-to-result
                ^[[38;5;127m(^[[0m^[[38;5;133mswitch-to-buffer^[[0m buf^[[38;5;127m)^[[0m
                ,^[[38;5;127m(^[[0m^[[38;5;167mwhen^[[0m view-setup ^[[38;5;69m`^[[0m^[[38;5;71m(^[[0mfuncall ,view-setup^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
              buf^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
          ,^[[38;5;71m(^[[0m^[[38;5;167mwhen^[[0m standalone-transformer
             ^[[38;5;69m`^[[0m^[[38;5;173m(^[[0m^[[38;5;167mdefun^[[0m ,^[[38;5;69m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m name ^[[38;5;71m"-buffer-standalone"^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;94m&optional^[[0m switch-to-result^[[38;5;69m)^[[0m
                ^[[38;5;69m(^[[0m^[[38;5;167minteractive^[[0m ^[[38;5;69m'^[[0m^[[38;5;127m(^[[0mt^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
                ,^[[38;5;69m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"Export the current buffer to a standalone "^[[0m name ^[[38;5;71m" buffer."^[[0m^[[38;5;69m)^[[0m
                ^[[38;5;69m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;127m(^[[0m^[[38;5;71m(^[[0mbuf ^[[38;5;173m(^[[0m^[[38;5;127mengrave-faces-buffer^[[0m ,name^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
                  ^[[38;5;127m(^[[0m^[[38;5;167mwith-current-buffer^[[0m buf
                    ^[[38;5;71m(^[[0m^[[38;5;133mfuncall^[[0m ,standalone-transformer^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
                  ^[[38;5;127m(^[[0m^[[38;5;167mwhen^[[0m switch-to-result
                    ^[[38;5;71m(^[[0m^[[38;5;133mswitch-to-buffer^[[0m buf^[[38;5;71m)^[[0m
                    ,^[[38;5;71m(^[[0m^[[38;5;167mwhen^[[0m view-setup ^[[38;5;69m`^[[0m^[[38;5;173m(^[[0mfuncall ,view-setup^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
                  buf^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
          ^[[38;5;71m(^[[0m^[[38;5;167mdefvar^[[0m ,^[[38;5;173m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m name ^[[38;5;71m"-before-hook"^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m nil^[[38;5;71m)^[[0m
          ^[[38;5;71m(^[[0m^[[38;5;167mdefvar^[[0m ,^[[38;5;173m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m name ^[[38;5;71m"-after-hook"^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m nil^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefgroup^[[0m ^[[38;5;94mengrave-faces^[[0m nil
  ^[[3m^[[38;5;245m"Export buffers with font-lock information to other formats."^[[0m
  ^[[38;5;127m:group^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mhypermedia^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefcustom^[[0m ^[[38;5;53mengrave-faces-attributes-of-interest^[[0m
  ^[[38;5;69m'^[[0m^[[38;5;127m(^[[0m^[[38;5;127m:foreground^[[0m ^[[38;5;127m:background^[[0m ^[[38;5;127m:slant^[[0m ^[[38;5;127m:weight^[[0m ^[[38;5;127m:height^[[0m ^[[38;5;127m:strike-through^[[0m^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Attributes which sould be paid attention to."^[[0m
  ^[[38;5;127m:type^[[0m ^[[38;5;69m'^[[0m^[[38;5;127m(^[[0mrepeat symbol^[[38;5;127m)^[[0m
  ^[[38;5;127m:group^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mengrave-faces^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefcustom^[[0m ^[[38;5;53mengrave-faces-before-hook^[[0m nil
  ^[[3m^[[38;5;245m"Hook run before engraving a buffer.
The hook functions are run in the source buffer (not the resulting buffer)."^[[0m
  ^[[38;5;127m:type^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mhook^[[0m
  ^[[38;5;127m:group^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mengrave-faces^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefcustom^[[0m ^[[38;5;53mengrave-faces-after-hook^[[0m nil
  ^[[3m^[[38;5;245m"Hook run after engraving a buffer.
Unlike `^[[0m^[[3m^[[38;5;133mengrave-faces-before-hook^[[0m^[[3m^[[38;5;245m', these functions are run in the generated
buffer.  You may use them to modify the outlook of the final output."^[[0m
  ^[[38;5;127m:type^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mhook^[[0m
  ^[[38;5;127m:group^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mengrave-faces^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-buffer^[[0m ^[[38;5;127m(^[[0mbackend^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Export the current buffer with BACKEND and return the created buffer."^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;167msave-excursion^[[0m
    ^[[38;5;145m;; ^[[0m^[[38;5;145mProtect against the hook changing the current buffer.
^[[0m    ^[[38;5;71m(^[[0m^[[38;5;167msave-excursion^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;133mrun-hooks^[[0m ^[[38;5;69m'^[[0m^[[38;5;53mengrave-faces-before-hook^[[0m^[[38;5;173m)^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;133mrun-hooks^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m backend ^[[38;5;71m"-before-hook"^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;145m;; ^[[0m^[[38;5;145mConvince font-lock support modes to fontify the entire buffer
^[[0m    ^[[38;5;145m;; ^[[0m^[[38;5;145min advance.
^[[0m    ^[[38;5;71m(^[[0m^[[38;5;167mwhen^[[0m ^[[38;5;173m(^[[0m^[[38;5;167mand^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mboundp^[[0m ^[[38;5;69m'^[[0m^[[38;5;53mjit-lock-mode^[[0m^[[38;5;69m)^[[0m
               ^[[38;5;69m(^[[0m^[[38;5;133msymbol-value^[[0m ^[[38;5;69m'^[[0m^[[38;5;53mjit-lock-mode^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;133mjit-lock-fontify-now^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mpoint-min^[[0m^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mpoint-max^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;133mfont-lock-ensure^[[0m^[[38;5;71m)^[[0m

    ^[[38;5;145m;; ^[[0m^[[38;5;145mIt's important that the new buffer inherits default-directory
^[[0m    ^[[38;5;145m;; ^[[0m^[[38;5;145mfrom the current buffer.
^[[0m    ^[[38;5;71m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0mengraved-buf ^[[38;5;127m(^[[0m^[[38;5;133mgenerate-new-buffer^[[0m ^[[38;5;71m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;173m(^[[0m^[[38;5;53mbuffer-file-name^[[0m^[[38;5;173m)^[[0m
                                                 ^[[38;5;173m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mfile-name-nondirectory^[[0m ^[[38;5;127m(^[[0m^[[38;5;53mbuffer-file-name^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
                                                         ^[[38;5;69m(^[[0m^[[38;5;133mplist-get^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mcdr^[[0m ^[[38;5;71m(^[[0m^[[38;5;133massoc^[[0m backend ^[[38;5;53mengrave-faces--backends^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m ^[[38;5;127m:extension^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
                                               ^[[38;5;173m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"*"^[[0m backend ^[[38;5;71m"*"^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
          ^[[38;5;69m(^[[0mface-transformer ^[[38;5;127m(^[[0m^[[38;5;133mplist-get^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mcdr^[[0m ^[[38;5;173m(^[[0m^[[38;5;133massoc^[[0m backend ^[[38;5;53mengrave-faces--backends^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m ^[[38;5;127m:face-transformer^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
          ^[[38;5;69m(^[[0mcompleted nil^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;167munwind-protect^[[0m
          ^[[38;5;69m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;127m(^[[0mnext-change text^[[38;5;127m)^[[0m
            ^[[38;5;145m;; ^[[0m^[[38;5;145mThis loop traverses and reads the source buffer, appending the
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145mresulting text to the export buffer. This method is fast because:
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145m1) it doesn't require examining the text properties char by char
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145m(engrave-faces-next-face-change is used to move between runs with
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145mthe same face), and 2) it doesn't require frequent buffer
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145mswitches, which are slow because they rebind all buffer-local
^[[0m            ^[[38;5;145m;; ^[[0m^[[38;5;145mvars.
^[[0m            ^[[38;5;127m(^[[0m^[[38;5;133mgoto-char^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mpoint-min^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
            ^[[38;5;127m(^[[0m^[[38;5;167mwhile^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mnot^[[0m ^[[38;5;173m(^[[0m^[[38;5;133meobp^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
              ^[[38;5;71m(^[[0m^[[38;5;167msetq^[[0m next-change ^[[38;5;173m(^[[0m^[[38;5;127mengrave-faces-next-face-change^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mpoint^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
              ^[[38;5;71m(^[[0m^[[38;5;167msetq^[[0m text ^[[38;5;173m(^[[0m^[[38;5;133mbuffer-substring-no-properties^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mpoint^[[0m^[[38;5;69m)^[[0m next-change^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
              ^[[38;5;145m;; ^[[0m^[[38;5;145mDon't bother writing anything if there's no text (this
^[[0m              ^[[38;5;145m;; ^[[0m^[[38;5;145mhappens in invisible regions).
^[[0m              ^[[38;5;71m(^[[0m^[[38;5;167mwhen^[[0m ^[[38;5;173m(^[[0m^[[38;5;133m>^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mlength^[[0m text^[[38;5;69m)^[[0m ^[[1m^[[38;5;173m0^[[0m^[[38;5;173m)^[[0m
                ^[[38;5;173m(^[[0m^[[38;5;133mprinc^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mfuncall^[[0m face-transformer
                                ^[[38;5;127m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;71m(^[[0m^[[38;5;173m(^[[0mprop ^[[38;5;69m(^[[0m^[[38;5;133mget-text-property^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mpoint^[[0m^[[38;5;127m)^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mface^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
                                  ^[[38;5;71m(^[[0m^[[38;5;167mcond^[[0m
                                   ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0m^[[38;5;133mnull^[[0m prop^[[38;5;69m)^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mdefault^[[0m^[[38;5;173m)^[[0m
                                   ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0m^[[38;5;167mand^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mlistp^[[0m prop^[[38;5;127m)^[[0m
                                         ^[[38;5;127m(^[[0m^[[38;5;133meq^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mcar^[[0m prop^[[38;5;71m)^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mquote^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;94meval^[[0m^[[38;5;94m prop^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
                                   ^[[38;5;173m(^[[0mt prop^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
                                text^[[38;5;69m)^[[0m
                       engraved-buf^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
              ^[[38;5;71m(^[[0m^[[38;5;133mgoto-char^[[0m next-change^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
        ^[[38;5;69m(^[[0m^[[38;5;167msetq^[[0m completed t^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mnot^[[0m completed^[[38;5;69m)^[[0m
          ^[[38;5;69m(^[[0m^[[38;5;133mkill-buffer^[[0m engraved-buf^[[38;5;69m)^[[0m
        ^[[38;5;69m(^[[0m^[[38;5;167mwith-current-buffer^[[0m engraved-buf
          ^[[38;5;127m(^[[0m^[[38;5;133mrun-hooks^[[0m ^[[38;5;69m'^[[0m^[[38;5;53mengrave-faces-after-hook^[[0m^[[38;5;127m)^[[0m
          ^[[38;5;127m(^[[0m^[[38;5;133mrun-hooks^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mintern^[[0m ^[[38;5;173m(^[[0m^[[38;5;133mconcat^[[0m ^[[38;5;71m"engrave-faces-"^[[0m backend ^[[38;5;71m"-after-hook"^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
        engraved-buf^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-merge-attributes^[[0m ^[[38;5;127m(^[[0mfaces ^[[38;5;94m&optional^[[0m attributes^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Find the final ATTRIBUTES for text with FACES."^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;167msetq^[[0m faces ^[[38;5;71m(^[[0m^[[38;5;127mengrave-faces-explicit-inheritance^[[0m ^[[38;5;173m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mlistp^[[0m faces^[[38;5;69m)^[[0m faces ^[[38;5;69m(^[[0m^[[38;5;133mlist^[[0m faces^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;133mmapcan^[[0m ^[[38;5;71m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;173m(^[[0mattr^[[38;5;173m)^[[0m
            ^[[38;5;173m(^[[0m^[[38;5;133mlist^[[0m attr ^[[38;5;69m(^[[0m^[[38;5;133mcar^[[0m ^[[38;5;127m(^[[0m^[[38;5;127mengrave-faces-attribute-values^[[0m faces attr^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
          ^[[38;5;71m(^[[0m^[[38;5;167mor^[[0m attributes ^[[38;5;53mengrave-faces-attributes-of-interest^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-explicit-inheritance^[[0m ^[[38;5;127m(^[[0mfaces^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Expand :inherit for each face in FACES.
I.e. ([facea :inherit faceb] facec) results in (facea faceb facec)"^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;133mdelq^[[0m nil
        ^[[38;5;71m(^[[0m^[[38;5;133mmapcan^[[0m
         ^[[38;5;173m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;69m(^[[0mface^[[38;5;69m)^[[0m
           ^[[38;5;69m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mlistp^[[0m face^[[38;5;127m)^[[0m
               ^[[38;5;127m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;71m(^[[0m^[[38;5;173m(^[[0minherit ^[[38;5;69m(^[[0m^[[38;5;133mplist-get^[[0m face ^[[38;5;127m:inherit^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
                 ^[[38;5;71m(^[[0m^[[38;5;133mcons^[[0m ^[[38;5;173m(^[[0m^[[38;5;127mmap-delete^[[0m face ^[[38;5;127m:inherit^[[0m^[[38;5;173m)^[[0m
                       ^[[38;5;173m(^[[0m^[[38;5;127mengrave-faces-explicit-inheritance^[[0m inherit^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
             ^[[38;5;127m(^[[0m^[[38;5;133mcons^[[0m face
                   ^[[38;5;71m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0minherit ^[[38;5;127m(^[[0m^[[38;5;133mface-attribute^[[0m face ^[[38;5;127m:inherit^[[0m nil nil^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
                     ^[[38;5;173m(^[[0m^[[38;5;167mwhen^[[0m ^[[38;5;69m(^[[0m^[[38;5;167mand^[[0m inherit ^[[38;5;127m(^[[0m^[[38;5;133mnot^[[0m ^[[38;5;71m(^[[0m^[[38;5;133meq^[[0m inherit ^[[38;5;69m'^[[0m^[[38;5;94munspecified^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
                       ^[[38;5;69m(^[[0m^[[38;5;127mengrave-faces-explicit-inheritance^[[0m inherit^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
         ^[[38;5;173m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mlistp^[[0m faces^[[38;5;69m)^[[0m faces ^[[38;5;69m(^[[0m^[[38;5;133mlist^[[0m faces^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-attribute-values^[[0m ^[[38;5;127m(^[[0mfaces attribute^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Fetch all specified instances of ATTRIBUTE for FACES, ignoring inheritence.
To consider inheritence, use `^[[0m^[[3m^[[38;5;133mengrave-faces-explicit-inheritance^[[0m^[[3m^[[38;5;245m' first."^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;133mdelq^[[0m nil ^[[38;5;71m(^[[0m^[[38;5;133mdelq^[[0m ^[[38;5;69m'^[[0m^[[38;5;94munspecified^[[0m
                  ^[[38;5;173m(^[[0m^[[38;5;133mmapcar^[[0m
                   ^[[38;5;69m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;127m(^[[0mface^[[38;5;127m)^[[0m
                     ^[[38;5;127m(^[[0m^[[38;5;167mor^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mplist-get^[[0m ^[[38;5;173m(^[[0m^[[38;5;133mcdr^[[0m ^[[38;5;69m(^[[0m^[[38;5;133massoc^[[0m face ^[[38;5;53mengrave-faces-preset-styles^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m attribute^[[38;5;71m)^[[0m
                         ^[[38;5;71m(^[[0m^[[38;5;167mcond^[[0m
                          ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0m^[[38;5;133msymbolp^[[0m face^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mface-attribute^[[0m face attribute nil nil^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
                          ^[[38;5;173m(^[[0m^[[38;5;69m(^[[0m^[[38;5;133mlistp^[[0m face^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;133mplist-get^[[0m face attribute^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
                   ^[[38;5;69m(^[[0m^[[38;5;133mdelq^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mdefault^[[0m ^[[38;5;127m(^[[0m^[[38;5;167mif^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mlistp^[[0m faces^[[38;5;71m)^[[0m faces ^[[38;5;71m(^[[0m^[[38;5;133mlist^[[0m faces^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-next-face-change^[[0m ^[[38;5;127m(^[[0mpos ^[[38;5;94m&optional^[[0m limit^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Find the next face change from POS up to LIMIT.

This function is lifted from htmlize."^[[0m
  ^[[38;5;145m;; ^[[0m^[[38;5;145m(engrave-faces-next-change pos 'face limit) would skip over entire
^[[0m  ^[[38;5;145m;; ^[[0m^[[38;5;145moverlays that specify the `^[[0m^[[38;5;133mface^[[0m^[[38;5;145m' property, even when they
^[[0m  ^[[38;5;145m;; ^[[0m^[[38;5;145mcontain smaller text properties that also specify `^[[0m^[[38;5;133mface^[[0m^[[38;5;145m'.
^[[0m  ^[[38;5;145m;; ^[[0m^[[38;5;145mEmacs display engine merges those faces, and so must we.
^[[0m  ^[[38;5;127m(^[[0m^[[38;5;167mor^[[0m limit
      ^[[38;5;71m(^[[0m^[[38;5;167msetq^[[0m limit ^[[38;5;173m(^[[0m^[[38;5;133mpoint-max^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;71m(^[[0m^[[38;5;173m(^[[0mnext-prop ^[[38;5;69m(^[[0m^[[38;5;133mnext-single-property-change^[[0m pos ^[[38;5;69m'^[[0m^[[38;5;94mface^[[0m nil limit^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
        ^[[38;5;173m(^[[0moverlay-faces ^[[38;5;69m(^[[0m^[[38;5;127mengrave-faces-overlay-faces-at^[[0m pos^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;167mwhile^[[0m ^[[38;5;173m(^[[0m^[[38;5;167mprogn^[[0m
             ^[[38;5;69m(^[[0m^[[38;5;167msetq^[[0m pos ^[[38;5;127m(^[[0m^[[38;5;133mnext-overlay-change^[[0m pos^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
             ^[[38;5;69m(^[[0m^[[38;5;167mand^[[0m ^[[38;5;127m(^[[0m^[[38;5;133m<^[[0m pos next-prop^[[38;5;127m)^[[0m
                  ^[[38;5;127m(^[[0m^[[38;5;133mequal^[[0m overlay-faces ^[[38;5;71m(^[[0m^[[38;5;127mengrave-faces-overlay-faces-at^[[0m pos^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;167msetq^[[0m pos ^[[38;5;173m(^[[0m^[[38;5;133mmin^[[0m pos next-prop^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;145m;; ^[[0m^[[38;5;145mAdditionally, we include the entire region that specifies the
^[[0m    ^[[38;5;145m;; ^[[0m^[[38;5;145m`^[[0m^[[38;5;133mdisplay^[[0m^[[38;5;145m' property.
^[[0m    ^[[38;5;71m(^[[0m^[[38;5;167mwhen^[[0m ^[[38;5;173m(^[[0m^[[38;5;133mget-char-property^[[0m pos ^[[38;5;69m'^[[0m^[[38;5;94mdisplay^[[0m^[[38;5;173m)^[[0m
      ^[[38;5;173m(^[[0m^[[38;5;167msetq^[[0m pos ^[[38;5;69m(^[[0m^[[38;5;133mnext-single-char-property-change^[[0m pos ^[[38;5;69m'^[[0m^[[38;5;94mdisplay^[[0m nil limit^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    pos^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-overlay-faces-at^[[0m ^[[38;5;127m(^[[0mpos^[[38;5;127m)^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;133mdelq^[[0m nil ^[[38;5;71m(^[[0m^[[38;5;133mmapcar^[[0m ^[[38;5;173m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;69m(^[[0mo^[[38;5;69m)^[[0m ^[[38;5;69m(^[[0m^[[38;5;133moverlay-get^[[0m o ^[[38;5;69m'^[[0m^[[38;5;94mface^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m ^[[38;5;173m(^[[0m^[[38;5;133moverlays-at^[[0m pos^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;145m;;; ^[[0m^[[38;5;145mStyle helpers
^[[0m
^[[38;5;69m(^[[0m^[[38;5;167mdefcustom^[[0m ^[[38;5;53mengrave-faces-preset-styles^[[0m ^[[38;5;145m; ^[[0m^[[38;5;145mdoom-one-light
^[[0m  ^[[38;5;69m'^[[0m^[[38;5;127m(^[[0m^[[38;5;71m(^[[0mdefault                             ^[[38;5;127m:short^[[0m ^[[38;5;71m"default"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"D"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#383a42"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-keyword-face^[[0m              ^[[38;5;127m:short^[[0m ^[[38;5;71m"keyword"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"k"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#e45649"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-doc-face^[[0m                  ^[[38;5;127m:short^[[0m ^[[38;5;71m"doc"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"d"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#84888b"^[[0m ^[[38;5;127m:slant^[[0m italic^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-type-face^[[0m                 ^[[38;5;127m:short^[[0m ^[[38;5;71m"type"^[[0m             ^[[38;5;127m:slug^[[0m ^[[38;5;71m"t"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#986801"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-string-face^[[0m               ^[[38;5;127m:short^[[0m ^[[38;5;71m"string"^[[0m           ^[[38;5;127m:slug^[[0m ^[[38;5;71m"s"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#50a14f"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-warning-face^[[0m              ^[[38;5;127m:short^[[0m ^[[38;5;71m"warning"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"w"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#986801"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-builtin-face^[[0m              ^[[38;5;127m:short^[[0m ^[[38;5;71m"builtin"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"b"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#a626a4"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-comment-face^[[0m              ^[[38;5;127m:short^[[0m ^[[38;5;71m"comment"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"ct"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#9ca0a4"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-constant-face^[[0m             ^[[38;5;127m:short^[[0m ^[[38;5;71m"constant"^[[0m         ^[[38;5;127m:slug^[[0m ^[[38;5;71m"c"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#b751b6"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-preprocessor-face^[[0m         ^[[38;5;127m:short^[[0m ^[[38;5;71m"preprocessor"^[[0m     ^[[38;5;127m:slug^[[0m ^[[38;5;71m"pp"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m ^[[38;5;127m:weight^[[0m bold^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-negation-char-face^[[0m        ^[[38;5;127m:short^[[0m ^[[38;5;71m"neg-char"^[[0m         ^[[38;5;127m:slug^[[0m ^[[38;5;71m"nc"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m ^[[38;5;127m:weight^[[0m bold^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-variable-name-face^[[0m        ^[[38;5;127m:short^[[0m ^[[38;5;71m"variable"^[[0m         ^[[38;5;127m:slug^[[0m ^[[38;5;71m"v"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#6a1868"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-function-name-face^[[0m        ^[[38;5;127m:short^[[0m ^[[38;5;71m"function"^[[0m         ^[[38;5;127m:slug^[[0m ^[[38;5;71m"f"^[[0m     ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#a626a4"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;53mfont-lock-comment-delimiter-face^[[0m    ^[[38;5;127m:short^[[0m ^[[38;5;71m"comment-delim"^[[0m    ^[[38;5;127m:slug^[[0m ^[[38;5;71m"cd"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#9ca0a4"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mfont-lock-regexp-grouping-construct ^[[38;5;127m:short^[[0m ^[[38;5;71m"regexp"^[[0m           ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rc"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m ^[[38;5;127m:weight^[[0m bold^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mfont-lock-regexp-grouping-backslash ^[[38;5;127m:short^[[0m ^[[38;5;71m"regexp-backslash"^[[0m ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rb"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m ^[[38;5;127m:weight^[[0m bold^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0m^[[38;5;133morg-block^[[0m                           ^[[38;5;127m:short^[[0m ^[[38;5;71m"org-block"^[[0m        ^[[38;5;127m:slug^[[0m ^[[38;5;71m"ob"^[[0m^[[38;5;71m)^[[0m ^[[38;5;145m; ^[[0m^[[38;5;145mforcing no background is preferable
^[[0m    ^[[38;5;71m(^[[0mhighlight-numbers-number            ^[[38;5;127m:short^[[0m ^[[38;5;71m"number"^[[0m           ^[[38;5;127m:slug^[[0m ^[[38;5;71m"hn"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#da8548"^[[0m ^[[38;5;127m:weight^[[0m bold^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mhighlight-quoted-quote              ^[[38;5;127m:short^[[0m ^[[38;5;71m"qquote"^[[0m           ^[[38;5;127m:slug^[[0m ^[[38;5;71m"hq"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mhighlight-quoted-symbol             ^[[38;5;127m:short^[[0m ^[[38;5;71m"qsymbol"^[[0m          ^[[38;5;127m:slug^[[0m ^[[38;5;71m"hs"^[[0m    ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#986801"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-1-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd1"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdi"^[[0m   ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4078f2"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-2-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd2"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdii"^[[0m  ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#a626a4"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-3-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd3"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdiii"^[[0m ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#50a14f"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-4-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd4"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdiv"^[[0m  ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#da8548"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-5-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd5"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdv"^[[0m   ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#b751b6"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-6-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd6"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdvi"^[[0m  ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#986801"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-7-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd7"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdvii"^[[0m ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#4db5bd"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-8-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd8"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdiix"^[[0m ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#80a880"^[[0m^[[38;5;71m)^[[0m
    ^[[38;5;71m(^[[0mrainbow-delimiters-depth-9-face     ^[[38;5;127m:short^[[0m ^[[38;5;71m"rd9"^[[0m              ^[[38;5;127m:slug^[[0m ^[[38;5;71m"rdix"^[[0m  ^[[38;5;127m:foreground^[[0m ^[[38;5;71m"#887070"^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Overriding face values.

By setting :foreground, :background, etc. a certain theme can be set for
the faces.  The face attributes here will also be used when calculating
inherited styles.

Faces here will represented more compactly when possible, by using the
:short or :slug parameter to produce a named version styles, wheras other
faces will need to be explicitly styled each time they're used."^[[0m
  ^[[38;5;127m:type^[[0m ^[[38;5;69m'^[[0m^[[38;5;127m(^[[0mrepeat ^[[38;5;71m(^[[0m^[[38;5;127mrepeat^[[0m ^[[38;5;173m(^[[0mchoice symbol string^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
  ^[[38;5;127m:group^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mengrave-faces^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-check-nondefault^[[0m ^[[38;5;127m(^[[0mattr value^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Return VALUE as long as it is specified, and not the default for ATTR."^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;167munless^[[0m ^[[38;5;71m(^[[0m^[[38;5;167mor^[[0m ^[[38;5;173m(^[[0m^[[38;5;133meq^[[0m value ^[[38;5;69m(^[[0m^[[38;5;133mface-attribute^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mdefault^[[0m attr nil t^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m
              ^[[38;5;173m(^[[0m^[[38;5;133meq^[[0m value ^[[38;5;69m'^[[0m^[[38;5;94munspecified^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
    value^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;167mdefun^[[0m ^[[38;5;127mengrave-faces-generate-preset^[[0m ^[[38;5;127m(^[[0m^[[38;5;127m)^[[0m
  ^[[3m^[[38;5;245m"Generate `^[[0m^[[3m^[[38;5;133mengrave-faces-preset-styles^[[0m^[[3m^[[38;5;245m' based on the current theme."^[[0m
  ^[[38;5;127m(^[[0m^[[38;5;133mmapcar^[[0m
   ^[[38;5;71m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;173m(^[[0mface-style^[[38;5;173m)^[[0m
     ^[[38;5;173m(^[[0m^[[38;5;133mapply^[[0m ^[[38;5;69m#'^[[0m^[[38;5;94mappend^[[0m
            ^[[38;5;69m(^[[0m^[[38;5;133mlist^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mcar^[[0m face-style^[[38;5;127m)^[[0m
                  ^[[38;5;127m:short^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mplist-get^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mcdr^[[0m face-style^[[38;5;71m)^[[0m ^[[38;5;127m:short^[[0m^[[38;5;127m)^[[0m
                  ^[[38;5;127m:slug^[[0m ^[[38;5;127m(^[[0m^[[38;5;133mplist-get^[[0m ^[[38;5;71m(^[[0m^[[38;5;133mcdr^[[0m face-style^[[38;5;71m)^[[0m ^[[38;5;127m:slug^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
            ^[[38;5;69m(^[[0m^[[38;5;133mdelq^[[0m nil
                  ^[[38;5;127m(^[[0m^[[38;5;133mmapcar^[[0m
                   ^[[38;5;71m(^[[0m^[[38;5;167mlambda^[[0m ^[[38;5;173m(^[[0mattr^[[38;5;173m)^[[0m
                     ^[[38;5;173m(^[[0m^[[38;5;167mlet^[[0m ^[[38;5;69m(^[[0m^[[38;5;127m(^[[0mattr-val ^[[38;5;71m(^[[0m^[[38;5;133mface-attribute^[[0m ^[[38;5;173m(^[[0m^[[38;5;133mcar^[[0m face-style^[[38;5;173m)^[[0m attr nil t^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m
                       ^[[38;5;69m(^[[0m^[[38;5;167mwhen^[[0m ^[[38;5;127m(^[[0m^[[38;5;167mor^[[0m ^[[38;5;71m(^[[0m^[[38;5;127mengrave-faces-check-nondefault^[[0m attr attr-val^[[38;5;71m)^[[0m
                                 ^[[38;5;71m(^[[0m^[[38;5;133meq^[[0m ^[[38;5;173m(^[[0m^[[38;5;133mcar^[[0m face-style^[[38;5;173m)^[[0m ^[[38;5;69m'^[[0m^[[38;5;94mdefault^[[0m^[[38;5;71m)^[[0m^[[38;5;127m)^[[0m
                         ^[[38;5;127m(^[[0m^[[38;5;133mlist^[[0m attr attr-val^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
                   ^[[38;5;53mengrave-faces-attributes-of-interest^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m^[[38;5;173m)^[[0m^[[38;5;71m)^[[0m
   ^[[38;5;53mengrave-faces-preset-styles^[[0m^[[38;5;127m)^[[0m^[[38;5;69m)^[[0m

^[[38;5;69m(^[[0m^[[38;5;133mprovide^[[0m ^[[38;5;69m'^[[0m^[[38;5;133mengrave-faces^[[0m^[[38;5;69m)^[[0m
^[[38;5;145m;;; ^[[0m^[[38;5;145mengrave-faces.el ends here
^[[0m

[-- Attachment #8: engrave-faces.el.html --]
[-- Type: text/html, Size: 59786 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-09 17:39 Core package offering - engrave-faces.el Timothy
@ 2021-07-09 21:14 ` Clément Pit-Claudel
  2021-07-16  6:04 ` Timothy
  1 sibling, 0 replies; 13+ messages in thread
From: Clément Pit-Claudel @ 2021-07-09 21:14 UTC (permalink / raw)
  To: emacs-devel

On 7/9/21 1:39 PM, Timothy wrote:
> Unlike htmlize, Engrave Faces provides general functionality to
> transform a buffer into another format with font-lock information.
> This core functionality is currently made us of in
> engrave-faces-latex.el, engrave-faces-ansi.el, and engrave-faces-html.el
> to provide exporters for LaTeX, ASCII/ANSI, and HTML.

Neat! But, I'm not sure I understand what the package actually does. Can you give a short example of the API and the results it produces?

If I understand correctly, the following may be relevant:

https://github.com/cpitclaudel/esh (I mention it because it supports exporting overlays to HTML and LaTeX, too, and it uses a tricky algorithm to correctly handle overlapping fontification)
https://github.com/Lindydancer/faceup
https://github.com/Lindydancer/face-explorer

> For comparison, htmlize.el is 1700 sloc and htmlfontify.el is 2200 sloc.

My experience is that getting faces right is hard, so I'm not sure I'd assume these thousands of lines are just due to bloat ^^



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-09 17:39 Core package offering - engrave-faces.el Timothy
  2021-07-09 21:14 ` Clément Pit-Claudel
@ 2021-07-16  6:04 ` Timothy
  2021-07-16  7:09   ` Eli Zaretskii
       [not found]   ` <7319ffa7-d566-41b8-b778-69db2f6becc9@gmail.com>
  1 sibling, 2 replies; 13+ messages in thread
From: Timothy @ 2021-07-16  6:04 UTC (permalink / raw)
  To: Emacs developers


It would be great if someone might be willing to take a look at this and
respond :) If my proposal doesn't sound good, I'd rather it was rejected
outright so I know where I stand before I get started with an exploratory
Org branch to integrate this with some of the current exporters.

--
Timothy <tecosaur@gmail.com> writes:

> Hi All,
>
> Over the last few months I have worked on a package that I think may be
> a good candidate for inclusion into Emacs. It has recently reached what
> I consider a sufficient quality for consideration - though I anticipate
> that should this be seen as promising there will likely be suggested
> improvements.
>
> This has been motivated by a desire to produce a better method of
> formatting Org source blocks in PDF export, and inspired by htmlize.
>
> Unlike htmlize, Engrave Faces provides general functionality to
> transform a buffer into another format with font-lock information.
> This core functionality is currently made us of in
> engrave-faces-latex.el, engrave-faces-ansi.el, and engrave-faces-html.el
> to provide exporters for LaTeX, ASCII/ANSI, and HTML.
>
> Compared to htmlize and htmlfontify.el, Engrave Faces currently lacks
> the ability to treat links specially, or properly handle the text
> transformation performed by overlays*. However, this is also much smaller
> and hopefully more maintainable.
> - engrave-faces.el is 250 sloc
> - engrave-faces-latex.el is 100 sloc
> - engrave-faces-ansi.el is 140 sloc
> - engrave-faces-html.el is 115 sloc
> ( *there may be other differences, but this is what's apparent to me. )
>
> For comparison, htmlize.el is 1700 sloc and htmlfontify.el is 2200 sloc.
>
> Engrave Faces also possesses the unique capability that it can be given
> a list of preset face styles which will override the current attributes
> of those faces. These overrides are also passed down with inheritance,
> via an internal expansion and merging of face attribute information.
> This allows one to save the details for a certain theme, and "engrave"
> the buffer with font-lock attributes from that theme regardless of what
> the current theme is. For ease of applying this to with your current
> theme a function `engrave-faces-generate-preset' is provided which
> re-generates the saved face information using the current theme.
>
> I feel that this is likely generally useful functionality, and so would
> like to offer it to Emacs to make it more accessible and more visible
> for improvement.
>
> I am currently an FSF-assigned contributor to Org mode, and assume that
> this should make contributing to Emacs fairly straightforward.
>
> Attached you may find the engrave-faces{,-latex,-ansi,-html}.el as well
> as LaTeX, ASCII/ANSI, and HTML versions of engrave-faces.el for sampling.
> The code is also available at https://github.com/tecosaur/engrave-faces.
>
> All the best,


--
Timothy



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-16  6:04 ` Timothy
@ 2021-07-16  7:09   ` Eli Zaretskii
  2021-07-16  7:36     ` Timothy
       [not found]   ` <7319ffa7-d566-41b8-b778-69db2f6becc9@gmail.com>
  1 sibling, 1 reply; 13+ messages in thread
From: Eli Zaretskii @ 2021-07-16  7:09 UTC (permalink / raw)
  To: Timothy; +Cc: emacs-devel

> From: Timothy <tecosaur@gmail.com>
> Date: Fri, 16 Jul 2021 14:04:00 +0800
> 
> It would be great if someone might be willing to take a look at this and
> respond :) If my proposal doesn't sound good, I'd rather it was rejected
> outright so I know where I stand before I get started with an exploratory
> Org branch to integrate this with some of the current exporters.

You were asked in

  https://lists.gnu.org/archive/html/emacs-devel/2021-07/msg00071.html

to provide some additional details about the package, but you never
responded to that request.  Would you please do that now?



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-16  7:09   ` Eli Zaretskii
@ 2021-07-16  7:36     ` Timothy
  0 siblings, 0 replies; 13+ messages in thread
From: Timothy @ 2021-07-16  7:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel


Eli Zaretskii <eliz@gnu.org> writes:

> You were asked in
>
>   https://lists.gnu.org/archive/html/emacs-devel/2021-07/msg00071.html
>
> to provide some additional details about the package, but you never
> responded to that request.  Would you please do that now?

Unfortunately I never received that email. Clément just mentioned that
he replied to the list but not to me. I'll send a response to his email
shortly :)

--
Timothy



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
       [not found]   ` <7319ffa7-d566-41b8-b778-69db2f6becc9@gmail.com>
@ 2021-07-16 13:08     ` Timothy
  2021-07-23 15:49       ` Timothy
  0 siblings, 1 reply; 13+ messages in thread
From: Timothy @ 2021-07-16 13:08 UTC (permalink / raw)
  To: Clément Pit-Claudel; +Cc: Emacs developers


Hi Clément, thanks for your email. I'll paste your reply that you kindly
linked and respond inline.

Clément Pit-Claudel <cpitclaudel@gmail.com> writes:

>> Unlike htmlize, Engrave Faces provides general functionality to
>> transform a buffer into another format with font-lock information.
>> This core functionality is currently made us of in
>> engrave-faces-latex.el, engrave-faces-ansi.el, and engrave-faces-html.el
>> to provide exporters for LaTeX, ASCII/ANSI, and HTML.
>
> Neat! But, I'm not sure I understand what the package actually does. Can you
> give a short example of the API and the results it produces?

Sure! So each of the engrave-faces-FORMAT.el files defines 1-2 user
facing interactive functions:
- engrave-faces-FORMAT-buffer, and maybe
- engrave-faces-FORMAT-buffer-standalone

These both return a new buffer which is an export of the current to
FORMAT.

So for example if I have an elisp buffer which only contains

  (funcall #'message "hi")

and call M-x engrave-faces-latex-buffer I will be taken to a new buffer
containing

  \color[HTML]{383a42}\textcolor[HTML]{4078f2}{(}\EFc{funcall} \EFhq{\#'}\EFhs{message} \EFs{"hi"}\textcolor[HTML]{4078f2}{)}

and M-x engrave-faces-latex-buffer will give

  <span style="color: #4078f2;">(</span><span class="ef-c">funcall</span> <span class="ef-hq">#'</span><span class="ef-hs">message</span> <span class="ef-s">"hi"</span><span style="color: #4078f2;">)</span>

While the -standalone variants will generate a valid LaTeX/HTML document
with a preamble defining some of the commands/classes seen above.

Meanwhile, the most notable part of the core library itself
(engrave-faces.el) would be the macro `engrave-faces-define-backend'.

This could really do with a good docstring, but it takes
- a format name FORMAT
- the file extension for FORMAT
- a "face transforming" function

What this "face transforming" function needs to do is given a list of
faces applying to a certain content string, generate a new string that
applies that information to the content in FORMAT.

There's a rather nice helper function which I expect to be made use of
here, `engrave-faces-merge-attributes' which will take a face spec list
(as simple as "(default)" or complex as "((:weight bold :inherit
default) warning highlight (:extend t))") and a list of attributes, and
resolve all the attributes for that face spec while considering the
'saved' faces in `engrave-faces-preset-styles' -- returning a plist like:

  (:foreground #FCCE7B :background #51afef :slant nil :weight bold :height nil :strike-through nil)

This can then be used to create a transformed version of the content
with face information in FORMAT like:

  <span style="color: #FCCE7B; background-color: #51afef; weight: bold">content...</span>

With such a "face transforming" function `engrave-faces-define-backend'
will create the user-facing function `engrave-faces-FORMAT-buffer' which
will apply this transformation across the whole buffer.

I hope this gives you a better idea of what this package is doing :)

> If I understand correctly, the following may be relevant:
>
> https://github.com/cpitclaudel/esh (I mention it because it supports exporting
> overlays to HTML and LaTeX, too, and it uses a tricky algorithm to correctly
> handle overlapping fontification)
> https://github.com/Lindydancer/faceup
> https://github.com/Lindydancer/face-explorer

Interesting. I have not seen many of these before.

>> For comparison, htmlize.el is 1700 sloc and htmlfontify.el is 2200 sloc.
>
> My experience is that getting faces right is hard, so I'm not sure I'd assume
> these thousands of lines are just due to bloat ^^

Regarding the the thousands of lines being "bloat", I do not think that
this is the case, and I suspect that not (yet) handling overlays is has
made my job much easier with engrave-faces.el. However, from my usage I
have not found any clear failings with the current approach. To get a
sample of how things look see the attachments to my original email.
Should you be interested in seeing how a LaTeX export of some code
looks, I can refer you to https://tecosaur.github.io/emacs-config/config.pdf.
This is mostly elisp but has a sprinkling of other languages too.

Having looked at htmlize.el a bit I noticed that:
- It was hard to get a clear picture of what's going on easily
- The HTML generation and face-processing parts where deeply intertwined

With this package I'm aiming to create:
- A simpler, smaller (as much as possible), easier to understand core
- Separate the concerns of processing face information and creating a
  representation in a certain format(s)
- Have the face processing be generalised such that creating exporters
  in certain formats is a much easier task

I expect the current state of affairs has room for improvement, but I
feel it has also reached a state where it is plausibly useful :)

Please let me know if this has answered your questions, and if you have
any more.

--
Timothy



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-16 13:08     ` Timothy
@ 2021-07-23 15:49       ` Timothy
  2021-07-23 20:08         ` Clément Pit-Claudel
  2021-07-23 20:32         ` Stefan Monnier
  0 siblings, 2 replies; 13+ messages in thread
From: Timothy @ 2021-07-23 15:49 UTC (permalink / raw)
  To: Clément Pit-Claudel; +Cc: Emacs developers


Hello again Clément,

Just following up on this -- might you (or anyone else!), have any
further comments/questions on this?

--
Timothy



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-23 15:49       ` Timothy
@ 2021-07-23 20:08         ` Clément Pit-Claudel
  2021-07-24  5:54           ` Eli Zaretskii
  2021-07-23 20:32         ` Stefan Monnier
  1 sibling, 1 reply; 13+ messages in thread
From: Clément Pit-Claudel @ 2021-07-23 20:08 UTC (permalink / raw)
  To: Timothy; +Cc: Emacs developers

Hi Timothy,

On 7/23/21 11:49 AM, Timothy wrote:
> Just following up on this -- might you (or anyone else!), have any
> further comments/questions on this?

Not many; your explanations were helpful! Thanks.  I'd love to hear more when you have a better sense of the differences with other packages, and in the meantime I think it would be best to have this as an ELPA package (but it's not my decision at all).

I would love to rebuild ESH on top of engrave-faces, if it is flexible enough. FWIW, the way I handle overlays there is to "commit" them to the buffer, in the sense that I replace existing properties in the buffer with them, insert the corresponding before-string, etc, so that export functions can then worry only about text properties.

Clément.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-23 15:49       ` Timothy
  2021-07-23 20:08         ` Clément Pit-Claudel
@ 2021-07-23 20:32         ` Stefan Monnier
  2021-08-29 19:11           ` Timothy
  1 sibling, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2021-07-23 20:32 UTC (permalink / raw)
  To: Timothy; +Cc: Clément Pit-Claudel, Emacs developers

From the side-lines: I'd be happy to add it to GNU ELPA if you're
interested, and I'll let others decide whether to add it to
Emacs itself.


        Stefan


Timothy [2021-07-23 23:49:51] wrote:

> Hello again Clément,
>
> Just following up on this -- might you (or anyone else!), have any
> further comments/questions on this?
>
> --
> Timothy




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-23 20:08         ` Clément Pit-Claudel
@ 2021-07-24  5:54           ` Eli Zaretskii
  0 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2021-07-24  5:54 UTC (permalink / raw)
  To: Clément Pit-Claudel; +Cc: emacs-devel, tecosaur

> From: Clément Pit-Claudel <cpitclaudel@gmail.com>
> Date: Fri, 23 Jul 2021 16:08:38 -0400
> Cc: Emacs developers <emacs-devel@gnu.org>
> 
> On 7/23/21 11:49 AM, Timothy wrote:
> > Just following up on this -- might you (or anyone else!), have any
> > further comments/questions on this?
> 
> Not many; your explanations were helpful! Thanks.  I'd love to hear more when you have a better sense of the differences with other packages, and in the meantime I think it would be best to have this as an ELPA package (but it's not my decision at all).

My comment is that the package seems to support a subset of what
htmlfontify supports, basically the colors.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-07-23 20:32         ` Stefan Monnier
@ 2021-08-29 19:11           ` Timothy
  2021-08-29 23:33             ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Timothy @ 2021-08-29 19:11 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Emacs developers

[-- Attachment #1: Type: text/plain, Size: 1627 bytes --]

Hi Stefan,

> From the side-lines: I’d be happy to add it to GNU ELPA if you’re
> interested, and I’ll let others decide whether to add it to
> Emacs itself.

Thanks for the offer. While I’ve left this linger I’ve been mulling over what I
think would be best in the near future. While I still consider this to be
something likely worth replacing/supplimenting htmlfontify with in due course,
given how young this is I suspect that this package could benefit from living
somewhere which lets it mature a bit and possibly make the odd breaking change
as it settles down — like ELPA!

I also feel like with this in ELPA this would be a reasonable optional
dependency for Org. I’m hoping for the ability to (setq org-latex-listings
’engraved) in the future, and potentially spreading it to ox-ascii and ox-html,
maybe even ox-odt if someone gets around to that backend.

I have never done anything with ELPA before though, so I’m liable to ask one or
two annoying questions :P.

All the best,
Timothy

――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

Oh, and just while I’m writing a reply, for the record

> My comment is that the package seems to support a subset of what
> htmlfontify supports, basically the colors.  [Eli]

I don’t find this characterisation accurate, if I were to summarise this I’d
call it my slightly-worse-than-htmlize-but-more-generic-and-supports-multiple-formats-package 😛.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-08-29 19:11           ` Timothy
@ 2021-08-29 23:33             ` Stefan Monnier
  2021-08-30 18:22               ` Timothy
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2021-08-29 23:33 UTC (permalink / raw)
  To: Timothy; +Cc: Emacs developers

>> From the side-lines: I’d be happy to add it to GNU ELPA if you’re
>> interested, and I’ll let others decide whether to add it to
>> Emacs itself.
> Thanks for the offer. While I’ve left this linger I’ve been mulling over what I
> think would be best in the near future. While I still consider this to be
> something likely worth replacing/supplimenting htmlfontify with in due course,
> given how young this is I suspect that this package could benefit from living
> somewhere which lets it mature a bit and possibly make the odd breaking change
> as it settles down — like ELPA!

If you give the URL of a Git repository from which we can fetch that
package, I'll see about adding it to GNU ELPA.


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Core package offering - engrave-faces.el
  2021-08-29 23:33             ` Stefan Monnier
@ 2021-08-30 18:22               ` Timothy
  0 siblings, 0 replies; 13+ messages in thread
From: Timothy @ 2021-08-30 18:22 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Emacs developers

[-- Attachment #1: Type: text/plain, Size: 265 bytes --]

> If you give the URL of a Git repository from which we can fetch that
> package, I’ll see about adding it to GNU ELPA.

Thanks Stefan, I’m developing this on GitHub so the repo is
<https://github.com/tecosaur/engrave-faces.git>.

All the best,
Timothy

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2021-08-30 18:22 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-09 17:39 Core package offering - engrave-faces.el Timothy
2021-07-09 21:14 ` Clément Pit-Claudel
2021-07-16  6:04 ` Timothy
2021-07-16  7:09   ` Eli Zaretskii
2021-07-16  7:36     ` Timothy
     [not found]   ` <7319ffa7-d566-41b8-b778-69db2f6becc9@gmail.com>
2021-07-16 13:08     ` Timothy
2021-07-23 15:49       ` Timothy
2021-07-23 20:08         ` Clément Pit-Claudel
2021-07-24  5:54           ` Eli Zaretskii
2021-07-23 20:32         ` Stefan Monnier
2021-08-29 19:11           ` Timothy
2021-08-29 23:33             ` Stefan Monnier
2021-08-30 18:22               ` Timothy

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).