orgmk icon indicating copy to clipboard operation
orgmk copied to clipboard

Streamline your Org document exports to PDF, HTML, DOC, and more with OrgMk. Simplify document compilation and enhance productivity. #orgmode #productivity #automation

#+TITLE: Export Org mode documents to HTML, PDF, etc. #+AUTHOR: Fabrice Niessen #+EMAIL: (concat "fniessen" at-sign "pirilampo.org") #+DESCRIPTION: Orgmk completely automates the export of Org documents. #+KEYWORDS: org-mode, orgmk, export, automate, emacs, pdf, latex, html #+LANGUAGE: en #+OPTIONS: H:4 toc:t num:nil

#+PROPERTY: header-args :eval no :noweb yes :comments no #+SETUPFILE: ~/org/theme-readtheorg.setup

#+begin_export html :license-gpl-blue.svg

btn_donate_LG.gif #+end_export
  • Overview

** Description

Orgmk is a suite of Bash scripts for automating the conversion of Org mode documents to different formats (such as HTML or PDF) from the command line.

#+begin_export html

Tweet #+end_export

The scripts run on both Unix and Microsoft Windows (with Cygwin).

** Objectives

The first goal is to be more productive, by running the export only when the source Org files are updated.

The second goal is to share some common Emacs and Org configuration inside your team, separately of what you have in your personal Emacs configuration file.

Finally, the third goal is also to offload compilation into an external batch Emacs process, allowing you to go on editing or working while exporting the documents.

** Quotations

"If one has been trying to bulk convert many quickly changing org-documents, [one] will appreciate the power and flexibility of this tool." \ -- /z27/

"Orgmk is a wonderful tool, and, as far as I know, still unique." \ -- /Priyadarshan/

** Requirements :PROPERTIES: :header-args+: :tangle no :END:

*** Emacs 24+

Emacs version 24 (for the ~package~ facility) must be in your =PATH=.

*** Org 8+

Org mode version 8 (or later) is required.

If such a version is not bundled with your Emacs, Org 8 will be automatically installed -- if you accept it! -- through ELPA.

*** LaTeX (PDF)

