[{"data":1,"prerenderedAt":5706},["ShallowReactive",2],{"blog-zh-unidoc-migration":3},{"id":4,"title":5,"author":6,"body":10,"date":5666,"description":5667,"draft":5668,"extension":5669,"howTo":5670,"image":5696,"meta":5697,"navigation":305,"path":5698,"seo":5699,"stem":5700,"tags":5701,"updated":5696,"__hash__":5705},"blogZh/zh/blog/016.unidoc-migration.md","unipdf 是 AGPL 或付费：迁移到 gpdf 的完整指南",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/en/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":5649},"minimark",[13,18,46,61,72,75,79,82,85,107,114,118,121,251,258,261,264,411,532,550,557,561,572,881,912,916,919,924,1298,1303,1733,1742,1746,1763,1767,2383,2386,2390,2782,2795,2806,2813,2817,2820,2824,3087,3093,3097,3550,3565,3568,3572,3587,3591,3884,3887,3891,4553,4561,4565,4571,4575,5041,5045,5333,5344,5347,5353,5449,5452,5455,5459,5462,5501,5507,5511,5517,5529,5549,5555,5574,5580,5584,5587,5604,5617,5620,5646],[14,15,17],"h2",{"id":16},"tldr","TL;DR",[19,20,21,25,26,29,30,29,33,36,37,41,42,45],"p",{},[22,23,24],"strong",{},"gpdf"," 是一个采用 ",[22,27,28],{},"MIT 许可证","、",[22,31,32],{},"零外部依赖",[22,34,35],{},"无需许可证注册"," 的纯 Go PDF 库。如果你在用 ",[38,39,40],"code",{},"unidoc/unipdf"," 是因为没有别的库能搞定 CJK 或 AcroForm，但 AGPL 条款让法务部卡住分发，商用许可的成本又难以说服管理层 — 这篇文章是 unipdf ",[38,43,44],{},"creator"," API 到 gpdf 的迁移地图。",[19,47,48,49,52,53,56,57,60],{},"上个季度，一个金融科技朋友的团队把 ",[38,50,51],{},"github.com/unidoc/unipdf/v3"," 提交到了 OSS 审批流程。第二天单子被打回，",[22,54,55],{},"AGPL-3.0"," 旁边一个红 ✗，加上法务一句：\"",[22,58,59],{},"不能链接到闭源产品中分发。请购买商用许可或移除。","\"商用报价按每个开发者每年算，12 人团队一总，所有人又重新打开了搜索结果。",[19,62,63,64,67,68,71],{},"这是 unipdf README 不会写的另一面。unipdf 技术上很出色 — 成熟、功能全、维护积极。但它是 ",[22,65,66],{},"双许可证","：开源用途用 AGPL v3，其他用途必须 ",[22,69,70],{},"付费商用许可","。AGPL v3 是常见 copyleft 中最严的之一。如果你把 unipdf 链接进一个用户通过网络访问的服务，§13 条要求你必须公开整个对应源码。大多数公司的法务都会说不。",[19,73,74],{},"如果你已经有 unipdf 代码在生产，许可证在审计中被卡或即将续费，这篇是迁移地图。如果你刚开始项目因为 unipdf 文档最完善就装了它，这篇是不带账单关系的替代方案。",[14,76,78],{"id":77},"agpl-或付费在实际操作中是什么意思","\"AGPL 或付费\"在实际操作中是什么意思",[19,80,81],{},"很多 Go 库随便贴个 \"AGPL\" 标签，没真考虑过含义。unipdf 不是这样。仓库的许可证文件就是纯 AGPL v3，README 明确说商用必须密钥，二进制本身也强制执行 — 启动时不注册许可证就调用 unipdf API，要么报错要么每页输出加水印。",[19,83,84],{},"你大致会处于以下三种模式之一：",[86,87,88,95,101],"ol",{},[89,90,91,94],"li",{},[22,92,93],{},"AGPL 模式","。你以 AGPL v3 公开自己的代码。所有接触 unipdf 的字节，加上链接到它的所有代码，必须能让通过网络访问该服务的任何人获取。对内部工具和 SaaS 产品来说，这基本不可行。",[89,96,97,100],{},[22,98,99],{},"商用模式","。你按每开发者每年付费给 UniDoc。价格变化，最近公开报价大约每席每年四位数美元，包含一个 metering 或许可证密钥注册调用，每个二进制启动时都要执行。密钥被当成 secret 处理，意味着它要进 secret manager 并注入每个容器。",[89,102,103,106],{},[22,104,105],{},"试用 / 评估模式","。限时免费。输出带水印。生产环境用不了。",[19,108,109,110,113],{},"这三种模式本身都不是错的。UniDoc 是真公司、真工程师维护的产品，价格反映真实成本。",[22,111,112],{},"问题在于这个许可证决策渗透到每一层","：法务审查、密钥轮换、财务续约、部署面 (每个容器都要密钥)。gpdf 因为是 MIT，把这一整列从你的电子表格里抹掉了。",[14,115,117],{"id":116},"你失去什么保留什么","你失去什么，保留什么",[19,119,120],{},"进入 API 之前先实事求是。unipdf 有些事 gpdf 做不到：",[122,123,124,139],"table",{},[125,126,127],"thead",{},[128,129,130,134,137],"tr",{},[131,132,133],"th",{},"能力",[131,135,136],{},"unipdf",[131,138,24],{},[140,141,142,153,163,173,183,192,202,212,222,231,240],"tbody",{},[128,143,144,148,151],{},[145,146,147],"td",{},"PDF 生成",[145,149,150],{},"✅",[145,152,150],{},[128,154,155,158,160],{},[145,156,157],{},"TrueType / CJK 字体",[145,159,150],{},[145,161,162],{},"✅ (无 CGO，自动子集化)",[128,164,165,168,170],{},[145,166,167],{},"AES-128/256 加密",[145,169,150],{},[145,171,172],{},"✅ (ISO 32000-2 Rev 6，纯 Go)",[128,174,175,178,180],{},[145,176,177],{},"PKCS#7 / PAdES 签名",[145,179,150],{},[145,181,182],{},"✅ (支持 RFC 3161 TSA)",[128,184,185,188,190],{},[145,186,187],{},"PDF/A-1b/2b",[145,189,150],{},[145,191,150],{},[128,193,194,197,199],{},[145,195,196],{},"AcroForm — 填写已有字段",[145,198,150],{},[145,200,201],{},"✅ (仅扁平化，不能创建新字段)",[128,203,204,207,209],{},[145,205,206],{},"AcroForm — 创建新字段",[145,208,150],{},[145,210,211],{},"❌",[128,213,214,217,219],{},[145,215,216],{},"PDF 解析 / 文本抽取",[145,218,150],{},[145,220,221],{},"❌ (gpdf 专注于生成)",[128,223,224,227,229],{},[145,225,226],{},"OCR",[145,228,150],{},[145,230,211],{},[128,232,233,236,238],{},[145,234,235],{},"PDF 涂黑 (redaction)",[145,237,150],{},[145,239,211],{},[128,241,242,245,248],{},[145,243,244],{},"HTML 渲染",[145,246,247],{},"部分",[145,249,250],{},"❌ (用单独的渲染器，再合并)",[19,252,253,254,257],{},"如果你需要 PDF 解析、OCR 或 redaction，这次迁移走不到底。要么只在那些代码路径里保留 unipdf (那部分二进制还得买商用许可)，要么读取侧换用专门的解析库。对 ",[22,255,256],{},"生成、加密、签名、字体、CJK"," 这些路径 — 也就是大多数 unipdf 账单实际买的东西 — gpdf 是完整替代。",[14,259,260],{"id":260},"删除许可证注册代码",[19,262,263],{},"这是整个迁移最小的 diff，也是让其他工作变得真实的一步。unipdf 二进制启动时必须注册密钥，有几种写法：",[265,266,271],"pre",{"className":267,"code":268,"language":269,"meta":270,"style":270},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","// API key (metered)\nimport \"github.com/unidoc/unipdf/v3/common/license\"\n\nfunc init() {\n    if err := license.SetMeteredKey(os.Getenv(\"UNIDOC_API_KEY\")); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[38,272,273,282,300,307,323,380,399,405],{"__ignoreMap":270},[274,275,278],"span",{"class":276,"line":277},"line",1,[274,279,281],{"class":280},"sHwdD","// API key (metered)\n",[274,283,285,289,293,297],{"class":276,"line":284},2,[274,286,288],{"class":287},"s7zQu","import",[274,290,292],{"class":291},"sMK4o"," \"",[274,294,296],{"class":295},"sBMFI","github.com/unidoc/unipdf/v3/common/license",[274,298,299],{"class":291},"\"\n",[274,301,303],{"class":276,"line":302},3,[274,304,306],{"emptyLinePlaceholder":305},true,"\n",[274,308,310,313,317,320],{"class":276,"line":309},4,[274,311,312],{"class":291},"func",[274,314,316],{"class":315},"s2Zo4"," init",[274,318,319],{"class":291},"()",[274,321,322],{"class":291}," {\n",[274,324,326,329,333,336,339,342,345,348,351,353,356,358,361,365,367,370,372,375,378],{"class":276,"line":325},5,[274,327,328],{"class":287},"    if",[274,330,332],{"class":331},"sTEyZ"," err ",[274,334,335],{"class":291},":=",[274,337,338],{"class":331}," license",[274,340,341],{"class":291},".",[274,343,344],{"class":315},"SetMeteredKey",[274,346,347],{"class":291},"(",[274,349,350],{"class":331},"os",[274,352,341],{"class":291},[274,354,355],{"class":315},"Getenv",[274,357,347],{"class":291},[274,359,360],{"class":291},"\"",[274,362,364],{"class":363},"sfazB","UNIDOC_API_KEY",[274,366,360],{"class":291},[274,368,369],{"class":291},"));",[274,371,332],{"class":331},[274,373,374],{"class":291},"!=",[274,376,377],{"class":291}," nil",[274,379,322],{"class":291},[274,381,383,386,388,391,393,396],{"class":276,"line":382},6,[274,384,385],{"class":331},"        log",[274,387,341],{"class":291},[274,389,390],{"class":315},"Fatal",[274,392,347],{"class":291},[274,394,395],{"class":331},"err",[274,397,398],{"class":291},")\n",[274,400,402],{"class":276,"line":401},7,[274,403,404],{"class":291},"    }\n",[274,406,408],{"class":276,"line":407},8,[274,409,410],{"class":291},"}\n",[265,412,414],{"className":267,"code":413,"language":269,"meta":270,"style":270},"// 离线许可证文件\nfunc init() {\n    licenseKey, _ := os.ReadFile(\"/etc/unidoc/license.txt\")\n    if err := license.SetLicenseKey(string(licenseKey), \"Acme Corp\"); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,415,416,421,431,463,510,524,528],{"__ignoreMap":270},[274,417,418],{"class":276,"line":277},[274,419,420],{"class":280},"// 离线许可证文件\n",[274,422,423,425,427,429],{"class":276,"line":284},[274,424,312],{"class":291},[274,426,316],{"class":315},[274,428,319],{"class":291},[274,430,322],{"class":291},[274,432,433,436,439,442,444,447,449,452,454,456,459,461],{"class":276,"line":302},[274,434,435],{"class":331},"    licenseKey",[274,437,438],{"class":291},",",[274,440,441],{"class":331}," _ ",[274,443,335],{"class":291},[274,445,446],{"class":331}," os",[274,448,341],{"class":291},[274,450,451],{"class":315},"ReadFile",[274,453,347],{"class":291},[274,455,360],{"class":291},[274,457,458],{"class":363},"/etc/unidoc/license.txt",[274,460,360],{"class":291},[274,462,398],{"class":291},[274,464,465,467,469,471,473,475,478,480,484,486,489,492,494,497,499,502,504,506,508],{"class":276,"line":309},[274,466,328],{"class":287},[274,468,332],{"class":331},[274,470,335],{"class":291},[274,472,338],{"class":331},[274,474,341],{"class":291},[274,476,477],{"class":315},"SetLicenseKey",[274,479,347],{"class":291},[274,481,483],{"class":482},"spNyl","string",[274,485,347],{"class":291},[274,487,488],{"class":331},"licenseKey",[274,490,491],{"class":291},"),",[274,493,292],{"class":291},[274,495,496],{"class":363},"Acme Corp",[274,498,360],{"class":291},[274,500,501],{"class":291},");",[274,503,332],{"class":331},[274,505,374],{"class":291},[274,507,377],{"class":291},[274,509,322],{"class":291},[274,511,512,514,516,518,520,522],{"class":276,"line":325},[274,513,385],{"class":331},[274,515,341],{"class":291},[274,517,390],{"class":315},[274,519,347],{"class":291},[274,521,395],{"class":331},[274,523,398],{"class":291},[274,525,526],{"class":276,"line":382},[274,527,404],{"class":291},[274,529,530],{"class":276,"line":401},[274,531,410],{"class":291},[19,533,534,535,538,539,541,542,545,546,549],{},"gpdf 没有对应的东西。整个 ",[38,536,537],{},"init()"," 块删掉。把 ",[38,540,364],{}," 从 secret manager、CI 变量、容器 manifest 里拿掉。从镜像中移除许可证文件。要 import 的只有 ",[38,543,544],{},"github.com/gpdf-dev/gpdf","，唯一的要求是在某处调用 ",[38,547,548],{},"gpdf.NewDocument","。",[19,551,552,553,556],{},"就是这样。也是迁移完成的判定标准：完成后 ",[38,554,555],{},"grep -r unidoc ."," 应该返回 0 行。",[14,558,560],{"id":559},"api-映射表","API 映射表",[19,562,563,564,567,568,571],{},"下表是速查表。后续章节是 5 个具体配对。unipdf 的高级构建器叫 ",[38,565,566],{},"Creator","，gpdf 叫 ",[38,569,570],{},"Document","。形状足够接近，大多数代码靠肉眼对照就能转换。",[122,573,574,589],{},[125,575,576],{},[128,577,578,581,587],{},[131,579,580],{},"你想做的事",[131,582,583,584,586],{},"unipdf (",[38,585,44],{},")",[131,588,24],{},[140,590,591,606,621,636,656,674,692,708,723,738,753,775,798,818,833,851,866],{},[128,592,593,596,601],{},[145,594,595],{},"创建构建器",[145,597,598],{},[38,599,600],{},"c := creator.New(); c.SetPageSize(creator.PageSizeA4)",[145,602,603],{},[38,604,605],{},"doc := gpdf.NewDocument(gpdf.WithPageSize(document.A4))",[128,607,608,611,616],{},[145,609,610],{},"设置边距",[145,612,613],{},[38,614,615],{},"c.SetPageMargins(L, R, T, B)",[145,617,618],{},[38,619,620],{},"gpdf.WithMargins(document.UniformEdges(document.Mm(20)))",[128,622,623,626,631],{},[145,624,625],{},"新建页面",[145,627,628],{},[38,629,630],{},"c.NewPage()",[145,632,633],{},[38,634,635],{},"page := doc.AddPage()",[128,637,638,641,646],{},[145,639,640],{},"单行文本",[145,642,643],{},[38,644,645],{},"p := c.NewParagraph(\"hi\"); c.Draw(p)",[145,647,648,651,652],{},[38,649,650],{},"c.Text(\"hi\")"," ",[653,654,655],"em",{},"(在列内)",[128,657,658,661,666],{},[145,659,660],{},"自动换行文本",[145,662,663],{},[38,664,665],{},"p := c.NewStyledParagraph(); p.SetText(...); c.Draw(p)",[145,667,668,651,671],{},[38,669,670],{},"c.Text(body)",[653,672,673],{},"(自动换行)",[128,675,676,679,684],{},[145,677,678],{},"字体注册",[145,680,681],{},[38,682,683],{},"model.NewCompositePdfFontFromTTFFile(path)",[145,685,686,651,689],{},[38,687,688],{},"gpdf.WithFont(\"Name\", ttfBytes)",[653,690,691],{},"(构建时)",[128,693,694,697,702],{},[145,695,696],{},"设置文本字体",[145,698,699],{},[38,700,701],{},"style.Font = font; style.FontSize = 12",[145,703,704,707],{},[38,705,706],{},"template.FontFamily(\"Name\"), template.FontSize(12)"," per-text",[128,709,710,713,718],{},[145,711,712],{},"颜色",[145,714,715],{},[38,716,717],{},"style.Color = creator.ColorRGBFromHex(\"#1A237E\")",[145,719,720],{},[38,721,722],{},"template.TextColor(pdf.RGBHex(0x1A237E))",[128,724,725,728,733],{},[145,726,727],{},"表格",[145,729,730],{},[38,731,732],{},"t := c.NewTable(4); t.SetColumnWidths(...); c.Draw(t)",[145,734,735],{},[38,736,737],{},"c.Table(headers, rows, template.ColumnWidths(...))",[128,739,740,743,748],{},[145,741,742],{},"图片",[145,744,745],{},[38,746,747],{},"img, _ := c.NewImageFromFile(path); img.ScaleToWidth(w); c.Draw(img)",[145,749,750],{},[38,751,752],{},"c.Image(imgBytes, template.FitWidth(document.Mm(50)))",[128,754,755,758,767],{},[145,756,757],{},"页眉 / 页脚",[145,759,760,763,764],{},[38,761,762],{},"c.DrawHeader(fn)"," / ",[38,765,766],{},"c.DrawFooter(fn)",[145,768,769,763,772],{},[38,770,771],{},"doc.Header(fn)",[38,773,774],{},"doc.Footer(fn)",[128,776,777,780,787],{},[145,778,779],{},"页码",[145,781,782,783,786],{},"在 ",[38,784,785],{},"DrawFooter"," 调用中手动追踪",[145,788,789,763,792,651,795],{},[38,790,791],{},"c.PageNumber()",[38,793,794],{},"c.TotalPages()",[653,796,797],{},"(占位符)",[128,799,800,803,813],{},[145,801,802],{},"加密",[145,804,805,808,809,812],{},[38,806,807],{},"model.PdfWriter"," + ",[38,810,811],{},"Encrypt"," 重新编码",[145,814,815],{},[38,816,817],{},"gpdf.WithEncryption(gpdf.AES256, \"user\", \"owner\", perms)",[128,819,820,823,828],{},[145,821,822],{},"签名",[145,824,825],{},[38,826,827],{},"model.NewPdfAppender(...).Sign(...)",[145,829,830],{},[38,831,832],{},"gpdf.SignDocument(pdfBytes, signer, opts)",[128,834,835,838,846],{},[145,836,837],{},"许可证注册",[145,839,840,843,844],{},[38,841,842],{},"license.SetMeteredKey(...)"," 在 ",[38,845,537],{},[145,847,848],{},[653,849,850],{},"(无 — 删除即可)",[128,852,853,856,861],{},[145,854,855],{},"输出到文件",[145,857,858],{},[38,859,860],{},"c.WriteToFile(\"out.pdf\")",[145,862,863],{},[38,864,865],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[128,867,868,871,876],{},[145,869,870],{},"输出到 writer",[145,872,873],{},[38,874,875],{},"c.Write(w)",[145,877,878],{},[38,879,880],{},"doc.Render(w)",[19,882,883,884,887,888,891,892,895,896,899,900,903,904,907,908,911],{},"两个结构性转变要记住。unipdf 的 creator 是 ",[22,885,886],{},"有状态"," 的：你构建一个 ",[38,889,890],{},"Paragraph"," 或 ",[38,893,894],{},"Table","，然后调用 ",[38,897,898],{},"c.Draw(thing)"," 提交。gpdf 是 ",[22,901,902],{},"声明式"," 的：描述一棵行和列的树，让布局引擎放置。第二个是 gpdf 像 Bootstrap 一样有 ",[22,905,906],{},"12 栅格","。每行隐式宽 12 单位，用 ",[38,909,910],{},"r.Col(n, fn)"," 消费。一旦不再用毫米追列宽，多数布局会缩成两三行。",[14,913,915],{"id":914},"before-after-1-最小可能的-pdf","Before / After 1: 最小可能的 PDF",[19,917,918],{},"\"hello world\"配对。unipdf 版本不长，只是因为许可证调用而多了仪式感。",[19,920,921],{},[22,922,923],{},"Before — unipdf:",[265,925,927],{"className":267,"code":926,"language":269,"meta":270,"style":270},"package main\n\nimport (\n    \"log\"\n    \"os\"\n\n    \"github.com/unidoc/unipdf/v3/common/license\"\n    \"github.com/unidoc/unipdf/v3/creator\"\n)\n\nfunc init() {\n    if err := license.SetMeteredKey(os.Getenv(\"UNIDOC_API_KEY\")); err != nil {\n        log.Fatal(err)\n    }\n}\n\nfunc main() {\n    c := creator.New()\n    c.SetPageSize(creator.PageSizeA4)\n\n    p := c.NewParagraph(\"Hello, World!\")\n    p.SetFontSize(24)\n    if err := c.Draw(p); err != nil {\n        log.Fatal(err)\n    }\n\n    if err := c.WriteToFile(\"hello.pdf\"); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,928,929,937,941,948,958,966,970,978,987,992,997,1008,1049,1064,1069,1074,1079,1091,1110,1132,1137,1164,1183,1213,1228,1233,1238,1273,1288,1293],{"__ignoreMap":270},[274,930,931,934],{"class":276,"line":277},[274,932,933],{"class":291},"package",[274,935,936],{"class":295}," main\n",[274,938,939],{"class":276,"line":284},[274,940,306],{"emptyLinePlaceholder":305},[274,942,943,945],{"class":276,"line":302},[274,944,288],{"class":287},[274,946,947],{"class":291}," (\n",[274,949,950,953,956],{"class":276,"line":309},[274,951,952],{"class":291},"    \"",[274,954,955],{"class":295},"log",[274,957,299],{"class":291},[274,959,960,962,964],{"class":276,"line":325},[274,961,952],{"class":291},[274,963,350],{"class":295},[274,965,299],{"class":291},[274,967,968],{"class":276,"line":382},[274,969,306],{"emptyLinePlaceholder":305},[274,971,972,974,976],{"class":276,"line":401},[274,973,952],{"class":291},[274,975,296],{"class":295},[274,977,299],{"class":291},[274,979,980,982,985],{"class":276,"line":407},[274,981,952],{"class":291},[274,983,984],{"class":295},"github.com/unidoc/unipdf/v3/creator",[274,986,299],{"class":291},[274,988,990],{"class":276,"line":989},9,[274,991,398],{"class":291},[274,993,995],{"class":276,"line":994},10,[274,996,306],{"emptyLinePlaceholder":305},[274,998,1000,1002,1004,1006],{"class":276,"line":999},11,[274,1001,312],{"class":291},[274,1003,316],{"class":315},[274,1005,319],{"class":291},[274,1007,322],{"class":291},[274,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027,1029,1031,1033,1035,1037,1039,1041,1043,1045,1047],{"class":276,"line":1010},12,[274,1012,328],{"class":287},[274,1014,332],{"class":331},[274,1016,335],{"class":291},[274,1018,338],{"class":331},[274,1020,341],{"class":291},[274,1022,344],{"class":315},[274,1024,347],{"class":291},[274,1026,350],{"class":331},[274,1028,341],{"class":291},[274,1030,355],{"class":315},[274,1032,347],{"class":291},[274,1034,360],{"class":291},[274,1036,364],{"class":363},[274,1038,360],{"class":291},[274,1040,369],{"class":291},[274,1042,332],{"class":331},[274,1044,374],{"class":291},[274,1046,377],{"class":291},[274,1048,322],{"class":291},[274,1050,1052,1054,1056,1058,1060,1062],{"class":276,"line":1051},13,[274,1053,385],{"class":331},[274,1055,341],{"class":291},[274,1057,390],{"class":315},[274,1059,347],{"class":291},[274,1061,395],{"class":331},[274,1063,398],{"class":291},[274,1065,1067],{"class":276,"line":1066},14,[274,1068,404],{"class":291},[274,1070,1072],{"class":276,"line":1071},15,[274,1073,410],{"class":291},[274,1075,1077],{"class":276,"line":1076},16,[274,1078,306],{"emptyLinePlaceholder":305},[274,1080,1082,1084,1087,1089],{"class":276,"line":1081},17,[274,1083,312],{"class":291},[274,1085,1086],{"class":315}," main",[274,1088,319],{"class":291},[274,1090,322],{"class":291},[274,1092,1094,1097,1099,1102,1104,1107],{"class":276,"line":1093},18,[274,1095,1096],{"class":331},"    c ",[274,1098,335],{"class":291},[274,1100,1101],{"class":331}," creator",[274,1103,341],{"class":291},[274,1105,1106],{"class":315},"New",[274,1108,1109],{"class":291},"()\n",[274,1111,1113,1116,1118,1121,1123,1125,1127,1130],{"class":276,"line":1112},19,[274,1114,1115],{"class":331},"    c",[274,1117,341],{"class":291},[274,1119,1120],{"class":315},"SetPageSize",[274,1122,347],{"class":291},[274,1124,44],{"class":331},[274,1126,341],{"class":291},[274,1128,1129],{"class":331},"PageSizeA4",[274,1131,398],{"class":291},[274,1133,1135],{"class":276,"line":1134},20,[274,1136,306],{"emptyLinePlaceholder":305},[274,1138,1140,1143,1145,1148,1150,1153,1155,1157,1160,1162],{"class":276,"line":1139},21,[274,1141,1142],{"class":331},"    p ",[274,1144,335],{"class":291},[274,1146,1147],{"class":331}," c",[274,1149,341],{"class":291},[274,1151,1152],{"class":315},"NewParagraph",[274,1154,347],{"class":291},[274,1156,360],{"class":291},[274,1158,1159],{"class":363},"Hello, World!",[274,1161,360],{"class":291},[274,1163,398],{"class":291},[274,1165,1167,1170,1172,1175,1177,1181],{"class":276,"line":1166},22,[274,1168,1169],{"class":331},"    p",[274,1171,341],{"class":291},[274,1173,1174],{"class":315},"SetFontSize",[274,1176,347],{"class":291},[274,1178,1180],{"class":1179},"sbssI","24",[274,1182,398],{"class":291},[274,1184,1186,1188,1190,1192,1194,1196,1199,1201,1203,1205,1207,1209,1211],{"class":276,"line":1185},23,[274,1187,328],{"class":287},[274,1189,332],{"class":331},[274,1191,335],{"class":291},[274,1193,1147],{"class":331},[274,1195,341],{"class":291},[274,1197,1198],{"class":315},"Draw",[274,1200,347],{"class":291},[274,1202,19],{"class":331},[274,1204,501],{"class":291},[274,1206,332],{"class":331},[274,1208,374],{"class":291},[274,1210,377],{"class":291},[274,1212,322],{"class":291},[274,1214,1216,1218,1220,1222,1224,1226],{"class":276,"line":1215},24,[274,1217,385],{"class":331},[274,1219,341],{"class":291},[274,1221,390],{"class":315},[274,1223,347],{"class":291},[274,1225,395],{"class":331},[274,1227,398],{"class":291},[274,1229,1231],{"class":276,"line":1230},25,[274,1232,404],{"class":291},[274,1234,1236],{"class":276,"line":1235},26,[274,1237,306],{"emptyLinePlaceholder":305},[274,1239,1241,1243,1245,1247,1249,1251,1254,1256,1258,1261,1263,1265,1267,1269,1271],{"class":276,"line":1240},27,[274,1242,328],{"class":287},[274,1244,332],{"class":331},[274,1246,335],{"class":291},[274,1248,1147],{"class":331},[274,1250,341],{"class":291},[274,1252,1253],{"class":315},"WriteToFile",[274,1255,347],{"class":291},[274,1257,360],{"class":291},[274,1259,1260],{"class":363},"hello.pdf",[274,1262,360],{"class":291},[274,1264,501],{"class":291},[274,1266,332],{"class":331},[274,1268,374],{"class":291},[274,1270,377],{"class":291},[274,1272,322],{"class":291},[274,1274,1276,1278,1280,1282,1284,1286],{"class":276,"line":1275},28,[274,1277,385],{"class":331},[274,1279,341],{"class":291},[274,1281,390],{"class":315},[274,1283,347],{"class":291},[274,1285,395],{"class":331},[274,1287,398],{"class":291},[274,1289,1291],{"class":276,"line":1290},29,[274,1292,404],{"class":291},[274,1294,1296],{"class":276,"line":1295},30,[274,1297,410],{"class":291},[19,1299,1300],{},[22,1301,1302],{},"After — gpdf:",[265,1304,1306],{"className":267,"code":1305,"language":269,"meta":270,"style":270},"package main\n\nimport (\n    \"log\"\n    \"os\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Hello, World!\", template.FontSize(24), template.Bold())\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"hello.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,1307,1308,1314,1318,1324,1332,1340,1344,1352,1361,1370,1374,1378,1388,1406,1429,1464,1469,1473,1490,1522,1558,1602,1607,1612,1616,1636,1648,1662,1666,1709,1723,1728],{"__ignoreMap":270},[274,1309,1310,1312],{"class":276,"line":277},[274,1311,933],{"class":291},[274,1313,936],{"class":295},[274,1315,1316],{"class":276,"line":284},[274,1317,306],{"emptyLinePlaceholder":305},[274,1319,1320,1322],{"class":276,"line":302},[274,1321,288],{"class":287},[274,1323,947],{"class":291},[274,1325,1326,1328,1330],{"class":276,"line":309},[274,1327,952],{"class":291},[274,1329,955],{"class":295},[274,1331,299],{"class":291},[274,1333,1334,1336,1338],{"class":276,"line":325},[274,1335,952],{"class":291},[274,1337,350],{"class":295},[274,1339,299],{"class":291},[274,1341,1342],{"class":276,"line":382},[274,1343,306],{"emptyLinePlaceholder":305},[274,1345,1346,1348,1350],{"class":276,"line":401},[274,1347,952],{"class":291},[274,1349,544],{"class":295},[274,1351,299],{"class":291},[274,1353,1354,1356,1359],{"class":276,"line":407},[274,1355,952],{"class":291},[274,1357,1358],{"class":295},"github.com/gpdf-dev/gpdf/document",[274,1360,299],{"class":291},[274,1362,1363,1365,1368],{"class":276,"line":989},[274,1364,952],{"class":291},[274,1366,1367],{"class":295},"github.com/gpdf-dev/gpdf/template",[274,1369,299],{"class":291},[274,1371,1372],{"class":276,"line":994},[274,1373,398],{"class":291},[274,1375,1376],{"class":276,"line":999},[274,1377,306],{"emptyLinePlaceholder":305},[274,1379,1380,1382,1384,1386],{"class":276,"line":1010},[274,1381,312],{"class":291},[274,1383,1086],{"class":315},[274,1385,319],{"class":291},[274,1387,322],{"class":291},[274,1389,1390,1393,1395,1398,1400,1403],{"class":276,"line":1051},[274,1391,1392],{"class":331},"    doc ",[274,1394,335],{"class":291},[274,1396,1397],{"class":331}," gpdf",[274,1399,341],{"class":291},[274,1401,1402],{"class":315},"NewDocument",[274,1404,1405],{"class":291},"(\n",[274,1407,1408,1411,1413,1416,1418,1421,1423,1426],{"class":276,"line":1066},[274,1409,1410],{"class":331},"        gpdf",[274,1412,341],{"class":291},[274,1414,1415],{"class":315},"WithPageSize",[274,1417,347],{"class":291},[274,1419,1420],{"class":331},"document",[274,1422,341],{"class":291},[274,1424,1425],{"class":331},"A4",[274,1427,1428],{"class":291},"),\n",[274,1430,1431,1433,1435,1438,1440,1442,1444,1447,1449,1451,1453,1456,1458,1461],{"class":276,"line":1071},[274,1432,1410],{"class":331},[274,1434,341],{"class":291},[274,1436,1437],{"class":315},"WithMargins",[274,1439,347],{"class":291},[274,1441,1420],{"class":331},[274,1443,341],{"class":291},[274,1445,1446],{"class":315},"UniformEdges",[274,1448,347],{"class":291},[274,1450,1420],{"class":331},[274,1452,341],{"class":291},[274,1454,1455],{"class":315},"Mm",[274,1457,347],{"class":291},[274,1459,1460],{"class":1179},"20",[274,1462,1463],{"class":291},"))),\n",[274,1465,1466],{"class":276,"line":1076},[274,1467,1468],{"class":291},"    )\n",[274,1470,1471],{"class":276,"line":1081},[274,1472,306],{"emptyLinePlaceholder":305},[274,1474,1475,1478,1480,1483,1485,1488],{"class":276,"line":1093},[274,1476,1477],{"class":331},"    page ",[274,1479,335],{"class":291},[274,1481,1482],{"class":331}," doc",[274,1484,341],{"class":291},[274,1486,1487],{"class":315},"AddPage",[274,1489,1109],{"class":291},[274,1491,1492,1495,1497,1500,1503,1507,1510,1513,1515,1518,1520],{"class":276,"line":1112},[274,1493,1494],{"class":331},"    page",[274,1496,341],{"class":291},[274,1498,1499],{"class":315},"AutoRow",[274,1501,1502],{"class":291},"(func(",[274,1504,1506],{"class":1505},"sHdIc","r",[274,1508,1509],{"class":291}," *",[274,1511,1512],{"class":295},"template",[274,1514,341],{"class":291},[274,1516,1517],{"class":295},"RowBuilder",[274,1519,586],{"class":291},[274,1521,322],{"class":291},[274,1523,1524,1527,1529,1532,1534,1537,1539,1542,1545,1547,1549,1551,1554,1556],{"class":276,"line":1134},[274,1525,1526],{"class":331},"        r",[274,1528,341],{"class":291},[274,1530,1531],{"class":315},"Col",[274,1533,347],{"class":291},[274,1535,1536],{"class":1179},"12",[274,1538,438],{"class":291},[274,1540,1541],{"class":291}," func(",[274,1543,1544],{"class":1505},"c",[274,1546,1509],{"class":291},[274,1548,1512],{"class":295},[274,1550,341],{"class":291},[274,1552,1553],{"class":295},"ColBuilder",[274,1555,586],{"class":291},[274,1557,322],{"class":291},[274,1559,1560,1563,1565,1568,1570,1572,1574,1576,1578,1581,1583,1586,1588,1590,1592,1594,1596,1599],{"class":276,"line":1139},[274,1561,1562],{"class":331},"            c",[274,1564,341],{"class":291},[274,1566,1567],{"class":315},"Text",[274,1569,347],{"class":291},[274,1571,360],{"class":291},[274,1573,1159],{"class":363},[274,1575,360],{"class":291},[274,1577,438],{"class":291},[274,1579,1580],{"class":331}," template",[274,1582,341],{"class":291},[274,1584,1585],{"class":315},"FontSize",[274,1587,347],{"class":291},[274,1589,1180],{"class":1179},[274,1591,491],{"class":291},[274,1593,1580],{"class":331},[274,1595,341],{"class":291},[274,1597,1598],{"class":315},"Bold",[274,1600,1601],{"class":291},"())\n",[274,1603,1604],{"class":276,"line":1166},[274,1605,1606],{"class":291},"        })\n",[274,1608,1609],{"class":276,"line":1185},[274,1610,1611],{"class":291},"    })\n",[274,1613,1614],{"class":276,"line":1215},[274,1615,306],{"emptyLinePlaceholder":305},[274,1617,1618,1621,1623,1625,1627,1629,1631,1634],{"class":276,"line":1230},[274,1619,1620],{"class":331},"    data",[274,1622,438],{"class":291},[274,1624,332],{"class":331},[274,1626,335],{"class":291},[274,1628,1482],{"class":331},[274,1630,341],{"class":291},[274,1632,1633],{"class":315},"Generate",[274,1635,1109],{"class":291},[274,1637,1638,1640,1642,1644,1646],{"class":276,"line":1235},[274,1639,328],{"class":287},[274,1641,332],{"class":331},[274,1643,374],{"class":291},[274,1645,377],{"class":291},[274,1647,322],{"class":291},[274,1649,1650,1652,1654,1656,1658,1660],{"class":276,"line":1240},[274,1651,385],{"class":331},[274,1653,341],{"class":291},[274,1655,390],{"class":315},[274,1657,347],{"class":291},[274,1659,395],{"class":331},[274,1661,398],{"class":291},[274,1663,1664],{"class":276,"line":1275},[274,1665,404],{"class":291},[274,1667,1668,1670,1672,1674,1676,1678,1681,1683,1685,1687,1689,1691,1694,1696,1699,1701,1703,1705,1707],{"class":276,"line":1290},[274,1669,328],{"class":287},[274,1671,332],{"class":331},[274,1673,335],{"class":291},[274,1675,446],{"class":331},[274,1677,341],{"class":291},[274,1679,1680],{"class":315},"WriteFile",[274,1682,347],{"class":291},[274,1684,360],{"class":291},[274,1686,1260],{"class":363},[274,1688,360],{"class":291},[274,1690,438],{"class":291},[274,1692,1693],{"class":331}," data",[274,1695,438],{"class":291},[274,1697,1698],{"class":1179}," 0o644",[274,1700,501],{"class":291},[274,1702,332],{"class":331},[274,1704,374],{"class":291},[274,1706,377],{"class":291},[274,1708,322],{"class":291},[274,1710,1711,1713,1715,1717,1719,1721],{"class":276,"line":1295},[274,1712,385],{"class":331},[274,1714,341],{"class":291},[274,1716,390],{"class":315},[274,1718,347],{"class":291},[274,1720,395],{"class":331},[274,1722,398],{"class":291},[274,1724,1726],{"class":276,"line":1725},31,[274,1727,404],{"class":291},[274,1729,1731],{"class":276,"line":1730},32,[274,1732,410],{"class":291},[19,1734,1735,1736,1738,1739,1741],{},"三处区别。",[38,1737,537],{}," 块没了 — 没密钥，没环境变量。构建用选项而不是修改构建器。文本住在行和列里，而不是后面再 draw 的独立 ",[38,1740,890],{},"。栅格在做布局，不需要选坐标。",[14,1743,1745],{"id":1744},"before-after-2-带样式的发票明细表格","Before / After 2: 带样式的发票明细表格",[19,1747,1748,1749,1751,1752,1755,1756,763,1759,1762],{},"unipdf 的 creator API 在做表格时会变长。构造一个 ",[38,1750,894],{},"，按绝对比例 ",[38,1753,1754],{},"SetColumnWidths","，逐个用 ",[38,1757,1758],{},"NewCell",[38,1760,1761],{},"SetContent"," 构建单元格，手动配置每个单元格的边框和对齐。",[19,1764,1765],{},[22,1766,923],{},[265,1768,1770],{"className":267,"code":1769,"language":269,"meta":270,"style":270},"table := c.NewTable(4)\ntable.SetColumnWidths(0.5, 0.15, 0.15, 0.2)\n\nheaderStyle := c.NewTextStyle()\nheaderStyle.Font, _ = model.NewStandard14Font(\"Helvetica-Bold\")\nheaderStyle.FontSize = 11\nheaderStyle.Color = creator.ColorWhite\n\ndrawHeaderCell := func(text string) {\n    cell := table.NewCell()\n    cell.SetBackgroundColor(creator.ColorRGBFromHex(\"#1A237E\"))\n    cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 0.5)\n\n    p := c.NewStyledParagraph()\n    chunk := p.Append(text)\n    chunk.Style = headerStyle\n    cell.SetContent(p)\n}\n\nfor _, h := range []string{\"项目\", \"数量\", \"单价\", \"金额\"} {\n    drawHeaderCell(h)\n}\n\nfor _, row := range items {\n    for _, cellText := range row {\n        cell := table.NewCell()\n        cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 0.3)\n\n        p := c.NewParagraph(cellText)\n        p.SetFontSize(11)\n        cell.SetContent(p)\n    }\n}\n\nif err := c.Draw(table); err != nil {\n    log.Fatal(err)\n}\n",[38,1771,1772,1793,1822,1826,1842,1878,1892,1910,1914,1933,1949,1980,2014,2018,2033,2054,2069,2083,2087,2091,2156,2168,2172,2176,2197,2217,2232,2264,2268,2288,2304,2318,2322,2327,2332,2362,2378],{"__ignoreMap":270},[274,1773,1774,1777,1779,1781,1783,1786,1788,1791],{"class":276,"line":277},[274,1775,1776],{"class":331},"table ",[274,1778,335],{"class":291},[274,1780,1147],{"class":331},[274,1782,341],{"class":291},[274,1784,1785],{"class":315},"NewTable",[274,1787,347],{"class":291},[274,1789,1790],{"class":1179},"4",[274,1792,398],{"class":291},[274,1794,1795,1797,1799,1801,1803,1806,1808,1811,1813,1815,1817,1820],{"class":276,"line":284},[274,1796,122],{"class":331},[274,1798,341],{"class":291},[274,1800,1754],{"class":315},[274,1802,347],{"class":291},[274,1804,1805],{"class":1179},"0.5",[274,1807,438],{"class":291},[274,1809,1810],{"class":1179}," 0.15",[274,1812,438],{"class":291},[274,1814,1810],{"class":1179},[274,1816,438],{"class":291},[274,1818,1819],{"class":1179}," 0.2",[274,1821,398],{"class":291},[274,1823,1824],{"class":276,"line":302},[274,1825,306],{"emptyLinePlaceholder":305},[274,1827,1828,1831,1833,1835,1837,1840],{"class":276,"line":309},[274,1829,1830],{"class":331},"headerStyle ",[274,1832,335],{"class":291},[274,1834,1147],{"class":331},[274,1836,341],{"class":291},[274,1838,1839],{"class":315},"NewTextStyle",[274,1841,1109],{"class":291},[274,1843,1844,1847,1849,1852,1854,1856,1859,1862,1864,1867,1869,1871,1874,1876],{"class":276,"line":325},[274,1845,1846],{"class":331},"headerStyle",[274,1848,341],{"class":291},[274,1850,1851],{"class":331},"Font",[274,1853,438],{"class":291},[274,1855,441],{"class":331},[274,1857,1858],{"class":291},"=",[274,1860,1861],{"class":331}," model",[274,1863,341],{"class":291},[274,1865,1866],{"class":315},"NewStandard14Font",[274,1868,347],{"class":291},[274,1870,360],{"class":291},[274,1872,1873],{"class":363},"Helvetica-Bold",[274,1875,360],{"class":291},[274,1877,398],{"class":291},[274,1879,1880,1882,1884,1887,1889],{"class":276,"line":382},[274,1881,1846],{"class":331},[274,1883,341],{"class":291},[274,1885,1886],{"class":331},"FontSize ",[274,1888,1858],{"class":291},[274,1890,1891],{"class":1179}," 11\n",[274,1893,1894,1896,1898,1901,1903,1905,1907],{"class":276,"line":401},[274,1895,1846],{"class":331},[274,1897,341],{"class":291},[274,1899,1900],{"class":331},"Color ",[274,1902,1858],{"class":291},[274,1904,1101],{"class":331},[274,1906,341],{"class":291},[274,1908,1909],{"class":331},"ColorWhite\n",[274,1911,1912],{"class":276,"line":407},[274,1913,306],{"emptyLinePlaceholder":305},[274,1915,1916,1919,1921,1923,1926,1929,1931],{"class":276,"line":989},[274,1917,1918],{"class":331},"drawHeaderCell ",[274,1920,335],{"class":291},[274,1922,1541],{"class":291},[274,1924,1925],{"class":1505},"text",[274,1927,1928],{"class":482}," string",[274,1930,586],{"class":291},[274,1932,322],{"class":291},[274,1934,1935,1938,1940,1943,1945,1947],{"class":276,"line":994},[274,1936,1937],{"class":331},"    cell ",[274,1939,335],{"class":291},[274,1941,1942],{"class":331}," table",[274,1944,341],{"class":291},[274,1946,1758],{"class":315},[274,1948,1109],{"class":291},[274,1950,1951,1954,1956,1959,1961,1963,1965,1968,1970,1972,1975,1977],{"class":276,"line":999},[274,1952,1953],{"class":331},"    cell",[274,1955,341],{"class":291},[274,1957,1958],{"class":315},"SetBackgroundColor",[274,1960,347],{"class":291},[274,1962,44],{"class":331},[274,1964,341],{"class":291},[274,1966,1967],{"class":315},"ColorRGBFromHex",[274,1969,347],{"class":291},[274,1971,360],{"class":291},[274,1973,1974],{"class":363},"#1A237E",[274,1976,360],{"class":291},[274,1978,1979],{"class":291},"))\n",[274,1981,1982,1984,1986,1989,1991,1993,1995,1998,2000,2002,2004,2007,2009,2012],{"class":276,"line":1010},[274,1983,1953],{"class":331},[274,1985,341],{"class":291},[274,1987,1988],{"class":315},"SetBorder",[274,1990,347],{"class":291},[274,1992,44],{"class":331},[274,1994,341],{"class":291},[274,1996,1997],{"class":331},"CellBorderSideAll",[274,1999,438],{"class":291},[274,2001,1101],{"class":331},[274,2003,341],{"class":291},[274,2005,2006],{"class":331},"CellBorderStyleSingle",[274,2008,438],{"class":291},[274,2010,2011],{"class":1179}," 0.5",[274,2013,398],{"class":291},[274,2015,2016],{"class":276,"line":1051},[274,2017,306],{"emptyLinePlaceholder":305},[274,2019,2020,2022,2024,2026,2028,2031],{"class":276,"line":1066},[274,2021,1142],{"class":331},[274,2023,335],{"class":291},[274,2025,1147],{"class":331},[274,2027,341],{"class":291},[274,2029,2030],{"class":315},"NewStyledParagraph",[274,2032,1109],{"class":291},[274,2034,2035,2038,2040,2043,2045,2048,2050,2052],{"class":276,"line":1071},[274,2036,2037],{"class":331},"    chunk ",[274,2039,335],{"class":291},[274,2041,2042],{"class":331}," p",[274,2044,341],{"class":291},[274,2046,2047],{"class":315},"Append",[274,2049,347],{"class":291},[274,2051,1925],{"class":331},[274,2053,398],{"class":291},[274,2055,2056,2059,2061,2064,2066],{"class":276,"line":1076},[274,2057,2058],{"class":331},"    chunk",[274,2060,341],{"class":291},[274,2062,2063],{"class":331},"Style ",[274,2065,1858],{"class":291},[274,2067,2068],{"class":331}," headerStyle\n",[274,2070,2071,2073,2075,2077,2079,2081],{"class":276,"line":1081},[274,2072,1953],{"class":331},[274,2074,341],{"class":291},[274,2076,1761],{"class":315},[274,2078,347],{"class":291},[274,2080,19],{"class":331},[274,2082,398],{"class":291},[274,2084,2085],{"class":276,"line":1093},[274,2086,410],{"class":291},[274,2088,2089],{"class":276,"line":1112},[274,2090,306],{"emptyLinePlaceholder":305},[274,2092,2093,2096,2099,2101,2104,2106,2109,2112,2114,2117,2119,2122,2124,2126,2128,2131,2133,2135,2137,2140,2142,2144,2146,2149,2151,2154],{"class":276,"line":1134},[274,2094,2095],{"class":287},"for",[274,2097,2098],{"class":331}," _",[274,2100,438],{"class":291},[274,2102,2103],{"class":331}," h ",[274,2105,335],{"class":291},[274,2107,2108],{"class":287}," range",[274,2110,2111],{"class":291}," []",[274,2113,483],{"class":482},[274,2115,2116],{"class":291},"{",[274,2118,360],{"class":291},[274,2120,2121],{"class":363},"项目",[274,2123,360],{"class":291},[274,2125,438],{"class":291},[274,2127,292],{"class":291},[274,2129,2130],{"class":363},"数量",[274,2132,360],{"class":291},[274,2134,438],{"class":291},[274,2136,292],{"class":291},[274,2138,2139],{"class":363},"单价",[274,2141,360],{"class":291},[274,2143,438],{"class":291},[274,2145,292],{"class":291},[274,2147,2148],{"class":363},"金额",[274,2150,360],{"class":291},[274,2152,2153],{"class":291},"}",[274,2155,322],{"class":291},[274,2157,2158,2161,2163,2166],{"class":276,"line":1139},[274,2159,2160],{"class":315},"    drawHeaderCell",[274,2162,347],{"class":291},[274,2164,2165],{"class":331},"h",[274,2167,398],{"class":291},[274,2169,2170],{"class":276,"line":1166},[274,2171,410],{"class":291},[274,2173,2174],{"class":276,"line":1185},[274,2175,306],{"emptyLinePlaceholder":305},[274,2177,2178,2180,2182,2184,2187,2189,2191,2194],{"class":276,"line":1215},[274,2179,2095],{"class":287},[274,2181,2098],{"class":331},[274,2183,438],{"class":291},[274,2185,2186],{"class":331}," row ",[274,2188,335],{"class":291},[274,2190,2108],{"class":287},[274,2192,2193],{"class":331}," items ",[274,2195,2196],{"class":291},"{\n",[274,2198,2199,2202,2204,2206,2209,2211,2213,2215],{"class":276,"line":1230},[274,2200,2201],{"class":287},"    for",[274,2203,2098],{"class":331},[274,2205,438],{"class":291},[274,2207,2208],{"class":331}," cellText ",[274,2210,335],{"class":291},[274,2212,2108],{"class":287},[274,2214,2186],{"class":331},[274,2216,2196],{"class":291},[274,2218,2219,2222,2224,2226,2228,2230],{"class":276,"line":1235},[274,2220,2221],{"class":331},"        cell ",[274,2223,335],{"class":291},[274,2225,1942],{"class":331},[274,2227,341],{"class":291},[274,2229,1758],{"class":315},[274,2231,1109],{"class":291},[274,2233,2234,2237,2239,2241,2243,2245,2247,2249,2251,2253,2255,2257,2259,2262],{"class":276,"line":1240},[274,2235,2236],{"class":331},"        cell",[274,2238,341],{"class":291},[274,2240,1988],{"class":315},[274,2242,347],{"class":291},[274,2244,44],{"class":331},[274,2246,341],{"class":291},[274,2248,1997],{"class":331},[274,2250,438],{"class":291},[274,2252,1101],{"class":331},[274,2254,341],{"class":291},[274,2256,2006],{"class":331},[274,2258,438],{"class":291},[274,2260,2261],{"class":1179}," 0.3",[274,2263,398],{"class":291},[274,2265,2266],{"class":276,"line":1275},[274,2267,306],{"emptyLinePlaceholder":305},[274,2269,2270,2273,2275,2277,2279,2281,2283,2286],{"class":276,"line":1290},[274,2271,2272],{"class":331},"        p ",[274,2274,335],{"class":291},[274,2276,1147],{"class":331},[274,2278,341],{"class":291},[274,2280,1152],{"class":315},[274,2282,347],{"class":291},[274,2284,2285],{"class":331},"cellText",[274,2287,398],{"class":291},[274,2289,2290,2293,2295,2297,2299,2302],{"class":276,"line":1295},[274,2291,2292],{"class":331},"        p",[274,2294,341],{"class":291},[274,2296,1174],{"class":315},[274,2298,347],{"class":291},[274,2300,2301],{"class":1179},"11",[274,2303,398],{"class":291},[274,2305,2306,2308,2310,2312,2314,2316],{"class":276,"line":1725},[274,2307,2236],{"class":331},[274,2309,341],{"class":291},[274,2311,1761],{"class":315},[274,2313,347],{"class":291},[274,2315,19],{"class":331},[274,2317,398],{"class":291},[274,2319,2320],{"class":276,"line":1730},[274,2321,404],{"class":291},[274,2323,2325],{"class":276,"line":2324},33,[274,2326,410],{"class":291},[274,2328,2330],{"class":276,"line":2329},34,[274,2331,306],{"emptyLinePlaceholder":305},[274,2333,2335,2338,2340,2342,2344,2346,2348,2350,2352,2354,2356,2358,2360],{"class":276,"line":2334},35,[274,2336,2337],{"class":287},"if",[274,2339,332],{"class":331},[274,2341,335],{"class":291},[274,2343,1147],{"class":331},[274,2345,341],{"class":291},[274,2347,1198],{"class":315},[274,2349,347],{"class":291},[274,2351,122],{"class":331},[274,2353,501],{"class":291},[274,2355,332],{"class":331},[274,2357,374],{"class":291},[274,2359,377],{"class":291},[274,2361,322],{"class":291},[274,2363,2365,2368,2370,2372,2374,2376],{"class":276,"line":2364},36,[274,2366,2367],{"class":331},"    log",[274,2369,341],{"class":291},[274,2371,390],{"class":315},[274,2373,347],{"class":291},[274,2375,395],{"class":331},[274,2377,398],{"class":291},[274,2379,2381],{"class":276,"line":2380},37,[274,2382,410],{"class":291},[19,2384,2385],{},"边框、每单元格内容、画表头的循环 — 全是机械操作。",[19,2387,2388],{},[22,2389,1302],{},[265,2391,2393],{"className":267,"code":2392,"language":269,"meta":270,"style":270},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"项目\", \"数量\", \"单价\", \"金额\"},\n            [][]string{\n                {\"前端开发\", \"40 小时\", \"¥1,500\", \"¥60,000\"},\n                {\"后端开发\", \"60 小时\", \"¥1,500\", \"¥90,000\"},\n                {\"UI 设计\", \"20 小时\", \"¥1,200\", \"¥24,000\"},\n            },\n            template.ColumnWidths(50, 15, 15, 20),\n            template.TableHeaderStyle(\n                template.Bold(),\n                template.TextColor(pdf.White),\n                template.BgColor(pdf.RGBHex(0x1A237E)),\n            ),\n            template.TableStripe(pdf.RGBHex(0xF5F5F5)),\n        )\n    })\n})\n",[38,2394,2395,2420,2451,2462,2504,2513,2554,2593,2633,2638,2669,2680,2692,2713,2739,2744,2768,2773,2777],{"__ignoreMap":270},[274,2396,2397,2400,2402,2404,2406,2408,2410,2412,2414,2416,2418],{"class":276,"line":277},[274,2398,2399],{"class":331},"page",[274,2401,341],{"class":291},[274,2403,1499],{"class":315},[274,2405,1502],{"class":291},[274,2407,1506],{"class":1505},[274,2409,1509],{"class":291},[274,2411,1512],{"class":295},[274,2413,341],{"class":291},[274,2415,1517],{"class":295},[274,2417,586],{"class":291},[274,2419,322],{"class":291},[274,2421,2422,2425,2427,2429,2431,2433,2435,2437,2439,2441,2443,2445,2447,2449],{"class":276,"line":284},[274,2423,2424],{"class":331},"    r",[274,2426,341],{"class":291},[274,2428,1531],{"class":315},[274,2430,347],{"class":291},[274,2432,1536],{"class":1179},[274,2434,438],{"class":291},[274,2436,1541],{"class":291},[274,2438,1544],{"class":1505},[274,2440,1509],{"class":291},[274,2442,1512],{"class":295},[274,2444,341],{"class":291},[274,2446,1553],{"class":295},[274,2448,586],{"class":291},[274,2450,322],{"class":291},[274,2452,2453,2456,2458,2460],{"class":276,"line":302},[274,2454,2455],{"class":331},"        c",[274,2457,341],{"class":291},[274,2459,894],{"class":315},[274,2461,1405],{"class":291},[274,2463,2464,2467,2469,2471,2473,2475,2477,2479,2481,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501],{"class":276,"line":309},[274,2465,2466],{"class":291},"            []",[274,2468,483],{"class":482},[274,2470,2116],{"class":291},[274,2472,360],{"class":291},[274,2474,2121],{"class":363},[274,2476,360],{"class":291},[274,2478,438],{"class":291},[274,2480,292],{"class":291},[274,2482,2130],{"class":363},[274,2484,360],{"class":291},[274,2486,438],{"class":291},[274,2488,292],{"class":291},[274,2490,2139],{"class":363},[274,2492,360],{"class":291},[274,2494,438],{"class":291},[274,2496,292],{"class":291},[274,2498,2148],{"class":363},[274,2500,360],{"class":291},[274,2502,2503],{"class":291},"},\n",[274,2505,2506,2509,2511],{"class":276,"line":325},[274,2507,2508],{"class":291},"            [][]",[274,2510,483],{"class":482},[274,2512,2196],{"class":291},[274,2514,2515,2518,2520,2523,2525,2527,2529,2532,2534,2536,2538,2541,2543,2545,2547,2550,2552],{"class":276,"line":382},[274,2516,2517],{"class":291},"                {",[274,2519,360],{"class":291},[274,2521,2522],{"class":363},"前端开发",[274,2524,360],{"class":291},[274,2526,438],{"class":291},[274,2528,292],{"class":291},[274,2530,2531],{"class":363},"40 小时",[274,2533,360],{"class":291},[274,2535,438],{"class":291},[274,2537,292],{"class":291},[274,2539,2540],{"class":363},"¥1,500",[274,2542,360],{"class":291},[274,2544,438],{"class":291},[274,2546,292],{"class":291},[274,2548,2549],{"class":363},"¥60,000",[274,2551,360],{"class":291},[274,2553,2503],{"class":291},[274,2555,2556,2558,2560,2563,2565,2567,2569,2572,2574,2576,2578,2580,2582,2584,2586,2589,2591],{"class":276,"line":401},[274,2557,2517],{"class":291},[274,2559,360],{"class":291},[274,2561,2562],{"class":363},"后端开发",[274,2564,360],{"class":291},[274,2566,438],{"class":291},[274,2568,292],{"class":291},[274,2570,2571],{"class":363},"60 小时",[274,2573,360],{"class":291},[274,2575,438],{"class":291},[274,2577,292],{"class":291},[274,2579,2540],{"class":363},[274,2581,360],{"class":291},[274,2583,438],{"class":291},[274,2585,292],{"class":291},[274,2587,2588],{"class":363},"¥90,000",[274,2590,360],{"class":291},[274,2592,2503],{"class":291},[274,2594,2595,2597,2599,2602,2604,2606,2608,2611,2613,2615,2617,2620,2622,2624,2626,2629,2631],{"class":276,"line":407},[274,2596,2517],{"class":291},[274,2598,360],{"class":291},[274,2600,2601],{"class":363},"UI 设计",[274,2603,360],{"class":291},[274,2605,438],{"class":291},[274,2607,292],{"class":291},[274,2609,2610],{"class":363},"20 小时",[274,2612,360],{"class":291},[274,2614,438],{"class":291},[274,2616,292],{"class":291},[274,2618,2619],{"class":363},"¥1,200",[274,2621,360],{"class":291},[274,2623,438],{"class":291},[274,2625,292],{"class":291},[274,2627,2628],{"class":363},"¥24,000",[274,2630,360],{"class":291},[274,2632,2503],{"class":291},[274,2634,2635],{"class":276,"line":989},[274,2636,2637],{"class":291},"            },\n",[274,2639,2640,2643,2645,2648,2650,2653,2655,2658,2660,2662,2664,2667],{"class":276,"line":994},[274,2641,2642],{"class":331},"            template",[274,2644,341],{"class":291},[274,2646,2647],{"class":315},"ColumnWidths",[274,2649,347],{"class":291},[274,2651,2652],{"class":1179},"50",[274,2654,438],{"class":291},[274,2656,2657],{"class":1179}," 15",[274,2659,438],{"class":291},[274,2661,2657],{"class":1179},[274,2663,438],{"class":291},[274,2665,2666],{"class":1179}," 20",[274,2668,1428],{"class":291},[274,2670,2671,2673,2675,2678],{"class":276,"line":999},[274,2672,2642],{"class":331},[274,2674,341],{"class":291},[274,2676,2677],{"class":315},"TableHeaderStyle",[274,2679,1405],{"class":291},[274,2681,2682,2685,2687,2689],{"class":276,"line":1010},[274,2683,2684],{"class":331},"                template",[274,2686,341],{"class":291},[274,2688,1598],{"class":315},[274,2690,2691],{"class":291},"(),\n",[274,2693,2694,2696,2698,2701,2703,2706,2708,2711],{"class":276,"line":1051},[274,2695,2684],{"class":331},[274,2697,341],{"class":291},[274,2699,2700],{"class":315},"TextColor",[274,2702,347],{"class":291},[274,2704,2705],{"class":331},"pdf",[274,2707,341],{"class":291},[274,2709,2710],{"class":331},"White",[274,2712,1428],{"class":291},[274,2714,2715,2717,2719,2722,2724,2726,2728,2731,2733,2736],{"class":276,"line":1066},[274,2716,2684],{"class":331},[274,2718,341],{"class":291},[274,2720,2721],{"class":315},"BgColor",[274,2723,347],{"class":291},[274,2725,2705],{"class":331},[274,2727,341],{"class":291},[274,2729,2730],{"class":315},"RGBHex",[274,2732,347],{"class":291},[274,2734,2735],{"class":1179},"0x1A237E",[274,2737,2738],{"class":291},")),\n",[274,2740,2741],{"class":276,"line":1071},[274,2742,2743],{"class":291},"            ),\n",[274,2745,2746,2748,2750,2753,2755,2757,2759,2761,2763,2766],{"class":276,"line":1076},[274,2747,2642],{"class":331},[274,2749,341],{"class":291},[274,2751,2752],{"class":315},"TableStripe",[274,2754,347],{"class":291},[274,2756,2705],{"class":331},[274,2758,341],{"class":291},[274,2760,2730],{"class":315},[274,2762,347],{"class":291},[274,2764,2765],{"class":1179},"0xF5F5F5",[274,2767,2738],{"class":291},[274,2769,2770],{"class":276,"line":1081},[274,2771,2772],{"class":291},"        )\n",[274,2774,2775],{"class":276,"line":1093},[274,2776,1611],{"class":291},[274,2778,2779],{"class":276,"line":1112},[274,2780,2781],{"class":291},"})\n",[19,2783,2784,2786,2787,2790,2791,2794],{},[38,2785,2647],{}," 是 ",[22,2788,2789],{},"该表格所在列宽度的百分比","，而不是页面的绝对比例。把同一表格放进 ",[38,2792,2793],{},"r.Col(6, ...)","，百分比依然成立 — 表格占据行的一半，列按比例重新分配。分页自动处理；如果正文越过下边距，下一页会自动重复表头，无需手动接线。",[19,2796,2797,2798,2801,2802,2805],{},"一个具体细节值得提。unipdf 的 Table 在 100 行发票运行的基准里大约 ",[22,2799,2800],{},"8.6 ms"," 一次渲染。gpdf 的相同负载在 ",[22,2803,2804],{},"108 µs"," 完成 — 大约 80 倍 — 因为布局引擎一次测量每行并单遍写出页面，而不是逐单元格地物化 DOM。单张发票看不出差。批量 cron 报表运行下，决定了你需不需要队列。",[19,2807,2808,2809,2812],{},"中国的增值税发票格式 (税号、税率、税额) 在排版上 gpdf 完全能表达。如果还需要 PDF 章 (电子签章) 或时间戳，用 ",[38,2810,2811],{},"gpdf.SignDocument"," 的 RFC 3161 TSA 选项处理。",[14,2814,2816],{"id":2815},"before-after-3-中文文本无需-composite-font-仪式","Before / After 3: 中文文本，无需 composite font 仪式",[19,2818,2819],{},"unipdf 支持 CJK，但路径啰嗦。你从磁盘上的 TTF 构造 composite font，设为 style font，传过每个 paragraph。要 fallback 自己接线。",[19,2821,2822],{},[22,2823,923],{},[265,2825,2827],{"className":267,"code":2826,"language":269,"meta":270,"style":270},"font, err := model.NewCompositePdfFontFromTTFFile(\"NotoSansSC-Regular.ttf\")\nif err != nil {\n    log.Fatal(err)\n}\n\nc := creator.New()\nc.SetPageSize(creator.PageSizeA4)\n\nstyle := c.NewTextStyle()\nstyle.Font = font\nstyle.FontSize = 14\n\np := c.NewStyledParagraph()\np.Append(\"你好，世界。\").Style = style\nif err := c.Draw(p); err != nil {\n    log.Fatal(err)\n}\n\nc.WriteToFile(\"zh.pdf\")\n",[38,2828,2829,2858,2870,2884,2888,2892,2907,2925,2929,2944,2959,2972,2976,2991,3018,3046,3060,3064,3068],{"__ignoreMap":270},[274,2830,2831,2834,2836,2838,2840,2842,2844,2847,2849,2851,2854,2856],{"class":276,"line":277},[274,2832,2833],{"class":331},"font",[274,2835,438],{"class":291},[274,2837,332],{"class":331},[274,2839,335],{"class":291},[274,2841,1861],{"class":331},[274,2843,341],{"class":291},[274,2845,2846],{"class":315},"NewCompositePdfFontFromTTFFile",[274,2848,347],{"class":291},[274,2850,360],{"class":291},[274,2852,2853],{"class":363},"NotoSansSC-Regular.ttf",[274,2855,360],{"class":291},[274,2857,398],{"class":291},[274,2859,2860,2862,2864,2866,2868],{"class":276,"line":284},[274,2861,2337],{"class":287},[274,2863,332],{"class":331},[274,2865,374],{"class":291},[274,2867,377],{"class":291},[274,2869,322],{"class":291},[274,2871,2872,2874,2876,2878,2880,2882],{"class":276,"line":302},[274,2873,2367],{"class":331},[274,2875,341],{"class":291},[274,2877,390],{"class":315},[274,2879,347],{"class":291},[274,2881,395],{"class":331},[274,2883,398],{"class":291},[274,2885,2886],{"class":276,"line":309},[274,2887,410],{"class":291},[274,2889,2890],{"class":276,"line":325},[274,2891,306],{"emptyLinePlaceholder":305},[274,2893,2894,2897,2899,2901,2903,2905],{"class":276,"line":382},[274,2895,2896],{"class":331},"c ",[274,2898,335],{"class":291},[274,2900,1101],{"class":331},[274,2902,341],{"class":291},[274,2904,1106],{"class":315},[274,2906,1109],{"class":291},[274,2908,2909,2911,2913,2915,2917,2919,2921,2923],{"class":276,"line":401},[274,2910,1544],{"class":331},[274,2912,341],{"class":291},[274,2914,1120],{"class":315},[274,2916,347],{"class":291},[274,2918,44],{"class":331},[274,2920,341],{"class":291},[274,2922,1129],{"class":331},[274,2924,398],{"class":291},[274,2926,2927],{"class":276,"line":407},[274,2928,306],{"emptyLinePlaceholder":305},[274,2930,2931,2934,2936,2938,2940,2942],{"class":276,"line":989},[274,2932,2933],{"class":331},"style ",[274,2935,335],{"class":291},[274,2937,1147],{"class":331},[274,2939,341],{"class":291},[274,2941,1839],{"class":315},[274,2943,1109],{"class":291},[274,2945,2946,2949,2951,2954,2956],{"class":276,"line":994},[274,2947,2948],{"class":331},"style",[274,2950,341],{"class":291},[274,2952,2953],{"class":331},"Font ",[274,2955,1858],{"class":291},[274,2957,2958],{"class":331}," font\n",[274,2960,2961,2963,2965,2967,2969],{"class":276,"line":999},[274,2962,2948],{"class":331},[274,2964,341],{"class":291},[274,2966,1886],{"class":331},[274,2968,1858],{"class":291},[274,2970,2971],{"class":1179}," 14\n",[274,2973,2974],{"class":276,"line":1010},[274,2975,306],{"emptyLinePlaceholder":305},[274,2977,2978,2981,2983,2985,2987,2989],{"class":276,"line":1051},[274,2979,2980],{"class":331},"p ",[274,2982,335],{"class":291},[274,2984,1147],{"class":331},[274,2986,341],{"class":291},[274,2988,2030],{"class":315},[274,2990,1109],{"class":291},[274,2992,2993,2995,2997,2999,3001,3003,3006,3008,3011,3013,3015],{"class":276,"line":1066},[274,2994,19],{"class":331},[274,2996,341],{"class":291},[274,2998,2047],{"class":315},[274,3000,347],{"class":291},[274,3002,360],{"class":291},[274,3004,3005],{"class":363},"你好，世界。",[274,3007,360],{"class":291},[274,3009,3010],{"class":291},").",[274,3012,2063],{"class":331},[274,3014,1858],{"class":291},[274,3016,3017],{"class":331}," style\n",[274,3019,3020,3022,3024,3026,3028,3030,3032,3034,3036,3038,3040,3042,3044],{"class":276,"line":1071},[274,3021,2337],{"class":287},[274,3023,332],{"class":331},[274,3025,335],{"class":291},[274,3027,1147],{"class":331},[274,3029,341],{"class":291},[274,3031,1198],{"class":315},[274,3033,347],{"class":291},[274,3035,19],{"class":331},[274,3037,501],{"class":291},[274,3039,332],{"class":331},[274,3041,374],{"class":291},[274,3043,377],{"class":291},[274,3045,322],{"class":291},[274,3047,3048,3050,3052,3054,3056,3058],{"class":276,"line":1076},[274,3049,2367],{"class":331},[274,3051,341],{"class":291},[274,3053,390],{"class":315},[274,3055,347],{"class":291},[274,3057,395],{"class":331},[274,3059,398],{"class":291},[274,3061,3062],{"class":276,"line":1081},[274,3063,410],{"class":291},[274,3065,3066],{"class":276,"line":1093},[274,3067,306],{"emptyLinePlaceholder":305},[274,3069,3070,3072,3074,3076,3078,3080,3083,3085],{"class":276,"line":1112},[274,3071,1544],{"class":331},[274,3073,341],{"class":291},[274,3075,1253],{"class":315},[274,3077,347],{"class":291},[274,3079,360],{"class":291},[274,3081,3082],{"class":363},"zh.pdf",[274,3084,360],{"class":291},[274,3086,398],{"class":291},[19,3088,3089,3090,3092],{},"TTF 必须在你给的路径上、运行时、运行二进制的主机上存在。容器镜像得带上字体。",[38,3091,2846],{}," 必须在使用字体的 draw 调用之前发生，意味着它要么放在全局，要么作为依赖到处传。",[19,3094,3095],{},[22,3096,1302],{},[265,3098,3100],{"className":267,"code":3099,"language":269,"meta":270,"style":270},"package main\n\nimport (\n    _ \"embed\"\n    \"log\"\n    \"os\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\n//go:embed NotoSansSC-Regular.ttf\nvar notoSC []byte\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansSC\", notoSC),\n        gpdf.WithDefaultFont(\"NotoSansSC\", 14),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"你好，世界。\")\n            c.Text(\"床前明月光，疑是地上霜。\")\n            c.Text(\"北京市朝阳区建国门外大街 1 号\")\n        })\n    })\n\n    data, _ := doc.Generate()\n    if err := os.WriteFile(\"zh.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,3101,3102,3108,3112,3118,3130,3138,3146,3150,3158,3166,3174,3178,3182,3187,3201,3205,3215,3229,3247,3277,3302,3326,3330,3334,3348,3372,3402,3420,3439,3458,3462,3466,3470,3488,3528,3542,3546],{"__ignoreMap":270},[274,3103,3104,3106],{"class":276,"line":277},[274,3105,933],{"class":291},[274,3107,936],{"class":295},[274,3109,3110],{"class":276,"line":284},[274,3111,306],{"emptyLinePlaceholder":305},[274,3113,3114,3116],{"class":276,"line":302},[274,3115,288],{"class":287},[274,3117,947],{"class":291},[274,3119,3120,3123,3125,3128],{"class":276,"line":309},[274,3121,3122],{"class":331},"    _ ",[274,3124,360],{"class":291},[274,3126,3127],{"class":295},"embed",[274,3129,299],{"class":291},[274,3131,3132,3134,3136],{"class":276,"line":325},[274,3133,952],{"class":291},[274,3135,955],{"class":295},[274,3137,299],{"class":291},[274,3139,3140,3142,3144],{"class":276,"line":382},[274,3141,952],{"class":291},[274,3143,350],{"class":295},[274,3145,299],{"class":291},[274,3147,3148],{"class":276,"line":401},[274,3149,306],{"emptyLinePlaceholder":305},[274,3151,3152,3154,3156],{"class":276,"line":407},[274,3153,952],{"class":291},[274,3155,544],{"class":295},[274,3157,299],{"class":291},[274,3159,3160,3162,3164],{"class":276,"line":989},[274,3161,952],{"class":291},[274,3163,1358],{"class":295},[274,3165,299],{"class":291},[274,3167,3168,3170,3172],{"class":276,"line":994},[274,3169,952],{"class":291},[274,3171,1367],{"class":295},[274,3173,299],{"class":291},[274,3175,3176],{"class":276,"line":999},[274,3177,398],{"class":291},[274,3179,3180],{"class":276,"line":1010},[274,3181,306],{"emptyLinePlaceholder":305},[274,3183,3184],{"class":276,"line":1051},[274,3185,3186],{"class":280},"//go:embed NotoSansSC-Regular.ttf\n",[274,3188,3189,3192,3195,3198],{"class":276,"line":1066},[274,3190,3191],{"class":291},"var",[274,3193,3194],{"class":331}," notoSC ",[274,3196,3197],{"class":291},"[]",[274,3199,3200],{"class":482},"byte\n",[274,3202,3203],{"class":276,"line":1071},[274,3204,306],{"emptyLinePlaceholder":305},[274,3206,3207,3209,3211,3213],{"class":276,"line":1076},[274,3208,312],{"class":291},[274,3210,1086],{"class":315},[274,3212,319],{"class":291},[274,3214,322],{"class":291},[274,3216,3217,3219,3221,3223,3225,3227],{"class":276,"line":1081},[274,3218,1392],{"class":331},[274,3220,335],{"class":291},[274,3222,1397],{"class":331},[274,3224,341],{"class":291},[274,3226,1402],{"class":315},[274,3228,1405],{"class":291},[274,3230,3231,3233,3235,3237,3239,3241,3243,3245],{"class":276,"line":1093},[274,3232,1410],{"class":331},[274,3234,341],{"class":291},[274,3236,1415],{"class":315},[274,3238,347],{"class":291},[274,3240,1420],{"class":331},[274,3242,341],{"class":291},[274,3244,1425],{"class":331},[274,3246,1428],{"class":291},[274,3248,3249,3251,3253,3255,3257,3259,3261,3263,3265,3267,3269,3271,3273,3275],{"class":276,"line":1112},[274,3250,1410],{"class":331},[274,3252,341],{"class":291},[274,3254,1437],{"class":315},[274,3256,347],{"class":291},[274,3258,1420],{"class":331},[274,3260,341],{"class":291},[274,3262,1446],{"class":315},[274,3264,347],{"class":291},[274,3266,1420],{"class":331},[274,3268,341],{"class":291},[274,3270,1455],{"class":315},[274,3272,347],{"class":291},[274,3274,1460],{"class":1179},[274,3276,1463],{"class":291},[274,3278,3279,3281,3283,3286,3288,3290,3293,3295,3297,3300],{"class":276,"line":1134},[274,3280,1410],{"class":331},[274,3282,341],{"class":291},[274,3284,3285],{"class":315},"WithFont",[274,3287,347],{"class":291},[274,3289,360],{"class":291},[274,3291,3292],{"class":363},"NotoSansSC",[274,3294,360],{"class":291},[274,3296,438],{"class":291},[274,3298,3299],{"class":331}," notoSC",[274,3301,1428],{"class":291},[274,3303,3304,3306,3308,3311,3313,3315,3317,3319,3321,3324],{"class":276,"line":1139},[274,3305,1410],{"class":331},[274,3307,341],{"class":291},[274,3309,3310],{"class":315},"WithDefaultFont",[274,3312,347],{"class":291},[274,3314,360],{"class":291},[274,3316,3292],{"class":363},[274,3318,360],{"class":291},[274,3320,438],{"class":291},[274,3322,3323],{"class":1179}," 14",[274,3325,1428],{"class":291},[274,3327,3328],{"class":276,"line":1166},[274,3329,1468],{"class":291},[274,3331,3332],{"class":276,"line":1185},[274,3333,306],{"emptyLinePlaceholder":305},[274,3335,3336,3338,3340,3342,3344,3346],{"class":276,"line":1215},[274,3337,1477],{"class":331},[274,3339,335],{"class":291},[274,3341,1482],{"class":331},[274,3343,341],{"class":291},[274,3345,1487],{"class":315},[274,3347,1109],{"class":291},[274,3349,3350,3352,3354,3356,3358,3360,3362,3364,3366,3368,3370],{"class":276,"line":1230},[274,3351,1494],{"class":331},[274,3353,341],{"class":291},[274,3355,1499],{"class":315},[274,3357,1502],{"class":291},[274,3359,1506],{"class":1505},[274,3361,1509],{"class":291},[274,3363,1512],{"class":295},[274,3365,341],{"class":291},[274,3367,1517],{"class":295},[274,3369,586],{"class":291},[274,3371,322],{"class":291},[274,3373,3374,3376,3378,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400],{"class":276,"line":1235},[274,3375,1526],{"class":331},[274,3377,341],{"class":291},[274,3379,1531],{"class":315},[274,3381,347],{"class":291},[274,3383,1536],{"class":1179},[274,3385,438],{"class":291},[274,3387,1541],{"class":291},[274,3389,1544],{"class":1505},[274,3391,1509],{"class":291},[274,3393,1512],{"class":295},[274,3395,341],{"class":291},[274,3397,1553],{"class":295},[274,3399,586],{"class":291},[274,3401,322],{"class":291},[274,3403,3404,3406,3408,3410,3412,3414,3416,3418],{"class":276,"line":1240},[274,3405,1562],{"class":331},[274,3407,341],{"class":291},[274,3409,1567],{"class":315},[274,3411,347],{"class":291},[274,3413,360],{"class":291},[274,3415,3005],{"class":363},[274,3417,360],{"class":291},[274,3419,398],{"class":291},[274,3421,3422,3424,3426,3428,3430,3432,3435,3437],{"class":276,"line":1275},[274,3423,1562],{"class":331},[274,3425,341],{"class":291},[274,3427,1567],{"class":315},[274,3429,347],{"class":291},[274,3431,360],{"class":291},[274,3433,3434],{"class":363},"床前明月光，疑是地上霜。",[274,3436,360],{"class":291},[274,3438,398],{"class":291},[274,3440,3441,3443,3445,3447,3449,3451,3454,3456],{"class":276,"line":1290},[274,3442,1562],{"class":331},[274,3444,341],{"class":291},[274,3446,1567],{"class":315},[274,3448,347],{"class":291},[274,3450,360],{"class":291},[274,3452,3453],{"class":363},"北京市朝阳区建国门外大街 1 号",[274,3455,360],{"class":291},[274,3457,398],{"class":291},[274,3459,3460],{"class":276,"line":1295},[274,3461,1606],{"class":291},[274,3463,3464],{"class":276,"line":1725},[274,3465,1611],{"class":291},[274,3467,3468],{"class":276,"line":1730},[274,3469,306],{"emptyLinePlaceholder":305},[274,3471,3472,3474,3476,3478,3480,3482,3484,3486],{"class":276,"line":2324},[274,3473,1620],{"class":331},[274,3475,438],{"class":291},[274,3477,441],{"class":331},[274,3479,335],{"class":291},[274,3481,1482],{"class":331},[274,3483,341],{"class":291},[274,3485,1633],{"class":315},[274,3487,1109],{"class":291},[274,3489,3490,3492,3494,3496,3498,3500,3502,3504,3506,3508,3510,3512,3514,3516,3518,3520,3522,3524,3526],{"class":276,"line":2329},[274,3491,328],{"class":287},[274,3493,332],{"class":331},[274,3495,335],{"class":291},[274,3497,446],{"class":331},[274,3499,341],{"class":291},[274,3501,1680],{"class":315},[274,3503,347],{"class":291},[274,3505,360],{"class":291},[274,3507,3082],{"class":363},[274,3509,360],{"class":291},[274,3511,438],{"class":291},[274,3513,1693],{"class":331},[274,3515,438],{"class":291},[274,3517,1698],{"class":1179},[274,3519,501],{"class":291},[274,3521,332],{"class":331},[274,3523,374],{"class":291},[274,3525,377],{"class":291},[274,3527,322],{"class":291},[274,3529,3530,3532,3534,3536,3538,3540],{"class":276,"line":2334},[274,3531,385],{"class":331},[274,3533,341],{"class":291},[274,3535,390],{"class":315},[274,3537,347],{"class":291},[274,3539,395],{"class":331},[274,3541,398],{"class":291},[274,3543,3544],{"class":276,"line":2364},[274,3545,404],{"class":291},[274,3547,3548],{"class":276,"line":2380},[274,3549,410],{"class":291},[19,3551,3552,3553,3556,3557,3560,3561,3564],{},"三处区别。字体是 ",[22,3554,3555],{},"字节","，不是路径 — ",[38,3558,3559],{},"//go:embed"," 把它编入二进制，运行时镜像不再需要字体目录。字体在 ",[22,3562,3563],{},"构建时注册一次","；不需要逐 paragraph 串 style。gpdf 的 TrueType 子集器理解 CJK cmap 格式 (4、6、12) 和 Identity-H 编码，所以输出 PDF 只携带你实际用到的字形。一份 200 字的中文发票产生约 30 KB 的字体子集，而非 4 MB 的完整嵌入。",[19,3566,3567],{},"关于 Source Han Sans、IPAex Gothic、fallback 链的细节，CJK 字体专题文章里讲。",[14,3569,3571],{"id":3570},"before-after-4-每页页眉与页脚页码","Before / After 4: 每页页眉与页脚页码",[19,3573,3574,3575,763,3577,3579,3580,763,3583,3586],{},"unipdf 的模式是 ",[38,3576,762],{},[38,3578,766],{},"，两者都接收一个带当前 block 和页码的上下文。页码从上下文的 ",[38,3581,3582],{},"PageNum",[38,3584,3585],{},"TotalPages"," 字段取。",[19,3588,3589],{},[22,3590,923],{},[265,3592,3594],{"className":267,"code":3593,"language":269,"meta":270,"style":270},"c.DrawHeader(func(block *creator.Block, args creator.HeaderFunctionArgs) {\n    p := c.NewParagraph(\"ACME 公司\")\n    p.SetFontSize(12)\n    p.SetPos(40, 30)\n    block.Draw(p)\n})\n\nc.DrawFooter(func(block *creator.Block, args creator.FooterFunctionArgs) {\n    p := c.NewParagraph(fmt.Sprintf(\"第 %d 页 / 共 %d 页\", args.PageNum, args.TotalPages))\n    p.SetFontSize(8)\n    p.SetPos(0, 20)\n    p.SetTextAlignment(creator.TextAlignmentCenter)\n    block.Draw(p)\n})\n",[38,3595,3596,3635,3658,3672,3693,3708,3712,3716,3751,3812,3827,3846,3866,3880],{"__ignoreMap":270},[274,3597,3598,3600,3602,3605,3607,3610,3612,3614,3616,3619,3621,3624,3626,3628,3631,3633],{"class":276,"line":277},[274,3599,1544],{"class":331},[274,3601,341],{"class":291},[274,3603,3604],{"class":315},"DrawHeader",[274,3606,1502],{"class":291},[274,3608,3609],{"class":1505},"block",[274,3611,1509],{"class":291},[274,3613,44],{"class":295},[274,3615,341],{"class":291},[274,3617,3618],{"class":295},"Block",[274,3620,438],{"class":291},[274,3622,3623],{"class":1505}," args",[274,3625,1101],{"class":295},[274,3627,341],{"class":291},[274,3629,3630],{"class":295},"HeaderFunctionArgs",[274,3632,586],{"class":291},[274,3634,322],{"class":291},[274,3636,3637,3639,3641,3643,3645,3647,3649,3651,3654,3656],{"class":276,"line":284},[274,3638,1142],{"class":331},[274,3640,335],{"class":291},[274,3642,1147],{"class":331},[274,3644,341],{"class":291},[274,3646,1152],{"class":315},[274,3648,347],{"class":291},[274,3650,360],{"class":291},[274,3652,3653],{"class":363},"ACME 公司",[274,3655,360],{"class":291},[274,3657,398],{"class":291},[274,3659,3660,3662,3664,3666,3668,3670],{"class":276,"line":302},[274,3661,1169],{"class":331},[274,3663,341],{"class":291},[274,3665,1174],{"class":315},[274,3667,347],{"class":291},[274,3669,1536],{"class":1179},[274,3671,398],{"class":291},[274,3673,3674,3676,3678,3681,3683,3686,3688,3691],{"class":276,"line":309},[274,3675,1169],{"class":331},[274,3677,341],{"class":291},[274,3679,3680],{"class":315},"SetPos",[274,3682,347],{"class":291},[274,3684,3685],{"class":1179},"40",[274,3687,438],{"class":291},[274,3689,3690],{"class":1179}," 30",[274,3692,398],{"class":291},[274,3694,3695,3698,3700,3702,3704,3706],{"class":276,"line":325},[274,3696,3697],{"class":331},"    block",[274,3699,341],{"class":291},[274,3701,1198],{"class":315},[274,3703,347],{"class":291},[274,3705,19],{"class":331},[274,3707,398],{"class":291},[274,3709,3710],{"class":276,"line":382},[274,3711,2781],{"class":291},[274,3713,3714],{"class":276,"line":401},[274,3715,306],{"emptyLinePlaceholder":305},[274,3717,3718,3720,3722,3724,3726,3728,3730,3732,3734,3736,3738,3740,3742,3744,3747,3749],{"class":276,"line":407},[274,3719,1544],{"class":331},[274,3721,341],{"class":291},[274,3723,785],{"class":315},[274,3725,1502],{"class":291},[274,3727,3609],{"class":1505},[274,3729,1509],{"class":291},[274,3731,44],{"class":295},[274,3733,341],{"class":291},[274,3735,3618],{"class":295},[274,3737,438],{"class":291},[274,3739,3623],{"class":1505},[274,3741,1101],{"class":295},[274,3743,341],{"class":291},[274,3745,3746],{"class":295},"FooterFunctionArgs",[274,3748,586],{"class":291},[274,3750,322],{"class":291},[274,3752,3753,3755,3757,3759,3761,3763,3765,3768,3770,3773,3775,3777,3780,3784,3787,3789,3792,3794,3796,3798,3800,3802,3804,3806,3808,3810],{"class":276,"line":989},[274,3754,1142],{"class":331},[274,3756,335],{"class":291},[274,3758,1147],{"class":331},[274,3760,341],{"class":291},[274,3762,1152],{"class":315},[274,3764,347],{"class":291},[274,3766,3767],{"class":331},"fmt",[274,3769,341],{"class":291},[274,3771,3772],{"class":315},"Sprintf",[274,3774,347],{"class":291},[274,3776,360],{"class":291},[274,3778,3779],{"class":363},"第 ",[274,3781,3783],{"class":3782},"swJcz","%d",[274,3785,3786],{"class":363}," 页 / 共 ",[274,3788,3783],{"class":3782},[274,3790,3791],{"class":363}," 页",[274,3793,360],{"class":291},[274,3795,438],{"class":291},[274,3797,3623],{"class":331},[274,3799,341],{"class":291},[274,3801,3582],{"class":331},[274,3803,438],{"class":291},[274,3805,3623],{"class":331},[274,3807,341],{"class":291},[274,3809,3585],{"class":331},[274,3811,1979],{"class":291},[274,3813,3814,3816,3818,3820,3822,3825],{"class":276,"line":994},[274,3815,1169],{"class":331},[274,3817,341],{"class":291},[274,3819,1174],{"class":315},[274,3821,347],{"class":291},[274,3823,3824],{"class":1179},"8",[274,3826,398],{"class":291},[274,3828,3829,3831,3833,3835,3837,3840,3842,3844],{"class":276,"line":999},[274,3830,1169],{"class":331},[274,3832,341],{"class":291},[274,3834,3680],{"class":315},[274,3836,347],{"class":291},[274,3838,3839],{"class":1179},"0",[274,3841,438],{"class":291},[274,3843,2666],{"class":1179},[274,3845,398],{"class":291},[274,3847,3848,3850,3852,3855,3857,3859,3861,3864],{"class":276,"line":1010},[274,3849,1169],{"class":331},[274,3851,341],{"class":291},[274,3853,3854],{"class":315},"SetTextAlignment",[274,3856,347],{"class":291},[274,3858,44],{"class":331},[274,3860,341],{"class":291},[274,3862,3863],{"class":331},"TextAlignmentCenter",[274,3865,398],{"class":291},[274,3867,3868,3870,3872,3874,3876,3878],{"class":276,"line":1051},[274,3869,3697],{"class":331},[274,3871,341],{"class":291},[274,3873,1198],{"class":315},[274,3875,347],{"class":291},[274,3877,19],{"class":331},[274,3879,398],{"class":291},[274,3881,3882],{"class":276,"line":1066},[274,3883,2781],{"class":291},[19,3885,3886],{},"页眉 / 页脚都是按绝对位置 draw 的 block。Y 坐标错了、边距错了 — 每次改页面尺寸都要重新对齐。",[19,3888,3889],{},[22,3890,1302],{},[265,3892,3894],{"className":267,"code":3893,"language":269,"meta":270,"style":270},"doc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n)\n\ndoc.Header(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"ACME 公司\", template.Bold(), template.FontSize(12))\n            c.Line(template.LineColor(pdf.Gray(0.7)))\n            c.Spacer(document.Mm(4))\n        })\n    })\n})\n\ndoc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"ACME 公司\",\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            c.PageNumber(template.AlignRight(),\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n\nfor i := 0; i \u003C 10; i++ {\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(fmt.Sprintf(\"第 %d 页正文。\", i+1))\n        })\n    })\n}\n",[38,3895,3896,3911,3930,3960,3964,3968,3995,4019,4049,4088,4123,4146,4150,4154,4158,4162,4187,4211,4242,4261,4295,4299,4329,4349,4383,4387,4391,4395,4399,4432,4446,4470,4500,4541,4545,4549],{"__ignoreMap":270},[274,3897,3898,3901,3903,3905,3907,3909],{"class":276,"line":277},[274,3899,3900],{"class":331},"doc ",[274,3902,335],{"class":291},[274,3904,1397],{"class":331},[274,3906,341],{"class":291},[274,3908,1402],{"class":315},[274,3910,1405],{"class":291},[274,3912,3913,3916,3918,3920,3922,3924,3926,3928],{"class":276,"line":284},[274,3914,3915],{"class":331},"    gpdf",[274,3917,341],{"class":291},[274,3919,1415],{"class":315},[274,3921,347],{"class":291},[274,3923,1420],{"class":331},[274,3925,341],{"class":291},[274,3927,1425],{"class":331},[274,3929,1428],{"class":291},[274,3931,3932,3934,3936,3938,3940,3942,3944,3946,3948,3950,3952,3954,3956,3958],{"class":276,"line":302},[274,3933,3915],{"class":331},[274,3935,341],{"class":291},[274,3937,1437],{"class":315},[274,3939,347],{"class":291},[274,3941,1420],{"class":331},[274,3943,341],{"class":291},[274,3945,1446],{"class":315},[274,3947,347],{"class":291},[274,3949,1420],{"class":331},[274,3951,341],{"class":291},[274,3953,1455],{"class":315},[274,3955,347],{"class":291},[274,3957,1460],{"class":1179},[274,3959,1463],{"class":291},[274,3961,3962],{"class":276,"line":309},[274,3963,398],{"class":291},[274,3965,3966],{"class":276,"line":325},[274,3967,306],{"emptyLinePlaceholder":305},[274,3969,3970,3973,3975,3978,3980,3982,3984,3986,3988,3991,3993],{"class":276,"line":382},[274,3971,3972],{"class":331},"doc",[274,3974,341],{"class":291},[274,3976,3977],{"class":315},"Header",[274,3979,1502],{"class":291},[274,3981,19],{"class":1505},[274,3983,1509],{"class":291},[274,3985,1512],{"class":295},[274,3987,341],{"class":291},[274,3989,3990],{"class":295},"PageBuilder",[274,3992,586],{"class":291},[274,3994,322],{"class":291},[274,3996,3997,3999,4001,4003,4005,4007,4009,4011,4013,4015,4017],{"class":276,"line":401},[274,3998,1169],{"class":331},[274,4000,341],{"class":291},[274,4002,1499],{"class":315},[274,4004,1502],{"class":291},[274,4006,1506],{"class":1505},[274,4008,1509],{"class":291},[274,4010,1512],{"class":295},[274,4012,341],{"class":291},[274,4014,1517],{"class":295},[274,4016,586],{"class":291},[274,4018,322],{"class":291},[274,4020,4021,4023,4025,4027,4029,4031,4033,4035,4037,4039,4041,4043,4045,4047],{"class":276,"line":407},[274,4022,1526],{"class":331},[274,4024,341],{"class":291},[274,4026,1531],{"class":315},[274,4028,347],{"class":291},[274,4030,1536],{"class":1179},[274,4032,438],{"class":291},[274,4034,1541],{"class":291},[274,4036,1544],{"class":1505},[274,4038,1509],{"class":291},[274,4040,1512],{"class":295},[274,4042,341],{"class":291},[274,4044,1553],{"class":295},[274,4046,586],{"class":291},[274,4048,322],{"class":291},[274,4050,4051,4053,4055,4057,4059,4061,4063,4065,4067,4069,4071,4073,4076,4078,4080,4082,4084,4086],{"class":276,"line":989},[274,4052,1562],{"class":331},[274,4054,341],{"class":291},[274,4056,1567],{"class":315},[274,4058,347],{"class":291},[274,4060,360],{"class":291},[274,4062,3653],{"class":363},[274,4064,360],{"class":291},[274,4066,438],{"class":291},[274,4068,1580],{"class":331},[274,4070,341],{"class":291},[274,4072,1598],{"class":315},[274,4074,4075],{"class":291},"(),",[274,4077,1580],{"class":331},[274,4079,341],{"class":291},[274,4081,1585],{"class":315},[274,4083,347],{"class":291},[274,4085,1536],{"class":1179},[274,4087,1979],{"class":291},[274,4089,4090,4092,4094,4097,4099,4101,4103,4106,4108,4110,4112,4115,4117,4120],{"class":276,"line":994},[274,4091,1562],{"class":331},[274,4093,341],{"class":291},[274,4095,4096],{"class":315},"Line",[274,4098,347],{"class":291},[274,4100,1512],{"class":331},[274,4102,341],{"class":291},[274,4104,4105],{"class":315},"LineColor",[274,4107,347],{"class":291},[274,4109,2705],{"class":331},[274,4111,341],{"class":291},[274,4113,4114],{"class":315},"Gray",[274,4116,347],{"class":291},[274,4118,4119],{"class":1179},"0.7",[274,4121,4122],{"class":291},")))\n",[274,4124,4125,4127,4129,4132,4134,4136,4138,4140,4142,4144],{"class":276,"line":999},[274,4126,1562],{"class":331},[274,4128,341],{"class":291},[274,4130,4131],{"class":315},"Spacer",[274,4133,347],{"class":291},[274,4135,1420],{"class":331},[274,4137,341],{"class":291},[274,4139,1455],{"class":315},[274,4141,347],{"class":291},[274,4143,1790],{"class":1179},[274,4145,1979],{"class":291},[274,4147,4148],{"class":276,"line":1010},[274,4149,1606],{"class":291},[274,4151,4152],{"class":276,"line":1051},[274,4153,1611],{"class":291},[274,4155,4156],{"class":276,"line":1066},[274,4157,2781],{"class":291},[274,4159,4160],{"class":276,"line":1071},[274,4161,306],{"emptyLinePlaceholder":305},[274,4163,4164,4166,4168,4171,4173,4175,4177,4179,4181,4183,4185],{"class":276,"line":1076},[274,4165,3972],{"class":331},[274,4167,341],{"class":291},[274,4169,4170],{"class":315},"Footer",[274,4172,1502],{"class":291},[274,4174,19],{"class":1505},[274,4176,1509],{"class":291},[274,4178,1512],{"class":295},[274,4180,341],{"class":291},[274,4182,3990],{"class":295},[274,4184,586],{"class":291},[274,4186,322],{"class":291},[274,4188,4189,4191,4193,4195,4197,4199,4201,4203,4205,4207,4209],{"class":276,"line":1081},[274,4190,1169],{"class":331},[274,4192,341],{"class":291},[274,4194,1499],{"class":315},[274,4196,1502],{"class":291},[274,4198,1506],{"class":1505},[274,4200,1509],{"class":291},[274,4202,1512],{"class":295},[274,4204,341],{"class":291},[274,4206,1517],{"class":295},[274,4208,586],{"class":291},[274,4210,322],{"class":291},[274,4212,4213,4215,4217,4219,4221,4224,4226,4228,4230,4232,4234,4236,4238,4240],{"class":276,"line":1093},[274,4214,1526],{"class":331},[274,4216,341],{"class":291},[274,4218,1531],{"class":315},[274,4220,347],{"class":291},[274,4222,4223],{"class":1179},"6",[274,4225,438],{"class":291},[274,4227,1541],{"class":291},[274,4229,1544],{"class":1505},[274,4231,1509],{"class":291},[274,4233,1512],{"class":295},[274,4235,341],{"class":291},[274,4237,1553],{"class":295},[274,4239,586],{"class":291},[274,4241,322],{"class":291},[274,4243,4244,4246,4248,4250,4252,4254,4256,4258],{"class":276,"line":1112},[274,4245,1562],{"class":331},[274,4247,341],{"class":291},[274,4249,1567],{"class":315},[274,4251,347],{"class":291},[274,4253,360],{"class":291},[274,4255,3653],{"class":363},[274,4257,360],{"class":291},[274,4259,4260],{"class":291},",\n",[274,4262,4263,4265,4267,4269,4271,4273,4275,4277,4279,4281,4283,4285,4287,4289,4291,4293],{"class":276,"line":1134},[274,4264,2684],{"class":331},[274,4266,341],{"class":291},[274,4268,1585],{"class":315},[274,4270,347],{"class":291},[274,4272,3824],{"class":1179},[274,4274,491],{"class":291},[274,4276,1580],{"class":331},[274,4278,341],{"class":291},[274,4280,2700],{"class":315},[274,4282,347],{"class":291},[274,4284,2705],{"class":331},[274,4286,341],{"class":291},[274,4288,4114],{"class":315},[274,4290,347],{"class":291},[274,4292,1805],{"class":1179},[274,4294,4122],{"class":291},[274,4296,4297],{"class":276,"line":1139},[274,4298,1606],{"class":291},[274,4300,4301,4303,4305,4307,4309,4311,4313,4315,4317,4319,4321,4323,4325,4327],{"class":276,"line":1166},[274,4302,1526],{"class":331},[274,4304,341],{"class":291},[274,4306,1531],{"class":315},[274,4308,347],{"class":291},[274,4310,4223],{"class":1179},[274,4312,438],{"class":291},[274,4314,1541],{"class":291},[274,4316,1544],{"class":1505},[274,4318,1509],{"class":291},[274,4320,1512],{"class":295},[274,4322,341],{"class":291},[274,4324,1553],{"class":295},[274,4326,586],{"class":291},[274,4328,322],{"class":291},[274,4330,4331,4333,4335,4338,4340,4342,4344,4347],{"class":276,"line":1185},[274,4332,1562],{"class":331},[274,4334,341],{"class":291},[274,4336,4337],{"class":315},"PageNumber",[274,4339,347],{"class":291},[274,4341,1512],{"class":331},[274,4343,341],{"class":291},[274,4345,4346],{"class":315},"AlignRight",[274,4348,2691],{"class":291},[274,4350,4351,4353,4355,4357,4359,4361,4363,4365,4367,4369,4371,4373,4375,4377,4379,4381],{"class":276,"line":1215},[274,4352,2684],{"class":331},[274,4354,341],{"class":291},[274,4356,1585],{"class":315},[274,4358,347],{"class":291},[274,4360,3824],{"class":1179},[274,4362,491],{"class":291},[274,4364,1580],{"class":331},[274,4366,341],{"class":291},[274,4368,2700],{"class":315},[274,4370,347],{"class":291},[274,4372,2705],{"class":331},[274,4374,341],{"class":291},[274,4376,4114],{"class":315},[274,4378,347],{"class":291},[274,4380,1805],{"class":1179},[274,4382,4122],{"class":291},[274,4384,4385],{"class":276,"line":1230},[274,4386,1606],{"class":291},[274,4388,4389],{"class":276,"line":1235},[274,4390,1611],{"class":291},[274,4392,4393],{"class":276,"line":1240},[274,4394,2781],{"class":291},[274,4396,4397],{"class":276,"line":1275},[274,4398,306],{"emptyLinePlaceholder":305},[274,4400,4401,4403,4406,4408,4411,4414,4416,4419,4422,4424,4427,4430],{"class":276,"line":1290},[274,4402,2095],{"class":287},[274,4404,4405],{"class":331}," i ",[274,4407,335],{"class":291},[274,4409,4410],{"class":1179}," 0",[274,4412,4413],{"class":291},";",[274,4415,4405],{"class":331},[274,4417,4418],{"class":291},"\u003C",[274,4420,4421],{"class":1179}," 10",[274,4423,4413],{"class":291},[274,4425,4426],{"class":331}," i",[274,4428,4429],{"class":291},"++",[274,4431,322],{"class":291},[274,4433,4434,4436,4438,4440,4442,4444],{"class":276,"line":1295},[274,4435,1477],{"class":331},[274,4437,335],{"class":291},[274,4439,1482],{"class":331},[274,4441,341],{"class":291},[274,4443,1487],{"class":315},[274,4445,1109],{"class":291},[274,4447,4448,4450,4452,4454,4456,4458,4460,4462,4464,4466,4468],{"class":276,"line":1725},[274,4449,1494],{"class":331},[274,4451,341],{"class":291},[274,4453,1499],{"class":315},[274,4455,1502],{"class":291},[274,4457,1506],{"class":1505},[274,4459,1509],{"class":291},[274,4461,1512],{"class":295},[274,4463,341],{"class":291},[274,4465,1517],{"class":295},[274,4467,586],{"class":291},[274,4469,322],{"class":291},[274,4471,4472,4474,4476,4478,4480,4482,4484,4486,4488,4490,4492,4494,4496,4498],{"class":276,"line":1730},[274,4473,1526],{"class":331},[274,4475,341],{"class":291},[274,4477,1531],{"class":315},[274,4479,347],{"class":291},[274,4481,1536],{"class":1179},[274,4483,438],{"class":291},[274,4485,1541],{"class":291},[274,4487,1544],{"class":1505},[274,4489,1509],{"class":291},[274,4491,1512],{"class":295},[274,4493,341],{"class":291},[274,4495,1553],{"class":295},[274,4497,586],{"class":291},[274,4499,322],{"class":291},[274,4501,4502,4504,4506,4508,4510,4512,4514,4516,4518,4520,4522,4524,4527,4529,4531,4533,4536,4539],{"class":276,"line":2324},[274,4503,1562],{"class":331},[274,4505,341],{"class":291},[274,4507,1567],{"class":315},[274,4509,347],{"class":291},[274,4511,3767],{"class":331},[274,4513,341],{"class":291},[274,4515,3772],{"class":315},[274,4517,347],{"class":291},[274,4519,360],{"class":291},[274,4521,3779],{"class":363},[274,4523,3783],{"class":3782},[274,4525,4526],{"class":363}," 页正文。",[274,4528,360],{"class":291},[274,4530,438],{"class":291},[274,4532,4426],{"class":331},[274,4534,4535],{"class":291},"+",[274,4537,4538],{"class":1179},"1",[274,4540,1979],{"class":291},[274,4542,4543],{"class":276,"line":2329},[274,4544,1606],{"class":291},[274,4546,4547],{"class":276,"line":2334},[274,4548,1611],{"class":291},[274,4550,4551],{"class":276,"line":2364},[274,4552,410],{"class":291},[19,4554,4555,4557,4558,4560],{},[38,4556,4337],{}," 和 ",[38,4559,3585],{}," 是占位符，布局引擎在分页之后解析它们。页眉和页脚本身就是树，不是你手动定位的 block。引擎自动为它们在每页保留空间；从 A4 改成 Letter，其他都不用动。",[14,4562,4564],{"id":4563},"before-after-5-aes-256-加密","Before / After 5: AES-256 加密",[19,4566,4567,4568,4570],{},"许可证差异最明显的一对。unipdf 的加密走 ",[38,4569,807],{},"，这算商用使用并触发许可证注册检查。gpdf 的加密在一个函数选项后面，AES-256 (ISO 32000-2 Rev 6) 实现就在开源 MIT 核心里。",[19,4572,4573],{},[22,4574,923],{},[265,4576,4578],{"className":267,"code":4577,"language":269,"meta":270,"style":270},"// 先用 creator 渲染内容，然后用 model.PdfWriter 重新编码以附加加密。\n// 许可证检查在这里触发。\nc := creator.New()\n// ... 绘制内容 ...\n\nvar buf bytes.Buffer\nif err := c.Write(&buf); err != nil {\n    log.Fatal(err)\n}\n\nreader, err := model.NewPdfReader(bytes.NewReader(buf.Bytes()))\nif err != nil {\n    log.Fatal(err)\n}\n\nwriter := model.NewPdfWriter()\nencryptOpts := &model.EncryptOptions{Algorithm: model.RC4_128bit, Permissions: model.PermPrinting}\nif err := writer.Encrypt([]byte(\"user-pwd\"), []byte(\"owner-pwd\"), encryptOpts); err != nil {\n    log.Fatal(err)\n}\n\nfor i := 1; i \u003C= reader.NumPage; i++ {\n    page, _ := reader.GetPage(i)\n    writer.AddPage(page)\n}\n\nf, _ := os.Create(\"encrypted.pdf\")\ndefer f.Close()\nwriter.Write(f)\n",[38,4579,4580,4585,4590,4604,4609,4613,4628,4659,4673,4677,4681,4720,4732,4746,4750,4754,4770,4819,4879,4893,4897,4901,4935,4959,4974,4978,4982,5011,5026],{"__ignoreMap":270},[274,4581,4582],{"class":276,"line":277},[274,4583,4584],{"class":280},"// 先用 creator 渲染内容，然后用 model.PdfWriter 重新编码以附加加密。\n",[274,4586,4587],{"class":276,"line":284},[274,4588,4589],{"class":280},"// 许可证检查在这里触发。\n",[274,4591,4592,4594,4596,4598,4600,4602],{"class":276,"line":302},[274,4593,2896],{"class":331},[274,4595,335],{"class":291},[274,4597,1101],{"class":331},[274,4599,341],{"class":291},[274,4601,1106],{"class":315},[274,4603,1109],{"class":291},[274,4605,4606],{"class":276,"line":309},[274,4607,4608],{"class":280},"// ... 绘制内容 ...\n",[274,4610,4611],{"class":276,"line":325},[274,4612,306],{"emptyLinePlaceholder":305},[274,4614,4615,4617,4620,4623,4625],{"class":276,"line":382},[274,4616,3191],{"class":291},[274,4618,4619],{"class":331}," buf ",[274,4621,4622],{"class":295},"bytes",[274,4624,341],{"class":291},[274,4626,4627],{"class":295},"Buffer\n",[274,4629,4630,4632,4634,4636,4638,4640,4643,4646,4649,4651,4653,4655,4657],{"class":276,"line":401},[274,4631,2337],{"class":287},[274,4633,332],{"class":331},[274,4635,335],{"class":291},[274,4637,1147],{"class":331},[274,4639,341],{"class":291},[274,4641,4642],{"class":315},"Write",[274,4644,4645],{"class":291},"(&",[274,4647,4648],{"class":331},"buf",[274,4650,501],{"class":291},[274,4652,332],{"class":331},[274,4654,374],{"class":291},[274,4656,377],{"class":291},[274,4658,322],{"class":291},[274,4660,4661,4663,4665,4667,4669,4671],{"class":276,"line":407},[274,4662,2367],{"class":331},[274,4664,341],{"class":291},[274,4666,390],{"class":315},[274,4668,347],{"class":291},[274,4670,395],{"class":331},[274,4672,398],{"class":291},[274,4674,4675],{"class":276,"line":989},[274,4676,410],{"class":291},[274,4678,4679],{"class":276,"line":994},[274,4680,306],{"emptyLinePlaceholder":305},[274,4682,4683,4686,4688,4690,4692,4694,4696,4699,4701,4703,4705,4708,4710,4712,4714,4717],{"class":276,"line":999},[274,4684,4685],{"class":331},"reader",[274,4687,438],{"class":291},[274,4689,332],{"class":331},[274,4691,335],{"class":291},[274,4693,1861],{"class":331},[274,4695,341],{"class":291},[274,4697,4698],{"class":315},"NewPdfReader",[274,4700,347],{"class":291},[274,4702,4622],{"class":331},[274,4704,341],{"class":291},[274,4706,4707],{"class":315},"NewReader",[274,4709,347],{"class":291},[274,4711,4648],{"class":331},[274,4713,341],{"class":291},[274,4715,4716],{"class":315},"Bytes",[274,4718,4719],{"class":291},"()))\n",[274,4721,4722,4724,4726,4728,4730],{"class":276,"line":1010},[274,4723,2337],{"class":287},[274,4725,332],{"class":331},[274,4727,374],{"class":291},[274,4729,377],{"class":291},[274,4731,322],{"class":291},[274,4733,4734,4736,4738,4740,4742,4744],{"class":276,"line":1051},[274,4735,2367],{"class":331},[274,4737,341],{"class":291},[274,4739,390],{"class":315},[274,4741,347],{"class":291},[274,4743,395],{"class":331},[274,4745,398],{"class":291},[274,4747,4748],{"class":276,"line":1066},[274,4749,410],{"class":291},[274,4751,4752],{"class":276,"line":1071},[274,4753,306],{"emptyLinePlaceholder":305},[274,4755,4756,4759,4761,4763,4765,4768],{"class":276,"line":1076},[274,4757,4758],{"class":331},"writer ",[274,4760,335],{"class":291},[274,4762,1861],{"class":331},[274,4764,341],{"class":291},[274,4766,4767],{"class":315},"NewPdfWriter",[274,4769,1109],{"class":291},[274,4771,4772,4775,4777,4780,4783,4785,4788,4790,4793,4796,4798,4800,4803,4805,4808,4810,4812,4814,4817],{"class":276,"line":1081},[274,4773,4774],{"class":331},"encryptOpts ",[274,4776,335],{"class":291},[274,4778,4779],{"class":291}," &",[274,4781,4782],{"class":295},"model",[274,4784,341],{"class":291},[274,4786,4787],{"class":295},"EncryptOptions",[274,4789,2116],{"class":291},[274,4791,4792],{"class":331},"Algorithm",[274,4794,4795],{"class":291},":",[274,4797,1861],{"class":331},[274,4799,341],{"class":291},[274,4801,4802],{"class":331},"RC4_128bit",[274,4804,438],{"class":291},[274,4806,4807],{"class":331}," Permissions",[274,4809,4795],{"class":291},[274,4811,1861],{"class":331},[274,4813,341],{"class":291},[274,4815,4816],{"class":331},"PermPrinting",[274,4818,410],{"class":291},[274,4820,4821,4823,4825,4827,4830,4832,4834,4837,4840,4842,4844,4847,4849,4851,4853,4855,4857,4859,4862,4864,4866,4869,4871,4873,4875,4877],{"class":276,"line":1093},[274,4822,2337],{"class":287},[274,4824,332],{"class":331},[274,4826,335],{"class":291},[274,4828,4829],{"class":331}," writer",[274,4831,341],{"class":291},[274,4833,811],{"class":315},[274,4835,4836],{"class":291},"([]",[274,4838,4839],{"class":482},"byte",[274,4841,347],{"class":291},[274,4843,360],{"class":291},[274,4845,4846],{"class":363},"user-pwd",[274,4848,360],{"class":291},[274,4850,491],{"class":291},[274,4852,2111],{"class":291},[274,4854,4839],{"class":482},[274,4856,347],{"class":291},[274,4858,360],{"class":291},[274,4860,4861],{"class":363},"owner-pwd",[274,4863,360],{"class":291},[274,4865,491],{"class":291},[274,4867,4868],{"class":331}," encryptOpts",[274,4870,501],{"class":291},[274,4872,332],{"class":331},[274,4874,374],{"class":291},[274,4876,377],{"class":291},[274,4878,322],{"class":291},[274,4880,4881,4883,4885,4887,4889,4891],{"class":276,"line":1112},[274,4882,2367],{"class":331},[274,4884,341],{"class":291},[274,4886,390],{"class":315},[274,4888,347],{"class":291},[274,4890,395],{"class":331},[274,4892,398],{"class":291},[274,4894,4895],{"class":276,"line":1134},[274,4896,410],{"class":291},[274,4898,4899],{"class":276,"line":1139},[274,4900,306],{"emptyLinePlaceholder":305},[274,4902,4903,4905,4907,4909,4912,4914,4916,4919,4922,4924,4927,4929,4931,4933],{"class":276,"line":1166},[274,4904,2095],{"class":287},[274,4906,4405],{"class":331},[274,4908,335],{"class":291},[274,4910,4911],{"class":1179}," 1",[274,4913,4413],{"class":291},[274,4915,4405],{"class":331},[274,4917,4918],{"class":291},"\u003C=",[274,4920,4921],{"class":331}," reader",[274,4923,341],{"class":291},[274,4925,4926],{"class":331},"NumPage",[274,4928,4413],{"class":291},[274,4930,4426],{"class":331},[274,4932,4429],{"class":291},[274,4934,322],{"class":291},[274,4936,4937,4939,4941,4943,4945,4947,4949,4952,4954,4957],{"class":276,"line":1185},[274,4938,1494],{"class":331},[274,4940,438],{"class":291},[274,4942,441],{"class":331},[274,4944,335],{"class":291},[274,4946,4921],{"class":331},[274,4948,341],{"class":291},[274,4950,4951],{"class":315},"GetPage",[274,4953,347],{"class":291},[274,4955,4956],{"class":331},"i",[274,4958,398],{"class":291},[274,4960,4961,4964,4966,4968,4970,4972],{"class":276,"line":1215},[274,4962,4963],{"class":331},"    writer",[274,4965,341],{"class":291},[274,4967,1487],{"class":315},[274,4969,347],{"class":291},[274,4971,2399],{"class":331},[274,4973,398],{"class":291},[274,4975,4976],{"class":276,"line":1230},[274,4977,410],{"class":291},[274,4979,4980],{"class":276,"line":1235},[274,4981,306],{"emptyLinePlaceholder":305},[274,4983,4984,4987,4989,4991,4993,4995,4997,5000,5002,5004,5007,5009],{"class":276,"line":1240},[274,4985,4986],{"class":331},"f",[274,4988,438],{"class":291},[274,4990,441],{"class":331},[274,4992,335],{"class":291},[274,4994,446],{"class":331},[274,4996,341],{"class":291},[274,4998,4999],{"class":315},"Create",[274,5001,347],{"class":291},[274,5003,360],{"class":291},[274,5005,5006],{"class":363},"encrypted.pdf",[274,5008,360],{"class":291},[274,5010,398],{"class":291},[274,5012,5013,5016,5019,5021,5024],{"class":276,"line":1275},[274,5014,5015],{"class":287},"defer",[274,5017,5018],{"class":331}," f",[274,5020,341],{"class":291},[274,5022,5023],{"class":315},"Close",[274,5025,1109],{"class":291},[274,5027,5028,5031,5033,5035,5037,5039],{"class":276,"line":1290},[274,5029,5030],{"class":331},"writer",[274,5032,341],{"class":291},[274,5034,4642],{"class":315},[274,5036,347],{"class":291},[274,5038,4986],{"class":331},[274,5040,398],{"class":291},[19,5042,5043],{},[22,5044,1302],{},[265,5046,5048],{"className":267,"code":5047,"language":269,"meta":270,"style":270},"doc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    gpdf.WithEncryption(\n        gpdf.AES256,\n        \"user-pwd\",\n        \"owner-pwd\",\n        gpdf.PermPrinting|gpdf.PermCopyContent,\n    ),\n)\n\npage := doc.AddPage()\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"机密\")\n    })\n})\n\ndata, _ := doc.Generate()\nos.WriteFile(\"encrypted.pdf\", data, 0o644)\n",[38,5049,5050,5064,5082,5112,5123,5134,5145,5155,5175,5180,5184,5188,5203,5227,5257,5276,5280,5284,5288,5307],{"__ignoreMap":270},[274,5051,5052,5054,5056,5058,5060,5062],{"class":276,"line":277},[274,5053,3900],{"class":331},[274,5055,335],{"class":291},[274,5057,1397],{"class":331},[274,5059,341],{"class":291},[274,5061,1402],{"class":315},[274,5063,1405],{"class":291},[274,5065,5066,5068,5070,5072,5074,5076,5078,5080],{"class":276,"line":284},[274,5067,3915],{"class":331},[274,5069,341],{"class":291},[274,5071,1415],{"class":315},[274,5073,347],{"class":291},[274,5075,1420],{"class":331},[274,5077,341],{"class":291},[274,5079,1425],{"class":331},[274,5081,1428],{"class":291},[274,5083,5084,5086,5088,5090,5092,5094,5096,5098,5100,5102,5104,5106,5108,5110],{"class":276,"line":302},[274,5085,3915],{"class":331},[274,5087,341],{"class":291},[274,5089,1437],{"class":315},[274,5091,347],{"class":291},[274,5093,1420],{"class":331},[274,5095,341],{"class":291},[274,5097,1446],{"class":315},[274,5099,347],{"class":291},[274,5101,1420],{"class":331},[274,5103,341],{"class":291},[274,5105,1455],{"class":315},[274,5107,347],{"class":291},[274,5109,1460],{"class":1179},[274,5111,1463],{"class":291},[274,5113,5114,5116,5118,5121],{"class":276,"line":309},[274,5115,3915],{"class":331},[274,5117,341],{"class":291},[274,5119,5120],{"class":315},"WithEncryption",[274,5122,1405],{"class":291},[274,5124,5125,5127,5129,5132],{"class":276,"line":325},[274,5126,1410],{"class":331},[274,5128,341],{"class":291},[274,5130,5131],{"class":331},"AES256",[274,5133,4260],{"class":291},[274,5135,5136,5139,5141,5143],{"class":276,"line":382},[274,5137,5138],{"class":291},"        \"",[274,5140,4846],{"class":363},[274,5142,360],{"class":291},[274,5144,4260],{"class":291},[274,5146,5147,5149,5151,5153],{"class":276,"line":401},[274,5148,5138],{"class":291},[274,5150,4861],{"class":363},[274,5152,360],{"class":291},[274,5154,4260],{"class":291},[274,5156,5157,5159,5161,5163,5166,5168,5170,5173],{"class":276,"line":407},[274,5158,1410],{"class":331},[274,5160,341],{"class":291},[274,5162,4816],{"class":331},[274,5164,5165],{"class":291},"|",[274,5167,24],{"class":331},[274,5169,341],{"class":291},[274,5171,5172],{"class":331},"PermCopyContent",[274,5174,4260],{"class":291},[274,5176,5177],{"class":276,"line":989},[274,5178,5179],{"class":291},"    ),\n",[274,5181,5182],{"class":276,"line":994},[274,5183,398],{"class":291},[274,5185,5186],{"class":276,"line":999},[274,5187,306],{"emptyLinePlaceholder":305},[274,5189,5190,5193,5195,5197,5199,5201],{"class":276,"line":1010},[274,5191,5192],{"class":331},"page ",[274,5194,335],{"class":291},[274,5196,1482],{"class":331},[274,5198,341],{"class":291},[274,5200,1487],{"class":315},[274,5202,1109],{"class":291},[274,5204,5205,5207,5209,5211,5213,5215,5217,5219,5221,5223,5225],{"class":276,"line":1051},[274,5206,2399],{"class":331},[274,5208,341],{"class":291},[274,5210,1499],{"class":315},[274,5212,1502],{"class":291},[274,5214,1506],{"class":1505},[274,5216,1509],{"class":291},[274,5218,1512],{"class":295},[274,5220,341],{"class":291},[274,5222,1517],{"class":295},[274,5224,586],{"class":291},[274,5226,322],{"class":291},[274,5228,5229,5231,5233,5235,5237,5239,5241,5243,5245,5247,5249,5251,5253,5255],{"class":276,"line":1066},[274,5230,2424],{"class":331},[274,5232,341],{"class":291},[274,5234,1531],{"class":315},[274,5236,347],{"class":291},[274,5238,1536],{"class":1179},[274,5240,438],{"class":291},[274,5242,1541],{"class":291},[274,5244,1544],{"class":1505},[274,5246,1509],{"class":291},[274,5248,1512],{"class":295},[274,5250,341],{"class":291},[274,5252,1553],{"class":295},[274,5254,586],{"class":291},[274,5256,322],{"class":291},[274,5258,5259,5261,5263,5265,5267,5269,5272,5274],{"class":276,"line":1071},[274,5260,2455],{"class":331},[274,5262,341],{"class":291},[274,5264,1567],{"class":315},[274,5266,347],{"class":291},[274,5268,360],{"class":291},[274,5270,5271],{"class":363},"机密",[274,5273,360],{"class":291},[274,5275,398],{"class":291},[274,5277,5278],{"class":276,"line":1076},[274,5279,1611],{"class":291},[274,5281,5282],{"class":276,"line":1081},[274,5283,2781],{"class":291},[274,5285,5286],{"class":276,"line":1093},[274,5287,306],{"emptyLinePlaceholder":305},[274,5289,5290,5293,5295,5297,5299,5301,5303,5305],{"class":276,"line":1112},[274,5291,5292],{"class":331},"data",[274,5294,438],{"class":291},[274,5296,441],{"class":331},[274,5298,335],{"class":291},[274,5300,1482],{"class":331},[274,5302,341],{"class":291},[274,5304,1633],{"class":315},[274,5306,1109],{"class":291},[274,5308,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329,5331],{"class":276,"line":1134},[274,5310,350],{"class":331},[274,5312,341],{"class":291},[274,5314,1680],{"class":315},[274,5316,347],{"class":291},[274,5318,360],{"class":291},[274,5320,5006],{"class":363},[274,5322,360],{"class":291},[274,5324,438],{"class":291},[274,5326,1693],{"class":331},[274,5328,438],{"class":291},[274,5330,1698],{"class":1179},[274,5332,398],{"class":291},[19,5334,5335,5336,5339,5340,5343],{},"一个选项，默认 AES-256，没有单独的 writer pass。整个加密路径在 MIT 核心里 — 同一个模块，同一个 ",[38,5337,5338],{},"go get","。签名一样：",[38,5341,5342],{},"gpdf.SignDocument(pdfBytes, signer, gpdf.WithTSA(\"http://timestamp.digicert.com\"))"," 后处理字节，附加 PKCS#7 + RFC 3161 时间戳，没有额外包，没有密钥注册。",[14,5345,5346],{"id":5346},"速度怎么样",[19,5348,5349,5352],{},[38,5350,5351],{},"_benchmark/benchmark_test.go"," 在 Apple M1 / Go 1.25 上运行的结果。unipdf 不在我们的提交套件里，因为其许可证使分发对比代码变得不便；下面的数字是我们在同一硬件 / 同一负载下针对 unipdf 收集的。",[122,5354,5355,5373],{},[125,5356,5357],{},[128,5358,5359,5362,5364,5367,5370],{},[131,5360,5361],{},"基准",[131,5363,24],{},[131,5365,5366],{},"unipdf*",[131,5368,5369],{},"gofpdf",[131,5371,5372],{},"Maroto v2",[140,5374,5375,5394,5411,5430],{},[128,5376,5377,5380,5385,5388,5391],{},[145,5378,5379],{},"单页",[145,5381,5382],{},[22,5383,5384],{},"13 µs",[145,5386,5387],{},"~180 µs",[145,5389,5390],{},"132 µs",[145,5392,5393],{},"237 µs",[128,5395,5396,5399,5403,5406,5409],{},[145,5397,5398],{},"4×10 发票表格",[145,5400,5401],{},[22,5402,2804],{},[145,5404,5405],{},"~8.6 ms",[145,5407,5408],{},"241 µs",[145,5410,2800],{},[128,5412,5413,5416,5421,5424,5427],{},[145,5414,5415],{},"100 页报告",[145,5417,5418],{},[22,5419,5420],{},"683 µs",[145,5422,5423],{},"~95 ms",[145,5425,5426],{},"11.7 ms",[145,5428,5429],{},"19.8 ms",[128,5431,5432,5435,5440,5443,5446],{},[145,5433,5434],{},"复杂 CJK 发票",[145,5436,5437],{},[22,5438,5439],{},"133 µs",[145,5441,5442],{},"~12 ms",[145,5444,5445],{},"254 µs",[145,5447,5448],{},"10.4 ms",[19,5450,5451],{},"* unipdf 数字是单独运行收集的，同样在 Apple M1 / Go 1.25 / 当前 unipdf v3 上。视为参考；不在我们提交套件里。",[19,5453,5454],{},"形状和 gofpdf 对比相同：在实际负载上 gpdf 比 unipdf 快 10〜80 倍。每张表格密集页 108 µs，单核每秒能产生约 9,000 张发票。重点不是吹嘘 — 是你可以不再考虑 PDF 生成是否要缓存或异步队列。在请求路径上生成对绝大多数场景都够用。",[14,5456,5458],{"id":5457},"gpdf-没有的那些怎么办","gpdf 没有的那些怎么办",[19,5460,5461],{},"如果你的 unipdf 账单是为 OCR、redaction 或 PDF 解析付的，这次迁移到不了终点。诚实的选项：",[5463,5464,5465,5478,5489,5495],"ul",{},[89,5466,5467,5469,5470,5477],{},[22,5468,226],{},"。gpdf 不做 OCR，短期内也不会。用 ",[5471,5472,5476],"a",{"href":5473,"rel":5474},"https://github.com/tesseract-ocr/tesseract",[5475],"nofollow","Tesseract"," 通过 gosseract 调用，或托管 OCR API。生成留在 gpdf，解析走别的路径。",[89,5479,5480,5482,5483,5488],{},[22,5481,216],{},"。gpdf 按设计只做生成。读取侧 ",[5471,5484,5487],{"href":5485,"rel":5486},"https://github.com/pdfcpu/pdfcpu",[5475],"pdfcpu"," (Apache 2.0) 能覆盖很多常见情况。把 unipdf 留给解析专用，可能减少席位数。",[89,5490,5491,5494],{},[22,5492,5493],{},"AcroForm 字段创建","。gpdf 能扁平化已有 AcroForm 字段，还不能创建新字段。如果你产出的是用户在阅读器里填的表单，这是你会感受到的 gap。已在 roadmap。",[89,5496,5497,5500],{},[22,5498,5499],{},"Redaction (涂黑)","。gpdf 路线图里没有。redaction 需要真正的渲染器知道要黑掉什么，是和生成不同的架构。",[19,5502,5503,5504,5506],{},"对 ",[22,5505,256],{}," 这条路径 — 大多数 unipdf 账单实际买的东西 — 替换是完整的。",[14,5508,5510],{"id":5509},"faq","FAQ",[19,5512,5513,5516],{},[22,5514,5515],{},"gpdf 是 unipdf 的 fork 吗？","\n不是。gpdf 是纯 Go 的全新实现。PDF 线格式、布局引擎、TrueType 子集器、AES、PKCS#7 — 全部从零写起。和 unipdf 无共享代码，无血缘，许可证清白上没有任何隐患因为没复制任何东西。",[19,5518,5519,5522,5523,5528],{},[22,5520,5521],{},"gpdf 真的是 MIT 吗？没有\"在某些条件下变 AGPL\"的条款？","\n是的。仓库的 ",[5471,5524,5527],{"href":5525,"rel":5526},"https://github.com/gpdf-dev/gpdf/blob/main/LICENSE",[5475],"LICENSE"," 是 MIT 原样，没有附录，没有使用领域条款，没有商用层切割。可以在闭源分发产品中使用、嵌入商用 SaaS、随设备分发。唯一义务是在你的发行物中包含许可证和版权声明。",[19,5530,5531,5534,5535,651,5538,5541,5542,5544,5545,5548],{},[22,5532,5533],{},"传递依赖里有没有 copyleft 偷偷藏着？","\ngpdf 核心的 ",[38,5536,5537],{},"go.mod",[38,5539,5540],{},"require"," 块是空的。没有传递的 AGPL，没有传递的 GPL，没有任何传递依赖。",[38,5543,5338],{}," 后用 ",[38,5546,5547],{},"go mod graph | grep gpdf"," 验证。",[19,5550,5551,5554],{},[22,5552,5553],{},"移除许可证密钥真的那么重要？","\n对一些团队是全部。许可证密钥要进 secret manager，要轮换，要审计，要包含在每个容器镜像里，且不能泄露在日志中。多租户 SaaS 几百个 pod 里，这是真实的运维表面。删掉这个要求消除一类事故。",[19,5556,5557,5564,5565,5568,5569,5573],{},[22,5558,5559,5560,5563],{},"我现有 unipdf 代码到处用 ",[38,5561,5562],{},"creator.Block.SetPos"," 做绝对定位。gpdf 有等价物吗？","\n有 — ",[38,5566,5567],{},"page.Absolute(x, y, fn)"," 把子树放到明确坐标。但如果你的代码大量用绝对定位，布局引擎模型是思维转变，不是语法转换。估工时之前先读 ",[5471,5570,5572],{"href":5571},"/zh/blog/12-column-grid","12 栅格文章","。重写后的代码通常比原来短。",[19,5575,5576,5579],{},[22,5577,5578],{},"如果将来 UniDoc 把 unipdf 改成 MIT？","\n那你多一个选项。gpdf 背后的押注不是 \"unipdf 永远 AGPL\"；而是 \"需要启动注册调用、按开发者收费的许可证，对大多数负载是不该存在的税\"。即使明天 unipdf 改了，许可证密钥的运维表面还会留着直到他们删掉它。",[14,5581,5583],{"id":5582},"试试-gpdf","试试 gpdf",[19,5585,5586],{},"gpdf 是 Go 的 PDF 生成库。MIT 许可，零外部依赖，无许可证密钥，原生 CJK 支持。",[265,5588,5592],{"className":5589,"code":5590,"language":5591,"meta":270,"style":270},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[38,5593,5594],{"__ignoreMap":270},[274,5595,5596,5598,5601],{"class":276,"line":277},[274,5597,269],{"class":295},[274,5599,5600],{"class":363}," get",[274,5602,5603],{"class":363}," github.com/gpdf-dev/gpdf\n",[19,5605,5606,5611,5612],{},[5471,5607,5610],{"href":5608,"rel":5609},"https://github.com/gpdf-dev/gpdf",[5475],"⭐ Star on GitHub"," · ",[5471,5613,5616],{"href":5614,"rel":5615},"https://gpdf.dev/zh/docs/quickstart",[5475],"文档",[14,5618,5619],{"id":5619},"接下来读什么",[5463,5621,5622,5628,5633,5639],{},[89,5623,5624],{},[5471,5625,5627],{"href":5626},"/zh/blog/embed-japanese-font","如何在 gpdf 中嵌入中文/日文字体？",[89,5629,5630],{},[5471,5631,5632],{"href":5571},"12 栅格：把 Bootstrap 思维带到 PDF 排版",[89,5634,5635],{},[5471,5636,5638],{"href":5637},"/zh/blog/gofpdf-migration","gofpdf 已归档：迁移到 gpdf 的完整指南",[89,5640,5641,5645],{},[5471,5642,5644],{"href":5614,"rel":5643},[5475],"Quickstart"," — 5 分钟搭建",[2948,5647,5648],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":270,"searchDepth":284,"depth":284,"links":5650},[5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664,5665],{"id":16,"depth":284,"text":17},{"id":77,"depth":284,"text":78},{"id":116,"depth":284,"text":117},{"id":260,"depth":284,"text":260},{"id":559,"depth":284,"text":560},{"id":914,"depth":284,"text":915},{"id":1744,"depth":284,"text":1745},{"id":2815,"depth":284,"text":2816},{"id":3570,"depth":284,"text":3571},{"id":4563,"depth":284,"text":4564},{"id":5346,"depth":284,"text":5346},{"id":5457,"depth":284,"text":5458},{"id":5509,"depth":284,"text":5510},{"id":5582,"depth":284,"text":5583},{"id":5619,"depth":284,"text":5619},"2026-04-29","UniDoc 的 unipdf 必须选 AGPL v3 或按开发者收费的商用许可。迁移到 MIT、零依赖、无许可证密钥的 gpdf。",false,"md",{"name":5671,"totalTime":5672,"tools":5673,"steps":5675},"将 Go 项目从 unidoc/unipdf 迁移到 gpdf","PT45M",[5674],"Go 1.22+",[5676,5678,5681,5684,5687,5690,5693],{"name":260,"text":5677},"移除 main 和 init() 中的 license.SetMeteredKey 或 license.SetLicenseKey 调用。gpdf 没有许可证密钥、metering API 或启动注册步骤。",{"name":5679,"text":5680},"替换 import 路径","把 github.com/unidoc/unipdf/v3/creator 和 github.com/unidoc/unipdf/v3/model 换成 github.com/gpdf-dev/gpdf、github.com/gpdf-dev/gpdf/document、github.com/gpdf-dev/gpdf/template。",{"name":5682,"text":5683},"用 gpdf.NewDocument 替代 creator.New","用 gpdf.NewDocument(WithPageSize(document.A4), WithMargins(...)) 构建文档。页面通过 doc.AddPage() 返回的 PageBuilder 处理，不再有自由游标。",{"name":5685,"text":5686},"把 Paragraph / StyledParagraph 替换为 c.Text","在列里调用 c.Text(string, options...)，不再构建 Paragraph 然后 c.Draw。字号、字体、颜色从结构体字段变为 per-text 选项。",{"name":5688,"text":5689},"用 12 栅格重写表格","把 creator.NewTable(cols).SetColumnWidths 和 SetCellSpan 换成 AutoRow 内的 row.Col(span, fn)。12 栅格按百分比处理列宽并自动分页。",{"name":5691,"text":5692},"字体改为字节而非文件路径","把 model.NewCompositePdfFontFromTTFFile 换成构建时的 gpdf.WithFont(name, ttfBytes)。用 //go:embed 把 TTF 编入二进制，运行时不再需要字体路径。",{"name":5694,"text":5695},"切换输出调用","把 c.WriteToFile(path) 换成 doc.Generate() 加 os.WriteFile(path, data, 0o644)，或用 doc.Render(w) 直接写入 io.Writer。",null,{},"/zh/blog/unidoc-migration",{"title":5,"description":5667},"zh/blog/016.unidoc-migration",[5702,5703,5704],"migration","comparison","tutorial","7hwYyg2Umyu57ph_Xe2SQcV73HBDIferP0PR7xnSTKQ",1779199017010]