;; -*- lexical-binding: t; -*- ;;; yekneb-pandoc.el --- Functions for working with pandoc. ;; Copyright © 2021 - 2025 Ben Key ;; This file is part of the yekneb package. ;; ;; The yekneb package 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. ;; ;; The yekneb package 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 ;; the yekneb package. If not, see . (require 'yekneb-debug) (require 'yekneb-file-utilities) (require 'yekneb-process) ;;;###autoload (defvar yekneb-pandoc--load-file--directory (file-name-directory (expand-file-name load-file-name)) "The path of the directory containing this package.") (defun yekneb-find-pandoc () "This function attempts to obtain the path of pandoc.exe." (cond ((yekneb-executable-find "pandoc") (yekneb-executable-find "pandoc") ) ((and (getenv "LOCALAPPDATA") (file-exists-p (substitute-in-file-name "${LOCALAPPDATA}/Pandoc/pandoc.exe"))) (substitute-in-file-name "${LOCALAPPDATA}/Pandoc/pandoc.exe") ) ((file-exists-p (substitute-in-file-name "/mnt/c/Users/${USER}/AppData/Local/Pandoc/pandoc.exe")) (substitute-in-file-name "/mnt/c/Users/${USER}/AppData/Local/Pandoc/pandoc.exe") ) ((and (getenv "ProgramFiles(x86)") (file-exists-p (substitute-in-file-name "${ProgramFiles(x86)}/Pandoc/pandoc.exe"))) (substitute-in-file-name "${ProgramFiles(x86)}/Pandoc/pandoc.exe") ) ((and (getenv "ProgramFiles") (file-exists-p (substitute-in-file-name "${ProgramFiles}/Pandoc/pandoc.exe"))) (substitute-in-file-name "${ProgramFiles}/Pandoc/pandoc.exe") ) ) ) (defun yekneb-get-pandoc-template-path (template-file-name) "Obtain the path to the TEMPLATE-FILE-NAME file. It will first search for a project local file. If the project local file could not be found, it will return the path to the html5.template file in the `user-emacs-directory'." (let ((current-directory (yekneb-get-default-directory)) (ret nil)) (setq ret (cond ((and current-directory (file-exists-p (expand-file-name template-file-name current-directory))) (expand-file-name template-file-name current-directory) ) ((and current-directory (locate-dominating-file current-directory template-file-name)) (expand-file-name template-file-name (locate-dominating-file current-directory template-file-name)) ) ((locate-user-emacs-file template-file-name) (expand-file-name (locate-user-emacs-file template-file-name)) ) ((and yekneb-pandoc--load-file--directory (file-exists-p (expand-file-name template-file-name yekneb-pandoc--load-file--directory))) (expand-file-name template-file-name yekneb-pandoc--load-file--directory) ) ) ) ret ) ) (defun yekneb-get-pandoc-template-path-l (template-file-names) "Obtain the path to the TEMPLATE-FILE-NAME file. It will first search for a project local file. If the project local file could not be found, it will return the path to the html5.template file in the `user-emacs-directory'." (let ( (function-name "yekneb-get-pandoc-template-path-l") (current-directory (yekneb-get-default-directory)) (ret nil) ) (dolist (template-file-name template-file-names) (when (null ret) (yekneb-log yekneb-log-debug "%s status: Looking for %s in the current-directory." function-name template-file-name) (when (and current-directory (file-exists-p (expand-file-name template-file-name current-directory)) ) (setq ret (expand-file-name template-file-name current-directory)) ) ) ) (when (null ret) (dolist (template-file-name template-file-names) (yekneb-log yekneb-log-debug "%s status: Looking for %s using locate-dominating-file." function-name template-file-name) (when (and (null ret) current-directory (locate-dominating-file current-directory template-file-name) ) (setq ret (expand-file-name template-file-name (locate-dominating-file current-directory template-file-name))) ) ) ) (when (null ret) (dolist (template-file-name template-file-names) (yekneb-log yekneb-log-debug "%s status: Looking for %s using locate-user-emacs-file." function-name template-file-name) (when (and (null ret) (locate-user-emacs-file template-file-name)) (setq ret (expand-file-name (locate-user-emacs-file template-file-name))) ) ) ) (when (null ret) (dolist (template-file-name template-file-names) (yekneb-log yekneb-log-debug "%s status: Looking for %s in the load file directry." function-name template-file-name) (when (and (null ret) yekneb-pandoc--load-file--directory (file-exists-p (expand-file-name template-file-name yekneb-pandoc--load-file--directory)) ) (setq ret (expand-file-name template-file-name yekneb-pandoc--load-file--directory)) ) ) ) ret ) ) (defun yekneb-get-custom-reference-doc-path () "Obtain the path to the custom-reference.docx file. It will first search for a project local file. If the project local file could not be found, it will return the path to the custom-reference.docx file in the `user-emacs-directory'." (yekneb-get-pandoc-template-path "custom-reference.docx") ) (defun yekneb-get-html5-template-path () "Obtain the path to the html5.template file. It will first search for a project local file. If the project local file could not be found, it will return the path to the html5.template file in the `user-emacs-directory'." (yekneb-get-pandoc-template-path-l '("html5.template" "default.html5")) ) (defun yekneb-get-context-template-path () "Obtain the path to the html5.template file. It will first search for a project local file. If the project local file could not be found, it will return the path to the html5.template file in the `user-emacs-directory'." (yekneb-get-pandoc-template-path-l '("context.template" "default.context")) ) ;;;###autoload (defun yekneb-pandoc-html-to-markdown () "Use Pandoc to convert HTML to Markdown." (interactive) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--variable" "lang=en" "-f" "html+raw_html" "-t" "markdown" "--atx-headers" "--reference-links" "-o" (concat (file-name-sans-extension buffer-file-name) ".md") buffer-file-name ) ) ;;;###autoload (defun yekneb-pandoc-markdown-to-docx () "Use Pandoc to convert Markdown to DOCX." (interactive) (let ((template (yekneb-get-custom-reference-doc-path))) (if (and template (file-exists-p template)) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--variable" "lang=en" "-f" "markdown" "-t" "docx" (format "--reference-doc=%s" template) "-o" (concat (file-name-sans-extension buffer-file-name) ".docx") buffer-file-name ) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--variable" "lang=en" "-f" "markdown" "-t" "docx" "-o" (concat (file-name-sans-extension buffer-file-name) ".docx") buffer-file-name ) ) ) ) ;;;###autoload (defun yekneb-pandoc-markdown-to-html () "Use Pandoc to convert Markdown to HTML." (interactive) (let ((template (yekneb-get-html5-template-path))) (if (and template (file-exists-p template)) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--variable" "lang=en" "-f" "markdown" "-t" "html5" (format "--template=%s" template) "--mathjax" "-s" "-o" (concat (file-name-sans-extension buffer-file-name) ".html") buffer-file-name ) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--variable" "lang=en" "-f" "markdown" "-t" "html5" "--mathjax" "-s" "-o" (concat (file-name-sans-extension buffer-file-name) ".html") buffer-file-name ) ) ) ) ;;;###autoload (defun yekneb-pandoc-markdown-to-pdf () "Use Pandoc to convert Markdown to PDF." (interactive) (let ((template (yekneb-get-context-template-path))) (if (and template (file-exists-p template)) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--metadata" "pdfa:2a" "--variable" "context-lang=en" "--variable" "fontsize=16pt" "--variable" "lang=en" "--variable" "linkstyle=bold" "--variable" "papersize=letter" (format "--template=%s" template) "-f" "markdown" "--pdf-engine" "context" "-o" (concat (file-name-sans-extension buffer-file-name) ".pdf") buffer-file-name ) (yekneb-exec-and-output-to-buffer (yekneb-find-pandoc) "*pandoc output*" "--metadata" "pdfa:2a" "--variable" "context-lang=en" "--variable" "fontsize=16pt" "--variable" "lang=en" "--variable" "linkstyle=bold" "--variable" "papersize=letter" "-f" "markdown" "--pdf-engine" "context" "-o" (concat (file-name-sans-extension buffer-file-name) ".pdf") buffer-file-name ) ) ) ) (provide 'yekneb-pandoc)