A working LaTeX installation is required for exporting to PDF. If it is not yet installed on your system, install [[http://www.tug.org/texlive/][TeX Live]] (for example).

  • Usage

Org files can have the extension ~.org~ or ~.txt~.

** Standalone scripts

  • For "weaving" files (producing the formatted documentation files):

    #+begin_src shell org2html [OPTION] FILE org2latex [OPTION] FILE org2pdf [OPTION] FILE org2beamerpdf [OPTION] FILE org2odt [OPTION] FILE org2txt [OPTION] FILE #+end_src

  • For "tangling" files:

    #+begin_src shell org-tangle FILE #+end_src

** Makefile wrapper

#+begin_src shell orgmk [OPTION] orgmk [OPTION] [html | pdf] orgmk [OPTION] [FILE] #+end_src

Currently, =orgmk= does not tangle Org files.

  • Options

** Standalone scripts

  • =-h=, =--help= :: Print usage information and exit.

  • =-y= :: Authorize the update of the source file (update of dynamic blocks and re-computation of tables) without confirmation.

    Before the conversion, the standalone scripts will try to update all dynamic blocks and recompute all tables in the source Org file, a potentially destructive operation for which the programs require confirmation: in case the source file gets updated, you will be queried to overwrite the source file with the updated version of the file -- that's because the environment variable =UPDATE_SRC= defaults to =query=.

    To avoid the question, you can also set that variable to =yes= or =no= in a personal configuration file, =~/.orgmk-rc=.

    Note that, by design, the command line argument =-y= is passed to every =org2<BACKEND>= script called from =orgmk=, so that =orgmk= doesn't wait for answers.

  • =--body-only= :: Export only body (for HTML and LaTeX).

** Makefile wrapper

  • =-h=, =--help= :: Print usage information and exit.

  • =-r=, =--recursive= :: Export all Org files under each directory, recursively.

    It just calls =orgmk.mk= with the argument =ALLSUBDIRS=yes=.

    By default, =orgmk= exports all Org files (which need it) in the current directory only.

  • Files

[[~/.orgmk-rc]]

  • Examples

** Update all documents

Regenerate HTML and/or PDF files for Org files that have been modified.

#+begin_src shell orgmk #+end_src

** Update specific types of documents

Regenerate HTML only for the Org files that have been changed.

#+begin_src shell orgmk html #+end_src

** Update specific document

Regenerate (or create for the first time) the file ~thesis.pdf~ from the Org file ~thesis.org~ or ~thesis.txt~. Don't require confirmation for updating the source file.

#+begin_src shell orgmk thesis.pdf #+end_src

Regenerate (or create for the first time) the file ~thesis.pdf~ from the Org file ~thesis.org~. Require confirmation for updating the source file.

#+begin_src shell org2pdf thesis.org #+end_src

  • Installation

The installation process consists of creating symbolic links from the standard final directories on your machine to the =orgmk= executables.

** Installing from the Git repository

See one-line for installation at https://github.com/tj/git-extras

  1. Extract the latest version of =orgmk=.

    #+begin_src shell git clone [email protected]:fniessen/orgmk.git #+end_src

  2. Create configuration files which record where =orgmk= has been extracted on your host machine.

    #+begin_src shell make #+end_src

  3. By default, the =orgmk= executable and the standalone scripts (=org2html=, =org2pdf=, etc.) are installed to the ~/usr/local/bin~ so that all users are able to run them.

    If you want to change the destination directory, create a ~Make.params~ file with the target directory. For example:

    #+begin_src makefile BIN_DIR=~/bin #+end_src

    If needed, add that directory to your =PATH=.

    Install =orgmk= (executable and standalone scripts that you run) to the =BIN_DIR= location by typing:

    #+begin_src shell make install #+end_src

At this point, everything should be ready for use.

** Makefile for installation :PROPERTIES: :header-args+: :shebang "" :tangle Makefile :END:

#+begin_src makefile

-- Makefile -- definition file for Orgmk

To install orgmk', type make' and then `make install'.

PWD=$(shell pwd)

BIN_DIR=/usr/local/bin

-include Make.params

ORGMK_ROOT=$(PWD) ORGMK_EL=$(ORGMK_ROOT)/site-lisp/orgmk.el

WARNING -- ORGMK_EL must contain the correct path to the `orgmk.el' file

according to the Emacs version used (Windows, Cygwin or Linux path).

EMACS_SYSTEM_TYPE=$(shell emacs -batch --eval "(message "%s" system-type)" 2>&1) ifeq ($(EMACS_SYSTEM_TYPE),windows-nt) ORGMK_EL:="$(shell cygpath --mixed $(ORGMK_EL))" endif

ORGMK_INIT=orgmk-init ORGMK_SYSTEM_CONFIG=orgmk.conf ORGMK_UPDATE_CHECK_DIFF=orgmk-update-src-check-diff ORG2HTML=org2html ORG2GFM=org2gfm ORG2MD=org2md

ORG2REVEAL=org2reveal

ORG2LATEX=org2latex ORG2PDF=org2pdf ORG2BEAMERPDF=org2beamerpdf ORG2ODT=org2odt ORG2TXT=org2txt ORGTANGLE=org-tangle ORGMK_MAKE_SETUP=orgmk-stow-orgmk.mk ORGMK_MAKE_RUN=orgmk

MAKE

Ensure `all' is the default target

all: bin/$(ORGMK_SYSTEM_CONFIG) bin/$(ORGMK_MAKE_SETUP) # Create Orgmk system files

bin/$(ORGMK_SYSTEM_CONFIG): # Create file with location of `orgmk.el' @echo "Generating system-wide configuration file..." echo "ORGMK_EL=$(ORGMK_EL)" > bin/$(ORGMK_SYSTEM_CONFIG) @echo

bin/$(ORGMK_MAKE_SETUP): # Create core file for `orgmk' @echo "Generating setup file..." echo "#!/bin/sh" > bin/$(ORGMK_MAKE_SETUP) echo "ln -f -s $(PWD)/bin/orgmk.mk orgmk.mk" >> bin/$(ORGMK_MAKE_SETUP) chmod u+x bin/$(ORGMK_MAKE_SETUP) @echo

MAKE INSTALL

.PHONY: install install: all # Create symlinks to Orgmk scripts @echo "Creating symlinks..." ln -f -s $(PWD)/bin/$(ORGMK_INIT) $(BIN_DIR)/$(ORGMK_INIT) ln -f -s $(PWD)/bin/$(ORGMK_SYSTEM_CONFIG) $(BIN_DIR)/$(ORGMK_SYSTEM_CONFIG) ln -f -s $(PWD)/bin/$(ORGMK_UPDATE_CHECK_DIFF) $(BIN_DIR)/$(ORGMK_UPDATE_CHECK_DIFF) ln -f -s $(PWD)/bin/$(ORG2HTML) $(BIN_DIR)/$(ORG2HTML) ln -f -s $(PWD)/bin/$(ORG2GFM) $(BIN_DIR)/$(ORG2GFM) ln -f -s $(PWD)/bin/$(ORG2MD) $(BIN_DIR)/$(ORG2MD) # ln -f -s $(PWD)/bin/$(ORG2REVEAL) $(BIN_DIR)/$(ORG2REVEAL) ln -f -s $(PWD)/bin/$(ORG2LATEX) $(BIN_DIR)/$(ORG2LATEX) ln -f -s $(PWD)/bin/$(ORG2PDF) $(BIN_DIR)/$(ORG2PDF) ln -f -s $(PWD)/bin/$(ORG2BEAMERPDF) $(BIN_DIR)/$(ORG2BEAMERPDF) ln -f -s $(PWD)/bin/$(ORG2ODT) $(BIN_DIR)/$(ORG2ODT) ln -f -s $(PWD)/bin/$(ORG2TXT) $(BIN_DIR)/$(ORG2TXT) ln -f -s $(PWD)/bin/$(ORGTANGLE) $(BIN_DIR)/$(ORGTANGLE) ln -f -s $(PWD)/bin/$(ORGMK_MAKE_SETUP) $(BIN_DIR)/$(ORGMK_MAKE_SETUP) ln -f -s $(PWD)/bin/$(ORGMK_MAKE_RUN) $(BIN_DIR)/$(ORGMK_MAKE_RUN)

Makefile ends here

#+end_src

  • Standalone scripts

** Orgmk-init :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/orgmk-init :END:

The =orgmk-init= script contains the common part used to setup Emacs and Org in order to export to HTML, PDF, etc.

*** Functions

#+begin_src shell Usage () { cat << EOF 1>&2 Usage: $(basename $0) [OPTION] FILE Convert FILE to another format.

-h, --help display this help and exit -y authorize update of the source file without confirmation --body-only export only body (for HTML and LaTeX exports) EOF exit 1 } #+end_src

#+begin_src shell die () { printf "$(basename $0): $@\n" > /dev/stderr exit 2 # an error occurred } #+end_src

#+begin_src shell CleanUp () { # restore backed up target file if it hasn't been produced if [ ! -z $FILE_TARGET ]; then if [ -r $FILE_TARGET.orig ]; then if [ ! -r $FILE_TARGET ]; then printf "Recovering OLD file $FILE_TARGET...\n" mv $FILE_TARGET.orig $FILE_TARGET else rm $FILE_TARGET.orig fi fi fi

# display log file, if present
if [ ! -z $FILE_LOG ]; then
    if [ -r $FILE_LOG ]; then
        grep -e "^!.*" -A 3 -B 1 $FILE_LOG
    fi
fi

# remove the temporary copy of the source Org file
if [ ! -z $FILE_SRC_UPDT ]; then
    rm -f $FILE_SRC_UPDT
fi

} #+end_src

