[{"data":1,"prerenderedAt":1278},["ShallowReactive",2],{"blog-zh-tofu-boxes-japanese":3},{"id":4,"title":5,"author":6,"body":9,"date":1242,"description":1243,"draft":1244,"extension":1245,"howTo":1246,"image":1268,"meta":1269,"navigation":124,"path":1270,"seo":1271,"stem":1272,"tags":1273,"updated":1268,"__hash__":1277},"blogZh/zh/blog/008.tofu-boxes-japanese.md","为什么 gpdf 生成的 PDF 中日文显示为方块（豆腐字）？",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1230},"minimark",[12,16,20,23,26,29,90,94,97,700,714,728,731,734,750,770,776,780,787,932,965,969,976,979,982,1012,1015,1019,1025,1090,1101,1104,1137,1140,1156,1159,1189,1193,1196,1213,1226],[13,14,15],"h2",{"id":15},"这个问题的另一种表达",[17,18,19],"p",{},"我用 gpdf 写了日文，输出的 PDF 里那些字都变成了空方块。这是什么，怎么让真的日文字形出现在文件里？",[13,21,22],{"id":22},"速答",[17,24,25],{},"这就是豆腐字（tofu）——PDF 查看器在嵌入的字体里找不到对应 Unicode 码位的字形时，会画一个占位矩形。原因有 4 种，其中一种远比其余的常见。",[17,27,28],{},"按频率排序：",[30,31,32,49,66,80],"ol",{},[33,34,35,39,40,44,45,48],"li",{},[36,37,38],"strong",{},"没有注册 CJK 字体。"," ",[41,42,43],"code",{},"gpdf.NewDocument"," 里没有 ",[41,46,47],{},"WithFont"," 调用，所以文档回落到 PDF Base-14 字体（Helvetica、Times、Courier）。它们都不覆盖 U+3040–U+9FFF。",[33,50,51,39,58,61,62,65],{},[36,52,53,54,57],{},"注册了 CJK 字体，但 ",[41,55,56],{},"c.Text"," 的族名不对。",[41,59,60],{},"WithFont(\"NotoSansJP\", ...)"," 设好了，但文本上写着 ",[41,63,64],{},"template.FontFamily(\"Arial\")","，于是 gpdf 在一个 Latin 字体里查日文字形。",[33,67,68,71,72,75,76,79],{},[36,69,70],{},"字体文件本身没有 CJK 字形。"," 磁盘上的 TTF 是 Latin 子集（",[41,73,74],{},"NotoSans-Regular.ttf","，不是 ",[41,77,78],{},"NotoSansJP-Regular.ttf","）。文件名看起来对，覆盖却是空的。",[33,81,82,85,86,89],{},[36,83,84],{},"字节在到达 gpdf 之前就坏了。"," 字符串在上游被当成 Shift-JIS 或 Latin-1 解码过，你想渲染的已经不再是日文码位。如果看到的是 ",[41,87,88],{},"縺ゅ→縺"," 而不是方块，属于这一种。",[13,91,93],{"id":92},"原因-1-的标准修复","原因 #1 的标准修复",[17,95,96],{},"十有八九是这个：",[98,99,104],"pre",{"className":100,"code":101,"language":102,"meta":103,"style":103},"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\", 12),\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        })\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","go","",[41,105,106,119,126,136,148,158,163,173,183,193,199,204,220,258,274,292,298,303,322,346,384,409,434,440,445,464,498,535,557,563,569,574,595,608,623,628,674,689,694],{"__ignoreMap":103},[107,108,111,115],"span",{"class":109,"line":110},"line",1,[107,112,114],{"class":113},"sMK4o","package",[107,116,118],{"class":117},"sBMFI"," main\n",[107,120,122],{"class":109,"line":121},2,[107,123,125],{"emptyLinePlaceholder":124},true,"\n",[107,127,129,133],{"class":109,"line":128},3,[107,130,132],{"class":131},"s7zQu","import",[107,134,135],{"class":113}," (\n",[107,137,139,142,145],{"class":109,"line":138},4,[107,140,141],{"class":113},"    \"",[107,143,144],{"class":117},"log",[107,146,147],{"class":113},"\"\n",[107,149,151,153,156],{"class":109,"line":150},5,[107,152,141],{"class":113},[107,154,155],{"class":117},"os",[107,157,147],{"class":113},[107,159,161],{"class":109,"line":160},6,[107,162,125],{"emptyLinePlaceholder":124},[107,164,166,168,171],{"class":109,"line":165},7,[107,167,141],{"class":113},[107,169,170],{"class":117},"github.com/gpdf-dev/gpdf",[107,172,147],{"class":113},[107,174,176,178,181],{"class":109,"line":175},8,[107,177,141],{"class":113},[107,179,180],{"class":117},"github.com/gpdf-dev/gpdf/document",[107,182,147],{"class":113},[107,184,186,188,191],{"class":109,"line":185},9,[107,187,141],{"class":113},[107,189,190],{"class":117},"github.com/gpdf-dev/gpdf/template",[107,192,147],{"class":113},[107,194,196],{"class":109,"line":195},10,[107,197,198],{"class":113},")\n",[107,200,202],{"class":109,"line":201},11,[107,203,125],{"emptyLinePlaceholder":124},[107,205,207,210,214,217],{"class":109,"line":206},12,[107,208,209],{"class":113},"func",[107,211,213],{"class":212},"s2Zo4"," main",[107,215,216],{"class":113},"()",[107,218,219],{"class":113}," {\n",[107,221,223,227,230,233,236,239,242,245,248,251,254,256],{"class":109,"line":222},13,[107,224,226],{"class":225},"sTEyZ","    font",[107,228,229],{"class":113},",",[107,231,232],{"class":225}," err ",[107,234,235],{"class":113},":=",[107,237,238],{"class":225}," os",[107,240,241],{"class":113},".",[107,243,244],{"class":212},"ReadFile",[107,246,247],{"class":113},"(",[107,249,250],{"class":113},"\"",[107,252,78],{"class":253},"sfazB",[107,255,250],{"class":113},[107,257,198],{"class":113},[107,259,261,264,266,269,272],{"class":109,"line":260},14,[107,262,263],{"class":131},"    if",[107,265,232],{"class":225},[107,267,268],{"class":113},"!=",[107,270,271],{"class":113}," nil",[107,273,219],{"class":113},[107,275,277,280,282,285,287,290],{"class":109,"line":276},15,[107,278,279],{"class":225},"        log",[107,281,241],{"class":113},[107,283,284],{"class":212},"Fatal",[107,286,247],{"class":113},[107,288,289],{"class":225},"err",[107,291,198],{"class":113},[107,293,295],{"class":109,"line":294},16,[107,296,297],{"class":113},"    }\n",[107,299,301],{"class":109,"line":300},17,[107,302,125],{"emptyLinePlaceholder":124},[107,304,306,309,311,314,316,319],{"class":109,"line":305},18,[107,307,308],{"class":225},"    doc ",[107,310,235],{"class":113},[107,312,313],{"class":225}," gpdf",[107,315,241],{"class":113},[107,317,318],{"class":212},"NewDocument",[107,320,321],{"class":113},"(\n",[107,323,325,328,330,333,335,338,340,343],{"class":109,"line":324},19,[107,326,327],{"class":225},"        gpdf",[107,329,241],{"class":113},[107,331,332],{"class":212},"WithPageSize",[107,334,247],{"class":113},[107,336,337],{"class":225},"gpdf",[107,339,241],{"class":113},[107,341,342],{"class":225},"A4",[107,344,345],{"class":113},"),\n",[107,347,349,351,353,356,358,361,363,366,368,370,372,375,377,381],{"class":109,"line":348},20,[107,350,327],{"class":225},[107,352,241],{"class":113},[107,354,355],{"class":212},"WithMargins",[107,357,247],{"class":113},[107,359,360],{"class":225},"document",[107,362,241],{"class":113},[107,364,365],{"class":212},"UniformEdges",[107,367,247],{"class":113},[107,369,360],{"class":225},[107,371,241],{"class":113},[107,373,374],{"class":212},"Mm",[107,376,247],{"class":113},[107,378,380],{"class":379},"sbssI","20",[107,382,383],{"class":113},"))),\n",[107,385,387,389,391,393,395,397,400,402,404,407],{"class":109,"line":386},21,[107,388,327],{"class":225},[107,390,241],{"class":113},[107,392,47],{"class":212},[107,394,247],{"class":113},[107,396,250],{"class":113},[107,398,399],{"class":253},"NotoSansJP",[107,401,250],{"class":113},[107,403,229],{"class":113},[107,405,406],{"class":225}," font",[107,408,345],{"class":113},[107,410,412,414,416,419,421,423,425,427,429,432],{"class":109,"line":411},22,[107,413,327],{"class":225},[107,415,241],{"class":113},[107,417,418],{"class":212},"WithDefaultFont",[107,420,247],{"class":113},[107,422,250],{"class":113},[107,424,399],{"class":253},[107,426,250],{"class":113},[107,428,229],{"class":113},[107,430,431],{"class":379}," 12",[107,433,345],{"class":113},[107,435,437],{"class":109,"line":436},23,[107,438,439],{"class":113},"    )\n",[107,441,443],{"class":109,"line":442},24,[107,444,125],{"emptyLinePlaceholder":124},[107,446,448,451,453,456,458,461],{"class":109,"line":447},25,[107,449,450],{"class":225},"    page ",[107,452,235],{"class":113},[107,454,455],{"class":225}," doc",[107,457,241],{"class":113},[107,459,460],{"class":212},"AddPage",[107,462,463],{"class":113},"()\n",[107,465,467,470,472,475,478,482,485,488,490,493,496],{"class":109,"line":466},26,[107,468,469],{"class":225},"    page",[107,471,241],{"class":113},[107,473,474],{"class":212},"AutoRow",[107,476,477],{"class":113},"(func(",[107,479,481],{"class":480},"sHdIc","r",[107,483,484],{"class":113}," *",[107,486,487],{"class":117},"template",[107,489,241],{"class":113},[107,491,492],{"class":117},"RowBuilder",[107,494,495],{"class":113},")",[107,497,219],{"class":113},[107,499,501,504,506,509,511,514,516,519,522,524,526,528,531,533],{"class":109,"line":500},27,[107,502,503],{"class":225},"        r",[107,505,241],{"class":113},[107,507,508],{"class":212},"Col",[107,510,247],{"class":113},[107,512,513],{"class":379},"12",[107,515,229],{"class":113},[107,517,518],{"class":113}," func(",[107,520,521],{"class":480},"c",[107,523,484],{"class":113},[107,525,487],{"class":117},[107,527,241],{"class":113},[107,529,530],{"class":117},"ColBuilder",[107,532,495],{"class":113},[107,534,219],{"class":113},[107,536,538,541,543,546,548,550,553,555],{"class":109,"line":537},28,[107,539,540],{"class":225},"            c",[107,542,241],{"class":113},[107,544,545],{"class":212},"Text",[107,547,247],{"class":113},[107,549,250],{"class":113},[107,551,552],{"class":253},"こんにちは、世界。",[107,554,250],{"class":113},[107,556,198],{"class":113},[107,558,560],{"class":109,"line":559},29,[107,561,562],{"class":113},"        })\n",[107,564,566],{"class":109,"line":565},30,[107,567,568],{"class":113},"    })\n",[107,570,572],{"class":109,"line":571},31,[107,573,125],{"emptyLinePlaceholder":124},[107,575,577,580,582,584,586,588,590,593],{"class":109,"line":576},32,[107,578,579],{"class":225},"    data",[107,581,229],{"class":113},[107,583,232],{"class":225},[107,585,235],{"class":113},[107,587,455],{"class":225},[107,589,241],{"class":113},[107,591,592],{"class":212},"Generate",[107,594,463],{"class":113},[107,596,598,600,602,604,606],{"class":109,"line":597},33,[107,599,263],{"class":131},[107,601,232],{"class":225},[107,603,268],{"class":113},[107,605,271],{"class":113},[107,607,219],{"class":113},[107,609,611,613,615,617,619,621],{"class":109,"line":610},34,[107,612,279],{"class":225},[107,614,241],{"class":113},[107,616,284],{"class":212},[107,618,247],{"class":113},[107,620,289],{"class":225},[107,622,198],{"class":113},[107,624,626],{"class":109,"line":625},35,[107,627,297],{"class":113},[107,629,631,633,635,637,639,641,644,646,648,651,653,655,658,660,663,666,668,670,672],{"class":109,"line":630},36,[107,632,263],{"class":131},[107,634,232],{"class":225},[107,636,235],{"class":113},[107,638,238],{"class":225},[107,640,241],{"class":113},[107,642,643],{"class":212},"WriteFile",[107,645,247],{"class":113},[107,647,250],{"class":113},[107,649,650],{"class":253},"hello.pdf",[107,652,250],{"class":113},[107,654,229],{"class":113},[107,656,657],{"class":225}," data",[107,659,229],{"class":113},[107,661,662],{"class":379}," 0o644",[107,664,665],{"class":113},");",[107,667,232],{"class":225},[107,669,268],{"class":113},[107,671,271],{"class":113},[107,673,219],{"class":113},[107,675,677,679,681,683,685,687],{"class":109,"line":676},37,[107,678,279],{"class":225},[107,680,241],{"class":113},[107,682,284],{"class":212},[107,684,247],{"class":113},[107,686,289],{"class":225},[107,688,198],{"class":113},[107,690,692],{"class":109,"line":691},38,[107,693,297],{"class":113},[107,695,697],{"class":109,"line":696},39,[107,698,699],{"class":113},"}\n",[17,701,702,703,706,707,710,711,713],{},"两行完成字体注册与默认设置。不依赖 CGO，不需要 ",[41,704,705],{},"AddUTF8Font"," 的那套簿记。如果之前看到 ",[41,708,709],{},"□□□□□、□□。","，现在把上面这段代码和真实的 ",[41,712,78],{}," 放一起跑，会出真的字形。",[17,715,716,717,724,725,727],{},"从 ",[718,719,723],"a",{"href":720,"rel":721},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[722],"nofollow","Google Fonts"," 下载 ",[41,726,78],{},"。",[13,729,730],{"id":730},"怎么判断是哪种原因",[17,732,733],{},"要盯三个地方：文档构造处、写文本的地方、以及 TTF 文件本身。",[17,735,736,742,743,746,747,749],{},[36,737,738,739],{},"输出是整齐的 ",[41,740,741],{},"□□□","（所有矩形一模一样）：原因 1、2 或 3。PDF 里确实嵌入了一个字体，但它没有那些字形。在 Acrobat 中打开 PDF，进入 ",[41,744,745],{},"文件 → 属性 → 字体","，看实际嵌入的是什么。如果只有 Helvetica / Times / Courier，就是原因 1；如果 ",[41,748,399],{}," 已经列出来但还是方块，就是原因 2 或 3。",[17,751,752,761,762,765,766,769],{},[36,753,754,755,757,758],{},"输出像 ",[41,756,88],{}," 或 ",[41,759,760],{},"ã\"ã‚\"ã«ã¡ã¯"," 这种乱码：原因 4。日文字符串在到达 gpdf 之前已经被重新编码过了。常见元凶：Excel 保存的 Shift-JIS CSV 被 ",[41,763,764],{},"os.ReadFile"," 直接当成 UTF-8 读入，或者一个 HTTP 接口没有声明 ",[41,767,768],{},"charset=utf-8","。修的是解码器，不是 PDF。",[17,771,772,775],{},[36,773,774],{},"部分字形正常，部分是方块","：字体覆盖不完整。号称\"日文支持\"的字体可能只有假名和常用汉字，而跳过 鬱、龠 这类罕用字。切换到 Noto Sans JP（覆盖 JIS X 0213）或 Source Han Sans JP 即可。",[13,777,779],{"id":778},"原因-2-细节字体对了族名错了","原因 2 细节：字体对了，族名错了",[17,781,782,783,786],{},"这种最隐蔽，因为字体",[36,784,785],{},"的确","被嵌入了，只是没被用。最小复现：",[98,788,790],{"className":100,"code":789,"language":102,"meta":103,"style":103},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    // 漏了 WithDefaultFont\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"こんにちは\") // 用的是默认字体 Helvetica\n    })\n})\n",[41,791,792,807,830,836,840,844,869,900,923,927],{"__ignoreMap":103},[107,793,794,797,799,801,803,805],{"class":109,"line":110},[107,795,796],{"class":225},"doc ",[107,798,235],{"class":113},[107,800,313],{"class":225},[107,802,241],{"class":113},[107,804,318],{"class":212},[107,806,321],{"class":113},[107,808,809,812,814,816,818,820,822,824,826,828],{"class":109,"line":121},[107,810,811],{"class":225},"    gpdf",[107,813,241],{"class":113},[107,815,47],{"class":212},[107,817,247],{"class":113},[107,819,250],{"class":113},[107,821,399],{"class":253},[107,823,250],{"class":113},[107,825,229],{"class":113},[107,827,406],{"class":225},[107,829,345],{"class":113},[107,831,832],{"class":109,"line":128},[107,833,835],{"class":834},"sHwdD","    // 漏了 WithDefaultFont\n",[107,837,838],{"class":109,"line":138},[107,839,198],{"class":113},[107,841,842],{"class":109,"line":150},[107,843,125],{"emptyLinePlaceholder":124},[107,845,846,849,851,853,855,857,859,861,863,865,867],{"class":109,"line":160},[107,847,848],{"class":225},"page",[107,850,241],{"class":113},[107,852,474],{"class":212},[107,854,477],{"class":113},[107,856,481],{"class":480},[107,858,484],{"class":113},[107,860,487],{"class":117},[107,862,241],{"class":113},[107,864,492],{"class":117},[107,866,495],{"class":113},[107,868,219],{"class":113},[107,870,871,874,876,878,880,882,884,886,888,890,892,894,896,898],{"class":109,"line":165},[107,872,873],{"class":225},"    r",[107,875,241],{"class":113},[107,877,508],{"class":212},[107,879,247],{"class":113},[107,881,513],{"class":379},[107,883,229],{"class":113},[107,885,518],{"class":113},[107,887,521],{"class":480},[107,889,484],{"class":113},[107,891,487],{"class":117},[107,893,241],{"class":113},[107,895,530],{"class":117},[107,897,495],{"class":113},[107,899,219],{"class":113},[107,901,902,905,907,909,911,913,916,918,920],{"class":109,"line":175},[107,903,904],{"class":225},"        c",[107,906,241],{"class":113},[107,908,545],{"class":212},[107,910,247],{"class":113},[107,912,250],{"class":113},[107,914,915],{"class":253},"こんにちは",[107,917,250],{"class":113},[107,919,495],{"class":113},[107,921,922],{"class":834}," // 用的是默认字体 Helvetica\n",[107,924,925],{"class":109,"line":185},[107,926,568],{"class":113},[107,928,929],{"class":109,"line":195},[107,930,931],{"class":113},"})\n",[17,933,934,935,937,938,941,942,944,945,727,948,950,951,953,954,957,958,960,961,964],{},"修复：要么在 ",[41,936,318],{}," 里加 ",[41,939,940],{},"gpdf.WithDefaultFont(\"NotoSansJP\", 12)","，要么在每个要渲染日文的 ",[41,943,56],{}," 上都传 ",[41,946,947],{},"template.FontFamily(\"NotoSansJP\")",[41,949,47],{}," 里的族名和 ",[41,952,56],{}," 里的族名必须",[36,955,956],{},"完全一致","，包括大小写。",[41,959,399],{}," 和 ",[41,962,963],{},"notosansjp"," 在 gpdf 看来是两个不同的字体。",[13,966,968],{"id":967},"原因-3-细节拿错了-ttf-文件","原因 3 细节：拿错了 TTF 文件",[17,970,971,960,973,975],{},[41,972,74],{},[41,974,78],{}," 是两个不同的文件。前者是 Latin 字体，CJK 覆盖为零；后者是日文版，约 17,000 个字形。目录列表里看起来几乎一样，自动补全很容易补成错的那个。",[17,977,978],{},"gpdf 在注册字体时不做字形覆盖校验——你给它字节，它就相信你。失败只会在渲染时以豆腐字的形式暴露。",[17,980,981],{},"最快的检查方法：",[983,984,985,992,999],"ul",{},[33,986,987,988,991],{},"macOS：",[41,989,990],{},"字体册"," 里双击文件，能看到字形网格",[33,993,994,995,998],{},"Linux：",[41,996,997],{},"otfinfo -u NotoSans-Regular.ttf"," 打印 Unicode 覆盖",[33,1000,1001,1002,1007,1008,1011],{},"跨平台：",[718,1003,1006],{"href":1004,"rel":1005},"https://github.com/fonttools/fonttools",[722],"fontTools"," 的 ",[41,1009,1010],{},"ttx -t cmap NotoSans-Regular.ttf"," 把 cmap 表导出为 XML",[17,1013,1014],{},"如果列表里没有 U+3042（あ），手上拿的就是 Latin 子集。",[13,1016,1018],{"id":1017},"原因-4-细节编码损坏","原因 4 细节：编码损坏",[17,1020,1021,1022,1024],{},"这其实和 gpdf 无关。传给 ",[41,1023,56],{}," 的字符串在更早的时候就已经坏了。渲染前先打印：",[98,1026,1028],{"className":100,"code":1027,"language":102,"meta":103,"style":103},"text := loadLabelFromSomewhere()\nfmt.Printf(\"%q\\n\", text) // 打印实际 rune\nc.Text(text)\n",[41,1029,1030,1042,1075],{"__ignoreMap":103},[107,1031,1032,1035,1037,1040],{"class":109,"line":110},[107,1033,1034],{"class":225},"text ",[107,1036,235],{"class":113},[107,1038,1039],{"class":212}," loadLabelFromSomewhere",[107,1041,463],{"class":113},[107,1043,1044,1047,1049,1052,1054,1056,1060,1063,1065,1067,1070,1072],{"class":109,"line":121},[107,1045,1046],{"class":225},"fmt",[107,1048,241],{"class":113},[107,1050,1051],{"class":212},"Printf",[107,1053,247],{"class":113},[107,1055,250],{"class":113},[107,1057,1059],{"class":1058},"swJcz","%q",[107,1061,1062],{"class":225},"\\n",[107,1064,250],{"class":113},[107,1066,229],{"class":113},[107,1068,1069],{"class":225}," text",[107,1071,495],{"class":113},[107,1073,1074],{"class":834}," // 打印实际 rune\n",[107,1076,1077,1079,1081,1083,1085,1088],{"class":109,"line":128},[107,1078,521],{"class":225},[107,1080,241],{"class":113},[107,1082,545],{"class":212},[107,1084,247],{"class":113},[107,1086,1087],{"class":225},"text",[107,1089,198],{"class":113},[17,1091,1092,1093,1096,1097,1100],{},"如果这里输出 ",[41,1094,1095],{},"\"縺ゅ→縺\""," 而不是 ",[41,1098,1099],{},"\"あいうえ\"","，损坏发生在上游。gpdf 修不了——去找 UTF-8 被错误解码的那一步。",[17,1102,1103],{},"常见上游元凶：",[983,1105,1106,1116,1130],{},[33,1107,1108,1109,1111,1112,1115],{},"用 ",[41,1110,764],{}," 读 Excel 导出的 Shift-JIS CSV，然后直接 ",[41,1113,1114],{},"string(data)"," 转换",[33,1117,1118,1119,757,1122,1125,1126,1129],{},"数据库字段定义为 ",[41,1120,1121],{},"latin1",[41,1123,1124],{},"utf8mb3","（而不是 ",[41,1127,1128],{},"utf8mb4","），存的本来就是乱码",[33,1131,1132,1133,1136],{},"HTTP 响应没有 ",[41,1134,1135],{},"Content-Type: application/json; charset=utf-8","，客户端猜成了 Latin-1",[13,1138,1139],{"id":1139},"一个容易忽略的边界情况",[17,1141,1142,1143,1146,1147,1149,1150,1153,1154,727],{},"gpdf 会自动做字形子集化，时机是 ",[41,1144,1145],{},"Generate()"," 被调用的那一刻。如果你在文档构造中先渲染 ",[41,1148,915],{},"，后渲染 ",[41,1151,1152],{},"鬱陶しい","，第二次也会被正确加入子集。但如果生成完 PDF 再用 Acrobat 打开、手动输入一个原本没有出现过的汉字，那个字就会变成豆腐——子集已经冻结了。不要后编辑 PDF，回到 Go 里重新 ",[41,1155,1145],{},[13,1157,1158],{"id":1158},"延伸阅读",[983,1160,1161,1171,1182],{},[33,1162,1163,1167,1168,1170],{},[718,1164,1166],{"href":1165},"/zh/blog/embed-japanese-font","如何在 gpdf 中嵌入日文字体？"," —— ",[41,1169,47],{}," 的完整走查，含粗体/斜体变体与多 CJK 文档",[33,1172,1173,1177,1178,1181],{},[718,1174,1176],{"href":1175},"/zh/blog/noto-sans-jp-with-gpdf","如何在 gpdf 中使用 Noto Sans JP？"," —— 该选哪个 Noto 文件，以及用 ",[41,1179,1180],{},"go:embed"," 简化分发",[33,1183,1184,1188],{},[718,1185,1187],{"href":1186},"/zh/blog/japanese-pdf-in-go","Go 日文 PDF 决定版指南（2026）"," —— 字体、竖排、ruby 及日文特有排版的长篇指南",[13,1190,1192],{"id":1191},"试试-gpdf","试试 gpdf",[17,1194,1195],{},"gpdf 是一个 Go PDF 生成库。MIT 协议、零外部依赖、原生支持 CJK。",[98,1197,1201],{"className":1198,"code":1199,"language":1200,"meta":103,"style":103},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[41,1202,1203],{"__ignoreMap":103},[107,1204,1205,1207,1210],{"class":109,"line":110},[107,1206,102],{"class":117},[107,1208,1209],{"class":253}," get",[107,1211,1212],{"class":253}," github.com/gpdf-dev/gpdf\n",[17,1214,1215,1220,1221],{},[718,1216,1219],{"href":1217,"rel":1218},"https://github.com/gpdf-dev/gpdf",[722],"⭐ 在 GitHub 上 Star"," · ",[718,1222,1225],{"href":1223,"rel":1224},"https://gpdf.dev/zh/docs/quickstart",[722],"阅读文档",[1227,1228,1229],"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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":103,"searchDepth":121,"depth":121,"links":1231},[1232,1233,1234,1235,1236,1237,1238,1239,1240,1241],{"id":15,"depth":121,"text":15},{"id":22,"depth":121,"text":22},{"id":92,"depth":121,"text":93},{"id":730,"depth":121,"text":730},{"id":778,"depth":121,"text":779},{"id":967,"depth":121,"text":968},{"id":1017,"depth":121,"text":1018},{"id":1139,"depth":121,"text":1139},{"id":1158,"depth":121,"text":1158},{"id":1191,"depth":121,"text":1192},"2026-04-17","日文字符变成空方块（□），通常是字体未注册或族名不匹配。整理 4 种常见原因和最快的修复路径。",false,"md",{"name":1247,"totalTime":1248,"tools":1249,"steps":1252},"定位并修复 gpdf 文档中的豆腐字","PT15M",[1250,1251],"Go 1.22+","支持 CJK 的 TTF（如 NotoSansJP-Regular.ttf）",[1253,1256,1259,1262,1265],{"name":1254,"text":1255},"先区分是豆腐字还是 mojibake","打开 PDF。日文显示为空方块（□）表示字体查找失败；显示为 縺ゅ→縺 这种乱码则说明 UTF-8 在到达 gpdf 之前就被错误解码了。两种问题解法完全不同。",{"name":1257,"text":1258},"检查是否注册了 CJK 字体","在文档构造处搜索 gpdf.WithFont。如果没有注册任何 CJK TTF，gpdf 会回落到 PDF Base-14 字体，而这些字体不覆盖 CJK 码位。",{"name":1260,"text":1261},"核对每个 c.Text 的字体族名","如果没有 WithDefaultFont，每个渲染日文的 c.Text 都需要显式传 template.FontFamily(\"NotoSansJP\")。族名不一致会静默回落到默认字体。",{"name":1263,"text":1264},"确认 TTF 文件本身含有 CJK 字形","NotoSans-Regular.ttf（Latin 子集）和 NotoSansJP-Regular.ttf 是两个不同的文件。gpdf 在注册时不会校验字形覆盖。",{"name":1266,"text":1267},"在两个查看器中重新验证","在 Adobe Acrobat 和 Chrome 中分别打开生成的 PDF。两者都正常才算通过。只在其中一个正常，说明字形嵌入了但子集注册有偏差。",null,{},"/zh/blog/tofu-boxes-japanese",{"title":5,"description":1243},"zh/blog/008.tofu-boxes-japanese",[1274,1275,1276],"recipe","troubleshooting","cjk","HMNxWGP0gjjUxtKru9BRqi2g3l-6AdCGPuzL6Ggf5e0",1776529262175]