[{"data":1,"prerenderedAt":5408},["ShallowReactive",2],{"blog-ja-signintech-gopdf-migration":3},{"id":4,"title":5,"author":6,"body":10,"date":5367,"description":5368,"draft":5369,"extension":5370,"howTo":5371,"image":5398,"meta":5399,"navigation":542,"path":5400,"seo":5401,"stem":5402,"tags":5403,"updated":5398,"__hash__":5407},"blogJa/ja/blog/021.signintech-gopdf-migration.md","signintech/gopdf から gpdf へ — 座標計算が消える",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":5350},"minimark",[13,18,39,57,60,63,67,70,73,82,85,88,91,101,123,126,130,133,482,503,507,510,515,994,999,1432,1439,1443,1446,1450,1909,1919,1923,2205,2222,2226,2232,2236,3039,3045,3049,3307,3313,3324,3328,3331,3335,3737,3744,3748,4005,4015,4019,4022,4026,4310,4313,4317,4669,4677,4681,4684,4963,4977,4980,4983,5078,5085,5092,5096,5099,5162,5165,5169,5175,5181,5195,5217,5240,5256,5273,5277,5280,5297,5311,5314,5346],[14,15,17],"h2",{"id":16},"tldr","TL;DR",[19,20,21,25,26,29,30,34,35,38],"p",{},[22,23,24],"strong",{},"gpdf"," は 12 カラムレイアウトエンジンを持つ純 Go の PDF ライブラリ。",[22,27,28],{},"signintech/gopdf"," は PDF 座標系に対する低レベルバインディング。gopdf を長く運用してきたコードベースが今や ",[31,32,33],"code",{},"SetXY","、",[31,36,37],{},"Cell","、幅の四則演算で埋まっているなら、それらの呼び出しがレイアウトエンジン下で何に畳み込まれるかをこの記事で追う。",[19,40,41,42,34,44,34,46,34,49,52,53,56],{},"先週、ある人と signintech/gopdf 製の請求書ジェネレーターをリファクタリングしていた。5 年ぶんの積層。明細テーブルを描画する関数が 280 行。実際に仕事をしているのはそのうち 40 行ほど — 金額のフォーマット、日付のフォーマット、行ごとに繰り返し。残りの 240 行は x 座標の計算、y 座標の追跡、",[31,43,33],{},[31,45,37],{},[31,47,48],{},"Br",[31,50,51],{},"Line(x1, y1, x2, y2)"," での罫線描画、行がページに収まるかどうかの判定、収まらないときの手動 ",[31,54,55],{},"AddPage"," とヘッダ再描画だった。",[19,58,59],{},"これが本番の gopdf。悪いライブラリではない。CGO なしの薄くて速い PDF イメージングモデルへのバインディング — 名前通りそれだけのもの。カーソルがあり、座標があり、レイアウトエンジンの役回りをエンジニアが演じる。",[19,61,62],{},"この記事は gopdf の API を gpdf にマッピングしていく。タイトルに結論は書いてある: ほとんどの行は消える、なぜならランタイムが代わりにやってくれるレイアウト計算だったから。",[14,64,66],{"id":65},"signintechgopdf-の良いところとそうでないところ","signintech/gopdf の良いところと、そうでないところ",[19,68,69],{},"「移行しよう」のフレーミングに入る前に正直に言っておくと、gopdf には実際の美点がある。",[19,71,72],{},"メンテナンスは活発。純 Go (CGO なし) なのでクロスコンパイルも Alpine イメージも素直に動く。CJK を含む TrueType フォントをサポートする。出力速度は速い — gopdf は重いエンジンを挟まずに PDF wire format を直接書くため、イメージングプリミティブだけ見れば gpdf と同じレンジに入る。API は内部 PDF モデルにそのまま対応している: 現在点があり、それを動かし、そこに描く。すでに PDF 座標で考える人にとって gopdf は素直。",[19,74,75,76,78,79,81],{},"そうでないのは、レイアウトシステムを持っていないところ。行・列・flex・グリッドという概念がない。自動改ページがない: コンテンツが下マージンを越えても、",[31,77,55],{}," を自分で呼ぶまで描画は続く (ページ外に出るかもしれない)。テーブルはプリミティブとしては存在しない — プロジェクトごとに ",[31,80,37],{}," 呼び出しと手書きの罫線、自前の改ページロジックで再実装するパターン。",[19,83,84],{},"1 ページの修了証や非常に決まったフォームならカーソルモデルで十分。請求書、レポート、明細書、可変長コンテンツを含むものは、ドキュメント表面積に比例して座標計算が増える。それが gpdf の対象ワークロード。",[14,86,87],{"id":87},"メンタルモデルの転換",[19,89,90],{},"ここがコードの読み味を実際に変える部分。gpdf には gopdf にない 2 つの考え方がある。",[19,92,93,96,97,100],{},[22,94,95],{},"宣言的なツリー。"," 描画位置をレンダラーに伝えるのではない。ページ → 行 → 列 → コンテンツのツリーを記述すると、レイアウトエンジンが 1 パスで位置を解決する。進めるカーソルがない。連続する 2 つの ",[31,98,99],{},"r.Col(...)"," は互いを知らない。",[19,102,103,106,107,110,111,114,115,118,119,122],{},[22,104,105],{},"12 カラムグリッド。"," 各行は暗黙的に 12 ユニット幅。それを列で消費する: ",[31,108,109],{},"r.Col(8, ...)"," は 2/3、",[31,112,113],{},"r.Col(4, ...)"," は 1/3。Bootstrap や Tailwind が HTML で使う考え方を PDF に持ち込んだもの。",[31,116,117],{},"pageWidth - leftMargin - rightMargin"," を 4 で割る代わりに、",[31,120,121],{},"r.Col(3, ...)"," を 4 回書く。",[19,124,125],{},"この 2 つだけで計算の大半が消える。後ろの Before/After ペアは全部同じ形に畳み込まれる: カーソルを進める命令ループが、小さな宣言的ツリーになる。",[14,127,129],{"id":128},"api-対応表","API 対応表",[19,131,132],{},"まずカンニングペーパー。具体的な対比は後ろのセクションで 5 ペア追う。",[134,135,136,150],"table",{},[137,138,139],"thead",{},[140,141,142,146,148],"tr",{},[143,144,145],"th",{},"やりたいこと",[143,147,28],{},[143,149,24],{},[151,152,153,169,184,199,219,237,255,274,292,308,323,338,353,368,389,411,434,449,464],"tbody",{},[140,154,155,159,164],{},[156,157,158],"td",{},"構築",[156,160,161],{},[31,162,163],{},"pdf := gopdf.GoPdf{}; pdf.Start(gopdf.Config{...})",[156,165,166],{},[31,167,168],{},"doc := gpdf.NewDocument(gpdf.WithPageSize(document.A4), ...)",[140,170,171,174,179],{},[156,172,173],{},"ページサイズ",[156,175,176],{},[31,177,178],{},"Config{PageSize: gopdf.PageSizeA4}",[156,180,181],{},[31,182,183],{},"gpdf.WithPageSize(document.A4)",[140,185,186,189,194],{},[156,187,188],{},"ページ追加",[156,190,191],{},[31,192,193],{},"pdf.AddPage()",[156,195,196],{},[31,197,198],{},"page := doc.AddPage()",[140,200,201,204,214],{},[156,202,203],{},"カーソル移動",[156,205,206,209,210],{},[31,207,208],{},"pdf.SetX(40); pdf.SetY(80)"," ",[211,212,213],"em",{},"(随所で)",[156,215,216],{},[211,217,218],{},"(カーソルなし)",[140,220,221,224,229],{},[156,222,223],{},"1 行テキスト",[156,225,226],{},[31,227,228],{},"pdf.SetXY(x, y); pdf.Cell(nil, \"hi\")",[156,230,231,209,234],{},[31,232,233],{},"c.Text(\"hi\")",[211,235,236],{},"(列の中)",[140,238,239,242,247],{},[156,240,241],{},"折り返しテキスト",[156,243,244],{},[31,245,246],{},"pdf.MultiCell(&gopdf.Rect{W: 200, H: 100}, body)",[156,248,249,209,252],{},[31,250,251],{},"c.Text(body)",[211,253,254],{},"(自動折り返し)",[140,256,257,260,265],{},[156,258,259],{},"改行",[156,261,262],{},[31,263,264],{},"pdf.Br(20)",[156,266,267],{},[211,268,269,270,273],{},"(行の間で暗黙、必要なら ",[31,271,272],{},"c.Spacer(document.Mm(4))",")",[140,275,276,279,284],{},[156,277,278],{},"フォント登録",[156,280,281],{},[31,282,283],{},"pdf.AddTTFFont(\"noto\", \"fonts/Noto.ttf\")",[156,285,286,209,289],{},[31,287,288],{},"gpdf.WithFont(\"Noto\", ttfBytes)",[211,290,291],{},"(構築時)",[140,293,294,297,302],{},[156,295,296],{},"アクティブフォント",[156,298,299],{},[31,300,301],{},"pdf.SetFont(\"noto\", \"\", 14)",[156,303,304,305],{},"テキスト単位で ",[31,306,307],{},"template.FontFamily(\"Noto\"), template.FontSize(14)",[140,309,310,313,318],{},[156,311,312],{},"色",[156,314,315],{},[31,316,317],{},"pdf.SetTextColor(26, 35, 126)",[156,319,320],{},[31,321,322],{},"template.TextColor(pdf.RGBHex(0x1A237E))",[140,324,325,328,333],{},[156,326,327],{},"横罫線",[156,329,330],{},[31,331,332],{},"pdf.Line(40, 100, 555, 100)",[156,334,335],{},[31,336,337],{},"c.Line(template.LineColor(pdf.Gray(0.7)))",[140,339,340,343,348],{},[156,341,342],{},"矩形",[156,344,345],{},[31,346,347],{},"pdf.RectFromUpperLeftWithStyle(x, y, w, h, \"FD\")",[156,349,350],{},[31,351,352],{},"c.Box(template.BgColor(...), template.Border(...))",[140,354,355,358,363],{},[156,356,357],{},"画像",[156,359,360],{},[31,361,362],{},"pdf.Image(\"logo.png\", x, y, &gopdf.Rect{W: 100, H: 50})",[156,364,365],{},[31,366,367],{},"c.Image(imgBytes, template.FitWidth(document.Mm(35)))",[140,369,370,373,384],{},[156,371,372],{},"手書きテーブル",[156,374,375,376,378,379,378,382],{},"多数の ",[31,377,37],{}," + ",[31,380,381],{},"Line",[31,383,33],{},[156,385,386],{},[31,387,388],{},"c.Table(headers, rows, template.ColumnWidths(...))",[140,390,391,394,403],{},[156,392,393],{},"ヘッダ / フッタ",[156,395,396,399,400],{},[31,397,398],{},"pdf.AddHeader(fn)"," / ",[31,401,402],{},"pdf.AddFooter(fn)",[156,404,405,399,408],{},[31,406,407],{},"doc.Header(fn)",[31,409,410],{},"doc.Footer(fn)",[140,412,413,416,423],{},[156,414,415],{},"ページ番号",[156,417,418,419,422],{},"自前カウンタから ",[31,420,421],{},"\"Page %d of %d\""," を組む",[156,424,425,399,428,209,431],{},[31,426,427],{},"c.PageNumber()",[31,429,430],{},"c.TotalPages()",[211,432,433],{},"(プレースホルダ)",[140,435,436,439,444],{},[156,437,438],{},"暗号化",[156,440,441],{},[31,442,443],{},"Config{Protection: PDFProtectionConfig{...}}",[156,445,446],{},[31,447,448],{},"gpdf.WithEncryption(gpdf.AES256, \"user\", \"owner\", perms)",[140,450,451,454,459],{},[156,452,453],{},"出力",[156,455,456],{},[31,457,458],{},"pdf.WritePdf(\"out.pdf\")",[156,460,461],{},[31,462,463],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[140,465,466,469,477],{},[156,467,468],{},"writer に出力",[156,470,471,399,474],{},[31,472,473],{},"pdf.Write(w)",[31,475,476],{},"pdf.ToBuffer()",[156,478,479],{},[31,480,481],{},"doc.Render(w)",[19,483,484,485,488,489,491,492,494,495,498,499,502],{},"構造的な変化は 2 つ。第一に、",[22,486,487],{},"カーソルが消える","。表で「随所で」とマークした行は誇張ではない — 実プロダクションの gopdf コードでは ",[31,490,33],{}," の出現数が ",[31,493,37],{}," を上回る。これらは gpdf 側ではすべて消滅する。第二に、",[22,496,497],{},"ピクセルがパーセンテージに変わる","。",[31,500,501],{},"Rect{W: 200, H: 100}"," は「この列はコンテナの 12 ユニット中 4 を取る」になる。同じ列を半幅の行に入れても比率が保たれて再計算は不要。",[14,504,506],{"id":505},"before-after-1-hello-world","Before / After 1: hello world",[19,508,509],{},"最小限の差分。右側で何が消えているかに注目。",[19,511,512],{},[22,513,514],{},"Before — signintech/gopdf:",[516,517,522],"pre",{"className":518,"code":519,"language":520,"meta":521,"style":521},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package main\n\nimport (\n    \"log\"\n\n    \"github.com/signintech/gopdf\"\n)\n\nfunc main() {\n    pdf := gopdf.GoPdf{}\n    pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})\n    pdf.AddPage()\n\n    if err := pdf.AddTTFFont(\"helvetica\", \"fonts/Helvetica.ttf\"); err != nil {\n        log.Fatal(err)\n    }\n    if err := pdf.SetFont(\"helvetica\", \"\", 24); err != nil {\n        log.Fatal(err)\n    }\n\n    pdf.SetX(40)\n    pdf.SetY(80)\n    if err := pdf.Cell(nil, \"Hello, World!\"); err != nil {\n        log.Fatal(err)\n    }\n\n    if err := pdf.WritePdf(\"hello.pdf\"); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[31,523,524,537,544,554,566,571,581,587,592,608,630,674,686,691,745,763,769,814,829,834,839,856,873,908,923,928,933,968,983,988],{"__ignoreMap":521},[525,526,529,533],"span",{"class":527,"line":528},"line",1,[525,530,532],{"class":531},"sMK4o","package",[525,534,536],{"class":535},"sBMFI"," main\n",[525,538,540],{"class":527,"line":539},2,[525,541,543],{"emptyLinePlaceholder":542},true,"\n",[525,545,547,551],{"class":527,"line":546},3,[525,548,550],{"class":549},"s7zQu","import",[525,552,553],{"class":531}," (\n",[525,555,557,560,563],{"class":527,"line":556},4,[525,558,559],{"class":531},"    \"",[525,561,562],{"class":535},"log",[525,564,565],{"class":531},"\"\n",[525,567,569],{"class":527,"line":568},5,[525,570,543],{"emptyLinePlaceholder":542},[525,572,574,576,579],{"class":527,"line":573},6,[525,575,559],{"class":531},[525,577,578],{"class":535},"github.com/signintech/gopdf",[525,580,565],{"class":531},[525,582,584],{"class":527,"line":583},7,[525,585,586],{"class":531},")\n",[525,588,590],{"class":527,"line":589},8,[525,591,543],{"emptyLinePlaceholder":542},[525,593,595,598,602,605],{"class":527,"line":594},9,[525,596,597],{"class":531},"func",[525,599,601],{"class":600},"s2Zo4"," main",[525,603,604],{"class":531},"()",[525,606,607],{"class":531}," {\n",[525,609,611,615,618,621,624,627],{"class":527,"line":610},10,[525,612,614],{"class":613},"sTEyZ","    pdf ",[525,616,617],{"class":531},":=",[525,619,620],{"class":535}," gopdf",[525,622,623],{"class":531},".",[525,625,626],{"class":535},"GoPdf",[525,628,629],{"class":531},"{}\n",[525,631,633,636,638,641,644,647,649,652,655,658,661,664,666,668,671],{"class":527,"line":632},11,[525,634,635],{"class":613},"    pdf",[525,637,623],{"class":531},[525,639,640],{"class":600},"Start",[525,642,643],{"class":531},"(",[525,645,646],{"class":535},"gopdf",[525,648,623],{"class":531},[525,650,651],{"class":535},"Config",[525,653,654],{"class":531},"{",[525,656,657],{"class":613},"PageSize",[525,659,660],{"class":531},":",[525,662,663],{"class":531}," *",[525,665,646],{"class":613},[525,667,623],{"class":531},[525,669,670],{"class":613},"PageSizeA4",[525,672,673],{"class":531},"})\n",[525,675,677,679,681,683],{"class":527,"line":676},12,[525,678,635],{"class":613},[525,680,623],{"class":531},[525,682,55],{"class":600},[525,684,685],{"class":531},"()\n",[525,687,689],{"class":527,"line":688},13,[525,690,543],{"emptyLinePlaceholder":542},[525,692,694,697,700,702,705,707,710,712,715,719,721,724,727,730,732,735,737,740,743],{"class":527,"line":693},14,[525,695,696],{"class":549},"    if",[525,698,699],{"class":613}," err ",[525,701,617],{"class":531},[525,703,704],{"class":613}," pdf",[525,706,623],{"class":531},[525,708,709],{"class":600},"AddTTFFont",[525,711,643],{"class":531},[525,713,714],{"class":531},"\"",[525,716,718],{"class":717},"sfazB","helvetica",[525,720,714],{"class":531},[525,722,723],{"class":531},",",[525,725,726],{"class":531}," \"",[525,728,729],{"class":717},"fonts/Helvetica.ttf",[525,731,714],{"class":531},[525,733,734],{"class":531},");",[525,736,699],{"class":613},[525,738,739],{"class":531},"!=",[525,741,742],{"class":531}," nil",[525,744,607],{"class":531},[525,746,748,751,753,756,758,761],{"class":527,"line":747},15,[525,749,750],{"class":613},"        log",[525,752,623],{"class":531},[525,754,755],{"class":600},"Fatal",[525,757,643],{"class":531},[525,759,760],{"class":613},"err",[525,762,586],{"class":531},[525,764,766],{"class":527,"line":765},16,[525,767,768],{"class":531},"    }\n",[525,770,772,774,776,778,780,782,785,787,789,791,793,795,798,800,804,806,808,810,812],{"class":527,"line":771},17,[525,773,696],{"class":549},[525,775,699],{"class":613},[525,777,617],{"class":531},[525,779,704],{"class":613},[525,781,623],{"class":531},[525,783,784],{"class":600},"SetFont",[525,786,643],{"class":531},[525,788,714],{"class":531},[525,790,718],{"class":717},[525,792,714],{"class":531},[525,794,723],{"class":531},[525,796,797],{"class":531}," \"\"",[525,799,723],{"class":531},[525,801,803],{"class":802},"sbssI"," 24",[525,805,734],{"class":531},[525,807,699],{"class":613},[525,809,739],{"class":531},[525,811,742],{"class":531},[525,813,607],{"class":531},[525,815,817,819,821,823,825,827],{"class":527,"line":816},18,[525,818,750],{"class":613},[525,820,623],{"class":531},[525,822,755],{"class":600},[525,824,643],{"class":531},[525,826,760],{"class":613},[525,828,586],{"class":531},[525,830,832],{"class":527,"line":831},19,[525,833,768],{"class":531},[525,835,837],{"class":527,"line":836},20,[525,838,543],{"emptyLinePlaceholder":542},[525,840,842,844,846,849,851,854],{"class":527,"line":841},21,[525,843,635],{"class":613},[525,845,623],{"class":531},[525,847,848],{"class":600},"SetX",[525,850,643],{"class":531},[525,852,853],{"class":802},"40",[525,855,586],{"class":531},[525,857,859,861,863,866,868,871],{"class":527,"line":858},22,[525,860,635],{"class":613},[525,862,623],{"class":531},[525,864,865],{"class":600},"SetY",[525,867,643],{"class":531},[525,869,870],{"class":802},"80",[525,872,586],{"class":531},[525,874,876,878,880,882,884,886,888,891,893,896,898,900,902,904,906],{"class":527,"line":875},23,[525,877,696],{"class":549},[525,879,699],{"class":613},[525,881,617],{"class":531},[525,883,704],{"class":613},[525,885,623],{"class":531},[525,887,37],{"class":600},[525,889,890],{"class":531},"(nil,",[525,892,726],{"class":531},[525,894,895],{"class":717},"Hello, World!",[525,897,714],{"class":531},[525,899,734],{"class":531},[525,901,699],{"class":613},[525,903,739],{"class":531},[525,905,742],{"class":531},[525,907,607],{"class":531},[525,909,911,913,915,917,919,921],{"class":527,"line":910},24,[525,912,750],{"class":613},[525,914,623],{"class":531},[525,916,755],{"class":600},[525,918,643],{"class":531},[525,920,760],{"class":613},[525,922,586],{"class":531},[525,924,926],{"class":527,"line":925},25,[525,927,768],{"class":531},[525,929,931],{"class":527,"line":930},26,[525,932,543],{"emptyLinePlaceholder":542},[525,934,936,938,940,942,944,946,949,951,953,956,958,960,962,964,966],{"class":527,"line":935},27,[525,937,696],{"class":549},[525,939,699],{"class":613},[525,941,617],{"class":531},[525,943,704],{"class":613},[525,945,623],{"class":531},[525,947,948],{"class":600},"WritePdf",[525,950,643],{"class":531},[525,952,714],{"class":531},[525,954,955],{"class":717},"hello.pdf",[525,957,714],{"class":531},[525,959,734],{"class":531},[525,961,699],{"class":613},[525,963,739],{"class":531},[525,965,742],{"class":531},[525,967,607],{"class":531},[525,969,971,973,975,977,979,981],{"class":527,"line":970},28,[525,972,750],{"class":613},[525,974,623],{"class":531},[525,976,755],{"class":600},[525,978,643],{"class":531},[525,980,760],{"class":613},[525,982,586],{"class":531},[525,984,986],{"class":527,"line":985},29,[525,987,768],{"class":531},[525,989,991],{"class":527,"line":990},30,[525,992,993],{"class":531},"}\n",[19,995,996],{},[22,997,998],{},"After — gpdf:",[516,1000,1002],{"className":518,"code":1001,"language":520,"meta":521,"style":521},"package main\n\nimport (\n    \"log\"\n    \"os\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Hello, World!\", template.FontSize(24), template.Bold())\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"hello.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[31,1003,1004,1010,1014,1020,1028,1037,1041,1050,1059,1068,1072,1076,1086,1104,1127,1162,1167,1171,1187,1218,1254,1300,1305,1310,1314,1334,1346,1360,1364,1408,1422,1427],{"__ignoreMap":521},[525,1005,1006,1008],{"class":527,"line":528},[525,1007,532],{"class":531},[525,1009,536],{"class":535},[525,1011,1012],{"class":527,"line":539},[525,1013,543],{"emptyLinePlaceholder":542},[525,1015,1016,1018],{"class":527,"line":546},[525,1017,550],{"class":549},[525,1019,553],{"class":531},[525,1021,1022,1024,1026],{"class":527,"line":556},[525,1023,559],{"class":531},[525,1025,562],{"class":535},[525,1027,565],{"class":531},[525,1029,1030,1032,1035],{"class":527,"line":568},[525,1031,559],{"class":531},[525,1033,1034],{"class":535},"os",[525,1036,565],{"class":531},[525,1038,1039],{"class":527,"line":573},[525,1040,543],{"emptyLinePlaceholder":542},[525,1042,1043,1045,1048],{"class":527,"line":583},[525,1044,559],{"class":531},[525,1046,1047],{"class":535},"github.com/gpdf-dev/gpdf",[525,1049,565],{"class":531},[525,1051,1052,1054,1057],{"class":527,"line":589},[525,1053,559],{"class":531},[525,1055,1056],{"class":535},"github.com/gpdf-dev/gpdf/document",[525,1058,565],{"class":531},[525,1060,1061,1063,1066],{"class":527,"line":594},[525,1062,559],{"class":531},[525,1064,1065],{"class":535},"github.com/gpdf-dev/gpdf/template",[525,1067,565],{"class":531},[525,1069,1070],{"class":527,"line":610},[525,1071,586],{"class":531},[525,1073,1074],{"class":527,"line":632},[525,1075,543],{"emptyLinePlaceholder":542},[525,1077,1078,1080,1082,1084],{"class":527,"line":676},[525,1079,597],{"class":531},[525,1081,601],{"class":600},[525,1083,604],{"class":531},[525,1085,607],{"class":531},[525,1087,1088,1091,1093,1096,1098,1101],{"class":527,"line":688},[525,1089,1090],{"class":613},"    doc ",[525,1092,617],{"class":531},[525,1094,1095],{"class":613}," gpdf",[525,1097,623],{"class":531},[525,1099,1100],{"class":600},"NewDocument",[525,1102,1103],{"class":531},"(\n",[525,1105,1106,1109,1111,1114,1116,1119,1121,1124],{"class":527,"line":693},[525,1107,1108],{"class":613},"        gpdf",[525,1110,623],{"class":531},[525,1112,1113],{"class":600},"WithPageSize",[525,1115,643],{"class":531},[525,1117,1118],{"class":613},"document",[525,1120,623],{"class":531},[525,1122,1123],{"class":613},"A4",[525,1125,1126],{"class":531},"),\n",[525,1128,1129,1131,1133,1136,1138,1140,1142,1145,1147,1149,1151,1154,1156,1159],{"class":527,"line":747},[525,1130,1108],{"class":613},[525,1132,623],{"class":531},[525,1134,1135],{"class":600},"WithMargins",[525,1137,643],{"class":531},[525,1139,1118],{"class":613},[525,1141,623],{"class":531},[525,1143,1144],{"class":600},"UniformEdges",[525,1146,643],{"class":531},[525,1148,1118],{"class":613},[525,1150,623],{"class":531},[525,1152,1153],{"class":600},"Mm",[525,1155,643],{"class":531},[525,1157,1158],{"class":802},"20",[525,1160,1161],{"class":531},"))),\n",[525,1163,1164],{"class":527,"line":765},[525,1165,1166],{"class":531},"    )\n",[525,1168,1169],{"class":527,"line":771},[525,1170,543],{"emptyLinePlaceholder":542},[525,1172,1173,1176,1178,1181,1183,1185],{"class":527,"line":816},[525,1174,1175],{"class":613},"    page ",[525,1177,617],{"class":531},[525,1179,1180],{"class":613}," doc",[525,1182,623],{"class":531},[525,1184,55],{"class":600},[525,1186,685],{"class":531},[525,1188,1189,1192,1194,1197,1200,1204,1206,1209,1211,1214,1216],{"class":527,"line":831},[525,1190,1191],{"class":613},"    page",[525,1193,623],{"class":531},[525,1195,1196],{"class":600},"AutoRow",[525,1198,1199],{"class":531},"(func(",[525,1201,1203],{"class":1202},"sHdIc","r",[525,1205,663],{"class":531},[525,1207,1208],{"class":535},"template",[525,1210,623],{"class":531},[525,1212,1213],{"class":535},"RowBuilder",[525,1215,273],{"class":531},[525,1217,607],{"class":531},[525,1219,1220,1223,1225,1228,1230,1233,1235,1238,1241,1243,1245,1247,1250,1252],{"class":527,"line":836},[525,1221,1222],{"class":613},"        r",[525,1224,623],{"class":531},[525,1226,1227],{"class":600},"Col",[525,1229,643],{"class":531},[525,1231,1232],{"class":802},"12",[525,1234,723],{"class":531},[525,1236,1237],{"class":531}," func(",[525,1239,1240],{"class":1202},"c",[525,1242,663],{"class":531},[525,1244,1208],{"class":535},[525,1246,623],{"class":531},[525,1248,1249],{"class":535},"ColBuilder",[525,1251,273],{"class":531},[525,1253,607],{"class":531},[525,1255,1256,1259,1261,1264,1266,1268,1270,1272,1274,1277,1279,1282,1284,1287,1290,1292,1294,1297],{"class":527,"line":841},[525,1257,1258],{"class":613},"            c",[525,1260,623],{"class":531},[525,1262,1263],{"class":600},"Text",[525,1265,643],{"class":531},[525,1267,714],{"class":531},[525,1269,895],{"class":717},[525,1271,714],{"class":531},[525,1273,723],{"class":531},[525,1275,1276],{"class":613}," template",[525,1278,623],{"class":531},[525,1280,1281],{"class":600},"FontSize",[525,1283,643],{"class":531},[525,1285,1286],{"class":802},"24",[525,1288,1289],{"class":531},"),",[525,1291,1276],{"class":613},[525,1293,623],{"class":531},[525,1295,1296],{"class":600},"Bold",[525,1298,1299],{"class":531},"())\n",[525,1301,1302],{"class":527,"line":858},[525,1303,1304],{"class":531},"        })\n",[525,1306,1307],{"class":527,"line":875},[525,1308,1309],{"class":531},"    })\n",[525,1311,1312],{"class":527,"line":910},[525,1313,543],{"emptyLinePlaceholder":542},[525,1315,1316,1319,1321,1323,1325,1327,1329,1332],{"class":527,"line":925},[525,1317,1318],{"class":613},"    data",[525,1320,723],{"class":531},[525,1322,699],{"class":613},[525,1324,617],{"class":531},[525,1326,1180],{"class":613},[525,1328,623],{"class":531},[525,1330,1331],{"class":600},"Generate",[525,1333,685],{"class":531},[525,1335,1336,1338,1340,1342,1344],{"class":527,"line":930},[525,1337,696],{"class":549},[525,1339,699],{"class":613},[525,1341,739],{"class":531},[525,1343,742],{"class":531},[525,1345,607],{"class":531},[525,1347,1348,1350,1352,1354,1356,1358],{"class":527,"line":935},[525,1349,750],{"class":613},[525,1351,623],{"class":531},[525,1353,755],{"class":600},[525,1355,643],{"class":531},[525,1357,760],{"class":613},[525,1359,586],{"class":531},[525,1361,1362],{"class":527,"line":970},[525,1363,768],{"class":531},[525,1365,1366,1368,1370,1372,1375,1377,1380,1382,1384,1386,1388,1390,1393,1395,1398,1400,1402,1404,1406],{"class":527,"line":985},[525,1367,696],{"class":549},[525,1369,699],{"class":613},[525,1371,617],{"class":531},[525,1373,1374],{"class":613}," os",[525,1376,623],{"class":531},[525,1378,1379],{"class":600},"WriteFile",[525,1381,643],{"class":531},[525,1383,714],{"class":531},[525,1385,955],{"class":717},[525,1387,714],{"class":531},[525,1389,723],{"class":531},[525,1391,1392],{"class":613}," data",[525,1394,723],{"class":531},[525,1396,1397],{"class":802}," 0o644",[525,1399,734],{"class":531},[525,1401,699],{"class":613},[525,1403,739],{"class":531},[525,1405,742],{"class":531},[525,1407,607],{"class":531},[525,1409,1410,1412,1414,1416,1418,1420],{"class":527,"line":990},[525,1411,750],{"class":613},[525,1413,623],{"class":531},[525,1415,755],{"class":600},[525,1417,643],{"class":531},[525,1419,760],{"class":613},[525,1421,586],{"class":531},[525,1423,1425],{"class":527,"line":1424},31,[525,1426,768],{"class":531},[525,1428,1430],{"class":527,"line":1429},32,[525,1431,993],{"class":531},[19,1433,1434,1435,1438],{},"消えたものが 2 つ。Helvetica は標準 14 フォントの一員で gpdf にバンドル済みなので、ランタイムに TTF ファイルは不要。",[31,1436,1437],{},"SetX(40); SetY(80)"," も消える — 行はページマージン内に自動で収まる。代わりに加わったのは 12 ユニット幅の単一列を持つ行。\"Hello, World!\" にはこの足場は重く見えるが、これがそのまま 100 ページレポートを支える足場でもある、というのが本題。",[14,1440,1442],{"id":1441},"before-after-2-4-列のヘッダ行","Before / After 2: 4 列のヘッダ行",[19,1444,1445],{},"ここが座標計算の見え方が一番強く出る場所。ページ幅をぶち抜く 4 つの等幅セルがほしい場合: ページ幅からマージンを引き、4 で割る。gopdf ではその割り算を書く。gpdf では 12 ユニットを 4 つに分ける。",[19,1447,1448],{},[22,1449,514],{},[516,1451,1453],{"className":518,"code":1452,"language":520,"meta":521,"style":521},"const (\n    pageWidth   = 595.28 // A4 (pt)\n    leftMargin  = 40.0\n    rightMargin = 40.0\n    rowY        = 100.0\n    rowH        = 24.0\n)\n\ncontentWidth := pageWidth - leftMargin - rightMargin // 515.28\ncolW := contentWidth / 4                              // 128.82\n\npdf.SetFont(\"helvetica-bold\", \"\", 11)\npdf.SetFillColor(26, 35, 126)\npdf.SetTextColor(255, 255, 255)\n\nheaders := []string{\"明細\", \"数量\", \"単価\", \"金額\"}\nfor i, h := range headers {\n    x := leftMargin + colW*float64(i)\n    pdf.RectFromUpperLeftWithStyle(x, rowY, colW, rowH, \"F\")\n\n    pdf.SetXY(x+6, rowY+7)\n    if err := pdf.Cell(nil, h); err != nil {\n        log.Fatal(err)\n    }\n}\n\npdf.SetTextColor(0, 0, 0)\n",[31,1454,1455,1462,1477,1487,1496,1506,1516,1520,1524,1548,1567,1571,1600,1626,1651,1655,1707,1731,1759,1798,1802,1830,1859,1873,1877,1881,1885],{"__ignoreMap":521},[525,1456,1457,1460],{"class":527,"line":528},[525,1458,1459],{"class":531},"const",[525,1461,553],{"class":531},[525,1463,1464,1467,1470,1473],{"class":527,"line":539},[525,1465,1466],{"class":613},"    pageWidth   ",[525,1468,1469],{"class":531},"=",[525,1471,1472],{"class":802}," 595.28",[525,1474,1476],{"class":1475},"sHwdD"," // A4 (pt)\n",[525,1478,1479,1482,1484],{"class":527,"line":546},[525,1480,1481],{"class":613},"    leftMargin  ",[525,1483,1469],{"class":531},[525,1485,1486],{"class":802}," 40.0\n",[525,1488,1489,1492,1494],{"class":527,"line":556},[525,1490,1491],{"class":613},"    rightMargin ",[525,1493,1469],{"class":531},[525,1495,1486],{"class":802},[525,1497,1498,1501,1503],{"class":527,"line":568},[525,1499,1500],{"class":613},"    rowY        ",[525,1502,1469],{"class":531},[525,1504,1505],{"class":802}," 100.0\n",[525,1507,1508,1511,1513],{"class":527,"line":573},[525,1509,1510],{"class":613},"    rowH        ",[525,1512,1469],{"class":531},[525,1514,1515],{"class":802}," 24.0\n",[525,1517,1518],{"class":527,"line":583},[525,1519,586],{"class":531},[525,1521,1522],{"class":527,"line":589},[525,1523,543],{"emptyLinePlaceholder":542},[525,1525,1526,1529,1531,1534,1537,1540,1542,1545],{"class":527,"line":594},[525,1527,1528],{"class":613},"contentWidth ",[525,1530,617],{"class":531},[525,1532,1533],{"class":613}," pageWidth ",[525,1535,1536],{"class":531},"-",[525,1538,1539],{"class":613}," leftMargin ",[525,1541,1536],{"class":531},[525,1543,1544],{"class":613}," rightMargin ",[525,1546,1547],{"class":1475},"// 515.28\n",[525,1549,1550,1553,1555,1558,1561,1564],{"class":527,"line":610},[525,1551,1552],{"class":613},"colW ",[525,1554,617],{"class":531},[525,1556,1557],{"class":613}," contentWidth ",[525,1559,1560],{"class":531},"/",[525,1562,1563],{"class":802}," 4",[525,1565,1566],{"class":1475},"                              // 128.82\n",[525,1568,1569],{"class":527,"line":632},[525,1570,543],{"emptyLinePlaceholder":542},[525,1572,1573,1576,1578,1580,1582,1584,1587,1589,1591,1593,1595,1598],{"class":527,"line":676},[525,1574,1575],{"class":613},"pdf",[525,1577,623],{"class":531},[525,1579,784],{"class":600},[525,1581,643],{"class":531},[525,1583,714],{"class":531},[525,1585,1586],{"class":717},"helvetica-bold",[525,1588,714],{"class":531},[525,1590,723],{"class":531},[525,1592,797],{"class":531},[525,1594,723],{"class":531},[525,1596,1597],{"class":802}," 11",[525,1599,586],{"class":531},[525,1601,1602,1604,1606,1609,1611,1614,1616,1619,1621,1624],{"class":527,"line":688},[525,1603,1575],{"class":613},[525,1605,623],{"class":531},[525,1607,1608],{"class":600},"SetFillColor",[525,1610,643],{"class":531},[525,1612,1613],{"class":802},"26",[525,1615,723],{"class":531},[525,1617,1618],{"class":802}," 35",[525,1620,723],{"class":531},[525,1622,1623],{"class":802}," 126",[525,1625,586],{"class":531},[525,1627,1628,1630,1632,1635,1637,1640,1642,1645,1647,1649],{"class":527,"line":693},[525,1629,1575],{"class":613},[525,1631,623],{"class":531},[525,1633,1634],{"class":600},"SetTextColor",[525,1636,643],{"class":531},[525,1638,1639],{"class":802},"255",[525,1641,723],{"class":531},[525,1643,1644],{"class":802}," 255",[525,1646,723],{"class":531},[525,1648,1644],{"class":802},[525,1650,586],{"class":531},[525,1652,1653],{"class":527,"line":747},[525,1654,543],{"emptyLinePlaceholder":542},[525,1656,1657,1660,1662,1665,1669,1671,1673,1676,1678,1680,1682,1685,1687,1689,1691,1694,1696,1698,1700,1703,1705],{"class":527,"line":765},[525,1658,1659],{"class":613},"headers ",[525,1661,617],{"class":531},[525,1663,1664],{"class":531}," []",[525,1666,1668],{"class":1667},"spNyl","string",[525,1670,654],{"class":531},[525,1672,714],{"class":531},[525,1674,1675],{"class":717},"明細",[525,1677,714],{"class":531},[525,1679,723],{"class":531},[525,1681,726],{"class":531},[525,1683,1684],{"class":717},"数量",[525,1686,714],{"class":531},[525,1688,723],{"class":531},[525,1690,726],{"class":531},[525,1692,1693],{"class":717},"単価",[525,1695,714],{"class":531},[525,1697,723],{"class":531},[525,1699,726],{"class":531},[525,1701,1702],{"class":717},"金額",[525,1704,714],{"class":531},[525,1706,993],{"class":531},[525,1708,1709,1712,1715,1717,1720,1722,1725,1728],{"class":527,"line":771},[525,1710,1711],{"class":549},"for",[525,1713,1714],{"class":613}," i",[525,1716,723],{"class":531},[525,1718,1719],{"class":613}," h ",[525,1721,617],{"class":531},[525,1723,1724],{"class":549}," range",[525,1726,1727],{"class":613}," headers ",[525,1729,1730],{"class":531},"{\n",[525,1732,1733,1736,1738,1740,1743,1746,1749,1752,1754,1757],{"class":527,"line":816},[525,1734,1735],{"class":613},"    x ",[525,1737,617],{"class":531},[525,1739,1539],{"class":613},[525,1741,1742],{"class":531},"+",[525,1744,1745],{"class":613}," colW",[525,1747,1748],{"class":531},"*",[525,1750,1751],{"class":1667},"float64",[525,1753,643],{"class":531},[525,1755,1756],{"class":613},"i",[525,1758,586],{"class":531},[525,1760,1761,1763,1765,1768,1770,1773,1775,1778,1780,1782,1784,1787,1789,1791,1794,1796],{"class":527,"line":831},[525,1762,635],{"class":613},[525,1764,623],{"class":531},[525,1766,1767],{"class":600},"RectFromUpperLeftWithStyle",[525,1769,643],{"class":531},[525,1771,1772],{"class":613},"x",[525,1774,723],{"class":531},[525,1776,1777],{"class":613}," rowY",[525,1779,723],{"class":531},[525,1781,1745],{"class":613},[525,1783,723],{"class":531},[525,1785,1786],{"class":613}," rowH",[525,1788,723],{"class":531},[525,1790,726],{"class":531},[525,1792,1793],{"class":717},"F",[525,1795,714],{"class":531},[525,1797,586],{"class":531},[525,1799,1800],{"class":527,"line":836},[525,1801,543],{"emptyLinePlaceholder":542},[525,1803,1804,1806,1808,1810,1812,1814,1816,1819,1821,1823,1825,1828],{"class":527,"line":841},[525,1805,635],{"class":613},[525,1807,623],{"class":531},[525,1809,33],{"class":600},[525,1811,643],{"class":531},[525,1813,1772],{"class":613},[525,1815,1742],{"class":531},[525,1817,1818],{"class":802},"6",[525,1820,723],{"class":531},[525,1822,1777],{"class":613},[525,1824,1742],{"class":531},[525,1826,1827],{"class":802},"7",[525,1829,586],{"class":531},[525,1831,1832,1834,1836,1838,1840,1842,1844,1846,1849,1851,1853,1855,1857],{"class":527,"line":858},[525,1833,696],{"class":549},[525,1835,699],{"class":613},[525,1837,617],{"class":531},[525,1839,704],{"class":613},[525,1841,623],{"class":531},[525,1843,37],{"class":600},[525,1845,890],{"class":531},[525,1847,1848],{"class":613}," h",[525,1850,734],{"class":531},[525,1852,699],{"class":613},[525,1854,739],{"class":531},[525,1856,742],{"class":531},[525,1858,607],{"class":531},[525,1860,1861,1863,1865,1867,1869,1871],{"class":527,"line":875},[525,1862,750],{"class":613},[525,1864,623],{"class":531},[525,1866,755],{"class":600},[525,1868,643],{"class":531},[525,1870,760],{"class":613},[525,1872,586],{"class":531},[525,1874,1875],{"class":527,"line":910},[525,1876,768],{"class":531},[525,1878,1879],{"class":527,"line":925},[525,1880,993],{"class":531},[525,1882,1883],{"class":527,"line":930},[525,1884,543],{"emptyLinePlaceholder":542},[525,1886,1887,1889,1891,1893,1895,1898,1900,1903,1905,1907],{"class":527,"line":935},[525,1888,1575],{"class":613},[525,1890,623],{"class":531},[525,1892,1634],{"class":600},[525,1894,643],{"class":531},[525,1896,1897],{"class":802},"0",[525,1899,723],{"class":531},[525,1901,1902],{"class":802}," 0",[525,1904,723],{"class":531},[525,1906,1902],{"class":802},[525,1908,586],{"class":531},[19,1910,1911,1912,1915,1916,1918],{},"定数が 4 つ、幅の引き算 1 回、割り算 1 回、",[31,1913,1914],{},"colW*float64(i)"," のループ — その float キャストは Go の ",[31,1917,1748],{}," が int を float64 に昇格しないからだけのコード。gpdf 版にはどれも存在しない。",[19,1920,1921],{},[22,1922,998],{},[516,1924,1926],{"className":518,"code":1925,"language":520,"meta":521,"style":521},"page.AutoRow(func(r *template.RowBuilder) {\n    headers := []string{\"明細\", \"数量\", \"単価\", \"金額\"}\n    for _, h := range headers {\n        r.Col(3, func(c *template.ColBuilder) {\n            c.Box(\n                template.BgColor(pdf.RGBHex(0x1A237E)),\n                template.Padding(document.Mm(2), document.Mm(3)),\n            )\n            c.Text(h,\n                template.Bold(), template.FontSize(11),\n                template.TextColor(pdf.White),\n            )\n        })\n    }\n})\n",[31,1927,1928,1953,1998,2018,2049,2060,2087,2124,2129,2145,2169,2189,2193,2197,2201],{"__ignoreMap":521},[525,1929,1930,1933,1935,1937,1939,1941,1943,1945,1947,1949,1951],{"class":527,"line":528},[525,1931,1932],{"class":613},"page",[525,1934,623],{"class":531},[525,1936,1196],{"class":600},[525,1938,1199],{"class":531},[525,1940,1203],{"class":1202},[525,1942,663],{"class":531},[525,1944,1208],{"class":535},[525,1946,623],{"class":531},[525,1948,1213],{"class":535},[525,1950,273],{"class":531},[525,1952,607],{"class":531},[525,1954,1955,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990,1992,1994,1996],{"class":527,"line":539},[525,1956,1957],{"class":613},"    headers ",[525,1959,617],{"class":531},[525,1961,1664],{"class":531},[525,1963,1668],{"class":1667},[525,1965,654],{"class":531},[525,1967,714],{"class":531},[525,1969,1675],{"class":717},[525,1971,714],{"class":531},[525,1973,723],{"class":531},[525,1975,726],{"class":531},[525,1977,1684],{"class":717},[525,1979,714],{"class":531},[525,1981,723],{"class":531},[525,1983,726],{"class":531},[525,1985,1693],{"class":717},[525,1987,714],{"class":531},[525,1989,723],{"class":531},[525,1991,726],{"class":531},[525,1993,1702],{"class":717},[525,1995,714],{"class":531},[525,1997,993],{"class":531},[525,1999,2000,2003,2006,2008,2010,2012,2014,2016],{"class":527,"line":546},[525,2001,2002],{"class":549},"    for",[525,2004,2005],{"class":613}," _",[525,2007,723],{"class":531},[525,2009,1719],{"class":613},[525,2011,617],{"class":531},[525,2013,1724],{"class":549},[525,2015,1727],{"class":613},[525,2017,1730],{"class":531},[525,2019,2020,2022,2024,2026,2028,2031,2033,2035,2037,2039,2041,2043,2045,2047],{"class":527,"line":556},[525,2021,1222],{"class":613},[525,2023,623],{"class":531},[525,2025,1227],{"class":600},[525,2027,643],{"class":531},[525,2029,2030],{"class":802},"3",[525,2032,723],{"class":531},[525,2034,1237],{"class":531},[525,2036,1240],{"class":1202},[525,2038,663],{"class":531},[525,2040,1208],{"class":535},[525,2042,623],{"class":531},[525,2044,1249],{"class":535},[525,2046,273],{"class":531},[525,2048,607],{"class":531},[525,2050,2051,2053,2055,2058],{"class":527,"line":568},[525,2052,1258],{"class":613},[525,2054,623],{"class":531},[525,2056,2057],{"class":600},"Box",[525,2059,1103],{"class":531},[525,2061,2062,2065,2067,2070,2072,2074,2076,2079,2081,2084],{"class":527,"line":573},[525,2063,2064],{"class":613},"                template",[525,2066,623],{"class":531},[525,2068,2069],{"class":600},"BgColor",[525,2071,643],{"class":531},[525,2073,1575],{"class":613},[525,2075,623],{"class":531},[525,2077,2078],{"class":600},"RGBHex",[525,2080,643],{"class":531},[525,2082,2083],{"class":802},"0x1A237E",[525,2085,2086],{"class":531},")),\n",[525,2088,2089,2091,2093,2096,2098,2100,2102,2104,2106,2109,2111,2114,2116,2118,2120,2122],{"class":527,"line":583},[525,2090,2064],{"class":613},[525,2092,623],{"class":531},[525,2094,2095],{"class":600},"Padding",[525,2097,643],{"class":531},[525,2099,1118],{"class":613},[525,2101,623],{"class":531},[525,2103,1153],{"class":600},[525,2105,643],{"class":531},[525,2107,2108],{"class":802},"2",[525,2110,1289],{"class":531},[525,2112,2113],{"class":613}," document",[525,2115,623],{"class":531},[525,2117,1153],{"class":600},[525,2119,643],{"class":531},[525,2121,2030],{"class":802},[525,2123,2086],{"class":531},[525,2125,2126],{"class":527,"line":589},[525,2127,2128],{"class":531},"            )\n",[525,2130,2131,2133,2135,2137,2139,2142],{"class":527,"line":594},[525,2132,1258],{"class":613},[525,2134,623],{"class":531},[525,2136,1263],{"class":600},[525,2138,643],{"class":531},[525,2140,2141],{"class":613},"h",[525,2143,2144],{"class":531},",\n",[525,2146,2147,2149,2151,2153,2156,2158,2160,2162,2164,2167],{"class":527,"line":610},[525,2148,2064],{"class":613},[525,2150,623],{"class":531},[525,2152,1296],{"class":600},[525,2154,2155],{"class":531},"(),",[525,2157,1276],{"class":613},[525,2159,623],{"class":531},[525,2161,1281],{"class":600},[525,2163,643],{"class":531},[525,2165,2166],{"class":802},"11",[525,2168,1126],{"class":531},[525,2170,2171,2173,2175,2178,2180,2182,2184,2187],{"class":527,"line":632},[525,2172,2064],{"class":613},[525,2174,623],{"class":531},[525,2176,2177],{"class":600},"TextColor",[525,2179,643],{"class":531},[525,2181,1575],{"class":613},[525,2183,623],{"class":531},[525,2185,2186],{"class":613},"White",[525,2188,1126],{"class":531},[525,2190,2191],{"class":527,"line":676},[525,2192,2128],{"class":531},[525,2194,2195],{"class":527,"line":688},[525,2196,1304],{"class":531},[525,2198,2199],{"class":527,"line":693},[525,2200,768],{"class":531},[525,2202,2203],{"class":527,"line":747},[525,2204,673],{"class":531},[19,2206,2207,2209,2210,2213,2214,2217,2218,2221],{},[31,2208,121],{}," を 4 回で合計 12。幅はグリッドが面倒を見る。A4 を Letter に変えても、マージンを縮めても、このコードは ",[31,2211,2212],{},"pageWidth"," に一切依存していないので並びは正しいまま。1 列目だけ他より 2 倍広くしたければ、その列を ",[31,2215,2216],{},"r.Col(6, ...)","、他のうち 1 つを ",[31,2219,2220],{},"r.Col(2, ...)"," にすればいい。算術なし。",[14,2223,2225],{"id":2224},"before-after-3-改ページする請求書テーブル","Before / After 3: 改ページする請求書テーブル",[19,2227,2228,2229,2231],{},"本丸。gopdf で複数ページにまたがるテーブルを描くのはほぼ全部簿記: 現在の y を追い、各行を描き、次の行が収まるかチェックし、収まらなければ ",[31,2230,55],{}," を呼んでヘッダを再描画。状態機械があなたのコードに住む。",[19,2233,2234],{},[22,2235,514],{},[516,2237,2239],{"className":518,"code":2238,"language":520,"meta":521,"style":521},"func drawInvoiceTable(pdf *gopdf.GoPdf, items [][4]string) error {\n    const (\n        pageH       = 841.89 // A4 height\n        bottomLimit = pageH - 40\n        rowH        = 22.0\n        leftX       = 40.0\n    )\n    cols := []float64{260, 80, 80, 95} // 明細・数量・単価・金額\n\n    // 1 ページ目と改ページ後に呼ぶヘッダ描画関数。\n    drawHeader := func(y float64) float64 {\n        pdf.SetFont(\"helvetica-bold\", \"\", 11)\n        pdf.SetFillColor(26, 35, 126)\n        pdf.SetTextColor(255, 255, 255)\n        x := leftX\n        for i, h := range []string{\"明細\", \"数量\", \"単価\", \"金額\"} {\n            pdf.RectFromUpperLeftWithStyle(x, y, cols[i], rowH, \"F\")\n            pdf.SetXY(x+6, y+7)\n            if err := pdf.Cell(nil, h); err != nil {\n                log.Println(err)\n            }\n            x += cols[i]\n        }\n        pdf.SetTextColor(0, 0, 0)\n        pdf.SetFont(\"helvetica\", \"\", 11)\n        return y + rowH\n    }\n\n    y := drawHeader(100)\n    for _, row := range items {\n        if y+rowH > bottomLimit {\n            pdf.AddPage()\n            y = drawHeader(60)\n        }\n\n        x := leftX\n        for i, cell := range row {\n            pdf.RectFromUpperLeftWithStyle(x, y, cols[i], rowH, \"D\") // 罫線のみ\n            pdf.SetXY(x+6, y+7)\n            if err := pdf.Cell(nil, cell); err != nil {\n                return err\n            }\n            x += cols[i]\n        }\n        y += rowH\n    }\n    return nil\n}\n",[31,2240,2241,2283,2290,2303,2318,2328,2337,2341,2377,2381,2386,2407,2434,2456,2478,2488,2543,2586,2612,2641,2657,2662,2679,2684,2706,2732,2745,2749,2753,2770,2790,2810,2820,2837,2842,2847,2856,2876,2919,2946,2976,2985,2990,3005,3010,3020,3025,3034],{"__ignoreMap":521},[525,2242,2243,2245,2248,2250,2252,2254,2256,2258,2260,2262,2265,2268,2271,2274,2276,2278,2281],{"class":527,"line":528},[525,2244,597],{"class":531},[525,2246,2247],{"class":600}," drawInvoiceTable",[525,2249,643],{"class":531},[525,2251,1575],{"class":1202},[525,2253,663],{"class":531},[525,2255,646],{"class":535},[525,2257,623],{"class":531},[525,2259,626],{"class":535},[525,2261,723],{"class":531},[525,2263,2264],{"class":1202}," items",[525,2266,2267],{"class":531}," [][",[525,2269,2270],{"class":802},"4",[525,2272,2273],{"class":531},"]",[525,2275,1668],{"class":1667},[525,2277,273],{"class":531},[525,2279,2280],{"class":1667}," error",[525,2282,607],{"class":531},[525,2284,2285,2288],{"class":527,"line":539},[525,2286,2287],{"class":531},"    const",[525,2289,553],{"class":531},[525,2291,2292,2295,2297,2300],{"class":527,"line":546},[525,2293,2294],{"class":613},"        pageH       ",[525,2296,1469],{"class":531},[525,2298,2299],{"class":802}," 841.89",[525,2301,2302],{"class":1475}," // A4 height\n",[525,2304,2305,2308,2310,2313,2315],{"class":527,"line":556},[525,2306,2307],{"class":613},"        bottomLimit ",[525,2309,1469],{"class":531},[525,2311,2312],{"class":613}," pageH ",[525,2314,1536],{"class":531},[525,2316,2317],{"class":802}," 40\n",[525,2319,2320,2323,2325],{"class":527,"line":568},[525,2321,2322],{"class":613},"        rowH        ",[525,2324,1469],{"class":531},[525,2326,2327],{"class":802}," 22.0\n",[525,2329,2330,2333,2335],{"class":527,"line":573},[525,2331,2332],{"class":613},"        leftX       ",[525,2334,1469],{"class":531},[525,2336,1486],{"class":802},[525,2338,2339],{"class":527,"line":583},[525,2340,1166],{"class":531},[525,2342,2343,2346,2348,2350,2352,2354,2357,2359,2362,2364,2366,2368,2371,2374],{"class":527,"line":589},[525,2344,2345],{"class":613},"    cols ",[525,2347,617],{"class":531},[525,2349,1664],{"class":531},[525,2351,1751],{"class":1667},[525,2353,654],{"class":531},[525,2355,2356],{"class":802},"260",[525,2358,723],{"class":531},[525,2360,2361],{"class":802}," 80",[525,2363,723],{"class":531},[525,2365,2361],{"class":802},[525,2367,723],{"class":531},[525,2369,2370],{"class":802}," 95",[525,2372,2373],{"class":531},"}",[525,2375,2376],{"class":1475}," // 明細・数量・単価・金額\n",[525,2378,2379],{"class":527,"line":594},[525,2380,543],{"emptyLinePlaceholder":542},[525,2382,2383],{"class":527,"line":610},[525,2384,2385],{"class":1475},"    // 1 ページ目と改ページ後に呼ぶヘッダ描画関数。\n",[525,2387,2388,2391,2393,2395,2398,2401,2403,2405],{"class":527,"line":632},[525,2389,2390],{"class":613},"    drawHeader ",[525,2392,617],{"class":531},[525,2394,1237],{"class":531},[525,2396,2397],{"class":1202},"y",[525,2399,2400],{"class":1667}," float64",[525,2402,273],{"class":531},[525,2404,2400],{"class":1667},[525,2406,607],{"class":531},[525,2408,2409,2412,2414,2416,2418,2420,2422,2424,2426,2428,2430,2432],{"class":527,"line":676},[525,2410,2411],{"class":613},"        pdf",[525,2413,623],{"class":531},[525,2415,784],{"class":600},[525,2417,643],{"class":531},[525,2419,714],{"class":531},[525,2421,1586],{"class":717},[525,2423,714],{"class":531},[525,2425,723],{"class":531},[525,2427,797],{"class":531},[525,2429,723],{"class":531},[525,2431,1597],{"class":802},[525,2433,586],{"class":531},[525,2435,2436,2438,2440,2442,2444,2446,2448,2450,2452,2454],{"class":527,"line":688},[525,2437,2411],{"class":613},[525,2439,623],{"class":531},[525,2441,1608],{"class":600},[525,2443,643],{"class":531},[525,2445,1613],{"class":802},[525,2447,723],{"class":531},[525,2449,1618],{"class":802},[525,2451,723],{"class":531},[525,2453,1623],{"class":802},[525,2455,586],{"class":531},[525,2457,2458,2460,2462,2464,2466,2468,2470,2472,2474,2476],{"class":527,"line":693},[525,2459,2411],{"class":613},[525,2461,623],{"class":531},[525,2463,1634],{"class":600},[525,2465,643],{"class":531},[525,2467,1639],{"class":802},[525,2469,723],{"class":531},[525,2471,1644],{"class":802},[525,2473,723],{"class":531},[525,2475,1644],{"class":802},[525,2477,586],{"class":531},[525,2479,2480,2483,2485],{"class":527,"line":747},[525,2481,2482],{"class":613},"        x ",[525,2484,617],{"class":531},[525,2486,2487],{"class":613}," leftX\n",[525,2489,2490,2493,2495,2497,2499,2501,2503,2505,2507,2509,2511,2513,2515,2517,2519,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539,2541],{"class":527,"line":765},[525,2491,2492],{"class":549},"        for",[525,2494,1714],{"class":613},[525,2496,723],{"class":531},[525,2498,1719],{"class":613},[525,2500,617],{"class":531},[525,2502,1724],{"class":549},[525,2504,1664],{"class":531},[525,2506,1668],{"class":1667},[525,2508,654],{"class":531},[525,2510,714],{"class":531},[525,2512,1675],{"class":717},[525,2514,714],{"class":531},[525,2516,723],{"class":531},[525,2518,726],{"class":531},[525,2520,1684],{"class":717},[525,2522,714],{"class":531},[525,2524,723],{"class":531},[525,2526,726],{"class":531},[525,2528,1693],{"class":717},[525,2530,714],{"class":531},[525,2532,723],{"class":531},[525,2534,726],{"class":531},[525,2536,1702],{"class":717},[525,2538,714],{"class":531},[525,2540,2373],{"class":531},[525,2542,607],{"class":531},[525,2544,2545,2548,2550,2552,2554,2556,2558,2561,2563,2566,2569,2571,2574,2576,2578,2580,2582,2584],{"class":527,"line":771},[525,2546,2547],{"class":613},"            pdf",[525,2549,623],{"class":531},[525,2551,1767],{"class":600},[525,2553,643],{"class":531},[525,2555,1772],{"class":613},[525,2557,723],{"class":531},[525,2559,2560],{"class":613}," y",[525,2562,723],{"class":531},[525,2564,2565],{"class":613}," cols",[525,2567,2568],{"class":531},"[",[525,2570,1756],{"class":613},[525,2572,2573],{"class":531},"],",[525,2575,1786],{"class":613},[525,2577,723],{"class":531},[525,2579,726],{"class":531},[525,2581,1793],{"class":717},[525,2583,714],{"class":531},[525,2585,586],{"class":531},[525,2587,2588,2590,2592,2594,2596,2598,2600,2602,2604,2606,2608,2610],{"class":527,"line":816},[525,2589,2547],{"class":613},[525,2591,623],{"class":531},[525,2593,33],{"class":600},[525,2595,643],{"class":531},[525,2597,1772],{"class":613},[525,2599,1742],{"class":531},[525,2601,1818],{"class":802},[525,2603,723],{"class":531},[525,2605,2560],{"class":613},[525,2607,1742],{"class":531},[525,2609,1827],{"class":802},[525,2611,586],{"class":531},[525,2613,2614,2617,2619,2621,2623,2625,2627,2629,2631,2633,2635,2637,2639],{"class":527,"line":831},[525,2615,2616],{"class":549},"            if",[525,2618,699],{"class":613},[525,2620,617],{"class":531},[525,2622,704],{"class":613},[525,2624,623],{"class":531},[525,2626,37],{"class":600},[525,2628,890],{"class":531},[525,2630,1848],{"class":613},[525,2632,734],{"class":531},[525,2634,699],{"class":613},[525,2636,739],{"class":531},[525,2638,742],{"class":531},[525,2640,607],{"class":531},[525,2642,2643,2646,2648,2651,2653,2655],{"class":527,"line":836},[525,2644,2645],{"class":613},"                log",[525,2647,623],{"class":531},[525,2649,2650],{"class":600},"Println",[525,2652,643],{"class":531},[525,2654,760],{"class":613},[525,2656,586],{"class":531},[525,2658,2659],{"class":527,"line":841},[525,2660,2661],{"class":531},"            }\n",[525,2663,2664,2667,2670,2672,2674,2676],{"class":527,"line":858},[525,2665,2666],{"class":613},"            x ",[525,2668,2669],{"class":531},"+=",[525,2671,2565],{"class":613},[525,2673,2568],{"class":531},[525,2675,1756],{"class":613},[525,2677,2678],{"class":531},"]\n",[525,2680,2681],{"class":527,"line":875},[525,2682,2683],{"class":531},"        }\n",[525,2685,2686,2688,2690,2692,2694,2696,2698,2700,2702,2704],{"class":527,"line":910},[525,2687,2411],{"class":613},[525,2689,623],{"class":531},[525,2691,1634],{"class":600},[525,2693,643],{"class":531},[525,2695,1897],{"class":802},[525,2697,723],{"class":531},[525,2699,1902],{"class":802},[525,2701,723],{"class":531},[525,2703,1902],{"class":802},[525,2705,586],{"class":531},[525,2707,2708,2710,2712,2714,2716,2718,2720,2722,2724,2726,2728,2730],{"class":527,"line":925},[525,2709,2411],{"class":613},[525,2711,623],{"class":531},[525,2713,784],{"class":600},[525,2715,643],{"class":531},[525,2717,714],{"class":531},[525,2719,718],{"class":717},[525,2721,714],{"class":531},[525,2723,723],{"class":531},[525,2725,797],{"class":531},[525,2727,723],{"class":531},[525,2729,1597],{"class":802},[525,2731,586],{"class":531},[525,2733,2734,2737,2740,2742],{"class":527,"line":930},[525,2735,2736],{"class":549},"        return",[525,2738,2739],{"class":613}," y ",[525,2741,1742],{"class":531},[525,2743,2744],{"class":613}," rowH\n",[525,2746,2747],{"class":527,"line":935},[525,2748,768],{"class":531},[525,2750,2751],{"class":527,"line":970},[525,2752,543],{"emptyLinePlaceholder":542},[525,2754,2755,2758,2760,2763,2765,2768],{"class":527,"line":985},[525,2756,2757],{"class":613},"    y ",[525,2759,617],{"class":531},[525,2761,2762],{"class":600}," drawHeader",[525,2764,643],{"class":531},[525,2766,2767],{"class":802},"100",[525,2769,586],{"class":531},[525,2771,2772,2774,2776,2778,2781,2783,2785,2788],{"class":527,"line":990},[525,2773,2002],{"class":549},[525,2775,2005],{"class":613},[525,2777,723],{"class":531},[525,2779,2780],{"class":613}," row ",[525,2782,617],{"class":531},[525,2784,1724],{"class":549},[525,2786,2787],{"class":613}," items ",[525,2789,1730],{"class":531},[525,2791,2792,2795,2797,2799,2802,2805,2808],{"class":527,"line":1424},[525,2793,2794],{"class":549},"        if",[525,2796,2560],{"class":613},[525,2798,1742],{"class":531},[525,2800,2801],{"class":613},"rowH ",[525,2803,2804],{"class":531},">",[525,2806,2807],{"class":613}," bottomLimit ",[525,2809,1730],{"class":531},[525,2811,2812,2814,2816,2818],{"class":527,"line":1429},[525,2813,2547],{"class":613},[525,2815,623],{"class":531},[525,2817,55],{"class":600},[525,2819,685],{"class":531},[525,2821,2823,2826,2828,2830,2832,2835],{"class":527,"line":2822},33,[525,2824,2825],{"class":613},"            y ",[525,2827,1469],{"class":531},[525,2829,2762],{"class":600},[525,2831,643],{"class":531},[525,2833,2834],{"class":802},"60",[525,2836,586],{"class":531},[525,2838,2840],{"class":527,"line":2839},34,[525,2841,2683],{"class":531},[525,2843,2845],{"class":527,"line":2844},35,[525,2846,543],{"emptyLinePlaceholder":542},[525,2848,2850,2852,2854],{"class":527,"line":2849},36,[525,2851,2482],{"class":613},[525,2853,617],{"class":531},[525,2855,2487],{"class":613},[525,2857,2859,2861,2863,2865,2868,2870,2872,2874],{"class":527,"line":2858},37,[525,2860,2492],{"class":549},[525,2862,1714],{"class":613},[525,2864,723],{"class":531},[525,2866,2867],{"class":613}," cell ",[525,2869,617],{"class":531},[525,2871,1724],{"class":549},[525,2873,2780],{"class":613},[525,2875,1730],{"class":531},[525,2877,2879,2881,2883,2885,2887,2889,2891,2893,2895,2897,2899,2901,2903,2905,2907,2909,2912,2914,2916],{"class":527,"line":2878},38,[525,2880,2547],{"class":613},[525,2882,623],{"class":531},[525,2884,1767],{"class":600},[525,2886,643],{"class":531},[525,2888,1772],{"class":613},[525,2890,723],{"class":531},[525,2892,2560],{"class":613},[525,2894,723],{"class":531},[525,2896,2565],{"class":613},[525,2898,2568],{"class":531},[525,2900,1756],{"class":613},[525,2902,2573],{"class":531},[525,2904,1786],{"class":613},[525,2906,723],{"class":531},[525,2908,726],{"class":531},[525,2910,2911],{"class":717},"D",[525,2913,714],{"class":531},[525,2915,273],{"class":531},[525,2917,2918],{"class":1475}," // 罫線のみ\n",[525,2920,2922,2924,2926,2928,2930,2932,2934,2936,2938,2940,2942,2944],{"class":527,"line":2921},39,[525,2923,2547],{"class":613},[525,2925,623],{"class":531},[525,2927,33],{"class":600},[525,2929,643],{"class":531},[525,2931,1772],{"class":613},[525,2933,1742],{"class":531},[525,2935,1818],{"class":802},[525,2937,723],{"class":531},[525,2939,2560],{"class":613},[525,2941,1742],{"class":531},[525,2943,1827],{"class":802},[525,2945,586],{"class":531},[525,2947,2949,2951,2953,2955,2957,2959,2961,2963,2966,2968,2970,2972,2974],{"class":527,"line":2948},40,[525,2950,2616],{"class":549},[525,2952,699],{"class":613},[525,2954,617],{"class":531},[525,2956,704],{"class":613},[525,2958,623],{"class":531},[525,2960,37],{"class":600},[525,2962,890],{"class":531},[525,2964,2965],{"class":613}," cell",[525,2967,734],{"class":531},[525,2969,699],{"class":613},[525,2971,739],{"class":531},[525,2973,742],{"class":531},[525,2975,607],{"class":531},[525,2977,2979,2982],{"class":527,"line":2978},41,[525,2980,2981],{"class":549},"                return",[525,2983,2984],{"class":613}," err\n",[525,2986,2988],{"class":527,"line":2987},42,[525,2989,2661],{"class":531},[525,2991,2993,2995,2997,2999,3001,3003],{"class":527,"line":2992},43,[525,2994,2666],{"class":613},[525,2996,2669],{"class":531},[525,2998,2565],{"class":613},[525,3000,2568],{"class":531},[525,3002,1756],{"class":613},[525,3004,2678],{"class":531},[525,3006,3008],{"class":527,"line":3007},44,[525,3009,2683],{"class":531},[525,3011,3013,3016,3018],{"class":527,"line":3012},45,[525,3014,3015],{"class":613},"        y ",[525,3017,2669],{"class":531},[525,3019,2744],{"class":613},[525,3021,3023],{"class":527,"line":3022},46,[525,3024,768],{"class":531},[525,3026,3028,3031],{"class":527,"line":3027},47,[525,3029,3030],{"class":549},"    return",[525,3032,3033],{"class":531}," nil\n",[525,3035,3037],{"class":527,"line":3036},48,[525,3038,993],{"class":531},[19,3040,3041,3042,3044],{},"このテーブル描画関数は 30 行、そのうちデータに関するのは 5 行。残りはレイアウト: ハードコードされた高さ、ハードコードされた下限、改ページ後のヘッダ再描画クロージャ、",[31,3043,1711],{}," ループ 2 つ、セルあたりカーソル 2 回更新。これが gopdf テーブルの中央値。",[19,3046,3047],{},[22,3048,998],{},[516,3050,3052],{"className":518,"code":3051,"language":520,"meta":521,"style":521},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"明細\", \"数量\", \"単価\", \"金額\"},\n            items, // [][]string\n            template.ColumnWidths(55, 15, 15, 15),\n            template.TableHeaderStyle(\n                template.Bold(),\n                template.TextColor(pdf.White),\n                template.BgColor(pdf.RGBHex(0x1A237E)),\n            ),\n            template.TableStripe(pdf.RGBHex(0xF5F5F5)),\n        )\n    })\n})\n",[31,3053,3054,3078,3109,3121,3163,3173,3203,3214,3225,3243,3265,3270,3294,3299,3303],{"__ignoreMap":521},[525,3055,3056,3058,3060,3062,3064,3066,3068,3070,3072,3074,3076],{"class":527,"line":528},[525,3057,1932],{"class":613},[525,3059,623],{"class":531},[525,3061,1196],{"class":600},[525,3063,1199],{"class":531},[525,3065,1203],{"class":1202},[525,3067,663],{"class":531},[525,3069,1208],{"class":535},[525,3071,623],{"class":531},[525,3073,1213],{"class":535},[525,3075,273],{"class":531},[525,3077,607],{"class":531},[525,3079,3080,3083,3085,3087,3089,3091,3093,3095,3097,3099,3101,3103,3105,3107],{"class":527,"line":539},[525,3081,3082],{"class":613},"    r",[525,3084,623],{"class":531},[525,3086,1227],{"class":600},[525,3088,643],{"class":531},[525,3090,1232],{"class":802},[525,3092,723],{"class":531},[525,3094,1237],{"class":531},[525,3096,1240],{"class":1202},[525,3098,663],{"class":531},[525,3100,1208],{"class":535},[525,3102,623],{"class":531},[525,3104,1249],{"class":535},[525,3106,273],{"class":531},[525,3108,607],{"class":531},[525,3110,3111,3114,3116,3119],{"class":527,"line":546},[525,3112,3113],{"class":613},"        c",[525,3115,623],{"class":531},[525,3117,3118],{"class":600},"Table",[525,3120,1103],{"class":531},[525,3122,3123,3126,3128,3130,3132,3134,3136,3138,3140,3142,3144,3146,3148,3150,3152,3154,3156,3158,3160],{"class":527,"line":556},[525,3124,3125],{"class":531},"            []",[525,3127,1668],{"class":1667},[525,3129,654],{"class":531},[525,3131,714],{"class":531},[525,3133,1675],{"class":717},[525,3135,714],{"class":531},[525,3137,723],{"class":531},[525,3139,726],{"class":531},[525,3141,1684],{"class":717},[525,3143,714],{"class":531},[525,3145,723],{"class":531},[525,3147,726],{"class":531},[525,3149,1693],{"class":717},[525,3151,714],{"class":531},[525,3153,723],{"class":531},[525,3155,726],{"class":531},[525,3157,1702],{"class":717},[525,3159,714],{"class":531},[525,3161,3162],{"class":531},"},\n",[525,3164,3165,3168,3170],{"class":527,"line":568},[525,3166,3167],{"class":613},"            items",[525,3169,723],{"class":531},[525,3171,3172],{"class":1475}," // [][]string\n",[525,3174,3175,3178,3180,3183,3185,3188,3190,3193,3195,3197,3199,3201],{"class":527,"line":573},[525,3176,3177],{"class":613},"            template",[525,3179,623],{"class":531},[525,3181,3182],{"class":600},"ColumnWidths",[525,3184,643],{"class":531},[525,3186,3187],{"class":802},"55",[525,3189,723],{"class":531},[525,3191,3192],{"class":802}," 15",[525,3194,723],{"class":531},[525,3196,3192],{"class":802},[525,3198,723],{"class":531},[525,3200,3192],{"class":802},[525,3202,1126],{"class":531},[525,3204,3205,3207,3209,3212],{"class":527,"line":583},[525,3206,3177],{"class":613},[525,3208,623],{"class":531},[525,3210,3211],{"class":600},"TableHeaderStyle",[525,3213,1103],{"class":531},[525,3215,3216,3218,3220,3222],{"class":527,"line":589},[525,3217,2064],{"class":613},[525,3219,623],{"class":531},[525,3221,1296],{"class":600},[525,3223,3224],{"class":531},"(),\n",[525,3226,3227,3229,3231,3233,3235,3237,3239,3241],{"class":527,"line":594},[525,3228,2064],{"class":613},[525,3230,623],{"class":531},[525,3232,2177],{"class":600},[525,3234,643],{"class":531},[525,3236,1575],{"class":613},[525,3238,623],{"class":531},[525,3240,2186],{"class":613},[525,3242,1126],{"class":531},[525,3244,3245,3247,3249,3251,3253,3255,3257,3259,3261,3263],{"class":527,"line":610},[525,3246,2064],{"class":613},[525,3248,623],{"class":531},[525,3250,2069],{"class":600},[525,3252,643],{"class":531},[525,3254,1575],{"class":613},[525,3256,623],{"class":531},[525,3258,2078],{"class":600},[525,3260,643],{"class":531},[525,3262,2083],{"class":802},[525,3264,2086],{"class":531},[525,3266,3267],{"class":527,"line":632},[525,3268,3269],{"class":531},"            ),\n",[525,3271,3272,3274,3276,3279,3281,3283,3285,3287,3289,3292],{"class":527,"line":676},[525,3273,3177],{"class":613},[525,3275,623],{"class":531},[525,3277,3278],{"class":600},"TableStripe",[525,3280,643],{"class":531},[525,3282,1575],{"class":613},[525,3284,623],{"class":531},[525,3286,2078],{"class":600},[525,3288,643],{"class":531},[525,3290,3291],{"class":802},"0xF5F5F5",[525,3293,2086],{"class":531},[525,3295,3296],{"class":527,"line":688},[525,3297,3298],{"class":531},"        )\n",[525,3300,3301],{"class":527,"line":693},[525,3302,1309],{"class":531},[525,3304,3305],{"class":527,"line":747},[525,3306,673],{"class":531},[19,3308,3309,3310,3312],{},"これだけ。改ページ自動。本体が継続するページではヘッダが自動で繰り返される。ストライプ行はオプション 1 つ。列幅はコンテナのパーセンテージなので、同じテーブルを ",[31,3311,2216],{}," の中に入れれば比率を保ったまま半分のサイズになり書き直しは不要。25 行の gopdf 簿記関数は消える。",[19,3314,3315,3316,3319,3320,3323],{},"具体的な数字を 1 つ。100 行の請求書レンダリングは gpdf で ",[22,3317,3318],{},"108 µs","、signintech/gopdf でだいたい ",[22,3321,3322],{},"2.4 ms"," — gopdf 側はあなたが書いたセル単位パターンに依存するので変動する。倍率が見出しなのではなく、関数そのものが消えたことが見出し。",[14,3325,3327],{"id":3326},"before-after-4-段落の隣に画像","Before / After 4: 段落の隣に画像",[19,3329,3330],{},"よくある形: 左にロゴ、右に住所ブロック。",[19,3332,3333],{},[22,3334,514],{},[516,3336,3338],{"className":518,"code":3337,"language":520,"meta":521,"style":521},"const (\n    leftX  = 40.0\n    rightX = 380.0\n    blockY = 50.0\n)\n\nif err := pdf.Image(\"logo.png\", leftX, blockY, &gopdf.Rect{W: 100, H: 60}); err != nil {\n    log.Fatal(err)\n}\n\npdf.SetFont(\"helvetica-bold\", \"\", 14)\npdf.SetXY(rightX, blockY)\nif err := pdf.Cell(nil, \"ACME 株式会社\"); err != nil {\n    log.Fatal(err)\n}\n\npdf.SetFont(\"helvetica\", \"\", 10)\npdf.SetXY(rightX, blockY+20)\npdf.Cell(nil, \"東京都渋谷区神宮前 1-2-3\")\npdf.SetXY(rightX, blockY+34)\npdf.Cell(nil, \"150-0001\")\npdf.SetXY(rightX, blockY+48)\npdf.Cell(nil, \"billing@acme.example\")\n",[31,3339,3340,3346,3355,3365,3375,3379,3383,3461,3476,3480,3484,3511,3530,3563,3577,3581,3585,3612,3634,3653,3676,3695,3718],{"__ignoreMap":521},[525,3341,3342,3344],{"class":527,"line":528},[525,3343,1459],{"class":531},[525,3345,553],{"class":531},[525,3347,3348,3351,3353],{"class":527,"line":539},[525,3349,3350],{"class":613},"    leftX  ",[525,3352,1469],{"class":531},[525,3354,1486],{"class":802},[525,3356,3357,3360,3362],{"class":527,"line":546},[525,3358,3359],{"class":613},"    rightX ",[525,3361,1469],{"class":531},[525,3363,3364],{"class":802}," 380.0\n",[525,3366,3367,3370,3372],{"class":527,"line":556},[525,3368,3369],{"class":613},"    blockY ",[525,3371,1469],{"class":531},[525,3373,3374],{"class":802}," 50.0\n",[525,3376,3377],{"class":527,"line":568},[525,3378,586],{"class":531},[525,3380,3381],{"class":527,"line":573},[525,3382,543],{"emptyLinePlaceholder":542},[525,3384,3385,3388,3390,3392,3394,3396,3399,3401,3403,3406,3408,3410,3413,3415,3418,3420,3423,3425,3427,3430,3432,3435,3437,3440,3442,3445,3447,3450,3453,3455,3457,3459],{"class":527,"line":583},[525,3386,3387],{"class":549},"if",[525,3389,699],{"class":613},[525,3391,617],{"class":531},[525,3393,704],{"class":613},[525,3395,623],{"class":531},[525,3397,3398],{"class":600},"Image",[525,3400,643],{"class":531},[525,3402,714],{"class":531},[525,3404,3405],{"class":717},"logo.png",[525,3407,714],{"class":531},[525,3409,723],{"class":531},[525,3411,3412],{"class":613}," leftX",[525,3414,723],{"class":531},[525,3416,3417],{"class":613}," blockY",[525,3419,723],{"class":531},[525,3421,3422],{"class":531}," &",[525,3424,646],{"class":535},[525,3426,623],{"class":531},[525,3428,3429],{"class":535},"Rect",[525,3431,654],{"class":531},[525,3433,3434],{"class":613},"W",[525,3436,660],{"class":531},[525,3438,3439],{"class":802}," 100",[525,3441,723],{"class":531},[525,3443,3444],{"class":613}," H",[525,3446,660],{"class":531},[525,3448,3449],{"class":802}," 60",[525,3451,3452],{"class":531},"});",[525,3454,699],{"class":613},[525,3456,739],{"class":531},[525,3458,742],{"class":531},[525,3460,607],{"class":531},[525,3462,3463,3466,3468,3470,3472,3474],{"class":527,"line":589},[525,3464,3465],{"class":613},"    log",[525,3467,623],{"class":531},[525,3469,755],{"class":600},[525,3471,643],{"class":531},[525,3473,760],{"class":613},[525,3475,586],{"class":531},[525,3477,3478],{"class":527,"line":594},[525,3479,993],{"class":531},[525,3481,3482],{"class":527,"line":610},[525,3483,543],{"emptyLinePlaceholder":542},[525,3485,3486,3488,3490,3492,3494,3496,3498,3500,3502,3504,3506,3509],{"class":527,"line":632},[525,3487,1575],{"class":613},[525,3489,623],{"class":531},[525,3491,784],{"class":600},[525,3493,643],{"class":531},[525,3495,714],{"class":531},[525,3497,1586],{"class":717},[525,3499,714],{"class":531},[525,3501,723],{"class":531},[525,3503,797],{"class":531},[525,3505,723],{"class":531},[525,3507,3508],{"class":802}," 14",[525,3510,586],{"class":531},[525,3512,3513,3515,3517,3519,3521,3524,3526,3528],{"class":527,"line":676},[525,3514,1575],{"class":613},[525,3516,623],{"class":531},[525,3518,33],{"class":600},[525,3520,643],{"class":531},[525,3522,3523],{"class":613},"rightX",[525,3525,723],{"class":531},[525,3527,3417],{"class":613},[525,3529,586],{"class":531},[525,3531,3532,3534,3536,3538,3540,3542,3544,3546,3548,3551,3553,3555,3557,3559,3561],{"class":527,"line":688},[525,3533,3387],{"class":549},[525,3535,699],{"class":613},[525,3537,617],{"class":531},[525,3539,704],{"class":613},[525,3541,623],{"class":531},[525,3543,37],{"class":600},[525,3545,890],{"class":531},[525,3547,726],{"class":531},[525,3549,3550],{"class":717},"ACME 株式会社",[525,3552,714],{"class":531},[525,3554,734],{"class":531},[525,3556,699],{"class":613},[525,3558,739],{"class":531},[525,3560,742],{"class":531},[525,3562,607],{"class":531},[525,3564,3565,3567,3569,3571,3573,3575],{"class":527,"line":693},[525,3566,3465],{"class":613},[525,3568,623],{"class":531},[525,3570,755],{"class":600},[525,3572,643],{"class":531},[525,3574,760],{"class":613},[525,3576,586],{"class":531},[525,3578,3579],{"class":527,"line":747},[525,3580,993],{"class":531},[525,3582,3583],{"class":527,"line":765},[525,3584,543],{"emptyLinePlaceholder":542},[525,3586,3587,3589,3591,3593,3595,3597,3599,3601,3603,3605,3607,3610],{"class":527,"line":771},[525,3588,1575],{"class":613},[525,3590,623],{"class":531},[525,3592,784],{"class":600},[525,3594,643],{"class":531},[525,3596,714],{"class":531},[525,3598,718],{"class":717},[525,3600,714],{"class":531},[525,3602,723],{"class":531},[525,3604,797],{"class":531},[525,3606,723],{"class":531},[525,3608,3609],{"class":802}," 10",[525,3611,586],{"class":531},[525,3613,3614,3616,3618,3620,3622,3624,3626,3628,3630,3632],{"class":527,"line":816},[525,3615,1575],{"class":613},[525,3617,623],{"class":531},[525,3619,33],{"class":600},[525,3621,643],{"class":531},[525,3623,3523],{"class":613},[525,3625,723],{"class":531},[525,3627,3417],{"class":613},[525,3629,1742],{"class":531},[525,3631,1158],{"class":802},[525,3633,586],{"class":531},[525,3635,3636,3638,3640,3642,3644,3646,3649,3651],{"class":527,"line":831},[525,3637,1575],{"class":613},[525,3639,623],{"class":531},[525,3641,37],{"class":600},[525,3643,890],{"class":531},[525,3645,726],{"class":531},[525,3647,3648],{"class":717},"東京都渋谷区神宮前 1-2-3",[525,3650,714],{"class":531},[525,3652,586],{"class":531},[525,3654,3655,3657,3659,3661,3663,3665,3667,3669,3671,3674],{"class":527,"line":836},[525,3656,1575],{"class":613},[525,3658,623],{"class":531},[525,3660,33],{"class":600},[525,3662,643],{"class":531},[525,3664,3523],{"class":613},[525,3666,723],{"class":531},[525,3668,3417],{"class":613},[525,3670,1742],{"class":531},[525,3672,3673],{"class":802},"34",[525,3675,586],{"class":531},[525,3677,3678,3680,3682,3684,3686,3688,3691,3693],{"class":527,"line":841},[525,3679,1575],{"class":613},[525,3681,623],{"class":531},[525,3683,37],{"class":600},[525,3685,890],{"class":531},[525,3687,726],{"class":531},[525,3689,3690],{"class":717},"150-0001",[525,3692,714],{"class":531},[525,3694,586],{"class":531},[525,3696,3697,3699,3701,3703,3705,3707,3709,3711,3713,3716],{"class":527,"line":858},[525,3698,1575],{"class":613},[525,3700,623],{"class":531},[525,3702,33],{"class":600},[525,3704,643],{"class":531},[525,3706,3523],{"class":613},[525,3708,723],{"class":531},[525,3710,3417],{"class":613},[525,3712,1742],{"class":531},[525,3714,3715],{"class":802},"48",[525,3717,586],{"class":531},[525,3719,3720,3722,3724,3726,3728,3730,3733,3735],{"class":527,"line":875},[525,3721,1575],{"class":613},[525,3723,623],{"class":531},[525,3725,37],{"class":600},[525,3727,890],{"class":531},[525,3729,726],{"class":531},[525,3731,3732],{"class":717},"billing@acme.example",[525,3734,714],{"class":531},[525,3736,586],{"class":531},[19,3738,3739,3740,3743],{},"明示的な y 座標が 6 個、右ブロックは ",[31,3741,3742],{},"rightX = 380"," から始まる — 誰かがロゴを 100 幅と決め、右ブロックは 240 ピクセル空けると決めた結果の数値。ロゴを右に動かすと数値全部が変わる。",[19,3745,3746],{},[22,3747,998],{},[516,3749,3751],{"className":518,"code":3750,"language":520,"meta":521,"style":521},"//go:embed logo.png\nvar logoData []byte\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Image(logoData, template.FitWidth(document.Mm(35)))\n    })\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"ACME 株式会社\", template.Bold(), template.FontSize(14))\n        c.Text(\"東京都渋谷区神宮前 1-2-3\")\n        c.Text(\"150-0001\")\n        c.Text(\"billing@acme.example\")\n    })\n})\n",[31,3752,3753,3758,3772,3776,3800,3830,3868,3872,3903,3943,3961,3979,3997,4001],{"__ignoreMap":521},[525,3754,3755],{"class":527,"line":528},[525,3756,3757],{"class":1475},"//go:embed logo.png\n",[525,3759,3760,3763,3766,3769],{"class":527,"line":539},[525,3761,3762],{"class":531},"var",[525,3764,3765],{"class":613}," logoData ",[525,3767,3768],{"class":531},"[]",[525,3770,3771],{"class":1667},"byte\n",[525,3773,3774],{"class":527,"line":546},[525,3775,543],{"emptyLinePlaceholder":542},[525,3777,3778,3780,3782,3784,3786,3788,3790,3792,3794,3796,3798],{"class":527,"line":556},[525,3779,1932],{"class":613},[525,3781,623],{"class":531},[525,3783,1196],{"class":600},[525,3785,1199],{"class":531},[525,3787,1203],{"class":1202},[525,3789,663],{"class":531},[525,3791,1208],{"class":535},[525,3793,623],{"class":531},[525,3795,1213],{"class":535},[525,3797,273],{"class":531},[525,3799,607],{"class":531},[525,3801,3802,3804,3806,3808,3810,3812,3814,3816,3818,3820,3822,3824,3826,3828],{"class":527,"line":568},[525,3803,3082],{"class":613},[525,3805,623],{"class":531},[525,3807,1227],{"class":600},[525,3809,643],{"class":531},[525,3811,2270],{"class":802},[525,3813,723],{"class":531},[525,3815,1237],{"class":531},[525,3817,1240],{"class":1202},[525,3819,663],{"class":531},[525,3821,1208],{"class":535},[525,3823,623],{"class":531},[525,3825,1249],{"class":535},[525,3827,273],{"class":531},[525,3829,607],{"class":531},[525,3831,3832,3834,3836,3838,3840,3843,3845,3847,3849,3852,3854,3856,3858,3860,3862,3865],{"class":527,"line":573},[525,3833,3113],{"class":613},[525,3835,623],{"class":531},[525,3837,3398],{"class":600},[525,3839,643],{"class":531},[525,3841,3842],{"class":613},"logoData",[525,3844,723],{"class":531},[525,3846,1276],{"class":613},[525,3848,623],{"class":531},[525,3850,3851],{"class":600},"FitWidth",[525,3853,643],{"class":531},[525,3855,1118],{"class":613},[525,3857,623],{"class":531},[525,3859,1153],{"class":600},[525,3861,643],{"class":531},[525,3863,3864],{"class":802},"35",[525,3866,3867],{"class":531},")))\n",[525,3869,3870],{"class":527,"line":583},[525,3871,1309],{"class":531},[525,3873,3874,3876,3878,3880,3882,3885,3887,3889,3891,3893,3895,3897,3899,3901],{"class":527,"line":589},[525,3875,3082],{"class":613},[525,3877,623],{"class":531},[525,3879,1227],{"class":600},[525,3881,643],{"class":531},[525,3883,3884],{"class":802},"8",[525,3886,723],{"class":531},[525,3888,1237],{"class":531},[525,3890,1240],{"class":1202},[525,3892,663],{"class":531},[525,3894,1208],{"class":535},[525,3896,623],{"class":531},[525,3898,1249],{"class":535},[525,3900,273],{"class":531},[525,3902,607],{"class":531},[525,3904,3905,3907,3909,3911,3913,3915,3917,3919,3921,3923,3925,3927,3929,3931,3933,3935,3937,3940],{"class":527,"line":594},[525,3906,3113],{"class":613},[525,3908,623],{"class":531},[525,3910,1263],{"class":600},[525,3912,643],{"class":531},[525,3914,714],{"class":531},[525,3916,3550],{"class":717},[525,3918,714],{"class":531},[525,3920,723],{"class":531},[525,3922,1276],{"class":613},[525,3924,623],{"class":531},[525,3926,1296],{"class":600},[525,3928,2155],{"class":531},[525,3930,1276],{"class":613},[525,3932,623],{"class":531},[525,3934,1281],{"class":600},[525,3936,643],{"class":531},[525,3938,3939],{"class":802},"14",[525,3941,3942],{"class":531},"))\n",[525,3944,3945,3947,3949,3951,3953,3955,3957,3959],{"class":527,"line":610},[525,3946,3113],{"class":613},[525,3948,623],{"class":531},[525,3950,1263],{"class":600},[525,3952,643],{"class":531},[525,3954,714],{"class":531},[525,3956,3648],{"class":717},[525,3958,714],{"class":531},[525,3960,586],{"class":531},[525,3962,3963,3965,3967,3969,3971,3973,3975,3977],{"class":527,"line":632},[525,3964,3113],{"class":613},[525,3966,623],{"class":531},[525,3968,1263],{"class":600},[525,3970,643],{"class":531},[525,3972,714],{"class":531},[525,3974,3690],{"class":717},[525,3976,714],{"class":531},[525,3978,586],{"class":531},[525,3980,3981,3983,3985,3987,3989,3991,3993,3995],{"class":527,"line":676},[525,3982,3113],{"class":613},[525,3984,623],{"class":531},[525,3986,1263],{"class":600},[525,3988,643],{"class":531},[525,3990,714],{"class":531},[525,3992,3732],{"class":717},[525,3994,714],{"class":531},[525,3996,586],{"class":531},[525,3998,3999],{"class":527,"line":688},[525,4000,1309],{"class":531},[525,4002,4003],{"class":527,"line":693},[525,4004,673],{"class":531},[19,4006,4007,4008,4011,4012,4014],{},"2 列、4 + 8 = 12。画像は固定幅にフィットさせ、高さはアスペクト比から gpdf が計算する。各 ",[31,4009,4010],{},"c.Text"," は前の行の下に流れる — ",[31,4013,48],{}," も y 計算もない。ロゴを右に移したければ列の順番を入れ替えるだけ。",[14,4016,4018],{"id":4017},"before-after-5-フッタのページ番号","Before / After 5: フッタのページ番号",[19,4020,4021],{},"gopdf ではカウントを自前で持つ。レンダリングはシングルパスで、最初のフッタを描く時点では総ページ数が分からないため。多くのコードベースは 2 パス回避策をやる: 一度レンダリングしてページ数を数え、再度レンダリングして総数を埋める。",[19,4023,4024],{},[22,4025,514],{},[516,4027,4029],{"className":518,"code":4028,"language":520,"meta":521,"style":521},"totalPages := 0\npdf.AddFooter(func() {\n    totalPages++\n})\n\n// 1 パス目: 全体を描いてページ数を数える。\nbuildContent(&pdf)\nfinalTotal := totalPages\n\n// 2 パス目: 既知の総数で再レンダリング。\npdf2 := gopdf.GoPdf{}\npdf2.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})\npageNum := 0\npdf2.AddFooter(func() {\n    pageNum++\n    pdf2.SetFont(\"helvetica\", \"\", 8)\n    pdf2.SetXY(40, 800)\n    pdf2.Cell(nil, fmt.Sprintf(\"%d / %d ページ\", pageNum, finalTotal))\n})\nbuildContent(&pdf2)\npdf2.WritePdf(\"report.pdf\")\n",[31,4030,4031,4041,4055,4063,4067,4071,4076,4088,4098,4102,4107,4122,4155,4164,4176,4183,4211,4230,4277,4281,4291],{"__ignoreMap":521},[525,4032,4033,4036,4038],{"class":527,"line":528},[525,4034,4035],{"class":613},"totalPages ",[525,4037,617],{"class":531},[525,4039,4040],{"class":802}," 0\n",[525,4042,4043,4045,4047,4050,4053],{"class":527,"line":539},[525,4044,1575],{"class":613},[525,4046,623],{"class":531},[525,4048,4049],{"class":600},"AddFooter",[525,4051,4052],{"class":531},"(func()",[525,4054,607],{"class":531},[525,4056,4057,4060],{"class":527,"line":546},[525,4058,4059],{"class":613},"    totalPages",[525,4061,4062],{"class":531},"++\n",[525,4064,4065],{"class":527,"line":556},[525,4066,673],{"class":531},[525,4068,4069],{"class":527,"line":568},[525,4070,543],{"emptyLinePlaceholder":542},[525,4072,4073],{"class":527,"line":573},[525,4074,4075],{"class":1475},"// 1 パス目: 全体を描いてページ数を数える。\n",[525,4077,4078,4081,4084,4086],{"class":527,"line":583},[525,4079,4080],{"class":600},"buildContent",[525,4082,4083],{"class":531},"(&",[525,4085,1575],{"class":613},[525,4087,586],{"class":531},[525,4089,4090,4093,4095],{"class":527,"line":589},[525,4091,4092],{"class":613},"finalTotal ",[525,4094,617],{"class":531},[525,4096,4097],{"class":613}," totalPages\n",[525,4099,4100],{"class":527,"line":594},[525,4101,543],{"emptyLinePlaceholder":542},[525,4103,4104],{"class":527,"line":610},[525,4105,4106],{"class":1475},"// 2 パス目: 既知の総数で再レンダリング。\n",[525,4108,4109,4112,4114,4116,4118,4120],{"class":527,"line":632},[525,4110,4111],{"class":613},"pdf2 ",[525,4113,617],{"class":531},[525,4115,620],{"class":535},[525,4117,623],{"class":531},[525,4119,626],{"class":535},[525,4121,629],{"class":531},[525,4123,4124,4127,4129,4131,4133,4135,4137,4139,4141,4143,4145,4147,4149,4151,4153],{"class":527,"line":676},[525,4125,4126],{"class":613},"pdf2",[525,4128,623],{"class":531},[525,4130,640],{"class":600},[525,4132,643],{"class":531},[525,4134,646],{"class":535},[525,4136,623],{"class":531},[525,4138,651],{"class":535},[525,4140,654],{"class":531},[525,4142,657],{"class":613},[525,4144,660],{"class":531},[525,4146,663],{"class":531},[525,4148,646],{"class":613},[525,4150,623],{"class":531},[525,4152,670],{"class":613},[525,4154,673],{"class":531},[525,4156,4157,4160,4162],{"class":527,"line":688},[525,4158,4159],{"class":613},"pageNum ",[525,4161,617],{"class":531},[525,4163,4040],{"class":802},[525,4165,4166,4168,4170,4172,4174],{"class":527,"line":693},[525,4167,4126],{"class":613},[525,4169,623],{"class":531},[525,4171,4049],{"class":600},[525,4173,4052],{"class":531},[525,4175,607],{"class":531},[525,4177,4178,4181],{"class":527,"line":747},[525,4179,4180],{"class":613},"    pageNum",[525,4182,4062],{"class":531},[525,4184,4185,4188,4190,4192,4194,4196,4198,4200,4202,4204,4206,4209],{"class":527,"line":765},[525,4186,4187],{"class":613},"    pdf2",[525,4189,623],{"class":531},[525,4191,784],{"class":600},[525,4193,643],{"class":531},[525,4195,714],{"class":531},[525,4197,718],{"class":717},[525,4199,714],{"class":531},[525,4201,723],{"class":531},[525,4203,797],{"class":531},[525,4205,723],{"class":531},[525,4207,4208],{"class":802}," 8",[525,4210,586],{"class":531},[525,4212,4213,4215,4217,4219,4221,4223,4225,4228],{"class":527,"line":771},[525,4214,4187],{"class":613},[525,4216,623],{"class":531},[525,4218,33],{"class":600},[525,4220,643],{"class":531},[525,4222,853],{"class":802},[525,4224,723],{"class":531},[525,4226,4227],{"class":802}," 800",[525,4229,586],{"class":531},[525,4231,4232,4234,4236,4238,4240,4243,4245,4248,4250,4252,4256,4258,4260,4263,4265,4267,4270,4272,4275],{"class":527,"line":816},[525,4233,4187],{"class":613},[525,4235,623],{"class":531},[525,4237,37],{"class":600},[525,4239,890],{"class":531},[525,4241,4242],{"class":613}," fmt",[525,4244,623],{"class":531},[525,4246,4247],{"class":600},"Sprintf",[525,4249,643],{"class":531},[525,4251,714],{"class":531},[525,4253,4255],{"class":4254},"swJcz","%d",[525,4257,399],{"class":717},[525,4259,4255],{"class":4254},[525,4261,4262],{"class":717}," ページ",[525,4264,714],{"class":531},[525,4266,723],{"class":531},[525,4268,4269],{"class":613}," pageNum",[525,4271,723],{"class":531},[525,4273,4274],{"class":613}," finalTotal",[525,4276,3942],{"class":531},[525,4278,4279],{"class":527,"line":831},[525,4280,673],{"class":531},[525,4282,4283,4285,4287,4289],{"class":527,"line":836},[525,4284,4080],{"class":600},[525,4286,4083],{"class":531},[525,4288,4126],{"class":613},[525,4290,586],{"class":531},[525,4292,4293,4295,4297,4299,4301,4303,4306,4308],{"class":527,"line":841},[525,4294,4126],{"class":613},[525,4296,623],{"class":531},[525,4298,948],{"class":600},[525,4300,643],{"class":531},[525,4302,714],{"class":531},[525,4304,4305],{"class":717},"report.pdf",[525,4307,714],{"class":531},[525,4309,586],{"class":531},[19,4311,4312],{},"gopdf を運用したことがあれば、これを書いたことがあるはず。FAQ には載らないが、出力をパースせずに正直な「X / Y ページ」フッタを得る唯一の方法。",[19,4314,4315],{},[22,4316,998],{},[516,4318,4320],{"className":518,"code":4319,"language":520,"meta":521,"style":521},"doc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"ACME 株式会社\",\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Stack(template.AlignRight(), func(c *template.ColBuilder) {\n                c.PageNumber(template.Inline())\n                c.Text(\" / \", template.Inline())\n                c.TotalPages(template.Inline())\n                c.Text(\" ページ\", template.Inline())\n            }, template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n",[31,4321,4322,4349,4374,4404,4422,4458,4462,4492,4528,4549,4575,4594,4620,4657,4661,4665],{"__ignoreMap":521},[525,4323,4324,4327,4329,4332,4334,4336,4338,4340,4342,4345,4347],{"class":527,"line":528},[525,4325,4326],{"class":613},"doc",[525,4328,623],{"class":531},[525,4330,4331],{"class":600},"Footer",[525,4333,1199],{"class":531},[525,4335,19],{"class":1202},[525,4337,663],{"class":531},[525,4339,1208],{"class":535},[525,4341,623],{"class":531},[525,4343,4344],{"class":535},"PageBuilder",[525,4346,273],{"class":531},[525,4348,607],{"class":531},[525,4350,4351,4354,4356,4358,4360,4362,4364,4366,4368,4370,4372],{"class":527,"line":539},[525,4352,4353],{"class":613},"    p",[525,4355,623],{"class":531},[525,4357,1196],{"class":600},[525,4359,1199],{"class":531},[525,4361,1203],{"class":1202},[525,4363,663],{"class":531},[525,4365,1208],{"class":535},[525,4367,623],{"class":531},[525,4369,1213],{"class":535},[525,4371,273],{"class":531},[525,4373,607],{"class":531},[525,4375,4376,4378,4380,4382,4384,4386,4388,4390,4392,4394,4396,4398,4400,4402],{"class":527,"line":546},[525,4377,1222],{"class":613},[525,4379,623],{"class":531},[525,4381,1227],{"class":600},[525,4383,643],{"class":531},[525,4385,1818],{"class":802},[525,4387,723],{"class":531},[525,4389,1237],{"class":531},[525,4391,1240],{"class":1202},[525,4393,663],{"class":531},[525,4395,1208],{"class":535},[525,4397,623],{"class":531},[525,4399,1249],{"class":535},[525,4401,273],{"class":531},[525,4403,607],{"class":531},[525,4405,4406,4408,4410,4412,4414,4416,4418,4420],{"class":527,"line":556},[525,4407,1258],{"class":613},[525,4409,623],{"class":531},[525,4411,1263],{"class":600},[525,4413,643],{"class":531},[525,4415,714],{"class":531},[525,4417,3550],{"class":717},[525,4419,714],{"class":531},[525,4421,2144],{"class":531},[525,4423,4424,4426,4428,4430,4432,4434,4436,4438,4440,4442,4444,4446,4448,4451,4453,4456],{"class":527,"line":568},[525,4425,2064],{"class":613},[525,4427,623],{"class":531},[525,4429,1281],{"class":600},[525,4431,643],{"class":531},[525,4433,3884],{"class":802},[525,4435,1289],{"class":531},[525,4437,1276],{"class":613},[525,4439,623],{"class":531},[525,4441,2177],{"class":600},[525,4443,643],{"class":531},[525,4445,1575],{"class":613},[525,4447,623],{"class":531},[525,4449,4450],{"class":600},"Gray",[525,4452,643],{"class":531},[525,4454,4455],{"class":802},"0.5",[525,4457,3867],{"class":531},[525,4459,4460],{"class":527,"line":573},[525,4461,1304],{"class":531},[525,4463,4464,4466,4468,4470,4472,4474,4476,4478,4480,4482,4484,4486,4488,4490],{"class":527,"line":583},[525,4465,1222],{"class":613},[525,4467,623],{"class":531},[525,4469,1227],{"class":600},[525,4471,643],{"class":531},[525,4473,1818],{"class":802},[525,4475,723],{"class":531},[525,4477,1237],{"class":531},[525,4479,1240],{"class":1202},[525,4481,663],{"class":531},[525,4483,1208],{"class":535},[525,4485,623],{"class":531},[525,4487,1249],{"class":535},[525,4489,273],{"class":531},[525,4491,607],{"class":531},[525,4493,4494,4496,4498,4501,4503,4505,4507,4510,4512,4514,4516,4518,4520,4522,4524,4526],{"class":527,"line":589},[525,4495,1258],{"class":613},[525,4497,623],{"class":531},[525,4499,4500],{"class":600},"Stack",[525,4502,643],{"class":531},[525,4504,1208],{"class":613},[525,4506,623],{"class":531},[525,4508,4509],{"class":600},"AlignRight",[525,4511,2155],{"class":531},[525,4513,1237],{"class":531},[525,4515,1240],{"class":1202},[525,4517,663],{"class":531},[525,4519,1208],{"class":535},[525,4521,623],{"class":531},[525,4523,1249],{"class":535},[525,4525,273],{"class":531},[525,4527,607],{"class":531},[525,4529,4530,4533,4535,4538,4540,4542,4544,4547],{"class":527,"line":594},[525,4531,4532],{"class":613},"                c",[525,4534,623],{"class":531},[525,4536,4537],{"class":600},"PageNumber",[525,4539,643],{"class":531},[525,4541,1208],{"class":613},[525,4543,623],{"class":531},[525,4545,4546],{"class":600},"Inline",[525,4548,1299],{"class":531},[525,4550,4551,4553,4555,4557,4559,4561,4563,4565,4567,4569,4571,4573],{"class":527,"line":610},[525,4552,4532],{"class":613},[525,4554,623],{"class":531},[525,4556,1263],{"class":600},[525,4558,643],{"class":531},[525,4560,714],{"class":531},[525,4562,399],{"class":717},[525,4564,714],{"class":531},[525,4566,723],{"class":531},[525,4568,1276],{"class":613},[525,4570,623],{"class":531},[525,4572,4546],{"class":600},[525,4574,1299],{"class":531},[525,4576,4577,4579,4581,4584,4586,4588,4590,4592],{"class":527,"line":632},[525,4578,4532],{"class":613},[525,4580,623],{"class":531},[525,4582,4583],{"class":600},"TotalPages",[525,4585,643],{"class":531},[525,4587,1208],{"class":613},[525,4589,623],{"class":531},[525,4591,4546],{"class":600},[525,4593,1299],{"class":531},[525,4595,4596,4598,4600,4602,4604,4606,4608,4610,4612,4614,4616,4618],{"class":527,"line":676},[525,4597,4532],{"class":613},[525,4599,623],{"class":531},[525,4601,1263],{"class":600},[525,4603,643],{"class":531},[525,4605,714],{"class":531},[525,4607,4262],{"class":717},[525,4609,714],{"class":531},[525,4611,723],{"class":531},[525,4613,1276],{"class":613},[525,4615,623],{"class":531},[525,4617,4546],{"class":600},[525,4619,1299],{"class":531},[525,4621,4622,4625,4627,4629,4631,4633,4635,4637,4639,4641,4643,4645,4647,4649,4651,4653,4655],{"class":527,"line":688},[525,4623,4624],{"class":531},"            },",[525,4626,1276],{"class":613},[525,4628,623],{"class":531},[525,4630,1281],{"class":600},[525,4632,643],{"class":531},[525,4634,3884],{"class":802},[525,4636,1289],{"class":531},[525,4638,1276],{"class":613},[525,4640,623],{"class":531},[525,4642,2177],{"class":600},[525,4644,643],{"class":531},[525,4646,1575],{"class":613},[525,4648,623],{"class":531},[525,4650,4450],{"class":600},[525,4652,643],{"class":531},[525,4654,4455],{"class":802},[525,4656,3867],{"class":531},[525,4658,4659],{"class":527,"line":693},[525,4660,1304],{"class":531},[525,4662,4663],{"class":527,"line":747},[525,4664,1309],{"class":531},[525,4666,4667],{"class":527,"line":765},[525,4668,673],{"class":531},[19,4670,4671,4673,4674,4676],{},[31,4672,4537],{}," と ",[31,4675,4583],{}," はプレースホルダ。レイアウトエンジンが先にページネーションし、総数を解決してから書き込む。1 パス、自前カウントなし、二重描画なし。「5 / 12」のような句中表記もそのまま動く。",[14,4678,4680],{"id":4679},"日本語-手動サブセットなしで","日本語: 手動サブセットなしで",[19,4682,4683],{},"signintech/gopdf でも CJK は使えるが、文字集合の簿記を自分でやる構成。TTF を追加し、文字マップを設定し、登録したサブセット外の文字が来ると豆腐になる。gpdf の TrueType サブセッタは cmap (フォーマット 4・6・12) を歩いて、実際に使ったグリフだけを埋め込む — 手動サブセットリストは存在しない。",[516,4685,4687],{"className":518,"code":4686,"language":520,"meta":521,"style":521},"//go:embed NotoSansJP-Regular.ttf\nvar notoJP []byte\n\ndoc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    gpdf.WithFont(\"NotoSansJP\", notoJP),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 14),\n)\n\npage := doc.AddPage()\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"こんにちは、世界。\")\n        c.Text(\"吾輩は猫である。名前はまだ無い。\")\n        c.Text(\"東京都渋谷区神宮前1-2-3\")\n    })\n})\n",[31,4688,4689,4694,4705,4709,4724,4743,4773,4798,4821,4825,4829,4844,4868,4898,4917,4936,4955,4959],{"__ignoreMap":521},[525,4690,4691],{"class":527,"line":528},[525,4692,4693],{"class":1475},"//go:embed NotoSansJP-Regular.ttf\n",[525,4695,4696,4698,4701,4703],{"class":527,"line":539},[525,4697,3762],{"class":531},[525,4699,4700],{"class":613}," notoJP ",[525,4702,3768],{"class":531},[525,4704,3771],{"class":1667},[525,4706,4707],{"class":527,"line":546},[525,4708,543],{"emptyLinePlaceholder":542},[525,4710,4711,4714,4716,4718,4720,4722],{"class":527,"line":556},[525,4712,4713],{"class":613},"doc ",[525,4715,617],{"class":531},[525,4717,1095],{"class":613},[525,4719,623],{"class":531},[525,4721,1100],{"class":600},[525,4723,1103],{"class":531},[525,4725,4726,4729,4731,4733,4735,4737,4739,4741],{"class":527,"line":568},[525,4727,4728],{"class":613},"    gpdf",[525,4730,623],{"class":531},[525,4732,1113],{"class":600},[525,4734,643],{"class":531},[525,4736,1118],{"class":613},[525,4738,623],{"class":531},[525,4740,1123],{"class":613},[525,4742,1126],{"class":531},[525,4744,4745,4747,4749,4751,4753,4755,4757,4759,4761,4763,4765,4767,4769,4771],{"class":527,"line":573},[525,4746,4728],{"class":613},[525,4748,623],{"class":531},[525,4750,1135],{"class":600},[525,4752,643],{"class":531},[525,4754,1118],{"class":613},[525,4756,623],{"class":531},[525,4758,1144],{"class":600},[525,4760,643],{"class":531},[525,4762,1118],{"class":613},[525,4764,623],{"class":531},[525,4766,1153],{"class":600},[525,4768,643],{"class":531},[525,4770,1158],{"class":802},[525,4772,1161],{"class":531},[525,4774,4775,4777,4779,4782,4784,4786,4789,4791,4793,4796],{"class":527,"line":583},[525,4776,4728],{"class":613},[525,4778,623],{"class":531},[525,4780,4781],{"class":600},"WithFont",[525,4783,643],{"class":531},[525,4785,714],{"class":531},[525,4787,4788],{"class":717},"NotoSansJP",[525,4790,714],{"class":531},[525,4792,723],{"class":531},[525,4794,4795],{"class":613}," notoJP",[525,4797,1126],{"class":531},[525,4799,4800,4802,4804,4807,4809,4811,4813,4815,4817,4819],{"class":527,"line":589},[525,4801,4728],{"class":613},[525,4803,623],{"class":531},[525,4805,4806],{"class":600},"WithDefaultFont",[525,4808,643],{"class":531},[525,4810,714],{"class":531},[525,4812,4788],{"class":717},[525,4814,714],{"class":531},[525,4816,723],{"class":531},[525,4818,3508],{"class":802},[525,4820,1126],{"class":531},[525,4822,4823],{"class":527,"line":594},[525,4824,586],{"class":531},[525,4826,4827],{"class":527,"line":610},[525,4828,543],{"emptyLinePlaceholder":542},[525,4830,4831,4834,4836,4838,4840,4842],{"class":527,"line":632},[525,4832,4833],{"class":613},"page ",[525,4835,617],{"class":531},[525,4837,1180],{"class":613},[525,4839,623],{"class":531},[525,4841,55],{"class":600},[525,4843,685],{"class":531},[525,4845,4846,4848,4850,4852,4854,4856,4858,4860,4862,4864,4866],{"class":527,"line":676},[525,4847,1932],{"class":613},[525,4849,623],{"class":531},[525,4851,1196],{"class":600},[525,4853,1199],{"class":531},[525,4855,1203],{"class":1202},[525,4857,663],{"class":531},[525,4859,1208],{"class":535},[525,4861,623],{"class":531},[525,4863,1213],{"class":535},[525,4865,273],{"class":531},[525,4867,607],{"class":531},[525,4869,4870,4872,4874,4876,4878,4880,4882,4884,4886,4888,4890,4892,4894,4896],{"class":527,"line":688},[525,4871,3082],{"class":613},[525,4873,623],{"class":531},[525,4875,1227],{"class":600},[525,4877,643],{"class":531},[525,4879,1232],{"class":802},[525,4881,723],{"class":531},[525,4883,1237],{"class":531},[525,4885,1240],{"class":1202},[525,4887,663],{"class":531},[525,4889,1208],{"class":535},[525,4891,623],{"class":531},[525,4893,1249],{"class":535},[525,4895,273],{"class":531},[525,4897,607],{"class":531},[525,4899,4900,4902,4904,4906,4908,4910,4913,4915],{"class":527,"line":693},[525,4901,3113],{"class":613},[525,4903,623],{"class":531},[525,4905,1263],{"class":600},[525,4907,643],{"class":531},[525,4909,714],{"class":531},[525,4911,4912],{"class":717},"こんにちは、世界。",[525,4914,714],{"class":531},[525,4916,586],{"class":531},[525,4918,4919,4921,4923,4925,4927,4929,4932,4934],{"class":527,"line":747},[525,4920,3113],{"class":613},[525,4922,623],{"class":531},[525,4924,1263],{"class":600},[525,4926,643],{"class":531},[525,4928,714],{"class":531},[525,4930,4931],{"class":717},"吾輩は猫である。名前はまだ無い。",[525,4933,714],{"class":531},[525,4935,586],{"class":531},[525,4937,4938,4940,4942,4944,4946,4948,4951,4953],{"class":527,"line":765},[525,4939,3113],{"class":613},[525,4941,623],{"class":531},[525,4943,1263],{"class":600},[525,4945,643],{"class":531},[525,4947,714],{"class":531},[525,4949,4950],{"class":717},"東京都渋谷区神宮前1-2-3",[525,4952,714],{"class":531},[525,4954,586],{"class":531},[525,4956,4957],{"class":527,"line":771},[525,4958,1309],{"class":531},[525,4960,4961],{"class":527,"line":816},[525,4962,673],{"class":531},[19,4964,4965,4966,4971,4972,4976],{},"200 字程度の日本語請求書だと、フォントサブセットは ~30 KB。フル埋め込みの 4 MB ではなく。日本語フォント周りの詳細は",[4967,4968,4970],"a",{"href":4969},"/ja/blog/embed-japanese-font","日本語フォント埋め込み","、IPAex Gothic や Source Han Sans のフォールバックは",[4967,4973,4975],{"href":4974},"/ja/blog/ipaex-gothic-gpdf","IPAex Gothic"," を参照。",[14,4978,4979],{"id":4979},"ベンチマーク",[19,4981,4982],{},"同一ハードウェア・同一ワークロード、Apple M1 と Go 1.25。",[134,4984,4985,5002],{},[137,4986,4987],{},[140,4988,4989,4992,4994,4996,4999],{},[143,4990,4991],{},"ベンチ",[143,4993,24],{},[143,4995,28],{},[143,4997,4998],{},"gofpdf",[143,5000,5001],{},"Maroto v2",[151,5003,5004,5023,5041,5059],{},[140,5005,5006,5009,5014,5017,5020],{},[156,5007,5008],{},"単一ページ",[156,5010,5011],{},[22,5012,5013],{},"13 µs",[156,5015,5016],{},"423 µs",[156,5018,5019],{},"132 µs",[156,5021,5022],{},"237 µs",[140,5024,5025,5028,5032,5035,5038],{},[156,5026,5027],{},"4×10 請求書テーブル",[156,5029,5030],{},[22,5031,3318],{},[156,5033,5034],{},"835 µs",[156,5036,5037],{},"241 µs",[156,5039,5040],{},"8.6 ms",[140,5042,5043,5046,5051,5053,5056],{},[156,5044,5045],{},"100 ページレポート",[156,5047,5048],{},[22,5049,5050],{},"683 µs",[156,5052,5040],{},[156,5054,5055],{},"11.7 ms",[156,5057,5058],{},"19.8 ms",[140,5060,5061,5064,5069,5072,5075],{},[156,5062,5063],{},"複雑な CJK 請求書",[156,5065,5066],{},[22,5067,5068],{},"133 µs",[156,5070,5071],{},"997 µs",[156,5073,5074],{},"254 µs",[156,5076,5077],{},"10.4 ms",[19,5079,5080,5081,5084],{},"数値は ",[31,5082,5083],{},"gpdf/_benchmark/benchmark_test.go"," から。signintech/gopdf は 1 呼び出しあたりの仕事が少ないぶん単純パスでは gofpdf より速いが、テーブルになると差が広がる: gpdf のレイアウト処理はホットパスでアロケーションのない C 風の命令的内部コードで動き、手書きの gopdf テーブルはセルごとに文字列とフォントメトリクスをアロケートする。",[19,5086,5087,5088,5091],{},"シングルコアで ",[22,5089,5090],{},"1 ページあたり 108 µs"," のテーブル付きスループット = 1 秒あたり約 9,000 請求書。たいていのワークロードでは PDF 生成をリクエストパス上に置いておける。gopdf が 10 倍遅かったから cron / queue / 事前生成を挟んでいた回避策はだいたい外せる。",[14,5093,5095],{"id":5094},"gopdf-にあって-gpdf-にないもの","gopdf にあって gpdf にないもの",[19,5097,5098],{},"正直セクション。以下に依存している場合、移行はこの記事だけでは完結しない。",[5100,5101,5102,5119,5132,5146],"ul",{},[5103,5104,5105,5110,5111,5114,5115,5118],"li",{},[22,5106,5107,498],{},[31,5108,5109],{},"ImportPage"," 既存 PDF から 1 ページを取り込み、その上にコンテンツをスタンプする「PDF テンプレート」ワークフロー。gpdf の overlay (",[31,5112,5113],{},"gpdf.Overlay",") で典型ケースは扱えるが、",[31,5116,5117],{},"UseImportedTemplate"," と同じプリミティブは公開していない。ベンダ提供 PDF への描画が前提なら overlay API を先に評価する。",[5103,5120,5121,5124,5125,4673,5128,5131],{},[22,5122,5123],{},"多角形・楕円のプリミティブ。"," gopdf には ",[31,5126,5127],{},"Oval",[31,5129,5130],{},"Polygon"," がある。gpdf のプリミティブは矩形・線・画像・テキスト・テーブルで、任意パス描画は一級市民ではない。データ可視化はチャートライブラリで PNG/SVG にして埋め込む構成にする。",[5103,5133,5134,5137,5138,5141,5142,5145],{},[22,5135,5136],{},"直接的なカーソル位置。"," ピクセル単位での配置 (例えば ",[31,5139,5140],{},"(420, 240)"," ちょうどにスタンプ) が本当にほしい場合 ",[31,5143,5144],{},"page.Absolute(x, y, fn)"," がある。ただしこれは脱出口で、コードの大半はここに手を伸ばすべきではない。",[5103,5147,5148,5156,5157,399,5159,5161],{},[22,5149,5150,399,5153,498],{},[31,5151,5152],{},"PlaceHolderText",[31,5154,5155],{},"FillInPlaceHoldText"," レイアウト後にテキストを埋める 2 パスのプレースホルダパターン。gpdf の ",[31,5158,4537],{},[31,5160,4583],{}," プレースホルダは典型ケース (ページ番号) を扱うが、汎用「あとで埋めるスロット」機構はまだない。",[19,5163,5164],{},"請求書、明細書、レポート、修了証、契約書、領収書、配送ラベル、納品書、CJK ドキュメント — つまり gopdf の請求の大半が消費している用途では、移行は完全に行ける。",[14,5166,5168],{"id":5167},"faq","FAQ",[19,5170,5171,5174],{},[22,5172,5173],{},"gpdf は signintech/gopdf のフォーク?","\n違う。gpdf は純 Go によるクリーン実装。PDF wire writer、レイアウトエンジン、TrueType サブセッタ、AES 暗号化、PKCS#7 署名、すべて一から書かれている。共有コードも血統もない。",[19,5176,5177,5180],{},[22,5178,5179],{},"両方とも純 Go・CGO なし。乗り換える実利は?","\nレイアウトエンジン。前述の移行セクションは 8 割が座標計算の削除で、それが日々のコードの読み味を実際に変える差分。ベンチマークは副次。MIT ライセンスは gopdf の MIT と同じなのでライセンス面の理由はない。",[19,5182,5183,5186,5187,5190,5191,5194],{},[22,5184,5185],{},"段階的に移行できる?","\nできる。両者は衝突しない。それぞれ独立した ",[31,5188,5189],{},"[]byte"," 出力を生む。あるセクションを gpdf、別のを gopdf でレンダリングし、",[31,5192,5193],{},"gpdf.Merge(a, b)"," で 1 つの PDF に綴じる。実際にはドキュメント単位で一気に置き換えるほうが楽だと感じるチームが多い — 同じファイルに 2 つのメンタルモデルが共存するのは混乱の元。",[19,5196,5197,5204,5205,5208,5209,5212,5213,5216],{},[22,5198,5199,5200,5203],{},"既存コードは ",[31,5201,5202],{},"pdf.Image(path, ...)"," でディスクからロゴを読んでいる。embed する必要は?","\n必須ではない。",[31,5206,5207],{},"c.Image(imageBytes, ...)"," はバイトを取るので、ランタイムで読みたければ ",[31,5210,5211],{},"os.ReadFile"," で問題ない。だが ",[31,5214,5215],{},"//go:embed"," のほうが既定としては良い: バイナリが書き込み可能ファイルシステムを期待しなくなり、コンテナイメージは 1 バイナリで済み、本番でアセットが欠ける事故が消える。",[19,5218,5219,5225,5226,5229,5230,5229,5233,5236,5237,498],{},[22,5220,5221,5224],{},[31,5222,5223],{},"gopdf.PageSizeA4"," などのページサイズ定数は?","\ngpdf 側の ",[31,5227,5228],{},"document.A4",", ",[31,5231,5232],{},"document.Letter",[31,5234,5235],{},"document.Legal"," 等が同じ集合をカバーする。カスタムサイズは ",[31,5238,5239],{},"document.PageSize(document.Mm(210), document.Mm(297))",[19,5241,5242,5249,5251,5252,5255],{},[22,5243,5244,5245,5248],{},"請求書ジェネレータで ",[31,5246,5247],{},"pdf.Rotate"," を使った縦書きスタンプを行っている。等価機能は?",[31,5250,5144],{}," が回転オプションを受け取るので、典型的な「ページに対角の透かし」は ",[31,5253,5254],{},"page.Absolute"," 1 呼び出しで済む。テーブル内のセル単位の回転はプリミティブとしてはなく、必要なら該当セルだけ画像にして埋め込む。",[19,5257,5258,5261,5262,1560,5264,5266,5267,1560,5270,5272],{},[22,5259,5260],{},"自動で書き換えるツールはある?","\nまだない。単純部分 (",[31,5263,33],{},[31,5265,37],{}," → ",[31,5268,5269],{},"r.Col",[31,5271,4010],{},") のマッピングは機械的だが、テーブルの書き換えは構造的 — 翻訳ではなく簿記コードの削除。典型的なジェネレータの手動移行はドキュメントタイプあたり数時間が目安。",[14,5274,5276],{"id":5275},"gpdf-を使ってみる","gpdf を使ってみる",[19,5278,5279],{},"gpdf は Go の PDF 生成ライブラリ。MIT、ゼロ依存、CJK ネイティブ対応、12 カラムグリッドレイアウト。",[516,5281,5285],{"className":5282,"code":5283,"language":5284,"meta":521,"style":521},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[31,5286,5287],{"__ignoreMap":521},[525,5288,5289,5291,5294],{"class":527,"line":528},[525,5290,520],{"class":535},[525,5292,5293],{"class":717}," get",[525,5295,5296],{"class":717}," github.com/gpdf-dev/gpdf\n",[19,5298,5299,5305,5306],{},[4967,5300,5304],{"href":5301,"rel":5302},"https://github.com/gpdf-dev/gpdf",[5303],"nofollow","⭐ Star on GitHub"," · ",[4967,5307,5310],{"href":5308,"rel":5309},"https://gpdf.dev/ja/docs/quickstart",[5303],"ドキュメントを読む",[14,5312,5313],{"id":5313},"次に読む",[5100,5315,5316,5322,5328,5334,5340],{},[5103,5317,5318],{},[4967,5319,5321],{"href":5320},"/ja/blog/12-column-grid","12 カラムグリッド: PDF レイアウトに Bootstrap 思考を持ち込む",[5103,5323,5324],{},[4967,5325,5327],{"href":5326},"/ja/blog/bootstrap-grid-thinking-for-pdf","Bootstrap 風グリッドで PDF を組む",[5103,5329,5330],{},[4967,5331,5333],{"href":5332},"/ja/blog/gofpdf-migration","gofpdf がアーカイブされた。gpdf へ移行する方法",[5103,5335,5336],{},[4967,5337,5339],{"href":5338},"/ja/blog/unidoc-migration","unipdf は AGPL か有償。gpdf へ移行する方法",[5103,5341,5342],{},[4967,5343,5345],{"href":5344},"/ja/blog/invoice-pdf-go-under-50-lines","Go で 50 行未満の請求書 PDF を生成する",[5347,5348,5349],"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 .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .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 .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":521,"searchDepth":539,"depth":539,"links":5351},[5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366],{"id":16,"depth":539,"text":17},{"id":65,"depth":539,"text":66},{"id":87,"depth":539,"text":87},{"id":128,"depth":539,"text":129},{"id":505,"depth":539,"text":506},{"id":1441,"depth":539,"text":1442},{"id":2224,"depth":539,"text":2225},{"id":3326,"depth":539,"text":3327},{"id":4017,"depth":539,"text":4018},{"id":4679,"depth":539,"text":4680},{"id":4979,"depth":539,"text":4979},{"id":5094,"depth":539,"text":5095},{"id":5167,"depth":539,"text":5168},{"id":5275,"depth":539,"text":5276},{"id":5313,"depth":539,"text":5313},"2026-05-06","signintech/gopdf は速いが、セル・線・ヘッダ全てが (x, y) の計算式。gpdf 移行で座標計算がほぼ消える流れを API 対応表とコードで示す。",false,"md",{"name":5372,"totalTime":5373,"tools":5374,"steps":5376},"Go プロジェクトを signintech/gopdf から gpdf へ移行する","PT40M",[5375],"Go 1.22+",[5377,5380,5383,5386,5389,5392,5395],{"name":5378,"text":5379},"import パスを置き換える","github.com/signintech/gopdf を github.com/gpdf-dev/gpdf, github.com/gpdf-dev/gpdf/document, github.com/gpdf-dev/gpdf/template に置き換える。共有可変カーソルである gopdf.GoPdf{} 構造体は廃止し、対応する型は存在しない。",{"name":5381,"text":5382},"pdf.Start と pdf.AddPage を gpdf.NewDocument に置き換える","gpdf.NewDocument(WithPageSize(document.A4), WithMargins(...)) でドキュメントを構築する。doc.AddPage() は PageBuilder を返し、カーソルは返らない。現在の y 座標を手で追う必要が消える。",{"name":5384,"text":5385},"SetX / SetY / SetXY を全て削除する","全てのカーソル呼び出しを削除する。コンテンツは page.AutoRow(func(r *RowBuilder)) と r.Col(span, func(c *ColBuilder)) で包む。横配置は 12 カラムグリッドが、縦配置は AutoRow が処理し y 計算は不要になる。",{"name":5387,"text":5388},"Cell / MultiCell を c.Text に置き換える","Rect を計算して Cell や MultiCell を呼ぶ代わりに、列の中で c.Text(string, options...) を呼ぶ。列が自身の幅を知っているので折り返しは自動。",{"name":5390,"text":5391},"手書きのテーブルループを c.Table に書き換える","ヘッダと本体行を SetXY/Cell で 1 セルずつ描いていたループを、c.Table(headers, rows, template.ColumnWidths(...)) に置き換える。罫線・改ページ・ヘッダ再描画はレイアウトエンジンが自動で行う。",{"name":5393,"text":5394},"フォント登録をバイト列ベースに切り替える","pdf.AddTTFFont(name, path) を、構築時の gpdf.WithFont(name, ttfBytes) に置き換える。//go:embed で TTF をバイナリに埋め込み、ランタイムでフォントパスを必要としない構成にする。",{"name":5396,"text":5397},"WritePdf を doc.Generate + os.WriteFile に置き換える","pdf.WritePdf(path) を data, _ := doc.Generate() と os.WriteFile(path, data, 0o644) に置き換える。http.ResponseWriter や S3 PutObject へ直接ストリームしたい場合は doc.Render(w) を使う。",null,{},"/ja/blog/signintech-gopdf-migration",{"title":5,"description":5368},"ja/blog/021.signintech-gopdf-migration",[5404,5405,5406],"migration","comparison","tutorial","t-4iDafNKcnzwyp7lY13RSwks4OQKyVvEYSl4si0zj4",1779199021545]