Skip to content

Commit af727e8

Browse files
committed
A lua filter enabling multiple columns in Latex, PDF, and HTML documents
based on Pandoc fenced divs
1 parent 5d2024b commit af727e8

File tree

6 files changed

+517
-0
lines changed

6 files changed

+517
-0
lines changed

column-div/Makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
DIFF ?= diff --strip-trailing-cr -u
2+
3+
.PHONY: test
4+
5+
test: test_latex test_html
6+
7+
test_html: sample.md expected.html column-div.lua
8+
@pandoc -s --lua-filter column-div.lua --to=html $< \
9+
| $(DIFF) expected.html -
10+
11+
test_latex: sample.md expected.tex column-div.lua
12+
@pandoc -s --lua-filter column-div.lua --to=latex $< \
13+
| $(DIFF) expected.tex -
14+
15+
expected.html: sample.md column-div.lua
16+
pandoc -s --lua-filter column-div.lua --output $@ $<
17+
18+
expected.tex: sample.md column-div.lua
19+
pandoc -s --lua-filter column-div.lua --output $@ $<

column-div/README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
title: "Column Div - leverage Pandoc native divs to make columns
3+
an other things"
4+
author: "Christophe Agathon"
5+
---
6+
7+
Column Div
8+
=======
9+
10+
Columns and other things with Pandoc's markdown
11+
12+
This Lua filter for Pandoc improves Pandoc's Div usage.Especially
13+
fenced divs witten in Pandocs markdown.
14+
15+
v1.0. Copyright: © 2021 Christophe Agathon
16+
17+
License: MIT - see LICENSE file for details.
18+
19+
Introduction
20+
------------
21+
Pandoc fenced divs can be very powerful allowing providing, in
22+
theory many document formating possibilities. Unfortunately, plain
23+
Panfoc processing doesn't make full adventage of it and discards
24+
some formating in HTML outputs and most of it in Latex outputs.
25+
26+
Multiple columns in document are only partialy accessible in
27+
Beamer (not plain Latex) and HTML outputs.
28+
29+
As a result, it's not possible to render fancy multi columns
30+
PDF document from markdown sources.
31+
32+
The main purpose of this filter is to make it possible and give
33+
similar formating features for both Latex/PDF and HTML outputs.
34+
35+
Usage
36+
-----
37+
38+
### Basic usage
39+
40+
Copy `column-div.lua` in your document folder or in your pandoc
41+
data directory (details in
42+
[Pandoc's manual](https://pandoc.org/MANUAL.html#option--lua-filter)).
43+
Run it on your document with a `--luafilter` option:
44+
45+
```bash
46+
pandoc --luafilter column-div.lua SOURCE.md -o OUTPUT.pdf
47+
48+
```
49+
50+
or specify it in a defaults file (details in
51+
[Pandoc's manual](https://pandoc.org/MANUAL.html#option--defaults)).
52+
53+
This will generate consistent HTML, Latex and PDF outputs from
54+
Pandoc markdown files.
55+
56+
### Formating the document
57+
58+
Everything is done with Pandoc's fenced divs with class names and
59+
attributes. The attributes are similar to those from Latex and/or
60+
HTML styling.
61+
62+
#### Multiple balanced columns
63+
For Latex and PDF output, you will need to call the multicol
64+
package. This can be done un the YAML header.
65+
66+
**Example:**
67+
68+
```markdown
69+
---
70+
header-includes:
71+
- |
72+
```{=latex}
73+
\usepackage{multicol}
74+
75+
```
76+
---
77+
78+
Some regular text
79+
80+
:::: {.multicols column-count="2"}
81+
Some text formatted on 2 columns
82+
::::
83+
```
84+
85+
* Latex output is done with `multicols` environment.
86+
* HTML output uses `style="column-count: 2"` on a div block.
87+
88+
#### Unbalanced columns
89+
90+
No specific Latex package are needed. We use Nested Pandoc divs in
91+
the same way that columns and column environments are used in
92+
Beamer/Latex.
93+
94+
**Example:**
95+
96+
```markdown
97+
98+
:::::::: {.columns}
99+
:::: {.column width="20%" valign="c"}
100+
Some text or image using 20% of the page width.
101+
::::
102+
:::: {.column width="80%" valign="c"}
103+
Some text or image using 80% of the page with.
104+
::::
105+
::::::::
106+
```
107+
108+
* Beamer/Latex output is based on columns and column environments
109+
* Plain Latex (and PDF) rendering use minipage environments
110+
* HTML rendering is not affected by this filter since Pandoc do it
111+
well already (based on divs with `width` attributes).
112+
113+
#### Other usages
114+
115+
HTML : you can already create divs with whatever class names youl
116+
like and style them with `style=" … "` attributes. This is
117+
proccessed by Pandoc and as nothing to do with this filter.
118+
119+
This filter allows to do the same in Latex (and PDF).
120+
The class name is used as the environment name and a
121+
`data-latex=" … "` attribute allows you to pass options and
122+
parameters to the `\begin` instruction.
123+
124+
To Do
125+
-----
126+
127+
Others multi column features could be implemented as column
128+
spacing, rules, etc.
129+
130+
Since Pandoc does a very good job with the `width` styling
131+
attribute to implement variable column width, it could easily
132+
support HTML balanced column via the `column-count` attribute.
133+
134+
Contributing
135+
------------
136+
137+
PRs welcome.
138+

column-div/column-div.lua

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
--[[
2+
column-div - leverage Pandoc native divs to make balanced and unbalanced column
3+
and other things based on class name and attirbutes.
4+
5+
Copyright: © 2021 Christophe Agathon <[email protected]>
6+
License: MIT – see LICENSE file for details
7+
8+
Credits: Romain Lesur and Yihui Xie for the original column filter
9+
implementation (output in beamer format).
10+
11+
Output: latex, pdf, html
12+
13+
Usage: classname attributes
14+
balanced columns .columns column-count
15+
columns(container) .columns
16+
column(each column) .column width(percent) valign(t|c|b)
17+
other divs .<somename> data-latex
18+
19+
See README.md for details
20+
21+
Note: You need to include multicol latex package to get balanced columns
22+
in latex or pdf
23+
I tried to use well known html or latex parameter.
24+
Even if lua doen't like hyphens like in column-count.
25+
--]]
26+
local List = require 'pandoc.List'
27+
28+
function Div(div)
29+
options = ''
30+
local env = div.classes[1]
31+
local returned_list
32+
local begin_env
33+
local end_env
34+
local opt
35+
36+
-- if the div has no class, the object is left unchanged
37+
if not env then return nil end
38+
39+
-- if the output is beamer do columns
40+
if FORMAT:match 'beamer' then
41+
-- build the returned list of blocks
42+
begin_env = List:new{pandoc.RawBlock('tex',
43+
'\\begin' .. '{' .. env .. '}' .. options)}
44+
end_env = List:new{pandoc.RawBlock('tex', '\\end{' .. env .. '}')}
45+
returned_list = begin_env .. div.content .. end_env
46+
47+
-- if the format is latex then do minipage and others (like multicol)
48+
elseif FORMAT:match 'latex' then
49+
-- build the returned list of blocks
50+
if env == 'column' then
51+
--opt = div.attributes['width']
52+
opt = div.attributes.width
53+
if opt then
54+
local width=tonumber(string.match(opt,'(%f[%d]%d[,.%d]*%f[%D])%%'))/100
55+
options = '{' .. tostring(width) .. '\\columnwidth}'
56+
end
57+
58+
opt = div.attributes.valign
59+
if opt then options = '[' .. opt .. ']' .. options end
60+
61+
begin_env = List:new{pandoc.RawBlock('tex',
62+
'\\begin' .. '{' .. 'minipage' .. '}' .. options)}
63+
end_env = List:new{pandoc.RawBlock('tex', '\\end{' .. 'minipage' .. '}')}
64+
returned_list = begin_env .. div.content .. end_env
65+
66+
elseif env == 'columns' then
67+
-- merge two consecutives RawBlocks (\end... and \begin...)
68+
-- to get rid of the unwanted blank line
69+
local blocks = div.content
70+
local rbtxt = ''
71+
72+
for i = #blocks-1, 1, -1 do
73+
if i > 1 and blocks[i].tag == 'RawBlock' and blocks[i].text:match 'end'
74+
and blocks[i+1].tag == 'RawBlock' and blocks[i+1].text:match 'begin'
75+
then
76+
rbtxt = blocks[i].text .. blocks[i+1].text
77+
blocks:remove(i+1)
78+
blocks[i].text = rbtxt
79+
end
80+
end
81+
returned_list=blocks
82+
83+
else
84+
-- other environments ex: multicols
85+
86+
-- process supported options
87+
opt = div.attributes['column-count'] -- this synthax needed due to '_'
88+
if opt then options = '{' .. opt .. '}' end
89+
90+
-- default if no known options
91+
if options == '' then options = div.attributes.data-latex end
92+
93+
begin_env = List:new{pandoc.RawBlock('tex',
94+
'\\begin' .. '{' .. env .. '}' .. options)}
95+
end_env = List:new{pandoc.RawBlock('tex', '\\end{' .. env .. '}')}
96+
returned_list = begin_env .. div.content .. end_env
97+
end
98+
99+
-- if the format is html add support for multi columns
100+
elseif FORMAT:match 'html' then
101+
opt = div.attributes['column-count']
102+
if opt then
103+
-- add column-count to style if it exists
104+
if div.attributes.style then
105+
div.attributes.style = div.attributes.style ..
106+
'; column-count: ' .. opt
107+
else
108+
div.attributes.style = 'column-count:' .. opt
109+
end
110+
div.attributes['column-count'] = nil
111+
returned_list = List:new{pandoc.Div(div.content, div.attr)}
112+
end
113+
end
114+
return returned_list
115+
end

column-div/expected.html

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!DOCTYPE html>
2+
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="generator" content="pandoc" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
7+
<title>Test</title>
8+
<style>
9+
code{white-space: pre-wrap;}
10+
span.smallcaps{font-variant: small-caps;}
11+
span.underline{text-decoration: underline;}
12+
div.column{display: inline-block; vertical-align: top; width: 50%;}
13+
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
14+
ul.task-list{list-style: none;}
15+
</style>
16+
</head>
17+
<body>
18+
<header id="title-block-header">
19+
<h1 class="title">Test</h1>
20+
</header>
21+
<h1 id="column-div-test">column-div test</h1>
22+
<p>content…</p>
23+
<h2 id="three-columns">Three columns</h2>
24+
<div class="multicols" style="column-count:3">
25+
<p>content…</p>
26+
<p>content…</p>
27+
<p>content…</p>
28+
</div>
29+
<h2 id="two-unbalanced-columns">Two unbalanced columns</h2>
30+
<div class="columns">
31+
<div class="column" data-valign="b" style="width:40%;">
32+
<p>contents…</p>
33+
</div><div class="column" data-valign="b" style="width:60%;">
34+
<p>contents…</p>
35+
</div>
36+
</div>
37+
<h2 id="columns-in-columns">Columns in columns</h2>
38+
<div class="multicols" style="column-count:3">
39+
<div class="columns">
40+
<div class="column" data-valign="b" style="width:20%;">
41+
<p>contents…</p>
42+
</div><div class="column" data-valign="b" style="width:80%;">
43+
<p>contents…</p>
44+
</div>
45+
</div>
46+
<div class="columns">
47+
<div class="column" data-valign="b" style="width:20%;">
48+
<p>contents…</p>
49+
</div><div class="column" data-valign="b" style="width:80%;">
50+
<p>contents…</p>
51+
</div>
52+
</div>
53+
<div class="columns">
54+
<div class="column" data-valign="b" style="width:20%;">
55+
<p>contents…</p>
56+
</div><div class="column" data-valign="b" style="width:80%;">
57+
<p>contents…</p>
58+
</div>
59+
</div>
60+
</div>
61+
</body>
62+
</html>

0 commit comments

Comments
 (0)