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