fpdf.go 141 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860
  1. /*
  2. * Copyright (c) 2013-2014 Kurt Jung (Gmail: kurt.w.jung)
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. package gofpdf
  17. // Version: 1.7
  18. // Date: 2011-06-18
  19. // Author: Olivier PLATHEY
  20. // Port to Go: Kurt Jung, 2013-07-15
  21. import (
  22. "bytes"
  23. "encoding/binary"
  24. "encoding/json"
  25. "fmt"
  26. "image"
  27. "image/color"
  28. "image/gif"
  29. "image/jpeg"
  30. "image/png"
  31. "io"
  32. "io/ioutil"
  33. "math"
  34. "os"
  35. "path"
  36. "sort"
  37. "strconv"
  38. "strings"
  39. "time"
  40. )
  41. var gl struct {
  42. catalogSort bool
  43. noCompress bool // Initial zero value indicates compression
  44. creationDate time.Time
  45. }
  46. type fmtBuffer struct {
  47. bytes.Buffer
  48. }
  49. func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
  50. b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...))
  51. }
  52. func fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr string, size SizeType) (f *Fpdf) {
  53. f = new(Fpdf)
  54. if orientationStr == "" {
  55. orientationStr = "p"
  56. } else {
  57. orientationStr = strings.ToLower(orientationStr)
  58. }
  59. if unitStr == "" {
  60. unitStr = "mm"
  61. }
  62. if sizeStr == "" {
  63. sizeStr = "A4"
  64. }
  65. if fontDirStr == "" {
  66. fontDirStr = "."
  67. }
  68. f.page = 0
  69. f.n = 2
  70. f.pages = make([]*bytes.Buffer, 0, 8)
  71. f.pages = append(f.pages, bytes.NewBufferString("")) // pages[0] is unused (1-based)
  72. f.pageSizes = make(map[int]SizeType)
  73. f.pageBoxes = make(map[int]map[string]PageBox)
  74. f.defPageBoxes = make(map[string]PageBox)
  75. f.state = 0
  76. f.fonts = make(map[string]fontDefType)
  77. f.fontFiles = make(map[string]fontFileType)
  78. f.diffs = make([]string, 0, 8)
  79. f.templates = make(map[string]Template)
  80. f.templateObjects = make(map[string]int)
  81. f.importedObjs = make(map[string][]byte, 0)
  82. f.importedObjPos = make(map[string]map[int]string, 0)
  83. f.importedTplObjs = make(map[string]string)
  84. f.importedTplIDs = make(map[string]int, 0)
  85. f.images = make(map[string]*ImageInfoType)
  86. f.pageLinks = make([][]linkType, 0, 8)
  87. f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) // pageLinks[0] is unused (1-based)
  88. f.links = make([]intLinkType, 0, 8)
  89. f.links = append(f.links, intLinkType{}) // links[0] is unused (1-based)
  90. f.aliasMap = make(map[string]string)
  91. f.inHeader = false
  92. f.inFooter = false
  93. f.lasth = 0
  94. f.fontFamily = ""
  95. f.fontStyle = ""
  96. f.SetFontSize(12)
  97. f.underline = false
  98. f.setDrawColor(0, 0, 0)
  99. f.setFillColor(0, 0, 0)
  100. f.setTextColor(0, 0, 0)
  101. f.colorFlag = false
  102. f.ws = 0
  103. f.fontpath = fontDirStr
  104. // Core fonts
  105. f.coreFonts = map[string]bool{
  106. "courier": true,
  107. "helvetica": true,
  108. "times": true,
  109. "symbol": true,
  110. "zapfdingbats": true,
  111. }
  112. // Scale factor
  113. switch unitStr {
  114. case "pt", "point":
  115. f.k = 1.0
  116. case "mm":
  117. f.k = 72.0 / 25.4
  118. case "cm":
  119. f.k = 72.0 / 2.54
  120. case "in", "inch":
  121. f.k = 72.0
  122. default:
  123. f.err = fmt.Errorf("incorrect unit %s", unitStr)
  124. return
  125. }
  126. f.unitStr = unitStr
  127. // Page sizes
  128. f.stdPageSizes = make(map[string]SizeType)
  129. f.stdPageSizes["a3"] = SizeType{841.89, 1190.55}
  130. f.stdPageSizes["a4"] = SizeType{595.28, 841.89}
  131. f.stdPageSizes["a5"] = SizeType{420.94, 595.28}
  132. f.stdPageSizes["a6"] = SizeType{297.64, 420.94}
  133. f.stdPageSizes["a2"] = SizeType{1190.55, 1683.78}
  134. f.stdPageSizes["a1"] = SizeType{1683.78, 2383.94}
  135. f.stdPageSizes["letter"] = SizeType{612, 792}
  136. f.stdPageSizes["legal"] = SizeType{612, 1008}
  137. f.stdPageSizes["tabloid"] = SizeType{792, 1224}
  138. if size.Wd > 0 && size.Ht > 0 {
  139. f.defPageSize = size
  140. } else {
  141. f.defPageSize = f.getpagesizestr(sizeStr)
  142. if f.err != nil {
  143. return
  144. }
  145. }
  146. f.curPageSize = f.defPageSize
  147. // Page orientation
  148. switch orientationStr {
  149. case "p", "portrait":
  150. f.defOrientation = "P"
  151. f.w = f.defPageSize.Wd
  152. f.h = f.defPageSize.Ht
  153. // dbg("Assign h: %8.2f", f.h)
  154. case "l", "landscape":
  155. f.defOrientation = "L"
  156. f.w = f.defPageSize.Ht
  157. f.h = f.defPageSize.Wd
  158. default:
  159. f.err = fmt.Errorf("incorrect orientation: %s", orientationStr)
  160. return
  161. }
  162. f.curOrientation = f.defOrientation
  163. f.wPt = f.w * f.k
  164. f.hPt = f.h * f.k
  165. // Page margins (1 cm)
  166. margin := 28.35 / f.k
  167. f.SetMargins(margin, margin, margin)
  168. // Interior cell margin (1 mm)
  169. f.cMargin = margin / 10
  170. // Line width (0.2 mm)
  171. f.lineWidth = 0.567 / f.k
  172. // Automatic page break
  173. f.SetAutoPageBreak(true, 2*margin)
  174. // Default display mode
  175. f.SetDisplayMode("default", "default")
  176. if f.err != nil {
  177. return
  178. }
  179. f.acceptPageBreak = func() bool {
  180. return f.autoPageBreak
  181. }
  182. // Enable compression
  183. f.SetCompression(!gl.noCompress)
  184. f.spotColorMap = make(map[string]spotColorType)
  185. f.blendList = make([]blendModeType, 0, 8)
  186. f.blendList = append(f.blendList, blendModeType{}) // blendList[0] is unused (1-based)
  187. f.blendMap = make(map[string]int)
  188. f.blendMode = "Normal"
  189. f.alpha = 1
  190. f.gradientList = make([]gradientType, 0, 8)
  191. f.gradientList = append(f.gradientList, gradientType{}) // gradientList[0] is unused
  192. // Set default PDF version number
  193. f.pdfVersion = "1.3"
  194. f.SetProducer("FPDF "+cnFpdfVersion, true)
  195. f.layerInit()
  196. f.catalogSort = gl.catalogSort
  197. f.creationDate = gl.creationDate
  198. f.userUnderlineThickness = 1
  199. return
  200. }
  201. // NewCustom returns a pointer to a new Fpdf instance. Its methods are
  202. // subsequently called to produce a single PDF document. NewCustom() is an
  203. // alternative to New() that provides additional customization. The PageSize()
  204. // example demonstrates this method.
  205. func NewCustom(init *InitType) (f *Fpdf) {
  206. return fpdfNew(init.OrientationStr, init.UnitStr, init.SizeStr, init.FontDirStr, init.Size)
  207. }
  208. // New returns a pointer to a new Fpdf instance. Its methods are subsequently
  209. // called to produce a single PDF document.
  210. //
  211. // orientationStr specifies the default page orientation. For portrait mode,
  212. // specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape".
  213. // An empty string will be replaced with "P".
  214. //
  215. // unitStr specifies the unit of length used in size parameters for elements
  216. // other than fonts, which are always measured in points. Specify "pt" for
  217. // point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty
  218. // string will be replaced with "mm".
  219. //
  220. // sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
  221. // "Letter", "Legal", or "Tabloid". An empty string will be replaced with "A4".
  222. //
  223. // fontDirStr specifies the file system location in which font resources will
  224. // be found. An empty string is replaced with ".". This argument only needs to
  225. // reference an actual directory if a font other than one of the core
  226. // fonts is used. The core fonts are "courier", "helvetica" (also called
  227. // "arial"), "times", and "zapfdingbats" (also called "symbol").
  228. func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
  229. return fpdfNew(orientationStr, unitStr, sizeStr, fontDirStr, SizeType{0, 0})
  230. }
  231. // Ok returns true if no processing errors have occurred.
  232. func (f *Fpdf) Ok() bool {
  233. return f.err == nil
  234. }
  235. // Err returns true if a processing error has occurred.
  236. func (f *Fpdf) Err() bool {
  237. return f.err != nil
  238. }
  239. // ClearError unsets the internal Fpdf error. This method should be used with
  240. // care, as an internal error condition usually indicates an unrecoverable
  241. // problem with the generation of a document. It is intended to deal with cases
  242. // in which an error is used to select an alternate form of the document.
  243. func (f *Fpdf) ClearError() {
  244. f.err = nil
  245. }
  246. // SetErrorf sets the internal Fpdf error with formatted text to halt PDF
  247. // generation; this may facilitate error handling by application. If an error
  248. // condition is already set, this call is ignored.
  249. //
  250. // See the documentation for printing in the standard fmt package for details
  251. // about fmtStr and args.
  252. func (f *Fpdf) SetErrorf(fmtStr string, args ...interface{}) {
  253. if f.err == nil {
  254. f.err = fmt.Errorf(fmtStr, args...)
  255. }
  256. }
  257. // String satisfies the fmt.Stringer interface and summarizes the Fpdf
  258. // instance.
  259. func (f *Fpdf) String() string {
  260. return "Fpdf " + cnFpdfVersion
  261. }
  262. // SetError sets an error to halt PDF generation. This may facilitate error
  263. // handling by application. See also Ok(), Err() and Error().
  264. func (f *Fpdf) SetError(err error) {
  265. if f.err == nil && err != nil {
  266. f.err = err
  267. }
  268. }
  269. // Error returns the internal Fpdf error; this will be nil if no error has occurred.
  270. func (f *Fpdf) Error() error {
  271. return f.err
  272. }
  273. // GetPageSize returns the current page's width and height. This is the paper's
  274. // size. To compute the size of the area being used, subtract the margins (see
  275. // GetMargins()).
  276. func (f *Fpdf) GetPageSize() (width, height float64) {
  277. width = f.w
  278. height = f.h
  279. return
  280. }
  281. // GetMargins returns the left, top, right, and bottom margins. The first three
  282. // are set with the SetMargins() method. The bottom margin is set with the
  283. // SetAutoPageBreak() method.
  284. func (f *Fpdf) GetMargins() (left, top, right, bottom float64) {
  285. left = f.lMargin
  286. top = f.tMargin
  287. right = f.rMargin
  288. bottom = f.bMargin
  289. return
  290. }
  291. // SetMargins defines the left, top and right margins. By default, they equal 1
  292. // cm. Call this method to change them. If the value of the right margin is
  293. // less than zero, it is set to the same as the left margin.
  294. func (f *Fpdf) SetMargins(left, top, right float64) {
  295. f.lMargin = left
  296. f.tMargin = top
  297. if right < 0 {
  298. right = left
  299. }
  300. f.rMargin = right
  301. }
  302. // SetLeftMargin defines the left margin. The method can be called before
  303. // creating the first page. If the current abscissa gets out of page, it is
  304. // brought back to the margin.
  305. func (f *Fpdf) SetLeftMargin(margin float64) {
  306. f.lMargin = margin
  307. if f.page > 0 && f.x < margin {
  308. f.x = margin
  309. }
  310. }
  311. // GetCellMargin returns the cell margin. This is the amount of space before
  312. // and after the text within a cell that's left blank, and is in units passed
  313. // to New(). It defaults to 1mm.
  314. func (f *Fpdf) GetCellMargin() float64 {
  315. return f.cMargin
  316. }
  317. // SetCellMargin sets the cell margin. This is the amount of space before and
  318. // after the text within a cell that's left blank, and is in units passed to
  319. // New().
  320. func (f *Fpdf) SetCellMargin(margin float64) {
  321. f.cMargin = margin
  322. }
  323. // SetPageBoxRec sets the page box for the current page, and any following
  324. // pages. Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox,
  325. // art and artbox box types are case insensitive. See SetPageBox() for a method
  326. // that specifies the coordinates and extent of the page box individually.
  327. func (f *Fpdf) SetPageBoxRec(t string, pb PageBox) {
  328. switch strings.ToLower(t) {
  329. case "trim":
  330. fallthrough
  331. case "trimbox":
  332. t = "TrimBox"
  333. case "crop":
  334. fallthrough
  335. case "cropbox":
  336. t = "CropBox"
  337. case "bleed":
  338. fallthrough
  339. case "bleedbox":
  340. t = "BleedBox"
  341. case "art":
  342. fallthrough
  343. case "artbox":
  344. t = "ArtBox"
  345. default:
  346. f.err = fmt.Errorf("%s is not a valid page box type", t)
  347. return
  348. }
  349. pb.X = pb.X * f.k
  350. pb.Y = pb.Y * f.k
  351. pb.Wd = (pb.Wd * f.k) + pb.X
  352. pb.Ht = (pb.Ht * f.k) + pb.Y
  353. if f.page > 0 {
  354. f.pageBoxes[f.page][t] = pb
  355. }
  356. // always override. page defaults are supplied in addPage function
  357. f.defPageBoxes[t] = pb
  358. }
  359. // SetPageBox sets the page box for the current page, and any following pages.
  360. // Allowable types are trim, trimbox, crop, cropbox, bleed, bleedbox, art and
  361. // artbox box types are case insensitive.
  362. func (f *Fpdf) SetPageBox(t string, x, y, wd, ht float64) {
  363. f.SetPageBoxRec(t, PageBox{SizeType{Wd: wd, Ht: ht}, PointType{X: x, Y: y}})
  364. }
  365. // SetPage sets the current page to that of a valid page in the PDF document.
  366. // pageNum is one-based. The SetPage() example demonstrates this method.
  367. func (f *Fpdf) SetPage(pageNum int) {
  368. if (pageNum > 0) && (pageNum < len(f.pages)) {
  369. f.page = pageNum
  370. }
  371. }
  372. // PageCount returns the number of pages currently in the document. Since page
  373. // numbers in gofpdf are one-based, the page count is the same as the page
  374. // number of the current last page.
  375. func (f *Fpdf) PageCount() int {
  376. return len(f.pages) - 1
  377. }
  378. // SetFontLocation sets the location in the file system of the font and font
  379. // definition files.
  380. func (f *Fpdf) SetFontLocation(fontDirStr string) {
  381. f.fontpath = fontDirStr
  382. }
  383. // SetFontLoader sets a loader used to read font files (.json and .z) from an
  384. // arbitrary source. If a font loader has been specified, it is used to load
  385. // the named font resources when AddFont() is called. If this operation fails,
  386. // an attempt is made to load the resources from the configured font directory
  387. // (see SetFontLocation()).
  388. func (f *Fpdf) SetFontLoader(loader FontLoader) {
  389. f.fontLoader = loader
  390. }
  391. // SetHeaderFuncMode sets the function that lets the application render the
  392. // page header. See SetHeaderFunc() for more details. The value for homeMode
  393. // should be set to true to have the current position set to the left and top
  394. // margin after the header function is called.
  395. func (f *Fpdf) SetHeaderFuncMode(fnc func(), homeMode bool) {
  396. f.headerFnc = fnc
  397. f.headerHomeMode = homeMode
  398. }
  399. // SetHeaderFunc sets the function that lets the application render the page
  400. // header. The specified function is automatically called by AddPage() and
  401. // should not be called directly by the application. The implementation in Fpdf
  402. // is empty, so you have to provide an appropriate function if you want page
  403. // headers. fnc will typically be a closure that has access to the Fpdf
  404. // instance and other document generation variables.
  405. //
  406. // A header is a convenient place to put background content that repeats on
  407. // each page such as a watermark. When this is done, remember to reset the X
  408. // and Y values so the normal content begins where expected. Including a
  409. // watermark on each page is demonstrated in the example for TransformRotate.
  410. //
  411. // This method is demonstrated in the example for AddPage().
  412. func (f *Fpdf) SetHeaderFunc(fnc func()) {
  413. f.headerFnc = fnc
  414. }
  415. // SetFooterFunc sets the function that lets the application render the page
  416. // footer. The specified function is automatically called by AddPage() and
  417. // Close() and should not be called directly by the application. The
  418. // implementation in Fpdf is empty, so you have to provide an appropriate
  419. // function if you want page footers. fnc will typically be a closure that has
  420. // access to the Fpdf instance and other document generation variables. See
  421. // SetFooterFuncLpi for a similar function that passes a last page indicator.
  422. //
  423. // This method is demonstrated in the example for AddPage().
  424. func (f *Fpdf) SetFooterFunc(fnc func()) {
  425. f.footerFnc = fnc
  426. f.footerFncLpi = nil
  427. }
  428. // SetFooterFuncLpi sets the function that lets the application render the page
  429. // footer. The specified function is automatically called by AddPage() and
  430. // Close() and should not be called directly by the application. It is passed a
  431. // boolean that is true if the last page of the document is being rendered. The
  432. // implementation in Fpdf is empty, so you have to provide an appropriate
  433. // function if you want page footers. fnc will typically be a closure that has
  434. // access to the Fpdf instance and other document generation variables.
  435. func (f *Fpdf) SetFooterFuncLpi(fnc func(lastPage bool)) {
  436. f.footerFncLpi = fnc
  437. f.footerFnc = nil
  438. }
  439. // SetTopMargin defines the top margin. The method can be called before
  440. // creating the first page.
  441. func (f *Fpdf) SetTopMargin(margin float64) {
  442. f.tMargin = margin
  443. }
  444. // SetRightMargin defines the right margin. The method can be called before
  445. // creating the first page.
  446. func (f *Fpdf) SetRightMargin(margin float64) {
  447. f.rMargin = margin
  448. }
  449. // GetAutoPageBreak returns true if automatic pages breaks are enabled, false
  450. // otherwise. This is followed by the triggering limit from the bottom of the
  451. // page. This value applies only if automatic page breaks are enabled.
  452. func (f *Fpdf) GetAutoPageBreak() (auto bool, margin float64) {
  453. auto = f.autoPageBreak
  454. margin = f.bMargin
  455. return
  456. }
  457. // SetAutoPageBreak enables or disables the automatic page breaking mode. When
  458. // enabling, the second parameter is the distance from the bottom of the page
  459. // that defines the triggering limit. By default, the mode is on and the margin
  460. // is 2 cm.
  461. func (f *Fpdf) SetAutoPageBreak(auto bool, margin float64) {
  462. f.autoPageBreak = auto
  463. f.bMargin = margin
  464. f.pageBreakTrigger = f.h - margin
  465. }
  466. // SetDisplayMode sets advisory display directives for the document viewer.
  467. // Pages can be displayed entirely on screen, occupy the full width of the
  468. // window, use real size, be scaled by a specific zooming factor or use viewer
  469. // default (configured in the Preferences menu of Adobe Reader). The page
  470. // layout can be specified so that pages are displayed individually or in
  471. // pairs.
  472. //
  473. // zoomStr can be "fullpage" to display the entire page on screen, "fullwidth"
  474. // to use maximum width of window, "real" to use real size (equivalent to 100%
  475. // zoom) or "default" to use viewer default mode.
  476. //
  477. // layoutStr can be "single" (or "SinglePage") to display one page at once,
  478. // "continuous" (or "OneColumn") to display pages continuously, "two" (or
  479. // "TwoColumnLeft") to display two pages on two columns with odd-numbered pages
  480. // on the left, or "TwoColumnRight" to display two pages on two columns with
  481. // odd-numbered pages on the right, or "TwoPageLeft" to display pages two at a
  482. // time with odd-numbered pages on the left, or "TwoPageRight" to display pages
  483. // two at a time with odd-numbered pages on the right, or "default" to use
  484. // viewer default mode.
  485. func (f *Fpdf) SetDisplayMode(zoomStr, layoutStr string) {
  486. if f.err != nil {
  487. return
  488. }
  489. if layoutStr == "" {
  490. layoutStr = "default"
  491. }
  492. switch zoomStr {
  493. case "fullpage", "fullwidth", "real", "default":
  494. f.zoomMode = zoomStr
  495. default:
  496. f.err = fmt.Errorf("incorrect zoom display mode: %s", zoomStr)
  497. return
  498. }
  499. switch layoutStr {
  500. case "single", "continuous", "two", "default", "SinglePage", "OneColumn",
  501. "TwoColumnLeft", "TwoColumnRight", "TwoPageLeft", "TwoPageRight":
  502. f.layoutMode = layoutStr
  503. default:
  504. f.err = fmt.Errorf("incorrect layout display mode: %s", layoutStr)
  505. return
  506. }
  507. }
  508. // SetDefaultCompression controls the default setting of the internal
  509. // compression flag. See SetCompression() for more details. Compression is on
  510. // by default.
  511. func SetDefaultCompression(compress bool) {
  512. gl.noCompress = !compress
  513. }
  514. // SetCompression activates or deactivates page compression with zlib. When
  515. // activated, the internal representation of each page is compressed, which
  516. // leads to a compression ratio of about 2 for the resulting document.
  517. // Compression is on by default.
  518. func (f *Fpdf) SetCompression(compress bool) {
  519. f.compress = compress
  520. }
  521. // SetProducer defines the producer of the document. isUTF8 indicates if the string
  522. // is encoded in ISO-8859-1 (false) or UTF-8 (true).
  523. func (f *Fpdf) SetProducer(producerStr string, isUTF8 bool) {
  524. if isUTF8 {
  525. producerStr = utf8toutf16(producerStr)
  526. }
  527. f.producer = producerStr
  528. }
  529. // SetTitle defines the title of the document. isUTF8 indicates if the string
  530. // is encoded in ISO-8859-1 (false) or UTF-8 (true).
  531. func (f *Fpdf) SetTitle(titleStr string, isUTF8 bool) {
  532. if isUTF8 {
  533. titleStr = utf8toutf16(titleStr)
  534. }
  535. f.title = titleStr
  536. }
  537. // SetSubject defines the subject of the document. isUTF8 indicates if the
  538. // string is encoded in ISO-8859-1 (false) or UTF-8 (true).
  539. func (f *Fpdf) SetSubject(subjectStr string, isUTF8 bool) {
  540. if isUTF8 {
  541. subjectStr = utf8toutf16(subjectStr)
  542. }
  543. f.subject = subjectStr
  544. }
  545. // SetAuthor defines the author of the document. isUTF8 indicates if the string
  546. // is encoded in ISO-8859-1 (false) or UTF-8 (true).
  547. func (f *Fpdf) SetAuthor(authorStr string, isUTF8 bool) {
  548. if isUTF8 {
  549. authorStr = utf8toutf16(authorStr)
  550. }
  551. f.author = authorStr
  552. }
  553. // SetKeywords defines the keywords of the document. keywordStr is a
  554. // space-delimited string, for example "invoice August". isUTF8 indicates if
  555. // the string is encoded
  556. func (f *Fpdf) SetKeywords(keywordsStr string, isUTF8 bool) {
  557. if isUTF8 {
  558. keywordsStr = utf8toutf16(keywordsStr)
  559. }
  560. f.keywords = keywordsStr
  561. }
  562. // SetCreator defines the creator of the document. isUTF8 indicates if the
  563. // string is encoded in ISO-8859-1 (false) or UTF-8 (true).
  564. func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) {
  565. if isUTF8 {
  566. creatorStr = utf8toutf16(creatorStr)
  567. }
  568. f.creator = creatorStr
  569. }
  570. // SetXmpMetadata defines XMP metadata that will be embedded with the document.
  571. func (f *Fpdf) SetXmpMetadata(xmpStream []byte) {
  572. f.xmp = xmpStream
  573. }
  574. // AliasNbPages defines an alias for the total number of pages. It will be
  575. // substituted as the document is closed. An empty string is replaced with the
  576. // string "{nb}".
  577. //
  578. // See the example for AddPage() for a demonstration of this method.
  579. func (f *Fpdf) AliasNbPages(aliasStr string) {
  580. if aliasStr == "" {
  581. aliasStr = "{nb}"
  582. }
  583. f.aliasNbPagesStr = aliasStr
  584. }
  585. // RTL enables right-to-left mode
  586. func (f *Fpdf) RTL() {
  587. f.isRTL = true
  588. }
  589. // LTR disables right-to-left mode
  590. func (f *Fpdf) LTR() {
  591. f.isRTL = false
  592. }
  593. // open begins a document
  594. func (f *Fpdf) open() {
  595. f.state = 1
  596. }
  597. // Close terminates the PDF document. It is not necessary to call this method
  598. // explicitly because Output(), OutputAndClose() and OutputFileAndClose() do it
  599. // automatically. If the document contains no page, AddPage() is called to
  600. // prevent the generation of an invalid document.
  601. func (f *Fpdf) Close() {
  602. if f.err == nil {
  603. if f.clipNest > 0 {
  604. f.err = fmt.Errorf("clip procedure must be explicitly ended")
  605. } else if f.transformNest > 0 {
  606. f.err = fmt.Errorf("transformation procedure must be explicitly ended")
  607. }
  608. }
  609. if f.err != nil {
  610. return
  611. }
  612. if f.state == 3 {
  613. return
  614. }
  615. if f.page == 0 {
  616. f.AddPage()
  617. if f.err != nil {
  618. return
  619. }
  620. }
  621. // Page footer
  622. f.inFooter = true
  623. if f.footerFnc != nil {
  624. f.footerFnc()
  625. } else if f.footerFncLpi != nil {
  626. f.footerFncLpi(true)
  627. }
  628. f.inFooter = false
  629. // Close page
  630. f.endpage()
  631. // Close document
  632. f.enddoc()
  633. return
  634. }
  635. // PageSize returns the width and height of the specified page in the units
  636. // established in New(). These return values are followed by the unit of
  637. // measure itself. If pageNum is zero or otherwise out of bounds, it returns
  638. // the default page size, that is, the size of the page that would be added by
  639. // AddPage().
  640. func (f *Fpdf) PageSize(pageNum int) (wd, ht float64, unitStr string) {
  641. sz, ok := f.pageSizes[pageNum]
  642. if ok {
  643. sz.Wd, sz.Ht = sz.Wd/f.k, sz.Ht/f.k
  644. } else {
  645. sz = f.defPageSize // user units
  646. }
  647. return sz.Wd, sz.Ht, f.unitStr
  648. }
  649. // AddPageFormat adds a new page with non-default orientation or size. See
  650. // AddPage() for more details.
  651. //
  652. // See New() for a description of orientationStr.
  653. //
  654. // size specifies the size of the new page in the units established in New().
  655. //
  656. // The PageSize() example demonstrates this method.
  657. func (f *Fpdf) AddPageFormat(orientationStr string, size SizeType) {
  658. if f.err != nil {
  659. return
  660. }
  661. if f.page != len(f.pages)-1 {
  662. f.page = len(f.pages) - 1
  663. }
  664. if f.state == 0 {
  665. f.open()
  666. }
  667. familyStr := f.fontFamily
  668. style := f.fontStyle
  669. if f.underline {
  670. style += "U"
  671. }
  672. fontsize := f.fontSizePt
  673. lw := f.lineWidth
  674. dc := f.color.draw
  675. fc := f.color.fill
  676. tc := f.color.text
  677. cf := f.colorFlag
  678. if f.page > 0 {
  679. f.inFooter = true
  680. // Page footer avoid double call on footer.
  681. if f.footerFnc != nil {
  682. f.footerFnc()
  683. } else if f.footerFncLpi != nil {
  684. f.footerFncLpi(false) // not last page.
  685. }
  686. f.inFooter = false
  687. // Close page
  688. f.endpage()
  689. }
  690. // Start new page
  691. f.beginpage(orientationStr, size)
  692. // Set line cap style to current value
  693. // f.out("2 J")
  694. f.outf("%d J", f.capStyle)
  695. // Set line join style to current value
  696. f.outf("%d j", f.joinStyle)
  697. // Set line width
  698. f.lineWidth = lw
  699. f.outf("%.2f w", lw*f.k)
  700. // Set dash pattern
  701. if len(f.dashArray) > 0 {
  702. f.outputDashPattern()
  703. }
  704. // Set font
  705. if familyStr != "" {
  706. f.SetFont(familyStr, style, fontsize)
  707. if f.err != nil {
  708. return
  709. }
  710. }
  711. // Set colors
  712. f.color.draw = dc
  713. if dc.str != "0 G" {
  714. f.out(dc.str)
  715. }
  716. f.color.fill = fc
  717. if fc.str != "0 g" {
  718. f.out(fc.str)
  719. }
  720. f.color.text = tc
  721. f.colorFlag = cf
  722. // Page header
  723. if f.headerFnc != nil {
  724. f.inHeader = true
  725. f.headerFnc()
  726. f.inHeader = false
  727. if f.headerHomeMode {
  728. f.SetHomeXY()
  729. }
  730. }
  731. // Restore line width
  732. if f.lineWidth != lw {
  733. f.lineWidth = lw
  734. f.outf("%.2f w", lw*f.k)
  735. }
  736. // Restore font
  737. if familyStr != "" {
  738. f.SetFont(familyStr, style, fontsize)
  739. if f.err != nil {
  740. return
  741. }
  742. }
  743. // Restore colors
  744. if f.color.draw.str != dc.str {
  745. f.color.draw = dc
  746. f.out(dc.str)
  747. }
  748. if f.color.fill.str != fc.str {
  749. f.color.fill = fc
  750. f.out(fc.str)
  751. }
  752. f.color.text = tc
  753. f.colorFlag = cf
  754. return
  755. }
  756. // AddPage adds a new page to the document. If a page is already present, the
  757. // Footer() method is called first to output the footer. Then the page is
  758. // added, the current position set to the top-left corner according to the left
  759. // and top margins, and Header() is called to display the header.
  760. //
  761. // The font which was set before calling is automatically restored. There is no
  762. // need to call SetFont() again if you want to continue with the same font. The
  763. // same is true for colors and line width.
  764. //
  765. // The origin of the coordinate system is at the top-left corner and increasing
  766. // ordinates go downwards.
  767. //
  768. // See AddPageFormat() for a version of this method that allows the page size
  769. // and orientation to be different than the default.
  770. func (f *Fpdf) AddPage() {
  771. if f.err != nil {
  772. return
  773. }
  774. // dbg("AddPage")
  775. f.AddPageFormat(f.defOrientation, f.defPageSize)
  776. return
  777. }
  778. // PageNo returns the current page number.
  779. //
  780. // See the example for AddPage() for a demonstration of this method.
  781. func (f *Fpdf) PageNo() int {
  782. return f.page
  783. }
  784. func colorComp(v int) (int, float64) {
  785. if v < 0 {
  786. v = 0
  787. } else if v > 255 {
  788. v = 255
  789. }
  790. return v, float64(v) / 255.0
  791. }
  792. func rgbColorValue(r, g, b int, grayStr, fullStr string) (clr colorType) {
  793. clr.ir, clr.r = colorComp(r)
  794. clr.ig, clr.g = colorComp(g)
  795. clr.ib, clr.b = colorComp(b)
  796. clr.mode = colorModeRGB
  797. clr.gray = clr.ir == clr.ig && clr.r == clr.b
  798. if len(grayStr) > 0 {
  799. if clr.gray {
  800. clr.str = sprintf("%.3f %s", clr.r, grayStr)
  801. } else {
  802. clr.str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr)
  803. }
  804. } else {
  805. clr.str = sprintf("%.3f %.3f %.3f", clr.r, clr.g, clr.b)
  806. }
  807. return
  808. }
  809. // SetDrawColor defines the color used for all drawing operations (lines,
  810. // rectangles and cell borders). It is expressed in RGB components (0 - 255).
  811. // The method can be called before the first page is created. The value is
  812. // retained from page to page.
  813. func (f *Fpdf) SetDrawColor(r, g, b int) {
  814. f.setDrawColor(r, g, b)
  815. }
  816. func (f *Fpdf) setDrawColor(r, g, b int) {
  817. f.color.draw = rgbColorValue(r, g, b, "G", "RG")
  818. if f.page > 0 {
  819. f.out(f.color.draw.str)
  820. }
  821. }
  822. // GetDrawColor returns the most recently set draw color as RGB components (0 -
  823. // 255). This will not be the current value if a draw color of some other type
  824. // (for example, spot) has been more recently set.
  825. func (f *Fpdf) GetDrawColor() (int, int, int) {
  826. return f.color.draw.ir, f.color.draw.ig, f.color.draw.ib
  827. }
  828. // SetFillColor defines the color used for all filling operations (filled
  829. // rectangles and cell backgrounds). It is expressed in RGB components (0
  830. // -255). The method can be called before the first page is created and the
  831. // value is retained from page to page.
  832. func (f *Fpdf) SetFillColor(r, g, b int) {
  833. f.setFillColor(r, g, b)
  834. }
  835. func (f *Fpdf) setFillColor(r, g, b int) {
  836. f.color.fill = rgbColorValue(r, g, b, "g", "rg")
  837. f.colorFlag = f.color.fill.str != f.color.text.str
  838. if f.page > 0 {
  839. f.out(f.color.fill.str)
  840. }
  841. }
  842. // GetFillColor returns the most recently set fill color as RGB components (0 -
  843. // 255). This will not be the current value if a fill color of some other type
  844. // (for example, spot) has been more recently set.
  845. func (f *Fpdf) GetFillColor() (int, int, int) {
  846. return f.color.fill.ir, f.color.fill.ig, f.color.fill.ib
  847. }
  848. // SetTextColor defines the color used for text. It is expressed in RGB
  849. // components (0 - 255). The method can be called before the first page is
  850. // created. The value is retained from page to page.
  851. func (f *Fpdf) SetTextColor(r, g, b int) {
  852. f.setTextColor(r, g, b)
  853. }
  854. func (f *Fpdf) setTextColor(r, g, b int) {
  855. f.color.text = rgbColorValue(r, g, b, "g", "rg")
  856. f.colorFlag = f.color.fill.str != f.color.text.str
  857. }
  858. // GetTextColor returns the most recently set text color as RGB components (0 -
  859. // 255). This will not be the current value if a text color of some other type
  860. // (for example, spot) has been more recently set.
  861. func (f *Fpdf) GetTextColor() (int, int, int) {
  862. return f.color.text.ir, f.color.text.ig, f.color.text.ib
  863. }
  864. // GetStringWidth returns the length of a string in user units. A font must be
  865. // currently selected.
  866. func (f *Fpdf) GetStringWidth(s string) float64 {
  867. if f.err != nil {
  868. return 0
  869. }
  870. w := f.GetStringSymbolWidth(s)
  871. return float64(w) * f.fontSize / 1000
  872. }
  873. // GetStringSymbolWidth returns the length of a string in glyf units. A font must be
  874. // currently selected.
  875. func (f *Fpdf) GetStringSymbolWidth(s string) int {
  876. if f.err != nil {
  877. return 0
  878. }
  879. w := 0
  880. if f.isCurrentUTF8 {
  881. unicode := []rune(s)
  882. for _, char := range unicode {
  883. intChar := int(char)
  884. if len(f.currentFont.Cw) >= intChar && f.currentFont.Cw[intChar] > 0 {
  885. if f.currentFont.Cw[intChar] != 65535 {
  886. w += f.currentFont.Cw[intChar]
  887. }
  888. } else if f.currentFont.Desc.MissingWidth != 0 {
  889. w += f.currentFont.Desc.MissingWidth
  890. } else {
  891. w += 500
  892. }
  893. }
  894. } else {
  895. for _, ch := range []byte(s) {
  896. if ch == 0 {
  897. break
  898. }
  899. w += f.currentFont.Cw[ch]
  900. }
  901. }
  902. return w
  903. }
  904. // SetLineWidth defines the line width. By default, the value equals 0.2 mm.
  905. // The method can be called before the first page is created. The value is
  906. // retained from page to page.
  907. func (f *Fpdf) SetLineWidth(width float64) {
  908. f.setLineWidth(width)
  909. }
  910. func (f *Fpdf) setLineWidth(width float64) {
  911. f.lineWidth = width
  912. if f.page > 0 {
  913. f.outf("%.2f w", width*f.k)
  914. }
  915. }
  916. // GetLineWidth returns the current line thickness.
  917. func (f *Fpdf) GetLineWidth() float64 {
  918. return f.lineWidth
  919. }
  920. // SetLineCapStyle defines the line cap style. styleStr should be "butt",
  921. // "round" or "square". A square style projects from the end of the line. The
  922. // method can be called before the first page is created. The value is
  923. // retained from page to page.
  924. func (f *Fpdf) SetLineCapStyle(styleStr string) {
  925. var capStyle int
  926. switch styleStr {
  927. case "round":
  928. capStyle = 1
  929. case "square":
  930. capStyle = 2
  931. default:
  932. capStyle = 0
  933. }
  934. f.capStyle = capStyle
  935. if f.page > 0 {
  936. f.outf("%d J", f.capStyle)
  937. }
  938. }
  939. // SetLineJoinStyle defines the line cap style. styleStr should be "miter",
  940. // "round" or "bevel". The method can be called before the first page
  941. // is created. The value is retained from page to page.
  942. func (f *Fpdf) SetLineJoinStyle(styleStr string) {
  943. var joinStyle int
  944. switch styleStr {
  945. case "round":
  946. joinStyle = 1
  947. case "bevel":
  948. joinStyle = 2
  949. default:
  950. joinStyle = 0
  951. }
  952. f.joinStyle = joinStyle
  953. if f.page > 0 {
  954. f.outf("%d j", f.joinStyle)
  955. }
  956. }
  957. // SetDashPattern sets the dash pattern that is used to draw lines. The
  958. // dashArray elements are numbers that specify the lengths, in units
  959. // established in New(), of alternating dashes and gaps. The dash phase
  960. // specifies the distance into the dash pattern at which to start the dash. The
  961. // dash pattern is retained from page to page. Call this method with an empty
  962. // array to restore solid line drawing.
  963. //
  964. // The Beziergon() example demonstrates this method.
  965. func (f *Fpdf) SetDashPattern(dashArray []float64, dashPhase float64) {
  966. scaled := make([]float64, len(dashArray))
  967. for i, value := range dashArray {
  968. scaled[i] = value * f.k
  969. }
  970. dashPhase *= f.k
  971. f.dashArray = scaled
  972. f.dashPhase = dashPhase
  973. if f.page > 0 {
  974. f.outputDashPattern()
  975. }
  976. }
  977. func (f *Fpdf) outputDashPattern() {
  978. var buf bytes.Buffer
  979. buf.WriteByte('[')
  980. for i, value := range f.dashArray {
  981. if i > 0 {
  982. buf.WriteByte(' ')
  983. }
  984. buf.WriteString(strconv.FormatFloat(value, 'f', 2, 64))
  985. }
  986. buf.WriteString("] ")
  987. buf.WriteString(strconv.FormatFloat(f.dashPhase, 'f', 2, 64))
  988. buf.WriteString(" d")
  989. f.outbuf(&buf)
  990. }
  991. // Line draws a line between points (x1, y1) and (x2, y2) using the current
  992. // draw color, line width and cap style.
  993. func (f *Fpdf) Line(x1, y1, x2, y2 float64) {
  994. f.outf("%.2f %.2f m %.2f %.2f l S", x1*f.k, (f.h-y1)*f.k, x2*f.k, (f.h-y2)*f.k)
  995. }
  996. // fillDrawOp corrects path painting operators
  997. func fillDrawOp(styleStr string) (opStr string) {
  998. switch strings.ToUpper(styleStr) {
  999. case "", "D":
  1000. // Stroke the path.
  1001. opStr = "S"
  1002. case "F":
  1003. // fill the path, using the nonzero winding number rule
  1004. opStr = "f"
  1005. case "F*":
  1006. // fill the path, using the even-odd rule
  1007. opStr = "f*"
  1008. case "FD", "DF":
  1009. // fill and then stroke the path, using the nonzero winding number rule
  1010. opStr = "B"
  1011. case "FD*", "DF*":
  1012. // fill and then stroke the path, using the even-odd rule
  1013. opStr = "B*"
  1014. default:
  1015. opStr = styleStr
  1016. }
  1017. return
  1018. }
  1019. // Rect outputs a rectangle of width w and height h with the upper left corner
  1020. // positioned at point (x, y).
  1021. //
  1022. // It can be drawn (border only), filled (with no border) or both. styleStr can
  1023. // be "F" for filled, "D" for outlined only, or "DF" or "FD" for outlined and
  1024. // filled. An empty string will be replaced with "D". Drawing uses the current
  1025. // draw color and line width centered on the rectangle's perimeter. Filling
  1026. // uses the current fill color.
  1027. func (f *Fpdf) Rect(x, y, w, h float64, styleStr string) {
  1028. f.outf("%.2f %.2f %.2f %.2f re %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, fillDrawOp(styleStr))
  1029. }
  1030. // RoundedRect outputs a rectangle of width w and height h with the upper left
  1031. // corner positioned at point (x, y). It can be drawn (border only), filled
  1032. // (with no border) or both. styleStr can be "F" for filled, "D" for outlined
  1033. // only, or "DF" or "FD" for outlined and filled. An empty string will be
  1034. // replaced with "D". Drawing uses the current draw color and line width
  1035. // centered on the rectangle's perimeter. Filling uses the current fill color.
  1036. // The rounded corners of the rectangle are specified by radius r. corners is a
  1037. // string that includes "1" to round the upper left corner, "2" to round the
  1038. // upper right corner, "3" to round the lower right corner, and "4" to round
  1039. // the lower left corner. The RoundedRect example demonstrates this method.
  1040. func (f *Fpdf) RoundedRect(x, y, w, h, r float64, corners string, stylestr string) {
  1041. // This routine was adapted by Brigham Thompson from a script by Christophe Prugnaud
  1042. k := f.k
  1043. hp := f.h
  1044. myArc := r * (4.0 / 3.0) * (math.Sqrt2 - 1.0)
  1045. f.outf("q %.5f %.5f m", (x+r)*k, (hp-y)*k)
  1046. xc := x + w - r
  1047. yc := y + r
  1048. f.outf("%.5f %.5f l", xc*k, (hp-y)*k)
  1049. if strings.Contains(corners, "2") == false {
  1050. f.outf("%.5f %.5f l", (x+w)*k, (hp-y)*k)
  1051. } else {
  1052. f.clipArc(xc+myArc, yc-r, xc+r, yc-myArc, xc+r, yc)
  1053. }
  1054. xc = x + w - r
  1055. yc = y + h - r
  1056. f.outf("%.5f %.5f l", (x+w)*k, (hp-yc)*k)
  1057. if strings.Contains(corners, "3") == false {
  1058. f.outf("%.5f %.5f l", (x+w)*k, (hp-(y+h))*k)
  1059. } else {
  1060. f.clipArc(xc+r, yc+myArc, xc+myArc, yc+r, xc, yc+r)
  1061. }
  1062. xc = x + r
  1063. yc = y + h - r
  1064. f.outf("%.5f %.5f l", xc*k, (hp-(y+h))*k)
  1065. if strings.Contains(corners, "4") == false {
  1066. f.outf("%.5f %.5f l", x*k, (hp-(y+h))*k)
  1067. } else {
  1068. f.clipArc(xc-myArc, yc+r, xc-r, yc+myArc, xc-r, yc)
  1069. }
  1070. xc = x + r
  1071. yc = y + r
  1072. f.outf("%.5f %.5f l", x*k, (hp-yc)*k)
  1073. if strings.Contains(corners, "1") == false {
  1074. f.outf("%.5f %.5f l", x*k, (hp-y)*k)
  1075. f.outf("%.5f %.5f l", (x+r)*k, (hp-y)*k)
  1076. } else {
  1077. f.clipArc(xc-r, yc-myArc, xc-myArc, yc-r, xc, yc-r)
  1078. }
  1079. f.out(fillDrawOp(stylestr))
  1080. }
  1081. // Circle draws a circle centered on point (x, y) with radius r.
  1082. //
  1083. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1084. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1085. // the current draw color and line width centered on the circle's perimeter.
  1086. // Filling uses the current fill color.
  1087. func (f *Fpdf) Circle(x, y, r float64, styleStr string) {
  1088. f.Ellipse(x, y, r, r, 0, styleStr)
  1089. }
  1090. // Ellipse draws an ellipse centered at point (x, y). rx and ry specify its
  1091. // horizontal and vertical radii.
  1092. //
  1093. // degRotate specifies the counter-clockwise angle in degrees that the ellipse
  1094. // will be rotated.
  1095. //
  1096. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1097. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1098. // the current draw color and line width centered on the ellipse's perimeter.
  1099. // Filling uses the current fill color.
  1100. //
  1101. // The Circle() example demonstrates this method.
  1102. func (f *Fpdf) Ellipse(x, y, rx, ry, degRotate float64, styleStr string) {
  1103. f.arc(x, y, rx, ry, degRotate, 0, 360, styleStr, false)
  1104. }
  1105. // Polygon draws a closed figure defined by a series of vertices specified by
  1106. // points. The x and y fields of the points use the units established in New().
  1107. // The last point in the slice will be implicitly joined to the first to close
  1108. // the polygon.
  1109. //
  1110. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1111. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1112. // the current draw color and line width centered on the ellipse's perimeter.
  1113. // Filling uses the current fill color.
  1114. func (f *Fpdf) Polygon(points []PointType, styleStr string) {
  1115. if len(points) > 2 {
  1116. for j, pt := range points {
  1117. if j == 0 {
  1118. f.point(pt.X, pt.Y)
  1119. } else {
  1120. f.outf("%.5f %.5f l ", pt.X*f.k, (f.h-pt.Y)*f.k)
  1121. }
  1122. }
  1123. f.outf("%.5f %.5f l ", points[0].X*f.k, (f.h-points[0].Y)*f.k)
  1124. f.DrawPath(styleStr)
  1125. }
  1126. }
  1127. // Beziergon draws a closed figure defined by a series of cubic Bézier curve
  1128. // segments. The first point in the slice defines the starting point of the
  1129. // figure. Each three following points p1, p2, p3 represent a curve segment to
  1130. // the point p3 using p1 and p2 as the Bézier control points.
  1131. //
  1132. // The x and y fields of the points use the units established in New().
  1133. //
  1134. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1135. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1136. // the current draw color and line width centered on the ellipse's perimeter.
  1137. // Filling uses the current fill color.
  1138. func (f *Fpdf) Beziergon(points []PointType, styleStr string) {
  1139. // Thanks, Robert Lillack, for contributing this function.
  1140. if len(points) < 4 {
  1141. return
  1142. }
  1143. f.point(points[0].XY())
  1144. points = points[1:]
  1145. for len(points) >= 3 {
  1146. cx0, cy0 := points[0].XY()
  1147. cx1, cy1 := points[1].XY()
  1148. x1, y1 := points[2].XY()
  1149. f.curve(cx0, cy0, cx1, cy1, x1, y1)
  1150. points = points[3:]
  1151. }
  1152. f.DrawPath(styleStr)
  1153. }
  1154. // point outputs current point
  1155. func (f *Fpdf) point(x, y float64) {
  1156. f.outf("%.2f %.2f m", x*f.k, (f.h-y)*f.k)
  1157. }
  1158. // curve outputs a single cubic Bézier curve segment from current point
  1159. func (f *Fpdf) curve(cx0, cy0, cx1, cy1, x, y float64) {
  1160. // Thanks, Robert Lillack, for straightening this out
  1161. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c", cx0*f.k, (f.h-cy0)*f.k, cx1*f.k,
  1162. (f.h-cy1)*f.k, x*f.k, (f.h-y)*f.k)
  1163. }
  1164. // Curve draws a single-segment quadratic Bézier curve. The curve starts at
  1165. // the point (x0, y0) and ends at the point (x1, y1). The control point (cx,
  1166. // cy) specifies the curvature. At the start point, the curve is tangent to the
  1167. // straight line between the start point and the control point. At the end
  1168. // point, the curve is tangent to the straight line between the end point and
  1169. // the control point.
  1170. //
  1171. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1172. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1173. // the current draw color, line width, and cap style centered on the curve's
  1174. // path. Filling uses the current fill color.
  1175. //
  1176. // The Circle() example demonstrates this method.
  1177. func (f *Fpdf) Curve(x0, y0, cx, cy, x1, y1 float64, styleStr string) {
  1178. f.point(x0, y0)
  1179. f.outf("%.5f %.5f %.5f %.5f v %s", cx*f.k, (f.h-cy)*f.k, x1*f.k, (f.h-y1)*f.k,
  1180. fillDrawOp(styleStr))
  1181. }
  1182. // CurveCubic draws a single-segment cubic Bézier curve. This routine performs
  1183. // the same function as CurveBezierCubic() but has a nonstandard argument order.
  1184. // It is retained to preserve backward compatibility.
  1185. func (f *Fpdf) CurveCubic(x0, y0, cx0, cy0, x1, y1, cx1, cy1 float64, styleStr string) {
  1186. // f.point(x0, y0)
  1187. // f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
  1188. // cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
  1189. f.CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1, styleStr)
  1190. }
  1191. // CurveBezierCubic draws a single-segment cubic Bézier curve. The curve starts at
  1192. // the point (x0, y0) and ends at the point (x1, y1). The control points (cx0,
  1193. // cy0) and (cx1, cy1) specify the curvature. At the start point, the curve is
  1194. // tangent to the straight line between the start point and the control point
  1195. // (cx0, cy0). At the end point, the curve is tangent to the straight line
  1196. // between the end point and the control point (cx1, cy1).
  1197. //
  1198. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1199. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1200. // the current draw color, line width, and cap style centered on the curve's
  1201. // path. Filling uses the current fill color.
  1202. //
  1203. // This routine performs the same function as CurveCubic() but uses standard
  1204. // argument order.
  1205. //
  1206. // The Circle() example demonstrates this method.
  1207. func (f *Fpdf) CurveBezierCubic(x0, y0, cx0, cy0, cx1, cy1, x1, y1 float64, styleStr string) {
  1208. f.point(x0, y0)
  1209. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c %s", cx0*f.k, (f.h-cy0)*f.k,
  1210. cx1*f.k, (f.h-cy1)*f.k, x1*f.k, (f.h-y1)*f.k, fillDrawOp(styleStr))
  1211. }
  1212. // Arc draws an elliptical arc centered at point (x, y). rx and ry specify its
  1213. // horizontal and vertical radii.
  1214. //
  1215. // degRotate specifies the angle that the arc will be rotated. degStart and
  1216. // degEnd specify the starting and ending angle of the arc. All angles are
  1217. // specified in degrees and measured counter-clockwise from the 3 o'clock
  1218. // position.
  1219. //
  1220. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  1221. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  1222. // the current draw color, line width, and cap style centered on the arc's
  1223. // path. Filling uses the current fill color.
  1224. //
  1225. // The Circle() example demonstrates this method.
  1226. func (f *Fpdf) Arc(x, y, rx, ry, degRotate, degStart, degEnd float64, styleStr string) {
  1227. f.arc(x, y, rx, ry, degRotate, degStart, degEnd, styleStr, false)
  1228. }
  1229. // GetAlpha returns the alpha blending channel, which consists of the
  1230. // alpha transparency value and the blend mode. See SetAlpha for more
  1231. // details.
  1232. func (f *Fpdf) GetAlpha() (alpha float64, blendModeStr string) {
  1233. return f.alpha, f.blendMode
  1234. }
  1235. // SetAlpha sets the alpha blending channel. The blending effect applies to
  1236. // text, drawings and images.
  1237. //
  1238. // alpha must be a value between 0.0 (fully transparent) to 1.0 (fully opaque).
  1239. // Values outside of this range result in an error.
  1240. //
  1241. // blendModeStr must be one of "Normal", "Multiply", "Screen", "Overlay",
  1242. // "Darken", "Lighten", "ColorDodge", "ColorBurn","HardLight", "SoftLight",
  1243. // "Difference", "Exclusion", "Hue", "Saturation", "Color", or "Luminosity". An
  1244. // empty string is replaced with "Normal".
  1245. //
  1246. // To reset normal rendering after applying a blending mode, call this method
  1247. // with alpha set to 1.0 and blendModeStr set to "Normal".
  1248. func (f *Fpdf) SetAlpha(alpha float64, blendModeStr string) {
  1249. if f.err != nil {
  1250. return
  1251. }
  1252. var bl blendModeType
  1253. switch blendModeStr {
  1254. case "Normal", "Multiply", "Screen", "Overlay",
  1255. "Darken", "Lighten", "ColorDodge", "ColorBurn", "HardLight", "SoftLight",
  1256. "Difference", "Exclusion", "Hue", "Saturation", "Color", "Luminosity":
  1257. bl.modeStr = blendModeStr
  1258. case "":
  1259. bl.modeStr = "Normal"
  1260. default:
  1261. f.err = fmt.Errorf("unrecognized blend mode \"%s\"", blendModeStr)
  1262. return
  1263. }
  1264. if alpha < 0.0 || alpha > 1.0 {
  1265. f.err = fmt.Errorf("alpha value (0.0 - 1.0) is out of range: %.3f", alpha)
  1266. return
  1267. }
  1268. f.alpha = alpha
  1269. f.blendMode = blendModeStr
  1270. alphaStr := sprintf("%.3f", alpha)
  1271. keyStr := sprintf("%s %s", alphaStr, blendModeStr)
  1272. pos, ok := f.blendMap[keyStr]
  1273. if !ok {
  1274. pos = len(f.blendList) // at least 1
  1275. f.blendList = append(f.blendList, blendModeType{alphaStr, alphaStr, blendModeStr, 0})
  1276. f.blendMap[keyStr] = pos
  1277. }
  1278. f.outf("/GS%d gs", pos)
  1279. }
  1280. func (f *Fpdf) gradientClipStart(x, y, w, h float64) {
  1281. // Save current graphic state and set clipping area
  1282. f.outf("q %.2f %.2f %.2f %.2f re W n", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k)
  1283. // Set up transformation matrix for gradient
  1284. f.outf("%.5f 0 0 %.5f %.5f %.5f cm", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k)
  1285. }
  1286. func (f *Fpdf) gradientClipEnd() {
  1287. // Restore previous graphic state
  1288. f.out("Q")
  1289. }
  1290. func (f *Fpdf) gradient(tp, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) {
  1291. pos := len(f.gradientList)
  1292. clr1 := rgbColorValue(r1, g1, b1, "", "")
  1293. clr2 := rgbColorValue(r2, g2, b2, "", "")
  1294. f.gradientList = append(f.gradientList, gradientType{tp, clr1.str, clr2.str,
  1295. x1, y1, x2, y2, r, 0})
  1296. f.outf("/Sh%d sh", pos)
  1297. }
  1298. // LinearGradient draws a rectangular area with a blending of one color to
  1299. // another. The rectangle is of width w and height h. Its upper left corner is
  1300. // positioned at point (x, y).
  1301. //
  1302. // Each color is specified with three component values, one each for red, green
  1303. // and blue. The values range from 0 to 255. The first color is specified by
  1304. // (r1, g1, b1) and the second color by (r2, g2, b2).
  1305. //
  1306. // The blending is controlled with a gradient vector that uses normalized
  1307. // coordinates in which the lower left corner is position (0, 0) and the upper
  1308. // right corner is (1, 1). The vector's origin and destination are specified by
  1309. // the points (x1, y1) and (x2, y2). In a linear gradient, blending occurs
  1310. // perpendicularly to the vector. The vector does not necessarily need to be
  1311. // anchored on the rectangle edge. Color 1 is used up to the origin of the
  1312. // vector and color 2 is used beyond the vector's end point. Between the points
  1313. // the colors are gradually blended.
  1314. func (f *Fpdf) LinearGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2 float64) {
  1315. f.gradientClipStart(x, y, w, h)
  1316. f.gradient(2, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, 0)
  1317. f.gradientClipEnd()
  1318. }
  1319. // RadialGradient draws a rectangular area with a blending of one color to
  1320. // another. The rectangle is of width w and height h. Its upper left corner is
  1321. // positioned at point (x, y).
  1322. //
  1323. // Each color is specified with three component values, one each for red, green
  1324. // and blue. The values range from 0 to 255. The first color is specified by
  1325. // (r1, g1, b1) and the second color by (r2, g2, b2).
  1326. //
  1327. // The blending is controlled with a point and a circle, both specified with
  1328. // normalized coordinates in which the lower left corner of the rendered
  1329. // rectangle is position (0, 0) and the upper right corner is (1, 1). Color 1
  1330. // begins at the origin point specified by (x1, y1). Color 2 begins at the
  1331. // circle specified by the center point (x2, y2) and radius r. Colors are
  1332. // gradually blended from the origin to the circle. The origin and the circle's
  1333. // center do not necessarily have to coincide, but the origin must be within
  1334. // the circle to avoid rendering problems.
  1335. //
  1336. // The LinearGradient() example demonstrates this method.
  1337. func (f *Fpdf) RadialGradient(x, y, w, h float64, r1, g1, b1, r2, g2, b2 int, x1, y1, x2, y2, r float64) {
  1338. f.gradientClipStart(x, y, w, h)
  1339. f.gradient(3, r1, g1, b1, r2, g2, b2, x1, y1, x2, y2, r)
  1340. f.gradientClipEnd()
  1341. }
  1342. // ClipRect begins a rectangular clipping operation. The rectangle is of width
  1343. // w and height h. Its upper left corner is positioned at point (x, y). outline
  1344. // is true to draw a border with the current draw color and line width centered
  1345. // on the rectangle's perimeter. Only the outer half of the border will be
  1346. // shown. After calling this method, all rendering operations (for example,
  1347. // Image(), LinearGradient(), etc) will be clipped by the specified rectangle.
  1348. // Call ClipEnd() to restore unclipped operations.
  1349. //
  1350. // This ClipText() example demonstrates this method.
  1351. func (f *Fpdf) ClipRect(x, y, w, h float64, outline bool) {
  1352. f.clipNest++
  1353. f.outf("q %.2f %.2f %.2f %.2f re W %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, strIf(outline, "S", "n"))
  1354. }
  1355. // ClipText begins a clipping operation in which rendering is confined to the
  1356. // character string specified by txtStr. The origin (x, y) is on the left of
  1357. // the first character at the baseline. The current font is used. outline is
  1358. // true to draw a border with the current draw color and line width centered on
  1359. // the perimeters of the text characters. Only the outer half of the border
  1360. // will be shown. After calling this method, all rendering operations (for
  1361. // example, Image(), LinearGradient(), etc) will be clipped. Call ClipEnd() to
  1362. // restore unclipped operations.
  1363. func (f *Fpdf) ClipText(x, y float64, txtStr string, outline bool) {
  1364. f.clipNest++
  1365. f.outf("q BT %.5f %.5f Td %d Tr (%s) Tj ET", x*f.k, (f.h-y)*f.k, intIf(outline, 5, 7), f.escape(txtStr))
  1366. }
  1367. func (f *Fpdf) clipArc(x1, y1, x2, y2, x3, y3 float64) {
  1368. h := f.h
  1369. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c ", x1*f.k, (h-y1)*f.k,
  1370. x2*f.k, (h-y2)*f.k, x3*f.k, (h-y3)*f.k)
  1371. }
  1372. // ClipRoundedRect begins a rectangular clipping operation. The rectangle is of
  1373. // width w and height h. Its upper left corner is positioned at point (x, y).
  1374. // The rounded corners of the rectangle are specified by radius r. outline is
  1375. // true to draw a border with the current draw color and line width centered on
  1376. // the rectangle's perimeter. Only the outer half of the border will be shown.
  1377. // After calling this method, all rendering operations (for example, Image(),
  1378. // LinearGradient(), etc) will be clipped by the specified rectangle. Call
  1379. // ClipEnd() to restore unclipped operations.
  1380. //
  1381. // This ClipText() example demonstrates this method.
  1382. func (f *Fpdf) ClipRoundedRect(x, y, w, h, r float64, outline bool) {
  1383. f.clipNest++
  1384. k := f.k
  1385. hp := f.h
  1386. myArc := (4.0 / 3.0) * (math.Sqrt2 - 1.0)
  1387. f.outf("q %.5f %.5f m", (x+r)*k, (hp-y)*k)
  1388. xc := x + w - r
  1389. yc := y + r
  1390. f.outf("%.5f %.5f l", xc*k, (hp-y)*k)
  1391. f.clipArc(xc+r*myArc, yc-r, xc+r, yc-r*myArc, xc+r, yc)
  1392. xc = x + w - r
  1393. yc = y + h - r
  1394. f.outf("%.5f %.5f l", (x+w)*k, (hp-yc)*k)
  1395. f.clipArc(xc+r, yc+r*myArc, xc+r*myArc, yc+r, xc, yc+r)
  1396. xc = x + r
  1397. yc = y + h - r
  1398. f.outf("%.5f %.5f l", xc*k, (hp-(y+h))*k)
  1399. f.clipArc(xc-r*myArc, yc+r, xc-r, yc+r*myArc, xc-r, yc)
  1400. xc = x + r
  1401. yc = y + r
  1402. f.outf("%.5f %.5f l", x*k, (hp-yc)*k)
  1403. f.clipArc(xc-r, yc-r*myArc, xc-r*myArc, yc-r, xc, yc-r)
  1404. f.outf(" W %s", strIf(outline, "S", "n"))
  1405. }
  1406. // ClipEllipse begins an elliptical clipping operation. The ellipse is centered
  1407. // at (x, y). Its horizontal and vertical radii are specified by rx and ry.
  1408. // outline is true to draw a border with the current draw color and line width
  1409. // centered on the ellipse's perimeter. Only the outer half of the border will
  1410. // be shown. After calling this method, all rendering operations (for example,
  1411. // Image(), LinearGradient(), etc) will be clipped by the specified ellipse.
  1412. // Call ClipEnd() to restore unclipped operations.
  1413. //
  1414. // This ClipText() example demonstrates this method.
  1415. func (f *Fpdf) ClipEllipse(x, y, rx, ry float64, outline bool) {
  1416. f.clipNest++
  1417. lx := (4.0 / 3.0) * rx * (math.Sqrt2 - 1)
  1418. ly := (4.0 / 3.0) * ry * (math.Sqrt2 - 1)
  1419. k := f.k
  1420. h := f.h
  1421. f.outf("q %.5f %.5f m %.5f %.5f %.5f %.5f %.5f %.5f c",
  1422. (x+rx)*k, (h-y)*k,
  1423. (x+rx)*k, (h-(y-ly))*k,
  1424. (x+lx)*k, (h-(y-ry))*k,
  1425. x*k, (h-(y-ry))*k)
  1426. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c",
  1427. (x-lx)*k, (h-(y-ry))*k,
  1428. (x-rx)*k, (h-(y-ly))*k,
  1429. (x-rx)*k, (h-y)*k)
  1430. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c",
  1431. (x-rx)*k, (h-(y+ly))*k,
  1432. (x-lx)*k, (h-(y+ry))*k,
  1433. x*k, (h-(y+ry))*k)
  1434. f.outf("%.5f %.5f %.5f %.5f %.5f %.5f c W %s",
  1435. (x+lx)*k, (h-(y+ry))*k,
  1436. (x+rx)*k, (h-(y+ly))*k,
  1437. (x+rx)*k, (h-y)*k,
  1438. strIf(outline, "S", "n"))
  1439. }
  1440. // ClipCircle begins a circular clipping operation. The circle is centered at
  1441. // (x, y) and has radius r. outline is true to draw a border with the current
  1442. // draw color and line width centered on the circle's perimeter. Only the outer
  1443. // half of the border will be shown. After calling this method, all rendering
  1444. // operations (for example, Image(), LinearGradient(), etc) will be clipped by
  1445. // the specified circle. Call ClipEnd() to restore unclipped operations.
  1446. //
  1447. // The ClipText() example demonstrates this method.
  1448. func (f *Fpdf) ClipCircle(x, y, r float64, outline bool) {
  1449. f.ClipEllipse(x, y, r, r, outline)
  1450. }
  1451. // ClipPolygon begins a clipping operation within a polygon. The figure is
  1452. // defined by a series of vertices specified by points. The x and y fields of
  1453. // the points use the units established in New(). The last point in the slice
  1454. // will be implicitly joined to the first to close the polygon. outline is true
  1455. // to draw a border with the current draw color and line width centered on the
  1456. // polygon's perimeter. Only the outer half of the border will be shown. After
  1457. // calling this method, all rendering operations (for example, Image(),
  1458. // LinearGradient(), etc) will be clipped by the specified polygon. Call
  1459. // ClipEnd() to restore unclipped operations.
  1460. //
  1461. // The ClipText() example demonstrates this method.
  1462. func (f *Fpdf) ClipPolygon(points []PointType, outline bool) {
  1463. f.clipNest++
  1464. var s fmtBuffer
  1465. h := f.h
  1466. k := f.k
  1467. s.printf("q ")
  1468. for j, pt := range points {
  1469. s.printf("%.5f %.5f %s ", pt.X*k, (h-pt.Y)*k, strIf(j == 0, "m", "l"))
  1470. }
  1471. s.printf("h W %s", strIf(outline, "S", "n"))
  1472. f.out(s.String())
  1473. }
  1474. // ClipEnd ends a clipping operation that was started with a call to
  1475. // ClipRect(), ClipRoundedRect(), ClipText(), ClipEllipse(), ClipCircle() or
  1476. // ClipPolygon(). Clipping operations can be nested. The document cannot be
  1477. // successfully output while a clipping operation is active.
  1478. //
  1479. // The ClipText() example demonstrates this method.
  1480. func (f *Fpdf) ClipEnd() {
  1481. if f.err == nil {
  1482. if f.clipNest > 0 {
  1483. f.clipNest--
  1484. f.out("Q")
  1485. } else {
  1486. f.err = fmt.Errorf("error attempting to end clip operation out of sequence")
  1487. }
  1488. }
  1489. }
  1490. // AddFont imports a TrueType, OpenType or Type1 font and makes it available.
  1491. // It is necessary to generate a font definition file first with the makefont
  1492. // utility. It is not necessary to call this function for the core PDF fonts
  1493. // (courier, helvetica, times, zapfdingbats).
  1494. //
  1495. // The JSON definition file (and the font file itself when embedding) must be
  1496. // present in the font directory. If it is not found, the error "Could not
  1497. // include font definition file" is set.
  1498. //
  1499. // family specifies the font family. The name can be chosen arbitrarily. If it
  1500. // is a standard family name, it will override the corresponding font. This
  1501. // string is used to subsequently set the font with the SetFont method.
  1502. //
  1503. // style specifies the font style. Acceptable values are (case insensitive) the
  1504. // empty string for regular style, "B" for bold, "I" for italic, or "BI" or
  1505. // "IB" for bold and italic combined.
  1506. //
  1507. // fileStr specifies the base name with ".json" extension of the font
  1508. // definition file to be added. The file will be loaded from the font directory
  1509. // specified in the call to New() or SetFontLocation().
  1510. func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) {
  1511. f.addFont(familyStr, styleStr, fileStr, false)
  1512. }
  1513. // AddUTF8Font imports a TrueType font with utf-8 symbols and makes it available.
  1514. // It is necessary to generate a font definition file first with the makefont
  1515. // utility. It is not necessary to call this function for the core PDF fonts
  1516. // (courier, helvetica, times, zapfdingbats).
  1517. //
  1518. // The JSON definition file (and the font file itself when embedding) must be
  1519. // present in the font directory. If it is not found, the error "Could not
  1520. // include font definition file" is set.
  1521. //
  1522. // family specifies the font family. The name can be chosen arbitrarily. If it
  1523. // is a standard family name, it will override the corresponding font. This
  1524. // string is used to subsequently set the font with the SetFont method.
  1525. //
  1526. // style specifies the font style. Acceptable values are (case insensitive) the
  1527. // empty string for regular style, "B" for bold, "I" for italic, or "BI" or
  1528. // "IB" for bold and italic combined.
  1529. //
  1530. // fileStr specifies the base name with ".json" extension of the font
  1531. // definition file to be added. The file will be loaded from the font directory
  1532. // specified in the call to New() or SetFontLocation().
  1533. func (f *Fpdf) AddUTF8Font(familyStr, styleStr, fileStr string) {
  1534. f.addFont(familyStr, styleStr, fileStr, true)
  1535. }
  1536. func (f *Fpdf) addFont(familyStr, styleStr, fileStr string, isUTF8 bool) {
  1537. if fileStr == "" {
  1538. if isUTF8 {
  1539. fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".ttf"
  1540. } else {
  1541. fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json"
  1542. }
  1543. }
  1544. if isUTF8 {
  1545. fontKey := getFontKey(familyStr, styleStr)
  1546. _, ok := f.fonts[fontKey]
  1547. if ok {
  1548. return
  1549. }
  1550. var ttfStat os.FileInfo
  1551. var err error
  1552. fileStr = path.Join(f.fontpath, fileStr)
  1553. ttfStat, err = os.Stat(fileStr)
  1554. if err != nil {
  1555. f.SetError(err)
  1556. return
  1557. }
  1558. originalSize := ttfStat.Size()
  1559. Type := "UTF8"
  1560. var utf8Bytes []byte
  1561. utf8Bytes, err = ioutil.ReadFile(fileStr)
  1562. if err != nil {
  1563. f.SetError(err)
  1564. return
  1565. }
  1566. reader := fileReader{readerPosition: 0, array: utf8Bytes}
  1567. utf8File := newUTF8Font(&reader)
  1568. err = utf8File.parseFile()
  1569. if err != nil {
  1570. f.SetError(err)
  1571. return
  1572. }
  1573. desc := FontDescType{
  1574. Ascent: int(utf8File.Ascent),
  1575. Descent: int(utf8File.Descent),
  1576. CapHeight: utf8File.CapHeight,
  1577. Flags: utf8File.Flags,
  1578. FontBBox: utf8File.Bbox,
  1579. ItalicAngle: utf8File.ItalicAngle,
  1580. StemV: utf8File.StemV,
  1581. MissingWidth: round(utf8File.DefaultWidth),
  1582. }
  1583. var sbarr map[int]int
  1584. if f.aliasNbPagesStr == "" {
  1585. sbarr = makeSubsetRange(57)
  1586. } else {
  1587. sbarr = makeSubsetRange(32)
  1588. }
  1589. def := fontDefType{
  1590. Tp: Type,
  1591. Name: fontKey,
  1592. Desc: desc,
  1593. Up: int(round(utf8File.UnderlinePosition)),
  1594. Ut: round(utf8File.UnderlineThickness),
  1595. Cw: utf8File.CharWidths,
  1596. usedRunes: sbarr,
  1597. File: fileStr,
  1598. utf8File: utf8File,
  1599. }
  1600. def.i, _ = generateFontID(def)
  1601. f.fonts[fontKey] = def
  1602. f.fontFiles[fontKey] = fontFileType{
  1603. length1: originalSize,
  1604. fontType: "UTF8",
  1605. }
  1606. f.fontFiles[fileStr] = fontFileType{
  1607. fontType: "UTF8",
  1608. }
  1609. } else {
  1610. if f.fontLoader != nil {
  1611. reader, err := f.fontLoader.Open(fileStr)
  1612. if err == nil {
  1613. f.AddFontFromReader(familyStr, styleStr, reader)
  1614. if closer, ok := reader.(io.Closer); ok {
  1615. closer.Close()
  1616. }
  1617. return
  1618. }
  1619. }
  1620. fileStr = path.Join(f.fontpath, fileStr)
  1621. file, err := os.Open(fileStr)
  1622. if err != nil {
  1623. f.err = err
  1624. return
  1625. }
  1626. defer file.Close()
  1627. f.AddFontFromReader(familyStr, styleStr, file)
  1628. }
  1629. }
  1630. func makeSubsetRange(end int) map[int]int {
  1631. answer := make(map[int]int)
  1632. for i := 0; i < end; i++ {
  1633. answer[i] = 0
  1634. }
  1635. return answer
  1636. }
  1637. // AddFontFromBytes imports a TrueType, OpenType or Type1 font from static
  1638. // bytes within the executable and makes it available for use in the generated
  1639. // document.
  1640. //
  1641. // family specifies the font family. The name can be chosen arbitrarily. If it
  1642. // is a standard family name, it will override the corresponding font. This
  1643. // string is used to subsequently set the font with the SetFont method.
  1644. //
  1645. // style specifies the font style. Acceptable values are (case insensitive) the
  1646. // empty string for regular style, "B" for bold, "I" for italic, or "BI" or
  1647. // "IB" for bold and italic combined.
  1648. //
  1649. // jsonFileBytes contain all bytes of JSON file.
  1650. //
  1651. // zFileBytes contain all bytes of Z file.
  1652. func (f *Fpdf) AddFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes []byte) {
  1653. f.addFontFromBytes(familyStr, styleStr, jsonFileBytes, zFileBytes, nil)
  1654. }
  1655. // AddUTF8FontFromBytes imports a TrueType font with utf-8 symbols from static
  1656. // bytes within the executable and makes it available for use in the generated
  1657. // document.
  1658. //
  1659. // family specifies the font family. The name can be chosen arbitrarily. If it
  1660. // is a standard family name, it will override the corresponding font. This
  1661. // string is used to subsequently set the font with the SetFont method.
  1662. //
  1663. // style specifies the font style. Acceptable values are (case insensitive) the
  1664. // empty string for regular style, "B" for bold, "I" for italic, or "BI" or
  1665. // "IB" for bold and italic combined.
  1666. //
  1667. // jsonFileBytes contain all bytes of JSON file.
  1668. //
  1669. // zFileBytes contain all bytes of Z file.
  1670. func (f *Fpdf) AddUTF8FontFromBytes(familyStr, styleStr string, utf8Bytes []byte) {
  1671. f.addFontFromBytes(familyStr, styleStr, nil, nil, utf8Bytes)
  1672. }
  1673. func (f *Fpdf) addFontFromBytes(familyStr, styleStr string, jsonFileBytes, zFileBytes, utf8Bytes []byte) {
  1674. if f.err != nil {
  1675. return
  1676. }
  1677. // load font key
  1678. var ok bool
  1679. fontkey := getFontKey(familyStr, styleStr)
  1680. _, ok = f.fonts[fontkey]
  1681. if ok {
  1682. return
  1683. }
  1684. if utf8Bytes != nil {
  1685. // if styleStr == "IB" {
  1686. // styleStr = "BI"
  1687. // }
  1688. Type := "UTF8"
  1689. reader := fileReader{readerPosition: 0, array: utf8Bytes}
  1690. utf8File := newUTF8Font(&reader)
  1691. err := utf8File.parseFile()
  1692. if err != nil {
  1693. fmt.Printf("get metrics Error: %e\n", err)
  1694. return
  1695. }
  1696. desc := FontDescType{
  1697. Ascent: int(utf8File.Ascent),
  1698. Descent: int(utf8File.Descent),
  1699. CapHeight: utf8File.CapHeight,
  1700. Flags: utf8File.Flags,
  1701. FontBBox: utf8File.Bbox,
  1702. ItalicAngle: utf8File.ItalicAngle,
  1703. StemV: utf8File.StemV,
  1704. MissingWidth: round(utf8File.DefaultWidth),
  1705. }
  1706. var sbarr map[int]int
  1707. if f.aliasNbPagesStr == "" {
  1708. sbarr = makeSubsetRange(57)
  1709. } else {
  1710. sbarr = makeSubsetRange(32)
  1711. }
  1712. def := fontDefType{
  1713. Tp: Type,
  1714. Name: fontkey,
  1715. Desc: desc,
  1716. Up: int(round(utf8File.UnderlinePosition)),
  1717. Ut: round(utf8File.UnderlineThickness),
  1718. Cw: utf8File.CharWidths,
  1719. utf8File: utf8File,
  1720. usedRunes: sbarr,
  1721. }
  1722. def.i, _ = generateFontID(def)
  1723. f.fonts[fontkey] = def
  1724. } else {
  1725. // load font definitions
  1726. var info fontDefType
  1727. err := json.Unmarshal(jsonFileBytes, &info)
  1728. if err != nil {
  1729. f.err = err
  1730. }
  1731. if f.err != nil {
  1732. return
  1733. }
  1734. if info.i, err = generateFontID(info); err != nil {
  1735. f.err = err
  1736. return
  1737. }
  1738. // search existing encodings
  1739. if len(info.Diff) > 0 {
  1740. n := -1
  1741. for j, str := range f.diffs {
  1742. if str == info.Diff {
  1743. n = j + 1
  1744. break
  1745. }
  1746. }
  1747. if n < 0 {
  1748. f.diffs = append(f.diffs, info.Diff)
  1749. n = len(f.diffs)
  1750. }
  1751. info.DiffN = n
  1752. }
  1753. // embed font
  1754. if len(info.File) > 0 {
  1755. if info.Tp == "TrueType" {
  1756. f.fontFiles[info.File] = fontFileType{
  1757. length1: int64(info.OriginalSize),
  1758. embedded: true,
  1759. content: zFileBytes,
  1760. }
  1761. } else {
  1762. f.fontFiles[info.File] = fontFileType{
  1763. length1: int64(info.Size1),
  1764. length2: int64(info.Size2),
  1765. embedded: true,
  1766. content: zFileBytes,
  1767. }
  1768. }
  1769. }
  1770. f.fonts[fontkey] = info
  1771. }
  1772. }
  1773. // getFontKey is used by AddFontFromReader and GetFontDesc
  1774. func getFontKey(familyStr, styleStr string) string {
  1775. familyStr = strings.ToLower(familyStr)
  1776. styleStr = strings.ToUpper(styleStr)
  1777. if styleStr == "IB" {
  1778. styleStr = "BI"
  1779. }
  1780. return familyStr + styleStr
  1781. }
  1782. // AddFontFromReader imports a TrueType, OpenType or Type1 font and makes it
  1783. // available using a reader that satisifies the io.Reader interface. See
  1784. // AddFont for details about familyStr and styleStr.
  1785. func (f *Fpdf) AddFontFromReader(familyStr, styleStr string, r io.Reader) {
  1786. if f.err != nil {
  1787. return
  1788. }
  1789. // dbg("Adding family [%s], style [%s]", familyStr, styleStr)
  1790. var ok bool
  1791. fontkey := getFontKey(familyStr, styleStr)
  1792. _, ok = f.fonts[fontkey]
  1793. if ok {
  1794. return
  1795. }
  1796. var info fontDefType
  1797. info = f.loadfont(r)
  1798. if f.err != nil {
  1799. return
  1800. }
  1801. if len(info.Diff) > 0 {
  1802. // Search existing encodings
  1803. n := -1
  1804. for j, str := range f.diffs {
  1805. if str == info.Diff {
  1806. n = j + 1
  1807. break
  1808. }
  1809. }
  1810. if n < 0 {
  1811. f.diffs = append(f.diffs, info.Diff)
  1812. n = len(f.diffs)
  1813. }
  1814. info.DiffN = n
  1815. }
  1816. // dbg("font [%s], type [%s]", info.File, info.Tp)
  1817. if len(info.File) > 0 {
  1818. // Embedded font
  1819. if info.Tp == "TrueType" {
  1820. f.fontFiles[info.File] = fontFileType{length1: int64(info.OriginalSize)}
  1821. } else {
  1822. f.fontFiles[info.File] = fontFileType{length1: int64(info.Size1), length2: int64(info.Size2)}
  1823. }
  1824. }
  1825. f.fonts[fontkey] = info
  1826. return
  1827. }
  1828. // GetFontDesc returns the font descriptor, which can be used for
  1829. // example to find the baseline of a font. If familyStr is empty
  1830. // current font descriptor will be returned.
  1831. // See FontDescType for documentation about the font descriptor.
  1832. // See AddFont for details about familyStr and styleStr.
  1833. func (f *Fpdf) GetFontDesc(familyStr, styleStr string) FontDescType {
  1834. if familyStr == "" {
  1835. return f.currentFont.Desc
  1836. }
  1837. return f.fonts[getFontKey(familyStr, styleStr)].Desc
  1838. }
  1839. // SetFont sets the font used to print character strings. It is mandatory to
  1840. // call this method at least once before printing text or the resulting
  1841. // document will not be valid.
  1842. //
  1843. // The font can be either a standard one or a font added via the AddFont()
  1844. // method or AddFontFromReader() method. Standard fonts use the Windows
  1845. // encoding cp1252 (Western Europe).
  1846. //
  1847. // The method can be called before the first page is created and the font is
  1848. // kept from page to page. If you just wish to change the current font size, it
  1849. // is simpler to call SetFontSize().
  1850. //
  1851. // Note: the font definition file must be accessible. An error is set if the
  1852. // file cannot be read.
  1853. //
  1854. // familyStr specifies the font family. It can be either a name defined by
  1855. // AddFont(), AddFontFromReader() or one of the standard families (case
  1856. // insensitive): "Courier" for fixed-width, "Helvetica" or "Arial" for sans
  1857. // serif, "Times" for serif, "Symbol" or "ZapfDingbats" for symbolic.
  1858. //
  1859. // styleStr can be "B" (bold), "I" (italic), "U" (underscore) or any
  1860. // combination. The default value (specified with an empty string) is regular.
  1861. // Bold and italic styles do not apply to Symbol and ZapfDingbats.
  1862. //
  1863. // size is the font size measured in points. The default value is the current
  1864. // size. If no size has been specified since the beginning of the document, the
  1865. // value taken is 12.
  1866. func (f *Fpdf) SetFont(familyStr, styleStr string, size float64) {
  1867. // dbg("SetFont x %.2f, lMargin %.2f", f.x, f.lMargin)
  1868. if f.err != nil {
  1869. return
  1870. }
  1871. // dbg("SetFont")
  1872. var ok bool
  1873. if familyStr == "" {
  1874. familyStr = f.fontFamily
  1875. } else {
  1876. familyStr = strings.ToLower(familyStr)
  1877. }
  1878. styleStr = strings.ToUpper(styleStr)
  1879. f.underline = strings.Contains(styleStr, "U")
  1880. if f.underline {
  1881. styleStr = strings.Replace(styleStr, "U", "", -1)
  1882. }
  1883. if styleStr == "IB" {
  1884. styleStr = "BI"
  1885. }
  1886. if size == 0.0 {
  1887. size = f.fontSizePt
  1888. }
  1889. // Test if font is already loaded
  1890. fontKey := familyStr + styleStr
  1891. _, ok = f.fonts[fontKey]
  1892. if !ok {
  1893. // Test if one of the core fonts
  1894. if familyStr == "arial" {
  1895. familyStr = "helvetica"
  1896. }
  1897. _, ok = f.coreFonts[familyStr]
  1898. if ok {
  1899. if familyStr == "symbol" {
  1900. familyStr = "zapfdingbats"
  1901. }
  1902. if familyStr == "zapfdingbats" {
  1903. styleStr = ""
  1904. }
  1905. fontKey = familyStr + styleStr
  1906. _, ok = f.fonts[fontKey]
  1907. if !ok {
  1908. rdr := f.coreFontReader(familyStr, styleStr)
  1909. if f.err == nil {
  1910. f.AddFontFromReader(familyStr, styleStr, rdr)
  1911. }
  1912. if f.err != nil {
  1913. return
  1914. }
  1915. }
  1916. } else {
  1917. f.err = fmt.Errorf("undefined font: %s %s", familyStr, styleStr)
  1918. return
  1919. }
  1920. }
  1921. // Select it
  1922. f.fontFamily = familyStr
  1923. f.fontStyle = styleStr
  1924. f.fontSizePt = size
  1925. f.fontSize = size / f.k
  1926. f.currentFont = f.fonts[fontKey]
  1927. if f.currentFont.Tp == "UTF8" {
  1928. f.isCurrentUTF8 = true
  1929. } else {
  1930. f.isCurrentUTF8 = false
  1931. }
  1932. if f.page > 0 {
  1933. f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
  1934. }
  1935. return
  1936. }
  1937. // SetFontStyle sets the style of the current font. See also SetFont()
  1938. func (f *Fpdf) SetFontStyle(styleStr string) {
  1939. f.SetFont(f.fontFamily, styleStr, f.fontSizePt)
  1940. }
  1941. // SetFontSize defines the size of the current font. Size is specified in
  1942. // points (1/ 72 inch). See also SetFontUnitSize().
  1943. func (f *Fpdf) SetFontSize(size float64) {
  1944. f.fontSizePt = size
  1945. f.fontSize = size / f.k
  1946. if f.page > 0 {
  1947. f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
  1948. }
  1949. }
  1950. // SetFontUnitSize defines the size of the current font. Size is specified in
  1951. // the unit of measure specified in New(). See also SetFontSize().
  1952. func (f *Fpdf) SetFontUnitSize(size float64) {
  1953. f.fontSizePt = size * f.k
  1954. f.fontSize = size
  1955. if f.page > 0 {
  1956. f.outf("BT /F%s %.2f Tf ET", f.currentFont.i, f.fontSizePt)
  1957. }
  1958. }
  1959. // GetFontSize returns the size of the current font in points followed by the
  1960. // size in the unit of measure specified in New(). The second value can be used
  1961. // as a line height value in drawing operations.
  1962. func (f *Fpdf) GetFontSize() (ptSize, unitSize float64) {
  1963. return f.fontSizePt, f.fontSize
  1964. }
  1965. // AddLink creates a new internal link and returns its identifier. An internal
  1966. // link is a clickable area which directs to another place within the document.
  1967. // The identifier can then be passed to Cell(), Write(), Image() or Link(). The
  1968. // destination is defined with SetLink().
  1969. func (f *Fpdf) AddLink() int {
  1970. f.links = append(f.links, intLinkType{})
  1971. return len(f.links) - 1
  1972. }
  1973. // SetLink defines the page and position a link points to. See AddLink().
  1974. func (f *Fpdf) SetLink(link int, y float64, page int) {
  1975. if y == -1 {
  1976. y = f.y
  1977. }
  1978. if page == -1 {
  1979. page = f.page
  1980. }
  1981. f.links[link] = intLinkType{page, y}
  1982. }
  1983. // newLink adds a new clickable link on current page
  1984. func (f *Fpdf) newLink(x, y, w, h float64, link int, linkStr string) {
  1985. // linkList, ok := f.pageLinks[f.page]
  1986. // if !ok {
  1987. // linkList = make([]linkType, 0, 8)
  1988. // f.pageLinks[f.page] = linkList
  1989. // }
  1990. f.pageLinks[f.page] = append(f.pageLinks[f.page],
  1991. linkType{x * f.k, f.hPt - y*f.k, w * f.k, h * f.k, link, linkStr})
  1992. }
  1993. // Link puts a link on a rectangular area of the page. Text or image links are
  1994. // generally put via Cell(), Write() or Image(), but this method can be useful
  1995. // for instance to define a clickable area inside an image. link is the value
  1996. // returned by AddLink().
  1997. func (f *Fpdf) Link(x, y, w, h float64, link int) {
  1998. f.newLink(x, y, w, h, link, "")
  1999. }
  2000. // LinkString puts a link on a rectangular area of the page. Text or image
  2001. // links are generally put via Cell(), Write() or Image(), but this method can
  2002. // be useful for instance to define a clickable area inside an image. linkStr
  2003. // is the target URL.
  2004. func (f *Fpdf) LinkString(x, y, w, h float64, linkStr string) {
  2005. f.newLink(x, y, w, h, 0, linkStr)
  2006. }
  2007. // Bookmark sets a bookmark that will be displayed in a sidebar outline. txtStr
  2008. // is the title of the bookmark. level specifies the level of the bookmark in
  2009. // the outline; 0 is the top level, 1 is just below, and so on. y specifies the
  2010. // vertical position of the bookmark destination in the current page; -1
  2011. // indicates the current position.
  2012. func (f *Fpdf) Bookmark(txtStr string, level int, y float64) {
  2013. if y == -1 {
  2014. y = f.y
  2015. }
  2016. f.outlines = append(f.outlines, outlineType{text: txtStr, level: level, y: y, p: f.PageNo(), prev: -1, last: -1, next: -1, first: -1})
  2017. }
  2018. // Text prints a character string. The origin (x, y) is on the left of the
  2019. // first character at the baseline. This method permits a string to be placed
  2020. // precisely on the page, but it is usually easier to use Cell(), MultiCell()
  2021. // or Write() which are the standard methods to print text.
  2022. func (f *Fpdf) Text(x, y float64, txtStr string) {
  2023. var txt2 string
  2024. if f.isCurrentUTF8 {
  2025. if f.isRTL {
  2026. txtStr = reverseText(txtStr)
  2027. x -= f.GetStringWidth(txtStr)
  2028. }
  2029. txt2 = f.escape(utf8toutf16(txtStr, false))
  2030. for _, uni := range []rune(txtStr) {
  2031. f.currentFont.usedRunes[int(uni)] = int(uni)
  2032. }
  2033. } else {
  2034. txt2 = f.escape(txtStr)
  2035. }
  2036. s := sprintf("BT %.2f %.2f Td (%s) Tj ET", x*f.k, (f.h-y)*f.k, txt2)
  2037. if f.underline && txtStr != "" {
  2038. s += " " + f.dounderline(x, y, txtStr)
  2039. }
  2040. if f.colorFlag {
  2041. s = sprintf("q %s %s Q", f.color.text.str, s)
  2042. }
  2043. f.out(s)
  2044. }
  2045. // SetWordSpacing sets spacing between words of following text. See the
  2046. // WriteAligned() example for a demonstration of its use.
  2047. func (f *Fpdf) SetWordSpacing(space float64) {
  2048. f.out(sprintf("%.5f Tw", space*f.k))
  2049. }
  2050. // SetAcceptPageBreakFunc allows the application to control where page breaks
  2051. // occur.
  2052. //
  2053. // fnc is an application function (typically a closure) that is called by the
  2054. // library whenever a page break condition is met. The break is issued if true
  2055. // is returned. The default implementation returns a value according to the
  2056. // mode selected by SetAutoPageBreak. The function provided should not be
  2057. // called by the application.
  2058. //
  2059. // See the example for SetLeftMargin() to see how this function can be used to
  2060. // manage multiple columns.
  2061. func (f *Fpdf) SetAcceptPageBreakFunc(fnc func() bool) {
  2062. f.acceptPageBreak = fnc
  2063. }
  2064. // CellFormat prints a rectangular cell with optional borders, background color
  2065. // and character string. The upper-left corner of the cell corresponds to the
  2066. // current position. The text can be aligned or centered. After the call, the
  2067. // current position moves to the right or to the next line. It is possible to
  2068. // put a link on the text.
  2069. //
  2070. // An error will be returned if a call to SetFont() has not already taken
  2071. // place before this method is called.
  2072. //
  2073. // If automatic page breaking is enabled and the cell goes beyond the limit, a
  2074. // page break is done before outputting.
  2075. //
  2076. // w and h specify the width and height of the cell. If w is 0, the cell
  2077. // extends up to the right margin. Specifying 0 for h will result in no output,
  2078. // but the current position will be advanced by w.
  2079. //
  2080. // txtStr specifies the text to display.
  2081. //
  2082. // borderStr specifies how the cell border will be drawn. An empty string
  2083. // indicates no border, "1" indicates a full border, and one or more of "L",
  2084. // "T", "R" and "B" indicate the left, top, right and bottom sides of the
  2085. // border.
  2086. //
  2087. // ln indicates where the current position should go after the call. Possible
  2088. // values are 0 (to the right), 1 (to the beginning of the next line), and 2
  2089. // (below). Putting 1 is equivalent to putting 0 and calling Ln() just after.
  2090. //
  2091. // alignStr specifies how the text is to be positioned within the cell.
  2092. // Horizontal alignment is controlled by including "L", "C" or "R" (left,
  2093. // center, right) in alignStr. Vertical alignment is controlled by including
  2094. // "T", "M", "B" or "A" (top, middle, bottom, baseline) in alignStr. The default
  2095. // alignment is left middle.
  2096. //
  2097. // fill is true to paint the cell background or false to leave it transparent.
  2098. //
  2099. // link is the identifier returned by AddLink() or 0 for no internal link.
  2100. //
  2101. // linkStr is a target URL or empty for no external link. A non--zero value for
  2102. // link takes precedence over linkStr.
  2103. func (f *Fpdf) CellFormat(w, h float64, txtStr, borderStr string, ln int,
  2104. alignStr string, fill bool, link int, linkStr string) {
  2105. // dbg("CellFormat. h = %.2f, borderStr = %s", h, borderStr)
  2106. if f.err != nil {
  2107. return
  2108. }
  2109. if f.currentFont.Name == "" {
  2110. f.err = fmt.Errorf("font has not been set; unable to render text")
  2111. return
  2112. }
  2113. borderStr = strings.ToUpper(borderStr)
  2114. k := f.k
  2115. if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
  2116. // Automatic page break
  2117. x := f.x
  2118. ws := f.ws
  2119. // dbg("auto page break, x %.2f, ws %.2f", x, ws)
  2120. if ws > 0 {
  2121. f.ws = 0
  2122. f.out("0 Tw")
  2123. }
  2124. f.AddPageFormat(f.curOrientation, f.curPageSize)
  2125. if f.err != nil {
  2126. return
  2127. }
  2128. f.x = x
  2129. if ws > 0 {
  2130. f.ws = ws
  2131. f.outf("%.3f Tw", ws*k)
  2132. }
  2133. }
  2134. if w == 0 {
  2135. w = f.w - f.rMargin - f.x
  2136. }
  2137. var s fmtBuffer
  2138. if fill || borderStr == "1" {
  2139. var op string
  2140. if fill {
  2141. if borderStr == "1" {
  2142. op = "B"
  2143. // dbg("border is '1', fill")
  2144. } else {
  2145. op = "f"
  2146. // dbg("border is empty, fill")
  2147. }
  2148. } else {
  2149. // dbg("border is '1', no fill")
  2150. op = "S"
  2151. }
  2152. /// dbg("(CellFormat) f.x %.2f f.k %.2f", f.x, f.k)
  2153. s.printf("%.2f %.2f %.2f %.2f re %s ", f.x*k, (f.h-f.y)*k, w*k, -h*k, op)
  2154. }
  2155. if len(borderStr) > 0 && borderStr != "1" {
  2156. // fmt.Printf("border is '%s', no fill\n", borderStr)
  2157. x := f.x
  2158. y := f.y
  2159. left := x * k
  2160. top := (f.h - y) * k
  2161. right := (x + w) * k
  2162. bottom := (f.h - (y + h)) * k
  2163. if strings.Contains(borderStr, "L") {
  2164. s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, left, bottom)
  2165. }
  2166. if strings.Contains(borderStr, "T") {
  2167. s.printf("%.2f %.2f m %.2f %.2f l S ", left, top, right, top)
  2168. }
  2169. if strings.Contains(borderStr, "R") {
  2170. s.printf("%.2f %.2f m %.2f %.2f l S ", right, top, right, bottom)
  2171. }
  2172. if strings.Contains(borderStr, "B") {
  2173. s.printf("%.2f %.2f m %.2f %.2f l S ", left, bottom, right, bottom)
  2174. }
  2175. }
  2176. if len(txtStr) > 0 {
  2177. var dx, dy float64
  2178. // Horizontal alignment
  2179. switch {
  2180. case strings.Contains(alignStr, "R"):
  2181. dx = w - f.cMargin - f.GetStringWidth(txtStr)
  2182. case strings.Contains(alignStr, "C"):
  2183. dx = (w - f.GetStringWidth(txtStr)) / 2
  2184. default:
  2185. dx = f.cMargin
  2186. }
  2187. // Vertical alignment
  2188. switch {
  2189. case strings.Contains(alignStr, "T"):
  2190. dy = (f.fontSize - h) / 2.0
  2191. case strings.Contains(alignStr, "B"):
  2192. dy = (h - f.fontSize) / 2.0
  2193. case strings.Contains(alignStr, "A"):
  2194. var descent float64
  2195. d := f.currentFont.Desc
  2196. if d.Descent == 0 {
  2197. // not defined (standard font?), use average of 19%
  2198. descent = -0.19 * f.fontSize
  2199. } else {
  2200. descent = float64(d.Descent) * f.fontSize / float64(d.Ascent-d.Descent)
  2201. }
  2202. dy = (h-f.fontSize)/2.0 - descent
  2203. default:
  2204. dy = 0
  2205. }
  2206. if f.colorFlag {
  2207. s.printf("q %s ", f.color.text.str)
  2208. }
  2209. //If multibyte, Tw has no effect - do word spacing using an adjustment before each space
  2210. if (f.ws != 0 || alignStr == "J") && f.isCurrentUTF8 { // && f.ws != 0
  2211. if f.isRTL {
  2212. txtStr = reverseText(txtStr)
  2213. }
  2214. wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
  2215. for _, uni := range []rune(txtStr) {
  2216. f.currentFont.usedRunes[int(uni)] = int(uni)
  2217. }
  2218. space := f.escape(utf8toutf16(" ", false))
  2219. strSize := f.GetStringSymbolWidth(txtStr)
  2220. s.printf("BT 0 Tw %.2f %.2f Td [", (f.x+dx)*k, (f.h-(f.y+.5*h+.3*f.fontSize))*k)
  2221. t := strings.Split(txtStr, " ")
  2222. shift := float64((wmax - strSize)) / float64(len(t)-1)
  2223. numt := len(t)
  2224. for i := 0; i < numt; i++ {
  2225. tx := t[i]
  2226. tx = "(" + f.escape(utf8toutf16(tx, false)) + ")"
  2227. s.printf("%s ", tx)
  2228. if (i + 1) < numt {
  2229. s.printf("%.3f(%s) ", -shift, space)
  2230. }
  2231. }
  2232. s.printf("] TJ ET")
  2233. } else {
  2234. var txt2 string
  2235. if f.isCurrentUTF8 {
  2236. if f.isRTL {
  2237. txtStr = reverseText(txtStr)
  2238. }
  2239. txt2 = f.escape(utf8toutf16(txtStr, false))
  2240. for _, uni := range []rune(txtStr) {
  2241. f.currentFont.usedRunes[int(uni)] = int(uni)
  2242. }
  2243. } else {
  2244. txt2 = strings.Replace(txtStr, "\\", "\\\\", -1)
  2245. txt2 = strings.Replace(txt2, "(", "\\(", -1)
  2246. txt2 = strings.Replace(txt2, ")", "\\)", -1)
  2247. }
  2248. bt := (f.x + dx) * k
  2249. td := (f.h - (f.y + dy + .5*h + .3*f.fontSize)) * k
  2250. s.printf("BT %.2f %.2f Td (%s)Tj ET", bt, td, txt2)
  2251. //BT %.2F %.2F Td (%s) Tj ET',(f.x+dx)*k,(f.h-(f.y+.5*h+.3*f.FontSize))*k,txt2);
  2252. }
  2253. if f.underline {
  2254. s.printf(" %s", f.dounderline(f.x+dx, f.y+dy+.5*h+.3*f.fontSize, txtStr))
  2255. }
  2256. if f.colorFlag {
  2257. s.printf(" Q")
  2258. }
  2259. if link > 0 || len(linkStr) > 0 {
  2260. f.newLink(f.x+dx, f.y+dy+.5*h-.5*f.fontSize, f.GetStringWidth(txtStr), f.fontSize, link, linkStr)
  2261. }
  2262. }
  2263. str := s.String()
  2264. if len(str) > 0 {
  2265. f.out(str)
  2266. }
  2267. f.lasth = h
  2268. if ln > 0 {
  2269. // Go to next line
  2270. f.y += h
  2271. if ln == 1 {
  2272. f.x = f.lMargin
  2273. }
  2274. } else {
  2275. f.x += w
  2276. }
  2277. return
  2278. }
  2279. // Revert string to use in RTL languages
  2280. func reverseText(text string) string {
  2281. oldText := []rune(text)
  2282. newText := make([]rune, len(oldText))
  2283. length := len(oldText) - 1
  2284. for i, r := range oldText {
  2285. newText[length-i] = r
  2286. }
  2287. return string(newText)
  2288. }
  2289. // Cell is a simpler version of CellFormat with no fill, border, links or
  2290. // special alignment.
  2291. func (f *Fpdf) Cell(w, h float64, txtStr string) {
  2292. f.CellFormat(w, h, txtStr, "", 0, "L", false, 0, "")
  2293. }
  2294. // Cellf is a simpler printf-style version of CellFormat with no fill, border,
  2295. // links or special alignment. See documentation for the fmt package for
  2296. // details on fmtStr and args.
  2297. func (f *Fpdf) Cellf(w, h float64, fmtStr string, args ...interface{}) {
  2298. f.CellFormat(w, h, sprintf(fmtStr, args...), "", 0, "L", false, 0, "")
  2299. }
  2300. // SplitLines splits text into several lines using the current font. Each line
  2301. // has its length limited to a maximum width given by w. This function can be
  2302. // used to determine the total height of wrapped text for vertical placement
  2303. // purposes.
  2304. //
  2305. // This method is useful for codepage-based fonts only. For UTF-8 encoded text,
  2306. // use SplitText().
  2307. //
  2308. // You can use MultiCell if you want to print a text on several lines in a
  2309. // simple way.
  2310. func (f *Fpdf) SplitLines(txt []byte, w float64) [][]byte {
  2311. // Function contributed by Bruno Michel
  2312. lines := [][]byte{}
  2313. cw := f.currentFont.Cw
  2314. wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
  2315. s := bytes.Replace(txt, []byte("\r"), []byte{}, -1)
  2316. nb := len(s)
  2317. for nb > 0 && s[nb-1] == '\n' {
  2318. nb--
  2319. }
  2320. s = s[0:nb]
  2321. sep := -1
  2322. i := 0
  2323. j := 0
  2324. l := 0
  2325. for i < nb {
  2326. c := s[i]
  2327. l += cw[c]
  2328. if c == ' ' || c == '\t' || c == '\n' {
  2329. sep = i
  2330. }
  2331. if c == '\n' || l > wmax {
  2332. if sep == -1 {
  2333. if i == j {
  2334. i++
  2335. }
  2336. sep = i
  2337. } else {
  2338. i = sep + 1
  2339. }
  2340. lines = append(lines, s[j:sep])
  2341. sep = -1
  2342. j = i
  2343. l = 0
  2344. } else {
  2345. i++
  2346. }
  2347. }
  2348. if i != j {
  2349. lines = append(lines, s[j:i])
  2350. }
  2351. return lines
  2352. }
  2353. // MultiCell supports printing text with line breaks. They can be automatic (as
  2354. // soon as the text reaches the right border of the cell) or explicit (via the
  2355. // \n character). As many cells as necessary are output, one below the other.
  2356. //
  2357. // Text can be aligned, centered or justified. The cell block can be framed and
  2358. // the background painted. See CellFormat() for more details.
  2359. //
  2360. // The current position after calling MultiCell() is the beginning of the next
  2361. // line, equivalent to calling CellFormat with ln equal to 1.
  2362. //
  2363. // w is the width of the cells. A value of zero indicates cells that reach to
  2364. // the right margin.
  2365. //
  2366. // h indicates the line height of each cell in the unit of measure specified in New().
  2367. func (f *Fpdf) MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) {
  2368. if f.err != nil {
  2369. return
  2370. }
  2371. // dbg("MultiCell")
  2372. if alignStr == "" {
  2373. alignStr = "J"
  2374. }
  2375. cw := f.currentFont.Cw
  2376. if w == 0 {
  2377. w = f.w - f.rMargin - f.x
  2378. }
  2379. wmax := int(math.Ceil((w - 2*f.cMargin) * 1000 / f.fontSize))
  2380. s := strings.Replace(txtStr, "\r", "", -1)
  2381. srune := []rune(s)
  2382. // remove extra line breaks
  2383. var nb int
  2384. if f.isCurrentUTF8 {
  2385. nb = len(srune)
  2386. for nb > 0 && srune[nb-1] == '\n' {
  2387. nb--
  2388. }
  2389. srune = srune[0:nb]
  2390. } else {
  2391. nb = len(s)
  2392. bytes2 := []byte(s)
  2393. for nb > 0 && bytes2[nb-1] == '\n' {
  2394. nb--
  2395. }
  2396. s = s[0:nb]
  2397. }
  2398. // dbg("[%s]\n", s)
  2399. var b, b2 string
  2400. b = "0"
  2401. if len(borderStr) > 0 {
  2402. if borderStr == "1" {
  2403. borderStr = "LTRB"
  2404. b = "LRT"
  2405. b2 = "LR"
  2406. } else {
  2407. b2 = ""
  2408. if strings.Contains(borderStr, "L") {
  2409. b2 += "L"
  2410. }
  2411. if strings.Contains(borderStr, "R") {
  2412. b2 += "R"
  2413. }
  2414. if strings.Contains(borderStr, "T") {
  2415. b = b2 + "T"
  2416. } else {
  2417. b = b2
  2418. }
  2419. }
  2420. }
  2421. sep := -1
  2422. i := 0
  2423. j := 0
  2424. l := 0
  2425. ls := 0
  2426. ns := 0
  2427. nl := 1
  2428. for i < nb {
  2429. // Get next character
  2430. var c rune
  2431. if f.isCurrentUTF8 {
  2432. c = srune[i]
  2433. } else {
  2434. c = rune(s[i])
  2435. }
  2436. if c == '\n' {
  2437. // Explicit line break
  2438. if f.ws > 0 {
  2439. f.ws = 0
  2440. f.out("0 Tw")
  2441. }
  2442. if f.isCurrentUTF8 {
  2443. newAlignStr := alignStr
  2444. if newAlignStr == "J" {
  2445. if f.isRTL {
  2446. newAlignStr = "R"
  2447. } else {
  2448. newAlignStr = "L"
  2449. }
  2450. }
  2451. f.CellFormat(w, h, string(srune[j:i]), b, 2, newAlignStr, fill, 0, "")
  2452. } else {
  2453. f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
  2454. }
  2455. i++
  2456. sep = -1
  2457. j = i
  2458. l = 0
  2459. ns = 0
  2460. nl++
  2461. if len(borderStr) > 0 && nl == 2 {
  2462. b = b2
  2463. }
  2464. continue
  2465. }
  2466. if c == ' ' || isChinese(c) {
  2467. sep = i
  2468. ls = l
  2469. ns++
  2470. }
  2471. if cw[int(c)] == 0 { //Marker width 0 used for missing symbols
  2472. l += f.currentFont.Desc.MissingWidth
  2473. } else if cw[int(c)] != 65535 { //Marker width 65535 used for zero width symbols
  2474. l += cw[int(c)]
  2475. }
  2476. if l > wmax {
  2477. // Automatic line break
  2478. if sep == -1 {
  2479. if i == j {
  2480. i++
  2481. }
  2482. if f.ws > 0 {
  2483. f.ws = 0
  2484. f.out("0 Tw")
  2485. }
  2486. if f.isCurrentUTF8 {
  2487. f.CellFormat(w, h, string(srune[j:i]), b, 2, alignStr, fill, 0, "")
  2488. } else {
  2489. f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
  2490. }
  2491. } else {
  2492. if alignStr == "J" {
  2493. if ns > 1 {
  2494. f.ws = float64((wmax-ls)/1000) * f.fontSize / float64(ns-1)
  2495. } else {
  2496. f.ws = 0
  2497. }
  2498. f.outf("%.3f Tw", f.ws*f.k)
  2499. }
  2500. if f.isCurrentUTF8 {
  2501. f.CellFormat(w, h, string(srune[j:sep]), b, 2, alignStr, fill, 0, "")
  2502. } else {
  2503. f.CellFormat(w, h, s[j:sep], b, 2, alignStr, fill, 0, "")
  2504. }
  2505. i = sep + 1
  2506. }
  2507. sep = -1
  2508. j = i
  2509. l = 0
  2510. ns = 0
  2511. nl++
  2512. if len(borderStr) > 0 && nl == 2 {
  2513. b = b2
  2514. }
  2515. } else {
  2516. i++
  2517. }
  2518. }
  2519. // Last chunk
  2520. if f.ws > 0 {
  2521. f.ws = 0
  2522. f.out("0 Tw")
  2523. }
  2524. if len(borderStr) > 0 && strings.Contains(borderStr, "B") {
  2525. b += "B"
  2526. }
  2527. if f.isCurrentUTF8 {
  2528. if alignStr == "J" {
  2529. if f.isRTL {
  2530. alignStr = "R"
  2531. } else {
  2532. alignStr = ""
  2533. }
  2534. }
  2535. f.CellFormat(w, h, string(srune[j:i]), b, 2, alignStr, fill, 0, "")
  2536. } else {
  2537. f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
  2538. }
  2539. f.x = f.lMargin
  2540. }
  2541. // write outputs text in flowing mode
  2542. func (f *Fpdf) write(h float64, txtStr string, link int, linkStr string) {
  2543. // dbg("Write")
  2544. cw := f.currentFont.Cw
  2545. w := f.w - f.rMargin - f.x
  2546. wmax := (w - 2*f.cMargin) * 1000 / f.fontSize
  2547. s := strings.Replace(txtStr, "\r", "", -1)
  2548. var nb int
  2549. if f.isCurrentUTF8 {
  2550. nb = len([]rune(s))
  2551. if nb == 1 && s == " " {
  2552. f.x += f.GetStringWidth(s)
  2553. return
  2554. }
  2555. } else {
  2556. nb = len(s)
  2557. }
  2558. sep := -1
  2559. i := 0
  2560. j := 0
  2561. l := 0.0
  2562. nl := 1
  2563. for i < nb {
  2564. // Get next character
  2565. var c rune
  2566. if f.isCurrentUTF8 {
  2567. c = []rune(s)[i]
  2568. } else {
  2569. c = rune(byte(s[i]))
  2570. }
  2571. if c == '\n' {
  2572. // Explicit line break
  2573. if f.isCurrentUTF8 {
  2574. f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr)
  2575. } else {
  2576. f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
  2577. }
  2578. i++
  2579. sep = -1
  2580. j = i
  2581. l = 0.0
  2582. if nl == 1 {
  2583. f.x = f.lMargin
  2584. w = f.w - f.rMargin - f.x
  2585. wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
  2586. }
  2587. nl++
  2588. continue
  2589. }
  2590. if c == ' ' {
  2591. sep = i
  2592. }
  2593. l += float64(cw[int(c)])
  2594. if l > wmax {
  2595. // Automatic line break
  2596. if sep == -1 {
  2597. if f.x > f.lMargin {
  2598. // Move to next line
  2599. f.x = f.lMargin
  2600. f.y += h
  2601. w = f.w - f.rMargin - f.x
  2602. wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
  2603. i++
  2604. nl++
  2605. continue
  2606. }
  2607. if i == j {
  2608. i++
  2609. }
  2610. if f.isCurrentUTF8 {
  2611. f.CellFormat(w, h, string([]rune(s)[j:i]), "", 2, "", false, link, linkStr)
  2612. } else {
  2613. f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
  2614. }
  2615. } else {
  2616. if f.isCurrentUTF8 {
  2617. f.CellFormat(w, h, string([]rune(s)[j:sep]), "", 2, "", false, link, linkStr)
  2618. } else {
  2619. f.CellFormat(w, h, s[j:sep], "", 2, "", false, link, linkStr)
  2620. }
  2621. i = sep + 1
  2622. }
  2623. sep = -1
  2624. j = i
  2625. l = 0.0
  2626. if nl == 1 {
  2627. f.x = f.lMargin
  2628. w = f.w - f.rMargin - f.x
  2629. wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
  2630. }
  2631. nl++
  2632. } else {
  2633. i++
  2634. }
  2635. }
  2636. // Last chunk
  2637. if i != j {
  2638. if f.isCurrentUTF8 {
  2639. f.CellFormat(l/1000*f.fontSize, h, string([]rune(s)[j:]), "", 0, "", false, link, linkStr)
  2640. } else {
  2641. f.CellFormat(l/1000*f.fontSize, h, s[j:], "", 0, "", false, link, linkStr)
  2642. }
  2643. }
  2644. }
  2645. // Write prints text from the current position. When the right margin is
  2646. // reached (or the \n character is met) a line break occurs and text continues
  2647. // from the left margin. Upon method exit, the current position is left just at
  2648. // the end of the text.
  2649. //
  2650. // It is possible to put a link on the text.
  2651. //
  2652. // h indicates the line height in the unit of measure specified in New().
  2653. func (f *Fpdf) Write(h float64, txtStr string) {
  2654. f.write(h, txtStr, 0, "")
  2655. }
  2656. // Writef is like Write but uses printf-style formatting. See the documentation
  2657. // for package fmt for more details on fmtStr and args.
  2658. func (f *Fpdf) Writef(h float64, fmtStr string, args ...interface{}) {
  2659. f.write(h, sprintf(fmtStr, args...), 0, "")
  2660. }
  2661. // WriteLinkString writes text that when clicked launches an external URL. See
  2662. // Write() for argument details.
  2663. func (f *Fpdf) WriteLinkString(h float64, displayStr, targetStr string) {
  2664. f.write(h, displayStr, 0, targetStr)
  2665. }
  2666. // WriteLinkID writes text that when clicked jumps to another location in the
  2667. // PDF. linkID is an identifier returned by AddLink(). See Write() for argument
  2668. // details.
  2669. func (f *Fpdf) WriteLinkID(h float64, displayStr string, linkID int) {
  2670. f.write(h, displayStr, linkID, "")
  2671. }
  2672. // WriteAligned is an implementation of Write that makes it possible to align
  2673. // text.
  2674. //
  2675. // width indicates the width of the box the text will be drawn in. This is in
  2676. // the unit of measure specified in New(). If it is set to 0, the bounding box
  2677. //of the page will be taken (pageWidth - leftMargin - rightMargin).
  2678. //
  2679. // lineHeight indicates the line height in the unit of measure specified in
  2680. // New().
  2681. //
  2682. // alignStr sees to horizontal alignment of the given textStr. The options are
  2683. // "L", "C" and "R" (Left, Center, Right). The default is "L".
  2684. func (f *Fpdf) WriteAligned(width, lineHeight float64, textStr, alignStr string) {
  2685. lMargin, _, rMargin, _ := f.GetMargins()
  2686. pageWidth, _ := f.GetPageSize()
  2687. if width == 0 {
  2688. width = pageWidth - (lMargin + rMargin)
  2689. }
  2690. var lines []string
  2691. if f.isCurrentUTF8 {
  2692. lines = f.SplitText(textStr, width)
  2693. } else {
  2694. for _, line := range f.SplitLines([]byte(textStr), width) {
  2695. lines = append(lines, string(line))
  2696. }
  2697. }
  2698. for _, lineBt := range lines {
  2699. lineStr := string(lineBt)
  2700. lineWidth := f.GetStringWidth(lineStr)
  2701. switch alignStr {
  2702. case "C":
  2703. f.SetLeftMargin(lMargin + ((width - lineWidth) / 2))
  2704. f.Write(lineHeight, lineStr)
  2705. f.SetLeftMargin(lMargin)
  2706. case "R":
  2707. f.SetLeftMargin(lMargin + (width - lineWidth) - 2.01*f.cMargin)
  2708. f.Write(lineHeight, lineStr)
  2709. f.SetLeftMargin(lMargin)
  2710. default:
  2711. f.SetRightMargin(pageWidth - lMargin - width)
  2712. f.Write(lineHeight, lineStr)
  2713. f.SetRightMargin(rMargin)
  2714. }
  2715. }
  2716. }
  2717. // Ln performs a line break. The current abscissa goes back to the left margin
  2718. // and the ordinate increases by the amount passed in parameter. A negative
  2719. // value of h indicates the height of the last printed cell.
  2720. //
  2721. // This method is demonstrated in the example for MultiCell.
  2722. func (f *Fpdf) Ln(h float64) {
  2723. f.x = f.lMargin
  2724. if h < 0 {
  2725. f.y += f.lasth
  2726. } else {
  2727. f.y += h
  2728. }
  2729. }
  2730. // ImageTypeFromMime returns the image type used in various image-related
  2731. // functions (for example, Image()) that is associated with the specified MIME
  2732. // type. For example, "jpg" is returned if mimeStr is "image/jpeg". An error is
  2733. // set if the specified MIME type is not supported.
  2734. func (f *Fpdf) ImageTypeFromMime(mimeStr string) (tp string) {
  2735. switch mimeStr {
  2736. case "image/png":
  2737. tp = "png"
  2738. case "image/jpg":
  2739. tp = "jpg"
  2740. case "image/jpeg":
  2741. tp = "jpg"
  2742. case "image/gif":
  2743. tp = "gif"
  2744. default:
  2745. f.SetErrorf("unsupported image type: %s", mimeStr)
  2746. }
  2747. return
  2748. }
  2749. func (f *Fpdf) imageOut(info *ImageInfoType, x, y, w, h float64, allowNegativeX, flow bool, link int, linkStr string) {
  2750. // Automatic width and height calculation if needed
  2751. if w == 0 && h == 0 {
  2752. // Put image at 96 dpi
  2753. w = -96
  2754. h = -96
  2755. }
  2756. if w == -1 {
  2757. // Set image width to whatever value for dpi we read
  2758. // from the image or that was set manually
  2759. w = -info.dpi
  2760. }
  2761. if h == -1 {
  2762. // Set image height to whatever value for dpi we read
  2763. // from the image or that was set manually
  2764. h = -info.dpi
  2765. }
  2766. if w < 0 {
  2767. w = -info.w * 72.0 / w / f.k
  2768. }
  2769. if h < 0 {
  2770. h = -info.h * 72.0 / h / f.k
  2771. }
  2772. if w == 0 {
  2773. w = h * info.w / info.h
  2774. }
  2775. if h == 0 {
  2776. h = w * info.h / info.w
  2777. }
  2778. // Flowing mode
  2779. if flow {
  2780. if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
  2781. // Automatic page break
  2782. x2 := f.x
  2783. f.AddPageFormat(f.curOrientation, f.curPageSize)
  2784. if f.err != nil {
  2785. return
  2786. }
  2787. f.x = x2
  2788. }
  2789. y = f.y
  2790. f.y += h
  2791. }
  2792. if !allowNegativeX {
  2793. if x < 0 {
  2794. x = f.x
  2795. }
  2796. }
  2797. // dbg("h %.2f", h)
  2798. // q 85.04 0 0 NaN 28.35 NaN cm /I2 Do Q
  2799. f.outf("q %.5f 0 0 %.5f %.5f %.5f cm /I%s Do Q", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k, info.i)
  2800. if link > 0 || len(linkStr) > 0 {
  2801. f.newLink(x, y, w, h, link, linkStr)
  2802. }
  2803. }
  2804. // Image puts a JPEG, PNG or GIF image in the current page.
  2805. //
  2806. // Deprecated in favor of ImageOptions -- see that function for
  2807. // details on the behavior of arguments
  2808. func (f *Fpdf) Image(imageNameStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) {
  2809. options := ImageOptions{
  2810. ReadDpi: false,
  2811. ImageType: tp,
  2812. }
  2813. f.ImageOptions(imageNameStr, x, y, w, h, flow, options, link, linkStr)
  2814. }
  2815. // ImageOptions puts a JPEG, PNG or GIF image in the current page. The size it
  2816. // will take on the page can be specified in different ways. If both w and h
  2817. // are 0, the image is rendered at 96 dpi. If either w or h is zero, it will be
  2818. // calculated from the other dimension so that the aspect ratio is maintained.
  2819. // If w and/or h are -1, the dpi for that dimension will be read from the
  2820. // ImageInfoType object. PNG files can contain dpi information, and if present,
  2821. // this information will be populated in the ImageInfoType object and used in
  2822. // Width, Height, and Extent calculations. Otherwise, the SetDpi function can
  2823. // be used to change the dpi from the default of 72.
  2824. //
  2825. // If w and h are any other negative value, their absolute values
  2826. // indicate their dpi extents.
  2827. //
  2828. // Supported JPEG formats are 24 bit, 32 bit and gray scale. Supported PNG
  2829. // formats are 24 bit, indexed color, and 8 bit indexed gray scale. If a GIF
  2830. // image is animated, only the first frame is rendered. Transparency is
  2831. // supported. It is possible to put a link on the image.
  2832. //
  2833. // imageNameStr may be the name of an image as registered with a call to either
  2834. // RegisterImageReader() or RegisterImage(). In the first case, the image is
  2835. // loaded using an io.Reader. This is generally useful when the image is
  2836. // obtained from some other means than as a disk-based file. In the second
  2837. // case, the image is loaded as a file. Alternatively, imageNameStr may
  2838. // directly specify a sufficiently qualified filename.
  2839. //
  2840. // However the image is loaded, if it is used more than once only one copy is
  2841. // embedded in the file.
  2842. //
  2843. // If x is negative, the current abscissa is used.
  2844. //
  2845. // If flow is true, the current y value is advanced after placing the image and
  2846. // a page break may be made if necessary.
  2847. //
  2848. // If link refers to an internal page anchor (that is, it is non-zero; see
  2849. // AddLink()), the image will be a clickable internal link. Otherwise, if
  2850. // linkStr specifies a URL, the image will be a clickable external link.
  2851. func (f *Fpdf) ImageOptions(imageNameStr string, x, y, w, h float64, flow bool, options ImageOptions, link int, linkStr string) {
  2852. if f.err != nil {
  2853. return
  2854. }
  2855. info := f.RegisterImageOptions(imageNameStr, options)
  2856. if f.err != nil {
  2857. return
  2858. }
  2859. f.imageOut(info, x, y, w, h, options.AllowNegativePosition, flow, link, linkStr)
  2860. return
  2861. }
  2862. // RegisterImageReader registers an image, reading it from Reader r, adding it
  2863. // to the PDF file but not adding it to the page.
  2864. //
  2865. // This function is now deprecated in favor of RegisterImageOptionsReader
  2866. func (f *Fpdf) RegisterImageReader(imgName, tp string, r io.Reader) (info *ImageInfoType) {
  2867. options := ImageOptions{
  2868. ReadDpi: false,
  2869. ImageType: tp,
  2870. }
  2871. return f.RegisterImageOptionsReader(imgName, options, r)
  2872. }
  2873. // ImageOptions provides a place to hang any options we want to use while
  2874. // parsing an image.
  2875. //
  2876. // ImageType's possible values are (case insensitive):
  2877. // "JPG", "JPEG", "PNG" and "GIF". If empty, the type is inferred from
  2878. // the file extension.
  2879. //
  2880. // ReadDpi defines whether to attempt to automatically read the image
  2881. // dpi information from the image file. Normally, this should be set
  2882. // to true (understanding that not all images will have this info
  2883. // available). However, for backwards compatibility with previous
  2884. // versions of the API, it defaults to false.
  2885. //
  2886. // AllowNegativePosition can be set to true in order to prevent the default
  2887. // coercion of negative x values to the current x position.
  2888. type ImageOptions struct {
  2889. ImageType string
  2890. ReadDpi bool
  2891. AllowNegativePosition bool
  2892. }
  2893. // RegisterImageOptionsReader registers an image, reading it from Reader r, adding it
  2894. // to the PDF file but not adding it to the page. Use Image() with the same
  2895. // name to add the image to the page. Note that tp should be specified in this
  2896. // case.
  2897. //
  2898. // See Image() for restrictions on the image and the options parameters.
  2899. func (f *Fpdf) RegisterImageOptionsReader(imgName string, options ImageOptions, r io.Reader) (info *ImageInfoType) {
  2900. // Thanks, Ivan Daniluk, for generalizing this code to use the Reader interface.
  2901. if f.err != nil {
  2902. return
  2903. }
  2904. info, ok := f.images[imgName]
  2905. if ok {
  2906. return
  2907. }
  2908. // First use of this image, get info
  2909. if options.ImageType == "" {
  2910. f.err = fmt.Errorf("image type should be specified if reading from custom reader")
  2911. return
  2912. }
  2913. options.ImageType = strings.ToLower(options.ImageType)
  2914. if options.ImageType == "jpeg" {
  2915. options.ImageType = "jpg"
  2916. }
  2917. switch options.ImageType {
  2918. case "jpg":
  2919. info = f.parsejpg(r)
  2920. case "png":
  2921. info = f.parsepng(r, options.ReadDpi)
  2922. case "gif":
  2923. info = f.parsegif(r)
  2924. default:
  2925. f.err = fmt.Errorf("unsupported image type: %s", options.ImageType)
  2926. }
  2927. if f.err != nil {
  2928. return
  2929. }
  2930. if info.i, f.err = generateImageID(info); f.err != nil {
  2931. return
  2932. }
  2933. f.images[imgName] = info
  2934. return
  2935. }
  2936. // RegisterImage registers an image, adding it to the PDF file but not adding
  2937. // it to the page. Use Image() with the same filename to add the image to the
  2938. // page. Note that Image() calls this function, so this function is only
  2939. // necessary if you need information about the image before placing it.
  2940. //
  2941. // This function is now deprecated in favor of RegisterImageOptions.
  2942. // See Image() for restrictions on the image and the "tp" parameters.
  2943. func (f *Fpdf) RegisterImage(fileStr, tp string) (info *ImageInfoType) {
  2944. options := ImageOptions{
  2945. ReadDpi: false,
  2946. ImageType: tp,
  2947. }
  2948. return f.RegisterImageOptions(fileStr, options)
  2949. }
  2950. // RegisterImageOptions registers an image, adding it to the PDF file but not
  2951. // adding it to the page. Use Image() with the same filename to add the image
  2952. // to the page. Note that Image() calls this function, so this function is only
  2953. // necessary if you need information about the image before placing it. See
  2954. // Image() for restrictions on the image and the "tp" parameters.
  2955. func (f *Fpdf) RegisterImageOptions(fileStr string, options ImageOptions) (info *ImageInfoType) {
  2956. info, ok := f.images[fileStr]
  2957. if ok {
  2958. return
  2959. }
  2960. file, err := os.Open(fileStr)
  2961. if err != nil {
  2962. f.err = err
  2963. return
  2964. }
  2965. defer file.Close()
  2966. // First use of this image, get info
  2967. if options.ImageType == "" {
  2968. pos := strings.LastIndex(fileStr, ".")
  2969. if pos < 0 {
  2970. f.err = fmt.Errorf("image file has no extension and no type was specified: %s", fileStr)
  2971. return
  2972. }
  2973. options.ImageType = fileStr[pos+1:]
  2974. }
  2975. return f.RegisterImageOptionsReader(fileStr, options, file)
  2976. }
  2977. // GetImageInfo returns information about the registered image specified by
  2978. // imageStr. If the image has not been registered, nil is returned. The
  2979. // internal error is not modified by this method.
  2980. func (f *Fpdf) GetImageInfo(imageStr string) (info *ImageInfoType) {
  2981. return f.images[imageStr]
  2982. }
  2983. // ImportObjects imports objects from gofpdi into current document
  2984. func (f *Fpdf) ImportObjects(objs map[string][]byte) {
  2985. for k, v := range objs {
  2986. f.importedObjs[k] = v
  2987. }
  2988. }
  2989. // ImportObjPos imports object hash positions from gofpdi
  2990. func (f *Fpdf) ImportObjPos(objPos map[string]map[int]string) {
  2991. for k, v := range objPos {
  2992. f.importedObjPos[k] = v
  2993. }
  2994. }
  2995. // putImportedTemplates writes the imported template objects to the PDF
  2996. func (f *Fpdf) putImportedTemplates() {
  2997. nOffset := f.n + 1
  2998. // keep track of list of sha1 hashes (to be replaced with integers)
  2999. objsIDHash := make([]string, len(f.importedObjs))
  3000. // actual object data with new id
  3001. objsIDData := make([][]byte, len(f.importedObjs))
  3002. // Populate hash slice and data slice
  3003. i := 0
  3004. for k, v := range f.importedObjs {
  3005. objsIDHash[i] = k
  3006. objsIDData[i] = v
  3007. i++
  3008. }
  3009. // Populate a lookup table to get an object id from a hash
  3010. hashToObjID := make(map[string]int, len(f.importedObjs))
  3011. for i = 0; i < len(objsIDHash); i++ {
  3012. hashToObjID[objsIDHash[i]] = i + nOffset
  3013. }
  3014. // Now, replace hashes inside data with %040d object id
  3015. for i = 0; i < len(objsIDData); i++ {
  3016. // get hash
  3017. hash := objsIDHash[i]
  3018. for pos, h := range f.importedObjPos[hash] {
  3019. // Convert object id into a 40 character string padded with spaces
  3020. objIDPadded := fmt.Sprintf("%40s", fmt.Sprintf("%d", hashToObjID[h]))
  3021. // Convert objIDPadded into []byte
  3022. objIDBytes := []byte(objIDPadded)
  3023. // Replace sha1 hash with object id padded
  3024. for j := pos; j < pos+40; j++ {
  3025. objsIDData[i][j] = objIDBytes[j-pos]
  3026. }
  3027. }
  3028. // Save objsIDHash so that procset dictionary has the correct object ids
  3029. f.importedTplIDs[hash] = i + nOffset
  3030. }
  3031. // Now, put objects
  3032. for i = 0; i < len(objsIDData); i++ {
  3033. f.newobj()
  3034. f.out(string(objsIDData[i]))
  3035. }
  3036. }
  3037. // UseImportedTemplate uses imported template from gofpdi. It draws imported
  3038. // PDF page onto page.
  3039. func (f *Fpdf) UseImportedTemplate(tplName string, scaleX float64, scaleY float64, tX float64, tY float64) {
  3040. f.outf("q 0 J 1 w 0 j 0 G 0 g q %.4F 0 0 %.4F %.4F %.4F cm %s Do Q Q\n", scaleX*f.k, scaleY*f.k, tX*f.k, (tY+f.h)*f.k, tplName)
  3041. }
  3042. // ImportTemplates imports gofpdi template names into importedTplObjs for
  3043. // inclusion in the procset dictionary
  3044. func (f *Fpdf) ImportTemplates(tpls map[string]string) {
  3045. for tplName, tplID := range tpls {
  3046. f.importedTplObjs[tplName] = tplID
  3047. }
  3048. }
  3049. // GetConversionRatio returns the conversion ratio based on the unit given when
  3050. // creating the PDF.
  3051. func (f *Fpdf) GetConversionRatio() float64 {
  3052. return f.k
  3053. }
  3054. // GetXY returns the abscissa and ordinate of the current position.
  3055. //
  3056. // Note: the value returned for the abscissa will be affected by the current
  3057. // cell margin. To account for this, you may need to either add the value
  3058. // returned by GetCellMargin() to it or call SetCellMargin(0) to remove the
  3059. // cell margin.
  3060. func (f *Fpdf) GetXY() (float64, float64) {
  3061. return f.x, f.y
  3062. }
  3063. // GetX returns the abscissa of the current position.
  3064. //
  3065. // Note: the value returned will be affected by the current cell margin. To
  3066. // account for this, you may need to either add the value returned by
  3067. // GetCellMargin() to it or call SetCellMargin(0) to remove the cell margin.
  3068. func (f *Fpdf) GetX() float64 {
  3069. return f.x
  3070. }
  3071. // SetX defines the abscissa of the current position. If the passed value is
  3072. // negative, it is relative to the right of the page.
  3073. func (f *Fpdf) SetX(x float64) {
  3074. if x >= 0 {
  3075. f.x = x
  3076. } else {
  3077. f.x = f.w + x
  3078. }
  3079. }
  3080. // GetY returns the ordinate of the current position.
  3081. func (f *Fpdf) GetY() float64 {
  3082. return f.y
  3083. }
  3084. // SetY moves the current abscissa back to the left margin and sets the
  3085. // ordinate. If the passed value is negative, it is relative to the bottom of
  3086. // the page.
  3087. func (f *Fpdf) SetY(y float64) {
  3088. // dbg("SetY x %.2f, lMargin %.2f", f.x, f.lMargin)
  3089. f.x = f.lMargin
  3090. if y >= 0 {
  3091. f.y = y
  3092. } else {
  3093. f.y = f.h + y
  3094. }
  3095. }
  3096. // SetHomeXY is a convenience method that sets the current position to the left
  3097. // and top margins.
  3098. func (f *Fpdf) SetHomeXY() {
  3099. f.SetY(f.tMargin)
  3100. f.SetX(f.lMargin)
  3101. }
  3102. // SetXY defines the abscissa and ordinate of the current position. If the
  3103. // passed values are negative, they are relative respectively to the right and
  3104. // bottom of the page.
  3105. func (f *Fpdf) SetXY(x, y float64) {
  3106. f.SetY(y)
  3107. f.SetX(x)
  3108. }
  3109. // SetProtection applies certain constraints on the finished PDF document.
  3110. //
  3111. // actionFlag is a bitflag that controls various document operations.
  3112. // CnProtectPrint allows the document to be printed. CnProtectModify allows a
  3113. // document to be modified by a PDF editor. CnProtectCopy allows text and
  3114. // images to be copied into the system clipboard. CnProtectAnnotForms allows
  3115. // annotations and forms to be added by a PDF editor. These values can be
  3116. // combined by or-ing them together, for example,
  3117. // CnProtectCopy|CnProtectModify. This flag is advisory; not all PDF readers
  3118. // implement the constraints that this argument attempts to control.
  3119. //
  3120. // userPassStr specifies the password that will need to be provided to view the
  3121. // contents of the PDF. The permissions specified by actionFlag will apply.
  3122. //
  3123. // ownerPassStr specifies the password that will need to be provided to gain
  3124. // full access to the document regardless of the actionFlag value. An empty
  3125. // string for this argument will be replaced with a random value, effectively
  3126. // prohibiting full access to the document.
  3127. func (f *Fpdf) SetProtection(actionFlag byte, userPassStr, ownerPassStr string) {
  3128. if f.err != nil {
  3129. return
  3130. }
  3131. f.protect.setProtection(actionFlag, userPassStr, ownerPassStr)
  3132. }
  3133. // OutputAndClose sends the PDF document to the writer specified by w. This
  3134. // method will close both f and w, even if an error is detected and no document
  3135. // is produced.
  3136. func (f *Fpdf) OutputAndClose(w io.WriteCloser) error {
  3137. f.Output(w)
  3138. w.Close()
  3139. return f.err
  3140. }
  3141. // OutputFileAndClose creates or truncates the file specified by fileStr and
  3142. // writes the PDF document to it. This method will close f and the newly
  3143. // written file, even if an error is detected and no document is produced.
  3144. //
  3145. // Most examples demonstrate the use of this method.
  3146. func (f *Fpdf) OutputFileAndClose(fileStr string) error {
  3147. if f.err == nil {
  3148. pdfFile, err := os.Create(fileStr)
  3149. if err == nil {
  3150. f.Output(pdfFile)
  3151. pdfFile.Close()
  3152. } else {
  3153. f.err = err
  3154. }
  3155. }
  3156. return f.err
  3157. }
  3158. // Output sends the PDF document to the writer specified by w. No output will
  3159. // take place if an error has occurred in the document generation process. w
  3160. // remains open after this function returns. After returning, f is in a closed
  3161. // state and its methods should not be called.
  3162. func (f *Fpdf) Output(w io.Writer) error {
  3163. if f.err != nil {
  3164. return f.err
  3165. }
  3166. // dbg("Output")
  3167. if f.state < 3 {
  3168. f.Close()
  3169. }
  3170. _, err := f.buffer.WriteTo(w)
  3171. if err != nil {
  3172. f.err = err
  3173. }
  3174. return f.err
  3175. }
  3176. func (f *Fpdf) getpagesizestr(sizeStr string) (size SizeType) {
  3177. if f.err != nil {
  3178. return
  3179. }
  3180. sizeStr = strings.ToLower(sizeStr)
  3181. // dbg("Size [%s]", sizeStr)
  3182. var ok bool
  3183. size, ok = f.stdPageSizes[sizeStr]
  3184. if ok {
  3185. // dbg("found %s", sizeStr)
  3186. size.Wd /= f.k
  3187. size.Ht /= f.k
  3188. } else {
  3189. f.err = fmt.Errorf("unknown page size %s", sizeStr)
  3190. }
  3191. return
  3192. }
  3193. // GetPageSizeStr returns the SizeType for the given sizeStr (that is A4, A3, etc..)
  3194. func (f *Fpdf) GetPageSizeStr(sizeStr string) (size SizeType) {
  3195. return f.getpagesizestr(sizeStr)
  3196. }
  3197. func (f *Fpdf) _getpagesize(size SizeType) SizeType {
  3198. if size.Wd > size.Ht {
  3199. size.Wd, size.Ht = size.Ht, size.Wd
  3200. }
  3201. return size
  3202. }
  3203. func (f *Fpdf) beginpage(orientationStr string, size SizeType) {
  3204. if f.err != nil {
  3205. return
  3206. }
  3207. f.page++
  3208. // add the default page boxes, if any exist, to the page
  3209. f.pageBoxes[f.page] = make(map[string]PageBox)
  3210. for box, pb := range f.defPageBoxes {
  3211. f.pageBoxes[f.page][box] = pb
  3212. }
  3213. f.pages = append(f.pages, bytes.NewBufferString(""))
  3214. f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0))
  3215. f.state = 2
  3216. f.x = f.lMargin
  3217. f.y = f.tMargin
  3218. f.fontFamily = ""
  3219. // Check page size and orientation
  3220. if orientationStr == "" {
  3221. orientationStr = f.defOrientation
  3222. } else {
  3223. orientationStr = strings.ToUpper(orientationStr[0:1])
  3224. }
  3225. if orientationStr != f.curOrientation || size.Wd != f.curPageSize.Wd || size.Ht != f.curPageSize.Ht {
  3226. // New size or orientation
  3227. if orientationStr == "P" {
  3228. f.w = size.Wd
  3229. f.h = size.Ht
  3230. } else {
  3231. f.w = size.Ht
  3232. f.h = size.Wd
  3233. }
  3234. f.wPt = f.w * f.k
  3235. f.hPt = f.h * f.k
  3236. f.pageBreakTrigger = f.h - f.bMargin
  3237. f.curOrientation = orientationStr
  3238. f.curPageSize = size
  3239. }
  3240. if orientationStr != f.defOrientation || size.Wd != f.defPageSize.Wd || size.Ht != f.defPageSize.Ht {
  3241. f.pageSizes[f.page] = SizeType{f.wPt, f.hPt}
  3242. }
  3243. return
  3244. }
  3245. func (f *Fpdf) endpage() {
  3246. f.EndLayer()
  3247. f.state = 1
  3248. }
  3249. // Load a font definition file from the given Reader
  3250. func (f *Fpdf) loadfont(r io.Reader) (def fontDefType) {
  3251. if f.err != nil {
  3252. return
  3253. }
  3254. // dbg("Loading font [%s]", fontStr)
  3255. var buf bytes.Buffer
  3256. _, err := buf.ReadFrom(r)
  3257. if err != nil {
  3258. f.err = err
  3259. return
  3260. }
  3261. err = json.Unmarshal(buf.Bytes(), &def)
  3262. if err != nil {
  3263. f.err = err
  3264. return
  3265. }
  3266. if def.i, err = generateFontID(def); err != nil {
  3267. f.err = err
  3268. }
  3269. // dump(def)
  3270. return
  3271. }
  3272. // Escape special characters in strings
  3273. func (f *Fpdf) escape(s string) string {
  3274. s = strings.Replace(s, "\\", "\\\\", -1)
  3275. s = strings.Replace(s, "(", "\\(", -1)
  3276. s = strings.Replace(s, ")", "\\)", -1)
  3277. s = strings.Replace(s, "\r", "\\r", -1)
  3278. return s
  3279. }
  3280. // textstring formats a text string
  3281. func (f *Fpdf) textstring(s string) string {
  3282. if f.protect.encrypted {
  3283. b := []byte(s)
  3284. f.protect.rc4(uint32(f.n), &b)
  3285. s = string(b)
  3286. }
  3287. return "(" + f.escape(s) + ")"
  3288. }
  3289. func blankCount(str string) (count int) {
  3290. l := len(str)
  3291. for j := 0; j < l; j++ {
  3292. if byte(' ') == str[j] {
  3293. count++
  3294. }
  3295. }
  3296. return
  3297. }
  3298. // SetUnderlineThickness accepts a multiplier for adjusting the text underline
  3299. // thickness, defaulting to 1. See SetUnderlineThickness example.
  3300. func (f *Fpdf) SetUnderlineThickness(thickness float64) {
  3301. f.userUnderlineThickness = thickness
  3302. }
  3303. // Underline text
  3304. func (f *Fpdf) dounderline(x, y float64, txt string) string {
  3305. up := float64(f.currentFont.Up)
  3306. ut := float64(f.currentFont.Ut) * f.userUnderlineThickness
  3307. w := f.GetStringWidth(txt) + f.ws*float64(blankCount(txt))
  3308. return sprintf("%.2f %.2f %.2f %.2f re f", x*f.k,
  3309. (f.h-(y-up/1000*f.fontSize))*f.k, w*f.k, -ut/1000*f.fontSizePt)
  3310. }
  3311. func bufEqual(buf []byte, str string) bool {
  3312. return string(buf[0:len(str)]) == str
  3313. }
  3314. func be16(buf []byte) int {
  3315. return 256*int(buf[0]) + int(buf[1])
  3316. }
  3317. func (f *Fpdf) newImageInfo() *ImageInfoType {
  3318. // default dpi to 72 unless told otherwise
  3319. return &ImageInfoType{scale: f.k, dpi: 72}
  3320. }
  3321. // parsejpg extracts info from io.Reader with JPEG data
  3322. // Thank you, Bruno Michel, for providing this code.
  3323. func (f *Fpdf) parsejpg(r io.Reader) (info *ImageInfoType) {
  3324. info = f.newImageInfo()
  3325. var (
  3326. data bytes.Buffer
  3327. err error
  3328. )
  3329. _, err = data.ReadFrom(r)
  3330. if err != nil {
  3331. f.err = err
  3332. return
  3333. }
  3334. info.data = data.Bytes()
  3335. config, err := jpeg.DecodeConfig(bytes.NewReader(info.data))
  3336. if err != nil {
  3337. f.err = err
  3338. return
  3339. }
  3340. info.w = float64(config.Width)
  3341. info.h = float64(config.Height)
  3342. info.f = "DCTDecode"
  3343. info.bpc = 8
  3344. switch config.ColorModel {
  3345. case color.GrayModel:
  3346. info.cs = "DeviceGray"
  3347. case color.YCbCrModel:
  3348. info.cs = "DeviceRGB"
  3349. case color.CMYKModel:
  3350. info.cs = "DeviceCMYK"
  3351. default:
  3352. f.err = fmt.Errorf("image JPEG buffer has unsupported color space (%v)", config.ColorModel)
  3353. return
  3354. }
  3355. return
  3356. }
  3357. // parsepng extracts info from a PNG data
  3358. func (f *Fpdf) parsepng(r io.Reader, readdpi bool) (info *ImageInfoType) {
  3359. buf, err := bufferFromReader(r)
  3360. if err != nil {
  3361. f.err = err
  3362. return
  3363. }
  3364. return f.parsepngstream(buf, readdpi)
  3365. }
  3366. func (f *Fpdf) readBeInt32(r io.Reader) (val int32) {
  3367. err := binary.Read(r, binary.BigEndian, &val)
  3368. if err != nil && err != io.EOF {
  3369. f.err = err
  3370. }
  3371. return
  3372. }
  3373. func (f *Fpdf) readByte(r io.Reader) (val byte) {
  3374. err := binary.Read(r, binary.BigEndian, &val)
  3375. if err != nil {
  3376. f.err = err
  3377. }
  3378. return
  3379. }
  3380. // parsegif extracts info from a GIF data (via PNG conversion)
  3381. func (f *Fpdf) parsegif(r io.Reader) (info *ImageInfoType) {
  3382. data, err := bufferFromReader(r)
  3383. if err != nil {
  3384. f.err = err
  3385. return
  3386. }
  3387. var img image.Image
  3388. img, err = gif.Decode(data)
  3389. if err != nil {
  3390. f.err = err
  3391. return
  3392. }
  3393. pngBuf := new(bytes.Buffer)
  3394. err = png.Encode(pngBuf, img)
  3395. if err != nil {
  3396. f.err = err
  3397. return
  3398. }
  3399. return f.parsepngstream(pngBuf, false)
  3400. }
  3401. // newobj begins a new object
  3402. func (f *Fpdf) newobj() {
  3403. // dbg("newobj")
  3404. f.n++
  3405. for j := len(f.offsets); j <= f.n; j++ {
  3406. f.offsets = append(f.offsets, 0)
  3407. }
  3408. f.offsets[f.n] = f.buffer.Len()
  3409. f.outf("%d 0 obj", f.n)
  3410. }
  3411. func (f *Fpdf) putstream(b []byte) {
  3412. // dbg("putstream")
  3413. if f.protect.encrypted {
  3414. f.protect.rc4(uint32(f.n), &b)
  3415. }
  3416. f.out("stream")
  3417. f.out(string(b))
  3418. f.out("endstream")
  3419. }
  3420. // out; Add a line to the document
  3421. func (f *Fpdf) out(s string) {
  3422. if f.state == 2 {
  3423. f.pages[f.page].WriteString(s)
  3424. f.pages[f.page].WriteString("\n")
  3425. } else {
  3426. f.buffer.WriteString(s)
  3427. f.buffer.WriteString("\n")
  3428. }
  3429. }
  3430. // outbuf adds a buffered line to the document
  3431. func (f *Fpdf) outbuf(r io.Reader) {
  3432. if f.state == 2 {
  3433. f.pages[f.page].ReadFrom(r)
  3434. f.pages[f.page].WriteString("\n")
  3435. } else {
  3436. f.buffer.ReadFrom(r)
  3437. f.buffer.WriteString("\n")
  3438. }
  3439. }
  3440. // RawWriteStr writes a string directly to the PDF generation buffer. This is a
  3441. // low-level function that is not required for normal PDF construction. An
  3442. // understanding of the PDF specification is needed to use this method
  3443. // correctly.
  3444. func (f *Fpdf) RawWriteStr(str string) {
  3445. f.out(str)
  3446. }
  3447. // RawWriteBuf writes the contents of the specified buffer directly to the PDF
  3448. // generation buffer. This is a low-level function that is not required for
  3449. // normal PDF construction. An understanding of the PDF specification is needed
  3450. // to use this method correctly.
  3451. func (f *Fpdf) RawWriteBuf(r io.Reader) {
  3452. f.outbuf(r)
  3453. }
  3454. // outf adds a formatted line to the document
  3455. func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
  3456. f.out(sprintf(fmtStr, args...))
  3457. }
  3458. // SetDefaultCatalogSort sets the default value of the catalog sort flag that
  3459. // will be used when initializing a new Fpdf instance. See SetCatalogSort() for
  3460. // more details.
  3461. func SetDefaultCatalogSort(flag bool) {
  3462. gl.catalogSort = flag
  3463. }
  3464. // SetCatalogSort sets a flag that will be used, if true, to consistently order
  3465. // the document's internal resource catalogs. This method is typically only
  3466. // used for test purposes to facilitate PDF comparison.
  3467. func (f *Fpdf) SetCatalogSort(flag bool) {
  3468. f.catalogSort = flag
  3469. }
  3470. // SetDefaultCreationDate sets the default value of the document creation date
  3471. // that will be used when initializing a new Fpdf instance. See
  3472. // SetCreationDate() for more details.
  3473. func SetDefaultCreationDate(tm time.Time) {
  3474. gl.creationDate = tm
  3475. }
  3476. // SetCreationDate fixes the document's internal CreationDate value. By
  3477. // default, the time when the document is generated is used for this value.
  3478. // This method is typically only used for testing purposes to facilitate PDF
  3479. // comparison. Specify a zero-value time to revert to the default behavior.
  3480. func (f *Fpdf) SetCreationDate(tm time.Time) {
  3481. f.creationDate = tm
  3482. }
  3483. // SetJavascript adds Adobe JavaScript to the document.
  3484. func (f *Fpdf) SetJavascript(script string) {
  3485. f.javascript = &script
  3486. }
  3487. // RegisterAlias adds an (alias, replacement) pair to the document so we can
  3488. // replace all occurrences of that alias after writing but before the
  3489. // document is closed.
  3490. func (f *Fpdf) RegisterAlias(alias, replacement string) {
  3491. f.aliasMap[alias] = replacement
  3492. }
  3493. func (f *Fpdf) replaceAliases() {
  3494. for alias, replacement := range f.aliasMap {
  3495. for n := 1; n <= f.page; n++ {
  3496. s := f.pages[n].String()
  3497. if strings.Contains(s, alias) {
  3498. s = strings.Replace(s, alias, replacement, -1)
  3499. f.pages[n].Truncate(0)
  3500. f.pages[n].WriteString(s)
  3501. }
  3502. }
  3503. }
  3504. }
  3505. func (f *Fpdf) putpages() {
  3506. var wPt, hPt float64
  3507. var pageSize SizeType
  3508. // var linkList []linkType
  3509. var ok bool
  3510. nb := f.page
  3511. if len(f.aliasNbPagesStr) > 0 {
  3512. // Replace number of pages
  3513. alias := utf8toutf16(f.aliasNbPagesStr, false)
  3514. r := utf8toutf16(sprintf("%d", nb), false)
  3515. f.RegisterAlias(alias, r)
  3516. f.RegisterAlias(f.aliasNbPagesStr, sprintf("%d", nb))
  3517. }
  3518. f.replaceAliases()
  3519. if f.defOrientation == "P" {
  3520. wPt = f.defPageSize.Wd * f.k
  3521. hPt = f.defPageSize.Ht * f.k
  3522. } else {
  3523. wPt = f.defPageSize.Ht * f.k
  3524. hPt = f.defPageSize.Wd * f.k
  3525. }
  3526. for n := 1; n <= nb; n++ {
  3527. // Page
  3528. f.newobj()
  3529. f.out("<</Type /Page")
  3530. f.out("/Parent 1 0 R")
  3531. pageSize, ok = f.pageSizes[n]
  3532. if ok {
  3533. f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.Wd, pageSize.Ht)
  3534. }
  3535. for t, pb := range f.pageBoxes[n] {
  3536. f.outf("/%s [%.2f %.2f %.2f %.2f]", t, pb.X, pb.Y, pb.Wd, pb.Ht)
  3537. }
  3538. f.out("/Resources 2 0 R")
  3539. // Links
  3540. if len(f.pageLinks[n]) > 0 {
  3541. var annots fmtBuffer
  3542. annots.printf("/Annots [")
  3543. for _, pl := range f.pageLinks[n] {
  3544. annots.printf("<</Type /Annot /Subtype /Link /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0] ",
  3545. pl.x, pl.y, pl.x+pl.wd, pl.y-pl.ht)
  3546. if pl.link == 0 {
  3547. annots.printf("/A <</S /URI /URI %s>>>>", f.textstring(pl.linkStr))
  3548. } else {
  3549. l := f.links[pl.link]
  3550. var sz SizeType
  3551. var h float64
  3552. sz, ok = f.pageSizes[l.page]
  3553. if ok {
  3554. h = sz.Ht
  3555. } else {
  3556. h = hPt
  3557. }
  3558. // dbg("h [%.2f], l.y [%.2f] f.k [%.2f]\n", h, l.y, f.k)
  3559. annots.printf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k)
  3560. }
  3561. }
  3562. annots.printf("]")
  3563. f.out(annots.String())
  3564. }
  3565. if f.pdfVersion > "1.3" {
  3566. f.out("/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>")
  3567. }
  3568. f.outf("/Contents %d 0 R>>", f.n+1)
  3569. f.out("endobj")
  3570. // Page content
  3571. f.newobj()
  3572. if f.compress {
  3573. data := sliceCompress(f.pages[n].Bytes())
  3574. f.outf("<</Filter /FlateDecode /Length %d>>", len(data))
  3575. f.putstream(data)
  3576. } else {
  3577. f.outf("<</Length %d>>", f.pages[n].Len())
  3578. f.putstream(f.pages[n].Bytes())
  3579. }
  3580. f.out("endobj")
  3581. }
  3582. // Pages root
  3583. f.offsets[1] = f.buffer.Len()
  3584. f.out("1 0 obj")
  3585. f.out("<</Type /Pages")
  3586. var kids fmtBuffer
  3587. kids.printf("/Kids [")
  3588. for i := 0; i < nb; i++ {
  3589. kids.printf("%d 0 R ", 3+2*i)
  3590. }
  3591. kids.printf("]")
  3592. f.out(kids.String())
  3593. f.outf("/Count %d", nb)
  3594. f.outf("/MediaBox [0 0 %.2f %.2f]", wPt, hPt)
  3595. f.out(">>")
  3596. f.out("endobj")
  3597. }
  3598. func (f *Fpdf) putfonts() {
  3599. if f.err != nil {
  3600. return
  3601. }
  3602. nf := f.n
  3603. for _, diff := range f.diffs {
  3604. // Encodings
  3605. f.newobj()
  3606. f.outf("<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>", diff)
  3607. f.out("endobj")
  3608. }
  3609. {
  3610. var fileList []string
  3611. var info fontFileType
  3612. var file string
  3613. for file = range f.fontFiles {
  3614. fileList = append(fileList, file)
  3615. }
  3616. if f.catalogSort {
  3617. sort.SliceStable(fileList, func(i, j int) bool { return fileList[i] < fileList[j] })
  3618. }
  3619. for _, file = range fileList {
  3620. info = f.fontFiles[file]
  3621. if info.fontType != "UTF8" {
  3622. f.newobj()
  3623. info.n = f.n
  3624. f.fontFiles[file] = info
  3625. var font []byte
  3626. if info.embedded {
  3627. font = info.content
  3628. } else {
  3629. var err error
  3630. font, err = f.loadFontFile(file)
  3631. if err != nil {
  3632. f.err = err
  3633. return
  3634. }
  3635. }
  3636. compressed := file[len(file)-2:] == ".z"
  3637. if !compressed && info.length2 > 0 {
  3638. buf := font[6:info.length1]
  3639. buf = append(buf, font[6+info.length1+6:info.length2]...)
  3640. font = buf
  3641. }
  3642. f.outf("<</Length %d", len(font))
  3643. if compressed {
  3644. f.out("/Filter /FlateDecode")
  3645. }
  3646. f.outf("/Length1 %d", info.length1)
  3647. if info.length2 > 0 {
  3648. f.outf("/Length2 %d /Length3 0", info.length2)
  3649. }
  3650. f.out(">>")
  3651. f.putstream(font)
  3652. f.out("endobj")
  3653. }
  3654. }
  3655. }
  3656. {
  3657. var keyList []string
  3658. var font fontDefType
  3659. var key string
  3660. for key = range f.fonts {
  3661. keyList = append(keyList, key)
  3662. }
  3663. if f.catalogSort {
  3664. sort.SliceStable(keyList, func(i, j int) bool { return keyList[i] < keyList[j] })
  3665. }
  3666. for _, key = range keyList {
  3667. font = f.fonts[key]
  3668. // Font objects
  3669. font.N = f.n + 1
  3670. f.fonts[key] = font
  3671. tp := font.Tp
  3672. name := font.Name
  3673. switch tp {
  3674. case "Core":
  3675. // Core font
  3676. f.newobj()
  3677. f.out("<</Type /Font")
  3678. f.outf("/BaseFont /%s", name)
  3679. f.out("/Subtype /Type1")
  3680. if name != "Symbol" && name != "ZapfDingbats" {
  3681. f.out("/Encoding /WinAnsiEncoding")
  3682. }
  3683. f.out(">>")
  3684. f.out("endobj")
  3685. case "Type1":
  3686. fallthrough
  3687. case "TrueType":
  3688. // Additional Type1 or TrueType/OpenType font
  3689. f.newobj()
  3690. f.out("<</Type /Font")
  3691. f.outf("/BaseFont /%s", name)
  3692. f.outf("/Subtype /%s", tp)
  3693. f.out("/FirstChar 32 /LastChar 255")
  3694. f.outf("/Widths %d 0 R", f.n+1)
  3695. f.outf("/FontDescriptor %d 0 R", f.n+2)
  3696. if font.DiffN > 0 {
  3697. f.outf("/Encoding %d 0 R", nf+font.DiffN)
  3698. } else {
  3699. f.out("/Encoding /WinAnsiEncoding")
  3700. }
  3701. f.out(">>")
  3702. f.out("endobj")
  3703. // Widths
  3704. f.newobj()
  3705. var s fmtBuffer
  3706. s.WriteString("[")
  3707. for j := 32; j < 256; j++ {
  3708. s.printf("%d ", font.Cw[j])
  3709. }
  3710. s.WriteString("]")
  3711. f.out(s.String())
  3712. f.out("endobj")
  3713. // Descriptor
  3714. f.newobj()
  3715. s.Truncate(0)
  3716. s.printf("<</Type /FontDescriptor /FontName /%s ", name)
  3717. s.printf("/Ascent %d ", font.Desc.Ascent)
  3718. s.printf("/Descent %d ", font.Desc.Descent)
  3719. s.printf("/CapHeight %d ", font.Desc.CapHeight)
  3720. s.printf("/Flags %d ", font.Desc.Flags)
  3721. s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
  3722. font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
  3723. s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
  3724. s.printf("/StemV %d ", font.Desc.StemV)
  3725. s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
  3726. var suffix string
  3727. if tp != "Type1" {
  3728. suffix = "2"
  3729. }
  3730. s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
  3731. f.out(s.String())
  3732. f.out("endobj")
  3733. case "UTF8":
  3734. fontName := "utf8" + font.Name
  3735. usedRunes := font.usedRunes
  3736. delete(usedRunes, 0)
  3737. utf8FontStream := font.utf8File.GenerateСutFont(usedRunes)
  3738. utf8FontSize := len(utf8FontStream)
  3739. compressedFontStream := sliceCompress(utf8FontStream)
  3740. CodeSignDictionary := font.utf8File.CodeSymbolDictionary
  3741. delete(CodeSignDictionary, 0)
  3742. f.newobj()
  3743. f.out(fmt.Sprintf("<</Type /Font\n/Subtype /Type0\n/BaseFont /%s\n/Encoding /Identity-H\n/DescendantFonts [%d 0 R]\n/ToUnicode %d 0 R>>\n"+"endobj", fontName, f.n+1, f.n+2))
  3744. f.newobj()
  3745. f.out("<</Type /Font\n/Subtype /CIDFontType2\n/BaseFont /" + fontName + "\n" +
  3746. "/CIDSystemInfo " + strconv.Itoa(f.n+2) + " 0 R\n/FontDescriptor " + strconv.Itoa(f.n+3) + " 0 R")
  3747. if font.Desc.MissingWidth != 0 {
  3748. f.out("/DW " + strconv.Itoa(font.Desc.MissingWidth) + "")
  3749. }
  3750. f.generateCIDFontMap(&font, font.utf8File.LastRune)
  3751. f.out("/CIDToGIDMap " + strconv.Itoa(f.n+4) + " 0 R>>")
  3752. f.out("endobj")
  3753. f.newobj()
  3754. f.out("<</Length " + strconv.Itoa(len(toUnicode)) + ">>")
  3755. f.putstream([]byte(toUnicode))
  3756. f.out("endobj")
  3757. // CIDInfo
  3758. f.newobj()
  3759. f.out("<</Registry (Adobe)\n/Ordering (UCS)\n/Supplement 0>>")
  3760. f.out("endobj")
  3761. // Font descriptor
  3762. f.newobj()
  3763. var s fmtBuffer
  3764. s.printf("<</Type /FontDescriptor /FontName /%s\n /Ascent %d", fontName, font.Desc.Ascent)
  3765. s.printf(" /Descent %d", font.Desc.Descent)
  3766. s.printf(" /CapHeight %d", font.Desc.CapHeight)
  3767. v := font.Desc.Flags
  3768. v = v | 4
  3769. v = v &^ 32
  3770. s.printf(" /Flags %d", v)
  3771. s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
  3772. font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
  3773. s.printf(" /ItalicAngle %d", font.Desc.ItalicAngle)
  3774. s.printf(" /StemV %d", font.Desc.StemV)
  3775. s.printf(" /MissingWidth %d", font.Desc.MissingWidth)
  3776. s.printf("/FontFile2 %d 0 R", f.n+2)
  3777. s.printf(">>")
  3778. f.out(s.String())
  3779. f.out("endobj")
  3780. // Embed CIDToGIDMap
  3781. cidToGidMap := make([]byte, 256*256*2)
  3782. for cc, glyph := range CodeSignDictionary {
  3783. cidToGidMap[cc*2] = byte(glyph >> 8)
  3784. cidToGidMap[cc*2+1] = byte(glyph & 0xFF)
  3785. }
  3786. cidToGidMap = sliceCompress(cidToGidMap)
  3787. f.newobj()
  3788. f.out("<</Length " + strconv.Itoa(len(cidToGidMap)) + "/Filter /FlateDecode>>")
  3789. f.putstream(cidToGidMap)
  3790. f.out("endobj")
  3791. //Font file
  3792. f.newobj()
  3793. f.out("<</Length " + strconv.Itoa(len(compressedFontStream)))
  3794. f.out("/Filter /FlateDecode")
  3795. f.out("/Length1 " + strconv.Itoa(utf8FontSize))
  3796. f.out(">>")
  3797. f.putstream(compressedFontStream)
  3798. f.out("endobj")
  3799. default:
  3800. f.err = fmt.Errorf("unsupported font type: %s", tp)
  3801. return
  3802. }
  3803. }
  3804. }
  3805. return
  3806. }
  3807. func (f *Fpdf) generateCIDFontMap(font *fontDefType, LastRune int) {
  3808. rangeID := 0
  3809. cidArray := make(map[int]*untypedKeyMap)
  3810. cidArrayKeys := make([]int, 0)
  3811. prevCid := -2
  3812. prevWidth := -1
  3813. interval := false
  3814. startCid := 1
  3815. cwLen := LastRune + 1
  3816. // for each character
  3817. for cid := startCid; cid < cwLen; cid++ {
  3818. if font.Cw[cid] == 0x00 {
  3819. continue
  3820. }
  3821. width := font.Cw[cid]
  3822. if width == 65535 {
  3823. width = 0
  3824. }
  3825. if numb, OK := font.usedRunes[cid]; cid > 255 && (!OK || numb == 0) {
  3826. continue
  3827. }
  3828. if cid == prevCid+1 {
  3829. if width == prevWidth {
  3830. if width == cidArray[rangeID].get(0) {
  3831. cidArray[rangeID].put(nil, width)
  3832. } else {
  3833. cidArray[rangeID].pop()
  3834. rangeID = prevCid
  3835. r := untypedKeyMap{
  3836. valueSet: make([]int, 0),
  3837. keySet: make([]interface{}, 0),
  3838. }
  3839. cidArray[rangeID] = &r
  3840. cidArrayKeys = append(cidArrayKeys, rangeID)
  3841. cidArray[rangeID].put(nil, prevWidth)
  3842. cidArray[rangeID].put(nil, width)
  3843. }
  3844. interval = true
  3845. cidArray[rangeID].put("interval", 1)
  3846. } else {
  3847. if interval {
  3848. // new range
  3849. rangeID = cid
  3850. r := untypedKeyMap{
  3851. valueSet: make([]int, 0),
  3852. keySet: make([]interface{}, 0),
  3853. }
  3854. cidArray[rangeID] = &r
  3855. cidArrayKeys = append(cidArrayKeys, rangeID)
  3856. cidArray[rangeID].put(nil, width)
  3857. } else {
  3858. cidArray[rangeID].put(nil, width)
  3859. }
  3860. interval = false
  3861. }
  3862. } else {
  3863. rangeID = cid
  3864. r := untypedKeyMap{
  3865. valueSet: make([]int, 0),
  3866. keySet: make([]interface{}, 0),
  3867. }
  3868. cidArray[rangeID] = &r
  3869. cidArrayKeys = append(cidArrayKeys, rangeID)
  3870. cidArray[rangeID].put(nil, width)
  3871. interval = false
  3872. }
  3873. prevCid = cid
  3874. prevWidth = width
  3875. }
  3876. previousKey := -1
  3877. nextKey := -1
  3878. isInterval := false
  3879. for g := 0; g < len(cidArrayKeys); {
  3880. key := cidArrayKeys[g]
  3881. ws := *cidArray[key]
  3882. cws := len(ws.keySet)
  3883. if (key == nextKey) && (!isInterval) && (ws.getIndex("interval") < 0 || cws < 4) {
  3884. if cidArray[key].getIndex("interval") >= 0 {
  3885. cidArray[key].delete("interval")
  3886. }
  3887. cidArray[previousKey] = arrayMerge(cidArray[previousKey], cidArray[key])
  3888. cidArrayKeys = remove(cidArrayKeys, key)
  3889. } else {
  3890. g++
  3891. previousKey = key
  3892. }
  3893. nextKey = key + cws
  3894. // ui := ws.getIndex("interval")
  3895. // ui = ui + 1
  3896. if ws.getIndex("interval") >= 0 {
  3897. if cws > 3 {
  3898. isInterval = true
  3899. } else {
  3900. isInterval = false
  3901. }
  3902. cidArray[key].delete("interval")
  3903. nextKey--
  3904. } else {
  3905. isInterval = false
  3906. }
  3907. }
  3908. var w fmtBuffer
  3909. for _, k := range cidArrayKeys {
  3910. ws := cidArray[k]
  3911. if len(arrayCountValues(ws.valueSet)) == 1 {
  3912. w.printf(" %d %d %d", k, k+len(ws.valueSet)-1, ws.get(0))
  3913. } else {
  3914. w.printf(" %d [ %s ]\n", k, implode(" ", ws.valueSet))
  3915. }
  3916. }
  3917. f.out("/W [" + w.String() + " ]")
  3918. }
  3919. func implode(sep string, arr []int) string {
  3920. var s fmtBuffer
  3921. for i := 0; i < len(arr)-1; i++ {
  3922. s.printf("%v", arr[i])
  3923. s.printf(sep)
  3924. }
  3925. if len(arr) > 0 {
  3926. s.printf("%v", arr[len(arr)-1])
  3927. }
  3928. return s.String()
  3929. }
  3930. func arrayCountValues(mp []int) map[int]int {
  3931. answer := make(map[int]int)
  3932. for _, v := range mp {
  3933. answer[v] = answer[v] + 1
  3934. }
  3935. return answer
  3936. }
  3937. func (f *Fpdf) loadFontFile(name string) ([]byte, error) {
  3938. if f.fontLoader != nil {
  3939. reader, err := f.fontLoader.Open(name)
  3940. if err == nil {
  3941. data, err := ioutil.ReadAll(reader)
  3942. if closer, ok := reader.(io.Closer); ok {
  3943. closer.Close()
  3944. }
  3945. return data, err
  3946. }
  3947. }
  3948. return ioutil.ReadFile(path.Join(f.fontpath, name))
  3949. }
  3950. func (f *Fpdf) putimages() {
  3951. var keyList []string
  3952. var key string
  3953. for key = range f.images {
  3954. keyList = append(keyList, key)
  3955. }
  3956. if f.catalogSort {
  3957. sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].w < f.images[keyList[j]].w })
  3958. }
  3959. for _, key = range keyList {
  3960. f.putimage(f.images[key])
  3961. }
  3962. }
  3963. func (f *Fpdf) putimage(info *ImageInfoType) {
  3964. f.newobj()
  3965. info.n = f.n
  3966. f.out("<</Type /XObject")
  3967. f.out("/Subtype /Image")
  3968. f.outf("/Width %d", int(info.w))
  3969. f.outf("/Height %d", int(info.h))
  3970. if info.cs == "Indexed" {
  3971. f.outf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]", len(info.pal)/3-1, f.n+1)
  3972. } else {
  3973. f.outf("/ColorSpace /%s", info.cs)
  3974. if info.cs == "DeviceCMYK" {
  3975. f.out("/Decode [1 0 1 0 1 0 1 0]")
  3976. }
  3977. }
  3978. f.outf("/BitsPerComponent %d", info.bpc)
  3979. if len(info.f) > 0 {
  3980. f.outf("/Filter /%s", info.f)
  3981. }
  3982. if len(info.dp) > 0 {
  3983. f.outf("/DecodeParms <<%s>>", info.dp)
  3984. }
  3985. if len(info.trns) > 0 {
  3986. var trns fmtBuffer
  3987. for _, v := range info.trns {
  3988. trns.printf("%d %d ", v, v)
  3989. }
  3990. f.outf("/Mask [%s]", trns.String())
  3991. }
  3992. if info.smask != nil {
  3993. f.outf("/SMask %d 0 R", f.n+1)
  3994. }
  3995. f.outf("/Length %d>>", len(info.data))
  3996. f.putstream(info.data)
  3997. f.out("endobj")
  3998. // Soft mask
  3999. if len(info.smask) > 0 {
  4000. smask := &ImageInfoType{
  4001. w: info.w,
  4002. h: info.h,
  4003. cs: "DeviceGray",
  4004. bpc: 8,
  4005. f: info.f,
  4006. dp: sprintf("/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns %d", int(info.w)),
  4007. data: info.smask,
  4008. scale: f.k,
  4009. }
  4010. f.putimage(smask)
  4011. }
  4012. // Palette
  4013. if info.cs == "Indexed" {
  4014. f.newobj()
  4015. if f.compress {
  4016. pal := sliceCompress(info.pal)
  4017. f.outf("<</Filter /FlateDecode /Length %d>>", len(pal))
  4018. f.putstream(pal)
  4019. } else {
  4020. f.outf("<</Length %d>>", len(info.pal))
  4021. f.putstream(info.pal)
  4022. }
  4023. f.out("endobj")
  4024. }
  4025. }
  4026. func (f *Fpdf) putxobjectdict() {
  4027. {
  4028. var image *ImageInfoType
  4029. var key string
  4030. var keyList []string
  4031. for key = range f.images {
  4032. keyList = append(keyList, key)
  4033. }
  4034. if f.catalogSort {
  4035. sort.SliceStable(keyList, func(i, j int) bool { return f.images[keyList[i]].i < f.images[keyList[j]].i })
  4036. }
  4037. for _, key = range keyList {
  4038. image = f.images[key]
  4039. f.outf("/I%s %d 0 R", image.i, image.n)
  4040. }
  4041. }
  4042. {
  4043. var keyList []string
  4044. var key string
  4045. var tpl Template
  4046. keyList = templateKeyList(f.templates, f.catalogSort)
  4047. for _, key = range keyList {
  4048. tpl = f.templates[key]
  4049. // for _, tpl := range f.templates {
  4050. id := tpl.ID()
  4051. if objID, ok := f.templateObjects[id]; ok {
  4052. f.outf("/TPL%s %d 0 R", id, objID)
  4053. }
  4054. }
  4055. }
  4056. {
  4057. for tplName, objID := range f.importedTplObjs {
  4058. // here replace obj id hash with n
  4059. f.outf("%s %d 0 R", tplName, f.importedTplIDs[objID])
  4060. }
  4061. }
  4062. }
  4063. func (f *Fpdf) putresourcedict() {
  4064. f.out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
  4065. f.out("/Font <<")
  4066. {
  4067. var keyList []string
  4068. var font fontDefType
  4069. var key string
  4070. for key = range f.fonts {
  4071. keyList = append(keyList, key)
  4072. }
  4073. if f.catalogSort {
  4074. sort.SliceStable(keyList, func(i, j int) bool { return f.fonts[keyList[i]].i < f.fonts[keyList[j]].i })
  4075. }
  4076. for _, key = range keyList {
  4077. font = f.fonts[key]
  4078. f.outf("/F%s %d 0 R", font.i, font.N)
  4079. }
  4080. }
  4081. f.out(">>")
  4082. f.out("/XObject <<")
  4083. f.putxobjectdict()
  4084. f.out(">>")
  4085. count := len(f.blendList)
  4086. if count > 1 {
  4087. f.out("/ExtGState <<")
  4088. for j := 1; j < count; j++ {
  4089. f.outf("/GS%d %d 0 R", j, f.blendList[j].objNum)
  4090. }
  4091. f.out(">>")
  4092. }
  4093. count = len(f.gradientList)
  4094. if count > 1 {
  4095. f.out("/Shading <<")
  4096. for j := 1; j < count; j++ {
  4097. f.outf("/Sh%d %d 0 R", j, f.gradientList[j].objNum)
  4098. }
  4099. f.out(">>")
  4100. }
  4101. // Layers
  4102. f.layerPutResourceDict()
  4103. f.spotColorPutResourceDict()
  4104. }
  4105. func (f *Fpdf) putBlendModes() {
  4106. count := len(f.blendList)
  4107. for j := 1; j < count; j++ {
  4108. bl := f.blendList[j]
  4109. f.newobj()
  4110. f.blendList[j].objNum = f.n
  4111. f.outf("<</Type /ExtGState /ca %s /CA %s /BM /%s>>",
  4112. bl.fillStr, bl.strokeStr, bl.modeStr)
  4113. f.out("endobj")
  4114. }
  4115. }
  4116. func (f *Fpdf) putGradients() {
  4117. count := len(f.gradientList)
  4118. for j := 1; j < count; j++ {
  4119. var f1 int
  4120. gr := f.gradientList[j]
  4121. if gr.tp == 2 || gr.tp == 3 {
  4122. f.newobj()
  4123. f.outf("<</FunctionType 2 /Domain [0.0 1.0] /C0 [%s] /C1 [%s] /N 1>>", gr.clr1Str, gr.clr2Str)
  4124. f.out("endobj")
  4125. f1 = f.n
  4126. }
  4127. f.newobj()
  4128. f.outf("<</ShadingType %d /ColorSpace /DeviceRGB", gr.tp)
  4129. if gr.tp == 2 {
  4130. f.outf("/Coords [%.5f %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>",
  4131. gr.x1, gr.y1, gr.x2, gr.y2, f1)
  4132. } else if gr.tp == 3 {
  4133. f.outf("/Coords [%.5f %.5f 0 %.5f %.5f %.5f] /Function %d 0 R /Extend [true true]>>",
  4134. gr.x1, gr.y1, gr.x2, gr.y2, gr.r, f1)
  4135. }
  4136. f.out("endobj")
  4137. f.gradientList[j].objNum = f.n
  4138. }
  4139. }
  4140. func (f *Fpdf) putjavascript() {
  4141. if f.javascript == nil {
  4142. return
  4143. }
  4144. f.newobj()
  4145. f.nJs = f.n
  4146. f.out("<<")
  4147. f.outf("/Names [(EmbeddedJS) %d 0 R]", f.n+1)
  4148. f.out(">>")
  4149. f.out("endobj")
  4150. f.newobj()
  4151. f.out("<<")
  4152. f.out("/S /JavaScript")
  4153. f.outf("/JS %s", f.textstring(*f.javascript))
  4154. f.out(">>")
  4155. f.out("endobj")
  4156. }
  4157. func (f *Fpdf) putresources() {
  4158. if f.err != nil {
  4159. return
  4160. }
  4161. f.layerPutLayers()
  4162. f.putBlendModes()
  4163. f.putGradients()
  4164. f.putSpotColors()
  4165. f.putfonts()
  4166. if f.err != nil {
  4167. return
  4168. }
  4169. f.putimages()
  4170. f.putTemplates()
  4171. f.putImportedTemplates() // gofpdi
  4172. // Resource dictionary
  4173. f.offsets[2] = f.buffer.Len()
  4174. f.out("2 0 obj")
  4175. f.out("<<")
  4176. f.putresourcedict()
  4177. f.out(">>")
  4178. f.out("endobj")
  4179. f.putjavascript()
  4180. if f.protect.encrypted {
  4181. f.newobj()
  4182. f.protect.objNum = f.n
  4183. f.out("<<")
  4184. f.out("/Filter /Standard")
  4185. f.out("/V 1")
  4186. f.out("/R 2")
  4187. f.outf("/O (%s)", f.escape(string(f.protect.oValue)))
  4188. f.outf("/U (%s)", f.escape(string(f.protect.uValue)))
  4189. f.outf("/P %d", f.protect.pValue)
  4190. f.out(">>")
  4191. f.out("endobj")
  4192. }
  4193. return
  4194. }
  4195. func (f *Fpdf) putinfo() {
  4196. var tm time.Time
  4197. if len(f.producer) > 0 {
  4198. f.outf("/Producer %s", f.textstring(f.producer))
  4199. }
  4200. if len(f.title) > 0 {
  4201. f.outf("/Title %s", f.textstring(f.title))
  4202. }
  4203. if len(f.subject) > 0 {
  4204. f.outf("/Subject %s", f.textstring(f.subject))
  4205. }
  4206. if len(f.author) > 0 {
  4207. f.outf("/Author %s", f.textstring(f.author))
  4208. }
  4209. if len(f.keywords) > 0 {
  4210. f.outf("/Keywords %s", f.textstring(f.keywords))
  4211. }
  4212. if len(f.creator) > 0 {
  4213. f.outf("/Creator %s", f.textstring(f.creator))
  4214. }
  4215. if f.creationDate.IsZero() {
  4216. tm = time.Now()
  4217. } else {
  4218. tm = f.creationDate
  4219. }
  4220. f.outf("/CreationDate %s", f.textstring("D:"+tm.Format("20060102150405")))
  4221. }
  4222. func (f *Fpdf) putcatalog() {
  4223. f.out("/Type /Catalog")
  4224. f.out("/Pages 1 0 R")
  4225. switch f.zoomMode {
  4226. case "fullpage":
  4227. f.out("/OpenAction [3 0 R /Fit]")
  4228. case "fullwidth":
  4229. f.out("/OpenAction [3 0 R /FitH null]")
  4230. case "real":
  4231. f.out("/OpenAction [3 0 R /XYZ null null 1]")
  4232. }
  4233. // } else if !is_string($this->zoomMode))
  4234. // $this->out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2f',$this->zoomMode/100).']');
  4235. switch f.layoutMode {
  4236. case "single", "SinglePage":
  4237. f.out("/PageLayout /SinglePage")
  4238. case "continuous", "OneColumn":
  4239. f.out("/PageLayout /OneColumn")
  4240. case "two", "TwoColumnLeft":
  4241. f.out("/PageLayout /TwoColumnLeft")
  4242. case "TwoColumnRight":
  4243. f.out("/PageLayout /TwoColumnRight")
  4244. case "TwoPageLeft", "TwoPageRight":
  4245. if f.pdfVersion < "1.5" {
  4246. f.pdfVersion = "1.5"
  4247. }
  4248. f.out("/PageLayout /" + f.layoutMode)
  4249. }
  4250. // Bookmarks
  4251. if len(f.outlines) > 0 {
  4252. f.outf("/Outlines %d 0 R", f.outlineRoot)
  4253. f.out("/PageMode /UseOutlines")
  4254. }
  4255. // Layers
  4256. f.layerPutCatalog()
  4257. // JavaScript
  4258. if f.javascript != nil {
  4259. f.outf("/Names <</JavaScript %d 0 R>>", f.nJs)
  4260. }
  4261. }
  4262. func (f *Fpdf) putheader() {
  4263. if len(f.blendMap) > 0 && f.pdfVersion < "1.4" {
  4264. f.pdfVersion = "1.4"
  4265. }
  4266. f.outf("%%PDF-%s", f.pdfVersion)
  4267. }
  4268. func (f *Fpdf) puttrailer() {
  4269. f.outf("/Size %d", f.n+1)
  4270. f.outf("/Root %d 0 R", f.n)
  4271. f.outf("/Info %d 0 R", f.n-1)
  4272. if f.protect.encrypted {
  4273. f.outf("/Encrypt %d 0 R", f.protect.objNum)
  4274. f.out("/ID [()()]")
  4275. }
  4276. }
  4277. func (f *Fpdf) putxmp() {
  4278. if len(f.xmp) == 0 {
  4279. return
  4280. }
  4281. f.newobj()
  4282. f.outf("<< /Type /Metadata /Subtype /XML /Length %d >>", len(f.xmp))
  4283. f.putstream(f.xmp)
  4284. f.out("endobj")
  4285. }
  4286. func (f *Fpdf) putbookmarks() {
  4287. nb := len(f.outlines)
  4288. if nb > 0 {
  4289. lru := make(map[int]int)
  4290. level := 0
  4291. for i, o := range f.outlines {
  4292. if o.level > 0 {
  4293. parent := lru[o.level-1]
  4294. f.outlines[i].parent = parent
  4295. f.outlines[parent].last = i
  4296. if o.level > level {
  4297. f.outlines[parent].first = i
  4298. }
  4299. } else {
  4300. f.outlines[i].parent = nb
  4301. }
  4302. if o.level <= level && i > 0 {
  4303. prev := lru[o.level]
  4304. f.outlines[prev].next = i
  4305. f.outlines[i].prev = prev
  4306. }
  4307. lru[o.level] = i
  4308. level = o.level
  4309. }
  4310. n := f.n + 1
  4311. for _, o := range f.outlines {
  4312. f.newobj()
  4313. f.outf("<</Title %s", f.textstring(o.text))
  4314. f.outf("/Parent %d 0 R", n+o.parent)
  4315. if o.prev != -1 {
  4316. f.outf("/Prev %d 0 R", n+o.prev)
  4317. }
  4318. if o.next != -1 {
  4319. f.outf("/Next %d 0 R", n+o.next)
  4320. }
  4321. if o.first != -1 {
  4322. f.outf("/First %d 0 R", n+o.first)
  4323. }
  4324. if o.last != -1 {
  4325. f.outf("/Last %d 0 R", n+o.last)
  4326. }
  4327. f.outf("/Dest [%d 0 R /XYZ 0 %.2f null]", 1+2*o.p, (f.h-o.y)*f.k)
  4328. f.out("/Count 0>>")
  4329. f.out("endobj")
  4330. }
  4331. f.newobj()
  4332. f.outlineRoot = f.n
  4333. f.outf("<</Type /Outlines /First %d 0 R", n)
  4334. f.outf("/Last %d 0 R>>", n+lru[0])
  4335. f.out("endobj")
  4336. }
  4337. }
  4338. func (f *Fpdf) enddoc() {
  4339. if f.err != nil {
  4340. return
  4341. }
  4342. f.layerEndDoc()
  4343. f.putheader()
  4344. f.putpages()
  4345. f.putresources()
  4346. if f.err != nil {
  4347. return
  4348. }
  4349. // Bookmarks
  4350. f.putbookmarks()
  4351. // Metadata
  4352. f.putxmp()
  4353. // Info
  4354. f.newobj()
  4355. f.out("<<")
  4356. f.putinfo()
  4357. f.out(">>")
  4358. f.out("endobj")
  4359. // Catalog
  4360. f.newobj()
  4361. f.out("<<")
  4362. f.putcatalog()
  4363. f.out(">>")
  4364. f.out("endobj")
  4365. // Cross-ref
  4366. o := f.buffer.Len()
  4367. f.out("xref")
  4368. f.outf("0 %d", f.n+1)
  4369. f.out("0000000000 65535 f ")
  4370. for j := 1; j <= f.n; j++ {
  4371. f.outf("%010d 00000 n ", f.offsets[j])
  4372. }
  4373. // Trailer
  4374. f.out("trailer")
  4375. f.out("<<")
  4376. f.puttrailer()
  4377. f.out(">>")
  4378. f.out("startxref")
  4379. f.outf("%d", o)
  4380. f.out("%%EOF")
  4381. f.state = 3
  4382. return
  4383. }
  4384. // Path Drawing
  4385. // MoveTo moves the stylus to (x, y) without drawing the path from the
  4386. // previous point. Paths must start with a MoveTo to set the original
  4387. // stylus location or the result is undefined.
  4388. //
  4389. // Create a "path" by moving a virtual stylus around the page (with
  4390. // MoveTo, LineTo, CurveTo, CurveBezierCubicTo, ArcTo & ClosePath)
  4391. // then draw it or fill it in (with DrawPath). The main advantage of
  4392. // using the path drawing routines rather than multiple Fpdf.Line is
  4393. // that PDF creates nice line joins at the angles, rather than just
  4394. // overlaying the lines.
  4395. func (f *Fpdf) MoveTo(x, y float64) {
  4396. f.point(x, y)
  4397. f.x, f.y = x, y
  4398. }
  4399. // LineTo creates a line from the current stylus location to (x, y), which
  4400. // becomes the new stylus location. Note that this only creates the line in
  4401. // the path; it does not actually draw the line on the page.
  4402. //
  4403. // The MoveTo() example demonstrates this method.
  4404. func (f *Fpdf) LineTo(x, y float64) {
  4405. f.outf("%.2f %.2f l", x*f.k, (f.h-y)*f.k)
  4406. f.x, f.y = x, y
  4407. }
  4408. // CurveTo creates a single-segment quadratic Bézier curve. The curve starts at
  4409. // the current stylus location and ends at the point (x, y). The control point
  4410. // (cx, cy) specifies the curvature. At the start point, the curve is tangent
  4411. // to the straight line between the current stylus location and the control
  4412. // point. At the end point, the curve is tangent to the straight line between
  4413. // the end point and the control point.
  4414. //
  4415. // The MoveTo() example demonstrates this method.
  4416. func (f *Fpdf) CurveTo(cx, cy, x, y float64) {
  4417. f.outf("%.5f %.5f %.5f %.5f v", cx*f.k, (f.h-cy)*f.k, x*f.k, (f.h-y)*f.k)
  4418. f.x, f.y = x, y
  4419. }
  4420. // CurveBezierCubicTo creates a single-segment cubic Bézier curve. The curve
  4421. // starts at the current stylus location and ends at the point (x, y). The
  4422. // control points (cx0, cy0) and (cx1, cy1) specify the curvature. At the
  4423. // current stylus, the curve is tangent to the straight line between the
  4424. // current stylus location and the control point (cx0, cy0). At the end point,
  4425. // the curve is tangent to the straight line between the end point and the
  4426. // control point (cx1, cy1).
  4427. //
  4428. // The MoveTo() example demonstrates this method.
  4429. func (f *Fpdf) CurveBezierCubicTo(cx0, cy0, cx1, cy1, x, y float64) {
  4430. f.curve(cx0, cy0, cx1, cy1, x, y)
  4431. f.x, f.y = x, y
  4432. }
  4433. // ClosePath creates a line from the current location to the last MoveTo point
  4434. // (if not the same) and mark the path as closed so the first and last lines
  4435. // join nicely.
  4436. //
  4437. // The MoveTo() example demonstrates this method.
  4438. func (f *Fpdf) ClosePath() {
  4439. f.outf("h")
  4440. }
  4441. // DrawPath actually draws the path on the page.
  4442. //
  4443. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  4444. // outlined and filled. An empty string will be replaced with "D".
  4445. // Path-painting operators as defined in the PDF specification are also
  4446. // allowed: "S" (Stroke the path), "s" (Close and stroke the path),
  4447. // "f" (fill the path, using the nonzero winding number), "f*"
  4448. // (Fill the path, using the even-odd rule), "B" (Fill and then stroke
  4449. // the path, using the nonzero winding number rule), "B*" (Fill and
  4450. // then stroke the path, using the even-odd rule), "b" (Close, fill,
  4451. // and then stroke the path, using the nonzero winding number rule) and
  4452. // "b*" (Close, fill, and then stroke the path, using the even-odd
  4453. // rule).
  4454. // Drawing uses the current draw color, line width, and cap style
  4455. // centered on the
  4456. // path. Filling uses the current fill color.
  4457. //
  4458. // The MoveTo() example demonstrates this method.
  4459. func (f *Fpdf) DrawPath(styleStr string) {
  4460. f.outf(fillDrawOp(styleStr))
  4461. }
  4462. // ArcTo draws an elliptical arc centered at point (x, y). rx and ry specify its
  4463. // horizontal and vertical radii. If the start of the arc is not at
  4464. // the current position, a connecting line will be drawn.
  4465. //
  4466. // degRotate specifies the angle that the arc will be rotated. degStart and
  4467. // degEnd specify the starting and ending angle of the arc. All angles are
  4468. // specified in degrees and measured counter-clockwise from the 3 o'clock
  4469. // position.
  4470. //
  4471. // styleStr can be "F" for filled, "D" for outlined only, or "DF" or "FD" for
  4472. // outlined and filled. An empty string will be replaced with "D". Drawing uses
  4473. // the current draw color, line width, and cap style centered on the arc's
  4474. // path. Filling uses the current fill color.
  4475. //
  4476. // The MoveTo() example demonstrates this method.
  4477. func (f *Fpdf) ArcTo(x, y, rx, ry, degRotate, degStart, degEnd float64) {
  4478. f.arc(x, y, rx, ry, degRotate, degStart, degEnd, "", true)
  4479. }
  4480. func (f *Fpdf) arc(x, y, rx, ry, degRotate, degStart, degEnd float64,
  4481. styleStr string, path bool) {
  4482. x *= f.k
  4483. y = (f.h - y) * f.k
  4484. rx *= f.k
  4485. ry *= f.k
  4486. segments := int(degEnd-degStart) / 60
  4487. if segments < 2 {
  4488. segments = 2
  4489. }
  4490. angleStart := degStart * math.Pi / 180
  4491. angleEnd := degEnd * math.Pi / 180
  4492. angleTotal := angleEnd - angleStart
  4493. dt := angleTotal / float64(segments)
  4494. dtm := dt / 3
  4495. if degRotate != 0 {
  4496. a := -degRotate * math.Pi / 180
  4497. f.outf("q %.5f %.5f %.5f %.5f %.5f %.5f cm",
  4498. math.Cos(a), -1*math.Sin(a),
  4499. math.Sin(a), math.Cos(a), x, y)
  4500. x = 0
  4501. y = 0
  4502. }
  4503. t := angleStart
  4504. a0 := x + rx*math.Cos(t)
  4505. b0 := y + ry*math.Sin(t)
  4506. c0 := -rx * math.Sin(t)
  4507. d0 := ry * math.Cos(t)
  4508. sx := a0 / f.k // start point of arc
  4509. sy := f.h - (b0 / f.k)
  4510. if path {
  4511. if f.x != sx || f.y != sy {
  4512. // Draw connecting line to start point
  4513. f.LineTo(sx, sy)
  4514. }
  4515. } else {
  4516. f.point(sx, sy)
  4517. }
  4518. for j := 1; j <= segments; j++ {
  4519. // Draw this bit of the total curve
  4520. t = (float64(j) * dt) + angleStart
  4521. a1 := x + rx*math.Cos(t)
  4522. b1 := y + ry*math.Sin(t)
  4523. c1 := -rx * math.Sin(t)
  4524. d1 := ry * math.Cos(t)
  4525. f.curve((a0+(c0*dtm))/f.k,
  4526. f.h-((b0+(d0*dtm))/f.k),
  4527. (a1-(c1*dtm))/f.k,
  4528. f.h-((b1-(d1*dtm))/f.k),
  4529. a1/f.k,
  4530. f.h-(b1/f.k))
  4531. a0 = a1
  4532. b0 = b1
  4533. c0 = c1
  4534. d0 = d1
  4535. if path {
  4536. f.x = a1 / f.k
  4537. f.y = f.h - (b1 / f.k)
  4538. }
  4539. }
  4540. if !path {
  4541. f.out(fillDrawOp(styleStr))
  4542. }
  4543. if degRotate != 0 {
  4544. f.out("Q")
  4545. }
  4546. }