[{"data":1,"prerenderedAt":1553},["ShallowReactive",2],{"blog-ja-embed-japanese-font":3},{"id":4,"title":5,"author":6,"body":9,"date":1520,"description":1521,"draft":1522,"extension":1523,"howTo":1524,"image":1543,"meta":1544,"navigation":86,"path":1545,"seo":1546,"stem":1547,"tags":1548,"updated":1543,"__hash__":1552},"blogJa/ja/blog/003.embed-japanese-font.md","gpdf で日本語フォントを埋め込むには?",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1509},"minimark",[12,16,32,36,56,59,708,726,730,733,739,749,763,767,778,932,950,953,960,1393,1400,1404,1410,1418,1434,1437,1469,1473,1476,1493,1505],[13,14,15],"h2",{"id":15},"質問を言い換えると",[17,18,19,26,27,31],"p",{},[20,21,25],"a",{"href":22,"rel":23},"https://github.com/gpdf-dev/gpdf",[24],"nofollow","gpdf"," で日本語(あるいは CJK 全般)の PDF を作りたい。",[28,29,30],"code",{},"AddUTF8Font"," のお作法も、CGO も、一枚ごとに 5 MB のフォントを埋め込むのも避けたい。最短でどう書くか。",[13,33,35],{"id":34},"tldr","TL;DR",[17,37,38,39,42,43,46,47,50,51,55],{},"TTF を ",[28,40,41],{},"os.ReadFile"," で読み込み、",[28,44,45],{},"gpdf.WithFont(\"NotoSansJP\", fontBytes)"," を ",[28,48,49],{},"NewDocument"," に渡し、必要ならデフォルトフォントに指定するだけ。",[52,53,54],"strong",{},"セットアップ 3 行で、gpdf は使った文字のグリフだけを抽出して埋め込む"," — 5 MB のフォント丸ごとは入らない。",[13,57,58],{"id":58},"動くコード",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"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(\"こんにちは、世界。\", template.FontSize(24), template.Bold())\n            c.Text(\"日本語 PDF、これだけ。\")\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","",[28,67,68,81,88,98,110,120,125,135,145,155,161,166,182,221,237,255,261,266,284,307,345,371,396,402,407,426,460,497,545,565,571,577,582,603,616,631,636,682,697,702],{"__ignoreMap":65},[69,70,73,77],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sMK4o","package",[69,78,80],{"class":79},"sBMFI"," main\n",[69,82,84],{"class":71,"line":83},2,[69,85,87],{"emptyLinePlaceholder":86},true,"\n",[69,89,91,95],{"class":71,"line":90},3,[69,92,94],{"class":93},"s7zQu","import",[69,96,97],{"class":75}," (\n",[69,99,101,104,107],{"class":71,"line":100},4,[69,102,103],{"class":75},"    \"",[69,105,106],{"class":79},"log",[69,108,109],{"class":75},"\"\n",[69,111,113,115,118],{"class":71,"line":112},5,[69,114,103],{"class":75},[69,116,117],{"class":79},"os",[69,119,109],{"class":75},[69,121,123],{"class":71,"line":122},6,[69,124,87],{"emptyLinePlaceholder":86},[69,126,128,130,133],{"class":71,"line":127},7,[69,129,103],{"class":75},[69,131,132],{"class":79},"github.com/gpdf-dev/gpdf",[69,134,109],{"class":75},[69,136,138,140,143],{"class":71,"line":137},8,[69,139,103],{"class":75},[69,141,142],{"class":79},"github.com/gpdf-dev/gpdf/document",[69,144,109],{"class":75},[69,146,148,150,153],{"class":71,"line":147},9,[69,149,103],{"class":75},[69,151,152],{"class":79},"github.com/gpdf-dev/gpdf/template",[69,154,109],{"class":75},[69,156,158],{"class":71,"line":157},10,[69,159,160],{"class":75},")\n",[69,162,164],{"class":71,"line":163},11,[69,165,87],{"emptyLinePlaceholder":86},[69,167,169,172,176,179],{"class":71,"line":168},12,[69,170,171],{"class":75},"func",[69,173,175],{"class":174},"s2Zo4"," main",[69,177,178],{"class":75},"()",[69,180,181],{"class":75}," {\n",[69,183,185,189,192,195,198,201,204,207,210,213,217,219],{"class":71,"line":184},13,[69,186,188],{"class":187},"sTEyZ","    font",[69,190,191],{"class":75},",",[69,193,194],{"class":187}," err ",[69,196,197],{"class":75},":=",[69,199,200],{"class":187}," os",[69,202,203],{"class":75},".",[69,205,206],{"class":174},"ReadFile",[69,208,209],{"class":75},"(",[69,211,212],{"class":75},"\"",[69,214,216],{"class":215},"sfazB","NotoSansJP-Regular.ttf",[69,218,212],{"class":75},[69,220,160],{"class":75},[69,222,224,227,229,232,235],{"class":71,"line":223},14,[69,225,226],{"class":93},"    if",[69,228,194],{"class":187},[69,230,231],{"class":75},"!=",[69,233,234],{"class":75}," nil",[69,236,181],{"class":75},[69,238,240,243,245,248,250,253],{"class":71,"line":239},15,[69,241,242],{"class":187},"        log",[69,244,203],{"class":75},[69,246,247],{"class":174},"Fatal",[69,249,209],{"class":75},[69,251,252],{"class":187},"err",[69,254,160],{"class":75},[69,256,258],{"class":71,"line":257},16,[69,259,260],{"class":75},"    }\n",[69,262,264],{"class":71,"line":263},17,[69,265,87],{"emptyLinePlaceholder":86},[69,267,269,272,274,277,279,281],{"class":71,"line":268},18,[69,270,271],{"class":187},"    doc ",[69,273,197],{"class":75},[69,275,276],{"class":187}," gpdf",[69,278,203],{"class":75},[69,280,49],{"class":174},[69,282,283],{"class":75},"(\n",[69,285,287,290,292,295,297,299,301,304],{"class":71,"line":286},19,[69,288,289],{"class":187},"        gpdf",[69,291,203],{"class":75},[69,293,294],{"class":174},"WithPageSize",[69,296,209],{"class":75},[69,298,25],{"class":187},[69,300,203],{"class":75},[69,302,303],{"class":187},"A4",[69,305,306],{"class":75},"),\n",[69,308,310,312,314,317,319,322,324,327,329,331,333,336,338,342],{"class":71,"line":309},20,[69,311,289],{"class":187},[69,313,203],{"class":75},[69,315,316],{"class":174},"WithMargins",[69,318,209],{"class":75},[69,320,321],{"class":187},"document",[69,323,203],{"class":75},[69,325,326],{"class":174},"UniformEdges",[69,328,209],{"class":75},[69,330,321],{"class":187},[69,332,203],{"class":75},[69,334,335],{"class":174},"Mm",[69,337,209],{"class":75},[69,339,341],{"class":340},"sbssI","20",[69,343,344],{"class":75},"))),\n",[69,346,348,350,352,355,357,359,362,364,366,369],{"class":71,"line":347},21,[69,349,289],{"class":187},[69,351,203],{"class":75},[69,353,354],{"class":174},"WithFont",[69,356,209],{"class":75},[69,358,212],{"class":75},[69,360,361],{"class":215},"NotoSansJP",[69,363,212],{"class":75},[69,365,191],{"class":75},[69,367,368],{"class":187}," font",[69,370,306],{"class":75},[69,372,374,376,378,381,383,385,387,389,391,394],{"class":71,"line":373},22,[69,375,289],{"class":187},[69,377,203],{"class":75},[69,379,380],{"class":174},"WithDefaultFont",[69,382,209],{"class":75},[69,384,212],{"class":75},[69,386,361],{"class":215},[69,388,212],{"class":75},[69,390,191],{"class":75},[69,392,393],{"class":340}," 12",[69,395,306],{"class":75},[69,397,399],{"class":71,"line":398},23,[69,400,401],{"class":75},"    )\n",[69,403,405],{"class":71,"line":404},24,[69,406,87],{"emptyLinePlaceholder":86},[69,408,410,413,415,418,420,423],{"class":71,"line":409},25,[69,411,412],{"class":187},"    page ",[69,414,197],{"class":75},[69,416,417],{"class":187}," doc",[69,419,203],{"class":75},[69,421,422],{"class":174},"AddPage",[69,424,425],{"class":75},"()\n",[69,427,429,432,434,437,440,444,447,450,452,455,458],{"class":71,"line":428},26,[69,430,431],{"class":187},"    page",[69,433,203],{"class":75},[69,435,436],{"class":174},"AutoRow",[69,438,439],{"class":75},"(func(",[69,441,443],{"class":442},"sHdIc","r",[69,445,446],{"class":75}," *",[69,448,449],{"class":79},"template",[69,451,203],{"class":75},[69,453,454],{"class":79},"RowBuilder",[69,456,457],{"class":75},")",[69,459,181],{"class":75},[69,461,463,466,468,471,473,476,478,481,484,486,488,490,493,495],{"class":71,"line":462},27,[69,464,465],{"class":187},"        r",[69,467,203],{"class":75},[69,469,470],{"class":174},"Col",[69,472,209],{"class":75},[69,474,475],{"class":340},"12",[69,477,191],{"class":75},[69,479,480],{"class":75}," func(",[69,482,483],{"class":442},"c",[69,485,446],{"class":75},[69,487,449],{"class":79},[69,489,203],{"class":75},[69,491,492],{"class":79},"ColBuilder",[69,494,457],{"class":75},[69,496,181],{"class":75},[69,498,500,503,505,508,510,512,515,517,519,522,524,527,529,532,535,537,539,542],{"class":71,"line":499},28,[69,501,502],{"class":187},"            c",[69,504,203],{"class":75},[69,506,507],{"class":174},"Text",[69,509,209],{"class":75},[69,511,212],{"class":75},[69,513,514],{"class":215},"こんにちは、世界。",[69,516,212],{"class":75},[69,518,191],{"class":75},[69,520,521],{"class":187}," template",[69,523,203],{"class":75},[69,525,526],{"class":174},"FontSize",[69,528,209],{"class":75},[69,530,531],{"class":340},"24",[69,533,534],{"class":75},"),",[69,536,521],{"class":187},[69,538,203],{"class":75},[69,540,541],{"class":174},"Bold",[69,543,544],{"class":75},"())\n",[69,546,548,550,552,554,556,558,561,563],{"class":71,"line":547},29,[69,549,502],{"class":187},[69,551,203],{"class":75},[69,553,507],{"class":174},[69,555,209],{"class":75},[69,557,212],{"class":75},[69,559,560],{"class":215},"日本語 PDF、これだけ。",[69,562,212],{"class":75},[69,564,160],{"class":75},[69,566,568],{"class":71,"line":567},30,[69,569,570],{"class":75},"        })\n",[69,572,574],{"class":71,"line":573},31,[69,575,576],{"class":75},"    })\n",[69,578,580],{"class":71,"line":579},32,[69,581,87],{"emptyLinePlaceholder":86},[69,583,585,588,590,592,594,596,598,601],{"class":71,"line":584},33,[69,586,587],{"class":187},"    data",[69,589,191],{"class":75},[69,591,194],{"class":187},[69,593,197],{"class":75},[69,595,417],{"class":187},[69,597,203],{"class":75},[69,599,600],{"class":174},"Generate",[69,602,425],{"class":75},[69,604,606,608,610,612,614],{"class":71,"line":605},34,[69,607,226],{"class":93},[69,609,194],{"class":187},[69,611,231],{"class":75},[69,613,234],{"class":75},[69,615,181],{"class":75},[69,617,619,621,623,625,627,629],{"class":71,"line":618},35,[69,620,242],{"class":187},[69,622,203],{"class":75},[69,624,247],{"class":174},[69,626,209],{"class":75},[69,628,252],{"class":187},[69,630,160],{"class":75},[69,632,634],{"class":71,"line":633},36,[69,635,260],{"class":75},[69,637,639,641,643,645,647,649,652,654,656,659,661,663,666,668,671,674,676,678,680],{"class":71,"line":638},37,[69,640,226],{"class":93},[69,642,194],{"class":187},[69,644,197],{"class":75},[69,646,200],{"class":187},[69,648,203],{"class":75},[69,650,651],{"class":174},"WriteFile",[69,653,209],{"class":75},[69,655,212],{"class":75},[69,657,658],{"class":215},"hello.pdf",[69,660,212],{"class":75},[69,662,191],{"class":75},[69,664,665],{"class":187}," data",[69,667,191],{"class":75},[69,669,670],{"class":340}," 0o644",[69,672,673],{"class":75},");",[69,675,194],{"class":187},[69,677,231],{"class":75},[69,679,234],{"class":75},[69,681,181],{"class":75},[69,683,685,687,689,691,693,695],{"class":71,"line":684},38,[69,686,242],{"class":187},[69,688,203],{"class":75},[69,690,247],{"class":174},[69,692,209],{"class":75},[69,694,252],{"class":187},[69,696,160],{"class":75},[69,698,700],{"class":71,"line":699},39,[69,701,260],{"class":75},[69,703,705],{"class":71,"line":704},40,[69,706,707],{"class":75},"}\n",[17,709,710,46,712,717,718,721,722,725],{},[28,711,216],{},[20,713,716],{"href":714,"rel":715},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[24],"Google Fonts"," からダウンロードして ",[28,719,720],{},"main.go"," の隣に置き、",[28,723,724],{},"go run main.go"," を叩けば 1 ページの日本語 PDF が出る。",[13,727,729],{"id":728},"この-3-行で何が起きているか","この 3 行で何が起きているか",[17,731,732],{},"背後では 2 つの処理が走っていて、どちらもアプリ側で面倒を見る必要はない。",[17,734,735,738],{},[52,736,737],{},"サブセット埋め込み。"," Noto Sans JP は 17,000 字前後のグリフを含み、Regular だけでもディスク上で約 5 MB。これをそのまま埋め込むと、4 行の日本語が載った領収書でも PDF が 5 MB を超えてしまう。gpdf は描画したテキストを走査して使われたグリフ ID を洗い出し、そのサブセットだけを PDF に書き込む。短い請求書 1 枚ならフォントデータは 20〜40 KB 程度に収まる。",[17,740,741,742,744,745,748],{},"gofpdf もサブセット化自体は可能だったが、",[28,743,30],{}," にファイルパスと UTF-8 フラグを渡して都度読み込ませる API 設計のため、ドキュメント途中でのフォント切り替えが扱いづらかった。gpdf はドキュメント生成時に一度だけ登録し、以降は ",[28,746,747],{},"c.Text"," がファミリ名で参照する。呼び出しごとのお膳立ては不要。",[17,750,751,754,755,758,759,762],{},[52,752,753],{},"CGO 不使用。"," これは地味に大きい。他言語のエコシステムではフォント処理が FreeType や HarfBuzz を経由することが多く、C 依存が入るとビルドキャッシュの挙動が変わり、Docker イメージにレイヤが増え、macOS から ",[28,756,757],{},"linux/arm64"," へのクロスコンパイルで一手間かかるようになる。gpdf は TrueType テーブルを純 Go で読んでいる。",[28,760,761],{},"go build"," は static のまま。distroless コンテナに Go バイナリと TTF だけ入れて出荷できる。",[13,764,766],{"id":765},"bold-italic-を使う","Bold / Italic を使う",[17,768,769,770,773,774,777],{},"Noto Sans JP は Weight ごとに別ファイル。",[52,771,772],{},"太字","を使いたい場合は Bold 用 TTF を ",[28,775,776],{},"-Bold"," サフィックス付きで別登録する:",[60,779,781],{"className":62,"code":780,"language":64,"meta":65,"style":65},"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\", 12),\n)\n",[28,782,783,811,839,843,858,882,906,928],{"__ignoreMap":65},[69,784,785,788,790,793,795,797,799,801,803,805,807,809],{"class":71,"line":72},[69,786,787],{"class":187},"reg",[69,789,191],{"class":75},[69,791,792],{"class":187}," _ ",[69,794,197],{"class":75},[69,796,200],{"class":187},[69,798,203],{"class":75},[69,800,206],{"class":174},[69,802,209],{"class":75},[69,804,212],{"class":75},[69,806,216],{"class":215},[69,808,212],{"class":75},[69,810,160],{"class":75},[69,812,813,816,818,820,822,824,826,828,830,832,835,837],{"class":71,"line":83},[69,814,815],{"class":187},"bold",[69,817,191],{"class":75},[69,819,792],{"class":187},[69,821,197],{"class":75},[69,823,200],{"class":187},[69,825,203],{"class":75},[69,827,206],{"class":174},[69,829,209],{"class":75},[69,831,212],{"class":75},[69,833,834],{"class":215},"NotoSansJP-Bold.ttf",[69,836,212],{"class":75},[69,838,160],{"class":75},[69,840,841],{"class":71,"line":90},[69,842,87],{"emptyLinePlaceholder":86},[69,844,845,848,850,852,854,856],{"class":71,"line":100},[69,846,847],{"class":187},"doc ",[69,849,197],{"class":75},[69,851,276],{"class":187},[69,853,203],{"class":75},[69,855,49],{"class":174},[69,857,283],{"class":75},[69,859,860,863,865,867,869,871,873,875,877,880],{"class":71,"line":112},[69,861,862],{"class":187},"    gpdf",[69,864,203],{"class":75},[69,866,354],{"class":174},[69,868,209],{"class":75},[69,870,212],{"class":75},[69,872,361],{"class":215},[69,874,212],{"class":75},[69,876,191],{"class":75},[69,878,879],{"class":187}," reg",[69,881,306],{"class":75},[69,883,884,886,888,890,892,894,897,899,901,904],{"class":71,"line":122},[69,885,862],{"class":187},[69,887,203],{"class":75},[69,889,354],{"class":174},[69,891,209],{"class":75},[69,893,212],{"class":75},[69,895,896],{"class":215},"NotoSansJP-Bold",[69,898,212],{"class":75},[69,900,191],{"class":75},[69,902,903],{"class":187}," bold",[69,905,306],{"class":75},[69,907,908,910,912,914,916,918,920,922,924,926],{"class":71,"line":127},[69,909,862],{"class":187},[69,911,203],{"class":75},[69,913,380],{"class":174},[69,915,209],{"class":75},[69,917,212],{"class":75},[69,919,361],{"class":215},[69,921,212],{"class":75},[69,923,191],{"class":75},[69,925,393],{"class":340},[69,927,306],{"class":75},[69,929,930],{"class":71,"line":137},[69,931,160],{"class":75},[17,933,934,935,938,939,941,942,945,946,949],{},"これで ",[28,936,937],{},"template.Bold()"," が ",[28,940,776],{}," バリアントを拾う。同じ規約で ",[28,943,944],{},"-Italic"," と ",[28,947,948],{},"-BoldItalic"," も使える。バリアントを登録していない場合は合成ウェイトにフォールバックされる — 画面では読めるが、タイポグラフィ的には正直でない。本番の請求書には実バリアントを登録しておく。",[13,951,952],{"id":952},"日中韓を同じドキュメントに混在させる",[17,954,955,956,959],{},"登録は何ファミリでも OK。文字ごとに ",[28,957,958],{},"template.FontFamily(...)"," で切り替える:",[60,961,963],{"className":62,"code":962,"language":64,"meta":65,"style":65},"jp, _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nsc, _ := os.ReadFile(\"NotoSansSC-Regular.ttf\")\nkr, _ := os.ReadFile(\"NotoSansKR-Regular.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", jp),\n    gpdf.WithFont(\"NotoSansSC\", sc),\n    gpdf.WithFont(\"NotoSansKR\", kr),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 12),\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"日本語\")\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"中文\", template.FontFamily(\"NotoSansSC\"))\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"한국어\", template.FontFamily(\"NotoSansKR\"))\n    })\n})\n",[28,964,965,992,1020,1048,1052,1066,1089,1113,1137,1159,1163,1167,1192,1224,1244,1248,1278,1315,1319,1349,1384,1388],{"__ignoreMap":65},[69,966,967,970,972,974,976,978,980,982,984,986,988,990],{"class":71,"line":72},[69,968,969],{"class":187},"jp",[69,971,191],{"class":75},[69,973,792],{"class":187},[69,975,197],{"class":75},[69,977,200],{"class":187},[69,979,203],{"class":75},[69,981,206],{"class":174},[69,983,209],{"class":75},[69,985,212],{"class":75},[69,987,216],{"class":215},[69,989,212],{"class":75},[69,991,160],{"class":75},[69,993,994,997,999,1001,1003,1005,1007,1009,1011,1013,1016,1018],{"class":71,"line":83},[69,995,996],{"class":187},"sc",[69,998,191],{"class":75},[69,1000,792],{"class":187},[69,1002,197],{"class":75},[69,1004,200],{"class":187},[69,1006,203],{"class":75},[69,1008,206],{"class":174},[69,1010,209],{"class":75},[69,1012,212],{"class":75},[69,1014,1015],{"class":215},"NotoSansSC-Regular.ttf",[69,1017,212],{"class":75},[69,1019,160],{"class":75},[69,1021,1022,1025,1027,1029,1031,1033,1035,1037,1039,1041,1044,1046],{"class":71,"line":90},[69,1023,1024],{"class":187},"kr",[69,1026,191],{"class":75},[69,1028,792],{"class":187},[69,1030,197],{"class":75},[69,1032,200],{"class":187},[69,1034,203],{"class":75},[69,1036,206],{"class":174},[69,1038,209],{"class":75},[69,1040,212],{"class":75},[69,1042,1043],{"class":215},"NotoSansKR-Regular.ttf",[69,1045,212],{"class":75},[69,1047,160],{"class":75},[69,1049,1050],{"class":71,"line":100},[69,1051,87],{"emptyLinePlaceholder":86},[69,1053,1054,1056,1058,1060,1062,1064],{"class":71,"line":112},[69,1055,847],{"class":187},[69,1057,197],{"class":75},[69,1059,276],{"class":187},[69,1061,203],{"class":75},[69,1063,49],{"class":174},[69,1065,283],{"class":75},[69,1067,1068,1070,1072,1074,1076,1078,1080,1082,1084,1087],{"class":71,"line":122},[69,1069,862],{"class":187},[69,1071,203],{"class":75},[69,1073,354],{"class":174},[69,1075,209],{"class":75},[69,1077,212],{"class":75},[69,1079,361],{"class":215},[69,1081,212],{"class":75},[69,1083,191],{"class":75},[69,1085,1086],{"class":187}," jp",[69,1088,306],{"class":75},[69,1090,1091,1093,1095,1097,1099,1101,1104,1106,1108,1111],{"class":71,"line":127},[69,1092,862],{"class":187},[69,1094,203],{"class":75},[69,1096,354],{"class":174},[69,1098,209],{"class":75},[69,1100,212],{"class":75},[69,1102,1103],{"class":215},"NotoSansSC",[69,1105,212],{"class":75},[69,1107,191],{"class":75},[69,1109,1110],{"class":187}," sc",[69,1112,306],{"class":75},[69,1114,1115,1117,1119,1121,1123,1125,1128,1130,1132,1135],{"class":71,"line":137},[69,1116,862],{"class":187},[69,1118,203],{"class":75},[69,1120,354],{"class":174},[69,1122,209],{"class":75},[69,1124,212],{"class":75},[69,1126,1127],{"class":215},"NotoSansKR",[69,1129,212],{"class":75},[69,1131,191],{"class":75},[69,1133,1134],{"class":187}," kr",[69,1136,306],{"class":75},[69,1138,1139,1141,1143,1145,1147,1149,1151,1153,1155,1157],{"class":71,"line":147},[69,1140,862],{"class":187},[69,1142,203],{"class":75},[69,1144,380],{"class":174},[69,1146,209],{"class":75},[69,1148,212],{"class":75},[69,1150,361],{"class":215},[69,1152,212],{"class":75},[69,1154,191],{"class":75},[69,1156,393],{"class":340},[69,1158,306],{"class":75},[69,1160,1161],{"class":71,"line":157},[69,1162,160],{"class":75},[69,1164,1165],{"class":71,"line":163},[69,1166,87],{"emptyLinePlaceholder":86},[69,1168,1169,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190],{"class":71,"line":168},[69,1170,1171],{"class":187},"page",[69,1173,203],{"class":75},[69,1175,436],{"class":174},[69,1177,439],{"class":75},[69,1179,443],{"class":442},[69,1181,446],{"class":75},[69,1183,449],{"class":79},[69,1185,203],{"class":75},[69,1187,454],{"class":79},[69,1189,457],{"class":75},[69,1191,181],{"class":75},[69,1193,1194,1197,1199,1201,1203,1206,1208,1210,1212,1214,1216,1218,1220,1222],{"class":71,"line":184},[69,1195,1196],{"class":187},"    r",[69,1198,203],{"class":75},[69,1200,470],{"class":174},[69,1202,209],{"class":75},[69,1204,1205],{"class":340},"4",[69,1207,191],{"class":75},[69,1209,480],{"class":75},[69,1211,483],{"class":442},[69,1213,446],{"class":75},[69,1215,449],{"class":79},[69,1217,203],{"class":75},[69,1219,492],{"class":79},[69,1221,457],{"class":75},[69,1223,181],{"class":75},[69,1225,1226,1229,1231,1233,1235,1237,1240,1242],{"class":71,"line":223},[69,1227,1228],{"class":187},"        c",[69,1230,203],{"class":75},[69,1232,507],{"class":174},[69,1234,209],{"class":75},[69,1236,212],{"class":75},[69,1238,1239],{"class":215},"日本語",[69,1241,212],{"class":75},[69,1243,160],{"class":75},[69,1245,1246],{"class":71,"line":239},[69,1247,576],{"class":75},[69,1249,1250,1252,1254,1256,1258,1260,1262,1264,1266,1268,1270,1272,1274,1276],{"class":71,"line":257},[69,1251,1196],{"class":187},[69,1253,203],{"class":75},[69,1255,470],{"class":174},[69,1257,209],{"class":75},[69,1259,1205],{"class":340},[69,1261,191],{"class":75},[69,1263,480],{"class":75},[69,1265,483],{"class":442},[69,1267,446],{"class":75},[69,1269,449],{"class":79},[69,1271,203],{"class":75},[69,1273,492],{"class":79},[69,1275,457],{"class":75},[69,1277,181],{"class":75},[69,1279,1280,1282,1284,1286,1288,1290,1293,1295,1297,1299,1301,1304,1306,1308,1310,1312],{"class":71,"line":263},[69,1281,1228],{"class":187},[69,1283,203],{"class":75},[69,1285,507],{"class":174},[69,1287,209],{"class":75},[69,1289,212],{"class":75},[69,1291,1292],{"class":215},"中文",[69,1294,212],{"class":75},[69,1296,191],{"class":75},[69,1298,521],{"class":187},[69,1300,203],{"class":75},[69,1302,1303],{"class":174},"FontFamily",[69,1305,209],{"class":75},[69,1307,212],{"class":75},[69,1309,1103],{"class":215},[69,1311,212],{"class":75},[69,1313,1314],{"class":75},"))\n",[69,1316,1317],{"class":71,"line":268},[69,1318,576],{"class":75},[69,1320,1321,1323,1325,1327,1329,1331,1333,1335,1337,1339,1341,1343,1345,1347],{"class":71,"line":286},[69,1322,1196],{"class":187},[69,1324,203],{"class":75},[69,1326,470],{"class":174},[69,1328,209],{"class":75},[69,1330,1205],{"class":340},[69,1332,191],{"class":75},[69,1334,480],{"class":75},[69,1336,483],{"class":442},[69,1338,446],{"class":75},[69,1340,449],{"class":79},[69,1342,203],{"class":75},[69,1344,492],{"class":79},[69,1346,457],{"class":75},[69,1348,181],{"class":75},[69,1350,1351,1353,1355,1357,1359,1361,1364,1366,1368,1370,1372,1374,1376,1378,1380,1382],{"class":71,"line":309},[69,1352,1228],{"class":187},[69,1354,203],{"class":75},[69,1356,507],{"class":174},[69,1358,209],{"class":75},[69,1360,212],{"class":75},[69,1362,1363],{"class":215},"한국어",[69,1365,212],{"class":75},[69,1367,191],{"class":75},[69,1369,521],{"class":187},[69,1371,203],{"class":75},[69,1373,1303],{"class":174},[69,1375,209],{"class":75},[69,1377,212],{"class":75},[69,1379,1127],{"class":215},[69,1381,212],{"class":75},[69,1383,1314],{"class":75},[69,1385,1386],{"class":71,"line":347},[69,1387,576],{"class":75},[69,1389,1390],{"class":71,"line":373},[69,1391,1392],{"class":75},"})\n",[17,1394,1395,1396,1399],{},"漢字統合 (Han unification) の都合で、日本語と中国語簡体字は Unicode コードポイントが重なるが、実際に描画される字形は別物。",[52,1397,1398],{},"同じコードポイントでもフォント次第で字の形が変わる"," ので、越境 EC 向けに和文・中文両対応の帳票を作るなら両方のフォントを登録する必要がある。国によって「骨」「直」などの形が違うことがあるのはここが原因。",[13,1401,1403],{"id":1402},"豆腐文字-が出たら","豆腐文字 (□□□□) が出たら",[17,1405,1406,1407,1409],{},"日本語を書いているのに ",[28,1408,354],{}," を忘れると、gpdf は標準 14 フォントにフォールバックし、そこには CJK のグリフが入っていないため文字が矩形のまま表示される。これが俗に言う「豆腐文字」。",[60,1411,1416],{"className":1412,"code":1414,"language":1415},[1413],"language-text","□□□□□、□□。\n","text",[28,1417,1414],{"__ignoreMap":65},[17,1419,1420,1421,1423,1424,1426,1427,1429,1430,1433],{},"この出力を見たら原因は一択: CJK フォントが未登録か、書いた文字を含まない別ファミリで描画している。直し方も一択: ",[28,1422,354],{}," を追加して ",[28,1425,380],{}," で既定にするか、",[28,1428,747],{}," に ",[28,1431,1432],{},"template.FontFamily"," を付ける。",[13,1435,1436],{"id":1436},"関連記事",[1438,1439,1440,1452,1459],"ul",{},[1441,1442,1443,1447,1448,1451],"li",{},[20,1444,1446],{"href":1445},"/ja/blog/gofpdf-migration","gofpdf がアーカイブされた。gpdf への移行ガイド"," — ",[28,1449,1450],{},"pdf.AddUTF8Font"," から乗り換える場合のフルマップ",[1441,1453,1454,1458],{},[20,1455,1457],{"href":1456},"/ja/blog/go-pdf-library-showdown-2026","Go PDF ライブラリ比較 2026"," — gofpdf / gopdf / Maroto / unipdf と gpdf の CJK 対応比較",[1441,1460,1461,1447,1466,1468],{},[20,1462,1465],{"href":1463,"rel":1464},"https://gpdf.dev/ja/docs/guide/fonts",[24],"フォントガイド",[28,1467,354],{}," のフルリファレンスとバリアント命名規則",[13,1470,1472],{"id":1471},"gpdf-を使ってみる","gpdf を使ってみる",[17,1474,1475],{},"gpdf は Go の PDF 生成ライブラリ。MIT、外部依存ゼロ、ネイティブ CJK 対応。",[60,1477,1481],{"className":1478,"code":1479,"language":1480,"meta":65,"style":65},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[28,1482,1483],{"__ignoreMap":65},[69,1484,1485,1487,1490],{"class":71,"line":72},[69,1486,64],{"class":79},[69,1488,1489],{"class":215}," get",[69,1491,1492],{"class":215}," github.com/gpdf-dev/gpdf\n",[17,1494,1495,1499,1500],{},[20,1496,1498],{"href":22,"rel":1497},[24],"⭐ GitHub でスター"," · ",[20,1501,1504],{"href":1502,"rel":1503},"https://gpdf.dev/ja/docs/quickstart",[24],"ドキュメントを読む",[1506,1507,1508],"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":65,"searchDepth":83,"depth":83,"links":1510},[1511,1512,1513,1514,1515,1516,1517,1518,1519],{"id":15,"depth":83,"text":15},{"id":34,"depth":83,"text":35},{"id":58,"depth":83,"text":58},{"id":728,"depth":83,"text":729},{"id":765,"depth":83,"text":766},{"id":952,"depth":83,"text":952},{"id":1402,"depth":83,"text":1403},{"id":1436,"depth":83,"text":1436},{"id":1471,"depth":83,"text":1472},"2026-04-15","gpdf.WithFont にTTFバイトを渡すだけ。サブセット埋め込みも自動、CGOも不要。Go で日本語 PDF を作る最短手順。",false,"md",{"name":1525,"totalTime":1526,"tools":1527,"steps":1530},"gpdf ドキュメントに日本語 TrueType フォントを埋め込む","PT10M",[1528,1529],"Go 1.22+","NotoSansJP-Regular.ttf (または任意の CJK 対応 TTF)",[1531,1534,1537,1540],{"name":1532,"text":1533},"TTF のバイト列を読み込む","プログラム起動時に NotoSansJP-Regular.ttf を os.ReadFile で []byte に読み込む。バイナリに焼き付けたいなら //go:embed でも可。",{"name":1535,"text":1536},"ドキュメント構築時に WithFont で登録する","gpdf.WithFont(\"NotoSansJP\", fontBytes) を gpdf.NewDocument に渡す。ファミリ名は任意 — 後で参照する名前を自由に決めてよい。サブセット化はレンダリング時に自動で走る。",{"name":1538,"text":1539},"デフォルトフォントに設定する","gpdf.WithDefaultFont(\"NotoSansJP\", 12) を追加すれば、FontFamily を毎回指定せずとも c.Text が日本語フォントで描画される。",{"name":1541,"text":1542},"日本語を書いて PDF を生成する","カラム内で c.Text(\"こんにちは、世界。\") を呼ぶ。doc.Generate() が []byte を返すので os.WriteFile でファイルに書き出す。",null,{},"/ja/blog/embed-japanese-font",{"title":5,"description":1521},"ja/blog/003.embed-japanese-font",[1549,1550,1551],"recipe","cjk","tutorial","7Ku_4hRojpBbCvwsilwANzdGrF4MXNK-CmN0LOzC66g",1776529265028]