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