[{"data":1,"prerenderedAt":5708},["ShallowReactive",2],{"blog-ko-unidoc-migration":3},{"id":4,"title":5,"author":6,"body":10,"date":5667,"description":5668,"draft":5669,"extension":5670,"howTo":5671,"image":5698,"meta":5699,"navigation":311,"path":5700,"seo":5701,"stem":5702,"tags":5703,"updated":5698,"__hash__":5707},"blogKo/ko/blog/016.unidoc-migration.md","unipdf는 AGPL 또는 유료. gpdf로 마이그레이션하는 가이드",{"name":7,"url":8,"avatar":9},"Taiki Noda","https://nadai.dev/en/about","https://nadai.dev/og-default.png",{"type":11,"value":12,"toc":5650},"minimark",[13,18,46,62,77,80,84,87,90,112,119,123,126,256,263,267,270,417,538,556,563,567,578,885,916,920,923,928,1302,1307,1737,1746,1750,1767,1771,2387,2390,2394,2787,2799,2810,2817,2821,2824,2828,3091,3097,3101,3554,3569,3572,3576,3591,3595,3884,3887,3891,4551,4559,4563,4569,4573,5039,5043,5331,5342,5346,5352,5448,5451,5454,5458,5461,5502,5507,5511,5517,5529,5549,5555,5574,5580,5584,5587,5604,5617,5621,5647],[14,15,17],"h2",{"id":16},"tldr","TL;DR",[19,20,21,25,26,29,30,29,33,36,37,41,42,45],"p",{},[22,23,24],"strong",{},"gpdf","는 ",[22,27,28],{},"MIT 라이선스",", ",[22,31,32],{},"외부 의존성 0",[22,34,35],{},"라이선스 키 등록 단계가 없는"," 순수 Go PDF 라이브러리다. CJK나 AcroForm 때문에 ",[38,39,40],"code",{},"unidoc/unipdf","를 쓰고 있는데, AGPL 조항이 법무팀의 배포 승인을 막거나 상용 라이선스 비용을 정당화하기 어렵다면, 이 글이 unipdf ",[38,43,44],{},"creator"," API에서 gpdf로의 마이그레이션 지도가 된다.",[19,47,48,49,52,53,56,57,61],{},"지난 분기, 지인의 핀테크 회사에서 ",[38,50,51],{},"github.com/unidoc/unipdf/v3","를 OSS 승인 흐름에 올렸다. 다음 날 돌아온 답은 ",[22,54,55],{},"AGPL-3.0"," 옆의 빨간 X와 법무팀의 메모였다 — ",[58,59,60],"em",{},"\"클로즈드소스 배포 제품에 링크할 수 없음. 상용 라이선스 취득 또는 제거 필요.\""," 상용 견적은 개발자 1명당 연간 비용으로 왔고, 12명 팀에 곱하니 모두 다시 검색 결과를 열었다.",[19,63,64,65,68,69,72,73,76],{},"이것이 README에 나오지 않는 unipdf의 한 면이다. 기술적으로는 훌륭하다 — 성숙하고, 기능이 깊고, 잘 유지보수된다. 동시에 ",[22,66,67],{},"듀얼 라이선스","다: 오픈 사용은 ",[22,70,71],{},"AGPL v3",", 그 외는 ",[22,74,75],{},"유료 상용 라이선스",". AGPL v3는 일반적으로 사용되는 copyleft 중 가장 강한 것에 속한다. unipdf를 사용자가 네트워크로 접근하는 서비스에 링크하면, §13에 따라 전체 대응 소스를 공개해야 한다. 대부분 회사의 법무는 안 된다고 한다.",[19,78,79],{},"이미 unipdf 코드가 운영 중이고 라이선스가 감사에서 걸렸거나 갱신이 다가왔다면, 이 글이 마이그레이션 지도다. 새 프로젝트를 시작하면서 가장 잘 정리된 문서 때문에 무심코 unipdf를 잡았다면, 이건 청구서가 따라오지 않는 대안이다.",[14,81,83],{"id":82},"agpl-또는-유료가-실제-운영에서-의미하는-것","\"AGPL 또는 유료\"가 실제 운영에서 의미하는 것",[19,85,86],{},"많은 Go 라이브러리가 별생각 없이 \"AGPL\" 라벨이 붙어 있지만, unipdf는 그렇지 않다. 저장소의 라이선스 파일은 순수 AGPL v3, README는 상용 사용에 키가 필요하다고 명시하며, 바이너리 자체가 이를 강제한다. 시작 시 라이선스를 등록하지 않고 unipdf API를 호출하면 에러가 나거나 모든 출력 페이지에 워터마크가 찍힌다.",[19,88,89],{},"대략 세 모드 중 하나에 속하게 된다:",[91,92,93,100,106],"ol",{},[94,95,96,99],"li",{},[22,97,98],{},"AGPL 모드."," 자사 코드를 AGPL v3로 공개한다. unipdf에 닿는 모든 바이트, 그리고 그것에 링크된 모든 코드는 네트워크로 서비스를 사용하는 누구든 받아갈 수 있어야 한다. 사내 도구나 SaaS 제품에는 사실상 불가능하다.",[94,101,102,105],{},[22,103,104],{},"상용 모드."," UniDoc에 개발자당 연 단위로 비용을 지불한다. 가격은 변동하지만 최근 공개 견적은 개발자 1명당 연 4자리 달러대였고, 모든 바이너리가 시작 시 metering 또는 라이선스 키 등록 호출을 해야 한다. 키는 시크릿으로 취급되므로 시크릿 매니저에 두고 모든 컨테이너에 주입한다.",[94,107,108,111],{},[22,109,110],{},"트라이얼 / 평가 모드."," 기간 한정 무료. 출력에 워터마크. 운영에는 못 쓴다.",[19,113,114,115,118],{},"세 모드 모두 본질적으로 잘못된 건 아니다. UniDoc은 실제 회사가 실제 엔지니어로 만들고 유지하는 제품이고, 가격은 그 비용을 반영한다. ",[22,116,117],{},"요점은, 라이선스 결정이 모든 레이어에 스며든다는 것"," — 법무 검토, 시크릿 로테이션, 재무 갱신, 배포 면 (모든 컨테이너에 키 필요). gpdf는 MIT라서 그 한 열을 스프레드시트에서 통째로 지운다.",[14,120,122],{"id":121},"잃는-것과-남는-것","잃는 것과 남는 것",[19,124,125],{},"API 이야기 전에 솔직하게. unipdf가 하고 gpdf가 못하는 게 있다:",[127,128,129,144],"table",{},[130,131,132],"thead",{},[133,134,135,139,142],"tr",{},[136,137,138],"th",{},"기능",[136,140,141],{},"unipdf",[136,143,24],{},[145,146,147,158,168,178,188,197,207,217,227,236,245],"tbody",{},[133,148,149,153,156],{},[150,151,152],"td",{},"PDF 생성",[150,154,155],{},"✅",[150,157,155],{},[133,159,160,163,165],{},[150,161,162],{},"TrueType / CJK 폰트",[150,164,155],{},[150,166,167],{},"✅ (CGO 미사용, 자동 서브셋팅)",[133,169,170,173,175],{},[150,171,172],{},"AES-128/256 암호화",[150,174,155],{},[150,176,177],{},"✅ (ISO 32000-2 Rev 6, 순수 Go)",[133,179,180,183,185],{},[150,181,182],{},"PKCS#7 / PAdES 서명",[150,184,155],{},[150,186,187],{},"✅ (RFC 3161 TSA 지원)",[133,189,190,193,195],{},[150,191,192],{},"PDF/A-1b/2b",[150,194,155],{},[150,196,155],{},[133,198,199,202,204],{},[150,200,201],{},"AcroForm — 기존 필드 작성",[150,203,155],{},[150,205,206],{},"✅ (flatten만, 신규 필드 생성은 미지원)",[133,208,209,212,214],{},[150,210,211],{},"AcroForm — 신규 필드 작성",[150,213,155],{},[150,215,216],{},"❌",[133,218,219,222,224],{},[150,220,221],{},"PDF 파싱 / 텍스트 추출",[150,223,155],{},[150,225,226],{},"❌ (gpdf는 생성 전용)",[133,228,229,232,234],{},[150,230,231],{},"OCR",[150,233,155],{},[150,235,216],{},[133,237,238,241,243],{},[150,239,240],{},"PDF 마스킹 (redaction)",[150,242,155],{},[150,244,216],{},[133,246,247,250,253],{},[150,248,249],{},"HTML 렌더링",[150,251,252],{},"부분",[150,254,255],{},"❌ (별도 렌더러로 만들어 merge)",[19,257,258,259,262],{},"PDF 파싱, OCR, redaction이 필요한 경로는 이 마이그레이션으로 끝까지 갈 수 없다. 그 경로만 unipdf를 남기거나 (해당 바이너리는 여전히 상용 라이선스 필요), 읽기 측을 다른 라이브러리로 분리한다. ",[22,260,261],{},"생성, 암호화, 서명, 폰트, CJK"," 경로 — 대부분의 unipdf 청구서가 실제로 사용하는 것 — 에는 gpdf가 완전한 대체다.",[14,264,266],{"id":265},"라이선스-등록-코드-삭제","라이선스 등록 코드 삭제",[19,268,269],{},"전체 마이그레이션에서 가장 작은 diff이지만 나머지를 실감하게 만드는 한 걸음. unipdf 바이너리는 시작 시 키를 등록해야 하며 변형이 몇 가지 있다:",[271,272,277],"pre",{"className":273,"code":274,"language":275,"meta":276,"style":276},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","// API 키 (metering)\nimport \"github.com/unidoc/unipdf/v3/common/license\"\n\nfunc init() {\n    if err := license.SetMeteredKey(os.Getenv(\"UNIDOC_API_KEY\")); err != nil {\n        log.Fatal(err)\n    }\n}\n","go","",[38,278,279,288,306,313,329,386,405,411],{"__ignoreMap":276},[280,281,284],"span",{"class":282,"line":283},"line",1,[280,285,287],{"class":286},"sHwdD","// API 키 (metering)\n",[280,289,291,295,299,303],{"class":282,"line":290},2,[280,292,294],{"class":293},"s7zQu","import",[280,296,298],{"class":297},"sMK4o"," \"",[280,300,302],{"class":301},"sBMFI","github.com/unidoc/unipdf/v3/common/license",[280,304,305],{"class":297},"\"\n",[280,307,309],{"class":282,"line":308},3,[280,310,312],{"emptyLinePlaceholder":311},true,"\n",[280,314,316,319,323,326],{"class":282,"line":315},4,[280,317,318],{"class":297},"func",[280,320,322],{"class":321},"s2Zo4"," init",[280,324,325],{"class":297},"()",[280,327,328],{"class":297}," {\n",[280,330,332,335,339,342,345,348,351,354,357,359,362,364,367,371,373,376,378,381,384],{"class":282,"line":331},5,[280,333,334],{"class":293},"    if",[280,336,338],{"class":337},"sTEyZ"," err ",[280,340,341],{"class":297},":=",[280,343,344],{"class":337}," license",[280,346,347],{"class":297},".",[280,349,350],{"class":321},"SetMeteredKey",[280,352,353],{"class":297},"(",[280,355,356],{"class":337},"os",[280,358,347],{"class":297},[280,360,361],{"class":321},"Getenv",[280,363,353],{"class":297},[280,365,366],{"class":297},"\"",[280,368,370],{"class":369},"sfazB","UNIDOC_API_KEY",[280,372,366],{"class":297},[280,374,375],{"class":297},"));",[280,377,338],{"class":337},[280,379,380],{"class":297},"!=",[280,382,383],{"class":297}," nil",[280,385,328],{"class":297},[280,387,389,392,394,397,399,402],{"class":282,"line":388},6,[280,390,391],{"class":337},"        log",[280,393,347],{"class":297},[280,395,396],{"class":321},"Fatal",[280,398,353],{"class":297},[280,400,401],{"class":337},"err",[280,403,404],{"class":297},")\n",[280,406,408],{"class":282,"line":407},7,[280,409,410],{"class":297},"    }\n",[280,412,414],{"class":282,"line":413},8,[280,415,416],{"class":297},"}\n",[271,418,420],{"className":273,"code":419,"language":275,"meta":276,"style":276},"// 오프라인 라이선스 파일\nfunc init() {\n    licenseKey, _ := os.ReadFile(\"/etc/unidoc/license.txt\")\n    if err := license.SetLicenseKey(string(licenseKey), \"Acme Corp\"); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,421,422,427,437,469,516,530,534],{"__ignoreMap":276},[280,423,424],{"class":282,"line":283},[280,425,426],{"class":286},"// 오프라인 라이선스 파일\n",[280,428,429,431,433,435],{"class":282,"line":290},[280,430,318],{"class":297},[280,432,322],{"class":321},[280,434,325],{"class":297},[280,436,328],{"class":297},[280,438,439,442,445,448,450,453,455,458,460,462,465,467],{"class":282,"line":308},[280,440,441],{"class":337},"    licenseKey",[280,443,444],{"class":297},",",[280,446,447],{"class":337}," _ ",[280,449,341],{"class":297},[280,451,452],{"class":337}," os",[280,454,347],{"class":297},[280,456,457],{"class":321},"ReadFile",[280,459,353],{"class":297},[280,461,366],{"class":297},[280,463,464],{"class":369},"/etc/unidoc/license.txt",[280,466,366],{"class":297},[280,468,404],{"class":297},[280,470,471,473,475,477,479,481,484,486,490,492,495,498,500,503,505,508,510,512,514],{"class":282,"line":315},[280,472,334],{"class":293},[280,474,338],{"class":337},[280,476,341],{"class":297},[280,478,344],{"class":337},[280,480,347],{"class":297},[280,482,483],{"class":321},"SetLicenseKey",[280,485,353],{"class":297},[280,487,489],{"class":488},"spNyl","string",[280,491,353],{"class":297},[280,493,494],{"class":337},"licenseKey",[280,496,497],{"class":297},"),",[280,499,298],{"class":297},[280,501,502],{"class":369},"Acme Corp",[280,504,366],{"class":297},[280,506,507],{"class":297},");",[280,509,338],{"class":337},[280,511,380],{"class":297},[280,513,383],{"class":297},[280,515,328],{"class":297},[280,517,518,520,522,524,526,528],{"class":282,"line":331},[280,519,391],{"class":337},[280,521,347],{"class":297},[280,523,396],{"class":321},[280,525,353],{"class":297},[280,527,401],{"class":337},[280,529,404],{"class":297},[280,531,532],{"class":282,"line":388},[280,533,410],{"class":297},[280,535,536],{"class":282,"line":407},[280,537,416],{"class":297},[19,539,540,541,544,545,547,548,551,552,555],{},"gpdf에는 대응물이 없다. ",[38,542,543],{},"init()"," 블록 전체를 삭제. ",[38,546,370],{},"를 시크릿 매니저, CI 변수, 컨테이너 매니페스트에서 빼낸다. 이미지에서 라이선스 파일을 제거한다. import할 것은 ",[38,549,550],{},"github.com/gpdf-dev/gpdf","뿐, 요구 사항은 어딘가에서 ",[38,553,554],{},"gpdf.NewDocument","를 호출하는 것뿐이다.",[19,557,558,559,562],{},"이게 전부다. 마이그레이션 완료 판정 기준이기도 하다: ",[38,560,561],{},"grep -r unidoc .","이 끝났을 때 0건을 반환해야 한다.",[14,564,566],{"id":565},"api-매핑-표","API 매핑 표",[19,568,569,570,573,574,577],{},"치트 시트. 이후 섹션에서 다섯 쌍을 구체적으로 본다. unipdf의 고수준 빌더는 ",[38,571,572],{},"Creator",", gpdf는 ",[38,575,576],{},"Document",". 모양이 충분히 비슷해서 대부분의 코드는 보면서 옮길 수 있다.",[127,579,580,595],{},[130,581,582],{},[133,583,584,587,593],{},[136,585,586],{},"하고 싶은 일",[136,588,589,590,592],{},"unipdf (",[38,591,44],{},")",[136,594,24],{},[145,596,597,612,627,642,661,679,697,713,728,743,758,780,802,822,837,855,870],{},[133,598,599,602,607],{},[150,600,601],{},"빌더 생성",[150,603,604],{},[38,605,606],{},"c := creator.New(); c.SetPageSize(creator.PageSizeA4)",[150,608,609],{},[38,610,611],{},"doc := gpdf.NewDocument(gpdf.WithPageSize(document.A4))",[133,613,614,617,622],{},[150,615,616],{},"마진 설정",[150,618,619],{},[38,620,621],{},"c.SetPageMargins(L, R, T, B)",[150,623,624],{},[38,625,626],{},"gpdf.WithMargins(document.UniformEdges(document.Mm(20)))",[133,628,629,632,637],{},[150,630,631],{},"새 페이지",[150,633,634],{},[38,635,636],{},"c.NewPage()",[150,638,639],{},[38,640,641],{},"page := doc.AddPage()",[133,643,644,647,652],{},[150,645,646],{},"한 줄 텍스트",[150,648,649],{},[38,650,651],{},"p := c.NewParagraph(\"hi\"); c.Draw(p)",[150,653,654,657,658],{},[38,655,656],{},"c.Text(\"hi\")"," ",[58,659,660],{},"(컬럼 안에서)",[133,662,663,666,671],{},[150,664,665],{},"자동 줄바꿈 텍스트",[150,667,668],{},[38,669,670],{},"p := c.NewStyledParagraph(); p.SetText(...); c.Draw(p)",[150,672,673,657,676],{},[38,674,675],{},"c.Text(body)",[58,677,678],{},"(자동 줄바꿈)",[133,680,681,684,689],{},[150,682,683],{},"폰트 등록",[150,685,686],{},[38,687,688],{},"model.NewCompositePdfFontFromTTFFile(path)",[150,690,691,657,694],{},[38,692,693],{},"gpdf.WithFont(\"Name\", ttfBytes)",[58,695,696],{},"(생성 시점)",[133,698,699,702,707],{},[150,700,701],{},"텍스트 폰트 지정",[150,703,704],{},[38,705,706],{},"style.Font = font; style.FontSize = 12",[150,708,709,712],{},[38,710,711],{},"template.FontFamily(\"Name\"), template.FontSize(12)"," per-text",[133,714,715,718,723],{},[150,716,717],{},"색상",[150,719,720],{},[38,721,722],{},"style.Color = creator.ColorRGBFromHex(\"#1A237E\")",[150,724,725],{},[38,726,727],{},"template.TextColor(pdf.RGBHex(0x1A237E))",[133,729,730,733,738],{},[150,731,732],{},"테이블",[150,734,735],{},[38,736,737],{},"t := c.NewTable(4); t.SetColumnWidths(...); c.Draw(t)",[150,739,740],{},[38,741,742],{},"c.Table(headers, rows, template.ColumnWidths(...))",[133,744,745,748,753],{},[150,746,747],{},"이미지",[150,749,750],{},[38,751,752],{},"img, _ := c.NewImageFromFile(path); img.ScaleToWidth(w); c.Draw(img)",[150,754,755],{},[38,756,757],{},"c.Image(imgBytes, template.FitWidth(document.Mm(50)))",[133,759,760,763,772],{},[150,761,762],{},"헤더 / 푸터",[150,764,765,768,769],{},[38,766,767],{},"c.DrawHeader(fn)"," / ",[38,770,771],{},"c.DrawFooter(fn)",[150,773,774,768,777],{},[38,775,776],{},"doc.Header(fn)",[38,778,779],{},"doc.Footer(fn)",[133,781,782,785,791],{},[150,783,784],{},"페이지 번호",[150,786,787,790],{},[38,788,789],{},"DrawFooter"," 인자에서 수동 추적",[150,792,793,768,796,657,799],{},[38,794,795],{},"c.PageNumber()",[38,797,798],{},"c.TotalPages()",[58,800,801],{},"(플레이스홀더)",[133,803,804,807,817],{},[150,805,806],{},"암호화",[150,808,809,812,813,816],{},[38,810,811],{},"model.PdfWriter"," + ",[38,814,815],{},"Encrypt","로 재인코딩",[150,818,819],{},[38,820,821],{},"gpdf.WithEncryption(gpdf.AES256, \"user\", \"owner\", perms)",[133,823,824,827,832],{},[150,825,826],{},"서명",[150,828,829],{},[38,830,831],{},"model.NewPdfAppender(...).Sign(...)",[150,833,834],{},[38,835,836],{},"gpdf.SignDocument(pdfBytes, signer, opts)",[133,838,839,842,850],{},[150,840,841],{},"라이선스 등록",[150,843,844,846,847],{},[38,845,543],{},"의 ",[38,848,849],{},"license.SetMeteredKey(...)",[150,851,852],{},[58,853,854],{},"(없음 — 삭제)",[133,856,857,860,865],{},[150,858,859],{},"파일 출력",[150,861,862],{},[38,863,864],{},"c.WriteToFile(\"out.pdf\")",[150,866,867],{},[38,868,869],{},"data, _ := doc.Generate(); os.WriteFile(\"out.pdf\", data, 0o644)",[133,871,872,875,880],{},[150,873,874],{},"Writer 출력",[150,876,877],{},[38,878,879],{},"c.Write(w)",[150,881,882],{},[38,883,884],{},"doc.Render(w)",[19,886,887,888,891,892,895,896,899,900,903,904,907,908,911,912,915],{},"기억해야 할 구조 변화 두 가지. unipdf의 creator는 ",[22,889,890],{},"상태를 가진다"," — ",[38,893,894],{},"Paragraph","나 ",[38,897,898],{},"Table","을 만들고 ",[38,901,902],{},"c.Draw(thing)","으로 커밋한다. gpdf는 ",[22,905,906],{},"선언적이다"," — 행과 컬럼의 트리를 묘사하고 레이아웃 엔진이 배치한다. 두 번째는 gpdf가 Bootstrap처럼 ",[22,909,910],{},"12 컬럼 그리드","를 가진다는 것. 모든 행은 암묵적으로 12 단위 너비, ",[38,913,914],{},"r.Col(n, fn)","으로 소비한다. 밀리미터로 너비를 따라다니지 않게 되면, 대부분의 레이아웃이 두세 줄로 줄어든다.",[14,917,919],{"id":918},"before-after-1-가장-작은-pdf","Before / After 1: 가장 작은 PDF",[19,921,922],{},"\"hello world\" 쌍. unipdf 쪽이 길어 보이는 건 라이선스 호출 의식 때문이다.",[19,924,925],{},[22,926,927],{},"Before — unipdf:",[271,929,931],{"className":273,"code":930,"language":275,"meta":276,"style":276},"package main\n\nimport (\n    \"log\"\n    \"os\"\n\n    \"github.com/unidoc/unipdf/v3/common/license\"\n    \"github.com/unidoc/unipdf/v3/creator\"\n)\n\nfunc init() {\n    if err := license.SetMeteredKey(os.Getenv(\"UNIDOC_API_KEY\")); err != nil {\n        log.Fatal(err)\n    }\n}\n\nfunc main() {\n    c := creator.New()\n    c.SetPageSize(creator.PageSizeA4)\n\n    p := c.NewParagraph(\"Hello, World!\")\n    p.SetFontSize(24)\n    if err := c.Draw(p); err != nil {\n        log.Fatal(err)\n    }\n\n    if err := c.WriteToFile(\"hello.pdf\"); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,932,933,941,945,952,962,970,974,982,991,996,1001,1012,1053,1068,1073,1078,1083,1095,1114,1136,1141,1168,1187,1217,1232,1237,1242,1277,1292,1297],{"__ignoreMap":276},[280,934,935,938],{"class":282,"line":283},[280,936,937],{"class":297},"package",[280,939,940],{"class":301}," main\n",[280,942,943],{"class":282,"line":290},[280,944,312],{"emptyLinePlaceholder":311},[280,946,947,949],{"class":282,"line":308},[280,948,294],{"class":293},[280,950,951],{"class":297}," (\n",[280,953,954,957,960],{"class":282,"line":315},[280,955,956],{"class":297},"    \"",[280,958,959],{"class":301},"log",[280,961,305],{"class":297},[280,963,964,966,968],{"class":282,"line":331},[280,965,956],{"class":297},[280,967,356],{"class":301},[280,969,305],{"class":297},[280,971,972],{"class":282,"line":388},[280,973,312],{"emptyLinePlaceholder":311},[280,975,976,978,980],{"class":282,"line":407},[280,977,956],{"class":297},[280,979,302],{"class":301},[280,981,305],{"class":297},[280,983,984,986,989],{"class":282,"line":413},[280,985,956],{"class":297},[280,987,988],{"class":301},"github.com/unidoc/unipdf/v3/creator",[280,990,305],{"class":297},[280,992,994],{"class":282,"line":993},9,[280,995,404],{"class":297},[280,997,999],{"class":282,"line":998},10,[280,1000,312],{"emptyLinePlaceholder":311},[280,1002,1004,1006,1008,1010],{"class":282,"line":1003},11,[280,1005,318],{"class":297},[280,1007,322],{"class":321},[280,1009,325],{"class":297},[280,1011,328],{"class":297},[280,1013,1015,1017,1019,1021,1023,1025,1027,1029,1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051],{"class":282,"line":1014},12,[280,1016,334],{"class":293},[280,1018,338],{"class":337},[280,1020,341],{"class":297},[280,1022,344],{"class":337},[280,1024,347],{"class":297},[280,1026,350],{"class":321},[280,1028,353],{"class":297},[280,1030,356],{"class":337},[280,1032,347],{"class":297},[280,1034,361],{"class":321},[280,1036,353],{"class":297},[280,1038,366],{"class":297},[280,1040,370],{"class":369},[280,1042,366],{"class":297},[280,1044,375],{"class":297},[280,1046,338],{"class":337},[280,1048,380],{"class":297},[280,1050,383],{"class":297},[280,1052,328],{"class":297},[280,1054,1056,1058,1060,1062,1064,1066],{"class":282,"line":1055},13,[280,1057,391],{"class":337},[280,1059,347],{"class":297},[280,1061,396],{"class":321},[280,1063,353],{"class":297},[280,1065,401],{"class":337},[280,1067,404],{"class":297},[280,1069,1071],{"class":282,"line":1070},14,[280,1072,410],{"class":297},[280,1074,1076],{"class":282,"line":1075},15,[280,1077,416],{"class":297},[280,1079,1081],{"class":282,"line":1080},16,[280,1082,312],{"emptyLinePlaceholder":311},[280,1084,1086,1088,1091,1093],{"class":282,"line":1085},17,[280,1087,318],{"class":297},[280,1089,1090],{"class":321}," main",[280,1092,325],{"class":297},[280,1094,328],{"class":297},[280,1096,1098,1101,1103,1106,1108,1111],{"class":282,"line":1097},18,[280,1099,1100],{"class":337},"    c ",[280,1102,341],{"class":297},[280,1104,1105],{"class":337}," creator",[280,1107,347],{"class":297},[280,1109,1110],{"class":321},"New",[280,1112,1113],{"class":297},"()\n",[280,1115,1117,1120,1122,1125,1127,1129,1131,1134],{"class":282,"line":1116},19,[280,1118,1119],{"class":337},"    c",[280,1121,347],{"class":297},[280,1123,1124],{"class":321},"SetPageSize",[280,1126,353],{"class":297},[280,1128,44],{"class":337},[280,1130,347],{"class":297},[280,1132,1133],{"class":337},"PageSizeA4",[280,1135,404],{"class":297},[280,1137,1139],{"class":282,"line":1138},20,[280,1140,312],{"emptyLinePlaceholder":311},[280,1142,1144,1147,1149,1152,1154,1157,1159,1161,1164,1166],{"class":282,"line":1143},21,[280,1145,1146],{"class":337},"    p ",[280,1148,341],{"class":297},[280,1150,1151],{"class":337}," c",[280,1153,347],{"class":297},[280,1155,1156],{"class":321},"NewParagraph",[280,1158,353],{"class":297},[280,1160,366],{"class":297},[280,1162,1163],{"class":369},"Hello, World!",[280,1165,366],{"class":297},[280,1167,404],{"class":297},[280,1169,1171,1174,1176,1179,1181,1185],{"class":282,"line":1170},22,[280,1172,1173],{"class":337},"    p",[280,1175,347],{"class":297},[280,1177,1178],{"class":321},"SetFontSize",[280,1180,353],{"class":297},[280,1182,1184],{"class":1183},"sbssI","24",[280,1186,404],{"class":297},[280,1188,1190,1192,1194,1196,1198,1200,1203,1205,1207,1209,1211,1213,1215],{"class":282,"line":1189},23,[280,1191,334],{"class":293},[280,1193,338],{"class":337},[280,1195,341],{"class":297},[280,1197,1151],{"class":337},[280,1199,347],{"class":297},[280,1201,1202],{"class":321},"Draw",[280,1204,353],{"class":297},[280,1206,19],{"class":337},[280,1208,507],{"class":297},[280,1210,338],{"class":337},[280,1212,380],{"class":297},[280,1214,383],{"class":297},[280,1216,328],{"class":297},[280,1218,1220,1222,1224,1226,1228,1230],{"class":282,"line":1219},24,[280,1221,391],{"class":337},[280,1223,347],{"class":297},[280,1225,396],{"class":321},[280,1227,353],{"class":297},[280,1229,401],{"class":337},[280,1231,404],{"class":297},[280,1233,1235],{"class":282,"line":1234},25,[280,1236,410],{"class":297},[280,1238,1240],{"class":282,"line":1239},26,[280,1241,312],{"emptyLinePlaceholder":311},[280,1243,1245,1247,1249,1251,1253,1255,1258,1260,1262,1265,1267,1269,1271,1273,1275],{"class":282,"line":1244},27,[280,1246,334],{"class":293},[280,1248,338],{"class":337},[280,1250,341],{"class":297},[280,1252,1151],{"class":337},[280,1254,347],{"class":297},[280,1256,1257],{"class":321},"WriteToFile",[280,1259,353],{"class":297},[280,1261,366],{"class":297},[280,1263,1264],{"class":369},"hello.pdf",[280,1266,366],{"class":297},[280,1268,507],{"class":297},[280,1270,338],{"class":337},[280,1272,380],{"class":297},[280,1274,383],{"class":297},[280,1276,328],{"class":297},[280,1278,1280,1282,1284,1286,1288,1290],{"class":282,"line":1279},28,[280,1281,391],{"class":337},[280,1283,347],{"class":297},[280,1285,396],{"class":321},[280,1287,353],{"class":297},[280,1289,401],{"class":337},[280,1291,404],{"class":297},[280,1293,1295],{"class":282,"line":1294},29,[280,1296,410],{"class":297},[280,1298,1300],{"class":282,"line":1299},30,[280,1301,416],{"class":297},[19,1303,1304],{},[22,1305,1306],{},"After — gpdf:",[271,1308,1310],{"className":273,"code":1309,"language":275,"meta":276,"style":276},"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",[38,1311,1312,1318,1322,1328,1336,1344,1348,1356,1365,1374,1378,1382,1392,1410,1433,1468,1473,1477,1494,1526,1562,1606,1611,1616,1620,1640,1652,1666,1670,1713,1727,1732],{"__ignoreMap":276},[280,1313,1314,1316],{"class":282,"line":283},[280,1315,937],{"class":297},[280,1317,940],{"class":301},[280,1319,1320],{"class":282,"line":290},[280,1321,312],{"emptyLinePlaceholder":311},[280,1323,1324,1326],{"class":282,"line":308},[280,1325,294],{"class":293},[280,1327,951],{"class":297},[280,1329,1330,1332,1334],{"class":282,"line":315},[280,1331,956],{"class":297},[280,1333,959],{"class":301},[280,1335,305],{"class":297},[280,1337,1338,1340,1342],{"class":282,"line":331},[280,1339,956],{"class":297},[280,1341,356],{"class":301},[280,1343,305],{"class":297},[280,1345,1346],{"class":282,"line":388},[280,1347,312],{"emptyLinePlaceholder":311},[280,1349,1350,1352,1354],{"class":282,"line":407},[280,1351,956],{"class":297},[280,1353,550],{"class":301},[280,1355,305],{"class":297},[280,1357,1358,1360,1363],{"class":282,"line":413},[280,1359,956],{"class":297},[280,1361,1362],{"class":301},"github.com/gpdf-dev/gpdf/document",[280,1364,305],{"class":297},[280,1366,1367,1369,1372],{"class":282,"line":993},[280,1368,956],{"class":297},[280,1370,1371],{"class":301},"github.com/gpdf-dev/gpdf/template",[280,1373,305],{"class":297},[280,1375,1376],{"class":282,"line":998},[280,1377,404],{"class":297},[280,1379,1380],{"class":282,"line":1003},[280,1381,312],{"emptyLinePlaceholder":311},[280,1383,1384,1386,1388,1390],{"class":282,"line":1014},[280,1385,318],{"class":297},[280,1387,1090],{"class":321},[280,1389,325],{"class":297},[280,1391,328],{"class":297},[280,1393,1394,1397,1399,1402,1404,1407],{"class":282,"line":1055},[280,1395,1396],{"class":337},"    doc ",[280,1398,341],{"class":297},[280,1400,1401],{"class":337}," gpdf",[280,1403,347],{"class":297},[280,1405,1406],{"class":321},"NewDocument",[280,1408,1409],{"class":297},"(\n",[280,1411,1412,1415,1417,1420,1422,1425,1427,1430],{"class":282,"line":1070},[280,1413,1414],{"class":337},"        gpdf",[280,1416,347],{"class":297},[280,1418,1419],{"class":321},"WithPageSize",[280,1421,353],{"class":297},[280,1423,1424],{"class":337},"document",[280,1426,347],{"class":297},[280,1428,1429],{"class":337},"A4",[280,1431,1432],{"class":297},"),\n",[280,1434,1435,1437,1439,1442,1444,1446,1448,1451,1453,1455,1457,1460,1462,1465],{"class":282,"line":1075},[280,1436,1414],{"class":337},[280,1438,347],{"class":297},[280,1440,1441],{"class":321},"WithMargins",[280,1443,353],{"class":297},[280,1445,1424],{"class":337},[280,1447,347],{"class":297},[280,1449,1450],{"class":321},"UniformEdges",[280,1452,353],{"class":297},[280,1454,1424],{"class":337},[280,1456,347],{"class":297},[280,1458,1459],{"class":321},"Mm",[280,1461,353],{"class":297},[280,1463,1464],{"class":1183},"20",[280,1466,1467],{"class":297},"))),\n",[280,1469,1470],{"class":282,"line":1080},[280,1471,1472],{"class":297},"    )\n",[280,1474,1475],{"class":282,"line":1085},[280,1476,312],{"emptyLinePlaceholder":311},[280,1478,1479,1482,1484,1487,1489,1492],{"class":282,"line":1097},[280,1480,1481],{"class":337},"    page ",[280,1483,341],{"class":297},[280,1485,1486],{"class":337}," doc",[280,1488,347],{"class":297},[280,1490,1491],{"class":321},"AddPage",[280,1493,1113],{"class":297},[280,1495,1496,1499,1501,1504,1507,1511,1514,1517,1519,1522,1524],{"class":282,"line":1116},[280,1497,1498],{"class":337},"    page",[280,1500,347],{"class":297},[280,1502,1503],{"class":321},"AutoRow",[280,1505,1506],{"class":297},"(func(",[280,1508,1510],{"class":1509},"sHdIc","r",[280,1512,1513],{"class":297}," *",[280,1515,1516],{"class":301},"template",[280,1518,347],{"class":297},[280,1520,1521],{"class":301},"RowBuilder",[280,1523,592],{"class":297},[280,1525,328],{"class":297},[280,1527,1528,1531,1533,1536,1538,1541,1543,1546,1549,1551,1553,1555,1558,1560],{"class":282,"line":1138},[280,1529,1530],{"class":337},"        r",[280,1532,347],{"class":297},[280,1534,1535],{"class":321},"Col",[280,1537,353],{"class":297},[280,1539,1540],{"class":1183},"12",[280,1542,444],{"class":297},[280,1544,1545],{"class":297}," func(",[280,1547,1548],{"class":1509},"c",[280,1550,1513],{"class":297},[280,1552,1516],{"class":301},[280,1554,347],{"class":297},[280,1556,1557],{"class":301},"ColBuilder",[280,1559,592],{"class":297},[280,1561,328],{"class":297},[280,1563,1564,1567,1569,1572,1574,1576,1578,1580,1582,1585,1587,1590,1592,1594,1596,1598,1600,1603],{"class":282,"line":1143},[280,1565,1566],{"class":337},"            c",[280,1568,347],{"class":297},[280,1570,1571],{"class":321},"Text",[280,1573,353],{"class":297},[280,1575,366],{"class":297},[280,1577,1163],{"class":369},[280,1579,366],{"class":297},[280,1581,444],{"class":297},[280,1583,1584],{"class":337}," template",[280,1586,347],{"class":297},[280,1588,1589],{"class":321},"FontSize",[280,1591,353],{"class":297},[280,1593,1184],{"class":1183},[280,1595,497],{"class":297},[280,1597,1584],{"class":337},[280,1599,347],{"class":297},[280,1601,1602],{"class":321},"Bold",[280,1604,1605],{"class":297},"())\n",[280,1607,1608],{"class":282,"line":1170},[280,1609,1610],{"class":297},"        })\n",[280,1612,1613],{"class":282,"line":1189},[280,1614,1615],{"class":297},"    })\n",[280,1617,1618],{"class":282,"line":1219},[280,1619,312],{"emptyLinePlaceholder":311},[280,1621,1622,1625,1627,1629,1631,1633,1635,1638],{"class":282,"line":1234},[280,1623,1624],{"class":337},"    data",[280,1626,444],{"class":297},[280,1628,338],{"class":337},[280,1630,341],{"class":297},[280,1632,1486],{"class":337},[280,1634,347],{"class":297},[280,1636,1637],{"class":321},"Generate",[280,1639,1113],{"class":297},[280,1641,1642,1644,1646,1648,1650],{"class":282,"line":1239},[280,1643,334],{"class":293},[280,1645,338],{"class":337},[280,1647,380],{"class":297},[280,1649,383],{"class":297},[280,1651,328],{"class":297},[280,1653,1654,1656,1658,1660,1662,1664],{"class":282,"line":1244},[280,1655,391],{"class":337},[280,1657,347],{"class":297},[280,1659,396],{"class":321},[280,1661,353],{"class":297},[280,1663,401],{"class":337},[280,1665,404],{"class":297},[280,1667,1668],{"class":282,"line":1279},[280,1669,410],{"class":297},[280,1671,1672,1674,1676,1678,1680,1682,1685,1687,1689,1691,1693,1695,1698,1700,1703,1705,1707,1709,1711],{"class":282,"line":1294},[280,1673,334],{"class":293},[280,1675,338],{"class":337},[280,1677,341],{"class":297},[280,1679,452],{"class":337},[280,1681,347],{"class":297},[280,1683,1684],{"class":321},"WriteFile",[280,1686,353],{"class":297},[280,1688,366],{"class":297},[280,1690,1264],{"class":369},[280,1692,366],{"class":297},[280,1694,444],{"class":297},[280,1696,1697],{"class":337}," data",[280,1699,444],{"class":297},[280,1701,1702],{"class":1183}," 0o644",[280,1704,507],{"class":297},[280,1706,338],{"class":337},[280,1708,380],{"class":297},[280,1710,383],{"class":297},[280,1712,328],{"class":297},[280,1714,1715,1717,1719,1721,1723,1725],{"class":282,"line":1299},[280,1716,391],{"class":337},[280,1718,347],{"class":297},[280,1720,396],{"class":321},[280,1722,353],{"class":297},[280,1724,401],{"class":337},[280,1726,404],{"class":297},[280,1728,1730],{"class":282,"line":1729},31,[280,1731,410],{"class":297},[280,1733,1735],{"class":282,"line":1734},32,[280,1736,416],{"class":297},[19,1738,1739,1740,1742,1743,1745],{},"세 가지 차이. ",[38,1741,543],{}," 블록이 사라졌다 — 키 없음, 환경변수 없음. 생성은 mutating 대신 옵션 전달. 텍스트는 자유 ",[38,1744,894],{},"가 아니라 행과 컬럼 안에 들어간다. 그리드가 배치를 하므로 좌표를 고를 필요가 없다.",[14,1747,1749],{"id":1748},"before-after-2-스타일이-적용된-인보이스-항목-테이블","Before / After 2: 스타일이 적용된 인보이스 항목 테이블",[19,1751,1752,1753,1755,1756,1759,1760,768,1763,1766],{},"unipdf의 creator API에서 테이블은 길어진다. ",[38,1754,898],{},"을 만들고, 절대 비율로 ",[38,1757,1758],{},"SetColumnWidths","를 호출하고, ",[38,1761,1762],{},"NewCell",[38,1764,1765],{},"SetContent","로 셀을 하나씩 만들고, 각 셀의 테두리와 정렬을 직접 설정한다.",[19,1768,1769],{},[22,1770,927],{},[271,1772,1774],{"className":273,"code":1773,"language":275,"meta":276,"style":276},"table := c.NewTable(4)\ntable.SetColumnWidths(0.5, 0.15, 0.15, 0.2)\n\nheaderStyle := c.NewTextStyle()\nheaderStyle.Font, _ = model.NewStandard14Font(\"Helvetica-Bold\")\nheaderStyle.FontSize = 11\nheaderStyle.Color = creator.ColorWhite\n\ndrawHeaderCell := func(text string) {\n    cell := table.NewCell()\n    cell.SetBackgroundColor(creator.ColorRGBFromHex(\"#1A237E\"))\n    cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 0.5)\n\n    p := c.NewStyledParagraph()\n    chunk := p.Append(text)\n    chunk.Style = headerStyle\n    cell.SetContent(p)\n}\n\nfor _, h := range []string{\"항목\", \"수량\", \"단가\", \"금액\"} {\n    drawHeaderCell(h)\n}\n\nfor _, row := range items {\n    for _, cellText := range row {\n        cell := table.NewCell()\n        cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 0.3)\n\n        p := c.NewParagraph(cellText)\n        p.SetFontSize(11)\n        cell.SetContent(p)\n    }\n}\n\nif err := c.Draw(table); err != nil {\n    log.Fatal(err)\n}\n",[38,1775,1776,1797,1826,1830,1846,1882,1896,1914,1918,1937,1953,1984,2018,2022,2037,2058,2073,2087,2091,2095,2160,2172,2176,2180,2201,2221,2236,2268,2272,2292,2308,2322,2326,2331,2336,2366,2382],{"__ignoreMap":276},[280,1777,1778,1781,1783,1785,1787,1790,1792,1795],{"class":282,"line":283},[280,1779,1780],{"class":337},"table ",[280,1782,341],{"class":297},[280,1784,1151],{"class":337},[280,1786,347],{"class":297},[280,1788,1789],{"class":321},"NewTable",[280,1791,353],{"class":297},[280,1793,1794],{"class":1183},"4",[280,1796,404],{"class":297},[280,1798,1799,1801,1803,1805,1807,1810,1812,1815,1817,1819,1821,1824],{"class":282,"line":290},[280,1800,127],{"class":337},[280,1802,347],{"class":297},[280,1804,1758],{"class":321},[280,1806,353],{"class":297},[280,1808,1809],{"class":1183},"0.5",[280,1811,444],{"class":297},[280,1813,1814],{"class":1183}," 0.15",[280,1816,444],{"class":297},[280,1818,1814],{"class":1183},[280,1820,444],{"class":297},[280,1822,1823],{"class":1183}," 0.2",[280,1825,404],{"class":297},[280,1827,1828],{"class":282,"line":308},[280,1829,312],{"emptyLinePlaceholder":311},[280,1831,1832,1835,1837,1839,1841,1844],{"class":282,"line":315},[280,1833,1834],{"class":337},"headerStyle ",[280,1836,341],{"class":297},[280,1838,1151],{"class":337},[280,1840,347],{"class":297},[280,1842,1843],{"class":321},"NewTextStyle",[280,1845,1113],{"class":297},[280,1847,1848,1851,1853,1856,1858,1860,1863,1866,1868,1871,1873,1875,1878,1880],{"class":282,"line":331},[280,1849,1850],{"class":337},"headerStyle",[280,1852,347],{"class":297},[280,1854,1855],{"class":337},"Font",[280,1857,444],{"class":297},[280,1859,447],{"class":337},[280,1861,1862],{"class":297},"=",[280,1864,1865],{"class":337}," model",[280,1867,347],{"class":297},[280,1869,1870],{"class":321},"NewStandard14Font",[280,1872,353],{"class":297},[280,1874,366],{"class":297},[280,1876,1877],{"class":369},"Helvetica-Bold",[280,1879,366],{"class":297},[280,1881,404],{"class":297},[280,1883,1884,1886,1888,1891,1893],{"class":282,"line":388},[280,1885,1850],{"class":337},[280,1887,347],{"class":297},[280,1889,1890],{"class":337},"FontSize ",[280,1892,1862],{"class":297},[280,1894,1895],{"class":1183}," 11\n",[280,1897,1898,1900,1902,1905,1907,1909,1911],{"class":282,"line":407},[280,1899,1850],{"class":337},[280,1901,347],{"class":297},[280,1903,1904],{"class":337},"Color ",[280,1906,1862],{"class":297},[280,1908,1105],{"class":337},[280,1910,347],{"class":297},[280,1912,1913],{"class":337},"ColorWhite\n",[280,1915,1916],{"class":282,"line":413},[280,1917,312],{"emptyLinePlaceholder":311},[280,1919,1920,1923,1925,1927,1930,1933,1935],{"class":282,"line":993},[280,1921,1922],{"class":337},"drawHeaderCell ",[280,1924,341],{"class":297},[280,1926,1545],{"class":297},[280,1928,1929],{"class":1509},"text",[280,1931,1932],{"class":488}," string",[280,1934,592],{"class":297},[280,1936,328],{"class":297},[280,1938,1939,1942,1944,1947,1949,1951],{"class":282,"line":998},[280,1940,1941],{"class":337},"    cell ",[280,1943,341],{"class":297},[280,1945,1946],{"class":337}," table",[280,1948,347],{"class":297},[280,1950,1762],{"class":321},[280,1952,1113],{"class":297},[280,1954,1955,1958,1960,1963,1965,1967,1969,1972,1974,1976,1979,1981],{"class":282,"line":1003},[280,1956,1957],{"class":337},"    cell",[280,1959,347],{"class":297},[280,1961,1962],{"class":321},"SetBackgroundColor",[280,1964,353],{"class":297},[280,1966,44],{"class":337},[280,1968,347],{"class":297},[280,1970,1971],{"class":321},"ColorRGBFromHex",[280,1973,353],{"class":297},[280,1975,366],{"class":297},[280,1977,1978],{"class":369},"#1A237E",[280,1980,366],{"class":297},[280,1982,1983],{"class":297},"))\n",[280,1985,1986,1988,1990,1993,1995,1997,1999,2002,2004,2006,2008,2011,2013,2016],{"class":282,"line":1014},[280,1987,1957],{"class":337},[280,1989,347],{"class":297},[280,1991,1992],{"class":321},"SetBorder",[280,1994,353],{"class":297},[280,1996,44],{"class":337},[280,1998,347],{"class":297},[280,2000,2001],{"class":337},"CellBorderSideAll",[280,2003,444],{"class":297},[280,2005,1105],{"class":337},[280,2007,347],{"class":297},[280,2009,2010],{"class":337},"CellBorderStyleSingle",[280,2012,444],{"class":297},[280,2014,2015],{"class":1183}," 0.5",[280,2017,404],{"class":297},[280,2019,2020],{"class":282,"line":1055},[280,2021,312],{"emptyLinePlaceholder":311},[280,2023,2024,2026,2028,2030,2032,2035],{"class":282,"line":1070},[280,2025,1146],{"class":337},[280,2027,341],{"class":297},[280,2029,1151],{"class":337},[280,2031,347],{"class":297},[280,2033,2034],{"class":321},"NewStyledParagraph",[280,2036,1113],{"class":297},[280,2038,2039,2042,2044,2047,2049,2052,2054,2056],{"class":282,"line":1075},[280,2040,2041],{"class":337},"    chunk ",[280,2043,341],{"class":297},[280,2045,2046],{"class":337}," p",[280,2048,347],{"class":297},[280,2050,2051],{"class":321},"Append",[280,2053,353],{"class":297},[280,2055,1929],{"class":337},[280,2057,404],{"class":297},[280,2059,2060,2063,2065,2068,2070],{"class":282,"line":1080},[280,2061,2062],{"class":337},"    chunk",[280,2064,347],{"class":297},[280,2066,2067],{"class":337},"Style ",[280,2069,1862],{"class":297},[280,2071,2072],{"class":337}," headerStyle\n",[280,2074,2075,2077,2079,2081,2083,2085],{"class":282,"line":1085},[280,2076,1957],{"class":337},[280,2078,347],{"class":297},[280,2080,1765],{"class":321},[280,2082,353],{"class":297},[280,2084,19],{"class":337},[280,2086,404],{"class":297},[280,2088,2089],{"class":282,"line":1097},[280,2090,416],{"class":297},[280,2092,2093],{"class":282,"line":1116},[280,2094,312],{"emptyLinePlaceholder":311},[280,2096,2097,2100,2103,2105,2108,2110,2113,2116,2118,2121,2123,2126,2128,2130,2132,2135,2137,2139,2141,2144,2146,2148,2150,2153,2155,2158],{"class":282,"line":1138},[280,2098,2099],{"class":293},"for",[280,2101,2102],{"class":337}," _",[280,2104,444],{"class":297},[280,2106,2107],{"class":337}," h ",[280,2109,341],{"class":297},[280,2111,2112],{"class":293}," range",[280,2114,2115],{"class":297}," []",[280,2117,489],{"class":488},[280,2119,2120],{"class":297},"{",[280,2122,366],{"class":297},[280,2124,2125],{"class":369},"항목",[280,2127,366],{"class":297},[280,2129,444],{"class":297},[280,2131,298],{"class":297},[280,2133,2134],{"class":369},"수량",[280,2136,366],{"class":297},[280,2138,444],{"class":297},[280,2140,298],{"class":297},[280,2142,2143],{"class":369},"단가",[280,2145,366],{"class":297},[280,2147,444],{"class":297},[280,2149,298],{"class":297},[280,2151,2152],{"class":369},"금액",[280,2154,366],{"class":297},[280,2156,2157],{"class":297},"}",[280,2159,328],{"class":297},[280,2161,2162,2165,2167,2170],{"class":282,"line":1143},[280,2163,2164],{"class":321},"    drawHeaderCell",[280,2166,353],{"class":297},[280,2168,2169],{"class":337},"h",[280,2171,404],{"class":297},[280,2173,2174],{"class":282,"line":1170},[280,2175,416],{"class":297},[280,2177,2178],{"class":282,"line":1189},[280,2179,312],{"emptyLinePlaceholder":311},[280,2181,2182,2184,2186,2188,2191,2193,2195,2198],{"class":282,"line":1219},[280,2183,2099],{"class":293},[280,2185,2102],{"class":337},[280,2187,444],{"class":297},[280,2189,2190],{"class":337}," row ",[280,2192,341],{"class":297},[280,2194,2112],{"class":293},[280,2196,2197],{"class":337}," items ",[280,2199,2200],{"class":297},"{\n",[280,2202,2203,2206,2208,2210,2213,2215,2217,2219],{"class":282,"line":1234},[280,2204,2205],{"class":293},"    for",[280,2207,2102],{"class":337},[280,2209,444],{"class":297},[280,2211,2212],{"class":337}," cellText ",[280,2214,341],{"class":297},[280,2216,2112],{"class":293},[280,2218,2190],{"class":337},[280,2220,2200],{"class":297},[280,2222,2223,2226,2228,2230,2232,2234],{"class":282,"line":1239},[280,2224,2225],{"class":337},"        cell ",[280,2227,341],{"class":297},[280,2229,1946],{"class":337},[280,2231,347],{"class":297},[280,2233,1762],{"class":321},[280,2235,1113],{"class":297},[280,2237,2238,2241,2243,2245,2247,2249,2251,2253,2255,2257,2259,2261,2263,2266],{"class":282,"line":1244},[280,2239,2240],{"class":337},"        cell",[280,2242,347],{"class":297},[280,2244,1992],{"class":321},[280,2246,353],{"class":297},[280,2248,44],{"class":337},[280,2250,347],{"class":297},[280,2252,2001],{"class":337},[280,2254,444],{"class":297},[280,2256,1105],{"class":337},[280,2258,347],{"class":297},[280,2260,2010],{"class":337},[280,2262,444],{"class":297},[280,2264,2265],{"class":1183}," 0.3",[280,2267,404],{"class":297},[280,2269,2270],{"class":282,"line":1279},[280,2271,312],{"emptyLinePlaceholder":311},[280,2273,2274,2277,2279,2281,2283,2285,2287,2290],{"class":282,"line":1294},[280,2275,2276],{"class":337},"        p ",[280,2278,341],{"class":297},[280,2280,1151],{"class":337},[280,2282,347],{"class":297},[280,2284,1156],{"class":321},[280,2286,353],{"class":297},[280,2288,2289],{"class":337},"cellText",[280,2291,404],{"class":297},[280,2293,2294,2297,2299,2301,2303,2306],{"class":282,"line":1299},[280,2295,2296],{"class":337},"        p",[280,2298,347],{"class":297},[280,2300,1178],{"class":321},[280,2302,353],{"class":297},[280,2304,2305],{"class":1183},"11",[280,2307,404],{"class":297},[280,2309,2310,2312,2314,2316,2318,2320],{"class":282,"line":1729},[280,2311,2240],{"class":337},[280,2313,347],{"class":297},[280,2315,1765],{"class":321},[280,2317,353],{"class":297},[280,2319,19],{"class":337},[280,2321,404],{"class":297},[280,2323,2324],{"class":282,"line":1734},[280,2325,410],{"class":297},[280,2327,2329],{"class":282,"line":2328},33,[280,2330,416],{"class":297},[280,2332,2334],{"class":282,"line":2333},34,[280,2335,312],{"emptyLinePlaceholder":311},[280,2337,2339,2342,2344,2346,2348,2350,2352,2354,2356,2358,2360,2362,2364],{"class":282,"line":2338},35,[280,2340,2341],{"class":293},"if",[280,2343,338],{"class":337},[280,2345,341],{"class":297},[280,2347,1151],{"class":337},[280,2349,347],{"class":297},[280,2351,1202],{"class":321},[280,2353,353],{"class":297},[280,2355,127],{"class":337},[280,2357,507],{"class":297},[280,2359,338],{"class":337},[280,2361,380],{"class":297},[280,2363,383],{"class":297},[280,2365,328],{"class":297},[280,2367,2369,2372,2374,2376,2378,2380],{"class":282,"line":2368},36,[280,2370,2371],{"class":337},"    log",[280,2373,347],{"class":297},[280,2375,396],{"class":321},[280,2377,353],{"class":297},[280,2379,401],{"class":337},[280,2381,404],{"class":297},[280,2383,2385],{"class":282,"line":2384},37,[280,2386,416],{"class":297},[19,2388,2389],{},"테두리, 셀별 content, 헤더 그리는 루프 — 전부 기계적인 작업이다.",[19,2391,2392],{},[22,2393,1306],{},[271,2395,2397],{"className":273,"code":2396,"language":275,"meta":276,"style":276},"page.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Table(\n            []string{\"항목\", \"수량\", \"단가\", \"금액\"},\n            [][]string{\n                {\"프론트엔드 개발\", \"40 시간\", \"₩150,000\", \"₩6,000,000\"},\n                {\"백엔드 개발\",    \"60 시간\", \"₩150,000\", \"₩9,000,000\"},\n                {\"UI 디자인\",      \"20 시간\", \"₩120,000\", \"₩2,400,000\"},\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",[38,2398,2399,2424,2455,2466,2508,2517,2558,2597,2638,2643,2674,2685,2697,2718,2744,2749,2773,2778,2782],{"__ignoreMap":276},[280,2400,2401,2404,2406,2408,2410,2412,2414,2416,2418,2420,2422],{"class":282,"line":283},[280,2402,2403],{"class":337},"page",[280,2405,347],{"class":297},[280,2407,1503],{"class":321},[280,2409,1506],{"class":297},[280,2411,1510],{"class":1509},[280,2413,1513],{"class":297},[280,2415,1516],{"class":301},[280,2417,347],{"class":297},[280,2419,1521],{"class":301},[280,2421,592],{"class":297},[280,2423,328],{"class":297},[280,2425,2426,2429,2431,2433,2435,2437,2439,2441,2443,2445,2447,2449,2451,2453],{"class":282,"line":290},[280,2427,2428],{"class":337},"    r",[280,2430,347],{"class":297},[280,2432,1535],{"class":321},[280,2434,353],{"class":297},[280,2436,1540],{"class":1183},[280,2438,444],{"class":297},[280,2440,1545],{"class":297},[280,2442,1548],{"class":1509},[280,2444,1513],{"class":297},[280,2446,1516],{"class":301},[280,2448,347],{"class":297},[280,2450,1557],{"class":301},[280,2452,592],{"class":297},[280,2454,328],{"class":297},[280,2456,2457,2460,2462,2464],{"class":282,"line":308},[280,2458,2459],{"class":337},"        c",[280,2461,347],{"class":297},[280,2463,898],{"class":321},[280,2465,1409],{"class":297},[280,2467,2468,2471,2473,2475,2477,2479,2481,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501,2503,2505],{"class":282,"line":315},[280,2469,2470],{"class":297},"            []",[280,2472,489],{"class":488},[280,2474,2120],{"class":297},[280,2476,366],{"class":297},[280,2478,2125],{"class":369},[280,2480,366],{"class":297},[280,2482,444],{"class":297},[280,2484,298],{"class":297},[280,2486,2134],{"class":369},[280,2488,366],{"class":297},[280,2490,444],{"class":297},[280,2492,298],{"class":297},[280,2494,2143],{"class":369},[280,2496,366],{"class":297},[280,2498,444],{"class":297},[280,2500,298],{"class":297},[280,2502,2152],{"class":369},[280,2504,366],{"class":297},[280,2506,2507],{"class":297},"},\n",[280,2509,2510,2513,2515],{"class":282,"line":331},[280,2511,2512],{"class":297},"            [][]",[280,2514,489],{"class":488},[280,2516,2200],{"class":297},[280,2518,2519,2522,2524,2527,2529,2531,2533,2536,2538,2540,2542,2545,2547,2549,2551,2554,2556],{"class":282,"line":388},[280,2520,2521],{"class":297},"                {",[280,2523,366],{"class":297},[280,2525,2526],{"class":369},"프론트엔드 개발",[280,2528,366],{"class":297},[280,2530,444],{"class":297},[280,2532,298],{"class":297},[280,2534,2535],{"class":369},"40 시간",[280,2537,366],{"class":297},[280,2539,444],{"class":297},[280,2541,298],{"class":297},[280,2543,2544],{"class":369},"₩150,000",[280,2546,366],{"class":297},[280,2548,444],{"class":297},[280,2550,298],{"class":297},[280,2552,2553],{"class":369},"₩6,000,000",[280,2555,366],{"class":297},[280,2557,2507],{"class":297},[280,2559,2560,2562,2564,2567,2569,2571,2573,2576,2578,2580,2582,2584,2586,2588,2590,2593,2595],{"class":282,"line":407},[280,2561,2521],{"class":297},[280,2563,366],{"class":297},[280,2565,2566],{"class":369},"백엔드 개발",[280,2568,366],{"class":297},[280,2570,444],{"class":297},[280,2572,956],{"class":297},[280,2574,2575],{"class":369},"60 시간",[280,2577,366],{"class":297},[280,2579,444],{"class":297},[280,2581,298],{"class":297},[280,2583,2544],{"class":369},[280,2585,366],{"class":297},[280,2587,444],{"class":297},[280,2589,298],{"class":297},[280,2591,2592],{"class":369},"₩9,000,000",[280,2594,366],{"class":297},[280,2596,2507],{"class":297},[280,2598,2599,2601,2603,2606,2608,2610,2613,2616,2618,2620,2622,2625,2627,2629,2631,2634,2636],{"class":282,"line":413},[280,2600,2521],{"class":297},[280,2602,366],{"class":297},[280,2604,2605],{"class":369},"UI 디자인",[280,2607,366],{"class":297},[280,2609,444],{"class":297},[280,2611,2612],{"class":297},"      \"",[280,2614,2615],{"class":369},"20 시간",[280,2617,366],{"class":297},[280,2619,444],{"class":297},[280,2621,298],{"class":297},[280,2623,2624],{"class":369},"₩120,000",[280,2626,366],{"class":297},[280,2628,444],{"class":297},[280,2630,298],{"class":297},[280,2632,2633],{"class":369},"₩2,400,000",[280,2635,366],{"class":297},[280,2637,2507],{"class":297},[280,2639,2640],{"class":282,"line":993},[280,2641,2642],{"class":297},"            },\n",[280,2644,2645,2648,2650,2653,2655,2658,2660,2663,2665,2667,2669,2672],{"class":282,"line":998},[280,2646,2647],{"class":337},"            template",[280,2649,347],{"class":297},[280,2651,2652],{"class":321},"ColumnWidths",[280,2654,353],{"class":297},[280,2656,2657],{"class":1183},"50",[280,2659,444],{"class":297},[280,2661,2662],{"class":1183}," 15",[280,2664,444],{"class":297},[280,2666,2662],{"class":1183},[280,2668,444],{"class":297},[280,2670,2671],{"class":1183}," 20",[280,2673,1432],{"class":297},[280,2675,2676,2678,2680,2683],{"class":282,"line":1003},[280,2677,2647],{"class":337},[280,2679,347],{"class":297},[280,2681,2682],{"class":321},"TableHeaderStyle",[280,2684,1409],{"class":297},[280,2686,2687,2690,2692,2694],{"class":282,"line":1014},[280,2688,2689],{"class":337},"                template",[280,2691,347],{"class":297},[280,2693,1602],{"class":321},[280,2695,2696],{"class":297},"(),\n",[280,2698,2699,2701,2703,2706,2708,2711,2713,2716],{"class":282,"line":1055},[280,2700,2689],{"class":337},[280,2702,347],{"class":297},[280,2704,2705],{"class":321},"TextColor",[280,2707,353],{"class":297},[280,2709,2710],{"class":337},"pdf",[280,2712,347],{"class":297},[280,2714,2715],{"class":337},"White",[280,2717,1432],{"class":297},[280,2719,2720,2722,2724,2727,2729,2731,2733,2736,2738,2741],{"class":282,"line":1070},[280,2721,2689],{"class":337},[280,2723,347],{"class":297},[280,2725,2726],{"class":321},"BgColor",[280,2728,353],{"class":297},[280,2730,2710],{"class":337},[280,2732,347],{"class":297},[280,2734,2735],{"class":321},"RGBHex",[280,2737,353],{"class":297},[280,2739,2740],{"class":1183},"0x1A237E",[280,2742,2743],{"class":297},")),\n",[280,2745,2746],{"class":282,"line":1075},[280,2747,2748],{"class":297},"            ),\n",[280,2750,2751,2753,2755,2758,2760,2762,2764,2766,2768,2771],{"class":282,"line":1080},[280,2752,2647],{"class":337},[280,2754,347],{"class":297},[280,2756,2757],{"class":321},"TableStripe",[280,2759,353],{"class":297},[280,2761,2710],{"class":337},[280,2763,347],{"class":297},[280,2765,2735],{"class":321},[280,2767,353],{"class":297},[280,2769,2770],{"class":1183},"0xF5F5F5",[280,2772,2743],{"class":297},[280,2774,2775],{"class":282,"line":1085},[280,2776,2777],{"class":297},"        )\n",[280,2779,2780],{"class":282,"line":1097},[280,2781,1615],{"class":297},[280,2783,2784],{"class":282,"line":1116},[280,2785,2786],{"class":297},"})\n",[19,2788,2789,25,2791,2794,2795,2798],{},[38,2790,2652],{},[22,2792,2793],{},"테이블이 위치한 컬럼 너비의 퍼센트","다. 페이지의 절대 비율이 아니다. 같은 테이블을 ",[38,2796,2797],{},"r.Col(6, ...)","에 넣으면 퍼센트는 그대로 — 테이블이 행의 절반을 차지하고 컬럼이 같은 비율로 재분배된다. 페이지 분할은 자동, 본문이 하단 마진을 넘으면 다음 페이지에 헤더가 자동으로 반복된다.",[19,2800,2801,2802,2805,2806,2809],{},"수치 하나만 짚어두자. 100행 인보이스 벤치에서 unipdf의 Table은 렌더링당 약 ",[22,2803,2804],{},"8.6 ms",". gpdf는 같은 작업을 ",[22,2807,2808],{},"108 µs"," (약 80배). 레이아웃 엔진이 각 행을 한 번 측정하고 단일 패스로 페이지를 쓰는 구조 덕분이다. 인보이스 한 장으로는 안 보이지만, cron으로 대량 배치를 돌리면 \"큐가 필요한가\"의 경계가 바뀐다.",[19,2811,2812,2813,2816],{},"전자세금계산서나 전자문서 인증 (KISA) 양식의 항목들은 레이아웃으로는 gpdf에서 그대로 표현 가능하다. 타임스탬프가 필요하면 ",[38,2814,2815],{},"gpdf.SignDocument","의 RFC 3161 TSA 옵션 쪽에서 처리한다.",[14,2818,2820],{"id":2819},"before-after-3-composite-font-의식-없이-한국어","Before / After 3: composite font 의식 없이 한국어",[19,2822,2823],{},"unipdf도 CJK는 지원하지만 경로가 장황하다. 디스크의 TTF로 composite font를 구성하고, style font로 설정하고, 모든 paragraph에 통과시킨다. 폴백을 원하면 직접 배선한다.",[19,2825,2826],{},[22,2827,927],{},[271,2829,2831],{"className":273,"code":2830,"language":275,"meta":276,"style":276},"font, err := model.NewCompositePdfFontFromTTFFile(\"NotoSansKR-Regular.ttf\")\nif err != nil {\n    log.Fatal(err)\n}\n\nc := creator.New()\nc.SetPageSize(creator.PageSizeA4)\n\nstyle := c.NewTextStyle()\nstyle.Font = font\nstyle.FontSize = 14\n\np := c.NewStyledParagraph()\np.Append(\"안녕하세요, 세계.\").Style = style\nif err := c.Draw(p); err != nil {\n    log.Fatal(err)\n}\n\nc.WriteToFile(\"ko.pdf\")\n",[38,2832,2833,2862,2874,2888,2892,2896,2911,2929,2933,2948,2963,2976,2980,2995,3022,3050,3064,3068,3072],{"__ignoreMap":276},[280,2834,2835,2838,2840,2842,2844,2846,2848,2851,2853,2855,2858,2860],{"class":282,"line":283},[280,2836,2837],{"class":337},"font",[280,2839,444],{"class":297},[280,2841,338],{"class":337},[280,2843,341],{"class":297},[280,2845,1865],{"class":337},[280,2847,347],{"class":297},[280,2849,2850],{"class":321},"NewCompositePdfFontFromTTFFile",[280,2852,353],{"class":297},[280,2854,366],{"class":297},[280,2856,2857],{"class":369},"NotoSansKR-Regular.ttf",[280,2859,366],{"class":297},[280,2861,404],{"class":297},[280,2863,2864,2866,2868,2870,2872],{"class":282,"line":290},[280,2865,2341],{"class":293},[280,2867,338],{"class":337},[280,2869,380],{"class":297},[280,2871,383],{"class":297},[280,2873,328],{"class":297},[280,2875,2876,2878,2880,2882,2884,2886],{"class":282,"line":308},[280,2877,2371],{"class":337},[280,2879,347],{"class":297},[280,2881,396],{"class":321},[280,2883,353],{"class":297},[280,2885,401],{"class":337},[280,2887,404],{"class":297},[280,2889,2890],{"class":282,"line":315},[280,2891,416],{"class":297},[280,2893,2894],{"class":282,"line":331},[280,2895,312],{"emptyLinePlaceholder":311},[280,2897,2898,2901,2903,2905,2907,2909],{"class":282,"line":388},[280,2899,2900],{"class":337},"c ",[280,2902,341],{"class":297},[280,2904,1105],{"class":337},[280,2906,347],{"class":297},[280,2908,1110],{"class":321},[280,2910,1113],{"class":297},[280,2912,2913,2915,2917,2919,2921,2923,2925,2927],{"class":282,"line":407},[280,2914,1548],{"class":337},[280,2916,347],{"class":297},[280,2918,1124],{"class":321},[280,2920,353],{"class":297},[280,2922,44],{"class":337},[280,2924,347],{"class":297},[280,2926,1133],{"class":337},[280,2928,404],{"class":297},[280,2930,2931],{"class":282,"line":413},[280,2932,312],{"emptyLinePlaceholder":311},[280,2934,2935,2938,2940,2942,2944,2946],{"class":282,"line":993},[280,2936,2937],{"class":337},"style ",[280,2939,341],{"class":297},[280,2941,1151],{"class":337},[280,2943,347],{"class":297},[280,2945,1843],{"class":321},[280,2947,1113],{"class":297},[280,2949,2950,2953,2955,2958,2960],{"class":282,"line":998},[280,2951,2952],{"class":337},"style",[280,2954,347],{"class":297},[280,2956,2957],{"class":337},"Font ",[280,2959,1862],{"class":297},[280,2961,2962],{"class":337}," font\n",[280,2964,2965,2967,2969,2971,2973],{"class":282,"line":1003},[280,2966,2952],{"class":337},[280,2968,347],{"class":297},[280,2970,1890],{"class":337},[280,2972,1862],{"class":297},[280,2974,2975],{"class":1183}," 14\n",[280,2977,2978],{"class":282,"line":1014},[280,2979,312],{"emptyLinePlaceholder":311},[280,2981,2982,2985,2987,2989,2991,2993],{"class":282,"line":1055},[280,2983,2984],{"class":337},"p ",[280,2986,341],{"class":297},[280,2988,1151],{"class":337},[280,2990,347],{"class":297},[280,2992,2034],{"class":321},[280,2994,1113],{"class":297},[280,2996,2997,2999,3001,3003,3005,3007,3010,3012,3015,3017,3019],{"class":282,"line":1070},[280,2998,19],{"class":337},[280,3000,347],{"class":297},[280,3002,2051],{"class":321},[280,3004,353],{"class":297},[280,3006,366],{"class":297},[280,3008,3009],{"class":369},"안녕하세요, 세계.",[280,3011,366],{"class":297},[280,3013,3014],{"class":297},").",[280,3016,2067],{"class":337},[280,3018,1862],{"class":297},[280,3020,3021],{"class":337}," style\n",[280,3023,3024,3026,3028,3030,3032,3034,3036,3038,3040,3042,3044,3046,3048],{"class":282,"line":1075},[280,3025,2341],{"class":293},[280,3027,338],{"class":337},[280,3029,341],{"class":297},[280,3031,1151],{"class":337},[280,3033,347],{"class":297},[280,3035,1202],{"class":321},[280,3037,353],{"class":297},[280,3039,19],{"class":337},[280,3041,507],{"class":297},[280,3043,338],{"class":337},[280,3045,380],{"class":297},[280,3047,383],{"class":297},[280,3049,328],{"class":297},[280,3051,3052,3054,3056,3058,3060,3062],{"class":282,"line":1080},[280,3053,2371],{"class":337},[280,3055,347],{"class":297},[280,3057,396],{"class":321},[280,3059,353],{"class":297},[280,3061,401],{"class":337},[280,3063,404],{"class":297},[280,3065,3066],{"class":282,"line":1085},[280,3067,416],{"class":297},[280,3069,3070],{"class":282,"line":1097},[280,3071,312],{"emptyLinePlaceholder":311},[280,3073,3074,3076,3078,3080,3082,3084,3087,3089],{"class":282,"line":1116},[280,3075,1548],{"class":337},[280,3077,347],{"class":297},[280,3079,1257],{"class":321},[280,3081,353],{"class":297},[280,3083,366],{"class":297},[280,3085,3086],{"class":369},"ko.pdf",[280,3088,366],{"class":297},[280,3090,404],{"class":297},[19,3092,3093,3094,3096],{},"TTF는 지정한 경로에 런타임 시점, 바이너리가 도는 호스트에 존재해야 한다. 컨테이너 이미지에 폰트를 같이 넣어야 한다. ",[38,3095,2850],{},"은 폰트를 사용하는 draw 호출 전에 발생해야 하므로 글로벌에 두거나 의존성으로 들고 다녀야 한다.",[19,3098,3099],{},[22,3100,1306],{},[271,3102,3104],{"className":273,"code":3103,"language":275,"meta":276,"style":276},"package main\n\nimport (\n    _ \"embed\"\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\n//go:embed NotoSansKR-Regular.ttf\nvar notoKR []byte\n\nfunc main() {\n    doc := gpdf.NewDocument(\n        gpdf.WithPageSize(document.A4),\n        gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n        gpdf.WithFont(\"NotoSansKR\", notoKR),\n        gpdf.WithDefaultFont(\"NotoSansKR\", 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(\"서울특별시 강남구 테헤란로 152\")\n        })\n    })\n\n    data, _ := doc.Generate()\n    if err := os.WriteFile(\"ko.pdf\", data, 0o644); err != nil {\n        log.Fatal(err)\n    }\n}\n",[38,3105,3106,3112,3116,3122,3134,3142,3150,3154,3162,3170,3178,3182,3186,3191,3205,3209,3219,3233,3251,3281,3306,3330,3334,3338,3352,3376,3406,3424,3443,3462,3466,3470,3474,3492,3532,3546,3550],{"__ignoreMap":276},[280,3107,3108,3110],{"class":282,"line":283},[280,3109,937],{"class":297},[280,3111,940],{"class":301},[280,3113,3114],{"class":282,"line":290},[280,3115,312],{"emptyLinePlaceholder":311},[280,3117,3118,3120],{"class":282,"line":308},[280,3119,294],{"class":293},[280,3121,951],{"class":297},[280,3123,3124,3127,3129,3132],{"class":282,"line":315},[280,3125,3126],{"class":337},"    _ ",[280,3128,366],{"class":297},[280,3130,3131],{"class":301},"embed",[280,3133,305],{"class":297},[280,3135,3136,3138,3140],{"class":282,"line":331},[280,3137,956],{"class":297},[280,3139,959],{"class":301},[280,3141,305],{"class":297},[280,3143,3144,3146,3148],{"class":282,"line":388},[280,3145,956],{"class":297},[280,3147,356],{"class":301},[280,3149,305],{"class":297},[280,3151,3152],{"class":282,"line":407},[280,3153,312],{"emptyLinePlaceholder":311},[280,3155,3156,3158,3160],{"class":282,"line":413},[280,3157,956],{"class":297},[280,3159,550],{"class":301},[280,3161,305],{"class":297},[280,3163,3164,3166,3168],{"class":282,"line":993},[280,3165,956],{"class":297},[280,3167,1362],{"class":301},[280,3169,305],{"class":297},[280,3171,3172,3174,3176],{"class":282,"line":998},[280,3173,956],{"class":297},[280,3175,1371],{"class":301},[280,3177,305],{"class":297},[280,3179,3180],{"class":282,"line":1003},[280,3181,404],{"class":297},[280,3183,3184],{"class":282,"line":1014},[280,3185,312],{"emptyLinePlaceholder":311},[280,3187,3188],{"class":282,"line":1055},[280,3189,3190],{"class":286},"//go:embed NotoSansKR-Regular.ttf\n",[280,3192,3193,3196,3199,3202],{"class":282,"line":1070},[280,3194,3195],{"class":297},"var",[280,3197,3198],{"class":337}," notoKR ",[280,3200,3201],{"class":297},"[]",[280,3203,3204],{"class":488},"byte\n",[280,3206,3207],{"class":282,"line":1075},[280,3208,312],{"emptyLinePlaceholder":311},[280,3210,3211,3213,3215,3217],{"class":282,"line":1080},[280,3212,318],{"class":297},[280,3214,1090],{"class":321},[280,3216,325],{"class":297},[280,3218,328],{"class":297},[280,3220,3221,3223,3225,3227,3229,3231],{"class":282,"line":1085},[280,3222,1396],{"class":337},[280,3224,341],{"class":297},[280,3226,1401],{"class":337},[280,3228,347],{"class":297},[280,3230,1406],{"class":321},[280,3232,1409],{"class":297},[280,3234,3235,3237,3239,3241,3243,3245,3247,3249],{"class":282,"line":1097},[280,3236,1414],{"class":337},[280,3238,347],{"class":297},[280,3240,1419],{"class":321},[280,3242,353],{"class":297},[280,3244,1424],{"class":337},[280,3246,347],{"class":297},[280,3248,1429],{"class":337},[280,3250,1432],{"class":297},[280,3252,3253,3255,3257,3259,3261,3263,3265,3267,3269,3271,3273,3275,3277,3279],{"class":282,"line":1116},[280,3254,1414],{"class":337},[280,3256,347],{"class":297},[280,3258,1441],{"class":321},[280,3260,353],{"class":297},[280,3262,1424],{"class":337},[280,3264,347],{"class":297},[280,3266,1450],{"class":321},[280,3268,353],{"class":297},[280,3270,1424],{"class":337},[280,3272,347],{"class":297},[280,3274,1459],{"class":321},[280,3276,353],{"class":297},[280,3278,1464],{"class":1183},[280,3280,1467],{"class":297},[280,3282,3283,3285,3287,3290,3292,3294,3297,3299,3301,3304],{"class":282,"line":1138},[280,3284,1414],{"class":337},[280,3286,347],{"class":297},[280,3288,3289],{"class":321},"WithFont",[280,3291,353],{"class":297},[280,3293,366],{"class":297},[280,3295,3296],{"class":369},"NotoSansKR",[280,3298,366],{"class":297},[280,3300,444],{"class":297},[280,3302,3303],{"class":337}," notoKR",[280,3305,1432],{"class":297},[280,3307,3308,3310,3312,3315,3317,3319,3321,3323,3325,3328],{"class":282,"line":1143},[280,3309,1414],{"class":337},[280,3311,347],{"class":297},[280,3313,3314],{"class":321},"WithDefaultFont",[280,3316,353],{"class":297},[280,3318,366],{"class":297},[280,3320,3296],{"class":369},[280,3322,366],{"class":297},[280,3324,444],{"class":297},[280,3326,3327],{"class":1183}," 14",[280,3329,1432],{"class":297},[280,3331,3332],{"class":282,"line":1170},[280,3333,1472],{"class":297},[280,3335,3336],{"class":282,"line":1189},[280,3337,312],{"emptyLinePlaceholder":311},[280,3339,3340,3342,3344,3346,3348,3350],{"class":282,"line":1219},[280,3341,1481],{"class":337},[280,3343,341],{"class":297},[280,3345,1486],{"class":337},[280,3347,347],{"class":297},[280,3349,1491],{"class":321},[280,3351,1113],{"class":297},[280,3353,3354,3356,3358,3360,3362,3364,3366,3368,3370,3372,3374],{"class":282,"line":1234},[280,3355,1498],{"class":337},[280,3357,347],{"class":297},[280,3359,1503],{"class":321},[280,3361,1506],{"class":297},[280,3363,1510],{"class":1509},[280,3365,1513],{"class":297},[280,3367,1516],{"class":301},[280,3369,347],{"class":297},[280,3371,1521],{"class":301},[280,3373,592],{"class":297},[280,3375,328],{"class":297},[280,3377,3378,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404],{"class":282,"line":1239},[280,3379,1530],{"class":337},[280,3381,347],{"class":297},[280,3383,1535],{"class":321},[280,3385,353],{"class":297},[280,3387,1540],{"class":1183},[280,3389,444],{"class":297},[280,3391,1545],{"class":297},[280,3393,1548],{"class":1509},[280,3395,1513],{"class":297},[280,3397,1516],{"class":301},[280,3399,347],{"class":297},[280,3401,1557],{"class":301},[280,3403,592],{"class":297},[280,3405,328],{"class":297},[280,3407,3408,3410,3412,3414,3416,3418,3420,3422],{"class":282,"line":1244},[280,3409,1566],{"class":337},[280,3411,347],{"class":297},[280,3413,1571],{"class":321},[280,3415,353],{"class":297},[280,3417,366],{"class":297},[280,3419,3009],{"class":369},[280,3421,366],{"class":297},[280,3423,404],{"class":297},[280,3425,3426,3428,3430,3432,3434,3436,3439,3441],{"class":282,"line":1279},[280,3427,1566],{"class":337},[280,3429,347],{"class":297},[280,3431,1571],{"class":321},[280,3433,353],{"class":297},[280,3435,366],{"class":297},[280,3437,3438],{"class":369},"동해물과 백두산이 마르고 닳도록",[280,3440,366],{"class":297},[280,3442,404],{"class":297},[280,3444,3445,3447,3449,3451,3453,3455,3458,3460],{"class":282,"line":1294},[280,3446,1566],{"class":337},[280,3448,347],{"class":297},[280,3450,1571],{"class":321},[280,3452,353],{"class":297},[280,3454,366],{"class":297},[280,3456,3457],{"class":369},"서울특별시 강남구 테헤란로 152",[280,3459,366],{"class":297},[280,3461,404],{"class":297},[280,3463,3464],{"class":282,"line":1299},[280,3465,1610],{"class":297},[280,3467,3468],{"class":282,"line":1729},[280,3469,1615],{"class":297},[280,3471,3472],{"class":282,"line":1734},[280,3473,312],{"emptyLinePlaceholder":311},[280,3475,3476,3478,3480,3482,3484,3486,3488,3490],{"class":282,"line":2328},[280,3477,1624],{"class":337},[280,3479,444],{"class":297},[280,3481,447],{"class":337},[280,3483,341],{"class":297},[280,3485,1486],{"class":337},[280,3487,347],{"class":297},[280,3489,1637],{"class":321},[280,3491,1113],{"class":297},[280,3493,3494,3496,3498,3500,3502,3504,3506,3508,3510,3512,3514,3516,3518,3520,3522,3524,3526,3528,3530],{"class":282,"line":2333},[280,3495,334],{"class":293},[280,3497,338],{"class":337},[280,3499,341],{"class":297},[280,3501,452],{"class":337},[280,3503,347],{"class":297},[280,3505,1684],{"class":321},[280,3507,353],{"class":297},[280,3509,366],{"class":297},[280,3511,3086],{"class":369},[280,3513,366],{"class":297},[280,3515,444],{"class":297},[280,3517,1697],{"class":337},[280,3519,444],{"class":297},[280,3521,1702],{"class":1183},[280,3523,507],{"class":297},[280,3525,338],{"class":337},[280,3527,380],{"class":297},[280,3529,383],{"class":297},[280,3531,328],{"class":297},[280,3533,3534,3536,3538,3540,3542,3544],{"class":282,"line":2338},[280,3535,391],{"class":337},[280,3537,347],{"class":297},[280,3539,396],{"class":321},[280,3541,353],{"class":297},[280,3543,401],{"class":337},[280,3545,404],{"class":297},[280,3547,3548],{"class":282,"line":2368},[280,3549,410],{"class":297},[280,3551,3552],{"class":282,"line":2384},[280,3553,416],{"class":297},[19,3555,3556,3557,3560,3561,3564,3565,3568],{},"세 가지가 다르다. 폰트는 ",[22,3558,3559],{},"바이트",", 경로가 아니다 — ",[38,3562,3563],{},"//go:embed","로 바이너리에 컴파일 포함, 런타임 이미지에 폰트 디렉터리가 필요 없어진다. 폰트는 ",[22,3566,3567],{},"생성 시점에 한 번"," 등록한다. paragraph마다 style을 끌고 다닐 필요가 없다. 그리고 gpdf의 TrueType 서브셋팅은 CJK cmap 포맷 (4, 6, 12)과 Identity-H 인코딩을 이해하므로, 출력 PDF는 실제 사용한 글리프만 담는다. 한글 200자 인보이스가 약 30 KB 폰트 서브셋이 된다. 4 MB 풀 임베드가 아니다.",[19,3570,3571],{},"본명조 (Source Han Sans), 나눔고딕, 폴백 체인 등 자세한 건 한국어 폰트 글에서 다룬다.",[14,3573,3575],{"id":3574},"before-after-4-모든-페이지에-헤더-푸터에-페이지-번호","Before / After 4: 모든 페이지에 헤더, 푸터에 페이지 번호",[19,3577,3578,3579,768,3581,3583,3584,768,3587,3590],{},"unipdf의 패턴은 ",[38,3580,767],{},[38,3582,771],{},". 둘 다 현재 block과 페이지 번호를 담은 컨텍스트를 받는다. 페이지 번호는 컨텍스트의 ",[38,3585,3586],{},"PageNum",[38,3588,3589],{},"TotalPages"," 필드에서 꺼낸다.",[19,3592,3593],{},[22,3594,927],{},[271,3596,3598],{"className":273,"code":3597,"language":275,"meta":276,"style":276},"c.DrawHeader(func(block *creator.Block, args creator.HeaderFunctionArgs) {\n    p := c.NewParagraph(\"ACME 주식회사\")\n    p.SetFontSize(12)\n    p.SetPos(40, 30)\n    block.Draw(p)\n})\n\nc.DrawFooter(func(block *creator.Block, args creator.FooterFunctionArgs) {\n    p := c.NewParagraph(fmt.Sprintf(\"%d / %d 페이지\", args.PageNum, args.TotalPages))\n    p.SetFontSize(8)\n    p.SetPos(0, 20)\n    p.SetTextAlignment(creator.TextAlignmentCenter)\n    block.Draw(p)\n})\n",[38,3599,3600,3639,3662,3676,3697,3712,3716,3720,3755,3812,3827,3846,3866,3880],{"__ignoreMap":276},[280,3601,3602,3604,3606,3609,3611,3614,3616,3618,3620,3623,3625,3628,3630,3632,3635,3637],{"class":282,"line":283},[280,3603,1548],{"class":337},[280,3605,347],{"class":297},[280,3607,3608],{"class":321},"DrawHeader",[280,3610,1506],{"class":297},[280,3612,3613],{"class":1509},"block",[280,3615,1513],{"class":297},[280,3617,44],{"class":301},[280,3619,347],{"class":297},[280,3621,3622],{"class":301},"Block",[280,3624,444],{"class":297},[280,3626,3627],{"class":1509}," args",[280,3629,1105],{"class":301},[280,3631,347],{"class":297},[280,3633,3634],{"class":301},"HeaderFunctionArgs",[280,3636,592],{"class":297},[280,3638,328],{"class":297},[280,3640,3641,3643,3645,3647,3649,3651,3653,3655,3658,3660],{"class":282,"line":290},[280,3642,1146],{"class":337},[280,3644,341],{"class":297},[280,3646,1151],{"class":337},[280,3648,347],{"class":297},[280,3650,1156],{"class":321},[280,3652,353],{"class":297},[280,3654,366],{"class":297},[280,3656,3657],{"class":369},"ACME 주식회사",[280,3659,366],{"class":297},[280,3661,404],{"class":297},[280,3663,3664,3666,3668,3670,3672,3674],{"class":282,"line":308},[280,3665,1173],{"class":337},[280,3667,347],{"class":297},[280,3669,1178],{"class":321},[280,3671,353],{"class":297},[280,3673,1540],{"class":1183},[280,3675,404],{"class":297},[280,3677,3678,3680,3682,3685,3687,3690,3692,3695],{"class":282,"line":315},[280,3679,1173],{"class":337},[280,3681,347],{"class":297},[280,3683,3684],{"class":321},"SetPos",[280,3686,353],{"class":297},[280,3688,3689],{"class":1183},"40",[280,3691,444],{"class":297},[280,3693,3694],{"class":1183}," 30",[280,3696,404],{"class":297},[280,3698,3699,3702,3704,3706,3708,3710],{"class":282,"line":331},[280,3700,3701],{"class":337},"    block",[280,3703,347],{"class":297},[280,3705,1202],{"class":321},[280,3707,353],{"class":297},[280,3709,19],{"class":337},[280,3711,404],{"class":297},[280,3713,3714],{"class":282,"line":388},[280,3715,2786],{"class":297},[280,3717,3718],{"class":282,"line":407},[280,3719,312],{"emptyLinePlaceholder":311},[280,3721,3722,3724,3726,3728,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3751,3753],{"class":282,"line":413},[280,3723,1548],{"class":337},[280,3725,347],{"class":297},[280,3727,789],{"class":321},[280,3729,1506],{"class":297},[280,3731,3613],{"class":1509},[280,3733,1513],{"class":297},[280,3735,44],{"class":301},[280,3737,347],{"class":297},[280,3739,3622],{"class":301},[280,3741,444],{"class":297},[280,3743,3627],{"class":1509},[280,3745,1105],{"class":301},[280,3747,347],{"class":297},[280,3749,3750],{"class":301},"FooterFunctionArgs",[280,3752,592],{"class":297},[280,3754,328],{"class":297},[280,3756,3757,3759,3761,3763,3765,3767,3769,3772,3774,3777,3779,3781,3785,3787,3789,3792,3794,3796,3798,3800,3802,3804,3806,3808,3810],{"class":282,"line":993},[280,3758,1146],{"class":337},[280,3760,341],{"class":297},[280,3762,1151],{"class":337},[280,3764,347],{"class":297},[280,3766,1156],{"class":321},[280,3768,353],{"class":297},[280,3770,3771],{"class":337},"fmt",[280,3773,347],{"class":297},[280,3775,3776],{"class":321},"Sprintf",[280,3778,353],{"class":297},[280,3780,366],{"class":297},[280,3782,3784],{"class":3783},"swJcz","%d",[280,3786,768],{"class":369},[280,3788,3784],{"class":3783},[280,3790,3791],{"class":369}," 페이지",[280,3793,366],{"class":297},[280,3795,444],{"class":297},[280,3797,3627],{"class":337},[280,3799,347],{"class":297},[280,3801,3586],{"class":337},[280,3803,444],{"class":297},[280,3805,3627],{"class":337},[280,3807,347],{"class":297},[280,3809,3589],{"class":337},[280,3811,1983],{"class":297},[280,3813,3814,3816,3818,3820,3822,3825],{"class":282,"line":998},[280,3815,1173],{"class":337},[280,3817,347],{"class":297},[280,3819,1178],{"class":321},[280,3821,353],{"class":297},[280,3823,3824],{"class":1183},"8",[280,3826,404],{"class":297},[280,3828,3829,3831,3833,3835,3837,3840,3842,3844],{"class":282,"line":1003},[280,3830,1173],{"class":337},[280,3832,347],{"class":297},[280,3834,3684],{"class":321},[280,3836,353],{"class":297},[280,3838,3839],{"class":1183},"0",[280,3841,444],{"class":297},[280,3843,2671],{"class":1183},[280,3845,404],{"class":297},[280,3847,3848,3850,3852,3855,3857,3859,3861,3864],{"class":282,"line":1014},[280,3849,1173],{"class":337},[280,3851,347],{"class":297},[280,3853,3854],{"class":321},"SetTextAlignment",[280,3856,353],{"class":297},[280,3858,44],{"class":337},[280,3860,347],{"class":297},[280,3862,3863],{"class":337},"TextAlignmentCenter",[280,3865,404],{"class":297},[280,3867,3868,3870,3872,3874,3876,3878],{"class":282,"line":1055},[280,3869,3701],{"class":337},[280,3871,347],{"class":297},[280,3873,1202],{"class":321},[280,3875,353],{"class":297},[280,3877,19],{"class":337},[280,3879,404],{"class":297},[280,3881,3882],{"class":282,"line":1070},[280,3883,2786],{"class":297},[19,3885,3886],{},"헤더 / 푸터 모두 절대 좌표로 그리는 block. Y 좌표나 마진이 틀리면 페이지 크기를 바꿀 때마다 좌표를 다시 맞춰야 한다.",[19,3888,3889],{},[22,3890,1306],{},[271,3892,3894],{"className":273,"code":3893,"language":275,"meta":276,"style":276},"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 주식회사\", 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 주식회사\",\n                template.FontSize(8), template.TextColor(pdf.Gray(0.5)))\n        })\n        r.Col(6, func(c *template.ColBuilder) {\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(\"%d 페이지의 본문.\", i+1))\n        })\n    })\n}\n",[38,3895,3896,3911,3930,3960,3964,3968,3995,4019,4049,4088,4123,4146,4150,4154,4158,4162,4187,4211,4242,4261,4295,4299,4329,4349,4383,4387,4391,4395,4399,4432,4446,4470,4500,4539,4543,4547],{"__ignoreMap":276},[280,3897,3898,3901,3903,3905,3907,3909],{"class":282,"line":283},[280,3899,3900],{"class":337},"doc ",[280,3902,341],{"class":297},[280,3904,1401],{"class":337},[280,3906,347],{"class":297},[280,3908,1406],{"class":321},[280,3910,1409],{"class":297},[280,3912,3913,3916,3918,3920,3922,3924,3926,3928],{"class":282,"line":290},[280,3914,3915],{"class":337},"    gpdf",[280,3917,347],{"class":297},[280,3919,1419],{"class":321},[280,3921,353],{"class":297},[280,3923,1424],{"class":337},[280,3925,347],{"class":297},[280,3927,1429],{"class":337},[280,3929,1432],{"class":297},[280,3931,3932,3934,3936,3938,3940,3942,3944,3946,3948,3950,3952,3954,3956,3958],{"class":282,"line":308},[280,3933,3915],{"class":337},[280,3935,347],{"class":297},[280,3937,1441],{"class":321},[280,3939,353],{"class":297},[280,3941,1424],{"class":337},[280,3943,347],{"class":297},[280,3945,1450],{"class":321},[280,3947,353],{"class":297},[280,3949,1424],{"class":337},[280,3951,347],{"class":297},[280,3953,1459],{"class":321},[280,3955,353],{"class":297},[280,3957,1464],{"class":1183},[280,3959,1467],{"class":297},[280,3961,3962],{"class":282,"line":315},[280,3963,404],{"class":297},[280,3965,3966],{"class":282,"line":331},[280,3967,312],{"emptyLinePlaceholder":311},[280,3969,3970,3973,3975,3978,3980,3982,3984,3986,3988,3991,3993],{"class":282,"line":388},[280,3971,3972],{"class":337},"doc",[280,3974,347],{"class":297},[280,3976,3977],{"class":321},"Header",[280,3979,1506],{"class":297},[280,3981,19],{"class":1509},[280,3983,1513],{"class":297},[280,3985,1516],{"class":301},[280,3987,347],{"class":297},[280,3989,3990],{"class":301},"PageBuilder",[280,3992,592],{"class":297},[280,3994,328],{"class":297},[280,3996,3997,3999,4001,4003,4005,4007,4009,4011,4013,4015,4017],{"class":282,"line":407},[280,3998,1173],{"class":337},[280,4000,347],{"class":297},[280,4002,1503],{"class":321},[280,4004,1506],{"class":297},[280,4006,1510],{"class":1509},[280,4008,1513],{"class":297},[280,4010,1516],{"class":301},[280,4012,347],{"class":297},[280,4014,1521],{"class":301},[280,4016,592],{"class":297},[280,4018,328],{"class":297},[280,4020,4021,4023,4025,4027,4029,4031,4033,4035,4037,4039,4041,4043,4045,4047],{"class":282,"line":413},[280,4022,1530],{"class":337},[280,4024,347],{"class":297},[280,4026,1535],{"class":321},[280,4028,353],{"class":297},[280,4030,1540],{"class":1183},[280,4032,444],{"class":297},[280,4034,1545],{"class":297},[280,4036,1548],{"class":1509},[280,4038,1513],{"class":297},[280,4040,1516],{"class":301},[280,4042,347],{"class":297},[280,4044,1557],{"class":301},[280,4046,592],{"class":297},[280,4048,328],{"class":297},[280,4050,4051,4053,4055,4057,4059,4061,4063,4065,4067,4069,4071,4073,4076,4078,4080,4082,4084,4086],{"class":282,"line":993},[280,4052,1566],{"class":337},[280,4054,347],{"class":297},[280,4056,1571],{"class":321},[280,4058,353],{"class":297},[280,4060,366],{"class":297},[280,4062,3657],{"class":369},[280,4064,366],{"class":297},[280,4066,444],{"class":297},[280,4068,1584],{"class":337},[280,4070,347],{"class":297},[280,4072,1602],{"class":321},[280,4074,4075],{"class":297},"(),",[280,4077,1584],{"class":337},[280,4079,347],{"class":297},[280,4081,1589],{"class":321},[280,4083,353],{"class":297},[280,4085,1540],{"class":1183},[280,4087,1983],{"class":297},[280,4089,4090,4092,4094,4097,4099,4101,4103,4106,4108,4110,4112,4115,4117,4120],{"class":282,"line":998},[280,4091,1566],{"class":337},[280,4093,347],{"class":297},[280,4095,4096],{"class":321},"Line",[280,4098,353],{"class":297},[280,4100,1516],{"class":337},[280,4102,347],{"class":297},[280,4104,4105],{"class":321},"LineColor",[280,4107,353],{"class":297},[280,4109,2710],{"class":337},[280,4111,347],{"class":297},[280,4113,4114],{"class":321},"Gray",[280,4116,353],{"class":297},[280,4118,4119],{"class":1183},"0.7",[280,4121,4122],{"class":297},")))\n",[280,4124,4125,4127,4129,4132,4134,4136,4138,4140,4142,4144],{"class":282,"line":1003},[280,4126,1566],{"class":337},[280,4128,347],{"class":297},[280,4130,4131],{"class":321},"Spacer",[280,4133,353],{"class":297},[280,4135,1424],{"class":337},[280,4137,347],{"class":297},[280,4139,1459],{"class":321},[280,4141,353],{"class":297},[280,4143,1794],{"class":1183},[280,4145,1983],{"class":297},[280,4147,4148],{"class":282,"line":1014},[280,4149,1610],{"class":297},[280,4151,4152],{"class":282,"line":1055},[280,4153,1615],{"class":297},[280,4155,4156],{"class":282,"line":1070},[280,4157,2786],{"class":297},[280,4159,4160],{"class":282,"line":1075},[280,4161,312],{"emptyLinePlaceholder":311},[280,4163,4164,4166,4168,4171,4173,4175,4177,4179,4181,4183,4185],{"class":282,"line":1080},[280,4165,3972],{"class":337},[280,4167,347],{"class":297},[280,4169,4170],{"class":321},"Footer",[280,4172,1506],{"class":297},[280,4174,19],{"class":1509},[280,4176,1513],{"class":297},[280,4178,1516],{"class":301},[280,4180,347],{"class":297},[280,4182,3990],{"class":301},[280,4184,592],{"class":297},[280,4186,328],{"class":297},[280,4188,4189,4191,4193,4195,4197,4199,4201,4203,4205,4207,4209],{"class":282,"line":1085},[280,4190,1173],{"class":337},[280,4192,347],{"class":297},[280,4194,1503],{"class":321},[280,4196,1506],{"class":297},[280,4198,1510],{"class":1509},[280,4200,1513],{"class":297},[280,4202,1516],{"class":301},[280,4204,347],{"class":297},[280,4206,1521],{"class":301},[280,4208,592],{"class":297},[280,4210,328],{"class":297},[280,4212,4213,4215,4217,4219,4221,4224,4226,4228,4230,4232,4234,4236,4238,4240],{"class":282,"line":1097},[280,4214,1530],{"class":337},[280,4216,347],{"class":297},[280,4218,1535],{"class":321},[280,4220,353],{"class":297},[280,4222,4223],{"class":1183},"6",[280,4225,444],{"class":297},[280,4227,1545],{"class":297},[280,4229,1548],{"class":1509},[280,4231,1513],{"class":297},[280,4233,1516],{"class":301},[280,4235,347],{"class":297},[280,4237,1557],{"class":301},[280,4239,592],{"class":297},[280,4241,328],{"class":297},[280,4243,4244,4246,4248,4250,4252,4254,4256,4258],{"class":282,"line":1116},[280,4245,1566],{"class":337},[280,4247,347],{"class":297},[280,4249,1571],{"class":321},[280,4251,353],{"class":297},[280,4253,366],{"class":297},[280,4255,3657],{"class":369},[280,4257,366],{"class":297},[280,4259,4260],{"class":297},",\n",[280,4262,4263,4265,4267,4269,4271,4273,4275,4277,4279,4281,4283,4285,4287,4289,4291,4293],{"class":282,"line":1138},[280,4264,2689],{"class":337},[280,4266,347],{"class":297},[280,4268,1589],{"class":321},[280,4270,353],{"class":297},[280,4272,3824],{"class":1183},[280,4274,497],{"class":297},[280,4276,1584],{"class":337},[280,4278,347],{"class":297},[280,4280,2705],{"class":321},[280,4282,353],{"class":297},[280,4284,2710],{"class":337},[280,4286,347],{"class":297},[280,4288,4114],{"class":321},[280,4290,353],{"class":297},[280,4292,1809],{"class":1183},[280,4294,4122],{"class":297},[280,4296,4297],{"class":282,"line":1143},[280,4298,1610],{"class":297},[280,4300,4301,4303,4305,4307,4309,4311,4313,4315,4317,4319,4321,4323,4325,4327],{"class":282,"line":1170},[280,4302,1530],{"class":337},[280,4304,347],{"class":297},[280,4306,1535],{"class":321},[280,4308,353],{"class":297},[280,4310,4223],{"class":1183},[280,4312,444],{"class":297},[280,4314,1545],{"class":297},[280,4316,1548],{"class":1509},[280,4318,1513],{"class":297},[280,4320,1516],{"class":301},[280,4322,347],{"class":297},[280,4324,1557],{"class":301},[280,4326,592],{"class":297},[280,4328,328],{"class":297},[280,4330,4331,4333,4335,4338,4340,4342,4344,4347],{"class":282,"line":1189},[280,4332,1566],{"class":337},[280,4334,347],{"class":297},[280,4336,4337],{"class":321},"PageNumber",[280,4339,353],{"class":297},[280,4341,1516],{"class":337},[280,4343,347],{"class":297},[280,4345,4346],{"class":321},"AlignRight",[280,4348,2696],{"class":297},[280,4350,4351,4353,4355,4357,4359,4361,4363,4365,4367,4369,4371,4373,4375,4377,4379,4381],{"class":282,"line":1219},[280,4352,2689],{"class":337},[280,4354,347],{"class":297},[280,4356,1589],{"class":321},[280,4358,353],{"class":297},[280,4360,3824],{"class":1183},[280,4362,497],{"class":297},[280,4364,1584],{"class":337},[280,4366,347],{"class":297},[280,4368,2705],{"class":321},[280,4370,353],{"class":297},[280,4372,2710],{"class":337},[280,4374,347],{"class":297},[280,4376,4114],{"class":321},[280,4378,353],{"class":297},[280,4380,1809],{"class":1183},[280,4382,4122],{"class":297},[280,4384,4385],{"class":282,"line":1234},[280,4386,1610],{"class":297},[280,4388,4389],{"class":282,"line":1239},[280,4390,1615],{"class":297},[280,4392,4393],{"class":282,"line":1244},[280,4394,2786],{"class":297},[280,4396,4397],{"class":282,"line":1279},[280,4398,312],{"emptyLinePlaceholder":311},[280,4400,4401,4403,4406,4408,4411,4414,4416,4419,4422,4424,4427,4430],{"class":282,"line":1294},[280,4402,2099],{"class":293},[280,4404,4405],{"class":337}," i ",[280,4407,341],{"class":297},[280,4409,4410],{"class":1183}," 0",[280,4412,4413],{"class":297},";",[280,4415,4405],{"class":337},[280,4417,4418],{"class":297},"\u003C",[280,4420,4421],{"class":1183}," 10",[280,4423,4413],{"class":297},[280,4425,4426],{"class":337}," i",[280,4428,4429],{"class":297},"++",[280,4431,328],{"class":297},[280,4433,4434,4436,4438,4440,4442,4444],{"class":282,"line":1299},[280,4435,1481],{"class":337},[280,4437,341],{"class":297},[280,4439,1486],{"class":337},[280,4441,347],{"class":297},[280,4443,1491],{"class":321},[280,4445,1113],{"class":297},[280,4447,4448,4450,4452,4454,4456,4458,4460,4462,4464,4466,4468],{"class":282,"line":1729},[280,4449,1498],{"class":337},[280,4451,347],{"class":297},[280,4453,1503],{"class":321},[280,4455,1506],{"class":297},[280,4457,1510],{"class":1509},[280,4459,1513],{"class":297},[280,4461,1516],{"class":301},[280,4463,347],{"class":297},[280,4465,1521],{"class":301},[280,4467,592],{"class":297},[280,4469,328],{"class":297},[280,4471,4472,4474,4476,4478,4480,4482,4484,4486,4488,4490,4492,4494,4496,4498],{"class":282,"line":1734},[280,4473,1530],{"class":337},[280,4475,347],{"class":297},[280,4477,1535],{"class":321},[280,4479,353],{"class":297},[280,4481,1540],{"class":1183},[280,4483,444],{"class":297},[280,4485,1545],{"class":297},[280,4487,1548],{"class":1509},[280,4489,1513],{"class":297},[280,4491,1516],{"class":301},[280,4493,347],{"class":297},[280,4495,1557],{"class":301},[280,4497,592],{"class":297},[280,4499,328],{"class":297},[280,4501,4502,4504,4506,4508,4510,4512,4514,4516,4518,4520,4522,4525,4527,4529,4531,4534,4537],{"class":282,"line":2328},[280,4503,1566],{"class":337},[280,4505,347],{"class":297},[280,4507,1571],{"class":321},[280,4509,353],{"class":297},[280,4511,3771],{"class":337},[280,4513,347],{"class":297},[280,4515,3776],{"class":321},[280,4517,353],{"class":297},[280,4519,366],{"class":297},[280,4521,3784],{"class":3783},[280,4523,4524],{"class":369}," 페이지의 본문.",[280,4526,366],{"class":297},[280,4528,444],{"class":297},[280,4530,4426],{"class":337},[280,4532,4533],{"class":297},"+",[280,4535,4536],{"class":1183},"1",[280,4538,1983],{"class":297},[280,4540,4541],{"class":282,"line":2333},[280,4542,1610],{"class":297},[280,4544,4545],{"class":282,"line":2338},[280,4546,1615],{"class":297},[280,4548,4549],{"class":282,"line":2368},[280,4550,416],{"class":297},[19,4552,4553,4555,4556,4558],{},[38,4554,4337],{},"와 ",[38,4557,3589],{},"는 플레이스홀더, 페이지 확정 후 레이아웃 엔진이 해석한다. 헤더와 푸터 자체가 트리이지 직접 위치를 잡는 block이 아니다. 엔진이 모든 페이지에 자동으로 공간을 확보한다. A4에서 Letter로 페이지 크기를 바꿔도 다른 건 손댈 필요가 없다.",[14,4560,4562],{"id":4561},"before-after-5-aes-256-암호화","Before / After 5: AES-256 암호화",[19,4564,4565,4566,4568],{},"라이선스 차이가 가장 뚜렷한 쌍. unipdf의 암호화는 ",[38,4567,811],{},"를 거치는 경로로, 이는 상용 사용에 해당하며 라이선스 등록 체크를 발동한다. gpdf 측은 단일 함수 옵션, AES-256 (ISO 32000-2 Rev 6) 구현은 오픈소스 MIT 코어에 들어 있다.",[19,4570,4571],{},[22,4572,927],{},[271,4574,4576],{"className":273,"code":4575,"language":275,"meta":276,"style":276},"// creator로 생성 후 model.PdfWriter로 재인코딩하여 암호화 부착.\n// 라이선스 체크가 여기에서 발동.\nc := creator.New()\n// ... 콘텐츠 그리기 ...\n\nvar buf bytes.Buffer\nif err := c.Write(&buf); err != nil {\n    log.Fatal(err)\n}\n\nreader, err := model.NewPdfReader(bytes.NewReader(buf.Bytes()))\nif err != nil {\n    log.Fatal(err)\n}\n\nwriter := model.NewPdfWriter()\nencryptOpts := &model.EncryptOptions{Algorithm: model.RC4_128bit, Permissions: model.PermPrinting}\nif err := writer.Encrypt([]byte(\"user-pwd\"), []byte(\"owner-pwd\"), encryptOpts); err != nil {\n    log.Fatal(err)\n}\n\nfor i := 1; i \u003C= reader.NumPage; i++ {\n    page, _ := reader.GetPage(i)\n    writer.AddPage(page)\n}\n\nf, _ := os.Create(\"encrypted.pdf\")\ndefer f.Close()\nwriter.Write(f)\n",[38,4577,4578,4583,4588,4602,4607,4611,4626,4657,4671,4675,4679,4718,4730,4744,4748,4752,4768,4817,4877,4891,4895,4899,4933,4957,4972,4976,4980,5009,5024],{"__ignoreMap":276},[280,4579,4580],{"class":282,"line":283},[280,4581,4582],{"class":286},"// creator로 생성 후 model.PdfWriter로 재인코딩하여 암호화 부착.\n",[280,4584,4585],{"class":282,"line":290},[280,4586,4587],{"class":286},"// 라이선스 체크가 여기에서 발동.\n",[280,4589,4590,4592,4594,4596,4598,4600],{"class":282,"line":308},[280,4591,2900],{"class":337},[280,4593,341],{"class":297},[280,4595,1105],{"class":337},[280,4597,347],{"class":297},[280,4599,1110],{"class":321},[280,4601,1113],{"class":297},[280,4603,4604],{"class":282,"line":315},[280,4605,4606],{"class":286},"// ... 콘텐츠 그리기 ...\n",[280,4608,4609],{"class":282,"line":331},[280,4610,312],{"emptyLinePlaceholder":311},[280,4612,4613,4615,4618,4621,4623],{"class":282,"line":388},[280,4614,3195],{"class":297},[280,4616,4617],{"class":337}," buf ",[280,4619,4620],{"class":301},"bytes",[280,4622,347],{"class":297},[280,4624,4625],{"class":301},"Buffer\n",[280,4627,4628,4630,4632,4634,4636,4638,4641,4644,4647,4649,4651,4653,4655],{"class":282,"line":407},[280,4629,2341],{"class":293},[280,4631,338],{"class":337},[280,4633,341],{"class":297},[280,4635,1151],{"class":337},[280,4637,347],{"class":297},[280,4639,4640],{"class":321},"Write",[280,4642,4643],{"class":297},"(&",[280,4645,4646],{"class":337},"buf",[280,4648,507],{"class":297},[280,4650,338],{"class":337},[280,4652,380],{"class":297},[280,4654,383],{"class":297},[280,4656,328],{"class":297},[280,4658,4659,4661,4663,4665,4667,4669],{"class":282,"line":413},[280,4660,2371],{"class":337},[280,4662,347],{"class":297},[280,4664,396],{"class":321},[280,4666,353],{"class":297},[280,4668,401],{"class":337},[280,4670,404],{"class":297},[280,4672,4673],{"class":282,"line":993},[280,4674,416],{"class":297},[280,4676,4677],{"class":282,"line":998},[280,4678,312],{"emptyLinePlaceholder":311},[280,4680,4681,4684,4686,4688,4690,4692,4694,4697,4699,4701,4703,4706,4708,4710,4712,4715],{"class":282,"line":1003},[280,4682,4683],{"class":337},"reader",[280,4685,444],{"class":297},[280,4687,338],{"class":337},[280,4689,341],{"class":297},[280,4691,1865],{"class":337},[280,4693,347],{"class":297},[280,4695,4696],{"class":321},"NewPdfReader",[280,4698,353],{"class":297},[280,4700,4620],{"class":337},[280,4702,347],{"class":297},[280,4704,4705],{"class":321},"NewReader",[280,4707,353],{"class":297},[280,4709,4646],{"class":337},[280,4711,347],{"class":297},[280,4713,4714],{"class":321},"Bytes",[280,4716,4717],{"class":297},"()))\n",[280,4719,4720,4722,4724,4726,4728],{"class":282,"line":1014},[280,4721,2341],{"class":293},[280,4723,338],{"class":337},[280,4725,380],{"class":297},[280,4727,383],{"class":297},[280,4729,328],{"class":297},[280,4731,4732,4734,4736,4738,4740,4742],{"class":282,"line":1055},[280,4733,2371],{"class":337},[280,4735,347],{"class":297},[280,4737,396],{"class":321},[280,4739,353],{"class":297},[280,4741,401],{"class":337},[280,4743,404],{"class":297},[280,4745,4746],{"class":282,"line":1070},[280,4747,416],{"class":297},[280,4749,4750],{"class":282,"line":1075},[280,4751,312],{"emptyLinePlaceholder":311},[280,4753,4754,4757,4759,4761,4763,4766],{"class":282,"line":1080},[280,4755,4756],{"class":337},"writer ",[280,4758,341],{"class":297},[280,4760,1865],{"class":337},[280,4762,347],{"class":297},[280,4764,4765],{"class":321},"NewPdfWriter",[280,4767,1113],{"class":297},[280,4769,4770,4773,4775,4778,4781,4783,4786,4788,4791,4794,4796,4798,4801,4803,4806,4808,4810,4812,4815],{"class":282,"line":1085},[280,4771,4772],{"class":337},"encryptOpts ",[280,4774,341],{"class":297},[280,4776,4777],{"class":297}," &",[280,4779,4780],{"class":301},"model",[280,4782,347],{"class":297},[280,4784,4785],{"class":301},"EncryptOptions",[280,4787,2120],{"class":297},[280,4789,4790],{"class":337},"Algorithm",[280,4792,4793],{"class":297},":",[280,4795,1865],{"class":337},[280,4797,347],{"class":297},[280,4799,4800],{"class":337},"RC4_128bit",[280,4802,444],{"class":297},[280,4804,4805],{"class":337}," Permissions",[280,4807,4793],{"class":297},[280,4809,1865],{"class":337},[280,4811,347],{"class":297},[280,4813,4814],{"class":337},"PermPrinting",[280,4816,416],{"class":297},[280,4818,4819,4821,4823,4825,4828,4830,4832,4835,4838,4840,4842,4845,4847,4849,4851,4853,4855,4857,4860,4862,4864,4867,4869,4871,4873,4875],{"class":282,"line":1097},[280,4820,2341],{"class":293},[280,4822,338],{"class":337},[280,4824,341],{"class":297},[280,4826,4827],{"class":337}," writer",[280,4829,347],{"class":297},[280,4831,815],{"class":321},[280,4833,4834],{"class":297},"([]",[280,4836,4837],{"class":488},"byte",[280,4839,353],{"class":297},[280,4841,366],{"class":297},[280,4843,4844],{"class":369},"user-pwd",[280,4846,366],{"class":297},[280,4848,497],{"class":297},[280,4850,2115],{"class":297},[280,4852,4837],{"class":488},[280,4854,353],{"class":297},[280,4856,366],{"class":297},[280,4858,4859],{"class":369},"owner-pwd",[280,4861,366],{"class":297},[280,4863,497],{"class":297},[280,4865,4866],{"class":337}," encryptOpts",[280,4868,507],{"class":297},[280,4870,338],{"class":337},[280,4872,380],{"class":297},[280,4874,383],{"class":297},[280,4876,328],{"class":297},[280,4878,4879,4881,4883,4885,4887,4889],{"class":282,"line":1116},[280,4880,2371],{"class":337},[280,4882,347],{"class":297},[280,4884,396],{"class":321},[280,4886,353],{"class":297},[280,4888,401],{"class":337},[280,4890,404],{"class":297},[280,4892,4893],{"class":282,"line":1138},[280,4894,416],{"class":297},[280,4896,4897],{"class":282,"line":1143},[280,4898,312],{"emptyLinePlaceholder":311},[280,4900,4901,4903,4905,4907,4910,4912,4914,4917,4920,4922,4925,4927,4929,4931],{"class":282,"line":1170},[280,4902,2099],{"class":293},[280,4904,4405],{"class":337},[280,4906,341],{"class":297},[280,4908,4909],{"class":1183}," 1",[280,4911,4413],{"class":297},[280,4913,4405],{"class":337},[280,4915,4916],{"class":297},"\u003C=",[280,4918,4919],{"class":337}," reader",[280,4921,347],{"class":297},[280,4923,4924],{"class":337},"NumPage",[280,4926,4413],{"class":297},[280,4928,4426],{"class":337},[280,4930,4429],{"class":297},[280,4932,328],{"class":297},[280,4934,4935,4937,4939,4941,4943,4945,4947,4950,4952,4955],{"class":282,"line":1189},[280,4936,1498],{"class":337},[280,4938,444],{"class":297},[280,4940,447],{"class":337},[280,4942,341],{"class":297},[280,4944,4919],{"class":337},[280,4946,347],{"class":297},[280,4948,4949],{"class":321},"GetPage",[280,4951,353],{"class":297},[280,4953,4954],{"class":337},"i",[280,4956,404],{"class":297},[280,4958,4959,4962,4964,4966,4968,4970],{"class":282,"line":1219},[280,4960,4961],{"class":337},"    writer",[280,4963,347],{"class":297},[280,4965,1491],{"class":321},[280,4967,353],{"class":297},[280,4969,2403],{"class":337},[280,4971,404],{"class":297},[280,4973,4974],{"class":282,"line":1234},[280,4975,416],{"class":297},[280,4977,4978],{"class":282,"line":1239},[280,4979,312],{"emptyLinePlaceholder":311},[280,4981,4982,4985,4987,4989,4991,4993,4995,4998,5000,5002,5005,5007],{"class":282,"line":1244},[280,4983,4984],{"class":337},"f",[280,4986,444],{"class":297},[280,4988,447],{"class":337},[280,4990,341],{"class":297},[280,4992,452],{"class":337},[280,4994,347],{"class":297},[280,4996,4997],{"class":321},"Create",[280,4999,353],{"class":297},[280,5001,366],{"class":297},[280,5003,5004],{"class":369},"encrypted.pdf",[280,5006,366],{"class":297},[280,5008,404],{"class":297},[280,5010,5011,5014,5017,5019,5022],{"class":282,"line":1279},[280,5012,5013],{"class":293},"defer",[280,5015,5016],{"class":337}," f",[280,5018,347],{"class":297},[280,5020,5021],{"class":321},"Close",[280,5023,1113],{"class":297},[280,5025,5026,5029,5031,5033,5035,5037],{"class":282,"line":1294},[280,5027,5028],{"class":337},"writer",[280,5030,347],{"class":297},[280,5032,4640],{"class":321},[280,5034,353],{"class":297},[280,5036,4984],{"class":337},[280,5038,404],{"class":297},[19,5040,5041],{},[22,5042,1306],{},[271,5044,5046],{"className":273,"code":5045,"language":275,"meta":276,"style":276},"doc := gpdf.NewDocument(\n    gpdf.WithPageSize(document.A4),\n    gpdf.WithMargins(document.UniformEdges(document.Mm(20))),\n    gpdf.WithEncryption(\n        gpdf.AES256,\n        \"user-pwd\",\n        \"owner-pwd\",\n        gpdf.PermPrinting|gpdf.PermCopyContent,\n    ),\n)\n\npage := doc.AddPage()\npage.AutoRow(func(r *template.RowBuilder) {\n    r.Col(12, func(c *template.ColBuilder) {\n        c.Text(\"기밀\")\n    })\n})\n\ndata, _ := doc.Generate()\nos.WriteFile(\"encrypted.pdf\", data, 0o644)\n",[38,5047,5048,5062,5080,5110,5121,5132,5143,5153,5173,5178,5182,5186,5201,5225,5255,5274,5278,5282,5286,5305],{"__ignoreMap":276},[280,5049,5050,5052,5054,5056,5058,5060],{"class":282,"line":283},[280,5051,3900],{"class":337},[280,5053,341],{"class":297},[280,5055,1401],{"class":337},[280,5057,347],{"class":297},[280,5059,1406],{"class":321},[280,5061,1409],{"class":297},[280,5063,5064,5066,5068,5070,5072,5074,5076,5078],{"class":282,"line":290},[280,5065,3915],{"class":337},[280,5067,347],{"class":297},[280,5069,1419],{"class":321},[280,5071,353],{"class":297},[280,5073,1424],{"class":337},[280,5075,347],{"class":297},[280,5077,1429],{"class":337},[280,5079,1432],{"class":297},[280,5081,5082,5084,5086,5088,5090,5092,5094,5096,5098,5100,5102,5104,5106,5108],{"class":282,"line":308},[280,5083,3915],{"class":337},[280,5085,347],{"class":297},[280,5087,1441],{"class":321},[280,5089,353],{"class":297},[280,5091,1424],{"class":337},[280,5093,347],{"class":297},[280,5095,1450],{"class":321},[280,5097,353],{"class":297},[280,5099,1424],{"class":337},[280,5101,347],{"class":297},[280,5103,1459],{"class":321},[280,5105,353],{"class":297},[280,5107,1464],{"class":1183},[280,5109,1467],{"class":297},[280,5111,5112,5114,5116,5119],{"class":282,"line":315},[280,5113,3915],{"class":337},[280,5115,347],{"class":297},[280,5117,5118],{"class":321},"WithEncryption",[280,5120,1409],{"class":297},[280,5122,5123,5125,5127,5130],{"class":282,"line":331},[280,5124,1414],{"class":337},[280,5126,347],{"class":297},[280,5128,5129],{"class":337},"AES256",[280,5131,4260],{"class":297},[280,5133,5134,5137,5139,5141],{"class":282,"line":388},[280,5135,5136],{"class":297},"        \"",[280,5138,4844],{"class":369},[280,5140,366],{"class":297},[280,5142,4260],{"class":297},[280,5144,5145,5147,5149,5151],{"class":282,"line":407},[280,5146,5136],{"class":297},[280,5148,4859],{"class":369},[280,5150,366],{"class":297},[280,5152,4260],{"class":297},[280,5154,5155,5157,5159,5161,5164,5166,5168,5171],{"class":282,"line":413},[280,5156,1414],{"class":337},[280,5158,347],{"class":297},[280,5160,4814],{"class":337},[280,5162,5163],{"class":297},"|",[280,5165,24],{"class":337},[280,5167,347],{"class":297},[280,5169,5170],{"class":337},"PermCopyContent",[280,5172,4260],{"class":297},[280,5174,5175],{"class":282,"line":993},[280,5176,5177],{"class":297},"    ),\n",[280,5179,5180],{"class":282,"line":998},[280,5181,404],{"class":297},[280,5183,5184],{"class":282,"line":1003},[280,5185,312],{"emptyLinePlaceholder":311},[280,5187,5188,5191,5193,5195,5197,5199],{"class":282,"line":1014},[280,5189,5190],{"class":337},"page ",[280,5192,341],{"class":297},[280,5194,1486],{"class":337},[280,5196,347],{"class":297},[280,5198,1491],{"class":321},[280,5200,1113],{"class":297},[280,5202,5203,5205,5207,5209,5211,5213,5215,5217,5219,5221,5223],{"class":282,"line":1055},[280,5204,2403],{"class":337},[280,5206,347],{"class":297},[280,5208,1503],{"class":321},[280,5210,1506],{"class":297},[280,5212,1510],{"class":1509},[280,5214,1513],{"class":297},[280,5216,1516],{"class":301},[280,5218,347],{"class":297},[280,5220,1521],{"class":301},[280,5222,592],{"class":297},[280,5224,328],{"class":297},[280,5226,5227,5229,5231,5233,5235,5237,5239,5241,5243,5245,5247,5249,5251,5253],{"class":282,"line":1070},[280,5228,2428],{"class":337},[280,5230,347],{"class":297},[280,5232,1535],{"class":321},[280,5234,353],{"class":297},[280,5236,1540],{"class":1183},[280,5238,444],{"class":297},[280,5240,1545],{"class":297},[280,5242,1548],{"class":1509},[280,5244,1513],{"class":297},[280,5246,1516],{"class":301},[280,5248,347],{"class":297},[280,5250,1557],{"class":301},[280,5252,592],{"class":297},[280,5254,328],{"class":297},[280,5256,5257,5259,5261,5263,5265,5267,5270,5272],{"class":282,"line":1075},[280,5258,2459],{"class":337},[280,5260,347],{"class":297},[280,5262,1571],{"class":321},[280,5264,353],{"class":297},[280,5266,366],{"class":297},[280,5268,5269],{"class":369},"기밀",[280,5271,366],{"class":297},[280,5273,404],{"class":297},[280,5275,5276],{"class":282,"line":1080},[280,5277,1615],{"class":297},[280,5279,5280],{"class":282,"line":1085},[280,5281,2786],{"class":297},[280,5283,5284],{"class":282,"line":1097},[280,5285,312],{"emptyLinePlaceholder":311},[280,5287,5288,5291,5293,5295,5297,5299,5301,5303],{"class":282,"line":1116},[280,5289,5290],{"class":337},"data",[280,5292,444],{"class":297},[280,5294,447],{"class":337},[280,5296,341],{"class":297},[280,5298,1486],{"class":337},[280,5300,347],{"class":297},[280,5302,1637],{"class":321},[280,5304,1113],{"class":297},[280,5306,5307,5309,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329],{"class":282,"line":1138},[280,5308,356],{"class":337},[280,5310,347],{"class":297},[280,5312,1684],{"class":321},[280,5314,353],{"class":297},[280,5316,366],{"class":297},[280,5318,5004],{"class":369},[280,5320,366],{"class":297},[280,5322,444],{"class":297},[280,5324,1697],{"class":337},[280,5326,444],{"class":297},[280,5328,1702],{"class":1183},[280,5330,404],{"class":297},[19,5332,5333,5334,5337,5338,5341],{},"옵션 하나, 기본 AES-256, 별도 writer 패스 없음. 전체 암호화 경로가 MIT 코어 안에 — 같은 모듈, 같은 ",[38,5335,5336],{},"go get",". 서명도 같은 이야기: ",[38,5339,5340],{},"gpdf.SignDocument(pdfBytes, signer, gpdf.WithTSA(\"http://timestamp.digicert.com\"))","로 PKCS#7 + RFC 3161 타임스탬프를 후처리로 부착한다. 추가 패키지도, 키 등록도 없다.",[14,5343,5345],{"id":5344},"얼마나-빠른가","얼마나 빠른가",[19,5347,5348,5351],{},[38,5349,5350],{},"_benchmark/benchmark_test.go","를 Apple M1 / Go 1.25에서 실행한 결과. unipdf는 라이선스상 비교 코드를 함께 배포하기가 까다로워 우리 커밋된 스위트에 직접 포함되어 있지 않다. 아래 수치는 같은 하드웨어, 같은 워크로드에서 unipdf v3에 대해 우리가 별도 수집한 값이다.",[127,5353,5354,5372],{},[130,5355,5356],{},[133,5357,5358,5361,5363,5366,5369],{},[136,5359,5360],{},"벤치마크",[136,5362,24],{},[136,5364,5365],{},"unipdf*",[136,5367,5368],{},"gofpdf",[136,5370,5371],{},"Maroto v2",[145,5373,5374,5393,5410,5429],{},[133,5375,5376,5379,5384,5387,5390],{},[150,5377,5378],{},"단일 페이지",[150,5380,5381],{},[22,5382,5383],{},"13 µs",[150,5385,5386],{},"~180 µs",[150,5388,5389],{},"132 µs",[150,5391,5392],{},"237 µs",[133,5394,5395,5398,5402,5405,5408],{},[150,5396,5397],{},"4×10 인보이스 테이블",[150,5399,5400],{},[22,5401,2808],{},[150,5403,5404],{},"~8.6 ms",[150,5406,5407],{},"241 µs",[150,5409,2804],{},[133,5411,5412,5415,5420,5423,5426],{},[150,5413,5414],{},"100 페이지 리포트",[150,5416,5417],{},[22,5418,5419],{},"683 µs",[150,5421,5422],{},"~95 ms",[150,5424,5425],{},"11.7 ms",[150,5427,5428],{},"19.8 ms",[133,5430,5431,5434,5439,5442,5445],{},[150,5432,5433],{},"복잡한 CJK 인보이스",[150,5435,5436],{},[22,5437,5438],{},"133 µs",[150,5440,5441],{},"~12 ms",[150,5443,5444],{},"254 µs",[150,5446,5447],{},"10.4 ms",[19,5449,5450],{},"* unipdf 수치는 별도 실행에서 수집했고, 같은 Apple M1 / Go 1.25 / 작성 시점 unipdf v3 기준. 참고용; 우리 커밋 스위트에는 포함되지 않는다.",[19,5452,5453],{},"모양은 gofpdf 비교와 같다. 실제 워크로드에서 gpdf가 10–80배 빠르다. 테이블이 많은 페이지 한 장이 108 µs라는 건, 코어 한 개로 초당 약 9,000개의 인보이스가 가능하다는 뜻. PDF 생성을 캐싱하거나 비동기 큐에 넣을지 고민할 필요가 없어진다. 요청 경로에서 생성하는 게 거의 다 충분하다.",[14,5455,5457],{"id":5456},"gpdf에-없는-것은-어떻게","gpdf에 없는 것은 어떻게",[19,5459,5460],{},"unipdf 청구서가 OCR, redaction, PDF 파싱을 떠받치고 있다면, 이 마이그레이션으로는 끝까지 갈 수 없다. 솔직한 선택지:",[5462,5463,5464,5478,5490,5496],"ul",{},[94,5465,5466,5469,5470,5477],{},[22,5467,5468],{},"OCR."," gpdf는 OCR을 하지 않으며 당분간 할 계획도 없다. ",[5471,5472,5476],"a",{"href":5473,"rel":5474},"https://github.com/tesseract-ocr/tesseract",[5475],"nofollow","Tesseract","를 gosseract으로 호출하거나 호스팅 OCR API를 쓴다. 생성은 gpdf, OCR은 별도 경로.",[94,5479,5480,5483,5484,5489],{},[22,5481,5482],{},"PDF 파싱 / 텍스트 추출."," gpdf는 설계상 생성 전용. 읽기 측은 ",[5471,5485,5488],{"href":5486,"rel":5487},"https://github.com/pdfcpu/pdfcpu",[5475],"pdfcpu"," (Apache 2.0)가 흔한 케이스를 폭넓게 커버한다. unipdf를 파싱 전용으로 남기면 시트 수를 줄일 수 있을 가능성도 있다.",[94,5491,5492,5495],{},[22,5493,5494],{},"AcroForm 필드 신규 작성."," gpdf는 기존 AcroForm 필드를 flatten할 수 있지만 신규 필드 작성은 아직. 뷰어에서 사용자가 입력하는 폼 PDF를 만든다면 여기가 갭. 로드맵에 있다.",[94,5497,5498,5501],{},[22,5499,5500],{},"Redaction (마스킹)."," gpdf 로드맵에 없다. redaction은 무엇을 가릴지 알기 위해 렌더러 측 지식이 필요하며 생성과는 다른 아키텍처.",[19,5503,5504,5506],{},[22,5505,261],{}," 경로 — 대부분의 unipdf 청구서가 실제로 사용하는 것 — 는 완전히 대체 가능하다.",[14,5508,5510],{"id":5509},"faq","FAQ",[19,5512,5513,5516],{},[22,5514,5515],{},"gpdf는 unipdf의 fork인가?","\n아니다. gpdf는 순수 Go의 클린 재구현. PDF 와이어 포맷, 레이아웃 엔진, TrueType 서브셋터, AES, PKCS#7 — 전부 처음부터 작성. unipdf와 공유 코드 없음, 혈통 없음, 라이선스 분쟁 가능성도 없음 (복사한 게 없으므로).",[19,5518,5519,5522,5523,5528],{},[22,5520,5521],{},"정말 MIT인가? \"어떤 조건에서 AGPL\"이 되는 부속 조항은?","\n없다. 저장소의 ",[5471,5524,5527],{"href":5525,"rel":5526},"https://github.com/gpdf-dev/gpdf/blob/main/LICENSE",[5475],"LICENSE","는 MIT 그 자체. 부록 없음, 사용 분야 제한 없음, 상용 등급 분리 없음. 클로즈드소스 배포 제품에 사용, 상용 SaaS에 임베드, 온프레 어플라이언스에 배포 — 모두 가능. 의무는 배포물에 라이선스와 저작권 표기를 포함하는 것뿐.",[19,5530,5531,5534,5535,657,5538,5541,5542,5544,5545,5548],{},[22,5532,5533],{},"전이적 의존성에 copyleft가 숨어 있지 않은가?","\ngpdf 코어의 ",[38,5536,5537],{},"go.mod",[38,5539,5540],{},"require"," 블록은 비어 있다. 전이적 AGPL도 GPL도 아무것도 없다. ",[38,5543,5336],{}," 후 ",[38,5546,5547],{},"go mod graph | grep gpdf","로 확인할 수 있다.",[19,5550,5551,5554],{},[22,5552,5553],{},"라이선스 키 제거가 그렇게 중요한가?","\n어떤 팀에는 전부다. 라이선스 키는 시크릿 매니저에 들어가야 하고, 로테이션 대상이고, 감사 대상이고, 모든 컨테이너 이미지에 포함되어야 하고, 로그에 누출되면 안 된다. 멀티테넌트 SaaS에서 pod이 수백 개라면 실제 운영 면이 된다. 요구 자체를 없애면 사고 한 부류가 사라진다.",[19,5556,5557,5564,5565,5568,5569,5573],{},[22,5558,5559,5560,5563],{},"기존 unipdf 코드는 ",[38,5561,5562],{},"creator.Block.SetPos","로 절대 좌표를 많이 쓴다. gpdf에 등가물은?","\n있다 — ",[38,5566,5567],{},"page.Absolute(x, y, fn)","으로 명시적 좌표에 서브트리를 둘 수 있다. 다만 코드가 절대 좌표 위주라면 레이아웃 엔진 모델은 문법 전환이 아니라 사고방식 전환이다. 견적 전에 ",[5471,5570,5572],{"href":5571},"/ko/blog/12-column-grid","12 컬럼 그리드 글","을 읽는 걸 추천. 다시 쓴 코드가 보통 원본보다 짧다.",[19,5575,5576,5579],{},[22,5577,5578],{},"미래에 UniDoc이 unipdf를 MIT로 재라이선스하면?","\n선택지가 하나 늘어난다. gpdf의 베팅은 \"unipdf가 영원히 AGPL\"이 아니라, \"시작 시 등록 호출을 요구하는 라이선스와 개발자별 재무 갱신은 대부분의 워크로드에 존재하지 않아도 되는 비용\"이라는 점. 내일 재라이선스되더라도 라이선스 키의 운영 면은 그들이 제거할 때까지 남는다.",[14,5581,5583],{"id":5582},"gpdf-사용해-보기","gpdf 사용해 보기",[19,5585,5586],{},"gpdf는 Go의 PDF 생성 라이브러리. MIT, 외부 의존성 0, 라이선스 키 불필요, 네이티브 CJK 지원.",[271,5588,5592],{"className":5589,"code":5590,"language":5591,"meta":276,"style":276},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go get github.com/gpdf-dev/gpdf\n","bash",[38,5593,5594],{"__ignoreMap":276},[280,5595,5596,5598,5601],{"class":282,"line":283},[280,5597,275],{"class":301},[280,5599,5600],{"class":369}," get",[280,5602,5603],{"class":369}," github.com/gpdf-dev/gpdf\n",[19,5605,5606,5611,5612],{},[5471,5607,5610],{"href":5608,"rel":5609},"https://github.com/gpdf-dev/gpdf",[5475],"⭐ Star on GitHub"," · ",[5471,5613,5616],{"href":5614,"rel":5615},"https://gpdf.dev/ko/docs/quickstart",[5475],"문서",[14,5618,5620],{"id":5619},"다음-읽을거리","다음 읽을거리",[5462,5622,5623,5629,5634,5640],{},[94,5624,5625],{},[5471,5626,5628],{"href":5627},"/ko/blog/embed-japanese-font","gpdf에 한국어 폰트를 임베드하는 방법",[94,5630,5631],{},[5471,5632,5633],{"href":5571},"12 컬럼 그리드: PDF 레이아웃에 Bootstrap 사고방식을",[94,5635,5636],{},[5471,5637,5639],{"href":5638},"/ko/blog/gofpdf-migration","gofpdf는 아카이브되었다. gpdf로 마이그레이션하는 가이드",[94,5641,5642,5646],{},[5471,5643,5645],{"href":5614,"rel":5644},[5475],"Quickstart"," — 5분 셋업",[2952,5648,5649],{},"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 .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 .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 .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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":276,"searchDepth":290,"depth":290,"links":5651},[5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664,5665,5666],{"id":16,"depth":290,"text":17},{"id":82,"depth":290,"text":83},{"id":121,"depth":290,"text":122},{"id":265,"depth":290,"text":266},{"id":565,"depth":290,"text":566},{"id":918,"depth":290,"text":919},{"id":1748,"depth":290,"text":1749},{"id":2819,"depth":290,"text":2820},{"id":3574,"depth":290,"text":3575},{"id":4561,"depth":290,"text":4562},{"id":5344,"depth":290,"text":5345},{"id":5456,"depth":290,"text":5457},{"id":5509,"depth":290,"text":5510},{"id":5582,"depth":290,"text":5583},{"id":5619,"depth":290,"text":5620},"2026-04-29","UniDoc의 unipdf는 AGPL v3 또는 개발자별 유료 상용 라이선스. MIT, 의존성 0, 라이선스 키 불필요한 gpdf로 옮기는 방법.",false,"md",{"name":5672,"totalTime":5673,"tools":5674,"steps":5676},"Go 프로젝트를 unidoc/unipdf에서 gpdf로 마이그레이션","PT45M",[5675],"Go 1.22+",[5677,5680,5683,5686,5689,5692,5695],{"name":5678,"text":5679},"라이선스 등록 코드를 삭제한다","main과 init() 안의 license.SetMeteredKey 또는 license.SetLicenseKey 호출을 삭제한다. gpdf에는 라이선스 키, metering API, 시작 등록 절차가 없다.",{"name":5681,"text":5682},"import 경로를 교체한다","github.com/unidoc/unipdf/v3/creator와 github.com/unidoc/unipdf/v3/model을 github.com/gpdf-dev/gpdf, github.com/gpdf-dev/gpdf/document, github.com/gpdf-dev/gpdf/template로 바꾼다.",{"name":5684,"text":5685},"creator.New를 gpdf.NewDocument로 교체한다","gpdf.NewDocument(WithPageSize(document.A4), WithMargins(...))로 문서를 만든다. 페이지는 doc.AddPage()가 반환하는 PageBuilder 단위이며 자유 커서는 없다.",{"name":5687,"text":5688},"Paragraph / StyledParagraph를 c.Text로 다시 쓴다","Paragraph를 만들고 c.Draw에 넘기는 대신, 컬럼 안에서 c.Text(string, options...)를 호출한다. 폰트, 사이즈, 색은 구조체 필드가 아닌 per-text 옵션이 된다.",{"name":5690,"text":5691},"테이블을 12 컬럼 그리드로 다시 쓴다","creator.NewTable(cols).SetColumnWidths와 SetCellSpan을 AutoRow 안의 row.Col(span, fn)으로 바꾼다. 12 컬럼 그리드가 너비를 퍼센트로 처리하고 페이지 분할도 자동 수행한다.",{"name":5693,"text":5694},"폰트를 경로가 아닌 바이트로 다시 등록한다","model.NewCompositePdfFontFromTTFFile을 생성 시점의 gpdf.WithFont(name, ttfBytes)로 바꾼다. //go:embed로 TTF를 바이너리에 포함하면 런타임에 폰트 경로가 필요 없다.",{"name":5696,"text":5697},"출력 호출을 전환한다","c.WriteToFile(path) 대신 doc.Generate()로 바이트를 얻어 os.WriteFile(path, data, 0o644)로 쓴다. io.Writer로 직접 쓰려면 doc.Render(w)를 사용한다.",null,{},"/ko/blog/unidoc-migration",{"title":5,"description":5668},"ko/blog/016.unidoc-migration",[5704,5705,5706],"migration","comparison","tutorial","YB7QyxcYj-QI4JItoRCpPHxoSdERbVfSG_Z4QnJ-5Q0",1779199026807]