[{"data":1,"prerenderedAt":1286},["ShallowReactive",2],{"blog-en-tofu-boxes-japanese":3},{"id":4,"title":5,"author":6,"body":9,"date":1250,"description":1251,"draft":1252,"extension":1253,"howTo":1254,"image":1276,"meta":1277,"navigation":126,"path":1278,"seo":1279,"stem":1280,"tags":1281,"updated":1276,"__hash__":1285},"blog/blog/008.tofu-boxes-japanese.md","Why does my PDF show tofu boxes for Japanese?",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1238},"minimark",[12,17,21,25,28,31,92,96,99,701,715,728,732,735,755,774,780,784,791,936,966,970,977,980,983,1013,1016,1020,1026,1091,1106,1109,1141,1145,1163,1167,1197,1201,1204,1221,1234],[13,14,16],"h2",{"id":15},"the-question-in-other-words","The question, in other words",[18,19,20],"p",{},"I wrote Japanese text with gpdf and the output PDF shows empty rectangles where the characters should be. What is that, and how do I get real Japanese glyphs into the file?",[13,22,24],{"id":23},"the-quick-answer","The quick answer",[18,26,27],{},"That is tofu — the PDF viewer draws a placeholder rectangle because the font embedded in the PDF has no glyph for the Unicode codepoint you asked for. Four things cause it, and one is far more common than the rest.",[18,29,30],{},"In order of frequency:",[32,33,34,51,68,82],"ol",{},[35,36,37,41,42,46,47,50],"li",{},[38,39,40],"strong",{},"No CJK font registered."," ",[43,44,45],"code",{},"gpdf.NewDocument"," has no ",[43,48,49],{},"WithFont"," call, so the document falls back to the PDF Base-14 fonts (Helvetica, Times, Courier). None of those cover U+3040–U+9FFF.",[35,52,53,41,60,63,64,67],{},[38,54,55,56,59],{},"CJK font registered, but wrong family on ",[43,57,58],{},"c.Text",".",[43,61,62],{},"WithFont(\"NotoSansJP\", ...)"," is set, but ",[43,65,66],{},"template.FontFamily(\"Arial\")"," on the text forces gpdf to look up Japanese in a Latin font.",[35,69,70,73,74,77,78,81],{},[38,71,72],{},"Font file doesn't actually contain CJK glyphs."," The TTF on disk is a Latin subset (",[43,75,76],{},"NotoSans-Regular.ttf"," rather than ",[43,79,80],{},"NotoSansJP-Regular.ttf","). Name looks right, coverage is empty.",[35,83,84,87,88,91],{},[38,85,86],{},"Bytes corrupted before gpdf saw them."," The string was decoded as Shift-JIS or Latin-1 upstream and the runes you are trying to render are no longer Japanese. If you see ",[43,89,90],{},"縺ゅ→縺"," instead of rectangles, this is the one.",[13,93,95],{"id":94},"the-canonical-fix-for-cause-1","The canonical fix for cause #1",[18,97,98],{},"Nine times out of ten, it is this:",[100,101,106],"pre",{"className":102,"code":103,"language":104,"meta":105,"style":105},"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","",[43,107,108,121,128,138,150,160,165,175,185,195,201,206,222,259,275,293,299,304,323,347,385,410,435,441,446,465,499,536,558,564,570,575,596,609,624,629,675,690,695],{"__ignoreMap":105},[109,110,113,117],"span",{"class":111,"line":112},"line",1,[109,114,116],{"class":115},"sMK4o","package",[109,118,120],{"class":119},"sBMFI"," main\n",[109,122,124],{"class":111,"line":123},2,[109,125,127],{"emptyLinePlaceholder":126},true,"\n",[109,129,131,135],{"class":111,"line":130},3,[109,132,134],{"class":133},"s7zQu","import",[109,136,137],{"class":115}," (\n",[109,139,141,144,147],{"class":111,"line":140},4,[109,142,143],{"class":115},"    \"",[109,145,146],{"class":119},"log",[109,148,149],{"class":115},"\"\n",[109,151,153,155,158],{"class":111,"line":152},5,[109,154,143],{"class":115},[109,156,157],{"class":119},"os",[109,159,149],{"class":115},[109,161,163],{"class":111,"line":162},6,[109,164,127],{"emptyLinePlaceholder":126},[109,166,168,170,173],{"class":111,"line":167},7,[109,169,143],{"class":115},[109,171,172],{"class":119},"github.com/gpdf-dev/gpdf",[109,174,149],{"class":115},[109,176,178,180,183],{"class":111,"line":177},8,[109,179,143],{"class":115},[109,181,182],{"class":119},"github.com/gpdf-dev/gpdf/document",[109,184,149],{"class":115},[109,186,188,190,193],{"class":111,"line":187},9,[109,189,143],{"class":115},[109,191,192],{"class":119},"github.com/gpdf-dev/gpdf/template",[109,194,149],{"class":115},[109,196,198],{"class":111,"line":197},10,[109,199,200],{"class":115},")\n",[109,202,204],{"class":111,"line":203},11,[109,205,127],{"emptyLinePlaceholder":126},[109,207,209,212,216,219],{"class":111,"line":208},12,[109,210,211],{"class":115},"func",[109,213,215],{"class":214},"s2Zo4"," main",[109,217,218],{"class":115},"()",[109,220,221],{"class":115}," {\n",[109,223,225,229,232,235,238,241,243,246,249,252,255,257],{"class":111,"line":224},13,[109,226,228],{"class":227},"sTEyZ","    font",[109,230,231],{"class":115},",",[109,233,234],{"class":227}," err ",[109,236,237],{"class":115},":=",[109,239,240],{"class":227}," os",[109,242,59],{"class":115},[109,244,245],{"class":214},"ReadFile",[109,247,248],{"class":115},"(",[109,250,251],{"class":115},"\"",[109,253,80],{"class":254},"sfazB",[109,256,251],{"class":115},[109,258,200],{"class":115},[109,260,262,265,267,270,273],{"class":111,"line":261},14,[109,263,264],{"class":133},"    if",[109,266,234],{"class":227},[109,268,269],{"class":115},"!=",[109,271,272],{"class":115}," nil",[109,274,221],{"class":115},[109,276,278,281,283,286,288,291],{"class":111,"line":277},15,[109,279,280],{"class":227},"        log",[109,282,59],{"class":115},[109,284,285],{"class":214},"Fatal",[109,287,248],{"class":115},[109,289,290],{"class":227},"err",[109,292,200],{"class":115},[109,294,296],{"class":111,"line":295},16,[109,297,298],{"class":115},"    }\n",[109,300,302],{"class":111,"line":301},17,[109,303,127],{"emptyLinePlaceholder":126},[109,305,307,310,312,315,317,320],{"class":111,"line":306},18,[109,308,309],{"class":227},"    doc ",[109,311,237],{"class":115},[109,313,314],{"class":227}," gpdf",[109,316,59],{"class":115},[109,318,319],{"class":214},"NewDocument",[109,321,322],{"class":115},"(\n",[109,324,326,329,331,334,336,339,341,344],{"class":111,"line":325},19,[109,327,328],{"class":227},"        gpdf",[109,330,59],{"class":115},[109,332,333],{"class":214},"WithPageSize",[109,335,248],{"class":115},[109,337,338],{"class":227},"gpdf",[109,340,59],{"class":115},[109,342,343],{"class":227},"A4",[109,345,346],{"class":115},"),\n",[109,348,350,352,354,357,359,362,364,367,369,371,373,376,378,382],{"class":111,"line":349},20,[109,351,328],{"class":227},[109,353,59],{"class":115},[109,355,356],{"class":214},"WithMargins",[109,358,248],{"class":115},[109,360,361],{"class":227},"document",[109,363,59],{"class":115},[109,365,366],{"class":214},"UniformEdges",[109,368,248],{"class":115},[109,370,361],{"class":227},[109,372,59],{"class":115},[109,374,375],{"class":214},"Mm",[109,377,248],{"class":115},[109,379,381],{"class":380},"sbssI","20",[109,383,384],{"class":115},"))),\n",[109,386,388,390,392,394,396,398,401,403,405,408],{"class":111,"line":387},21,[109,389,328],{"class":227},[109,391,59],{"class":115},[109,393,49],{"class":214},[109,395,248],{"class":115},[109,397,251],{"class":115},[109,399,400],{"class":254},"NotoSansJP",[109,402,251],{"class":115},[109,404,231],{"class":115},[109,406,407],{"class":227}," font",[109,409,346],{"class":115},[109,411,413,415,417,420,422,424,426,428,430,433],{"class":111,"line":412},22,[109,414,328],{"class":227},[109,416,59],{"class":115},[109,418,419],{"class":214},"WithDefaultFont",[109,421,248],{"class":115},[109,423,251],{"class":115},[109,425,400],{"class":254},[109,427,251],{"class":115},[109,429,231],{"class":115},[109,431,432],{"class":380}," 12",[109,434,346],{"class":115},[109,436,438],{"class":111,"line":437},23,[109,439,440],{"class":115},"    )\n",[109,442,444],{"class":111,"line":443},24,[109,445,127],{"emptyLinePlaceholder":126},[109,447,449,452,454,457,459,462],{"class":111,"line":448},25,[109,450,451],{"class":227},"    page ",[109,453,237],{"class":115},[109,455,456],{"class":227}," doc",[109,458,59],{"class":115},[109,460,461],{"class":214},"AddPage",[109,463,464],{"class":115},"()\n",[109,466,468,471,473,476,479,483,486,489,491,494,497],{"class":111,"line":467},26,[109,469,470],{"class":227},"    page",[109,472,59],{"class":115},[109,474,475],{"class":214},"AutoRow",[109,477,478],{"class":115},"(func(",[109,480,482],{"class":481},"sHdIc","r",[109,484,485],{"class":115}," *",[109,487,488],{"class":119},"template",[109,490,59],{"class":115},[109,492,493],{"class":119},"RowBuilder",[109,495,496],{"class":115},")",[109,498,221],{"class":115},[109,500,502,505,507,510,512,515,517,520,523,525,527,529,532,534],{"class":111,"line":501},27,[109,503,504],{"class":227},"        r",[109,506,59],{"class":115},[109,508,509],{"class":214},"Col",[109,511,248],{"class":115},[109,513,514],{"class":380},"12",[109,516,231],{"class":115},[109,518,519],{"class":115}," func(",[109,521,522],{"class":481},"c",[109,524,485],{"class":115},[109,526,488],{"class":119},[109,528,59],{"class":115},[109,530,531],{"class":119},"ColBuilder",[109,533,496],{"class":115},[109,535,221],{"class":115},[109,537,539,542,544,547,549,551,554,556],{"class":111,"line":538},28,[109,540,541],{"class":227},"            c",[109,543,59],{"class":115},[109,545,546],{"class":214},"Text",[109,548,248],{"class":115},[109,550,251],{"class":115},[109,552,553],{"class":254},"こんにちは、世界。",[109,555,251],{"class":115},[109,557,200],{"class":115},[109,559,561],{"class":111,"line":560},29,[109,562,563],{"class":115},"        })\n",[109,565,567],{"class":111,"line":566},30,[109,568,569],{"class":115},"    })\n",[109,571,573],{"class":111,"line":572},31,[109,574,127],{"emptyLinePlaceholder":126},[109,576,578,581,583,585,587,589,591,594],{"class":111,"line":577},32,[109,579,580],{"class":227},"    data",[109,582,231],{"class":115},[109,584,234],{"class":227},[109,586,237],{"class":115},[109,588,456],{"class":227},[109,590,59],{"class":115},[109,592,593],{"class":214},"Generate",[109,595,464],{"class":115},[109,597,599,601,603,605,607],{"class":111,"line":598},33,[109,600,264],{"class":133},[109,602,234],{"class":227},[109,604,269],{"class":115},[109,606,272],{"class":115},[109,608,221],{"class":115},[109,610,612,614,616,618,620,622],{"class":111,"line":611},34,[109,613,280],{"class":227},[109,615,59],{"class":115},[109,617,285],{"class":214},[109,619,248],{"class":115},[109,621,290],{"class":227},[109,623,200],{"class":115},[109,625,627],{"class":111,"line":626},35,[109,628,298],{"class":115},[109,630,632,634,636,638,640,642,645,647,649,652,654,656,659,661,664,667,669,671,673],{"class":111,"line":631},36,[109,633,264],{"class":133},[109,635,234],{"class":227},[109,637,237],{"class":115},[109,639,240],{"class":227},[109,641,59],{"class":115},[109,643,644],{"class":214},"WriteFile",[109,646,248],{"class":115},[109,648,251],{"class":115},[109,650,651],{"class":254},"hello.pdf",[109,653,251],{"class":115},[109,655,231],{"class":115},[109,657,658],{"class":227}," data",[109,660,231],{"class":115},[109,662,663],{"class":380}," 0o644",[109,665,666],{"class":115},");",[109,668,234],{"class":227},[109,670,269],{"class":115},[109,672,272],{"class":115},[109,674,221],{"class":115},[109,676,678,680,682,684,686,688],{"class":111,"line":677},37,[109,679,280],{"class":227},[109,681,59],{"class":115},[109,683,285],{"class":214},[109,685,248],{"class":115},[109,687,290],{"class":227},[109,689,200],{"class":115},[109,691,693],{"class":111,"line":692},38,[109,694,298],{"class":115},[109,696,698],{"class":111,"line":697},39,[109,699,700],{"class":115},"}\n",[18,702,703,704,707,708,711,712,714],{},"Two lines register and default the font. No CGO. No ",[43,705,706],{},"AddUTF8Font"," bookkeeping. If you were seeing ",[43,709,710],{},"□□□□□、□□。"," before and run this program with a real ",[43,713,80],{}," next to it, you get real glyphs.",[18,716,717,718,720,721,59],{},"Grab ",[43,719,80],{}," from ",[722,723,727],"a",{"href":724,"rel":725},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[726],"nofollow","Google Fonts",[13,729,731],{"id":730},"how-to-tell-which-cause-you-are-hitting","How to tell which cause you are hitting",[18,733,734],{},"Most of this is staring at three places: where you build the document, where you write the text, and the TTF file itself.",[18,736,737,743,744,747,748,751,752,754],{},[38,738,739,740],{},"If your output is ",[43,741,742],{},"□□□"," (identical rectangles), it is cause 1, 2, or 3. The PDF embedded ",[745,746,722],"em",{}," font but it has no glyphs for those codepoints. Open the PDF in Acrobat, go to ",[43,749,750],{},"File → Properties → Fonts",", and look at which fonts were actually embedded. If the list is only Helvetica / Times / Courier, cause 1. If ",[43,753,400],{}," is listed and rectangles are still there, cause 2 or 3.",[18,756,757,765,766,769,770,773],{},[38,758,739,759,761,762],{},[43,760,90],{}," or ",[43,763,764],{},"ã\"ã‚\"ã«ã¡ã¯"," (garbled Latin-ish characters), it is cause 4. Your Japanese string was re-encoded somewhere before gpdf got it. Most common culprit: a CSV saved as Shift-JIS by Excel and read with ",[43,767,768],{},"os.ReadFile"," as if it were UTF-8, or an HTTP endpoint that didn't declare ",[43,771,772],{},"charset=utf-8",". Fix the decoder, not the PDF.",[18,775,776,779],{},[38,777,778],{},"Mixed output"," — some characters render, some are boxes — means the font has partial coverage. A font labelled \"Japanese\" might include hiragana and katakana but skip uncommon kanji like 鬱 or 龠. Switch to Noto Sans JP (covers JIS X 0213) or Source Han Sans JP if you hit this.",[13,781,783],{"id":782},"cause-2-in-detail-right-font-wrong-family-name","Cause 2 in detail: right font, wrong family name",[18,785,786,787,790],{},"This one is sneaky because the font ",[745,788,789],{},"is"," embedded — it just isn't used. A minimal repro:",[100,792,794],{"className":102,"code":793,"language":104,"meta":105,"style":105},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    // No WithDefaultFont.\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"こんにちは\") // Uses the default font: Helvetica.\n    })\n})\n",[43,795,796,811,834,840,844,848,873,904,927,931],{"__ignoreMap":105},[109,797,798,801,803,805,807,809],{"class":111,"line":112},[109,799,800],{"class":227},"doc ",[109,802,237],{"class":115},[109,804,314],{"class":227},[109,806,59],{"class":115},[109,808,319],{"class":214},[109,810,322],{"class":115},[109,812,813,816,818,820,822,824,826,828,830,832],{"class":111,"line":123},[109,814,815],{"class":227},"    gpdf",[109,817,59],{"class":115},[109,819,49],{"class":214},[109,821,248],{"class":115},[109,823,251],{"class":115},[109,825,400],{"class":254},[109,827,251],{"class":115},[109,829,231],{"class":115},[109,831,407],{"class":227},[109,833,346],{"class":115},[109,835,836],{"class":111,"line":130},[109,837,839],{"class":838},"sHwdD","    // No WithDefaultFont.\n",[109,841,842],{"class":111,"line":140},[109,843,200],{"class":115},[109,845,846],{"class":111,"line":152},[109,847,127],{"emptyLinePlaceholder":126},[109,849,850,853,855,857,859,861,863,865,867,869,871],{"class":111,"line":162},[109,851,852],{"class":227},"page",[109,854,59],{"class":115},[109,856,475],{"class":214},[109,858,478],{"class":115},[109,860,482],{"class":481},[109,862,485],{"class":115},[109,864,488],{"class":119},[109,866,59],{"class":115},[109,868,493],{"class":119},[109,870,496],{"class":115},[109,872,221],{"class":115},[109,874,875,878,880,882,884,886,888,890,892,894,896,898,900,902],{"class":111,"line":167},[109,876,877],{"class":227},"    r",[109,879,59],{"class":115},[109,881,509],{"class":214},[109,883,248],{"class":115},[109,885,514],{"class":380},[109,887,231],{"class":115},[109,889,519],{"class":115},[109,891,522],{"class":481},[109,893,485],{"class":115},[109,895,488],{"class":119},[109,897,59],{"class":115},[109,899,531],{"class":119},[109,901,496],{"class":115},[109,903,221],{"class":115},[109,905,906,909,911,913,915,917,920,922,924],{"class":111,"line":177},[109,907,908],{"class":227},"        c",[109,910,59],{"class":115},[109,912,546],{"class":214},[109,914,248],{"class":115},[109,916,251],{"class":115},[109,918,919],{"class":254},"こんにちは",[109,921,251],{"class":115},[109,923,496],{"class":115},[109,925,926],{"class":838}," // Uses the default font: Helvetica.\n",[109,928,929],{"class":111,"line":187},[109,930,569],{"class":115},[109,932,933],{"class":111,"line":197},[109,934,935],{"class":115},"})\n",[18,937,938,939,942,943,945,946,949,950,952,953,955,956,958,959,961,962,965],{},"Fix: add ",[43,940,941],{},"gpdf.WithDefaultFont(\"NotoSansJP\", 12)"," to ",[43,944,319],{},", or pass ",[43,947,948],{},"template.FontFamily(\"NotoSansJP\")"," on every ",[43,951,58],{}," that needs Japanese. The family name in ",[43,954,49],{}," and the one on ",[43,957,58],{}," must match exactly, including case. ",[43,960,400],{}," and ",[43,963,964],{},"notosansjp"," are two different fonts to gpdf.",[13,967,969],{"id":968},"cause-3-in-detail-the-wrong-ttf-file","Cause 3 in detail: the wrong TTF file",[18,971,972,961,974,976],{},[43,973,76],{},[43,975,80],{}," are two different files. The first is a Latin font with zero CJK coverage. The second is the Japanese cut, around 17,000 glyphs. They look almost identical in a directory listing. Autocomplete will happily hand you the wrong one.",[18,978,979],{},"gpdf does not validate glyph coverage at registration. If you hand it bytes, it trusts you. The failure shows up as tofu at render time.",[18,981,982],{},"Quickest way to check:",[984,985,986,993,1000],"ul",{},[35,987,988,989,992],{},"macOS: ",[43,990,991],{},"Font Book → double-click the file → preview"," shows a glyph grid",[35,994,995,996,999],{},"Linux: ",[43,997,998],{},"otfinfo -u NotoSans-Regular.ttf"," dumps the Unicode coverage",[35,1001,1002,1003,1008,1009,1012],{},"Cross-platform: ",[722,1004,1007],{"href":1005,"rel":1006},"https://github.com/fonttools/fonttools",[726],"fontTools"," — ",[43,1010,1011],{},"ttx -t cmap NotoSans-Regular.ttf"," dumps the cmap table as XML",[18,1014,1015],{},"If U+3042 (あ) is not in the list, you have the Latin subset.",[13,1017,1019],{"id":1018},"cause-4-in-detail-encoding-corruption","Cause 4 in detail: encoding corruption",[18,1021,1022,1023,1025],{},"This one does not actually involve gpdf. The string handed to ",[43,1024,58],{}," already had the wrong bytes. Print it before rendering:",[100,1027,1029],{"className":102,"code":1028,"language":104,"meta":105,"style":105},"text := loadLabelFromSomewhere()\nfmt.Printf(\"%q\\n\", text) // Shows actual runes\nc.Text(text)\n",[43,1030,1031,1043,1076],{"__ignoreMap":105},[109,1032,1033,1036,1038,1041],{"class":111,"line":112},[109,1034,1035],{"class":227},"text ",[109,1037,237],{"class":115},[109,1039,1040],{"class":214}," loadLabelFromSomewhere",[109,1042,464],{"class":115},[109,1044,1045,1048,1050,1053,1055,1057,1061,1064,1066,1068,1071,1073],{"class":111,"line":123},[109,1046,1047],{"class":227},"fmt",[109,1049,59],{"class":115},[109,1051,1052],{"class":214},"Printf",[109,1054,248],{"class":115},[109,1056,251],{"class":115},[109,1058,1060],{"class":1059},"swJcz","%q",[109,1062,1063],{"class":227},"\\n",[109,1065,251],{"class":115},[109,1067,231],{"class":115},[109,1069,1070],{"class":227}," text",[109,1072,496],{"class":115},[109,1074,1075],{"class":838}," // Shows actual runes\n",[109,1077,1078,1080,1082,1084,1086,1089],{"class":111,"line":130},[109,1079,522],{"class":227},[109,1081,59],{"class":115},[109,1083,546],{"class":214},[109,1085,248],{"class":115},[109,1087,1088],{"class":227},"text",[109,1090,200],{"class":115},[18,1092,1093,1094,1097,1098,1101,1102,1105],{},"If ",[43,1095,1096],{},"fmt.Printf(\"%q\\n\", text)"," prints ",[43,1099,1100],{},"\"縺ゅ→縺\""," instead of ",[43,1103,1104],{},"\"あいうえ\"",", the corruption happened upstream. gpdf cannot fix it — find the place where UTF-8 was mis-decoded.",[18,1107,1108],{},"Common upstream culprits:",[984,1110,1111,1120,1134],{},[35,1112,1113,1114,1116,1117],{},"Reading a CSV exported from Excel (Windows Shift-JIS) with ",[43,1115,768],{}," and casting straight to ",[43,1118,1119],{},"string",[35,1121,1122,1123,761,1126,1129,1130,1133],{},"A database column declared ",[43,1124,1125],{},"latin1",[43,1127,1128],{},"utf8mb3"," (not ",[43,1131,1132],{},"utf8mb4",") already storing mojibake",[35,1135,1136,1137,1140],{},"An HTTP response missing ",[43,1138,1139],{},"Content-Type: application/json; charset=utf-8",", and a client that guessed Latin-1",[13,1142,1144],{"id":1143},"one-edge-case-worth-naming","One edge case worth naming",[18,1146,1147,1148,1150,1151,1154,1155,1158,1159,1162],{},"gpdf silently subsets. If you register NotoSansJP at document construction, render ",[43,1149,919],{},", and then later in the same document render ",[43,1152,1153],{},"鬱陶しい",", the second render will work — gpdf adds the new glyphs to the subset on the fly. But if you generate a PDF, hand it to a viewer, and ",[745,1156,1157],{},"then"," try to edit the PDF in Acrobat and type a new kanji that wasn't in the original text, you will get tofu there. The subset is frozen at ",[43,1160,1161],{},"Generate()"," time. Re-run your program; don't patch the PDF.",[13,1164,1166],{"id":1165},"related-recipes","Related recipes",[984,1168,1169,1179,1190],{},[35,1170,1171,1175,1176,1178],{},[722,1172,1174],{"href":1173},"/blog/embed-japanese-font","How do I embed a Japanese font in gpdf?"," — full ",[43,1177,49],{}," walkthrough with bold/italic variants and multi-CJK documents",[35,1180,1181,1185,1186,1189],{},[722,1182,1184],{"href":1183},"/blog/noto-sans-jp-with-gpdf","How do I use Noto Sans JP with gpdf?"," — which Noto file to pick and how ",[43,1187,1188],{},"go:embed"," simplifies distribution",[35,1191,1192,1196],{},[722,1193,1195],{"href":1194},"/blog/japanese-pdf-in-go","The definitive guide to Japanese PDFs in Go (2026)"," — long-form guide covering fonts, vertical text, ruby, and JP-specific layout",[13,1198,1200],{"id":1199},"try-gpdf","Try gpdf",[18,1202,1203],{},"gpdf is a Go library for generating PDFs. MIT licensed, zero external dependencies, native CJK support.",[100,1205,1209],{"className":1206,"code":1207,"language":1208,"meta":105,"style":105},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[43,1210,1211],{"__ignoreMap":105},[109,1212,1213,1215,1218],{"class":111,"line":112},[109,1214,104],{"class":119},[109,1216,1217],{"class":254}," get",[109,1219,1220],{"class":254}," github.com/gpdf-dev/gpdf\n",[18,1222,1223,1228,1229],{},[722,1224,1227],{"href":1225,"rel":1226},"https://github.com/gpdf-dev/gpdf",[726],"⭐ Star on GitHub"," · ",[722,1230,1233],{"href":1231,"rel":1232},"https://gpdf.dev/docs/quickstart",[726],"Read the docs",[1235,1236,1237],"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":105,"searchDepth":123,"depth":123,"links":1239},[1240,1241,1242,1243,1244,1245,1246,1247,1248,1249],{"id":15,"depth":123,"text":16},{"id":23,"depth":123,"text":24},{"id":94,"depth":123,"text":95},{"id":730,"depth":123,"text":731},{"id":782,"depth":123,"text":783},{"id":968,"depth":123,"text":969},{"id":1018,"depth":123,"text":1019},{"id":1143,"depth":123,"text":1144},{"id":1165,"depth":123,"text":1166},{"id":1199,"depth":123,"text":1200},"2026-04-17","Empty rectangles instead of Japanese characters mean your PDF couldn't find glyphs for those codepoints. Here are the four causes and the fixes.",false,"md",{"name":1255,"totalTime":1256,"tools":1257,"steps":1260},"Diagnose and fix tofu boxes in a gpdf document","PT15M",[1258,1259],"Go 1.22+","A CJK-capable TTF such as NotoSansJP-Regular.ttf",[1261,1264,1267,1270,1273],{"name":1262,"text":1263},"Confirm the symptom is tofu, not mojibake","Open the PDF. Japanese characters as empty rectangles (□) mean a font lookup miss. Garbled Latin like 縺ゅ→縺 means UTF-8 was decoded wrong upstream of gpdf.",{"name":1265,"text":1266},"Check that a CJK-capable font is registered","Search your document construction for gpdf.WithFont. With no CJK TTF registered, gpdf falls back to the Base-14 PDF fonts, none of which cover CJK codepoints.",{"name":1268,"text":1269},"Verify the font family on each c.Text call","If WithDefaultFont is not set, every c.Text that renders Japanese needs template.FontFamily(\"NotoSansJP\") explicitly. A mismatched family silently falls back to the default.",{"name":1271,"text":1272},"Confirm the TTF file actually contains CJK glyphs","A file called NotoSans-Regular.ttf (Latin subset) looks right at a glance but has no CJK table. gpdf does not validate coverage at registration time.",{"name":1274,"text":1275},"Regenerate and verify in two viewers","Open the PDF in Adobe Acrobat and in Chrome. Both should render Japanese. If one works and one does not, glyphs are embedded but not subset-registered for that codepoint.",null,{},"/blog/tofu-boxes-japanese",{"title":5,"description":1251},"blog/008.tofu-boxes-japanese",[1282,1283,1284],"recipe","troubleshooting","cjk","FYMNCUXl9ezM4RLFCpGxajERddhmZIZ3aA9snvFjzQU",1776529258767]