2021-09-09
Abstract –
marky
is a preprocessor for Markdown using Python.marky
is inspired by pandoc, RMarkdown, Quarto. This document is created usingmarky
(Version 0.1) and contains examples which illustrate the generation of document content forhtml
andpython
code. The full rawmarky
source code of this documentation appended at the end. Themarky
source code of this document can be read here. For more information please refer to themarky
repository,marky
quickstart or the simplemarky
example.
marky
is a Markdown preprocessor which transforms a Markdown document using python. marky
implements new markup which controls the execution of python code and the generation and manipulation of Markdown text. The marky
quickstart can be found here and a very simple marky
example can be found here.
marky
only depends on pandoc
and pyyaml
. pandoc
is used for rendering the Markdown into html
and pdf
. pandoc
supports various Markdown extensions allowing for scientific writing using equations, figures, tables, citations and corresponding referencing mechanism for the latter. pyyaml
is used for parsing meta data in the front matter of the Markdown text if it is present.
Workflow for creating html
or pdf
using marky
user writes a Markdown text file and places it in md/*.md
directory with the extension .md
. the Markdown text contains special marky
markup which executes python code and manipulates the Markdown text.
marky
transforms the files in md/*.md
into regular Markdown text and places the transformed files in build/*.md
. The transformed text only contains regular Markdown, and placeholders for format dependent output for html
and pdf
.
before rendering marky
replaces placeholders for format dependent output with content creating a temporary file which only contains regular Markdown text for html
and pdf
documents according to pandoc
Markdown specification.
the regular Markdown text in the files build/*.md
is rendered into html
and pdf
using pandoc
.
The three steps are implemented in marky.py
and a Makefile
. The following document describes the special marky
markup and shows how to use marky.py
and the Makefile
.
For scientific reporting and writing usually typesetting systems or complicted WYSIWYG editors are used. In order to simplify the writing different approaches and frameworks have been developed.
All of those approaches use pandoc
as an underlying framework for document conversion. pandoc
is a powerful framework for conversion between different document formats including marky
, html
and pdf
. pandoc
implements an own internal AST, in which different document formats can be imported and exported. Using this intermediate document representation, pandoc
allows to modify document using filters, which operate on the AST. Filters can be written in haskell
, lua
and python
.
Where as RMarkdown and Quarto are integrated frameworks, which additionally depend on knitr
, RStudio
, Jupyter
, marky
depends on pandoc
and pyyaml
only. marky
natively only supports executable python code blocks, however, other languages can be executed using wrappers, which are available for other languages.
marky
Featuresmarky
implements following features using an simple Markdown-style syntax.
---
<key>: <value>
---
```!
<python_code_shown>
```
```!!
<python_code_hidden>
```
```!
_("<markdown_text>")
__("""
<markdown_text>
<markdown_text>
<markdown_text>
""")
```
Output into text: `!<python_variable>`
Output into text: `!<python_expression>`
!!! include_file.mdi
html
and pdf
documents for referencing external documents of the same format, see Section 6.8. [Format Link to html/pdf document](path/to/file.???)
html
and pdf
documents, see Section 6.9. ```!
class myfmt(fmtcode):
def html(self):
_("<HTML_CODE>")
return """
<MORE_CODE>
<MORE_CODE>
<MORE_CODE>
"""
def pdf(self):
__("""
{TEX_CODE}
{TEX_CODE}
{TEX_CODE}
""")
fmtc = myfmt()
```
Format dependent output: `!fmtc()`
Markdown is a markup language for technical writing, with emphasis on readability. Markdown can be rendered in many formats including html
and pdf
by using pandoc
for example.
Using various Markdown extensions of pandoc
a sufficient structure for writing scientific documents can be reflected using Markdown syntax. marky
uses the following pandoc
Markdown extensions.
pandoc
supports equations rendered inline and single-line in tex-style using $...$
and $$...$$
, bibliography using the --citeproc
option, section numbering using the --number-sections
option and table of contents using the --table-of-contents
option.
pandoc
supports xnos
filters for referencing document content like figures, equations, tables, sections by using the --filter pandoc-xnos
option. xnos
integrates clever references, which means “Fig.”, “Sec.”, “Eq.” and “Tab.” are added automatically to the corresponding element. If the prefix is to be omitted, the reference can be written as !@ref:label
.
Example
## Referenced Section {#sec:label}
This is a reference to @sec:label.
[This is the caption](data:image/png;base64,iVBORw0KGgoAAAANS
!
UhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DH
xgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==){#fig:label}
This is a reference to @fig:label.
A |B |C |D
---|---|---|---
000|111|444|555
222|333|666|777
Table: This is the caption {#tbl:label}
This is a reference to @tbl:label.
$$\mbox{e}^{\mbox{i}\pi}+1=0$${#eq:label}
This is a reference to @eq:label.
[@Muller1993]. This is a citation
The file marky.bib
is specified in the meta data in the front matter of the Markdown text and contains the following article.
@article{Muller1993,
author = {Peter Muller},
title = {The title of the work},
journal = {The name of the journal},
year = {1993},
number = {2},
pages = {201-213},
month = {7},
note = {An optional note},
volume = {4}
}
Output
This is a reference to Section 4.1.
This is a reference to Fig. 1.
A | B | C | D |
---|---|---|---|
000 | 111 | 444 | 555 |
222 | 333 | 666 | 777 |
This is a reference to Table 1.
eiπ + 1 = 0(1)
This is a reference to Eq. 1.
This is a citation (Muller 1993).
marky.py
Command-Line Usagemarky.py
Script Usagemarky
is supplied as a single-file script which contains the marky
Makefile
as well as the marky
documentation marky.md
, marky.mdi
and marky.bib
.
After downloading marky.py
the script needs to be placed in a project working directory working_dir
. The script can be invoked using a python interpreter python
marky.py or it can be executed using a shell:
> cd working_dir
> chmod +x marky.py
> ./marky.py
A new project is initialized in the working_dir
using the --init
option. marky
creates a directory tree for the project, which is explained in detail in Section 5.2. The marky
Makefile
and documentation marky.md
, marky.mdi
and marky.bib
are auto-generated and placed inside the subdirs md/
and data/
in working_dir
accordingly.
> cd working_dir
> ./marky.py --init
WRITE ./Makefile
WRITE ./md/marky.md
WRITE ./md/marky.mdi
WRITE ./data/marky.bib
USAGE
1. `make help`
2. `make all-html httpd`
3. `make all-pdf`
marky
renders the documentation using pandoc
into html
and pdf
using make all
. html
and pdf
documents can be rendered after installing the dependencies python-pyyaml
, pandoc
and pandoc-xnos
(pandoc-fignos
, pandoc-secnos
, pandoc-eqnos
, pandoc-tablenos
). The details are shown in the Makefile
help message in Section 5.3.
marky
Project StructureA marky
project has the following structure, which is auto-generated in the project directory working_dir
after invocation of marky.py --init
.
# PROJECT TREE
##############
#
# working_dir
# |
# |- marky.py - marky executable
# |- Makefile (*) - marky Makefile
# |
# |- md/ (*) - user Markdown dir
# | |- marky.md(i) (*) - marky documentation text
# | |- *.md - user Markdown text files
# | |- *.mdi - user Markdown include files
# |
# |- data/ (*) - pandoc resource directory
# | |- marky.bib (*) - marky documentation bib
# | |- *.bib - user bibliography files
# | |- *.png/jpg - user image files
# | |- ... etc...
# |
# |- build/ (*) - build Markdown dir
# | |- *.md (*) - preprocessed Markdown text
# | |- *.md.mk (*) - Makefile dependencies
# | |- *.html.md (*) - linked Markdown for html format
# | |- *.pdf.md (*) - linked Markdown for pdf format
# |
# |- html/ (*) - rendered html dir
# |- pdf/ (*) - rendered pdf dir
#
# (*) directories/files are auto-generated using
# `./marky.py --init` and `make all´
#
By invoking make all
all files md/*.md
are transformed into corresponding html/*.html
and pdf/*.pdf
files. By invoking make httpd
a python web server is started in html/
.
All user-generated project content goes into md/*.md(i)
for Markdown text and Markdown include files and data/*
for images, bibliography, videos, html frames, etc…
ATTENTION: The files in the directories build/*.md
are auto-generated. All user-generated content *.md
and *.mdi
has to be placed inside the directory md/
. Invoking make clean
will delete all files in html/
, build/
and pdf/
.
marky
Makefile UsageBy running make
or make help
in the project working_dir
the Makefile
help is shown.
#
# marky HELP
############
#
# TARGETS
#
# Tools:
# * help - show this message
# * tree - show the project tree
# * cheat - show the marky *Cheat Sheet*
# * httpd - run python -m httpd.server in `html/`
# * scan - build make dependencies and targets
#
# Build:
# * all - alias: `make all-html all-pdf`
# * all-html - render html (`build/*.html.md`->`html/*.html`)
# * all-pdf - render pdf (`build/*.pdf.md`->`pdf/*.pdf`)
# * clean - delete: `build/*`, `html/*`, `pdf/*`
#
# Debug:
# * all-mk - depend: `md/*.md`->`build/*.md.mk`
# * all-md - marky: `md/*.md`->`build/*.md`
# * all-link - link (`build/*.md`->`build/*.html.md/pdf`)
#
# Dependencies:
# * pandoc >= 2.10
# * pip install pandoc-fignos
# * pip install pandoc-eqnos
# * pip install pandoc-secnos
# * pip install pandoc-tablenos
# * pip install pandoc-xnos
# * pip install pyyaml
#
# ATTENTION
# files in `build/*.md` and `html/*.html` are auto-generated.
# user files `*.md(i)` have to be placed in `md/*.md(i)`.
# `make clean` deletes all files in `build/`, `html/` and `pdf/`.
#
# EXAMPLE
# 1. run `make all-html httpd`:
# * transform `md/*.md`->`html/*.html`
# * start a python httpd server in `html`
# 2. run `make all-pdf`
# * transform `md/*.md`->`pdf/*.pdf`
#
marky
Cheat SheetBy running make cheat
in the project working_dir
the marky
cheat sheet is shown, which presents a quick overview of marky
special markup for execution of python code and manipulation of Markdown text, according to the features describes in Section 3.
#
# marky CHEAT SHEET
###################
#
# CODE-BLOCK
#
# ```!
# print("The code is shown in the document,")
# print("but printed text is shown in console.")
# _("This text is inserted into Markdown", _)
# _(_, "output and appended to prev line.")
# _(1, 2, 3, [4, 5,], "a", "b", sep=", ")
# __("""
# * This is cropped and shifted.
# * This is cropped and shifted.
# * This is cropped and shifted.
# """, crop=True, shift=)
# ```
#
# ```!!
# print("The code is hidden in the document,")
# print("but printed text is shown in console.")
# import sys
# sys.path.append(".")
# import mymodule
# new_vars = {"a": 1, "b": 2}
# globals().update(new_vars)
# ```
#
#
# FORMATTED OUTPUT
#
# ```!!
# value = float(1.2345)
# ```
# The number `!value` is not formatted.
# The number 1.2345 is not formatted.
#
# The number `!value:.2f` is formatted.
# The number 1.23 is formatted.
#
# This `\!<variable>` is not parsed.
#
#
# INLINE-EXPRESSION
#
# This is a Paragraph with an `!<expression>`.
# This `\!<expression>` is not parsed.
#
# The alphabet: `![chr(ord("A")+i for i in range(7)]`.
# The alphabet: ['A', 'B', 'C', 'D', 'E', 'F', 'G'].
#
# A nice list: `!", ".join(list(range(1, 11)))`.
# A nice list: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
#
#
# META DATA
#
# ---
# title: Document
# date: Today
# author: Name
# link-citations: true
# bibliography: bibl.bib
# fontsize--pdf: 11pt
# fontsize--html: 10pt
# header-includes--pdf: >
# \usepackage{...}
# \usepackage{...}
# header-includes--html: >
# <script ...>
# <link ...>
# <style ...>
# xnos-cleveref: true
# xnos-capitalise: true
# -hidden_field: text
# ---
#
#
# INCLUDE-STATEMENT
#
# !!! path/incl.mdi FLAGS
# \!!! This is not parsed as include statement.
#
# Flags:
# * aux only Makefile dependency, no include
# * nodep include without Makefile dependency
# * raw do not parse file, include as-is
# * nometa ignore and skip all meta data
# * nobody ignore and skip Markdown body
# * nomarky no processing of marky markup
# * code include only the hidden code blocks
# * code! include only code, but all code blocks
# * #+N increase level of ATX headings by N
# * >>N increase indentation level by N tabs
# * >N increase indentation level by N spaces
#
#
# FORMAT LINK
#
# [Link to Document](path/to/file.html)
# [Link to Document](path/to/file.pdf)
# [Format Link to Document](path/to/file.???)
# This is not parsed as format link .\???
#
#
# FORMAT CODE
#
# ```!
# test1 = fmtcode(html="HTML", pdf="PDF")
# class Test2(fmtcode):
# def html(self):
# __("""
# This is HTML2!
# This is HTML2!
# This is HTML2!
# """)
# def pdf(self):
# return """
# This is \{PDF2\}!
# This is \{PDF2\}!
# This is \{PDF2\}!
# """
# test2 = Test2()
# ```
# Run Format Code `!test1()` and `!test2()`.
# `\!test1()` and `\!test2()` are not parsed.
#
# ```!
# _(test1())
# _(test2())
# ```
#
marky
Preprocessor MarkupMeta data is annotated in the front matter of a Markdown text document. The front matter must start in the first line with ---
and precedes all other text being fenced by ---
. The meta data is in yaml
format. The yaml
block is parsed using python-pyyaml
. By default all meta data is imported into the preprocessed document. If a meta data key starts with -
the key is not exposed into the resulting front matter of the preprocessed document. All meta data keys will be exposed into the python scope as a local variable, unless the variable already exists. In the following exmample all keys except figsize
, figdpi
and version
are copied into the preprocessed Markdown document.
Example
---
title: marky Documentation
date: `Date`
author: `Author`
link-citations: True
bibliography: data/marky.bib
header-includes--pdf: >
\hypersetup{
colorlinks=false,
allbordercolors={0 0 0},
pdfborderstyle={/S/U/W 1}}header-includes--html: >
<style>* { box-sizing: border-box; }</style>xnos-cleveref: True
xnos-capitalise: True
fontsize: 11pt
version: 0.1
figsize: [10, 8]
figdpi: 300
---
The meta data fields title
, date
, author
, link-citations
, bibliography
and header-includes
are processed by pandoc
during document rendering. fontsize
adjusts the font size in html
and pdf
documents. The header-includes
field is used for underlining links in pdf
and html
documents. The xnos-cleveref
and xnos-capitalise
fields are used by the pandoc-xnos
extensions for referencing figures, tables, sections and equations.
The field header-includes
ends with --pdf
and --html
, which specifies corresponding options for generation of pdf
and html
documents. During make, marky
scans all meta data fields, and fields which end with --pdf
and --html
are selected and forwarded to pandoc
based on the format to be rendered. This was format dependent meta data can be specified in marky
Markdown text.
The version
field is a user-defined field which shows the version of this document: 0.1. figsize
and figdpi
are used in this document to control the figure size and resolution in the numpy
and matplotlib
example, see Section 7. The font size is 11pt and the Fig. 2, 3, 4, 5 and 6 have a size of 10x8cm. The font size applies to both document text and figure text.
The values of meta data fields can be manipulated during include when being preceeded by -
or --
, as described in Section 6.7.
Python code can be executed during transformation of the Markdown text. Python code is directly written inside the Markdown text and is fenced using the ```
statement. The block needs to start with either !
or !!
.
!
: The python code is executed and shown in the output.!!
: The python code is executed and hidden in the output. ```!
<python_code_shown>
```
```!!
<python_code_hidden>
```
Meta data from Markdown front matter can be used as local variables in python code blocks. The import
statement can be used in python code blocks in order to access installed python packages. All code blocks span one large scope for sharing functions and local variables. Using the print()
function the text will be printed to the console and not inside the resulting Markdown text. In order to modify the Markdown text using marky
during preprocessing, the _()
statement has to be used, see Section 6.3.
Example
import numpy as np
def get_x(a=0):
return np.array([41 + a])
= 1 y
This is a paragraph.
= get_x(y)
x print("Hello Console! x is", x)
_()
StatementUsing the print()
statement the text will be printed to the console. When using the _()
and __()
statements new Markdown text can be inserted dynamically into the document during preprocessing.
_()
Statement
_(*args, sep=" ")
:
sep
_(_, *args )
: append to previous output_(_, *args, _)
: append to previous output and append next output_( *args, _)
: append next output to this output__()
Statement
__(arg, crop=True, shift="")
:
arg
to stringshift
string to each line__(arg, _)
: append next output to this outputCrop and Shift
def test():
"""
__( * List Level 1
* List Level 1
""")
"""
__( * List Level 2
* List Level 2
* List Level 3
""", shift=" "*4)
* List Level 1
* List Level 1
* List Level 2
* List Level 2
* List Level 3
Example
+= 1
y f"""
__( * This is `marky` Version *{version}*.
* This is `marky` Version *{version}*.
""")
f"""
__( 1. This is `marky` Version *{version}*.
2. This is `marky` Version *{version}*.
""", shift=" "*4)
marky
Version 0.1.marky
Version 0.1.
marky
Version 0.1.marky
Version 0.1."This", _)
_("is")
_(" one", _)
_(_, "line! not ending with \\")
_("this?") _(
Thisis oneline! not ending with
this?
f"Hello Markdown! x is **{x}** and y is *{y}*") _(
Hello Markdown! x is [42] and y is 2
_()
StatementThe _()
statement needs to be indented according to the python program flow (for
, while
, if
, else
, try
, with
, def
, class
) and supports dynamic insertion of Markdown text into the document based on loops and conditions.
Example 1
"This is the **generated output**:")
_("")
_("> This is a *listing*:")
_(= ["zero", "one", "two", "three"]
text for i in range(10):
if i < 2:
f"> {i}")
_(elif i == 2:
= text[i]
j f"> {j}")
_(elif i == 3:
"")
_(elif i < 7:
f">> {' '*(i-4)}* {i}")
_(elif i == 7:
"")
_(else:
= i - 7
j = text[j]
k f"> {j}. {k}") _(
This is the generated output:
This is a listing: 0 1 two
- 4
- 5
- 6
- one
- two
Example 2
Table 2 is generated using the following python clode block.
= 13
n = ["*%s*", "**%s**", "~~%s~~", "`%s`",
dec r"$\times^%s$", "$\infty_%s$"]
"|".join("X"*n) + "\n" + "|".join("-"*n))
_(for i in range(n):
= [chr(ord("A")+(2*i+3*k)%26) for k in range(i+1)]
fill = [dec[(l+i)%len(dec)]%k for l, k in enumerate(fill)]
fill = list("0")*n
text >>1)-(i>>1):(n>>1)+(i>>1)] = fill
text[(n"|".join(text)) _(
X | X | X | X | X | X | X | X | X | X | X | X | X |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | A | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | C | 0 | 0 | 0 | 0 | 0 | |
0 | 0 | 0 | 0 | 0 | H |
×K | 0 | 0 | 0 | 0 | 0 | |
0 | 0 | 0 | 0 | 0 | G |
×J | ∞M | P | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | ×I | ∞L | O | R | 0 | 0 | 0 | 0 | |
0 | 0 | 0 | 0 | ∞K | N | Q | W |
×Z | 0 | 0 | 0 | |
0 | 0 | 0 | M | P | V |
×Y | ∞B | E | 0 | 0 | 0 | |
0 | 0 | 0 | O | U |
×X | ∞A | D | G | 0 | 0 | ||
0 | 0 | T |
×W | ∞Z | C | F | L |
×O | 0 | 0 | ||
0 | 0 | S |
×V | ∞Y | B | E | K |
×N | ∞Q | T | 0 | |
0 | ×U | ∞X | A | D | J |
×M | ∞P | S | V | 0 | ||
0 | ∞W | Z | C | I |
×L | ∞O | R | U | A |
×D | ||
Y | B | H |
×K | ∞N | Q | T | Z |
×C | ∞F | I |
marky
can output python variables inline into Markdown text using the `!VARIABLE`
statement. VARIABLE
can be any python variable from a python code block or meta data field. The output can be formatted using the `!VARIABLE[:FORMAT]`
statement according to the python operator {<variable>[:<format>]}
implemented in the str.format()
specification. The `!VARIABLE`
statement is escaped using `\!VARIABLE`
.
Example
= int(1)
x = float(2.3)
y = 0
z = [1, 2, 3]
a = (4, 5) b
`!x:03d` and y is `!y:.2f`.
This is a paragraph and x is `!a`, `!b` and escaping works: `\!z`. Other content is:
This is a paragraph and x is 001 and y is 2.30. Other content is: [1, 2, 3], (4, 5) and escaping works: !z
.
marky
outputs results of python expressions inline into Markdown text using the `!EXPRESSION`
statement. EXPRESSION
can be any python expression. The output can be formatted using the python `!EXPRESSION[:FORMAT]`
statement according to the python operator {<expression>[:<format>]}
implemented in the python f
-strings specification. The `!EXPRESSION`
statement is escaped using `\!EXPRESSION`
.
Example
`!", ".join([str(i) for i in a])`.
This is a list with the numbers `get_x` is `!get_x()` and escaping
The result of the function `\!get_x(b[1])[0]`. works:
This is a list with the numbers 1, 2, 3. The result of the function get_x
is [41] and escaping works: !get_x(b[1])[0]
.
marky
supports include of Markdown text using the !!!
statement. The !!!
statement must be on a single line and follows the path of the include file. The path of the include file is relative to the root Markdown document which is processed. The paths of all included files are collected and a Makefile
rule is created and stored in a file (path of output Markdown text appended with .mk
).
The !!!
statement is escaped using \!!!
. The include statement cannot be used in code blocks. marky
Markdown text must have the extension .md
and include files must have the extension .mdi
The include statement supports flags for parsing the include file.
!!! PATH/FILE.mdi FLAGS
Flags
aux
: reference as Makefile dependency, but do not processnodep
: do not reference this file as Makefile dependencyraw
: the file is included as-is without any parsingnometa
: meta data in front matter is skipped during parsingnobody
: all Markdown text is skipped during parsingnomarky
: include the Markdown text without any marky
processingcode
: only include hidden code blockscode!
: include all code blocks>>N
: increase the indentation using N
tabs>N
: increase the indentation using N
spaces#+N
: increase the level of ATX headings #
. The headings are parsed according to pandoc
extensions (blank_before_header, space_in_atx_header)Example
!!! marky.mdi #+2`!included` and $x=`!x`$ and $y=`!y`$. The file was included:
print("Hello Console!")
"Hello Markdown!")
_(= 123
x = 4.567 y
Hello Markdown!
First | Second |
---|---|
00123 | 4.5670 |
The file was included: 1 and x = 123 and y = 4.567.
The file marky.mdi
was loaded with shifting ATX headings by 2 which means ##
has been added to the included section. The file contains:
---
included: 1
---
# Included Section
```!
print("Hello Console!")
_("Hello Markdown!")
x = 123
y = 4.567
```
First|Second
-----|------
`!x:05d`|`!y:.4f`
The unmodified source was loaded using the flags raw >>1
.
The file marky.md.mk
contains:
build/marky.md: \
md/marky.mdi
.PHONY: md-marky
md-marky: build/marky.md
.PHONY: html-marky
html-marky: html/marky.html
.PHONY: pdf-marky
pdf-marky: pdf/marky.pdf
The include statement !!!
loads and parses an *.mdi
include file. The yaml
meta data in the front matter of the document also is loaded and parsed if the nometa
flag is not specified in the include statement. Assuming the root document and the included document have the following meta data.
Root Document
---
width: 10
height: 20
serial: none
parameter: value
text: abc
---
!!! include.mdi
Included Document
---
depth: 30
serial-: A
parameter--: default
text: def
---
By default the values of meta data keys are appended. When a meta data key is preceded by -
the included key replaces the present key, if it is preceded by --
the included key is skipped if the meta data key already is present in the root document. Given the example above, the resulting meta data in the front matter of the preprocessed Markdown text looks as follows.
Preprocessed Document
---
width: 10
height: 20
depth: 30
serial: A
parameter: value
text: |
abc
def
---
html
and pdf
DocumentsWhen writing multiple documents, often documents are referenced between each other using links. In order to refer to external html
and pdf
documents the Markdown link statement is used.
[Link Caption](path/to/file.html)
[Link Caption](path/to/file.pdf)
When using relative paths in the URL, the documents can be referenced according to the directory tree of the source marky
Markdown text md/*/*.md
. However, the resulting link will be a path relative to the directory html/
for html
documents and relative to pdf/
for pdf
documents. As all html
and pdf
documents are kept in separate directories, one link statement cannot be used for rendering html
and pdf
with consistent paths in the link statement.
By using the marky
format link statement .???
, the file extension in the links is replaced depending on the output format resuling in consistent links for html
and pdf
documents. The format link statement can be escaped using .\???
.
Example
[Link to this Document](marky.???)
html
and pdf
DocumentsOften when writing markdown for html
and pdf
documents, the format needs to be adjusted according to the format. pandoc
Markdown already renders all common Markdown into html
and pdf
. marky
supports format specific tweaking using format codes.
In order to inject format specific code, html
code or tex
code for pdf
documents, the fmtcode
class is used. The fmtcode
class implements the call
-operator ()
and attr
-forwarding to the class members html
and pdf
. Each call to the fmtcode
class is disptached inttoo the html
and pdf
members, which either can be member variables or class methods.
During preprocessing, marky
processes all format codes for each format html
and pdf
and caches the output. When rendering the Markdown in one particular format using pandoc
, marky
only uses the results of the corresponding format. Additional tex
packages have to be included for pdf
as well as JavaScript and style sheets for html
using the meta data fields header-includes--pdf
and header-includes--html
respectively.
For returning the format specific code, either the _()
statement can be used Section 6.3 or the return
statement can be used. If both statements are mixed, the output which had been returned will be appended to the text generated with the _()
statement.
Example: Functions and Variables
class fmt_test1(fmtcode):
def html(self):
"<sup>HTML in")
_(return "superscript</sup>"
def pdf(self):
r"""
__( ${}_{\mbox{PDF in subscript}}$
""")
= fmt_test1()
test1 = fmtcode(
test2 ="<sub>HTML in subscript</sub>",
html=r"${}^{\mbox{PDF in superscript}}$"
pdf
)= fmtcode(html="HTML", pdf="PDF") test3
`\!test3()` returns the format of
The format code `!test3()`.
the document: * `!test1()`
* `!test2()`
The format code !test3()
returns the format of the document: HTML.
Example: Classes
class html:
def test1(self):
"<sup>HTML in")
_(return "superscript</sup>"
def test2(self):
return "<sub>HTML in subscript</sub>"
def test3(self):
return "HTML"
class pdf:
def test1(self):
r"""
__( ${}_{\mbox{PDF in subscript}}$
""")
def test2(self):
return r"${}^{\mbox{PDF in superscript}}$"
def test3(self):
return "PDF"
= fmtcode(html=html(), pdf=pdf()) fmtc
`\!fmtc.test3()` returns the format of
The format code `!fmtc.test3()`.
the document: * `!fmtc.test1()`
* `!fmtc.test2()`
The format code !fmtc.test3()
returns the format of the document: HTML.
marky
Markdown Exampleshtml
and Placeholder in pdf
When creating Markdown text for html
output, the user often wants interactivity using widgets like sliders, check boxes, drop down boxes etc. However, when exporting into pdf
those elements need to be replaced with non-interactive placeholders. In order to develop a single Markdown document, which can be rendered in html
with interactive elements and into pdf
with placeholder, the marky
format codes can be used, see Section 6.9. The following example defines a <input type="range">
and two <spans>
with id="myval"
and id="myres"
, in order to update the value of y = sin(x) in html
. For pdf
output the equation and the value range is shown.
Example
class Range(fmtcode):
def html(self):
"""
__( $x\in [0$ <input type='range' value='0' min='0' max='100'
onchange="
document.getElementById('myval').innerHTML = this.value;
document.getElementById('myres').innerHTML =
Math.sin(this.value);"> $100]$
""")
def pdf(self):
return "$x\in[0,100]$"
class Formula(fmtcode):
def html(self):
"""
__( $y=sin(x)=$ <span id="myres">0.000</span>
with $x=$ <span id="myval">0</span>
""")
def pdf(self):
return "$y=sin(x)$"
= Range()
Ra = Formula() Fo
`!Fo()`.
$x$ and $y$ are related to each other by
`!Ra()`. $x$ must be in the range
x and y are related to each other by .
x must be in the range .
This section illustrates how python modules can be used to create document content. Document content is placed inside the data/
directory of the current project working directory (refer to marky
project structure, Section 5.2)
numpy
and matplotlib
are powerful python modules for mathematical computing and plot generation. The following example shows how to generate Fig. 2 using numpy
and matplotlib
and include it into the document.
Example
import numpy as np
import matplotlib.pyplot as plt
= lambda A: chr(ord(u"\u0391") + ord(A) - ord("A"))
GREEK = lambda a: chr(ord(u"\u03b1") + ord(a) - ord("a"))
greek = lambda xy: tuple(i/2.54 for i in xy)
cm2inch = int(fontsize[:-2]) # convert to int
fontsize = cm2inch(figsize) # convert from cm to inch
figsize = {
params 'figure.figsize': figsize,
'legend.fontsize': fontsize,
'axes.labelsize': fontsize,
'axes.titlesize': fontsize,
'xtick.labelsize': fontsize,
'ytick.labelsize': fontsize,
'font.family': 'Times New Roman'
}
plt.rcParams.update(params)= np.random.rand(50)
x = np.random.rand(50)
y
plt.figure()="Random Coordinates")
plt.scatter(x, y, label= "".join([greek(i) for i in ["a", "b", "c", "d"]])
text =(0.5,0.5), xytext=(0.25,0.25),
plt.annotate(text, xy=dict(arrowstyle='->',lw=1.5))
arrowprops"Two Random Datasets")
plt.title(r"Data #1 - $\mathdefault{%s_1}$" % GREEK("C"))
plt.xlabel(r"Data #2 - $\mathdefault{%s_2}$" % GREEK("D"))
plt.ylabel(
plt.grid()
plt.legend()
plt.tight_layout()"build/figure1.png", dpi=figdpi)
plt.savefig("all") plt.close(
`numpy`
{#fig:figure1} and
This section illustrates how a sequence of complex figures can be generated using numpy
and matplotlib
and how the figures are formatted using python and referenced using marky
.
Suppose one experiment which can be run in four different setups with different values for λ=
The results of the experiments for the setups (1)–(4) are summarized in the Fig. 3, 4, 5 and 6.
Example
= 100
n = u"\u03b1"
alpha = u"\u03b5"
epsilon = u"\u03bb"
lamda = lambda x, a, b: a*(np.sqrt(x)+b*np.sin(x*b))
f = lambda x, a ,b, c: np.fabs(f(x, a, b) - f(c, a, b)) + c
g = np.zeros((n-1, 3, 3, 4), dtype=np.float32)
dat = ["red", "green", "blue"]
cols = ["o", "x", "<"]
mark = np.array([50.*x/n for x in range(1, n)], dtype=np.float32)
x for k, c in enumerate([10., 20., 30., 40.]):
for i, a in enumerate([1, 2, 4]):
for j, b in enumerate([0.2, 0.4, 0.6]):
= g(x, a, b, c)
dat[:, i, j, k]
plt.figure()for j in range(3):
for i in range(3):
= "%s=%.1f%%" % (epsilon, (i+1)*10.2) \
label_i if j == 0 else None
= "%s=%.1fHz" % (alpha, (j+1)/10.) \
label_j if i == 0 else None
= dat[:, i, j, k].flatten()
y =cols[i], lw=0.75, label=label_i)
plt.plot(x, y, color1::4], y[1::4], color="black",
plt.scatter(x[=mark[j], lw=0.5, s=5, label=label_j)
marker= k + 1
k = k*125.33
kval "Experiment Setup #%d: %s=%.2fnm" % (k, lamda, kval))
plt.title("Time [s]")
plt.xlabel("Intensity [kg/s³]")
plt.ylabel(
plt.grid()
plt.legend()
plt.tight_layout()"build/figure2-%d.png" % k, dpi=figdpi)
plt.savefig("all")
plt.close(f"""
__( {{#fig:figure2_{k}}}
""")
Thanks for reading, please try marky
.