mdforge 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mdforge-0.1.0 → mdforge-0.2.0}/LICENSE +0 -0
- mdforge-0.2.0/PKG-INFO +606 -0
- mdforge-0.2.0/README.md +582 -0
- mdforge-0.2.0/mdforge/__init__.py +15 -0
- mdforge-0.2.0/mdforge/_norm.py +107 -0
- mdforge-0.2.0/mdforge/_utils.py +62 -0
- mdforge-0.2.0/mdforge/container.py +214 -0
- mdforge-0.2.0/mdforge/document.py +79 -0
- mdforge-0.2.0/mdforge/element.py +225 -0
- mdforge-0.2.0/mdforge/elements/__init__.py +11 -0
- mdforge-0.2.0/mdforge/elements/_image.py +45 -0
- mdforge-0.2.0/mdforge/elements/block/__init__.py +14 -0
- mdforge-0.2.0/mdforge/elements/block/basic.py +109 -0
- mdforge-0.2.0/mdforge/elements/block/lists/__init__.py +11 -0
- mdforge-0.2.0/mdforge/elements/block/lists/definition.py +130 -0
- mdforge-0.2.0/mdforge/elements/block/lists/item.py +209 -0
- mdforge-0.2.0/mdforge/elements/block/section.py +49 -0
- mdforge-0.2.0/mdforge/elements/block/table/__init__.py +11 -0
- mdforge-0.2.0/mdforge/elements/block/table/_context.py +427 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/flavor.py +87 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/flavors.py +31 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/frame/__init__.py +4 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/frame/frame.py +492 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/frame/variants.py +98 -0
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/list/__init__.py +3 -0
- mdforge-0.2.0/mdforge/elements/block/table/_params.py +215 -0
- mdforge-0.2.0/mdforge/elements/block/table/_vcell.py +177 -0
- mdforge-0.2.0/mdforge/elements/block/table/cell.py +151 -0
- mdforge-0.2.0/mdforge/elements/block/table/table.py +241 -0
- mdforge-0.2.0/mdforge/elements/block/text.py +45 -0
- mdforge-0.2.0/mdforge/elements/inline/__init__.py +11 -0
- mdforge-0.2.0/mdforge/elements/inline/basic.py +66 -0
- mdforge-0.2.0/mdforge/elements/inline/text.py +152 -0
- mdforge-0.2.0/mdforge/exceptions.py +21 -0
- mdforge-0.2.0/mdforge/types.py +23 -0
- {mdforge-0.1.0 → mdforge-0.2.0}/pyproject.toml +41 -16
- mdforge-0.1.0/PKG-INFO +0 -29
- mdforge-0.1.0/README.md +0 -8
- mdforge-0.1.0/mdforge/__init__.py +0 -13
- mdforge-0.1.0/mdforge/_element.py +0 -12
- mdforge-0.1.0/mdforge/document.py +0 -57
- mdforge-0.1.0/mdforge/elements.py +0 -65
|
File without changes
|
mdforge-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mdforge
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Pythonic Markdown generator
|
|
5
|
+
Home-page: https://github.com/mm21/mdforge
|
|
6
|
+
Author: mm21
|
|
7
|
+
Author-email: mm21.dev@gmail.com
|
|
8
|
+
Requires-Python: >=3.12,<4.0
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Dist: pyrollup (>=0.1.0,<0.2.0)
|
|
21
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# MDForge
|
|
25
|
+
Pythonic multi-flavor Markdown generator
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/mdforge)
|
|
28
|
+
[](https://pypi.org/project/mdforge)
|
|
29
|
+
[]()
|
|
30
|
+
[]()
|
|
31
|
+
[](https://github.com/psf/black)
|
|
32
|
+
|
|
33
|
+
- [MDForge](#mdforge)
|
|
34
|
+
- [Motivation](#motivation)
|
|
35
|
+
- [Getting started](#getting-started)
|
|
36
|
+
- [Working with documents](#working-with-documents)
|
|
37
|
+
- [Pythonic Markdown elements](#pythonic-markdown-elements)
|
|
38
|
+
- [Formatting](#formatting)
|
|
39
|
+
- [Lists](#lists)
|
|
40
|
+
- [Tables](#tables)
|
|
41
|
+
- [Images](#images)
|
|
42
|
+
- [Advanced features](#advanced-features)
|
|
43
|
+
- [HTML attributes](#html-attributes)
|
|
44
|
+
- [Pandoc extensions](#pandoc-extensions)
|
|
45
|
+
|
|
46
|
+
## Motivation
|
|
47
|
+
|
|
48
|
+
MDForge provides a Pythonic interface for generating Markdown documents programmatically. It allows you to construct a document tree and render it for various Markdown flavors, making it ideal for applications such as documentation generators and report builders. It emphasizes clean flavor-agnostic elements and type safety.
|
|
49
|
+
|
|
50
|
+
While MDForge supports the rich capabilities of [Pandoc's Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown), it is designed to support generating documents with multiple flavors from the same document model. Other flavors currently planned are:
|
|
51
|
+
|
|
52
|
+
- [GitHub Flavored Markdown](https://github.github.com/gfm/)
|
|
53
|
+
- [MyST Markdown](https://mystmd.org/guide)
|
|
54
|
+
|
|
55
|
+
## Getting started
|
|
56
|
+
|
|
57
|
+
First, install using pip:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install mdforge
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Create your first Markdown document:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from mdforge import Document, Heading, Paragraph, Strong
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
|
|
69
|
+
# create a document
|
|
70
|
+
doc = Document()
|
|
71
|
+
|
|
72
|
+
# add elements
|
|
73
|
+
doc += [
|
|
74
|
+
Heading("My first MDForge document"),
|
|
75
|
+
Paragraph("Hello, ", Strong("world"), "!"),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
# render pandoc-flavored Markdown document to a file
|
|
79
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This is rendered as:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
# My first MDForge document
|
|
86
|
+
|
|
87
|
+
Hello, **world**!
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Working with documents
|
|
92
|
+
|
|
93
|
+
The `Document` class is the fundamental interface to create a Markdown document. It implements a [Composite pattern](https://en.wikipedia.org/wiki/Composite_pattern) wherein elements are composed using the `+=` operator:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from mdforge import BulletList, Document, Section
|
|
97
|
+
|
|
98
|
+
# create a document with frontmatter
|
|
99
|
+
doc = Document(frontmatter={"title": "My Document", "author": "Me"})
|
|
100
|
+
|
|
101
|
+
# create sections, automatically adding headings of the appropriate level
|
|
102
|
+
intro = Section("Introduction")
|
|
103
|
+
intro += "This is an introduction paragraph."
|
|
104
|
+
|
|
105
|
+
key_points = Section("Key Points")
|
|
106
|
+
key_points += BulletList(["Point 1", "Point 2", "Point 3"])
|
|
107
|
+
|
|
108
|
+
# add subsections for each point
|
|
109
|
+
key_points += [
|
|
110
|
+
Section(
|
|
111
|
+
"Point 1",
|
|
112
|
+
elements=[
|
|
113
|
+
"Elaboration on point 1:",
|
|
114
|
+
BulletList(["Point 1-a", "Point 1-b"]),
|
|
115
|
+
],
|
|
116
|
+
),
|
|
117
|
+
Section("Point 2", elements=["Elaboration on point 2."]),
|
|
118
|
+
Section("Point 3", elements=["Elaboration on point 3."]),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# add sections to document
|
|
122
|
+
doc += [intro, key_points]
|
|
123
|
+
|
|
124
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This is rendered as:
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
---
|
|
131
|
+
title: My Document
|
|
132
|
+
author: Me
|
|
133
|
+
---
|
|
134
|
+
# Introduction
|
|
135
|
+
|
|
136
|
+
This is an introduction paragraph.
|
|
137
|
+
|
|
138
|
+
# Key Points
|
|
139
|
+
|
|
140
|
+
- Point 1
|
|
141
|
+
- Point 2
|
|
142
|
+
- Point 3
|
|
143
|
+
<!-- end of list -->
|
|
144
|
+
|
|
145
|
+
## Point 1
|
|
146
|
+
|
|
147
|
+
Elaboration on point 1:
|
|
148
|
+
|
|
149
|
+
- Point 1-a
|
|
150
|
+
- Point 1-b
|
|
151
|
+
<!-- end of list -->
|
|
152
|
+
|
|
153
|
+
## Point 2
|
|
154
|
+
|
|
155
|
+
Elaboration on point 2.
|
|
156
|
+
|
|
157
|
+
## Point 3
|
|
158
|
+
|
|
159
|
+
Elaboration on point 3.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Note the following observations:
|
|
164
|
+
|
|
165
|
+
- Sections can be nested in other sections, with optional headings
|
|
166
|
+
- Heading levels for sections are automatically set by their placement in the hierarchy
|
|
167
|
+
- Strings added to a document or section via `+=` are treated as raw text
|
|
168
|
+
- A comment is placed at the end of each list; this disambiguates the end of one list and the start of the next, in case there are no other elements in between
|
|
169
|
+
|
|
170
|
+
## Pythonic Markdown elements
|
|
171
|
+
|
|
172
|
+
MDForge provides a set of Markdown elements that can be composed to create rich documents. Not all common elements are implemented yet, e.g. code blocks. Nonetheless, you can insert raw text (which may be pre-formatted as Markdown) using `+=`.
|
|
173
|
+
|
|
174
|
+
We have already seen basic elements and container elements. The following examples illustrate the use of other common elements.
|
|
175
|
+
|
|
176
|
+
### Formatting
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from mdforge import (
|
|
180
|
+
Document,
|
|
181
|
+
Emph,
|
|
182
|
+
Paragraph,
|
|
183
|
+
Strikethrough,
|
|
184
|
+
Strong,
|
|
185
|
+
Underline,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
doc = Document()
|
|
189
|
+
|
|
190
|
+
# basic formatting
|
|
191
|
+
doc += Strong("Bold text")
|
|
192
|
+
doc += Emph("Italicized text")
|
|
193
|
+
doc += Strikethrough("Strikethrough text")
|
|
194
|
+
doc += Underline("Underlined text")
|
|
195
|
+
|
|
196
|
+
# combined formatting
|
|
197
|
+
doc += Strong(Emph("Bold and italicized text"))
|
|
198
|
+
|
|
199
|
+
# mixed formatting in a paragraph, automatically putting spaces between elements
|
|
200
|
+
doc += Paragraph(
|
|
201
|
+
"Normal text with",
|
|
202
|
+
Strong("bold"),
|
|
203
|
+
"and",
|
|
204
|
+
Emph("italic"),
|
|
205
|
+
"segments.",
|
|
206
|
+
auto_space=True,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This is rendered as:
|
|
213
|
+
|
|
214
|
+
```markdown
|
|
215
|
+
**Bold text**
|
|
216
|
+
|
|
217
|
+
_Italicized text_
|
|
218
|
+
|
|
219
|
+
~~Strikethrough text~~
|
|
220
|
+
|
|
221
|
+
[Underlined text]{.underline}
|
|
222
|
+
|
|
223
|
+
**_Bold and italicized text_**
|
|
224
|
+
|
|
225
|
+
Normal text with **bold** and _italic_ segments.
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Note the following observations:
|
|
230
|
+
|
|
231
|
+
- Here we pass `auto_space=True` in `Paragraph()`; this will automatically insert spaces between the inline elements passed to it
|
|
232
|
+
- As underline is not supported in CommonMark, pandoc implements its own [underline syntax](https://pandoc.org/MANUAL.html#underline)
|
|
233
|
+
|
|
234
|
+
### Lists
|
|
235
|
+
|
|
236
|
+
MDForge supports bullet lists, numbered lists, and definition lists:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from mdforge import (
|
|
240
|
+
BulletList,
|
|
241
|
+
DefinitionItem,
|
|
242
|
+
DefinitionList,
|
|
243
|
+
Document,
|
|
244
|
+
ListItem,
|
|
245
|
+
NumberedList,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
doc = Document()
|
|
249
|
+
|
|
250
|
+
# bullet list
|
|
251
|
+
doc += BulletList(["Item 1", "Item 2", "Item 3"])
|
|
252
|
+
|
|
253
|
+
# numbered list
|
|
254
|
+
doc += NumberedList(["First", "Second", "Third"])
|
|
255
|
+
|
|
256
|
+
# definition list
|
|
257
|
+
doc += DefinitionList(
|
|
258
|
+
[
|
|
259
|
+
DefinitionItem("Term A", "Definition A"),
|
|
260
|
+
DefinitionItem("Term B", ["Definition B1", "Definition B2"]),
|
|
261
|
+
],
|
|
262
|
+
compact=True,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# nested lists
|
|
266
|
+
doc += BulletList(
|
|
267
|
+
[
|
|
268
|
+
"Item 1",
|
|
269
|
+
ListItem(
|
|
270
|
+
"Item 2",
|
|
271
|
+
[
|
|
272
|
+
"Item 2-1",
|
|
273
|
+
"Item 2-2",
|
|
274
|
+
],
|
|
275
|
+
),
|
|
276
|
+
]
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# mixed nested lists
|
|
280
|
+
doc += BulletList(
|
|
281
|
+
[
|
|
282
|
+
"Item 1",
|
|
283
|
+
ListItem(
|
|
284
|
+
"Item 2",
|
|
285
|
+
NumberedList(
|
|
286
|
+
[
|
|
287
|
+
"Item 2-1",
|
|
288
|
+
"Item 2-2",
|
|
289
|
+
]
|
|
290
|
+
),
|
|
291
|
+
),
|
|
292
|
+
]
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This is rendered as:
|
|
299
|
+
|
|
300
|
+
```markdown
|
|
301
|
+
- Item 1
|
|
302
|
+
- Item 2
|
|
303
|
+
- Item 3
|
|
304
|
+
<!-- end of list -->
|
|
305
|
+
|
|
306
|
+
1. First
|
|
307
|
+
1. Second
|
|
308
|
+
1. Third
|
|
309
|
+
<!-- end of list -->
|
|
310
|
+
|
|
311
|
+
Term A
|
|
312
|
+
: Definition A
|
|
313
|
+
|
|
314
|
+
Term B
|
|
315
|
+
: Definition B1
|
|
316
|
+
: Definition B2
|
|
317
|
+
|
|
318
|
+
<!-- end of definition list -->
|
|
319
|
+
|
|
320
|
+
- Item 1
|
|
321
|
+
- Item 2
|
|
322
|
+
- Item 2-1
|
|
323
|
+
- Item 2-2
|
|
324
|
+
<!-- end of list -->
|
|
325
|
+
<!-- end of list -->
|
|
326
|
+
|
|
327
|
+
- Item 1
|
|
328
|
+
- Item 2
|
|
329
|
+
1. Item 2-1
|
|
330
|
+
1. Item 2-2
|
|
331
|
+
<!-- end of list -->
|
|
332
|
+
<!-- end of list -->
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Note the following observations:
|
|
337
|
+
|
|
338
|
+
- Here we pass `compact=True` in `DefinitionList()`; this will generate [compact definition lists](https://pandoc.org/MANUAL.html#extension-compact_definition_lists) for pandoc flavor
|
|
339
|
+
- As mentioned above, a comment disambiguates the end of one list and the start of the next
|
|
340
|
+
|
|
341
|
+
### Tables
|
|
342
|
+
|
|
343
|
+
MDForge provides powerful table support with cell spanning, alignment, and formatting:
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
from mdforge import BulletList, Cell, Document, Table
|
|
347
|
+
|
|
348
|
+
doc = Document()
|
|
349
|
+
|
|
350
|
+
# simple table
|
|
351
|
+
doc += Table(
|
|
352
|
+
[
|
|
353
|
+
["Cell 1-1", "Cell 1-2"],
|
|
354
|
+
["Cell 2-1", "Cell 2-2"],
|
|
355
|
+
]
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# table with alignment
|
|
359
|
+
doc += Table(
|
|
360
|
+
[["Cell 1", "Cell 2", "Cell 3"]],
|
|
361
|
+
align=["left", "center", "right"],
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# table with header and footer (needs block=True)
|
|
365
|
+
doc += Table(
|
|
366
|
+
[["Cell 1", "Cell 2"]],
|
|
367
|
+
header=["Header 1", "Header 2"],
|
|
368
|
+
footer=["Footer 1", "Footer 2"],
|
|
369
|
+
block=True,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# table with row and column spanning (needs block=True)
|
|
373
|
+
doc += Table(
|
|
374
|
+
[
|
|
375
|
+
["Cell 1-1", "Cell 1-2", "Cell 1-3"],
|
|
376
|
+
["Cell 2-1", "Cell 2-2", "Cell 2-3"],
|
|
377
|
+
],
|
|
378
|
+
header=[
|
|
379
|
+
[Cell("Column 1", rspan=2), Cell("Columns 2 & 3", cspan=2)],
|
|
380
|
+
["Column 2", "Column 3"],
|
|
381
|
+
],
|
|
382
|
+
block=True,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# table with each cell wrapped in an explicit paragraph if it doesn't
|
|
386
|
+
# already contain block content (needs block=True)
|
|
387
|
+
doc += Table(
|
|
388
|
+
[
|
|
389
|
+
[
|
|
390
|
+
BlockContainer(
|
|
391
|
+
"This text is implicitly wrapped in a paragraph",
|
|
392
|
+
BulletList(["Item 1", "Item 2"]),
|
|
393
|
+
),
|
|
394
|
+
"Cell 2",
|
|
395
|
+
"Cell 3",
|
|
396
|
+
]
|
|
397
|
+
],
|
|
398
|
+
align=["left", "center", "right"],
|
|
399
|
+
block=True,
|
|
400
|
+
loose=True,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# table with character widths
|
|
404
|
+
doc += Table(
|
|
405
|
+
[["Short text", "This is longer text that will be wrapped"]],
|
|
406
|
+
widths=[15, 20],
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
# table with percentage widths
|
|
410
|
+
doc += Table(
|
|
411
|
+
[["25% width", "75% width"]],
|
|
412
|
+
widths_pct=[25, 75],
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
This is rendered as:
|
|
419
|
+
|
|
420
|
+
```markdown
|
|
421
|
+
<!-- table start -->
|
|
422
|
+
|
|
423
|
+
---------- ----------
|
|
424
|
+
Cell 1-1 Cell 1-2
|
|
425
|
+
|
|
426
|
+
Cell 2-1 Cell 2-2
|
|
427
|
+
---------------------
|
|
428
|
+
|
|
429
|
+
<!-- table end -->
|
|
430
|
+
|
|
431
|
+
<!-- table start: align=['left', 'center', 'right'] -->
|
|
432
|
+
|
|
433
|
+
-------- -------- --------
|
|
434
|
+
Cell 1 Cell 2 Cell 3
|
|
435
|
+
|
|
436
|
+
--------------------------
|
|
437
|
+
|
|
438
|
+
<!-- table end -->
|
|
439
|
+
|
|
440
|
+
<!-- table start: block=True -->
|
|
441
|
+
|
|
442
|
+
+----------+----------+
|
|
443
|
+
| Header 1 | Header 2 |
|
|
444
|
+
+==========+==========+
|
|
445
|
+
| Cell 1 | Cell 2 |
|
|
446
|
+
+==========+==========+
|
|
447
|
+
| Footer 1 | Footer 2 |
|
|
448
|
+
+==========+==========+
|
|
449
|
+
|
|
450
|
+
<!-- table end -->
|
|
451
|
+
|
|
452
|
+
<!-- table start: block=True -->
|
|
453
|
+
|
|
454
|
+
+----------+---------------------+
|
|
455
|
+
| Column 1 | Columns 2 & 3 |
|
|
456
|
+
| +----------+----------+
|
|
457
|
+
| | Column 2 | Column 3 |
|
|
458
|
+
+==========+==========+==========+
|
|
459
|
+
| Cell 1-1 | Cell 1-2 | Cell 1-3 |
|
|
460
|
+
+----------+----------+----------+
|
|
461
|
+
| Cell 2-1 | Cell 2-2 | Cell 2-3 |
|
|
462
|
+
+----------+----------+----------+
|
|
463
|
+
|
|
464
|
+
<!-- table end -->
|
|
465
|
+
|
|
466
|
+
<!-- table start: align=['left', 'center', 'right'], block=True, loose=True -->
|
|
467
|
+
|
|
468
|
+
+:-----------------------------------------------+:-------------:+--------------:+
|
|
469
|
+
| This text is implicitly wrapped in a paragraph | <p>Cell 2</p> | <p>Cell 3</p> |
|
|
470
|
+
| | | |
|
|
471
|
+
| - Item 1 | | |
|
|
472
|
+
| - Item 2 | | |
|
|
473
|
+
| <!-- end of list --> | | |
|
|
474
|
+
+------------------------------------------------+---------------+---------------+
|
|
475
|
+
|
|
476
|
+
<!-- table end -->
|
|
477
|
+
|
|
478
|
+
<!-- table start: widths=[15, 20] -->
|
|
479
|
+
|
|
480
|
+
----------------- ----------------------
|
|
481
|
+
Short text This is longer text
|
|
482
|
+
that will be wrapped
|
|
483
|
+
|
|
484
|
+
----------------------------------------
|
|
485
|
+
|
|
486
|
+
<!-- table end -->
|
|
487
|
+
|
|
488
|
+
<!-- table start: widths_pct=[25, 75] -->
|
|
489
|
+
|
|
490
|
+
----------- -----------------------------
|
|
491
|
+
25% width 75% width
|
|
492
|
+
|
|
493
|
+
-----------------------------------------
|
|
494
|
+
|
|
495
|
+
<!-- table end -->
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Note the following observations:
|
|
500
|
+
|
|
501
|
+
- Merged cells and footers are only supported with `block=True`; this causes pandoc flavor to use a [grid table](https://pandoc.org/MANUAL.html#extension-grid_tables) instead of a [multiline table](https://pandoc.org/MANUAL.html#extension-multiline_tables)
|
|
502
|
+
- Pass `loose=True` to ensure all cells are wrapped in a paragraph, even if they don't get parsed as block content (requires `block=True`)
|
|
503
|
+
- This ensures consistently-padded cells
|
|
504
|
+
- Use `BlockContainer` to wrap multiple block elements in a single element
|
|
505
|
+
|
|
506
|
+
### Images
|
|
507
|
+
|
|
508
|
+
MDForge supports both inline and block images:
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
from mdforge import BlockImage, Document, InlineImage, Paragraph
|
|
512
|
+
|
|
513
|
+
doc = Document()
|
|
514
|
+
|
|
515
|
+
# inline image in a paragraph
|
|
516
|
+
doc += Paragraph(
|
|
517
|
+
"Here is an inline image",
|
|
518
|
+
InlineImage("./image.png", alt_text="Inline image"),
|
|
519
|
+
"in a paragraph.",
|
|
520
|
+
auto_space=True,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# block image with caption and alignment
|
|
524
|
+
doc += BlockImage("./image.png", caption="Block image", align="center")
|
|
525
|
+
|
|
526
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
This is rendered as:
|
|
530
|
+
|
|
531
|
+
```markdown
|
|
532
|
+
Here is an inline image  in a paragraph.
|
|
533
|
+
|
|
534
|
+
{fig-align="center"}
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Note the following observations:
|
|
539
|
+
|
|
540
|
+
- Support for caption and alignment depends on flavor
|
|
541
|
+
|
|
542
|
+
## Advanced features
|
|
543
|
+
|
|
544
|
+
### HTML attributes
|
|
545
|
+
|
|
546
|
+
For pandoc flavor, HTML attributes can be added to various elements:
|
|
547
|
+
|
|
548
|
+
```python
|
|
549
|
+
from mdforge import Attributes, Document, Heading, Ref, Span
|
|
550
|
+
|
|
551
|
+
doc = Document()
|
|
552
|
+
|
|
553
|
+
# heading with ID, classes, and custom attributes
|
|
554
|
+
my_heading = Heading(
|
|
555
|
+
"Heading with attributes",
|
|
556
|
+
attributes=Attributes(
|
|
557
|
+
html_id="my-heading",
|
|
558
|
+
css_classes=["class1", "class2"],
|
|
559
|
+
attrs={"style": "color: blue;"},
|
|
560
|
+
),
|
|
561
|
+
)
|
|
562
|
+
doc += my_heading
|
|
563
|
+
|
|
564
|
+
# span with attributes
|
|
565
|
+
doc += Span(
|
|
566
|
+
"Text with attributes",
|
|
567
|
+
attributes=Attributes(html_id="my-span", css_classes="class1"),
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
# reference to heading by id
|
|
571
|
+
doc += Ref(my_heading, "See previous heading")
|
|
572
|
+
|
|
573
|
+
doc.render_file("doc.md", flavor="pandoc")
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
This is rendered as:
|
|
577
|
+
|
|
578
|
+
```markdown
|
|
579
|
+
# Heading with attributes {#my-heading .class1 .class2 style="color: blue;"}
|
|
580
|
+
|
|
581
|
+
[Text with attributes]{#my-span .class1}
|
|
582
|
+
|
|
583
|
+
[See previous heading](#my-heading)
|
|
584
|
+
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Pandoc extensions
|
|
588
|
+
|
|
589
|
+
As MDForge is designed with pandoc compatibility in mind, it automatically tracks required pandoc extensions:
|
|
590
|
+
|
|
591
|
+
```python
|
|
592
|
+
from mdforge import Attributes, Document, Heading, Strikethrough
|
|
593
|
+
|
|
594
|
+
doc = Document()
|
|
595
|
+
|
|
596
|
+
# requires "header_attributes"
|
|
597
|
+
doc += Heading("Heading 1", attributes=Attributes(html_id="heading-1"))
|
|
598
|
+
|
|
599
|
+
# requires "strikeout"
|
|
600
|
+
doc += Strikethrough("This text is struck through")
|
|
601
|
+
|
|
602
|
+
# get required pandoc extensions
|
|
603
|
+
extensions = doc.get_pandoc_extensions()
|
|
604
|
+
assert extensions == ["header_attributes", "strikeout"]
|
|
605
|
+
```
|
|
606
|
+
|