[{"data":1,"prerenderedAt":19140},["ShallowReactive",2],{"blog-tag-pt-recipe":3},[4,1327,2920,4030,5261,6626,8376,10048,11669,12989,14358],{"id":5,"title":6,"author":7,"body":10,"date":1291,"description":1292,"draft":1293,"extension":1294,"howTo":1295,"image":1317,"meta":1318,"navigation":88,"path":1319,"seo":1320,"stem":1321,"tags":1322,"updated":1317,"__hash__":1326},"blogPt/pt/blog/010.source-han-sans-jp-with-gpdf.md","Como usar Source Han Sans JP no gpdf?",{"name":8,"url":9},"gpdf team","https://gpdf.dev",{"type":11,"value":12,"toc":1279},"minimark",[13,18,35,39,57,61,710,723,727,738,741,826,837,841,876,883,907,916,920,923,931,934,1089,1103,1106,1110,1113,1150,1164,1168,1171,1185,1188,1203,1206,1210,1239,1243,1246,1263,1275],[14,15,17],"h2",{"id":16},"a-pergunta-em-outras-palavras","A pergunta, em outras palavras",[19,20,21,22,26,27,34],"p",{},"Você quer usar ",[23,24,25],"strong",{},"Source Han Sans JP"," — a sans-serif pan-CJK open source da Adobe, lançada em 2014 como resultado da parceria Adobe–Google — em um documento ",[28,29,33],"a",{"href":30,"rel":31},"https://github.com/gpdf-dev/gpdf",[32],"nofollow","gpdf",". Talvez seu time prefira fixar fontes em tags de release do GitHub pela reprodutibilidade, talvez você tenha herdado um design system que se padronizou em Source Han anos atrás, talvez o ciclo de releases da Adobe seja mais conveniente. Qualquer motivo serve. Antes de baixar qualquer coisa, vale deixar três coisas claras: qual arquivo pegar, qual é a relação real com Noto Sans JP e qual formato o gpdf consegue ler.",[14,36,38],{"id":37},"tldr","TL;DR",[19,40,41,42,46,47,52,53,56],{},"Baixe ",[43,44,45],"code",{},"SourceHanSansJP-Regular.ttf"," na ",[28,48,51],{"href":49,"rel":50},"https://github.com/adobe-fonts/source-han-sans/releases",[32],"página de releases de adobe-fonts/source-han-sans"," (o pacote TTF, não o OTF padrão), passe para ",[43,54,55],{},"gpdf.WithFont(\"SourceHanSansJP\", bytes)"," e defina como fonte padrão. Source Han Sans JP e Noto Sans JP compartilham os mesmos contornos de glifo — se a história de tooling da Adobe não importa para você, Noto é um download mais simples.",[14,58,60],{"id":59},"o-exemplo-completo","O exemplo completo",[62,63,68],"pre",{"className":64,"code":65,"language":66,"meta":67,"style":67},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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    font, err := os.ReadFile(\"SourceHanSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"SourceHanSansJP\", font),\n        gpdf.WithDefaultFont(\"SourceHanSansJP\", 11),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"報告書\", template.FontSize(24), template.Bold())\n            c.Text(\"Source Han Sans JP — fonte CJK gratuita distribuída pela Adobe.\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"report.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[43,69,70,83,90,100,112,122,127,137,147,157,163,168,184,222,238,256,262,267,286,309,347,373,398,404,409,428,462,499,547,567,573,579,584,605,618,633,638,684,699,704],{"__ignoreMap":67},[71,72,75,79],"span",{"class":73,"line":74},"line",1,[71,76,78],{"class":77},"sMK4o","package",[71,80,82],{"class":81},"sBMFI"," main\n",[71,84,86],{"class":73,"line":85},2,[71,87,89],{"emptyLinePlaceholder":88},true,"\n",[71,91,93,97],{"class":73,"line":92},3,[71,94,96],{"class":95},"s7zQu","import",[71,98,99],{"class":77}," (\n",[71,101,103,106,109],{"class":73,"line":102},4,[71,104,105],{"class":77},"    \"",[71,107,108],{"class":81},"log",[71,110,111],{"class":77},"\"\n",[71,113,115,117,120],{"class":73,"line":114},5,[71,116,105],{"class":77},[71,118,119],{"class":81},"os",[71,121,111],{"class":77},[71,123,125],{"class":73,"line":124},6,[71,126,89],{"emptyLinePlaceholder":88},[71,128,130,132,135],{"class":73,"line":129},7,[71,131,105],{"class":77},[71,133,134],{"class":81},"github.com/gpdf-dev/gpdf",[71,136,111],{"class":77},[71,138,140,142,145],{"class":73,"line":139},8,[71,141,105],{"class":77},[71,143,144],{"class":81},"github.com/gpdf-dev/gpdf/document",[71,146,111],{"class":77},[71,148,150,152,155],{"class":73,"line":149},9,[71,151,105],{"class":77},[71,153,154],{"class":81},"github.com/gpdf-dev/gpdf/template",[71,156,111],{"class":77},[71,158,160],{"class":73,"line":159},10,[71,161,162],{"class":77},")\n",[71,164,166],{"class":73,"line":165},11,[71,167,89],{"emptyLinePlaceholder":88},[71,169,171,174,178,181],{"class":73,"line":170},12,[71,172,173],{"class":77},"func",[71,175,177],{"class":176},"s2Zo4"," main",[71,179,180],{"class":77},"()",[71,182,183],{"class":77}," {\n",[71,185,187,191,194,197,200,203,206,209,212,215,218,220],{"class":73,"line":186},13,[71,188,190],{"class":189},"sTEyZ","    font",[71,192,193],{"class":77},",",[71,195,196],{"class":189}," err ",[71,198,199],{"class":77},":=",[71,201,202],{"class":189}," os",[71,204,205],{"class":77},".",[71,207,208],{"class":176},"ReadFile",[71,210,211],{"class":77},"(",[71,213,214],{"class":77},"\"",[71,216,45],{"class":217},"sfazB",[71,219,214],{"class":77},[71,221,162],{"class":77},[71,223,225,228,230,233,236],{"class":73,"line":224},14,[71,226,227],{"class":95},"    if",[71,229,196],{"class":189},[71,231,232],{"class":77},"!=",[71,234,235],{"class":77}," nil",[71,237,183],{"class":77},[71,239,241,244,246,249,251,254],{"class":73,"line":240},15,[71,242,243],{"class":189},"        log",[71,245,205],{"class":77},[71,247,248],{"class":176},"Fatal",[71,250,211],{"class":77},[71,252,253],{"class":189},"err",[71,255,162],{"class":77},[71,257,259],{"class":73,"line":258},16,[71,260,261],{"class":77},"    }\n",[71,263,265],{"class":73,"line":264},17,[71,266,89],{"emptyLinePlaceholder":88},[71,268,270,273,275,278,280,283],{"class":73,"line":269},18,[71,271,272],{"class":189},"    doc ",[71,274,199],{"class":77},[71,276,277],{"class":189}," gpdf",[71,279,205],{"class":77},[71,281,282],{"class":176},"NewDocument",[71,284,285],{"class":77},"(\n",[71,287,289,292,294,297,299,301,303,306],{"class":73,"line":288},19,[71,290,291],{"class":189},"        gpdf",[71,293,205],{"class":77},[71,295,296],{"class":176},"WithPageSize",[71,298,211],{"class":77},[71,300,33],{"class":189},[71,302,205],{"class":77},[71,304,305],{"class":189},"A4",[71,307,308],{"class":77},"),\n",[71,310,312,314,316,319,321,324,326,329,331,333,335,338,340,344],{"class":73,"line":311},20,[71,313,291],{"class":189},[71,315,205],{"class":77},[71,317,318],{"class":176},"WithMargins",[71,320,211],{"class":77},[71,322,323],{"class":189},"document",[71,325,205],{"class":77},[71,327,328],{"class":176},"UniformEdges",[71,330,211],{"class":77},[71,332,323],{"class":189},[71,334,205],{"class":77},[71,336,337],{"class":176},"Mm",[71,339,211],{"class":77},[71,341,343],{"class":342},"sbssI","20",[71,345,346],{"class":77},"))),\n",[71,348,350,352,354,357,359,361,364,366,368,371],{"class":73,"line":349},21,[71,351,291],{"class":189},[71,353,205],{"class":77},[71,355,356],{"class":176},"WithFont",[71,358,211],{"class":77},[71,360,214],{"class":77},[71,362,363],{"class":217},"SourceHanSansJP",[71,365,214],{"class":77},[71,367,193],{"class":77},[71,369,370],{"class":189}," font",[71,372,308],{"class":77},[71,374,376,378,380,383,385,387,389,391,393,396],{"class":73,"line":375},22,[71,377,291],{"class":189},[71,379,205],{"class":77},[71,381,382],{"class":176},"WithDefaultFont",[71,384,211],{"class":77},[71,386,214],{"class":77},[71,388,363],{"class":217},[71,390,214],{"class":77},[71,392,193],{"class":77},[71,394,395],{"class":342}," 11",[71,397,308],{"class":77},[71,399,401],{"class":73,"line":400},23,[71,402,403],{"class":77},"    )\n",[71,405,407],{"class":73,"line":406},24,[71,408,89],{"emptyLinePlaceholder":88},[71,410,412,415,417,420,422,425],{"class":73,"line":411},25,[71,413,414],{"class":189},"    page ",[71,416,199],{"class":77},[71,418,419],{"class":189}," doc",[71,421,205],{"class":77},[71,423,424],{"class":176},"AddPage",[71,426,427],{"class":77},"()\n",[71,429,431,434,436,439,442,446,449,452,454,457,460],{"class":73,"line":430},26,[71,432,433],{"class":189},"    page",[71,435,205],{"class":77},[71,437,438],{"class":176},"AutoRow",[71,440,441],{"class":77},"(func(",[71,443,445],{"class":444},"sHdIc","r",[71,447,448],{"class":77}," *",[71,450,451],{"class":81},"template",[71,453,205],{"class":77},[71,455,456],{"class":81},"RowBuilder",[71,458,459],{"class":77},")",[71,461,183],{"class":77},[71,463,465,468,470,473,475,478,480,483,486,488,490,492,495,497],{"class":73,"line":464},27,[71,466,467],{"class":189},"        r",[71,469,205],{"class":77},[71,471,472],{"class":176},"Col",[71,474,211],{"class":77},[71,476,477],{"class":342},"12",[71,479,193],{"class":77},[71,481,482],{"class":77}," func(",[71,484,485],{"class":444},"c",[71,487,448],{"class":77},[71,489,451],{"class":81},[71,491,205],{"class":77},[71,493,494],{"class":81},"ColBuilder",[71,496,459],{"class":77},[71,498,183],{"class":77},[71,500,502,505,507,510,512,514,517,519,521,524,526,529,531,534,537,539,541,544],{"class":73,"line":501},28,[71,503,504],{"class":189},"            c",[71,506,205],{"class":77},[71,508,509],{"class":176},"Text",[71,511,211],{"class":77},[71,513,214],{"class":77},[71,515,516],{"class":217},"報告書",[71,518,214],{"class":77},[71,520,193],{"class":77},[71,522,523],{"class":189}," template",[71,525,205],{"class":77},[71,527,528],{"class":176},"FontSize",[71,530,211],{"class":77},[71,532,533],{"class":342},"24",[71,535,536],{"class":77},"),",[71,538,523],{"class":189},[71,540,205],{"class":77},[71,542,543],{"class":176},"Bold",[71,545,546],{"class":77},"())\n",[71,548,550,552,554,556,558,560,563,565],{"class":73,"line":549},29,[71,551,504],{"class":189},[71,553,205],{"class":77},[71,555,509],{"class":176},[71,557,211],{"class":77},[71,559,214],{"class":77},[71,561,562],{"class":217},"Source Han Sans JP — fonte CJK gratuita distribuída pela Adobe.",[71,564,214],{"class":77},[71,566,162],{"class":77},[71,568,570],{"class":73,"line":569},30,[71,571,572],{"class":77},"        })\n",[71,574,576],{"class":73,"line":575},31,[71,577,578],{"class":77},"    })\n",[71,580,582],{"class":73,"line":581},32,[71,583,89],{"emptyLinePlaceholder":88},[71,585,587,590,592,594,596,598,600,603],{"class":73,"line":586},33,[71,588,589],{"class":189},"    data",[71,591,193],{"class":77},[71,593,196],{"class":189},[71,595,199],{"class":77},[71,597,419],{"class":189},[71,599,205],{"class":77},[71,601,602],{"class":176},"Generate",[71,604,427],{"class":77},[71,606,608,610,612,614,616],{"class":73,"line":607},34,[71,609,227],{"class":95},[71,611,196],{"class":189},[71,613,232],{"class":77},[71,615,235],{"class":77},[71,617,183],{"class":77},[71,619,621,623,625,627,629,631],{"class":73,"line":620},35,[71,622,243],{"class":189},[71,624,205],{"class":77},[71,626,248],{"class":176},[71,628,211],{"class":77},[71,630,253],{"class":189},[71,632,162],{"class":77},[71,634,636],{"class":73,"line":635},36,[71,637,261],{"class":77},[71,639,641,643,645,647,649,651,654,656,658,661,663,665,668,670,673,676,678,680,682],{"class":73,"line":640},37,[71,642,227],{"class":95},[71,644,196],{"class":189},[71,646,199],{"class":77},[71,648,202],{"class":189},[71,650,205],{"class":77},[71,652,653],{"class":176},"WriteFile",[71,655,211],{"class":77},[71,657,214],{"class":77},[71,659,660],{"class":217},"report.pdf",[71,662,214],{"class":77},[71,664,193],{"class":77},[71,666,667],{"class":189}," data",[71,669,193],{"class":77},[71,671,672],{"class":342}," 0o644",[71,674,675],{"class":77},");",[71,677,196],{"class":189},[71,679,232],{"class":77},[71,681,235],{"class":77},[71,683,183],{"class":77},[71,685,687,689,691,693,695,697],{"class":73,"line":686},38,[71,688,243],{"class":189},[71,690,205],{"class":77},[71,692,248],{"class":176},[71,694,211],{"class":77},[71,696,253],{"class":189},[71,698,162],{"class":77},[71,700,702],{"class":73,"line":701},39,[71,703,261],{"class":77},[71,705,707],{"class":73,"line":706},40,[71,708,709],{"class":77},"}\n",[19,711,712,713,716,717,720,721,205],{},"Coloque o TTF ao lado de ",[43,714,715],{},"main.go",", rode ",[43,718,719],{},"go run main.go"," e você terá um PDF de uma página com japonês em ",[43,722,660],{},[14,724,726],{"id":725},"source-han-sans-jp-é-noto-sans-cjk-jp","Source Han Sans JP é Noto Sans CJK JP",[19,728,729,730,733,734,737],{},"O fato que poupa horas de leitura: ",[23,731,732],{},"Source Han Sans e Noto Sans CJK são as mesmas fontes",". A Adobe fez o desenho dos glifos, as tabelas de métricas e a cobertura de caracteres. O Google tocou um canal de distribuição paralelo sob o guarda-chuva Noto. Ambos foram lançados em 2014-07-15. Os contornos, a tabela ",[43,735,736],{},"hmtx"," e a cobertura de JIS X 0213 / Adobe-Japan1-6 são idênticos, bit a bit. Quando a Adobe publica uma nova versão, as mudanças de glifo chegam ao Noto em poucas semanas.",[19,739,740],{},"A diferença está na marca e no empacotamento:",[742,743,744,758],"table",{},[745,746,747],"thead",{},[748,749,750,753,755],"tr",{},[751,752],"th",{},[751,754,25],{},[751,756,757],{},"Noto Sans JP",[759,760,761,773,793,804,815],"tbody",{},[748,762,763,767,770],{},[764,765,766],"td",{},"Publica",[764,768,769],{},"Adobe",[764,771,772],{},"Google",[748,774,775,778,785],{},[764,776,777],{},"Fonte canônica",[764,779,780],{},[28,781,784],{"href":782,"rel":783},"https://github.com/adobe-fonts/source-han-sans",[32],"adobe-fonts/source-han-sans",[764,786,787,792],{},[28,788,791],{"href":789,"rel":790},"https://notofonts.github.io",[32],"notofonts.github.io"," + Google Fonts",[748,794,795,798,801],{},[764,796,797],{},"Formato padrão",[764,799,800],{},"OTF (contornos CFF)",[764,802,803],{},"TTF (estático) + variável",[748,805,806,809,812],{},[764,807,808],{},"Modelo de release",[764,810,811],{},"Tags do GitHub, versionamento manual",[764,813,814],{},"CDN do Google Fonts + repositórios git",[748,816,817,820,823],{},[764,818,819],{},"Bundling por idioma",[764,821,822],{},"TTF por idioma + OTC pan-CJK",[764,824,825],{},"Apenas JP",[19,827,828,829,832,833,205],{},"Use Source Han Sans JP se seu time fixa fontes em tags do GitHub da Adobe, já mantém um mirror interno de ",[43,830,831],{},"github.com/adobe-fonts",", ou precisa do bundle OTC pan-CJK em outro pipeline. Use Noto Sans JP se quiser o caminho mais curto para um TTF. Esse caminho está descrito na ",[28,834,836],{"href":835},"/pt/blog/noto-sans-jp-with-gpdf","receita de Noto Sans JP",[14,838,840],{"id":839},"por-que-ttf-e-não-otf","Por que TTF, e não OTF",[19,842,843,844,847,848,851,852,855,856,855,859,855,862,864,865,868,869,872,873,875],{},"O ativo padrão da Adobe para Source Han Sans é ",[43,845,846],{},".otf"," — especificamente OpenType baseado em CFF. O parser de fontes do gpdf vive em um único arquivo (",[43,849,850],{},"pdf/font/truetype.go",") e lê ",[43,853,854],{},"glyf",", ",[43,857,858],{},"loca",[43,860,861],{},"cmap",[43,863,736],{}," e glifos compostos. Ele não lê contornos ",[43,866,867],{},"CFF "," / ",[43,870,871],{},"CFF2",". Passe um ",[43,874,846],{}," baseado em CFF e o parser rejeita o arquivo já na construção do documento, bem antes de renderizar.",[19,877,878,879,882],{},"A página de releases da Adobe publica tanto OTF quanto TTF. Pegue o ",[23,880,881],{},"pacote TTF",". Se em algum release pontual só houver OTF (acontece de vez em quando), duas alternativas limpas:",[884,885,886,893],"ol",{},[887,888,889,892],"li",{},[23,890,891],{},"Troque para Noto Sans JP."," O Google Fonts serve os TTFs estáticos direto; os dados de glifo são idênticos. Zero conversão, mesmo resultado.",[887,894,895,898,899,902,903,906],{},[23,896,897],{},"Converta uma vez e faça commit do resultado."," ",[43,900,901],{},"fonttools"," (",[43,904,905],{},"otf2ttf",") produz um TTF em um minuto. Suba o resultado para o repo ou para um artifact server interno; a conversão nunca passa pelo seu pipeline de build.",[19,908,909,910,912,913,205],{},"Evite converter em tempo de build. Ferramentas de conversão de fonte mudam de comportamento entre versões, e uma tabela ",[43,911,736],{}," ligeiramente diferente desloca suas quebras de linha depois de um simples ",[43,914,915],{},"pip install -U",[14,917,919],{"id":918},"os-sete-pesos","Os sete pesos",[19,921,922],{},"Source Han Sans JP publica de ExtraLight a Heavy, um arquivo por peso:",[62,924,929],{"className":925,"code":927,"language":928},[926],"language-text","SourceHanSansJP-ExtraLight.ttf\nSourceHanSansJP-Light.ttf\nSourceHanSansJP-Normal.ttf\nSourceHanSansJP-Regular.ttf\nSourceHanSansJP-Medium.ttf\nSourceHanSansJP-Bold.ttf\nSourceHanSansJP-Heavy.ttf\n","text",[43,930,927],{"__ignoreMap":67},[19,932,933],{},"Para a maioria dos documentos de negócio, Regular e Bold dão conta:",[62,935,937],{"className":64,"code":936,"language":66,"meta":67,"style":67},"reg,  _ := os.ReadFile(\"SourceHanSansJP-Regular.ttf\")\nbold, _ := os.ReadFile(\"SourceHanSansJP-Bold.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"SourceHanSansJP\", reg),\n    gpdf.WithFont(\"SourceHanSansJP-Bold\", bold),\n    gpdf.WithDefaultFont(\"SourceHanSansJP\", 11),\n)\n",[43,938,939,967,996,1000,1015,1039,1063,1085],{"__ignoreMap":67},[71,940,941,944,946,949,951,953,955,957,959,961,963,965],{"class":73,"line":74},[71,942,943],{"class":189},"reg",[71,945,193],{"class":77},[71,947,948],{"class":189},"  _ ",[71,950,199],{"class":77},[71,952,202],{"class":189},[71,954,205],{"class":77},[71,956,208],{"class":176},[71,958,211],{"class":77},[71,960,214],{"class":77},[71,962,45],{"class":217},[71,964,214],{"class":77},[71,966,162],{"class":77},[71,968,969,972,974,977,979,981,983,985,987,989,992,994],{"class":73,"line":85},[71,970,971],{"class":189},"bold",[71,973,193],{"class":77},[71,975,976],{"class":189}," _ ",[71,978,199],{"class":77},[71,980,202],{"class":189},[71,982,205],{"class":77},[71,984,208],{"class":176},[71,986,211],{"class":77},[71,988,214],{"class":77},[71,990,991],{"class":217},"SourceHanSansJP-Bold.ttf",[71,993,214],{"class":77},[71,995,162],{"class":77},[71,997,998],{"class":73,"line":92},[71,999,89],{"emptyLinePlaceholder":88},[71,1001,1002,1005,1007,1009,1011,1013],{"class":73,"line":102},[71,1003,1004],{"class":189},"doc ",[71,1006,199],{"class":77},[71,1008,277],{"class":189},[71,1010,205],{"class":77},[71,1012,282],{"class":176},[71,1014,285],{"class":77},[71,1016,1017,1020,1022,1024,1026,1028,1030,1032,1034,1037],{"class":73,"line":114},[71,1018,1019],{"class":189},"    gpdf",[71,1021,205],{"class":77},[71,1023,356],{"class":176},[71,1025,211],{"class":77},[71,1027,214],{"class":77},[71,1029,363],{"class":217},[71,1031,214],{"class":77},[71,1033,193],{"class":77},[71,1035,1036],{"class":189}," reg",[71,1038,308],{"class":77},[71,1040,1041,1043,1045,1047,1049,1051,1054,1056,1058,1061],{"class":73,"line":124},[71,1042,1019],{"class":189},[71,1044,205],{"class":77},[71,1046,356],{"class":176},[71,1048,211],{"class":77},[71,1050,214],{"class":77},[71,1052,1053],{"class":217},"SourceHanSansJP-Bold",[71,1055,214],{"class":77},[71,1057,193],{"class":77},[71,1059,1060],{"class":189}," bold",[71,1062,308],{"class":77},[71,1064,1065,1067,1069,1071,1073,1075,1077,1079,1081,1083],{"class":73,"line":129},[71,1066,1019],{"class":189},[71,1068,205],{"class":77},[71,1070,382],{"class":176},[71,1072,211],{"class":77},[71,1074,214],{"class":77},[71,1076,363],{"class":217},[71,1078,214],{"class":77},[71,1080,193],{"class":77},[71,1082,395],{"class":342},[71,1084,308],{"class":77},[71,1086,1087],{"class":73,"line":139},[71,1088,162],{"class":77},[19,1090,1091,1092,1095,1096,1099,1100,1102],{},"O sufixo ",[43,1093,1094],{},"-Bold"," é o contrato que liga ",[43,1097,1098],{},"template.Bold()"," ao TTF Bold. Sem esse registro, ",[43,1101,1098],{}," cai no negrito sintetizado — um traço sobreposto sobre os glifos Regular. Legível para cabeçalhos de tabela, mas em corpos grandes fica visivelmente mais fino do que o contorno Bold real.",[19,1104,1105],{},"Fontes CJK, por convenção, não publicam variantes itálicas, e Source Han Sans JP não é exceção. Se seu layout pede ênfase itálica em uma linha em japonês, vá de peso ou cor — uma transformação oblíqua em glifos CJK parece quebrada, não ênfase.",[14,1107,1109],{"id":1108},"pan-cjk-só-jp-ou-o-super-otc","Pan-CJK, só JP, ou o Super OTC",[19,1111,1112],{},"A Adobe publica Source Han Sans em vários níveis de granularidade. Eles não são intercambiáveis para um gerador de PDF em Go:",[1114,1115,1116,1129,1139],"ul",{},[887,1117,1118,1121,1122,1125,1126,1128],{},[23,1119,1120],{},"SourceHanSans.ttc"," (Super OTC) — todos os idiomas CJK em um TrueType Collection de 20 MB+. O gpdf não seleciona o face index dentro de um ",[43,1123,1124],{},".ttc","; você teria que fatiar a face JP primeiro com ",[43,1127,901],{}," e registrar o resultado. Ignore o Super OTC.",[887,1130,1131,1134,1135,1138],{},[23,1132,1133],{},"OTF regional"," (ex.: ",[43,1136,1137],{},"SourceHanSans-Regular.otf",") — scripts CJK unificados, contornos CFF. O gpdf não lê.",[887,1140,1141,902,1144,1146,1147,1149],{},[23,1142,1143],{},"TTF por idioma",[43,1145,45],{},") — só JP, contornos ",[43,1148,854],{},". É o que você quer.",[19,1151,1152,1153,1155,1156,1159,1160,1163],{},"Se seu documento mistura japonês com coreano ou chinês na mesma página, registre famílias específicas por idioma lado a lado: ",[43,1154,363],{}," e ",[43,1157,1158],{},"SourceHanSansKR",". Troque explicitamente com ",[43,1161,1162],{},"template.FontFamily"," onde o script muda. Apelar para o OTF pan-CJK \"resolveria\" com Han unification, mas isso traz suas próprias surpresas para leitores que esperam kanji com forma JP em texto JP.",[14,1165,1167],{"id":1166},"quando-escolher-source-han-em-vez-de-noto","Quando escolher Source Han em vez de Noto",[19,1169,1170],{},"Mesmos contornos, canal de distribuição diferente. Source Han Sans JP faz sentido quando:",[1114,1172,1173,1176,1182],{},[887,1174,1175],{},"Seu time de ops prefere prender fontes a tags de release da Adobe por reprodutibilidade",[887,1177,1178,1179,1181],{},"Você já mantém mirror interno de ",[43,1180,831],{}," (comum em empresas com políticas restritas de artefatos)",[887,1183,1184],{},"O bundle OTC pan-CJK é útil em outra etapa do pipeline — um fluxo DTP, um handoff para design, um sistema de marca padronizado no nome da Adobe",[19,1186,1187],{},"Noto Sans JP é melhor quando:",[1114,1189,1190,1197,1200],{},[887,1191,1192,1193,1196],{},"Você quer o caminho mais curto para um TTF (",[43,1194,1195],{},"fonts.google.com/noto/specimen/Noto+Sans+JP"," → zip → pronto)",[887,1198,1199],{},"Não quer meter conversão OTF → TTF no seu build",[887,1201,1202],{},"Seu projeto já puxa outras Google Fonts por um workflow existente",[19,1204,1205],{},"O PDF renderizado fica igual dos dois jeitos. A decisão é operacional — onde o arquivo mora, como você versiona, como o time de ops se sente com isso — não estética.",[14,1207,1209],{"id":1208},"leituras-relacionadas","Leituras relacionadas",[1114,1211,1212,1218,1225,1232],{},[887,1213,1214,1217],{},[28,1215,1216],{"href":835},"Como usar Noto Sans JP com o gpdf?"," — os mesmos glifos, já publicados como TTF",[887,1219,1220,1224],{},[28,1221,1223],{"href":1222},"/pt/blog/embed-japanese-font","Como incorporar uma fonte japonesa no gpdf?"," — a receita geral de incorporação CJK",[887,1226,1227,1231],{},[28,1228,1230],{"href":1229},"/pt/blog/ipaex-gothic-gpdf","Como usar IPAex Gothic no gpdf?"," — a alternativa com licença IPA para envios institucionais japoneses",[887,1233,1234,1238],{},[28,1235,1237],{"href":1236},"/pt/blog/tofu-boxes-japanese","Por que meu PDF mostra retângulos no lugar de japonês?"," — troubleshooting quando os glifos não aparecem",[14,1240,1242],{"id":1241},"experimente-o-gpdf","Experimente o gpdf",[19,1244,1245],{},"gpdf é uma biblioteca Go para gerar PDFs. Licença MIT, zero dependências externas, suporte nativo a CJK.",[62,1247,1251],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[43,1252,1253],{"__ignoreMap":67},[71,1254,1255,1257,1260],{"class":73,"line":74},[71,1256,66],{"class":81},[71,1258,1259],{"class":217}," get",[71,1261,1262],{"class":217}," github.com/gpdf-dev/gpdf\n",[19,1264,1265,1269,1270],{},[28,1266,1268],{"href":30,"rel":1267},[32],"⭐ Star no GitHub"," · ",[28,1271,1274],{"href":1272,"rel":1273},"https://gpdf.dev/pt/docs/quickstart",[32],"Ler a documentação",[1276,1277,1278],"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 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 .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":67,"searchDepth":85,"depth":85,"links":1280},[1281,1282,1283,1284,1285,1286,1287,1288,1289,1290],{"id":16,"depth":85,"text":17},{"id":37,"depth":85,"text":38},{"id":59,"depth":85,"text":60},{"id":725,"depth":85,"text":726},{"id":839,"depth":85,"text":840},{"id":918,"depth":85,"text":919},{"id":1108,"depth":85,"text":1109},{"id":1166,"depth":85,"text":1167},{"id":1208,"depth":85,"text":1209},{"id":1241,"depth":85,"text":1242},"2026-04-19","Registre a variante TTF de Source Han Sans JP do release no GitHub da Adobe com gpdf.WithFont. Sete pesos, SIL OFL, mesmos glifos que Noto Sans JP.",false,"md",{"name":1296,"totalTime":1297,"tools":1298,"steps":1301},"Usar Source Han Sans JP como fonte padrão em um documento gpdf","PT15M",[1299,1300],"Go 1.22+","SourceHanSansJP-Regular.ttf de adobe-fonts/source-han-sans",[1302,1305,1308,1311,1314],{"name":1303,"text":1304},"Baixe o TTF do release no GitHub da Adobe","Abra github.com/adobe-fonts/source-han-sans/releases. A partir do release mais recente, baixe o pacote TTF (não o OTF nem o SuperOTC) e extraia SourceHanSansJP-Regular.ttf. O gpdf faz parse de TrueType, não de OpenType com contornos CFF.",{"name":1306,"text":1307},"Carregue os bytes na inicialização","Use os.ReadFile(\"SourceHanSansJP-Regular.ttf\") ou //go:embed. Para reprodutibilidade em CI, fixe uma tag específica do release da Adobe no seu repositório em vez de baixar em tempo de build.",{"name":1309,"text":1310},"Registre a fonte ao construir o documento","Passe gpdf.WithFont(\"SourceHanSansJP\", fontBytes) e gpdf.WithDefaultFont(\"SourceHanSansJP\", 11) para gpdf.NewDocument. Sem AddUTF8Font, sem caminho de filesystem.",{"name":1312,"text":1313},"Registre pesos adicionais quando precisar","Source Han Sans JP publica sete pesos, de ExtraLight a Heavy. Registre o TTF Bold com a chave SourceHanSansJP-Bold e o template.Bold() passará a usar os contornos reais de negrito em vez de um traço sintetizado.",{"name":1315,"text":1316},"Mantenha OFL.txt junto à sua distribuição","A SIL OFL 1.1 exige que o texto da licença viaje junto com o binário da fonte. Se você //go:embed o TTF, embuta também OFL.txt em LICENSES/ e referencie-o no NOTICE.",null,{},"/pt/blog/source-han-sans-jp-with-gpdf",{"title":6,"description":1292},"pt/blog/010.source-han-sans-jp-with-gpdf",[1323,1324,1325],"recipe","cjk","tutorial","7Xg0MOgX_0_muENkAHkp8ohMv_F3FA86sF5Uyn9bRLE",{"id":1328,"title":1329,"author":1330,"body":1331,"date":1291,"description":2910,"draft":1293,"extension":1294,"howTo":1317,"image":1317,"meta":2911,"navigation":88,"path":2912,"seo":2913,"stem":2914,"tags":2915,"updated":1317,"__hash__":2919},"blogPt/pt/blog/011.why-gpdf-is-faster.md","Por que gpdf é 10–30 vezes mais rápido que outras bibliotecas Go PDF",{"name":8,"url":9},{"type":11,"value":1332,"toc":2888},[1333,1335,1358,1382,1385,1396,1399,1403,1410,1519,1529,1532,1539,1543,1546,1552,1563,1569,1576,1873,1908,1931,1934,1939,1942,1960,1967,1977,1980,1987,1990,1997,2094,2123,2126,2129,2275,2281,2291,2295,2301,2307,2313,2316,2322,2325,2329,2339,2342,2346,2349,2352,2355,2381,2388,2391,2398,2527,2530,2534,2547,2550,2554,2567,2574,2578,2581,2659,2662,2665,2669,2672,2678,2688,2702,2711,2714,2718,2721,2761,2770,2773,2784,2788,2798,2804,2817,2823,2829,2833,2836,2848,2858,2862,2885],[14,1334,38],{"id":37},[19,1336,1337,1338,1341,1342,1345,1346,1349,1350,1353,1354,1357],{},"gpdf gera uma página em ",[23,1339,1340],{},"13 µs",", uma tabela de fatura 4×10 em ",[23,1343,1344],{},"108 µs"," e um relatório paginado de 100 páginas em ",[23,1347,1348],{},"683 µs",". A próxima biblioteca Go PDF mais rápida em manutenção — ",[43,1351,1352],{},"jung-kurt/gofpdf"," — faz as mesmas 100 páginas em ",[23,1355,1356],{},"11.7 ms",", cerca de 17× mais lento. Não é diferença de tuning. São três decisões de design que empilham:",[884,1359,1360,1366,1376],{},[887,1361,1362,1365],{},[23,1363,1364],{},"Layout em passagem única."," Nenhuma AST intermediária entre a API Builder e o fluxo de conteúdo PDF.",[887,1367,1368,1371,1372,1375],{},[23,1369,1370],{},"Tipos concretos no caminho quente."," Sem reflexão, sem ",[43,1373,1374],{},"interface{}",", sem dispatch virtual dentro do loop de layout.",[887,1377,1378,1381],{},[23,1379,1380],{},"Um subsetter TrueType que resolve o cmap uma vez."," Não uma vez por glifo. Não uma vez por página. Uma vez.",[19,1383,1384],{},"Qualquer um dos três dá 2–3×. Empilhados, dá uma ordem de grandeza.",[19,1386,1387,1388,1395],{},"Este artigo percorre o caminho de código que produz esses números. O fonte do benchmark está público — ",[28,1389,1392],{"href":1390,"rel":1391},"https://github.com/gpdf-dev/gpdf/tree/main/_benchmark",[32],[43,1393,1394],{},"_benchmark/benchmark_test.go"," — clone, rode na sua máquina, abra um issue se os números discordarem.",[19,1397,1398],{},"Aviso de viés adiantado: somos o time do gpdf. A versão honesta de \"somos mais rápidos\" é \"tomamos um conjunto diferente de trade-offs\", e a pergunta interessante é o que abrimos mão para chegar aqui. Essa é a segunda metade do artigo.",[14,1400,1402],{"id":1401},"o-que-significa-rápido-aqui","O que significa \"rápido\" aqui",[19,1404,1405,1406,1409],{},"Antes da arquitetura, o placar que vamos explicar (Apple M1, Go 1.25, sem CGO, ",[43,1407,1408],{},"-benchmem"," ativo):",[742,1411,1412,1433],{},[745,1413,1414],{},[748,1415,1416,1419,1421,1424,1427,1430],{},[751,1417,1418],{},"Carga de trabalho",[751,1420,33],{},[751,1422,1423],{},"gofpdf",[751,1425,1426],{},"go-pdf/fpdf",[751,1428,1429],{},"signintech/gopdf",[751,1431,1432],{},"Maroto v2",[759,1434,1435,1456,1477,1497],{},[748,1436,1437,1440,1444,1447,1450,1453],{},[764,1438,1439],{},"Página única Hello World",[764,1441,1442],{},[23,1443,1340],{},[764,1445,1446],{},"132 µs",[764,1448,1449],{},"135 µs",[764,1451,1452],{},"423 µs",[764,1454,1455],{},"237 µs",[748,1457,1458,1461,1465,1468,1471,1474],{},[764,1459,1460],{},"Tabela de fatura 4×10",[764,1462,1463],{},[23,1464,1344],{},[764,1466,1467],{},"241 µs",[764,1469,1470],{},"243 µs",[764,1472,1473],{},"835 µs",[764,1475,1476],{},"8,600 µs",[748,1478,1479,1482,1486,1489,1492,1494],{},[764,1480,1481],{},"Relatório paginado 100 páginas",[764,1483,1484],{},[23,1485,1348],{},[764,1487,1488],{},"11,700 µs",[764,1490,1491],{},"11,900 µs",[764,1493,1476],{},[764,1495,1496],{},"19,800 µs",[748,1498,1499,1502,1507,1510,1513,1516],{},[764,1500,1501],{},"Fatura CJK complexa",[764,1503,1504],{},[23,1505,1506],{},"133 µs",[764,1508,1509],{},"254 µs",[764,1511,1512],{},"n/a",[764,1514,1515],{},"997 µs",[764,1517,1518],{},"10,400 µs",[19,1520,1521,1522,1525,1526,1528],{},"Duas formas visíveis antes de explicar. A distância ",[23,1523,1524],{},"aumenta"," com o número de páginas — 10× num hello world, 17× em 100 páginas. E a distância ",[23,1527,1524],{}," com a complexidade — 108 µs para uma tabela contra 8.6 ms para a mesma tabela pelo backend gofpdf do Maroto.",[19,1530,1531],{},"As duas formas vêm da mesma raiz: o custo por elemento no gpdf é quase plano, porque o loop de layout não aloca no caminho comum. O porquê vem a seguir.",[19,1533,1534,1535,1538],{},"Aviso rápido que ninguém quer ler mas escrevemos mesmo assim: ",[23,1536,1537],{},"velocidade absoluta importa menos do que as pessoas pensam para a maioria das cargas de PDF",". Se seu maior documento é um recibo de uma página, qualquer biblioteca mantida dessa tabela é rápida o bastante para gerar no caminho da requisição. O limiar que importa é \"consigo gerar 100 desses síncronos em lote sem empurrar pra fila\", e é aí que a distância começa a contar.",[14,1540,1542],{"id":1541},"decisão-1-sem-ast-intermediária","Decisão 1: Sem AST intermediária",[19,1544,1545],{},"A maioria das bibliotecas Builder de PDF funciona assim:",[62,1547,1550],{"className":1548,"code":1549,"language":928},[926],"API Builder → árvore de documento (AST) → passe de layout → serializador → bytes\n",[43,1551,1549],{"__ignoreMap":67},[19,1553,1554,1555,1558,1559,1562],{},"O passo da árvore de documento é o problema. Cada chamada ",[43,1556,1557],{},".Text()"," aloca um nó. Cada ",[43,1560,1561],{},".Row()"," aloca um container. O passe de layout anda a árvore para calcular posições. Então o serializador anda de novo para emitir bytes. Três passes, três conjuntos de alocações, três viagens pelos mesmos dados pelo cache de CPU.",[19,1564,1565,1566,205],{},"gpdf não tem o passo 2. O Builder escreve direto em um contexto de layout que escreve direto no fluxo de conteúdo. ",[23,1567,1568],{},"Uma passagem",[19,1570,1571,1572,1575],{},"Eis o caminho concreto para um elemento de texto, editado por espaço (a versão real fica em ",[43,1573,1574],{},"template/col_builder.go","):",[62,1577,1579],{"className":64,"code":1578,"language":66,"meta":67,"style":67},"func (c *ColBuilder) Text(s string, opts ...TextOption) {\n    opt := c.resolveOptions(opts)\n    box := c.currentBox()\n    w := c.measureText(s, opt)\n    h := opt.FontSize.Pt() * opt.LineHeight\n    c.writer.BeginText()\n    c.writer.SetFont(opt.Font, opt.FontSize)\n    c.writer.MoveTo(box.X, box.Y-opt.FontSize.Pt())\n    c.writer.ShowString(s)\n    c.writer.EndText()\n    c.advance(w, h)\n}\n",[43,1580,1581,1624,1646,1662,1687,1716,1733,1766,1814,1833,1848,1869],{"__ignoreMap":67},[71,1582,1583,1585,1587,1590,1593,1595,1597,1600,1602,1605,1609,1611,1614,1617,1620,1622],{"class":73,"line":74},[71,1584,173],{"class":77},[71,1586,902],{"class":77},[71,1588,1589],{"class":444},"c ",[71,1591,1592],{"class":77},"*",[71,1594,494],{"class":81},[71,1596,459],{"class":77},[71,1598,1599],{"class":176}," Text",[71,1601,211],{"class":77},[71,1603,1604],{"class":444},"s",[71,1606,1608],{"class":1607},"spNyl"," string",[71,1610,193],{"class":77},[71,1612,1613],{"class":444}," opts",[71,1615,1616],{"class":77}," ...",[71,1618,1619],{"class":81},"TextOption",[71,1621,459],{"class":77},[71,1623,183],{"class":77},[71,1625,1626,1629,1631,1634,1636,1639,1641,1644],{"class":73,"line":85},[71,1627,1628],{"class":189},"    opt ",[71,1630,199],{"class":77},[71,1632,1633],{"class":189}," c",[71,1635,205],{"class":77},[71,1637,1638],{"class":176},"resolveOptions",[71,1640,211],{"class":77},[71,1642,1643],{"class":189},"opts",[71,1645,162],{"class":77},[71,1647,1648,1651,1653,1655,1657,1660],{"class":73,"line":92},[71,1649,1650],{"class":189},"    box ",[71,1652,199],{"class":77},[71,1654,1633],{"class":189},[71,1656,205],{"class":77},[71,1658,1659],{"class":176},"currentBox",[71,1661,427],{"class":77},[71,1663,1664,1667,1669,1671,1673,1676,1678,1680,1682,1685],{"class":73,"line":102},[71,1665,1666],{"class":189},"    w ",[71,1668,199],{"class":77},[71,1670,1633],{"class":189},[71,1672,205],{"class":77},[71,1674,1675],{"class":176},"measureText",[71,1677,211],{"class":77},[71,1679,1604],{"class":189},[71,1681,193],{"class":77},[71,1683,1684],{"class":189}," opt",[71,1686,162],{"class":77},[71,1688,1689,1692,1694,1696,1698,1700,1702,1705,1707,1709,1711,1713],{"class":73,"line":114},[71,1690,1691],{"class":189},"    h ",[71,1693,199],{"class":77},[71,1695,1684],{"class":189},[71,1697,205],{"class":77},[71,1699,528],{"class":189},[71,1701,205],{"class":77},[71,1703,1704],{"class":176},"Pt",[71,1706,180],{"class":77},[71,1708,448],{"class":77},[71,1710,1684],{"class":189},[71,1712,205],{"class":77},[71,1714,1715],{"class":189},"LineHeight\n",[71,1717,1718,1721,1723,1726,1728,1731],{"class":73,"line":124},[71,1719,1720],{"class":189},"    c",[71,1722,205],{"class":77},[71,1724,1725],{"class":189},"writer",[71,1727,205],{"class":77},[71,1729,1730],{"class":176},"BeginText",[71,1732,427],{"class":77},[71,1734,1735,1737,1739,1741,1743,1746,1748,1751,1753,1756,1758,1760,1762,1764],{"class":73,"line":129},[71,1736,1720],{"class":189},[71,1738,205],{"class":77},[71,1740,1725],{"class":189},[71,1742,205],{"class":77},[71,1744,1745],{"class":176},"SetFont",[71,1747,211],{"class":77},[71,1749,1750],{"class":189},"opt",[71,1752,205],{"class":77},[71,1754,1755],{"class":189},"Font",[71,1757,193],{"class":77},[71,1759,1684],{"class":189},[71,1761,205],{"class":77},[71,1763,528],{"class":189},[71,1765,162],{"class":77},[71,1767,1768,1770,1772,1774,1776,1779,1781,1784,1786,1789,1791,1794,1796,1799,1802,1804,1806,1808,1810,1812],{"class":73,"line":139},[71,1769,1720],{"class":189},[71,1771,205],{"class":77},[71,1773,1725],{"class":189},[71,1775,205],{"class":77},[71,1777,1778],{"class":176},"MoveTo",[71,1780,211],{"class":77},[71,1782,1783],{"class":189},"box",[71,1785,205],{"class":77},[71,1787,1788],{"class":189},"X",[71,1790,193],{"class":77},[71,1792,1793],{"class":189}," box",[71,1795,205],{"class":77},[71,1797,1798],{"class":189},"Y",[71,1800,1801],{"class":77},"-",[71,1803,1750],{"class":189},[71,1805,205],{"class":77},[71,1807,528],{"class":189},[71,1809,205],{"class":77},[71,1811,1704],{"class":176},[71,1813,546],{"class":77},[71,1815,1816,1818,1820,1822,1824,1827,1829,1831],{"class":73,"line":149},[71,1817,1720],{"class":189},[71,1819,205],{"class":77},[71,1821,1725],{"class":189},[71,1823,205],{"class":77},[71,1825,1826],{"class":176},"ShowString",[71,1828,211],{"class":77},[71,1830,1604],{"class":189},[71,1832,162],{"class":77},[71,1834,1835,1837,1839,1841,1843,1846],{"class":73,"line":159},[71,1836,1720],{"class":189},[71,1838,205],{"class":77},[71,1840,1725],{"class":189},[71,1842,205],{"class":77},[71,1844,1845],{"class":176},"EndText",[71,1847,427],{"class":77},[71,1849,1850,1852,1854,1857,1859,1862,1864,1867],{"class":73,"line":165},[71,1851,1720],{"class":189},[71,1853,205],{"class":77},[71,1855,1856],{"class":176},"advance",[71,1858,211],{"class":77},[71,1860,1861],{"class":189},"w",[71,1863,193],{"class":77},[71,1865,1866],{"class":189}," h",[71,1868,162],{"class":77},[71,1870,1871],{"class":73,"line":170},[71,1872,709],{"class":77},[19,1874,1875,1876,1879,1880,1883,1884,1887,1888,868,1890,868,1892,1894,1895,855,1898,855,1901,855,1904,1907],{},"Nenhum nó vai pra árvore. Nenhuma posição é adiada. O writer é um ",[43,1877,1878],{},"*pdf.Writer"," que guarda um ",[43,1881,1882],{},"io.Writer"," (tipicamente um ",[43,1885,1886],{},"bytes.Buffer","), e ",[43,1889,1730],{},[43,1891,1778],{},[43,1893,1826],{}," escrevem os operadores PDF (",[43,1896,1897],{},"BT",[43,1899,1900],{},"Td",[43,1902,1903],{},"Tj",[43,1905,1906],{},"ET",") imediatamente pro buffer.",[19,1909,1910,1911,1914,1915,1918,1919,1922,1923,1926,1927,1930],{},"Compare com como o gofpdf faz a mesma operação lógica. O gofpdf mantém um objeto ",[43,1912,1913],{},"page"," com um slice de operações. Cada chamada ",[43,1916,1917],{},"SetXY"," + ",[43,1920,1921],{},"Cell"," anexa a esse slice. ",[43,1924,1925],{},"Output"," (ou ",[43,1928,1929],{},"OutputFileAndClose",") anda o slice no fim e emite os bytes. Duas alocações por célula — uma pela struct de operação, outra pela cópia de string — mais um passe extra sobre os dados.",[19,1932,1933],{},"Para um relatório de 100 páginas com ~40 linhas por página, são 4,000 alocações extras que o gpdf não faz.",[1935,1936,1938],"h3",{"id":1937},"onde-dói-a-passagem-única","Onde dói a passagem única",[19,1940,1941],{},"A pergunta óbvia: como fazer qualquer coisa que precisa conhecer o layout final da página antes de começar a emitir bytes? Cabeçalhos com números de página. Tabelas que atravessam páginas. Rodapés ancorados na última linha do corpo.",[19,1943,1944,1945,1948,1949,1952,1953,855,1956,1959],{},"Duas respostas. Uma, bufferizamos a ",[23,1946,1947],{},"página"," atual, não o documento. Uma página é uma unidade limitada — dezenas de KB, não megabytes. Quando o próximo ",[43,1950,1951],{},"AddPage()"," roda, o fluxo de conteúdo da página atual é finalizado (",[43,1954,1955],{},"Length",[43,1957,1958],{},"Filter",", offsets), sua entrada xref é escrita, e o buffer de página é resetado. O pico de memória fica em O(uma página).",[19,1961,1962,1963,1966],{},"Dois, para elementos genuinamente globais (\"Page 3 of 27\"), adiamos ",[23,1964,1965],{},"aquele trecho específico"," para um passe de fix-up. O resto do conteúdo já está no fluxo. O fix-up percorre uma lista curta de marcadores deferred-reference e aplica patch. Esse é o único lugar na base de código onde pagamos algo parecido com custo de AST, e só pagamos para o conteúdo que realmente precisa.",[19,1968,1969,1970,1972,1973,1976],{},"O trade: você não pode fazer pós-processamento arbitrário numa árvore de nós, porque não há árvore de nós. Você não pode escrever um plugin que reordene \"todos os nós ",[43,1971,509],{}," com ",[43,1974,1975],{},"bold: true","\". Se precisar desse formato de API, Maroto v2 faz; gpdf não.",[19,1978,1979],{},"Achamos que é o trade certo para os casos de uso que gpdf mira. A maioria dos PDFs é produzida da esquerda pra direita, de cima pra baixo, em layout conhecido na hora da construção. O custo de manter uma AST para a minoria de casos que precisa foi pago em toda página pela maioria. Invertemos essa proporção.",[14,1981,1983,1984,1986],{"id":1982},"decisão-2-sem-reflexão-sem-interface-no-caminho-quente","Decisão 2: Sem reflexão, sem ",[43,1985,1374],{}," no caminho quente",[19,1988,1989],{},"Escrever sobre isso é menos interessante do que perfilar. Mas é de onde vem a outra metade da velocidade.",[19,1991,1992,1993,1996],{},"Veja a assinatura de ",[43,1994,1995],{},"CellFormat"," do gofpdf:",[62,1998,2000],{"className":64,"code":1999,"language":66,"meta":67,"style":67},"func (f *Fpdf) CellFormat(w, h float64, txtStr, borderStr string,\n    ln int, alignStr string, fill bool, link int, linkStr string) { ... }\n",[43,2001,2002,2047],{"__ignoreMap":67},[71,2003,2004,2006,2008,2011,2013,2016,2018,2021,2023,2025,2027,2029,2032,2034,2037,2039,2042,2044],{"class":73,"line":74},[71,2005,173],{"class":77},[71,2007,902],{"class":77},[71,2009,2010],{"class":444},"f ",[71,2012,1592],{"class":77},[71,2014,2015],{"class":81},"Fpdf",[71,2017,459],{"class":77},[71,2019,2020],{"class":176}," CellFormat",[71,2022,211],{"class":77},[71,2024,1861],{"class":444},[71,2026,193],{"class":77},[71,2028,1866],{"class":444},[71,2030,2031],{"class":1607}," float64",[71,2033,193],{"class":77},[71,2035,2036],{"class":444}," txtStr",[71,2038,193],{"class":77},[71,2040,2041],{"class":444}," borderStr",[71,2043,1608],{"class":1607},[71,2045,2046],{"class":77},",\n",[71,2048,2049,2052,2055,2057,2060,2062,2064,2067,2070,2072,2075,2077,2079,2082,2084,2086,2089,2091],{"class":73,"line":85},[71,2050,2051],{"class":444},"    ln",[71,2053,2054],{"class":1607}," int",[71,2056,193],{"class":77},[71,2058,2059],{"class":444}," alignStr",[71,2061,1608],{"class":1607},[71,2063,193],{"class":77},[71,2065,2066],{"class":444}," fill",[71,2068,2069],{"class":1607}," bool",[71,2071,193],{"class":77},[71,2073,2074],{"class":444}," link",[71,2076,2054],{"class":1607},[71,2078,193],{"class":77},[71,2080,2081],{"class":444}," linkStr",[71,2083,1608],{"class":1607},[71,2085,459],{"class":77},[71,2087,2088],{"class":77}," {",[71,2090,1616],{"class":77},[71,2092,2093],{"class":77}," }\n",[19,2095,2096,2097,2100,2101,2104,2105,2108,2109,2112,2113,2115,2116,2118,2119,2122],{},"Tudo bem. Agora veja a árvore de componentes do Maroto. Um ",[43,2098,2099],{},"Row"," tem ",[43,2102,2103],{},"[]Component",". Um ",[43,2106,2107],{},"Component"," é uma interface. Toda operação de layout é um dispatch virtual: ",[43,2110,2111],{},"component.Render(ctx)",". Para um único ",[43,2114,472],{}," com um ",[43,2117,509],{}," e um ",[43,2120,2121],{},"Spacer",", são três dispatches. Num relatório de 100 páginas com ~30 linhas por página e ~3 componentes por linha, são ~9,000 dispatches.",[19,2124,2125],{},"Individualmente, um dispatch de interface em Go é ~2–3 ns. Não é crime. Mas o dispatch também força o compilador a manter o valor boxed na heap — você não consegue stack-alocar através de uma interface sem um passe de devirtualization que o compilador Go nem sempre faz. Então o custo não é só o dispatch; é a alocação que o alimenta.",[19,2127,2128],{},"O engine de layout do gpdf usa structs concretas:",[62,2130,2132],{"className":64,"code":2131,"language":66,"meta":67,"style":67},"type RowBuilder struct {\n    doc    *Document\n    parent *pageState\n    spans  [12]int\n    cols   [12]ColBuilder  // valor, não ponteiro, não interface\n    n      uint8\n}\n\ntype ColBuilder struct {\n    row    *RowBuilder\n    span   int\n    cursor document.Point\n    writer *pdf.Writer\n}\n",[43,2133,2134,2147,2157,2167,2183,2200,2208,2212,2216,2227,2237,2244,2256,2271],{"__ignoreMap":67},[71,2135,2136,2139,2142,2145],{"class":73,"line":74},[71,2137,2138],{"class":77},"type",[71,2140,2141],{"class":81}," RowBuilder",[71,2143,2144],{"class":77}," struct",[71,2146,183],{"class":77},[71,2148,2149,2152,2154],{"class":73,"line":85},[71,2150,2151],{"class":189},"    doc    ",[71,2153,1592],{"class":77},[71,2155,2156],{"class":81},"Document\n",[71,2158,2159,2162,2164],{"class":73,"line":92},[71,2160,2161],{"class":189},"    parent ",[71,2163,1592],{"class":77},[71,2165,2166],{"class":81},"pageState\n",[71,2168,2169,2172,2175,2177,2180],{"class":73,"line":102},[71,2170,2171],{"class":189},"    spans  ",[71,2173,2174],{"class":77},"[",[71,2176,477],{"class":342},[71,2178,2179],{"class":77},"]",[71,2181,2182],{"class":1607},"int\n",[71,2184,2185,2188,2190,2192,2194,2196],{"class":73,"line":114},[71,2186,2187],{"class":189},"    cols   ",[71,2189,2174],{"class":77},[71,2191,477],{"class":342},[71,2193,2179],{"class":77},[71,2195,494],{"class":81},[71,2197,2199],{"class":2198},"sHwdD","  // valor, não ponteiro, não interface\n",[71,2201,2202,2205],{"class":73,"line":124},[71,2203,2204],{"class":189},"    n      ",[71,2206,2207],{"class":1607},"uint8\n",[71,2209,2210],{"class":73,"line":129},[71,2211,709],{"class":77},[71,2213,2214],{"class":73,"line":139},[71,2215,89],{"emptyLinePlaceholder":88},[71,2217,2218,2220,2223,2225],{"class":73,"line":149},[71,2219,2138],{"class":77},[71,2221,2222],{"class":81}," ColBuilder",[71,2224,2144],{"class":77},[71,2226,183],{"class":77},[71,2228,2229,2232,2234],{"class":73,"line":159},[71,2230,2231],{"class":189},"    row    ",[71,2233,1592],{"class":77},[71,2235,2236],{"class":81},"RowBuilder\n",[71,2238,2239,2242],{"class":73,"line":165},[71,2240,2241],{"class":189},"    span   ",[71,2243,2182],{"class":1607},[71,2245,2246,2249,2251,2253],{"class":73,"line":170},[71,2247,2248],{"class":189},"    cursor ",[71,2250,323],{"class":81},[71,2252,205],{"class":77},[71,2254,2255],{"class":81},"Point\n",[71,2257,2258,2261,2263,2266,2268],{"class":73,"line":186},[71,2259,2260],{"class":189},"    writer ",[71,2262,1592],{"class":77},[71,2264,2265],{"class":81},"pdf",[71,2267,205],{"class":77},[71,2269,2270],{"class":81},"Writer\n",[71,2272,2273],{"class":73,"line":224},[71,2274,709],{"class":77},[19,2276,2277,2280],{},[43,2278,2279],{},"cols"," é um array de valor, dimensionado pelo máximo de colunas (12, do sistema de grid). Sem alocação em heap. Sem dispatch de interface quando a row itera suas colunas. O Builder guarda um ponteiro pro writer, não o contrário — o writer não sabe da existência da árvore do Builder.",[19,2282,2283,2284,2287,2288,2290],{},"O padrão de callback (",[43,2285,2286],{},"r.Col(4, func(c *ColBuilder) { ... })",") não é acidente. Todas as outras formas que prototipamos — uma API que retorna structs encadeáveis, uma árvore de interfaces Component boxed — foram mais lentas. A closure tem zero alocações porque o ",[43,2289,494],{}," é um valor que o chamador mantém por ponteiro via parâmetro; a própria closure é escape-analisada pra pilha no caso comum.",[1935,2292,2294],{"id":2293},"como-sabemos-que-funcionou","Como sabemos que funcionou",[19,2296,2297,2300],{},[43,2298,2299],{},"go test -run=XXX -bench=BenchmarkSinglePage -memprofile=mem.out"," no gpdf dá um número do qual a gente tem orgulho:",[62,2302,2305],{"className":2303,"code":2304,"language":928},[926],"BenchmarkSinglePage-8   91270   13120 ns/op   8321 B/op   52 allocs/op\n",[43,2306,2304],{"__ignoreMap":67},[19,2308,2309,2310,2312],{},"Cinquenta e duas alocações pra uma página PDF inteira. Quase todas são o buffer inicial da página, a busca de métricas de fonte (uma por fonte, não uma por glifo) e o crescimento final do ",[43,2311,1886],{},". O loop de layout aloca zero — olhe o profile.",[19,2314,2315],{},"gofpdf na mesma página:",[62,2317,2320],{"className":2318,"code":2319,"language":928},[926],"BenchmarkGofpdfSinglePage-8   7500   132400 ns/op   71200 B/op   430 allocs/op\n",[43,2321,2319],{"__ignoreMap":67},[19,2323,2324],{},"430 alocações. A maioria é o slice de operações e as cópias de string que o alimentam. Passe essa diferença de ~8× em alocações pelo GC, e a diferença de runtime de ~10× segue mecanicamente.",[1935,2326,2328],{"id":2327},"o-que-cedemos","O que cedemos",[19,2330,2331,2332,2334,2335,2338],{},"Zero ergonomia no caminho quente significa menos pontos de extensão. Se você quer escrever um tipo de elemento customizado que se pluga no layout do gpdf — o equivalente a implementar ",[43,2333,2107],{}," no Maroto — não dá. Não existe interface a satisfazer. O que oferecemos no lugar é ",[43,2336,2337],{},"template.WithWriterSetup()",", que dá um hook no writer PDF para coisas como anotações customizadas, metadados PDF/A ou criptografia. Para extensão de layout, você escreve como helper que chama os mesmos métodos Builder que um usuário chamaria.",[19,2340,2341],{},"Menos pontos de extensão é um custo real. Decidimos que vale a pena. Se a direção do projeto mudar num ponto onde não vale, revisitamos.",[14,2343,2345],{"id":2344},"decisão-3-subsetting-truetype-sem-re-percorrimentos","Decisão 3: Subsetting TrueType sem re-percorrimentos",[19,2347,2348],{},"É aqui que o benchmark CJK (133 µs vs 254 µs do gofpdf) pega a maior parte da distância.",[19,2350,2351],{},"Um resumo rápido do que o subsetting TrueType faz. Quando você embute uma fonte japonesa num PDF, você não quer embutir todos os 20,000+ glifos — são 15 MB de dados de fonte num documento de 100 KB. Você quer embutir só os glifos que seu documento realmente usa, empacotados como um TTF subset válido que um leitor de PDF consiga decodificar.",[19,2353,2354],{},"Para isso:",[884,2356,2357,2372,2375,2378],{},[887,2358,2359,2360,2362,2363,2365,2366,2368,2369,2371],{},"Parse das tabelas TTF completas: ",[43,2361,861],{}," (mapeamento caractere-pra-glifo), ",[43,2364,854],{}," (contornos), ",[43,2367,858],{}," (offsets no glyf), ",[43,2370,736],{}," (métricas horizontais), etc.",[887,2373,2374],{},"Para cada caractere que o documento usa, buscar seu ID de glifo via o cmap.",[887,2376,2377],{},"Coletar transitivamente os glifos que glifos compostos referenciam.",[887,2379,2380],{},"Emitir um TTF novo só com esses glifos, renumerados.",[19,2382,2383,2384,2387],{},"Passo 2 — a busca no cmap — é o caminho quente. A implementação do gofpdf ",[23,2385,2386],{},"percorre a tabela cmap do topo a cada busca de glifo",". Pra uma página só Latin, tudo bem; o cmap é pequeno e o cache se comporta. Pra uma página CJK com 150 glifos únicos, são 150 percorrimentos completos da tabela.",[19,2389,2390],{},"O cmap format 12 (usado por quase toda fonte CJK moderna) é um array ordenado de triplas (start, end, startGlyphID). Um percorrimento é O(n) no número de ranges, ~200–500 para NotoSansJP. 150 buscas de glifo × 400 ranges × comparação por range = muito mais trabalho do que o necessário.",[19,2392,2393,2394,2397],{},"O gpdf resolve o cmap inteiro em um ",[43,2395,2396],{},"map[rune]uint16"," no primeiro load da fonte. Depois disso, cada busca é O(1). Pro NotoSansJP, o custo único é ~150 µs; depois, 10 ns por caractere.",[62,2399,2401],{"className":64,"code":2400,"language":66,"meta":67,"style":67},"// Simplificado de pdf/font/ttf.go\ntype Font struct {\n    runeToGID map[rune]uint16  // resolvido uma vez no load\n    glyphs    []glyph          // indexado por GID\n    metrics   []glyphMetric\n}\n\nfunc (f *Font) GlyphFor(r rune) uint16 {\n    return f.runeToGID[r]  // O(1), cache-friendly, sem percorrer tabela\n}\n",[43,2402,2403,2408,2419,2438,2452,2462,2466,2470,2501,2523],{"__ignoreMap":67},[71,2404,2405],{"class":73,"line":74},[71,2406,2407],{"class":2198},"// Simplificado de pdf/font/ttf.go\n",[71,2409,2410,2412,2415,2417],{"class":73,"line":85},[71,2411,2138],{"class":77},[71,2413,2414],{"class":81}," Font",[71,2416,2144],{"class":77},[71,2418,183],{"class":77},[71,2420,2421,2424,2427,2430,2432,2435],{"class":73,"line":92},[71,2422,2423],{"class":189},"    runeToGID ",[71,2425,2426],{"class":77},"map[",[71,2428,2429],{"class":1607},"rune",[71,2431,2179],{"class":77},[71,2433,2434],{"class":1607},"uint16",[71,2436,2437],{"class":2198},"  // resolvido uma vez no load\n",[71,2439,2440,2443,2446,2449],{"class":73,"line":102},[71,2441,2442],{"class":189},"    glyphs    ",[71,2444,2445],{"class":77},"[]",[71,2447,2448],{"class":81},"glyph",[71,2450,2451],{"class":2198},"          // indexado por GID\n",[71,2453,2454,2457,2459],{"class":73,"line":114},[71,2455,2456],{"class":189},"    metrics   ",[71,2458,2445],{"class":77},[71,2460,2461],{"class":81},"glyphMetric\n",[71,2463,2464],{"class":73,"line":124},[71,2465,709],{"class":77},[71,2467,2468],{"class":73,"line":129},[71,2469,89],{"emptyLinePlaceholder":88},[71,2471,2472,2474,2476,2478,2480,2482,2484,2487,2489,2491,2494,2496,2499],{"class":73,"line":139},[71,2473,173],{"class":77},[71,2475,902],{"class":77},[71,2477,2010],{"class":444},[71,2479,1592],{"class":77},[71,2481,1755],{"class":81},[71,2483,459],{"class":77},[71,2485,2486],{"class":176}," GlyphFor",[71,2488,211],{"class":77},[71,2490,445],{"class":444},[71,2492,2493],{"class":1607}," rune",[71,2495,459],{"class":77},[71,2497,2498],{"class":1607}," uint16",[71,2500,183],{"class":77},[71,2502,2503,2506,2509,2511,2514,2516,2518,2520],{"class":73,"line":149},[71,2504,2505],{"class":95},"    return",[71,2507,2508],{"class":189}," f",[71,2510,205],{"class":77},[71,2512,2513],{"class":189},"runeToGID",[71,2515,2174],{"class":77},[71,2517,445],{"class":189},[71,2519,2179],{"class":77},[71,2521,2522],{"class":2198},"  // O(1), cache-friendly, sem percorrer tabela\n",[71,2524,2525],{"class":73,"line":159},[71,2526,709],{"class":77},[19,2528,2529],{},"Um mapa, indexado por rune, populado por uma varredura linear única da tabela cmap. Pra um documento que usa a mesma fonte em várias páginas (todas), isso move a busca de glifos de \"quase quadrática em páginas × glifos\" pra \"linear em total de glifos mais uma constante fixa\".",[1935,2531,2533],{"id":2532},"por-que-format-12-é-o-detalhe-que-importa","Por que \"format 12\" é o detalhe que importa",[19,2535,2536,2537,2540,2541,898,2543,2546],{},"A maioria das bibliotecas Go PDF mais antigas foi escrita quando texto Latin era o que todo mundo se importava, e implementaram cmap format 4 — um range segmentado pro Basic Multilingual Plane (U+0000–U+FFFF). Texto japonês fora do BMP (menos comum, mas algumas variantes Kanji) precisa de format 12. O ",[43,2538,2539],{},"AddUTF8Font"," do ",[43,2542,1426],{},[23,2544,2545],{},"dá panic"," no NotoSansJP-Regular.ttf porque o parser de format 12 nunca foi terminado.",[19,2548,2549],{},"Não é uma crítica. É um artefato: gofpdf foi uma ótima biblioteca pro que web apps Latin-heavy precisavam em 2015, e o fork herdou o escopo. O mundo mudou; CJK foi de \"problema de outro\" pra \"problema da maioria dos ecossistemas Go do Japão e da China\". gpdf implementou a spec cmap completa porque a alternativa era uma fatura que mostra caixas de tofu pra 品目 — um bug report real da primeira semana depois do release público.",[1935,2551,2553],{"id":2552},"cache-que-escala-com-número-de-fontes-não-com-tamanho-do-documento","Cache que escala com número de fontes, não com tamanho do documento",[19,2555,2556,2557,2560,2561,2563,2564,205],{},"O cache de fontes é por ",[43,2558,2559],{},"Document",", não global. Se você gera 10,000 PDFs com a mesma fonte, paga o custo de resolução de 150 µs 10,000 vezes — a menos que compartilhe uma instância ",[43,2562,1755],{}," entre documentos, o que a API permite via ",[43,2565,2566],{},"gpdf.WithSharedFont(preloadedFont)",[19,2568,2569,2570,2573],{},"Pra geração em lote de alto volume (o SaaS ",[43,2571,2572],{},"gpdf-api"," roda assim), o padrão de fonte compartilhada é o que deixa a latência P95 previsível. Publicamos nos docs; a maioria dos usuários OSS não precisa.",[14,2575,2577],{"id":2576},"o-efeito-combinado","O efeito combinado",[19,2579,2580],{},"Colocamos as três decisões lado a lado no benchmark de 100 páginas (683 µs pro gpdf, 11.7 ms pro gofpdf):",[742,2582,2583,2596],{},[745,2584,2585],{},[748,2586,2587,2590,2593],{},[751,2588,2589],{},"Origem do tempo",[751,2591,2592],{},"gofpdf (por página, aprox)",[751,2594,2595],{},"gpdf (por página, aprox)",[759,2597,2598,2609,2620,2631,2642],{},[748,2599,2600,2603,2606],{},[764,2601,2602],{},"Construção do slice ops",[764,2604,2605],{},"~60 µs",[764,2607,2608],{},"0 (stream direto)",[748,2610,2611,2614,2617],{},[764,2612,2613],{},"Serialização das ops",[764,2615,2616],{},"~35 µs",[764,2618,2619],{},"0 (já escrito)",[748,2621,2622,2625,2628],{},[764,2623,2624],{},"Buscas de glifo (40 chars)",[764,2626,2627],{},"~6 µs",[764,2629,2630],{},"~0.4 µs",[748,2632,2633,2636,2639],{},[764,2634,2635],{},"Alocação / pressão de GC",[764,2637,2638],{},"~20 µs",[764,2640,2641],{},"~2 µs",[748,2643,2644,2649,2654],{},[764,2645,2646],{},[23,2647,2648],{},"Total",[764,2650,2651],{},[23,2652,2653],{},"~120 µs",[764,2655,2656],{},[23,2657,2658],{},"~7 µs",[19,2660,2661],{},"Os números são estimativas de profiling; a decomposição real depende do conteúdo. Mas o formato está certo. Nenhum dos três designs ganha 10× sozinho. Eles somam.",[19,2663,2664],{},"Corolário: se você copia só um design numa biblioteca existente, você consegue 2–3×. Se quer o 10×, precisa dos três, e você não consegue retrofit o primeiro (passagem única) numa biblioteca baseada em AST sem reescrevê-la.",[14,2666,2668],{"id":2667},"o-que-cedemos-a-seção-honesta","O que cedemos (a seção honesta)",[19,2670,2671],{},"Ficamos dançando em volta. A lista completa:",[19,2673,2674,2677],{},[23,2675,2676],{},"Pós-processamento baseado em AST."," Sem arquitetura de plugins. Sem \"percorre a árvore de nós e aplica esta transformação\". Se quer editar estilos de texto globalmente antes de renderizar, você faz antes de chamar o Builder, não depois.",[19,2679,2680,2683,2684,2687],{},[23,2681,2682],{},"Introspecção."," Não existe ",[43,2685,2686],{},"doc.Components()"," que devolva tudo o que você colocou. O documento é um fluxo de operadores na hora em que qualquer método significativo consegue rodar. Pra maioria dos usuários isso nunca aparece; pra minoria que escreve ferramentas de manipulação de documentos, aparece.",[19,2689,2690,2693,2694,2697,2698,2701],{},[23,2691,2692],{},"Serialização por reflexão."," Não temos uma API estilo ",[43,2695,2696],{},"json.Unmarshal"," que converte structs arbitrárias em PDF. O ponto de entrada JSON Schema (",[43,2699,2700],{},"template.FromJSON",") é explícito sobre os formatos que suporta, de propósito. Se você quer apontar uma biblioteca pra uma struct Go genérica e receber um PDF, isso é território do unidoc.",[19,2703,2704,2707,2708,2710],{},[23,2705,2706],{},"A extensibilidade de uma interface."," Você não pode implementar ",[43,2709,2107],{}," e registrar um elemento customizado. Pode escrever uma função helper que envolve as chamadas Builder, e na prática isso cobre 95% do que o pessoal pede, mas é um modelo diferente.",[19,2712,2713],{},"São deliberadas. Cada uma individualmente mataria a velocidade. Escolhemos o grupo de usuários cujo trabalho se beneficia de \"rápido e opinativo\" sobre o grupo que precisa de \"flexível e rico em plugins\". Se você tá no segundo grupo, Maroto v2 ou unidoc provavelmente encaixam melhor.",[14,2715,2717],{"id":2716},"dá-pra-rodar-o-benchmark","Dá pra rodar o benchmark?",[19,2719,2720],{},"Dá. Esse é todo o propósito de publicar o código.",[62,2722,2724],{"className":1248,"code":2723,"language":1250,"meta":67,"style":67},"git clone https://github.com/gpdf-dev/gpdf\ncd gpdf/_benchmark\ngo test -bench=. -benchmem -benchtime=5s\n",[43,2725,2726,2737,2745],{"__ignoreMap":67},[71,2727,2728,2731,2734],{"class":73,"line":74},[71,2729,2730],{"class":81},"git",[71,2732,2733],{"class":217}," clone",[71,2735,2736],{"class":217}," https://github.com/gpdf-dev/gpdf\n",[71,2738,2739,2742],{"class":73,"line":85},[71,2740,2741],{"class":176},"cd",[71,2743,2744],{"class":217}," gpdf/_benchmark\n",[71,2746,2747,2749,2752,2755,2758],{"class":73,"line":92},[71,2748,66],{"class":81},[71,2750,2751],{"class":217}," test",[71,2753,2754],{"class":217}," -bench=.",[71,2756,2757],{"class":217}," -benchmem",[71,2759,2760],{"class":217}," -benchtime=5s\n",[19,2762,2763,2764,2769],{},"O README naquele diretório documenta as quatro cargas e o que elas medem. Se seus números diferirem materialmente (>20%) na mesma arquitetura de CPU e versão do Go, ",[28,2765,2768],{"href":2766,"rel":2767},"https://github.com/gpdf-dev/gpdf/issues",[32],"abra um issue"," — drift existe e queremos saber.",[19,2771,2772],{},"Duas ressalvas:",[1114,2774,2775,2781],{},[887,2776,2777,2778,2780],{},"O benchmark roda com ",[43,2779,1408],{},". Se desativar, os números melhoram ~5% no geral, o que a gente não conta em afirmações públicas porque não é como ninguém roda código real.",[887,2782,2783],{},"CGO tá off. Alguns leitores perguntaram se um backend FreeType em CGO seria mais rápido pra operações de fonte; testamos, e o custo de marshaling na fronteira FFI dominou qualquer ganho. O subsetter em Go puro ganha pros padrões de acesso que um gerador PDF tem.",[14,2785,2787],{"id":2786},"faq","FAQ",[19,2789,2790,2793,2794,205],{},[23,2791,2792],{},"Por que comparar com gofpdf se tá arquivado?","\nPorque ainda é o primeiro resultado no GitHub pra \"go pdf\", e a maioria dos times que chega no gpdf tá migrando de lá. O benchmark precisa responder \"a migração vale o esforço?\" pra esse público. Versão curta: vale, e escrevemos um ",[28,2795,2797],{"href":2796},"/pt/blog/gofpdf-migration","guia de migração",[19,2799,2800,2803],{},[23,2801,2802],{},"10× mais rápido é realmente significativo pra geração de PDF?","\nDepende da carga. Pra um documento por requisição, nem tanto — as duas bibliotecas passam o limiar de \"gerar na request\". Pra operações em lote (extratos noturnos, faturas em massa, geração de relatórios a partir de query em DB), a distância se traduz direto em menos máquinas. Ouvimos \"10× menos workers\" do primeiro time que migrou o pipeline de lotes; não auditamos as contas deles mas bate com o benchmark.",[19,2805,2806,2809,2810,2813,2814,2816],{},[23,2807,2808],{},"Qual é a pegadinha no número CJK?","\nVocê ainda precisa enviar o arquivo de fonte. gpdf faz o subset, mas um TTF NotoSansJP de 3 MB é 3 MB que você ou embute no binário Go ou faz ",[43,2811,2812],{},"os.ReadFile"," na inicialização. Pra imagens distroless isso importa. O SaaS ",[43,2815,2572],{}," resolve enviando as fontes comuns na imagem; usuários OSS lidam por conta.",[19,2818,2819,2822],{},[23,2820,2821],{},"gpdf vai ficar mais lento conforme features forem adicionadas?","\nÉ a pergunta que mais nos importa. Resposta: fazemos benchmark de cada release contra a anterior, e regressão maior que 5% em qualquer das quatro cargas bloqueia a release. Os benchmarks vivem no mesmo repo que a biblioteca exatamente por isso.",[19,2824,2825,2828],{},[23,2826,2827],{},"De onde vem o nome?","\ngpdf = Go + PDF. Não é esperto. É intencional.",[14,2830,2832],{"id":2831},"testar-o-gpdf","Testar o gpdf",[19,2834,2835],{},"gpdf é uma biblioteca Go pra gerar PDFs. MIT, zero dependências, CJK nativo.",[62,2837,2838],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,2839,2840],{"__ignoreMap":67},[71,2841,2842,2844,2846],{"class":73,"line":74},[71,2843,66],{"class":81},[71,2845,1259],{"class":217},[71,2847,1262],{"class":217},[19,2849,2850,1269,2854],{},[28,2851,2853],{"href":30,"rel":2852},[32],"⭐ Star on GitHub",[28,2855,2857],{"href":1272,"rel":2856},[32],"Ler os docs",[14,2859,2861],{"id":2860},"leituras-seguintes","Leituras seguintes",[1114,2863,2864,2871,2877],{},[887,2865,2866,2870],{},[28,2867,2869],{"href":2868},"/pt/blog/go-pdf-library-showdown-2026","Comparativo de bibliotecas Go PDF 2026"," — comparação completa com licenças e dependências.",[887,2872,2873,2876],{},[28,2874,2875],{"href":2796},"gofpdf foi arquivado. Como migrar pro gpdf."," — cinco pares de API antes/depois, todos executáveis.",[887,2878,2879,2880,205],{},"O código do benchmark: ",[28,2881,2883],{"href":1390,"rel":2882},[32],[43,2884,1394],{},[1276,2886,2887],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .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 .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":67,"searchDepth":85,"depth":85,"links":2889},[2890,2891,2892,2895,2900,2904,2905,2906,2907,2908,2909],{"id":37,"depth":85,"text":38},{"id":1401,"depth":85,"text":1402},{"id":1541,"depth":85,"text":1542,"children":2893},[2894],{"id":1937,"depth":92,"text":1938},{"id":1982,"depth":85,"text":2896,"children":2897},"Decisão 2: Sem reflexão, sem interface{} no caminho quente",[2898,2899],{"id":2293,"depth":92,"text":2294},{"id":2327,"depth":92,"text":2328},{"id":2344,"depth":85,"text":2345,"children":2901},[2902,2903],{"id":2532,"depth":92,"text":2533},{"id":2552,"depth":92,"text":2553},{"id":2576,"depth":85,"text":2577},{"id":2667,"depth":85,"text":2668},{"id":2716,"depth":85,"text":2717},{"id":2786,"depth":85,"text":2787},{"id":2831,"depth":85,"text":2832},{"id":2860,"depth":85,"text":2861},"gpdf gera uma página em 13 µs e um relatório de 100 páginas em 683 µs. Não é truque de tuning — são três decisões arquiteturais que se somam. Percorremos o código.",{},"/pt/blog/why-gpdf-is-faster",{"title":1329,"description":2910},"pt/blog/011.why-gpdf-is-faster",[2916,2917,2918],"benchmark","internals","comparison","YfQYx-rnuvP24tGMZ3xAULmP9HGkdzQlXf5Emw2v9K8",{"id":2921,"title":2922,"author":2923,"body":2924,"date":4002,"description":4003,"draft":1293,"extension":1294,"howTo":4004,"image":1317,"meta":4024,"navigation":88,"path":1236,"seo":4025,"stem":4026,"tags":4027,"updated":1317,"__hash__":4029},"blogPt/pt/blog/008.tofu-boxes-japanese.md","Por que meu PDF mostra retângulos (tofu) no lugar de japonês?",{"name":8,"url":9},{"type":11,"value":2925,"toc":3990},[2926,2928,2931,2935,2943,2946,3001,3005,3008,3477,3490,3500,3504,3507,3527,3546,3552,3556,3563,3704,3737,3741,3752,3755,3758,3787,3790,3794,3800,3864,3878,3881,3913,3917,3933,3937,3963,3965,3967,3979,3987],[14,2927,17],{"id":16},[19,2929,2930],{},"Escrevi texto japonês com gpdf e o PDF resultante mostra retângulos vazios onde os caracteres deveriam estar. O que é isso e como faço para que os glifos japoneses reais apareçam no arquivo?",[14,2932,2934],{"id":2933},"a-resposta-rápida","A resposta rápida",[19,2936,2937,2938,2942],{},"Isso é ",[2939,2940,2941],"em",{},"tofu"," — o visualizador de PDF desenha um retângulo de marcador porque a fonte embutida no PDF não tem glifo para o code point Unicode que você pediu. Quatro coisas causam isso, e uma é muito mais comum que o restante.",[19,2944,2945],{},"Por ordem de frequência:",[884,2947,2948,2960,2977,2991],{},[887,2949,2950,898,2953,2956,2957,2959],{},[23,2951,2952],{},"Nenhuma fonte CJK registrada.",[43,2954,2955],{},"gpdf.NewDocument"," não tem nenhuma chamada a ",[43,2958,356],{},", então o documento recai nas fontes Base-14 do PDF (Helvetica, Times, Courier). Nenhuma cobre U+3040–U+9FFF.",[887,2961,2962,898,2969,2972,2973,2976],{},[23,2963,2964,2965,2968],{},"Fonte CJK registrada, mas o nome da família em ",[43,2966,2967],{},"c.Text"," está errado.",[43,2970,2971],{},"WithFont(\"NotoSansJP\", ...)"," está configurado, mas ",[43,2974,2975],{},"template.FontFamily(\"Arial\")"," no texto força o gpdf a procurar japonês em uma fonte latina.",[887,2978,2979,2982,2983,2986,2987,2990],{},[23,2980,2981],{},"O arquivo de fonte não contém glifos CJK."," O TTF em disco é um subset latino (",[43,2984,2985],{},"NotoSans-Regular.ttf"," em vez de ",[43,2988,2989],{},"NotoSansJP-Regular.ttf","). O nome parece certo, a cobertura está vazia.",[887,2992,2993,2996,2997,3000],{},[23,2994,2995],{},"Os bytes foram corrompidos antes do gpdf recebê-los."," A string foi decodificada como Shift-JIS ou Latin-1 em algum ponto anterior, e os code points já não são japoneses. Se você vê ",[43,2998,2999],{},"縺ゅ→縺"," em vez de retângulos, é essa.",[14,3002,3004],{"id":3003},"a-correção-canônica-para-a-causa-1","A correção canônica para a causa #1",[19,3006,3007],{},"Nove em cada dez vezes é isto:",[62,3009,3011],{"className":64,"code":3010,"language":66,"meta":67,"style":67},"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    font, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", font),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 12),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"こんにちは、世界。\")\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",[43,3012,3013,3019,3023,3029,3037,3045,3049,3057,3065,3073,3077,3081,3091,3117,3129,3143,3147,3151,3165,3183,3213,3236,3259,3263,3267,3281,3305,3335,3354,3358,3362,3366,3384,3396,3410,3414,3455,3469,3473],{"__ignoreMap":67},[71,3014,3015,3017],{"class":73,"line":74},[71,3016,78],{"class":77},[71,3018,82],{"class":81},[71,3020,3021],{"class":73,"line":85},[71,3022,89],{"emptyLinePlaceholder":88},[71,3024,3025,3027],{"class":73,"line":92},[71,3026,96],{"class":95},[71,3028,99],{"class":77},[71,3030,3031,3033,3035],{"class":73,"line":102},[71,3032,105],{"class":77},[71,3034,108],{"class":81},[71,3036,111],{"class":77},[71,3038,3039,3041,3043],{"class":73,"line":114},[71,3040,105],{"class":77},[71,3042,119],{"class":81},[71,3044,111],{"class":77},[71,3046,3047],{"class":73,"line":124},[71,3048,89],{"emptyLinePlaceholder":88},[71,3050,3051,3053,3055],{"class":73,"line":129},[71,3052,105],{"class":77},[71,3054,134],{"class":81},[71,3056,111],{"class":77},[71,3058,3059,3061,3063],{"class":73,"line":139},[71,3060,105],{"class":77},[71,3062,144],{"class":81},[71,3064,111],{"class":77},[71,3066,3067,3069,3071],{"class":73,"line":149},[71,3068,105],{"class":77},[71,3070,154],{"class":81},[71,3072,111],{"class":77},[71,3074,3075],{"class":73,"line":159},[71,3076,162],{"class":77},[71,3078,3079],{"class":73,"line":165},[71,3080,89],{"emptyLinePlaceholder":88},[71,3082,3083,3085,3087,3089],{"class":73,"line":170},[71,3084,173],{"class":77},[71,3086,177],{"class":176},[71,3088,180],{"class":77},[71,3090,183],{"class":77},[71,3092,3093,3095,3097,3099,3101,3103,3105,3107,3109,3111,3113,3115],{"class":73,"line":186},[71,3094,190],{"class":189},[71,3096,193],{"class":77},[71,3098,196],{"class":189},[71,3100,199],{"class":77},[71,3102,202],{"class":189},[71,3104,205],{"class":77},[71,3106,208],{"class":176},[71,3108,211],{"class":77},[71,3110,214],{"class":77},[71,3112,2989],{"class":217},[71,3114,214],{"class":77},[71,3116,162],{"class":77},[71,3118,3119,3121,3123,3125,3127],{"class":73,"line":224},[71,3120,227],{"class":95},[71,3122,196],{"class":189},[71,3124,232],{"class":77},[71,3126,235],{"class":77},[71,3128,183],{"class":77},[71,3130,3131,3133,3135,3137,3139,3141],{"class":73,"line":240},[71,3132,243],{"class":189},[71,3134,205],{"class":77},[71,3136,248],{"class":176},[71,3138,211],{"class":77},[71,3140,253],{"class":189},[71,3142,162],{"class":77},[71,3144,3145],{"class":73,"line":258},[71,3146,261],{"class":77},[71,3148,3149],{"class":73,"line":264},[71,3150,89],{"emptyLinePlaceholder":88},[71,3152,3153,3155,3157,3159,3161,3163],{"class":73,"line":269},[71,3154,272],{"class":189},[71,3156,199],{"class":77},[71,3158,277],{"class":189},[71,3160,205],{"class":77},[71,3162,282],{"class":176},[71,3164,285],{"class":77},[71,3166,3167,3169,3171,3173,3175,3177,3179,3181],{"class":73,"line":288},[71,3168,291],{"class":189},[71,3170,205],{"class":77},[71,3172,296],{"class":176},[71,3174,211],{"class":77},[71,3176,33],{"class":189},[71,3178,205],{"class":77},[71,3180,305],{"class":189},[71,3182,308],{"class":77},[71,3184,3185,3187,3189,3191,3193,3195,3197,3199,3201,3203,3205,3207,3209,3211],{"class":73,"line":311},[71,3186,291],{"class":189},[71,3188,205],{"class":77},[71,3190,318],{"class":176},[71,3192,211],{"class":77},[71,3194,323],{"class":189},[71,3196,205],{"class":77},[71,3198,328],{"class":176},[71,3200,211],{"class":77},[71,3202,323],{"class":189},[71,3204,205],{"class":77},[71,3206,337],{"class":176},[71,3208,211],{"class":77},[71,3210,343],{"class":342},[71,3212,346],{"class":77},[71,3214,3215,3217,3219,3221,3223,3225,3228,3230,3232,3234],{"class":73,"line":349},[71,3216,291],{"class":189},[71,3218,205],{"class":77},[71,3220,356],{"class":176},[71,3222,211],{"class":77},[71,3224,214],{"class":77},[71,3226,3227],{"class":217},"NotoSansJP",[71,3229,214],{"class":77},[71,3231,193],{"class":77},[71,3233,370],{"class":189},[71,3235,308],{"class":77},[71,3237,3238,3240,3242,3244,3246,3248,3250,3252,3254,3257],{"class":73,"line":375},[71,3239,291],{"class":189},[71,3241,205],{"class":77},[71,3243,382],{"class":176},[71,3245,211],{"class":77},[71,3247,214],{"class":77},[71,3249,3227],{"class":217},[71,3251,214],{"class":77},[71,3253,193],{"class":77},[71,3255,3256],{"class":342}," 12",[71,3258,308],{"class":77},[71,3260,3261],{"class":73,"line":400},[71,3262,403],{"class":77},[71,3264,3265],{"class":73,"line":406},[71,3266,89],{"emptyLinePlaceholder":88},[71,3268,3269,3271,3273,3275,3277,3279],{"class":73,"line":411},[71,3270,414],{"class":189},[71,3272,199],{"class":77},[71,3274,419],{"class":189},[71,3276,205],{"class":77},[71,3278,424],{"class":176},[71,3280,427],{"class":77},[71,3282,3283,3285,3287,3289,3291,3293,3295,3297,3299,3301,3303],{"class":73,"line":430},[71,3284,433],{"class":189},[71,3286,205],{"class":77},[71,3288,438],{"class":176},[71,3290,441],{"class":77},[71,3292,445],{"class":444},[71,3294,448],{"class":77},[71,3296,451],{"class":81},[71,3298,205],{"class":77},[71,3300,456],{"class":81},[71,3302,459],{"class":77},[71,3304,183],{"class":77},[71,3306,3307,3309,3311,3313,3315,3317,3319,3321,3323,3325,3327,3329,3331,3333],{"class":73,"line":464},[71,3308,467],{"class":189},[71,3310,205],{"class":77},[71,3312,472],{"class":176},[71,3314,211],{"class":77},[71,3316,477],{"class":342},[71,3318,193],{"class":77},[71,3320,482],{"class":77},[71,3322,485],{"class":444},[71,3324,448],{"class":77},[71,3326,451],{"class":81},[71,3328,205],{"class":77},[71,3330,494],{"class":81},[71,3332,459],{"class":77},[71,3334,183],{"class":77},[71,3336,3337,3339,3341,3343,3345,3347,3350,3352],{"class":73,"line":501},[71,3338,504],{"class":189},[71,3340,205],{"class":77},[71,3342,509],{"class":176},[71,3344,211],{"class":77},[71,3346,214],{"class":77},[71,3348,3349],{"class":217},"こんにちは、世界。",[71,3351,214],{"class":77},[71,3353,162],{"class":77},[71,3355,3356],{"class":73,"line":549},[71,3357,572],{"class":77},[71,3359,3360],{"class":73,"line":569},[71,3361,578],{"class":77},[71,3363,3364],{"class":73,"line":575},[71,3365,89],{"emptyLinePlaceholder":88},[71,3367,3368,3370,3372,3374,3376,3378,3380,3382],{"class":73,"line":581},[71,3369,589],{"class":189},[71,3371,193],{"class":77},[71,3373,196],{"class":189},[71,3375,199],{"class":77},[71,3377,419],{"class":189},[71,3379,205],{"class":77},[71,3381,602],{"class":176},[71,3383,427],{"class":77},[71,3385,3386,3388,3390,3392,3394],{"class":73,"line":586},[71,3387,227],{"class":95},[71,3389,196],{"class":189},[71,3391,232],{"class":77},[71,3393,235],{"class":77},[71,3395,183],{"class":77},[71,3397,3398,3400,3402,3404,3406,3408],{"class":73,"line":607},[71,3399,243],{"class":189},[71,3401,205],{"class":77},[71,3403,248],{"class":176},[71,3405,211],{"class":77},[71,3407,253],{"class":189},[71,3409,162],{"class":77},[71,3411,3412],{"class":73,"line":620},[71,3413,261],{"class":77},[71,3415,3416,3418,3420,3422,3424,3426,3428,3430,3432,3435,3437,3439,3441,3443,3445,3447,3449,3451,3453],{"class":73,"line":635},[71,3417,227],{"class":95},[71,3419,196],{"class":189},[71,3421,199],{"class":77},[71,3423,202],{"class":189},[71,3425,205],{"class":77},[71,3427,653],{"class":176},[71,3429,211],{"class":77},[71,3431,214],{"class":77},[71,3433,3434],{"class":217},"hello.pdf",[71,3436,214],{"class":77},[71,3438,193],{"class":77},[71,3440,667],{"class":189},[71,3442,193],{"class":77},[71,3444,672],{"class":342},[71,3446,675],{"class":77},[71,3448,196],{"class":189},[71,3450,232],{"class":77},[71,3452,235],{"class":77},[71,3454,183],{"class":77},[71,3456,3457,3459,3461,3463,3465,3467],{"class":73,"line":640},[71,3458,243],{"class":189},[71,3460,205],{"class":77},[71,3462,248],{"class":176},[71,3464,211],{"class":77},[71,3466,253],{"class":189},[71,3468,162],{"class":77},[71,3470,3471],{"class":73,"line":686},[71,3472,261],{"class":77},[71,3474,3475],{"class":73,"line":701},[71,3476,709],{"class":77},[19,3478,3479,3480,3482,3483,3486,3487,3489],{},"Duas linhas registram a fonte e a definem como padrão. Sem CGO. Sem a burocracia do ",[43,3481,2539],{},". Se você estava vendo ",[43,3484,3485],{},"□□□□□、□□。"," e rodar este programa com um ",[43,3488,2989],{}," real ao lado, os glifos reais aparecem.",[19,3491,41,3492,3494,3495,205],{},[43,3493,2989],{}," no ",[28,3496,3499],{"href":3497,"rel":3498},"https://fonts.google.com/noto/specimen/Noto+Sans+JP",[32],"Google Fonts",[14,3501,3503],{"id":3502},"como-saber-qual-causa-é-a-sua","Como saber qual causa é a sua",[19,3505,3506],{},"A maior parte é olhar três lugares: onde você constrói o documento, onde você escreve o texto e o próprio arquivo TTF.",[19,3508,3509,3515,3516,3519,3520,3523,3524,3526],{},[23,3510,3511,3512],{},"Se a saída são ",[43,3513,3514],{},"□□□"," (retângulos idênticos), é causa 1, 2 ou 3. O PDF embutiu ",[2939,3517,3518],{},"uma"," fonte, mas ela não tem os glifos. Abra o PDF no Acrobat, vá em ",[43,3521,3522],{},"Arquivo → Propriedades → Fontes"," e veja quais fontes foram realmente embutidas. Se a lista só tem Helvetica / Times / Courier, causa 1. Se ",[43,3525,3227],{}," está listada e ainda há retângulos, causa 2 ou 3.",[19,3528,3529,3538,3539,3541,3542,3545],{},[23,3530,3531,3532,3534,3535],{},"Se a saída é ",[43,3533,2999],{}," ou ",[43,3536,3537],{},"ã\"ã‚\"ã«ã¡ã¯"," (latim embaralhado), é causa 4. Sua string japonesa foi recodificada antes de chegar ao gpdf. Culpado mais comum: um CSV salvo como Shift-JIS pelo Excel e lido com ",[43,3540,2812],{}," como se fosse UTF-8, ou um endpoint HTTP que não declarou ",[43,3543,3544],{},"charset=utf-8",". Conserte o decodificador, não o PDF.",[19,3547,3548,3551],{},[23,3549,3550],{},"Saída mista"," — alguns caracteres renderizam, outros viram retângulos — significa cobertura parcial da fonte. Uma fonte rotulada como \"japonesa\" pode incluir hiragana e katakana mas pular kanjis incomuns como 鬱 ou 龠. Troque para Noto Sans JP (cobre JIS X 0213) ou Source Han Sans JP se isso acontecer.",[14,3553,3555],{"id":3554},"causa-2-em-detalhe-fonte-certa-nome-de-família-errado","Causa 2 em detalhe: fonte certa, nome de família errado",[19,3557,3558,3559,3562],{},"Essa é traiçoeira porque a fonte ",[2939,3560,3561],{},"está"," embutida — simplesmente não é usada. Reprodução mínima:",[62,3564,3566],{"className":64,"code":3565,"language":66,"meta":67,"style":67},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    // Sem WithDefaultFont.\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"こんにちは\") // Usa a fonte padrão: Helvetica.\n    })\n})\n",[43,3567,3568,3582,3604,3609,3613,3617,3641,3672,3695,3699],{"__ignoreMap":67},[71,3569,3570,3572,3574,3576,3578,3580],{"class":73,"line":74},[71,3571,1004],{"class":189},[71,3573,199],{"class":77},[71,3575,277],{"class":189},[71,3577,205],{"class":77},[71,3579,282],{"class":176},[71,3581,285],{"class":77},[71,3583,3584,3586,3588,3590,3592,3594,3596,3598,3600,3602],{"class":73,"line":85},[71,3585,1019],{"class":189},[71,3587,205],{"class":77},[71,3589,356],{"class":176},[71,3591,211],{"class":77},[71,3593,214],{"class":77},[71,3595,3227],{"class":217},[71,3597,214],{"class":77},[71,3599,193],{"class":77},[71,3601,370],{"class":189},[71,3603,308],{"class":77},[71,3605,3606],{"class":73,"line":92},[71,3607,3608],{"class":2198},"    // Sem WithDefaultFont.\n",[71,3610,3611],{"class":73,"line":102},[71,3612,162],{"class":77},[71,3614,3615],{"class":73,"line":114},[71,3616,89],{"emptyLinePlaceholder":88},[71,3618,3619,3621,3623,3625,3627,3629,3631,3633,3635,3637,3639],{"class":73,"line":124},[71,3620,1913],{"class":189},[71,3622,205],{"class":77},[71,3624,438],{"class":176},[71,3626,441],{"class":77},[71,3628,445],{"class":444},[71,3630,448],{"class":77},[71,3632,451],{"class":81},[71,3634,205],{"class":77},[71,3636,456],{"class":81},[71,3638,459],{"class":77},[71,3640,183],{"class":77},[71,3642,3643,3646,3648,3650,3652,3654,3656,3658,3660,3662,3664,3666,3668,3670],{"class":73,"line":129},[71,3644,3645],{"class":189},"    r",[71,3647,205],{"class":77},[71,3649,472],{"class":176},[71,3651,211],{"class":77},[71,3653,477],{"class":342},[71,3655,193],{"class":77},[71,3657,482],{"class":77},[71,3659,485],{"class":444},[71,3661,448],{"class":77},[71,3663,451],{"class":81},[71,3665,205],{"class":77},[71,3667,494],{"class":81},[71,3669,459],{"class":77},[71,3671,183],{"class":77},[71,3673,3674,3677,3679,3681,3683,3685,3688,3690,3692],{"class":73,"line":139},[71,3675,3676],{"class":189},"        c",[71,3678,205],{"class":77},[71,3680,509],{"class":176},[71,3682,211],{"class":77},[71,3684,214],{"class":77},[71,3686,3687],{"class":217},"こんにちは",[71,3689,214],{"class":77},[71,3691,459],{"class":77},[71,3693,3694],{"class":2198}," // Usa a fonte padrão: Helvetica.\n",[71,3696,3697],{"class":73,"line":149},[71,3698,578],{"class":77},[71,3700,3701],{"class":73,"line":159},[71,3702,3703],{"class":77},"})\n",[19,3705,3706,3707,3710,3711,3713,3714,3717,3718,3720,3721,3723,3724,3726,3727,3730,3731,1155,3733,3736],{},"Correção: adicione ",[43,3708,3709],{},"gpdf.WithDefaultFont(\"NotoSansJP\", 12)"," ao ",[43,3712,282],{},", ou passe ",[43,3715,3716],{},"template.FontFamily(\"NotoSansJP\")"," em cada ",[43,3719,2967],{}," que precisa de japonês. O nome de família em ",[43,3722,356],{}," e o em ",[43,3725,2967],{}," devem bater ",[23,3728,3729],{},"exatamente",", incluindo caixa. Para o gpdf, ",[43,3732,3227],{},[43,3734,3735],{},"notosansjp"," são duas fontes diferentes.",[14,3738,3740],{"id":3739},"causa-3-em-detalhe-o-arquivo-ttf-errado","Causa 3 em detalhe: o arquivo TTF errado",[19,3742,3743,1155,3745,3747,3748,3751],{},[43,3744,2985],{},[43,3746,2989],{}," são arquivos diferentes. O primeiro é uma fonte latina sem nenhuma cobertura CJK. O segundo é a versão japonesa, com cerca de 17.000 glifos. Eles ficam quase idênticos em um ",[43,3749,3750],{},"ls",", e o autocomplete do editor pega o errado com facilidade.",[19,3753,3754],{},"O gpdf não valida cobertura de glifos no registro. Se você entrega bytes, ele confia. A falha só aparece como tofu no momento do render.",[19,3756,3757],{},"Maneira rápida de conferir:",[1114,3759,3760,3767,3774],{},[887,3761,3762,3763,3766],{},"macOS: ",[43,3764,3765],{},"Font Book"," → duplo-clique no arquivo → a prévia mostra uma grade de glifos",[887,3768,3769,3770,3773],{},"Linux: ",[43,3771,3772],{},"otfinfo -u NotoSans-Regular.ttf"," lista a cobertura Unicode",[887,3775,3776,3777,3782,3783,3786],{},"Multiplataforma: ",[28,3778,3781],{"href":3779,"rel":3780},"https://github.com/fonttools/fonttools",[32],"fontTools"," — ",[43,3784,3785],{},"ttx -t cmap NotoSans-Regular.ttf"," despeja a tabela cmap como XML",[19,3788,3789],{},"Se U+3042 (あ) não está na lista, você está com o subset latino.",[14,3791,3793],{"id":3792},"causa-4-em-detalhe-corrupção-de-encoding","Causa 4 em detalhe: corrupção de encoding",[19,3795,3796,3797,3799],{},"Essa na verdade não envolve o gpdf. A string entregue ao ",[43,3798,2967],{}," já tinha os bytes errados. Imprima antes de renderizar:",[62,3801,3803],{"className":64,"code":3802,"language":66,"meta":67,"style":67},"text := loadLabelFromSomewhere()\nfmt.Printf(\"%q\\n\", text) // Mostra as runas reais\nc.Text(text)\n",[43,3804,3805,3817,3850],{"__ignoreMap":67},[71,3806,3807,3810,3812,3815],{"class":73,"line":74},[71,3808,3809],{"class":189},"text ",[71,3811,199],{"class":77},[71,3813,3814],{"class":176}," loadLabelFromSomewhere",[71,3816,427],{"class":77},[71,3818,3819,3822,3824,3827,3829,3831,3835,3838,3840,3842,3845,3847],{"class":73,"line":85},[71,3820,3821],{"class":189},"fmt",[71,3823,205],{"class":77},[71,3825,3826],{"class":176},"Printf",[71,3828,211],{"class":77},[71,3830,214],{"class":77},[71,3832,3834],{"class":3833},"swJcz","%q",[71,3836,3837],{"class":189},"\\n",[71,3839,214],{"class":77},[71,3841,193],{"class":77},[71,3843,3844],{"class":189}," text",[71,3846,459],{"class":77},[71,3848,3849],{"class":2198}," // Mostra as runas reais\n",[71,3851,3852,3854,3856,3858,3860,3862],{"class":73,"line":92},[71,3853,485],{"class":189},[71,3855,205],{"class":77},[71,3857,509],{"class":176},[71,3859,211],{"class":77},[71,3861,928],{"class":189},[71,3863,162],{"class":77},[19,3865,3866,3867,3870,3871,2986,3874,3877],{},"Se ",[43,3868,3869],{},"fmt.Printf(\"%q\\n\", text)"," imprime ",[43,3872,3873],{},"\"縺ゅ→縺\"",[43,3875,3876],{},"\"あいうえ\"",", a corrupção aconteceu antes. O gpdf não pode consertar — ache o ponto onde o UTF-8 foi decodificado errado.",[19,3879,3880],{},"Culpados habituais lá na frente:",[1114,3882,3883,3892,3906],{},[887,3884,3885,3886,3888,3889],{},"Ler um CSV exportado do Excel (Windows Shift-JIS) com ",[43,3887,2812],{}," e converter direto em ",[43,3890,3891],{},"string",[887,3893,3894,3895,3534,3898,3901,3902,3905],{},"Uma coluna de banco declarada ",[43,3896,3897],{},"latin1",[43,3899,3900],{},"utf8mb3"," (não ",[43,3903,3904],{},"utf8mb4",") já guardando mojibake",[887,3907,3908,3909,3912],{},"Uma resposta HTTP sem ",[43,3910,3911],{},"Content-Type: application/json; charset=utf-8"," e um cliente que chutou Latin-1",[14,3914,3916],{"id":3915},"um-caso-de-borda-que-vale-mencionar","Um caso de borda que vale mencionar",[19,3918,3919,3920,3923,3924,3926,3927,3930,3931,205],{},"O gpdf faz subset silenciosamente. O subset congela no instante de ",[43,3921,3922],{},"Generate()",". Se durante a construção do documento você renderiza ",[43,3925,3687],{}," e depois ",[43,3928,3929],{},"鬱陶しい",", o segundo também entra no subset corretamente. Mas se você gerar o PDF, abrir no Acrobat e digitar um kanji que não estava no texto original, aquele caractere virá como tofu — aquele glifo nunca entrou no subset. Não edite o PDF depois; rode o programa Go de novo e chame ",[43,3932,3922],{},[14,3934,3936],{"id":3935},"receitas-relacionadas","Receitas relacionadas",[1114,3938,3939,3947,3956],{},[887,3940,3941,3943,3944,3946],{},[28,3942,1223],{"href":1222}," — guia completo do ",[43,3945,356],{}," com variantes bold/italic e documentos multi-CJK",[887,3948,3949,3951,3952,3955],{},[28,3950,1216],{"href":835}," — qual arquivo do Noto escolher e como o ",[43,3953,3954],{},"go:embed"," simplifica a distribuição",[887,3957,3958,3962],{},[28,3959,3961],{"href":3960},"/pt/blog/japanese-pdf-in-go","Guia definitivo de PDFs em japonês com Go (2026)"," — guia longo cobrindo fontes, texto vertical, ruby e layout específico de JP",[14,3964,1242],{"id":1241},[19,3966,1245],{},[62,3968,3969],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,3970,3971],{"__ignoreMap":67},[71,3972,3973,3975,3977],{"class":73,"line":74},[71,3974,66],{"class":81},[71,3976,1259],{"class":217},[71,3978,1262],{"class":217},[19,3980,3981,1269,3984],{},[28,3982,1268],{"href":30,"rel":3983},[32],[28,3985,1274],{"href":1272,"rel":3986},[32],[1276,3988,3989],{},"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 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 .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 .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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":67,"searchDepth":85,"depth":85,"links":3991},[3992,3993,3994,3995,3996,3997,3998,3999,4000,4001],{"id":16,"depth":85,"text":17},{"id":2933,"depth":85,"text":2934},{"id":3003,"depth":85,"text":3004},{"id":3502,"depth":85,"text":3503},{"id":3554,"depth":85,"text":3555},{"id":3739,"depth":85,"text":3740},{"id":3792,"depth":85,"text":3793},{"id":3915,"depth":85,"text":3916},{"id":3935,"depth":85,"text":3936},{"id":1241,"depth":85,"text":1242},"2026-04-17","Retângulos vazios em vez de caracteres japoneses significam que o PDF não encontrou glifos para esses code points. Quatro causas e como corrigir.",{"name":4005,"totalTime":1297,"tools":4006,"steps":4008},"Diagnosticar e corrigir retângulos tofu em um documento gpdf",[1299,4007],"Uma TTF com suporte a CJK, como NotoSansJP-Regular.ttf",[4009,4012,4015,4018,4021],{"name":4010,"text":4011},"Confirme se o sintoma é tofu ou mojibake","Abra o PDF. Caracteres japoneses aparecendo como retângulos vazios (□) indicam falha de lookup da fonte. Texto latino embaralhado como 縺ゅ→縺 indica UTF-8 decodificado errado antes de chegar ao gpdf.",{"name":4013,"text":4014},"Verifique se há uma fonte CJK registrada","Procure por gpdf.WithFont na construção do documento. Sem um TTF CJK registrado, o gpdf recai nas fontes Base-14 do PDF, e nenhuma cobre code points CJK.",{"name":4016,"text":4017},"Valide o nome da família em cada c.Text","Sem WithDefaultFont, todo c.Text que renderiza japonês precisa de template.FontFamily(\"NotoSansJP\") explícito. Um nome errado cai silenciosamente na fonte padrão.",{"name":4019,"text":4020},"Confirme que o arquivo TTF contém mesmo glifos CJK","NotoSans-Regular.ttf (subset Latin) e NotoSansJP-Regular.ttf são arquivos diferentes. O gpdf não valida cobertura de glifos no momento do registro.",{"name":4022,"text":4023},"Regenere e verifique em dois visualizadores","Abra o PDF no Adobe Acrobat e no Chrome. Ambos devem renderizar japonês. Se só um funciona, os glifos estão embutidos mas o subset registrado não bate com aquele code point.",{},{"title":2922,"description":4003},"pt/blog/008.tofu-boxes-japanese",[1323,4028,1324],"troubleshooting","y_ip_ZS6ag1jRJItYThmCnO7fPJl7zFkrHkgVsI6fMQ",{"id":4031,"title":1230,"author":4032,"body":4033,"date":4002,"description":5234,"draft":1293,"extension":1294,"howTo":5235,"image":1317,"meta":5256,"navigation":88,"path":1229,"seo":5257,"stem":5258,"tags":5259,"updated":1317,"__hash__":5260},"blogPt/pt/blog/009.ipaex-gothic-gpdf.md",{"name":8,"url":9},{"type":11,"value":4034,"toc":5223},[4035,4037,4050,4052,4065,4067,4596,4615,4619,4622,4678,4681,4687,4691,4694,4697,4705,4737,4743,5026,5029,5033,5042,5068,5075,5078,5082,5166,5169,5171,5197,5199,5201,5213,5221],[14,4036,17],{"id":16},[19,4038,4039,4040,4045,4046,4049],{},"Você quer usar a IPAex Gothic — a fonte Gothic proporcional que a ",[28,4041,4044],{"href":4042,"rel":4043},"https://moji.or.jp/ipafont/",[32],"Information-technology Promotion Agency"," (IPA) do Japão mantém — em um documento ",[28,4047,33],{"href":30,"rel":4048},[32],". Motivos típicos: envios de PDF para o e-Tax, papelada para órgãos governamentais, ou um guia de estilo interno que vive com IPAex desde o início dos anos 2010. Três coisas costumam atrapalhar: qual arquivo pegar, como lidar com a ausência de Bold e o que a IPA Font License realmente te pede.",[14,4051,38],{"id":37},[19,4053,4054,4055,1972,4058,4061,4062,4064],{},"Registre ",[43,4056,4057],{},"ipaexg.ttf",[43,4059,4060],{},"gpdf.WithFont(\"IPAexGothic\", bytes)",". Defina como padrão. O negrito precisa ser sintetizado com ",[43,4063,1098],{}," ou pareado com IPAex Mincho, já que IPAex só publica Regular. Mantenha o texto da licença junto do binário.",[14,4066,60],{"id":59},[62,4068,4070],{"className":64,"code":4069,"language":66,"meta":67,"style":67},"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    font, err := os.ReadFile(\"ipaexg.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(25))),\n        gpdf.WithFont(\"IPAexGothic\", font),\n        gpdf.WithDefaultFont(\"IPAexGothic\", 10.5),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"請求書\", template.FontSize(24), template.Bold())\n            c.Text(\"令和8年4月17日発行\")\n            c.Text(\"金額: ¥100,000 (税込)\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[43,4071,4072,4078,4082,4088,4096,4104,4108,4116,4124,4132,4136,4140,4150,4176,4188,4202,4206,4210,4224,4242,4273,4296,4319,4323,4327,4341,4365,4395,4434,4453,4472,4476,4480,4484,4502,4514,4528,4532,4573,4587,4591],{"__ignoreMap":67},[71,4073,4074,4076],{"class":73,"line":74},[71,4075,78],{"class":77},[71,4077,82],{"class":81},[71,4079,4080],{"class":73,"line":85},[71,4081,89],{"emptyLinePlaceholder":88},[71,4083,4084,4086],{"class":73,"line":92},[71,4085,96],{"class":95},[71,4087,99],{"class":77},[71,4089,4090,4092,4094],{"class":73,"line":102},[71,4091,105],{"class":77},[71,4093,108],{"class":81},[71,4095,111],{"class":77},[71,4097,4098,4100,4102],{"class":73,"line":114},[71,4099,105],{"class":77},[71,4101,119],{"class":81},[71,4103,111],{"class":77},[71,4105,4106],{"class":73,"line":124},[71,4107,89],{"emptyLinePlaceholder":88},[71,4109,4110,4112,4114],{"class":73,"line":129},[71,4111,105],{"class":77},[71,4113,134],{"class":81},[71,4115,111],{"class":77},[71,4117,4118,4120,4122],{"class":73,"line":139},[71,4119,105],{"class":77},[71,4121,144],{"class":81},[71,4123,111],{"class":77},[71,4125,4126,4128,4130],{"class":73,"line":149},[71,4127,105],{"class":77},[71,4129,154],{"class":81},[71,4131,111],{"class":77},[71,4133,4134],{"class":73,"line":159},[71,4135,162],{"class":77},[71,4137,4138],{"class":73,"line":165},[71,4139,89],{"emptyLinePlaceholder":88},[71,4141,4142,4144,4146,4148],{"class":73,"line":170},[71,4143,173],{"class":77},[71,4145,177],{"class":176},[71,4147,180],{"class":77},[71,4149,183],{"class":77},[71,4151,4152,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172,4174],{"class":73,"line":186},[71,4153,190],{"class":189},[71,4155,193],{"class":77},[71,4157,196],{"class":189},[71,4159,199],{"class":77},[71,4161,202],{"class":189},[71,4163,205],{"class":77},[71,4165,208],{"class":176},[71,4167,211],{"class":77},[71,4169,214],{"class":77},[71,4171,4057],{"class":217},[71,4173,214],{"class":77},[71,4175,162],{"class":77},[71,4177,4178,4180,4182,4184,4186],{"class":73,"line":224},[71,4179,227],{"class":95},[71,4181,196],{"class":189},[71,4183,232],{"class":77},[71,4185,235],{"class":77},[71,4187,183],{"class":77},[71,4189,4190,4192,4194,4196,4198,4200],{"class":73,"line":240},[71,4191,243],{"class":189},[71,4193,205],{"class":77},[71,4195,248],{"class":176},[71,4197,211],{"class":77},[71,4199,253],{"class":189},[71,4201,162],{"class":77},[71,4203,4204],{"class":73,"line":258},[71,4205,261],{"class":77},[71,4207,4208],{"class":73,"line":264},[71,4209,89],{"emptyLinePlaceholder":88},[71,4211,4212,4214,4216,4218,4220,4222],{"class":73,"line":269},[71,4213,272],{"class":189},[71,4215,199],{"class":77},[71,4217,277],{"class":189},[71,4219,205],{"class":77},[71,4221,282],{"class":176},[71,4223,285],{"class":77},[71,4225,4226,4228,4230,4232,4234,4236,4238,4240],{"class":73,"line":288},[71,4227,291],{"class":189},[71,4229,205],{"class":77},[71,4231,296],{"class":176},[71,4233,211],{"class":77},[71,4235,33],{"class":189},[71,4237,205],{"class":77},[71,4239,305],{"class":189},[71,4241,308],{"class":77},[71,4243,4244,4246,4248,4250,4252,4254,4256,4258,4260,4262,4264,4266,4268,4271],{"class":73,"line":311},[71,4245,291],{"class":189},[71,4247,205],{"class":77},[71,4249,318],{"class":176},[71,4251,211],{"class":77},[71,4253,323],{"class":189},[71,4255,205],{"class":77},[71,4257,328],{"class":176},[71,4259,211],{"class":77},[71,4261,323],{"class":189},[71,4263,205],{"class":77},[71,4265,337],{"class":176},[71,4267,211],{"class":77},[71,4269,4270],{"class":342},"25",[71,4272,346],{"class":77},[71,4274,4275,4277,4279,4281,4283,4285,4288,4290,4292,4294],{"class":73,"line":349},[71,4276,291],{"class":189},[71,4278,205],{"class":77},[71,4280,356],{"class":176},[71,4282,211],{"class":77},[71,4284,214],{"class":77},[71,4286,4287],{"class":217},"IPAexGothic",[71,4289,214],{"class":77},[71,4291,193],{"class":77},[71,4293,370],{"class":189},[71,4295,308],{"class":77},[71,4297,4298,4300,4302,4304,4306,4308,4310,4312,4314,4317],{"class":73,"line":375},[71,4299,291],{"class":189},[71,4301,205],{"class":77},[71,4303,382],{"class":176},[71,4305,211],{"class":77},[71,4307,214],{"class":77},[71,4309,4287],{"class":217},[71,4311,214],{"class":77},[71,4313,193],{"class":77},[71,4315,4316],{"class":342}," 10.5",[71,4318,308],{"class":77},[71,4320,4321],{"class":73,"line":400},[71,4322,403],{"class":77},[71,4324,4325],{"class":73,"line":406},[71,4326,89],{"emptyLinePlaceholder":88},[71,4328,4329,4331,4333,4335,4337,4339],{"class":73,"line":411},[71,4330,414],{"class":189},[71,4332,199],{"class":77},[71,4334,419],{"class":189},[71,4336,205],{"class":77},[71,4338,424],{"class":176},[71,4340,427],{"class":77},[71,4342,4343,4345,4347,4349,4351,4353,4355,4357,4359,4361,4363],{"class":73,"line":430},[71,4344,433],{"class":189},[71,4346,205],{"class":77},[71,4348,438],{"class":176},[71,4350,441],{"class":77},[71,4352,445],{"class":444},[71,4354,448],{"class":77},[71,4356,451],{"class":81},[71,4358,205],{"class":77},[71,4360,456],{"class":81},[71,4362,459],{"class":77},[71,4364,183],{"class":77},[71,4366,4367,4369,4371,4373,4375,4377,4379,4381,4383,4385,4387,4389,4391,4393],{"class":73,"line":464},[71,4368,467],{"class":189},[71,4370,205],{"class":77},[71,4372,472],{"class":176},[71,4374,211],{"class":77},[71,4376,477],{"class":342},[71,4378,193],{"class":77},[71,4380,482],{"class":77},[71,4382,485],{"class":444},[71,4384,448],{"class":77},[71,4386,451],{"class":81},[71,4388,205],{"class":77},[71,4390,494],{"class":81},[71,4392,459],{"class":77},[71,4394,183],{"class":77},[71,4396,4397,4399,4401,4403,4405,4407,4410,4412,4414,4416,4418,4420,4422,4424,4426,4428,4430,4432],{"class":73,"line":501},[71,4398,504],{"class":189},[71,4400,205],{"class":77},[71,4402,509],{"class":176},[71,4404,211],{"class":77},[71,4406,214],{"class":77},[71,4408,4409],{"class":217},"請求書",[71,4411,214],{"class":77},[71,4413,193],{"class":77},[71,4415,523],{"class":189},[71,4417,205],{"class":77},[71,4419,528],{"class":176},[71,4421,211],{"class":77},[71,4423,533],{"class":342},[71,4425,536],{"class":77},[71,4427,523],{"class":189},[71,4429,205],{"class":77},[71,4431,543],{"class":176},[71,4433,546],{"class":77},[71,4435,4436,4438,4440,4442,4444,4446,4449,4451],{"class":73,"line":549},[71,4437,504],{"class":189},[71,4439,205],{"class":77},[71,4441,509],{"class":176},[71,4443,211],{"class":77},[71,4445,214],{"class":77},[71,4447,4448],{"class":217},"令和8年4月17日発行",[71,4450,214],{"class":77},[71,4452,162],{"class":77},[71,4454,4455,4457,4459,4461,4463,4465,4468,4470],{"class":73,"line":569},[71,4456,504],{"class":189},[71,4458,205],{"class":77},[71,4460,509],{"class":176},[71,4462,211],{"class":77},[71,4464,214],{"class":77},[71,4466,4467],{"class":217},"金額: ¥100,000 (税込)",[71,4469,214],{"class":77},[71,4471,162],{"class":77},[71,4473,4474],{"class":73,"line":575},[71,4475,572],{"class":77},[71,4477,4478],{"class":73,"line":581},[71,4479,578],{"class":77},[71,4481,4482],{"class":73,"line":586},[71,4483,89],{"emptyLinePlaceholder":88},[71,4485,4486,4488,4490,4492,4494,4496,4498,4500],{"class":73,"line":607},[71,4487,589],{"class":189},[71,4489,193],{"class":77},[71,4491,196],{"class":189},[71,4493,199],{"class":77},[71,4495,419],{"class":189},[71,4497,205],{"class":77},[71,4499,602],{"class":176},[71,4501,427],{"class":77},[71,4503,4504,4506,4508,4510,4512],{"class":73,"line":620},[71,4505,227],{"class":95},[71,4507,196],{"class":189},[71,4509,232],{"class":77},[71,4511,235],{"class":77},[71,4513,183],{"class":77},[71,4515,4516,4518,4520,4522,4524,4526],{"class":73,"line":635},[71,4517,243],{"class":189},[71,4519,205],{"class":77},[71,4521,248],{"class":176},[71,4523,211],{"class":77},[71,4525,253],{"class":189},[71,4527,162],{"class":77},[71,4529,4530],{"class":73,"line":640},[71,4531,261],{"class":77},[71,4533,4534,4536,4538,4540,4542,4544,4546,4548,4550,4553,4555,4557,4559,4561,4563,4565,4567,4569,4571],{"class":73,"line":686},[71,4535,227],{"class":95},[71,4537,196],{"class":189},[71,4539,199],{"class":77},[71,4541,202],{"class":189},[71,4543,205],{"class":77},[71,4545,653],{"class":176},[71,4547,211],{"class":77},[71,4549,214],{"class":77},[71,4551,4552],{"class":217},"invoice.pdf",[71,4554,214],{"class":77},[71,4556,193],{"class":77},[71,4558,667],{"class":189},[71,4560,193],{"class":77},[71,4562,672],{"class":342},[71,4564,675],{"class":77},[71,4566,196],{"class":189},[71,4568,232],{"class":77},[71,4570,235],{"class":77},[71,4572,183],{"class":77},[71,4574,4575,4577,4579,4581,4583,4585],{"class":73,"line":701},[71,4576,243],{"class":189},[71,4578,205],{"class":77},[71,4580,248],{"class":176},[71,4582,211],{"class":77},[71,4584,253],{"class":189},[71,4586,162],{"class":77},[71,4588,4589],{"class":73,"line":706},[71,4590,261],{"class":77},[71,4592,4594],{"class":73,"line":4593},41,[71,4595,709],{"class":77},[19,4597,41,4598,4601,4602,4606,4607,4609,4610,4612,4613,205],{},[43,4599,4600],{},"IPAex00401.zip"," em ",[28,4603,4605],{"href":4042,"rel":4604},[32],"moji.or.jp/ipafont",", extraia ",[43,4608,4057],{},", coloque ao lado de ",[43,4611,715],{}," e rode ",[43,4614,719],{},[14,4616,4618],{"id":4617},"qual-arquivo-ipa-é-o-certo","Qual arquivo IPA é o certo",[19,4620,4621],{},"Ao abrir o zip, você tem três TTFs mais uma licença. As pessoas confundem constantemente:",[742,4623,4624,4634],{},[745,4625,4626],{},[748,4627,4628,4631],{},[751,4629,4630],{},"Arquivo",[751,4632,4633],{},"O que é",[759,4635,4636,4648,4661],{},[748,4637,4638,4642],{},[764,4639,4640],{},[43,4641,4057],{},[764,4643,4644,4647],{},[23,4645,4646],{},"IPAex Gothic"," — sans-serif, latino proporcional. Para a maioria dos documentos.",[748,4649,4650,4655],{},[764,4651,4652],{},[43,4653,4654],{},"ipaexm.ttf",[764,4656,4657,4660],{},[23,4658,4659],{},"IPAex Mincho"," — serif, latino proporcional. Corpo de textos longos ou pareada com a Gothic para ênfase.",[748,4662,4663,4668],{},[764,4664,4665],{},[43,4666,4667],{},"ipag.ttf",[764,4669,4670,4673,4674,4677],{},[23,4671,4672],{},"IPA Gothic"," (sem \"ex\") — sans-serif, ",[23,4675,4676],{},"latino monoespaçado",". Raramente é o que você quer hoje.",[19,4679,4680],{},"O \"ex\" em IPAex significa \"extended proportional\". As fontes IPA originais colocavam os caracteres latinos em grades CJK de largura fixa, fazendo o texto misto J/E parecer esticado. IPAex corrige isso tornando os caracteres latinos proporcionais enquanto mantém os CJK na grade normal. Para qualquer documento com palavras em inglês, URLs ou números — ou seja, basicamente todo documento de negócio no Japão — você quer IPAex.",[19,4682,4683,4684,4686],{},"Se você herdou um projeto que usa ",[43,4685,4667],{}," porque o engenheiro que escolheu a fonte fez isso antes de IPAex existir (IPA Gothic original: 2003, IPAex: 2010), a troca é um swap de arquivo. Mesmo nome de família, tudo igual.",[14,4688,4690],{"id":4689},"sem-arquivo-bold-e-agora","Sem arquivo Bold — e agora?",[19,4692,4693],{},"IPAex publica exatamente um peso por família: Regular. É incomum comparado aos nove pesos da Noto Sans JP, e é o principal motivo pelo qual as pessoas olham para IPAex e decidem que não dá para usar.",[19,4695,4696],{},"Duas formas de lidar no gpdf:",[19,4698,4699,898,4702,4704],{},[23,4700,4701],{},"Negrito sintetizado.",[43,4703,1098],{}," aplica um traço sobre os glifos Regular. Tipograficamente é uma trapaça — pesos bold reais têm contornos redesenhados com traços mais grossos, não Regular traçado duas vezes. Mas para títulos de fatura e rótulos de tabela a 10 pt ou mais, o bold sintetizado é indistinguível para a maioria dos leitores:",[62,4706,4708],{"className":64,"code":4707,"language":66,"meta":67,"style":67},"c.Text(\"合計金額\", template.Bold())\n",[43,4709,4710],{"__ignoreMap":67},[71,4711,4712,4714,4716,4718,4720,4722,4725,4727,4729,4731,4733,4735],{"class":73,"line":74},[71,4713,485],{"class":189},[71,4715,205],{"class":77},[71,4717,509],{"class":176},[71,4719,211],{"class":77},[71,4721,214],{"class":77},[71,4723,4724],{"class":217},"合計金額",[71,4726,214],{"class":77},[71,4728,193],{"class":77},[71,4730,523],{"class":189},[71,4732,205],{"class":77},[71,4734,543],{"class":176},[71,4736,546],{"class":77},[19,4738,4739,4742],{},[23,4740,4741],{},"Pareamento com IPAex Mincho."," O recurso clássico da tipografia japonesa para ênfase não é negritar — é alternar serif/sans. Registre as duas famílias:",[62,4744,4746],{"className":64,"code":4745,"language":66,"meta":67,"style":67},"gothic, _ := os.ReadFile(\"ipaexg.ttf\")\nmincho, _ := os.ReadFile(\"ipaexm.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"IPAexGothic\", gothic),\n    gpdf.WithFont(\"IPAexMincho\", mincho),\n    gpdf.WithDefaultFont(\"IPAexGothic\", 10.5),\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"請求書\", template.FontFamily(\"IPAexMincho\"), template.FontSize(24))\n        c.Text(\"ご請求内容は下記の通りです。\")\n    })\n})\n",[43,4747,4748,4775,4802,4806,4820,4843,4867,4889,4893,4897,4921,4951,4999,5018,5022],{"__ignoreMap":67},[71,4749,4750,4753,4755,4757,4759,4761,4763,4765,4767,4769,4771,4773],{"class":73,"line":74},[71,4751,4752],{"class":189},"gothic",[71,4754,193],{"class":77},[71,4756,976],{"class":189},[71,4758,199],{"class":77},[71,4760,202],{"class":189},[71,4762,205],{"class":77},[71,4764,208],{"class":176},[71,4766,211],{"class":77},[71,4768,214],{"class":77},[71,4770,4057],{"class":217},[71,4772,214],{"class":77},[71,4774,162],{"class":77},[71,4776,4777,4780,4782,4784,4786,4788,4790,4792,4794,4796,4798,4800],{"class":73,"line":85},[71,4778,4779],{"class":189},"mincho",[71,4781,193],{"class":77},[71,4783,976],{"class":189},[71,4785,199],{"class":77},[71,4787,202],{"class":189},[71,4789,205],{"class":77},[71,4791,208],{"class":176},[71,4793,211],{"class":77},[71,4795,214],{"class":77},[71,4797,4654],{"class":217},[71,4799,214],{"class":77},[71,4801,162],{"class":77},[71,4803,4804],{"class":73,"line":92},[71,4805,89],{"emptyLinePlaceholder":88},[71,4807,4808,4810,4812,4814,4816,4818],{"class":73,"line":102},[71,4809,1004],{"class":189},[71,4811,199],{"class":77},[71,4813,277],{"class":189},[71,4815,205],{"class":77},[71,4817,282],{"class":176},[71,4819,285],{"class":77},[71,4821,4822,4824,4826,4828,4830,4832,4834,4836,4838,4841],{"class":73,"line":114},[71,4823,1019],{"class":189},[71,4825,205],{"class":77},[71,4827,356],{"class":176},[71,4829,211],{"class":77},[71,4831,214],{"class":77},[71,4833,4287],{"class":217},[71,4835,214],{"class":77},[71,4837,193],{"class":77},[71,4839,4840],{"class":189}," gothic",[71,4842,308],{"class":77},[71,4844,4845,4847,4849,4851,4853,4855,4858,4860,4862,4865],{"class":73,"line":124},[71,4846,1019],{"class":189},[71,4848,205],{"class":77},[71,4850,356],{"class":176},[71,4852,211],{"class":77},[71,4854,214],{"class":77},[71,4856,4857],{"class":217},"IPAexMincho",[71,4859,214],{"class":77},[71,4861,193],{"class":77},[71,4863,4864],{"class":189}," mincho",[71,4866,308],{"class":77},[71,4868,4869,4871,4873,4875,4877,4879,4881,4883,4885,4887],{"class":73,"line":129},[71,4870,1019],{"class":189},[71,4872,205],{"class":77},[71,4874,382],{"class":176},[71,4876,211],{"class":77},[71,4878,214],{"class":77},[71,4880,4287],{"class":217},[71,4882,214],{"class":77},[71,4884,193],{"class":77},[71,4886,4316],{"class":342},[71,4888,308],{"class":77},[71,4890,4891],{"class":73,"line":139},[71,4892,162],{"class":77},[71,4894,4895],{"class":73,"line":149},[71,4896,89],{"emptyLinePlaceholder":88},[71,4898,4899,4901,4903,4905,4907,4909,4911,4913,4915,4917,4919],{"class":73,"line":159},[71,4900,1913],{"class":189},[71,4902,205],{"class":77},[71,4904,438],{"class":176},[71,4906,441],{"class":77},[71,4908,445],{"class":444},[71,4910,448],{"class":77},[71,4912,451],{"class":81},[71,4914,205],{"class":77},[71,4916,456],{"class":81},[71,4918,459],{"class":77},[71,4920,183],{"class":77},[71,4922,4923,4925,4927,4929,4931,4933,4935,4937,4939,4941,4943,4945,4947,4949],{"class":73,"line":165},[71,4924,3645],{"class":189},[71,4926,205],{"class":77},[71,4928,472],{"class":176},[71,4930,211],{"class":77},[71,4932,477],{"class":342},[71,4934,193],{"class":77},[71,4936,482],{"class":77},[71,4938,485],{"class":444},[71,4940,448],{"class":77},[71,4942,451],{"class":81},[71,4944,205],{"class":77},[71,4946,494],{"class":81},[71,4948,459],{"class":77},[71,4950,183],{"class":77},[71,4952,4953,4955,4957,4959,4961,4963,4965,4967,4969,4971,4973,4976,4978,4980,4982,4984,4986,4988,4990,4992,4994,4996],{"class":73,"line":170},[71,4954,3676],{"class":189},[71,4956,205],{"class":77},[71,4958,509],{"class":176},[71,4960,211],{"class":77},[71,4962,214],{"class":77},[71,4964,4409],{"class":217},[71,4966,214],{"class":77},[71,4968,193],{"class":77},[71,4970,523],{"class":189},[71,4972,205],{"class":77},[71,4974,4975],{"class":176},"FontFamily",[71,4977,211],{"class":77},[71,4979,214],{"class":77},[71,4981,4857],{"class":217},[71,4983,214],{"class":77},[71,4985,536],{"class":77},[71,4987,523],{"class":189},[71,4989,205],{"class":77},[71,4991,528],{"class":176},[71,4993,211],{"class":77},[71,4995,533],{"class":342},[71,4997,4998],{"class":77},"))\n",[71,5000,5001,5003,5005,5007,5009,5011,5014,5016],{"class":73,"line":186},[71,5002,3676],{"class":189},[71,5004,205],{"class":77},[71,5006,509],{"class":176},[71,5008,211],{"class":77},[71,5010,214],{"class":77},[71,5012,5013],{"class":217},"ご請求内容は下記の通りです。",[71,5015,214],{"class":77},[71,5017,162],{"class":77},[71,5019,5020],{"class":73,"line":224},[71,5021,578],{"class":77},[71,5023,5024],{"class":73,"line":240},[71,5025,3703],{"class":77},[19,5027,5028],{},"É o visual que se vê em convites de casamento e relatórios formais japoneses — Mincho para títulos, Gothic para corpo. Se seu documento vai para um órgão público, essa é provavelmente a combinação que eles esperam.",[14,5030,5032],{"id":5031},"a-ipa-font-license-rapidamente","A IPA Font License, rapidamente",[19,5034,5035,5036,5041],{},"IPAex não é SIL OFL. É a ",[28,5037,5040],{"href":5038,"rel":5039},"https://opensource.org/licenses/IPA",[32],"IPA Font License Agreement v1.0",", aprovada pela OSI e em geral permissiva, mas com duas exigências que vale nomear:",[884,5043,5044,5058],{},[887,5045,5046,5049,5050,5053,5054,5057],{},[23,5047,5048],{},"Preserve o texto da licença"," onde quer que você distribua o binário da fonte. Se você ",[43,5051,5052],{},"//go:embed"," o TTF, empacote também o arquivo de licença. Um ",[43,5055,5056],{},"LICENSES/IPA-FONT-1.0.txt"," na raiz do projeto atende a maior parte dos casos.",[887,5059,5060,5063,5064,5067],{},[23,5061,5062],{},"Não renomeie a fonte."," Se você modificar o próprio TTF e redistribuir, o derivado precisa ter um nome diferente (sem conter \"IPA\" ou \"IPAex\"). Atenção: essa restrição ",[23,5065,5066],{},"não"," se aplica ao subset de glifos feito em tempo de render. O artigo 3.4 da licença isenta explicitamente os documentos de saída criados com a fonte dessa restrição de nomes.",[19,5069,5070,5071,5074],{},"Ou seja: o subset do gpdf em ",[43,5072,5073],{},"doc.Generate()"," está OK. O subset da fonte que fica embutido no seu PDF não precisa de outro nome e não dispara as cláusulas de \"Derivative Font Program\". Você está criando um documento, não redistribuindo uma fonte.",[19,5076,5077],{},"Um detalhe para quem contribui com o gpdf em si: evitamos colocar IPAex no repositório do gpdf (os testes de golden usam fontes SIL OFL como Noto) justamente para que usuários a jusante não precisem pensar na compatibilidade de licença com o LICENSE raiz do próprio projeto. Se você usa IPAex na sua aplicação, a decisão é do seu projeto, não nossa.",[14,5079,5081],{"id":5080},"quando-escolher-ipaex-no-lugar-de-noto-sans-jp","Quando escolher IPAex no lugar de Noto Sans JP",[742,5083,5084,5095],{},[745,5085,5086],{},[748,5087,5088,5091,5093],{},[751,5089,5090],{},"Dimensão",[751,5092,4646],{},[751,5094,757],{},[759,5096,5097,5108,5119,5130,5144,5155],{},[748,5098,5099,5102,5105],{},[764,5100,5101],{},"Pesos publicados",[764,5103,5104],{},"1 (Regular)",[764,5106,5107],{},"9 (Thin → Black)",[748,5109,5110,5113,5116],{},[764,5111,5112],{},"Licença",[764,5114,5115],{},"IPA Font License v1.0",[764,5117,5118],{},"SIL OFL 1.1",[748,5120,5121,5124,5127],{},[764,5122,5123],{},"Tratamento latino",[764,5125,5126],{},"Proporcional (IPAex) ou monoespaçado (IPA)",[764,5128,5129],{},"Proporcional",[748,5131,5132,5135,5141],{},[764,5133,5134],{},"Pré-instalada em",[764,5136,5137,5138],{},"Algumas distros Linux japonesas, TeX Live ",[43,5139,5140],{},"ptex-fonts",[764,5142,5143],{},"Android, ChromeOS",[748,5145,5146,5149,5152],{},[764,5147,5148],{},"Público típico",[764,5150,5151],{},"Governo, jurídico e acadêmico no Japão",[764,5153,5154],{},"Web de consumo, internacional",[748,5156,5157,5160,5163],{},[764,5158,5159],{},"Tamanho",[764,5161,5162],{},"7,5 MB (Gothic)",[764,5164,5165],{},"5 MB (só Regular)",[19,5167,5168],{},"Escolha IPAex quando sua saída atravessa uma fronteira institucional japonesa — envios de PDF para e-Tax, peças processuais, artigos para revistas japonesas — porque avaliadores, revisores e ferramentas de OCR nesses ecossistemas estão calibrados em IPA. Para o resto, Noto Sans JP. Renderizam muito parecido; a escolha é de encaixe no ecossistema, não estética.",[14,5170,1209],{"id":1208},[1114,5172,5173,5178,5183,5187],{},[887,5174,5175,5177],{},[28,5176,1223],{"href":1222}," — a receita geral, serve para qualquer TTF CJK",[887,5179,5180,5182],{},[28,5181,1216],{"href":835}," — a alternativa SIL OFL com nove pesos",[887,5184,5185,1238],{},[28,5186,1237],{"href":1236},[887,5188,5189,5194,5195],{},[28,5190,5193],{"href":5191,"rel":5192},"https://gpdf.dev/pt/docs/guide/fonts",[32],"Guia de fontes"," — referência completa de ",[43,5196,356],{},[14,5198,1242],{"id":1241},[19,5200,1245],{},[62,5202,5203],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,5204,5205],{"__ignoreMap":67},[71,5206,5207,5209,5211],{"class":73,"line":74},[71,5208,66],{"class":81},[71,5210,1259],{"class":217},[71,5212,1262],{"class":217},[19,5214,5215,1269,5218],{},[28,5216,1268],{"href":30,"rel":5217},[32],[28,5219,1274],{"href":1272,"rel":5220},[32],[1276,5222,1278],{},{"title":67,"searchDepth":85,"depth":85,"links":5224},[5225,5226,5227,5228,5229,5230,5231,5232,5233],{"id":16,"depth":85,"text":17},{"id":37,"depth":85,"text":38},{"id":59,"depth":85,"text":60},{"id":4617,"depth":85,"text":4618},{"id":4689,"depth":85,"text":4690},{"id":5031,"depth":85,"text":5032},{"id":5080,"depth":85,"text":5081},{"id":1208,"depth":85,"text":1209},{"id":1241,"depth":85,"text":1242},"Registre ipaexg.ttf com gpdf.WithFont. IPAex Gothic vem com um único peso Regular sob a IPA Font License; o negrito é sintetizado ou pareado com Mincho.",{"name":5236,"totalTime":5237,"tools":5238,"steps":5240},"Usar IPAex Gothic como fonte padrão em um documento gpdf","PT10M",[1299,5239],"ipaexg.ttf (IPAex Gothic v4.01 de moji.or.jp)",[5241,5244,5247,5250,5253],{"name":5242,"text":5243},"Baixe o pacote IPAex em moji.or.jp","Pegue o IPAex00401.zip em moji.or.jp/ipafont. Descompacte e guarde ipaexg.ttf junto com o arquivo de texto do IPA Font License Agreement v1.0 que acompanha o pacote.",{"name":5245,"text":5246},"Carregue os bytes do TTF","Na inicialização do programa, use os.ReadFile(\"ipaexg.ttf\") para carregar a fonte em um []byte. Para implantações em container, //go:embed mantém a fonte dentro do binário Go.",{"name":5248,"text":5249},"Registre a fonte na construção do documento","Passe gpdf.WithFont(\"IPAexGothic\", fontBytes) e gpdf.WithDefaultFont(\"IPAexGothic\", 10.5) para gpdf.NewDocument. 10.5 pt é o tamanho padrão que o Word usa para documentos em japonês.",{"name":5251,"text":5252},"Lide com a falta de arquivo Bold","IPAex Gothic não tem variante Bold. Sintetize o negrito com template.Bold() (o gpdf aplica um traço de 0.4 pt por cima) ou registre IPAex Mincho como família separada para ênfase.",{"name":5254,"text":5255},"Mantenha a licença junto à sua distribuição","A IPA Font License v1.0 exige preservar o texto da licença onde quer que o binário da fonte viaje. Se você //go:embed o TTF, embuta também LICENSES/IPA-FONT-1.0.txt e referencie-o no NOTICE.",{},{"title":1230,"description":5234},"pt/blog/009.ipaex-gothic-gpdf",[1323,1324,1325],"ojWJ80PMXFrlVIlHPQc6CQAiWV9H4nzOl-RVT2uWOb0",{"id":5262,"title":5263,"author":5264,"body":5265,"date":5749,"description":6599,"draft":1293,"extension":1294,"howTo":6600,"image":1317,"meta":6619,"navigation":88,"path":6620,"seo":6621,"stem":6622,"tags":6623,"updated":1317,"__hash__":6625},"blogPt/pt/blog/005.12-column-grid.md","Como funciona o grid de 12 colunas do gpdf?",{"name":8,"url":9},{"type":11,"value":5266,"toc":6587},[5267,5269,5279,5283,5297,5301,6245,6251,6255,6262,6266,6285,6299,6303,6306,6320,6334,6338,6344,6350,6504,6508,6514,6525,6528,6534,6538,6559,6561,6563,6575,6584],[14,5268,17],{"id":16},[19,5270,5271,5272,855,5275,5278],{},"Você já viu a API do gpdf — page builder, row builder, column builder — e o construtor da coluna recebe um número: ",[43,5273,5274],{},"r.Col(4, fn)",[43,5276,5277],{},"r.Col(8, fn)",". O que é esse número, o que acontece se os spans não somarem 12, e como isso se compara ao grid que você já conhece do CSS?",[14,5280,5282],{"id":5281},"resposta-curta","Resposta curta",[19,5284,5285,5288,5289,5292,5293,5296],{},[43,5286,5287],{},"r.Col(span, fn)"," recebe um inteiro de 1 a 12. Esse inteiro é a fatia da linha que a coluna ocupa — ",[43,5290,5291],{},"span / 12"," da largura disponível. Valores abaixo de 1 são fixados em 1, acima de 12 em 12, e ",[23,5294,5295],{},"a biblioteca não força que os spans somem 12 por linha",". O grid tem 12 divisões fixas; o resto é você decidindo como cortar cada linha.",[14,5298,5300],{"id":5299},"um-exemplo-que-funciona","Um exemplo que funciona",[62,5302,5304],{"className":64,"code":5303,"language":66,"meta":67,"style":67},"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(15))),\n    )\n\n    page := doc.AddPage()\n\n    // Largura total\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"Nota Fiscal #2026-0416\", template.FontSize(18), template.Bold())\n        })\n    })\n\n    // Cabeçalho em duas colunas (6 + 6)\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"Destinatário\")\n            c.Text(\"Acme Ltda.\")\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"Data de emissão\")\n            c.Text(\"2026-04-16\")\n        })\n    })\n\n    // Resumo em três colunas (4 + 4 + 4)\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Subtotal\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Imposto\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Total\")\n        })\n    })\n\n    // Assimétrica (8 + 4) — corpo + painel lateral\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(8, func(c *template.ColBuilder) {\n            c.Text(\"Os itens aparecem aqui\")\n        })\n        r.Col(4, func(c *template.ColBuilder) {\n            c.Text(\"Observações\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"layout.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[43,5305,5306,5312,5316,5322,5330,5338,5342,5350,5358,5366,5370,5374,5384,5398,5416,5447,5451,5455,5469,5473,5478,5502,5532,5572,5576,5580,5584,5589,5613,5644,5663,5682,5686,5716,5735,5754,5758,5762,5766,5771,5795,5826,5846,5851,5882,5902,5907,5938,5957,5962,5967,5972,5978,6003,6035,6055,6060,6091,6111,6116,6121,6126,6145,6158,6173,6178,6220,6235,6240],{"__ignoreMap":67},[71,5307,5308,5310],{"class":73,"line":74},[71,5309,78],{"class":77},[71,5311,82],{"class":81},[71,5313,5314],{"class":73,"line":85},[71,5315,89],{"emptyLinePlaceholder":88},[71,5317,5318,5320],{"class":73,"line":92},[71,5319,96],{"class":95},[71,5321,99],{"class":77},[71,5323,5324,5326,5328],{"class":73,"line":102},[71,5325,105],{"class":77},[71,5327,108],{"class":81},[71,5329,111],{"class":77},[71,5331,5332,5334,5336],{"class":73,"line":114},[71,5333,105],{"class":77},[71,5335,119],{"class":81},[71,5337,111],{"class":77},[71,5339,5340],{"class":73,"line":124},[71,5341,89],{"emptyLinePlaceholder":88},[71,5343,5344,5346,5348],{"class":73,"line":129},[71,5345,105],{"class":77},[71,5347,134],{"class":81},[71,5349,111],{"class":77},[71,5351,5352,5354,5356],{"class":73,"line":139},[71,5353,105],{"class":77},[71,5355,144],{"class":81},[71,5357,111],{"class":77},[71,5359,5360,5362,5364],{"class":73,"line":149},[71,5361,105],{"class":77},[71,5363,154],{"class":81},[71,5365,111],{"class":77},[71,5367,5368],{"class":73,"line":159},[71,5369,162],{"class":77},[71,5371,5372],{"class":73,"line":165},[71,5373,89],{"emptyLinePlaceholder":88},[71,5375,5376,5378,5380,5382],{"class":73,"line":170},[71,5377,173],{"class":77},[71,5379,177],{"class":176},[71,5381,180],{"class":77},[71,5383,183],{"class":77},[71,5385,5386,5388,5390,5392,5394,5396],{"class":73,"line":186},[71,5387,272],{"class":189},[71,5389,199],{"class":77},[71,5391,277],{"class":189},[71,5393,205],{"class":77},[71,5395,282],{"class":176},[71,5397,285],{"class":77},[71,5399,5400,5402,5404,5406,5408,5410,5412,5414],{"class":73,"line":224},[71,5401,291],{"class":189},[71,5403,205],{"class":77},[71,5405,296],{"class":176},[71,5407,211],{"class":77},[71,5409,323],{"class":189},[71,5411,205],{"class":77},[71,5413,305],{"class":189},[71,5415,308],{"class":77},[71,5417,5418,5420,5422,5424,5426,5428,5430,5432,5434,5436,5438,5440,5442,5445],{"class":73,"line":240},[71,5419,291],{"class":189},[71,5421,205],{"class":77},[71,5423,318],{"class":176},[71,5425,211],{"class":77},[71,5427,323],{"class":189},[71,5429,205],{"class":77},[71,5431,328],{"class":176},[71,5433,211],{"class":77},[71,5435,323],{"class":189},[71,5437,205],{"class":77},[71,5439,337],{"class":176},[71,5441,211],{"class":77},[71,5443,5444],{"class":342},"15",[71,5446,346],{"class":77},[71,5448,5449],{"class":73,"line":258},[71,5450,403],{"class":77},[71,5452,5453],{"class":73,"line":264},[71,5454,89],{"emptyLinePlaceholder":88},[71,5456,5457,5459,5461,5463,5465,5467],{"class":73,"line":269},[71,5458,414],{"class":189},[71,5460,199],{"class":77},[71,5462,419],{"class":189},[71,5464,205],{"class":77},[71,5466,424],{"class":176},[71,5468,427],{"class":77},[71,5470,5471],{"class":73,"line":288},[71,5472,89],{"emptyLinePlaceholder":88},[71,5474,5475],{"class":73,"line":311},[71,5476,5477],{"class":2198},"    // Largura total\n",[71,5479,5480,5482,5484,5486,5488,5490,5492,5494,5496,5498,5500],{"class":73,"line":349},[71,5481,433],{"class":189},[71,5483,205],{"class":77},[71,5485,438],{"class":176},[71,5487,441],{"class":77},[71,5489,445],{"class":444},[71,5491,448],{"class":77},[71,5493,451],{"class":81},[71,5495,205],{"class":77},[71,5497,456],{"class":81},[71,5499,459],{"class":77},[71,5501,183],{"class":77},[71,5503,5504,5506,5508,5510,5512,5514,5516,5518,5520,5522,5524,5526,5528,5530],{"class":73,"line":375},[71,5505,467],{"class":189},[71,5507,205],{"class":77},[71,5509,472],{"class":176},[71,5511,211],{"class":77},[71,5513,477],{"class":342},[71,5515,193],{"class":77},[71,5517,482],{"class":77},[71,5519,485],{"class":444},[71,5521,448],{"class":77},[71,5523,451],{"class":81},[71,5525,205],{"class":77},[71,5527,494],{"class":81},[71,5529,459],{"class":77},[71,5531,183],{"class":77},[71,5533,5534,5536,5538,5540,5542,5544,5547,5549,5551,5553,5555,5557,5559,5562,5564,5566,5568,5570],{"class":73,"line":400},[71,5535,504],{"class":189},[71,5537,205],{"class":77},[71,5539,509],{"class":176},[71,5541,211],{"class":77},[71,5543,214],{"class":77},[71,5545,5546],{"class":217},"Nota Fiscal #2026-0416",[71,5548,214],{"class":77},[71,5550,193],{"class":77},[71,5552,523],{"class":189},[71,5554,205],{"class":77},[71,5556,528],{"class":176},[71,5558,211],{"class":77},[71,5560,5561],{"class":342},"18",[71,5563,536],{"class":77},[71,5565,523],{"class":189},[71,5567,205],{"class":77},[71,5569,543],{"class":176},[71,5571,546],{"class":77},[71,5573,5574],{"class":73,"line":406},[71,5575,572],{"class":77},[71,5577,5578],{"class":73,"line":411},[71,5579,578],{"class":77},[71,5581,5582],{"class":73,"line":430},[71,5583,89],{"emptyLinePlaceholder":88},[71,5585,5586],{"class":73,"line":464},[71,5587,5588],{"class":2198},"    // Cabeçalho em duas colunas (6 + 6)\n",[71,5590,5591,5593,5595,5597,5599,5601,5603,5605,5607,5609,5611],{"class":73,"line":501},[71,5592,433],{"class":189},[71,5594,205],{"class":77},[71,5596,438],{"class":176},[71,5598,441],{"class":77},[71,5600,445],{"class":444},[71,5602,448],{"class":77},[71,5604,451],{"class":81},[71,5606,205],{"class":77},[71,5608,456],{"class":81},[71,5610,459],{"class":77},[71,5612,183],{"class":77},[71,5614,5615,5617,5619,5621,5623,5626,5628,5630,5632,5634,5636,5638,5640,5642],{"class":73,"line":549},[71,5616,467],{"class":189},[71,5618,205],{"class":77},[71,5620,472],{"class":176},[71,5622,211],{"class":77},[71,5624,5625],{"class":342},"6",[71,5627,193],{"class":77},[71,5629,482],{"class":77},[71,5631,485],{"class":444},[71,5633,448],{"class":77},[71,5635,451],{"class":81},[71,5637,205],{"class":77},[71,5639,494],{"class":81},[71,5641,459],{"class":77},[71,5643,183],{"class":77},[71,5645,5646,5648,5650,5652,5654,5656,5659,5661],{"class":73,"line":569},[71,5647,504],{"class":189},[71,5649,205],{"class":77},[71,5651,509],{"class":176},[71,5653,211],{"class":77},[71,5655,214],{"class":77},[71,5657,5658],{"class":217},"Destinatário",[71,5660,214],{"class":77},[71,5662,162],{"class":77},[71,5664,5665,5667,5669,5671,5673,5675,5678,5680],{"class":73,"line":575},[71,5666,504],{"class":189},[71,5668,205],{"class":77},[71,5670,509],{"class":176},[71,5672,211],{"class":77},[71,5674,214],{"class":77},[71,5676,5677],{"class":217},"Acme Ltda.",[71,5679,214],{"class":77},[71,5681,162],{"class":77},[71,5683,5684],{"class":73,"line":581},[71,5685,572],{"class":77},[71,5687,5688,5690,5692,5694,5696,5698,5700,5702,5704,5706,5708,5710,5712,5714],{"class":73,"line":586},[71,5689,467],{"class":189},[71,5691,205],{"class":77},[71,5693,472],{"class":176},[71,5695,211],{"class":77},[71,5697,5625],{"class":342},[71,5699,193],{"class":77},[71,5701,482],{"class":77},[71,5703,485],{"class":444},[71,5705,448],{"class":77},[71,5707,451],{"class":81},[71,5709,205],{"class":77},[71,5711,494],{"class":81},[71,5713,459],{"class":77},[71,5715,183],{"class":77},[71,5717,5718,5720,5722,5724,5726,5728,5731,5733],{"class":73,"line":607},[71,5719,504],{"class":189},[71,5721,205],{"class":77},[71,5723,509],{"class":176},[71,5725,211],{"class":77},[71,5727,214],{"class":77},[71,5729,5730],{"class":217},"Data de emissão",[71,5732,214],{"class":77},[71,5734,162],{"class":77},[71,5736,5737,5739,5741,5743,5745,5747,5750,5752],{"class":73,"line":620},[71,5738,504],{"class":189},[71,5740,205],{"class":77},[71,5742,509],{"class":176},[71,5744,211],{"class":77},[71,5746,214],{"class":77},[71,5748,5749],{"class":217},"2026-04-16",[71,5751,214],{"class":77},[71,5753,162],{"class":77},[71,5755,5756],{"class":73,"line":635},[71,5757,572],{"class":77},[71,5759,5760],{"class":73,"line":640},[71,5761,578],{"class":77},[71,5763,5764],{"class":73,"line":686},[71,5765,89],{"emptyLinePlaceholder":88},[71,5767,5768],{"class":73,"line":701},[71,5769,5770],{"class":2198},"    // Resumo em três colunas (4 + 4 + 4)\n",[71,5772,5773,5775,5777,5779,5781,5783,5785,5787,5789,5791,5793],{"class":73,"line":706},[71,5774,433],{"class":189},[71,5776,205],{"class":77},[71,5778,438],{"class":176},[71,5780,441],{"class":77},[71,5782,445],{"class":444},[71,5784,448],{"class":77},[71,5786,451],{"class":81},[71,5788,205],{"class":77},[71,5790,456],{"class":81},[71,5792,459],{"class":77},[71,5794,183],{"class":77},[71,5796,5797,5799,5801,5803,5805,5808,5810,5812,5814,5816,5818,5820,5822,5824],{"class":73,"line":4593},[71,5798,467],{"class":189},[71,5800,205],{"class":77},[71,5802,472],{"class":176},[71,5804,211],{"class":77},[71,5806,5807],{"class":342},"4",[71,5809,193],{"class":77},[71,5811,482],{"class":77},[71,5813,485],{"class":444},[71,5815,448],{"class":77},[71,5817,451],{"class":81},[71,5819,205],{"class":77},[71,5821,494],{"class":81},[71,5823,459],{"class":77},[71,5825,183],{"class":77},[71,5827,5829,5831,5833,5835,5837,5839,5842,5844],{"class":73,"line":5828},42,[71,5830,504],{"class":189},[71,5832,205],{"class":77},[71,5834,509],{"class":176},[71,5836,211],{"class":77},[71,5838,214],{"class":77},[71,5840,5841],{"class":217},"Subtotal",[71,5843,214],{"class":77},[71,5845,162],{"class":77},[71,5847,5849],{"class":73,"line":5848},43,[71,5850,572],{"class":77},[71,5852,5854,5856,5858,5860,5862,5864,5866,5868,5870,5872,5874,5876,5878,5880],{"class":73,"line":5853},44,[71,5855,467],{"class":189},[71,5857,205],{"class":77},[71,5859,472],{"class":176},[71,5861,211],{"class":77},[71,5863,5807],{"class":342},[71,5865,193],{"class":77},[71,5867,482],{"class":77},[71,5869,485],{"class":444},[71,5871,448],{"class":77},[71,5873,451],{"class":81},[71,5875,205],{"class":77},[71,5877,494],{"class":81},[71,5879,459],{"class":77},[71,5881,183],{"class":77},[71,5883,5885,5887,5889,5891,5893,5895,5898,5900],{"class":73,"line":5884},45,[71,5886,504],{"class":189},[71,5888,205],{"class":77},[71,5890,509],{"class":176},[71,5892,211],{"class":77},[71,5894,214],{"class":77},[71,5896,5897],{"class":217},"Imposto",[71,5899,214],{"class":77},[71,5901,162],{"class":77},[71,5903,5905],{"class":73,"line":5904},46,[71,5906,572],{"class":77},[71,5908,5910,5912,5914,5916,5918,5920,5922,5924,5926,5928,5930,5932,5934,5936],{"class":73,"line":5909},47,[71,5911,467],{"class":189},[71,5913,205],{"class":77},[71,5915,472],{"class":176},[71,5917,211],{"class":77},[71,5919,5807],{"class":342},[71,5921,193],{"class":77},[71,5923,482],{"class":77},[71,5925,485],{"class":444},[71,5927,448],{"class":77},[71,5929,451],{"class":81},[71,5931,205],{"class":77},[71,5933,494],{"class":81},[71,5935,459],{"class":77},[71,5937,183],{"class":77},[71,5939,5941,5943,5945,5947,5949,5951,5953,5955],{"class":73,"line":5940},48,[71,5942,504],{"class":189},[71,5944,205],{"class":77},[71,5946,509],{"class":176},[71,5948,211],{"class":77},[71,5950,214],{"class":77},[71,5952,2648],{"class":217},[71,5954,214],{"class":77},[71,5956,162],{"class":77},[71,5958,5960],{"class":73,"line":5959},49,[71,5961,572],{"class":77},[71,5963,5965],{"class":73,"line":5964},50,[71,5966,578],{"class":77},[71,5968,5970],{"class":73,"line":5969},51,[71,5971,89],{"emptyLinePlaceholder":88},[71,5973,5975],{"class":73,"line":5974},52,[71,5976,5977],{"class":2198},"    // Assimétrica (8 + 4) — corpo + painel lateral\n",[71,5979,5981,5983,5985,5987,5989,5991,5993,5995,5997,5999,6001],{"class":73,"line":5980},53,[71,5982,433],{"class":189},[71,5984,205],{"class":77},[71,5986,438],{"class":176},[71,5988,441],{"class":77},[71,5990,445],{"class":444},[71,5992,448],{"class":77},[71,5994,451],{"class":81},[71,5996,205],{"class":77},[71,5998,456],{"class":81},[71,6000,459],{"class":77},[71,6002,183],{"class":77},[71,6004,6006,6008,6010,6012,6014,6017,6019,6021,6023,6025,6027,6029,6031,6033],{"class":73,"line":6005},54,[71,6007,467],{"class":189},[71,6009,205],{"class":77},[71,6011,472],{"class":176},[71,6013,211],{"class":77},[71,6015,6016],{"class":342},"8",[71,6018,193],{"class":77},[71,6020,482],{"class":77},[71,6022,485],{"class":444},[71,6024,448],{"class":77},[71,6026,451],{"class":81},[71,6028,205],{"class":77},[71,6030,494],{"class":81},[71,6032,459],{"class":77},[71,6034,183],{"class":77},[71,6036,6038,6040,6042,6044,6046,6048,6051,6053],{"class":73,"line":6037},55,[71,6039,504],{"class":189},[71,6041,205],{"class":77},[71,6043,509],{"class":176},[71,6045,211],{"class":77},[71,6047,214],{"class":77},[71,6049,6050],{"class":217},"Os itens aparecem aqui",[71,6052,214],{"class":77},[71,6054,162],{"class":77},[71,6056,6058],{"class":73,"line":6057},56,[71,6059,572],{"class":77},[71,6061,6063,6065,6067,6069,6071,6073,6075,6077,6079,6081,6083,6085,6087,6089],{"class":73,"line":6062},57,[71,6064,467],{"class":189},[71,6066,205],{"class":77},[71,6068,472],{"class":176},[71,6070,211],{"class":77},[71,6072,5807],{"class":342},[71,6074,193],{"class":77},[71,6076,482],{"class":77},[71,6078,485],{"class":444},[71,6080,448],{"class":77},[71,6082,451],{"class":81},[71,6084,205],{"class":77},[71,6086,494],{"class":81},[71,6088,459],{"class":77},[71,6090,183],{"class":77},[71,6092,6094,6096,6098,6100,6102,6104,6107,6109],{"class":73,"line":6093},58,[71,6095,504],{"class":189},[71,6097,205],{"class":77},[71,6099,509],{"class":176},[71,6101,211],{"class":77},[71,6103,214],{"class":77},[71,6105,6106],{"class":217},"Observações",[71,6108,214],{"class":77},[71,6110,162],{"class":77},[71,6112,6114],{"class":73,"line":6113},59,[71,6115,572],{"class":77},[71,6117,6119],{"class":73,"line":6118},60,[71,6120,578],{"class":77},[71,6122,6124],{"class":73,"line":6123},61,[71,6125,89],{"emptyLinePlaceholder":88},[71,6127,6129,6131,6133,6135,6137,6139,6141,6143],{"class":73,"line":6128},62,[71,6130,589],{"class":189},[71,6132,193],{"class":77},[71,6134,196],{"class":189},[71,6136,199],{"class":77},[71,6138,419],{"class":189},[71,6140,205],{"class":77},[71,6142,602],{"class":176},[71,6144,427],{"class":77},[71,6146,6148,6150,6152,6154,6156],{"class":73,"line":6147},63,[71,6149,227],{"class":95},[71,6151,196],{"class":189},[71,6153,232],{"class":77},[71,6155,235],{"class":77},[71,6157,183],{"class":77},[71,6159,6161,6163,6165,6167,6169,6171],{"class":73,"line":6160},64,[71,6162,243],{"class":189},[71,6164,205],{"class":77},[71,6166,248],{"class":176},[71,6168,211],{"class":77},[71,6170,253],{"class":189},[71,6172,162],{"class":77},[71,6174,6176],{"class":73,"line":6175},65,[71,6177,261],{"class":77},[71,6179,6181,6183,6185,6187,6189,6191,6193,6195,6197,6200,6202,6204,6206,6208,6210,6212,6214,6216,6218],{"class":73,"line":6180},66,[71,6182,227],{"class":95},[71,6184,196],{"class":189},[71,6186,199],{"class":77},[71,6188,202],{"class":189},[71,6190,205],{"class":77},[71,6192,653],{"class":176},[71,6194,211],{"class":77},[71,6196,214],{"class":77},[71,6198,6199],{"class":217},"layout.pdf",[71,6201,214],{"class":77},[71,6203,193],{"class":77},[71,6205,667],{"class":189},[71,6207,193],{"class":77},[71,6209,672],{"class":342},[71,6211,675],{"class":77},[71,6213,196],{"class":189},[71,6215,232],{"class":77},[71,6217,235],{"class":77},[71,6219,183],{"class":77},[71,6221,6223,6225,6227,6229,6231,6233],{"class":73,"line":6222},67,[71,6224,243],{"class":189},[71,6226,205],{"class":77},[71,6228,248],{"class":176},[71,6230,211],{"class":77},[71,6232,253],{"class":189},[71,6234,162],{"class":77},[71,6236,6238],{"class":73,"line":6237},68,[71,6239,261],{"class":77},[71,6241,6243],{"class":73,"line":6242},69,[71,6244,709],{"class":77},[19,6246,6247,6248,6250],{},"Rode ",[43,6249,719],{},". Você obtém uma página com quatro linhas, cada uma dividida de um jeito diferente.",[14,6252,6254],{"id":6253},"por-que-12","Por que 12",[19,6256,6257,6258,6261],{},"12 é divisível por 2, 3, 4 e 6 sem sobra. Isso cobre quase todo layout real: metades (6+6), terços (4+4+4), quartos (3+3+3+3), sidebar + corpo (3+9 ou 4+8), corpo + trilho (8+4). Pegue um número com menos fatores e uma dessas composições deixa de sair barata. O Bootstrap fixou 12 em 2011 pela mesma razão, e hoje \"grid de 12 colunas\" é o vocabulário comum que designer e engenheiro de frontend já falam. O gpdf importa o idioma de propósito — ",[23,6259,6260],{},"um layout de PDF não é um modelo mental diferente do layout web",", mesmo que o destino de render seja papel de largura fixa.",[14,6263,6265],{"id":6264},"a-matemática-explicitada","A matemática, explicitada",[19,6267,6268,6269,6272,6273,6276,6277,6280,6281,6284],{},"Com A4 retrato e 15 mm de margem uniforme, a largura útil é 180 mm. Um ",[43,6270,6271],{},"Col(4)"," dentro de uma linha ocupa 4/12 disso — 60 mm. ",[43,6274,6275],{},"Col(8)"," ocupa 120 mm. Entre colunas não há gutter por padrão. Se quiser respiro, adicione um ",[43,6278,6279],{},"c.Spacer"," dentro da coluna mais curta ou deixe um ",[43,6282,6283],{},"Col(1)"," vazio.",[19,6286,6287,6288,6291,6292,6295,6296,205],{},"A largura é computada como porcentagem em tempo de build (a implementação está em ",[43,6289,6290],{},"gpdf/template/grid.go",") e o motor de layout converte para pontos usando a largura atual da página menos as margens. Ou seja, o mesmo ",[43,6293,6294],{},"r.Col(6, fn)"," tem largura física diferente em A4 e Letter, mas ",[23,6297,6298],{},"a mesma proporção da linha",[14,6300,6302],{"id":6301},"quando-a-soma-não-dá-12","Quando a soma não dá 12",[19,6304,6305],{},"gpdf não valida a soma dos spans. É proposital.",[1114,6307,6308,6314],{},[887,6309,6310,6313],{},[23,6311,6312],{},"Soma \u003C 12",": o lado direito da linha fica em branco. Útil quando você quer ancorar uma coluna à borda esquerda e deixar o resto vazio de propósito.",[887,6315,6316,6319],{},[23,6317,6318],{},"Soma > 12",": a última coluna vaza a margem direita. Geralmente é bug. O PDF sai torto, mas não há crash.",[19,6321,6322,6323,6326,6327,6330,6331,6333],{},"A maioria dos layouts fecha em exatamente 12 por linha, porque é o que preenche a página. Mas quando você quer um bloco de largura 6 centralizado, o jeito natural é ",[43,6324,6325],{},"Col(3)"," vazia, ",[43,6328,6329],{},"Col(6)"," conteúdo, ",[43,6332,6325],{}," vazia — o grid foi desenhado para esse tipo de notação curta.",[14,6335,6337],{"id":6336},"autorow-vs-row","AutoRow vs Row",[19,6339,6340,6343],{},[43,6341,6342],{},"page.AutoRow(fn)"," cresce verticalmente até a coluna mais alta. A maioria das linhas deve usar isso.",[19,6345,6346,6349],{},[43,6347,6348],{},"page.Row(height, fn)"," fixa a altura. Conteúdo que passa é cortado. Use para cabeçalhos de nota fiscal que precisam ter exatamente 30 mm para alinhar o grampeamento downstream, e para qualquer situação em que consistência visual pesa mais do que liberdade de conteúdo.",[62,6351,6353],{"className":64,"code":6352,"language":66,"meta":67,"style":67},"page.Row(document.Mm(30), func(r *template.RowBuilder) {\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"Logo\")\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"Nº da nota\")\n    })\n})\n",[43,6354,6355,6394,6424,6443,6447,6477,6496,6500],{"__ignoreMap":67},[71,6356,6357,6359,6361,6363,6365,6367,6369,6371,6373,6376,6378,6380,6382,6384,6386,6388,6390,6392],{"class":73,"line":74},[71,6358,1913],{"class":189},[71,6360,205],{"class":77},[71,6362,2099],{"class":176},[71,6364,211],{"class":77},[71,6366,323],{"class":189},[71,6368,205],{"class":77},[71,6370,337],{"class":176},[71,6372,211],{"class":77},[71,6374,6375],{"class":342},"30",[71,6377,536],{"class":77},[71,6379,482],{"class":77},[71,6381,445],{"class":444},[71,6383,448],{"class":77},[71,6385,451],{"class":81},[71,6387,205],{"class":77},[71,6389,456],{"class":81},[71,6391,459],{"class":77},[71,6393,183],{"class":77},[71,6395,6396,6398,6400,6402,6404,6406,6408,6410,6412,6414,6416,6418,6420,6422],{"class":73,"line":85},[71,6397,3645],{"class":189},[71,6399,205],{"class":77},[71,6401,472],{"class":176},[71,6403,211],{"class":77},[71,6405,6016],{"class":342},[71,6407,193],{"class":77},[71,6409,482],{"class":77},[71,6411,485],{"class":444},[71,6413,448],{"class":77},[71,6415,451],{"class":81},[71,6417,205],{"class":77},[71,6419,494],{"class":81},[71,6421,459],{"class":77},[71,6423,183],{"class":77},[71,6425,6426,6428,6430,6432,6434,6436,6439,6441],{"class":73,"line":92},[71,6427,3676],{"class":189},[71,6429,205],{"class":77},[71,6431,509],{"class":176},[71,6433,211],{"class":77},[71,6435,214],{"class":77},[71,6437,6438],{"class":217},"Logo",[71,6440,214],{"class":77},[71,6442,162],{"class":77},[71,6444,6445],{"class":73,"line":102},[71,6446,578],{"class":77},[71,6448,6449,6451,6453,6455,6457,6459,6461,6463,6465,6467,6469,6471,6473,6475],{"class":73,"line":114},[71,6450,3645],{"class":189},[71,6452,205],{"class":77},[71,6454,472],{"class":176},[71,6456,211],{"class":77},[71,6458,5807],{"class":342},[71,6460,193],{"class":77},[71,6462,482],{"class":77},[71,6464,485],{"class":444},[71,6466,448],{"class":77},[71,6468,451],{"class":81},[71,6470,205],{"class":77},[71,6472,494],{"class":81},[71,6474,459],{"class":77},[71,6476,183],{"class":77},[71,6478,6479,6481,6483,6485,6487,6489,6492,6494],{"class":73,"line":124},[71,6480,3676],{"class":189},[71,6482,205],{"class":77},[71,6484,509],{"class":176},[71,6486,211],{"class":77},[71,6488,214],{"class":77},[71,6490,6491],{"class":217},"Nº da nota",[71,6493,214],{"class":77},[71,6495,162],{"class":77},[71,6497,6498],{"class":73,"line":129},[71,6499,578],{"class":77},[71,6501,6502],{"class":73,"line":139},[71,6503,3703],{"class":77},[14,6505,6507],{"id":6506},"o-que-o-grid-não-faz","O que o grid não faz",[19,6509,6510,6511,6513],{},"Sem aninhamento. O ",[43,6512,494],{}," aceita elementos de conteúdo (Text / Image / Table / List / Spacer), mas não outra linha dentro. Layouts que parecem pedir aninhamento normalmente ficam mais limpos expressos como duas linhas irmãs no nível da página.",[19,6515,6516,6517,6520,6521,6524],{},"Sem colunas de offset. O ",[43,6518,6519],{},".offset-2"," do Bootstrap não existe aqui. Para empurrar conteúdo para a direita, deixe um ",[43,6522,6523],{},"Col(n)"," vazio à esquerda.",[19,6526,6527],{},"Sem breakpoints. Páginas PDF não redimensionam. O grid produz o mesmo layout em qualquer dispositivo porque a saída é um raster de coordenadas fixas, não um DOM que re-flua.",[19,6529,6530,6533],{},[23,6531,6532],{},"Essas ausências são o ponto",". Cada recurso que o grid não tem é uma classe de ambiguidade que o PDF final não precisa raciocinar.",[14,6535,6537],{"id":6536},"leitura-relacionada","Leitura relacionada",[1114,6539,6540,6546,6551],{},[887,6541,6542,6545],{},[28,6543,6544],{"href":1222},"Como embutir uma fonte japonesa no gpdf?"," — CJK dentro de colunas do grid",[887,6547,6548,6550],{},[28,6549,2869],{"href":2868}," — como a API Builder se compara a gofpdf, gopdf e Maroto",[887,6552,6553,6558],{},[28,6554,6557],{"href":6555,"rel":6556},"https://gpdf.dev/pt/docs/guide/layout",[32],"Guia de layout"," — referência completa de linhas, colunas e espaçamento",[14,6560,1242],{"id":1241},[19,6562,1245],{},[62,6564,6565],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,6566,6567],{"__ignoreMap":67},[71,6568,6569,6571,6573],{"class":73,"line":74},[71,6570,66],{"class":81},[71,6572,1259],{"class":217},[71,6574,1262],{"class":217},[19,6576,6577,1269,6581],{},[28,6578,6580],{"href":30,"rel":6579},[32],"⭐ Favoritar no GitHub",[28,6582,1274],{"href":1272,"rel":6583},[32],[1276,6585,6586],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .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 .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":67,"searchDepth":85,"depth":85,"links":6588},[6589,6590,6591,6592,6593,6594,6595,6596,6597,6598],{"id":16,"depth":85,"text":17},{"id":5281,"depth":85,"text":5282},{"id":5299,"depth":85,"text":5300},{"id":6253,"depth":85,"text":6254},{"id":6264,"depth":85,"text":6265},{"id":6301,"depth":85,"text":6302},{"id":6336,"depth":85,"text":6337},{"id":6506,"depth":85,"text":6507},{"id":6536,"depth":85,"text":6537},{"id":1241,"depth":85,"text":1242},"O grid de 12 colunas do gpdf usa r.Col(span, fn) com span de 1 a 12. A largura da coluna é (span/12) da linha. Sem breakpoints, sem gutter, previsível por design.",{"name":6601,"totalTime":5237,"tools":6602,"steps":6603},"Diagramar uma página com o grid de 12 colunas do gpdf",[1299,134],[6604,6607,6610,6613,6616],{"name":6605,"text":6606},"Abrir uma linha na página","Chame page.AutoRow(fn) para que a altura cresça até a coluna mais alta, ou page.Row(height, fn) quando quiser fixar a altura.",{"name":6608,"text":6609},"Declarar colunas com r.Col(span, fn)","Dentro da linha, chame r.Col(span, fn) uma vez por coluna. span é um inteiro de 1 a 12 — a fração da largura da linha que aquela coluna ocupa.",{"name":6611,"text":6612},"Mantenha a soma dos spans por linha em 12 ou menos","Se a soma for menor que 12, o lado direito fica vazio. Se passar de 12, a última coluna extrapola a linha — geralmente um bug.",{"name":6614,"text":6615},"Preencher a coluna com conteúdo","No callback do ColBuilder chame c.Text, c.Image, c.Table ou c.Spacer. O conteúdo se empilha verticalmente na ordem em que você adiciona.",{"name":6617,"text":6618},"Começar a próxima linha","Chame page.AutoRow de novo para a próxima linha visual. Linhas são independentes — uma linha 4+8 pode ficar logo acima de uma 3+3+3+3.",{},"/pt/blog/12-column-grid",{"title":5263,"description":6599},"pt/blog/005.12-column-grid",[1323,1325,6624],"templates","ya25Z5i-crbHMa_HX6ACdaPzNhVvVz0WnaAV0aXgajo",{"id":6627,"title":6628,"author":6629,"body":6630,"date":5749,"description":8368,"draft":1293,"extension":1294,"howTo":1317,"image":1317,"meta":8369,"navigation":88,"path":8370,"seo":8371,"stem":8372,"tags":8373,"updated":1317,"__hash__":8375},"blogPt/pt/blog/006.go-pdf-fpdf-archived.md","go-pdf/fpdf também foi arquivado. O stack Go PDF de 2026.",{"name":8,"url":9},{"type":11,"value":6631,"toc":8352},[6632,6634,6658,6662,6680,6683,6700,6710,6714,6717,6750,6756,6760,6766,6781,6788,6797,6803,6807,6810,6987,6990,7002,7014,7020,7026,7029,7033,7039,7042,7082,7085,7266,7279,7283,7299,7302,7307,7626,7631,8010,8026,8036,8040,8047,8130,8137,8143,8146,8153,8156,8204,8207,8211,8218,8227,8234,8236,8247,8253,8263,8282,8290,8296,8298,8300,8312,8320,8324,8349],[14,6633,38],{"id":37},[19,6635,6636,6637,6640,6641,6643,6644,6647,6648,6650,6651,6654,6655,6657],{},"Os dois forks mantidos da linhagem ",[43,6638,6639],{},"fpdf"," agora são somente leitura. ",[43,6642,1352],{}," foi arquivado em ",[23,6645,6646],{},"setembro de 2021","; o fork da comunidade ",[43,6649,1426],{}," seguiu em ",[23,6652,6653],{},"2025",". Não há um \"próximo mantenedor\" vindo aí. Para projetos novos, ",[23,6656,33],{}," é o default moderno: Go puro, zero dependências externas, CJK nativo, 10–30× mais rápido em cargas comuns. Este post é um mapa do panorama em 2026 e uma resposta honesta sobre quando gpdf é a escolha certa e quando não é.",[14,6659,6661],{"id":6660},"a-situação","A situação",[19,6663,6664,6665,6668,6669,6672,6673,6676,6677,6679],{},"Um colega digitou ",[43,6666,6667],{},"go get github.com/go-pdf/fpdf"," semana passada e parou no banner do GitHub: ",[2939,6670,6671],{},"\"This repository has been archived by the owner. It is now read-only.\""," Era para ser a versão ",[2939,6674,6675],{},"consertada"," — o fork da comunidade de ",[43,6678,1352],{}," que deveria manter a linhagem viva depois que o original foi arquivado em 2021.",[19,6681,6682],{},"Também está arquivado. O README agora recomenda procurar outra biblioteca.",[19,6684,6685,6686,6689,6690,6692,6693,6695,6696,6699],{},"Se você passou os últimos cinco anos subindo serviços em Go que emitem PDFs — notas fiscais, relatórios, etiquetas de envio, documentos de conformidade — a biblioteca no fundo do seu ",[43,6687,6688],{},"go.mod"," quase certamente foi uma dessas duas. As respostas do Stack Overflow apontam para ",[43,6691,1352],{},". Os tutoriais mais recentes apontam para ",[43,6694,1426],{},". ",[23,6697,6698],{},"Agora as duas são passivo de cadeia de suprimentos",": sem triagem de CVE, sem trabalho de compatibilidade com novas versões de Go, sem correções de performance, sem atualizações da spec.",[19,6701,6702,6703,6706,6707],{},"Este post não é mais um guia de migração linha por linha — ",[28,6704,6705],{"href":2796},"esse a gente já escreveu",". É a versão longa da pergunta que o guia de migração não responde: ",[23,6708,6709],{},"o que realmente funciona para gerar PDF em Go em 2026, e como o ecossistema chegou até aqui?",[14,6711,6713],{"id":6712},"o-que-arquivado-custa-na-prática","O que \"arquivado\" custa na prática",[19,6715,6716],{},"A palavra \"archived\" no GitHub soa leve. Na prática, para uma biblioteca no seu grafo de imports, significa quatro coisas concretas:",[884,6718,6719,6725,6735,6741],{},[887,6720,6721,6724],{},[23,6722,6723],{},"Sem correções de segurança."," Se aparece um problema de memory-safety no parser de TTF, ninguém vai fazer o merge do fix upstream. Você pode forkar e corrigir. A maioria dos times não vai.",[887,6726,6727,6730,6731,6734],{},[23,6728,6729],{},"Sem compatibilidade para a frente com a toolchain Go."," A semântica de variável de laço do Go 1.25 funciona bem com gofpdf hoje. Alguma coisa em ",[43,6732,6733],{},"for range",", ou uma deprecação futura na stdlib, pode quebrar o build amanhã. E quem vai patchar o fork de um repo read-only é você.",[887,6736,6737,6740],{},[23,6738,6739],{},"Sem atualizações de spec."," PDF 2.0 (ISO 32000-2) saiu em 2020. gofpdf implementa em grande parte PDF 1.7. Coisas como arquivos associados a nível de página, metadados XMP ricos e assinaturas digitais modernas (PAdES-B-LT) estão ausentes ou são ligadas por cola de terceiros.",[887,6742,6743,6746,6747,6749],{},[23,6744,6745],{},"Sem progresso em CJK."," O caminho Unicode do gofpdf foi retrofitado sobre um design de fonte de um byte. Funciona, mas embute fontes completas em vez de subsets na maioria das configs reais, e certos TTFs de CJK produzem colisão de glyph-id com saída corrompida. ",[43,6748,1426],{}," herdou a mesma arquitetura.",[19,6751,6752,6753,205],{},"Segurança e compatibilidade para a frente são as que mordem em conversas de compliance. \"Nossa biblioteca de PDF está arquivada e não recebe patches de CVE\" não é uma resposta que o auditor quer ouvir. ",[23,6754,6755],{},"Se você guarda NFes ou emite DANFEs no escopo da Receita Federal, esse ponto não dá mais para empurrar",[14,6757,6759],{"id":6758},"por-que-os-dois-forks-morreram","Por que os dois forks morreram",[19,6761,6762,6763],{},"É tentador explicar os arquivamentos como burnout de mantenedor — uma pessoa cansada de revisar PRs, um bus factor de um saindo do ar. É parte disso, mas não é tudo. ",[23,6764,6765],{},"A arquitetura dificultou o acompanhamento.",[19,6767,6768,6770,6771,855,6774,855,6777,6780],{},[43,6769,1352],{}," era um port de FPDF — uma biblioteca PHP de 2002. O original em PHP empurrava um cursor pela página e cuspia conteúdo procedural: ",[43,6772,6773],{},"SetXY(x, y)",[43,6775,6776],{},"Cell(w, h, text)",[43,6778,6779],{},"Ln(h)",". Aquele modelo era um acordo razoável para o PHP de 2002, onde a alternativa era PostScript cru ou toolkits proprietários. Portado para Go, manteve o cursor, manteve as tabelas de fonte de um byte, manteve o bookkeeping manual de quebra de página.",[19,6782,6783,6784,6787],{},"Ano após ano, a distância entre o que as pessoas queriam gerar e o que o modelo de cursor conseguia expressar só aumentou. Nota fiscal é tabela. Relatório é grid com chrome que repete. Etiqueta de envio é QR code mais texto em idioma local. O cursor foi embrulhado em helpers, os helpers em tutoriais, e lá por 2023 a maior parte do código que as pessoas escreviam ",[2939,6785,6786],{},"contra"," gofpdf não era gofpdf — era uma camada de cola por time tentando fingir que o cursor era um motor de layout.",[19,6789,6790,6792,6793,6796],{},[43,6791,1426],{}," herdou isso. O fork refatorou internos e corrigiu bugs antigos, mas ",[23,6794,6795],{},"não podia mudar a API pública"," sem quebrar todo projeto dependente. O formato da biblioteca estava congelado no PHP de 2002, e o custo de manter esse formato vivo crescia mais rápido do que o benefício.",[19,6798,6799,6802],{},[23,6800,6801],{},"Então",": dois mantenedores, dois arquivamentos, uma razão arquitetural. Recomeçar em 2026 significa escolher uma abordagem que combine com como PDFs são produzidos hoje — que se parece muito mais com montar uma página web do que com dirigir um plotter.",[14,6804,6806],{"id":6805},"o-panorama-go-pdf-em-2026","O panorama Go PDF em 2026",[19,6808,6809],{},"Antes de recomendar qualquer coisa, o campo inteiro. Vou usar \"mantida\" de forma frouxa para significar \"um commit nos últimos 6 meses e issues que recebem resposta\".",[742,6811,6812,6833],{},[745,6813,6814],{},[748,6815,6816,6819,6822,6824,6827,6830],{},[751,6817,6818],{},"Biblioteca",[751,6820,6821],{},"Status (2026-04)",[751,6823,5112],{},[751,6825,6826],{},"CJK nativo",[751,6828,6829],{},"Zero deps",[751,6831,6832],{},"Notas",[759,6834,6835,6858,6878,6897,6921,6942,6965],{},[748,6836,6837,6841,6846,6849,6852,6855],{},[764,6838,6839],{},[43,6840,1352],{},[764,6842,6843],{},[23,6844,6845],{},"Arquivada 2021",[764,6847,6848],{},"MIT",[764,6850,6851],{},"Retrofit",[764,6853,6854],{},"Sim",[764,6856,6857],{},"A original. Ainda é o primeiro resultado em quase todos os idiomas.",[748,6859,6860,6864,6869,6871,6873,6875],{},[764,6861,6862],{},[43,6863,1426],{},[764,6865,6866],{},[23,6867,6868],{},"Arquivada 2025",[764,6870,6848],{},[764,6872,6851],{},[764,6874,6854],{},[764,6876,6877],{},"Fork da comunidade. Mesma arquitetura, mesmo teto.",[748,6879,6880,6884,6887,6889,6892,6894],{},[764,6881,6882],{},[43,6883,1429],{},[764,6885,6886],{},"Mantida",[764,6888,6848],{},[764,6890,6891],{},"Parcial",[764,6893,6854],{},[764,6895,6896],{},"Baixo nível. Você escreve coordenadas. Bom para overlays de formulário.",[748,6898,6899,6905,6907,6909,6912,6915],{},[764,6900,6901,6904],{},[43,6902,6903],{},"johnfercher/maroto"," v2",[764,6906,6886],{},[764,6908,6848],{},[764,6910,6911],{},"Via gofpdf",[764,6913,6914],{},"Não",[764,6916,6917,6918,6920],{},"Builder com grid, mas depende de ",[43,6919,1426],{}," por baixo.",[748,6922,6923,6928,6930,6935,6937,6939],{},[764,6924,6925],{},[43,6926,6927],{},"unidoc/unipdf",[764,6929,6886],{},[764,6931,6932],{},[23,6933,6934],{},"Comercial",[764,6936,6854],{},[764,6938,6914],{},[764,6940,6941],{},"SDK PDF completo. Licença paga obrigatória para uso comercial.",[748,6943,6944,6950,6952,6955,6957,6962],{},[764,6945,6946,6949],{},[43,6947,6948],{},"chromedp"," + Chromium",[764,6951,6886],{},[764,6953,6954],{},"MIT + Chrome",[764,6956,6854],{},[764,6958,6959],{},[23,6960,6961],{},"Não — traz um browser",[764,6963,6964],{},"HTML→PDF via Chrome headless. Runtime gigante.",[748,6966,6967,6971,6973,6975,6980,6984],{},[764,6968,6969],{},[43,6970,33],{},[764,6972,6886],{},[764,6974,6848],{},[764,6976,6977],{},[23,6978,6979],{},"Nativo",[764,6981,6982],{},[23,6983,6854],{},[764,6985,6986],{},"Reimplementação em Go puro. API builder, grid de 12 colunas.",[19,6988,6989],{},"A tabela sozinha já mostra algumas coisas:",[19,6991,6992,6995,6996,6998,6999,7001],{},[23,6993,6994],{},"Tudo que está mantido é ou comercial, ou traz um runtime gigante, ou senta em cima de uma fundação prestes a ficar obsoleta."," A exceção é ",[43,6997,1429],{}," — genuinamente mantida e com poucas dependências — mas é uma biblioteca de nível de coordenadas. Você volta a ",[43,7000,1917],{}," com outro nome de pacote.",[19,7003,7004,7007,7008,7010,7011,7013],{},[23,7005,7006],{},"Maroto v2 é um builder com grid e uma boa API."," O problema é que no fundo do ",[43,7009,6688],{}," está ",[43,7012,1426],{},". Todo teto de performance e toda limitação de CJK em fpdf também é teto do Maroto. Um v3 maior poderia se soltar disso — ainda não saiu.",[19,7015,7016,7019],{},[23,7017,7018],{},"unipdf é rico em features mas não é MIT-compatível para uso comercial."," Você paga por assento ou por deploy. É uma escolha válida se sua receita sustenta; para um side project OSS ou uma startup no começo, a conta da licença não fecha.",[19,7021,7022,7025],{},[23,7023,7024],{},"chromedp funciona, mas você está enviando um browser."," Uma imagem base de 100 MB vira uma de 1 GB+. Cold start em serverless é doloroso. Fontes ainda precisam estar instaladas no container. A vantagem é que você reaproveita templates de React; a desvantagem é rodar Chromium para renderizar uma NFe.",[19,7027,7028],{},"O buraco é óbvio: uma biblioteca em Go puro, zero deps, CJK nativo, grid-first, que não exige licença comercial nem runtime de browser. É isso que gpdf é.",[14,7030,7032],{"id":7031},"o-que-é-gpdf","O que é gpdf",[19,7034,7035,7036,7038],{},"gpdf (",[43,7037,134],{},") é uma reimplementação limpa. Não é um fork. O writer do formato PDF, o motor de layout e o subsetter TrueType — todos escritos do zero em Go puro.",[19,7040,7041],{},"As três propriedades que importam para a maioria dos times:",[1114,7043,7044,7057,7074],{},[887,7045,7046,898,7049,7052,7053,7056],{},[23,7047,7048],{},"Go puro, sem CGO.",[43,7050,7051],{},"go build"," é estático. ",[43,7054,7055],{},"GOOS=linux GOARCH=arm64 go build"," roda de uma MacBook sem configurar toolchain. Imagens Docker ficam pequenas — um container distroless de 12 MB já roda.",[887,7058,7059,898,7062,7065,7066,7069,7070,7073],{},[23,7060,7061],{},"Zero dependências externas.",[43,7063,7064],{},"go mod graph"," depois de ",[43,7067,7068],{},"go get github.com/gpdf-dev/gpdf"," mostra uma linha só: o próprio gpdf. O core usa apenas ",[43,7071,7072],{},"std",". (Add-ons opcionais para HTML→PDF ou assinaturas digitais trazem dependências pequenas, e são opt-in.)",[887,7075,7076,898,7079,7081],{},[23,7077,7078],{},"CJK nativo.",[43,7080,356],{}," registra uma fonte TrueType na construção do documento. Subset embedding acontece no tempo de render. Uma nota fiscal de 200 caracteres em japonês carrega ~30 KB de subset, não 5 MB de fonte completa.",[19,7083,7084],{},"A forma da API é declarativa. Você descreve uma árvore de linhas e colunas; o motor de layout posiciona. O grid é de 12 colunas — o mesmo idioma que o Bootstrap entrega desde 2011. Se você já escreveu uma linha de HTML/CSS, a API do gpdf é familiar:",[62,7086,7088],{"className":64,"code":7087,"language":66,"meta":67,"style":67},"page := doc.AddPage()\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(8, func(c *template.ColBuilder) {\n        c.Text(\"Nota Fiscal #2026-0416\", template.FontSize(18), template.Bold())\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"2026-04-16\", template.AlignRight())\n    })\n})\n",[43,7089,7090,7105,7129,7159,7197,7201,7231,7258,7262],{"__ignoreMap":67},[71,7091,7092,7095,7097,7099,7101,7103],{"class":73,"line":74},[71,7093,7094],{"class":189},"page ",[71,7096,199],{"class":77},[71,7098,419],{"class":189},[71,7100,205],{"class":77},[71,7102,424],{"class":176},[71,7104,427],{"class":77},[71,7106,7107,7109,7111,7113,7115,7117,7119,7121,7123,7125,7127],{"class":73,"line":85},[71,7108,1913],{"class":189},[71,7110,205],{"class":77},[71,7112,438],{"class":176},[71,7114,441],{"class":77},[71,7116,445],{"class":444},[71,7118,448],{"class":77},[71,7120,451],{"class":81},[71,7122,205],{"class":77},[71,7124,456],{"class":81},[71,7126,459],{"class":77},[71,7128,183],{"class":77},[71,7130,7131,7133,7135,7137,7139,7141,7143,7145,7147,7149,7151,7153,7155,7157],{"class":73,"line":92},[71,7132,3645],{"class":189},[71,7134,205],{"class":77},[71,7136,472],{"class":176},[71,7138,211],{"class":77},[71,7140,6016],{"class":342},[71,7142,193],{"class":77},[71,7144,482],{"class":77},[71,7146,485],{"class":444},[71,7148,448],{"class":77},[71,7150,451],{"class":81},[71,7152,205],{"class":77},[71,7154,494],{"class":81},[71,7156,459],{"class":77},[71,7158,183],{"class":77},[71,7160,7161,7163,7165,7167,7169,7171,7173,7175,7177,7179,7181,7183,7185,7187,7189,7191,7193,7195],{"class":73,"line":102},[71,7162,3676],{"class":189},[71,7164,205],{"class":77},[71,7166,509],{"class":176},[71,7168,211],{"class":77},[71,7170,214],{"class":77},[71,7172,5546],{"class":217},[71,7174,214],{"class":77},[71,7176,193],{"class":77},[71,7178,523],{"class":189},[71,7180,205],{"class":77},[71,7182,528],{"class":176},[71,7184,211],{"class":77},[71,7186,5561],{"class":342},[71,7188,536],{"class":77},[71,7190,523],{"class":189},[71,7192,205],{"class":77},[71,7194,543],{"class":176},[71,7196,546],{"class":77},[71,7198,7199],{"class":73,"line":114},[71,7200,578],{"class":77},[71,7202,7203,7205,7207,7209,7211,7213,7215,7217,7219,7221,7223,7225,7227,7229],{"class":73,"line":124},[71,7204,3645],{"class":189},[71,7206,205],{"class":77},[71,7208,472],{"class":176},[71,7210,211],{"class":77},[71,7212,5807],{"class":342},[71,7214,193],{"class":77},[71,7216,482],{"class":77},[71,7218,485],{"class":444},[71,7220,448],{"class":77},[71,7222,451],{"class":81},[71,7224,205],{"class":77},[71,7226,494],{"class":81},[71,7228,459],{"class":77},[71,7230,183],{"class":77},[71,7232,7233,7235,7237,7239,7241,7243,7245,7247,7249,7251,7253,7256],{"class":73,"line":129},[71,7234,3676],{"class":189},[71,7236,205],{"class":77},[71,7238,509],{"class":176},[71,7240,211],{"class":77},[71,7242,214],{"class":77},[71,7244,5749],{"class":217},[71,7246,214],{"class":77},[71,7248,193],{"class":77},[71,7250,523],{"class":189},[71,7252,205],{"class":77},[71,7254,7255],{"class":176},"AlignRight",[71,7257,546],{"class":77},[71,7259,7260],{"class":73,"line":139},[71,7261,578],{"class":77},[71,7263,7264],{"class":73,"line":149},[71,7265,3703],{"class":77},[19,7267,7268,7269,7271,7272,7275,7276,7278],{},"Mais sobre o grid em ",[28,7270,5263],{"href":6620},". Versão em uma frase: ",[43,7273,7274],{},"Col(span, fn)"," pega span de 1 a 12, e ",[43,7277,5291],{}," é a fração da linha que ele ocupa.",[14,7280,7282],{"id":7281},"o-diff-mínimo-go-pdffpdf-gpdf","O diff mínimo go-pdf/fpdf → gpdf",[19,7284,7285,7286,7288,7289,7291,7292,7294,7295,7298],{},"Se você está vindo especificamente de ",[43,7287,1426],{}," (não de ",[43,7290,1352],{},"), a boa notícia é que as superfícies de API são quase idênticas — ",[43,7293,1426],{}," é um fork que mudou praticamente nada no call-site. A migração para gpdf é a mesma que o ",[28,7296,7297],{"href":2796},"guia de gofpdf"," percorre, com uma troca no caminho do import.",[19,7300,7301],{},"O menor diff possível — um handler HTTP que \"gera um PDF\":",[19,7303,7304],{},[23,7305,7306],{},"Before — go-pdf/fpdf:",[62,7308,7310],{"className":64,"code":7309,"language":66,"meta":67,"style":67},"package main\n\nimport (\n    \"net/http\"\n\n    \"github.com/go-pdf/fpdf\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    pdf := fpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"B\", 16)\n    pdf.Cell(40, 10, \"Hello, World!\")\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := pdf.Output(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[43,7311,7312,7318,7322,7328,7337,7341,7350,7354,7358,7396,7445,7456,7489,7518,7522,7558,7587,7618,7622],{"__ignoreMap":67},[71,7313,7314,7316],{"class":73,"line":74},[71,7315,78],{"class":77},[71,7317,82],{"class":81},[71,7319,7320],{"class":73,"line":85},[71,7321,89],{"emptyLinePlaceholder":88},[71,7323,7324,7326],{"class":73,"line":92},[71,7325,96],{"class":95},[71,7327,99],{"class":77},[71,7329,7330,7332,7335],{"class":73,"line":102},[71,7331,105],{"class":77},[71,7333,7334],{"class":81},"net/http",[71,7336,111],{"class":77},[71,7338,7339],{"class":73,"line":114},[71,7340,89],{"emptyLinePlaceholder":88},[71,7342,7343,7345,7348],{"class":73,"line":124},[71,7344,105],{"class":77},[71,7346,7347],{"class":81},"github.com/go-pdf/fpdf",[71,7349,111],{"class":77},[71,7351,7352],{"class":73,"line":129},[71,7353,162],{"class":77},[71,7355,7356],{"class":73,"line":139},[71,7357,89],{"emptyLinePlaceholder":88},[71,7359,7360,7362,7365,7367,7369,7372,7374,7377,7379,7382,7384,7387,7389,7392,7394],{"class":73,"line":149},[71,7361,173],{"class":77},[71,7363,7364],{"class":176}," handler",[71,7366,211],{"class":77},[71,7368,1861],{"class":444},[71,7370,7371],{"class":81}," http",[71,7373,205],{"class":77},[71,7375,7376],{"class":81},"ResponseWriter",[71,7378,193],{"class":77},[71,7380,7381],{"class":444}," r",[71,7383,448],{"class":77},[71,7385,7386],{"class":81},"http",[71,7388,205],{"class":77},[71,7390,7391],{"class":81},"Request",[71,7393,459],{"class":77},[71,7395,183],{"class":77},[71,7397,7398,7401,7403,7406,7408,7411,7413,7415,7418,7420,7422,7425,7428,7430,7432,7434,7436,7438,7440,7443],{"class":73,"line":159},[71,7399,7400],{"class":189},"    pdf ",[71,7402,199],{"class":77},[71,7404,7405],{"class":189}," fpdf",[71,7407,205],{"class":77},[71,7409,7410],{"class":176},"New",[71,7412,211],{"class":77},[71,7414,214],{"class":77},[71,7416,7417],{"class":217},"P",[71,7419,214],{"class":77},[71,7421,193],{"class":77},[71,7423,7424],{"class":77}," \"",[71,7426,7427],{"class":217},"mm",[71,7429,214],{"class":77},[71,7431,193],{"class":77},[71,7433,7424],{"class":77},[71,7435,305],{"class":217},[71,7437,214],{"class":77},[71,7439,193],{"class":77},[71,7441,7442],{"class":77}," \"\"",[71,7444,162],{"class":77},[71,7446,7447,7450,7452,7454],{"class":73,"line":165},[71,7448,7449],{"class":189},"    pdf",[71,7451,205],{"class":77},[71,7453,424],{"class":176},[71,7455,427],{"class":77},[71,7457,7458,7460,7462,7464,7466,7468,7471,7473,7475,7477,7480,7482,7484,7487],{"class":73,"line":170},[71,7459,7449],{"class":189},[71,7461,205],{"class":77},[71,7463,1745],{"class":176},[71,7465,211],{"class":77},[71,7467,214],{"class":77},[71,7469,7470],{"class":217},"Arial",[71,7472,214],{"class":77},[71,7474,193],{"class":77},[71,7476,7424],{"class":77},[71,7478,7479],{"class":217},"B",[71,7481,214],{"class":77},[71,7483,193],{"class":77},[71,7485,7486],{"class":342}," 16",[71,7488,162],{"class":77},[71,7490,7491,7493,7495,7497,7499,7502,7504,7507,7509,7511,7514,7516],{"class":73,"line":186},[71,7492,7449],{"class":189},[71,7494,205],{"class":77},[71,7496,1921],{"class":176},[71,7498,211],{"class":77},[71,7500,7501],{"class":342},"40",[71,7503,193],{"class":77},[71,7505,7506],{"class":342}," 10",[71,7508,193],{"class":77},[71,7510,7424],{"class":77},[71,7512,7513],{"class":217},"Hello, World!",[71,7515,214],{"class":77},[71,7517,162],{"class":77},[71,7519,7520],{"class":73,"line":224},[71,7521,89],{"emptyLinePlaceholder":88},[71,7523,7524,7527,7529,7532,7535,7538,7540,7542,7545,7547,7549,7551,7554,7556],{"class":73,"line":240},[71,7525,7526],{"class":189},"    w",[71,7528,205],{"class":77},[71,7530,7531],{"class":176},"Header",[71,7533,7534],{"class":77},"().",[71,7536,7537],{"class":176},"Set",[71,7539,211],{"class":77},[71,7541,214],{"class":77},[71,7543,7544],{"class":217},"Content-Type",[71,7546,214],{"class":77},[71,7548,193],{"class":77},[71,7550,7424],{"class":77},[71,7552,7553],{"class":217},"application/pdf",[71,7555,214],{"class":77},[71,7557,162],{"class":77},[71,7559,7560,7562,7564,7566,7569,7571,7573,7575,7577,7579,7581,7583,7585],{"class":73,"line":258},[71,7561,227],{"class":95},[71,7563,196],{"class":189},[71,7565,199],{"class":77},[71,7567,7568],{"class":189}," pdf",[71,7570,205],{"class":77},[71,7572,1925],{"class":176},[71,7574,211],{"class":77},[71,7576,1861],{"class":189},[71,7578,675],{"class":77},[71,7580,196],{"class":189},[71,7582,232],{"class":77},[71,7584,235],{"class":77},[71,7586,183],{"class":77},[71,7588,7589,7592,7594,7597,7599,7601,7603,7606,7608,7610,7613,7616],{"class":73,"line":264},[71,7590,7591],{"class":189},"        http",[71,7593,205],{"class":77},[71,7595,7596],{"class":176},"Error",[71,7598,211],{"class":77},[71,7600,1861],{"class":189},[71,7602,193],{"class":77},[71,7604,7605],{"class":189}," err",[71,7607,205],{"class":77},[71,7609,7596],{"class":176},[71,7611,7612],{"class":77},"(),",[71,7614,7615],{"class":342}," 500",[71,7617,162],{"class":77},[71,7619,7620],{"class":73,"line":269},[71,7621,261],{"class":77},[71,7623,7624],{"class":73,"line":288},[71,7625,709],{"class":77},[19,7627,7628],{},[23,7629,7630],{},"After — gpdf:",[62,7632,7634],{"className":64,"code":7633,"language":66,"meta":67,"style":67},"package main\n\nimport (\n    \"net/http\"\n\n    \"github.com/gpdf-dev/gpdf\"\n    \"github.com/gpdf-dev/gpdf/document\"\n    \"github.com/gpdf-dev/gpdf/template\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\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(16), template.Bold())\n        })\n    })\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := doc.Render(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[43,7635,7636,7642,7646,7652,7660,7664,7672,7680,7688,7692,7696,7728,7742,7760,7790,7794,7798,7812,7836,7866,7905,7909,7913,7917,7947,7976,8002,8006],{"__ignoreMap":67},[71,7637,7638,7640],{"class":73,"line":74},[71,7639,78],{"class":77},[71,7641,82],{"class":81},[71,7643,7644],{"class":73,"line":85},[71,7645,89],{"emptyLinePlaceholder":88},[71,7647,7648,7650],{"class":73,"line":92},[71,7649,96],{"class":95},[71,7651,99],{"class":77},[71,7653,7654,7656,7658],{"class":73,"line":102},[71,7655,105],{"class":77},[71,7657,7334],{"class":81},[71,7659,111],{"class":77},[71,7661,7662],{"class":73,"line":114},[71,7663,89],{"emptyLinePlaceholder":88},[71,7665,7666,7668,7670],{"class":73,"line":124},[71,7667,105],{"class":77},[71,7669,134],{"class":81},[71,7671,111],{"class":77},[71,7673,7674,7676,7678],{"class":73,"line":129},[71,7675,105],{"class":77},[71,7677,144],{"class":81},[71,7679,111],{"class":77},[71,7681,7682,7684,7686],{"class":73,"line":139},[71,7683,105],{"class":77},[71,7685,154],{"class":81},[71,7687,111],{"class":77},[71,7689,7690],{"class":73,"line":149},[71,7691,162],{"class":77},[71,7693,7694],{"class":73,"line":159},[71,7695,89],{"emptyLinePlaceholder":88},[71,7697,7698,7700,7702,7704,7706,7708,7710,7712,7714,7716,7718,7720,7722,7724,7726],{"class":73,"line":165},[71,7699,173],{"class":77},[71,7701,7364],{"class":176},[71,7703,211],{"class":77},[71,7705,1861],{"class":444},[71,7707,7371],{"class":81},[71,7709,205],{"class":77},[71,7711,7376],{"class":81},[71,7713,193],{"class":77},[71,7715,7381],{"class":444},[71,7717,448],{"class":77},[71,7719,7386],{"class":81},[71,7721,205],{"class":77},[71,7723,7391],{"class":81},[71,7725,459],{"class":77},[71,7727,183],{"class":77},[71,7729,7730,7732,7734,7736,7738,7740],{"class":73,"line":170},[71,7731,272],{"class":189},[71,7733,199],{"class":77},[71,7735,277],{"class":189},[71,7737,205],{"class":77},[71,7739,282],{"class":176},[71,7741,285],{"class":77},[71,7743,7744,7746,7748,7750,7752,7754,7756,7758],{"class":73,"line":186},[71,7745,291],{"class":189},[71,7747,205],{"class":77},[71,7749,296],{"class":176},[71,7751,211],{"class":77},[71,7753,323],{"class":189},[71,7755,205],{"class":77},[71,7757,305],{"class":189},[71,7759,308],{"class":77},[71,7761,7762,7764,7766,7768,7770,7772,7774,7776,7778,7780,7782,7784,7786,7788],{"class":73,"line":224},[71,7763,291],{"class":189},[71,7765,205],{"class":77},[71,7767,318],{"class":176},[71,7769,211],{"class":77},[71,7771,323],{"class":189},[71,7773,205],{"class":77},[71,7775,328],{"class":176},[71,7777,211],{"class":77},[71,7779,323],{"class":189},[71,7781,205],{"class":77},[71,7783,337],{"class":176},[71,7785,211],{"class":77},[71,7787,343],{"class":342},[71,7789,346],{"class":77},[71,7791,7792],{"class":73,"line":240},[71,7793,403],{"class":77},[71,7795,7796],{"class":73,"line":258},[71,7797,89],{"emptyLinePlaceholder":88},[71,7799,7800,7802,7804,7806,7808,7810],{"class":73,"line":264},[71,7801,414],{"class":189},[71,7803,199],{"class":77},[71,7805,419],{"class":189},[71,7807,205],{"class":77},[71,7809,424],{"class":176},[71,7811,427],{"class":77},[71,7813,7814,7816,7818,7820,7822,7824,7826,7828,7830,7832,7834],{"class":73,"line":269},[71,7815,433],{"class":189},[71,7817,205],{"class":77},[71,7819,438],{"class":176},[71,7821,441],{"class":77},[71,7823,445],{"class":444},[71,7825,448],{"class":77},[71,7827,451],{"class":81},[71,7829,205],{"class":77},[71,7831,456],{"class":81},[71,7833,459],{"class":77},[71,7835,183],{"class":77},[71,7837,7838,7840,7842,7844,7846,7848,7850,7852,7854,7856,7858,7860,7862,7864],{"class":73,"line":288},[71,7839,467],{"class":189},[71,7841,205],{"class":77},[71,7843,472],{"class":176},[71,7845,211],{"class":77},[71,7847,477],{"class":342},[71,7849,193],{"class":77},[71,7851,482],{"class":77},[71,7853,485],{"class":444},[71,7855,448],{"class":77},[71,7857,451],{"class":81},[71,7859,205],{"class":77},[71,7861,494],{"class":81},[71,7863,459],{"class":77},[71,7865,183],{"class":77},[71,7867,7868,7870,7872,7874,7876,7878,7880,7882,7884,7886,7888,7890,7892,7895,7897,7899,7901,7903],{"class":73,"line":311},[71,7869,504],{"class":189},[71,7871,205],{"class":77},[71,7873,509],{"class":176},[71,7875,211],{"class":77},[71,7877,214],{"class":77},[71,7879,7513],{"class":217},[71,7881,214],{"class":77},[71,7883,193],{"class":77},[71,7885,523],{"class":189},[71,7887,205],{"class":77},[71,7889,528],{"class":176},[71,7891,211],{"class":77},[71,7893,7894],{"class":342},"16",[71,7896,536],{"class":77},[71,7898,523],{"class":189},[71,7900,205],{"class":77},[71,7902,543],{"class":176},[71,7904,546],{"class":77},[71,7906,7907],{"class":73,"line":349},[71,7908,572],{"class":77},[71,7910,7911],{"class":73,"line":375},[71,7912,578],{"class":77},[71,7914,7915],{"class":73,"line":400},[71,7916,89],{"emptyLinePlaceholder":88},[71,7918,7919,7921,7923,7925,7927,7929,7931,7933,7935,7937,7939,7941,7943,7945],{"class":73,"line":406},[71,7920,7526],{"class":189},[71,7922,205],{"class":77},[71,7924,7531],{"class":176},[71,7926,7534],{"class":77},[71,7928,7537],{"class":176},[71,7930,211],{"class":77},[71,7932,214],{"class":77},[71,7934,7544],{"class":217},[71,7936,214],{"class":77},[71,7938,193],{"class":77},[71,7940,7424],{"class":77},[71,7942,7553],{"class":217},[71,7944,214],{"class":77},[71,7946,162],{"class":77},[71,7948,7949,7951,7953,7955,7957,7959,7962,7964,7966,7968,7970,7972,7974],{"class":73,"line":411},[71,7950,227],{"class":95},[71,7952,196],{"class":189},[71,7954,199],{"class":77},[71,7956,419],{"class":189},[71,7958,205],{"class":77},[71,7960,7961],{"class":176},"Render",[71,7963,211],{"class":77},[71,7965,1861],{"class":189},[71,7967,675],{"class":77},[71,7969,196],{"class":189},[71,7971,232],{"class":77},[71,7973,235],{"class":77},[71,7975,183],{"class":77},[71,7977,7978,7980,7982,7984,7986,7988,7990,7992,7994,7996,7998,8000],{"class":73,"line":430},[71,7979,7591],{"class":189},[71,7981,205],{"class":77},[71,7983,7596],{"class":176},[71,7985,211],{"class":77},[71,7987,1861],{"class":189},[71,7989,193],{"class":77},[71,7991,7605],{"class":189},[71,7993,205],{"class":77},[71,7995,7596],{"class":176},[71,7997,7612],{"class":77},[71,7999,7615],{"class":342},[71,8001,162],{"class":77},[71,8003,8004],{"class":73,"line":464},[71,8005,261],{"class":77},[71,8007,8008],{"class":73,"line":501},[71,8009,709],{"class":77},[19,8011,8012,8013,8015,8016,8019,8020,8022,8023,8025],{},"Três linhas de código de cursor viram três chamadas de builder. A estrutura aparece no código-fonte em vez de se esconder dentro da ordem das chamadas a ",[43,8014,1921],{},". Para CJK, adicione ",[43,8017,8018],{},"gpdf.WithFont(\"NotoSansJP\", ttfBytes)"," — sem ",[43,8021,2539],{},", sem caminho do filesystem, sem flag UTF-8. Ver ",[28,8024,6544],{"href":1222}," para o passo a passo completo.",[19,8027,8028,8029,8032,8033,8035],{},"O ",[28,8030,8031],{"href":2796},"guia de migração de gofpdf"," tem mais cinco pares before/after cobrindo tabelas, cabeçalho/rodapé que repetem, números de página e posicionamento absoluto. Tudo que está lá se aplica literalmente a usuários de ",[43,8034,1426],{}," — basta trocar o caminho do import.",[14,8037,8039],{"id":8038},"a-foto-dos-benchmarks","A foto dos benchmarks",[19,8041,8042,8043,8046],{},"\"Rápido\" é fácil de alegar e difícil de conquistar. A tabela abaixo é de ",[43,8044,8045],{},"gpdf/_benchmark/benchmark_test.go"," em um Apple M1 rodando Go 1.25. As cargas são o que o código de produção faz de fato — não micro-benchmarks escolhidos para favorecer uma biblioteca.",[742,8048,8049,8065],{},[745,8050,8051],{},[748,8052,8053,8056,8058,8060,8063],{},[751,8054,8055],{},"Benchmark",[751,8057,33],{},[751,8059,1423],{},[751,8061,8062],{},"gopdf",[751,8064,1432],{},[759,8066,8067,8082,8098,8114],{},[748,8068,8069,8072,8076,8078,8080],{},[764,8070,8071],{},"Página única (hello)",[764,8073,8074],{},[23,8075,1340],{},[764,8077,1446],{},[764,8079,1452],{},[764,8081,1455],{},[748,8083,8084,8087,8091,8093,8095],{},[764,8085,8086],{},"Tabela 4×10 de itens",[764,8088,8089],{},[23,8090,1344],{},[764,8092,1467],{},[764,8094,1473],{},[764,8096,8097],{},"8.6 ms",[748,8099,8100,8103,8107,8109,8111],{},[764,8101,8102],{},"Relatório de 100 páginas",[764,8104,8105],{},[23,8106,1348],{},[764,8108,1356],{},[764,8110,8097],{},[764,8112,8113],{},"19.8 ms",[748,8115,8116,8119,8123,8125,8127],{},[764,8117,8118],{},"Nota fiscal CJK complexa",[764,8120,8121],{},[23,8122,1506],{},[764,8124,1509],{},[764,8126,1515],{},[764,8128,8129],{},"10.4 ms",[19,8131,8132,8133,8136],{},"A 13 µs por página única, um core produz ~75.000 PDFs hello-world por segundo. A 108 µs por nota fiscal, ~9.000 por segundo. ",[23,8134,8135],{},"O ponto não é ostentar números","; é que você pode parar de pensar se precisa cachear ou jogar geração de PDF numa fila assíncrona. Para a maioria das cargas, gerar no caminho da requisição está de bom tamanho.",[19,8138,8139,8140,8142],{},"Maroto v2 aparece lento na tabela porque dirige ",[43,8141,1426],{}," por baixo e adiciona seu próprio passo de layout em cima. Não é crítica à API do Maroto — a API é boa — é o custo estrutural de sentar sobre a fundação fpdf. Quando Maroto v3 soltar a dependência de fpdf, essa coluna muda.",[19,8144,8145],{},"O benchmark de 100 páginas merece um parágrafo extra. O writer streaming do gpdf emite conteúdo enquanto as linhas são layout; gofpdf buferiza mais estado por página. Para cargas pesadas em paginação (relatório mensal, catálogo, export de compliance), a diferença é de minutos contra segundos na ponta alta dos tamanhos.",[14,8147,8149,8150,8152],{"id":8148},"quando-gpdf-não-é-a-escolha-certa","Quando gpdf ",[2939,8151,5066],{}," é a escolha certa",[19,8154,8155],{},"Todo post de migração precisa responder \"quando não mudar?\". Respostas honestas:",[1114,8157,8158,8171,8180,8191],{},[887,8159,8160,8163,8164,8167,8168,8170],{},[23,8161,8162],{},"AcroForm / formulários preenchíveis."," Se seu caso é gerar PDFs que usuários abrem no Acrobat e preenchem, o suporte a campo de formulário no gpdf ainda é mínimo. ",[43,8165,8166],{},"unidoc"," é mais completo aqui; ",[43,8169,1429],{}," tem suporte parcial de AcroForm. Uma release futura do gpdf vai fechar o buraco, mas hoje é um buraco.",[887,8172,8173,898,8176,8179],{},[23,8174,8175],{},"Caminhos vetoriais arbitrários e desenho complexo.",[43,8177,8178],{},"c.Line()"," desenha uma régua horizontal dentro de uma coluna. Se você precisa de bézier, caminhos personalizados ou preenchimento com gradiente para gráficos ou desenhos técnicos, gpdf não chega lá. (Imagens de gráficos pré-renderizadas funcionam bem — isso aqui é sobre primitivas de desenho, não sobre embutir imagem.)",[887,8181,8182,8187,8188,8190],{},[23,8183,8184,8185,205],{},"Bases de código gofpdf existentes com muito ",[43,8186,1917],{}," Se seu código tem 2.000 linhas de manipulação de cursor, a migração é reescrita, não find-and-replace. O código reescrito quase sempre é mais curto, mas \"quase sempre\" é um consolo frio no dia do prazo. O ",[28,8189,2797],{"href":2796}," estima o esforço honestamente.",[887,8192,8193,8196,8197,8200,8201,8203],{},[23,8194,8195],{},"Precisa de HTML → PDF com suporte completo a CSS agora."," gpdf tem um subconjunto de HTML no add-on ",[43,8198,8199],{},"gpdf-pro",", mas paridade total de CSS com Chromium não é objetivo. Se seu template é um componente React complicado, ",[43,8202,6948],{}," ou uma API comercial encaixa mais direto.",[19,8205,8206],{},"Se nenhuma delas morde, gpdf é o default. Se uma morde, o normal é rodar as duas bibliotecas em paralelo — gpdf para os PDFs novos, a anterior para o caso de borda — e migrar o caso de borda depois, quando gpdf chegar lá.",[14,8208,8210],{"id":8209},"o-ângulo-de-compliance","O ângulo de compliance",[19,8212,8213,8214,8217],{},"Uma coisa que quase ninguém discute em posts de ecossistema: ",[23,8215,8216],{},"dependências arquivadas aparecem em auditorias SOC 2 e ISO 27001",". O auditor quer saber que o código de terceiros na sua cadeia de suprimentos é mantido ativamente. \"Arquivada em 2021\" dispara um finding. \"Arquivada em 2025\" dispara um finding. \"Fork que mantemos internamente\" dispara perguntas de follow-up sobre como você vai patchar um zero-day.",[19,8219,8220,8221,6695,8224,8226],{},"É em boa parte por isso que times em empresas maiores passando por security review nos perguntam baixinho quando gpdf vai chegar a um v1 estável. A resposta é: ",[23,8222,8223],{},"já chegou",[43,8225,134],{}," tem tag, semver estável, e a superfície de API v1 está congelada. O projeto tem contato de segurança, política de disclosure responsável, e CI rodando Go 1.22 até 1.26.",[19,8228,8229,8230,8233],{},"Você não migra ",[2939,8231,8232],{},"pela"," auditoria. Migra porque a auditoria está prestes a pedir de todo jeito.",[14,8235,2787],{"id":2786},[19,8237,8238,8241,8242,3534,8244,8246],{},[23,8239,8240],{},"\"O stack Go PDF moderno\" é só gpdf ou várias bibliotecas?","\nPara a maioria dos times é gpdf sozinho — a única biblioteca cobre criação de documento, CJK, tabelas, grid, paginação e saída. Times com requisito de formulário preenchível combinam com ",[43,8243,1429],{},[43,8245,8166],{}," só para esses documentos. Times com export pesado em gráficos pré-renderizam os gráficos para PNG e embutem. \"Stack\" aqui significa lista curta, não arquitetura em camadas.",[19,8248,8249,8252],{},[23,8250,8251],{},"Dá para rodar gpdf e go-pdf/fpdf lado a lado durante a migração?","\nDá. São caminhos de import e tipos diferentes. Roteie os endpoints novos para gpdf e deixe os antigos em go-pdf/fpdf até ter tempo de reescrever. Não tem conflito em runtime.",[19,8254,8255,8258,8259,8262],{},[23,8256,8257],{},"Vai sair um go-pdf/fpdf v3 ou um fork novo?","\nTalvez. A aposta por trás do gpdf não é que ninguém vai desarquivar o fork — é que ",[23,8260,8261],{},"a arquitetura não escala para o que se constrói hoje",". Um fork novo herdaria as mesmas limitações a menos que reescreva o modelo de layout, e aí é mais perto do gpdf do que do fpdf.",[19,8264,8265,8271,8272,855,8275,855,8278,8281],{},[23,8266,8267,8268,8270],{},"E ",[43,8269,1429],{}," como alternativa moderna?","\nGenuinamente mantida e zero deps. A API é nível de coordenada — ",[43,8273,8274],{},"SetX",[43,8276,8277],{},"SetY",[43,8279,8280],{},"CellWithOption"," — casa bem com overlay de formulário e template fixo. Para documentos tipo nota fiscal com tabelas e chrome que repete, você acaba escrevendo um helper de layout em cima, e cai no mesmo poço em que os usuários de gofpdf caíram. gpdf e gopdf não competem de verdade — resolvem problemas vizinhos.",[19,8283,8284,8287,8289],{},[23,8285,8286],{},"gpdf tem uma versão comercial ou hospedada?",[43,8288,2572],{}," está a caminho — uma API hospedada que aceita templates JSON e devolve PDF. Ainda não é pública. No lançamento, vai ter post aqui no blog. A biblioteca OSS continua MIT, zero deps, e útil por conta própria.",[19,8291,8292,8295],{},[23,8293,8294],{},"Qual a ordem de prioridades do roadmap?","\nRoadmap público do gpdf em 2026-04: (1) campos de formulário AcroForm, (2) conformidade completa com PDF/A-3, (3) cobertura expandida de HTML→PDF no gpdf-pro, (4) suporte a texto RTL (árabe, hebraico). Feedback sobre prioridade é bem-vindo nas issues do GitHub.",[14,8297,1242],{"id":1241},[19,8299,1245],{},[62,8301,8302],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,8303,8304],{"__ignoreMap":67},[71,8305,8306,8308,8310],{"class":73,"line":74},[71,8307,66],{"class":81},[71,8309,1259],{"class":217},[71,8311,1262],{"class":217},[19,8313,8314,1269,8317],{},[28,8315,6580],{"href":30,"rel":8316},[32],[28,8318,1274],{"href":1272,"rel":8319},[32],[14,8321,8323],{"id":8322},"próximas-leituras","Próximas leituras",[1114,8325,8326,8332,8337,8342],{},[887,8327,8328,8331],{},[28,8329,8330],{"href":2796},"gofpdf foi arquivada. Como migrar para o gpdf."," — o mapeamento de API linha a linha",[887,8333,8334,8336],{},[28,8335,2869],{"href":2868}," — benchmarks head-to-head mais fundos e grade de features",[887,8338,8339,8341],{},[28,8340,5263],{"href":6620}," — o idioma de builder que substitui o empurrar cursor",[887,8343,8344,8346,8347],{},[28,8345,6544],{"href":1222}," — CJK sem a dança do ",[43,8348,2539],{},[1276,8350,8351],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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}",{"title":67,"searchDepth":85,"depth":85,"links":8353},[8354,8355,8356,8357,8358,8359,8360,8361,8362,8364,8365,8366,8367],{"id":37,"depth":85,"text":38},{"id":6660,"depth":85,"text":6661},{"id":6712,"depth":85,"text":6713},{"id":6758,"depth":85,"text":6759},{"id":6805,"depth":85,"text":6806},{"id":7031,"depth":85,"text":7032},{"id":7281,"depth":85,"text":7282},{"id":8038,"depth":85,"text":8039},{"id":8148,"depth":85,"text":8363},"Quando gpdf não é a escolha certa",{"id":8209,"depth":85,"text":8210},{"id":2786,"depth":85,"text":2787},{"id":1241,"depth":85,"text":1242},{"id":8322,"depth":85,"text":8323},"jung-kurt/gofpdf foi arquivado em 2021, go-pdf/fpdf em 2025. Este é o stack Go PDF que usamos em 2026 — gpdf, os trade-offs e o porquê.",{},"/pt/blog/go-pdf-fpdf-archived",{"title":6628,"description":8368},"pt/blog/006.go-pdf-fpdf-archived",[8374,2918,2916],"migration","l9a4_wgQKmyZyb8P7wBzjs0ee66ak5XVBl6R9y6KwPE",{"id":8377,"title":8378,"author":8379,"body":8380,"date":5749,"description":10018,"draft":1293,"extension":1294,"howTo":10019,"image":1317,"meta":10043,"navigation":88,"path":3960,"seo":10044,"stem":10045,"tags":10046,"updated":1317,"__hash__":10047},"blogPt/pt/blog/007.japanese-pdf-in-go.md","PDFs em japonês com Go: o guia definitivo de 2026",{"name":8,"url":9},{"type":11,"value":8381,"toc":10004},[8382,8384,8404,8408,8411,8417,8424,8427,8431,8442,8481,8489,8495,8499,8502,8617,8620,8626,8632,8636,8644,9460,9463,9514,9523,9527,9533,9536,9539,9585,9598,9601,9626,9642,9646,9649,9655,9658,9672,9678,9759,9769,9773,9776,9785,9795,9819,9833,9836,9840,9847,9854,9860,9874,9877,9879,9903,9909,9915,9929,9939,9948,9950,9953,9965,9974,9976,10002],[14,8383,38],{"id":37},[19,8385,8386,8387,8389,8390,8393,8394,8396,8397,8400,8401,205],{},"Se seu PDF em Go renderiza ",[43,8388,3687],{}," como cinco caixas de tofu, a correção são duas linhas de configuração, não uma reescrita. Carregue um TTF japonês, passe ",[43,8391,8392],{},"gpdf.WithFont"," para ",[43,8395,282],{},", escreva em japonês. ",[23,8398,8399],{},"O gpdf faz o subconjunto da tabela de glifos automaticamente",", então a saída carrega apenas os caracteres que você usou — cerca de 30 KB, não os 5 MB da fonte completa. Este guia é o mapa: por que gerar PDFs em japonês com Go tem sido estranhamente difícil, as quatro opções reais em 2026, um exemplo completo e funcional, os detalhes internos do subconjunto de fontes, casos limite de escritas mistas, e ",[23,8402,8403],{},"o que ainda não funciona",[14,8405,8407],{"id":8406},"por-que-este-guia-existe","Por que este guia existe",[19,8409,8410],{},"Tirar um PDF com texto japonês do Go deveria ser trabalho de cinco minutos. Para muitos times é um dia e meio.",[19,8412,8413,8414,8416],{},"A história usual: alguém troca para ",[43,8415,2539],{},", o PDF mostra retângulos em branco — o infame 豆腐 — e um dev sênior passa a tarde descobrindo se o problema é o caminho da fonte, a flag de subconjunto, o CMap, a flag UTF-8 ou o leitor de PDF. Ao cair da noite há uma thread no Slack chamada \"POR QUE 漢字 AINDA ESTÁ QUEBRADO\" e um PR que adiciona três funções auxiliares das quais todos já se arrependem.",[19,8418,8419,8420,8423],{},"A causa raiz não é nenhuma delas. ",[23,8421,8422],{},"A biblioteca de PDF mais longeva do Go foi desenhada em 2002 para PHP e Latin-1",", e quase todos os tutoriais japoneses escritos desde então estão brigando com esse legado. Este guia é a versão 2026: o que realmente funciona quando você começa do zero, e o que ainda é genuinamente difícil.",[19,8425,8426],{},"Todo o código deste post roda com gpdf v1.x em 2026-04. Os números de benchmark são de um Apple M1 com Go 1.25.",[14,8428,8430],{"id":8429},"o-problema-do-tofu-em-90-segundos","O problema do tofu em 90 segundos",[19,8432,8433,8434,8437,8438,8441],{},"PDF não se importa com Unicode. Ele se importa com ",[23,8435,8436],{},"IDs de glifo"," — índices inteiros para a tabela de glifos embutida da fonte. Quando você escreve ",[43,8439,8440],{},"\"こんにちは\""," num PDF, alguém tem que:",[884,8443,8444,8453,8459,8465],{},[887,8445,8446,8449,8450,8452],{},[23,8447,8448],{},"Parsear o TTF"," e achar o ID de glifo para cada code point (via a subtabela ",[43,8451,861],{}," da fonte).",[887,8454,8455,8458],{},[23,8456,8457],{},"Escrever um ToUnicode CMap"," para que o leitor de PDF consiga mapear glifos de volta ao texto quando o usuário copiar ou buscar.",[887,8460,8461,8464],{},[23,8462,8463],{},"Fazer o subconjunto"," da fonte para que o PDF não carregue todos os 20.000 glifos da Noto Sans JP.",[887,8466,8467,8470,8471,855,8474,855,8477,8480],{},[23,8468,8469],{},"Embutir o resultado"," com as tabelas ",[43,8472,8473],{},"name",[43,8475,8476],{},"OS/2",[43,8478,8479],{},"head"," e as referências de codificação corretamente costuradas.",[19,8482,8483,8484,1155,8486,8488],{},"Se algum passo falta ou está errado, o leitor não acha glifo para o code point e pinta uma caixa de tofu. Os linhagens arquivadas ",[43,8485,1352],{},[43,8487,1426],{}," encaixaram tudo isso num modelo interno de fonte de byte único — o FPDF original de 2002 só conhecia Latin-1. É por isso que a configuração é frágil, a saída frequentemente embute a fonte completa em vez do subconjunto, e os modos de falha variam por SO e leitor de PDF.",[19,8490,8491,8492,8494],{},"gpdf trata CJK como caso de primeira classe. O subsetter TTF está no pacote core. O ToUnicode CMap é escrito automaticamente. Não há dança de ",[43,8493,2539],{}," porque não há legado de fonte de byte único para contornar.",[14,8496,8498],{"id":8497},"as-quatro-opções-reais-em-2026","As quatro opções reais em 2026",[19,8500,8501],{},"Antes de escrever código: o campo honesto. \"Capaz de japonês\" significa \"renderiza qualquer texto japonês sem crashes ou tofu, dado um TTF correto\".",[742,8503,8504,8524],{},[745,8505,8506],{},[748,8507,8508,8511,8513,8516,8519,8522],{},[751,8509,8510],{},"Opção",[751,8512,5112],{},[751,8514,8515],{},"Deps",[751,8517,8518],{},"Caminho CJK",[751,8520,8521],{},"Tamanho para 300 caracteres",[751,8523,6832],{},[759,8525,8526,8549,8571,8593],{},[748,8527,8528,8533,8535,8538,8543,8546],{},[764,8529,8530,8532],{},[43,8531,1426],{}," (arquivada 2025)",[764,8534,6848],{},[764,8536,8537],{},"stdlib",[764,8539,8540,8541],{},"Encaixe ",[43,8542,2539],{},[764,8544,8545],{},"~5 MB (fonte cheia)",[764,8547,8548],{},"Encaixe em core Latin-1. Subconjunto é opt-in e imperfeito.",[748,8550,8551,8555,8557,8559,8565,8568],{},[764,8552,8553],{},[43,8554,1429],{},[764,8556,6848],{},[764,8558,8537],{},[764,8560,8561,8564],{},[43,8562,8563],{},"AddTTFFont"," + manual",[764,8566,8567],{},"~3 MB típico",[764,8569,8570],{},"Baixo nível. Você escreve coordenadas. Subconjunto existe mas você conduz.",[748,8572,8573,8577,8579,8584,8587,8590],{},[764,8574,8575,6949],{},[43,8576,6948],{},[764,8578,6954],{},[764,8580,8581],{},[23,8582,8583],{},"binário Chromium",[764,8585,8586],{},"Nativo via navegador",[764,8588,8589],{},"variável",[764,8591,8592],{},"HTML/CSS. Precisa de fontes instaladas no container. Imagem 500 MB+.",[748,8594,8595,8599,8601,8606,8609,8614],{},[764,8596,8597],{},[43,8598,33],{},[764,8600,6848],{},[764,8602,8603],{},[23,8604,8605],{},"só stdlib",[764,8607,8608],{},"Nativo, subconjunto automático",[764,8610,8611],{},[23,8612,8613],{},"~30 KB",[764,8615,8616],{},"Go puro. API builder. ToUnicode CMap escrito por você.",[19,8618,8619],{},"Duas coisas que vale sublinhar:",[19,8621,8622,8625],{},[23,8623,8624],{},"A diferença de 160× entre \"fonte cheia embutida\" e \"subconjunto automático\" não é arredondamento."," Uma fatura de e-commerce para o mercado japonês com dez linhas precisa de talvez 120 glifos japoneses únicos. Embutir a Noto Sans JP completa (5,1 MB) em cada fatura significa que sua conta de storage carrega os mesmos 5 MB de dados de glifos 10 milhões de vezes ao fim do ano. O subconjunto carrega só o que você usou.",[19,8627,8628,8631],{},[23,8629,8630],{},"\"chromedp funciona\" é verdade e também é a resposta mais cara."," Se seu time já roda uma frota de Chrome headless para screenshots, pegar carona dela para PDFs está bem. Se não, subir uma só para imprimir 日本語 é muita infraestrutura para um problema de 40 linhas de Go.",[14,8633,8635],{"id":8634},"o-caminho-mais-curto-que-funciona","O caminho mais curto que funciona",[19,8637,8638,8639,8641,8642,205],{},"Comece com isto. É completo — copie, salve como ",[43,8640,715],{},", ponha dois TTFs ao lado, ",[43,8643,719],{},[62,8645,8647],{"className":64,"code":8646,"language":66,"meta":67,"style":67},"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    regular, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    bold, err := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", regular),\n        gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"請求書\", template.FontFamily(\"NotoSansJP-Bold\"), template.FontSize(22))\n            c.Text(\"2026 年 4 月 16 日\")\n        })\n    })\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(7, func(c *template.ColBuilder) {\n            c.Text(\"株式会社 ABC 御中\", template.FontSize(13))\n            c.Text(\"〒 100-0001 東京都千代田区千代田 1-1\")\n        })\n        r.Col(5, func(c *template.ColBuilder) {\n            c.Text(\"合計 ¥ 128,000\", template.FontFamily(\"NotoSansJP-Bold\"), template.AlignRight())\n            c.Text(\"支払期限: 2026-05-31\", template.AlignRight())\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice-ja.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[43,8648,8649,8655,8659,8665,8673,8681,8685,8693,8701,8709,8713,8717,8727,8754,8766,8780,8784,8812,8824,8838,8842,8846,8860,8878,8908,8931,8954,8976,8980,8984,8998,9022,9052,9099,9118,9122,9126,9150,9181,9213,9232,9236,9267,9310,9337,9341,9345,9349,9367,9379,9393,9397,9438,9452,9456],{"__ignoreMap":67},[71,8650,8651,8653],{"class":73,"line":74},[71,8652,78],{"class":77},[71,8654,82],{"class":81},[71,8656,8657],{"class":73,"line":85},[71,8658,89],{"emptyLinePlaceholder":88},[71,8660,8661,8663],{"class":73,"line":92},[71,8662,96],{"class":95},[71,8664,99],{"class":77},[71,8666,8667,8669,8671],{"class":73,"line":102},[71,8668,105],{"class":77},[71,8670,108],{"class":81},[71,8672,111],{"class":77},[71,8674,8675,8677,8679],{"class":73,"line":114},[71,8676,105],{"class":77},[71,8678,119],{"class":81},[71,8680,111],{"class":77},[71,8682,8683],{"class":73,"line":124},[71,8684,89],{"emptyLinePlaceholder":88},[71,8686,8687,8689,8691],{"class":73,"line":129},[71,8688,105],{"class":77},[71,8690,134],{"class":81},[71,8692,111],{"class":77},[71,8694,8695,8697,8699],{"class":73,"line":139},[71,8696,105],{"class":77},[71,8698,144],{"class":81},[71,8700,111],{"class":77},[71,8702,8703,8705,8707],{"class":73,"line":149},[71,8704,105],{"class":77},[71,8706,154],{"class":81},[71,8708,111],{"class":77},[71,8710,8711],{"class":73,"line":159},[71,8712,162],{"class":77},[71,8714,8715],{"class":73,"line":165},[71,8716,89],{"emptyLinePlaceholder":88},[71,8718,8719,8721,8723,8725],{"class":73,"line":170},[71,8720,173],{"class":77},[71,8722,177],{"class":176},[71,8724,180],{"class":77},[71,8726,183],{"class":77},[71,8728,8729,8732,8734,8736,8738,8740,8742,8744,8746,8748,8750,8752],{"class":73,"line":186},[71,8730,8731],{"class":189},"    regular",[71,8733,193],{"class":77},[71,8735,196],{"class":189},[71,8737,199],{"class":77},[71,8739,202],{"class":189},[71,8741,205],{"class":77},[71,8743,208],{"class":176},[71,8745,211],{"class":77},[71,8747,214],{"class":77},[71,8749,2989],{"class":217},[71,8751,214],{"class":77},[71,8753,162],{"class":77},[71,8755,8756,8758,8760,8762,8764],{"class":73,"line":224},[71,8757,227],{"class":95},[71,8759,196],{"class":189},[71,8761,232],{"class":77},[71,8763,235],{"class":77},[71,8765,183],{"class":77},[71,8767,8768,8770,8772,8774,8776,8778],{"class":73,"line":240},[71,8769,243],{"class":189},[71,8771,205],{"class":77},[71,8773,248],{"class":176},[71,8775,211],{"class":77},[71,8777,253],{"class":189},[71,8779,162],{"class":77},[71,8781,8782],{"class":73,"line":258},[71,8783,261],{"class":77},[71,8785,8786,8789,8791,8793,8795,8797,8799,8801,8803,8805,8808,8810],{"class":73,"line":264},[71,8787,8788],{"class":189},"    bold",[71,8790,193],{"class":77},[71,8792,196],{"class":189},[71,8794,199],{"class":77},[71,8796,202],{"class":189},[71,8798,205],{"class":77},[71,8800,208],{"class":176},[71,8802,211],{"class":77},[71,8804,214],{"class":77},[71,8806,8807],{"class":217},"NotoSansJP-Bold.ttf",[71,8809,214],{"class":77},[71,8811,162],{"class":77},[71,8813,8814,8816,8818,8820,8822],{"class":73,"line":269},[71,8815,227],{"class":95},[71,8817,196],{"class":189},[71,8819,232],{"class":77},[71,8821,235],{"class":77},[71,8823,183],{"class":77},[71,8825,8826,8828,8830,8832,8834,8836],{"class":73,"line":288},[71,8827,243],{"class":189},[71,8829,205],{"class":77},[71,8831,248],{"class":176},[71,8833,211],{"class":77},[71,8835,253],{"class":189},[71,8837,162],{"class":77},[71,8839,8840],{"class":73,"line":311},[71,8841,261],{"class":77},[71,8843,8844],{"class":73,"line":349},[71,8845,89],{"emptyLinePlaceholder":88},[71,8847,8848,8850,8852,8854,8856,8858],{"class":73,"line":375},[71,8849,272],{"class":189},[71,8851,199],{"class":77},[71,8853,277],{"class":189},[71,8855,205],{"class":77},[71,8857,282],{"class":176},[71,8859,285],{"class":77},[71,8861,8862,8864,8866,8868,8870,8872,8874,8876],{"class":73,"line":400},[71,8863,291],{"class":189},[71,8865,205],{"class":77},[71,8867,296],{"class":176},[71,8869,211],{"class":77},[71,8871,323],{"class":189},[71,8873,205],{"class":77},[71,8875,305],{"class":189},[71,8877,308],{"class":77},[71,8879,8880,8882,8884,8886,8888,8890,8892,8894,8896,8898,8900,8902,8904,8906],{"class":73,"line":406},[71,8881,291],{"class":189},[71,8883,205],{"class":77},[71,8885,318],{"class":176},[71,8887,211],{"class":77},[71,8889,323],{"class":189},[71,8891,205],{"class":77},[71,8893,328],{"class":176},[71,8895,211],{"class":77},[71,8897,323],{"class":189},[71,8899,205],{"class":77},[71,8901,337],{"class":176},[71,8903,211],{"class":77},[71,8905,343],{"class":342},[71,8907,346],{"class":77},[71,8909,8910,8912,8914,8916,8918,8920,8922,8924,8926,8929],{"class":73,"line":411},[71,8911,291],{"class":189},[71,8913,205],{"class":77},[71,8915,356],{"class":176},[71,8917,211],{"class":77},[71,8919,214],{"class":77},[71,8921,3227],{"class":217},[71,8923,214],{"class":77},[71,8925,193],{"class":77},[71,8927,8928],{"class":189}," regular",[71,8930,308],{"class":77},[71,8932,8933,8935,8937,8939,8941,8943,8946,8948,8950,8952],{"class":73,"line":430},[71,8934,291],{"class":189},[71,8936,205],{"class":77},[71,8938,356],{"class":176},[71,8940,211],{"class":77},[71,8942,214],{"class":77},[71,8944,8945],{"class":217},"NotoSansJP-Bold",[71,8947,214],{"class":77},[71,8949,193],{"class":77},[71,8951,1060],{"class":189},[71,8953,308],{"class":77},[71,8955,8956,8958,8960,8962,8964,8966,8968,8970,8972,8974],{"class":73,"line":464},[71,8957,291],{"class":189},[71,8959,205],{"class":77},[71,8961,382],{"class":176},[71,8963,211],{"class":77},[71,8965,214],{"class":77},[71,8967,3227],{"class":217},[71,8969,214],{"class":77},[71,8971,193],{"class":77},[71,8973,395],{"class":342},[71,8975,308],{"class":77},[71,8977,8978],{"class":73,"line":501},[71,8979,403],{"class":77},[71,8981,8982],{"class":73,"line":549},[71,8983,89],{"emptyLinePlaceholder":88},[71,8985,8986,8988,8990,8992,8994,8996],{"class":73,"line":569},[71,8987,414],{"class":189},[71,8989,199],{"class":77},[71,8991,419],{"class":189},[71,8993,205],{"class":77},[71,8995,424],{"class":176},[71,8997,427],{"class":77},[71,8999,9000,9002,9004,9006,9008,9010,9012,9014,9016,9018,9020],{"class":73,"line":575},[71,9001,433],{"class":189},[71,9003,205],{"class":77},[71,9005,438],{"class":176},[71,9007,441],{"class":77},[71,9009,445],{"class":444},[71,9011,448],{"class":77},[71,9013,451],{"class":81},[71,9015,205],{"class":77},[71,9017,456],{"class":81},[71,9019,459],{"class":77},[71,9021,183],{"class":77},[71,9023,9024,9026,9028,9030,9032,9034,9036,9038,9040,9042,9044,9046,9048,9050],{"class":73,"line":581},[71,9025,467],{"class":189},[71,9027,205],{"class":77},[71,9029,472],{"class":176},[71,9031,211],{"class":77},[71,9033,477],{"class":342},[71,9035,193],{"class":77},[71,9037,482],{"class":77},[71,9039,485],{"class":444},[71,9041,448],{"class":77},[71,9043,451],{"class":81},[71,9045,205],{"class":77},[71,9047,494],{"class":81},[71,9049,459],{"class":77},[71,9051,183],{"class":77},[71,9053,9054,9056,9058,9060,9062,9064,9066,9068,9070,9072,9074,9076,9078,9080,9082,9084,9086,9088,9090,9092,9094,9097],{"class":73,"line":586},[71,9055,504],{"class":189},[71,9057,205],{"class":77},[71,9059,509],{"class":176},[71,9061,211],{"class":77},[71,9063,214],{"class":77},[71,9065,4409],{"class":217},[71,9067,214],{"class":77},[71,9069,193],{"class":77},[71,9071,523],{"class":189},[71,9073,205],{"class":77},[71,9075,4975],{"class":176},[71,9077,211],{"class":77},[71,9079,214],{"class":77},[71,9081,8945],{"class":217},[71,9083,214],{"class":77},[71,9085,536],{"class":77},[71,9087,523],{"class":189},[71,9089,205],{"class":77},[71,9091,528],{"class":176},[71,9093,211],{"class":77},[71,9095,9096],{"class":342},"22",[71,9098,4998],{"class":77},[71,9100,9101,9103,9105,9107,9109,9111,9114,9116],{"class":73,"line":607},[71,9102,504],{"class":189},[71,9104,205],{"class":77},[71,9106,509],{"class":176},[71,9108,211],{"class":77},[71,9110,214],{"class":77},[71,9112,9113],{"class":217},"2026 年 4 月 16 日",[71,9115,214],{"class":77},[71,9117,162],{"class":77},[71,9119,9120],{"class":73,"line":620},[71,9121,572],{"class":77},[71,9123,9124],{"class":73,"line":635},[71,9125,578],{"class":77},[71,9127,9128,9130,9132,9134,9136,9138,9140,9142,9144,9146,9148],{"class":73,"line":640},[71,9129,433],{"class":189},[71,9131,205],{"class":77},[71,9133,438],{"class":176},[71,9135,441],{"class":77},[71,9137,445],{"class":444},[71,9139,448],{"class":77},[71,9141,451],{"class":81},[71,9143,205],{"class":77},[71,9145,456],{"class":81},[71,9147,459],{"class":77},[71,9149,183],{"class":77},[71,9151,9152,9154,9156,9158,9160,9163,9165,9167,9169,9171,9173,9175,9177,9179],{"class":73,"line":686},[71,9153,467],{"class":189},[71,9155,205],{"class":77},[71,9157,472],{"class":176},[71,9159,211],{"class":77},[71,9161,9162],{"class":342},"7",[71,9164,193],{"class":77},[71,9166,482],{"class":77},[71,9168,485],{"class":444},[71,9170,448],{"class":77},[71,9172,451],{"class":81},[71,9174,205],{"class":77},[71,9176,494],{"class":81},[71,9178,459],{"class":77},[71,9180,183],{"class":77},[71,9182,9183,9185,9187,9189,9191,9193,9196,9198,9200,9202,9204,9206,9208,9211],{"class":73,"line":701},[71,9184,504],{"class":189},[71,9186,205],{"class":77},[71,9188,509],{"class":176},[71,9190,211],{"class":77},[71,9192,214],{"class":77},[71,9194,9195],{"class":217},"株式会社 ABC 御中",[71,9197,214],{"class":77},[71,9199,193],{"class":77},[71,9201,523],{"class":189},[71,9203,205],{"class":77},[71,9205,528],{"class":176},[71,9207,211],{"class":77},[71,9209,9210],{"class":342},"13",[71,9212,4998],{"class":77},[71,9214,9215,9217,9219,9221,9223,9225,9228,9230],{"class":73,"line":706},[71,9216,504],{"class":189},[71,9218,205],{"class":77},[71,9220,509],{"class":176},[71,9222,211],{"class":77},[71,9224,214],{"class":77},[71,9226,9227],{"class":217},"〒 100-0001 東京都千代田区千代田 1-1",[71,9229,214],{"class":77},[71,9231,162],{"class":77},[71,9233,9234],{"class":73,"line":4593},[71,9235,572],{"class":77},[71,9237,9238,9240,9242,9244,9246,9249,9251,9253,9255,9257,9259,9261,9263,9265],{"class":73,"line":5828},[71,9239,467],{"class":189},[71,9241,205],{"class":77},[71,9243,472],{"class":176},[71,9245,211],{"class":77},[71,9247,9248],{"class":342},"5",[71,9250,193],{"class":77},[71,9252,482],{"class":77},[71,9254,485],{"class":444},[71,9256,448],{"class":77},[71,9258,451],{"class":81},[71,9260,205],{"class":77},[71,9262,494],{"class":81},[71,9264,459],{"class":77},[71,9266,183],{"class":77},[71,9268,9269,9271,9273,9275,9277,9279,9282,9284,9286,9288,9290,9292,9294,9296,9298,9300,9302,9304,9306,9308],{"class":73,"line":5848},[71,9270,504],{"class":189},[71,9272,205],{"class":77},[71,9274,509],{"class":176},[71,9276,211],{"class":77},[71,9278,214],{"class":77},[71,9280,9281],{"class":217},"合計 ¥ 128,000",[71,9283,214],{"class":77},[71,9285,193],{"class":77},[71,9287,523],{"class":189},[71,9289,205],{"class":77},[71,9291,4975],{"class":176},[71,9293,211],{"class":77},[71,9295,214],{"class":77},[71,9297,8945],{"class":217},[71,9299,214],{"class":77},[71,9301,536],{"class":77},[71,9303,523],{"class":189},[71,9305,205],{"class":77},[71,9307,7255],{"class":176},[71,9309,546],{"class":77},[71,9311,9312,9314,9316,9318,9320,9322,9325,9327,9329,9331,9333,9335],{"class":73,"line":5853},[71,9313,504],{"class":189},[71,9315,205],{"class":77},[71,9317,509],{"class":176},[71,9319,211],{"class":77},[71,9321,214],{"class":77},[71,9323,9324],{"class":217},"支払期限: 2026-05-31",[71,9326,214],{"class":77},[71,9328,193],{"class":77},[71,9330,523],{"class":189},[71,9332,205],{"class":77},[71,9334,7255],{"class":176},[71,9336,546],{"class":77},[71,9338,9339],{"class":73,"line":5884},[71,9340,572],{"class":77},[71,9342,9343],{"class":73,"line":5904},[71,9344,578],{"class":77},[71,9346,9347],{"class":73,"line":5909},[71,9348,89],{"emptyLinePlaceholder":88},[71,9350,9351,9353,9355,9357,9359,9361,9363,9365],{"class":73,"line":5940},[71,9352,589],{"class":189},[71,9354,193],{"class":77},[71,9356,196],{"class":189},[71,9358,199],{"class":77},[71,9360,419],{"class":189},[71,9362,205],{"class":77},[71,9364,602],{"class":176},[71,9366,427],{"class":77},[71,9368,9369,9371,9373,9375,9377],{"class":73,"line":5959},[71,9370,227],{"class":95},[71,9372,196],{"class":189},[71,9374,232],{"class":77},[71,9376,235],{"class":77},[71,9378,183],{"class":77},[71,9380,9381,9383,9385,9387,9389,9391],{"class":73,"line":5964},[71,9382,243],{"class":189},[71,9384,205],{"class":77},[71,9386,248],{"class":176},[71,9388,211],{"class":77},[71,9390,253],{"class":189},[71,9392,162],{"class":77},[71,9394,9395],{"class":73,"line":5969},[71,9396,261],{"class":77},[71,9398,9399,9401,9403,9405,9407,9409,9411,9413,9415,9418,9420,9422,9424,9426,9428,9430,9432,9434,9436],{"class":73,"line":5974},[71,9400,227],{"class":95},[71,9402,196],{"class":189},[71,9404,199],{"class":77},[71,9406,202],{"class":189},[71,9408,205],{"class":77},[71,9410,653],{"class":176},[71,9412,211],{"class":77},[71,9414,214],{"class":77},[71,9416,9417],{"class":217},"invoice-ja.pdf",[71,9419,214],{"class":77},[71,9421,193],{"class":77},[71,9423,667],{"class":189},[71,9425,193],{"class":77},[71,9427,672],{"class":342},[71,9429,675],{"class":77},[71,9431,196],{"class":189},[71,9433,232],{"class":77},[71,9435,235],{"class":77},[71,9437,183],{"class":77},[71,9439,9440,9442,9444,9446,9448,9450],{"class":73,"line":5980},[71,9441,243],{"class":189},[71,9443,205],{"class":77},[71,9445,248],{"class":176},[71,9447,211],{"class":77},[71,9449,253],{"class":189},[71,9451,162],{"class":77},[71,9453,9454],{"class":73,"line":6005},[71,9455,261],{"class":77},[71,9457,9458],{"class":73,"line":6037},[71,9459,709],{"class":77},[19,9461,9462],{},"Coisas que vale notar sem narrar cada uma:",[1114,9464,9465,9481,9490,9505],{},[887,9466,9467,898,9475,9477,9478,9480],{},[23,9468,9469,9470,9472,9473,205],{},"Sem ",[43,9471,2539],{},", sem flag UTF-8, sem argumento de caminho de fonte para ",[43,9474,509],{},[43,9476,8392],{}," registra uma família; ",[43,9479,2967],{}," só escreve Unicode. O encanamento fica interno.",[887,9482,9483,9486,9487,9489],{},[23,9484,9485],{},"Negrito é uma família à parte, não uma flag."," Isso combina com a forma como os TTFs são distribuídos (Noto Sans JP Regular e Noto Sans JP Bold são arquivos distintos com tabelas ",[43,9488,8473],{}," diferentes). As variantes Gothic/Mincho, ou Source Han Sans JP Normal/Heavy, seguem o mesmo padrão.",[887,9491,9492,898,9495,1155,9498,9501,9502,205],{},[23,9493,9494],{},"Layout é grid, não cursor.",[43,9496,9497],{},"r.Col(7, ...)",[43,9499,9500],{},"r.Col(5, ...)"," somam 12. As larguras são declarativas; você não calcula coordenadas x. Mais em ",[28,9503,9504],{"href":6620},"Como funciona a grid de 12 colunas no gpdf",[887,9506,9507,9513],{},[23,9508,9509,9512],{},[43,9510,9511],{},"AlignRight()"," é agnóstico de locale."," O japonês \"¥ 128,000\" alinha à direita do mesmo jeito que \"R$ 1.280,00\" alinharia. O conteúdo do texto não muda o código de layout.",[19,9515,9516,9517,9519,9520,9522],{},"Abra o ",[43,9518,9417],{}," resultante em qualquer leitor. Selecione \"株式会社 ABC 御中\". Cole num editor de texto. Você obtém ",[43,9521,9195],{},", não uma bagunça. Isso é o ToUnicode CMap trabalhando; gpdf escreve um por padrão.",[14,9524,9526],{"id":9525},"subconjunto-de-fontes-a-bomba-de-tamanho-escondida","Subconjunto de fontes: a bomba de tamanho escondida",[19,9528,9529,9530,205],{},"Aqui vai a propriedade mais importante de CJK-em-PDF que os tutoriais pulam: ",[23,9531,9532],{},"embutir por subconjunto",[19,9534,9535],{},"Uma fonte TTF é uma coleção de contornos de glifo mais tabelas de metadados. Noto Sans JP Regular traz cerca de 17.500 glifos e pesa 5,1 MB. Uma fatura típica usa entre 60 e 200 caracteres japoneses únicos. Embutir a fonte completa em cada documento é desperdício de uma ordem de magnitude.",[19,9537,9538],{},"Embutir por subconjunto mantém só os glifos que você usou. gpdf faz isso automaticamente. Você pode ver isso rodando o exemplo acima:",[62,9540,9542],{"className":1248,"code":9541,"language":1250,"meta":67,"style":67},"$ ls -l invoice-ja.pdf\n-rw-r--r--  1 dev  staff  34892 Apr 16 10:12 invoice-ja.pdf\n",[43,9543,9544,9558],{"__ignoreMap":67},[71,9545,9546,9549,9552,9555],{"class":73,"line":74},[71,9547,9548],{"class":81},"$",[71,9550,9551],{"class":217}," ls",[71,9553,9554],{"class":217}," -l",[71,9556,9557],{"class":217}," invoice-ja.pdf\n",[71,9559,9560,9563,9566,9569,9572,9575,9578,9580,9583],{"class":73,"line":85},[71,9561,9562],{"class":81},"-rw-r--r--",[71,9564,9565],{"class":342},"  1",[71,9567,9568],{"class":217}," dev",[71,9570,9571],{"class":217},"  staff",[71,9573,9574],{"class":342},"  34892",[71,9576,9577],{"class":217}," Apr",[71,9579,7486],{"class":342},[71,9581,9582],{"class":217}," 10:12",[71,9584,9557],{"class":217},[19,9586,9587,9588,1155,9590,9593,9594,9597],{},"34 KB. Para comparar, o mesmo documento gerado com ",[43,9589,1426],{},[43,9591,9592],{},"AddUTF8Font(\"NotoSansJP\", \"NotoSansJP-Regular.ttf\", true)"," — onde o terceiro argumento é a flag UTF-8 — dá ",[23,9595,9596],{},"4,9 MB",". Mesma entrada, mesmo texto de saída, arquivo 143× maior. O motivo é que o caminho de código do fpdf embute a tabela inteira da fonte em vez de fazer subconjunto no momento do emit.",[19,9599,9600],{},"Algumas consequências que vale nomear:",[1114,9602,9603,9614,9620],{},[887,9604,9605,9606,9609,9610,9613],{},"A ",[23,9607,9608],{},"10 faturas por segundo"," (escala SaaS normal), a diferença de subconjunto é a diferença entre ",[23,9611,9612],{},"0,3 MB/s e 43 MB/s"," de bytes PDF de saída. Seu load balancer tem opinião sobre isso.",[887,9615,9616,9619],{},[23,9617,9618],{},"Contas de cold storage"," escalam linearmente com o tamanho do PDF. Cinco milhões de faturas arquivadas a 5 MB cada dão 25 TB. A 30 KB cada, 150 GB. Preço de object storage faz disso uma linha mensal de quatro dígitos contra dois.",[887,9621,9622,9625],{},[23,9623,9624],{},"Entrega por email"," tem limites de anexo de 10–25 MB dependendo do provedor. Uma fatura japonesa de 5 MB mais qualquer outro anexo mais a codificação MIME começa a bater nesse teto.",[19,9627,9628,9629,855,9632,855,9635,1155,9638,9641],{},"gpdf faz o subconjunto em tempo de render. Não há flag para ligar. Você pode ver quais glifos foram parar na saída rodando a ferramenta de verificação do gpdf localmente, mas a versão curta é: se você usou ",[43,9630,9631],{},"株",[43,9633,9634],{},"式",[43,9636,9637],{},"会",[43,9639,9640],{},"社",", esses quatro glifos estão na saída e os outros 17.496 não.",[14,9643,9645],{"id":9644},"escritas-mistas-kanji-kana-ascii-na-mesma-linha","Escritas mistas: kanji + kana + ASCII na mesma linha",[19,9647,9648],{},"Texto japonês raramente é só japonês. Uma linha real num documento japonês parece com isto:",[62,9650,9653],{"className":9651,"code":9652,"language":928},[926],"API の P95 レイテンシは 50 ms 未満です。\n",[43,9654,9652],{"__ignoreMap":67},[19,9656,9657],{},"São cinco escritas: romaji (ASCII Latin), katakana, hiragana, kanji (Han) e numerais. Uma implementação ingênua escolhe a fonte errada para as partes ASCII e você fica com um \"API\" monoespaçado ao lado de japonês proporcional, o que fica horrível.",[19,9659,9660,9661,9664,9665,1155,9668,9671],{},"O comportamento padrão do gpdf é ",[23,9662,9663],{},"renderizar cada code point com a família registrada",". Se Noto Sans JP é seu padrão, ",[43,9666,9667],{},"API",[43,9669,9670],{},"50 ms"," são desenhados com os glifos Latin da Noto Sans JP, que a Noto fornece (a maioria das superfamílias japonesas fornece). O resultado parece uma única tipografia, porque é.",[19,9673,9674,9675,9677],{},"Se você quer misturar famílias deliberadamente — por exemplo, uma sans condensada para ASCII e Noto Sans JP para japonês — registre as duas e sobrescreva por chamada de ",[43,9676,2967],{},":",[62,9679,9681],{"className":64,"code":9680,"language":66,"meta":67,"style":67},"c.Text(\"API の P95 レイテンシは 50 ms 未満です。\",\n    template.FontFamily(\"NotoSansJP\"))\nc.Text(\"API latency (P95) is under 50 ms.\",\n    template.FontFamily(\"InterVariable\"))\n",[43,9682,9683,9702,9721,9740],{"__ignoreMap":67},[71,9684,9685,9687,9689,9691,9693,9695,9698,9700],{"class":73,"line":74},[71,9686,485],{"class":189},[71,9688,205],{"class":77},[71,9690,509],{"class":176},[71,9692,211],{"class":77},[71,9694,214],{"class":77},[71,9696,9697],{"class":217},"API の P95 レイテンシは 50 ms 未満です。",[71,9699,214],{"class":77},[71,9701,2046],{"class":77},[71,9703,9704,9707,9709,9711,9713,9715,9717,9719],{"class":73,"line":85},[71,9705,9706],{"class":189},"    template",[71,9708,205],{"class":77},[71,9710,4975],{"class":176},[71,9712,211],{"class":77},[71,9714,214],{"class":77},[71,9716,3227],{"class":217},[71,9718,214],{"class":77},[71,9720,4998],{"class":77},[71,9722,9723,9725,9727,9729,9731,9733,9736,9738],{"class":73,"line":92},[71,9724,485],{"class":189},[71,9726,205],{"class":77},[71,9728,509],{"class":176},[71,9730,211],{"class":77},[71,9732,214],{"class":77},[71,9734,9735],{"class":217},"API latency (P95) is under 50 ms.",[71,9737,214],{"class":77},[71,9739,2046],{"class":77},[71,9741,9742,9744,9746,9748,9750,9752,9755,9757],{"class":73,"line":102},[71,9743,9706],{"class":189},[71,9745,205],{"class":77},[71,9747,4975],{"class":176},[71,9749,211],{"class":77},[71,9751,214],{"class":77},[71,9753,9754],{"class":217},"InterVariable",[71,9756,214],{"class":77},[71,9758,4998],{"class":77},[19,9760,9761,9762,9764,9765,9768],{},"Duas ",[43,9763,2967],{},", duas famílias, zero lógica de detecção de escrita no seu código. Se precisar de mistura intra-linha (Inter para ASCII + Noto para japonês na ",[2939,9766,9767],{},"mesma"," frase), vem no gpdf v1.2; hoje o workaround é quebrar nas fronteiras de escrita manualmente e dispor numa linha horizontal de colunas.",[14,9770,9772],{"id":9771},"o-que-ainda-dói","O que ainda dói",[19,9774,9775],{},"A história do PDF japonês em Go está 95% resolvida. Aqui estão os 5%.",[19,9777,9778,9781,9782,205],{},[23,9779,9780],{},"Texto vertical (縦書き) ainda não está lá."," gpdf renderiza só texto horizontal em v1.x. A diagramação japonesa tradicional — colunas da direita para a esquerda, caracteres de cima para baixo, com a rotação apropriada de glifos e reposicionamento de pontuação — é uma mudança profunda do motor de layout, não um ajuste de renderização. A issue aberta tem uma proposta de design; vai aterrissar quando aterrissar. Por enquanto, se você precisa de 縦書き para livros ou correspondência formal, gere com uma ferramenta que suporta (Word, InDesign ou um pipeline pandoc + LuaLaTeX) e embuta o PDF resultante com ",[43,9783,9784],{},"gpdf.Merge",[19,9786,9787,9790,9791,9794],{},[23,9788,9789],{},"Anotações ruby (振り仮名) são só workaround."," Não há primitiva ",[43,9792,9793],{},"c.Ruby(\"漢字\", \"かんじ\")",". Se você precisa de ruby para conteúdo infantil ou livros de língua, o workaround é uma coluna de duas linhas: texto kana pequeno em cima, kanji normal embaixo, alinhados. Funciona, mas é manual, e o kerning fino nas fronteiras de furigana exige cuidado.",[19,9796,9797,9800,9801,855,9804,855,9807,9810,9811,9813,9814,9818],{},[23,9798,9799],{},"Fallbacks complexos entre múltiplas fontes CJK."," Se um usuário submete texto que mistura kanji japonês com caracteres só chineses (as formas diferem — ",[43,9802,9803],{},"直",[43,9805,9806],{},"骨",[43,9808,9809],{},"角"," renderizam sutilmente diferentes em CN vs JP), você precisa partir manualmente e usar duas famílias. O gpdf não faz auto-fallback entre famílias dentro de uma única chamada de ",[43,9812,2967],{},". Na prática muito poucos documentos precisam disso; se o seu precisa, veja ",[28,9815,9817],{"href":9816},"/pt/blog/","PDFs multilíngues: misturando JP/CN/KR/EN"," (B-070 pendente).",[19,9820,9821,9824,9825,9828,9829,9832],{},[23,9822,9823],{},"Conformidade PDF/A-2b estrita com japonês."," gpdf produz saída PDF/A via ",[43,9826,9827],{},"gpdf.WithPDFA",", mas os requisitos estritos sobre metadados de glifos embutidos, o span ",[43,9830,9831],{},"ActualText"," em cada run CJK, e árvores de estrutura etiquetadas ainda estão sendo afinados para o caso CJK. Se você está exportando para arquivamento de longo prazo para atender à NFe, ao DANFE ou ao PDF/A-3b brasileiro (ou à 電子帳簿保存法 japonesa), valide com uma ferramenta de terceiros (veraPDF é grátis) antes de committar.",[19,9834,9835],{},"Nenhum desses é bloqueio para os casos comuns: faturas, relatórios, extratos, recibos, certificados. Vale nomear porque alguém lendo isto está prestes a encontrar um em produção, e \"está no roadmap\" é menos útil que \"aqui está o workaround\".",[14,9837,9839],{"id":9838},"uma-palavra-sobre-conformidade","Uma palavra sobre conformidade",[19,9841,9842,9843,9846],{},"Um pedaço de contexto de ecossistema que costuma ficar não dito: ",[23,9844,9845],{},"gerar PDFs japoneses em 2026 não é só problema tipográfico."," Duas mudanças regulatórias o empurram para a conversa de conformidade.",[19,9848,9849,9850,9853],{},"O regime ",[23,9851,9852],{},"適格請求書 (nota fiscal qualificada)"," sob a reforma do imposto de consumo exige que as notas incluam campos específicos (número de negócio registrado, alíquota aplicável, detalhamento) e que sejam retidas de forma à prova de adulteração. PDFs são o formato padrão para isso, e \"à prova de adulteração\" mapeia para assinaturas digitais PDF — PAdES-B-LT no caso estrito.",[19,9855,9605,9856,9859],{},[23,9857,9858],{},"電子帳簿保存法 (lei de armazenamento eletrônico de livros)",", revisada em 2024, estendeu mandatos de retenção para incluir notas armazenadas em forma eletrônica. PDFs arquivados precisam atender a certos requisitos de integridade. PDF/A-2b ou PDF/A-3b são o formato-alvo de fato. No Brasil, a NFe requer assinatura digital ICP-Brasil e PDF/A-3 para a DANFE arquivada — a mesma classe de requisito.",[19,9861,9862,9863,9866,9867,9870,9871,9873],{},"Ambos os requisitos se apoiam em ",[23,9864,9865],{},"recursos nativos do PDF"," — assinaturas, validação de longo prazo, metadados PDF/A embutidos. HTML-para-PDF via navegador headless não atende a nenhum limpamente: a saída PDF do Chromium não é compatível com PDF/A e não consegue embutir assinaturas digitais num único passo. Um stack Go nativo (gpdf + ",[43,9868,9869],{},"gpdf/signature"," para PAdES + ",[43,9872,9827],{},") faz a cadeia toda num pipeline sem sair do processo.",[19,9875,9876],{},"Isto é um sinal para posts futuros em vez de um mergulho profundo — assinatura e PDF/A merecem cada um seu próprio artigo hero (são B-067 e B-068 no backlog). Mas se você está escolhendo um stack de PDF japonês hoje e conformidade está no seu radar, escolha um stack que possa fazer assinaturas e PDF/A nativamente. O imposto de migração de \"funciona hoje\" para \"passa na auditoria\" é real.",[14,9878,2787],{"id":2786},[19,9880,9881,9884,9885,3534,9888,9891,9892,9895,9896,9899,9900,205],{},[23,9882,9883],{},"Preciso instalar fontes no servidor ou no container?","\nNão. gpdf lê bytes TTF; não passa pelo cache de fontes do sistema. ",[43,9886,9887],{},"os.ReadFile(\"NotoSansJP-Regular.ttf\")",[43,9889,9890],{},"//go:embed NotoSansJP-Regular.ttf"," funciona igual em macOS, Linux e Windows, dentro de um container distroless, e em AWS Lambda. Sem ",[43,9893,9894],{},"fontconfig",", sem ",[43,9897,9898],{},"fc-cache -fv",". Essa é uma das razões do gpdf funcionar em imagens ",[43,9901,9902],{},"FROM scratch",[19,9904,9905,9908],{},[23,9906,9907],{},"Noto Sans JP vs Source Han Sans JP — faz diferença?","\nSão a mesma família de fonte sob dois nomes. A Adobe publica Source Han Sans JP; o Google reempacota como Noto Sans JP. Cobertura de glifos idêntica. Escolha a distribuição de licença que passar na sua revisão jurídica; ambas são SIL Open Font License. Para documentação neutra de marca usamos Noto Sans JP por padrão porque os nomes de arquivo são mais fáceis de lembrar.",[19,9910,9911,9914],{},[23,9912,9913],{},"E 游ゴシック (Yu Gothic) ou Hiragino?","\nFontes proprietárias que vêm com o SO. Você pode usá-las se seu alvo de deploy tem licença (Windows Server traz Yu Gothic; macOS traz Hiragino), mas vai precisar pegar o arquivo TTF e confirmar os termos de redistribuição para seu build de container. Para deploys abertos, fique com Noto Sans JP ou IPAex Gothic (ambas de redistribuição livre).",[19,9916,9917,9924,9925,9928],{},[23,9918,9919,9920,9923],{},"O PDF renderiza mas ",[43,9921,9922],{},"Ctrl+F"," não acha nada. Por quê?","\nQuase sempre é problema de ToUnicode CMap. gpdf escreve um automaticamente, então se você vê isso com gpdf, abra uma issue com o nome do leitor. Se vê com gofpdf, o conserto é ligar a flag UTF-8 ",[2939,9926,9927],{},"e"," garantir que o leitor suporta fontes CID — versões antigas do Preview.app no macOS têm issues conhecidos. Teste com Adobe Reader ou Chrome como controle.",[19,9930,9931,9934,9935,9938],{},[23,9932,9933],{},"Como adiciono um caractere JIS X 0213 que não está na fonte?","\nNão tem como — não há glifo para desenhar. A resposta prática é \"use uma fonte que cubra JIS X 0213\". Noto Sans JP cobre o BMP completo mais JIS X 0213 Nível 1. Para variantes históricas raras, Hanazono Mincho (花園明朝) é o fallback final. Se um code point não está em nenhuma fonte, gpdf emite o caractere de substituição Unicode (U+FFFD) em vez de um tofu silencioso — então você vê ",[43,9936,9937],{},"�"," na saída e sabe que precisa investigar.",[19,9940,9941,9944,9945,9947],{},[23,9942,9943],{},"Tem custo de performance entre CJK e ASCII?","\nPequeno. O benchmark do gpdf para \"fatura CJK complexa\" é 133 µs por documento num Apple M1, vs 108 µs para uma tabela ASCII 4×10. São ~23% de overhead, quase todo do trabalho maior de lookup de glifo e subconjunto. Para referência, ",[43,9946,1426],{}," no mesmo benchmark CJK é 254 µs, e Maroto v2 é 10,4 ms. Renderização japonesa não é o gargalo no seu serviço.",[14,9949,1242],{"id":1241},[19,9951,9952],{},"gpdf é uma biblioteca Go para gerar PDFs. MIT, zero dependências externas, CJK nativo.",[62,9954,9955],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,9956,9957],{"__ignoreMap":67},[71,9958,9959,9961,9963],{"class":73,"line":74},[71,9960,66],{"class":81},[71,9962,1259],{"class":217},[71,9964,1262],{"class":217},[19,9966,9967,1269,9970],{},[28,9968,1268],{"href":30,"rel":9969},[32],[28,9971,9973],{"href":1272,"rel":9972},[32],"Leia a documentação",[14,9975,8323],{"id":8322},[1114,9977,9978,9984,9990,9996],{},[887,9979,9980,9983],{},[28,9981,9982],{"href":1222},"Como embuto uma fonte japonesa no gpdf?"," — a receita de três linhas sem o contexto",[887,9985,9986,9989],{},[28,9987,9988],{"href":835},"Como uso Noto Sans JP com gpdf?"," — configuração de pesos Regular / Bold / Medium",[887,9991,9992,9995],{},[28,9993,9994],{"href":6620},"Como funciona a grid de 12 colunas no gpdf?"," — o idioma de layout que substitui empurrar o cursor",[887,9997,9998,10001],{},[28,9999,10000],{"href":8370},"go-pdf/fpdf também foi arquivada. O stack Go PDF moderno."," — o panorama mais amplo de 2026",[1276,10003,1278],{},{"title":67,"searchDepth":85,"depth":85,"links":10005},[10006,10007,10008,10009,10010,10011,10012,10013,10014,10015,10016,10017],{"id":37,"depth":85,"text":38},{"id":8406,"depth":85,"text":8407},{"id":8429,"depth":85,"text":8430},{"id":8497,"depth":85,"text":8498},{"id":8634,"depth":85,"text":8635},{"id":9525,"depth":85,"text":9526},{"id":9644,"depth":85,"text":9645},{"id":9771,"depth":85,"text":9772},{"id":9838,"depth":85,"text":9839},{"id":2786,"depth":85,"text":2787},{"id":1241,"depth":85,"text":1242},{"id":8322,"depth":85,"text":8323},"Como gerar PDFs em japonês com Go em 2026 — fontes, subconjuntos TrueType, kanji/kana/ASCII misturados, e por que não precisa de CGO nem Chromium.",{"name":10020,"totalTime":10021,"tools":10022,"steps":10024},"Gerar um PDF em japonês com Go usando subconjunto TrueType nativo","PT20M",[1299,10023],"NotoSansJP-Regular.ttf e NotoSansJP-Bold.ttf (ou qualquer par TTF capaz de CJK)",[10025,10028,10031,10034,10037,10040],{"name":10026,"text":10027},"Instalar gpdf e baixar as fontes","Execute go get github.com/gpdf-dev/gpdf. Baixe Noto Sans JP Regular e Bold do Google Fonts e coloque os TTFs ao lado de main.go. Sem CGO, sem configuração de fontes do sistema.",{"name":10029,"text":10030},"Carregar os bytes TTF na inicialização","Leia os dois arquivos TTF com os.ReadFile para buffers []byte. //go:embed funciona se você quiser compilar as fontes dentro do binário.",{"name":10032,"text":10033},"Registrar as fontes na construção do documento","Passe gpdf.WithFont(\"NotoSansJP\", regular) e gpdf.WithFont(\"NotoSansJP-Bold\", bold) para gpdf.NewDocument. O nome da família é um identificador arbitrário — é o handle que você referenciará depois.",{"name":10035,"text":10036},"Definir a fonte japonesa como padrão","Adicione gpdf.WithDefaultFont(\"NotoSansJP\", 11). Cada c.Text usará a fonte japonesa sem uma opção FontFamily explícita.",{"name":10038,"text":10039},"Construir a árvore do documento com c.Text","Dentro de um bloco page.AutoRow, chame r.Col(span, fn) e c.Text(\"こんにちは、世界。\"). Negrito e tamanho são opções de template, não métodos separados.",{"name":10041,"text":10042},"Gerar e inspecionar a saída","Chame doc.Generate() para obter []byte, escreva em disco com os.WriteFile. Abra o PDF, selecione o texto, cole num editor — o ToUnicode CMap garante que copiar e colar funcione.",{},{"title":8378,"description":10018},"pt/blog/007.japanese-pdf-in-go",[1325,1324,4028],"r4clWad079eCB2emSAwP1ORJir7WBOcVJncNL8Jyz_c",{"id":10049,"title":2869,"author":10050,"body":10051,"date":11662,"description":11663,"draft":1293,"extension":1294,"howTo":1317,"image":1317,"meta":11664,"navigation":88,"path":2868,"seo":11665,"stem":11666,"tags":11667,"updated":1317,"__hash__":11668},"blogPt/pt/blog/002.go-pdf-library-showdown-2026.md",{"name":8,"url":9},{"type":11,"value":10052,"toc":11644},[10053,10055,10061,10081,10084,10090,10094,10097,10128,10131,10134,10138,10318,10321,10325,10337,10340,10366,10372,10376,10470,10478,10482,10489,10495,10501,10507,10514,10518,10528,10605,10608,10612,10666,10669,10674,10678,10716,10720,10723,10764,10767,10771,10774,11492,11509,11513,11516,11554,11556,11562,11572,11578,11584,11590,11592,11595,11607,11616,11618,11641],[14,10054,38],{"id":37},[19,10056,10057,10058,10060],{},"Há cinco anos, pesquisar \"biblioteca Go PDF\" levava quase sempre a ",[23,10059,1352],{},". Hoje, está arquivada. O fork da comunidade também. O terreno real é bem menor do que os resultados de busca sugerem:",[1114,10062,10063,10069,10075],{},[887,10064,10065,10068],{},[23,10066,10067],{},"Ativamente mantidas:"," gpdf (este time), signintech/gopdf, johnfercher/maroto v2 — mas Maroto ainda depende de um gofpdf arquivado.",[887,10070,10071,10074],{},[23,10072,10073],{},"Arquivadas:"," jung-kurt/gofpdf (2021), go-pdf/fpdf (2025).",[887,10076,10077,10080],{},[23,10078,10079],{},"Comercial / AGPL:"," unidoc/unipdf.",[19,10082,10083],{},"Este post submete as bibliotecas mantidas a quatro cargas de trabalho, lista licenças e grafos de dependências, e faz uma recomendação por caso de uso. Rodamos tudo de novo no ano que vem.",[19,10085,10086,10087,10089],{},"Aviso de viés: somos o time do gpdf. O código do benchmark é público (",[43,10088,1394],{},") — clone, rode de novo, conte onde os números divergem.",[14,10091,10093],{"id":10092},"o-que-estamos-realmente-comparando","O que estamos realmente comparando",[19,10095,10096],{},"A frase \"biblioteca Go PDF\" cobre pelo menos três ferramentas diferentes se passando pela mesma categoria:",[884,10098,10099,10109,10120],{},[887,10100,10101,10104,10105,855,10107,205],{},[23,10102,10103],{},"Escritores PDF de baixo nível"," — você empurra bytes e desenha com primitivas. ",[43,10106,1352],{},[43,10108,1429],{},[887,10110,10111,10114,10115,855,10118,205],{},[23,10112,10113],{},"Bibliotecas de layout que envolvem um escritor"," — linhas/colunas declarativas por cima. ",[43,10116,10117],{},"johnfercher/maroto v2",[43,10119,33],{},[887,10121,10122,10125,10126,205],{},[23,10123,10124],{},"Suítes documentais completas"," — parsing, assinatura, PDF/A, OCR, redação. ",[43,10127,6927],{},[19,10129,10130],{},"Perguntar \"qual é a melhor biblioteca Go PDF\" sem dizer de qual categoria você precisa é o motivo de a maioria das threads de recomendação no Reddit saírem dos trilhos. Nas comparações abaixo tentamos manter essa distinção visível.",[19,10132,10133],{},"Fora da lista: qualquer coisa que suba um Chromium headless (go-rod, chromedp). Isso não é biblioteca PDF; é um navegador que imprime. Bom para fidelidade em CSS complexo, ruim para cold start, ruim para memória, ruim para distroless. Se o requisito é \"o designer me entrega HTML+CSS e quer impressão pixel-perfect\", essas ferramentas existem — não competimos com elas neste post.",[14,10135,10137],{"id":10136},"placar","Placar",[742,10139,10140,10166],{},[745,10141,10142],{},[748,10143,10144,10146,10149,10152,10154,10157,10160,10163],{},[751,10145,6818],{},[751,10147,10148],{},"Último release",[751,10150,10151],{},"Arquivada",[751,10153,5112],{},[751,10155,10156],{},"Deps. do núcleo",[751,10158,10159],{},"CJK",[751,10161,10162],{},"Grid de layout",[751,10164,10165],{},"Status 2026",[759,10167,10168,10199,10218,10242,10269,10294],{},[748,10169,10170,10175,10178,10181,10183,10188,10191,10194],{},[764,10171,10172,10174],{},[23,10173,33],{}," (este time)",[764,10176,10177],{},"ativa",[764,10179,10180],{},"—",[764,10182,6848],{},[764,10184,10185],{},[23,10186,10187],{},"0",[764,10189,10190],{},"nativo",[764,10192,10193],{},"12 colunas",[764,10195,10196],{},[23,10197,10198],{},"mantida",[748,10200,10201,10203,10205,10207,10209,10211,10214,10216],{},[764,10202,1429],{},[764,10204,10177],{},[764,10206,10180],{},[764,10208,6848],{},[764,10210,10187],{},[764,10212,10213],{},"TTF manual",[764,10215,5066],{},[764,10217,10198],{},[748,10219,10220,10222,10224,10226,10228,10233,10236,10239],{},[764,10221,10117],{},[764,10223,10177],{},[764,10225,10180],{},[764,10227,6848],{},[764,10229,10230],{},[23,10231,10232],{},"gofpdf (arquivada)",[764,10234,10235],{},"via gofpdf",[764,10237,10238],{},"linha/coluna",[764,10240,10241],{},"mantida sobre base morta",[748,10243,10244,10246,10249,10254,10256,10258,10262,10264],{},[764,10245,1352],{},[764,10247,10248],{},"2021",[764,10250,10251],{},[23,10252,10253],{},"2021-09-08",[764,10255,6848],{},[764,10257,10187],{},[764,10259,10260],{},[43,10261,2539],{},[764,10263,5066],{},[764,10265,10266],{},[23,10267,10268],{},"arquivada",[748,10270,10271,10273,10276,10280,10282,10284,10288,10290],{},[764,10272,1426],{},[764,10274,10275],{},"2023",[764,10277,10278],{},[23,10279,6653],{},[764,10281,6848],{},[764,10283,10187],{},[764,10285,10286],{},[43,10287,2539],{},[764,10289,5066],{},[764,10291,10292],{},[23,10293,10268],{},[748,10295,10296,10298,10300,10302,10307,10310,10313,10315],{},[764,10297,6927],{},[764,10299,10177],{},[764,10301,10180],{},[764,10303,10304],{},[23,10305,10306],{},"AGPL-3.0 / Comercial",[764,10308,10309],{},"várias",[764,10311,10312],{},"sim",[764,10314,5066],{},[764,10316,10317],{},"comercial",[19,10319,10320],{},"Três pontos a notar. Um: metade do campo está arquivado. Dois: Maroto está mantida, mas a fundação não — é problema de supply chain mesmo que compile hoje. Três: se você não pode aceitar AGPL, unidoc vira decisão de licença comercial, não técnica.",[14,10322,10324],{"id":10323},"o-benchmark","O benchmark",[19,10326,10327,10328,10333,10334,10336],{},"Código: ",[28,10329,10331],{"href":1390,"rel":10330},[32],[43,10332,1394],{}," no repo do gpdf. Ambiente: Apple M1 (Max, 32 GB, macOS 14.5), Go 1.25, sem CGO. Cada caso roda por pelo menos cinco segundos de wall time. ",[43,10335,1408],{}," ligado; reportamos ns/op e alocações.",[19,10338,10339],{},"Escolhemos esses quatro casos porque refletem o que as pessoas de fato geram, não algo micro-sintético:",[884,10341,10342,10348,10354,10360],{},[887,10343,10344,10347],{},[23,10345,10346],{},"Hello world de uma página."," Uma página, uma linha de texto, uma fonte. Define o piso de overhead por documento.",[887,10349,10350,10353],{},[23,10351,10352],{},"Tabela de nota fiscal 4×10."," Linha de cabeçalho, dez linhas de corpo, alinhamentos de coluna, bordas finas. A forma \"gerar minha nota\".",[887,10355,10356,10359],{},[23,10357,10358],{},"Relatório paginado de 100 páginas."," Cabeçalho repetido, rodapé, números de página, corpo em cada página. Mede custo de paginação.",[887,10361,10362,10365],{},[23,10363,10364],{},"Nota fiscal CJK complexa."," Japonês misto (Hiragana, Katakana, Kanji), tabela de itens 4×15, cabeçalho, rodapé com número de página, subconjunto TrueType NotoSansJP embarcado.",[19,10367,10368,10369,10371],{},"Não incluído: ",[43,10370,6927],{},". O binário é gated por licença, e reproduzir a metodologia de benchmark publicada dentro de um repo público seria mais enganoso do que útil. Se está avaliando unidoc, rode os benchmarks oficiais deles.",[1935,10373,10375],{"id":10374},"resultados","Resultados",[742,10377,10378,10395],{},[745,10379,10380],{},[748,10381,10382,10385,10387,10389,10391,10393],{},[751,10383,10384],{},"Carga",[751,10386,33],{},[751,10388,1429],{},[751,10390,1432],{},[751,10392,1423],{},[751,10394,1426],{},[759,10396,10397,10414,10432,10452],{},[748,10398,10399,10402,10406,10408,10410,10412],{},[764,10400,10401],{},"Hello world de uma página",[764,10403,10404],{},[23,10405,1340],{},[764,10407,1452],{},[764,10409,1455],{},[764,10411,1446],{},[764,10413,1449],{},[748,10415,10416,10419,10423,10425,10428,10430],{},[764,10417,10418],{},"Tabela de nota fiscal 4×10",[764,10420,10421],{},[23,10422,1344],{},[764,10424,1473],{},[764,10426,10427],{},"8.600 µs",[764,10429,1467],{},[764,10431,1470],{},[748,10433,10434,10437,10441,10443,10446,10449],{},[764,10435,10436],{},"Relatório paginado de 100 pgs.",[764,10438,10439],{},[23,10440,1348],{},[764,10442,10427],{},[764,10444,10445],{},"19.800 µs",[764,10447,10448],{},"11.700 µs",[764,10450,10451],{},"11.900 µs",[748,10453,10454,10456,10460,10462,10465,10467],{},[764,10455,8118],{},[764,10457,10458],{},[23,10459,1506],{},[764,10461,1515],{},[764,10463,10464],{},"10.400 µs",[764,10466,1509],{},[764,10468,10469],{},"n/d",[19,10471,10472,10474,10475,10477],{},[43,10473,10469],{}," em go-pdf/fpdf no caso CJK: o caminho padrão do ",[43,10476,2539],{}," dá panic ao ler a tabela cmap formato 12 do NotoSansJP na versão testada. Dá para patchear, mas o fork está arquivado — ninguém publica a correção.",[1935,10479,10481],{"id":10480},"lendo-os-números","Lendo os números",[19,10483,10484,10485,10488],{},"A ordem é estável entre cargas. gpdf é ",[23,10486,10487],{},"10–30× mais rápido"," que o segundo colocado em todo caso testado — não é exótico nem acidental. Três escolhas de design se somam:",[19,10490,10491,10494],{},[23,10492,10493],{},"Layout em passada única."," gpdf não constrói um AST intermediário para depois serializar. Os builders escrevem direto no content stream do PDF à medida que resolvem, o que elimina cerca de metade das alocações das outras bibliotecas. É o que move o ponteiro no benchmark de 100 páginas, onde a pressão de alocação bate mais no GC — 683 µs contra 19.800 µs não é diferença de tuning, é arquitetura diferente.",[19,10496,10497,10500],{},[23,10498,10499],{},"Sem reflection no hot path."," Todo tipo que a engine de layout toca é concreto. Isoladamente soa a microotimização — e é — mas acumulado num relatório de 100 páginas, o dispatch por interface aparece no perfil. Deixamos de fora.",[19,10502,10503,10506],{},[23,10504,10505],{},"Subsetador TrueType que não varre toda vez."," A máquina de fontes do gofpdf relê a tabela cmap a cada lookup de glifo; gpdf resolve uma vez e cacheia. Para conteúdo só em Latin quase não importa. Para CJK, onde um parágrafo pode tocar 150 glifos únicos entre Kanji, Hiragana, Katakana e pontuação, é a diferença entre \"rápido o suficiente para geração síncrona\" e \"empurra pra fila\".",[19,10508,10509,10510,10513],{},"Uma ressalva que a tabela não dá: ",[23,10511,10512],{},"velocidade absoluta importa menos do que se pensa na maioria das cargas PDF",". O limiar interessante é \"rápido o suficiente para gerar no caminho da requisição\". Toda biblioteca aqui cruza esse limiar para um hello world de uma página. Só gpdf cruza para um relatório de 100 páginas com chrome repetido. Se seu maior documento é um recibo de uma página, as quatro mantidas dão conta; escolha por ergonomia de API e licença.",[14,10515,10517],{"id":10516},"dependências","Dependências",[19,10519,10520,10521,10523,10524,10527],{},"O que ",[43,10522,7064],{}," imprime após um ",[43,10525,10526],{},"go get"," fresco:",[742,10529,10530,10542],{},[745,10531,10532],{},[748,10533,10534,10536,10539],{},[751,10535,6818],{},[751,10537,10538],{},"Módulos externos",[751,10540,10541],{},"Deps. arquivadas transitivas",[759,10543,10544,10557,10565,10575,10583,10595],{},[748,10545,10546,10551,10555],{},[764,10547,10548,10550],{},[23,10549,33],{}," (core)",[764,10552,10553],{},[23,10554,10187],{},[764,10556,10180],{},[748,10558,10559,10561,10563],{},[764,10560,1429],{},[764,10562,10187],{},[764,10564,10180],{},[748,10566,10567,10569,10572],{},[764,10568,1423],{},[764,10570,10571],{},"0 (mas ela mesma arquivada)",[764,10573,10574],{},"ela mesma",[748,10576,10577,10579,10581],{},[764,10578,1426],{},[764,10580,10571],{},[764,10582,10574],{},[748,10584,10585,10587,10592],{},[764,10586,10117],{},[764,10588,10589],{},[23,10590,10591],{},"gofpdf (arquivada em 2021)",[764,10593,10594],{},"sim — gofpdf",[748,10596,10597,10599,10602],{},[764,10598,6927],{},[764,10600,10601],{},"várias (imagem, crypto, compressão)",[764,10603,10604],{},"nenhuma arquivada",[19,10606,10607],{},"Para times com regra de lint \"sem deps arquivadas em produção\", Maroto v2 hoje é barrada pelo transitivo gofpdf. Os mantenedores do Maroto vêm, há mais de um ano, reescrevendo o backend para sair do gofpdf; quando isso chegar, esta linha muda. Vale checar o repo do Maroto antes de decidir sobre essa base.",[14,10609,10611],{"id":10610},"licenciamento","Licenciamento",[742,10613,10614,10622],{},[745,10615,10616],{},[748,10617,10618,10620],{},[751,10619,6818],{},[751,10621,5112],{},[759,10623,10624,10631,10637,10643,10649,10655],{},[748,10625,10626,10629],{},[764,10627,10628],{},"gpdf (core)",[764,10630,6848],{},[748,10632,10633,10635],{},[764,10634,1429],{},[764,10636,6848],{},[748,10638,10639,10641],{},[764,10640,10117],{},[764,10642,6848],{},[748,10644,10645,10647],{},[764,10646,1423],{},[764,10648,6848],{},[748,10650,10651,10653],{},[764,10652,1426],{},[764,10654,6848],{},[748,10656,10657,10661],{},[764,10658,10659],{},[23,10660,6927],{},[764,10662,10663],{},[23,10664,10665],{},"AGPL-3.0 ou licença comercial",[19,10667,10668],{},"Os termos AGPL da unidoc são estritos. Se você a usa num servidor com o qual usuários interagem pela rede, o código do servidor também precisa ser publicado sob AGPL — inviável para a maioria de SaaS de código fechado. Normalmente sobra a licença comercial como única opção real, e o preço não é público: conta com uma conversa com o comercial.",[19,10670,10671,10672,205],{},"É o ponto mais esquecido em comparativos baseados em contagem de stars do GitHub. unidoc tem mais features e mais stars. Também tem uma licença que fecha a porta para a maioria dos casos comerciais sem uma compra. Não é ataque — o modelo é legítimo e o produto é excelente — mas você quer saber antes do ",[43,10673,10526],{},[14,10675,10677],{"id":10676},"estado-de-manutenção-em-português-claro","Estado de manutenção, em português claro",[1114,10679,10680,10685,10690,10695,10703,10711],{},[887,10681,10682,10684],{},[23,10683,33],{}," — mantenedor principal: este time (gpdf-dev). Releases a cada 2–4 semanas; roadmap no repo; CI roda em Go 1.22–1.26; issues do repo principal respondem em poucos dias úteis. Temos skin in the game.",[887,10686,10687,10689],{},[23,10688,1429],{}," — ativo, cadência de commits menor. Issues recebem atenção; PRs entram em poucas semanas. Caso de uso principal: geração de baixo nível.",[887,10691,10692,10694],{},[23,10693,1432],{}," — ativo. A reescrita v2 chegou em 2023 e está estável. A dependência em gofpdf é conhecida e está sendo substituída; cheque o repo antes de se comprometer.",[887,10696,10697,10699,10700,10702],{},[23,10698,1423],{}," — arquivado em 2021-09-08. Banner no repo: ",[2939,10701,6671],{}," Sem patches de segurança, sem correções.",[887,10704,10705,10707,10708,205],{},[23,10706,1426],{}," — arquivado em 2025. O README recomenda usar outra biblioteca. Escrevemos um guia dedicado: ",[28,10709,10710],{"href":2796},"gofpdf está arquivado. Veja como migrar para gpdf.",[887,10712,10713,10715],{},[23,10714,6927],{}," — ativo, time comercial, bem financiado. Suporte enterprise disponível.",[14,10717,10719],{"id":10718},"como-escolher","Como escolher",[19,10721,10722],{},"Em vez de matriz de features, uma árvore de decisão, porque \"mais features\" raramente é a pergunta certa:",[1114,10724,10725,10731,10737,10743,10749,10758],{},[887,10726,10727,10730],{},[23,10728,10729],{},"\"Tenho um código Go que gera notas fiscais, relatórios ou documentos, quero MIT, zero deps, e às vezes os documentos têm CJK.\""," → gpdf.",[887,10732,10733,10736],{},[23,10734,10735],{},"\"Faço geração PDF de baixo nível com geometria customizada e quero uma biblioteca pequena, estável e com controle manual.\""," → signintech/gopdf.",[887,10738,10739,10742],{},[23,10740,10741],{},"\"Já tenho código estilo Maroto que funciona hoje.\""," → fique no Maroto v2 até a remoção do gofpdf cair, depois reavalie. A API não é o problema.",[887,10744,10745,10748],{},[23,10746,10747],{},"\"Preciso de PDF/A, OCR, redação, assinatura digital, e meu empregador paga licença comercial.\""," → unidoc/unipdf, com a conversa de licenciamento primeiro.",[887,10750,10751,10754,10755],{},[23,10752,10753],{},"\"Ainda estou no gofpdf e funciona.\""," → hoje, ok. Planeje a migração antes que caia o próximo CVE em uma dep não relacionada e você fique preso em código sem manutenção. ",[28,10756,10757],{"href":2796},"Guia de migração.",[887,10759,10760,10763],{},[23,10761,10762],{},"\"Renderização pixel-perfect de HTML/CSS para PDF.\""," → nenhuma das acima. Use go-rod ou chromedp com Chromium headless, assumindo o custo de cold start.",[19,10765,10766],{},"Somos o time do gpdf, então claro que achamos gpdf o default certo para o primeiro caso e a maior parte do quinto. Leia o código do benchmark, rode localmente, não engula a tabela.",[14,10768,10770],{"id":10769},"um-exemplo-gpdf-em-30-linhas","Um exemplo gpdf em 30 linhas",[19,10772,10773],{},"Porque \"a mais rápida\" e \"com o menor grafo de deps\" só importam se o código for suportável. Esta é uma nota fiscal de uma página completa e executável — sem pseudocódigo, sem imports omitidos:",[62,10775,10777],{"className":64,"code":10776,"language":66,"meta":67,"style":67},"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/pdf\"\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(\"NOTA FISCAL #2026-0042\", template.Bold(), template.FontSize(20))\n            c.Spacer(document.Mm(6))\n            c.Table(\n                []string{\"Descrição\", \"Qtd.\", \"Unit.\", \"Valor\"},\n                [][]string{\n                    {\"Desenvolvimento frontend\", \"40 h\", \"R$ 750,00\", \"R$ 30.000,00\"},\n                    {\"Desenvolvimento backend\",  \"60 h\", \"R$ 750,00\", \"R$ 45.000,00\"},\n                    {\"Design de UI\",             \"20 h\", \"R$ 600,00\", \"R$ 12.000,00\"},\n                },\n                template.ColumnWidths(50, 15, 15, 20),\n                template.TableHeaderStyle(\n                    template.Bold(),\n                    template.TextColor(pdf.White),\n                    template.BgColor(pdf.RGBHex(0x1A237E)),\n                ),\n            )\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[43,10778,10779,10785,10789,10795,10803,10811,10815,10823,10831,10840,10848,10852,10856,10866,10880,10898,10928,10932,10936,10950,10974,11004,11043,11065,11076,11123,11133,11174,11214,11255,11260,11291,11302,11314,11334,11360,11365,11370,11374,11378,11382,11400,11412,11426,11430,11470,11484,11488],{"__ignoreMap":67},[71,10780,10781,10783],{"class":73,"line":74},[71,10782,78],{"class":77},[71,10784,82],{"class":81},[71,10786,10787],{"class":73,"line":85},[71,10788,89],{"emptyLinePlaceholder":88},[71,10790,10791,10793],{"class":73,"line":92},[71,10792,96],{"class":95},[71,10794,99],{"class":77},[71,10796,10797,10799,10801],{"class":73,"line":102},[71,10798,105],{"class":77},[71,10800,108],{"class":81},[71,10802,111],{"class":77},[71,10804,10805,10807,10809],{"class":73,"line":114},[71,10806,105],{"class":77},[71,10808,119],{"class":81},[71,10810,111],{"class":77},[71,10812,10813],{"class":73,"line":124},[71,10814,89],{"emptyLinePlaceholder":88},[71,10816,10817,10819,10821],{"class":73,"line":129},[71,10818,105],{"class":77},[71,10820,134],{"class":81},[71,10822,111],{"class":77},[71,10824,10825,10827,10829],{"class":73,"line":139},[71,10826,105],{"class":77},[71,10828,144],{"class":81},[71,10830,111],{"class":77},[71,10832,10833,10835,10838],{"class":73,"line":149},[71,10834,105],{"class":77},[71,10836,10837],{"class":81},"github.com/gpdf-dev/gpdf/pdf",[71,10839,111],{"class":77},[71,10841,10842,10844,10846],{"class":73,"line":159},[71,10843,105],{"class":77},[71,10845,154],{"class":81},[71,10847,111],{"class":77},[71,10849,10850],{"class":73,"line":165},[71,10851,162],{"class":77},[71,10853,10854],{"class":73,"line":170},[71,10855,89],{"emptyLinePlaceholder":88},[71,10857,10858,10860,10862,10864],{"class":73,"line":186},[71,10859,173],{"class":77},[71,10861,177],{"class":176},[71,10863,180],{"class":77},[71,10865,183],{"class":77},[71,10867,10868,10870,10872,10874,10876,10878],{"class":73,"line":224},[71,10869,272],{"class":189},[71,10871,199],{"class":77},[71,10873,277],{"class":189},[71,10875,205],{"class":77},[71,10877,282],{"class":176},[71,10879,285],{"class":77},[71,10881,10882,10884,10886,10888,10890,10892,10894,10896],{"class":73,"line":240},[71,10883,291],{"class":189},[71,10885,205],{"class":77},[71,10887,296],{"class":176},[71,10889,211],{"class":77},[71,10891,323],{"class":189},[71,10893,205],{"class":77},[71,10895,305],{"class":189},[71,10897,308],{"class":77},[71,10899,10900,10902,10904,10906,10908,10910,10912,10914,10916,10918,10920,10922,10924,10926],{"class":73,"line":258},[71,10901,291],{"class":189},[71,10903,205],{"class":77},[71,10905,318],{"class":176},[71,10907,211],{"class":77},[71,10909,323],{"class":189},[71,10911,205],{"class":77},[71,10913,328],{"class":176},[71,10915,211],{"class":77},[71,10917,323],{"class":189},[71,10919,205],{"class":77},[71,10921,337],{"class":176},[71,10923,211],{"class":77},[71,10925,343],{"class":342},[71,10927,346],{"class":77},[71,10929,10930],{"class":73,"line":264},[71,10931,403],{"class":77},[71,10933,10934],{"class":73,"line":269},[71,10935,89],{"emptyLinePlaceholder":88},[71,10937,10938,10940,10942,10944,10946,10948],{"class":73,"line":288},[71,10939,414],{"class":189},[71,10941,199],{"class":77},[71,10943,419],{"class":189},[71,10945,205],{"class":77},[71,10947,424],{"class":176},[71,10949,427],{"class":77},[71,10951,10952,10954,10956,10958,10960,10962,10964,10966,10968,10970,10972],{"class":73,"line":311},[71,10953,433],{"class":189},[71,10955,205],{"class":77},[71,10957,438],{"class":176},[71,10959,441],{"class":77},[71,10961,445],{"class":444},[71,10963,448],{"class":77},[71,10965,451],{"class":81},[71,10967,205],{"class":77},[71,10969,456],{"class":81},[71,10971,459],{"class":77},[71,10973,183],{"class":77},[71,10975,10976,10978,10980,10982,10984,10986,10988,10990,10992,10994,10996,10998,11000,11002],{"class":73,"line":349},[71,10977,467],{"class":189},[71,10979,205],{"class":77},[71,10981,472],{"class":176},[71,10983,211],{"class":77},[71,10985,477],{"class":342},[71,10987,193],{"class":77},[71,10989,482],{"class":77},[71,10991,485],{"class":444},[71,10993,448],{"class":77},[71,10995,451],{"class":81},[71,10997,205],{"class":77},[71,10999,494],{"class":81},[71,11001,459],{"class":77},[71,11003,183],{"class":77},[71,11005,11006,11008,11010,11012,11014,11016,11019,11021,11023,11025,11027,11029,11031,11033,11035,11037,11039,11041],{"class":73,"line":375},[71,11007,504],{"class":189},[71,11009,205],{"class":77},[71,11011,509],{"class":176},[71,11013,211],{"class":77},[71,11015,214],{"class":77},[71,11017,11018],{"class":217},"NOTA FISCAL #2026-0042",[71,11020,214],{"class":77},[71,11022,193],{"class":77},[71,11024,523],{"class":189},[71,11026,205],{"class":77},[71,11028,543],{"class":176},[71,11030,7612],{"class":77},[71,11032,523],{"class":189},[71,11034,205],{"class":77},[71,11036,528],{"class":176},[71,11038,211],{"class":77},[71,11040,343],{"class":342},[71,11042,4998],{"class":77},[71,11044,11045,11047,11049,11051,11053,11055,11057,11059,11061,11063],{"class":73,"line":400},[71,11046,504],{"class":189},[71,11048,205],{"class":77},[71,11050,2121],{"class":176},[71,11052,211],{"class":77},[71,11054,323],{"class":189},[71,11056,205],{"class":77},[71,11058,337],{"class":176},[71,11060,211],{"class":77},[71,11062,5625],{"class":342},[71,11064,4998],{"class":77},[71,11066,11067,11069,11071,11074],{"class":73,"line":406},[71,11068,504],{"class":189},[71,11070,205],{"class":77},[71,11072,11073],{"class":176},"Table",[71,11075,285],{"class":77},[71,11077,11078,11081,11083,11086,11088,11091,11093,11095,11097,11100,11102,11104,11106,11109,11111,11113,11115,11118,11120],{"class":73,"line":411},[71,11079,11080],{"class":77},"                []",[71,11082,3891],{"class":1607},[71,11084,11085],{"class":77},"{",[71,11087,214],{"class":77},[71,11089,11090],{"class":217},"Descrição",[71,11092,214],{"class":77},[71,11094,193],{"class":77},[71,11096,7424],{"class":77},[71,11098,11099],{"class":217},"Qtd.",[71,11101,214],{"class":77},[71,11103,193],{"class":77},[71,11105,7424],{"class":77},[71,11107,11108],{"class":217},"Unit.",[71,11110,214],{"class":77},[71,11112,193],{"class":77},[71,11114,7424],{"class":77},[71,11116,11117],{"class":217},"Valor",[71,11119,214],{"class":77},[71,11121,11122],{"class":77},"},\n",[71,11124,11125,11128,11130],{"class":73,"line":430},[71,11126,11127],{"class":77},"                [][]",[71,11129,3891],{"class":1607},[71,11131,11132],{"class":77},"{\n",[71,11134,11135,11138,11140,11143,11145,11147,11149,11152,11154,11156,11158,11161,11163,11165,11167,11170,11172],{"class":73,"line":464},[71,11136,11137],{"class":77},"                    {",[71,11139,214],{"class":77},[71,11141,11142],{"class":217},"Desenvolvimento frontend",[71,11144,214],{"class":77},[71,11146,193],{"class":77},[71,11148,7424],{"class":77},[71,11150,11151],{"class":217},"40 h",[71,11153,214],{"class":77},[71,11155,193],{"class":77},[71,11157,7424],{"class":77},[71,11159,11160],{"class":217},"R$ 750,00",[71,11162,214],{"class":77},[71,11164,193],{"class":77},[71,11166,7424],{"class":77},[71,11168,11169],{"class":217},"R$ 30.000,00",[71,11171,214],{"class":77},[71,11173,11122],{"class":77},[71,11175,11176,11178,11180,11183,11185,11187,11190,11193,11195,11197,11199,11201,11203,11205,11207,11210,11212],{"class":73,"line":501},[71,11177,11137],{"class":77},[71,11179,214],{"class":77},[71,11181,11182],{"class":217},"Desenvolvimento backend",[71,11184,214],{"class":77},[71,11186,193],{"class":77},[71,11188,11189],{"class":77},"  \"",[71,11191,11192],{"class":217},"60 h",[71,11194,214],{"class":77},[71,11196,193],{"class":77},[71,11198,7424],{"class":77},[71,11200,11160],{"class":217},[71,11202,214],{"class":77},[71,11204,193],{"class":77},[71,11206,7424],{"class":77},[71,11208,11209],{"class":217},"R$ 45.000,00",[71,11211,214],{"class":77},[71,11213,11122],{"class":77},[71,11215,11216,11218,11220,11223,11225,11227,11230,11233,11235,11237,11239,11242,11244,11246,11248,11251,11253],{"class":73,"line":549},[71,11217,11137],{"class":77},[71,11219,214],{"class":77},[71,11221,11222],{"class":217},"Design de UI",[71,11224,214],{"class":77},[71,11226,193],{"class":77},[71,11228,11229],{"class":77},"             \"",[71,11231,11232],{"class":217},"20 h",[71,11234,214],{"class":77},[71,11236,193],{"class":77},[71,11238,7424],{"class":77},[71,11240,11241],{"class":217},"R$ 600,00",[71,11243,214],{"class":77},[71,11245,193],{"class":77},[71,11247,7424],{"class":77},[71,11249,11250],{"class":217},"R$ 12.000,00",[71,11252,214],{"class":77},[71,11254,11122],{"class":77},[71,11256,11257],{"class":73,"line":569},[71,11258,11259],{"class":77},"                },\n",[71,11261,11262,11265,11267,11270,11272,11275,11277,11280,11282,11284,11286,11289],{"class":73,"line":575},[71,11263,11264],{"class":189},"                template",[71,11266,205],{"class":77},[71,11268,11269],{"class":176},"ColumnWidths",[71,11271,211],{"class":77},[71,11273,11274],{"class":342},"50",[71,11276,193],{"class":77},[71,11278,11279],{"class":342}," 15",[71,11281,193],{"class":77},[71,11283,11279],{"class":342},[71,11285,193],{"class":77},[71,11287,11288],{"class":342}," 20",[71,11290,308],{"class":77},[71,11292,11293,11295,11297,11300],{"class":73,"line":581},[71,11294,11264],{"class":189},[71,11296,205],{"class":77},[71,11298,11299],{"class":176},"TableHeaderStyle",[71,11301,285],{"class":77},[71,11303,11304,11307,11309,11311],{"class":73,"line":586},[71,11305,11306],{"class":189},"                    template",[71,11308,205],{"class":77},[71,11310,543],{"class":176},[71,11312,11313],{"class":77},"(),\n",[71,11315,11316,11318,11320,11323,11325,11327,11329,11332],{"class":73,"line":607},[71,11317,11306],{"class":189},[71,11319,205],{"class":77},[71,11321,11322],{"class":176},"TextColor",[71,11324,211],{"class":77},[71,11326,2265],{"class":189},[71,11328,205],{"class":77},[71,11330,11331],{"class":189},"White",[71,11333,308],{"class":77},[71,11335,11336,11338,11340,11343,11345,11347,11349,11352,11354,11357],{"class":73,"line":620},[71,11337,11306],{"class":189},[71,11339,205],{"class":77},[71,11341,11342],{"class":176},"BgColor",[71,11344,211],{"class":77},[71,11346,2265],{"class":189},[71,11348,205],{"class":77},[71,11350,11351],{"class":176},"RGBHex",[71,11353,211],{"class":77},[71,11355,11356],{"class":342},"0x1A237E",[71,11358,11359],{"class":77},")),\n",[71,11361,11362],{"class":73,"line":635},[71,11363,11364],{"class":77},"                ),\n",[71,11366,11367],{"class":73,"line":640},[71,11368,11369],{"class":77},"            )\n",[71,11371,11372],{"class":73,"line":686},[71,11373,572],{"class":77},[71,11375,11376],{"class":73,"line":701},[71,11377,578],{"class":77},[71,11379,11380],{"class":73,"line":706},[71,11381,89],{"emptyLinePlaceholder":88},[71,11383,11384,11386,11388,11390,11392,11394,11396,11398],{"class":73,"line":4593},[71,11385,589],{"class":189},[71,11387,193],{"class":77},[71,11389,196],{"class":189},[71,11391,199],{"class":77},[71,11393,419],{"class":189},[71,11395,205],{"class":77},[71,11397,602],{"class":176},[71,11399,427],{"class":77},[71,11401,11402,11404,11406,11408,11410],{"class":73,"line":5828},[71,11403,227],{"class":95},[71,11405,196],{"class":189},[71,11407,232],{"class":77},[71,11409,235],{"class":77},[71,11411,183],{"class":77},[71,11413,11414,11416,11418,11420,11422,11424],{"class":73,"line":5848},[71,11415,243],{"class":189},[71,11417,205],{"class":77},[71,11419,248],{"class":176},[71,11421,211],{"class":77},[71,11423,253],{"class":189},[71,11425,162],{"class":77},[71,11427,11428],{"class":73,"line":5853},[71,11429,261],{"class":77},[71,11431,11432,11434,11436,11438,11440,11442,11444,11446,11448,11450,11452,11454,11456,11458,11460,11462,11464,11466,11468],{"class":73,"line":5884},[71,11433,227],{"class":95},[71,11435,196],{"class":189},[71,11437,199],{"class":77},[71,11439,202],{"class":189},[71,11441,205],{"class":77},[71,11443,653],{"class":176},[71,11445,211],{"class":77},[71,11447,214],{"class":77},[71,11449,4552],{"class":217},[71,11451,214],{"class":77},[71,11453,193],{"class":77},[71,11455,667],{"class":189},[71,11457,193],{"class":77},[71,11459,672],{"class":342},[71,11461,675],{"class":77},[71,11463,196],{"class":189},[71,11465,232],{"class":77},[71,11467,235],{"class":77},[71,11469,183],{"class":77},[71,11471,11472,11474,11476,11478,11480,11482],{"class":73,"line":5904},[71,11473,243],{"class":189},[71,11475,205],{"class":77},[71,11477,248],{"class":176},[71,11479,211],{"class":77},[71,11481,253],{"class":189},[71,11483,162],{"class":77},[71,11485,11486],{"class":73,"line":5909},[71,11487,261],{"class":77},[71,11489,11490],{"class":73,"line":5940},[71,11491,709],{"class":77},[19,11493,11494,11495,11497,11498,11501,11502,11505,11506,11508],{},"Zero ",[43,11496,1917],{},". Zero conta manual de largura de coluna. Troque ",[43,11499,11500],{},"\"Descrição\""," por ",[43,11503,11504],{},"\"品目\""," e adicione ",[43,11507,8018],{}," nas opções do documento — renderiza japonês sem nenhuma outra mudança. Útil também para DANFE e layouts de nota fiscal eletrônica que precisam de fontes específicas.",[14,11510,11512],{"id":11511},"o-que-ficou-de-fora","O que ficou de fora",[19,11514,11515],{},"Todo post de comparação tem a seção \"omitido por X\". A nossa:",[1114,11517,11518,11524,11536,11546],{},[887,11519,11520,11523],{},[23,11521,11522],{},"Forks privados de gofpdf."," Existem forks em produção dentro de empresas. Não dá para benchmarkear código que não vemos.",[887,11525,11526,11531,11532,11535],{},[23,11527,11528,205],{},[43,11529,11530],{},"pdfcpu"," Está em toda lista de \"bibliotecas Go PDF\", mas é sobretudo um ",[23,11533,11534],{},"processador"," (merge, split, encrypt, stamp), não um gerador. Fora de escopo aqui; um artigo separado sobre processamento está planejado.",[887,11537,11538,11545],{},[23,11539,11540,11541,11544],{},"Qualquer wrapper em volta do ",[43,11542,11543],{},"gotenberg"," ou serviço de navegador headless."," Não é biblioteca. Não é comparação justa.",[887,11547,11548,11553],{},[23,11549,11550,11551,205],{},"Nossos próprios benchmarks do ",[43,11552,33],{}," O que importa para a comparação são os números do core.",[14,11555,2787],{"id":2786},[19,11557,11558,11561],{},[23,11559,11560],{},"Por que gpdf é 10× mais rápido que gofpdf? Qual é o truque?","\nNenhum truque único. Três designs se somam: layout em passada única (sem AST entre builder e writer), tipos concretos no hot path e um subsetador TrueType que cacheia a cmap. Qualquer um isolado dá 2×. Empilhados, dá uma ordem de grandeza.",[19,11563,11564,11567,11568,11571],{},[23,11565,11566],{},"Dá para reproduzir esse benchmark?","\nDá. ",[43,11569,11570],{},"git clone https://github.com/gpdf-dev/gpdf && cd gpdf/_benchmark && go test -bench=. -benchmem",". Se os números divergirem do post — mesma arquitetura, mesma versão do Go — abra uma issue. Drift de benchmark acontece; preferimos saber.",[19,11573,11574,11577],{},[23,11575,11576],{},"Gofpdf volta?","\nNa prática, não. Último commit de 2021. Issue tracker fechado. Mesmo que alguém reabrisse, a arquitetura (cursor único, fontes de um byte, sem grid) é ponto de partida errado para 2026. Melhor tratar como artefato histórico e migrar.",[19,11579,11580,11583],{},[23,11581,11582],{},"E Java iText / Python ReportLab / Node pdfkit?","\nBenchmark entre linguagens é outro post. Versão curta: Go costuma vencer em throughput e cold start, perde em amplitude de features (sobretudo em fidelidade HTML→PDF). Para times já em Go, gpdf é mais rápido e menor que qualquer dessas opções cross-language; para times em Python ou Node, o custo de migração só se paga em volume alto.",[19,11585,11586,11589],{},[23,11587,11588],{},"Essa comparação continua justa se os concorrentes melhorarem?","\nContinua. Rodamos todo ano. Se signintech/gopdf publicar uma API de tabela que corta o tempo pela metade, entra no post de 2027. Se Maroto v2 terminar de remover gofpdf, aquela linha muda. O código do benchmark é público justamente para ninguém precisar acreditar em nós pela palavra.",[14,11591,1242],{"id":1241},[19,11593,11594],{},"gpdf é uma biblioteca Go para gerar PDFs. MIT, zero dependências, CJK nativo.",[62,11596,11597],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,11598,11599],{"__ignoreMap":67},[71,11600,11601,11603,11605],{"class":73,"line":74},[71,11602,66],{"class":81},[71,11604,1259],{"class":217},[71,11606,1262],{"class":217},[19,11608,11609,1269,11612],{},[28,11610,1268],{"href":30,"rel":11611},[32],[28,11613,11615],{"href":1272,"rel":11614},[32],"Leia os docs",[14,11617,8323],{"id":8322},[1114,11619,11620,11625,11634],{},[887,11621,11622,11624],{},[28,11623,10710],{"href":2796}," — o mapeamento completo de API com cinco pares antes/depois.",[887,11626,11627,11631,11632,205],{},[28,11628,11630],{"href":1272,"rel":11629},[32],"Quickstart"," — setup de cinco minutos, incluindo ",[43,11633,6688],{},[887,11635,2879,11636,205],{},[28,11637,11639],{"href":1390,"rel":11638},[32],[43,11640,1394],{},[1276,11642,11643],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":67,"searchDepth":85,"depth":85,"links":11645},[11646,11647,11648,11649,11653,11654,11655,11656,11657,11658,11659,11660,11661],{"id":37,"depth":85,"text":38},{"id":10092,"depth":85,"text":10093},{"id":10136,"depth":85,"text":10137},{"id":10323,"depth":85,"text":10324,"children":11650},[11651,11652],{"id":10374,"depth":92,"text":10375},{"id":10480,"depth":92,"text":10481},{"id":10516,"depth":85,"text":10517},{"id":10610,"depth":85,"text":10611},{"id":10676,"depth":85,"text":10677},{"id":10718,"depth":85,"text":10719},{"id":10769,"depth":85,"text":10770},{"id":11511,"depth":85,"text":11512},{"id":2786,"depth":85,"text":2787},{"id":1241,"depth":85,"text":1242},{"id":8322,"depth":85,"text":8323},"2026-04-15","Todas as bibliotecas Go PDF ainda vivas em 2026, comparadas em 4 cargas de trabalho, com licenças, dependências e estado de manutenção.",{},{"title":2869,"description":11663},"pt/blog/002.go-pdf-library-showdown-2026",[2918,2916],"kmq_cU0BTl9KsT2GUXEWN5va7Pp1nDtdlCbQGaEYEc4",{"id":11670,"title":1223,"author":11671,"body":11672,"date":11662,"description":12967,"draft":1293,"extension":1294,"howTo":12968,"image":1317,"meta":12984,"navigation":88,"path":1222,"seo":12985,"stem":12986,"tags":12987,"updated":1317,"__hash__":12988},"blogPt/pt/blog/003.embed-japanese-font.md",{"name":8,"url":9},{"type":11,"value":11673,"toc":12956},[11674,11676,11686,11690,11704,11706,12210,12222,12226,12229,12235,12244,12257,12261,12270,12415,12431,12435,12441,12867,12874,12878,12884,12890,12902,12904,12929,12931,12934,12946,12954],[14,11675,17],{"id":16},[19,11677,11678,11679,11682,11683,11685],{},"Como renderizar texto japonês (ou CJK em geral) em um PDF gerado com o ",[28,11680,33],{"href":30,"rel":11681},[32]," — sem a cerimônia do ",[43,11684,2539],{},", sem CGO, sem embutir cinco megabytes de fonte em cada documento?",[14,11687,11689],{"id":11688},"resposta-rápida","Resposta rápida",[19,11691,11692,11693,11696,11697,11699,11700,11703],{},"Leia os bytes do TTF. Passe ",[43,11694,11695],{},"gpdf.WithFont(\"NotoSansJP\", fontBytes)"," para o ",[43,11698,282],{},". Opcionalmente marque-a como padrão. ",[23,11701,11702],{},"Três linhas de setup, e o gpdf embute automaticamente só os glifos que você realmente usou"," — não os 5 MB inteiros.",[14,11705,60],{"id":59},[62,11707,11709],{"className":64,"code":11708,"language":66,"meta":67,"style":67},"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    font, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", font),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 12),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"こんにちは、世界。\", template.FontSize(24), template.Bold())\n            c.Text(\"日本語 PDF、これだけ。\")\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",[43,11710,11711,11717,11721,11727,11735,11743,11747,11755,11763,11771,11775,11779,11789,11815,11827,11841,11845,11849,11863,11881,11911,11933,11955,11959,11963,11977,12001,12031,12069,12088,12092,12096,12100,12118,12130,12144,12148,12188,12202,12206],{"__ignoreMap":67},[71,11712,11713,11715],{"class":73,"line":74},[71,11714,78],{"class":77},[71,11716,82],{"class":81},[71,11718,11719],{"class":73,"line":85},[71,11720,89],{"emptyLinePlaceholder":88},[71,11722,11723,11725],{"class":73,"line":92},[71,11724,96],{"class":95},[71,11726,99],{"class":77},[71,11728,11729,11731,11733],{"class":73,"line":102},[71,11730,105],{"class":77},[71,11732,108],{"class":81},[71,11734,111],{"class":77},[71,11736,11737,11739,11741],{"class":73,"line":114},[71,11738,105],{"class":77},[71,11740,119],{"class":81},[71,11742,111],{"class":77},[71,11744,11745],{"class":73,"line":124},[71,11746,89],{"emptyLinePlaceholder":88},[71,11748,11749,11751,11753],{"class":73,"line":129},[71,11750,105],{"class":77},[71,11752,134],{"class":81},[71,11754,111],{"class":77},[71,11756,11757,11759,11761],{"class":73,"line":139},[71,11758,105],{"class":77},[71,11760,144],{"class":81},[71,11762,111],{"class":77},[71,11764,11765,11767,11769],{"class":73,"line":149},[71,11766,105],{"class":77},[71,11768,154],{"class":81},[71,11770,111],{"class":77},[71,11772,11773],{"class":73,"line":159},[71,11774,162],{"class":77},[71,11776,11777],{"class":73,"line":165},[71,11778,89],{"emptyLinePlaceholder":88},[71,11780,11781,11783,11785,11787],{"class":73,"line":170},[71,11782,173],{"class":77},[71,11784,177],{"class":176},[71,11786,180],{"class":77},[71,11788,183],{"class":77},[71,11790,11791,11793,11795,11797,11799,11801,11803,11805,11807,11809,11811,11813],{"class":73,"line":186},[71,11792,190],{"class":189},[71,11794,193],{"class":77},[71,11796,196],{"class":189},[71,11798,199],{"class":77},[71,11800,202],{"class":189},[71,11802,205],{"class":77},[71,11804,208],{"class":176},[71,11806,211],{"class":77},[71,11808,214],{"class":77},[71,11810,2989],{"class":217},[71,11812,214],{"class":77},[71,11814,162],{"class":77},[71,11816,11817,11819,11821,11823,11825],{"class":73,"line":224},[71,11818,227],{"class":95},[71,11820,196],{"class":189},[71,11822,232],{"class":77},[71,11824,235],{"class":77},[71,11826,183],{"class":77},[71,11828,11829,11831,11833,11835,11837,11839],{"class":73,"line":240},[71,11830,243],{"class":189},[71,11832,205],{"class":77},[71,11834,248],{"class":176},[71,11836,211],{"class":77},[71,11838,253],{"class":189},[71,11840,162],{"class":77},[71,11842,11843],{"class":73,"line":258},[71,11844,261],{"class":77},[71,11846,11847],{"class":73,"line":264},[71,11848,89],{"emptyLinePlaceholder":88},[71,11850,11851,11853,11855,11857,11859,11861],{"class":73,"line":269},[71,11852,272],{"class":189},[71,11854,199],{"class":77},[71,11856,277],{"class":189},[71,11858,205],{"class":77},[71,11860,282],{"class":176},[71,11862,285],{"class":77},[71,11864,11865,11867,11869,11871,11873,11875,11877,11879],{"class":73,"line":288},[71,11866,291],{"class":189},[71,11868,205],{"class":77},[71,11870,296],{"class":176},[71,11872,211],{"class":77},[71,11874,33],{"class":189},[71,11876,205],{"class":77},[71,11878,305],{"class":189},[71,11880,308],{"class":77},[71,11882,11883,11885,11887,11889,11891,11893,11895,11897,11899,11901,11903,11905,11907,11909],{"class":73,"line":311},[71,11884,291],{"class":189},[71,11886,205],{"class":77},[71,11888,318],{"class":176},[71,11890,211],{"class":77},[71,11892,323],{"class":189},[71,11894,205],{"class":77},[71,11896,328],{"class":176},[71,11898,211],{"class":77},[71,11900,323],{"class":189},[71,11902,205],{"class":77},[71,11904,337],{"class":176},[71,11906,211],{"class":77},[71,11908,343],{"class":342},[71,11910,346],{"class":77},[71,11912,11913,11915,11917,11919,11921,11923,11925,11927,11929,11931],{"class":73,"line":349},[71,11914,291],{"class":189},[71,11916,205],{"class":77},[71,11918,356],{"class":176},[71,11920,211],{"class":77},[71,11922,214],{"class":77},[71,11924,3227],{"class":217},[71,11926,214],{"class":77},[71,11928,193],{"class":77},[71,11930,370],{"class":189},[71,11932,308],{"class":77},[71,11934,11935,11937,11939,11941,11943,11945,11947,11949,11951,11953],{"class":73,"line":375},[71,11936,291],{"class":189},[71,11938,205],{"class":77},[71,11940,382],{"class":176},[71,11942,211],{"class":77},[71,11944,214],{"class":77},[71,11946,3227],{"class":217},[71,11948,214],{"class":77},[71,11950,193],{"class":77},[71,11952,3256],{"class":342},[71,11954,308],{"class":77},[71,11956,11957],{"class":73,"line":400},[71,11958,403],{"class":77},[71,11960,11961],{"class":73,"line":406},[71,11962,89],{"emptyLinePlaceholder":88},[71,11964,11965,11967,11969,11971,11973,11975],{"class":73,"line":411},[71,11966,414],{"class":189},[71,11968,199],{"class":77},[71,11970,419],{"class":189},[71,11972,205],{"class":77},[71,11974,424],{"class":176},[71,11976,427],{"class":77},[71,11978,11979,11981,11983,11985,11987,11989,11991,11993,11995,11997,11999],{"class":73,"line":430},[71,11980,433],{"class":189},[71,11982,205],{"class":77},[71,11984,438],{"class":176},[71,11986,441],{"class":77},[71,11988,445],{"class":444},[71,11990,448],{"class":77},[71,11992,451],{"class":81},[71,11994,205],{"class":77},[71,11996,456],{"class":81},[71,11998,459],{"class":77},[71,12000,183],{"class":77},[71,12002,12003,12005,12007,12009,12011,12013,12015,12017,12019,12021,12023,12025,12027,12029],{"class":73,"line":464},[71,12004,467],{"class":189},[71,12006,205],{"class":77},[71,12008,472],{"class":176},[71,12010,211],{"class":77},[71,12012,477],{"class":342},[71,12014,193],{"class":77},[71,12016,482],{"class":77},[71,12018,485],{"class":444},[71,12020,448],{"class":77},[71,12022,451],{"class":81},[71,12024,205],{"class":77},[71,12026,494],{"class":81},[71,12028,459],{"class":77},[71,12030,183],{"class":77},[71,12032,12033,12035,12037,12039,12041,12043,12045,12047,12049,12051,12053,12055,12057,12059,12061,12063,12065,12067],{"class":73,"line":501},[71,12034,504],{"class":189},[71,12036,205],{"class":77},[71,12038,509],{"class":176},[71,12040,211],{"class":77},[71,12042,214],{"class":77},[71,12044,3349],{"class":217},[71,12046,214],{"class":77},[71,12048,193],{"class":77},[71,12050,523],{"class":189},[71,12052,205],{"class":77},[71,12054,528],{"class":176},[71,12056,211],{"class":77},[71,12058,533],{"class":342},[71,12060,536],{"class":77},[71,12062,523],{"class":189},[71,12064,205],{"class":77},[71,12066,543],{"class":176},[71,12068,546],{"class":77},[71,12070,12071,12073,12075,12077,12079,12081,12084,12086],{"class":73,"line":549},[71,12072,504],{"class":189},[71,12074,205],{"class":77},[71,12076,509],{"class":176},[71,12078,211],{"class":77},[71,12080,214],{"class":77},[71,12082,12083],{"class":217},"日本語 PDF、これだけ。",[71,12085,214],{"class":77},[71,12087,162],{"class":77},[71,12089,12090],{"class":73,"line":569},[71,12091,572],{"class":77},[71,12093,12094],{"class":73,"line":575},[71,12095,578],{"class":77},[71,12097,12098],{"class":73,"line":581},[71,12099,89],{"emptyLinePlaceholder":88},[71,12101,12102,12104,12106,12108,12110,12112,12114,12116],{"class":73,"line":586},[71,12103,589],{"class":189},[71,12105,193],{"class":77},[71,12107,196],{"class":189},[71,12109,199],{"class":77},[71,12111,419],{"class":189},[71,12113,205],{"class":77},[71,12115,602],{"class":176},[71,12117,427],{"class":77},[71,12119,12120,12122,12124,12126,12128],{"class":73,"line":607},[71,12121,227],{"class":95},[71,12123,196],{"class":189},[71,12125,232],{"class":77},[71,12127,235],{"class":77},[71,12129,183],{"class":77},[71,12131,12132,12134,12136,12138,12140,12142],{"class":73,"line":620},[71,12133,243],{"class":189},[71,12135,205],{"class":77},[71,12137,248],{"class":176},[71,12139,211],{"class":77},[71,12141,253],{"class":189},[71,12143,162],{"class":77},[71,12145,12146],{"class":73,"line":635},[71,12147,261],{"class":77},[71,12149,12150,12152,12154,12156,12158,12160,12162,12164,12166,12168,12170,12172,12174,12176,12178,12180,12182,12184,12186],{"class":73,"line":640},[71,12151,227],{"class":95},[71,12153,196],{"class":189},[71,12155,199],{"class":77},[71,12157,202],{"class":189},[71,12159,205],{"class":77},[71,12161,653],{"class":176},[71,12163,211],{"class":77},[71,12165,214],{"class":77},[71,12167,3434],{"class":217},[71,12169,214],{"class":77},[71,12171,193],{"class":77},[71,12173,667],{"class":189},[71,12175,193],{"class":77},[71,12177,672],{"class":342},[71,12179,675],{"class":77},[71,12181,196],{"class":189},[71,12183,232],{"class":77},[71,12185,235],{"class":77},[71,12187,183],{"class":77},[71,12189,12190,12192,12194,12196,12198,12200],{"class":73,"line":686},[71,12191,243],{"class":189},[71,12193,205],{"class":77},[71,12195,248],{"class":176},[71,12197,211],{"class":77},[71,12199,253],{"class":189},[71,12201,162],{"class":77},[71,12203,12204],{"class":73,"line":701},[71,12205,261],{"class":77},[71,12207,12208],{"class":73,"line":706},[71,12209,709],{"class":77},[19,12211,41,12212,3494,12214,4609,12217,4612,12219,12221],{},[43,12213,2989],{},[28,12215,3499],{"href":3497,"rel":12216},[32],[43,12218,715],{},[43,12220,719],{},". Sai um PDF de uma página com texto japonês.",[14,12223,12225],{"id":12224},"o-que-essas-três-linhas-fazem-por-baixo","O que essas três linhas fazem por baixo",[19,12227,12228],{},"Duas coisas acontecem nos bastidores, e nenhuma delas exige sua intervenção.",[19,12230,12231,12234],{},[23,12232,12233],{},"Subset embedding."," A Noto Sans JP traz cerca de 17.000 glifos — o peso Regular ocupa uns 5 MB em disco. Se você embutisse a fonte inteira, um recibo com quatro linhas de japonês custaria cinco megabytes em dados tipográficos. O gpdf percorre o texto renderizado, identifica quais IDs de glifo você usou e grava apenas esse subconjunto no PDF. Uma nota curta costuma acabar com 20–40 KB de dados de fonte, não 5 MB.",[19,12236,12237,12238,12240,12241,12243],{},"O gofpdf também sabia fazer subset, mas o ",[43,12239,2539],{}," exigia caminho de arquivo e flag UTF-8, com o carregamento ocorrendo enquanto o cursor se movia; trocar de fonte no meio do documento ficava desconfortável. O gpdf registra a fonte uma vez na construção do documento; dali em diante cada ",[43,12242,2967],{}," só a referencia pelo nome de família. Não há preparação por chamada.",[19,12245,12246,12249,12250,12253,12254,12256],{},[23,12247,12248],{},"Sem CGO."," Isso pesa mais do que parece. Em outros ecossistemas o tratamento de fontes passa por FreeType ou HarfBuzz — o que significa dependência em C, caches de build invalidando diferente, camadas extras nas imagens Docker e cross-compile de macOS para ",[43,12251,12252],{},"linux/arm64"," deixando de ser trivial. O gpdf faz o parsing das tabelas TrueType em Go puro. ",[43,12255,7051],{}," continua produzindo um binário estático. Empacote num container distroless o binário Go e o TTF; é só disso que você precisa.",[14,12258,12260],{"id":12259},"variantes-bold-e-italic","Variantes Bold e Italic",[19,12262,12263,12264,12267,12268,9677],{},"As famílias Noto japonesas entregam um arquivo por peso. Para usar ",[23,12265,12266],{},"negrito",", registre o TTF Bold com o sufixo ",[43,12269,1094],{},[62,12271,12273],{"className":64,"code":12272,"language":66,"meta":67,"style":67},"reg, _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nbold, _ := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", reg),\n    gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 12),\n)\n",[43,12274,12275,12301,12327,12331,12345,12367,12389,12411],{"__ignoreMap":67},[71,12276,12277,12279,12281,12283,12285,12287,12289,12291,12293,12295,12297,12299],{"class":73,"line":74},[71,12278,943],{"class":189},[71,12280,193],{"class":77},[71,12282,976],{"class":189},[71,12284,199],{"class":77},[71,12286,202],{"class":189},[71,12288,205],{"class":77},[71,12290,208],{"class":176},[71,12292,211],{"class":77},[71,12294,214],{"class":77},[71,12296,2989],{"class":217},[71,12298,214],{"class":77},[71,12300,162],{"class":77},[71,12302,12303,12305,12307,12309,12311,12313,12315,12317,12319,12321,12323,12325],{"class":73,"line":85},[71,12304,971],{"class":189},[71,12306,193],{"class":77},[71,12308,976],{"class":189},[71,12310,199],{"class":77},[71,12312,202],{"class":189},[71,12314,205],{"class":77},[71,12316,208],{"class":176},[71,12318,211],{"class":77},[71,12320,214],{"class":77},[71,12322,8807],{"class":217},[71,12324,214],{"class":77},[71,12326,162],{"class":77},[71,12328,12329],{"class":73,"line":92},[71,12330,89],{"emptyLinePlaceholder":88},[71,12332,12333,12335,12337,12339,12341,12343],{"class":73,"line":102},[71,12334,1004],{"class":189},[71,12336,199],{"class":77},[71,12338,277],{"class":189},[71,12340,205],{"class":77},[71,12342,282],{"class":176},[71,12344,285],{"class":77},[71,12346,12347,12349,12351,12353,12355,12357,12359,12361,12363,12365],{"class":73,"line":114},[71,12348,1019],{"class":189},[71,12350,205],{"class":77},[71,12352,356],{"class":176},[71,12354,211],{"class":77},[71,12356,214],{"class":77},[71,12358,3227],{"class":217},[71,12360,214],{"class":77},[71,12362,193],{"class":77},[71,12364,1036],{"class":189},[71,12366,308],{"class":77},[71,12368,12369,12371,12373,12375,12377,12379,12381,12383,12385,12387],{"class":73,"line":124},[71,12370,1019],{"class":189},[71,12372,205],{"class":77},[71,12374,356],{"class":176},[71,12376,211],{"class":77},[71,12378,214],{"class":77},[71,12380,8945],{"class":217},[71,12382,214],{"class":77},[71,12384,193],{"class":77},[71,12386,1060],{"class":189},[71,12388,308],{"class":77},[71,12390,12391,12393,12395,12397,12399,12401,12403,12405,12407,12409],{"class":73,"line":129},[71,12392,1019],{"class":189},[71,12394,205],{"class":77},[71,12396,382],{"class":176},[71,12398,211],{"class":77},[71,12400,214],{"class":77},[71,12402,3227],{"class":217},[71,12404,214],{"class":77},[71,12406,193],{"class":77},[71,12408,3256],{"class":342},[71,12410,308],{"class":77},[71,12412,12413],{"class":73,"line":139},[71,12414,162],{"class":77},[19,12416,12417,12418,12420,12421,12423,12424,1155,12427,12430],{},"Agora ",[43,12419,1098],{}," pega a variante ",[43,12422,1094],{},". Mesma convenção para ",[43,12425,12426],{},"-Italic",[43,12428,12429],{},"-BoldItalic",". Sem registrar a variante, o negrito cai num peso sintetizado — legível na tela, mas não tipograficamente honesto. Em faturas de produção (NFSe, DANFE), registre o peso real.",[14,12432,12434],{"id":12433},"múltiplos-idiomas-cjk-no-mesmo-documento","Múltiplos idiomas CJK no mesmo documento",[19,12436,12437,12438,9677],{},"Pode registrar quantas famílias quiser — o gpdf gerencia cada uma de forma independente. Alterne por texto com ",[43,12439,12440],{},"template.FontFamily(...)",[62,12442,12444],{"className":64,"code":12443,"language":66,"meta":67,"style":67},"jp, _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nsc, _ := os.ReadFile(\"NotoSansSC-Regular.ttf\")\nkr, _ := os.ReadFile(\"NotoSansKR-Regular.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", jp),\n    gpdf.WithFont(\"NotoSansSC\", sc),\n    gpdf.WithFont(\"NotoSansKR\", kr),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 12),\n)\n\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"日本語\")\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"中文\", template.FontFamily(\"NotoSansSC\"))\n    })\n    r.Col(4, func(c *template.ColBuilder) {\n        c.Text(\"한국어\", template.FontFamily(\"NotoSansKR\"))\n    })\n})\n",[43,12445,12446,12473,12501,12529,12533,12547,12570,12594,12618,12640,12644,12648,12672,12702,12721,12725,12755,12790,12794,12824,12859,12863],{"__ignoreMap":67},[71,12447,12448,12451,12453,12455,12457,12459,12461,12463,12465,12467,12469,12471],{"class":73,"line":74},[71,12449,12450],{"class":189},"jp",[71,12452,193],{"class":77},[71,12454,976],{"class":189},[71,12456,199],{"class":77},[71,12458,202],{"class":189},[71,12460,205],{"class":77},[71,12462,208],{"class":176},[71,12464,211],{"class":77},[71,12466,214],{"class":77},[71,12468,2989],{"class":217},[71,12470,214],{"class":77},[71,12472,162],{"class":77},[71,12474,12475,12478,12480,12482,12484,12486,12488,12490,12492,12494,12497,12499],{"class":73,"line":85},[71,12476,12477],{"class":189},"sc",[71,12479,193],{"class":77},[71,12481,976],{"class":189},[71,12483,199],{"class":77},[71,12485,202],{"class":189},[71,12487,205],{"class":77},[71,12489,208],{"class":176},[71,12491,211],{"class":77},[71,12493,214],{"class":77},[71,12495,12496],{"class":217},"NotoSansSC-Regular.ttf",[71,12498,214],{"class":77},[71,12500,162],{"class":77},[71,12502,12503,12506,12508,12510,12512,12514,12516,12518,12520,12522,12525,12527],{"class":73,"line":92},[71,12504,12505],{"class":189},"kr",[71,12507,193],{"class":77},[71,12509,976],{"class":189},[71,12511,199],{"class":77},[71,12513,202],{"class":189},[71,12515,205],{"class":77},[71,12517,208],{"class":176},[71,12519,211],{"class":77},[71,12521,214],{"class":77},[71,12523,12524],{"class":217},"NotoSansKR-Regular.ttf",[71,12526,214],{"class":77},[71,12528,162],{"class":77},[71,12530,12531],{"class":73,"line":102},[71,12532,89],{"emptyLinePlaceholder":88},[71,12534,12535,12537,12539,12541,12543,12545],{"class":73,"line":114},[71,12536,1004],{"class":189},[71,12538,199],{"class":77},[71,12540,277],{"class":189},[71,12542,205],{"class":77},[71,12544,282],{"class":176},[71,12546,285],{"class":77},[71,12548,12549,12551,12553,12555,12557,12559,12561,12563,12565,12568],{"class":73,"line":124},[71,12550,1019],{"class":189},[71,12552,205],{"class":77},[71,12554,356],{"class":176},[71,12556,211],{"class":77},[71,12558,214],{"class":77},[71,12560,3227],{"class":217},[71,12562,214],{"class":77},[71,12564,193],{"class":77},[71,12566,12567],{"class":189}," jp",[71,12569,308],{"class":77},[71,12571,12572,12574,12576,12578,12580,12582,12585,12587,12589,12592],{"class":73,"line":129},[71,12573,1019],{"class":189},[71,12575,205],{"class":77},[71,12577,356],{"class":176},[71,12579,211],{"class":77},[71,12581,214],{"class":77},[71,12583,12584],{"class":217},"NotoSansSC",[71,12586,214],{"class":77},[71,12588,193],{"class":77},[71,12590,12591],{"class":189}," sc",[71,12593,308],{"class":77},[71,12595,12596,12598,12600,12602,12604,12606,12609,12611,12613,12616],{"class":73,"line":139},[71,12597,1019],{"class":189},[71,12599,205],{"class":77},[71,12601,356],{"class":176},[71,12603,211],{"class":77},[71,12605,214],{"class":77},[71,12607,12608],{"class":217},"NotoSansKR",[71,12610,214],{"class":77},[71,12612,193],{"class":77},[71,12614,12615],{"class":189}," kr",[71,12617,308],{"class":77},[71,12619,12620,12622,12624,12626,12628,12630,12632,12634,12636,12638],{"class":73,"line":149},[71,12621,1019],{"class":189},[71,12623,205],{"class":77},[71,12625,382],{"class":176},[71,12627,211],{"class":77},[71,12629,214],{"class":77},[71,12631,3227],{"class":217},[71,12633,214],{"class":77},[71,12635,193],{"class":77},[71,12637,3256],{"class":342},[71,12639,308],{"class":77},[71,12641,12642],{"class":73,"line":159},[71,12643,162],{"class":77},[71,12645,12646],{"class":73,"line":165},[71,12647,89],{"emptyLinePlaceholder":88},[71,12649,12650,12652,12654,12656,12658,12660,12662,12664,12666,12668,12670],{"class":73,"line":170},[71,12651,1913],{"class":189},[71,12653,205],{"class":77},[71,12655,438],{"class":176},[71,12657,441],{"class":77},[71,12659,445],{"class":444},[71,12661,448],{"class":77},[71,12663,451],{"class":81},[71,12665,205],{"class":77},[71,12667,456],{"class":81},[71,12669,459],{"class":77},[71,12671,183],{"class":77},[71,12673,12674,12676,12678,12680,12682,12684,12686,12688,12690,12692,12694,12696,12698,12700],{"class":73,"line":186},[71,12675,3645],{"class":189},[71,12677,205],{"class":77},[71,12679,472],{"class":176},[71,12681,211],{"class":77},[71,12683,5807],{"class":342},[71,12685,193],{"class":77},[71,12687,482],{"class":77},[71,12689,485],{"class":444},[71,12691,448],{"class":77},[71,12693,451],{"class":81},[71,12695,205],{"class":77},[71,12697,494],{"class":81},[71,12699,459],{"class":77},[71,12701,183],{"class":77},[71,12703,12704,12706,12708,12710,12712,12714,12717,12719],{"class":73,"line":224},[71,12705,3676],{"class":189},[71,12707,205],{"class":77},[71,12709,509],{"class":176},[71,12711,211],{"class":77},[71,12713,214],{"class":77},[71,12715,12716],{"class":217},"日本語",[71,12718,214],{"class":77},[71,12720,162],{"class":77},[71,12722,12723],{"class":73,"line":240},[71,12724,578],{"class":77},[71,12726,12727,12729,12731,12733,12735,12737,12739,12741,12743,12745,12747,12749,12751,12753],{"class":73,"line":258},[71,12728,3645],{"class":189},[71,12730,205],{"class":77},[71,12732,472],{"class":176},[71,12734,211],{"class":77},[71,12736,5807],{"class":342},[71,12738,193],{"class":77},[71,12740,482],{"class":77},[71,12742,485],{"class":444},[71,12744,448],{"class":77},[71,12746,451],{"class":81},[71,12748,205],{"class":77},[71,12750,494],{"class":81},[71,12752,459],{"class":77},[71,12754,183],{"class":77},[71,12756,12757,12759,12761,12763,12765,12767,12770,12772,12774,12776,12778,12780,12782,12784,12786,12788],{"class":73,"line":264},[71,12758,3676],{"class":189},[71,12760,205],{"class":77},[71,12762,509],{"class":176},[71,12764,211],{"class":77},[71,12766,214],{"class":77},[71,12768,12769],{"class":217},"中文",[71,12771,214],{"class":77},[71,12773,193],{"class":77},[71,12775,523],{"class":189},[71,12777,205],{"class":77},[71,12779,4975],{"class":176},[71,12781,211],{"class":77},[71,12783,214],{"class":77},[71,12785,12584],{"class":217},[71,12787,214],{"class":77},[71,12789,4998],{"class":77},[71,12791,12792],{"class":73,"line":269},[71,12793,578],{"class":77},[71,12795,12796,12798,12800,12802,12804,12806,12808,12810,12812,12814,12816,12818,12820,12822],{"class":73,"line":288},[71,12797,3645],{"class":189},[71,12799,205],{"class":77},[71,12801,472],{"class":176},[71,12803,211],{"class":77},[71,12805,5807],{"class":342},[71,12807,193],{"class":77},[71,12809,482],{"class":77},[71,12811,485],{"class":444},[71,12813,448],{"class":77},[71,12815,451],{"class":81},[71,12817,205],{"class":77},[71,12819,494],{"class":81},[71,12821,459],{"class":77},[71,12823,183],{"class":77},[71,12825,12826,12828,12830,12832,12834,12836,12839,12841,12843,12845,12847,12849,12851,12853,12855,12857],{"class":73,"line":311},[71,12827,3676],{"class":189},[71,12829,205],{"class":77},[71,12831,509],{"class":176},[71,12833,211],{"class":77},[71,12835,214],{"class":77},[71,12837,12838],{"class":217},"한국어",[71,12840,214],{"class":77},[71,12842,193],{"class":77},[71,12844,523],{"class":189},[71,12846,205],{"class":77},[71,12848,4975],{"class":176},[71,12850,211],{"class":77},[71,12852,214],{"class":77},[71,12854,12608],{"class":217},[71,12856,214],{"class":77},[71,12858,4998],{"class":77},[71,12860,12861],{"class":73,"line":349},[71,12862,578],{"class":77},[71,12864,12865],{"class":73,"line":375},[71,12866,3703],{"class":77},[19,12868,12869,12870,12873],{},"A unificação Han faz com que o japonês e o chinês simplificado compartilhem codepoints Unicode, mas os glifos são desenhados diferente. ",[23,12871,12872],{},"O mesmo codepoint renderiza com formato diferente dependendo da fonte"," — escolher fonte não é estética, é correção. Se você gera documentos para ambos os mercados, precisa das duas famílias registradas.",[14,12875,12877],{"id":12876},"a-armadilha-do-tofu","A armadilha do tofu",[19,12879,12880,12881,12883],{},"Se você escreve japonês mas esquece o ",[43,12882,356],{},", o gpdf cai nas fontes Base-14 do PDF — nenhuma delas cobre o range CJK. Os caracteres saem como retângulos vazios, o que o mundo Unicode chama de \"caixas tofu\":",[62,12885,12888],{"className":12886,"code":12887,"language":928},[926],"□□□□□、□□。\n",[43,12889,12887],{"__ignoreMap":67},[19,12891,12892,12893,12895,12896,3713,12898,3494,12900,205],{},"Se ver isso na saída, a causa é sempre a mesma: não registrou uma fonte CJK, ou está escrevendo numa família que não inclui esses glifos. A correção também: adicione ",[43,12894,356],{}," e use ",[43,12897,382],{},[43,12899,1162],{},[43,12901,2967],{},[14,12903,6537],{"id":6536},[1114,12905,12906,12915,12921],{},[887,12907,12908,12911,12912],{},[28,12909,12910],{"href":2796},"gofpdf foi arquivado. Como migrar para o gpdf."," — o mapa completo se você vem do ",[43,12913,12914],{},"pdf.AddUTF8Font",[887,12916,12917,12920],{},[28,12918,12919],{"href":2868},"Go PDF Library Showdown 2026"," — como o gpdf se compara a gofpdf, gopdf, Maroto e unipdf em CJK",[887,12922,12923,5194,12926,12928],{},[28,12924,5193],{"href":5191,"rel":12925},[32],[43,12927,356],{}," e regras de nomeação de variantes",[14,12930,1242],{"id":1241},[19,12932,12933],{},"O gpdf é uma biblioteca Go para gerar PDFs. Licença MIT, zero dependências externas, suporte CJK nativo.",[62,12935,12936],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,12937,12938],{"__ignoreMap":67},[71,12939,12940,12942,12944],{"class":73,"line":74},[71,12941,66],{"class":81},[71,12943,1259],{"class":217},[71,12945,1262],{"class":217},[19,12947,12948,1269,12951],{},[28,12949,1268],{"href":30,"rel":12950},[32],[28,12952,1274],{"href":1272,"rel":12953},[32],[1276,12955,1278],{},{"title":67,"searchDepth":85,"depth":85,"links":12957},[12958,12959,12960,12961,12962,12963,12964,12965,12966],{"id":16,"depth":85,"text":17},{"id":11688,"depth":85,"text":11689},{"id":59,"depth":85,"text":60},{"id":12224,"depth":85,"text":12225},{"id":12259,"depth":85,"text":12260},{"id":12433,"depth":85,"text":12434},{"id":12876,"depth":85,"text":12877},{"id":6536,"depth":85,"text":6537},{"id":1241,"depth":85,"text":1242},"Passe os bytes do TTF para gpdf.WithFont ao construir o documento. Três linhas, subset embedding automático e sem CGO.",{"name":12969,"totalTime":5237,"tools":12970,"steps":12972},"Incorporar uma fonte TrueType japonesa em um documento gpdf",[1299,12971],"NotoSansJP-Regular.ttf (ou qualquer TTF com suporte a CJK)",[12973,12976,12978,12981],{"name":12974,"text":12975},"Leia os bytes do TTF","No início do programa, use os.ReadFile para carregar NotoSansJP-Regular.ttf em um []byte. Se preferir embutir a fonte no binário, use //go:embed.",{"name":5248,"text":12977},"Passe gpdf.WithFont(\"NotoSansJP\", fontBytes) para gpdf.NewDocument. O nome da família é livre — use o mesmo identificador ao referenciar depois. O gpdf faz o subset dos glifos em tempo de renderização.",{"name":12979,"text":12980},"Defina-a como fonte padrão","Adicione gpdf.WithDefaultFont(\"NotoSansJP\", 12) para que cada c.Text use automaticamente a fonte japonesa sem precisar passar FontFamily.",{"name":12982,"text":12983},"Escreva texto japonês e gere o PDF","Dentro de uma coluna, chame c.Text(\"こんにちは、世界。\"). doc.Generate() retorna []byte, que você grava em disco com os.WriteFile.",{},{"title":1223,"description":12967},"pt/blog/003.embed-japanese-font",[1323,1324,1325],"5j8iEKg_ZlYMYdzk_y9xRmW9AomIXrL_MU4Z5TQSv7o",{"id":12990,"title":12991,"author":12992,"body":12993,"date":11662,"description":14335,"draft":1293,"extension":1294,"howTo":14336,"image":1317,"meta":14353,"navigation":88,"path":835,"seo":14354,"stem":14355,"tags":14356,"updated":1317,"__hash__":14357},"blogPt/pt/blog/004.noto-sans-jp-with-gpdf.md","Como usar Noto Sans JP com gpdf?",{"name":8,"url":9},{"type":11,"value":12994,"toc":14322},[12995,12999,13009,13011,13028,13032,13537,13550,13554,13561,13586,13593,13616,13619,13623,13626,13693,13712,13722,13726,13729,13874,13887,13890,13959,13969,13973,13976,13979,14040,14043,14046,14050,14053,14058,14070,14079,14092,14096,14099,14256,14263,14265,14293,14295,14298,14310,14319],[14,12996,12998],{"id":12997},"a-pergunta-de-outro-jeito","A pergunta, de outro jeito",[19,13000,13001,13002,13005,13006,205],{},"Você quer renderizar texto em japonês num documento do ",[28,13003,33],{"href":30,"rel":13004},[32]," e escolheu Noto Sans JP — a sans-serif gratuita da Google sob licença SIL OFL que cobre toda a faixa JIS. Você já baixou o zip do Google Fonts. O que falta esclarecer são três coisas: ",[23,13007,13008],{},"qual arquivo escolher, quais pesos registrar e qual é a única pegadinha escondida dentro do zip",[14,13010,38],{"id":37},[19,13012,13013,13014,898,13017,13019,13020,13023,13024,13027],{},"Use o TTF ",[23,13015,13016],{},"static",[43,13018,2989],{}," que está dentro da pasta ",[43,13021,13022],{},"static/"," do zip. Não use a variable font na raiz. Passe para ",[43,13025,13026],{},"gpdf.WithFont(\"NotoSansJP\", bytes)"," e defina como fonte padrão. O gpdf faz subset dos ~17.000 glifos e mantém apenas os que você renderizou de fato — uma fatura típica fica com 20–40 KB de dados de fonte no PDF final.",[14,13029,13031],{"id":13030},"exemplo-completo","Exemplo completo",[62,13033,13035],{"className":64,"code":13034,"language":66,"meta":67,"style":67},"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    font, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(gpdf.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", font),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"請求書\", template.FontSize(28), template.Bold())\n            c.Text(\"Noto Sans JP、これで十分。\")\n        })\n    })\n\n    data, err := doc.Generate()\n    if err != nil {\n        log.Fatal(err)\n    }\n    if err := os.WriteFile(\"invoice.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[43,13036,13037,13043,13047,13053,13061,13069,13073,13081,13089,13097,13101,13105,13115,13141,13153,13167,13171,13175,13189,13207,13237,13259,13281,13285,13289,13303,13327,13357,13396,13415,13419,13423,13427,13445,13457,13471,13475,13515,13529,13533],{"__ignoreMap":67},[71,13038,13039,13041],{"class":73,"line":74},[71,13040,78],{"class":77},[71,13042,82],{"class":81},[71,13044,13045],{"class":73,"line":85},[71,13046,89],{"emptyLinePlaceholder":88},[71,13048,13049,13051],{"class":73,"line":92},[71,13050,96],{"class":95},[71,13052,99],{"class":77},[71,13054,13055,13057,13059],{"class":73,"line":102},[71,13056,105],{"class":77},[71,13058,108],{"class":81},[71,13060,111],{"class":77},[71,13062,13063,13065,13067],{"class":73,"line":114},[71,13064,105],{"class":77},[71,13066,119],{"class":81},[71,13068,111],{"class":77},[71,13070,13071],{"class":73,"line":124},[71,13072,89],{"emptyLinePlaceholder":88},[71,13074,13075,13077,13079],{"class":73,"line":129},[71,13076,105],{"class":77},[71,13078,134],{"class":81},[71,13080,111],{"class":77},[71,13082,13083,13085,13087],{"class":73,"line":139},[71,13084,105],{"class":77},[71,13086,144],{"class":81},[71,13088,111],{"class":77},[71,13090,13091,13093,13095],{"class":73,"line":149},[71,13092,105],{"class":77},[71,13094,154],{"class":81},[71,13096,111],{"class":77},[71,13098,13099],{"class":73,"line":159},[71,13100,162],{"class":77},[71,13102,13103],{"class":73,"line":165},[71,13104,89],{"emptyLinePlaceholder":88},[71,13106,13107,13109,13111,13113],{"class":73,"line":170},[71,13108,173],{"class":77},[71,13110,177],{"class":176},[71,13112,180],{"class":77},[71,13114,183],{"class":77},[71,13116,13117,13119,13121,13123,13125,13127,13129,13131,13133,13135,13137,13139],{"class":73,"line":186},[71,13118,190],{"class":189},[71,13120,193],{"class":77},[71,13122,196],{"class":189},[71,13124,199],{"class":77},[71,13126,202],{"class":189},[71,13128,205],{"class":77},[71,13130,208],{"class":176},[71,13132,211],{"class":77},[71,13134,214],{"class":77},[71,13136,2989],{"class":217},[71,13138,214],{"class":77},[71,13140,162],{"class":77},[71,13142,13143,13145,13147,13149,13151],{"class":73,"line":224},[71,13144,227],{"class":95},[71,13146,196],{"class":189},[71,13148,232],{"class":77},[71,13150,235],{"class":77},[71,13152,183],{"class":77},[71,13154,13155,13157,13159,13161,13163,13165],{"class":73,"line":240},[71,13156,243],{"class":189},[71,13158,205],{"class":77},[71,13160,248],{"class":176},[71,13162,211],{"class":77},[71,13164,253],{"class":189},[71,13166,162],{"class":77},[71,13168,13169],{"class":73,"line":258},[71,13170,261],{"class":77},[71,13172,13173],{"class":73,"line":264},[71,13174,89],{"emptyLinePlaceholder":88},[71,13176,13177,13179,13181,13183,13185,13187],{"class":73,"line":269},[71,13178,272],{"class":189},[71,13180,199],{"class":77},[71,13182,277],{"class":189},[71,13184,205],{"class":77},[71,13186,282],{"class":176},[71,13188,285],{"class":77},[71,13190,13191,13193,13195,13197,13199,13201,13203,13205],{"class":73,"line":288},[71,13192,291],{"class":189},[71,13194,205],{"class":77},[71,13196,296],{"class":176},[71,13198,211],{"class":77},[71,13200,33],{"class":189},[71,13202,205],{"class":77},[71,13204,305],{"class":189},[71,13206,308],{"class":77},[71,13208,13209,13211,13213,13215,13217,13219,13221,13223,13225,13227,13229,13231,13233,13235],{"class":73,"line":311},[71,13210,291],{"class":189},[71,13212,205],{"class":77},[71,13214,318],{"class":176},[71,13216,211],{"class":77},[71,13218,323],{"class":189},[71,13220,205],{"class":77},[71,13222,328],{"class":176},[71,13224,211],{"class":77},[71,13226,323],{"class":189},[71,13228,205],{"class":77},[71,13230,337],{"class":176},[71,13232,211],{"class":77},[71,13234,343],{"class":342},[71,13236,346],{"class":77},[71,13238,13239,13241,13243,13245,13247,13249,13251,13253,13255,13257],{"class":73,"line":349},[71,13240,291],{"class":189},[71,13242,205],{"class":77},[71,13244,356],{"class":176},[71,13246,211],{"class":77},[71,13248,214],{"class":77},[71,13250,3227],{"class":217},[71,13252,214],{"class":77},[71,13254,193],{"class":77},[71,13256,370],{"class":189},[71,13258,308],{"class":77},[71,13260,13261,13263,13265,13267,13269,13271,13273,13275,13277,13279],{"class":73,"line":375},[71,13262,291],{"class":189},[71,13264,205],{"class":77},[71,13266,382],{"class":176},[71,13268,211],{"class":77},[71,13270,214],{"class":77},[71,13272,3227],{"class":217},[71,13274,214],{"class":77},[71,13276,193],{"class":77},[71,13278,395],{"class":342},[71,13280,308],{"class":77},[71,13282,13283],{"class":73,"line":400},[71,13284,403],{"class":77},[71,13286,13287],{"class":73,"line":406},[71,13288,89],{"emptyLinePlaceholder":88},[71,13290,13291,13293,13295,13297,13299,13301],{"class":73,"line":411},[71,13292,414],{"class":189},[71,13294,199],{"class":77},[71,13296,419],{"class":189},[71,13298,205],{"class":77},[71,13300,424],{"class":176},[71,13302,427],{"class":77},[71,13304,13305,13307,13309,13311,13313,13315,13317,13319,13321,13323,13325],{"class":73,"line":430},[71,13306,433],{"class":189},[71,13308,205],{"class":77},[71,13310,438],{"class":176},[71,13312,441],{"class":77},[71,13314,445],{"class":444},[71,13316,448],{"class":77},[71,13318,451],{"class":81},[71,13320,205],{"class":77},[71,13322,456],{"class":81},[71,13324,459],{"class":77},[71,13326,183],{"class":77},[71,13328,13329,13331,13333,13335,13337,13339,13341,13343,13345,13347,13349,13351,13353,13355],{"class":73,"line":464},[71,13330,467],{"class":189},[71,13332,205],{"class":77},[71,13334,472],{"class":176},[71,13336,211],{"class":77},[71,13338,477],{"class":342},[71,13340,193],{"class":77},[71,13342,482],{"class":77},[71,13344,485],{"class":444},[71,13346,448],{"class":77},[71,13348,451],{"class":81},[71,13350,205],{"class":77},[71,13352,494],{"class":81},[71,13354,459],{"class":77},[71,13356,183],{"class":77},[71,13358,13359,13361,13363,13365,13367,13369,13371,13373,13375,13377,13379,13381,13383,13386,13388,13390,13392,13394],{"class":73,"line":501},[71,13360,504],{"class":189},[71,13362,205],{"class":77},[71,13364,509],{"class":176},[71,13366,211],{"class":77},[71,13368,214],{"class":77},[71,13370,4409],{"class":217},[71,13372,214],{"class":77},[71,13374,193],{"class":77},[71,13376,523],{"class":189},[71,13378,205],{"class":77},[71,13380,528],{"class":176},[71,13382,211],{"class":77},[71,13384,13385],{"class":342},"28",[71,13387,536],{"class":77},[71,13389,523],{"class":189},[71,13391,205],{"class":77},[71,13393,543],{"class":176},[71,13395,546],{"class":77},[71,13397,13398,13400,13402,13404,13406,13408,13411,13413],{"class":73,"line":549},[71,13399,504],{"class":189},[71,13401,205],{"class":77},[71,13403,509],{"class":176},[71,13405,211],{"class":77},[71,13407,214],{"class":77},[71,13409,13410],{"class":217},"Noto Sans JP、これで十分。",[71,13412,214],{"class":77},[71,13414,162],{"class":77},[71,13416,13417],{"class":73,"line":569},[71,13418,572],{"class":77},[71,13420,13421],{"class":73,"line":575},[71,13422,578],{"class":77},[71,13424,13425],{"class":73,"line":581},[71,13426,89],{"emptyLinePlaceholder":88},[71,13428,13429,13431,13433,13435,13437,13439,13441,13443],{"class":73,"line":586},[71,13430,589],{"class":189},[71,13432,193],{"class":77},[71,13434,196],{"class":189},[71,13436,199],{"class":77},[71,13438,419],{"class":189},[71,13440,205],{"class":77},[71,13442,602],{"class":176},[71,13444,427],{"class":77},[71,13446,13447,13449,13451,13453,13455],{"class":73,"line":607},[71,13448,227],{"class":95},[71,13450,196],{"class":189},[71,13452,232],{"class":77},[71,13454,235],{"class":77},[71,13456,183],{"class":77},[71,13458,13459,13461,13463,13465,13467,13469],{"class":73,"line":620},[71,13460,243],{"class":189},[71,13462,205],{"class":77},[71,13464,248],{"class":176},[71,13466,211],{"class":77},[71,13468,253],{"class":189},[71,13470,162],{"class":77},[71,13472,13473],{"class":73,"line":635},[71,13474,261],{"class":77},[71,13476,13477,13479,13481,13483,13485,13487,13489,13491,13493,13495,13497,13499,13501,13503,13505,13507,13509,13511,13513],{"class":73,"line":640},[71,13478,227],{"class":95},[71,13480,196],{"class":189},[71,13482,199],{"class":77},[71,13484,202],{"class":189},[71,13486,205],{"class":77},[71,13488,653],{"class":176},[71,13490,211],{"class":77},[71,13492,214],{"class":77},[71,13494,4552],{"class":217},[71,13496,214],{"class":77},[71,13498,193],{"class":77},[71,13500,667],{"class":189},[71,13502,193],{"class":77},[71,13504,672],{"class":342},[71,13506,675],{"class":77},[71,13508,196],{"class":189},[71,13510,232],{"class":77},[71,13512,235],{"class":77},[71,13514,183],{"class":77},[71,13516,13517,13519,13521,13523,13525,13527],{"class":73,"line":686},[71,13518,243],{"class":189},[71,13520,205],{"class":77},[71,13522,248],{"class":176},[71,13524,211],{"class":77},[71,13526,253],{"class":189},[71,13528,162],{"class":77},[71,13530,13531],{"class":73,"line":701},[71,13532,261],{"class":77},[71,13534,13535],{"class":73,"line":706},[71,13536,709],{"class":77},[19,13538,13539,13540,4606,13543,4609,13546,4612,13548,205],{},"Baixe o zip de Noto Sans JP no ",[28,13541,3499],{"href":3497,"rel":13542},[32],[43,13544,13545],{},"static/NotoSansJP-Regular.ttf",[43,13547,715],{},[43,13549,719],{},[14,13551,13553],{"id":13552},"pegue-o-ttf-static-não-a-variable-font","Pegue o TTF static, não a variable font",[19,13555,13556,13557,13560],{},"Na página do Google Fonts, clique em ",[23,13558,13559],{},"Get font → Download all"," e descompacte. Dentro do zip há dois grupos que parecem equivalentes e não são:",[1114,13562,13563,13573],{},[887,13564,13565,13568,13569,13572],{},[43,13566,13567],{},"NotoSansJP-VariableFont_wght.ttf"," na raiz — a ",[23,13570,13571],{},"variable font",", ~7 MB, carrega todos os pesos de 100 a 900 num único arquivo",[887,13574,13575,13577,13578,13581,13582,13585],{},[43,13576,13022],{}," — nove TTFs separados, de ",[43,13579,13580],{},"NotoSansJP-Thin.ttf"," a ",[43,13583,13584],{},"NotoSansJP-Black.ttf",", cada um com ~5 MB",[19,13587,13588,205],{},[23,13589,13590,13591],{},"Use os de ",[43,13592,13022],{},[19,13594,13595,13596,1155,13598,13600,13601,855,13604,13607,13608,13611,13612,13615],{},"O parser TrueType do gpdf foi escopado de propósito. Ele lida com contornos de glifos, glifos compostos, ",[43,13597,861],{},[43,13599,736],{}," — as tabelas necessárias para renderizar texto com peso fixo. Mas ele não avalia ",[43,13602,13603],{},"fvar",[43,13605,13606],{},"gvar"," nem ",[43,13609,13610],{},"HVAR",", que são as tabelas OpenType responsáveis por tornar uma variable font realmente variável. Se você passar o ",[43,13613,13614],{},"VariableFont_wght.ttf",", uma de duas: ou o parser dá erro de forma limpa, ou — pior — ele pega os glifos da instância padrão e ignora em silêncio qualquer eixo de peso que você achava estar ajustando.",[19,13617,13618],{},"A matemática do tamanho também joga contra a variable. Ela carrega os contornos de todos os pesos no mesmo arquivo — esse é o propósito do formato. Se você usa só Regular, está pagando por oito pesos que nunca aparecem no PDF. Static Regular tem 5 MB; a variable tem 7 MB. O subsetting reduz os dois, mas o static é uma entrada mais limpa.",[14,13620,13622],{"id":13621},"as-quatro-linhas-que-importam","As quatro linhas que importam",[19,13624,13625],{},"Tudo que interessa está nas opções do construtor:",[62,13627,13629],{"className":64,"code":13628,"language":66,"meta":67,"style":67},"doc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", font),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,13630,13631,13645,13667,13689],{"__ignoreMap":67},[71,13632,13633,13635,13637,13639,13641,13643],{"class":73,"line":74},[71,13634,1004],{"class":189},[71,13636,199],{"class":77},[71,13638,277],{"class":189},[71,13640,205],{"class":77},[71,13642,282],{"class":176},[71,13644,285],{"class":77},[71,13646,13647,13649,13651,13653,13655,13657,13659,13661,13663,13665],{"class":73,"line":85},[71,13648,1019],{"class":189},[71,13650,205],{"class":77},[71,13652,356],{"class":176},[71,13654,211],{"class":77},[71,13656,214],{"class":77},[71,13658,3227],{"class":217},[71,13660,214],{"class":77},[71,13662,193],{"class":77},[71,13664,370],{"class":189},[71,13666,308],{"class":77},[71,13668,13669,13671,13673,13675,13677,13679,13681,13683,13685,13687],{"class":73,"line":92},[71,13670,1019],{"class":189},[71,13672,205],{"class":77},[71,13674,382],{"class":176},[71,13676,211],{"class":77},[71,13678,214],{"class":77},[71,13680,3227],{"class":217},[71,13682,214],{"class":77},[71,13684,193],{"class":77},[71,13686,395],{"class":342},[71,13688,308],{"class":77},[71,13690,13691],{"class":73,"line":102},[71,13692,162],{"class":77},[19,13694,13695,13696,13699,13700,855,13703,3534,13706,13709,13710,205],{},"O nome da família (",[43,13697,13698],{},"\"NotoSansJP\"",") é arbitrário. O gpdf usa só como chave de lookup — não é caminho de arquivo, nem campo lido dos metadados da fonte. Chame de ",[43,13701,13702],{},"\"body\"",[43,13704,13705],{},"\"jp\"",[43,13707,13708],{},"\"Noto\""," se ficar mais natural no código. Só mantenha consistente com o argumento que você passar depois em ",[43,13711,12440],{},[19,13713,13714,13716,13717,3717,13719,13721],{},[43,13715,382],{}," é o que te poupa de escrever ",[43,13718,3716],{},[43,13720,2967],{},". Sem ele, o gpdf cai em Helvetica — que não cobre nenhum codepoint CJK — e você acaba com caixinhas vazias (tofu, □□□) em todo o texto sem família explícita. Uma hora perdida descobrindo por que \"só os títulos saem certos\".",[14,13723,13725],{"id":13724},"quais-pesos-você-realmente-precisa","Quais pesos você realmente precisa?",[19,13727,13728],{},"A maioria de faturas, recibos e relatórios usa dois: Regular e Bold. Registre os dois:",[62,13730,13732],{"className":64,"code":13731,"language":66,"meta":67,"style":67},"reg,  _ := os.ReadFile(\"NotoSansJP-Regular.ttf\")\nbold, _ := os.ReadFile(\"NotoSansJP-Bold.ttf\")\n\ndoc := gpdf.NewDocument(\n    gpdf.WithFont(\"NotoSansJP\", reg),\n    gpdf.WithFont(\"NotoSansJP-Bold\", bold),\n    gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n)\n",[43,13733,13734,13760,13786,13790,13804,13826,13848,13870],{"__ignoreMap":67},[71,13735,13736,13738,13740,13742,13744,13746,13748,13750,13752,13754,13756,13758],{"class":73,"line":74},[71,13737,943],{"class":189},[71,13739,193],{"class":77},[71,13741,948],{"class":189},[71,13743,199],{"class":77},[71,13745,202],{"class":189},[71,13747,205],{"class":77},[71,13749,208],{"class":176},[71,13751,211],{"class":77},[71,13753,214],{"class":77},[71,13755,2989],{"class":217},[71,13757,214],{"class":77},[71,13759,162],{"class":77},[71,13761,13762,13764,13766,13768,13770,13772,13774,13776,13778,13780,13782,13784],{"class":73,"line":85},[71,13763,971],{"class":189},[71,13765,193],{"class":77},[71,13767,976],{"class":189},[71,13769,199],{"class":77},[71,13771,202],{"class":189},[71,13773,205],{"class":77},[71,13775,208],{"class":176},[71,13777,211],{"class":77},[71,13779,214],{"class":77},[71,13781,8807],{"class":217},[71,13783,214],{"class":77},[71,13785,162],{"class":77},[71,13787,13788],{"class":73,"line":92},[71,13789,89],{"emptyLinePlaceholder":88},[71,13791,13792,13794,13796,13798,13800,13802],{"class":73,"line":102},[71,13793,1004],{"class":189},[71,13795,199],{"class":77},[71,13797,277],{"class":189},[71,13799,205],{"class":77},[71,13801,282],{"class":176},[71,13803,285],{"class":77},[71,13805,13806,13808,13810,13812,13814,13816,13818,13820,13822,13824],{"class":73,"line":114},[71,13807,1019],{"class":189},[71,13809,205],{"class":77},[71,13811,356],{"class":176},[71,13813,211],{"class":77},[71,13815,214],{"class":77},[71,13817,3227],{"class":217},[71,13819,214],{"class":77},[71,13821,193],{"class":77},[71,13823,1036],{"class":189},[71,13825,308],{"class":77},[71,13827,13828,13830,13832,13834,13836,13838,13840,13842,13844,13846],{"class":73,"line":124},[71,13829,1019],{"class":189},[71,13831,205],{"class":77},[71,13833,356],{"class":176},[71,13835,211],{"class":77},[71,13837,214],{"class":77},[71,13839,8945],{"class":217},[71,13841,214],{"class":77},[71,13843,193],{"class":77},[71,13845,1060],{"class":189},[71,13847,308],{"class":77},[71,13849,13850,13852,13854,13856,13858,13860,13862,13864,13866,13868],{"class":73,"line":129},[71,13851,1019],{"class":189},[71,13853,205],{"class":77},[71,13855,382],{"class":176},[71,13857,211],{"class":77},[71,13859,214],{"class":77},[71,13861,3227],{"class":217},[71,13863,214],{"class":77},[71,13865,193],{"class":77},[71,13867,395],{"class":342},[71,13869,308],{"class":77},[71,13871,13872],{"class":73,"line":139},[71,13873,162],{"class":77},[19,13875,13876,13877,855,13879,13881,13882,1155,13884,13886],{},"Com o sufixo ",[43,13878,1094],{},[43,13880,1098],{}," pega automaticamente. Mesma regra para ",[43,13883,12426],{},[43,13885,12429],{},", só que Noto Sans JP não vem com itálico — fontes CJK geralmente não publicam variante oblíqua porque o desenho dos glifos não tem inclinação natural. Se o layout pede ênfase itálica num trecho em japonês, use cor, tamanho ou peso no lugar.",[19,13888,13889],{},"Folhetos de marketing às vezes pedem Medium ou SemiBold para citações em destaque. Tudo bem. Registre com qualquer sufixo e referencie pelo nome da família diretamente:",[62,13891,13893],{"className":64,"code":13892,"language":66,"meta":67,"style":67},"gpdf.WithFont(\"NotoSansJP-Medium\", medium)\n// ...\nc.Text(\"見出し\", template.FontFamily(\"NotoSansJP-Medium\"))\n",[43,13894,13895,13919,13924],{"__ignoreMap":67},[71,13896,13897,13899,13901,13903,13905,13907,13910,13912,13914,13917],{"class":73,"line":74},[71,13898,33],{"class":189},[71,13900,205],{"class":77},[71,13902,356],{"class":176},[71,13904,211],{"class":77},[71,13906,214],{"class":77},[71,13908,13909],{"class":217},"NotoSansJP-Medium",[71,13911,214],{"class":77},[71,13913,193],{"class":77},[71,13915,13916],{"class":189}," medium",[71,13918,162],{"class":77},[71,13920,13921],{"class":73,"line":85},[71,13922,13923],{"class":2198},"// ...\n",[71,13925,13926,13928,13930,13932,13934,13936,13939,13941,13943,13945,13947,13949,13951,13953,13955,13957],{"class":73,"line":92},[71,13927,485],{"class":189},[71,13929,205],{"class":77},[71,13931,509],{"class":176},[71,13933,211],{"class":77},[71,13935,214],{"class":77},[71,13937,13938],{"class":217},"見出し",[71,13940,214],{"class":77},[71,13942,193],{"class":77},[71,13944,523],{"class":189},[71,13946,205],{"class":77},[71,13948,4975],{"class":176},[71,13950,211],{"class":77},[71,13952,214],{"class":77},[71,13954,13909],{"class":217},[71,13956,214],{"class":77},[71,13958,4998],{"class":77},[19,13960,13961,13962,868,13964,868,13966,13968],{},"O atalho Bold/Italic por sufixo só se conecta com os nomes literais ",[43,13963,1094],{},[43,13965,12426],{},[43,13967,12429],{},". Qualquer outra coisa vai por nome de família.",[14,13970,13972],{"id":13971},"o-tamanho-depois-do-subsetting","O tamanho depois do subsetting",[19,13974,13975],{},"Noto Sans JP Regular tem ~5 MB em disco. Esse número empurra alguns times a montar CDN separado só para fonte, ou a adicionar pós-processamento para retirar a fonte do PDF. Com o gpdf, nenhum dos dois é necessário.",[19,13977,13978],{},"O que de fato cai no PDF:",[742,13980,13981,13994],{},[745,13982,13983],{},[748,13984,13985,13988,13991],{},[751,13986,13987],{},"Documento",[751,13989,13990],{},"Glifos usados",[751,13992,13993],{},"Dados de fonte no PDF",[759,13995,13996,14007,14018,14029],{},[748,13997,13998,14001,14004],{},[764,13999,14000],{},"Recibo de uma linha (~15 caract.)",[764,14002,14003],{},"~14",[764,14005,14006],{},"~11 KB",[748,14008,14009,14012,14015],{},[764,14010,14011],{},"Fatura típica (~200 caract.)",[764,14013,14014],{},"~80",[764,14016,14017],{},"~28 KB",[748,14019,14020,14023,14026],{},[764,14021,14022],{},"Relatório de 10 páginas (~8.000 caract.)",[764,14024,14025],{},"~900",[764,14027,14028],{},"~180 KB",[748,14030,14031,14034,14037],{},[764,14032,14033],{},"Dump tipo dicionário (JIS Nível 1 completo)",[764,14035,14036],{},"~6.800",[764,14038,14039],{},"~2,1 MB",[19,14041,14042],{},"(gpdf v1.0, subsetting estático ligado. Os números oscilam alguns KB conforme onde os IDs de glifo caem em CFF e hmtx.)",[19,14044,14045],{},"Para uma fatura final de 50 KB, mais da metade é dado de fonte. Mesmo assim é uma fração do que seriam 5 MB incorporados sem subsetting, e o visualizador abre o PDF instantaneamente.",[14,14047,14049],{"id":14048},"noto-sans-jp-vs-noto-sans-cjk-jp-não-confunda","Noto Sans JP vs. Noto Sans CJK JP — não confunda",[19,14051,14052],{},"Existem duas famílias Noto que dizem lidar com japonês e os nomes fazem parecer intercambiáveis. Não são.",[19,14054,14055,14057],{},[23,14056,757],{}," é a que você quer. Distribuída como TTF, um único idioma, um arquivo por peso. É o que sai do Google Fonts.",[19,14059,14060,14063,14064,14066,14067,205],{},[23,14061,14062],{},"Noto Sans CJK JP"," é a super-família pan-CJK. Distribuída como OpenType Collection (",[43,14065,1124],{},"), um único arquivo que contém japonês, chinês simplificado, chinês tradicional e coreano unificados num pacote só. É o que vinha nas primeiras releases do Noto e o que você encontra em ",[43,14068,14069],{},"notofonts.github.io/noto-cjk",[19,14071,14072,14073,14075,14076,14078],{},"O gpdf suporta TTF direto. TTC é um formato container — você precisaria escolher o índice de face correto antes de entregar os bytes ao ",[43,14074,356],{},", e o ",[43,14077,861],{}," dentro de cada face é calibrado para uma locale CJK específica, o que significa estar tomando decisões implícitas sobre unificação Han. É mais claro tomá-las de forma explícita, escolhendo o TTF específico de JP.",[19,14080,14081,14082,14085,14086,3534,14089,14091],{},"Começando hoje? Use Noto Sans JP. Já tem ",[43,14083,14084],{},"NotoSansCJK-Regular.ttc"," num projeto legado? Extraia a face JP com ",[43,14087,14088],{},"pyftsubset",[43,14090,901],{}," e faça commit do TTF resultante como artefato canônico do repositório.",[14,14093,14095],{"id":14094},"empacotando-a-fonte-no-binário","Empacotando a fonte no binário",[19,14097,14098],{},"Geradores de PDF costumam rodar em contêineres, e a forma mais limpa de distribuir a fonte é compilá-la junto:",[62,14100,14102],{"className":64,"code":14101,"language":66,"meta":67,"style":67},"package main\n\nimport (\n    _ \"embed\"\n\n    \"github.com/gpdf-dev/gpdf\"\n)\n\n//go:embed NotoSansJP-Regular.ttf\nvar notoJP []byte\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithFont(\"NotoSansJP\", notoJP),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 11),\n    )\n    // ...\n}\n",[43,14103,14104,14110,14114,14120,14132,14136,14144,14148,14152,14157,14170,14174,14184,14198,14221,14243,14247,14252],{"__ignoreMap":67},[71,14105,14106,14108],{"class":73,"line":74},[71,14107,78],{"class":77},[71,14109,82],{"class":81},[71,14111,14112],{"class":73,"line":85},[71,14113,89],{"emptyLinePlaceholder":88},[71,14115,14116,14118],{"class":73,"line":92},[71,14117,96],{"class":95},[71,14119,99],{"class":77},[71,14121,14122,14125,14127,14130],{"class":73,"line":102},[71,14123,14124],{"class":189},"    _ ",[71,14126,214],{"class":77},[71,14128,14129],{"class":81},"embed",[71,14131,111],{"class":77},[71,14133,14134],{"class":73,"line":114},[71,14135,89],{"emptyLinePlaceholder":88},[71,14137,14138,14140,14142],{"class":73,"line":124},[71,14139,105],{"class":77},[71,14141,134],{"class":81},[71,14143,111],{"class":77},[71,14145,14146],{"class":73,"line":129},[71,14147,162],{"class":77},[71,14149,14150],{"class":73,"line":139},[71,14151,89],{"emptyLinePlaceholder":88},[71,14153,14154],{"class":73,"line":149},[71,14155,14156],{"class":2198},"//go:embed NotoSansJP-Regular.ttf\n",[71,14158,14159,14162,14165,14167],{"class":73,"line":159},[71,14160,14161],{"class":77},"var",[71,14163,14164],{"class":189}," notoJP ",[71,14166,2445],{"class":77},[71,14168,14169],{"class":1607},"byte\n",[71,14171,14172],{"class":73,"line":165},[71,14173,89],{"emptyLinePlaceholder":88},[71,14175,14176,14178,14180,14182],{"class":73,"line":170},[71,14177,173],{"class":77},[71,14179,177],{"class":176},[71,14181,180],{"class":77},[71,14183,183],{"class":77},[71,14185,14186,14188,14190,14192,14194,14196],{"class":73,"line":186},[71,14187,272],{"class":189},[71,14189,199],{"class":77},[71,14191,277],{"class":189},[71,14193,205],{"class":77},[71,14195,282],{"class":176},[71,14197,285],{"class":77},[71,14199,14200,14202,14204,14206,14208,14210,14212,14214,14216,14219],{"class":73,"line":224},[71,14201,291],{"class":189},[71,14203,205],{"class":77},[71,14205,356],{"class":176},[71,14207,211],{"class":77},[71,14209,214],{"class":77},[71,14211,3227],{"class":217},[71,14213,214],{"class":77},[71,14215,193],{"class":77},[71,14217,14218],{"class":189}," notoJP",[71,14220,308],{"class":77},[71,14222,14223,14225,14227,14229,14231,14233,14235,14237,14239,14241],{"class":73,"line":240},[71,14224,291],{"class":189},[71,14226,205],{"class":77},[71,14228,382],{"class":176},[71,14230,211],{"class":77},[71,14232,214],{"class":77},[71,14234,3227],{"class":217},[71,14236,214],{"class":77},[71,14238,193],{"class":77},[71,14240,395],{"class":342},[71,14242,308],{"class":77},[71,14244,14245],{"class":73,"line":258},[71,14246,403],{"class":77},[71,14248,14249],{"class":73,"line":264},[71,14250,14251],{"class":2198},"    // ...\n",[71,14253,14254],{"class":73,"line":269},[71,14255,709],{"class":77},[19,14257,14258,14259,14262],{},"O binário cresce de ~8 MB para ~13 MB. Em troca, a imagem Docker tem um artefato só em vez de dois, ",[43,14260,14261],{},"COPY --from=builder /app /app"," basta, e ninguém consegue subir um contêiner quebrado por ter esquecido o arquivo de fonte. Para um batch que gera milhares de PDFs por dia, esse é o default certo.",[14,14264,6537],{"id":6536},[1114,14266,14267,14272,14280,14285],{},[887,14268,14269,14271],{},[28,14270,1223],{"href":1222}," — a receita geral, vale para qualquer TTF CJK",[887,14273,14274,14277,14278],{},[28,14275,14276],{"href":2796},"gofpdf foi arquivado. Guia de migração para gpdf"," — mapeamento a partir de ",[43,14279,2539],{},[887,14281,14282,14284],{},[28,14283,12919],{"href":2868}," — comparativo específico em CJK",[887,14286,14287,5194,14291],{},[28,14288,5193],{"href":14289,"rel":14290},"https://gpdf.dev/docs/guide/fonts",[32],[43,14292,356],{},[14,14294,1242],{"id":1241},[19,14296,14297],{},"O gpdf é uma biblioteca Go para gerar PDFs. Licença MIT, zero dependências externas, suporte nativo a CJK.",[62,14299,14300],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,14301,14302],{"__ignoreMap":67},[71,14303,14304,14306,14308],{"class":73,"line":74},[71,14305,66],{"class":81},[71,14307,1259],{"class":217},[71,14309,1262],{"class":217},[19,14311,14312,1269,14315],{},[28,14313,1268],{"href":30,"rel":14314},[32],[28,14316,1274],{"href":14317,"rel":14318},"https://gpdf.dev/docs/quickstart",[32],[1276,14320,14321],{},"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 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 .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 .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}",{"title":67,"searchDepth":85,"depth":85,"links":14323},[14324,14325,14326,14327,14328,14329,14330,14331,14332,14333,14334],{"id":12997,"depth":85,"text":12998},{"id":37,"depth":85,"text":38},{"id":13030,"depth":85,"text":13031},{"id":13552,"depth":85,"text":13553},{"id":13621,"depth":85,"text":13622},{"id":13724,"depth":85,"text":13725},{"id":13971,"depth":85,"text":13972},{"id":14048,"depth":85,"text":14049},{"id":14094,"depth":85,"text":14095},{"id":6536,"depth":85,"text":6537},{"id":1241,"depth":85,"text":1242},"Registre o TTF static NotoSansJP-Regular com gpdf.WithFont. Não use a variable font. O gpdf faz subset dos 17.000 glifos para menos de 40 KB por PDF.",{"name":14337,"totalTime":5237,"tools":14338,"steps":14340},"Usar Noto Sans JP como fonte padrão num documento gpdf",[1299,14339],"NotoSansJP-Regular.ttf (TTF static do Google Fonts)",[14341,14344,14347,14350],{"name":14342,"text":14343},"Baixar o TTF static do Google Fonts","Baixe Noto Sans JP em fonts.google.com, descompacte o zip e pegue static/NotoSansJP-Regular.ttf. Não use o NotoSansJP-VariableFont_wght.ttf da raiz do zip.",{"name":14345,"text":14346},"Carregar os bytes na inicialização","Leia NotoSansJP-Regular.ttf com os.ReadFile, ou compile dentro do binário com //go:embed se preferir um único artefato.",{"name":14348,"text":14349},"Registrar a fonte na construção do documento","Passe gpdf.WithFont(\"NotoSansJP\", fontBytes) e gpdf.WithDefaultFont(\"NotoSansJP\", 11) para gpdf.NewDocument. Sem AddUTF8Font, sem caminho de arquivo.",{"name":14351,"text":14352},"Escrever texto em japonês e gerar o PDF","Chame c.Text(\"請求書\") dentro de uma coluna. doc.Generate() retorna []byte e o gpdf faz subset apenas dos glifos que você de fato usou no PDF final.",{},{"title":12991,"description":14335},"pt/blog/004.noto-sans-jp-with-gpdf",[1323,1324,1325],"LNiwZpHZRDgfaa27PprhAxjGJ-utYuhbHpX-x1_Orco",{"id":14359,"title":14360,"author":14361,"body":14362,"date":19110,"description":19111,"draft":1293,"extension":1294,"howTo":19112,"image":1317,"meta":19135,"navigation":88,"path":2796,"seo":19136,"stem":19137,"tags":19138,"updated":1317,"__hash__":19139},"blogPt/pt/blog/001.gofpdf-migration.md","gofpdf foi arquivado. Guia de migração para gpdf.",{"name":8,"url":9},{"type":11,"value":14363,"toc":19094},[14364,14366,14381,14391,14394,14405,14408,14412,14418,14424,14431,14437,14440,14444,14452,14478,14484,14488,14491,14757,14770,14800,14804,14807,14812,14984,14989,15370,15380,15392,15396,15406,15410,16141,16144,16148,16510,16527,16530,16534,16540,16544,16704,16713,16717,17161,17164,17174,17177,17180,17184,17203,17207,17522,17528,17532,18195,18209,18213,18226,18230,18492,18496,18815,18831,18835,18844,18925,18928,18931,18935,18938,18977,18980,18982,18988,19007,19016,19028,19037,19043,19045,19048,19060,19068,19070,19091],[14,14365,38],{"id":37},[19,14367,14368,14370,14371,14373,14374,14376,14377,14380],{},[23,14369,33],{}," é uma biblioteca Go pura, sem dependências externas, com suporte CJK nativo (sem a dança do ",[43,14372,2539],{},"), que usa uma grade de 12 colunas em vez de empurrar pixels com ",[43,14375,1917],{}," e roda aproximadamente ",[23,14378,14379],{},"10× mais rápido que o gofpdf"," nas mesmas cargas. A migração consiste basicamente em substituir chamadas imperativas ao cursor por builders declarativos. Este guia percorre o mapeamento com cinco pares antes/depois.",[19,14382,14383,14384,14387,14388,14390],{},"Semana passada um colega abriu um projeto Go novo, rodou ",[43,14385,14386],{},"go get github.com/jung-kurt/gofpdf"," e dez minutos depois me mandou o screenshot do banner do GitHub: ",[23,14389,6671],{}," Seguido por: \"Espera, o fork também está arquivado?\"",[19,14392,14393],{},"Sim. Os dois.",[19,14395,14396,6643,14398,14401,14402,14404],{},[43,14397,1352],{},[23,14399,14400],{},"8 de setembro de 2021",". O fork comunitário ",[43,14403,1426],{}," publicou seu último release em 2023 e foi arquivado em 2025. A biblioteca Go que dois terços das respostas no Stack Overflow ainda recomendam está há mais de quatro anos em modo somente-leitura, e o fork que deveria substituí-la também já se foi.",[19,14406,14407],{},"Se você tem uma base gofpdf em produção, este post é um mapa de migração. Se está começando um projeto novo e foi puxar gofpdf por reflexo, porque era o que os resultados de busca mostravam, esta é a alternativa.",[14,14409,14411],{"id":14410},"por-que-o-gofpdf-realmente-não-volta","Por que o gofpdf realmente não volta",[19,14413,14414,14415,14417],{},"Bibliotecas open source nem sempre morrem. Às vezes o mantenedor se afasta e alguém assume. Era o que todo mundo assumiu que ia acontecer com o gofpdf — e por um tempo aconteceu. O fork ",[43,14416,1426],{}," reorganizou o código, resolveu alguns bugs antigos, aceitou PRs, parecia uma continuação legítima.",[19,14419,14420,14421],{},"Aí, no início de 2025, o fork também foi arquivado. O README agora diz, em parte: ",[2939,14422,14423],{},"\"Este projeto não é mais mantido ativamente. Considere usar outra biblioteca.\"",[19,14425,14426,14427,14430],{},"A razão importa menos que a consequência: ",[23,14428,14429],{},"todo projeto Go que depende do gofpdf agora está em cima de duas camadas de código sem manutenção",". Questões de segurança não vão ser corrigidas. A spec PDF 2.0 saiu em 2020 e o gofpdf não cobre a maior parte do que mudou. A semântica de variáveis de loop do Go 1.25 ainda funciona com gofpdf hoje, mas o que quebrar amanhã é você que conserta em um fork privado.",[19,14432,14433,14434,205],{},"Não é um problema de \"a biblioteca tem bugs\". É um problema de ",[23,14435,14436],{},"cadeia de suprimentos",[19,14438,14439],{},"Para times no Brasil há um agravante: exigências fiscais (NFe, DANFE, PDF/A para arquivamento) já são difíceis de justificar com uma biblioteca abandonada em auditoria.",[14,14441,14443],{"id":14442},"para-que-o-pessoal-realmente-usa-gofpdf","Para que o pessoal realmente usa gofpdf",[19,14445,14446,14447,1155,14449,14451],{},"Antes de entrar no mapeamento, vale ser específico sobre as cargas que migram. Olhando os trackers de issues e as perguntas de Stack Overflow e dev.to sobre ",[43,14448,1352],{},[43,14450,1426],{},", os usos dominantes são:",[884,14453,14454,14460,14466,14472],{},[887,14455,14456,14459],{},[23,14457,14458],{},"Notas fiscais, recibos, DANFEs"," — cabeçalho, bloco do cliente, tabela de itens, totais, rodapé.",[887,14461,14462,14465],{},[23,14463,14464],{},"Relatórios"," — documentos multipágina com cabeçalhos repetidos, números de página, gráficos inseridos como imagens.",[887,14467,14468,14471],{},[23,14469,14470],{},"Formulários e certificados"," — texto em posições fixas sobre um template.",[887,14473,14474,14477],{},[23,14475,14476],{},"Documentos CJK"," — notas e etiquetas de envio em japonês, chinês, coreano.",[19,14479,14480,14481,14483],{},"Os três primeiros a API de builder do gpdf cobre tranquilamente. O quarto — CJK — é onde o gpdf abre a maior distância em relação ao gofpdf. O gofpdf obrigava você a chamar ",[43,14482,2539],{},", gerenciar um caminho para um TTF, e torcer para o texto não sair do plano básico. O gpdf trata CJK como cidadão de primeira classe: registra uma fonte TrueType, escreve em japonês, sai um PDF.",[14,14485,14487],{"id":14486},"a-tabela-de-mapeamento-da-api","A tabela de mapeamento da API",[19,14489,14490],{},"A tabela a seguir é a colinha. As seções seguintes percorrem cinco pares antes/depois concretos.",[742,14492,14493,14504],{},[745,14494,14495],{},[748,14496,14497,14500,14502],{},[751,14498,14499],{},"O que você quer fazer",[751,14501,1423],{},[751,14503,33],{},[759,14505,14506,14521,14542,14562,14579,14594,14612,14630,14645,14660,14678,14693,14708,14727,14742],{},[748,14507,14508,14511,14516],{},[764,14509,14510],{},"Criar um documento",[764,14512,14513],{},[43,14514,14515],{},"gofpdf.New(\"P\", \"mm\", \"A4\", \"\")",[764,14517,14518],{},[43,14519,14520],{},"gpdf.NewDocument(gpdf.WithPageSize(document.A4))",[748,14522,14523,14526,14531],{},[764,14524,14525],{},"Adicionar uma página",[764,14527,14528],{},[43,14529,14530],{},"pdf.AddPage()",[764,14532,14533,898,14536],{},[43,14534,14535],{},"doc.AddPage()",[2939,14537,14538,14539,459],{},"(retorna um ",[43,14540,14541],{},"*PageBuilder",[748,14543,14544,14547,14552],{},[764,14545,14546],{},"Definir uma fonte",[764,14548,14549],{},[43,14550,14551],{},"pdf.SetFont(\"Arial\", \"B\", 16)",[764,14553,14554,855,14556,855,14558,14561],{},[43,14555,12440],{},[43,14557,1098],{},[43,14559,14560],{},"template.FontSize(16)"," como opções de texto",[748,14563,14564,14567,14572],{},[764,14565,14566],{},"Registrar um TTF (CJK)",[764,14568,14569],{},[43,14570,14571],{},"pdf.AddUTF8Font(\"noto\", \"\", \"NotoSansJP-Regular.ttf\")",[764,14573,14574,898,14576],{},[43,14575,8018],{},[2939,14577,14578],{},"(na construção)",[748,14580,14581,14584,14589],{},[764,14582,14583],{},"Escrever uma linha",[764,14585,14586],{},[43,14587,14588],{},"pdf.Cell(40, 10, \"hi\")",[764,14590,14591],{},[43,14592,14593],{},"c.Text(\"hi\")",[748,14595,14596,14599,14604],{},[764,14597,14598],{},"Escrever texto com quebra",[764,14600,14601],{},[43,14602,14603],{},"pdf.MultiCell(0, 10, body, \"\", \"L\", false)",[764,14605,14606,898,14609],{},[43,14607,14608],{},"c.Text(body)",[2939,14610,14611],{},"(quebra automaticamente)",[748,14613,14614,14617,14622],{},[764,14615,14616],{},"Cor do texto",[764,14618,14619],{},[43,14620,14621],{},"pdf.SetTextColor(255, 0, 0)",[764,14623,14624,898,14627],{},[43,14625,14626],{},"template.TextColor(pdf.Red)",[2939,14628,14629],{},"(opção por texto)",[748,14631,14632,14635,14640],{},[764,14633,14634],{},"Desenhar uma linha horizontal",[764,14636,14637],{},[43,14638,14639],{},"pdf.Line(x1, y1, x2, y2)",[764,14641,14642],{},[43,14643,14644],{},"c.Line(template.LineThickness(document.Pt(1)))",[748,14646,14647,14650,14655],{},[764,14648,14649],{},"Inserir uma imagem",[764,14651,14652],{},[43,14653,14654],{},"pdf.ImageOptions(\"logo.png\", x, y, w, h, ...)",[764,14656,14657],{},[43,14658,14659],{},"c.Image(imgBytes, template.FitWidth(document.Mm(50)))",[748,14661,14662,14665,14670],{},[764,14663,14664],{},"Definir cursor XY",[764,14666,14667],{},[43,14668,14669],{},"pdf.SetXY(x, y)",[764,14671,14672],{},[2939,14673,14674,14675,459],{},"(sem equivalente — use linhas/colunas ou ",[43,14676,14677],{},"page.Absolute(x, y, fn)",[748,14679,14680,14683,14688],{},[764,14681,14682],{},"Cabeçalho repetido",[764,14684,14685],{},[43,14686,14687],{},"pdf.SetHeaderFunc(fn)",[764,14689,14690],{},[43,14691,14692],{},"doc.Header(fn)",[748,14694,14695,14698,14703],{},[764,14696,14697],{},"Rodapé repetido",[764,14699,14700],{},[43,14701,14702],{},"pdf.SetFooterFunc(fn)",[764,14704,14705],{},[43,14706,14707],{},"doc.Footer(fn)",[748,14709,14710,14713,14719],{},[764,14711,14712],{},"Número de página",[764,14714,14715,14716],{},"manual: ",[43,14717,14718],{},"pdf.PageNo()",[764,14720,14721,868,14724],{},[43,14722,14723],{},"c.PageNumber()",[43,14725,14726],{},"c.TotalPages()",[748,14728,14729,14732,14737],{},[764,14730,14731],{},"Saída para arquivo",[764,14733,14734],{},[43,14735,14736],{},"pdf.OutputFileAndClose(\"out.pdf\")",[764,14738,14739],{},[43,14740,14741],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[748,14743,14744,14747,14752],{},[764,14745,14746],{},"Saída para writer",[764,14748,14749],{},[43,14750,14751],{},"pdf.Output(w)",[764,14753,14754],{},[43,14755,14756],{},"doc.Render(w)",[19,14758,14759,14760,14763,14764,14767,14768,205],{},"A mudança de forma é o que pesa mais: gofpdf é ",[23,14761,14762],{},"imperativo",", gpdf é ",[23,14765,14766],{},"declarativo",". No gofpdf você empurra um cursor pela página e escreve onde ele cai. No gpdf você descreve uma árvore de linhas e colunas e deixa o motor de layout posicionar as coisas. Os primeiros trechos parecem mais longos em gpdf. No terceiro você para de sentir falta do ",[43,14769,1917],{},[19,14771,14772,14773,855,14776,855,14779,14782,14783,855,14786,855,14789,855,14792,14795,14796,14799],{},"Uma nota sobre unidades. O gofpdf te faz escolher uma unidade base na construção (",[43,14774,14775],{},"\"mm\"",[43,14777,14778],{},"\"pt\"",[43,14780,14781],{},"\"in\"","). O gpdf usa pontos internamente e oferece helpers — ",[43,14784,14785],{},"document.Mm(20)",[43,14787,14788],{},"document.Pt(12)",[43,14790,14791],{},"document.Cm(1)",[43,14793,14794],{},"document.In(0.5)"," — para a unidade que você preferir no ponto de chamada. É mais próximo de CSS do que do gofpdf, e depois que você coloca um cabeçalho em cada página com margens em ",[43,14797,14798],{},"document.Mm(15)",", para de pensar em unidade.",[14,14801,14803],{"id":14802},"antes-depois-1-o-pdf-mais-simples-possível","Antes / Depois 1: o PDF mais simples possível",[19,14805,14806],{},"O par \"hello world\". A brevidade do gofpdf foi o que o deixou tão citável. A versão gpdf tem algumas linhas a mais porque está construindo uma árvore, não dirigindo um cursor.",[19,14808,14809],{},[23,14810,14811],{},"Antes — gofpdf:",[62,14813,14815],{"className":64,"code":14814,"language":66,"meta":67,"style":67},"package main\n\nimport \"github.com/jung-kurt/gofpdf\"\n\nfunc main() {\n    pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"B\", 24)\n    pdf.Cell(40, 10, \"Hello, World!\")\n    pdf.OutputFileAndClose(\"hello.pdf\")\n}\n",[43,14816,14817,14823,14827,14838,14842,14852,14895,14905,14936,14962,14980],{"__ignoreMap":67},[71,14818,14819,14821],{"class":73,"line":74},[71,14820,78],{"class":77},[71,14822,82],{"class":81},[71,14824,14825],{"class":73,"line":85},[71,14826,89],{"emptyLinePlaceholder":88},[71,14828,14829,14831,14833,14836],{"class":73,"line":92},[71,14830,96],{"class":95},[71,14832,7424],{"class":77},[71,14834,14835],{"class":81},"github.com/jung-kurt/gofpdf",[71,14837,111],{"class":77},[71,14839,14840],{"class":73,"line":102},[71,14841,89],{"emptyLinePlaceholder":88},[71,14843,14844,14846,14848,14850],{"class":73,"line":114},[71,14845,173],{"class":77},[71,14847,177],{"class":176},[71,14849,180],{"class":77},[71,14851,183],{"class":77},[71,14853,14854,14856,14858,14861,14863,14865,14867,14869,14871,14873,14875,14877,14879,14881,14883,14885,14887,14889,14891,14893],{"class":73,"line":124},[71,14855,7400],{"class":189},[71,14857,199],{"class":77},[71,14859,14860],{"class":189}," gofpdf",[71,14862,205],{"class":77},[71,14864,7410],{"class":176},[71,14866,211],{"class":77},[71,14868,214],{"class":77},[71,14870,7417],{"class":217},[71,14872,214],{"class":77},[71,14874,193],{"class":77},[71,14876,7424],{"class":77},[71,14878,7427],{"class":217},[71,14880,214],{"class":77},[71,14882,193],{"class":77},[71,14884,7424],{"class":77},[71,14886,305],{"class":217},[71,14888,214],{"class":77},[71,14890,193],{"class":77},[71,14892,7442],{"class":77},[71,14894,162],{"class":77},[71,14896,14897,14899,14901,14903],{"class":73,"line":129},[71,14898,7449],{"class":189},[71,14900,205],{"class":77},[71,14902,424],{"class":176},[71,14904,427],{"class":77},[71,14906,14907,14909,14911,14913,14915,14917,14919,14921,14923,14925,14927,14929,14931,14934],{"class":73,"line":139},[71,14908,7449],{"class":189},[71,14910,205],{"class":77},[71,14912,1745],{"class":176},[71,14914,211],{"class":77},[71,14916,214],{"class":77},[71,14918,7470],{"class":217},[71,14920,214],{"class":77},[71,14922,193],{"class":77},[71,14924,7424],{"class":77},[71,14926,7479],{"class":217},[71,14928,214],{"class":77},[71,14930,193],{"class":77},[71,14932,14933],{"class":342}," 24",[71,14935,162],{"class":77},[71,14937,14938,14940,14942,14944,14946,14948,14950,14952,14954,14956,14958,14960],{"class":73,"line":149},[71,14939,7449],{"class":189},[71,14941,205],{"class":77},[71,14943,1921],{"class":176},[71,14945,211],{"class":77},[71,14947,7501],{"class":342},[71,14949,193],{"class":77},[71,14951,7506],{"class":342},[71,14953,193],{"class":77},[71,14955,7424],{"class":77},[71,14957,7513],{"class":217},[71,14959,214],{"class":77},[71,14961,162],{"class":77},[71,14963,14964,14966,14968,14970,14972,14974,14976,14978],{"class":73,"line":159},[71,14965,7449],{"class":189},[71,14967,205],{"class":77},[71,14969,1929],{"class":176},[71,14971,211],{"class":77},[71,14973,214],{"class":77},[71,14975,3434],{"class":217},[71,14977,214],{"class":77},[71,14979,162],{"class":77},[71,14981,14982],{"class":73,"line":165},[71,14983,709],{"class":77},[19,14985,14986],{},[23,14987,14988],{},"Depois — gpdf:",[62,14990,14992],{"className":64,"code":14991,"language":66,"meta":67,"style":67},"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",[43,14993,14994,15000,15004,15010,15018,15026,15030,15038,15046,15054,15058,15062,15072,15086,15104,15134,15138,15142,15156,15180,15210,15248,15252,15256,15260,15278,15290,15304,15308,15348,15362,15366],{"__ignoreMap":67},[71,14995,14996,14998],{"class":73,"line":74},[71,14997,78],{"class":77},[71,14999,82],{"class":81},[71,15001,15002],{"class":73,"line":85},[71,15003,89],{"emptyLinePlaceholder":88},[71,15005,15006,15008],{"class":73,"line":92},[71,15007,96],{"class":95},[71,15009,99],{"class":77},[71,15011,15012,15014,15016],{"class":73,"line":102},[71,15013,105],{"class":77},[71,15015,108],{"class":81},[71,15017,111],{"class":77},[71,15019,15020,15022,15024],{"class":73,"line":114},[71,15021,105],{"class":77},[71,15023,119],{"class":81},[71,15025,111],{"class":77},[71,15027,15028],{"class":73,"line":124},[71,15029,89],{"emptyLinePlaceholder":88},[71,15031,15032,15034,15036],{"class":73,"line":129},[71,15033,105],{"class":77},[71,15035,134],{"class":81},[71,15037,111],{"class":77},[71,15039,15040,15042,15044],{"class":73,"line":139},[71,15041,105],{"class":77},[71,15043,144],{"class":81},[71,15045,111],{"class":77},[71,15047,15048,15050,15052],{"class":73,"line":149},[71,15049,105],{"class":77},[71,15051,154],{"class":81},[71,15053,111],{"class":77},[71,15055,15056],{"class":73,"line":159},[71,15057,162],{"class":77},[71,15059,15060],{"class":73,"line":165},[71,15061,89],{"emptyLinePlaceholder":88},[71,15063,15064,15066,15068,15070],{"class":73,"line":170},[71,15065,173],{"class":77},[71,15067,177],{"class":176},[71,15069,180],{"class":77},[71,15071,183],{"class":77},[71,15073,15074,15076,15078,15080,15082,15084],{"class":73,"line":186},[71,15075,272],{"class":189},[71,15077,199],{"class":77},[71,15079,277],{"class":189},[71,15081,205],{"class":77},[71,15083,282],{"class":176},[71,15085,285],{"class":77},[71,15087,15088,15090,15092,15094,15096,15098,15100,15102],{"class":73,"line":224},[71,15089,291],{"class":189},[71,15091,205],{"class":77},[71,15093,296],{"class":176},[71,15095,211],{"class":77},[71,15097,323],{"class":189},[71,15099,205],{"class":77},[71,15101,305],{"class":189},[71,15103,308],{"class":77},[71,15105,15106,15108,15110,15112,15114,15116,15118,15120,15122,15124,15126,15128,15130,15132],{"class":73,"line":240},[71,15107,291],{"class":189},[71,15109,205],{"class":77},[71,15111,318],{"class":176},[71,15113,211],{"class":77},[71,15115,323],{"class":189},[71,15117,205],{"class":77},[71,15119,328],{"class":176},[71,15121,211],{"class":77},[71,15123,323],{"class":189},[71,15125,205],{"class":77},[71,15127,337],{"class":176},[71,15129,211],{"class":77},[71,15131,343],{"class":342},[71,15133,346],{"class":77},[71,15135,15136],{"class":73,"line":258},[71,15137,403],{"class":77},[71,15139,15140],{"class":73,"line":264},[71,15141,89],{"emptyLinePlaceholder":88},[71,15143,15144,15146,15148,15150,15152,15154],{"class":73,"line":269},[71,15145,414],{"class":189},[71,15147,199],{"class":77},[71,15149,419],{"class":189},[71,15151,205],{"class":77},[71,15153,424],{"class":176},[71,15155,427],{"class":77},[71,15157,15158,15160,15162,15164,15166,15168,15170,15172,15174,15176,15178],{"class":73,"line":288},[71,15159,433],{"class":189},[71,15161,205],{"class":77},[71,15163,438],{"class":176},[71,15165,441],{"class":77},[71,15167,445],{"class":444},[71,15169,448],{"class":77},[71,15171,451],{"class":81},[71,15173,205],{"class":77},[71,15175,456],{"class":81},[71,15177,459],{"class":77},[71,15179,183],{"class":77},[71,15181,15182,15184,15186,15188,15190,15192,15194,15196,15198,15200,15202,15204,15206,15208],{"class":73,"line":311},[71,15183,467],{"class":189},[71,15185,205],{"class":77},[71,15187,472],{"class":176},[71,15189,211],{"class":77},[71,15191,477],{"class":342},[71,15193,193],{"class":77},[71,15195,482],{"class":77},[71,15197,485],{"class":444},[71,15199,448],{"class":77},[71,15201,451],{"class":81},[71,15203,205],{"class":77},[71,15205,494],{"class":81},[71,15207,459],{"class":77},[71,15209,183],{"class":77},[71,15211,15212,15214,15216,15218,15220,15222,15224,15226,15228,15230,15232,15234,15236,15238,15240,15242,15244,15246],{"class":73,"line":349},[71,15213,504],{"class":189},[71,15215,205],{"class":77},[71,15217,509],{"class":176},[71,15219,211],{"class":77},[71,15221,214],{"class":77},[71,15223,7513],{"class":217},[71,15225,214],{"class":77},[71,15227,193],{"class":77},[71,15229,523],{"class":189},[71,15231,205],{"class":77},[71,15233,528],{"class":176},[71,15235,211],{"class":77},[71,15237,533],{"class":342},[71,15239,536],{"class":77},[71,15241,523],{"class":189},[71,15243,205],{"class":77},[71,15245,543],{"class":176},[71,15247,546],{"class":77},[71,15249,15250],{"class":73,"line":375},[71,15251,572],{"class":77},[71,15253,15254],{"class":73,"line":400},[71,15255,578],{"class":77},[71,15257,15258],{"class":73,"line":406},[71,15259,89],{"emptyLinePlaceholder":88},[71,15261,15262,15264,15266,15268,15270,15272,15274,15276],{"class":73,"line":411},[71,15263,589],{"class":189},[71,15265,193],{"class":77},[71,15267,196],{"class":189},[71,15269,199],{"class":77},[71,15271,419],{"class":189},[71,15273,205],{"class":77},[71,15275,602],{"class":176},[71,15277,427],{"class":77},[71,15279,15280,15282,15284,15286,15288],{"class":73,"line":430},[71,15281,227],{"class":95},[71,15283,196],{"class":189},[71,15285,232],{"class":77},[71,15287,235],{"class":77},[71,15289,183],{"class":77},[71,15291,15292,15294,15296,15298,15300,15302],{"class":73,"line":464},[71,15293,243],{"class":189},[71,15295,205],{"class":77},[71,15297,248],{"class":176},[71,15299,211],{"class":77},[71,15301,253],{"class":189},[71,15303,162],{"class":77},[71,15305,15306],{"class":73,"line":501},[71,15307,261],{"class":77},[71,15309,15310,15312,15314,15316,15318,15320,15322,15324,15326,15328,15330,15332,15334,15336,15338,15340,15342,15344,15346],{"class":73,"line":549},[71,15311,227],{"class":95},[71,15313,196],{"class":189},[71,15315,199],{"class":77},[71,15317,202],{"class":189},[71,15319,205],{"class":77},[71,15321,653],{"class":176},[71,15323,211],{"class":77},[71,15325,214],{"class":77},[71,15327,3434],{"class":217},[71,15329,214],{"class":77},[71,15331,193],{"class":77},[71,15333,667],{"class":189},[71,15335,193],{"class":77},[71,15337,672],{"class":342},[71,15339,675],{"class":77},[71,15341,196],{"class":189},[71,15343,232],{"class":77},[71,15345,235],{"class":77},[71,15347,183],{"class":77},[71,15349,15350,15352,15354,15356,15358,15360],{"class":73,"line":569},[71,15351,243],{"class":189},[71,15353,205],{"class":77},[71,15355,248],{"class":176},[71,15357,211],{"class":77},[71,15359,253],{"class":189},[71,15361,162],{"class":77},[71,15363,15364],{"class":73,"line":575},[71,15365,261],{"class":77},[71,15367,15368],{"class":73,"line":581},[71,15369,709],{"class":77},[19,15371,15372,15373,15375,15376,15379],{},"A grade faz o trabalho. ",[43,15374,438],{}," adiciona uma linha cuja altura vem do conteúdo; ",[43,15377,15378],{},"r.Col(12, ...)"," diz \"esta coluna ocupa as 12 colunas da grade\". Mesma ideia do Bootstrap aplicada a uma página PDF.",[19,15381,15382,15384,15385,15388,15389,15391],{},[43,15383,3922],{}," devolve os bytes; ",[43,15386,15387],{},"Render(w)"," transmite para um ",[43,15390,1882],{}," se você preferir não alocar. Não existe o passo de \"fechar o arquivo\" porque o gpdf não segura file handle nenhum.",[14,15393,15395],{"id":15394},"antes-depois-2-uma-tabela-de-itens-de-nota","Antes / Depois 2: uma tabela de itens de nota",[19,15397,15398,15399,15401,15402,15405],{},"Tabelas é onde o gofpdf fica verboso. Não tem tabela embutida; você chama ",[43,15400,1921],{}," em loops aninhados, gerencia largura de colunas na mão e usa ",[43,15403,15404],{},"Ln(-1)"," para pular linha. Metade dos tutoriais de nota fiscal com gofpdf na internet é, literalmente, boilerplate de tabela.",[19,15407,15408],{},[23,15409,14811],{},[62,15411,15413],{"className":64,"code":15412,"language":66,"meta":67,"style":67},"pdf.SetFont(\"Arial\", \"B\", 11)\npdf.SetFillColor(220, 220, 220)\npdf.CellFormat(80, 8, \"Descrição\",    \"1\", 0, \"L\", true, 0, \"\")\npdf.CellFormat(20, 8, \"Qtd.\",         \"1\", 0, \"C\", true, 0, \"\")\npdf.CellFormat(30, 8, \"Preço Unit.\",  \"1\", 0, \"R\", true, 0, \"\")\npdf.CellFormat(30, 8, \"Total\",        \"1\", 1, \"R\", true, 0, \"\")\n\npdf.SetFont(\"Arial\", \"\", 11)\nitems := [][]string{\n    {\"Desenvolvimento frontend\", \"40 h\", \"R$ 150,00\", \"R$ 6.000,00\"},\n    {\"Desenvolvimento backend\",  \"60 h\", \"R$ 150,00\", \"R$ 9.000,00\"},\n    {\"Design de UI\",             \"20 h\", \"R$ 120,00\", \"R$ 2.400,00\"},\n}\nfor _, row := range items {\n    pdf.CellFormat(80, 8, row[0], \"1\", 0, \"L\", false, 0, \"\")\n    pdf.CellFormat(20, 8, row[1], \"1\", 0, \"C\", false, 0, \"\")\n    pdf.CellFormat(30, 8, row[2], \"1\", 0, \"R\", false, 0, \"\")\n    pdf.CellFormat(30, 8, row[3], \"1\", 1, \"R\", false, 0, \"\")\n}\n",[43,15414,15415,15445,15470,15535,15595,15655,15715,15719,15745,15759,15798,15835,15873,15877,15900,15961,16019,16078,16137],{"__ignoreMap":67},[71,15416,15417,15419,15421,15423,15425,15427,15429,15431,15433,15435,15437,15439,15441,15443],{"class":73,"line":74},[71,15418,2265],{"class":189},[71,15420,205],{"class":77},[71,15422,1745],{"class":176},[71,15424,211],{"class":77},[71,15426,214],{"class":77},[71,15428,7470],{"class":217},[71,15430,214],{"class":77},[71,15432,193],{"class":77},[71,15434,7424],{"class":77},[71,15436,7479],{"class":217},[71,15438,214],{"class":77},[71,15440,193],{"class":77},[71,15442,395],{"class":342},[71,15444,162],{"class":77},[71,15446,15447,15449,15451,15454,15456,15459,15461,15464,15466,15468],{"class":73,"line":85},[71,15448,2265],{"class":189},[71,15450,205],{"class":77},[71,15452,15453],{"class":176},"SetFillColor",[71,15455,211],{"class":77},[71,15457,15458],{"class":342},"220",[71,15460,193],{"class":77},[71,15462,15463],{"class":342}," 220",[71,15465,193],{"class":77},[71,15467,15463],{"class":342},[71,15469,162],{"class":77},[71,15471,15472,15474,15476,15478,15480,15483,15485,15488,15490,15492,15494,15496,15498,15500,15503,15505,15507,15510,15512,15514,15517,15519,15521,15525,15527,15529,15531,15533],{"class":73,"line":92},[71,15473,2265],{"class":189},[71,15475,205],{"class":77},[71,15477,1995],{"class":176},[71,15479,211],{"class":77},[71,15481,15482],{"class":342},"80",[71,15484,193],{"class":77},[71,15486,15487],{"class":342}," 8",[71,15489,193],{"class":77},[71,15491,7424],{"class":77},[71,15493,11090],{"class":217},[71,15495,214],{"class":77},[71,15497,193],{"class":77},[71,15499,105],{"class":77},[71,15501,15502],{"class":217},"1",[71,15504,214],{"class":77},[71,15506,193],{"class":77},[71,15508,15509],{"class":342}," 0",[71,15511,193],{"class":77},[71,15513,7424],{"class":77},[71,15515,15516],{"class":217},"L",[71,15518,214],{"class":77},[71,15520,193],{"class":77},[71,15522,15524],{"class":15523},"sfNiH"," true",[71,15526,193],{"class":77},[71,15528,15509],{"class":342},[71,15530,193],{"class":77},[71,15532,7442],{"class":77},[71,15534,162],{"class":77},[71,15536,15537,15539,15541,15543,15545,15547,15549,15551,15553,15555,15557,15559,15561,15564,15566,15568,15570,15572,15574,15576,15579,15581,15583,15585,15587,15589,15591,15593],{"class":73,"line":102},[71,15538,2265],{"class":189},[71,15540,205],{"class":77},[71,15542,1995],{"class":176},[71,15544,211],{"class":77},[71,15546,343],{"class":342},[71,15548,193],{"class":77},[71,15550,15487],{"class":342},[71,15552,193],{"class":77},[71,15554,7424],{"class":77},[71,15556,11099],{"class":217},[71,15558,214],{"class":77},[71,15560,193],{"class":77},[71,15562,15563],{"class":77},"         \"",[71,15565,15502],{"class":217},[71,15567,214],{"class":77},[71,15569,193],{"class":77},[71,15571,15509],{"class":342},[71,15573,193],{"class":77},[71,15575,7424],{"class":77},[71,15577,15578],{"class":217},"C",[71,15580,214],{"class":77},[71,15582,193],{"class":77},[71,15584,15524],{"class":15523},[71,15586,193],{"class":77},[71,15588,15509],{"class":342},[71,15590,193],{"class":77},[71,15592,7442],{"class":77},[71,15594,162],{"class":77},[71,15596,15597,15599,15601,15603,15605,15607,15609,15611,15613,15615,15618,15620,15622,15624,15626,15628,15630,15632,15634,15636,15639,15641,15643,15645,15647,15649,15651,15653],{"class":73,"line":114},[71,15598,2265],{"class":189},[71,15600,205],{"class":77},[71,15602,1995],{"class":176},[71,15604,211],{"class":77},[71,15606,6375],{"class":342},[71,15608,193],{"class":77},[71,15610,15487],{"class":342},[71,15612,193],{"class":77},[71,15614,7424],{"class":77},[71,15616,15617],{"class":217},"Preço Unit.",[71,15619,214],{"class":77},[71,15621,193],{"class":77},[71,15623,11189],{"class":77},[71,15625,15502],{"class":217},[71,15627,214],{"class":77},[71,15629,193],{"class":77},[71,15631,15509],{"class":342},[71,15633,193],{"class":77},[71,15635,7424],{"class":77},[71,15637,15638],{"class":217},"R",[71,15640,214],{"class":77},[71,15642,193],{"class":77},[71,15644,15524],{"class":15523},[71,15646,193],{"class":77},[71,15648,15509],{"class":342},[71,15650,193],{"class":77},[71,15652,7442],{"class":77},[71,15654,162],{"class":77},[71,15656,15657,15659,15661,15663,15665,15667,15669,15671,15673,15675,15677,15679,15681,15684,15686,15688,15690,15693,15695,15697,15699,15701,15703,15705,15707,15709,15711,15713],{"class":73,"line":124},[71,15658,2265],{"class":189},[71,15660,205],{"class":77},[71,15662,1995],{"class":176},[71,15664,211],{"class":77},[71,15666,6375],{"class":342},[71,15668,193],{"class":77},[71,15670,15487],{"class":342},[71,15672,193],{"class":77},[71,15674,7424],{"class":77},[71,15676,2648],{"class":217},[71,15678,214],{"class":77},[71,15680,193],{"class":77},[71,15682,15683],{"class":77},"        \"",[71,15685,15502],{"class":217},[71,15687,214],{"class":77},[71,15689,193],{"class":77},[71,15691,15692],{"class":342}," 1",[71,15694,193],{"class":77},[71,15696,7424],{"class":77},[71,15698,15638],{"class":217},[71,15700,214],{"class":77},[71,15702,193],{"class":77},[71,15704,15524],{"class":15523},[71,15706,193],{"class":77},[71,15708,15509],{"class":342},[71,15710,193],{"class":77},[71,15712,7442],{"class":77},[71,15714,162],{"class":77},[71,15716,15717],{"class":73,"line":129},[71,15718,89],{"emptyLinePlaceholder":88},[71,15720,15721,15723,15725,15727,15729,15731,15733,15735,15737,15739,15741,15743],{"class":73,"line":139},[71,15722,2265],{"class":189},[71,15724,205],{"class":77},[71,15726,1745],{"class":176},[71,15728,211],{"class":77},[71,15730,214],{"class":77},[71,15732,7470],{"class":217},[71,15734,214],{"class":77},[71,15736,193],{"class":77},[71,15738,7442],{"class":77},[71,15740,193],{"class":77},[71,15742,395],{"class":342},[71,15744,162],{"class":77},[71,15746,15747,15750,15752,15755,15757],{"class":73,"line":149},[71,15748,15749],{"class":189},"items ",[71,15751,199],{"class":77},[71,15753,15754],{"class":77}," [][]",[71,15756,3891],{"class":1607},[71,15758,11132],{"class":77},[71,15760,15761,15764,15766,15768,15770,15772,15774,15776,15778,15780,15782,15785,15787,15789,15791,15794,15796],{"class":73,"line":159},[71,15762,15763],{"class":77},"    {",[71,15765,214],{"class":77},[71,15767,11142],{"class":217},[71,15769,214],{"class":77},[71,15771,193],{"class":77},[71,15773,7424],{"class":77},[71,15775,11151],{"class":217},[71,15777,214],{"class":77},[71,15779,193],{"class":77},[71,15781,7424],{"class":77},[71,15783,15784],{"class":217},"R$ 150,00",[71,15786,214],{"class":77},[71,15788,193],{"class":77},[71,15790,7424],{"class":77},[71,15792,15793],{"class":217},"R$ 6.000,00",[71,15795,214],{"class":77},[71,15797,11122],{"class":77},[71,15799,15800,15802,15804,15806,15808,15810,15812,15814,15816,15818,15820,15822,15824,15826,15828,15831,15833],{"class":73,"line":165},[71,15801,15763],{"class":77},[71,15803,214],{"class":77},[71,15805,11182],{"class":217},[71,15807,214],{"class":77},[71,15809,193],{"class":77},[71,15811,11189],{"class":77},[71,15813,11192],{"class":217},[71,15815,214],{"class":77},[71,15817,193],{"class":77},[71,15819,7424],{"class":77},[71,15821,15784],{"class":217},[71,15823,214],{"class":77},[71,15825,193],{"class":77},[71,15827,7424],{"class":77},[71,15829,15830],{"class":217},"R$ 9.000,00",[71,15832,214],{"class":77},[71,15834,11122],{"class":77},[71,15836,15837,15839,15841,15843,15845,15847,15849,15851,15853,15855,15857,15860,15862,15864,15866,15869,15871],{"class":73,"line":170},[71,15838,15763],{"class":77},[71,15840,214],{"class":77},[71,15842,11222],{"class":217},[71,15844,214],{"class":77},[71,15846,193],{"class":77},[71,15848,11229],{"class":77},[71,15850,11232],{"class":217},[71,15852,214],{"class":77},[71,15854,193],{"class":77},[71,15856,7424],{"class":77},[71,15858,15859],{"class":217},"R$ 120,00",[71,15861,214],{"class":77},[71,15863,193],{"class":77},[71,15865,7424],{"class":77},[71,15867,15868],{"class":217},"R$ 2.400,00",[71,15870,214],{"class":77},[71,15872,11122],{"class":77},[71,15874,15875],{"class":73,"line":186},[71,15876,709],{"class":77},[71,15878,15879,15882,15885,15887,15890,15892,15895,15898],{"class":73,"line":224},[71,15880,15881],{"class":95},"for",[71,15883,15884],{"class":189}," _",[71,15886,193],{"class":77},[71,15888,15889],{"class":189}," row ",[71,15891,199],{"class":77},[71,15893,15894],{"class":95}," range",[71,15896,15897],{"class":189}," items ",[71,15899,11132],{"class":77},[71,15901,15902,15904,15906,15908,15910,15912,15914,15916,15918,15921,15923,15925,15928,15930,15932,15934,15936,15938,15940,15942,15944,15946,15948,15951,15953,15955,15957,15959],{"class":73,"line":240},[71,15903,7449],{"class":189},[71,15905,205],{"class":77},[71,15907,1995],{"class":176},[71,15909,211],{"class":77},[71,15911,15482],{"class":342},[71,15913,193],{"class":77},[71,15915,15487],{"class":342},[71,15917,193],{"class":77},[71,15919,15920],{"class":189}," row",[71,15922,2174],{"class":77},[71,15924,10187],{"class":342},[71,15926,15927],{"class":77},"],",[71,15929,7424],{"class":77},[71,15931,15502],{"class":217},[71,15933,214],{"class":77},[71,15935,193],{"class":77},[71,15937,15509],{"class":342},[71,15939,193],{"class":77},[71,15941,7424],{"class":77},[71,15943,15516],{"class":217},[71,15945,214],{"class":77},[71,15947,193],{"class":77},[71,15949,15950],{"class":15523}," false",[71,15952,193],{"class":77},[71,15954,15509],{"class":342},[71,15956,193],{"class":77},[71,15958,7442],{"class":77},[71,15960,162],{"class":77},[71,15962,15963,15965,15967,15969,15971,15973,15975,15977,15979,15981,15983,15985,15987,15989,15991,15993,15995,15997,15999,16001,16003,16005,16007,16009,16011,16013,16015,16017],{"class":73,"line":258},[71,15964,7449],{"class":189},[71,15966,205],{"class":77},[71,15968,1995],{"class":176},[71,15970,211],{"class":77},[71,15972,343],{"class":342},[71,15974,193],{"class":77},[71,15976,15487],{"class":342},[71,15978,193],{"class":77},[71,15980,15920],{"class":189},[71,15982,2174],{"class":77},[71,15984,15502],{"class":342},[71,15986,15927],{"class":77},[71,15988,7424],{"class":77},[71,15990,15502],{"class":217},[71,15992,214],{"class":77},[71,15994,193],{"class":77},[71,15996,15509],{"class":342},[71,15998,193],{"class":77},[71,16000,7424],{"class":77},[71,16002,15578],{"class":217},[71,16004,214],{"class":77},[71,16006,193],{"class":77},[71,16008,15950],{"class":15523},[71,16010,193],{"class":77},[71,16012,15509],{"class":342},[71,16014,193],{"class":77},[71,16016,7442],{"class":77},[71,16018,162],{"class":77},[71,16020,16021,16023,16025,16027,16029,16031,16033,16035,16037,16039,16041,16044,16046,16048,16050,16052,16054,16056,16058,16060,16062,16064,16066,16068,16070,16072,16074,16076],{"class":73,"line":264},[71,16022,7449],{"class":189},[71,16024,205],{"class":77},[71,16026,1995],{"class":176},[71,16028,211],{"class":77},[71,16030,6375],{"class":342},[71,16032,193],{"class":77},[71,16034,15487],{"class":342},[71,16036,193],{"class":77},[71,16038,15920],{"class":189},[71,16040,2174],{"class":77},[71,16042,16043],{"class":342},"2",[71,16045,15927],{"class":77},[71,16047,7424],{"class":77},[71,16049,15502],{"class":217},[71,16051,214],{"class":77},[71,16053,193],{"class":77},[71,16055,15509],{"class":342},[71,16057,193],{"class":77},[71,16059,7424],{"class":77},[71,16061,15638],{"class":217},[71,16063,214],{"class":77},[71,16065,193],{"class":77},[71,16067,15950],{"class":15523},[71,16069,193],{"class":77},[71,16071,15509],{"class":342},[71,16073,193],{"class":77},[71,16075,7442],{"class":77},[71,16077,162],{"class":77},[71,16079,16080,16082,16084,16086,16088,16090,16092,16094,16096,16098,16100,16103,16105,16107,16109,16111,16113,16115,16117,16119,16121,16123,16125,16127,16129,16131,16133,16135],{"class":73,"line":269},[71,16081,7449],{"class":189},[71,16083,205],{"class":77},[71,16085,1995],{"class":176},[71,16087,211],{"class":77},[71,16089,6375],{"class":342},[71,16091,193],{"class":77},[71,16093,15487],{"class":342},[71,16095,193],{"class":77},[71,16097,15920],{"class":189},[71,16099,2174],{"class":77},[71,16101,16102],{"class":342},"3",[71,16104,15927],{"class":77},[71,16106,7424],{"class":77},[71,16108,15502],{"class":217},[71,16110,214],{"class":77},[71,16112,193],{"class":77},[71,16114,15692],{"class":342},[71,16116,193],{"class":77},[71,16118,7424],{"class":77},[71,16120,15638],{"class":217},[71,16122,214],{"class":77},[71,16124,193],{"class":77},[71,16126,15950],{"class":15523},[71,16128,193],{"class":77},[71,16130,15509],{"class":342},[71,16132,193],{"class":77},[71,16134,7442],{"class":77},[71,16136,162],{"class":77},[71,16138,16139],{"class":73,"line":288},[71,16140,709],{"class":77},[19,16142,16143],{},"Você calcula as larguras de cabeça, e boa sorte se alguma descrição quebrar.",[19,16145,16146],{},[23,16147,14988],{},[62,16149,16151],{"className":64,"code":16150,"language":66,"meta":67,"style":67},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"Descrição\", \"Qtd.\", \"Preço Unit.\", \"Total\"},\n            [][]string{\n                {\"Desenvolvimento frontend\", \"40 h\", \"R$ 150,00\", \"R$ 6.000,00\"},\n                {\"Desenvolvimento backend\",  \"60 h\", \"R$ 150,00\", \"R$ 9.000,00\"},\n                {\"Design de UI\",             \"20 h\", \"R$ 120,00\", \"R$ 2.400,00\"},\n            },\n            template.ColumnWidths(50, 15, 15, 20),\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",[43,16152,16153,16177,16207,16217,16258,16267,16304,16340,16376,16381,16408,16418,16428,16446,16468,16473,16497,16502,16506],{"__ignoreMap":67},[71,16154,16155,16157,16159,16161,16163,16165,16167,16169,16171,16173,16175],{"class":73,"line":74},[71,16156,1913],{"class":189},[71,16158,205],{"class":77},[71,16160,438],{"class":176},[71,16162,441],{"class":77},[71,16164,445],{"class":444},[71,16166,448],{"class":77},[71,16168,451],{"class":81},[71,16170,205],{"class":77},[71,16172,456],{"class":81},[71,16174,459],{"class":77},[71,16176,183],{"class":77},[71,16178,16179,16181,16183,16185,16187,16189,16191,16193,16195,16197,16199,16201,16203,16205],{"class":73,"line":85},[71,16180,3645],{"class":189},[71,16182,205],{"class":77},[71,16184,472],{"class":176},[71,16186,211],{"class":77},[71,16188,477],{"class":342},[71,16190,193],{"class":77},[71,16192,482],{"class":77},[71,16194,485],{"class":444},[71,16196,448],{"class":77},[71,16198,451],{"class":81},[71,16200,205],{"class":77},[71,16202,494],{"class":81},[71,16204,459],{"class":77},[71,16206,183],{"class":77},[71,16208,16209,16211,16213,16215],{"class":73,"line":92},[71,16210,3676],{"class":189},[71,16212,205],{"class":77},[71,16214,11073],{"class":176},[71,16216,285],{"class":77},[71,16218,16219,16222,16224,16226,16228,16230,16232,16234,16236,16238,16240,16242,16244,16246,16248,16250,16252,16254,16256],{"class":73,"line":102},[71,16220,16221],{"class":77},"            []",[71,16223,3891],{"class":1607},[71,16225,11085],{"class":77},[71,16227,214],{"class":77},[71,16229,11090],{"class":217},[71,16231,214],{"class":77},[71,16233,193],{"class":77},[71,16235,7424],{"class":77},[71,16237,11099],{"class":217},[71,16239,214],{"class":77},[71,16241,193],{"class":77},[71,16243,7424],{"class":77},[71,16245,15617],{"class":217},[71,16247,214],{"class":77},[71,16249,193],{"class":77},[71,16251,7424],{"class":77},[71,16253,2648],{"class":217},[71,16255,214],{"class":77},[71,16257,11122],{"class":77},[71,16259,16260,16263,16265],{"class":73,"line":114},[71,16261,16262],{"class":77},"            [][]",[71,16264,3891],{"class":1607},[71,16266,11132],{"class":77},[71,16268,16269,16272,16274,16276,16278,16280,16282,16284,16286,16288,16290,16292,16294,16296,16298,16300,16302],{"class":73,"line":124},[71,16270,16271],{"class":77},"                {",[71,16273,214],{"class":77},[71,16275,11142],{"class":217},[71,16277,214],{"class":77},[71,16279,193],{"class":77},[71,16281,7424],{"class":77},[71,16283,11151],{"class":217},[71,16285,214],{"class":77},[71,16287,193],{"class":77},[71,16289,7424],{"class":77},[71,16291,15784],{"class":217},[71,16293,214],{"class":77},[71,16295,193],{"class":77},[71,16297,7424],{"class":77},[71,16299,15793],{"class":217},[71,16301,214],{"class":77},[71,16303,11122],{"class":77},[71,16305,16306,16308,16310,16312,16314,16316,16318,16320,16322,16324,16326,16328,16330,16332,16334,16336,16338],{"class":73,"line":129},[71,16307,16271],{"class":77},[71,16309,214],{"class":77},[71,16311,11182],{"class":217},[71,16313,214],{"class":77},[71,16315,193],{"class":77},[71,16317,11189],{"class":77},[71,16319,11192],{"class":217},[71,16321,214],{"class":77},[71,16323,193],{"class":77},[71,16325,7424],{"class":77},[71,16327,15784],{"class":217},[71,16329,214],{"class":77},[71,16331,193],{"class":77},[71,16333,7424],{"class":77},[71,16335,15830],{"class":217},[71,16337,214],{"class":77},[71,16339,11122],{"class":77},[71,16341,16342,16344,16346,16348,16350,16352,16354,16356,16358,16360,16362,16364,16366,16368,16370,16372,16374],{"class":73,"line":139},[71,16343,16271],{"class":77},[71,16345,214],{"class":77},[71,16347,11222],{"class":217},[71,16349,214],{"class":77},[71,16351,193],{"class":77},[71,16353,11229],{"class":77},[71,16355,11232],{"class":217},[71,16357,214],{"class":77},[71,16359,193],{"class":77},[71,16361,7424],{"class":77},[71,16363,15859],{"class":217},[71,16365,214],{"class":77},[71,16367,193],{"class":77},[71,16369,7424],{"class":77},[71,16371,15868],{"class":217},[71,16373,214],{"class":77},[71,16375,11122],{"class":77},[71,16377,16378],{"class":73,"line":149},[71,16379,16380],{"class":77},"            },\n",[71,16382,16383,16386,16388,16390,16392,16394,16396,16398,16400,16402,16404,16406],{"class":73,"line":159},[71,16384,16385],{"class":189},"            template",[71,16387,205],{"class":77},[71,16389,11269],{"class":176},[71,16391,211],{"class":77},[71,16393,11274],{"class":342},[71,16395,193],{"class":77},[71,16397,11279],{"class":342},[71,16399,193],{"class":77},[71,16401,11279],{"class":342},[71,16403,193],{"class":77},[71,16405,11288],{"class":342},[71,16407,308],{"class":77},[71,16409,16410,16412,16414,16416],{"class":73,"line":165},[71,16411,16385],{"class":189},[71,16413,205],{"class":77},[71,16415,11299],{"class":176},[71,16417,285],{"class":77},[71,16419,16420,16422,16424,16426],{"class":73,"line":170},[71,16421,11264],{"class":189},[71,16423,205],{"class":77},[71,16425,543],{"class":176},[71,16427,11313],{"class":77},[71,16429,16430,16432,16434,16436,16438,16440,16442,16444],{"class":73,"line":186},[71,16431,11264],{"class":189},[71,16433,205],{"class":77},[71,16435,11322],{"class":176},[71,16437,211],{"class":77},[71,16439,2265],{"class":189},[71,16441,205],{"class":77},[71,16443,11331],{"class":189},[71,16445,308],{"class":77},[71,16447,16448,16450,16452,16454,16456,16458,16460,16462,16464,16466],{"class":73,"line":224},[71,16449,11264],{"class":189},[71,16451,205],{"class":77},[71,16453,11342],{"class":176},[71,16455,211],{"class":77},[71,16457,2265],{"class":189},[71,16459,205],{"class":77},[71,16461,11351],{"class":176},[71,16463,211],{"class":77},[71,16465,11356],{"class":342},[71,16467,11359],{"class":77},[71,16469,16470],{"class":73,"line":240},[71,16471,16472],{"class":77},"            ),\n",[71,16474,16475,16477,16479,16482,16484,16486,16488,16490,16492,16495],{"class":73,"line":258},[71,16476,16385],{"class":189},[71,16478,205],{"class":77},[71,16480,16481],{"class":176},"TableStripe",[71,16483,211],{"class":77},[71,16485,2265],{"class":189},[71,16487,205],{"class":77},[71,16489,11351],{"class":176},[71,16491,211],{"class":77},[71,16493,16494],{"class":342},"0xF5F5F5",[71,16496,11359],{"class":77},[71,16498,16499],{"class":73,"line":264},[71,16500,16501],{"class":77},"        )\n",[71,16503,16504],{"class":73,"line":269},[71,16505,578],{"class":77},[71,16507,16508],{"class":73,"line":288},[71,16509,3703],{"class":77},[19,16511,16512,16515,16516,16519,16520,16523,16524,16526],{},[43,16513,16514],{},"ColumnWidths(50, 15, 15, 20)"," são ",[23,16517,16518],{},"porcentagens da coluna em que a tabela vive",", não milímetros absolutos. Jogue a mesma tabela dentro de um ",[43,16521,16522],{},"r.Col(6, ...)"," e as mesmas porcentagens continuam funcionando. Esse é o tipo de coisa que não sai do ",[43,16525,1995],{}," sem um wrapper.",[19,16528,16529],{},"Quebra de linha automática. Quebra de página automática — se a tabela passar da margem inferior, o cabeçalho se repete na página seguinte.",[14,16531,16533],{"id":16532},"antes-depois-3-texto-cjk-sem-a-dança-ritual","Antes / Depois 3: texto CJK sem a dança ritual",[19,16535,16536,16537,16539],{},"Esse é o que me fez largar o gofpdf. Para renderizar japonês em gofpdf você chama ",[43,16538,2539],{},", aponta para um TTF no disco, define a fonte e reza. O subsetting funciona na maior parte do tempo. Alguns TTFs disparam colisão de glyph-id e produzem caracteres corrompidos. As mensagens de erro não ajudam.",[19,16541,16542],{},[23,16543,14811],{},[62,16545,16547],{"className":64,"code":16546,"language":66,"meta":67,"style":67},"pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\npdf.AddUTF8Font(\"notosansjp\", \"\", \"NotoSansJP-Regular.ttf\")\npdf.AddPage()\npdf.SetFont(\"notosansjp\", \"\", 14)\npdf.Cell(0, 10, \"こんにちは、世界。\")\npdf.OutputFileAndClose(\"ja.pdf\")\n",[43,16548,16549,16592,16622,16632,16659,16685],{"__ignoreMap":67},[71,16550,16551,16554,16556,16558,16560,16562,16564,16566,16568,16570,16572,16574,16576,16578,16580,16582,16584,16586,16588,16590],{"class":73,"line":74},[71,16552,16553],{"class":189},"pdf ",[71,16555,199],{"class":77},[71,16557,14860],{"class":189},[71,16559,205],{"class":77},[71,16561,7410],{"class":176},[71,16563,211],{"class":77},[71,16565,214],{"class":77},[71,16567,7417],{"class":217},[71,16569,214],{"class":77},[71,16571,193],{"class":77},[71,16573,7424],{"class":77},[71,16575,7427],{"class":217},[71,16577,214],{"class":77},[71,16579,193],{"class":77},[71,16581,7424],{"class":77},[71,16583,305],{"class":217},[71,16585,214],{"class":77},[71,16587,193],{"class":77},[71,16589,7442],{"class":77},[71,16591,162],{"class":77},[71,16593,16594,16596,16598,16600,16602,16604,16606,16608,16610,16612,16614,16616,16618,16620],{"class":73,"line":85},[71,16595,2265],{"class":189},[71,16597,205],{"class":77},[71,16599,2539],{"class":176},[71,16601,211],{"class":77},[71,16603,214],{"class":77},[71,16605,3735],{"class":217},[71,16607,214],{"class":77},[71,16609,193],{"class":77},[71,16611,7442],{"class":77},[71,16613,193],{"class":77},[71,16615,7424],{"class":77},[71,16617,2989],{"class":217},[71,16619,214],{"class":77},[71,16621,162],{"class":77},[71,16623,16624,16626,16628,16630],{"class":73,"line":92},[71,16625,2265],{"class":189},[71,16627,205],{"class":77},[71,16629,424],{"class":176},[71,16631,427],{"class":77},[71,16633,16634,16636,16638,16640,16642,16644,16646,16648,16650,16652,16654,16657],{"class":73,"line":102},[71,16635,2265],{"class":189},[71,16637,205],{"class":77},[71,16639,1745],{"class":176},[71,16641,211],{"class":77},[71,16643,214],{"class":77},[71,16645,3735],{"class":217},[71,16647,214],{"class":77},[71,16649,193],{"class":77},[71,16651,7442],{"class":77},[71,16653,193],{"class":77},[71,16655,16656],{"class":342}," 14",[71,16658,162],{"class":77},[71,16660,16661,16663,16665,16667,16669,16671,16673,16675,16677,16679,16681,16683],{"class":73,"line":114},[71,16662,2265],{"class":189},[71,16664,205],{"class":77},[71,16666,1921],{"class":176},[71,16668,211],{"class":77},[71,16670,10187],{"class":342},[71,16672,193],{"class":77},[71,16674,7506],{"class":342},[71,16676,193],{"class":77},[71,16678,7424],{"class":77},[71,16680,3349],{"class":217},[71,16682,214],{"class":77},[71,16684,162],{"class":77},[71,16686,16687,16689,16691,16693,16695,16697,16700,16702],{"class":73,"line":124},[71,16688,2265],{"class":189},[71,16690,205],{"class":77},[71,16692,1929],{"class":176},[71,16694,211],{"class":77},[71,16696,214],{"class":77},[71,16698,16699],{"class":217},"ja.pdf",[71,16701,214],{"class":77},[71,16703,162],{"class":77},[19,16705,16706,16707,16709,16710,16712],{},"Duas minas. O TTF precisa existir no caminho dado em tempo de execução (então sua imagem Docker tem que carregar a fonte). E ",[43,16708,1921],{}," com largura ",[43,16711,10187],{}," significa \"até a margem direita\", o que em CJK frequentemente corta porque o estimador de largura não conta corretamente os glifos de largura total.",[19,16714,16715],{},[23,16716,14988],{},[62,16718,16720],{"className":64,"code":16719,"language":66,"meta":67,"style":67},"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    fontData, err := os.ReadFile(\"NotoSansJP-Regular.ttf\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansJP\", fontData),\n        gpdf.WithDefaultFont(\"NotoSansJP\", 14),\n    )\n\n    page := doc.AddPage()\n    page.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\n    data, _ := doc.Generate()\n    os.WriteFile(\"ja.pdf\", data, 0o644)\n}\n",[43,16721,16722,16728,16732,16738,16746,16754,16758,16766,16774,16782,16786,16790,16800,16827,16839,16853,16857,16861,16875,16893,16923,16946,16968,16972,16976,16990,17014,17044,17062,17081,17100,17104,17108,17112,17130,17157],{"__ignoreMap":67},[71,16723,16724,16726],{"class":73,"line":74},[71,16725,78],{"class":77},[71,16727,82],{"class":81},[71,16729,16730],{"class":73,"line":85},[71,16731,89],{"emptyLinePlaceholder":88},[71,16733,16734,16736],{"class":73,"line":92},[71,16735,96],{"class":95},[71,16737,99],{"class":77},[71,16739,16740,16742,16744],{"class":73,"line":102},[71,16741,105],{"class":77},[71,16743,108],{"class":81},[71,16745,111],{"class":77},[71,16747,16748,16750,16752],{"class":73,"line":114},[71,16749,105],{"class":77},[71,16751,119],{"class":81},[71,16753,111],{"class":77},[71,16755,16756],{"class":73,"line":124},[71,16757,89],{"emptyLinePlaceholder":88},[71,16759,16760,16762,16764],{"class":73,"line":129},[71,16761,105],{"class":77},[71,16763,134],{"class":81},[71,16765,111],{"class":77},[71,16767,16768,16770,16772],{"class":73,"line":139},[71,16769,105],{"class":77},[71,16771,144],{"class":81},[71,16773,111],{"class":77},[71,16775,16776,16778,16780],{"class":73,"line":149},[71,16777,105],{"class":77},[71,16779,154],{"class":81},[71,16781,111],{"class":77},[71,16783,16784],{"class":73,"line":159},[71,16785,162],{"class":77},[71,16787,16788],{"class":73,"line":165},[71,16789,89],{"emptyLinePlaceholder":88},[71,16791,16792,16794,16796,16798],{"class":73,"line":170},[71,16793,173],{"class":77},[71,16795,177],{"class":176},[71,16797,180],{"class":77},[71,16799,183],{"class":77},[71,16801,16802,16805,16807,16809,16811,16813,16815,16817,16819,16821,16823,16825],{"class":73,"line":186},[71,16803,16804],{"class":189},"    fontData",[71,16806,193],{"class":77},[71,16808,196],{"class":189},[71,16810,199],{"class":77},[71,16812,202],{"class":189},[71,16814,205],{"class":77},[71,16816,208],{"class":176},[71,16818,211],{"class":77},[71,16820,214],{"class":77},[71,16822,2989],{"class":217},[71,16824,214],{"class":77},[71,16826,162],{"class":77},[71,16828,16829,16831,16833,16835,16837],{"class":73,"line":224},[71,16830,227],{"class":95},[71,16832,196],{"class":189},[71,16834,232],{"class":77},[71,16836,235],{"class":77},[71,16838,183],{"class":77},[71,16840,16841,16843,16845,16847,16849,16851],{"class":73,"line":240},[71,16842,243],{"class":189},[71,16844,205],{"class":77},[71,16846,248],{"class":176},[71,16848,211],{"class":77},[71,16850,253],{"class":189},[71,16852,162],{"class":77},[71,16854,16855],{"class":73,"line":258},[71,16856,261],{"class":77},[71,16858,16859],{"class":73,"line":264},[71,16860,89],{"emptyLinePlaceholder":88},[71,16862,16863,16865,16867,16869,16871,16873],{"class":73,"line":269},[71,16864,272],{"class":189},[71,16866,199],{"class":77},[71,16868,277],{"class":189},[71,16870,205],{"class":77},[71,16872,282],{"class":176},[71,16874,285],{"class":77},[71,16876,16877,16879,16881,16883,16885,16887,16889,16891],{"class":73,"line":288},[71,16878,291],{"class":189},[71,16880,205],{"class":77},[71,16882,296],{"class":176},[71,16884,211],{"class":77},[71,16886,323],{"class":189},[71,16888,205],{"class":77},[71,16890,305],{"class":189},[71,16892,308],{"class":77},[71,16894,16895,16897,16899,16901,16903,16905,16907,16909,16911,16913,16915,16917,16919,16921],{"class":73,"line":311},[71,16896,291],{"class":189},[71,16898,205],{"class":77},[71,16900,318],{"class":176},[71,16902,211],{"class":77},[71,16904,323],{"class":189},[71,16906,205],{"class":77},[71,16908,328],{"class":176},[71,16910,211],{"class":77},[71,16912,323],{"class":189},[71,16914,205],{"class":77},[71,16916,337],{"class":176},[71,16918,211],{"class":77},[71,16920,343],{"class":342},[71,16922,346],{"class":77},[71,16924,16925,16927,16929,16931,16933,16935,16937,16939,16941,16944],{"class":73,"line":349},[71,16926,291],{"class":189},[71,16928,205],{"class":77},[71,16930,356],{"class":176},[71,16932,211],{"class":77},[71,16934,214],{"class":77},[71,16936,3227],{"class":217},[71,16938,214],{"class":77},[71,16940,193],{"class":77},[71,16942,16943],{"class":189}," fontData",[71,16945,308],{"class":77},[71,16947,16948,16950,16952,16954,16956,16958,16960,16962,16964,16966],{"class":73,"line":375},[71,16949,291],{"class":189},[71,16951,205],{"class":77},[71,16953,382],{"class":176},[71,16955,211],{"class":77},[71,16957,214],{"class":77},[71,16959,3227],{"class":217},[71,16961,214],{"class":77},[71,16963,193],{"class":77},[71,16965,16656],{"class":342},[71,16967,308],{"class":77},[71,16969,16970],{"class":73,"line":400},[71,16971,403],{"class":77},[71,16973,16974],{"class":73,"line":406},[71,16975,89],{"emptyLinePlaceholder":88},[71,16977,16978,16980,16982,16984,16986,16988],{"class":73,"line":411},[71,16979,414],{"class":189},[71,16981,199],{"class":77},[71,16983,419],{"class":189},[71,16985,205],{"class":77},[71,16987,424],{"class":176},[71,16989,427],{"class":77},[71,16991,16992,16994,16996,16998,17000,17002,17004,17006,17008,17010,17012],{"class":73,"line":430},[71,16993,433],{"class":189},[71,16995,205],{"class":77},[71,16997,438],{"class":176},[71,16999,441],{"class":77},[71,17001,445],{"class":444},[71,17003,448],{"class":77},[71,17005,451],{"class":81},[71,17007,205],{"class":77},[71,17009,456],{"class":81},[71,17011,459],{"class":77},[71,17013,183],{"class":77},[71,17015,17016,17018,17020,17022,17024,17026,17028,17030,17032,17034,17036,17038,17040,17042],{"class":73,"line":464},[71,17017,467],{"class":189},[71,17019,205],{"class":77},[71,17021,472],{"class":176},[71,17023,211],{"class":77},[71,17025,477],{"class":342},[71,17027,193],{"class":77},[71,17029,482],{"class":77},[71,17031,485],{"class":444},[71,17033,448],{"class":77},[71,17035,451],{"class":81},[71,17037,205],{"class":77},[71,17039,494],{"class":81},[71,17041,459],{"class":77},[71,17043,183],{"class":77},[71,17045,17046,17048,17050,17052,17054,17056,17058,17060],{"class":73,"line":501},[71,17047,504],{"class":189},[71,17049,205],{"class":77},[71,17051,509],{"class":176},[71,17053,211],{"class":77},[71,17055,214],{"class":77},[71,17057,3349],{"class":217},[71,17059,214],{"class":77},[71,17061,162],{"class":77},[71,17063,17064,17066,17068,17070,17072,17074,17077,17079],{"class":73,"line":549},[71,17065,504],{"class":189},[71,17067,205],{"class":77},[71,17069,509],{"class":176},[71,17071,211],{"class":77},[71,17073,214],{"class":77},[71,17075,17076],{"class":217},"吾輩は猫である。名前はまだ無い。",[71,17078,214],{"class":77},[71,17080,162],{"class":77},[71,17082,17083,17085,17087,17089,17091,17093,17096,17098],{"class":73,"line":569},[71,17084,504],{"class":189},[71,17086,205],{"class":77},[71,17088,509],{"class":176},[71,17090,211],{"class":77},[71,17092,214],{"class":77},[71,17094,17095],{"class":217},"東京都渋谷区神宮前1-2-3",[71,17097,214],{"class":77},[71,17099,162],{"class":77},[71,17101,17102],{"class":73,"line":575},[71,17103,572],{"class":77},[71,17105,17106],{"class":73,"line":581},[71,17107,578],{"class":77},[71,17109,17110],{"class":73,"line":586},[71,17111,89],{"emptyLinePlaceholder":88},[71,17113,17114,17116,17118,17120,17122,17124,17126,17128],{"class":73,"line":607},[71,17115,589],{"class":189},[71,17117,193],{"class":77},[71,17119,976],{"class":189},[71,17121,199],{"class":77},[71,17123,419],{"class":189},[71,17125,205],{"class":77},[71,17127,602],{"class":176},[71,17129,427],{"class":77},[71,17131,17132,17135,17137,17139,17141,17143,17145,17147,17149,17151,17153,17155],{"class":73,"line":620},[71,17133,17134],{"class":189},"    os",[71,17136,205],{"class":77},[71,17138,653],{"class":176},[71,17140,211],{"class":77},[71,17142,214],{"class":77},[71,17144,16699],{"class":217},[71,17146,214],{"class":77},[71,17148,193],{"class":77},[71,17150,667],{"class":189},[71,17152,193],{"class":77},[71,17154,672],{"class":342},[71,17156,162],{"class":77},[71,17158,17159],{"class":73,"line":635},[71,17160,709],{"class":77},[19,17162,17163],{},"Duas diferenças.",[19,17165,17166,17167,17170,17171,17173],{},"Primeira: você passa ",[23,17168,17169],{},"bytes",", não um caminho. Embute o TTF com ",[43,17172,5052],{}," e seu binário fica autocontido. Nada de \"font not found\" em produção porque alguém esqueceu de montar um volume.",[19,17175,17176],{},"Segunda: o subsetter TrueType do gpdf entende os formatos cmap de CJK (4, 6, 12) e a codificação Identity-H. O PDF de saída carrega apenas os glifos que você realmente usou — embutir NotoSansJP para uma nota de 200 caracteres dá um subset de ~30 KB, não um embed cheio de 4 MB. Se você já viu o gofpdf produzir um PDF de 5 MB para uma página em japonês, essa é a primeira coisa que você nota.",[19,17178,17179],{},"Para um tour mais profundo de opções específicas de CJK — IPAex Gothic, Source Han Sans, cadeias de fallback — veja o post complementar que vem.",[14,17181,17183],{"id":17182},"antes-depois-4-cabeçalho-em-cada-página-número-no-rodapé","Antes / Depois 4: cabeçalho em cada página, número no rodapé",[19,17185,17186,17187,1155,17190,17193,17194,17197,17198,1155,17200,205],{},"O padrão do gofpdf para cromo repetido é ",[43,17188,17189],{},"SetHeaderFunc",[43,17191,17192],{},"SetFooterFunc"," — ambos recebem um ",[43,17195,17196],{},"func()"," que roda contra o cursor atual. Números de página vêm de ",[43,17199,14718],{},[43,17201,17202],{},"pdf.AliasNbPages()",[19,17204,17205],{},[23,17206,14811],{},[62,17208,17210],{"className":64,"code":17209,"language":66,"meta":67,"style":67},"pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\npdf.SetHeaderFunc(func() {\n    pdf.SetFont(\"Arial\", \"B\", 12)\n    pdf.Cell(0, 10, \"ACME Ltda.\")\n    pdf.Ln(15)\n})\npdf.SetFooterFunc(func() {\n    pdf.SetY(-15)\n    pdf.SetFont(\"Arial\", \"I\", 8)\n    pdf.CellFormat(0, 10,\n        fmt.Sprintf(\"Página %d/{nb}\", pdf.PageNo()),\n        \"\", 0, \"C\", false, 0, \"\")\n})\npdf.AliasNbPages(\"\")\npdf.AddPage()\n// ... corpo ...\n",[43,17211,17212,17254,17267,17297,17324,17339,17343,17355,17370,17401,17419,17456,17487,17491,17507,17517],{"__ignoreMap":67},[71,17213,17214,17216,17218,17220,17222,17224,17226,17228,17230,17232,17234,17236,17238,17240,17242,17244,17246,17248,17250,17252],{"class":73,"line":74},[71,17215,16553],{"class":189},[71,17217,199],{"class":77},[71,17219,14860],{"class":189},[71,17221,205],{"class":77},[71,17223,7410],{"class":176},[71,17225,211],{"class":77},[71,17227,214],{"class":77},[71,17229,7417],{"class":217},[71,17231,214],{"class":77},[71,17233,193],{"class":77},[71,17235,7424],{"class":77},[71,17237,7427],{"class":217},[71,17239,214],{"class":77},[71,17241,193],{"class":77},[71,17243,7424],{"class":77},[71,17245,305],{"class":217},[71,17247,214],{"class":77},[71,17249,193],{"class":77},[71,17251,7442],{"class":77},[71,17253,162],{"class":77},[71,17255,17256,17258,17260,17262,17265],{"class":73,"line":85},[71,17257,2265],{"class":189},[71,17259,205],{"class":77},[71,17261,17189],{"class":176},[71,17263,17264],{"class":77},"(func()",[71,17266,183],{"class":77},[71,17268,17269,17271,17273,17275,17277,17279,17281,17283,17285,17287,17289,17291,17293,17295],{"class":73,"line":92},[71,17270,7449],{"class":189},[71,17272,205],{"class":77},[71,17274,1745],{"class":176},[71,17276,211],{"class":77},[71,17278,214],{"class":77},[71,17280,7470],{"class":217},[71,17282,214],{"class":77},[71,17284,193],{"class":77},[71,17286,7424],{"class":77},[71,17288,7479],{"class":217},[71,17290,214],{"class":77},[71,17292,193],{"class":77},[71,17294,3256],{"class":342},[71,17296,162],{"class":77},[71,17298,17299,17301,17303,17305,17307,17309,17311,17313,17315,17317,17320,17322],{"class":73,"line":102},[71,17300,7449],{"class":189},[71,17302,205],{"class":77},[71,17304,1921],{"class":176},[71,17306,211],{"class":77},[71,17308,10187],{"class":342},[71,17310,193],{"class":77},[71,17312,7506],{"class":342},[71,17314,193],{"class":77},[71,17316,7424],{"class":77},[71,17318,17319],{"class":217},"ACME Ltda.",[71,17321,214],{"class":77},[71,17323,162],{"class":77},[71,17325,17326,17328,17330,17333,17335,17337],{"class":73,"line":114},[71,17327,7449],{"class":189},[71,17329,205],{"class":77},[71,17331,17332],{"class":176},"Ln",[71,17334,211],{"class":77},[71,17336,5444],{"class":342},[71,17338,162],{"class":77},[71,17340,17341],{"class":73,"line":124},[71,17342,3703],{"class":77},[71,17344,17345,17347,17349,17351,17353],{"class":73,"line":129},[71,17346,2265],{"class":189},[71,17348,205],{"class":77},[71,17350,17192],{"class":176},[71,17352,17264],{"class":77},[71,17354,183],{"class":77},[71,17356,17357,17359,17361,17363,17366,17368],{"class":73,"line":139},[71,17358,7449],{"class":189},[71,17360,205],{"class":77},[71,17362,8277],{"class":176},[71,17364,17365],{"class":77},"(-",[71,17367,5444],{"class":342},[71,17369,162],{"class":77},[71,17371,17372,17374,17376,17378,17380,17382,17384,17386,17388,17390,17393,17395,17397,17399],{"class":73,"line":149},[71,17373,7449],{"class":189},[71,17375,205],{"class":77},[71,17377,1745],{"class":176},[71,17379,211],{"class":77},[71,17381,214],{"class":77},[71,17383,7470],{"class":217},[71,17385,214],{"class":77},[71,17387,193],{"class":77},[71,17389,7424],{"class":77},[71,17391,17392],{"class":217},"I",[71,17394,214],{"class":77},[71,17396,193],{"class":77},[71,17398,15487],{"class":342},[71,17400,162],{"class":77},[71,17402,17403,17405,17407,17409,17411,17413,17415,17417],{"class":73,"line":159},[71,17404,7449],{"class":189},[71,17406,205],{"class":77},[71,17408,1995],{"class":176},[71,17410,211],{"class":77},[71,17412,10187],{"class":342},[71,17414,193],{"class":77},[71,17416,7506],{"class":342},[71,17418,2046],{"class":77},[71,17420,17421,17424,17426,17429,17431,17433,17436,17439,17442,17444,17446,17448,17450,17453],{"class":73,"line":165},[71,17422,17423],{"class":189},"        fmt",[71,17425,205],{"class":77},[71,17427,17428],{"class":176},"Sprintf",[71,17430,211],{"class":77},[71,17432,214],{"class":77},[71,17434,17435],{"class":217},"Página ",[71,17437,17438],{"class":3833},"%d",[71,17440,17441],{"class":217},"/{nb}",[71,17443,214],{"class":77},[71,17445,193],{"class":77},[71,17447,7568],{"class":189},[71,17449,205],{"class":77},[71,17451,17452],{"class":176},"PageNo",[71,17454,17455],{"class":77},"()),\n",[71,17457,17458,17461,17463,17465,17467,17469,17471,17473,17475,17477,17479,17481,17483,17485],{"class":73,"line":170},[71,17459,17460],{"class":77},"        \"\"",[71,17462,193],{"class":77},[71,17464,15509],{"class":342},[71,17466,193],{"class":77},[71,17468,7424],{"class":77},[71,17470,15578],{"class":217},[71,17472,214],{"class":77},[71,17474,193],{"class":77},[71,17476,15950],{"class":15523},[71,17478,193],{"class":77},[71,17480,15509],{"class":342},[71,17482,193],{"class":77},[71,17484,7442],{"class":77},[71,17486,162],{"class":77},[71,17488,17489],{"class":73,"line":186},[71,17490,3703],{"class":77},[71,17492,17493,17495,17497,17500,17502,17505],{"class":73,"line":224},[71,17494,2265],{"class":189},[71,17496,205],{"class":77},[71,17498,17499],{"class":176},"AliasNbPages",[71,17501,211],{"class":77},[71,17503,17504],{"class":77},"\"\"",[71,17506,162],{"class":77},[71,17508,17509,17511,17513,17515],{"class":73,"line":240},[71,17510,2265],{"class":189},[71,17512,205],{"class":77},[71,17514,424],{"class":176},[71,17516,427],{"class":77},[71,17518,17519],{"class":73,"line":258},[71,17520,17521],{"class":2198},"// ... corpo ...\n",[19,17523,17524,17527],{},[43,17525,17526],{},"{nb}"," é uma sentinela que o gofpdf reescreve na saída com o número total de páginas. Funciona, só faz parte daquelas coisas que você precisa saber.",[19,17529,17530],{},[23,17531,14988],{},[62,17533,17535],{"className":64,"code":17534,"language":66,"meta":67,"style":67},"doc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n)\n\ndoc.Header(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(\"ACME Ltda.\", template.Bold(), template.FontSize(12))\n            c.Line(template.LineColor(pdf.Gray(0.7)))\n            c.Spacer(document.Mm(4))\n        })\n    })\n})\n\ndoc.Footer(func(p *template.PageBuilder) {\n    p.AutoRow(func(r *template.RowBuilder) {\n        r.Col(6, func(c *template.ColBuilder) {\n            c.Text(\"ACME Ltda.\",\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\n            // \"Página X / Y\" — ambas partes são placeholders\n            // resolvidos pelo motor de layout após a paginação.\n            c.PageNumber(template.AlignRight(),\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n    })\n})\n\nfor i := 0; i \u003C 10; i++ {\n    page := doc.AddPage()\n    page.AutoRow(func(r *template.RowBuilder) {\n        r.Col(12, func(c *template.ColBuilder) {\n            c.Text(fmt.Sprintf(\"Conteúdo da página %d.\", i+1))\n        })\n    })\n}\n",[43,17536,17537,17551,17569,17599,17603,17607,17633,17658,17688,17726,17761,17783,17787,17791,17795,17799,17824,17848,17878,17896,17931,17935,17965,17970,17975,17994,18028,18032,18036,18040,18044,18075,18089,18113,18143,18183,18187,18191],{"__ignoreMap":67},[71,17538,17539,17541,17543,17545,17547,17549],{"class":73,"line":74},[71,17540,1004],{"class":189},[71,17542,199],{"class":77},[71,17544,277],{"class":189},[71,17546,205],{"class":77},[71,17548,282],{"class":176},[71,17550,285],{"class":77},[71,17552,17553,17555,17557,17559,17561,17563,17565,17567],{"class":73,"line":85},[71,17554,1019],{"class":189},[71,17556,205],{"class":77},[71,17558,296],{"class":176},[71,17560,211],{"class":77},[71,17562,323],{"class":189},[71,17564,205],{"class":77},[71,17566,305],{"class":189},[71,17568,308],{"class":77},[71,17570,17571,17573,17575,17577,17579,17581,17583,17585,17587,17589,17591,17593,17595,17597],{"class":73,"line":92},[71,17572,1019],{"class":189},[71,17574,205],{"class":77},[71,17576,318],{"class":176},[71,17578,211],{"class":77},[71,17580,323],{"class":189},[71,17582,205],{"class":77},[71,17584,328],{"class":176},[71,17586,211],{"class":77},[71,17588,323],{"class":189},[71,17590,205],{"class":77},[71,17592,337],{"class":176},[71,17594,211],{"class":77},[71,17596,343],{"class":342},[71,17598,346],{"class":77},[71,17600,17601],{"class":73,"line":102},[71,17602,162],{"class":77},[71,17604,17605],{"class":73,"line":114},[71,17606,89],{"emptyLinePlaceholder":88},[71,17608,17609,17612,17614,17616,17618,17620,17622,17624,17626,17629,17631],{"class":73,"line":124},[71,17610,17611],{"class":189},"doc",[71,17613,205],{"class":77},[71,17615,7531],{"class":176},[71,17617,441],{"class":77},[71,17619,19],{"class":444},[71,17621,448],{"class":77},[71,17623,451],{"class":81},[71,17625,205],{"class":77},[71,17627,17628],{"class":81},"PageBuilder",[71,17630,459],{"class":77},[71,17632,183],{"class":77},[71,17634,17635,17638,17640,17642,17644,17646,17648,17650,17652,17654,17656],{"class":73,"line":129},[71,17636,17637],{"class":189},"    p",[71,17639,205],{"class":77},[71,17641,438],{"class":176},[71,17643,441],{"class":77},[71,17645,445],{"class":444},[71,17647,448],{"class":77},[71,17649,451],{"class":81},[71,17651,205],{"class":77},[71,17653,456],{"class":81},[71,17655,459],{"class":77},[71,17657,183],{"class":77},[71,17659,17660,17662,17664,17666,17668,17670,17672,17674,17676,17678,17680,17682,17684,17686],{"class":73,"line":139},[71,17661,467],{"class":189},[71,17663,205],{"class":77},[71,17665,472],{"class":176},[71,17667,211],{"class":77},[71,17669,477],{"class":342},[71,17671,193],{"class":77},[71,17673,482],{"class":77},[71,17675,485],{"class":444},[71,17677,448],{"class":77},[71,17679,451],{"class":81},[71,17681,205],{"class":77},[71,17683,494],{"class":81},[71,17685,459],{"class":77},[71,17687,183],{"class":77},[71,17689,17690,17692,17694,17696,17698,17700,17702,17704,17706,17708,17710,17712,17714,17716,17718,17720,17722,17724],{"class":73,"line":149},[71,17691,504],{"class":189},[71,17693,205],{"class":77},[71,17695,509],{"class":176},[71,17697,211],{"class":77},[71,17699,214],{"class":77},[71,17701,17319],{"class":217},[71,17703,214],{"class":77},[71,17705,193],{"class":77},[71,17707,523],{"class":189},[71,17709,205],{"class":77},[71,17711,543],{"class":176},[71,17713,7612],{"class":77},[71,17715,523],{"class":189},[71,17717,205],{"class":77},[71,17719,528],{"class":176},[71,17721,211],{"class":77},[71,17723,477],{"class":342},[71,17725,4998],{"class":77},[71,17727,17728,17730,17732,17735,17737,17739,17741,17744,17746,17748,17750,17753,17755,17758],{"class":73,"line":159},[71,17729,504],{"class":189},[71,17731,205],{"class":77},[71,17733,17734],{"class":176},"Line",[71,17736,211],{"class":77},[71,17738,451],{"class":189},[71,17740,205],{"class":77},[71,17742,17743],{"class":176},"LineColor",[71,17745,211],{"class":77},[71,17747,2265],{"class":189},[71,17749,205],{"class":77},[71,17751,17752],{"class":176},"Gray",[71,17754,211],{"class":77},[71,17756,17757],{"class":342},"0.7",[71,17759,17760],{"class":77},")))\n",[71,17762,17763,17765,17767,17769,17771,17773,17775,17777,17779,17781],{"class":73,"line":165},[71,17764,504],{"class":189},[71,17766,205],{"class":77},[71,17768,2121],{"class":176},[71,17770,211],{"class":77},[71,17772,323],{"class":189},[71,17774,205],{"class":77},[71,17776,337],{"class":176},[71,17778,211],{"class":77},[71,17780,5807],{"class":342},[71,17782,4998],{"class":77},[71,17784,17785],{"class":73,"line":170},[71,17786,572],{"class":77},[71,17788,17789],{"class":73,"line":186},[71,17790,578],{"class":77},[71,17792,17793],{"class":73,"line":224},[71,17794,3703],{"class":77},[71,17796,17797],{"class":73,"line":240},[71,17798,89],{"emptyLinePlaceholder":88},[71,17800,17801,17803,17805,17808,17810,17812,17814,17816,17818,17820,17822],{"class":73,"line":258},[71,17802,17611],{"class":189},[71,17804,205],{"class":77},[71,17806,17807],{"class":176},"Footer",[71,17809,441],{"class":77},[71,17811,19],{"class":444},[71,17813,448],{"class":77},[71,17815,451],{"class":81},[71,17817,205],{"class":77},[71,17819,17628],{"class":81},[71,17821,459],{"class":77},[71,17823,183],{"class":77},[71,17825,17826,17828,17830,17832,17834,17836,17838,17840,17842,17844,17846],{"class":73,"line":264},[71,17827,17637],{"class":189},[71,17829,205],{"class":77},[71,17831,438],{"class":176},[71,17833,441],{"class":77},[71,17835,445],{"class":444},[71,17837,448],{"class":77},[71,17839,451],{"class":81},[71,17841,205],{"class":77},[71,17843,456],{"class":81},[71,17845,459],{"class":77},[71,17847,183],{"class":77},[71,17849,17850,17852,17854,17856,17858,17860,17862,17864,17866,17868,17870,17872,17874,17876],{"class":73,"line":269},[71,17851,467],{"class":189},[71,17853,205],{"class":77},[71,17855,472],{"class":176},[71,17857,211],{"class":77},[71,17859,5625],{"class":342},[71,17861,193],{"class":77},[71,17863,482],{"class":77},[71,17865,485],{"class":444},[71,17867,448],{"class":77},[71,17869,451],{"class":81},[71,17871,205],{"class":77},[71,17873,494],{"class":81},[71,17875,459],{"class":77},[71,17877,183],{"class":77},[71,17879,17880,17882,17884,17886,17888,17890,17892,17894],{"class":73,"line":288},[71,17881,504],{"class":189},[71,17883,205],{"class":77},[71,17885,509],{"class":176},[71,17887,211],{"class":77},[71,17889,214],{"class":77},[71,17891,17319],{"class":217},[71,17893,214],{"class":77},[71,17895,2046],{"class":77},[71,17897,17898,17900,17902,17904,17906,17908,17910,17912,17914,17916,17918,17920,17922,17924,17926,17929],{"class":73,"line":311},[71,17899,11264],{"class":189},[71,17901,205],{"class":77},[71,17903,528],{"class":176},[71,17905,211],{"class":77},[71,17907,6016],{"class":342},[71,17909,536],{"class":77},[71,17911,523],{"class":189},[71,17913,205],{"class":77},[71,17915,11322],{"class":176},[71,17917,211],{"class":77},[71,17919,2265],{"class":189},[71,17921,205],{"class":77},[71,17923,17752],{"class":176},[71,17925,211],{"class":77},[71,17927,17928],{"class":342},"0.5",[71,17930,17760],{"class":77},[71,17932,17933],{"class":73,"line":349},[71,17934,572],{"class":77},[71,17936,17937,17939,17941,17943,17945,17947,17949,17951,17953,17955,17957,17959,17961,17963],{"class":73,"line":375},[71,17938,467],{"class":189},[71,17940,205],{"class":77},[71,17942,472],{"class":176},[71,17944,211],{"class":77},[71,17946,5625],{"class":342},[71,17948,193],{"class":77},[71,17950,482],{"class":77},[71,17952,485],{"class":444},[71,17954,448],{"class":77},[71,17956,451],{"class":81},[71,17958,205],{"class":77},[71,17960,494],{"class":81},[71,17962,459],{"class":77},[71,17964,183],{"class":77},[71,17966,17967],{"class":73,"line":400},[71,17968,17969],{"class":2198},"            // \"Página X / Y\" — ambas partes são placeholders\n",[71,17971,17972],{"class":73,"line":406},[71,17973,17974],{"class":2198},"            // resolvidos pelo motor de layout após a paginação.\n",[71,17976,17977,17979,17981,17984,17986,17988,17990,17992],{"class":73,"line":411},[71,17978,504],{"class":189},[71,17980,205],{"class":77},[71,17982,17983],{"class":176},"PageNumber",[71,17985,211],{"class":77},[71,17987,451],{"class":189},[71,17989,205],{"class":77},[71,17991,7255],{"class":176},[71,17993,11313],{"class":77},[71,17995,17996,17998,18000,18002,18004,18006,18008,18010,18012,18014,18016,18018,18020,18022,18024,18026],{"class":73,"line":430},[71,17997,11264],{"class":189},[71,17999,205],{"class":77},[71,18001,528],{"class":176},[71,18003,211],{"class":77},[71,18005,6016],{"class":342},[71,18007,536],{"class":77},[71,18009,523],{"class":189},[71,18011,205],{"class":77},[71,18013,11322],{"class":176},[71,18015,211],{"class":77},[71,18017,2265],{"class":189},[71,18019,205],{"class":77},[71,18021,17752],{"class":176},[71,18023,211],{"class":77},[71,18025,17928],{"class":342},[71,18027,17760],{"class":77},[71,18029,18030],{"class":73,"line":464},[71,18031,572],{"class":77},[71,18033,18034],{"class":73,"line":501},[71,18035,578],{"class":77},[71,18037,18038],{"class":73,"line":549},[71,18039,3703],{"class":77},[71,18041,18042],{"class":73,"line":569},[71,18043,89],{"emptyLinePlaceholder":88},[71,18045,18046,18048,18051,18053,18055,18058,18060,18063,18065,18067,18070,18073],{"class":73,"line":575},[71,18047,15881],{"class":95},[71,18049,18050],{"class":189}," i ",[71,18052,199],{"class":77},[71,18054,15509],{"class":342},[71,18056,18057],{"class":77},";",[71,18059,18050],{"class":189},[71,18061,18062],{"class":77},"\u003C",[71,18064,7506],{"class":342},[71,18066,18057],{"class":77},[71,18068,18069],{"class":189}," i",[71,18071,18072],{"class":77},"++",[71,18074,183],{"class":77},[71,18076,18077,18079,18081,18083,18085,18087],{"class":73,"line":581},[71,18078,414],{"class":189},[71,18080,199],{"class":77},[71,18082,419],{"class":189},[71,18084,205],{"class":77},[71,18086,424],{"class":176},[71,18088,427],{"class":77},[71,18090,18091,18093,18095,18097,18099,18101,18103,18105,18107,18109,18111],{"class":73,"line":586},[71,18092,433],{"class":189},[71,18094,205],{"class":77},[71,18096,438],{"class":176},[71,18098,441],{"class":77},[71,18100,445],{"class":444},[71,18102,448],{"class":77},[71,18104,451],{"class":81},[71,18106,205],{"class":77},[71,18108,456],{"class":81},[71,18110,459],{"class":77},[71,18112,183],{"class":77},[71,18114,18115,18117,18119,18121,18123,18125,18127,18129,18131,18133,18135,18137,18139,18141],{"class":73,"line":607},[71,18116,467],{"class":189},[71,18118,205],{"class":77},[71,18120,472],{"class":176},[71,18122,211],{"class":77},[71,18124,477],{"class":342},[71,18126,193],{"class":77},[71,18128,482],{"class":77},[71,18130,485],{"class":444},[71,18132,448],{"class":77},[71,18134,451],{"class":81},[71,18136,205],{"class":77},[71,18138,494],{"class":81},[71,18140,459],{"class":77},[71,18142,183],{"class":77},[71,18144,18145,18147,18149,18151,18153,18155,18157,18159,18161,18163,18166,18168,18170,18172,18174,18176,18179,18181],{"class":73,"line":620},[71,18146,504],{"class":189},[71,18148,205],{"class":77},[71,18150,509],{"class":176},[71,18152,211],{"class":77},[71,18154,3821],{"class":189},[71,18156,205],{"class":77},[71,18158,17428],{"class":176},[71,18160,211],{"class":77},[71,18162,214],{"class":77},[71,18164,18165],{"class":217},"Conteúdo da página ",[71,18167,17438],{"class":3833},[71,18169,205],{"class":217},[71,18171,214],{"class":77},[71,18173,193],{"class":77},[71,18175,18069],{"class":189},[71,18177,18178],{"class":77},"+",[71,18180,15502],{"class":342},[71,18182,4998],{"class":77},[71,18184,18185],{"class":73,"line":635},[71,18186,572],{"class":77},[71,18188,18189],{"class":73,"line":640},[71,18190,578],{"class":77},[71,18192,18193],{"class":73,"line":686},[71,18194,709],{"class":77},[19,18196,18197,1155,18199,18202,18203,9895,18205,18208],{},[43,18198,17983],{},[43,18200,18201],{},"TotalPages"," são placeholders. Eles se expandem após a paginação, quando o motor de layout já sabe quantas páginas existem. Sem sentinela ",[43,18204,17526],{},[43,18206,18207],{},"SetY(-15)"," para cravar o rodapé embaixo — o rodapé é só uma árvore, e o motor reserva espaço para ele em cada página automaticamente.",[14,18210,18212],{"id":18211},"antes-depois-5-produzir-bytes-para-um-handler-http","Antes / Depois 5: produzir bytes para um handler HTTP",[19,18214,18215,18216,18218,18219,18222,18223,18225],{},"A maior parte do código gofpdf em produção não escreve em arquivo. Escreve num ",[43,18217,1882],{}," — geralmente um ",[43,18220,18221],{},"http.ResponseWriter"," retornando ",[43,18224,7553],{}," para o navegador. Este é o par onde a API do gpdf mais se parece com a do gofpdf.",[19,18227,18228],{},[23,18229,14811],{},[62,18231,18233],{"className":64,"code":18232,"language":66,"meta":67,"style":67},"func handler(w http.ResponseWriter, r *http.Request) {\n    pdf := gofpdf.New(\"P\", \"mm\", \"A4\", \"\")\n    pdf.AddPage()\n    pdf.SetFont(\"Arial\", \"\", 12)\n    pdf.Cell(0, 10, \"Gerado em \"+time.Now().Format(time.RFC3339))\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := pdf.Output(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[43,18234,18235,18267,18309,18319,18345,18396,18400,18430,18458,18484,18488],{"__ignoreMap":67},[71,18236,18237,18239,18241,18243,18245,18247,18249,18251,18253,18255,18257,18259,18261,18263,18265],{"class":73,"line":74},[71,18238,173],{"class":77},[71,18240,7364],{"class":176},[71,18242,211],{"class":77},[71,18244,1861],{"class":444},[71,18246,7371],{"class":81},[71,18248,205],{"class":77},[71,18250,7376],{"class":81},[71,18252,193],{"class":77},[71,18254,7381],{"class":444},[71,18256,448],{"class":77},[71,18258,7386],{"class":81},[71,18260,205],{"class":77},[71,18262,7391],{"class":81},[71,18264,459],{"class":77},[71,18266,183],{"class":77},[71,18268,18269,18271,18273,18275,18277,18279,18281,18283,18285,18287,18289,18291,18293,18295,18297,18299,18301,18303,18305,18307],{"class":73,"line":85},[71,18270,7400],{"class":189},[71,18272,199],{"class":77},[71,18274,14860],{"class":189},[71,18276,205],{"class":77},[71,18278,7410],{"class":176},[71,18280,211],{"class":77},[71,18282,214],{"class":77},[71,18284,7417],{"class":217},[71,18286,214],{"class":77},[71,18288,193],{"class":77},[71,18290,7424],{"class":77},[71,18292,7427],{"class":217},[71,18294,214],{"class":77},[71,18296,193],{"class":77},[71,18298,7424],{"class":77},[71,18300,305],{"class":217},[71,18302,214],{"class":77},[71,18304,193],{"class":77},[71,18306,7442],{"class":77},[71,18308,162],{"class":77},[71,18310,18311,18313,18315,18317],{"class":73,"line":92},[71,18312,7449],{"class":189},[71,18314,205],{"class":77},[71,18316,424],{"class":176},[71,18318,427],{"class":77},[71,18320,18321,18323,18325,18327,18329,18331,18333,18335,18337,18339,18341,18343],{"class":73,"line":102},[71,18322,7449],{"class":189},[71,18324,205],{"class":77},[71,18326,1745],{"class":176},[71,18328,211],{"class":77},[71,18330,214],{"class":77},[71,18332,7470],{"class":217},[71,18334,214],{"class":77},[71,18336,193],{"class":77},[71,18338,7442],{"class":77},[71,18340,193],{"class":77},[71,18342,3256],{"class":342},[71,18344,162],{"class":77},[71,18346,18347,18349,18351,18353,18355,18357,18359,18361,18363,18365,18368,18370,18372,18375,18377,18380,18382,18385,18387,18389,18391,18394],{"class":73,"line":114},[71,18348,7449],{"class":189},[71,18350,205],{"class":77},[71,18352,1921],{"class":176},[71,18354,211],{"class":77},[71,18356,10187],{"class":342},[71,18358,193],{"class":77},[71,18360,7506],{"class":342},[71,18362,193],{"class":77},[71,18364,7424],{"class":77},[71,18366,18367],{"class":217},"Gerado em ",[71,18369,214],{"class":77},[71,18371,18178],{"class":77},[71,18373,18374],{"class":189},"time",[71,18376,205],{"class":77},[71,18378,18379],{"class":176},"Now",[71,18381,7534],{"class":77},[71,18383,18384],{"class":176},"Format",[71,18386,211],{"class":77},[71,18388,18374],{"class":189},[71,18390,205],{"class":77},[71,18392,18393],{"class":189},"RFC3339",[71,18395,4998],{"class":77},[71,18397,18398],{"class":73,"line":124},[71,18399,89],{"emptyLinePlaceholder":88},[71,18401,18402,18404,18406,18408,18410,18412,18414,18416,18418,18420,18422,18424,18426,18428],{"class":73,"line":129},[71,18403,7526],{"class":189},[71,18405,205],{"class":77},[71,18407,7531],{"class":176},[71,18409,7534],{"class":77},[71,18411,7537],{"class":176},[71,18413,211],{"class":77},[71,18415,214],{"class":77},[71,18417,7544],{"class":217},[71,18419,214],{"class":77},[71,18421,193],{"class":77},[71,18423,7424],{"class":77},[71,18425,7553],{"class":217},[71,18427,214],{"class":77},[71,18429,162],{"class":77},[71,18431,18432,18434,18436,18438,18440,18442,18444,18446,18448,18450,18452,18454,18456],{"class":73,"line":139},[71,18433,227],{"class":95},[71,18435,196],{"class":189},[71,18437,199],{"class":77},[71,18439,7568],{"class":189},[71,18441,205],{"class":77},[71,18443,1925],{"class":176},[71,18445,211],{"class":77},[71,18447,1861],{"class":189},[71,18449,675],{"class":77},[71,18451,196],{"class":189},[71,18453,232],{"class":77},[71,18455,235],{"class":77},[71,18457,183],{"class":77},[71,18459,18460,18462,18464,18466,18468,18470,18472,18474,18476,18478,18480,18482],{"class":73,"line":149},[71,18461,7591],{"class":189},[71,18463,205],{"class":77},[71,18465,7596],{"class":176},[71,18467,211],{"class":77},[71,18469,1861],{"class":189},[71,18471,193],{"class":77},[71,18473,7605],{"class":189},[71,18475,205],{"class":77},[71,18477,7596],{"class":176},[71,18479,7612],{"class":77},[71,18481,7615],{"class":342},[71,18483,162],{"class":77},[71,18485,18486],{"class":73,"line":159},[71,18487,261],{"class":77},[71,18489,18490],{"class":73,"line":165},[71,18491,709],{"class":77},[19,18493,18494],{},[23,18495,14988],{},[62,18497,18499],{"className":64,"code":18498,"language":66,"meta":67,"style":67},"func handler(w http.ResponseWriter, r *http.Request) {\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(\"Gerado em \" + time.Now().Format(time.RFC3339))\n        })\n    })\n\n    w.Header().Set(\"Content-Type\", \"application/pdf\")\n    if err := doc.Render(w); err != nil {\n        http.Error(w, err.Error(), 500)\n    }\n}\n",[43,18500,18501,18533,18547,18565,18595,18599,18603,18617,18641,18671,18711,18715,18719,18723,18753,18781,18807,18811],{"__ignoreMap":67},[71,18502,18503,18505,18507,18509,18511,18513,18515,18517,18519,18521,18523,18525,18527,18529,18531],{"class":73,"line":74},[71,18504,173],{"class":77},[71,18506,7364],{"class":176},[71,18508,211],{"class":77},[71,18510,1861],{"class":444},[71,18512,7371],{"class":81},[71,18514,205],{"class":77},[71,18516,7376],{"class":81},[71,18518,193],{"class":77},[71,18520,7381],{"class":444},[71,18522,448],{"class":77},[71,18524,7386],{"class":81},[71,18526,205],{"class":77},[71,18528,7391],{"class":81},[71,18530,459],{"class":77},[71,18532,183],{"class":77},[71,18534,18535,18537,18539,18541,18543,18545],{"class":73,"line":85},[71,18536,272],{"class":189},[71,18538,199],{"class":77},[71,18540,277],{"class":189},[71,18542,205],{"class":77},[71,18544,282],{"class":176},[71,18546,285],{"class":77},[71,18548,18549,18551,18553,18555,18557,18559,18561,18563],{"class":73,"line":92},[71,18550,291],{"class":189},[71,18552,205],{"class":77},[71,18554,296],{"class":176},[71,18556,211],{"class":77},[71,18558,323],{"class":189},[71,18560,205],{"class":77},[71,18562,305],{"class":189},[71,18564,308],{"class":77},[71,18566,18567,18569,18571,18573,18575,18577,18579,18581,18583,18585,18587,18589,18591,18593],{"class":73,"line":102},[71,18568,291],{"class":189},[71,18570,205],{"class":77},[71,18572,318],{"class":176},[71,18574,211],{"class":77},[71,18576,323],{"class":189},[71,18578,205],{"class":77},[71,18580,328],{"class":176},[71,18582,211],{"class":77},[71,18584,323],{"class":189},[71,18586,205],{"class":77},[71,18588,337],{"class":176},[71,18590,211],{"class":77},[71,18592,343],{"class":342},[71,18594,346],{"class":77},[71,18596,18597],{"class":73,"line":114},[71,18598,403],{"class":77},[71,18600,18601],{"class":73,"line":124},[71,18602,89],{"emptyLinePlaceholder":88},[71,18604,18605,18607,18609,18611,18613,18615],{"class":73,"line":129},[71,18606,414],{"class":189},[71,18608,199],{"class":77},[71,18610,419],{"class":189},[71,18612,205],{"class":77},[71,18614,424],{"class":176},[71,18616,427],{"class":77},[71,18618,18619,18621,18623,18625,18627,18629,18631,18633,18635,18637,18639],{"class":73,"line":139},[71,18620,433],{"class":189},[71,18622,205],{"class":77},[71,18624,438],{"class":176},[71,18626,441],{"class":77},[71,18628,445],{"class":444},[71,18630,448],{"class":77},[71,18632,451],{"class":81},[71,18634,205],{"class":77},[71,18636,456],{"class":81},[71,18638,459],{"class":77},[71,18640,183],{"class":77},[71,18642,18643,18645,18647,18649,18651,18653,18655,18657,18659,18661,18663,18665,18667,18669],{"class":73,"line":149},[71,18644,467],{"class":189},[71,18646,205],{"class":77},[71,18648,472],{"class":176},[71,18650,211],{"class":77},[71,18652,477],{"class":342},[71,18654,193],{"class":77},[71,18656,482],{"class":77},[71,18658,485],{"class":444},[71,18660,448],{"class":77},[71,18662,451],{"class":81},[71,18664,205],{"class":77},[71,18666,494],{"class":81},[71,18668,459],{"class":77},[71,18670,183],{"class":77},[71,18672,18673,18675,18677,18679,18681,18683,18685,18687,18690,18693,18695,18697,18699,18701,18703,18705,18707,18709],{"class":73,"line":159},[71,18674,504],{"class":189},[71,18676,205],{"class":77},[71,18678,509],{"class":176},[71,18680,211],{"class":77},[71,18682,214],{"class":77},[71,18684,18367],{"class":217},[71,18686,214],{"class":77},[71,18688,18689],{"class":77}," +",[71,18691,18692],{"class":189}," time",[71,18694,205],{"class":77},[71,18696,18379],{"class":176},[71,18698,7534],{"class":77},[71,18700,18384],{"class":176},[71,18702,211],{"class":77},[71,18704,18374],{"class":189},[71,18706,205],{"class":77},[71,18708,18393],{"class":189},[71,18710,4998],{"class":77},[71,18712,18713],{"class":73,"line":165},[71,18714,572],{"class":77},[71,18716,18717],{"class":73,"line":170},[71,18718,578],{"class":77},[71,18720,18721],{"class":73,"line":186},[71,18722,89],{"emptyLinePlaceholder":88},[71,18724,18725,18727,18729,18731,18733,18735,18737,18739,18741,18743,18745,18747,18749,18751],{"class":73,"line":224},[71,18726,7526],{"class":189},[71,18728,205],{"class":77},[71,18730,7531],{"class":176},[71,18732,7534],{"class":77},[71,18734,7537],{"class":176},[71,18736,211],{"class":77},[71,18738,214],{"class":77},[71,18740,7544],{"class":217},[71,18742,214],{"class":77},[71,18744,193],{"class":77},[71,18746,7424],{"class":77},[71,18748,7553],{"class":217},[71,18750,214],{"class":77},[71,18752,162],{"class":77},[71,18754,18755,18757,18759,18761,18763,18765,18767,18769,18771,18773,18775,18777,18779],{"class":73,"line":240},[71,18756,227],{"class":95},[71,18758,196],{"class":189},[71,18760,199],{"class":77},[71,18762,419],{"class":189},[71,18764,205],{"class":77},[71,18766,7961],{"class":176},[71,18768,211],{"class":77},[71,18770,1861],{"class":189},[71,18772,675],{"class":77},[71,18774,196],{"class":189},[71,18776,232],{"class":77},[71,18778,235],{"class":77},[71,18780,183],{"class":77},[71,18782,18783,18785,18787,18789,18791,18793,18795,18797,18799,18801,18803,18805],{"class":73,"line":258},[71,18784,7591],{"class":189},[71,18786,205],{"class":77},[71,18788,7596],{"class":176},[71,18790,211],{"class":77},[71,18792,1861],{"class":189},[71,18794,193],{"class":77},[71,18796,7605],{"class":189},[71,18798,205],{"class":77},[71,18800,7596],{"class":176},[71,18802,7612],{"class":77},[71,18804,7615],{"class":342},[71,18806,162],{"class":77},[71,18808,18809],{"class":73,"line":264},[71,18810,261],{"class":77},[71,18812,18813],{"class":73,"line":269},[71,18814,709],{"class":77},[19,18816,18817,18818,18820,18821,18824,18825,18827,18828,205],{},"Mesma forma. ",[43,18819,14756],{}," transmite o PDF direto na resposta. Se quiser setar ",[43,18822,18823],{},"Content-Length",", chame ",[43,18826,3922],{}," primeiro para pegar o slice de bytes e tire ",[43,18829,18830],{},"len()",[14,18832,18834],{"id":18833},"quão-rápido-é-rápido-o-suficiente","Quão rápido é \"rápido o suficiente\"?",[19,18836,18837,18838,18840,18841,18843],{},"O gpdf é aproximadamente ",[23,18839,14379],{}," nas cargas que a galera realmente roda. Os números abaixo vêm de ",[43,18842,1394],{}," rodando num Apple M1 com Go 1.25.",[742,18845,18846,18860],{},[745,18847,18848],{},[748,18849,18850,18852,18854,18856,18858],{},[751,18851,8055],{},[751,18853,33],{},[751,18855,1423],{},[751,18857,8062],{},[751,18859,1432],{},[759,18861,18862,18877,18893,18910],{},[748,18863,18864,18867,18871,18873,18875],{},[764,18865,18866],{},"Página única",[764,18868,18869],{},[23,18870,1340],{},[764,18872,1446],{},[764,18874,1452],{},[764,18876,1455],{},[748,18878,18879,18882,18886,18888,18890],{},[764,18880,18881],{},"Tabela 4×10",[764,18883,18884],{},[23,18885,1344],{},[764,18887,1467],{},[764,18889,1473],{},[764,18891,18892],{},"8,6 ms",[748,18894,18895,18898,18902,18905,18907],{},[764,18896,18897],{},"Documento de 100 págs.",[764,18899,18900],{},[23,18901,1348],{},[764,18903,18904],{},"11,7 ms",[764,18906,18892],{},[764,18908,18909],{},"19,8 ms",[748,18911,18912,18914,18918,18920,18922],{},[764,18913,8118],{},[764,18915,18916],{},[23,18917,1506],{},[764,18919,1509],{},[764,18921,1515],{},[764,18923,18924],{},"10,4 ms",[19,18926,18927],{},"Não são sintéticos — o benchmark de tabela é uma de itens de nota com 4 colunas e 10 linhas; o de 100 páginas é um relatório paginado com cabeçalho repetido e números de página. A forma combina com o que o código de produção faz de verdade.",[19,18929,18930],{},"Sobre o que esses números significam. A 13 µs por página única, um único core produz ~75.000 PDFs \"hello world\" por segundo. A 108 µs por página com tabela, ~9.000 notas por segundo. O ponto não é ostentar — é que você pode parar de se perguntar se precisa cachear ou empurrar geração de PDF para uma fila assíncrona. Para a maioria das cargas, gerar no caminho da requisição basta.",[14,18932,18934],{"id":18933},"o-que-você-perde-na-migração","O que você perde na migração",[19,18936,18937],{},"Nada neste guia tem valor se esconder lacunas reais. Aqui está o que o gpdf ainda não faz e o gofpdf fazia:",[1114,18939,18940,18948,18965,18971],{},[887,18941,18942,898,18945,18947],{},[23,18943,18944],{},"Linhas em ângulos arbitrários, curvas Bézier e caminhos complexos.",[43,18946,8178],{}," desenha uma régua horizontal cruzando uma coluna. Se você produz desenhos técnicos ou gráficos com geometria própria, o gpdf ainda não chegou lá. (Gráficos como imagens pré-renderizadas: funciona sem problema.)",[887,18949,18950,18955,18956,18958,18959,18961,18962,18964],{},[23,18951,18952,18954],{},[43,18953,1917],{}," e trabalho com cursor absoluto."," Dá pra posicionar absoluto com ",[43,18957,14677],{},", mas se seu código existente são 2.000 linhas de ",[43,18960,1917],{}," seguidas de ",[43,18963,1921],{},", a migração é mais uma reescrita. A boa notícia é que o código reescrito costuma ser metade do tamanho do original.",[887,18966,18967,18970],{},[23,18968,18969],{},"Campos de formulário (AcroForm)."," O gpdf ainda não gera campos preenchíveis. Se seus PDFs são templates que o usuário preenche num visualizador, fique com uma biblioteca que suporte AcroForm — por enquanto.",[887,18972,18973,18976],{},[23,18974,18975],{},"Anotações e marcadores."," Suporte básico de outline existe; anotações ricas não.",[19,18978,18979],{},"Se nada disso morde você, a migração é direta. Se morde, abra uma issue — o roadmap é movido pelo que as pessoas pedem.",[14,18981,2787],{"id":2786},[19,18983,18984,18987],{},[23,18985,18986],{},"O gpdf é um fork do gofpdf?","\nNão. O gpdf é uma reimplementação limpa. O trabalho de formato de cabo PDF, o motor de layout, o subsetter TrueType — tudo escrito do zero em Go puro. Não há linhagem compartilhada com gofpdf ou seus forks. Tem que ser reescrita porque a arquitetura do gofpdf é construída em torno de um único cursor mutável; não dá para extrair uma grade declarativa daquilo sem quebrar todos os sites de chamada existentes.",[19,18989,18990,18993,18994,7065,18997,18999,19000,19002,19003,19006],{},[23,18991,18992],{},"O gpdf tem dependências externas?","\nA biblioteca core tem zero. Rode ",[43,18995,18996],{},"go mod graph | grep gpdf",[43,18998,7068],{}," e verá uma única linha. O add-on ",[43,19001,8199],{}," (HTML→PDF, criptografia AES, assinaturas, PDF/A) puxa ",[43,19004,19005],{},"golang.org/x/net"," para parsing HTML, mas é opt-in e não é necessário para migrar.",[19,19008,19009,19012,19013,19015],{},[23,19010,19011],{},"E CGO? O gofpdf era CGO-free, e o gpdf?","\nIgual. Go puro, sem CGO. Faça ",[43,19014,7055],{}," e entregue um binário estático. Isso importa em imagens distroless e Alpine, onde arrastar uma toolchain CGO dobra o tamanho do container.",[19,19017,19018,19024,19025,19027],{},[23,19019,19020,19021,19023],{},"Meu código gofpdf existente usa ",[43,19022,1917],{}," para posicionamento absoluto em todo lugar. Dá para migrar sem reescrever?","\nVocê consegue envolver ",[43,19026,14677],{}," e ter algo com sensação parecida. Mas se seu código está estruturado em torno de manipulação de cursor, o modelo do motor de layout é uma mudança mental, não sintática. A maior parte dos times descobre que a reescrita sai mais curta do que o original.",[19,19029,19030,19033,19034,19036],{},[23,19031,19032],{},"NFe, DANFE e PDF/A para arquivamento fiscal?","\nCarimbo de tempo e assinatura digital estão sendo implementados em ",[43,19035,8199],{},". Se você tem um requisito concreto, abra uma issue para subir a prioridade.",[19,19038,19039,19042],{},[23,19040,19041],{},"E se o go-pdf/fpdf for desarquivado?","\nAí você tem mais uma opção. A aposta por trás do gpdf não é que o gofpdf fique arquivado para sempre — é que a arquitetura (baseada em cursor, fontes de um byte, sem CJK nativo) é beco sem saída independente de quem mantém. Gerar PDF em 2026 se parece mais com montar uma página web do que com dirigir um plotter, e a API deveria refletir isso.",[14,19044,1242],{"id":1241},[19,19046,19047],{},"gpdf é uma biblioteca Go para gerar PDFs. Licença MIT, zero dependências externas, suporte CJK nativo.",[62,19049,19050],{"className":1248,"code":1249,"language":1250,"meta":67,"style":67},[43,19051,19052],{"__ignoreMap":67},[71,19053,19054,19056,19058],{"class":73,"line":74},[71,19055,66],{"class":81},[71,19057,1259],{"class":217},[71,19059,1262],{"class":217},[19,19061,19062,1269,19065],{},[28,19063,1268],{"href":30,"rel":19064},[32],[28,19066,9973],{"href":1272,"rel":19067},[32],[14,19069,8323],{"id":8322},[1114,19071,19072,19078,19083],{},[887,19073,19074,19075],{},"Como funciona a grade de 12 colunas no gpdf? ",[2939,19076,19077],{},"(em breve)",[887,19079,19080,19081],{},"Como embutir uma fonte japonesa no gpdf? ",[2939,19082,19077],{},[887,19084,19085,19088,19089],{},[28,19086,11630],{"href":1272,"rel":19087},[32]," — setup em cinco minutos, incluindo ",[43,19090,6688],{},[1276,19092,19093],{},"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 .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}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}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}",{"title":67,"searchDepth":85,"depth":85,"links":19095},[19096,19097,19098,19099,19100,19101,19102,19103,19104,19105,19106,19107,19108,19109],{"id":37,"depth":85,"text":38},{"id":14410,"depth":85,"text":14411},{"id":14442,"depth":85,"text":14443},{"id":14486,"depth":85,"text":14487},{"id":14802,"depth":85,"text":14803},{"id":15394,"depth":85,"text":15395},{"id":16532,"depth":85,"text":16533},{"id":17182,"depth":85,"text":17183},{"id":18211,"depth":85,"text":18212},{"id":18833,"depth":85,"text":18834},{"id":18933,"depth":85,"text":18934},{"id":2786,"depth":85,"text":2787},{"id":1241,"depth":85,"text":1242},{"id":8322,"depth":85,"text":8323},"2026-04-14","jung-kurt/gofpdf foi arquivado em 2021 e go-pdf/fpdf em 2025. Este guia mapeia toda a API do gofpdf para o gpdf — Go puro, zero dependências, CJK nativo.",{"name":19113,"totalTime":19114,"tools":19115,"steps":19116},"Migrar um projeto Go de gofpdf para gpdf","PT30M",[1299],[19117,19120,19123,19126,19129,19132],{"name":19118,"text":19119},"Substituir o caminho do import","Troque github.com/jung-kurt/gofpdf (arquivado em 2021) e github.com/go-pdf/fpdf (arquivado em 2025) por github.com/gpdf-dev/gpdf, github.com/gpdf-dev/gpdf/document e github.com/gpdf-dev/gpdf/template.",{"name":19121,"text":19122},"Construir o documento com builders em vez de cursores","Chame gpdf.NewDocument com WithPageSize, WithMargins e WithFont. Em vez de movimentar um cursor com SetXY, adicione páginas via doc.AddPage() e descreva o conteúdo com RowBuilder e ColBuilder.",{"name":19124,"text":19125},"Converter Cell e MultiCell em Text declarativo","Substitua pdf.Cell e pdf.MultiCell por c.Text(...) dentro de uma coluna. O texto quebra automaticamente no limite da coluna, então a flag final do MultiCell desaparece. Tamanho de fonte, peso e cor viram opções per-text.",{"name":19127,"text":19128},"Registrar fontes CJK via WithFont","Para textos em chinês, japonês ou coreano, substitua pdf.AddUTF8Font por gpdf.WithFont(name, ttfBytes) na construção do documento. Não há mais necessidade de gerenciar caminhos TTF ou a flag UTF-8 — o subset embedding acontece automaticamente.",{"name":19130,"text":19131},"Reescrever tabelas com linhas e colunas","Descarte os loops aninhados de Cell com larguras manuais. Dentro de um AutoRow, use row.Col(n, fn) para construir linhas de tabela; a grade de 12 colunas calcula as larguras e cuida das quebras de página.",{"name":19133,"text":19134},"Trocar a chamada de saída","Substitua pdf.OutputFileAndClose(path) por doc.Generate() seguido de os.WriteFile(path, data, 0o644), ou use doc.Render(w) para escrever direto em um io.Writer.",{},{"title":14360,"description":19111},"pt/blog/001.gofpdf-migration",[8374,2918,1325],"FkitdjLnTPGcq3xjJuzDfDe_o2FraVgXUCIC5BV00Eo",1776529277634]