Skip to content

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

fniessen/orgmk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Export Org mode documents to HTML, PDF, etc.

ADVERT: Elevate Your Emacs Skills in Paris, Rotterdam, Leuven or Valencia!

Unlock the power of Emacs in Valencia! Join my next exclusive “Emacs Boost” course on April 11th and 12th, 2023!

Are you ready to supercharge your productivity and become an Emacs ninja? Ready to become an Emacs pro? Look no further!

What you’ll learn:

  • Master Emacs from the basics to advanced tricks.
  • Boost your editing efficiency and streamline your workflow.
  • Customize Emacs to fit your unique needs.
  • Collaborate with fellow Emacs enthusiasts.
  • And much more!
  • When: April 11th and 12th (Thu + Fri)
  • Where: Valencia, Spain
  • Who should attend: Anyone interested in Emacs, from beginners to advanced users.

Why choose my course?

  • Expert instructor with 24 years of Emacs experience.
  • Hands-on exercises to reinforce your learning.
  • Networking opportunities with like-minded Emacs enthusiasts.
  • Personalized attention.

Don’t miss this opportunity to take your Emacs skills to the next level!
Seats are limited, so reserve yours today.

Visit https://emacsboost.com/ for more information, other dates and other locations, and to reserve your seat.

See you in Valencia! #EmacsBoostValencia

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.

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

:header-args+: :tangle no

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 TeX Live (for example).

Usage

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

Standalone scripts

  • For “weaving” files (producing the formatted documentation files):
    org2html [OPTION] FILE
    org2latex [OPTION] FILE
    org2pdf [OPTION] FILE
    org2beamerpdf [OPTION] FILE
    org2odt [OPTION] FILE
    org2txt [OPTION] FILE
        
  • For “tangling” files:
    org-tangle FILE
        

Makefile wrapper

orgmk [OPTION]
orgmk [OPTION] [html | pdf]
orgmk [OPTION] [FILE]

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.

orgmk

Update specific types of documents

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

orgmk html

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.

orgmk thesis.pdf

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

org2pdf thesis.org

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

  1. Extract the latest version of orgmk.
    git clone git@github.com:fniessen/orgmk.git
        
  2. Create configuration files which record where orgmk has been extracted on your host machine.
    make
        
  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:

    BIN_DIR=~/bin
        

    If needed, add that directory to your PATH.

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

    make install
        

At this point, everything should be ready for use.

Makefile for installation

:header-args+: :shebang “” :tangle 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

Standalone scripts

Orgmk-init

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/orgmk-init

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

Functions

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
}
die () {
    printf "$(basename $0): $@\n" > /dev/stderr
    exit 2 # an error occurred
}
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
}

Runtime

(format-time-string "%Y%m%d.%H%M")
    cat << EOF
This is $(basename $0), version: <<current-time()>>.
EOF
# perform housekeeping on program exit or a variety of signals
# (EXIT, HUP, INT, QUIT, TERM)
trap CleanUp 0 1 2 3 15
# 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 $@
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 <orig file name>.<backend ext>.
FILE_LOG="${FILE_SRC_ORIG%.*}.log"
rm -f $FILE_LOG

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.

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

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

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

org2html

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2html

The org2html script converts an Org file to HTML.

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"

org2latex

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2latex

The org2latex script converts an Org file to LaTeX.

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"

org2pdf

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2pdf

The org2pdf script converts an Org file to PDF.

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"

org2beamerpdf

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2beamerpdf

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

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"

org2odt

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2odt

The org2odt script converts an Org file to ODT.

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"

org2txt

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2txt

The org2txt script converts an Org file to text.

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"

org2gfm

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org2gfm

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

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"

Orgmk-update-src-check-diff

:header-args+: :shebang “#!/bin/bash\n” :tangle bin/orgmk-update-src-check-diff

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

Functions

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
}

Runtime

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

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.

org-tangle

:header-args+: :shebang “#!/bin/sh\n” :tangle bin/org-tangle

The org-tangle tangles an Org file.

TARGET_EXT=
. orgmk-init

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

Orgmk

Orgmk.mk

:header-args+: :tangle bin/orgmk.mk

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

### -*- Makefile -*- definition file for Orgmk

Default shell

SHELL = /bin/sh

Exportation scripts

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

ORG2_FLAGS=-y

ORG2HTML=org2html $(ORG2_FLAGS)
ORG2PDF=org2pdf $(ORG2_FLAGS)

Viewing stuff

VIEW_HTML ?= firefox
VIEW_PDF ?= xpdf

