Helps keep Markdown editing fun.
This tool reads string sequences from Markdown and generates custom visuals using custom image layers. Inserts or updates images back in to the Markdown.
To help keep Markdown editing fun for projects needing to add many images which are permutations of a source diagram, this tool will do the heavy lifting.
It is designed for the needs of the Qun mk2 synthesizer project’s README.md guide documentation, originally. Adding images to the guide provides helpful visualizations to teach synthesizer control combinations. Users may refer to pictures in order to activate features on the synthesizer, in addition to text. There are many combinations available with the Qun, and many images to generate, consequently.
So updating 60+ structured images and their Markdown links is work worthy of automation.
Before:
Button | Description |
---|---|
SHIFT + SEQ PLAY + turn dial |
After, adds images:
Button | Description |
---|---|
SHIFT + SEQ PLAY + turn dial ![]() |
and can make animated GIFs:
Button | Description |
---|---|
SHIFT + SEQ PLAY + turn dial ![]() |
The --gif option |
pip install -r requirements.txt
usage: mdpicgen [-h] --md-file MD_FILE [--md-out-file MD_OUT_FILE]
[--image-out-dir IMAGE_OUT_DIR]
[--button-pattern-file BUTTON_PATTERN_FILE]
[--image-height IMAGE_HEIGHT] [--gif] [--print-formatted]
[--print-extract]
{imageset,psd} ...
Parse Markdown table cells into recognized sequences of strings, "keys". Only
matches keys from table columns all identified by patterns in the button-
pattern-file. Inserts and updates links for image files to output Markdown, in
the cells after the keys. Generate images using image layers, named based upon
the keys -- see sub-commands for image generation details.
options:
-h, --help show this help message and exit
--md-file MD_FILE Input filename for the Markdown file.
--md-out-file MD_OUT_FILE
Output filename for Input Markdown with updated image
links.
--image-out-dir IMAGE_OUT_DIR
Output directory name for composited images, will be
created (Default: 'out').
--button-pattern-file BUTTON_PATTERN_FILE
Pattern filename for matching buttons (Default:
'qunmk2.patset').
--image-height IMAGE_HEIGHT
Pixel height of generated images, used with sub-
commands (Default: 48).
--gif Generate GIF from button sequences, sets filename
extension (Default: false, use PNG).
--print-formatted Print formatted Markdown (from '--md-file') to the
console.
--print-extract Print sequences to console.
Image generation sub-commands:
{imageset,psd} Optional sub-commands for how to generate images: the
source of image data.
imageset Read image data from a directory of images, supports
GIF animation of button sequences.
psd NOT RECOMMENDED: Read image data from PSD file.
depends on Adobe(tm) Photoshop tech, slow,
incompatibilities between PSD tools unexpectedly
breaks workflows, animation not supported.
Can be combined with Markdown.
usage: mdpicgen imageset [-h] [--imageset-file IMAGESET_FILE]
[--imageset-dir IMAGESET_DIR]
options:
-h, --help show this help message and exit
--imageset-file IMAGESET_FILE
Specifies what image filename will be used for what
layer, and their xy coordinates.(Default:
'qunmk2_imageset.csv')
--imageset-dir IMAGESET_DIR
Directory containing images used as layers defined in
'--imageset-file' (Default: 'imageset').
Can be combined with Markdown.
usage: mdpicgen psd [-h] --psd-file PSD_FILE
options:
-h, --help show this help message and exit
--psd-file PSD_FILE Input filename for the PSD file.
flowchart LR
A([INPUT \nMarkdown])
A1[Extract Sequences from Markdown]
A2[Generate Images of seqs]
A3[Generate image links]
A4[Format each line]
O1([OUTPUT \nImages, linked Markdown, formatted Markdown])
A --> A1
A1 --> A2
A1 --> A3
A --> A4
A2 --> O1
A3 --> O1
A4 --> O1
classDef default fill:#fff,stroke:#000,font-size:12pt;
See a larger diagram of how the data flows, from inputs to outputs.
"Button"
header text as the first column of a Markdown source document, if not already present."Button"
column, e.g “SHIFT + B1
”.<br>
tag at end of that text, inside the first cell, to mark this button sequence for processing. Repeat as desired.Button
” (customizable in *.patset
file). Non-matching tables will be ignored.Button + Sequence + String
<br>

