Related-to: https://list.orgmode.org/ca+hmbpnfxe5cick5cw19_enfj2_gp84xxv15coxqlkcn-8n...@mail.gmail.com/
I canceled the above patch because I realized that it would be difficult to explain what I was trying to fix without implementing the whole idea. When I looked into `org-html-link-home' and `org-html-link-use-abs-url' as options for verbatim link export for ox-html and ox-md, I saw that verbatim link export had already been implemented for ox-publish. For ox-publish, if `org-html-link-home' and `org-html-link-use-abs-url' are set then any files URIs that resolve to your project's base-directory will be converted to relative URIs and then the value of `org-html-link-home' will be appended to these URIs. So, I thought that the easiest way to implement verbatim link export. But there was one issue. Before this patch, if you set `org-html-link-home' and `org-html-link-use-abs-url' without setting a base-directory, the value of `org-html-link-home' would still be appended to the file uri. For example, when `org-html-link-use-abs-url' is set to t,`org-html-link-home' is set to "https://www.example.com" value and the ':base-directory` key of `org-publish-project-alist' has not been set, you would expect `org-html-link' to return: <a href=\"file:///my/project/en/install-emacs-on-android">Install Emacs</a> when given: [[/my/project/en/install-emacs-on-android][Install Emacs]] Instead `org-html-link' returns: <a href=\"https://www.example.com/file:///my/project/install-emacs-on-android\">install Emacs</a></p>. My patch introduces a new custom variable named `org-html-export-directory'. With `org-html-export-directory' set to "/my/project", `org-html-link-use-abs-url' set to t and `org-html-link-home' set to "https://www.example.com", `org-html-link' will return: <a href=\"https://www.example.com/en/install-emacs-on-android\">Install Emacs</a></p> when given: [[/my/project/en/install-emacs-on-android][Install Emacs]] Additionally, if you set `org-html-link-use-abs-url' to t, `org-html-link-home' to "https://www.example.com" and you forgot or choose to not set `org-html-export-directory', `org-html-link' will return: <a href=\"file:///my/project/en/install-emacs-on-android">Install Emacs</a> when given: [[/my/project/en/install-emacs-on-android][Install Emacs]] In summary, `org-html-export-directory' allows you to set the base-directory key of `org-publish-project-alist' even when you are not publishing. But if you forgot or choose to not set that variable, a valid link will still be exported even when you have set `org-html-link-use-abs-url' and `org-html-link-home' to non-nil values.
From ef88445054ae03540e12ff258d4c152dfbe707df Mon Sep 17 00:00:00 2001 From: ApollonDeParnasse <[email protected]> Date: Tue, 30 Jun 2026 16:07:54 -0500 Subject: [PATCH] lisp/ox-html: New custom variable for the file link export process * lisp/ox-html.el (org-html-export-directory): New custom variable for the file link export process. (org-html--create-file-link-path): Create file link paths for `org-html-link'. (org-html-link): Have `org-html--create-file-link-path' create file link paths. * testing/lisp/test-ox-html.el (test-ox-html-create-test-link-element): Helper function for `org-html-link' tests. (ox-html/test-org-html-link-use-abs-url): New tests for `org-html-link'. (ox-html/test-link-home-and-use-abs-url/no-base-directory): New tests for `org-html-link'. --- etc/ORG-NEWS | 18 +++++ lisp/ox-html.el | 51 +++++++++---- testing/lisp/test-ox-html.el | 135 +++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 15 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index bb965c02a..36f65bc57 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -251,6 +251,24 @@ non-org-mode files, or any other place where org can not use the link context to create a description. See its docstring for more information. +*** New custom variable ~ox-html-export-directory~ + +This option, nil by default, allows you to set the directory +of the project that you intend to export. This variable is the +equivalent of the ~:base-directory~ key of ~org-publish-project-alist~. +It is intended for people who use ox-html simply for exporting as +opposed to publishing. When used in conjunction with ~org-html-link-home~ +and ~org-html-link-use-abs-url~, file links to files in your export directory +will have the value of ~org-html-link-home~ appended to them. For example, +when ~org-html-export-directory~ is set to =~/my/project=, ~org-html-link-home~ +is set to =https://orgmode.org= and ~org-html-link-use-abs-url~ is set to ~t~, +~[[~/my/project/contribute][Contributing to Org]]~ will be exported as + +#+begin_example +"<a href=\"https://orgmode.org/contribute\">Contributing to Org</a>" +#+end_example + + ** New functions and changes in function arguments # This also includes changes in function behavior from Elisp perspective. diff --git a/lisp/ox-html.el b/lisp/ox-html.el index 74fd198e2..601a2d059 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -122,6 +122,7 @@ (:html-link-use-abs-url nil "html-link-use-abs-url" org-html-link-use-abs-url) (:html-link-home "HTML_LINK_HOME" nil org-html-link-home) (:html-link-up "HTML_LINK_UP" nil org-html-link-up) + (:html-export-directory "HTML_EXPORT_DIRECTORY" nil org-html-export-directory) (:html-mathjax "HTML_MATHJAX" nil "" space) (:html-equation-reference-format "HTML_EQUATION_REFERENCE_FORMAT" nil org-html-equation-reference-format t) (:html-postamble nil "html-postamble" org-html-postamble) @@ -1501,6 +1502,13 @@ ignored." :group 'org-export-html :type 'string) +(defcustom org-html-export-directory nil + "Directory of the project that you intend to export. +This variable is the equivalent of the `:base-directory' +key of `org-publish-project-alist'." + :group 'org-export-html + :type 'string) + ;;;; Template :: Scripts (defcustom org-html-head-include-scripts nil @@ -3325,6 +3333,31 @@ images, set it to: info nil 'link) (= link-count 1)))))) +(defun org-html--create-file-link-path (raw-path info) + "Convert RAW-PATH into a HTML file link path. +During publishing, turn absolute file names belonging to +base directory into relative file names. Otherwise, +append `file' protocol to absolute file name. INFO +should be the export options, as a plist." + (let* ((export-directory (plist-get info :html-export-directory)) + (updated-info (if export-directory + (plist-put info + :base-directory export-directory) + info)) + (file-relative-name (org-publish-file-relative-name + raw-path updated-info)) + (home (and (plist-get info :html-link-home) + (org-trim (plist-get info :html-link-home))))) + ;; Possibly append `:html-link-home' to relative file + ;; name. + (if (and home + (plist-get info :html-link-use-abs-url) + (not (file-name-absolute-p file-relative-name))) + (file-name-concat + (file-name-as-directory home) + file-relative-name) + (org-export-file-uri file-relative-name)))) + (defun org-html-link (link desc info) "Transcode a LINK object from Org to HTML. DESC is the description part of the link, or the empty string. @@ -3350,22 +3383,10 @@ INFO is a plist holding contextual information. See (path (cond ((string= "file" type) - ;; During publishing, turn absolute file names belonging - ;; to base directory into relative file names. Otherwise, - ;; append "file" protocol to absolute file name. - (setq raw-path - (org-export-file-uri - (org-publish-file-relative-name raw-path info))) - ;; Possibly append `:html-link-home' to relative file - ;; name. - (let ((home (and (plist-get info :html-link-home) - (org-trim (plist-get info :html-link-home))))) - (when (and home - (plist-get info :html-link-use-abs-url) - (not (file-name-absolute-p raw-path))) - (setq raw-path (concat (file-name-as-directory home) raw-path)))) ;; Maybe turn ".org" into ".html". - (setq raw-path (funcall link-org-files-as-html-maybe raw-path info)) + (setq raw-path (funcall link-org-files-as-html-maybe + (org-html--create-file-link-path raw-path info) + info)) ;; Add search option, if any. A search option can be ;; relative to a custom-id, a headline title, a name or ;; a target. diff --git a/testing/lisp/test-ox-html.el b/testing/lisp/test-ox-html.el index 717838105..6a28525c0 100644 --- a/testing/lisp/test-ox-html.el +++ b/testing/lisp/test-ox-html.el @@ -1260,4 +1260,139 @@ entirely." (org-export-string-as "" 'html nil '( :html-doctype "xhtml5" :html-klipsify-src t))))) + +;;; Rendering Links + +(defun test-ox-html-create-test-link-element (test-link-path + &optional test-desc) + "Helper function for `ox-html-link' tests. +Uses TEST-LINK-PATH and TEST-DESC to create a file link. +That file link will then be converted into an org-element." + (let ((test-desc (if test-desc + (format "[[file:%s][%s]]" + test-link-path + test-desc) + (format "[[file:%s]]" + test-link-path)))) + (org-test-with-temp-text test-desc + (org-element-link-parser)))) + +(ert-deftest ox-html/test-ox-html-export-directory () + "Test `org-html-export-directory'." + ;; file is not in export directory + (org-test-with-temp-text-in-file "" + (let* ((test-dir (file-name-parent-directory buffer-file-name)) + (test-home "https://www.example.com") + (test-file-name "/en/install-emacs-on-android") + (test-link-element (test-ox-html-create-test-link-element + test-file-name + nil)) + (expected-link-path (format "file://%s" test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-export-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-dir (file-name-parent-directory buffer-file-name)) + (test-home "https://www.notabug.com") + (test-file-name "/examples/babel.html") + (test-link-element (test-ox-html-create-test-link-element test-file-name)) + (expected-link-path (format "file://%s" test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-export-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link)))) + + ;; file is in export directory + (org-test-with-temp-text-in-file "" + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-dir (file-name-parent-directory buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element + buffer-file-name + test-desc)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :html-export-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (org-test-with-temp-text-in-file "" + (let* ((test-home "https://mywebsite.com") + (test-dir (file-name-parent-directory buffer-file-name)) + (test-file-name (file-name-nondirectory buffer-file-name)) + (test-link-element (test-ox-html-create-test-link-element buffer-file-name)) + (expected-link-path + (file-name-concat test-home test-file-name)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-export-directory test-dir + :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link))))) + +(ert-deftest ox-html/test-link-home-and-use-abs-url/no-base-directory () + "Test `org-html-link-use-abs-url' with `org-html-link-home'." + (ert-with-temp-file test-link-path + (let* ((test-desc "Contributing to Org") + (test-home "https://orgmode.org") + (test-link-element (test-ox-html-create-test-link-element + test-link-path + test-desc)) + (expected-link-path (format "file://%s" test-link-path)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + test-desc)) + (test-info (list :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + test-desc + test-info))) + (should (string-equal actual-link expected-link)))) + + (ert-with-temp-file test-link-path + (let* ((test-home "https://mywebsite.com") + (test-link-element (test-ox-html-create-test-link-element test-link-path)) + (expected-link-path (format "file://%s" test-link-path)) + (expected-link (format "<a href=\"%s\">%s</a>" + expected-link-path + expected-link-path)) + (test-info (list :html-link-use-abs-url t + :html-link-home test-home)) + (actual-link (org-html-link + test-link-element + nil + test-info))) + (should (string-equal actual-link expected-link))))) + ;;; test-ox-html.el ends here -- 2.54.0