Color output

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)
C_INFO="\\e[32m"
C_WARNING="\\e[35m"
C_SUCCESS="\\e[1\;32m"
C_FAILURE="\\e[1\;31m"
C_RESET="\\e[0m"

Basic configuration

Org Sources

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

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))
Add Org_DIRS defaulting to . Use Org_DIRS with foreach (?) in the following listsENDFix 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:

HTML_FILES_FROM_ORG = $(patsubst %.org,%.html,$(ORG_FILES))
HTML_FILES_FROM_TXT = $(patsubst %.txt,%.html,$(TXT_FILES))

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

CUR_HTML_FILES := $(HTML_FILES)

PDF Targets

List of PDF files which can be generated:

PDF_FILES_FROM_ORG = $(patsubst %.org,%.pdf,$(ORG_FILES))
PDF_FILES_FROM_TXT = $(patsubst %.txt,%.pdf,$(TXT_FILES))

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

CUR_PDF_FILES := $(PDF_FILES)

Debugging

# turn command echo'ing back on with VERBOSE=1
ifndef VERBOSE
    QUIET := @
endif

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

# turn on shell debugging with SHELL_DEBUG=1
ifdef SHELL_DEBUG
    SHELL += -x
endif

Commands

Define variables for each command used in targets:

PRINTF=$(QUIET)printf
EGREP=$(QUIET)egrep
LS=$(QUIET)ls

All

.PHONY: all
all: html pdf                           # Regenerate all HTML and PDF files

Help

Get help.

.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

HTML

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

.PHONY: html
html: $(CUR_HTML_FILES)                 # Regenerate all HTML documents from Org

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

$(HTML_FILES_FROM_ORG): %.html: %.org   # Export an Org document to HTML
<<recipe-convert-to-html>>

$(HTML_FILES_FROM_TXT): %.html: %.txt   # Export an Org document to HTML
<<recipe-convert-to-html>>

The recipe to convert Org to HTML is:

	$(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"

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

view-html:                              # Generate the HTML files, then show the result

PDF

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

.PHONY: pdf
pdf: $(CUR_PDF_FILES)                   # Regenerate all PDF documents from Org

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

$(PDF_FILES_FROM_ORG): %.pdf: %.org     # Export an Org document to PDF
<<recipe-convert-to-pdf>>

$(PDF_FILES_FROM_TXT): %.pdf: %.txt     # Export an Org document to PDF
<<recipe-convert-to-pdf>>

The recipe to convert Org to PDF is:

	$(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"
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

.PHONY: clean
clean:                                  # Delete all temporary files created by Org export

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

### orgmk.mk ends here

Makefile wrapper

:header-args+: :tangle bin/orgmk :shebang “#!/bin/sh\n”

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.

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 fni@missioncriticalit.com
EOF
    exit 1
}
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
# 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 $*

Orgmk configuration file

:header-args+: :tangle site-lisp/orgmk.el

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

Common

;;; orgmk.el --- Emacs configuration file for `orgmk'

;;; Commentary:

;;; Code:

Library Search

Remember current directory.

;; remember this directory
(defconst orgmk-el-directory
  (file-name-directory (or load-file-name (buffer-file-name)))
  "Directory path of Orgmk.")

Feedback

;; ;; activate debugging
;; (setq debug-on-error t)

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

Auto Save

;; don't make a backup of files
(setq backup-inhibited t)

Initialization for MS Windows

When running an native Microsoft Windows version of Emacs:

;; ;; 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)))

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.

;; 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))

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.

(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)))
(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")))))
(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)))))
  )
;; 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))))

Activation

(require 'org)
(require 'org-clock)
(require 'ox)
;; (require 'ox-reveal)

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

Language Environment

;; make sure that timestamps appear in English
(setq system-time-locale "C")           ; [default: nil]

Clock table

;; 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")

Markup

;; hide the emphasis marker characters
(setq org-hide-emphasis-markers t)      ; impact on table alignment!

Export options

;; 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)

Org-Babel

;; 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)

Library of Babel

;; 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)))

HTML export

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
(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" . "<!-- <xml version=\"1.0\" encoding=\"%s\"> -->")))

  ;; 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 &copy; %d %a\n  </div></div>")

  ;; 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 \"<body>\" and \"</body>\" tags to a HTML file."
    (interactive)
    (org-html-export-to-html nil nil nil t)))

PDF LaTeX export

Since [2012-10-26 Fri], the %b now means the real base name (no full part).END
(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)))

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.

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

Sample parameters

Custom LaTeX classes

:header-args+: :tangle lisp/org-latex-classes.el
;;; 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

Contributing

Issues

Report issues and suggest features and improvements on the 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 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.

About

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

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published