[{"data":1,"prerenderedAt":3190},["ShallowReactive",2],{"blog-zh-page-numbers-headers-footers":3},{"id":4,"title":5,"author":6,"body":9,"date":3155,"description":3156,"draft":3157,"extension":3158,"howTo":3159,"image":3181,"meta":3182,"navigation":132,"path":3183,"seo":3184,"stem":3185,"tags":3186,"updated":3181,"__hash__":3189},"blogZh/zh/blog/027.page-numbers-headers-footers.md","在 Go PDF 中正确实现页码、页眉和页脚",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":3140},"minimark",[12,25,35,38,43,84,91,94,105,1459,1473,1477,1487,1490,1568,1583,1587,1593,1614,1617,1624,1627,1642,1909,1912,1919,1922,1926,1943,2270,2285,2288,2457,2471,2474,2477,2490,2498,2661,2668,2683,2692,2702,2706,2716,2990,2997,3000,3003,3006,3012,3015,3019,3029,3035,3041,3051,3074,3084,3087,3090,3097,3101,3104,3121,3136],[13,14,15,16,20,21,24],"p",{},"一份 60 页的财务报告。有人在打印队列里打开第 12 页，问了一个问题：现在是第几页，还剩多少页？如果页脚只写 ",[17,18,19],"code",{},"12","，谁都不知道。它应该写 ",[17,22,23],{},"12 / 60","。",[13,26,27,30,31,34],{},[17,28,29],{},"60"," 这一边，是大多数 PDF 库出错的地方。要么写页脚时根本拿不到总页数，要么藏在 ",[17,32,33],{},"AliasNbPages"," 这种事后替换的 token 里，要么把文档渲染两遍再扔掉第一遍。",[13,36,37],{},"gpdf 用两个 builder 方法和一个内部两阶段分页器把它解决得干净利落。本文介绍 API 长什么样、内部怎么实现、以及目前唯一一个值得吐槽的小毛刺。",[39,40,42],"h2",{"id":41},"tldr","TL;DR",[44,45,46,57,60,70,73],"ul",{},[47,48,49,52,53,56],"li",{},[17,50,51],{},"doc.Header(fn)"," 和 ",[17,54,55],{},"doc.Footer(fn)"," 注册一个在每一页运行的闭包。",[47,58,59],{},"闭包里用的是和正文一样的 12 栏网格。",[47,61,62,65,66,69],{},[17,63,64],{},"c.PageNumber()"," 输出当前页码，",[17,67,68],{},"c.TotalPages()"," 输出总页数。",[47,71,72],{},"总页数会在分页完成后的第二阶段自动解析。不需要自己写双遍构建逻辑。",[47,74,75,76,79,80,83],{},"一个粗糙点：没有 ",[17,77,78],{},"c.PageNumberOf(total)"," 这种把 ",[17,81,82],{},"\"3 of 12\""," 当成一个内联字符串的辅助方法。要拼出来得用三列。下面会讲。",[13,85,86,87,90],{},"文中所有代码都来自 ",[17,88,89],{},"gpdf/_examples/builder/26_page_number_test.go","，是测试套件的一部分，能编能跑。",[39,92,93],{"id":93},"一个文件搞定一切",[13,95,96,97,100,101,104],{},"完整可运行的程序。存为 ",[17,98,99],{},"main.go","，执行 ",[17,102,103],{},"go run main.go","，得到 4 页的 PDF，每页页眉显示总页数，页脚显示当前页码。",[106,107,112],"pre",{"className":108,"code":109,"language":110,"meta":111,"style":111},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package main\n\nimport (\n    \"os\"\n\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/pdf\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc main() {\n    doc := template.New(\n        template.WithPageSize(document.A4),\n        template.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    doc.Header(func(p *template.PageBuilder) {\n        p.AutoRow(func(r *template.RowBuilder) {\n            r.Col(6, func(c *template.ColBuilder) {\n                c.Text(\"季度报告\", template.Bold(), template.FontSize(10))\n            })\n            r.Col(6, func(c *template.ColBuilder) {\n                c.TotalPages(template.AlignRight(), template.FontSize(9),\n                    template.TextColor(pdf.Gray(0.5)))\n            })\n        })\n        p.AutoRow(func(r *template.RowBuilder) {\n            r.Col(12, func(c *template.ColBuilder) {\n                c.Line(template.LineColor(pdf.RGBHex(0x1565C0)))\n                c.Spacer(document.Mm(3))\n            })\n        })\n    })\n\n    doc.Footer(func(p *template.PageBuilder) {\n        p.AutoRow(func(r *template.RowBuilder) {\n            r.Col(12, func(c *template.ColBuilder) {\n                c.Spacer(document.Mm(3))\n                c.Line(template.LineColor(pdf.Gray(0.7)))\n                c.Spacer(document.Mm(2))\n            })\n        })\n        p.AutoRow(func(r *template.RowBuilder) {\n            r.Col(6, func(c *template.ColBuilder) {\n                c.Text(\"Generated by gpdf\", template.FontSize(8),\n                    template.TextColor(pdf.Gray(0.5)))\n            })\n            r.Col(6, func(c *template.ColBuilder) {\n                c.PageNumber(template.AlignRight(), template.FontSize(8),\n                    template.TextColor(pdf.Gray(0.5)))\n            })\n        })\n    })\n\n    for _, title := range []string{\"引言\", \"背景\", \"分析\", \"结论\"} {\n        page := doc.AddPage()\n        page.AutoRow(func(r *template.RowBuilder) {\n            r.Col(12, func(c *template.ColBuilder) {\n                c.Text(title, template.FontSize(18), template.Bold())\n                c.Spacer(document.Mm(5))\n                c.Text(title + \" 章节正文。\")\n            })\n        })\n    }\n\n    out, err := doc.Generate()\n    if err != nil {\n        panic(err)\n    }\n    _ = os.WriteFile(\"report.pdf\", out, 0o644)\n}\n","go","",[17,113,114,127,134,144,156,161,171,181,191,197,202,218,240,265,302,308,313,346,375,413,462,468,499,533,562,567,573,598,629,664,689,694,699,705,710,736,761,792,815,847,871,876,881,906,937,970,993,998,1029,1061,1084,1089,1094,1099,1104,1173,1192,1218,1249,1288,1312,1338,1343,1348,1354,1359,1381,1397,1410,1415,1453],{"__ignoreMap":111},[115,116,119,123],"span",{"class":117,"line":118},"line",1,[115,120,122],{"class":121},"sMK4o","package",[115,124,126],{"class":125},"sBMFI"," main\n",[115,128,130],{"class":117,"line":129},2,[115,131,133],{"emptyLinePlaceholder":132},true,"\n",[115,135,137,141],{"class":117,"line":136},3,[115,138,140],{"class":139},"s7zQu","import",[115,142,143],{"class":121}," (\n",[115,145,147,150,153],{"class":117,"line":146},4,[115,148,149],{"class":121},"    \"",[115,151,152],{"class":125},"os",[115,154,155],{"class":121},"\"\n",[115,157,159],{"class":117,"line":158},5,[115,160,133],{"emptyLinePlaceholder":132},[115,162,164,166,169],{"class":117,"line":163},6,[115,165,149],{"class":121},[115,167,168],{"class":125},"github.com/gpdf-dev/gpdf/document",[115,170,155],{"class":121},[115,172,174,176,179],{"class":117,"line":173},7,[115,175,149],{"class":121},[115,177,178],{"class":125},"github.com/gpdf-dev/gpdf/pdf",[115,180,155],{"class":121},[115,182,184,186,189],{"class":117,"line":183},8,[115,185,149],{"class":121},[115,187,188],{"class":125},"github.com/gpdf-dev/gpdf/template",[115,190,155],{"class":121},[115,192,194],{"class":117,"line":193},9,[115,195,196],{"class":121},")\n",[115,198,200],{"class":117,"line":199},10,[115,201,133],{"emptyLinePlaceholder":132},[115,203,205,208,212,215],{"class":117,"line":204},11,[115,206,207],{"class":121},"func",[115,209,211],{"class":210},"s2Zo4"," main",[115,213,214],{"class":121},"()",[115,216,217],{"class":121}," {\n",[115,219,221,225,228,231,234,237],{"class":117,"line":220},12,[115,222,224],{"class":223},"sTEyZ","    doc ",[115,226,227],{"class":121},":=",[115,229,230],{"class":223}," template",[115,232,233],{"class":121},".",[115,235,236],{"class":210},"New",[115,238,239],{"class":121},"(\n",[115,241,243,246,248,251,254,257,259,262],{"class":117,"line":242},13,[115,244,245],{"class":223},"        template",[115,247,233],{"class":121},[115,249,250],{"class":210},"WithPageSize",[115,252,253],{"class":121},"(",[115,255,256],{"class":223},"document",[115,258,233],{"class":121},[115,260,261],{"class":223},"A4",[115,263,264],{"class":121},"),\n",[115,266,268,270,272,275,277,279,281,284,286,288,290,293,295,299],{"class":117,"line":267},14,[115,269,245],{"class":223},[115,271,233],{"class":121},[115,273,274],{"class":210},"WithMargins",[115,276,253],{"class":121},[115,278,256],{"class":223},[115,280,233],{"class":121},[115,282,283],{"class":210},"UniformEdges",[115,285,253],{"class":121},[115,287,256],{"class":223},[115,289,233],{"class":121},[115,291,292],{"class":210},"Mm",[115,294,253],{"class":121},[115,296,298],{"class":297},"sbssI","20",[115,300,301],{"class":121},"))),\n",[115,303,305],{"class":117,"line":304},15,[115,306,307],{"class":121},"    )\n",[115,309,311],{"class":117,"line":310},16,[115,312,133],{"emptyLinePlaceholder":132},[115,314,316,319,321,324,327,330,333,336,338,341,344],{"class":117,"line":315},17,[115,317,318],{"class":223},"    doc",[115,320,233],{"class":121},[115,322,323],{"class":210},"Header",[115,325,326],{"class":121},"(func(",[115,328,13],{"class":329},"sHdIc",[115,331,332],{"class":121}," *",[115,334,335],{"class":125},"template",[115,337,233],{"class":121},[115,339,340],{"class":125},"PageBuilder",[115,342,343],{"class":121},")",[115,345,217],{"class":121},[115,347,349,352,354,357,359,362,364,366,368,371,373],{"class":117,"line":348},18,[115,350,351],{"class":223},"        p",[115,353,233],{"class":121},[115,355,356],{"class":210},"AutoRow",[115,358,326],{"class":121},[115,360,361],{"class":329},"r",[115,363,332],{"class":121},[115,365,335],{"class":125},[115,367,233],{"class":121},[115,369,370],{"class":125},"RowBuilder",[115,372,343],{"class":121},[115,374,217],{"class":121},[115,376,378,381,383,386,388,391,394,397,400,402,404,406,409,411],{"class":117,"line":377},19,[115,379,380],{"class":223},"            r",[115,382,233],{"class":121},[115,384,385],{"class":210},"Col",[115,387,253],{"class":121},[115,389,390],{"class":297},"6",[115,392,393],{"class":121},",",[115,395,396],{"class":121}," func(",[115,398,399],{"class":329},"c",[115,401,332],{"class":121},[115,403,335],{"class":125},[115,405,233],{"class":121},[115,407,408],{"class":125},"ColBuilder",[115,410,343],{"class":121},[115,412,217],{"class":121},[115,414,416,419,421,424,426,429,433,435,437,439,441,444,447,449,451,454,456,459],{"class":117,"line":415},20,[115,417,418],{"class":223},"                c",[115,420,233],{"class":121},[115,422,423],{"class":210},"Text",[115,425,253],{"class":121},[115,427,428],{"class":121},"\"",[115,430,432],{"class":431},"sfazB","季度报告",[115,434,428],{"class":121},[115,436,393],{"class":121},[115,438,230],{"class":223},[115,440,233],{"class":121},[115,442,443],{"class":210},"Bold",[115,445,446],{"class":121},"(),",[115,448,230],{"class":223},[115,450,233],{"class":121},[115,452,453],{"class":210},"FontSize",[115,455,253],{"class":121},[115,457,458],{"class":297},"10",[115,460,461],{"class":121},"))\n",[115,463,465],{"class":117,"line":464},21,[115,466,467],{"class":121},"            })\n",[115,469,471,473,475,477,479,481,483,485,487,489,491,493,495,497],{"class":117,"line":470},22,[115,472,380],{"class":223},[115,474,233],{"class":121},[115,476,385],{"class":210},[115,478,253],{"class":121},[115,480,390],{"class":297},[115,482,393],{"class":121},[115,484,396],{"class":121},[115,486,399],{"class":329},[115,488,332],{"class":121},[115,490,335],{"class":125},[115,492,233],{"class":121},[115,494,408],{"class":125},[115,496,343],{"class":121},[115,498,217],{"class":121},[115,500,502,504,506,509,511,513,515,518,520,522,524,526,528,531],{"class":117,"line":501},23,[115,503,418],{"class":223},[115,505,233],{"class":121},[115,507,508],{"class":210},"TotalPages",[115,510,253],{"class":121},[115,512,335],{"class":223},[115,514,233],{"class":121},[115,516,517],{"class":210},"AlignRight",[115,519,446],{"class":121},[115,521,230],{"class":223},[115,523,233],{"class":121},[115,525,453],{"class":210},[115,527,253],{"class":121},[115,529,530],{"class":297},"9",[115,532,264],{"class":121},[115,534,536,539,541,544,546,549,551,554,556,559],{"class":117,"line":535},24,[115,537,538],{"class":223},"                    template",[115,540,233],{"class":121},[115,542,543],{"class":210},"TextColor",[115,545,253],{"class":121},[115,547,548],{"class":223},"pdf",[115,550,233],{"class":121},[115,552,553],{"class":210},"Gray",[115,555,253],{"class":121},[115,557,558],{"class":297},"0.5",[115,560,561],{"class":121},")))\n",[115,563,565],{"class":117,"line":564},25,[115,566,467],{"class":121},[115,568,570],{"class":117,"line":569},26,[115,571,572],{"class":121},"        })\n",[115,574,576,578,580,582,584,586,588,590,592,594,596],{"class":117,"line":575},27,[115,577,351],{"class":223},[115,579,233],{"class":121},[115,581,356],{"class":210},[115,583,326],{"class":121},[115,585,361],{"class":329},[115,587,332],{"class":121},[115,589,335],{"class":125},[115,591,233],{"class":121},[115,593,370],{"class":125},[115,595,343],{"class":121},[115,597,217],{"class":121},[115,599,601,603,605,607,609,611,613,615,617,619,621,623,625,627],{"class":117,"line":600},28,[115,602,380],{"class":223},[115,604,233],{"class":121},[115,606,385],{"class":210},[115,608,253],{"class":121},[115,610,19],{"class":297},[115,612,393],{"class":121},[115,614,396],{"class":121},[115,616,399],{"class":329},[115,618,332],{"class":121},[115,620,335],{"class":125},[115,622,233],{"class":121},[115,624,408],{"class":125},[115,626,343],{"class":121},[115,628,217],{"class":121},[115,630,632,634,636,639,641,643,645,648,650,652,654,657,659,662],{"class":117,"line":631},29,[115,633,418],{"class":223},[115,635,233],{"class":121},[115,637,638],{"class":210},"Line",[115,640,253],{"class":121},[115,642,335],{"class":223},[115,644,233],{"class":121},[115,646,647],{"class":210},"LineColor",[115,649,253],{"class":121},[115,651,548],{"class":223},[115,653,233],{"class":121},[115,655,656],{"class":210},"RGBHex",[115,658,253],{"class":121},[115,660,661],{"class":297},"0x1565C0",[115,663,561],{"class":121},[115,665,667,669,671,674,676,678,680,682,684,687],{"class":117,"line":666},30,[115,668,418],{"class":223},[115,670,233],{"class":121},[115,672,673],{"class":210},"Spacer",[115,675,253],{"class":121},[115,677,256],{"class":223},[115,679,233],{"class":121},[115,681,292],{"class":210},[115,683,253],{"class":121},[115,685,686],{"class":297},"3",[115,688,461],{"class":121},[115,690,692],{"class":117,"line":691},31,[115,693,467],{"class":121},[115,695,697],{"class":117,"line":696},32,[115,698,572],{"class":121},[115,700,702],{"class":117,"line":701},33,[115,703,704],{"class":121},"    })\n",[115,706,708],{"class":117,"line":707},34,[115,709,133],{"emptyLinePlaceholder":132},[115,711,713,715,717,720,722,724,726,728,730,732,734],{"class":117,"line":712},35,[115,714,318],{"class":223},[115,716,233],{"class":121},[115,718,719],{"class":210},"Footer",[115,721,326],{"class":121},[115,723,13],{"class":329},[115,725,332],{"class":121},[115,727,335],{"class":125},[115,729,233],{"class":121},[115,731,340],{"class":125},[115,733,343],{"class":121},[115,735,217],{"class":121},[115,737,739,741,743,745,747,749,751,753,755,757,759],{"class":117,"line":738},36,[115,740,351],{"class":223},[115,742,233],{"class":121},[115,744,356],{"class":210},[115,746,326],{"class":121},[115,748,361],{"class":329},[115,750,332],{"class":121},[115,752,335],{"class":125},[115,754,233],{"class":121},[115,756,370],{"class":125},[115,758,343],{"class":121},[115,760,217],{"class":121},[115,762,764,766,768,770,772,774,776,778,780,782,784,786,788,790],{"class":117,"line":763},37,[115,765,380],{"class":223},[115,767,233],{"class":121},[115,769,385],{"class":210},[115,771,253],{"class":121},[115,773,19],{"class":297},[115,775,393],{"class":121},[115,777,396],{"class":121},[115,779,399],{"class":329},[115,781,332],{"class":121},[115,783,335],{"class":125},[115,785,233],{"class":121},[115,787,408],{"class":125},[115,789,343],{"class":121},[115,791,217],{"class":121},[115,793,795,797,799,801,803,805,807,809,811,813],{"class":117,"line":794},38,[115,796,418],{"class":223},[115,798,233],{"class":121},[115,800,673],{"class":210},[115,802,253],{"class":121},[115,804,256],{"class":223},[115,806,233],{"class":121},[115,808,292],{"class":210},[115,810,253],{"class":121},[115,812,686],{"class":297},[115,814,461],{"class":121},[115,816,818,820,822,824,826,828,830,832,834,836,838,840,842,845],{"class":117,"line":817},39,[115,819,418],{"class":223},[115,821,233],{"class":121},[115,823,638],{"class":210},[115,825,253],{"class":121},[115,827,335],{"class":223},[115,829,233],{"class":121},[115,831,647],{"class":210},[115,833,253],{"class":121},[115,835,548],{"class":223},[115,837,233],{"class":121},[115,839,553],{"class":210},[115,841,253],{"class":121},[115,843,844],{"class":297},"0.7",[115,846,561],{"class":121},[115,848,850,852,854,856,858,860,862,864,866,869],{"class":117,"line":849},40,[115,851,418],{"class":223},[115,853,233],{"class":121},[115,855,673],{"class":210},[115,857,253],{"class":121},[115,859,256],{"class":223},[115,861,233],{"class":121},[115,863,292],{"class":210},[115,865,253],{"class":121},[115,867,868],{"class":297},"2",[115,870,461],{"class":121},[115,872,874],{"class":117,"line":873},41,[115,875,467],{"class":121},[115,877,879],{"class":117,"line":878},42,[115,880,572],{"class":121},[115,882,884,886,888,890,892,894,896,898,900,902,904],{"class":117,"line":883},43,[115,885,351],{"class":223},[115,887,233],{"class":121},[115,889,356],{"class":210},[115,891,326],{"class":121},[115,893,361],{"class":329},[115,895,332],{"class":121},[115,897,335],{"class":125},[115,899,233],{"class":121},[115,901,370],{"class":125},[115,903,343],{"class":121},[115,905,217],{"class":121},[115,907,909,911,913,915,917,919,921,923,925,927,929,931,933,935],{"class":117,"line":908},44,[115,910,380],{"class":223},[115,912,233],{"class":121},[115,914,385],{"class":210},[115,916,253],{"class":121},[115,918,390],{"class":297},[115,920,393],{"class":121},[115,922,396],{"class":121},[115,924,399],{"class":329},[115,926,332],{"class":121},[115,928,335],{"class":125},[115,930,233],{"class":121},[115,932,408],{"class":125},[115,934,343],{"class":121},[115,936,217],{"class":121},[115,938,940,942,944,946,948,950,953,955,957,959,961,963,965,968],{"class":117,"line":939},45,[115,941,418],{"class":223},[115,943,233],{"class":121},[115,945,423],{"class":210},[115,947,253],{"class":121},[115,949,428],{"class":121},[115,951,952],{"class":431},"Generated by gpdf",[115,954,428],{"class":121},[115,956,393],{"class":121},[115,958,230],{"class":223},[115,960,233],{"class":121},[115,962,453],{"class":210},[115,964,253],{"class":121},[115,966,967],{"class":297},"8",[115,969,264],{"class":121},[115,971,973,975,977,979,981,983,985,987,989,991],{"class":117,"line":972},46,[115,974,538],{"class":223},[115,976,233],{"class":121},[115,978,543],{"class":210},[115,980,253],{"class":121},[115,982,548],{"class":223},[115,984,233],{"class":121},[115,986,553],{"class":210},[115,988,253],{"class":121},[115,990,558],{"class":297},[115,992,561],{"class":121},[115,994,996],{"class":117,"line":995},47,[115,997,467],{"class":121},[115,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027],{"class":117,"line":1000},48,[115,1002,380],{"class":223},[115,1004,233],{"class":121},[115,1006,385],{"class":210},[115,1008,253],{"class":121},[115,1010,390],{"class":297},[115,1012,393],{"class":121},[115,1014,396],{"class":121},[115,1016,399],{"class":329},[115,1018,332],{"class":121},[115,1020,335],{"class":125},[115,1022,233],{"class":121},[115,1024,408],{"class":125},[115,1026,343],{"class":121},[115,1028,217],{"class":121},[115,1030,1032,1034,1036,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059],{"class":117,"line":1031},49,[115,1033,418],{"class":223},[115,1035,233],{"class":121},[115,1037,1038],{"class":210},"PageNumber",[115,1040,253],{"class":121},[115,1042,335],{"class":223},[115,1044,233],{"class":121},[115,1046,517],{"class":210},[115,1048,446],{"class":121},[115,1050,230],{"class":223},[115,1052,233],{"class":121},[115,1054,453],{"class":210},[115,1056,253],{"class":121},[115,1058,967],{"class":297},[115,1060,264],{"class":121},[115,1062,1064,1066,1068,1070,1072,1074,1076,1078,1080,1082],{"class":117,"line":1063},50,[115,1065,538],{"class":223},[115,1067,233],{"class":121},[115,1069,543],{"class":210},[115,1071,253],{"class":121},[115,1073,548],{"class":223},[115,1075,233],{"class":121},[115,1077,553],{"class":210},[115,1079,253],{"class":121},[115,1081,558],{"class":297},[115,1083,561],{"class":121},[115,1085,1087],{"class":117,"line":1086},51,[115,1088,467],{"class":121},[115,1090,1092],{"class":117,"line":1091},52,[115,1093,572],{"class":121},[115,1095,1097],{"class":117,"line":1096},53,[115,1098,704],{"class":121},[115,1100,1102],{"class":117,"line":1101},54,[115,1103,133],{"emptyLinePlaceholder":132},[115,1105,1107,1110,1113,1115,1118,1120,1123,1126,1130,1133,1135,1138,1140,1142,1145,1148,1150,1152,1154,1157,1159,1161,1163,1166,1168,1171],{"class":117,"line":1106},55,[115,1108,1109],{"class":139},"    for",[115,1111,1112],{"class":223}," _",[115,1114,393],{"class":121},[115,1116,1117],{"class":223}," title ",[115,1119,227],{"class":121},[115,1121,1122],{"class":139}," range",[115,1124,1125],{"class":121}," []",[115,1127,1129],{"class":1128},"spNyl","string",[115,1131,1132],{"class":121},"{",[115,1134,428],{"class":121},[115,1136,1137],{"class":431},"引言",[115,1139,428],{"class":121},[115,1141,393],{"class":121},[115,1143,1144],{"class":121}," \"",[115,1146,1147],{"class":431},"背景",[115,1149,428],{"class":121},[115,1151,393],{"class":121},[115,1153,1144],{"class":121},[115,1155,1156],{"class":431},"分析",[115,1158,428],{"class":121},[115,1160,393],{"class":121},[115,1162,1144],{"class":121},[115,1164,1165],{"class":431},"结论",[115,1167,428],{"class":121},[115,1169,1170],{"class":121},"}",[115,1172,217],{"class":121},[115,1174,1176,1179,1181,1184,1186,1189],{"class":117,"line":1175},56,[115,1177,1178],{"class":223},"        page ",[115,1180,227],{"class":121},[115,1182,1183],{"class":223}," doc",[115,1185,233],{"class":121},[115,1187,1188],{"class":210},"AddPage",[115,1190,1191],{"class":121},"()\n",[115,1193,1195,1198,1200,1202,1204,1206,1208,1210,1212,1214,1216],{"class":117,"line":1194},57,[115,1196,1197],{"class":223},"        page",[115,1199,233],{"class":121},[115,1201,356],{"class":210},[115,1203,326],{"class":121},[115,1205,361],{"class":329},[115,1207,332],{"class":121},[115,1209,335],{"class":125},[115,1211,233],{"class":121},[115,1213,370],{"class":125},[115,1215,343],{"class":121},[115,1217,217],{"class":121},[115,1219,1221,1223,1225,1227,1229,1231,1233,1235,1237,1239,1241,1243,1245,1247],{"class":117,"line":1220},58,[115,1222,380],{"class":223},[115,1224,233],{"class":121},[115,1226,385],{"class":210},[115,1228,253],{"class":121},[115,1230,19],{"class":297},[115,1232,393],{"class":121},[115,1234,396],{"class":121},[115,1236,399],{"class":329},[115,1238,332],{"class":121},[115,1240,335],{"class":125},[115,1242,233],{"class":121},[115,1244,408],{"class":125},[115,1246,343],{"class":121},[115,1248,217],{"class":121},[115,1250,1252,1254,1256,1258,1260,1263,1265,1267,1269,1271,1273,1276,1279,1281,1283,1285],{"class":117,"line":1251},59,[115,1253,418],{"class":223},[115,1255,233],{"class":121},[115,1257,423],{"class":210},[115,1259,253],{"class":121},[115,1261,1262],{"class":223},"title",[115,1264,393],{"class":121},[115,1266,230],{"class":223},[115,1268,233],{"class":121},[115,1270,453],{"class":210},[115,1272,253],{"class":121},[115,1274,1275],{"class":297},"18",[115,1277,1278],{"class":121},"),",[115,1280,230],{"class":223},[115,1282,233],{"class":121},[115,1284,443],{"class":210},[115,1286,1287],{"class":121},"())\n",[115,1289,1291,1293,1295,1297,1299,1301,1303,1305,1307,1310],{"class":117,"line":1290},60,[115,1292,418],{"class":223},[115,1294,233],{"class":121},[115,1296,673],{"class":210},[115,1298,253],{"class":121},[115,1300,256],{"class":223},[115,1302,233],{"class":121},[115,1304,292],{"class":210},[115,1306,253],{"class":121},[115,1308,1309],{"class":297},"5",[115,1311,461],{"class":121},[115,1313,1315,1317,1319,1321,1323,1326,1329,1331,1334,1336],{"class":117,"line":1314},61,[115,1316,418],{"class":223},[115,1318,233],{"class":121},[115,1320,423],{"class":210},[115,1322,253],{"class":121},[115,1324,1325],{"class":223},"title ",[115,1327,1328],{"class":121},"+",[115,1330,1144],{"class":121},[115,1332,1333],{"class":431}," 章节正文。",[115,1335,428],{"class":121},[115,1337,196],{"class":121},[115,1339,1341],{"class":117,"line":1340},62,[115,1342,467],{"class":121},[115,1344,1346],{"class":117,"line":1345},63,[115,1347,572],{"class":121},[115,1349,1351],{"class":117,"line":1350},64,[115,1352,1353],{"class":121},"    }\n",[115,1355,1357],{"class":117,"line":1356},65,[115,1358,133],{"emptyLinePlaceholder":132},[115,1360,1362,1365,1367,1370,1372,1374,1376,1379],{"class":117,"line":1361},66,[115,1363,1364],{"class":223},"    out",[115,1366,393],{"class":121},[115,1368,1369],{"class":223}," err ",[115,1371,227],{"class":121},[115,1373,1183],{"class":223},[115,1375,233],{"class":121},[115,1377,1378],{"class":210},"Generate",[115,1380,1191],{"class":121},[115,1382,1384,1387,1389,1392,1395],{"class":117,"line":1383},67,[115,1385,1386],{"class":139},"    if",[115,1388,1369],{"class":223},[115,1390,1391],{"class":121},"!=",[115,1393,1394],{"class":121}," nil",[115,1396,217],{"class":121},[115,1398,1400,1403,1405,1408],{"class":117,"line":1399},68,[115,1401,1402],{"class":210},"        panic",[115,1404,253],{"class":121},[115,1406,1407],{"class":223},"err",[115,1409,196],{"class":121},[115,1411,1413],{"class":117,"line":1412},69,[115,1414,1353],{"class":121},[115,1416,1418,1421,1424,1427,1429,1432,1434,1436,1439,1441,1443,1446,1448,1451],{"class":117,"line":1417},70,[115,1419,1420],{"class":223},"    _ ",[115,1422,1423],{"class":121},"=",[115,1425,1426],{"class":223}," os",[115,1428,233],{"class":121},[115,1430,1431],{"class":210},"WriteFile",[115,1433,253],{"class":121},[115,1435,428],{"class":121},[115,1437,1438],{"class":431},"report.pdf",[115,1440,428],{"class":121},[115,1442,393],{"class":121},[115,1444,1445],{"class":223}," out",[115,1447,393],{"class":121},[115,1449,1450],{"class":297}," 0o644",[115,1452,196],{"class":121},[115,1454,1456],{"class":117,"line":1455},71,[115,1457,1458],{"class":121},"}\n",[13,1460,1461,1462,1465,1466,1469,1470,1472],{},"生成 4 页，页眉右上显示 ",[17,1463,1464],{},"4","，页脚右下显示 ",[17,1467,1468],{},"1","〜",[17,1471,1464],{},"。代码里从没告诉 gpdf 文档有 4 页 —— gpdf 也是在分页完成后才知道的。",[39,1474,1476],{"id":1475},"为什么-page-x-of-y-难做","为什么 \"Page X of Y\" 难做",[13,1478,1479,1482,1483,1486],{},[17,1480,1481],{},"Y"," 的难点在于，画第 1 页时还不知道它是多少。50 页的报告里，第 47 页可能因为表格行无法放下而被拆到下一页。总数 ",[17,1484,1485],{},"50"," 只有在分页器跑完才能确定。但第 1 页的页脚早就画完了。",[13,1488,1489],{},"每个 PDF 库都会撞到这堵墙。Go 几个主流库的处理方式：",[1491,1492,1493,1506],"table",{},[1494,1495,1496],"thead",{},[1497,1498,1499,1503],"tr",{},[1500,1501,1502],"th",{},"库",[1500,1504,1505],{},"\"Page X of Y\" 实现",[1507,1508,1509,1525,1533,1541,1555],"tbody",{},[1497,1510,1511,1515],{},[1512,1513,1514],"td",{},"gofpdf",[1512,1516,1517,1520,1521,1524],{},[17,1518,1519],{},"pdf.AliasNbPages(\"{nb}\")","：在正文中写 ",[17,1522,1523],{},"{nb}"," 作为字面量，事后对 PDF 流做字符串替换。能用，但要记得调用，而且占位符是魔法字符串。",[1497,1526,1527,1530],{},[1512,1528,1529],{},"go-pdf/fpdf",[1512,1531,1532],{},"gofpdf 的 fork，机制相同。",[1497,1534,1535,1538],{},[1512,1536,1537],{},"signintech/gopdf",[1512,1539,1540],{},"没有原生支持。自己渲染一遍数页数，再重新渲染一遍。",[1497,1542,1543,1546],{},[1512,1544,1545],{},"maroto v2",[1512,1547,1548,1549,1551,1552,1554],{},"提供和 gpdf 类似的 ",[17,1550,323],{},"/",[17,1553,719],{}," 注册，内部也是两阶段。但底层是 gofpdf，所以在常见工作负载下比 gpdf 慢约 10 倍。",[1497,1556,1557,1560],{},[1512,1558,1559],{},"gpdf",[1512,1561,1562,1564,1565,1567],{},[17,1563,64],{}," / ",[17,1566,68],{},"：类型化方法调用，无魔法字符串，由内部第二阶段解析。",[13,1569,1570,1571,1573,1574,1577,1578,24,1580,1582],{},"只有 gpdf 把页码原语放在类型化 builder API 里。gofpdf 里把 ",[17,1572,1523],{}," 打错成 ",[17,1575,1576],{},"{nB}","，PDF 上就直接印出 ",[17,1579,1576],{},[17,1581,68],{}," 最糟糕只会是忘记调用 —— 那就什么都不显示，而不是显示错的数字。",[1584,1585,1586],"h3",{"id":1586},"第二阶段是怎么跑的",[13,1588,1589,1590,1592],{},"内部 ",[17,1591,64],{}," 会渲染成一个占位字符串 —— 没有任何真实字体字形会匹配的哨兵值。分页器布局完所有页面、确定总数后，会遍历已渲染的文本指令做替换：",[1594,1595,1596,1608],"ol",{},[47,1597,1598,1602,1603,52,1605,1607],{},[1599,1600,1601],"strong",{},"第一阶段 (分页)",": 渲染每一页，包括页眉页脚，把 ",[17,1604,1038],{},[17,1606,508],{}," 当作固定宽度的 token 处理。计算总页数。",[47,1609,1610,1613],{},[1599,1611,1612],{},"第二阶段 (解析)",": 回溯页树，找到每个哨兵值，替换为实际页码/总数。",[13,1615,1616],{},"占位符的宽度按预期最大页数预留 (启发式)，所以替换后布局不会偏移。右对齐的页码在 9 → 10 桁数变化时仍然对齐。",[13,1618,1619,1620,1623],{},"第二阶段你不用写。文档不用渲染两遍。调用 ",[17,1621,1622],{},"doc.Generate()"," 拿字节就好。",[39,1625,1626],{"id":1626},"页眉和页脚就是普通布局",[13,1628,1629,1630,1633,1634,1637,1638,1641],{},"从 gofpdf 过来的人在这里会迷惑。那边 ",[17,1631,1632],{},"SetHeaderFunc"," 是在固定 Y 坐标上回调，用绝对定位的 ",[17,1635,1636],{},"Cell(...)"," 放文字。在 gpdf 里，页眉的闭包收到的是 ",[17,1639,1640],{},"*template.PageBuilder"," —— 和正文同一个类型。网格相同，行列相同，样式选项相同。",[106,1643,1645],{"className":108,"code":1644,"language":110,"meta":111,"style":111},"doc.Header(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(2, func(c *template.ColBuilder) {\n            c.Image(\"logo.png\", template.ImageHeight(document.Mm(12)))\n        })\n        r.Col(8, func(c *template.ColBuilder) {\n            c.Text(\"Annual Report 2026\", template.Bold(), template.FontSize(14))\n        })\n        r.Col(2, func(c *template.ColBuilder) {\n            c.TotalPages(template.AlignRight())\n        })\n    })\n})\n",[17,1646,1647,1672,1697,1728,1770,1774,1804,1844,1848,1878,1896,1900,1904],{"__ignoreMap":111},[115,1648,1649,1652,1654,1656,1658,1660,1662,1664,1666,1668,1670],{"class":117,"line":118},[115,1650,1651],{"class":223},"doc",[115,1653,233],{"class":121},[115,1655,323],{"class":210},[115,1657,326],{"class":121},[115,1659,13],{"class":329},[115,1661,332],{"class":121},[115,1663,335],{"class":125},[115,1665,233],{"class":121},[115,1667,340],{"class":125},[115,1669,343],{"class":121},[115,1671,217],{"class":121},[115,1673,1674,1677,1679,1681,1683,1685,1687,1689,1691,1693,1695],{"class":117,"line":129},[115,1675,1676],{"class":223},"    p",[115,1678,233],{"class":121},[115,1680,356],{"class":210},[115,1682,326],{"class":121},[115,1684,361],{"class":329},[115,1686,332],{"class":121},[115,1688,335],{"class":125},[115,1690,233],{"class":121},[115,1692,370],{"class":125},[115,1694,343],{"class":121},[115,1696,217],{"class":121},[115,1698,1699,1702,1704,1706,1708,1710,1712,1714,1716,1718,1720,1722,1724,1726],{"class":117,"line":136},[115,1700,1701],{"class":223},"        r",[115,1703,233],{"class":121},[115,1705,385],{"class":210},[115,1707,253],{"class":121},[115,1709,868],{"class":297},[115,1711,393],{"class":121},[115,1713,396],{"class":121},[115,1715,399],{"class":329},[115,1717,332],{"class":121},[115,1719,335],{"class":125},[115,1721,233],{"class":121},[115,1723,408],{"class":125},[115,1725,343],{"class":121},[115,1727,217],{"class":121},[115,1729,1730,1733,1735,1738,1740,1742,1745,1747,1749,1751,1753,1756,1758,1760,1762,1764,1766,1768],{"class":117,"line":146},[115,1731,1732],{"class":223},"            c",[115,1734,233],{"class":121},[115,1736,1737],{"class":210},"Image",[115,1739,253],{"class":121},[115,1741,428],{"class":121},[115,1743,1744],{"class":431},"logo.png",[115,1746,428],{"class":121},[115,1748,393],{"class":121},[115,1750,230],{"class":223},[115,1752,233],{"class":121},[115,1754,1755],{"class":210},"ImageHeight",[115,1757,253],{"class":121},[115,1759,256],{"class":223},[115,1761,233],{"class":121},[115,1763,292],{"class":210},[115,1765,253],{"class":121},[115,1767,19],{"class":297},[115,1769,561],{"class":121},[115,1771,1772],{"class":117,"line":158},[115,1773,572],{"class":121},[115,1775,1776,1778,1780,1782,1784,1786,1788,1790,1792,1794,1796,1798,1800,1802],{"class":117,"line":163},[115,1777,1701],{"class":223},[115,1779,233],{"class":121},[115,1781,385],{"class":210},[115,1783,253],{"class":121},[115,1785,967],{"class":297},[115,1787,393],{"class":121},[115,1789,396],{"class":121},[115,1791,399],{"class":329},[115,1793,332],{"class":121},[115,1795,335],{"class":125},[115,1797,233],{"class":121},[115,1799,408],{"class":125},[115,1801,343],{"class":121},[115,1803,217],{"class":121},[115,1805,1806,1808,1810,1812,1814,1816,1819,1821,1823,1825,1827,1829,1831,1833,1835,1837,1839,1842],{"class":117,"line":173},[115,1807,1732],{"class":223},[115,1809,233],{"class":121},[115,1811,423],{"class":210},[115,1813,253],{"class":121},[115,1815,428],{"class":121},[115,1817,1818],{"class":431},"Annual Report 2026",[115,1820,428],{"class":121},[115,1822,393],{"class":121},[115,1824,230],{"class":223},[115,1826,233],{"class":121},[115,1828,443],{"class":210},[115,1830,446],{"class":121},[115,1832,230],{"class":223},[115,1834,233],{"class":121},[115,1836,453],{"class":210},[115,1838,253],{"class":121},[115,1840,1841],{"class":297},"14",[115,1843,461],{"class":121},[115,1845,1846],{"class":117,"line":183},[115,1847,572],{"class":121},[115,1849,1850,1852,1854,1856,1858,1860,1862,1864,1866,1868,1870,1872,1874,1876],{"class":117,"line":193},[115,1851,1701],{"class":223},[115,1853,233],{"class":121},[115,1855,385],{"class":210},[115,1857,253],{"class":121},[115,1859,868],{"class":297},[115,1861,393],{"class":121},[115,1863,396],{"class":121},[115,1865,399],{"class":329},[115,1867,332],{"class":121},[115,1869,335],{"class":125},[115,1871,233],{"class":121},[115,1873,408],{"class":125},[115,1875,343],{"class":121},[115,1877,217],{"class":121},[115,1879,1880,1882,1884,1886,1888,1890,1892,1894],{"class":117,"line":199},[115,1881,1732],{"class":223},[115,1883,233],{"class":121},[115,1885,508],{"class":210},[115,1887,253],{"class":121},[115,1889,335],{"class":223},[115,1891,233],{"class":121},[115,1893,517],{"class":210},[115,1895,1287],{"class":121},[115,1897,1898],{"class":117,"line":204},[115,1899,572],{"class":121},[115,1901,1902],{"class":117,"line":220},[115,1903,704],{"class":121},[115,1905,1906],{"class":117,"line":242},[115,1907,1908],{"class":121},"})\n",[13,1910,1911],{},"左 logo、中标题、右总页数。三列宽度合 12，和正文行规则一致。",[13,1913,1914,1915,1918],{},"页眉高度自动测量。gpdf 在正文布局前执行一次页眉闭包，量出渲染高度，从每一页的可用正文高度里减去。页脚同理。不需要传 ",[17,1916,1917],{},"headerHeight","。给页眉加一行，正文就会自动缩小。",[13,1920,1921],{},"页眉和页脚在所有页面上重复，包括内容溢出生成的页面。如果长表格溢出到第 12 页，第 12 页同样有页眉页脚。目前没有 \"仅首页\" 标志位 (见下文)。",[39,1923,1925],{"id":1924},"粗糙点把-page-x-of-y-写成一行","粗糙点：把 \"Page X of Y\" 写成一行",[13,1927,1928,1929,1932,1933,1936,1937,52,1940,1942],{},"这是我觉得 API 还可以做得更好的地方。没有 ",[17,1930,1931],{},"c.PageOf(\"Page %d of %d\")"," 这种辅助方法。要得到一个字面字符串 ",[17,1934,1935],{},"\"Page 3 of 12\"","，因为 ",[17,1938,1939],{},"c.Text()",[17,1941,64],{}," 是独立的列子元素，所以得用列拼起来：",[106,1944,1946],{"className":108,"code":1945,"language":110,"meta":111,"style":111},"r.Col(12, func(c *template.ColBuilder) {\n    c.AutoRow(func(r *template.RowBuilder) {\n        r.Col(3, func(c *template.ColBuilder) {\n            c.Text(\"Page\", template.AlignRight())\n        })\n        r.Col(2, func(c *template.ColBuilder) {\n            c.PageNumber(template.AlignCenter())\n        })\n        r.Col(2, func(c *template.ColBuilder) {\n            c.Text(\"of\", template.AlignCenter())\n        })\n        r.Col(3, func(c *template.ColBuilder) {\n            c.TotalPages(template.AlignLeft())\n        })\n        r.Col(2, func(c *template.ColBuilder) {})\n    })\n})\n",[17,1947,1948,1978,2003,2033,2060,2064,2094,2113,2117,2147,2174,2178,2208,2227,2231,2262,2266],{"__ignoreMap":111},[115,1949,1950,1952,1954,1956,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976],{"class":117,"line":118},[115,1951,361],{"class":223},[115,1953,233],{"class":121},[115,1955,385],{"class":210},[115,1957,253],{"class":121},[115,1959,19],{"class":297},[115,1961,393],{"class":121},[115,1963,396],{"class":121},[115,1965,399],{"class":329},[115,1967,332],{"class":121},[115,1969,335],{"class":125},[115,1971,233],{"class":121},[115,1973,408],{"class":125},[115,1975,343],{"class":121},[115,1977,217],{"class":121},[115,1979,1980,1983,1985,1987,1989,1991,1993,1995,1997,1999,2001],{"class":117,"line":129},[115,1981,1982],{"class":223},"    c",[115,1984,233],{"class":121},[115,1986,356],{"class":210},[115,1988,326],{"class":121},[115,1990,361],{"class":329},[115,1992,332],{"class":121},[115,1994,335],{"class":125},[115,1996,233],{"class":121},[115,1998,370],{"class":125},[115,2000,343],{"class":121},[115,2002,217],{"class":121},[115,2004,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2029,2031],{"class":117,"line":136},[115,2006,1701],{"class":223},[115,2008,233],{"class":121},[115,2010,385],{"class":210},[115,2012,253],{"class":121},[115,2014,686],{"class":297},[115,2016,393],{"class":121},[115,2018,396],{"class":121},[115,2020,399],{"class":329},[115,2022,332],{"class":121},[115,2024,335],{"class":125},[115,2026,233],{"class":121},[115,2028,408],{"class":125},[115,2030,343],{"class":121},[115,2032,217],{"class":121},[115,2034,2035,2037,2039,2041,2043,2045,2048,2050,2052,2054,2056,2058],{"class":117,"line":146},[115,2036,1732],{"class":223},[115,2038,233],{"class":121},[115,2040,423],{"class":210},[115,2042,253],{"class":121},[115,2044,428],{"class":121},[115,2046,2047],{"class":431},"Page",[115,2049,428],{"class":121},[115,2051,393],{"class":121},[115,2053,230],{"class":223},[115,2055,233],{"class":121},[115,2057,517],{"class":210},[115,2059,1287],{"class":121},[115,2061,2062],{"class":117,"line":158},[115,2063,572],{"class":121},[115,2065,2066,2068,2070,2072,2074,2076,2078,2080,2082,2084,2086,2088,2090,2092],{"class":117,"line":163},[115,2067,1701],{"class":223},[115,2069,233],{"class":121},[115,2071,385],{"class":210},[115,2073,253],{"class":121},[115,2075,868],{"class":297},[115,2077,393],{"class":121},[115,2079,396],{"class":121},[115,2081,399],{"class":329},[115,2083,332],{"class":121},[115,2085,335],{"class":125},[115,2087,233],{"class":121},[115,2089,408],{"class":125},[115,2091,343],{"class":121},[115,2093,217],{"class":121},[115,2095,2096,2098,2100,2102,2104,2106,2108,2111],{"class":117,"line":173},[115,2097,1732],{"class":223},[115,2099,233],{"class":121},[115,2101,1038],{"class":210},[115,2103,253],{"class":121},[115,2105,335],{"class":223},[115,2107,233],{"class":121},[115,2109,2110],{"class":210},"AlignCenter",[115,2112,1287],{"class":121},[115,2114,2115],{"class":117,"line":183},[115,2116,572],{"class":121},[115,2118,2119,2121,2123,2125,2127,2129,2131,2133,2135,2137,2139,2141,2143,2145],{"class":117,"line":193},[115,2120,1701],{"class":223},[115,2122,233],{"class":121},[115,2124,385],{"class":210},[115,2126,253],{"class":121},[115,2128,868],{"class":297},[115,2130,393],{"class":121},[115,2132,396],{"class":121},[115,2134,399],{"class":329},[115,2136,332],{"class":121},[115,2138,335],{"class":125},[115,2140,233],{"class":121},[115,2142,408],{"class":125},[115,2144,343],{"class":121},[115,2146,217],{"class":121},[115,2148,2149,2151,2153,2155,2157,2159,2162,2164,2166,2168,2170,2172],{"class":117,"line":199},[115,2150,1732],{"class":223},[115,2152,233],{"class":121},[115,2154,423],{"class":210},[115,2156,253],{"class":121},[115,2158,428],{"class":121},[115,2160,2161],{"class":431},"of",[115,2163,428],{"class":121},[115,2165,393],{"class":121},[115,2167,230],{"class":223},[115,2169,233],{"class":121},[115,2171,2110],{"class":210},[115,2173,1287],{"class":121},[115,2175,2176],{"class":117,"line":204},[115,2177,572],{"class":121},[115,2179,2180,2182,2184,2186,2188,2190,2192,2194,2196,2198,2200,2202,2204,2206],{"class":117,"line":220},[115,2181,1701],{"class":223},[115,2183,233],{"class":121},[115,2185,385],{"class":210},[115,2187,253],{"class":121},[115,2189,686],{"class":297},[115,2191,393],{"class":121},[115,2193,396],{"class":121},[115,2195,399],{"class":329},[115,2197,332],{"class":121},[115,2199,335],{"class":125},[115,2201,233],{"class":121},[115,2203,408],{"class":125},[115,2205,343],{"class":121},[115,2207,217],{"class":121},[115,2209,2210,2212,2214,2216,2218,2220,2222,2225],{"class":117,"line":242},[115,2211,1732],{"class":223},[115,2213,233],{"class":121},[115,2215,508],{"class":210},[115,2217,253],{"class":121},[115,2219,335],{"class":223},[115,2221,233],{"class":121},[115,2223,2224],{"class":210},"AlignLeft",[115,2226,1287],{"class":121},[115,2228,2229],{"class":117,"line":267},[115,2230,572],{"class":121},[115,2232,2233,2235,2237,2239,2241,2243,2245,2247,2249,2251,2253,2255,2257,2259],{"class":117,"line":304},[115,2234,1701],{"class":223},[115,2236,233],{"class":121},[115,2238,385],{"class":210},[115,2240,253],{"class":121},[115,2242,868],{"class":297},[115,2244,393],{"class":121},[115,2246,396],{"class":121},[115,2248,399],{"class":329},[115,2250,332],{"class":121},[115,2252,335],{"class":125},[115,2254,233],{"class":121},[115,2256,408],{"class":125},[115,2258,343],{"class":121},[115,2260,2261],{"class":121}," {})\n",[115,2263,2264],{"class":117,"line":310},[115,2265,704],{"class":121},[115,2267,2268],{"class":117,"line":315},[115,2269,1908],{"class":121},[13,2271,2272,2273,2276,2277,2280,2281,2284],{},"能用，看上去也还行。但本来一行格式化字符串能搞定的事情扩成了四列，是一道纸割。我们在考虑加 ",[17,2274,2275],{},"c.PageOf(format string, opts ...TextOption)","，用 ",[17,2278,2279],{},"fmt.Sprintf"," 风格的 ",[17,2282,2283],{},"%d"," 占位。如果你对 API 形态有想法，GitHub issue 上留言。",[13,2286,2287],{},"目前的实用快捷做法是去掉 \"Page\" 用斜杠隔开：",[106,2289,2291],{"className":108,"code":2290,"language":110,"meta":111,"style":111},"r.Col(6, func(c *template.ColBuilder) {\n    c.PageNumber(template.AlignRight())\n})\nr.Col(1, func(c *template.ColBuilder) {\n    c.Text(\"/\", template.AlignCenter())\n})\nr.Col(5, func(c *template.ColBuilder) {\n    c.TotalPages(template.AlignLeft())\n})\n",[17,2292,2293,2323,2341,2345,2375,2401,2405,2435,2453],{"__ignoreMap":111},[115,2294,2295,2297,2299,2301,2303,2305,2307,2309,2311,2313,2315,2317,2319,2321],{"class":117,"line":118},[115,2296,361],{"class":223},[115,2298,233],{"class":121},[115,2300,385],{"class":210},[115,2302,253],{"class":121},[115,2304,390],{"class":297},[115,2306,393],{"class":121},[115,2308,396],{"class":121},[115,2310,399],{"class":329},[115,2312,332],{"class":121},[115,2314,335],{"class":125},[115,2316,233],{"class":121},[115,2318,408],{"class":125},[115,2320,343],{"class":121},[115,2322,217],{"class":121},[115,2324,2325,2327,2329,2331,2333,2335,2337,2339],{"class":117,"line":129},[115,2326,1982],{"class":223},[115,2328,233],{"class":121},[115,2330,1038],{"class":210},[115,2332,253],{"class":121},[115,2334,335],{"class":223},[115,2336,233],{"class":121},[115,2338,517],{"class":210},[115,2340,1287],{"class":121},[115,2342,2343],{"class":117,"line":136},[115,2344,1908],{"class":121},[115,2346,2347,2349,2351,2353,2355,2357,2359,2361,2363,2365,2367,2369,2371,2373],{"class":117,"line":146},[115,2348,361],{"class":223},[115,2350,233],{"class":121},[115,2352,385],{"class":210},[115,2354,253],{"class":121},[115,2356,1468],{"class":297},[115,2358,393],{"class":121},[115,2360,396],{"class":121},[115,2362,399],{"class":329},[115,2364,332],{"class":121},[115,2366,335],{"class":125},[115,2368,233],{"class":121},[115,2370,408],{"class":125},[115,2372,343],{"class":121},[115,2374,217],{"class":121},[115,2376,2377,2379,2381,2383,2385,2387,2389,2391,2393,2395,2397,2399],{"class":117,"line":158},[115,2378,1982],{"class":223},[115,2380,233],{"class":121},[115,2382,423],{"class":210},[115,2384,253],{"class":121},[115,2386,428],{"class":121},[115,2388,1551],{"class":431},[115,2390,428],{"class":121},[115,2392,393],{"class":121},[115,2394,230],{"class":223},[115,2396,233],{"class":121},[115,2398,2110],{"class":210},[115,2400,1287],{"class":121},[115,2402,2403],{"class":117,"line":163},[115,2404,1908],{"class":121},[115,2406,2407,2409,2411,2413,2415,2417,2419,2421,2423,2425,2427,2429,2431,2433],{"class":117,"line":173},[115,2408,361],{"class":223},[115,2410,233],{"class":121},[115,2412,385],{"class":210},[115,2414,253],{"class":121},[115,2416,1309],{"class":297},[115,2418,393],{"class":121},[115,2420,396],{"class":121},[115,2422,399],{"class":329},[115,2424,332],{"class":121},[115,2426,335],{"class":125},[115,2428,233],{"class":121},[115,2430,408],{"class":125},[115,2432,343],{"class":121},[115,2434,217],{"class":121},[115,2436,2437,2439,2441,2443,2445,2447,2449,2451],{"class":117,"line":183},[115,2438,1982],{"class":223},[115,2440,233],{"class":121},[115,2442,508],{"class":210},[115,2444,253],{"class":121},[115,2446,335],{"class":223},[115,2448,233],{"class":121},[115,2450,2224],{"class":210},[115,2452,1287],{"class":121},[115,2454,2455],{"class":117,"line":193},[115,2456,1908],{"class":121},[13,2458,2459,2462,2463,2466,2467,2470],{},[17,2460,2461],{},"3 / 12"," 作为页脚足够清晰。要写 ",[17,2464,2465],{},"第 3 页 / 共 12 页"," 也是同样的方法，多塞几个 ",[17,2468,2469],{},"c.Text"," 即可。",[39,2472,2473],{"id":2473},"常见模式",[13,2475,2476],{},"实务中经常用到的几种。",[13,2478,2479,2482,2483,2485,2486,2489],{},[1599,2480,2481],{},"标题下加分割线。"," 多加一个 ",[17,2484,356],{},"，里面放 ",[17,2487,2488],{},"c.Line()","。开头的示例就是这样。",[13,2491,2492,2495,2496,24],{},[1599,2493,2494],{},"居中的\"保密\"页脚。"," 一行一列、",[17,2497,2110],{},[106,2499,2501],{"className":108,"code":2500,"language":110,"meta":111,"style":111},"doc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"机密 — 内部使用\",\n                template.AlignCenter(),\n                template.FontSize(8),\n                template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n",[17,2502,2503,2527,2551,2581,2601,2613,2627,2649,2653,2657],{"__ignoreMap":111},[115,2504,2505,2507,2509,2511,2513,2515,2517,2519,2521,2523,2525],{"class":117,"line":118},[115,2506,1651],{"class":223},[115,2508,233],{"class":121},[115,2510,719],{"class":210},[115,2512,326],{"class":121},[115,2514,13],{"class":329},[115,2516,332],{"class":121},[115,2518,335],{"class":125},[115,2520,233],{"class":121},[115,2522,340],{"class":125},[115,2524,343],{"class":121},[115,2526,217],{"class":121},[115,2528,2529,2531,2533,2535,2537,2539,2541,2543,2545,2547,2549],{"class":117,"line":129},[115,2530,1676],{"class":223},[115,2532,233],{"class":121},[115,2534,356],{"class":210},[115,2536,326],{"class":121},[115,2538,361],{"class":329},[115,2540,332],{"class":121},[115,2542,335],{"class":125},[115,2544,233],{"class":121},[115,2546,370],{"class":125},[115,2548,343],{"class":121},[115,2550,217],{"class":121},[115,2552,2553,2555,2557,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579],{"class":117,"line":136},[115,2554,1701],{"class":223},[115,2556,233],{"class":121},[115,2558,385],{"class":210},[115,2560,253],{"class":121},[115,2562,19],{"class":297},[115,2564,393],{"class":121},[115,2566,396],{"class":121},[115,2568,399],{"class":329},[115,2570,332],{"class":121},[115,2572,335],{"class":125},[115,2574,233],{"class":121},[115,2576,408],{"class":125},[115,2578,343],{"class":121},[115,2580,217],{"class":121},[115,2582,2583,2585,2587,2589,2591,2593,2596,2598],{"class":117,"line":146},[115,2584,1732],{"class":223},[115,2586,233],{"class":121},[115,2588,423],{"class":210},[115,2590,253],{"class":121},[115,2592,428],{"class":121},[115,2594,2595],{"class":431},"机密 — 内部使用",[115,2597,428],{"class":121},[115,2599,2600],{"class":121},",\n",[115,2602,2603,2606,2608,2610],{"class":117,"line":158},[115,2604,2605],{"class":223},"                template",[115,2607,233],{"class":121},[115,2609,2110],{"class":210},[115,2611,2612],{"class":121},"(),\n",[115,2614,2615,2617,2619,2621,2623,2625],{"class":117,"line":163},[115,2616,2605],{"class":223},[115,2618,233],{"class":121},[115,2620,453],{"class":210},[115,2622,253],{"class":121},[115,2624,967],{"class":297},[115,2626,264],{"class":121},[115,2628,2629,2631,2633,2635,2637,2639,2641,2643,2645,2647],{"class":117,"line":173},[115,2630,2605],{"class":223},[115,2632,233],{"class":121},[115,2634,543],{"class":210},[115,2636,253],{"class":121},[115,2638,548],{"class":223},[115,2640,233],{"class":121},[115,2642,553],{"class":210},[115,2644,253],{"class":121},[115,2646,558],{"class":297},[115,2648,561],{"class":121},[115,2650,2651],{"class":117,"line":183},[115,2652,572],{"class":121},[115,2654,2655],{"class":117,"line":193},[115,2656,704],{"class":121},[115,2658,2659],{"class":117,"line":199},[115,2660,1908],{"class":121},[13,2662,2663,2664,2667],{},"国内公司的报告里经常还会加\"打印日期: 2026年5月19日\"\"文档编号: DOC-2026-0517\"之类的几行。再叠几个 ",[17,2665,2666],{},"c.Text(...)"," 就好。",[13,2669,2670,2673,2674,2677,2678,2680,2681,24],{},[1599,2671,2672],{},"左 logo、右页码。"," 8/4 或 6/6 分列。左边 ",[17,2675,2676],{},"c.Image(...)","，右边 ",[17,2679,64],{}," 加 ",[17,2682,517],{},[13,2684,2685,2688,2689,2691],{},[1599,2686,2687],{},"页脚显示\"下接下页\"。"," 当前不支持。页眉/页脚闭包只收到 ",[17,2690,340],{},"，不知道当前页索引，所以无法分支判断\"是不是最后一页\"。要在正文里加，反而需要事先知道总页数，矛盾。在功能需求清单里。",[13,2693,2694,2697,2698,2701],{},[1599,2695,2696],{},"首页用不同的页眉。"," 同样原因目前不支持。变通办法是首页正文顶部塞一个 spacer 让页眉看上去为空，从第二页开始恢复 —— 比较丑。",[17,2699,2700],{},"doc.HeaderOn(pages, fn)"," 变种在设计中。",[39,2703,2705],{"id":2704},"cjk-直接能用","CJK 直接能用",[13,2707,2708,2709,2711,2712,2715],{},"gpdf 不依赖 CGO 就能做 TrueType 字体子集化。中文、日文、韩文可以直接 ",[17,2710,2666],{},"。没有 ",[17,2713,2714],{},"AddUTF8Font"," 之类的仪式感操作，只要字体覆盖了你要的字符，就不会出豆腐字。",[106,2717,2719],{"className":108,"code":2718,"language":110,"meta":111,"style":111},"doc := template.New(\n    template.WithPageSize(document.A4),\n    template.WithFont(\"NotoSansSC\", notoSansSCRegular),\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(\"机密\", template.FontFamily(\"NotoSansSC\"), template.FontSize(8))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            c.PageNumber(template.AlignRight(), template.FontSize(8))\n        })\n    })\n})\n",[17,2720,2721,2736,2755,2780,2784,2788,2812,2836,2866,2914,2918,2948,2978,2982,2986],{"__ignoreMap":111},[115,2722,2723,2726,2728,2730,2732,2734],{"class":117,"line":118},[115,2724,2725],{"class":223},"doc ",[115,2727,227],{"class":121},[115,2729,230],{"class":223},[115,2731,233],{"class":121},[115,2733,236],{"class":210},[115,2735,239],{"class":121},[115,2737,2738,2741,2743,2745,2747,2749,2751,2753],{"class":117,"line":129},[115,2739,2740],{"class":223},"    template",[115,2742,233],{"class":121},[115,2744,250],{"class":210},[115,2746,253],{"class":121},[115,2748,256],{"class":223},[115,2750,233],{"class":121},[115,2752,261],{"class":223},[115,2754,264],{"class":121},[115,2756,2757,2759,2761,2764,2766,2768,2771,2773,2775,2778],{"class":117,"line":136},[115,2758,2740],{"class":223},[115,2760,233],{"class":121},[115,2762,2763],{"class":210},"WithFont",[115,2765,253],{"class":121},[115,2767,428],{"class":121},[115,2769,2770],{"class":431},"NotoSansSC",[115,2772,428],{"class":121},[115,2774,393],{"class":121},[115,2776,2777],{"class":223}," notoSansSCRegular",[115,2779,264],{"class":121},[115,2781,2782],{"class":117,"line":146},[115,2783,196],{"class":121},[115,2785,2786],{"class":117,"line":158},[115,2787,133],{"emptyLinePlaceholder":132},[115,2789,2790,2792,2794,2796,2798,2800,2802,2804,2806,2808,2810],{"class":117,"line":163},[115,2791,1651],{"class":223},[115,2793,233],{"class":121},[115,2795,719],{"class":210},[115,2797,326],{"class":121},[115,2799,13],{"class":329},[115,2801,332],{"class":121},[115,2803,335],{"class":125},[115,2805,233],{"class":121},[115,2807,340],{"class":125},[115,2809,343],{"class":121},[115,2811,217],{"class":121},[115,2813,2814,2816,2818,2820,2822,2824,2826,2828,2830,2832,2834],{"class":117,"line":173},[115,2815,1676],{"class":223},[115,2817,233],{"class":121},[115,2819,356],{"class":210},[115,2821,326],{"class":121},[115,2823,361],{"class":329},[115,2825,332],{"class":121},[115,2827,335],{"class":125},[115,2829,233],{"class":121},[115,2831,370],{"class":125},[115,2833,343],{"class":121},[115,2835,217],{"class":121},[115,2837,2838,2840,2842,2844,2846,2848,2850,2852,2854,2856,2858,2860,2862,2864],{"class":117,"line":183},[115,2839,1701],{"class":223},[115,2841,233],{"class":121},[115,2843,385],{"class":210},[115,2845,253],{"class":121},[115,2847,390],{"class":297},[115,2849,393],{"class":121},[115,2851,396],{"class":121},[115,2853,399],{"class":329},[115,2855,332],{"class":121},[115,2857,335],{"class":125},[115,2859,233],{"class":121},[115,2861,408],{"class":125},[115,2863,343],{"class":121},[115,2865,217],{"class":121},[115,2867,2868,2870,2872,2874,2876,2878,2881,2883,2885,2887,2889,2892,2894,2896,2898,2900,2902,2904,2906,2908,2910,2912],{"class":117,"line":193},[115,2869,1732],{"class":223},[115,2871,233],{"class":121},[115,2873,423],{"class":210},[115,2875,253],{"class":121},[115,2877,428],{"class":121},[115,2879,2880],{"class":431},"机密",[115,2882,428],{"class":121},[115,2884,393],{"class":121},[115,2886,230],{"class":223},[115,2888,233],{"class":121},[115,2890,2891],{"class":210},"FontFamily",[115,2893,253],{"class":121},[115,2895,428],{"class":121},[115,2897,2770],{"class":431},[115,2899,428],{"class":121},[115,2901,1278],{"class":121},[115,2903,230],{"class":223},[115,2905,233],{"class":121},[115,2907,453],{"class":210},[115,2909,253],{"class":121},[115,2911,967],{"class":297},[115,2913,461],{"class":121},[115,2915,2916],{"class":117,"line":199},[115,2917,572],{"class":121},[115,2919,2920,2922,2924,2926,2928,2930,2932,2934,2936,2938,2940,2942,2944,2946],{"class":117,"line":204},[115,2921,1701],{"class":223},[115,2923,233],{"class":121},[115,2925,385],{"class":210},[115,2927,253],{"class":121},[115,2929,390],{"class":297},[115,2931,393],{"class":121},[115,2933,396],{"class":121},[115,2935,399],{"class":329},[115,2937,332],{"class":121},[115,2939,335],{"class":125},[115,2941,233],{"class":121},[115,2943,408],{"class":125},[115,2945,343],{"class":121},[115,2947,217],{"class":121},[115,2949,2950,2952,2954,2956,2958,2960,2962,2964,2966,2968,2970,2972,2974,2976],{"class":117,"line":220},[115,2951,1732],{"class":223},[115,2953,233],{"class":121},[115,2955,1038],{"class":210},[115,2957,253],{"class":121},[115,2959,335],{"class":223},[115,2961,233],{"class":121},[115,2963,517],{"class":210},[115,2965,446],{"class":121},[115,2967,230],{"class":223},[115,2969,233],{"class":121},[115,2971,453],{"class":210},[115,2973,253],{"class":121},[115,2975,967],{"class":297},[115,2977,461],{"class":121},[115,2979,2980],{"class":117,"line":242},[115,2981,572],{"class":121},[115,2983,2984],{"class":117,"line":267},[115,2985,704],{"class":121},[115,2987,2988],{"class":117,"line":304},[115,2989,1908],{"class":121},[13,2991,2992,2993,2996],{},"最终 PDF 里嵌入的子集只包含\"实际用到的字形\"。60 页报告页脚只有 ",[17,2994,2995],{},"\"机密\"","，那就只从 NotoSansSC 里嵌入两个字形，不是 20000 个。在增值税电子发票之类需要 PDF/A-3 且对文件大小敏感的场景里很有用。",[39,2998,2999],{"id":2999},"性能",[13,3001,3002],{},"如果你要规模化生成 PDF，这部分重要。",[13,3004,3005],{},"第二阶段不是免费的，但很便宜。100 页文档在 M1 上第二阶段不到 50µs。占总生成时间不到 1%。gpdf 单页基准 13µs，100 页基准 683µs。页码解析是与页面复杂度无关的常数因子。",[13,3007,3008,3009,3011],{},"对比一下，gofpdf 的 ",[17,3010,33],{}," 是在压缩决策之后对整个内容流做字符串替换，包含别名的流要重新压缩。在 gofpdf 自己的基准里大概占 100 页文档总时间的 2〜4%。gpdf 的替换发生在流编码之前，所以更快。",[13,3013,3014],{},"每天百万级 PDF 生成时差距很明显。每天十个的话无所谓。",[39,3016,3018],{"id":3017},"faq","FAQ",[13,3020,3021,3024,3025,3028],{},[1599,3022,3023],{},"页眉/页脚高度算在页边距里吗？","\n不算。gpdf 量出页眉和页脚的实际高度，正文可用高度按 ",[17,3026,3027],{},"pageHeight - top_margin - headerHeight - footerHeight - bottom_margin"," 计算。上边距 20mm、页眉 15mm，正文从页顶 35mm 开始。",[13,3030,3031,3034],{},[1599,3032,3033],{},"能让每页页眉高度不同吗？","\n不能。页眉闭包只评估一次用于测量，结果对整个文档固定。要可变高度的话，只能设一个最大高度并用空白调整内容。",[13,3036,3037,3040],{},[1599,3038,3039],{},"正文为空的页面会显示页眉/页脚吗？","\ngpdf 不生成空页。正文放得下 3 页就只有 3 页。页眉页脚就只在那 3 页上出现。",[13,3042,3043,3046,3047,3050],{},[1599,3044,3045],{},"纵横混排文档里横向页只想要不同页眉？","\n按页设置 ",[17,3048,3049],{},"WithPageSize(...)"," 改方向是支持的，但页眉/页脚闭包对所有页面相同。实务上把页眉做成在两种方向下都看得过去的居中布局比较稳。",[13,3052,3053,3056,3057,3060,3061,52,3064,3060,3067,24,3070,3073],{},[1599,3054,3055],{},"JSON 模板入口能用吗？","\n能用。JSON schema 里有 ",[17,3058,3059],{},"header","、",[17,3062,3063],{},"footer",[17,3065,3066],{},"{\"type\": \"pageNumber\"}",[17,3068,3069],{},"{\"type\": \"totalPages\"}",[17,3071,3072],{},"gpdf/_examples/json/26_page_number_test.go"," 验证 JSON 入口和 builder 入口生成相同的 golden PDF。",[13,3075,3076,3079,3080,3083],{},[1599,3077,3078],{},"Go text/template 入口呢？","\n能用。",[17,3081,3082],{},"gpdf/_examples/gotemplate/26_page_number_test.go"," 跑同一场景。无论是 builder、JSON 还是 Go template，底下都是同一个两阶段分页器。",[39,3085,3086],{"id":3086},"下一步",[13,3088,3089],{},"页眉、页脚、页码是报告里最不起眼的部分 —— 但也是让报告看起来\"做完了\"的关键。如果你之前都是在低级 PDF 库上手写这部分，本文几行代码就能搞定。复制示例改改字符串，发布。",[13,3091,3092,3093,3096],{},"未解决的问题 —— ",[17,3094,3095],{},"c.PageOf(...)"," 单字符串格式化、首页不同页眉、\"是否最后一页\"检测 —— 在排队中。任何一个挡住你了，去 GitHub issue 提一下。具体的用例比抽象的需求更能决定 API 长什么样。",[39,3098,3100],{"id":3099},"用-gpdf-试试","用 gpdf 试试",[13,3102,3103],{},"gpdf 是 Go 的 PDF 生成库，MIT 许可，零依赖，CJK 支持。",[106,3105,3109],{"className":3106,"code":3107,"language":3108,"meta":111,"style":111},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[17,3110,3111],{"__ignoreMap":111},[115,3112,3113,3115,3118],{"class":117,"line":118},[115,3114,110],{"class":125},[115,3116,3117],{"class":431}," get",[115,3119,3120],{"class":431}," github.com/gpdf-dev/gpdf\n",[13,3122,3123,3130,3131],{},[3124,3125,3129],"a",{"href":3126,"rel":3127},"https://github.com/gpdf-dev/gpdf",[3128],"nofollow","⭐ 在 GitHub 上 Star"," · ",[3124,3132,3135],{"href":3133,"rel":3134},"https://gpdf.dev/zh/docs/quickstart",[3128],"阅读文档",[3137,3138,3139],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":111,"searchDepth":129,"depth":129,"links":3141},[3142,3143,3144,3147,3148,3149,3150,3151,3152,3153,3154],{"id":41,"depth":129,"text":42},{"id":93,"depth":129,"text":93},{"id":1475,"depth":129,"text":1476,"children":3145},[3146],{"id":1586,"depth":136,"text":1586},{"id":1626,"depth":129,"text":1626},{"id":1924,"depth":129,"text":1925},{"id":2473,"depth":129,"text":2473},{"id":2704,"depth":129,"text":2705},{"id":2999,"depth":129,"text":2999},{"id":3017,"depth":129,"text":3018},{"id":3086,"depth":129,"text":3086},{"id":3099,"depth":129,"text":3100},"2026-05-19","用 gpdf 在 Go PDF 中加入页眉、页脚和『Page X of Y』：两个 builder 方法和两阶段分页器自动填充总页数，无需 hack。",false,"md",{"name":3160,"totalTime":3161,"tools":3162,"steps":3165},"在 Go PDF 中加入页眉、页脚和 Page X of Y 页码","PT15M",[3163,3164],"Go 1.22+","github.com/gpdf-dev/gpdf",[3166,3169,3172,3175,3178],{"name":3167,"text":3168},"用 template.New 创建文档","调用 template.New 并传入 WithPageSize(document.A4) 和 WithMargins。所有页面级配置都在这里。",{"name":3170,"text":3171},"用 doc.Header 注册页眉","传入一个接收 *template.PageBuilder 的闭包。在闭包内使用与正文相同的 AutoRow 和 Col 12 栏网格。用 c.TotalPages() 输出总页数。",{"name":3173,"text":3174},"用 doc.Footer 注册页脚","再传入一个闭包。把 c.PageNumber() 放入某一列即可输出当前页码。页眉和页脚在所有页面 (包括溢出页) 上重复。",{"name":3176,"text":3177},"用 doc.AddPage 添加正文","为每个逻辑页面调用 doc.AddPage，然后填入行和列。正文可用高度会自动减去测量得到的页眉/页脚高度。",{"name":3179,"text":3180},"用 doc.Generate 输出 PDF","调用 doc.Generate 获取 []byte，或用 doc.Render(w) 写入 io.Writer。字节返回前，第二阶段会自动把页码占位符替换为实际数字。",null,{},"/zh/blog/page-numbers-headers-footers",{"title":5,"description":3156},"zh/blog/027.page-numbers-headers-footers",[3187,3188],"tutorial","internals","qVYg82zA9iiGUOKbBOUAEHlzMC8nysyyiJSce4LtHSg",1779199016540]