[{"data":1,"prerenderedAt":1935},["ShallowReactive",2],{"blog-zh-japanese-pdf-in-go":3},{"id":4,"title":5,"author":6,"body":9,"date":1896,"description":1897,"draft":1898,"extension":1899,"howTo":1900,"image":1925,"meta":1926,"navigation":347,"path":1927,"seo":1928,"stem":1929,"tags":1930,"updated":1925,"__hash__":1934},"blogZh/zh/blog/007.japanese-pdf-in-go.md","用 Go 生成日文 PDF 的 2026 权威指南",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1882},"minimark",[12,17,43,46,49,56,66,69,73,84,127,142,152,156,159,288,291,297,307,310,320,1295,1298,1354,1363,1367,1377,1380,1386,1435,1449,1452,1476,1493,1497,1500,1508,1511,1525,1535,1617,1623,1625,1628,1638,1648,1672,1686,1689,1692,1699,1705,1714,1728,1731,1735,1761,1767,1777,1791,1801,1810,1814,1817,1832,1846,1849,1878],[13,14,16],"h2",{"id":15},"tldr","TL;DR",[18,19,20,21,25,26,29,30,33,34,38,39,42],"p",{},"如果你的 Go PDF 把 ",[22,23,24],"code",{},"こんにちは"," 渲染成 5 个豆腐方块,修复方式是两行配置,不是重写。加载一个日文 TTF,把 ",[22,27,28],{},"gpdf.WithFont"," 传给 ",[22,31,32],{},"NewDocument",",写日文。",[35,36,37],"strong",{},"gpdf 会自动子集化字形表",",所以输出只带你实际用过的字符 — 约 30 KB,而不是整套 5 MB 的字体。本文是这条路径的地图:为什么 Go 里的日文 PDF 出奇地难、2026 年真正的四个选项、一份完整可跑的示例、字体子集化的内部机制、混合排版的边界情况,以及",[35,40,41],{},"仍然难解的部分","。",[13,44,45],{"id":45},"为什么需要这篇指南",[18,47,48],{},"Go 里渲染一个日文 PDF 本该是 5 分钟的事。对很多团队而言,它要花一天半。",[18,50,51,52,55],{},"典型剧情:有人换上 ",[22,53,54],{},"AddUTF8Font",",PDF 里出现一排空白方框 — 臭名昭著的\"豆腐\" — 一个资深工程师花一下午排查到底是字体路径、子集标志、CMap、UTF-8 开关,还是 PDF 阅读器。到傍晚 Slack 上出现了一条名为\"为什么漢字还在坏\"的讨论,第二天提交了一个新增三个谁都后悔的辅助函数的 PR。",[18,57,58,59,62,63,42],{},"根源不是这些任何一条。",[35,60,61],{},"Go 上寿命最长的 PDF 库是 2002 年为 PHP 和 Latin-1 设计的",",之后几乎所有日文教程都在与这个遗产作战。本文是 2026 版:从干净的起点出发,真正能工作的做法,以及",[35,64,65],{},"仍然困难的部分",[18,67,68],{},"本文代码基于 gpdf v1.x (2026-04)。基准数据来自 Apple M1 + Go 1.25。",[13,70,72],{"id":71},"_90-秒看懂豆腐问题","90 秒看懂豆腐问题",[18,74,75,76,79,80,83],{},"PDF 不在乎 Unicode。它在乎的是",[35,77,78],{},"字形 ID"," — 嵌入字体字形表的整数索引。要把 ",[22,81,82],{},"\"こんにちは\""," 写进 PDF,必须有人完成:",[85,86,87,98,104,110],"ol",{},[88,89,90,93,94,97],"li",{},[35,91,92],{},"解析 TTF",",从 ",[22,95,96],{},"cmap"," 子表里找出每个码点对应的字形 ID。",[88,99,100,103],{},[35,101,102],{},"写 ToUnicode CMap",",这样 PDF 阅读器在用户复制或搜索时能把字形映射回文本。",[88,105,106,109],{},[35,107,108],{},"子集化",",不要把 Noto Sans JP 的两万字形全塞进去。",[88,111,112,115,116,119,120,119,123,126],{},[35,113,114],{},"嵌入",",正确拼接 ",[22,117,118],{},"name"," / ",[22,121,122],{},"OS/2",[22,124,125],{},"head"," 表和编码对象。",[18,128,129,130,133,134,137,138,141],{},"任何一步缺失或出错,阅读器都找不到字形,画出豆腐。已归档的 ",[22,131,132],{},"jung-kurt/gofpdf"," 和 ",[22,135,136],{},"go-pdf/fpdf"," 系列把上面所有步骤",[35,139,140],{},"后挂","在单字节字体模型上 — 2002 年的 FPDF 只认 Latin-1。这就是为什么设置易碎、输出经常嵌入整套字体而非子集、故障模式因操作系统和阅读器而异。",[18,143,144,145,148,149,151],{},"gpdf 把 CJK 当成",[35,146,147],{},"一等用例","。TTF 子集器在核心包内。ToUnicode CMap 自动写出。没有单字节字体的历史包袱,因此也没有 ",[22,150,54],{}," 的折腾。",[13,153,155],{"id":154},"_2026-年的四个真实选项","2026 年的四个真实选项",[18,157,158],{},"先摆牌。\"支持日文\"指\"给定正确 TTF 时,能不崩溃、不豆腐地渲染任意日文\"。",[160,161,162,187],"table",{},[163,164,165],"thead",{},[166,167,168,172,175,178,181,184],"tr",{},[169,170,171],"th",{},"选项",[169,173,174],{},"许可",[169,176,177],{},"依赖",[169,179,180],{},"CJK 路径",[169,182,183],{},"300 字文档大小",[169,185,186],{},"备注",[188,189,190,215,238,263],"tbody",{},[166,191,192,198,201,204,209,212],{},[193,194,195,197],"td",{},[22,196,136],{}," (2025 归档)",[193,199,200],{},"MIT",[193,202,203],{},"标准库",[193,205,206,208],{},[22,207,54],{}," 后挂",[193,210,211],{},"约 5 MB (整套)",[193,213,214],{},"后挂在 Latin-1 核心上。子集化需显式开启且不完整。",[166,216,217,222,224,226,232,235],{},[193,218,219],{},[22,220,221],{},"signintech/gopdf",[193,223,200],{},[193,225,203],{},[193,227,228,231],{},[22,229,230],{},"AddTTFFont"," + 手工",[193,233,234],{},"约 3 MB",[193,236,237],{},"低层。自己写坐标。有子集化但要自己驱动。",[166,239,240,246,249,254,257,260],{},[193,241,242,245],{},[22,243,244],{},"chromedp"," + Chromium",[193,247,248],{},"MIT + Chrome",[193,250,251],{},[35,252,253],{},"Chromium 二进制",[193,255,256],{},"浏览器原生",[193,258,259],{},"可变",[193,261,262],{},"HTML/CSS。容器里要装字体。镜像 500 MB+。",[166,264,265,270,272,277,280,285],{},[193,266,267],{},[22,268,269],{},"gpdf",[193,271,200],{},[193,273,274],{},[35,275,276],{},"仅标准库",[193,278,279],{},"原生,自动子集化",[193,281,282],{},[35,283,284],{},"约 30 KB",[193,286,287],{},"纯 Go。Builder API。自动写 ToUnicode CMap。",[18,289,290],{},"两点值得强调。",[18,292,293,296],{},[35,294,295],{},"\"整套嵌入\"与\"自动子集\"之间 160 倍的差不是小事。"," 一张十条明细的日文电商发票,唯一的日文字符可能只有 120 个。每张发票都嵌入整套 Noto Sans JP (5.1 MB) 意味着到年底,同样 5 MB 的字形数据会在对象存储里存在一千万份。子集嵌入只带你用到的字形。",[18,298,299,302,303,306],{},[35,300,301],{},"\"chromedp 能用\"是事实,也是最贵的答案。"," 如果团队已经在跑一队无头 Chrome 做截图,给它加个 PDF 用途也行。如果没跑,",[35,304,305],{},"仅仅为了打印日文","就立起一个 Chromium,相对于一个 40 行 Go 能解决的问题而言,基础设施开销过大。",[13,308,309],{"id":309},"最短可行路径",[18,311,312,313,316,317,42],{},"先试这个。完整可运行 — 拷贝保存为 ",[22,314,315],{},"main.go",",把两个 TTF 放在旁边,",[22,318,319],{},"go run main.go",[321,322,327],"pre",{"className":323,"code":324,"language":325,"meta":326,"style":326},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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    regular, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    bold, err := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", regular),\n        gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"請求書\", template.FontFamily(\"NotoSansJP-Bold\"), template.FontSize(22))\n            c.Text(\"2026 年 4 月 16 日\")\n        })\n    })\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(7, func(c *template.ColBuilder) {\n            c.Text(\"株式会社 ABC 御中\", template.FontSize(13))\n            c.Text(\"〒 100-0001 東京都千代田区千代田 1-1\")\n        })\n        r.Col(5, func(c *template.ColBuilder) {\n            c.Text(\"合計 ¥ 128,000\", template.FontFamily(\"NotoSansJP-Bold\"), template.AlignRight())\n            c.Text(\"支払期限: 2026-05-31\", template.AlignRight())\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice-ja.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[22,328,329,342,349,359,371,381,386,396,406,416,422,427,443,482,498,516,522,551,564,579,584,589,607,631,668,694,719,744,750,755,774,808,845,901,921,927,933,958,990,1023,1043,1048,1080,1126,1154,1159,1164,1169,1190,1203,1218,1223,1269,1284,1289],{"__ignoreMap":326},[330,331,334,338],"span",{"class":332,"line":333},"line",1,[330,335,337],{"class":336},"sMK4o","package",[330,339,341],{"class":340},"sBMFI"," main\n",[330,343,345],{"class":332,"line":344},2,[330,346,348],{"emptyLinePlaceholder":347},true,"\n",[330,350,352,356],{"class":332,"line":351},3,[330,353,355],{"class":354},"s7zQu","import",[330,357,358],{"class":336}," (\n",[330,360,362,365,368],{"class":332,"line":361},4,[330,363,364],{"class":336},"    \"",[330,366,367],{"class":340},"log",[330,369,370],{"class":336},"\"\n",[330,372,374,376,379],{"class":332,"line":373},5,[330,375,364],{"class":336},[330,377,378],{"class":340},"os",[330,380,370],{"class":336},[330,382,384],{"class":332,"line":383},6,[330,385,348],{"emptyLinePlaceholder":347},[330,387,389,391,394],{"class":332,"line":388},7,[330,390,364],{"class":336},[330,392,393],{"class":340},"github.com/gpdf-dev/gpdf",[330,395,370],{"class":336},[330,397,399,401,404],{"class":332,"line":398},8,[330,400,364],{"class":336},[330,402,403],{"class":340},"github.com/gpdf-dev/gpdf/document",[330,405,370],{"class":336},[330,407,409,411,414],{"class":332,"line":408},9,[330,410,364],{"class":336},[330,412,413],{"class":340},"github.com/gpdf-dev/gpdf/template",[330,415,370],{"class":336},[330,417,419],{"class":332,"line":418},10,[330,420,421],{"class":336},")\n",[330,423,425],{"class":332,"line":424},11,[330,426,348],{"emptyLinePlaceholder":347},[330,428,430,433,437,440],{"class":332,"line":429},12,[330,431,432],{"class":336},"func",[330,434,436],{"class":435},"s2Zo4"," main",[330,438,439],{"class":336},"()",[330,441,442],{"class":336}," {\n",[330,444,446,450,453,456,459,462,465,468,471,474,478,480],{"class":332,"line":445},13,[330,447,449],{"class":448},"sTEyZ","    regular",[330,451,452],{"class":336},",",[330,454,455],{"class":448}," err ",[330,457,458],{"class":336},":=",[330,460,461],{"class":448}," os",[330,463,464],{"class":336},".",[330,466,467],{"class":435},"ReadFile",[330,469,470],{"class":336},"(",[330,472,473],{"class":336},"\"",[330,475,477],{"class":476},"sfazB","NotoSansJP-Regular.ttf",[330,479,473],{"class":336},[330,481,421],{"class":336},[330,483,485,488,490,493,496],{"class":332,"line":484},14,[330,486,487],{"class":354},"    if",[330,489,455],{"class":448},[330,491,492],{"class":336},"!=",[330,494,495],{"class":336}," nil",[330,497,442],{"class":336},[330,499,501,504,506,509,511,514],{"class":332,"line":500},15,[330,502,503],{"class":448},"        log",[330,505,464],{"class":336},[330,507,508],{"class":435},"Fatal",[330,510,470],{"class":336},[330,512,513],{"class":448},"err",[330,515,421],{"class":336},[330,517,519],{"class":332,"line":518},16,[330,520,521],{"class":336},"    }\n",[330,523,525,528,530,532,534,536,538,540,542,544,547,549],{"class":332,"line":524},17,[330,526,527],{"class":448},"    bold",[330,529,452],{"class":336},[330,531,455],{"class":448},[330,533,458],{"class":336},[330,535,461],{"class":448},[330,537,464],{"class":336},[330,539,467],{"class":435},[330,541,470],{"class":336},[330,543,473],{"class":336},[330,545,546],{"class":476},"NotoSansJP-Bold.ttf",[330,548,473],{"class":336},[330,550,421],{"class":336},[330,552,554,556,558,560,562],{"class":332,"line":553},18,[330,555,487],{"class":354},[330,557,455],{"class":448},[330,559,492],{"class":336},[330,561,495],{"class":336},[330,563,442],{"class":336},[330,565,567,569,571,573,575,577],{"class":332,"line":566},19,[330,568,503],{"class":448},[330,570,464],{"class":336},[330,572,508],{"class":435},[330,574,470],{"class":336},[330,576,513],{"class":448},[330,578,421],{"class":336},[330,580,582],{"class":332,"line":581},20,[330,583,521],{"class":336},[330,585,587],{"class":332,"line":586},21,[330,588,348],{"emptyLinePlaceholder":347},[330,590,592,595,597,600,602,604],{"class":332,"line":591},22,[330,593,594],{"class":448},"    doc ",[330,596,458],{"class":336},[330,598,599],{"class":448}," gpdf",[330,601,464],{"class":336},[330,603,32],{"class":435},[330,605,606],{"class":336},"(\n",[330,608,610,613,615,618,620,623,625,628],{"class":332,"line":609},23,[330,611,612],{"class":448},"        gpdf",[330,614,464],{"class":336},[330,616,617],{"class":435},"WithPageSize",[330,619,470],{"class":336},[330,621,622],{"class":448},"document",[330,624,464],{"class":336},[330,626,627],{"class":448},"A4",[330,629,630],{"class":336},"),\n",[330,632,634,636,638,641,643,645,647,650,652,654,656,659,661,665],{"class":332,"line":633},24,[330,635,612],{"class":448},[330,637,464],{"class":336},[330,639,640],{"class":435},"WithMargins",[330,642,470],{"class":336},[330,644,622],{"class":448},[330,646,464],{"class":336},[330,648,649],{"class":435},"UniformEdges",[330,651,470],{"class":336},[330,653,622],{"class":448},[330,655,464],{"class":336},[330,657,658],{"class":435},"Mm",[330,660,470],{"class":336},[330,662,664],{"class":663},"sbssI","20",[330,666,667],{"class":336},"))),\n",[330,669,671,673,675,678,680,682,685,687,689,692],{"class":332,"line":670},25,[330,672,612],{"class":448},[330,674,464],{"class":336},[330,676,677],{"class":435},"WithFont",[330,679,470],{"class":336},[330,681,473],{"class":336},[330,683,684],{"class":476},"NotoSansJP",[330,686,473],{"class":336},[330,688,452],{"class":336},[330,690,691],{"class":448}," regular",[330,693,630],{"class":336},[330,695,697,699,701,703,705,707,710,712,714,717],{"class":332,"line":696},26,[330,698,612],{"class":448},[330,700,464],{"class":336},[330,702,677],{"class":435},[330,704,470],{"class":336},[330,706,473],{"class":336},[330,708,709],{"class":476},"NotoSansJP-Bold",[330,711,473],{"class":336},[330,713,452],{"class":336},[330,715,716],{"class":448}," bold",[330,718,630],{"class":336},[330,720,722,724,726,729,731,733,735,737,739,742],{"class":332,"line":721},27,[330,723,612],{"class":448},[330,725,464],{"class":336},[330,727,728],{"class":435},"WithDefaultFont",[330,730,470],{"class":336},[330,732,473],{"class":336},[330,734,684],{"class":476},[330,736,473],{"class":336},[330,738,452],{"class":336},[330,740,741],{"class":663}," 11",[330,743,630],{"class":336},[330,745,747],{"class":332,"line":746},28,[330,748,749],{"class":336},"    )\n",[330,751,753],{"class":332,"line":752},29,[330,754,348],{"emptyLinePlaceholder":347},[330,756,758,761,763,766,768,771],{"class":332,"line":757},30,[330,759,760],{"class":448},"    page ",[330,762,458],{"class":336},[330,764,765],{"class":448}," doc",[330,767,464],{"class":336},[330,769,770],{"class":435},"AddPage",[330,772,773],{"class":336},"()\n",[330,775,777,780,782,785,788,792,795,798,800,803,806],{"class":332,"line":776},31,[330,778,779],{"class":448},"    page",[330,781,464],{"class":336},[330,783,784],{"class":435},"AutoRow",[330,786,787],{"class":336},"(func(",[330,789,791],{"class":790},"sHdIc","r",[330,793,794],{"class":336}," *",[330,796,797],{"class":340},"template",[330,799,464],{"class":336},[330,801,802],{"class":340},"RowBuilder",[330,804,805],{"class":336},")",[330,807,442],{"class":336},[330,809,811,814,816,819,821,824,826,829,832,834,836,838,841,843],{"class":332,"line":810},32,[330,812,813],{"class":448},"        r",[330,815,464],{"class":336},[330,817,818],{"class":435},"Col",[330,820,470],{"class":336},[330,822,823],{"class":663},"12",[330,825,452],{"class":336},[330,827,828],{"class":336}," func(",[330,830,831],{"class":790},"c",[330,833,794],{"class":336},[330,835,797],{"class":340},[330,837,464],{"class":336},[330,839,840],{"class":340},"ColBuilder",[330,842,805],{"class":336},[330,844,442],{"class":336},[330,846,848,851,853,856,858,860,863,865,867,870,872,875,877,879,881,883,886,888,890,893,895,898],{"class":332,"line":847},33,[330,849,850],{"class":448},"            c",[330,852,464],{"class":336},[330,854,855],{"class":435},"Text",[330,857,470],{"class":336},[330,859,473],{"class":336},[330,861,862],{"class":476},"請求書",[330,864,473],{"class":336},[330,866,452],{"class":336},[330,868,869],{"class":448}," template",[330,871,464],{"class":336},[330,873,874],{"class":435},"FontFamily",[330,876,470],{"class":336},[330,878,473],{"class":336},[330,880,709],{"class":476},[330,882,473],{"class":336},[330,884,885],{"class":336},"),",[330,887,869],{"class":448},[330,889,464],{"class":336},[330,891,892],{"class":435},"FontSize",[330,894,470],{"class":336},[330,896,897],{"class":663},"22",[330,899,900],{"class":336},"))\n",[330,902,904,906,908,910,912,914,917,919],{"class":332,"line":903},34,[330,905,850],{"class":448},[330,907,464],{"class":336},[330,909,855],{"class":435},[330,911,470],{"class":336},[330,913,473],{"class":336},[330,915,916],{"class":476},"2026 年 4 月 16 日",[330,918,473],{"class":336},[330,920,421],{"class":336},[330,922,924],{"class":332,"line":923},35,[330,925,926],{"class":336},"        })\n",[330,928,930],{"class":332,"line":929},36,[330,931,932],{"class":336},"    })\n",[330,934,936,938,940,942,944,946,948,950,952,954,956],{"class":332,"line":935},37,[330,937,779],{"class":448},[330,939,464],{"class":336},[330,941,784],{"class":435},[330,943,787],{"class":336},[330,945,791],{"class":790},[330,947,794],{"class":336},[330,949,797],{"class":340},[330,951,464],{"class":336},[330,953,802],{"class":340},[330,955,805],{"class":336},[330,957,442],{"class":336},[330,959,961,963,965,967,969,972,974,976,978,980,982,984,986,988],{"class":332,"line":960},38,[330,962,813],{"class":448},[330,964,464],{"class":336},[330,966,818],{"class":435},[330,968,470],{"class":336},[330,970,971],{"class":663},"7",[330,973,452],{"class":336},[330,975,828],{"class":336},[330,977,831],{"class":790},[330,979,794],{"class":336},[330,981,797],{"class":340},[330,983,464],{"class":336},[330,985,840],{"class":340},[330,987,805],{"class":336},[330,989,442],{"class":336},[330,991,993,995,997,999,1001,1003,1006,1008,1010,1012,1014,1016,1018,1021],{"class":332,"line":992},39,[330,994,850],{"class":448},[330,996,464],{"class":336},[330,998,855],{"class":435},[330,1000,470],{"class":336},[330,1002,473],{"class":336},[330,1004,1005],{"class":476},"株式会社 ABC 御中",[330,1007,473],{"class":336},[330,1009,452],{"class":336},[330,1011,869],{"class":448},[330,1013,464],{"class":336},[330,1015,892],{"class":435},[330,1017,470],{"class":336},[330,1019,1020],{"class":663},"13",[330,1022,900],{"class":336},[330,1024,1026,1028,1030,1032,1034,1036,1039,1041],{"class":332,"line":1025},40,[330,1027,850],{"class":448},[330,1029,464],{"class":336},[330,1031,855],{"class":435},[330,1033,470],{"class":336},[330,1035,473],{"class":336},[330,1037,1038],{"class":476},"〒 100-0001 東京都千代田区千代田 1-1",[330,1040,473],{"class":336},[330,1042,421],{"class":336},[330,1044,1046],{"class":332,"line":1045},41,[330,1047,926],{"class":336},[330,1049,1051,1053,1055,1057,1059,1062,1064,1066,1068,1070,1072,1074,1076,1078],{"class":332,"line":1050},42,[330,1052,813],{"class":448},[330,1054,464],{"class":336},[330,1056,818],{"class":435},[330,1058,470],{"class":336},[330,1060,1061],{"class":663},"5",[330,1063,452],{"class":336},[330,1065,828],{"class":336},[330,1067,831],{"class":790},[330,1069,794],{"class":336},[330,1071,797],{"class":340},[330,1073,464],{"class":336},[330,1075,840],{"class":340},[330,1077,805],{"class":336},[330,1079,442],{"class":336},[330,1081,1083,1085,1087,1089,1091,1093,1096,1098,1100,1102,1104,1106,1108,1110,1112,1114,1116,1118,1120,1123],{"class":332,"line":1082},43,[330,1084,850],{"class":448},[330,1086,464],{"class":336},[330,1088,855],{"class":435},[330,1090,470],{"class":336},[330,1092,473],{"class":336},[330,1094,1095],{"class":476},"合計 ¥ 128,000",[330,1097,473],{"class":336},[330,1099,452],{"class":336},[330,1101,869],{"class":448},[330,1103,464],{"class":336},[330,1105,874],{"class":435},[330,1107,470],{"class":336},[330,1109,473],{"class":336},[330,1111,709],{"class":476},[330,1113,473],{"class":336},[330,1115,885],{"class":336},[330,1117,869],{"class":448},[330,1119,464],{"class":336},[330,1121,1122],{"class":435},"AlignRight",[330,1124,1125],{"class":336},"())\n",[330,1127,1129,1131,1133,1135,1137,1139,1142,1144,1146,1148,1150,1152],{"class":332,"line":1128},44,[330,1130,850],{"class":448},[330,1132,464],{"class":336},[330,1134,855],{"class":435},[330,1136,470],{"class":336},[330,1138,473],{"class":336},[330,1140,1141],{"class":476},"支払期限: 2026-05-31",[330,1143,473],{"class":336},[330,1145,452],{"class":336},[330,1147,869],{"class":448},[330,1149,464],{"class":336},[330,1151,1122],{"class":435},[330,1153,1125],{"class":336},[330,1155,1157],{"class":332,"line":1156},45,[330,1158,926],{"class":336},[330,1160,1162],{"class":332,"line":1161},46,[330,1163,932],{"class":336},[330,1165,1167],{"class":332,"line":1166},47,[330,1168,348],{"emptyLinePlaceholder":347},[330,1170,1172,1175,1177,1179,1181,1183,1185,1188],{"class":332,"line":1171},48,[330,1173,1174],{"class":448},"    data",[330,1176,452],{"class":336},[330,1178,455],{"class":448},[330,1180,458],{"class":336},[330,1182,765],{"class":448},[330,1184,464],{"class":336},[330,1186,1187],{"class":435},"Generate",[330,1189,773],{"class":336},[330,1191,1193,1195,1197,1199,1201],{"class":332,"line":1192},49,[330,1194,487],{"class":354},[330,1196,455],{"class":448},[330,1198,492],{"class":336},[330,1200,495],{"class":336},[330,1202,442],{"class":336},[330,1204,1206,1208,1210,1212,1214,1216],{"class":332,"line":1205},50,[330,1207,503],{"class":448},[330,1209,464],{"class":336},[330,1211,508],{"class":435},[330,1213,470],{"class":336},[330,1215,513],{"class":448},[330,1217,421],{"class":336},[330,1219,1221],{"class":332,"line":1220},51,[330,1222,521],{"class":336},[330,1224,1226,1228,1230,1232,1234,1236,1239,1241,1243,1246,1248,1250,1253,1255,1258,1261,1263,1265,1267],{"class":332,"line":1225},52,[330,1227,487],{"class":354},[330,1229,455],{"class":448},[330,1231,458],{"class":336},[330,1233,461],{"class":448},[330,1235,464],{"class":336},[330,1237,1238],{"class":435},"WriteFile",[330,1240,470],{"class":336},[330,1242,473],{"class":336},[330,1244,1245],{"class":476},"invoice-ja.pdf",[330,1247,473],{"class":336},[330,1249,452],{"class":336},[330,1251,1252],{"class":448}," data",[330,1254,452],{"class":336},[330,1256,1257],{"class":663}," 0o644",[330,1259,1260],{"class":336},");",[330,1262,455],{"class":448},[330,1264,492],{"class":336},[330,1266,495],{"class":336},[330,1268,442],{"class":336},[330,1270,1272,1274,1276,1278,1280,1282],{"class":332,"line":1271},53,[330,1273,503],{"class":448},[330,1275,464],{"class":336},[330,1277,508],{"class":435},[330,1279,470],{"class":336},[330,1281,513],{"class":448},[330,1283,421],{"class":336},[330,1285,1287],{"class":332,"line":1286},54,[330,1288,521],{"class":336},[330,1290,1292],{"class":332,"line":1291},55,[330,1293,1294],{"class":336},"}\n",[18,1296,1297],{},"几点值得注意:",[1299,1300,1301,1319,1328,1345],"ul",{},[88,1302,1303,42,1312,1314,1315,1318],{},[35,1304,1305,1306,1308,1309,1311],{},"没有 ",[22,1307,54],{},"、没有 UTF-8 标志、",[22,1310,855],{}," 也不需要字体路径参数",[22,1313,28],{}," 注册一个 family,",[22,1316,1317],{},"c.Text"," 直接写 Unicode。底层连线全部在内部。",[88,1320,1321,1324,1325,1327],{},[35,1322,1323],{},"粗体是独立的 family,不是标志","。这与 TTF 的分发方式吻合 (Noto Sans JP Regular 和 Bold 是不同的 TTF 文件,",[22,1326,118],{}," 表不同)。Gothic 和 Mincho、Source Han Sans JP 的 Normal / Heavy 都遵循同一模式。",[88,1329,1330,42,1333,133,1336,1339,1340,42],{},[35,1331,1332],{},"布局用网格,不是光标",[22,1334,1335],{},"r.Col(7, ...)",[22,1337,1338],{},"r.Col(5, ...)"," 相加为 12。宽度是声明式的,你不算 x 坐标。详见 ",[1341,1342,1344],"a",{"href":1343},"/zh/blog/12-column-grid","gpdf 的 12 列网格如何工作",[88,1346,1347,1353],{},[35,1348,1349,1352],{},[22,1350,1351],{},"AlignRight()"," 与区域无关","。日文 \"¥ 128,000\" 和 \"$1,280.00\" 用同样的方式右对齐。文本内容不影响布局代码。",[18,1355,1356,1357,1359,1360,1362],{},"用任意阅读器打开生成的 ",[22,1358,1245],{},"。选中\"株式会社 ABC 御中\"粘贴到文本编辑器。得到 ",[22,1361,1005],{},",不乱码。这就是 ToUnicode CMap 在工作;gpdf 默认写出。",[13,1364,1366],{"id":1365},"字体子集化隐藏的体积炸弹","字体子集化:隐藏的体积炸弹",[18,1368,1369,1370,1373,1374,42],{},"教程最常跳过的 CJK in PDF ",[35,1371,1372],{},"最重要性质"," — ",[35,1375,1376],{},"子集嵌入",[18,1378,1379],{},"TTF 是字形轮廓和元数据表的集合。Noto Sans JP Regular 约 17,500 字形、5.1 MB。典型发票用到的唯一日文字符 60〜200 个。每个文档嵌入整套字体是数量级浪费。",[18,1381,1376,1382,1385],{},[35,1383,1384],{},"只保留用过的字形","。gpdf 自动完成。运行上面示例验证:",[321,1387,1391],{"className":1388,"code":1389,"language":1390,"meta":326,"style":326},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","$ ls -l invoice-ja.pdf\n-rw-r--r--  1 dev  staff  34892 Apr 16 10:12 invoice-ja.pdf\n","bash",[22,1392,1393,1407],{"__ignoreMap":326},[330,1394,1395,1398,1401,1404],{"class":332,"line":333},[330,1396,1397],{"class":340},"$",[330,1399,1400],{"class":476}," ls",[330,1402,1403],{"class":476}," -l",[330,1405,1406],{"class":476}," invoice-ja.pdf\n",[330,1408,1409,1412,1415,1418,1421,1424,1427,1430,1433],{"class":332,"line":344},[330,1410,1411],{"class":340},"-rw-r--r--",[330,1413,1414],{"class":663},"  1",[330,1416,1417],{"class":476}," dev",[330,1419,1420],{"class":476},"  staff",[330,1422,1423],{"class":663},"  34892",[330,1425,1426],{"class":476}," Apr",[330,1428,1429],{"class":663}," 16",[330,1431,1432],{"class":476}," 10:12",[330,1434,1406],{"class":476},[18,1436,1437,1438,1440,1441,1444,1445,1448],{},"34 KB。对照:同文档用 ",[22,1439,136],{}," + ",[22,1442,1443],{},"AddUTF8Font(\"NotoSansJP\", \"NotoSansJP-Regular.ttf\", true)"," 生成 (第三个参数是 UTF-8 标志) 是 ",[35,1446,1447],{},"4.9 MB","。同样的输入,同样的输出文本,文件大 143 倍。原因是 fpdf 代码路径在输出时不做子集化,直接嵌入整张字体表。",[18,1450,1451],{},"生产影响:",[1299,1453,1454,1464,1470],{},[88,1455,1456,1459,1460,1463],{},[35,1457,1458],{},"每秒 10 张发票"," (常见 SaaS 规模),子集差 = ",[35,1461,1462],{},"0.3 MB/s vs 43 MB/s"," 的出口字节差。负载均衡器对此有意见。",[88,1465,1466,1469],{},[35,1467,1468],{},"冷存储费用与 PDF 大小线性","。500 万张归档发票 × 5 MB = 25 TB。× 30 KB = 150 GB。对象存储价格把这做成月度四位数对比两位数的差别。",[88,1471,1472,1475],{},[35,1473,1474],{},"邮件投递"," 附件限额 10〜25 MB。一张 5 MB 日文发票 + 其他附件 + MIME 编码,很容易顶到上限。",[18,1477,1478,1479,1482,1483,1482,1486,1482,1489,1492],{},"gpdf 在渲染时子集化。没有开启开关。要看哪些字形进了输出,可以用 gpdf 的本地验证工具,但短版本是:如果你用了 ",[22,1480,1481],{},"株","、",[22,1484,1485],{},"式",[22,1487,1488],{},"会",[22,1490,1491],{},"社",",这四个字形进输出,其余 17,496 不进。",[13,1494,1496],{"id":1495},"混合排版同一行里的漢字-仮名-ascii","混合排版:同一行里的漢字 + 仮名 + ASCII",[18,1498,1499],{},"日文文本很少只有日文。真实世界的一行看起来像这样:",[321,1501,1506],{"className":1502,"code":1504,"language":1505},[1503],"language-text","API の P95 レイテンシは 50 ms 未満です。\n","text",[22,1507,1504],{"__ignoreMap":326},[18,1509,1510],{},"5 种文字并存:罗马字 (ASCII Latin)、片假名、平假名、漢字 (Han)、数字。朴素实现给 ASCII 部分挑错字体,结果单间距的 \"API\" 贴着比例排布的日文,视觉崩坏。",[18,1512,1513,1514,1517,1518,133,1521,1524],{},"gpdf 的默认行为是",[35,1515,1516],{},"用注册的 family 渲染每个码点","。如果 Noto Sans JP 是默认,",[22,1519,1520],{},"API",[22,1522,1523],{},"50 ms"," 就用 Noto Sans JP 的拉丁字形来画 — Noto 有这些 (大多数日文超家族都有)。结果看起来是单一字体,因为它就是。",[18,1526,1527,1528,1531,1532,1534],{},"若要",[35,1529,1530],{},"有意混合"," family (ASCII 用 condensed 无衬线、日文用 Noto Sans JP),注册两个并按 ",[22,1533,1317],{}," 覆盖:",[321,1536,1538],{"className":323,"code":1537,"language":325,"meta":326,"style":326},"c.Text(\"API の P95 レイテンシは 50 ms 未満です。\",\n    template.FontFamily(\"NotoSansJP\"))\nc.Text(\"API latency (P95) is under 50 ms.\",\n    template.FontFamily(\"InterVariable\"))\n",[22,1539,1540,1560,1579,1598],{"__ignoreMap":326},[330,1541,1542,1544,1546,1548,1550,1552,1555,1557],{"class":332,"line":333},[330,1543,831],{"class":448},[330,1545,464],{"class":336},[330,1547,855],{"class":435},[330,1549,470],{"class":336},[330,1551,473],{"class":336},[330,1553,1554],{"class":476},"API の P95 レイテンシは 50 ms 未満です。",[330,1556,473],{"class":336},[330,1558,1559],{"class":336},",\n",[330,1561,1562,1565,1567,1569,1571,1573,1575,1577],{"class":332,"line":344},[330,1563,1564],{"class":448},"    template",[330,1566,464],{"class":336},[330,1568,874],{"class":435},[330,1570,470],{"class":336},[330,1572,473],{"class":336},[330,1574,684],{"class":476},[330,1576,473],{"class":336},[330,1578,900],{"class":336},[330,1580,1581,1583,1585,1587,1589,1591,1594,1596],{"class":332,"line":351},[330,1582,831],{"class":448},[330,1584,464],{"class":336},[330,1586,855],{"class":435},[330,1588,470],{"class":336},[330,1590,473],{"class":336},[330,1592,1593],{"class":476},"API latency (P95) is under 50 ms.",[330,1595,473],{"class":336},[330,1597,1559],{"class":336},[330,1599,1600,1602,1604,1606,1608,1610,1613,1615],{"class":332,"line":361},[330,1601,1564],{"class":448},[330,1603,464],{"class":336},[330,1605,874],{"class":435},[330,1607,470],{"class":336},[330,1609,473],{"class":336},[330,1611,1612],{"class":476},"InterVariable",[330,1614,473],{"class":336},[330,1616,900],{"class":336},[18,1618,1619,1620,1622],{},"两次 ",[22,1621,1317],{},",两个 family,你的代码里没有脚本检测逻辑。同一行内混合 (同一句子里 ASCII 走 Inter、日文走 Noto) 的功能计划在 gpdf v1.2 加入;现在的绕行是手动按脚本边界切分,用横向的列排布。",[13,1624,41],{"id":41},[18,1626,1627],{},"Go 上的日文 PDF 故事已解决 95%。诚实写出剩下 5%。",[18,1629,1630,1633,1634,1637],{},[35,1631,1632],{},"纵排 (縦書き) 尚未支持","。gpdf v1.x 仅支持横排。传统日文排版 — 从右向左的列、自上而下的字符、合适的字形旋转和标点重定位 — 是布局引擎的深度改动,不是渲染微调。有已开设计案的 issue,落地时会落地。现在若必须纵排 (书籍、正式书信),用其它工具 (Word、InDesign、pandoc + LuaLaTeX 管道) 产出纵排 PDF,再用 ",[22,1635,1636],{},"gpdf.Merge"," 合并。",[18,1639,1640,1643,1644,1647],{},[35,1641,1642],{},"旁注假名 (ルビ、振り仮名) 只能绕行","。没有 ",[22,1645,1646],{},"c.Ruby(\"漢字\", \"かんじ\")"," 原语。若儿童内容或语言教材需要,绕行是两行列结构:上面小号假名、下面普通汉字,对齐。能用,手动,假名边界的精细字距要小心。",[18,1649,1650,1653,1654,1482,1657,1482,1660,1663,1664,1666,1667,1671],{},[35,1651,1652],{},"多 CJK 字体间的自动回退不存在","。若用户输入混了日文漢字和中文专有字 (",[22,1655,1656],{},"直",[22,1658,1659],{},"骨",[22,1661,1662],{},"角"," 在 JP/CN 字形略有差别),你要手动分割并用两个 family。同一 ",[22,1665,1317],{}," 调用内不会跨 family 自动回退。实际上很少有文档需要这种,但若需要请参考 ",[1341,1668,1670],{"href":1669},"/zh/blog/","JP/CN/KR/EN 混排 PDF"," (B-070 待发)。",[18,1673,1674,1677,1678,1681,1682,1685],{},[35,1675,1676],{},"严格 PDF/A-2b + 日文","。gpdf 通过 ",[22,1679,1680],{},"gpdf.WithPDFA"," 产出 PDF/A,但嵌入字形元数据、CJK 段的 ",[22,1683,1684],{},"ActualText","、带标签的结构树等严格合规要求在 CJK 场景下仍在打磨。若要长期归档 (中国的会计凭证归档条例、日本的电子帐簿保存法),在提交前用第三方工具 (veraPDF 免费) 校验。",[18,1687,1688],{},"这些都不是常见场景 (发票、报表、对账单、收据、凭证) 的阻塞项。写出来是因为总会有人在生产里踩到其中之一,\"在路线图上\"没有\"这里是绕行方案\"实用。",[13,1690,1691],{"id":1691},"合规视角",[18,1693,1694,1695,1698],{},"一条生态上下文通常说得太少:",[35,1696,1697],{},"2026 年的日文 PDF 生成不只是排版问题","。两项监管要求把它推进了合规讨论。",[18,1700,1701,1704],{},[35,1702,1703],{},"适格请求書 (资格发票)"," 制度要求发票包含特定字段 (登录业务编号、适用税率、税额明细) 并以防篡改方式保存。PDF 是默认格式,\"防篡改\"映射为 PDF 数字签名 — 严格模式下即 PAdES-B-LT。",[18,1706,1707,1710,1711,42],{},[35,1708,1709],{},"电子帳簿保存法"," (2024 修订) 扩展了电子形式收到的发票的留存要求。归档 PDF 必须满足完整性要求。事实目标格式是 ",[35,1712,1713],{},"PDF/A-2b 或 PDF/A-3b",[18,1715,1716,1717,1720,1721,1724,1725,1727],{},"两者都依赖 ",[35,1718,1719],{},"PDF 原生能力"," — 签名、长期验证、PDF/A 嵌入元数据。经无头浏览器的 HTML→PDF 两边都不能干净满足:Chromium 的 PDF 输出不是 PDF/A,也不能一步嵌入数字签名。原生 Go 栈 (gpdf + ",[22,1722,1723],{},"gpdf/signature"," 走 PAdES + ",[22,1726,1680],{},") 可以在单一流水线、不出进程地完成全链条。",[18,1729,1730],{},"这是预告,不是深潜 — 签名与 PDF/A 各自值一篇长文 (待发 B-067、B-068)。但若今天要选日文 PDF 栈且合规进入视野,请挑一个原生支持签名和 PDF/A 的。从\"能跑\"到\"过审\"的迁移税是真实的,推迟支付更贵。",[13,1732,1734],{"id":1733},"faq","FAQ",[18,1736,1737,1740,1741,1744,1745,1748,1749,1752,1753,1756,1757,1760],{},[35,1738,1739],{},"需要在服务器或容器里装字体吗?","\n不需要。gpdf 读取 TTF 字节,不走系统字体缓存。",[22,1742,1743],{},"os.ReadFile(\"NotoSansJP-Regular.ttf\")"," 或 ",[22,1746,1747],{},"//go:embed NotoSansJP-Regular.ttf"," 在 macOS / Linux / Windows、distroless 容器、AWS Lambda 上等价。无需 ",[22,1750,1751],{},"fontconfig",",无需 ",[22,1754,1755],{},"fc-cache -fv","。这是 gpdf 能在 ",[22,1758,1759],{},"FROM scratch"," 镜像里运行的理由之一。",[18,1762,1763,1766],{},[35,1764,1765],{},"Noto Sans JP vs Source Han Sans JP 有区别吗?","\n同字体,两个名字。Adobe 发行 Source Han Sans JP,Google 重新打包为 Noto Sans JP。字形覆盖相同。都是 SIL Open Font License — 选法务容易通过的那个。gpdf 示例默认用 Noto Sans JP 是因为文件名好记。",[18,1768,1769,1772,1773,1776],{},[35,1770,1771],{},"游ゴシック (Yu Gothic) 或 Hiragino 呢?","\nOS 自带的商用字体。只要部署目标授权 (Windows Server 带 Yu Gothic、macOS 带 Hiragino) 就能用,但 TTF 文件的获取和容器构建中的再分发条款得自行确认。开放部署用 ",[35,1774,1775],{},"Noto Sans JP 或 IPAex 黑体"," (均可自由再分发)。",[18,1778,1779,1786,1787,1790],{},[35,1780,1781,1782,1785],{},"PDF 出来但 ",[22,1783,1784],{},"Ctrl+F"," 搜不到","\n几乎总是 ToUnicode CMap 问题。gpdf 默认写出,若用 gpdf 仍遇到请带阅读器名字开 issue。用 gofpdf 遇到的话,修复方法是启用 UTF-8 标志",[35,1788,1789],{},"并","确认阅读器支持 CID 字体 (旧版 macOS Preview.app 有已知问题)。用 Adobe Reader 或 Chrome 做对照。",[18,1792,1793,1796,1797,1800],{},[35,1794,1795],{},"字体里没有的 JIS X 0213 字符怎么办?","\n没有字形就画不出来。实用答:\"用覆盖 JIS X 0213 的字体\"。Noto Sans JP 覆盖 BMP 全域加 JIS X 0213 第一水平。罕见异体有 Hanazono Mincho (花园明朝) 这种末端回退。若任何字体都没有该码点,gpdf 输出 Unicode 替换字符 (U+FFFD) — 不是无声的豆腐,会显示 ",[22,1798,1799],{},"�",",提示你去查。",[18,1802,1803,1806,1807,1809],{},[35,1804,1805],{},"CJK 比 ASCII 慢吗?","\n小幅慢。gpdf 的\"complex CJK invoice\"基准在 Apple M1 是 133 µs,ASCII 4×10 表格是 108 µs。约 23% 开销,主要来自字形查找和子集化。参考:同一 CJK 基准 ",[22,1808,136],{}," 254 µs、Maroto v2 10.4 ms。日文渲染不会成为服务瓶颈。",[13,1811,1813],{"id":1812},"试用-gpdf","试用 gpdf",[18,1815,1816],{},"gpdf 是一个 Go 的 PDF 生成库。MIT,零外部依赖,原生 CJK。",[321,1818,1820],{"className":1388,"code":1819,"language":1390,"meta":326,"style":326},"go get github.com/gpdf-dev/gpdf\n",[22,1821,1822],{"__ignoreMap":326},[330,1823,1824,1826,1829],{"class":332,"line":333},[330,1825,325],{"class":340},[330,1827,1828],{"class":476}," get",[330,1830,1831],{"class":476}," github.com/gpdf-dev/gpdf\n",[18,1833,1834,1840,1841],{},[1341,1835,1839],{"href":1836,"rel":1837},"https://github.com/gpdf-dev/gpdf",[1838],"nofollow","⭐ Star on GitHub"," · ",[1341,1842,1845],{"href":1843,"rel":1844},"https://gpdf.dev/zh/docs/quickstart",[1838],"阅读文档",[13,1847,1848],{"id":1848},"延伸阅读",[1299,1850,1851,1858,1865,1871],{},[88,1852,1853,1857],{},[1341,1854,1856],{"href":1855},"/zh/blog/embed-japanese-font","如何用 gpdf 嵌入日文字体?"," — 不含背景的三行配方",[88,1859,1860,1864],{},[1341,1861,1863],{"href":1862},"/zh/blog/noto-sans-jp-with-gpdf","如何用 Noto Sans JP 配合 gpdf?"," — Regular / Bold / Medium 字重设置",[88,1866,1867,1870],{},[1341,1868,1869],{"href":1343},"gpdf 的 12 列网格如何工作?"," — 取代光标计算的布局习语",[88,1872,1873,1877],{},[1341,1874,1876],{"href":1875},"/zh/blog/go-pdf-fpdf-archived","go-pdf/fpdf 也归档了。2026 年的 Go PDF 栈"," — 更广的 2026 版图",[1879,1880,1881],"style",{},"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 .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 .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 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 .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);}",{"title":326,"searchDepth":344,"depth":344,"links":1883},[1884,1885,1886,1887,1888,1889,1890,1891,1892,1893,1894,1895],{"id":15,"depth":344,"text":16},{"id":45,"depth":344,"text":45},{"id":71,"depth":344,"text":72},{"id":154,"depth":344,"text":155},{"id":309,"depth":344,"text":309},{"id":1365,"depth":344,"text":1366},{"id":1495,"depth":344,"text":1496},{"id":41,"depth":344,"text":41},{"id":1691,"depth":344,"text":1691},{"id":1733,"depth":344,"text":1734},{"id":1812,"depth":344,"text":1813},{"id":1848,"depth":344,"text":1848},"2026-04-16","用 Go 生成日文 PDF 的完整流程。无 CGO、无 Chromium、无豆腐字。涵盖字体、子集化、混合排版、纵排。",false,"md",{"name":1901,"totalTime":1902,"tools":1903,"steps":1906},"用 Go 生成带原生 TrueType 子集嵌入的日文 PDF","PT20M",[1904,1905],"Go 1.22+","NotoSansJP-Regular.ttf 和 NotoSansJP-Bold.ttf(或任意支持日文的 TTF 字体对)",[1907,1910,1913,1916,1919,1922],{"name":1908,"text":1909},"安装 gpdf 并准备字体","执行 go get github.com/gpdf-dev/gpdf。从 Google Fonts 下载 Noto Sans JP Regular 和 Bold,放在 main.go 旁边。无需 CGO,无需系统字体配置。",{"name":1911,"text":1912},"启动时读取 TTF 字节","用 os.ReadFile 把两个 TTF 文件读入 []byte。如果想把字体打进二进制,也可以用 //go:embed。",{"name":1914,"text":1915},"在文档构造时注册字体","把 gpdf.WithFont(\"NotoSansJP\", regular) 和 gpdf.WithFont(\"NotoSansJP-Bold\", bold) 传给 gpdf.NewDocument。family 名字是任意句柄,后续引用时保持一致即可。",{"name":1917,"text":1918},"将日文字体设为默认","添加 gpdf.WithDefaultFont(\"NotoSansJP\", 11)。之后的 c.Text 调用无需显式 FontFamily 选项就会用日文字体。",{"name":1920,"text":1921},"用 c.Text 构建文档树","在 page.AutoRow 块内调用 r.Col(span, fn),写 c.Text(\"こんにちは、世界。\")。粗体和字号是 template 选项,不是单独方法。",{"name":1923,"text":1924},"生成并验证输出","调用 doc.Generate() 得到 []byte,用 os.WriteFile 写盘。打开 PDF,选中文字粘贴到文本编辑器 — ToUnicode CMap 保证复制粘贴可用。",null,{},"/zh/blog/japanese-pdf-in-go",{"title":5,"description":1897},"zh/blog/007.japanese-pdf-in-go",[1931,1932,1933],"tutorial","cjk","troubleshooting","XF2bLbr-URdjq46rnznhVD3z4d5i-OziNbpWceX4ZaU",1776529262281]