*** Runtime

#+name: current-time #+begin_src emacs-lisp :eval yes :results silent :tangle no (format-time-string "%Y%m%d.%H%M") #+end_src

#+begin_src shell cat << EOF This is $(basename $0), version: <<current-time()>>. EOF #+end_src

#+begin_src shell

perform housekeeping on program exit or a variety of signals

(EXIT, HUP, INT, QUIT, TERM)

trap CleanUp 0 1 2 3 15 #+end_src

#+begin_src shell

load the system-wide config file (definition of `ORGMK_EL')

. orgmk.conf

if [ "$ORGMK_EL" = "" ]; then printf "ORGMK_EL variable not defined, please check your setup!\n" exit 2 fi

source the user config file (default for `UPDATE_SRC', etc.)

ORGMK_USER_CONF=$HOME/.orgmk-rc if [ -r $ORGMK_USER_CONF ]; then . $ORGMK_USER_CONF fi

export UPDATE_SRC

set a default value

export BODY_ONLY="no"

while true; do case "$1" in -y ) UPDATE_SRC="yes"; shift ;; --body-only ) BODY_ONLY="yes"; shift ;; -h | --help ) Usage ;; ,* ) break ;; esac done

now do something with $@

#+end_src

#+begin_src shell if [ $# -ne 1 ]; then Usage fi

FILE_SRC_ORIG=$1 if [ ! -r "$FILE_SRC_ORIG" ]; then die "Cannot read file $FILE_SRC_ORIG" fi

FILE_SRC_UPDT="${FILE_SRC_ORIG%.*}.orgmk" # replace extension

