[{"data":1,"prerenderedAt":1602},["ShallowReactive",2],{"blog-zh-noto-sans-jp-with-gpdf":3},{"id":4,"title":5,"author":6,"body":9,"date":1569,"description":1570,"draft":1571,"extension":1572,"howTo":1573,"image":1592,"meta":1593,"navigation":81,"path":1594,"seo":1595,"stem":1596,"tags":1597,"updated":1592,"__hash__":1601},"blogZh/zh/blog/004.noto-sans-jp-with-gpdf.md","如何在 gpdf 中使用 Noto Sans JP？",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1556},"minimark",[12,16,33,37,51,54,704,724,728,735,764,772,794,797,801,804,873,894,907,911,914,1067,1086,1089,1161,1171,1174,1177,1180,1247,1250,1253,1257,1260,1266,1280,1289,1304,1307,1310,1469,1476,1479,1516,1520,1523,1540,1552],[13,14,15],"h2",{"id":15},"把问题换个说法",[17,18,19,20,27,28,32],"p",{},"你想在 ",[21,22,26],"a",{"href":23,"rel":24},"https://github.com/gpdf-dev/gpdf",[25],"nofollow","gpdf"," 文档里渲染日语，字体选了 Noto Sans JP — Google 发布的 SIL OFL 免费无衬线字体，完整覆盖 JIS 字符集。你已经下好了 Google Fonts 的 zip 包。从这里开始，你想知道的三件事是：",[29,30,31],"strong",{},"选哪个文件、注册哪几个字重、zip 里藏着的那一个坑是什么","。",[13,34,36],{"id":35},"结论-tldr","结论 (TL;DR)",[17,38,39,40,46,47,50],{},"解压 zip 之后，用 ",[29,41,42],{},[43,44,45],"code",{},"static/NotoSansJP-Regular.ttf"," — 不是根目录下的 variable font。把它传给 ",[43,48,49],{},"gpdf.WithFont(\"NotoSansJP\", bytes)"," 并设为默认字体。gpdf 会从大约 17,000 个字形里只把你实际渲染过的那些子集化后嵌入 PDF — 一份普通发票最终携带的字体数据通常在 20–40 KB。",[13,52,53],{"id":53},"完整示例",[55,56,61],"pre",{"className":57,"code":58,"language":59,"meta":60,"style":60},"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    font, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", font),\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.FontSize(28), template.Bold())\n            c.Text(\"Noto Sans JP、これで十分。\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[43,62,63,76,83,93,105,115,120,130,140,150,156,161,177,216,232,250,256,261,280,303,341,367,392,398,403,422,456,493,541,561,567,573,578,599,612,627,632,678,693,698],{"__ignoreMap":60},[64,65,68,72],"span",{"class":66,"line":67},"line",1,[64,69,71],{"class":70},"sMK4o","package",[64,73,75],{"class":74},"sBMFI"," main\n",[64,77,79],{"class":66,"line":78},2,[64,80,82],{"emptyLinePlaceholder":81},true,"\n",[64,84,86,90],{"class":66,"line":85},3,[64,87,89],{"class":88},"s7zQu","import",[64,91,92],{"class":70}," (\n",[64,94,96,99,102],{"class":66,"line":95},4,[64,97,98],{"class":70},"    \"",[64,100,101],{"class":74},"log",[64,103,104],{"class":70},"\"\n",[64,106,108,110,113],{"class":66,"line":107},5,[64,109,98],{"class":70},[64,111,112],{"class":74},"os",[64,114,104],{"class":70},[64,116,118],{"class":66,"line":117},6,[64,119,82],{"emptyLinePlaceholder":81},[64,121,123,125,128],{"class":66,"line":122},7,[64,124,98],{"class":70},[64,126,127],{"class":74},"github.com/gpdf-dev/gpdf",[64,129,104],{"class":70},[64,131,133,135,138],{"class":66,"line":132},8,[64,134,98],{"class":70},[64,136,137],{"class":74},"github.com/gpdf-dev/gpdf/document",[64,139,104],{"class":70},[64,141,143,145,148],{"class":66,"line":142},9,[64,144,98],{"class":70},[64,146,147],{"class":74},"github.com/gpdf-dev/gpdf/template",[64,149,104],{"class":70},[64,151,153],{"class":66,"line":152},10,[64,154,155],{"class":70},")\n",[64,157,159],{"class":66,"line":158},11,[64,160,82],{"emptyLinePlaceholder":81},[64,162,164,167,171,174],{"class":66,"line":163},12,[64,165,166],{"class":70},"func",[64,168,170],{"class":169},"s2Zo4"," main",[64,172,173],{"class":70},"()",[64,175,176],{"class":70}," {\n",[64,178,180,184,187,190,193,196,199,202,205,208,212,214],{"class":66,"line":179},13,[64,181,183],{"class":182},"sTEyZ","    font",[64,185,186],{"class":70},",",[64,188,189],{"class":182}," err ",[64,191,192],{"class":70},":=",[64,194,195],{"class":182}," os",[64,197,198],{"class":70},".",[64,200,201],{"class":169},"ReadFile",[64,203,204],{"class":70},"(",[64,206,207],{"class":70},"\"",[64,209,211],{"class":210},"sfazB","NotoSansJP-Regular.ttf",[64,213,207],{"class":70},[64,215,155],{"class":70},[64,217,219,222,224,227,230],{"class":66,"line":218},14,[64,220,221],{"class":88},"    if",[64,223,189],{"class":182},[64,225,226],{"class":70},"!=",[64,228,229],{"class":70}," nil",[64,231,176],{"class":70},[64,233,235,238,240,243,245,248],{"class":66,"line":234},15,[64,236,237],{"class":182},"        log",[64,239,198],{"class":70},[64,241,242],{"class":169},"Fatal",[64,244,204],{"class":70},[64,246,247],{"class":182},"err",[64,249,155],{"class":70},[64,251,253],{"class":66,"line":252},16,[64,254,255],{"class":70},"    }\n",[64,257,259],{"class":66,"line":258},17,[64,260,82],{"emptyLinePlaceholder":81},[64,262,264,267,269,272,274,277],{"class":66,"line":263},18,[64,265,266],{"class":182},"    doc ",[64,268,192],{"class":70},[64,270,271],{"class":182}," gpdf",[64,273,198],{"class":70},[64,275,276],{"class":169},"NewDocument",[64,278,279],{"class":70},"(\n",[64,281,283,286,288,291,293,295,297,300],{"class":66,"line":282},19,[64,284,285],{"class":182},"        gpdf",[64,287,198],{"class":70},[64,289,290],{"class":169},"WithPageSize",[64,292,204],{"class":70},[64,294,26],{"class":182},[64,296,198],{"class":70},[64,298,299],{"class":182},"A4",[64,301,302],{"class":70},"),\n",[64,304,306,308,310,313,315,318,320,323,325,327,329,332,334,338],{"class":66,"line":305},20,[64,307,285],{"class":182},[64,309,198],{"class":70},[64,311,312],{"class":169},"WithMargins",[64,314,204],{"class":70},[64,316,317],{"class":182},"document",[64,319,198],{"class":70},[64,321,322],{"class":169},"UniformEdges",[64,324,204],{"class":70},[64,326,317],{"class":182},[64,328,198],{"class":70},[64,330,331],{"class":169},"Mm",[64,333,204],{"class":70},[64,335,337],{"class":336},"sbssI","20",[64,339,340],{"class":70},"))),\n",[64,342,344,346,348,351,353,355,358,360,362,365],{"class":66,"line":343},21,[64,345,285],{"class":182},[64,347,198],{"class":70},[64,349,350],{"class":169},"WithFont",[64,352,204],{"class":70},[64,354,207],{"class":70},[64,356,357],{"class":210},"NotoSansJP",[64,359,207],{"class":70},[64,361,186],{"class":70},[64,363,364],{"class":182}," font",[64,366,302],{"class":70},[64,368,370,372,374,377,379,381,383,385,387,390],{"class":66,"line":369},22,[64,371,285],{"class":182},[64,373,198],{"class":70},[64,375,376],{"class":169},"WithDefaultFont",[64,378,204],{"class":70},[64,380,207],{"class":70},[64,382,357],{"class":210},[64,384,207],{"class":70},[64,386,186],{"class":70},[64,388,389],{"class":336}," 11",[64,391,302],{"class":70},[64,393,395],{"class":66,"line":394},23,[64,396,397],{"class":70},"    )\n",[64,399,401],{"class":66,"line":400},24,[64,402,82],{"emptyLinePlaceholder":81},[64,404,406,409,411,414,416,419],{"class":66,"line":405},25,[64,407,408],{"class":182},"    page ",[64,410,192],{"class":70},[64,412,413],{"class":182}," doc",[64,415,198],{"class":70},[64,417,418],{"class":169},"AddPage",[64,420,421],{"class":70},"()\n",[64,423,425,428,430,433,436,440,443,446,448,451,454],{"class":66,"line":424},26,[64,426,427],{"class":182},"    page",[64,429,198],{"class":70},[64,431,432],{"class":169},"AutoRow",[64,434,435],{"class":70},"(func(",[64,437,439],{"class":438},"sHdIc","r",[64,441,442],{"class":70}," *",[64,444,445],{"class":74},"template",[64,447,198],{"class":70},[64,449,450],{"class":74},"RowBuilder",[64,452,453],{"class":70},")",[64,455,176],{"class":70},[64,457,459,462,464,467,469,472,474,477,480,482,484,486,489,491],{"class":66,"line":458},27,[64,460,461],{"class":182},"        r",[64,463,198],{"class":70},[64,465,466],{"class":169},"Col",[64,468,204],{"class":70},[64,470,471],{"class":336},"12",[64,473,186],{"class":70},[64,475,476],{"class":70}," func(",[64,478,479],{"class":438},"c",[64,481,442],{"class":70},[64,483,445],{"class":74},[64,485,198],{"class":70},[64,487,488],{"class":74},"ColBuilder",[64,490,453],{"class":70},[64,492,176],{"class":70},[64,494,496,499,501,504,506,508,511,513,515,518,520,523,525,528,531,533,535,538],{"class":66,"line":495},28,[64,497,498],{"class":182},"            c",[64,500,198],{"class":70},[64,502,503],{"class":169},"Text",[64,505,204],{"class":70},[64,507,207],{"class":70},[64,509,510],{"class":210},"請求書",[64,512,207],{"class":70},[64,514,186],{"class":70},[64,516,517],{"class":182}," template",[64,519,198],{"class":70},[64,521,522],{"class":169},"FontSize",[64,524,204],{"class":70},[64,526,527],{"class":336},"28",[64,529,530],{"class":70},"),",[64,532,517],{"class":182},[64,534,198],{"class":70},[64,536,537],{"class":169},"Bold",[64,539,540],{"class":70},"())\n",[64,542,544,546,548,550,552,554,557,559],{"class":66,"line":543},29,[64,545,498],{"class":182},[64,547,198],{"class":70},[64,549,503],{"class":169},[64,551,204],{"class":70},[64,553,207],{"class":70},[64,555,556],{"class":210},"Noto Sans JP、これで十分。",[64,558,207],{"class":70},[64,560,155],{"class":70},[64,562,564],{"class":66,"line":563},30,[64,565,566],{"class":70},"        })\n",[64,568,570],{"class":66,"line":569},31,[64,571,572],{"class":70},"    })\n",[64,574,576],{"class":66,"line":575},32,[64,577,82],{"emptyLinePlaceholder":81},[64,579,581,584,586,588,590,592,594,597],{"class":66,"line":580},33,[64,582,583],{"class":182},"    data",[64,585,186],{"class":70},[64,587,189],{"class":182},[64,589,192],{"class":70},[64,591,413],{"class":182},[64,593,198],{"class":70},[64,595,596],{"class":169},"Generate",[64,598,421],{"class":70},[64,600,602,604,606,608,610],{"class":66,"line":601},34,[64,603,221],{"class":88},[64,605,189],{"class":182},[64,607,226],{"class":70},[64,609,229],{"class":70},[64,611,176],{"class":70},[64,613,615,617,619,621,623,625],{"class":66,"line":614},35,[64,616,237],{"class":182},[64,618,198],{"class":70},[64,620,242],{"class":169},[64,622,204],{"class":70},[64,624,247],{"class":182},[64,626,155],{"class":70},[64,628,630],{"class":66,"line":629},36,[64,631,255],{"class":70},[64,633,635,637,639,641,643,645,648,650,652,655,657,659,662,664,667,670,672,674,676],{"class":66,"line":634},37,[64,636,221],{"class":88},[64,638,189],{"class":182},[64,640,192],{"class":70},[64,642,195],{"class":182},[64,644,198],{"class":70},[64,646,647],{"class":169},"WriteFile",[64,649,204],{"class":70},[64,651,207],{"class":70},[64,653,654],{"class":210},"invoice.pdf",[64,656,207],{"class":70},[64,658,186],{"class":70},[64,660,661],{"class":182}," data",[64,663,186],{"class":70},[64,665,666],{"class":336}," 0o644",[64,668,669],{"class":70},");",[64,671,189],{"class":182},[64,673,226],{"class":70},[64,675,229],{"class":70},[64,677,176],{"class":70},[64,679,681,683,685,687,689,691],{"class":66,"line":680},38,[64,682,237],{"class":182},[64,684,198],{"class":70},[64,686,242],{"class":169},[64,688,204],{"class":70},[64,690,247],{"class":182},[64,692,155],{"class":70},[64,694,696],{"class":66,"line":695},39,[64,697,255],{"class":70},[64,699,701],{"class":66,"line":700},40,[64,702,703],{"class":70},"}\n",[17,705,706,707,712,713,715,716,719,720,723],{},"从 ",[21,708,711],{"href":709,"rel":710},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[25],"Google Fonts"," 下载 zip，解压后把 ",[43,714,45],{}," 放到 ",[43,717,718],{},"main.go"," 旁边，运行 ",[43,721,722],{},"go run main.go"," — 就能得到一页的 PDF。",[13,725,727],{"id":726},"选-static-ttf不要选-variable-font","选 static TTF，不要选 variable font",[17,729,730,731,734],{},"在 Google Fonts 点 ",[29,732,733],{},"Get font → Download all","，解压 zip，里面有两组看起来差不多、实际上完全不一样的文件:",[736,737,738,750],"ul",{},[739,740,741,742,745,746,749],"li",{},"根目录的 ",[43,743,744],{},"NotoSansJP-VariableFont_wght.ttf"," — ",[29,747,748],{},"variable 字体","，把 100–900 所有字重合并在一个文件里，约 7 MB",[739,751,752,755,756,759,760,763],{},[43,753,754],{},"static/"," 目录 — 9 个独立的 TTF，从 ",[43,757,758],{},"NotoSansJP-Thin.ttf"," 到 ",[43,761,762],{},"NotoSansJP-Black.ttf","，每个约 5 MB",[17,765,766,32],{},[29,767,768,769,771],{},"选 ",[43,770,754],{}," 里的那一个",[17,773,774,775,778,779,782,783,786,787,786,790,793],{},"gpdf 的 TrueType 解析器是刻意收窄过功能的。它处理字形轮廓、复合字形、",[43,776,777],{},"cmap","、",[43,780,781],{},"hmtx"," — 渲染固定字重文本所需的表。但它不处理让 variable font 真正可变的 ",[43,784,785],{},"fvar"," / ",[43,788,789],{},"gvar",[43,791,792],{},"HVAR"," 表。如果你把 VariableFont_wght.ttf 喂给它，要么解析器直接报错，要么更糟糕 — 它拿到默认实例的字形，然后默默无视你以为自己设好的字重轴。",[17,795,796],{},"文件大小也支持 static 方案。variable 字体把整个字重轴上的所有 outline 都塞在一个文件里，这是设计初衷 — 但如果你只用 Regular，就等于白付了另外 8 个字重的数据。static Regular 是 5 MB，variable 是 7 MB。子集化会把两者都削掉，但 static 是更干净的输入。",[13,798,800],{"id":799},"关键就是这-4-行","关键就是这 4 行",[17,802,803],{},"真正有意思的只有构造函数的选项:",[55,805,807],{"className":57,"code":806,"language":59,"meta":60,"style":60},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,808,809,824,847,869],{"__ignoreMap":60},[64,810,811,814,816,818,820,822],{"class":66,"line":67},[64,812,813],{"class":182},"doc ",[64,815,192],{"class":70},[64,817,271],{"class":182},[64,819,198],{"class":70},[64,821,276],{"class":169},[64,823,279],{"class":70},[64,825,826,829,831,833,835,837,839,841,843,845],{"class":66,"line":78},[64,827,828],{"class":182},"    gpdf",[64,830,198],{"class":70},[64,832,350],{"class":169},[64,834,204],{"class":70},[64,836,207],{"class":70},[64,838,357],{"class":210},[64,840,207],{"class":70},[64,842,186],{"class":70},[64,844,364],{"class":182},[64,846,302],{"class":70},[64,848,849,851,853,855,857,859,861,863,865,867],{"class":66,"line":85},[64,850,828],{"class":182},[64,852,198],{"class":70},[64,854,376],{"class":169},[64,856,204],{"class":70},[64,858,207],{"class":70},[64,860,357],{"class":210},[64,862,207],{"class":70},[64,864,186],{"class":70},[64,866,389],{"class":336},[64,868,302],{"class":70},[64,870,871],{"class":66,"line":95},[64,872,155],{"class":70},[17,874,875,876,879,880,778,883,778,886,889,890,893],{},"字体族名 (",[43,877,878],{},"\"NotoSansJP\"",") 是任意的。gpdf 把它当查找键用 — 不是文件路径，也不是读自字体元数据的名字。如果你的项目里 ",[43,881,882],{},"\"body\"",[43,884,885],{},"\"jp\"",[43,887,888],{},"\"Noto\""," 读起来更顺手就用那个。只要后面 ",[43,891,892],{},"template.FontFamily(...)"," 保持一致即可。",[17,895,896,898,899,902,903,906],{},[43,897,376],{}," 能让你不用在每个 ",[43,900,901],{},"c.Text"," 里写 ",[43,904,905],{},"template.FontFamily(\"NotoSansJP\")","。省掉它，gpdf 会回落到 Helvetica — 而 Helvetica 不覆盖任何 CJK 码位，你会得到一篇只有标题正常、正文全是豆腐 (□□□) 的 PDF。",[13,908,910],{"id":909},"到底需要哪几个字重","到底需要哪几个字重？",[17,912,913],{},"发票、收据、业务报告只需要 Regular 和 Bold 两个:",[55,915,917],{"className":57,"code":916,"language":59,"meta":60,"style":60},"reg,  _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nbold, _ := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", reg),\n    gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,918,919,947,976,980,994,1017,1041,1063],{"__ignoreMap":60},[64,920,921,924,926,929,931,933,935,937,939,941,943,945],{"class":66,"line":67},[64,922,923],{"class":182},"reg",[64,925,186],{"class":70},[64,927,928],{"class":182},"  _ ",[64,930,192],{"class":70},[64,932,195],{"class":182},[64,934,198],{"class":70},[64,936,201],{"class":169},[64,938,204],{"class":70},[64,940,207],{"class":70},[64,942,211],{"class":210},[64,944,207],{"class":70},[64,946,155],{"class":70},[64,948,949,952,954,957,959,961,963,965,967,969,972,974],{"class":66,"line":78},[64,950,951],{"class":182},"bold",[64,953,186],{"class":70},[64,955,956],{"class":182}," _ ",[64,958,192],{"class":70},[64,960,195],{"class":182},[64,962,198],{"class":70},[64,964,201],{"class":169},[64,966,204],{"class":70},[64,968,207],{"class":70},[64,970,971],{"class":210},"NotoSansJP-Bold.ttf",[64,973,207],{"class":70},[64,975,155],{"class":70},[64,977,978],{"class":66,"line":85},[64,979,82],{"emptyLinePlaceholder":81},[64,981,982,984,986,988,990,992],{"class":66,"line":95},[64,983,813],{"class":182},[64,985,192],{"class":70},[64,987,271],{"class":182},[64,989,198],{"class":70},[64,991,276],{"class":169},[64,993,279],{"class":70},[64,995,996,998,1000,1002,1004,1006,1008,1010,1012,1015],{"class":66,"line":107},[64,997,828],{"class":182},[64,999,198],{"class":70},[64,1001,350],{"class":169},[64,1003,204],{"class":70},[64,1005,207],{"class":70},[64,1007,357],{"class":210},[64,1009,207],{"class":70},[64,1011,186],{"class":70},[64,1013,1014],{"class":182}," reg",[64,1016,302],{"class":70},[64,1018,1019,1021,1023,1025,1027,1029,1032,1034,1036,1039],{"class":66,"line":117},[64,1020,828],{"class":182},[64,1022,198],{"class":70},[64,1024,350],{"class":169},[64,1026,204],{"class":70},[64,1028,207],{"class":70},[64,1030,1031],{"class":210},"NotoSansJP-Bold",[64,1033,207],{"class":70},[64,1035,186],{"class":70},[64,1037,1038],{"class":182}," bold",[64,1040,302],{"class":70},[64,1042,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061],{"class":66,"line":122},[64,1044,828],{"class":182},[64,1046,198],{"class":70},[64,1048,376],{"class":169},[64,1050,204],{"class":70},[64,1052,207],{"class":70},[64,1054,357],{"class":210},[64,1056,207],{"class":70},[64,1058,186],{"class":70},[64,1060,389],{"class":336},[64,1062,302],{"class":70},[64,1064,1065],{"class":66,"line":132},[64,1066,155],{"class":70},[17,1068,1069,1070,1073,1074,1077,1078,1081,1082,1085],{},"用 ",[43,1071,1072],{},"-Bold"," 这个后缀注册之后，",[43,1075,1076],{},"template.Bold()"," 会自动挑到它。",[43,1079,1080],{},"-Italic"," 和 ",[43,1083,1084],{},"-BoldItalic"," 规则相同。不过 Noto Sans JP 没有斜体 — CJK 字体通常不发布斜体，因为字形本身没有自然的倾斜形式。需要在日语里做强调时，用颜色、字号或粗体代替。",[17,1087,1088],{},"宣传册需要 Medium 或 SemiBold 时，用任意后缀注册，再按字体族名直接引用即可:",[55,1090,1092],{"className":57,"code":1091,"language":59,"meta":60,"style":60},"gpdf.WithFont(\"NotoSansJP-Medium\", medium)\n// ...\nc.Text(\"見出し\", template.FontFamily(\"NotoSansJP-Medium\"))\n",[43,1093,1094,1118,1124],{"__ignoreMap":60},[64,1095,1096,1098,1100,1102,1104,1106,1109,1111,1113,1116],{"class":66,"line":67},[64,1097,26],{"class":182},[64,1099,198],{"class":70},[64,1101,350],{"class":169},[64,1103,204],{"class":70},[64,1105,207],{"class":70},[64,1107,1108],{"class":210},"NotoSansJP-Medium",[64,1110,207],{"class":70},[64,1112,186],{"class":70},[64,1114,1115],{"class":182}," medium",[64,1117,155],{"class":70},[64,1119,1120],{"class":66,"line":78},[64,1121,1123],{"class":1122},"sHwdD","// ...\n",[64,1125,1126,1128,1130,1132,1134,1136,1139,1141,1143,1145,1147,1150,1152,1154,1156,1158],{"class":66,"line":85},[64,1127,479],{"class":182},[64,1129,198],{"class":70},[64,1131,503],{"class":169},[64,1133,204],{"class":70},[64,1135,207],{"class":70},[64,1137,1138],{"class":210},"見出し",[64,1140,207],{"class":70},[64,1142,186],{"class":70},[64,1144,517],{"class":182},[64,1146,198],{"class":70},[64,1148,1149],{"class":169},"FontFamily",[64,1151,204],{"class":70},[64,1153,207],{"class":70},[64,1155,1108],{"class":210},[64,1157,207],{"class":70},[64,1159,1160],{"class":70},"))\n",[17,1162,1163,1164,786,1166,786,1168,1170],{},"基于后缀的 Bold/Italic 快捷方式只在 ",[43,1165,1072],{},[43,1167,1080],{},[43,1169,1084],{}," 这三个字面名上生效，其他都按族名显式引用。",[13,1172,1173],{"id":1173},"子集化之后的真实体积",[17,1175,1176],{},"Noto Sans JP Regular 磁盘上约 5 MB。这个数字让一些团队去另建字体 CDN、或者给 PDF 做后处理剥离字体。用 gpdf 不需要。",[17,1178,1179],{},"下面是实际落到 PDF 里的数据量:",[1181,1182,1183,1199],"table",{},[1184,1185,1186],"thead",{},[1187,1188,1189,1193,1196],"tr",{},[1190,1191,1192],"th",{},"文档",[1190,1194,1195],{},"使用字形数",[1190,1197,1198],{},"PDF 中的字体数据",[1200,1201,1202,1214,1225,1236],"tbody",{},[1187,1203,1204,1208,1211],{},[1205,1206,1207],"td",{},"一行收据 (约 15 字)",[1205,1209,1210],{},"约 14",[1205,1212,1213],{},"约 11 KB",[1187,1215,1216,1219,1222],{},[1205,1217,1218],{},"普通发票 (约 200 字)",[1205,1220,1221],{},"约 80",[1205,1223,1224],{},"约 28 KB",[1187,1226,1227,1230,1233],{},[1205,1228,1229],{},"10 页报告 (约 8,000 字)",[1205,1231,1232],{},"约 900",[1205,1234,1235],{},"约 180 KB",[1187,1237,1238,1241,1244],{},[1205,1239,1240],{},"字典级满字 (JIS Level 1 全)",[1205,1242,1243],{},"约 6,800",[1205,1245,1246],{},"约 2.1 MB",[17,1248,1249],{},"(gpdf v1.0，静态子集化开启。数字会因字形 ID 落在 CFF 和 hmtx 哪一块而上下浮动几 KB)",[17,1251,1252],{},"一份最终 50 KB 的发票 PDF，一半以上是字体数据。但比起不做子集化直接嵌入 5 MB 来说，这点开销几乎可以忽略，查看器会瞬间打开。",[13,1254,1256],{"id":1255},"noto-sans-jp-与-noto-sans-cjk-jp-不要搞混","Noto Sans JP 与 Noto Sans CJK JP — 不要搞混",[17,1258,1259],{},"Noto 家族里有两个都声称能处理日语的子族，名字相似得让人以为可以互换。实际上不是。",[17,1261,1262,1265],{},[29,1263,1264],{},"Noto Sans JP"," 是你要用的。TTF 格式，单一语言，每个字重一个文件。就是 Google Fonts 上下载的那个。",[17,1267,1268,1271,1272,1275,1276,1279],{},[29,1269,1270],{},"Noto Sans CJK JP"," 是覆盖整个 CJK 的超级族。以 OpenType Collection (",[43,1273,1274],{},".ttc",") 形式发布，把日语、简体中文、繁体中文、韩语的字形经过汉字统合 (Han unification) 后塞在一个文件里。早期 Noto 发行版和 ",[43,1277,1278],{},"notofonts.github.io/noto-cjk"," 上的都是这个。",[17,1281,1282,1283,1285,1286,1288],{},"gpdf 直接支持 TTF。TTC 是容器格式 — 你需要在传给 ",[43,1284,350],{}," 之前挑选正确的 face index，而且每个 face 里的 ",[43,1287,777],{}," 是针对特定 CJK 地区调过的，等于你在默默地替汉字统合做选择。直接选 JP 专用的 TTF 会让这些选择显式化。",[17,1290,1291,1292,1295,1296,1299,1300,1303],{},"新项目用 Noto Sans JP。如果遗留项目里已经有 ",[43,1293,1294],{},"NotoSansCJK-Regular.ttc","，用 ",[43,1297,1298],{},"pyftsubset"," 或 ",[43,1301,1302],{},"fonttools"," 抽出 JP face，把结果 TTF 作为项目里的标准产物 check in。",[13,1305,1306],{"id":1306},"把字体编译进二进制",[17,1308,1309],{},"PDF 生成服务大多跑在容器里，发字体最干净的方式是编译进去:",[55,1311,1313],{"className":57,"code":1312,"language":59,"meta":60,"style":60},"package main\n\nimport (\n    _ \"embed\"\n\n    \"github.com/gpdf-dev/gpdf\"\n)\n\n//go:embed NotoSansJP-Regular.ttf\nvar notoJP []byte\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithFont(\"NotoSansJP\", notoJP),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n    // ...\n}\n",[43,1314,1315,1321,1325,1331,1343,1347,1355,1359,1363,1368,1383,1387,1397,1411,1434,1456,1460,1465],{"__ignoreMap":60},[64,1316,1317,1319],{"class":66,"line":67},[64,1318,71],{"class":70},[64,1320,75],{"class":74},[64,1322,1323],{"class":66,"line":78},[64,1324,82],{"emptyLinePlaceholder":81},[64,1326,1327,1329],{"class":66,"line":85},[64,1328,89],{"class":88},[64,1330,92],{"class":70},[64,1332,1333,1336,1338,1341],{"class":66,"line":95},[64,1334,1335],{"class":182},"    _ ",[64,1337,207],{"class":70},[64,1339,1340],{"class":74},"embed",[64,1342,104],{"class":70},[64,1344,1345],{"class":66,"line":107},[64,1346,82],{"emptyLinePlaceholder":81},[64,1348,1349,1351,1353],{"class":66,"line":117},[64,1350,98],{"class":70},[64,1352,127],{"class":74},[64,1354,104],{"class":70},[64,1356,1357],{"class":66,"line":122},[64,1358,155],{"class":70},[64,1360,1361],{"class":66,"line":132},[64,1362,82],{"emptyLinePlaceholder":81},[64,1364,1365],{"class":66,"line":142},[64,1366,1367],{"class":1122},"//go:embed NotoSansJP-Regular.ttf\n",[64,1369,1370,1373,1376,1379],{"class":66,"line":152},[64,1371,1372],{"class":70},"var",[64,1374,1375],{"class":182}," notoJP ",[64,1377,1378],{"class":70},"[]",[64,1380,1382],{"class":1381},"spNyl","byte\n",[64,1384,1385],{"class":66,"line":158},[64,1386,82],{"emptyLinePlaceholder":81},[64,1388,1389,1391,1393,1395],{"class":66,"line":163},[64,1390,166],{"class":70},[64,1392,170],{"class":169},[64,1394,173],{"class":70},[64,1396,176],{"class":70},[64,1398,1399,1401,1403,1405,1407,1409],{"class":66,"line":179},[64,1400,266],{"class":182},[64,1402,192],{"class":70},[64,1404,271],{"class":182},[64,1406,198],{"class":70},[64,1408,276],{"class":169},[64,1410,279],{"class":70},[64,1412,1413,1415,1417,1419,1421,1423,1425,1427,1429,1432],{"class":66,"line":218},[64,1414,285],{"class":182},[64,1416,198],{"class":70},[64,1418,350],{"class":169},[64,1420,204],{"class":70},[64,1422,207],{"class":70},[64,1424,357],{"class":210},[64,1426,207],{"class":70},[64,1428,186],{"class":70},[64,1430,1431],{"class":182}," notoJP",[64,1433,302],{"class":70},[64,1435,1436,1438,1440,1442,1444,1446,1448,1450,1452,1454],{"class":66,"line":234},[64,1437,285],{"class":182},[64,1439,198],{"class":70},[64,1441,376],{"class":169},[64,1443,204],{"class":70},[64,1445,207],{"class":70},[64,1447,357],{"class":210},[64,1449,207],{"class":70},[64,1451,186],{"class":70},[64,1453,389],{"class":336},[64,1455,302],{"class":70},[64,1457,1458],{"class":66,"line":252},[64,1459,397],{"class":70},[64,1461,1462],{"class":66,"line":258},[64,1463,1464],{"class":1122},"    // ...\n",[64,1466,1467],{"class":66,"line":263},[64,1468,703],{"class":70},[17,1470,1471,1472,1475],{},"二进制从约 8 MB 涨到约 13 MB。换来的是：Docker 镜像只有一个产物而不是两个，",[43,1473,1474],{},"COPY --from=builder /app /app"," 就够用，也不会有人因为忘记复制字体文件而上线一个坏掉的容器。每天生成几千份 PDF 的批处理任务，这是合理的默认方案。",[13,1477,1478],{"id":1478},"相关阅读",[736,1480,1481,1488,1499,1506],{},[739,1482,1483,1487],{},[21,1484,1486],{"href":1485},"/zh/blog/embed-japanese-font","如何在 gpdf 中嵌入日语字体？"," — 适用于任何 CJK TTF 的通用方法",[739,1489,1490,1494,1495,1498],{},[21,1491,1493],{"href":1492},"/zh/blog/gofpdf-migration","gofpdf 已归档。gpdf 迁移指南"," — 从 ",[43,1496,1497],{},"AddUTF8Font"," 过来的迁移表",[739,1500,1501,1505],{},[21,1502,1504],{"href":1503},"/zh/blog/go-pdf-library-showdown-2026","Go PDF 库横评 2026"," — 主要库在 CJK 上的对比",[739,1507,1508,745,1513,1515],{},[21,1509,1512],{"href":1510,"rel":1511},"https://gpdf.dev/docs/guide/fonts",[25],"字体指南",[43,1514,350],{}," 完整参考",[13,1517,1519],{"id":1518},"试用-gpdf","试用 gpdf",[17,1521,1522],{},"gpdf 是 Go 的 PDF 生成库。MIT 许可，零外部依赖，原生 CJK 支持。",[55,1524,1528],{"className":1525,"code":1526,"language":1527,"meta":60,"style":60},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[43,1529,1530],{"__ignoreMap":60},[64,1531,1532,1534,1537],{"class":66,"line":67},[64,1533,59],{"class":74},[64,1535,1536],{"class":210}," get",[64,1538,1539],{"class":210}," github.com/gpdf-dev/gpdf\n",[17,1541,1542,1546,1547],{},[21,1543,1545],{"href":23,"rel":1544},[25],"⭐ 在 GitHub 上加星"," · ",[21,1548,1551],{"href":1549,"rel":1550},"https://gpdf.dev/docs/quickstart",[25],"阅读文档",[1553,1554,1555],"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);}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":60,"searchDepth":78,"depth":78,"links":1557},[1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568],{"id":15,"depth":78,"text":15},{"id":35,"depth":78,"text":36},{"id":53,"depth":78,"text":53},{"id":726,"depth":78,"text":727},{"id":799,"depth":78,"text":800},{"id":909,"depth":78,"text":910},{"id":1173,"depth":78,"text":1173},{"id":1255,"depth":78,"text":1256},{"id":1306,"depth":78,"text":1306},{"id":1478,"depth":78,"text":1478},{"id":1518,"depth":78,"text":1519},"2026-04-15","用 gpdf.WithFont 注册 static 版 NotoSansJP-Regular.ttf，不要用 variable font。gpdf 会把 17,000 个字形子集化到每份 PDF 不到 40 KB。",false,"md",{"name":1574,"totalTime":1575,"tools":1576,"steps":1579},"在 gpdf 文档中把 Noto Sans JP 设为默认字体","PT10M",[1577,1578],"Go 1.22+","NotoSansJP-Regular.ttf (Google Fonts 的 static TTF)",[1580,1583,1586,1589],{"name":1581,"text":1582},"从 Google Fonts 下载 static TTF","在 fonts.google.com 下载 Noto Sans JP 的 zip 包，解压后选择 static/NotoSansJP-Regular.ttf。不要用根目录下的 NotoSansJP-VariableFont_wght.ttf。",{"name":1584,"text":1585},"在启动时读取字节","用 os.ReadFile 读取 NotoSansJP-Regular.ttf。如果想做成自包含的二进制，可用 //go:embed 编译进去。",{"name":1587,"text":1588},"在构造文档时注册字体","把 gpdf.WithFont(\"NotoSansJP\", fontBytes) 和 gpdf.WithDefaultFont(\"NotoSansJP\", 11) 传给 gpdf.NewDocument。不需要 AddUTF8Font，也不需要文件路径。",{"name":1590,"text":1591},"写入日语文本并生成 PDF","在列里调用 c.Text(\"請求書\")。doc.Generate() 返回 []byte，gpdf 会把你实际用到的字形子集化后嵌入 PDF。",null,{},"/zh/blog/noto-sans-jp-with-gpdf",{"title":5,"description":1570},"zh/blog/004.noto-sans-jp-with-gpdf",[1598,1599,1600],"recipe","cjk","tutorial","4FD_pEfyAQq5_MH8ZwQlbflMrT7uQE-NnOc7ENXZago",1776529262414]