| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549 |
- // 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 <string.h>
- /* Must be kept in sync with definitions in pdf_util.js */
- enum
- {
- Display_Visible,
- Display_Hidden,
- Display_NoPrint,
- Display_NoView
- };
- enum
- {
- SigFlag_SignaturesExist = 1,
- SigFlag_AppendOnly = 2
- };
- const char *pdf_field_value(fz_context *ctx, pdf_obj *field)
- {
- pdf_obj *v = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V));
- if (pdf_is_name(ctx, v))
- return pdf_to_name(ctx, v);
- if (pdf_is_stream(ctx, v))
- {
- // FIXME: pdf_dict_put_inheritable...
- char *str = pdf_new_utf8_from_pdf_stream_obj(ctx, v);
- fz_try(ctx)
- pdf_dict_put_text_string(ctx, field, PDF_NAME(V), str);
- fz_always(ctx)
- fz_free(ctx, str);
- fz_catch(ctx)
- fz_rethrow(ctx);
- v = pdf_dict_get(ctx, field, PDF_NAME(V));
- }
- return pdf_to_text_string(ctx, v);
- }
- int pdf_field_flags(fz_context *ctx, pdf_obj *obj)
- {
- return pdf_dict_get_inheritable_int(ctx, obj, PDF_NAME(Ff));
- }
- int pdf_field_type(fz_context *ctx, pdf_obj *obj)
- {
- pdf_obj *type = pdf_dict_get_inheritable(ctx, obj, PDF_NAME(FT));
- int flags = pdf_field_flags(ctx, obj);
- if (pdf_name_eq(ctx, type, PDF_NAME(Btn)))
- {
- if (flags & PDF_BTN_FIELD_IS_PUSHBUTTON)
- return PDF_WIDGET_TYPE_BUTTON;
- else if (flags & PDF_BTN_FIELD_IS_RADIO)
- return PDF_WIDGET_TYPE_RADIOBUTTON;
- else
- return PDF_WIDGET_TYPE_CHECKBOX;
- }
- else if (pdf_name_eq(ctx, type, PDF_NAME(Tx)))
- return PDF_WIDGET_TYPE_TEXT;
- else if (pdf_name_eq(ctx, type, PDF_NAME(Ch)))
- {
- if (flags & PDF_CH_FIELD_IS_COMBO)
- return PDF_WIDGET_TYPE_COMBOBOX;
- else
- return PDF_WIDGET_TYPE_LISTBOX;
- }
- else if (pdf_name_eq(ctx, type, PDF_NAME(Sig)))
- return PDF_WIDGET_TYPE_SIGNATURE;
- else
- return PDF_WIDGET_TYPE_BUTTON;
- }
- const char *pdf_field_type_string(fz_context *ctx, pdf_obj *obj)
- {
- switch (pdf_field_type(ctx, obj))
- {
- default:
- case PDF_WIDGET_TYPE_BUTTON: return "button";
- case PDF_WIDGET_TYPE_CHECKBOX: return "checkbox";
- case PDF_WIDGET_TYPE_COMBOBOX: return "combobox";
- case PDF_WIDGET_TYPE_LISTBOX: return "listbox";
- case PDF_WIDGET_TYPE_RADIOBUTTON: return "radiobutton";
- case PDF_WIDGET_TYPE_SIGNATURE: return "signature";
- case PDF_WIDGET_TYPE_TEXT: return "text";
- }
- }
- /* Find the point in a field hierarchy where all descendants
- * share the same name */
- static pdf_obj *find_head_of_field_group(fz_context *ctx, pdf_obj *obj)
- {
- if (obj == NULL || pdf_dict_get(ctx, obj, PDF_NAME(T)))
- return obj;
- else
- return find_head_of_field_group(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Parent)));
- }
- static void pdf_field_mark_dirty(fz_context *ctx, pdf_obj *field)
- {
- pdf_document *doc = pdf_get_bound_document(ctx, field);
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- if (kids)
- {
- int i, n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- pdf_field_mark_dirty(ctx, pdf_array_get(ctx, kids, i));
- }
- pdf_dirty_obj(ctx, field);
- if (doc)
- doc->resynth_required = 1;
- }
- static void update_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *obj, const char *text)
- {
- const char *old_text;
- pdf_obj *grp;
- if (!text)
- text = "";
- /* All fields of the same name should be updated, so
- * set the value at the head of the group */
- grp = find_head_of_field_group(ctx, obj);
- if (grp)
- obj = grp;
- /* Only update if we change the actual value. */
- old_text = pdf_dict_get_text_string(ctx, obj, PDF_NAME(V));
- if (old_text && !strcmp(old_text, text))
- return;
- pdf_dict_put_text_string(ctx, obj, PDF_NAME(V), text);
- pdf_field_mark_dirty(ctx, obj);
- }
- static pdf_obj *
- pdf_lookup_field_imp(fz_context *ctx, pdf_obj *arr, const char *str, pdf_cycle_list *cycle_up);
- static pdf_obj *
- lookup_field_sub(fz_context *ctx, pdf_obj *dict, const char *str, pdf_cycle_list *cycle_up)
- {
- pdf_obj *kids;
- pdf_obj *name;
- name = pdf_dict_get(ctx, dict, PDF_NAME(T));
- /* If we have a name, check it matches. If it matches, consume that
- * portion of str. If not, exit. */
- if (name)
- {
- const char *match = pdf_to_text_string(ctx, name);
- const char *e = str;
- size_t len;
- while (*e && *e != '.')
- e++;
- len = e-str;
- if (strncmp(str, match, len) != 0 || (match[len] != 0 && match[len] != '.'))
- /* name doesn't match. */
- return NULL;
- str = e;
- if (*str == '.')
- str++;
- }
- /* If there is a kids array, but the search string is not empty, we have
- encountered an internal field which represents a set of terminal fields. */
- /* If there is a kids array and the search string is not empty,
- walk those looking for the appropriate one. */
- kids = pdf_dict_get(ctx, dict, PDF_NAME(Kids));
- if (kids && *str != 0)
- return pdf_lookup_field_imp(ctx, kids, str, cycle_up);
- /* The field may be a terminal or an internal field at this point.
- Accept it as the match if the match string is exhausted. */
- if (*str == 0)
- return dict;
- return NULL;
- }
- static pdf_obj *
- pdf_lookup_field_imp(fz_context *ctx, pdf_obj *arr, const char *str, pdf_cycle_list *cycle_up)
- {
- pdf_cycle_list cycle;
- int len = pdf_array_len(ctx, arr);
- int i;
- for (i = 0; i < len; i++)
- {
- pdf_obj *k = pdf_array_get(ctx, arr, i);
- pdf_obj *found;
- if (pdf_cycle(ctx, &cycle, cycle_up, k))
- fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in fields");
- found = lookup_field_sub(ctx, k, str, &cycle);
- if (found)
- return found;
- }
- return NULL;
- }
- pdf_obj *
- pdf_lookup_field(fz_context *ctx, pdf_obj *arr, const char *str)
- {
- return pdf_lookup_field_imp(ctx, arr, str, NULL);
- }
- static void reset_form_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- /* Set V to DV wherever DV is present, and delete V where DV is not.
- * FIXME: we assume for now that V has not been set unequal
- * to DV higher in the hierarchy than "field".
- *
- * At the bottom of the hierarchy we may find widget annotations
- * that aren't also fields, but DV and V will not be present in their
- * dictionaries, and attempts to remove V will be harmless. */
- pdf_obj *dv = pdf_dict_get(ctx, field, PDF_NAME(DV));
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- if (dv)
- pdf_dict_put(ctx, field, PDF_NAME(V), dv);
- else
- pdf_dict_del(ctx, field, PDF_NAME(V));
- if (kids == NULL)
- {
- /* The leaves of the tree are widget annotations
- * In some cases we need to update the appearance state;
- * in others we need to mark the field as dirty so that
- * the appearance stream will be regenerated. */
- switch (pdf_field_type(ctx, field))
- {
- case PDF_WIDGET_TYPE_CHECKBOX:
- case PDF_WIDGET_TYPE_RADIOBUTTON:
- {
- pdf_obj *leafv = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V));
- pdf_obj *ap = pdf_dict_get(ctx, field, PDF_NAME(AP));
- pdf_obj *n = pdf_dict_get(ctx, ap, PDF_NAME(N));
- /* Value does not refer to any appearance state in the
- normal appearance stream dictionary, default to Off instead. */
- if (pdf_is_dict(ctx, n) && !pdf_dict_get(ctx, n, leafv))
- leafv = NULL;
- if (!pdf_is_name(ctx, leafv))
- leafv = PDF_NAME(Off);
- pdf_dict_put(ctx, field, PDF_NAME(AS), leafv);
- }
- pdf_field_mark_dirty(ctx, field);
- break;
- case PDF_WIDGET_TYPE_BUTTON:
- case PDF_WIDGET_TYPE_SIGNATURE:
- /* Pushbuttons and signatures have no value to reset. */
- break;
- default:
- pdf_field_mark_dirty(ctx, field);
- break;
- }
- }
- }
- void pdf_field_reset(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- reset_form_field(ctx, doc, field);
- if (kids)
- {
- int i, n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- pdf_field_reset(ctx, doc, pdf_array_get(ctx, kids, i));
- }
- }
- static void add_field_hierarchy_to_array(fz_context *ctx, pdf_obj *array, pdf_obj *field, pdf_obj *fields, int exclude)
- {
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- char *needle = pdf_load_field_name(ctx, field);
- int i, n;
- fz_try(ctx)
- {
- n = pdf_array_len(ctx, fields);
- for (i = 0; i < n; i++)
- {
- char *name = pdf_load_field_name(ctx, pdf_array_get(ctx, fields, i));
- int found = !strcmp(needle, name);
- fz_free(ctx, name);
- if (found)
- break;
- }
- }
- fz_always(ctx)
- fz_free(ctx, needle);
- fz_catch(ctx)
- fz_rethrow(ctx);
- if ((exclude && i < n) || (!exclude && i == n))
- return;
- pdf_array_push(ctx, array, field);
- if (kids)
- {
- n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- add_field_hierarchy_to_array(ctx, array, pdf_array_get(ctx, kids, i), fields, exclude);
- }
- }
- /*
- When resetting or submitting a form, the fields to act upon are defined
- by an array of either field references or field names, plus a flag determining
- whether to act upon the fields in the array, or all fields other than those in
- the array. specified_fields interprets this information and produces the array
- of fields to be acted upon.
- */
- static pdf_obj *specified_fields(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
- {
- pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL);
- int i, n;
- pdf_obj *result = pdf_new_array(ctx, doc, 0);
- fz_try(ctx)
- {
- n = pdf_array_len(ctx, fields);
- for (i = 0; i < n; i++)
- {
- pdf_obj *field = pdf_array_get(ctx, fields, i);
- if (pdf_is_string(ctx, field))
- field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
- if (field)
- add_field_hierarchy_to_array(ctx, result, field, fields, exclude);
- }
- }
- fz_catch(ctx)
- {
- pdf_drop_obj(ctx, result);
- fz_rethrow(ctx);
- }
- return result;
- }
- void pdf_reset_form(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
- {
- pdf_obj *sfields = specified_fields(ctx, doc, fields, exclude);
- fz_try(ctx)
- {
- int i, n = pdf_array_len(ctx, sfields);
- for (i = 0; i < n; i++)
- reset_form_field(ctx, doc, pdf_array_get(ctx, sfields, i));
- doc->recalculate = 1;
- }
- fz_always(ctx)
- pdf_drop_obj(ctx, sfields);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- typedef struct
- {
- pdf_obj *pageobj;
- pdf_obj *chk;
- } lookup_state;
- static void *find_widget_on_page(fz_context *ctx, fz_page *page_, void *state_)
- {
- lookup_state *state = (lookup_state *) state_;
- pdf_page *page = (pdf_page *) page_;
- pdf_annot *widget;
- if (state->pageobj && pdf_objcmp_resolve(ctx, state->pageobj, page->obj))
- return NULL;
- for (widget = pdf_first_widget(ctx, page); widget != NULL; widget = pdf_next_widget(ctx, widget))
- {
- if (!pdf_objcmp_resolve(ctx, state->chk, widget->obj))
- return widget;
- }
- return NULL;
- }
- static pdf_annot *find_widget(fz_context *ctx, pdf_document *doc, pdf_obj *chk)
- {
- lookup_state state;
- state.pageobj = pdf_dict_get(ctx, chk, PDF_NAME(P));
- state.chk = chk;
- return fz_process_opened_pages(ctx, (fz_document *) doc, find_widget_on_page, &state);
- }
- static void set_check(fz_context *ctx, pdf_document *doc, pdf_obj *chk, pdf_obj *name)
- {
- pdf_obj *n = pdf_dict_getp(ctx, chk, "AP/N");
- pdf_obj *val;
- /* If name is a possible value of this check
- * box then use it, otherwise use "Off" */
- if (pdf_dict_get(ctx, n, name))
- val = name;
- else
- val = PDF_NAME(Off);
- if (pdf_name_eq(ctx, pdf_dict_get(ctx, chk, PDF_NAME(AS)), val))
- return;
- pdf_dict_put(ctx, chk, PDF_NAME(AS), val);
- pdf_set_annot_has_changed(ctx, find_widget(ctx, doc, chk));
- }
- /* Set the values of all fields in a group defined by a node
- * in the hierarchy */
- static void set_check_grp(fz_context *ctx, pdf_document *doc, pdf_obj *grp, pdf_obj *val)
- {
- pdf_obj *kids = pdf_dict_get(ctx, grp, PDF_NAME(Kids));
- if (kids == NULL)
- {
- set_check(ctx, doc, grp, val);
- }
- else
- {
- int i, n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- set_check_grp(ctx, doc, pdf_array_get(ctx, kids, i), val);
- }
- }
- void pdf_calculate_form(fz_context *ctx, pdf_document *doc)
- {
- if (doc->js)
- {
- fz_try(ctx)
- {
- pdf_obj *co = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/CO");
- int i, n = pdf_array_len(ctx, co);
- for (i = 0; i < n; i++)
- {
- pdf_obj *field = pdf_array_get(ctx, co, i);
- pdf_field_event_calculate(ctx, doc, field);
- }
- }
- fz_always(ctx)
- doc->recalculate = 0;
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- }
- static pdf_obj *find_on_state(fz_context *ctx, pdf_obj *dict)
- {
- int i, n = pdf_dict_len(ctx, dict);
- for (i = 0; i < n; ++i)
- {
- pdf_obj *key = pdf_dict_get_key(ctx, dict, i);
- if (key != PDF_NAME(Off))
- return key;
- }
- return NULL;
- }
- pdf_obj *pdf_button_field_on_state(fz_context *ctx, pdf_obj *field)
- {
- pdf_obj *ap = pdf_dict_get(ctx, field, PDF_NAME(AP));
- pdf_obj *on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(N)));
- if (!on) on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(D)));
- if (!on) on = PDF_NAME(Yes);
- return on;
- }
- static void
- begin_annot_op(fz_context *ctx, pdf_annot *annot, const char *op)
- {
- if (!annot->page)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
- pdf_begin_operation(ctx, annot->page->doc, op);
- }
- static void
- end_annot_op(fz_context *ctx, pdf_annot *annot)
- {
- pdf_end_operation(ctx, annot->page->doc);
- }
- static void
- abandon_annot_op(fz_context *ctx, pdf_annot *annot)
- {
- pdf_abandon_operation(ctx, annot->page->doc);
- }
- static void toggle_check_box(fz_context *ctx, pdf_annot *annot)
- {
- pdf_document *doc = annot->page->doc;
- begin_annot_op(ctx, annot, "Toggle checkbox");
- fz_try(ctx)
- {
- pdf_obj *field = annot->obj;
- int ff = pdf_field_flags(ctx, field);
- int is_radio = (ff & PDF_BTN_FIELD_IS_RADIO);
- int is_no_toggle_to_off = (ff & PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF);
- pdf_obj *grp, *as, *val;
- grp = find_head_of_field_group(ctx, field);
- if (!grp)
- grp = field;
- /* TODO: check V value as well as or instead of AS? */
- as = pdf_dict_get(ctx, field, PDF_NAME(AS));
- if (as && as != PDF_NAME(Off))
- {
- if (is_radio && is_no_toggle_to_off)
- {
- end_annot_op(ctx, annot);
- break;
- }
- val = PDF_NAME(Off);
- }
- else
- {
- val = pdf_button_field_on_state(ctx, field);
- }
- pdf_dict_put(ctx, grp, PDF_NAME(V), val);
- set_check_grp(ctx, doc, grp, val);
- doc->recalculate = 1;
- end_annot_op(ctx, annot);
- }
- fz_catch(ctx)
- {
- abandon_annot_op(ctx, annot);
- fz_rethrow(ctx);
- }
- pdf_set_annot_has_changed(ctx, annot);
- }
- int pdf_has_unsaved_changes(fz_context *ctx, pdf_document *doc)
- {
- int i;
- if (doc->num_incremental_sections == 0)
- return 0;
- for (i = 0; i < doc->xref_sections->num_objects; i++)
- if (doc->xref_sections->subsec->table[i].type != 0)
- break;
- return i != doc->xref_sections->num_objects;
- }
- int pdf_was_repaired(fz_context *ctx, pdf_document *doc)
- {
- return doc->repair_attempted;
- }
- int pdf_toggle_widget(fz_context *ctx, pdf_annot *widget)
- {
- switch (pdf_widget_type(ctx, widget))
- {
- default:
- return 0;
- case PDF_WIDGET_TYPE_CHECKBOX:
- case PDF_WIDGET_TYPE_RADIOBUTTON:
- toggle_check_box(ctx, widget);
- return 1;
- }
- }
- int
- pdf_update_page(fz_context *ctx, pdf_page *page)
- {
- pdf_annot *annot;
- pdf_annot *widget;
- int changed = 0;
- fz_try(ctx)
- {
- pdf_begin_implicit_operation(ctx, page->doc);
- if (page->doc->recalculate)
- pdf_calculate_form(ctx, page->doc);
- for (annot = page->annots; annot; annot = annot->next)
- if (pdf_update_annot(ctx, annot))
- changed = 1;
- for (widget = page->widgets; widget; widget = widget->next)
- if (pdf_update_annot(ctx, widget))
- changed = 1;
- pdf_end_operation(ctx, page->doc);
- }
- fz_catch(ctx)
- {
- pdf_abandon_operation(ctx, page->doc);
- fz_rethrow(ctx);
- }
- return changed;
- }
- pdf_annot *pdf_first_widget(fz_context *ctx, pdf_page *page)
- {
- return page->widgets;
- }
- pdf_annot *pdf_next_widget(fz_context *ctx, pdf_annot *widget)
- {
- return widget->next;
- }
- enum pdf_widget_type pdf_widget_type(fz_context *ctx, pdf_annot *widget)
- {
- enum pdf_widget_type ret = PDF_WIDGET_TYPE_BUTTON;
- pdf_annot_push_local_xref(ctx, widget);
- fz_try(ctx)
- {
- pdf_obj *subtype = pdf_dict_get(ctx, widget->obj, PDF_NAME(Subtype));
- if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
- ret = pdf_field_type(ctx, widget->obj);
- }
- fz_always(ctx)
- pdf_annot_pop_local_xref(ctx, widget);
- fz_catch(ctx)
- fz_rethrow(ctx);
- return ret;
- }
- static int set_validated_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events)
- {
- char *newtext = NULL;
- if (!ignore_trigger_events)
- {
- if (!pdf_field_event_validate(ctx, doc, field, text, &newtext))
- return 0;
- }
- update_field_value(ctx, doc, field, newtext ? newtext : text);
- fz_free(ctx, newtext);
- return 1;
- }
- static void update_checkbox_selector(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val)
- {
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- if (kids)
- {
- int i, n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- update_checkbox_selector(ctx, doc, pdf_array_get(ctx, kids, i), val);
- }
- else
- {
- pdf_obj *n = pdf_dict_getp(ctx, field, "AP/N");
- pdf_obj *oval;
- if (pdf_dict_gets(ctx, n, val))
- oval = pdf_new_name(ctx, val);
- else
- oval = PDF_NAME(Off);
- pdf_dict_put_drop(ctx, field, PDF_NAME(AS), oval);
- }
- }
- static int set_checkbox_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val)
- {
- update_checkbox_selector(ctx, doc, field, val);
- update_field_value(ctx, doc, field, val);
- return 1;
- }
- int pdf_set_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events)
- {
- int accepted = 0;
- switch (pdf_field_type(ctx, field))
- {
- case PDF_WIDGET_TYPE_TEXT:
- case PDF_WIDGET_TYPE_COMBOBOX:
- case PDF_WIDGET_TYPE_LISTBOX:
- accepted = set_validated_field_value(ctx, doc, field, text, ignore_trigger_events);
- break;
- case PDF_WIDGET_TYPE_CHECKBOX:
- case PDF_WIDGET_TYPE_RADIOBUTTON:
- accepted = set_checkbox_value(ctx, doc, field, text);
- break;
- default:
- update_field_value(ctx, doc, field, text);
- accepted = 1;
- break;
- }
- if (!ignore_trigger_events)
- doc->recalculate = 1;
- return accepted;
- }
- char *pdf_field_border_style(fz_context *ctx, pdf_obj *field)
- {
- const char *bs = pdf_to_name(ctx, pdf_dict_getl(ctx, field, PDF_NAME(BS), PDF_NAME(S), NULL));
- switch (*bs)
- {
- case 'S': return "Solid";
- case 'D': return "Dashed";
- case 'B': return "Beveled";
- case 'I': return "Inset";
- case 'U': return "Underline";
- }
- return "Solid";
- }
- void pdf_field_set_border_style(fz_context *ctx, pdf_obj *field, const char *text)
- {
- pdf_obj *val;
- if (!strcmp(text, "Solid"))
- val = PDF_NAME(S);
- else if (!strcmp(text, "Dashed"))
- val = PDF_NAME(D);
- else if (!strcmp(text, "Beveled"))
- val = PDF_NAME(B);
- else if (!strcmp(text, "Inset"))
- val = PDF_NAME(I);
- else if (!strcmp(text, "Underline"))
- val = PDF_NAME(U);
- else
- return;
- pdf_dict_putl_drop(ctx, field, val, PDF_NAME(BS), PDF_NAME(S), NULL);
- pdf_field_mark_dirty(ctx, field);
- }
- void pdf_field_set_button_caption(fz_context *ctx, pdf_obj *field, const char *text)
- {
- if (pdf_field_type(ctx, field) == PDF_WIDGET_TYPE_BUTTON)
- {
- pdf_obj *val = pdf_new_text_string(ctx, text);
- pdf_dict_putl_drop(ctx, field, val, PDF_NAME(MK), PDF_NAME(CA), NULL);
- pdf_field_mark_dirty(ctx, field);
- }
- }
- int pdf_field_display(fz_context *ctx, pdf_obj *field)
- {
- pdf_obj *kids;
- int f, res = Display_Visible;
- /* Base response on first of children. Not ideal,
- * but not clear how to handle children with
- * differing values */
- while ((kids = pdf_dict_get(ctx, field, PDF_NAME(Kids))) != NULL)
- field = pdf_array_get(ctx, kids, 0);
- f = pdf_dict_get_int(ctx, field, PDF_NAME(F));
- if (f & PDF_ANNOT_IS_HIDDEN)
- {
- res = Display_Hidden;
- }
- else if (f & PDF_ANNOT_IS_PRINT)
- {
- if (f & PDF_ANNOT_IS_NO_VIEW)
- res = Display_NoView;
- }
- else
- {
- if (f & PDF_ANNOT_IS_NO_VIEW)
- res = Display_Hidden;
- else
- res = Display_NoPrint;
- }
- return res;
- }
- /*
- * get the field name in a char buffer that has spare room to
- * add more characters at the end.
- */
- static char *load_field_name(fz_context *ctx, pdf_obj *field, int spare, pdf_cycle_list *cycle_up)
- {
- pdf_cycle_list cycle;
- char *res = NULL;
- pdf_obj *parent;
- const char *lname;
- int llen;
- if (pdf_cycle(ctx, &cycle, cycle_up, field))
- fz_throw(ctx, FZ_ERROR_FORMAT, "Cycle in field parents");
- parent = pdf_dict_get(ctx, field, PDF_NAME(Parent));
- lname = pdf_dict_get_text_string(ctx, field, PDF_NAME(T));
- llen = (int)strlen(lname);
- // Limit fields to 16K
- if (llen > (16 << 10) || llen + spare > (16 << 10))
- fz_throw(ctx, FZ_ERROR_LIMIT, "Field name too long");
- /*
- * If we found a name at this point in the field hierarchy
- * then we'll need extra space for it and a dot
- */
- if (llen)
- spare += llen+1;
- if (parent)
- {
- res = load_field_name(ctx, parent, spare, &cycle);
- }
- else
- {
- res = Memento_label(fz_malloc(ctx, spare+1), "form_field_name");
- res[0] = 0;
- }
- if (llen)
- {
- if (res[0])
- strcat(res, ".");
- strcat(res, lname);
- }
- return res;
- }
- char *pdf_load_field_name(fz_context *ctx, pdf_obj *field)
- {
- return load_field_name(ctx, field, 0, NULL);
- }
- void pdf_create_field_name(fz_context *ctx, pdf_document *doc, const char *prefix, char *buf, size_t len)
- {
- pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc),
- PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL);
- int i;
- for (i = 0; i < 65536; ++i) {
- fz_snprintf(buf, len, "%s%d", prefix, i);
- if (!pdf_lookup_field(ctx, form, buf))
- return;
- }
- fz_throw(ctx, FZ_ERROR_LIMIT, "Could not create unique field name.");
- }
- const char *pdf_field_label(fz_context *ctx, pdf_obj *field)
- {
- pdf_obj *label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(TU));
- if (!label)
- label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(T));
- if (label)
- return pdf_to_text_string(ctx, label);
- return "Unnamed";
- }
- void pdf_field_set_display(fz_context *ctx, pdf_obj *field, int d)
- {
- pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
- if (!kids)
- {
- int mask = (PDF_ANNOT_IS_HIDDEN|PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW);
- int f = pdf_dict_get_int(ctx, field, PDF_NAME(F)) & ~mask;
- switch (d)
- {
- case Display_Visible:
- f |= PDF_ANNOT_IS_PRINT;
- break;
- case Display_Hidden:
- f |= PDF_ANNOT_IS_HIDDEN;
- break;
- case Display_NoView:
- f |= (PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW);
- break;
- case Display_NoPrint:
- break;
- }
- pdf_dict_put_int(ctx, field, PDF_NAME(F), f);
- }
- else
- {
- int i, n = pdf_array_len(ctx, kids);
- for (i = 0; i < n; i++)
- pdf_field_set_display(ctx, pdf_array_get(ctx, kids, i), d);
- }
- }
- void pdf_field_set_fill_color(fz_context *ctx, pdf_obj *field, pdf_obj *col)
- {
- /* col == NULL mean transparent, but we can simply pass it on as with
- * non-NULL values because pdf_dict_putp interprets a NULL value as
- * delete */
- pdf_dict_putl(ctx, field, col, PDF_NAME(MK), PDF_NAME(BG), NULL);
- pdf_field_mark_dirty(ctx, field);
- }
- void pdf_field_set_text_color(fz_context *ctx, pdf_obj *field, pdf_obj *col)
- {
- char buf[100];
- const char *font;
- float size, color[4];
- /* TODO? */
- const char *da = pdf_to_str_buf(ctx, pdf_dict_get_inheritable(ctx, field, PDF_NAME(DA)));
- int n;
- pdf_parse_default_appearance(ctx, da, &font, &size, &n, color);
- switch (pdf_array_len(ctx, col))
- {
- default:
- n = 0;
- color[0] = color[1] = color[2] = color[3] = 0;
- break;
- case 1:
- n = 1;
- color[0] = pdf_array_get_real(ctx, col, 0);
- break;
- case 3:
- n = 3;
- color[0] = pdf_array_get_real(ctx, col, 0);
- color[1] = pdf_array_get_real(ctx, col, 1);
- color[2] = pdf_array_get_real(ctx, col, 2);
- break;
- case 4:
- n = 4;
- color[0] = pdf_array_get_real(ctx, col, 0);
- color[1] = pdf_array_get_real(ctx, col, 1);
- color[2] = pdf_array_get_real(ctx, col, 2);
- color[3] = pdf_array_get_real(ctx, col, 3);
- break;
- }
- pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, n, color);
- pdf_dict_put_string(ctx, field, PDF_NAME(DA), buf, strlen(buf));
- pdf_field_mark_dirty(ctx, field);
- }
- pdf_annot *
- pdf_keep_widget(fz_context *ctx, pdf_annot *widget)
- {
- return pdf_keep_annot(ctx, widget);
- }
- void
- pdf_drop_widget(fz_context *ctx, pdf_annot *widget)
- {
- pdf_drop_annot(ctx, widget);
- }
- void
- pdf_drop_widgets(fz_context *ctx, pdf_annot *widget)
- {
- while (widget)
- {
- pdf_annot *next = widget->next;
- pdf_drop_widget(ctx, widget);
- widget = next;
- }
- }
- pdf_annot *
- pdf_create_signature_widget(fz_context *ctx, pdf_page *page, char *name)
- {
- fz_rect rect = { 12, 12, 12+100, 12+50 };
- pdf_annot *annot;
- pdf_begin_operation(ctx, page->doc, "Create signature");
- annot = pdf_create_annot_raw(ctx, page, PDF_ANNOT_WIDGET);
- fz_try(ctx)
- {
- pdf_obj *obj = annot->obj;
- pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, page->doc), PDF_NAME(Root));
- pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
- pdf_obj *fields, *lock;
- if (!acroform)
- {
- acroform = pdf_new_dict(ctx, page->doc, 1);
- pdf_dict_put_drop(ctx, root, PDF_NAME(AcroForm), acroform);
- }
- fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
- if (!fields)
- {
- fields = pdf_new_array(ctx, page->doc, 1);
- pdf_dict_put_drop(ctx, acroform, PDF_NAME(Fields), fields);
- }
- pdf_set_annot_rect(ctx, annot, rect);
- pdf_dict_put(ctx, obj, PDF_NAME(FT), PDF_NAME(Sig));
- pdf_dict_put_int(ctx, obj, PDF_NAME(F), PDF_ANNOT_IS_PRINT);
- pdf_dict_put_text_string(ctx, obj, PDF_NAME(DA), "/Helv 0 Tf 0 g");
- pdf_dict_put_text_string(ctx, obj, PDF_NAME(T), name);
- pdf_array_push(ctx, fields, obj);
- lock = pdf_dict_put_dict(ctx, obj, PDF_NAME(Lock), 1);
- pdf_dict_put(ctx, lock, PDF_NAME(Action), PDF_NAME(All));
- pdf_end_operation(ctx, page->doc);
- }
- fz_catch(ctx)
- {
- pdf_abandon_operation(ctx, page->doc);
- pdf_delete_annot(ctx, page, annot);
- }
- return (pdf_annot *)annot;
- }
- fz_rect
- pdf_bound_widget(fz_context *ctx, pdf_annot *widget)
- {
- return pdf_bound_annot(ctx, widget);
- }
- int
- pdf_update_widget(fz_context *ctx, pdf_annot *widget)
- {
- return pdf_update_annot(ctx, widget);
- }
- int pdf_text_widget_max_len(fz_context *ctx, pdf_annot *tw)
- {
- pdf_annot *annot = (pdf_annot *)tw;
- return pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen));
- }
- int pdf_text_widget_format(fz_context *ctx, pdf_annot *tw)
- {
- pdf_annot *annot = (pdf_annot *)tw;
- int type = PDF_WIDGET_TX_FORMAT_NONE;
- pdf_obj *js = pdf_dict_getl(ctx, annot->obj, PDF_NAME(AA), PDF_NAME(F), PDF_NAME(JS), NULL);
- if (js)
- {
- char *code = pdf_load_stream_or_string_as_utf8(ctx, js);
- if (strstr(code, "AFNumber_Format"))
- type = PDF_WIDGET_TX_FORMAT_NUMBER;
- else if (strstr(code, "AFSpecial_Format"))
- type = PDF_WIDGET_TX_FORMAT_SPECIAL;
- else if (strstr(code, "AFDate_FormatEx"))
- type = PDF_WIDGET_TX_FORMAT_DATE;
- else if (strstr(code, "AFTime_FormatEx"))
- type = PDF_WIDGET_TX_FORMAT_TIME;
- fz_free(ctx, code);
- }
- return type;
- }
- static char *
- merge_changes(fz_context *ctx, const char *value, int start, int end, const char *change)
- {
- int changelen = change ? (int)strlen(change) : 0;
- int valuelen = value ? (int)strlen(value) : 0;
- int prelen = (start >= 0 ? (start < valuelen ? start : valuelen) : 0);
- int postlen = (end >= 0 && end <= valuelen ? valuelen - end : 0);
- int newlen = prelen + changelen + postlen + 1;
- char *merged = fz_malloc(ctx, newlen);
- char *m = merged;
- if (prelen)
- {
- memcpy(m, value, prelen);
- m += prelen;
- }
- if (changelen)
- {
- memcpy(m, change, changelen);
- m += changelen;
- }
- if (postlen)
- {
- memcpy(m, &value[end], postlen);
- m += postlen;
- }
- *m = 0;
- return merged;
- }
- int pdf_set_text_field_value(fz_context *ctx, pdf_annot *widget, const char *update)
- {
- pdf_document *doc;
- pdf_keystroke_event evt = { 0 };
- char *new_change = NULL;
- char *new_value = NULL;
- char *merged_value = NULL;
- int rc = 1;
- begin_annot_op(ctx, widget, "Edit text field");
- doc = widget->page->doc;
- fz_var(new_value);
- fz_var(new_change);
- fz_var(merged_value);
- fz_try(ctx)
- {
- if (!widget->ignore_trigger_events)
- {
- evt.value = pdf_annot_field_value(ctx, widget);
- evt.change = update;
- evt.selStart = 0;
- evt.selEnd = (int)strlen(evt.value);
- evt.willCommit = 0;
- rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt);
- new_change = evt.newChange;
- new_value = evt.newValue;
- evt.newValue = NULL;
- evt.newChange = NULL;
- if (rc)
- {
- merged_value = merge_changes(ctx, new_value, evt.selStart, evt.selEnd, new_change);
- evt.value = merged_value;
- evt.change = "";
- evt.selStart = -1;
- evt.selEnd = -1;
- evt.willCommit = 1;
- rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt);
- if (rc)
- rc = pdf_set_annot_field_value(ctx, doc, widget, evt.newValue, 0);
- }
- }
- else
- {
- rc = pdf_set_annot_field_value(ctx, doc, widget, update, 1);
- }
- end_annot_op(ctx, widget);
- }
- fz_always(ctx)
- {
- fz_free(ctx, new_value);
- fz_free(ctx, evt.newValue);
- fz_free(ctx, new_change);
- fz_free(ctx, evt.newChange);
- fz_free(ctx, merged_value);
- }
- fz_catch(ctx)
- {
- abandon_annot_op(ctx, widget);
- fz_warn(ctx, "could not set widget text");
- rc = 0;
- }
- return rc;
- }
- int pdf_edit_text_field_value(fz_context *ctx, pdf_annot *widget, const char *value, const char *change, int *selStart, int *selEnd, char **result)
- {
- pdf_document *doc = widget->page->doc;
- pdf_keystroke_event evt = {0};
- int rc = 1;
- pdf_begin_operation(ctx, doc, "Text field keystroke");
- fz_try(ctx)
- {
- if (!widget->ignore_trigger_events)
- {
- evt.value = value;
- evt.change = change;
- evt.selStart = *selStart;
- evt.selEnd = *selEnd;
- evt.willCommit = 0;
- rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt);
- if (rc)
- {
- *result = merge_changes(ctx, evt.newValue, evt.selStart, evt.selEnd, evt.newChange);
- *selStart = evt.selStart + (int)strlen(evt.newChange);
- *selEnd = *selStart;
- }
- }
- else
- {
- *result = merge_changes(ctx, value, *selStart, *selEnd, change);
- *selStart = evt.selStart + (int)strlen(change);
- *selEnd = *selStart;
- }
- pdf_end_operation(ctx, doc);
- }
- fz_always(ctx)
- {
- fz_free(ctx, evt.newValue);
- fz_free(ctx, evt.newChange);
- }
- fz_catch(ctx)
- {
- pdf_abandon_operation(ctx, doc);
- fz_warn(ctx, "could not process text widget keystroke");
- rc = 0;
- }
- return rc;
- }
- int pdf_set_choice_field_value(fz_context *ctx, pdf_annot *widget, const char *new_value)
- {
- /* Choice widgets use almost the same keystroke processing as text fields. */
- return pdf_set_text_field_value(ctx, widget, new_value);
- }
- int pdf_choice_widget_options(fz_context *ctx, pdf_annot *tw, int exportval, const char *opts[])
- {
- pdf_annot *annot = (pdf_annot *)tw;
- pdf_obj *optarr;
- int i, n, m;
- optarr = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Opt));
- n = pdf_array_len(ctx, optarr);
- if (opts)
- {
- for (i = 0; i < n; i++)
- {
- m = pdf_array_len(ctx, pdf_array_get(ctx, optarr, i));
- /* If it is a two element array, the second item is the one that we want if we want the listing value. */
- if (m == 2)
- if (exportval)
- opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 0);
- else
- opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 1);
- else
- opts[i] = pdf_array_get_text_string(ctx, optarr, i);
- }
- }
- return n;
- }
- int pdf_choice_field_option_count(fz_context *ctx, pdf_obj *field)
- {
- pdf_obj *opt = pdf_dict_get_inheritable(ctx, field, PDF_NAME(Opt));
- return pdf_array_len(ctx, opt);
- }
- const char *pdf_choice_field_option(fz_context *ctx, pdf_obj *field, int export, int i)
- {
- pdf_obj *opt = pdf_dict_get_inheritable(ctx, field, PDF_NAME(Opt));
- pdf_obj *ent = pdf_array_get(ctx, opt, i);
- if (pdf_array_len(ctx, ent) == 2)
- return pdf_array_get_text_string(ctx, ent, export ? 0 : 1);
- else
- return pdf_to_text_string(ctx, ent);
- }
- int pdf_choice_widget_is_multiselect(fz_context *ctx, pdf_annot *tw)
- {
- pdf_annot *annot = (pdf_annot *)tw;
- if (!annot) return 0;
- switch (pdf_field_type(ctx, annot->obj))
- {
- case PDF_WIDGET_TYPE_LISTBOX:
- return (pdf_field_flags(ctx, annot->obj) & PDF_CH_FIELD_IS_MULTI_SELECT) != 0;
- default:
- return 0;
- }
- }
- int pdf_choice_widget_value(fz_context *ctx, pdf_annot *tw, const char *opts[])
- {
- pdf_annot *annot = (pdf_annot *)tw;
- pdf_obj *optarr;
- int i, n;
- if (!annot)
- return 0;
- optarr = pdf_dict_get(ctx, annot->obj, PDF_NAME(V));
- if (pdf_is_string(ctx, optarr))
- {
- if (opts)
- opts[0] = pdf_to_text_string(ctx, optarr);
- return 1;
- }
- else
- {
- n = pdf_array_len(ctx, optarr);
- if (opts)
- {
- for (i = 0; i < n; i++)
- {
- pdf_obj *elem = pdf_array_get(ctx, optarr, i);
- if (pdf_is_array(ctx, elem))
- elem = pdf_array_get(ctx, elem, 1);
- opts[i] = pdf_to_text_string(ctx, elem);
- }
- }
- return n;
- }
- }
- void pdf_choice_widget_set_value(fz_context *ctx, pdf_annot *tw, int n, const char *opts[])
- {
- pdf_annot *annot = (pdf_annot *)tw;
- pdf_obj *optarr = NULL;
- int i;
- if (!annot)
- return;
- begin_annot_op(ctx, annot, "Set choice");
- fz_var(optarr);
- fz_try(ctx)
- {
- if (n != 1)
- {
- optarr = pdf_new_array(ctx, annot->page->doc, n);
- for (i = 0; i < n; i++)
- pdf_array_push_text_string(ctx, optarr, opts[i]);
- pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(V), optarr);
- }
- else
- pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(V), opts[0]);
- /* FIXME: when n > 1, we should be regenerating the indexes */
- pdf_dict_del(ctx, annot->obj, PDF_NAME(I));
- pdf_field_mark_dirty(ctx, annot->obj);
- end_annot_op(ctx, annot);
- }
- fz_catch(ctx)
- {
- abandon_annot_op(ctx, annot);
- pdf_drop_obj(ctx, optarr);
- fz_rethrow(ctx);
- }
- }
- int pdf_signature_byte_range(fz_context *ctx, pdf_document *doc, pdf_obj *signature, fz_range *byte_range)
- {
- pdf_obj *br = pdf_dict_getl(ctx, signature, PDF_NAME(V), PDF_NAME(ByteRange), NULL);
- int i, n = pdf_array_len(ctx, br)/2;
- if (byte_range)
- {
- for (i = 0; i < n; i++)
- {
- int64_t offset = pdf_array_get_int(ctx, br, 2*i);
- int length = pdf_array_get_int(ctx, br, 2*i+1);
- if (offset < 0 || offset > doc->file_size)
- fz_throw(ctx, FZ_ERROR_FORMAT, "offset of signature byte range outside of file");
- else if (length < 0)
- fz_throw(ctx, FZ_ERROR_FORMAT, "length of signature byte range negative");
- else if (offset + length > doc->file_size)
- fz_throw(ctx, FZ_ERROR_FORMAT, "signature byte range extends past end of file");
- byte_range[i].offset = offset;
- byte_range[i].length = length;
- }
- }
- return n;
- }
- static int is_white(int c)
- {
- return c == '\x00' || c == '\x09' || c == '\x0a' || c == '\x0c' || c == '\x0d' || c == '\x20';
- }
- static int is_hex_or_white(int c)
- {
- return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9') || is_white(c);
- }
- static void validate_certificate_data(fz_context *ctx, pdf_document *doc, fz_range *hole)
- {
- fz_stream *stm;
- int c;
- stm = fz_open_range_filter(ctx, doc->file, hole, 1);
- fz_try(ctx)
- {
- while (is_white((c = fz_read_byte(ctx, stm))))
- ;
- if (c == '<')
- c = fz_read_byte(ctx, stm);
- while (is_hex_or_white(c))
- c = fz_read_byte(ctx, stm);
- if (c == '>')
- c = fz_read_byte(ctx, stm);
- while (is_white(c))
- c = fz_read_byte(ctx, stm);
- if (c != EOF)
- fz_throw(ctx, FZ_ERROR_FORMAT, "signature certificate data contains invalid character");
- if ((size_t)fz_tell(ctx, stm) != hole->length)
- fz_throw(ctx, FZ_ERROR_FORMAT, "premature end of signature certificate data");
- }
- fz_always(ctx)
- fz_drop_stream(ctx, stm);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- static int rangecmp(const void *a_, const void *b_)
- {
- const fz_range *a = (const fz_range *) a_;
- const fz_range *b = (const fz_range *) b_;
- return (int) (a->offset - b->offset);
- }
- static void validate_byte_ranges(fz_context *ctx, pdf_document *doc, fz_range *unsorted, int nranges)
- {
- int64_t offset = 0;
- fz_range *sorted;
- int i;
- sorted = fz_calloc(ctx, nranges, sizeof(*sorted));
- memcpy(sorted, unsorted, nranges * sizeof(*sorted));
- qsort(sorted, nranges, sizeof(*sorted), rangecmp);
- fz_try(ctx)
- {
- offset = 0;
- for (i = 0; i < nranges; i++)
- {
- if (sorted[i].offset > offset)
- {
- fz_range hole;
- hole.offset = offset;
- hole.length = sorted[i].offset - offset;
- validate_certificate_data(ctx, doc, &hole);
- }
- offset = fz_maxi64(offset, sorted[i].offset + sorted[i].length);
- }
- }
- fz_always(ctx)
- fz_free(ctx, sorted);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- fz_stream *pdf_signature_hash_bytes(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
- {
- fz_range *byte_range = NULL;
- int byte_range_len;
- fz_stream *bytes = NULL;
- fz_var(byte_range);
- fz_try(ctx)
- {
- byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL);
- if (byte_range_len)
- {
- byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range));
- pdf_signature_byte_range(ctx, doc, signature, byte_range);
- }
- validate_byte_ranges(ctx, doc, byte_range, byte_range_len);
- bytes = fz_open_range_filter(ctx, doc->file, byte_range, byte_range_len);
- }
- fz_always(ctx)
- {
- fz_free(ctx, byte_range);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- return bytes;
- }
- int pdf_incremental_change_since_signing_widget(fz_context *ctx, pdf_annot *widget)
- {
- if (!widget->page)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
- return pdf_signature_incremental_change_since_signing(ctx, widget->page->doc, widget->obj);
- }
- int pdf_signature_incremental_change_since_signing(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
- {
- fz_range *byte_range = NULL;
- int byte_range_len;
- int changed = 0;
- if (pdf_dict_get_inheritable(ctx, signature, PDF_NAME(FT)) != PDF_NAME(Sig))
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation is not a signature widget");
- if (!pdf_signature_is_signed(ctx, doc, signature))
- return 0;
- fz_var(byte_range);
- fz_try(ctx)
- {
- byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL);
- if (byte_range_len)
- {
- fz_range *last_range;
- int64_t end_of_range;
- byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range));
- pdf_signature_byte_range(ctx, doc, signature, byte_range);
- last_range = &byte_range[byte_range_len -1];
- end_of_range = last_range->offset + last_range->length;
- /* We can see how long the document was when signed by inspecting the byte
- * ranges of the signature. The document, when read in, may have already
- * had changes tagged on to it, past its extent when signed, or we may have
- * made changes since reading it, which will be held in a new incremental
- * xref section. */
- if (doc->file_size > end_of_range || doc->num_incremental_sections > 0)
- changed = 1;
- }
- }
- fz_always(ctx)
- {
- fz_free(ctx, byte_range);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- return changed;
- }
- int pdf_signature_is_signed(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- pdf_obj *v;
- pdf_obj* vtype;
- if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) != PDF_NAME(Sig))
- return 0;
- /* Signatures can only be signed if the value is a dictionary,
- * and if the value has a Type, it should be Sig. */
- v = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V));
- vtype = pdf_dict_get(ctx, v, PDF_NAME(Type));
- return pdf_is_dict(ctx, v) && (vtype ? pdf_name_eq(ctx, vtype, PDF_NAME(Sig)) : 1);
- }
- int pdf_widget_is_signed(fz_context *ctx, pdf_annot *widget)
- {
- if (widget == NULL)
- return 0;
- if (!widget->page)
- fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
- return pdf_signature_is_signed(ctx, widget->page->doc, widget->obj);
- }
- int pdf_widget_is_readonly(fz_context *ctx, pdf_annot *widget)
- {
- int fflags;
- if (widget == NULL)
- return 0;
- fflags = pdf_field_flags(ctx, ((pdf_annot *) widget)->obj);
- return fflags & PDF_FIELD_IS_READ_ONLY;
- }
- size_t pdf_signature_contents(fz_context *ctx, pdf_document *doc, pdf_obj *signature, char **contents)
- {
- pdf_obj *v_ref = pdf_dict_get_inheritable(ctx, signature, PDF_NAME(V));
- pdf_obj *v_obj = pdf_load_unencrypted_object(ctx, doc, pdf_to_num(ctx, v_ref));
- char *copy = NULL;
- size_t len;
- fz_var(copy);
- fz_try(ctx)
- {
- pdf_obj *c = pdf_dict_get(ctx, v_obj, PDF_NAME(Contents));
- char *s;
- s = pdf_to_str_buf(ctx, c);
- len = pdf_to_str_len(ctx, c);
- if (contents)
- {
- copy = Memento_label(fz_malloc(ctx, len), "sig_contents");
- memcpy(copy, s, len);
- }
- }
- fz_always(ctx)
- pdf_drop_obj(ctx, v_obj);
- fz_catch(ctx)
- {
- fz_free(ctx, copy);
- fz_rethrow(ctx);
- }
- if (contents)
- *contents = copy;
- return len;
- }
- static fz_xml_doc *load_xfa(fz_context *ctx, pdf_document *doc)
- {
- pdf_obj *xfa;
- fz_buffer *buf = NULL;
- fz_buffer *packet = NULL;
- int i;
- if (doc->xfa)
- return doc->xfa; /* Already loaded, and present. */
- xfa = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/XFA");
- if (!pdf_is_array(ctx, xfa) && !pdf_is_stream(ctx, xfa))
- return NULL; /* No XFA */
- fz_var(buf);
- fz_var(packet);
- fz_try(ctx)
- {
- if (pdf_is_stream(ctx, xfa))
- {
- /* Load entire XFA resource */
- buf = pdf_load_stream(ctx, xfa);
- }
- else
- {
- /* Concatenate packets to create entire XFA resource */
- buf = fz_new_buffer(ctx, 1024);
- for(i = 0; i < pdf_array_len(ctx, xfa); ++i)
- {
- pdf_obj *ref = pdf_array_get(ctx, xfa, i);
- if (pdf_is_stream(ctx, ref))
- {
- packet = pdf_load_stream(ctx, ref);
- fz_append_buffer(ctx, buf, packet);
- fz_drop_buffer(ctx, packet);
- packet = NULL;
- }
- }
- }
- /* Parse and stow away XFA resource in document */
- doc->xfa = fz_parse_xml(ctx, buf, 0);
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, packet);
- fz_drop_buffer(ctx, buf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- return doc->xfa;
- }
- static fz_xml *
- get_xfa_resource(fz_context *ctx, pdf_document *doc, const char *str)
- {
- fz_xml_doc *xfa;
- xfa = load_xfa(ctx, doc);
- if (!xfa)
- return NULL;
- return fz_xml_find_down(fz_xml_root(xfa), str);
- }
- static int
- find_name_component(char **np, char **sp, char **ep)
- {
- char *n = *np;
- char *s, *e;
- int idx = 0;
- if (*n == '.')
- n++;
- /* Find the next name we are looking for. */
- s = e = n;
- while (*e && *e != '[' && *e != '.')
- e++;
- /* So the next name is s..e */
- n = e;
- if (*n == '[')
- {
- n++;
- while (*n >= '0' && *n <= '9')
- idx = idx*10 + *n++ - '0';
- while (*n && *n != ']')
- n++;
- if (*n == ']')
- n++;
- }
- *np = n;
- *sp = s;
- *ep = e;
- return idx;
- }
- static pdf_obj *
- annot_from_name(fz_context *ctx, pdf_document *doc, const char *str)
- {
- pdf_obj *fields = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields");
- if (strncmp(str, "xfa[0].", 7) == 0)
- str += 7;
- if (strncmp(str, "template[0].", 12) == 0)
- str += 12;
- return pdf_lookup_field(ctx, fields, str);
- }
- static pdf_obj *
- get_locked_fields_from_xfa(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- char *name = pdf_load_field_name(ctx, field);
- char *n = name;
- const char *use;
- fz_xml *node;
- if (name == NULL)
- return NULL;
- fz_try(ctx)
- {
- node = get_xfa_resource(ctx, doc, "template");
- do
- {
- char c, *s, *e;
- int idx = 0;
- char *key;
- idx = find_name_component(&n, &s, &e);
- /* We want the idx'th occurrence of s..e */
- /* Hacky */
- c = *e;
- *e = 0;
- key = *n ? "subform" : "field";
- node = fz_xml_find_down_match(node, key, "name", s);
- while (node && idx > 0)
- {
- node = fz_xml_find_next_match(node, key, "name", s);
- idx--;
- }
- *e = c;
- }
- while (node && *n == '.');
- }
- fz_always(ctx)
- fz_free(ctx, name);
- fz_catch(ctx)
- fz_rethrow(ctx);
- if (node == NULL)
- return NULL;
- node = fz_xml_find_down(node, "ui");
- node = fz_xml_find_down(node, "signature");
- node = fz_xml_find_down(node, "manifest");
- use = fz_xml_att(node, "use");
- if (use == NULL)
- return NULL;
- if (*use == '#')
- use++;
- /* Now look for a variables entry in a subform that defines this. */
- while (node)
- {
- fz_xml *variables, *manifest, *ref;
- pdf_obj *arr;
- /* Find the enclosing subform */
- do {
- node = fz_xml_up(node);
- } while (node && strcmp(fz_xml_tag(node), "subform"));
- /* Look for a variables within that. */
- variables = fz_xml_find_down(node, "variables");
- if (variables == NULL)
- continue;
- manifest = fz_xml_find_down_match(variables, "manifest", "id", use);
- if (manifest == NULL)
- continue;
- arr = pdf_new_array(ctx, doc, 16);
- fz_try(ctx)
- {
- ref = fz_xml_find_down(manifest, "ref");
- while (ref)
- {
- const char *s = fz_xml_text(fz_xml_down(ref));
- pdf_array_push(ctx, arr, annot_from_name(ctx, doc, s));
- ref = fz_xml_find_next(ref, "ref");
- }
- }
- fz_catch(ctx)
- {
- pdf_drop_obj(ctx, arr);
- fz_rethrow(ctx);
- }
- return arr;
- }
- return NULL;
- }
- static void
- lock_field(fz_context *ctx, pdf_obj *f)
- {
- int ff = pdf_dict_get_inheritable_int(ctx, f, PDF_NAME(Ff));
- if ((ff & PDF_FIELD_IS_READ_ONLY) ||
- !pdf_name_eq(ctx, pdf_dict_get(ctx, f, PDF_NAME(Type)), PDF_NAME(Annot)) ||
- !pdf_name_eq(ctx, pdf_dict_get(ctx, f, PDF_NAME(Subtype)), PDF_NAME(Widget)))
- return;
- pdf_dict_put_int(ctx, f, PDF_NAME(Ff), ff | PDF_FIELD_IS_READ_ONLY);
- }
- static void
- lock_xfa_locked_fields(fz_context *ctx, pdf_obj *a)
- {
- int i;
- int len = pdf_array_len(ctx, a);
- for (i = 0; i < len; i++)
- {
- lock_field(ctx, pdf_array_get(ctx, a, i));
- }
- }
- void pdf_signature_set_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_pkcs7_signer *signer, int64_t stime)
- {
- pdf_obj *v = NULL;
- pdf_obj *o = NULL;
- pdf_obj *r = NULL;
- pdf_obj *t = NULL;
- pdf_obj *a = NULL;
- pdf_obj *b = NULL;
- pdf_obj *l = NULL;
- pdf_obj *indv;
- int vnum;
- size_t max_digest_size;
- char *buf = NULL;
- vnum = pdf_create_object(ctx, doc);
- indv = pdf_new_indirect(ctx, doc, vnum, 0);
- pdf_dict_put_drop(ctx, field, PDF_NAME(V), indv);
- max_digest_size = signer->max_digest_size(ctx, signer);
- fz_var(v);
- fz_var(o);
- fz_var(r);
- fz_var(t);
- fz_var(a);
- fz_var(b);
- fz_var(l);
- fz_var(buf);
- fz_try(ctx)
- {
- v = pdf_new_dict(ctx, doc, 4);
- pdf_update_object(ctx, doc, vnum, v);
- buf = fz_calloc(ctx, max_digest_size, 1);
- /* Ensure that the /Filter entry is the first entry in the
- dictionary after the digest contents since we look for
- this tag when completing signatures in pdf-write.c in order
- to generate the correct byte range. */
- pdf_dict_put_array(ctx, v, PDF_NAME(ByteRange), 4);
- pdf_dict_put_string(ctx, v, PDF_NAME(Contents), buf, max_digest_size);
- pdf_dict_put(ctx, v, PDF_NAME(Filter), PDF_NAME(Adobe_PPKLite));
- pdf_dict_put(ctx, v, PDF_NAME(SubFilter), PDF_NAME(adbe_pkcs7_detached));
- pdf_dict_put(ctx, v, PDF_NAME(Type), PDF_NAME(Sig));
- pdf_dict_put_date(ctx, v, PDF_NAME(M), stime);
- o = pdf_dict_put_array(ctx, v, PDF_NAME(Reference), 1);
- r = pdf_array_put_dict(ctx, o, 0, 4);
- pdf_dict_put(ctx, r, PDF_NAME(Data), pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)));
- pdf_dict_put(ctx, r, PDF_NAME(TransformMethod), PDF_NAME(FieldMDP));
- pdf_dict_put(ctx, r, PDF_NAME(Type), PDF_NAME(SigRef));
- t = pdf_dict_put_dict(ctx, r, PDF_NAME(TransformParams), 5);
- l = pdf_dict_getp(ctx, field, "Lock/Action");
- if (l)
- {
- a = pdf_dict_getp(ctx, field, "Lock/Fields");
- }
- else
- {
- /* Lock action wasn't specified so we need to encode an Include.
- * Before we just use an empty array, check in the XFA for locking
- * details. */
- a = get_locked_fields_from_xfa(ctx, doc, field);
- if (a)
- lock_xfa_locked_fields(ctx, a);
- /* If we don't get a result from the XFA, just encode an empty array
- * (leave a == NULL), even if Lock/Fields exists because we don't really
- * know what to do with the information if the action isn't defined. */
- l = PDF_NAME(Include);
- }
- pdf_dict_put(ctx, t, PDF_NAME(Action), l);
- if (pdf_name_eq(ctx, l, PDF_NAME(Include)) || pdf_name_eq(ctx, l, PDF_NAME(Exclude)))
- {
- /* For action Include and Exclude, we need to encode a Fields array */
- if (!a)
- {
- /* If one wasn't defined or we chose to ignore it because no action
- * was defined then use an empty one. */
- b = pdf_new_array(ctx, doc, 0);
- a = b;
- }
- pdf_dict_put_drop(ctx, t, PDF_NAME(Fields), pdf_copy_array(ctx, a));
- }
- pdf_dict_put(ctx, t, PDF_NAME(Type), PDF_NAME(TransformParams));
- pdf_dict_put(ctx, t, PDF_NAME(V), PDF_NAME(1_2));
- /* Record details within the document structure so that contents
- * and byte_range can be updated with their correct values at
- * saving time */
- pdf_xref_store_unsaved_signature(ctx, doc, field, signer);
- }
- fz_always(ctx)
- {
- pdf_drop_obj(ctx, v);
- pdf_drop_obj(ctx, b);
- fz_free(ctx, buf);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- }
- void pdf_set_widget_editing_state(fz_context *ctx, pdf_annot *widget, int editing)
- {
- widget->ignore_trigger_events = editing;
- }
- int pdf_get_widget_editing_state(fz_context *ctx, pdf_annot *widget)
- {
- return widget->ignore_trigger_events;
- }
- static void pdf_execute_js_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *js)
- {
- if (js)
- {
- char *code = pdf_load_stream_or_string_as_utf8(ctx, js);
- int in_op = 0;
- fz_var(in_op);
- fz_try(ctx)
- {
- char buf[100];
- fz_snprintf(buf, sizeof buf, "%d/%s", pdf_to_num(ctx, target), path);
- pdf_begin_operation(ctx, doc, "Javascript Event");
- in_op = 1;
- pdf_js_execute(doc->js, buf, code, NULL);
- pdf_end_operation(ctx, doc);
- }
- fz_always(ctx)
- {
- fz_free(ctx, code);
- }
- fz_catch(ctx)
- {
- if (in_op)
- pdf_abandon_operation(ctx, doc);
- fz_rethrow(ctx);
- }
- }
- }
- static void pdf_execute_action_imp(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action)
- {
- pdf_obj *S = pdf_dict_get(ctx, action, PDF_NAME(S));
- if (pdf_name_eq(ctx, S, PDF_NAME(JavaScript)))
- {
- if (doc->js)
- pdf_execute_js_action(ctx, doc, target, path, pdf_dict_get(ctx, action, PDF_NAME(JS)));
- }
- if (pdf_name_eq(ctx, S, PDF_NAME(ResetForm)))
- {
- pdf_obj *fields = pdf_dict_get(ctx, action, PDF_NAME(Fields));
- int flags = pdf_dict_get_int(ctx, action, PDF_NAME(Flags));
- pdf_reset_form(ctx, doc, fields, flags & 1);
- }
- }
- static void pdf_execute_action_chain(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action, pdf_cycle_list *cycle_up)
- {
- pdf_cycle_list cycle;
- pdf_obj *next;
- if (pdf_cycle(ctx, &cycle, cycle_up, action))
- fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in action chain");
- if (pdf_is_array(ctx, action))
- {
- int i, n = pdf_array_len(ctx, action);
- for (i = 0; i < n; ++i)
- pdf_execute_action_chain(ctx, doc, target, path, pdf_array_get(ctx, action, i), &cycle);
- }
- else
- {
- pdf_execute_action_imp(ctx, doc, target, path, action);
- next = pdf_dict_get(ctx, action, PDF_NAME(Next));
- if (next)
- pdf_execute_action_chain(ctx, doc, target, path, next, &cycle);
- }
- }
- static void pdf_execute_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path)
- {
- pdf_obj *action = pdf_dict_getp_inheritable(ctx, target, path);
- if (action)
- pdf_execute_action_chain(ctx, doc, target, path, action, NULL);
- }
- void pdf_document_event_will_close(fz_context *ctx, pdf_document *doc)
- {
- pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WC");
- }
- void pdf_document_event_will_save(fz_context *ctx, pdf_document *doc)
- {
- pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WS");
- }
- void pdf_document_event_did_save(fz_context *ctx, pdf_document *doc)
- {
- pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DS");
- }
- void pdf_document_event_will_print(fz_context *ctx, pdf_document *doc)
- {
- pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WP");
- }
- void pdf_document_event_did_print(fz_context *ctx, pdf_document *doc)
- {
- pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DP");
- }
- void pdf_page_event_open(fz_context *ctx, pdf_page *page)
- {
- pdf_execute_action(ctx, page->doc, page->obj, "AA/O");
- }
- void pdf_page_event_close(fz_context *ctx, pdf_page *page)
- {
- pdf_execute_action(ctx, page->doc, page->obj, "AA/C");
- }
- static void
- annot_execute_action(fz_context *ctx, pdf_annot *annot, const char *act)
- {
- begin_annot_op(ctx, annot, "JavaScript action");
- fz_try(ctx)
- {
- pdf_execute_action(ctx, annot->page->doc, annot->obj, act);
- end_annot_op(ctx, annot);
- }
- fz_catch(ctx)
- {
- abandon_annot_op(ctx, annot);
- fz_rethrow(ctx);
- }
- }
- void pdf_annot_event_enter(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/E");
- }
- void pdf_annot_event_exit(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/X");
- }
- void pdf_annot_event_down(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/D");
- }
- void pdf_annot_event_up(fz_context *ctx, pdf_annot *annot)
- {
- pdf_obj *action;
- begin_annot_op(ctx, annot, "JavaScript action");
- fz_try(ctx)
- {
- action = pdf_dict_get(ctx, annot->obj, PDF_NAME(A));
- if (action)
- pdf_execute_action_chain(ctx, annot->page->doc, annot->obj, "A", action, NULL);
- else
- pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/U");
- end_annot_op(ctx, annot);
- }
- fz_catch(ctx)
- {
- abandon_annot_op(ctx, annot);
- fz_rethrow(ctx);
- }
- }
- void pdf_annot_event_focus(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/Fo");
- }
- void pdf_annot_event_blur(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/Bl");
- }
- void pdf_annot_event_page_open(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/PO");
- }
- void pdf_annot_event_page_close(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/PC");
- }
- void pdf_annot_event_page_visible(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/PV");
- }
- void pdf_annot_event_page_invisible(fz_context *ctx, pdf_annot *annot)
- {
- annot_execute_action(ctx, annot, "AA/PI");
- }
- int pdf_field_event_keystroke(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_keystroke_event *evt)
- {
- pdf_js *js = doc->js;
- if (js)
- {
- pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/K/JS");
- if (action)
- {
- pdf_js_event_init_keystroke(js, field, evt);
- pdf_execute_js_action(ctx, doc, field, "AA/K/JS", action);
- return pdf_js_event_result_keystroke(js, evt);
- }
- }
- evt->newChange = fz_strdup(ctx, evt->change);
- evt->newValue = fz_strdup(ctx, evt->value);
- return 1;
- }
- int pdf_annot_field_event_keystroke(fz_context *ctx, pdf_document *doc, pdf_annot *annot, pdf_keystroke_event *evt)
- {
- int ret;
- pdf_annot_push_local_xref(ctx, annot);
- fz_try(ctx)
- ret = pdf_field_event_keystroke(ctx, doc, annot->obj, evt);
- fz_always(ctx)
- pdf_annot_pop_local_xref(ctx, annot);
- fz_catch(ctx)
- fz_rethrow(ctx);
- return ret;
- }
- char *pdf_field_event_format(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- pdf_js *js = doc->js;
- if (js)
- {
- pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/F/JS");
- if (action)
- {
- const char *value = pdf_field_value(ctx, field);
- pdf_js_event_init(js, field, value, 1);
- pdf_execute_js_action(ctx, doc, field, "AA/F/JS", action);
- return pdf_js_event_value(js);
- }
- }
- return NULL;
- }
- int pdf_field_event_validate(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *value, char **newvalue)
- {
- pdf_js *js = doc->js;
- *newvalue = NULL;
- if (js)
- {
- pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/V/JS");
- if (action)
- {
- pdf_js_event_init(js, field, value, 1);
- pdf_execute_js_action(ctx, doc, field, "AA/V/JS", action);
- return pdf_js_event_result_validate(js, newvalue);
- }
- }
- return 1;
- }
- void pdf_field_event_calculate(fz_context *ctx, pdf_document *doc, pdf_obj *field)
- {
- pdf_js *js = doc->js;
- if (js)
- {
- pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/C/JS");
- if (action)
- {
- char *old_value = fz_strdup(ctx, pdf_field_value(ctx, field));
- char *new_value = NULL;
- fz_var(new_value);
- fz_try(ctx)
- {
- pdf_js_event_init(js, field, old_value, 1);
- pdf_execute_js_action(ctx, doc, field, "AA/C/JS", action);
- if (pdf_js_event_result(js))
- {
- new_value = pdf_js_event_value(js);
- if (strcmp(old_value, new_value))
- pdf_set_field_value(ctx, doc, field, new_value, 0);
- }
- }
- fz_always(ctx)
- {
- fz_free(ctx, old_value);
- fz_free(ctx, new_value);
- }
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- }
- }
- static void
- count_sigs(fz_context *ctx, pdf_obj *field, void *arg, pdf_obj **ft)
- {
- int *n = (int *)arg;
- if (!pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Type)), PDF_NAME(Annot)) ||
- !pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Subtype)), PDF_NAME(Widget)) ||
- !pdf_name_eq(ctx, *ft, PDF_NAME(Sig)))
- return;
- (*n)++;
- }
- static pdf_obj *ft_name[2] = { PDF_NAME(FT), NULL };
- int pdf_count_signatures(fz_context *ctx, pdf_document *doc)
- {
- int n = 0;
- pdf_obj *ft = NULL;
- pdf_obj *form_fields = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields");
- pdf_walk_tree(ctx, form_fields, PDF_NAME(Kids), count_sigs, NULL, &n, ft_name, &ft);
- return n;
- }
- /*
- * Bake interactive form fields into static content.
- */
- static pdf_obj *get_annot_ap(fz_context *ctx, pdf_obj *annot)
- {
- pdf_obj *ap = pdf_dict_get(ctx, annot, PDF_NAME(AP));
- pdf_obj *as = pdf_dict_get(ctx, annot, PDF_NAME(AS));
- if (ap)
- {
- ap = pdf_dict_get(ctx, ap, PDF_NAME(N));
- if (pdf_is_stream(ctx, ap))
- return ap;
- ap = pdf_dict_get(ctx, ap, as);
- if (pdf_is_stream(ctx, ap))
- return ap;
- }
- return NULL;
- }
- static fz_matrix get_annot_transform(fz_context *ctx, pdf_obj *annot, pdf_obj *ap)
- {
- float w, h, x, y;
- fz_matrix transform;
- fz_rect bbox;
- fz_rect rect;
- rect = pdf_dict_get_rect(ctx, annot, PDF_NAME(Rect));
- bbox = pdf_dict_get_rect(ctx, ap, PDF_NAME(BBox));
- transform = pdf_dict_get_matrix(ctx, ap, PDF_NAME(Matrix));
- bbox = fz_transform_rect(bbox, transform);
- w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
- h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
- x = rect.x0 - bbox.x0 * w;
- y = rect.y0 - bbox.y0 * h;
- return fz_make_matrix(w, 0, 0, h, x, y);
- }
- static void pdf_bake_annot(fz_context *ctx, fz_buffer *buf, pdf_document *doc, pdf_obj *page, pdf_obj *res_xobj, pdf_obj *annot)
- {
- fz_matrix m;
- pdf_obj *ap;
- char name[20];
- ap = get_annot_ap(ctx, annot);
- if (ap)
- {
- fz_snprintf(name, sizeof name, "Annot%d", pdf_to_num(ctx, annot));
- pdf_dict_puts(ctx, res_xobj, name, ap);
- pdf_dict_put(ctx, ap, PDF_NAME(Type), PDF_NAME(XObject));
- pdf_dict_put(ctx, ap, PDF_NAME(Subtype), PDF_NAME(Form));
- m = get_annot_transform(ctx, annot, ap);
- fz_append_printf(ctx, buf,
- "q\n%g %g %g %g %g %g cm\n/%s Do\nQ\n",
- m.a, m.b, m.c, m.d, m.e, m.f,
- name
- );
- }
- }
- static void pdf_bake_page(fz_context *ctx, pdf_document *doc, pdf_obj *page, int bake_annots, int bake_widgets)
- {
- pdf_obj *res;
- pdf_obj *res_xobj;
- pdf_obj *contents;
- pdf_obj *new_contents = NULL;
- pdf_obj *annots;
- pdf_obj *annot;
- pdf_obj *subtype;
- pdf_obj *prologue = NULL;
- fz_buffer *buf = NULL;
- int prepend, append;
- int i;
- fz_var(buf);
- fz_var(prologue);
- fz_var(new_contents);
- annots = pdf_dict_get(ctx, page, PDF_NAME(Annots));
- if (pdf_array_len(ctx, annots) == 0)
- return;
- res = pdf_dict_get(ctx, page, PDF_NAME(Resources));
- if (!res)
- res = pdf_dict_put_dict(ctx, page, PDF_NAME(Resources), 4);
- res_xobj = pdf_dict_get(ctx, res, PDF_NAME(XObject));
- if (!res_xobj)
- res_xobj = pdf_dict_put_dict(ctx, res, PDF_NAME(XObject), 8);
- fz_try(ctx)
- {
- // Ensure that the graphics state is balanced.
- contents = pdf_dict_get(ctx, page, PDF_NAME(Contents));
- pdf_count_q_balance(ctx, doc, res, contents, &prepend, &append);
- if (prepend)
- {
- // Prepend enough 'q' to ensure we can get back to initial state.
- buf = fz_new_buffer(ctx, 1024);
- while (prepend-- > 0)
- fz_append_string(ctx, buf, "q\n");
- prologue = pdf_add_stream(ctx, doc, buf, NULL, 0);
- fz_drop_buffer(ctx, buf);
- buf = NULL;
- }
- // Append enough 'Q' to get back to initial state.
- buf = fz_new_buffer(ctx, 1024);
- while (append-- > 0)
- fz_append_string(ctx, buf, "Q\n");
- for (i = 0; i < pdf_array_len(ctx, annots); )
- {
- annot = pdf_array_get(ctx, annots, i);
- subtype = pdf_dict_get(ctx, annot, PDF_NAME(Subtype));
- if (subtype == PDF_NAME(Link))
- {
- ++i;
- }
- else if (subtype == PDF_NAME(Widget))
- {
- if (bake_widgets)
- {
- pdf_bake_annot(ctx, buf, doc, page, res_xobj, annot);
- pdf_array_delete(ctx, annots, i);
- }
- else
- {
- ++i;
- }
- }
- else
- {
- if (bake_annots)
- {
- pdf_bake_annot(ctx, buf, doc, page, res_xobj, annot);
- pdf_array_delete(ctx, annots, i);
- }
- else
- {
- ++i;
- }
- }
- }
- if (!pdf_is_array(ctx, contents))
- {
- new_contents = pdf_new_array(ctx, doc, 10);
- if (prologue)
- pdf_array_push(ctx, new_contents, prologue);
- if (contents)
- pdf_array_push(ctx, new_contents, contents);
- pdf_dict_put(ctx, page, PDF_NAME(Contents), new_contents);
- pdf_drop_obj(ctx, new_contents);
- contents = new_contents;
- new_contents = NULL;
- }
- else if (prologue)
- {
- pdf_array_insert(ctx, contents, prologue, 0);
- }
- pdf_array_push_drop(ctx, contents, pdf_add_stream(ctx, doc, buf, NULL, 0));
- }
- fz_always(ctx)
- {
- fz_drop_buffer(ctx, buf);
- pdf_drop_obj(ctx, prologue);
- pdf_drop_obj(ctx, new_contents);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- }
- void pdf_bake_document(fz_context *ctx, pdf_document *doc, int bake_annots, int bake_widgets)
- {
- pdf_page *page = NULL;
- pdf_annot *annot;
- int i, n;
- fz_var(page);
- pdf_begin_operation(ctx, doc, "Bake interactive content");
- fz_try(ctx)
- {
- n = pdf_count_pages(ctx, doc);
- for (i = 0; i < n; ++i)
- {
- page = pdf_load_page(ctx, doc, i);
- if (bake_annots)
- for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot))
- pdf_annot_request_synthesis(ctx, annot);
- if (bake_widgets)
- for (annot = pdf_first_widget(ctx, page); annot; annot = pdf_next_widget(ctx, annot))
- pdf_annot_request_synthesis(ctx, annot);
- pdf_update_page(ctx, page);
- pdf_bake_page(ctx, doc, page->obj, bake_annots, bake_widgets);
- fz_drop_page(ctx, (fz_page*)page);
- page = NULL;
- }
- if (bake_widgets)
- {
- pdf_obj *trailer = pdf_trailer(ctx, doc);
- pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
- pdf_dict_del(ctx, root, PDF_NAME(AcroForm));
- }
- pdf_end_operation(ctx, doc);
- }
- fz_always(ctx)
- {
- fz_drop_page(ctx, (fz_page*)page);
- }
- fz_catch(ctx)
- {
- pdf_abandon_operation(ctx, doc);
- }
- }
|