The temporary file only differ by its extension (`.orgmk'), so that the

exported file will be named ..

#+end_src

#+begin_src shell FILE_LOG="${FILE_SRC_ORIG%.*}.log" rm -f $FILE_LOG #+end_src

If the PDF file isn't produced (for example, because of an /undefined control sequence/), there is no PDF file anymore; hence, =orgmk= would ignore the source file in future calls, and the PDF file (now, missing...) would completely disappear from the user attention.

To avoid that, we must make a backup of the target file before the export, and put it back if there is no target file produced.

#+begin_src shell FILE_TARGET="${FILE_SRC_ORIG%.*}.$TARGET_EXT"

back up previous version of target file

if [ -r $FILE_TARGET ]; then mv $FILE_TARGET $FILE_TARGET.orig fi #+end_src

Note that, like with the ~patch~ command, the original file is saved with the =.orig= extension.

#+begin_src shell EMACS=emacs

WHICH_EMACS=$(which $EMACS) EMACS_FLAGS="--eval "(progn (message \"Launching $WHICH_EMACS...\") (message \"Emacs %s (%s)\" emacs-version system-type))"" EMACS_BATCH="emacs --batch -Q $EMACS_FLAGS" ORG_FLAGS="" ORGMK="$EMACS_BATCH $ORG_FLAGS --eval "(message \"Loading ${ORGMK_EL}...\")" -l $ORGMK_EL" ORGMK_UPDATE_FLAGS="-f org-update-all-dblocks -f org-table-iterate-buffer-tables --eval "(write-file \"${FILE_SRC_UPDT##*/}\")"" # base name #+end_src

#+begin_note Batch option \ Font Lock mode is disabled by default in batch mode. That generates errors with some Java code blocks. Hence, one workaround, in that case, is to call Emacs without batch mode (and, then, to add ~-f kill-emacs~, or simply ~--kill~, at the end of the command line). #+end_note

** org2html :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2html :END:

The =org2html= script converts an Org file to HTML.

#+begin_src shell TARGET_EXT=html . orgmk-init

case $BODY_ONLY in "yes") eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-html-export-body-only-to-html
|| die "Exported file wasn't produced" ;; "no") eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-html-export-to-html
|| die "Exported file wasn't produced" esac

orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2latex :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2latex :END:

The =org2latex= script converts an Org file to LaTeX.

#+begin_src shell TARGET_EXT=tex . orgmk-init

case $BODY_ONLY in "yes") eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-latex-export-body-only-to-latex
|| die "Exported file wasn't produced" ;; "no") if grep -E "^#+BEAMER_THEME: " $FILE_SRC_ORIG > /dev/null; then eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-beamer-export-to-latex
|| die "Exported file wasn't produced" else eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-latex-export-to-latex
|| die "Exported file wasn't produced" fi esac

orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2pdf :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2pdf :END:

The =org2pdf= script converts an Org file to PDF.

#+begin_note It tries to detect whether the target is a standard LaTeX document or a Beamer presentation by searching for the string =#+BEAMER_THEME:= inside the Org source. #+end_note

#+begin_src shell TARGET_EXT=pdf . orgmk-init

if grep -E "^#+BEAMER_THEME: " $FILE_SRC_ORIG > /dev/null; then eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-beamer-export-to-latex
|| die "Exported file wasn't produced" else eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-latex-export-to-latex
|| die "Exported file wasn't produced" fi orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2beamerpdf :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2beamerpdf :END:

The =org2beamerpdf= script converts an Org file to a Beamer presentation (PDF).

#+begin_src shell TARGET_EXT=pdf . orgmk-init

eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-beamer-export-to-pdf
|| die "Exported file wasn't produced" orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2odt :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2odt :END:

The =org2odt= script converts an Org file to ODT.

#+begin_src shell TARGET_EXT=odt . orgmk-init

eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-odt-export-to-odt
|| die "Exported file wasn't produced" orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2txt :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2txt :END:

The =org2txt= script converts an Org file to text.

#+begin_src shell TARGET_EXT=txt . orgmk-init

eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-ascii-export-to-ascii
|| die "Exported file wasn't produced" orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+end_src

** org2gfm :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org2gfm :END:

The =org2gfm= script converts an Org file to Github Flavored Markdown.

#+BEGIN_SRC shell TARGET_EXT=md . orgmk-init

eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-gfm-export-to-markdown
|| die "Exported file wasn't produced" orgmk-update-src-check-diff "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" #+END_SRC

** Orgmk-update-src-check-diff :PROPERTIES: :header-args+: :shebang "#!/bin/bash\n" :tangle bin/orgmk-update-src-check-diff :END:

Check whether dynamic blocks and tables have been updated, and (if yes) propose to overwrite the source Org file.

*** Functions

#+begin_src shell ask () { while true; do if [ "${2:-}" = "Y" ]; then prompt="Y/n" default=Y elif [ "${2:-}" = "N" ]; then prompt="y/N" default=N else prompt="y/n" default= fi

    # ask the question (without the need to press RET)
    printf "$1 ($prompt): "
    read -n 1 -r REPLY
    printf "\n" # just a final linefeed

    # default?
    if [ -z "$REPLY" ]; then
        REPLY=$default
    fi

    # check if the reply is valid
    case "$REPLY" in
        y*|Y*) return 0 ;;
        n*|N*) return 1 ;;
    esac
done

} #+end_src

*** Runtime

#+begin_src shell FILE_SRC_ORIG=$1 FILE_SRC_UPDT=$2

set a default value

: ${UPDATE_SRC:="query"}

if [ ! -r "$FILE_SRC_UPDT" ]; then printf "Cannot read file $FILE_SRC_UPDT\n" else if diff -q "$FILE_SRC_ORIG" "$FILE_SRC_UPDT" > /dev/null; then rm "$FILE_SRC_UPDT" else if [[ "$UPDATE_SRC" = "query" ]]; then ask "Update source file?" && UPDATE_SRC="yes" || UPDATE_SRC="no" fi

    if [[ "$UPDATE_SRC" = "yes" ]]; then
        mv "$FILE_SRC_UPDT" "$FILE_SRC_ORIG"
        printf "${C_INFO}Dynamic blocks and tables in \`$FILE_SRC_ORIG' successfully updated${C_RESET}\n"
    else
        printf "Nothing gets updated...\n"
    fi
fi

fi #+end_src

In order to avoid "file changed on disk" messages (and be forced to revert open buffers) when nothing changed, we save the file with another name, and diff it with the original file. If there is no change, the new file is dropped.

#+begin_note The time stamps in "Clock summary at ..." will be different, each time dynamic blocks are updated (if not done during the same minute). In that case, =diff= will always "succeed". #+end_note

** org-tangle :PROPERTIES: :header-args+: :shebang "#!/bin/sh\n" :tangle bin/org-tangle :END:

The =org-tangle= tangles an Org file.

#+begin_src shell TARGET_EXT= . orgmk-init

eval $ORGMK $FILE_SRC_ORIG $ORGMK_UPDATE_FLAGS -f org-babel-tangle
|| die "File wasn't tangled" #+end_src

  • Orgmk

** Orgmk.mk :PROPERTIES: :header-args+: :tangle bin/orgmk.mk :END:

Here is the "ultimate" Makefile to automate the generation of HTML or PDF versions from your Org mode files.

It is designed to solve tedious tasks nicely, such as updating:

  • [X] table of contents,
  • [X] cross-references,
  • [ ] index,
  • [ ] list of figures,
  • [ ] BibTeX references,
  • [X] Org dynamic blocks,
  • [X] tables with formulas, and
  • [ ] extracting (tangle) code blocks.

*************** TODO Test whether Orgmk works with symbolic links Does their modification time change to reflect the latest modification of the source? *************** END

*** Some system-dependent stuff

#+begin_src makefile

-- Makefile -- definition file for Orgmk

#+end_src

**** Default shell

#+begin_src makefile SHELL = /bin/sh #+end_src

**** Exportation scripts

By default, the =orgmk.mk= will overwrite the Org source file if it has changed after dblocks update and tables re-computation.

#+begin_src makefile ORG2_FLAGS=-y

ORG2HTML=org2html $(ORG2_FLAGS) ORG2PDF=org2pdf $(ORG2_FLAGS) #+end_src

**** Viewing stuff

#+begin_src makefile VIEW_HTML ?= firefox VIEW_PDF ?= xpdf #+end_src

**** Color output

#+begin_src makefile :tangle no ifdef NO_COLOR tput = else tput = $(shell $(TPUT) $1) endif

black := $(call tput,setaf 0) red := $(call tput,setaf 1) green := $(call tput,setaf 2) yellow := $(call tput,setaf 3) blue := $(call tput,setaf 4) magenta := $(call tput,setaf 5) cyan := $(call tput,setaf 6) white := $(call tput,setaf 7) bold := $(call tput,bold) uline := $(call tput,smul) reset := $(call tput,sgr0) #+end_src

#+begin_src makefile C_INFO="\e[32m" C_WARNING="\e[35m" C_SUCCESS="\e[1;32m" C_FAILURE="\e[1;31m" C_RESET="\e[0m" #+end_src

*** Basic configuration

**** Org Sources

Discover and process all Org files in the current directory (or in the current subtree).

#+begin_src makefile ifeq ($(ALLSUBDIRS),yes) TXT_FILES=$(shell find . -name *.txt) ORG_FILES=$(shell find . -name *.org) HTML_FILES=$(shell find . -name *.html) PDF_FILES=$(shell find . -name *.pdf) else TXT_FILES=$(shell find . -maxdepth 1 -name *.txt) ORG_FILES=$(shell find . -maxdepth 1 -name *.org) HTML_FILES=$(shell find . -maxdepth 1 -name *.html) PDF_FILES=$(shell find . -maxdepth 1 -name *.pdf) endif

FILES_TO_TANGLE := $(join $(ORG_FILES), $(TXT_FILES)) #+end_src

*************** TODO Add Org_DIRS defaulting to . Use Org_DIRS with foreach (?) in the following lists *************** END

*************** TODO Fix problem of Orgfile with .org/.txt extension If an Org file exists with the 2 file extensions =.txt= and =.org= (same base name), then we get messages such as:

Makefile:102: warning: overriding recipe for target plot.html' Makefile:89: warning: ignoring old recipe for target plot.html' *************** END

**** HTML Targets

List of HTML files which can be generated:

#+begin_src makefile HTML_FILES_FROM_ORG = $(patsubst %.org,%.html,$(ORG_FILES)) HTML_FILES_FROM_TXT = $(patsubst %.txt,%.html,$(TXT_FILES)) #+end_src

List of HTML files to be re-generated (if they have an Org counterpart):

#+begin_src makefile CUR_HTML_FILES := $(HTML_FILES) #+end_src

**** PDF Targets

List of PDF files which can be generated:

#+begin_src makefile PDF_FILES_FROM_ORG = $(patsubst %.org,%.pdf,$(ORG_FILES)) PDF_FILES_FROM_TXT = $(patsubst %.txt,%.pdf,$(TXT_FILES)) #+end_src

List of PDF files to be re-generated (if they have an Org counterpart):

#+begin_src makefile CUR_PDF_FILES := $(PDF_FILES) #+end_src

**** Debugging

#+begin_src makefile

turn command echo'ing back on with VERBOSE=1

ifndef VERBOSE QUIET := @ endif #+end_src

Everything is echo'ed, even =$(shell ...)= invocations:

#+begin_src makefile

turn on shell debugging with SHELL_DEBUG=1

ifdef SHELL_DEBUG SHELL += -x endif #+end_src

**** Commands

Define variables for each command used in targets:

#+begin_src makefile PRINTF=$(QUIET)printf EGREP=$(QUIET)egrep LS=$(QUIET)ls #+end_src

*** All

#+begin_src makefile .PHONY: all all: html pdf # Regenerate all HTML and PDF files #+end_src

*** Help

Get help.

#+begin_src makefile .PHONY: help help: # Display callable targets $(PRINTF) "Usage: orgmk [OPTION]... [TARGET]...\n" > /dev/stderr $(PRINTF) "\n" $(PRINTF) "What target do you want?\n" > /dev/stderr $(EGREP) "^[^ #A-Z]+:" [Mm]akefile
| sed 's/:[^#]*//' | sed 's/^/\n/g' | sed 's/# /\n\t/g' > /dev/stderr #+end_src

*** HTML

=orgmk html= will regenerate all HTML files which currently exist in the directory, and whose source Org file has changed.

#+begin_src makefile .PHONY: html html: $(CUR_HTML_FILES) # Regenerate all HTML documents from Org #+end_src

To create an HTML file for the first time, type =orgmk file.html=. It will ignore other Org files in the directory.

#+begin_src makefile $(HTML_FILES_FROM_ORG): %.html: %.org # Export an Org document to HTML <>

$(HTML_FILES_FROM_TXT): %.html: %.txt # Export an Org document to HTML <> #+end_src

The recipe to convert Org to HTML is:

#+name: recipe-convert-to-html #+begin_src makefile :tangle no $(PRINTF) "$(C_INFO)Exporting `$(CURDIR)/$<' to HTML...$(C_RESET)\n" $(ORG2HTML) $< $(LS) -l -t $< $@ | head -n 1 | grep -E ".html" > /dev/null
|| { printf "$(C_FAILURE)`$(CURDIR)/$@' is NOT up to date$(C_RESET)\n"; exit 1; } $(PRINTF) "$(C_SUCCESS)`$(CURDIR)/$@' successfully generated$(C_RESET)\n" #+end_src

What about =org-export-as-html-batch= and =org-export-as-html-and-open=?

#+begin_src makefile view-html: # Generate the HTML files, then show the result #+end_src

*** PDF

=orgmk pdf= will find out which PDF files have to be regenerated, and will regenerate all of them.

#+begin_src makefile .PHONY: pdf pdf: $(CUR_PDF_FILES) # Regenerate all PDF documents from Org #+end_src

Type =orgmk file.pdf= to generate only the specified PDF file.

#+begin_src makefile $(PDF_FILES_FROM_ORG): %.pdf: %.org # Export an Org document to PDF <>

$(PDF_FILES_FROM_TXT): %.pdf: %.txt # Export an Org document to PDF <> #+end_src

#+begin_note The drawback of updating the dblocks inside the the recipes for HTML and PDF means that, if we have a file that's converted to both file formats, the update will be done twice (so, once at least for nothing): once for HTML, then once again for PDF. Though, this case is quite uncommon. #+end_note

The recipe to convert Org to PDF is:

#+name: recipe-convert-to-pdf #+begin_src makefile :tangle no $(PRINTF) "$(C_INFO)Exporting `$(CURDIR)/$<' to PDF...$(C_RESET)\n" $(ORG2PDF) $< $(LS) -l -t $< $@ | head -n 1 | grep -E ".pdf" > /dev/null
|| { printf "$(C_FAILURE)`$(CURDIR)/$@' is NOT up to date$(C_RESET)\n"; exit 1; } $(PRINTF) "$(C_SUCCESS)`$(CURDIR)/$@' successfully generated$(C_RESET)\n" #+end_src

*************** TODO Export as LaTeX? Maybe the export should be done to TeX, and then call (the minimum number of times) PDFLaTeX from the =orgmk.mk=? Check out if this is as robust as using Org to build the PDF... (which goes on, even with some errors occurring) *************** END

I've observed cases where =orgmk pdf= was reporting that "Exporting to PDF...done", but I was left with the previous PDF file. Hence, every call to =orgmk= tried to redo them...

On deleting the PDF, and relaunching =orgmk= on that file, I had an error during the "Processing LaTeX file".

Workaround, to be sure that an old PDF does not stay in the way, remove the PDF output upon startup of the recipe. The TeX file was well updated. Hence, no need to delete it at startup...

Problem: when the PDF file is not generated anymore, it won't be remade with =orgmk pdf=. We then have to explicitly =orgmk file.pdf=.

*** Clean

#+begin_src makefile .PHONY: clean clean: # Delete all temporary files created by Org export #+end_src

*** TODO Orgmk init file

Here, we should make a symlink, if it does not exist yet. Would then be used just once...

The only thing that's not done yet automatically is: tangling this file!

*** Footer

#+begin_src makefile

orgmk.mk ends here

#+end_src

** Makefile wrapper :PROPERTIES: :header-args+: :tangle bin/orgmk :shebang "#!/bin/sh\n" :END:

As you could want to run the batch scripts from a (development) directory where there is already a ~Makefile~, you must install the ~Makefile~ under a different name: ~orgmk.mk~.

An addition is to create a symbolic link to the ~orgmk.mk~ into the directory of your Org files just for the duration of the script execution (on-the-fly install). The links can be safely removed after =make= has been run.

#+begin_src shell Usage () { cat << EOF 1>&2 Usage: $(basename $0) [OPTION] FILE Export Org source files whenever they are updated.

-h, --help display this help and exit -r, --recursive export all Org files under each directory, recursively

Report $(basename $0) bugs to [email protected] EOF exit 1 } #+end_src

