| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281 |
- // 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 "gl-app.h"
- #include <limits.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <time.h>
- #ifndef _WIN32
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <signal.h>
- #endif
- #include "mupdf/helpers/pkcs7-openssl.h"
- #if FZ_ENABLE_JS
- #include "mujs.h"
- #endif
- #ifndef _WIN32
- #include <sys/stat.h> /* for mkdir */
- #include <unistd.h> /* for getcwd */
- #include <spawn.h> /* for posix_spawn */
- extern char **environ; /* see environ (7) */
- #else
- #include <direct.h> /* for getcwd */
- #endif
- #ifdef __APPLE__
- static void cleanup(void);
- void glutLeaveMainLoop(void)
- {
- cleanup();
- exit(0);
- }
- #endif
- fz_context *ctx = NULL;
- fz_colorspace *profile = NULL;
- pdf_document *pdf = NULL;
- pdf_page *page = NULL;
- fz_stext_page *page_text = NULL;
- fz_matrix draw_page_ctm, view_page_ctm, view_page_inv_ctm;
- fz_rect page_bounds, draw_page_bounds, view_page_bounds;
- fz_irect view_page_area;
- char filename[PATH_MAX];
- enum
- {
- /* Screen furniture: aggregate size of unusable space from title bars, task bars, window borders, etc */
- SCREEN_FURNITURE_W = 20,
- SCREEN_FURNITURE_H = 40,
- };
- static void open_browser(const char *uri)
- {
- #ifndef _WIN32
- char *argv[3];
- #endif
- char buf[PATH_MAX];
- #ifndef _WIN32
- pid_t pid;
- int err;
- #endif
- /* Relative file: URI, make it absolute! */
- if (!strncmp(uri, "file:", 5) && uri[5] != '/')
- {
- char buf_base[PATH_MAX];
- char buf_cwd[PATH_MAX];
- fz_dirname(buf_base, filename, sizeof buf_base);
- if (getcwd(buf_cwd, sizeof buf_cwd))
- {
- fz_snprintf(buf, sizeof buf, "file://%s/%s/%s", buf_cwd, buf_base, uri+5);
- fz_cleanname(buf+7);
- uri = buf;
- }
- }
- if (strncmp(uri, "file://", 7) && strncmp(uri, "http://", 7) && strncmp(uri, "https://", 8) && strncmp(uri, "mailto:", 7))
- {
- fz_warn(ctx, "refusing to open unknown link (%s)", uri);
- return;
- }
- #ifdef _WIN32
- ShellExecuteA(NULL, "open", uri, 0, 0, SW_SHOWNORMAL);
- #else
- const char *browser = getenv("BROWSER");
- if (!browser)
- {
- #ifdef __APPLE__
- browser = "open";
- #else
- browser = "xdg-open";
- #endif
- }
- argv[0] = (char*) browser;
- argv[1] = (char*) uri;
- argv[2] = NULL;
- err = posix_spawnp(&pid, browser, NULL, NULL, argv, environ);
- if (err)
- fz_warn(ctx, "cannot spawn browser '%s': %s", browser, strerror(err));
- #endif
- }
- static const int zoom_list[] = {
- 6, 12, 24, 36, 48, 60, 72, 84, 96, 108,
- 120, 144, 168, 192, 228, 264,
- 300, 350, 400, 450, 500, 550, 600
- };
- static int zoom_in(int oldres)
- {
- int i;
- for (i = 0; i < (int)nelem(zoom_list) - 1; ++i)
- if (zoom_list[i] <= oldres && zoom_list[i+1] > oldres)
- return zoom_list[i+1];
- return zoom_list[i];
- }
- static int zoom_out(int oldres)
- {
- int i;
- for (i = 0; i < (int)nelem(zoom_list) - 1; ++i)
- if (zoom_list[i] < oldres && zoom_list[i+1] >= oldres)
- return zoom_list[i];
- return zoom_list[0];
- }
- static const char *paper_size_name(int w, int h)
- {
- /* ISO A */
- if (w == 2384 && h == 3370) return "A0";
- if (w == 1684 && h == 2384) return "A1";
- if (w == 1191 && h == 1684) return "A2";
- if (w == 842 && h == 1191) return "A3";
- if (w == 595 && h == 842) return "A4";
- if (w == 420 && h == 595) return "A5";
- if (w == 297 && h == 420) return "A6";
- /* US */
- if (w == 612 && h == 792) return "Letter";
- if (w == 612 && h == 1008) return "Legal";
- if (w == 792 && h == 1224) return "Ledger";
- if (w == 1224 && h == 792) return "Tabloid";
- return NULL;
- }
- #define MINRES (zoom_list[0])
- #define MAXRES (zoom_list[nelem(zoom_list)-1])
- #define DEFRES 96
- static char *password = "";
- static char *anchor = NULL;
- static float layout_w = FZ_DEFAULT_LAYOUT_W;
- static float layout_h = FZ_DEFAULT_LAYOUT_H;
- static float layout_em = FZ_DEFAULT_LAYOUT_EM;
- static char *layout_css = NULL;
- static int layout_use_doc_css = 1;
- static int enable_js = 1;
- static int tint_white = 0xFFFFF0;
- static int tint_black = 0x303030;
- static fz_document *doc = NULL;
- static fz_page *fzpage = NULL;
- static fz_separations *seps = NULL;
- static fz_outline *outline = NULL;
- static fz_link *links = NULL;
- static int number = 0;
- static fz_pixmap *page_contents = NULL;
- static struct texture page_tex = { 0 };
- static int screen_w = 0, screen_h = 0;
- static int scroll_x = 0, scroll_y = 0;
- static int canvas_x = 0, canvas_w = 100;
- static int canvas_y = 0, canvas_h = 100;
- static int outline_w = 14; /* to be scaled by lineheight */
- static int annotate_w = 12; /* to be scaled by lineheight */
- static int outline_start_x = 0;
- static int oldbox = FZ_CROP_BOX, currentbox = FZ_CROP_BOX;
- static int oldtint = 0, currenttint = 0;
- static int oldinvert = 0, currentinvert = 0;
- static int oldicc = 1, currenticc = 1;
- static int oldaa = 8, currentaa = 8;
- static int oldseparations = 1, currentseparations = 1;
- static fz_location oldpage = {0,0}, currentpage = {0,0};
- static float oldzoom = DEFRES, currentzoom = DEFRES;
- static float oldrotate = 0, currentrotate = 0;
- int page_contents_changed = 0;
- int page_annots_changed = 0;
- static fz_output *trace_file = NULL;
- static char *reflow_options = NULL;
- static int isfullscreen = 0;
- static int showoutline = 0;
- static int showundo = 0;
- static int showlayers = 0;
- static int showlinks = 0;
- static int showsearch = 0;
- int showannotate = 0;
- int showform = 0;
- #if FZ_ENABLE_JS
- static int showconsole = 0;
- static int console_h = 14; /* to be scaled by lineheight */
- static pdf_js_console gl_js_console;
- static int console_start_y = 0;
- #endif
- static const char *tooltip = NULL;
- struct mark
- {
- fz_location loc;
- fz_point scroll;
- };
- static int history_count = 0;
- static struct mark history[256];
- static int future_count = 0;
- static struct mark future[256];
- static struct mark marks[10];
- static char *get_history_filename(void)
- {
- static char history_path[PATH_MAX];
- static int once = 0;
- if (!once)
- {
- char *home = getenv("MUPDF_HISTORY");
- if (home)
- return home;
- home = getenv("XDG_CACHE_HOME");
- if (!home)
- home = getenv("HOME");
- if (!home)
- home = getenv("USERPROFILE");
- if (!home)
- home = "/tmp";
- fz_snprintf(history_path, sizeof history_path, "%s/.mupdf.history", home);
- fz_cleanname(history_path);
- once = 1;
- }
- return history_path;
- }
- static fz_json *read_history_file_as_json(fz_pool *pool)
- {
- fz_buffer *buf = NULL;
- const char *json = "{}";
- const char *history_file;
- fz_json *result = NULL;
- fz_var(buf);
- history_file = get_history_filename();
- if (strlen(history_file) == 0)
- return NULL;
- if (fz_file_exists(ctx, history_file))
- {
- fz_try(ctx)
- {
- buf = fz_read_file(ctx, history_file);
- json = fz_string_from_buffer(ctx, buf);
- }
- fz_catch(ctx)
- ;
- }
- fz_try(ctx)
- {
- result = fz_parse_json(ctx, pool, json);
- }
- fz_catch(ctx)
- {
- fz_report_error(ctx);
- fz_warn(ctx, "can't parse history file");
- result = NULL;
- }
- fz_drop_buffer(ctx, buf);
- if (result == NULL || result->type != FZ_JSON_OBJECT)
- result = fz_json_new_object(ctx, pool);
- return result;
- }
- static fz_location load_location(fz_json *val)
- {
- if (fz_json_is_number(ctx, val))
- return fz_make_location(0, fz_json_to_number(ctx, val) - 1);
- if (fz_json_is_array(ctx, val))
- return fz_make_location(
- fz_json_to_number(ctx, fz_json_array_get(ctx, val, 0)) - 1,
- fz_json_to_number(ctx, fz_json_array_get(ctx, val, 1)) - 1
- );
- return fz_make_location(0, 0);
- }
- static fz_json *save_location(fz_pool *pool, fz_location loc)
- {
- fz_json *arr;
- if (loc.chapter == 0)
- {
- return fz_json_new_number(ctx, pool, loc.page + 1);
- }
- else
- {
- arr = fz_json_new_array(ctx, pool);
- fz_json_array_push(ctx, pool, arr, fz_json_new_number(ctx, pool, loc.chapter + 1));
- fz_json_array_push(ctx, pool, arr, fz_json_new_number(ctx, pool, loc.page + 1));
- return arr;
- }
- }
- static void load_history(void)
- {
- char absname[PATH_MAX];
- fz_pool *pool = NULL;
- fz_json *json, *item, *arr, *val;
- int i, n;
- fz_var(pool);
- if (!fz_realpath(filename, absname))
- return;
- fz_try(ctx)
- {
- pool = fz_new_pool(ctx);
- json = read_history_file_as_json(pool);
- if (json)
- {
- item = fz_json_object_get(ctx, json, absname);
- if (item)
- {
- val = fz_json_object_get(ctx, item, "current");
- if (val)
- currentpage = load_location(val);
- arr = fz_json_object_get(ctx, item, "history");
- if (fz_json_is_array(ctx, arr))
- {
- history_count = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(history));
- for (i = 0; i < history_count; ++i)
- history[i].loc = load_location(fz_json_array_get(ctx, arr, i));
- }
- arr = fz_json_object_get(ctx, item, "future");
- if (fz_json_is_array(ctx, arr))
- {
- future_count = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(future));
- for (i = 0; i < future_count; ++i)
- future[i].loc = load_location(fz_json_array_get(ctx, arr, i));
- }
- arr = fz_json_object_get(ctx, item, "marks");
- if (fz_json_is_array(ctx, arr))
- {
- n = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(marks));
- for (i = 0; i < n; ++i)
- marks[i].loc = load_location(fz_json_array_get(ctx, arr, i));
- }
- }
- }
- }
- fz_always(ctx)
- {
- fz_drop_pool(ctx, pool);
- }
- fz_catch(ctx)
- {
- fz_report_error(ctx);
- fz_warn(ctx, "Can't read history file.");
- }
- }
- static void save_history(void)
- {
- fz_pool *pool;
- char absname[PATH_MAX];
- fz_output *out = NULL;
- fz_json *json, *item, *arr;
- const char *history_file;
- int i;
- fz_var(pool);
- fz_var(out);
- if (!doc)
- return;
- if (!fz_realpath(filename, absname))
- return;
- fz_try(ctx)
- {
- pool = fz_new_pool(ctx);
- json = read_history_file_as_json(pool);
- if (json)
- {
- item = fz_json_new_object(ctx, pool);
- fz_json_object_set(ctx, pool, item, "current", save_location(pool, currentpage));
- arr = fz_json_new_array(ctx, pool);
- for (i = 0; i < history_count; ++i)
- fz_json_array_push(ctx, pool, arr, save_location(pool, history[i].loc));
- fz_json_object_set(ctx, pool, item, "history", arr);
- arr = fz_json_new_array(ctx, pool);
- for (i = 0; i < future_count; ++i)
- fz_json_array_push(ctx, pool, arr, save_location(pool, future[i].loc));
- fz_json_object_set(ctx, pool, item, "future", arr);
- arr = fz_json_new_array(ctx, pool);
- for (i = 0; i < (int)nelem(marks); ++i)
- fz_json_array_push(ctx, pool, arr, save_location(pool, marks[i].loc));
- fz_json_object_set(ctx, pool, item, "marks", arr);
- fz_json_object_set(ctx, pool, json, absname, item);
- history_file = get_history_filename();
- if (strlen(history_file) > 0) {
- out = fz_new_output_with_path(ctx, history_file, 0);
- fz_write_json(ctx, out, json);
- fz_write_byte(ctx, out, '\n');
- fz_close_output(ctx, out);
- }
- }
- }
- fz_always(ctx)
- {
- fz_drop_pool(ctx, pool);
- fz_drop_output(ctx, out);
- }
- fz_catch(ctx)
- {
- fz_report_error(ctx);
- fz_warn(ctx, "Can't write history file.");
- }
- }
- static int create_accel_path(char outname[], size_t len, int create, const char *absname, ...)
- {
- va_list args;
- char *s = outname;
- size_t z, remain = len;
- char *arg;
- va_start(args, absname);
- while ((arg = va_arg(args, char *)) != NULL)
- {
- z = fz_snprintf(s, remain, "%s", arg);
- if (z+1 > remain)
- goto fail; /* won't fit */
- if (create)
- (void) fz_mkdir(outname);
- if (!fz_is_directory(ctx, outname))
- goto fail; /* directory creation failed, or that dir doesn't exist! */
- #ifdef _WIN32
- s[z] = '\\';
- #else
- s[z] = '/';
- #endif
- s[z+1] = 0;
- s += z+1;
- remain -= z+1;
- }
- if (fz_snprintf(s, remain, "%s.accel", absname) >= remain)
- goto fail; /* won't fit */
- va_end(args);
- return 1;
- fail:
- va_end(args);
- return 0;
- }
- static int convert_to_accel_path(char outname[], char *absname, size_t len, int create)
- {
- char *tmpdir;
- char *s;
- if (absname[0] == '/' || absname[0] == '\\')
- ++absname;
- s = absname;
- while (*s) {
- if (*s == '/' || *s == '\\' || *s == ':')
- *s = '%';
- ++s;
- }
- #ifdef _WIN32
- tmpdir = getenv("USERPROFILE");
- if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, ".config", "mupdf", NULL))
- return 1; /* OK! */
- /* TEMP and TMP are user-specific on modern windows. */
- tmpdir = getenv("TEMP");
- if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL))
- return 1; /* OK! */
- tmpdir = getenv("TMP");
- if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL))
- return 1; /* OK! */
- #else
- tmpdir = getenv("XDG_CACHE_HOME");
- if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL))
- return 1; /* OK! */
- tmpdir = getenv("HOME");
- if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, ".cache", "mupdf", NULL))
- return 1; /* OK! */
- #endif
- return 0; /* Fail */
- }
- static int get_accelerator_filename(char outname[], size_t len, int create)
- {
- char absname[PATH_MAX];
- if (!fz_realpath(filename, absname))
- return 0;
- if (!convert_to_accel_path(outname, absname, len, create))
- return 0;
- return 1;
- }
- static void save_accelerator(void)
- {
- char absname[PATH_MAX];
- if (!doc)
- return;
- if (!fz_document_supports_accelerator(ctx, doc))
- return;
- if (!get_accelerator_filename(absname, sizeof(absname), 1))
- return;
- fz_save_accelerator(ctx, doc, absname);
- }
- static struct input search_input = { { 0 }, 0 };
- static int search_dir = 1;
- static fz_location search_page = {-1, -1};
- static fz_location search_hit_page = {-1, -1};
- static int search_active = 0;
- char *search_needle = 0;
- int search_hit_count = 0;
- fz_quad search_hit_quads[5000];
- static char *help_dialog_text =
- "The middle mouse button (scroll wheel button) pans the document view. "
- "The right mouse button selects a region and copies the marked text to the clipboard."
- "\n"
- "\n"
- "F1 - show this message\n"
- "` F12 - show javascript console\n"
- "i - show document information\n"
- "o - show document outline\n"
- "u - show undo history\n"
- "Y - show layer list\n"
- "a - show annotation editor\n"
- "R - show redaction editor\n"
- "L - highlight links\n"
- "F - highlight form fields\n"
- "r - reload file\n"
- "S - save file (only for PDF)\n"
- "q - quit\n"
- "\n"
- "< - decrease E-book font size\n"
- "> - increase E-book font size\n"
- "B - cycle between MediaBox, CropBox, ArtBox, etc.\n"
- "A - toggle anti-aliasing\n"
- "I - toggle inverted color mode\n"
- "C - toggle tinted color mode\n"
- "E - toggle ICC color management\n"
- "e - toggle spot color emulation\n"
- "\n"
- "f - fullscreen window\n"
- "w - shrink wrap window\n"
- "W - fit to width\n"
- "H - fit to height\n"
- "Z - fit to page\n"
- "z - reset zoom\n"
- "[number] z - set zoom resolution in DPI\n"
- "plus - zoom in\n"
- "minus - zoom out\n"
- "[ - rotate counter-clockwise\n"
- "] - rotate clockwise\n"
- "arrow keys - scroll in small increments\n"
- "h, j, k, l - scroll in small increments\n"
- "\n"
- "b - smart move backward\n"
- "space - smart move forward\n"
- "comma or page up - go backward\n"
- "period or page down - go forward\n"
- "g - go to first page\n"
- "G - go to last page\n"
- "[number] g - go to page number\n"
- "\n"
- "m - save current location in history\n"
- "t - go backward in history\n"
- "T - go forward in history\n"
- "[number] m - save current location in numbered bookmark\n"
- "[number] t - go to numbered bookmark\n"
- "\n"
- "/ - search for text forward\n"
- "? - search for text backward\n"
- "n - repeat search\n"
- "N - repeat search in reverse direction"
- ;
- static void help_dialog(void)
- {
- static int scroll;
- ui_dialog_begin(ui.gridsize*20, ui.gridsize*40);
- ui_layout(T, X, W, ui.padsize, ui.padsize);
- ui_label("MuPDF %s", FZ_VERSION);
- ui_spacer();
- ui_layout(B, NONE, S, ui.padsize, ui.padsize);
- if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE)
- ui.dialog = NULL;
- ui_spacer();
- ui_layout(ALL, BOTH, CENTER, ui.padsize, ui.padsize);
- ui_label_with_scrollbar(help_dialog_text, 0, 0, &scroll, NULL);
- ui_dialog_end();
- }
- static fz_buffer *format_info_text();
- static void info_dialog(void)
- {
- static int scroll;
- fz_buffer *info_text;
- ui_dialog_begin(ui.gridsize*20, ui.gridsize*20);
- ui_layout(B, NONE, S, ui.padsize, ui.padsize);
- if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE)
- ui.dialog = NULL;
- ui_spacer();
- ui_layout(ALL, BOTH, CENTER, ui.padsize, ui.padsize);
- info_text = format_info_text();
- ui_label_with_scrollbar((char*)fz_string_from_buffer(ctx, info_text), 0, 0, &scroll, NULL);
- fz_drop_buffer(ctx, info_text);
- ui_dialog_end();
- }
- static char error_message[256];
- static void error_dialog(void)
- {
- ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*4);
- ui_layout(T, NONE, NW, ui.padsize, ui.padsize);
- ui_label("%C %s", 0x1f4a3, error_message); /* BOMB */
- ui_layout(B, NONE, S, ui.padsize, ui.padsize);
- if (ui_button("Quit") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE || ui.key == 'q')
- glutLeaveMainLoop();
- ui_dialog_end();
- }
- void ui_show_error_dialog(const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- fz_vsnprintf(error_message, sizeof error_message, fmt, ap);
- va_end(ap);
- ui.dialog = error_dialog;
- }
- static char warning_message[256];
- static void warning_dialog(void)
- {
- ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*4);
- ui_layout(T, NONE, NW, ui.padsize, ui.padsize);
- ui_label("%C %s", 0x26a0, warning_message); /* WARNING SIGN */
- ui_layout(B, NONE, S, ui.padsize, ui.padsize);
- if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE)
- ui.dialog = NULL;
- ui_dialog_end();
- }
- void ui_show_warning_dialog(const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- fz_vsnprintf(warning_message, sizeof warning_message, fmt, ap);
- va_end(ap);
- ui.dialog = warning_dialog;
- }
- static void quit_dialog(void)
- {
- ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*3);
- ui_layout(T, NONE, NW, ui.padsize, ui.padsize);
- ui_label("%C The document has unsaved changes. Are you sure you want to quit?", 0x26a0); /* WARNING SIGN */
- ui_layout(B, X, S, ui.padsize, ui.padsize);
- ui_panel_begin(0, ui.gridsize, 0, 0, 0);
- {
- ui_layout(R, NONE, S, 0, 0);
- if (ui_button("Save"))
- do_save_pdf_file();
- ui_spacer();
- if (ui_button("Discard") || ui.key == 'q')
- glutLeaveMainLoop();
- ui_layout(L, NONE, S, 0, 0);
- if (ui_button("Cancel") || ui.key == KEY_ESCAPE)
- ui.dialog = NULL;
- }
- ui_panel_end();
- ui_dialog_end();
- }
- static void quit(void)
- {
- if (pdf && pdf_has_unsaved_changes(ctx, pdf))
- ui.dialog = quit_dialog;
- else
- glutLeaveMainLoop();
- }
- static void reload_dialog(void)
- {
- ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*3);
- ui_layout(T, NONE, NW, ui.padsize, ui.padsize);
- ui_label("%C The document has unsaved changes. Are you sure you want to reload?", 0x26a0); /* WARNING SIGN */
- ui_layout(B, X, S, ui.padsize, ui.padsize);
- ui_panel_begin(0, ui.gridsize, 0, 0, 0);
- {
- ui_layout(R, NONE, S, 0, 0);
- if (ui_button("Save"))
- do_save_pdf_file();
- ui_spacer();
- if (ui_button("Reload") || ui.key == 'q')
- {
- ui.dialog = NULL;
- reload_document();
- }
- ui_layout(L, NONE, S, 0, 0);
- if (ui_button("Cancel") || ui.key == KEY_ESCAPE)
- ui.dialog = NULL;
- }
- ui_panel_end();
- ui_dialog_end();
- }
- void reload(void)
- {
- if (pdf && pdf_has_unsaved_changes(ctx, pdf))
- ui.dialog = reload_dialog;
- else
- reload_document();
- }
- void trace_action(const char *fmt, ...)
- {
- va_list args;
- if (trace_file)
- {
- va_start(args, fmt);
- fz_write_vprintf(ctx, trace_file, fmt, args);
- fz_flush_output(ctx, trace_file);
- va_end(args);
- va_start(args, fmt);
- fz_write_vprintf(ctx, fz_stdout(ctx), fmt, args);
- fz_flush_output(ctx, fz_stdout(ctx));
- va_end(args);
- }
- }
- void trace_page_update(void)
- {
- trace_action("page.update();\n");
- }
- void trace_save_snapshot(void)
- {
- static int trace_idx = 1;
- trace_action("page.toPixmap(Matrix.identity, ColorSpace.DeviceRGB).saveAsPNG(\"trace-%03d.png\");\n", trace_idx++);
- }
- static int document_shown_as_dirty = 0;
- void update_title(void)
- {
- char buf[256];
- const char *title = "MuPDF/GL";
- char *extra = "";
- size_t n;
- int nc = fz_count_chapters(ctx, doc);
- title = fz_basename(filename);
- document_shown_as_dirty = pdf && pdf_has_unsaved_changes(ctx, pdf);
- if (document_shown_as_dirty)
- extra = "*";
- n = strlen(title);
- if (n > 50)
- {
- if (nc == 1)
- sprintf(buf, "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc));
- else
- sprintf(buf, "...%s%s - %d/%d - %d/%d", title + n - 50, extra,
- currentpage.chapter + 1, nc,
- currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter));
- }
- else
- {
- if (nc == 1)
- sprintf(buf, "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc));
- else
- sprintf(buf, "%s%s - %d/%d - %d/%d", title, extra,
- currentpage.chapter + 1, nc,
- currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter));
- }
- glutSetWindowTitle(buf);
- glutSetIconTitle(buf);
- }
- void transform_page(void)
- {
- draw_page_ctm = fz_transform_page(page_bounds, currentzoom, currentrotate);
- draw_page_bounds = fz_transform_rect(page_bounds, draw_page_ctm);
- }
- static void clear_selected_annot(void)
- {
- /* clear all editor selections */
- if (ui.selected_annot && pdf_annot_type(ctx, ui.selected_annot) == PDF_ANNOT_WIDGET)
- pdf_annot_event_blur(ctx, ui.selected_annot);
- ui_select_annot(NULL);
- }
- void load_page(void)
- {
- fz_irect area;
- clear_selected_annot();
- if (trace_file)
- trace_action("page = doc.loadPage(%d);\n", fz_page_number_from_location(ctx, doc, currentpage));
- fz_drop_stext_page(ctx, page_text);
- page_text = NULL;
- fz_drop_separations(ctx, seps);
- seps = NULL;
- fz_drop_link(ctx, links);
- links = NULL;
- fz_drop_page(ctx, fzpage);
- fzpage = NULL;
- fzpage = fz_load_chapter_page(ctx, doc, currentpage.chapter, currentpage.page);
- if (pdf)
- page = (pdf_page*)fzpage;
- if (trace_file)
- {
- pdf_annot *w;
- int i, s;
- for (i = 0, s = 0, w = pdf_first_widget(ctx, page); w != NULL; i++, w = pdf_next_widget(ctx, w))
- if (pdf_widget_type(ctx, w) == PDF_WIDGET_TYPE_SIGNATURE)
- {
- int is_signed;
- s++;
- trace_action("widget = page.getWidgets()[%d];\n", i);
- trace_action("widgetstr = 'Signature %d on page %d';\n",
- s, fz_page_number_from_location(ctx, doc, currentpage));
- is_signed = pdf_widget_is_signed(ctx, w);
- trace_action("tmp = widget.isSigned();\n");
- trace_action("if (tmp != %d)\n", is_signed);
- trace_action(" throw new RegressionError(widgetstr, 'is signed:', tmp|0, 'expected:', %d);\n", is_signed);
- if (is_signed)
- {
- int valid_until, is_readonly;
- char *cert_error, *digest_error;
- pdf_pkcs7_distinguished_name *dn;
- pdf_pkcs7_verifier *verifier;
- char *signatory = NULL;
- char buf[500];
- valid_until = pdf_validate_signature(ctx, w);
- is_readonly = pdf_widget_is_readonly(ctx, w);
- verifier = pkcs7_openssl_new_verifier(ctx);
- cert_error = pdf_signature_error_description(pdf_check_widget_certificate(ctx, verifier, w));
- digest_error = pdf_signature_error_description(pdf_check_widget_digest(ctx, verifier, w));
- dn = pdf_signature_get_widget_signatory(ctx, verifier, w);
- if (dn)
- {
- char *s = pdf_signature_format_distinguished_name(ctx, dn);
- fz_strlcpy(buf, s, sizeof buf);
- fz_free(ctx, s);
- pdf_signature_drop_distinguished_name(ctx, dn);
- }
- else
- {
- fz_strlcpy(buf, "Signature information missing.", sizeof buf);
- }
- signatory = &buf[0];
- pdf_drop_verifier(ctx, verifier);
- trace_action("tmp = widget.validateSignature();\n");
- trace_action("if (tmp != %d)\n", valid_until);
- trace_action(" throw new RegressionError(widgetstr, 'valid until:', tmp, 'expected:', %d);\n", valid_until);
- trace_action("tmp = widget.isReadOnly();\n");
- trace_action("if (tmp != %d)\n", is_readonly);
- trace_action(" throw new RegressionError(widgetstr, 'is read-only:', tmp, 'expected:', %d);\n", is_readonly);
- trace_action("tmp = widget.checkCertificate();\n");
- trace_action("if (tmp != '%s')\n", cert_error);
- trace_action(" throw new RegressionError(widgetstr, 'certificate error:', tmp, 'expected:', %d);\n", cert_error);
- trace_action("tmp = widget.checkDigest();\n");
- trace_action("if (tmp != %q)\n", digest_error);
- trace_action(" throw new RegressionError(widgetstr, 'digest error:', tmp, 'expected:', %q);\n", digest_error);
- trace_action("tmp = widget.getSignatory();\n");
- trace_action("if (tmp != '%s')\n", signatory);
- trace_action(" throw new RegressionError(widgetstr, 'signatory:', '[', tmp, ']', 'expected:', '[', %q, ']');\n", signatory);
- }
- }
- }
- links = fz_load_links(ctx, fzpage);
- page_text = fz_new_stext_page_from_page(ctx, fzpage, NULL);
- if (currenticc)
- fz_enable_icc(ctx);
- else
- fz_disable_icc(ctx);
- if (currentseparations)
- {
- seps = fz_page_separations(ctx, fzpage);
- if (seps)
- {
- int i, n = fz_count_separations(ctx, seps);
- for (i = 0; i < n; i++)
- fz_set_separation_behavior(ctx, seps, i, FZ_SEPARATION_COMPOSITE);
- }
- else if (fz_page_uses_overprint(ctx, fzpage))
- seps = fz_new_separations(ctx, 0);
- else if (fz_document_output_intent(ctx, doc))
- seps = fz_new_separations(ctx, 0);
- }
- /* compute bounds here for initial window size */
- page_bounds = fz_bound_page_box(ctx, fzpage, currentbox);
- transform_page();
- area = fz_irect_from_rect(draw_page_bounds);
- page_tex.w = area.x1 - area.x0;
- page_tex.h = area.y1 - area.y0;
- page_contents_changed = 1;
- }
- static void render_page(void)
- {
- fz_irect bbox;
- fz_pixmap *pix;
- fz_device *dev;
- page_bounds = fz_bound_page_box(ctx, fzpage, currentbox);
- transform_page();
- fz_set_aa_level(ctx, currentaa);
- if (page_contents_changed)
- {
- fz_drop_pixmap(ctx, page_contents);
- page_contents = NULL;
- bbox = fz_round_rect(fz_transform_rect(fz_bound_page_box(ctx, fzpage, currentbox), draw_page_ctm));
- page_contents = fz_new_pixmap_with_bbox(ctx, profile, bbox, seps, 0);
- fz_clear_pixmap(ctx, page_contents);
- dev = fz_new_draw_device(ctx, draw_page_ctm, page_contents);
- fz_try(ctx)
- {
- fz_run_page_contents(ctx, fzpage, dev, fz_identity, NULL);
- fz_close_device(ctx, dev);
- }
- fz_always(ctx)
- fz_drop_device(ctx, dev);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- pix = fz_clone_pixmap_area_with_different_seps(ctx, page_contents, NULL, profile, NULL, fz_default_color_params, NULL);
- {
- dev = fz_new_draw_device(ctx, draw_page_ctm, pix);
- fz_try(ctx)
- {
- fz_run_page_annots(ctx, fzpage, dev, fz_identity, NULL);
- fz_run_page_widgets(ctx, fzpage, dev, fz_identity, NULL);
- fz_close_device(ctx, dev);
- }
- fz_always(ctx)
- fz_drop_device(ctx, dev);
- fz_catch(ctx)
- fz_rethrow(ctx);
- }
- if (currentinvert)
- {
- fz_invert_pixmap_luminance(ctx, pix);
- fz_gamma_pixmap(ctx, pix, 1 / 1.4f);
- }
- if (currenttint)
- {
- fz_tint_pixmap(ctx, pix, tint_black, tint_white);
- }
- ui_texture_from_pixmap(&page_tex, pix);
- fz_drop_pixmap(ctx, pix);
- FZ_LOG_DUMP_STORE(ctx, "Store state after page render:\n");
- }
- void render_page_if_changed(void)
- {
- if (pdf)
- {
- if (pdf_update_page(ctx, page))
- {
- trace_page_update();
- page_annots_changed = 1;
- }
- }
- if (oldpage.chapter != currentpage.chapter ||
- oldpage.page != currentpage.page ||
- oldzoom != currentzoom ||
- oldrotate != currentrotate ||
- oldinvert != currentinvert ||
- oldtint != currenttint ||
- oldicc != currenticc ||
- oldseparations != currentseparations ||
- oldaa != currentaa ||
- oldbox != currentbox)
- {
- page_contents_changed = 1;
- }
- if (page_contents_changed || page_annots_changed)
- {
- render_page();
- oldpage = currentpage;
- oldzoom = currentzoom;
- oldrotate = currentrotate;
- oldinvert = currentinvert;
- oldtint = currenttint;
- oldicc = currenticc;
- oldseparations = currentseparations;
- oldaa = currentaa;
- oldbox = currentbox;
- page_contents_changed = 0;
- page_annots_changed = 0;
- }
- }
- static struct mark save_mark()
- {
- struct mark mark;
- mark.loc = currentpage;
- mark.scroll = fz_transform_point_xy(scroll_x, scroll_y, view_page_inv_ctm);
- return mark;
- }
- static void restore_mark(struct mark mark)
- {
- currentpage = mark.loc;
- mark.scroll = fz_transform_point(mark.scroll, draw_page_ctm);
- scroll_x = mark.scroll.x;
- scroll_y = mark.scroll.y;
- }
- static int eqloc(fz_location a, fz_location b)
- {
- return a.chapter == b.chapter && a.page == b.page;
- }
- int search_has_results(void)
- {
- return !search_active && eqloc(search_hit_page, currentpage) && search_hit_count > 0;
- }
- static int is_first_page(fz_location loc)
- {
- return (loc.chapter == 0 && loc.page == 0);
- }
- static int is_last_page(fz_location loc)
- {
- fz_location last = fz_last_page(ctx, doc);
- return (loc.chapter == last.chapter && loc.page == last.page);
- }
- static void push_history(void)
- {
- if (history_count > 0 && eqloc(history[history_count-1].loc, currentpage))
- return;
- if (history_count + 1 >= (int)nelem(history))
- {
- memmove(history, history + 1, sizeof *history * (nelem(history) - 1));
- history[history_count] = save_mark();
- }
- else
- {
- history[history_count++] = save_mark();
- }
- }
- static void push_future(void)
- {
- if (future_count + 1 >= (int)nelem(future))
- {
- memmove(future, future + 1, sizeof *future * (nelem(future) - 1));
- future[future_count] = save_mark();
- }
- else
- {
- future[future_count++] = save_mark();
- }
- }
- static void clear_future(void)
- {
- future_count = 0;
- }
- static void jump_to_location(fz_location loc)
- {
- clear_future();
- push_history();
- currentpage = fz_clamp_location(ctx, doc, loc);
- push_history();
- }
- static void jump_to_location_xy(fz_location loc, float x, float y)
- {
- fz_point p = fz_transform_point_xy(x, y, draw_page_ctm);
- clear_future();
- push_history();
- currentpage = fz_clamp_location(ctx, doc, loc);
- scroll_x = p.x;
- scroll_y = p.y;
- push_history();
- }
- static void jump_to_page(int newpage)
- {
- clear_future();
- push_history();
- currentpage = fz_location_from_page_number(ctx, doc, newpage);
- currentpage = fz_clamp_location(ctx, doc, currentpage);
- push_history();
- }
- static void jump_to_page_xy(int newpage, float x, float y)
- {
- fz_point p = fz_transform_point_xy(x, y, draw_page_ctm);
- clear_future();
- push_history();
- currentpage = fz_location_from_page_number(ctx, doc, newpage);
- currentpage = fz_clamp_location(ctx, doc, currentpage);
- scroll_x = p.x;
- scroll_y = p.y;
- push_history();
- }
- static void pop_history(void)
- {
- fz_location here = currentpage;
- push_future();
- while (history_count > 0 && eqloc(currentpage, here))
- restore_mark(history[--history_count]);
- }
- static void pop_future(void)
- {
- fz_location here = currentpage;
- push_history();
- while (future_count > 0 && eqloc(currentpage, here))
- restore_mark(future[--future_count]);
- push_history();
- }
- static void relayout(void)
- {
- if (layout_em < 6) layout_em = 6;
- if (layout_em > 36) layout_em = 36;
- if (fz_is_document_reflowable(ctx, doc))
- {
- fz_bookmark mark = fz_make_bookmark(ctx, doc, currentpage);
- fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);
- currentpage = fz_lookup_bookmark(ctx, doc, mark);
- history_count = 0;
- future_count = 0;
- load_page();
- update_title();
- }
- }
- static int count_outline(fz_outline *node, int end)
- {
- int is_selected, n, p, np;
- int count = 0;
- if (!node)
- return 0;
- np = fz_page_number_from_location(ctx, doc, node->page);
- do
- {
- p = np;
- count += 1;
- n = end;
- if (node->next && (np = fz_page_number_from_location(ctx, doc, node->next->page)) >= 0)
- n = fz_page_number_from_location(ctx, doc, node->next->page);
- is_selected = 0;
- if (fz_count_chapters(ctx, doc) == 1)
- is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n));
- if (node->down && (node->is_open || is_selected))
- count += count_outline(node->down, end);
- node = node->next;
- }
- while (node);
- return count;
- }
- static void do_outline_imp(struct list *list, int end, fz_outline *node, int depth)
- {
- int is_selected, is_open, was_open, n, np;
- if (!node)
- return;
- np = fz_page_number_from_location(ctx, doc, node->page);
- do
- {
- int p = np;
- n = end;
- if (node->next && (np = fz_page_number_from_location(ctx, doc, node->next->page)) >= 0)
- n = np;
- is_open = was_open = node->is_open;
- is_selected = 0;
- if (fz_count_chapters(ctx, doc) == 1)
- is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n));
- if (ui_tree_item(list, node, node->title, is_selected, depth, !!node->down, &is_open))
- {
- node->is_open = is_open;
- if (p < 0)
- {
- currentpage = fz_resolve_link(ctx, doc, node->uri, &node->x, &node->y);
- jump_to_location_xy(currentpage, node->x, node->y);
- }
- else
- {
- jump_to_page_xy(p, node->x, node->y);
- }
- }
- node->is_open = is_open;
- if (node->down && (was_open || is_selected))
- do_outline_imp(list, n, node->down, depth + 1);
- node = node->next;
- }
- while (node);
- }
- static void do_outline(fz_outline *node)
- {
- static struct list list;
- ui_layout(L, BOTH, NW, 0, 0);
- ui_tree_begin(&list, count_outline(node, 65535), outline_w, 0, 1);
- do_outline_imp(&list, 65535, node, 0);
- ui_tree_end(&list);
- }
- static void do_undo(void)
- {
- static struct list list;
- int count = 0;
- int pos;
- int i;
- int desired = -1;
- if (pdf)
- pos = pdf_undoredo_state(ctx, pdf, &count);
- else
- pos = 0;
- ui_layout(L, BOTH, NW, 0, 0);
- ui_panel_begin(outline_w, 0, ui.padsize*2, ui.padsize*2, 1);
- ui_layout(T, X, NW, ui.padsize, ui.padsize);
- ui_label("Undo history:");
- ui_layout(B, X, NW, ui.padsize, ui.padsize);
- if (ui_button_aux("Redo", pos == count))
- desired = pos+1;
- if (ui_button_aux("Undo", pos == 0))
- desired = pos-1;
- ui_layout(ALL, BOTH, NW, ui.padsize, ui.padsize);
- ui_list_begin(&list, count+1, 0, ui.lineheight * 4 + 4);
- for (i = 0; i < count+1; i++)
- {
- const char *op;
- if (i == 0)
- op = "Original Document";
- else
- op = pdf_undoredo_step(ctx, pdf, i-1);
- if (ui_list_item(&list, (void *)(intptr_t)(i+1), op, i <= pos))
- {
- desired = i;
- }
- }
- ui_list_end(&list);
- if (desired != -1 && desired != pos)
- {
- clear_selected_annot();
- page_contents_changed = 1;
- while (pos > desired)
- {
- trace_action("doc.undo();\n");
- pdf_undo(ctx, pdf);
- pos--;
- }
- while (pos < desired)
- {
- trace_action("doc.redo();\n");
- pdf_redo(ctx, pdf);
- pos++;
- }
- load_page();
- }
- ui_panel_end();
- }
- static void do_layers(void)
- {
- const char *name;
- int n, i, on;
- ui_layout(L, BOTH, NW, 0, 0);
- ui_panel_begin(outline_w, 0, ui.padsize*2, ui.padsize*2, 1);
- ui_layout(T, X, NW, ui.padsize, ui.padsize);
- ui_label("Layers:");
- ui_layout(T, X, NW, ui.padsize*2, ui.padsize);
- if (pdf)
- {
- n = pdf_count_layers(ctx, pdf);
- for (i = 0; i < n; ++i)
- {
- name = pdf_layer_name(ctx, pdf, i);
- on = pdf_layer_is_enabled(ctx, pdf, i);
- if (ui_checkbox(name, &on))
- {
- pdf_enable_layer(ctx, pdf, i, on);
- page_contents_changed = 1;
- }
- }
- if (n == 0)
- ui_label("None");
- }
- else
- {
- ui_label("None");
- }
- ui_panel_end();
- }
- static void do_links(fz_link *link)
- {
- fz_rect bounds;
- fz_irect area;
- float link_x, link_y;
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- tooltip = NULL;
- while (link)
- {
- bounds = link->rect;
- bounds = fz_transform_rect(link->rect, view_page_ctm);
- area = fz_irect_from_rect(bounds);
- if (ui_mouse_inside(area))
- {
- if (!tooltip)
- tooltip = link->uri;
- ui.hot = link;
- if (!ui.active && ui.down)
- ui.active = link;
- }
- if (ui.hot == link || showlinks)
- {
- if (ui.active == link && ui.hot == link)
- glColor4f(0, 0, 1, 0.4f);
- else if (ui.hot == link)
- glColor4f(0, 0, 1, 0.2f);
- else
- glColor4f(0, 0, 1, 0.1f);
- glRectf(area.x0, area.y0, area.x1, area.y1);
- }
- if (ui.active == link && !ui.down)
- {
- if (ui.hot == link)
- {
- if (fz_is_external_link(ctx, link->uri))
- open_browser(link->uri);
- else
- {
- fz_location loc = fz_resolve_link(ctx, doc, link->uri, &link_x, &link_y);
- jump_to_location_xy(loc, link_x, link_y);
- }
- }
- }
- link = link->next;
- }
- glDisable(GL_BLEND);
- }
- static void do_page_selection(void)
- {
- static fz_point pt = { 0, 0 };
- static fz_quad hits[1000];
- fz_rect rect;
- int i, n;
- if (ui_mouse_inside(view_page_area))
- {
- ui.hot = &pt;
- if (!ui.active && ui.right)
- {
- ui.active = &pt;
- pt.x = ui.x;
- pt.y = ui.y;
- }
- }
- if (ui.active == &pt)
- {
- fz_point page_a = { pt.x, pt.y };
- fz_point page_b = { ui.x, ui.y };
- page_a = fz_transform_point(page_a, view_page_inv_ctm);
- page_b = fz_transform_point(page_b, view_page_inv_ctm);
- if (ui.mod == GLUT_ACTIVE_CTRL)
- fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_WORDS);
- else if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT)
- fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_LINES);
- if (ui.mod == GLUT_ACTIVE_SHIFT)
- {
- rect = fz_make_rect(
- fz_min(page_a.x, page_b.x),
- fz_min(page_a.y, page_b.y),
- fz_max(page_a.x, page_b.x),
- fz_max(page_a.y, page_b.y));
- n = 1;
- hits[0] = fz_quad_from_rect(rect);
- }
- else
- {
- n = fz_highlight_selection(ctx, page_text, page_a, page_b, hits, nelem(hits));
- }
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- glColor4f(0.0, 0.1, 0.4, 0.3f);
- glBegin(GL_QUADS);
- for (i = 0; i < n; ++i)
- {
- fz_quad thit = fz_transform_quad(hits[i], view_page_ctm);
- glVertex2f(thit.ul.x, thit.ul.y);
- glVertex2f(thit.ur.x, thit.ur.y);
- glVertex2f(thit.lr.x, thit.lr.y);
- glVertex2f(thit.ll.x, thit.ll.y);
- }
- glEnd();
- glDisable(GL_BLEND);
- if (!ui.right)
- {
- char *s;
- #ifdef _WIN32
- if (ui.mod == GLUT_ACTIVE_SHIFT)
- s = fz_copy_rectangle(ctx, page_text, rect, 1);
- else
- s = fz_copy_selection(ctx, page_text, page_a, page_b, 1);
- #else
- if (ui.mod == GLUT_ACTIVE_SHIFT)
- s = fz_copy_rectangle(ctx, page_text, rect, 0);
- else
- s = fz_copy_selection(ctx, page_text, page_a, page_b, 0);
- #endif
- ui_set_clipboard(s);
- fz_free(ctx, s);
- }
- }
- }
- static void do_search_hits(void)
- {
- int i;
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- glColor4f(1, 0, 0, 0.4f);
- glBegin(GL_QUADS);
- for (i = 0; i < search_hit_count; ++i)
- {
- fz_quad thit = fz_transform_quad(search_hit_quads[i], view_page_ctm);
- glVertex2f(thit.ul.x, thit.ul.y);
- glVertex2f(thit.ur.x, thit.ur.y);
- glVertex2f(thit.lr.x, thit.lr.y);
- glVertex2f(thit.ll.x, thit.ll.y);
- }
- glEnd();
- glDisable(GL_BLEND);
- }
- static void toggle_fullscreen(void)
- {
- static int win_x = 0, win_y = 0;
- static int win_w = 100, win_h = 100;
- if (!isfullscreen)
- {
- win_w = glutGet(GLUT_WINDOW_WIDTH);
- win_h = glutGet(GLUT_WINDOW_HEIGHT);
- win_x = glutGet(GLUT_WINDOW_X);
- win_y = glutGet(GLUT_WINDOW_Y);
- glutFullScreen();
- isfullscreen = 1;
- }
- else
- {
- glutPositionWindow(win_x, win_y);
- glutReshapeWindow(win_w, win_h);
- isfullscreen = 0;
- }
- }
- static void shrinkwrap(void)
- {
- int w = page_tex.w;
- int h = page_tex.h;
- if (showoutline || showundo || showlayers)
- w += outline_w + 4;
- if (showannotate)
- w += annotate_w;
- #if FZ_ENABLE_JS
- if (showconsole)
- h += console_h;
- #endif
- if (screen_w > 0 && w > screen_w)
- w = screen_w;
- if (screen_h > 0 && h > screen_h)
- h = screen_h;
- if (isfullscreen)
- toggle_fullscreen();
- glutReshapeWindow(w, h);
- }
- static struct input input_password;
- static void password_dialog(void)
- {
- int is;
- ui_dialog_begin(ui.gridsize*16, (ui.gridsize+ui.padsize*2)*3);
- {
- ui_layout(T, X, NW, ui.padsize, ui.padsize);
- ui_label("Password:");
- is = ui_input(&input_password, 200, 1);
- ui_layout(B, X, NW, ui.padsize, ui.padsize);
- ui_panel_begin(0, ui.gridsize, 0, 0, 0);
- {
- ui_layout(R, NONE, S, 0, 0);
- if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE))
- glutLeaveMainLoop();
- ui_spacer();
- if (ui_button("Okay") || is == UI_INPUT_ACCEPT)
- {
- password = input_password.text;
- ui.dialog = NULL;
- reload_document();
- shrinkwrap();
- }
- }
- ui_panel_end();
- }
- ui_dialog_end();
- }
- /* Parse "chapter:page" from anchor. "chapter:" is also accepted,
- * meaning first page. Return 1 if parsing succeeded, 0 if failed.
- */
- static int
- parse_location(const char *anc, fz_location *loc)
- {
- const char *s, *p;
- if (anc == NULL)
- return 0;
- s = anc;
- while (*s >= '0' && *s <= '9')
- s++;
- loc->chapter = fz_atoi(anc)-1;
- if (*s == 0)
- {
- *loc = fz_location_from_page_number(ctx, doc, loc->chapter);
- return 1;
- }
- if (*s != ':')
- return 0;
- p = ++s;
- while (*s >= '0' && *s <= '9')
- s++;
- if (s == p)
- loc->page = 0;
- else
- loc->page = fz_atoi(p)-1;
- return 1;
- }
- static void
- reload_or_start_journalling(void)
- {
- char journal[PATH_MAX];
- fz_strlcpy(journal, filename, sizeof(journal));
- fz_strlcat(journal, ".journal", sizeof(journal));
- fz_try(ctx)
- {
- /* Probe with fz_file_exists to avoid 'can't find' errors. */
- if (fz_file_exists(ctx, journal))
- pdf_load_journal(ctx, pdf, journal);
- }
- fz_catch(ctx)
- {
- /* Ignore any failures here. */
- }
- trace_action("doc.enableJournal();\n");
- pdf_enable_journal(ctx, pdf);
- }
- static void alert_box(const char *fmt, const char *str)
- {
- #ifdef _WIN32
- MessageBoxA(NULL, str, "MuPDF Alert", MB_ICONERROR);
- #else
- fprintf(stderr, "MuPDF Alert: %s\n", str);
- #endif
- }
- static void event_cb(fz_context *callback_ctx, pdf_document *callback_doc, pdf_doc_event *evt, void *data)
- {
- switch (evt->type)
- {
- case PDF_DOCUMENT_EVENT_ALERT:
- {
- pdf_alert_event *alert = pdf_access_alert_event(callback_ctx, evt);
- alert_box("%s", alert->message);
- }
- break;
- default:
- fz_throw(callback_ctx, FZ_ERROR_UNSUPPORTED, "event not yet implemented");
- break;
- }
- }
- static void load_document(void)
- {
- char accelpath[PATH_MAX];
- char *accel = NULL;
- time_t atime;
- time_t dtime;
- fz_location location;
- fz_drop_outline(ctx, outline);
- outline = NULL;
- fz_drop_document(ctx, doc);
- doc = NULL;
- if (!strncmp(filename, "file://", 7))
- {
- anchor = strchr(filename + 7, '#');
- if (anchor)
- {
- memmove(anchor + 1, anchor, strlen(anchor) + 1);
- *anchor = 0;
- anchor++;
- }
- memmove(filename, filename + 7, strlen(filename));
- }
- /* If there was an accelerator to load, what would it be called? */
- if (get_accelerator_filename(accelpath, sizeof(accelpath), 0))
- {
- /* Check whether that file exists, and isn't older than
- * the document. */
- atime = fz_stat_mtime(accelpath);
- dtime = fz_stat_mtime(filename);
- if (atime == 0)
- {
- /* No accelerator */
- }
- else if (atime > dtime)
- accel = accelpath;
- else
- {
- /* Accelerator data is out of date */
- #ifdef _WIN32
- fz_remove_utf8(accelpath);
- #else
- remove(accelpath);
- #endif
- accel = NULL; /* In case we have jumped up from below */
- }
- }
- trace_action("doc = Document.openDocument(%q);\n", filename);
- doc = fz_open_accelerated_document(ctx, filename, accel);
- pdf = pdf_specifics(ctx, doc);
- if (pdf && trace_file)
- {
- int needspass = pdf_needs_password(ctx, pdf);
- trace_action(
- "tmp = doc.needsPassword();\n"
- "if (tmp != %s)\n"
- " throw new RegressionError('Document password needed:', tmp, 'expected:', %s);\n",
- needspass ? "true" : "false",
- needspass ? "true" : "false");
- }
- if (fz_needs_password(ctx, doc))
- {
- int result = fz_authenticate_password(ctx, doc, password);
- if (pdf && trace_file)
- {
- trace_action(
- "tmp = doc.authenticatePassword(%q);\n"
- "if (tmp != %s)\n"
- " throw new RegressionError('Open document with password %q result: %s', 'expected:', '%s');\n",
- password,
- result ? "true" : "false",
- password,
- !result ? "pass" : "fail",
- result ? "pass" : "fail");
- }
- if (!result)
- {
- fz_drop_document(ctx, doc);
- doc = NULL;
- ui_input_init(&input_password, "");
- ui.focus = &input_password;
- ui.dialog = password_dialog;
- return;
- }
- }
- fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);
- fz_try(ctx)
- outline = fz_load_outline(ctx, doc);
- fz_catch(ctx)
- {
- fz_report_error(ctx);
- outline = NULL;
- }
- load_history();
- if (pdf)
- {
- #if FZ_ENABLE_JS
- if (enable_js)
- {
- trace_action("doc.enableJS();\n");
- pdf_enable_js(ctx, pdf);
- pdf_js_set_console(ctx, pdf, &gl_js_console, NULL);
- }
- #endif
- reload_or_start_journalling();
- if (trace_file)
- {
- int vsns = pdf_count_versions(ctx, pdf);
- trace_action(
- "tmp = doc.countVersions();\n"
- "if (tmp != %d)\n"
- " throw new RegressionError('Document versions:', tmp, 'expected:', %d);\n",
- vsns, vsns);
- if (vsns > 1)
- {
- int valid = pdf_validate_change_history(ctx, pdf);
- trace_action("tmp = doc.validateChangeHistory();\n");
- trace_action("if (tmp != %d)\n", valid);
- trace_action(" throw new RegressionError('History validation:', tmp, 'expected:', %d);\n", valid);
- }
- }
- }
- if (anchor)
- {
- if (parse_location(anchor, &location))
- jump_to_location(location);
- else
- {
- location = fz_resolve_link(ctx, doc, anchor, NULL, NULL);
- if (location.page < 0)
- fz_warn(ctx, "cannot find location: %s", anchor);
- else
- jump_to_location(location);
- }
- }
- anchor = NULL;
- oldpage = currentpage = fz_clamp_location(ctx, doc, currentpage);
- if (pdf)
- pdf_set_doc_event_callback(ctx, pdf, event_cb, NULL, NULL);
- }
- static void reflow_document(void)
- {
- char buf[256];
- fz_document *new_doc;
- fz_stext_options opts;
- if (fz_is_document_reflowable(ctx, doc))
- return;
- fz_drop_outline(ctx, outline);
- outline = NULL;
- fz_parse_stext_options(ctx, &opts, reflow_options);
- new_doc = fz_open_reflowed_document(ctx, doc, &opts);
- fz_drop_document(ctx, doc);
- doc = new_doc;
- pdf = NULL;
- page = NULL;
- fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);
- fz_try(ctx)
- outline = fz_load_outline(ctx, doc);
- fz_catch(ctx)
- outline = NULL;
- fz_strlcpy(buf, filename, sizeof buf);
- fz_snprintf(filename, sizeof filename, "%s.xhtml", buf);
- load_history();
- if (anchor)
- jump_to_page(fz_atoi(anchor) - 1);
- anchor = NULL;
- currentpage = fz_clamp_location(ctx, doc, currentpage);
- }
- void reload_document(void)
- {
- save_history();
- save_accelerator();
- load_document();
- if (doc)
- {
- if (reflow_options)
- reflow_document();
- load_page();
- update_title();
- }
- }
- static void toggle_outline(void)
- {
- if (outline)
- {
- showoutline = !showoutline;
- showundo = showlayers = 0;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- }
- }
- static void toggle_undo(void)
- {
- showundo = !showundo;
- showoutline = showlayers = 0;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- }
- static void toggle_layers(void)
- {
- showlayers = !showlayers;
- showoutline = showundo = 0;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- }
- void toggle_annotate(int mode)
- {
- if (pdf)
- {
- if (showannotate != mode)
- showannotate = mode;
- else
- showannotate = ANNOTATE_MODE_NONE;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- }
- }
- static void set_zoom(int z, int cx, int cy)
- {
- z = fz_clamp(z, MINRES, MAXRES);
- scroll_x = (scroll_x + cx - canvas_x) * z / currentzoom - cx + canvas_x;
- scroll_y = (scroll_y + cy - canvas_y) * z / currentzoom - cy + canvas_y;
- currentzoom = z;
- }
- static void auto_zoom_w(void)
- {
- currentzoom = fz_clamp(currentzoom * canvas_w / page_tex.w, MINRES, MAXRES);
- }
- static void auto_zoom_h(void)
- {
- currentzoom = fz_clamp(currentzoom * canvas_h / page_tex.h, MINRES, MAXRES);
- }
- static void auto_zoom(void)
- {
- float page_a = (float) page_tex.w / page_tex.h;
- float screen_a = (float) canvas_w / canvas_h;
- if (page_a > screen_a)
- auto_zoom_w();
- else
- auto_zoom_h();
- }
- static void smart_move_backward(void)
- {
- int slop_x = page_tex.w / 20;
- int slop_y = page_tex.h / 20;
- if (scroll_y <= slop_y)
- {
- if (scroll_x <= slop_x)
- {
- fz_location prev = fz_previous_page(ctx, doc, currentpage);
- if (!eqloc(currentpage, prev))
- {
- scroll_x = (page_tex.w <= canvas_w) ? 0 : page_tex.w - canvas_w;
- scroll_y = (page_tex.h <= canvas_h) ? 0 : page_tex.h - canvas_h;
- currentpage = prev;
- }
- }
- else
- {
- scroll_y = page_tex.h;
- scroll_x -= canvas_w * 9 / 10;
- }
- }
- else
- {
- scroll_y -= canvas_h * 9 / 10;
- }
- }
- static void smart_move_forward(void)
- {
- int slop_x = page_tex.w / 20;
- int slop_y = page_tex.h / 20;
- if (scroll_y + canvas_h >= page_tex.h - slop_y)
- {
- if (scroll_x + canvas_w >= page_tex.w - slop_x)
- {
- fz_location next = fz_next_page(ctx, doc, currentpage);
- if (!eqloc(currentpage, next))
- {
- scroll_x = 0;
- scroll_y = 0;
- currentpage = next;
- }
- }
- else
- {
- scroll_y = 0;
- scroll_x += canvas_w * 9 / 10;
- }
- }
- else
- {
- scroll_y += canvas_h * 9 / 10;
- }
- }
- static void clear_search(void)
- {
- showsearch = 0;
- search_page = currentpage;
- search_hit_page = fz_make_location(-1, -1);
- search_hit_count = 0;
- }
- #if FZ_ENABLE_JS
- #define MAX_CONSOLE_LINES 500
- static fz_buffer *console_buffer;
- static int console_scroll = 0;
- static int console_sticky = 1;
- static int console_lines = 0;
- static struct readline console_readline;
- static void (*warning_callback)(void *, const char *) = NULL;
- static void (*error_callback)(void *, const char *) = NULL;
- static void *warning_user = NULL;
- static void *error_user = NULL;
- static void
- remove_oldest_console_line()
- {
- unsigned char *s;
- size_t size = fz_buffer_storage(ctx, console_buffer, &s);
- unsigned char *p = s;
- unsigned char *e = s + size;
- while (p < e && *p != '\n')
- p++;
- if (p < e && *p == '\n')
- {
- p++;
- memmove(s, p, e - p);
- fz_resize_buffer(ctx, console_buffer, e - p);
- console_lines--;
- }
- }
- static void
- gl_js_console_write(void *user, const char *message)
- {
- const char *p = NULL;
- if (message == NULL)
- return;
- p = message;
- while (*p)
- {
- if (*p == '\n')
- console_lines++;
- if (console_lines >= MAX_CONSOLE_LINES)
- remove_oldest_console_line();
- if (*p)
- fz_append_byte(ctx, console_buffer, *p);
- p++;
- }
- }
- static void
- gl_js_console_show(void *user)
- {
- if (showconsole)
- return;
- showconsole = 1;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- ui.focus = &console_readline;
- }
- static void
- gl_js_console_hide(void *user)
- {
- if (!showconsole)
- return;
- showconsole = 0;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- ui.focus = NULL;
- }
- static void
- gl_js_console_clear(void *user)
- {
- fz_resize_buffer(ctx, console_buffer, 0);
- console_lines = 0;
- }
- static void console_warn(void *user, const char *message)
- {
- gl_js_console_write(ctx, "\nwarning: ");
- gl_js_console_write(ctx, message);
- if (warning_callback)
- warning_callback(warning_user, message);
- }
- static void console_err(void *user, const char *message)
- {
- gl_js_console_write(ctx, "\nerror: ");
- gl_js_console_write(ctx, message);
- if (error_callback)
- error_callback(error_user, message);
- }
- static void console_init(void)
- {
- ui_readline_init(&console_readline, NULL);
- console_buffer = fz_new_buffer(ctx, 0);
- fz_append_printf(ctx, console_buffer, "Welcome to MuPDF %s with MuJS %d.%d.%d",
- FZ_VERSION,
- JS_VERSION_MAJOR, JS_VERSION_MINOR, JS_VERSION_PATCH);
- warning_callback = fz_warning_callback(ctx, &warning_user);
- fz_set_warning_callback(ctx, console_warn, NULL);
- error_callback = fz_error_callback(ctx, &error_user);
- fz_set_error_callback(ctx, console_err, NULL);
- }
- static void console_fin(void)
- {
- fz_set_warning_callback(ctx, warning_callback, warning_user);
- fz_set_error_callback(ctx, error_callback, error_user);
- fz_drop_buffer(ctx, console_buffer);
- console_buffer = NULL;
- }
- static pdf_js_console gl_js_console = {
- NULL,
- gl_js_console_show,
- gl_js_console_hide,
- gl_js_console_clear,
- gl_js_console_write,
- };
- static void toggle_console(void)
- {
- showconsole = !showconsole;
- if (showconsole)
- ui.focus = &console_readline;
- if (canvas_w == page_tex.w && canvas_h == page_tex.h)
- shrinkwrap();
- }
- void do_console(void)
- {
- pdf_js_console *console = pdf_js_get_console(ctx, pdf);
- char *result = NULL;
- const char *accepted = NULL;
- fz_var(result);
- ui_layout(B, BOTH, NW, 0, 0);
- ui_panel_begin(canvas_w, console_h, ui.padsize, ui.padsize, 1);
- ui_layout(B, X, NW, 0, 0);
- accepted = ui_readline(&console_readline, 0);
- if (accepted != NULL)
- {
- ui.focus = &console_readline;
- if (console_readline.input.text[0])
- {
- fz_try(ctx)
- {
- if (console && console->write)
- {
- console->write(ctx, "\n> ");
- console->write(ctx, console_readline.input.text);
- }
- pdf_js_execute(pdf ? pdf->js : NULL, "console", console_readline.input.text, &result);
- if (result && console && console->write)
- {
- console->write(ctx, "\n");
- console->write(ctx, result);
- }
- }
- fz_always(ctx)
- fz_free(ctx, result);
- fz_catch(ctx)
- {
- if (console)
- {
- console->write(ctx, "\nError: ");
- console->write(ctx, fz_caught_message(ctx));
- fz_report_error(ctx);
- }
- }
- fz_flush_warnings(ctx);
- ui_input_init(&console_readline.input, "");
- }
- }
- ui_layout(ALL, BOTH, NW, ui.padsize, ui.padsize);
- // White background!
- glColorHex(0xF5F5F5);
- glRectf(ui.cavity->x0, ui.cavity->y0, ui.cavity->x1, ui.cavity->y1);
- char *console_string = (char *) fz_string_from_buffer(ctx, console_buffer);
- ui_label_with_scrollbar(console_string, 0, 10, &console_scroll, &console_sticky);
- ui_panel_end();
- }
- #endif
- static void do_app(void)
- {
- if (ui.mod == GLUT_ACTIVE_ALT)
- {
- if (ui.key == KEY_F4)
- quit();
- if (ui.key == KEY_LEFT)
- ui.key = 't', ui.mod = 0, ui.plain = 1;
- if (ui.key == KEY_RIGHT)
- ui.key = 'T', ui.mod = 0, ui.plain = 1;
- }
- if (trace_file && ui.key == KEY_CTL_P)
- trace_save_snapshot();
- if (!ui.focus && ui.key && ui.plain)
- {
- switch (ui.key)
- {
- case KEY_ESCAPE: clear_search(); ui_select_annot(NULL); break;
- case KEY_F1: ui.dialog = help_dialog; break;
- case 'a': toggle_annotate(ANNOTATE_MODE_NORMAL); break;
- case 'R': toggle_annotate(ANNOTATE_MODE_REDACT); break;
- case 'o': toggle_outline(); break;
- case 'u': toggle_undo(); break;
- case 'Y': toggle_layers(); break;
- case 'L': showlinks = !showlinks; break;
- case 'F': showform = !showform; break;
- case 'i': ui.dialog = info_dialog; break;
- #if FZ_ENABLE_JS
- case '`': case KEY_F12: toggle_console(); break;
- #endif
- case 'r': reload(); break;
- case 'q': quit(); break;
- case 'S': do_save_pdf_file(); break;
- case '>': layout_em = number > 0 ? number : layout_em + 1; relayout(); break;
- case '<': layout_em = number > 0 ? number : layout_em - 1; relayout(); break;
- case 'C': currenttint = !currenttint; break;
- case 'I': currentinvert = !currentinvert; break;
- case 'e': currentseparations = !currentseparations; break;
- case 'E': currenticc = !currenticc; break;
- case 'f': toggle_fullscreen(); break;
- case 'w': shrinkwrap(); break;
- case 'W': auto_zoom_w(); break;
- case 'H': auto_zoom_h(); break;
- case 'Z': auto_zoom(); break;
- case 'z': set_zoom(number > 0 ? number : DEFRES, canvas_w/2, canvas_h/2); break;
- case '+': set_zoom(zoom_in(currentzoom), ui.x, ui.y); break;
- case '-': set_zoom(zoom_out(currentzoom), ui.x, ui.y); break;
- case '[': currentrotate -= 90; break;
- case ']': currentrotate += 90; break;
- case 'k': case KEY_UP: scroll_y -= canvas_h/10; break;
- case 'j': case KEY_DOWN: scroll_y += canvas_h/10; break;
- case 'h': case KEY_LEFT: scroll_x -= canvas_w/10; break;
- case 'l': case KEY_RIGHT: scroll_x += canvas_w/10; break;
- case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break;
- case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break;
- case 'g': jump_to_page(number - 1); break;
- case 'G': jump_to_location(fz_last_page(ctx, doc)); break;
- case ',': case KEY_PAGE_UP:
- number = fz_maxi(number, 1);
- while (number--)
- currentpage = fz_previous_page(ctx, doc, currentpage);
- break;
- case '.': case KEY_PAGE_DOWN:
- number = fz_maxi(number, 1);
- while (number--)
- currentpage = fz_next_page(ctx, doc, currentpage);
- break;
- case 'A':
- if (number == 0)
- currentaa = (currentaa == 8 ? 0 : 8);
- else
- currentaa = number;
- break;
- case 'B':
- currentbox += 1;
- if (currentbox >= FZ_UNKNOWN_BOX)
- currentbox = FZ_MEDIA_BOX;
- break;
- case 'm':
- if (number == 0)
- push_history();
- else if (number > 0 && number < (int)nelem(marks))
- marks[number] = save_mark();
- break;
- case 't':
- if (number == 0)
- {
- if (history_count > 0)
- pop_history();
- }
- else if (number > 0 && number < (int)nelem(marks))
- {
- struct mark mark = marks[number];
- restore_mark(mark);
- jump_to_location(mark.loc);
- }
- break;
- case 'T':
- if (number == 0)
- {
- if (future_count > 0)
- pop_future();
- }
- break;
- case '/':
- clear_search();
- search_dir = 1;
- showsearch = 1;
- ui.focus = &search_input;
- search_input.p = search_input.text;
- search_input.q = search_input.end;
- break;
- case '?':
- clear_search();
- search_dir = -1;
- showsearch = 1;
- ui.focus = &search_input;
- search_input.p = search_input.text;
- search_input.q = search_input.end;
- break;
- case 'N':
- search_dir = -1;
- search_active = !!search_needle;
- if (eqloc(search_hit_page, currentpage))
- {
- if (is_first_page(search_page))
- search_active = 0;
- else
- search_page = fz_previous_page(ctx, doc, currentpage);
- }
- else
- {
- search_page = currentpage;
- }
- search_hit_page = fz_make_location(-1, -1);
- break;
- case 'n':
- search_dir = 1;
- search_active = !!search_needle;
- if (eqloc(search_hit_page, currentpage))
- {
- if (is_last_page(search_page))
- search_active = 0;
- else
- search_page = fz_next_page(ctx, doc, currentpage);
- }
- else
- {
- search_page = currentpage;
- }
- search_hit_page = fz_make_location(-1, -1);
- break;
- default:
- if (ui.key < '0' || ui.key > '9')
- {
- number = 0;
- return; /* unrecognized key, pass it through */
- }
- }
- if (ui.key >= '0' && ui.key <= '9')
- number = number * 10 + ui.key - '0';
- else
- number = 0;
- currentpage = fz_clamp_location(ctx, doc, currentpage);
- while (currentrotate < 0) currentrotate += 360;
- while (currentrotate >= 360) currentrotate -= 360;
- if (!eqloc(search_hit_page, currentpage))
- search_hit_page = fz_make_location(-1, -1); /* clear highlights when navigating */
- ui.key = 0; /* we ate the key event, so zap it */
- }
- }
- typedef struct
- {
- int max;
- int len;
- pdf_obj **sig;
- } sigs_list;
- static void
- process_sigs(fz_context *ctx_, pdf_obj *field, void *arg, pdf_obj **ft)
- {
- sigs_list *sigs = (sigs_list *)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;
- if (sigs->len == sigs->max)
- {
- int newsize = sigs->max * 2;
- if (newsize == 0)
- newsize = 4;
- sigs->sig = fz_realloc_array(ctx, sigs->sig, newsize, pdf_obj *);
- sigs->max = newsize;
- }
- sigs->sig[sigs->len++] = field;
- }
- static char *short_signature_error_desc(pdf_signature_error err)
- {
- switch (err)
- {
- case PDF_SIGNATURE_ERROR_OKAY:
- return "OK";
- case PDF_SIGNATURE_ERROR_NO_SIGNATURES:
- return "No signatures";
- case PDF_SIGNATURE_ERROR_NO_CERTIFICATE:
- return "No certificate";
- case PDF_SIGNATURE_ERROR_DIGEST_FAILURE:
- return "Invalid";
- case PDF_SIGNATURE_ERROR_SELF_SIGNED:
- return "Self-signed";
- case PDF_SIGNATURE_ERROR_SELF_SIGNED_IN_CHAIN:
- return "Self-signed in chain";
- case PDF_SIGNATURE_ERROR_NOT_TRUSTED:
- return "Untrusted";
- case PDF_SIGNATURE_ERROR_NOT_SIGNED:
- return "Not signed";
- default:
- case PDF_SIGNATURE_ERROR_UNKNOWN:
- return "Unknown error";
- }
- }
- const char *format_date(int64_t secs64)
- {
- static char buf[100];
- #ifdef _POSIX_SOURCE
- struct tm tmbuf, *tm;
- #else
- struct tm *tm;
- #endif
- time_t secs = (time_t)secs64;
- if (secs <= 0)
- return NULL;
- #ifdef _POSIX_SOURCE
- tm = gmtime_r(&secs, &tmbuf);
- #else
- tm = gmtime(&secs);
- #endif
- if (!tm)
- return NULL;
- strftime(buf, sizeof buf, "%Y-%m-%d %H:%M UTC", tm);
- return buf;
- }
- static fz_buffer *format_info_text()
- {
- fz_buffer *out = fz_new_buffer(ctx, 4096);
- pdf_document *pdoc = pdf_specifics(ctx, doc);
- sigs_list list = { 0, 0, NULL };
- char buf[100];
- if (pdoc)
- {
- static pdf_obj *ft_list[2] = { PDF_NAME(FT), NULL };
- pdf_obj *ft = NULL;
- pdf_obj *form_fields = pdf_dict_getp(ctx, pdf_trailer(ctx, pdoc), "Root/AcroForm/Fields");
- pdf_walk_tree(ctx, form_fields, PDF_NAME(Kids), process_sigs, NULL, &list, &ft_list[0], &ft);
- }
- fz_append_printf(ctx, out, "File: %s\n\n", filename);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_TITLE, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Title: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_AUTHOR, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Author: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Format: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_ENCRYPTION, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Encryption: %s\n", buf);
- fz_append_string(ctx, out, "\n");
- if (pdoc)
- {
- int updates = pdf_count_versions(ctx, pdoc);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_CREATOR, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "PDF Creator: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_PRODUCER, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "PDF Producer: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_SUBJECT, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Subject: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_KEYWORDS, buf, sizeof buf) > 0)
- fz_append_printf(ctx, out, "Keywords: %s\n", buf);
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_CREATIONDATE, buf, sizeof buf) > 0)
- {
- const char *s = format_date(pdf_parse_date(ctx, buf));
- if (s)
- fz_append_printf(ctx, out, "Creation date: %s\n", s);
- }
- if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_MODIFICATIONDATE, buf, sizeof buf) > 0)
- {
- const char *s = format_date(pdf_parse_date(ctx, buf));
- if (s)
- fz_append_printf(ctx, out, "Modification date: %s\n", s);
- }
- buf[0] = 0;
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT))
- fz_strlcat(buf, "print, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_COPY))
- fz_strlcat(buf, "copy, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_EDIT))
- fz_strlcat(buf, "edit, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_ANNOTATE))
- fz_strlcat(buf, "annotate, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_FORM))
- fz_strlcat(buf, "form, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_ACCESSIBILITY))
- fz_strlcat(buf, "accessibility, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_ASSEMBLE))
- fz_strlcat(buf, "assemble, ", sizeof buf);
- if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT_HQ))
- fz_strlcat(buf, "print-hq, ", sizeof buf);
- if (strlen(buf) > 2)
- buf[strlen(buf)-2] = 0;
- else
- fz_strlcat(buf, "none", sizeof buf);
- fz_append_printf(ctx, out, "Permissions: %s\n", buf);
- fz_append_printf(ctx, out, "PDF %sdocument with %d update%s\n",
- pdf_doc_was_linearized(ctx, pdoc) ? "linearized " : "",
- updates, updates > 1 ? "s" : "");
- if (updates > 0)
- {
- int n = pdf_validate_change_history(ctx, pdoc);
- if (n == 0)
- fz_append_printf(ctx, out, "Change history seems valid.\n");
- else if (n == 1)
- fz_append_printf(ctx, out, "Invalid changes made to the document in the last update.\n");
- else if (n == 2)
- fz_append_printf(ctx, out, "Invalid changes made to the document in the penultimate update.\n");
- else
- fz_append_printf(ctx, out, "Invalid changes made to the document %d updates ago.\n", n);
- }
- if (list.len)
- {
- int i;
- for (i = 0; i < list.len; i++)
- {
- pdf_obj *field = list.sig[i];
- fz_try(ctx)
- {
- if (pdf_signature_is_signed(ctx, pdf, field))
- {
- pdf_pkcs7_verifier *verifier = pkcs7_openssl_new_verifier(ctx);
- pdf_signature_error sig_cert_error = pdf_check_certificate(ctx, verifier, pdf, field);
- pdf_signature_error sig_digest_error = pdf_check_digest(ctx, verifier, pdf, field);
- fz_append_printf(ctx, out, "Signature %d: CERT: %s, DIGEST: %s%s\n", i+1,
- short_signature_error_desc(sig_cert_error),
- short_signature_error_desc(sig_digest_error),
- pdf_signature_incremental_change_since_signing(ctx, pdf, field) ? ", Changed since": "");
- pdf_drop_verifier(ctx, verifier);
- }
- else
- fz_append_printf(ctx, out, "Signature %d: Unsigned\n", i+1);
- }
- fz_catch(ctx)
- fz_append_printf(ctx, out, "Signature %d: Error\n", i+1);
- }
- fz_free(ctx, list.sig);
- if (updates == 0)
- fz_append_printf(ctx, out, "No updates since document creation\n");
- else
- {
- int n = pdf_validate_change_history(ctx, pdf);
- if (n == 0)
- fz_append_printf(ctx, out, "Document changes conform to permissions\n");
- else
- fz_append_printf(ctx, out, "Document permissions violated %d updates ago\n", n);
- }
- }
- fz_append_string(ctx, out, "\n");
- }
- fz_append_printf(ctx, out, "Page: %d / %d\n", fz_page_number_from_location(ctx, doc, currentpage)+1, fz_count_pages(ctx, doc));
- fz_append_printf(ctx, out, "Page Label: %s\n", fz_page_label(ctx, fzpage, buf, sizeof buf));
- {
- int w = (int)(page_bounds.x1 - page_bounds.x0 + 0.5f);
- int h = (int)(page_bounds.y1 - page_bounds.y0 + 0.5f);
- const char *size = paper_size_name(w, h);
- if (!size)
- size = paper_size_name(h, w);
- if (size)
- fz_append_printf(ctx, out, "Size: %d x %d (%s - %s)\n", w, h, fz_string_from_box_type(currentbox), size);
- else
- fz_append_printf(ctx, out, "Size: %d x %d (%s)\n", w, h, fz_string_from_box_type(currentbox));
- }
- fz_append_printf(ctx, out, "ICC rendering: %s.\n", currenticc ? "on" : "off");
- fz_append_printf(ctx, out, "Spot rendering: %s.\n", currentseparations ? "on" : "off");
- if (fz_is_document_reflowable(ctx, doc))
- fz_append_printf(ctx, out, "Em size: %g\n", layout_em);
- return out;
- }
- static void do_canvas(void)
- {
- static int saved_scroll_x = 0;
- static int saved_scroll_y = 0;
- static int saved_ui_x = 0;
- static int saved_ui_y = 0;
- fz_irect area;
- int page_x, page_y;
- tooltip = NULL;
- ui_layout(ALL, BOTH, NW, 0, 0);
- ui_pack_push(area = ui_pack(0, 0));
- glScissor(area.x0, ui.window_h-area.y1, area.x1-area.x0, area.y1-area.y0);
- glEnable(GL_SCISSOR_TEST);
- canvas_x = area.x0;
- canvas_y = area.y0;
- canvas_w = area.x1 - area.x0;
- canvas_h = area.y1 - area.y0;
- if (ui_mouse_inside(area))
- {
- ui.hot = doc;
- if (!ui.active && ui.middle)
- {
- ui.active = doc;
- saved_scroll_x = scroll_x;
- saved_scroll_y = scroll_y;
- saved_ui_x = ui.x;
- saved_ui_y = ui.y;
- }
- }
- if (ui.hot == doc)
- {
- if (ui.mod == 0)
- {
- scroll_x -= ui.scroll_x * ui.lineheight * 3;
- scroll_y -= ui.scroll_y * ui.lineheight * 3;
- }
- else if (ui.mod == GLUT_ACTIVE_CTRL)
- {
- if (ui.scroll_y > 0) set_zoom(zoom_in(currentzoom), ui.x, ui.y);
- if (ui.scroll_y < 0) set_zoom(zoom_out(currentzoom), ui.x, ui.y);
- }
- }
- render_page_if_changed();
- if (ui.active == doc)
- {
- scroll_x = saved_scroll_x + saved_ui_x - ui.x;
- scroll_y = saved_scroll_y + saved_ui_y - ui.y;
- }
- if (page_tex.w <= canvas_w)
- {
- scroll_x = 0;
- page_x = canvas_x + (canvas_w - page_tex.w) / 2;
- }
- else
- {
- scroll_x = fz_clamp(scroll_x, 0, page_tex.w - canvas_w);
- page_x = canvas_x - scroll_x;
- }
- if (page_tex.h <= canvas_h)
- {
- scroll_y = 0;
- page_y = canvas_y + (canvas_h - page_tex.h) / 2;
- }
- else
- {
- scroll_y = fz_clamp(scroll_y, 0, page_tex.h - canvas_h);
- page_y = canvas_y - scroll_y;
- }
- view_page_ctm = draw_page_ctm;
- view_page_ctm.e += page_x;
- view_page_ctm.f += page_y;
- view_page_inv_ctm = fz_invert_matrix(view_page_ctm);
- view_page_bounds = fz_transform_rect(page_bounds, view_page_ctm);
- view_page_area = fz_irect_from_rect(view_page_bounds);
- ui_draw_image(&page_tex, page_x, page_y);
- if (search_active)
- {
- int chapters = fz_count_chapters(ctx, doc);
- ui_layout(T, X, NW, 0, 0);
- ui_panel_begin(0, ui.gridsize + ui.padsize*4, ui.padsize*2, ui.padsize*2, 1);
- ui_layout(L, NONE, W, ui.padsize, 0);
- if (chapters == 1 && search_page.chapter == 0)
- ui_label("Searching page %d...", search_page.page);
- else
- ui_label("Searching chapter %d page %d...", search_page.chapter, search_page.page);
- ui_panel_end();
- }
- else
- {
- if (pdf)
- {
- do_annotate_canvas(area);
- do_widget_canvas(area);
- }
- do_links(links);
- do_page_selection();
- if (eqloc(search_hit_page, currentpage) && search_hit_count > 0)
- do_search_hits();
- }
- if (showsearch)
- {
- ui_layout(T, X, NW, 0, 0);
- ui_panel_begin(0, ui.gridsize + ui.padsize*4, ui.padsize*2, ui.padsize*2, 1);
- ui_layout(L, NONE, W, ui.padsize, 0);
- ui_label("Search:");
- ui_layout(ALL, X, E, ui.padsize, 0);
- if (ui_input(&search_input, 0, 1) == UI_INPUT_ACCEPT)
- {
- showsearch = 0;
- search_page = fz_make_location(-1, -1);
- if (search_needle)
- {
- fz_free(ctx, search_needle);
- search_needle = NULL;
- }
- if (search_input.end > search_input.text)
- {
- search_needle = fz_strdup(ctx, search_input.text);
- search_active = 1;
- search_page = currentpage;
- }
- }
- if (ui.focus != &search_input)
- showsearch = 0;
- ui_panel_end();
- }
- if (tooltip)
- {
- ui_layout(B, X, N, 0, 0);
- ui_panel_begin(0, ui.gridsize, ui.padsize*2, ui.padsize*2, 1);
- ui_layout(L, NONE, W, ui.padsize, 0);
- ui_label("%s", tooltip);
- ui_panel_end();
- }
- ui_pack_pop();
- glDisable(GL_SCISSOR_TEST);
- }
- void do_main(void)
- {
- if (search_active)
- {
- int start_time = glutGet(GLUT_ELAPSED_TIME);
- if (ui.key == KEY_ESCAPE)
- search_active = 0;
- /* ignore events during search */
- ui.key = ui.mod = ui.plain = 0;
- ui.down = ui.middle = ui.right = 0;
- while (search_active && glutGet(GLUT_ELAPSED_TIME) < start_time + 200)
- {
- search_hit_count = fz_search_chapter_page_number(ctx, doc,
- search_page.chapter, search_page.page,
- search_needle,
- NULL, search_hit_quads, nelem(search_hit_quads));
- trace_action("hits = doc.loadPage(%d).search(%q);\n", fz_page_number_from_location(ctx, doc, search_page), search_needle);
- trace_action("print('Search page %d:', repr(%q), hits.length, repr(hits));\n", fz_page_number_from_location(ctx, doc, search_page), search_needle);
- if (search_hit_count)
- {
- float search_hit_x = search_hit_quads[0].ul.x;
- float search_hit_y = search_hit_quads[0].ul.y;
- search_active = 0;
- search_hit_page = search_page;
- jump_to_location_xy(search_hit_page, search_hit_x, search_hit_y);
- }
- else
- {
- if (search_dir > 0)
- {
- if (is_last_page(search_page))
- search_active = 0;
- else
- search_page = fz_next_page(ctx, doc, search_page);
- }
- else
- {
- if (is_first_page(search_page))
- search_active = 0;
- else
- search_page = fz_previous_page(ctx, doc, search_page);
- }
- }
- }
- /* keep searching later */
- if (search_active)
- glutPostRedisplay();
- }
- do_app();
- if (showoutline)
- do_outline(outline);
- else if (showundo)
- do_undo();
- else if (showlayers)
- do_layers();
- if (showoutline || showundo || showlayers)
- ui_splitter(&outline_start_x, &outline_w, 6*ui.gridsize, 20*ui.gridsize, R);
- if (!eqloc(oldpage, currentpage) || oldseparations != currentseparations || oldicc != currenticc)
- {
- load_page();
- update_title();
- }
- if (showannotate)
- {
- ui_layout(R, BOTH, NW, 0, 0);
- ui_panel_begin(annotate_w, 0, ui.padsize*2, ui.padsize*2, 1);
- if (showannotate == ANNOTATE_MODE_NORMAL)
- do_annotate_panel();
- else
- do_redact_panel();
- ui_panel_end();
- }
- #if FZ_ENABLE_JS
- if (showconsole)
- {
- do_console();
- ui_splitter(&console_start_y, &console_h, 6*ui.lineheight, 25*ui.lineheight, T);
- }
- #endif
- do_canvas();
- if (pdf)
- {
- if (document_shown_as_dirty != pdf_has_unsaved_changes(ctx, pdf))
- update_title();
- }
- }
- void run_main_loop(void)
- {
- if (currentinvert)
- glClearColor(0, 0, 0, 1);
- else
- glClearColor(0.3f, 0.3f, 0.3f, 1);
- ui_begin();
- fz_try(ctx)
- {
- if (ui.dialog)
- ui.dialog();
- else
- do_main();
- }
- fz_catch(ctx)
- {
- ui_show_error_dialog("%s", fz_caught_message(ctx));
- fz_report_error(ctx);
- }
- ui_end();
- }
- static void usage(const char *argv0)
- {
- fprintf(stderr, "mupdf-gl version %s\n", FZ_VERSION);
- fprintf(stderr, "usage: %s [options] document [page]\n", argv0);
- fprintf(stderr, "\t-p -\tpassword\n");
- fprintf(stderr, "\t-r -\tresolution\n");
- fprintf(stderr, "\t-c -\tdisplay ICC profile\n");
- fprintf(stderr, "\t-b -\tuse named page box (MediaBox, CropBox, BleedBox, TrimBox, or ArtBox)\n");
- fprintf(stderr, "\t-I\tinvert colors\n");
- fprintf(stderr, "\t-W -\tpage width for EPUB layout\n");
- fprintf(stderr, "\t-H -\tpage height for EPUB layout\n");
- fprintf(stderr, "\t-S -\tfont size for EPUB layout\n");
- fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n");
- fprintf(stderr, "\t-X\tdisable document styles for EPUB layout\n");
- fprintf(stderr, "\t-J\tdisable javascript in PDF forms\n");
- fprintf(stderr, "\t-A -\tset anti-aliasing level (0-8,9,10)\n");
- fprintf(stderr, "\t-B -\tset black tint color (default: 303030)\n");
- fprintf(stderr, "\t-C -\tset white tint color (default: FFFFF0)\n");
- fprintf(stderr, "\t-Y -\tset the UI scaling factor\n");
- fprintf(stderr, "\t-R -\tenable reflow and set the text extraction options\n");
- fprintf(stderr, "\t\t\texample: -R dehyphenate,preserve-images\n");
- exit(1);
- }
- static int document_filter(const char *fname)
- {
- return !!fz_recognize_document(ctx, fname);
- }
- static void do_open_document_dialog(void)
- {
- if (ui_open_file(filename, "Select a document to open:"))
- {
- ui.dialog = NULL;
- if (filename[0] == 0)
- glutLeaveMainLoop();
- else
- {
- load_document();
- if (doc)
- {
- if (reflow_options)
- reflow_document();
- load_page();
- shrinkwrap();
- update_title();
- }
- }
- }
- }
- static void cleanup(void)
- {
- save_history();
- fz_try(ctx)
- save_accelerator();
- fz_catch(ctx)
- fz_warn(ctx, "cannot save accelerator file");
- ui_finish();
- fz_drop_pixmap(ctx, page_contents);
- page_contents = NULL;
- #ifndef NDEBUG
- if (fz_atoi(getenv("FZ_DEBUG_STORE")))
- fz_debug_store(ctx, fz_stdout(ctx));
- #endif
- trace_action("quit(0);\n");
- fz_flush_warnings(ctx);
- #if FZ_ENABLE_JS
- console_fin();
- #endif
- fz_drop_output(ctx, trace_file);
- fz_drop_stext_page(ctx, page_text);
- fz_drop_separations(ctx, seps);
- fz_drop_link(ctx, links);
- fz_drop_page(ctx, fzpage);
- fz_drop_outline(ctx, outline);
- fz_drop_document(ctx, doc);
- fz_drop_context(ctx);
- }
- int reloadrequested = 0;
- #ifndef _WIN32
- static void signal_handler(int signal)
- {
- if (signal == SIGHUP)
- reloadrequested = 1;
- }
- #endif
- #ifdef _MSC_VER
- int main_utf8(int argc, char **argv)
- #else
- int main(int argc, char **argv)
- #endif
- {
- const char *trace_file_name = NULL;
- const char *profile_name = NULL;
- float scale = 0;
- int c;
- #ifndef _WIN32
- /* Never wait for termination of child processes. */
- struct sigaction arg = {
- .sa_handler=SIG_IGN,
- .sa_flags=SA_NOCLDWAIT
- };
- sigaction(SIGCHLD, &arg, NULL);
- signal(SIGHUP, signal_handler);
- #endif
- glutInit(&argc, argv);
- while ((c = fz_getopt(argc, argv, "p:r:IW:H:S:U:XJb:A:B:C:T:Y:R:c:")) != -1)
- {
- switch (c)
- {
- default: usage(argv[0]); break;
- case 'p': password = fz_optarg; break;
- case 'r': currentzoom = fz_atof(fz_optarg); break;
- case 'c': profile_name = fz_optarg; break;
- case 'I': currentinvert = !currentinvert; break;
- case 'b': currentbox = fz_box_type_from_string(fz_optarg); break;
- case 'W': layout_w = fz_atof(fz_optarg); break;
- case 'H': layout_h = fz_atof(fz_optarg); break;
- case 'S': layout_em = fz_atof(fz_optarg); break;
- case 'U': layout_css = fz_optarg; break;
- case 'X': layout_use_doc_css = 0; break;
- case 'J': enable_js = !enable_js; break;
- case 'A': currentaa = fz_atoi(fz_optarg); break;
- case 'C': currenttint = 1; tint_white = strtol(fz_optarg, NULL, 16); break;
- case 'B': currenttint = 1; tint_black = strtol(fz_optarg, NULL, 16); break;
- case 'R': reflow_options = fz_optarg; break;
- case 'T': trace_file_name = fz_optarg; break;
- case 'Y': scale = fz_atof(fz_optarg); break;
- }
- }
- screen_w = glutGet(GLUT_SCREEN_WIDTH) - SCREEN_FURNITURE_W;
- screen_h = glutGet(GLUT_SCREEN_HEIGHT) - SCREEN_FURNITURE_H;
- ui_init_dpi(scale);
- oldzoom = currentzoom = currentzoom * ui.scale;
- ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
- #ifdef _WIN32
- /* stderr goes nowhere. Get us a debug stream we have a chance
- * of seeing. */
- fz_set_stddbg(ctx, fz_stdods(ctx));
- #endif
- #if FZ_ENABLE_JS
- console_init();
- #endif
- fz_register_document_handlers(ctx);
- if (trace_file_name)
- {
- if (!strcmp(trace_file_name, "-"))
- trace_file = fz_stdout(ctx);
- else
- trace_file = fz_new_output_with_path(ctx, trace_file_name, 0);
- trace_action("var doc, page, annot, widget, widgetstr, hits, tmp;\n");
- trace_action("function RegressionError() {\n");
- trace_action(" var err = new Error(Array.prototype.join.call(arguments, ' '));\n");
- trace_action(" err.name = 'RegressionError';\n");
- trace_action(" return err;\n");
- trace_action("}\n");
- }
- if (profile_name)
- {
- fz_buffer *profile_data = fz_read_file(ctx, profile_name);
- profile = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, NULL, profile_data);
- fz_drop_buffer(ctx, profile_data);
- }
- else
- {
- profile = fz_device_rgb(ctx);
- }
- if (layout_css)
- fz_load_user_css(ctx, layout_css);
- fz_set_use_document_css(ctx, layout_use_doc_css);
- if (fz_optind < argc)
- {
- fz_strlcpy(filename, argv[fz_optind++], sizeof filename);
- if (fz_optind < argc)
- anchor = argv[fz_optind++];
- if (fz_optind < argc)
- usage(argv[0]);
- fz_try(ctx)
- {
- page_tex.w = 600;
- page_tex.h = 700;
- load_document();
- if (doc)
- {
- if (reflow_options)
- reflow_document();
- load_page();
- }
- }
- fz_always(ctx)
- {
- float sx = 1, sy = 1;
- if (screen_w > 0 && page_tex.w > screen_w)
- sx = (float)screen_w / page_tex.w;
- if (screen_h > 0 && page_tex.h > screen_h)
- sy = (float)screen_h / page_tex.h;
- if (sy < sx)
- sx = sy;
- if (sx < 1)
- {
- fz_irect area;
- currentzoom *= sx;
- oldzoom = currentzoom;
- /* compute bounds here for initial window size */
- page_bounds = fz_bound_page_box(ctx, fzpage, currentbox);
- transform_page();
- area = fz_irect_from_rect(draw_page_bounds);
- page_tex.w = area.x1 - area.x0;
- page_tex.h = area.y1 - area.y0;
- }
- ui_init(page_tex.w, page_tex.h, "MuPDF: Loading...");
- ui_input_init(&search_input, "");
- }
- fz_catch(ctx)
- {
- ui_show_error_dialog("%s", fz_caught_message(ctx));
- fz_report_error(ctx);
- }
- fz_try(ctx)
- {
- if (doc)
- update_title();
- }
- fz_catch(ctx)
- {
- ui_show_error_dialog("%s", fz_caught_message(ctx));
- fz_report_error(ctx);
- }
- }
- else
- {
- #ifdef _WIN32
- win_install();
- #endif
- ui_init(ui.gridsize * 26, ui.gridsize * 26, "MuPDF: Open document");
- ui_input_init(&search_input, "");
- ui_init_open_file(".", document_filter);
- ui.dialog = do_open_document_dialog;
- }
- annotate_w *= ui.lineheight;
- outline_w *= ui.lineheight;
- #if FZ_ENABLE_JS
- console_h *= ui.lineheight;
- #endif
- glutMainLoop();
- cleanup();
- return 0;
- }
- #ifdef _WIN32
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
- {
- int argc;
- LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
- char **argv = fz_argv_from_wargv(argc, wargv);
- int ret = main_utf8(argc, argv);
- fz_free_argv(argc, argv);
- return ret;
- }
- #endif
|