[{"data":1,"prerenderedAt":1576},["ShallowReactive",2],{"blog-en-add-custom-truetype-font":3},{"id":4,"title":5,"author":6,"body":10,"date":1544,"description":1545,"draft":1546,"extension":1547,"howTo":1548,"image":1567,"meta":1568,"navigation":113,"path":1569,"seo":1570,"stem":1571,"tags":1572,"updated":1567,"__hash__":1575},"blog/blog/018.add-custom-truetype-font.md","How do I add a custom TrueType font to gpdf?",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/en/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":1532},"minimark",[13,18,39,43,66,82,86,724,744,748,781,787,791,794,866,875,878,1135,1146,1150,1156,1244,1250,1254,1257,1418,1421,1425,1465,1469,1492,1496,1499,1516,1528],[14,15,17],"h2",{"id":16},"the-question-in-other-words","The question, in other words",[19,20,21,22,26,27,34,35,38],"p",{},"I have a ",[23,24,25],"code",{},".ttf"," file — Inter for the brand, JetBrains Mono for code blocks, an icon font for glyphs. How do I get it into a ",[28,29,33],"a",{"href":30,"rel":31},"https://github.com/gpdf-dev/gpdf",[32],"nofollow","gpdf"," document and reference it from a ",[23,36,37],{},"c.Text(...)"," call?",[14,40,42],{"id":41},"tldr","TL;DR",[19,44,45,46,49,50,53,54,57,58,61,62,65],{},"Load the TTF bytes. Pass ",[23,47,48],{},"gpdf.WithFont(\"YourFamily\", bytes)"," to ",[23,51,52],{},"NewDocument",". Then reference ",[23,55,56],{},"\"YourFamily\""," from ",[23,59,60],{},"template.FontFamily(...)"," or set it as the default with ",[23,63,64],{},"gpdf.WithDefaultFont",".",[19,67,68,69,73,74,77,78,81],{},"The family name is ",[70,71,72],"strong",{},"arbitrary",". It has nothing to do with the font's internal ",[23,75,76],{},"name"," table — it's just the lookup key gpdf uses when resolving a ",[23,79,80],{},"FontFamily"," option. Pick something short.",[14,83,85],{"id":84},"working-code","Working code",[87,88,93],"pre",{"className":89,"code":90,"language":91,"meta":92,"style":92},"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    regular, err := os.ReadFile(\"Inter-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(\"Inter\", regular),\n        gpdf.WithDefaultFont(\"Inter\", 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(\"Quarterly Report\", template.FontSize(28))\n            c.Text(\"Generated with gpdf and Inter.\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"report.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[23,94,95,108,115,125,137,147,152,162,172,182,188,193,209,247,263,281,287,292,310,333,371,397,422,428,433,452,486,523,561,581,587,593,598,619,632,647,652,698,713,718],{"__ignoreMap":92},[96,97,100,104],"span",{"class":98,"line":99},"line",1,[96,101,103],{"class":102},"sMK4o","package",[96,105,107],{"class":106},"sBMFI"," main\n",[96,109,111],{"class":98,"line":110},2,[96,112,114],{"emptyLinePlaceholder":113},true,"\n",[96,116,118,122],{"class":98,"line":117},3,[96,119,121],{"class":120},"s7zQu","import",[96,123,124],{"class":102}," (\n",[96,126,128,131,134],{"class":98,"line":127},4,[96,129,130],{"class":102},"    \"",[96,132,133],{"class":106},"log",[96,135,136],{"class":102},"\"\n",[96,138,140,142,145],{"class":98,"line":139},5,[96,141,130],{"class":102},[96,143,144],{"class":106},"os",[96,146,136],{"class":102},[96,148,150],{"class":98,"line":149},6,[96,151,114],{"emptyLinePlaceholder":113},[96,153,155,157,160],{"class":98,"line":154},7,[96,156,130],{"class":102},[96,158,159],{"class":106},"github.com/gpdf-dev/gpdf",[96,161,136],{"class":102},[96,163,165,167,170],{"class":98,"line":164},8,[96,166,130],{"class":102},[96,168,169],{"class":106},"github.com/gpdf-dev/gpdf/document",[96,171,136],{"class":102},[96,173,175,177,180],{"class":98,"line":174},9,[96,176,130],{"class":102},[96,178,179],{"class":106},"github.com/gpdf-dev/gpdf/template",[96,181,136],{"class":102},[96,183,185],{"class":98,"line":184},10,[96,186,187],{"class":102},")\n",[96,189,191],{"class":98,"line":190},11,[96,192,114],{"emptyLinePlaceholder":113},[96,194,196,199,203,206],{"class":98,"line":195},12,[96,197,198],{"class":102},"func",[96,200,202],{"class":201},"s2Zo4"," main",[96,204,205],{"class":102},"()",[96,207,208],{"class":102}," {\n",[96,210,212,216,219,222,225,228,230,233,236,239,243,245],{"class":98,"line":211},13,[96,213,215],{"class":214},"sTEyZ","    regular",[96,217,218],{"class":102},",",[96,220,221],{"class":214}," err ",[96,223,224],{"class":102},":=",[96,226,227],{"class":214}," os",[96,229,65],{"class":102},[96,231,232],{"class":201},"ReadFile",[96,234,235],{"class":102},"(",[96,237,238],{"class":102},"\"",[96,240,242],{"class":241},"sfazB","Inter-Regular.ttf",[96,244,238],{"class":102},[96,246,187],{"class":102},[96,248,250,253,255,258,261],{"class":98,"line":249},14,[96,251,252],{"class":120},"    if",[96,254,221],{"class":214},[96,256,257],{"class":102},"!=",[96,259,260],{"class":102}," nil",[96,262,208],{"class":102},[96,264,266,269,271,274,276,279],{"class":98,"line":265},15,[96,267,268],{"class":214},"        log",[96,270,65],{"class":102},[96,272,273],{"class":201},"Fatal",[96,275,235],{"class":102},[96,277,278],{"class":214},"err",[96,280,187],{"class":102},[96,282,284],{"class":98,"line":283},16,[96,285,286],{"class":102},"    }\n",[96,288,290],{"class":98,"line":289},17,[96,291,114],{"emptyLinePlaceholder":113},[96,293,295,298,300,303,305,307],{"class":98,"line":294},18,[96,296,297],{"class":214},"    doc ",[96,299,224],{"class":102},[96,301,302],{"class":214}," gpdf",[96,304,65],{"class":102},[96,306,52],{"class":201},[96,308,309],{"class":102},"(\n",[96,311,313,316,318,321,323,325,327,330],{"class":98,"line":312},19,[96,314,315],{"class":214},"        gpdf",[96,317,65],{"class":102},[96,319,320],{"class":201},"WithPageSize",[96,322,235],{"class":102},[96,324,33],{"class":214},[96,326,65],{"class":102},[96,328,329],{"class":214},"A4",[96,331,332],{"class":102},"),\n",[96,334,336,338,340,343,345,348,350,353,355,357,359,362,364,368],{"class":98,"line":335},20,[96,337,315],{"class":214},[96,339,65],{"class":102},[96,341,342],{"class":201},"WithMargins",[96,344,235],{"class":102},[96,346,347],{"class":214},"document",[96,349,65],{"class":102},[96,351,352],{"class":201},"UniformEdges",[96,354,235],{"class":102},[96,356,347],{"class":214},[96,358,65],{"class":102},[96,360,361],{"class":201},"Mm",[96,363,235],{"class":102},[96,365,367],{"class":366},"sbssI","20",[96,369,370],{"class":102},"))),\n",[96,372,374,376,378,381,383,385,388,390,392,395],{"class":98,"line":373},21,[96,375,315],{"class":214},[96,377,65],{"class":102},[96,379,380],{"class":201},"WithFont",[96,382,235],{"class":102},[96,384,238],{"class":102},[96,386,387],{"class":241},"Inter",[96,389,238],{"class":102},[96,391,218],{"class":102},[96,393,394],{"class":214}," regular",[96,396,332],{"class":102},[96,398,400,402,404,407,409,411,413,415,417,420],{"class":98,"line":399},22,[96,401,315],{"class":214},[96,403,65],{"class":102},[96,405,406],{"class":201},"WithDefaultFont",[96,408,235],{"class":102},[96,410,238],{"class":102},[96,412,387],{"class":241},[96,414,238],{"class":102},[96,416,218],{"class":102},[96,418,419],{"class":366}," 11",[96,421,332],{"class":102},[96,423,425],{"class":98,"line":424},23,[96,426,427],{"class":102},"    )\n",[96,429,431],{"class":98,"line":430},24,[96,432,114],{"emptyLinePlaceholder":113},[96,434,436,439,441,444,446,449],{"class":98,"line":435},25,[96,437,438],{"class":214},"    page ",[96,440,224],{"class":102},[96,442,443],{"class":214}," doc",[96,445,65],{"class":102},[96,447,448],{"class":201},"AddPage",[96,450,451],{"class":102},"()\n",[96,453,455,458,460,463,466,470,473,476,478,481,484],{"class":98,"line":454},26,[96,456,457],{"class":214},"    page",[96,459,65],{"class":102},[96,461,462],{"class":201},"AutoRow",[96,464,465],{"class":102},"(func(",[96,467,469],{"class":468},"sHdIc","r",[96,471,472],{"class":102}," *",[96,474,475],{"class":106},"template",[96,477,65],{"class":102},[96,479,480],{"class":106},"RowBuilder",[96,482,483],{"class":102},")",[96,485,208],{"class":102},[96,487,489,492,494,497,499,502,504,507,510,512,514,516,519,521],{"class":98,"line":488},27,[96,490,491],{"class":214},"        r",[96,493,65],{"class":102},[96,495,496],{"class":201},"Col",[96,498,235],{"class":102},[96,500,501],{"class":366},"12",[96,503,218],{"class":102},[96,505,506],{"class":102}," func(",[96,508,509],{"class":468},"c",[96,511,472],{"class":102},[96,513,475],{"class":106},[96,515,65],{"class":102},[96,517,518],{"class":106},"ColBuilder",[96,520,483],{"class":102},[96,522,208],{"class":102},[96,524,526,529,531,534,536,538,541,543,545,548,550,553,555,558],{"class":98,"line":525},28,[96,527,528],{"class":214},"            c",[96,530,65],{"class":102},[96,532,533],{"class":201},"Text",[96,535,235],{"class":102},[96,537,238],{"class":102},[96,539,540],{"class":241},"Quarterly Report",[96,542,238],{"class":102},[96,544,218],{"class":102},[96,546,547],{"class":214}," template",[96,549,65],{"class":102},[96,551,552],{"class":201},"FontSize",[96,554,235],{"class":102},[96,556,557],{"class":366},"28",[96,559,560],{"class":102},"))\n",[96,562,564,566,568,570,572,574,577,579],{"class":98,"line":563},29,[96,565,528],{"class":214},[96,567,65],{"class":102},[96,569,533],{"class":201},[96,571,235],{"class":102},[96,573,238],{"class":102},[96,575,576],{"class":241},"Generated with gpdf and Inter.",[96,578,238],{"class":102},[96,580,187],{"class":102},[96,582,584],{"class":98,"line":583},30,[96,585,586],{"class":102},"        })\n",[96,588,590],{"class":98,"line":589},31,[96,591,592],{"class":102},"    })\n",[96,594,596],{"class":98,"line":595},32,[96,597,114],{"emptyLinePlaceholder":113},[96,599,601,604,606,608,610,612,614,617],{"class":98,"line":600},33,[96,602,603],{"class":214},"    data",[96,605,218],{"class":102},[96,607,221],{"class":214},[96,609,224],{"class":102},[96,611,443],{"class":214},[96,613,65],{"class":102},[96,615,616],{"class":201},"Generate",[96,618,451],{"class":102},[96,620,622,624,626,628,630],{"class":98,"line":621},34,[96,623,252],{"class":120},[96,625,221],{"class":214},[96,627,257],{"class":102},[96,629,260],{"class":102},[96,631,208],{"class":102},[96,633,635,637,639,641,643,645],{"class":98,"line":634},35,[96,636,268],{"class":214},[96,638,65],{"class":102},[96,640,273],{"class":201},[96,642,235],{"class":102},[96,644,278],{"class":214},[96,646,187],{"class":102},[96,648,650],{"class":98,"line":649},36,[96,651,286],{"class":102},[96,653,655,657,659,661,663,665,668,670,672,675,677,679,682,684,687,690,692,694,696],{"class":98,"line":654},37,[96,656,252],{"class":120},[96,658,221],{"class":214},[96,660,224],{"class":102},[96,662,227],{"class":214},[96,664,65],{"class":102},[96,666,667],{"class":201},"WriteFile",[96,669,235],{"class":102},[96,671,238],{"class":102},[96,673,674],{"class":241},"report.pdf",[96,676,238],{"class":102},[96,678,218],{"class":102},[96,680,681],{"class":214}," data",[96,683,218],{"class":102},[96,685,686],{"class":366}," 0o644",[96,688,689],{"class":102},");",[96,691,221],{"class":214},[96,693,257],{"class":102},[96,695,260],{"class":102},[96,697,208],{"class":102},[96,699,701,703,705,707,709,711],{"class":98,"line":700},38,[96,702,268],{"class":214},[96,704,65],{"class":102},[96,706,273],{"class":201},[96,708,235],{"class":102},[96,710,278],{"class":214},[96,712,187],{"class":102},[96,714,716],{"class":98,"line":715},39,[96,717,286],{"class":102},[96,719,721],{"class":98,"line":720},40,[96,722,723],{"class":102},"}\n",[19,725,726,727,729,730,733,734,739,740,743],{},"Drop ",[23,728,242],{}," next to ",[23,731,732],{},"main.go"," (download from ",[28,735,738],{"href":736,"rel":737},"https://rsms.me/inter/",[32],"rsms.me/inter","). ",[23,741,742],{},"go run main.go",". Done.",[14,745,747],{"id":746},"what-gpdf-does-with-the-bytes","What gpdf does with the bytes",[19,749,750,751,754,755,758,759,758,762,758,765,768,769,772,773,776,777,780],{},"When ",[23,752,753],{},"Generate()"," runs, gpdf parses the TrueType tables (",[23,756,757],{},"cmap",", ",[23,760,761],{},"glyf",[23,763,764],{},"loca",[23,766,767],{},"hmtx",", …) in pure Go — no FreeType, no CGO. It walks the rendered text, collects the code points actually used, and ",[70,770,771],{},"subsets the glyph table"," to that set. The PDF embeds a ",[23,774,775],{},"Type0"," / ",[23,778,779],{},"CIDFontType2"," font carrying only the glyphs you needed.",[19,782,783,784,786],{},"Practical effect: a 600 KB ",[23,785,242],{}," becomes roughly a 12 KB font subset inside the PDF if your document used a couple of paragraphs. The brand font lands without bloating the file.",[14,788,790],{"id":789},"bold-and-italic-need-their-own-files","Bold and italic need their own files",[19,792,793],{},"This is the part that bites people. gpdf does not synthesize bold or italic — there is no algorithmic \"make it bolder\" step. It looks up a variant ID built from the style flags:",[795,796,797,817],"table",{},[798,799,800],"thead",{},[801,802,803,809,814],"tr",{},[804,805,806],"th",{},[23,807,808],{},"Bold()",[804,810,811],{},[23,812,813],{},"Italic()",[804,815,816],{},"Lookup key",[818,819,820,832,844,855],"tbody",{},[801,821,822,826,828],{},[823,824,825],"td",{},"no",[823,827,825],{},[823,829,830],{},[23,831,387],{},[801,833,834,837,839],{},[823,835,836],{},"yes",[823,838,825],{},[823,840,841],{},[23,842,843],{},"Inter-Bold",[801,845,846,848,850],{},[823,847,825],{},[823,849,836],{},[823,851,852],{},[23,853,854],{},"Inter-Italic",[801,856,857,859,861],{},[823,858,836],{},[823,860,836],{},[823,862,863],{},[23,864,865],{},"Inter-BoldItalic",[19,867,868,869,871,872,874],{},"If you didn't register ",[23,870,843],{},", the lookup falls back to plain ",[23,873,387],{}," — silently. The PDF renders, but everything stays regular weight. There's no warning.",[19,876,877],{},"Register all four:",[87,879,881],{"className":89,"code":880,"language":91,"meta":92,"style":92},"regular, _    := os.ReadFile(\"Inter-Regular.ttf\")\nbold, _       := os.ReadFile(\"Inter-Bold.ttf\")\nitalic, _     := os.ReadFile(\"Inter-Italic.ttf\")\nboldItalic, _ := os.ReadFile(\"Inter-BoldItalic.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"Inter\", regular),\n    gpdf.WithFont(\"Inter-Bold\", bold),\n    gpdf.WithFont(\"Inter-Italic\", italic),\n    gpdf.WithFont(\"Inter-BoldItalic\", boldItalic),\n    gpdf.WithDefaultFont(\"Inter\", 11),\n)\n",[23,882,883,911,940,969,998,1002,1017,1040,1063,1086,1109,1131],{"__ignoreMap":92},[96,884,885,888,890,893,895,897,899,901,903,905,907,909],{"class":98,"line":99},[96,886,887],{"class":214},"regular",[96,889,218],{"class":102},[96,891,892],{"class":214}," _    ",[96,894,224],{"class":102},[96,896,227],{"class":214},[96,898,65],{"class":102},[96,900,232],{"class":201},[96,902,235],{"class":102},[96,904,238],{"class":102},[96,906,242],{"class":241},[96,908,238],{"class":102},[96,910,187],{"class":102},[96,912,913,916,918,921,923,925,927,929,931,933,936,938],{"class":98,"line":110},[96,914,915],{"class":214},"bold",[96,917,218],{"class":102},[96,919,920],{"class":214}," _       ",[96,922,224],{"class":102},[96,924,227],{"class":214},[96,926,65],{"class":102},[96,928,232],{"class":201},[96,930,235],{"class":102},[96,932,238],{"class":102},[96,934,935],{"class":241},"Inter-Bold.ttf",[96,937,238],{"class":102},[96,939,187],{"class":102},[96,941,942,945,947,950,952,954,956,958,960,962,965,967],{"class":98,"line":117},[96,943,944],{"class":214},"italic",[96,946,218],{"class":102},[96,948,949],{"class":214}," _     ",[96,951,224],{"class":102},[96,953,227],{"class":214},[96,955,65],{"class":102},[96,957,232],{"class":201},[96,959,235],{"class":102},[96,961,238],{"class":102},[96,963,964],{"class":241},"Inter-Italic.ttf",[96,966,238],{"class":102},[96,968,187],{"class":102},[96,970,971,974,976,979,981,983,985,987,989,991,994,996],{"class":98,"line":127},[96,972,973],{"class":214},"boldItalic",[96,975,218],{"class":102},[96,977,978],{"class":214}," _ ",[96,980,224],{"class":102},[96,982,227],{"class":214},[96,984,65],{"class":102},[96,986,232],{"class":201},[96,988,235],{"class":102},[96,990,238],{"class":102},[96,992,993],{"class":241},"Inter-BoldItalic.ttf",[96,995,238],{"class":102},[96,997,187],{"class":102},[96,999,1000],{"class":98,"line":139},[96,1001,114],{"emptyLinePlaceholder":113},[96,1003,1004,1007,1009,1011,1013,1015],{"class":98,"line":149},[96,1005,1006],{"class":214},"doc ",[96,1008,224],{"class":102},[96,1010,302],{"class":214},[96,1012,65],{"class":102},[96,1014,52],{"class":201},[96,1016,309],{"class":102},[96,1018,1019,1022,1024,1026,1028,1030,1032,1034,1036,1038],{"class":98,"line":154},[96,1020,1021],{"class":214},"    gpdf",[96,1023,65],{"class":102},[96,1025,380],{"class":201},[96,1027,235],{"class":102},[96,1029,238],{"class":102},[96,1031,387],{"class":241},[96,1033,238],{"class":102},[96,1035,218],{"class":102},[96,1037,394],{"class":214},[96,1039,332],{"class":102},[96,1041,1042,1044,1046,1048,1050,1052,1054,1056,1058,1061],{"class":98,"line":164},[96,1043,1021],{"class":214},[96,1045,65],{"class":102},[96,1047,380],{"class":201},[96,1049,235],{"class":102},[96,1051,238],{"class":102},[96,1053,843],{"class":241},[96,1055,238],{"class":102},[96,1057,218],{"class":102},[96,1059,1060],{"class":214}," bold",[96,1062,332],{"class":102},[96,1064,1065,1067,1069,1071,1073,1075,1077,1079,1081,1084],{"class":98,"line":174},[96,1066,1021],{"class":214},[96,1068,65],{"class":102},[96,1070,380],{"class":201},[96,1072,235],{"class":102},[96,1074,238],{"class":102},[96,1076,854],{"class":241},[96,1078,238],{"class":102},[96,1080,218],{"class":102},[96,1082,1083],{"class":214}," italic",[96,1085,332],{"class":102},[96,1087,1088,1090,1092,1094,1096,1098,1100,1102,1104,1107],{"class":98,"line":184},[96,1089,1021],{"class":214},[96,1091,65],{"class":102},[96,1093,380],{"class":201},[96,1095,235],{"class":102},[96,1097,238],{"class":102},[96,1099,865],{"class":241},[96,1101,238],{"class":102},[96,1103,218],{"class":102},[96,1105,1106],{"class":214}," boldItalic",[96,1108,332],{"class":102},[96,1110,1111,1113,1115,1117,1119,1121,1123,1125,1127,1129],{"class":98,"line":190},[96,1112,1021],{"class":214},[96,1114,65],{"class":102},[96,1116,406],{"class":201},[96,1118,235],{"class":102},[96,1120,238],{"class":102},[96,1122,387],{"class":241},[96,1124,238],{"class":102},[96,1126,218],{"class":102},[96,1128,419],{"class":366},[96,1130,332],{"class":102},[96,1132,1133],{"class":98,"line":195},[96,1134,187],{"class":102},[19,1136,1137,1138,1141,1142,1145],{},"If a font ships only one weight (lots of icon and display fonts do), don't call ",[23,1139,1140],{},"template.Bold()"," or ",[23,1143,1144],{},"template.Italic()"," for those at all. Skipping a variant is fine. Falling back to the wrong variant is what produces \"why is the bold not bold\" bug reports.",[14,1147,1149],{"id":1148},"embed-the-font-in-the-binary","Embed the font in the binary",[19,1151,1152,1155],{},[23,1153,1154],{},"os.ReadFile"," at startup works in development. In production the font is part of the program — it should travel inside the binary:",[87,1157,1159],{"className":89,"code":1158,"language":91,"meta":92,"style":92},"import _ \"embed\"\n\n//go:embed fonts/Inter-Regular.ttf\nvar interRegular []byte\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"Inter\", interRegular),\n)\n",[23,1160,1161,1174,1178,1184,1199,1203,1217,1240],{"__ignoreMap":92},[96,1162,1163,1165,1167,1169,1172],{"class":98,"line":99},[96,1164,121],{"class":120},[96,1166,978],{"class":214},[96,1168,238],{"class":102},[96,1170,1171],{"class":106},"embed",[96,1173,136],{"class":102},[96,1175,1176],{"class":98,"line":110},[96,1177,114],{"emptyLinePlaceholder":113},[96,1179,1180],{"class":98,"line":117},[96,1181,1183],{"class":1182},"sHwdD","//go:embed fonts/Inter-Regular.ttf\n",[96,1185,1186,1189,1192,1195],{"class":98,"line":127},[96,1187,1188],{"class":102},"var",[96,1190,1191],{"class":214}," interRegular ",[96,1193,1194],{"class":102},"[]",[96,1196,1198],{"class":1197},"spNyl","byte\n",[96,1200,1201],{"class":98,"line":139},[96,1202,114],{"emptyLinePlaceholder":113},[96,1204,1205,1207,1209,1211,1213,1215],{"class":98,"line":149},[96,1206,1006],{"class":214},[96,1208,224],{"class":102},[96,1210,302],{"class":214},[96,1212,65],{"class":102},[96,1214,52],{"class":201},[96,1216,309],{"class":102},[96,1218,1219,1221,1223,1225,1227,1229,1231,1233,1235,1238],{"class":98,"line":154},[96,1220,1021],{"class":214},[96,1222,65],{"class":102},[96,1224,380],{"class":201},[96,1226,235],{"class":102},[96,1228,238],{"class":102},[96,1230,387],{"class":241},[96,1232,238],{"class":102},[96,1234,218],{"class":102},[96,1236,1237],{"class":214}," interRegular",[96,1239,332],{"class":102},[96,1241,1242],{"class":98,"line":164},[96,1243,187],{"class":102},[19,1245,1246,1249],{},[23,1247,1248],{},"go build"," bakes the bytes in. No more \"where is the .ttf in the deploy image\" debugging on a Friday afternoon.",[14,1251,1253],{"id":1252},"icon-fonts-work-the-same-way","Icon fonts work the same way",[19,1255,1256],{},"Font Awesome, Material Symbols exported as TTF, IcoMoon, custom brand glyph sets — they're all just TrueType files. Register them the same way:",[87,1258,1260],{"className":89,"code":1259,"language":91,"meta":92,"style":92},"icons, _ := os.ReadFile(\"MaterialSymbols-Regular.ttf\")\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"Icons\", icons),\n    gpdf.WithDefaultFont(\"Inter\", 11), // body text default\n)\n\n// In a column:\nc.Text(\"\", template.FontFamily(\"Icons\"), template.FontSize(20)) // \"home\" icon\n",[23,1261,1262,1290,1304,1328,1354,1358,1362,1367],{"__ignoreMap":92},[96,1263,1264,1267,1269,1271,1273,1275,1277,1279,1281,1283,1286,1288],{"class":98,"line":99},[96,1265,1266],{"class":214},"icons",[96,1268,218],{"class":102},[96,1270,978],{"class":214},[96,1272,224],{"class":102},[96,1274,227],{"class":214},[96,1276,65],{"class":102},[96,1278,232],{"class":201},[96,1280,235],{"class":102},[96,1282,238],{"class":102},[96,1284,1285],{"class":241},"MaterialSymbols-Regular.ttf",[96,1287,238],{"class":102},[96,1289,187],{"class":102},[96,1291,1292,1294,1296,1298,1300,1302],{"class":98,"line":110},[96,1293,1006],{"class":214},[96,1295,224],{"class":102},[96,1297,302],{"class":214},[96,1299,65],{"class":102},[96,1301,52],{"class":201},[96,1303,309],{"class":102},[96,1305,1306,1308,1310,1312,1314,1316,1319,1321,1323,1326],{"class":98,"line":117},[96,1307,1021],{"class":214},[96,1309,65],{"class":102},[96,1311,380],{"class":201},[96,1313,235],{"class":102},[96,1315,238],{"class":102},[96,1317,1318],{"class":241},"Icons",[96,1320,238],{"class":102},[96,1322,218],{"class":102},[96,1324,1325],{"class":214}," icons",[96,1327,332],{"class":102},[96,1329,1330,1332,1334,1336,1338,1340,1342,1344,1346,1348,1351],{"class":98,"line":127},[96,1331,1021],{"class":214},[96,1333,65],{"class":102},[96,1335,406],{"class":201},[96,1337,235],{"class":102},[96,1339,238],{"class":102},[96,1341,387],{"class":241},[96,1343,238],{"class":102},[96,1345,218],{"class":102},[96,1347,419],{"class":366},[96,1349,1350],{"class":102},"),",[96,1352,1353],{"class":1182}," // body text default\n",[96,1355,1356],{"class":98,"line":139},[96,1357,187],{"class":102},[96,1359,1360],{"class":98,"line":149},[96,1361,114],{"emptyLinePlaceholder":113},[96,1363,1364],{"class":98,"line":154},[96,1365,1366],{"class":1182},"// In a column:\n",[96,1368,1369,1371,1373,1375,1377,1379,1382,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1415],{"class":98,"line":164},[96,1370,509],{"class":214},[96,1372,65],{"class":102},[96,1374,533],{"class":201},[96,1376,235],{"class":102},[96,1378,238],{"class":102},[96,1380,1381],{"class":241},"",[96,1383,238],{"class":102},[96,1385,218],{"class":102},[96,1387,547],{"class":214},[96,1389,65],{"class":102},[96,1391,80],{"class":201},[96,1393,235],{"class":102},[96,1395,238],{"class":102},[96,1397,1318],{"class":241},[96,1399,238],{"class":102},[96,1401,1350],{"class":102},[96,1403,547],{"class":214},[96,1405,65],{"class":102},[96,1407,552],{"class":201},[96,1409,235],{"class":102},[96,1411,367],{"class":366},[96,1413,1414],{"class":102},"))",[96,1416,1417],{"class":1182}," // \"home\" icon\n",[19,1419,1420],{},"The Unicode escape is whatever the font's documentation says it is. gpdf doesn't care that the glyph is an icon — to it, it's a code point, and it subsets the same way it does for letters.",[14,1422,1424],{"id":1423},"common-mistakes","Common mistakes",[1426,1427,1428,1439,1452],"ul",{},[1429,1430,1431,1434,1435,1438],"li",{},[70,1432,1433],{},"Family name typo at the call site."," ",[23,1436,1437],{},"template.FontFamily(\"Intr\")"," falls back to the document default. No error, no warning. If text suddenly looks like Helvetica, this is the first place to look.",[1429,1440,1441,1448,1449,1451],{},[70,1442,1443,1444,1447],{},"Not embedding via ",[23,1445,1446],{},"//go:embed"," in containers."," A trimmed Docker context drops the ",[23,1450,25],{},", the runtime fallback kicks in, and you find out from a customer email. Embed.",[1429,1453,1454,1457,1458,1460,1461,1464],{},[70,1455,1456],{},"Using the font's PostScript name as the family."," \"Inter-Regular\" is the PostScript name. Pass that to ",[23,1459,380],{}," and the bold lookup tries to find \"Inter-Regular-Bold\" — which doesn't exist. Pick a clean family root (",[23,1462,1463],{},"\"Inter\"",") and let the variant suffix handle the styles.",[14,1466,1468],{"id":1467},"related-recipes","Related recipes",[1426,1470,1471,1478,1485],{},[1429,1472,1473,1477],{},[28,1474,1476],{"href":1475},"/blog/embed-japanese-font","How do I embed a Japanese font in gpdf?"," — same mechanism, with CJK-specific notes.",[1429,1479,1480,1484],{},[28,1481,1483],{"href":1482},"/blog/bold-italic-together","How do I use bold and italic together?"," — the variant resolver in detail.",[1429,1486,1487,1491],{},[28,1488,1490],{"href":1489},"/blog/tofu-boxes-japanese","Why does my PDF show tofu boxes?"," — what happens when the font doesn't cover the code points.",[14,1493,1495],{"id":1494},"try-gpdf","Try gpdf",[19,1497,1498],{},"gpdf is a Go library for generating PDFs. MIT licensed, zero external dependencies, pure-Go TrueType handling.",[87,1500,1504],{"className":1501,"code":1502,"language":1503,"meta":92,"style":92},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[23,1505,1506],{"__ignoreMap":92},[96,1507,1508,1510,1513],{"class":98,"line":99},[96,1509,91],{"class":106},[96,1511,1512],{"class":241}," get",[96,1514,1515],{"class":241}," github.com/gpdf-dev/gpdf\n",[19,1517,1518,1522,1523],{},[28,1519,1521],{"href":30,"rel":1520},[32],"⭐ Star on GitHub"," · ",[28,1524,1527],{"href":1525,"rel":1526},"https://gpdf.dev/docs/quickstart",[32],"Read the docs",[1529,1530,1531],"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":92,"searchDepth":110,"depth":110,"links":1533},[1534,1535,1536,1537,1538,1539,1540,1541,1542,1543],{"id":16,"depth":110,"text":17},{"id":41,"depth":110,"text":42},{"id":84,"depth":110,"text":85},{"id":746,"depth":110,"text":747},{"id":789,"depth":110,"text":790},{"id":1148,"depth":110,"text":1149},{"id":1252,"depth":110,"text":1253},{"id":1423,"depth":110,"text":1424},{"id":1467,"depth":110,"text":1468},{"id":1494,"depth":110,"text":1495},"2026-04-30","Load TTF bytes, register with gpdf.WithFont, then reference the family name. Works for any TrueType — Inter, Roboto, icon fonts, brand fonts.",false,"md",{"name":1549,"totalTime":1550,"tools":1551,"steps":1554},"Register a custom TrueType font in a gpdf document","PT10M",[1552,1553],"Go 1.22+","Any TrueType .ttf file (Inter, Roboto, JetBrains Mono, an icon font, etc.)",[1555,1558,1561,1564],{"name":1556,"text":1557},"Load the TTF bytes","Read the .ttf file with os.ReadFile into a []byte. For deployable builds, prefer //go:embed so the font travels inside the binary.",{"name":1559,"text":1560},"Register the family at document construction","Pass gpdf.WithFont(\"Inter\", fontBytes) to gpdf.NewDocument. The family name is whatever string you choose — it has no relation to the font's internal name.",{"name":1562,"text":1563},"Set the family as the default, or pass it per call","Add gpdf.WithDefaultFont(\"Inter\", 11) so every c.Text uses it. Otherwise pass template.FontFamily(\"Inter\") on each Text call that needs it.",{"name":1565,"text":1566},"Register Bold, Italic, and BoldItalic variants separately","Each style is its own TTF. Register them as \"Inter-Bold\", \"Inter-Italic\", \"Inter-BoldItalic\". gpdf builds the variant ID from the style flags and looks up that exact string.",null,{},"/blog/add-custom-truetype-font",{"title":5,"description":1545},"blog/018.add-custom-truetype-font",[1573,1574],"recipe","tutorial","j12k87D6aG8KPMN43KnArhS-YHkbVgYkzJjeBWKn2Xk",1779199010185]