Wenlan
Wenlan is a source-backed AI knowledge base and LLM wiki for AI work: organize documents and decisions into pages you can find, inspect, and review.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Flask app that assembles the Taiwanese-language Sunday worship deck from the week's inputs: the agenda PDF, the hymn / responsive-reading PPTX files, and the anthem lyrics DOCX.
python3 -m pip install -r requirements.txt
python3 app.py # http://localhost:5001
Three PPTX assets, with distinct roles (deck_sources.py):
| Asset | Role |
|---|---|
template/intro.pptx | The fixed pre-worship slides. The output deck starts as a copy of this file, so it also supplies the slide masters and the 詩歌 / Blank layouts every generated slide uses. |
template/fixed.pptx | Searched by text marker for every recurring slide: 宣召, 祈禱, 主禱文, 信仰告白, 奉獻, 聖餐, 頌榮, 祝禱, 默禱, website. |
example/*/output/*.pptx | Library, searched for hymn / reading / anthem slides when the week's own input file is missing. |
template/fixed.pptx is pinned deliberately. It used to be whichever past
output deck os.listdir returned first, which meant editing an old week's
deck silently changed this week's output. If you replace it, run the
regression harness — it asserts every marker in slide_finder.SLIDE_MARKERS
still resolves to exactly one slide.
Pipeline: pdf_parser → slide_planner (agenda + files → ordered slide specs)
→ ppt_builder (dispatch) → slide_copier (copy existing slides) or
slide_generators (build new ones). text_layout does all text measurement;
autofit shrinks anything that would still overflow.
Where slides break is arithmetic, and it is all in text_layout.py:
display_width). A CJK glyph
is 1 em, Latin is 0.5. Counting len() measured a URL-heavy announcement as
twice its real width.DEFAULT_LINE_HEIGHT = 1.36 is measured, not assumed. Rendering the decks
through LibreOffice and reading back text extents gives ~1.02in per line for
54pt 標楷體. With the usual 1.2 the 詩歌 body box looks like it holds 7
lines; it holds 6 — exactly the off-by-one that pushed the last lyric line
off every slide.service_config.SLIDE_STYLES, measured from the
詩歌 layout rather than hard-coded.Multi-line text is written with styles.write_lines, which emits real
<a:br/> elements. A literal \n assigned through run.text lands inside
<a:t>, where OOXML collapses it — PowerPoint renders no break at all.
Three rules the generated text follows, all derived from the hand-adjusted reference decks:
Verse numbers align. Continuation lines are indented to clear the verse number, and the number field is padded to the width of the widest number on that slide, so a slide holding verses 9 and 10 reads
9. 因為我在人的權下, not 9. 因為我在人的權下,
下;對這個說 下;對這個說
10. 耶穌聽見就希奇 10.耶穌聽見就希奇
Grouping uses the widest number in the whole passage, then each slide is re-wrapped against its own — which can only shorten lines, never overflow.
No orphaned punctuation (text_layout.NO_LINE_START / NO_LINE_END). A
line never begins with a comma, full stop or closing bracket, and never ends
with an opening one. Without this, a verse could end with a line holding
nothing but 」.
Chinese clauses lose their space. The bulletin writes Chinese clauses with
an ASCII comma, so 信箱, 可填好, 11/15, 積極會員 and 1559, 微信 all lose
the space. What the comma follows doesn't matter, only what it introduces —
which keeps Apt.3F, Flushing and 密碼 626954; 電話 646-558-8656 intact.
LINE_PITCH is 1.2 and PARA_SPACING is 0.2, both measured off rendered PDFs:
consecutive baselines are exactly 60.0pt for 50pt text in a plain text box, and
70.0pt in a 詩歌 body placeholder, which adds the master's 20% spcBef. Keeping
them separate matters — rolling both into one 1.36 factor and adding
paragraph spacing double-counted the gap, over-predicted multi-paragraph
heights by ~30%, and made the autofit pass shrink 宣召 from 66pt to 55pt and
信仰告白 from 50pt to 33pt.
autofit.TOP_LEADING covers a separate effect: a body placeholder inheriting
the 詩歌 master renders its first line about a full line below the top of the
box, so a 6-line body whose text is 438pt tall starts 59pt down a 455pt box and
runs off the slide even though the text alone fits. Plain text boxes have no
such offset.
Slides copied from template/fixed.pptx are never re-measured — that deck is
curated by hand and is reproduced exactly.
Eleven weeks of real input/output pairs live in example_2/. The harness
generates each week and compares it to the hand-finished deck:
python3 tools/regress.py # structural diff + marker check
python3 tools/regress.py --render # + LibreOffice render, off-slide text
python3 tools/regress.py --week 20260802 -v
python3 tools/regress.py --save-baseline
python3 tools/regress.py --render --check # non-zero exit if worse than baseline
Three checks, because they catch different failures:
difflib. Catches
dropped sections, wrong content, changed page splits.Current state across 11 weeks: 0 slides with off-slide text, against 45 in the hand-finished reference decks; mean text similarity 0.876.
Reading the structural diff: a difference is not automatically a defect. The
references contain hand omissions the generator correctly does not reproduce —
20260816 drops the 306A closing hymn the agenda lists, and several 啟應文
slides there lost words when continuation lines were merged by deleting them.
The generator follows the agenda.
This is the beginning of the README. Read the rest at the original. Read the full README ↗