[{"data":1,"prerenderedAt":5436},["ShallowReactive",2],{"blog-en-signintech-gopdf-migration":3},{"id":4,"title":5,"author":6,"body":10,"date":5395,"description":5396,"draft":5397,"extension":5398,"howTo":5399,"image":5426,"meta":5427,"navigation":549,"path":5428,"seo":5429,"stem":5430,"tags":5431,"updated":5426,"__hash__":5435},"blog/blog/021.signintech-gopdf-migration.md","From signintech/gopdf to gpdf: less coordinate math",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/en/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":5378},"minimark",[13,18,39,59,62,65,69,72,75,84,87,91,94,104,126,129,133,136,486,510,514,517,522,1001,1006,1440,1447,1451,1454,1458,1917,1927,1931,2213,2233,2237,2243,2247,3061,3067,3071,3329,3335,3346,3350,3353,3357,3758,3765,3769,4026,4036,4040,4043,4047,4332,4335,4339,4691,4699,4703,4706,4985,4994,4998,5001,5096,5103,5110,5114,5117,5180,5187,5191,5197,5203,5217,5243,5267,5283,5300,5304,5307,5324,5338,5342,5374],[14,15,17],"h2",{"id":16},"tldr","TL;DR",[19,20,21,25,26,29,30,34,35,38],"p",{},[22,23,24],"strong",{},"gpdf"," is a pure-Go PDF library with a 12-column layout engine. ",[22,27,28],{},"signintech/gopdf"," is a low-level binding around the PDF coordinate system. If you've been running gopdf for a while and the codebase is now mostly ",[31,32,33],"code",{},"SetXY",", ",[31,36,37],{},"Cell",", and width arithmetic, this guide shows what those calls collapse into when there's a layout engine underneath.",[19,40,41,42,44,45,44,47,50,51,54,55,58],{},"Last week I sat with someone refactoring an invoice generator on signintech/gopdf. Five years of accretion. The function rendering the line-items table was 280 lines. About 40 of those did real work: format the amount, format the date, repeat for each row. The other 240 were calculating x-positions, tracking y, calling ",[31,43,33],{},", calling ",[31,46,37],{},[31,48,49],{},"Br",", drawing border lines with ",[31,52,53],{},"Line(x1, y1, x2, y2)",", deciding whether the row fit on the page or needed a manual ",[31,56,57],{},"AddPage"," and a manual reprint of the header.",[19,60,61],{},"That's the gopdf experience in production. It's not a bad library. It's a thin, fast, CGO-free binding around the PDF imaging model — and that's exactly what you get. There's a cursor, there are coordinates, and the engineer is the layout engine.",[19,63,64],{},"This article maps the gopdf API to gpdf, function by function. The thesis is in the title: most of the lines disappear because they were doing layout math the runtime can do for you.",[14,66,68],{"id":67},"what-signintechgopdf-is-and-what-it-isnt","What signintech/gopdf is — and what it isn't",[19,70,71],{},"Worth getting this straight before any \"migrate away\" framing, because gopdf has real virtues.",[19,73,74],{},"It's actively maintained. It's pure Go (no CGO), so cross-compilation and Alpine images just work. It supports TrueType fonts including CJK. Output is fast — gopdf is in the same ballpark as gpdf for the imaging primitives because they're both writing PDF wire format directly without a heavyweight engine in front. The API maps cleanly onto the underlying PDF model: there's a current point, you move it, you draw at it. If you already think in PDF coordinates, gopdf is comfortable.",[19,76,77,78,80,81,83],{},"What it isn't, is a layout system. There's no notion of a row, a column, a flex container, or a grid. There's no automatic page break: when your content runs past the bottom margin, you get content past the bottom margin (or off the page entirely) until you call ",[31,79,57],{}," yourself. Tables don't exist as a primitive — they're a pattern you re-implement every project, with cell-by-cell ",[31,82,37],{}," calls, manual border lines, and your own page-break logic.",[19,85,86],{},"For a one-page certificate or a very controlled fixed-template form, the cursor model is fine. For invoices, reports, statements, anything with variable-length content — the coordinate math grows with the surface area of the document. That's the workload gpdf is built for.",[14,88,90],{"id":89},"the-mental-model-shift","The mental model shift",[19,92,93],{},"This is the part that actually changes how the code reads. gpdf has two ideas that gopdf doesn't:",[19,95,96,99,100,103],{},[22,97,98],{},"Declarative tree."," You don't tell the renderer where to put things. You describe a tree of pages → rows → columns → content, and the layout engine resolves positions in a single pass. There's no cursor to advance. Two consecutive ",[31,101,102],{},"r.Col(...)"," calls don't need to know about each other.",[19,105,106,109,110,113,114,117,118,121,122,125],{},[22,107,108],{},"12-column grid."," Every row is implicitly 12 units wide. You spend those 12 units across columns: ",[31,111,112],{},"r.Col(8, ...)"," takes two-thirds, ",[31,115,116],{},"r.Col(4, ...)"," takes one-third. The grid is the same idea Bootstrap and Tailwind use for HTML, applied to PDF. You stop calculating ",[31,119,120],{},"pageWidth - leftMargin - rightMargin"," and dividing by 4. You write ",[31,123,124],{},"r.Col(3, ...)"," four times.",[19,127,128],{},"These two ideas remove most of the math. The before/after pairs below all collapse the same way: a cursor-advancing imperative loop becomes a small declarative tree.",[14,130,132],{"id":131},"the-api-mapping-table","The API mapping table",[19,134,135],{},"Cheat sheet first. Sections after walk through five concrete pairs.",[137,138,139,153],"table",{},[140,141,142],"thead",{},[143,144,145,149,151],"tr",{},[146,147,148],"th",{},"What you want to do",[146,150,28],{},[146,152,24],{},[154,155,156,172,187,202,222,240,258,277,295,311,326,341,356,371,393,415,438,453,468],"tbody",{},[143,157,158,162,167],{},[159,160,161],"td",{},"Construct",[159,163,164],{},[31,165,166],{},"pdf := gopdf.GoPdf{}; pdf.Start(gopdf.Config{...})",[159,168,169],{},[31,170,171],{},"doc := gpdf.NewDocument(gpdf.WithPageSize(document.A4), ...)",[143,173,174,177,182],{},[159,175,176],{},"Set page size",[159,178,179],{},[31,180,181],{},"Config{PageSize: gopdf.PageSizeA4}",[159,183,184],{},[31,185,186],{},"gpdf.WithPageSize(document.A4)",[143,188,189,192,197],{},[159,190,191],{},"Add page",[159,193,194],{},[31,195,196],{},"pdf.AddPage()",[159,198,199],{},[31,200,201],{},"page := doc.AddPage()",[143,203,204,207,217],{},[159,205,206],{},"Move cursor",[159,208,209,212,213],{},[31,210,211],{},"pdf.SetX(40); pdf.SetY(80)"," ",[214,215,216],"em",{},"(everywhere)",[159,218,219],{},[214,220,221],{},"(no cursor)",[143,223,224,227,232],{},[159,225,226],{},"Single line of text",[159,228,229],{},[31,230,231],{},"pdf.SetXY(x, y); pdf.Cell(nil, \"hi\")",[159,233,234,212,237],{},[31,235,236],{},"c.Text(\"hi\")",[214,238,239],{},"(inside a column)",[143,241,242,245,250],{},[159,243,244],{},"Wrapped text",[159,246,247],{},[31,248,249],{},"pdf.MultiCell(&gopdf.Rect{W: 200, H: 100}, body)",[159,251,252,212,255],{},[31,253,254],{},"c.Text(body)",[214,256,257],{},"(wraps automatically)",[143,259,260,263,268],{},[159,261,262],{},"Line break",[159,264,265],{},[31,266,267],{},"pdf.Br(20)",[159,269,270],{},[214,271,272,273,276],{},"(implicit between rows; ",[31,274,275],{},"c.Spacer(document.Mm(4))"," if you need one)",[143,278,279,282,287],{},[159,280,281],{},"Font registration",[159,283,284],{},[31,285,286],{},"pdf.AddTTFFont(\"noto\", \"fonts/Noto.ttf\")",[159,288,289,212,292],{},[31,290,291],{},"gpdf.WithFont(\"Noto\", ttfBytes)",[214,293,294],{},"(at construction)",[143,296,297,300,305],{},[159,298,299],{},"Set active font",[159,301,302],{},[31,303,304],{},"pdf.SetFont(\"noto\", \"\", 14)",[159,306,307,310],{},[31,308,309],{},"template.FontFamily(\"Noto\"), template.FontSize(14)"," per-text",[143,312,313,316,321],{},[159,314,315],{},"Color",[159,317,318],{},[31,319,320],{},"pdf.SetTextColor(26, 35, 126)",[159,322,323],{},[31,324,325],{},"template.TextColor(pdf.RGBHex(0x1A237E))",[143,327,328,331,336],{},[159,329,330],{},"Horizontal rule",[159,332,333],{},[31,334,335],{},"pdf.Line(40, 100, 555, 100)",[159,337,338],{},[31,339,340],{},"c.Line(template.LineColor(pdf.Gray(0.7)))",[143,342,343,346,351],{},[159,344,345],{},"Rectangle",[159,347,348],{},[31,349,350],{},"pdf.RectFromUpperLeftWithStyle(x, y, w, h, \"FD\")",[159,352,353],{},[31,354,355],{},"c.Box(template.BgColor(...), template.Border(...))",[143,357,358,361,366],{},[159,359,360],{},"Image",[159,362,363],{},[31,364,365],{},"pdf.Image(\"logo.png\", x, y, &gopdf.Rect{W: 100, H: 50})",[159,367,368],{},[31,369,370],{},"c.Image(imgBytes, template.FitWidth(document.Mm(35)))",[143,372,373,376,388],{},[159,374,375],{},"Manual table (cell-by-cell)",[159,377,378,379,381,382,381,385,387],{},"dozens of ",[31,380,37],{}," + ",[31,383,384],{},"Line",[31,386,33],{}," calls",[159,389,390],{},[31,391,392],{},"c.Table(headers, rows, template.ColumnWidths(...))",[143,394,395,398,407],{},[159,396,397],{},"Header / footer",[159,399,400,403,404],{},[31,401,402],{},"pdf.AddHeader(fn)"," / ",[31,405,406],{},"pdf.AddFooter(fn)",[159,408,409,403,412],{},[31,410,411],{},"doc.Header(fn)",[31,413,414],{},"doc.Footer(fn)",[143,416,417,420,427],{},[159,418,419],{},"Page number",[159,421,422,423,426],{},"format ",[31,424,425],{},"\"Page %d of %d\""," from a counter you maintain",[159,428,429,403,432,212,435],{},[31,430,431],{},"c.PageNumber()",[31,433,434],{},"c.TotalPages()",[214,436,437],{},"(placeholders)",[143,439,440,443,448],{},[159,441,442],{},"Encrypt",[159,444,445],{},[31,446,447],{},"Config{Protection: PDFProtectionConfig{...}}",[159,449,450],{},[31,451,452],{},"gpdf.WithEncryption(gpdf.AES256, \"user\", \"owner\", perms)",[143,454,455,458,463],{},[159,456,457],{},"Output",[159,459,460],{},[31,461,462],{},"pdf.WritePdf(\"out.pdf\")",[159,464,465],{},[31,466,467],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[143,469,470,473,481],{},[159,471,472],{},"Output to writer",[159,474,475,403,478],{},[31,476,477],{},"pdf.Write(w)",[31,479,480],{},"pdf.ToBuffer()",[159,482,483],{},[31,484,485],{},"doc.Render(w)",[19,487,488,489,492,493,495,496,498,499,501,502,505,506,509],{},"Two structural shifts. First, ",[22,490,491],{},"the cursor goes away",". Lines marked ",[214,494,216],{}," in the table aren't an exaggeration — in a real gopdf codebase, ",[31,497,33],{}," calls outnumber ",[31,500,37],{}," calls. They all collapse to nothing in gpdf. Second, ",[22,503,504],{},"percentages replace pixels",". ",[31,507,508],{},"Rect{W: 200, H: 100}"," becomes \"this column takes 4 of 12 units of whatever container it's in.\" Drop the same column inside a half-width row and it scales without changes.",[14,511,513],{"id":512},"before-after-1-hello-world","Before / After 1: hello world",[19,515,516],{},"The shortest possible diff. Notice what's missing on the right.",[19,518,519],{},[22,520,521],{},"Before — signintech/gopdf:",[523,524,529],"pre",{"className":525,"code":526,"language":527,"meta":528,"style":528},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package main\n\nimport (\n    \"log\"\n\n    \"github.com/signintech/gopdf\"\n)\n\nfunc main() {\n    pdf := gopdf.GoPdf{}\n    pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})\n    pdf.AddPage()\n\n    if err := pdf.AddTTFFont(\"helvetica\", \"fonts/Helvetica.ttf\"); err != nil {\n        log.Fatal(err)\n    }\n    if err := pdf.SetFont(\"helvetica\", \"\", 24); err != nil {\n        log.Fatal(err)\n    }\n\n    pdf.SetX(40)\n    pdf.SetY(80)\n    if err := pdf.Cell(nil, \"Hello, World!\"); err != nil {\n        log.Fatal(err)\n    }\n\n    if err := pdf.WritePdf(\"hello.pdf\"); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[31,530,531,544,551,561,573,578,588,594,599,615,637,681,693,698,752,770,776,821,836,841,846,863,880,915,930,935,940,975,990,995],{"__ignoreMap":528},[532,533,536,540],"span",{"class":534,"line":535},"line",1,[532,537,539],{"class":538},"sMK4o","package",[532,541,543],{"class":542},"sBMFI"," main\n",[532,545,547],{"class":534,"line":546},2,[532,548,550],{"emptyLinePlaceholder":549},true,"\n",[532,552,554,558],{"class":534,"line":553},3,[532,555,557],{"class":556},"s7zQu","import",[532,559,560],{"class":538}," (\n",[532,562,564,567,570],{"class":534,"line":563},4,[532,565,566],{"class":538},"    \"",[532,568,569],{"class":542},"log",[532,571,572],{"class":538},"\"\n",[532,574,576],{"class":534,"line":575},5,[532,577,550],{"emptyLinePlaceholder":549},[532,579,581,583,586],{"class":534,"line":580},6,[532,582,566],{"class":538},[532,584,585],{"class":542},"github.com/signintech/gopdf",[532,587,572],{"class":538},[532,589,591],{"class":534,"line":590},7,[532,592,593],{"class":538},")\n",[532,595,597],{"class":534,"line":596},8,[532,598,550],{"emptyLinePlaceholder":549},[532,600,602,605,609,612],{"class":534,"line":601},9,[532,603,604],{"class":538},"func",[532,606,608],{"class":607},"s2Zo4"," main",[532,610,611],{"class":538},"()",[532,613,614],{"class":538}," {\n",[532,616,618,622,625,628,631,634],{"class":534,"line":617},10,[532,619,621],{"class":620},"sTEyZ","    pdf ",[532,623,624],{"class":538},":=",[532,626,627],{"class":542}," gopdf",[532,629,630],{"class":538},".",[532,632,633],{"class":542},"GoPdf",[532,635,636],{"class":538},"{}\n",[532,638,640,643,645,648,651,654,656,659,662,665,668,671,673,675,678],{"class":534,"line":639},11,[532,641,642],{"class":620},"    pdf",[532,644,630],{"class":538},[532,646,647],{"class":607},"Start",[532,649,650],{"class":538},"(",[532,652,653],{"class":542},"gopdf",[532,655,630],{"class":538},[532,657,658],{"class":542},"Config",[532,660,661],{"class":538},"{",[532,663,664],{"class":620},"PageSize",[532,666,667],{"class":538},":",[532,669,670],{"class":538}," *",[532,672,653],{"class":620},[532,674,630],{"class":538},[532,676,677],{"class":620},"PageSizeA4",[532,679,680],{"class":538},"})\n",[532,682,684,686,688,690],{"class":534,"line":683},12,[532,685,642],{"class":620},[532,687,630],{"class":538},[532,689,57],{"class":607},[532,691,692],{"class":538},"()\n",[532,694,696],{"class":534,"line":695},13,[532,697,550],{"emptyLinePlaceholder":549},[532,699,701,704,707,709,712,714,717,719,722,726,728,731,734,737,739,742,744,747,750],{"class":534,"line":700},14,[532,702,703],{"class":556},"    if",[532,705,706],{"class":620}," err ",[532,708,624],{"class":538},[532,710,711],{"class":620}," pdf",[532,713,630],{"class":538},[532,715,716],{"class":607},"AddTTFFont",[532,718,650],{"class":538},[532,720,721],{"class":538},"\"",[532,723,725],{"class":724},"sfazB","helvetica",[532,727,721],{"class":538},[532,729,730],{"class":538},",",[532,732,733],{"class":538}," \"",[532,735,736],{"class":724},"fonts/Helvetica.ttf",[532,738,721],{"class":538},[532,740,741],{"class":538},");",[532,743,706],{"class":620},[532,745,746],{"class":538},"!=",[532,748,749],{"class":538}," nil",[532,751,614],{"class":538},[532,753,755,758,760,763,765,768],{"class":534,"line":754},15,[532,756,757],{"class":620},"        log",[532,759,630],{"class":538},[532,761,762],{"class":607},"Fatal",[532,764,650],{"class":538},[532,766,767],{"class":620},"err",[532,769,593],{"class":538},[532,771,773],{"class":534,"line":772},16,[532,774,775],{"class":538},"    }\n",[532,777,779,781,783,785,787,789,792,794,796,798,800,802,805,807,811,813,815,817,819],{"class":534,"line":778},17,[532,780,703],{"class":556},[532,782,706],{"class":620},[532,784,624],{"class":538},[532,786,711],{"class":620},[532,788,630],{"class":538},[532,790,791],{"class":607},"SetFont",[532,793,650],{"class":538},[532,795,721],{"class":538},[532,797,725],{"class":724},[532,799,721],{"class":538},[532,801,730],{"class":538},[532,803,804],{"class":538}," \"\"",[532,806,730],{"class":538},[532,808,810],{"class":809},"sbssI"," 24",[532,812,741],{"class":538},[532,814,706],{"class":620},[532,816,746],{"class":538},[532,818,749],{"class":538},[532,820,614],{"class":538},[532,822,824,826,828,830,832,834],{"class":534,"line":823},18,[532,825,757],{"class":620},[532,827,630],{"class":538},[532,829,762],{"class":607},[532,831,650],{"class":538},[532,833,767],{"class":620},[532,835,593],{"class":538},[532,837,839],{"class":534,"line":838},19,[532,840,775],{"class":538},[532,842,844],{"class":534,"line":843},20,[532,845,550],{"emptyLinePlaceholder":549},[532,847,849,851,853,856,858,861],{"class":534,"line":848},21,[532,850,642],{"class":620},[532,852,630],{"class":538},[532,854,855],{"class":607},"SetX",[532,857,650],{"class":538},[532,859,860],{"class":809},"40",[532,862,593],{"class":538},[532,864,866,868,870,873,875,878],{"class":534,"line":865},22,[532,867,642],{"class":620},[532,869,630],{"class":538},[532,871,872],{"class":607},"SetY",[532,874,650],{"class":538},[532,876,877],{"class":809},"80",[532,879,593],{"class":538},[532,881,883,885,887,889,891,893,895,898,900,903,905,907,909,911,913],{"class":534,"line":882},23,[532,884,703],{"class":556},[532,886,706],{"class":620},[532,888,624],{"class":538},[532,890,711],{"class":620},[532,892,630],{"class":538},[532,894,37],{"class":607},[532,896,897],{"class":538},"(nil,",[532,899,733],{"class":538},[532,901,902],{"class":724},"Hello, World!",[532,904,721],{"class":538},[532,906,741],{"class":538},[532,908,706],{"class":620},[532,910,746],{"class":538},[532,912,749],{"class":538},[532,914,614],{"class":538},[532,916,918,920,922,924,926,928],{"class":534,"line":917},24,[532,919,757],{"class":620},[532,921,630],{"class":538},[532,923,762],{"class":607},[532,925,650],{"class":538},[532,927,767],{"class":620},[532,929,593],{"class":538},[532,931,933],{"class":534,"line":932},25,[532,934,775],{"class":538},[532,936,938],{"class":534,"line":937},26,[532,939,550],{"emptyLinePlaceholder":549},[532,941,943,945,947,949,951,953,956,958,960,963,965,967,969,971,973],{"class":534,"line":942},27,[532,944,703],{"class":556},[532,946,706],{"class":620},[532,948,624],{"class":538},[532,950,711],{"class":620},[532,952,630],{"class":538},[532,954,955],{"class":607},"WritePdf",[532,957,650],{"class":538},[532,959,721],{"class":538},[532,961,962],{"class":724},"hello.pdf",[532,964,721],{"class":538},[532,966,741],{"class":538},[532,968,706],{"class":620},[532,970,746],{"class":538},[532,972,749],{"class":538},[532,974,614],{"class":538},[532,976,978,980,982,984,986,988],{"class":534,"line":977},28,[532,979,757],{"class":620},[532,981,630],{"class":538},[532,983,762],{"class":607},[532,985,650],{"class":538},[532,987,767],{"class":620},[532,989,593],{"class":538},[532,991,993],{"class":534,"line":992},29,[532,994,775],{"class":538},[532,996,998],{"class":534,"line":997},30,[532,999,1000],{"class":538},"}\n",[19,1002,1003],{},[22,1004,1005],{},"After — gpdf:",[523,1007,1009],{"className":525,"code":1008,"language":527,"meta":528,"style":528},"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",[31,1010,1011,1017,1021,1027,1035,1044,1048,1057,1066,1075,1079,1083,1093,1111,1134,1169,1174,1178,1194,1226,1262,1308,1313,1318,1322,1342,1354,1368,1372,1416,1430,1435],{"__ignoreMap":528},[532,1012,1013,1015],{"class":534,"line":535},[532,1014,539],{"class":538},[532,1016,543],{"class":542},[532,1018,1019],{"class":534,"line":546},[532,1020,550],{"emptyLinePlaceholder":549},[532,1022,1023,1025],{"class":534,"line":553},[532,1024,557],{"class":556},[532,1026,560],{"class":538},[532,1028,1029,1031,1033],{"class":534,"line":563},[532,1030,566],{"class":538},[532,1032,569],{"class":542},[532,1034,572],{"class":538},[532,1036,1037,1039,1042],{"class":534,"line":575},[532,1038,566],{"class":538},[532,1040,1041],{"class":542},"os",[532,1043,572],{"class":538},[532,1045,1046],{"class":534,"line":580},[532,1047,550],{"emptyLinePlaceholder":549},[532,1049,1050,1052,1055],{"class":534,"line":590},[532,1051,566],{"class":538},[532,1053,1054],{"class":542},"github.com/gpdf-dev/gpdf",[532,1056,572],{"class":538},[532,1058,1059,1061,1064],{"class":534,"line":596},[532,1060,566],{"class":538},[532,1062,1063],{"class":542},"github.com/gpdf-dev/gpdf/document",[532,1065,572],{"class":538},[532,1067,1068,1070,1073],{"class":534,"line":601},[532,1069,566],{"class":538},[532,1071,1072],{"class":542},"github.com/gpdf-dev/gpdf/template",[532,1074,572],{"class":538},[532,1076,1077],{"class":534,"line":617},[532,1078,593],{"class":538},[532,1080,1081],{"class":534,"line":639},[532,1082,550],{"emptyLinePlaceholder":549},[532,1084,1085,1087,1089,1091],{"class":534,"line":683},[532,1086,604],{"class":538},[532,1088,608],{"class":607},[532,1090,611],{"class":538},[532,1092,614],{"class":538},[532,1094,1095,1098,1100,1103,1105,1108],{"class":534,"line":695},[532,1096,1097],{"class":620},"    doc ",[532,1099,624],{"class":538},[532,1101,1102],{"class":620}," gpdf",[532,1104,630],{"class":538},[532,1106,1107],{"class":607},"NewDocument",[532,1109,1110],{"class":538},"(\n",[532,1112,1113,1116,1118,1121,1123,1126,1128,1131],{"class":534,"line":700},[532,1114,1115],{"class":620},"        gpdf",[532,1117,630],{"class":538},[532,1119,1120],{"class":607},"WithPageSize",[532,1122,650],{"class":538},[532,1124,1125],{"class":620},"document",[532,1127,630],{"class":538},[532,1129,1130],{"class":620},"A4",[532,1132,1133],{"class":538},"),\n",[532,1135,1136,1138,1140,1143,1145,1147,1149,1152,1154,1156,1158,1161,1163,1166],{"class":534,"line":754},[532,1137,1115],{"class":620},[532,1139,630],{"class":538},[532,1141,1142],{"class":607},"WithMargins",[532,1144,650],{"class":538},[532,1146,1125],{"class":620},[532,1148,630],{"class":538},[532,1150,1151],{"class":607},"UniformEdges",[532,1153,650],{"class":538},[532,1155,1125],{"class":620},[532,1157,630],{"class":538},[532,1159,1160],{"class":607},"Mm",[532,1162,650],{"class":538},[532,1164,1165],{"class":809},"20",[532,1167,1168],{"class":538},"))),\n",[532,1170,1171],{"class":534,"line":772},[532,1172,1173],{"class":538},"    )\n",[532,1175,1176],{"class":534,"line":778},[532,1177,550],{"emptyLinePlaceholder":549},[532,1179,1180,1183,1185,1188,1190,1192],{"class":534,"line":823},[532,1181,1182],{"class":620},"    page ",[532,1184,624],{"class":538},[532,1186,1187],{"class":620}," doc",[532,1189,630],{"class":538},[532,1191,57],{"class":607},[532,1193,692],{"class":538},[532,1195,1196,1199,1201,1204,1207,1211,1213,1216,1218,1221,1224],{"class":534,"line":838},[532,1197,1198],{"class":620},"    page",[532,1200,630],{"class":538},[532,1202,1203],{"class":607},"AutoRow",[532,1205,1206],{"class":538},"(func(",[532,1208,1210],{"class":1209},"sHdIc","r",[532,1212,670],{"class":538},[532,1214,1215],{"class":542},"template",[532,1217,630],{"class":538},[532,1219,1220],{"class":542},"RowBuilder",[532,1222,1223],{"class":538},")",[532,1225,614],{"class":538},[532,1227,1228,1231,1233,1236,1238,1241,1243,1246,1249,1251,1253,1255,1258,1260],{"class":534,"line":843},[532,1229,1230],{"class":620},"        r",[532,1232,630],{"class":538},[532,1234,1235],{"class":607},"Col",[532,1237,650],{"class":538},[532,1239,1240],{"class":809},"12",[532,1242,730],{"class":538},[532,1244,1245],{"class":538}," func(",[532,1247,1248],{"class":1209},"c",[532,1250,670],{"class":538},[532,1252,1215],{"class":542},[532,1254,630],{"class":538},[532,1256,1257],{"class":542},"ColBuilder",[532,1259,1223],{"class":538},[532,1261,614],{"class":538},[532,1263,1264,1267,1269,1272,1274,1276,1278,1280,1282,1285,1287,1290,1292,1295,1298,1300,1302,1305],{"class":534,"line":848},[532,1265,1266],{"class":620},"            c",[532,1268,630],{"class":538},[532,1270,1271],{"class":607},"Text",[532,1273,650],{"class":538},[532,1275,721],{"class":538},[532,1277,902],{"class":724},[532,1279,721],{"class":538},[532,1281,730],{"class":538},[532,1283,1284],{"class":620}," template",[532,1286,630],{"class":538},[532,1288,1289],{"class":607},"FontSize",[532,1291,650],{"class":538},[532,1293,1294],{"class":809},"24",[532,1296,1297],{"class":538},"),",[532,1299,1284],{"class":620},[532,1301,630],{"class":538},[532,1303,1304],{"class":607},"Bold",[532,1306,1307],{"class":538},"())\n",[532,1309,1310],{"class":534,"line":865},[532,1311,1312],{"class":538},"        })\n",[532,1314,1315],{"class":534,"line":882},[532,1316,1317],{"class":538},"    })\n",[532,1319,1320],{"class":534,"line":917},[532,1321,550],{"emptyLinePlaceholder":549},[532,1323,1324,1327,1329,1331,1333,1335,1337,1340],{"class":534,"line":932},[532,1325,1326],{"class":620},"    data",[532,1328,730],{"class":538},[532,1330,706],{"class":620},[532,1332,624],{"class":538},[532,1334,1187],{"class":620},[532,1336,630],{"class":538},[532,1338,1339],{"class":607},"Generate",[532,1341,692],{"class":538},[532,1343,1344,1346,1348,1350,1352],{"class":534,"line":937},[532,1345,703],{"class":556},[532,1347,706],{"class":620},[532,1349,746],{"class":538},[532,1351,749],{"class":538},[532,1353,614],{"class":538},[532,1355,1356,1358,1360,1362,1364,1366],{"class":534,"line":942},[532,1357,757],{"class":620},[532,1359,630],{"class":538},[532,1361,762],{"class":607},[532,1363,650],{"class":538},[532,1365,767],{"class":620},[532,1367,593],{"class":538},[532,1369,1370],{"class":534,"line":977},[532,1371,775],{"class":538},[532,1373,1374,1376,1378,1380,1383,1385,1388,1390,1392,1394,1396,1398,1401,1403,1406,1408,1410,1412,1414],{"class":534,"line":992},[532,1375,703],{"class":556},[532,1377,706],{"class":620},[532,1379,624],{"class":538},[532,1381,1382],{"class":620}," os",[532,1384,630],{"class":538},[532,1386,1387],{"class":607},"WriteFile",[532,1389,650],{"class":538},[532,1391,721],{"class":538},[532,1393,962],{"class":724},[532,1395,721],{"class":538},[532,1397,730],{"class":538},[532,1399,1400],{"class":620}," data",[532,1402,730],{"class":538},[532,1404,1405],{"class":809}," 0o644",[532,1407,741],{"class":538},[532,1409,706],{"class":620},[532,1411,746],{"class":538},[532,1413,749],{"class":538},[532,1415,614],{"class":538},[532,1417,1418,1420,1422,1424,1426,1428],{"class":534,"line":997},[532,1419,757],{"class":620},[532,1421,630],{"class":538},[532,1423,762],{"class":607},[532,1425,650],{"class":538},[532,1427,767],{"class":620},[532,1429,593],{"class":538},[532,1431,1433],{"class":534,"line":1432},31,[532,1434,775],{"class":538},[532,1436,1438],{"class":534,"line":1437},32,[532,1439,1000],{"class":538},[19,1441,1442,1443,1446],{},"Two things gone. The TTF file is no longer required at runtime — Helvetica is part of the standard 14 fonts, and gpdf bundles them. The ",[31,1444,1445],{},"SetX(40); SetY(80)"," is gone — the row sits inside the page margins automatically. What's added: a row with one column spanning all 12 units. That row scaffolding looks heavy for \"Hello, World!\", but it's the same scaffolding that handles a 100-page report, which is the whole point.",[14,1448,1450],{"id":1449},"before-after-2-a-4-column-header-row","Before / After 2: a 4-column header row",[19,1452,1453],{},"This is the place coordinate math is most visible. You want a header strip across the page with four equal cells: page width minus margins, divided by four. In gopdf you do that division. In gpdf you spend 12 units four ways.",[19,1455,1456],{},[22,1457,521],{},[523,1459,1461],{"className":525,"code":1460,"language":527,"meta":528,"style":528},"const (\n    pageWidth   = 595.28 // A4 in points\n    leftMargin  = 40.0\n    rightMargin = 40.0\n    rowY        = 100.0\n    rowH        = 24.0\n)\n\ncontentWidth := pageWidth - leftMargin - rightMargin // 515.28\ncolW := contentWidth / 4                              // 128.82\n\npdf.SetFont(\"helvetica-bold\", \"\", 11)\npdf.SetFillColor(26, 35, 126)\npdf.SetTextColor(255, 255, 255)\n\nheaders := []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"}\nfor i, h := range headers {\n    x := leftMargin + colW*float64(i)\n    pdf.RectFromUpperLeftWithStyle(x, rowY, colW, rowH, \"F\")\n\n    pdf.SetXY(x+6, rowY+7)\n    if err := pdf.Cell(nil, h); err != nil {\n        log.Fatal(err)\n    }\n}\n\npdf.SetTextColor(0, 0, 0)\n",[31,1462,1463,1470,1485,1495,1504,1514,1524,1528,1532,1556,1575,1579,1608,1634,1659,1663,1715,1739,1767,1806,1810,1838,1867,1881,1885,1889,1893],{"__ignoreMap":528},[532,1464,1465,1468],{"class":534,"line":535},[532,1466,1467],{"class":538},"const",[532,1469,560],{"class":538},[532,1471,1472,1475,1478,1481],{"class":534,"line":546},[532,1473,1474],{"class":620},"    pageWidth   ",[532,1476,1477],{"class":538},"=",[532,1479,1480],{"class":809}," 595.28",[532,1482,1484],{"class":1483},"sHwdD"," // A4 in points\n",[532,1486,1487,1490,1492],{"class":534,"line":553},[532,1488,1489],{"class":620},"    leftMargin  ",[532,1491,1477],{"class":538},[532,1493,1494],{"class":809}," 40.0\n",[532,1496,1497,1500,1502],{"class":534,"line":563},[532,1498,1499],{"class":620},"    rightMargin ",[532,1501,1477],{"class":538},[532,1503,1494],{"class":809},[532,1505,1506,1509,1511],{"class":534,"line":575},[532,1507,1508],{"class":620},"    rowY        ",[532,1510,1477],{"class":538},[532,1512,1513],{"class":809}," 100.0\n",[532,1515,1516,1519,1521],{"class":534,"line":580},[532,1517,1518],{"class":620},"    rowH        ",[532,1520,1477],{"class":538},[532,1522,1523],{"class":809}," 24.0\n",[532,1525,1526],{"class":534,"line":590},[532,1527,593],{"class":538},[532,1529,1530],{"class":534,"line":596},[532,1531,550],{"emptyLinePlaceholder":549},[532,1533,1534,1537,1539,1542,1545,1548,1550,1553],{"class":534,"line":601},[532,1535,1536],{"class":620},"contentWidth ",[532,1538,624],{"class":538},[532,1540,1541],{"class":620}," pageWidth ",[532,1543,1544],{"class":538},"-",[532,1546,1547],{"class":620}," leftMargin ",[532,1549,1544],{"class":538},[532,1551,1552],{"class":620}," rightMargin ",[532,1554,1555],{"class":1483},"// 515.28\n",[532,1557,1558,1561,1563,1566,1569,1572],{"class":534,"line":617},[532,1559,1560],{"class":620},"colW ",[532,1562,624],{"class":538},[532,1564,1565],{"class":620}," contentWidth ",[532,1567,1568],{"class":538},"/",[532,1570,1571],{"class":809}," 4",[532,1573,1574],{"class":1483},"                              // 128.82\n",[532,1576,1577],{"class":534,"line":639},[532,1578,550],{"emptyLinePlaceholder":549},[532,1580,1581,1584,1586,1588,1590,1592,1595,1597,1599,1601,1603,1606],{"class":534,"line":683},[532,1582,1583],{"class":620},"pdf",[532,1585,630],{"class":538},[532,1587,791],{"class":607},[532,1589,650],{"class":538},[532,1591,721],{"class":538},[532,1593,1594],{"class":724},"helvetica-bold",[532,1596,721],{"class":538},[532,1598,730],{"class":538},[532,1600,804],{"class":538},[532,1602,730],{"class":538},[532,1604,1605],{"class":809}," 11",[532,1607,593],{"class":538},[532,1609,1610,1612,1614,1617,1619,1622,1624,1627,1629,1632],{"class":534,"line":695},[532,1611,1583],{"class":620},[532,1613,630],{"class":538},[532,1615,1616],{"class":607},"SetFillColor",[532,1618,650],{"class":538},[532,1620,1621],{"class":809},"26",[532,1623,730],{"class":538},[532,1625,1626],{"class":809}," 35",[532,1628,730],{"class":538},[532,1630,1631],{"class":809}," 126",[532,1633,593],{"class":538},[532,1635,1636,1638,1640,1643,1645,1648,1650,1653,1655,1657],{"class":534,"line":700},[532,1637,1583],{"class":620},[532,1639,630],{"class":538},[532,1641,1642],{"class":607},"SetTextColor",[532,1644,650],{"class":538},[532,1646,1647],{"class":809},"255",[532,1649,730],{"class":538},[532,1651,1652],{"class":809}," 255",[532,1654,730],{"class":538},[532,1656,1652],{"class":809},[532,1658,593],{"class":538},[532,1660,1661],{"class":534,"line":754},[532,1662,550],{"emptyLinePlaceholder":549},[532,1664,1665,1668,1670,1673,1677,1679,1681,1684,1686,1688,1690,1693,1695,1697,1699,1702,1704,1706,1708,1711,1713],{"class":534,"line":772},[532,1666,1667],{"class":620},"headers ",[532,1669,624],{"class":538},[532,1671,1672],{"class":538}," []",[532,1674,1676],{"class":1675},"spNyl","string",[532,1678,661],{"class":538},[532,1680,721],{"class":538},[532,1682,1683],{"class":724},"Description",[532,1685,721],{"class":538},[532,1687,730],{"class":538},[532,1689,733],{"class":538},[532,1691,1692],{"class":724},"Qty",[532,1694,721],{"class":538},[532,1696,730],{"class":538},[532,1698,733],{"class":538},[532,1700,1701],{"class":724},"Unit",[532,1703,721],{"class":538},[532,1705,730],{"class":538},[532,1707,733],{"class":538},[532,1709,1710],{"class":724},"Amount",[532,1712,721],{"class":538},[532,1714,1000],{"class":538},[532,1716,1717,1720,1723,1725,1728,1730,1733,1736],{"class":534,"line":778},[532,1718,1719],{"class":556},"for",[532,1721,1722],{"class":620}," i",[532,1724,730],{"class":538},[532,1726,1727],{"class":620}," h ",[532,1729,624],{"class":538},[532,1731,1732],{"class":556}," range",[532,1734,1735],{"class":620}," headers ",[532,1737,1738],{"class":538},"{\n",[532,1740,1741,1744,1746,1748,1751,1754,1757,1760,1762,1765],{"class":534,"line":823},[532,1742,1743],{"class":620},"    x ",[532,1745,624],{"class":538},[532,1747,1547],{"class":620},[532,1749,1750],{"class":538},"+",[532,1752,1753],{"class":620}," colW",[532,1755,1756],{"class":538},"*",[532,1758,1759],{"class":1675},"float64",[532,1761,650],{"class":538},[532,1763,1764],{"class":620},"i",[532,1766,593],{"class":538},[532,1768,1769,1771,1773,1776,1778,1781,1783,1786,1788,1790,1792,1795,1797,1799,1802,1804],{"class":534,"line":838},[532,1770,642],{"class":620},[532,1772,630],{"class":538},[532,1774,1775],{"class":607},"RectFromUpperLeftWithStyle",[532,1777,650],{"class":538},[532,1779,1780],{"class":620},"x",[532,1782,730],{"class":538},[532,1784,1785],{"class":620}," rowY",[532,1787,730],{"class":538},[532,1789,1753],{"class":620},[532,1791,730],{"class":538},[532,1793,1794],{"class":620}," rowH",[532,1796,730],{"class":538},[532,1798,733],{"class":538},[532,1800,1801],{"class":724},"F",[532,1803,721],{"class":538},[532,1805,593],{"class":538},[532,1807,1808],{"class":534,"line":843},[532,1809,550],{"emptyLinePlaceholder":549},[532,1811,1812,1814,1816,1818,1820,1822,1824,1827,1829,1831,1833,1836],{"class":534,"line":848},[532,1813,642],{"class":620},[532,1815,630],{"class":538},[532,1817,33],{"class":607},[532,1819,650],{"class":538},[532,1821,1780],{"class":620},[532,1823,1750],{"class":538},[532,1825,1826],{"class":809},"6",[532,1828,730],{"class":538},[532,1830,1785],{"class":620},[532,1832,1750],{"class":538},[532,1834,1835],{"class":809},"7",[532,1837,593],{"class":538},[532,1839,1840,1842,1844,1846,1848,1850,1852,1854,1857,1859,1861,1863,1865],{"class":534,"line":865},[532,1841,703],{"class":556},[532,1843,706],{"class":620},[532,1845,624],{"class":538},[532,1847,711],{"class":620},[532,1849,630],{"class":538},[532,1851,37],{"class":607},[532,1853,897],{"class":538},[532,1855,1856],{"class":620}," h",[532,1858,741],{"class":538},[532,1860,706],{"class":620},[532,1862,746],{"class":538},[532,1864,749],{"class":538},[532,1866,614],{"class":538},[532,1868,1869,1871,1873,1875,1877,1879],{"class":534,"line":882},[532,1870,757],{"class":620},[532,1872,630],{"class":538},[532,1874,762],{"class":607},[532,1876,650],{"class":538},[532,1878,767],{"class":620},[532,1880,593],{"class":538},[532,1882,1883],{"class":534,"line":917},[532,1884,775],{"class":538},[532,1886,1887],{"class":534,"line":932},[532,1888,1000],{"class":538},[532,1890,1891],{"class":534,"line":937},[532,1892,550],{"emptyLinePlaceholder":549},[532,1894,1895,1897,1899,1901,1903,1906,1908,1911,1913,1915],{"class":534,"line":942},[532,1896,1583],{"class":620},[532,1898,630],{"class":538},[532,1900,1642],{"class":607},[532,1902,650],{"class":538},[532,1904,1905],{"class":809},"0",[532,1907,730],{"class":538},[532,1909,1910],{"class":809}," 0",[532,1912,730],{"class":538},[532,1914,1910],{"class":809},[532,1916,593],{"class":538},[19,1918,1919,1920,1923,1924,1926],{},"There are four constants. There's a width subtraction. There's a division. There's a loop with ",[31,1921,1922],{},"colW*float64(i)"," — and that float cast was only there because Go's ",[31,1925,1756],{}," doesn't promote int to float64. None of those exist in the gpdf version.",[19,1928,1929],{},[22,1930,1005],{},[523,1932,1934],{"className":525,"code":1933,"language":527,"meta":528,"style":528},"page.AutoRow(func(r *template.RowBuilder) {\n    headers := []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"}\n    for _, h := range headers {\n        r.Col(3, func(c *template.ColBuilder) {\n            c.Box(\n                template.BgColor(pdf.RGBHex(0x1A237E)),\n                template.Padding(document.Mm(2), document.Mm(3)),\n            )\n            c.Text(h,\n                template.Bold(), template.FontSize(11),\n                template.TextColor(pdf.White),\n            )\n        })\n    }\n})\n",[31,1935,1936,1961,2006,2026,2057,2068,2095,2132,2137,2153,2177,2197,2201,2205,2209],{"__ignoreMap":528},[532,1937,1938,1941,1943,1945,1947,1949,1951,1953,1955,1957,1959],{"class":534,"line":535},[532,1939,1940],{"class":620},"page",[532,1942,630],{"class":538},[532,1944,1203],{"class":607},[532,1946,1206],{"class":538},[532,1948,1210],{"class":1209},[532,1950,670],{"class":538},[532,1952,1215],{"class":542},[532,1954,630],{"class":538},[532,1956,1220],{"class":542},[532,1958,1223],{"class":538},[532,1960,614],{"class":538},[532,1962,1963,1966,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000,2002,2004],{"class":534,"line":546},[532,1964,1965],{"class":620},"    headers ",[532,1967,624],{"class":538},[532,1969,1672],{"class":538},[532,1971,1676],{"class":1675},[532,1973,661],{"class":538},[532,1975,721],{"class":538},[532,1977,1683],{"class":724},[532,1979,721],{"class":538},[532,1981,730],{"class":538},[532,1983,733],{"class":538},[532,1985,1692],{"class":724},[532,1987,721],{"class":538},[532,1989,730],{"class":538},[532,1991,733],{"class":538},[532,1993,1701],{"class":724},[532,1995,721],{"class":538},[532,1997,730],{"class":538},[532,1999,733],{"class":538},[532,2001,1710],{"class":724},[532,2003,721],{"class":538},[532,2005,1000],{"class":538},[532,2007,2008,2011,2014,2016,2018,2020,2022,2024],{"class":534,"line":553},[532,2009,2010],{"class":556},"    for",[532,2012,2013],{"class":620}," _",[532,2015,730],{"class":538},[532,2017,1727],{"class":620},[532,2019,624],{"class":538},[532,2021,1732],{"class":556},[532,2023,1735],{"class":620},[532,2025,1738],{"class":538},[532,2027,2028,2030,2032,2034,2036,2039,2041,2043,2045,2047,2049,2051,2053,2055],{"class":534,"line":563},[532,2029,1230],{"class":620},[532,2031,630],{"class":538},[532,2033,1235],{"class":607},[532,2035,650],{"class":538},[532,2037,2038],{"class":809},"3",[532,2040,730],{"class":538},[532,2042,1245],{"class":538},[532,2044,1248],{"class":1209},[532,2046,670],{"class":538},[532,2048,1215],{"class":542},[532,2050,630],{"class":538},[532,2052,1257],{"class":542},[532,2054,1223],{"class":538},[532,2056,614],{"class":538},[532,2058,2059,2061,2063,2066],{"class":534,"line":575},[532,2060,1266],{"class":620},[532,2062,630],{"class":538},[532,2064,2065],{"class":607},"Box",[532,2067,1110],{"class":538},[532,2069,2070,2073,2075,2078,2080,2082,2084,2087,2089,2092],{"class":534,"line":580},[532,2071,2072],{"class":620},"                template",[532,2074,630],{"class":538},[532,2076,2077],{"class":607},"BgColor",[532,2079,650],{"class":538},[532,2081,1583],{"class":620},[532,2083,630],{"class":538},[532,2085,2086],{"class":607},"RGBHex",[532,2088,650],{"class":538},[532,2090,2091],{"class":809},"0x1A237E",[532,2093,2094],{"class":538},")),\n",[532,2096,2097,2099,2101,2104,2106,2108,2110,2112,2114,2117,2119,2122,2124,2126,2128,2130],{"class":534,"line":590},[532,2098,2072],{"class":620},[532,2100,630],{"class":538},[532,2102,2103],{"class":607},"Padding",[532,2105,650],{"class":538},[532,2107,1125],{"class":620},[532,2109,630],{"class":538},[532,2111,1160],{"class":607},[532,2113,650],{"class":538},[532,2115,2116],{"class":809},"2",[532,2118,1297],{"class":538},[532,2120,2121],{"class":620}," document",[532,2123,630],{"class":538},[532,2125,1160],{"class":607},[532,2127,650],{"class":538},[532,2129,2038],{"class":809},[532,2131,2094],{"class":538},[532,2133,2134],{"class":534,"line":596},[532,2135,2136],{"class":538},"            )\n",[532,2138,2139,2141,2143,2145,2147,2150],{"class":534,"line":601},[532,2140,1266],{"class":620},[532,2142,630],{"class":538},[532,2144,1271],{"class":607},[532,2146,650],{"class":538},[532,2148,2149],{"class":620},"h",[532,2151,2152],{"class":538},",\n",[532,2154,2155,2157,2159,2161,2164,2166,2168,2170,2172,2175],{"class":534,"line":617},[532,2156,2072],{"class":620},[532,2158,630],{"class":538},[532,2160,1304],{"class":607},[532,2162,2163],{"class":538},"(),",[532,2165,1284],{"class":620},[532,2167,630],{"class":538},[532,2169,1289],{"class":607},[532,2171,650],{"class":538},[532,2173,2174],{"class":809},"11",[532,2176,1133],{"class":538},[532,2178,2179,2181,2183,2186,2188,2190,2192,2195],{"class":534,"line":639},[532,2180,2072],{"class":620},[532,2182,630],{"class":538},[532,2184,2185],{"class":607},"TextColor",[532,2187,650],{"class":538},[532,2189,1583],{"class":620},[532,2191,630],{"class":538},[532,2193,2194],{"class":620},"White",[532,2196,1133],{"class":538},[532,2198,2199],{"class":534,"line":683},[532,2200,2136],{"class":538},[532,2202,2203],{"class":534,"line":695},[532,2204,1312],{"class":538},[532,2206,2207],{"class":534,"line":700},[532,2208,775],{"class":538},[532,2210,2211],{"class":534,"line":754},[532,2212,680],{"class":538},[19,2214,2215,2217,2218,2221,2222,2224,2225,2228,2229,2232],{},[31,2216,124],{}," four times sums to 12. The grid handles widths. If you change A4 to Letter, or shrink margins, the header still tiles correctly because nothing in this code depends on ",[31,2219,2220],{},"pageWidth"," at all. If you decide column 1 should be twice as wide as the other three, change ",[31,2223,124],{}," to ",[31,2226,2227],{},"r.Col(6, ...)"," for that one and one of the others to ",[31,2230,2231],{},"r.Col(2, ...)",". No arithmetic.",[14,2234,2236],{"id":2235},"before-after-3-an-invoice-table-that-breaks-across-pages","Before / After 3: an invoice table that breaks across pages",[19,2238,2239,2240,2242],{},"The big one. In gopdf, drawing a table that flows over multiple pages is mostly bookkeeping: you track the current y, draw each row, check if the next row will fit, and if not, call ",[31,2241,57],{}," and reprint the header. The state machine is in your code.",[19,2244,2245],{},[22,2246,521],{},[523,2248,2250],{"className":525,"code":2249,"language":527,"meta":528,"style":528},"func drawInvoiceTable(pdf *gopdf.GoPdf, items [][4]string) error {\n    const (\n        pageH       = 841.89 // A4 height\n        bottomLimit = pageH - 40\n        rowH        = 22.0\n        leftX       = 40.0\n        widths      = 4\n    )\n    cols := []float64{260, 80, 80, 95} // Description, Qty, Unit, Amount\n\n    // Header function we'll call on first page and after page breaks.\n    drawHeader := func(y float64) float64 {\n        pdf.SetFont(\"helvetica-bold\", \"\", 11)\n        pdf.SetFillColor(26, 35, 126)\n        pdf.SetTextColor(255, 255, 255)\n        x := leftX\n        for i, h := range []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"} {\n            pdf.RectFromUpperLeftWithStyle(x, y, cols[i], rowH, \"F\")\n            pdf.SetXY(x+6, y+7)\n            if err := pdf.Cell(nil, h); err != nil {\n                log.Println(err)\n            }\n            x += cols[i]\n        }\n        pdf.SetTextColor(0, 0, 0)\n        pdf.SetFont(\"helvetica\", \"\", 11)\n        return y + rowH\n    }\n\n    y := drawHeader(100)\n    for _, row := range items {\n        if y+rowH > bottomLimit {\n            pdf.AddPage()\n            y = drawHeader(60)\n        }\n\n        x := leftX\n        for i, cell := range row {\n            pdf.RectFromUpperLeftWithStyle(x, y, cols[i], rowH, \"D\") // border only\n            pdf.SetXY(x+6, y+7)\n            if err := pdf.Cell(nil, cell); err != nil {\n                return err\n            }\n            x += cols[i]\n        }\n        y += rowH\n    }\n    return nil\n}\n",[31,2251,2252,2294,2301,2314,2329,2339,2348,2358,2362,2398,2402,2407,2428,2455,2477,2499,2509,2564,2607,2633,2662,2678,2683,2700,2705,2727,2753,2766,2770,2774,2791,2811,2831,2842,2859,2864,2869,2878,2898,2941,2968,2998,3007,3012,3027,3032,3042,3047,3056],{"__ignoreMap":528},[532,2253,2254,2256,2259,2261,2263,2265,2267,2269,2271,2273,2276,2279,2282,2285,2287,2289,2292],{"class":534,"line":535},[532,2255,604],{"class":538},[532,2257,2258],{"class":607}," drawInvoiceTable",[532,2260,650],{"class":538},[532,2262,1583],{"class":1209},[532,2264,670],{"class":538},[532,2266,653],{"class":542},[532,2268,630],{"class":538},[532,2270,633],{"class":542},[532,2272,730],{"class":538},[532,2274,2275],{"class":1209}," items",[532,2277,2278],{"class":538}," [][",[532,2280,2281],{"class":809},"4",[532,2283,2284],{"class":538},"]",[532,2286,1676],{"class":1675},[532,2288,1223],{"class":538},[532,2290,2291],{"class":1675}," error",[532,2293,614],{"class":538},[532,2295,2296,2299],{"class":534,"line":546},[532,2297,2298],{"class":538},"    const",[532,2300,560],{"class":538},[532,2302,2303,2306,2308,2311],{"class":534,"line":553},[532,2304,2305],{"class":620},"        pageH       ",[532,2307,1477],{"class":538},[532,2309,2310],{"class":809}," 841.89",[532,2312,2313],{"class":1483}," // A4 height\n",[532,2315,2316,2319,2321,2324,2326],{"class":534,"line":563},[532,2317,2318],{"class":620},"        bottomLimit ",[532,2320,1477],{"class":538},[532,2322,2323],{"class":620}," pageH ",[532,2325,1544],{"class":538},[532,2327,2328],{"class":809}," 40\n",[532,2330,2331,2334,2336],{"class":534,"line":575},[532,2332,2333],{"class":620},"        rowH        ",[532,2335,1477],{"class":538},[532,2337,2338],{"class":809}," 22.0\n",[532,2340,2341,2344,2346],{"class":534,"line":580},[532,2342,2343],{"class":620},"        leftX       ",[532,2345,1477],{"class":538},[532,2347,1494],{"class":809},[532,2349,2350,2353,2355],{"class":534,"line":590},[532,2351,2352],{"class":620},"        widths      ",[532,2354,1477],{"class":538},[532,2356,2357],{"class":809}," 4\n",[532,2359,2360],{"class":534,"line":596},[532,2361,1173],{"class":538},[532,2363,2364,2367,2369,2371,2373,2375,2378,2380,2383,2385,2387,2389,2392,2395],{"class":534,"line":601},[532,2365,2366],{"class":620},"    cols ",[532,2368,624],{"class":538},[532,2370,1672],{"class":538},[532,2372,1759],{"class":1675},[532,2374,661],{"class":538},[532,2376,2377],{"class":809},"260",[532,2379,730],{"class":538},[532,2381,2382],{"class":809}," 80",[532,2384,730],{"class":538},[532,2386,2382],{"class":809},[532,2388,730],{"class":538},[532,2390,2391],{"class":809}," 95",[532,2393,2394],{"class":538},"}",[532,2396,2397],{"class":1483}," // Description, Qty, Unit, Amount\n",[532,2399,2400],{"class":534,"line":617},[532,2401,550],{"emptyLinePlaceholder":549},[532,2403,2404],{"class":534,"line":639},[532,2405,2406],{"class":1483},"    // Header function we'll call on first page and after page breaks.\n",[532,2408,2409,2412,2414,2416,2419,2422,2424,2426],{"class":534,"line":683},[532,2410,2411],{"class":620},"    drawHeader ",[532,2413,624],{"class":538},[532,2415,1245],{"class":538},[532,2417,2418],{"class":1209},"y",[532,2420,2421],{"class":1675}," float64",[532,2423,1223],{"class":538},[532,2425,2421],{"class":1675},[532,2427,614],{"class":538},[532,2429,2430,2433,2435,2437,2439,2441,2443,2445,2447,2449,2451,2453],{"class":534,"line":695},[532,2431,2432],{"class":620},"        pdf",[532,2434,630],{"class":538},[532,2436,791],{"class":607},[532,2438,650],{"class":538},[532,2440,721],{"class":538},[532,2442,1594],{"class":724},[532,2444,721],{"class":538},[532,2446,730],{"class":538},[532,2448,804],{"class":538},[532,2450,730],{"class":538},[532,2452,1605],{"class":809},[532,2454,593],{"class":538},[532,2456,2457,2459,2461,2463,2465,2467,2469,2471,2473,2475],{"class":534,"line":700},[532,2458,2432],{"class":620},[532,2460,630],{"class":538},[532,2462,1616],{"class":607},[532,2464,650],{"class":538},[532,2466,1621],{"class":809},[532,2468,730],{"class":538},[532,2470,1626],{"class":809},[532,2472,730],{"class":538},[532,2474,1631],{"class":809},[532,2476,593],{"class":538},[532,2478,2479,2481,2483,2485,2487,2489,2491,2493,2495,2497],{"class":534,"line":754},[532,2480,2432],{"class":620},[532,2482,630],{"class":538},[532,2484,1642],{"class":607},[532,2486,650],{"class":538},[532,2488,1647],{"class":809},[532,2490,730],{"class":538},[532,2492,1652],{"class":809},[532,2494,730],{"class":538},[532,2496,1652],{"class":809},[532,2498,593],{"class":538},[532,2500,2501,2504,2506],{"class":534,"line":772},[532,2502,2503],{"class":620},"        x ",[532,2505,624],{"class":538},[532,2507,2508],{"class":620}," leftX\n",[532,2510,2511,2514,2516,2518,2520,2522,2524,2526,2528,2530,2532,2534,2536,2538,2540,2542,2544,2546,2548,2550,2552,2554,2556,2558,2560,2562],{"class":534,"line":778},[532,2512,2513],{"class":556},"        for",[532,2515,1722],{"class":620},[532,2517,730],{"class":538},[532,2519,1727],{"class":620},[532,2521,624],{"class":538},[532,2523,1732],{"class":556},[532,2525,1672],{"class":538},[532,2527,1676],{"class":1675},[532,2529,661],{"class":538},[532,2531,721],{"class":538},[532,2533,1683],{"class":724},[532,2535,721],{"class":538},[532,2537,730],{"class":538},[532,2539,733],{"class":538},[532,2541,1692],{"class":724},[532,2543,721],{"class":538},[532,2545,730],{"class":538},[532,2547,733],{"class":538},[532,2549,1701],{"class":724},[532,2551,721],{"class":538},[532,2553,730],{"class":538},[532,2555,733],{"class":538},[532,2557,1710],{"class":724},[532,2559,721],{"class":538},[532,2561,2394],{"class":538},[532,2563,614],{"class":538},[532,2565,2566,2569,2571,2573,2575,2577,2579,2582,2584,2587,2590,2592,2595,2597,2599,2601,2603,2605],{"class":534,"line":823},[532,2567,2568],{"class":620},"            pdf",[532,2570,630],{"class":538},[532,2572,1775],{"class":607},[532,2574,650],{"class":538},[532,2576,1780],{"class":620},[532,2578,730],{"class":538},[532,2580,2581],{"class":620}," y",[532,2583,730],{"class":538},[532,2585,2586],{"class":620}," cols",[532,2588,2589],{"class":538},"[",[532,2591,1764],{"class":620},[532,2593,2594],{"class":538},"],",[532,2596,1794],{"class":620},[532,2598,730],{"class":538},[532,2600,733],{"class":538},[532,2602,1801],{"class":724},[532,2604,721],{"class":538},[532,2606,593],{"class":538},[532,2608,2609,2611,2613,2615,2617,2619,2621,2623,2625,2627,2629,2631],{"class":534,"line":838},[532,2610,2568],{"class":620},[532,2612,630],{"class":538},[532,2614,33],{"class":607},[532,2616,650],{"class":538},[532,2618,1780],{"class":620},[532,2620,1750],{"class":538},[532,2622,1826],{"class":809},[532,2624,730],{"class":538},[532,2626,2581],{"class":620},[532,2628,1750],{"class":538},[532,2630,1835],{"class":809},[532,2632,593],{"class":538},[532,2634,2635,2638,2640,2642,2644,2646,2648,2650,2652,2654,2656,2658,2660],{"class":534,"line":843},[532,2636,2637],{"class":556},"            if",[532,2639,706],{"class":620},[532,2641,624],{"class":538},[532,2643,711],{"class":620},[532,2645,630],{"class":538},[532,2647,37],{"class":607},[532,2649,897],{"class":538},[532,2651,1856],{"class":620},[532,2653,741],{"class":538},[532,2655,706],{"class":620},[532,2657,746],{"class":538},[532,2659,749],{"class":538},[532,2661,614],{"class":538},[532,2663,2664,2667,2669,2672,2674,2676],{"class":534,"line":848},[532,2665,2666],{"class":620},"                log",[532,2668,630],{"class":538},[532,2670,2671],{"class":607},"Println",[532,2673,650],{"class":538},[532,2675,767],{"class":620},[532,2677,593],{"class":538},[532,2679,2680],{"class":534,"line":865},[532,2681,2682],{"class":538},"            }\n",[532,2684,2685,2688,2691,2693,2695,2697],{"class":534,"line":882},[532,2686,2687],{"class":620},"            x ",[532,2689,2690],{"class":538},"+=",[532,2692,2586],{"class":620},[532,2694,2589],{"class":538},[532,2696,1764],{"class":620},[532,2698,2699],{"class":538},"]\n",[532,2701,2702],{"class":534,"line":917},[532,2703,2704],{"class":538},"        }\n",[532,2706,2707,2709,2711,2713,2715,2717,2719,2721,2723,2725],{"class":534,"line":932},[532,2708,2432],{"class":620},[532,2710,630],{"class":538},[532,2712,1642],{"class":607},[532,2714,650],{"class":538},[532,2716,1905],{"class":809},[532,2718,730],{"class":538},[532,2720,1910],{"class":809},[532,2722,730],{"class":538},[532,2724,1910],{"class":809},[532,2726,593],{"class":538},[532,2728,2729,2731,2733,2735,2737,2739,2741,2743,2745,2747,2749,2751],{"class":534,"line":937},[532,2730,2432],{"class":620},[532,2732,630],{"class":538},[532,2734,791],{"class":607},[532,2736,650],{"class":538},[532,2738,721],{"class":538},[532,2740,725],{"class":724},[532,2742,721],{"class":538},[532,2744,730],{"class":538},[532,2746,804],{"class":538},[532,2748,730],{"class":538},[532,2750,1605],{"class":809},[532,2752,593],{"class":538},[532,2754,2755,2758,2761,2763],{"class":534,"line":942},[532,2756,2757],{"class":556},"        return",[532,2759,2760],{"class":620}," y ",[532,2762,1750],{"class":538},[532,2764,2765],{"class":620}," rowH\n",[532,2767,2768],{"class":534,"line":977},[532,2769,775],{"class":538},[532,2771,2772],{"class":534,"line":992},[532,2773,550],{"emptyLinePlaceholder":549},[532,2775,2776,2779,2781,2784,2786,2789],{"class":534,"line":997},[532,2777,2778],{"class":620},"    y ",[532,2780,624],{"class":538},[532,2782,2783],{"class":607}," drawHeader",[532,2785,650],{"class":538},[532,2787,2788],{"class":809},"100",[532,2790,593],{"class":538},[532,2792,2793,2795,2797,2799,2802,2804,2806,2809],{"class":534,"line":1432},[532,2794,2010],{"class":556},[532,2796,2013],{"class":620},[532,2798,730],{"class":538},[532,2800,2801],{"class":620}," row ",[532,2803,624],{"class":538},[532,2805,1732],{"class":556},[532,2807,2808],{"class":620}," items ",[532,2810,1738],{"class":538},[532,2812,2813,2816,2818,2820,2823,2826,2829],{"class":534,"line":1437},[532,2814,2815],{"class":556},"        if",[532,2817,2581],{"class":620},[532,2819,1750],{"class":538},[532,2821,2822],{"class":620},"rowH ",[532,2824,2825],{"class":538},">",[532,2827,2828],{"class":620}," bottomLimit ",[532,2830,1738],{"class":538},[532,2832,2834,2836,2838,2840],{"class":534,"line":2833},33,[532,2835,2568],{"class":620},[532,2837,630],{"class":538},[532,2839,57],{"class":607},[532,2841,692],{"class":538},[532,2843,2845,2848,2850,2852,2854,2857],{"class":534,"line":2844},34,[532,2846,2847],{"class":620},"            y ",[532,2849,1477],{"class":538},[532,2851,2783],{"class":607},[532,2853,650],{"class":538},[532,2855,2856],{"class":809},"60",[532,2858,593],{"class":538},[532,2860,2862],{"class":534,"line":2861},35,[532,2863,2704],{"class":538},[532,2865,2867],{"class":534,"line":2866},36,[532,2868,550],{"emptyLinePlaceholder":549},[532,2870,2872,2874,2876],{"class":534,"line":2871},37,[532,2873,2503],{"class":620},[532,2875,624],{"class":538},[532,2877,2508],{"class":620},[532,2879,2881,2883,2885,2887,2890,2892,2894,2896],{"class":534,"line":2880},38,[532,2882,2513],{"class":556},[532,2884,1722],{"class":620},[532,2886,730],{"class":538},[532,2888,2889],{"class":620}," cell ",[532,2891,624],{"class":538},[532,2893,1732],{"class":556},[532,2895,2801],{"class":620},[532,2897,1738],{"class":538},[532,2899,2901,2903,2905,2907,2909,2911,2913,2915,2917,2919,2921,2923,2925,2927,2929,2931,2934,2936,2938],{"class":534,"line":2900},39,[532,2902,2568],{"class":620},[532,2904,630],{"class":538},[532,2906,1775],{"class":607},[532,2908,650],{"class":538},[532,2910,1780],{"class":620},[532,2912,730],{"class":538},[532,2914,2581],{"class":620},[532,2916,730],{"class":538},[532,2918,2586],{"class":620},[532,2920,2589],{"class":538},[532,2922,1764],{"class":620},[532,2924,2594],{"class":538},[532,2926,1794],{"class":620},[532,2928,730],{"class":538},[532,2930,733],{"class":538},[532,2932,2933],{"class":724},"D",[532,2935,721],{"class":538},[532,2937,1223],{"class":538},[532,2939,2940],{"class":1483}," // border only\n",[532,2942,2944,2946,2948,2950,2952,2954,2956,2958,2960,2962,2964,2966],{"class":534,"line":2943},40,[532,2945,2568],{"class":620},[532,2947,630],{"class":538},[532,2949,33],{"class":607},[532,2951,650],{"class":538},[532,2953,1780],{"class":620},[532,2955,1750],{"class":538},[532,2957,1826],{"class":809},[532,2959,730],{"class":538},[532,2961,2581],{"class":620},[532,2963,1750],{"class":538},[532,2965,1835],{"class":809},[532,2967,593],{"class":538},[532,2969,2971,2973,2975,2977,2979,2981,2983,2985,2988,2990,2992,2994,2996],{"class":534,"line":2970},41,[532,2972,2637],{"class":556},[532,2974,706],{"class":620},[532,2976,624],{"class":538},[532,2978,711],{"class":620},[532,2980,630],{"class":538},[532,2982,37],{"class":607},[532,2984,897],{"class":538},[532,2986,2987],{"class":620}," cell",[532,2989,741],{"class":538},[532,2991,706],{"class":620},[532,2993,746],{"class":538},[532,2995,749],{"class":538},[532,2997,614],{"class":538},[532,2999,3001,3004],{"class":534,"line":3000},42,[532,3002,3003],{"class":556},"                return",[532,3005,3006],{"class":620}," err\n",[532,3008,3010],{"class":534,"line":3009},43,[532,3011,2682],{"class":538},[532,3013,3015,3017,3019,3021,3023,3025],{"class":534,"line":3014},44,[532,3016,2687],{"class":620},[532,3018,2690],{"class":538},[532,3020,2586],{"class":620},[532,3022,2589],{"class":538},[532,3024,1764],{"class":620},[532,3026,2699],{"class":538},[532,3028,3030],{"class":534,"line":3029},45,[532,3031,2704],{"class":538},[532,3033,3035,3038,3040],{"class":534,"line":3034},46,[532,3036,3037],{"class":620},"        y ",[532,3039,2690],{"class":538},[532,3041,2765],{"class":620},[532,3043,3045],{"class":534,"line":3044},47,[532,3046,775],{"class":538},[532,3048,3050,3053],{"class":534,"line":3049},48,[532,3051,3052],{"class":556},"    return",[532,3054,3055],{"class":538}," nil\n",[532,3057,3059],{"class":534,"line":3058},49,[532,3060,1000],{"class":538},[19,3062,3063,3064,3066],{},"The table-drawing function is 30 lines, and only 5 of them are about the data. The rest is layout: hard-coded heights, a hard-coded bottom limit, a closure to redraw the header after page breaks, two ",[31,3065,1719],{}," loops, two cursor advances per cell. This is the median gopdf table.",[19,3068,3069],{},[22,3070,1005],{},[523,3072,3074],{"className":525,"code":3073,"language":527,"meta":528,"style":528},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"Description\", \"Qty\", \"Unit\", \"Amount\"},\n            items, // [][]string\n            template.ColumnWidths(55, 15, 15, 15),\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",[31,3075,3076,3100,3131,3143,3185,3195,3225,3236,3247,3265,3287,3292,3316,3321,3325],{"__ignoreMap":528},[532,3077,3078,3080,3082,3084,3086,3088,3090,3092,3094,3096,3098],{"class":534,"line":535},[532,3079,1940],{"class":620},[532,3081,630],{"class":538},[532,3083,1203],{"class":607},[532,3085,1206],{"class":538},[532,3087,1210],{"class":1209},[532,3089,670],{"class":538},[532,3091,1215],{"class":542},[532,3093,630],{"class":538},[532,3095,1220],{"class":542},[532,3097,1223],{"class":538},[532,3099,614],{"class":538},[532,3101,3102,3105,3107,3109,3111,3113,3115,3117,3119,3121,3123,3125,3127,3129],{"class":534,"line":546},[532,3103,3104],{"class":620},"    r",[532,3106,630],{"class":538},[532,3108,1235],{"class":607},[532,3110,650],{"class":538},[532,3112,1240],{"class":809},[532,3114,730],{"class":538},[532,3116,1245],{"class":538},[532,3118,1248],{"class":1209},[532,3120,670],{"class":538},[532,3122,1215],{"class":542},[532,3124,630],{"class":538},[532,3126,1257],{"class":542},[532,3128,1223],{"class":538},[532,3130,614],{"class":538},[532,3132,3133,3136,3138,3141],{"class":534,"line":553},[532,3134,3135],{"class":620},"        c",[532,3137,630],{"class":538},[532,3139,3140],{"class":607},"Table",[532,3142,1110],{"class":538},[532,3144,3145,3148,3150,3152,3154,3156,3158,3160,3162,3164,3166,3168,3170,3172,3174,3176,3178,3180,3182],{"class":534,"line":563},[532,3146,3147],{"class":538},"            []",[532,3149,1676],{"class":1675},[532,3151,661],{"class":538},[532,3153,721],{"class":538},[532,3155,1683],{"class":724},[532,3157,721],{"class":538},[532,3159,730],{"class":538},[532,3161,733],{"class":538},[532,3163,1692],{"class":724},[532,3165,721],{"class":538},[532,3167,730],{"class":538},[532,3169,733],{"class":538},[532,3171,1701],{"class":724},[532,3173,721],{"class":538},[532,3175,730],{"class":538},[532,3177,733],{"class":538},[532,3179,1710],{"class":724},[532,3181,721],{"class":538},[532,3183,3184],{"class":538},"},\n",[532,3186,3187,3190,3192],{"class":534,"line":575},[532,3188,3189],{"class":620},"            items",[532,3191,730],{"class":538},[532,3193,3194],{"class":1483}," // [][]string\n",[532,3196,3197,3200,3202,3205,3207,3210,3212,3215,3217,3219,3221,3223],{"class":534,"line":580},[532,3198,3199],{"class":620},"            template",[532,3201,630],{"class":538},[532,3203,3204],{"class":607},"ColumnWidths",[532,3206,650],{"class":538},[532,3208,3209],{"class":809},"55",[532,3211,730],{"class":538},[532,3213,3214],{"class":809}," 15",[532,3216,730],{"class":538},[532,3218,3214],{"class":809},[532,3220,730],{"class":538},[532,3222,3214],{"class":809},[532,3224,1133],{"class":538},[532,3226,3227,3229,3231,3234],{"class":534,"line":590},[532,3228,3199],{"class":620},[532,3230,630],{"class":538},[532,3232,3233],{"class":607},"TableHeaderStyle",[532,3235,1110],{"class":538},[532,3237,3238,3240,3242,3244],{"class":534,"line":596},[532,3239,2072],{"class":620},[532,3241,630],{"class":538},[532,3243,1304],{"class":607},[532,3245,3246],{"class":538},"(),\n",[532,3248,3249,3251,3253,3255,3257,3259,3261,3263],{"class":534,"line":601},[532,3250,2072],{"class":620},[532,3252,630],{"class":538},[532,3254,2185],{"class":607},[532,3256,650],{"class":538},[532,3258,1583],{"class":620},[532,3260,630],{"class":538},[532,3262,2194],{"class":620},[532,3264,1133],{"class":538},[532,3266,3267,3269,3271,3273,3275,3277,3279,3281,3283,3285],{"class":534,"line":617},[532,3268,2072],{"class":620},[532,3270,630],{"class":538},[532,3272,2077],{"class":607},[532,3274,650],{"class":538},[532,3276,1583],{"class":620},[532,3278,630],{"class":538},[532,3280,2086],{"class":607},[532,3282,650],{"class":538},[532,3284,2091],{"class":809},[532,3286,2094],{"class":538},[532,3288,3289],{"class":534,"line":639},[532,3290,3291],{"class":538},"            ),\n",[532,3293,3294,3296,3298,3301,3303,3305,3307,3309,3311,3314],{"class":534,"line":683},[532,3295,3199],{"class":620},[532,3297,630],{"class":538},[532,3299,3300],{"class":607},"TableStripe",[532,3302,650],{"class":538},[532,3304,1583],{"class":620},[532,3306,630],{"class":538},[532,3308,2086],{"class":607},[532,3310,650],{"class":538},[532,3312,3313],{"class":809},"0xF5F5F5",[532,3315,2094],{"class":538},[532,3317,3318],{"class":534,"line":695},[532,3319,3320],{"class":538},"        )\n",[532,3322,3323],{"class":534,"line":700},[532,3324,1317],{"class":538},[532,3326,3327],{"class":534,"line":754},[532,3328,680],{"class":538},[19,3330,3331,3332,3334],{},"That's it. Page breaks are automatic. The header repeats on every page where the body continues. Striped rows take one option. Column widths are percentages of the container, so this same table inside ",[31,3333,2227],{}," would render at half size with the same proportions, no rewrites. The 25-line gopdf bookkeeping function disappears.",[19,3336,3337,3338,3341,3342,3345],{},"A specific number worth seeing. The 100-row invoice render benchmarks at ",[22,3339,3340],{},"108 µs"," in gpdf and roughly ",[22,3343,3344],{},"2.4 ms"," in signintech/gopdf — and the gopdf number depends on the cell-by-cell pattern you wrote, so it varies. The factor isn't the headline; the disappearance of the function is.",[14,3347,3349],{"id":3348},"before-after-4-an-image-positioned-next-to-a-paragraph","Before / After 4: an image positioned next to a paragraph",[19,3351,3352],{},"Common pattern: company logo on the left, address block on the right.",[19,3354,3355],{},[22,3356,521],{},[523,3358,3360],{"className":525,"code":3359,"language":527,"meta":528,"style":528},"const (\n    leftX  = 40.0\n    rightX = 380.0\n    blockY = 50.0\n)\n\nif err := pdf.Image(\"logo.png\", leftX, blockY, &gopdf.Rect{W: 100, H: 60}); err != nil {\n    log.Fatal(err)\n}\n\npdf.SetFont(\"helvetica-bold\", \"\", 14)\npdf.SetXY(rightX, blockY)\nif err := pdf.Cell(nil, \"ACME Corporation\"); err != nil {\n    log.Fatal(err)\n}\n\npdf.SetFont(\"helvetica\", \"\", 10)\npdf.SetXY(rightX, blockY+20)\npdf.Cell(nil, \"1 Market Street, Suite 400\")\npdf.SetXY(rightX, blockY+34)\npdf.Cell(nil, \"San Francisco, CA 94103\")\npdf.SetXY(rightX, blockY+48)\npdf.Cell(nil, \"billing@acme.example\")\n",[31,3361,3362,3368,3377,3387,3397,3401,3405,3482,3497,3501,3505,3532,3551,3584,3598,3602,3606,3633,3655,3674,3697,3716,3739],{"__ignoreMap":528},[532,3363,3364,3366],{"class":534,"line":535},[532,3365,1467],{"class":538},[532,3367,560],{"class":538},[532,3369,3370,3373,3375],{"class":534,"line":546},[532,3371,3372],{"class":620},"    leftX  ",[532,3374,1477],{"class":538},[532,3376,1494],{"class":809},[532,3378,3379,3382,3384],{"class":534,"line":553},[532,3380,3381],{"class":620},"    rightX ",[532,3383,1477],{"class":538},[532,3385,3386],{"class":809}," 380.0\n",[532,3388,3389,3392,3394],{"class":534,"line":563},[532,3390,3391],{"class":620},"    blockY ",[532,3393,1477],{"class":538},[532,3395,3396],{"class":809}," 50.0\n",[532,3398,3399],{"class":534,"line":575},[532,3400,593],{"class":538},[532,3402,3403],{"class":534,"line":580},[532,3404,550],{"emptyLinePlaceholder":549},[532,3406,3407,3410,3412,3414,3416,3418,3420,3422,3424,3427,3429,3431,3434,3436,3439,3441,3444,3446,3448,3451,3453,3456,3458,3461,3463,3466,3468,3471,3474,3476,3478,3480],{"class":534,"line":590},[532,3408,3409],{"class":556},"if",[532,3411,706],{"class":620},[532,3413,624],{"class":538},[532,3415,711],{"class":620},[532,3417,630],{"class":538},[532,3419,360],{"class":607},[532,3421,650],{"class":538},[532,3423,721],{"class":538},[532,3425,3426],{"class":724},"logo.png",[532,3428,721],{"class":538},[532,3430,730],{"class":538},[532,3432,3433],{"class":620}," leftX",[532,3435,730],{"class":538},[532,3437,3438],{"class":620}," blockY",[532,3440,730],{"class":538},[532,3442,3443],{"class":538}," &",[532,3445,653],{"class":542},[532,3447,630],{"class":538},[532,3449,3450],{"class":542},"Rect",[532,3452,661],{"class":538},[532,3454,3455],{"class":620},"W",[532,3457,667],{"class":538},[532,3459,3460],{"class":809}," 100",[532,3462,730],{"class":538},[532,3464,3465],{"class":620}," H",[532,3467,667],{"class":538},[532,3469,3470],{"class":809}," 60",[532,3472,3473],{"class":538},"});",[532,3475,706],{"class":620},[532,3477,746],{"class":538},[532,3479,749],{"class":538},[532,3481,614],{"class":538},[532,3483,3484,3487,3489,3491,3493,3495],{"class":534,"line":596},[532,3485,3486],{"class":620},"    log",[532,3488,630],{"class":538},[532,3490,762],{"class":607},[532,3492,650],{"class":538},[532,3494,767],{"class":620},[532,3496,593],{"class":538},[532,3498,3499],{"class":534,"line":601},[532,3500,1000],{"class":538},[532,3502,3503],{"class":534,"line":617},[532,3504,550],{"emptyLinePlaceholder":549},[532,3506,3507,3509,3511,3513,3515,3517,3519,3521,3523,3525,3527,3530],{"class":534,"line":639},[532,3508,1583],{"class":620},[532,3510,630],{"class":538},[532,3512,791],{"class":607},[532,3514,650],{"class":538},[532,3516,721],{"class":538},[532,3518,1594],{"class":724},[532,3520,721],{"class":538},[532,3522,730],{"class":538},[532,3524,804],{"class":538},[532,3526,730],{"class":538},[532,3528,3529],{"class":809}," 14",[532,3531,593],{"class":538},[532,3533,3534,3536,3538,3540,3542,3545,3547,3549],{"class":534,"line":683},[532,3535,1583],{"class":620},[532,3537,630],{"class":538},[532,3539,33],{"class":607},[532,3541,650],{"class":538},[532,3543,3544],{"class":620},"rightX",[532,3546,730],{"class":538},[532,3548,3438],{"class":620},[532,3550,593],{"class":538},[532,3552,3553,3555,3557,3559,3561,3563,3565,3567,3569,3572,3574,3576,3578,3580,3582],{"class":534,"line":695},[532,3554,3409],{"class":556},[532,3556,706],{"class":620},[532,3558,624],{"class":538},[532,3560,711],{"class":620},[532,3562,630],{"class":538},[532,3564,37],{"class":607},[532,3566,897],{"class":538},[532,3568,733],{"class":538},[532,3570,3571],{"class":724},"ACME Corporation",[532,3573,721],{"class":538},[532,3575,741],{"class":538},[532,3577,706],{"class":620},[532,3579,746],{"class":538},[532,3581,749],{"class":538},[532,3583,614],{"class":538},[532,3585,3586,3588,3590,3592,3594,3596],{"class":534,"line":700},[532,3587,3486],{"class":620},[532,3589,630],{"class":538},[532,3591,762],{"class":607},[532,3593,650],{"class":538},[532,3595,767],{"class":620},[532,3597,593],{"class":538},[532,3599,3600],{"class":534,"line":754},[532,3601,1000],{"class":538},[532,3603,3604],{"class":534,"line":772},[532,3605,550],{"emptyLinePlaceholder":549},[532,3607,3608,3610,3612,3614,3616,3618,3620,3622,3624,3626,3628,3631],{"class":534,"line":778},[532,3609,1583],{"class":620},[532,3611,630],{"class":538},[532,3613,791],{"class":607},[532,3615,650],{"class":538},[532,3617,721],{"class":538},[532,3619,725],{"class":724},[532,3621,721],{"class":538},[532,3623,730],{"class":538},[532,3625,804],{"class":538},[532,3627,730],{"class":538},[532,3629,3630],{"class":809}," 10",[532,3632,593],{"class":538},[532,3634,3635,3637,3639,3641,3643,3645,3647,3649,3651,3653],{"class":534,"line":823},[532,3636,1583],{"class":620},[532,3638,630],{"class":538},[532,3640,33],{"class":607},[532,3642,650],{"class":538},[532,3644,3544],{"class":620},[532,3646,730],{"class":538},[532,3648,3438],{"class":620},[532,3650,1750],{"class":538},[532,3652,1165],{"class":809},[532,3654,593],{"class":538},[532,3656,3657,3659,3661,3663,3665,3667,3670,3672],{"class":534,"line":838},[532,3658,1583],{"class":620},[532,3660,630],{"class":538},[532,3662,37],{"class":607},[532,3664,897],{"class":538},[532,3666,733],{"class":538},[532,3668,3669],{"class":724},"1 Market Street, Suite 400",[532,3671,721],{"class":538},[532,3673,593],{"class":538},[532,3675,3676,3678,3680,3682,3684,3686,3688,3690,3692,3695],{"class":534,"line":843},[532,3677,1583],{"class":620},[532,3679,630],{"class":538},[532,3681,33],{"class":607},[532,3683,650],{"class":538},[532,3685,3544],{"class":620},[532,3687,730],{"class":538},[532,3689,3438],{"class":620},[532,3691,1750],{"class":538},[532,3693,3694],{"class":809},"34",[532,3696,593],{"class":538},[532,3698,3699,3701,3703,3705,3707,3709,3712,3714],{"class":534,"line":848},[532,3700,1583],{"class":620},[532,3702,630],{"class":538},[532,3704,37],{"class":607},[532,3706,897],{"class":538},[532,3708,733],{"class":538},[532,3710,3711],{"class":724},"San Francisco, CA 94103",[532,3713,721],{"class":538},[532,3715,593],{"class":538},[532,3717,3718,3720,3722,3724,3726,3728,3730,3732,3734,3737],{"class":534,"line":865},[532,3719,1583],{"class":620},[532,3721,630],{"class":538},[532,3723,33],{"class":607},[532,3725,650],{"class":538},[532,3727,3544],{"class":620},[532,3729,730],{"class":538},[532,3731,3438],{"class":620},[532,3733,1750],{"class":538},[532,3735,3736],{"class":809},"48",[532,3738,593],{"class":538},[532,3740,3741,3743,3745,3747,3749,3751,3754,3756],{"class":534,"line":882},[532,3742,1583],{"class":620},[532,3744,630],{"class":538},[532,3746,37],{"class":607},[532,3748,897],{"class":538},[532,3750,733],{"class":538},[532,3752,3753],{"class":724},"billing@acme.example",[532,3755,721],{"class":538},[532,3757,593],{"class":538},[19,3759,3760,3761,3764],{},"There are six explicit y-coordinates, and the right column starts at ",[31,3762,3763],{},"rightX = 380"," because someone decided the logo was 100 wide and the right block needed a 240-pixel gap. Move the logo to the right side and every number changes.",[19,3766,3767],{},[22,3768,1005],{},[523,3770,3772],{"className":525,"code":3771,"language":527,"meta":528,"style":528},"//go:embed logo.png\nvar logoData []byte\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Image(logoData, template.FitWidth(document.Mm(35)))\n    })\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"ACME Corporation\", template.Bold(), template.FontSize(14))\n        c.Text(\"1 Market Street, Suite 400\")\n        c.Text(\"San Francisco, CA 94103\")\n        c.Text(\"billing@acme.example\")\n    })\n})\n",[31,3773,3774,3779,3793,3797,3821,3851,3889,3893,3924,3964,3982,4000,4018,4022],{"__ignoreMap":528},[532,3775,3776],{"class":534,"line":535},[532,3777,3778],{"class":1483},"//go:embed logo.png\n",[532,3780,3781,3784,3787,3790],{"class":534,"line":546},[532,3782,3783],{"class":538},"var",[532,3785,3786],{"class":620}," logoData ",[532,3788,3789],{"class":538},"[]",[532,3791,3792],{"class":1675},"byte\n",[532,3794,3795],{"class":534,"line":553},[532,3796,550],{"emptyLinePlaceholder":549},[532,3798,3799,3801,3803,3805,3807,3809,3811,3813,3815,3817,3819],{"class":534,"line":563},[532,3800,1940],{"class":620},[532,3802,630],{"class":538},[532,3804,1203],{"class":607},[532,3806,1206],{"class":538},[532,3808,1210],{"class":1209},[532,3810,670],{"class":538},[532,3812,1215],{"class":542},[532,3814,630],{"class":538},[532,3816,1220],{"class":542},[532,3818,1223],{"class":538},[532,3820,614],{"class":538},[532,3822,3823,3825,3827,3829,3831,3833,3835,3837,3839,3841,3843,3845,3847,3849],{"class":534,"line":575},[532,3824,3104],{"class":620},[532,3826,630],{"class":538},[532,3828,1235],{"class":607},[532,3830,650],{"class":538},[532,3832,2281],{"class":809},[532,3834,730],{"class":538},[532,3836,1245],{"class":538},[532,3838,1248],{"class":1209},[532,3840,670],{"class":538},[532,3842,1215],{"class":542},[532,3844,630],{"class":538},[532,3846,1257],{"class":542},[532,3848,1223],{"class":538},[532,3850,614],{"class":538},[532,3852,3853,3855,3857,3859,3861,3864,3866,3868,3870,3873,3875,3877,3879,3881,3883,3886],{"class":534,"line":580},[532,3854,3135],{"class":620},[532,3856,630],{"class":538},[532,3858,360],{"class":607},[532,3860,650],{"class":538},[532,3862,3863],{"class":620},"logoData",[532,3865,730],{"class":538},[532,3867,1284],{"class":620},[532,3869,630],{"class":538},[532,3871,3872],{"class":607},"FitWidth",[532,3874,650],{"class":538},[532,3876,1125],{"class":620},[532,3878,630],{"class":538},[532,3880,1160],{"class":607},[532,3882,650],{"class":538},[532,3884,3885],{"class":809},"35",[532,3887,3888],{"class":538},")))\n",[532,3890,3891],{"class":534,"line":590},[532,3892,1317],{"class":538},[532,3894,3895,3897,3899,3901,3903,3906,3908,3910,3912,3914,3916,3918,3920,3922],{"class":534,"line":596},[532,3896,3104],{"class":620},[532,3898,630],{"class":538},[532,3900,1235],{"class":607},[532,3902,650],{"class":538},[532,3904,3905],{"class":809},"8",[532,3907,730],{"class":538},[532,3909,1245],{"class":538},[532,3911,1248],{"class":1209},[532,3913,670],{"class":538},[532,3915,1215],{"class":542},[532,3917,630],{"class":538},[532,3919,1257],{"class":542},[532,3921,1223],{"class":538},[532,3923,614],{"class":538},[532,3925,3926,3928,3930,3932,3934,3936,3938,3940,3942,3944,3946,3948,3950,3952,3954,3956,3958,3961],{"class":534,"line":601},[532,3927,3135],{"class":620},[532,3929,630],{"class":538},[532,3931,1271],{"class":607},[532,3933,650],{"class":538},[532,3935,721],{"class":538},[532,3937,3571],{"class":724},[532,3939,721],{"class":538},[532,3941,730],{"class":538},[532,3943,1284],{"class":620},[532,3945,630],{"class":538},[532,3947,1304],{"class":607},[532,3949,2163],{"class":538},[532,3951,1284],{"class":620},[532,3953,630],{"class":538},[532,3955,1289],{"class":607},[532,3957,650],{"class":538},[532,3959,3960],{"class":809},"14",[532,3962,3963],{"class":538},"))\n",[532,3965,3966,3968,3970,3972,3974,3976,3978,3980],{"class":534,"line":617},[532,3967,3135],{"class":620},[532,3969,630],{"class":538},[532,3971,1271],{"class":607},[532,3973,650],{"class":538},[532,3975,721],{"class":538},[532,3977,3669],{"class":724},[532,3979,721],{"class":538},[532,3981,593],{"class":538},[532,3983,3984,3986,3988,3990,3992,3994,3996,3998],{"class":534,"line":639},[532,3985,3135],{"class":620},[532,3987,630],{"class":538},[532,3989,1271],{"class":607},[532,3991,650],{"class":538},[532,3993,721],{"class":538},[532,3995,3711],{"class":724},[532,3997,721],{"class":538},[532,3999,593],{"class":538},[532,4001,4002,4004,4006,4008,4010,4012,4014,4016],{"class":534,"line":683},[532,4003,3135],{"class":620},[532,4005,630],{"class":538},[532,4007,1271],{"class":607},[532,4009,650],{"class":538},[532,4011,721],{"class":538},[532,4013,3753],{"class":724},[532,4015,721],{"class":538},[532,4017,593],{"class":538},[532,4019,4020],{"class":534,"line":695},[532,4021,1317],{"class":538},[532,4023,4024],{"class":534,"line":700},[532,4025,680],{"class":538},[19,4027,4028,4029,4032,4033,4035],{},"Two columns, 4 + 8 = 12. The image fits a fixed width and lets gpdf compute the height from the aspect ratio. Each ",[31,4030,4031],{},"c.Text"," flows below the previous one — no ",[31,4034,49],{},", no y arithmetic. Swap the column order if you want the logo on the right.",[14,4037,4039],{"id":4038},"before-after-5-page-numbers-in-the-footer","Before / After 5: page numbers in the footer",[19,4041,4042],{},"In gopdf you maintain the count yourself, because the render is single-pass and the total isn't known when you draw the first footer. Most codebases do a two-pass workaround: render once to count pages, render again with the count baked in.",[19,4044,4045],{},[22,4046,521],{},[523,4048,4050],{"className":525,"code":4049,"language":527,"meta":528,"style":528},"totalPages := 0\npdf.AddFooter(func() {\n    totalPages++\n})\n\n// First pass: count pages by rendering the whole document.\nbuildContent(&pdf)\nfinalTotal := totalPages\n\n// Second pass: re-render with the known total.\npdf2 := gopdf.GoPdf{}\npdf2.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})\npageNum := 0\npdf2.AddFooter(func() {\n    pageNum++\n    pdf2.SetFont(\"helvetica\", \"\", 8)\n    pdf2.SetXY(40, 800)\n    pdf2.Cell(nil, fmt.Sprintf(\"Page %d of %d\", pageNum, finalTotal))\n})\nbuildContent(&pdf2)\npdf2.WritePdf(\"report.pdf\")\n",[31,4051,4052,4062,4076,4084,4088,4092,4097,4109,4119,4123,4128,4143,4176,4185,4197,4204,4232,4251,4299,4303,4313],{"__ignoreMap":528},[532,4053,4054,4057,4059],{"class":534,"line":535},[532,4055,4056],{"class":620},"totalPages ",[532,4058,624],{"class":538},[532,4060,4061],{"class":809}," 0\n",[532,4063,4064,4066,4068,4071,4074],{"class":534,"line":546},[532,4065,1583],{"class":620},[532,4067,630],{"class":538},[532,4069,4070],{"class":607},"AddFooter",[532,4072,4073],{"class":538},"(func()",[532,4075,614],{"class":538},[532,4077,4078,4081],{"class":534,"line":553},[532,4079,4080],{"class":620},"    totalPages",[532,4082,4083],{"class":538},"++\n",[532,4085,4086],{"class":534,"line":563},[532,4087,680],{"class":538},[532,4089,4090],{"class":534,"line":575},[532,4091,550],{"emptyLinePlaceholder":549},[532,4093,4094],{"class":534,"line":580},[532,4095,4096],{"class":1483},"// First pass: count pages by rendering the whole document.\n",[532,4098,4099,4102,4105,4107],{"class":534,"line":590},[532,4100,4101],{"class":607},"buildContent",[532,4103,4104],{"class":538},"(&",[532,4106,1583],{"class":620},[532,4108,593],{"class":538},[532,4110,4111,4114,4116],{"class":534,"line":596},[532,4112,4113],{"class":620},"finalTotal ",[532,4115,624],{"class":538},[532,4117,4118],{"class":620}," totalPages\n",[532,4120,4121],{"class":534,"line":601},[532,4122,550],{"emptyLinePlaceholder":549},[532,4124,4125],{"class":534,"line":617},[532,4126,4127],{"class":1483},"// Second pass: re-render with the known total.\n",[532,4129,4130,4133,4135,4137,4139,4141],{"class":534,"line":639},[532,4131,4132],{"class":620},"pdf2 ",[532,4134,624],{"class":538},[532,4136,627],{"class":542},[532,4138,630],{"class":538},[532,4140,633],{"class":542},[532,4142,636],{"class":538},[532,4144,4145,4148,4150,4152,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172,4174],{"class":534,"line":683},[532,4146,4147],{"class":620},"pdf2",[532,4149,630],{"class":538},[532,4151,647],{"class":607},[532,4153,650],{"class":538},[532,4155,653],{"class":542},[532,4157,630],{"class":538},[532,4159,658],{"class":542},[532,4161,661],{"class":538},[532,4163,664],{"class":620},[532,4165,667],{"class":538},[532,4167,670],{"class":538},[532,4169,653],{"class":620},[532,4171,630],{"class":538},[532,4173,677],{"class":620},[532,4175,680],{"class":538},[532,4177,4178,4181,4183],{"class":534,"line":695},[532,4179,4180],{"class":620},"pageNum ",[532,4182,624],{"class":538},[532,4184,4061],{"class":809},[532,4186,4187,4189,4191,4193,4195],{"class":534,"line":700},[532,4188,4147],{"class":620},[532,4190,630],{"class":538},[532,4192,4070],{"class":607},[532,4194,4073],{"class":538},[532,4196,614],{"class":538},[532,4198,4199,4202],{"class":534,"line":754},[532,4200,4201],{"class":620},"    pageNum",[532,4203,4083],{"class":538},[532,4205,4206,4209,4211,4213,4215,4217,4219,4221,4223,4225,4227,4230],{"class":534,"line":772},[532,4207,4208],{"class":620},"    pdf2",[532,4210,630],{"class":538},[532,4212,791],{"class":607},[532,4214,650],{"class":538},[532,4216,721],{"class":538},[532,4218,725],{"class":724},[532,4220,721],{"class":538},[532,4222,730],{"class":538},[532,4224,804],{"class":538},[532,4226,730],{"class":538},[532,4228,4229],{"class":809}," 8",[532,4231,593],{"class":538},[532,4233,4234,4236,4238,4240,4242,4244,4246,4249],{"class":534,"line":778},[532,4235,4208],{"class":620},[532,4237,630],{"class":538},[532,4239,33],{"class":607},[532,4241,650],{"class":538},[532,4243,860],{"class":809},[532,4245,730],{"class":538},[532,4247,4248],{"class":809}," 800",[532,4250,593],{"class":538},[532,4252,4253,4255,4257,4259,4261,4264,4266,4269,4271,4273,4276,4280,4283,4285,4287,4289,4292,4294,4297],{"class":534,"line":823},[532,4254,4208],{"class":620},[532,4256,630],{"class":538},[532,4258,37],{"class":607},[532,4260,897],{"class":538},[532,4262,4263],{"class":620}," fmt",[532,4265,630],{"class":538},[532,4267,4268],{"class":607},"Sprintf",[532,4270,650],{"class":538},[532,4272,721],{"class":538},[532,4274,4275],{"class":724},"Page ",[532,4277,4279],{"class":4278},"swJcz","%d",[532,4281,4282],{"class":724}," of ",[532,4284,4279],{"class":4278},[532,4286,721],{"class":538},[532,4288,730],{"class":538},[532,4290,4291],{"class":620}," pageNum",[532,4293,730],{"class":538},[532,4295,4296],{"class":620}," finalTotal",[532,4298,3963],{"class":538},[532,4300,4301],{"class":534,"line":838},[532,4302,680],{"class":538},[532,4304,4305,4307,4309,4311],{"class":534,"line":843},[532,4306,4101],{"class":607},[532,4308,4104],{"class":538},[532,4310,4147],{"class":620},[532,4312,593],{"class":538},[532,4314,4315,4317,4319,4321,4323,4325,4328,4330],{"class":534,"line":848},[532,4316,4147],{"class":620},[532,4318,630],{"class":538},[532,4320,955],{"class":607},[532,4322,650],{"class":538},[532,4324,721],{"class":538},[532,4326,4327],{"class":724},"report.pdf",[532,4329,721],{"class":538},[532,4331,593],{"class":538},[19,4333,4334],{},"If you've maintained gopdf code, you've written this. It's not in any FAQ, but it's the only way to get an honest \"Page X of Y\" footer without parsing the output.",[19,4336,4337],{},[22,4338,1005],{},[523,4340,4342],{"className":525,"code":4341,"language":527,"meta":528,"style":528},"doc.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            c.Stack(template.AlignRight(), func(c *template.ColBuilder) {\n                c.Text(\"Page \", template.Inline())\n                c.PageNumber(template.Inline())\n                c.Text(\" of \", template.Inline())\n                c.TotalPages(template.Inline())\n            }, template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n",[31,4343,4344,4371,4396,4426,4444,4480,4484,4514,4550,4578,4597,4623,4642,4679,4683,4687],{"__ignoreMap":528},[532,4345,4346,4349,4351,4354,4356,4358,4360,4362,4364,4367,4369],{"class":534,"line":535},[532,4347,4348],{"class":620},"doc",[532,4350,630],{"class":538},[532,4352,4353],{"class":607},"Footer",[532,4355,1206],{"class":538},[532,4357,19],{"class":1209},[532,4359,670],{"class":538},[532,4361,1215],{"class":542},[532,4363,630],{"class":538},[532,4365,4366],{"class":542},"PageBuilder",[532,4368,1223],{"class":538},[532,4370,614],{"class":538},[532,4372,4373,4376,4378,4380,4382,4384,4386,4388,4390,4392,4394],{"class":534,"line":546},[532,4374,4375],{"class":620},"    p",[532,4377,630],{"class":538},[532,4379,1203],{"class":607},[532,4381,1206],{"class":538},[532,4383,1210],{"class":1209},[532,4385,670],{"class":538},[532,4387,1215],{"class":542},[532,4389,630],{"class":538},[532,4391,1220],{"class":542},[532,4393,1223],{"class":538},[532,4395,614],{"class":538},[532,4397,4398,4400,4402,4404,4406,4408,4410,4412,4414,4416,4418,4420,4422,4424],{"class":534,"line":553},[532,4399,1230],{"class":620},[532,4401,630],{"class":538},[532,4403,1235],{"class":607},[532,4405,650],{"class":538},[532,4407,1826],{"class":809},[532,4409,730],{"class":538},[532,4411,1245],{"class":538},[532,4413,1248],{"class":1209},[532,4415,670],{"class":538},[532,4417,1215],{"class":542},[532,4419,630],{"class":538},[532,4421,1257],{"class":542},[532,4423,1223],{"class":538},[532,4425,614],{"class":538},[532,4427,4428,4430,4432,4434,4436,4438,4440,4442],{"class":534,"line":563},[532,4429,1266],{"class":620},[532,4431,630],{"class":538},[532,4433,1271],{"class":607},[532,4435,650],{"class":538},[532,4437,721],{"class":538},[532,4439,3571],{"class":724},[532,4441,721],{"class":538},[532,4443,2152],{"class":538},[532,4445,4446,4448,4450,4452,4454,4456,4458,4460,4462,4464,4466,4468,4470,4473,4475,4478],{"class":534,"line":575},[532,4447,2072],{"class":620},[532,4449,630],{"class":538},[532,4451,1289],{"class":607},[532,4453,650],{"class":538},[532,4455,3905],{"class":809},[532,4457,1297],{"class":538},[532,4459,1284],{"class":620},[532,4461,630],{"class":538},[532,4463,2185],{"class":607},[532,4465,650],{"class":538},[532,4467,1583],{"class":620},[532,4469,630],{"class":538},[532,4471,4472],{"class":607},"Gray",[532,4474,650],{"class":538},[532,4476,4477],{"class":809},"0.5",[532,4479,3888],{"class":538},[532,4481,4482],{"class":534,"line":580},[532,4483,1312],{"class":538},[532,4485,4486,4488,4490,4492,4494,4496,4498,4500,4502,4504,4506,4508,4510,4512],{"class":534,"line":590},[532,4487,1230],{"class":620},[532,4489,630],{"class":538},[532,4491,1235],{"class":607},[532,4493,650],{"class":538},[532,4495,1826],{"class":809},[532,4497,730],{"class":538},[532,4499,1245],{"class":538},[532,4501,1248],{"class":1209},[532,4503,670],{"class":538},[532,4505,1215],{"class":542},[532,4507,630],{"class":538},[532,4509,1257],{"class":542},[532,4511,1223],{"class":538},[532,4513,614],{"class":538},[532,4515,4516,4518,4520,4523,4525,4527,4529,4532,4534,4536,4538,4540,4542,4544,4546,4548],{"class":534,"line":596},[532,4517,1266],{"class":620},[532,4519,630],{"class":538},[532,4521,4522],{"class":607},"Stack",[532,4524,650],{"class":538},[532,4526,1215],{"class":620},[532,4528,630],{"class":538},[532,4530,4531],{"class":607},"AlignRight",[532,4533,2163],{"class":538},[532,4535,1245],{"class":538},[532,4537,1248],{"class":1209},[532,4539,670],{"class":538},[532,4541,1215],{"class":542},[532,4543,630],{"class":538},[532,4545,1257],{"class":542},[532,4547,1223],{"class":538},[532,4549,614],{"class":538},[532,4551,4552,4555,4557,4559,4561,4563,4565,4567,4569,4571,4573,4576],{"class":534,"line":601},[532,4553,4554],{"class":620},"                c",[532,4556,630],{"class":538},[532,4558,1271],{"class":607},[532,4560,650],{"class":538},[532,4562,721],{"class":538},[532,4564,4275],{"class":724},[532,4566,721],{"class":538},[532,4568,730],{"class":538},[532,4570,1284],{"class":620},[532,4572,630],{"class":538},[532,4574,4575],{"class":607},"Inline",[532,4577,1307],{"class":538},[532,4579,4580,4582,4584,4587,4589,4591,4593,4595],{"class":534,"line":617},[532,4581,4554],{"class":620},[532,4583,630],{"class":538},[532,4585,4586],{"class":607},"PageNumber",[532,4588,650],{"class":538},[532,4590,1215],{"class":620},[532,4592,630],{"class":538},[532,4594,4575],{"class":607},[532,4596,1307],{"class":538},[532,4598,4599,4601,4603,4605,4607,4609,4611,4613,4615,4617,4619,4621],{"class":534,"line":639},[532,4600,4554],{"class":620},[532,4602,630],{"class":538},[532,4604,1271],{"class":607},[532,4606,650],{"class":538},[532,4608,721],{"class":538},[532,4610,4282],{"class":724},[532,4612,721],{"class":538},[532,4614,730],{"class":538},[532,4616,1284],{"class":620},[532,4618,630],{"class":538},[532,4620,4575],{"class":607},[532,4622,1307],{"class":538},[532,4624,4625,4627,4629,4632,4634,4636,4638,4640],{"class":534,"line":683},[532,4626,4554],{"class":620},[532,4628,630],{"class":538},[532,4630,4631],{"class":607},"TotalPages",[532,4633,650],{"class":538},[532,4635,1215],{"class":620},[532,4637,630],{"class":538},[532,4639,4575],{"class":607},[532,4641,1307],{"class":538},[532,4643,4644,4647,4649,4651,4653,4655,4657,4659,4661,4663,4665,4667,4669,4671,4673,4675,4677],{"class":534,"line":695},[532,4645,4646],{"class":538},"            },",[532,4648,1284],{"class":620},[532,4650,630],{"class":538},[532,4652,1289],{"class":607},[532,4654,650],{"class":538},[532,4656,3905],{"class":809},[532,4658,1297],{"class":538},[532,4660,1284],{"class":620},[532,4662,630],{"class":538},[532,4664,2185],{"class":607},[532,4666,650],{"class":538},[532,4668,1583],{"class":620},[532,4670,630],{"class":538},[532,4672,4472],{"class":607},[532,4674,650],{"class":538},[532,4676,4477],{"class":809},[532,4678,3888],{"class":538},[532,4680,4681],{"class":534,"line":700},[532,4682,1312],{"class":538},[532,4684,4685],{"class":534,"line":754},[532,4686,1317],{"class":538},[532,4688,4689],{"class":534,"line":772},[532,4690,680],{"class":538},[19,4692,4693,4695,4696,4698],{},[31,4694,4586],{}," and ",[31,4697,4631],{}," are placeholders. The layout engine paginates first, resolves the totals, then writes them in. One pass, no manual counting, no double render. The placeholders also work mid-paragraph if you want phrasing like \"5 of 12\" rather than the bare numbers.",[14,4700,4702],{"id":4701},"japanese-text-without-the-manual-subset","Japanese text without the manual subset",[19,4704,4705],{},"signintech/gopdf supports CJK well, but the path is character-set bookkeeping you do by hand. You add the TTF, you set the character map, and if your text contains a glyph outside the subset you registered, you get tofu. gpdf's TrueType subsetter walks the cmap (formats 4, 6, 12) and embeds exactly the glyphs you used — no manual subset list.",[523,4707,4709],{"className":525,"code":4708,"language":527,"meta":528,"style":528},"//go:embed NotoSansJP-Regular.ttf\nvar notoJP []byte\n\ndoc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    gpdf.WithFont(\"NotoSansJP\", notoJP),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 14),\n)\n\npage := doc.AddPage()\npage.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",[31,4710,4711,4716,4727,4731,4746,4765,4795,4820,4843,4847,4851,4866,4890,4920,4939,4958,4977,4981],{"__ignoreMap":528},[532,4712,4713],{"class":534,"line":535},[532,4714,4715],{"class":1483},"//go:embed NotoSansJP-Regular.ttf\n",[532,4717,4718,4720,4723,4725],{"class":534,"line":546},[532,4719,3783],{"class":538},[532,4721,4722],{"class":620}," notoJP ",[532,4724,3789],{"class":538},[532,4726,3792],{"class":1675},[532,4728,4729],{"class":534,"line":553},[532,4730,550],{"emptyLinePlaceholder":549},[532,4732,4733,4736,4738,4740,4742,4744],{"class":534,"line":563},[532,4734,4735],{"class":620},"doc ",[532,4737,624],{"class":538},[532,4739,1102],{"class":620},[532,4741,630],{"class":538},[532,4743,1107],{"class":607},[532,4745,1110],{"class":538},[532,4747,4748,4751,4753,4755,4757,4759,4761,4763],{"class":534,"line":575},[532,4749,4750],{"class":620},"    gpdf",[532,4752,630],{"class":538},[532,4754,1120],{"class":607},[532,4756,650],{"class":538},[532,4758,1125],{"class":620},[532,4760,630],{"class":538},[532,4762,1130],{"class":620},[532,4764,1133],{"class":538},[532,4766,4767,4769,4771,4773,4775,4777,4779,4781,4783,4785,4787,4789,4791,4793],{"class":534,"line":580},[532,4768,4750],{"class":620},[532,4770,630],{"class":538},[532,4772,1142],{"class":607},[532,4774,650],{"class":538},[532,4776,1125],{"class":620},[532,4778,630],{"class":538},[532,4780,1151],{"class":607},[532,4782,650],{"class":538},[532,4784,1125],{"class":620},[532,4786,630],{"class":538},[532,4788,1160],{"class":607},[532,4790,650],{"class":538},[532,4792,1165],{"class":809},[532,4794,1168],{"class":538},[532,4796,4797,4799,4801,4804,4806,4808,4811,4813,4815,4818],{"class":534,"line":590},[532,4798,4750],{"class":620},[532,4800,630],{"class":538},[532,4802,4803],{"class":607},"WithFont",[532,4805,650],{"class":538},[532,4807,721],{"class":538},[532,4809,4810],{"class":724},"NotoSansJP",[532,4812,721],{"class":538},[532,4814,730],{"class":538},[532,4816,4817],{"class":620}," notoJP",[532,4819,1133],{"class":538},[532,4821,4822,4824,4826,4829,4831,4833,4835,4837,4839,4841],{"class":534,"line":596},[532,4823,4750],{"class":620},[532,4825,630],{"class":538},[532,4827,4828],{"class":607},"WithDefaultFont",[532,4830,650],{"class":538},[532,4832,721],{"class":538},[532,4834,4810],{"class":724},[532,4836,721],{"class":538},[532,4838,730],{"class":538},[532,4840,3529],{"class":809},[532,4842,1133],{"class":538},[532,4844,4845],{"class":534,"line":601},[532,4846,593],{"class":538},[532,4848,4849],{"class":534,"line":617},[532,4850,550],{"emptyLinePlaceholder":549},[532,4852,4853,4856,4858,4860,4862,4864],{"class":534,"line":639},[532,4854,4855],{"class":620},"page ",[532,4857,624],{"class":538},[532,4859,1187],{"class":620},[532,4861,630],{"class":538},[532,4863,57],{"class":607},[532,4865,692],{"class":538},[532,4867,4868,4870,4872,4874,4876,4878,4880,4882,4884,4886,4888],{"class":534,"line":683},[532,4869,1940],{"class":620},[532,4871,630],{"class":538},[532,4873,1203],{"class":607},[532,4875,1206],{"class":538},[532,4877,1210],{"class":1209},[532,4879,670],{"class":538},[532,4881,1215],{"class":542},[532,4883,630],{"class":538},[532,4885,1220],{"class":542},[532,4887,1223],{"class":538},[532,4889,614],{"class":538},[532,4891,4892,4894,4896,4898,4900,4902,4904,4906,4908,4910,4912,4914,4916,4918],{"class":534,"line":695},[532,4893,3104],{"class":620},[532,4895,630],{"class":538},[532,4897,1235],{"class":607},[532,4899,650],{"class":538},[532,4901,1240],{"class":809},[532,4903,730],{"class":538},[532,4905,1245],{"class":538},[532,4907,1248],{"class":1209},[532,4909,670],{"class":538},[532,4911,1215],{"class":542},[532,4913,630],{"class":538},[532,4915,1257],{"class":542},[532,4917,1223],{"class":538},[532,4919,614],{"class":538},[532,4921,4922,4924,4926,4928,4930,4932,4935,4937],{"class":534,"line":700},[532,4923,3135],{"class":620},[532,4925,630],{"class":538},[532,4927,1271],{"class":607},[532,4929,650],{"class":538},[532,4931,721],{"class":538},[532,4933,4934],{"class":724},"こんにちは、世界。",[532,4936,721],{"class":538},[532,4938,593],{"class":538},[532,4940,4941,4943,4945,4947,4949,4951,4954,4956],{"class":534,"line":754},[532,4942,3135],{"class":620},[532,4944,630],{"class":538},[532,4946,1271],{"class":607},[532,4948,650],{"class":538},[532,4950,721],{"class":538},[532,4952,4953],{"class":724},"吾輩は猫である。名前はまだ無い。",[532,4955,721],{"class":538},[532,4957,593],{"class":538},[532,4959,4960,4962,4964,4966,4968,4970,4973,4975],{"class":534,"line":772},[532,4961,3135],{"class":620},[532,4963,630],{"class":538},[532,4965,1271],{"class":607},[532,4967,650],{"class":538},[532,4969,721],{"class":538},[532,4971,4972],{"class":724},"東京都渋谷区神宮前1-2-3",[532,4974,721],{"class":538},[532,4976,593],{"class":538},[532,4978,4979],{"class":534,"line":778},[532,4980,1317],{"class":538},[532,4982,4983],{"class":534,"line":823},[532,4984,680],{"class":538},[19,4986,4987,4988,4993],{},"A 200-character Japanese invoice produces a ~30 KB font subset rather than a 4 MB full embed. The companion piece on ",[4989,4990,4992],"a",{"href":4991},"/blog/embed-japanese-font","embedding Japanese fonts"," covers IPAex Gothic and Source Han Sans fallbacks.",[14,4995,4997],{"id":4996},"benchmarks","Benchmarks",[19,4999,5000],{},"Same hardware, same workloads, Apple M1 and Go 1.25.",[137,5002,5003,5020],{},[140,5004,5005],{},[143,5006,5007,5010,5012,5014,5017],{},[146,5008,5009],{},"Benchmark",[146,5011,24],{},[146,5013,28],{},[146,5015,5016],{},"gofpdf",[146,5018,5019],{},"Maroto v2",[154,5021,5022,5041,5059,5077],{},[143,5023,5024,5027,5032,5035,5038],{},[159,5025,5026],{},"Single page",[159,5028,5029],{},[22,5030,5031],{},"13 µs",[159,5033,5034],{},"423 µs",[159,5036,5037],{},"132 µs",[159,5039,5040],{},"237 µs",[143,5042,5043,5046,5050,5053,5056],{},[159,5044,5045],{},"4×10 invoice table",[159,5047,5048],{},[22,5049,3340],{},[159,5051,5052],{},"835 µs",[159,5054,5055],{},"241 µs",[159,5057,5058],{},"8.6 ms",[143,5060,5061,5064,5069,5071,5074],{},[159,5062,5063],{},"100-page report",[159,5065,5066],{},[22,5067,5068],{},"683 µs",[159,5070,5058],{},[159,5072,5073],{},"11.7 ms",[159,5075,5076],{},"19.8 ms",[143,5078,5079,5082,5087,5090,5093],{},[159,5080,5081],{},"Complex CJK invoice",[159,5083,5084],{},[22,5085,5086],{},"133 µs",[159,5088,5089],{},"997 µs",[159,5091,5092],{},"254 µs",[159,5094,5095],{},"10.4 ms",[19,5097,5098,5099,5102],{},"Numbers come from ",[31,5100,5101],{},"gpdf/_benchmark/benchmark_test.go",". signintech/gopdf is faster than gofpdf on simple paths because it does less per call, but the gap widens on tables because the layout work in gpdf is internal C-style imperative code with no allocations in the hot path, while a hand-rolled gopdf table allocates strings and font metrics per cell.",[19,5104,5105,5106,5109],{},"The single-core throughput at ",[22,5107,5108],{},"108 µs per table-rich page"," is roughly 9,000 invoices per second. For most workloads that means PDF generation can stay on the request path. Cron, queue, and pre-render workarounds you wrote because gopdf was 10× slower can usually be removed.",[14,5111,5113],{"id":5112},"what-gopdf-does-that-gpdf-doesnt","What gopdf does that gpdf doesn't",[19,5115,5116],{},"Honest section. If your gopdf usage relies on these, the migration won't carry you all the way.",[5118,5119,5120,5137,5150,5164],"ul",{},[5121,5122,5123,5128,5129,5132,5133,5136],"li",{},[22,5124,5125,630],{},[31,5126,5127],{},"ImportPage"," gopdf can import a single page from an existing PDF and stamp content over it. This is the \"PDF template\" workflow — load a vendor-provided invoice template, write your data on top. gpdf's overlay support handles the common case (",[31,5130,5131],{},"gpdf.Overlay","), but it doesn't expose the same ",[31,5134,5135],{},"UseImportedTemplate"," primitive. If your codebase is built around drawing on top of vendor PDFs, evaluate the overlay API first.",[5121,5138,5139,5142,5143,4695,5146,5149],{},[22,5140,5141],{},"Polygons and ovals as primitives."," gopdf has ",[31,5144,5145],{},"Oval",[31,5147,5148],{},"Polygon"," calls. gpdf's primitive set is rectangles, lines, images, text, and tables; arbitrary path drawing is not first-class. For data visualization, render with a charting library to PNG/SVG and embed that.",[5121,5151,5152,5155,5156,5159,5160,5163],{},[22,5153,5154],{},"Direct cursor positioning."," If your codebase legitimately wants pixel-perfect placement (a stamp at exactly ",[31,5157,5158],{},"(420, 240)","), ",[31,5161,5162],{},"page.Absolute(x, y, fn)"," exists, but it's the escape hatch — most code shouldn't reach for it.",[5121,5165,5166,5174,5175,403,5177,5179],{},[22,5167,5168,403,5171,630],{},[31,5169,5170],{},"PlaceHolderText",[31,5172,5173],{},"FillInPlaceHoldText"," gopdf has a two-pass placeholder pattern for filling in text after the layout is done. gpdf's ",[31,5176,4586],{},[31,5178,4631],{}," placeholders handle the common case (page numbering); a general \"fill this slot later\" mechanism doesn't exist yet.",[19,5181,5182,5183,5186],{},"For ",[22,5184,5185],{},"invoices, statements, reports, certificates, contracts, receipts, shipping labels, packing slips, and CJK documents"," — what most gopdf bills actually generate — the swap is complete.",[14,5188,5190],{"id":5189},"faq","FAQ",[19,5192,5193,5196],{},[22,5194,5195],{},"Is gpdf a fork of signintech/gopdf?","\nNo. gpdf is a clean reimplementation in pure Go. The PDF wire writer, layout engine, TrueType subsetter, AES encryption, and PKCS#7 signing are all written from scratch. There is no shared code or shared lineage.",[19,5198,5199,5202],{},[22,5200,5201],{},"Both are pure Go and CGO-free. What's the actual value of switching?","\nThe layout engine. The migration sections above are 80% about removing coordinate math, and that's the lived-day-to-day difference. The benchmarks are a secondary win. The MIT license is identical to gopdf's MIT, so licensing is not a factor.",[19,5204,5205,5208,5209,5212,5213,5216],{},[22,5206,5207],{},"Can I migrate incrementally?","\nYes — the two libraries don't conflict. They produce independent ",[31,5210,5211],{},"[]byte"," outputs. Render one section with gpdf, another with gopdf, and ",[31,5214,5215],{},"gpdf.Merge(a, b)"," glues them. In practice most teams find it easier to migrate a whole document at a time, because the layout shift is conceptual and partial migrations end up with both mental models in the same file.",[19,5218,5219,5226,5227,5230,5231,5234,5235,5238,5239,5242],{},[22,5220,5221,5222,5225],{},"My existing code uses ",[31,5223,5224],{},"pdf.Image(path, ...)"," to load logos from disk. Do I have to embed them?","\nYou don't ",[214,5228,5229],{},"have"," to. ",[31,5232,5233],{},"c.Image(imageBytes, ...)"," takes bytes — read the file with ",[31,5236,5237],{},"os.ReadFile"," if you want runtime loading. But ",[31,5240,5241],{},"//go:embed"," is the better default: the binary stops needing a writable filesystem at the path it expects, container images shrink to one binary, and the asset can't go missing in production.",[19,5244,5245,5252,5253,34,5256,34,5259,5262,5263,5266],{},[22,5246,5247,5248,5251],{},"What about ",[31,5249,5250],{},"gopdf.PageSizeA4"," and the other page size constants?","\ngpdf's ",[31,5254,5255],{},"document.A4",[31,5257,5258],{},"document.Letter",[31,5260,5261],{},"document.Legal",", etc., cover the same set. For a custom size, ",[31,5264,5265],{},"document.PageSize(document.Mm(210), document.Mm(297))"," works.",[19,5268,5269,5276,5278,5279,5282],{},[22,5270,5271,5272,5275],{},"My invoice generator uses ",[31,5273,5274],{},"pdf.Rotate"," for portrait-on-landscape stamping. Is there an equivalent?",[31,5277,5162],{}," accepts a rotation option; the typical \"watermark across the page diagonally\" pattern is one ",[31,5280,5281],{},"page.Absolute"," call. Per-element rotation on inline content isn't a primitive — if you need rotated cells inside a table, render that cell to an image and embed.",[19,5284,5285,5288,5289,1568,5291,5293,5294,1568,5297,5299],{},[22,5286,5287],{},"Is there a tool that auto-rewrites my code?","\nNot yet. The mapping is mechanical for the simple parts (",[31,5290,33],{},[31,5292,37],{}," → ",[31,5295,5296],{},"r.Col",[31,5298,4031],{},") but the table rewrite is structural — you delete the bookkeeping rather than translating it. Hand-migration of a typical generator takes a few hours per document type.",[14,5301,5303],{"id":5302},"try-gpdf","Try gpdf",[19,5305,5306],{},"gpdf is a Go library for generating PDFs. MIT licensed, zero external dependencies, native CJK support, 12-column grid layout.",[523,5308,5312],{"className":5309,"code":5310,"language":5311,"meta":528,"style":528},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[31,5313,5314],{"__ignoreMap":528},[532,5315,5316,5318,5321],{"class":534,"line":535},[532,5317,527],{"class":542},[532,5319,5320],{"class":724}," get",[532,5322,5323],{"class":724}," github.com/gpdf-dev/gpdf\n",[19,5325,5326,5332,5333],{},[4989,5327,5331],{"href":5328,"rel":5329},"https://github.com/gpdf-dev/gpdf",[5330],"nofollow","⭐ Star on GitHub"," · ",[4989,5334,5337],{"href":5335,"rel":5336},"https://gpdf.dev/docs/quickstart",[5330],"Read the docs",[14,5339,5341],{"id":5340},"next-reads","Next reads",[5118,5343,5344,5350,5356,5362,5368],{},[5121,5345,5346],{},[4989,5347,5349],{"href":5348},"/blog/12-column-grid","The 12-column grid: bringing Bootstrap thinking to PDF layout",[5121,5351,5352],{},[4989,5353,5355],{"href":5354},"/blog/bootstrap-grid-thinking-for-pdf","Bootstrap-grid thinking for PDF",[5121,5357,5358],{},[4989,5359,5361],{"href":5360},"/blog/gofpdf-migration","gofpdf is archived. Here's how to migrate to gpdf.",[5121,5363,5364],{},[4989,5365,5367],{"href":5366},"/blog/unidoc-migration","unipdf is AGPL or paid. Here's how to migrate to gpdf.",[5121,5369,5370],{},[4989,5371,5373],{"href":5372},"/blog/invoice-pdf-go-under-50-lines","Generate an invoice PDF in Go in under 50 lines",[5375,5376,5377],"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 .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 .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}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":528,"searchDepth":546,"depth":546,"links":5379},[5380,5381,5382,5383,5384,5385,5386,5387,5388,5389,5390,5391,5392,5393,5394],{"id":16,"depth":546,"text":17},{"id":67,"depth":546,"text":68},{"id":89,"depth":546,"text":90},{"id":131,"depth":546,"text":132},{"id":512,"depth":546,"text":513},{"id":1449,"depth":546,"text":1450},{"id":2235,"depth":546,"text":2236},{"id":3348,"depth":546,"text":3349},{"id":4038,"depth":546,"text":4039},{"id":4701,"depth":546,"text":4702},{"id":4996,"depth":546,"text":4997},{"id":5112,"depth":546,"text":5113},{"id":5189,"depth":546,"text":5190},{"id":5302,"depth":546,"text":5303},{"id":5340,"depth":546,"text":5341},"2026-05-06","signintech/gopdf works, but every cell, line, and header is an (x, y) calculation. This guide maps the gopdf API to gpdf — same Go, no coordinate math.",false,"md",{"name":5400,"totalTime":5401,"tools":5402,"steps":5404},"Migrate a Go project from signintech/gopdf to gpdf","PT40M",[5403],"Go 1.22+",[5405,5408,5411,5414,5417,5420,5423],{"name":5406,"text":5407},"Replace the import path","Swap github.com/signintech/gopdf for github.com/gpdf-dev/gpdf, github.com/gpdf-dev/gpdf/document, and github.com/gpdf-dev/gpdf/template. The old gopdf.GoPdf{} struct goes away — there is no equivalent shared mutable cursor.",{"name":5409,"text":5410},"Replace pdf.Start and pdf.AddPage with gpdf.NewDocument","Construct the document with gpdf.NewDocument(WithPageSize(document.A4), WithMargins(...)). doc.AddPage() returns a PageBuilder, not a cursor. You stop tracking the current y-position by hand.",{"name":5412,"text":5413},"Stop calling SetX, SetY, and SetXY","Delete every cursor call. Wrap content in page.AutoRow(func(r *RowBuilder)) and r.Col(span, func(c *ColBuilder)). The 12-column grid handles horizontal placement; AutoRow stacks vertically with no manual y math.",{"name":5415,"text":5416},"Replace Cell and MultiCell with c.Text","Inside a column, call c.Text(string, options...) instead of computing a Rect and calling Cell or MultiCell. Wrapping is automatic — the column knows its own width.",{"name":5418,"text":5419},"Rewrite manual table loops with c.Table","Replace the SetXY/Cell loops drawing each header and body row with c.Table(headers, rows, template.ColumnWidths(...)). Borders, page breaks, and header repetition are handled by the layout engine.",{"name":5421,"text":5422},"Switch font registration to bytes","Replace pdf.AddTTFFont(name, path) with gpdf.WithFont(name, ttfBytes) at construction. Embed the TTF via //go:embed so the binary stops needing a font path at runtime.",{"name":5424,"text":5425},"Replace WritePdf with doc.Generate plus os.WriteFile","Swap pdf.WritePdf(path) for data, _ := doc.Generate() then os.WriteFile(path, data, 0o644). Use doc.Render(w) when you want to stream straight into an http.ResponseWriter or S3 PutObject.",null,{},"/blog/signintech-gopdf-migration",{"title":5,"description":5396},"blog/021.signintech-gopdf-migration",[5432,5433,5434],"migration","comparison","tutorial","Mvbdanhi8qGwsEO9AEjcxx5HIdQu-OeC_rgCAgAClDI",1779199010132]