幫一年級的孩子印一張字帖,狀態列寫著「每頁 10 行 · 每行 10 格」,那就是 100 個格子;一個「範字 + 描紅 + 2 空格」要吃 4 格,100 ÷ 4 = 25,我敲了 25 個字,列印出來卻是兩張紙,第二頁掛著 5 個字:預設的 A4 每頁只有 20 個字,而不是 100 ÷ 4 得到的 25 個。重現過裝箱程式碼才明白,它用的不是這道除法。
每頁字數 = floor(每行格數 ÷ 組寬) × 每頁行數
711–734 行的裝箱一組一組往當前行裡塞,「再塞一組就超過每行格數」(717 行)就整組另起一行:組不跨行,行末餘格作廢。
預設版式(A4 縱向、格寬 16mm、邊距 10mm、開拼音、每頁資訊欄)實測 cols=10、rowsPerPage=10,mtb 的組寬是 4,floor(10 ÷ 4) × 10 = 20,每行 2 組、餘 2 格;差的 5 組正是每行 2 格 × 10 行 = 20 個排不進內容的格子位。兒童練字字帖生成 的說明區寫的就是這道除法。
換 pattern 就是換組寬
格寬 12mm 配「窄」邊距 6mm 把每行頂到 14 格、每頁頂到 13 行,A5 同參數只剩 7 格 7 行;這五組在 Node 與 headless Chrome 上各跑一遍完全一致。
| 版式 | 每行格數 | 每頁行數 | 每行組數 / 餘格 | 每頁字數 |
|---|---|---|---|---|
| A4 預設 mtb | 10 | 10 | 2 組 / 餘 2 | 20 |
| A4 預設 mb | 10 | 10 | 3 組 / 餘 1 | 30 |
| A4 預設 m(整行範字) | 10 | 10 | 10 組 / 餘 0 | 100 |
| A4 格寬 12mm + 邊距 6mm | 14 | 13 | 3 組 / 餘 2 | 39 |
| A5 預設參數 mtb | 7 | 7 | 1 組 / 餘 3 | 7 |
關拼音只動行數
拼音那一橫帶高 = 格寬 × 0.4,16mm 即 6.4mm(662、671–672 行):關掉它每頁 13 行、26 個字,資訊欄改「不顯示」是 11 行、22 個字,兩個都關 14 行、28 個字。狀態列(1067–1069 行)原文「每頁 10 行 · 每行 10 格 · 格寬 16 mm · 練習位 44」,不說每頁幾個字。
三條上限各管一件事
600 數的是非空白字元(742–744 行):600 個字夾空格時原始長度已達 1,199 仍不截,640 個字(raw 1,279)才觸發「內容超過 600 個字,已截斷。」(1075 行)。
40 頁是一筆行預算,maxRows = rowsPerPage × 40(759–761 行):預設版式預算 400 行,600 個字只排到 300 行,「已達到 40 頁上限」在預設版式永遠印不出來。
組寬大於每行格數是第三條路:A5 + 格寬 30mm + 寬邊距 15mm → cols=3、rows=3,716 行的 slice(0, cols) 把組尾 1 個空格砍了,11 個字排 11 行 4 頁;提示前半句是「每行只放得下 3 格,裝不下『 4 格一組』,超出的空格已丟棄。」
讓 40 頁那條提示可達
A5 + 格寬 20mm + mtb:rowsPerPage 5、maxRows 200。201 個字開始提示並丟 1 個,600 個字被 slice 到 200 行、一次丟 400 個,而 pageCount 仍是 40,被丟掉的行不在預覽裡。
圖 1:輸入長度對著行預算 200 的丟字量。
#py: 那一格認什麼、不認什麼
頁面提示(73 行)讓另起一行寫 #py: yin1 yue4,落不落得進格子取決於 586 行的守衛 /^[a-z]+[0-9]$/:
- 純 ASCII 音節加數字聲調才轉:
tian1→tiān、lve4→lüè; - 帶調字母原樣用,全形冒號
#py:、#PY:和帶序號的寫法都認; - 混寫不轉,數字留在格子裡印出來:
lüe4、yuè4、TIAN1。
直調 applyToneMarks 傳 lüe4 只剩 è(377 行的正則不收 ü),但 586 行把它擋在介面外,頁面上只是殘留數字。
拼音表 2,639 個鍵,44 個重複
Object.keys(PINYIN_MAP).length 是 2,639:303–350 行字面寫了 2,676 條、44 個鍵重複(去重剩 2,632),353–359 行補錄 7 個漏掉的高頻字(粒 得 區 我 為 而 西)。後寫蓋先寫,9 個取值相同沒有副作用,35 個兩讀不同的留下常常不是常用音——了 → liǎo(le0 寫在 325 行前面)、阿 → ē、省 → xǐng。核對讀音可先用 漢字轉拼音 轉一遍,兩邊是同一份表。
空格不分組,標點和數字都佔格
漢字模式下 567–578 行把空白全部丟棄,天 地、天地、換行、製表符四種輸入等價——「不佔格子」對,「用來分組」不成立。只有拼音和數字那條分支按空白分組(605–614 行,609 行連製表符都不算):ni hao 是 2 個 unit、nihao 是 1 個。快捷字表「詞語練習」那串 朋友 同学 … 因此排成 21 個連排單字。
標點和數字一格一個:123 456 是 6 個 unit,「靜夜思」的 , 。 也各佔一格。沒收錄的不猜:魃鑫龘弼黉 5 個字留空並計入 miss 提示(1081 行),^丨丶 這類字元也計 miss(實測 2)。
整行範字後面的空格不另起行
mrow 的 hint 說空格數就是空臨行數,但 696–709 行是把 b 份「與內容等長的空格串」接在範字流尾巴上再連續裝箱。預設 10 列下 11 字配空格數 1,行內是 MMMMMMMMMM / M········· / ··(空格數 8 時總格數 99 = 11×9)。
匯出體積與 PDF 裡裝了什麼
A4 單頁 11 個字實測:JPEG 514,478 B、PNG 950,158 B(1.85 倍)、工具自寫的 PDF 515,288 B,容器 810 B。PDF 每頁是一張點陣圖:內容流只有 36 字元的 q … cm /ImN Do Q、沒有文字操作符(1450、1454 行)。匯出按 12 px/mm=305dpi,直接列印是 8 px/mm≈203dpi,超過 15 頁降到 6 px/mm≈152dpi(470–472、1255 行)。
把頁面文案逐條對回程式碼
config/tools.php 裡 2205–2209 行那段說明對著當前模板重現,是 33 條 = 28 條 [一致] / 5 條 [衝突]:寫死的 2639、每頁 20 / 30 / 100 / 39、關拼音 13 行、A5 的 7×7×1、305 / 203 / 152dpi 全部命中;剩下 5 條都是口徑級,把「按空白分組」「常用音」「都認」「40 頁」「空臨行數」說得太滿。拼音資料的另一面見站內那篇 漢字加拼音注音。
三個容易踩的坑
Q1:把格距調到 0 多出一列,能多放一組嗎?
不能。實測格距 0 時 cols 變 11,11 ÷ 4 仍取 2,每頁還是 20 個字。
Q2:600 個字是從哪一段開始被丟的?
提示只說「已截斷」(1075 行),不報起始位置,被丟的行也不在預覽裡。
Q3:不心算能不能知道一頁放幾個字?
頁面上給不出,只能導一版數末頁:預設每頁 20 個字,25 個字就分兩頁;排長文換 mb(每頁 30 個字)。