improvements
This commit is contained in:
parent
3d889ec117
commit
b1f1d7b5f6
11 changed files with 305 additions and 514 deletions
|
|
@ -17,6 +17,7 @@ This repository contains command line helpers for transforming Markdown into for
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Python 3.9+.
|
- Python 3.9+.
|
||||||
|
- Optional: `PyHyphen` (preferred) or `pyphen` for dictionary-driven hyphenation.
|
||||||
- Optional: `pyfiglet` for FIGlet headings (`pip install pyfiglet`). When unavailable, headings fall back to the H4-style renderer automatically.
|
- Optional: `pyfiglet` for FIGlet headings (`pip install pyfiglet`). When unavailable, headings fall back to the H4-style renderer automatically.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -38,6 +39,11 @@ You can set FIGlet fonts for H1–H3 per document using YAML frontmatter:
|
||||||
h1_font: slant
|
h1_font: slant
|
||||||
h2_font: standard
|
h2_font: standard
|
||||||
h3_font: small
|
h3_font: small
|
||||||
|
margin_left: 4
|
||||||
|
margin_right: 4
|
||||||
|
paragraph_spacing: 1
|
||||||
|
hyphenate: true
|
||||||
|
hyphen_lang: en_US
|
||||||
---
|
---
|
||||||
|
|
||||||
# Main Title
|
# Main Title
|
||||||
|
|
@ -45,6 +51,8 @@ h3_font: small
|
||||||
### Subsection
|
### Subsection
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Font names also accept the special keywords `caps` (force uppercase) and `title` (title case) for any heading. `margin_left` and `margin_right` add leading/trailing spaces to body text, `paragraph_spacing` (or `lines_between_paragraphs`) inserts blank lines between paragraphs, and enabling `hyphenate` activates PyHyphen-based wrapping (optional `hyphen_lang` defaults to `en_US`).
|
||||||
|
|
||||||
If the rendered FIGlet text would exceed the configured width, the converter automatically falls back to the H4-style uppercase heading with a dashed underline.
|
If the rendered FIGlet text would exceed the configured width, the converter automatically falls back to the H4-style uppercase heading with a dashed underline.
|
||||||
|
|
||||||
## Styling via Attribute Blocks
|
## Styling via Attribute Blocks
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
---
|
---
|
||||||
h1_font: colossal
|
h1_font: big
|
||||||
h2_font: big
|
h2_font: small
|
||||||
h3_font: small
|
h3_font: caps
|
||||||
|
paragraph_spacing: 2
|
||||||
|
hyphenate: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# Salientia
|
# Salientia
|
||||||
{:.center}
|
{:.right}
|
||||||
|
|
||||||
## Structa hinc vetitus factum
|
## Structa hinc vetitus factum
|
||||||
|
{:.center}
|
||||||
|
|
||||||
Lorem markdownum omnes tellus, in, Herculeis `reciprocalPower` neque promissa
|
Lorem markdownum omnes tellus, in, Herculeis `reciprocalPower` neque promissa
|
||||||
est Latiae caput umero talia fidissima, nymphae. Foedera et sagittas tenetur
|
est Latiae caput umero talia fidissima, nymphae. Foedera et sagittas tenetur
|
||||||
adde matri esse: talia manibus.
|
adde matri esse: talia manibus.
|
||||||
|
|
||||||
## Fuit video longique dedisse
|
## Fuit video longique dedisse
|
||||||
|
{:.center}
|
||||||
|
|
||||||
Ille stupet, ultra **incunabula agendum urbes** mirer **aper**, metu. Aegides
|
Ille stupet, ultra **incunabula agendum urbes** mirer **aper**, metu. Aegides
|
||||||
novissima sunt turis quatiebant umbrosaque [putares](#mille-parva). Metuunt
|
novissima sunt turis quatiebant umbrosaque [putares](#mille-parva). Metuunt
|
||||||
|
|
@ -25,6 +29,7 @@ omnia inmotusque, est et aetas sonus. Exhibuit coeperunt, canis. Suis minora,
|
||||||
tempore fateor neve moderamine erit. Non *hoc radiis*, Aenea etiamnum cogeris
|
tempore fateor neve moderamine erit. Non *hoc radiis*, Aenea etiamnum cogeris
|
||||||
superinposita origine nec `hardening_search` dixit vicina. Heros subito
|
superinposita origine nec `hardening_search` dixit vicina. Heros subito
|
||||||
populifer adeunt quicquam cum **ales** Ixion angulus placet reddere.
|
populifer adeunt quicquam cum **ales** Ixion angulus placet reddere.
|
||||||
|
{:.right}
|
||||||
|
|
||||||
## Tractaque serpens arva quoque
|
## Tractaque serpens arva quoque
|
||||||
|
|
||||||
167
loremipsum.txt
167
loremipsum.txt
|
|
@ -1,167 +0,0 @@
|
||||||
|
|
||||||
.d8888b. 888d8b 888 d8b
|
|
||||||
d88P Y88b 888Y8P 888 Y8P
|
|
||||||
Y88b. 888 888
|
|
||||||
"Y888b. 8888b. 888888 .d88b. 88888b. 888888888 8888b.
|
|
||||||
"Y88b. "88b888888d8P Y8b888 "88b888 888 "88b
|
|
||||||
"888.d88888888888888888888888 888888 888.d888888
|
|
||||||
Y88b d88P888 888888888Y8b. 888 888Y88b. 888888 888
|
|
||||||
"Y8888P" "Y888888888888 "Y8888 888 888 "Y888888"Y888888
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_____ _ _ _ _
|
|
||||||
/ ____| | | | | | (_)
|
|
||||||
| (___ | |_ _ __ _ _ ___| |_ __ _ | |__ _ _ __ ___
|
|
||||||
\___ \| __| '__| | | |/ __| __/ _` | | '_ \| | '_ \ / __|
|
|
||||||
____) | |_| | | |_| | (__| || (_| | | | | | | | | | (__
|
|
||||||
|_____/ \__|_| \__,_|\___|\__\__,_| |_| |_|_|_| |_|\___|
|
|
||||||
|
|
||||||
|
|
||||||
_ _ _ __ _
|
|
||||||
| | (_) | / _| | |
|
|
||||||
__ _____| |_ _| |_ _ _ ___ | |_ __ _ ___| |_ _ _ _ __ ___
|
|
||||||
\ \ / / _ \ __| | __| | | / __| | _/ _` |/ __| __| | | | '_ ` _ \
|
|
||||||
\ V / __/ |_| | |_| |_| \__ \ | || (_| | (__| |_| |_| | | | | | |
|
|
||||||
\_/ \___|\__|_|\__|\__,_|___/ |_| \__,_|\___|\__|\__,_|_| |_| |_|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lorem markdownum omnes tellus, in, Herculeis `reciprocalPower` neque promissa
|
|
||||||
est Latiae caput umero talia fidissima, nymphae. Foedera et sagittas tenetur
|
|
||||||
adde matri esse: talia manibus.
|
|
||||||
|
|
||||||
______ _ _ _ _
|
|
||||||
| ____| (_) | (_) | |
|
|
||||||
| |__ _ _ _| |_ __ ___ __| | ___ ___
|
|
||||||
| __| | | | | __| \ \ / / |/ _` |/ _ \/ _ \
|
|
||||||
| | | |_| | | |_ \ V /| | (_| | __/ (_) |
|
|
||||||
|_| \__,_|_|\__| \_/ |_|\__,_|\___|\___/
|
|
||||||
|
|
||||||
|
|
||||||
_ _ _ _ _
|
|
||||||
| | (_) | | | (_)
|
|
||||||
| | ___ _ __ __ _ _ __ _ _ _ ___ __| | ___ __| |_ ___ ___ ___
|
|
||||||
| |/ _ \| '_ \ / _` | |/ _` | | | |/ _ \ / _` |/ _ \/ _` | / __/ __|/ _ \
|
|
||||||
| | (_) | | | | (_| | | (_| | |_| | __/ | (_| | __/ (_| | \__ \__ \ __/
|
|
||||||
|_|\___/|_| |_|\__, |_|\__, |\__,_|\___| \__,_|\___|\__,_|_|___/___/\___|
|
|
||||||
__/ | | |
|
|
||||||
|___/ |_|
|
|
||||||
|
|
||||||
|
|
||||||
Ille stupet, ultra I N C U N A B U L A A G E N D U M U R B E S mirer A P E
|
|
||||||
R, metu. Aegides novissima sunt turis quatiebant umbrosaque [putares](1).
|
|
||||||
Metuunt eruiturque maximus velit, ater Vesta vulnus rustica verba! Mei vincite
|
|
||||||
nulla, in Iovem: fama exurunt vernum!
|
|
||||||
|
|
||||||
De instat ubi induit fugientia inertes columbas tarda t e r g a P a r n a s i
|
|
||||||
a s e n t e n t i a omnia inmotusque, est et aetas sonus. Exhibuit coeperunt,
|
|
||||||
canis. Suis minora, tempore fateor neve moderamine erit. Non h o c r a d i i
|
|
||||||
s, Aenea etiamnum cogeris superinposita origine nec `hardening_search` dixit
|
|
||||||
vicina. Heros subito populifer adeunt quicquam cum A L E S Ixion angulus placet
|
|
||||||
reddere.
|
|
||||||
|
|
||||||
_______ _
|
|
||||||
|__ __| | |
|
|
||||||
| |_ __ __ _ ___| |_ __ _ __ _ _ _ ___
|
|
||||||
| | '__/ _` |/ __| __/ _` |/ _` | | | |/ _ \
|
|
||||||
| | | | (_| | (__| || (_| | (_| | |_| | __/
|
|
||||||
|_|_| \__,_|\___|\__\__,_|\__, |\__,_|\___|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
___ ___ _ __ _ __ ___ _ __ ___ __ _ _ ____ ____ _
|
|
||||||
/ __|/ _ \ '__| '_ \ / _ \ '_ \/ __| / _` | '__\ \ / / _` |
|
|
||||||
\__ \ __/ | | |_) | __/ | | \__ \ | (_| | | \ V / (_| |
|
|
||||||
|___/\___|_| | .__/ \___|_| |_|___/ \__,_|_| \_/ \__,_|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
__ _ _ _ ___ __ _ _ _ ___
|
|
||||||
/ _` | | | |/ _ \ / _` | | | |/ _ \
|
|
||||||
| (_| | |_| | (_) | (_| | |_| | __/
|
|
||||||
\__, |\__,_|\___/ \__, |\__,_|\___|
|
|
||||||
| | | |
|
|
||||||
|_| |_|
|
|
||||||
|
|
||||||
|
|
||||||
Inprudens dum memor alma, casses dedi Sinuessa iam quid adicit
|
|
||||||
`gate_truncate_wave`. [Geratur de](2), insolida temptat `open` carentia;
|
|
||||||
genitorem solet, potens. Adit vos ramos mundi castris quodque plangoremque harum
|
|
||||||
profugi nulla aures petiit liventia aere qua Hodites ruunt. Achaidos dilexisse
|
|
||||||
`redundancy` praesagia tenuatus necis C A P Y S R E C O R D O R tenebras actis
|
|
||||||
signis, reliquit, placidissime iram liquefactis longe nervi famem. Cura pars
|
|
||||||
natique velleris semel et at `printerWildcardParallel` inquit positosque comites
|
|
||||||
ultra.
|
|
||||||
|
|
||||||
____ _
|
|
||||||
/ __ \ | |
|
|
||||||
| | | |_ _ __ _ _ __ | |_ __ _ __ _ _ _ ___
|
|
||||||
| | | | | | |/ _` | '_ \| __/ _` |/ _` | | | |/ _ \
|
|
||||||
| |__| | |_| | (_| | | | | || (_| | (_| | |_| | __/
|
|
||||||
\___\_\\__,_|\__,_|_| |_|\__\__,_|\__, |\__,_|\___|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
_
|
|
||||||
| |
|
|
||||||
__ _ _ _ __ _ ___ _ __ | |_
|
|
||||||
/ _` | | | |/ _` |/ _ \ '_ \| __|
|
|
||||||
| (_| | |_| | (_| | __/ | | | |_
|
|
||||||
\__,_|\__,_|\__, |\___|_| |_|\__|
|
|
||||||
__/ |
|
|
||||||
|___/
|
|
||||||
|
|
||||||
|
|
||||||
Acuti ignotis ignis, ostendit nostras terras nondum, [petit utque utero](3)
|
|
||||||
sepulcrales, pluma reseratis Ismario. Tria silentia duae n y m p h e: certe
|
|
||||||
parva cernam et dixit. [Verborum sedisti](1).
|
|
||||||
|
|
||||||
01 | if (firewall(4, 3) == 1 * device_media_parity) {
|
|
||||||
02 | system_copy_class += systray + oasis_wi_clean + lpi;
|
|
||||||
03 | modelLdap.search_secondary_socket *= bounce.proxy_text(cableMyspacePerl,
|
|
||||||
04 | scanner(spam));
|
|
||||||
05 | text_class_traceroute(vga, hdtv);
|
|
||||||
06 | }
|
|
||||||
07 | if (commerceStack) {
|
|
||||||
08 | wrapSafe(pum(itunesRosettaCache, sdramOpticalWorm));
|
|
||||||
09 | } else {
|
|
||||||
10 | minisite_worm(2, ssdString(84), gnuFriendly);
|
|
||||||
11 | state.qwerty(moduleJavascriptFloating + ugc, koffice,
|
|
||||||
12 | mail_system.prebinding_service_teraflops(mediaIcmpBoot));
|
|
||||||
13 | }
|
|
||||||
14 | memoryHexadecimalMultitasking += oop_android;
|
|
||||||
15 | var passive_vdu = bar;
|
|
||||||
16 | var ddrJava = leaf(unc_web);
|
|
||||||
|
|
||||||
|
|
||||||
__ __ _ _ _
|
|
||||||
| \/ (_) | |___ _ __ __ _ _ ___ ____ _
|
|
||||||
| |\/| | | | / -_) | '_ \/ _` | '_\ V / _` |
|
|
||||||
|_| |_|_|_|_\___| | .__/\__,_|_| \_/\__,_|
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
M I N E T U R E T aurum! Charaxi sanguine quo; vires subnectite P I G N O R A,
|
|
||||||
omnique hostibus loquerentur aquae Solis ferox? C U M N E, putat utar ex ad
|
|
||||||
aede mane. Vitalesque geminas exstimulat, iam vivunt dat artes et quem! Iam
|
|
||||||
violas molire ipse, spoliabitur dente nervis regis, factos quae.
|
|
||||||
|
|
||||||
| Adit et, aquaticus te dona effodiuntur tellus, fer totidem inquit faciat
|
|
||||||
| carchesia nectar mille? Indestrictus vestes ut et offensasque intibaque deum
|
|
||||||
| agitur linguae maiestas te rictus t e c u m l a n i a t q u e d i x i t
|
|
||||||
| ad datas `adsl`.
|
|
||||||
| Vino u t quod iaculi mutua armis uno est vos totidemque
|
|
||||||
| [exhortor](4). Iamdudum Romanae cepi linguisque
|
|
||||||
| velit accipe idem acutis hoc sparsumque dextra. Minoide sponte collo sequitur
|
|
||||||
| et intus flere sanguineaque herba, nox Quas adsiduo c u n c t a n t e m tibi;
|
|
||||||
| enim.
|
|
||||||
|
|
||||||
[1] #mille-parva
|
|
||||||
[2] #salientia-iungit-contra-et
|
|
||||||
[3] #structa-hinc-vetitus-factum
|
|
||||||
[4] #tractaque-serpens-arva-quoque
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
|
|
||||||
.d8888b. 888d8b 888 d8b
|
|
||||||
d88P Y88b 888Y8P 888 Y8P
|
|
||||||
Y88b. 888 888
|
|
||||||
"Y888b. 8888b. 888888 .d88b. 88888b. 888888888 8888b.
|
|
||||||
"Y88b. "88b888888d8P Y8b888 "88b888 888 "88b
|
|
||||||
"888.d88888888888888888888888 888888 888.d888888
|
|
||||||
Y88b d88P888 888888888Y8b. 888 888Y88b. 888888 888
|
|
||||||
"Y8888P" "Y888888888888 "Y8888 888 888 "Y888888"Y888888
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
STRUCTA HINC VETITUS FACTUM
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Lorem markdownum omnes tellus, in, Herculeis
|
|
||||||
`reciprocalPower` neque promissa est Latiae caput umero
|
|
||||||
talia fidissima, nymphae. Foedera et sagittas tenetur adde
|
|
||||||
matri esse: talia manibus.
|
|
||||||
|
|
||||||
FUIT VIDEO LONGIQUE DEDISSE
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Ille stupet, ultra I N C U N A B U L A A G E N D U M U
|
|
||||||
R B E S mirer A P E R , metu. Aegides novissima sunt
|
|
||||||
turis quatiebant umbrosaque [putares](1). Metuunt eruiturque
|
|
||||||
maximus velit, ater Vesta vulnus rustica verba! Mei vincite
|
|
||||||
nulla, in Iovem: fama exurunt vernum!
|
|
||||||
|
|
||||||
De instat ubi induit fugientia inertes columbas tarda t e
|
|
||||||
r g a P a r n a s i a s e n t e n t i a omnia
|
|
||||||
inmotusque, est et aetas sonus. Exhibuit coeperunt, canis.
|
|
||||||
Suis minora, tempore fateor neve moderamine erit. Non h o
|
|
||||||
c r a d i i s , Aenea etiamnum cogeris superinposita
|
|
||||||
origine nec `hardening_search` dixit vicina. Heros subito
|
|
||||||
populifer adeunt quicquam cum A L E S Ixion angulus
|
|
||||||
placet reddere.
|
|
||||||
|
|
||||||
_______ _
|
|
||||||
|__ __| | |
|
|
||||||
| |_ __ __ _ ___| |_ __ _ __ _ _ _ ___
|
|
||||||
| | '__/ _` |/ __| __/ _` |/ _` | | | |/ _ \
|
|
||||||
| | | | (_| | (__| || (_| | (_| | |_| | __/
|
|
||||||
|_|_| \__,_|\___|\__\__,_|\__, |\__,_|\___|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
___ ___ _ __ _ __ ___ _ __ ___ __ _ _ ____ ____ _
|
|
||||||
/ __|/ _ \ '__| '_ \ / _ \ '_ \/ __| / _` | '__\ \ / / _` |
|
|
||||||
\__ \ __/ | | |_) | __/ | | \__ \ | (_| | | \ V / (_| |
|
|
||||||
|___/\___|_| | .__/ \___|_| |_|___/ \__,_|_| \_/ \__,_|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
__ _ _ _ ___ __ _ _ _ ___
|
|
||||||
/ _` | | | |/ _ \ / _` | | | |/ _ \
|
|
||||||
| (_| | |_| | (_) | (_| | |_| | __/
|
|
||||||
\__, |\__,_|\___/ \__, |\__,_|\___|
|
|
||||||
| | | |
|
|
||||||
|_| |_|
|
|
||||||
|
|
||||||
|
|
||||||
Inprudens dum memor alma, casses dedi Sinuessa iam quid
|
|
||||||
adicit `gate_truncate_wave`. [Geratur de](2), insolida
|
|
||||||
temptat `open` carentia; genitorem solet, potens. Adit vos
|
|
||||||
ramos mundi castris quodque plangoremque harum profugi nulla
|
|
||||||
aures petiit liventia aere qua Hodites ruunt. Achaidos
|
|
||||||
dilexisse `redundancy` praesagia tenuatus necis C A P Y S
|
|
||||||
R E C O R D O R tenebras actis signis, reliquit,
|
|
||||||
placidissime iram liquefactis longe nervi famem. Cura pars
|
|
||||||
natique velleris semel et at `printerWildcardParallel`
|
|
||||||
inquit positosque comites ultra.
|
|
||||||
|
|
||||||
____ _
|
|
||||||
/ __ \ | |
|
|
||||||
| | | |_ _ __ _ _ __ | |_ __ _ __ _ _ _ ___
|
|
||||||
| | | | | | |/ _` | '_ \| __/ _` |/ _` | | | |/ _ \
|
|
||||||
| |__| | |_| | (_| | | | | || (_| | (_| | |_| | __/
|
|
||||||
\___\_\\__,_|\__,_|_| |_|\__\__,_|\__, |\__,_|\___|
|
|
||||||
| |
|
|
||||||
|_|
|
|
||||||
_
|
|
||||||
| |
|
|
||||||
__ _ _ _ __ _ ___ _ __ | |_
|
|
||||||
/ _` | | | |/ _` |/ _ \ '_ \| __|
|
|
||||||
| (_| | |_| | (_| | __/ | | | |_
|
|
||||||
\__,_|\__,_|\__, |\___|_| |_|\__|
|
|
||||||
__/ |
|
|
||||||
|___/
|
|
||||||
|
|
||||||
|
|
||||||
Acuti ignotis ignis, ostendit nostras terras nondum, [petit
|
|
||||||
utque utero](3) sepulcrales, pluma reseratis Ismario. Tria
|
|
||||||
silentia duae n y m p h e : certe parva cernam et dixit.
|
|
||||||
[Verborum sedisti](1).
|
|
||||||
|
|
||||||
01 | if (firewall(4, 3) == 1 * device_media_parity) {
|
|
||||||
02 | system_copy_class += systray + oasis_wi_clean + lpi;
|
|
||||||
03 | modelLdap.search_secondary_socket *= bounce.proxy_text(cableMyspacePerl,
|
|
||||||
04 | scanner(spam));
|
|
||||||
05 | text_class_traceroute(vga, hdtv);
|
|
||||||
06 | }
|
|
||||||
07 | if (commerceStack) {
|
|
||||||
08 | wrapSafe(pum(itunesRosettaCache, sdramOpticalWorm));
|
|
||||||
09 | } else {
|
|
||||||
10 | minisite_worm(2, ssdString(84), gnuFriendly);
|
|
||||||
11 | state.qwerty(moduleJavascriptFloating + ugc, koffice,
|
|
||||||
12 | mail_system.prebinding_service_teraflops(mediaIcmpBoot));
|
|
||||||
13 | }
|
|
||||||
14 | memoryHexadecimalMultitasking += oop_android;
|
|
||||||
15 | var passive_vdu = bar;
|
|
||||||
16 | var ddrJava = leaf(unc_web);
|
|
||||||
|
|
||||||
|
|
||||||
__ __ _ _ _
|
|
||||||
| \/ (_) | |___ _ __ __ _ _ ___ ____ _
|
|
||||||
| |\/| | | | / -_) | '_ \/ _` | '_\ V / _` |
|
|
||||||
|_| |_|_|_|_\___| | .__/\__,_|_| \_/\__,_|
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
M I N E T U R E T aurum! Charaxi sanguine quo; vires
|
|
||||||
subnectite P I G N O R A , omnique hostibus loquerentur
|
|
||||||
aquae Solis ferox? C U M N E , putat utar ex ad aede
|
|
||||||
mane. Vitalesque geminas exstimulat, iam vivunt dat artes et
|
|
||||||
quem! Iam violas molire ipse, spoliabitur dente nervis
|
|
||||||
regis, factos quae.
|
|
||||||
|
|
||||||
| Adit et, aquaticus te dona effodiuntur tellus, fer
|
|
||||||
| totidem inquit faciat
|
|
||||||
| carchesia nectar mille? Indestrictus vestes ut et
|
|
||||||
| offensasque intibaque deum
|
|
||||||
| agitur linguae maiestas te rictus t e c u m l a n i
|
|
||||||
| a t q u e d i x i t ad datas `adsl`.
|
|
||||||
| Vino u t quod iaculi mutua armis uno est vos
|
|
||||||
| totidemque
|
|
||||||
| [exhortor](4). Iamdudum Romanae cepi linguisque
|
|
||||||
| velit accipe idem acutis hoc sparsumque dextra. Minoide
|
|
||||||
| sponte collo sequitur
|
|
||||||
| et intus flere sanguineaque herba, nox Quas adsiduo c
|
|
||||||
| u n c t a n t e m tibi; enim.
|
|
||||||
|
|
||||||
[1] #mille-parva
|
|
||||||
[2] #salientia-iungit-contra-et
|
|
||||||
[3] #structa-hinc-vetitus-factum
|
|
||||||
[4] #tractaque-serpens-arva-quoque
|
|
||||||
6
main.py
6
main.py
|
|
@ -1,6 +0,0 @@
|
||||||
def main():
|
|
||||||
print("Hello from md2amb!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
293
md2txt.py
293
md2txt.py
|
|
@ -6,12 +6,38 @@ from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
|
import string
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Dict, Iterable, List, Optional, Tuple
|
from typing import Callable, Dict, Iterable, List, Optional, Tuple
|
||||||
|
|
||||||
|
try: # pragma: no cover - optional dependency
|
||||||
|
from hyphen import Hyphenator as _Hyphenator # type: ignore
|
||||||
|
except ImportError: # pragma: no cover - handled at runtime
|
||||||
|
_Hyphenator = None # type: ignore[misc]
|
||||||
|
|
||||||
|
if _Hyphenator is not None: # pragma: no branch
|
||||||
|
Hyphenator = _Hyphenator # type: ignore[assignment]
|
||||||
|
else: # pragma: no cover - fallback path
|
||||||
|
try:
|
||||||
|
import pyphen
|
||||||
|
except ImportError: # pragma: no cover - handled at runtime
|
||||||
|
Hyphenator = None # type: ignore[misc, assignment]
|
||||||
|
else:
|
||||||
|
class _PyphenWrapper:
|
||||||
|
def __init__(self, lang: str) -> None:
|
||||||
|
self._dic = pyphen.Pyphen(lang=lang)
|
||||||
|
|
||||||
|
def hyphenate_word(self, word: str): # type: ignore[override]
|
||||||
|
inserted = self._dic.inserted(word)
|
||||||
|
if not inserted:
|
||||||
|
return []
|
||||||
|
return inserted.split("-")
|
||||||
|
|
||||||
|
Hyphenator = _PyphenWrapper # type: ignore[assignment]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pyfiglet import Figlet, FontNotFound
|
from pyfiglet import Figlet, FontNotFound
|
||||||
except ImportError: # pragma: no cover - emit a helpful error at runtime instead
|
except ImportError: # pragma: no cover - emit a helpful error at runtime instead
|
||||||
|
|
@ -32,6 +58,29 @@ MMD_ATTR_LINE_RE = re.compile(r"^\{\s*:(.+)\}\s*$")
|
||||||
MMD_ATTR_TAIL_RE = re.compile(r"(.*?)\s*\{\s*:(.+?)\}\s*$")
|
MMD_ATTR_TAIL_RE = re.compile(r"(.*?)\s*\{\s*:(.+?)\}\s*$")
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_int(value: Optional[str], default: int = 0) -> int:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
match = re.search(r"-?\d+", value)
|
||||||
|
if not match:
|
||||||
|
return default
|
||||||
|
try:
|
||||||
|
return int(match.group())
|
||||||
|
except ValueError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_bool(value: Optional[str], default: bool = False) -> bool:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
lowered = value.strip().lower()
|
||||||
|
if lowered in {"true", "yes", "1", "on"}:
|
||||||
|
return True
|
||||||
|
if lowered in {"false", "no", "0", "off"}:
|
||||||
|
return False
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BlockStyle:
|
class BlockStyle:
|
||||||
align: str = "left"
|
align: str = "left"
|
||||||
|
|
@ -59,6 +108,11 @@ class FrontMatter:
|
||||||
h1_font: str = "standard"
|
h1_font: str = "standard"
|
||||||
h2_font: str = "standard"
|
h2_font: str = "standard"
|
||||||
h3_font: str = "standard"
|
h3_font: str = "standard"
|
||||||
|
margin_left: int = 0
|
||||||
|
margin_right: int = 0
|
||||||
|
paragraph_spacing: int = 0
|
||||||
|
hyphenate: bool = False
|
||||||
|
hyphen_lang: str = "en_US"
|
||||||
|
|
||||||
|
|
||||||
class MarkdownToTxtConverter:
|
class MarkdownToTxtConverter:
|
||||||
|
|
@ -68,7 +122,22 @@ class MarkdownToTxtConverter:
|
||||||
self.links: List[Tuple[int, str]] = []
|
self.links: List[Tuple[int, str]] = []
|
||||||
self.link_indices: Dict[str, int] = {}
|
self.link_indices: Dict[str, int] = {}
|
||||||
self.figlets: Dict[str, Figlet] = {}
|
self.figlets: Dict[str, Figlet] = {}
|
||||||
self._style_stack: List[BlockStyle] = [BlockStyle()]
|
self._base_margin_left = max(0, self.frontmatter.margin_left)
|
||||||
|
self._base_margin_right = max(0, self.frontmatter.margin_right)
|
||||||
|
self.paragraph_spacing = max(0, self.frontmatter.paragraph_spacing)
|
||||||
|
self.hyphenate = self.frontmatter.hyphenate
|
||||||
|
self.hyphen_lang = self.frontmatter.hyphen_lang or "en_US"
|
||||||
|
self.hyphenator: Optional[Hyphenator]
|
||||||
|
if self.hyphenate:
|
||||||
|
if Hyphenator is None:
|
||||||
|
raise RuntimeError("PyHyphen is required for hyphenation but is not installed.")
|
||||||
|
try:
|
||||||
|
self.hyphenator = Hyphenator(self.hyphen_lang)
|
||||||
|
except Exception as exc: # pragma: no cover - defensive
|
||||||
|
raise RuntimeError(f"Failed to initialise hyphenator for language '{self.hyphen_lang}': {exc}") from exc
|
||||||
|
else:
|
||||||
|
self.hyphenator = None
|
||||||
|
self._style_stack: List[BlockStyle] = [self._make_base_style()]
|
||||||
self._paragraph_style_spec: Optional[StyleSpec] = None
|
self._paragraph_style_spec: Optional[StyleSpec] = None
|
||||||
self._pending_block_style_spec: Optional[StyleSpec] = None
|
self._pending_block_style_spec: Optional[StyleSpec] = None
|
||||||
self._last_stylable_block: Optional[BlockRecord] = None
|
self._last_stylable_block: Optional[BlockRecord] = None
|
||||||
|
|
@ -77,7 +146,7 @@ class MarkdownToTxtConverter:
|
||||||
output: List[str] = []
|
output: List[str] = []
|
||||||
self.links = []
|
self.links = []
|
||||||
self.link_indices = {}
|
self.link_indices = {}
|
||||||
self._style_stack = [BlockStyle()]
|
self._style_stack = [self._make_base_style()]
|
||||||
self._paragraph_style_spec = None
|
self._paragraph_style_spec = None
|
||||||
self._pending_block_style_spec = None
|
self._pending_block_style_spec = None
|
||||||
self._last_stylable_block = None
|
self._last_stylable_block = None
|
||||||
|
|
@ -199,7 +268,8 @@ class MarkdownToTxtConverter:
|
||||||
|
|
||||||
if not line.strip():
|
if not line.strip():
|
||||||
self._flush_paragraph(current_paragraph, output)
|
self._flush_paragraph(current_paragraph, output)
|
||||||
output.append("")
|
if self.paragraph_spacing == 0:
|
||||||
|
output.append("")
|
||||||
current_paragraph = []
|
current_paragraph = []
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -216,7 +286,14 @@ class MarkdownToTxtConverter:
|
||||||
output.append("")
|
output.append("")
|
||||||
for index, url in self.links:
|
for index, url in self.links:
|
||||||
entry = f"[{index}] {url}"
|
entry = f"[{index}] {url}"
|
||||||
output.extend(self._wrap_text(entry, initial_indent="", subsequent_indent="", style=BlockStyle()))
|
output.extend(
|
||||||
|
self._wrap_text(
|
||||||
|
entry,
|
||||||
|
initial_indent="",
|
||||||
|
subsequent_indent="",
|
||||||
|
style=self._make_base_style(),
|
||||||
|
)
|
||||||
|
)
|
||||||
self._last_stylable_block = None
|
self._last_stylable_block = None
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
@ -230,10 +307,12 @@ class MarkdownToTxtConverter:
|
||||||
style = self._combine_styles(self._current_style(), combined_spec)
|
style = self._combine_styles(self._current_style(), combined_spec)
|
||||||
|
|
||||||
def render(target_style: BlockStyle) -> List[str]:
|
def render(target_style: BlockStyle) -> List[str]:
|
||||||
return self._wrap_text(processed, style=target_style)
|
return self._wrap_text(processed, style=target_style, hyphenate=self.hyphenate)
|
||||||
|
|
||||||
lines = render(style)
|
lines = render(style)
|
||||||
self._emit_block(output, lines, stylable=True, render_fn=render, style=style)
|
self._emit_block(output, lines, stylable=True, render_fn=render, style=style)
|
||||||
|
if self.paragraph_spacing > 0:
|
||||||
|
output.extend([ "" for _ in range(self.paragraph_spacing) ])
|
||||||
paragraph_lines.clear()
|
paragraph_lines.clear()
|
||||||
self._paragraph_style_spec = None
|
self._paragraph_style_spec = None
|
||||||
self._pending_block_style_spec = None
|
self._pending_block_style_spec = None
|
||||||
|
|
@ -264,6 +343,7 @@ class MarkdownToTxtConverter:
|
||||||
initial_indent=indent,
|
initial_indent=indent,
|
||||||
subsequent_indent=indent,
|
subsequent_indent=indent,
|
||||||
style=self._current_style(),
|
style=self._current_style(),
|
||||||
|
hyphenate=self.hyphenate,
|
||||||
)
|
)
|
||||||
self._emit_block(output, wrapped)
|
self._emit_block(output, wrapped)
|
||||||
|
|
||||||
|
|
@ -327,6 +407,7 @@ class MarkdownToTxtConverter:
|
||||||
initial_indent=prefix,
|
initial_indent=prefix,
|
||||||
subsequent_indent=" " * len(prefix),
|
subsequent_indent=" " * len(prefix),
|
||||||
style=self._current_style(),
|
style=self._current_style(),
|
||||||
|
hyphenate=self.hyphenate,
|
||||||
)
|
)
|
||||||
self._emit_block(output, wrapped)
|
self._emit_block(output, wrapped)
|
||||||
|
|
||||||
|
|
@ -359,6 +440,13 @@ class MarkdownToTxtConverter:
|
||||||
self._last_stylable_block.length = len(new_lines)
|
self._last_stylable_block.length = len(new_lines)
|
||||||
self._last_stylable_block.style = new_style
|
self._last_stylable_block.style = new_style
|
||||||
|
|
||||||
|
def _make_base_style(self) -> BlockStyle:
|
||||||
|
return BlockStyle(
|
||||||
|
align="left",
|
||||||
|
margin_left=self._base_margin_left,
|
||||||
|
margin_right=self._base_margin_right,
|
||||||
|
)
|
||||||
|
|
||||||
def _current_style(self) -> BlockStyle:
|
def _current_style(self) -> BlockStyle:
|
||||||
return self._style_stack[-1]
|
return self._style_stack[-1]
|
||||||
|
|
||||||
|
|
@ -588,6 +676,10 @@ class MarkdownToTxtConverter:
|
||||||
return clean_text, spec
|
return clean_text, spec
|
||||||
|
|
||||||
def _render_heading_lines(self, level: int, text: str, style: BlockStyle) -> List[str]:
|
def _render_heading_lines(self, level: int, text: str, style: BlockStyle) -> List[str]:
|
||||||
|
font_name = getattr(self.frontmatter, f"h{level}_font", "standard")
|
||||||
|
style_key = font_name.lower()
|
||||||
|
if style_key in {"caps", "title"}:
|
||||||
|
return self._render_h4_plus(text, style, transform=style_key)
|
||||||
if level <= 3:
|
if level <= 3:
|
||||||
figlet_lines = self._figlet_render(level, text)
|
figlet_lines = self._figlet_render(level, text)
|
||||||
if figlet_lines:
|
if figlet_lines:
|
||||||
|
|
@ -608,22 +700,23 @@ class MarkdownToTxtConverter:
|
||||||
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
||||||
margin_right = max(style.margin_right, 0)
|
margin_right = max(style.margin_right, 0)
|
||||||
available_width = max(1, self.width - margin_left - margin_right)
|
available_width = max(1, self.width - margin_left - margin_right)
|
||||||
|
block_width = max((len(line.rstrip()) for line in lines), default=0)
|
||||||
|
block_width = min(block_width, available_width)
|
||||||
|
extra_space = max(0, available_width - block_width)
|
||||||
|
if style.align == "center":
|
||||||
|
align_offset = extra_space // 2
|
||||||
|
elif style.align == "right":
|
||||||
|
align_offset = extra_space
|
||||||
|
else:
|
||||||
|
align_offset = 0
|
||||||
|
max_indent = max(0, self.width - block_width)
|
||||||
|
indent = min(margin_left + align_offset, max_indent)
|
||||||
result: List[str] = []
|
result: List[str] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if not line:
|
if not line:
|
||||||
result.append("")
|
result.append("")
|
||||||
continue
|
continue
|
||||||
trimmed = line.rstrip()
|
trimmed = line.rstrip()
|
||||||
line_len = len(trimmed)
|
|
||||||
extra_space = max(0, available_width - line_len)
|
|
||||||
if style.align == "center":
|
|
||||||
extra_left = extra_space // 2
|
|
||||||
elif style.align == "right":
|
|
||||||
extra_left = extra_space
|
|
||||||
else:
|
|
||||||
extra_left = 0
|
|
||||||
max_indent = max(0, self.width - line_len)
|
|
||||||
indent = min(margin_left + extra_left, max_indent)
|
|
||||||
result.append(" " * indent + trimmed)
|
result.append(" " * indent + trimmed)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -834,9 +927,14 @@ class MarkdownToTxtConverter:
|
||||||
return None
|
return None
|
||||||
return [line.rstrip() for line in trimmed_lines]
|
return [line.rstrip() for line in trimmed_lines]
|
||||||
|
|
||||||
def _render_h4_plus(self, text: str, style: BlockStyle) -> List[str]:
|
def _render_h4_plus(self, text: str, style: BlockStyle, transform: str = "caps") -> List[str]:
|
||||||
uppercase = text.upper()
|
if transform == "title":
|
||||||
wrapped = self._wrap_text(uppercase, style=style)
|
processed = self._to_title_case(text)
|
||||||
|
elif transform == "caps":
|
||||||
|
processed = text.upper()
|
||||||
|
else:
|
||||||
|
processed = text
|
||||||
|
wrapped = self._wrap_text(processed, style=style)
|
||||||
output: List[str] = []
|
output: List[str] = []
|
||||||
for line in wrapped:
|
for line in wrapped:
|
||||||
line = line.rstrip()
|
line = line.rstrip()
|
||||||
|
|
@ -850,18 +948,32 @@ class MarkdownToTxtConverter:
|
||||||
output.append("")
|
output.append("")
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def _to_title_case(self, value: str) -> str:
|
||||||
|
return string.capwords(value)
|
||||||
|
|
||||||
def _wrap_text(
|
def _wrap_text(
|
||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
initial_indent: str = "",
|
initial_indent: str = "",
|
||||||
subsequent_indent: Optional[str] = None,
|
subsequent_indent: Optional[str] = None,
|
||||||
style: Optional[BlockStyle] = None,
|
style: Optional[BlockStyle] = None,
|
||||||
|
hyphenate: bool = False,
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
style = style or BlockStyle()
|
style = style or BlockStyle()
|
||||||
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
||||||
available_width = max(1, self._effective_width(style))
|
available_width = max(1, self._effective_width(style))
|
||||||
|
|
||||||
subsequent = initial_indent if subsequent_indent is None else subsequent_indent
|
subsequent = initial_indent if subsequent_indent is None else subsequent_indent
|
||||||
|
|
||||||
|
if hyphenate and self.hyphenator is not None:
|
||||||
|
return self._wrap_text_hyphenated(
|
||||||
|
text,
|
||||||
|
initial_indent,
|
||||||
|
subsequent,
|
||||||
|
style,
|
||||||
|
available_width,
|
||||||
|
)
|
||||||
|
|
||||||
wrapper = textwrap.TextWrapper(
|
wrapper = textwrap.TextWrapper(
|
||||||
width=available_width,
|
width=available_width,
|
||||||
expand_tabs=False,
|
expand_tabs=False,
|
||||||
|
|
@ -891,6 +1003,135 @@ class MarkdownToTxtConverter:
|
||||||
result.append(" " * indent + line)
|
result.append(" " * indent + line)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _wrap_text_hyphenated(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
initial_indent: str,
|
||||||
|
subsequent_indent: str,
|
||||||
|
style: BlockStyle,
|
||||||
|
available_width: int,
|
||||||
|
) -> List[str]:
|
||||||
|
tokens = re.split(r"(\s+)", text)
|
||||||
|
lines: List[str] = []
|
||||||
|
current_indent = initial_indent
|
||||||
|
current_line = initial_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
width = available_width
|
||||||
|
for index, token in enumerate(tokens):
|
||||||
|
if token == "":
|
||||||
|
continue
|
||||||
|
if token.isspace():
|
||||||
|
if current_len + len(token) > width and current_len > len(current_indent):
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
current_indent = subsequent_indent
|
||||||
|
current_line = subsequent_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
else:
|
||||||
|
current_line += token
|
||||||
|
current_len += len(token)
|
||||||
|
continue
|
||||||
|
segments = self._hyphenate_token(token) or [token]
|
||||||
|
while segments:
|
||||||
|
remaining = width - current_len
|
||||||
|
if remaining <= 1:
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
current_indent = subsequent_indent
|
||||||
|
current_line = subsequent_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
joined_length = sum(len(part) for part in segments)
|
||||||
|
if current_len + joined_length <= width:
|
||||||
|
current_line += "".join(segments)
|
||||||
|
current_len += joined_length
|
||||||
|
segments = []
|
||||||
|
break
|
||||||
|
|
||||||
|
split_index = None
|
||||||
|
running = 0
|
||||||
|
for idx in range(1, len(segments)):
|
||||||
|
running += len(segments[idx - 1])
|
||||||
|
needed = running + 1 # hyphen
|
||||||
|
if current_len + needed <= width:
|
||||||
|
split_index = idx
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if split_index is None:
|
||||||
|
# fallback: break the first segment
|
||||||
|
fragment = segments[0]
|
||||||
|
force_split = min(len(fragment), remaining - 1)
|
||||||
|
if force_split <= 0:
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
current_indent = subsequent_indent
|
||||||
|
current_line = subsequent_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
continue
|
||||||
|
head = fragment[:force_split] + "-"
|
||||||
|
tail = fragment[force_split:]
|
||||||
|
current_line += head
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
current_indent = subsequent_indent
|
||||||
|
current_line = subsequent_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
segments[0] = tail
|
||||||
|
if not tail:
|
||||||
|
segments.pop(0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
consumed_segments = segments[:split_index]
|
||||||
|
current_line += "".join(consumed_segments) + "-"
|
||||||
|
current_len += sum(len(part) for part in consumed_segments) + 1
|
||||||
|
segments = segments[split_index:]
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
current_indent = subsequent_indent
|
||||||
|
current_line = subsequent_indent
|
||||||
|
current_len = len(current_line)
|
||||||
|
if current_line.strip():
|
||||||
|
lines.append(current_line.rstrip())
|
||||||
|
if not lines:
|
||||||
|
lines.append(initial_indent.rstrip())
|
||||||
|
|
||||||
|
result: List[str] = []
|
||||||
|
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
||||||
|
width = available_width
|
||||||
|
for line in lines:
|
||||||
|
stripped = line.rstrip()
|
||||||
|
line_len = len(stripped)
|
||||||
|
extra_space = max(0, width - line_len)
|
||||||
|
if style.align == "center":
|
||||||
|
extra_left = extra_space // 2
|
||||||
|
elif style.align == "right":
|
||||||
|
extra_left = extra_space
|
||||||
|
else:
|
||||||
|
extra_left = 0
|
||||||
|
max_indent = max(0, self.width - line_len)
|
||||||
|
indent = min(margin_left + extra_left, max_indent)
|
||||||
|
result.append(" " * indent + stripped)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _hyphenate_token(self, token: str) -> Optional[List[str]]:
|
||||||
|
if self.hyphenator is None:
|
||||||
|
return None
|
||||||
|
match = re.match(r"^([^A-Za-zÀ-ÖØ-öø-ÿ'’]*)([A-Za-zÀ-ÖØ-öø-ÿ'’]+)([^A-Za-zÀ-ÖØ-öø-ÿ'’]*)$", token)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
leading, word, trailing = match.groups()
|
||||||
|
if len(word) <= 4:
|
||||||
|
return None
|
||||||
|
parts = self.hyphenator.hyphenate_word(word)
|
||||||
|
if not parts:
|
||||||
|
return None
|
||||||
|
if isinstance(parts, str):
|
||||||
|
segments = [segment for segment in parts.split("-") if segment]
|
||||||
|
else:
|
||||||
|
segments = [segment for segment in parts if segment]
|
||||||
|
if len(segments) < 2:
|
||||||
|
return None
|
||||||
|
segments[0] = leading + segments[0]
|
||||||
|
segments[-1] = segments[-1] + trailing
|
||||||
|
return segments
|
||||||
|
|
||||||
def _effective_width(self, style: BlockStyle) -> int:
|
def _effective_width(self, style: BlockStyle) -> int:
|
||||||
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
margin_left = min(max(style.margin_left, 0), self.width - 1)
|
||||||
remaining = self.width - margin_left
|
remaining = self.width - margin_left
|
||||||
|
|
@ -913,10 +1154,20 @@ def parse_frontmatter(lines: List[str]) -> Tuple[FrontMatter, List[str]]:
|
||||||
if idx >= len(lines):
|
if idx >= len(lines):
|
||||||
return FrontMatter(), lines
|
return FrontMatter(), lines
|
||||||
remaining = lines[idx + 1 :] if idx + 1 < len(lines) else []
|
remaining = lines[idx + 1 :] if idx + 1 < len(lines) else []
|
||||||
|
paragraph_spacing_value = frontmatter.get("paragraph_spacing")
|
||||||
|
if paragraph_spacing_value is None:
|
||||||
|
paragraph_spacing_value = frontmatter.get("lines_between_paragraphs")
|
||||||
|
if paragraph_spacing_value is None:
|
||||||
|
paragraph_spacing_value = frontmatter.get("paragraph_lines")
|
||||||
fm = FrontMatter(
|
fm = FrontMatter(
|
||||||
h1_font=frontmatter.get("h1_font", "standard"),
|
h1_font=frontmatter.get("h1_font", "standard").strip() or "standard",
|
||||||
h2_font=frontmatter.get("h2_font", "standard"),
|
h2_font=frontmatter.get("h2_font", "standard").strip() or "standard",
|
||||||
h3_font=frontmatter.get("h3_font", "standard"),
|
h3_font=frontmatter.get("h3_font", "standard").strip() or "standard",
|
||||||
|
margin_left=_parse_int(frontmatter.get("margin_left"), 0),
|
||||||
|
margin_right=_parse_int(frontmatter.get("margin_right"), 0),
|
||||||
|
paragraph_spacing=max(0, _parse_int(paragraph_spacing_value, 0)),
|
||||||
|
hyphenate=_parse_bool(frontmatter.get("hyphenate"), False),
|
||||||
|
hyphen_lang=(frontmatter.get("hyphen_lang") or "en_US").strip() or "en_US",
|
||||||
)
|
)
|
||||||
return fm, remaining
|
return fm, remaining
|
||||||
|
|
||||||
|
|
|
||||||
163
output.txt
163
output.txt
|
|
@ -1,163 +0,0 @@
|
||||||
_ _ _ _ _ _ ___ __
|
|
||||||
| |__ / | | | | | ___ __ _ __| (_)_ __ __ _ ( _ ) \ \
|
|
||||||
| '_ \| | | |_| |/ _ \/ _` |/ _` | | '_ \ / _` | / _ \ _____| |
|
|
||||||
| | | | | | _ | __/ (_| | (_| | | | | | (_| | | (_) |_____| |
|
|
||||||
|_| |_|_| |_| |_|\___|\__,_|\__,_|_|_| |_|\__, | \___/ | |
|
|
||||||
|___/ /_/
|
|
||||||
|
|
||||||
_ ____ _ _ _ _
|
|
||||||
| |__ |___ \ | | | | ___ __ _ __| (_)_ __ __ _
|
|
||||||
| '_ \ __) | | |_| |/ _ \/ _` |/ _` | | '_ \ / _` |
|
|
||||||
| | | |/ __/ | _ | __/ (_| | (_| | | | | | (_| |
|
|
||||||
|_| |_|_____| |_| |_|\___|\__,_|\__,_|_|_| |_|\__, |
|
|
||||||
|___/
|
|
||||||
|
|
||||||
_ _____ _ _ _ _
|
|
||||||
| |__ |___ / | | | | ___ __ _ __| (_)_ __ __ _
|
|
||||||
| '_ \ |_ \ | |_| |/ _ \/ _` |/ _` | | '_ \ / _` |
|
|
||||||
| | | |___) | | _ | __/ (_| | (_| | | | | | (_| |
|
|
||||||
|_| |_|____/ |_| |_|\___|\__,_|\__,_|_|_| |_|\__, |
|
|
||||||
|___/
|
|
||||||
|
|
||||||
H4 HEADING
|
|
||||||
----------
|
|
||||||
|
|
||||||
H5 HEADING
|
|
||||||
----------
|
|
||||||
|
|
||||||
H6 HEADING
|
|
||||||
----------
|
|
||||||
|
|
||||||
|
|
||||||
_ _ _ _ _ ____ _
|
|
||||||
| | | | ___ _ __(_)_______ _ __ | |_ __ _| | | _ \ _ _| | ___ ___
|
|
||||||
| |_| |/ _ \| '__| |_ / _ \| '_ \| __/ _` | | | |_) | | | | |/ _ \/ __|
|
|
||||||
| _ | (_) | | | |/ / (_) | | | | || (_| | | | _ <| |_| | | __/\__ \
|
|
||||||
|_| |_|\___/|_| |_/___\___/|_| |_|\__\__,_|_| |_| \_\\__,_|_|\___||___/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_____ _ _
|
|
||||||
| ____|_ __ ___ _ __ | |__ __ _ ___(_)___
|
|
||||||
| _| | '_ ` _ \| '_ \| '_ \ / _` / __| / __|
|
|
||||||
| |___| | | | | | |_) | | | | (_| \__ \ \__ \
|
|
||||||
|_____|_| |_| |_| .__/|_| |_|\__,_|___/_|___/
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
T H I S I S B O L D T E X T _T_H_I_S___I_S___B_O_L_D___T_E_X_T_ T h i s
|
|
||||||
i s i t a l i c t e x t _T_h_i_s___i_s___i_t_a_l_i_c___t_e_x_t_
|
|
||||||
-S-t-r-i-k-e-t-h-r-o-u-g-h-
|
|
||||||
|
|
||||||
____ _ _ _
|
|
||||||
| __ )| | ___ ___| | ____ _ _ _ ___ | |_ ___ ___
|
|
||||||
| _ \| |/ _ \ / __| |/ / _` | | | |/ _ \| __/ _ \/ __|
|
|
||||||
| |_) | | (_) | (__| < (_| | |_| | (_) | || __/\__ \
|
|
||||||
|____/|_|\___/ \___|_|\_\__, |\__,_|\___/ \__\___||___/
|
|
||||||
|_|
|
|
||||||
|
|
||||||
|
|
||||||
| Blockquotes can also be nested...
|
|
||||||
| | by using additional greater-than signs right next to each other...
|
|
||||||
| | | or with spaces between arrows.
|
|
||||||
|
|
||||||
_ _ _
|
|
||||||
| | (_)___| |_ ___
|
|
||||||
| | | / __| __/ __|
|
|
||||||
| |___| \__ \ |_\__ \
|
|
||||||
|_____|_|___/\__|___/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ _ _ _
|
|
||||||
| | | |_ __ ___ _ __ __| | ___ _ __ ___ __| |
|
|
||||||
| | | | '_ \ / _ \| '__/ _` |/ _ \ '__/ _ \/ _` |
|
|
||||||
| |_| | | | | (_) | | | (_| | __/ | | __/ (_| |
|
|
||||||
\___/|_| |_|\___/|_| \__,_|\___|_| \___|\__,_|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+ Create a list by starting a line with `+`, `-`, or `*`
|
|
||||||
- Marker character change forces new list start:
|
|
||||||
* Ac tristique libero volutpat at
|
|
||||||
+ Facilisis in pretium nisl aliquet
|
|
||||||
- Nulla volutpat aliquam velit
|
|
||||||
+ Very easy
|
|
||||||
|
|
||||||
___ _ _
|
|
||||||
/ _ \ _ __ __| | ___ _ __ ___ __| |
|
|
||||||
| | | | '__/ _` |/ _ \ '__/ _ \/ _` |
|
|
||||||
| |_| | | | (_| | __/ | | __/ (_| |
|
|
||||||
\___/|_| \__,_|\___|_| \___|\__,_|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1. Lorem ipsum dolor sit amet
|
|
||||||
2. Consectetur adipiscing elit
|
|
||||||
3. Integer molestie lorem at massa
|
|
||||||
|
|
||||||
____ _
|
|
||||||
/ ___|___ __| | ___
|
|
||||||
| | / _ \ / _` |/ _ \
|
|
||||||
| |__| (_) | (_| | __/
|
|
||||||
\____\___/ \__,_|\___|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Inline `code`
|
|
||||||
|
|
||||||
Indented code
|
|
||||||
|
|
||||||
01 | // Some comments
|
|
||||||
02 | line 1 of code
|
|
||||||
03 | line 2 of code
|
|
||||||
04 | line 3 of code
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Block code "fences"
|
|
||||||
|
|
||||||
01 | Sample text here...
|
|
||||||
|
|
||||||
|
|
||||||
Syntax highlighting
|
|
||||||
|
|
||||||
01 | var foo = function (bar) {
|
|
||||||
02 | return bar++;
|
|
||||||
03 | };
|
|
||||||
04 |
|
|
||||||
05 | console.log(foo(5));
|
|
||||||
|
|
||||||
|
|
||||||
_ _ _
|
|
||||||
| | (_)_ __ | | _____
|
|
||||||
| | | | '_ \| |/ / __|
|
|
||||||
| |___| | | | | <\__ \
|
|
||||||
|_____|_|_| |_|_|\_\___/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[link text](1)
|
|
||||||
|
|
||||||
[link with title](2)
|
|
||||||
|
|
||||||
___
|
|
||||||
|_ _|_ __ ___ __ _ __ _ ___ ___
|
|
||||||
| || '_ ` _ \ / _` |/ _` |/ _ \/ __|
|
|
||||||
| || | | | | | (_| | (_| | __/\__ \
|
|
||||||
|___|_| |_| |_|\__,_|\__, |\___||___/
|
|
||||||
|___/
|
|
||||||
|
|
||||||
|
|
||||||
[Image: Minion](3) [Image: Stormtroopocat](4)
|
|
||||||
|
|
||||||
[1] http://dev.nodeca.com
|
|
||||||
[2] http://nodeca.github.io/pica/demo/
|
|
||||||
[3] https://octodex.github.com/images/minion.png
|
|
||||||
[4] https://octodex.github.com/images/stormtroopocat.jpg
|
|
||||||
|
|
@ -6,4 +6,5 @@ readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyfiglet>=0.8.0",
|
"pyfiglet>=0.8.0",
|
||||||
|
"pyphen>=0.17.2",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
pyphen>=0.17.2
|
||||||
pyfiglet>=0.8.0
|
pyfiglet>=0.8.0
|
||||||
|
|
|
||||||
15
uv.lock
generated
15
uv.lock
generated
|
|
@ -8,10 +8,14 @@ version = "0.1.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "pyfiglet" },
|
{ name = "pyfiglet" },
|
||||||
|
{ name = "pyphen" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [{ name = "pyfiglet", specifier = ">=0.8.0" }]
|
requires-dist = [
|
||||||
|
{ name = "pyfiglet", specifier = ">=0.8.0" },
|
||||||
|
{ name = "pyphen", specifier = ">=0.17.2" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyfiglet"
|
name = "pyfiglet"
|
||||||
|
|
@ -21,3 +25,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/c8/e3/0a86276ad2c383ce0
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl", hash = "sha256:65b57b7a8e1dff8a67dc8e940a117238661d5e14c3e49121032bd404d9b2b39f", size = 1806118, upload-time = "2025-08-15T18:32:45.556Z" },
|
{ url = "https://files.pythonhosted.org/packages/9f/5c/fe9f95abd5eaedfa69f31e450f7e2768bef121dbdf25bcddee2cd3087a16/pyfiglet-1.0.4-py3-none-any.whl", hash = "sha256:65b57b7a8e1dff8a67dc8e940a117238661d5e14c3e49121032bd404d9b2b39f", size = 1806118, upload-time = "2025-08-15T18:32:45.556Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyphen"
|
||||||
|
version = "0.17.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/69/56/e4d7e1bd70d997713649c5ce530b2d15a5fc2245a74ca820fc2d51d89d4d/pyphen-0.17.2.tar.gz", hash = "sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3", size = 2079470, upload-time = "2025-01-20T13:18:36.296Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/1f/c2142d2edf833a90728e5cdeb10bdbdc094dde8dbac078cee0cf33f5e11b/pyphen-0.17.2-py3-none-any.whl", hash = "sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd", size = 2079358, upload-time = "2025-01-20T13:18:29.629Z" },
|
||||||
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue