[{"data":1,"prerenderedAt":1574},["ShallowReactive",2],{"blog-zh-add-custom-truetype-font":3},{"id":4,"title":5,"author":6,"body":10,"date":1542,"description":1543,"draft":1544,"extension":1545,"howTo":1546,"image":1565,"meta":1566,"navigation":110,"path":1567,"seo":1568,"stem":1569,"tags":1570,"updated":1565,"__hash__":1573},"blogZh/zh/blog/018.add-custom-truetype-font.md","如何在 gpdf 中添加自定义 TrueType 字体？",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/en/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":1530},"minimark",[13,17,38,41,64,80,83,722,742,746,778,784,788,795,867,880,883,1140,1151,1154,1161,1249,1255,1258,1261,1418,1421,1424,1464,1467,1490,1494,1497,1514,1526],[14,15,16],"h2",{"id":16},"换个说法",[18,19,20,21,25,26,33,34,37],"p",{},"我有一个 ",[22,23,24],"code",{},".ttf"," 文件 — 品牌用的 Inter、代码块用的 JetBrains Mono、图标用的字体。怎么把它放进 ",[27,28,32],"a",{"href":29,"rel":30},"https://github.com/gpdf-dev/gpdf",[31],"nofollow","gpdf"," 文档，并在 ",[22,35,36],{},"c.Text(...)"," 里按名字引用?",[14,39,40],{"id":40},"速答",[18,42,43,44,47,48,51,52,55,56,59,60,63],{},"读取 TTF 字节。把 ",[22,45,46],{},"gpdf.WithFont(\"你的字族名\", bytes)"," 传给 ",[22,49,50],{},"NewDocument","。然后用 ",[22,53,54],{},"template.FontFamily(...)"," 引用 ",[22,57,58],{},"\"你的字族名\"","，或用 ",[22,61,62],{},"gpdf.WithDefaultFont"," 设为默认。",[18,65,66,67,71,72,75,76,79],{},"字族名是 ",[68,69,70],"strong",{},"任意字符串","。它和字体内部的 ",[22,73,74],{},"name"," 表毫无关系 — 只是 gpdf 解析 ",[22,77,78],{},"FontFamily"," 选项时使用的查找 key。取个短的。",[14,81,82],{"id":82},"可运行代码",[84,85,90],"pre",{"className":86,"code":87,"language":88,"meta":89,"style":89},"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","",[22,91,92,105,112,122,134,144,149,159,169,179,185,190,206,245,261,279,285,290,308,331,369,395,420,426,431,450,484,521,559,579,585,591,596,617,630,645,650,696,711,716],{"__ignoreMap":89},[93,94,97,101],"span",{"class":95,"line":96},"line",1,[93,98,100],{"class":99},"sMK4o","package",[93,102,104],{"class":103},"sBMFI"," main\n",[93,106,108],{"class":95,"line":107},2,[93,109,111],{"emptyLinePlaceholder":110},true,"\n",[93,113,115,119],{"class":95,"line":114},3,[93,116,118],{"class":117},"s7zQu","import",[93,120,121],{"class":99}," (\n",[93,123,125,128,131],{"class":95,"line":124},4,[93,126,127],{"class":99},"    \"",[93,129,130],{"class":103},"log",[93,132,133],{"class":99},"\"\n",[93,135,137,139,142],{"class":95,"line":136},5,[93,138,127],{"class":99},[93,140,141],{"class":103},"os",[93,143,133],{"class":99},[93,145,147],{"class":95,"line":146},6,[93,148,111],{"emptyLinePlaceholder":110},[93,150,152,154,157],{"class":95,"line":151},7,[93,153,127],{"class":99},[93,155,156],{"class":103},"github.com/gpdf-dev/gpdf",[93,158,133],{"class":99},[93,160,162,164,167],{"class":95,"line":161},8,[93,163,127],{"class":99},[93,165,166],{"class":103},"github.com/gpdf-dev/gpdf/document",[93,168,133],{"class":99},[93,170,172,174,177],{"class":95,"line":171},9,[93,173,127],{"class":99},[93,175,176],{"class":103},"github.com/gpdf-dev/gpdf/template",[93,178,133],{"class":99},[93,180,182],{"class":95,"line":181},10,[93,183,184],{"class":99},")\n",[93,186,188],{"class":95,"line":187},11,[93,189,111],{"emptyLinePlaceholder":110},[93,191,193,196,200,203],{"class":95,"line":192},12,[93,194,195],{"class":99},"func",[93,197,199],{"class":198},"s2Zo4"," main",[93,201,202],{"class":99},"()",[93,204,205],{"class":99}," {\n",[93,207,209,213,216,219,222,225,228,231,234,237,241,243],{"class":95,"line":208},13,[93,210,212],{"class":211},"sTEyZ","    regular",[93,214,215],{"class":99},",",[93,217,218],{"class":211}," err ",[93,220,221],{"class":99},":=",[93,223,224],{"class":211}," os",[93,226,227],{"class":99},".",[93,229,230],{"class":198},"ReadFile",[93,232,233],{"class":99},"(",[93,235,236],{"class":99},"\"",[93,238,240],{"class":239},"sfazB","Inter-Regular.ttf",[93,242,236],{"class":99},[93,244,184],{"class":99},[93,246,248,251,253,256,259],{"class":95,"line":247},14,[93,249,250],{"class":117},"    if",[93,252,218],{"class":211},[93,254,255],{"class":99},"!=",[93,257,258],{"class":99}," nil",[93,260,205],{"class":99},[93,262,264,267,269,272,274,277],{"class":95,"line":263},15,[93,265,266],{"class":211},"        log",[93,268,227],{"class":99},[93,270,271],{"class":198},"Fatal",[93,273,233],{"class":99},[93,275,276],{"class":211},"err",[93,278,184],{"class":99},[93,280,282],{"class":95,"line":281},16,[93,283,284],{"class":99},"    }\n",[93,286,288],{"class":95,"line":287},17,[93,289,111],{"emptyLinePlaceholder":110},[93,291,293,296,298,301,303,305],{"class":95,"line":292},18,[93,294,295],{"class":211},"    doc ",[93,297,221],{"class":99},[93,299,300],{"class":211}," gpdf",[93,302,227],{"class":99},[93,304,50],{"class":198},[93,306,307],{"class":99},"(\n",[93,309,311,314,316,319,321,323,325,328],{"class":95,"line":310},19,[93,312,313],{"class":211},"        gpdf",[93,315,227],{"class":99},[93,317,318],{"class":198},"WithPageSize",[93,320,233],{"class":99},[93,322,32],{"class":211},[93,324,227],{"class":99},[93,326,327],{"class":211},"A4",[93,329,330],{"class":99},"),\n",[93,332,334,336,338,341,343,346,348,351,353,355,357,360,362,366],{"class":95,"line":333},20,[93,335,313],{"class":211},[93,337,227],{"class":99},[93,339,340],{"class":198},"WithMargins",[93,342,233],{"class":99},[93,344,345],{"class":211},"document",[93,347,227],{"class":99},[93,349,350],{"class":198},"UniformEdges",[93,352,233],{"class":99},[93,354,345],{"class":211},[93,356,227],{"class":99},[93,358,359],{"class":198},"Mm",[93,361,233],{"class":99},[93,363,365],{"class":364},"sbssI","20",[93,367,368],{"class":99},"))),\n",[93,370,372,374,376,379,381,383,386,388,390,393],{"class":95,"line":371},21,[93,373,313],{"class":211},[93,375,227],{"class":99},[93,377,378],{"class":198},"WithFont",[93,380,233],{"class":99},[93,382,236],{"class":99},[93,384,385],{"class":239},"Inter",[93,387,236],{"class":99},[93,389,215],{"class":99},[93,391,392],{"class":211}," regular",[93,394,330],{"class":99},[93,396,398,400,402,405,407,409,411,413,415,418],{"class":95,"line":397},22,[93,399,313],{"class":211},[93,401,227],{"class":99},[93,403,404],{"class":198},"WithDefaultFont",[93,406,233],{"class":99},[93,408,236],{"class":99},[93,410,385],{"class":239},[93,412,236],{"class":99},[93,414,215],{"class":99},[93,416,417],{"class":364}," 11",[93,419,330],{"class":99},[93,421,423],{"class":95,"line":422},23,[93,424,425],{"class":99},"    )\n",[93,427,429],{"class":95,"line":428},24,[93,430,111],{"emptyLinePlaceholder":110},[93,432,434,437,439,442,444,447],{"class":95,"line":433},25,[93,435,436],{"class":211},"    page ",[93,438,221],{"class":99},[93,440,441],{"class":211}," doc",[93,443,227],{"class":99},[93,445,446],{"class":198},"AddPage",[93,448,449],{"class":99},"()\n",[93,451,453,456,458,461,464,468,471,474,476,479,482],{"class":95,"line":452},26,[93,454,455],{"class":211},"    page",[93,457,227],{"class":99},[93,459,460],{"class":198},"AutoRow",[93,462,463],{"class":99},"(func(",[93,465,467],{"class":466},"sHdIc","r",[93,469,470],{"class":99}," *",[93,472,473],{"class":103},"template",[93,475,227],{"class":99},[93,477,478],{"class":103},"RowBuilder",[93,480,481],{"class":99},")",[93,483,205],{"class":99},[93,485,487,490,492,495,497,500,502,505,508,510,512,514,517,519],{"class":95,"line":486},27,[93,488,489],{"class":211},"        r",[93,491,227],{"class":99},[93,493,494],{"class":198},"Col",[93,496,233],{"class":99},[93,498,499],{"class":364},"12",[93,501,215],{"class":99},[93,503,504],{"class":99}," func(",[93,506,507],{"class":466},"c",[93,509,470],{"class":99},[93,511,473],{"class":103},[93,513,227],{"class":99},[93,515,516],{"class":103},"ColBuilder",[93,518,481],{"class":99},[93,520,205],{"class":99},[93,522,524,527,529,532,534,536,539,541,543,546,548,551,553,556],{"class":95,"line":523},28,[93,525,526],{"class":211},"            c",[93,528,227],{"class":99},[93,530,531],{"class":198},"Text",[93,533,233],{"class":99},[93,535,236],{"class":99},[93,537,538],{"class":239},"Quarterly Report",[93,540,236],{"class":99},[93,542,215],{"class":99},[93,544,545],{"class":211}," template",[93,547,227],{"class":99},[93,549,550],{"class":198},"FontSize",[93,552,233],{"class":99},[93,554,555],{"class":364},"28",[93,557,558],{"class":99},"))\n",[93,560,562,564,566,568,570,572,575,577],{"class":95,"line":561},29,[93,563,526],{"class":211},[93,565,227],{"class":99},[93,567,531],{"class":198},[93,569,233],{"class":99},[93,571,236],{"class":99},[93,573,574],{"class":239},"Generated with gpdf and Inter.",[93,576,236],{"class":99},[93,578,184],{"class":99},[93,580,582],{"class":95,"line":581},30,[93,583,584],{"class":99},"        })\n",[93,586,588],{"class":95,"line":587},31,[93,589,590],{"class":99},"    })\n",[93,592,594],{"class":95,"line":593},32,[93,595,111],{"emptyLinePlaceholder":110},[93,597,599,602,604,606,608,610,612,615],{"class":95,"line":598},33,[93,600,601],{"class":211},"    data",[93,603,215],{"class":99},[93,605,218],{"class":211},[93,607,221],{"class":99},[93,609,441],{"class":211},[93,611,227],{"class":99},[93,613,614],{"class":198},"Generate",[93,616,449],{"class":99},[93,618,620,622,624,626,628],{"class":95,"line":619},34,[93,621,250],{"class":117},[93,623,218],{"class":211},[93,625,255],{"class":99},[93,627,258],{"class":99},[93,629,205],{"class":99},[93,631,633,635,637,639,641,643],{"class":95,"line":632},35,[93,634,266],{"class":211},[93,636,227],{"class":99},[93,638,271],{"class":198},[93,640,233],{"class":99},[93,642,276],{"class":211},[93,644,184],{"class":99},[93,646,648],{"class":95,"line":647},36,[93,649,284],{"class":99},[93,651,653,655,657,659,661,663,666,668,670,673,675,677,680,682,685,688,690,692,694],{"class":95,"line":652},37,[93,654,250],{"class":117},[93,656,218],{"class":211},[93,658,221],{"class":99},[93,660,224],{"class":211},[93,662,227],{"class":99},[93,664,665],{"class":198},"WriteFile",[93,667,233],{"class":99},[93,669,236],{"class":99},[93,671,672],{"class":239},"report.pdf",[93,674,236],{"class":99},[93,676,215],{"class":99},[93,678,679],{"class":211}," data",[93,681,215],{"class":99},[93,683,684],{"class":364}," 0o644",[93,686,687],{"class":99},");",[93,689,218],{"class":211},[93,691,255],{"class":99},[93,693,258],{"class":99},[93,695,205],{"class":99},[93,697,699,701,703,705,707,709],{"class":95,"line":698},38,[93,700,266],{"class":211},[93,702,227],{"class":99},[93,704,271],{"class":198},[93,706,233],{"class":99},[93,708,276],{"class":211},[93,710,184],{"class":99},[93,712,714],{"class":95,"line":713},39,[93,715,284],{"class":99},[93,717,719],{"class":95,"line":718},40,[93,720,721],{"class":99},"}\n",[18,723,724,725,727,728,731,732,737,738,741],{},"把 ",[22,726,240],{}," 放在 ",[22,729,730],{},"main.go"," 旁边 (从 ",[27,733,736],{"href":734,"rel":735},"https://rsms.me/inter/",[31],"rsms.me/inter"," 下载)。",[22,739,740],{},"go run main.go","。完事。",[14,743,745],{"id":744},"gpdf-对字节做了什么","gpdf 对字节做了什么",[18,747,748,751,752,755,756,755,759,755,762,765,766,769,770,773,774,777],{},[22,749,750],{},"Generate()"," 触发后，gpdf 用纯 Go 解析 TrueType 表 (",[22,753,754],{},"cmap",", ",[22,757,758],{},"glyf",[22,760,761],{},"loca",[22,763,764],{},"hmtx"," …) — 不依赖 FreeType，不需要 CGO。它扫描渲染文本，收集实际用到的码点，然后",[68,767,768],{},"对字形表做子集化","，只保留这些码点所需的字形。PDF 内嵌入一个 ",[22,771,772],{},"Type0"," / ",[22,775,776],{},"CIDFontType2"," 字体，仅承载真正需要的字形。",[18,779,780,781,783],{},"实际效果: 600 KB 的 ",[22,782,240],{},"，如果文档只用了几段，PDF 内的字体子集大约只有 12 KB。能用上品牌字体而不让文件变大。",[14,785,787],{"id":786},"bold-和-italic-需要各自的文件","Bold 和 Italic 需要各自的文件",[18,789,790,791,794],{},"这一点最容易踩坑。gpdf ",[68,792,793],{},"不会合成 bold 或 italic"," — 没有「让笔画变粗」这种算法。它根据样式标志拼接出变体 ID 来查找:",[796,797,798,818],"table",{},[799,800,801],"thead",{},[802,803,804,810,815],"tr",{},[805,806,807],"th",{},[22,808,809],{},"Bold()",[805,811,812],{},[22,813,814],{},"Italic()",[805,816,817],{},"查找 key",[819,820,821,833,845,856],"tbody",{},[802,822,823,827,829],{},[824,825,826],"td",{},"否",[824,828,826],{},[824,830,831],{},[22,832,385],{},[802,834,835,838,840],{},[824,836,837],{},"是",[824,839,826],{},[824,841,842],{},[22,843,844],{},"Inter-Bold",[802,846,847,849,851],{},[824,848,826],{},[824,850,837],{},[824,852,853],{},[22,854,855],{},"Inter-Italic",[802,857,858,860,862],{},[824,859,837],{},[824,861,837],{},[824,863,864],{},[22,865,866],{},"Inter-BoldItalic",[18,868,869,870,872,873,876,877,879],{},"如果没注册 ",[22,871,844],{},"，查找会",[68,874,875],{},"静默回退","到普通的 ",[22,878,385],{},"。PDF 仍然会生成，但所有「应该是粗体」的部分都是常规字重。没有任何警告。",[18,881,882],{},"四个变体一起注册:",[84,884,886],{"className":86,"code":885,"language":88,"meta":89,"style":89},"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",[22,887,888,916,945,974,1003,1007,1022,1045,1068,1091,1114,1136],{"__ignoreMap":89},[93,889,890,893,895,898,900,902,904,906,908,910,912,914],{"class":95,"line":96},[93,891,892],{"class":211},"regular",[93,894,215],{"class":99},[93,896,897],{"class":211}," _    ",[93,899,221],{"class":99},[93,901,224],{"class":211},[93,903,227],{"class":99},[93,905,230],{"class":198},[93,907,233],{"class":99},[93,909,236],{"class":99},[93,911,240],{"class":239},[93,913,236],{"class":99},[93,915,184],{"class":99},[93,917,918,921,923,926,928,930,932,934,936,938,941,943],{"class":95,"line":107},[93,919,920],{"class":211},"bold",[93,922,215],{"class":99},[93,924,925],{"class":211}," _       ",[93,927,221],{"class":99},[93,929,224],{"class":211},[93,931,227],{"class":99},[93,933,230],{"class":198},[93,935,233],{"class":99},[93,937,236],{"class":99},[93,939,940],{"class":239},"Inter-Bold.ttf",[93,942,236],{"class":99},[93,944,184],{"class":99},[93,946,947,950,952,955,957,959,961,963,965,967,970,972],{"class":95,"line":114},[93,948,949],{"class":211},"italic",[93,951,215],{"class":99},[93,953,954],{"class":211}," _     ",[93,956,221],{"class":99},[93,958,224],{"class":211},[93,960,227],{"class":99},[93,962,230],{"class":198},[93,964,233],{"class":99},[93,966,236],{"class":99},[93,968,969],{"class":239},"Inter-Italic.ttf",[93,971,236],{"class":99},[93,973,184],{"class":99},[93,975,976,979,981,984,986,988,990,992,994,996,999,1001],{"class":95,"line":124},[93,977,978],{"class":211},"boldItalic",[93,980,215],{"class":99},[93,982,983],{"class":211}," _ ",[93,985,221],{"class":99},[93,987,224],{"class":211},[93,989,227],{"class":99},[93,991,230],{"class":198},[93,993,233],{"class":99},[93,995,236],{"class":99},[93,997,998],{"class":239},"Inter-BoldItalic.ttf",[93,1000,236],{"class":99},[93,1002,184],{"class":99},[93,1004,1005],{"class":95,"line":136},[93,1006,111],{"emptyLinePlaceholder":110},[93,1008,1009,1012,1014,1016,1018,1020],{"class":95,"line":146},[93,1010,1011],{"class":211},"doc ",[93,1013,221],{"class":99},[93,1015,300],{"class":211},[93,1017,227],{"class":99},[93,1019,50],{"class":198},[93,1021,307],{"class":99},[93,1023,1024,1027,1029,1031,1033,1035,1037,1039,1041,1043],{"class":95,"line":151},[93,1025,1026],{"class":211},"    gpdf",[93,1028,227],{"class":99},[93,1030,378],{"class":198},[93,1032,233],{"class":99},[93,1034,236],{"class":99},[93,1036,385],{"class":239},[93,1038,236],{"class":99},[93,1040,215],{"class":99},[93,1042,392],{"class":211},[93,1044,330],{"class":99},[93,1046,1047,1049,1051,1053,1055,1057,1059,1061,1063,1066],{"class":95,"line":161},[93,1048,1026],{"class":211},[93,1050,227],{"class":99},[93,1052,378],{"class":198},[93,1054,233],{"class":99},[93,1056,236],{"class":99},[93,1058,844],{"class":239},[93,1060,236],{"class":99},[93,1062,215],{"class":99},[93,1064,1065],{"class":211}," bold",[93,1067,330],{"class":99},[93,1069,1070,1072,1074,1076,1078,1080,1082,1084,1086,1089],{"class":95,"line":171},[93,1071,1026],{"class":211},[93,1073,227],{"class":99},[93,1075,378],{"class":198},[93,1077,233],{"class":99},[93,1079,236],{"class":99},[93,1081,855],{"class":239},[93,1083,236],{"class":99},[93,1085,215],{"class":99},[93,1087,1088],{"class":211}," italic",[93,1090,330],{"class":99},[93,1092,1093,1095,1097,1099,1101,1103,1105,1107,1109,1112],{"class":95,"line":181},[93,1094,1026],{"class":211},[93,1096,227],{"class":99},[93,1098,378],{"class":198},[93,1100,233],{"class":99},[93,1102,236],{"class":99},[93,1104,866],{"class":239},[93,1106,236],{"class":99},[93,1108,215],{"class":99},[93,1110,1111],{"class":211}," boldItalic",[93,1113,330],{"class":99},[93,1115,1116,1118,1120,1122,1124,1126,1128,1130,1132,1134],{"class":95,"line":187},[93,1117,1026],{"class":211},[93,1119,227],{"class":99},[93,1121,404],{"class":198},[93,1123,233],{"class":99},[93,1125,236],{"class":99},[93,1127,385],{"class":239},[93,1129,236],{"class":99},[93,1131,215],{"class":99},[93,1133,417],{"class":364},[93,1135,330],{"class":99},[93,1137,1138],{"class":95,"line":192},[93,1139,184],{"class":99},[18,1141,1142,1143,1146,1147,1150],{},"如果某个字体只有一种字重 (很多图标字体和展示字体都是这样)，就不要对它调用 ",[22,1144,1145],{},"template.Bold()"," 或 ",[22,1148,1149],{},"template.Italic()","。少一个变体没关系。错误地回退到另一个变体才是 bug 报告的根源。",[14,1152,1153],{"id":1153},"把字体打进二进制",[18,1155,1156,1157,1160],{},"启动时 ",[22,1158,1159],{},"os.ReadFile"," 在开发阶段没问题。生产环境里，字体是程序的一部分，应该跟着二进制走:",[84,1162,1164],{"className":86,"code":1163,"language":88,"meta":89,"style":89},"import _ \"embed\"\n\n//go:embed fonts/Inter-Regular.ttf\nvar interRegular []byte\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"Inter\", interRegular),\n)\n",[22,1165,1166,1179,1183,1189,1204,1208,1222,1245],{"__ignoreMap":89},[93,1167,1168,1170,1172,1174,1177],{"class":95,"line":96},[93,1169,118],{"class":117},[93,1171,983],{"class":211},[93,1173,236],{"class":99},[93,1175,1176],{"class":103},"embed",[93,1178,133],{"class":99},[93,1180,1181],{"class":95,"line":107},[93,1182,111],{"emptyLinePlaceholder":110},[93,1184,1185],{"class":95,"line":114},[93,1186,1188],{"class":1187},"sHwdD","//go:embed fonts/Inter-Regular.ttf\n",[93,1190,1191,1194,1197,1200],{"class":95,"line":124},[93,1192,1193],{"class":99},"var",[93,1195,1196],{"class":211}," interRegular ",[93,1198,1199],{"class":99},"[]",[93,1201,1203],{"class":1202},"spNyl","byte\n",[93,1205,1206],{"class":95,"line":136},[93,1207,111],{"emptyLinePlaceholder":110},[93,1209,1210,1212,1214,1216,1218,1220],{"class":95,"line":146},[93,1211,1011],{"class":211},[93,1213,221],{"class":99},[93,1215,300],{"class":211},[93,1217,227],{"class":99},[93,1219,50],{"class":198},[93,1221,307],{"class":99},[93,1223,1224,1226,1228,1230,1232,1234,1236,1238,1240,1243],{"class":95,"line":151},[93,1225,1026],{"class":211},[93,1227,227],{"class":99},[93,1229,378],{"class":198},[93,1231,233],{"class":99},[93,1233,236],{"class":99},[93,1235,385],{"class":239},[93,1237,236],{"class":99},[93,1239,215],{"class":99},[93,1241,1242],{"class":211}," interRegular",[93,1244,330],{"class":99},[93,1246,1247],{"class":95,"line":161},[93,1248,184],{"class":99},[18,1250,1251,1254],{},[22,1252,1253],{},"go build"," 把字节直接编译进二进制。再也不会在周五傍晚追查「部署镜像里的 .ttf 跑哪去了」。",[14,1256,1257],{"id":1257},"图标字体一样的玩法",[18,1259,1260],{},"Font Awesome、Material Symbols 的 TTF 版、IcoMoon、企业自定义字形集 — 都只是 TrueType 文件。注册方式相同:",[84,1262,1264],{"className":86,"code":1263,"language":88,"meta":89,"style":89},"icons, _ := os.ReadFile(\"MaterialSymbols-Regular.ttf\")\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"Icons\", icons),\n    gpdf.WithDefaultFont(\"Inter\", 11), // 正文默认\n)\n\n// 在某列里:\nc.Text(\"\", template.FontFamily(\"Icons\"), template.FontSize(20)) // \"home\" 图标\n",[22,1265,1266,1294,1308,1332,1358,1362,1366,1371],{"__ignoreMap":89},[93,1267,1268,1271,1273,1275,1277,1279,1281,1283,1285,1287,1290,1292],{"class":95,"line":96},[93,1269,1270],{"class":211},"icons",[93,1272,215],{"class":99},[93,1274,983],{"class":211},[93,1276,221],{"class":99},[93,1278,224],{"class":211},[93,1280,227],{"class":99},[93,1282,230],{"class":198},[93,1284,233],{"class":99},[93,1286,236],{"class":99},[93,1288,1289],{"class":239},"MaterialSymbols-Regular.ttf",[93,1291,236],{"class":99},[93,1293,184],{"class":99},[93,1295,1296,1298,1300,1302,1304,1306],{"class":95,"line":107},[93,1297,1011],{"class":211},[93,1299,221],{"class":99},[93,1301,300],{"class":211},[93,1303,227],{"class":99},[93,1305,50],{"class":198},[93,1307,307],{"class":99},[93,1309,1310,1312,1314,1316,1318,1320,1323,1325,1327,1330],{"class":95,"line":114},[93,1311,1026],{"class":211},[93,1313,227],{"class":99},[93,1315,378],{"class":198},[93,1317,233],{"class":99},[93,1319,236],{"class":99},[93,1321,1322],{"class":239},"Icons",[93,1324,236],{"class":99},[93,1326,215],{"class":99},[93,1328,1329],{"class":211}," icons",[93,1331,330],{"class":99},[93,1333,1334,1336,1338,1340,1342,1344,1346,1348,1350,1352,1355],{"class":95,"line":124},[93,1335,1026],{"class":211},[93,1337,227],{"class":99},[93,1339,404],{"class":198},[93,1341,233],{"class":99},[93,1343,236],{"class":99},[93,1345,385],{"class":239},[93,1347,236],{"class":99},[93,1349,215],{"class":99},[93,1351,417],{"class":364},[93,1353,1354],{"class":99},"),",[93,1356,1357],{"class":1187}," // 正文默认\n",[93,1359,1360],{"class":95,"line":136},[93,1361,184],{"class":99},[93,1363,1364],{"class":95,"line":146},[93,1365,111],{"emptyLinePlaceholder":110},[93,1367,1368],{"class":95,"line":151},[93,1369,1370],{"class":1187},"// 在某列里:\n",[93,1372,1373,1375,1377,1379,1381,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1412,1415],{"class":95,"line":161},[93,1374,507],{"class":211},[93,1376,227],{"class":99},[93,1378,531],{"class":198},[93,1380,233],{"class":99},[93,1382,1383],{"class":99},"\"\"",[93,1385,215],{"class":99},[93,1387,545],{"class":211},[93,1389,227],{"class":99},[93,1391,78],{"class":198},[93,1393,233],{"class":99},[93,1395,236],{"class":99},[93,1397,1322],{"class":239},[93,1399,236],{"class":99},[93,1401,1354],{"class":99},[93,1403,545],{"class":211},[93,1405,227],{"class":99},[93,1407,550],{"class":198},[93,1409,233],{"class":99},[93,1411,365],{"class":364},[93,1413,1414],{"class":99},"))",[93,1416,1417],{"class":1187}," // \"home\" 图标\n",[18,1419,1420],{},"Unicode 转义码是字体文档里写的值。gpdf 不在乎那个字形是不是图标 — 在它眼里就是个码点，按字母同样的方式做子集化。",[14,1422,1423],{"id":1423},"常见错误",[1425,1426,1427,1438,1451],"ul",{},[1428,1429,1430,1433,1434,1437],"li",{},[68,1431,1432],{},"调用处字族名打错。"," ",[22,1435,1436],{},"template.FontFamily(\"Intr\")"," 静默回退到默认字族。无报错无警告。文字突然变成 Helvetica？先查这个。",[1428,1439,1440,1447,1448,1450],{},[68,1441,1442,1443,1446],{},"容器化时不用 ",[22,1444,1445],{},"//go:embed","。"," 精简过的 Docker 上下文丢掉了 ",[22,1449,24],{},"，运行时静默回退，最后从用户邮件里得知问题。打进二进制。",[1428,1452,1453,1456,1457,1459,1460,1463],{},[68,1454,1455],{},"把字体的 PostScript 名当字族名用。"," \"Inter-Regular\" 是 PostScript 名。用它注册 ",[22,1458,378],{},"，bold 查找会去找 \"Inter-Regular-Bold\" — 根本不存在。取干净的根字族名 (",[22,1461,1462],{},"\"Inter\"",")，让变体后缀去处理样式。",[14,1465,1466],{"id":1466},"相关菜谱",[1425,1468,1469,1476,1483],{},[1428,1470,1471,1475],{},[27,1472,1474],{"href":1473},"/zh/blog/embed-japanese-font","如何在 gpdf 中嵌入日文字体？"," — 同样的机制，附带 CJK 注意事项",[1428,1477,1478,1482],{},[27,1479,1481],{"href":1480},"/zh/blog/bold-italic-together","如何同时使用 bold 和 italic？"," — 变体解析的细节",[1428,1484,1485,1489],{},[27,1486,1488],{"href":1487},"/zh/blog/tofu-boxes-japanese","为什么我的 PDF 显示豆腐方块？"," — 字体不覆盖码点时会发生什么",[14,1491,1493],{"id":1492},"试试-gpdf","试试 gpdf",[18,1495,1496],{},"gpdf 是一个 Go PDF 生成库。MIT 协议、零外部依赖、TrueType 处理纯 Go 实现。",[84,1498,1502],{"className":1499,"code":1500,"language":1501,"meta":89,"style":89},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[22,1503,1504],{"__ignoreMap":89},[93,1505,1506,1508,1511],{"class":95,"line":96},[93,1507,88],{"class":103},[93,1509,1510],{"class":239}," get",[93,1512,1513],{"class":239}," github.com/gpdf-dev/gpdf\n",[18,1515,1516,1520,1521],{},[27,1517,1519],{"href":29,"rel":1518},[31],"⭐ 在 GitHub 上 Star"," · ",[27,1522,1525],{"href":1523,"rel":1524},"https://gpdf.dev/zh/docs/quickstart",[31],"阅读文档",[1527,1528,1529],"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":89,"searchDepth":107,"depth":107,"links":1531},[1532,1533,1534,1535,1536,1537,1538,1539,1540,1541],{"id":16,"depth":107,"text":16},{"id":40,"depth":107,"text":40},{"id":82,"depth":107,"text":82},{"id":744,"depth":107,"text":745},{"id":786,"depth":107,"text":787},{"id":1153,"depth":107,"text":1153},{"id":1257,"depth":107,"text":1257},{"id":1423,"depth":107,"text":1423},{"id":1466,"depth":107,"text":1466},{"id":1492,"depth":107,"text":1493},"2026-04-30","读取 TTF 字节，用 gpdf.WithFont 注册字族，再按名称引用即可。Inter、Roboto、图标字体、品牌字体都同样适用。",false,"md",{"name":1547,"totalTime":1548,"tools":1549,"steps":1552},"在 gpdf 文档中注册自定义 TrueType 字体","PT10M",[1550,1551],"Go 1.22+","任意 TrueType .ttf 文件 (Inter、Roboto、JetBrains Mono、图标字体等)",[1553,1556,1559,1562],{"name":1554,"text":1555},"读取 TTF 字节","用 os.ReadFile 把 .ttf 读成 []byte。生产构建建议使用 //go:embed，把字体打进二进制。",{"name":1557,"text":1558},"在文档构造时注册字族","向 gpdf.NewDocument 传入 gpdf.WithFont(\"Inter\", fontBytes)。字族名是任意字符串，与字体内部的 name 表无关，只是 gpdf 用来查找的 key。",{"name":1560,"text":1561},"设为默认字族，或在每次调用时指定","加上 gpdf.WithDefaultFont(\"Inter\", 11)，所有 c.Text 都会用它。否则在每个 Text 调用上传 template.FontFamily(\"Inter\")。",{"name":1563,"text":1564},"Bold、Italic、BoldItalic 各自独立注册","每种样式都是单独的 TTF 文件。注册为 \"Inter-Bold\"、\"Inter-Italic\"、\"Inter-BoldItalic\"。gpdf 根据样式标志拼出变体 ID 后做精确匹配。",null,{},"/zh/blog/add-custom-truetype-font",{"title":5,"description":1543},"zh/blog/018.add-custom-truetype-font",[1571,1572],"recipe","tutorial","4Vq4wvRsFo00HRom2aqD1tAs6q_watk0FYF4X15gtIM",1779199016835]