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