[{"data":1,"prerenderedAt":16407},["ShallowReactive",2],{"blog-list-en":3},[4,1287,2549,3900,5664,7327,8960,10283,11651],{"id":5,"title":6,"author":7,"body":10,"date":1251,"description":1252,"draft":1253,"extension":1254,"howTo":1255,"image":1277,"meta":1278,"navigation":127,"path":1279,"seo":1280,"stem":1281,"tags":1282,"updated":1277,"__hash__":1286},"blog/blog/008.tofu-boxes-japanese.md","Why does my PDF show tofu boxes for Japanese?",{"name":8,"url":9},"gpdf team","https://gpdf.dev",{"type":11,"value":12,"toc":1239},"minimark",[13,18,22,26,29,32,93,97,100,702,716,729,733,736,756,775,781,785,792,937,967,971,978,981,984,1014,1017,1021,1027,1092,1107,1110,1142,1146,1164,1168,1198,1202,1205,1222,1235],[14,15,17],"h2",{"id":16},"the-question-in-other-words","The question, in other words",[19,20,21],"p",{},"I wrote Japanese text with gpdf and the output PDF shows empty rectangles where the characters should be. What is that, and how do I get real Japanese glyphs into the file?",[14,23,25],{"id":24},"the-quick-answer","The quick answer",[19,27,28],{},"That is tofu — the PDF viewer draws a placeholder rectangle because the font embedded in the PDF has no glyph for the Unicode codepoint you asked for. Four things cause it, and one is far more common than the rest.",[19,30,31],{},"In order of frequency:",[33,34,35,52,69,83],"ol",{},[36,37,38,42,43,47,48,51],"li",{},[39,40,41],"strong",{},"No CJK font registered."," ",[44,45,46],"code",{},"gpdf.NewDocument"," has no ",[44,49,50],{},"WithFont"," call, so the document falls back to the PDF Base-14 fonts (Helvetica, Times, Courier). None of those cover U+3040–U+9FFF.",[36,53,54,42,61,64,65,68],{},[39,55,56,57,60],{},"CJK font registered, but wrong family on ",[44,58,59],{},"c.Text",".",[44,62,63],{},"WithFont(\"NotoSansJP\", ...)"," is set, but ",[44,66,67],{},"template.FontFamily(\"Arial\")"," on the text forces gpdf to look up Japanese in a Latin font.",[36,70,71,74,75,78,79,82],{},[39,72,73],{},"Font file doesn't actually contain CJK glyphs."," The TTF on disk is a Latin subset (",[44,76,77],{},"NotoSans-Regular.ttf"," rather than ",[44,80,81],{},"NotoSansJP-Regular.ttf","). Name looks right, coverage is empty.",[36,84,85,88,89,92],{},[39,86,87],{},"Bytes corrupted before gpdf saw them."," The string was decoded as Shift-JIS or Latin-1 upstream and the runes you are trying to render are no longer Japanese. If you see ",[44,90,91],{},"縺ゅ→縺"," instead of rectangles, this is the one.",[14,94,96],{"id":95},"the-canonical-fix-for-cause-1","The canonical fix for cause #1",[19,98,99],{},"Nine times out of ten, it is this:",[101,102,107],"pre",{"className":103,"code":104,"language":105,"meta":106,"style":106},"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(\"こんにちは、世界。\")\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","",[44,108,109,122,129,139,151,161,166,176,186,196,202,207,223,260,276,294,300,305,324,348,386,411,436,442,447,466,500,537,559,565,571,576,597,610,625,630,676,691,696],{"__ignoreMap":106},[110,111,114,118],"span",{"class":112,"line":113},"line",1,[110,115,117],{"class":116},"sMK4o","package",[110,119,121],{"class":120},"sBMFI"," main\n",[110,123,125],{"class":112,"line":124},2,[110,126,128],{"emptyLinePlaceholder":127},true,"\n",[110,130,132,136],{"class":112,"line":131},3,[110,133,135],{"class":134},"s7zQu","import",[110,137,138],{"class":116}," (\n",[110,140,142,145,148],{"class":112,"line":141},4,[110,143,144],{"class":116},"    \"",[110,146,147],{"class":120},"log",[110,149,150],{"class":116},"\"\n",[110,152,154,156,159],{"class":112,"line":153},5,[110,155,144],{"class":116},[110,157,158],{"class":120},"os",[110,160,150],{"class":116},[110,162,164],{"class":112,"line":163},6,[110,165,128],{"emptyLinePlaceholder":127},[110,167,169,171,174],{"class":112,"line":168},7,[110,170,144],{"class":116},[110,172,173],{"class":120},"github.com/gpdf-dev/gpdf",[110,175,150],{"class":116},[110,177,179,181,184],{"class":112,"line":178},8,[110,180,144],{"class":116},[110,182,183],{"class":120},"github.com/gpdf-dev/gpdf/document",[110,185,150],{"class":116},[110,187,189,191,194],{"class":112,"line":188},9,[110,190,144],{"class":116},[110,192,193],{"class":120},"github.com/gpdf-dev/gpdf/template",[110,195,150],{"class":116},[110,197,199],{"class":112,"line":198},10,[110,200,201],{"class":116},")\n",[110,203,205],{"class":112,"line":204},11,[110,206,128],{"emptyLinePlaceholder":127},[110,208,210,213,217,220],{"class":112,"line":209},12,[110,211,212],{"class":116},"func",[110,214,216],{"class":215},"s2Zo4"," main",[110,218,219],{"class":116},"()",[110,221,222],{"class":116}," {\n",[110,224,226,230,233,236,239,242,244,247,250,253,256,258],{"class":112,"line":225},13,[110,227,229],{"class":228},"sTEyZ","    font",[110,231,232],{"class":116},",",[110,234,235],{"class":228}," err ",[110,237,238],{"class":116},":=",[110,240,241],{"class":228}," os",[110,243,60],{"class":116},[110,245,246],{"class":215},"ReadFile",[110,248,249],{"class":116},"(",[110,251,252],{"class":116},"\"",[110,254,81],{"class":255},"sfazB",[110,257,252],{"class":116},[110,259,201],{"class":116},[110,261,263,266,268,271,274],{"class":112,"line":262},14,[110,264,265],{"class":134},"    if",[110,267,235],{"class":228},[110,269,270],{"class":116},"!=",[110,272,273],{"class":116}," nil",[110,275,222],{"class":116},[110,277,279,282,284,287,289,292],{"class":112,"line":278},15,[110,280,281],{"class":228},"        log",[110,283,60],{"class":116},[110,285,286],{"class":215},"Fatal",[110,288,249],{"class":116},[110,290,291],{"class":228},"err",[110,293,201],{"class":116},[110,295,297],{"class":112,"line":296},16,[110,298,299],{"class":116},"    }\n",[110,301,303],{"class":112,"line":302},17,[110,304,128],{"emptyLinePlaceholder":127},[110,306,308,311,313,316,318,321],{"class":112,"line":307},18,[110,309,310],{"class":228},"    doc ",[110,312,238],{"class":116},[110,314,315],{"class":228}," gpdf",[110,317,60],{"class":116},[110,319,320],{"class":215},"NewDocument",[110,322,323],{"class":116},"(\n",[110,325,327,330,332,335,337,340,342,345],{"class":112,"line":326},19,[110,328,329],{"class":228},"        gpdf",[110,331,60],{"class":116},[110,333,334],{"class":215},"WithPageSize",[110,336,249],{"class":116},[110,338,339],{"class":228},"gpdf",[110,341,60],{"class":116},[110,343,344],{"class":228},"A4",[110,346,347],{"class":116},"),\n",[110,349,351,353,355,358,360,363,365,368,370,372,374,377,379,383],{"class":112,"line":350},20,[110,352,329],{"class":228},[110,354,60],{"class":116},[110,356,357],{"class":215},"WithMargins",[110,359,249],{"class":116},[110,361,362],{"class":228},"document",[110,364,60],{"class":116},[110,366,367],{"class":215},"UniformEdges",[110,369,249],{"class":116},[110,371,362],{"class":228},[110,373,60],{"class":116},[110,375,376],{"class":215},"Mm",[110,378,249],{"class":116},[110,380,382],{"class":381},"sbssI","20",[110,384,385],{"class":116},"))),\n",[110,387,389,391,393,395,397,399,402,404,406,409],{"class":112,"line":388},21,[110,390,329],{"class":228},[110,392,60],{"class":116},[110,394,50],{"class":215},[110,396,249],{"class":116},[110,398,252],{"class":116},[110,400,401],{"class":255},"NotoSansJP",[110,403,252],{"class":116},[110,405,232],{"class":116},[110,407,408],{"class":228}," font",[110,410,347],{"class":116},[110,412,414,416,418,421,423,425,427,429,431,434],{"class":112,"line":413},22,[110,415,329],{"class":228},[110,417,60],{"class":116},[110,419,420],{"class":215},"WithDefaultFont",[110,422,249],{"class":116},[110,424,252],{"class":116},[110,426,401],{"class":255},[110,428,252],{"class":116},[110,430,232],{"class":116},[110,432,433],{"class":381}," 12",[110,435,347],{"class":116},[110,437,439],{"class":112,"line":438},23,[110,440,441],{"class":116},"    )\n",[110,443,445],{"class":112,"line":444},24,[110,446,128],{"emptyLinePlaceholder":127},[110,448,450,453,455,458,460,463],{"class":112,"line":449},25,[110,451,452],{"class":228},"    page ",[110,454,238],{"class":116},[110,456,457],{"class":228}," doc",[110,459,60],{"class":116},[110,461,462],{"class":215},"AddPage",[110,464,465],{"class":116},"()\n",[110,467,469,472,474,477,480,484,487,490,492,495,498],{"class":112,"line":468},26,[110,470,471],{"class":228},"    page",[110,473,60],{"class":116},[110,475,476],{"class":215},"AutoRow",[110,478,479],{"class":116},"(func(",[110,481,483],{"class":482},"sHdIc","r",[110,485,486],{"class":116}," *",[110,488,489],{"class":120},"template",[110,491,60],{"class":116},[110,493,494],{"class":120},"RowBuilder",[110,496,497],{"class":116},")",[110,499,222],{"class":116},[110,501,503,506,508,511,513,516,518,521,524,526,528,530,533,535],{"class":112,"line":502},27,[110,504,505],{"class":228},"        r",[110,507,60],{"class":116},[110,509,510],{"class":215},"Col",[110,512,249],{"class":116},[110,514,515],{"class":381},"12",[110,517,232],{"class":116},[110,519,520],{"class":116}," func(",[110,522,523],{"class":482},"c",[110,525,486],{"class":116},[110,527,489],{"class":120},[110,529,60],{"class":116},[110,531,532],{"class":120},"ColBuilder",[110,534,497],{"class":116},[110,536,222],{"class":116},[110,538,540,543,545,548,550,552,555,557],{"class":112,"line":539},28,[110,541,542],{"class":228},"            c",[110,544,60],{"class":116},[110,546,547],{"class":215},"Text",[110,549,249],{"class":116},[110,551,252],{"class":116},[110,553,554],{"class":255},"こんにちは、世界。",[110,556,252],{"class":116},[110,558,201],{"class":116},[110,560,562],{"class":112,"line":561},29,[110,563,564],{"class":116},"        })\n",[110,566,568],{"class":112,"line":567},30,[110,569,570],{"class":116},"    })\n",[110,572,574],{"class":112,"line":573},31,[110,575,128],{"emptyLinePlaceholder":127},[110,577,579,582,584,586,588,590,592,595],{"class":112,"line":578},32,[110,580,581],{"class":228},"    data",[110,583,232],{"class":116},[110,585,235],{"class":228},[110,587,238],{"class":116},[110,589,457],{"class":228},[110,591,60],{"class":116},[110,593,594],{"class":215},"Generate",[110,596,465],{"class":116},[110,598,600,602,604,606,608],{"class":112,"line":599},33,[110,601,265],{"class":134},[110,603,235],{"class":228},[110,605,270],{"class":116},[110,607,273],{"class":116},[110,609,222],{"class":116},[110,611,613,615,617,619,621,623],{"class":112,"line":612},34,[110,614,281],{"class":228},[110,616,60],{"class":116},[110,618,286],{"class":215},[110,620,249],{"class":116},[110,622,291],{"class":228},[110,624,201],{"class":116},[110,626,628],{"class":112,"line":627},35,[110,629,299],{"class":116},[110,631,633,635,637,639,641,643,646,648,650,653,655,657,660,662,665,668,670,672,674],{"class":112,"line":632},36,[110,634,265],{"class":134},[110,636,235],{"class":228},[110,638,238],{"class":116},[110,640,241],{"class":228},[110,642,60],{"class":116},[110,644,645],{"class":215},"WriteFile",[110,647,249],{"class":116},[110,649,252],{"class":116},[110,651,652],{"class":255},"hello.pdf",[110,654,252],{"class":116},[110,656,232],{"class":116},[110,658,659],{"class":228}," data",[110,661,232],{"class":116},[110,663,664],{"class":381}," 0o644",[110,666,667],{"class":116},");",[110,669,235],{"class":228},[110,671,270],{"class":116},[110,673,273],{"class":116},[110,675,222],{"class":116},[110,677,679,681,683,685,687,689],{"class":112,"line":678},37,[110,680,281],{"class":228},[110,682,60],{"class":116},[110,684,286],{"class":215},[110,686,249],{"class":116},[110,688,291],{"class":228},[110,690,201],{"class":116},[110,692,694],{"class":112,"line":693},38,[110,695,299],{"class":116},[110,697,699],{"class":112,"line":698},39,[110,700,701],{"class":116},"}\n",[19,703,704,705,708,709,712,713,715],{},"Two lines register and default the font. No CGO. No ",[44,706,707],{},"AddUTF8Font"," bookkeeping. If you were seeing ",[44,710,711],{},"□□□□□、□□。"," before and run this program with a real ",[44,714,81],{}," next to it, you get real glyphs.",[19,717,718,719,721,722,60],{},"Grab ",[44,720,81],{}," from ",[723,724,728],"a",{"href":725,"rel":726},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[727],"nofollow","Google Fonts",[14,730,732],{"id":731},"how-to-tell-which-cause-you-are-hitting","How to tell which cause you are hitting",[19,734,735],{},"Most of this is staring at three places: where you build the document, where you write the text, and the TTF file itself.",[19,737,738,744,745,748,749,752,753,755],{},[39,739,740,741],{},"If your output is ",[44,742,743],{},"□□□"," (identical rectangles), it is cause 1, 2, or 3. The PDF embedded ",[746,747,723],"em",{}," font but it has no glyphs for those codepoints. Open the PDF in Acrobat, go to ",[44,750,751],{},"File → Properties → Fonts",", and look at which fonts were actually embedded. If the list is only Helvetica / Times / Courier, cause 1. If ",[44,754,401],{}," is listed and rectangles are still there, cause 2 or 3.",[19,757,758,766,767,770,771,774],{},[39,759,740,760,762,763],{},[44,761,91],{}," or ",[44,764,765],{},"ã\"ã‚\"ã«ã¡ã¯"," (garbled Latin-ish characters), it is cause 4. Your Japanese string was re-encoded somewhere before gpdf got it. Most common culprit: a CSV saved as Shift-JIS by Excel and read with ",[44,768,769],{},"os.ReadFile"," as if it were UTF-8, or an HTTP endpoint that didn't declare ",[44,772,773],{},"charset=utf-8",". Fix the decoder, not the PDF.",[19,776,777,780],{},[39,778,779],{},"Mixed output"," — some characters render, some are boxes — means the font has partial coverage. A font labelled \"Japanese\" might include hiragana and katakana but skip uncommon kanji like 鬱 or 龠. Switch to Noto Sans JP (covers JIS X 0213) or Source Han Sans JP if you hit this.",[14,782,784],{"id":783},"cause-2-in-detail-right-font-wrong-family-name","Cause 2 in detail: right font, wrong family name",[19,786,787,788,791],{},"This one is sneaky because the font ",[746,789,790],{},"is"," embedded — it just isn't used. A minimal repro:",[101,793,795],{"className":103,"code":794,"language":105,"meta":106,"style":106},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    // No WithDefaultFont.\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"こんにちは\") // Uses the default font: Helvetica.\n    })\n})\n",[44,796,797,812,835,841,845,849,874,905,928,932],{"__ignoreMap":106},[110,798,799,802,804,806,808,810],{"class":112,"line":113},[110,800,801],{"class":228},"doc ",[110,803,238],{"class":116},[110,805,315],{"class":228},[110,807,60],{"class":116},[110,809,320],{"class":215},[110,811,323],{"class":116},[110,813,814,817,819,821,823,825,827,829,831,833],{"class":112,"line":124},[110,815,816],{"class":228},"    gpdf",[110,818,60],{"class":116},[110,820,50],{"class":215},[110,822,249],{"class":116},[110,824,252],{"class":116},[110,826,401],{"class":255},[110,828,252],{"class":116},[110,830,232],{"class":116},[110,832,408],{"class":228},[110,834,347],{"class":116},[110,836,837],{"class":112,"line":131},[110,838,840],{"class":839},"sHwdD","    // No WithDefaultFont.\n",[110,842,843],{"class":112,"line":141},[110,844,201],{"class":116},[110,846,847],{"class":112,"line":153},[110,848,128],{"emptyLinePlaceholder":127},[110,850,851,854,856,858,860,862,864,866,868,870,872],{"class":112,"line":163},[110,852,853],{"class":228},"page",[110,855,60],{"class":116},[110,857,476],{"class":215},[110,859,479],{"class":116},[110,861,483],{"class":482},[110,863,486],{"class":116},[110,865,489],{"class":120},[110,867,60],{"class":116},[110,869,494],{"class":120},[110,871,497],{"class":116},[110,873,222],{"class":116},[110,875,876,879,881,883,885,887,889,891,893,895,897,899,901,903],{"class":112,"line":168},[110,877,878],{"class":228},"    r",[110,880,60],{"class":116},[110,882,510],{"class":215},[110,884,249],{"class":116},[110,886,515],{"class":381},[110,888,232],{"class":116},[110,890,520],{"class":116},[110,892,523],{"class":482},[110,894,486],{"class":116},[110,896,489],{"class":120},[110,898,60],{"class":116},[110,900,532],{"class":120},[110,902,497],{"class":116},[110,904,222],{"class":116},[110,906,907,910,912,914,916,918,921,923,925],{"class":112,"line":178},[110,908,909],{"class":228},"        c",[110,911,60],{"class":116},[110,913,547],{"class":215},[110,915,249],{"class":116},[110,917,252],{"class":116},[110,919,920],{"class":255},"こんにちは",[110,922,252],{"class":116},[110,924,497],{"class":116},[110,926,927],{"class":839}," // Uses the default font: Helvetica.\n",[110,929,930],{"class":112,"line":188},[110,931,570],{"class":116},[110,933,934],{"class":112,"line":198},[110,935,936],{"class":116},"})\n",[19,938,939,940,943,944,946,947,950,951,953,954,956,957,959,960,962,963,966],{},"Fix: add ",[44,941,942],{},"gpdf.WithDefaultFont(\"NotoSansJP\", 12)"," to ",[44,945,320],{},", or pass ",[44,948,949],{},"template.FontFamily(\"NotoSansJP\")"," on every ",[44,952,59],{}," that needs Japanese. The family name in ",[44,955,50],{}," and the one on ",[44,958,59],{}," must match exactly, including case. ",[44,961,401],{}," and ",[44,964,965],{},"notosansjp"," are two different fonts to gpdf.",[14,968,970],{"id":969},"cause-3-in-detail-the-wrong-ttf-file","Cause 3 in detail: the wrong TTF file",[19,972,973,962,975,977],{},[44,974,77],{},[44,976,81],{}," are two different files. The first is a Latin font with zero CJK coverage. The second is the Japanese cut, around 17,000 glyphs. They look almost identical in a directory listing. Autocomplete will happily hand you the wrong one.",[19,979,980],{},"gpdf does not validate glyph coverage at registration. If you hand it bytes, it trusts you. The failure shows up as tofu at render time.",[19,982,983],{},"Quickest way to check:",[985,986,987,994,1001],"ul",{},[36,988,989,990,993],{},"macOS: ",[44,991,992],{},"Font Book → double-click the file → preview"," shows a glyph grid",[36,995,996,997,1000],{},"Linux: ",[44,998,999],{},"otfinfo -u NotoSans-Regular.ttf"," dumps the Unicode coverage",[36,1002,1003,1004,1009,1010,1013],{},"Cross-platform: ",[723,1005,1008],{"href":1006,"rel":1007},"https://github.com/fonttools/fonttools",[727],"fontTools"," — ",[44,1011,1012],{},"ttx -t cmap NotoSans-Regular.ttf"," dumps the cmap table as XML",[19,1015,1016],{},"If U+3042 (あ) is not in the list, you have the Latin subset.",[14,1018,1020],{"id":1019},"cause-4-in-detail-encoding-corruption","Cause 4 in detail: encoding corruption",[19,1022,1023,1024,1026],{},"This one does not actually involve gpdf. The string handed to ",[44,1025,59],{}," already had the wrong bytes. Print it before rendering:",[101,1028,1030],{"className":103,"code":1029,"language":105,"meta":106,"style":106},"text := loadLabelFromSomewhere()\nfmt.Printf(\"%q\\n\", text) // Shows actual runes\nc.Text(text)\n",[44,1031,1032,1044,1077],{"__ignoreMap":106},[110,1033,1034,1037,1039,1042],{"class":112,"line":113},[110,1035,1036],{"class":228},"text ",[110,1038,238],{"class":116},[110,1040,1041],{"class":215}," loadLabelFromSomewhere",[110,1043,465],{"class":116},[110,1045,1046,1049,1051,1054,1056,1058,1062,1065,1067,1069,1072,1074],{"class":112,"line":124},[110,1047,1048],{"class":228},"fmt",[110,1050,60],{"class":116},[110,1052,1053],{"class":215},"Printf",[110,1055,249],{"class":116},[110,1057,252],{"class":116},[110,1059,1061],{"class":1060},"swJcz","%q",[110,1063,1064],{"class":228},"\\n",[110,1066,252],{"class":116},[110,1068,232],{"class":116},[110,1070,1071],{"class":228}," text",[110,1073,497],{"class":116},[110,1075,1076],{"class":839}," // Shows actual runes\n",[110,1078,1079,1081,1083,1085,1087,1090],{"class":112,"line":131},[110,1080,523],{"class":228},[110,1082,60],{"class":116},[110,1084,547],{"class":215},[110,1086,249],{"class":116},[110,1088,1089],{"class":228},"text",[110,1091,201],{"class":116},[19,1093,1094,1095,1098,1099,1102,1103,1106],{},"If ",[44,1096,1097],{},"fmt.Printf(\"%q\\n\", text)"," prints ",[44,1100,1101],{},"\"縺ゅ→縺\""," instead of ",[44,1104,1105],{},"\"あいうえ\"",", the corruption happened upstream. gpdf cannot fix it — find the place where UTF-8 was mis-decoded.",[19,1108,1109],{},"Common upstream culprits:",[985,1111,1112,1121,1135],{},[36,1113,1114,1115,1117,1118],{},"Reading a CSV exported from Excel (Windows Shift-JIS) with ",[44,1116,769],{}," and casting straight to ",[44,1119,1120],{},"string",[36,1122,1123,1124,762,1127,1130,1131,1134],{},"A database column declared ",[44,1125,1126],{},"latin1",[44,1128,1129],{},"utf8mb3"," (not ",[44,1132,1133],{},"utf8mb4",") already storing mojibake",[36,1136,1137,1138,1141],{},"An HTTP response missing ",[44,1139,1140],{},"Content-Type: application/json; charset=utf-8",", and a client that guessed Latin-1",[14,1143,1145],{"id":1144},"one-edge-case-worth-naming","One edge case worth naming",[19,1147,1148,1149,1151,1152,1155,1156,1159,1160,1163],{},"gpdf silently subsets. If you register NotoSansJP at document construction, render ",[44,1150,920],{},", and then later in the same document render ",[44,1153,1154],{},"鬱陶しい",", the second render will work — gpdf adds the new glyphs to the subset on the fly. But if you generate a PDF, hand it to a viewer, and ",[746,1157,1158],{},"then"," try to edit the PDF in Acrobat and type a new kanji that wasn't in the original text, you will get tofu there. The subset is frozen at ",[44,1161,1162],{},"Generate()"," time. Re-run your program; don't patch the PDF.",[14,1165,1167],{"id":1166},"related-recipes","Related recipes",[985,1169,1170,1180,1191],{},[36,1171,1172,1176,1177,1179],{},[723,1173,1175],{"href":1174},"/blog/embed-japanese-font","How do I embed a Japanese font in gpdf?"," — full ",[44,1178,50],{}," walkthrough with bold/italic variants and multi-CJK documents",[36,1181,1182,1186,1187,1190],{},[723,1183,1185],{"href":1184},"/blog/noto-sans-jp-with-gpdf","How do I use Noto Sans JP with gpdf?"," — which Noto file to pick and how ",[44,1188,1189],{},"go:embed"," simplifies distribution",[36,1192,1193,1197],{},[723,1194,1196],{"href":1195},"/blog/japanese-pdf-in-go","The definitive guide to Japanese PDFs in Go (2026)"," — long-form guide covering fonts, vertical text, ruby, and JP-specific layout",[14,1199,1201],{"id":1200},"try-gpdf","Try gpdf",[19,1203,1204],{},"gpdf is a Go library for generating PDFs. MIT licensed, zero external dependencies, native CJK support.",[101,1206,1210],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[44,1211,1212],{"__ignoreMap":106},[110,1213,1214,1216,1219],{"class":112,"line":113},[110,1215,105],{"class":120},[110,1217,1218],{"class":255}," get",[110,1220,1221],{"class":255}," github.com/gpdf-dev/gpdf\n",[19,1223,1224,1229,1230],{},[723,1225,1228],{"href":1226,"rel":1227},"https://github.com/gpdf-dev/gpdf",[727],"⭐ Star on GitHub"," · ",[723,1231,1234],{"href":1232,"rel":1233},"https://gpdf.dev/docs/quickstart",[727],"Read the docs",[1236,1237,1238],"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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":106,"searchDepth":124,"depth":124,"links":1240},[1241,1242,1243,1244,1245,1246,1247,1248,1249,1250],{"id":16,"depth":124,"text":17},{"id":24,"depth":124,"text":25},{"id":95,"depth":124,"text":96},{"id":731,"depth":124,"text":732},{"id":783,"depth":124,"text":784},{"id":969,"depth":124,"text":970},{"id":1019,"depth":124,"text":1020},{"id":1144,"depth":124,"text":1145},{"id":1166,"depth":124,"text":1167},{"id":1200,"depth":124,"text":1201},"2026-04-17","Empty rectangles instead of Japanese characters mean your PDF couldn't find glyphs for those codepoints. Here are the four causes and the fixes.",false,"md",{"name":1256,"totalTime":1257,"tools":1258,"steps":1261},"Diagnose and fix tofu boxes in a gpdf document","PT15M",[1259,1260],"Go 1.22+","A CJK-capable TTF such as NotoSansJP-Regular.ttf",[1262,1265,1268,1271,1274],{"name":1263,"text":1264},"Confirm the symptom is tofu, not mojibake","Open the PDF. Japanese characters as empty rectangles (□) mean a font lookup miss. Garbled Latin like 縺ゅ→縺 means UTF-8 was decoded wrong upstream of gpdf.",{"name":1266,"text":1267},"Check that a CJK-capable font is registered","Search your document construction for gpdf.WithFont. With no CJK TTF registered, gpdf falls back to the Base-14 PDF fonts, none of which cover CJK codepoints.",{"name":1269,"text":1270},"Verify the font family on each c.Text call","If WithDefaultFont is not set, every c.Text that renders Japanese needs template.FontFamily(\"NotoSansJP\") explicitly. A mismatched family silently falls back to the default.",{"name":1272,"text":1273},"Confirm the TTF file actually contains CJK glyphs","A file called NotoSans-Regular.ttf (Latin subset) looks right at a glance but has no CJK table. gpdf does not validate coverage at registration time.",{"name":1275,"text":1276},"Regenerate and verify in two viewers","Open the PDF in Adobe Acrobat and in Chrome. Both should render Japanese. If one works and one does not, glyphs are embedded but not subset-registered for that codepoint.",null,{},"/blog/tofu-boxes-japanese",{"title":6,"description":1252},"blog/008.tofu-boxes-japanese",[1283,1284,1285],"recipe","troubleshooting","cjk","FYMNCUXl9ezM4RLFCpGxajERddhmZIZ3aA9snvFjzQU",{"id":1288,"title":1289,"author":1290,"body":1291,"date":1251,"description":2520,"draft":1253,"extension":1254,"howTo":2521,"image":1277,"meta":2542,"navigation":127,"path":2543,"seo":2544,"stem":2545,"tags":2546,"updated":1277,"__hash__":2548},"blog/blog/009.ipaex-gothic-gpdf.md","How do I use IPAex Gothic in gpdf?",{"name":8,"url":9},{"type":11,"value":1292,"toc":2509},[1293,1295,1308,1312,1327,1331,1867,1888,1892,1895,1957,1960,1966,1970,1973,1976,1984,2016,2022,2307,2310,2314,2323,2349,2356,2359,2363,2448,2451,2455,2482,2484,2486,2498,2506],[14,1294,17],{"id":16},[19,1296,1297,1298,1303,1304,1307],{},"You want to use IPAex Gothic — the proportional Gothic font the Japanese ",[723,1299,1302],{"href":1300,"rel":1301},"https://moji.or.jp/ipafont/",[727],"Information-technology Promotion Agency"," (IPA) maintains — in a ",[723,1305,339],{"href":1226,"rel":1306},[727]," document. The typical reasons: e-Tax PDF submissions, government-facing paperwork, or just a house style that's been on IPAex since the early 2010s. Three things trip people up: which file to grab, how to deal with there being no Bold, and what the IPA Font License actually asks of you.",[14,1309,1311],{"id":1310},"tldr","TL;DR",[19,1313,1314,1315,1318,1319,1322,1323,1326],{},"Register ",[44,1316,1317],{},"ipaexg.ttf"," with ",[44,1320,1321],{},"gpdf.WithFont(\"IPAexGothic\", bytes)",". Set it as the default. Bold emphasis has to be synthesized with ",[44,1324,1325],{},"template.Bold()"," or paired with IPAex Mincho since IPAex ships only Regular. Keep the license text next to the binary.",[14,1328,1330],{"id":1329},"the-complete-example","The complete example",[101,1332,1334],{"className":103,"code":1333,"language":105,"meta":106,"style":106},"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(\"ipaexg.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(25))),\n        gpdf.WithFont(\"IPAexGothic\", font),\n        gpdf.WithDefaultFont(\"IPAexGothic\", 10.5),\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(\"令和8年4月17日発行\")\n            c.Text(\"金額: ¥100,000 (税込)\")\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",[44,1335,1336,1342,1346,1352,1360,1368,1372,1380,1388,1396,1400,1404,1414,1440,1452,1466,1470,1474,1488,1506,1537,1560,1583,1587,1591,1605,1629,1659,1704,1723,1742,1746,1750,1754,1772,1784,1798,1802,1843,1857,1862],{"__ignoreMap":106},[110,1337,1338,1340],{"class":112,"line":113},[110,1339,117],{"class":116},[110,1341,121],{"class":120},[110,1343,1344],{"class":112,"line":124},[110,1345,128],{"emptyLinePlaceholder":127},[110,1347,1348,1350],{"class":112,"line":131},[110,1349,135],{"class":134},[110,1351,138],{"class":116},[110,1353,1354,1356,1358],{"class":112,"line":141},[110,1355,144],{"class":116},[110,1357,147],{"class":120},[110,1359,150],{"class":116},[110,1361,1362,1364,1366],{"class":112,"line":153},[110,1363,144],{"class":116},[110,1365,158],{"class":120},[110,1367,150],{"class":116},[110,1369,1370],{"class":112,"line":163},[110,1371,128],{"emptyLinePlaceholder":127},[110,1373,1374,1376,1378],{"class":112,"line":168},[110,1375,144],{"class":116},[110,1377,173],{"class":120},[110,1379,150],{"class":116},[110,1381,1382,1384,1386],{"class":112,"line":178},[110,1383,144],{"class":116},[110,1385,183],{"class":120},[110,1387,150],{"class":116},[110,1389,1390,1392,1394],{"class":112,"line":188},[110,1391,144],{"class":116},[110,1393,193],{"class":120},[110,1395,150],{"class":116},[110,1397,1398],{"class":112,"line":198},[110,1399,201],{"class":116},[110,1401,1402],{"class":112,"line":204},[110,1403,128],{"emptyLinePlaceholder":127},[110,1405,1406,1408,1410,1412],{"class":112,"line":209},[110,1407,212],{"class":116},[110,1409,216],{"class":215},[110,1411,219],{"class":116},[110,1413,222],{"class":116},[110,1415,1416,1418,1420,1422,1424,1426,1428,1430,1432,1434,1436,1438],{"class":112,"line":225},[110,1417,229],{"class":228},[110,1419,232],{"class":116},[110,1421,235],{"class":228},[110,1423,238],{"class":116},[110,1425,241],{"class":228},[110,1427,60],{"class":116},[110,1429,246],{"class":215},[110,1431,249],{"class":116},[110,1433,252],{"class":116},[110,1435,1317],{"class":255},[110,1437,252],{"class":116},[110,1439,201],{"class":116},[110,1441,1442,1444,1446,1448,1450],{"class":112,"line":262},[110,1443,265],{"class":134},[110,1445,235],{"class":228},[110,1447,270],{"class":116},[110,1449,273],{"class":116},[110,1451,222],{"class":116},[110,1453,1454,1456,1458,1460,1462,1464],{"class":112,"line":278},[110,1455,281],{"class":228},[110,1457,60],{"class":116},[110,1459,286],{"class":215},[110,1461,249],{"class":116},[110,1463,291],{"class":228},[110,1465,201],{"class":116},[110,1467,1468],{"class":112,"line":296},[110,1469,299],{"class":116},[110,1471,1472],{"class":112,"line":302},[110,1473,128],{"emptyLinePlaceholder":127},[110,1475,1476,1478,1480,1482,1484,1486],{"class":112,"line":307},[110,1477,310],{"class":228},[110,1479,238],{"class":116},[110,1481,315],{"class":228},[110,1483,60],{"class":116},[110,1485,320],{"class":215},[110,1487,323],{"class":116},[110,1489,1490,1492,1494,1496,1498,1500,1502,1504],{"class":112,"line":326},[110,1491,329],{"class":228},[110,1493,60],{"class":116},[110,1495,334],{"class":215},[110,1497,249],{"class":116},[110,1499,339],{"class":228},[110,1501,60],{"class":116},[110,1503,344],{"class":228},[110,1505,347],{"class":116},[110,1507,1508,1510,1512,1514,1516,1518,1520,1522,1524,1526,1528,1530,1532,1535],{"class":112,"line":350},[110,1509,329],{"class":228},[110,1511,60],{"class":116},[110,1513,357],{"class":215},[110,1515,249],{"class":116},[110,1517,362],{"class":228},[110,1519,60],{"class":116},[110,1521,367],{"class":215},[110,1523,249],{"class":116},[110,1525,362],{"class":228},[110,1527,60],{"class":116},[110,1529,376],{"class":215},[110,1531,249],{"class":116},[110,1533,1534],{"class":381},"25",[110,1536,385],{"class":116},[110,1538,1539,1541,1543,1545,1547,1549,1552,1554,1556,1558],{"class":112,"line":388},[110,1540,329],{"class":228},[110,1542,60],{"class":116},[110,1544,50],{"class":215},[110,1546,249],{"class":116},[110,1548,252],{"class":116},[110,1550,1551],{"class":255},"IPAexGothic",[110,1553,252],{"class":116},[110,1555,232],{"class":116},[110,1557,408],{"class":228},[110,1559,347],{"class":116},[110,1561,1562,1564,1566,1568,1570,1572,1574,1576,1578,1581],{"class":112,"line":413},[110,1563,329],{"class":228},[110,1565,60],{"class":116},[110,1567,420],{"class":215},[110,1569,249],{"class":116},[110,1571,252],{"class":116},[110,1573,1551],{"class":255},[110,1575,252],{"class":116},[110,1577,232],{"class":116},[110,1579,1580],{"class":381}," 10.5",[110,1582,347],{"class":116},[110,1584,1585],{"class":112,"line":438},[110,1586,441],{"class":116},[110,1588,1589],{"class":112,"line":444},[110,1590,128],{"emptyLinePlaceholder":127},[110,1592,1593,1595,1597,1599,1601,1603],{"class":112,"line":449},[110,1594,452],{"class":228},[110,1596,238],{"class":116},[110,1598,457],{"class":228},[110,1600,60],{"class":116},[110,1602,462],{"class":215},[110,1604,465],{"class":116},[110,1606,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625,1627],{"class":112,"line":468},[110,1608,471],{"class":228},[110,1610,60],{"class":116},[110,1612,476],{"class":215},[110,1614,479],{"class":116},[110,1616,483],{"class":482},[110,1618,486],{"class":116},[110,1620,489],{"class":120},[110,1622,60],{"class":116},[110,1624,494],{"class":120},[110,1626,497],{"class":116},[110,1628,222],{"class":116},[110,1630,1631,1633,1635,1637,1639,1641,1643,1645,1647,1649,1651,1653,1655,1657],{"class":112,"line":502},[110,1632,505],{"class":228},[110,1634,60],{"class":116},[110,1636,510],{"class":215},[110,1638,249],{"class":116},[110,1640,515],{"class":381},[110,1642,232],{"class":116},[110,1644,520],{"class":116},[110,1646,523],{"class":482},[110,1648,486],{"class":116},[110,1650,489],{"class":120},[110,1652,60],{"class":116},[110,1654,532],{"class":120},[110,1656,497],{"class":116},[110,1658,222],{"class":116},[110,1660,1661,1663,1665,1667,1669,1671,1674,1676,1678,1681,1683,1686,1688,1691,1694,1696,1698,1701],{"class":112,"line":539},[110,1662,542],{"class":228},[110,1664,60],{"class":116},[110,1666,547],{"class":215},[110,1668,249],{"class":116},[110,1670,252],{"class":116},[110,1672,1673],{"class":255},"請求書",[110,1675,252],{"class":116},[110,1677,232],{"class":116},[110,1679,1680],{"class":228}," template",[110,1682,60],{"class":116},[110,1684,1685],{"class":215},"FontSize",[110,1687,249],{"class":116},[110,1689,1690],{"class":381},"24",[110,1692,1693],{"class":116},"),",[110,1695,1680],{"class":228},[110,1697,60],{"class":116},[110,1699,1700],{"class":215},"Bold",[110,1702,1703],{"class":116},"())\n",[110,1705,1706,1708,1710,1712,1714,1716,1719,1721],{"class":112,"line":561},[110,1707,542],{"class":228},[110,1709,60],{"class":116},[110,1711,547],{"class":215},[110,1713,249],{"class":116},[110,1715,252],{"class":116},[110,1717,1718],{"class":255},"令和8年4月17日発行",[110,1720,252],{"class":116},[110,1722,201],{"class":116},[110,1724,1725,1727,1729,1731,1733,1735,1738,1740],{"class":112,"line":567},[110,1726,542],{"class":228},[110,1728,60],{"class":116},[110,1730,547],{"class":215},[110,1732,249],{"class":116},[110,1734,252],{"class":116},[110,1736,1737],{"class":255},"金額: ¥100,000 (税込)",[110,1739,252],{"class":116},[110,1741,201],{"class":116},[110,1743,1744],{"class":112,"line":573},[110,1745,564],{"class":116},[110,1747,1748],{"class":112,"line":578},[110,1749,570],{"class":116},[110,1751,1752],{"class":112,"line":599},[110,1753,128],{"emptyLinePlaceholder":127},[110,1755,1756,1758,1760,1762,1764,1766,1768,1770],{"class":112,"line":612},[110,1757,581],{"class":228},[110,1759,232],{"class":116},[110,1761,235],{"class":228},[110,1763,238],{"class":116},[110,1765,457],{"class":228},[110,1767,60],{"class":116},[110,1769,594],{"class":215},[110,1771,465],{"class":116},[110,1773,1774,1776,1778,1780,1782],{"class":112,"line":627},[110,1775,265],{"class":134},[110,1777,235],{"class":228},[110,1779,270],{"class":116},[110,1781,273],{"class":116},[110,1783,222],{"class":116},[110,1785,1786,1788,1790,1792,1794,1796],{"class":112,"line":632},[110,1787,281],{"class":228},[110,1789,60],{"class":116},[110,1791,286],{"class":215},[110,1793,249],{"class":116},[110,1795,291],{"class":228},[110,1797,201],{"class":116},[110,1799,1800],{"class":112,"line":678},[110,1801,299],{"class":116},[110,1803,1804,1806,1808,1810,1812,1814,1816,1818,1820,1823,1825,1827,1829,1831,1833,1835,1837,1839,1841],{"class":112,"line":693},[110,1805,265],{"class":134},[110,1807,235],{"class":228},[110,1809,238],{"class":116},[110,1811,241],{"class":228},[110,1813,60],{"class":116},[110,1815,645],{"class":215},[110,1817,249],{"class":116},[110,1819,252],{"class":116},[110,1821,1822],{"class":255},"invoice.pdf",[110,1824,252],{"class":116},[110,1826,232],{"class":116},[110,1828,659],{"class":228},[110,1830,232],{"class":116},[110,1832,664],{"class":381},[110,1834,667],{"class":116},[110,1836,235],{"class":228},[110,1838,270],{"class":116},[110,1840,273],{"class":116},[110,1842,222],{"class":116},[110,1844,1845,1847,1849,1851,1853,1855],{"class":112,"line":698},[110,1846,281],{"class":228},[110,1848,60],{"class":116},[110,1850,286],{"class":215},[110,1852,249],{"class":116},[110,1854,291],{"class":228},[110,1856,201],{"class":116},[110,1858,1860],{"class":112,"line":1859},40,[110,1861,299],{"class":116},[110,1863,1865],{"class":112,"line":1864},41,[110,1866,701],{"class":116},[19,1868,1869,1870,721,1873,1877,1878,1880,1881,1884,1885,60],{},"Download the ",[44,1871,1872],{},"IPAex00401.zip",[723,1874,1876],{"href":1300,"rel":1875},[727],"moji.or.jp/ipafont",", extract ",[44,1879,1317],{},", drop it next to ",[44,1882,1883],{},"main.go",", and ",[44,1886,1887],{},"go run main.go",[14,1889,1891],{"id":1890},"which-ipa-file-is-the-right-one","Which IPA file is the right one",[19,1893,1894],{},"Open the zip and you get three TTFs plus a license. People mix these up constantly:",[1896,1897,1898,1911],"table",{},[1899,1900,1901],"thead",{},[1902,1903,1904,1908],"tr",{},[1905,1906,1907],"th",{},"File",[1905,1909,1910],{},"What it is",[1912,1913,1914,1927,1940],"tbody",{},[1902,1915,1916,1921],{},[1917,1918,1919],"td",{},[44,1920,1317],{},[1917,1922,1923,1926],{},[39,1924,1925],{},"IPAex Gothic"," — sans-serif, proportional Latin. Use this for most documents.",[1902,1928,1929,1934],{},[1917,1930,1931],{},[44,1932,1933],{},"ipaexm.ttf",[1917,1935,1936,1939],{},[39,1937,1938],{},"IPAex Mincho"," — serif, proportional Latin. Use for body text in long-form documents or to pair with Gothic for emphasis.",[1902,1941,1942,1947],{},[1917,1943,1944],{},[44,1945,1946],{},"ipag.ttf",[1917,1948,1949,1952,1953,1956],{},[39,1950,1951],{},"IPA Gothic"," (no \"ex\") — sans-serif, ",[39,1954,1955],{},"monospace Latin",". Rarely what you want today.",[19,1958,1959],{},"The \"ex\" in IPAex stands for \"extended proportional.\" The original IPA fonts put Latin characters on fixed CJK-width grids, which made mixed J/E text look stretched. IPAex fixes that by making Latin characters proportional while keeping CJK characters on the normal grid. For any document with English loan words, URLs, or numbers — which is basically every business document in Japan — you want IPAex.",[19,1961,1962,1963,1965],{},"If you inherited a project using ",[44,1964,1946],{}," because the engineer who picked the font did it before IPAex existed (original IPA Gothic: 2003, IPAex: 2010), the switch is one file swap. Same family name, same everything else.",[14,1967,1969],{"id":1968},"no-bold-file-now-what","No Bold file — now what",[19,1971,1972],{},"IPAex ships exactly one weight per family: Regular. That's unusual compared to Noto Sans JP (nine weights) and it's the single biggest reason people look at IPAex and decide it won't work for their design.",[19,1974,1975],{},"Two ways to handle it in gpdf:",[19,1977,1978,42,1981,1983],{},[39,1979,1980],{},"Synthesized bold.",[44,1982,1325],{}," applies a stroke overlay on Regular glyphs. Typographically it's a cheat — real bold weights have redrawn outlines with thicker strokes, not Regular traced twice. But for invoice headings and table labels at 10 pt or larger, synthesized bold is indistinguishable to most readers:",[101,1985,1987],{"className":103,"code":1986,"language":105,"meta":106,"style":106},"c.Text(\"合計金額\", template.Bold())\n",[44,1988,1989],{"__ignoreMap":106},[110,1990,1991,1993,1995,1997,1999,2001,2004,2006,2008,2010,2012,2014],{"class":112,"line":113},[110,1992,523],{"class":228},[110,1994,60],{"class":116},[110,1996,547],{"class":215},[110,1998,249],{"class":116},[110,2000,252],{"class":116},[110,2002,2003],{"class":255},"合計金額",[110,2005,252],{"class":116},[110,2007,232],{"class":116},[110,2009,1680],{"class":228},[110,2011,60],{"class":116},[110,2013,1700],{"class":215},[110,2015,1703],{"class":116},[19,2017,2018,2021],{},[39,2019,2020],{},"Pair with IPAex Mincho."," The classic Japanese typography move for emphasis isn't bold — it's a serif/sans switch. Register both families:",[101,2023,2025],{"className":103,"code":2024,"language":105,"meta":106,"style":106},"gothic,  _ := os.ReadFile(\"ipaexg.ttf\")\nmincho, _ := os.ReadFile(\"ipaexm.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"IPAexGothic\", gothic),\n    gpdf.WithFont(\"IPAexMincho\", mincho),\n    gpdf.WithDefaultFont(\"IPAexGothic\", 10.5),\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"請求書\", template.FontFamily(\"IPAexMincho\"), template.FontSize(24))\n        c.Text(\"ご請求内容は下記の通りです。\")\n    })\n})\n",[44,2026,2027,2055,2083,2087,2101,2124,2148,2170,2174,2178,2202,2232,2280,2299,2303],{"__ignoreMap":106},[110,2028,2029,2032,2034,2037,2039,2041,2043,2045,2047,2049,2051,2053],{"class":112,"line":113},[110,2030,2031],{"class":228},"gothic",[110,2033,232],{"class":116},[110,2035,2036],{"class":228},"  _ ",[110,2038,238],{"class":116},[110,2040,241],{"class":228},[110,2042,60],{"class":116},[110,2044,246],{"class":215},[110,2046,249],{"class":116},[110,2048,252],{"class":116},[110,2050,1317],{"class":255},[110,2052,252],{"class":116},[110,2054,201],{"class":116},[110,2056,2057,2060,2062,2065,2067,2069,2071,2073,2075,2077,2079,2081],{"class":112,"line":124},[110,2058,2059],{"class":228},"mincho",[110,2061,232],{"class":116},[110,2063,2064],{"class":228}," _ ",[110,2066,238],{"class":116},[110,2068,241],{"class":228},[110,2070,60],{"class":116},[110,2072,246],{"class":215},[110,2074,249],{"class":116},[110,2076,252],{"class":116},[110,2078,1933],{"class":255},[110,2080,252],{"class":116},[110,2082,201],{"class":116},[110,2084,2085],{"class":112,"line":131},[110,2086,128],{"emptyLinePlaceholder":127},[110,2088,2089,2091,2093,2095,2097,2099],{"class":112,"line":141},[110,2090,801],{"class":228},[110,2092,238],{"class":116},[110,2094,315],{"class":228},[110,2096,60],{"class":116},[110,2098,320],{"class":215},[110,2100,323],{"class":116},[110,2102,2103,2105,2107,2109,2111,2113,2115,2117,2119,2122],{"class":112,"line":153},[110,2104,816],{"class":228},[110,2106,60],{"class":116},[110,2108,50],{"class":215},[110,2110,249],{"class":116},[110,2112,252],{"class":116},[110,2114,1551],{"class":255},[110,2116,252],{"class":116},[110,2118,232],{"class":116},[110,2120,2121],{"class":228}," gothic",[110,2123,347],{"class":116},[110,2125,2126,2128,2130,2132,2134,2136,2139,2141,2143,2146],{"class":112,"line":163},[110,2127,816],{"class":228},[110,2129,60],{"class":116},[110,2131,50],{"class":215},[110,2133,249],{"class":116},[110,2135,252],{"class":116},[110,2137,2138],{"class":255},"IPAexMincho",[110,2140,252],{"class":116},[110,2142,232],{"class":116},[110,2144,2145],{"class":228}," mincho",[110,2147,347],{"class":116},[110,2149,2150,2152,2154,2156,2158,2160,2162,2164,2166,2168],{"class":112,"line":168},[110,2151,816],{"class":228},[110,2153,60],{"class":116},[110,2155,420],{"class":215},[110,2157,249],{"class":116},[110,2159,252],{"class":116},[110,2161,1551],{"class":255},[110,2163,252],{"class":116},[110,2165,232],{"class":116},[110,2167,1580],{"class":381},[110,2169,347],{"class":116},[110,2171,2172],{"class":112,"line":178},[110,2173,201],{"class":116},[110,2175,2176],{"class":112,"line":188},[110,2177,128],{"emptyLinePlaceholder":127},[110,2179,2180,2182,2184,2186,2188,2190,2192,2194,2196,2198,2200],{"class":112,"line":198},[110,2181,853],{"class":228},[110,2183,60],{"class":116},[110,2185,476],{"class":215},[110,2187,479],{"class":116},[110,2189,483],{"class":482},[110,2191,486],{"class":116},[110,2193,489],{"class":120},[110,2195,60],{"class":116},[110,2197,494],{"class":120},[110,2199,497],{"class":116},[110,2201,222],{"class":116},[110,2203,2204,2206,2208,2210,2212,2214,2216,2218,2220,2222,2224,2226,2228,2230],{"class":112,"line":204},[110,2205,878],{"class":228},[110,2207,60],{"class":116},[110,2209,510],{"class":215},[110,2211,249],{"class":116},[110,2213,515],{"class":381},[110,2215,232],{"class":116},[110,2217,520],{"class":116},[110,2219,523],{"class":482},[110,2221,486],{"class":116},[110,2223,489],{"class":120},[110,2225,60],{"class":116},[110,2227,532],{"class":120},[110,2229,497],{"class":116},[110,2231,222],{"class":116},[110,2233,2234,2236,2238,2240,2242,2244,2246,2248,2250,2252,2254,2257,2259,2261,2263,2265,2267,2269,2271,2273,2275,2277],{"class":112,"line":209},[110,2235,909],{"class":228},[110,2237,60],{"class":116},[110,2239,547],{"class":215},[110,2241,249],{"class":116},[110,2243,252],{"class":116},[110,2245,1673],{"class":255},[110,2247,252],{"class":116},[110,2249,232],{"class":116},[110,2251,1680],{"class":228},[110,2253,60],{"class":116},[110,2255,2256],{"class":215},"FontFamily",[110,2258,249],{"class":116},[110,2260,252],{"class":116},[110,2262,2138],{"class":255},[110,2264,252],{"class":116},[110,2266,1693],{"class":116},[110,2268,1680],{"class":228},[110,2270,60],{"class":116},[110,2272,1685],{"class":215},[110,2274,249],{"class":116},[110,2276,1690],{"class":381},[110,2278,2279],{"class":116},"))\n",[110,2281,2282,2284,2286,2288,2290,2292,2295,2297],{"class":112,"line":225},[110,2283,909],{"class":228},[110,2285,60],{"class":116},[110,2287,547],{"class":215},[110,2289,249],{"class":116},[110,2291,252],{"class":116},[110,2293,2294],{"class":255},"ご請求内容は下記の通りです。",[110,2296,252],{"class":116},[110,2298,201],{"class":116},[110,2300,2301],{"class":112,"line":262},[110,2302,570],{"class":116},[110,2304,2305],{"class":112,"line":278},[110,2306,936],{"class":116},[19,2308,2309],{},"That's the look you see on Japanese wedding invitations and formal reports — Mincho for titles, Gothic for body. If your document is going to a government office, this is probably the expected combination anyway.",[14,2311,2313],{"id":2312},"the-ipa-font-license-briefly","The IPA Font License, briefly",[19,2315,2316,2317,2322],{},"IPAex is not SIL OFL. It's the ",[723,2318,2321],{"href":2319,"rel":2320},"https://opensource.org/licenses/IPA",[727],"IPA Font License Agreement v1.0",", which is OSI-approved and mostly permissive but has two asks worth naming:",[33,2324,2325,2339],{},[36,2326,2327,2330,2331,2334,2335,2338],{},[39,2328,2329],{},"Preserve the license text"," wherever you ship the font binary. If you ",[44,2332,2333],{},"//go:embed"," the TTF, also embed the license file. A project-root ",[44,2336,2337],{},"LICENSES/IPA-FONT-1.0.txt"," satisfies this for most distributions.",[36,2340,2341,2344,2345,2348],{},[39,2342,2343],{},"Don't rename the font."," If you modify the TTF itself and redistribute it, you must give the derivative a different name (not containing \"IPA\" or \"IPAex\"). Note: this restriction does ",[39,2346,2347],{},"not"," apply to glyph subsetting performed at render time. Article 3.4 of the license explicitly exempts output documents created using the font from the naming restriction.",[19,2350,2351,2352,2355],{},"Meaning: gpdf's subsetting at ",[44,2353,2354],{},"doc.Generate()"," is fine. The subset font embedded in your PDF doesn't need a different name and doesn't trigger the \"Derivative Font Program\" clauses. You are creating a document, not redistributing a font.",[19,2357,2358],{},"One thing to flag for contributors building gpdf itself: we avoid shipping IPAex in the gpdf repo (golden file tests use SIL OFL fonts like Noto) precisely so downstream users never have to think about license compatibility with their repo's top-level LICENSE. If you use IPAex in your application, that's your project's choice, not ours.",[14,2360,2362],{"id":2361},"when-to-pick-ipaex-over-noto-sans-jp","When to pick IPAex over Noto Sans JP",[1896,2364,2365,2377],{},[1899,2366,2367],{},[1902,2368,2369,2372,2374],{},[1905,2370,2371],{},"Dimension",[1905,2373,1925],{},[1905,2375,2376],{},"Noto Sans JP",[1912,2378,2379,2390,2401,2412,2426,2437],{},[1902,2380,2381,2384,2387],{},[1917,2382,2383],{},"Weights shipped",[1917,2385,2386],{},"1 (Regular)",[1917,2388,2389],{},"9 (Thin → Black)",[1902,2391,2392,2395,2398],{},[1917,2393,2394],{},"License",[1917,2396,2397],{},"IPA Font License v1.0",[1917,2399,2400],{},"SIL OFL 1.1",[1902,2402,2403,2406,2409],{},[1917,2404,2405],{},"Latin handling",[1917,2407,2408],{},"Proportional (IPAex) or monospace (IPA)",[1917,2410,2411],{},"Proportional",[1902,2413,2414,2417,2423],{},[1917,2415,2416],{},"Pre-installed on",[1917,2418,2419,2420],{},"Some JP Linux distros, TeX Live ",[44,2421,2422],{},"ptex-fonts",[1917,2424,2425],{},"Android, ChromeOS",[1902,2427,2428,2431,2434],{},[1917,2429,2430],{},"Typical audience",[1917,2432,2433],{},"Japanese government, legal, academic",[1917,2435,2436],{},"Consumer web, international",[1902,2438,2439,2442,2445],{},[1917,2440,2441],{},"File size",[1917,2443,2444],{},"7.5 MB (Gothic)",[1917,2446,2447],{},"5 MB (Regular only)",[19,2449,2450],{},"Pick IPAex when your output crosses a Japanese institutional boundary — e-Tax PDF submissions, court filings, academic paper submissions to a Japanese journal — because graders, reviewers, and OCR tools in those ecosystems are calibrated against IPA. Pick Noto Sans JP for everything else. They render very similarly; the choice is about ecosystem fit, not aesthetics.",[14,2452,2454],{"id":2453},"related-reading","Related reading",[985,2456,2457,2462,2467,2472],{},[36,2458,2459,2461],{},[723,2460,1175],{"href":1174}," — the general recipe, works for any CJK TTF",[36,2463,2464,2466],{},[723,2465,1185],{"href":1184}," — the SIL OFL alternative with nine weights",[36,2468,2469,2471],{},[723,2470,6],{"href":1279}," — troubleshooting when glyphs aren't showing up",[36,2473,2474,1176,2479,2481],{},[723,2475,2478],{"href":2476,"rel":2477},"https://gpdf.dev/docs/guide/fonts",[727],"Fonts guide",[44,2480,50],{}," reference including variant naming",[14,2483,1201],{"id":1200},[19,2485,1204],{},[101,2487,2488],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,2489,2490],{"__ignoreMap":106},[110,2491,2492,2494,2496],{"class":112,"line":113},[110,2493,105],{"class":120},[110,2495,1218],{"class":255},[110,2497,1221],{"class":255},[19,2499,2500,1229,2503],{},[723,2501,1228],{"href":1226,"rel":2502},[727],[723,2504,1234],{"href":1232,"rel":2505},[727],[1236,2507,2508],{},"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":106,"searchDepth":124,"depth":124,"links":2510},[2511,2512,2513,2514,2515,2516,2517,2518,2519],{"id":16,"depth":124,"text":17},{"id":1310,"depth":124,"text":1311},{"id":1329,"depth":124,"text":1330},{"id":1890,"depth":124,"text":1891},{"id":1968,"depth":124,"text":1969},{"id":2312,"depth":124,"text":2313},{"id":2361,"depth":124,"text":2362},{"id":2453,"depth":124,"text":2454},{"id":1200,"depth":124,"text":1201},"Register ipaexg.ttf with gpdf.WithFont. IPAex Gothic ships a single Regular weight under the IPA Font License — bold emphasis has to be synthesized or paired.",{"name":2522,"totalTime":2523,"tools":2524,"steps":2526},"Use IPAex Gothic as the default font in a gpdf document","PT10M",[1259,2525],"ipaexg.ttf (IPAex Gothic v4.01 from moji.or.jp)",[2527,2530,2533,2536,2539],{"name":2528,"text":2529},"Download the IPAex font archive from moji.or.jp","Grab the IPAex00401.zip from moji.or.jp/ipafont. Unzip and keep ipaexg.ttf (Gothic) and the IPA Font License Agreement v1.0 text file that travels with it.",{"name":2531,"text":2532},"Load the TTF bytes","Use os.ReadFile(\"ipaexg.ttf\") to load the font into a []byte at program start. For container deployments, //go:embed keeps the font inside the Go binary.",{"name":2534,"text":2535},"Register the font at document construction","Pass gpdf.WithFont(\"IPAexGothic\", fontBytes) and gpdf.WithDefaultFont(\"IPAexGothic\", 10.5) to gpdf.NewDocument. 10.5 pt matches the default size Word uses for Japanese documents.",{"name":2537,"text":2538},"Handle bold emphasis without a Bold file","IPAex Gothic has no Bold variant. Either synthesize bold with template.Bold() (gpdf applies a 0.4 pt stroke overlay) or register IPAex Mincho as a separate family for emphasis.",{"name":2540,"text":2541},"Keep the license notice with your distribution","The IPA Font License v1.0 requires preserving the license text wherever the font binary ships. If you //go:embed the TTF, embed the license alongside it and reference it from LICENSES/NOTICE.",{},"/blog/ipaex-gothic-gpdf",{"title":1289,"description":2520},"blog/009.ipaex-gothic-gpdf",[1283,1285,2547],"tutorial","MT9s2SZvJpmof-qrvnFeO4uEtYvwB7hPRkOjr_X7hto",{"id":2550,"title":2551,"author":2552,"body":2553,"date":3037,"description":3873,"draft":1253,"extension":1254,"howTo":3874,"image":1277,"meta":3893,"navigation":127,"path":3894,"seo":3895,"stem":3896,"tags":3897,"updated":1277,"__hash__":3899},"blog/blog/005.12-column-grid.md","How does the 12-column grid work in gpdf?",{"name":8,"url":9},{"type":11,"value":2554,"toc":3861},[2555,2557,2568,2572,2585,2589,3534,3540,3544,3547,3551,3566,3577,3581,3584,3598,3612,3616,3622,3628,3783,3787,3793,3804,3807,3810,3812,3834,3836,3838,3850,3858],[14,2556,17],{"id":16},[19,2558,2559,2560,2563,2564,2567],{},"You've seen the gpdf API — page builder, row builder, column builder — and the column constructor takes a number: ",[44,2561,2562],{},"r.Col(4, fn)",", ",[44,2565,2566],{},"r.Col(8, fn)",". What's the number, what happens if the spans don't add up to 12, and how does this compare to the grid you already know from CSS?",[14,2569,2571],{"id":2570},"the-short-version","The short version",[19,2573,2574,2577,2578,2581,2582],{},[44,2575,2576],{},"r.Col(span, fn)"," takes an integer from 1 to 12. That integer is the column's share of the row — ",[44,2579,2580],{},"span / 12"," of the available width. Spans under 1 are clamped to 1, spans over 12 are clamped to 12, and the library does not force spans to sum to 12 per row. ",[39,2583,2584],{},"The grid is fixed at 12 divisions. Everything else is you choosing how to carve up a row.",[14,2586,2588],{"id":2587},"a-working-example","A working example",[101,2590,2592],{"className":103,"code":2591,"language":105,"meta":106,"style":106},"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    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(15))),\n    )\n\n    page := doc.AddPage()\n\n    // Full width\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Invoice #2026-0416\", template.FontSize(18), template.Bold())\n        })\n    })\n\n    // 2-column header (6 + 6)\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"Billed to\")\n            c.Text(\"Acme GmbH\")\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"Issue date\")\n            c.Text(\"2026-04-16\")\n        })\n    })\n\n    // 3-column summary (4 + 4 + 4)\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Subtotal\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Tax\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Total\")\n        })\n    })\n\n    // Asymmetric (8 + 4) — article area + side panel\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(8, func(c *template.ColBuilder) {\n            c.Text(\"Line items appear here.\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Notes\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"layout.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[44,2593,2594,2600,2604,2610,2618,2626,2630,2638,2646,2654,2658,2662,2672,2686,2704,2735,2739,2743,2757,2761,2766,2790,2820,2860,2864,2868,2872,2877,2901,2932,2951,2970,2974,3004,3023,3042,3046,3050,3054,3059,3083,3114,3134,3139,3170,3190,3195,3226,3246,3251,3256,3261,3267,3292,3324,3344,3349,3380,3400,3405,3410,3415,3434,3447,3462,3467,3509,3524,3529],{"__ignoreMap":106},[110,2595,2596,2598],{"class":112,"line":113},[110,2597,117],{"class":116},[110,2599,121],{"class":120},[110,2601,2602],{"class":112,"line":124},[110,2603,128],{"emptyLinePlaceholder":127},[110,2605,2606,2608],{"class":112,"line":131},[110,2607,135],{"class":134},[110,2609,138],{"class":116},[110,2611,2612,2614,2616],{"class":112,"line":141},[110,2613,144],{"class":116},[110,2615,147],{"class":120},[110,2617,150],{"class":116},[110,2619,2620,2622,2624],{"class":112,"line":153},[110,2621,144],{"class":116},[110,2623,158],{"class":120},[110,2625,150],{"class":116},[110,2627,2628],{"class":112,"line":163},[110,2629,128],{"emptyLinePlaceholder":127},[110,2631,2632,2634,2636],{"class":112,"line":168},[110,2633,144],{"class":116},[110,2635,173],{"class":120},[110,2637,150],{"class":116},[110,2639,2640,2642,2644],{"class":112,"line":178},[110,2641,144],{"class":116},[110,2643,183],{"class":120},[110,2645,150],{"class":116},[110,2647,2648,2650,2652],{"class":112,"line":188},[110,2649,144],{"class":116},[110,2651,193],{"class":120},[110,2653,150],{"class":116},[110,2655,2656],{"class":112,"line":198},[110,2657,201],{"class":116},[110,2659,2660],{"class":112,"line":204},[110,2661,128],{"emptyLinePlaceholder":127},[110,2663,2664,2666,2668,2670],{"class":112,"line":209},[110,2665,212],{"class":116},[110,2667,216],{"class":215},[110,2669,219],{"class":116},[110,2671,222],{"class":116},[110,2673,2674,2676,2678,2680,2682,2684],{"class":112,"line":225},[110,2675,310],{"class":228},[110,2677,238],{"class":116},[110,2679,315],{"class":228},[110,2681,60],{"class":116},[110,2683,320],{"class":215},[110,2685,323],{"class":116},[110,2687,2688,2690,2692,2694,2696,2698,2700,2702],{"class":112,"line":262},[110,2689,329],{"class":228},[110,2691,60],{"class":116},[110,2693,334],{"class":215},[110,2695,249],{"class":116},[110,2697,362],{"class":228},[110,2699,60],{"class":116},[110,2701,344],{"class":228},[110,2703,347],{"class":116},[110,2705,2706,2708,2710,2712,2714,2716,2718,2720,2722,2724,2726,2728,2730,2733],{"class":112,"line":278},[110,2707,329],{"class":228},[110,2709,60],{"class":116},[110,2711,357],{"class":215},[110,2713,249],{"class":116},[110,2715,362],{"class":228},[110,2717,60],{"class":116},[110,2719,367],{"class":215},[110,2721,249],{"class":116},[110,2723,362],{"class":228},[110,2725,60],{"class":116},[110,2727,376],{"class":215},[110,2729,249],{"class":116},[110,2731,2732],{"class":381},"15",[110,2734,385],{"class":116},[110,2736,2737],{"class":112,"line":296},[110,2738,441],{"class":116},[110,2740,2741],{"class":112,"line":302},[110,2742,128],{"emptyLinePlaceholder":127},[110,2744,2745,2747,2749,2751,2753,2755],{"class":112,"line":307},[110,2746,452],{"class":228},[110,2748,238],{"class":116},[110,2750,457],{"class":228},[110,2752,60],{"class":116},[110,2754,462],{"class":215},[110,2756,465],{"class":116},[110,2758,2759],{"class":112,"line":326},[110,2760,128],{"emptyLinePlaceholder":127},[110,2762,2763],{"class":112,"line":350},[110,2764,2765],{"class":839},"    // Full width\n",[110,2767,2768,2770,2772,2774,2776,2778,2780,2782,2784,2786,2788],{"class":112,"line":388},[110,2769,471],{"class":228},[110,2771,60],{"class":116},[110,2773,476],{"class":215},[110,2775,479],{"class":116},[110,2777,483],{"class":482},[110,2779,486],{"class":116},[110,2781,489],{"class":120},[110,2783,60],{"class":116},[110,2785,494],{"class":120},[110,2787,497],{"class":116},[110,2789,222],{"class":116},[110,2791,2792,2794,2796,2798,2800,2802,2804,2806,2808,2810,2812,2814,2816,2818],{"class":112,"line":413},[110,2793,505],{"class":228},[110,2795,60],{"class":116},[110,2797,510],{"class":215},[110,2799,249],{"class":116},[110,2801,515],{"class":381},[110,2803,232],{"class":116},[110,2805,520],{"class":116},[110,2807,523],{"class":482},[110,2809,486],{"class":116},[110,2811,489],{"class":120},[110,2813,60],{"class":116},[110,2815,532],{"class":120},[110,2817,497],{"class":116},[110,2819,222],{"class":116},[110,2821,2822,2824,2826,2828,2830,2832,2835,2837,2839,2841,2843,2845,2847,2850,2852,2854,2856,2858],{"class":112,"line":438},[110,2823,542],{"class":228},[110,2825,60],{"class":116},[110,2827,547],{"class":215},[110,2829,249],{"class":116},[110,2831,252],{"class":116},[110,2833,2834],{"class":255},"Invoice #2026-0416",[110,2836,252],{"class":116},[110,2838,232],{"class":116},[110,2840,1680],{"class":228},[110,2842,60],{"class":116},[110,2844,1685],{"class":215},[110,2846,249],{"class":116},[110,2848,2849],{"class":381},"18",[110,2851,1693],{"class":116},[110,2853,1680],{"class":228},[110,2855,60],{"class":116},[110,2857,1700],{"class":215},[110,2859,1703],{"class":116},[110,2861,2862],{"class":112,"line":444},[110,2863,564],{"class":116},[110,2865,2866],{"class":112,"line":449},[110,2867,570],{"class":116},[110,2869,2870],{"class":112,"line":468},[110,2871,128],{"emptyLinePlaceholder":127},[110,2873,2874],{"class":112,"line":502},[110,2875,2876],{"class":839},"    // 2-column header (6 + 6)\n",[110,2878,2879,2881,2883,2885,2887,2889,2891,2893,2895,2897,2899],{"class":112,"line":539},[110,2880,471],{"class":228},[110,2882,60],{"class":116},[110,2884,476],{"class":215},[110,2886,479],{"class":116},[110,2888,483],{"class":482},[110,2890,486],{"class":116},[110,2892,489],{"class":120},[110,2894,60],{"class":116},[110,2896,494],{"class":120},[110,2898,497],{"class":116},[110,2900,222],{"class":116},[110,2902,2903,2905,2907,2909,2911,2914,2916,2918,2920,2922,2924,2926,2928,2930],{"class":112,"line":561},[110,2904,505],{"class":228},[110,2906,60],{"class":116},[110,2908,510],{"class":215},[110,2910,249],{"class":116},[110,2912,2913],{"class":381},"6",[110,2915,232],{"class":116},[110,2917,520],{"class":116},[110,2919,523],{"class":482},[110,2921,486],{"class":116},[110,2923,489],{"class":120},[110,2925,60],{"class":116},[110,2927,532],{"class":120},[110,2929,497],{"class":116},[110,2931,222],{"class":116},[110,2933,2934,2936,2938,2940,2942,2944,2947,2949],{"class":112,"line":567},[110,2935,542],{"class":228},[110,2937,60],{"class":116},[110,2939,547],{"class":215},[110,2941,249],{"class":116},[110,2943,252],{"class":116},[110,2945,2946],{"class":255},"Billed to",[110,2948,252],{"class":116},[110,2950,201],{"class":116},[110,2952,2953,2955,2957,2959,2961,2963,2966,2968],{"class":112,"line":573},[110,2954,542],{"class":228},[110,2956,60],{"class":116},[110,2958,547],{"class":215},[110,2960,249],{"class":116},[110,2962,252],{"class":116},[110,2964,2965],{"class":255},"Acme GmbH",[110,2967,252],{"class":116},[110,2969,201],{"class":116},[110,2971,2972],{"class":112,"line":578},[110,2973,564],{"class":116},[110,2975,2976,2978,2980,2982,2984,2986,2988,2990,2992,2994,2996,2998,3000,3002],{"class":112,"line":599},[110,2977,505],{"class":228},[110,2979,60],{"class":116},[110,2981,510],{"class":215},[110,2983,249],{"class":116},[110,2985,2913],{"class":381},[110,2987,232],{"class":116},[110,2989,520],{"class":116},[110,2991,523],{"class":482},[110,2993,486],{"class":116},[110,2995,489],{"class":120},[110,2997,60],{"class":116},[110,2999,532],{"class":120},[110,3001,497],{"class":116},[110,3003,222],{"class":116},[110,3005,3006,3008,3010,3012,3014,3016,3019,3021],{"class":112,"line":612},[110,3007,542],{"class":228},[110,3009,60],{"class":116},[110,3011,547],{"class":215},[110,3013,249],{"class":116},[110,3015,252],{"class":116},[110,3017,3018],{"class":255},"Issue date",[110,3020,252],{"class":116},[110,3022,201],{"class":116},[110,3024,3025,3027,3029,3031,3033,3035,3038,3040],{"class":112,"line":627},[110,3026,542],{"class":228},[110,3028,60],{"class":116},[110,3030,547],{"class":215},[110,3032,249],{"class":116},[110,3034,252],{"class":116},[110,3036,3037],{"class":255},"2026-04-16",[110,3039,252],{"class":116},[110,3041,201],{"class":116},[110,3043,3044],{"class":112,"line":632},[110,3045,564],{"class":116},[110,3047,3048],{"class":112,"line":678},[110,3049,570],{"class":116},[110,3051,3052],{"class":112,"line":693},[110,3053,128],{"emptyLinePlaceholder":127},[110,3055,3056],{"class":112,"line":698},[110,3057,3058],{"class":839},"    // 3-column summary (4 + 4 + 4)\n",[110,3060,3061,3063,3065,3067,3069,3071,3073,3075,3077,3079,3081],{"class":112,"line":1859},[110,3062,471],{"class":228},[110,3064,60],{"class":116},[110,3066,476],{"class":215},[110,3068,479],{"class":116},[110,3070,483],{"class":482},[110,3072,486],{"class":116},[110,3074,489],{"class":120},[110,3076,60],{"class":116},[110,3078,494],{"class":120},[110,3080,497],{"class":116},[110,3082,222],{"class":116},[110,3084,3085,3087,3089,3091,3093,3096,3098,3100,3102,3104,3106,3108,3110,3112],{"class":112,"line":1864},[110,3086,505],{"class":228},[110,3088,60],{"class":116},[110,3090,510],{"class":215},[110,3092,249],{"class":116},[110,3094,3095],{"class":381},"4",[110,3097,232],{"class":116},[110,3099,520],{"class":116},[110,3101,523],{"class":482},[110,3103,486],{"class":116},[110,3105,489],{"class":120},[110,3107,60],{"class":116},[110,3109,532],{"class":120},[110,3111,497],{"class":116},[110,3113,222],{"class":116},[110,3115,3117,3119,3121,3123,3125,3127,3130,3132],{"class":112,"line":3116},42,[110,3118,542],{"class":228},[110,3120,60],{"class":116},[110,3122,547],{"class":215},[110,3124,249],{"class":116},[110,3126,252],{"class":116},[110,3128,3129],{"class":255},"Subtotal",[110,3131,252],{"class":116},[110,3133,201],{"class":116},[110,3135,3137],{"class":112,"line":3136},43,[110,3138,564],{"class":116},[110,3140,3142,3144,3146,3148,3150,3152,3154,3156,3158,3160,3162,3164,3166,3168],{"class":112,"line":3141},44,[110,3143,505],{"class":228},[110,3145,60],{"class":116},[110,3147,510],{"class":215},[110,3149,249],{"class":116},[110,3151,3095],{"class":381},[110,3153,232],{"class":116},[110,3155,520],{"class":116},[110,3157,523],{"class":482},[110,3159,486],{"class":116},[110,3161,489],{"class":120},[110,3163,60],{"class":116},[110,3165,532],{"class":120},[110,3167,497],{"class":116},[110,3169,222],{"class":116},[110,3171,3173,3175,3177,3179,3181,3183,3186,3188],{"class":112,"line":3172},45,[110,3174,542],{"class":228},[110,3176,60],{"class":116},[110,3178,547],{"class":215},[110,3180,249],{"class":116},[110,3182,252],{"class":116},[110,3184,3185],{"class":255},"Tax",[110,3187,252],{"class":116},[110,3189,201],{"class":116},[110,3191,3193],{"class":112,"line":3192},46,[110,3194,564],{"class":116},[110,3196,3198,3200,3202,3204,3206,3208,3210,3212,3214,3216,3218,3220,3222,3224],{"class":112,"line":3197},47,[110,3199,505],{"class":228},[110,3201,60],{"class":116},[110,3203,510],{"class":215},[110,3205,249],{"class":116},[110,3207,3095],{"class":381},[110,3209,232],{"class":116},[110,3211,520],{"class":116},[110,3213,523],{"class":482},[110,3215,486],{"class":116},[110,3217,489],{"class":120},[110,3219,60],{"class":116},[110,3221,532],{"class":120},[110,3223,497],{"class":116},[110,3225,222],{"class":116},[110,3227,3229,3231,3233,3235,3237,3239,3242,3244],{"class":112,"line":3228},48,[110,3230,542],{"class":228},[110,3232,60],{"class":116},[110,3234,547],{"class":215},[110,3236,249],{"class":116},[110,3238,252],{"class":116},[110,3240,3241],{"class":255},"Total",[110,3243,252],{"class":116},[110,3245,201],{"class":116},[110,3247,3249],{"class":112,"line":3248},49,[110,3250,564],{"class":116},[110,3252,3254],{"class":112,"line":3253},50,[110,3255,570],{"class":116},[110,3257,3259],{"class":112,"line":3258},51,[110,3260,128],{"emptyLinePlaceholder":127},[110,3262,3264],{"class":112,"line":3263},52,[110,3265,3266],{"class":839},"    // Asymmetric (8 + 4) — article area + side panel\n",[110,3268,3270,3272,3274,3276,3278,3280,3282,3284,3286,3288,3290],{"class":112,"line":3269},53,[110,3271,471],{"class":228},[110,3273,60],{"class":116},[110,3275,476],{"class":215},[110,3277,479],{"class":116},[110,3279,483],{"class":482},[110,3281,486],{"class":116},[110,3283,489],{"class":120},[110,3285,60],{"class":116},[110,3287,494],{"class":120},[110,3289,497],{"class":116},[110,3291,222],{"class":116},[110,3293,3295,3297,3299,3301,3303,3306,3308,3310,3312,3314,3316,3318,3320,3322],{"class":112,"line":3294},54,[110,3296,505],{"class":228},[110,3298,60],{"class":116},[110,3300,510],{"class":215},[110,3302,249],{"class":116},[110,3304,3305],{"class":381},"8",[110,3307,232],{"class":116},[110,3309,520],{"class":116},[110,3311,523],{"class":482},[110,3313,486],{"class":116},[110,3315,489],{"class":120},[110,3317,60],{"class":116},[110,3319,532],{"class":120},[110,3321,497],{"class":116},[110,3323,222],{"class":116},[110,3325,3327,3329,3331,3333,3335,3337,3340,3342],{"class":112,"line":3326},55,[110,3328,542],{"class":228},[110,3330,60],{"class":116},[110,3332,547],{"class":215},[110,3334,249],{"class":116},[110,3336,252],{"class":116},[110,3338,3339],{"class":255},"Line items appear here.",[110,3341,252],{"class":116},[110,3343,201],{"class":116},[110,3345,3347],{"class":112,"line":3346},56,[110,3348,564],{"class":116},[110,3350,3352,3354,3356,3358,3360,3362,3364,3366,3368,3370,3372,3374,3376,3378],{"class":112,"line":3351},57,[110,3353,505],{"class":228},[110,3355,60],{"class":116},[110,3357,510],{"class":215},[110,3359,249],{"class":116},[110,3361,3095],{"class":381},[110,3363,232],{"class":116},[110,3365,520],{"class":116},[110,3367,523],{"class":482},[110,3369,486],{"class":116},[110,3371,489],{"class":120},[110,3373,60],{"class":116},[110,3375,532],{"class":120},[110,3377,497],{"class":116},[110,3379,222],{"class":116},[110,3381,3383,3385,3387,3389,3391,3393,3396,3398],{"class":112,"line":3382},58,[110,3384,542],{"class":228},[110,3386,60],{"class":116},[110,3388,547],{"class":215},[110,3390,249],{"class":116},[110,3392,252],{"class":116},[110,3394,3395],{"class":255},"Notes",[110,3397,252],{"class":116},[110,3399,201],{"class":116},[110,3401,3403],{"class":112,"line":3402},59,[110,3404,564],{"class":116},[110,3406,3408],{"class":112,"line":3407},60,[110,3409,570],{"class":116},[110,3411,3413],{"class":112,"line":3412},61,[110,3414,128],{"emptyLinePlaceholder":127},[110,3416,3418,3420,3422,3424,3426,3428,3430,3432],{"class":112,"line":3417},62,[110,3419,581],{"class":228},[110,3421,232],{"class":116},[110,3423,235],{"class":228},[110,3425,238],{"class":116},[110,3427,457],{"class":228},[110,3429,60],{"class":116},[110,3431,594],{"class":215},[110,3433,465],{"class":116},[110,3435,3437,3439,3441,3443,3445],{"class":112,"line":3436},63,[110,3438,265],{"class":134},[110,3440,235],{"class":228},[110,3442,270],{"class":116},[110,3444,273],{"class":116},[110,3446,222],{"class":116},[110,3448,3450,3452,3454,3456,3458,3460],{"class":112,"line":3449},64,[110,3451,281],{"class":228},[110,3453,60],{"class":116},[110,3455,286],{"class":215},[110,3457,249],{"class":116},[110,3459,291],{"class":228},[110,3461,201],{"class":116},[110,3463,3465],{"class":112,"line":3464},65,[110,3466,299],{"class":116},[110,3468,3470,3472,3474,3476,3478,3480,3482,3484,3486,3489,3491,3493,3495,3497,3499,3501,3503,3505,3507],{"class":112,"line":3469},66,[110,3471,265],{"class":134},[110,3473,235],{"class":228},[110,3475,238],{"class":116},[110,3477,241],{"class":228},[110,3479,60],{"class":116},[110,3481,645],{"class":215},[110,3483,249],{"class":116},[110,3485,252],{"class":116},[110,3487,3488],{"class":255},"layout.pdf",[110,3490,252],{"class":116},[110,3492,232],{"class":116},[110,3494,659],{"class":228},[110,3496,232],{"class":116},[110,3498,664],{"class":381},[110,3500,667],{"class":116},[110,3502,235],{"class":228},[110,3504,270],{"class":116},[110,3506,273],{"class":116},[110,3508,222],{"class":116},[110,3510,3512,3514,3516,3518,3520,3522],{"class":112,"line":3511},67,[110,3513,281],{"class":228},[110,3515,60],{"class":116},[110,3517,286],{"class":215},[110,3519,249],{"class":116},[110,3521,291],{"class":228},[110,3523,201],{"class":116},[110,3525,3527],{"class":112,"line":3526},68,[110,3528,299],{"class":116},[110,3530,3532],{"class":112,"line":3531},69,[110,3533,701],{"class":116},[19,3535,3536,3537,3539],{},"Run ",[44,3538,1887],{},". You get one page with four rows, each divided differently.",[14,3541,3543],{"id":3542},"why-12","Why 12",[19,3545,3546],{},"Twelve factors cleanly into 2, 3, 4, and 6. That covers almost every real layout: halves (6+6), thirds (4+4+4), quarters (3+3+3+3), a sidebar + body (3+9 or 4+8), and a body + rail (8+4). Pick a grid with a smaller factor count and you lose one of those cheaply. Bootstrap settled on twelve back in 2011, and by now \"12-column grid\" is the lingua franca that every designer and frontend engineer already speaks. gpdf borrows the idiom on purpose — a PDF layout isn't a different mental model than a web layout, even though the rendering target happens to be fixed-width paper.",[14,3548,3550],{"id":3549},"the-math-spelled-out","The math, spelled out",[19,3552,3553,3554,3557,3558,3561,3562,3565],{},"With A4 portrait and 15 mm uniform margins, the usable width is 180 mm. A ",[44,3555,3556],{},"Col(4)"," inside a row takes 4/12 of that — 60 mm. ",[44,3559,3560],{},"Col(8)"," takes 120 mm. There is no gutter between columns by default. If you want breathing room, either add a ",[44,3563,3564],{},"c.Spacer"," inside the shorter column or leave a one-unit span empty.",[19,3567,3568,3569,3572,3573,3576],{},"The width is computed as a percentage at build time (the relevant line is in ",[44,3570,3571],{},"gpdf/template/grid.go","), and the layout engine resolves it to points using the current page width minus margins. That means the same ",[44,3574,3575],{},"r.Col(6, fn)"," means different physical widths on A4 vs Letter, but the same proportion of the row.",[14,3578,3580],{"id":3579},"sums-that-dont-hit-12","Sums that don't hit 12",[19,3582,3583],{},"gpdf does not validate that your spans add up. This is deliberate.",[985,3585,3586,3592],{},[36,3587,3588,3591],{},[39,3589,3590],{},"Sum \u003C 12",": the right side of the row is blank. Useful when you want a column anchored to the left edge and the rest of the line to stay empty.",[36,3593,3594,3597],{},[39,3595,3596],{},"Sum > 12",": the last column overflows past the right margin. Usually a bug. The generated PDF looks wrong; nothing crashes.",[19,3599,3600,3601,3604,3605,3608,3609,3611],{},"Most layouts land on exactly 12 per row because that's what fills the page. But when you want a 6-width block centered on a line, the cheap move is ",[44,3602,3603],{},"Col(3)"," empty, ",[44,3606,3607],{},"Col(6)"," content, ",[44,3610,3603],{}," empty — the grid was designed for this kind of shorthand.",[14,3613,3615],{"id":3614},"autorow-vs-row","AutoRow vs Row",[19,3617,3618,3621],{},[44,3619,3620],{},"page.AutoRow(fn)"," grows vertically to fit the tallest column. Most rows should use this.",[19,3623,3624,3627],{},[44,3625,3626],{},"page.Row(height, fn)"," pins the height. Content past that height gets clipped. Use it for invoice headers that must stay exactly 30 mm tall so downstream stapling lines up, and for anything else where visual consistency beats content freedom.",[101,3629,3631],{"className":103,"code":3630,"language":105,"meta":106,"style":106},"page.Row(document.Mm(30), func(r *template.RowBuilder) {\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"Logo\")\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"Invoice #\")\n    })\n})\n",[44,3632,3633,3673,3703,3722,3726,3756,3775,3779],{"__ignoreMap":106},[110,3634,3635,3637,3639,3642,3644,3646,3648,3650,3652,3655,3657,3659,3661,3663,3665,3667,3669,3671],{"class":112,"line":113},[110,3636,853],{"class":228},[110,3638,60],{"class":116},[110,3640,3641],{"class":215},"Row",[110,3643,249],{"class":116},[110,3645,362],{"class":228},[110,3647,60],{"class":116},[110,3649,376],{"class":215},[110,3651,249],{"class":116},[110,3653,3654],{"class":381},"30",[110,3656,1693],{"class":116},[110,3658,520],{"class":116},[110,3660,483],{"class":482},[110,3662,486],{"class":116},[110,3664,489],{"class":120},[110,3666,60],{"class":116},[110,3668,494],{"class":120},[110,3670,497],{"class":116},[110,3672,222],{"class":116},[110,3674,3675,3677,3679,3681,3683,3685,3687,3689,3691,3693,3695,3697,3699,3701],{"class":112,"line":124},[110,3676,878],{"class":228},[110,3678,60],{"class":116},[110,3680,510],{"class":215},[110,3682,249],{"class":116},[110,3684,3305],{"class":381},[110,3686,232],{"class":116},[110,3688,520],{"class":116},[110,3690,523],{"class":482},[110,3692,486],{"class":116},[110,3694,489],{"class":120},[110,3696,60],{"class":116},[110,3698,532],{"class":120},[110,3700,497],{"class":116},[110,3702,222],{"class":116},[110,3704,3705,3707,3709,3711,3713,3715,3718,3720],{"class":112,"line":131},[110,3706,909],{"class":228},[110,3708,60],{"class":116},[110,3710,547],{"class":215},[110,3712,249],{"class":116},[110,3714,252],{"class":116},[110,3716,3717],{"class":255},"Logo",[110,3719,252],{"class":116},[110,3721,201],{"class":116},[110,3723,3724],{"class":112,"line":141},[110,3725,570],{"class":116},[110,3727,3728,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3754],{"class":112,"line":153},[110,3729,878],{"class":228},[110,3731,60],{"class":116},[110,3733,510],{"class":215},[110,3735,249],{"class":116},[110,3737,3095],{"class":381},[110,3739,232],{"class":116},[110,3741,520],{"class":116},[110,3743,523],{"class":482},[110,3745,486],{"class":116},[110,3747,489],{"class":120},[110,3749,60],{"class":116},[110,3751,532],{"class":120},[110,3753,497],{"class":116},[110,3755,222],{"class":116},[110,3757,3758,3760,3762,3764,3766,3768,3771,3773],{"class":112,"line":163},[110,3759,909],{"class":228},[110,3761,60],{"class":116},[110,3763,547],{"class":215},[110,3765,249],{"class":116},[110,3767,252],{"class":116},[110,3769,3770],{"class":255},"Invoice #",[110,3772,252],{"class":116},[110,3774,201],{"class":116},[110,3776,3777],{"class":112,"line":168},[110,3778,570],{"class":116},[110,3780,3781],{"class":112,"line":178},[110,3782,936],{"class":116},[14,3784,3786],{"id":3785},"what-the-grid-does-not-do","What the grid does not do",[19,3788,3789,3790,3792],{},"No nesting. You can't put a sub-row inside a column — ",[44,3791,532],{}," accepts content (Text, Image, Table, List, Spacer), not another row. Layouts that need that pattern are usually better expressed as two sibling rows at the page level.",[19,3794,3795,3796,3799,3800,3803],{},"No offset columns. Bootstrap has ",[44,3797,3798],{},".offset-2","; gpdf does not. Leave an empty ",[44,3801,3802],{},"Col(n)"," to push content right.",[19,3805,3806],{},"No breakpoints. PDF pages don't resize. The grid produces the same layout on every device because the output is a raster of fixed coordinates, not a DOM that re-flows.",[19,3808,3809],{},"These omissions are the point. Every feature the grid doesn't have is a class of ambiguity the PDF output doesn't have to reason about.",[14,3811,2454],{"id":2453},[985,3813,3814,3819,3826],{},[36,3815,3816,3818],{},[723,3817,1175],{"href":1174}," — CJK inside grid columns, same API",[36,3820,3821,3825],{},[723,3822,3824],{"href":3823},"/blog/go-pdf-library-showdown-2026","Go PDF Library Showdown 2026"," — how the Builder API compares to gofpdf, gopdf, and Maroto",[36,3827,3828,3833],{},[723,3829,3832],{"href":3830,"rel":3831},"https://gpdf.dev/docs/guide/layout",[727],"Layout guide"," — full reference for rows, columns, and spacing",[14,3835,1201],{"id":1200},[19,3837,1204],{},[101,3839,3840],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,3841,3842],{"__ignoreMap":106},[110,3843,3844,3846,3848],{"class":112,"line":113},[110,3845,105],{"class":120},[110,3847,1218],{"class":255},[110,3849,1221],{"class":255},[19,3851,3852,1229,3855],{},[723,3853,1228],{"href":1226,"rel":3854},[727],[723,3856,1234],{"href":1232,"rel":3857},[727],[1236,3859,3860],{},"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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .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 pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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":106,"searchDepth":124,"depth":124,"links":3862},[3863,3864,3865,3866,3867,3868,3869,3870,3871,3872],{"id":16,"depth":124,"text":17},{"id":2570,"depth":124,"text":2571},{"id":2587,"depth":124,"text":2588},{"id":3542,"depth":124,"text":3543},{"id":3549,"depth":124,"text":3550},{"id":3579,"depth":124,"text":3580},{"id":3614,"depth":124,"text":3615},{"id":3785,"depth":124,"text":3786},{"id":2453,"depth":124,"text":2454},{"id":1200,"depth":124,"text":1201},"gpdf's 12-column grid uses r.Col(span, fn) where span is 1–12. Column width is (span/12) of the row. No breakpoints, no gutters, predictable by design.",{"name":3875,"totalTime":2523,"tools":3876,"steps":3877},"Lay out a page with gpdf's 12-column grid",[1259,173],[3878,3881,3884,3887,3890],{"name":3879,"text":3880},"Open a row on the page","Call page.AutoRow(fn) for a row whose height grows to fit the tallest column, or page.Row(height, fn) when you want to pin the height.",{"name":3882,"text":3883},"Declare columns with r.Col(span, fn)","Inside the row, call r.Col(span, fn) once per column. span is an integer from 1 to 12 — the fraction of the row width this column takes.",{"name":3885,"text":3886},"Keep the spans per row summing to 12 or less","If the spans sum to less than 12 the remainder is left empty. If they sum to more than 12 the last column overflows the row — usually not what you want.",{"name":3888,"text":3889},"Fill the column with content","Inside the ColBuilder callback, call c.Text, c.Image, c.Table, or c.Spacer. Columns stack content vertically in the order you add it.",{"name":3891,"text":3892},"Start the next row","Call page.AutoRow again for the next visual row. Rows are independent — a 4+8 row can sit directly above a 3+3+3+3 row.",{},"/blog/12-column-grid",{"title":2551,"description":3873},"blog/005.12-column-grid",[1283,2547,3898],"templates","VkukJjF_h1mccyCuNBa-_3u6h22zpCgdRwRZ1XCbp2E",{"id":3901,"title":3902,"author":3903,"body":3904,"date":3037,"description":5654,"draft":1253,"extension":1254,"howTo":1277,"image":1277,"meta":5655,"navigation":127,"path":5656,"seo":5657,"stem":5658,"tags":5659,"updated":1277,"__hash__":5663},"blog/blog/006.go-pdf-fpdf-archived.md","go-pdf/fpdf is archived too. Here's the modern Go PDF stack.",{"name":8,"url":9},{"type":11,"value":3905,"toc":5638},[3906,3908,3934,3938,3956,3959,3972,3983,3987,3990,4023,4026,4030,4036,4051,4058,4063,4069,4073,4076,4253,4256,4268,4280,4286,4292,4295,4299,4305,4308,4348,4351,4532,4545,4549,4565,4568,4573,4896,4901,5280,5296,5306,5310,5317,5414,5421,5427,5430,5437,5443,5493,5496,5500,5507,5513,5520,5524,5535,5541,5547,5566,5575,5581,5583,5585,5597,5605,5609,5635],[14,3907,1311],{"id":1310},[19,3909,3910,3911,3914,3915,3918,3919,3922,3923,3926,3927,3930,3931,3933],{},"Both maintained forks of the ",[44,3912,3913],{},"fpdf"," lineage are now read-only. ",[44,3916,3917],{},"jung-kurt/gofpdf"," was archived in ",[39,3920,3921],{},"September 2021","; the community fork ",[44,3924,3925],{},"go-pdf/fpdf"," followed in ",[39,3928,3929],{},"2025",". There is no \"next maintainer\" coming. For new Go projects, ",[39,3932,339],{}," is the modern default: pure Go, zero external dependencies, native CJK, 10–30× faster on common workloads. This is a map of the 2026 landscape and an honest account of when gpdf is the right pick and when it isn't.",[14,3935,3937],{"id":3936},"the-situation","The situation",[19,3939,3940,3941,3944,3945,3948,3949,3952,3953,3955],{},"A teammate typed ",[44,3942,3943],{},"go get github.com/go-pdf/fpdf"," last week and paused at the GitHub banner: ",[746,3946,3947],{},"\"This repository has been archived by the owner. It is now read-only.\""," This was supposed to be the ",[746,3950,3951],{},"fixed"," version — the community fork of ",[44,3954,3917],{}," that was meant to keep the lineage alive after the original was archived in 2021.",[19,3957,3958],{},"It's archived too. The README now recommends looking elsewhere.",[19,3960,3961,3962,3965,3966,3968,3969,3971],{},"If you've been building Go services that emit PDFs — invoices, reports, shipping labels, compliance documents — the library at the bottom of your ",[44,3963,3964],{},"go.mod"," has almost certainly been one of these two for the past five years. Stack Overflow answers point to ",[44,3967,3917],{},". Newer tutorials point to ",[44,3970,3925],{},". Both are now supply-chain liabilities: no CVE triage, no Go version compatibility work, no performance fixes, no spec updates.",[19,3973,3974,3975,3979,3980],{},"This post isn't another \"here's how to migrate line-by-line\" guide — ",[723,3976,3978],{"href":3977},"/blog/gofpdf-migration","we already wrote that one",". This is the longer version of the question the migration guide doesn't answer: ",[39,3981,3982],{},"what actually works for Go PDF generation in 2026, and how did the ecosystem end up here?",[14,3984,3986],{"id":3985},"what-archived-costs-you-in-practice","What \"archived\" costs you in practice",[19,3988,3989],{},"The word \"archived\" on GitHub is deceptively soft. In practice, for a library in your import graph, it means four concrete things:",[33,3991,3992,3998,4008,4014],{},[36,3993,3994,3997],{},[39,3995,3996],{},"No security patches."," If a memory-safety issue lands in the TTF parser, nobody is merging the fix upstream. You can fork it yourself. Most teams won't.",[36,3999,4000,4003,4004,4007],{},[39,4001,4002],{},"No Go toolchain forward-compat."," Go 1.25's loop variable semantics work fine with gofpdf today. Something about ",[44,4005,4006],{},"for range"," over an integer, or a future std-lib deprecation, can break the build tomorrow. You'll be the one patching a read-only repo's fork.",[36,4009,4010,4013],{},[39,4011,4012],{},"No spec updates."," PDF 2.0 (ISO 32000-2) landed in 2020. gofpdf implements most of PDF 1.7. Things like page-level associated files, rich XMP metadata, and modern digital signatures (PAdES-B-LT) are either absent or wired in by third-party glue.",[36,4015,4016,4019,4020,4022],{},[39,4017,4018],{},"No CJK progress."," gofpdf's Unicode path was retrofitted onto a single-byte-font design. It works but embeds full fonts instead of subsets in most real configurations, and glyph-id collisions in certain CJK TTFs produce corrupted output. ",[44,4021,3925],{}," inherited the same architecture.",[19,4024,4025],{},"Security and forward-compat are the ones that bite teams in compliance conversations. \"Our PDF library is archived and gets no CVE patches\" is not an answer your auditor wants to hear.",[14,4027,4029],{"id":4028},"why-both-forks-died","Why both forks died",[19,4031,4032,4033],{},"It's tempting to explain the archives as maintainer burnout — a single person tired of reviewing PRs, a bus factor of one going off-line. That's part of it, but it isn't the whole story. ",[39,4034,4035],{},"The architecture made it hard to keep up.",[19,4037,4038,4040,4041,2563,4044,2563,4047,4050],{},[44,4039,3917],{}," was a port of FPDF, a PHP library from 2002. The PHP original drove a cursor around the page and emitted content procedurally: ",[44,4042,4043],{},"SetXY(x, y)",[44,4045,4046],{},"Cell(w, h, text)",[44,4048,4049],{},"Ln(h)",". That model was a reasonable fit for 2002-era PHP, where the alternative was raw PostScript or proprietary toolkits. Ported to Go, it kept the cursor, kept the single-byte font tables, kept the manual page-break bookkeeping.",[19,4052,4053,4054,4057],{},"Each year, the gap between what people wanted to generate and what the cursor model could express grew. Invoices are tables. Reports are grids with repeating chrome. Shipping labels are QR codes plus locale-specific text. The cursor kept getting wrapped in helpers, and the helpers kept getting wrapped in tutorials, and by 2023 most of the code people wrote ",[746,4055,4056],{},"against"," gofpdf wasn't gofpdf at all — it was a per-team glue layer that tried to pretend the cursor was a layout engine.",[19,4059,4060,4062],{},[44,4061,3925],{}," inherited this. The fork refactored the internals and fixed longstanding bugs, but it couldn't change the public API without breaking every downstream project. The shape of the library was frozen in 2002-era PHP, and the cost of keeping that shape alive grew faster than the benefit.",[19,4064,4065,4068],{},[39,4066,4067],{},"So:"," two maintainers, two archives, one architectural reason. Rebuilding in 2026 means picking an approach that matches how PDFs actually get produced today — which looks a lot more like building a web page than driving a plotter.",[14,4070,4072],{"id":4071},"the-2026-go-pdf-landscape","The 2026 Go PDF landscape",[19,4074,4075],{},"Before recommending anything, here's the field. We'll use \"maintained\" loosely to mean \"a commit in the last 6 months and responsive issues.\"",[1896,4077,4078,4098],{},[1899,4079,4080],{},[1902,4081,4082,4085,4088,4090,4093,4096],{},[1905,4083,4084],{},"Library",[1905,4086,4087],{},"Status (2026-04)",[1905,4089,2394],{},[1905,4091,4092],{},"Native CJK",[1905,4094,4095],{},"Zero deps",[1905,4097,3395],{},[1912,4099,4100,4123,4143,4163,4187,4208,4231],{},[1902,4101,4102,4106,4111,4114,4117,4120],{},[1917,4103,4104],{},[44,4105,3917],{},[1917,4107,4108],{},[39,4109,4110],{},"Archived 2021",[1917,4112,4113],{},"MIT",[1917,4115,4116],{},"Retrofit",[1917,4118,4119],{},"Yes",[1917,4121,4122],{},"The original. Still the top search result in most locales.",[1902,4124,4125,4129,4134,4136,4138,4140],{},[1917,4126,4127],{},[44,4128,3925],{},[1917,4130,4131],{},[39,4132,4133],{},"Archived 2025",[1917,4135,4113],{},[1917,4137,4116],{},[1917,4139,4119],{},[1917,4141,4142],{},"Community fork of the above. Same architecture, same ceiling.",[1902,4144,4145,4150,4153,4155,4158,4160],{},[1917,4146,4147],{},[44,4148,4149],{},"signintech/gopdf",[1917,4151,4152],{},"Maintained",[1917,4154,4113],{},[1917,4156,4157],{},"Partial",[1917,4159,4119],{},[1917,4161,4162],{},"Low-level. You write coordinates. Good for form overlays.",[1902,4164,4165,4171,4173,4175,4178,4181],{},[1917,4166,4167,4170],{},[44,4168,4169],{},"johnfercher/maroto"," v2",[1917,4172,4152],{},[1917,4174,4113],{},[1917,4176,4177],{},"Via gofpdf",[1917,4179,4180],{},"No",[1917,4182,4183,4184,4186],{},"Grid-first builder, but depends on ",[44,4185,3925],{}," underneath.",[1902,4188,4189,4194,4196,4201,4203,4205],{},[1917,4190,4191],{},[44,4192,4193],{},"unidoc/unipdf",[1917,4195,4152],{},[1917,4197,4198],{},[39,4199,4200],{},"Commercial",[1917,4202,4119],{},[1917,4204,4180],{},[1917,4206,4207],{},"Feature-complete PDF SDK. Requires a paid license for commercial use.",[1902,4209,4210,4216,4218,4221,4223,4228],{},[1917,4211,4212,4215],{},[44,4213,4214],{},"chromedp"," + Chromium",[1917,4217,4152],{},[1917,4219,4220],{},"MIT + Chrome",[1917,4222,4119],{},[1917,4224,4225],{},[39,4226,4227],{},"No — ships a browser",[1917,4229,4230],{},"HTML→PDF via headless Chrome. Huge runtime.",[1902,4232,4233,4237,4239,4241,4246,4250],{},[1917,4234,4235],{},[44,4236,339],{},[1917,4238,4152],{},[1917,4240,4113],{},[1917,4242,4243],{},[39,4244,4245],{},"Native",[1917,4247,4248],{},[39,4249,4119],{},[1917,4251,4252],{},"Pure-Go reimplementation. Builder API, 12-column grid.",[19,4254,4255],{},"A few observations you can make from the table without running anything:",[19,4257,4258,42,4261,4263,4264,4267],{},[39,4259,4260],{},"Everything maintained is either commercial, carries a huge runtime, or sits on top of a soon-to-be-stale foundation.",[44,4262,4149],{}," is the exception — genuinely maintained and dependency-light — but it's a coordinate-level library. You're back to ",[44,4265,4266],{},"SetXY"," with a different package name.",[19,4269,4270,4273,4274,4276,4277,4279],{},[39,4271,4272],{},"Maroto v2 is a grid-first builder with a good API."," The problem is that at the bottom of its ",[44,4275,3964],{}," is ",[44,4278,3925],{},". Every performance ceiling and CJK limitation in fpdf is also a ceiling for Maroto. A major v3 could break free of that — it isn't out yet.",[19,4281,4282,4285],{},[39,4283,4284],{},"unipdf is feature-rich but not MIT-compatible for commercial use."," You pay per seat or per deployment. That's a fine choice if your revenue supports it; for an open-source side project or an early-stage startup, the license math doesn't work.",[19,4287,4288,4291],{},[39,4289,4290],{},"chromedp works, but you're shipping a browser."," A 100 MB base image becomes a 1 GB+ image. Cold start on serverless is painful. Fonts still need to be installed in the container. The upside is that you can reuse your React templates; the downside is that you're running Chromium to render an invoice.",[19,4293,4294],{},"The gap is obvious: a pure-Go, zero-dep, CJK-native, grid-first library that doesn't require a commercial license or a browser runtime. That's what gpdf is.",[14,4296,4298],{"id":4297},"what-gpdf-actually-is","What gpdf actually is",[19,4300,4301,4302,4304],{},"gpdf (",[44,4303,173],{},") is a clean reimplementation. Not a fork. The PDF wire-format writer, the layout engine, and the TrueType subsetter are all written from scratch in pure Go.",[19,4306,4307],{},"The three properties that matter for most teams:",[985,4309,4310,4323,4340],{},[36,4311,4312,42,4315,4318,4319,4322],{},[39,4313,4314],{},"Pure Go, no CGO.",[44,4316,4317],{},"go build"," is static. ",[44,4320,4321],{},"GOOS=linux GOARCH=arm64 go build"," works from a MacBook with no toolchain setup. Docker images stay small — a 12 MB distroless container runs it.",[36,4324,4325,42,4328,4331,4332,4335,4336,4339],{},[39,4326,4327],{},"Zero external dependencies.",[44,4329,4330],{},"go mod graph"," after ",[44,4333,4334],{},"go get github.com/gpdf-dev/gpdf"," shows one line: gpdf itself. The core uses only ",[44,4337,4338],{},"std",". (Optional add-ons for HTML→PDF or digital signatures bring in small dependencies, and they're opt-in.)",[36,4341,4342,42,4345,4347],{},[39,4343,4344],{},"Native CJK.",[44,4346,50],{}," registers a TrueType font at document construction. Subset embedding happens at render time. A 200-character Japanese invoice carries ~30 KB of subset font data, not 5 MB of full font.",[19,4349,4350],{},"The API shape is declarative. You describe a tree of rows and columns; the layout engine places them. The grid is 12 columns — the same idiom Bootstrap has shipped since 2011. If you've written a single line of HTML/CSS, the gpdf API is familiar:",[101,4352,4354],{"className":103,"code":4353,"language":105,"meta":106,"style":106},"page := doc.AddPage()\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"Invoice #2026-0416\", template.FontSize(18), template.Bold())\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"2026-04-16\", template.AlignRight())\n    })\n})\n",[44,4355,4356,4371,4395,4425,4463,4467,4497,4524,4528],{"__ignoreMap":106},[110,4357,4358,4361,4363,4365,4367,4369],{"class":112,"line":113},[110,4359,4360],{"class":228},"page ",[110,4362,238],{"class":116},[110,4364,457],{"class":228},[110,4366,60],{"class":116},[110,4368,462],{"class":215},[110,4370,465],{"class":116},[110,4372,4373,4375,4377,4379,4381,4383,4385,4387,4389,4391,4393],{"class":112,"line":124},[110,4374,853],{"class":228},[110,4376,60],{"class":116},[110,4378,476],{"class":215},[110,4380,479],{"class":116},[110,4382,483],{"class":482},[110,4384,486],{"class":116},[110,4386,489],{"class":120},[110,4388,60],{"class":116},[110,4390,494],{"class":120},[110,4392,497],{"class":116},[110,4394,222],{"class":116},[110,4396,4397,4399,4401,4403,4405,4407,4409,4411,4413,4415,4417,4419,4421,4423],{"class":112,"line":131},[110,4398,878],{"class":228},[110,4400,60],{"class":116},[110,4402,510],{"class":215},[110,4404,249],{"class":116},[110,4406,3305],{"class":381},[110,4408,232],{"class":116},[110,4410,520],{"class":116},[110,4412,523],{"class":482},[110,4414,486],{"class":116},[110,4416,489],{"class":120},[110,4418,60],{"class":116},[110,4420,532],{"class":120},[110,4422,497],{"class":116},[110,4424,222],{"class":116},[110,4426,4427,4429,4431,4433,4435,4437,4439,4441,4443,4445,4447,4449,4451,4453,4455,4457,4459,4461],{"class":112,"line":141},[110,4428,909],{"class":228},[110,4430,60],{"class":116},[110,4432,547],{"class":215},[110,4434,249],{"class":116},[110,4436,252],{"class":116},[110,4438,2834],{"class":255},[110,4440,252],{"class":116},[110,4442,232],{"class":116},[110,4444,1680],{"class":228},[110,4446,60],{"class":116},[110,4448,1685],{"class":215},[110,4450,249],{"class":116},[110,4452,2849],{"class":381},[110,4454,1693],{"class":116},[110,4456,1680],{"class":228},[110,4458,60],{"class":116},[110,4460,1700],{"class":215},[110,4462,1703],{"class":116},[110,4464,4465],{"class":112,"line":153},[110,4466,570],{"class":116},[110,4468,4469,4471,4473,4475,4477,4479,4481,4483,4485,4487,4489,4491,4493,4495],{"class":112,"line":163},[110,4470,878],{"class":228},[110,4472,60],{"class":116},[110,4474,510],{"class":215},[110,4476,249],{"class":116},[110,4478,3095],{"class":381},[110,4480,232],{"class":116},[110,4482,520],{"class":116},[110,4484,523],{"class":482},[110,4486,486],{"class":116},[110,4488,489],{"class":120},[110,4490,60],{"class":116},[110,4492,532],{"class":120},[110,4494,497],{"class":116},[110,4496,222],{"class":116},[110,4498,4499,4501,4503,4505,4507,4509,4511,4513,4515,4517,4519,4522],{"class":112,"line":168},[110,4500,909],{"class":228},[110,4502,60],{"class":116},[110,4504,547],{"class":215},[110,4506,249],{"class":116},[110,4508,252],{"class":116},[110,4510,3037],{"class":255},[110,4512,252],{"class":116},[110,4514,232],{"class":116},[110,4516,1680],{"class":228},[110,4518,60],{"class":116},[110,4520,4521],{"class":215},"AlignRight",[110,4523,1703],{"class":116},[110,4525,4526],{"class":112,"line":178},[110,4527,570],{"class":116},[110,4529,4530],{"class":112,"line":188},[110,4531,936],{"class":116},[19,4533,4534,4535,4537,4538,4541,4542,4544],{},"More on the grid in ",[723,4536,2551],{"href":3894},". The one-sentence version: ",[44,4539,4540],{},"Col(span, fn)"," takes a span from 1 to 12, and ",[44,4543,2580],{}," is the fraction of the row it takes.",[14,4546,4548],{"id":4547},"the-minimal-go-pdffpdf-gpdf-diff","The minimal go-pdf/fpdf → gpdf diff",[19,4550,4551,4552,4554,4555,4557,4558,4560,4561,4564],{},"If you're coming from ",[44,4553,3925],{}," specifically (not ",[44,4556,3917],{},"), the good news is the API surfaces are almost identical — ",[44,4559,3925],{}," is a fork that changed almost nothing at the call-site level. The migration to gpdf is the same migration our ",[723,4562,4563],{"href":3977},"gofpdf guide"," walks through, with one import-path rename.",[19,4566,4567],{},"Here's the smallest possible diff — a \"generate PDF\" HTTP handler:",[19,4569,4570],{},[39,4571,4572],{},"Before — go-pdf/fpdf:",[101,4574,4576],{"className":103,"code":4575,"language":105,"meta":106,"style":106},"package main\n\nimport (\n    \"net/http\"\n\n    \"github.com/go-pdf/fpdf\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    pdf := fpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"B\", 16)\n    pdf.Cell(40, 10, \"Hello, World!\")\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := pdf.Output(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[44,4577,4578,4584,4588,4594,4603,4607,4616,4620,4624,4663,4712,4723,4757,4787,4791,4827,4857,4888,4892],{"__ignoreMap":106},[110,4579,4580,4582],{"class":112,"line":113},[110,4581,117],{"class":116},[110,4583,121],{"class":120},[110,4585,4586],{"class":112,"line":124},[110,4587,128],{"emptyLinePlaceholder":127},[110,4589,4590,4592],{"class":112,"line":131},[110,4591,135],{"class":134},[110,4593,138],{"class":116},[110,4595,4596,4598,4601],{"class":112,"line":141},[110,4597,144],{"class":116},[110,4599,4600],{"class":120},"net/http",[110,4602,150],{"class":116},[110,4604,4605],{"class":112,"line":153},[110,4606,128],{"emptyLinePlaceholder":127},[110,4608,4609,4611,4614],{"class":112,"line":163},[110,4610,144],{"class":116},[110,4612,4613],{"class":120},"github.com/go-pdf/fpdf",[110,4615,150],{"class":116},[110,4617,4618],{"class":112,"line":168},[110,4619,201],{"class":116},[110,4621,4622],{"class":112,"line":178},[110,4623,128],{"emptyLinePlaceholder":127},[110,4625,4626,4628,4631,4633,4636,4639,4641,4644,4646,4649,4651,4654,4656,4659,4661],{"class":112,"line":188},[110,4627,212],{"class":116},[110,4629,4630],{"class":215}," handler",[110,4632,249],{"class":116},[110,4634,4635],{"class":482},"w",[110,4637,4638],{"class":120}," http",[110,4640,60],{"class":116},[110,4642,4643],{"class":120},"ResponseWriter",[110,4645,232],{"class":116},[110,4647,4648],{"class":482}," r",[110,4650,486],{"class":116},[110,4652,4653],{"class":120},"http",[110,4655,60],{"class":116},[110,4657,4658],{"class":120},"Request",[110,4660,497],{"class":116},[110,4662,222],{"class":116},[110,4664,4665,4668,4670,4673,4675,4678,4680,4682,4685,4687,4689,4692,4695,4697,4699,4701,4703,4705,4707,4710],{"class":112,"line":198},[110,4666,4667],{"class":228},"    pdf ",[110,4669,238],{"class":116},[110,4671,4672],{"class":228}," fpdf",[110,4674,60],{"class":116},[110,4676,4677],{"class":215},"New",[110,4679,249],{"class":116},[110,4681,252],{"class":116},[110,4683,4684],{"class":255},"P",[110,4686,252],{"class":116},[110,4688,232],{"class":116},[110,4690,4691],{"class":116}," \"",[110,4693,4694],{"class":255},"mm",[110,4696,252],{"class":116},[110,4698,232],{"class":116},[110,4700,4691],{"class":116},[110,4702,344],{"class":255},[110,4704,252],{"class":116},[110,4706,232],{"class":116},[110,4708,4709],{"class":116}," \"\"",[110,4711,201],{"class":116},[110,4713,4714,4717,4719,4721],{"class":112,"line":204},[110,4715,4716],{"class":228},"    pdf",[110,4718,60],{"class":116},[110,4720,462],{"class":215},[110,4722,465],{"class":116},[110,4724,4725,4727,4729,4732,4734,4736,4739,4741,4743,4745,4748,4750,4752,4755],{"class":112,"line":209},[110,4726,4716],{"class":228},[110,4728,60],{"class":116},[110,4730,4731],{"class":215},"SetFont",[110,4733,249],{"class":116},[110,4735,252],{"class":116},[110,4737,4738],{"class":255},"Arial",[110,4740,252],{"class":116},[110,4742,232],{"class":116},[110,4744,4691],{"class":116},[110,4746,4747],{"class":255},"B",[110,4749,252],{"class":116},[110,4751,232],{"class":116},[110,4753,4754],{"class":381}," 16",[110,4756,201],{"class":116},[110,4758,4759,4761,4763,4766,4768,4771,4773,4776,4778,4780,4783,4785],{"class":112,"line":225},[110,4760,4716],{"class":228},[110,4762,60],{"class":116},[110,4764,4765],{"class":215},"Cell",[110,4767,249],{"class":116},[110,4769,4770],{"class":381},"40",[110,4772,232],{"class":116},[110,4774,4775],{"class":381}," 10",[110,4777,232],{"class":116},[110,4779,4691],{"class":116},[110,4781,4782],{"class":255},"Hello, World!",[110,4784,252],{"class":116},[110,4786,201],{"class":116},[110,4788,4789],{"class":112,"line":262},[110,4790,128],{"emptyLinePlaceholder":127},[110,4792,4793,4796,4798,4801,4804,4807,4809,4811,4814,4816,4818,4820,4823,4825],{"class":112,"line":278},[110,4794,4795],{"class":228},"    w",[110,4797,60],{"class":116},[110,4799,4800],{"class":215},"Header",[110,4802,4803],{"class":116},"().",[110,4805,4806],{"class":215},"Set",[110,4808,249],{"class":116},[110,4810,252],{"class":116},[110,4812,4813],{"class":255},"Content-Type",[110,4815,252],{"class":116},[110,4817,232],{"class":116},[110,4819,4691],{"class":116},[110,4821,4822],{"class":255},"application/pdf",[110,4824,252],{"class":116},[110,4826,201],{"class":116},[110,4828,4829,4831,4833,4835,4838,4840,4843,4845,4847,4849,4851,4853,4855],{"class":112,"line":296},[110,4830,265],{"class":134},[110,4832,235],{"class":228},[110,4834,238],{"class":116},[110,4836,4837],{"class":228}," pdf",[110,4839,60],{"class":116},[110,4841,4842],{"class":215},"Output",[110,4844,249],{"class":116},[110,4846,4635],{"class":228},[110,4848,667],{"class":116},[110,4850,235],{"class":228},[110,4852,270],{"class":116},[110,4854,273],{"class":116},[110,4856,222],{"class":116},[110,4858,4859,4862,4864,4867,4869,4871,4873,4876,4878,4880,4883,4886],{"class":112,"line":302},[110,4860,4861],{"class":228},"        http",[110,4863,60],{"class":116},[110,4865,4866],{"class":215},"Error",[110,4868,249],{"class":116},[110,4870,4635],{"class":228},[110,4872,232],{"class":116},[110,4874,4875],{"class":228}," err",[110,4877,60],{"class":116},[110,4879,4866],{"class":215},[110,4881,4882],{"class":116},"(),",[110,4884,4885],{"class":381}," 500",[110,4887,201],{"class":116},[110,4889,4890],{"class":112,"line":307},[110,4891,299],{"class":116},[110,4893,4894],{"class":112,"line":326},[110,4895,701],{"class":116},[19,4897,4898],{},[39,4899,4900],{},"After — gpdf:",[101,4902,4904],{"className":103,"code":4903,"language":105,"meta":106,"style":106},"package main\n\nimport (\n    \"net/http\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Hello, World!\", template.FontSize(16), template.Bold())\n        })\n    })\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := doc.Render(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[44,4905,4906,4912,4916,4922,4930,4934,4942,4950,4958,4962,4966,4998,5012,5030,5060,5064,5068,5082,5106,5136,5175,5179,5183,5187,5217,5246,5272,5276],{"__ignoreMap":106},[110,4907,4908,4910],{"class":112,"line":113},[110,4909,117],{"class":116},[110,4911,121],{"class":120},[110,4913,4914],{"class":112,"line":124},[110,4915,128],{"emptyLinePlaceholder":127},[110,4917,4918,4920],{"class":112,"line":131},[110,4919,135],{"class":134},[110,4921,138],{"class":116},[110,4923,4924,4926,4928],{"class":112,"line":141},[110,4925,144],{"class":116},[110,4927,4600],{"class":120},[110,4929,150],{"class":116},[110,4931,4932],{"class":112,"line":153},[110,4933,128],{"emptyLinePlaceholder":127},[110,4935,4936,4938,4940],{"class":112,"line":163},[110,4937,144],{"class":116},[110,4939,173],{"class":120},[110,4941,150],{"class":116},[110,4943,4944,4946,4948],{"class":112,"line":168},[110,4945,144],{"class":116},[110,4947,183],{"class":120},[110,4949,150],{"class":116},[110,4951,4952,4954,4956],{"class":112,"line":178},[110,4953,144],{"class":116},[110,4955,193],{"class":120},[110,4957,150],{"class":116},[110,4959,4960],{"class":112,"line":188},[110,4961,201],{"class":116},[110,4963,4964],{"class":112,"line":198},[110,4965,128],{"emptyLinePlaceholder":127},[110,4967,4968,4970,4972,4974,4976,4978,4980,4982,4984,4986,4988,4990,4992,4994,4996],{"class":112,"line":204},[110,4969,212],{"class":116},[110,4971,4630],{"class":215},[110,4973,249],{"class":116},[110,4975,4635],{"class":482},[110,4977,4638],{"class":120},[110,4979,60],{"class":116},[110,4981,4643],{"class":120},[110,4983,232],{"class":116},[110,4985,4648],{"class":482},[110,4987,486],{"class":116},[110,4989,4653],{"class":120},[110,4991,60],{"class":116},[110,4993,4658],{"class":120},[110,4995,497],{"class":116},[110,4997,222],{"class":116},[110,4999,5000,5002,5004,5006,5008,5010],{"class":112,"line":209},[110,5001,310],{"class":228},[110,5003,238],{"class":116},[110,5005,315],{"class":228},[110,5007,60],{"class":116},[110,5009,320],{"class":215},[110,5011,323],{"class":116},[110,5013,5014,5016,5018,5020,5022,5024,5026,5028],{"class":112,"line":225},[110,5015,329],{"class":228},[110,5017,60],{"class":116},[110,5019,334],{"class":215},[110,5021,249],{"class":116},[110,5023,362],{"class":228},[110,5025,60],{"class":116},[110,5027,344],{"class":228},[110,5029,347],{"class":116},[110,5031,5032,5034,5036,5038,5040,5042,5044,5046,5048,5050,5052,5054,5056,5058],{"class":112,"line":262},[110,5033,329],{"class":228},[110,5035,60],{"class":116},[110,5037,357],{"class":215},[110,5039,249],{"class":116},[110,5041,362],{"class":228},[110,5043,60],{"class":116},[110,5045,367],{"class":215},[110,5047,249],{"class":116},[110,5049,362],{"class":228},[110,5051,60],{"class":116},[110,5053,376],{"class":215},[110,5055,249],{"class":116},[110,5057,382],{"class":381},[110,5059,385],{"class":116},[110,5061,5062],{"class":112,"line":278},[110,5063,441],{"class":116},[110,5065,5066],{"class":112,"line":296},[110,5067,128],{"emptyLinePlaceholder":127},[110,5069,5070,5072,5074,5076,5078,5080],{"class":112,"line":302},[110,5071,452],{"class":228},[110,5073,238],{"class":116},[110,5075,457],{"class":228},[110,5077,60],{"class":116},[110,5079,462],{"class":215},[110,5081,465],{"class":116},[110,5083,5084,5086,5088,5090,5092,5094,5096,5098,5100,5102,5104],{"class":112,"line":307},[110,5085,471],{"class":228},[110,5087,60],{"class":116},[110,5089,476],{"class":215},[110,5091,479],{"class":116},[110,5093,483],{"class":482},[110,5095,486],{"class":116},[110,5097,489],{"class":120},[110,5099,60],{"class":116},[110,5101,494],{"class":120},[110,5103,497],{"class":116},[110,5105,222],{"class":116},[110,5107,5108,5110,5112,5114,5116,5118,5120,5122,5124,5126,5128,5130,5132,5134],{"class":112,"line":326},[110,5109,505],{"class":228},[110,5111,60],{"class":116},[110,5113,510],{"class":215},[110,5115,249],{"class":116},[110,5117,515],{"class":381},[110,5119,232],{"class":116},[110,5121,520],{"class":116},[110,5123,523],{"class":482},[110,5125,486],{"class":116},[110,5127,489],{"class":120},[110,5129,60],{"class":116},[110,5131,532],{"class":120},[110,5133,497],{"class":116},[110,5135,222],{"class":116},[110,5137,5138,5140,5142,5144,5146,5148,5150,5152,5154,5156,5158,5160,5162,5165,5167,5169,5171,5173],{"class":112,"line":350},[110,5139,542],{"class":228},[110,5141,60],{"class":116},[110,5143,547],{"class":215},[110,5145,249],{"class":116},[110,5147,252],{"class":116},[110,5149,4782],{"class":255},[110,5151,252],{"class":116},[110,5153,232],{"class":116},[110,5155,1680],{"class":228},[110,5157,60],{"class":116},[110,5159,1685],{"class":215},[110,5161,249],{"class":116},[110,5163,5164],{"class":381},"16",[110,5166,1693],{"class":116},[110,5168,1680],{"class":228},[110,5170,60],{"class":116},[110,5172,1700],{"class":215},[110,5174,1703],{"class":116},[110,5176,5177],{"class":112,"line":388},[110,5178,564],{"class":116},[110,5180,5181],{"class":112,"line":413},[110,5182,570],{"class":116},[110,5184,5185],{"class":112,"line":438},[110,5186,128],{"emptyLinePlaceholder":127},[110,5188,5189,5191,5193,5195,5197,5199,5201,5203,5205,5207,5209,5211,5213,5215],{"class":112,"line":444},[110,5190,4795],{"class":228},[110,5192,60],{"class":116},[110,5194,4800],{"class":215},[110,5196,4803],{"class":116},[110,5198,4806],{"class":215},[110,5200,249],{"class":116},[110,5202,252],{"class":116},[110,5204,4813],{"class":255},[110,5206,252],{"class":116},[110,5208,232],{"class":116},[110,5210,4691],{"class":116},[110,5212,4822],{"class":255},[110,5214,252],{"class":116},[110,5216,201],{"class":116},[110,5218,5219,5221,5223,5225,5227,5229,5232,5234,5236,5238,5240,5242,5244],{"class":112,"line":449},[110,5220,265],{"class":134},[110,5222,235],{"class":228},[110,5224,238],{"class":116},[110,5226,457],{"class":228},[110,5228,60],{"class":116},[110,5230,5231],{"class":215},"Render",[110,5233,249],{"class":116},[110,5235,4635],{"class":228},[110,5237,667],{"class":116},[110,5239,235],{"class":228},[110,5241,270],{"class":116},[110,5243,273],{"class":116},[110,5245,222],{"class":116},[110,5247,5248,5250,5252,5254,5256,5258,5260,5262,5264,5266,5268,5270],{"class":112,"line":468},[110,5249,4861],{"class":228},[110,5251,60],{"class":116},[110,5253,4866],{"class":215},[110,5255,249],{"class":116},[110,5257,4635],{"class":228},[110,5259,232],{"class":116},[110,5261,4875],{"class":228},[110,5263,60],{"class":116},[110,5265,4866],{"class":215},[110,5267,4882],{"class":116},[110,5269,4885],{"class":381},[110,5271,201],{"class":116},[110,5273,5274],{"class":112,"line":502},[110,5275,299],{"class":116},[110,5277,5278],{"class":112,"line":539},[110,5279,701],{"class":116},[19,5281,5282,5283,5285,5286,5289,5290,5292,5293,5295],{},"Three-line cursor code becomes three-call builder code. The structure shows up in the source instead of hiding inside the order of ",[44,5284,4765],{}," calls. For CJK, add ",[44,5287,5288],{},"gpdf.WithFont(\"NotoSansJP\", ttfBytes)"," — no ",[44,5291,707],{},", no filesystem path, no UTF-8 flag. See ",[723,5294,1175],{"href":1174}," for the full walkthrough.",[19,5297,5298,5299,5302,5303,5305],{},"The ",[723,5300,5301],{"href":3977},"gofpdf migration guide"," has five more before/after pairs covering tables, repeating headers/footers, page numbers, and absolute positioning. Everything there applies verbatim to ",[44,5304,3925],{}," users — just swap the import path.",[14,5307,5309],{"id":5308},"the-benchmark-picture","The benchmark picture",[19,5311,5312,5313,5316],{},"\"Faster\" is easy to claim and hard to earn. The table below is from ",[44,5314,5315],{},"gpdf/_benchmark/benchmark_test.go"," on an Apple M1 running Go 1.25. Workloads are what production code actually does — not micro-benchmarks chosen to flatter one library.",[1896,5318,5319,5337],{},[1899,5320,5321],{},[1902,5322,5323,5326,5328,5331,5334],{},[1905,5324,5325],{},"Benchmark",[1905,5327,339],{},[1905,5329,5330],{},"gofpdf",[1905,5332,5333],{},"gopdf",[1905,5335,5336],{},"Maroto v2",[1912,5338,5339,5358,5377,5395],{},[1902,5340,5341,5344,5349,5352,5355],{},[1917,5342,5343],{},"Single page (hello)",[1917,5345,5346],{},[39,5347,5348],{},"13 µs",[1917,5350,5351],{},"132 µs",[1917,5353,5354],{},"423 µs",[1917,5356,5357],{},"237 µs",[1902,5359,5360,5363,5368,5371,5374],{},[1917,5361,5362],{},"4×10 line-items table",[1917,5364,5365],{},[39,5366,5367],{},"108 µs",[1917,5369,5370],{},"241 µs",[1917,5372,5373],{},"835 µs",[1917,5375,5376],{},"8.6 ms",[1902,5378,5379,5382,5387,5390,5392],{},[1917,5380,5381],{},"100-page report",[1917,5383,5384],{},[39,5385,5386],{},"683 µs",[1917,5388,5389],{},"11.7 ms",[1917,5391,5376],{},[1917,5393,5394],{},"19.8 ms",[1902,5396,5397,5400,5405,5408,5411],{},[1917,5398,5399],{},"Complex CJK invoice",[1917,5401,5402],{},[39,5403,5404],{},"133 µs",[1917,5406,5407],{},"254 µs",[1917,5409,5410],{},"997 µs",[1917,5412,5413],{},"10.4 ms",[19,5415,5416,5417,5420],{},"At 13 µs per single page, one core produces ~75,000 hello-world PDFs per second. At 108 µs per invoice, ~9,000 per second. ",[39,5418,5419],{},"The point is not bragging rights","; it's that you can stop thinking about whether to cache or async-queue PDF generation. For most workloads, generating on the request path is fine.",[19,5422,5423,5424,5426],{},"Maroto v2 shows up slow on the table benchmark because it drives ",[44,5425,3925],{}," underneath and adds its own layout pass on top. That's not a criticism of the Maroto API — the API is good — it's a structural cost of sitting on the fpdf foundation. When Maroto v3 drops the fpdf dependency, expect this column to change.",[19,5428,5429],{},"The 100-page benchmark is worth dwelling on. gpdf's streaming writer emits content as rows are laid out; gofpdf buffers more state per page. For a pagination-heavy workload (monthly reports, catalogs, compliance exports), the difference is minutes vs seconds at the upper end of document sizes.",[14,5431,5433,5434,5436],{"id":5432},"when-gpdf-is-not-the-right-pick","When gpdf is ",[746,5435,2347],{}," the right pick",[19,5438,5439,5440,5442],{},"Every migration post has to answer \"when should I ",[746,5441,2347],{}," move?\" Honest answers:",[985,5444,5445,5458,5467,5480],{},[36,5446,5447,5450,5451,5454,5455,5457],{},[39,5448,5449],{},"AcroForm / fillable forms."," If your use case is generating PDFs that users open in Acrobat and type into, gpdf's form-field support is still minimal. ",[44,5452,5453],{},"unidoc"," is more complete here; ",[44,5456,4149],{}," has partial AcroForm support. A future gpdf release will close this gap, but today it's a gap.",[36,5459,5460,42,5463,5466],{},[39,5461,5462],{},"Arbitrary vector paths and complex drawing.",[44,5464,5465],{},"c.Line()"," draws a horizontal rule inside a column. If you need beziers, custom paths, or gradient fills for charts or technical drawings, gpdf isn't there. (Pre-rendered chart images work fine — this is about drawing primitives, not embedding.)",[36,5468,5469,5475,5476,5479],{},[39,5470,5471,5472,5474],{},"Existing gofpdf codebases with heavy ",[44,5473,4266],{}," use."," If your code is 2,000 lines of cursor manipulation, the migration is a rewrite, not a find-and-replace. The rewritten code is almost always shorter, but \"almost always\" is cold comfort on the day the deadline lands. The ",[723,5477,5478],{"href":3977},"migration guide"," estimates effort honestly.",[36,5481,5482,5485,5486,5489,5490,5492],{},[39,5483,5484],{},"You need HTML → PDF with full CSS support right now."," gpdf has an HTML subset in its ",[44,5487,5488],{},"gpdf-pro"," add-on, but full CSS parity with Chromium is not a goal. If your template is a complicated React component, ",[44,5491,4214],{}," or a commercial API is a more direct fit.",[19,5494,5495],{},"If none of those bite, gpdf is the default. If one of them does, the right move is usually to run both libraries side-by-side — gpdf for the new PDFs, the incumbent for the edge case — and migrate the edge case later once gpdf catches up.",[14,5497,5499],{"id":5498},"the-compliance-angle","The compliance angle",[19,5501,5502,5503,5506],{},"One thing we don't see discussed enough in ecosystem posts: ",[39,5504,5505],{},"archived dependencies show up in SOC 2 and ISO 27001 audits."," The auditor wants to know that third-party code in your supply chain is actively maintained. \"Archived in 2021\" triggers a finding. \"Archived in 2025\" triggers a finding. \"Fork we maintain internally\" triggers follow-up questions about how you'll patch a zero-day.",[19,5508,5509,5510,5512],{},"This is mostly why teams on larger companies' security reviews have been quietly asking us when gpdf will hit a stable v1. The answer is: it already has. ",[44,5511,173],{}," is tagged, semver-stable, and the v1 API surface is frozen. The project has a security contact, a responsible disclosure policy, and CI that runs against Go 1.22 through 1.26.",[19,5514,5515,5516,5519],{},"You don't migrate ",[746,5517,5518],{},"for"," the audit. You migrate because the audit is about to ask you to anyway.",[14,5521,5523],{"id":5522},"faq","FAQ",[19,5525,5526,5529,5530,762,5532,5534],{},[39,5527,5528],{},"Is \"the modern Go PDF stack\" just gpdf, or multiple libraries?","\nFor most teams, it's gpdf alone — the single library covers document creation, CJK, tables, grids, pagination, and output. Teams with fillable-form requirements pair it with ",[44,5531,4149],{},[44,5533,5453],{}," for those documents specifically. Teams with chart-heavy exports pre-render charts to PNG and embed them. \"Stack\" here means a short list, not a layered architecture.",[19,5536,5537,5540],{},[39,5538,5539],{},"Can I run gpdf and go-pdf/fpdf side-by-side during migration?","\nYes. They're different import paths and different types. Route new endpoints to gpdf and leave old ones on go-pdf/fpdf until you have time to rewrite. There's no runtime conflict.",[19,5542,5543,5546],{},[39,5544,5545],{},"Will there be a go-pdf/fpdf v3 or a new fork?","\nMaybe. The bet behind gpdf isn't that nobody will ever un-archive the fork — it's that the architecture doesn't scale to what people are building today. A new fork would inherit the same limitations unless it rewrites the layout model, at which point it's closer to gpdf than to fpdf.",[19,5548,5549,5555,5556,2563,5559,2563,5562,5565],{},[39,5550,5551,5552,5554],{},"What about ",[44,5553,4149],{}," as a modern alternative?","\nIt's genuinely maintained and zero-dep. The API is coordinate-level — ",[44,5557,5558],{},"SetX",[44,5560,5561],{},"SetY",[44,5563,5564],{},"CellWithOption"," — so it suits form overlays and fixed templates well. For invoice-like documents with tables and repeating chrome, you end up writing a layout helper on top, which is the same pit gofpdf users fell into. gpdf and gopdf don't really compete — they solve adjacent problems.",[19,5567,5568,5571,5574],{},[39,5569,5570],{},"Does gpdf have a commercial/hosted version?",[44,5572,5573],{},"gpdf-api"," is coming — a hosted API that accepts JSON templates and returns PDFs. It's not public yet. When it launches, this blog will have a post about it. The OSS library will remain MIT, zero-dep, and independently useful.",[19,5576,5577,5580],{},[39,5578,5579],{},"What's the roadmap priority order?","\nPublic gpdf roadmap as of 2026-04: (1) AcroForm form fields, (2) full PDF/A-3 compliance, (3) expanded HTML→PDF coverage in gpdf-pro, (4) RTL text support (Arabic, Hebrew). Feedback on priority is welcome on GitHub issues.",[14,5582,1201],{"id":1200},[19,5584,1204],{},[101,5586,5587],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,5588,5589],{"__ignoreMap":106},[110,5590,5591,5593,5595],{"class":112,"line":113},[110,5592,105],{"class":120},[110,5594,1218],{"class":255},[110,5596,1221],{"class":255},[19,5598,5599,1229,5602],{},[723,5600,1228],{"href":1226,"rel":5601},[727],[723,5603,1234],{"href":1232,"rel":5604},[727],[14,5606,5608],{"id":5607},"next-reads","Next reads",[985,5610,5611,5617,5622,5627],{},[36,5612,5613,5616],{},[723,5614,5615],{"href":3977},"gofpdf is archived. Here's how to migrate to gpdf."," — the line-by-line API mapping",[36,5618,5619,5621],{},[723,5620,3824],{"href":3823}," — deeper head-to-head benchmarks and feature grids",[36,5623,5624,5626],{},[723,5625,2551],{"href":3894}," — the builder idiom that replaces cursor-pushing",[36,5628,5629,5631,5632,5634],{},[723,5630,1175],{"href":1174}," — CJK without the ",[44,5633,707],{}," dance",[1236,5636,5637],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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}",{"title":106,"searchDepth":124,"depth":124,"links":5639},[5640,5641,5642,5643,5644,5645,5646,5647,5648,5650,5651,5652,5653],{"id":1310,"depth":124,"text":1311},{"id":3936,"depth":124,"text":3937},{"id":3985,"depth":124,"text":3986},{"id":4028,"depth":124,"text":4029},{"id":4071,"depth":124,"text":4072},{"id":4297,"depth":124,"text":4298},{"id":4547,"depth":124,"text":4548},{"id":5308,"depth":124,"text":5309},{"id":5432,"depth":124,"text":5649},"When gpdf is not the right pick",{"id":5498,"depth":124,"text":5499},{"id":5522,"depth":124,"text":5523},{"id":1200,"depth":124,"text":1201},{"id":5607,"depth":124,"text":5608},"jung-kurt/gofpdf archived in 2021. go-pdf/fpdf followed in 2025. Here's the Go PDF stack we actually use in 2026 — gpdf, the trade-offs, and why.",{},"/blog/go-pdf-fpdf-archived",{"title":3902,"description":5654},"blog/006.go-pdf-fpdf-archived",[5660,5661,5662],"migration","comparison","benchmark","m6yzj1ZwNoUzIvr7qgkXUBoLGLrEMU_uslxsmRzwtjM",{"id":5665,"title":5666,"author":5667,"body":5668,"date":3037,"description":7297,"draft":1253,"extension":1254,"howTo":7298,"image":1277,"meta":7322,"navigation":127,"path":1195,"seo":7323,"stem":7324,"tags":7325,"updated":1277,"__hash__":7326},"blog/blog/007.japanese-pdf-in-go.md","Japanese PDFs in Go: the 2026 definitive guide",{"name":8,"url":9},{"type":11,"value":5669,"toc":7283},[5670,5672,5688,5692,5695,5701,5704,5710,5714,5725,5765,5773,5779,5783,5786,5901,5904,5910,5916,5920,5928,6746,6749,6799,6808,6812,6818,6821,6824,6870,6883,6886,6911,6927,6931,6934,6941,6944,6958,6961,7043,7053,7057,7060,7069,7079,7103,7117,7120,7124,7131,7137,7143,7157,7160,7162,7187,7193,7199,7213,7223,7232,7234,7237,7249,7257,7259,7281],[14,5671,1311],{"id":1310},[19,5673,5674,5675,5677,5678,943,5681,5683,5684,5687],{},"If your Go PDF renders ",[44,5676,920],{}," as five tofu boxes, the fix is two lines of setup, not a rewrite. Load a Japanese TTF, pass ",[44,5679,5680],{},"gpdf.WithFont",[44,5682,320],{},", write Japanese. ",[39,5685,5686],{},"gpdf subsets the glyph table automatically",", so the output carries only the characters you actually used — around 30 KB, not the 5 MB full font. This guide is the map: why Japanese PDF generation is weirdly hard in Go, the four real options in 2026, a complete working example, font subsetting internals, mixed-script edge cases, and what still doesn't work.",[14,5689,5691],{"id":5690},"why-this-guide-exists","Why this guide exists",[19,5693,5694],{},"A Japanese-text PDF in Go should be five minutes of work. For a lot of teams it's a day and a half.",[19,5696,5697,5698,5700],{},"The usual story: someone swaps in ",[44,5699,707],{},", the PDF renders blank rectangles — the infamous 豆腐 — and a senior engineer spends an afternoon figuring out whether the problem is the font path, the subset flag, the CMap, the UTF-8 flag, or the PDF reader. By the end of the day there's a Slack thread titled \"WHY IS 漢字 STILL BROKEN\" and a pull request that adds three helper functions everyone already regrets.",[19,5702,5703],{},"The root cause isn't any one of those things. It's that Go's longest-lived PDF library was designed in 2002 for PHP and Latin-1, and almost every Japanese tutorial written since has been fighting that legacy. This guide is the 2026 version: what actually works when you start clean, and what's still genuinely hard.",[19,5705,5706,5707,5709],{},"All code in this post runs against ",[44,5708,339],{}," v1.x as of 2026-04. The benchmark numbers are from an Apple M1 with Go 1.25.",[14,5711,5713],{"id":5712},"the-tofu-problem-in-90-seconds","The tofu problem in 90 seconds",[19,5715,5716,5717,5720,5721,5724],{},"PDF doesn't care about Unicode. It cares about ",[39,5718,5719],{},"glyph IDs"," — integer indices into a font's embedded glyph table. When you write ",[44,5722,5723],{},"\"こんにちは\""," to a PDF, somebody has to:",[33,5726,5727,5737,5743,5749],{},[36,5728,5729,5732,5733,5736],{},[39,5730,5731],{},"Parse the TTF"," and find the glyph ID for each code point (via the font's ",[44,5734,5735],{},"cmap"," subtable).",[36,5738,5739,5742],{},[39,5740,5741],{},"Write a ToUnicode CMap"," so the PDF reader can map glyphs back to text when the user copies or searches.",[36,5744,5745,5748],{},[39,5746,5747],{},"Subset"," the font so the PDF doesn't carry all 20,000 glyphs of Noto Sans JP.",[36,5750,5751,5754,5755,2563,5758,2563,5761,5764],{},[39,5752,5753],{},"Embed the result"," with correctly stitched ",[44,5756,5757],{},"name",[44,5759,5760],{},"OS/2",[44,5762,5763],{},"head",", and encoding objects.",[19,5766,5767,5768,962,5770,5772],{},"If any of those steps is missing or wrong, the reader can't find a glyph for the code point and paints a tofu box. The archived ",[44,5769,3917],{},[44,5771,3925],{}," lineages retrofitted all of this onto a single-byte-font internal model — the original FPDF from 2002 only knew about Latin-1. That's why setup is fragile, why the output often embeds the full font instead of a subset, and why the failure modes vary by OS and PDF reader.",[19,5774,5775,5776,5778],{},"gpdf treats CJK as a first-class case. The TTF subsetter is in the core package. The ToUnicode CMap is written automatically. There is no ",[44,5777,707],{}," dance because there is no single-byte-font legacy to retrofit around.",[14,5780,5782],{"id":5781},"the-four-real-options-in-2026","The four real options in 2026",[19,5784,5785],{},"Before writing any code: the honest field. \"Japanese-capable\" means \"will render arbitrary Japanese text without crashes or tofu, given a correct TTF.\"",[1896,5787,5788,5808],{},[1899,5789,5790],{},[1902,5791,5792,5795,5797,5800,5803,5806],{},[1905,5793,5794],{},"Option",[1905,5796,2394],{},[1905,5798,5799],{},"Deps",[1905,5801,5802],{},"CJK path",[1905,5804,5805],{},"PDF size for 300-char doc",[1905,5807,3395],{},[1912,5809,5810,5833,5855,5877],{},[1902,5811,5812,5817,5819,5822,5827,5830],{},[1917,5813,5814,5816],{},[44,5815,3925],{}," (archived 2025)",[1917,5818,4113],{},[1917,5820,5821],{},"stdlib",[1917,5823,5824,5826],{},[44,5825,707],{}," retrofit",[1917,5828,5829],{},"~5 MB (full font)",[1917,5831,5832],{},"Retrofitted onto Latin-1 core. Subsetting is opt-in and imperfect.",[1902,5834,5835,5839,5841,5843,5849,5852],{},[1917,5836,5837],{},[44,5838,4149],{},[1917,5840,4113],{},[1917,5842,5821],{},[1917,5844,5845,5848],{},[44,5846,5847],{},"AddTTFFont"," + manual",[1917,5850,5851],{},"~3 MB typical",[1917,5853,5854],{},"Low-level. You write coordinates. Subsetting exists but you drive it.",[1902,5856,5857,5861,5863,5868,5871,5874],{},[1917,5858,5859,4215],{},[44,5860,4214],{},[1917,5862,4220],{},[1917,5864,5865],{},[39,5866,5867],{},"Chromium binary",[1917,5869,5870],{},"Native via browser",[1917,5872,5873],{},"varies",[1917,5875,5876],{},"HTML/CSS. Needs fonts installed in the container. 500 MB+ image.",[1902,5878,5879,5883,5885,5890,5893,5898],{},[1917,5880,5881],{},[44,5882,339],{},[1917,5884,4113],{},[1917,5886,5887],{},[39,5888,5889],{},"stdlib only",[1917,5891,5892],{},"Native, automatic subset",[1917,5894,5895],{},[39,5896,5897],{},"~30 KB",[1917,5899,5900],{},"Pure Go. Builder API. ToUnicode CMap written for you.",[19,5902,5903],{},"Two things worth underlining:",[19,5905,5906,5909],{},[39,5907,5908],{},"The 160× size difference between \"full font embedded\" and \"automatic subset\" is not a rounding error."," A customer-facing e-commerce invoice with ten line items needs maybe 120 unique Japanese glyphs. Embedding the full Noto Sans JP (5.1 MB) on every invoice means your object storage bill includes the same 5 MB of glyph data 10 million times by the end of the year. Subset embedding carries only the glyphs you used.",[19,5911,5912,5915],{},[39,5913,5914],{},"\"Chromedp works\" is true and also the most expensive answer."," If your team already runs a headless Chrome fleet for screenshotting, piggybacking on it for PDFs is fine. If you don't, standing one up just to print 日本語 is a lot of infrastructure for a problem that's 40 lines of Go.",[14,5917,5919],{"id":5918},"the-shortest-path-that-works","The shortest path that works",[19,5921,5922,5923,5925,5926,60],{},"Start with this. It's complete — copy, save as ",[44,5924,1883],{},", drop two TTFs next to it, ",[44,5927,1887],{},[101,5929,5931],{"className":103,"code":5930,"language":105,"meta":106,"style":106},"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(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    bold, err := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", regular),\n        gpdf.WithFont(\"NotoSansJP-Bold\", bold),\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.FontFamily(\"NotoSansJP-Bold\"), template.FontSize(22))\n            c.Text(\"2026 年 4 月 16 日\")\n        })\n    })\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(7, func(c *template.ColBuilder) {\n            c.Text(\"株式会社 ABC 御中\", template.FontSize(13))\n            c.Text(\"〒 100-0001 東京都千代田区千代田 1-1\")\n        })\n        r.Col(5, func(c *template.ColBuilder) {\n            c.Text(\"合計 ¥ 128,000\", template.FontFamily(\"NotoSansJP-Bold\"), template.AlignRight())\n            c.Text(\"支払期限: 2026-05-31\", template.AlignRight())\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice-ja.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[44,5932,5933,5939,5943,5949,5957,5965,5969,5977,5985,5993,5997,6001,6011,6038,6050,6064,6068,6096,6108,6122,6126,6130,6144,6162,6192,6215,6239,6262,6266,6270,6284,6308,6338,6385,6404,6408,6412,6436,6467,6499,6518,6522,6553,6596,6623,6627,6631,6635,6653,6665,6679,6683,6724,6738,6742],{"__ignoreMap":106},[110,5934,5935,5937],{"class":112,"line":113},[110,5936,117],{"class":116},[110,5938,121],{"class":120},[110,5940,5941],{"class":112,"line":124},[110,5942,128],{"emptyLinePlaceholder":127},[110,5944,5945,5947],{"class":112,"line":131},[110,5946,135],{"class":134},[110,5948,138],{"class":116},[110,5950,5951,5953,5955],{"class":112,"line":141},[110,5952,144],{"class":116},[110,5954,147],{"class":120},[110,5956,150],{"class":116},[110,5958,5959,5961,5963],{"class":112,"line":153},[110,5960,144],{"class":116},[110,5962,158],{"class":120},[110,5964,150],{"class":116},[110,5966,5967],{"class":112,"line":163},[110,5968,128],{"emptyLinePlaceholder":127},[110,5970,5971,5973,5975],{"class":112,"line":168},[110,5972,144],{"class":116},[110,5974,173],{"class":120},[110,5976,150],{"class":116},[110,5978,5979,5981,5983],{"class":112,"line":178},[110,5980,144],{"class":116},[110,5982,183],{"class":120},[110,5984,150],{"class":116},[110,5986,5987,5989,5991],{"class":112,"line":188},[110,5988,144],{"class":116},[110,5990,193],{"class":120},[110,5992,150],{"class":116},[110,5994,5995],{"class":112,"line":198},[110,5996,201],{"class":116},[110,5998,5999],{"class":112,"line":204},[110,6000,128],{"emptyLinePlaceholder":127},[110,6002,6003,6005,6007,6009],{"class":112,"line":209},[110,6004,212],{"class":116},[110,6006,216],{"class":215},[110,6008,219],{"class":116},[110,6010,222],{"class":116},[110,6012,6013,6016,6018,6020,6022,6024,6026,6028,6030,6032,6034,6036],{"class":112,"line":225},[110,6014,6015],{"class":228},"    regular",[110,6017,232],{"class":116},[110,6019,235],{"class":228},[110,6021,238],{"class":116},[110,6023,241],{"class":228},[110,6025,60],{"class":116},[110,6027,246],{"class":215},[110,6029,249],{"class":116},[110,6031,252],{"class":116},[110,6033,81],{"class":255},[110,6035,252],{"class":116},[110,6037,201],{"class":116},[110,6039,6040,6042,6044,6046,6048],{"class":112,"line":262},[110,6041,265],{"class":134},[110,6043,235],{"class":228},[110,6045,270],{"class":116},[110,6047,273],{"class":116},[110,6049,222],{"class":116},[110,6051,6052,6054,6056,6058,6060,6062],{"class":112,"line":278},[110,6053,281],{"class":228},[110,6055,60],{"class":116},[110,6057,286],{"class":215},[110,6059,249],{"class":116},[110,6061,291],{"class":228},[110,6063,201],{"class":116},[110,6065,6066],{"class":112,"line":296},[110,6067,299],{"class":116},[110,6069,6070,6073,6075,6077,6079,6081,6083,6085,6087,6089,6092,6094],{"class":112,"line":302},[110,6071,6072],{"class":228},"    bold",[110,6074,232],{"class":116},[110,6076,235],{"class":228},[110,6078,238],{"class":116},[110,6080,241],{"class":228},[110,6082,60],{"class":116},[110,6084,246],{"class":215},[110,6086,249],{"class":116},[110,6088,252],{"class":116},[110,6090,6091],{"class":255},"NotoSansJP-Bold.ttf",[110,6093,252],{"class":116},[110,6095,201],{"class":116},[110,6097,6098,6100,6102,6104,6106],{"class":112,"line":307},[110,6099,265],{"class":134},[110,6101,235],{"class":228},[110,6103,270],{"class":116},[110,6105,273],{"class":116},[110,6107,222],{"class":116},[110,6109,6110,6112,6114,6116,6118,6120],{"class":112,"line":326},[110,6111,281],{"class":228},[110,6113,60],{"class":116},[110,6115,286],{"class":215},[110,6117,249],{"class":116},[110,6119,291],{"class":228},[110,6121,201],{"class":116},[110,6123,6124],{"class":112,"line":350},[110,6125,299],{"class":116},[110,6127,6128],{"class":112,"line":388},[110,6129,128],{"emptyLinePlaceholder":127},[110,6131,6132,6134,6136,6138,6140,6142],{"class":112,"line":413},[110,6133,310],{"class":228},[110,6135,238],{"class":116},[110,6137,315],{"class":228},[110,6139,60],{"class":116},[110,6141,320],{"class":215},[110,6143,323],{"class":116},[110,6145,6146,6148,6150,6152,6154,6156,6158,6160],{"class":112,"line":438},[110,6147,329],{"class":228},[110,6149,60],{"class":116},[110,6151,334],{"class":215},[110,6153,249],{"class":116},[110,6155,362],{"class":228},[110,6157,60],{"class":116},[110,6159,344],{"class":228},[110,6161,347],{"class":116},[110,6163,6164,6166,6168,6170,6172,6174,6176,6178,6180,6182,6184,6186,6188,6190],{"class":112,"line":444},[110,6165,329],{"class":228},[110,6167,60],{"class":116},[110,6169,357],{"class":215},[110,6171,249],{"class":116},[110,6173,362],{"class":228},[110,6175,60],{"class":116},[110,6177,367],{"class":215},[110,6179,249],{"class":116},[110,6181,362],{"class":228},[110,6183,60],{"class":116},[110,6185,376],{"class":215},[110,6187,249],{"class":116},[110,6189,382],{"class":381},[110,6191,385],{"class":116},[110,6193,6194,6196,6198,6200,6202,6204,6206,6208,6210,6213],{"class":112,"line":449},[110,6195,329],{"class":228},[110,6197,60],{"class":116},[110,6199,50],{"class":215},[110,6201,249],{"class":116},[110,6203,252],{"class":116},[110,6205,401],{"class":255},[110,6207,252],{"class":116},[110,6209,232],{"class":116},[110,6211,6212],{"class":228}," regular",[110,6214,347],{"class":116},[110,6216,6217,6219,6221,6223,6225,6227,6230,6232,6234,6237],{"class":112,"line":468},[110,6218,329],{"class":228},[110,6220,60],{"class":116},[110,6222,50],{"class":215},[110,6224,249],{"class":116},[110,6226,252],{"class":116},[110,6228,6229],{"class":255},"NotoSansJP-Bold",[110,6231,252],{"class":116},[110,6233,232],{"class":116},[110,6235,6236],{"class":228}," bold",[110,6238,347],{"class":116},[110,6240,6241,6243,6245,6247,6249,6251,6253,6255,6257,6260],{"class":112,"line":502},[110,6242,329],{"class":228},[110,6244,60],{"class":116},[110,6246,420],{"class":215},[110,6248,249],{"class":116},[110,6250,252],{"class":116},[110,6252,401],{"class":255},[110,6254,252],{"class":116},[110,6256,232],{"class":116},[110,6258,6259],{"class":381}," 11",[110,6261,347],{"class":116},[110,6263,6264],{"class":112,"line":539},[110,6265,441],{"class":116},[110,6267,6268],{"class":112,"line":561},[110,6269,128],{"emptyLinePlaceholder":127},[110,6271,6272,6274,6276,6278,6280,6282],{"class":112,"line":567},[110,6273,452],{"class":228},[110,6275,238],{"class":116},[110,6277,457],{"class":228},[110,6279,60],{"class":116},[110,6281,462],{"class":215},[110,6283,465],{"class":116},[110,6285,6286,6288,6290,6292,6294,6296,6298,6300,6302,6304,6306],{"class":112,"line":573},[110,6287,471],{"class":228},[110,6289,60],{"class":116},[110,6291,476],{"class":215},[110,6293,479],{"class":116},[110,6295,483],{"class":482},[110,6297,486],{"class":116},[110,6299,489],{"class":120},[110,6301,60],{"class":116},[110,6303,494],{"class":120},[110,6305,497],{"class":116},[110,6307,222],{"class":116},[110,6309,6310,6312,6314,6316,6318,6320,6322,6324,6326,6328,6330,6332,6334,6336],{"class":112,"line":578},[110,6311,505],{"class":228},[110,6313,60],{"class":116},[110,6315,510],{"class":215},[110,6317,249],{"class":116},[110,6319,515],{"class":381},[110,6321,232],{"class":116},[110,6323,520],{"class":116},[110,6325,523],{"class":482},[110,6327,486],{"class":116},[110,6329,489],{"class":120},[110,6331,60],{"class":116},[110,6333,532],{"class":120},[110,6335,497],{"class":116},[110,6337,222],{"class":116},[110,6339,6340,6342,6344,6346,6348,6350,6352,6354,6356,6358,6360,6362,6364,6366,6368,6370,6372,6374,6376,6378,6380,6383],{"class":112,"line":599},[110,6341,542],{"class":228},[110,6343,60],{"class":116},[110,6345,547],{"class":215},[110,6347,249],{"class":116},[110,6349,252],{"class":116},[110,6351,1673],{"class":255},[110,6353,252],{"class":116},[110,6355,232],{"class":116},[110,6357,1680],{"class":228},[110,6359,60],{"class":116},[110,6361,2256],{"class":215},[110,6363,249],{"class":116},[110,6365,252],{"class":116},[110,6367,6229],{"class":255},[110,6369,252],{"class":116},[110,6371,1693],{"class":116},[110,6373,1680],{"class":228},[110,6375,60],{"class":116},[110,6377,1685],{"class":215},[110,6379,249],{"class":116},[110,6381,6382],{"class":381},"22",[110,6384,2279],{"class":116},[110,6386,6387,6389,6391,6393,6395,6397,6400,6402],{"class":112,"line":612},[110,6388,542],{"class":228},[110,6390,60],{"class":116},[110,6392,547],{"class":215},[110,6394,249],{"class":116},[110,6396,252],{"class":116},[110,6398,6399],{"class":255},"2026 年 4 月 16 日",[110,6401,252],{"class":116},[110,6403,201],{"class":116},[110,6405,6406],{"class":112,"line":627},[110,6407,564],{"class":116},[110,6409,6410],{"class":112,"line":632},[110,6411,570],{"class":116},[110,6413,6414,6416,6418,6420,6422,6424,6426,6428,6430,6432,6434],{"class":112,"line":678},[110,6415,471],{"class":228},[110,6417,60],{"class":116},[110,6419,476],{"class":215},[110,6421,479],{"class":116},[110,6423,483],{"class":482},[110,6425,486],{"class":116},[110,6427,489],{"class":120},[110,6429,60],{"class":116},[110,6431,494],{"class":120},[110,6433,497],{"class":116},[110,6435,222],{"class":116},[110,6437,6438,6440,6442,6444,6446,6449,6451,6453,6455,6457,6459,6461,6463,6465],{"class":112,"line":693},[110,6439,505],{"class":228},[110,6441,60],{"class":116},[110,6443,510],{"class":215},[110,6445,249],{"class":116},[110,6447,6448],{"class":381},"7",[110,6450,232],{"class":116},[110,6452,520],{"class":116},[110,6454,523],{"class":482},[110,6456,486],{"class":116},[110,6458,489],{"class":120},[110,6460,60],{"class":116},[110,6462,532],{"class":120},[110,6464,497],{"class":116},[110,6466,222],{"class":116},[110,6468,6469,6471,6473,6475,6477,6479,6482,6484,6486,6488,6490,6492,6494,6497],{"class":112,"line":698},[110,6470,542],{"class":228},[110,6472,60],{"class":116},[110,6474,547],{"class":215},[110,6476,249],{"class":116},[110,6478,252],{"class":116},[110,6480,6481],{"class":255},"株式会社 ABC 御中",[110,6483,252],{"class":116},[110,6485,232],{"class":116},[110,6487,1680],{"class":228},[110,6489,60],{"class":116},[110,6491,1685],{"class":215},[110,6493,249],{"class":116},[110,6495,6496],{"class":381},"13",[110,6498,2279],{"class":116},[110,6500,6501,6503,6505,6507,6509,6511,6514,6516],{"class":112,"line":1859},[110,6502,542],{"class":228},[110,6504,60],{"class":116},[110,6506,547],{"class":215},[110,6508,249],{"class":116},[110,6510,252],{"class":116},[110,6512,6513],{"class":255},"〒 100-0001 東京都千代田区千代田 1-1",[110,6515,252],{"class":116},[110,6517,201],{"class":116},[110,6519,6520],{"class":112,"line":1864},[110,6521,564],{"class":116},[110,6523,6524,6526,6528,6530,6532,6535,6537,6539,6541,6543,6545,6547,6549,6551],{"class":112,"line":3116},[110,6525,505],{"class":228},[110,6527,60],{"class":116},[110,6529,510],{"class":215},[110,6531,249],{"class":116},[110,6533,6534],{"class":381},"5",[110,6536,232],{"class":116},[110,6538,520],{"class":116},[110,6540,523],{"class":482},[110,6542,486],{"class":116},[110,6544,489],{"class":120},[110,6546,60],{"class":116},[110,6548,532],{"class":120},[110,6550,497],{"class":116},[110,6552,222],{"class":116},[110,6554,6555,6557,6559,6561,6563,6565,6568,6570,6572,6574,6576,6578,6580,6582,6584,6586,6588,6590,6592,6594],{"class":112,"line":3136},[110,6556,542],{"class":228},[110,6558,60],{"class":116},[110,6560,547],{"class":215},[110,6562,249],{"class":116},[110,6564,252],{"class":116},[110,6566,6567],{"class":255},"合計 ¥ 128,000",[110,6569,252],{"class":116},[110,6571,232],{"class":116},[110,6573,1680],{"class":228},[110,6575,60],{"class":116},[110,6577,2256],{"class":215},[110,6579,249],{"class":116},[110,6581,252],{"class":116},[110,6583,6229],{"class":255},[110,6585,252],{"class":116},[110,6587,1693],{"class":116},[110,6589,1680],{"class":228},[110,6591,60],{"class":116},[110,6593,4521],{"class":215},[110,6595,1703],{"class":116},[110,6597,6598,6600,6602,6604,6606,6608,6611,6613,6615,6617,6619,6621],{"class":112,"line":3141},[110,6599,542],{"class":228},[110,6601,60],{"class":116},[110,6603,547],{"class":215},[110,6605,249],{"class":116},[110,6607,252],{"class":116},[110,6609,6610],{"class":255},"支払期限: 2026-05-31",[110,6612,252],{"class":116},[110,6614,232],{"class":116},[110,6616,1680],{"class":228},[110,6618,60],{"class":116},[110,6620,4521],{"class":215},[110,6622,1703],{"class":116},[110,6624,6625],{"class":112,"line":3172},[110,6626,564],{"class":116},[110,6628,6629],{"class":112,"line":3192},[110,6630,570],{"class":116},[110,6632,6633],{"class":112,"line":3197},[110,6634,128],{"emptyLinePlaceholder":127},[110,6636,6637,6639,6641,6643,6645,6647,6649,6651],{"class":112,"line":3228},[110,6638,581],{"class":228},[110,6640,232],{"class":116},[110,6642,235],{"class":228},[110,6644,238],{"class":116},[110,6646,457],{"class":228},[110,6648,60],{"class":116},[110,6650,594],{"class":215},[110,6652,465],{"class":116},[110,6654,6655,6657,6659,6661,6663],{"class":112,"line":3248},[110,6656,265],{"class":134},[110,6658,235],{"class":228},[110,6660,270],{"class":116},[110,6662,273],{"class":116},[110,6664,222],{"class":116},[110,6666,6667,6669,6671,6673,6675,6677],{"class":112,"line":3253},[110,6668,281],{"class":228},[110,6670,60],{"class":116},[110,6672,286],{"class":215},[110,6674,249],{"class":116},[110,6676,291],{"class":228},[110,6678,201],{"class":116},[110,6680,6681],{"class":112,"line":3258},[110,6682,299],{"class":116},[110,6684,6685,6687,6689,6691,6693,6695,6697,6699,6701,6704,6706,6708,6710,6712,6714,6716,6718,6720,6722],{"class":112,"line":3263},[110,6686,265],{"class":134},[110,6688,235],{"class":228},[110,6690,238],{"class":116},[110,6692,241],{"class":228},[110,6694,60],{"class":116},[110,6696,645],{"class":215},[110,6698,249],{"class":116},[110,6700,252],{"class":116},[110,6702,6703],{"class":255},"invoice-ja.pdf",[110,6705,252],{"class":116},[110,6707,232],{"class":116},[110,6709,659],{"class":228},[110,6711,232],{"class":116},[110,6713,664],{"class":381},[110,6715,667],{"class":116},[110,6717,235],{"class":228},[110,6719,270],{"class":116},[110,6721,273],{"class":116},[110,6723,222],{"class":116},[110,6725,6726,6728,6730,6732,6734,6736],{"class":112,"line":3269},[110,6727,281],{"class":228},[110,6729,60],{"class":116},[110,6731,286],{"class":215},[110,6733,249],{"class":116},[110,6735,291],{"class":228},[110,6737,201],{"class":116},[110,6739,6740],{"class":112,"line":3294},[110,6741,299],{"class":116},[110,6743,6744],{"class":112,"line":3326},[110,6745,701],{"class":116},[19,6747,6748],{},"Things to notice without me narrating them to death:",[985,6750,6751,6767,6776,6790],{},[36,6752,6753,42,6761,6763,6764,6766],{},[39,6754,6755,6756,6758,6759,60],{},"No ",[44,6757,707],{},", no UTF-8 flag, no font path argument to ",[44,6760,547],{},[44,6762,5680],{}," registers a family; ",[44,6765,59],{}," just writes Unicode. The plumbing is internal.",[36,6768,6769,6772,6773,6775],{},[39,6770,6771],{},"Bold is a separate family, not a flag."," This matches how TTFs ship (Noto Sans JP Regular and Noto Sans JP Bold are distinct files with different ",[44,6774,5757],{}," tables). Gothic and Mincho variants, or Source Han Sans JP Normal/Heavy, follow the same pattern.",[36,6777,6778,42,6781,962,6784,6787,6788,60],{},[39,6779,6780],{},"Layout is grid, not cursor.",[44,6782,6783],{},"r.Col(7, ...)",[44,6785,6786],{},"r.Col(5, ...)"," add to 12. Widths are declarative; you don't compute x-coordinates. More on this in ",[723,6789,2551],{"href":3894},[36,6791,6792,6798],{},[39,6793,6794,6797],{},[44,6795,6796],{},"AlignRight()"," is locale-agnostic."," The Japanese \"¥ 128,000\" right-aligns the same way as \"$1,280.00\" would. The text content doesn't change the layout code.",[19,6800,6801,6802,6804,6805,6807],{},"Open the resulting ",[44,6803,6703],{}," in any reader. Select \"株式会社 ABC 御中\". Paste into a text editor. You get ",[44,6806,6481],{},", not a jumble. That's the ToUnicode CMap working; gpdf writes one by default.",[14,6809,6811],{"id":6810},"font-subsetting-the-hidden-size-bomb","Font subsetting: the hidden size bomb",[19,6813,6814,6815,60],{},"Here is the single most important property of CJK-in-PDF that tutorials skip: ",[39,6816,6817],{},"subset embedding",[19,6819,6820],{},"A TTF font is a collection of glyph outlines plus metadata tables. Noto Sans JP Regular ships about 17,500 glyphs and weighs 5.1 MB. A typical invoice uses 60 to 200 unique Japanese characters. Embedding the full font on every document is an order-of-magnitude waste.",[19,6822,6823],{},"Subset embedding keeps only the glyphs you used. gpdf does this automatically. You can see it by running the example above and inspecting the output:",[101,6825,6827],{"className":1207,"code":6826,"language":1209,"meta":106,"style":106},"$ ls -l invoice-ja.pdf\n-rw-r--r--  1 dev  staff  34892 Apr 16 10:12 invoice-ja.pdf\n",[44,6828,6829,6843],{"__ignoreMap":106},[110,6830,6831,6834,6837,6840],{"class":112,"line":113},[110,6832,6833],{"class":120},"$",[110,6835,6836],{"class":255}," ls",[110,6838,6839],{"class":255}," -l",[110,6841,6842],{"class":255}," invoice-ja.pdf\n",[110,6844,6845,6848,6851,6854,6857,6860,6863,6865,6868],{"class":112,"line":124},[110,6846,6847],{"class":120},"-rw-r--r--",[110,6849,6850],{"class":381},"  1",[110,6852,6853],{"class":255}," dev",[110,6855,6856],{"class":255},"  staff",[110,6858,6859],{"class":381},"  34892",[110,6861,6862],{"class":255}," Apr",[110,6864,4754],{"class":381},[110,6866,6867],{"class":255}," 10:12",[110,6869,6842],{"class":255},[19,6871,6872,6873,962,6875,6878,6879,6882],{},"34 KB. For comparison, the same document generated with ",[44,6874,3925],{},[44,6876,6877],{},"AddUTF8Font(\"NotoSansJP\", \"NotoSansJP-Regular.ttf\", true)"," — where the third argument is the UTF-8 flag — is ",[39,6880,6881],{},"4.9 MB",". Same input, same output text, 143× larger file. The reason is the fpdf code path embeds the entire font table rather than subsetting it at emit time.",[19,6884,6885],{},"A few consequences worth naming:",[985,6887,6888,6899,6905],{},[36,6889,6890,6891,6894,6895,6898],{},"At ",[39,6892,6893],{},"10 invoices per second"," (a normal SaaS scale), the subsetting difference is the difference between ",[39,6896,6897],{},"0.3 MB/s and 43 MB/s"," of outbound PDF bytes. Your load balancer has an opinion on that.",[36,6900,6901,6904],{},[39,6902,6903],{},"Cold storage bills"," scale linearly with PDF size. Five million archived invoices at 5 MB each is 25 TB. At 30 KB each, it's 150 GB. Object storage pricing makes this a four-figures-versus-two-figures monthly line item.",[36,6906,6907,6910],{},[39,6908,6909],{},"Email delivery"," has 10–25 MB attachment limits depending on provider. A 5 MB Japanese invoice plus any other attachment plus MIME encoding starts bumping into that ceiling.",[19,6912,6913,6914,2563,6917,2563,6920,1884,6923,6926],{},"gpdf subsets at render time. There's no flag to turn it on. You can see which glyphs ended up in the output by running gpdf's verification tool locally, but the short version is: if you used ",[44,6915,6916],{},"株",[44,6918,6919],{},"式",[44,6921,6922],{},"会",[44,6924,6925],{},"社",", those four glyphs are in the output and the other 17,496 are not.",[14,6928,6930],{"id":6929},"mixed-scripts-kanji-kana-ascii-on-one-line","Mixed scripts: kanji + kana + ASCII on one line",[19,6932,6933],{},"Japanese text is rarely Japanese-only. A real-world line in a Japanese document looks like this:",[101,6935,6939],{"className":6936,"code":6938,"language":1089},[6937],"language-text","API の P95 レイテンシは 50 ms 未満です。\n",[44,6940,6938],{"__ignoreMap":106},[19,6942,6943],{},"That's five scripts: romaji (ASCII Latin), katakana, hiragana, kanji (Han), and numerals. A naive implementation picks the wrong font for the ASCII parts and you end up with a monospaced \"API\" next to proportional Japanese, which looks terrible.",[19,6945,6946,6947,6950,6951,962,6954,6957],{},"gpdf's default behavior is to render ",[39,6948,6949],{},"every code point in the registered family",". If Noto Sans JP is your default, ",[44,6952,6953],{},"API",[44,6955,6956],{},"50 ms"," are drawn with Noto Sans JP's Latin glyphs, which Noto provides (most Japanese superfamilies do). The result looks like a single typeface, because it is.",[19,6959,6960],{},"If you want to mix families deliberately — say, use a condensed sans for ASCII and Noto Sans JP for Japanese — register both and override per-text-call:",[101,6962,6964],{"className":103,"code":6963,"language":105,"meta":106,"style":106},"c.Text(\"API の P95 レイテンシは 50 ms 未満です。\",\n    template.FontFamily(\"NotoSansJP\"))\nc.Text(\"API latency (P95) is under 50 ms.\",\n    template.FontFamily(\"InterVariable\"))\n",[44,6965,6966,6986,7005,7024],{"__ignoreMap":106},[110,6967,6968,6970,6972,6974,6976,6978,6981,6983],{"class":112,"line":113},[110,6969,523],{"class":228},[110,6971,60],{"class":116},[110,6973,547],{"class":215},[110,6975,249],{"class":116},[110,6977,252],{"class":116},[110,6979,6980],{"class":255},"API の P95 レイテンシは 50 ms 未満です。",[110,6982,252],{"class":116},[110,6984,6985],{"class":116},",\n",[110,6987,6988,6991,6993,6995,6997,6999,7001,7003],{"class":112,"line":124},[110,6989,6990],{"class":228},"    template",[110,6992,60],{"class":116},[110,6994,2256],{"class":215},[110,6996,249],{"class":116},[110,6998,252],{"class":116},[110,7000,401],{"class":255},[110,7002,252],{"class":116},[110,7004,2279],{"class":116},[110,7006,7007,7009,7011,7013,7015,7017,7020,7022],{"class":112,"line":131},[110,7008,523],{"class":228},[110,7010,60],{"class":116},[110,7012,547],{"class":215},[110,7014,249],{"class":116},[110,7016,252],{"class":116},[110,7018,7019],{"class":255},"API latency (P95) is under 50 ms.",[110,7021,252],{"class":116},[110,7023,6985],{"class":116},[110,7025,7026,7028,7030,7032,7034,7036,7039,7041],{"class":112,"line":141},[110,7027,6990],{"class":228},[110,7029,60],{"class":116},[110,7031,2256],{"class":215},[110,7033,249],{"class":116},[110,7035,252],{"class":116},[110,7037,7038],{"class":255},"InterVariable",[110,7040,252],{"class":116},[110,7042,2279],{"class":116},[19,7044,7045,7046,7048,7049,7052],{},"Two ",[44,7047,59],{}," calls, two families, no script-detection logic in your code. If you need intra-line mixing (ASCII Inter + Japanese Noto in the ",[746,7050,7051],{},"same"," sentence), that's coming in gpdf v1.2; today the workaround is to split at script boundaries manually and lay out with a horizontal row of columns.",[14,7054,7056],{"id":7055},"what-still-hurts","What still hurts",[19,7058,7059],{},"The Japanese PDF story in Go is 95% solved. Here's the 5%.",[19,7061,7062,7065,7066,60],{},[39,7063,7064],{},"Vertical text (縦書き) is not there yet."," gpdf renders horizontal text only in v1.x. Traditional Japanese layout — right-to-left columns of top-to-bottom characters with the appropriate glyph rotation and punctuation repositioning — is a deep layout engine change, not a rendering tweak. The open issue has a proposed design; it'll land when it lands. For now, if you need 縦書き for books or formal correspondence, generate with a tool that supports it (Word, InDesign, or a pandoc + LuaLaTeX pipeline) and embed the output PDF with ",[44,7067,7068],{},"gpdf.Merge",[19,7070,7071,7074,7075,7078],{},[39,7072,7073],{},"Ruby annotations (振り仮名) are workaround-only."," There's no ",[44,7076,7077],{},"c.Ruby(\"漢字\", \"かんじ\")"," primitive. If you need ruby for children's content or language textbooks, the workaround is a two-row column: small kana text on top, regular kanji below, aligned. It works, but it's manual, and fine kerning across furigana boundaries takes care.",[19,7080,7081,7084,7085,2563,7088,2563,7091,7094,7095,7097,7098,7102],{},[39,7082,7083],{},"Complex fallbacks across multiple CJK fonts."," If a user submits text that mixes Japanese kanji with Chinese-only characters (the character forms differ — ",[44,7086,7087],{},"直",[44,7089,7090],{},"骨",[44,7092,7093],{},"角"," render subtly differently in CN vs JP), you need to manually split and use two families. gpdf doesn't auto-fall-back across families within a single ",[44,7096,59],{}," call. In practice, very few documents need this; if yours does, see ",[723,7099,7101],{"href":7100},"/blog/","Multi-language PDFs: mixing JP/CN/KR/EN",". (Article pending — B-070.)",[19,7104,7105,7108,7109,7112,7113,7116],{},[39,7106,7107],{},"PDF/A-2b strict compliance with Japanese."," gpdf produces PDF/A output via ",[44,7110,7111],{},"gpdf.WithPDFA",", but the tight compliance requirements around embedded glyph metadata, the ",[44,7114,7115],{},"ActualText"," span on every CJK run, and tagged structure trees are still being ironed out for the CJK case. If you're exporting for long-term archival under 電子帳簿保存法, validate with a third-party tool (veraPDF is free) before committing.",[19,7118,7119],{},"None of these are blockers for the common cases: invoices, reports, statements, receipts, certificates. They're worth naming because somebody reading this is about to hit one of them in production, and \"it's on the roadmap\" is less useful than \"here's the workaround.\"",[14,7121,7123],{"id":7122},"a-word-on-compliance","A word on compliance",[19,7125,7126,7127,7130],{},"One piece of ecosystem context that usually goes unsaid: ",[39,7128,7129],{},"Japanese PDF generation in 2026 is not just a typography problem."," Two regulatory shifts push it into the compliance conversation.",[19,7132,5298,7133,7136],{},[39,7134,7135],{},"適格請求書 (qualified invoice) regime"," under the consumption-tax reform requires invoices to include specific fields (registered business number, applicable tax rate, breakdown) and to be retained in a tamper-evident way. PDFs are the default format for this, and \"tamper-evident\" maps to PDF digital signatures — PAdES-B-LT in the strict case.",[19,7138,5298,7139,7142],{},[39,7140,7141],{},"電子帳簿保存法 (e-book storage act)",", revised in 2024, extended retention mandates to include invoices stored in electronic form. Archived PDFs must meet certain integrity requirements. PDF/A-2b or PDF/A-3b is the de-facto target format.",[19,7144,7145,7146,7149,7150,7153,7154,7156],{},"Both requirements lean on ",[39,7147,7148],{},"native PDF features"," — signatures, long-term validation, PDF/A embedded metadata. HTML-to-PDF via headless browser does not meet either cleanly; Chromium's PDF output isn't PDF/A-compliant and can't embed digital signatures in a single step. A native Go stack (gpdf + ",[44,7151,7152],{},"gpdf/signature"," for PAdES + ",[44,7155,7111],{},") does the whole chain in one pipeline without leaving the process.",[19,7158,7159],{},"This is a future-topic flag rather than a deep dive — signature and PDF/A each deserve their own hero article (they're B-067 and B-068 on the backlog). But if you're choosing a Japanese PDF stack today and compliance is anywhere on your radar, pick a stack that can do signatures and PDF/A natively. The migration tax from \"works today\" to \"passes audit\" is real.",[14,7161,5523],{"id":5522},[19,7163,7164,7167,7168,762,7171,7174,7175,7178,7179,7182,7183,7186],{},[39,7165,7166],{},"Do I need to install fonts on the server or in the container?","\nNo. gpdf reads TTF bytes; it doesn't go through the system font cache. ",[44,7169,7170],{},"os.ReadFile(\"NotoSansJP-Regular.ttf\")",[44,7172,7173],{},"//go:embed NotoSansJP-Regular.ttf"," works identically on macOS, Linux, and Windows, inside a distroless container, and on AWS Lambda. No ",[44,7176,7177],{},"fontconfig",", no ",[44,7180,7181],{},"fc-cache -fv",". This is one of the reasons gpdf works in ",[44,7184,7185],{},"FROM scratch"," images.",[19,7188,7189,7192],{},[39,7190,7191],{},"Noto Sans JP vs Source Han Sans JP — does it matter?","\nThey're the same font family under two names. Adobe publishes Source Han Sans JP; Google repackages it as Noto Sans JP. Glyph coverage is identical. Pick whichever license distribution fits your legal review; both are SIL Open Font License. For brand-neutral documentation we default to Noto Sans JP because the file names are easier to remember.",[19,7194,7195,7198],{},[39,7196,7197],{},"What about 游ゴシック (Yu Gothic) or Hiragino?","\nThose are OS-bundled proprietary fonts. You can use them if your deployment target has licensed them (Windows Server bundles Yu Gothic; macOS bundles Hiragino), but you'll need to source the TTF file and confirm redistribution terms for your container build. For open deployments, stick with Noto Sans JP or IPAex Gothic (both free redistribution).",[19,7200,7201,7208,7209,7212],{},[39,7202,7203,7204,7207],{},"The PDF renders but ",[44,7205,7206],{},"Ctrl+F"," search finds nothing. Why?","\nAlmost always a ToUnicode CMap issue. gpdf writes one automatically, so if you're seeing this with gpdf, open an issue with the reader name. If you're seeing it with gofpdf, the fix is to enable the UTF-8 flag ",[746,7210,7211],{},"and"," ensure the reader supports CID fonts — old Preview.app versions on macOS have known issues. Test with Adobe Reader or Chrome as a control.",[19,7214,7215,7218,7219,7222],{},[39,7216,7217],{},"How do I add a JIS X 0213 character that's not in the font?","\nYou don't — there's no glyph to draw. The practical answer is \"use a font that covers JIS X 0213.\" Noto Sans JP covers the full BMP plus JIS X 0213 Level 1. For rare historical variants, Hanazono Mincho (花園明朝) is the last-mile fallback. If a code point isn't in any font, gpdf emits the Unicode replacement character (U+FFFD) rather than a silent tofu — so you'll see ",[44,7220,7221],{},"�"," in the output and know to check.",[19,7224,7225,7228,7229,7231],{},[39,7226,7227],{},"Is there a performance cost to CJK vs ASCII?","\nSmall. gpdf's benchmark for a \"complex CJK invoice\" is 133 µs per document on an Apple M1, vs 108 µs for a 4×10 ASCII line-items table. That's a ~23% overhead, almost entirely from the larger glyph-lookup and subsetting work. For reference, ",[44,7230,3925],{}," on the same CJK benchmark is 254 µs, and Maroto v2 is 10.4 ms. Japanese rendering isn't the bottleneck in your service.",[14,7233,1201],{"id":1200},[19,7235,7236],{},"gpdf is a Go library for generating PDFs. MIT, zero external dependencies, native CJK.",[101,7238,7239],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,7240,7241],{"__ignoreMap":106},[110,7242,7243,7245,7247],{"class":112,"line":113},[110,7244,105],{"class":120},[110,7246,1218],{"class":255},[110,7248,1221],{"class":255},[19,7250,7251,1229,7254],{},[723,7252,1228],{"href":1226,"rel":7253},[727],[723,7255,1234],{"href":1232,"rel":7256},[727],[14,7258,5608],{"id":5607},[985,7260,7261,7266,7271,7276],{},[36,7262,7263,7265],{},[723,7264,1175],{"href":1174}," — the three-line recipe without the background",[36,7267,7268,7270],{},[723,7269,1185],{"href":1184}," — Regular / Bold / Medium weight setup",[36,7272,7273,7275],{},[723,7274,2551],{"href":3894}," — the layout idiom that replaces cursor math",[36,7277,7278,7280],{},[723,7279,3902],{"href":5656}," — the broader 2026 landscape",[1236,7282,2508],{},{"title":106,"searchDepth":124,"depth":124,"links":7284},[7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,7296],{"id":1310,"depth":124,"text":1311},{"id":5690,"depth":124,"text":5691},{"id":5712,"depth":124,"text":5713},{"id":5781,"depth":124,"text":5782},{"id":5918,"depth":124,"text":5919},{"id":6810,"depth":124,"text":6811},{"id":6929,"depth":124,"text":6930},{"id":7055,"depth":124,"text":7056},{"id":7122,"depth":124,"text":7123},{"id":5522,"depth":124,"text":5523},{"id":1200,"depth":124,"text":1201},{"id":5607,"depth":124,"text":5608},"How to generate Japanese PDFs in Go in 2026 — fonts, TrueType subsets, mixed kanji/kana/ASCII, and why CGO and Chromium are unnecessary.",{"name":7299,"totalTime":7300,"tools":7301,"steps":7303},"Generate a Japanese PDF in Go with native TrueType subsetting","PT20M",[1259,7302],"NotoSansJP-Regular.ttf and NotoSansJP-Bold.ttf (or any CJK-capable TTF pair)",[7304,7307,7310,7313,7316,7319],{"name":7305,"text":7306},"Install gpdf and fetch the fonts","Run go get github.com/gpdf-dev/gpdf. Download Noto Sans JP Regular and Bold from Google Fonts and drop the TTF files next to main.go. No CGO, no system font setup.",{"name":7308,"text":7309},"Load the TTF bytes at startup","Read both TTF files with os.ReadFile into []byte buffers. //go:embed works if you want the fonts compiled into the binary for single-file deployment.",{"name":7311,"text":7312},"Register the fonts at document construction","Pass gpdf.WithFont(\"NotoSansJP\", regular) and gpdf.WithFont(\"NotoSansJP-Bold\", bold) to gpdf.NewDocument. The family name is arbitrary — it's the handle you reference later.",{"name":7314,"text":7315},"Set the Japanese font as default","Add gpdf.WithDefaultFont(\"NotoSansJP\", 11). Every c.Text call then picks up the Japanese font without an explicit FontFamily option.",{"name":7317,"text":7318},"Write the document tree with c.Text","Inside a page.AutoRow block, call r.Col(span, fn) and c.Text(\"こんにちは、世界。\"). Bold and size are template options, not separate methods.",{"name":7320,"text":7321},"Generate and inspect the output","Call doc.Generate() to get []byte, write to disk with os.WriteFile. Open the PDF, select the text, paste it into a text editor — the ToUnicode CMap guarantees copy-paste works.",{},{"title":5666,"description":7297},"blog/007.japanese-pdf-in-go",[2547,1285,1284],"QJusugE0TlEj0twoIZgBoPWmHC385C_tDWmgtjYn_gQ",{"id":7328,"title":3824,"author":7329,"body":7330,"date":8953,"description":8954,"draft":1253,"extension":1254,"howTo":1277,"image":1277,"meta":8955,"navigation":127,"path":3823,"seo":8956,"stem":8957,"tags":8958,"updated":1277,"__hash__":8959},"blog/blog/002.go-pdf-library-showdown-2026.md",{"name":8,"url":9},{"type":11,"value":7331,"toc":8935},[7332,7334,7340,7360,7363,7370,7374,7377,7408,7411,7414,7418,7599,7602,7606,7620,7623,7649,7655,7660,7756,7764,7768,7775,7781,7787,7793,7800,7804,7814,7891,7894,7898,7952,7955,7960,7964,8001,8005,8008,8049,8052,8056,8059,8779,8796,8800,8803,8842,8844,8850,8860,8866,8872,8881,8883,8886,8898,8906,8908,8932],[14,7333,1311],{"id":1310},[19,7335,7336,7337,7339],{},"Five years ago, the Go PDF library search landed you on ",[39,7338,3917],{},". Today it's archived. So is its community fork. What's left is a much smaller field than the search results suggest:",[985,7341,7342,7348,7354],{},[36,7343,7344,7347],{},[39,7345,7346],{},"Maintained and actively developed:"," gpdf (this team), signintech/gopdf, johnfercher/maroto v2 — but Maroto still depends on an archived gofpdf.",[36,7349,7350,7353],{},[39,7351,7352],{},"Archived:"," jung-kurt/gofpdf (2021), go-pdf/fpdf (2025).",[36,7355,7356,7359],{},[39,7357,7358],{},"Commercial / AGPL:"," unidoc/unipdf.",[19,7361,7362],{},"This post benchmarks the maintained libraries on four workloads, lays out licenses and dependency graphs, and makes a recommendation by use case. We run it again next year.",[19,7364,7365,7366,7369],{},"Bias disclosure: we ship gpdf. The benchmark code is public (",[44,7367,7368],{},"_benchmark/benchmark_test.go",") — clone it, re-run the numbers, tell us where we're wrong.",[14,7371,7373],{"id":7372},"what-were-actually-comparing","What we're actually comparing",[19,7375,7376],{},"The phrase \"Go PDF library\" covers at least three different tools pretending to be the same category:",[33,7378,7379,7389,7400],{},[36,7380,7381,7384,7385,2563,7387,60],{},[39,7382,7383],{},"Low-level PDF writers"," — you push bytes and draw with primitives. ",[44,7386,3917],{},[44,7388,4149],{},[36,7390,7391,7394,7395,2563,7398,60],{},[39,7392,7393],{},"Layout libraries that wrap a writer"," — declarative rows and columns on top. ",[44,7396,7397],{},"johnfercher/maroto v2",[44,7399,339],{},[36,7401,7402,7405,7406,60],{},[39,7403,7404],{},"Full document suites"," — parsing, signing, PDF/A, OCR, redaction. ",[44,7407,4193],{},[19,7409,7410],{},"Picking \"the best Go PDF library\" without saying which category you need is how most recommendation threads on Reddit go sideways. We try to keep the distinction visible in each comparison below.",[19,7412,7413],{},"Missing from the lineup: anything that shells out to a headless Chromium (go-rod, chromedp). Those aren't PDF libraries; they're browsers that happen to print. Great for fidelity on CSS-heavy designs, bad for cold start, bad for memory, bad for distroless. If your spec is \"our designer hands me HTML+CSS and wants pixel-perfect rendering,\" those tools exist and we're not competing with them in this post.",[14,7415,7417],{"id":7416},"the-scoreboard","The scoreboard",[1896,7419,7420,7446],{},[1899,7421,7422],{},[1902,7423,7424,7426,7429,7432,7434,7437,7440,7443],{},[1905,7425,4084],{},[1905,7427,7428],{},"Last release",[1905,7430,7431],{},"Archived",[1905,7433,2394],{},[1905,7435,7436],{},"Core deps",[1905,7438,7439],{},"CJK",[1905,7441,7442],{},"Layout grid",[1905,7444,7445],{},"2026 status",[1912,7447,7448,7479,7499,7523,7550,7575],{},[1902,7449,7450,7455,7458,7461,7463,7468,7471,7474],{},[1917,7451,7452,7454],{},[39,7453,339],{}," (this team)",[1917,7456,7457],{},"active",[1917,7459,7460],{},"—",[1917,7462,4113],{},[1917,7464,7465],{},[39,7466,7467],{},"0",[1917,7469,7470],{},"native",[1917,7472,7473],{},"12-col",[1917,7475,7476],{},[39,7477,7478],{},"maintained",[1902,7480,7481,7483,7485,7487,7489,7491,7494,7497],{},[1917,7482,4149],{},[1917,7484,7457],{},[1917,7486,7460],{},[1917,7488,4113],{},[1917,7490,7467],{},[1917,7492,7493],{},"manual TTF",[1917,7495,7496],{},"no",[1917,7498,7478],{},[1902,7500,7501,7503,7505,7507,7509,7514,7517,7520],{},[1917,7502,7397],{},[1917,7504,7457],{},[1917,7506,7460],{},[1917,7508,4113],{},[1917,7510,7511],{},[39,7512,7513],{},"gofpdf (archived)",[1917,7515,7516],{},"via gofpdf",[1917,7518,7519],{},"row/col",[1917,7521,7522],{},"maintained on a dead base",[1902,7524,7525,7527,7530,7535,7537,7539,7543,7545],{},[1917,7526,3917],{},[1917,7528,7529],{},"2021",[1917,7531,7532],{},[39,7533,7534],{},"2021-09-08",[1917,7536,4113],{},[1917,7538,7467],{},[1917,7540,7541],{},[44,7542,707],{},[1917,7544,7496],{},[1917,7546,7547],{},[39,7548,7549],{},"archived",[1902,7551,7552,7554,7557,7561,7563,7565,7569,7571],{},[1917,7553,3925],{},[1917,7555,7556],{},"2023",[1917,7558,7559],{},[39,7560,3929],{},[1917,7562,4113],{},[1917,7564,7467],{},[1917,7566,7567],{},[44,7568,707],{},[1917,7570,7496],{},[1917,7572,7573],{},[39,7574,7549],{},[1902,7576,7577,7579,7581,7583,7588,7591,7594,7596],{},[1917,7578,4193],{},[1917,7580,7457],{},[1917,7582,7460],{},[1917,7584,7585],{},[39,7586,7587],{},"AGPL-3.0 / Commercial",[1917,7589,7590],{},"many",[1917,7592,7593],{},"yes",[1917,7595,7496],{},[1917,7597,7598],{},"commercial",[19,7600,7601],{},"Three things to notice. One: half the field is archived. Two: Maroto is maintained, but its foundation isn't — that's a supply-chain problem even if it builds today. Three: if you aren't willing to accept the AGPL, unidoc becomes a commercial-license conversation, not a technical one.",[14,7603,7605],{"id":7604},"the-benchmark","The benchmark",[19,7607,7608,7609,7615,7616,7619],{},"Code: ",[723,7610,7613],{"href":7611,"rel":7612},"https://github.com/gpdf-dev/gpdf/tree/main/_benchmark",[727],[44,7614,7368],{}," in the gpdf repo. Environment: Apple M1 (Max, 32 GB, macOS 14.5), Go 1.25, no CGO. Each case runs for at least five seconds wall time. ",[44,7617,7618],{},"-benchmem"," was on; we report ns/op and allocations.",[19,7621,7622],{},"We picked the four cases below because they reflect what people actually generate, not something micro-synthetic:",[33,7624,7625,7631,7637,7643],{},[36,7626,7627,7630],{},[39,7628,7629],{},"Single page hello world."," One page, one line of text, one font. Sets the per-document overhead floor.",[36,7632,7633,7636],{},[39,7634,7635],{},"4×10 invoice table."," A header row, ten body rows, column alignments, thin borders. The \"generate my invoice\" shape.",[36,7638,7639,7642],{},[39,7640,7641],{},"100-page paginated report."," Repeating header, footer, page numbers, body text on each page. Measures pagination cost.",[36,7644,7645,7648],{},[39,7646,7647],{},"Complex CJK invoice."," Mixed Japanese (Hiragana, Katakana, Kanji), a 4×15 line-items table, header, footer with page numbers, embedded NotoSansJP TrueType subset.",[19,7650,7651,7652,7654],{},"Not included: ",[44,7653,4193],{},". Its binary is license-gated, and reproducing its published benchmark methodology inside a public benchmark repo would be misleading without the same license terms. If you're evaluating unidoc, run its own benchmarks — they publish them.",[7656,7657,7659],"h3",{"id":7658},"results","Results",[1896,7661,7662,7679],{},[1899,7663,7664],{},[1902,7665,7666,7669,7671,7673,7675,7677],{},[1905,7667,7668],{},"Workload",[1905,7670,339],{},[1905,7672,4149],{},[1905,7674,5336],{},[1905,7676,5330],{},[1905,7678,3925],{},[1912,7680,7681,7699,7718,7738],{},[1902,7682,7683,7686,7690,7692,7694,7696],{},[1917,7684,7685],{},"Single page hello world",[1917,7687,7688],{},[39,7689,5348],{},[1917,7691,5354],{},[1917,7693,5357],{},[1917,7695,5351],{},[1917,7697,7698],{},"135 µs",[1902,7700,7701,7704,7708,7710,7713,7715],{},[1917,7702,7703],{},"4×10 invoice table",[1917,7705,7706],{},[39,7707,5367],{},[1917,7709,5373],{},[1917,7711,7712],{},"8,600 µs",[1917,7714,5370],{},[1917,7716,7717],{},"243 µs",[1902,7719,7720,7723,7727,7729,7732,7735],{},[1917,7721,7722],{},"100-page paginated report",[1917,7724,7725],{},[39,7726,5386],{},[1917,7728,7712],{},[1917,7730,7731],{},"19,800 µs",[1917,7733,7734],{},"11,700 µs",[1917,7736,7737],{},"11,900 µs",[1902,7739,7740,7742,7746,7748,7751,7753],{},[1917,7741,5399],{},[1917,7743,7744],{},[39,7745,5404],{},[1917,7747,5410],{},[1917,7749,7750],{},"10,400 µs",[1917,7752,5407],{},[1917,7754,7755],{},"n/a",[19,7757,7758,7760,7761,7763],{},[44,7759,7755],{}," for go-pdf/fpdf on the CJK case: the default ",[44,7762,707],{}," path panics on NotoSansJP's cmap format-12 table in the version we tested. Fixable with a patch, but the fork is archived — nobody's shipping the fix.",[7656,7765,7767],{"id":7766},"reading-the-numbers","Reading the numbers",[19,7769,7770,7771,7774],{},"The order is stable across workloads. gpdf is ",[39,7772,7773],{},"10–30× faster"," than the second-fastest library on every case we tested, which is neither exotic nor accidental. Three design choices compound:",[19,7776,7777,7780],{},[39,7778,7779],{},"Single-pass layout."," gpdf doesn't build an intermediate AST and then serialize it. Builders write to the PDF content stream directly as they resolve, which eliminates roughly half the allocations the other libraries do. This is what moves the needle on the 100-page benchmark, where allocation pressure hits the GC hardest — 683 µs versus 19,800 µs isn't a tuning difference, it's a different architecture.",[19,7782,7783,7786],{},[39,7784,7785],{},"No reflection on the hot path."," Every type the layout engine touches is concrete. This sounds like micro-optimization — and individually, it is — but compounded over a 100-page report, interface dispatch starts to show up in profiles. We kept it out.",[19,7788,7789,7792],{},[39,7790,7791],{},"TrueType subsetter that doesn't re-walk on every operation."," gofpdf's font machinery re-reads the cmap table on every glyph lookup; gpdf resolves once and caches. For Latin-only content this barely matters. For CJK, where a single paragraph might touch 150 unique glyphs across Kanji, Hiragana, Katakana, and punctuation, it's the gap between \"fast enough for synchronous generation\" and \"push to a queue.\"",[19,7794,7795,7796,7799],{},"A caveat we'll volunteer that the benchmark table won't: ",[39,7797,7798],{},"absolute speed matters less than people think for most PDF workloads",". The interesting threshold is \"fast enough to generate on the request path.\" Every library in this comparison clears that threshold for a single hello-world page. Only gpdf clears it for a 100-page report with repeating chrome. If your biggest document is a one-page receipt, all four maintained libraries are fine; pick by API ergonomics and license instead.",[14,7801,7803],{"id":7802},"dependencies","Dependencies",[19,7805,7806,7807,7809,7810,7813],{},"What ",[44,7808,4330],{}," prints after a fresh ",[44,7811,7812],{},"go get"," of each:",[1896,7815,7816,7828],{},[1899,7817,7818],{},[1902,7819,7820,7822,7825],{},[1905,7821,4084],{},[1905,7823,7824],{},"External modules",[1905,7826,7827],{},"Transitive archived deps",[1912,7829,7830,7843,7851,7861,7869,7881],{},[1902,7831,7832,7837,7841],{},[1917,7833,7834,7836],{},[39,7835,339],{}," (core)",[1917,7838,7839],{},[39,7840,7467],{},[1917,7842,7460],{},[1902,7844,7845,7847,7849],{},[1917,7846,4149],{},[1917,7848,7467],{},[1917,7850,7460],{},[1902,7852,7853,7855,7858],{},[1917,7854,5330],{},[1917,7856,7857],{},"0 (but itself is archived)",[1917,7859,7860],{},"itself",[1902,7862,7863,7865,7867],{},[1917,7864,3925],{},[1917,7866,7857],{},[1917,7868,7860],{},[1902,7870,7871,7873,7878],{},[1917,7872,7397],{},[1917,7874,7875],{},[39,7876,7877],{},"gofpdf (archived 2021)",[1917,7879,7880],{},"yes — gofpdf",[1902,7882,7883,7885,7888],{},[1917,7884,4193],{},[1917,7886,7887],{},"many (imaging, crypto, compression)",[1917,7889,7890],{},"none archived",[19,7892,7893],{},"For teams with a \"no archived deps in production\" lint rule, Maroto v2 today fails it via its gofpdf transitive. The Maroto maintainers have been rewriting the backend off gofpdf for over a year; when that ships, this row changes. Worth checking the Maroto repo before making a decision on this basis — the state may have moved between when we wrote this and when you're reading it.",[14,7895,7897],{"id":7896},"licensing","Licensing",[1896,7899,7900,7908],{},[1899,7901,7902],{},[1902,7903,7904,7906],{},[1905,7905,4084],{},[1905,7907,2394],{},[1912,7909,7910,7917,7923,7929,7935,7941],{},[1902,7911,7912,7915],{},[1917,7913,7914],{},"gpdf (core)",[1917,7916,4113],{},[1902,7918,7919,7921],{},[1917,7920,4149],{},[1917,7922,4113],{},[1902,7924,7925,7927],{},[1917,7926,7397],{},[1917,7928,4113],{},[1902,7930,7931,7933],{},[1917,7932,5330],{},[1917,7934,4113],{},[1902,7936,7937,7939],{},[1917,7938,3925],{},[1917,7940,4113],{},[1902,7942,7943,7947],{},[1917,7944,7945],{},[39,7946,4193],{},[1917,7948,7949],{},[39,7950,7951],{},"AGPL-3.0 or commercial license",[19,7953,7954],{},"Unidoc's AGPL terms are strict. If you use it in a server that users interact with over a network, your server code has to be released under AGPL too — a non-starter for most closed-source SaaS. That usually leaves the commercial license as the only real option, and their pricing isn't published publicly: plan on a conversation with sales.",[19,7956,7957,7958,60],{},"This is the single biggest thing people miss in GitHub-star-count comparisons. Unidoc has the most features and the most stars. It also has a license that closes the door on most commercial use cases without a purchase. We don't say this as a dig at unidoc — their model is a legitimate one and the product is excellent — but you should know before ",[44,7959,7812],{},[14,7961,7963],{"id":7962},"maintenance-status-plain-english-edition","Maintenance status, plain-English edition",[985,7965,7966,7971,7976,7981,7989,7996],{},[36,7967,7968,7970],{},[39,7969,339],{}," — primary maintainer is this team (gpdf-dev). Active releases every 2–4 weeks; roadmap in the repo; CI runs on Go 1.22 through 1.26; issue response within a few business days on the main repo. We have skin in the game.",[36,7972,7973,7975],{},[39,7974,4149],{}," — active with a smaller commit cadence. Issues get attention; PRs tend to merge within a few weeks. Primary use case remains low-level generation.",[36,7977,7978,7980],{},[39,7979,5336],{}," — active. The v2 rewrite landed in 2023 and is stable. The gofpdf dependency is known and the team is working on replacing it; check the repo for current state before committing.",[36,7982,7983,7985,7986,7988],{},[39,7984,5330],{}," — archived 2021-09-08. Banner on the repo: ",[746,7987,3947],{}," No security patches, no bug fixes.",[36,7990,7991,7993,7994,60],{},[39,7992,3925],{}," — archived in 2025. The README now recommends using a different library. We wrote a dedicated migration guide: ",[723,7995,5615],{"href":3977},[36,7997,7998,8000],{},[39,7999,4193],{}," — active, commercial team, well-resourced. Enterprise support is available.",[14,8002,8004],{"id":8003},"how-to-pick","How to pick",[19,8006,8007],{},"We're going to try an actual decision tree rather than a feature matrix, because \"most features\" isn't usually the right question:",[985,8009,8010,8016,8022,8028,8034,8043],{},[36,8011,8012,8015],{},[39,8013,8014],{},"\"I have a Go codebase that generates invoices, reports, or documents, I want MIT, zero deps, and my documents sometimes contain CJK.\""," → gpdf.",[36,8017,8018,8021],{},[39,8019,8020],{},"\"I'm doing low-level PDF generation with custom geometry and I want a small, stable, hand-on-the-wheel library.\""," → signintech/gopdf.",[36,8023,8024,8027],{},[39,8025,8026],{},"\"I already have Maroto-flavored layout code that works today.\""," → stay on Maroto v2 until the gofpdf-removal lands, then re-evaluate. The API isn't the problem.",[36,8029,8030,8033],{},[39,8031,8032],{},"\"I need PDF/A, OCR, redaction, digital signatures, and my employer will pay for a commercial license.\""," → unidoc/unipdf, with the license conversation up front.",[36,8035,8036,8039,8040],{},[39,8037,8038],{},"\"I'm still on gofpdf and it works.\""," → fine today. Plan the migration before the next CVE lands in an unrelated dependency and you're stuck on unmaintained code. ",[723,8041,8042],{"href":3977},"Migration guide here.",[36,8044,8045,8048],{},[39,8046,8047],{},"\"Pixel-perfect HTML/CSS to PDF rendering.\""," → none of the above. Use go-rod or chromedp with a headless Chromium, and plan for the cold-start cost.",[19,8050,8051],{},"We ship gpdf, so of course we think gpdf is the right default for the first bucket and most of the fifth. Read the benchmark code, run it locally, don't take the table at face value.",[14,8053,8055],{"id":8054},"a-30-line-gpdf-example","A 30-line gpdf example",[19,8057,8058],{},"Because \"fastest\" and \"smallest dep graph\" only matter if the code is bearable to read. This is a complete, runnable invoice page — no pseudo-code, no missing imports:",[101,8060,8062],{"className":103,"code":8061,"language":105,"meta":106,"style":106},"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/pdf\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"INVOICE #2026-0042\", template.Bold(), template.FontSize(20))\n            c.Spacer(document.Mm(6))\n            c.Table(\n                []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"},\n                [][]string{\n                    {\"Frontend dev\", \"40 hrs\", \"$150.00\", \"$6,000.00\"},\n                    {\"Backend dev\",  \"60 hrs\", \"$150.00\", \"$9,000.00\"},\n                    {\"UI design\",    \"20 hrs\", \"$120.00\", \"$2,400.00\"},\n                },\n                template.ColumnWidths(50, 15, 15, 20),\n                template.TableHeaderStyle(\n                    template.Bold(),\n                    template.TextColor(pdf.White),\n                    template.BgColor(pdf.RGBHex(0x1A237E)),\n                ),\n            )\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",[44,8063,8064,8070,8074,8080,8088,8096,8100,8108,8116,8125,8133,8137,8141,8151,8165,8183,8213,8217,8221,8235,8259,8289,8328,8351,8362,8410,8420,8461,8501,8541,8546,8577,8588,8600,8621,8647,8652,8657,8661,8665,8669,8687,8699,8713,8717,8757,8771,8775],{"__ignoreMap":106},[110,8065,8066,8068],{"class":112,"line":113},[110,8067,117],{"class":116},[110,8069,121],{"class":120},[110,8071,8072],{"class":112,"line":124},[110,8073,128],{"emptyLinePlaceholder":127},[110,8075,8076,8078],{"class":112,"line":131},[110,8077,135],{"class":134},[110,8079,138],{"class":116},[110,8081,8082,8084,8086],{"class":112,"line":141},[110,8083,144],{"class":116},[110,8085,147],{"class":120},[110,8087,150],{"class":116},[110,8089,8090,8092,8094],{"class":112,"line":153},[110,8091,144],{"class":116},[110,8093,158],{"class":120},[110,8095,150],{"class":116},[110,8097,8098],{"class":112,"line":163},[110,8099,128],{"emptyLinePlaceholder":127},[110,8101,8102,8104,8106],{"class":112,"line":168},[110,8103,144],{"class":116},[110,8105,173],{"class":120},[110,8107,150],{"class":116},[110,8109,8110,8112,8114],{"class":112,"line":178},[110,8111,144],{"class":116},[110,8113,183],{"class":120},[110,8115,150],{"class":116},[110,8117,8118,8120,8123],{"class":112,"line":188},[110,8119,144],{"class":116},[110,8121,8122],{"class":120},"github.com/gpdf-dev/gpdf/pdf",[110,8124,150],{"class":116},[110,8126,8127,8129,8131],{"class":112,"line":198},[110,8128,144],{"class":116},[110,8130,193],{"class":120},[110,8132,150],{"class":116},[110,8134,8135],{"class":112,"line":204},[110,8136,201],{"class":116},[110,8138,8139],{"class":112,"line":209},[110,8140,128],{"emptyLinePlaceholder":127},[110,8142,8143,8145,8147,8149],{"class":112,"line":225},[110,8144,212],{"class":116},[110,8146,216],{"class":215},[110,8148,219],{"class":116},[110,8150,222],{"class":116},[110,8152,8153,8155,8157,8159,8161,8163],{"class":112,"line":262},[110,8154,310],{"class":228},[110,8156,238],{"class":116},[110,8158,315],{"class":228},[110,8160,60],{"class":116},[110,8162,320],{"class":215},[110,8164,323],{"class":116},[110,8166,8167,8169,8171,8173,8175,8177,8179,8181],{"class":112,"line":278},[110,8168,329],{"class":228},[110,8170,60],{"class":116},[110,8172,334],{"class":215},[110,8174,249],{"class":116},[110,8176,362],{"class":228},[110,8178,60],{"class":116},[110,8180,344],{"class":228},[110,8182,347],{"class":116},[110,8184,8185,8187,8189,8191,8193,8195,8197,8199,8201,8203,8205,8207,8209,8211],{"class":112,"line":296},[110,8186,329],{"class":228},[110,8188,60],{"class":116},[110,8190,357],{"class":215},[110,8192,249],{"class":116},[110,8194,362],{"class":228},[110,8196,60],{"class":116},[110,8198,367],{"class":215},[110,8200,249],{"class":116},[110,8202,362],{"class":228},[110,8204,60],{"class":116},[110,8206,376],{"class":215},[110,8208,249],{"class":116},[110,8210,382],{"class":381},[110,8212,385],{"class":116},[110,8214,8215],{"class":112,"line":302},[110,8216,441],{"class":116},[110,8218,8219],{"class":112,"line":307},[110,8220,128],{"emptyLinePlaceholder":127},[110,8222,8223,8225,8227,8229,8231,8233],{"class":112,"line":326},[110,8224,452],{"class":228},[110,8226,238],{"class":116},[110,8228,457],{"class":228},[110,8230,60],{"class":116},[110,8232,462],{"class":215},[110,8234,465],{"class":116},[110,8236,8237,8239,8241,8243,8245,8247,8249,8251,8253,8255,8257],{"class":112,"line":350},[110,8238,471],{"class":228},[110,8240,60],{"class":116},[110,8242,476],{"class":215},[110,8244,479],{"class":116},[110,8246,483],{"class":482},[110,8248,486],{"class":116},[110,8250,489],{"class":120},[110,8252,60],{"class":116},[110,8254,494],{"class":120},[110,8256,497],{"class":116},[110,8258,222],{"class":116},[110,8260,8261,8263,8265,8267,8269,8271,8273,8275,8277,8279,8281,8283,8285,8287],{"class":112,"line":388},[110,8262,505],{"class":228},[110,8264,60],{"class":116},[110,8266,510],{"class":215},[110,8268,249],{"class":116},[110,8270,515],{"class":381},[110,8272,232],{"class":116},[110,8274,520],{"class":116},[110,8276,523],{"class":482},[110,8278,486],{"class":116},[110,8280,489],{"class":120},[110,8282,60],{"class":116},[110,8284,532],{"class":120},[110,8286,497],{"class":116},[110,8288,222],{"class":116},[110,8290,8291,8293,8295,8297,8299,8301,8304,8306,8308,8310,8312,8314,8316,8318,8320,8322,8324,8326],{"class":112,"line":413},[110,8292,542],{"class":228},[110,8294,60],{"class":116},[110,8296,547],{"class":215},[110,8298,249],{"class":116},[110,8300,252],{"class":116},[110,8302,8303],{"class":255},"INVOICE #2026-0042",[110,8305,252],{"class":116},[110,8307,232],{"class":116},[110,8309,1680],{"class":228},[110,8311,60],{"class":116},[110,8313,1700],{"class":215},[110,8315,4882],{"class":116},[110,8317,1680],{"class":228},[110,8319,60],{"class":116},[110,8321,1685],{"class":215},[110,8323,249],{"class":116},[110,8325,382],{"class":381},[110,8327,2279],{"class":116},[110,8329,8330,8332,8334,8337,8339,8341,8343,8345,8347,8349],{"class":112,"line":438},[110,8331,542],{"class":228},[110,8333,60],{"class":116},[110,8335,8336],{"class":215},"Spacer",[110,8338,249],{"class":116},[110,8340,362],{"class":228},[110,8342,60],{"class":116},[110,8344,376],{"class":215},[110,8346,249],{"class":116},[110,8348,2913],{"class":381},[110,8350,2279],{"class":116},[110,8352,8353,8355,8357,8360],{"class":112,"line":444},[110,8354,542],{"class":228},[110,8356,60],{"class":116},[110,8358,8359],{"class":215},"Table",[110,8361,323],{"class":116},[110,8363,8364,8367,8370,8373,8375,8378,8380,8382,8384,8387,8389,8391,8393,8396,8398,8400,8402,8405,8407],{"class":112,"line":449},[110,8365,8366],{"class":116},"                []",[110,8368,1120],{"class":8369},"spNyl",[110,8371,8372],{"class":116},"{",[110,8374,252],{"class":116},[110,8376,8377],{"class":255},"Description",[110,8379,252],{"class":116},[110,8381,232],{"class":116},[110,8383,4691],{"class":116},[110,8385,8386],{"class":255},"Qty",[110,8388,252],{"class":116},[110,8390,232],{"class":116},[110,8392,4691],{"class":116},[110,8394,8395],{"class":255},"Unit",[110,8397,252],{"class":116},[110,8399,232],{"class":116},[110,8401,4691],{"class":116},[110,8403,8404],{"class":255},"Amount",[110,8406,252],{"class":116},[110,8408,8409],{"class":116},"},\n",[110,8411,8412,8415,8417],{"class":112,"line":468},[110,8413,8414],{"class":116},"                [][]",[110,8416,1120],{"class":8369},[110,8418,8419],{"class":116},"{\n",[110,8421,8422,8425,8427,8430,8432,8434,8436,8439,8441,8443,8445,8448,8450,8452,8454,8457,8459],{"class":112,"line":502},[110,8423,8424],{"class":116},"                    {",[110,8426,252],{"class":116},[110,8428,8429],{"class":255},"Frontend dev",[110,8431,252],{"class":116},[110,8433,232],{"class":116},[110,8435,4691],{"class":116},[110,8437,8438],{"class":255},"40 hrs",[110,8440,252],{"class":116},[110,8442,232],{"class":116},[110,8444,4691],{"class":116},[110,8446,8447],{"class":255},"$150.00",[110,8449,252],{"class":116},[110,8451,232],{"class":116},[110,8453,4691],{"class":116},[110,8455,8456],{"class":255},"$6,000.00",[110,8458,252],{"class":116},[110,8460,8409],{"class":116},[110,8462,8463,8465,8467,8470,8472,8474,8477,8480,8482,8484,8486,8488,8490,8492,8494,8497,8499],{"class":112,"line":539},[110,8464,8424],{"class":116},[110,8466,252],{"class":116},[110,8468,8469],{"class":255},"Backend dev",[110,8471,252],{"class":116},[110,8473,232],{"class":116},[110,8475,8476],{"class":116},"  \"",[110,8478,8479],{"class":255},"60 hrs",[110,8481,252],{"class":116},[110,8483,232],{"class":116},[110,8485,4691],{"class":116},[110,8487,8447],{"class":255},[110,8489,252],{"class":116},[110,8491,232],{"class":116},[110,8493,4691],{"class":116},[110,8495,8496],{"class":255},"$9,000.00",[110,8498,252],{"class":116},[110,8500,8409],{"class":116},[110,8502,8503,8505,8507,8510,8512,8514,8516,8519,8521,8523,8525,8528,8530,8532,8534,8537,8539],{"class":112,"line":561},[110,8504,8424],{"class":116},[110,8506,252],{"class":116},[110,8508,8509],{"class":255},"UI design",[110,8511,252],{"class":116},[110,8513,232],{"class":116},[110,8515,144],{"class":116},[110,8517,8518],{"class":255},"20 hrs",[110,8520,252],{"class":116},[110,8522,232],{"class":116},[110,8524,4691],{"class":116},[110,8526,8527],{"class":255},"$120.00",[110,8529,252],{"class":116},[110,8531,232],{"class":116},[110,8533,4691],{"class":116},[110,8535,8536],{"class":255},"$2,400.00",[110,8538,252],{"class":116},[110,8540,8409],{"class":116},[110,8542,8543],{"class":112,"line":567},[110,8544,8545],{"class":116},"                },\n",[110,8547,8548,8551,8553,8556,8558,8561,8563,8566,8568,8570,8572,8575],{"class":112,"line":573},[110,8549,8550],{"class":228},"                template",[110,8552,60],{"class":116},[110,8554,8555],{"class":215},"ColumnWidths",[110,8557,249],{"class":116},[110,8559,8560],{"class":381},"50",[110,8562,232],{"class":116},[110,8564,8565],{"class":381}," 15",[110,8567,232],{"class":116},[110,8569,8565],{"class":381},[110,8571,232],{"class":116},[110,8573,8574],{"class":381}," 20",[110,8576,347],{"class":116},[110,8578,8579,8581,8583,8586],{"class":112,"line":578},[110,8580,8550],{"class":228},[110,8582,60],{"class":116},[110,8584,8585],{"class":215},"TableHeaderStyle",[110,8587,323],{"class":116},[110,8589,8590,8593,8595,8597],{"class":112,"line":599},[110,8591,8592],{"class":228},"                    template",[110,8594,60],{"class":116},[110,8596,1700],{"class":215},[110,8598,8599],{"class":116},"(),\n",[110,8601,8602,8604,8606,8609,8611,8614,8616,8619],{"class":112,"line":612},[110,8603,8592],{"class":228},[110,8605,60],{"class":116},[110,8607,8608],{"class":215},"TextColor",[110,8610,249],{"class":116},[110,8612,8613],{"class":228},"pdf",[110,8615,60],{"class":116},[110,8617,8618],{"class":228},"White",[110,8620,347],{"class":116},[110,8622,8623,8625,8627,8630,8632,8634,8636,8639,8641,8644],{"class":112,"line":627},[110,8624,8592],{"class":228},[110,8626,60],{"class":116},[110,8628,8629],{"class":215},"BgColor",[110,8631,249],{"class":116},[110,8633,8613],{"class":228},[110,8635,60],{"class":116},[110,8637,8638],{"class":215},"RGBHex",[110,8640,249],{"class":116},[110,8642,8643],{"class":381},"0x1A237E",[110,8645,8646],{"class":116},")),\n",[110,8648,8649],{"class":112,"line":632},[110,8650,8651],{"class":116},"                ),\n",[110,8653,8654],{"class":112,"line":678},[110,8655,8656],{"class":116},"            )\n",[110,8658,8659],{"class":112,"line":693},[110,8660,564],{"class":116},[110,8662,8663],{"class":112,"line":698},[110,8664,570],{"class":116},[110,8666,8667],{"class":112,"line":1859},[110,8668,128],{"emptyLinePlaceholder":127},[110,8670,8671,8673,8675,8677,8679,8681,8683,8685],{"class":112,"line":1864},[110,8672,581],{"class":228},[110,8674,232],{"class":116},[110,8676,235],{"class":228},[110,8678,238],{"class":116},[110,8680,457],{"class":228},[110,8682,60],{"class":116},[110,8684,594],{"class":215},[110,8686,465],{"class":116},[110,8688,8689,8691,8693,8695,8697],{"class":112,"line":3116},[110,8690,265],{"class":134},[110,8692,235],{"class":228},[110,8694,270],{"class":116},[110,8696,273],{"class":116},[110,8698,222],{"class":116},[110,8700,8701,8703,8705,8707,8709,8711],{"class":112,"line":3136},[110,8702,281],{"class":228},[110,8704,60],{"class":116},[110,8706,286],{"class":215},[110,8708,249],{"class":116},[110,8710,291],{"class":228},[110,8712,201],{"class":116},[110,8714,8715],{"class":112,"line":3141},[110,8716,299],{"class":116},[110,8718,8719,8721,8723,8725,8727,8729,8731,8733,8735,8737,8739,8741,8743,8745,8747,8749,8751,8753,8755],{"class":112,"line":3172},[110,8720,265],{"class":134},[110,8722,235],{"class":228},[110,8724,238],{"class":116},[110,8726,241],{"class":228},[110,8728,60],{"class":116},[110,8730,645],{"class":215},[110,8732,249],{"class":116},[110,8734,252],{"class":116},[110,8736,1822],{"class":255},[110,8738,252],{"class":116},[110,8740,232],{"class":116},[110,8742,659],{"class":228},[110,8744,232],{"class":116},[110,8746,664],{"class":381},[110,8748,667],{"class":116},[110,8750,235],{"class":228},[110,8752,270],{"class":116},[110,8754,273],{"class":116},[110,8756,222],{"class":116},[110,8758,8759,8761,8763,8765,8767,8769],{"class":112,"line":3192},[110,8760,281],{"class":228},[110,8762,60],{"class":116},[110,8764,286],{"class":215},[110,8766,249],{"class":116},[110,8768,291],{"class":228},[110,8770,201],{"class":116},[110,8772,8773],{"class":112,"line":3197},[110,8774,299],{"class":116},[110,8776,8777],{"class":112,"line":3228},[110,8778,701],{"class":116},[19,8780,8781,8782,8784,8785,8788,8789,8792,8793,8795],{},"Zero ",[44,8783,4266],{},". Zero manual column-width math. Swap ",[44,8786,8787],{},"\"Description\""," for ",[44,8790,8791],{},"\"品目\""," and add ",[44,8794,5288],{}," to the document options — it renders Japanese without any other change.",[14,8797,8799],{"id":8798},"what-we-chose-to-leave-out","What we chose to leave out",[19,8801,8802],{},"Every comparison post has a section for \"omitted because of X.\" Ours:",[985,8804,8805,8811,8823,8833],{},[36,8806,8807,8810],{},[39,8808,8809],{},"Private gofpdf forks."," There are production forks inside companies. We couldn't benchmark code we can't see.",[36,8812,8813,8818,8819,8822],{},[39,8814,8815,60],{},[44,8816,8817],{},"pdfcpu"," It's in every list of \"Go PDF libraries,\" but it's primarily a PDF ",[39,8820,8821],{},"processor"," (merge, split, encrypt, stamp), not a generator. Out of scope for this post; a processing-oriented article is planned separately.",[36,8824,8825,8832],{},[39,8826,8827,8828,8831],{},"Anything wrapping ",[44,8829,8830],{},"gotenberg"," or a headless-browser service."," Not a library. Not a fair comparison.",[36,8834,8835,8841],{},[39,8836,8837,8838,8840],{},"Our own ",[44,8839,339],{}," benchmarks."," The core numbers are what matters for the comparison.",[14,8843,5523],{"id":5522},[19,8845,8846,8849],{},[39,8847,8848],{},"Why is gpdf 10× faster than gofpdf? What's the trick?","\nNo single trick. Three designs compound: single-pass layout (no AST between builder and writer), concrete types on the hot path, and a TrueType subsetter that caches the cmap. Any one of these in isolation gives a 2× gain. Stacked, it's an order of magnitude.",[19,8851,8852,8855,8856,8859],{},[39,8853,8854],{},"Can I actually re-run this benchmark?","\nYes. ",[44,8857,8858],{},"git clone https://github.com/gpdf-dev/gpdf && cd gpdf/_benchmark && go test -bench=. -benchmem",". If the numbers don't match what's in this post — same machine architecture, same Go version — open an issue. Benchmark drift is real; we'd rather know.",[19,8861,8862,8865],{},[39,8863,8864],{},"Is gofpdf coming back?","\nRealistically, no. The last commit is from 2021. The issue tracker is closed. Even if someone re-opened it, the architecture (single cursor, single-byte fonts, no grid) is the wrong starting point for 2026. Better to treat it as a historical artifact and migrate.",[19,8867,8868,8871],{},[39,8869,8870],{},"What about Java iText / Python ReportLab / Node pdfkit?","\nCross-language benchmarks are a different post. Short version: Go generally wins on throughput and cold-start, loses on feature breadth (especially on HTML-to-PDF fidelity). For teams already on Go, gpdf is faster and smaller than any of those cross-language options; for teams on Python or Node, the migration cost usually only pays back at high-volume scale.",[19,8873,8874,8877,8878,8880],{},[39,8875,8876],{},"Is this comparison going to stay fair if gpdf's competitors improve?","\nYes. We run this every year. If ",[44,8879,4149],{}," ships a table API that halves its time, that's in the 2027 post. If Maroto v2 finishes removing gofpdf, that row changes. The benchmark code is public specifically so nobody has to take our word for it.",[14,8882,1201],{"id":1200},[19,8884,8885],{},"gpdf is a Go library for generating PDFs. MIT, zero dependencies, native CJK.",[101,8887,8888],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,8889,8890],{"__ignoreMap":106},[110,8891,8892,8894,8896],{"class":112,"line":113},[110,8893,105],{"class":120},[110,8895,1218],{"class":255},[110,8897,1221],{"class":255},[19,8899,8900,1229,8903],{},[723,8901,1228],{"href":1226,"rel":8902},[727],[723,8904,1234],{"href":1232,"rel":8905},[727],[14,8907,5608],{"id":5607},[985,8909,8910,8915,8924],{},[36,8911,8912,8914],{},[723,8913,5615],{"href":3977}," — the full API mapping with five before/after code pairs.",[36,8916,8917,8921,8922,60],{},[723,8918,8920],{"href":1232,"rel":8919},[727],"Quickstart"," — five-minute setup, including ",[44,8923,3964],{},[36,8925,8926,8927,60],{},"The benchmark code itself: ",[723,8928,8930],{"href":7611,"rel":8929},[727],[44,8931,7368],{},[1236,8933,8934],{},"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 .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 pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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":106,"searchDepth":124,"depth":124,"links":8936},[8937,8938,8939,8940,8944,8945,8946,8947,8948,8949,8950,8951,8952],{"id":1310,"depth":124,"text":1311},{"id":7372,"depth":124,"text":7373},{"id":7416,"depth":124,"text":7417},{"id":7604,"depth":124,"text":7605,"children":8941},[8942,8943],{"id":7658,"depth":131,"text":7659},{"id":7766,"depth":131,"text":7767},{"id":7802,"depth":124,"text":7803},{"id":7896,"depth":124,"text":7897},{"id":7962,"depth":124,"text":7963},{"id":8003,"depth":124,"text":8004},{"id":8054,"depth":124,"text":8055},{"id":8798,"depth":124,"text":8799},{"id":5522,"depth":124,"text":5523},{"id":1200,"depth":124,"text":1201},{"id":5607,"depth":124,"text":5608},"2026-04-15","The Go PDF library landscape in 2026: every active and archived library, benchmarked on 4 workloads, with license and dependency details.",{},{"title":3824,"description":8954},"blog/002.go-pdf-library-showdown-2026",[5661,5662],"PpXTt_zWOo7PL0Mmmi5ks1Mn8HoS4ZL3GX4GHlmzHG0",{"id":8961,"title":1175,"author":8962,"body":8963,"date":8953,"description":10262,"draft":1253,"extension":1254,"howTo":10263,"image":1277,"meta":10278,"navigation":127,"path":1174,"seo":10279,"stem":10280,"tags":10281,"updated":1277,"__hash__":10282},"blog/blog/003.embed-japanese-font.md",{"name":8,"url":9},{"type":11,"value":8964,"toc":10251},[8965,8967,8977,8979,8992,8994,9498,9513,9517,9520,9526,9535,9544,9548,9559,9706,9722,9726,9733,10159,10166,10170,10176,10182,10198,10200,10225,10227,10229,10241,10249],[14,8966,17],{"id":16},[19,8968,8969,8970,8973,8974,8976],{},"How do I render Japanese (or any CJK) text in a PDF generated with ",[723,8971,339],{"href":1226,"rel":8972},[727]," — without the ",[44,8975,707],{}," dance, without CGO, without a five-megabyte font embedded on every document?",[14,8978,1311],{"id":1310},[19,8980,8981,8982,943,8985,8987,8988,8991],{},"Load the TTF bytes. Pass ",[44,8983,8984],{},"gpdf.WithFont(\"NotoSansJP\", fontBytes)",[44,8986,320],{},". Optionally set it as the default. Write Japanese. ",[39,8989,8990],{},"Three lines of setup, and gpdf subsets the glyphs automatically"," so the final PDF carries only the characters you actually used.",[14,8993,1330],{"id":1329},[101,8995,8997],{"className":103,"code":8996,"language":105,"meta":106,"style":106},"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",[44,8998,8999,9005,9009,9015,9023,9031,9035,9043,9051,9059,9063,9067,9077,9103,9115,9129,9133,9137,9151,9169,9199,9221,9243,9247,9251,9265,9289,9319,9357,9376,9380,9384,9388,9406,9418,9432,9436,9476,9490,9494],{"__ignoreMap":106},[110,9000,9001,9003],{"class":112,"line":113},[110,9002,117],{"class":116},[110,9004,121],{"class":120},[110,9006,9007],{"class":112,"line":124},[110,9008,128],{"emptyLinePlaceholder":127},[110,9010,9011,9013],{"class":112,"line":131},[110,9012,135],{"class":134},[110,9014,138],{"class":116},[110,9016,9017,9019,9021],{"class":112,"line":141},[110,9018,144],{"class":116},[110,9020,147],{"class":120},[110,9022,150],{"class":116},[110,9024,9025,9027,9029],{"class":112,"line":153},[110,9026,144],{"class":116},[110,9028,158],{"class":120},[110,9030,150],{"class":116},[110,9032,9033],{"class":112,"line":163},[110,9034,128],{"emptyLinePlaceholder":127},[110,9036,9037,9039,9041],{"class":112,"line":168},[110,9038,144],{"class":116},[110,9040,173],{"class":120},[110,9042,150],{"class":116},[110,9044,9045,9047,9049],{"class":112,"line":178},[110,9046,144],{"class":116},[110,9048,183],{"class":120},[110,9050,150],{"class":116},[110,9052,9053,9055,9057],{"class":112,"line":188},[110,9054,144],{"class":116},[110,9056,193],{"class":120},[110,9058,150],{"class":116},[110,9060,9061],{"class":112,"line":198},[110,9062,201],{"class":116},[110,9064,9065],{"class":112,"line":204},[110,9066,128],{"emptyLinePlaceholder":127},[110,9068,9069,9071,9073,9075],{"class":112,"line":209},[110,9070,212],{"class":116},[110,9072,216],{"class":215},[110,9074,219],{"class":116},[110,9076,222],{"class":116},[110,9078,9079,9081,9083,9085,9087,9089,9091,9093,9095,9097,9099,9101],{"class":112,"line":225},[110,9080,229],{"class":228},[110,9082,232],{"class":116},[110,9084,235],{"class":228},[110,9086,238],{"class":116},[110,9088,241],{"class":228},[110,9090,60],{"class":116},[110,9092,246],{"class":215},[110,9094,249],{"class":116},[110,9096,252],{"class":116},[110,9098,81],{"class":255},[110,9100,252],{"class":116},[110,9102,201],{"class":116},[110,9104,9105,9107,9109,9111,9113],{"class":112,"line":262},[110,9106,265],{"class":134},[110,9108,235],{"class":228},[110,9110,270],{"class":116},[110,9112,273],{"class":116},[110,9114,222],{"class":116},[110,9116,9117,9119,9121,9123,9125,9127],{"class":112,"line":278},[110,9118,281],{"class":228},[110,9120,60],{"class":116},[110,9122,286],{"class":215},[110,9124,249],{"class":116},[110,9126,291],{"class":228},[110,9128,201],{"class":116},[110,9130,9131],{"class":112,"line":296},[110,9132,299],{"class":116},[110,9134,9135],{"class":112,"line":302},[110,9136,128],{"emptyLinePlaceholder":127},[110,9138,9139,9141,9143,9145,9147,9149],{"class":112,"line":307},[110,9140,310],{"class":228},[110,9142,238],{"class":116},[110,9144,315],{"class":228},[110,9146,60],{"class":116},[110,9148,320],{"class":215},[110,9150,323],{"class":116},[110,9152,9153,9155,9157,9159,9161,9163,9165,9167],{"class":112,"line":326},[110,9154,329],{"class":228},[110,9156,60],{"class":116},[110,9158,334],{"class":215},[110,9160,249],{"class":116},[110,9162,339],{"class":228},[110,9164,60],{"class":116},[110,9166,344],{"class":228},[110,9168,347],{"class":116},[110,9170,9171,9173,9175,9177,9179,9181,9183,9185,9187,9189,9191,9193,9195,9197],{"class":112,"line":350},[110,9172,329],{"class":228},[110,9174,60],{"class":116},[110,9176,357],{"class":215},[110,9178,249],{"class":116},[110,9180,362],{"class":228},[110,9182,60],{"class":116},[110,9184,367],{"class":215},[110,9186,249],{"class":116},[110,9188,362],{"class":228},[110,9190,60],{"class":116},[110,9192,376],{"class":215},[110,9194,249],{"class":116},[110,9196,382],{"class":381},[110,9198,385],{"class":116},[110,9200,9201,9203,9205,9207,9209,9211,9213,9215,9217,9219],{"class":112,"line":388},[110,9202,329],{"class":228},[110,9204,60],{"class":116},[110,9206,50],{"class":215},[110,9208,249],{"class":116},[110,9210,252],{"class":116},[110,9212,401],{"class":255},[110,9214,252],{"class":116},[110,9216,232],{"class":116},[110,9218,408],{"class":228},[110,9220,347],{"class":116},[110,9222,9223,9225,9227,9229,9231,9233,9235,9237,9239,9241],{"class":112,"line":413},[110,9224,329],{"class":228},[110,9226,60],{"class":116},[110,9228,420],{"class":215},[110,9230,249],{"class":116},[110,9232,252],{"class":116},[110,9234,401],{"class":255},[110,9236,252],{"class":116},[110,9238,232],{"class":116},[110,9240,433],{"class":381},[110,9242,347],{"class":116},[110,9244,9245],{"class":112,"line":438},[110,9246,441],{"class":116},[110,9248,9249],{"class":112,"line":444},[110,9250,128],{"emptyLinePlaceholder":127},[110,9252,9253,9255,9257,9259,9261,9263],{"class":112,"line":449},[110,9254,452],{"class":228},[110,9256,238],{"class":116},[110,9258,457],{"class":228},[110,9260,60],{"class":116},[110,9262,462],{"class":215},[110,9264,465],{"class":116},[110,9266,9267,9269,9271,9273,9275,9277,9279,9281,9283,9285,9287],{"class":112,"line":468},[110,9268,471],{"class":228},[110,9270,60],{"class":116},[110,9272,476],{"class":215},[110,9274,479],{"class":116},[110,9276,483],{"class":482},[110,9278,486],{"class":116},[110,9280,489],{"class":120},[110,9282,60],{"class":116},[110,9284,494],{"class":120},[110,9286,497],{"class":116},[110,9288,222],{"class":116},[110,9290,9291,9293,9295,9297,9299,9301,9303,9305,9307,9309,9311,9313,9315,9317],{"class":112,"line":502},[110,9292,505],{"class":228},[110,9294,60],{"class":116},[110,9296,510],{"class":215},[110,9298,249],{"class":116},[110,9300,515],{"class":381},[110,9302,232],{"class":116},[110,9304,520],{"class":116},[110,9306,523],{"class":482},[110,9308,486],{"class":116},[110,9310,489],{"class":120},[110,9312,60],{"class":116},[110,9314,532],{"class":120},[110,9316,497],{"class":116},[110,9318,222],{"class":116},[110,9320,9321,9323,9325,9327,9329,9331,9333,9335,9337,9339,9341,9343,9345,9347,9349,9351,9353,9355],{"class":112,"line":539},[110,9322,542],{"class":228},[110,9324,60],{"class":116},[110,9326,547],{"class":215},[110,9328,249],{"class":116},[110,9330,252],{"class":116},[110,9332,554],{"class":255},[110,9334,252],{"class":116},[110,9336,232],{"class":116},[110,9338,1680],{"class":228},[110,9340,60],{"class":116},[110,9342,1685],{"class":215},[110,9344,249],{"class":116},[110,9346,1690],{"class":381},[110,9348,1693],{"class":116},[110,9350,1680],{"class":228},[110,9352,60],{"class":116},[110,9354,1700],{"class":215},[110,9356,1703],{"class":116},[110,9358,9359,9361,9363,9365,9367,9369,9372,9374],{"class":112,"line":561},[110,9360,542],{"class":228},[110,9362,60],{"class":116},[110,9364,547],{"class":215},[110,9366,249],{"class":116},[110,9368,252],{"class":116},[110,9370,9371],{"class":255},"日本語 PDF、これだけ。",[110,9373,252],{"class":116},[110,9375,201],{"class":116},[110,9377,9378],{"class":112,"line":567},[110,9379,564],{"class":116},[110,9381,9382],{"class":112,"line":573},[110,9383,570],{"class":116},[110,9385,9386],{"class":112,"line":578},[110,9387,128],{"emptyLinePlaceholder":127},[110,9389,9390,9392,9394,9396,9398,9400,9402,9404],{"class":112,"line":599},[110,9391,581],{"class":228},[110,9393,232],{"class":116},[110,9395,235],{"class":228},[110,9397,238],{"class":116},[110,9399,457],{"class":228},[110,9401,60],{"class":116},[110,9403,594],{"class":215},[110,9405,465],{"class":116},[110,9407,9408,9410,9412,9414,9416],{"class":112,"line":612},[110,9409,265],{"class":134},[110,9411,235],{"class":228},[110,9413,270],{"class":116},[110,9415,273],{"class":116},[110,9417,222],{"class":116},[110,9419,9420,9422,9424,9426,9428,9430],{"class":112,"line":627},[110,9421,281],{"class":228},[110,9423,60],{"class":116},[110,9425,286],{"class":215},[110,9427,249],{"class":116},[110,9429,291],{"class":228},[110,9431,201],{"class":116},[110,9433,9434],{"class":112,"line":632},[110,9435,299],{"class":116},[110,9437,9438,9440,9442,9444,9446,9448,9450,9452,9454,9456,9458,9460,9462,9464,9466,9468,9470,9472,9474],{"class":112,"line":678},[110,9439,265],{"class":134},[110,9441,235],{"class":228},[110,9443,238],{"class":116},[110,9445,241],{"class":228},[110,9447,60],{"class":116},[110,9449,645],{"class":215},[110,9451,249],{"class":116},[110,9453,252],{"class":116},[110,9455,652],{"class":255},[110,9457,252],{"class":116},[110,9459,232],{"class":116},[110,9461,659],{"class":228},[110,9463,232],{"class":116},[110,9465,664],{"class":381},[110,9467,667],{"class":116},[110,9469,235],{"class":228},[110,9471,270],{"class":116},[110,9473,273],{"class":116},[110,9475,222],{"class":116},[110,9477,9478,9480,9482,9484,9486,9488],{"class":112,"line":693},[110,9479,281],{"class":228},[110,9481,60],{"class":116},[110,9483,286],{"class":215},[110,9485,249],{"class":116},[110,9487,291],{"class":228},[110,9489,201],{"class":116},[110,9491,9492],{"class":112,"line":698},[110,9493,299],{"class":116},[110,9495,9496],{"class":112,"line":1859},[110,9497,701],{"class":116},[19,9499,9500,9501,721,9503,9506,9507,9509,9510,9512],{},"Download ",[44,9502,81],{},[723,9504,728],{"href":725,"rel":9505},[727]," and drop it next to ",[44,9508,1883],{},". Run ",[44,9511,1887],{},". You get a one-page PDF with Japanese text.",[14,9514,9516],{"id":9515},"what-those-three-lines-actually-do","What those three lines actually do",[19,9518,9519],{},"Two things happen under the hood, and neither one needs any help from you.",[19,9521,9522,9525],{},[39,9523,9524],{},"Subset embedding."," Noto Sans JP ships with around 17,000 glyphs — the regular weight is about 5 MB on disk. If every PDF embedded the whole font, a receipt with four lines of Japanese would still cost you five megabytes of font data. gpdf walks the text you rendered, figures out which glyph IDs you used, and writes only that subset into the PDF. A short invoice usually ends up carrying 20–40 KB of font data instead of 5 MB.",[19,9527,9528,9529,9531,9532,9534],{},"gofpdf could do subset embedding too, but it needed ",[44,9530,707],{}," called with a filesystem path and a UTF-8 flag, and font switching mid-document was awkward. gpdf registers the font once at document construction and then every ",[44,9533,59],{}," call just references it by family name. There's no per-call bookkeeping.",[19,9536,9537,9540,9541,9543],{},[39,9538,9539],{},"No CGO."," This matters more than it sounds. A lot of font handling in other ecosystems routes through FreeType or HarfBuzz, which means a C dependency, which means your build caches invalidate differently, your Docker images gain layers, and cross-compilation from macOS to linux/arm64 becomes a thing you have to think about. gpdf parses TrueType tables in pure Go. ",[44,9542,4317],{}," stays static. Ship a distroless container with the Go binary and the TTF file; that's all.",[14,9545,9547],{"id":9546},"bold-and-italic-variants","Bold and italic variants",[19,9549,9550,9551,9554,9555,9558],{},"Japanese Noto ships a separate file per weight. To use ",[39,9552,9553],{},"bold",", register the bold TTF under the ",[44,9556,9557],{},"-Bold"," suffix:",[101,9560,9562],{"className":103,"code":9561,"language":105,"meta":106,"style":106},"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",[44,9563,9564,9591,9617,9621,9635,9658,9680,9702],{"__ignoreMap":106},[110,9565,9566,9569,9571,9573,9575,9577,9579,9581,9583,9585,9587,9589],{"class":112,"line":113},[110,9567,9568],{"class":228},"reg",[110,9570,232],{"class":116},[110,9572,2064],{"class":228},[110,9574,238],{"class":116},[110,9576,241],{"class":228},[110,9578,60],{"class":116},[110,9580,246],{"class":215},[110,9582,249],{"class":116},[110,9584,252],{"class":116},[110,9586,81],{"class":255},[110,9588,252],{"class":116},[110,9590,201],{"class":116},[110,9592,9593,9595,9597,9599,9601,9603,9605,9607,9609,9611,9613,9615],{"class":112,"line":124},[110,9594,9553],{"class":228},[110,9596,232],{"class":116},[110,9598,2064],{"class":228},[110,9600,238],{"class":116},[110,9602,241],{"class":228},[110,9604,60],{"class":116},[110,9606,246],{"class":215},[110,9608,249],{"class":116},[110,9610,252],{"class":116},[110,9612,6091],{"class":255},[110,9614,252],{"class":116},[110,9616,201],{"class":116},[110,9618,9619],{"class":112,"line":131},[110,9620,128],{"emptyLinePlaceholder":127},[110,9622,9623,9625,9627,9629,9631,9633],{"class":112,"line":141},[110,9624,801],{"class":228},[110,9626,238],{"class":116},[110,9628,315],{"class":228},[110,9630,60],{"class":116},[110,9632,320],{"class":215},[110,9634,323],{"class":116},[110,9636,9637,9639,9641,9643,9645,9647,9649,9651,9653,9656],{"class":112,"line":153},[110,9638,816],{"class":228},[110,9640,60],{"class":116},[110,9642,50],{"class":215},[110,9644,249],{"class":116},[110,9646,252],{"class":116},[110,9648,401],{"class":255},[110,9650,252],{"class":116},[110,9652,232],{"class":116},[110,9654,9655],{"class":228}," reg",[110,9657,347],{"class":116},[110,9659,9660,9662,9664,9666,9668,9670,9672,9674,9676,9678],{"class":112,"line":163},[110,9661,816],{"class":228},[110,9663,60],{"class":116},[110,9665,50],{"class":215},[110,9667,249],{"class":116},[110,9669,252],{"class":116},[110,9671,6229],{"class":255},[110,9673,252],{"class":116},[110,9675,232],{"class":116},[110,9677,6236],{"class":228},[110,9679,347],{"class":116},[110,9681,9682,9684,9686,9688,9690,9692,9694,9696,9698,9700],{"class":112,"line":168},[110,9683,816],{"class":228},[110,9685,60],{"class":116},[110,9687,420],{"class":215},[110,9689,249],{"class":116},[110,9691,252],{"class":116},[110,9693,401],{"class":255},[110,9695,252],{"class":116},[110,9697,232],{"class":116},[110,9699,433],{"class":381},[110,9701,347],{"class":116},[110,9703,9704],{"class":112,"line":178},[110,9705,201],{"class":116},[19,9707,9708,9709,9711,9712,9714,9715,962,9718,9721],{},"Now ",[44,9710,1325],{}," picks up the ",[44,9713,9557],{}," variant. Same convention for ",[44,9716,9717],{},"-Italic",[44,9719,9720],{},"-BoldItalic",". If you don't register the variant, bold falls back to a synthesized weight — readable on screen but not typographically honest. For production invoices, register the real weight.",[14,9723,9725],{"id":9724},"multiple-cjk-languages-in-the-same-document","Multiple CJK languages in the same document",[19,9727,9728,9729,9732],{},"Registering more than one family is fine — gpdf tracks them independently. Use ",[44,9730,9731],{},"template.FontFamily(...)"," to switch per text:",[101,9734,9736],{"className":103,"code":9735,"language":105,"meta":106,"style":106},"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",[44,9737,9738,9765,9793,9821,9825,9839,9862,9886,9910,9932,9936,9940,9964,9994,10013,10017,10047,10082,10086,10116,10151,10155],{"__ignoreMap":106},[110,9739,9740,9743,9745,9747,9749,9751,9753,9755,9757,9759,9761,9763],{"class":112,"line":113},[110,9741,9742],{"class":228},"jp",[110,9744,232],{"class":116},[110,9746,2064],{"class":228},[110,9748,238],{"class":116},[110,9750,241],{"class":228},[110,9752,60],{"class":116},[110,9754,246],{"class":215},[110,9756,249],{"class":116},[110,9758,252],{"class":116},[110,9760,81],{"class":255},[110,9762,252],{"class":116},[110,9764,201],{"class":116},[110,9766,9767,9770,9772,9774,9776,9778,9780,9782,9784,9786,9789,9791],{"class":112,"line":124},[110,9768,9769],{"class":228},"sc",[110,9771,232],{"class":116},[110,9773,2064],{"class":228},[110,9775,238],{"class":116},[110,9777,241],{"class":228},[110,9779,60],{"class":116},[110,9781,246],{"class":215},[110,9783,249],{"class":116},[110,9785,252],{"class":116},[110,9787,9788],{"class":255},"NotoSansSC-Regular.ttf",[110,9790,252],{"class":116},[110,9792,201],{"class":116},[110,9794,9795,9798,9800,9802,9804,9806,9808,9810,9812,9814,9817,9819],{"class":112,"line":131},[110,9796,9797],{"class":228},"kr",[110,9799,232],{"class":116},[110,9801,2064],{"class":228},[110,9803,238],{"class":116},[110,9805,241],{"class":228},[110,9807,60],{"class":116},[110,9809,246],{"class":215},[110,9811,249],{"class":116},[110,9813,252],{"class":116},[110,9815,9816],{"class":255},"NotoSansKR-Regular.ttf",[110,9818,252],{"class":116},[110,9820,201],{"class":116},[110,9822,9823],{"class":112,"line":141},[110,9824,128],{"emptyLinePlaceholder":127},[110,9826,9827,9829,9831,9833,9835,9837],{"class":112,"line":153},[110,9828,801],{"class":228},[110,9830,238],{"class":116},[110,9832,315],{"class":228},[110,9834,60],{"class":116},[110,9836,320],{"class":215},[110,9838,323],{"class":116},[110,9840,9841,9843,9845,9847,9849,9851,9853,9855,9857,9860],{"class":112,"line":163},[110,9842,816],{"class":228},[110,9844,60],{"class":116},[110,9846,50],{"class":215},[110,9848,249],{"class":116},[110,9850,252],{"class":116},[110,9852,401],{"class":255},[110,9854,252],{"class":116},[110,9856,232],{"class":116},[110,9858,9859],{"class":228}," jp",[110,9861,347],{"class":116},[110,9863,9864,9866,9868,9870,9872,9874,9877,9879,9881,9884],{"class":112,"line":168},[110,9865,816],{"class":228},[110,9867,60],{"class":116},[110,9869,50],{"class":215},[110,9871,249],{"class":116},[110,9873,252],{"class":116},[110,9875,9876],{"class":255},"NotoSansSC",[110,9878,252],{"class":116},[110,9880,232],{"class":116},[110,9882,9883],{"class":228}," sc",[110,9885,347],{"class":116},[110,9887,9888,9890,9892,9894,9896,9898,9901,9903,9905,9908],{"class":112,"line":178},[110,9889,816],{"class":228},[110,9891,60],{"class":116},[110,9893,50],{"class":215},[110,9895,249],{"class":116},[110,9897,252],{"class":116},[110,9899,9900],{"class":255},"NotoSansKR",[110,9902,252],{"class":116},[110,9904,232],{"class":116},[110,9906,9907],{"class":228}," kr",[110,9909,347],{"class":116},[110,9911,9912,9914,9916,9918,9920,9922,9924,9926,9928,9930],{"class":112,"line":188},[110,9913,816],{"class":228},[110,9915,60],{"class":116},[110,9917,420],{"class":215},[110,9919,249],{"class":116},[110,9921,252],{"class":116},[110,9923,401],{"class":255},[110,9925,252],{"class":116},[110,9927,232],{"class":116},[110,9929,433],{"class":381},[110,9931,347],{"class":116},[110,9933,9934],{"class":112,"line":198},[110,9935,201],{"class":116},[110,9937,9938],{"class":112,"line":204},[110,9939,128],{"emptyLinePlaceholder":127},[110,9941,9942,9944,9946,9948,9950,9952,9954,9956,9958,9960,9962],{"class":112,"line":209},[110,9943,853],{"class":228},[110,9945,60],{"class":116},[110,9947,476],{"class":215},[110,9949,479],{"class":116},[110,9951,483],{"class":482},[110,9953,486],{"class":116},[110,9955,489],{"class":120},[110,9957,60],{"class":116},[110,9959,494],{"class":120},[110,9961,497],{"class":116},[110,9963,222],{"class":116},[110,9965,9966,9968,9970,9972,9974,9976,9978,9980,9982,9984,9986,9988,9990,9992],{"class":112,"line":225},[110,9967,878],{"class":228},[110,9969,60],{"class":116},[110,9971,510],{"class":215},[110,9973,249],{"class":116},[110,9975,3095],{"class":381},[110,9977,232],{"class":116},[110,9979,520],{"class":116},[110,9981,523],{"class":482},[110,9983,486],{"class":116},[110,9985,489],{"class":120},[110,9987,60],{"class":116},[110,9989,532],{"class":120},[110,9991,497],{"class":116},[110,9993,222],{"class":116},[110,9995,9996,9998,10000,10002,10004,10006,10009,10011],{"class":112,"line":262},[110,9997,909],{"class":228},[110,9999,60],{"class":116},[110,10001,547],{"class":215},[110,10003,249],{"class":116},[110,10005,252],{"class":116},[110,10007,10008],{"class":255},"日本語",[110,10010,252],{"class":116},[110,10012,201],{"class":116},[110,10014,10015],{"class":112,"line":278},[110,10016,570],{"class":116},[110,10018,10019,10021,10023,10025,10027,10029,10031,10033,10035,10037,10039,10041,10043,10045],{"class":112,"line":296},[110,10020,878],{"class":228},[110,10022,60],{"class":116},[110,10024,510],{"class":215},[110,10026,249],{"class":116},[110,10028,3095],{"class":381},[110,10030,232],{"class":116},[110,10032,520],{"class":116},[110,10034,523],{"class":482},[110,10036,486],{"class":116},[110,10038,489],{"class":120},[110,10040,60],{"class":116},[110,10042,532],{"class":120},[110,10044,497],{"class":116},[110,10046,222],{"class":116},[110,10048,10049,10051,10053,10055,10057,10059,10062,10064,10066,10068,10070,10072,10074,10076,10078,10080],{"class":112,"line":302},[110,10050,909],{"class":228},[110,10052,60],{"class":116},[110,10054,547],{"class":215},[110,10056,249],{"class":116},[110,10058,252],{"class":116},[110,10060,10061],{"class":255},"中文",[110,10063,252],{"class":116},[110,10065,232],{"class":116},[110,10067,1680],{"class":228},[110,10069,60],{"class":116},[110,10071,2256],{"class":215},[110,10073,249],{"class":116},[110,10075,252],{"class":116},[110,10077,9876],{"class":255},[110,10079,252],{"class":116},[110,10081,2279],{"class":116},[110,10083,10084],{"class":112,"line":307},[110,10085,570],{"class":116},[110,10087,10088,10090,10092,10094,10096,10098,10100,10102,10104,10106,10108,10110,10112,10114],{"class":112,"line":326},[110,10089,878],{"class":228},[110,10091,60],{"class":116},[110,10093,510],{"class":215},[110,10095,249],{"class":116},[110,10097,3095],{"class":381},[110,10099,232],{"class":116},[110,10101,520],{"class":116},[110,10103,523],{"class":482},[110,10105,486],{"class":116},[110,10107,489],{"class":120},[110,10109,60],{"class":116},[110,10111,532],{"class":120},[110,10113,497],{"class":116},[110,10115,222],{"class":116},[110,10117,10118,10120,10122,10124,10126,10128,10131,10133,10135,10137,10139,10141,10143,10145,10147,10149],{"class":112,"line":350},[110,10119,909],{"class":228},[110,10121,60],{"class":116},[110,10123,547],{"class":215},[110,10125,249],{"class":116},[110,10127,252],{"class":116},[110,10129,10130],{"class":255},"한국어",[110,10132,252],{"class":116},[110,10134,232],{"class":116},[110,10136,1680],{"class":228},[110,10138,60],{"class":116},[110,10140,2256],{"class":215},[110,10142,249],{"class":116},[110,10144,252],{"class":116},[110,10146,9900],{"class":255},[110,10148,252],{"class":116},[110,10150,2279],{"class":116},[110,10152,10153],{"class":112,"line":388},[110,10154,570],{"class":116},[110,10156,10157],{"class":112,"line":413},[110,10158,936],{"class":116},[19,10160,10161,10162,10165],{},"Han unification means the Unicode codepoints between Japanese and Simplified Chinese overlap, but the glyphs are drawn differently. Picking the right font isn't just aesthetic — ",[39,10163,10164],{},"the same codepoint renders as a different character shape depending on the font",". If you're producing invoices for both markets, get both files registered.",[14,10167,10169],{"id":10168},"the-tofu-trap","The tofu trap",[19,10171,10172,10173,10175],{},"If you write Japanese but forget ",[44,10174,50],{},", gpdf falls back to the Base-14 PDF fonts — none of which cover the CJK range. The characters render as blank rectangles, what Unicode people call \"tofu boxes\":",[101,10177,10180],{"className":10178,"code":10179,"language":1089},[6937],"□□□□□、□□。\n",[44,10181,10179],{"__ignoreMap":106},[19,10183,10184,10185,10187,10188,10190,10191,10194,10195,10197],{},"If you see that output, you forgot to register a CJK font, or you wrote the text in a font family that doesn't include those glyphs. The fix is always the same: add ",[44,10186,50],{}," and either use ",[44,10189,420],{}," or pass ",[44,10192,10193],{},"template.FontFamily"," on the ",[44,10196,59],{}," call.",[14,10199,2454],{"id":2453},[985,10201,10202,10211,10216],{},[36,10203,10204,10206,10207,10210],{},[723,10205,5615],{"href":3977}," — if you're coming from ",[44,10208,10209],{},"pdf.AddUTF8Font"," and want the full migration map",[36,10212,10213,10215],{},[723,10214,3824],{"href":3823}," — how gpdf compares to gofpdf, gopdf, Maroto, and unipdf on CJK",[36,10217,10218,10221,10222,10224],{},[723,10219,2478],{"href":2476,"rel":10220},[727]," — the full ",[44,10223,50],{}," reference including variant naming rules",[14,10226,1201],{"id":1200},[19,10228,1204],{},[101,10230,10231],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,10232,10233],{"__ignoreMap":106},[110,10234,10235,10237,10239],{"class":112,"line":113},[110,10236,105],{"class":120},[110,10238,1218],{"class":255},[110,10240,1221],{"class":255},[19,10242,10243,1229,10246],{},[723,10244,1228],{"href":1226,"rel":10245},[727],[723,10247,1234],{"href":1232,"rel":10248},[727],[1236,10250,2508],{},{"title":106,"searchDepth":124,"depth":124,"links":10252},[10253,10254,10255,10256,10257,10258,10259,10260,10261],{"id":16,"depth":124,"text":17},{"id":1310,"depth":124,"text":1311},{"id":1329,"depth":124,"text":1330},{"id":9515,"depth":124,"text":9516},{"id":9546,"depth":124,"text":9547},{"id":9724,"depth":124,"text":9725},{"id":10168,"depth":124,"text":10169},{"id":2453,"depth":124,"text":2454},{"id":1200,"depth":124,"text":1201},"Register a Japanese TrueType font with gpdf.WithFont at document construction. Three lines, subset embedding happens automatically, no CGO.",{"name":10264,"totalTime":2523,"tools":10265,"steps":10267},"Embed a Japanese TrueType font in a gpdf document",[1259,10266],"NotoSansJP-Regular.ttf (or any CJK-capable TTF)",[10268,10270,10272,10275],{"name":2531,"text":10269},"Read the NotoSansJP-Regular.ttf file with os.ReadFile into a []byte at program start. Embedding with //go:embed works too if you want the font compiled into the binary.",{"name":2534,"text":10271},"Pass gpdf.WithFont(\"NotoSansJP\", fontBytes) to gpdf.NewDocument. The family name is arbitrary — use whatever you'll reference later. gpdf subsets the glyph table at render time.",{"name":10273,"text":10274},"Set it as the default font","Add gpdf.WithDefaultFont(\"NotoSansJP\", 12) so every c.Text call uses the Japanese font without an explicit FontFamily option.",{"name":10276,"text":10277},"Write Japanese text and generate the PDF","Call c.Text(\"こんにちは、世界。\") inside a column. Then doc.Generate() returns []byte, which you write to disk with os.WriteFile.",{},{"title":1175,"description":10262},"blog/003.embed-japanese-font",[1283,1285,2547],"fD5yk3yBNUIQuBc8PSm_Qu8w7ukyfcoTTpmaanNiEkA",{"id":10284,"title":1185,"author":10285,"body":10286,"date":8953,"description":11629,"draft":1253,"extension":1254,"howTo":11630,"image":1277,"meta":11646,"navigation":127,"path":1184,"seo":11647,"stem":11648,"tags":11649,"updated":1277,"__hash__":11650},"blog/blog/004.noto-sans-jp-with-gpdf.md",{"name":8,"url":9},{"type":11,"value":10287,"toc":11616},[10288,10290,10297,10299,10316,10318,10823,10837,10841,10852,10877,10882,10906,10909,10913,10916,10983,11003,11014,11018,11021,11166,11180,11183,11252,11263,11267,11270,11273,11334,11337,11340,11344,11347,11352,11365,11374,11388,11392,11395,11553,11560,11562,11589,11591,11593,11605,11613],[14,10289,17],{"id":16},[19,10291,10292,10293,10296],{},"You want Japanese text in a ",[723,10294,339],{"href":1226,"rel":10295},[727]," document, you've picked Noto Sans JP — Google's free, SIL-OFL-licensed sans-serif that covers the full JIS range — and you want to know the three things nobody spells out: which file to download, which weights to register, and the one footgun hiding in the zip.",[14,10298,1311],{"id":1310},[19,10300,10301,10302,42,10305,10307,10308,10311,10312,10315],{},"Use the ",[39,10303,10304],{},"static",[44,10306,81],{}," from the ",[44,10309,10310],{},"static/"," folder inside the Google Fonts zip. Not the variable font at the root. Pass it to ",[44,10313,10314],{},"gpdf.WithFont(\"NotoSansJP\", bytes)"," and set it as the default. gpdf subsets the ~17,000 glyphs down to whatever you rendered — an invoice typically carries 20–40 KB of font data in the final PDF.",[14,10317,1330],{"id":1329},[101,10319,10321],{"className":103,"code":10320,"language":105,"meta":106,"style":106},"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",[44,10322,10323,10329,10333,10339,10347,10355,10359,10367,10375,10383,10387,10391,10401,10427,10439,10453,10457,10461,10475,10493,10523,10545,10567,10571,10575,10589,10613,10643,10682,10701,10705,10709,10713,10731,10743,10757,10761,10801,10815,10819],{"__ignoreMap":106},[110,10324,10325,10327],{"class":112,"line":113},[110,10326,117],{"class":116},[110,10328,121],{"class":120},[110,10330,10331],{"class":112,"line":124},[110,10332,128],{"emptyLinePlaceholder":127},[110,10334,10335,10337],{"class":112,"line":131},[110,10336,135],{"class":134},[110,10338,138],{"class":116},[110,10340,10341,10343,10345],{"class":112,"line":141},[110,10342,144],{"class":116},[110,10344,147],{"class":120},[110,10346,150],{"class":116},[110,10348,10349,10351,10353],{"class":112,"line":153},[110,10350,144],{"class":116},[110,10352,158],{"class":120},[110,10354,150],{"class":116},[110,10356,10357],{"class":112,"line":163},[110,10358,128],{"emptyLinePlaceholder":127},[110,10360,10361,10363,10365],{"class":112,"line":168},[110,10362,144],{"class":116},[110,10364,173],{"class":120},[110,10366,150],{"class":116},[110,10368,10369,10371,10373],{"class":112,"line":178},[110,10370,144],{"class":116},[110,10372,183],{"class":120},[110,10374,150],{"class":116},[110,10376,10377,10379,10381],{"class":112,"line":188},[110,10378,144],{"class":116},[110,10380,193],{"class":120},[110,10382,150],{"class":116},[110,10384,10385],{"class":112,"line":198},[110,10386,201],{"class":116},[110,10388,10389],{"class":112,"line":204},[110,10390,128],{"emptyLinePlaceholder":127},[110,10392,10393,10395,10397,10399],{"class":112,"line":209},[110,10394,212],{"class":116},[110,10396,216],{"class":215},[110,10398,219],{"class":116},[110,10400,222],{"class":116},[110,10402,10403,10405,10407,10409,10411,10413,10415,10417,10419,10421,10423,10425],{"class":112,"line":225},[110,10404,229],{"class":228},[110,10406,232],{"class":116},[110,10408,235],{"class":228},[110,10410,238],{"class":116},[110,10412,241],{"class":228},[110,10414,60],{"class":116},[110,10416,246],{"class":215},[110,10418,249],{"class":116},[110,10420,252],{"class":116},[110,10422,81],{"class":255},[110,10424,252],{"class":116},[110,10426,201],{"class":116},[110,10428,10429,10431,10433,10435,10437],{"class":112,"line":262},[110,10430,265],{"class":134},[110,10432,235],{"class":228},[110,10434,270],{"class":116},[110,10436,273],{"class":116},[110,10438,222],{"class":116},[110,10440,10441,10443,10445,10447,10449,10451],{"class":112,"line":278},[110,10442,281],{"class":228},[110,10444,60],{"class":116},[110,10446,286],{"class":215},[110,10448,249],{"class":116},[110,10450,291],{"class":228},[110,10452,201],{"class":116},[110,10454,10455],{"class":112,"line":296},[110,10456,299],{"class":116},[110,10458,10459],{"class":112,"line":302},[110,10460,128],{"emptyLinePlaceholder":127},[110,10462,10463,10465,10467,10469,10471,10473],{"class":112,"line":307},[110,10464,310],{"class":228},[110,10466,238],{"class":116},[110,10468,315],{"class":228},[110,10470,60],{"class":116},[110,10472,320],{"class":215},[110,10474,323],{"class":116},[110,10476,10477,10479,10481,10483,10485,10487,10489,10491],{"class":112,"line":326},[110,10478,329],{"class":228},[110,10480,60],{"class":116},[110,10482,334],{"class":215},[110,10484,249],{"class":116},[110,10486,339],{"class":228},[110,10488,60],{"class":116},[110,10490,344],{"class":228},[110,10492,347],{"class":116},[110,10494,10495,10497,10499,10501,10503,10505,10507,10509,10511,10513,10515,10517,10519,10521],{"class":112,"line":350},[110,10496,329],{"class":228},[110,10498,60],{"class":116},[110,10500,357],{"class":215},[110,10502,249],{"class":116},[110,10504,362],{"class":228},[110,10506,60],{"class":116},[110,10508,367],{"class":215},[110,10510,249],{"class":116},[110,10512,362],{"class":228},[110,10514,60],{"class":116},[110,10516,376],{"class":215},[110,10518,249],{"class":116},[110,10520,382],{"class":381},[110,10522,385],{"class":116},[110,10524,10525,10527,10529,10531,10533,10535,10537,10539,10541,10543],{"class":112,"line":388},[110,10526,329],{"class":228},[110,10528,60],{"class":116},[110,10530,50],{"class":215},[110,10532,249],{"class":116},[110,10534,252],{"class":116},[110,10536,401],{"class":255},[110,10538,252],{"class":116},[110,10540,232],{"class":116},[110,10542,408],{"class":228},[110,10544,347],{"class":116},[110,10546,10547,10549,10551,10553,10555,10557,10559,10561,10563,10565],{"class":112,"line":413},[110,10548,329],{"class":228},[110,10550,60],{"class":116},[110,10552,420],{"class":215},[110,10554,249],{"class":116},[110,10556,252],{"class":116},[110,10558,401],{"class":255},[110,10560,252],{"class":116},[110,10562,232],{"class":116},[110,10564,6259],{"class":381},[110,10566,347],{"class":116},[110,10568,10569],{"class":112,"line":438},[110,10570,441],{"class":116},[110,10572,10573],{"class":112,"line":444},[110,10574,128],{"emptyLinePlaceholder":127},[110,10576,10577,10579,10581,10583,10585,10587],{"class":112,"line":449},[110,10578,452],{"class":228},[110,10580,238],{"class":116},[110,10582,457],{"class":228},[110,10584,60],{"class":116},[110,10586,462],{"class":215},[110,10588,465],{"class":116},[110,10590,10591,10593,10595,10597,10599,10601,10603,10605,10607,10609,10611],{"class":112,"line":468},[110,10592,471],{"class":228},[110,10594,60],{"class":116},[110,10596,476],{"class":215},[110,10598,479],{"class":116},[110,10600,483],{"class":482},[110,10602,486],{"class":116},[110,10604,489],{"class":120},[110,10606,60],{"class":116},[110,10608,494],{"class":120},[110,10610,497],{"class":116},[110,10612,222],{"class":116},[110,10614,10615,10617,10619,10621,10623,10625,10627,10629,10631,10633,10635,10637,10639,10641],{"class":112,"line":502},[110,10616,505],{"class":228},[110,10618,60],{"class":116},[110,10620,510],{"class":215},[110,10622,249],{"class":116},[110,10624,515],{"class":381},[110,10626,232],{"class":116},[110,10628,520],{"class":116},[110,10630,523],{"class":482},[110,10632,486],{"class":116},[110,10634,489],{"class":120},[110,10636,60],{"class":116},[110,10638,532],{"class":120},[110,10640,497],{"class":116},[110,10642,222],{"class":116},[110,10644,10645,10647,10649,10651,10653,10655,10657,10659,10661,10663,10665,10667,10669,10672,10674,10676,10678,10680],{"class":112,"line":539},[110,10646,542],{"class":228},[110,10648,60],{"class":116},[110,10650,547],{"class":215},[110,10652,249],{"class":116},[110,10654,252],{"class":116},[110,10656,1673],{"class":255},[110,10658,252],{"class":116},[110,10660,232],{"class":116},[110,10662,1680],{"class":228},[110,10664,60],{"class":116},[110,10666,1685],{"class":215},[110,10668,249],{"class":116},[110,10670,10671],{"class":381},"28",[110,10673,1693],{"class":116},[110,10675,1680],{"class":228},[110,10677,60],{"class":116},[110,10679,1700],{"class":215},[110,10681,1703],{"class":116},[110,10683,10684,10686,10688,10690,10692,10694,10697,10699],{"class":112,"line":561},[110,10685,542],{"class":228},[110,10687,60],{"class":116},[110,10689,547],{"class":215},[110,10691,249],{"class":116},[110,10693,252],{"class":116},[110,10695,10696],{"class":255},"Noto Sans JP、これで十分。",[110,10698,252],{"class":116},[110,10700,201],{"class":116},[110,10702,10703],{"class":112,"line":567},[110,10704,564],{"class":116},[110,10706,10707],{"class":112,"line":573},[110,10708,570],{"class":116},[110,10710,10711],{"class":112,"line":578},[110,10712,128],{"emptyLinePlaceholder":127},[110,10714,10715,10717,10719,10721,10723,10725,10727,10729],{"class":112,"line":599},[110,10716,581],{"class":228},[110,10718,232],{"class":116},[110,10720,235],{"class":228},[110,10722,238],{"class":116},[110,10724,457],{"class":228},[110,10726,60],{"class":116},[110,10728,594],{"class":215},[110,10730,465],{"class":116},[110,10732,10733,10735,10737,10739,10741],{"class":112,"line":612},[110,10734,265],{"class":134},[110,10736,235],{"class":228},[110,10738,270],{"class":116},[110,10740,273],{"class":116},[110,10742,222],{"class":116},[110,10744,10745,10747,10749,10751,10753,10755],{"class":112,"line":627},[110,10746,281],{"class":228},[110,10748,60],{"class":116},[110,10750,286],{"class":215},[110,10752,249],{"class":116},[110,10754,291],{"class":228},[110,10756,201],{"class":116},[110,10758,10759],{"class":112,"line":632},[110,10760,299],{"class":116},[110,10762,10763,10765,10767,10769,10771,10773,10775,10777,10779,10781,10783,10785,10787,10789,10791,10793,10795,10797,10799],{"class":112,"line":678},[110,10764,265],{"class":134},[110,10766,235],{"class":228},[110,10768,238],{"class":116},[110,10770,241],{"class":228},[110,10772,60],{"class":116},[110,10774,645],{"class":215},[110,10776,249],{"class":116},[110,10778,252],{"class":116},[110,10780,1822],{"class":255},[110,10782,252],{"class":116},[110,10784,232],{"class":116},[110,10786,659],{"class":228},[110,10788,232],{"class":116},[110,10790,664],{"class":381},[110,10792,667],{"class":116},[110,10794,235],{"class":228},[110,10796,270],{"class":116},[110,10798,273],{"class":116},[110,10800,222],{"class":116},[110,10802,10803,10805,10807,10809,10811,10813],{"class":112,"line":693},[110,10804,281],{"class":228},[110,10806,60],{"class":116},[110,10808,286],{"class":215},[110,10810,249],{"class":116},[110,10812,291],{"class":228},[110,10814,201],{"class":116},[110,10816,10817],{"class":112,"line":698},[110,10818,299],{"class":116},[110,10820,10821],{"class":112,"line":1859},[110,10822,701],{"class":116},[19,10824,10825,10826,1877,10829,1880,10832,10834,10835,60],{},"Download the Noto Sans JP zip from ",[723,10827,728],{"href":725,"rel":10828},[727],[44,10830,10831],{},"static/NotoSansJP-Regular.ttf",[44,10833,1883],{},", and run ",[44,10836,1887],{},[14,10838,10840],{"id":10839},"grab-the-static-ttf-not-the-variable-font","Grab the static TTF, not the variable font",[19,10842,10843,10844,10847,10848,10851],{},"Open the Google Fonts page, hit ",[39,10845,10846],{},"Get font"," → ",[39,10849,10850],{},"Download all",", unzip. You get two things that look interchangeable and aren't:",[985,10853,10854,10864],{},[36,10855,10856,10859,10860,10863],{},[44,10857,10858],{},"NotoSansJP-VariableFont_wght.ttf"," at the root — the ",[39,10861,10862],{},"variable"," font, ~7 MB, carries every weight between 100 and 900 in a single file",[36,10865,10866,10868,10869,10872,10873,10876],{},[44,10867,10310],{}," — nine separate TTFs, ",[44,10870,10871],{},"NotoSansJP-Thin.ttf"," through ",[44,10874,10875],{},"NotoSansJP-Black.ttf",", each ~5 MB",[19,10878,10879,10880,60],{},"Pick from ",[44,10881,10310],{},[19,10883,10884,10885,1884,10887,10890,10891,2563,10894,10897,10898,10901,10902,10905],{},"gpdf's TrueType parser is deliberately scoped. It handles glyph outlines, composite glyphs, ",[44,10886,5735],{},[44,10888,10889],{},"hmtx"," — the tables you need to render fixed-weight type. It does not evaluate ",[44,10892,10893],{},"fvar",[44,10895,10896],{},"gvar",", or ",[44,10899,10900],{},"HVAR",", which are the OpenType tables that make variable fonts actually variable. Hand it the ",[44,10903,10904],{},"VariableFont_wght.ttf"," and the parser either errors out or falls through to the default instance glyphs, silently ignoring any weight axis you thought you were setting.",[19,10907,10908],{},"The file-size math also argues against it. A variable font bundles every weight's outlines — that's the whole point. If you use only Regular, you're paying for eight weights of outline data that never render. Static Regular is 5 MB; the variable font is 7 MB. Subsetting will prune both down, but the static file is cleaner input.",[14,10910,10912],{"id":10911},"the-four-lines-that-matter","The four lines that matter",[19,10914,10915],{},"Everything interesting is in the constructor options:",[101,10917,10919],{"className":103,"code":10918,"language":105,"meta":106,"style":106},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[44,10920,10921,10935,10957,10979],{"__ignoreMap":106},[110,10922,10923,10925,10927,10929,10931,10933],{"class":112,"line":113},[110,10924,801],{"class":228},[110,10926,238],{"class":116},[110,10928,315],{"class":228},[110,10930,60],{"class":116},[110,10932,320],{"class":215},[110,10934,323],{"class":116},[110,10936,10937,10939,10941,10943,10945,10947,10949,10951,10953,10955],{"class":112,"line":124},[110,10938,816],{"class":228},[110,10940,60],{"class":116},[110,10942,50],{"class":215},[110,10944,249],{"class":116},[110,10946,252],{"class":116},[110,10948,401],{"class":255},[110,10950,252],{"class":116},[110,10952,232],{"class":116},[110,10954,408],{"class":228},[110,10956,347],{"class":116},[110,10958,10959,10961,10963,10965,10967,10969,10971,10973,10975,10977],{"class":112,"line":131},[110,10960,816],{"class":228},[110,10962,60],{"class":116},[110,10964,420],{"class":215},[110,10966,249],{"class":116},[110,10968,252],{"class":116},[110,10970,401],{"class":255},[110,10972,252],{"class":116},[110,10974,232],{"class":116},[110,10976,6259],{"class":381},[110,10978,347],{"class":116},[110,10980,10981],{"class":112,"line":141},[110,10982,201],{"class":116},[19,10984,10985,10986,10989,10990,762,10993,762,10996,10999,11000,11002],{},"The family name (",[44,10987,10988],{},"\"NotoSansJP\"",") is arbitrary. gpdf uses it as a lookup key — not a filesystem path, not a field read from the font's metadata. Call it ",[44,10991,10992],{},"\"body\"",[44,10994,10995],{},"\"jp\"",[44,10997,10998],{},"\"Noto\""," if that reads better in your codebase. Just keep it consistent with the argument you pass to ",[44,11001,9731],{}," later.",[19,11004,11005,11007,11008,11010,11011,11013],{},[44,11006,420],{}," is the one that saves you from writing ",[44,11009,949],{}," on every single ",[44,11012,59],{}," call. Skip it and gpdf falls back to Helvetica for unlabeled text, and Helvetica covers zero CJK codepoints. You'll get tofu boxes on half the document and spend an hour figuring out why only the headings render.",[14,11015,11017],{"id":11016},"which-weights-do-you-actually-need","Which weights do you actually need?",[19,11019,11020],{},"Most invoices, receipts, and reports need two: Regular and Bold. Register both:",[101,11022,11024],{"className":103,"code":11023,"language":105,"meta":106,"style":106},"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",[44,11025,11026,11052,11078,11082,11096,11118,11140,11162],{"__ignoreMap":106},[110,11027,11028,11030,11032,11034,11036,11038,11040,11042,11044,11046,11048,11050],{"class":112,"line":113},[110,11029,9568],{"class":228},[110,11031,232],{"class":116},[110,11033,2036],{"class":228},[110,11035,238],{"class":116},[110,11037,241],{"class":228},[110,11039,60],{"class":116},[110,11041,246],{"class":215},[110,11043,249],{"class":116},[110,11045,252],{"class":116},[110,11047,81],{"class":255},[110,11049,252],{"class":116},[110,11051,201],{"class":116},[110,11053,11054,11056,11058,11060,11062,11064,11066,11068,11070,11072,11074,11076],{"class":112,"line":124},[110,11055,9553],{"class":228},[110,11057,232],{"class":116},[110,11059,2064],{"class":228},[110,11061,238],{"class":116},[110,11063,241],{"class":228},[110,11065,60],{"class":116},[110,11067,246],{"class":215},[110,11069,249],{"class":116},[110,11071,252],{"class":116},[110,11073,6091],{"class":255},[110,11075,252],{"class":116},[110,11077,201],{"class":116},[110,11079,11080],{"class":112,"line":131},[110,11081,128],{"emptyLinePlaceholder":127},[110,11083,11084,11086,11088,11090,11092,11094],{"class":112,"line":141},[110,11085,801],{"class":228},[110,11087,238],{"class":116},[110,11089,315],{"class":228},[110,11091,60],{"class":116},[110,11093,320],{"class":215},[110,11095,323],{"class":116},[110,11097,11098,11100,11102,11104,11106,11108,11110,11112,11114,11116],{"class":112,"line":153},[110,11099,816],{"class":228},[110,11101,60],{"class":116},[110,11103,50],{"class":215},[110,11105,249],{"class":116},[110,11107,252],{"class":116},[110,11109,401],{"class":255},[110,11111,252],{"class":116},[110,11113,232],{"class":116},[110,11115,9655],{"class":228},[110,11117,347],{"class":116},[110,11119,11120,11122,11124,11126,11128,11130,11132,11134,11136,11138],{"class":112,"line":163},[110,11121,816],{"class":228},[110,11123,60],{"class":116},[110,11125,50],{"class":215},[110,11127,249],{"class":116},[110,11129,252],{"class":116},[110,11131,6229],{"class":255},[110,11133,252],{"class":116},[110,11135,232],{"class":116},[110,11137,6236],{"class":228},[110,11139,347],{"class":116},[110,11141,11142,11144,11146,11148,11150,11152,11154,11156,11158,11160],{"class":112,"line":168},[110,11143,816],{"class":228},[110,11145,60],{"class":116},[110,11147,420],{"class":215},[110,11149,249],{"class":116},[110,11151,252],{"class":116},[110,11153,401],{"class":255},[110,11155,252],{"class":116},[110,11157,232],{"class":116},[110,11159,6259],{"class":381},[110,11161,347],{"class":116},[110,11163,11164],{"class":112,"line":178},[110,11165,201],{"class":116},[19,11167,11168,11169,11171,11172,11174,11175,962,11177,11179],{},"With ",[44,11170,9557],{}," registered under that suffix, ",[44,11173,1325],{}," picks it up automatically. Same rule for ",[44,11176,9717],{},[44,11178,9720],{},". Note that Noto Sans JP doesn't ship an italic — CJK fonts generally don't, because the glyph design doesn't have an obvious oblique. If your layout wants italic emphasis on a Japanese run, reach for color, size, or weight instead.",[19,11181,11182],{},"Marketing brochures occasionally want Medium or SemiBold for pull quotes. That's fine. Register them under any suffix and address them by family name directly:",[101,11184,11186],{"className":103,"code":11185,"language":105,"meta":106,"style":106},"gpdf.WithFont(\"NotoSansJP-Medium\", medium)\n// ...\nc.Text(\"見出し\", template.FontFamily(\"NotoSansJP-Medium\"))\n",[44,11187,11188,11212,11217],{"__ignoreMap":106},[110,11189,11190,11192,11194,11196,11198,11200,11203,11205,11207,11210],{"class":112,"line":113},[110,11191,339],{"class":228},[110,11193,60],{"class":116},[110,11195,50],{"class":215},[110,11197,249],{"class":116},[110,11199,252],{"class":116},[110,11201,11202],{"class":255},"NotoSansJP-Medium",[110,11204,252],{"class":116},[110,11206,232],{"class":116},[110,11208,11209],{"class":228}," medium",[110,11211,201],{"class":116},[110,11213,11214],{"class":112,"line":124},[110,11215,11216],{"class":839},"// ...\n",[110,11218,11219,11221,11223,11225,11227,11229,11232,11234,11236,11238,11240,11242,11244,11246,11248,11250],{"class":112,"line":131},[110,11220,523],{"class":228},[110,11222,60],{"class":116},[110,11224,547],{"class":215},[110,11226,249],{"class":116},[110,11228,252],{"class":116},[110,11230,11231],{"class":255},"見出し",[110,11233,252],{"class":116},[110,11235,232],{"class":116},[110,11237,1680],{"class":228},[110,11239,60],{"class":116},[110,11241,2256],{"class":215},[110,11243,249],{"class":116},[110,11245,252],{"class":116},[110,11247,11202],{"class":255},[110,11249,252],{"class":116},[110,11251,2279],{"class":116},[19,11253,11254,11255,11257,11258,11257,11260,11262],{},"The suffix-based Bold/Italic shortcut only hooks up for the literal ",[44,11256,9557],{}," / ",[44,11259,9717],{},[44,11261,9720],{}," names. Anything else, you reference by family name.",[14,11264,11266],{"id":11265},"the-size-after-subsetting","The size after subsetting",[19,11268,11269],{},"Noto Sans JP Regular is ~5 MB on disk. That number pushes people into building separate CDNs for font files or post-processing PDFs to strip embedded fonts. Neither is necessary with gpdf.",[19,11271,11272],{},"Here's what actually lands in the PDF:",[1896,11274,11275,11288],{},[1899,11276,11277],{},[1902,11278,11279,11282,11285],{},[1905,11280,11281],{},"Document",[1905,11283,11284],{},"Glyphs used",[1905,11286,11287],{},"Font data in PDF",[1912,11289,11290,11301,11312,11323],{},[1902,11291,11292,11295,11298],{},[1917,11293,11294],{},"One-line receipt (~15 chars)",[1917,11296,11297],{},"~14",[1917,11299,11300],{},"~11 KB",[1902,11302,11303,11306,11309],{},[1917,11304,11305],{},"Typical invoice (~200 chars)",[1917,11307,11308],{},"~80",[1917,11310,11311],{},"~28 KB",[1902,11313,11314,11317,11320],{},[1917,11315,11316],{},"10-page report (~8,000 chars)",[1917,11318,11319],{},"~900",[1917,11321,11322],{},"~180 KB",[1902,11324,11325,11328,11331],{},[1917,11326,11327],{},"Dictionary-style dump (full JIS Level 1)",[1917,11329,11330],{},"~6,800",[1917,11332,11333],{},"~2.1 MB",[19,11335,11336],{},"(gpdf v1.0, static subsetting on. Numbers shift by a few KB depending on which glyph IDs fall in the CFF and hmtx tables.)",[19,11338,11339],{},"For an invoice that ends up 50 KB, more than half of the bytes are font data. That's still a rounding error next to the 5 MB you'd embed without subsetting, and the PDF viewer opens it instantly.",[14,11341,11343],{"id":11342},"noto-sans-jp-vs-noto-sans-cjk-jp-dont-mix-these-up","Noto Sans JP vs Noto Sans CJK JP — don't mix these up",[19,11345,11346],{},"There are two Noto families that both claim to handle Japanese, and the naming makes them sound interchangeable. They aren't.",[19,11348,11349,11351],{},[39,11350,2376],{}," is the one you want. Distributed as TTF, one language, each weight is a separate file. This is the Google Fonts download.",[19,11353,11354,11357,11358,11361,11362,60],{},[39,11355,11356],{},"Noto Sans CJK JP"," is the pan-CJK super-family. Distributed as OpenType Collection (",[44,11359,11360],{},".ttc","), a single file containing Japanese, Simplified Chinese, Traditional Chinese, and Korean glyphs unified in one package. It's what shipped in earlier Noto releases and what you'll find on ",[44,11363,11364],{},"notofonts.github.io/noto-cjk",[19,11366,11367,11368,11370,11371,11373],{},"gpdf supports TTF directly. TTC is a container format — you'd need to pick the right face index before handing bytes to ",[44,11369,50],{},", and the ",[44,11372,5735],{}," inside each face is tuned for a specific CJK locale, which means you're making implicit choices about Han unification. Easier to make those choices explicitly by picking the JP-specific TTF.",[19,11375,11376,11377,11380,11381,762,11384,11387],{},"Starting today? Use Noto Sans JP. Already have ",[44,11378,11379],{},"NotoSansCJK-Regular.ttc"," in a legacy project? Extract the JP face with ",[44,11382,11383],{},"pyftsubset",[44,11385,11386],{},"fonttools"," and check the resulting TTF into your repo as the canonical artifact.",[14,11389,11391],{"id":11390},"embedding-the-font-into-the-binary","Embedding the font into the binary",[19,11393,11394],{},"PDF generators usually run in containers, and the cleanest way to ship the font is to compile it in:",[101,11396,11398],{"className":103,"code":11397,"language":105,"meta":106,"style":106},"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",[44,11399,11400,11406,11410,11416,11428,11432,11440,11444,11448,11453,11467,11471,11481,11495,11518,11540,11544,11549],{"__ignoreMap":106},[110,11401,11402,11404],{"class":112,"line":113},[110,11403,117],{"class":116},[110,11405,121],{"class":120},[110,11407,11408],{"class":112,"line":124},[110,11409,128],{"emptyLinePlaceholder":127},[110,11411,11412,11414],{"class":112,"line":131},[110,11413,135],{"class":134},[110,11415,138],{"class":116},[110,11417,11418,11421,11423,11426],{"class":112,"line":141},[110,11419,11420],{"class":228},"    _ ",[110,11422,252],{"class":116},[110,11424,11425],{"class":120},"embed",[110,11427,150],{"class":116},[110,11429,11430],{"class":112,"line":153},[110,11431,128],{"emptyLinePlaceholder":127},[110,11433,11434,11436,11438],{"class":112,"line":163},[110,11435,144],{"class":116},[110,11437,173],{"class":120},[110,11439,150],{"class":116},[110,11441,11442],{"class":112,"line":168},[110,11443,201],{"class":116},[110,11445,11446],{"class":112,"line":178},[110,11447,128],{"emptyLinePlaceholder":127},[110,11449,11450],{"class":112,"line":188},[110,11451,11452],{"class":839},"//go:embed NotoSansJP-Regular.ttf\n",[110,11454,11455,11458,11461,11464],{"class":112,"line":198},[110,11456,11457],{"class":116},"var",[110,11459,11460],{"class":228}," notoJP ",[110,11462,11463],{"class":116},"[]",[110,11465,11466],{"class":8369},"byte\n",[110,11468,11469],{"class":112,"line":204},[110,11470,128],{"emptyLinePlaceholder":127},[110,11472,11473,11475,11477,11479],{"class":112,"line":209},[110,11474,212],{"class":116},[110,11476,216],{"class":215},[110,11478,219],{"class":116},[110,11480,222],{"class":116},[110,11482,11483,11485,11487,11489,11491,11493],{"class":112,"line":225},[110,11484,310],{"class":228},[110,11486,238],{"class":116},[110,11488,315],{"class":228},[110,11490,60],{"class":116},[110,11492,320],{"class":215},[110,11494,323],{"class":116},[110,11496,11497,11499,11501,11503,11505,11507,11509,11511,11513,11516],{"class":112,"line":262},[110,11498,329],{"class":228},[110,11500,60],{"class":116},[110,11502,50],{"class":215},[110,11504,249],{"class":116},[110,11506,252],{"class":116},[110,11508,401],{"class":255},[110,11510,252],{"class":116},[110,11512,232],{"class":116},[110,11514,11515],{"class":228}," notoJP",[110,11517,347],{"class":116},[110,11519,11520,11522,11524,11526,11528,11530,11532,11534,11536,11538],{"class":112,"line":278},[110,11521,329],{"class":228},[110,11523,60],{"class":116},[110,11525,420],{"class":215},[110,11527,249],{"class":116},[110,11529,252],{"class":116},[110,11531,401],{"class":255},[110,11533,252],{"class":116},[110,11535,232],{"class":116},[110,11537,6259],{"class":381},[110,11539,347],{"class":116},[110,11541,11542],{"class":112,"line":296},[110,11543,441],{"class":116},[110,11545,11546],{"class":112,"line":302},[110,11547,11548],{"class":839},"    // ...\n",[110,11550,11551],{"class":112,"line":307},[110,11552,701],{"class":116},[19,11554,11555,11556,11559],{},"Binary grows from ~8 MB to ~13 MB. In return, your Docker image has one artifact instead of two, ",[44,11557,11558],{},"COPY --from=builder /app /app"," is all you need, and nobody can ship a broken container missing the font file. For a batch job generating thousands of PDFs a day, this is the right default.",[14,11561,2454],{"id":2453},[985,11563,11564,11569,11576,11581],{},[36,11565,11566,11568],{},[723,11567,1175],{"href":1174}," — the general recipe, applies to any CJK TTF",[36,11570,11571,11573,11574],{},[723,11572,5615],{"href":3977}," — migration map from ",[44,11575,10209],{},[36,11577,11578,11580],{},[723,11579,3824],{"href":3823}," — how gpdf compares on CJK specifically",[36,11582,11583,10221,11586,11588],{},[723,11584,2478],{"href":2476,"rel":11585},[727],[44,11587,50],{}," reference and variant naming rules",[14,11590,1201],{"id":1200},[19,11592,1204],{},[101,11594,11595],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,11596,11597],{"__ignoreMap":106},[110,11598,11599,11601,11603],{"class":112,"line":113},[110,11600,105],{"class":120},[110,11602,1218],{"class":255},[110,11604,1221],{"class":255},[19,11606,11607,1229,11610],{},[723,11608,1228],{"href":1226,"rel":11609},[727],[723,11611,1234],{"href":1232,"rel":11612},[727],[1236,11614,11615],{},"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":106,"searchDepth":124,"depth":124,"links":11617},[11618,11619,11620,11621,11622,11623,11624,11625,11626,11627,11628],{"id":16,"depth":124,"text":17},{"id":1310,"depth":124,"text":1311},{"id":1329,"depth":124,"text":1330},{"id":10839,"depth":124,"text":10840},{"id":10911,"depth":124,"text":10912},{"id":11016,"depth":124,"text":11017},{"id":11265,"depth":124,"text":11266},{"id":11342,"depth":124,"text":11343},{"id":11390,"depth":124,"text":11391},{"id":2453,"depth":124,"text":2454},{"id":1200,"depth":124,"text":1201},"Register the static NotoSansJP-Regular.ttf with gpdf.WithFont. Skip the variable font — gpdf's pure-Go parser does not read fvar tables. Subsetting lands around 30 KB.",{"name":11631,"totalTime":2523,"tools":11632,"steps":11634},"Use Noto Sans JP as the default font in a gpdf document",[1259,11633],"NotoSansJP-Regular.ttf (static TTF from Google Fonts)",[11635,11638,11641,11643],{"name":11636,"text":11637},"Download the static TTF from Google Fonts","Grab Noto Sans JP from fonts.google.com, unzip the bundle, and pick static/NotoSansJP-Regular.ttf. Do not use the NotoSansJP-VariableFont_wght.ttf sitting at the root.",{"name":11639,"text":11640},"Load the bytes at startup","Read NotoSansJP-Regular.ttf with os.ReadFile, or compile it in with //go:embed for a single-artifact binary.",{"name":2534,"text":11642},"Pass gpdf.WithFont(\"NotoSansJP\", fontBytes) and gpdf.WithDefaultFont(\"NotoSansJP\", 11) to gpdf.NewDocument. No AddUTF8Font, no filesystem path.",{"name":11644,"text":11645},"Write Japanese text and generate","Call c.Text(\"請求書\") inside a column. doc.Generate() returns []byte and gpdf subsets only the glyphs you actually used into the final PDF.",{},{"title":1185,"description":11629},"blog/004.noto-sans-jp-with-gpdf",[1283,1285,2547],"Pa_qo0A9IKywvaQbcsuRmUxB9WbNQ2lpH74ZV1Nx_ZQ",{"id":11652,"title":5615,"author":11653,"body":11654,"date":16377,"description":16378,"draft":1253,"extension":1254,"howTo":16379,"image":1277,"meta":16402,"navigation":127,"path":3977,"seo":16403,"stem":16404,"tags":16405,"updated":1277,"__hash__":16406},"blog/blog/001.gofpdf-migration.md",{"name":8,"url":9},{"type":11,"value":11655,"toc":16361},[11656,11658,11673,11683,11686,11698,11701,11705,11711,11717,11724,11727,11731,11739,11765,11771,11775,11778,12044,12057,12087,12091,12094,12099,12272,12276,12657,12667,12680,12684,12694,12698,13425,13428,13432,13794,13810,13813,13817,13823,13827,13987,13993,13997,14441,14444,14454,14457,14460,14464,14483,14487,14802,14808,14812,15475,15490,15494,15507,15511,15773,15777,16096,16113,16117,16126,16203,16206,16209,16213,16216,16255,16258,16260,16266,16285,16294,16306,16312,16314,16316,16328,16336,16338,16358],[14,11657,1311],{"id":1310},[19,11659,11660,11662,11663,11665,11666,11668,11669,11672],{},[39,11661,339],{}," is a pure-Go, zero-dependency PDF library that handles CJK natively (no ",[44,11664,707],{}," dance), uses a 12-column grid instead of pixel-pushing with ",[44,11667,4266],{},", and runs roughly ",[39,11670,11671],{},"10× faster than gofpdf"," on the same workloads. The migration is mostly about replacing imperative cursor calls with declarative builders. This guide walks the mapping with five before/after pairs.",[19,11674,11675,11676,11679,11680,11682],{},"A teammate opened a fresh Go project last week, ran ",[44,11677,11678],{},"go get github.com/jung-kurt/gofpdf",", and pinged me ten minutes later with a screenshot of the GitHub banner: ",[39,11681,3947],{}," Then a follow-up: \"Wait, the fork is archived too?\"",[19,11684,11685],{},"Yes. Both of them.",[19,11687,11688,11690,11691,11694,11695,11697],{},[44,11689,3917],{}," was archived on ",[39,11692,11693],{},"September 8, 2021",". The community fork at ",[44,11696,3925],{}," shipped its last release in 2023 and was archived in 2025. The Go PDF library that two thirds of Stack Overflow answers still point to has been read-only for over four years, and the fork that was supposed to replace it is gone too.",[19,11699,11700],{},"If you have a gofpdf codebase in production, this post is a migration map. If you're starting a new project and reflexively reached for gofpdf because that's what the search results showed, this is the alternative.",[14,11702,11704],{"id":11703},"why-gofpdf-is-actually-staying-dead","Why gofpdf is actually staying dead",[19,11706,11707,11708,11710],{},"Open-source libraries don't always die. Sometimes the maintainer steps back and someone else picks it up. That's what most people assumed would happen with gofpdf — and for a while, it did. The community fork at ",[44,11709,3925],{}," reorganized the code, fixed a few long-standing bugs, accepted PRs, and felt like a genuine continuation.",[19,11712,11713,11714],{},"Then in early 2025 the fork was archived too. The README now reads, in part: ",[746,11715,11716],{},"\"This project is no longer actively maintained. Consider using a different library.\"",[19,11718,11719,11720,11723],{},"The reason matters less than the consequence: every Go project that depends on gofpdf is now sitting on ",[39,11721,11722],{},"two layers"," of unmaintained code. Security issues won't be patched. The PDF 2.0 spec landed in 2020 and gofpdf still doesn't support most of what changed. Go 1.25's loop variable semantics work fine with gofpdf today, but anything that breaks tomorrow is on you to fix in a fork.",[19,11725,11726],{},"This isn't a \"the library has bugs\" problem. It's a supply chain problem.",[14,11728,11730],{"id":11729},"what-people-actually-use-gofpdf-for","What people actually use gofpdf for",[19,11732,11733,11734,962,11736,11738],{},"Before getting into the mapping, it helps to be specific about the workloads that actually get migrated. From the issue trackers and Stack Overflow questions on both ",[44,11735,3917],{},[44,11737,3925],{},", the dominant uses are:",[33,11740,11741,11747,11753,11759],{},[36,11742,11743,11746],{},[39,11744,11745],{},"Invoices and receipts"," — header, customer block, line items table, totals, footer.",[36,11748,11749,11752],{},[39,11750,11751],{},"Reports"," — multi-page documents with repeating headers, page numbers, charts as images.",[36,11754,11755,11758],{},[39,11756,11757],{},"Forms and certificates"," — fixed-position text overlaid on a template.",[36,11760,11761,11764],{},[39,11762,11763],{},"CJK documents"," — invoices and shipping labels in Japanese, Chinese, Korean.",[19,11766,11767,11768,11770],{},"The first three are well-served by gpdf's builder API. The fourth — CJK — is where gpdf has the largest gap over gofpdf. gofpdf required you to call ",[44,11769,707],{},", manage a TTF file path, and hope your text didn't contain characters outside the basic plane. gpdf treats CJK as a first-class case: register a TrueType font, write Japanese, get a PDF.",[14,11772,11774],{"id":11773},"the-api-mapping-table","The API mapping table",[19,11776,11777],{},"The table below is the cheat sheet. Sections after it walk five concrete before/after pairs.",[1896,11779,11780,11791],{},[1899,11781,11782],{},[1902,11783,11784,11787,11789],{},[1905,11785,11786],{},"What you want to do",[1905,11788,5330],{},[1905,11790,339],{},[1912,11792,11793,11808,11829,11849,11866,11881,11899,11917,11932,11947,11965,11980,11995,12014,12029],{},[1902,11794,11795,11798,11803],{},[1917,11796,11797],{},"Create a document",[1917,11799,11800],{},[44,11801,11802],{},"gofpdf.New(\"P\", \"mm\", \"A4\", \"\")",[1917,11804,11805],{},[44,11806,11807],{},"gpdf.NewDocument(gpdf.WithPageSize(document.A4))",[1902,11809,11810,11813,11818],{},[1917,11811,11812],{},"Add a page",[1917,11814,11815],{},[44,11816,11817],{},"pdf.AddPage()",[1917,11819,11820,42,11823],{},[44,11821,11822],{},"doc.AddPage()",[746,11824,11825,11826,497],{},"(returns a ",[44,11827,11828],{},"*PageBuilder",[1902,11830,11831,11834,11839],{},[1917,11832,11833],{},"Set a font",[1917,11835,11836],{},[44,11837,11838],{},"pdf.SetFont(\"Arial\", \"B\", 16)",[1917,11840,11841,2563,11843,2563,11845,11848],{},[44,11842,9731],{},[44,11844,1325],{},[44,11846,11847],{},"template.FontSize(16)"," as text options",[1902,11850,11851,11854,11859],{},[1917,11852,11853],{},"Register a TTF (CJK)",[1917,11855,11856],{},[44,11857,11858],{},"pdf.AddUTF8Font(\"noto\", \"\", \"NotoSansJP-Regular.ttf\")",[1917,11860,11861,42,11863],{},[44,11862,5288],{},[746,11864,11865],{},"(at construction)",[1902,11867,11868,11871,11876],{},[1917,11869,11870],{},"Write a single line",[1917,11872,11873],{},[44,11874,11875],{},"pdf.Cell(40, 10, \"hi\")",[1917,11877,11878],{},[44,11879,11880],{},"c.Text(\"hi\")",[1902,11882,11883,11886,11891],{},[1917,11884,11885],{},"Write wrapped text",[1917,11887,11888],{},[44,11889,11890],{},"pdf.MultiCell(0, 10, body, \"\", \"L\", false)",[1917,11892,11893,42,11896],{},[44,11894,11895],{},"c.Text(body)",[746,11897,11898],{},"(wraps automatically)",[1902,11900,11901,11904,11909],{},[1917,11902,11903],{},"Set text color",[1917,11905,11906],{},[44,11907,11908],{},"pdf.SetTextColor(255, 0, 0)",[1917,11910,11911,42,11914],{},[44,11912,11913],{},"template.TextColor(pdf.Red)",[746,11915,11916],{},"(per-text option)",[1902,11918,11919,11922,11927],{},[1917,11920,11921],{},"Draw a horizontal rule",[1917,11923,11924],{},[44,11925,11926],{},"pdf.Line(x1, y1, x2, y2)",[1917,11928,11929],{},[44,11930,11931],{},"c.Line(template.LineThickness(document.Pt(1)))",[1902,11933,11934,11937,11942],{},[1917,11935,11936],{},"Embed an image",[1917,11938,11939],{},[44,11940,11941],{},"pdf.ImageOptions(\"logo.png\", x, y, w, h, ...)",[1917,11943,11944],{},[44,11945,11946],{},"c.Image(imgBytes, template.FitWidth(document.Mm(50)))",[1902,11948,11949,11952,11957],{},[1917,11950,11951],{},"Set XY cursor",[1917,11953,11954],{},[44,11955,11956],{},"pdf.SetXY(x, y)",[1917,11958,11959],{},[746,11960,11961,11962,497],{},"(no equivalent — use rows/cols, or ",[44,11963,11964],{},"page.Absolute(x, y, fn)",[1902,11966,11967,11970,11975],{},[1917,11968,11969],{},"Repeating header",[1917,11971,11972],{},[44,11973,11974],{},"pdf.SetHeaderFunc(fn)",[1917,11976,11977],{},[44,11978,11979],{},"doc.Header(fn)",[1902,11981,11982,11985,11990],{},[1917,11983,11984],{},"Repeating footer",[1917,11986,11987],{},[44,11988,11989],{},"pdf.SetFooterFunc(fn)",[1917,11991,11992],{},[44,11993,11994],{},"doc.Footer(fn)",[1902,11996,11997,12000,12006],{},[1917,11998,11999],{},"Page number",[1917,12001,12002,12003],{},"manual: ",[44,12004,12005],{},"pdf.PageNo()",[1917,12007,12008,11257,12011],{},[44,12009,12010],{},"c.PageNumber()",[44,12012,12013],{},"c.TotalPages()",[1902,12015,12016,12019,12024],{},[1917,12017,12018],{},"Output to file",[1917,12020,12021],{},[44,12022,12023],{},"pdf.OutputFileAndClose(\"out.pdf\")",[1917,12025,12026],{},[44,12027,12028],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[1902,12030,12031,12034,12039],{},[1917,12032,12033],{},"Output to writer",[1917,12035,12036],{},[44,12037,12038],{},"pdf.Output(w)",[1917,12040,12041],{},[44,12042,12043],{},"doc.Render(w)",[19,12045,12046,12047,12050,12051,12054,12055,60],{},"The shape change is the biggest thing: gofpdf is ",[39,12048,12049],{},"imperative",", gpdf is ",[39,12052,12053],{},"declarative",". In gofpdf you push a cursor around the page and write whatever it points at. In gpdf you describe a tree of rows and columns and let the layout engine place things. The first few snippets feel longer in gpdf. By the third you stop missing ",[44,12056,4266],{},[19,12058,12059,12060,2563,12063,2563,12066,12069,12070,2563,12073,2563,12076,2563,12079,12082,12083,12086],{},"A note on units. gofpdf lets you pick a base unit at construction (",[44,12061,12062],{},"\"mm\"",[44,12064,12065],{},"\"pt\"",[44,12067,12068],{},"\"in\"","). gpdf is always points internally and gives you helpers — ",[44,12071,12072],{},"document.Mm(20)",[44,12074,12075],{},"document.Pt(12)",[44,12077,12078],{},"document.Cm(1)",[44,12080,12081],{},"document.In(0.5)"," — for whichever you prefer at the call site. This is closer to CSS than to gofpdf, and once you have a header on every page using ",[44,12084,12085],{},"document.Mm(15)"," margins, you stop thinking about it.",[14,12088,12090],{"id":12089},"before-after-1-the-simplest-possible-pdf","Before / After 1: the simplest possible PDF",[19,12092,12093],{},"The \"hello world\" pair. gofpdf's brevity is what made it so quotable. gpdf's version is a few more lines because it's building a tree, not driving a cursor.",[19,12095,12096],{},[39,12097,12098],{},"Before — gofpdf:",[101,12100,12102],{"className":103,"code":12101,"language":105,"meta":106,"style":106},"package main\n\nimport \"github.com/jung-kurt/gofpdf\"\n\nfunc main() {\n    pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"B\", 24)\n    pdf.Cell(40, 10, \"Hello, World!\")\n    pdf.OutputFileAndClose(\"hello.pdf\")\n}\n",[44,12103,12104,12110,12114,12125,12129,12139,12182,12192,12223,12249,12268],{"__ignoreMap":106},[110,12105,12106,12108],{"class":112,"line":113},[110,12107,117],{"class":116},[110,12109,121],{"class":120},[110,12111,12112],{"class":112,"line":124},[110,12113,128],{"emptyLinePlaceholder":127},[110,12115,12116,12118,12120,12123],{"class":112,"line":131},[110,12117,135],{"class":134},[110,12119,4691],{"class":116},[110,12121,12122],{"class":120},"github.com/jung-kurt/gofpdf",[110,12124,150],{"class":116},[110,12126,12127],{"class":112,"line":141},[110,12128,128],{"emptyLinePlaceholder":127},[110,12130,12131,12133,12135,12137],{"class":112,"line":153},[110,12132,212],{"class":116},[110,12134,216],{"class":215},[110,12136,219],{"class":116},[110,12138,222],{"class":116},[110,12140,12141,12143,12145,12148,12150,12152,12154,12156,12158,12160,12162,12164,12166,12168,12170,12172,12174,12176,12178,12180],{"class":112,"line":163},[110,12142,4667],{"class":228},[110,12144,238],{"class":116},[110,12146,12147],{"class":228}," gofpdf",[110,12149,60],{"class":116},[110,12151,4677],{"class":215},[110,12153,249],{"class":116},[110,12155,252],{"class":116},[110,12157,4684],{"class":255},[110,12159,252],{"class":116},[110,12161,232],{"class":116},[110,12163,4691],{"class":116},[110,12165,4694],{"class":255},[110,12167,252],{"class":116},[110,12169,232],{"class":116},[110,12171,4691],{"class":116},[110,12173,344],{"class":255},[110,12175,252],{"class":116},[110,12177,232],{"class":116},[110,12179,4709],{"class":116},[110,12181,201],{"class":116},[110,12183,12184,12186,12188,12190],{"class":112,"line":168},[110,12185,4716],{"class":228},[110,12187,60],{"class":116},[110,12189,462],{"class":215},[110,12191,465],{"class":116},[110,12193,12194,12196,12198,12200,12202,12204,12206,12208,12210,12212,12214,12216,12218,12221],{"class":112,"line":178},[110,12195,4716],{"class":228},[110,12197,60],{"class":116},[110,12199,4731],{"class":215},[110,12201,249],{"class":116},[110,12203,252],{"class":116},[110,12205,4738],{"class":255},[110,12207,252],{"class":116},[110,12209,232],{"class":116},[110,12211,4691],{"class":116},[110,12213,4747],{"class":255},[110,12215,252],{"class":116},[110,12217,232],{"class":116},[110,12219,12220],{"class":381}," 24",[110,12222,201],{"class":116},[110,12224,12225,12227,12229,12231,12233,12235,12237,12239,12241,12243,12245,12247],{"class":112,"line":188},[110,12226,4716],{"class":228},[110,12228,60],{"class":116},[110,12230,4765],{"class":215},[110,12232,249],{"class":116},[110,12234,4770],{"class":381},[110,12236,232],{"class":116},[110,12238,4775],{"class":381},[110,12240,232],{"class":116},[110,12242,4691],{"class":116},[110,12244,4782],{"class":255},[110,12246,252],{"class":116},[110,12248,201],{"class":116},[110,12250,12251,12253,12255,12258,12260,12262,12264,12266],{"class":112,"line":198},[110,12252,4716],{"class":228},[110,12254,60],{"class":116},[110,12256,12257],{"class":215},"OutputFileAndClose",[110,12259,249],{"class":116},[110,12261,252],{"class":116},[110,12263,652],{"class":255},[110,12265,252],{"class":116},[110,12267,201],{"class":116},[110,12269,12270],{"class":112,"line":204},[110,12271,701],{"class":116},[19,12273,12274],{},[39,12275,4900],{},[101,12277,12279],{"className":103,"code":12278,"language":105,"meta":106,"style":106},"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    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Hello, World!\", template.FontSize(24), template.Bold())\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",[44,12280,12281,12287,12291,12297,12305,12313,12317,12325,12333,12341,12345,12349,12359,12373,12391,12421,12425,12429,12443,12467,12497,12535,12539,12543,12547,12565,12577,12591,12595,12635,12649,12653],{"__ignoreMap":106},[110,12282,12283,12285],{"class":112,"line":113},[110,12284,117],{"class":116},[110,12286,121],{"class":120},[110,12288,12289],{"class":112,"line":124},[110,12290,128],{"emptyLinePlaceholder":127},[110,12292,12293,12295],{"class":112,"line":131},[110,12294,135],{"class":134},[110,12296,138],{"class":116},[110,12298,12299,12301,12303],{"class":112,"line":141},[110,12300,144],{"class":116},[110,12302,147],{"class":120},[110,12304,150],{"class":116},[110,12306,12307,12309,12311],{"class":112,"line":153},[110,12308,144],{"class":116},[110,12310,158],{"class":120},[110,12312,150],{"class":116},[110,12314,12315],{"class":112,"line":163},[110,12316,128],{"emptyLinePlaceholder":127},[110,12318,12319,12321,12323],{"class":112,"line":168},[110,12320,144],{"class":116},[110,12322,173],{"class":120},[110,12324,150],{"class":116},[110,12326,12327,12329,12331],{"class":112,"line":178},[110,12328,144],{"class":116},[110,12330,183],{"class":120},[110,12332,150],{"class":116},[110,12334,12335,12337,12339],{"class":112,"line":188},[110,12336,144],{"class":116},[110,12338,193],{"class":120},[110,12340,150],{"class":116},[110,12342,12343],{"class":112,"line":198},[110,12344,201],{"class":116},[110,12346,12347],{"class":112,"line":204},[110,12348,128],{"emptyLinePlaceholder":127},[110,12350,12351,12353,12355,12357],{"class":112,"line":209},[110,12352,212],{"class":116},[110,12354,216],{"class":215},[110,12356,219],{"class":116},[110,12358,222],{"class":116},[110,12360,12361,12363,12365,12367,12369,12371],{"class":112,"line":225},[110,12362,310],{"class":228},[110,12364,238],{"class":116},[110,12366,315],{"class":228},[110,12368,60],{"class":116},[110,12370,320],{"class":215},[110,12372,323],{"class":116},[110,12374,12375,12377,12379,12381,12383,12385,12387,12389],{"class":112,"line":262},[110,12376,329],{"class":228},[110,12378,60],{"class":116},[110,12380,334],{"class":215},[110,12382,249],{"class":116},[110,12384,362],{"class":228},[110,12386,60],{"class":116},[110,12388,344],{"class":228},[110,12390,347],{"class":116},[110,12392,12393,12395,12397,12399,12401,12403,12405,12407,12409,12411,12413,12415,12417,12419],{"class":112,"line":278},[110,12394,329],{"class":228},[110,12396,60],{"class":116},[110,12398,357],{"class":215},[110,12400,249],{"class":116},[110,12402,362],{"class":228},[110,12404,60],{"class":116},[110,12406,367],{"class":215},[110,12408,249],{"class":116},[110,12410,362],{"class":228},[110,12412,60],{"class":116},[110,12414,376],{"class":215},[110,12416,249],{"class":116},[110,12418,382],{"class":381},[110,12420,385],{"class":116},[110,12422,12423],{"class":112,"line":296},[110,12424,441],{"class":116},[110,12426,12427],{"class":112,"line":302},[110,12428,128],{"emptyLinePlaceholder":127},[110,12430,12431,12433,12435,12437,12439,12441],{"class":112,"line":307},[110,12432,452],{"class":228},[110,12434,238],{"class":116},[110,12436,457],{"class":228},[110,12438,60],{"class":116},[110,12440,462],{"class":215},[110,12442,465],{"class":116},[110,12444,12445,12447,12449,12451,12453,12455,12457,12459,12461,12463,12465],{"class":112,"line":326},[110,12446,471],{"class":228},[110,12448,60],{"class":116},[110,12450,476],{"class":215},[110,12452,479],{"class":116},[110,12454,483],{"class":482},[110,12456,486],{"class":116},[110,12458,489],{"class":120},[110,12460,60],{"class":116},[110,12462,494],{"class":120},[110,12464,497],{"class":116},[110,12466,222],{"class":116},[110,12468,12469,12471,12473,12475,12477,12479,12481,12483,12485,12487,12489,12491,12493,12495],{"class":112,"line":350},[110,12470,505],{"class":228},[110,12472,60],{"class":116},[110,12474,510],{"class":215},[110,12476,249],{"class":116},[110,12478,515],{"class":381},[110,12480,232],{"class":116},[110,12482,520],{"class":116},[110,12484,523],{"class":482},[110,12486,486],{"class":116},[110,12488,489],{"class":120},[110,12490,60],{"class":116},[110,12492,532],{"class":120},[110,12494,497],{"class":116},[110,12496,222],{"class":116},[110,12498,12499,12501,12503,12505,12507,12509,12511,12513,12515,12517,12519,12521,12523,12525,12527,12529,12531,12533],{"class":112,"line":388},[110,12500,542],{"class":228},[110,12502,60],{"class":116},[110,12504,547],{"class":215},[110,12506,249],{"class":116},[110,12508,252],{"class":116},[110,12510,4782],{"class":255},[110,12512,252],{"class":116},[110,12514,232],{"class":116},[110,12516,1680],{"class":228},[110,12518,60],{"class":116},[110,12520,1685],{"class":215},[110,12522,249],{"class":116},[110,12524,1690],{"class":381},[110,12526,1693],{"class":116},[110,12528,1680],{"class":228},[110,12530,60],{"class":116},[110,12532,1700],{"class":215},[110,12534,1703],{"class":116},[110,12536,12537],{"class":112,"line":413},[110,12538,564],{"class":116},[110,12540,12541],{"class":112,"line":438},[110,12542,570],{"class":116},[110,12544,12545],{"class":112,"line":444},[110,12546,128],{"emptyLinePlaceholder":127},[110,12548,12549,12551,12553,12555,12557,12559,12561,12563],{"class":112,"line":449},[110,12550,581],{"class":228},[110,12552,232],{"class":116},[110,12554,235],{"class":228},[110,12556,238],{"class":116},[110,12558,457],{"class":228},[110,12560,60],{"class":116},[110,12562,594],{"class":215},[110,12564,465],{"class":116},[110,12566,12567,12569,12571,12573,12575],{"class":112,"line":468},[110,12568,265],{"class":134},[110,12570,235],{"class":228},[110,12572,270],{"class":116},[110,12574,273],{"class":116},[110,12576,222],{"class":116},[110,12578,12579,12581,12583,12585,12587,12589],{"class":112,"line":502},[110,12580,281],{"class":228},[110,12582,60],{"class":116},[110,12584,286],{"class":215},[110,12586,249],{"class":116},[110,12588,291],{"class":228},[110,12590,201],{"class":116},[110,12592,12593],{"class":112,"line":539},[110,12594,299],{"class":116},[110,12596,12597,12599,12601,12603,12605,12607,12609,12611,12613,12615,12617,12619,12621,12623,12625,12627,12629,12631,12633],{"class":112,"line":561},[110,12598,265],{"class":134},[110,12600,235],{"class":228},[110,12602,238],{"class":116},[110,12604,241],{"class":228},[110,12606,60],{"class":116},[110,12608,645],{"class":215},[110,12610,249],{"class":116},[110,12612,252],{"class":116},[110,12614,652],{"class":255},[110,12616,252],{"class":116},[110,12618,232],{"class":116},[110,12620,659],{"class":228},[110,12622,232],{"class":116},[110,12624,664],{"class":381},[110,12626,667],{"class":116},[110,12628,235],{"class":228},[110,12630,270],{"class":116},[110,12632,273],{"class":116},[110,12634,222],{"class":116},[110,12636,12637,12639,12641,12643,12645,12647],{"class":112,"line":567},[110,12638,281],{"class":228},[110,12640,60],{"class":116},[110,12642,286],{"class":215},[110,12644,249],{"class":116},[110,12646,291],{"class":228},[110,12648,201],{"class":116},[110,12650,12651],{"class":112,"line":573},[110,12652,299],{"class":116},[110,12654,12655],{"class":112,"line":578},[110,12656,701],{"class":116},[19,12658,12659,12660,12662,12663,12666],{},"The grid does the work. ",[44,12661,476],{}," adds a row whose height is determined by its content; ",[44,12664,12665],{},"r.Col(12, ...)"," says \"this column spans all 12 grid columns.\" Same idea as Bootstrap, applied to a PDF page.",[19,12668,12669,12671,12672,12675,12676,12679],{},[44,12670,594],{}," returns the bytes; ",[44,12673,12674],{},"Render(w)"," streams to an ",[44,12677,12678],{},"io.Writer"," if you'd rather not allocate. There's no \"close the file\" step because gpdf doesn't own a file handle.",[14,12681,12683],{"id":12682},"before-after-2-a-table-of-line-items","Before / After 2: a table of line items",[19,12685,12686,12687,12689,12690,12693],{},"Tables are where gofpdf gets verbose. There's no built-in table; you call ",[44,12688,4765],{}," in nested loops, manage your own column widths, and do ",[44,12691,12692],{},"Ln(-1)"," to move to the next row. Half the gofpdf invoice tutorials on the internet are mostly table boilerplate.",[19,12695,12696],{},[39,12697,12098],{},[101,12699,12701],{"className":103,"code":12700,"language":105,"meta":106,"style":106},"pdf.SetFont(\"Arial\", \"B\", 11)\npdf.SetFillColor(220, 220, 220)\npdf.CellFormat(80, 8, \"Description\", \"1\", 0, \"L\", true, 0, \"\")\npdf.CellFormat(20, 8, \"Qty\",         \"1\", 0, \"C\", true, 0, \"\")\npdf.CellFormat(30, 8, \"Unit\",        \"1\", 0, \"R\", true, 0, \"\")\npdf.CellFormat(30, 8, \"Amount\",      \"1\", 1, \"R\", true, 0, \"\")\n\npdf.SetFont(\"Arial\", \"\", 11)\nitems := [][]string{\n    {\"Frontend dev\", \"40 hrs\", \"$150.00\", \"$6,000.00\"},\n    {\"Backend dev\",  \"60 hrs\", \"$150.00\", \"$9,000.00\"},\n    {\"UI design\",    \"20 hrs\", \"$120.00\", \"$2,400.00\"},\n}\nfor _, row := range items {\n    pdf.CellFormat(80, 8, row[0], \"1\", 0, \"L\", false, 0, \"\")\n    pdf.CellFormat(20, 8, row[1], \"1\", 0, \"C\", false, 0, \"\")\n    pdf.CellFormat(30, 8, row[2], \"1\", 0, \"R\", false, 0, \"\")\n    pdf.CellFormat(30, 8, row[3], \"1\", 1, \"R\", false, 0, \"\")\n}\n",[44,12702,12703,12733,12758,12824,12884,12944,13004,13008,13034,13048,13085,13121,13157,13161,13183,13245,13303,13362,13421],{"__ignoreMap":106},[110,12704,12705,12707,12709,12711,12713,12715,12717,12719,12721,12723,12725,12727,12729,12731],{"class":112,"line":113},[110,12706,8613],{"class":228},[110,12708,60],{"class":116},[110,12710,4731],{"class":215},[110,12712,249],{"class":116},[110,12714,252],{"class":116},[110,12716,4738],{"class":255},[110,12718,252],{"class":116},[110,12720,232],{"class":116},[110,12722,4691],{"class":116},[110,12724,4747],{"class":255},[110,12726,252],{"class":116},[110,12728,232],{"class":116},[110,12730,6259],{"class":381},[110,12732,201],{"class":116},[110,12734,12735,12737,12739,12742,12744,12747,12749,12752,12754,12756],{"class":112,"line":124},[110,12736,8613],{"class":228},[110,12738,60],{"class":116},[110,12740,12741],{"class":215},"SetFillColor",[110,12743,249],{"class":116},[110,12745,12746],{"class":381},"220",[110,12748,232],{"class":116},[110,12750,12751],{"class":381}," 220",[110,12753,232],{"class":116},[110,12755,12751],{"class":381},[110,12757,201],{"class":116},[110,12759,12760,12762,12764,12767,12769,12772,12774,12777,12779,12781,12783,12785,12787,12789,12792,12794,12796,12799,12801,12803,12806,12808,12810,12814,12816,12818,12820,12822],{"class":112,"line":131},[110,12761,8613],{"class":228},[110,12763,60],{"class":116},[110,12765,12766],{"class":215},"CellFormat",[110,12768,249],{"class":116},[110,12770,12771],{"class":381},"80",[110,12773,232],{"class":116},[110,12775,12776],{"class":381}," 8",[110,12778,232],{"class":116},[110,12780,4691],{"class":116},[110,12782,8377],{"class":255},[110,12784,252],{"class":116},[110,12786,232],{"class":116},[110,12788,4691],{"class":116},[110,12790,12791],{"class":255},"1",[110,12793,252],{"class":116},[110,12795,232],{"class":116},[110,12797,12798],{"class":381}," 0",[110,12800,232],{"class":116},[110,12802,4691],{"class":116},[110,12804,12805],{"class":255},"L",[110,12807,252],{"class":116},[110,12809,232],{"class":116},[110,12811,12813],{"class":12812},"sfNiH"," true",[110,12815,232],{"class":116},[110,12817,12798],{"class":381},[110,12819,232],{"class":116},[110,12821,4709],{"class":116},[110,12823,201],{"class":116},[110,12825,12826,12828,12830,12832,12834,12836,12838,12840,12842,12844,12846,12848,12850,12853,12855,12857,12859,12861,12863,12865,12868,12870,12872,12874,12876,12878,12880,12882],{"class":112,"line":141},[110,12827,8613],{"class":228},[110,12829,60],{"class":116},[110,12831,12766],{"class":215},[110,12833,249],{"class":116},[110,12835,382],{"class":381},[110,12837,232],{"class":116},[110,12839,12776],{"class":381},[110,12841,232],{"class":116},[110,12843,4691],{"class":116},[110,12845,8386],{"class":255},[110,12847,252],{"class":116},[110,12849,232],{"class":116},[110,12851,12852],{"class":116},"         \"",[110,12854,12791],{"class":255},[110,12856,252],{"class":116},[110,12858,232],{"class":116},[110,12860,12798],{"class":381},[110,12862,232],{"class":116},[110,12864,4691],{"class":116},[110,12866,12867],{"class":255},"C",[110,12869,252],{"class":116},[110,12871,232],{"class":116},[110,12873,12813],{"class":12812},[110,12875,232],{"class":116},[110,12877,12798],{"class":381},[110,12879,232],{"class":116},[110,12881,4709],{"class":116},[110,12883,201],{"class":116},[110,12885,12886,12888,12890,12892,12894,12896,12898,12900,12902,12904,12906,12908,12910,12913,12915,12917,12919,12921,12923,12925,12928,12930,12932,12934,12936,12938,12940,12942],{"class":112,"line":153},[110,12887,8613],{"class":228},[110,12889,60],{"class":116},[110,12891,12766],{"class":215},[110,12893,249],{"class":116},[110,12895,3654],{"class":381},[110,12897,232],{"class":116},[110,12899,12776],{"class":381},[110,12901,232],{"class":116},[110,12903,4691],{"class":116},[110,12905,8395],{"class":255},[110,12907,252],{"class":116},[110,12909,232],{"class":116},[110,12911,12912],{"class":116},"        \"",[110,12914,12791],{"class":255},[110,12916,252],{"class":116},[110,12918,232],{"class":116},[110,12920,12798],{"class":381},[110,12922,232],{"class":116},[110,12924,4691],{"class":116},[110,12926,12927],{"class":255},"R",[110,12929,252],{"class":116},[110,12931,232],{"class":116},[110,12933,12813],{"class":12812},[110,12935,232],{"class":116},[110,12937,12798],{"class":381},[110,12939,232],{"class":116},[110,12941,4709],{"class":116},[110,12943,201],{"class":116},[110,12945,12946,12948,12950,12952,12954,12956,12958,12960,12962,12964,12966,12968,12970,12973,12975,12977,12979,12982,12984,12986,12988,12990,12992,12994,12996,12998,13000,13002],{"class":112,"line":163},[110,12947,8613],{"class":228},[110,12949,60],{"class":116},[110,12951,12766],{"class":215},[110,12953,249],{"class":116},[110,12955,3654],{"class":381},[110,12957,232],{"class":116},[110,12959,12776],{"class":381},[110,12961,232],{"class":116},[110,12963,4691],{"class":116},[110,12965,8404],{"class":255},[110,12967,252],{"class":116},[110,12969,232],{"class":116},[110,12971,12972],{"class":116},"      \"",[110,12974,12791],{"class":255},[110,12976,252],{"class":116},[110,12978,232],{"class":116},[110,12980,12981],{"class":381}," 1",[110,12983,232],{"class":116},[110,12985,4691],{"class":116},[110,12987,12927],{"class":255},[110,12989,252],{"class":116},[110,12991,232],{"class":116},[110,12993,12813],{"class":12812},[110,12995,232],{"class":116},[110,12997,12798],{"class":381},[110,12999,232],{"class":116},[110,13001,4709],{"class":116},[110,13003,201],{"class":116},[110,13005,13006],{"class":112,"line":168},[110,13007,128],{"emptyLinePlaceholder":127},[110,13009,13010,13012,13014,13016,13018,13020,13022,13024,13026,13028,13030,13032],{"class":112,"line":178},[110,13011,8613],{"class":228},[110,13013,60],{"class":116},[110,13015,4731],{"class":215},[110,13017,249],{"class":116},[110,13019,252],{"class":116},[110,13021,4738],{"class":255},[110,13023,252],{"class":116},[110,13025,232],{"class":116},[110,13027,4709],{"class":116},[110,13029,232],{"class":116},[110,13031,6259],{"class":381},[110,13033,201],{"class":116},[110,13035,13036,13039,13041,13044,13046],{"class":112,"line":188},[110,13037,13038],{"class":228},"items ",[110,13040,238],{"class":116},[110,13042,13043],{"class":116}," [][]",[110,13045,1120],{"class":8369},[110,13047,8419],{"class":116},[110,13049,13050,13053,13055,13057,13059,13061,13063,13065,13067,13069,13071,13073,13075,13077,13079,13081,13083],{"class":112,"line":198},[110,13051,13052],{"class":116},"    {",[110,13054,252],{"class":116},[110,13056,8429],{"class":255},[110,13058,252],{"class":116},[110,13060,232],{"class":116},[110,13062,4691],{"class":116},[110,13064,8438],{"class":255},[110,13066,252],{"class":116},[110,13068,232],{"class":116},[110,13070,4691],{"class":116},[110,13072,8447],{"class":255},[110,13074,252],{"class":116},[110,13076,232],{"class":116},[110,13078,4691],{"class":116},[110,13080,8456],{"class":255},[110,13082,252],{"class":116},[110,13084,8409],{"class":116},[110,13086,13087,13089,13091,13093,13095,13097,13099,13101,13103,13105,13107,13109,13111,13113,13115,13117,13119],{"class":112,"line":204},[110,13088,13052],{"class":116},[110,13090,252],{"class":116},[110,13092,8469],{"class":255},[110,13094,252],{"class":116},[110,13096,232],{"class":116},[110,13098,8476],{"class":116},[110,13100,8479],{"class":255},[110,13102,252],{"class":116},[110,13104,232],{"class":116},[110,13106,4691],{"class":116},[110,13108,8447],{"class":255},[110,13110,252],{"class":116},[110,13112,232],{"class":116},[110,13114,4691],{"class":116},[110,13116,8496],{"class":255},[110,13118,252],{"class":116},[110,13120,8409],{"class":116},[110,13122,13123,13125,13127,13129,13131,13133,13135,13137,13139,13141,13143,13145,13147,13149,13151,13153,13155],{"class":112,"line":209},[110,13124,13052],{"class":116},[110,13126,252],{"class":116},[110,13128,8509],{"class":255},[110,13130,252],{"class":116},[110,13132,232],{"class":116},[110,13134,144],{"class":116},[110,13136,8518],{"class":255},[110,13138,252],{"class":116},[110,13140,232],{"class":116},[110,13142,4691],{"class":116},[110,13144,8527],{"class":255},[110,13146,252],{"class":116},[110,13148,232],{"class":116},[110,13150,4691],{"class":116},[110,13152,8536],{"class":255},[110,13154,252],{"class":116},[110,13156,8409],{"class":116},[110,13158,13159],{"class":112,"line":225},[110,13160,701],{"class":116},[110,13162,13163,13165,13168,13170,13173,13175,13178,13181],{"class":112,"line":262},[110,13164,5518],{"class":134},[110,13166,13167],{"class":228}," _",[110,13169,232],{"class":116},[110,13171,13172],{"class":228}," row ",[110,13174,238],{"class":116},[110,13176,13177],{"class":134}," range",[110,13179,13180],{"class":228}," items ",[110,13182,8419],{"class":116},[110,13184,13185,13187,13189,13191,13193,13195,13197,13199,13201,13204,13207,13209,13212,13214,13216,13218,13220,13222,13224,13226,13228,13230,13232,13235,13237,13239,13241,13243],{"class":112,"line":278},[110,13186,4716],{"class":228},[110,13188,60],{"class":116},[110,13190,12766],{"class":215},[110,13192,249],{"class":116},[110,13194,12771],{"class":381},[110,13196,232],{"class":116},[110,13198,12776],{"class":381},[110,13200,232],{"class":116},[110,13202,13203],{"class":228}," row",[110,13205,13206],{"class":116},"[",[110,13208,7467],{"class":381},[110,13210,13211],{"class":116},"],",[110,13213,4691],{"class":116},[110,13215,12791],{"class":255},[110,13217,252],{"class":116},[110,13219,232],{"class":116},[110,13221,12798],{"class":381},[110,13223,232],{"class":116},[110,13225,4691],{"class":116},[110,13227,12805],{"class":255},[110,13229,252],{"class":116},[110,13231,232],{"class":116},[110,13233,13234],{"class":12812}," false",[110,13236,232],{"class":116},[110,13238,12798],{"class":381},[110,13240,232],{"class":116},[110,13242,4709],{"class":116},[110,13244,201],{"class":116},[110,13246,13247,13249,13251,13253,13255,13257,13259,13261,13263,13265,13267,13269,13271,13273,13275,13277,13279,13281,13283,13285,13287,13289,13291,13293,13295,13297,13299,13301],{"class":112,"line":296},[110,13248,4716],{"class":228},[110,13250,60],{"class":116},[110,13252,12766],{"class":215},[110,13254,249],{"class":116},[110,13256,382],{"class":381},[110,13258,232],{"class":116},[110,13260,12776],{"class":381},[110,13262,232],{"class":116},[110,13264,13203],{"class":228},[110,13266,13206],{"class":116},[110,13268,12791],{"class":381},[110,13270,13211],{"class":116},[110,13272,4691],{"class":116},[110,13274,12791],{"class":255},[110,13276,252],{"class":116},[110,13278,232],{"class":116},[110,13280,12798],{"class":381},[110,13282,232],{"class":116},[110,13284,4691],{"class":116},[110,13286,12867],{"class":255},[110,13288,252],{"class":116},[110,13290,232],{"class":116},[110,13292,13234],{"class":12812},[110,13294,232],{"class":116},[110,13296,12798],{"class":381},[110,13298,232],{"class":116},[110,13300,4709],{"class":116},[110,13302,201],{"class":116},[110,13304,13305,13307,13309,13311,13313,13315,13317,13319,13321,13323,13325,13328,13330,13332,13334,13336,13338,13340,13342,13344,13346,13348,13350,13352,13354,13356,13358,13360],{"class":112,"line":302},[110,13306,4716],{"class":228},[110,13308,60],{"class":116},[110,13310,12766],{"class":215},[110,13312,249],{"class":116},[110,13314,3654],{"class":381},[110,13316,232],{"class":116},[110,13318,12776],{"class":381},[110,13320,232],{"class":116},[110,13322,13203],{"class":228},[110,13324,13206],{"class":116},[110,13326,13327],{"class":381},"2",[110,13329,13211],{"class":116},[110,13331,4691],{"class":116},[110,13333,12791],{"class":255},[110,13335,252],{"class":116},[110,13337,232],{"class":116},[110,13339,12798],{"class":381},[110,13341,232],{"class":116},[110,13343,4691],{"class":116},[110,13345,12927],{"class":255},[110,13347,252],{"class":116},[110,13349,232],{"class":116},[110,13351,13234],{"class":12812},[110,13353,232],{"class":116},[110,13355,12798],{"class":381},[110,13357,232],{"class":116},[110,13359,4709],{"class":116},[110,13361,201],{"class":116},[110,13363,13364,13366,13368,13370,13372,13374,13376,13378,13380,13382,13384,13387,13389,13391,13393,13395,13397,13399,13401,13403,13405,13407,13409,13411,13413,13415,13417,13419],{"class":112,"line":307},[110,13365,4716],{"class":228},[110,13367,60],{"class":116},[110,13369,12766],{"class":215},[110,13371,249],{"class":116},[110,13373,3654],{"class":381},[110,13375,232],{"class":116},[110,13377,12776],{"class":381},[110,13379,232],{"class":116},[110,13381,13203],{"class":228},[110,13383,13206],{"class":116},[110,13385,13386],{"class":381},"3",[110,13388,13211],{"class":116},[110,13390,4691],{"class":116},[110,13392,12791],{"class":255},[110,13394,252],{"class":116},[110,13396,232],{"class":116},[110,13398,12981],{"class":381},[110,13400,232],{"class":116},[110,13402,4691],{"class":116},[110,13404,12927],{"class":255},[110,13406,252],{"class":116},[110,13408,232],{"class":116},[110,13410,13234],{"class":12812},[110,13412,232],{"class":116},[110,13414,12798],{"class":381},[110,13416,232],{"class":116},[110,13418,4709],{"class":116},[110,13420,201],{"class":116},[110,13422,13423],{"class":112,"line":326},[110,13424,701],{"class":116},[19,13426,13427],{},"Counts widths in your head, and good luck if a description wraps.",[19,13429,13430],{},[39,13431,4900],{},[101,13433,13435],{"className":103,"code":13434,"language":105,"meta":106,"style":106},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"},\n            [][]string{\n                {\"Frontend dev\", \"40 hrs\", \"$150.00\", \"$6,000.00\"},\n                {\"Backend dev\",  \"60 hrs\", \"$150.00\", \"$9,000.00\"},\n                {\"UI design\",    \"20 hrs\", \"$120.00\", \"$2,400.00\"},\n            },\n            template.ColumnWidths(50, 15, 15, 20),\n            template.TableHeaderStyle(\n                template.Bold(),\n                template.TextColor(pdf.White),\n                template.BgColor(pdf.RGBHex(0x1A237E)),\n            ),\n            template.TableStripe(pdf.RGBHex(0xF5F5F5)),\n        )\n    })\n})\n",[44,13436,13437,13461,13491,13501,13542,13551,13588,13624,13660,13665,13692,13702,13712,13730,13752,13757,13781,13786,13790],{"__ignoreMap":106},[110,13438,13439,13441,13443,13445,13447,13449,13451,13453,13455,13457,13459],{"class":112,"line":113},[110,13440,853],{"class":228},[110,13442,60],{"class":116},[110,13444,476],{"class":215},[110,13446,479],{"class":116},[110,13448,483],{"class":482},[110,13450,486],{"class":116},[110,13452,489],{"class":120},[110,13454,60],{"class":116},[110,13456,494],{"class":120},[110,13458,497],{"class":116},[110,13460,222],{"class":116},[110,13462,13463,13465,13467,13469,13471,13473,13475,13477,13479,13481,13483,13485,13487,13489],{"class":112,"line":124},[110,13464,878],{"class":228},[110,13466,60],{"class":116},[110,13468,510],{"class":215},[110,13470,249],{"class":116},[110,13472,515],{"class":381},[110,13474,232],{"class":116},[110,13476,520],{"class":116},[110,13478,523],{"class":482},[110,13480,486],{"class":116},[110,13482,489],{"class":120},[110,13484,60],{"class":116},[110,13486,532],{"class":120},[110,13488,497],{"class":116},[110,13490,222],{"class":116},[110,13492,13493,13495,13497,13499],{"class":112,"line":131},[110,13494,909],{"class":228},[110,13496,60],{"class":116},[110,13498,8359],{"class":215},[110,13500,323],{"class":116},[110,13502,13503,13506,13508,13510,13512,13514,13516,13518,13520,13522,13524,13526,13528,13530,13532,13534,13536,13538,13540],{"class":112,"line":141},[110,13504,13505],{"class":116},"            []",[110,13507,1120],{"class":8369},[110,13509,8372],{"class":116},[110,13511,252],{"class":116},[110,13513,8377],{"class":255},[110,13515,252],{"class":116},[110,13517,232],{"class":116},[110,13519,4691],{"class":116},[110,13521,8386],{"class":255},[110,13523,252],{"class":116},[110,13525,232],{"class":116},[110,13527,4691],{"class":116},[110,13529,8395],{"class":255},[110,13531,252],{"class":116},[110,13533,232],{"class":116},[110,13535,4691],{"class":116},[110,13537,8404],{"class":255},[110,13539,252],{"class":116},[110,13541,8409],{"class":116},[110,13543,13544,13547,13549],{"class":112,"line":153},[110,13545,13546],{"class":116},"            [][]",[110,13548,1120],{"class":8369},[110,13550,8419],{"class":116},[110,13552,13553,13556,13558,13560,13562,13564,13566,13568,13570,13572,13574,13576,13578,13580,13582,13584,13586],{"class":112,"line":163},[110,13554,13555],{"class":116},"                {",[110,13557,252],{"class":116},[110,13559,8429],{"class":255},[110,13561,252],{"class":116},[110,13563,232],{"class":116},[110,13565,4691],{"class":116},[110,13567,8438],{"class":255},[110,13569,252],{"class":116},[110,13571,232],{"class":116},[110,13573,4691],{"class":116},[110,13575,8447],{"class":255},[110,13577,252],{"class":116},[110,13579,232],{"class":116},[110,13581,4691],{"class":116},[110,13583,8456],{"class":255},[110,13585,252],{"class":116},[110,13587,8409],{"class":116},[110,13589,13590,13592,13594,13596,13598,13600,13602,13604,13606,13608,13610,13612,13614,13616,13618,13620,13622],{"class":112,"line":168},[110,13591,13555],{"class":116},[110,13593,252],{"class":116},[110,13595,8469],{"class":255},[110,13597,252],{"class":116},[110,13599,232],{"class":116},[110,13601,8476],{"class":116},[110,13603,8479],{"class":255},[110,13605,252],{"class":116},[110,13607,232],{"class":116},[110,13609,4691],{"class":116},[110,13611,8447],{"class":255},[110,13613,252],{"class":116},[110,13615,232],{"class":116},[110,13617,4691],{"class":116},[110,13619,8496],{"class":255},[110,13621,252],{"class":116},[110,13623,8409],{"class":116},[110,13625,13626,13628,13630,13632,13634,13636,13638,13640,13642,13644,13646,13648,13650,13652,13654,13656,13658],{"class":112,"line":178},[110,13627,13555],{"class":116},[110,13629,252],{"class":116},[110,13631,8509],{"class":255},[110,13633,252],{"class":116},[110,13635,232],{"class":116},[110,13637,144],{"class":116},[110,13639,8518],{"class":255},[110,13641,252],{"class":116},[110,13643,232],{"class":116},[110,13645,4691],{"class":116},[110,13647,8527],{"class":255},[110,13649,252],{"class":116},[110,13651,232],{"class":116},[110,13653,4691],{"class":116},[110,13655,8536],{"class":255},[110,13657,252],{"class":116},[110,13659,8409],{"class":116},[110,13661,13662],{"class":112,"line":188},[110,13663,13664],{"class":116},"            },\n",[110,13666,13667,13670,13672,13674,13676,13678,13680,13682,13684,13686,13688,13690],{"class":112,"line":198},[110,13668,13669],{"class":228},"            template",[110,13671,60],{"class":116},[110,13673,8555],{"class":215},[110,13675,249],{"class":116},[110,13677,8560],{"class":381},[110,13679,232],{"class":116},[110,13681,8565],{"class":381},[110,13683,232],{"class":116},[110,13685,8565],{"class":381},[110,13687,232],{"class":116},[110,13689,8574],{"class":381},[110,13691,347],{"class":116},[110,13693,13694,13696,13698,13700],{"class":112,"line":204},[110,13695,13669],{"class":228},[110,13697,60],{"class":116},[110,13699,8585],{"class":215},[110,13701,323],{"class":116},[110,13703,13704,13706,13708,13710],{"class":112,"line":209},[110,13705,8550],{"class":228},[110,13707,60],{"class":116},[110,13709,1700],{"class":215},[110,13711,8599],{"class":116},[110,13713,13714,13716,13718,13720,13722,13724,13726,13728],{"class":112,"line":225},[110,13715,8550],{"class":228},[110,13717,60],{"class":116},[110,13719,8608],{"class":215},[110,13721,249],{"class":116},[110,13723,8613],{"class":228},[110,13725,60],{"class":116},[110,13727,8618],{"class":228},[110,13729,347],{"class":116},[110,13731,13732,13734,13736,13738,13740,13742,13744,13746,13748,13750],{"class":112,"line":262},[110,13733,8550],{"class":228},[110,13735,60],{"class":116},[110,13737,8629],{"class":215},[110,13739,249],{"class":116},[110,13741,8613],{"class":228},[110,13743,60],{"class":116},[110,13745,8638],{"class":215},[110,13747,249],{"class":116},[110,13749,8643],{"class":381},[110,13751,8646],{"class":116},[110,13753,13754],{"class":112,"line":278},[110,13755,13756],{"class":116},"            ),\n",[110,13758,13759,13761,13763,13766,13768,13770,13772,13774,13776,13779],{"class":112,"line":296},[110,13760,13669],{"class":228},[110,13762,60],{"class":116},[110,13764,13765],{"class":215},"TableStripe",[110,13767,249],{"class":116},[110,13769,8613],{"class":228},[110,13771,60],{"class":116},[110,13773,8638],{"class":215},[110,13775,249],{"class":116},[110,13777,13778],{"class":381},"0xF5F5F5",[110,13780,8646],{"class":116},[110,13782,13783],{"class":112,"line":302},[110,13784,13785],{"class":116},"        )\n",[110,13787,13788],{"class":112,"line":307},[110,13789,570],{"class":116},[110,13791,13792],{"class":112,"line":326},[110,13793,936],{"class":116},[19,13795,13796,4276,13799,13802,13803,13806,13807,13809],{},[44,13797,13798],{},"ColumnWidths(50, 15, 15, 20)",[39,13800,13801],{},"percentages of the column the table lives in",", not absolute millimeters. Drop the table into a ",[44,13804,13805],{},"r.Col(6, ...)"," and the same percentages still work. That's the kind of thing you can't get out of ",[44,13808,12766],{}," without a wrapper.",[19,13811,13812],{},"Wrapping is automatic. Page breaks are automatic — if the table runs past the bottom margin, the header repeats on the next page.",[14,13814,13816],{"id":13815},"before-after-3-japanese-text-without-the-dance","Before / After 3: Japanese text without the dance",[19,13818,13819,13820,13822],{},"This is the one that pushed me off gofpdf. To render Japanese in gofpdf you call ",[44,13821,707],{},", point it at a TTF on disk, set the font, and pray. Subsetting works most of the time. Some TTFs trigger glyph-id collisions and emit garbled output. The error messages don't help.",[19,13824,13825],{},[39,13826,12098],{},[101,13828,13830],{"className":103,"code":13829,"language":105,"meta":106,"style":106},"pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\npdf.AddUTF8Font(\"notosansjp\", \"\", \"NotoSansJP-Regular.ttf\")\npdf.AddPage()\npdf.SetFont(\"notosansjp\", \"\", 14)\npdf.Cell(0, 10, \"こんにちは、世界。\")\npdf.OutputFileAndClose(\"ja.pdf\")\n",[44,13831,13832,13875,13905,13915,13942,13968],{"__ignoreMap":106},[110,13833,13834,13837,13839,13841,13843,13845,13847,13849,13851,13853,13855,13857,13859,13861,13863,13865,13867,13869,13871,13873],{"class":112,"line":113},[110,13835,13836],{"class":228},"pdf ",[110,13838,238],{"class":116},[110,13840,12147],{"class":228},[110,13842,60],{"class":116},[110,13844,4677],{"class":215},[110,13846,249],{"class":116},[110,13848,252],{"class":116},[110,13850,4684],{"class":255},[110,13852,252],{"class":116},[110,13854,232],{"class":116},[110,13856,4691],{"class":116},[110,13858,4694],{"class":255},[110,13860,252],{"class":116},[110,13862,232],{"class":116},[110,13864,4691],{"class":116},[110,13866,344],{"class":255},[110,13868,252],{"class":116},[110,13870,232],{"class":116},[110,13872,4709],{"class":116},[110,13874,201],{"class":116},[110,13876,13877,13879,13881,13883,13885,13887,13889,13891,13893,13895,13897,13899,13901,13903],{"class":112,"line":124},[110,13878,8613],{"class":228},[110,13880,60],{"class":116},[110,13882,707],{"class":215},[110,13884,249],{"class":116},[110,13886,252],{"class":116},[110,13888,965],{"class":255},[110,13890,252],{"class":116},[110,13892,232],{"class":116},[110,13894,4709],{"class":116},[110,13896,232],{"class":116},[110,13898,4691],{"class":116},[110,13900,81],{"class":255},[110,13902,252],{"class":116},[110,13904,201],{"class":116},[110,13906,13907,13909,13911,13913],{"class":112,"line":131},[110,13908,8613],{"class":228},[110,13910,60],{"class":116},[110,13912,462],{"class":215},[110,13914,465],{"class":116},[110,13916,13917,13919,13921,13923,13925,13927,13929,13931,13933,13935,13937,13940],{"class":112,"line":141},[110,13918,8613],{"class":228},[110,13920,60],{"class":116},[110,13922,4731],{"class":215},[110,13924,249],{"class":116},[110,13926,252],{"class":116},[110,13928,965],{"class":255},[110,13930,252],{"class":116},[110,13932,232],{"class":116},[110,13934,4709],{"class":116},[110,13936,232],{"class":116},[110,13938,13939],{"class":381}," 14",[110,13941,201],{"class":116},[110,13943,13944,13946,13948,13950,13952,13954,13956,13958,13960,13962,13964,13966],{"class":112,"line":153},[110,13945,8613],{"class":228},[110,13947,60],{"class":116},[110,13949,4765],{"class":215},[110,13951,249],{"class":116},[110,13953,7467],{"class":381},[110,13955,232],{"class":116},[110,13957,4775],{"class":381},[110,13959,232],{"class":116},[110,13961,4691],{"class":116},[110,13963,554],{"class":255},[110,13965,252],{"class":116},[110,13967,201],{"class":116},[110,13969,13970,13972,13974,13976,13978,13980,13983,13985],{"class":112,"line":163},[110,13971,8613],{"class":228},[110,13973,60],{"class":116},[110,13975,12257],{"class":215},[110,13977,249],{"class":116},[110,13979,252],{"class":116},[110,13981,13982],{"class":255},"ja.pdf",[110,13984,252],{"class":116},[110,13986,201],{"class":116},[19,13988,13989,13990,13992],{},"Two land mines: the TTF must exist at the path you give, at runtime, on the host that runs your binary (so your Docker image has to ship the font). And ",[44,13991,4765],{}," of \"0\" width means \"to the right margin,\" which for CJK text often clips because the width estimator doesn't account for full-width glyphs correctly.",[19,13994,13995],{},[39,13996,4900],{},[101,13998,14000],{"className":103,"code":13999,"language":105,"meta":106,"style":106},"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    fontData, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", fontData),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 14),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"こんにちは、世界。\")\n            c.Text(\"吾輩は猫である。名前はまだ無い。\")\n            c.Text(\"東京都渋谷区神宮前1-2-3\")\n        })\n    })\n\n    data, _ := doc.Generate()\n    os.WriteFile(\"ja.pdf\", data, 0o644)\n}\n",[44,14001,14002,14008,14012,14018,14026,14034,14038,14046,14054,14062,14066,14070,14080,14107,14119,14133,14137,14141,14155,14173,14203,14226,14248,14252,14256,14270,14294,14324,14342,14361,14380,14384,14388,14392,14410,14437],{"__ignoreMap":106},[110,14003,14004,14006],{"class":112,"line":113},[110,14005,117],{"class":116},[110,14007,121],{"class":120},[110,14009,14010],{"class":112,"line":124},[110,14011,128],{"emptyLinePlaceholder":127},[110,14013,14014,14016],{"class":112,"line":131},[110,14015,135],{"class":134},[110,14017,138],{"class":116},[110,14019,14020,14022,14024],{"class":112,"line":141},[110,14021,144],{"class":116},[110,14023,147],{"class":120},[110,14025,150],{"class":116},[110,14027,14028,14030,14032],{"class":112,"line":153},[110,14029,144],{"class":116},[110,14031,158],{"class":120},[110,14033,150],{"class":116},[110,14035,14036],{"class":112,"line":163},[110,14037,128],{"emptyLinePlaceholder":127},[110,14039,14040,14042,14044],{"class":112,"line":168},[110,14041,144],{"class":116},[110,14043,173],{"class":120},[110,14045,150],{"class":116},[110,14047,14048,14050,14052],{"class":112,"line":178},[110,14049,144],{"class":116},[110,14051,183],{"class":120},[110,14053,150],{"class":116},[110,14055,14056,14058,14060],{"class":112,"line":188},[110,14057,144],{"class":116},[110,14059,193],{"class":120},[110,14061,150],{"class":116},[110,14063,14064],{"class":112,"line":198},[110,14065,201],{"class":116},[110,14067,14068],{"class":112,"line":204},[110,14069,128],{"emptyLinePlaceholder":127},[110,14071,14072,14074,14076,14078],{"class":112,"line":209},[110,14073,212],{"class":116},[110,14075,216],{"class":215},[110,14077,219],{"class":116},[110,14079,222],{"class":116},[110,14081,14082,14085,14087,14089,14091,14093,14095,14097,14099,14101,14103,14105],{"class":112,"line":225},[110,14083,14084],{"class":228},"    fontData",[110,14086,232],{"class":116},[110,14088,235],{"class":228},[110,14090,238],{"class":116},[110,14092,241],{"class":228},[110,14094,60],{"class":116},[110,14096,246],{"class":215},[110,14098,249],{"class":116},[110,14100,252],{"class":116},[110,14102,81],{"class":255},[110,14104,252],{"class":116},[110,14106,201],{"class":116},[110,14108,14109,14111,14113,14115,14117],{"class":112,"line":262},[110,14110,265],{"class":134},[110,14112,235],{"class":228},[110,14114,270],{"class":116},[110,14116,273],{"class":116},[110,14118,222],{"class":116},[110,14120,14121,14123,14125,14127,14129,14131],{"class":112,"line":278},[110,14122,281],{"class":228},[110,14124,60],{"class":116},[110,14126,286],{"class":215},[110,14128,249],{"class":116},[110,14130,291],{"class":228},[110,14132,201],{"class":116},[110,14134,14135],{"class":112,"line":296},[110,14136,299],{"class":116},[110,14138,14139],{"class":112,"line":302},[110,14140,128],{"emptyLinePlaceholder":127},[110,14142,14143,14145,14147,14149,14151,14153],{"class":112,"line":307},[110,14144,310],{"class":228},[110,14146,238],{"class":116},[110,14148,315],{"class":228},[110,14150,60],{"class":116},[110,14152,320],{"class":215},[110,14154,323],{"class":116},[110,14156,14157,14159,14161,14163,14165,14167,14169,14171],{"class":112,"line":326},[110,14158,329],{"class":228},[110,14160,60],{"class":116},[110,14162,334],{"class":215},[110,14164,249],{"class":116},[110,14166,362],{"class":228},[110,14168,60],{"class":116},[110,14170,344],{"class":228},[110,14172,347],{"class":116},[110,14174,14175,14177,14179,14181,14183,14185,14187,14189,14191,14193,14195,14197,14199,14201],{"class":112,"line":350},[110,14176,329],{"class":228},[110,14178,60],{"class":116},[110,14180,357],{"class":215},[110,14182,249],{"class":116},[110,14184,362],{"class":228},[110,14186,60],{"class":116},[110,14188,367],{"class":215},[110,14190,249],{"class":116},[110,14192,362],{"class":228},[110,14194,60],{"class":116},[110,14196,376],{"class":215},[110,14198,249],{"class":116},[110,14200,382],{"class":381},[110,14202,385],{"class":116},[110,14204,14205,14207,14209,14211,14213,14215,14217,14219,14221,14224],{"class":112,"line":388},[110,14206,329],{"class":228},[110,14208,60],{"class":116},[110,14210,50],{"class":215},[110,14212,249],{"class":116},[110,14214,252],{"class":116},[110,14216,401],{"class":255},[110,14218,252],{"class":116},[110,14220,232],{"class":116},[110,14222,14223],{"class":228}," fontData",[110,14225,347],{"class":116},[110,14227,14228,14230,14232,14234,14236,14238,14240,14242,14244,14246],{"class":112,"line":413},[110,14229,329],{"class":228},[110,14231,60],{"class":116},[110,14233,420],{"class":215},[110,14235,249],{"class":116},[110,14237,252],{"class":116},[110,14239,401],{"class":255},[110,14241,252],{"class":116},[110,14243,232],{"class":116},[110,14245,13939],{"class":381},[110,14247,347],{"class":116},[110,14249,14250],{"class":112,"line":438},[110,14251,441],{"class":116},[110,14253,14254],{"class":112,"line":444},[110,14255,128],{"emptyLinePlaceholder":127},[110,14257,14258,14260,14262,14264,14266,14268],{"class":112,"line":449},[110,14259,452],{"class":228},[110,14261,238],{"class":116},[110,14263,457],{"class":228},[110,14265,60],{"class":116},[110,14267,462],{"class":215},[110,14269,465],{"class":116},[110,14271,14272,14274,14276,14278,14280,14282,14284,14286,14288,14290,14292],{"class":112,"line":468},[110,14273,471],{"class":228},[110,14275,60],{"class":116},[110,14277,476],{"class":215},[110,14279,479],{"class":116},[110,14281,483],{"class":482},[110,14283,486],{"class":116},[110,14285,489],{"class":120},[110,14287,60],{"class":116},[110,14289,494],{"class":120},[110,14291,497],{"class":116},[110,14293,222],{"class":116},[110,14295,14296,14298,14300,14302,14304,14306,14308,14310,14312,14314,14316,14318,14320,14322],{"class":112,"line":502},[110,14297,505],{"class":228},[110,14299,60],{"class":116},[110,14301,510],{"class":215},[110,14303,249],{"class":116},[110,14305,515],{"class":381},[110,14307,232],{"class":116},[110,14309,520],{"class":116},[110,14311,523],{"class":482},[110,14313,486],{"class":116},[110,14315,489],{"class":120},[110,14317,60],{"class":116},[110,14319,532],{"class":120},[110,14321,497],{"class":116},[110,14323,222],{"class":116},[110,14325,14326,14328,14330,14332,14334,14336,14338,14340],{"class":112,"line":539},[110,14327,542],{"class":228},[110,14329,60],{"class":116},[110,14331,547],{"class":215},[110,14333,249],{"class":116},[110,14335,252],{"class":116},[110,14337,554],{"class":255},[110,14339,252],{"class":116},[110,14341,201],{"class":116},[110,14343,14344,14346,14348,14350,14352,14354,14357,14359],{"class":112,"line":561},[110,14345,542],{"class":228},[110,14347,60],{"class":116},[110,14349,547],{"class":215},[110,14351,249],{"class":116},[110,14353,252],{"class":116},[110,14355,14356],{"class":255},"吾輩は猫である。名前はまだ無い。",[110,14358,252],{"class":116},[110,14360,201],{"class":116},[110,14362,14363,14365,14367,14369,14371,14373,14376,14378],{"class":112,"line":567},[110,14364,542],{"class":228},[110,14366,60],{"class":116},[110,14368,547],{"class":215},[110,14370,249],{"class":116},[110,14372,252],{"class":116},[110,14374,14375],{"class":255},"東京都渋谷区神宮前1-2-3",[110,14377,252],{"class":116},[110,14379,201],{"class":116},[110,14381,14382],{"class":112,"line":573},[110,14383,564],{"class":116},[110,14385,14386],{"class":112,"line":578},[110,14387,570],{"class":116},[110,14389,14390],{"class":112,"line":599},[110,14391,128],{"emptyLinePlaceholder":127},[110,14393,14394,14396,14398,14400,14402,14404,14406,14408],{"class":112,"line":612},[110,14395,581],{"class":228},[110,14397,232],{"class":116},[110,14399,2064],{"class":228},[110,14401,238],{"class":116},[110,14403,457],{"class":228},[110,14405,60],{"class":116},[110,14407,594],{"class":215},[110,14409,465],{"class":116},[110,14411,14412,14415,14417,14419,14421,14423,14425,14427,14429,14431,14433,14435],{"class":112,"line":627},[110,14413,14414],{"class":228},"    os",[110,14416,60],{"class":116},[110,14418,645],{"class":215},[110,14420,249],{"class":116},[110,14422,252],{"class":116},[110,14424,13982],{"class":255},[110,14426,252],{"class":116},[110,14428,232],{"class":116},[110,14430,659],{"class":228},[110,14432,232],{"class":116},[110,14434,664],{"class":381},[110,14436,201],{"class":116},[110,14438,14439],{"class":112,"line":632},[110,14440,701],{"class":116},[19,14442,14443],{},"Two things are different.",[19,14445,14446,14447,14450,14451,14453],{},"First, you pass ",[39,14448,14449],{},"bytes",", not a path. Embed the TTF with ",[44,14452,2333],{}," and your binary becomes self-contained. No \"font not found\" in production because someone forgot to mount a volume.",[19,14455,14456],{},"Second, gpdf's TrueType subsetter understands CJK cmap formats (4, 6, 12) and Identity-H encoding. The output PDF only carries the glyphs you actually used — embedding NotoSansJP for a 200-character invoice produces a ~30 KB font subset, not a 4 MB full embed. If you've ever watched gofpdf write a 5 MB PDF for one page of Japanese, this is the thing you'll notice first.",[19,14458,14459],{},"For a deeper walkthrough of CJK-specific options — IPAex Gothic, Source Han Sans, fallback chains — see the upcoming companion post on Japanese fonts.",[14,14461,14463],{"id":14462},"before-after-4-header-on-every-page-page-numbers-in-the-footer","Before / After 4: header on every page, page numbers in the footer",[19,14465,14466,14467,962,14470,14473,14474,14477,14478,962,14480,60],{},"The gofpdf pattern for repeating chrome is ",[44,14468,14469],{},"SetHeaderFunc",[44,14471,14472],{},"SetFooterFunc"," — both take a ",[44,14475,14476],{},"func()"," that runs against the current cursor. Page numbers come from ",[44,14479,12005],{},[44,14481,14482],{},"pdf.AliasNbPages()",[19,14484,14485],{},[39,14486,12098],{},[101,14488,14490],{"className":103,"code":14489,"language":105,"meta":106,"style":106},"pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\npdf.SetHeaderFunc(func() {\n    pdf.SetFont(\"Arial\", \"B\", 12)\n    pdf.Cell(0, 10, \"ACME Corporation\")\n    pdf.Ln(15)\n})\npdf.SetFooterFunc(func() {\n    pdf.SetY(-15)\n    pdf.SetFont(\"Arial\", \"I\", 8)\n    pdf.CellFormat(0, 10,\n        fmt.Sprintf(\"Page %d/{nb}\", pdf.PageNo()),\n        \"\", 0, \"C\", false, 0, \"\")\n})\npdf.AliasNbPages(\"\")\npdf.AddPage()\n// ... body ...\n",[44,14491,14492,14534,14547,14577,14604,14619,14623,14635,14650,14681,14699,14736,14767,14771,14787,14797],{"__ignoreMap":106},[110,14493,14494,14496,14498,14500,14502,14504,14506,14508,14510,14512,14514,14516,14518,14520,14522,14524,14526,14528,14530,14532],{"class":112,"line":113},[110,14495,13836],{"class":228},[110,14497,238],{"class":116},[110,14499,12147],{"class":228},[110,14501,60],{"class":116},[110,14503,4677],{"class":215},[110,14505,249],{"class":116},[110,14507,252],{"class":116},[110,14509,4684],{"class":255},[110,14511,252],{"class":116},[110,14513,232],{"class":116},[110,14515,4691],{"class":116},[110,14517,4694],{"class":255},[110,14519,252],{"class":116},[110,14521,232],{"class":116},[110,14523,4691],{"class":116},[110,14525,344],{"class":255},[110,14527,252],{"class":116},[110,14529,232],{"class":116},[110,14531,4709],{"class":116},[110,14533,201],{"class":116},[110,14535,14536,14538,14540,14542,14545],{"class":112,"line":124},[110,14537,8613],{"class":228},[110,14539,60],{"class":116},[110,14541,14469],{"class":215},[110,14543,14544],{"class":116},"(func()",[110,14546,222],{"class":116},[110,14548,14549,14551,14553,14555,14557,14559,14561,14563,14565,14567,14569,14571,14573,14575],{"class":112,"line":131},[110,14550,4716],{"class":228},[110,14552,60],{"class":116},[110,14554,4731],{"class":215},[110,14556,249],{"class":116},[110,14558,252],{"class":116},[110,14560,4738],{"class":255},[110,14562,252],{"class":116},[110,14564,232],{"class":116},[110,14566,4691],{"class":116},[110,14568,4747],{"class":255},[110,14570,252],{"class":116},[110,14572,232],{"class":116},[110,14574,433],{"class":381},[110,14576,201],{"class":116},[110,14578,14579,14581,14583,14585,14587,14589,14591,14593,14595,14597,14600,14602],{"class":112,"line":141},[110,14580,4716],{"class":228},[110,14582,60],{"class":116},[110,14584,4765],{"class":215},[110,14586,249],{"class":116},[110,14588,7467],{"class":381},[110,14590,232],{"class":116},[110,14592,4775],{"class":381},[110,14594,232],{"class":116},[110,14596,4691],{"class":116},[110,14598,14599],{"class":255},"ACME Corporation",[110,14601,252],{"class":116},[110,14603,201],{"class":116},[110,14605,14606,14608,14610,14613,14615,14617],{"class":112,"line":153},[110,14607,4716],{"class":228},[110,14609,60],{"class":116},[110,14611,14612],{"class":215},"Ln",[110,14614,249],{"class":116},[110,14616,2732],{"class":381},[110,14618,201],{"class":116},[110,14620,14621],{"class":112,"line":163},[110,14622,936],{"class":116},[110,14624,14625,14627,14629,14631,14633],{"class":112,"line":168},[110,14626,8613],{"class":228},[110,14628,60],{"class":116},[110,14630,14472],{"class":215},[110,14632,14544],{"class":116},[110,14634,222],{"class":116},[110,14636,14637,14639,14641,14643,14646,14648],{"class":112,"line":178},[110,14638,4716],{"class":228},[110,14640,60],{"class":116},[110,14642,5561],{"class":215},[110,14644,14645],{"class":116},"(-",[110,14647,2732],{"class":381},[110,14649,201],{"class":116},[110,14651,14652,14654,14656,14658,14660,14662,14664,14666,14668,14670,14673,14675,14677,14679],{"class":112,"line":188},[110,14653,4716],{"class":228},[110,14655,60],{"class":116},[110,14657,4731],{"class":215},[110,14659,249],{"class":116},[110,14661,252],{"class":116},[110,14663,4738],{"class":255},[110,14665,252],{"class":116},[110,14667,232],{"class":116},[110,14669,4691],{"class":116},[110,14671,14672],{"class":255},"I",[110,14674,252],{"class":116},[110,14676,232],{"class":116},[110,14678,12776],{"class":381},[110,14680,201],{"class":116},[110,14682,14683,14685,14687,14689,14691,14693,14695,14697],{"class":112,"line":198},[110,14684,4716],{"class":228},[110,14686,60],{"class":116},[110,14688,12766],{"class":215},[110,14690,249],{"class":116},[110,14692,7467],{"class":381},[110,14694,232],{"class":116},[110,14696,4775],{"class":381},[110,14698,6985],{"class":116},[110,14700,14701,14704,14706,14709,14711,14713,14716,14719,14722,14724,14726,14728,14730,14733],{"class":112,"line":204},[110,14702,14703],{"class":228},"        fmt",[110,14705,60],{"class":116},[110,14707,14708],{"class":215},"Sprintf",[110,14710,249],{"class":116},[110,14712,252],{"class":116},[110,14714,14715],{"class":255},"Page ",[110,14717,14718],{"class":1060},"%d",[110,14720,14721],{"class":255},"/{nb}",[110,14723,252],{"class":116},[110,14725,232],{"class":116},[110,14727,4837],{"class":228},[110,14729,60],{"class":116},[110,14731,14732],{"class":215},"PageNo",[110,14734,14735],{"class":116},"()),\n",[110,14737,14738,14741,14743,14745,14747,14749,14751,14753,14755,14757,14759,14761,14763,14765],{"class":112,"line":209},[110,14739,14740],{"class":116},"        \"\"",[110,14742,232],{"class":116},[110,14744,12798],{"class":381},[110,14746,232],{"class":116},[110,14748,4691],{"class":116},[110,14750,12867],{"class":255},[110,14752,252],{"class":116},[110,14754,232],{"class":116},[110,14756,13234],{"class":12812},[110,14758,232],{"class":116},[110,14760,12798],{"class":381},[110,14762,232],{"class":116},[110,14764,4709],{"class":116},[110,14766,201],{"class":116},[110,14768,14769],{"class":112,"line":225},[110,14770,936],{"class":116},[110,14772,14773,14775,14777,14780,14782,14785],{"class":112,"line":262},[110,14774,8613],{"class":228},[110,14776,60],{"class":116},[110,14778,14779],{"class":215},"AliasNbPages",[110,14781,249],{"class":116},[110,14783,14784],{"class":116},"\"\"",[110,14786,201],{"class":116},[110,14788,14789,14791,14793,14795],{"class":112,"line":278},[110,14790,8613],{"class":228},[110,14792,60],{"class":116},[110,14794,462],{"class":215},[110,14796,465],{"class":116},[110,14798,14799],{"class":112,"line":296},[110,14800,14801],{"class":839},"// ... body ...\n",[19,14803,14804,14807],{},[44,14805,14806],{},"{nb}"," is a sentinel that gofpdf rewrites at output time with the total page count. It works, it's just one of those things you have to know.",[19,14809,14810],{},[39,14811,4900],{},[101,14813,14815],{"className":103,"code":14814,"language":105,"meta":106,"style":106},"doc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n)\n\ndoc.Header(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"ACME Corporation\", template.Bold(), template.FontSize(12))\n            c.Line(template.LineColor(pdf.Gray(0.7)))\n            c.Spacer(document.Mm(4))\n        })\n    })\n})\n\ndoc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"ACME Corporation\",\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            // \"Page X / Y\" — both pieces are placeholders resolved\n            // by the layout engine after pagination.\n            c.PageNumber(template.AlignRight(),\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n\nfor i := 0; i \u003C 10; i++ {\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(fmt.Sprintf(\"Body content for page %d.\", i+1))\n        })\n    })\n}\n",[44,14816,14817,14831,14849,14879,14883,14887,14913,14938,14968,15006,15041,15063,15067,15071,15075,15079,15104,15128,15158,15176,15211,15215,15245,15250,15255,15274,15308,15312,15316,15320,15324,15355,15369,15393,15423,15463,15467,15471],{"__ignoreMap":106},[110,14818,14819,14821,14823,14825,14827,14829],{"class":112,"line":113},[110,14820,801],{"class":228},[110,14822,238],{"class":116},[110,14824,315],{"class":228},[110,14826,60],{"class":116},[110,14828,320],{"class":215},[110,14830,323],{"class":116},[110,14832,14833,14835,14837,14839,14841,14843,14845,14847],{"class":112,"line":124},[110,14834,816],{"class":228},[110,14836,60],{"class":116},[110,14838,334],{"class":215},[110,14840,249],{"class":116},[110,14842,362],{"class":228},[110,14844,60],{"class":116},[110,14846,344],{"class":228},[110,14848,347],{"class":116},[110,14850,14851,14853,14855,14857,14859,14861,14863,14865,14867,14869,14871,14873,14875,14877],{"class":112,"line":131},[110,14852,816],{"class":228},[110,14854,60],{"class":116},[110,14856,357],{"class":215},[110,14858,249],{"class":116},[110,14860,362],{"class":228},[110,14862,60],{"class":116},[110,14864,367],{"class":215},[110,14866,249],{"class":116},[110,14868,362],{"class":228},[110,14870,60],{"class":116},[110,14872,376],{"class":215},[110,14874,249],{"class":116},[110,14876,382],{"class":381},[110,14878,385],{"class":116},[110,14880,14881],{"class":112,"line":141},[110,14882,201],{"class":116},[110,14884,14885],{"class":112,"line":153},[110,14886,128],{"emptyLinePlaceholder":127},[110,14888,14889,14892,14894,14896,14898,14900,14902,14904,14906,14909,14911],{"class":112,"line":163},[110,14890,14891],{"class":228},"doc",[110,14893,60],{"class":116},[110,14895,4800],{"class":215},[110,14897,479],{"class":116},[110,14899,19],{"class":482},[110,14901,486],{"class":116},[110,14903,489],{"class":120},[110,14905,60],{"class":116},[110,14907,14908],{"class":120},"PageBuilder",[110,14910,497],{"class":116},[110,14912,222],{"class":116},[110,14914,14915,14918,14920,14922,14924,14926,14928,14930,14932,14934,14936],{"class":112,"line":168},[110,14916,14917],{"class":228},"    p",[110,14919,60],{"class":116},[110,14921,476],{"class":215},[110,14923,479],{"class":116},[110,14925,483],{"class":482},[110,14927,486],{"class":116},[110,14929,489],{"class":120},[110,14931,60],{"class":116},[110,14933,494],{"class":120},[110,14935,497],{"class":116},[110,14937,222],{"class":116},[110,14939,14940,14942,14944,14946,14948,14950,14952,14954,14956,14958,14960,14962,14964,14966],{"class":112,"line":178},[110,14941,505],{"class":228},[110,14943,60],{"class":116},[110,14945,510],{"class":215},[110,14947,249],{"class":116},[110,14949,515],{"class":381},[110,14951,232],{"class":116},[110,14953,520],{"class":116},[110,14955,523],{"class":482},[110,14957,486],{"class":116},[110,14959,489],{"class":120},[110,14961,60],{"class":116},[110,14963,532],{"class":120},[110,14965,497],{"class":116},[110,14967,222],{"class":116},[110,14969,14970,14972,14974,14976,14978,14980,14982,14984,14986,14988,14990,14992,14994,14996,14998,15000,15002,15004],{"class":112,"line":188},[110,14971,542],{"class":228},[110,14973,60],{"class":116},[110,14975,547],{"class":215},[110,14977,249],{"class":116},[110,14979,252],{"class":116},[110,14981,14599],{"class":255},[110,14983,252],{"class":116},[110,14985,232],{"class":116},[110,14987,1680],{"class":228},[110,14989,60],{"class":116},[110,14991,1700],{"class":215},[110,14993,4882],{"class":116},[110,14995,1680],{"class":228},[110,14997,60],{"class":116},[110,14999,1685],{"class":215},[110,15001,249],{"class":116},[110,15003,515],{"class":381},[110,15005,2279],{"class":116},[110,15007,15008,15010,15012,15015,15017,15019,15021,15024,15026,15028,15030,15033,15035,15038],{"class":112,"line":198},[110,15009,542],{"class":228},[110,15011,60],{"class":116},[110,15013,15014],{"class":215},"Line",[110,15016,249],{"class":116},[110,15018,489],{"class":228},[110,15020,60],{"class":116},[110,15022,15023],{"class":215},"LineColor",[110,15025,249],{"class":116},[110,15027,8613],{"class":228},[110,15029,60],{"class":116},[110,15031,15032],{"class":215},"Gray",[110,15034,249],{"class":116},[110,15036,15037],{"class":381},"0.7",[110,15039,15040],{"class":116},")))\n",[110,15042,15043,15045,15047,15049,15051,15053,15055,15057,15059,15061],{"class":112,"line":204},[110,15044,542],{"class":228},[110,15046,60],{"class":116},[110,15048,8336],{"class":215},[110,15050,249],{"class":116},[110,15052,362],{"class":228},[110,15054,60],{"class":116},[110,15056,376],{"class":215},[110,15058,249],{"class":116},[110,15060,3095],{"class":381},[110,15062,2279],{"class":116},[110,15064,15065],{"class":112,"line":209},[110,15066,564],{"class":116},[110,15068,15069],{"class":112,"line":225},[110,15070,570],{"class":116},[110,15072,15073],{"class":112,"line":262},[110,15074,936],{"class":116},[110,15076,15077],{"class":112,"line":278},[110,15078,128],{"emptyLinePlaceholder":127},[110,15080,15081,15083,15085,15088,15090,15092,15094,15096,15098,15100,15102],{"class":112,"line":296},[110,15082,14891],{"class":228},[110,15084,60],{"class":116},[110,15086,15087],{"class":215},"Footer",[110,15089,479],{"class":116},[110,15091,19],{"class":482},[110,15093,486],{"class":116},[110,15095,489],{"class":120},[110,15097,60],{"class":116},[110,15099,14908],{"class":120},[110,15101,497],{"class":116},[110,15103,222],{"class":116},[110,15105,15106,15108,15110,15112,15114,15116,15118,15120,15122,15124,15126],{"class":112,"line":302},[110,15107,14917],{"class":228},[110,15109,60],{"class":116},[110,15111,476],{"class":215},[110,15113,479],{"class":116},[110,15115,483],{"class":482},[110,15117,486],{"class":116},[110,15119,489],{"class":120},[110,15121,60],{"class":116},[110,15123,494],{"class":120},[110,15125,497],{"class":116},[110,15127,222],{"class":116},[110,15129,15130,15132,15134,15136,15138,15140,15142,15144,15146,15148,15150,15152,15154,15156],{"class":112,"line":307},[110,15131,505],{"class":228},[110,15133,60],{"class":116},[110,15135,510],{"class":215},[110,15137,249],{"class":116},[110,15139,2913],{"class":381},[110,15141,232],{"class":116},[110,15143,520],{"class":116},[110,15145,523],{"class":482},[110,15147,486],{"class":116},[110,15149,489],{"class":120},[110,15151,60],{"class":116},[110,15153,532],{"class":120},[110,15155,497],{"class":116},[110,15157,222],{"class":116},[110,15159,15160,15162,15164,15166,15168,15170,15172,15174],{"class":112,"line":326},[110,15161,542],{"class":228},[110,15163,60],{"class":116},[110,15165,547],{"class":215},[110,15167,249],{"class":116},[110,15169,252],{"class":116},[110,15171,14599],{"class":255},[110,15173,252],{"class":116},[110,15175,6985],{"class":116},[110,15177,15178,15180,15182,15184,15186,15188,15190,15192,15194,15196,15198,15200,15202,15204,15206,15209],{"class":112,"line":350},[110,15179,8550],{"class":228},[110,15181,60],{"class":116},[110,15183,1685],{"class":215},[110,15185,249],{"class":116},[110,15187,3305],{"class":381},[110,15189,1693],{"class":116},[110,15191,1680],{"class":228},[110,15193,60],{"class":116},[110,15195,8608],{"class":215},[110,15197,249],{"class":116},[110,15199,8613],{"class":228},[110,15201,60],{"class":116},[110,15203,15032],{"class":215},[110,15205,249],{"class":116},[110,15207,15208],{"class":381},"0.5",[110,15210,15040],{"class":116},[110,15212,15213],{"class":112,"line":388},[110,15214,564],{"class":116},[110,15216,15217,15219,15221,15223,15225,15227,15229,15231,15233,15235,15237,15239,15241,15243],{"class":112,"line":413},[110,15218,505],{"class":228},[110,15220,60],{"class":116},[110,15222,510],{"class":215},[110,15224,249],{"class":116},[110,15226,2913],{"class":381},[110,15228,232],{"class":116},[110,15230,520],{"class":116},[110,15232,523],{"class":482},[110,15234,486],{"class":116},[110,15236,489],{"class":120},[110,15238,60],{"class":116},[110,15240,532],{"class":120},[110,15242,497],{"class":116},[110,15244,222],{"class":116},[110,15246,15247],{"class":112,"line":438},[110,15248,15249],{"class":839},"            // \"Page X / Y\" — both pieces are placeholders resolved\n",[110,15251,15252],{"class":112,"line":444},[110,15253,15254],{"class":839},"            // by the layout engine after pagination.\n",[110,15256,15257,15259,15261,15264,15266,15268,15270,15272],{"class":112,"line":449},[110,15258,542],{"class":228},[110,15260,60],{"class":116},[110,15262,15263],{"class":215},"PageNumber",[110,15265,249],{"class":116},[110,15267,489],{"class":228},[110,15269,60],{"class":116},[110,15271,4521],{"class":215},[110,15273,8599],{"class":116},[110,15275,15276,15278,15280,15282,15284,15286,15288,15290,15292,15294,15296,15298,15300,15302,15304,15306],{"class":112,"line":468},[110,15277,8550],{"class":228},[110,15279,60],{"class":116},[110,15281,1685],{"class":215},[110,15283,249],{"class":116},[110,15285,3305],{"class":381},[110,15287,1693],{"class":116},[110,15289,1680],{"class":228},[110,15291,60],{"class":116},[110,15293,8608],{"class":215},[110,15295,249],{"class":116},[110,15297,8613],{"class":228},[110,15299,60],{"class":116},[110,15301,15032],{"class":215},[110,15303,249],{"class":116},[110,15305,15208],{"class":381},[110,15307,15040],{"class":116},[110,15309,15310],{"class":112,"line":502},[110,15311,564],{"class":116},[110,15313,15314],{"class":112,"line":539},[110,15315,570],{"class":116},[110,15317,15318],{"class":112,"line":561},[110,15319,936],{"class":116},[110,15321,15322],{"class":112,"line":567},[110,15323,128],{"emptyLinePlaceholder":127},[110,15325,15326,15328,15331,15333,15335,15338,15340,15343,15345,15347,15350,15353],{"class":112,"line":573},[110,15327,5518],{"class":134},[110,15329,15330],{"class":228}," i ",[110,15332,238],{"class":116},[110,15334,12798],{"class":381},[110,15336,15337],{"class":116},";",[110,15339,15330],{"class":228},[110,15341,15342],{"class":116},"\u003C",[110,15344,4775],{"class":381},[110,15346,15337],{"class":116},[110,15348,15349],{"class":228}," i",[110,15351,15352],{"class":116},"++",[110,15354,222],{"class":116},[110,15356,15357,15359,15361,15363,15365,15367],{"class":112,"line":578},[110,15358,452],{"class":228},[110,15360,238],{"class":116},[110,15362,457],{"class":228},[110,15364,60],{"class":116},[110,15366,462],{"class":215},[110,15368,465],{"class":116},[110,15370,15371,15373,15375,15377,15379,15381,15383,15385,15387,15389,15391],{"class":112,"line":599},[110,15372,471],{"class":228},[110,15374,60],{"class":116},[110,15376,476],{"class":215},[110,15378,479],{"class":116},[110,15380,483],{"class":482},[110,15382,486],{"class":116},[110,15384,489],{"class":120},[110,15386,60],{"class":116},[110,15388,494],{"class":120},[110,15390,497],{"class":116},[110,15392,222],{"class":116},[110,15394,15395,15397,15399,15401,15403,15405,15407,15409,15411,15413,15415,15417,15419,15421],{"class":112,"line":612},[110,15396,505],{"class":228},[110,15398,60],{"class":116},[110,15400,510],{"class":215},[110,15402,249],{"class":116},[110,15404,515],{"class":381},[110,15406,232],{"class":116},[110,15408,520],{"class":116},[110,15410,523],{"class":482},[110,15412,486],{"class":116},[110,15414,489],{"class":120},[110,15416,60],{"class":116},[110,15418,532],{"class":120},[110,15420,497],{"class":116},[110,15422,222],{"class":116},[110,15424,15425,15427,15429,15431,15433,15435,15437,15439,15441,15443,15446,15448,15450,15452,15454,15456,15459,15461],{"class":112,"line":627},[110,15426,542],{"class":228},[110,15428,60],{"class":116},[110,15430,547],{"class":215},[110,15432,249],{"class":116},[110,15434,1048],{"class":228},[110,15436,60],{"class":116},[110,15438,14708],{"class":215},[110,15440,249],{"class":116},[110,15442,252],{"class":116},[110,15444,15445],{"class":255},"Body content for page ",[110,15447,14718],{"class":1060},[110,15449,60],{"class":255},[110,15451,252],{"class":116},[110,15453,232],{"class":116},[110,15455,15349],{"class":228},[110,15457,15458],{"class":116},"+",[110,15460,12791],{"class":381},[110,15462,2279],{"class":116},[110,15464,15465],{"class":112,"line":632},[110,15466,564],{"class":116},[110,15468,15469],{"class":112,"line":678},[110,15470,570],{"class":116},[110,15472,15473],{"class":112,"line":693},[110,15474,701],{"class":116},[19,15476,15477,962,15479,15482,15483,15485,15486,15489],{},[44,15478,15263],{},[44,15480,15481],{},"TotalPages"," are placeholders. They get expanded after pagination, when the layout engine knows how many pages exist. No ",[44,15484,14806],{}," sentinel, no manual ",[44,15487,15488],{},"SetY(-15)"," to peg the footer to the bottom — the footer is just a tree, and the engine reserves space for it on every page automatically.",[14,15491,15493],{"id":15492},"before-after-5-producing-bytes-for-an-http-handler","Before / After 5: producing bytes for an HTTP handler",[19,15495,15496,15497,15499,15500,15503,15504,15506],{},"Most production gofpdf code doesn't write to a file. It writes to an ",[44,15498,12678],{}," — usually a ",[44,15501,15502],{},"http.ResponseWriter"," returning ",[44,15505,4822],{}," to a browser. This is the pair where gpdf's API is closest to gofpdf's.",[19,15508,15509],{},[39,15510,12098],{},[101,15512,15514],{"className":103,"code":15513,"language":105,"meta":106,"style":106},"func handler(w http.ResponseWriter, r *http.Request) {\n    pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"\", 12)\n    pdf.Cell(0, 10, \"Generated at \"+time.Now().Format(time.RFC3339))\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := pdf.Output(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[44,15515,15516,15548,15590,15600,15626,15677,15681,15711,15739,15765,15769],{"__ignoreMap":106},[110,15517,15518,15520,15522,15524,15526,15528,15530,15532,15534,15536,15538,15540,15542,15544,15546],{"class":112,"line":113},[110,15519,212],{"class":116},[110,15521,4630],{"class":215},[110,15523,249],{"class":116},[110,15525,4635],{"class":482},[110,15527,4638],{"class":120},[110,15529,60],{"class":116},[110,15531,4643],{"class":120},[110,15533,232],{"class":116},[110,15535,4648],{"class":482},[110,15537,486],{"class":116},[110,15539,4653],{"class":120},[110,15541,60],{"class":116},[110,15543,4658],{"class":120},[110,15545,497],{"class":116},[110,15547,222],{"class":116},[110,15549,15550,15552,15554,15556,15558,15560,15562,15564,15566,15568,15570,15572,15574,15576,15578,15580,15582,15584,15586,15588],{"class":112,"line":124},[110,15551,4667],{"class":228},[110,15553,238],{"class":116},[110,15555,12147],{"class":228},[110,15557,60],{"class":116},[110,15559,4677],{"class":215},[110,15561,249],{"class":116},[110,15563,252],{"class":116},[110,15565,4684],{"class":255},[110,15567,252],{"class":116},[110,15569,232],{"class":116},[110,15571,4691],{"class":116},[110,15573,4694],{"class":255},[110,15575,252],{"class":116},[110,15577,232],{"class":116},[110,15579,4691],{"class":116},[110,15581,344],{"class":255},[110,15583,252],{"class":116},[110,15585,232],{"class":116},[110,15587,4709],{"class":116},[110,15589,201],{"class":116},[110,15591,15592,15594,15596,15598],{"class":112,"line":131},[110,15593,4716],{"class":228},[110,15595,60],{"class":116},[110,15597,462],{"class":215},[110,15599,465],{"class":116},[110,15601,15602,15604,15606,15608,15610,15612,15614,15616,15618,15620,15622,15624],{"class":112,"line":141},[110,15603,4716],{"class":228},[110,15605,60],{"class":116},[110,15607,4731],{"class":215},[110,15609,249],{"class":116},[110,15611,252],{"class":116},[110,15613,4738],{"class":255},[110,15615,252],{"class":116},[110,15617,232],{"class":116},[110,15619,4709],{"class":116},[110,15621,232],{"class":116},[110,15623,433],{"class":381},[110,15625,201],{"class":116},[110,15627,15628,15630,15632,15634,15636,15638,15640,15642,15644,15646,15649,15651,15653,15656,15658,15661,15663,15666,15668,15670,15672,15675],{"class":112,"line":153},[110,15629,4716],{"class":228},[110,15631,60],{"class":116},[110,15633,4765],{"class":215},[110,15635,249],{"class":116},[110,15637,7467],{"class":381},[110,15639,232],{"class":116},[110,15641,4775],{"class":381},[110,15643,232],{"class":116},[110,15645,4691],{"class":116},[110,15647,15648],{"class":255},"Generated at ",[110,15650,252],{"class":116},[110,15652,15458],{"class":116},[110,15654,15655],{"class":228},"time",[110,15657,60],{"class":116},[110,15659,15660],{"class":215},"Now",[110,15662,4803],{"class":116},[110,15664,15665],{"class":215},"Format",[110,15667,249],{"class":116},[110,15669,15655],{"class":228},[110,15671,60],{"class":116},[110,15673,15674],{"class":228},"RFC3339",[110,15676,2279],{"class":116},[110,15678,15679],{"class":112,"line":163},[110,15680,128],{"emptyLinePlaceholder":127},[110,15682,15683,15685,15687,15689,15691,15693,15695,15697,15699,15701,15703,15705,15707,15709],{"class":112,"line":168},[110,15684,4795],{"class":228},[110,15686,60],{"class":116},[110,15688,4800],{"class":215},[110,15690,4803],{"class":116},[110,15692,4806],{"class":215},[110,15694,249],{"class":116},[110,15696,252],{"class":116},[110,15698,4813],{"class":255},[110,15700,252],{"class":116},[110,15702,232],{"class":116},[110,15704,4691],{"class":116},[110,15706,4822],{"class":255},[110,15708,252],{"class":116},[110,15710,201],{"class":116},[110,15712,15713,15715,15717,15719,15721,15723,15725,15727,15729,15731,15733,15735,15737],{"class":112,"line":178},[110,15714,265],{"class":134},[110,15716,235],{"class":228},[110,15718,238],{"class":116},[110,15720,4837],{"class":228},[110,15722,60],{"class":116},[110,15724,4842],{"class":215},[110,15726,249],{"class":116},[110,15728,4635],{"class":228},[110,15730,667],{"class":116},[110,15732,235],{"class":228},[110,15734,270],{"class":116},[110,15736,273],{"class":116},[110,15738,222],{"class":116},[110,15740,15741,15743,15745,15747,15749,15751,15753,15755,15757,15759,15761,15763],{"class":112,"line":188},[110,15742,4861],{"class":228},[110,15744,60],{"class":116},[110,15746,4866],{"class":215},[110,15748,249],{"class":116},[110,15750,4635],{"class":228},[110,15752,232],{"class":116},[110,15754,4875],{"class":228},[110,15756,60],{"class":116},[110,15758,4866],{"class":215},[110,15760,4882],{"class":116},[110,15762,4885],{"class":381},[110,15764,201],{"class":116},[110,15766,15767],{"class":112,"line":198},[110,15768,299],{"class":116},[110,15770,15771],{"class":112,"line":204},[110,15772,701],{"class":116},[19,15774,15775],{},[39,15776,4900],{},[101,15778,15780],{"className":103,"code":15779,"language":105,"meta":106,"style":106},"func handler(w http.ResponseWriter, r *http.Request) {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Generated at \" + time.Now().Format(time.RFC3339))\n        })\n    })\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := doc.Render(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[44,15781,15782,15814,15828,15846,15876,15880,15884,15898,15922,15952,15992,15996,16000,16004,16034,16062,16088,16092],{"__ignoreMap":106},[110,15783,15784,15786,15788,15790,15792,15794,15796,15798,15800,15802,15804,15806,15808,15810,15812],{"class":112,"line":113},[110,15785,212],{"class":116},[110,15787,4630],{"class":215},[110,15789,249],{"class":116},[110,15791,4635],{"class":482},[110,15793,4638],{"class":120},[110,15795,60],{"class":116},[110,15797,4643],{"class":120},[110,15799,232],{"class":116},[110,15801,4648],{"class":482},[110,15803,486],{"class":116},[110,15805,4653],{"class":120},[110,15807,60],{"class":116},[110,15809,4658],{"class":120},[110,15811,497],{"class":116},[110,15813,222],{"class":116},[110,15815,15816,15818,15820,15822,15824,15826],{"class":112,"line":124},[110,15817,310],{"class":228},[110,15819,238],{"class":116},[110,15821,315],{"class":228},[110,15823,60],{"class":116},[110,15825,320],{"class":215},[110,15827,323],{"class":116},[110,15829,15830,15832,15834,15836,15838,15840,15842,15844],{"class":112,"line":131},[110,15831,329],{"class":228},[110,15833,60],{"class":116},[110,15835,334],{"class":215},[110,15837,249],{"class":116},[110,15839,362],{"class":228},[110,15841,60],{"class":116},[110,15843,344],{"class":228},[110,15845,347],{"class":116},[110,15847,15848,15850,15852,15854,15856,15858,15860,15862,15864,15866,15868,15870,15872,15874],{"class":112,"line":141},[110,15849,329],{"class":228},[110,15851,60],{"class":116},[110,15853,357],{"class":215},[110,15855,249],{"class":116},[110,15857,362],{"class":228},[110,15859,60],{"class":116},[110,15861,367],{"class":215},[110,15863,249],{"class":116},[110,15865,362],{"class":228},[110,15867,60],{"class":116},[110,15869,376],{"class":215},[110,15871,249],{"class":116},[110,15873,382],{"class":381},[110,15875,385],{"class":116},[110,15877,15878],{"class":112,"line":153},[110,15879,441],{"class":116},[110,15881,15882],{"class":112,"line":163},[110,15883,128],{"emptyLinePlaceholder":127},[110,15885,15886,15888,15890,15892,15894,15896],{"class":112,"line":168},[110,15887,452],{"class":228},[110,15889,238],{"class":116},[110,15891,457],{"class":228},[110,15893,60],{"class":116},[110,15895,462],{"class":215},[110,15897,465],{"class":116},[110,15899,15900,15902,15904,15906,15908,15910,15912,15914,15916,15918,15920],{"class":112,"line":178},[110,15901,471],{"class":228},[110,15903,60],{"class":116},[110,15905,476],{"class":215},[110,15907,479],{"class":116},[110,15909,483],{"class":482},[110,15911,486],{"class":116},[110,15913,489],{"class":120},[110,15915,60],{"class":116},[110,15917,494],{"class":120},[110,15919,497],{"class":116},[110,15921,222],{"class":116},[110,15923,15924,15926,15928,15930,15932,15934,15936,15938,15940,15942,15944,15946,15948,15950],{"class":112,"line":188},[110,15925,505],{"class":228},[110,15927,60],{"class":116},[110,15929,510],{"class":215},[110,15931,249],{"class":116},[110,15933,515],{"class":381},[110,15935,232],{"class":116},[110,15937,520],{"class":116},[110,15939,523],{"class":482},[110,15941,486],{"class":116},[110,15943,489],{"class":120},[110,15945,60],{"class":116},[110,15947,532],{"class":120},[110,15949,497],{"class":116},[110,15951,222],{"class":116},[110,15953,15954,15956,15958,15960,15962,15964,15966,15968,15971,15974,15976,15978,15980,15982,15984,15986,15988,15990],{"class":112,"line":198},[110,15955,542],{"class":228},[110,15957,60],{"class":116},[110,15959,547],{"class":215},[110,15961,249],{"class":116},[110,15963,252],{"class":116},[110,15965,15648],{"class":255},[110,15967,252],{"class":116},[110,15969,15970],{"class":116}," +",[110,15972,15973],{"class":228}," time",[110,15975,60],{"class":116},[110,15977,15660],{"class":215},[110,15979,4803],{"class":116},[110,15981,15665],{"class":215},[110,15983,249],{"class":116},[110,15985,15655],{"class":228},[110,15987,60],{"class":116},[110,15989,15674],{"class":228},[110,15991,2279],{"class":116},[110,15993,15994],{"class":112,"line":204},[110,15995,564],{"class":116},[110,15997,15998],{"class":112,"line":209},[110,15999,570],{"class":116},[110,16001,16002],{"class":112,"line":225},[110,16003,128],{"emptyLinePlaceholder":127},[110,16005,16006,16008,16010,16012,16014,16016,16018,16020,16022,16024,16026,16028,16030,16032],{"class":112,"line":262},[110,16007,4795],{"class":228},[110,16009,60],{"class":116},[110,16011,4800],{"class":215},[110,16013,4803],{"class":116},[110,16015,4806],{"class":215},[110,16017,249],{"class":116},[110,16019,252],{"class":116},[110,16021,4813],{"class":255},[110,16023,252],{"class":116},[110,16025,232],{"class":116},[110,16027,4691],{"class":116},[110,16029,4822],{"class":255},[110,16031,252],{"class":116},[110,16033,201],{"class":116},[110,16035,16036,16038,16040,16042,16044,16046,16048,16050,16052,16054,16056,16058,16060],{"class":112,"line":278},[110,16037,265],{"class":134},[110,16039,235],{"class":228},[110,16041,238],{"class":116},[110,16043,457],{"class":228},[110,16045,60],{"class":116},[110,16047,5231],{"class":215},[110,16049,249],{"class":116},[110,16051,4635],{"class":228},[110,16053,667],{"class":116},[110,16055,235],{"class":228},[110,16057,270],{"class":116},[110,16059,273],{"class":116},[110,16061,222],{"class":116},[110,16063,16064,16066,16068,16070,16072,16074,16076,16078,16080,16082,16084,16086],{"class":112,"line":296},[110,16065,4861],{"class":228},[110,16067,60],{"class":116},[110,16069,4866],{"class":215},[110,16071,249],{"class":116},[110,16073,4635],{"class":228},[110,16075,232],{"class":116},[110,16077,4875],{"class":228},[110,16079,60],{"class":116},[110,16081,4866],{"class":215},[110,16083,4882],{"class":116},[110,16085,4885],{"class":381},[110,16087,201],{"class":116},[110,16089,16090],{"class":112,"line":302},[110,16091,299],{"class":116},[110,16093,16094],{"class":112,"line":307},[110,16095,701],{"class":116},[19,16097,16098,16099,16101,16102,16105,16106,16108,16109,16112],{},"Same shape. ",[44,16100,12043],{}," streams the PDF directly into the response. If you want to set ",[44,16103,16104],{},"Content-Length",", call ",[44,16107,1162],{}," first to get the byte slice, then ",[44,16110,16111],{},"len()"," it.",[14,16114,16116],{"id":16115},"how-fast-is-fast-enough","How fast is \"fast enough\"?",[19,16118,16119,16120,16122,16123,16125],{},"gpdf is roughly ",[39,16121,11671],{}," on the workloads people actually run. The numbers below are from ",[44,16124,7368],{}," on an Apple M1 with Go 1.25.",[1896,16127,16128,16142],{},[1899,16129,16130],{},[1902,16131,16132,16134,16136,16138,16140],{},[1905,16133,5325],{},[1905,16135,339],{},[1905,16137,5330],{},[1905,16139,5333],{},[1905,16141,5336],{},[1912,16143,16144,16159,16174,16189],{},[1902,16145,16146,16149,16153,16155,16157],{},[1917,16147,16148],{},"Single page",[1917,16150,16151],{},[39,16152,5348],{},[1917,16154,5351],{},[1917,16156,5354],{},[1917,16158,5357],{},[1902,16160,16161,16164,16168,16170,16172],{},[1917,16162,16163],{},"4×10 table",[1917,16165,16166],{},[39,16167,5367],{},[1917,16169,5370],{},[1917,16171,5373],{},[1917,16173,5376],{},[1902,16175,16176,16179,16183,16185,16187],{},[1917,16177,16178],{},"100-page document",[1917,16180,16181],{},[39,16182,5386],{},[1917,16184,5389],{},[1917,16186,5376],{},[1917,16188,5394],{},[1902,16190,16191,16193,16197,16199,16201],{},[1917,16192,5399],{},[1917,16194,16195],{},[39,16196,5404],{},[1917,16198,5407],{},[1917,16200,5410],{},[1917,16202,5413],{},[19,16204,16205],{},"These aren't synthetic — the table benchmark is a 4-column, 10-row invoice line-items table; the 100-page benchmark is a paginated report with a repeating header and page numbers. The shape matches what production code actually does.",[19,16207,16208],{},"A small note on what these numbers mean. At 13 µs per single page, a single core can produce ~75,000 hello-world PDFs per second. At 108 µs per table-rich page, ~9,000 invoices per second. The point isn't bragging rights — it's that you can stop thinking about whether to cache or async-queue PDF generation. For most workloads, generating on the request path is fine.",[14,16210,16212],{"id":16211},"what-youre-giving-up","What you're giving up",[19,16214,16215],{},"Nothing in this guide is interesting if it papers over real gaps. Here's what gpdf doesn't do that gofpdf does:",[985,16217,16218,16226,16243,16249],{},[36,16219,16220,42,16223,16225],{},[39,16221,16222],{},"Arbitrary line angles, beziers, and complex paths.",[44,16224,5465],{}," draws a horizontal rule across a column. If you're producing technical drawings or charts with custom geometry, gpdf isn't there yet. (Charts as pre-rendered images: works fine.)",[36,16227,16228,16233,16234,16236,16237,16239,16240,16242],{},[39,16229,16230,16232],{},[44,16231,4266],{}," and absolute cursor work."," You can do absolute positioning with ",[44,16235,11964],{},", but if your existing code is 2,000 lines of ",[44,16238,4266],{}," followed by ",[44,16241,4765],{},", the migration is more like a rewrite. The upside is the rewritten code is usually half the size.",[36,16244,16245,16248],{},[39,16246,16247],{},"Form fields (AcroForm)."," gpdf doesn't yet generate fillable form fields. If your PDFs are form templates that users fill in a viewer, stay on a library that supports AcroForm — for now.",[36,16250,16251,16254],{},[39,16252,16253],{},"Annotations and bookmarks."," Basic outline support exists; rich annotations don't.",[19,16256,16257],{},"If none of those bite, the migration is straight-through. If one of them does, file an issue — the roadmap is driven by what people ask for.",[14,16259,5523],{"id":5522},[19,16261,16262,16265],{},[39,16263,16264],{},"Is gpdf a fork of gofpdf?","\nNo. gpdf is a clean reimplementation. The PDF wire-format work, the layout engine, the TrueType subsetter — all written from scratch in pure Go. There's no shared lineage with gofpdf or its forks. The reason it has to be a clean rewrite is that gofpdf's architecture is built around a single mutable cursor; you can't get a declarative grid out of that without breaking every existing call site.",[19,16267,16268,16271,16272,4331,16275,16277,16278,16280,16281,16284],{},[39,16269,16270],{},"Does gpdf have any external dependencies?","\nThe core library has zero. Run ",[44,16273,16274],{},"go mod graph | grep gpdf",[44,16276,4334],{}," and you'll see one line. The ",[44,16279,5488],{}," add-on (HTML→PDF, AES encryption, signatures, PDF/A) pulls in ",[44,16282,16283],{},"golang.org/x/net"," for HTML parsing, but that's opt-in and not required for migration.",[19,16286,16287,16290,16291,16293],{},[39,16288,16289],{},"What about CGO? gofpdf was CGO-free, what about gpdf?","\nSame. Pure Go, no CGO. Cross-compile with ",[44,16292,4321],{}," and ship a static binary. This matters for distroless and Alpine images, where dragging in a CGO toolchain doubles your container size.",[19,16295,16296,16302,16303,16305],{},[39,16297,16298,16299,16301],{},"My existing gofpdf code uses ",[44,16300,4266],{}," for absolute positioning everywhere. Can I migrate without rewriting?","\nYou can wrap ",[44,16304,11964],{}," and get something that feels similar. But if your code is structured around cursor manipulation, the layout-engine model is a mental shift, not a syntactic one. Read the 12-column grid post (link to come) before estimating the work — most teams find the rewrite is shorter than the original.",[19,16307,16308,16311],{},[39,16309,16310],{},"What if go-pdf/fpdf gets unarchived?","\nThen you have one more option. The bet behind gpdf isn't that gofpdf will stay archived forever — it's that the architecture (cursor-based, single-byte fonts, no native CJK) is a dead end regardless of who maintains it. PDF generation in 2026 looks more like building a web page than driving a plotter, and the API should reflect that.",[14,16313,1201],{"id":1200},[19,16315,1204],{},[101,16317,16318],{"className":1207,"code":1208,"language":1209,"meta":106,"style":106},[44,16319,16320],{"__ignoreMap":106},[110,16321,16322,16324,16326],{"class":112,"line":113},[110,16323,105],{"class":120},[110,16325,1218],{"class":255},[110,16327,1221],{"class":255},[19,16329,16330,1229,16333],{},[723,16331,1228],{"href":1226,"rel":16332},[727],[723,16334,1234],{"href":1232,"rel":16335},[727],[14,16337,5608],{"id":5607},[985,16339,16340,16346,16351],{},[36,16341,16342,16343],{},"How does the 12-column grid work in gpdf? ",[746,16344,16345],{},"(coming soon)",[36,16347,16348,16349],{},"How do I embed a Japanese font in gpdf? ",[746,16350,16345],{},[36,16352,16353,8921,16356],{},[723,16354,8920],{"href":1232,"rel":16355},[727],[44,16357,3964],{},[1236,16359,16360],{},"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 .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 .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 pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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}",{"title":106,"searchDepth":124,"depth":124,"links":16362},[16363,16364,16365,16366,16367,16368,16369,16370,16371,16372,16373,16374,16375,16376],{"id":1310,"depth":124,"text":1311},{"id":11703,"depth":124,"text":11704},{"id":11729,"depth":124,"text":11730},{"id":11773,"depth":124,"text":11774},{"id":12089,"depth":124,"text":12090},{"id":12682,"depth":124,"text":12683},{"id":13815,"depth":124,"text":13816},{"id":14462,"depth":124,"text":14463},{"id":15492,"depth":124,"text":15493},{"id":16115,"depth":124,"text":16116},{"id":16211,"depth":124,"text":16212},{"id":5522,"depth":124,"text":5523},{"id":1200,"depth":124,"text":1201},{"id":5607,"depth":124,"text":5608},"2026-04-14","jung-kurt/gofpdf was archived in 2021. This guide maps every gofpdf API to gpdf — a pure-Go replacement with native CJK support and zero dependencies.",{"name":16380,"totalTime":16381,"tools":16382,"steps":16383},"Migrate a Go project from gofpdf to gpdf","PT30M",[1259],[16384,16387,16390,16393,16396,16399],{"name":16385,"text":16386},"Replace the import path","Swap github.com/jung-kurt/gofpdf (archived 2021) and github.com/go-pdf/fpdf (archived 2025) for github.com/gpdf-dev/gpdf, github.com/gpdf-dev/gpdf/document, and github.com/gpdf-dev/gpdf/template.",{"name":16388,"text":16389},"Construct the document with builders instead of cursors","Call gpdf.NewDocument with WithPageSize, WithMargins, and optional WithFont. Instead of driving a cursor with SetXY, add pages via doc.AddPage() and describe content with RowBuilder and ColBuilder.",{"name":16391,"text":16392},"Convert Cell and MultiCell calls to declarative Text","Replace pdf.Cell and pdf.MultiCell with c.Text(...) inside a column. Text wraps at the column boundary automatically, so MultiCell's trailing flag disappears. Font size, weight, and color become per-text options.",{"name":16394,"text":16395},"Register CJK fonts through WithFont","For Japanese, Chinese, or Korean text, replace pdf.AddUTF8Font with gpdf.WithFont(name, ttfBytes) at document construction. No more TTF path bookkeeping or UTF-8 flag — subset embedding happens automatically.",{"name":16397,"text":16398},"Rewrite tables with rows and columns","Drop the nested Cell loops with manual column widths. Use row.Col(n, fn) inside an AutoRow to build table rows; the 12-column grid computes widths and handles page breaks.",{"name":16400,"text":16401},"Switch the output call","Replace pdf.OutputFileAndClose(path) with doc.Generate() plus os.WriteFile(path, data, 0o644), or use doc.Render(w) to stream to an io.Writer.",{},{"title":5615,"description":16378},"blog/001.gofpdf-migration",[5660,5661,2547],"rwfhEq6jxLzrt6NECIA6pJM2d2cu0CQxTMRm2Pt1Znk",1776428079051]