#+begin_src shell CleanUp () { # remove the Makefile rm orgmk.mk }

perform housekeeping on program exit or a variety of signals

(EXIT, HUP, INT, QUIT, TERM)

trap CleanUp 0 1 2 3 15 #+end_src

#+begin_src shell

install the Makefile where you are

orgmk-stow-orgmk.mk

FLAGS="" while true; do case "$1" in -h | --help ) Usage ;; -r | --recursive) FLAGS="ALLSUBDIRS=yes"; shift ;; ,* ) break ;; esac done

make -f orgmk.mk $FLAGS $* #+end_src

  • Orgmk configuration file :PROPERTIES: :header-args+: :tangle site-lisp/orgmk.el :END:

I use a setup file =orgmk.el= to hold minimal customization.

#+begin_info In batch mode, [...] Emacs functions that normally print a message in the echo area will print to either the standard output stream (=stdout=) or the standard error stream (=stderr=) instead. (To be precise, functions like =prin1=, =princ= and =print= print to =stdout=, while =message= and =error= print to =stderr=.) #+end_info

** Common

#+begin_src emacs-lisp ;;; orgmk.el --- Emacs configuration file for `orgmk'

;;; Commentary:

;;; Code: #+end_src

*** Library Search

Remember current directory.

#+begin_src emacs-lisp ;; remember this directory (defconst orgmk-el-directory (file-name-directory (or load-file-name (buffer-file-name))) "Directory path of Orgmk.") #+end_src