- see an example below.
<br>
tag is required.--md-out-file
when there is a properly formatted button sequence and <br>
tag.This script employs sub-commands to generate images.
--image-height
parameter to customize the height."BG"
. This will be composited behind all other layers during image generation."s"
in the layer name, "SHIFT - s"
Output images feature several useful qualities.
"BG"
layer, and a declaration of the "BG"
layer in the image datasource – the CSV for imageset, or the PSD."SPLAY + 5 + 5"
will flash "5"
by inserting a frame only containing the prior "SPLAY"
image, before inserting the final "5"
frame.--md-out-file
option, e.g. 
as set with --image-out-dir
, is the same path data used during generation of images with the imageset
and psd
sub-commands. This dual-purpose can be at odds with itself.Images are named according to their button sequence, with shortened button names.
"1"
and "2"
"s_12.png"
Button pattern files (*.patset
) match button sequences, and individual buttons. They define the output image filenames with substrings that map to each individual button in the sequence.
The files are structured similar to CSVs, and use equals (=
) instead of commas. They are formatted and line-oriented:
Format | Description |
---|---|
^SHIFT$ = s |
Match a button description with a pattern, and map the button to a substring to be used when naming a generated image for this button. FORMAT: [RegEx to match buttons] = [Filename substring, or %digits% macro] E.g. Here, SHIFT maps to the s in s_splay_d.png for the sample Markdown’s sequence SHIFT + SEQ PLAY + turn dial . |
# this is a comment |
Comment lines with hash-tag (# ) |
__header__ |
Identify which tables to parse with this unquoted string. Matches against a table’s first column’s header text contents. |
__separator__ |
Help break-down and split up a long button sequence into individual buttons, with one or more quoted strings. These individual buttons are then matched against the button patterns, above. |
A default button pattern file is provided for the Qun mk2 synthesizer.
%digits%
macro. Can embed the macro in a short string, too.%digits%
macro creates a value from digits in a button sequence. It expands ranges of digits, and carries over single digits. Carefully craft the regex to make the most of this macro.
^My Pattern 2-5,8 Here$ = %digits%
, VALUE = 23458
.%digits%
macro, carefully craft the regex. RegEx key match supports a single capture group: the first. Use this to identify the important digits in a match.
(blabla)
is a capturing group pattern, identifying the important part as blabla
. Only the first of these will be converted to a value.(?:blabla)
is a non-capturing (aka “shy”) group, avoiding this becoming the first group, making it invisible to %digits%
.1-8
, not 2
, and to ignore “Press
”, in Press B[1-8] (2nd pattern)
with pattern ^(?:Press )?([B]?\[[1-8]-[1-8]\])[ a-zA-Z0-9\(\)\-,\[\].]*$
. This ignores the “Press
” substring, and captures the first bracket-surrounded digits and digit ranges. See this tricky match on pythex.__header__ = Button
__separator__ = "+"
^SHIFT$ = s
^[B]?(utton)?[ ]?\d[ \-a-zA-Z]*( \(Long press\))?$ = %digits%
^(turn)?[ ]?dial[ a-z]*?$ = d
^NO( \(<\))?[ a-z]*$ = n
Encode filenames and layer names for all layers matchable in the button pattern file.
A default imageset file and directory is provided for the Qun mk2 synthesizer.
image_file,layer_name,x_pos,y_pos,width,height
bg.png,BG,0,0,1856,1236
s.png,s,1572,157
o.png,1,78,631
/imageset
/bg.png
/s.png
/o.png
Button
. This matches the default patset file.<br>
tag is used only once
--md-out-file
, an image link will be added, if missing.| Button | Description |
|:----------------------------------:|-------------------------------------|
| B1 + B2 <br>  | A button sequence and image |
| SHIFT + B3 <br> | No image. Will injected with image. |
| SYS + B4 | No br-tag. Won't receive image. |
python3 .
– base commandpython3 . imageset -h
– sub-command will need parameters from base commandout
directorypython3 . --md-file test.md --gif imageset
For this script’s README.md
to output both PNG and GIF to doc
:
python3 . --md-file README.md --image-height 56 --image-out-dir doc imageset
python3 . --md-file README.md --gif --image-height 56 --image-out-dir doc imageset
python3 . --md-file test.md --md-out-file out_test_md.md
Assumes BASH, changes directory for clarity’s sake, assumes Qun repository is cloned to ../Qun-mk2
:
MDPICGEN=$(PWD) ; cd ../Qun-mk2 ; python3 $MDPICGEN/. --md-file README.md --md-out-file README-merge_me.md --image-out-dir manual_images/but --image-height 56 --gif imageset --imageset-file $MDPICGEN/qunmk2_imageset.csv --imageset-dir $MDPICGEN/imageset ; cd -
python3 . --md-file test.md --print-extract
python3 . --md-file test.md --print-extract --button-pattern-file custom.patset
python3 . --md-file test.md --print-formatted
<br>
tag should be present in a cell for a matching table’s first-column. With --md-out-file
, each br-tag will result in a new image added to the Markdown.Currently, column one is the only column for image extraction and placement. Markdown tables can be written with, or without boundary edge markers (|), which complicates parsing.
<bp> <bpr> <bpc> <bpg>
tags, which could arbitrarily insert a button picture or columns:| Button | Function |
|------------------------------------------|------------------------------------------|
| SHIFT + SEQ PLAY + turn dial | <bp> <-BP Inserts image before this text |
| SHIFT + SEQ PLAY + turn dial BPR-> <bpr> | Inserts line-break, and then new image |
| SHIFT + SEQ PLAY + turn dial BPC-> <bpc> | Inserts new column |
| SHIFT + SEQ PLAY + turn dial BPG-> <bpg> | Inserts new GIF |