完善国标公文全条款核验与特定版式
This commit is contained in:
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
|
||||
甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥天地玄黄宇宙
|
||||
@@ -20,11 +20,12 @@ function decodeHtml(value) {
|
||||
|
||||
function words(pdf) {
|
||||
const html = execFileSync("pdftotext", ["-f", "1", "-l", "1", "-bbox-layout", pdf, "-"], { encoding: "utf8" });
|
||||
return [...html.matchAll(/<word\s+[^>]*xMin="([0-9.]+)"[^>]*yMin="([0-9.]+)"[^>]*xMax="([0-9.]+)"[^>]*>([\s\S]*?)<\/word>/g)].map((match) => ({
|
||||
return [...html.matchAll(/<word\s+[^>]*xMin="([0-9.]+)"[^>]*yMin="([0-9.]+)"[^>]*xMax="([0-9.]+)"[^>]*yMax="([0-9.]+)"[^>]*>([\s\S]*?)<\/word>/g)].map((match) => ({
|
||||
xMinPt: Number(match[1]),
|
||||
yMinPt: Number(match[2]),
|
||||
xMaxPt: Number(match[3]),
|
||||
text: decodeHtml(match[4]),
|
||||
yMaxPt: Number(match[4]),
|
||||
text: decodeHtml(match[5]),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -34,9 +35,12 @@ function findText(pdfWords, expected) {
|
||||
// measurement is independent of that tokenization choice.
|
||||
const rows = [];
|
||||
for (const word of [...pdfWords].sort((left, right) => left.yMinPt - right.yMinPt || left.xMinPt - right.xMinPt)) {
|
||||
const row = rows.find(({ yMinPt }) => Math.abs(yMinPt - word.yMinPt) <= 1.5);
|
||||
if (row) row.words.push(word);
|
||||
else rows.push({ yMinPt: word.yMinPt, words: [word] });
|
||||
const row = rows.find(({ yMinPt, yMaxPt }) => word.yMinPt <= yMaxPt + 1.5 && word.yMaxPt >= yMinPt - 1.5);
|
||||
if (row) {
|
||||
row.words.push(word);
|
||||
row.yMinPt = Math.min(row.yMinPt, word.yMinPt);
|
||||
row.yMaxPt = Math.max(row.yMaxPt, word.yMaxPt);
|
||||
} else rows.push({ yMinPt: word.yMinPt, yMaxPt: word.yMaxPt, words: [word] });
|
||||
}
|
||||
for (const row of rows) {
|
||||
const ordered = row.words.sort((left, right) => left.xMinPt - right.xMinPt);
|
||||
|
||||
@@ -12,6 +12,7 @@ FIXTURE="$SKILL_DIR/tests/fixture.md"
|
||||
|
||||
"$NODE" --check "$GEN"
|
||||
"$NODE" --check "$VERIFY"
|
||||
"$NODE" --check "$SKILL_DIR/tests/measure-header-coordinates.mjs"
|
||||
|
||||
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/layout.docx" --format ordinary --org "不应变红头的单位" --title "公文格式回归测试" --page-number center
|
||||
"$NODE" "$VERIFY" --input "$TMP_DIR/layout.docx" --profile ordinary
|
||||
@@ -74,6 +75,9 @@ unzip -p "$TMP_DIR/letter.docx" word/footerLetter.xml | grep -q 'w:fill="FF0000"
|
||||
unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q '附件:1. 测试附件'
|
||||
unzip -p "$TMP_DIR/with-attachment.docx" word/document.xml | grep -q 'w:pageBreakBefore'
|
||||
|
||||
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/detached-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕1号" --title "分离附件测试" --attachment-note "1. 测试附件" --attachment-file "$TMP_DIR/attachment.md" --attachment-detached
|
||||
unzip -p "$TMP_DIR/detached-attachment.docx" word/document.xml | grep -q '示例发〔2026〕1号 附件1'
|
||||
|
||||
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/bad-attachment.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕6号" --title "附件一致性测试" --attachment-note "1. 另一份附件" --attachment-file "$TMP_DIR/attachment.md" >"$TMP_DIR/bad-attachment.out" 2>&1; then
|
||||
echo "mismatched attachment note unexpectedly succeeded" >&2
|
||||
exit 1
|
||||
@@ -91,6 +95,10 @@ grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-command.out"
|
||||
|
||||
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/minutes.docx" --format minutes --org "示例机关纪要" --title "纪要正文" --attendees "张三、李四" --absent "王五"
|
||||
"$NODE" "$VERIFY" --input "$TMP_DIR/minutes.docx" --profile minutes
|
||||
minutes_xml=$(unzip -p "$TMP_DIR/minutes.docx" word/document.xml)
|
||||
test "${minutes_xml%%出席:*}" != "$minutes_xml"
|
||||
test "${minutes_xml%%w:sectPr*}" != "$minutes_xml"
|
||||
test "${minutes_xml%%出席:*}" != "${minutes_xml%%w:sectPr*}"
|
||||
|
||||
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/seal-layout.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "签章位置测试" --sender "示例机关" --date "2026年9月10日" --seal-mode seal
|
||||
unzip -p "$TMP_DIR/seal-layout.docx" word/document.xml | grep -q 'w:right="1280"'
|
||||
@@ -112,6 +120,18 @@ grep -q 'GENERATOR ERROR:' "$TMP_DIR/bad-date.out"
|
||||
unzip -p "$TMP_DIR/letter-fields.docx" word/document.xml | grep -q '000007'
|
||||
unzip -p "$TMP_DIR/letter-fields.docx" word/document.xml | grep -q '机密★3年'
|
||||
|
||||
"$NODE" "$GEN" --input "$SKILL_DIR/tests/fixtures/visual-horizontal.md" --output "$TMP_DIR/horizontal-table.docx" --format horizontal-table --title "横排表格测试" --page-number standard
|
||||
"$NODE" "$VERIFY" --input "$TMP_DIR/horizontal-table.docx" --profile horizontal-table
|
||||
unzip -p "$TMP_DIR/horizontal-table.docx" word/document.xml | grep -q 'w:orient="landscape"'
|
||||
unzip -p "$TMP_DIR/horizontal-table.docx" word/document.xml | grep -q 'w:tblW w:w="12756"'
|
||||
|
||||
"$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/joint.docx" --format formal --letterhead digital --org "主办机关文件" --joint-org "协办机关" --doc-no "示例发〔2026〕3号" --title "联合行文测试"
|
||||
"$NODE" "$VERIFY" --input "$TMP_DIR/joint.docx" --profile formal --letterhead digital
|
||||
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '主办机关'
|
||||
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '协办机关'
|
||||
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q '文件'
|
||||
unzip -p "$TMP_DIR/joint.docx" word/document.xml | grep -q 'w:sz w:val="48"'
|
||||
|
||||
if "$NODE" "$GEN" --input "$FIXTURE" --output "$TMP_DIR/strict.docx" --format formal --letterhead digital --org "示例单位文件" --title "严格字体测试" --require-standard-fonts >"$TMP_DIR/strict.out" 2>&1; then
|
||||
if ! fc-list -f '%{family}\n' | grep -Eq '方正小标宋简体|方正小标宋_GBK|FZXiaoBiaoSong'; then
|
||||
echo "strict font mode unexpectedly succeeded without a small-standard-title font" >&2
|
||||
|
||||
@@ -15,6 +15,7 @@ long="$skill_dir/tests/fixtures/visual-long.md"
|
||||
attach_a="$skill_dir/tests/fixtures/visual-attachment-a.md"
|
||||
attach_b="$skill_dir/tests/fixtures/visual-attachment-b.md"
|
||||
horizontal="$skill_dir/tests/fixtures/visual-horizontal.md"
|
||||
calibration="$skill_dir/tests/fixtures/visual-calibration.md"
|
||||
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/ordinary.docx" --format ordinary --title "普通材料全要素实测" --page-number center
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-digital-all.docx" --format formal --letterhead digital --copy-no 7 --secret "机密★3年" --urgent "特急" --org "示例单位文件" --doc-no "示例发〔2026〕7号" --title "正式公文全要素实测" --to "各主送机关" --sender "示例单位" --date "2026年9月10日" --note "此件公开" --attachment-note "1. 数据清单;2. 实施计划。" --cc "办公室、财务部" --print-org "示例印发机关" --print-date "2026年9月10日" --page-number standard
|
||||
@@ -22,25 +23,31 @@ node "$gen" --input "$all" --output "$out_dir/docx/formal-digital-upward.docx" -
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-preprinted.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --copy-no 7 --secret "机密★3年" --urgent "特急" --doc-no "示例发〔2026〕9号" --title "预印红头套打实测" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-seal.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "盖章公文实测" --sender "示例单位" --date "2026年9月10日" --seal-mode seal --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-signed.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "签名章公文实测" --sender "示例单位" --date "2026年9月10日" --seal-mode signed --signer-title "主要负责人" --signer "张三" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-unsigned.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --title "不盖章公文实测" --sender "示例单位" --date "2026年9月10日" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-attachments.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕10号" --title "多附件实测" --attachment-note "1. 数据清单;2. 实施计划。" --attachment-file "$attach_a" --attachment-file "$attach_b" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-attachments-detached.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕15号" --title "分离装订附件实测" --attachment-note "1. 数据清单;2. 实施计划。" --attachment-file "$attach_a" --attachment-file "$attach_b" --attachment-detached --page-number standard
|
||||
node "$gen" --input "$long" --output "$out_dir/docx/formal-long.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕11号" --title "长文版记与单双页实测" --cc "办公室、财务部、审计部、采购部、法务部、信息中心" --print-org "示例印发机关" --print-date "2026年9月10日" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/formal-joint.docx" --format formal --letterhead digital --org "主办机关文件" --joint-org "协办机关一" --joint-org "协办机关二" --doc-no "示例发〔2026〕16号" --title "联合行文实测" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/letter.docx" --format letter --org "示例机关" --doc-no "示例〔2026〕12号" --title "信函格式实测" --page-number none
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/letter-fields.docx" --format letter --org "示例机关" --copy-no 7 --secret "机密★3年" --urgent "特急" --doc-no "示例〔2026〕14号" --title "信函版头字段实测" --page-number none
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/command.docx" --format command --org "示例机关命令" --doc-no "第1号" --title "命令格式实测" --page-number standard
|
||||
node "$gen" --input "$all" --output "$out_dir/docx/minutes.docx" --format minutes --org "示例机关纪要" --title "纪要格式实测" --attendees "张三(办公室)、李四(财务部)" --absent "王五(审计部)" --observers "赵六(采购部)" --page-number standard
|
||||
node "$gen" --input "$horizontal" --output "$out_dir/docx/table.docx" --format formal --letterhead preprinted --letterhead-reserve-mm 72 --doc-no "示例发〔2026〕13号" --title "表格版式实测" --page-number standard
|
||||
node "$gen" --input "$horizontal" --output "$out_dir/docx/horizontal-table.docx" --format horizontal-table --title "横排表格实测" --page-number standard
|
||||
node "$gen" --input "$calibration" --output "$out_dir/docx/ordinary-calibration.docx" --format ordinary --page-number center
|
||||
|
||||
for file in "$out_dir/docx"/*.docx; do
|
||||
base=$(basename "$file" .docx)
|
||||
case "$base" in
|
||||
ordinary) profile=ordinary ;;
|
||||
ordinary|ordinary-calibration) profile=ordinary ;;
|
||||
letter|letter-fields) profile=letter ;;
|
||||
horizontal-table) profile=horizontal-table ;;
|
||||
command) profile=command ;;
|
||||
minutes) profile=minutes ;;
|
||||
*) profile=formal ;;
|
||||
esac
|
||||
case "$base" in
|
||||
formal-digital-*) letterhead=digital ;;
|
||||
formal-digital-*|formal-joint) letterhead=digital ;;
|
||||
*) letterhead=preprinted ;;
|
||||
esac
|
||||
node "$verify" --input "$file" --profile "$profile" --letterhead "$letterhead" >"$out_dir/logs/$base.verify.log"
|
||||
|
||||
Reference in New Issue
Block a user