*** Feedback

#+begin_src emacs-lisp ;; ;; activate debugging ;; (setq debug-on-error t)

;; no limit when printing values (setq eval-expression-print-length nil) (setq eval-expression-print-level nil) #+end_src

*** Auto Save

#+begin_src emacs-lisp ;; don't make a backup of files (setq backup-inhibited t) #+end_src

*** Initialization for MS Windows

When running an native Microsoft Windows version of Emacs:

#+begin_src emacs-lisp ;; ;; let Emacs recognize Cygwin paths (e.g. /usr/local/lib) ;; (add-to-list 'load-path "~/Downloads/emacs/site-lisp") ;; <- adjust ;; (when (eq system-type 'windows-nt) ;; (when (try-require 'cygwin-mount) ;; (cygwin-mount-activate))) #+end_src

*** Shell

Fix the shell to use: the default value of shell-file-name (that is the program =/bin/bash=) is not found under Windows Emacs.

#+begin_src emacs-lisp ;; shell (message "Current value of `shell-file-name': %s" shell-file-name) (unless (equal shell-file-name "bash") (setq shell-file-name "bash") (message "... changed to: %s" shell-file-name)) #+end_src

This is important when launching external tools, such as ~PDFLaTeX~ or ~Latexmk~.

*** Installation of version 8 or later

