diff --git a/packages/preview/gbt9704-gongwen/0.2.0/LICENSE b/packages/preview/gbt9704-gongwen/0.2.0/LICENSE new file mode 100644 index 0000000000..8bf937e752 --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-2026 songwupei + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/preview/gbt9704-gongwen/0.2.0/README.md b/packages/preview/gbt9704-gongwen/0.2.0/README.md new file mode 100644 index 0000000000..f871181e72 --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/README.md @@ -0,0 +1,215 @@ +# gbt9704-gongwen + +> GB/T 9704-2012 Official Document Format — Typst Template + +Version: 0.2.0 + +A Typst template implementing **GB/T 9704-2012**, the Chinese national standard for official government document layout. + +--- + +## Features + +- **Strict GB compliance**: page margins, font sizes, line spacing, heading fonts, indentation, justified alignment. +- **Document element functions**: red-header, main receiver, attachments, signatures, dates, end-matter (版记). +- **Red and black separator lines** for red-header and end-matter respectively. +- **Configurable spacing themes**: `compact` | `normal` | `relaxed` — three line-spacing presets. +- **Configurable table themes**: `full-grid` (default) | `three-line` | `plain` — three table border styles. +- **Faux bold table headers**: CJK-friendly text-stroke technique for fonts without native bold variants. +- **CJK font fallback chain** with open-source alternatives (FZDaBiaoSong → SimHei, FangSong → SimSun, etc.). + +--- + +## Quick Start + +### typst init + +```bash +typst init @preview/gbt9704-gongwen +``` + +### Manual import + +```typst +#import "@preview/gbt9704-gongwen:0.2.0": * +#show: gbt9704.with(redline: true, title-indent: true) + +// Your document content here ... +``` + +### Options + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `redline` | bool | `false` | Red separator line below red-header | +| `title-indent` | bool | `true` | Indent section headings 2 characters | +| `spacing-theme` | str | `"normal"` | Line spacing preset: `"compact"` / `"normal"` / `"relaxed"` | +| `table-theme` | str | `"full-grid"` | Table border style: `"full-grid"` / `"three-line"` / `"single-line"` / `"plain"` | +| `table-align` | str | `"center"` | Table horizontal alignment: `"center"` / `"left"` / `"right"` | +| `show-red-header` | bool | `true` | Show the red-header block / 红头显隐 | +| `first-page-number` | bool | `true` | Show page number on the first page / 首页是否显示页码 | +| `page-number-pos` | str | `"both"` | Page number position: `"both"` / `"center"` / `"left"` / `"right"` / 页码位置 | + +### Spacing Themes + +| Theme | `leading` | `spacing` | `heading-v` | Feel | +|---|---|---|---|---| +| `compact` | 0.75em | 0.5em | 0.5em | Tight GB/T grid | +| `normal` | 0.75em | 0.5em | 1em | Balanced (default) | +| `relaxed` | 0.7em | 0.5em | 2% | Airy, relaxed | + +All three themes share the same paragraph spacing (0.5em), so "paragraph gap = line gap + 8pt" holds consistently. + +### Table Themes + +| Theme | Effect | +|---|---| +| `full-grid` | All cell borders (default) | +| `three-line` | Top thick + header-bottom thin + bottom thick, no verticals | +| `single-line` | Header-bottom 1pt line only, zebra stripes | +| `plain` | No borders, centered only | + +--- + +### Changelog + +### v0.2.0 + +- Add `show-red-header` parameter — toggle entire red-header block +- Add `first-page-number` parameter — suppress page number on first page +- Add `page-number-pos` parameter — `"both"` / `"center"` / `"left"` / `"right"` +- Add `endmatter()` function — bottom-aligned CC + issuing info block with separator lines +- Page number format: `- 1 -` style +- Red line at bottom of first page (when `redline: true`) +- Signature: 3 blank lines before signature block, signatory right-aligned +- Bilingual README (EN + ZH) + +### v0.1.1 + +- Add `spacing-theme` parameter: `"compact"` / `"normal"` / `"relaxed"` +- Add `table-theme` parameter: `"full-grid"` / `"three-line"` / `"single-line"` / `"plain"` +- Add `table-align` parameter: `"center"` / `"left"` / `"right"` +- Faux bold table headers via text stroke (CJK-friendly) +- Table centering with paragraph indent neutralization +- All spacing values use `em` units (font-size-relative) +- Refactored `src/` layout: `spacing.typ` + `table.typ` + `lib.typ` + +### v0.1.0 +## Core Functions + +| Function | Description | +|---|---| +| `gongwentitle(title)` | Document title — 22pt red title font, centered | +| `gongwensubtitle(subtitle)` | Subtitle — 16pt FangSong, centered | +| `make-header(organ, number, signatory, redline)` | Red-header: organ name, document number, signatory | +| `main-receiver(receiver)` | Main receiver — flush left, no indent | +| `attachment(title)` | Single attachment with "附件:" prefix, 2-char indent | +| `attachment-no-hz(title)` | Attachment without prefix, 5-char block indent | +| `attachment-item(title, indent)` | Attachment item with custom indent | +| `signature(name)` | Signature — right-aligned, 2-char right margin | +| `signdate(date)` | Date — same format as signature | +| `notes(text)` | Notes in parentheses, no indent | +| `copyto(text)` | CC field with "抄送:" prefix | +| `issueinfo(organ, date)` | Issuing organ and date, left-right aligned | +| `seprule` | Black separator line for end-matter (版记) | +| `redrule` | Red separator line for red-header | + +--- + +## Heading Levels + +| Level | Syntax | Font | Size | +|---|---|---|---| +| 1 | `=` | SimHei (黑体) | 16pt | +| 2 | `==` | KaiTi bold (楷体加粗) | 16pt | +| 3 | `===` | FangSong bold (仿宋加粗) | 16pt | + +--- + +## Font Dependencies + +Fonts are listed in priority order; fallbacks are used automatically. + +| Font | Purpose | Fallback | +|---|---|---| +| FZDaBiaoSong-B06 (方正大标宋) | Red-header, document title | SimHei (黑体) | +| FangSong (仿宋) | Body text | SimSun (宋体) | +| SimHei (黑体) | Level-1 headings, table headers | Noto Sans CJK SC | +| KaiTi (楷体) | Level-2 headings | STKaiti, Noto Serif CJK SC | +| SimSun (宋体) | Auxiliary | Noto Serif CJK SC | + +--- + +## Project Structure + +```text +gbt9704-gongwen/ +├── src/ +│ └── lib.typ # Template source +├── template/ +│ └── main.typ # typst init template +├── examples/ +│ ├── example.typ # Full document example (zh) +│ ├── example-en.typ # Full document example (en) +│ ├── manual.typ # Usage manual (zh) +│ └── manual-en.typ # Usage manual (en) +├── typst.toml +├── LICENSE +└── README.md +``` + +--- + +## Examples + +Compile the examples: + +```bash +# Chinese — red-header, tables, attachments, end-matter +typst compile --root . examples/example.typ + +# English +typst compile --root . examples/example-en.typ + +# Chinese usage manual (API reference) +typst compile --root . examples/manual.typ + +# English usage manual +typst compile --root . examples/manual-en.typ +``` + +Pre-compiled PDFs: + +- [example.pdf](https://codeberg.org/songwupei/typst-gbt9704/raw/tag/v0.1.0/examples/example.pdf) +- [example-en.pdf](https://codeberg.org/songwupei/typst-gbt9704/raw/tag/v0.1.0/examples/example-en.pdf) +- [manual.pdf](https://codeberg.org/songwupei/typst-gbt9704/raw/tag/v0.1.0/examples/manual.pdf) +- [manual-en.pdf](https://codeberg.org/songwupei/typst-gbt9704/raw/tag/v0.1.0/examples/manual-en.pdf) + +--- + +## Changelog + +### v0.1.0 (2026-07-09) + +- **First Typst release**: Full GB/T 9704-2012 official document format support. +- A4 page, GB-standard margins (L 28mm, R 26mm, T 37mm, B 35mm), 28pt line spacing. +- Document element functions: red-header, title, main receiver, attachment, signature, date, end-matter, notes, CC. +- Chinese heading numbering (一、(一)、1.), first-line indent, centered tables. +- Font sizes: title 22pt, body 16pt, document number 12pt. +- Font fallback: FZDaBiaoSong, FangSong, SimHei, KaiTi, SimSun + open-source alternatives. +- `typst init @preview/gbt9704-gongwen` quick-start template. + +--- + +## 中文简述 + +GB/T 9704-2012《党政机关公文格式》Typst 模板。严格遵循国标:A4 页面、国标边距、28 磅行距、三号仿宋正文、首行缩进 2 字符。提供红头、主送、附件、署名、日期、版记等公文要素函数。支持三档行间距主题(紧凑/正常/宽松)和三档表格样式(全线表/三线表/无线表)。字体回退链覆盖方正大标宋、仿宋、黑体、楷体、宋体及开源替代。 + +详细中文手册见 [manual.pdf](https://codeberg.org/songwupei/typst-gbt9704/raw/tag/v0.1.0/examples/manual.pdf)。 + +--- + +## Feedback & Contributing + +- Issues: https://codeberg.org/songwupei/typst-gbt9704/issues +- Repository: https://codeberg.org/songwupei/typst-gbt9704 diff --git a/packages/preview/gbt9704-gongwen/0.2.0/src/lib.typ b/packages/preview/gbt9704-gongwen/0.2.0/src/lib.typ new file mode 100644 index 0000000000..f014087bef --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/src/lib.typ @@ -0,0 +1,324 @@ +// ═══════════════════════════════════════════════════════════════ +// gbt9704-gongwen — 适配 GB/T 9704-2012 · 党政机关公文格式 +// Typst Template v0.1.0 +// License: MIT +// Repository: https://codeberg.org/songwupei/typst-gbt9704 +// ═══════════════════════════════════════════════════════════════ + +#import "spacing.typ" as sp +#import "table.typ" as tbl + +// ══════ Constants · 常量 ══════ + +/// Standard document colors / 公文标准颜色。 +#let _colors = ( + red: rgb("#C8102E"), + black: rgb("#000000"), +) + +/// Standard document fonts with fallback chain / 公文标准字体(按优先级排列,自动回退)。 +#let _fonts = ( + title: ("FZDaBiaoSong-B06", "FZXiaoBiaoSong-B05S", "SimHei"), + body: ("FangSong", "HYShuFang", "SimSun", "Noto Serif CJK SC"), + heading1: ("SimHei", "Noto Sans SC", "Noto Serif CJK SC"), + heading2: ("KaiTi", "STKaiti", "HYShuFang", "Noto Serif CJK SC"), + heading3: ("FangSong", "HYShuFang", "SimSun", "Noto Serif CJK SC"), + number: ("FangSong", "HYShuFang", "SimSun"), +) + +// ══════ Template · 模板 ══════ + +/// State: controls red-header visibility / 控制红头显隐的状态。 +#let _show-red-header-state = state("gbt9704-show-red-header", true) + +/// Apply GB/T 9704-2012 official document formatting. +/// 应用 GB/T 9704-2012 公文格式。 +/// +/// # Parameters · 参数 +/// - `redline` (bool): Draw a red separator line below the red-header. Default `false`. +/// 是否在红头下方绘制红色分隔线,默认 `false`。 +/// - `title-indent` (bool): Whether to indent section headings by 2 characters. Default `true`. +/// 章节标题是否首行缩进 2 字符,默认 `true`。 +/// - `spacing-theme` (str): `"compact"` | `"normal"` (default) | `"relaxed"`. +/// 行间距主题。 +/// - `table-theme` (str): `"full-grid"` (default) | `"three-line"` | `"single-line"` | `"plain"`. +/// 表格样式主题。 +/// - `table-align` (str): Table horizontal alignment. `"center"` (default) | `"left"` | `"right"`. +/// 表格水平对齐方式。 +/// +/// - `first-page-number` (bool): Show page number on the first page. Default `true`. +/// 首页是否显示页码,默认 `true`。 +/// - `page-number-pos` (str): Page number position. `"both"` (default) | `"center"` | `"left"` | `"right"`. +/// 页码位置:`"both"` 奇右偶左(外侧)| `"center"` 居中 | `"left"` 左侧 | `"right"` 右侧。 +/// - `show-red-header` (bool): Whether to render the red-header block. Default `true`. +/// 是否显示红头块,默认 `true`。设为 `false` 则 `make-header()` 无输出。 +/// +/// # Usage · 用法 +/// ```typst +/// #import "@preview/gbt9704-gongwen:0.1.0": * +/// #show: gbt9704.with(redline: true) +/// +/// #gongwentitle[Document Title / 文件标题] +/// = Level-1 Heading · 一级标题 +/// Body text ... 正文内容 ... +/// ``` +#let gbt9704( + redline: false, + title-indent: true, + spacing-theme: "normal", + table-theme: "full-grid", + table-align: "center", + first-page-number: false, + page-number-pos: "both", + show-red-header: true, + body, +) = { + // Document metadata / 文档元数据 + set document(title: "", author: "") + + // Page setup: A4 with GB-standard margins / 页面设置:A4,国标边距 + set page( + paper: "a4", + margin: (left: 28mm, right: 26mm, top: 37mm, bottom: 35mm), + footer: context { + let p = counter(page).get().first() + // Red line at bottom of first page / 首页底部红线 + if redline and p == 1 { + set text(font: _fonts.body, size: 14pt) + line(length: 100%, stroke: (paint: _colors.red, thickness: 1.5pt)) + } + if first-page-number or p > 1 { + set text(font: _fonts.body, size: 14pt) + let num = [\- #p -] + if page-number-pos == "left" { + [#num #h(1fr)] + } else if page-number-pos == "right" { + [#h(1fr) #num] + } else if page-number-pos == "both" { + if calc.odd(p) { + [#h(1fr) #num] + } else { + [#num #h(1fr)] + } + } else { // center + align(center, num) + } + } + }, + ) + + // Body font: 16pt FangSong, Chinese locale / 正文字体:三号 (16pt) 仿宋,中文语境 + set text( + font: _fonts.body, + size: 16pt, + lang: "zh", + region: "cn", + ) + + // ── Spacing / 间距 ── + let s = sp.resolve(spacing-theme) + + set par( + leading: s.leading, + first-line-indent: if title-indent { + (amount: 2em, all: true) + } else { + (amount: 0em, all: true) + }, + spacing: s.para-spacing, + justify: true, + linebreaks: "optimized", + ) + + // ── Headings / 标题 ── + set heading(numbering: none) + show heading.where(level: 1): set text(font: _fonts.heading1, size: 16pt) + show heading.where(level: 2): set text(font: _fonts.heading2, size: 16pt, weight: "bold") + show heading.where(level: 3): set text(font: _fonts.heading3, size: 16pt, weight: "bold") + show heading: set par(leading: s.leading) + show heading: it => { + block(width: 100%, inset: (left: 2em))[#it] + v(s.heading-v, weak: true) + } + show heading: set text(hyphenate: false) + + // ── Tables / 表格 ── + tbl.apply(table-theme, align-h: table-align) + + // ── Code blocks / 代码块 ── + show raw.where(block: true): it => { + let indent = if title-indent { 32pt } else { 0pt } + block(inset: (left: indent))[#it] + } + + // ── Lists / 列表 ── + show list: it => { + let indent = if title-indent { 32pt } else { 0pt } + block(inset: (left: indent))[#it] + } + show enum: it => { + let indent = if title-indent { 32pt } else { 0pt } + block(inset: (left: indent))[#it] + } + + // Context isolation / 上下文隔离 + { _show-red-header-state.update(show-red-header); body } +} + +// ══════ Document Elements · 公文要素 ══════ +// Elements use function-scoped set rules to return content directly. +// Typst set rules inside a function automatically apply to the returned content without leaking. +// 元素使用函数作用域 set 规则直接返回 content,不会泄漏到外部。 + +/// Document title: 22pt DaBiaoSong, centered. +/// 公文大标题:二号 (22pt) 大标宋,居中。 +#let gongwentitle(body) = { + set text(font: _fonts.title, size: 22pt) + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true), justify: false) + align(center, body) +} + +/// Document subtitle: 16pt FangSong, centered. +/// 公文副标题:三号 (16pt) 仿宋,居中。 +#let gongwensubtitle(body) = { + set text(font: _fonts.body, size: 16pt) + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true), justify: false) + align(center, body) +} + +/// Red-header: organ name, document number, optional signatory, optional red separator line. +/// 红头:发文机关、发文字号、签发人(可选)及红色分隔线(可选)。 +#let make-header( + organ: "", + number: "", + signatory: "", + redline: false, +) = { + context { + if _show-red-header-state.get() { + block(breakable: false)[ + // Organ name: DaBiaoSong, red, centered / 发文机关:大标宋,红色,居中 + #set text(font: _fonts.title, size: 22pt, fill: _colors.red) + #set par(first-line-indent: (amount: 0em, all: true), justify: false) + #align(center, organ) + #v(0.5cm) + + // Document number + signatory: left-right distribution / 发文字号 + 签发人:左右分布 + #set text(font: _fonts.number, size: 12pt, fill: _colors.black) + #grid( + columns: (1fr, 1fr), + gutter: 2em, + number, + if signatory != "" { align(right, signatory) } else { [] }, + ) + + // Red separator line / 红色分隔线 + #if redline { + v(0.3cm) + line(length: 100%, stroke: (paint: _colors.red, thickness: 1.5pt)) + v(0.3cm) + } + ] + } + } +} + +/// Main receiver: flush left, no indent. +/// 主送机关:取消首行缩进,顶格书写。 +#let main-receiver(body) = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true)) + body +} + +/// Single attachment with "附件:" prefix, 2-char indent. +/// 单附件(带「附件:」前缀,首行缩进 2 字符)。 +#let attachment(body) = { + [附件:#body] +} + +/// Attachment without prefix, block-indented by 5 chars for numbered items. +/// Uses block(inset) so ALL lines stay aligned at the 5-char position (not just first line). +/// 单附件(不带「附件:」前缀,整段缩进 5 字符,用于带序号条目)。 +#let attachment-no-hz(body) = { + set par(first-line-indent: (amount: 0em, all: true)) + block(inset: (left: 5em))[#body] +} + +/// Attachment item with custom block indent. +/// 多附件条目,可自定义整段缩进量。 +#let attachment-item(body, indent: 4.9em) = { + set par(first-line-indent: (amount: 0em, all: true)) + block(inset: (left: indent))[#body] +} + +/// Signature block: right-aligned, 2-char right margin, preceded by 3 blank lines. +/// 署名:右对齐,右侧空 2 字符,前空 3 行。 +#let signature(name) = { + v(3em, weak: true) + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true), justify: false) + align(right, box(inset: (right: 2em), name)) +} + +/// Date block: right-aligned, 2-char right margin. +/// 日期:右对齐,右侧空 2 字符。 +#let signdate(date) = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true), justify: false) + align(right, box(inset: (right: 2em), date)) +} + +/// Notes: wrapped in parentheses, no indent. +/// 附注:加圆括号,无缩进。 +#let notes(body) = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true)) + [(#body)] +} + +/// CC field with "抄送:" prefix, no indent. +/// 抄送:「抄送:」前缀,无缩进。 +#let copyto(body) = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true)) + [抄送:#body] +} + +/// Issuing organ and date: same line, organ left, date right. +/// 印发机关与印发日期:同一行,左侧机关、右侧日期。 +#let issueinfo(organ: "", date: "") = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true), justify: false) + grid( + columns: (1fr, 1fr), + organ, + align(right, date), + ) +} + +/// Black separator line for end-matter (版记). +/// 黑色分隔线(版记使用)。 +#let seprule = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true)) + line(length: 100%, stroke: 0.5pt) +} + +/// End-matter block: pinned to page bottom with separator lines. +/// 版记块:贴底定位,抄送+印发机关+日期,上下黑色分隔线。 +/// +/// - `pb` (bool): Force page break before end-matter. Default `false`. +/// 版记前是否强制换页,默认 `false`。 +/// - `copyto-text` (content): Content after "抄送:" prefix. +/// - `organ` (content): Issuing organ name. +/// - `date` (content): Issuing date. +#let endmatter(pb: false, copyto-text: "", organ: "", date: "") = { + if pb { pagebreak() } + place(bottom, dy: 10mm, block(breakable: false, width: 100%, [ + #seprule + #if copyto-text != "" { copyto[#copyto-text] } + #issueinfo(organ: organ, date: date) + #seprule + ])) +} + +/// Red separator line for red-header. +/// 红色分隔线(红头使用)。 +#let redrule = { + set par(leading: 0.75em, first-line-indent: (amount: 0em, all: true)) + line(length: 100%, stroke: (paint: _colors.red, thickness: 1.5pt)) +} diff --git a/packages/preview/gbt9704-gongwen/0.2.0/src/spacing.typ b/packages/preview/gbt9704-gongwen/0.2.0/src/spacing.typ new file mode 100644 index 0000000000..5c9c6a5c50 --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/src/spacing.typ @@ -0,0 +1,17 @@ +// spacing.typ — 行间距主题解析 +// 输入主题名 → 输出 (leading, para-spacing, heading-v) config dict + +/// Resolve a spacing theme name to concrete values. +/// 解析间距主题名,返回配置字典。 +/// +/// - `theme` (str): `"compact"` | `"normal"` | `"relaxed"` +/// - returns: `(leading: length, para-spacing: length, heading-v: length)` +#let resolve(theme) = { + if theme == "compact" { + (leading: 0.75em, para-spacing: 0.9em, heading-v: 1em) + } else if theme == "relaxed" { + (leading: 0.75em, para-spacing: 0.9em, heading-v: 2%) + } else { // normal (default) + (leading: 0.75em, para-spacing: 0.9em, heading-v: 1em) + } +} diff --git a/packages/preview/gbt9704-gongwen/0.2.0/src/table.typ b/packages/preview/gbt9704-gongwen/0.2.0/src/table.typ new file mode 100644 index 0000000000..f63bd0f3b4 --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/src/table.typ @@ -0,0 +1,119 @@ +// table.typ — 表格主题样式 +// 提供两个接口: +// apply(theme, align-h) — 全局 show/set 规则 +// wrap(theme, body, align-h) — 单表包裹 +// +// align-h: "center" (default) | "left" | "right" + +// ── Theme data ── + +#let _themes = ( + plain: ( + stroke: none, + inset: (x: 5pt, y: 4pt), + rule-bottom: none, + align: auto, + fill: none, + ), + "full-grid": ( + stroke: 0.5pt, + inset: (x: 5pt, y: 4pt), + rule-bottom: none, + align: auto, + fill: none, + ), + "single-line": ( + stroke: (_, y) => if y == 0 { + (bottom: 1pt, top: none, left: none, right: none) + } else { + none + }, + inset: (x: 5pt, y: 4pt), + rule-bottom: none, + fill: (_, y) => if calc.odd(y) { gray.lighten(90%) }, + align: (x, y) => + if x == 0 { right } else { left } + + if y == 0 { bottom } else { top }, + ), + "three-line": ( + stroke: (x, y) => { + if y == 0 { + (top: 1.5pt, bottom: 0.5pt, left: none, right: none) + } else { + (top: none, bottom: none, left: none, right: none) + } + }, + inset: (x: 5pt, y: 4pt), + rule-bottom: 1.5pt, + align: auto, + fill: none, + ), +) + +// ── Helpers ── + +// Wrap body with horizontal alignment + optional bottom rule. +// align-h: left | center | right +// block(width: 100%) neutralizes paragraph first-line-indent. +#let _align-wrap(body, align-h, rule-bottom) = { + let inner = if rule-bottom != none { + block(width: auto, stroke: (bottom: rule-bottom), inset: 0pt, body) + } else { + block(width: auto, inset: 0pt, body) + } + + if align-h == left { + block(width: 100%, inset: 0pt, inner) + } else if align-h == right { + block(width: 100%, inset: 0pt, + align(right, inner) + ) + } else { // center (default) + block(width: 100%, inset: 0pt, + align(center, inner) + ) + } +} + +// ── Public API ── + +/// Apply a table theme globally via show/set rules. +/// - `theme` (str): `"full-grid"` (default) | `"three-line"` | `"single-line"` | `"plain"` +/// - `align-h` (str): `"center"` (default) | `"left"` | `"right"` +#let apply(theme, align-h: center) = { + let t = _themes.at(theme, default: _themes.at("full-grid")) + + show table: set par(first-line-indent: (amount: 0em, all: true)) + show table.cell.where(y: 0): set text(stroke: 0.6pt + black) + + show table: set table( + stroke: t.stroke, + inset: t.inset, + fill: t.fill, + align: t.align, + ) + + show table: it => _align-wrap(it, align-h, t.rule-bottom) +} + +/// Wrap a single table with theme-specific styling. +/// - `theme` (str): `"full-grid"` | `"three-line"` | `"single-line"` | `"plain"` +/// - `body` (content): content block containing `#table(...)` +/// - `align-h` (str): `"center"` (default) | `"left"` | `"right"` +#let wrap(theme, body, align-h: center) = { + let t = _themes.at(theme, default: _themes.at("full-grid")) + + { + set par(first-line-indent: (amount: 0em, all: true)) + show table.cell.where(y: 0): set text(stroke: 0.6pt + black) + + set table( + stroke: t.stroke, + inset: t.inset, + fill: t.fill, + align: t.align, + ) + + _align-wrap(body, align-h, t.rule-bottom) + } +} diff --git a/packages/preview/gbt9704-gongwen/0.2.0/template/main.typ b/packages/preview/gbt9704-gongwen/0.2.0/template/main.typ new file mode 100644 index 0000000000..19e7391de3 --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/template/main.typ @@ -0,0 +1,16 @@ +// gbt9704-gongwen — 适配 GB/T 9704-2012 · 党政机关公文格式 +// Quick start: typst init @preview/gbt9704-gongwen +// 快速开始:typst init @preview/gbt9704-gongwen + +#import "@preview/gbt9704-gongwen:0.2.0": * + +#show: gbt9704.with( + redline: true, + title-indent: true, + // spacing-theme: "normal", // "compact" | "normal" | "relaxed" + // table-theme: "full-grid", // "full-grid" | "three-line" | "single-line" | "plain" + // table-align: "center", // "center" | "left" | "right" +) + +// Write your document content here ... +// 在此撰写公文内容 ... diff --git a/packages/preview/gbt9704-gongwen/0.2.0/thumbnail.png b/packages/preview/gbt9704-gongwen/0.2.0/thumbnail.png new file mode 100644 index 0000000000..21283b6219 Binary files /dev/null and b/packages/preview/gbt9704-gongwen/0.2.0/thumbnail.png differ diff --git a/packages/preview/gbt9704-gongwen/0.2.0/typst.toml b/packages/preview/gbt9704-gongwen/0.2.0/typst.toml new file mode 100644 index 0000000000..3ee774075e --- /dev/null +++ b/packages/preview/gbt9704-gongwen/0.2.0/typst.toml @@ -0,0 +1,17 @@ +[package] +name = "gbt9704-gongwen" +version = "0.2.0" +entrypoint = "src/lib.typ" +authors = ["songwupei"] +license = "MIT" +description = "适配 GB/T 9704-2012 公文格式模板 / adapts Chinese GB/T 9704-2012 document format template" +repository = "https://codeberg.org/songwupei/typst-gbt9704" +keywords = ["government", "document", "chinese", "cjk", "official", "gb"] +categories = ["report"] +compiler = "0.13.1" +exclude = ["*.pdf", ".gitignore", "CLAUDE.md", "ds.md", ".claude/", ".omc/"] + +[template] +path = "template" +entrypoint = "main.typ" +thumbnail = "thumbnail.png"