[{"data":1,"prerenderedAt":3207},["ShallowReactive",2],{"blog-es-page-numbers-headers-footers":3},{"id":4,"title":5,"author":6,"body":9,"date":3172,"description":3173,"draft":3174,"extension":3175,"howTo":3176,"image":3198,"meta":3199,"navigation":134,"path":3200,"seo":3201,"stem":3202,"tags":3203,"updated":3198,"__hash__":3206},"blogEs/es/blog/027.page-numbers-headers-footers.md","Números de página, cabeceras y pies de página en PDFs Go con gpdf",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":3157},"minimark",[12,25,36,39,44,85,92,96,107,1468,1482,1486,1497,1500,1578,1594,1599,1605,1626,1629,1636,1640,1655,1922,1925,1932,1935,1939,1956,2283,2298,2301,2470,2484,2488,2491,2503,2512,2675,2682,2693,2702,2711,2715,2725,2999,3006,3010,3013,3016,3022,3025,3029,3039,3045,3051,3061,3086,3100,3104,3107,3114,3118,3121,3138,3153],[13,14,15,16,20,21,24],"p",{},"Un informe financiero de 60 páginas. Alguien abre la página 12 en la cola de impresión y hace una pregunta: ¿qué página es esta y cuántas quedan? Si el pie sólo dice ",[17,18,19],"code",{},"12",", nadie lo sabe. Tiene que decir ",[17,22,23],{},"12 / 60",".",[13,26,27,28,31,32,35],{},"Ese ",[17,29,30],{},"60"," es la parte donde la mayoría de las librerías PDF fallan. O el total de páginas no está disponible cuando escribes el pie, o se esconde detrás de un token tipo ",[17,33,34],{},"AliasNbPages"," que tienes que llamar después de construir, o terminas renderizando el documento dos veces y descartando la primera pasada.",[13,37,38],{},"gpdf lo resuelve limpiamente con dos métodos del builder y un paginador interno de dos pasadas. Este artículo cuenta cómo es la API, cómo funciona por dentro y el único punto áspero del que conviene estar al tanto.",[40,41,43],"h2",{"id":42},"tldr","TL;DR",[45,46,47,58,61,71,74],"ul",{},[48,49,50,53,54,57],"li",{},[17,51,52],{},"doc.Header(fn)"," y ",[17,55,56],{},"doc.Footer(fn)"," registran una closure que se ejecuta en cada página.",[48,59,60],{},"Dentro de la closure se usa el mismo grid de 12 columnas que en el cuerpo.",[48,62,63,66,67,70],{},[17,64,65],{},"c.PageNumber()"," imprime el número de página actual. ",[17,68,69],{},"c.TotalPages()"," imprime el total.",[48,72,73],{},"El total se resuelve en una segunda pasada, después de que termina la paginación. No tienes que escribir una build de dos pasadas tú mismo.",[48,75,76,77,80,81,84],{},"Un detalle áspero: no existe un helper ",[17,78,79],{},"c.PageNumberOf(total)"," que pinte ",[17,82,83],{},"\"3 of 12\""," como una sola cadena en línea. Se compone con tres columnas. Más abajo.",[13,86,87,88,91],{},"Todo el código aquí viene de ",[17,89,90],{},"gpdf/_examples/builder/26_page_number_test.go",", que forma parte de la suite de tests.",[40,93,95],{"id":94},"todo-en-un-solo-archivo","Todo en un solo archivo",[13,97,98,99,102,103,106],{},"Programa completo. Guárdalo como ",[17,100,101],{},"main.go",", ejecuta ",[17,104,105],{},"go run main.go"," y obtienes un PDF de 4 páginas con cabecera que muestra el total y pie con el número actual.",[108,109,114],"pre",{"className":110,"code":111,"language":112,"meta":113,"style":113},"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(\"Informe trimestral\", 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{\"Introducción\", \"Contexto\", \"Análisis\", \"Conclusión\"} {\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(\"Cuerpo de la sección \" + 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,115,116,129,136,146,158,163,173,183,193,199,204,220,241,266,303,309,314,347,376,414,463,469,500,534,563,568,574,599,630,665,690,695,700,706,711,737,762,793,816,848,872,877,882,907,938,971,994,999,1030,1062,1085,1090,1095,1100,1105,1174,1193,1219,1250,1289,1313,1347,1352,1357,1363,1368,1390,1406,1419,1424,1462],{"__ignoreMap":113},[117,118,121,125],"span",{"class":119,"line":120},"line",1,[117,122,124],{"class":123},"sMK4o","package",[117,126,128],{"class":127},"sBMFI"," main\n",[117,130,132],{"class":119,"line":131},2,[117,133,135],{"emptyLinePlaceholder":134},true,"\n",[117,137,139,143],{"class":119,"line":138},3,[117,140,142],{"class":141},"s7zQu","import",[117,144,145],{"class":123}," (\n",[117,147,149,152,155],{"class":119,"line":148},4,[117,150,151],{"class":123},"    \"",[117,153,154],{"class":127},"os",[117,156,157],{"class":123},"\"\n",[117,159,161],{"class":119,"line":160},5,[117,162,135],{"emptyLinePlaceholder":134},[117,164,166,168,171],{"class":119,"line":165},6,[117,167,151],{"class":123},[117,169,170],{"class":127},"github.com/gpdf-dev/gpdf/document",[117,172,157],{"class":123},[117,174,176,178,181],{"class":119,"line":175},7,[117,177,151],{"class":123},[117,179,180],{"class":127},"github.com/gpdf-dev/gpdf/pdf",[117,182,157],{"class":123},[117,184,186,188,191],{"class":119,"line":185},8,[117,187,151],{"class":123},[117,189,190],{"class":127},"github.com/gpdf-dev/gpdf/template",[117,192,157],{"class":123},[117,194,196],{"class":119,"line":195},9,[117,197,198],{"class":123},")\n",[117,200,202],{"class":119,"line":201},10,[117,203,135],{"emptyLinePlaceholder":134},[117,205,207,210,214,217],{"class":119,"line":206},11,[117,208,209],{"class":123},"func",[117,211,213],{"class":212},"s2Zo4"," main",[117,215,216],{"class":123},"()",[117,218,219],{"class":123}," {\n",[117,221,223,227,230,233,235,238],{"class":119,"line":222},12,[117,224,226],{"class":225},"sTEyZ","    doc ",[117,228,229],{"class":123},":=",[117,231,232],{"class":225}," template",[117,234,24],{"class":123},[117,236,237],{"class":212},"New",[117,239,240],{"class":123},"(\n",[117,242,244,247,249,252,255,258,260,263],{"class":119,"line":243},13,[117,245,246],{"class":225},"        template",[117,248,24],{"class":123},[117,250,251],{"class":212},"WithPageSize",[117,253,254],{"class":123},"(",[117,256,257],{"class":225},"document",[117,259,24],{"class":123},[117,261,262],{"class":225},"A4",[117,264,265],{"class":123},"),\n",[117,267,269,271,273,276,278,280,282,285,287,289,291,294,296,300],{"class":119,"line":268},14,[117,270,246],{"class":225},[117,272,24],{"class":123},[117,274,275],{"class":212},"WithMargins",[117,277,254],{"class":123},[117,279,257],{"class":225},[117,281,24],{"class":123},[117,283,284],{"class":212},"UniformEdges",[117,286,254],{"class":123},[117,288,257],{"class":225},[117,290,24],{"class":123},[117,292,293],{"class":212},"Mm",[117,295,254],{"class":123},[117,297,299],{"class":298},"sbssI","20",[117,301,302],{"class":123},"))),\n",[117,304,306],{"class":119,"line":305},15,[117,307,308],{"class":123},"    )\n",[117,310,312],{"class":119,"line":311},16,[117,313,135],{"emptyLinePlaceholder":134},[117,315,317,320,322,325,328,331,334,337,339,342,345],{"class":119,"line":316},17,[117,318,319],{"class":225},"    doc",[117,321,24],{"class":123},[117,323,324],{"class":212},"Header",[117,326,327],{"class":123},"(func(",[117,329,13],{"class":330},"sHdIc",[117,332,333],{"class":123}," *",[117,335,336],{"class":127},"template",[117,338,24],{"class":123},[117,340,341],{"class":127},"PageBuilder",[117,343,344],{"class":123},")",[117,346,219],{"class":123},[117,348,350,353,355,358,360,363,365,367,369,372,374],{"class":119,"line":349},18,[117,351,352],{"class":225},"        p",[117,354,24],{"class":123},[117,356,357],{"class":212},"AutoRow",[117,359,327],{"class":123},[117,361,362],{"class":330},"r",[117,364,333],{"class":123},[117,366,336],{"class":127},[117,368,24],{"class":123},[117,370,371],{"class":127},"RowBuilder",[117,373,344],{"class":123},[117,375,219],{"class":123},[117,377,379,382,384,387,389,392,395,398,401,403,405,407,410,412],{"class":119,"line":378},19,[117,380,381],{"class":225},"            r",[117,383,24],{"class":123},[117,385,386],{"class":212},"Col",[117,388,254],{"class":123},[117,390,391],{"class":298},"6",[117,393,394],{"class":123},",",[117,396,397],{"class":123}," func(",[117,399,400],{"class":330},"c",[117,402,333],{"class":123},[117,404,336],{"class":127},[117,406,24],{"class":123},[117,408,409],{"class":127},"ColBuilder",[117,411,344],{"class":123},[117,413,219],{"class":123},[117,415,417,420,422,425,427,430,434,436,438,440,442,445,448,450,452,455,457,460],{"class":119,"line":416},20,[117,418,419],{"class":225},"                c",[117,421,24],{"class":123},[117,423,424],{"class":212},"Text",[117,426,254],{"class":123},[117,428,429],{"class":123},"\"",[117,431,433],{"class":432},"sfazB","Informe trimestral",[117,435,429],{"class":123},[117,437,394],{"class":123},[117,439,232],{"class":225},[117,441,24],{"class":123},[117,443,444],{"class":212},"Bold",[117,446,447],{"class":123},"(),",[117,449,232],{"class":225},[117,451,24],{"class":123},[117,453,454],{"class":212},"FontSize",[117,456,254],{"class":123},[117,458,459],{"class":298},"10",[117,461,462],{"class":123},"))\n",[117,464,466],{"class":119,"line":465},21,[117,467,468],{"class":123},"            })\n",[117,470,472,474,476,478,480,482,484,486,488,490,492,494,496,498],{"class":119,"line":471},22,[117,473,381],{"class":225},[117,475,24],{"class":123},[117,477,386],{"class":212},[117,479,254],{"class":123},[117,481,391],{"class":298},[117,483,394],{"class":123},[117,485,397],{"class":123},[117,487,400],{"class":330},[117,489,333],{"class":123},[117,491,336],{"class":127},[117,493,24],{"class":123},[117,495,409],{"class":127},[117,497,344],{"class":123},[117,499,219],{"class":123},[117,501,503,505,507,510,512,514,516,519,521,523,525,527,529,532],{"class":119,"line":502},23,[117,504,419],{"class":225},[117,506,24],{"class":123},[117,508,509],{"class":212},"TotalPages",[117,511,254],{"class":123},[117,513,336],{"class":225},[117,515,24],{"class":123},[117,517,518],{"class":212},"AlignRight",[117,520,447],{"class":123},[117,522,232],{"class":225},[117,524,24],{"class":123},[117,526,454],{"class":212},[117,528,254],{"class":123},[117,530,531],{"class":298},"9",[117,533,265],{"class":123},[117,535,537,540,542,545,547,550,552,555,557,560],{"class":119,"line":536},24,[117,538,539],{"class":225},"                    template",[117,541,24],{"class":123},[117,543,544],{"class":212},"TextColor",[117,546,254],{"class":123},[117,548,549],{"class":225},"pdf",[117,551,24],{"class":123},[117,553,554],{"class":212},"Gray",[117,556,254],{"class":123},[117,558,559],{"class":298},"0.5",[117,561,562],{"class":123},")))\n",[117,564,566],{"class":119,"line":565},25,[117,567,468],{"class":123},[117,569,571],{"class":119,"line":570},26,[117,572,573],{"class":123},"        })\n",[117,575,577,579,581,583,585,587,589,591,593,595,597],{"class":119,"line":576},27,[117,578,352],{"class":225},[117,580,24],{"class":123},[117,582,357],{"class":212},[117,584,327],{"class":123},[117,586,362],{"class":330},[117,588,333],{"class":123},[117,590,336],{"class":127},[117,592,24],{"class":123},[117,594,371],{"class":127},[117,596,344],{"class":123},[117,598,219],{"class":123},[117,600,602,604,606,608,610,612,614,616,618,620,622,624,626,628],{"class":119,"line":601},28,[117,603,381],{"class":225},[117,605,24],{"class":123},[117,607,386],{"class":212},[117,609,254],{"class":123},[117,611,19],{"class":298},[117,613,394],{"class":123},[117,615,397],{"class":123},[117,617,400],{"class":330},[117,619,333],{"class":123},[117,621,336],{"class":127},[117,623,24],{"class":123},[117,625,409],{"class":127},[117,627,344],{"class":123},[117,629,219],{"class":123},[117,631,633,635,637,640,642,644,646,649,651,653,655,658,660,663],{"class":119,"line":632},29,[117,634,419],{"class":225},[117,636,24],{"class":123},[117,638,639],{"class":212},"Line",[117,641,254],{"class":123},[117,643,336],{"class":225},[117,645,24],{"class":123},[117,647,648],{"class":212},"LineColor",[117,650,254],{"class":123},[117,652,549],{"class":225},[117,654,24],{"class":123},[117,656,657],{"class":212},"RGBHex",[117,659,254],{"class":123},[117,661,662],{"class":298},"0x1565C0",[117,664,562],{"class":123},[117,666,668,670,672,675,677,679,681,683,685,688],{"class":119,"line":667},30,[117,669,419],{"class":225},[117,671,24],{"class":123},[117,673,674],{"class":212},"Spacer",[117,676,254],{"class":123},[117,678,257],{"class":225},[117,680,24],{"class":123},[117,682,293],{"class":212},[117,684,254],{"class":123},[117,686,687],{"class":298},"3",[117,689,462],{"class":123},[117,691,693],{"class":119,"line":692},31,[117,694,468],{"class":123},[117,696,698],{"class":119,"line":697},32,[117,699,573],{"class":123},[117,701,703],{"class":119,"line":702},33,[117,704,705],{"class":123},"    })\n",[117,707,709],{"class":119,"line":708},34,[117,710,135],{"emptyLinePlaceholder":134},[117,712,714,716,718,721,723,725,727,729,731,733,735],{"class":119,"line":713},35,[117,715,319],{"class":225},[117,717,24],{"class":123},[117,719,720],{"class":212},"Footer",[117,722,327],{"class":123},[117,724,13],{"class":330},[117,726,333],{"class":123},[117,728,336],{"class":127},[117,730,24],{"class":123},[117,732,341],{"class":127},[117,734,344],{"class":123},[117,736,219],{"class":123},[117,738,740,742,744,746,748,750,752,754,756,758,760],{"class":119,"line":739},36,[117,741,352],{"class":225},[117,743,24],{"class":123},[117,745,357],{"class":212},[117,747,327],{"class":123},[117,749,362],{"class":330},[117,751,333],{"class":123},[117,753,336],{"class":127},[117,755,24],{"class":123},[117,757,371],{"class":127},[117,759,344],{"class":123},[117,761,219],{"class":123},[117,763,765,767,769,771,773,775,777,779,781,783,785,787,789,791],{"class":119,"line":764},37,[117,766,381],{"class":225},[117,768,24],{"class":123},[117,770,386],{"class":212},[117,772,254],{"class":123},[117,774,19],{"class":298},[117,776,394],{"class":123},[117,778,397],{"class":123},[117,780,400],{"class":330},[117,782,333],{"class":123},[117,784,336],{"class":127},[117,786,24],{"class":123},[117,788,409],{"class":127},[117,790,344],{"class":123},[117,792,219],{"class":123},[117,794,796,798,800,802,804,806,808,810,812,814],{"class":119,"line":795},38,[117,797,419],{"class":225},[117,799,24],{"class":123},[117,801,674],{"class":212},[117,803,254],{"class":123},[117,805,257],{"class":225},[117,807,24],{"class":123},[117,809,293],{"class":212},[117,811,254],{"class":123},[117,813,687],{"class":298},[117,815,462],{"class":123},[117,817,819,821,823,825,827,829,831,833,835,837,839,841,843,846],{"class":119,"line":818},39,[117,820,419],{"class":225},[117,822,24],{"class":123},[117,824,639],{"class":212},[117,826,254],{"class":123},[117,828,336],{"class":225},[117,830,24],{"class":123},[117,832,648],{"class":212},[117,834,254],{"class":123},[117,836,549],{"class":225},[117,838,24],{"class":123},[117,840,554],{"class":212},[117,842,254],{"class":123},[117,844,845],{"class":298},"0.7",[117,847,562],{"class":123},[117,849,851,853,855,857,859,861,863,865,867,870],{"class":119,"line":850},40,[117,852,419],{"class":225},[117,854,24],{"class":123},[117,856,674],{"class":212},[117,858,254],{"class":123},[117,860,257],{"class":225},[117,862,24],{"class":123},[117,864,293],{"class":212},[117,866,254],{"class":123},[117,868,869],{"class":298},"2",[117,871,462],{"class":123},[117,873,875],{"class":119,"line":874},41,[117,876,468],{"class":123},[117,878,880],{"class":119,"line":879},42,[117,881,573],{"class":123},[117,883,885,887,889,891,893,895,897,899,901,903,905],{"class":119,"line":884},43,[117,886,352],{"class":225},[117,888,24],{"class":123},[117,890,357],{"class":212},[117,892,327],{"class":123},[117,894,362],{"class":330},[117,896,333],{"class":123},[117,898,336],{"class":127},[117,900,24],{"class":123},[117,902,371],{"class":127},[117,904,344],{"class":123},[117,906,219],{"class":123},[117,908,910,912,914,916,918,920,922,924,926,928,930,932,934,936],{"class":119,"line":909},44,[117,911,381],{"class":225},[117,913,24],{"class":123},[117,915,386],{"class":212},[117,917,254],{"class":123},[117,919,391],{"class":298},[117,921,394],{"class":123},[117,923,397],{"class":123},[117,925,400],{"class":330},[117,927,333],{"class":123},[117,929,336],{"class":127},[117,931,24],{"class":123},[117,933,409],{"class":127},[117,935,344],{"class":123},[117,937,219],{"class":123},[117,939,941,943,945,947,949,951,954,956,958,960,962,964,966,969],{"class":119,"line":940},45,[117,942,419],{"class":225},[117,944,24],{"class":123},[117,946,424],{"class":212},[117,948,254],{"class":123},[117,950,429],{"class":123},[117,952,953],{"class":432},"Generated by gpdf",[117,955,429],{"class":123},[117,957,394],{"class":123},[117,959,232],{"class":225},[117,961,24],{"class":123},[117,963,454],{"class":212},[117,965,254],{"class":123},[117,967,968],{"class":298},"8",[117,970,265],{"class":123},[117,972,974,976,978,980,982,984,986,988,990,992],{"class":119,"line":973},46,[117,975,539],{"class":225},[117,977,24],{"class":123},[117,979,544],{"class":212},[117,981,254],{"class":123},[117,983,549],{"class":225},[117,985,24],{"class":123},[117,987,554],{"class":212},[117,989,254],{"class":123},[117,991,559],{"class":298},[117,993,562],{"class":123},[117,995,997],{"class":119,"line":996},47,[117,998,468],{"class":123},[117,1000,1002,1004,1006,1008,1010,1012,1014,1016,1018,1020,1022,1024,1026,1028],{"class":119,"line":1001},48,[117,1003,381],{"class":225},[117,1005,24],{"class":123},[117,1007,386],{"class":212},[117,1009,254],{"class":123},[117,1011,391],{"class":298},[117,1013,394],{"class":123},[117,1015,397],{"class":123},[117,1017,400],{"class":330},[117,1019,333],{"class":123},[117,1021,336],{"class":127},[117,1023,24],{"class":123},[117,1025,409],{"class":127},[117,1027,344],{"class":123},[117,1029,219],{"class":123},[117,1031,1033,1035,1037,1040,1042,1044,1046,1048,1050,1052,1054,1056,1058,1060],{"class":119,"line":1032},49,[117,1034,419],{"class":225},[117,1036,24],{"class":123},[117,1038,1039],{"class":212},"PageNumber",[117,1041,254],{"class":123},[117,1043,336],{"class":225},[117,1045,24],{"class":123},[117,1047,518],{"class":212},[117,1049,447],{"class":123},[117,1051,232],{"class":225},[117,1053,24],{"class":123},[117,1055,454],{"class":212},[117,1057,254],{"class":123},[117,1059,968],{"class":298},[117,1061,265],{"class":123},[117,1063,1065,1067,1069,1071,1073,1075,1077,1079,1081,1083],{"class":119,"line":1064},50,[117,1066,539],{"class":225},[117,1068,24],{"class":123},[117,1070,544],{"class":212},[117,1072,254],{"class":123},[117,1074,549],{"class":225},[117,1076,24],{"class":123},[117,1078,554],{"class":212},[117,1080,254],{"class":123},[117,1082,559],{"class":298},[117,1084,562],{"class":123},[117,1086,1088],{"class":119,"line":1087},51,[117,1089,468],{"class":123},[117,1091,1093],{"class":119,"line":1092},52,[117,1094,573],{"class":123},[117,1096,1098],{"class":119,"line":1097},53,[117,1099,705],{"class":123},[117,1101,1103],{"class":119,"line":1102},54,[117,1104,135],{"emptyLinePlaceholder":134},[117,1106,1108,1111,1114,1116,1119,1121,1124,1127,1131,1134,1136,1139,1141,1143,1146,1149,1151,1153,1155,1158,1160,1162,1164,1167,1169,1172],{"class":119,"line":1107},55,[117,1109,1110],{"class":141},"    for",[117,1112,1113],{"class":225}," _",[117,1115,394],{"class":123},[117,1117,1118],{"class":225}," title ",[117,1120,229],{"class":123},[117,1122,1123],{"class":141}," range",[117,1125,1126],{"class":123}," []",[117,1128,1130],{"class":1129},"spNyl","string",[117,1132,1133],{"class":123},"{",[117,1135,429],{"class":123},[117,1137,1138],{"class":432},"Introducción",[117,1140,429],{"class":123},[117,1142,394],{"class":123},[117,1144,1145],{"class":123}," \"",[117,1147,1148],{"class":432},"Contexto",[117,1150,429],{"class":123},[117,1152,394],{"class":123},[117,1154,1145],{"class":123},[117,1156,1157],{"class":432},"Análisis",[117,1159,429],{"class":123},[117,1161,394],{"class":123},[117,1163,1145],{"class":123},[117,1165,1166],{"class":432},"Conclusión",[117,1168,429],{"class":123},[117,1170,1171],{"class":123},"}",[117,1173,219],{"class":123},[117,1175,1177,1180,1182,1185,1187,1190],{"class":119,"line":1176},56,[117,1178,1179],{"class":225},"        page ",[117,1181,229],{"class":123},[117,1183,1184],{"class":225}," doc",[117,1186,24],{"class":123},[117,1188,1189],{"class":212},"AddPage",[117,1191,1192],{"class":123},"()\n",[117,1194,1196,1199,1201,1203,1205,1207,1209,1211,1213,1215,1217],{"class":119,"line":1195},57,[117,1197,1198],{"class":225},"        page",[117,1200,24],{"class":123},[117,1202,357],{"class":212},[117,1204,327],{"class":123},[117,1206,362],{"class":330},[117,1208,333],{"class":123},[117,1210,336],{"class":127},[117,1212,24],{"class":123},[117,1214,371],{"class":127},[117,1216,344],{"class":123},[117,1218,219],{"class":123},[117,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248],{"class":119,"line":1221},58,[117,1223,381],{"class":225},[117,1225,24],{"class":123},[117,1227,386],{"class":212},[117,1229,254],{"class":123},[117,1231,19],{"class":298},[117,1233,394],{"class":123},[117,1235,397],{"class":123},[117,1237,400],{"class":330},[117,1239,333],{"class":123},[117,1241,336],{"class":127},[117,1243,24],{"class":123},[117,1245,409],{"class":127},[117,1247,344],{"class":123},[117,1249,219],{"class":123},[117,1251,1253,1255,1257,1259,1261,1264,1266,1268,1270,1272,1274,1277,1280,1282,1284,1286],{"class":119,"line":1252},59,[117,1254,419],{"class":225},[117,1256,24],{"class":123},[117,1258,424],{"class":212},[117,1260,254],{"class":123},[117,1262,1263],{"class":225},"title",[117,1265,394],{"class":123},[117,1267,232],{"class":225},[117,1269,24],{"class":123},[117,1271,454],{"class":212},[117,1273,254],{"class":123},[117,1275,1276],{"class":298},"18",[117,1278,1279],{"class":123},"),",[117,1281,232],{"class":225},[117,1283,24],{"class":123},[117,1285,444],{"class":212},[117,1287,1288],{"class":123},"())\n",[117,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1311],{"class":119,"line":1291},60,[117,1293,419],{"class":225},[117,1295,24],{"class":123},[117,1297,674],{"class":212},[117,1299,254],{"class":123},[117,1301,257],{"class":225},[117,1303,24],{"class":123},[117,1305,293],{"class":212},[117,1307,254],{"class":123},[117,1309,1310],{"class":298},"5",[117,1312,462],{"class":123},[117,1314,1316,1318,1320,1322,1324,1326,1329,1331,1334,1336,1339,1341,1343,1345],{"class":119,"line":1315},61,[117,1317,419],{"class":225},[117,1319,24],{"class":123},[117,1321,424],{"class":212},[117,1323,254],{"class":123},[117,1325,429],{"class":123},[117,1327,1328],{"class":432},"Cuerpo de la sección ",[117,1330,429],{"class":123},[117,1332,1333],{"class":123}," +",[117,1335,1118],{"class":225},[117,1337,1338],{"class":123},"+",[117,1340,1145],{"class":123},[117,1342,24],{"class":432},[117,1344,429],{"class":123},[117,1346,198],{"class":123},[117,1348,1350],{"class":119,"line":1349},62,[117,1351,468],{"class":123},[117,1353,1355],{"class":119,"line":1354},63,[117,1356,573],{"class":123},[117,1358,1360],{"class":119,"line":1359},64,[117,1361,1362],{"class":123},"    }\n",[117,1364,1366],{"class":119,"line":1365},65,[117,1367,135],{"emptyLinePlaceholder":134},[117,1369,1371,1374,1376,1379,1381,1383,1385,1388],{"class":119,"line":1370},66,[117,1372,1373],{"class":225},"    out",[117,1375,394],{"class":123},[117,1377,1378],{"class":225}," err ",[117,1380,229],{"class":123},[117,1382,1184],{"class":225},[117,1384,24],{"class":123},[117,1386,1387],{"class":212},"Generate",[117,1389,1192],{"class":123},[117,1391,1393,1396,1398,1401,1404],{"class":119,"line":1392},67,[117,1394,1395],{"class":141},"    if",[117,1397,1378],{"class":225},[117,1399,1400],{"class":123},"!=",[117,1402,1403],{"class":123}," nil",[117,1405,219],{"class":123},[117,1407,1409,1412,1414,1417],{"class":119,"line":1408},68,[117,1410,1411],{"class":212},"        panic",[117,1413,254],{"class":123},[117,1415,1416],{"class":225},"err",[117,1418,198],{"class":123},[117,1420,1422],{"class":119,"line":1421},69,[117,1423,1362],{"class":123},[117,1425,1427,1430,1433,1436,1438,1441,1443,1445,1448,1450,1452,1455,1457,1460],{"class":119,"line":1426},70,[117,1428,1429],{"class":225},"    _ ",[117,1431,1432],{"class":123},"=",[117,1434,1435],{"class":225}," os",[117,1437,24],{"class":123},[117,1439,1440],{"class":212},"WriteFile",[117,1442,254],{"class":123},[117,1444,429],{"class":123},[117,1446,1447],{"class":432},"report.pdf",[117,1449,429],{"class":123},[117,1451,394],{"class":123},[117,1453,1454],{"class":225}," out",[117,1456,394],{"class":123},[117,1458,1459],{"class":298}," 0o644",[117,1461,198],{"class":123},[117,1463,1465],{"class":119,"line":1464},71,[117,1466,1467],{"class":123},"}\n",[13,1469,1470,1471,1474,1475,1478,1479,1481],{},"Se generan 4 páginas, con ",[17,1472,1473],{},"4"," en la esquina superior derecha de la cabecera y ",[17,1476,1477],{},"1","〜",[17,1480,1473],{}," en la esquina inferior derecha del pie. En ningún momento le dijiste a gpdf cuántas páginas tendría el documento — gpdf tampoco lo sabe hasta que termina de paginar.",[40,1483,1485],{"id":1484},"por-qué-page-x-of-y-es-la-parte-difícil","Por qué \"Page X of Y\" es la parte difícil",[13,1487,1488,1489,1492,1493,1496],{},"El ",[17,1490,1491],{},"Y"," es complicado porque el motor de layout no lo conoce mientras dibuja la página 1. En un informe de 50 páginas, la 47 podría partirse en dos por una fila de tabla que no cabe. El total ",[17,1494,1495],{},"50"," sólo está disponible cuando el paginador termina. Pero el pie de la página 1 ya se dibujó mucho antes.",[13,1498,1499],{},"Cada librería PDF choca contra este muro. Cómo lo resuelven las más usadas en Go:",[1501,1502,1503,1516],"table",{},[1504,1505,1506],"thead",{},[1507,1508,1509,1513],"tr",{},[1510,1511,1512],"th",{},"Librería",[1510,1514,1515],{},"Cómo hace \"Page X of Y\"",[1517,1518,1519,1535,1543,1551,1565],"tbody",{},[1507,1520,1521,1525],{},[1522,1523,1524],"td",{},"gofpdf",[1522,1526,1527,1530,1531,1534],{},[17,1528,1529],{},"pdf.AliasNbPages(\"{nb}\")",". Escribes ",[17,1532,1533],{},"{nb}"," como literal en el texto, llamas al método y se reescribe el stream PDF a posteriori. Funciona, pero hay que acordarse de llamarlo, y el placeholder es un string mágico.",[1507,1536,1537,1540],{},[1522,1538,1539],{},"go-pdf/fpdf",[1522,1541,1542],{},"Fork de gofpdf. Mismo mecanismo.",[1507,1544,1545,1548],{},[1522,1546,1547],{},"signintech/gopdf",[1522,1549,1550],{},"Sin soporte de primera clase. Construyes el documento, cuentas páginas, reconstruyes.",[1507,1552,1553,1556],{},[1522,1554,1555],{},"maroto v2",[1522,1557,1558,1559,1561,1562,1564],{},"Registro ",[17,1560,324],{},"/",[17,1563,720],{}," parecido al de gpdf. Internamente también dos pasadas. Más lento porque por debajo es gofpdf — unas 10× más lento que gpdf en cargas comunes.",[1507,1566,1567,1570],{},[1522,1568,1569],{},"gpdf",[1522,1571,1572,1574,1575,1577],{},[17,1573,65],{}," / ",[17,1576,69],{},". Llamadas a métodos tipadas, sin strings mágicos, resuelto con la segunda pasada interna.",[13,1579,1580,1581,1584,1585,1587,1588,1590,1591,1593],{},"gpdf es la única en la que la primitiva de numeración es parte de la API tipada del builder. En gofpdf, si escribes ",[17,1582,1583],{},"{nB}"," en lugar de ",[17,1586,1533],{},", aparece literalmente ",[17,1589,1583],{}," en el pie. Con ",[17,1592,69],{}," lo peor que puede pasar es que se te olvide llamarlo — y entonces no aparece nada, no un número equivocado.",[1595,1596,1598],"h3",{"id":1597},"cómo-funciona-la-segunda-pasada","Cómo funciona la segunda pasada",[13,1600,1601,1602,1604],{},"Internamente ",[17,1603,65],{}," se renderiza como un placeholder — un valor centinela que ninguna glifo de fuente real va a coincidir. Cuando el paginador termina de maquetar todas las páginas y conoce el total, recorre las instrucciones de texto renderizadas y sustituye:",[1606,1607,1608,1620],"ol",{},[48,1609,1610,1614,1615,53,1617,1619],{},[1611,1612,1613],"strong",{},"Pasada 1 (paginar)",": renderiza cada página, incluida cabecera y pie, tratando ",[17,1616,1039],{},[17,1618,509],{}," como tokens de ancho fijo. Calcula el total.",[48,1621,1622,1625],{},[1611,1623,1624],{},"Pasada 2 (resolver)",": recorre el árbol de páginas, encuentra cada centinela y lo sustituye por el número real (actual o total).",[13,1627,1628],{},"El ancho del placeholder se reserva en función del máximo esperado de páginas (heurístico), así que tras la sustitución el layout no se desplaza. Los números alineados a la derecha siguen alineados cuando el conteo pasa de 9 a 10 dígitos.",[13,1630,1631,1632,1635],{},"Tú no escribes la segunda pasada. No renderizas el documento dos veces. Llamas a ",[17,1633,1634],{},"doc.Generate()"," y recibes los bytes.",[40,1637,1639],{"id":1638},"cabecera-y-pie-son-layout-normal","Cabecera y pie son layout normal",[13,1641,1642,1643,1646,1647,1650,1651,1654],{},"A quien viene de gofpdf esto le descoloca. Allí ",[17,1644,1645],{},"SetHeaderFunc"," se llama en una Y fija y colocas texto con ",[17,1648,1649],{},"Cell(...)"," en coordenadas absolutas. En gpdf, la closure de la cabecera recibe un ",[17,1652,1653],{},"*template.PageBuilder"," — el mismo tipo que usa el cuerpo. El grid es el mismo, las filas y columnas son las mismas, los estilos también.",[108,1656,1658],{"className":110,"code":1657,"language":112,"meta":113,"style":113},"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,1659,1660,1685,1710,1741,1783,1787,1817,1857,1861,1891,1909,1913,1917],{"__ignoreMap":113},[117,1661,1662,1665,1667,1669,1671,1673,1675,1677,1679,1681,1683],{"class":119,"line":120},[117,1663,1664],{"class":225},"doc",[117,1666,24],{"class":123},[117,1668,324],{"class":212},[117,1670,327],{"class":123},[117,1672,13],{"class":330},[117,1674,333],{"class":123},[117,1676,336],{"class":127},[117,1678,24],{"class":123},[117,1680,341],{"class":127},[117,1682,344],{"class":123},[117,1684,219],{"class":123},[117,1686,1687,1690,1692,1694,1696,1698,1700,1702,1704,1706,1708],{"class":119,"line":131},[117,1688,1689],{"class":225},"    p",[117,1691,24],{"class":123},[117,1693,357],{"class":212},[117,1695,327],{"class":123},[117,1697,362],{"class":330},[117,1699,333],{"class":123},[117,1701,336],{"class":127},[117,1703,24],{"class":123},[117,1705,371],{"class":127},[117,1707,344],{"class":123},[117,1709,219],{"class":123},[117,1711,1712,1715,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735,1737,1739],{"class":119,"line":138},[117,1713,1714],{"class":225},"        r",[117,1716,24],{"class":123},[117,1718,386],{"class":212},[117,1720,254],{"class":123},[117,1722,869],{"class":298},[117,1724,394],{"class":123},[117,1726,397],{"class":123},[117,1728,400],{"class":330},[117,1730,333],{"class":123},[117,1732,336],{"class":127},[117,1734,24],{"class":123},[117,1736,409],{"class":127},[117,1738,344],{"class":123},[117,1740,219],{"class":123},[117,1742,1743,1746,1748,1751,1753,1755,1758,1760,1762,1764,1766,1769,1771,1773,1775,1777,1779,1781],{"class":119,"line":148},[117,1744,1745],{"class":225},"            c",[117,1747,24],{"class":123},[117,1749,1750],{"class":212},"Image",[117,1752,254],{"class":123},[117,1754,429],{"class":123},[117,1756,1757],{"class":432},"logo.png",[117,1759,429],{"class":123},[117,1761,394],{"class":123},[117,1763,232],{"class":225},[117,1765,24],{"class":123},[117,1767,1768],{"class":212},"ImageHeight",[117,1770,254],{"class":123},[117,1772,257],{"class":225},[117,1774,24],{"class":123},[117,1776,293],{"class":212},[117,1778,254],{"class":123},[117,1780,19],{"class":298},[117,1782,562],{"class":123},[117,1784,1785],{"class":119,"line":160},[117,1786,573],{"class":123},[117,1788,1789,1791,1793,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815],{"class":119,"line":165},[117,1790,1714],{"class":225},[117,1792,24],{"class":123},[117,1794,386],{"class":212},[117,1796,254],{"class":123},[117,1798,968],{"class":298},[117,1800,394],{"class":123},[117,1802,397],{"class":123},[117,1804,400],{"class":330},[117,1806,333],{"class":123},[117,1808,336],{"class":127},[117,1810,24],{"class":123},[117,1812,409],{"class":127},[117,1814,344],{"class":123},[117,1816,219],{"class":123},[117,1818,1819,1821,1823,1825,1827,1829,1832,1834,1836,1838,1840,1842,1844,1846,1848,1850,1852,1855],{"class":119,"line":175},[117,1820,1745],{"class":225},[117,1822,24],{"class":123},[117,1824,424],{"class":212},[117,1826,254],{"class":123},[117,1828,429],{"class":123},[117,1830,1831],{"class":432},"Annual Report 2026",[117,1833,429],{"class":123},[117,1835,394],{"class":123},[117,1837,232],{"class":225},[117,1839,24],{"class":123},[117,1841,444],{"class":212},[117,1843,447],{"class":123},[117,1845,232],{"class":225},[117,1847,24],{"class":123},[117,1849,454],{"class":212},[117,1851,254],{"class":123},[117,1853,1854],{"class":298},"14",[117,1856,462],{"class":123},[117,1858,1859],{"class":119,"line":185},[117,1860,573],{"class":123},[117,1862,1863,1865,1867,1869,1871,1873,1875,1877,1879,1881,1883,1885,1887,1889],{"class":119,"line":195},[117,1864,1714],{"class":225},[117,1866,24],{"class":123},[117,1868,386],{"class":212},[117,1870,254],{"class":123},[117,1872,869],{"class":298},[117,1874,394],{"class":123},[117,1876,397],{"class":123},[117,1878,400],{"class":330},[117,1880,333],{"class":123},[117,1882,336],{"class":127},[117,1884,24],{"class":123},[117,1886,409],{"class":127},[117,1888,344],{"class":123},[117,1890,219],{"class":123},[117,1892,1893,1895,1897,1899,1901,1903,1905,1907],{"class":119,"line":201},[117,1894,1745],{"class":225},[117,1896,24],{"class":123},[117,1898,509],{"class":212},[117,1900,254],{"class":123},[117,1902,336],{"class":225},[117,1904,24],{"class":123},[117,1906,518],{"class":212},[117,1908,1288],{"class":123},[117,1910,1911],{"class":119,"line":206},[117,1912,573],{"class":123},[117,1914,1915],{"class":119,"line":222},[117,1916,705],{"class":123},[117,1918,1919],{"class":119,"line":243},[117,1920,1921],{"class":123},"})\n",[13,1923,1924],{},"Logo a la izquierda, título en el centro, total a la derecha. Las columnas suman 12, igual que en una fila de cuerpo.",[13,1926,1927,1928,1931],{},"La altura de la cabecera se mide automáticamente. gpdf ejecuta una vez la closure antes del cuerpo, mide la altura renderizada y la resta de la altura útil del cuerpo en cada página. El pie igual. No le pasas ",[17,1929,1930],{},"headerHeight",". Si añades una fila a la cabecera, el cuerpo se encoge.",[13,1933,1934],{},"Ambos se repiten en todas las páginas, también en las que se generan por desbordamiento de contenido. Si una tabla larga se derrama hasta la página 12, la página 12 también tiene cabecera y pie. No hay flag de \"solo primera página\" (ver más abajo).",[40,1936,1938],{"id":1937},"el-detalle-áspero-page-x-of-y-en-una-línea","El detalle áspero: \"Page X of Y\" en una línea",[13,1940,1941,1942,1945,1946,1949,1950,53,1953,1955],{},"Aquí, sinceramente, la API podría mejorar. No existe ",[17,1943,1944],{},"c.PageOf(\"Page %d of %d\")",". Para producir el string literal ",[17,1947,1948],{},"\"Page 3 of 12\""," hay que componerlo en columnas, porque ",[17,1951,1952],{},"c.Text()",[17,1954,65],{}," son hijos independientes de una columna:",[108,1957,1959],{"className":110,"code":1958,"language":112,"meta":113,"style":113},"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,1960,1961,1991,2016,2046,2073,2077,2107,2126,2130,2160,2187,2191,2221,2240,2244,2275,2279],{"__ignoreMap":113},[117,1962,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989],{"class":119,"line":120},[117,1964,362],{"class":225},[117,1966,24],{"class":123},[117,1968,386],{"class":212},[117,1970,254],{"class":123},[117,1972,19],{"class":298},[117,1974,394],{"class":123},[117,1976,397],{"class":123},[117,1978,400],{"class":330},[117,1980,333],{"class":123},[117,1982,336],{"class":127},[117,1984,24],{"class":123},[117,1986,409],{"class":127},[117,1988,344],{"class":123},[117,1990,219],{"class":123},[117,1992,1993,1996,1998,2000,2002,2004,2006,2008,2010,2012,2014],{"class":119,"line":131},[117,1994,1995],{"class":225},"    c",[117,1997,24],{"class":123},[117,1999,357],{"class":212},[117,2001,327],{"class":123},[117,2003,362],{"class":330},[117,2005,333],{"class":123},[117,2007,336],{"class":127},[117,2009,24],{"class":123},[117,2011,371],{"class":127},[117,2013,344],{"class":123},[117,2015,219],{"class":123},[117,2017,2018,2020,2022,2024,2026,2028,2030,2032,2034,2036,2038,2040,2042,2044],{"class":119,"line":138},[117,2019,1714],{"class":225},[117,2021,24],{"class":123},[117,2023,386],{"class":212},[117,2025,254],{"class":123},[117,2027,687],{"class":298},[117,2029,394],{"class":123},[117,2031,397],{"class":123},[117,2033,400],{"class":330},[117,2035,333],{"class":123},[117,2037,336],{"class":127},[117,2039,24],{"class":123},[117,2041,409],{"class":127},[117,2043,344],{"class":123},[117,2045,219],{"class":123},[117,2047,2048,2050,2052,2054,2056,2058,2061,2063,2065,2067,2069,2071],{"class":119,"line":148},[117,2049,1745],{"class":225},[117,2051,24],{"class":123},[117,2053,424],{"class":212},[117,2055,254],{"class":123},[117,2057,429],{"class":123},[117,2059,2060],{"class":432},"Page",[117,2062,429],{"class":123},[117,2064,394],{"class":123},[117,2066,232],{"class":225},[117,2068,24],{"class":123},[117,2070,518],{"class":212},[117,2072,1288],{"class":123},[117,2074,2075],{"class":119,"line":160},[117,2076,573],{"class":123},[117,2078,2079,2081,2083,2085,2087,2089,2091,2093,2095,2097,2099,2101,2103,2105],{"class":119,"line":165},[117,2080,1714],{"class":225},[117,2082,24],{"class":123},[117,2084,386],{"class":212},[117,2086,254],{"class":123},[117,2088,869],{"class":298},[117,2090,394],{"class":123},[117,2092,397],{"class":123},[117,2094,400],{"class":330},[117,2096,333],{"class":123},[117,2098,336],{"class":127},[117,2100,24],{"class":123},[117,2102,409],{"class":127},[117,2104,344],{"class":123},[117,2106,219],{"class":123},[117,2108,2109,2111,2113,2115,2117,2119,2121,2124],{"class":119,"line":175},[117,2110,1745],{"class":225},[117,2112,24],{"class":123},[117,2114,1039],{"class":212},[117,2116,254],{"class":123},[117,2118,336],{"class":225},[117,2120,24],{"class":123},[117,2122,2123],{"class":212},"AlignCenter",[117,2125,1288],{"class":123},[117,2127,2128],{"class":119,"line":185},[117,2129,573],{"class":123},[117,2131,2132,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158],{"class":119,"line":195},[117,2133,1714],{"class":225},[117,2135,24],{"class":123},[117,2137,386],{"class":212},[117,2139,254],{"class":123},[117,2141,869],{"class":298},[117,2143,394],{"class":123},[117,2145,397],{"class":123},[117,2147,400],{"class":330},[117,2149,333],{"class":123},[117,2151,336],{"class":127},[117,2153,24],{"class":123},[117,2155,409],{"class":127},[117,2157,344],{"class":123},[117,2159,219],{"class":123},[117,2161,2162,2164,2166,2168,2170,2172,2175,2177,2179,2181,2183,2185],{"class":119,"line":201},[117,2163,1745],{"class":225},[117,2165,24],{"class":123},[117,2167,424],{"class":212},[117,2169,254],{"class":123},[117,2171,429],{"class":123},[117,2173,2174],{"class":432},"of",[117,2176,429],{"class":123},[117,2178,394],{"class":123},[117,2180,232],{"class":225},[117,2182,24],{"class":123},[117,2184,2123],{"class":212},[117,2186,1288],{"class":123},[117,2188,2189],{"class":119,"line":206},[117,2190,573],{"class":123},[117,2192,2193,2195,2197,2199,2201,2203,2205,2207,2209,2211,2213,2215,2217,2219],{"class":119,"line":222},[117,2194,1714],{"class":225},[117,2196,24],{"class":123},[117,2198,386],{"class":212},[117,2200,254],{"class":123},[117,2202,687],{"class":298},[117,2204,394],{"class":123},[117,2206,397],{"class":123},[117,2208,400],{"class":330},[117,2210,333],{"class":123},[117,2212,336],{"class":127},[117,2214,24],{"class":123},[117,2216,409],{"class":127},[117,2218,344],{"class":123},[117,2220,219],{"class":123},[117,2222,2223,2225,2227,2229,2231,2233,2235,2238],{"class":119,"line":243},[117,2224,1745],{"class":225},[117,2226,24],{"class":123},[117,2228,509],{"class":212},[117,2230,254],{"class":123},[117,2232,336],{"class":225},[117,2234,24],{"class":123},[117,2236,2237],{"class":212},"AlignLeft",[117,2239,1288],{"class":123},[117,2241,2242],{"class":119,"line":268},[117,2243,573],{"class":123},[117,2245,2246,2248,2250,2252,2254,2256,2258,2260,2262,2264,2266,2268,2270,2272],{"class":119,"line":305},[117,2247,1714],{"class":225},[117,2249,24],{"class":123},[117,2251,386],{"class":212},[117,2253,254],{"class":123},[117,2255,869],{"class":298},[117,2257,394],{"class":123},[117,2259,397],{"class":123},[117,2261,400],{"class":330},[117,2263,333],{"class":123},[117,2265,336],{"class":127},[117,2267,24],{"class":123},[117,2269,409],{"class":127},[117,2271,344],{"class":123},[117,2273,2274],{"class":123}," {})\n",[117,2276,2277],{"class":119,"line":311},[117,2278,705],{"class":123},[117,2280,2281],{"class":119,"line":316},[117,2282,1921],{"class":123},[13,2284,2285,2286,2289,2290,2293,2294,2297],{},"Funciona. Visualmente queda bien. Pero es expandir a cuatro columnas algo que normalmente se escribiría como una cadena de formato. Un raspón. Estamos considerando un helper ",[17,2287,2288],{},"c.PageOf(format string, opts ...TextOption)"," al estilo ",[17,2291,2292],{},"fmt.Sprintf"," con ",[17,2295,2296],{},"%d",". Si tienes opinión sobre la forma de la API, hay un issue abierto en GitHub.",[13,2299,2300],{},"El atajo pragmático actual es quitar el prefijo y separar con barra:",[108,2302,2304],{"className":110,"code":2303,"language":112,"meta":113,"style":113},"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,2305,2306,2336,2354,2358,2388,2414,2418,2448,2466],{"__ignoreMap":113},[117,2307,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330,2332,2334],{"class":119,"line":120},[117,2309,362],{"class":225},[117,2311,24],{"class":123},[117,2313,386],{"class":212},[117,2315,254],{"class":123},[117,2317,391],{"class":298},[117,2319,394],{"class":123},[117,2321,397],{"class":123},[117,2323,400],{"class":330},[117,2325,333],{"class":123},[117,2327,336],{"class":127},[117,2329,24],{"class":123},[117,2331,409],{"class":127},[117,2333,344],{"class":123},[117,2335,219],{"class":123},[117,2337,2338,2340,2342,2344,2346,2348,2350,2352],{"class":119,"line":131},[117,2339,1995],{"class":225},[117,2341,24],{"class":123},[117,2343,1039],{"class":212},[117,2345,254],{"class":123},[117,2347,336],{"class":225},[117,2349,24],{"class":123},[117,2351,518],{"class":212},[117,2353,1288],{"class":123},[117,2355,2356],{"class":119,"line":138},[117,2357,1921],{"class":123},[117,2359,2360,2362,2364,2366,2368,2370,2372,2374,2376,2378,2380,2382,2384,2386],{"class":119,"line":148},[117,2361,362],{"class":225},[117,2363,24],{"class":123},[117,2365,386],{"class":212},[117,2367,254],{"class":123},[117,2369,1477],{"class":298},[117,2371,394],{"class":123},[117,2373,397],{"class":123},[117,2375,400],{"class":330},[117,2377,333],{"class":123},[117,2379,336],{"class":127},[117,2381,24],{"class":123},[117,2383,409],{"class":127},[117,2385,344],{"class":123},[117,2387,219],{"class":123},[117,2389,2390,2392,2394,2396,2398,2400,2402,2404,2406,2408,2410,2412],{"class":119,"line":160},[117,2391,1995],{"class":225},[117,2393,24],{"class":123},[117,2395,424],{"class":212},[117,2397,254],{"class":123},[117,2399,429],{"class":123},[117,2401,1561],{"class":432},[117,2403,429],{"class":123},[117,2405,394],{"class":123},[117,2407,232],{"class":225},[117,2409,24],{"class":123},[117,2411,2123],{"class":212},[117,2413,1288],{"class":123},[117,2415,2416],{"class":119,"line":165},[117,2417,1921],{"class":123},[117,2419,2420,2422,2424,2426,2428,2430,2432,2434,2436,2438,2440,2442,2444,2446],{"class":119,"line":175},[117,2421,362],{"class":225},[117,2423,24],{"class":123},[117,2425,386],{"class":212},[117,2427,254],{"class":123},[117,2429,1310],{"class":298},[117,2431,394],{"class":123},[117,2433,397],{"class":123},[117,2435,400],{"class":330},[117,2437,333],{"class":123},[117,2439,336],{"class":127},[117,2441,24],{"class":123},[117,2443,409],{"class":127},[117,2445,344],{"class":123},[117,2447,219],{"class":123},[117,2449,2450,2452,2454,2456,2458,2460,2462,2464],{"class":119,"line":185},[117,2451,1995],{"class":225},[117,2453,24],{"class":123},[117,2455,509],{"class":212},[117,2457,254],{"class":123},[117,2459,336],{"class":225},[117,2461,24],{"class":123},[117,2463,2237],{"class":212},[117,2465,1288],{"class":123},[117,2467,2468],{"class":119,"line":195},[117,2469,1921],{"class":123},[13,2471,2472,2475,2476,2479,2480,2483],{},[17,2473,2474],{},"3 / 12"," se lee perfectamente en un pie. Y si quieres ",[17,2477,2478],{},"Página 3 de 12",", basta con intercalar más ",[17,2481,2482],{},"c.Text"," por el medio.",[40,2485,2487],{"id":2486},"patrones-habituales","Patrones habituales",[13,2489,2490],{},"Configuraciones que la gente realmente quiere.",[13,2492,2493,2496,2497,2293,2499,2502],{},[1611,2494,2495],{},"Línea bajo el título."," Añade un segundo ",[17,2498,357],{},[17,2500,2501],{},"c.Line()",". Es lo que hace el ejemplo del principio.",[13,2504,2505,2508,2509,2511],{},[1611,2506,2507],{},"Pie centrado con aviso de confidencialidad."," Una fila, una columna, ",[17,2510,2123],{},". El caso más simple.",[108,2513,2515],{"className":110,"code":2514,"language":112,"meta":113,"style":113},"doc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Confidencial — Uso interno\",\n                template.AlignCenter(),\n                template.FontSize(8),\n                template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n",[17,2516,2517,2541,2565,2595,2615,2627,2641,2663,2667,2671],{"__ignoreMap":113},[117,2518,2519,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539],{"class":119,"line":120},[117,2520,1664],{"class":225},[117,2522,24],{"class":123},[117,2524,720],{"class":212},[117,2526,327],{"class":123},[117,2528,13],{"class":330},[117,2530,333],{"class":123},[117,2532,336],{"class":127},[117,2534,24],{"class":123},[117,2536,341],{"class":127},[117,2538,344],{"class":123},[117,2540,219],{"class":123},[117,2542,2543,2545,2547,2549,2551,2553,2555,2557,2559,2561,2563],{"class":119,"line":131},[117,2544,1689],{"class":225},[117,2546,24],{"class":123},[117,2548,357],{"class":212},[117,2550,327],{"class":123},[117,2552,362],{"class":330},[117,2554,333],{"class":123},[117,2556,336],{"class":127},[117,2558,24],{"class":123},[117,2560,371],{"class":127},[117,2562,344],{"class":123},[117,2564,219],{"class":123},[117,2566,2567,2569,2571,2573,2575,2577,2579,2581,2583,2585,2587,2589,2591,2593],{"class":119,"line":138},[117,2568,1714],{"class":225},[117,2570,24],{"class":123},[117,2572,386],{"class":212},[117,2574,254],{"class":123},[117,2576,19],{"class":298},[117,2578,394],{"class":123},[117,2580,397],{"class":123},[117,2582,400],{"class":330},[117,2584,333],{"class":123},[117,2586,336],{"class":127},[117,2588,24],{"class":123},[117,2590,409],{"class":127},[117,2592,344],{"class":123},[117,2594,219],{"class":123},[117,2596,2597,2599,2601,2603,2605,2607,2610,2612],{"class":119,"line":148},[117,2598,1745],{"class":225},[117,2600,24],{"class":123},[117,2602,424],{"class":212},[117,2604,254],{"class":123},[117,2606,429],{"class":123},[117,2608,2609],{"class":432},"Confidencial — Uso interno",[117,2611,429],{"class":123},[117,2613,2614],{"class":123},",\n",[117,2616,2617,2620,2622,2624],{"class":119,"line":160},[117,2618,2619],{"class":225},"                template",[117,2621,24],{"class":123},[117,2623,2123],{"class":212},[117,2625,2626],{"class":123},"(),\n",[117,2628,2629,2631,2633,2635,2637,2639],{"class":119,"line":165},[117,2630,2619],{"class":225},[117,2632,24],{"class":123},[117,2634,454],{"class":212},[117,2636,254],{"class":123},[117,2638,968],{"class":298},[117,2640,265],{"class":123},[117,2642,2643,2645,2647,2649,2651,2653,2655,2657,2659,2661],{"class":119,"line":175},[117,2644,2619],{"class":225},[117,2646,24],{"class":123},[117,2648,544],{"class":212},[117,2650,254],{"class":123},[117,2652,549],{"class":225},[117,2654,24],{"class":123},[117,2656,554],{"class":212},[117,2658,254],{"class":123},[117,2660,559],{"class":298},[117,2662,562],{"class":123},[117,2664,2665],{"class":119,"line":185},[117,2666,573],{"class":123},[117,2668,2669],{"class":119,"line":195},[117,2670,705],{"class":123},[117,2672,2673],{"class":119,"line":201},[117,2674,1921],{"class":123},[13,2676,2677,2678,2681],{},"En empresas españolas y latinoamericanas se suele añadir \"Fecha de impresión: 19 de mayo de 2026\" o \"Documento: DOC-2026-0517\". Apila dos o tres ",[17,2679,2680],{},"c.Text(...)",". En contextos de factura electrónica (España/LatAm) este pie aparece en casi todos los PDFs adjuntos al XML.",[13,2683,2684,2687,2688,2293,2690,2692],{},[1611,2685,2686],{},"Logo izquierda, número de página derecha."," Dos columnas 8/4 o 6/6. Imagen a la izquierda, ",[17,2689,65],{},[17,2691,518],{}," a la derecha.",[13,2694,2695,2698,2699,2701],{},[1611,2696,2697],{},"Pie con \"Continúa en la siguiente página\"."," Sin soporte actual. La closure recibe sólo ",[17,2700,341],{},", sin índice de página, así que no puedes ramificar por \"¿es la última?\". Si quieres ponerlo en el cuerpo, necesitas conocer el total antes — paradoja. Está en la lista.",[13,2703,2704,2707,2708,24],{},[1611,2705,2706],{},"Cabecera distinta en la primera página."," Mismo motivo, sin soporte. El workaround es dejar la cabecera \"vacía\" en la página 1 metiendo un spacer alto al inicio del cuerpo de la página 1, y dejar que las siguientes hereden el flujo. Feo. Estamos diseñando un ",[17,2709,2710],{},"doc.HeaderOn(pages, fn)",[40,2712,2714],{"id":2713},"cjk-funciona-directamente","CJK funciona directamente",[13,2716,2717,2718,2720,2721,2724],{},"Como gpdf hace subsetting de TrueType sin CGO, puedes meter japonés, chino o coreano en cabecera y pie como ",[17,2719,2680],{},". Sin ritual ",[17,2722,2723],{},"AddUTF8Font",", sin \"tofu\" si la fuente cubre los caracteres.",[108,2726,2728],{"className":110,"code":2727,"language":112,"meta":113,"style":113},"doc := template.New(\n    template.WithPageSize(document.A4),\n    template.WithFont(\"NotoSansJP\", notoSansJPRegular),\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(\"NotoSansJP\"), 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,2729,2730,2745,2764,2789,2793,2797,2821,2845,2875,2923,2927,2957,2987,2991,2995],{"__ignoreMap":113},[117,2731,2732,2735,2737,2739,2741,2743],{"class":119,"line":120},[117,2733,2734],{"class":225},"doc ",[117,2736,229],{"class":123},[117,2738,232],{"class":225},[117,2740,24],{"class":123},[117,2742,237],{"class":212},[117,2744,240],{"class":123},[117,2746,2747,2750,2752,2754,2756,2758,2760,2762],{"class":119,"line":131},[117,2748,2749],{"class":225},"    template",[117,2751,24],{"class":123},[117,2753,251],{"class":212},[117,2755,254],{"class":123},[117,2757,257],{"class":225},[117,2759,24],{"class":123},[117,2761,262],{"class":225},[117,2763,265],{"class":123},[117,2765,2766,2768,2770,2773,2775,2777,2780,2782,2784,2787],{"class":119,"line":138},[117,2767,2749],{"class":225},[117,2769,24],{"class":123},[117,2771,2772],{"class":212},"WithFont",[117,2774,254],{"class":123},[117,2776,429],{"class":123},[117,2778,2779],{"class":432},"NotoSansJP",[117,2781,429],{"class":123},[117,2783,394],{"class":123},[117,2785,2786],{"class":225}," notoSansJPRegular",[117,2788,265],{"class":123},[117,2790,2791],{"class":119,"line":148},[117,2792,198],{"class":123},[117,2794,2795],{"class":119,"line":160},[117,2796,135],{"emptyLinePlaceholder":134},[117,2798,2799,2801,2803,2805,2807,2809,2811,2813,2815,2817,2819],{"class":119,"line":165},[117,2800,1664],{"class":225},[117,2802,24],{"class":123},[117,2804,720],{"class":212},[117,2806,327],{"class":123},[117,2808,13],{"class":330},[117,2810,333],{"class":123},[117,2812,336],{"class":127},[117,2814,24],{"class":123},[117,2816,341],{"class":127},[117,2818,344],{"class":123},[117,2820,219],{"class":123},[117,2822,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841,2843],{"class":119,"line":175},[117,2824,1689],{"class":225},[117,2826,24],{"class":123},[117,2828,357],{"class":212},[117,2830,327],{"class":123},[117,2832,362],{"class":330},[117,2834,333],{"class":123},[117,2836,336],{"class":127},[117,2838,24],{"class":123},[117,2840,371],{"class":127},[117,2842,344],{"class":123},[117,2844,219],{"class":123},[117,2846,2847,2849,2851,2853,2855,2857,2859,2861,2863,2865,2867,2869,2871,2873],{"class":119,"line":185},[117,2848,1714],{"class":225},[117,2850,24],{"class":123},[117,2852,386],{"class":212},[117,2854,254],{"class":123},[117,2856,391],{"class":298},[117,2858,394],{"class":123},[117,2860,397],{"class":123},[117,2862,400],{"class":330},[117,2864,333],{"class":123},[117,2866,336],{"class":127},[117,2868,24],{"class":123},[117,2870,409],{"class":127},[117,2872,344],{"class":123},[117,2874,219],{"class":123},[117,2876,2877,2879,2881,2883,2885,2887,2890,2892,2894,2896,2898,2901,2903,2905,2907,2909,2911,2913,2915,2917,2919,2921],{"class":119,"line":195},[117,2878,1745],{"class":225},[117,2880,24],{"class":123},[117,2882,424],{"class":212},[117,2884,254],{"class":123},[117,2886,429],{"class":123},[117,2888,2889],{"class":432},"社外秘",[117,2891,429],{"class":123},[117,2893,394],{"class":123},[117,2895,232],{"class":225},[117,2897,24],{"class":123},[117,2899,2900],{"class":212},"FontFamily",[117,2902,254],{"class":123},[117,2904,429],{"class":123},[117,2906,2779],{"class":432},[117,2908,429],{"class":123},[117,2910,1279],{"class":123},[117,2912,232],{"class":225},[117,2914,24],{"class":123},[117,2916,454],{"class":212},[117,2918,254],{"class":123},[117,2920,968],{"class":298},[117,2922,462],{"class":123},[117,2924,2925],{"class":119,"line":201},[117,2926,573],{"class":123},[117,2928,2929,2931,2933,2935,2937,2939,2941,2943,2945,2947,2949,2951,2953,2955],{"class":119,"line":206},[117,2930,1714],{"class":225},[117,2932,24],{"class":123},[117,2934,386],{"class":212},[117,2936,254],{"class":123},[117,2938,391],{"class":298},[117,2940,394],{"class":123},[117,2942,397],{"class":123},[117,2944,400],{"class":330},[117,2946,333],{"class":123},[117,2948,336],{"class":127},[117,2950,24],{"class":123},[117,2952,409],{"class":127},[117,2954,344],{"class":123},[117,2956,219],{"class":123},[117,2958,2959,2961,2963,2965,2967,2969,2971,2973,2975,2977,2979,2981,2983,2985],{"class":119,"line":222},[117,2960,1745],{"class":225},[117,2962,24],{"class":123},[117,2964,1039],{"class":212},[117,2966,254],{"class":123},[117,2968,336],{"class":225},[117,2970,24],{"class":123},[117,2972,518],{"class":212},[117,2974,447],{"class":123},[117,2976,232],{"class":225},[117,2978,24],{"class":123},[117,2980,454],{"class":212},[117,2982,254],{"class":123},[117,2984,968],{"class":298},[117,2986,462],{"class":123},[117,2988,2989],{"class":119,"line":243},[117,2990,573],{"class":123},[117,2992,2993],{"class":119,"line":268},[117,2994,705],{"class":123},[117,2996,2997],{"class":119,"line":305},[117,2998,1921],{"class":123},[13,3000,3001,3002,3005],{},"El subset embebido en el PDF final sólo contiene \"los glifos que aparecen\". Para un informe de 60 páginas con ",[17,3003,3004],{},"\"社外秘\""," en el pie son tres glifos de NotoSansJP, no 20.000.",[40,3007,3009],{"id":3008},"rendimiento","Rendimiento",[13,3011,3012],{},"Esto importa si generas PDFs a gran escala.",[13,3014,3015],{},"La segunda pasada no es gratis, pero es barata. En un documento de 100 páginas en un M1, la segunda pasada se queda por debajo de 50µs — menos del 1% del tiempo total. El benchmark de una sola página de gpdf es 13µs; el de 100 páginas, 683µs. La resolución de números de página es un factor constante independiente de la complejidad de las páginas.",[13,3017,3018,3019,3021],{},"Para comparar, ",[17,3020,34],{}," de gofpdf hace un reemplazo de string sobre el stream completo después de decidir compresión, lo que fuerza recompresión de los streams que contienen el alias. En los propios benchmarks de gofpdf suele rondar el 2–4% del tiempo total en un documento de 100 páginas. En gpdf el reemplazo ocurre antes de codificar el stream.",[13,3023,3024],{},"Si generas un millón de PDFs al día, la diferencia se nota. Si generas diez, no.",[40,3026,3028],{"id":3027},"preguntas-frecuentes","Preguntas frecuentes",[13,3030,3031,3034,3035,3038],{},[1611,3032,3033],{},"¿La altura de la cabecera/pie cuenta dentro del margen de página?","\nSí. gpdf mide la altura renderizada de cabecera y pie, y calcula la altura útil del cuerpo como ",[17,3036,3037],{},"pageHeight - top_margin - headerHeight - footerHeight - bottom_margin",". Margen superior 20mm y cabecera 15mm: el cuerpo empieza a 35mm del borde superior.",[13,3040,3041,3044],{},[1611,3042,3043],{},"¿Puede cambiar la altura de la cabecera por página?","\nNo. La closure se evalúa una vez para la medición y el resultado queda fijo para todo el documento. Si necesitas altura variable, tienes que diseñar con una altura máxima y rellenar con espacio.",[13,3046,3047,3050],{},[1611,3048,3049],{},"¿Qué pasa en una página sin contenido?","\ngpdf no genera páginas vacías. Si tu cuerpo cabe en tres páginas, el PDF tiene tres páginas. Cabecera y pie aparecen en esas tres y en ninguna más.",[13,3052,3053,3056,3057,3060],{},[1611,3054,3055],{},"¿Puedo omitir la cabecera en páginas apaisadas de un documento mixto?","\nLas orientaciones mixtas se soportan vía ",[17,3058,3059],{},"WithPageSize(...)"," por página, pero la closure de cabecera/pie es la misma para todas las páginas independientemente de la orientación. Lo práctico es diseñar algo centrado que funcione en ambas.",[13,3062,3063,3066,3067,3070,3071,3074,3075,53,3078,3081,3082,3085],{},[1611,3064,3065],{},"¿Funciona con el input JSON?","\nSí. El schema JSON tiene ",[17,3068,3069],{},"header",", ",[17,3072,3073],{},"footer"," y los tipos ",[17,3076,3077],{},"{\"type\": \"pageNumber\"}",[17,3079,3080],{},"{\"type\": \"totalPages\"}",". El test ",[17,3083,3084],{},"gpdf/_examples/json/26_page_number_test.go"," valida que el JSON produce el mismo PDF golden que el builder.",[13,3087,3088,3095,3096,3099],{},[1611,3089,3090,3091,3094],{},"¿Y con ",[17,3092,3093],{},"text/template"," de Go?","\nSí. ",[17,3097,3098],{},"gpdf/_examples/gotemplate/26_page_number_test.go"," corre el mismo escenario. Sea cual sea la entrada — builder, JSON o template — debajo corre la misma paginación de dos pasadas.",[40,3101,3103],{"id":3102},"siguiente-paso","Siguiente paso",[13,3105,3106],{},"Cabeceras, pies y números de página son lo más aburrido de un informe, pero también lo que hace que parezca terminado. Si llevas tiempo escribiendo esto a mano sobre librerías PDF de bajo nivel, las pocas líneas de este artículo son todo lo que necesitas. Coge el ejemplo, cambia los strings, manda a producción.",[13,3108,3109,3110,3113],{},"Los puntos abiertos — ",[17,3111,3112],{},"c.PageOf(...)"," para formato de cadena única, cabecera distinta en la primera página, detección de \"última página\" — están en la lista. Si alguno te bloquea, escribe un issue en GitHub. Los casos de uso concretos definen mejor la API que las peticiones abstractas.",[40,3115,3117],{"id":3116},"probar-gpdf","Probar gpdf",[13,3119,3120],{},"gpdf es una librería de generación de PDF para Go. MIT, cero dependencias, soporte CJK.",[108,3122,3126],{"className":3123,"code":3124,"language":3125,"meta":113,"style":113},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[17,3127,3128],{"__ignoreMap":113},[117,3129,3130,3132,3135],{"class":119,"line":120},[117,3131,112],{"class":127},[117,3133,3134],{"class":432}," get",[117,3136,3137],{"class":432}," github.com/gpdf-dev/gpdf\n",[13,3139,3140,3147,3148],{},[3141,3142,3146],"a",{"href":3143,"rel":3144},"https://github.com/gpdf-dev/gpdf",[3145],"nofollow","⭐ Star en GitHub"," · ",[3141,3149,3152],{"href":3150,"rel":3151},"https://gpdf.dev/es/docs/quickstart",[3145],"Leer la documentación",[3154,3155,3156],"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":113,"searchDepth":131,"depth":131,"links":3158},[3159,3160,3161,3164,3165,3166,3167,3168,3169,3170,3171],{"id":42,"depth":131,"text":43},{"id":94,"depth":131,"text":95},{"id":1484,"depth":131,"text":1485,"children":3162},[3163],{"id":1597,"depth":138,"text":1598},{"id":1638,"depth":131,"text":1639},{"id":1937,"depth":131,"text":1938},{"id":2486,"depth":131,"text":2487},{"id":2713,"depth":131,"text":2714},{"id":3008,"depth":131,"text":3009},{"id":3027,"depth":131,"text":3028},{"id":3102,"depth":131,"text":3103},{"id":3116,"depth":131,"text":3117},"2026-05-19","Cómo añadir cabeceras, pies de página y 'Page X of Y' a PDFs en Go con gpdf: dos métodos del builder y un paginador de dos pasadas que resuelve los totales solo.",false,"md",{"name":3177,"totalTime":3178,"tools":3179,"steps":3182},"Añadir cabecera, pie y numeración Page X of Y a un PDF en Go","PT15M",[3180,3181],"Go 1.22+","github.com/gpdf-dev/gpdf",[3183,3186,3189,3192,3195],{"name":3184,"text":3185},"Crear el documento con template.New","Llama a template.New con WithPageSize(document.A4) y WithMargins. Toda la configuración global de página vive en este único punto.",{"name":3187,"text":3188},"Registrar la cabecera con doc.Header","Pasa una closure que reciba un *template.PageBuilder. Dentro usa el mismo grid de 12 columnas (AutoRow + Col) que el cuerpo. Para el total de páginas usa c.TotalPages().",{"name":3190,"text":3191},"Registrar el pie con doc.Footer","Pasa otra closure. Coloca c.PageNumber() en cualquier columna para imprimir el número de página actual. Cabecera y pie se repiten en todas las páginas, incluidas las de desbordamiento.",{"name":3193,"text":3194},"Añadir contenido con doc.AddPage","Para cada página lógica llama a doc.AddPage y rellena con filas y columnas. La altura útil del cuerpo se reduce automáticamente por la altura medida de cabecera y pie.",{"name":3196,"text":3197},"Generar el PDF con doc.Generate","Llama a doc.Generate para obtener []byte, o doc.Render(w) para escribir a un io.Writer. Justo antes de devolver los bytes, la segunda pasada sustituye los placeholders de página por números reales.",null,{},"/es/blog/page-numbers-headers-footers",{"title":5,"description":3173},"es/blog/027.page-numbers-headers-footers",[3204,3205],"tutorial","internals","Sv9z0PCZUoOQ6aXASbG_FM0xkQ2Ry44MFjmhjMtUXfM",1779199040884]