Ensure that a recent version of Org mode is installed and loaded.

#+begin_src emacs-lisp (when (locate-library "package") (require 'package) (add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/")) (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/")) (package-initialize) (unless package-archive-contents (package-refresh-contents))) #+end_src

#+begin_src emacs-lisp (unless (locate-library "ox") ; trick to detect the presence of Org 8 (ding) (message "The versions 6 and 7 of Org mode are no longer supported") (message "Time to upgrade, don't you think?") (when (locate-library "package") (let ((pkg 'org-plus-contrib)) (if (yes-or-no-p (format "Install package `%s'? " pkg)) (ignore-errors (package-install pkg)) (setq debug-on-error nil) (error "Please upgrade to 8 or later"))))) #+end_src

#+begin_src emacs-lisp (when (locate-library "package") (unless (locate-library "htmlize") ; for syntax highlighting in org2html (let ((pkg 'htmlize)) (if (yes-or-no-p (format "Install package %s'? " pkg)) (ignore-errors (package-install pkg))))) (unless (locate-library "ox-gfm") ; for exporting to GFM (let ((pkg 'ox-gfm)) (if (yes-or-no-p (format "Install package %s? " pkg)) (ignore-errors (package-install pkg))))) ;; (unless (locate-library "ox-reveal") ; for exporting to Reveal ;; (let ((pkg 'ox-reveal)) ;; (if (yes-or-no-p (format "Install package %s`? " pkg)) ;; (ignore-errors ;; (package-install pkg))))) ) #+end_src

#+begin_src emacs-lisp ;; version info (let ((org-install-dir (file-name-directory (locate-library "org-loaddefs"))) (org-dir (file-name-directory (locate-library "org")))) ; org.(el|elc) (message "Org mode version %s (org @ %s)" (org-version) (if (string= org-dir org-install-dir) org-install-dir (concat "mixed installation! " org-install-dir " and " org-dir)))) #+end_src

*** Activation

#+begin_src emacs-lisp (require 'org) (require 'org-clock) (require 'ox) ;; (require 'ox-reveal)

(add-to-list 'auto-mode-alist '("\.txt\'" . org-mode)) #+end_src

*** Language Environment

#+begin_src emacs-lisp ;; make sure that timestamps appear in English (setq system-time-locale "C") ; [default: nil] #+end_src

*** Clock table

#+begin_src emacs-lisp ;; format string used when creating CLOCKSUM lines and when generating a ;; time duration (avoid showing days) (setq org-time-clocksum-format '(:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t))

;; format string for the total time cells (setq org-clock-total-time-cell-format "%s")

;; format string for the file time cells (setq org-clock-file-time-cell-format "%s") #+end_src

*** Markup

#+begin_src emacs-lisp ;; hide the emphasis marker characters (setq org-hide-emphasis-markers t) ; impact on table alignment! #+end_src

*** Export options

#+begin_src emacs-lisp ;; don't insert a time stamp into the exported file (setq org-export-time-stamp-file nil)

