Skip to content

Latest commit

 

History

History
302 lines (267 loc) · 10.4 KB

File metadata and controls

302 lines (267 loc) · 10.4 KB

Tufte-style annotated html export in org-mode

Sample text with annotations

This is some sample text with annotations.[fn::This is an annotation, aka a “footnote”.] “Footnotes” actually appear on the side when exported to HTML.

Derived backend for export

(setq org-html-head-include-default-style t)
(setq org-html-htmlize-output-type nil)

We’ll define a new backend derived from html.

(org-export-define-derived-backend 'annotated-html 'html
:translate-alist '((footnote-reference . annotated-footnote-reference)
                   (src-block . pygments-org-html-code)
                   (example-block . pygments-org-html-code)))

The following function has been clobbered together from code inspired by ox-tufte and jnboehm’s Tufte CSS backend.

(defun annotated-footnote-reference (footnote-reference _contents info)
  "Transcode a FOOTNOTE-REFERENCE element from Org to HTML.
      CONTENTS is nil.  INFO is a plist holding contextual information."
  (concat
   ;; Insert separator between two footnotes in a row.
   (let ((prev (org-export-get-previous-element footnote-reference info)))
     (when (eq (org-element-type prev) 'footnote-reference)
       (plist-get info :html-footnote-separator)))
   (let* ((n (org-export-get-footnote-number footnote-reference info))
          (fnr-id (format "fnr.%d%s" n
                          (if (org-export-footnote-first-reference-p
                               footnote-reference info)
                              ""
                            ".100")))
          (fn-id (format "fn.%d" n))
          (raw-text (org-trim
                     (org-export-data
                      (org-export-get-footnote-definition footnote-reference info)
                      info)))
          ;; footnotes must have spurious <p> tags removed or they will not work
          (text (replace-regexp-in-string "</?p.*>" "" raw-text)))
     (concat
      ;; Format the footnote reference in the main text.
      (format
       ;; Format as per :html-footnote-format (typically within <sup> tags).
       (plist-get info :html-footnote-format)
       ;; Construct a link to the actual footnote.
       (org-html--anchor
        fnr-id n
        (format " class=\"footref\" href=\"#%s\" role=\"doc-backlink\" onmouseover=\"textHLOn(this, '%s')\" onmouseout=\"textHLOff(this, '%s')\"" fn-id fn-id fn-id) info))

      ;; Format the footnote number and text.
      (format
       "<span class=\"annot\" id=\"%s\" onmouseover=\"sidenoteHLOn(this,'%s')\" onmouseout=\"sidenoteHLOff(this, '%s')\">%d. %s</span>"
       fn-id
       fnr-id
       fnr-id
       n
       text)
      ))))

The following function is based on code from Anton Linevych’s blog.

(defvar pygments-path "pygmentize")

(defun pygments-org-html-code (code contents info)
  ;; Generating tmp file path.
  ;; Current date and time hash will ideally pass our needs.
  (setq temp-source-file (format "/tmp/pygmentize-%s.txt"(md5 (current-time-string))))
  ;; Writing block contents to the file.
  (with-temp-file temp-source-file (insert (org-element-property :value code)))
  (let* ((lang (or (org-element-property :language code) ""))
         ;; Executing the shell-command and reading an output
         (pygments-output (shell-command-to-string
                          (format "%s -l \"%s\" -f html %s"
                                  pygments-path
                                  lang
                                  temp-source-file)))
         (pre-replacement (if (string-equal lang "")
                              "<pre class=\"src\""
                            (format "<pre class=\"src src-%s\"" lang))))
    ;; Add language name to the <pre> tag. Now displays language name on hover.
    (replace-regexp-in-string "<pre>" pre-replacement pygments-output)))

Export functions for the new backend. Once again inspired by ox-tufte and jnboehm’s Tufte CSS backend.

<<derived-backend>>
<<annotated-footnote-reference>>
<<pyments-org-html-code>>
(defun org-annotated-export-to-buffer
    (&optional async subtreep visible-only body-only ext-plist)
  "Export current buffer as an html buffer with annotation customisations.
    If narrowing is active in the current buffer, only export its
    narrowed part.
    If a region is active, export that region.
    A non-nil optional argument ASYNC means the process should happen
    asynchronously.  The resulting buffer should be accessible
    through the `org-export-stack' interface.
    When optional argument SUBTREEP is non-nil, export the sub-tree
    at point, extracting information from the headline properties
    first.
    When optional argument VISIBLE-ONLY is non-nil, don't export
    contents of hidden elements.
    When optional argument BODY-ONLY is non-nil, only write code
    between the bo
    EXT-PLIST, when provided, is a property list with external
    parameters overriding Org default settings, but still inferior to
    file-local settings.
    Export is done in a buffer named \"*Org annotated html export*\", which
    will be displayed when `org-export-show-temporary-export-buffer'
    is non-nil."
  (interactive)
  (org-export-to-buffer 'annotated-html "*Org annotated html export*"
    async subtreep visible-only body-only ext-plist (lambda () (html-mode))))

(defun org-annotated-export-to-file (&optional async subtreep visible-only)
  "Export current buffer to an annotated HTML file.

  If narrowing is active in the current buffer, only export its
  narrowed part.

  If a region is active, export that region.

  A non-nil optional argument ASYNC means the process should happen
  asynchronously.  The resulting file should be accessible through
  the `org-export-stack' interface.

  When optional argument SUBTREEP is non-nil, export the sub-tree
  at point, extracting information from the headline properties
  first.

  When optional argument VISIBLE-ONLY is non-nil, don't export
  contents of hidden elements.

  Return output file's name."
  (interactive)
  (let ((outfile (org-export-output-file-name ".html" subtreep))
        ;; need to bind this because we don't want to display list of footnotes
        ;; at the bottom
        (org-html-footnotes-section "<!-- %s --><!-- %s -->"))
    (org-export-to-file 'annotated-html outfile async subtreep visible-only)))

Stylesheet

Convert the chosen pygments style (in the variable style) to css. We have (once again) used gruvbox-light.

pygmentize -S $style -f html -a .highlight

Basic CSS stylesheet to include in the html headers. We begin with some variables to set up the theme, which is based on gruvbox. We then override the default css used by ox-html.

@media screen and (max-width: 480px) {
    body {
        max-width:95%;
        font-size:90%;
    }
    #content { margin-right: 0em;}
    .annot { display: block;}
}
@media screen and (min-width: 480px) {
    body {
        max-width:80%;
        font-size:90%
    }
    #content {
        margin-right:20%;
    }
    .annot {float: right;
            clear: right; margin-left: 5%; margin-right: -25%; width: 20%;}  
}
@media screen and (min-width: 550px) {
    body {
    }
}
@media screen and (min-width: 1000px) {
    body {
        max-width:1000px;
    }
    #content {
        margin-right:30%;
    }
    .annot {float: right;
            clear: right; margin-left: 5%; margin-right: -35%; width: 30%;}  
}
body {
    font-family: 'Roboto Slab', sans-serif;
    background: <<bg-color>>;
    color: <<fg-color>>;
    margin-top: 0em;
    margin-left:auto;
    margin-right:auto;
}

.title {
    text-align: left;
    margin: 0em;
    padding: 0.5em 0em;
}
h1 > .title {
    font-size: 110%;
}

* a {
    color: <<link-color>>;
    text-decoration: none;
}

* a:hover {
    text-decoration: underline;
}
h1, h2, h3, h4 {

}

ul {
    padding-left: 1em;
}

ol {
    padding-left: 1em;
}
.annot {
    font-size: 75%;
}

pre {
    background-color: inherit;
    border: none;
    margin: 0;
}

.highlight {
    border: solid 1px <<link-color>>75;
}

.todo, .done, .priority {
    font-size: 90%;
    font-weight: bold;
}

Javascript

This section will contain scripts that do fancy things when we hover over annotations.

Produce HTML headers

Output HTML headers containing the stylesheet and (eventually) javascript.

(concat "<style>"
        "
<<pygments-style()>>
<<annotated-css>>
"
        "</style>")

Wrapper

I am not entirely sure why this is necessary. This StackOverflow post has more details. This won’t render in html for now because I have not added org support to pygments.

<<html-headers>>