Add Markdown-driven feasibility report with Typst styling pipeline.

Introduce cmarker/codly-based MD workflow, brutalist cover layout, and shared brand tokens so colleagues can author reports in report.md while the template handles PDF output.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MORRO 2026-06-23 19:29:48 +08:00
parent 350d101e0f
commit b8eed931ac
19 changed files with 6809 additions and 179 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Typst 编译产物(示例 PDF 除外) # Typst 编译产物(示例 PDF 除外)
*.pdf *.pdf
!resume/sample.pdf !resume/sample.pdf
!feasibility/sample.pdf
# IDE / 编辑器本地配置 # IDE / 编辑器本地配置
.cursor/ .cursor/

View File

@ -5,7 +5,7 @@ GreLin 内部 Typst 模板库,按文档类型分目录维护。目录结构如
| 文件夹 | 说明 | 入口文件 | | 文件夹 | 说明 | 入口文件 |
|--------|------|----------| |--------|------|----------|
| [`resume/`](resume/) | 团队简历 | `cv.typ` | | [`resume/`](resume/) | 团队简历 | `cv.typ` |
| [`feasibility/`](feasibility/) | 可行性研究报告 | `report.typ` | | [`feasibility/`](feasibility/) | 可行性研究报告 | `report.typ`(正文编辑 `feasibility/content/report.md` |
## 环境要求 ## 环境要求
@ -14,8 +14,9 @@ GreLin 内部 Typst 模板库,按文档类型分目录维护。目录结构如
## 快速开始 ## 快速开始
1. 用 VS Code / Cursor 打开本仓库,打开要编辑的入口文件,例如 `resume/cv.typ` 1. 用 VS Code / Cursor 打开本仓库,打开要编辑的入口文件,例如 `resume/cv.typ``feasibility/report.typ`
2. 看效果(预览):`Ctrl+Shift+P` → 输入 `Typst Preview` → 回车;侧边会实时显示排版效果 2. 可行性报告正文在 `feasibility/content/report.md` 用 Markdown 编写,无需改 Typst 代码
3. 导出 PDF`Ctrl+Shift+P` → 输入 `Typst Export PDF` → 回车PDF 默认生成在同目录(如 `resume/sample.pdf` 3. 看效果(预览):`Ctrl+Shift+P` → 输入 `Typst Preview` → 回车;侧边会实时显示排版效果
4. 导出 PDF`Ctrl+Shift+P` → 输入 `Typst Export PDF` → 回车PDF 默认生成在同目录(如 `resume/sample.pdf`
> 不熟悉 Typst可参考社区维护的 [小蓝书Typst 中文教程)](https://tutorial.typst.dev/);英文版见 [官方入门教程](https://typst.app/docs/tutorial/)。 > 不熟悉 Typst可参考社区维护的 [小蓝书Typst 中文教程)](https://tutorial.typst.dev/);英文版见 [官方入门教程](https://typst.app/docs/tutorial/)。

View File

@ -7,7 +7,8 @@
#let color-bar-bg = rgb("#EFF0F2") #let color-bar-bg = rgb("#EFF0F2")
#let color-page = rgb("#FDFDFD") #let color-page = rgb("#FDFDFD")
// 强调绿同色系lime → mid品牌主色→ olive // 品牌主色(全项目绿色统一)
#let color-lime = rgb("#D7F754") #let color-brand = rgb(200, 216, 98)
#let color-lime-mid = rgb("#C4D94A") // 浅底装饰、技能条、章节强调、图标 #let color-lime = color-brand.lighten(10%)
#let color-lime-deep = rgb("#A7C957") // 浅底标题文字 #let color-lime-mid = color-brand
#let color-lime-deep = color-brand.darken(22%)

View File

@ -1,32 +1,74 @@
# Feasibility Report · 可行性研究报告模板 # Feasibility Report · 可行性研究报告模板
GreLin 可行性报告 Typst 模板,适用于项目论证、内部评审或 BP 附件 GreLin 可行性报告 Typst 模板,面向矿石分选项目论证与客户交付。同事用 **Markdown** 写正文,模板负责封面、页眉页脚与图表排版
## 快速开始 ## 快速开始
1. 编辑 [`content/report.md`](content/report.md)(或从 [`content/report.template.md`](content/report.template.md) 复制)
2. 用 VS Code / Cursor 打开 [`report.typ`](report.typ)
3. `Ctrl+Shift+P`**Typst Preview** 预览,或 **Typst Export PDF** 导出
仓库根目录编译(共享 `brand/colors.typ`
```bash ```bash
cd feasibility-report typst compile feasibility/report.typ feasibility/sample.pdf --root .
typst compile report.typ
typst watch report.typ
``` ```
## 定制 示例输出:[`sample.pdf`](sample.pdf)
## 工作流
| 文件 | 说明 | | 文件 | 说明 |
|------|------| |------|------|
| [`data/config.typ`](data/config.typ) | 公司名、项目名、版本、日期、各章节正文 | | [`content/report.md`](content/report.md) | **主编辑入口**YAML 元数据 + 正文 |
| [`data/theme.typ`](data/theme.typ) | 配色与字号 | | [`content/assets/`](content/assets/) | 图片资源(支持 `.jpg` / `.png` / `.svg` |
| [`data/layout.typ`](data/layout.typ) | 封面、摘要、章节组件 | | [`data/config.typ`](data/config.typ) | 默认公司名、副标题等YAML 未写时使用) |
| [`data/theme.typ`](data/theme.typ) | 设计 token字号、间距、表格 |
| [`data/styling.typ`](data/styling.typ) | 标题 / 表格 / 图片 `#show` 规范 |
| [`data/layout.typ`](data/layout.typ) | 封面、摘要、页眉组件 |
| [`report.typ`](report.typ) | 编译入口(一般无需改动) |
## 章节结构 ## YAML 元数据
1. 项目概述 `report.md` 顶部填写:
2. 建设必要性与市场分析
3. 技术方案
4. 实施计划
5. 投资估算与资金用途
6. 效益分析
7. 风险分析与对策
8. 结论
`data/config.typ``sections` 数组中逐章填写内容即可。 ```yaml
---
title: 某某矿山智能分选可行性研究
brand: GreLin
company: 西安高岭绿能科技有限公司
date: 2026-06-22
version: v1.0
summary: |
摘要正文……
---
```
## Markdown 写法
- 章节:`# 项目概述`(自动编号为 1. / 1.1 / 1.1.1,无需手写「一、二、三」)
- 表格:标准 GFM 表格语法
- 图片:`![说明](assets/photo.jpg)` 或 `assets/chart.png`,文件放在 `content/assets/`
- 行内代码:`` `ONNX Runtime` ``(灰底、小圆角、品牌色文字)
- 代码块:` ```python ` 围栏 + 语言名,由 [codly](https://typst.app/universe/package/codly) 自动语法高亮并显示语言标签
- 下划线:`<u>文字</u>`(品牌色,线下移 5pt
- Emoji正文可直接写 🐱 等符号(需系统字体支持)
- 列表、加粗等均支持(基于 [cmarker](https://typst.app/universe/package/cmarker)
## 依赖包
| 包 | 用途 |
|----|------|
| [cmarker](https://typst.app/universe/package/cmarker) | Markdown → Typst 正文 |
| [codly](https://typst.app/universe/package/codly) + [codly-languages](https://typst.app/universe/package/codly-languages) | 代码块高亮与语言标签 |
可选扩展:[toutu](https://typst.app/universe/package/toutu)(目录)、[hydra](https://typst.app/universe/package/hydra)(页眉章节名)、[cetz](https://typst.app/universe/package/cetz)(矢量图表)。
## 环境要求
- **Typst ≥ 0.14.0**;请使用较新版本的 Tinymist 或 Typst CLI
- 首次编译会自动下载 `cmarker`、`codly`、`codly-languages`(需网络)
## 替换外部 MD
将同事发来的 `.md` 覆盖 `content/report.md`,图片放入 `content/assets/` 并修正文内路径,重新预览即可。

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

View File

@ -0,0 +1,89 @@
---
title: 铝土矿可行性研究报告
company: 西安高岭绿能科技有限公司
date: 2026-06-22
version: v1.0
summary: |
本项目拟在目标矿山建设智能矿石分选产线,以高光谱感知、深度学习推理与喷阀执行闭环为核心,实现铝土矿预选抛废与品位提升。经现场调研与算法验证,单线设计处理量 25 t/h精矿 A/S 可达 4.0+,具备工程化落地条件。
---
# 项目概述
本项目面向铝土矿预选场景,建设「感知—推理—执行—运维」一体化的智能分选系统,将算法能力转化为可部署、可运维的工业产品,降低后续磨选成本并提升资源利用率。
> 引用块示例:政策与市场需求推动预选抛废技术应用,本段为 blockquote 样式预览。
**常用 Markdown 语法预览:**
- **加粗**:单线处理量 `25 t/h`
- *斜体*:用于强调术语
- ~~删除线~~:已废弃方案
- <u>下划线</u>:关键指标标注
- Emoji产线状态 🐱 正常 / ⚠️ 告警
- 行内代码:`ONNX Runtime`、`Ratatui`
```python
# 推理回调示例
for sample in belt_stream:
grade = model.infer(sample)
valve.fire(grade)
```
## 项目概述2
建设内容包括:产线感知与推理控制软件、运营后台、运维工具及现场联调交付。
# 建设必要性与市场分析
铝土矿原料品位波动大,传统全量入磨能耗高、尾矿处置压力大。政策层面,绿色矿山与节能降碳要求推动预选抛废技术应用。目标客户在降本增效与环保合规方面需求明确,智能分选在预选环节具备可复制推广价值。
# 技术方案
系统采用 <!--raw-typst #gls-long("hsi") --><!--raw-typst #gls-short("onnx") --> 推理引擎,构建边缘推理与喷阀执行闭环架构。
<!--raw-typst #ore-sorting-flow()-->
![分选系统现场照片](assets/img.jpg)
核心技术路线:
- 高光谱采集与样本标准化处理
- ONNX 推理服务与产线实时回调
- Rust 运维控制台与产线状态监控
## 验证指标(实验室 / 现场)
| 指标 | 设计值 | 验证情况 |
| ---- | ------ | -------- |
| 单线处理量 | 25 t/h | 已验证 |
| 精矿 <!--raw-typst #gls-short("as-ratio") --> | ≥ 4.0 | 4.0+ |
| 尾矿 A/S | ≤ 1.7 | 1.7- |
| 回收率 | ≥ 30% | 30%+ |
# 实施计划
1. **研发验证**12 月):算法迭代、软件模块开发与台架测试。
2. **试点部署**23 月):单线现场安装、联调与试运行。
3. **规模化推广**(持续):复制部署、运维体系与客户培训。
# 投资估算与资金用途
投资主要用于光谱传感与执行机构硬件、软件开发与现场实施、人员与运维体系建设。具体金额结合矿山规模与产线配置在商务阶段细化。
# 效益分析
- **经济效益**:减少入磨量,降低能耗与药剂成本;提升精矿品位与回收率。
- **环保效益**:减少尾矿产生量,降低固废处置压力。
- **社会效益**:支撑绿色矿山建设,提升企业技术形象。
# 风险分析与对策
| 风险 | 对策 |
| ---- | ---- |
| 原料波动影响模型稳定性 | 持续采样标注与模型迭代;异常降级策略 |
| 现场交付周期 | 标准化模块与运维工具;阶段里程碑管控 |
| 客户接受度 | 试点数据透明展示;可量化的效益测算 |
# 结论
综合技术验证、市场需求与实施条件,本项目具备可行性。建议推进试点产线建设,以现场数据支撑后续规模化复制。

38
feasibility/data/code.typ Normal file
View File

@ -0,0 +1,38 @@
#import "@preview/codly:1.3.0": *
#import "@preview/codly-languages:0.1.10": codly-languages
#import "theme.typ": *
#let codly-init-rule = codly-init.with()
#let codly-config = codly(
languages: codly-languages,
fill: code-block-fill,
radius: code-block-radius,
stroke: none,
zebra-fill: none,
number-format: none,
display-icon: false,
display-name: true,
default-color: color-body,
lang-stroke: (_) => none,
lang-fill: (_) => code-block-fill,
lang-radius: 0pt,
)
/// 与 codly 配合的语法高亮色(须在 codly-init 之后注册)
#let code-token-highlight-init(doc) = {
show raw.where(block: true): it => {
show "keyword": set text(fill: code-token-keyword)
show "comment": set text(fill: code-token-comment)
show "string": set text(fill: code-token-string)
show "number": set text(fill: code-token-number)
show "builtin": set text(fill: code-token-function)
show "function": set text(fill: code-token-function)
show "decor": set text(fill: code-token-function)
show "operator": set text(fill: code-token-operator)
show "punctuation": set text(fill: code-token-operator)
set text(fill: color-body)
it
}
doc
}

View File

@ -1,64 +1,11 @@
// ============================================================================= // =============================================================================
// 可行性报告 — 修改此处定制内容 // 可行性报告 — 默认值(正文在 content/report.mdYAML 可覆盖下列字段)
// ============================================================================= // =============================================================================
#let company-name = "西安高岭绿能科技有限公司" #let default-brand = "GreLin"
#let project-name = "智能矿石分选系统建设项目" #let default-company = "西安高岭绿能科技有限公司"
#let report-version = "v0.1" #let default-title = "智能矿石分选系统建设项目"
#let report-date = "2026 年 6 月" #let default-version = "v1.0"
#let default-date = "2026 年 6 月"
#let report-summary = [ #let content-path = "content/report.md"
本项目旨在建设面向铝土矿等矿石场景的智能分选软硬件一体化系统,将分选算法工程化为可部署、可运维的产线产品,形成从算法接入、设备控制、运营后台到监控运维的完整闭环,支撑绿色高效矿物加工。
]
#let sections = (
(
title: "一、项目概述",
body: [
说明项目背景、建设目标、建设内容与预期成果。可引用 BP 中的产品定位与目标客户。
],
),
(
title: "二、建设必要性与市场分析",
body: [
行业痛点、政策与环保驱动、目标市场规模与竞争格局。建议配合 BP 市场章节数据。
],
),
(
title: "三、技术方案",
body: [
系统架构(感知、推理、控制、后台、运维)、核心技术路线、与现有产线对接方式。
可补充已验证指标:精矿 A/S、尾矿 A/S、回收率、处理量等。
],
),
(
title: "四、实施计划",
body: [
阶段划分(研发验证 → 试点部署 → 规模化推广)、里程碑、团队分工与关键依赖。
],
),
(
title: "五、投资估算与资金用途",
body: [
硬件、软件、人员、市场等费用结构。与 BP 财务预测保持一致。
],
),
(
title: "六、效益分析",
body: [
经济效益(降本增效、回收率提升)、社会效益与环保效益。尽量量化。
],
),
(
title: "七、风险分析与对策",
body: [
技术、市场、交付、政策等风险及应对措施。
],
),
(
title: "八、结论",
body: [
综合可行性判断与建议。
],
),
)

View File

@ -0,0 +1,51 @@
#import "theme.typ": *
/// 点阵底纹
#let tech-dots(width: 100%, height: 100%, step: 10pt) = box(width: width, height: height, clip: true)[
#for x in range(40) {
for y in range(40) {
place(
left + top,
dx: x * step,
dy: y * step,
circle(radius: 0.35pt, fill: color-grid),
)
}
}
]
/// 条形码条
#let tech-barcode(width, height, bars: 28) = box(width: width, height: height, clip: true)[
#for i in range(bars) {
let w = if calc.rem(i, 4) == 0 { 2.2pt } else if calc.rem(i, 2) == 0 { 1.4pt } else { 0.8pt }
place(
left + top,
dx: i * (width / bars),
box(width: w, height: 100%, fill: color-ink),
)
}
]
/// 等宽标签
#let tech-label(body, fill: color-ink, size: 7pt) = text(
font: mono-font,
size: size,
fill: fill,
tracking: 0.14em,
)[#body]
/// 章节编号
#let section-tech-id(..nums) = {
let n = nums.pos()
let id = if n.len() == 1 {
let num = n.first()
if num < 10 { "0" + str(num) } else { str(num) }
} else if n.len() == 2 {
str(n.at(0)) + "." + str(n.at(1))
} else {
str(n.at(0)) + "." + str(n.at(1)) + "." + str(n.at(2))
}
box(fill: color-neon, inset: (x: 5pt, y: 2pt))[
#text(font: mono-font, size: 7pt, weight: "bold", fill: color-ink)[#id]
]
}

View File

@ -0,0 +1,33 @@
#import "@preview/cetz:0.3.4"
#import "theme.typ": *
#let ore-sorting-flow() = figure(
caption: [智能分选系统流程CeTZ 矢量示意)],
cetz.canvas(length: 0.85cm, {
import cetz.draw: *
let w = 1.7
let gap = 0.55
let labels = ("采集", "推理", "分选", "产出")
for (i, label) in labels.enumerate() {
let x = i * (w + gap)
rect(
(x, 0),
(x + w, 1),
radius: 0.12,
fill: color-brand.lighten(72%),
stroke: 1pt + color-brand,
)
content((x + w / 2, 0.5), align(center + horizon)[
#text(font: body-font, size: 8pt, fill: color-body)[#label]
])
if i < labels.len() - 1 {
line(
(x + w, 0.5),
(x + w + gap, 0.5),
stroke: 1pt + color-muted,
mark: (end: ">", fill: color-muted),
)
}
}
}),
)

View File

@ -0,0 +1,43 @@
#import "@preview/glossarium:0.5.10": make-glossary, register-glossary, print-glossary, gls-long, gls-short
#import "theme.typ": *
#let glossary-entries = (
(
key: "hsi",
short: "HSI",
long: "高光谱成像",
description: "Hyperspectral Imaging获取矿石连续波段光谱信息。",
),
(
key: "onnx",
short: "ONNX",
long: "开放神经网络交换格式",
description: "Open Neural Network Exchange用于跨平台模型推理部署。",
),
(
key: "as-ratio",
short: "A/S",
long: "铝硅比",
description: "精矿品质评价的关键指标之一。",
),
)
#let init-glossary(doc) = {
show: make-glossary
doc
}
#let register-report-glossary() = {
register-glossary(glossary-entries)
}
#let print-glossary-block() = {
pagebreak(weak: true)
block(width: 100%, above: space-chapter)[
#box(width: 100%, fill: color-neon, inset: (x: 10pt, y: 6pt))[
#text(font: heading-font, size: size-h1, weight: "black", fill: color-ink)[术语与缩写]
]
#v(space-block)
#print-glossary(glossary-entries, show-all: true)
]
}

View File

@ -1,43 +1,148 @@
#import "theme.typ": * #import "theme.typ": *
#import "config.typ": * #import "logo.typ": grelin-logo, cover-index-badge
#import "decor.typ": tech-dots, tech-barcode, tech-label
#let report-title-block() = block(width: 100%, below: space-section)[ #let format-cover-date(date) = {
#align(center)[ let parts = date.split("-")
#text(font: heading-font, size: size-title, weight: "bold", fill: color-ink)[ if parts.len() == 3 {
#project-name parts.at(0) + "." + parts.at(1) + "." + parts.at(2)
] } else {
#v(6pt) date
#text(font: heading-font, size: size-h2, fill: color-muted)[可行性研究报告] }
#v(10pt) }
#text(size: size-meta, fill: color-muted)[
#company-name #sym.bar #report-version #sym.bar #report-date #let cover-meta-strip(meta) = box(
width: 100%,
fill: color-ink,
inset: (x: 12pt, y: 8pt),
)[
#grid(
columns: (1fr, auto, 1fr, auto, 1fr),
column-gutter: 12pt,
align: horizon,
tech-label("VERSION", fill: color-neon, size: 6.5pt),
text(font: mono-font, size: 8pt, fill: white)[#meta.version],
tech-label("DATE", fill: color-neon, size: 6.5pt),
text(font: mono-font, size: 8pt, fill: white)[#format-cover-date(meta.date)],
align(right)[
tech-label("STATUS · DRAFT", fill: color-neon, size: 6.5pt)
],
)
]
#let cover-block(meta) = {
pagebreak(weak: true)
block(width: 100%, below: space-chapter)[
#box(width: 100%, height: cover-block-height, fill: white)[
#place(top + left)[#grelin-logo()]
#place(top + right)[#cover-index-badge("01")]
#place(left + top, dy: 52pt)[#tech-barcode(14pt, 120pt, bars: 5)]
#place(right + top, dy: 72pt)[
#rotate(-90deg, reflow: true)[
#tech-label("FEASIBILITY REPORT", fill: color-caption, size: 6.5pt)
]
]
#place(left + top, dx: 28pt, dy: 100pt)[
#box(width: 88%, height: 130pt, clip: true)[#tech-dots(width: 100%, height: 100%, step: 11pt)]
]
#place(left + top, dx: 24pt, dy: 168pt)[
#box(width: 92%, fill: color-neon, inset: (x: 16pt, y: 18pt))[
#text(
font: heading-font,
size: size-cover-title,
weight: "black",
fill: color-ink,
hyphenate: false,
)[#meta.title]
]
]
#place(left + bottom, dy: -36pt)[
#box(width: 100%)[#cover-meta-strip(meta)]
]
#place(left + bottom, dy: -8pt)[
#tech-barcode(100%, 10pt, bars: 48)
]
] ]
] ]
#v(space-section) pagebreak()
#line(length: 100%, stroke: color-divider + 0.6pt) }
]
#let section-block(section) = block(width: 100%, above: space-section, below: space-block)[ #let accent-line() = box(width: 100%, height: 2pt, fill: color-ink)
#text(font: heading-font, size: size-h1, weight: "bold", fill: color-ink)[#section.title]
#v(6pt)
#box(width: 18pt, height: 3pt, fill: color-accent)
#v(8pt)
#set par(leading: leading-body, spacing: 6pt, justify: true)
#set text(size: size-body, fill: color-body)
#section.body
]
#let summary-block() = block(width: 100%, above: space-block, below: space-section)[ #let summary-block(meta) = {
#text(font: heading-font, size: size-h2, weight: "bold", fill: color-ink)[摘要] if meta.summary != "" {
block(width: 100%, above: space-block, below: space-chapter)[
#box(
width: 100%,
fill: white,
stroke: 1.5pt + color-ink,
inset: summary-inset,
)[
#box(width: 100%, fill: color-neon, inset: (x: 10pt, y: 5pt))[
#tech-label("ABSTRACT", fill: color-ink, size: 7pt)
]
#v(10pt)
#text(font: heading-font, size: size-h2, weight: "black", fill: color-ink)[摘要]
#v(space-block)
#set par(leading: leading-summary, justify: true)
#set text(size: size-body, fill: color-body)
#meta.summary
#v(space-section)
#grid(
columns: (auto, 1fr, auto, 1fr),
column-gutter: 8pt,
align: horizon,
tech-label("DATE", size: 6.5pt),
text(font: mono-font, size: size-meta, fill: color-body)[#meta.date],
tech-label("ORG", size: 6.5pt),
text(font: mono-font, size: size-meta, fill: color-body)[#meta.company],
)
]
]
}
}
#let page-footer() = context {
grid(
columns: (auto, 1fr, auto),
align: horizon,
tech-barcode(36pt, 8pt, bars: 10),
box(width: 100%, height: 1pt, fill: color-ink),
box(fill: color-ink, inset: (x: 8pt, y: 3pt))[
#text(font: mono-font, size: 8pt, fill: color-neon)[
#counter(page).display("1")
#h(0.12em)
#sym.slash
#h(0.12em)
#counter(page).final().first()
]
],
)
}
#let page-accent-bar() = place(
left + top,
tech-barcode(page-accent-bar-width, 100%, bars: 4),
)
#let page-header(meta) = [
#grid(
columns: (auto, 1fr, auto),
column-gutter: 8pt,
align: horizon,
box(fill: color-neon, inset: (x: 6pt, y: 2pt))[
#text(font: mono-font, size: 7pt, weight: "bold", fill: color-ink)[#upper(meta.brand)]
],
box(width: 100%, height: 1pt, fill: color-ink),
align(right)[
#text(font: mono-font, size: 7pt, fill: color-caption)[#meta.title]
],
)
#v(6pt) #v(6pt)
#box( #accent-line()
width: 100%,
fill: color-bar-bg,
inset: 10pt,
radius: 4pt,
)[
#set par(leading: leading-body, justify: true)
#set text(size: size-body, fill: color-body)
#report-summary
]
] ]

37
feasibility/data/logo.typ Normal file
View File

@ -0,0 +1,37 @@
#import "theme.typ": *
#import "decor.typ": tech-label
/// Logo霓虹绿块 + 白色折线图形 + GreLin | Tech
#let grelin-logo(
primary: "GreLin",
secondary: "Tech",
) = grid(
columns: (auto, auto),
column-gutter: 10pt,
align: horizon,
box(width: 34pt, height: 34pt, fill: color-neon)[
#place(left + top, dx: 7pt, dy: 8pt, box(width: 14pt, height: 3pt, fill: white))
#place(left + top, dx: 7pt, dy: 14pt, box(width: 3pt, height: 14pt, fill: white))
#place(left + top, dx: 18pt, dy: 14pt, box(width: 9pt, height: 3pt, fill: white))
#place(left + top, dx: 18pt, dy: 17pt, box(width: 3pt, height: 11pt, fill: white))
],
grid(
columns: (auto, auto, auto),
column-gutter: 7pt,
align: horizon,
text(font: heading-font, size: 12pt, weight: "black", fill: color-ink, tracking: 0.04em)[#primary],
box(width: 1.2pt, height: 1em, fill: color-ink),
text(font: heading-font, size: 12pt, weight: "black", fill: color-ink, tracking: 0.16em)[#secondary],
),
)
#let cover-index-badge(index) = box(
width: 58pt,
height: 58pt,
stroke: 2pt + color-ink,
fill: white,
)[
#align(center + horizon)[
#text(font: mono-font, size: size-cover-index, weight: "black", fill: color-ink)[#index]
]
]

70
feasibility/data/md.typ Normal file
View File

@ -0,0 +1,70 @@
#import "@preview/cmarker:0.1.8": render-with-metadata
#import "config.typ": *
#import "theme.typ": *
#import "styling.typ": underline-accent
#let source-dir(path) = {
let parts = path.split("/")
if parts.len() <= 1 {
""
} else {
parts.slice(0, parts.len() - 1).join("/")
}
}
#let resolve-image(source-path, source, alt: none) = {
let dir = source-dir(source-path)
let full = if dir == "" { source } else { dir + "/" + source }
let path = "../" + full
let img = image(path, width: image-max-width, fit: "contain")
if alt != none and alt != "" {
block(width: 100%)[
#align(center)[#img]
#v(space-micro)
#align(center)[
#text(size: size-caption, fill: color-caption)[#alt]
]
]
} else {
img
}
}
#let meta-string(meta, key, fallback) = {
let value = meta.at(key, default: none)
if value == none {
fallback
} else if type(value) == str {
value
} else {
str(value)
}
}
#let merge-meta(yaml) = (
title: meta-string(yaml, "title", default-title),
brand: meta-string(yaml, "brand", default-brand),
company: meta-string(yaml, "company", default-company),
version: meta-string(yaml, "version", default-version),
date: meta-string(yaml, "date", default-date),
summary: yaml.at("summary", default: ""),
)
#let load-report-md(markdown, source-path: content-path, extra-scope: (:)) = {
let (yaml, body) = render-with-metadata(
markdown,
metadata-block: "frontmatter-yaml",
smart-punctuation: true,
html: (
u: (_, body) => underline-accent(body),
),
scope: (
image: (source, alt: none) => resolve-image(source-path, source, alt: alt),
..extra-scope,
),
)
(
metadata: merge-meta(if yaml == none { (:) } else { yaml }),
body: body,
)
}

View File

@ -0,0 +1,160 @@
#import "theme.typ": *
#import "decor.typ": section-tech-id, tech-label
#let underline-accent(body) = underline(
body,
stroke: underline-stroke,
offset: underline-offset,
evade: true,
)
#let heading-number(it) = {
if it.numbering != none {
counter(heading).display(it.numbering)
h(heading-number-gap)
}
}
#let report-heading-numbering(..nums) = {
let n = nums.pos()
if n.len() == 1 {
numbering("1.", n.first())
} else if n.len() == 2 {
numbering("1.1", n.at(0), n.at(1))
} else {
numbering("1.1.1", ..n)
}
}
#let grelin-h1(it) = block(
width: 100%,
above: space-chapter,
below: space-block,
)[
#grid(
columns: (auto, 1fr),
column-gutter: 8pt,
align: horizon,
context section-tech-id(..counter(heading).get()),
box(width: 100%, fill: color-neon, inset: (x: 8pt, y: 4pt))[
#text(font: heading-font, size: size-h1, weight: "black", fill: color-ink)[
#heading-number(it)
#it.body
]
],
)
]
#let grelin-h2(it) = block(width: 100%, above: space-section, below: space-inline)[
#grid(
columns: (auto, 1fr),
column-gutter: 8pt,
align: horizon,
box(width: 3pt, height: 1em, fill: color-ink),
text(font: heading-font, size: size-h2, weight: "bold", fill: color-ink)[
#heading-number(it)
#it.body
],
)
]
#let grelin-h3(it) = block(width: 100%, above: space-block, below: space-micro)[
#grid(
columns: (auto, 1fr),
column-gutter: 6pt,
align: horizon,
tech-label("///", size: 6.5pt),
text(font: heading-font, size: size-h3, weight: "bold", fill: color-ink)[
#heading-number(it)
#it.body
],
)
]
#let grelin-image(it) = block(
width: 100%,
above: space-figure-before,
below: space-figure-after,
)[
#align(center)[
#box(width: image-max-width, stroke: 1pt + color-ink)[
#set image(width: 100%, fit: "contain")
#it
]
]
]
#let grelin-table(it) = block(
width: 100%,
above: space-figure-before,
below: space-figure-after,
)[
#it
]
#let grelin-figure(it) = block(
width: 100%,
above: space-figure-before,
below: space-figure-after,
)[
#align(center)[
#set image(width: image-max-width, fit: "contain")
#set text(size: size-caption, fill: color-caption)
#it
]
]
#let grelin-blockquote(it) = block(
width: 100%,
above: space-block,
below: space-block,
inset: (left: blockquote-inset),
stroke: (left: blockquote-stroke),
fill: white,
)[
#set par(leading: leading-body, justify: true)
#set text(fill: color-quote)
#it
]
#let grelin-inline-code(it) = box(
fill: code-inline-fill,
inset: code-inline-inset,
stroke: 0.8pt + color-ink,
)[
#set text(font: mono-font, size: size-body, fill: color-code-inline)
#it
]
#let apply-report-styling(body) = {
set heading(numbering: report-heading-numbering)
set text(size: size-table)
set table(
stroke: (x, y) => (
top: if y == 0 { 1.5pt + color-ink } else { none },
bottom: if y == 0 { 0.8pt + color-ink } else { none },
),
inset: table-inset,
align: (col, row) => if col == 0 { left + horizon } else { center + horizon },
fill: (col, row) => {
if row == 0 {
table-header-fill
} else if calc.rem(row, 2) == 0 {
table-zebra-fill
} else {
white
}
},
)
show heading.where(level: 1): grelin-h1
show heading.where(level: 2): grelin-h2
show heading.where(level: 3): grelin-h3
show image: grelin-image
show table.cell: set text(fill: table-data-text)
show table.cell.where(y: 0): set text(weight: "bold", fill: color-ink)
show table: grelin-table
show figure: grelin-figure
show quote.where(block: true): grelin-blockquote
show raw.where(block: false): grelin-inline-code
body
}

View File

@ -1,24 +1,102 @@
#import "../../brand/colors.typ": * #import "../../brand/colors.typ": *
#let color-accent = color-lime-mid // Brutalist / FUI 配色:霓虹绿 · 纯黑 · 冷灰(避免橄榄灰发脏)
#let color-divider = color-muted.lighten(68%) #let color-neon = rgb(210, 255, 72)
#let color-heading = color-neon
#let color-accent = color-neon
#let color-accent-line = color-ink
#let color-divider = rgb(205, 205, 205)
#let color-caption = rgb(110, 110, 110)
#let color-quote = rgb(90, 90, 90)
#let color-code-lang = rgb(90, 90, 90)
#let color-code-inline = color-ink
#let color-header = color-ink
#let color-page = rgb(238, 238, 238)
#let color-bar-bg = rgb(248, 248, 248)
#let color-grid = rgb(220, 220, 220)
#let heading-font = ("Microsoft YaHei", "Segoe UI") // 代码块语法高亮
#let body-font = ("Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI") #let code-token-keyword = color-ink
#let code-token-comment = rgb(130, 130, 130)
#let code-token-string = rgb(40, 40, 40)
#let code-token-number = rgb(70, 70, 70)
#let code-token-function = color-ink
#let code-token-operator = rgb(100, 100, 100)
#let heading-font = ("Microsoft YaHei", "Segoe UI", "Segoe UI Emoji")
#let body-font = ("Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI", "Segoe UI Emoji")
#let mono-font = ("Consolas", "Cascadia Mono", "Microsoft YaHei UI")
// =====================================================================
// GreLin Feasibility Report · Design Tokens
// =====================================================================
// --- 字号 ---
#let size-title = 22pt #let size-title = 22pt
#let size-cover-title = 34pt
#let size-cover-index = 52pt
#let size-h1 = 14pt #let size-h1 = 14pt
#let size-h2 = 12pt #let size-h2 = 12pt
#let size-h3 = 11pt
#let size-body = 10.5pt #let size-body = 10.5pt
#let size-meta = 9pt #let size-meta = 9pt
#let size-table = 9pt
#let size-caption = size-meta
// --- 行距 ---
#let leading-body = 12.5pt #let leading-body = 12.5pt
#let space-section = 14pt #let leading-summary = leading-body
#let space-block = 10pt
// --- 间距 ---
#let space-none = 0pt
#let space-micro = 4pt
#let space-inline = 6pt
#let space-block = 10pt
#let space-section = 12pt
#let space-chapter = 18pt
#let space-figure-before = space-block
#let space-figure-after = space-section
#let space-cover-line = 3pt
#let space-cover-title = 3pt
#let space-cover-meta = 6pt
#let cover-block-height = 19.5cm
// --- 线条 / 边框 ---
#let underline-stroke = 2pt + color-neon
#let underline-offset = 5pt
#let heading-number-gap = 0.35em
#let stroke-accent = 1.5pt + color-ink
#let summary-radius = 0pt
#let summary-inset = 14pt
// --- 页面 ---
#let page-margin = ( #let page-margin = (
top: 2.2cm, top: 2.5cm,
bottom: 2cm, bottom: 2.5cm,
left: 2.2cm, left: 2.5cm,
right: 2.2cm, right: 2.5cm,
) )
#let page-header-ascent = 1.0cm
#let page-footer-descent = 0.8cm
#let page-accent-bar-width = 10pt
// --- 表格 ---
#let table-zebra-fill = rgb(245, 255, 228)
#let table-header-fill = color-neon
#let table-data-text = rgb(50, 50, 50)
#let table-inset = (x: 10pt, y: 6pt)
// --- 图片 ---
#let image-max-width = 100%
// --- 代码 ---
#let code-block-inset = 10pt
#let code-block-radius = 0pt
#let code-block-fill = rgb(255, 255, 255)
#let code-inline-fill = rgb(255, 255, 255)
#let code-inline-inset = (x: 5pt, y: 0.12em)
#let code-inline-radius = 0pt
// --- 引用 ---
#let blockquote-inset = 10pt
#let blockquote-stroke = 3pt + color-neon

View File

@ -1,36 +1,53 @@
#import "data/config.typ": * #import "data/config.typ": *
#import "data/theme.typ": * #import "data/theme.typ": *
#import "data/layout.typ": * #import "data/glossary.typ": init-glossary, print-glossary-block, register-report-glossary, gls-long, gls-short
#import "data/diagrams.typ": ore-sorting-flow
#import "data/code.typ": codly-init-rule, codly-config, code-token-highlight-init
#import "data/styling.typ": apply-report-styling
#import "data/layout.typ": cover-block, summary-block, page-header, page-footer, page-accent-bar
#import "data/md.typ": load-report-md
#show: codly-init-rule
#show: code-token-highlight-init
#show: init-glossary
#codly-config
#register-report-glossary()
#let report = load-report-md(
read(content-path),
source-path: content-path,
extra-scope: (
ore-sorting-flow: ore-sorting-flow,
gls-long: gls-long,
gls-short: gls-short,
),
)
#let meta = report.metadata
#set page( #set page(
paper: "a4", paper: "a4",
margin: page-margin, margin: page-margin,
header-ascent: page-header-ascent,
footer-descent: page-footer-descent,
fill: color-page, fill: color-page,
background: page-accent-bar(),
header: context { header: context {
if counter(page).get().first() > 1 [ if counter(page).get().first() > 1 {
#set text(size: size-meta, fill: color-muted) page-header(meta)
#grid( }
columns: (1fr, 1fr),
[#company-name],
align(right)[#project-name · 可行性报告],
)
#v(4pt)
#line(length: 100%, stroke: color-divider + 0.5pt)
]
}, },
footer: context { footer: context {
set text(size: size-meta, fill: color-muted) if counter(page).get().first() > 1 {
align(center)[#counter(page).display("1")] page-footer()
}
}, },
) )
#set text(font: body-font, lang: "zh", size: size-body, fill: color-body) #set text(font: body-font, lang: "zh", size: size-body, fill: color-body)
#set par(leading: leading-body, justify: true) #set par(leading: leading-body, justify: true)
#set heading(numbering: none)
#report-title-block() #cover-block(meta)
#summary-block() #summary-block(meta)
#apply-report-styling(report.body)
#for section in sections { #print-glossary-block()
section-block(section)
}

5909
feasibility/sample.pdf Normal file

File diff suppressed because it is too large Load Diff

View File

@ -904,26 +904,34 @@ uUL
endstream endstream
endobj endobj
126 0 obj 126 0 obj
<</Length 5381/Filter/FlateDecode>> <</Length 5392/Filter/FlateDecode>>
stream stream
xœÝ]û$·mÿ]ÅôåZu<5A>€O$ªZí®ÔƱݺ§<š8il5®ãêÎ<C3AA>»4vÓöoïçËyqvg)éV£Sïî#î †C  ‡|zöí«ß}þéÕ«fõáúèj¨yŸäؤ¤©¹zþôŠš«— UaRØsùòêE~ÿyãÕ‰>4Ô\<5C>7C××G_}~ôñÑù‡ë§Ï¾þôÅÉÉÓ×?Ú4tzºÚL<C39A>òÁeÛÄH†j‰Y¶Q¼Ï—^Ô±àÒµàŒÝÑ겡æòÛ§ŸS#Ô\~^6˜šËç¿|÷"ú„ˆ»_é~íñ¯.ß?:¿œÁ“kxz2Nm,ñ Þ)µ˜Xo2Ö1ÇàÒ6Ž,5]÷ë»ßÐýÆüûYN_×/Úçlóïç9ýv»¤Ö.qÆ{¡P6Ìi"«‚¦ù¡+¼Íÿ¶ÈÏ “ñwê€;7z¶¹¬{‹ãñ'»š…*ϸòL*Ï:ì¥Ã^:ì¥ÃCZìÉvuÛ®Û•i»÷m÷¾íÞ·Ýû¶{Ÿãþî´;ÝiR¥»³Aò'7£èvQTo¼…-ŒâŸ"ù3$¾?_“²&0¹eÐû $ð—ùæÝ›‰v<>ff£Q”F:wû'ÇHÿ xœÝ]û$·mÿ]ÅôåZu<5A>€O$ªZí®ÔƱݺ§<š8il5®ãêÎ<C3AA>»4vÓöoïçËyqvg)éV£Sïî#î †C  ‡|zöí«ß}þéÕ«fõáúèj¨yŸäؤ¤©¹zþôŠš«— UaRØsùòêE~ÿyãÕ‰>4Ô\<5C>7C××G_}~ôñÑù‡ë§Ï¾þôÅÉÉÓ×?Ú4tzºÚL<C39A>òÁeÛÄH†j‰Y¶Q¼Ï—^Ô±àÒµàŒÝÑ겡æòÛ§ŸS#Ô\~^6˜šËç¿|÷"ú„ˆ»_é~íñ¯.ß?:¿œÁ“kxz2Nm,ñ Ì1¸ˆØŒ±h"«b·±d©aéº_ßý†î7æßÏrúª¸~Ñ>g?Ïé·û[&µ‰3Þ …²i®mèî‡Îð6ÿÛên˜Œ¿SܹѳÍí`Ý[<1C>Ÿxg„=ùØÕ,TyÆ•gRyÖa/öÒa/ÒbO¶«ÛvõØ®LÛ½o»÷m÷¾íÞ·Ýû÷w§ÝéNØØ”<> ¢zã-DhaÿÉŸ!ùóýøùšD<C5A1>5<EFBFBD>É-ƒÞ_ y§…¿Ì7ïÞLÔ°ƒ43<1B>¢¼0Ò¹Û?9FúWHÞCò×7cw1vÞ°†èÆøÉ”FA,Õ ³<>&9O6¿ž3gÍ]”…µ—Ô ÎâšJHÉ<0F>ü°®¦Úîd?éR…CC IÐß 9Eò·HþÉ5ÍœV;Gr<47>äïü!yÉ<>ØH>œíNŸ~„ä{­?%Ro霜<½üþëß>ýàÓï¿úý«j9ç µú&eK=§­Ÿ5d0r“W\EñNÉâ2S<32>cólý†õæ<0F>k>ìL!µÆ;kÉe[h¼c¶Æ&Ÿ$ds¨¼=úâèYõÑ7!kÕ†&&g†äa`Ùd$Iðôº€†(†™›ëâývôÅÑÏŽ^teo7¶-ˆ¸z]@Cd“"x]”P@¦ü Ûkß%¹AÕ`RŠÎO
É{HþúfŒã.ÆÎÖýÂ?™²Â(ˆ¥z1Hž"¡cöÑ$çÉæ×sæ,²¹²°öšÁY\sQ ‰"ùÖõÀTÛ<54>ì']ªphHbBHq! ú$§HþÉß!9C²B²F²™ÓjçH.<2E>ü=@ò#$ï#ùñûɇ³ÝÒéÓ<C3A9><C393>üc¯õ§Dê휓“§—ßýÛ§|úýW¿µC-缡VߤÌr©ç´õ3ŒØÁEòŠ«(Æ1xÇLAŽÍ³õGÞ›?4®ù°3„Ôï¬%—-¡ñŽÙ|<><E28099>¡òöè£g-ÖGßd„¬Uš˜œI‡ye“Ðë¢fn®÷ØÑG?;zÑ•½Ýضl â íHnýŽšÍW©?ÆÿÛ²I!TÞvxÝM¼Ÿð‰KÙŽ|Ò÷ÿÔЬYÄA‰Ö¥p€I\56;éEÅ-v3T
èu MbˆàuQB}˜ò'l¯}GäUƒI):?)# ºõ;j6_u¤þÿoË&…PyÛáuG6ñ~Â'.ek8FðIßÿS3³f &Z—ÂqÕØì,¦3x°ƒ ´Øuh=oBÌP)€×<05>ì¡Wq>A=µ}ü¤¸Ÿ«xÒ-)HtpY'Mà#J¬<4A>s¯H×%ܼßpó%\Fçà×U¸2Œ;1<>¸6®îƒAjD Bj˜MôNƒLaVu|Oaãô…º¾.‡Ê¯×G‚ÎÔŒã$15,‡œ-æW™”dì[á—Mð!yß°¯€9[ ·Ç²Ë@-rkÛnmÒ<6D>ñzÄÛCu<>YsýW<C3BD><0E>áÒ]¯&òÀƒÑ¨ÄhK!_ àÖÍ<C396> Å„ú¶HÝ+Aÿ´åÖäß<C3A4>+Ò4˜ëcy<>»Ò4 iò)ÌHS½4<E280BA>u$MvU<76>@IÊËë,$<øß¥Ü‰ŒÉ%\0û|e½PŒ¸rÄ)øæ àu$;÷GèÀUdœOPOm?)îç*žtK
ï¨<EFBFBD>L“øÞ76Q œäI¸ÄˆkàÜ+Òu 7ï7Ü| '<27>Ñ9øuFÕâÇn„ ãNL n„M„3{Á`<60>C!„<>f½Ó S˜UßSØ8}¡®¯Ë¡ò«Æ5ÞøÄQ¤‰ý… ³5ã8IL<49>Ë!gùUf¤%ûdøe|HÞ7lÇ+`Ψ“±Î²Ë@-rkÛnmÒ<6D>ñzÄÛCu<>YsýW<C3BD><0E>áÒ]¯&òÀƒÑ¨ÄhK!ÊÞ<1A>Ü:ºpÝ„bB}ۤÚrkòïÇiÌõ±‡¼Æ]i<1A>…4ùf¤©€ÞMšÆ:&»ª@ ¤aÅåu
0<>RBmIò}'5ÑI²Ÿ|p৘B*aÿ¸ÌKlXN&¡ªdb´ ˆr4]¶ &Ї¡ÄÞH¹Á<C2B9>E<>߱ƩjLà`q,ä1Î%q¶‰F"©ºØˆaiœá„!¬á¢µ<C2A2>5A(E±<45>ñA¢4 IüïRîDFˆä®H˜}¾²^(F\9â|s…wÔF¦ <0C>I|ï( ˜F
D¼øFL°.¯¸ò„smÄV¼¼aƒ&<26> ÆÃäÕ J&ò’ªâ¬=b{qÝ$ÃlÈ`T‰YS“ŒõÐnh£†èEvk"^UÔÄ6΄DÐk<C390>A6oH”بIÜã;i§ä¢õhw }£FI) /%—%ØÐ$\&7)²ÀˆQئÞÐÕ±õ. o| )¡¶$ù¾“šè$YˆO>8ðSLA!•°\æ%6¬ '“PU21ÚD9Ë.ÛÅÃPbo$ŽÜ`Hƒ"ÈïXãT5&p°8r<>ç8ÛD#T]lİ4ÎpBƒØpÑÚÆš ”¢ØFŒø Q "^|#&XƒW\y¹6b+^Þ°A“Æãaòê%yIUqÖ±½¸na¶ d0ªÄ¬©IFƒzh7´QCt<43>"»5¯*jbgB"èµF<C2B5> 7A$JlÔ$nñ<E2809A>´SrÑz´;PоQ£¤”…—K„lh .“›Y`Ä(lSïèêØzÐ7>ç³¶t)8—¯HÑ
ÎgméRp._¢+Ô`<60>ÒØ¡¸ëêY»\¤èGˆƒ²äÞWö°œ8‡³BOÆÆ(1¿å%k05Um IFî^,*%Ë)d&ÆðFÉEµ£4Tt•Û¯«¼Òbºª<C2BA>eü3gH.‡Û>>ò“œ3ü4'u6¸Î¥:¢u6KutN<74><4E>Ð;ª³¡ŽCÔ™k`Í j°GiìPÜu õ¬].Rô#ÄAYrï+{XN<1C>ÈÃY¡'cc”˜ßò5˜˪6<C2AA>1X Ž$# ÷ / ä •’å2“cx£ä¢ÚQ*ºÊí×U^i1]ÕÇ2þÉ3$—ÃmùÉÎ~š“Š:\çRÑŒ:€¥: :§ÎFèÕÙPÇ!êÌ5°f…°—°ar>6 FðÎ:¾ ` iqÏVïµ9‡W$G#¥ënŒïSS¾”yÝLª®€•xêA #¿5”M£±ãà Ç VdÑUUoKÅØn0¶£œÖža¤P‰hŸL ùYYÂOðÏ‘ü _ ùeE|†<>HÉþvF|`)>^æÄg„ÞQ|†:µzS /\*®ÚÇ‚¡&#{ „Ùòó K‡!Gw0üDz4¼GQ¹—ÆgÔž7ÁÀpãM(î`Õ͇µÑbч[¼Ó¶ñáPtžì—ÕTU“Õ,\Ÿ|²#šÏ‰ýÕ<C3BD>ü´ÍŸ<C38D>Ë_±ÊyòÓ_¼V„©è ;ëpÝ9tÀ¢ÆG û#"f¢âb<C3A2>ƒ>DG•™¦ZhÃ¥h"ÇiÈ÷NœrÓTSm*©ÃrÓÎ,]UÚStÑÁðÕ׎ÂêÓfmþ­¼¹,åä²€´<E282AC>[ÏÛÙLÁ·ðr¥6Çè¼3}4ßѱÖõ¶Ö"'†#üñû¾×ÏîÐý—wíý;kI'nB³Å´¤¸
5`/aÃä|l:Œà<C592>%t|ÀÒ0¢ž­Þks¯HŽEJ;Ö Üß§¦|¥\5(óº™T3\+7ðÔƒ V#œT],¾ÚÏ,Tx«6ÿhc4ìì뎧rû ù
F~k(Fb'ÆÁެ.$É¢«ªÞ02бÝ`lG9­ <ÃH¡"Ñ>>™@ò³²„Ÿ àŸ#ù$¿@òËŠø !‘’ýíŒø ÀR|¼Ì‰Ï½£ø uj ô¦@4^$¹T\µ<>CM0.Fö³å;æA,CŽ"ï`"ù!ŽeixÿŽ¢r/<2F>Ϩ=o<6F>?àÆPÜÁªšk£Å¢·x§mãÃ7 è<Ù/«©"«>,&«Y¸>ùdG4Ÿ û«!ùi)š?–¿b•ÿŠä7H>òä§¿x­vÖáºsè€E<E282AC><45>öG´†E|Š‹úUfšj¡ —¢‰§!ß;qÊMSMµ©¤ÿÉM;³tUiOmJÐEÃW_; ¡† W“l€sïÌ$Ïðÿ;/&(N¬fë¥ÃâùŠé—äBãAE
«Lµ ø·òæ²”“ËÒ>þm=o/f3ßÂ˕ڣóÎh@HôÑt|GÇZ×ÛZœŒðÇ#ìû^ >»C÷_Þµ÷ï¬%<25>¸ ÍÓ’â*XX<58>pRu±øj?³,Rá­Úü£<C3BC>Ѱ³¯;žÊí'ä+^M²̽3ÿ <ÃÿïH¾˜ 8±š­‹ç(¦_ <0B>)´ <36>e¶® ',j/ô:C“¸T9…"c[K “¢ÈJE%TÕ¢×Ó´@g€]•¨Ðë¢<C3AB>°$EWÉÔ•ØÍ½ÜYŠ Ê«‰ÐáR¬yý ú^]¶:¥D¥àŽ7pº³ë],º¬ª¶p<C2B6>„C;±7ÞÍ vôñTJb %+Æ_dˆ˜Î/ÿ®ÚЏˆïЙˆ‹C‰m).ºŒÏ °‰¸ ÐR\`ÁîC-%¬Çg".b"G¨<18>X4PTÓS·Äg ø÷ZÊË,iÑ×RÂz|^_`FÚ=&<26>™Ãê£Ñpry~s¸¾…°ÔV4XòF„D½waùÉTƺ]§ŠCÊ<µ(^×Hžï—XÑÔÕý| £;2³¨va¥ÄŽÐBbGà(qc-SØXä E#Ô›0WZTÓ“²Äg ï÷ZHì,iÑ×RÂz|^[b Ú="‰<>ÅjQ‰µ5g^b€<62>.q13ug<75>è<EFBFBD>þ‡o|¤èî¼Z F…çX‡bXc°ÉWýs„ý% ÞxÝHÄädTA@íëW# ¶Â¢(£‡© ¤NB™L¢ˆlC­#ó<> ×”†i¬öê >Ý"óí—[õn^OÎço 9¹¾™…f=Z» -¨Íèc™­kBà ËZÄK½ÎÐ$.@N¡ÈØÖR¤(r„RQd •dµ¨Åõ4-Ð`W%êôºhã,IÑU2u%vs/wârÅz"t¸ƒ«Ãªô½ºlu‰JÁoàtg×»X>t?Xeé°pI ÊŶP["ÆÁáÐNí<4E>w3¨}<•“XCÊŠñ¢ä$¦3̿뀶"0â;t&#ÁPb[
<ÜL_††¨CÄô«E­¬IÞ<49>ÿ$oµÖ7⓱6¹ïðϸföòË;+Œp>÷ zÁF“uÚª`«yæÜãÎåØ>âã›#¬çåR0óì™n,xdɱê7§öÒúåðVÑôp±®d¬¹kɾœŠøÉ7ÇOĨhHÑ¿;<3B>¢ïUÇn°ŽAröYµ=FÍÁ^€Vâ<„Iá<49> ½ÆØm^#C»Ô:¤û8 Œ„.cÁul"0´˜X0üPK ëñ™ŒØ¢È*F# ÕôÔ-ñ(>Á½‡–3KZôµ”°Ÿ×v<E28098>Idæ°ºµÈh4œ°ø AÝîúâR[Õ`ÉF õÞÅåK$ÿQïv+)sÕ¢x]#y¾_fESW÷ó-hŒNìÈ΢Úeù~„•2;B <1D>£Ì<C2A3>µLac#4EŽPoBÀ|iQMOÊŸ<>¼Ü{h!³#°¤E_K ëñym™-h÷ˆdv«…eÖÖ\z‰ÖºÄÅŒÕ<C592>Õ¢z!¼ñ¢»ózh1žc5Ša<C5A0>Á&[\õÏü—`$xàu#S”QYQG®|oµ¯_<C2AF>€dØ
°a å88<>p(¸‡ªßœêØOìƒ ·‰¨‡ëŽÝp4>‡èȾœîøö˳±ùþ%WÇŒU´,­&yYQ3Ä$j÷Ç©9 ‹¢Œ¦&<26>:-
+ÙªIQI—4:ȤˆÙŒv€ ïI´|s„õL>B e2‰"² µŽ ÌBæAs€\—ÈP
&<1F>Ã9\ <0C><43>Â%Ù"öAVÇÛDÔÃ5G¬9& EHðLŽ-$Ý:›Þ>K{9,êy*»qHÅš^+܈\Ðø¸ì ÂBÜüyÍC¨ ‰`×<C3B5>(ÀJDeÝøØ°àï4°w(¹»‡ ÃâPh1,öµ¾ASc?<3F>²5Þz®+fâìL#Å—S¿?fvF|L<Ú9¬ñŸy Xå;æÝ(Û`˜%<VeáŸ*à;ЇÐÁ<>&gS¡óÑãZò7€Ot¬ˆÐu<C390>iˆ®1º\ì$B×Uû&“}„>0âùvô`uávcžìÃ@òM kò‡ŠŽ<C5A0>Šò(uDa£•)ºEjTñ¡Mk1³qA-úFnA|6 <0C>¤à`1l=Tý(Ü<>-b¤'Þ&¢®+lÍ !<21>Ï-/ Ö<>¿7ØßWTÅn°3©±^añQ0ê½}\ <0B>F=‰{Ma“!QCÐ÷O™HÎë4œßŠh~ ˜ó[XËoË,œê¾Î78;²<>ÆÅ)ÞZ®f"$=¹T ÿ•§E<C2A7>üÉgeQQ 3¡L°Áì¼G©ØEìÀáaT!f<>ïÇ»$ø ¼ »Anvë`EØ­ƒLÃn°ﺂ'a·®ê7èlì%ö<>±Ì·‡¨‡ëŠ™Ýª¼ôd_PWÌÆ2ÿg°,¾ª¨<C2AA>Tq=¥Ö­aNj„¶ð»g­á±AõùîºñÞx%µ¸Ã¤VJû oްžÁGHÁà#°gñàIǪƒë±MìC´Æ[EÔõ†V\£Õ$Kyß ¦Fÿ±ÖüN}¯µr±Þš$Xÿz;c¥é‡[ùK®}mùÝH« óD</¿)Öþm=+{Hr c ¦±Ú«ƒ8uÌ·_tÕ;{=9Ÿ¿-äXäúfšõkín0*ð@p0‰¤^ qÓ¯r,µ²2y7
&êk+_û(vUývûÃßí/Üf?;™I*Dò)ëÙßÿ>†'ØÃÈ3ï˷ùׯ‰|êî=v,$ŠçÝïQ¢Óý­Ù<C2AD>3cY6³c“ƒ>ÞÓ˜¢Ä§åX;Ai7¤ Ùãb(<28>¾6ÙHdU"{FdWD‰8<E280B0>¸Ø&“( 6r|*,†T£E“,QrDnMä6ØL{K¹Õ$'O”PR$@äΉJT"wF”´Òc¾F„ð·¿K¾/ú¤3ì˜YÁ,Ôx)[Ñ!<0/űB ´Ê½j‰ä2¸"Jèù QBo_ z?)Dù2Êo©%RG¤žH-¶Ý±Å[ŠÒ”HψtÕa±®ôXªûJmW|_ôÑ5nˆôœH/Z«òVxËiØù¾xyÞ ”¼ ÖZ߈OÆÚ侃@ãÊÙË/ï¬0Æù܃èMLÖi«­æùs<C3B9>;—#üˆJŒoްž—GHÁÌ#°gº±à%ǪߜvØKëC”Ã[EÓÃUÄnбò®%ûr*âk$ß?£¢!EÿîtÖ%O¾WQ»!;ÉÙgÕö5{VX<56>ó&…7â•2ôcS´ aìRëSïã(8À†ap€”ãà¡àb ª~sªc?±2,Þ&¢®;vƒÒø(¢#ûrºãÛc,Ò
T!ÞR(<28>v½¶<C2BD>s"¹€ž?lÝæ­³»~<0F>Nˆ À;¹?ÛÍzC)Lt&D¿àq>ű1‰xìZôv*ÚÚ”¡²-ô®âH ylÇ´eÇe¾Ÿ k}+ƒ„];Àá; <0B>s½d÷Ÿùvˆ¢ýâv‡uçÉ<C3A7>â+àE Ø üã<ó„ÑX9YƒíoÅ̶„oÒã­ŽéžÙð6=¥ø ®0f¨é¥M$^`Ð{wÒç¥ùù°£Ñÿ"ùqõ™á:`wÓ¨c<ŠDgJtyAäCÇ¢«MkcSH<15>Àùh¦Ûšß§Ót×Cqš¶|¸‹Ó´F"}+“]Æi¢³õ®ÓTê!¿™:Mû<4D>«ýL©f º†ø°Jäš"X¥ƒÏ„ˆ3þurFóz"ýÚgBÌ_ŠO $3| !Û9šðaíóèÇžÁ×Dg°qAbë»Â3¨ oqwèu*†½·p7ÌÄ+´Ÿ†±«beißúlúUaã¬_༘¢ïàúˆcEYÙd±Ç»{`eUsE—B)G5:ot[1ÑJˆVvǵî£9ðº!t ö­ãÞˆVð|.vœ÷•'Z¢U$Z!1ìŒóѱgÔ|Vé1­ßy^àôÐÆ0EužJµÐ:ìçMü°<•jàR(µJl<g¦®¿ÒÌh籓<C2B1>ä<EFBFBD>±Bñ„(®ˆV+"N´µŠc0Œ]aE´¼¢En ³®€øE~” ·"Z3ÑZˆÖ/NŸØ€ÓU4¦™³Ð†g'Dk…ˆþ¢:O´Dkv $¶BœZÈÚJÂÙFnéd´8­uŒ#®Ï¦f,þhRUIö¶È»Ø¼Õçr*KÂéØpg!¬~ðÝx¾\sìLJúï@‡£ñ*ç¦9‡ç9<C3A7>_ºaÅ©sƒE?ïÞÿÌ]… 5_[5B[ç‚>æÙ·TjÃ!8nãAM­…µà ^C—­‰Ö¢õyëcݬûçå¶Ì­õEš¶–Žý†‰60êÜŽQ7ÌÀtÓ*0ê6xÍíï3­"ÁXñaÜËuK 0<>íüÉ&Õõ¹J<C2B9>Ë(š”=4—Ù—©œåæ\·¦k3€ÂgãŒoï=÷v°¥SïLT,&AǬˆ¬Ôí'<27>q¨óÖ,øzi±öÎîyt³Uª»€ƒ<E282AC>á¼_¶szÙ­Rrw´w ,$uBÊ—Ç«ß o ìÌ«à{Ä+Ã=£þÁdqã§šë<C5A1>uvΉ]D£Aîz Ö¯…¨výÌx™™t8¶rIL7k¢Í¦û;¯cÉ´;„Z<E2809E>±fqðÒÙq„cĬ†bŠ!œäPÄ.°/ív¹Û<C2B9>“蜉Ρ S<>DµQ”#ã@°é÷ÏÐW²0Õyá;Y†7oУxgˆ©O|‰ã› Ä>äû—H^3ÖÒ²´šäeEuÌ|“¨uݧæ(¬d«&E%]Òè “"æ4ÚR°ø2xÜYk¼'ÑòÍÖ3ù)˜|äPp1@U?
¦9?Þ}‰Ë·¬u ¶¢ëOÊù¤ñã7ïl0¹àãBŽÎþñ6˜Ć}Lk2Õ"å‹!Õ2¤=~#ƒ³¡ÁètzÏÓûs»uï¶îýô~½*î{ ˆÓ:Óf«Œ°ýÎÙñþBq¿Þ~!otðƒmÀ§uœm74î6Äòô<C3B2>ó´õŽNïWç[Ï϶îwˆõÑ6ïlÕ¸ÓòO^Õ„¹¶P=có"²|¾é†Ñꈓ*²£Å–Ó,šµI‡¥pj%s<<3C>¾nOóÌÑÓâq·S]Ç6a°·l,—`%åÅ$§èV{?݆<; mI½[ŽhЦ<C390>aÆ*@ŸL$,œçDçy\¢ d«éc£ul‰.„è³kzƒ¹=sn²S7<>sx—lú…# —dØYoQ×±æ˜X6"YÀ39¶<39>tëlzwø8íå°«æ©ìF"+{­p#VLpAãã²7ËqóG6¡6$iÕ_7¢X+Y,wã“ÿ{ÐÀÞ= äî6 C¡Å°Ø×úM<>ýt>ÈÖx;èy¸®˜´3<C2B4>_NYüþ˜Ùñ1ñhgä°Ææ•`•¯™wC lƒaðX•…g|°€¯)BWCšœM}„ÎG<C38E>kÉ_Z> Ðu°"B×A¦!ºXÄèr±“]WítLöúÀˆçÛAЃՅÛ<E280A6>É4-<2D>4[®É*:B*nÈ£Ô…eŒV¦è `¨QÅç6­ÅÌÆÍ[¦ÀÈ !(‚ïã8À†!p€”cà,æ¡à‚­‡ª²EìƒôÄÛDÔÃu…­¹!äñ1¡åtÅwÃJò÷›âûŠªØ v&5Ö+,>
çDžèâ×…ÇS“w=¿%å;ì—ï™C\å¿8[:.Á3˜ùd¢™•U·ü‡šÄø€{z:ÜmHq†£i÷¤ïÁÙË8‰†Ê»×øR-á U±¡Qœ3ª‡.?$¢ÿ¬˜:} F½·<EFBFBD>+b¡Ñ¨'q¢)l2$
ahúž‚â‰#Éy<C389>†ó[PÍoÓ`~ +bùm™…SÝ×ùgGöÑø 8ÅÛ@ËÃÃLd“¤'÷‚Šá¿ò´?"ùï¬,*Ša&”i“ ÞÁÎ{”š<E2809D>]ÄN F5âaÖù~¼KÏÂÛ°!ä¦a·V„Ý:È4ìÖ‹ñ®+xvëª~ƒÎÆ^bË|{ˆz¸®˜Ù³ÊKOöuÅl,óË⫊ÚH×ãQj<51>Ñæ¤Fh ¿{ÖÛ$XŸï®ï<>WR;Lj¥ä±+Àðæë|„ >{6 ™t¬ú1¸ÛÄ>Dk¼UD=\khÅõ€0ZM²”çñÝ`jôŸlÍï×÷Z+'+~¡Iõ¯·?Vš~¾•¿çÚ÷éß<E28093>´
:OÄóò[cíßܳ²“$×0¦`¢¾¶òµ<C3B2>boÕo·?ÿÝþÎmöÓ“)¤B$Ÿ±žýýïfx<78>‰<9üº|ýšÈ§îÞcßB¢xÞý^%:ÝßšÝ8ƒ0fc9;ö29è£à=<3D>9!J|Z޵”vC
<> 1.†èk<>D6Y%²gDvEĈˆÍ2‰a{!ǧÂbH5Z4É%GäÖDnƒ-5±Ã$[M²áqòD %E" Dîœ(¡D%rgDI+=ækäAûëäû¢O:þ™ÌB<C38C>—²ÂóR¬k!”@«Ü«ÈAN!ƒ+¢„žß%ôö‘ ÷B´?)©ìðZ"uDê‰ÙbÛ[¼¥(M‰ôŒHWëJ<C3AB>¥y°»Ôö×Å÷E]é†Hωô¢•±*oi…·œ†<C593>¯Œ—ç­@â-…h×k 9' èùSÁnÞ:»»í÷ð脼“û³Ý¬7”ÂDgBdñ çS츈ǮEo§¢­­*CïÚ!ŽÄ<C5BD>ǦLQv\æûÙ°Ö·²=Hص¾³8×Kvÿ™o‡(ºØ/.awXwž ^”€Í@À?Î3O<18>•³˜5ØþVÌl~@ø2=.Ø
à˜nAá™m`ÓSŠ
c†š^ŠÑDâ½w'}^*<2A>Ÿûý/WPŸ®ö¸H1-ˆ:Æ£Ht¦DgPD>t,ºjÙ´66…Tñœ<>fº¹ù}:Mw=ºá§iû؇»8MAk$²Ø½2Ùeœ&!:[ï:M¥ò›©Ó´ß¹ÚÏjŠ`¡kˆk D®)…P:ødˆ8ã_'gT0¯·(Ò¯}2ÄÌ!&¬øÄ`A2ÃÇ<>£ ïÖ><3E>|<7C>@t;÷D!¶¾+<ƒÚðw‡^§bØ{q wÃL°Bû™a{+&Yö­ÏV¡_u&1ÎúN<>9!Zá®<>X1V”•M;½»VV5Wt)”rT£óÆA·­„hew\ë>š¯Bbß:î}€hÏçbÇy_y¢U ZE¢²)ÃÎ8{FÍg•Ó™çÎP}` £QTç©T­Ã®ÞÄËS©6.…R«ÄÆÓfêú+ÍŒv»ùHÞk!OˆâŠhµ"òçD«uûWÒL 8ÃØvQD{Á[!ZäÆ0ë
ˆ_äGY p+¢5­…h<E280A6>ñâô‰ 8cEcš9mxvB´†Qˆè/ªóDë@´†`"Ù@b+Ä©…¬­$œpäNFÑZÇ8âúljÆâ<C386>¶(U•d_1Ö-a£l±Í[my.·™·J¡°$œá<C593>-wòÁêÇß<C387>§Ì5ÇΤ$©ÿt8 ¯rÚašsˆù¥Vœ=7XôóŽáýÏÜURóµ%X#´u>ècž}Kµ¨¶8å€C7tÐÔZX{1œ@á5tÙšh½!ZŸ·!¸>Öͺ^nËÜZ_´¡ik‰àØo˜h£Îí L7­£nƒ×Üþ>ÓšQ!ŒáÀ °\·´ØÓØÎŸlR]Ÿ«Ô¸Œ¢IÉÑCs™­Qq)œÊYÞaÎukº6ó(|6ÎøöÞso[:õÎDÅbtÌŠÈJÝ~Ò‡:oͯ—kïìžG7[¥ºkÎûe;§—Ý*%wG{—°ÁBR÷ ¤|9Pq¼úͰ¸ñÊÎl²
¾G¼b1Ü3êL7Npª¹ÞXgçœØE4ä®×`ýZˆj×ÏŒ—ù¨I‡Ã+—Ät³&Úlº¿ó:L»C¨kÇ/-‰f?<3F><>ˆ1À±¹ÇA8FÌj(¦ÂIûÒn—»8‰Î™èÚ0ÕHTE92ŽÃqoñ ½y% Sm¾“%lxó=Šw†˜úÄ—8¾É¡`šóãЗ¸|ËZ×`+ºþ¤œOÏ1~óΓ«±>.äèìÿoƒÉW Al(ÀÑÇô°† S-R¾R-CÚã'8X18kŒN§÷<½?·[÷nëÞOï׫⾗€8­3m¶ÊÛïœï/÷ëíÒñüpZÇÙvCãnC,Oß9O[ïèô~u¾õülë~‡XmcùÎV<C38E>;-ÿäUM˜kkÕc96/"Ëçn­Ž8©"1Ql:ýÀ¢YtX
§V2Ç3éëö4Ï@-§r;Õ…q¼i{ËÆRp VR^Lrq:€nµ÷ÓmèÀ³“ÐÔ»åè€ôÉDÉÂyNt~‘Ç%ºÀ@¶š>F0ZwÁèBˆ.l1»¦7˜Û3§';Å¡Ó8<C393>wɦ_8¢pNtá‰.np]x<;y×ó[R¾Ã~ùž9ʵXþ†±©ã<ƒ™O&º˜YYuËoy¨IŒ¸§gÄÝþ˜g8ZvWšñ.°œÁŒi¨¼{<7B>ÎRÅy£êpøòâúÞ9<ß
endstream endstream
endobj endobj
127 0 obj 127 0 obj
@ -4795,12 +4803,12 @@ T
endstream endstream
endobj endobj
129 0 obj 129 0 obj
<</Creator(Typst 0.15.0)/ModDate(D:20260623162844+08'00)/CreationDate(D:20260623162844+08'00)>> <</Creator(Typst 0.15.0)/ModDate(D:20260623174249+08'00)/CreationDate(D:20260623174249+08'00)>>
endobj endobj
130 0 obj 130 0 obj
<</Length 996/Type/Metadata/Subtype/XML>> <</Length 996/Type/Metadata/Subtype/XML>>
stream stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.0</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>zh</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-06-23T16:28:44+08:00</xmp:ModifyDate><xmp:CreateDate>2026-06-23T16:28:44+08:00</xmp:CreateDate><xmpTPg:NPages>1</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>cyD771vaNfjg2k+HwoDFcg==</xmpMM:InstanceID><xmpMM:DocumentID>cyD771vaNfjg2k+HwoDFcg==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?> <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmp-writer"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" xmlns:pdf="http://ns.adobe.com/pdf/1.3/" ><xmp:CreatorTool>Typst 0.15.0</xmp:CreatorTool><dc:language><rdf:Bag><rdf:li>zh</rdf:li></rdf:Bag></dc:language><xmp:ModifyDate>2026-06-23T17:42:49+08:00</xmp:ModifyDate><xmp:CreateDate>2026-06-23T17:42:49+08:00</xmp:CreateDate><xmpTPg:NPages>1</xmpTPg:NPages><dc:format>application/pdf</dc:format><xmpMM:InstanceID>SnqF9j32HgXwxSkGV6ulqQ==</xmpMM:InstanceID><xmpMM:DocumentID>SnqF9j32HgXwxSkGV6ulqQ==</xmpMM:DocumentID><xmpMM:RenditionClass>proof</xmpMM:RenditionClass><pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="r"?>
endstream endstream
endobj endobj
131 0 obj 131 0 obj
@ -4935,13 +4943,13 @@ xref
0000123633 00000 n 0000123633 00000 n
0000124345 00000 n 0000124345 00000 n
0000143033 00000 n 0000143033 00000 n
0000148485 00000 n 0000148496 00000 n
0000148892 00000 n 0000148903 00000 n
0001010607 00000 n 0001010618 00000 n
0001010720 00000 n 0001010731 00000 n
0001011793 00000 n 0001011804 00000 n
trailer trailer
<</Size 132/Root 131 0 R/Info 129 0 R/ID[(cyD771vaNfjg2k+HwoDFcg==)(cyD771vaNfjg2k+HwoDFcg==)]>> <</Size 132/Root 131 0 R/Info 129 0 R/ID[(SnqF9j32HgXwxSkGV6ulqQ==)(SnqF9j32HgXwxSkGV6ulqQ==)]>>
startxref startxref
1011963 1011974
%%EOF %%EOF