Rework emojis in Org LaTeX export
This commit is contained in:
parent
b3904e3201
commit
c083c2475e
269
config.org
269
config.org
|
@ -11649,6 +11649,8 @@ Now all that remains is to hook this into the preamble generation.
|
|||
|
||||
**** Emojis
|
||||
|
||||
#+call: confpkg("ox-latex-emoji", after="ox-latex", prefix="")
|
||||
|
||||
It would be nice to actually include emojis where used.
|
||||
Thanks to =emojify=, we have a folder of emoji images just sitting and waiting to
|
||||
be used 🙂.
|
||||
|
@ -11658,7 +11660,7 @@ constructing a regex for this would be a huge pain with the way the codepoints
|
|||
are scattered around, but thanks to ~char-script-table~ we don't have to!
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defvar +emoji-rx
|
||||
(defvar org-latex-emoji--rx
|
||||
(let (emojis)
|
||||
(map-char-table
|
||||
(lambda (char set)
|
||||
|
@ -11689,10 +11691,10 @@ surrogate form required by =\BeginAccSupp=.
|
|||
\usepackage{transparent}
|
||||
\newsavebox\emojibox
|
||||
|
||||
\NewDocumentCommand\DeclareEmoji{m m}{%
|
||||
\NewDocumentCommand\DeclareEmoji{m m}{% UTF-8 codepoint, UTF-16 codepoint
|
||||
\DeclareUnicodeCharacter{#1}{%
|
||||
\sbox\emojibox{\raisebox{-0.3ex}{%
|
||||
\includegraphics[height=1.8ex]{EMOJI-FOLDER/#1}}}%
|
||||
\sbox\emojibox{\raisebox{OFFSET}{%
|
||||
\includegraphics[height=HEIGHT]{EMOJI-FOLDER/#1}}}%
|
||||
\usebox\emojibox
|
||||
\llap{%
|
||||
\resizebox{\wd\emojibox}{\height}{%
|
||||
|
@ -11705,14 +11707,45 @@ Once we know that there are emojis present we can add a bit of preamble to the
|
|||
buffer to make insertion easier.
|
||||
|
||||
#+begin_src emacs-lisp :noweb no-export :noweb-prefix no
|
||||
(defconst org-latex-emoji-dir
|
||||
(expand-file-name "emojis/twemoji-v2/" doom-cache-dir)
|
||||
(defconst org-latex-emoji-base-dir
|
||||
(expand-file-name "emojis/" doom-cache-dir)
|
||||
"Directory where emojis should be saved and look for.")
|
||||
|
||||
(defvar org-latex-emoji-sets
|
||||
'(("twemoji" :url "https://github.com/twitter/twemoji/archive/refs/tags/v14.0.2.zip"
|
||||
:folder "twemoji-14.0.2/assets/svg" :height "1.8ex" :offset "-0.3ex")
|
||||
("twemoji-bw" :url "https://github.com/youdly/twemoji-color-font/archive/refs/heads/v11-release.zip"
|
||||
:folder "twemoji-color-font-11-release/assets/builds/svg-bw" :height "1.8ex" :offset "-0.3ex")
|
||||
("openmoji" :url "https://github.com/hfg-gmuend/openmoji/releases/latest/download/openmoji-svg-color.zip"
|
||||
:height "2.2ex" :offset "-0.45ex")
|
||||
("openmoji-bw" :url "https://github.com/hfg-gmuend/openmoji/releases/latest/download/openmoji-svg-black.zip"
|
||||
:height "2.2ex" :offset "-0.45ex")
|
||||
("emojione" :url "https://github.com/joypixels/emojione/archive/refs/tags/v2.2.7.zip"
|
||||
:folder "emojione-2.2.7/assets/svg") ; Warning, poor coverage
|
||||
("noto" :url "https://github.com/googlefonts/noto-emoji/archive/refs/tags/v2.038.zip"
|
||||
:folder "noto-emoji-2.038/svg" :file-regexp "^emoji_u\\([0-9a-f_]+\\)"
|
||||
:height "2.0ex" :offset "-0.3ex"))
|
||||
"An alist of plistst of emoji sets.
|
||||
Specified with the minimal form:
|
||||
(\"SET-NAME\" :url \"URL\")
|
||||
The following optional parameters are supported:
|
||||
:folder (defaults to \"\")
|
||||
The folder within the archive where the emojis exist.
|
||||
:file-regexp (defaults to nil)
|
||||
Pattern with the emoji code point as the first capture group.
|
||||
:height (defaults to \"1.8ex\")
|
||||
Height of the emojis to be used.
|
||||
:offset (defaults to \"-0.3ex\")
|
||||
Baseline offset of the emojis.")
|
||||
|
||||
(defconst org-latex-emoji-keyword
|
||||
"LATEX_EMOJI_SET"
|
||||
"Keyword used to set the emoji set from `org-latex-emoji-sets'.")
|
||||
|
||||
(defvar org-latex-emoji-preamble <<grab("latex-emoji-preamble")>>
|
||||
"LaTeX preamble snippet that will allow for emojis to be declared.
|
||||
Containes the string \"EMOJI-FOLDER\" which should be replaced with
|
||||
the value of `org-latex-emoji-dir'.")
|
||||
Contains the string \"EMOJI-FOLDER\" which should be replaced with
|
||||
the path to the emoji folder.")
|
||||
|
||||
(defun org-latex-emoji-utf16 (char)
|
||||
"Return the pair of UTF-16 surrogates that represent CHAR."
|
||||
|
@ -11721,6 +11754,7 @@ the value of `org-latex-emoji-dir'.")
|
|||
(+ #xDC00 (logand char #x03FF))))
|
||||
|
||||
(defun org-latex-emoji-declaration (char)
|
||||
"Construct the LaTeX command declaring CHAR as an emoji."
|
||||
(format "\\DeclareEmoji{%X}{%s} %% %s"
|
||||
char
|
||||
(if (< char #xFFFF)
|
||||
|
@ -11728,35 +11762,88 @@ the value of `org-latex-emoji-dir'.")
|
|||
(apply #'format "%X%X" (org-latex-emoji-utf16 char)))
|
||||
(capitalize (get-char-code-property char 'name))))
|
||||
|
||||
(defun org-latex-emoji-setup (&optional _info)
|
||||
(concat
|
||||
(replace-regexp-in-string
|
||||
"EMOJI-FOLDER"
|
||||
(directory-file-name
|
||||
(if (getenv "HOME")
|
||||
(replace-regexp-in-string
|
||||
(regexp-quote (getenv "HOME"))
|
||||
"\\string~"
|
||||
org-latex-emoji-dir t t)
|
||||
org-latex-emoji-dir))
|
||||
org-latex-emoji-preamble t t)
|
||||
"\n\n"
|
||||
(mapconcat
|
||||
#'org-latex-emoji-declaration
|
||||
(let (unicode-cars)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward +emoji-rx nil t)
|
||||
(push (aref (match-string 0) 0) unicode-cars)))
|
||||
(cl-delete-duplicates unicode-cars))
|
||||
"\n")
|
||||
"\n"))
|
||||
(defun org-latex-emoji-fill-preamble (emoji-folder &optional height offset svg-p)
|
||||
"Fill in `org-latex-emoji-preamble' with EMOJI-FOLDER, HEIGHT, and OFFSET.
|
||||
If SVG-P is set \"includegraphics\" will be replaced with \"includesvg\"."
|
||||
(let* (case-fold-search
|
||||
(filled-preamble
|
||||
(replace-regexp-in-string
|
||||
"HEIGHT"
|
||||
(or height "1.8ex")
|
||||
(replace-regexp-in-string
|
||||
"OFFSET"
|
||||
(or offset "-0.3ex")
|
||||
(replace-regexp-in-string
|
||||
"EMOJI-FOLDER"
|
||||
(directory-file-name
|
||||
(if (getenv "HOME")
|
||||
(replace-regexp-in-string
|
||||
(regexp-quote (getenv "HOME"))
|
||||
"\\string~"
|
||||
emoji-folder t t)
|
||||
emoji-folder))
|
||||
org-latex-emoji-preamble t t)
|
||||
t t)
|
||||
t t)))
|
||||
(if svg-p
|
||||
(replace-regexp-in-string
|
||||
"includegraphics" "includesvg"
|
||||
filled-preamble t t)
|
||||
filled-preamble)))
|
||||
|
||||
(defun org-latex-emoji-setup (&optional info)
|
||||
"Construct a preamble snippet to set up emojis based on INFO."
|
||||
(let* ((emoji-set
|
||||
(or (org-element-map
|
||||
(plist-get info :parse-tree)
|
||||
'keyword
|
||||
(lambda (keyword)
|
||||
(and (string= (org-element-property :key keyword)
|
||||
org-latex-emoji-keyword)
|
||||
(org-element-property :value keyword)))
|
||||
info t)
|
||||
(caar org-latex-emoji-sets)))
|
||||
(emoji-spec (cdr (assoc emoji-set org-latex-emoji-sets)))
|
||||
(emoji-folder
|
||||
(expand-file-name emoji-set org-latex-emoji-base-dir))
|
||||
(emoji-svg-only
|
||||
(and (file-exists-p emoji-folder)
|
||||
(not (cl-some
|
||||
(lambda (path)
|
||||
(not (string= (file-name-extension path) "svg")))
|
||||
(directory-files emoji-folder nil "\\....$"))))))
|
||||
(cond
|
||||
((not emoji-spec)
|
||||
(error "Emoji set `%s' is unknown. Try one of: %s" emoji-set
|
||||
(string-join (mapcar #'car org-latex-emoji-sets) ", ")))
|
||||
((not (file-exists-p emoji-folder))
|
||||
(if (and (not noninteractive)
|
||||
(yes-or-no-p (format "Emoji set `%s' is not installed, would you like to install it?" emoji-set)))
|
||||
(org-latex-emoji-install
|
||||
emoji-set
|
||||
(or (executable-find "cairosvg") (executable-find "inkscape")))
|
||||
(error "Emoji set `%s' is not installed" emoji-set))))
|
||||
(concat
|
||||
(org-latex-emoji-fill-preamble
|
||||
emoji-folder (plist-get emoji-spec :height)
|
||||
(plist-get emoji-spec :offset) emoji-svg-only)
|
||||
"\n\n"
|
||||
(mapconcat
|
||||
#'org-latex-emoji-declaration
|
||||
(let (unicode-cars)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward org-latex-emoji--rx nil t)
|
||||
(push (aref (match-string 0) 0) unicode-cars)))
|
||||
(cl-delete-duplicates unicode-cars))
|
||||
"\n")
|
||||
"\n")))
|
||||
|
||||
(add-to-list 'org-export-conditional-features
|
||||
(cons (lambda (_info)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward +emoji-rx nil t)))
|
||||
(re-search-forward org-latex-emoji--rx nil t)))
|
||||
'emoji)
|
||||
t)
|
||||
|
||||
|
@ -11769,9 +11856,8 @@ perform. =emojify= downloads the ~72x72~ versions of Twemoji, however SVG versio
|
|||
are also produced. We could use ~inkscape~ to convert those to PDFs, which would
|
||||
likely be best for including.
|
||||
|
||||
First, it's worth checking whether =.pdf= graphics files will be prioritised over
|
||||
=.png= files. If so, that would be ideal as no extra effort is required past
|
||||
fetching and converting the files.
|
||||
This works fairly nicely, but it would be good to use =.pdf= forms whenever
|
||||
possible. We can use =texdef= to check the file extension priority list.
|
||||
|
||||
#+begin_src shell :tangle no :exports both :results output verbatim :wrap example
|
||||
texdef -t pdflatex -p graphicx Gin@extensions
|
||||
|
@ -11783,34 +11869,82 @@ texdef -t pdflatex -p graphicx Gin@extensions
|
|||
macro:->.pdf,.png,.jpg,.mps,.jpeg,.jbig2,.jb2,.PDF,.PNG,.JPG,.JPEG,.JBIG2,.JB2,.eps
|
||||
#+end_example
|
||||
|
||||
Fantastic! We can see that =.pdf= actually comes first in the priority list. So
|
||||
now we just need to fetch and convert those SVGs --- ideally with a handy
|
||||
command to do so for us.
|
||||
Fantastic! We can see that =.pdf= actually comes first in the priority list.
|
||||
Now we just need to fetch and convert the emoji images.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun org-latex-emoji-install-vector-graphics ()
|
||||
"Dowload, convert, and install vector emojis for use with LaTeX."
|
||||
(interactive)
|
||||
(let ((dir (org-latex-emoji-install-vector-graphics--download)))
|
||||
(org-latex-emoji-install-vector-graphics--convert dir)
|
||||
(org-latex-emoji-install-vector-graphics--install dir))
|
||||
(message "Vector emojis installed."))
|
||||
(defun org-latex-emoji-install (set &optional convert)
|
||||
"Dowload, convert, and install emojis for use with LaTeX."
|
||||
(interactive
|
||||
(list (completing-read "Emoji set to install: "
|
||||
(mapcar
|
||||
(lambda (set-spec)
|
||||
(if (file-exists-p (expand-file-name (car set-spec) org-latex-emoji-base-dir))
|
||||
(propertize (car set-spec) 'face 'font-lock-doc-face)
|
||||
(car set-spec)))
|
||||
org-latex-emoji-sets)
|
||||
nil t)
|
||||
(if (or (executable-find "cairosvg") (executable-find "inkscape"))
|
||||
(yes-or-no-p "Would you like to create .pdf forms of the Emojis (strongly recommended)?")
|
||||
(message "Install `cairosvg' (recommended) or `inkscape' to convert to PDF forms")
|
||||
nil)))
|
||||
(let ((emoji-folder (expand-file-name set org-latex-emoji-base-dir)))
|
||||
(when (or (not (file-exists-p emoji-folder))
|
||||
(and (not noninteractive)
|
||||
(yes-or-no-p "Emoji folder already present, would you like to re-download?")
|
||||
(progn (delete-directory emoji-folder) t)))
|
||||
(let* ((spec (cdr (assoc set org-latex-emoji-sets)))
|
||||
(dir (org-latex-emoji-install--download set (plist-get spec :url)))
|
||||
(svg-dir (expand-file-name (or (plist-get spec :folder) "") dir)))
|
||||
(org-latex-emoji-install--install
|
||||
set svg-dir (plist-get spec :file-regexp))))
|
||||
(when convert
|
||||
(org-latex-emoji-install--convert (file-name-as-directory emoji-folder))))
|
||||
(message "Emojis set `%s' installed." set))
|
||||
|
||||
(defconst org-latex-emoji-source-url
|
||||
"https://github.com/twitter/twemoji/archive/refs/tags/v14.0.2.zip"
|
||||
"URL to the (tw)emoji source archive.")
|
||||
|
||||
(defun org-latex-emoji-install-vector-graphics--download ()
|
||||
(let* ((twemoji-version (replace-regexp-in-string "^.*tags/v\\(.*\\)\\.zip" "\\1" org-latex-emoji-source-url))
|
||||
(twemoji-dest-folder (make-temp-file "twemoji-" t)))
|
||||
(message "Downloading Twemoji v%s" twemoji-version)
|
||||
(let ((default-directory twemoji-dest-folder))
|
||||
(call-process "curl" nil nil nil "-L" org-latex-emoji-source-url "--output" "twemoji.zip")
|
||||
(defun org-latex-emoji-install--download (name url)
|
||||
"Download the emoji archive URL for the set NAME."
|
||||
(let* ((dest-folder (make-temp-file (format "%s-" name) t)))
|
||||
(message "Downloading %s..." name)
|
||||
(let ((default-directory dest-folder))
|
||||
(call-process "curl" nil nil nil "-sL" url "--output" "emojis.zip")
|
||||
(message "Unzipping")
|
||||
(call-process "unzip" nil nil nil "twemoji.zip")
|
||||
(concat twemoji-dest-folder "/twemoji-" twemoji-version "/assets/svg"))))
|
||||
(call-process "unzip" nil nil nil "emojis.zip")
|
||||
dest-folder)))
|
||||
|
||||
(defun org-latex-emoji-install-vector-graphics--convert (dir)
|
||||
(defun org-latex-emoji-install--install (name dir &optional filename-regexp)
|
||||
"Install the emoji files in DIR to the NAME set folder.
|
||||
If a FILENAME-REGEXP, only files matching this regexp will be moved,
|
||||
and they will be renamed to the first capture group of the regexp."
|
||||
(message "Installing %s emojis into emoji directory" name)
|
||||
(let ((images (append (directory-files dir t ".*.svg")
|
||||
(directory-files dir t ".*.pdf")))
|
||||
(emoji-dir (file-name-as-directory
|
||||
(expand-file-name name org-latex-emoji-base-dir))))
|
||||
(unless (file-exists-p emoji-dir)
|
||||
(make-directory emoji-dir t))
|
||||
(mapc
|
||||
(lambda (image)
|
||||
(if filename-regexp
|
||||
(when (string-match filename-regexp (file-name-nondirectory image))
|
||||
(rename-file image
|
||||
(expand-file-name
|
||||
(file-name-with-extension
|
||||
(downcase (match-string 1 (file-name-nondirectory image)))
|
||||
(file-name-extension image))
|
||||
emoji-dir)
|
||||
t))
|
||||
(rename-file image
|
||||
(expand-file-name
|
||||
(downcase (file-name-nondirectory image))
|
||||
emoji-dir)
|
||||
t)))
|
||||
images)
|
||||
(message "%d emojis installed" (length images))))
|
||||
|
||||
(defun org-latex-emoji-install--convert (dir)
|
||||
"Convert all .svg files in DIR to .pdf forms.
|
||||
Uses cairosvg if possible, falling back to inkscape."
|
||||
(let ((default-directory dir))
|
||||
(if (executable-find "cairosvg") ; cairo's PDFs are ~10% smaller
|
||||
(let* ((images (directory-files dir nil ".*.svg"))
|
||||
|
@ -11820,7 +11954,8 @@ command to do so for us.
|
|||
(threads 0))
|
||||
(while (< index num-images)
|
||||
(setf threads (1+ threads))
|
||||
(message "Converting emoji %d/%d (%s)" (1+ index) num-images (nth index images))
|
||||
(let (message-log-max)
|
||||
(message "Converting emoji %d/%d (%s)" (1+ index) num-images (nth index images)))
|
||||
(make-process :name "cairosvg"
|
||||
:command (list "cairosvg" (nth index images) "-o" (concat (file-name-sans-extension (nth index images)) ".pdf"))
|
||||
:sentinel (lambda (proc msg)
|
||||
|
@ -11834,17 +11969,6 @@ command to do so for us.
|
|||
(message "Cairosvg not found. Proceeding with inkscape as a fallback.")
|
||||
(shell-command "inkscape --batch-process --export-type='pdf' *.svg"))
|
||||
(message "Finished conversion!")))
|
||||
|
||||
(defun org-latex-emoji-install-vector-graphics--install (dir)
|
||||
(message "Installing vector emojis into emoji directory")
|
||||
(let ((images (directory-files dir t ".*.pdf"))
|
||||
(emoji-dir (file-name-as-directory org-latex-emoji-dir)))
|
||||
(unless (file-exists-p emoji-dir)
|
||||
(make-directory emoji-dir t))
|
||||
(mapc
|
||||
(lambda (image)
|
||||
(rename-file image emoji-dir t))
|
||||
images)))
|
||||
#+end_src
|
||||
|
||||
**** Remove non-ascii chars
|
||||
|
@ -12125,8 +12249,9 @@ export to a PDF.
|
|||
|
||||
#+name: org-missing-latex-packages
|
||||
#+begin_src emacs-lisp :noweb-ref none :var org-latex-required-packages-list=org-latex-required-packages-list[,0] :var org-latex-font-packages-list=org-latex-font-packages-list
|
||||
(setq org-required-latex-packages (append org-latex-required-packages-list
|
||||
(mapcar #'car org-latex-font-packages-list)))
|
||||
(setq org-required-latex-packages
|
||||
(append org-latex-required-packages-list
|
||||
org-latex-font-packages-list))
|
||||
|
||||
(defun check-for-latex-packages (packages)
|
||||
(delq nil (mapcar (lambda (package)
|
||||
|
|
Loading…
Reference in New Issue