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