| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910 |
- // Copyright (C) 2004-2025 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "pdf-annot-imp.h"
- #include "mupdf/ucdn.h"
- #include <float.h>
- #include <limits.h>
- #include <string.h>
- #include <time.h>
- #include <stdio.h>
- #include "annotation-icons.h"
- /* #define PDF_DEBUG_APPEARANCE_SYNTHESIS */
- #define REPLACEMENT 0xB7
- #define CIRCLE_MAGIC 0.551915f
- static fz_point rect_center(const fz_rect rect)
- {
- fz_point c;
- c.x = (rect.x0 + rect.x1) / 2.0f;
- c.y = (rect.y0 + rect.y1) / 2.0f;
- return c;
- }
- static fz_matrix center_rect_within_rect(const fz_rect tofit, const fz_rect within)
- {
- float xscale = (within.x1 - within.x0) / (tofit.x1 - tofit.x0);
- float yscale = (within.y1 - within.y0) / (tofit.y1 - tofit.y0);
- float scale = fz_min(xscale, yscale);
- fz_point tofit_center;
- fz_point within_center;
- within_center = rect_center(within);
- tofit_center = rect_center(tofit);
- /* Translate "tofit" to be centered on the origin
- * Scale "tofit" to a size that fits within "within"
- * Translate "tofit" to "within's" center
- * Do all the above in reverse order so that we can use the fz_pre_xx functions */
- return fz_pre_translate(fz_pre_scale(fz_translate(within_center.x, within_center.y), scale, -scale), -tofit_center.x, -tofit_center.y);
- }
- static void
- draw_circle(fz_context *ctx, fz_buffer *buf, float rx, float ry, float cx, float cy)
- {
- float mx = rx * CIRCLE_MAGIC;
- float my = ry * CIRCLE_MAGIC;
- fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry);
- }
- static void
- draw_circle_in_box(fz_context *ctx, fz_buffer *buf, float lw, float x0, float y0, float x1, float y1)
- {
- float rx = (x1 - x0) / 2 - lw/2;
- float ry = (y1 - y0) / 2 - lw/2;
- float cx = x0 + lw/2 + rx;
- float cy = y0 + lw/2 + ry;
- draw_circle(ctx, buf, rx, ry, cx, cy);
- }
- static void
- draw_arc_seg(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move)
- {
- float x1 = xc + r * cosf(th0);
- float y1 = yc + r * sinf(th0);
- float x4 = xc + r * cosf(th1);
- float y4 = yc + r * sinf(th1);
- float ax = x1 - xc;
- float ay = y1 - yc;
- float bx = x4 - xc;
- float by = y4 - yc;
- float q1 = ax * ax + ay * ay;
- float q2 = q1 + ax * bx + ay * by;
- float k2 = (4.0f/3.0f) * (sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx);
- float x2 = xc + ax - k2 * ay;
- float y2 = yc + ay + k2 * ax;
- float x3 = xc + bx + k2 * by;
- float y3 = yc + by - k2 * bx;
- if (move)
- fz_append_printf(ctx, buf, "%g %g m\n", x1, y1);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x2, y2, x3, y3, x4, y4);
- }
- static void
- draw_arc(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move)
- {
- float d = th0 - th1;
- if (d > FZ_PI / 4)
- {
- draw_arc(ctx, buf, r, xc, yc, th0, th0 - d / 2, move);
- draw_arc(ctx, buf, r, xc, yc, th0 - d / 2, th1, 0);
- }
- else
- {
- draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, move);
- }
- }
- static void
- draw_arc_tail(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int fill)
- {
- draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, 0);
- if (fill)
- draw_arc_seg(ctx, buf, r, xc, yc, th1, th0, 0);
- }
- static void
- pdf_write_opacity_blend_mode(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, int bm)
- {
- pdf_obj *res_egs, *res_egs_h;
- float opacity = pdf_annot_opacity(ctx, annot);
- if (bm == FZ_BLEND_NORMAL && opacity == 1)
- return;
- /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g /ca %g >> >> >> */
- if (!*res)
- *res = pdf_new_dict(ctx, annot->page->doc, 1);
- res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1);
- res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2);
- pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState));
- if (bm == FZ_BLEND_MULTIPLY)
- {
- pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply));
- }
- if (opacity < 1)
- {
- pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(CA), opacity);
- pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity);
- }
- fz_append_printf(ctx, buf, "/H gs\n");
- }
- static void
- pdf_write_opacity(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res)
- {
- pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_NORMAL);
- }
- static void
- pdf_write_dash_pattern(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res)
- {
- int count = pdf_annot_border_dash_count(ctx, annot);
- int i;
- if (count == 0)
- return;
- fz_append_printf(ctx, buf, "[");
- for (i = 0; i < count; ++i)
- {
- float length = pdf_annot_border_dash_item(ctx, annot, i);
- if (i == 0)
- fz_append_printf(ctx, buf, "%g", length);
- else
- fz_append_printf(ctx, buf, " %g", length);
- }
- fz_append_printf(ctx, buf, "]0 d\n");
- }
- static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float w = pdf_annot_border_width(ctx, annot);
- fz_append_printf(ctx, buf, "%g w\n", w);
- return w;
- }
- static int
- write_color(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke)
- {
- if (n == 4)
- fz_append_printf(ctx, buf, "%g %g %g %g %c\n", color[0], color[1], color[2], color[3], stroke ? 'K' : 'k');
- else if (n == 3)
- fz_append_printf(ctx, buf, "%g %g %g %s\n", color[0], color[1], color[2], stroke ? "RG" : "rg");
- else if (n == 1)
- fz_append_printf(ctx, buf, "%g %c\n", color[0], stroke ? 'G' : 'g');
- else
- return 0;
- return 1;
- }
- static void
- write_color0(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke)
- {
- if (n == 0)
- fz_append_printf(ctx, buf, "0 %c\n", stroke ? 'G' : 'g');
- else
- write_color(ctx, buf, n, color, stroke);
- }
- static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float color[4];
- int n;
- pdf_annot_color(ctx, annot, &n, color);
- return write_color(ctx, buf, n, color, 1);
- }
- static int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot)
- {
- float color[4], gray;
- int n;
- pdf_annot_color(ctx, annot, &n, color);
- switch (n)
- {
- default:
- gray = 1;
- break;
- case 1:
- gray = color[0];
- break;
- case 3:
- gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f;
- break;
- case 4:
- gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3];
- gray = 1 - fz_min(gray, 1);
- break;
- }
- return gray < 0.25f;
- }
- static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float color[4];
- int n;
- pdf_annot_color(ctx, annot, &n, color);
- return write_color(ctx, buf, n, color, 0);
- }
- static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float color[4];
- int n;
- pdf_annot_interior_color(ctx, annot, &n, color);
- return write_color(ctx, buf, n, color, 0);
- }
- static int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float color[4];
- int n;
- pdf_annot_MK_BG(ctx, annot, &n, color);
- return write_color(ctx, buf, n, color, 0);
- }
- static int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
- {
- float color[4];
- int n;
- pdf_annot_MK_BC(ctx, annot, &n, color);
- return write_color(ctx, buf, n, color, 1);
- }
- static void maybe_stroke_and_fill(fz_context *ctx, fz_buffer *buf, int sc, int ic)
- {
- if (sc)
- fz_append_string(ctx, buf, ic ? "b\n" : "S\n");
- else
- fz_append_string(ctx, buf, ic ? "f\n" : "n\n");
- }
- static void maybe_stroke(fz_context *ctx, fz_buffer *buf, int sc)
- {
- fz_append_string(ctx, buf, sc ? "S\n" : "n\n");
- }
- static fz_point rotate_vector(float angle, float x, float y)
- {
- float ca = cosf(angle);
- float sa = sinf(angle);
- return fz_make_point(x*ca - y*sa, x*sa + y*ca);
- }
- static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w, int close)
- {
- float r = fz_max(1, w);
- float angle = atan2f(dy, dx);
- fz_point v, a, b;
- v = rotate_vector(angle, 8.8f*r, 4.5f*r);
- a = fz_make_point(x + v.x, y + v.y);
- v = rotate_vector(angle, 8.8f*r, -4.5f*r);
- b = fz_make_point(x + v.x, y + v.y);
- *rect = fz_include_point_in_rect(*rect, a);
- *rect = fz_include_point_in_rect(*rect, b);
- *rect = fz_expand_rect(*rect, w);
- fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
- fz_append_printf(ctx, buf, "%g %g l\n", x, y);
- fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
- if (close)
- fz_append_printf(ctx, buf, "h\n");
- }
- static void include_cap(fz_rect *rect, float x, float y, float r)
- {
- rect->x0 = fz_min(rect->x0, x-r);
- rect->y0 = fz_min(rect->y0, y-r);
- rect->x1 = fz_max(rect->x1, x+r);
- rect->y1 = fz_max(rect->y1, y+r);
- }
- static void
- pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect,
- float x, float y, float dx, float dy, float w,
- int sc, int ic, pdf_obj *cap)
- {
- float l = sqrtf(dx*dx + dy*dy);
- dx = dx / l;
- dy = dy / l;
- if (cap == PDF_NAME(Square))
- {
- float r = fz_max(3.0f, w * 3.0f);
- fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2);
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- include_cap(rect, x, y, r + w/2);
- }
- else if (cap == PDF_NAME(Circle))
- {
- float r = fz_max(3.0f, w * 3.0f);
- float m = r * CIRCLE_MAGIC;
- fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r);
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- include_cap(rect, x, y, r + w/2);
- }
- else if (cap == PDF_NAME(Diamond))
- {
- float r = fz_max(3.0f, w * 3.0f);
- fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
- fz_append_printf(ctx, buf, "%g %g l\n", x+r, y);
- fz_append_printf(ctx, buf, "%g %g l\n", x, y-r);
- fz_append_printf(ctx, buf, "%g %g l\n", x-r, y);
- fz_append_printf(ctx, buf, "h\n");
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- include_cap(rect, x, y, r + w/sqrtf(2));
- }
- else if (cap == PDF_NAME(OpenArrow))
- {
- pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 0);
- maybe_stroke(ctx, buf, sc);
- }
- else if (cap == PDF_NAME(ClosedArrow))
- {
- pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 1);
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- }
- /* PDF 1.5 */
- else if (cap == PDF_NAME(Butt))
- {
- float r = fz_max(3, w * 3);
- fz_point a = { x-dy*r, y+dx*r };
- fz_point b = { x+dy*r, y-dx*r };
- fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
- fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
- maybe_stroke(ctx, buf, sc);
- *rect = fz_include_point_in_rect(*rect, a);
- *rect = fz_include_point_in_rect(*rect, a);
- *rect = fz_expand_rect(*rect, w);
- }
- else if (cap == PDF_NAME(ROpenArrow))
- {
- pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 0);
- maybe_stroke(ctx, buf, sc);
- }
- else if (cap == PDF_NAME(RClosedArrow))
- {
- pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 1);
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- }
- /* PDF 1.6 */
- else if (cap == PDF_NAME(Slash))
- {
- float r = fz_max(5, w * 5);
- float angle = atan2f(dy, dx) - (30 * FZ_PI / 180);
- fz_point a, b, v;
- v = rotate_vector(angle, 0, r);
- a = fz_make_point(x + v.x, y + v.y);
- v = rotate_vector(angle, 0, -r);
- b = fz_make_point(x + v.x, y + v.y);
- fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
- fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
- maybe_stroke(ctx, buf, sc);
- *rect = fz_include_point_in_rect(*rect, a);
- *rect = fz_include_point_in_rect(*rect, b);
- *rect = fz_expand_rect(*rect, w);
- }
- }
- static float pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b);
- static void
- pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- pdf_obj *line, *le;
- float w;
- fz_point a, b, aa, ab, ba, bb;
- float dx, dy, line_length, ll, lle, llo;
- int sc;
- int ic;
- // The start and end point of the actual line (a, b)
- // The start and end points of the leader line (aa, ab, ba, bb)
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- w = pdf_write_border_appearance(ctx, annot, buf);
- sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
- ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
- line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
- a.x = pdf_array_get_real(ctx, line, 0);
- a.y = pdf_array_get_real(ctx, line, 1);
- b.x = pdf_array_get_real(ctx, line, 2);
- b.y = pdf_array_get_real(ctx, line, 3);
- /* vector of line */
- dx = b.x - a.x;
- dy = b.y - a.y;
- line_length = hypotf(dx, dy);
- dx /= line_length;
- dy /= line_length;
- rect->x0 = fz_min(a.x, b.x);
- rect->y0 = fz_min(a.y, b.y);
- rect->x1 = fz_max(a.x, b.x);
- rect->y1 = fz_max(a.y, b.y);
- ll = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL));
- if (ll != 0)
- {
- lle = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE));
- llo = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO));
- if (ll < 0) {
- lle = -lle;
- llo = -llo;
- }
- aa = fz_make_point(a.x - dy * (llo), a.y + dx * (llo));
- ba = fz_make_point(b.x - dy * (llo), b.y + dx * (llo));
- ab = fz_make_point(a.x - dy * (llo + ll + lle), a.y + dx * (llo + ll + lle));
- bb = fz_make_point(b.x - dy * (llo + ll + lle), b.y + dx * (llo + ll + lle));
- a = fz_make_point(a.x - dy * (llo + ll), a.y + dx * (llo + ll));
- b = fz_make_point(b.x - dy * (llo + ll), b.y + dx * (llo + ll));
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", aa.x, aa.y, ab.x, ab.y);
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", ba.x, ba.y, bb.x, bb.y);
- *rect = fz_include_point_in_rect(*rect, a);
- *rect = fz_include_point_in_rect(*rect, b);
- *rect = fz_include_point_in_rect(*rect, ab);
- *rect = fz_include_point_in_rect(*rect, bb);
- }
- if (pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap)))
- {
- float gap = pdf_write_line_caption(ctx, annot, buf, rect, res, a, b);
- if (gap > 0)
- {
- fz_point m = fz_make_point((a.x + b.x) / 2, (a.y + b.y) / 2);
- fz_point ca = fz_make_point(m.x - dx * gap, m.y - dy * gap);
- fz_point cb = fz_make_point(m.x + dx * gap, m.y + dy * gap);
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, ca.x, ca.y);
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", cb.x, cb.y, b.x, b.y);
- }
- else
- {
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y);
- }
- }
- else
- {
- fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y);
- }
- maybe_stroke(ctx, buf, sc);
- le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
- if (pdf_array_len(ctx, le) == 2)
- {
- dx = b.x - a.x;
- dy = b.y - a.y;
- pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, w, sc, ic, pdf_array_get(ctx, le, 0));
- pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx, -dy, w, sc, ic, pdf_array_get(ctx, le, 1));
- }
- *rect = fz_expand_rect(*rect, fz_max(1, w));
- }
- /* The rect diff is NOT an fz_rect. It's differences between
- * 2 rects. We return it as a rect for convenience. */
- fz_rect
- pdf_annot_rect_diff(fz_context *ctx, pdf_annot *annot)
- {
- pdf_obj *rd_obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(RD));
- fz_rect rd;
- if (!pdf_is_array(ctx, rd_obj))
- return fz_make_rect(0, 0, 0, 0);
- rd.x0 = pdf_array_get_real(ctx, rd_obj, 0);
- rd.y0 = pdf_array_get_real(ctx, rd_obj, 1);
- rd.x1 = pdf_array_get_real(ctx, rd_obj, 2);
- rd.y1 = pdf_array_get_real(ctx, rd_obj, 3);
- return rd;
- }
- static float
- cloud_intensity(fz_context *ctx, pdf_annot *annot)
- {
- if (pdf_annot_border_effect(ctx, annot) == PDF_BORDER_EFFECT_CLOUDY)
- return pdf_annot_border_effect_intensity(ctx, annot);
- return 0;
- }
- struct cloud_list {
- // store first 2 and latest 3 points
- fz_point data[5];
- int len;
- int first;
- int fill;
- float spacing, radius, phase;
- };
- static float intersect_cloud(fz_point p0, fz_point p1, float r, int sel)
- {
- float dx = p1.x - p0.x;
- float dy = p1.y - p0.y;
- float d = sqrtf(dx * dx + dy * dy);
- float x2, y2, x3, y3;
- float a, h;
- if (d >= r + r) return 0;
- if (d <= 0) return 0;
- a = d / 2;
- h = sqrtf(r * r - a * a);
- x2 = (p0.x + p1.x) / 2;
- y2 = (p0.y + p1.y) / 2;
- x3 = x2 - h * (p1.y - p0.y) / d;
- y3 = y2 + h * (p1.x - p0.x) / d;
- if (sel == 0)
- return atan2(y3 - p1.y, x3 - p1.x);
- else
- return atan2(y3 - p0.y, x3 - p0.x);
- }
- static void start_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float border, float intensity, int fill)
- {
- // Constants measured from Acrobat Reader
- list->spacing = intensity * 6.666667f + border * 0.8333333f;
- list->radius = intensity * 4.0f + border * 0.5f;
- list->phase = 0;
- list->len = 0;
- list->first = 1;
- list->fill = fill;
- fz_append_string(ctx, buf, "2 j\n"); // bevel join
- }
- static void emit_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, fz_point a, fz_point b, fz_point c)
- {
- float th0 = intersect_cloud(a, b, list->radius, 0);
- float th1 = intersect_cloud(b, c, list->radius, 1);
- while (th1 > th0)
- th1 -= FZ_PI * 2;
- draw_arc(ctx, buf, list->radius, b.x, b.y, th0, th1, list->first || !list->fill);
- draw_arc_tail(ctx, buf, list->radius, b.x, b.y, th1, th1 - FZ_PI / 8, list->fill);
- list->first = 0;
- }
- static void add_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x, float y)
- {
- if (list->len < 5)
- {
- list->data[list->len].x = x;
- list->data[list->len].y = y;
- list->len++;
- }
- else
- {
- list->data[2] = list->data[3];
- list->data[3] = list->data[4];
- list->data[4].x = x;
- list->data[4].y = y;
- }
- if (list->len >= 3)
- {
- emit_cloud(ctx, list, buf, list->data[list->len-3], list->data[list->len-2], list->data[list->len-1]);
- }
- }
- static void add_cloud_line(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1)
- {
- float dx = x1 - x0;
- float dy = y1 - y0;
- float total = hypotf(dx, dy);
- float used = 0;
- float t;
- if (list->phase == 0)
- add_cloud(ctx, list, buf, x0, y0);
- while (total - used > list->spacing - list->phase)
- {
- used += list->spacing - list->phase;
- t = used / total;
- add_cloud(ctx, list, buf, x0 + dx * t, y0 + dy * t);
- list->phase = 0;
- }
- list->phase += total - used;
- }
- static void add_cloud_circle(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1)
- {
- float cx = (x0 + x1) / 2;
- float cy = (y0 + y1) / 2;
- float rx = (x1 - x0) / 2;
- float ry = (y1 - y0) / 2;
- float da = -FZ_PI * 2 / 32;
- int i;
- for (i = 1; i <= 32; ++i) {
- float ax = cx + cosf((i-1) * da) * rx;
- float ay = cy + sinf((i-1) * da) * ry;
- float bx = cx + cosf(i * da) * rx;
- float by = cy + sinf(i * da) * ry;
- add_cloud_line(ctx, list, buf, ax, ay, bx, by);
- }
- }
- static void end_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf)
- {
- // Join up with last and first circles.
- switch (list->len)
- {
- case 0:
- break;
- case 1:
- draw_circle(ctx, buf, list->radius, list->radius, list->data[0].x, list->data[0].y);
- break;
- case 2:
- emit_cloud(ctx, list, buf, list->data[0], list->data[1], list->data[0]);
- emit_cloud(ctx, list, buf, list->data[1], list->data[0], list->data[1]);
- break;
- case 3:
- emit_cloud(ctx, list, buf, list->data[1], list->data[2], list->data[0]);
- emit_cloud(ctx, list, buf, list->data[2], list->data[0], list->data[1]);
- break;
- case 4:
- emit_cloud(ctx, list, buf, list->data[2], list->data[3], list->data[0]);
- emit_cloud(ctx, list, buf, list->data[3], list->data[0], list->data[1]);
- break;
- case 5:
- emit_cloud(ctx, list, buf, list->data[3], list->data[4], list->data[0]);
- emit_cloud(ctx, list, buf, list->data[4], list->data[0], list->data[1]);
- break;
- }
- }
- static void
- pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- struct cloud_list cloud_list;
- fz_rect orect, rd;
- float x, y, w, h;
- float lw;
- int sc;
- int ic;
- float cloud;
- float exp;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- lw = pdf_write_border_appearance(ctx, annot, buf);
- sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
- ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
- orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
- rd = pdf_annot_rect_diff(ctx, annot);
- /* We have various rules that we need to follow here:
- * 1) No part of what we draw should extend outside of 'Rect'.
- * 2) The 'centre' of the border should be on 'Rect+RD'.
- * 3) RD and linewidth will therefore have problems if they are too large.
- * We do our best to cope with all of these.
- */
- exp = lw/2;
- if (rd.x0 < exp)
- rd.x0 = exp;
- if (rd.x1 < exp)
- rd.x1 = exp;
- if (rd.y0 < exp)
- rd.y0 = exp;
- if (rd.y1 < exp)
- rd.y1 = exp;
- x = orect.x0 + rd.x0;
- y = orect.y0 + rd.y0;
- w = orect.x1 - orect.x0 - rd.x0 - rd.x1;
- h = orect.y1 - orect.y0 - rd.y0 - rd.y1;
- if (w < 1) w = 1;
- if (h < 1) h = 1;
- cloud = cloud_intensity(ctx, annot);
- if (cloud > 0)
- {
- start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
- add_cloud_line(ctx, &cloud_list, buf, x, y, x, y+h);
- cloud_list.phase = 0;
- add_cloud_line(ctx, &cloud_list, buf, x, y+h, x+w, y+h);
- cloud_list.phase = 0;
- add_cloud_line(ctx, &cloud_list, buf, x+w, y+h, x+w, y);
- cloud_list.phase = 0;
- add_cloud_line(ctx, &cloud_list, buf, x+w, y, x, y);
- end_cloud(ctx, &cloud_list, buf);
- exp += cloud_list.radius;
- if (rd.x0 < exp)
- rd.x0 = exp;
- if (rd.x1 < exp)
- rd.x1 = exp;
- if (rd.y0 < exp)
- rd.y0 = exp;
- if (rd.y1 < exp)
- rd.y1 = exp;
- }
- else
- {
- fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h);
- }
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
- rect->x0 = x - rd.x0;
- rect->y0 = y - rd.y0;
- rect->x1 = x + w + rd.x1;
- rect->y1 = y + h + rd.y1;
- }
- static void
- pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- struct cloud_list cloud_list;
- fz_rect orect, rd;
- float x, y, w, h;
- float lw;
- int sc;
- int ic;
- float cloud;
- float exp;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- lw = pdf_write_border_appearance(ctx, annot, buf);
- sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
- ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
- orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
- rd = pdf_annot_rect_diff(ctx, annot);
- /* We have various rules that we need to follow here:
- * 1) No part of what we draw should extend outside of 'Rect'.
- * 2) The 'centre' of the border should be on 'Rect+RD'.
- * 3) RD and linewidth will therefore have problems if they are too large.
- * We do our best to cope with all of these.
- */
- exp = lw/2;
- if (rd.x0 < exp)
- rd.x0 = exp;
- if (rd.x1 < exp)
- rd.x1 = exp;
- if (rd.y0 < exp)
- rd.y0 = exp;
- if (rd.y1 < exp)
- rd.y1 = exp;
- x = orect.x0 + rd.x0;
- y = orect.y0 + rd.y0;
- w = orect.x1 - orect.x0 - rd.x0 - rd.x1;
- h = orect.y1 - orect.y0 - rd.y0 - rd.y1;
- if (w < 1) w = 1;
- if (h < 1) h = 1;
- cloud = cloud_intensity(ctx, annot);
- if (cloud > 0)
- {
- start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
- add_cloud_circle(ctx, &cloud_list, buf, x, y, x+w, y+h);
- end_cloud(ctx, &cloud_list, buf);
- exp += cloud_list.radius;
- if (rd.x0 < exp)
- rd.x0 = exp;
- if (rd.x1 < exp)
- rd.x1 = exp;
- if (rd.y0 < exp)
- rd.y0 = exp;
- if (rd.y1 < exp)
- rd.y1 = exp;
- }
- else
- {
- draw_circle(ctx, buf, w / 2, h / 2, x + w / 2, y + h / 2);
- }
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
- rect->x0 = x - rd.x0;
- rect->y0 = y - rd.x1;
- rect->x1 = x + w + rd.x1;
- rect->y1 = y + h + rd.y1;
- }
- /*
- * This doesn't quite match Acrobat, but I haven't really got a clue
- * what magic algorithm Acrobat is using. It'll match for all 'simple'
- * polygons, and makes a reasonable stab for complex ones.
- *
- * Find the y position of the centre of the polygon. Sum the
- * area * winding_number across that line. (Effectively an integration?)
- * Then pick cw or ccw according to what will be leave the largest
- * area correct.
- */
- static int
- polygon_winding(fz_context *ctx, pdf_obj *v, int n)
- {
- int i;
- float mid_y = 0;
- float min_x = 0;
- fz_point q, r;
- float area = 0;
- /* Find the centre for y, and min x. */
- for (i = 0; i < n; i++)
- {
- float x = pdf_array_get_real(ctx, v, 2*i);
- if (x < min_x || i == 0)
- min_x = x;
- mid_y += pdf_array_get_real(ctx, v, 2*i+1);
- }
- mid_y /= n;
- /* Now run through finding the weighted area across that middle line. */
- q.x = pdf_array_get_real(ctx, v, 0);
- q.y = pdf_array_get_real(ctx, v, 1);
- for (i = 1; i < n; i++)
- {
- r.x = pdf_array_get_real(ctx, v, 2*i);
- r.y = pdf_array_get_real(ctx, v, 2*i+1);
- if ((r.y > mid_y && mid_y > q.y) || (r.y < mid_y && mid_y < q.y))
- {
- int w = (r.y > mid_y) ? 1 : -1;
- float x = r.x + (q.x - r.x) * (r.y - mid_y) / (r.y - q.y);
- area += (x - min_x) * w;
- }
- q = r;
- }
- return area < 0;
- }
- static void
- pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, int close)
- {
- struct cloud_list cloud_list;
- float cloud = 0;
- pdf_obj *verts, *le;
- fz_point p, first = {0,0}, last = {0,0};
- int i, n;
- float lw;
- int sc, ic;
- int i0, i1, is;
- float exp = 0;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- lw = pdf_write_border_appearance(ctx, annot, buf);
- sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
- ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
- *rect = fz_empty_rect;
- if (close)
- cloud = cloud_intensity(ctx, annot);
- verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
- n = pdf_array_len(ctx, verts) / 2;
- if (n > 0)
- {
- if (polygon_winding(ctx, verts, n))
- i0 = 0, i1 = n, is = 1;
- else
- i0 = n-1, i1 = -1, is = -1;
- if (cloud > 0)
- {
- start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
- }
- for (i = i0; i != i1; i += is)
- {
- p.x = pdf_array_get_real(ctx, verts, i*2+0);
- p.y = pdf_array_get_real(ctx, verts, i*2+1);
- if (i == i0)
- {
- rect->x0 = rect->x1 = p.x;
- rect->y0 = rect->y1 = p.y;
- }
- else
- {
- *rect = fz_include_point_in_rect(*rect, p);
- }
- if (cloud > 0)
- {
- if (i == i0)
- first = p;
- else
- add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, p.x, p.y);
- last = p;
- }
- else
- {
- if (i == i0)
- fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y);
- else
- fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y);
- }
- }
- if (cloud > 0)
- {
- add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, first.x, first.y);
- end_cloud(ctx, &cloud_list, buf);
- }
- else
- {
- if (close)
- fz_append_string(ctx, buf, "h\n");
- }
- if (close)
- maybe_stroke_and_fill(ctx, buf, sc, ic);
- else
- maybe_stroke(ctx, buf, sc);
- exp = lw;
- if (cloud > 0)
- exp += cloud_list.radius;
- *rect = fz_expand_rect(*rect, exp);
- }
- le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
- if (!close && n >= 2 && pdf_array_len(ctx, le) == 2)
- {
- float dx, dy;
- fz_point a, b;
- a.x = pdf_array_get_real(ctx, verts, 0*2+0);
- a.y = pdf_array_get_real(ctx, verts, 0*2+1);
- b.x = pdf_array_get_real(ctx, verts, 1*2+0);
- b.y = pdf_array_get_real(ctx, verts, 1*2+1);
- dx = b.x - a.x;
- dy = b.y - a.y;
- pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 0));
- a.x = pdf_array_get_real(ctx, verts, (n-1)*2+0);
- a.y = pdf_array_get_real(ctx, verts, (n-1)*2+1);
- b.x = pdf_array_get_real(ctx, verts, (n-2)*2+0);
- b.y = pdf_array_get_real(ctx, verts, (n-2)*2+1);
- dx = b.x - a.x;
- dy = b.y - a.y;
- pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 1));
- }
- if (exp == 0)
- pdf_dict_del(ctx, annot->obj, PDF_NAME(RD));
- else
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(exp, exp, exp, exp));
- }
- static void
- pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- pdf_obj *ink_list, *stroke;
- int i, n, k, m;
- float lw;
- fz_point p;
- int sc;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- lw = pdf_write_border_appearance(ctx, annot, buf);
- sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
- *rect = fz_empty_rect;
- fz_append_printf(ctx, buf, "1 J\n1 j\n");
- ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
- n = pdf_array_len(ctx, ink_list);
- for (i = 0; i < n; ++i)
- {
- stroke = pdf_array_get(ctx, ink_list, i);
- m = pdf_array_len(ctx, stroke) / 2;
- for (k = 0; k < m; ++k)
- {
- p.x = pdf_array_get_real(ctx, stroke, k*2+0);
- p.y = pdf_array_get_real(ctx, stroke, k*2+1);
- if (i == 0 && k == 0)
- {
- rect->x0 = rect->x1 = p.x;
- rect->y0 = rect->y1 = p.y;
- }
- else
- *rect = fz_include_point_in_rect(*rect, p);
- fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l');
- }
- if (m == 1)
- fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, 'l');
- }
- maybe_stroke(ctx, buf, sc);
- /* Account for line width and add an extra 6 points of whitespace padding.
- * In case the annotation is a dot or a perfectly horizontal/vertical line,
- * we need some extra size to allow selecting it easily.
- */
- *rect = fz_expand_rect(*rect, lw + 6);
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(lw + 6, lw + 6, lw + 6, lw + 6));
- }
- /* Contrary to the specification, the points within a QuadPoint are NOT
- * ordered in a counter-clockwise fashion starting with the lower left.
- * Experiments with Adobe's implementation indicates a cross-wise
- * ordering is intended: ul, ur, ll, lr.
- */
- enum { UL, UR, LL, LR };
- static float
- extract_quad(fz_context *ctx, fz_point *quad, pdf_obj *obj, int i)
- {
- float dx, dy;
- quad[0].x = pdf_array_get_real(ctx, obj, i+0);
- quad[0].y = pdf_array_get_real(ctx, obj, i+1);
- quad[1].x = pdf_array_get_real(ctx, obj, i+2);
- quad[1].y = pdf_array_get_real(ctx, obj, i+3);
- quad[2].x = pdf_array_get_real(ctx, obj, i+4);
- quad[2].y = pdf_array_get_real(ctx, obj, i+5);
- quad[3].x = pdf_array_get_real(ctx, obj, i+6);
- quad[3].y = pdf_array_get_real(ctx, obj, i+7);
- dx = quad[UL].x - quad[LL].x;
- dy = quad[UL].y - quad[LL].y;
- return sqrtf(dx * dx + dy * dy);
- }
- static void
- union_quad(fz_rect *rect, const fz_point quad[4], float lw)
- {
- fz_rect qbox;
- qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x));
- qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y));
- qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x));
- qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y));
- *rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw));
- }
- static fz_point
- lerp_point(fz_point a, fz_point b, float t)
- {
- return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y));
- }
- static void
- pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- pdf_obj *qp;
- fz_point quad[4], mquad[4], v;
- float h, m, dx, dy, vn;
- int i, n;
- *rect = fz_empty_rect;
- pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_MULTIPLY);
- pdf_write_fill_color_appearance(ctx, annot, buf);
- qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
- n = pdf_array_len(ctx, qp);
- if (n > 0)
- {
- for (i = 0; i < n; i += 8)
- {
- h = extract_quad(ctx, quad, qp, i);
- m = h / 4.2425f; /* magic number that matches adobe's appearance */
- dx = quad[LR].x - quad[LL].x;
- dy = quad[LR].y - quad[LL].y;
- vn = sqrtf(dx * dx + dy * dy);
- v = fz_make_point(dx * m / vn, dy * m / vn);
- mquad[LL].x = quad[LL].x - v.x - v.y;
- mquad[LL].y = quad[LL].y - v.y + v.x;
- mquad[UL].x = quad[UL].x - v.x + v.y;
- mquad[UL].y = quad[UL].y - v.y - v.x;
- mquad[LR].x = quad[LR].x + v.x - v.y;
- mquad[LR].y = quad[LR].y + v.y + v.x;
- mquad[UR].x = quad[UR].x + v.x + v.y;
- mquad[UR].y = quad[UR].y + v.y - v.x;
- fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
- mquad[LL].x, mquad[LL].y,
- mquad[UL].x, mquad[UL].y,
- quad[UL].x, quad[UL].y);
- fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
- fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
- mquad[UR].x, mquad[UR].y,
- mquad[LR].x, mquad[LR].y,
- quad[LR].x, quad[LR].y);
- fz_append_printf(ctx, buf, "f\n");
- union_quad(rect, quad, h/16);
- union_quad(rect, mquad, 0);
- }
- }
- }
- static void
- pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- fz_point quad[4], a, b;
- float h;
- pdf_obj *qp;
- int i, n;
- *rect = fz_empty_rect;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_stroke_color_appearance(ctx, annot, buf);
- qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
- n = pdf_array_len(ctx, qp);
- if (n > 0)
- {
- for (i = 0; i < n; i += 8)
- {
- /* Acrobat draws the line at 1/7 of the box width from the bottom
- * of the box and 1/16 thick of the box width. */
- h = extract_quad(ctx, quad, qp, i);
- a = lerp_point(quad[LL], quad[UL], 1/7.0f);
- b = lerp_point(quad[LR], quad[UR], 1/7.0f);
- fz_append_printf(ctx, buf, "%g w\n", h/16);
- fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
- fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
- fz_append_printf(ctx, buf, "S\n");
- union_quad(rect, quad, h/16);
- }
- }
- }
- static void
- pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- fz_point quad[4], a, b;
- float h;
- pdf_obj *qp;
- int i, n;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_stroke_color_appearance(ctx, annot, buf);
- qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
- n = pdf_array_len(ctx, qp);
- if (n > 0)
- {
- *rect = fz_empty_rect;
- for (i = 0; i < n; i += 8)
- {
- /* Acrobat draws the line at 3/7 of the box width from the bottom
- * of the box and 1/16 thick of the box width. */
- h = extract_quad(ctx, quad, qp, i);
- a = lerp_point(quad[LL], quad[UL], 3/7.0f);
- b = lerp_point(quad[LR], quad[UR], 3/7.0f);
- fz_append_printf(ctx, buf, "%g w\n", h/16);
- fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
- fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
- fz_append_printf(ctx, buf, "S\n");
- union_quad(rect, quad, h/16);
- }
- }
- }
- static void
- pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- fz_point quad[4], a, b, c, v;
- float h, x, w;
- pdf_obj *qp;
- int i, n;
- *rect = fz_empty_rect;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_stroke_color_appearance(ctx, annot, buf);
- qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
- n = pdf_array_len(ctx, qp);
- if (n > 0)
- {
- for (i = 0; i < n; i += 8)
- {
- int up = 1;
- h = extract_quad(ctx, quad, qp, i);
- v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y);
- w = sqrtf(v.x * v.x + v.y * v.y);
- x = 0;
- fz_append_printf(ctx, buf, "%g w\n", h/16);
- fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
- while (x < w)
- {
- x += h/7;
- a = lerp_point(quad[LL], quad[LR], x/w);
- if (up)
- {
- b = lerp_point(quad[UL], quad[UR], x/w);
- c = lerp_point(a, b, 1/7.0f);
- fz_append_printf(ctx, buf, "%g %g l\n", c.x, c.y);
- }
- else
- fz_append_printf(ctx, buf, "%g %g l\n", a.x, a.y);
- up = !up;
- }
- fz_append_printf(ctx, buf, "S\n");
- union_quad(rect, quad, h/16);
- }
- }
- }
- static void
- pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
- {
- fz_point quad[4];
- pdf_obj *qp;
- int i, n;
- pdf_write_opacity(ctx, annot, buf, res);
- fz_append_printf(ctx, buf, "1 0 0 RG\n");
- qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
- n = pdf_array_len(ctx, qp);
- if (n > 0)
- {
- *rect = fz_empty_rect;
- for (i = 0; i < n; i += 8)
- {
- extract_quad(ctx, quad, qp, i);
- fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
- fz_append_printf(ctx, buf, "%g %g l\n", quad[LR].x, quad[LR].y);
- fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
- fz_append_printf(ctx, buf, "%g %g l\n", quad[UL].x, quad[UL].y);
- fz_append_printf(ctx, buf, "s\n");
- union_quad(rect, quad, 1);
- }
- }
- else
- {
- fz_append_printf(ctx, buf, "%g %g m\n", rect->x0+1, rect->y0+1);
- fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y0+1);
- fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y1-1);
- fz_append_printf(ctx, buf, "%g %g l\n", rect->x0+1, rect->y1-1);
- fz_append_printf(ctx, buf, "s\n");
- }
- }
- static void
- pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
- {
- float xc = (rect->x0 + rect->x1) / 2;
- float yc = (rect->y0 + rect->y1) / 2;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_fill_color_appearance(ctx, annot, buf);
- fz_append_string(ctx, buf, "0 0 m\n");
- fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n");
- fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n");
- fz_append_string(ctx, buf, "f\n");
- *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7);
- *bbox = fz_make_rect(0, 0, 20, 14);
- }
- static void
- pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
- {
- const char *name;
- float xc = (rect->x0 + rect->x1) / 2;
- float yc = (rect->y0 + rect->y1) / 2;
- pdf_write_opacity(ctx, annot, buf, res);
- if (!pdf_write_fill_color_appearance(ctx, annot, buf))
- fz_append_string(ctx, buf, "1 g\n");
- fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re\nb\n");
- fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n");
- if (pdf_is_dark_fill_color(ctx, annot))
- fz_append_string(ctx, buf, "1 g\n");
- else
- fz_append_string(ctx, buf, "0 g\n");
- name = pdf_annot_icon_name(ctx, annot);
- /* Text names */
- if (!strcmp(name, "Comment"))
- fz_append_string(ctx, buf, icon_comment);
- else if (!strcmp(name, "Key"))
- fz_append_string(ctx, buf, icon_key);
- else if (!strcmp(name, "Note"))
- fz_append_string(ctx, buf, icon_note);
- else if (!strcmp(name, "Help"))
- fz_append_string(ctx, buf, icon_help);
- else if (!strcmp(name, "NewParagraph"))
- fz_append_string(ctx, buf, icon_new_paragraph);
- else if (!strcmp(name, "Paragraph"))
- fz_append_string(ctx, buf, icon_paragraph);
- else if (!strcmp(name, "Insert"))
- fz_append_string(ctx, buf, icon_insert);
- /* FileAttachment names */
- else if (!strcmp(name, "Graph"))
- fz_append_string(ctx, buf, icon_graph);
- else if (!strcmp(name, "PushPin"))
- fz_append_string(ctx, buf, icon_push_pin);
- else if (!strcmp(name, "Paperclip"))
- fz_append_string(ctx, buf, icon_paperclip);
- else if (!strcmp(name, "Tag"))
- fz_append_string(ctx, buf, icon_tag);
- /* Sound names */
- else if (!strcmp(name, "Speaker"))
- fz_append_string(ctx, buf, icon_speaker);
- else if (!strcmp(name, "Mic"))
- fz_append_string(ctx, buf, icon_mic);
- /* Unknown */
- else
- fz_append_string(ctx, buf, icon_star);
- *rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9);
- *bbox = fz_make_rect(0, 0, 16, 16);
- }
- static float
- measure_stamp_string(fz_context *ctx, fz_font *font, const char *text)
- {
- float w = 0;
- while (*text)
- {
- int c, g;
- text += fz_chartorune(&c, text);
- if (fz_windows_1252_from_unicode(c) < 0)
- c = REPLACEMENT;
- g = fz_encode_character(ctx, font, c);
- w += fz_advance_glyph(ctx, font, g, 0);
- }
- return w;
- }
- static void
- write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text)
- {
- fz_append_byte(ctx, buf, '(');
- while (*text)
- {
- int c;
- text += fz_chartorune(&c, text);
- c = fz_windows_1252_from_unicode(c);
- if (c < 0) c = REPLACEMENT;
- if (c == '(' || c == ')' || c == '\\')
- fz_append_byte(ctx, buf, '\\');
- fz_append_byte(ctx, buf, c);
- }
- fz_append_byte(ctx, buf, ')');
- }
- static void
- write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h)
- {
- float tw = measure_stamp_string(ctx, font, text) * h;
- fz_append_string(ctx, buf, "BT\n");
- fz_append_printf(ctx, buf, "/Times %g Tf\n", h);
- fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y);
- write_stamp_string(ctx, buf, font, text);
- fz_append_string(ctx, buf, " Tj\n");
- fz_append_string(ctx, buf, "ET\n");
- }
- static void
- pdf_write_stamp_appearance_rubber(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- fz_font *font;
- pdf_obj *res_font;
- pdf_obj *name;
- float w, h, xs, ys;
- fz_matrix rotate;
- name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
- if (!name)
- name = PDF_NAME(Draft);
- h = rect->y1 - rect->y0;
- w = rect->x1 - rect->x0;
- xs = w / 190;
- ys = h / 50;
- font = fz_new_base14_font(ctx, "Times-Bold");
- fz_try(ctx)
- {
- /* /Resources << /Font << /Times %d 0 R >> >> */
- if (!*res)
- *res = pdf_new_dict(ctx, annot->page->doc, 1);
- res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
- pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0));
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_fill_color_appearance(ctx, annot, buf);
- pdf_write_stroke_color_appearance(ctx, annot, buf);
- rotate = fz_rotate(0.6f);
- fz_append_printf(ctx, buf, "%M cm\n", &rotate);
- fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n");
- if (name == PDF_NAME(Approved))
- write_stamp(ctx, buf, font, "APPROVED", 13, 30);
- else if (name == PDF_NAME(AsIs))
- write_stamp(ctx, buf, font, "AS IS", 13, 30);
- else if (name == PDF_NAME(Confidential))
- write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20);
- else if (name == PDF_NAME(Departmental))
- write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20);
- else if (name == PDF_NAME(Experimental))
- write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20);
- else if (name == PDF_NAME(Expired))
- write_stamp(ctx, buf, font, "EXPIRED", 13, 30);
- else if (name == PDF_NAME(Final))
- write_stamp(ctx, buf, font, "FINAL", 13, 30);
- else if (name == PDF_NAME(ForComment))
- write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20);
- else if (name == PDF_NAME(ForPublicRelease))
- {
- write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18);
- write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18);
- }
- else if (name == PDF_NAME(NotApproved))
- write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20);
- else if (name == PDF_NAME(NotForPublicRelease))
- {
- write_stamp(ctx, buf, font, "NOT FOR", 26, 18);
- write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18);
- }
- else if (name == PDF_NAME(Sold))
- write_stamp(ctx, buf, font, "SOLD", 13, 30);
- else if (name == PDF_NAME(TopSecret))
- write_stamp(ctx, buf, font, "TOP SECRET", 14, 26);
- else if (name == PDF_NAME(Draft))
- write_stamp(ctx, buf, font, "DRAFT", 13, 30);
- else
- write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20);
- }
- fz_always(ctx)
- fz_drop_font(ctx, font);
- fz_catch(ctx)
- fz_rethrow(ctx);
- *bbox = fz_make_rect(0, 0, 190, 50);
- if (xs > ys)
- {
- float xc = (rect->x1+rect->x0) / 2;
- rect->x0 = xc - 95 * ys;
- rect->x1 = xc + 95 * ys;
- }
- else
- {
- float yc = (rect->y1+rect->y0) / 2;
- rect->y0 = yc - 25 * xs;
- rect->y1 = yc + 25 * xs;
- }
- *matrix = fz_identity;
- }
- static void
- pdf_write_stamp_appearance_image(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res, pdf_obj *img)
- {
- pdf_obj *res_xobj;
- if (!*res)
- *res = pdf_new_dict(ctx, annot->page->doc, 1);
- res_xobj = pdf_dict_put_dict(ctx, *res, PDF_NAME(XObject), 1);
- pdf_dict_put(ctx, res_xobj, PDF_NAME(I), img);
- pdf_write_opacity(ctx, annot, buf, res);
- fz_append_string(ctx, buf, "/I Do\n");
- *bbox = fz_unit_rect;
- *matrix = fz_identity;
- }
- static void
- pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- pdf_obj *img = pdf_annot_stamp_image_obj(ctx, annot);
- if (img)
- pdf_write_stamp_appearance_image(ctx, annot, buf, rect, bbox, matrix, res, img);
- else
- pdf_write_stamp_appearance_rubber(ctx, annot, buf, rect, bbox, matrix, res);
- }
- static void
- add_required_fonts(fz_context *ctx, pdf_document *doc, pdf_obj *res_font,
- fz_text_language lang, fz_font *font, const char *fontname, const char *text)
- {
- fz_font *cjk_font;
- char buf[40];
- int add_latin = 0;
- int add_greek = 0;
- int add_cyrillic = 0;
- int add_korean = 0;
- int add_japanese = 0;
- int add_bopomofo = 0;
- int add_han = 0;
- int add_hans = 0;
- int add_hant = 0;
- while (*text)
- {
- int c;
- text += fz_chartorune(&c, text);
- switch (ucdn_get_script(c))
- {
- default: add_latin = 1; /* for fallback bullet character */ break;
- case UCDN_SCRIPT_COMMON: break;
- case UCDN_SCRIPT_INHERITED: break;
- case UCDN_SCRIPT_LATIN: add_latin = 1; break;
- case UCDN_SCRIPT_GREEK: add_greek = 1; break;
- case UCDN_SCRIPT_CYRILLIC: add_cyrillic = 1; break;
- case UCDN_SCRIPT_HANGUL: add_korean = 1; break;
- case UCDN_SCRIPT_HIRAGANA: add_japanese = 1; break;
- case UCDN_SCRIPT_KATAKANA: add_japanese = 1; break;
- case UCDN_SCRIPT_BOPOMOFO: add_bopomofo = 1; break;
- case UCDN_SCRIPT_HAN: add_han = 1; break;
- }
- }
- if (add_han)
- {
- switch (lang)
- {
- case FZ_LANG_ko: add_korean = 1; break;
- default: /* fall through */
- case FZ_LANG_ja: add_japanese = 1; break;
- case FZ_LANG_zh: /* fall through */
- case FZ_LANG_zh_Hant: add_hant = 1; break;
- case FZ_LANG_zh_Hans: add_hans = 1; break;
- }
- }
- if (add_bopomofo)
- {
- if (lang == FZ_LANG_zh_Hans)
- add_hans = 1;
- else
- add_hant = 1;
- }
- if (!add_greek && !add_cyrillic && !add_korean && !add_japanese && !add_hant && !add_hans)
- add_latin = 1;
- if (add_latin)
- {
- if (!pdf_dict_gets(ctx, res_font, fontname))
- pdf_dict_puts_drop(ctx, res_font, fontname,
- pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_LATIN));
- }
- if (add_greek)
- {
- fz_snprintf(buf, sizeof buf, "%sGRK", fontname);
- if (!pdf_dict_gets(ctx, res_font, buf))
- pdf_dict_puts_drop(ctx, res_font, buf,
- pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_GREEK));
- }
- if (add_cyrillic)
- {
- fz_snprintf(buf, sizeof buf, "%sCYR", fontname);
- if (!pdf_dict_gets(ctx, res_font, buf))
- pdf_dict_puts_drop(ctx, res_font, buf,
- pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_CYRILLIC));
- }
- if (add_korean && !pdf_dict_gets(ctx, res_font, "Batang"))
- {
- cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_KOREA);
- pdf_dict_puts_drop(ctx, res_font, "Batang",
- pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_KOREA, 0, 1));
- fz_drop_font(ctx, cjk_font);
- }
- if (add_japanese && !pdf_dict_gets(ctx, res_font, "Mincho"))
- {
- cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_JAPAN);
- pdf_dict_puts_drop(ctx, res_font, "Mincho",
- pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_JAPAN, 0, 1));
- fz_drop_font(ctx, cjk_font);
- }
- if (add_hant && !pdf_dict_gets(ctx, res_font, "Ming"))
- {
- cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_CNS);
- pdf_dict_puts_drop(ctx, res_font, "Ming",
- pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_CNS, 0, 1));
- fz_drop_font(ctx, cjk_font);
- }
- if (add_hans && !pdf_dict_gets(ctx, res_font, "Song"))
- {
- cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_GB);
- pdf_dict_puts_drop(ctx, res_font, "Song",
- pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_GB, 0, 1));
- fz_drop_font(ctx, cjk_font);
- }
- }
- static int find_initial_script(const char *text)
- {
- int script = UCDN_SCRIPT_COMMON;
- int c;
- while (*text)
- {
- text += fz_chartorune(&c, text);
- script = ucdn_get_script(c);
- if (script != UCDN_SCRIPT_COMMON && script != UCDN_SCRIPT_INHERITED)
- break;
- }
- if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
- script = UCDN_SCRIPT_LATIN;
- return script;
- }
- enum { ENC_LATIN = 1, ENC_GREEK, ENC_CYRILLIC, ENC_KOREAN, ENC_JAPANESE, ENC_HANT, ENC_HANS };
- struct text_walk_state
- {
- const char *text, *end;
- fz_font *font;
- fz_text_language lang;
- int enc, u, c, n, last_script;
- float w;
- };
- static void init_text_walk(fz_context *ctx, struct text_walk_state *state, fz_text_language lang, fz_font *font, const char *text, const char *end)
- {
- state->text = text;
- state->end = end ? end : text + strlen(text);
- state->lang = lang;
- state->font = font;
- state->last_script = find_initial_script(text);
- state->n = 0;
- }
- static int next_text_walk(fz_context *ctx, struct text_walk_state *state)
- {
- int script, g;
- state->text += state->n;
- if (state->text >= state->end)
- {
- state->n = 0;
- return 0;
- }
- state->n = fz_chartorune(&state->u, state->text);
- script = ucdn_get_script(state->u);
- if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
- script = state->last_script;
- state->last_script = script;
- switch (script)
- {
- default:
- state->enc = ENC_LATIN;
- state->c = REPLACEMENT;
- break;
- case UCDN_SCRIPT_LATIN:
- state->enc = ENC_LATIN;
- state->c = fz_windows_1252_from_unicode(state->u);
- break;
- case UCDN_SCRIPT_GREEK:
- state->enc = ENC_GREEK;
- state->c = fz_iso8859_7_from_unicode(state->u);
- break;
- case UCDN_SCRIPT_CYRILLIC:
- state->enc = ENC_CYRILLIC;
- state->c = fz_koi8u_from_unicode(state->u);
- break;
- case UCDN_SCRIPT_HANGUL:
- state->enc = ENC_KOREAN;
- state->c = state->u;
- break;
- case UCDN_SCRIPT_HIRAGANA:
- case UCDN_SCRIPT_KATAKANA:
- state->enc = ENC_JAPANESE;
- state->c = state->u;
- break;
- case UCDN_SCRIPT_BOPOMOFO:
- state->enc = (state->lang == FZ_LANG_zh_Hans) ? ENC_HANS : ENC_HANT;
- state->c = state->u;
- break;
- case UCDN_SCRIPT_HAN:
- switch (state->lang)
- {
- case FZ_LANG_ko: state->enc = ENC_KOREAN; break;
- default: /* fall through */
- case FZ_LANG_ja: state->enc = ENC_JAPANESE; break;
- case FZ_LANG_zh: /* fall through */
- case FZ_LANG_zh_Hant: state->enc = ENC_HANT; break;
- case FZ_LANG_zh_Hans: state->enc = ENC_HANS; break;
- }
- state->c = state->u;
- break;
- }
- /* TODO: check that character is encodable with ENC_KOREAN/etc */
- if (state->c < 0)
- {
- state->enc = ENC_LATIN;
- state->c = REPLACEMENT;
- }
- if (state->enc >= ENC_KOREAN)
- {
- state->w = 1;
- }
- else
- {
- if (state->font != NULL)
- {
- g = fz_encode_character(ctx, state->font, state->u);
- state->w = fz_advance_glyph(ctx, state->font, g, 0);
- }
- }
- return 1;
- }
- static float
- measure_string(fz_context *ctx, fz_text_language lang, fz_font *font, const char *a)
- {
- struct text_walk_state state;
- float w = 0;
- init_text_walk(ctx, &state, lang, font, a, NULL);
- while (next_text_walk(ctx, &state))
- w += state.w;
- return w;
- }
- static float
- break_string(fz_context *ctx, fz_text_language lang, fz_font *font, float size, const char *text, const char **endp, float maxw)
- {
- struct text_walk_state state;
- const char *space = NULL;
- float space_x, x = 0;
- init_text_walk(ctx, &state, lang, font, text, NULL);
- while (next_text_walk(ctx, &state))
- {
- if (state.u == '\n' || state.u == '\r')
- break;
- if (state.u == ' ')
- {
- space = state.text + state.n;
- space_x = x;
- }
- x += state.w * size;
- if (space && x > maxw)
- return *endp = space, space_x;
- }
- return *endp = state.text + state.n, x;
- }
- static void
- write_string(fz_context *ctx, fz_buffer *buf,
- fz_text_language lang, fz_font *font, const char *fontname, float size, const char *text, const char *end)
- {
- struct text_walk_state state;
- int last_enc = 0;
- init_text_walk(ctx, &state, lang, font, text, end);
- while (next_text_walk(ctx, &state))
- {
- if (state.enc != last_enc)
- {
- if (last_enc)
- {
- if (last_enc < ENC_KOREAN)
- fz_append_byte(ctx, buf, ')');
- else
- fz_append_byte(ctx, buf, '>');
- fz_append_string(ctx, buf, " Tj\n");
- }
- switch (state.enc)
- {
- case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
- case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
- case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
- case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
- case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
- case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
- case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
- }
- if (state.enc < ENC_KOREAN)
- fz_append_byte(ctx, buf, '(');
- else
- fz_append_byte(ctx, buf, '<');
- last_enc = state.enc;
- }
- if (state.enc < ENC_KOREAN)
- {
- if (state.c == '(' || state.c == ')' || state.c == '\\')
- fz_append_byte(ctx, buf, '\\');
- fz_append_byte(ctx, buf, state.c);
- }
- else
- {
- fz_append_printf(ctx, buf, "%04x", state.c);
- }
- }
- if (last_enc)
- {
- if (last_enc < ENC_KOREAN)
- fz_append_byte(ctx, buf, ')');
- else
- fz_append_byte(ctx, buf, '>');
- fz_append_string(ctx, buf, " Tj\n");
- }
- }
- static void
- write_string_with_quadding(fz_context *ctx, fz_buffer *buf,
- fz_text_language lang, const char *fontname,
- fz_font *font, float size, float lineheight,
- const char *a, float maxw, int q)
- {
- const char *b;
- float px = 0, x = 0, w;
- while (*a)
- {
- w = break_string(ctx, lang, font, size, a, &b, maxw);
- if (b > a)
- {
- if (q == 0)
- x = 0;
- else if (q == 1)
- x = (maxw - w) / 2;
- else
- x = (maxw - w);
- fz_append_printf(ctx, buf, "%g %g Td\n", x - px, -lineheight);
- if (b[-1] == '\n' || b[-1] == '\r')
- write_string(ctx, buf, lang, font, fontname, size, a, b-1);
- else
- write_string(ctx, buf, lang, font, fontname, size, a, b);
- a = b;
- px = x;
- }
- }
- }
- static void
- write_comb_string(fz_context *ctx, fz_buffer *buf,
- fz_text_language lang, const char *fontname,
- fz_font *font, float size, const char *text, float cell_w)
- {
- struct text_walk_state state;
- int last_enc = 0;
- float pad, carry = 0;
- init_text_walk(ctx, &state, lang, font, text, text + strlen(text));
- while (next_text_walk(ctx, &state))
- {
- if (state.enc != last_enc)
- {
- if (last_enc)
- fz_append_string(ctx, buf, "] TJ\n");
- switch (state.enc)
- {
- case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
- case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
- case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
- case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
- case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
- case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
- case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
- }
- fz_append_byte(ctx, buf, '[');
- last_enc = state.enc;
- }
- pad = (cell_w - state.w * 1000) / 2;
- fz_append_printf(ctx, buf, "%g", -(carry + pad));
- carry = pad;
- if (state.enc < ENC_KOREAN)
- {
- fz_append_byte(ctx, buf, '(');
- if (state.c == '(' || state.c == ')' || state.c == '\\')
- fz_append_byte(ctx, buf, '\\');
- fz_append_byte(ctx, buf, state.c);
- fz_append_byte(ctx, buf, ')');
- }
- else
- {
- fz_append_printf(ctx, buf, "<%04x>", state.c);
- }
- }
- if (last_enc)
- fz_append_string(ctx, buf, "] TJ\n");
- }
- static void
- layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y,
- const char *a, const char *b, fz_font *font, float size, float cell_w)
- {
- int n, c, g;
- int first = 1;
- float w;
- if (a == b)
- fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a);
- while (a < b)
- {
- n = fz_chartorune(&c, a);
- c = fz_windows_1252_from_unicode(c);
- if (c < 0) c = REPLACEMENT;
- g = fz_encode_character(ctx, font, c);
- w = fz_advance_glyph(ctx, font, g, 0) * size;
- if (first)
- {
- fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a);
- first = 0;
- }
- fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a);
- a += n;
- x += cell_w;
- }
- }
- static void
- layout_string(fz_context *ctx, fz_layout_block *out,
- fz_text_language lang, fz_font *font, float size,
- float x, float y, const char *a, const char *b)
- {
- struct text_walk_state state;
- fz_add_layout_line(ctx, out, x, y, size, a);
- init_text_walk(ctx, &state, lang, font, a, b);
- while (next_text_walk(ctx, &state))
- {
- fz_add_layout_char(ctx, out, x, state.w * size, state.text);
- x += state.w * size;
- }
- }
- static void
- layout_string_with_quadding(fz_context *ctx, fz_layout_block *out,
- fz_text_language lang, fz_font *font, float size, float lineheight,
- float xorig, float y, const char *a, float maxw, int q)
- {
- const char *b;
- float x = 0, w;
- int add_line_at_end = 0;
- if (!*a)
- add_line_at_end = 1;
- while (*a)
- {
- w = break_string(ctx, lang, font, size, a, &b, maxw);
- if (b > a)
- {
- if (q > 0)
- {
- if (q == 1)
- x = (maxw - w) / 2;
- else
- x = (maxw - w);
- }
- if (b[-1] == '\n' || b[-1] == '\r')
- {
- layout_string(ctx, out, lang, font, size, xorig+x, y, a, b-1);
- add_line_at_end = 1;
- }
- else
- {
- layout_string(ctx, out, lang, font, size, xorig+x, y, a, b);
- add_line_at_end = 0;
- }
- a = b;
- y -= lineheight;
- }
- }
- if (add_line_at_end)
- fz_add_layout_line(ctx, out, xorig, y, size, a);
- }
- static const char *full_font_name(const char **name)
- {
- if (!strcmp(*name, "Cour")) return "Courier";
- if (!strcmp(*name, "Helv")) return "Helvetica";
- if (!strcmp(*name, "TiRo")) return "Times-Roman";
- if (!strcmp(*name, "Symb")) return "Symbol";
- if (!strcmp(*name, "ZaDb")) return "ZapfDingbats";
- return *name = "Helv", "Helvetica";
- }
- static void
- write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res,
- fz_text_language lang, const char *text,
- const char *fontname, float size, int n, float *color, int q,
- float w, float h, float padding, float baseline, float lineheight,
- int multiline, int comb, int adjust_baseline)
- {
- fz_font *font;
- pdf_obj *res_font;
- w -= padding * 2;
- h -= padding * 2;
- font = fz_new_base14_font(ctx, full_font_name(&fontname));
- fz_try(ctx)
- {
- if (!*res)
- *res = pdf_new_dict(ctx, annot->page->doc, 1);
- res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
- add_required_fonts(ctx, annot->page->doc, res_font, lang, font, fontname, text);
- if (size == 0)
- {
- if (multiline)
- size = 12;
- else
- {
- size = w / measure_string(ctx, lang, font, text);
- if (size > h)
- size = h;
- }
- }
- lineheight = size * lineheight;
- baseline = size * baseline;
- if (adjust_baseline)
- {
- /* Make sure baseline is inside rectangle */
- if (baseline + 0.2f * size > h)
- baseline = h - 0.2f * size;
- }
- fz_append_string(ctx, buf, "BT\n");
- write_color0(ctx, buf, n, color, 0);
- if (multiline)
- {
- fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline+lineheight);
- write_string_with_quadding(ctx, buf, lang, fontname, font, size, lineheight, text, w, q);
- }
- else if (comb > 0)
- {
- float ty = (h - size) / 2;
- fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline-ty);
- write_comb_string(ctx, buf, lang, fontname, font, size, text, (w * 1000 / size) / comb);
- }
- else
- {
- float tx = 0, ty = (h - size) / 2;
- if (q > 0)
- {
- float tw = measure_string(ctx, lang, font, text) * size;
- if (q == 1)
- tx = (w - tw) / 2;
- else
- tx = (w - tw);
- }
- fz_append_printf(ctx, buf, "%g %g Td\n", padding+tx, padding+h-baseline-ty);
- write_string(ctx, buf, lang, font, fontname, size, text, text + strlen(text));
- }
- fz_append_string(ctx, buf, "ET\n");
- }
- fz_always(ctx)
- fz_drop_font(ctx, font);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- static void
- layout_variable_text(fz_context *ctx, fz_layout_block *out,
- const char *text, fz_text_language lang, const char *fontname, float size, int q,
- float x, float y, float w, float h, float padding, float baseline, float lineheight,
- int multiline, int comb, int adjust_baseline)
- {
- fz_font *font;
- w -= padding * 2;
- h -= padding * 2;
- font = fz_new_base14_font(ctx, full_font_name(&fontname));
- fz_try(ctx)
- {
- if (size == 0)
- {
- if (multiline)
- size = 12;
- else
- {
- size = w / measure_string(ctx, lang, font, text);
- if (size > h)
- size = h;
- }
- }
- lineheight = size * lineheight;
- baseline = size * baseline;
- if (adjust_baseline)
- {
- /* Make sure baseline is inside rectangle */
- if (baseline + 0.2f * size > h)
- baseline = h - 0.2f * size;
- }
- if (multiline)
- {
- x += padding;
- y += padding + h - baseline;
- layout_string_with_quadding(ctx, out, lang, font, size, lineheight, x, y, text, w, q);
- }
- else if (comb > 0)
- {
- float ty = (h - size) / 2;
- x += padding;
- y += padding + h - baseline - ty;
- layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb);
- }
- else
- {
- float tx = 0, ty = (h - size) / 2;
- if (q > 0)
- {
- float tw = measure_string(ctx, lang, font, text) * size;
- if (q == 1)
- tx = (w - tw) / 2;
- else
- tx = (w - tw);
- }
- x += padding + tx;
- y += padding + h - baseline - ty;
- layout_string(ctx, out, lang, font, size, x, y, text, text + strlen(text));
- }
- }
- fz_always(ctx)
- fz_drop_font(ctx, font);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- #if FZ_ENABLE_HTML_ENGINE
- static void
- write_rich_content(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, const char *rc, const char *ds, float size, float w, float h, float b, int multiline)
- {
- /* border_box is the actual rectangle that we are writing into.
- * We use this to feed to the pdfwriting device. */
- fz_rect border_box = { 0, 0, w, h };
- /* content_box is adjusted for padding and has added height.
- * We know a clipping rectangle will have been set to the proper rectangle
- * so we can allow text to flow out the bottom of the rectangle rather than
- * just missing it out. This matches adobe. */
- fz_rect content_box = fz_make_rect(b, b, w - b*2, h + size * 2);
- fz_buffer *inbuf = fz_new_buffer_from_copied_data(ctx, (const unsigned char *)rc, strlen(rc)+1);
- fz_story *story = NULL;
- fz_device *dev = NULL;
- fz_buffer *buf2 = NULL;
- const char *default_css = "@page{margin:0} body{margin:0;line-height:1.2;white-space:pre-wrap;} p{margin:0}";
- char *css = NULL;
- fz_var(story);
- fz_var(dev);
- fz_var(res);
- fz_var(buf2);
- fz_var(css);
- // single-line should be centered in the box. adjust content box accordingly.
- // this matches the math in write_variable_text.
- if (!multiline)
- {
- float ty = ((h - b * 2) - size) / 2;
- content_box.y0 = h - b - 0.8f * size + ty;
- content_box.y1 = content_box.y0 + size * 2;
- }
- fz_try(ctx)
- {
- if (ds)
- css = fz_asprintf(ctx, "%s body{%s}", default_css, ds);
- story = fz_new_story(ctx, inbuf, css ? css : default_css, size, NULL);
- dev = pdf_page_write(ctx, annot->page->doc, border_box, res, &buf2);
- fz_place_story(ctx, story, content_box, NULL);
- fz_draw_story(ctx, story, dev, fz_identity);
- fz_close_device(ctx, dev);
- fz_append_buffer(ctx, buf, buf2);
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_buffer(ctx, buf2);
- fz_drop_story(ctx, story);
- fz_drop_buffer(ctx, inbuf);
- fz_free(ctx, css);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- #endif
- #if FZ_ENABLE_HTML_ENGINE
- static char *
- escape_text(fz_context *ctx, const char *s)
- {
- size_t len = 1;
- char c;
- const char *s2;
- char *d, *d2;
- for (s2 = s; (c = *s2++) != 0; len++)
- {
- if (c == '<')
- len += 3; /* < */
- else if (c == '>')
- len += 3; /* > */
- else if (c == '&')
- len += 4; /* & */
- }
- d = d2 = fz_malloc(ctx, len);
- for (s2 = s; (c = *s2++) != 0; )
- {
- if (c == '<')
- {
- *d++ = '&';
- *d++ = 'l';
- *d++ = 't';
- *d++ = ';';
- }
- else if (c == '>')
- {
- *d++ = '&';
- *d++ = 'g';
- *d++ = 't';
- *d++ = ';';
- }
- else if (c == '&')
- {
- *d++ = '&';
- *d++ = 'a';
- *d++ = 'm';
- *d++ = 'p';
- *d++ = ';';
- }
- else
- *d++ = c;
- }
- *d++ = 0;
- return d2;
- }
- int text_needs_rich_layout(fz_context *ctx, const char *s)
- {
- int c, script;
- while (*s)
- {
- s += fz_chartorune(&c, s);
- // base 14 fonts
- if (fz_windows_1252_from_unicode(c) > 0)
- continue;
- if (fz_iso8859_7_from_unicode(c) > 0)
- continue;
- if (fz_koi8u_from_unicode(c) > 0)
- continue;
- // cjk fonts
- script = ucdn_get_script(c);
- if (
- script == UCDN_SCRIPT_HANGUL ||
- script == UCDN_SCRIPT_HIRAGANA ||
- script == UCDN_SCRIPT_KATAKANA ||
- script == UCDN_SCRIPT_BOPOMOFO ||
- script == UCDN_SCRIPT_HAN
- )
- continue;
- return 1;
- }
- return 0;
- }
- static unsigned int hex_from_color(fz_context *ctx, int n, float color[4])
- {
- float rgb[4];
- int r, g, b;
- switch (n)
- {
- default:
- r = g = b = 0;
- break;
- case 1:
- r = g = b = color[0] * 255;
- break;
- case 3:
- r = color[0] * 255;
- g = color[1] * 255;
- b = color[2] * 255;
- break;
- case 4:
- fz_convert_color(ctx, fz_device_cmyk(ctx), color, fz_device_rgb(ctx), rgb, NULL, fz_default_color_params);
- r = rgb[0] * 255;
- g = rgb[1] * 255;
- b = rgb[2] * 255;
- break;
- }
- return (r<<16) | (g<<8) | b;
- }
- #endif
- static float
- pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b)
- {
- float dx, dy, line_length;
- fz_point co;
- float size;
- const char *text;
- pdf_obj *res_font;
- fz_font *font = NULL;
- fz_matrix tm;
- int lang;
- int top;
- float tw;
- fz_var(font);
- fz_try(ctx)
- {
- // vector of line
- dx = b.x - a.x;
- dy = b.y - a.y;
- line_length = hypotf(dx, dy);
- dx /= line_length;
- dy /= line_length;
- text = pdf_annot_contents(ctx, annot);
- lang = pdf_annot_language(ctx, annot);
- co = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO));
- font = fz_new_base14_font(ctx, "Helvetica");
- if (!*res)
- *res = pdf_new_dict(ctx, annot->page->doc, 1);
- res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
- add_required_fonts(ctx, annot->page->doc, res_font, lang, font, "Helv", text);
- size = 12;
- tw = measure_string(ctx, lang, font, text) * size;
- // don't inline if CP says so
- top = 0;
- if (pdf_dict_get(ctx, annot->obj, PDF_NAME(CP)) == PDF_NAME(Top))
- top = 1;
- // don't inline if caption wouldn't fit
- if (tw + size > line_length)
- top = 1;
- tm = fz_rotate(atan2(dy, dx) * 180 / M_PI);
- tm.e = (a.x + b.x) / 2 - dx * (tw / 2);
- tm.f = (a.y + b.y) / 2 - dy * (tw / 2);
- // caption offset
- if (co.x || co.y)
- {
- // don't write text inline
- top = 1;
- if (co.y < 0)
- co.y -= size;
- tm.e += co.x * dx - co.y * dy;
- tm.f += co.x * dy + co.y * dx;
- }
- else if (top)
- {
- tm.e -= dy * size * 0.2f;
- tm.f += dx * size * 0.2f;
- }
- else
- {
- tm.e += dy * size * 0.3f;
- tm.f -= dx * size * 0.3f;
- }
- fz_append_printf(ctx, buf, "q\n%M cm\n", &tm);
- fz_append_string(ctx, buf, "0 g\n"); // Acrobat always draws captions in black
- fz_append_printf(ctx, buf, "BT\n");
- write_string(ctx, buf, lang, font, "Helv", size, text, text + strlen(text));
- fz_append_printf(ctx, buf, "ET\n");
- fz_append_printf(ctx, buf, "Q\n");
- *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e - dx * tw/2, tm.f - dy * tw/2));
- *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e + dx * tw/2, tm.f + dy * tw/2));
- *rect = fz_expand_rect(*rect, size);
- }
- fz_always(ctx)
- {
- fz_drop_font(ctx, font);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- if (!top)
- return (tw + size / 2) / 2;
- return 0;
- }
- static void
- pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- const char *font;
- float size, color[4];
- const char *text;
- float w, h, b;
- int q, r, n;
- int lang;
- fz_rect rd;
- pdf_obj *le;
- int ic;
- fz_rect text_box;
- fz_matrix tfm;
- #if FZ_ENABLE_HTML_ENGINE
- const char *rc, *ds;
- char *free_rc = NULL;
- char ds_buf[400];
- #endif
- text = pdf_annot_contents(ctx, annot);
- q = pdf_annot_quadding(ctx, annot);
- pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
- lang = pdf_annot_language(ctx, annot);
- rd = pdf_annot_rect_diff(ctx, annot);
- /* /Rotate is an undocumented annotation property supported by Adobe.
- * When Rotate is used, neither the box, nor the arrow move at all.
- * Only the position of the text moves within the box. Thus we don't
- * need to adjust rd at all! */
- r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate));
- // Adjust input Rect for RD to get the bounds of the text box area
- text_box = *rect;
- text_box.x0 += rd.x0;
- text_box.y0 += rd.y0;
- text_box.x1 -= rd.x1;
- text_box.y1 -= rd.y1;
- *rect = text_box;
- // Size of text box area (including padding and border)
- w = text_box.x1 - text_box.x0;
- h = text_box.y1 - text_box.y0;
- pdf_write_opacity(ctx, annot, buf, res);
- pdf_write_dash_pattern(ctx, annot, buf, res);
- // Set stroke and fill colors for box and callout line
- ic = pdf_write_fill_color_appearance(ctx, annot, buf);
- write_color0(ctx, buf, n, color, 1);
- b = pdf_write_border_appearance(ctx, annot, buf);
- // Draw Callout line
- if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT)), PDF_NAME(FreeTextCallout)))
- {
- pdf_obj *cl = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL));
- int i, len = pdf_array_len(ctx, cl);
- if (len == 4 || len == 6)
- {
- float xy[6];
- for (i = 0; i < len; i += 2)
- {
- float x = xy[i+0] = pdf_array_get_real(ctx, cl, i+0);
- float y = xy[i+1] = pdf_array_get_real(ctx, cl, i+1);
- if (x < rect->x0) rect->x0 = x;
- if (y < rect->y0) rect->y0 = y;
- if (x > rect->x1) rect->x1 = x;
- if (y > rect->y1) rect->y1 = y;
- }
- fz_append_printf(ctx, buf, "%g %g m\n", xy[0], xy[1]);
- for (i = 2; i < len; i += 2)
- fz_append_printf(ctx, buf, "%g %g l\n", xy[i+0], xy[i+1]);
- fz_append_printf(ctx, buf, "S\n");
- le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
- pdf_write_line_cap_appearance(ctx, buf, rect,
- xy[0], xy[1],
- xy[2] - xy[0], xy[3] - xy[1], b,
- 1, ic, le);
- }
- }
- // Draw text box background
- if (ic)
- fz_append_printf(ctx, buf, "%g %g %g %g re\nf\n", text_box.x0, text_box.y0, w, h);
- // Draw text box border
- if (b > 0)
- fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", text_box.x0 + b/2, text_box.y0 + b/2, w - b, h - b);
- // Clip text to box
- fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", text_box.x0 + b, text_box.y0 + b, w - b * 2, h - b * 2);
- // Recompute Rect and RD to account for Callout line
- rd.x0 = text_box.x0 - rect->x0;
- rd.y0 = text_box.y0 - rect->y0;
- rd.x1 = rect->x1 - text_box.x1;
- rd.y1 = rect->y1 - text_box.y1;
- // Compute rotation (and offset) transform for text
- if (r == 90 || r == 270)
- {
- float t = w; w = h; h = t;
- }
- tfm = fz_rotate(r);
- if (r == 270)
- tfm.e += 0, tfm.f += w;
- else if (r == 90)
- tfm.e += h, tfm.f -= 0;
- else if (r == 180)
- tfm.e += w, tfm.f += h;
- tfm.e += text_box.x0;
- tfm.f += text_box.y0;
- fz_append_printf(ctx, buf, "q\n%g %g %g %g %g %g cm\n", tfm.a, tfm.b, tfm.c, tfm.d, tfm.e, tfm.f);
- #if FZ_ENABLE_HTML_ENGINE
- ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS));
- rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RC));
- if (!rc && (ds || text_needs_rich_layout(ctx, text)))
- {
- rc = free_rc = escape_text(ctx, text);
- if (!ds)
- {
- fz_snprintf(ds_buf, sizeof ds_buf,
- "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s;",
- full_font_name(&font),
- size,
- hex_from_color(ctx, n, color),
- (q == 0 ? "left" : q == 1 ? "center" : "right")
- );
- ds = ds_buf;
- }
- }
- if (rc)
- {
- fz_try(ctx)
- write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2, 1);
- fz_always(ctx)
- fz_free(ctx, free_rc);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- else
- #endif
- {
- write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
- 0.8f, 1.2f, 1, 0, 0);
- }
- fz_append_printf(ctx, buf, "Q\n");
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
- *matrix = fz_identity;
- *bbox = *rect;
- }
- static void
- pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res,
- const char *text, int ff)
- {
- fz_text_language lang;
- const char *font;
- float size, color[4];
- float w, h, t, b;
- int has_bc = 0;
- int q, r, n;
- #if FZ_ENABLE_HTML_ENGINE
- const char *rc, *ds;
- char *free_rc = NULL;
- char ds_buf[400];
- #endif
- r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
- q = pdf_annot_quadding(ctx, annot);
- pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
- lang = pdf_annot_language(ctx, annot);
- w = rect->x1 - rect->x0;
- h = rect->y1 - rect->y0;
- r = r % 360;
- if (r == 90 || r == 270)
- t = h, h = w, w = t;
- *matrix = fz_rotate(r);
- *bbox = fz_make_rect(0, 0, w, h);
- fz_append_string(ctx, buf, "/Tx BMC\nq\n");
- if (pdf_write_MK_BG_appearance(ctx, annot, buf))
- fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
- b = pdf_write_border_appearance(ctx, annot, buf);
- if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
- {
- fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n", b/2, b/2, w-b, h-b);
- has_bc = 1;
- }
- fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
- #if FZ_ENABLE_HTML_ENGINE
- ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS));
- rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RV));
- if (!rc && (ds || text_needs_rich_layout(ctx, text)))
- {
- rc = free_rc = escape_text(ctx, text);
- if (!ds)
- {
- fz_snprintf(ds_buf, sizeof ds_buf,
- "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s",
- full_font_name(&font),
- size,
- hex_from_color(ctx, n, color),
- (q == 0 ? "left" : q == 1 ? "center" : "right")
- );
- ds = ds_buf;
- }
- }
- if (rc)
- {
- fz_try(ctx)
- write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2,
- (ff & PDF_TX_FIELD_IS_MULTILINE));
- fz_always(ctx)
- fz_free(ctx, free_rc);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- else
- #endif
- if (ff & PDF_TX_FIELD_IS_MULTILINE)
- {
- write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
- 1.116f, 1.116f, 1, 0, 1);
- }
- else if (ff & PDF_TX_FIELD_IS_COMB)
- {
- int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen));
- if (has_bc && maxlen > 1)
- {
- float cell_w = (w - 2 * b) / maxlen;
- int i;
- for (i = 1; i < maxlen; ++i)
- {
- float x = b + cell_w * i;
- fz_append_printf(ctx, buf, "%g %g m %g %g l s\n", x, b, x, h-b);
- }
- }
- write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, 0,
- 0.8f, 1.2f, 0, maxlen, 0);
- }
- else
- {
- write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
- 0.8f, 1.2f, 0, 0, 0);
- }
- fz_append_string(ctx, buf, "Q\nEMC\n");
- }
- fz_layout_block *
- pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot)
- {
- fz_text_language lang;
- fz_layout_block *out;
- const char *font;
- const char *text;
- fz_rect rect;
- float size, color[4];
- float w, h, t, b, x, y;
- int q, r, n;
- int ff;
- rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
- text = pdf_field_value(ctx, annot->obj);
- ff = pdf_field_flags(ctx, annot->obj);
- b = pdf_annot_border_width(ctx, annot);
- r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
- q = pdf_annot_quadding(ctx, annot);
- pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
- lang = pdf_annot_language(ctx, annot);
- w = rect.x1 - rect.x0;
- h = rect.y1 - rect.y0;
- r = r % 360;
- if (r == 90 || r == 270)
- t = h, h = w, w = t;
- x = rect.x0;
- y = rect.y0;
- out = fz_new_layout(ctx);
- fz_try(ctx)
- {
- pdf_page_transform(ctx, annot->page, NULL, &out->matrix);
- out->matrix = fz_concat(out->matrix, fz_rotate(r));
- out->inv_matrix = fz_invert_matrix(out->matrix);
- if (ff & PDF_TX_FIELD_IS_MULTILINE)
- {
- layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1);
- }
- else if (ff & PDF_TX_FIELD_IS_COMB)
- {
- int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen));
- layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0);
- }
- else
- {
- layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0);
- }
- }
- fz_catch(ctx)
- {
- fz_drop_layout(ctx, out);
- fz_rethrow(ctx);
- }
- return out;
- }
- static void
- pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- int ff = pdf_field_flags(ctx, annot->obj);
- if (ff & PDF_CH_FIELD_IS_COMBO)
- {
- /* TODO: Pop-down arrow */
- pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
- pdf_field_value(ctx, annot->obj), 0);
- }
- else
- {
- fz_buffer *text = fz_new_buffer(ctx, 1024);
- fz_try(ctx)
- {
- pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt));
- int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI));
- int n = pdf_array_len(ctx, opt);
- /* TODO: Scrollbar */
- /* TODO: Highlight selected items */
- if (i < 0)
- i = 0;
- for (; i < n; ++i)
- {
- pdf_obj *val = pdf_array_get(ctx, opt, i);
- if (pdf_is_array(ctx, val))
- fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1));
- else
- fz_append_string(ctx, text, pdf_to_text_string(ctx, val));
- fz_append_byte(ctx, text, '\n');
- }
- pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
- fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE);
- }
- fz_always(ctx)
- fz_drop_buffer(ctx, text);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- }
- static void
- pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- float x0 = rect->x0 + 1;
- float y0 = rect->y0 + 1;
- float x1 = rect->x1 - 1;
- float y1 = rect->y1 - 1;
- float w = x1 - x0;
- float h = y1 - y0;
- fz_append_printf(ctx, buf, "1 w\n0 G\n");
- fz_append_printf(ctx, buf, "%g %g %g %g re\n", x0, y0, w, h);
- fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x0, y0, x1, y1);
- fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x1, y0, x0, y1);
- fz_append_printf(ctx, buf, "s\n");
- *bbox = *rect;
- *matrix = fz_identity;
- }
- static void
- pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
- if (pdf_name_eq(ctx, ft, PDF_NAME(Tx)))
- {
- int ff = pdf_field_flags(ctx, annot->obj);
- char *format = NULL;
- const char *text = NULL;
- if (!annot->ignore_trigger_events)
- {
- format = pdf_field_event_format(ctx, annot->page->doc, annot->obj);
- if (format)
- text = format;
- else
- text = pdf_field_value(ctx, annot->obj);
- }
- else
- {
- text = pdf_field_value(ctx, annot->obj);
- }
- fz_try(ctx)
- pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff);
- fz_always(ctx)
- fz_free(ctx, format);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch)))
- {
- pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
- }
- else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig)))
- {
- pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
- }
- else
- {
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft));
- }
- }
- static void
- pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
- fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
- {
- switch (pdf_annot_type(ctx, annot))
- {
- default:
- fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot create appearance stream for %s annotations",
- pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype)));
- case PDF_ANNOT_WIDGET:
- pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
- break;
- case PDF_ANNOT_INK:
- pdf_write_ink_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_POLYGON:
- pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 1);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_POLY_LINE:
- pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 0);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_LINE:
- pdf_write_line_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_SQUARE:
- pdf_write_square_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_CIRCLE:
- pdf_write_circle_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_CARET:
- pdf_write_caret_appearance(ctx, annot, buf, rect, bbox, res);
- *matrix = fz_identity;
- break;
- case PDF_ANNOT_TEXT:
- case PDF_ANNOT_FILE_ATTACHMENT:
- case PDF_ANNOT_SOUND:
- pdf_write_icon_appearance(ctx, annot, buf, rect, bbox, res);
- *matrix = fz_identity;
- break;
- case PDF_ANNOT_HIGHLIGHT:
- pdf_write_highlight_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_UNDERLINE:
- pdf_write_underline_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_STRIKE_OUT:
- pdf_write_strike_out_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_SQUIGGLY:
- pdf_write_squiggly_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_REDACT:
- pdf_write_redact_appearance(ctx, annot, buf, rect, res);
- *matrix = fz_identity;
- *bbox = *rect;
- break;
- case PDF_ANNOT_STAMP:
- pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, matrix, res);
- break;
- case PDF_ANNOT_FREE_TEXT:
- pdf_write_free_text_appearance(ctx, annot, buf, rect, bbox, matrix, res);
- break;
- }
- }
- static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h,
- const char *caption, const char *font, float size, int n, float *color,
- int down)
- {
- pdf_obj *ap, *res = NULL;
- fz_buffer *buf;
- float bc[3] = { 0, 0, 0 };
- float bg[3] = { 0.8f, 0.8f, 0.8f };
- float hi[3], sh[3];
- int has_bg, has_bc;
- float b;
- int i;
- buf = fz_new_buffer(ctx, 1024);
- fz_var(res);
- fz_try(ctx)
- {
- b = pdf_annot_border_width(ctx, annot);
- has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc);
- has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg);
- for (i = 0; i < 3; ++i)
- {
- if (down)
- {
- sh[i] = 1 - (1 - bg[i]) / 2;
- hi[i] = bg[i] / 2;
- }
- else
- {
- hi[i] = 1 - (1 - bg[i]) / 2;
- sh[i] = bg[i] / 2;
- }
- }
- fz_append_string(ctx, buf, "q\n");
- fz_append_printf(ctx, buf, "%g w\n", b);
- if (has_bg)
- {
- fz_append_printf(ctx, buf, "%g %g %g rg\n", bg[0], bg[1], bg[2]);
- fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", 0, 0, w, h);
- }
- if (has_bc && b > 0)
- {
- fz_append_printf(ctx, buf, "%g %g %g RG\n", bc[0], bc[1], bc[2]);
- fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
- }
- if (has_bg)
- {
- fz_append_printf(ctx, buf, "%g %g %g rg\n", hi[0], hi[1], hi[2]);
- fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
- b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2);
- fz_append_printf(ctx, buf, "%g %g %g rg\n", sh[0], sh[1], sh[2]);
- fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
- b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b);
- }
- if (down)
- fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n");
- write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, caption, font, size, n, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0);
- fz_append_string(ctx, buf, "Q\n");
- ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, res);
- fz_drop_buffer(ctx, buf);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- return ap;
- }
- static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
- {
- pdf_obj *ap;
- fz_buffer *buf;
- float b;
- buf = fz_new_buffer(ctx, 1024);
- fz_try(ctx)
- {
- fz_append_string(ctx, buf, "q\n");
- if (pdf_write_MK_BG_appearance(ctx, annot, buf))
- {
- draw_circle_in_box(ctx, buf, 0, 0, 0, w, h);
- fz_append_string(ctx, buf, "f\n");
- }
- b = pdf_write_border_appearance(ctx, annot, buf);
- if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
- {
- draw_circle_in_box(ctx, buf, b, 0, 0, w, h);
- fz_append_string(ctx, buf, "s\n");
- }
- if (yes)
- {
- fz_append_string(ctx, buf, "0 g\n");
- draw_circle(ctx, buf, (w-b*2)/4, (h-b*2)/4, w/2, h/2);
- fz_append_string(ctx, buf, "f\n");
- }
- fz_append_string(ctx, buf, "Q\n");
- ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf);
- }
- fz_always(ctx)
- fz_drop_buffer(ctx, buf);
- fz_catch(ctx)
- fz_rethrow(ctx);
- return ap;
- }
- static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
- {
- float black[1] = { 0 };
- pdf_obj *ap, *res = NULL;
- fz_buffer *buf;
- float b;
- fz_var(res);
- buf = fz_new_buffer(ctx, 1024);
- fz_try(ctx)
- {
- fz_append_string(ctx, buf, "q\n");
- if (pdf_write_MK_BG_appearance(ctx, annot, buf))
- fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
- b = pdf_write_border_appearance(ctx, annot, buf);
- if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
- fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
- if (yes)
- write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, "3", "ZaDb", h, nelem(black), black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0);
- fz_append_string(ctx, buf, "Q\n");
- ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, res);
- fz_drop_buffer(ctx, buf);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- return ap;
- }
- static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot)
- {
- int ff = pdf_field_flags(ctx, annot->obj);
- fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
- fz_matrix matrix;
- fz_rect bbox;
- float w, h, t;
- int r;
- r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
- w = rect.x1 - rect.x0;
- h = rect.y1 - rect.y0;
- r = r % 360;
- if (r == 90 || r == 270)
- t = h, h = w, w = t;
- matrix = fz_rotate(r);
- bbox = fz_make_rect(0, 0, w, h);
- if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON)
- {
- pdf_obj *ap_n = NULL;
- pdf_obj *ap_d = NULL;
- fz_var(ap_n);
- fz_var(ap_d);
- fz_try(ctx)
- {
- pdf_obj *ap, *MK, *CA, *AC;
- const char *font;
- const char *label;
- float size, color[4];
- int n;
- pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
- MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK));
- CA = pdf_dict_get(ctx, MK, PDF_NAME(CA));
- AC = pdf_dict_get(ctx, MK, PDF_NAME(AC));
- label = pdf_to_text_string(ctx, CA);
- ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 0);
- label = pdf_to_text_string(ctx, AC ? AC : CA);
- ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 1);
- ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
- pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n);
- pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, ap_n);
- pdf_drop_obj(ctx, ap_d);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- else
- {
- pdf_obj *as_yes = NULL;
- pdf_obj *ap_off = NULL;
- pdf_obj *ap_yes = NULL;
- fz_var(ap_off);
- fz_var(ap_yes);
- fz_var(as_yes);
- fz_try(ctx)
- {
- pdf_obj *ap, *ap_n, *as;
- if (w > h) w = h;
- if (h > w) h = w;
- if (ff & PDF_BTN_FIELD_IS_RADIO)
- {
- ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0);
- ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1);
- }
- else
- {
- ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0);
- ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1);
- }
- as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
- if (!as)
- {
- pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off));
- as = PDF_NAME(Off);
- }
- if (as == PDF_NAME(Off))
- as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj));
- else
- as_yes = pdf_keep_obj(ctx, as);
- ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
- ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2);
- pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off);
- pdf_dict_put(ctx, ap_n, as_yes, ap_yes);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, as_yes);
- pdf_drop_obj(ctx, ap_yes);
- pdf_drop_obj(ctx, ap_off);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- }
- pdf_set_annot_resynthesised(ctx, annot);
- }
- static void draw_logo(fz_context *ctx, fz_path *path)
- {
- /* Use mupdf logo for signature appearance background. */
- fz_moveto(ctx, path, 122.25f, 0.0f);
- fz_lineto(ctx, path, 122.25f, 14.249f);
- fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f);
- fz_lineto(ctx, path, 133.5f, 0.0f);
- fz_lineto(ctx, path, 122.25f, 0.0f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 140.251f, 0.0f);
- fz_lineto(ctx, path, 140.251f, 12.935f);
- fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f);
- fz_lineto(ctx, path, 177.75f, 21.749f);
- fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f);
- fz_lineto(ctx, path, 140.251f, 39.0f);
- fz_lineto(ctx, path, 133.5f, 39.0f);
- fz_lineto(ctx, path, 133.5f, 20.704f);
- fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f);
- fz_lineto(ctx, path, 122.25f, 50.999f);
- fz_lineto(ctx, path, 177.751f, 50.999f);
- fz_lineto(ctx, path, 177.751f, 0.0f);
- fz_lineto(ctx, path, 140.251f, 0.0f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 23.482f, 129.419f);
- fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f);
- fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f);
- fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f);
- fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 254.751f, 128.492f);
- fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f);
- fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f);
- fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f);
- fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 111.0f, 98.999f);
- fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f);
- fz_lineto(ctx, path, 103.5f, 297.749f);
- fz_lineto(ctx, path, 213.75f, 298.499f);
- fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f);
- fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f);
- fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f);
- fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f);
- fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f);
- fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f);
- fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f);
- fz_lineto(ctx, path, 227.25f, 276.749f);
- fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f);
- fz_lineto(ctx, path, 197.25f, 112.499f);
- fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f);
- fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f);
- fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f);
- fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f);
- fz_closepath(ctx, path);
- fz_moveto(ctx, path, 125.25f, 105.749f);
- fz_lineto(ctx, path, 125.25f, 202.499f);
- fz_lineto(ctx, path, 95.25f, 117.749f);
- fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f);
- fz_closepath(ctx, path);
- };
- static float logo_color[3] = { (float)0xa4 / (float)0xFF, (float)0xca / (float)0xFF, (float)0xf5 / (float)0xFF };
- fz_display_list *
- pdf_signature_appearance_signed(fz_context *ctx, fz_rect rect, fz_text_language lang, fz_image *img, const char *left_text, const char *right_text, int include_logo)
- {
- fz_display_list *dlist = NULL;
- fz_device *dev = NULL;
- fz_text *text = NULL;
- fz_colorspace *cs = NULL;
- fz_path *path = NULL;
- fz_font *font = NULL;
- fz_var(path);
- fz_var(dlist);
- fz_var(dev);
- fz_var(text);
- fz_var(font);
- fz_try(ctx)
- {
- fz_rect prect;
- fz_rect logo_bounds;
- fz_matrix logo_tm;
- float color[] = { 0.0, 0.0, 0.0 };
- font = fz_new_base14_font(ctx, "Helvetica");
- dlist = fz_new_display_list(ctx, rect);
- dev = fz_new_list_device(ctx, dlist);
- cs = fz_device_rgb(ctx);
- if (include_logo)
- {
- path = fz_new_path(ctx);
- draw_logo(ctx, path);
- logo_bounds = fz_bound_path(ctx, path, NULL, fz_identity);
- logo_tm = center_rect_within_rect(logo_bounds, rect);
- fz_fill_path(ctx, dev, path, 0, logo_tm, cs, logo_color, 1.0f, fz_default_color_params);
- }
- prect = rect;
- /* If there is to be info on the right then use only the left half of the rectangle for
- * what is intended for the left */
- if (right_text)
- prect.x1 = (prect.x0 + prect.x1) / 2.0f;
- if (img)
- {
- float img_aspect = (float) img->w / img->h;
- float rectw = prect.x1 - prect.x0;
- float recth = prect.y1 - prect.y0;
- float midx = (prect.x0 + prect.x1) / 2.0f;
- float midy = (prect.y0 + prect.y1) / 2.0f;
- float rect_aspect = rectw / recth;
- float scale = img_aspect > rect_aspect ? rectw / img->w : recth / img->h;
- fz_matrix ctm = fz_pre_translate(fz_pre_scale(fz_translate(midx, midy), scale * img->w, scale * img->h), -0.5, -0.5);
- fz_fill_image(ctx, dev, img, ctm, 1.0f, fz_default_color_params);
- }
- if (left_text)
- {
- text = pdf_layout_fit_text(ctx, font, lang, left_text, prect);
- fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params);
- fz_drop_text(ctx, text);
- text = NULL;
- }
- prect = rect;
- /* If there is to be info on the left then use only the right half of the rectangle for
- * what is intended for the right */
- if (img || left_text)
- prect.x0 = (prect.x0 + prect.x1) / 2.0f;
- if (right_text)
- {
- text = pdf_layout_fit_text(ctx, font, lang, right_text, prect);
- fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params);
- }
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_path(ctx, path);
- fz_drop_text(ctx, text);
- fz_drop_font(ctx, font);
- }
- fz_catch(ctx)
- {
- fz_drop_display_list(ctx, dlist);
- fz_rethrow(ctx);
- }
- return dlist;
- }
- fz_display_list *
- pdf_signature_appearance_unsigned(fz_context *ctx, fz_rect rect, fz_text_language lang)
- {
- fz_display_list *dlist = NULL;
- fz_device *dev = NULL;
- fz_text *text = NULL;
- fz_colorspace *cs = NULL;
- fz_path *path = NULL;
- fz_font *font = NULL;
- fz_var(path);
- fz_var(dlist);
- fz_var(dev);
- fz_var(text);
- fz_var(font);
- fz_try(ctx)
- {
- float text_color[] = { 1.0f, 1.0f, 1.0f };
- float arrow_color[] = { 0.95f, 0.33f, 0.18f };
- dlist = fz_new_display_list(ctx, rect);
- dev = fz_new_list_device(ctx, dlist);
- rect.y1 = rect.y0 + (rect.y1 - rect.y0) / 6;
- rect.x1 = rect.x0 + (rect.y1 - rect.y0) * 4;
- font = fz_new_base14_font(ctx, "Helvetica");
- path = fz_new_path(ctx);
- /* Draw a rectangle with a protrusion to the right [xxxxx> */
- fz_moveto(ctx, path, rect.x0, rect.y0);
- fz_lineto(ctx, path, rect.x1, rect.y0);
- fz_lineto(ctx, path, rect.x1 + (rect.y1 - rect.y0) / 2.0f, (rect.y0 + rect.y1) / 2.0f);
- fz_lineto(ctx, path, rect.x1, rect.y1);
- fz_lineto(ctx, path, rect.x0, rect.y1);
- fz_closepath(ctx, path);
- cs = fz_device_rgb(ctx);
- fz_fill_path(ctx, dev, path, 0, fz_identity, cs, arrow_color, 1.0f, fz_default_color_params);
- text = pdf_layout_fit_text(ctx, font, lang, "SIGN", rect);
- fz_fill_text(ctx, dev, text, fz_identity, cs, text_color, 1.0f, fz_default_color_params);
- fz_drop_text(ctx, text);
- text = NULL;
- }
- fz_always(ctx)
- {
- fz_drop_device(ctx, dev);
- fz_drop_path(ctx, path);
- fz_drop_text(ctx, text);
- fz_drop_font(ctx, font);
- }
- fz_catch(ctx)
- {
- fz_drop_display_list(ctx, dlist);
- fz_rethrow(ctx);
- }
- return dlist;
- }
- char *
- pdf_signature_info(fz_context *ctx, const char *name, pdf_pkcs7_distinguished_name *dn, const char *reason, const char *location, int64_t date, int include_labels)
- {
- fz_buffer *fzbuf = NULL;
- char *dn_str = NULL;
- char *full_str = NULL;
- time_t tdate = (time_t)date;
- fz_var(fzbuf);
- fz_var(dn_str);
- fz_try(ctx)
- {
- #ifdef _POSIX_SOURCE
- struct tm tmbuf, *tm = localtime_r(&tdate, &tmbuf);
- #else
- struct tm *tm = localtime(&tdate);
- #endif
- char now_str[40];
- size_t len = 0;
- #ifdef CLUSTER
- memset(&date, 0, sizeof(date));
- memset(tm, 0, sizeof(*tm));
- #endif
- fzbuf = fz_new_buffer(ctx, 256);
- if (name && strlen(name) > 0)
- {
- if (include_labels)
- fz_append_string(ctx, fzbuf, "Digitally signed by ");
- fz_append_string(ctx, fzbuf, name);
- }
- if (dn)
- {
- fz_append_string(ctx, fzbuf, "\n");
- if (include_labels)
- fz_append_string(ctx, fzbuf, "DN: ");
- dn_str = pdf_signature_format_distinguished_name(ctx, dn);
- fz_append_string(ctx, fzbuf, dn_str);
- }
- if (reason && strlen(reason) > 0)
- {
- fz_append_string(ctx, fzbuf, "\n");
- if (include_labels)
- fz_append_string(ctx, fzbuf, "Reason: ");
- fz_append_string(ctx, fzbuf, reason);
- }
- if (location && strlen(location) > 0)
- {
- fz_append_string(ctx, fzbuf, "\n");
- if (include_labels)
- fz_append_string(ctx, fzbuf, "Location: ");
- fz_append_string(ctx, fzbuf, location);
- }
- if (date >= 0)
- {
- len = strftime(now_str, sizeof now_str, "%FT%T%z", tm);
- if (len)
- {
- fz_append_string(ctx, fzbuf, "\n");
- if (include_labels)
- fz_append_string(ctx, fzbuf, "Date: ");
- fz_append_string(ctx, fzbuf, now_str);
- }
- }
- fz_terminate_buffer(ctx, fzbuf);
- (void)fz_buffer_extract(ctx, fzbuf, (unsigned char **)&full_str);
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, fzbuf);
- fz_free(ctx, dn_str);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- return full_str;
- }
- void
- pdf_annot_push_local_xref(fz_context *ctx, pdf_annot *annot)
- {
- pdf_document *doc;
- if (!annot->page)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
- doc = annot->page->doc;
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- if (doc->local_xref_nesting == 0 && doc->local_xref)
- fz_write_printf(ctx, fz_stddbg(ctx), "push local_xref for annot\n");
- #endif
- doc->local_xref_nesting++;
- }
- void
- pdf_annot_ensure_local_xref(fz_context *ctx, pdf_annot *annot)
- {
- pdf_document *doc = annot->page->doc;
- if (doc->local_xref != NULL)
- return;
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- fz_write_printf(ctx, fz_stddbg(ctx), "creating local_xref\n");
- #endif
- /* We have no local_xref, but we want to be using one. */
- /* First off, create one. */
- doc->local_xref = pdf_new_local_xref(ctx, doc);
- }
- void
- pdf_annot_pop_local_xref(fz_context *ctx, pdf_annot *annot)
- {
- pdf_document *doc = annot->page->doc;
- --doc->local_xref_nesting;
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- if (doc->local_xref_nesting == 0 && doc->local_xref)
- fz_write_printf(ctx, fz_stddbg(ctx), "pop local_xref for annot\n");
- #endif
- }
- void pdf_annot_pop_and_discard_local_xref(fz_context *ctx, pdf_annot *annot)
- {
- pdf_document *doc = annot->page->doc;
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- if (doc->local_xref)
- fz_write_printf(ctx, fz_stddbg(ctx), "pop and discard local_xref for annot\n");
- #endif
- --doc->local_xref_nesting;
- assert(doc->local_xref_nesting == 0);
- pdf_drop_local_xref_and_resources(ctx, doc);
- }
- static void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
- {
- pdf_obj *subtype;
- pdf_obj *ft = NULL;
- pdf_obj *ap_n;
- int pop_local_xref = 1;
- retry_after_repair:
- /* Must have any local xref in place in order to check if it's dirty. */
- pdf_annot_push_local_xref(ctx, annot);
- pdf_begin_implicit_operation(ctx, annot->page->doc);
- fz_start_throw_on_repair(ctx);
- fz_var(pop_local_xref);
- fz_try(ctx)
- {
- int needs_resynth;
- int local_synthesis = 0;
- /* Never update Popup and Link annotations */
- subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
- if (subtype == PDF_NAME(Popup) || subtype == PDF_NAME(Link))
- {
- pdf_end_operation(ctx, annot->page->doc);
- break;
- }
- /* Never update signed Signature widgets */
- if (subtype == PDF_NAME(Widget))
- {
- ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
- if (ft == PDF_NAME(Sig))
- {
- /* We cannot synthesise an appearance for a signed Sig, so don't even try. */
- if (pdf_signature_is_signed(ctx, annot->page->doc, annot->obj))
- {
- pdf_end_operation(ctx, annot->page->doc);
- break;
- }
- }
- }
- /* Check if the field is dirtied by JS events */
- if (pdf_obj_is_dirty(ctx, annot->obj))
- pdf_annot_request_resynthesis(ctx, annot);
- /* Find the current appearance stream, if one exists. */
- ap_n = pdf_annot_ap(ctx, annot);
- /* If there is no appearance stream, we need to create a local one for display purposes. */
- if (!ap_n)
- local_synthesis = 1;
- /* Ignore appearance streams not created by us (so not local)
- * for unsigned digital signature widgets. They are often blank
- * and we want the "sign" arrow to be visible. Never write back
- * the forced appearance stream for unsigned signatures. */
- if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig))
- {
- if (ap_n && !pdf_is_local_object(ctx, annot->page->doc, ap_n))
- local_synthesis = 1;
- }
- /* We need to put this appearance stream back into the document. */
- needs_resynth = pdf_annot_needs_resynthesis(ctx, annot);
- if (needs_resynth)
- local_synthesis = 0;
- /* Some appearances can NEVER be resynthesised. Spot those here. */
- if (needs_resynth)
- {
- if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Stamp)))
- {
- /* Don't resynthesize Stamps with non-standard names if
- * they already have an appearance. These usually have
- * custom images already set for their appearance.
- */
- if (!pdf_annot_is_standard_stamp(ctx, annot) && ap_n)
- {
- /* However, we allow changing the Rect even if we don't
- * resynthesize the appearance. This should also count
- * as having a changed appearance. */
- pdf_set_annot_resynthesised(ctx, annot);
- needs_resynth = 0;
- }
- }
- }
- if (local_synthesis || needs_resynth)
- {
- fz_display_list *dlist;
- fz_rect rect, bbox;
- fz_buffer *buf;
- pdf_obj *res = NULL;
- pdf_obj *new_ap_n = NULL;
- fz_var(res);
- fz_var(new_ap_n);
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- fz_write_printf(ctx, fz_stddbg(ctx), "Update Appearance: %d\n", pdf_to_num(ctx, annot->obj));
- pdf_debug_obj(ctx, annot->obj);
- fz_write_printf(ctx, fz_stddbg(ctx), "\n");
- #endif
- if (local_synthesis)
- {
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- fz_write_printf(ctx, fz_stddbg(ctx), "Local synthesis\n");
- #endif
- pdf_annot_ensure_local_xref(ctx, annot);
- }
- else
- {
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- fz_write_printf(ctx, fz_stddbg(ctx), "Non-Local synthesis\n");
- #endif
- /* We don't want to be using any local xref, so
- * bin any that we have. */
- pdf_annot_pop_and_discard_local_xref(ctx, annot);
- /* Binning the xref may leave ap_n holding an invalid pointer.
- * We don't use it from this point onwards anymore, but beware
- * in future code changes. */
- pop_local_xref = 0;
- }
- if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Btn))
- {
- /* Special case for Btn widgets that need multiple appearance streams. */
- pdf_update_button_appearance(ctx, annot);
- }
- else if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig))
- {
- /* Special case for unsigned signature widgets,
- * which are most easily created via a display list. */
- rect = pdf_annot_rect(ctx, annot);
- dlist = pdf_signature_appearance_unsigned(ctx, rect, pdf_annot_language(ctx, annot));
- fz_try(ctx)
- pdf_set_annot_appearance_from_display_list(ctx, annot, "N", NULL, fz_identity, dlist);
- fz_always(ctx)
- fz_drop_display_list(ctx, dlist);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- else
- {
- buf = fz_new_buffer(ctx, 1024);
- fz_try(ctx)
- {
- fz_matrix matrix = fz_identity;
- rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
- pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res);
- pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
- pdf_set_annot_appearance(ctx, annot, "N", NULL, matrix, bbox, res, buf);
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, buf);
- pdf_drop_obj(ctx, res);
- pdf_drop_obj(ctx, new_ap_n);
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_REPAIRED);
- fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
- fz_report_error(ctx);
- fz_warn(ctx, "cannot create appearance stream");
- }
- }
- #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
- fz_write_printf(ctx, fz_stddbg(ctx), "Annot obj after synthesis\n");
- pdf_debug_obj(ctx, annot->obj);
- fz_write_printf(ctx, fz_stddbg(ctx), "\nAppearance after synthesis\n");
- pdf_debug_obj(ctx, pdf_dict_getp(ctx, annot->obj, "AP/N"));
- fz_write_printf(ctx, fz_stddbg(ctx), "\n");
- #endif
- }
- pdf_clean_obj(ctx, annot->obj);
- pdf_end_operation(ctx, annot->page->doc);
- }
- fz_always(ctx)
- {
- if (pop_local_xref)
- pdf_annot_pop_local_xref(ctx, annot);
- fz_end_throw_on_repair(ctx);
- }
- fz_catch(ctx)
- {
- pdf_abandon_operation(ctx, annot->page->doc);
- /* If we hit a repair while synthesising, we need to give it another
- * go. Do that directly here, rather than waiting for the next time
- * we are called, because we don't want to risk discarding any
- * local_xrefs on the second pass through the list of annotations.
- * Repairs only ever happen once for a document, so no infinite
- * loop potential here. */
- if (fz_caught(ctx) == FZ_ERROR_REPAIRED)
- {
- fz_report_error(ctx);
- goto retry_after_repair;
- }
- fz_rethrow(ctx);
- }
- }
- static void *
- update_appearances(fz_context *ctx, fz_page *page_, void *state)
- {
- pdf_page *page = (pdf_page *)page_;
- pdf_annot *annot;
- for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot))
- pdf_update_appearance(ctx, annot);
- for (annot = pdf_first_widget(ctx, page); annot; annot = pdf_next_widget(ctx, annot))
- pdf_update_appearance(ctx, annot);
- return NULL;
- }
- static void
- update_all_appearances(fz_context *ctx, pdf_page *page)
- {
- pdf_document *doc = page->doc;
- /* Update all the annotations on all the pages open in the document.
- * At least one annotation should be resynthesised because we'll
- * only reach here if resynth_required was set. Any such resynthesis
- * that changes the document will throw away any local_xref. */
- fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL);
- /* If the page isn't linked in yet (as is the case whilst loading
- * the annots for a page), process that too. */
- if (page->super.prev == NULL && page->super.next == NULL)
- update_appearances(ctx, &page->super, NULL);
- /* Run it a second time, so that any annotations whose synthesised
- * appearances live in the local_xref (such as unsigned sigs) can
- * be regenerated too. Running this for annots which are up to date
- * should be fast. */
- fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL);
- /* And cope with a non-linked in page again. */
- if (page->super.prev == NULL && page->super.next == NULL)
- update_appearances(ctx, &page->super, NULL);
- doc->resynth_required = 0;
- }
- int
- pdf_update_annot(fz_context *ctx, pdf_annot *annot)
- {
- int changed;
- if (!annot->page)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
- if (annot->page->doc->resynth_required)
- update_all_appearances(ctx, annot->page);
- changed = annot->has_new_ap;
- annot->has_new_ap = 0;
- return changed;
- }
|