;; activate smart quotes during export (convert " to \og, \fg in French) (setq org-export-with-smart-quotes t) ; curly quotes in HTML

;; interpret "_" and "^" for export when braces are used (setq org-export-with-sub-superscripts '{})

;; allow #+BIND to define local variable values for export (setq org-export-allow-bind-keywords t) #+end_src

*** Org-Babel

#+begin_src emacs-lisp ;; configure Babel to support most languages (add-to-list 'org-babel-load-languages '(R . t)) ; Requires R and ess-mode. (add-to-list 'org-babel-load-languages '(awk . t)) (add-to-list 'org-babel-load-languages '(ditaa . t)) ; Sudo aptitude install openjdk-6-jre. (add-to-list 'org-babel-load-languages '(dot . t)) (add-to-list 'org-babel-load-languages '(java . t)) (add-to-list 'org-babel-load-languages '(perl . t)) (add-to-list 'org-babel-load-languages '(latex . t)) ; Shouldn't you use #+begin/end_latex blocks instead? (add-to-list 'org-babel-load-languages '(ledger . t)) ; Requires ledger. (add-to-list 'org-babel-load-languages '(makefile . t)) (add-to-list 'org-babel-load-languages '(org . t)) (if (locate-library "ob-shell") ; ob-sh renamed on 2013-12-13 (add-to-list 'org-babel-load-languages '(shell . t)) (add-to-list 'org-babel-load-languages '(sh . t))) (add-to-list 'org-babel-load-languages '(sql . t))

(org-babel-do-load-languages ; loads org, gnus-sum, etc... 'org-babel-load-languages org-babel-load-languages)

;; accented characters on graphics (setq org-babel-R-command (concat org-babel-R-command " --encoding=UTF-8"))

;; don't require confirmation before evaluating code blocks (setq org-confirm-babel-evaluate nil) #+end_src

*** Library of Babel

#+begin_src emacs-lisp ;; load up Babel libraries (let ((lob-file (concat (file-name-directory (locate-library "org")) "../doc/library-of-babel.org"))) (when (file-exists-p lob-file) (org-babel-lob-ingest lob-file))) #+end_src

** HTML export

*************** TODO Understand the hidden xx used for spacing levels in column views Depending on interactive vs batch export to HTML, we still have the above difference in the resulting HTML file. *************** END

#+begin_src emacs-lisp (when (require 'ox-html)

;; export the CSS selectors only, when formatting code snippets (setq org-html-htmlize-output-type 'css)

;; ;; XML encoding ;; (setq org-html-xml-declaration ;; '(("html" . "")))

;; coding system for HTML export (setq org-html-coding-system 'utf-8)

;; ;; format for the HTML postamble ;; (setq org-html-postamble ;; " <div id="footer"><div id="copyright">\n Copyright © %d %a\n ")

;; don't include the JavaScript snippets in exported HTML files (setq org-html-head-include-scripts nil)

;; turn inclusion of the default CSS style off (setq org-html-head-include-default-style nil)

(defun org-html-export-body-only-to-html () "Export only code between between "

" and "" tags to a HTML file." (interactive) (org-html-export-to-html nil nil nil t))) #+end_src

** PDF LaTeX export

*************** TODO Since [2012-10-26 Fri], the %b now means the real base name (no full part). *************** END

#+begin_src emacs-lisp (when (require 'ox-latex)

;; ;; This is disturbing when calling `org2html'. ;; (when (executable-find "latexmk") ;; (message "%s" (shell-command-to-string "latexmk --version")))

(setq org-latex-pdf-process (if (eq system-type 'cygwin) ;; running a Cygwin version of Emacs ;; use Latexmk (if installed with LaTeX) (if (executable-find "latexmk") '("latexmk -CF -pdf $(cygpath -m %f) && latexmk -c") '("pdflatex -interaction=nonstopmode -output-directory=%o $(cygpath -m %f)" "pdflatex -interaction=nonstopmode -output-directory=%o $(cygpath -m %f)" "pdflatex -interaction=nonstopmode -output-directory=%o $(cygpath -m %f)")) (if (executable-find "latexmk") '("latexmk -CF -pdf %f && latexmk -c") '("pdflatex -interaction=nonstopmode -output-directory=%o %f" "pdflatex -interaction=nonstopmode -output-directory=%o %f" "pdflatex -interaction=nonstopmode -output-directory=%o %f"))))

(message "LaTeX command: %S" org-latex-pdf-process)

;; tell org to use listings' (instead of verbatim') for source code (setq org-latex-listings t)

;; default packages to be inserted in the header ;; include the `listings' package for fontified source code (add-to-list 'org-latex-packages-alist '("" "listings") t)

;; include the `xcolor' package for colored source code (add-to-list 'org-latex-packages-alist '("" "xcolor") t)

;; include the `babel' package for language-specific hyphenation and ;; typography (add-to-list 'org-latex-packages-alist '("english" "babel") t)

;; default position for LaTeX figures (setq org-latex-default-figure-position "!htbp")

(defun org-latex-export-body-only-to-latex () "Export only code between "\begin{document}" and "\end{document}" to a LaTeX file." (interactive) (org-latex-export-to-latex nil nil nil t))) #+end_src

** Extra customization files

To allow you to easily add extra settings to the above "standard" ones, you can create several files in the =lisp= directory. All of them will be loaded at the end of =orgmk='s initialization.

#+begin_src emacs-lisp ;; require all files from `lisp' directory (dolist (file (directory-files (concat orgmk-el-directory "../lisp/") t ".+\.elc?$")) (load-file file)) #+end_src

#+begin_src emacs-lisp ;;; orgmk.el ends here #+end_src

  • Sample parameters

** Custom LaTeX classes :PROPERTIES: :header-args+: :tangle lisp/org-latex-classes.el :END:

#+begin_src emacs-lisp ;;; org-latex-classes.el --- Sample configuration file for LaTeX

;;; Commentary:

;;; Code:

(require 'ox-latex)

(add-to-list 'org-latex-classes '("koma-article" "\documentclass{scrartcl} [NO-DEFAULT-PACKAGES] [EXTRA]" ("\section{%s}" . "\section*{%s}") ("\subsection{%s}" . "\subsection*{%s}") ("\subsubsection{%s}" . "\subsubsection*{%s}") ("\paragraph{%s}" . "\paragraph*{%s}") ("\subparagraph{%s}" . "\subparagraph*{%s}")))

;;; org-latex-classes.el ends here #+end_src

  • Contributing

** Issues

Report issues and suggest features and improvements on the [[https://github.com/fniessen/orgmk/issues/new][GitHub issue tracker]].

** Patches

I love contributions! Patches under any form are always welcome!

** Donations

If you like the Orgmk project, you can show your appreciation and help support future development by making a [[https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VCVAS6KPDQ4JC&lc=BE&item_number=orgmk&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted][donation]] through PayPal.

Regardless of the donations, Orgmk will always be free both as in beer and as in speech.

  • License

Copyright (C) 2011-2021 Fabrice Niessen

Author: Fabrice Niessen \ Keywords: org mode export automation

This program 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.

This program 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 this program. If not, see http://www.gnu.org/licenses/.

  • Standard disclaimer

I am furnishing this item "as is". I do NOT provide ANY WARRANTY of the item whatsoever, whether express, implied, or statutory, including, but not limited to, any warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE or any warranty that the contents of the item will be error-free.

In no respect shall I incur any liability for any damages, including, but limited to, direct, indirect, special, or consequential damages arising out of, resulting from, or any way connected to the use of the item, whether or not based upon warranty, contract, tort, or otherwise; whether or not injury was sustained by persons or property or otherwise; and whether or not loss was sustained from, or arose out of, the results of, the item, or any services that may be provided by me.

#+begin_export html :license-gpl-blue.svg

btn_donate_LG.gif #+end_export

This is for the sake of Emacs.

Local Variables:

before-save-hook: nil

End:

README.org ends here