[{"data":1,"prerenderedAt":3202},["ShallowReactive",2],{"blog-ko-page-numbers-headers-footers":3},{"id":4,"title":5,"author":6,"body":9,"date":3167,"description":3168,"draft":3169,"extension":3170,"howTo":3171,"image":3193,"meta":3194,"navigation":135,"path":3195,"seo":3196,"stem":3197,"tags":3198,"updated":3193,"__hash__":3201},"blogKo/ko/blog/027.page-numbers-headers-footers.md","Go PDF에서 페이지 번호, 헤더, 푸터를 제대로 넣기",{"name":7,"url":8},"gpdf team","https://gpdf.dev",{"type":10,"value":11,"toc":3151},"minimark",[12,25,36,39,44,86,93,97,108,1461,1475,1483,1492,1495,1573,1589,1594,1600,1621,1624,1631,1635,1650,1917,1920,1927,1930,1934,1951,2278,2293,2296,2465,2479,2483,2486,2499,2507,2670,2677,2692,2701,2711,2715,2725,2999,3006,3009,3012,3015,3021,3024,3028,3038,3044,3050,3060,3084,3094,3098,3101,3108,3112,3115,3132,3147],[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"," 같은 토큰 뒤에 숨겨두고 빌드 후에 호출해야 하거나, 문서를 두 번 렌더링해서 첫 번째를 버린다.",[13,37,38],{},"gpdf는 builder 메서드 두 개와 내부 2단계 페이지네이션으로 깔끔하게 해결한다. 이 글에서는 API의 모양, 동작 방식, 그리고 알아둘 만한 한 가지 거친 부분을 다룬다.",[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단계 패스에서 자동으로 해결된다. 두 번 빌드하는 로직을 직접 쓸 필요 없음.",[48,76,77,78,81,82,85],{},"한 가지 거친 부분: ",[17,79,80],{},"\"3 of 12\"","처럼 한 줄짜리 인라인 문자열로 표시해주는 ",[17,83,84],{},"c.PageNumberOf(total)"," 헬퍼는 없다. 세 컬럼으로 조합해야 한다. 아래에서 설명.",[13,87,88,89,92],{},"본문의 모든 코드는 ",[17,90,91],{},"gpdf/_examples/builder/26_page_number_test.go","에서 가져온 실제 코드다. 테스트 스위트의 일부라서 빌드는 보장된다.",[40,94,96],{"id":95},"한-파일로-끝나는-전체-예제","한 파일로 끝나는 전체 예제",[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,242,267,304,310,315,348,377,415,464,470,501,535,564,569,575,600,631,666,691,696,701,707,712,738,763,794,817,849,873,878,883,908,939,972,995,1000,1031,1063,1086,1091,1096,1101,1106,1175,1194,1220,1251,1290,1314,1340,1345,1350,1356,1361,1383,1399,1412,1417,1455],{"__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,236,239],{"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,71],{"class":124},[118,237,238],{"class":213},"New",[118,240,241],{"class":124},"(\n",[118,243,245,248,250,253,256,259,261,264],{"class":120,"line":244},13,[118,246,247],{"class":226},"        template",[118,249,71],{"class":124},[118,251,252],{"class":213},"WithPageSize",[118,254,255],{"class":124},"(",[118,257,258],{"class":226},"document",[118,260,71],{"class":124},[118,262,263],{"class":226},"A4",[118,265,266],{"class":124},"),\n",[118,268,270,272,274,277,279,281,283,286,288,290,292,295,297,301],{"class":120,"line":269},14,[118,271,247],{"class":226},[118,273,71],{"class":124},[118,275,276],{"class":213},"WithMargins",[118,278,255],{"class":124},[118,280,258],{"class":226},[118,282,71],{"class":124},[118,284,285],{"class":213},"UniformEdges",[118,287,255],{"class":124},[118,289,258],{"class":226},[118,291,71],{"class":124},[118,293,294],{"class":213},"Mm",[118,296,255],{"class":124},[118,298,300],{"class":299},"sbssI","20",[118,302,303],{"class":124},"))),\n",[118,305,307],{"class":120,"line":306},15,[118,308,309],{"class":124},"    )\n",[118,311,313],{"class":120,"line":312},16,[118,314,136],{"emptyLinePlaceholder":135},[118,316,318,321,323,326,329,332,335,338,340,343,346],{"class":120,"line":317},17,[118,319,320],{"class":226},"    doc",[118,322,71],{"class":124},[118,324,325],{"class":213},"Header",[118,327,328],{"class":124},"(func(",[118,330,13],{"class":331},"sHdIc",[118,333,334],{"class":124}," *",[118,336,337],{"class":128},"template",[118,339,71],{"class":124},[118,341,342],{"class":128},"PageBuilder",[118,344,345],{"class":124},")",[118,347,220],{"class":124},[118,349,351,354,356,359,361,364,366,368,370,373,375],{"class":120,"line":350},18,[118,352,353],{"class":226},"        p",[118,355,71],{"class":124},[118,357,358],{"class":213},"AutoRow",[118,360,328],{"class":124},[118,362,363],{"class":331},"r",[118,365,334],{"class":124},[118,367,337],{"class":128},[118,369,71],{"class":124},[118,371,372],{"class":128},"RowBuilder",[118,374,345],{"class":124},[118,376,220],{"class":124},[118,378,380,383,385,388,390,393,396,399,402,404,406,408,411,413],{"class":120,"line":379},19,[118,381,382],{"class":226},"            r",[118,384,71],{"class":124},[118,386,387],{"class":213},"Col",[118,389,255],{"class":124},[118,391,392],{"class":299},"6",[118,394,395],{"class":124},",",[118,397,398],{"class":124}," func(",[118,400,401],{"class":331},"c",[118,403,334],{"class":124},[118,405,337],{"class":128},[118,407,71],{"class":124},[118,409,410],{"class":128},"ColBuilder",[118,412,345],{"class":124},[118,414,220],{"class":124},[118,416,418,421,423,426,428,431,435,437,439,441,443,446,449,451,453,456,458,461],{"class":120,"line":417},20,[118,419,420],{"class":226},"                c",[118,422,71],{"class":124},[118,424,425],{"class":213},"Text",[118,427,255],{"class":124},[118,429,430],{"class":124},"\"",[118,432,434],{"class":433},"sfazB","분기 보고서",[118,436,430],{"class":124},[118,438,395],{"class":124},[118,440,233],{"class":226},[118,442,71],{"class":124},[118,444,445],{"class":213},"Bold",[118,447,448],{"class":124},"(),",[118,450,233],{"class":226},[118,452,71],{"class":124},[118,454,455],{"class":213},"FontSize",[118,457,255],{"class":124},[118,459,460],{"class":299},"10",[118,462,463],{"class":124},"))\n",[118,465,467],{"class":120,"line":466},21,[118,468,469],{"class":124},"            })\n",[118,471,473,475,477,479,481,483,485,487,489,491,493,495,497,499],{"class":120,"line":472},22,[118,474,382],{"class":226},[118,476,71],{"class":124},[118,478,387],{"class":213},[118,480,255],{"class":124},[118,482,392],{"class":299},[118,484,395],{"class":124},[118,486,398],{"class":124},[118,488,401],{"class":331},[118,490,334],{"class":124},[118,492,337],{"class":128},[118,494,71],{"class":124},[118,496,410],{"class":128},[118,498,345],{"class":124},[118,500,220],{"class":124},[118,502,504,506,508,511,513,515,517,520,522,524,526,528,530,533],{"class":120,"line":503},23,[118,505,420],{"class":226},[118,507,71],{"class":124},[118,509,510],{"class":213},"TotalPages",[118,512,255],{"class":124},[118,514,337],{"class":226},[118,516,71],{"class":124},[118,518,519],{"class":213},"AlignRight",[118,521,448],{"class":124},[118,523,233],{"class":226},[118,525,71],{"class":124},[118,527,455],{"class":213},[118,529,255],{"class":124},[118,531,532],{"class":299},"9",[118,534,266],{"class":124},[118,536,538,541,543,546,548,551,553,556,558,561],{"class":120,"line":537},24,[118,539,540],{"class":226},"                    template",[118,542,71],{"class":124},[118,544,545],{"class":213},"TextColor",[118,547,255],{"class":124},[118,549,550],{"class":226},"pdf",[118,552,71],{"class":124},[118,554,555],{"class":213},"Gray",[118,557,255],{"class":124},[118,559,560],{"class":299},"0.5",[118,562,563],{"class":124},")))\n",[118,565,567],{"class":120,"line":566},25,[118,568,469],{"class":124},[118,570,572],{"class":120,"line":571},26,[118,573,574],{"class":124},"        })\n",[118,576,578,580,582,584,586,588,590,592,594,596,598],{"class":120,"line":577},27,[118,579,353],{"class":226},[118,581,71],{"class":124},[118,583,358],{"class":213},[118,585,328],{"class":124},[118,587,363],{"class":331},[118,589,334],{"class":124},[118,591,337],{"class":128},[118,593,71],{"class":124},[118,595,372],{"class":128},[118,597,345],{"class":124},[118,599,220],{"class":124},[118,601,603,605,607,609,611,613,615,617,619,621,623,625,627,629],{"class":120,"line":602},28,[118,604,382],{"class":226},[118,606,71],{"class":124},[118,608,387],{"class":213},[118,610,255],{"class":124},[118,612,19],{"class":299},[118,614,395],{"class":124},[118,616,398],{"class":124},[118,618,401],{"class":331},[118,620,334],{"class":124},[118,622,337],{"class":128},[118,624,71],{"class":124},[118,626,410],{"class":128},[118,628,345],{"class":124},[118,630,220],{"class":124},[118,632,634,636,638,641,643,645,647,650,652,654,656,659,661,664],{"class":120,"line":633},29,[118,635,420],{"class":226},[118,637,71],{"class":124},[118,639,640],{"class":213},"Line",[118,642,255],{"class":124},[118,644,337],{"class":226},[118,646,71],{"class":124},[118,648,649],{"class":213},"LineColor",[118,651,255],{"class":124},[118,653,550],{"class":226},[118,655,71],{"class":124},[118,657,658],{"class":213},"RGBHex",[118,660,255],{"class":124},[118,662,663],{"class":299},"0x1565C0",[118,665,563],{"class":124},[118,667,669,671,673,676,678,680,682,684,686,689],{"class":120,"line":668},30,[118,670,420],{"class":226},[118,672,71],{"class":124},[118,674,675],{"class":213},"Spacer",[118,677,255],{"class":124},[118,679,258],{"class":226},[118,681,71],{"class":124},[118,683,294],{"class":213},[118,685,255],{"class":124},[118,687,688],{"class":299},"3",[118,690,463],{"class":124},[118,692,694],{"class":120,"line":693},31,[118,695,469],{"class":124},[118,697,699],{"class":120,"line":698},32,[118,700,574],{"class":124},[118,702,704],{"class":120,"line":703},33,[118,705,706],{"class":124},"    })\n",[118,708,710],{"class":120,"line":709},34,[118,711,136],{"emptyLinePlaceholder":135},[118,713,715,717,719,722,724,726,728,730,732,734,736],{"class":120,"line":714},35,[118,716,320],{"class":226},[118,718,71],{"class":124},[118,720,721],{"class":213},"Footer",[118,723,328],{"class":124},[118,725,13],{"class":331},[118,727,334],{"class":124},[118,729,337],{"class":128},[118,731,71],{"class":124},[118,733,342],{"class":128},[118,735,345],{"class":124},[118,737,220],{"class":124},[118,739,741,743,745,747,749,751,753,755,757,759,761],{"class":120,"line":740},36,[118,742,353],{"class":226},[118,744,71],{"class":124},[118,746,358],{"class":213},[118,748,328],{"class":124},[118,750,363],{"class":331},[118,752,334],{"class":124},[118,754,337],{"class":128},[118,756,71],{"class":124},[118,758,372],{"class":128},[118,760,345],{"class":124},[118,762,220],{"class":124},[118,764,766,768,770,772,774,776,778,780,782,784,786,788,790,792],{"class":120,"line":765},37,[118,767,382],{"class":226},[118,769,71],{"class":124},[118,771,387],{"class":213},[118,773,255],{"class":124},[118,775,19],{"class":299},[118,777,395],{"class":124},[118,779,398],{"class":124},[118,781,401],{"class":331},[118,783,334],{"class":124},[118,785,337],{"class":128},[118,787,71],{"class":124},[118,789,410],{"class":128},[118,791,345],{"class":124},[118,793,220],{"class":124},[118,795,797,799,801,803,805,807,809,811,813,815],{"class":120,"line":796},38,[118,798,420],{"class":226},[118,800,71],{"class":124},[118,802,675],{"class":213},[118,804,255],{"class":124},[118,806,258],{"class":226},[118,808,71],{"class":124},[118,810,294],{"class":213},[118,812,255],{"class":124},[118,814,688],{"class":299},[118,816,463],{"class":124},[118,818,820,822,824,826,828,830,832,834,836,838,840,842,844,847],{"class":120,"line":819},39,[118,821,420],{"class":226},[118,823,71],{"class":124},[118,825,640],{"class":213},[118,827,255],{"class":124},[118,829,337],{"class":226},[118,831,71],{"class":124},[118,833,649],{"class":213},[118,835,255],{"class":124},[118,837,550],{"class":226},[118,839,71],{"class":124},[118,841,555],{"class":213},[118,843,255],{"class":124},[118,845,846],{"class":299},"0.7",[118,848,563],{"class":124},[118,850,852,854,856,858,860,862,864,866,868,871],{"class":120,"line":851},40,[118,853,420],{"class":226},[118,855,71],{"class":124},[118,857,675],{"class":213},[118,859,255],{"class":124},[118,861,258],{"class":226},[118,863,71],{"class":124},[118,865,294],{"class":213},[118,867,255],{"class":124},[118,869,870],{"class":299},"2",[118,872,463],{"class":124},[118,874,876],{"class":120,"line":875},41,[118,877,469],{"class":124},[118,879,881],{"class":120,"line":880},42,[118,882,574],{"class":124},[118,884,886,888,890,892,894,896,898,900,902,904,906],{"class":120,"line":885},43,[118,887,353],{"class":226},[118,889,71],{"class":124},[118,891,358],{"class":213},[118,893,328],{"class":124},[118,895,363],{"class":331},[118,897,334],{"class":124},[118,899,337],{"class":128},[118,901,71],{"class":124},[118,903,372],{"class":128},[118,905,345],{"class":124},[118,907,220],{"class":124},[118,909,911,913,915,917,919,921,923,925,927,929,931,933,935,937],{"class":120,"line":910},44,[118,912,382],{"class":226},[118,914,71],{"class":124},[118,916,387],{"class":213},[118,918,255],{"class":124},[118,920,392],{"class":299},[118,922,395],{"class":124},[118,924,398],{"class":124},[118,926,401],{"class":331},[118,928,334],{"class":124},[118,930,337],{"class":128},[118,932,71],{"class":124},[118,934,410],{"class":128},[118,936,345],{"class":124},[118,938,220],{"class":124},[118,940,942,944,946,948,950,952,955,957,959,961,963,965,967,970],{"class":120,"line":941},45,[118,943,420],{"class":226},[118,945,71],{"class":124},[118,947,425],{"class":213},[118,949,255],{"class":124},[118,951,430],{"class":124},[118,953,954],{"class":433},"Generated by gpdf",[118,956,430],{"class":124},[118,958,395],{"class":124},[118,960,233],{"class":226},[118,962,71],{"class":124},[118,964,455],{"class":213},[118,966,255],{"class":124},[118,968,969],{"class":299},"8",[118,971,266],{"class":124},[118,973,975,977,979,981,983,985,987,989,991,993],{"class":120,"line":974},46,[118,976,540],{"class":226},[118,978,71],{"class":124},[118,980,545],{"class":213},[118,982,255],{"class":124},[118,984,550],{"class":226},[118,986,71],{"class":124},[118,988,555],{"class":213},[118,990,255],{"class":124},[118,992,560],{"class":299},[118,994,563],{"class":124},[118,996,998],{"class":120,"line":997},47,[118,999,469],{"class":124},[118,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027,1029],{"class":120,"line":1002},48,[118,1004,382],{"class":226},[118,1006,71],{"class":124},[118,1008,387],{"class":213},[118,1010,255],{"class":124},[118,1012,392],{"class":299},[118,1014,395],{"class":124},[118,1016,398],{"class":124},[118,1018,401],{"class":331},[118,1020,334],{"class":124},[118,1022,337],{"class":128},[118,1024,71],{"class":124},[118,1026,410],{"class":128},[118,1028,345],{"class":124},[118,1030,220],{"class":124},[118,1032,1034,1036,1038,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061],{"class":120,"line":1033},49,[118,1035,420],{"class":226},[118,1037,71],{"class":124},[118,1039,1040],{"class":213},"PageNumber",[118,1042,255],{"class":124},[118,1044,337],{"class":226},[118,1046,71],{"class":124},[118,1048,519],{"class":213},[118,1050,448],{"class":124},[118,1052,233],{"class":226},[118,1054,71],{"class":124},[118,1056,455],{"class":213},[118,1058,255],{"class":124},[118,1060,969],{"class":299},[118,1062,266],{"class":124},[118,1064,1066,1068,1070,1072,1074,1076,1078,1080,1082,1084],{"class":120,"line":1065},50,[118,1067,540],{"class":226},[118,1069,71],{"class":124},[118,1071,545],{"class":213},[118,1073,255],{"class":124},[118,1075,550],{"class":226},[118,1077,71],{"class":124},[118,1079,555],{"class":213},[118,1081,255],{"class":124},[118,1083,560],{"class":299},[118,1085,563],{"class":124},[118,1087,1089],{"class":120,"line":1088},51,[118,1090,469],{"class":124},[118,1092,1094],{"class":120,"line":1093},52,[118,1095,574],{"class":124},[118,1097,1099],{"class":120,"line":1098},53,[118,1100,706],{"class":124},[118,1102,1104],{"class":120,"line":1103},54,[118,1105,136],{"emptyLinePlaceholder":135},[118,1107,1109,1112,1115,1117,1120,1122,1125,1128,1132,1135,1137,1140,1142,1144,1147,1150,1152,1154,1156,1159,1161,1163,1165,1168,1170,1173],{"class":120,"line":1108},55,[118,1110,1111],{"class":142},"    for",[118,1113,1114],{"class":226}," _",[118,1116,395],{"class":124},[118,1118,1119],{"class":226}," title ",[118,1121,230],{"class":124},[118,1123,1124],{"class":142}," range",[118,1126,1127],{"class":124}," []",[118,1129,1131],{"class":1130},"spNyl","string",[118,1133,1134],{"class":124},"{",[118,1136,430],{"class":124},[118,1138,1139],{"class":433},"서론",[118,1141,430],{"class":124},[118,1143,395],{"class":124},[118,1145,1146],{"class":124}," \"",[118,1148,1149],{"class":433},"배경",[118,1151,430],{"class":124},[118,1153,395],{"class":124},[118,1155,1146],{"class":124},[118,1157,1158],{"class":433},"분석",[118,1160,430],{"class":124},[118,1162,395],{"class":124},[118,1164,1146],{"class":124},[118,1166,1167],{"class":433},"결론",[118,1169,430],{"class":124},[118,1171,1172],{"class":124},"}",[118,1174,220],{"class":124},[118,1176,1178,1181,1183,1186,1188,1191],{"class":120,"line":1177},56,[118,1179,1180],{"class":226},"        page ",[118,1182,230],{"class":124},[118,1184,1185],{"class":226}," doc",[118,1187,71],{"class":124},[118,1189,1190],{"class":213},"AddPage",[118,1192,1193],{"class":124},"()\n",[118,1195,1197,1200,1202,1204,1206,1208,1210,1212,1214,1216,1218],{"class":120,"line":1196},57,[118,1198,1199],{"class":226},"        page",[118,1201,71],{"class":124},[118,1203,358],{"class":213},[118,1205,328],{"class":124},[118,1207,363],{"class":331},[118,1209,334],{"class":124},[118,1211,337],{"class":128},[118,1213,71],{"class":124},[118,1215,372],{"class":128},[118,1217,345],{"class":124},[118,1219,220],{"class":124},[118,1221,1223,1225,1227,1229,1231,1233,1235,1237,1239,1241,1243,1245,1247,1249],{"class":120,"line":1222},58,[118,1224,382],{"class":226},[118,1226,71],{"class":124},[118,1228,387],{"class":213},[118,1230,255],{"class":124},[118,1232,19],{"class":299},[118,1234,395],{"class":124},[118,1236,398],{"class":124},[118,1238,401],{"class":331},[118,1240,334],{"class":124},[118,1242,337],{"class":128},[118,1244,71],{"class":124},[118,1246,410],{"class":128},[118,1248,345],{"class":124},[118,1250,220],{"class":124},[118,1252,1254,1256,1258,1260,1262,1265,1267,1269,1271,1273,1275,1278,1281,1283,1285,1287],{"class":120,"line":1253},59,[118,1255,420],{"class":226},[118,1257,71],{"class":124},[118,1259,425],{"class":213},[118,1261,255],{"class":124},[118,1263,1264],{"class":226},"title",[118,1266,395],{"class":124},[118,1268,233],{"class":226},[118,1270,71],{"class":124},[118,1272,455],{"class":213},[118,1274,255],{"class":124},[118,1276,1277],{"class":299},"18",[118,1279,1280],{"class":124},"),",[118,1282,233],{"class":226},[118,1284,71],{"class":124},[118,1286,445],{"class":213},[118,1288,1289],{"class":124},"())\n",[118,1291,1293,1295,1297,1299,1301,1303,1305,1307,1309,1312],{"class":120,"line":1292},60,[118,1294,420],{"class":226},[118,1296,71],{"class":124},[118,1298,675],{"class":213},[118,1300,255],{"class":124},[118,1302,258],{"class":226},[118,1304,71],{"class":124},[118,1306,294],{"class":213},[118,1308,255],{"class":124},[118,1310,1311],{"class":299},"5",[118,1313,463],{"class":124},[118,1315,1317,1319,1321,1323,1325,1328,1331,1333,1336,1338],{"class":120,"line":1316},61,[118,1318,420],{"class":226},[118,1320,71],{"class":124},[118,1322,425],{"class":213},[118,1324,255],{"class":124},[118,1326,1327],{"class":226},"title ",[118,1329,1330],{"class":124},"+",[118,1332,1146],{"class":124},[118,1334,1335],{"class":433}," 섹션 본문.",[118,1337,430],{"class":124},[118,1339,199],{"class":124},[118,1341,1343],{"class":120,"line":1342},62,[118,1344,469],{"class":124},[118,1346,1348],{"class":120,"line":1347},63,[118,1349,574],{"class":124},[118,1351,1353],{"class":120,"line":1352},64,[118,1354,1355],{"class":124},"    }\n",[118,1357,1359],{"class":120,"line":1358},65,[118,1360,136],{"emptyLinePlaceholder":135},[118,1362,1364,1367,1369,1372,1374,1376,1378,1381],{"class":120,"line":1363},66,[118,1365,1366],{"class":226},"    out",[118,1368,395],{"class":124},[118,1370,1371],{"class":226}," err ",[118,1373,230],{"class":124},[118,1375,1185],{"class":226},[118,1377,71],{"class":124},[118,1379,1380],{"class":213},"Generate",[118,1382,1193],{"class":124},[118,1384,1386,1389,1391,1394,1397],{"class":120,"line":1385},67,[118,1387,1388],{"class":142},"    if",[118,1390,1371],{"class":226},[118,1392,1393],{"class":124},"!=",[118,1395,1396],{"class":124}," nil",[118,1398,220],{"class":124},[118,1400,1402,1405,1407,1410],{"class":120,"line":1401},68,[118,1403,1404],{"class":213},"        panic",[118,1406,255],{"class":124},[118,1408,1409],{"class":226},"err",[118,1411,199],{"class":124},[118,1413,1415],{"class":120,"line":1414},69,[118,1416,1355],{"class":124},[118,1418,1420,1423,1426,1429,1431,1434,1436,1438,1441,1443,1445,1448,1450,1453],{"class":120,"line":1419},70,[118,1421,1422],{"class":226},"    _ ",[118,1424,1425],{"class":124},"=",[118,1427,1428],{"class":226}," os",[118,1430,71],{"class":124},[118,1432,1433],{"class":213},"WriteFile",[118,1435,255],{"class":124},[118,1437,430],{"class":124},[118,1439,1440],{"class":433},"report.pdf",[118,1442,430],{"class":124},[118,1444,395],{"class":124},[118,1446,1447],{"class":226}," out",[118,1449,395],{"class":124},[118,1451,1452],{"class":299}," 0o644",[118,1454,199],{"class":124},[118,1456,1458],{"class":120,"line":1457},71,[118,1459,1460],{"class":124},"}\n",[13,1462,1463,1464,1467,1468,1471,1472,1474],{},"4페이지가 생성되고, 헤더 우측에 ",[17,1465,1466],{},"4",", 푸터 우측에 ",[17,1469,1470],{},"1","〜",[17,1473,1466],{},"가 표시된다. \"이 문서는 4페이지\"라고 코드에 한 번도 적지 않았다. gpdf 자신도 페이지네이션이 끝날 때까지는 모른다.",[40,1476,1478,1479,1482],{"id":1477},"page-x-of-y의-y가-어려운-이유","\"Page X of Y\"의 ",[17,1480,1481],{},"Y","가 어려운 이유",[13,1484,1485,1487,1488,1491],{},[17,1486,1481],{},"는 페이지 1을 그릴 시점에 아직 확정되지 않았기 때문에 까다롭다. 50페이지 보고서에서 47페이지가 테이블 행이 안 맞아서 페이지 경계에 걸쳐 두 페이지로 쪼개질 수도 있다. 총 ",[17,1489,1490],{},"50","은 페이지네이션이 끝난 뒤에만 알 수 있다. 그런데 1페이지 푸터는 훨씬 전에 그려졌다.",[13,1493,1494],{},"모든 PDF 라이브러리가 이 벽에 부딪힌다. Go 주요 라이브러리들의 우회법:",[1496,1497,1498,1511],"table",{},[1499,1500,1501],"thead",{},[1502,1503,1504,1508],"tr",{},[1505,1506,1507],"th",{},"라이브러리",[1505,1509,1510],{},"\"Page X of Y\" 처리",[1512,1513,1514,1530,1538,1546,1560],"tbody",{},[1502,1515,1516,1520],{},[1517,1518,1519],"td",{},"gofpdf",[1517,1521,1522,1525,1526,1529],{},[17,1523,1524],{},"pdf.AliasNbPages(\"{nb}\")",". 본문에 ",[17,1527,1528],{},"{nb}","를 문자열로 쓰면 출력 스트림에 사후 치환이 들어간다. 동작은 하지만 호출을 잊으면 안 되고, 플레이스홀더가 매직 스트링이다.",[1502,1531,1532,1535],{},[1517,1533,1534],{},"go-pdf/fpdf",[1517,1536,1537],{},"gofpdf의 fork. 같은 메커니즘.",[1502,1539,1540,1543],{},[1517,1541,1542],{},"signintech/gopdf",[1517,1544,1545],{},"네이티브 지원 없음. 문서를 한 번 빌드해서 페이지 수를 세고, 다시 빌드한다.",[1502,1547,1548,1551],{},[1517,1549,1550],{},"maroto v2",[1517,1552,1553,1554,1556,1557,1559],{},"gpdf와 비슷한 ",[17,1555,325],{},"/",[17,1558,721],{}," 등록 방식. 내부적으로도 비슷한 2단계 패스. 다만 베이스가 gofpdf라 일반 워크로드에서 gpdf보다 약 10배 느리다.",[1502,1561,1562,1565],{},[1517,1563,1564],{},"gpdf",[1517,1566,1567,1569,1570,1572],{},[17,1568,66],{}," / ",[17,1571,70],{},". 타입이 있는 메서드 호출, 매직 스트링 없음, 내부 2단계 패스로 해결.",[13,1574,1575,1576,1578,1579,1582,1583,1585,1586,1588],{},"페이지 번호 프리미티브를 타입화된 builder API의 일부로 가진 것은 gpdf뿐이다. gofpdf에서 ",[17,1577,1528],{},"를 ",[17,1580,1581],{},"{nB}","로 오타 내면 그대로 PDF에 ",[17,1584,1581],{},"가 인쇄된다. ",[17,1587,70],{},"에서 일어날 수 있는 최악은 호출 누락이고, 그럼 숫자가 안 나올 뿐이다. 틀린 숫자가 나오지는 않는다.",[1590,1591,1593],"h3",{"id":1592},"_2단계-패스-작동-방식","2단계 패스 작동 방식",[13,1595,1596,1597,1599],{},"내부적으로 ",[17,1598,66],{},"는 플레이스홀더 문자열로 렌더링된다. 실제 폰트의 어떤 글리프와도 매칭되지 않는 sentinel 값이다. 페이지네이터가 모든 페이지 레이아웃을 완료하고 총 수를 알고 나면, 렌더된 텍스트 명령을 순회하면서 치환한다:",[1601,1602,1603,1615],"ol",{},[48,1604,1605,1609,1610,53,1612,1614],{},[1606,1607,1608],"strong",{},"1단계 (페이지네이션)",": 헤더/푸터 포함 모든 페이지를 렌더링. ",[17,1611,1040],{},[17,1613,510],{},"는 고정폭 토큰으로 취급. 총 페이지 수를 계산.",[48,1616,1617,1620],{},[1606,1618,1619],{},"2단계 (해결)",": 페이지 트리를 다시 순회해 각 sentinel을 실제 현재/총 페이지 번호로 치환.",[13,1622,1623],{},"플레이스홀더 폭은 예상 최대 페이지 수에 맞춰 미리 잡아두기 때문에 (휴리스틱), 치환 후 레이아웃이 어긋나지 않는다. 9 → 10페이지로 자릿수가 늘어도 우측 정렬 페이지 번호가 그대로 정렬을 유지한다.",[13,1625,1626,1627,1630],{},"2단계 코드를 직접 짤 필요 없다. 문서를 두 번 렌더하지 않아도 된다. ",[17,1628,1629],{},"doc.Generate()","를 호출하면 바이트가 돌아온다.",[40,1632,1634],{"id":1633},"헤더와-푸터는-그냥-보통의-레이아웃이다","헤더와 푸터는 그냥 보통의 레이아웃이다",[13,1636,1637,1638,1641,1642,1645,1646,1649],{},"gofpdf에서 넘어온 사람이 여기서 헤맨다. 거기는 ",[17,1639,1640],{},"SetHeaderFunc","가 고정 Y 좌표에서 콜백되고 절대 좌표 ",[17,1643,1644],{},"Cell(...)","로 텍스트를 놓는다. gpdf에서 헤더 클로저는 ",[17,1647,1648],{},"*template.PageBuilder","를 받는다 — 본문과 같은 타입이다. 그리드도, 행과 열도, 스타일 옵션도 동일하다.",[109,1651,1653],{"className":111,"code":1652,"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,1654,1655,1680,1705,1736,1778,1782,1812,1852,1856,1886,1904,1908,1912],{"__ignoreMap":114},[118,1656,1657,1660,1662,1664,1666,1668,1670,1672,1674,1676,1678],{"class":120,"line":121},[118,1658,1659],{"class":226},"doc",[118,1661,71],{"class":124},[118,1663,325],{"class":213},[118,1665,328],{"class":124},[118,1667,13],{"class":331},[118,1669,334],{"class":124},[118,1671,337],{"class":128},[118,1673,71],{"class":124},[118,1675,342],{"class":128},[118,1677,345],{"class":124},[118,1679,220],{"class":124},[118,1681,1682,1685,1687,1689,1691,1693,1695,1697,1699,1701,1703],{"class":120,"line":132},[118,1683,1684],{"class":226},"    p",[118,1686,71],{"class":124},[118,1688,358],{"class":213},[118,1690,328],{"class":124},[118,1692,363],{"class":331},[118,1694,334],{"class":124},[118,1696,337],{"class":128},[118,1698,71],{"class":124},[118,1700,372],{"class":128},[118,1702,345],{"class":124},[118,1704,220],{"class":124},[118,1706,1707,1710,1712,1714,1716,1718,1720,1722,1724,1726,1728,1730,1732,1734],{"class":120,"line":139},[118,1708,1709],{"class":226},"        r",[118,1711,71],{"class":124},[118,1713,387],{"class":213},[118,1715,255],{"class":124},[118,1717,870],{"class":299},[118,1719,395],{"class":124},[118,1721,398],{"class":124},[118,1723,401],{"class":331},[118,1725,334],{"class":124},[118,1727,337],{"class":128},[118,1729,71],{"class":124},[118,1731,410],{"class":128},[118,1733,345],{"class":124},[118,1735,220],{"class":124},[118,1737,1738,1741,1743,1746,1748,1750,1753,1755,1757,1759,1761,1764,1766,1768,1770,1772,1774,1776],{"class":120,"line":149},[118,1739,1740],{"class":226},"            c",[118,1742,71],{"class":124},[118,1744,1745],{"class":213},"Image",[118,1747,255],{"class":124},[118,1749,430],{"class":124},[118,1751,1752],{"class":433},"logo.png",[118,1754,430],{"class":124},[118,1756,395],{"class":124},[118,1758,233],{"class":226},[118,1760,71],{"class":124},[118,1762,1763],{"class":213},"ImageHeight",[118,1765,255],{"class":124},[118,1767,258],{"class":226},[118,1769,71],{"class":124},[118,1771,294],{"class":213},[118,1773,255],{"class":124},[118,1775,19],{"class":299},[118,1777,563],{"class":124},[118,1779,1780],{"class":120,"line":161},[118,1781,574],{"class":124},[118,1783,1784,1786,1788,1790,1792,1794,1796,1798,1800,1802,1804,1806,1808,1810],{"class":120,"line":166},[118,1785,1709],{"class":226},[118,1787,71],{"class":124},[118,1789,387],{"class":213},[118,1791,255],{"class":124},[118,1793,969],{"class":299},[118,1795,395],{"class":124},[118,1797,398],{"class":124},[118,1799,401],{"class":331},[118,1801,334],{"class":124},[118,1803,337],{"class":128},[118,1805,71],{"class":124},[118,1807,410],{"class":128},[118,1809,345],{"class":124},[118,1811,220],{"class":124},[118,1813,1814,1816,1818,1820,1822,1824,1827,1829,1831,1833,1835,1837,1839,1841,1843,1845,1847,1850],{"class":120,"line":176},[118,1815,1740],{"class":226},[118,1817,71],{"class":124},[118,1819,425],{"class":213},[118,1821,255],{"class":124},[118,1823,430],{"class":124},[118,1825,1826],{"class":433},"Annual Report 2026",[118,1828,430],{"class":124},[118,1830,395],{"class":124},[118,1832,233],{"class":226},[118,1834,71],{"class":124},[118,1836,445],{"class":213},[118,1838,448],{"class":124},[118,1840,233],{"class":226},[118,1842,71],{"class":124},[118,1844,455],{"class":213},[118,1846,255],{"class":124},[118,1848,1849],{"class":299},"14",[118,1851,463],{"class":124},[118,1853,1854],{"class":120,"line":186},[118,1855,574],{"class":124},[118,1857,1858,1860,1862,1864,1866,1868,1870,1872,1874,1876,1878,1880,1882,1884],{"class":120,"line":196},[118,1859,1709],{"class":226},[118,1861,71],{"class":124},[118,1863,387],{"class":213},[118,1865,255],{"class":124},[118,1867,870],{"class":299},[118,1869,395],{"class":124},[118,1871,398],{"class":124},[118,1873,401],{"class":331},[118,1875,334],{"class":124},[118,1877,337],{"class":128},[118,1879,71],{"class":124},[118,1881,410],{"class":128},[118,1883,345],{"class":124},[118,1885,220],{"class":124},[118,1887,1888,1890,1892,1894,1896,1898,1900,1902],{"class":120,"line":202},[118,1889,1740],{"class":226},[118,1891,71],{"class":124},[118,1893,510],{"class":213},[118,1895,255],{"class":124},[118,1897,337],{"class":226},[118,1899,71],{"class":124},[118,1901,519],{"class":213},[118,1903,1289],{"class":124},[118,1905,1906],{"class":120,"line":207},[118,1907,574],{"class":124},[118,1909,1910],{"class":120,"line":223},[118,1911,706],{"class":124},[118,1913,1914],{"class":120,"line":244},[118,1915,1916],{"class":124},"})\n",[13,1918,1919],{},"좌측에 로고, 중앙에 제목, 우측에 총 페이지 수를 배치한 헤더. 컬럼 폭 합계가 12로 본문 행과 같은 규칙이다.",[13,1921,1922,1923,1926],{},"헤더 높이는 자동으로 측정된다. gpdf는 본문 레이아웃 전에 헤더 클로저를 한 번 실행해 렌더 결과의 높이를 재고, 각 페이지의 본문 가용 높이에서 뺀다. 푸터도 같다. ",[17,1924,1925],{},"headerHeight","를 인자로 넘길 필요 없다. 헤더에 행을 추가하면 본문이 그만큼 줄어든다.",[13,1928,1929],{},"두 영역 모두 콘텐츠 오버플로우로 만들어진 페이지를 포함한 모든 페이지에서 반복된다. 긴 테이블이 12페이지까지 흘러도 12페이지에도 헤더/푸터가 나온다. \"첫 페이지만\" 플래그는 현재 없다 (아래 참고).",[40,1931,1933],{"id":1932},"거친-부분-page-x-of-y를-한-줄로","거친 부분: \"Page X of Y\"를 한 줄로",[13,1935,1936,1937,1940,1941,1944,1945,53,1948,1950],{},"API에서 솔직히 더 좋아질 수 있다고 생각하는 곳. ",[17,1938,1939],{},"c.PageOf(\"Page %d of %d\")"," 같은 헬퍼가 없다. ",[17,1942,1943],{},"\"Page 3 of 12\"","라는 한 문자열을 만들려면 ",[17,1946,1947],{},"c.Text()",[17,1949,66],{},"가 독립된 컬럼 자식이라 컬럼을 조합해야 한다:",[109,1952,1954],{"className":111,"code":1953,"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,1955,1956,1986,2011,2041,2068,2072,2102,2121,2125,2155,2182,2186,2216,2235,2239,2270,2274],{"__ignoreMap":114},[118,1957,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978,1980,1982,1984],{"class":120,"line":121},[118,1959,363],{"class":226},[118,1961,71],{"class":124},[118,1963,387],{"class":213},[118,1965,255],{"class":124},[118,1967,19],{"class":299},[118,1969,395],{"class":124},[118,1971,398],{"class":124},[118,1973,401],{"class":331},[118,1975,334],{"class":124},[118,1977,337],{"class":128},[118,1979,71],{"class":124},[118,1981,410],{"class":128},[118,1983,345],{"class":124},[118,1985,220],{"class":124},[118,1987,1988,1991,1993,1995,1997,1999,2001,2003,2005,2007,2009],{"class":120,"line":132},[118,1989,1990],{"class":226},"    c",[118,1992,71],{"class":124},[118,1994,358],{"class":213},[118,1996,328],{"class":124},[118,1998,363],{"class":331},[118,2000,334],{"class":124},[118,2002,337],{"class":128},[118,2004,71],{"class":124},[118,2006,372],{"class":128},[118,2008,345],{"class":124},[118,2010,220],{"class":124},[118,2012,2013,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033,2035,2037,2039],{"class":120,"line":139},[118,2014,1709],{"class":226},[118,2016,71],{"class":124},[118,2018,387],{"class":213},[118,2020,255],{"class":124},[118,2022,688],{"class":299},[118,2024,395],{"class":124},[118,2026,398],{"class":124},[118,2028,401],{"class":331},[118,2030,334],{"class":124},[118,2032,337],{"class":128},[118,2034,71],{"class":124},[118,2036,410],{"class":128},[118,2038,345],{"class":124},[118,2040,220],{"class":124},[118,2042,2043,2045,2047,2049,2051,2053,2056,2058,2060,2062,2064,2066],{"class":120,"line":149},[118,2044,1740],{"class":226},[118,2046,71],{"class":124},[118,2048,425],{"class":213},[118,2050,255],{"class":124},[118,2052,430],{"class":124},[118,2054,2055],{"class":433},"Page",[118,2057,430],{"class":124},[118,2059,395],{"class":124},[118,2061,233],{"class":226},[118,2063,71],{"class":124},[118,2065,519],{"class":213},[118,2067,1289],{"class":124},[118,2069,2070],{"class":120,"line":161},[118,2071,574],{"class":124},[118,2073,2074,2076,2078,2080,2082,2084,2086,2088,2090,2092,2094,2096,2098,2100],{"class":120,"line":166},[118,2075,1709],{"class":226},[118,2077,71],{"class":124},[118,2079,387],{"class":213},[118,2081,255],{"class":124},[118,2083,870],{"class":299},[118,2085,395],{"class":124},[118,2087,398],{"class":124},[118,2089,401],{"class":331},[118,2091,334],{"class":124},[118,2093,337],{"class":128},[118,2095,71],{"class":124},[118,2097,410],{"class":128},[118,2099,345],{"class":124},[118,2101,220],{"class":124},[118,2103,2104,2106,2108,2110,2112,2114,2116,2119],{"class":120,"line":176},[118,2105,1740],{"class":226},[118,2107,71],{"class":124},[118,2109,1040],{"class":213},[118,2111,255],{"class":124},[118,2113,337],{"class":226},[118,2115,71],{"class":124},[118,2117,2118],{"class":213},"AlignCenter",[118,2120,1289],{"class":124},[118,2122,2123],{"class":120,"line":186},[118,2124,574],{"class":124},[118,2126,2127,2129,2131,2133,2135,2137,2139,2141,2143,2145,2147,2149,2151,2153],{"class":120,"line":196},[118,2128,1709],{"class":226},[118,2130,71],{"class":124},[118,2132,387],{"class":213},[118,2134,255],{"class":124},[118,2136,870],{"class":299},[118,2138,395],{"class":124},[118,2140,398],{"class":124},[118,2142,401],{"class":331},[118,2144,334],{"class":124},[118,2146,337],{"class":128},[118,2148,71],{"class":124},[118,2150,410],{"class":128},[118,2152,345],{"class":124},[118,2154,220],{"class":124},[118,2156,2157,2159,2161,2163,2165,2167,2170,2172,2174,2176,2178,2180],{"class":120,"line":202},[118,2158,1740],{"class":226},[118,2160,71],{"class":124},[118,2162,425],{"class":213},[118,2164,255],{"class":124},[118,2166,430],{"class":124},[118,2168,2169],{"class":433},"of",[118,2171,430],{"class":124},[118,2173,395],{"class":124},[118,2175,233],{"class":226},[118,2177,71],{"class":124},[118,2179,2118],{"class":213},[118,2181,1289],{"class":124},[118,2183,2184],{"class":120,"line":207},[118,2185,574],{"class":124},[118,2187,2188,2190,2192,2194,2196,2198,2200,2202,2204,2206,2208,2210,2212,2214],{"class":120,"line":223},[118,2189,1709],{"class":226},[118,2191,71],{"class":124},[118,2193,387],{"class":213},[118,2195,255],{"class":124},[118,2197,688],{"class":299},[118,2199,395],{"class":124},[118,2201,398],{"class":124},[118,2203,401],{"class":331},[118,2205,334],{"class":124},[118,2207,337],{"class":128},[118,2209,71],{"class":124},[118,2211,410],{"class":128},[118,2213,345],{"class":124},[118,2215,220],{"class":124},[118,2217,2218,2220,2222,2224,2226,2228,2230,2233],{"class":120,"line":244},[118,2219,1740],{"class":226},[118,2221,71],{"class":124},[118,2223,510],{"class":213},[118,2225,255],{"class":124},[118,2227,337],{"class":226},[118,2229,71],{"class":124},[118,2231,2232],{"class":213},"AlignLeft",[118,2234,1289],{"class":124},[118,2236,2237],{"class":120,"line":269},[118,2238,574],{"class":124},[118,2240,2241,2243,2245,2247,2249,2251,2253,2255,2257,2259,2261,2263,2265,2267],{"class":120,"line":306},[118,2242,1709],{"class":226},[118,2244,71],{"class":124},[118,2246,387],{"class":213},[118,2248,255],{"class":124},[118,2250,870],{"class":299},[118,2252,395],{"class":124},[118,2254,398],{"class":124},[118,2256,401],{"class":331},[118,2258,334],{"class":124},[118,2260,337],{"class":128},[118,2262,71],{"class":124},[118,2264,410],{"class":128},[118,2266,345],{"class":124},[118,2268,2269],{"class":124}," {})\n",[118,2271,2272],{"class":120,"line":312},[118,2273,706],{"class":124},[118,2275,2276],{"class":120,"line":317},[118,2277,1916],{"class":124},[13,2279,2280,2281,2284,2285,2288,2289,2292],{},"동작은 하고 보기에도 괜찮다. 다만 한 줄의 포맷 문자열로 끝날 일을 네 컬럼으로 펼쳐놓은 셈이라 살짝 거슬린다. ",[17,2282,2283],{},"fmt.Sprintf"," 스타일의 ",[17,2286,2287],{},"%d"," 플레이스홀더를 받는 ",[17,2290,2291],{},"c.PageOf(format string, opts ...TextOption)"," 헬퍼 추가를 검토 중이다. API 형태에 의견이 있으면 GitHub 이슈에 남겨주면 좋겠다.",[13,2294,2295],{},"지금 실용적인 단축은 \"Page\"를 빼고 슬래시로 잇는 것:",[109,2297,2299],{"className":111,"code":2298,"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,2300,2301,2331,2349,2353,2383,2409,2413,2443,2461],{"__ignoreMap":114},[118,2302,2303,2305,2307,2309,2311,2313,2315,2317,2319,2321,2323,2325,2327,2329],{"class":120,"line":121},[118,2304,363],{"class":226},[118,2306,71],{"class":124},[118,2308,387],{"class":213},[118,2310,255],{"class":124},[118,2312,392],{"class":299},[118,2314,395],{"class":124},[118,2316,398],{"class":124},[118,2318,401],{"class":331},[118,2320,334],{"class":124},[118,2322,337],{"class":128},[118,2324,71],{"class":124},[118,2326,410],{"class":128},[118,2328,345],{"class":124},[118,2330,220],{"class":124},[118,2332,2333,2335,2337,2339,2341,2343,2345,2347],{"class":120,"line":132},[118,2334,1990],{"class":226},[118,2336,71],{"class":124},[118,2338,1040],{"class":213},[118,2340,255],{"class":124},[118,2342,337],{"class":226},[118,2344,71],{"class":124},[118,2346,519],{"class":213},[118,2348,1289],{"class":124},[118,2350,2351],{"class":120,"line":139},[118,2352,1916],{"class":124},[118,2354,2355,2357,2359,2361,2363,2365,2367,2369,2371,2373,2375,2377,2379,2381],{"class":120,"line":149},[118,2356,363],{"class":226},[118,2358,71],{"class":124},[118,2360,387],{"class":213},[118,2362,255],{"class":124},[118,2364,1470],{"class":299},[118,2366,395],{"class":124},[118,2368,398],{"class":124},[118,2370,401],{"class":331},[118,2372,334],{"class":124},[118,2374,337],{"class":128},[118,2376,71],{"class":124},[118,2378,410],{"class":128},[118,2380,345],{"class":124},[118,2382,220],{"class":124},[118,2384,2385,2387,2389,2391,2393,2395,2397,2399,2401,2403,2405,2407],{"class":120,"line":161},[118,2386,1990],{"class":226},[118,2388,71],{"class":124},[118,2390,425],{"class":213},[118,2392,255],{"class":124},[118,2394,430],{"class":124},[118,2396,1556],{"class":433},[118,2398,430],{"class":124},[118,2400,395],{"class":124},[118,2402,233],{"class":226},[118,2404,71],{"class":124},[118,2406,2118],{"class":213},[118,2408,1289],{"class":124},[118,2410,2411],{"class":120,"line":166},[118,2412,1916],{"class":124},[118,2414,2415,2417,2419,2421,2423,2425,2427,2429,2431,2433,2435,2437,2439,2441],{"class":120,"line":176},[118,2416,363],{"class":226},[118,2418,71],{"class":124},[118,2420,387],{"class":213},[118,2422,255],{"class":124},[118,2424,1311],{"class":299},[118,2426,395],{"class":124},[118,2428,398],{"class":124},[118,2430,401],{"class":331},[118,2432,334],{"class":124},[118,2434,337],{"class":128},[118,2436,71],{"class":124},[118,2438,410],{"class":128},[118,2440,345],{"class":124},[118,2442,220],{"class":124},[118,2444,2445,2447,2449,2451,2453,2455,2457,2459],{"class":120,"line":186},[118,2446,1990],{"class":226},[118,2448,71],{"class":124},[118,2450,510],{"class":213},[118,2452,255],{"class":124},[118,2454,337],{"class":226},[118,2456,71],{"class":124},[118,2458,2232],{"class":213},[118,2460,1289],{"class":124},[118,2462,2463],{"class":120,"line":196},[118,2464,1916],{"class":124},[13,2466,2467,2470,2471,2474,2475,2478],{},[17,2468,2469],{},"3 / 12","는 푸터로 충분히 읽힌다. ",[17,2472,2473],{},"현재 3 / 전체 12","처럼 한국어 표현으로 만들고 싶으면 같은 방식으로 ",[17,2476,2477],{},"c.Text"," 칸을 더 끼우면 된다.",[40,2480,2482],{"id":2481},"자주-쓰는-배치","자주 쓰는 배치",[13,2484,2485],{},"실무에서 자주 등장하는 몇 가지.",[13,2487,2488,2491,2492,2494,2495,2498],{},[1606,2489,2490],{},"제목 아래 구분선."," ",[17,2493,358],{},"를 하나 더 추가해서 ",[17,2496,2497],{},"c.Line()","을 넣는다. 글 앞쪽의 예제가 이 형태.",[13,2500,2501,2504,2505,71],{},[1606,2502,2503],{},"중앙 \"기밀\" 푸터."," 한 행 한 컬럼, ",[17,2506,2118],{},[109,2508,2510],{"className":111,"code":2509,"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,2511,2512,2536,2560,2590,2610,2622,2636,2658,2662,2666],{"__ignoreMap":114},[118,2513,2514,2516,2518,2520,2522,2524,2526,2528,2530,2532,2534],{"class":120,"line":121},[118,2515,1659],{"class":226},[118,2517,71],{"class":124},[118,2519,721],{"class":213},[118,2521,328],{"class":124},[118,2523,13],{"class":331},[118,2525,334],{"class":124},[118,2527,337],{"class":128},[118,2529,71],{"class":124},[118,2531,342],{"class":128},[118,2533,345],{"class":124},[118,2535,220],{"class":124},[118,2537,2538,2540,2542,2544,2546,2548,2550,2552,2554,2556,2558],{"class":120,"line":132},[118,2539,1684],{"class":226},[118,2541,71],{"class":124},[118,2543,358],{"class":213},[118,2545,328],{"class":124},[118,2547,363],{"class":331},[118,2549,334],{"class":124},[118,2551,337],{"class":128},[118,2553,71],{"class":124},[118,2555,372],{"class":128},[118,2557,345],{"class":124},[118,2559,220],{"class":124},[118,2561,2562,2564,2566,2568,2570,2572,2574,2576,2578,2580,2582,2584,2586,2588],{"class":120,"line":139},[118,2563,1709],{"class":226},[118,2565,71],{"class":124},[118,2567,387],{"class":213},[118,2569,255],{"class":124},[118,2571,19],{"class":299},[118,2573,395],{"class":124},[118,2575,398],{"class":124},[118,2577,401],{"class":331},[118,2579,334],{"class":124},[118,2581,337],{"class":128},[118,2583,71],{"class":124},[118,2585,410],{"class":128},[118,2587,345],{"class":124},[118,2589,220],{"class":124},[118,2591,2592,2594,2596,2598,2600,2602,2605,2607],{"class":120,"line":149},[118,2593,1740],{"class":226},[118,2595,71],{"class":124},[118,2597,425],{"class":213},[118,2599,255],{"class":124},[118,2601,430],{"class":124},[118,2603,2604],{"class":433},"기밀 — 내부용",[118,2606,430],{"class":124},[118,2608,2609],{"class":124},",\n",[118,2611,2612,2615,2617,2619],{"class":120,"line":161},[118,2613,2614],{"class":226},"                template",[118,2616,71],{"class":124},[118,2618,2118],{"class":213},[118,2620,2621],{"class":124},"(),\n",[118,2623,2624,2626,2628,2630,2632,2634],{"class":120,"line":166},[118,2625,2614],{"class":226},[118,2627,71],{"class":124},[118,2629,455],{"class":213},[118,2631,255],{"class":124},[118,2633,969],{"class":299},[118,2635,266],{"class":124},[118,2637,2638,2640,2642,2644,2646,2648,2650,2652,2654,2656],{"class":120,"line":176},[118,2639,2614],{"class":226},[118,2641,71],{"class":124},[118,2643,545],{"class":213},[118,2645,255],{"class":124},[118,2647,550],{"class":226},[118,2649,71],{"class":124},[118,2651,555],{"class":213},[118,2653,255],{"class":124},[118,2655,560],{"class":299},[118,2657,563],{"class":124},[118,2659,2660],{"class":120,"line":186},[118,2661,574],{"class":124},[118,2663,2664],{"class":120,"line":196},[118,2665,706],{"class":124},[118,2667,2668],{"class":120,"line":202},[118,2669,1916],{"class":124},[13,2671,2672,2673,2676],{},"한국 기업 문서에서는 \"인쇄일: 2026년 5월 19일\", \"문서번호: DOC-2026-0517\" 같은 줄을 같이 넣는 경우가 많다. ",[17,2674,2675],{},"c.Text(...)","를 두세 줄 쌓으면 된다. 전자세금계산서 첨부용 PDF에도 같은 패턴이 들어간다.",[13,2678,2679,2682,2683,2686,2687,2689,2690,71],{},[1606,2680,2681],{},"좌측 로고, 우측 페이지 번호."," 8/4 또는 6/6으로 두 컬럼. 왼쪽에 ",[17,2684,2685],{},"c.Image(...)",", 오른쪽에 ",[17,2688,66],{}," + ",[17,2691,519],{},[13,2693,2694,2697,2698,2700],{},[1606,2695,2696],{},"\"다음 페이지에 계속\" 푸터."," 현재 미지원. 헤더/푸터 클로저는 ",[17,2699,342],{},"만 받고 현재 페이지 인덱스를 모르기 때문에 \"마지막 페이지인가\"로 분기할 수 없다. 본문 측에 마지막 페이지 빼고 \"계속\" 줄을 추가하려면 결국 총 페이지를 미리 알아야 해서 모순. 요청 목록에 있음.",[13,2702,2703,2706,2707,2710],{},[1606,2704,2705],{},"첫 페이지만 다른 헤더."," 같은 이유로 미지원. 우회책은 페이지 1의 본문 맨 위에 스페이서를 넣어 헤더를 실질적으로 비우고 2페이지부터 흐름에 맡기는, 좀 어색한 방식. ",[17,2708,2709],{},"doc.HeaderOn(pages, fn)"," 변형을 설계 중이다.",[40,2712,2714],{"id":2713},"cjk-그대로-동작","CJK 그대로 동작",[13,2716,2717,2718,2720,2721,2724],{},"gpdf는 CGO 없이 TrueType 폰트를 서브셋한다. 한국어, 중국어, 일본어를 헤더/푸터에 ",[17,2719,2675],{},"로 그대로 쓸 수 있다. ",[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(\"NotoSansKR\", notoSansKRRegular),\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(\"NotoSansKR\"), 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,71],{"class":124},[118,2742,238],{"class":213},[118,2744,241],{"class":124},[118,2746,2747,2750,2752,2754,2756,2758,2760,2762],{"class":120,"line":132},[118,2748,2749],{"class":226},"    template",[118,2751,71],{"class":124},[118,2753,252],{"class":213},[118,2755,255],{"class":124},[118,2757,258],{"class":226},[118,2759,71],{"class":124},[118,2761,263],{"class":226},[118,2763,266],{"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,71],{"class":124},[118,2771,2772],{"class":213},"WithFont",[118,2774,255],{"class":124},[118,2776,430],{"class":124},[118,2778,2779],{"class":433},"NotoSansKR",[118,2781,430],{"class":124},[118,2783,395],{"class":124},[118,2785,2786],{"class":226}," notoSansKRRegular",[118,2788,266],{"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,1659],{"class":226},[118,2802,71],{"class":124},[118,2804,721],{"class":213},[118,2806,328],{"class":124},[118,2808,13],{"class":331},[118,2810,334],{"class":124},[118,2812,337],{"class":128},[118,2814,71],{"class":124},[118,2816,342],{"class":128},[118,2818,345],{"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,1684],{"class":226},[118,2826,71],{"class":124},[118,2828,358],{"class":213},[118,2830,328],{"class":124},[118,2832,363],{"class":331},[118,2834,334],{"class":124},[118,2836,337],{"class":128},[118,2838,71],{"class":124},[118,2840,372],{"class":128},[118,2842,345],{"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,1709],{"class":226},[118,2850,71],{"class":124},[118,2852,387],{"class":213},[118,2854,255],{"class":124},[118,2856,392],{"class":299},[118,2858,395],{"class":124},[118,2860,398],{"class":124},[118,2862,401],{"class":331},[118,2864,334],{"class":124},[118,2866,337],{"class":128},[118,2868,71],{"class":124},[118,2870,410],{"class":128},[118,2872,345],{"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,1740],{"class":226},[118,2880,71],{"class":124},[118,2882,425],{"class":213},[118,2884,255],{"class":124},[118,2886,430],{"class":124},[118,2888,2889],{"class":433},"기밀",[118,2891,430],{"class":124},[118,2893,395],{"class":124},[118,2895,233],{"class":226},[118,2897,71],{"class":124},[118,2899,2900],{"class":213},"FontFamily",[118,2902,255],{"class":124},[118,2904,430],{"class":124},[118,2906,2779],{"class":433},[118,2908,430],{"class":124},[118,2910,1280],{"class":124},[118,2912,233],{"class":226},[118,2914,71],{"class":124},[118,2916,455],{"class":213},[118,2918,255],{"class":124},[118,2920,969],{"class":299},[118,2922,463],{"class":124},[118,2924,2925],{"class":120,"line":202},[118,2926,574],{"class":124},[118,2928,2929,2931,2933,2935,2937,2939,2941,2943,2945,2947,2949,2951,2953,2955],{"class":120,"line":207},[118,2930,1709],{"class":226},[118,2932,71],{"class":124},[118,2934,387],{"class":213},[118,2936,255],{"class":124},[118,2938,392],{"class":299},[118,2940,395],{"class":124},[118,2942,398],{"class":124},[118,2944,401],{"class":331},[118,2946,334],{"class":124},[118,2948,337],{"class":128},[118,2950,71],{"class":124},[118,2952,410],{"class":128},[118,2954,345],{"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,1740],{"class":226},[118,2962,71],{"class":124},[118,2964,1040],{"class":213},[118,2966,255],{"class":124},[118,2968,337],{"class":226},[118,2970,71],{"class":124},[118,2972,519],{"class":213},[118,2974,448],{"class":124},[118,2976,233],{"class":226},[118,2978,71],{"class":124},[118,2980,455],{"class":213},[118,2982,255],{"class":124},[118,2984,969],{"class":299},[118,2986,463],{"class":124},[118,2988,2989],{"class":120,"line":244},[118,2990,574],{"class":124},[118,2992,2993],{"class":120,"line":269},[118,2994,706],{"class":124},[118,2996,2997],{"class":120,"line":306},[118,2998,1916],{"class":124},[13,3000,3001,3002,3005],{},"최종 PDF에 임베드되는 서브셋은 \"실제 사용된 글리프만\" 들어간다. 60페이지 보고서 푸터에 ",[17,3003,3004],{},"\"기밀\"","만 있다면 NotoSansKR에서 두 글리프만 임베드된다. 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],{},"는 압축 결정 이후 콘텐츠 스트림 전체에 문자열 치환을 걸고, alias가 들어 있는 스트림을 다시 압축한다. gofpdf 자체 벤치마크에서 100페이지 문서 총 시간의 2〜4% 정도. gpdf 쪽은 스트림 인코딩 이전에 치환이 들어가므로 더 빠르다.",[13,3022,3023],{},"하루에 100만 개 PDF를 만든다면 차이가 난다. 하루에 10개면 의미 없다.",[40,3025,3027],{"id":3026},"faq","FAQ",[13,3029,3030,3033,3034,3037],{},[1606,3031,3032],{},"헤더/푸터 높이가 페이지 마진을 잠식하나요?","\n아니요. gpdf는 헤더와 푸터의 실제 높이를 측정하고, 본문 가용 높이를 ",[17,3035,3036],{},"pageHeight - top_margin - headerHeight - footerHeight - bottom_margin","으로 계산한다. 상단 마진 20mm에 헤더 15mm면, 본문은 페이지 상단에서 35mm부터 시작한다.",[13,3039,3040,3043],{},[1606,3041,3042],{},"페이지별로 헤더 높이를 다르게 할 수 있나요?","\n없다. 헤더 클로저는 측정용으로 한 번만 평가되고 결과는 문서 전체에 고정된다. 가변 높이가 필요하면 최대 높이를 고정하고 공백으로 조정해야 한다.",[13,3045,3046,3049],{},[1606,3047,3048],{},"본문이 비어 있는 페이지에도 헤더/푸터가 나오나요?","\ngpdf는 빈 페이지를 만들지 않는다. 본문이 3페이지에 다 들어가면 PDF는 3페이지가 된다. 헤더/푸터도 그 3페이지에만 나온다.",[13,3051,3052,3055,3056,3059],{},[1606,3053,3054],{},"세로/가로 혼합 문서에서 가로 페이지에만 다른 헤더를 쓰고 싶을 때는?","\n페이지별 ",[17,3057,3058],{},"WithPageSize(...)","로 방향을 바꾸는 건 지원되지만, 헤더/푸터 클로저는 방향과 상관없이 같은 것이 적용된다. 실무적으로는 양쪽 방향에서 모두 무난하게 보이는 중앙 정렬 디자인이 안전하다.",[13,3061,3062,3065,3066,3069,3070,53,3073,3069,3076,3079,3080,3083],{},[1606,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","가 같은 시나리오를 JSON으로 돌려 builder 버전과 동일한 golden PDF가 나오는지 검증한다.",[13,3085,3086,3089,3090,3093],{},[1606,3087,3088],{},"Go text/template 입구는?","\n동작. ",[17,3091,3092],{},"gpdf/_examples/gotemplate/26_page_number_test.go","가 같은 시나리오를 통과시킨다. 입구가 builder, JSON, Go template 중 무엇이든 아래에서는 같은 2단계 페이지네이션이 돈다.",[40,3095,3097],{"id":3096},"다음-단계","다음 단계",[13,3099,3100],{},"헤더, 푸터, 페이지 번호는 보고서에서 가장 눈에 띄지 않는 부분이지만, 보고서가 \"완성된 것\"처럼 보이게 만드는 핵심이기도 하다. 저수준 PDF 라이브러리 위에서 매번 이걸 직접 짜고 있었다면, 이 글의 몇 줄이 전부다. 예제를 복사해 문자열만 바꿔서 배포한다.",[13,3102,3103,3104,3107],{},"미해결 부분 — ",[17,3105,3106],{},"c.PageOf(...)"," 단일 문자열 포맷팅, 첫 페이지만 다른 헤더, \"마지막 페이지\" 감지 — 는 리스트에 있다. 무엇이든 막힌다면 GitHub 이슈에 적어달라. 추상적 요청보다 구체적인 유스케이스가 API 형태를 더 잘 결정한다.",[40,3109,3111],{"id":3110},"gpdf를-사용해보기","gpdf를 사용해보기",[13,3113,3114],{},"gpdf는 Go용 PDF 생성 라이브러리. MIT, 제로 의존성, CJK 지원.",[109,3116,3120],{"className":3117,"code":3118,"language":3119,"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,3121,3122],{"__ignoreMap":114},[118,3123,3124,3126,3129],{"class":120,"line":121},[118,3125,113],{"class":128},[118,3127,3128],{"class":433}," get",[118,3130,3131],{"class":433}," github.com/gpdf-dev/gpdf\n",[13,3133,3134,3141,3142],{},[3135,3136,3140],"a",{"href":3137,"rel":3138},"https://github.com/gpdf-dev/gpdf",[3139],"nofollow","⭐ GitHub에서 Star"," · ",[3135,3143,3146],{"href":3144,"rel":3145},"https://gpdf.dev/ko/docs/quickstart",[3139],"문서 읽기",[3148,3149,3150],"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":3152},[3153,3154,3155,3159,3160,3161,3162,3163,3164,3165,3166],{"id":42,"depth":132,"text":43},{"id":95,"depth":132,"text":96},{"id":1477,"depth":132,"text":3156,"children":3157},"\"Page X of Y\"의 Y가 어려운 이유",[3158],{"id":1592,"depth":139,"text":1593},{"id":1633,"depth":132,"text":1634},{"id":1932,"depth":132,"text":1933},{"id":2481,"depth":132,"text":2482},{"id":2713,"depth":132,"text":2714},{"id":3008,"depth":132,"text":3008},{"id":3026,"depth":132,"text":3027},{"id":3096,"depth":132,"text":3097},{"id":3110,"depth":132,"text":3111},"2026-05-19","gpdf로 Go PDF에 헤더, 푸터, 'Page X of Y'를 추가하는 방법. 두 개의 builder 메서드와 2단계 페이지네이션이 총 페이지 수를 자동으로 채운다.",false,"md",{"name":3172,"totalTime":3173,"tools":3174,"steps":3177},"Go PDF에 헤더, 푸터, Page X of Y 페이지 번호 넣기","PT15M",[3175,3176],"Go 1.22+","github.com/gpdf-dev/gpdf",[3178,3181,3184,3187,3190],{"name":3179,"text":3180},"template.New로 문서 만들기","template.New에 WithPageSize(document.A4)와 WithMargins를 전달한다. 모든 페이지 공통 설정은 이 한 곳에서 처리한다.",{"name":3182,"text":3183},"doc.Header로 헤더 등록","*template.PageBuilder를 받는 클로저를 전달한다. 본문과 동일한 AutoRow와 Col 12열 그리드를 사용할 수 있다. 총 페이지 수는 c.TotalPages()로 출력.",{"name":3185,"text":3186},"doc.Footer로 푸터 등록","또 다른 클로저를 전달한다. c.PageNumber()를 임의의 컬럼에 놓으면 현재 페이지 번호가 출력된다. 헤더와 푸터는 오버플로우 페이지를 포함한 모든 페이지에서 반복된다.",{"name":3188,"text":3189},"doc.AddPage로 본문 추가","논리 페이지마다 doc.AddPage를 호출하고 행과 열을 채운다. 본문 가용 높이는 측정된 헤더/푸터 높이만큼 자동으로 줄어든다.",{"name":3191,"text":3192},"doc.Generate로 PDF 출력","doc.Generate로 []byte를 받거나 doc.Render(w)로 io.Writer에 스트리밍한다. 바이트가 반환되기 직전 2단계 패스가 페이지 번호 플레이스홀더를 실제 숫자로 치환한다.",null,{},"/ko/blog/page-numbers-headers-footers",{"title":5,"description":3168},"ko/blog/027.page-numbers-headers-footers",[3199,3200],"tutorial","internals","kD3kNeUkIYxeoGK-XvXbb7YqO6HLSEABc9cUzo-iM-s",1779199026459]