[{"data":1,"prerenderedAt":1602},["ShallowReactive",2],{"blog-ko-noto-sans-jp-with-gpdf":3},{"id":4,"title":5,"author":6,"body":9,"date":1569,"description":1570,"draft":1571,"extension":1572,"howTo":1573,"image":1592,"meta":1593,"navigation":82,"path":1594,"seo":1595,"stem":1596,"tags":1597,"updated":1592,"__hash__":1601},"blogKo/ko/blog/004.noto-sans-jp-with-gpdf.md","gpdf에서 Noto Sans JP를 사용하려면?",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":1556},"minimark",[12,17,33,37,51,55,704,723,727,734,763,770,792,795,799,802,871,893,906,910,913,1066,1083,1086,1158,1168,1172,1175,1178,1245,1248,1251,1255,1258,1264,1278,1287,1302,1306,1309,1468,1475,1479,1516,1520,1523,1540,1552],[13,14,16],"h2",{"id":15},"질문을-다시-정리하면","질문을 다시 정리하면",[18,19,20,27,28,32],"p",{},[21,22,26],"a",{"href":23,"rel":24},"https://github.com/gpdf-dev/gpdf",[25],"nofollow","gpdf"," 문서에 일본어를 렌더링하려 하고, 폰트는 Noto Sans JP — Google이 배포하는 SIL OFL 라이선스의 고딕체, JIS 영역을 완전히 커버하는 그 폰트 — 를 쓰기로 정했다. Google Fonts의 zip은 이미 받았다. 여기서부터 알고 싶은 세 가지: ",[29,30,31],"strong",{},"어느 파일을 고를지, 어느 굵기를 등록할지, zip 안에 숨어 있는 한 가지 함정은 무엇인지",".",[13,34,36],{"id":35},"결론-tldr","결론 (TL;DR)",[18,38,39,40,46,47,50],{},"zip을 풀면 ",[29,41,42],{},[43,44,45],"code",{},"static/NotoSansJP-Regular.ttf"," 를 씁니다 — zip 루트의 variable 폰트가 아닙니다. 이 파일을 ",[43,48,49],{},"gpdf.WithFont(\"NotoSansJP\", bytes)","에 넘기고 기본 폰트로 지정하면 끝. gpdf는 약 17,000개 글리프 중 실제로 렌더링된 글리프만 서브셋해서 PDF에 포함합니다 — 일반적인 청구서 한 장은 최종 PDF에 20–40 KB 정도의 폰트 데이터를 담게 됩니다.",[13,52,54],{"id":53},"완전한-예제","완전한 예제",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package main\n\nimport (\n    \"log\"\n    \"os\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc main() {\n    font, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", font),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"請求書\", template.FontSize(28), template.Bold())\n            c.Text(\"Noto Sans JP、これで十分。\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[43,63,64,77,84,94,106,116,121,131,141,151,157,162,178,216,232,250,256,261,280,303,341,367,392,398,403,422,456,493,541,561,567,573,578,599,612,627,632,678,693,698],{"__ignoreMap":61},[65,66,69,73],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"sMK4o","package",[65,74,76],{"class":75},"sBMFI"," main\n",[65,78,80],{"class":67,"line":79},2,[65,81,83],{"emptyLinePlaceholder":82},true,"\n",[65,85,87,91],{"class":67,"line":86},3,[65,88,90],{"class":89},"s7zQu","import",[65,92,93],{"class":71}," (\n",[65,95,97,100,103],{"class":67,"line":96},4,[65,98,99],{"class":71},"    \"",[65,101,102],{"class":75},"log",[65,104,105],{"class":71},"\"\n",[65,107,109,111,114],{"class":67,"line":108},5,[65,110,99],{"class":71},[65,112,113],{"class":75},"os",[65,115,105],{"class":71},[65,117,119],{"class":67,"line":118},6,[65,120,83],{"emptyLinePlaceholder":82},[65,122,124,126,129],{"class":67,"line":123},7,[65,125,99],{"class":71},[65,127,128],{"class":75},"github.com/gpdf-dev/gpdf",[65,130,105],{"class":71},[65,132,134,136,139],{"class":67,"line":133},8,[65,135,99],{"class":71},[65,137,138],{"class":75},"github.com/gpdf-dev/gpdf/document",[65,140,105],{"class":71},[65,142,144,146,149],{"class":67,"line":143},9,[65,145,99],{"class":71},[65,147,148],{"class":75},"github.com/gpdf-dev/gpdf/template",[65,150,105],{"class":71},[65,152,154],{"class":67,"line":153},10,[65,155,156],{"class":71},")\n",[65,158,160],{"class":67,"line":159},11,[65,161,83],{"emptyLinePlaceholder":82},[65,163,165,168,172,175],{"class":67,"line":164},12,[65,166,167],{"class":71},"func",[65,169,171],{"class":170},"s2Zo4"," main",[65,173,174],{"class":71},"()",[65,176,177],{"class":71}," {\n",[65,179,181,185,188,191,194,197,199,202,205,208,212,214],{"class":67,"line":180},13,[65,182,184],{"class":183},"sTEyZ","    font",[65,186,187],{"class":71},",",[65,189,190],{"class":183}," err ",[65,192,193],{"class":71},":=",[65,195,196],{"class":183}," os",[65,198,32],{"class":71},[65,200,201],{"class":170},"ReadFile",[65,203,204],{"class":71},"(",[65,206,207],{"class":71},"\"",[65,209,211],{"class":210},"sfazB","NotoSansJP-Regular.ttf",[65,213,207],{"class":71},[65,215,156],{"class":71},[65,217,219,222,224,227,230],{"class":67,"line":218},14,[65,220,221],{"class":89},"    if",[65,223,190],{"class":183},[65,225,226],{"class":71},"!=",[65,228,229],{"class":71}," nil",[65,231,177],{"class":71},[65,233,235,238,240,243,245,248],{"class":67,"line":234},15,[65,236,237],{"class":183},"        log",[65,239,32],{"class":71},[65,241,242],{"class":170},"Fatal",[65,244,204],{"class":71},[65,246,247],{"class":183},"err",[65,249,156],{"class":71},[65,251,253],{"class":67,"line":252},16,[65,254,255],{"class":71},"    }\n",[65,257,259],{"class":67,"line":258},17,[65,260,83],{"emptyLinePlaceholder":82},[65,262,264,267,269,272,274,277],{"class":67,"line":263},18,[65,265,266],{"class":183},"    doc ",[65,268,193],{"class":71},[65,270,271],{"class":183}," gpdf",[65,273,32],{"class":71},[65,275,276],{"class":170},"NewDocument",[65,278,279],{"class":71},"(\n",[65,281,283,286,288,291,293,295,297,300],{"class":67,"line":282},19,[65,284,285],{"class":183},"        gpdf",[65,287,32],{"class":71},[65,289,290],{"class":170},"WithPageSize",[65,292,204],{"class":71},[65,294,26],{"class":183},[65,296,32],{"class":71},[65,298,299],{"class":183},"A4",[65,301,302],{"class":71},"),\n",[65,304,306,308,310,313,315,318,320,323,325,327,329,332,334,338],{"class":67,"line":305},20,[65,307,285],{"class":183},[65,309,32],{"class":71},[65,311,312],{"class":170},"WithMargins",[65,314,204],{"class":71},[65,316,317],{"class":183},"document",[65,319,32],{"class":71},[65,321,322],{"class":170},"UniformEdges",[65,324,204],{"class":71},[65,326,317],{"class":183},[65,328,32],{"class":71},[65,330,331],{"class":170},"Mm",[65,333,204],{"class":71},[65,335,337],{"class":336},"sbssI","20",[65,339,340],{"class":71},"))),\n",[65,342,344,346,348,351,353,355,358,360,362,365],{"class":67,"line":343},21,[65,345,285],{"class":183},[65,347,32],{"class":71},[65,349,350],{"class":170},"WithFont",[65,352,204],{"class":71},[65,354,207],{"class":71},[65,356,357],{"class":210},"NotoSansJP",[65,359,207],{"class":71},[65,361,187],{"class":71},[65,363,364],{"class":183}," font",[65,366,302],{"class":71},[65,368,370,372,374,377,379,381,383,385,387,390],{"class":67,"line":369},22,[65,371,285],{"class":183},[65,373,32],{"class":71},[65,375,376],{"class":170},"WithDefaultFont",[65,378,204],{"class":71},[65,380,207],{"class":71},[65,382,357],{"class":210},[65,384,207],{"class":71},[65,386,187],{"class":71},[65,388,389],{"class":336}," 11",[65,391,302],{"class":71},[65,393,395],{"class":67,"line":394},23,[65,396,397],{"class":71},"    )\n",[65,399,401],{"class":67,"line":400},24,[65,402,83],{"emptyLinePlaceholder":82},[65,404,406,409,411,414,416,419],{"class":67,"line":405},25,[65,407,408],{"class":183},"    page ",[65,410,193],{"class":71},[65,412,413],{"class":183}," doc",[65,415,32],{"class":71},[65,417,418],{"class":170},"AddPage",[65,420,421],{"class":71},"()\n",[65,423,425,428,430,433,436,440,443,446,448,451,454],{"class":67,"line":424},26,[65,426,427],{"class":183},"    page",[65,429,32],{"class":71},[65,431,432],{"class":170},"AutoRow",[65,434,435],{"class":71},"(func(",[65,437,439],{"class":438},"sHdIc","r",[65,441,442],{"class":71}," *",[65,444,445],{"class":75},"template",[65,447,32],{"class":71},[65,449,450],{"class":75},"RowBuilder",[65,452,453],{"class":71},")",[65,455,177],{"class":71},[65,457,459,462,464,467,469,472,474,477,480,482,484,486,489,491],{"class":67,"line":458},27,[65,460,461],{"class":183},"        r",[65,463,32],{"class":71},[65,465,466],{"class":170},"Col",[65,468,204],{"class":71},[65,470,471],{"class":336},"12",[65,473,187],{"class":71},[65,475,476],{"class":71}," func(",[65,478,479],{"class":438},"c",[65,481,442],{"class":71},[65,483,445],{"class":75},[65,485,32],{"class":71},[65,487,488],{"class":75},"ColBuilder",[65,490,453],{"class":71},[65,492,177],{"class":71},[65,494,496,499,501,504,506,508,511,513,515,518,520,523,525,528,531,533,535,538],{"class":67,"line":495},28,[65,497,498],{"class":183},"            c",[65,500,32],{"class":71},[65,502,503],{"class":170},"Text",[65,505,204],{"class":71},[65,507,207],{"class":71},[65,509,510],{"class":210},"請求書",[65,512,207],{"class":71},[65,514,187],{"class":71},[65,516,517],{"class":183}," template",[65,519,32],{"class":71},[65,521,522],{"class":170},"FontSize",[65,524,204],{"class":71},[65,526,527],{"class":336},"28",[65,529,530],{"class":71},"),",[65,532,517],{"class":183},[65,534,32],{"class":71},[65,536,537],{"class":170},"Bold",[65,539,540],{"class":71},"())\n",[65,542,544,546,548,550,552,554,557,559],{"class":67,"line":543},29,[65,545,498],{"class":183},[65,547,32],{"class":71},[65,549,503],{"class":170},[65,551,204],{"class":71},[65,553,207],{"class":71},[65,555,556],{"class":210},"Noto Sans JP、これで十分。",[65,558,207],{"class":71},[65,560,156],{"class":71},[65,562,564],{"class":67,"line":563},30,[65,565,566],{"class":71},"        })\n",[65,568,570],{"class":67,"line":569},31,[65,571,572],{"class":71},"    })\n",[65,574,576],{"class":67,"line":575},32,[65,577,83],{"emptyLinePlaceholder":82},[65,579,581,584,586,588,590,592,594,597],{"class":67,"line":580},33,[65,582,583],{"class":183},"    data",[65,585,187],{"class":71},[65,587,190],{"class":183},[65,589,193],{"class":71},[65,591,413],{"class":183},[65,593,32],{"class":71},[65,595,596],{"class":170},"Generate",[65,598,421],{"class":71},[65,600,602,604,606,608,610],{"class":67,"line":601},34,[65,603,221],{"class":89},[65,605,190],{"class":183},[65,607,226],{"class":71},[65,609,229],{"class":71},[65,611,177],{"class":71},[65,613,615,617,619,621,623,625],{"class":67,"line":614},35,[65,616,237],{"class":183},[65,618,32],{"class":71},[65,620,242],{"class":170},[65,622,204],{"class":71},[65,624,247],{"class":183},[65,626,156],{"class":71},[65,628,630],{"class":67,"line":629},36,[65,631,255],{"class":71},[65,633,635,637,639,641,643,645,648,650,652,655,657,659,662,664,667,670,672,674,676],{"class":67,"line":634},37,[65,636,221],{"class":89},[65,638,190],{"class":183},[65,640,193],{"class":71},[65,642,196],{"class":183},[65,644,32],{"class":71},[65,646,647],{"class":170},"WriteFile",[65,649,204],{"class":71},[65,651,207],{"class":71},[65,653,654],{"class":210},"invoice.pdf",[65,656,207],{"class":71},[65,658,187],{"class":71},[65,660,661],{"class":183}," data",[65,663,187],{"class":71},[65,665,666],{"class":336}," 0o644",[65,668,669],{"class":71},");",[65,671,190],{"class":183},[65,673,226],{"class":71},[65,675,229],{"class":71},[65,677,177],{"class":71},[65,679,681,683,685,687,689,691],{"class":67,"line":680},38,[65,682,237],{"class":183},[65,684,32],{"class":71},[65,686,242],{"class":170},[65,688,204],{"class":71},[65,690,247],{"class":183},[65,692,156],{"class":71},[65,694,696],{"class":67,"line":695},39,[65,697,255],{"class":71},[65,699,701],{"class":67,"line":700},40,[65,702,703],{"class":71},"}\n",[18,705,706,711,712,714,715,718,719,722],{},[21,707,710],{"href":708,"rel":709},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[25],"Google Fonts","에서 zip을 받아 풀고, ",[43,713,45],{},"를 ",[43,716,717],{},"main.go"," 옆에 두고 ",[43,720,721],{},"go run main.go","를 실행하면 한 페이지짜리 PDF가 나옵니다.",[13,724,726],{"id":725},"variable-폰트가-아닌-static-ttf를-고르기","variable 폰트가 아닌 static TTF를 고르기",[18,728,729,730,733],{},"Google Fonts에서 ",[29,731,732],{},"Get font → Download all",", zip 압축을 풀면 보기에 비슷하지만 성격이 전혀 다른 두 그룹이 보입니다:",[735,736,737,749],"ul",{},[738,739,740,741,744,745,748],"li",{},"zip 루트의 ",[43,742,743],{},"NotoSansJP-VariableFont_wght.ttf"," — weight 100–900을 한 파일에 담은 ",[29,746,747],{},"variable 폰트",", 약 7 MB",[738,750,751,754,755,758,759,762],{},[43,752,753],{},"static/"," 디렉토리 — ",[43,756,757],{},"NotoSansJP-Thin.ttf","부터 ",[43,760,761],{},"NotoSansJP-Black.ttf","까지 굵기별로 분리된 9개의 TTF, 각 약 5 MB",[18,764,765,32],{},[29,766,767,769],{},[43,768,753],{}," 쪽을 쓰세요",[18,771,772,773,776,777,780,781,784,785,784,788,791],{},"gpdf의 TrueType 파서는 일부러 범위를 좁게 잡아 뒀습니다. 글리프 아웃라인, 복합 글리프, ",[43,774,775],{},"cmap",", ",[43,778,779],{},"hmtx"," — 고정 굵기 텍스트를 렌더링하는 데 필요한 테이블은 다 다룹니다. 하지만 variable 폰트를 진짜 가변적으로 만드는 ",[43,782,783],{},"fvar"," / ",[43,786,787],{},"gvar",[43,789,790],{},"HVAR"," 테이블은 읽지 않습니다. VariableFont_wght.ttf를 넘기면 파서가 깔끔히 에러를 내거나, 운이 나쁘면 기본 인스턴스의 글리프만 뽑고 당신이 지정했다고 생각한 weight 축을 조용히 무시합니다.",[18,793,794],{},"파일 크기 측면에서도 static 쪽이 유리합니다. variable 폰트는 weight 축 위의 모든 인스턴스 아웃라인을 한 파일에 담는 — 그게 설계 의도입니다. Regular만 쓴다면 나머지 8개 weight 만큼의 데이터를 그냥 실어 나르게 됩니다. static Regular가 5 MB, variable이 7 MB. 서브셋으로 둘 다 줄어들긴 하지만 입력은 static이 깔끔합니다.",[13,796,798],{"id":797},"핵심은-이-네-줄","핵심은 이 네 줄",[18,800,801],{},"의미 있는 건 생성자 옵션뿐입니다:",[56,803,805],{"className":58,"code":804,"language":60,"meta":61,"style":61},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,806,807,822,845,867],{"__ignoreMap":61},[65,808,809,812,814,816,818,820],{"class":67,"line":68},[65,810,811],{"class":183},"doc ",[65,813,193],{"class":71},[65,815,271],{"class":183},[65,817,32],{"class":71},[65,819,276],{"class":170},[65,821,279],{"class":71},[65,823,824,827,829,831,833,835,837,839,841,843],{"class":67,"line":79},[65,825,826],{"class":183},"    gpdf",[65,828,32],{"class":71},[65,830,350],{"class":170},[65,832,204],{"class":71},[65,834,207],{"class":71},[65,836,357],{"class":210},[65,838,207],{"class":71},[65,840,187],{"class":71},[65,842,364],{"class":183},[65,844,302],{"class":71},[65,846,847,849,851,853,855,857,859,861,863,865],{"class":67,"line":86},[65,848,826],{"class":183},[65,850,32],{"class":71},[65,852,376],{"class":170},[65,854,204],{"class":71},[65,856,207],{"class":71},[65,858,357],{"class":210},[65,860,207],{"class":71},[65,862,187],{"class":71},[65,864,389],{"class":336},[65,866,302],{"class":71},[65,868,869],{"class":67,"line":96},[65,870,156],{"class":71},[18,872,873,874,877,878,881,882,776,885,888,889,892],{},"폰트 패밀리명 (",[43,875,876],{},"\"NotoSansJP\"",") 은 임의로 정해도 됩니다. gpdf는 이를 조회 키로만 씁니다 — 파일 경로도 아니고, 폰트 메타데이터에서 읽는 이름도 아닙니다. 팀에서 ",[43,879,880],{},"\"body\"","나 ",[43,883,884],{},"\"jp\"",[43,886,887],{},"\"Noto\"","가 더 읽기 좋다면 그걸 쓰세요. 나중에 ",[43,890,891],{},"template.FontFamily(...)","에 같은 이름을 넘기기만 하면 됩니다.",[18,894,895,897,898,901,902,905],{},[43,896,376],{},"는 매번 ",[43,899,900],{},"c.Text"," 호출에 ",[43,903,904],{},"template.FontFamily(\"NotoSansJP\")","를 쓰지 않게 해 줍니다. 이걸 빼면 gpdf는 Helvetica로 폴백하는데, Helvetica는 CJK 코드포인트를 하나도 커버하지 않아서 — 제목만 제대로 나오고 본문 전체가 두부 네모 (□□□)가 된 PDF가 나옵니다. 왜 제목만 멀쩡한지 한 시간쯤 헤매게 됩니다.",[13,907,909],{"id":908},"어느-굵기를-등록해야-할까","어느 굵기를 등록해야 할까",[18,911,912],{},"청구서·영수증·업무용 리포트라면 Regular와 Bold 두 개면 충분합니다:",[56,914,916],{"className":58,"code":915,"language":60,"meta":61,"style":61},"reg,  _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nbold, _ := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", reg),\n    gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,917,918,946,975,979,993,1016,1040,1062],{"__ignoreMap":61},[65,919,920,923,925,928,930,932,934,936,938,940,942,944],{"class":67,"line":68},[65,921,922],{"class":183},"reg",[65,924,187],{"class":71},[65,926,927],{"class":183},"  _ ",[65,929,193],{"class":71},[65,931,196],{"class":183},[65,933,32],{"class":71},[65,935,201],{"class":170},[65,937,204],{"class":71},[65,939,207],{"class":71},[65,941,211],{"class":210},[65,943,207],{"class":71},[65,945,156],{"class":71},[65,947,948,951,953,956,958,960,962,964,966,968,971,973],{"class":67,"line":79},[65,949,950],{"class":183},"bold",[65,952,187],{"class":71},[65,954,955],{"class":183}," _ ",[65,957,193],{"class":71},[65,959,196],{"class":183},[65,961,32],{"class":71},[65,963,201],{"class":170},[65,965,204],{"class":71},[65,967,207],{"class":71},[65,969,970],{"class":210},"NotoSansJP-Bold.ttf",[65,972,207],{"class":71},[65,974,156],{"class":71},[65,976,977],{"class":67,"line":86},[65,978,83],{"emptyLinePlaceholder":82},[65,980,981,983,985,987,989,991],{"class":67,"line":96},[65,982,811],{"class":183},[65,984,193],{"class":71},[65,986,271],{"class":183},[65,988,32],{"class":71},[65,990,276],{"class":170},[65,992,279],{"class":71},[65,994,995,997,999,1001,1003,1005,1007,1009,1011,1014],{"class":67,"line":108},[65,996,826],{"class":183},[65,998,32],{"class":71},[65,1000,350],{"class":170},[65,1002,204],{"class":71},[65,1004,207],{"class":71},[65,1006,357],{"class":210},[65,1008,207],{"class":71},[65,1010,187],{"class":71},[65,1012,1013],{"class":183}," reg",[65,1015,302],{"class":71},[65,1017,1018,1020,1022,1024,1026,1028,1031,1033,1035,1038],{"class":67,"line":118},[65,1019,826],{"class":183},[65,1021,32],{"class":71},[65,1023,350],{"class":170},[65,1025,204],{"class":71},[65,1027,207],{"class":71},[65,1029,1030],{"class":210},"NotoSansJP-Bold",[65,1032,207],{"class":71},[65,1034,187],{"class":71},[65,1036,1037],{"class":183}," bold",[65,1039,302],{"class":71},[65,1041,1042,1044,1046,1048,1050,1052,1054,1056,1058,1060],{"class":67,"line":123},[65,1043,826],{"class":183},[65,1045,32],{"class":71},[65,1047,376],{"class":170},[65,1049,204],{"class":71},[65,1051,207],{"class":71},[65,1053,357],{"class":210},[65,1055,207],{"class":71},[65,1057,187],{"class":71},[65,1059,389],{"class":336},[65,1061,302],{"class":71},[65,1063,1064],{"class":67,"line":133},[65,1065,156],{"class":71},[18,1067,1068,1071,1072,1075,1076,776,1079,1082],{},[43,1069,1070],{},"-Bold"," 서픽스로 등록하면 ",[43,1073,1074],{},"template.Bold()","가 자동으로 잡습니다. ",[43,1077,1078],{},"-Italic",[43,1080,1081],{},"-BoldItalic","도 같은 규약. 다만 Noto Sans JP에는 이탤릭이 없습니다 — CJK 폰트는 자연스러운 기울임 형태가 없어서 Noto 계열에도 이탤릭이 존재하지 않습니다. 일본어에서 강조가 필요하면 색상·크기·굵기로 대체하세요.",[18,1084,1085],{},"브로슈어나 헤드라인에 Medium이나 SemiBold가 필요하면 원하는 서픽스로 등록하고 패밀리명으로 직접 참조하면 됩니다:",[56,1087,1089],{"className":58,"code":1088,"language":60,"meta":61,"style":61},"gpdf.WithFont(\"NotoSansJP-Medium\", medium)\n// ...\nc.Text(\"見出し\", template.FontFamily(\"NotoSansJP-Medium\"))\n",[43,1090,1091,1115,1121],{"__ignoreMap":61},[65,1092,1093,1095,1097,1099,1101,1103,1106,1108,1110,1113],{"class":67,"line":68},[65,1094,26],{"class":183},[65,1096,32],{"class":71},[65,1098,350],{"class":170},[65,1100,204],{"class":71},[65,1102,207],{"class":71},[65,1104,1105],{"class":210},"NotoSansJP-Medium",[65,1107,207],{"class":71},[65,1109,187],{"class":71},[65,1111,1112],{"class":183}," medium",[65,1114,156],{"class":71},[65,1116,1117],{"class":67,"line":79},[65,1118,1120],{"class":1119},"sHwdD","// ...\n",[65,1122,1123,1125,1127,1129,1131,1133,1136,1138,1140,1142,1144,1147,1149,1151,1153,1155],{"class":67,"line":86},[65,1124,479],{"class":183},[65,1126,32],{"class":71},[65,1128,503],{"class":170},[65,1130,204],{"class":71},[65,1132,207],{"class":71},[65,1134,1135],{"class":210},"見出し",[65,1137,207],{"class":71},[65,1139,187],{"class":71},[65,1141,517],{"class":183},[65,1143,32],{"class":71},[65,1145,1146],{"class":170},"FontFamily",[65,1148,204],{"class":71},[65,1150,207],{"class":71},[65,1152,1105],{"class":210},[65,1154,207],{"class":71},[65,1156,1157],{"class":71},"))\n",[18,1159,1160,1161,784,1163,784,1165,1167],{},"서픽스 기반 Bold/Italic 숏컷은 ",[43,1162,1070],{},[43,1164,1078],{},[43,1166,1081],{}," 이 세 개에만 자동 연결됩니다. 나머지는 패밀리명으로 명시적으로 부릅니다.",[13,1169,1171],{"id":1170},"서브셋-후의-실제-크기","서브셋 후의 실제 크기",[18,1173,1174],{},"Noto Sans JP Regular는 디스크에서 약 5 MB. 이 숫자를 보고 별도 폰트 CDN을 꾸리거나 PDF 후처리로 폰트를 빼내는 팀이 가끔 있는데, gpdf에서는 둘 다 필요 없습니다.",[18,1176,1177],{},"실제로 PDF에 들어가는 양은 이 정도:",[1179,1180,1181,1197],"table",{},[1182,1183,1184],"thead",{},[1185,1186,1187,1191,1194],"tr",{},[1188,1189,1190],"th",{},"문서",[1188,1192,1193],{},"사용 글리프",[1188,1195,1196],{},"PDF 내 폰트 데이터",[1198,1199,1200,1212,1223,1234],"tbody",{},[1185,1201,1202,1206,1209],{},[1203,1204,1205],"td",{},"한 줄 영수증 (~15자)",[1203,1207,1208],{},"~14",[1203,1210,1211],{},"~11 KB",[1185,1213,1214,1217,1220],{},[1203,1215,1216],{},"일반 청구서 (~200자)",[1203,1218,1219],{},"~80",[1203,1221,1222],{},"~28 KB",[1185,1224,1225,1228,1231],{},[1203,1226,1227],{},"10페이지 리포트 (~8,000자)",[1203,1229,1230],{},"~900",[1203,1232,1233],{},"~180 KB",[1185,1235,1236,1239,1242],{},[1203,1237,1238],{},"사전 수준 풀셋 (JIS Level 1)",[1203,1240,1241],{},"~6,800",[1203,1243,1244],{},"~2.1 MB",[18,1246,1247],{},"(gpdf v1.0, 정적 서브셋 활성화. 글리프 ID가 CFF와 hmtx 어디에 떨어지느냐에 따라 몇 KB 편차)",[18,1249,1250],{},"최종 50 KB짜리 청구서 PDF라면 그중 절반 이상이 폰트 데이터입니다. 그래도 서브셋 없이 5 MB를 통째로 넣는 것에 비하면 오차 수준이고, 뷰어는 즉시 엽니다.",[13,1252,1254],{"id":1253},"noto-sans-jp와-noto-sans-cjk-jp-혼동-금지","Noto Sans JP와 Noto Sans CJK JP — 혼동 금지",[18,1256,1257],{},"일본어를 처리할 수 있다고 주장하는 Noto 패밀리가 두 개 있고, 이름이 비슷해서 서로 호환된다고 착각하기 쉽습니다. 실제로는 완전히 다릅니다.",[18,1259,1260,1263],{},[29,1261,1262],{},"Noto Sans JP","가 쓰려는 쪽입니다. TTF 배포, 단일 언어, 굵기별로 파일이 분리. Google Fonts에서 받는 것이 이것입니다.",[18,1265,1266,1269,1270,1273,1274,1277],{},[29,1267,1268],{},"Noto Sans CJK JP","는 CJK 전체를 아우르는 슈퍼 패밀리. OpenType Collection (",[43,1271,1272],{},".ttc",") 형식으로, 일본어·간체 중국어·번체 중국어·한국어 글리프를 한자 통합 (Han unification) 방식으로 한 파일에 담아 배포합니다. 초기 Noto 릴리스와 ",[43,1275,1276],{},"notofonts.github.io/noto-cjk","에 있는 것은 이쪽입니다.",[18,1279,1280,1281,1283,1284,1286],{},"gpdf는 TTF를 바로 지원합니다. TTC는 컨테이너 포맷이라 ",[43,1282,350],{},"에 바이트를 넘기기 전에 face 인덱스를 골라야 하고, 각 face 안의 ",[43,1285,775],{},"은 특정 CJK 로케일에 맞춰져 있어 한자 통합에 관한 선택을 암묵적으로 하는 꼴이 됩니다. JP 전용 TTF를 고르면 이런 선택이 명시적이 됩니다.",[18,1288,1289,1290,1293,1294,1297,1298,1301],{},"새 프로젝트면 Noto Sans JP를 씁니다. 레거시 프로젝트에 ",[43,1291,1292],{},"NotoSansCJK-Regular.ttc","가 이미 있다면 ",[43,1295,1296],{},"pyftsubset","이나 ",[43,1299,1300],{},"fonttools","로 JP face만 추출해 TTF로 저장소에 커밋하는 편이 안전합니다.",[13,1303,1305],{"id":1304},"바이너리에-폰트-임베드하기","바이너리에 폰트 임베드하기",[18,1307,1308],{},"PDF 생성기는 대개 컨테이너에서 돕니다. 폰트를 함께 배포하는 가장 깔끔한 방법은 바이너리에 컴파일해 넣는 것:",[56,1310,1312],{"className":58,"code":1311,"language":60,"meta":61,"style":61},"package main\n\nimport (\n    _ \"embed\"\n\n    \"github.com/gpdf-dev/gpdf\"\n)\n\n//go:embed NotoSansJP-Regular.ttf\nvar notoJP []byte\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithFont(\"NotoSansJP\", notoJP),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n    // ...\n}\n",[43,1313,1314,1320,1324,1330,1342,1346,1354,1358,1362,1367,1382,1386,1396,1410,1433,1455,1459,1464],{"__ignoreMap":61},[65,1315,1316,1318],{"class":67,"line":68},[65,1317,72],{"class":71},[65,1319,76],{"class":75},[65,1321,1322],{"class":67,"line":79},[65,1323,83],{"emptyLinePlaceholder":82},[65,1325,1326,1328],{"class":67,"line":86},[65,1327,90],{"class":89},[65,1329,93],{"class":71},[65,1331,1332,1335,1337,1340],{"class":67,"line":96},[65,1333,1334],{"class":183},"    _ ",[65,1336,207],{"class":71},[65,1338,1339],{"class":75},"embed",[65,1341,105],{"class":71},[65,1343,1344],{"class":67,"line":108},[65,1345,83],{"emptyLinePlaceholder":82},[65,1347,1348,1350,1352],{"class":67,"line":118},[65,1349,99],{"class":71},[65,1351,128],{"class":75},[65,1353,105],{"class":71},[65,1355,1356],{"class":67,"line":123},[65,1357,156],{"class":71},[65,1359,1360],{"class":67,"line":133},[65,1361,83],{"emptyLinePlaceholder":82},[65,1363,1364],{"class":67,"line":143},[65,1365,1366],{"class":1119},"//go:embed NotoSansJP-Regular.ttf\n",[65,1368,1369,1372,1375,1378],{"class":67,"line":153},[65,1370,1371],{"class":71},"var",[65,1373,1374],{"class":183}," notoJP ",[65,1376,1377],{"class":71},"[]",[65,1379,1381],{"class":1380},"spNyl","byte\n",[65,1383,1384],{"class":67,"line":159},[65,1385,83],{"emptyLinePlaceholder":82},[65,1387,1388,1390,1392,1394],{"class":67,"line":164},[65,1389,167],{"class":71},[65,1391,171],{"class":170},[65,1393,174],{"class":71},[65,1395,177],{"class":71},[65,1397,1398,1400,1402,1404,1406,1408],{"class":67,"line":180},[65,1399,266],{"class":183},[65,1401,193],{"class":71},[65,1403,271],{"class":183},[65,1405,32],{"class":71},[65,1407,276],{"class":170},[65,1409,279],{"class":71},[65,1411,1412,1414,1416,1418,1420,1422,1424,1426,1428,1431],{"class":67,"line":218},[65,1413,285],{"class":183},[65,1415,32],{"class":71},[65,1417,350],{"class":170},[65,1419,204],{"class":71},[65,1421,207],{"class":71},[65,1423,357],{"class":210},[65,1425,207],{"class":71},[65,1427,187],{"class":71},[65,1429,1430],{"class":183}," notoJP",[65,1432,302],{"class":71},[65,1434,1435,1437,1439,1441,1443,1445,1447,1449,1451,1453],{"class":67,"line":234},[65,1436,285],{"class":183},[65,1438,32],{"class":71},[65,1440,376],{"class":170},[65,1442,204],{"class":71},[65,1444,207],{"class":71},[65,1446,357],{"class":210},[65,1448,207],{"class":71},[65,1450,187],{"class":71},[65,1452,389],{"class":336},[65,1454,302],{"class":71},[65,1456,1457],{"class":67,"line":252},[65,1458,397],{"class":71},[65,1460,1461],{"class":67,"line":258},[65,1462,1463],{"class":1119},"    // ...\n",[65,1465,1466],{"class":67,"line":263},[65,1467,703],{"class":71},[18,1469,1470,1471,1474],{},"바이너리는 약 8 MB에서 약 13 MB로 늘어납니다. 대신 Docker 이미지 산출물이 두 개가 아니라 하나가 되고, ",[43,1472,1473],{},"COPY --from=builder /app /app"," 만으로 충분하며, 폰트 파일 누락으로 망가진 컨테이너를 누군가 올릴 일도 없습니다. 하루에 수천 개 PDF를 생성하는 배치 작업이라면 이 쪽이 올바른 기본값입니다.",[13,1476,1478],{"id":1477},"관련-읽을거리","관련 읽을거리",[735,1480,1481,1488,1499,1506],{},[738,1482,1483,1487],{},[21,1484,1486],{"href":1485},"/ko/blog/embed-japanese-font","gpdf에서 일본어 폰트를 임베드하려면?"," — CJK TTF 전반에 적용되는 일반 레시피",[738,1489,1490,1494,1495,1498],{},[21,1491,1493],{"href":1492},"/ko/blog/gofpdf-migration","gofpdf가 아카이브되었다. gpdf 마이그레이션 가이드"," — ",[43,1496,1497],{},"AddUTF8Font","에서 옮겨 오는 매핑",[738,1500,1501,1505],{},[21,1502,1504],{"href":1503},"/ko/blog/go-pdf-library-showdown-2026","Go PDF 라이브러리 쇼다운 2026"," — CJK 처리 관점의 비교",[738,1507,1508,1494,1513,1515],{},[21,1509,1512],{"href":1510,"rel":1511},"https://gpdf.dev/docs/guide/fonts",[25],"폰트 가이드",[43,1514,350],{}," 완전 레퍼런스",[13,1517,1519],{"id":1518},"gpdf를-써-보기","gpdf를 써 보기",[18,1521,1522],{},"gpdf는 Go용 PDF 생성 라이브러리입니다. MIT, 외부 의존성 제로, 네이티브 CJK 지원.",[56,1524,1528],{"className":1525,"code":1526,"language":1527,"meta":61,"style":61},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[43,1529,1530],{"__ignoreMap":61},[65,1531,1532,1534,1537],{"class":67,"line":68},[65,1533,60],{"class":75},[65,1535,1536],{"class":210}," get",[65,1538,1539],{"class":210}," github.com/gpdf-dev/gpdf\n",[18,1541,1542,1546,1547],{},[21,1543,1545],{"href":23,"rel":1544},[25],"⭐ GitHub에서 Star"," · ",[21,1548,1551],{"href":1549,"rel":1550},"https://gpdf.dev/docs/quickstart",[25],"문서 보기",[1553,1554,1555],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":61,"searchDepth":79,"depth":79,"links":1557},[1558,1559,1560,1561,1562,1563,1564,1565,1566,1567,1568],{"id":15,"depth":79,"text":16},{"id":35,"depth":79,"text":36},{"id":53,"depth":79,"text":54},{"id":725,"depth":79,"text":726},{"id":797,"depth":79,"text":798},{"id":908,"depth":79,"text":909},{"id":1170,"depth":79,"text":1171},{"id":1253,"depth":79,"text":1254},{"id":1304,"depth":79,"text":1305},{"id":1477,"depth":79,"text":1478},{"id":1518,"depth":79,"text":1519},"2026-04-15","gpdf.WithFont에 static 버전 NotoSansJP-Regular.ttf를 등록합니다. variable 폰트를 쓰지 않는 이유와 17,000개 글리프가 PDF에서 40 KB 미만까지 줄어드는 서브셋 구조를 설명합니다.",false,"md",{"name":1574,"totalTime":1575,"tools":1576,"steps":1579},"gpdf 문서에서 Noto Sans JP를 기본 폰트로 사용하기","PT10M",[1577,1578],"Go 1.22+","NotoSansJP-Regular.ttf (Google Fonts의 static TTF)",[1580,1583,1586,1589],{"name":1581,"text":1582},"Google Fonts에서 static TTF 다운로드","fonts.google.com에서 Noto Sans JP zip을 받아 압축을 풀고 static/NotoSansJP-Regular.ttf를 선택합니다. zip 루트의 NotoSansJP-VariableFont_wght.ttf는 쓰지 않습니다.",{"name":1584,"text":1585},"시작 시점에 바이트로 읽기","os.ReadFile로 NotoSansJP-Regular.ttf를 읽습니다. 바이너리에 포함하고 싶다면 //go:embed를 씁니다.",{"name":1587,"text":1588},"문서 생성 시 폰트 등록","gpdf.NewDocument에 gpdf.WithFont(\"NotoSansJP\", fontBytes)와 gpdf.WithDefaultFont(\"NotoSansJP\", 11)을 전달합니다. AddUTF8Font도 파일 경로도 필요 없습니다.",{"name":1590,"text":1591},"일본어 텍스트 작성 후 PDF 생성","컬럼 안에서 c.Text(\"請求書\")를 호출합니다. doc.Generate()가 []byte를 반환하고, gpdf는 실제로 사용한 글리프만 서브셋으로 최종 PDF에 임베드합니다.",null,{},"/ko/blog/noto-sans-jp-with-gpdf",{"title":5,"description":1570},"ko/blog/004.noto-sans-jp-with-gpdf",[1598,1599,1600],"recipe","cjk","tutorial","hNM0iCUbRC1dUgryk9bj5q9wPeXGxWoSj3L4d8uNQ7c",1776529266988]