Viewer.java 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085
  1. // Copyright (C) 2004-2022 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. package example;
  23. import com.artifex.mupdf.fitz.*;
  24. import java.awt.*;
  25. import java.awt.event.*;
  26. import java.awt.image.*;
  27. import java.io.File;
  28. import java.io.FilenameFilter;
  29. import java.io.FileNotFoundException;
  30. import java.io.FileOutputStream;
  31. import java.io.PrintWriter;
  32. import java.io.StringWriter;
  33. import java.io.IOException;
  34. import java.io.RandomAccessFile;
  35. import java.lang.reflect.Field;
  36. import java.util.Vector;
  37. public class Viewer extends Frame implements WindowListener, ActionListener, ItemListener, KeyListener, MouseWheelListener, Context.Log, ViewerCore.Callback
  38. {
  39. protected Worker worker;
  40. protected ViewerCore doc;
  41. protected Location location;
  42. protected int chapterNumber;
  43. protected int pageNumber;
  44. protected ViewerCore.OutlineItem[] outline;
  45. protected int layoutWidth = 450;
  46. protected int layoutHeight = 600;
  47. protected int layoutEm = 12;
  48. protected String title;
  49. protected String author;
  50. protected String format;
  51. protected String encryption;
  52. protected boolean print;
  53. protected boolean copy;
  54. protected boolean edit;
  55. protected boolean annotate;
  56. protected boolean form;
  57. protected boolean accessibility;
  58. protected boolean assemble;
  59. protected boolean printHq;
  60. protected boolean reflowable;
  61. protected boolean linearized;
  62. protected int updates;
  63. protected int firstUpdate;
  64. protected int chapters;
  65. protected int pages;
  66. protected Pixmap pixmap;
  67. protected Rect bbox;
  68. protected Rect[] links;
  69. protected String[] linkURIs;
  70. protected Quad[][] hits;
  71. protected int pixmapWidth;
  72. protected int pixmapHeight;
  73. protected float pixelScale;
  74. protected int screenDPI;
  75. protected Dimension screenSize;
  76. protected int searchDirection = 1;
  77. protected String searchNeedle = null;
  78. protected final int MIN_ZOOM_DPI = 18;
  79. protected final int MAX_ZOOM_DPI = 288;
  80. protected int[] zoomList = {
  81. 18, 24, 36, 54, 72, 96, 120, 144, 180, 216, 288
  82. };
  83. protected boolean customZoom = false;
  84. protected int currentDPI = 72;
  85. protected int rotate = 0;
  86. protected boolean icc = true;
  87. protected int antialias = 8;
  88. protected boolean invert = false;
  89. protected boolean tint = false;
  90. protected int tintBlack = 0x303030;
  91. protected int tintWhite = 0xFFFFF0;
  92. protected boolean showLinks = false;
  93. protected boolean isFullscreen = false;
  94. protected ScrollPane pageScroll;
  95. protected Panel pageHolder;
  96. protected PageCanvas pageCanvas;
  97. protected Button firstButton, prevButton, nextButton, lastButton;
  98. protected TextField pageField;
  99. protected Label pageLabel;
  100. protected Button zoomInButton, zoomOutButton;
  101. protected Choice zoomChoice;
  102. protected Panel reflowPanel;
  103. protected Button fontIncButton, fontDecButton;
  104. protected Label fontSizeLabel;
  105. protected TextField searchField;
  106. protected Button searchPrevButton, searchNextButton;
  107. protected Panel searchStatusPanel;
  108. protected Label searchStatus;
  109. protected Panel outlinePanel;
  110. protected List outlineList;
  111. protected OCRProgressmeter OCRmeter;
  112. protected RenderProgressmeter renderMeter;
  113. protected int number = 0;
  114. protected class Mark {
  115. Location loc;
  116. protected Mark(Location loc) {
  117. this.loc = loc;
  118. }
  119. }
  120. protected int historyCount = 0;
  121. protected Mark[] history = new Mark[256];
  122. protected int futureCount = 0;
  123. protected Mark[] future = new Mark[256];
  124. protected Mark[] marks = new Mark[10];
  125. protected static void messageBox(Frame owner, String title, String message) {
  126. final Dialog box = new Dialog(owner, title, true);
  127. box.add(new Label(message), BorderLayout.CENTER);
  128. Panel buttonPane = new Panel(new FlowLayout());
  129. Button okayButton = new Button("Okay");
  130. okayButton.addActionListener(new ActionListener() {
  131. public void actionPerformed(ActionEvent event) {
  132. box.setVisible(false);
  133. }
  134. });
  135. buttonPane.add(okayButton);
  136. box.add(buttonPane, BorderLayout.SOUTH);
  137. box.pack();
  138. box.setVisible(true);
  139. box.dispose();
  140. }
  141. protected static String passwordDialog(Frame owner, String title) {
  142. final Dialog box = new Dialog(owner, title, true);
  143. final TextField textField = new TextField(20);
  144. textField.setEchoChar('*');
  145. Panel buttonPane = new Panel(new FlowLayout());
  146. Button cancelButton = new Button("Cancel");
  147. Button okayButton = new Button("Okay");
  148. cancelButton.addActionListener(new ActionListener() {
  149. public void actionPerformed(ActionEvent event) {
  150. textField.setText("");
  151. box.setVisible(false);
  152. }
  153. });
  154. okayButton.addActionListener(new ActionListener() {
  155. public void actionPerformed(ActionEvent event) {
  156. box.setVisible(false);
  157. }
  158. });
  159. box.add(new Label("Password:"), BorderLayout.NORTH);
  160. box.add(textField, BorderLayout.CENTER);
  161. buttonPane.add(cancelButton);
  162. buttonPane.add(okayButton);
  163. box.add(buttonPane, BorderLayout.SOUTH);
  164. box.pack();
  165. box.setVisible(true);
  166. box.dispose();
  167. String pwd = textField.getText();
  168. if (pwd.length() == 0)
  169. return null;
  170. return pwd;
  171. }
  172. public class LogDialog extends Dialog implements ActionListener, KeyListener{
  173. TextArea info = new TextArea("");
  174. Button okay = new Button("Okay");
  175. public LogDialog(Frame parent, String title, String message) {
  176. super(parent, title, true);
  177. setLayout(new GridLayout(2, 1));
  178. info.setText(message);
  179. info.setEnabled(false);
  180. add(info);
  181. Panel buttonPanel = new Panel(new FlowLayout(FlowLayout.CENTER));
  182. {
  183. okay.addActionListener(this);
  184. okay.addKeyListener(this);
  185. buttonPanel.add(okay);
  186. }
  187. add(buttonPanel);
  188. pack();
  189. setResizable(false);
  190. okay.requestFocusInWindow();
  191. setLocationRelativeTo(parent);
  192. setVisible(true);
  193. }
  194. public void actionPerformed(ActionEvent e) {
  195. if (e.getSource() == okay)
  196. dispose();
  197. }
  198. public void keyPressed(KeyEvent e) { }
  199. public void keyReleased(KeyEvent e) { }
  200. public void keyTyped(KeyEvent e) {
  201. if (e.getKeyChar() == '\u001b')
  202. dispose();
  203. }
  204. }
  205. public void exception(Throwable t) {
  206. StringWriter sw = new StringWriter();
  207. PrintWriter pw = new PrintWriter(sw);
  208. t.printStackTrace(pw);
  209. System.out.println("Exception: " + sw.toString());
  210. LogDialog ld = new LogDialog(this, "Exception!", "Exception: " + sw.toString());
  211. }
  212. public void error(String message) {
  213. LogDialog ld = new LogDialog(this, "Error!", "Error: " + message);
  214. }
  215. public void warning(String message) {
  216. LogDialog ld = new LogDialog(this, "Warning!", "Warning: " + message);
  217. }
  218. public Viewer(String documentPath) {
  219. pixelScale = getRetinaScale();
  220. screenDPI = getScreenDPI();
  221. screenSize = getScreenSize();
  222. currentDPI = zoomList[findNextLargerZoomLevel(screenDPI - 1)];
  223. setTitle("MuPDF: ");
  224. outlinePanel = new Panel(new BorderLayout());
  225. {
  226. outlineList = new List();
  227. outlineList.addItemListener(this);
  228. outlinePanel.add(outlineList, BorderLayout.CENTER);
  229. }
  230. this.add(outlinePanel, BorderLayout.WEST);
  231. outlinePanel.setMinimumSize(new Dimension(300, 300));
  232. outlinePanel.setPreferredSize(new Dimension(300, 300));
  233. outlinePanel.setVisible(false);
  234. Panel rightPanel = new Panel(new BorderLayout());
  235. {
  236. Panel toolpane = new Panel(new GridBagLayout());
  237. {
  238. GridBagConstraints c = new GridBagConstraints();
  239. c.fill = GridBagConstraints.HORIZONTAL;
  240. c.anchor = GridBagConstraints.WEST;
  241. Panel toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
  242. {
  243. firstButton = new Button("|<");
  244. firstButton.addActionListener(this);
  245. prevButton = new Button("<");
  246. prevButton.addActionListener(this);
  247. nextButton = new Button(">");
  248. nextButton.addActionListener(this);
  249. lastButton = new Button(">|");
  250. lastButton.addActionListener(this);
  251. pageField = new TextField(4);
  252. pageField.addActionListener(this);
  253. pageLabel = new Label("/ " + pages);
  254. toolbar.add(firstButton);
  255. toolbar.add(prevButton);
  256. toolbar.add(pageField);
  257. toolbar.add(pageLabel);
  258. toolbar.add(nextButton);
  259. toolbar.add(lastButton);
  260. }
  261. c.gridy = 0;
  262. toolpane.add(toolbar, c);
  263. toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
  264. {
  265. zoomOutButton = new Button("Zoom-");
  266. zoomOutButton.addActionListener(this);
  267. zoomInButton = new Button("Zoom+");
  268. zoomInButton.addActionListener(this);
  269. zoomChoice = new Choice();
  270. for (int i = 0; i < zoomList.length; ++i) {
  271. zoomChoice.add(String.valueOf(zoomList[i]));
  272. if (zoomList[i] == currentDPI)
  273. zoomChoice.select(i);
  274. }
  275. zoomChoice.addItemListener(this);
  276. toolbar.add(zoomOutButton);
  277. toolbar.add(zoomChoice);
  278. toolbar.add(zoomInButton);
  279. }
  280. c.gridy += 1;
  281. toolpane.add(toolbar, c);
  282. reflowPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
  283. {
  284. fontDecButton = new Button("Font-");
  285. fontDecButton.addActionListener(this);
  286. fontIncButton = new Button("Font+");
  287. fontIncButton.addActionListener(this);
  288. fontSizeLabel = new Label(String.valueOf(layoutEm));
  289. reflowPanel.add(fontDecButton);
  290. reflowPanel.add(fontSizeLabel);
  291. reflowPanel.add(fontIncButton);
  292. }
  293. c.gridy += 1;
  294. toolpane.add(reflowPanel, c);
  295. toolbar = new Panel(new FlowLayout(FlowLayout.LEFT));
  296. {
  297. searchField = new TextField(20);
  298. searchField.addActionListener(this);
  299. searchPrevButton = new Button("<");
  300. searchPrevButton.addActionListener(this);
  301. searchNextButton = new Button(">");
  302. searchNextButton.addActionListener(this);
  303. toolbar.add(searchField);
  304. toolbar.add(searchPrevButton);
  305. toolbar.add(searchNextButton);
  306. }
  307. searchField.addKeyListener(this);
  308. c.gridy += 1;
  309. toolpane.add(toolbar, c);
  310. searchStatusPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
  311. {
  312. searchStatus = new Label();
  313. searchStatusPanel.add(searchStatus);
  314. }
  315. c.gridy += 1;
  316. toolpane.add(searchStatusPanel, c);
  317. }
  318. rightPanel.add(toolpane, BorderLayout.NORTH);
  319. }
  320. this.add(rightPanel, BorderLayout.EAST);
  321. pageScroll = new ScrollPane(ScrollPane.SCROLLBARS_NEVER);
  322. {
  323. pageHolder = new Panel(new GridBagLayout());
  324. {
  325. pageHolder.setBackground(Color.gray);
  326. pageCanvas = new PageCanvas(pixelScale);
  327. pageHolder.add(pageCanvas);
  328. }
  329. pageCanvas.addKeyListener(this);
  330. pageCanvas.addMouseWheelListener(this);
  331. pageScroll.add(pageHolder);
  332. }
  333. this.add(pageScroll, BorderLayout.CENTER);
  334. addWindowListener(this);
  335. Toolkit toolkit = Toolkit.getDefaultToolkit();
  336. EventQueue eq = toolkit.getSystemEventQueue();
  337. worker = new Worker(eq);
  338. worker.start();
  339. pack();
  340. pageCanvas.requestFocusInWindow();
  341. doc = new ViewerCore(worker, this, documentPath);
  342. doc.openDocument(new ViewerCore.OnException() {
  343. public void run(Throwable t) {
  344. exception(t);
  345. }
  346. });
  347. }
  348. public void dispose() {
  349. doc.cancelSearch(null);
  350. doc.worker.stop();
  351. super.dispose();
  352. }
  353. public void keyPressed(KeyEvent e) {
  354. }
  355. public void keyReleased(KeyEvent e) {
  356. if (e.getSource() == pageCanvas) {
  357. int c = e.getKeyCode();
  358. switch(c)
  359. {
  360. case KeyEvent.VK_F1: showHelp(); break;
  361. case KeyEvent.VK_LEFT: pan(-10, 0); break;
  362. case KeyEvent.VK_RIGHT: pan(+10, 0); break;
  363. case KeyEvent.VK_UP: pan(0, -10); break;
  364. case KeyEvent.VK_DOWN: pan(0, +10); break;
  365. case KeyEvent.VK_PAGE_UP: doc.flipPages(-number, null); break;
  366. case KeyEvent.VK_PAGE_DOWN: doc.flipPages(+number, null); break;
  367. }
  368. }
  369. }
  370. public void keyTyped(KeyEvent e) {
  371. if (e.getSource() == pageCanvas)
  372. canvasKeyTyped(e);
  373. else if (e.getSource() == searchField)
  374. searchFieldKeyTyped(e);
  375. }
  376. protected void searchFieldKeyTyped(KeyEvent e) {
  377. if (e.getExtendedKeyCodeForChar(e.getKeyChar()) == java.awt.event.KeyEvent.VK_ESCAPE)
  378. clearSearch();
  379. }
  380. protected void canvasKeyTyped(KeyEvent e) {
  381. char c = e.getKeyChar();
  382. switch(c)
  383. {
  384. case 'r': doc.reloadDocument(null); break;
  385. case 'q': dispose(); break;
  386. case 'S': save(); break;
  387. case 'f': toggleFullscreen(); break;
  388. case 'm': mark(number); break;
  389. case 't': jumpHistoryBack(number); break;
  390. case 'T': jumpHistoryForward(number); break;
  391. case '>': relayout(number > 0 ? number : +1); break;
  392. case '<': relayout(number > 0 ? number : -1); break;
  393. case 'I': toggleInvert(); break;
  394. case 'E': toggleICC(); break;
  395. case 'A': toggleAntiAlias(); break;
  396. case 'C': toggleTint(); break;
  397. case 'o': toggleOutline(); break;
  398. case 'L': toggleLinks(); break;
  399. case 'i': showInfo(); break;
  400. case '[': rotate(-90); break;
  401. case ']': rotate(+90); break;
  402. case '+': zoomIn(); break;
  403. case '-': zoomOut(); break;
  404. case 'z': zoomToDPI(number); break;
  405. case 'w': shrinkWrap(); break;
  406. case 'W': fitWidth(); break;
  407. case 'H': fitHeight(); break;
  408. case 'Z': fitPage(); break;
  409. case 'k': pan(0, pageCanvas != null ? pageCanvas.getHeight() / -10 : -10); break;
  410. case 'j': pan(0, pageCanvas != null ? pageCanvas.getWidth() / +10 : +10); break;
  411. case 'h': pan(pageCanvas != null ? pageCanvas.getHeight() / -10 : -10, 0); break;
  412. case 'l': pan(pageCanvas != null ? pageCanvas.getWidth() / +10 : +10, 0); break;
  413. case 'b': smartMove(-1, number); break;
  414. case ' ': smartMove(+1, number); break;
  415. case ',': flipPages(-number); break;
  416. case '.': flipPages(+number); break;
  417. case 'g': gotoPage(number); break;
  418. case 'G': gotoLastPage(); break;
  419. case '/': editSearchNeedle(+1); break;
  420. case '?': editSearchNeedle(-1); break;
  421. case 'N': search(-1); break;
  422. case 'n': search(+1); break;
  423. case '\u001b': clearSearch(); break;
  424. }
  425. if (c >= '0' && c <= '9')
  426. number = number * 10 + c - '0';
  427. else
  428. number = 0;
  429. }
  430. public void mouseWheelMoved(MouseWheelEvent e) {
  431. int mod = e.getModifiersEx();
  432. int rot = e.getWheelRotation();
  433. if ((mod & MouseWheelEvent.CTRL_DOWN_MASK) != 0) {
  434. if (rot < 0)
  435. zoomIn();
  436. else
  437. zoomOut();
  438. } else if ((mod & MouseWheelEvent.SHIFT_DOWN_MASK) != 0) {
  439. if (rot < 0)
  440. pan(pageCanvas != null ? pageCanvas.getHeight() / -10 : -10, 0);
  441. else
  442. pan(pageCanvas != null ? pageCanvas.getHeight() / +10 : +10, 0);
  443. } else if (mod == 0) {
  444. if (rot < 0)
  445. pan(0, pageCanvas != null ? pageCanvas.getHeight() / -10 : -10);
  446. else
  447. pan(0, pageCanvas != null ? pageCanvas.getHeight() / +10 : +10);
  448. }
  449. }
  450. protected void editSearchNeedle(int direction) {
  451. clearSearch();
  452. searchDirection = direction;
  453. searchField.requestFocusInWindow();
  454. }
  455. protected void cancelSearch() {
  456. doc.cancelSearch(null);
  457. }
  458. protected void clearSearch() {
  459. cancelSearch();
  460. searchField.setText("");
  461. searchStatus.setText("");
  462. searchStatusPanel.validate();
  463. validate();
  464. hits = null;
  465. redraw();
  466. }
  467. public void search(int direction) {
  468. if (searchField.isEnabled()) {
  469. cancelSearch();
  470. searchField.setEnabled(false);
  471. searchNextButton.setEnabled(false);
  472. searchPrevButton.setEnabled(false);
  473. pageCanvas.requestFocusInWindow();
  474. doc.search(searchField.getText(), direction, new ViewerCore.OnException() {
  475. public void run(Throwable t) {
  476. exception(t);
  477. }
  478. });
  479. }
  480. }
  481. protected void render() {
  482. if (bbox == null)
  483. return;
  484. float width = bbox.x1 - bbox.x0;
  485. float height = bbox.y1 - bbox.y0;
  486. float scaleX = (float) Math.floor(width * (currentDPI/72.0f) * pixelScale + 0.5f) / width;
  487. float scaleY = (float) Math.floor(height * (currentDPI/72.0f) * pixelScale + 0.5f) / height;
  488. Matrix ctm = new Matrix().scale(scaleX, scaleY).rotate(rotate);
  489. Rect atOrigin = new Rect(bbox).transform(ctm);
  490. ctm.e -= atOrigin.x0;
  491. ctm.f -= atOrigin.y0;
  492. Rect bounds = new Rect(bbox).transform(ctm);
  493. Cookie cookie = new Cookie();
  494. renderMeter = new RenderProgressmeter(this, "Rendering...", cookie, 250);
  495. renderMeter.setLocationRelativeTo(this);
  496. pageCanvas.requestFocusInWindow();
  497. doc.renderPage(ctm, bounds, icc, antialias, invert, tint, tintBlack, tintWhite, cookie,
  498. new ViewerCore.OnException() {
  499. public void run(Throwable t) {
  500. if (!renderMeter.cancelled)
  501. exception(t);
  502. }
  503. }
  504. );
  505. }
  506. protected int findExactZoomLevel(int dpi) {
  507. for (int level = 0; level < zoomList.length - 1; level++)
  508. if (zoomList[level] == dpi)
  509. return level;
  510. return -1;
  511. }
  512. protected int findNextSmallerZoomLevel(int dpi) {
  513. for (int level = zoomList.length - 1; level >= 0; level--)
  514. if (zoomList[level] < dpi)
  515. return level;
  516. return 0;
  517. }
  518. protected int findNextLargerZoomLevel(int dpi) {
  519. for (int level = 0; level < zoomList.length - 1; level++)
  520. if (zoomList[level] > dpi)
  521. return level;
  522. return zoomList.length - 1;
  523. }
  524. protected void zoomToDPI(int newDPI) {
  525. if (newDPI == 0)
  526. newDPI = screenDPI;
  527. if (newDPI < MIN_ZOOM_DPI)
  528. newDPI = MIN_ZOOM_DPI;
  529. if (newDPI > MAX_ZOOM_DPI)
  530. newDPI = MAX_ZOOM_DPI;
  531. if (newDPI == currentDPI)
  532. return;
  533. int level = findExactZoomLevel(newDPI);
  534. if (level < 0) {
  535. if (customZoom)
  536. zoomChoice.remove(0);
  537. customZoom = true;
  538. zoomChoice.insert(String.valueOf(newDPI), 0);
  539. zoomChoice.select(0);
  540. } else {
  541. if (customZoom) {
  542. customZoom = false;
  543. zoomChoice.remove(0);
  544. }
  545. zoomChoice.select(level);
  546. }
  547. currentDPI = newDPI;
  548. render();
  549. }
  550. protected void zoomToLevel(int level) {
  551. if (level < 0)
  552. level = 0;
  553. if (level >= zoomList.length - 1)
  554. level = zoomList.length - 1;
  555. zoomToDPI(zoomList[level]);
  556. }
  557. protected void zoomIn() {
  558. zoomToLevel(findNextLargerZoomLevel(currentDPI));
  559. }
  560. protected void zoomOut() {
  561. zoomToLevel(findNextSmallerZoomLevel(currentDPI));
  562. }
  563. protected void zoomToScale(float newZoom) {
  564. zoomToDPI((int)(newZoom * 72));
  565. }
  566. protected void fit(float desired, float unscaled) {
  567. zoomToScale(desired / unscaled);
  568. }
  569. protected float unscaledWidth() {
  570. return bbox != null ? bbox.x1 - bbox.x0 : 0;
  571. }
  572. protected float unscaledHeight() {
  573. return bbox != null ? bbox.y1 - bbox.y0 : 0;
  574. }
  575. protected void fitWidth() {
  576. fit(pageScroll.getSize().width, unscaledWidth());
  577. }
  578. protected void fitHeight() {
  579. fit(pageScroll.getSize().height, unscaledHeight());
  580. }
  581. protected void fitPage() {
  582. Dimension size = pageScroll.getSize();
  583. float width = bbox != null ? bbox.x1 - bbox.x0 : 0;
  584. float height = bbox != null ? bbox.y1 - bbox.y0 : 0;
  585. float pageAspect = (width == 0 || height == 0) ? 0 : (width / height);
  586. float canvasAspect = (float) size.width / (float) size.height;
  587. if (pageAspect > canvasAspect)
  588. fitWidth();
  589. else
  590. fitHeight();
  591. }
  592. protected void rotate(int change) {
  593. int newRotate = rotate + change;
  594. while (newRotate < 0) newRotate += 360;
  595. while (newRotate >= 360) newRotate -= 360;
  596. if (newRotate - rotate == 0)
  597. return;
  598. rotate = newRotate;
  599. render();
  600. }
  601. protected void toggleAntiAlias() {
  602. int newAntialias = number != 0 ? number : (antialias == 8 ? 0 : 8);
  603. if (newAntialias - antialias == 0)
  604. return;
  605. antialias = newAntialias;
  606. render();
  607. }
  608. protected void toggleICC() {
  609. icc = !icc;
  610. render();
  611. }
  612. protected void toggleInvert() {
  613. invert = !invert;
  614. render();
  615. }
  616. protected void toggleTint() {
  617. tint = !tint;
  618. render();
  619. }
  620. protected void toggleOutline() {
  621. if (outlineList.getItemCount() <= 0)
  622. return;
  623. outlinePanel.setVisible(!outlinePanel.isVisible());
  624. pack();
  625. validate();
  626. }
  627. protected void toggleLinks() {
  628. showLinks = !showLinks;
  629. redraw();
  630. }
  631. protected boolean isShrinkWrapped(int oldPixmapWidth, int oldPixmapHeight) {
  632. Dimension size = pageScroll.getSize();
  633. if (oldPixmapWidth == 0 && oldPixmapHeight == 0)
  634. return true;
  635. if (oldPixmapWidth + 4 == size.width && oldPixmapHeight + 4 == size.height)
  636. return true;
  637. return false;
  638. }
  639. protected void shrinkWrap() {
  640. Dimension newSize = new Dimension(pixmapWidth, pixmapHeight);
  641. newSize.width += 4;
  642. newSize.height += 4;
  643. if (newSize.width > screenSize.width)
  644. newSize.width = screenSize.width;
  645. if (newSize.height > screenSize.height)
  646. newSize.height = screenSize.height;
  647. pageScroll.setPreferredSize(newSize);
  648. pack();
  649. }
  650. protected void redraw() {
  651. boolean wasShrinkWrapped = isShrinkWrapped(pixmapWidth, pixmapHeight);
  652. if (pixmap != null) {
  653. pixmapWidth = pixmap.getWidth();
  654. pixmapHeight = pixmap.getHeight();
  655. BufferedImage image = new BufferedImage(pixmapWidth, pixmapHeight, BufferedImage.TYPE_3BYTE_BGR);
  656. image.setRGB(0, 0, pixmapWidth, pixmapHeight, pixmap.getPixels(), 0, pixmapWidth);
  657. pageCanvas.setPage(image, showLinks ? links : null, hits);
  658. } else {
  659. pixmapWidth = 0;
  660. pixmapHeight = 0;
  661. pageCanvas.setPage(null, null, null);
  662. }
  663. if (wasShrinkWrapped)
  664. shrinkWrap();
  665. pageCanvas.invalidate();
  666. validate();
  667. }
  668. protected static String paperSizeName(int w, int h)
  669. {
  670. /* ISO A */
  671. if (w == 2384 && h == 3370) return "A0";
  672. if (w == 1684 && h == 2384) return "A1";
  673. if (w == 1191 && h == 1684) return "A2";
  674. if (w == 842 && h == 1191) return "A3";
  675. if (w == 595 && h == 842) return "A4";
  676. if (w == 420 && h == 595) return "A5";
  677. if (w == 297 && h == 420) return "A6";
  678. /* US */
  679. if (w == 612 && h == 792) return "Letter";
  680. if (w == 612 && h == 1008) return "Legal";
  681. if (w == 792 && h == 1224) return "Ledger";
  682. if (w == 1224 && h == 792) return "Tabloid";
  683. return null;
  684. }
  685. protected void showHelp() {
  686. final Dialog box = new Dialog(this, "Help", true);
  687. box.addWindowListener(new WindowListener() {
  688. public void windowActivated(WindowEvent event) { }
  689. public void windowDeactivated(WindowEvent event) { }
  690. public void windowIconified(WindowEvent event) { }
  691. public void windowDeiconified(WindowEvent event) { }
  692. public void windowOpened(WindowEvent event) { }
  693. public void windowClosed(WindowEvent event) { }
  694. public void windowClosing(WindowEvent event) {
  695. box.setVisible(false);
  696. pageCanvas.requestFocusInWindow();
  697. }
  698. });
  699. String help[] = {
  700. "The middle mouse button (scroll wheel button) pans the document view.",
  701. //"The right mouse button selects a region and copies the marked text to the clipboard.",
  702. //"",
  703. //"",
  704. "F1 - show this message",
  705. "i - show document information",
  706. "o - show document outline",
  707. //"a - show annotation editor",
  708. "L - highlight links",
  709. //"F - highlight form fields",
  710. "r - reload file",
  711. "S - save file",
  712. "q - quit",
  713. "",
  714. "< - decrease E-book font size",
  715. "> - increase E-book font size",
  716. "A - toggle anti-aliasing",
  717. "I - toggle inverted color mode",
  718. "C - toggle tinted color mode",
  719. "E - toggle ICC color management",
  720. //"e - toggle spot color emulation",
  721. "",
  722. "f - fullscreen window",
  723. "w - shrink wrap window",
  724. "W - fit to width",
  725. "H - fit to height",
  726. "Z - fit to page",
  727. "z - reset zoom",
  728. "[number] z - set zoom resolution in DPI",
  729. "plus - zoom in",
  730. "minus - zoom out",
  731. "[ - rotate counter-clockwise",
  732. "] - rotate clockwise",
  733. "arrow keys - scroll in small increments",
  734. "h, j, k, l - scroll in small increments",
  735. "",
  736. "b - smart move backward",
  737. "space - smart move forward",
  738. "comma or page up - go backward",
  739. "period or page down - go forward",
  740. "g - go to first page",
  741. "G - go to last page",
  742. "[number] g - go to page number",
  743. "",
  744. "m - save current location in history",
  745. "t - go backward in history",
  746. "T - go forward in history",
  747. "[number] m - save current location in numbered bookmark",
  748. "[number] t - go to numbered bookmark",
  749. "",
  750. "/ - search for text forward",
  751. "? - search for text backward",
  752. "n - repeat search",
  753. "N - repeat search in reverse direction",
  754. };
  755. Panel helpPanel = new Panel(new GridLayout(help.length, 1));
  756. for (int i = 0; i < help.length; i++)
  757. helpPanel.add(new Label(help[i]));
  758. Button button = new Button("OK");
  759. button.addActionListener(new ActionListener() {
  760. public void actionPerformed(ActionEvent event) {
  761. box.setVisible(false);
  762. }
  763. });
  764. button.addKeyListener(new KeyListener() {
  765. public void keyPressed(KeyEvent e) { }
  766. public void keyReleased(KeyEvent e) {
  767. if (e.getKeyCode() == KeyEvent.VK_F1)
  768. box.setVisible(false);
  769. }
  770. public void keyTyped(KeyEvent e) {
  771. if (e.getKeyChar() == '\u001b' || e.getKeyChar() == '\r' || e.getKeyChar() == '\n')
  772. box.setVisible(false);
  773. }
  774. });
  775. Panel buttonPane = new Panel(new FlowLayout());
  776. buttonPane.add(button);
  777. box.add(helpPanel, BorderLayout.CENTER);
  778. box.add(buttonPane, BorderLayout.SOUTH);
  779. box.setResizable(false);
  780. box.pack();
  781. java.awt.Point winLoc = this.getLocation();
  782. Dimension winDim = this.getSize();
  783. int winCenterX = winLoc.x + winDim.width / 2;
  784. int winCenterY = winLoc.y + winDim.height / 2;
  785. Dimension diagDim = box.getSize();
  786. int x = winCenterX - diagDim.width / 2;
  787. int y = winCenterY - diagDim.height / 2;
  788. box.setLocation(x, y);
  789. button.requestFocusInWindow();
  790. box.setVisible(true);
  791. box.dispose();
  792. }
  793. protected void showInfo() {
  794. StringBuffer buffer;
  795. cancelSearch();
  796. final Dialog box = new Dialog(this, "Document info", true);
  797. box.addWindowListener(new WindowListener() {
  798. public void windowActivated(WindowEvent event) { }
  799. public void windowDeactivated(WindowEvent event) { }
  800. public void windowIconified(WindowEvent event) { }
  801. public void windowDeiconified(WindowEvent event) { }
  802. public void windowOpened(WindowEvent event) { }
  803. public void windowClosed(WindowEvent event) { }
  804. public void windowClosing(WindowEvent event) {
  805. box.setVisible(false);
  806. pageCanvas.requestFocusInWindow();
  807. }
  808. });
  809. Panel infoPanel = new Panel();
  810. int rows = 0;
  811. if (title != null) rows++;
  812. if (author != null) rows++;
  813. if (format != null) rows++;
  814. if (encryption != null) rows++;
  815. buffer = new StringBuffer();
  816. if (print)
  817. buffer.append("print, ");
  818. if (copy)
  819. buffer.append("copy, ");
  820. if (edit)
  821. buffer.append("edit, ");
  822. if (annotate)
  823. buffer.append("annotate, ");
  824. if (form)
  825. buffer.append("form, ");
  826. if (accessibility)
  827. buffer.append("accessibility, ");
  828. if (assemble)
  829. buffer.append("assemble, ");
  830. if (printHq)
  831. buffer.append("print-hq, ");
  832. if (buffer.length() > 2)
  833. buffer.delete(buffer.length() - 2, buffer.length());
  834. String permissions = buffer.length() > 0 ? buffer.toString() : null;
  835. if (permissions != null) rows++;
  836. buffer = new StringBuffer();
  837. if (doc.equals("PDF")) {
  838. buffer.append("PDF ");
  839. if (linearized)
  840. buffer.append("linearized ");
  841. buffer.append("document with ");
  842. buffer.append(updates);
  843. if (updates == 1)
  844. buffer.append(" update");
  845. else
  846. buffer.append(" updates");
  847. }
  848. String versions = buffer.length() > 0 ? buffer.toString() : null;
  849. if (versions != null) rows++;
  850. buffer = new StringBuffer();
  851. if (doc.equals("PDF")) {
  852. if (updates > 1) {
  853. if (firstUpdate == 0)
  854. buffer.append("Change firstUpdate seems valid.");
  855. else if (firstUpdate == 1)
  856. buffer.append("Invalid changes made to the document in the last update.");
  857. else if (firstUpdate == 2)
  858. buffer.append("Invalid changes made to the document in the penultimate update.");
  859. else {
  860. buffer.append("Invalid changes made to the document ");
  861. buffer.append(firstUpdate);
  862. buffer.append(" updates ago.");
  863. }
  864. }
  865. }
  866. String validation = buffer.length() > 0 ? buffer.toString() : null;
  867. if (validation != null) rows++;
  868. buffer = new StringBuffer();
  869. int w = 0;
  870. int h = 0;
  871. if (bbox != null) {
  872. w = (int)(bbox.x1 - bbox.x0 + 0.5f);
  873. h = (int)(bbox.y1 - bbox.y0 + 0.5f);
  874. }
  875. buffer.append(w);
  876. buffer.append(" x ");
  877. buffer.append(h);
  878. String name = paperSizeName(w, h);
  879. if (name == null)
  880. name = paperSizeName(h, w);
  881. if (name != null)
  882. buffer.append("(" + name + ")");
  883. String paperSize = buffer.length() > 0 ? buffer.toString() : null;
  884. if (paperSize != null) rows++;
  885. buffer = new StringBuffer();
  886. buffer.append(pageNumber + 1);
  887. buffer.append(" / ");
  888. buffer.append(pages);
  889. String page = buffer.length() > 0 ? buffer.toString() : null;
  890. if (page != null) rows++;
  891. String iccstring = icc ? "on" : "off";
  892. rows++;
  893. String antialiasstring = Integer.toString(antialias);
  894. rows++;
  895. infoPanel.setLayout(new GridLayout(rows, 1));
  896. if (title != null) infoPanel.add(new Label("Title: " + title));
  897. if (author != null) infoPanel.add(new Label("Author: " + author));
  898. if (format != null) infoPanel.add(new Label("Format: " + format));
  899. if (encryption != null) infoPanel.add(new Label("Encryption: " + encryption));
  900. if (permissions != null) infoPanel.add(new Label("Permissions: " + permissions));
  901. if (versions != null) infoPanel.add(new Label(versions));
  902. if (validation != null) infoPanel.add(new Label(validation));
  903. infoPanel.add(new Label("Size: " + paperSize));
  904. infoPanel.add(new Label("Page: " + page));
  905. infoPanel.add(new Label("ICC rendering: " + iccstring));
  906. infoPanel.add(new Label("Antialias rendering: " + antialiasstring));
  907. Button button = new Button("OK");
  908. button.addActionListener(new ActionListener() {
  909. public void actionPerformed(ActionEvent event) {
  910. box.setVisible(false);
  911. }
  912. });
  913. button.addKeyListener(new KeyListener() {
  914. public void keyPressed(KeyEvent e) { }
  915. public void keyReleased(KeyEvent e) { }
  916. public void keyTyped(KeyEvent e) {
  917. if (e.getKeyChar() == '\u001b' || e.getKeyChar() == '\r' || e.getKeyChar() == '\n')
  918. box.setVisible(false);
  919. }
  920. });
  921. Panel buttonPane = new Panel(new FlowLayout());
  922. buttonPane.add(button);
  923. box.add(infoPanel, BorderLayout.CENTER);
  924. box.add(buttonPane, BorderLayout.SOUTH);
  925. button.requestFocusInWindow();
  926. box.setResizable(false);
  927. box.pack();
  928. java.awt.Point winLoc = this.getLocation();
  929. Dimension winDim = this.getSize();
  930. int winCenterX = winLoc.x + winDim.width / 2;
  931. int winCenterY = winLoc.y + winDim.height / 2;
  932. Dimension diagDim = box.getSize();
  933. int x = winCenterX - diagDim.width / 2;
  934. int y = winCenterY - diagDim.height / 2;
  935. box.setLocation(x, y);
  936. box.setVisible(true);
  937. box.dispose();
  938. }
  939. protected void toggleFullscreen() {
  940. isFullscreen = !isFullscreen;
  941. if (isFullscreen)
  942. setExtendedState(Frame.MAXIMIZED_BOTH);
  943. else
  944. setExtendedState(Frame.NORMAL);
  945. }
  946. protected void mark(int number) {
  947. cancelSearch();
  948. if (number == 0)
  949. pushHistory();
  950. else if (number > 0 && number < marks.length)
  951. marks[number] = saveMark();
  952. }
  953. protected void jumpHistoryBack(int number) {
  954. cancelSearch();
  955. if (number == 0) {
  956. if (historyCount > 0)
  957. popHistory();
  958. } else if (number > 0 && number < marks.length)
  959. restoreMark(marks[number]);
  960. }
  961. protected void jumpHistoryForward(int number) {
  962. cancelSearch();
  963. if (number == 0) {
  964. if (futureCount > 0) {
  965. popFuture();
  966. }
  967. }
  968. }
  969. protected Mark saveMark() {
  970. return new Mark(location);
  971. }
  972. protected void restoreMark(Mark mark) {
  973. if (mark != null) {
  974. doc.gotoLocation(mark.loc, null);
  975. pageCanvas.requestFocusInWindow();
  976. }
  977. }
  978. protected void pushHistory() {
  979. if (historyCount > 0 && location.equals(history[historyCount - 1].loc))
  980. {
  981. return;
  982. }
  983. if (historyCount + 1 >= history.length) {
  984. for (int i = 0; i < history.length - 1; i++)
  985. history[i] = history[i + 1];
  986. history[historyCount] = saveMark();
  987. } else {
  988. history[historyCount++] = saveMark();
  989. }
  990. }
  991. protected void pushFuture() {
  992. if (futureCount + 1 >= future.length) {
  993. for (int i = 0; i < future.length - 1; i++)
  994. future[i] = future[i + 1];
  995. future[futureCount] = saveMark();
  996. } else {
  997. future[futureCount++] = saveMark();
  998. }
  999. }
  1000. protected void clearFuture() {
  1001. futureCount = 0;
  1002. }
  1003. protected void popHistory() {
  1004. Location here = location;
  1005. pushFuture();
  1006. while (historyCount > 0 && location.equals(here))
  1007. restoreMark(history[--historyCount]);
  1008. }
  1009. protected void popFuture() {
  1010. Location here = location;
  1011. pushHistory();
  1012. while (futureCount > 0 && location.equals(here))
  1013. restoreMark(future[--futureCount]);
  1014. }
  1015. protected void pan(int panx, int pany) {
  1016. Adjustable hadj = pageScroll.getHAdjustable();
  1017. Adjustable vadj = pageScroll.getVAdjustable();
  1018. int h = hadj.getValue();
  1019. int v = vadj.getValue();
  1020. int newh = h + panx;
  1021. int newv = v + pany;
  1022. if (newh < hadj.getMinimum())
  1023. newh = hadj.getMinimum();
  1024. if (newh > hadj.getMaximum() - hadj.getVisibleAmount())
  1025. newh = hadj.getMaximum() - hadj.getVisibleAmount();
  1026. if (newv < vadj.getMinimum())
  1027. newv = vadj.getMinimum();
  1028. if (newv > vadj.getMaximum() - vadj.getVisibleAmount())
  1029. newv = vadj.getMaximum() - vadj.getVisibleAmount();
  1030. if (newh == h && newv == v)
  1031. return;
  1032. if (newh != h)
  1033. hadj.setValue(newh);
  1034. if (newv != v)
  1035. vadj.setValue(newv);
  1036. }
  1037. protected void smartMove(int direction, int moves) {
  1038. cancelSearch();
  1039. if (moves < 1)
  1040. moves = 1;
  1041. while (moves-- > 0)
  1042. {
  1043. Adjustable hadj = pageScroll.getHAdjustable();
  1044. Adjustable vadj = pageScroll.getVAdjustable();
  1045. int slop_x = hadj.getMaximum() / 20;
  1046. int slop_y = vadj.getMaximum() / 20;
  1047. if (direction > 0) {
  1048. int remaining_x = hadj.getMaximum() - hadj.getValue() - hadj.getVisibleAmount();
  1049. int remaining_y = vadj.getMaximum() - vadj.getValue() - vadj.getVisibleAmount();
  1050. if (remaining_y > slop_y) {
  1051. int value = vadj.getValue() + vadj.getVisibleAmount() * 9 / 10;
  1052. if (value > vadj.getMaximum())
  1053. value = vadj.getMaximum();
  1054. vadj.setValue(value);
  1055. } else if (remaining_x > slop_x) {
  1056. vadj.setValue(vadj.getMinimum());
  1057. int value = hadj.getValue() + hadj.getVisibleAmount() * 9 / 10;
  1058. if (value > hadj.getMaximum())
  1059. value = hadj.getMaximum();
  1060. hadj.setValue(value);
  1061. } else {
  1062. doc.flipPages(+1, null);
  1063. vadj.setValue(vadj.getMinimum());
  1064. hadj.setValue(hadj.getMinimum());
  1065. }
  1066. } else {
  1067. int remaining_x = Math.abs(hadj.getMinimum() - hadj.getValue());
  1068. int remaining_y = Math.abs(vadj.getMinimum() - vadj.getValue());
  1069. if (remaining_y > slop_y) {
  1070. int value = vadj.getValue() - vadj.getVisibleAmount() * 9 / 10;
  1071. if (value < vadj.getMinimum())
  1072. value = vadj.getMinimum();
  1073. vadj.setValue(value);
  1074. } else if (remaining_x > slop_x) {
  1075. vadj.setValue(vadj.getMaximum());
  1076. int value = hadj.getValue() - hadj.getVisibleAmount() * 9 / 10;
  1077. if (value < hadj.getMinimum())
  1078. value = hadj.getMinimum();
  1079. hadj.setValue(value);
  1080. } else {
  1081. doc.flipPages(-1, null);
  1082. vadj.setValue(vadj.getMaximum());
  1083. hadj.setValue(hadj.getMaximum());
  1084. }
  1085. }
  1086. }
  1087. }
  1088. protected void flipPages(int number) {
  1089. cancelSearch();
  1090. doc.flipPages(number, null);
  1091. }
  1092. protected void gotoPage(int number) {
  1093. cancelSearch();
  1094. doc.gotoPage(number - 1, null);
  1095. }
  1096. protected void gotoLastPage() {
  1097. cancelSearch();
  1098. doc.gotoLastPage(null);
  1099. }
  1100. protected void relayout(int change) {
  1101. int newEm = layoutEm + change;
  1102. if (newEm < 6)
  1103. newEm = 6;
  1104. if (newEm > 36)
  1105. newEm = 36;
  1106. if (newEm == layoutEm)
  1107. return;
  1108. layoutEm = newEm;
  1109. fontSizeLabel.setText(String.valueOf(layoutEm));
  1110. doc.relayoutDocument(layoutWidth, layoutHeight, layoutEm, null);
  1111. }
  1112. public void actionPerformed(ActionEvent event) {
  1113. Object source = event.getSource();
  1114. if (source == firstButton)
  1115. doc.gotoFirstPage(null);
  1116. if (source == lastButton)
  1117. doc.gotoLastPage(null);
  1118. if (source == prevButton)
  1119. doc.flipPages(-1, null);
  1120. if (source == nextButton)
  1121. doc.flipPages(+1, null);
  1122. if (source == pageField) {
  1123. doc.gotoPage(Integer.parseInt(pageField.getText()) - 1, null);
  1124. pageCanvas.requestFocusInWindow();
  1125. }
  1126. if (source == searchField)
  1127. search(searchDirection);
  1128. if (source == searchNextButton)
  1129. search(+1);
  1130. if (source == searchPrevButton)
  1131. search(-1);
  1132. if (source == fontIncButton && doc != null && reflowable)
  1133. relayout(+1);
  1134. if (source == fontDecButton && doc != null && reflowable)
  1135. relayout(-1);
  1136. if (source == zoomOutButton) {
  1137. zoomOut();
  1138. pageCanvas.requestFocusInWindow();
  1139. }
  1140. if (source == zoomInButton) {
  1141. zoomIn();
  1142. pageCanvas.requestFocusInWindow();
  1143. }
  1144. }
  1145. public void itemStateChanged(ItemEvent event) {
  1146. Object source = event.getSource();
  1147. if (source == zoomChoice) {
  1148. zoomToLevel(zoomChoice.getSelectedIndex());
  1149. pageCanvas.requestFocusInWindow();
  1150. }
  1151. if (source == outlineList) {
  1152. int i = outlineList.getSelectedIndex();
  1153. doc.gotoLocation(outline[i].location, null);
  1154. pageCanvas.requestFocusInWindow();
  1155. }
  1156. }
  1157. public void windowClosing(WindowEvent event) { dispose(); }
  1158. public void windowActivated(WindowEvent event) { }
  1159. public void windowDeactivated(WindowEvent event) { }
  1160. public void windowIconified(WindowEvent event) { }
  1161. public void windowDeiconified(WindowEvent event) { }
  1162. public void windowOpened(WindowEvent event) { }
  1163. public void windowClosed(WindowEvent event) { }
  1164. public void save() {
  1165. cancelSearch();
  1166. SaveOptionsDialog dialog = new SaveOptionsDialog(this);
  1167. dialog.populate();
  1168. dialog.setLocationRelativeTo(this);
  1169. dialog.setVisible(true);
  1170. dialog.dispose();
  1171. final String options = dialog.getOptions();
  1172. if (options == null)
  1173. {
  1174. pageCanvas.requestFocusInWindow();
  1175. return;
  1176. }
  1177. FileDialog fileDialog = new FileDialog(this, "MuPDF Save File", FileDialog.SAVE);
  1178. fileDialog.setDirectory(System.getProperty("user.dir"));
  1179. fileDialog.setFilenameFilter(new FilenameFilter() {
  1180. public boolean accept(File dir, String name) {
  1181. return Document.recognize(name);
  1182. }
  1183. });
  1184. fileDialog.setFile(doc.documentPath);
  1185. fileDialog.setVisible(true);
  1186. fileDialog.dispose();
  1187. if (fileDialog.getFile() == null)
  1188. {
  1189. pageCanvas.requestFocusInWindow();
  1190. return;
  1191. }
  1192. final String selectedPath = new StringBuffer(fileDialog.getDirectory()).append(File.separatorChar).append(fileDialog.getFile()).toString();
  1193. OCRmeter = new OCRProgressmeter(this, "Saving...", pages);
  1194. OCRmeter.setLocationRelativeTo(this);
  1195. OCRmeter.setVisible(true);
  1196. pageCanvas.requestFocusInWindow();
  1197. if (options.indexOf("ocr-language=") < 0)
  1198. doc.save(selectedPath, options, OCRmeter, new ViewerCore.OnException() {
  1199. public void run(Throwable t) {
  1200. if (t instanceof IOException)
  1201. exception(t);
  1202. else if (t instanceof RuntimeException && !OCRmeter.cancelled)
  1203. exception(t);
  1204. }
  1205. });
  1206. else
  1207. {
  1208. try {
  1209. FileStream fs = new FileStream(selectedPath, "rw");
  1210. doc.save(fs, options, OCRmeter, new ViewerCore.OnException() {
  1211. public void run(Throwable t) {
  1212. if (t instanceof RuntimeException && !OCRmeter.cancelled)
  1213. exception(t);
  1214. }
  1215. });
  1216. } catch (IOException e) {
  1217. exception(e);
  1218. }
  1219. }
  1220. }
  1221. public void onSaveComplete() {
  1222. if (OCRmeter != null)
  1223. OCRmeter.done();
  1224. }
  1225. class SaveOptionsDialog extends Dialog implements ActionListener, ItemListener, KeyListener {
  1226. Checkbox snapShot = new Checkbox("Snapshot", false);
  1227. Checkbox highSecurity = new Checkbox("High security", false);
  1228. Choice resolution = new Choice();
  1229. TextField language = new TextField("eng");
  1230. Checkbox incremental = new Checkbox("Incremental", false);
  1231. Checkbox prettyPrint = new Checkbox("Pretty print", false);
  1232. Checkbox ascii = new Checkbox("Ascii", false);
  1233. Checkbox decompress = new Checkbox("Decompress", false);
  1234. Checkbox compress = new Checkbox("Compress", true);
  1235. Checkbox compressImages = new Checkbox("Compress images", true);
  1236. Checkbox compressFonts = new Checkbox("Compress fonts", true);
  1237. Checkbox linearize = new Checkbox("Linearize", false);
  1238. Checkbox garbageCollect = new Checkbox("Garbage collect", false);
  1239. Checkbox cleanSyntax = new Checkbox("Clean syntax", false);
  1240. Checkbox sanitizeSyntax = new Checkbox("Sanitize syntax", false);
  1241. Choice encryption = new Choice();
  1242. TextField userPassword = new TextField();
  1243. TextField ownerPassword = new TextField();
  1244. Button cancel = new Button("Cancel");
  1245. Button save = new Button("Save");
  1246. String options = null;
  1247. public SaveOptionsDialog(Frame parent) {
  1248. super(parent, "MuPDF Save Options", true);
  1249. resolution.add("200dpi");
  1250. resolution.add("300dpi");
  1251. resolution.add("600dpi");
  1252. resolution.add("1200dpi");
  1253. encryption.add("Keep");
  1254. encryption.add("None");
  1255. encryption.add("RC4, 40bit");
  1256. encryption.add("RC4, 128bit");
  1257. encryption.add("AES, 128bit");
  1258. encryption.add("AES, 256bit");
  1259. snapShot.addItemListener(this);
  1260. highSecurity.addItemListener(this);
  1261. resolution.addItemListener(this);
  1262. language.addActionListener(this);
  1263. incremental.addItemListener(this);
  1264. prettyPrint.addItemListener(this);
  1265. ascii.addItemListener(this);
  1266. decompress.addItemListener(this);
  1267. compress.addItemListener(this);
  1268. compressImages.addItemListener(this);
  1269. compressFonts.addItemListener(this);
  1270. linearize.addItemListener(this);
  1271. garbageCollect.addItemListener(this);
  1272. cleanSyntax.addItemListener(this);
  1273. sanitizeSyntax.addItemListener(this);
  1274. encryption.addItemListener(this);
  1275. userPassword.addActionListener(this);
  1276. ownerPassword.addActionListener(this);
  1277. cancel.addActionListener(this);
  1278. save.addActionListener(this);
  1279. save.addKeyListener(this);
  1280. calculateOptions();
  1281. }
  1282. void populate(Container container, GridBagConstraints c, Component component) {
  1283. GridBagLayout gbl = (GridBagLayout) container.getLayout();
  1284. gbl.setConstraints(component, c);
  1285. container.add(component);
  1286. }
  1287. void populate() {
  1288. GridBagConstraints c = new GridBagConstraints();
  1289. c.fill = GridBagConstraints.BOTH;
  1290. c.weightx = 1.0;
  1291. c.gridwidth = GridBagConstraints.REMAINDER;
  1292. GridBagLayout gbl = new GridBagLayout();
  1293. setLayout(gbl);
  1294. Panel left = new Panel();
  1295. Panel right = new Panel();
  1296. GridBagLayout lgbl = new GridBagLayout();
  1297. GridBagLayout rgbl = new GridBagLayout();
  1298. left.setLayout(lgbl);
  1299. right.setLayout(rgbl);
  1300. populate(left, c, snapShot);
  1301. populate(left, c, highSecurity);
  1302. populate(left, c, resolution);
  1303. populate(left, c, language);
  1304. populate(left, c, incremental);
  1305. c.weighty = 1.5;
  1306. populate(left, c, new Panel());
  1307. c.weighty = 0.0;
  1308. populate(left, c, prettyPrint);
  1309. populate(left, c, ascii);
  1310. populate(left, c, decompress);
  1311. populate(left, c, compress);
  1312. populate(left, c, compressImages);
  1313. populate(left, c, compressFonts);
  1314. populate(right, c, linearize);
  1315. populate(right, c, garbageCollect);
  1316. populate(right, c, cleanSyntax);
  1317. populate(right, c, sanitizeSyntax);
  1318. c.weighty = 1.5;
  1319. populate(right, c, new Panel());
  1320. c.weighty = 0.0;
  1321. populate(right, c, new Label("Encryption"));
  1322. populate(right, c, encryption);
  1323. populate(right, c, new Label("User password"));
  1324. populate(right, c, userPassword);
  1325. populate(right, c, new Label("Owner password"));
  1326. populate(right, c, ownerPassword);
  1327. c.gridwidth = GridBagConstraints.REMAINDER;
  1328. populate(this, c, new Panel());
  1329. c.gridwidth = 1;
  1330. populate(this, c, left);
  1331. c.gridwidth = GridBagConstraints.REMAINDER;
  1332. populate(this, c, right);
  1333. c.gridwidth = GridBagConstraints.REMAINDER;
  1334. populate(this, c, new Panel());
  1335. c.gridwidth = 1;
  1336. populate(this, c, cancel);
  1337. c.gridwidth = GridBagConstraints.REMAINDER;
  1338. populate(this, c, save);
  1339. pack();
  1340. setResizable(false);
  1341. save.requestFocusInWindow();
  1342. }
  1343. public void keyPressed(KeyEvent e) { }
  1344. public void keyReleased(KeyEvent e) { }
  1345. public void keyTyped(KeyEvent e) {
  1346. if (e.getKeyChar() == '\u001b')
  1347. cancel();
  1348. else if (e.getKeyChar() == '\n')
  1349. save();
  1350. }
  1351. public void actionPerformed(ActionEvent e) {
  1352. if (e.getSource() == cancel)
  1353. cancel();
  1354. else if (e.getSource() == save)
  1355. save();
  1356. }
  1357. void cancel() {
  1358. options = null;
  1359. setVisible(false);
  1360. }
  1361. void save() {
  1362. setVisible(false);
  1363. }
  1364. public void itemStateChanged(ItemEvent e) {
  1365. calculateOptions();
  1366. }
  1367. void calculateOptions() {
  1368. boolean isPDF = false;
  1369. boolean canBeSavedIncrementally = false;
  1370. boolean isRedacted = false;
  1371. if (isPDF && !canBeSavedIncrementally)
  1372. incremental.setState(false);
  1373. if (highSecurity.getState()) {
  1374. incremental.setState(false);
  1375. prettyPrint.setState(false);
  1376. ascii.setState(false);
  1377. decompress.setState(false);
  1378. compress.setState(true);
  1379. compressImages.setState(false);
  1380. compressFonts.setState(false);
  1381. linearize.setState(false);
  1382. garbageCollect.setState(false);
  1383. cleanSyntax.setState(false);
  1384. sanitizeSyntax.setState(false);
  1385. encryption.select("None");
  1386. userPassword.setText("");
  1387. ownerPassword.setText("");
  1388. } else if (incremental.getState()) {
  1389. linearize.setState(false);
  1390. garbageCollect.setState(false);
  1391. cleanSyntax.setState(false);
  1392. sanitizeSyntax.setState(false);
  1393. encryption.select("Keep");
  1394. userPassword.setText("");
  1395. ownerPassword.setText("");
  1396. }
  1397. highSecurity.setEnabled(snapShot.getState() == false);
  1398. resolution.setEnabled(snapShot.getState() == false && highSecurity.getState() == true);
  1399. language.setEnabled(snapShot.getState() == false && highSecurity.getState() == true);
  1400. incremental.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && isPDF && canBeSavedIncrementally);
  1401. prettyPrint.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1402. ascii.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1403. decompress.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1404. compress.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1405. compressImages.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1406. compressFonts.setEnabled(snapShot.getState() == false && highSecurity.getState() == false);
  1407. linearize.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false);
  1408. garbageCollect.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false);
  1409. cleanSyntax.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false);
  1410. sanitizeSyntax.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false);
  1411. encryption.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false);
  1412. userPassword.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false && encryption.getSelectedItem() != "Keep" && encryption.getSelectedItem() != "None");
  1413. ownerPassword.setEnabled(snapShot.getState() == false && highSecurity.getState() == false && incremental.getState() == false && encryption.getSelectedItem() != "Keep" && encryption.getSelectedItem() != "None");
  1414. if (incremental.getState()) {
  1415. garbageCollect.setState(false);
  1416. linearize.setState(false);
  1417. cleanSyntax.setState(false);
  1418. sanitizeSyntax.setState(false);
  1419. encryption.select("Keep");
  1420. }
  1421. StringBuilder opts = new StringBuilder();
  1422. if (highSecurity.getState()) {
  1423. opts.append(",compression=flate");
  1424. opts.append(",resolution=");
  1425. opts.append(resolution.getSelectedItem());
  1426. opts.append(",ocr-language=");
  1427. opts.append(language.getText());
  1428. } else {
  1429. if (decompress.getState()) opts.append(",decompress=yes");
  1430. if (compress.getState()) opts.append(",compress=yes");
  1431. if (compressFonts.getState()) opts.append(",compress-fonts=yes");
  1432. if (compressImages.getState()) opts.append(",compress-images=yes");
  1433. if (ascii.getState()) opts.append(",ascii=yes");
  1434. if (prettyPrint.getState()) opts.append(",pretty=yes");
  1435. if (linearize.getState()) opts.append(",linearize=yes");
  1436. if (cleanSyntax.getState()) opts.append(",clean=yes");
  1437. if (sanitizeSyntax.getState()) opts.append(",sanitize=yes");
  1438. if (encryption.getSelectedItem() == "None") opts.append(",decrypt=yes");
  1439. if (encryption.getSelectedItem() == "Keep") opts.append(",decrypt=no");
  1440. if (encryption.getSelectedItem() == "None") opts.append(",encrypt=no");
  1441. if (encryption.getSelectedItem() == "Keep") opts.append(",encrypt=keep");
  1442. if (encryption.getSelectedItem() == "RC4, 40bit") opts.append(",encrypt=rc4-40");
  1443. if (encryption.getSelectedItem() == "RC4, 128bit") opts.append(",encrypt=rc4-128");
  1444. if (encryption.getSelectedItem() == "AES, 128bit") opts.append(",encrypt=aes-128");
  1445. if (encryption.getSelectedItem() == "AES, 256bit") opts.append(",encrypt=aes-256");
  1446. if (userPassword.getText().length() > 0) {
  1447. opts.append(",user-password=");
  1448. opts.append(userPassword.getText());
  1449. }
  1450. if (ownerPassword.getText().length() > 0) {
  1451. opts.append(",owner-password=");
  1452. opts.append(ownerPassword.getText());
  1453. }
  1454. opts.append(",permissions=-1");
  1455. if (garbageCollect.getState() && isPDF && isRedacted)
  1456. opts.append(",garbage=yes");
  1457. else
  1458. opts.append(",garbage=compact");
  1459. }
  1460. if (opts.charAt(0) == ',')
  1461. opts.deleteCharAt(0);
  1462. options = opts.toString();
  1463. }
  1464. String getOptions() {
  1465. return options;
  1466. }
  1467. }
  1468. class Progressmeter extends Dialog implements ActionListener, KeyListener {
  1469. Label info = new Label("", Label.CENTER);
  1470. Button cancel = new Button("Cancel");
  1471. boolean cancelled = false;
  1472. boolean done = false;
  1473. public Progressmeter(Frame parent, String title, boolean modal, String initialText) {
  1474. super(parent, title, modal);
  1475. setLayout(new GridLayout(2, 1));
  1476. info.setText(initialText);
  1477. add(info);
  1478. cancel.addActionListener(this);
  1479. cancel.addKeyListener(this);
  1480. add(cancel);
  1481. pack();
  1482. setResizable(false);
  1483. cancel.requestFocusInWindow();
  1484. }
  1485. public void actionPerformed(ActionEvent e) {
  1486. if (e.getSource() == cancel)
  1487. cancel();
  1488. }
  1489. public void keyPressed(KeyEvent e) { }
  1490. public void keyReleased(KeyEvent e) { }
  1491. public void keyTyped(KeyEvent e) {
  1492. if (e.getKeyChar() == '\u001b')
  1493. cancel();
  1494. }
  1495. public void cancel() {
  1496. cancelled = true;
  1497. }
  1498. public void done() {
  1499. done = true;
  1500. }
  1501. public boolean progress(String text) {
  1502. info.setText(text);
  1503. return cancelled || done;
  1504. }
  1505. }
  1506. class OCRProgressmeter extends Progressmeter implements DocumentWriter.OCRListener {
  1507. int pages;
  1508. public OCRProgressmeter(Frame parent, String title, int pages) {
  1509. super(parent, title, true, "Progress: Page 65535/65535: 100%");
  1510. this.pages = pages;
  1511. progress(-1, 0);
  1512. setVisible(true);
  1513. }
  1514. public void done() {
  1515. super.done();
  1516. setVisible(false);
  1517. dispose();
  1518. }
  1519. public boolean progress(int page, int percent) {
  1520. StringBuilder text = new StringBuilder();
  1521. if (page >= 0 || pages >= 0) {
  1522. text.append("Page ");
  1523. if (page >= 0)
  1524. text.append(page + 1);
  1525. else
  1526. text.append("?");
  1527. }
  1528. if (pages >= 0) {
  1529. text.append("/");
  1530. text.append(pages);
  1531. text.append(": ");
  1532. }
  1533. text.append(percent);
  1534. text.append("%");
  1535. return progress(text.toString());
  1536. }
  1537. }
  1538. class RenderProgressmeter extends Progressmeter {
  1539. Cookie cookie;
  1540. public RenderProgressmeter(Frame parent, String title, Cookie cookie, final int update) {
  1541. super(parent, title, false, "Progress: 100%");
  1542. this.cookie = cookie;
  1543. (new Thread() {
  1544. public void run() {
  1545. try {
  1546. int slept = 0;
  1547. while (!progress(slept))
  1548. {
  1549. sleep(update);
  1550. slept += update;
  1551. }
  1552. } catch (InterruptedException e) {
  1553. }
  1554. }
  1555. }).start();
  1556. }
  1557. public void cancel() {
  1558. super.cancel();
  1559. cookie.abort();
  1560. }
  1561. public boolean progress(int slept) {
  1562. int progress = cookie.getProgress();
  1563. int max = cookie.getProgressMax();
  1564. if (max <= 0 && progress < 100)
  1565. max = 100;
  1566. else if (max <= 0 && progress > 100)
  1567. {
  1568. int v = progress;
  1569. max = 10;
  1570. while (v > 10)
  1571. {
  1572. v /= 10;
  1573. max *= 10;
  1574. }
  1575. }
  1576. if (progress >= max)
  1577. done = true;
  1578. int percent = (int) ((float) progress / max * 100.0f);
  1579. StringBuilder text = new StringBuilder();
  1580. text.append("Progress: ");
  1581. text.append(percent);
  1582. text.append("%");
  1583. if (slept > 0)
  1584. setVisible(true);
  1585. if (progress(text.toString()))
  1586. {
  1587. setVisible(false);
  1588. dispose();
  1589. return true;
  1590. }
  1591. return false;
  1592. }
  1593. }
  1594. public static void main(String[] args) {
  1595. String selectedPath;
  1596. if (args.length <= 0) {
  1597. FileDialog fileDialog = new FileDialog((Frame)null, "MuPDF Open File", FileDialog.LOAD);
  1598. fileDialog.setDirectory(System.getProperty("user.dir"));
  1599. fileDialog.setFilenameFilter(new FilenameFilter() {
  1600. public boolean accept(File dir, String name) {
  1601. return Document.recognize(name);
  1602. }
  1603. });
  1604. fileDialog.setVisible(true);
  1605. if (fileDialog.getFile() == null)
  1606. System.exit(0);
  1607. selectedPath = new StringBuffer(fileDialog.getDirectory()).append(File.separatorChar).append(fileDialog.getFile()).toString();
  1608. fileDialog.dispose();
  1609. } else {
  1610. selectedPath = args[0];
  1611. }
  1612. try {
  1613. Viewer app = new Viewer(selectedPath);
  1614. app.setVisible(true);
  1615. } catch (Exception e) {
  1616. messageBox(null, "MuPDF Error", "Cannot open \"" + selectedPath + "\": " + e.getMessage() + ".");
  1617. System.exit(1);
  1618. }
  1619. }
  1620. public float getRetinaScale() {
  1621. // first try Oracle's VM (we should also test for 1.7.0_40 or higher)
  1622. final String vendor = System.getProperty("java.vm.vendor");
  1623. boolean isOracle = vendor != null && vendor.toLowerCase().contains("Oracle".toLowerCase());
  1624. if (isOracle) {
  1625. GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
  1626. final GraphicsDevice device = env.getDefaultScreenDevice();
  1627. try {
  1628. Field field = device.getClass().getDeclaredField("scale");
  1629. if (field != null) {
  1630. field.setAccessible(true);
  1631. Object scale = field.get(device);
  1632. if (scale instanceof Integer && ((Integer)scale).intValue() == 2)
  1633. return 2.0f;
  1634. }
  1635. }
  1636. catch (Exception ignore) {
  1637. }
  1638. return 1.0f;
  1639. }
  1640. // try Apple VM
  1641. final Float scaleFactor = (Float)Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
  1642. if (scaleFactor != null && scaleFactor.intValue() == 2)
  1643. return 2.0f;
  1644. return 1.0f;
  1645. }
  1646. public int getScreenDPI() {
  1647. try {
  1648. return Toolkit.getDefaultToolkit().getScreenResolution();
  1649. } catch (HeadlessException e) {
  1650. return 72;
  1651. }
  1652. }
  1653. public Dimension getScreenSize() {
  1654. try {
  1655. return Toolkit.getDefaultToolkit().getScreenSize();
  1656. } catch (HeadlessException e) {
  1657. return new Dimension(1920, 1080);
  1658. }
  1659. }
  1660. protected static class OutlineItem {
  1661. protected String title;
  1662. protected String uri;
  1663. protected int page;
  1664. public OutlineItem(String title, String uri, int page) {
  1665. this.title = title;
  1666. this.uri = uri;
  1667. this.page = page;
  1668. }
  1669. public String toString() {
  1670. return title;
  1671. }
  1672. }
  1673. public String askPassword() {
  1674. return passwordDialog(null, "Password");
  1675. }
  1676. public void onChapterCountChange(int chapters) {
  1677. this.chapters = chapters;
  1678. }
  1679. public void onPageCountChange(int pages) {
  1680. this.pages = pages;
  1681. pageLabel.setText("/ " + pages);
  1682. }
  1683. public void onPageChange(Location page, int chapterNumber, int pageNumber, Rect bbox) {
  1684. this.location = page;
  1685. this.chapterNumber = chapterNumber;
  1686. this.pageNumber = pageNumber;
  1687. this.bbox = bbox;
  1688. if (pageNumber >= 0 && pageNumber < pages)
  1689. pageField.setText(String.valueOf(pageNumber + 1));
  1690. else
  1691. pageField.setText("");
  1692. render();
  1693. }
  1694. public void onReflowableChange(boolean reflowable) {
  1695. this.reflowable = reflowable;
  1696. fontIncButton.setEnabled(reflowable);
  1697. fontDecButton.setEnabled(reflowable);
  1698. fontSizeLabel.setEnabled(reflowable);
  1699. }
  1700. public void onLayoutChange(int width, int height, int em) {
  1701. }
  1702. public void onOutlineChange(ViewerCore.OutlineItem[] outline) {
  1703. boolean hadOutline = this.outline != null;
  1704. this.outline = outline;
  1705. outlineList.removeAll();
  1706. if (outline != null)
  1707. for (int i = 0; i < outline.length; i++)
  1708. outlineList.add(outline[i].title);
  1709. if (hadOutline)
  1710. outlinePanel.setVisible(outline != null);
  1711. }
  1712. public void onPageContentsChange(Pixmap pixmap, Rect[] links, String[] linkURIs, Quad[][] hits) {
  1713. this.pixmap = pixmap;
  1714. this.links = links;
  1715. this.linkURIs = linkURIs;
  1716. this.hits = hits;
  1717. redraw();
  1718. if (renderMeter != null)
  1719. renderMeter.done();
  1720. }
  1721. public void onSearchStart(Location startPage, Location finalPage, int direction, String needle) {
  1722. searchField.setEnabled(false);
  1723. }
  1724. public void onSearchPage(Location page, String needle) {
  1725. String text;
  1726. if (chapters > 1)
  1727. text = "Searching " + (page.chapter + 1) + "/" + chapters + "-" + page.page + "/" + pages;
  1728. else
  1729. text = "Searching " + page.page + "/" + pages;
  1730. searchStatus.setText(text);
  1731. searchStatusPanel.validate();
  1732. }
  1733. public void onSearchStop(String needle, Location page) {
  1734. searchField.setEnabled(true);
  1735. searchNextButton.setEnabled(true);
  1736. searchPrevButton.setEnabled(true);
  1737. if (page != null) {
  1738. doc.gotoLocation(page, null);
  1739. searchStatus.setText("");
  1740. } else if (needle != null)
  1741. searchStatus.setText("Search text not found.");
  1742. else
  1743. searchStatus.setText("");
  1744. searchStatusPanel.validate();
  1745. pageCanvas.requestFocusInWindow();
  1746. }
  1747. public void onSearchCancelled() {
  1748. searchField.setEnabled(true);
  1749. searchNextButton.setEnabled(true);
  1750. searchPrevButton.setEnabled(true);
  1751. searchStatus.setText("");
  1752. searchStatusPanel.validate();
  1753. }
  1754. public void onOutlineItemChange(int index) {
  1755. if (index == -1) {
  1756. int selected = outlineList.getSelectedIndex();
  1757. if (selected >= 0)
  1758. outlineList.deselect(selected);
  1759. } else {
  1760. outlineList.makeVisible(index);
  1761. outlineList.select(index);
  1762. }
  1763. }
  1764. public void onMetadataChange(String title, String author, String format, String encryption) {
  1765. this.title = title;
  1766. this.author = author;
  1767. this.format = format;
  1768. this.encryption = encryption;
  1769. }
  1770. public void onPermissionsChange(boolean print, boolean copy, boolean edit, boolean annotate, boolean form, boolean accessibility, boolean assemble, boolean printHq) {
  1771. this.print = print;
  1772. this.copy = copy;
  1773. this.edit = edit;
  1774. this.annotate = annotate;
  1775. this.form = form;
  1776. this.accessibility = accessibility;
  1777. this.assemble = assemble;
  1778. this.printHq = printHq;
  1779. }
  1780. public void onLinearizedChange(boolean linearized) {
  1781. this.linearized = linearized;
  1782. }
  1783. public void onUpdatesChange(int updates, int firstUpdate) {
  1784. this.updates = updates;
  1785. this.firstUpdate = firstUpdate;
  1786. }
  1787. }