pdf-layer.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. // Copyright (C) 2004-2025 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. #include "mupdf/fitz.h"
  23. #include "mupdf/pdf.h"
  24. #include <string.h>
  25. /*
  26. Notes on OCGs etc.
  27. PDF Documents may contain Optional Content Groups. Which of
  28. these is shown at any given time is dependent on which
  29. Optional Content Configuration Dictionary is in force at the
  30. time.
  31. A pdf_document, once loaded, contains some state saying which
  32. OCGs are enabled/disabled, and which 'Intent' (or 'Intents')
  33. a file is being used for. This information is held outside of
  34. the actual PDF file.
  35. An Intent (just 'View' or 'Design' or 'All', according to
  36. PDF 2.0, but theoretically more) says which OCGs to consider
  37. or ignore in calculating the visibility of content. The
  38. Intent (or Intents, for there can be an array) is set by the
  39. current OCCD.
  40. When first loaded, we turn all OCGs on, then load the default
  41. OCCD. This may turn some OCGs off, and sets the document Intent.
  42. Callers can ask how many OCCDs there are, read the names/creators
  43. for each, and then select any one of them. That updates which
  44. OCGs are selected, and resets the Intent.
  45. Once an OCCD has been selected, a caller can enumerate the
  46. 'displayable configuration'. This is a list of labels/radio
  47. buttons/check buttons that can be used to enable/disable
  48. given OCGs. The caller can then enable/disable OCGs by
  49. asking to select (or toggle) given entries in that list.
  50. Thus the handling of radio button groups, and 'locked'
  51. elements is kept within the core of MuPDF.
  52. Finally, the caller can set the 'usage' for a document. This
  53. can be 'View', 'Print', or 'Export'.
  54. */
  55. typedef struct
  56. {
  57. pdf_obj *obj;
  58. int n;
  59. int state;
  60. } pdf_ocg_entry;
  61. typedef struct
  62. {
  63. int ocg;
  64. const char *name;
  65. int depth;
  66. unsigned int button_flags : 2;
  67. unsigned int locked : 1;
  68. } pdf_ocg_ui;
  69. struct pdf_ocg_descriptor
  70. {
  71. int current;
  72. int num_configs;
  73. int len;
  74. pdf_ocg_entry *ocgs;
  75. pdf_obj *intent;
  76. const char *usage;
  77. int num_ui_entries;
  78. pdf_ocg_ui *ui;
  79. };
  80. int
  81. pdf_count_layer_configs(fz_context *ctx, pdf_document *doc)
  82. {
  83. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  84. return desc ? desc->num_configs : 0;
  85. }
  86. int
  87. pdf_count_layers(fz_context *ctx, pdf_document *doc)
  88. {
  89. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  90. return desc ? desc->len : 0;
  91. }
  92. const char *
  93. pdf_layer_name(fz_context *ctx, pdf_document *doc, int layer)
  94. {
  95. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  96. if (desc && layer >= 0 && layer < desc->len)
  97. return pdf_dict_get_text_string(ctx, desc->ocgs[layer].obj, PDF_NAME(Name));
  98. fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid layer index");
  99. }
  100. int
  101. pdf_layer_is_enabled(fz_context *ctx, pdf_document *doc, int layer)
  102. {
  103. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  104. if (desc && layer >= 0 && layer < desc->len)
  105. return desc->ocgs[layer].state;
  106. fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid layer index");
  107. }
  108. void
  109. pdf_enable_layer(fz_context *ctx, pdf_document *doc, int layer, int enabled)
  110. {
  111. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  112. if (desc && layer >= 0 && layer < desc->len)
  113. desc->ocgs[layer].state = enabled;
  114. else
  115. fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid layer index");
  116. }
  117. static int
  118. count_entries(fz_context *ctx, pdf_obj *obj, pdf_cycle_list *cycle_up)
  119. {
  120. pdf_cycle_list cycle;
  121. int len = pdf_array_len(ctx, obj);
  122. int i;
  123. int count = 0;
  124. for (i = 0; i < len; i++)
  125. {
  126. pdf_obj *o = pdf_array_get(ctx, obj, i);
  127. if (pdf_cycle(ctx, &cycle, cycle_up, o))
  128. continue;
  129. count += (pdf_is_array(ctx, o) ? count_entries(ctx, o, &cycle) : 1);
  130. }
  131. return count;
  132. }
  133. static pdf_ocg_ui *
  134. get_ocg_ui(fz_context *ctx, pdf_ocg_descriptor *desc, int fill)
  135. {
  136. if (fill == desc->num_ui_entries)
  137. {
  138. /* Number of layers changed while parsing;
  139. * probably due to a repair. */
  140. int newsize = desc->num_ui_entries * 2;
  141. if (newsize == 0)
  142. newsize = 4; /* Arbitrary non-zero */
  143. desc->ui = fz_realloc_array(ctx, desc->ui, newsize, pdf_ocg_ui);
  144. desc->num_ui_entries = newsize;
  145. }
  146. return &desc->ui[fill];
  147. }
  148. static int
  149. ocgcmp(const void *a_, const void *b_)
  150. {
  151. const pdf_ocg_entry *a = a_;
  152. const pdf_ocg_entry *b = b_;
  153. return (b->n - a->n);
  154. }
  155. static int
  156. find_ocg(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *obj)
  157. {
  158. int n = pdf_to_num(ctx, obj);
  159. int l = 0;
  160. int r = desc->len-1;
  161. if (n <= 0)
  162. return -1;
  163. while (l <= r)
  164. {
  165. int m = (l + r) >> 1;
  166. int c = desc->ocgs[m].n - n;
  167. if (c < 0)
  168. r = m - 1;
  169. else if (c > 0)
  170. l = m + 1;
  171. else
  172. return m;
  173. }
  174. return -1;
  175. }
  176. static int
  177. populate_ui(fz_context *ctx, pdf_ocg_descriptor *desc, int fill, pdf_obj *order, int depth, pdf_obj *rbgroups, pdf_obj *locked,
  178. pdf_cycle_list *cycle_up)
  179. {
  180. pdf_cycle_list cycle;
  181. int len = pdf_array_len(ctx, order);
  182. int i, j;
  183. pdf_ocg_ui *ui;
  184. for (i = 0; i < len; i++)
  185. {
  186. pdf_obj *o = pdf_array_get(ctx, order, i);
  187. if (pdf_is_array(ctx, o))
  188. {
  189. if (pdf_cycle(ctx, &cycle, cycle_up, o))
  190. continue;
  191. fill = populate_ui(ctx, desc, fill, o, depth+1, rbgroups, locked, &cycle);
  192. continue;
  193. }
  194. if (pdf_is_string(ctx, o))
  195. {
  196. ui = get_ocg_ui(ctx, desc, fill++);
  197. ui->depth = depth;
  198. ui->ocg = -1;
  199. ui->name = pdf_to_text_string(ctx, o);
  200. ui->button_flags = PDF_LAYER_UI_LABEL;
  201. ui->locked = 1;
  202. continue;
  203. }
  204. j = find_ocg(ctx, desc, o);
  205. if (j < 0)
  206. continue; /* OCG not found in main list! Just ignore it */
  207. ui = get_ocg_ui(ctx, desc, fill++);
  208. ui->depth = depth;
  209. ui->ocg = j;
  210. ui->name = pdf_dict_get_text_string(ctx, o, PDF_NAME(Name));
  211. ui->button_flags = pdf_array_contains(ctx, o, rbgroups) ? PDF_LAYER_UI_RADIOBOX : PDF_LAYER_UI_CHECKBOX;
  212. ui->locked = pdf_array_contains(ctx, o, locked);
  213. }
  214. return fill;
  215. }
  216. static void
  217. drop_ui(fz_context *ctx, pdf_ocg_descriptor *desc)
  218. {
  219. if (!desc)
  220. return;
  221. fz_free(ctx, desc->ui);
  222. desc->ui = NULL;
  223. }
  224. static void
  225. load_ui(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *ocprops, pdf_obj *occg)
  226. {
  227. pdf_obj *order;
  228. pdf_obj *rbgroups;
  229. pdf_obj *locked;
  230. int count;
  231. /* Count the number of entries */
  232. order = pdf_dict_get(ctx, occg, PDF_NAME(Order));
  233. if (!order)
  234. order = pdf_dict_getp(ctx, ocprops, "D/Order");
  235. count = count_entries(ctx, order, NULL);
  236. rbgroups = pdf_dict_get(ctx, occg, PDF_NAME(RBGroups));
  237. if (!rbgroups)
  238. rbgroups = pdf_dict_getp(ctx, ocprops, "D/RBGroups");
  239. locked = pdf_dict_get(ctx, occg, PDF_NAME(Locked));
  240. desc->num_ui_entries = count;
  241. if (desc->num_ui_entries == 0)
  242. return;
  243. desc->ui = fz_malloc_struct_array(ctx, count, pdf_ocg_ui);
  244. fz_try(ctx)
  245. {
  246. desc->num_ui_entries = populate_ui(ctx, desc, 0, order, 0, rbgroups, locked, NULL);
  247. }
  248. fz_catch(ctx)
  249. {
  250. drop_ui(ctx, desc);
  251. fz_rethrow(ctx);
  252. }
  253. }
  254. void
  255. pdf_select_layer_config(fz_context *ctx, pdf_document *doc, int config)
  256. {
  257. pdf_ocg_descriptor *desc;
  258. int i, j, len, len2;
  259. pdf_obj *obj, *cobj;
  260. pdf_obj *name;
  261. desc = pdf_read_ocg(ctx, doc);
  262. obj = pdf_dict_get(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OCProperties));
  263. if (!obj)
  264. {
  265. if (config == 0)
  266. return;
  267. else
  268. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unknown Layer config (None known!)");
  269. }
  270. cobj = pdf_array_get(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Configs)), config);
  271. if (!cobj)
  272. {
  273. if (config != 0)
  274. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Illegal Layer config");
  275. cobj = pdf_dict_get(ctx, obj, PDF_NAME(D));
  276. if (!cobj)
  277. fz_throw(ctx, FZ_ERROR_FORMAT, "No default Layer config");
  278. }
  279. pdf_drop_obj(ctx, desc->intent);
  280. desc->intent = pdf_keep_obj(ctx, pdf_dict_get(ctx, cobj, PDF_NAME(Intent)));
  281. len = desc->len;
  282. name = pdf_dict_get(ctx, cobj, PDF_NAME(BaseState));
  283. if (pdf_name_eq(ctx, name, PDF_NAME(Unchanged)))
  284. {
  285. /* Do nothing */
  286. }
  287. else if (pdf_name_eq(ctx, name, PDF_NAME(OFF)))
  288. {
  289. for (i = 0; i < len; i++)
  290. {
  291. desc->ocgs[i].state = 0;
  292. }
  293. }
  294. else /* Default to ON */
  295. {
  296. for (i = 0; i < len; i++)
  297. {
  298. desc->ocgs[i].state = 1;
  299. }
  300. }
  301. obj = pdf_dict_get(ctx, cobj, PDF_NAME(ON));
  302. len2 = pdf_array_len(ctx, obj);
  303. for (i = 0; i < len2; i++)
  304. {
  305. pdf_obj *o = pdf_array_get(ctx, obj, i);
  306. for (j=0; j < len; j++)
  307. {
  308. if (!pdf_objcmp_resolve(ctx, desc->ocgs[j].obj, o))
  309. {
  310. desc->ocgs[j].state = 1;
  311. break;
  312. }
  313. }
  314. }
  315. obj = pdf_dict_get(ctx, cobj, PDF_NAME(OFF));
  316. len2 = pdf_array_len(ctx, obj);
  317. for (i = 0; i < len2; i++)
  318. {
  319. pdf_obj *o = pdf_array_get(ctx, obj, i);
  320. for (j=0; j < len; j++)
  321. {
  322. if (!pdf_objcmp_resolve(ctx, desc->ocgs[j].obj, o))
  323. {
  324. desc->ocgs[j].state = 0;
  325. break;
  326. }
  327. }
  328. }
  329. desc->current = config;
  330. drop_ui(ctx, desc);
  331. load_ui(ctx, desc, obj, cobj);
  332. }
  333. void
  334. pdf_layer_config_info(fz_context *ctx, pdf_document *doc, int config_num, pdf_layer_config *info)
  335. {
  336. pdf_ocg_descriptor *desc;
  337. pdf_obj *ocprops;
  338. pdf_obj *obj;
  339. if (!info)
  340. return;
  341. desc = pdf_read_ocg(ctx, doc);
  342. info->name = NULL;
  343. info->creator = NULL;
  344. if (config_num < 0 || config_num >= desc->num_configs)
  345. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Invalid layer config number");
  346. ocprops = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties");
  347. if (!ocprops)
  348. return;
  349. obj = pdf_dict_get(ctx, ocprops, PDF_NAME(Configs));
  350. if (pdf_is_array(ctx, obj))
  351. obj = pdf_array_get(ctx, obj, config_num);
  352. else if (config_num == 0)
  353. obj = pdf_dict_get(ctx, ocprops, PDF_NAME(D));
  354. else
  355. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Invalid layer config number");
  356. info->creator = pdf_dict_get_string(ctx, obj, PDF_NAME(Creator), NULL);
  357. info->name = pdf_dict_get_string(ctx, obj, PDF_NAME(Name), NULL);
  358. }
  359. void
  360. pdf_drop_ocg(fz_context *ctx, pdf_document *doc)
  361. {
  362. pdf_ocg_descriptor *desc;
  363. int i;
  364. if (!doc)
  365. return;
  366. desc = doc->ocg;
  367. if (!desc)
  368. return;
  369. drop_ui(ctx, desc);
  370. pdf_drop_obj(ctx, desc->intent);
  371. for (i = 0; i < desc->len; i++)
  372. pdf_drop_obj(ctx, desc->ocgs[i].obj);
  373. fz_free(ctx, desc->ocgs);
  374. fz_free(ctx, desc);
  375. }
  376. static void
  377. clear_radio_group(fz_context *ctx, pdf_document *doc, pdf_obj *ocg)
  378. {
  379. pdf_obj *rbgroups = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties/RBGroups");
  380. int len, i;
  381. len = pdf_array_len(ctx, rbgroups);
  382. for (i = 0; i < len; i++)
  383. {
  384. pdf_obj *group = pdf_array_get(ctx, rbgroups, i);
  385. if (pdf_array_contains(ctx, ocg, group))
  386. {
  387. int len2 = pdf_array_len(ctx, group);
  388. int j;
  389. for (j = 0; j < len2; j++)
  390. {
  391. pdf_obj *g = pdf_array_get(ctx, group, j);
  392. int k;
  393. for (k = 0; k < doc->ocg->len; k++)
  394. {
  395. pdf_ocg_entry *s = &doc->ocg->ocgs[k];
  396. if (!pdf_objcmp_resolve(ctx, s->obj, g))
  397. s->state = 0;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. int pdf_count_layer_config_ui(fz_context *ctx, pdf_document *doc)
  404. {
  405. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  406. return desc ? desc->num_ui_entries : 0;
  407. }
  408. void pdf_select_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
  409. {
  410. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  411. pdf_ocg_ui *entry;
  412. if (ui < 0 || ui >= desc->num_ui_entries)
  413. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Out of range UI entry selected");
  414. entry = &desc->ui[ui];
  415. if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
  416. entry->button_flags != PDF_LAYER_UI_CHECKBOX)
  417. return;
  418. if (entry->locked)
  419. return;
  420. if (entry->button_flags == PDF_LAYER_UI_RADIOBOX)
  421. clear_radio_group(ctx, doc, desc->ocgs[entry->ocg].obj);
  422. desc->ocgs[entry->ocg].state = 1;
  423. }
  424. void pdf_toggle_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
  425. {
  426. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  427. pdf_ocg_ui *entry;
  428. int selected;
  429. if (ui < 0 || ui >= desc->num_ui_entries)
  430. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Out of range UI entry toggled");
  431. entry = &desc->ui[ui];
  432. if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
  433. entry->button_flags != PDF_LAYER_UI_CHECKBOX)
  434. return;
  435. if (entry->locked)
  436. return;
  437. selected = desc->ocgs[entry->ocg].state;
  438. if (entry->button_flags == PDF_LAYER_UI_RADIOBOX)
  439. clear_radio_group(ctx, doc, desc->ocgs[entry->ocg].obj);
  440. desc->ocgs[entry->ocg].state = !selected;
  441. }
  442. void pdf_deselect_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
  443. {
  444. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  445. pdf_ocg_ui *entry;
  446. if (ui < 0 || ui >= desc->num_ui_entries)
  447. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Out of range UI entry deselected");
  448. entry = &desc->ui[ui];
  449. if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
  450. entry->button_flags != PDF_LAYER_UI_CHECKBOX)
  451. return;
  452. if (entry->locked)
  453. return;
  454. desc->ocgs[entry->ocg].state = 0;
  455. }
  456. void
  457. pdf_layer_config_ui_info(fz_context *ctx, pdf_document *doc, int ui, pdf_layer_config_ui *info)
  458. {
  459. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  460. pdf_ocg_ui *entry;
  461. if (!info)
  462. return;
  463. info->depth = 0;
  464. info->locked = 0;
  465. info->selected = 0;
  466. info->text = NULL;
  467. info->type = 0;
  468. if (ui < 0 || ui >= desc->num_ui_entries)
  469. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Out of range UI entry selected");
  470. entry = &desc->ui[ui];
  471. info->type = entry->button_flags;
  472. info->depth = entry->depth;
  473. info->selected = desc->ocgs[entry->ocg].state;
  474. info->locked = entry->locked;
  475. info->text = entry->name;
  476. }
  477. static int
  478. ocg_intents_include(fz_context *ctx, pdf_ocg_descriptor *desc, const char *name)
  479. {
  480. int i, len;
  481. if (strcmp(name, "All") == 0)
  482. return 1;
  483. /* In the absence of a specified intent, it's 'View' */
  484. if (!desc->intent)
  485. return (strcmp(name, "View") == 0);
  486. if (pdf_is_name(ctx, desc->intent))
  487. {
  488. const char *intent = pdf_to_name(ctx, desc->intent);
  489. if (strcmp(intent, "All") == 0)
  490. return 1;
  491. return (strcmp(intent, name) == 0);
  492. }
  493. if (!pdf_is_array(ctx, desc->intent))
  494. return 0;
  495. len = pdf_array_len(ctx, desc->intent);
  496. for (i=0; i < len; i++)
  497. {
  498. const char *intent = pdf_array_get_name(ctx, desc->intent, i);
  499. if (strcmp(intent, "All") == 0)
  500. return 1;
  501. if (strcmp(intent, name) == 0)
  502. return 1;
  503. }
  504. return 0;
  505. }
  506. static int
  507. pdf_is_ocg_hidden_imp(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, const char *usage, pdf_obj *ocg, pdf_cycle_list *cycle_up)
  508. {
  509. pdf_cycle_list cycle;
  510. pdf_ocg_descriptor *desc = pdf_read_ocg(ctx, doc);
  511. pdf_obj *obj, *obj2, *type;
  512. char event_state[16];
  513. /* If no usage, everything is visible */
  514. if (!usage)
  515. return 0;
  516. /* If no ocg descriptor or no ocgs described, everything is visible */
  517. if (!desc || desc->len == 0)
  518. return 0;
  519. /* If we've been handed a name, look it up in the properties. */
  520. if (pdf_is_name(ctx, ocg))
  521. {
  522. ocg = pdf_dict_get(ctx, pdf_dict_get(ctx, rdb, PDF_NAME(Properties)), ocg);
  523. }
  524. /* If we haven't been given an ocg at all, then we're visible */
  525. if (!ocg)
  526. return 0;
  527. /* Avoid infinite recursions */
  528. if (pdf_cycle(ctx, &cycle, cycle_up, ocg))
  529. return 0;
  530. fz_strlcpy(event_state, usage, sizeof event_state);
  531. fz_strlcat(event_state, "State", sizeof event_state);
  532. type = pdf_dict_get(ctx, ocg, PDF_NAME(Type));
  533. if (pdf_name_eq(ctx, type, PDF_NAME(OCG)))
  534. {
  535. /* An Optional Content Group */
  536. int default_value = 0;
  537. int len = desc->len;
  538. int i;
  539. pdf_obj *es;
  540. /* by default an OCG is visible, unless it's explicitly hidden */
  541. for (i = 0; i < len; i++)
  542. {
  543. /* Deliberately do NOT resolve here. Bug 702261. */
  544. if (!pdf_objcmp(ctx, desc->ocgs[i].obj, ocg))
  545. {
  546. default_value = !desc->ocgs[i].state;
  547. break;
  548. }
  549. }
  550. /* Check Intents; if our intent is not part of the set given
  551. * by the current config, we should ignore it. */
  552. obj = pdf_dict_get(ctx, ocg, PDF_NAME(Intent));
  553. if (pdf_is_name(ctx, obj))
  554. {
  555. /* If it doesn't match, it's hidden */
  556. if (ocg_intents_include(ctx, desc, pdf_to_name(ctx, obj)) == 0)
  557. return 1;
  558. }
  559. else if (pdf_is_array(ctx, obj))
  560. {
  561. int match = 0;
  562. len = pdf_array_len(ctx, obj);
  563. for (i=0; i<len; i++) {
  564. match |= ocg_intents_include(ctx, desc, pdf_array_get_name(ctx, obj, i));
  565. if (match)
  566. break;
  567. }
  568. /* If we don't match any, it's hidden */
  569. if (match == 0)
  570. return 1;
  571. }
  572. else
  573. {
  574. /* If it doesn't match, it's hidden */
  575. if (ocg_intents_include(ctx, desc, "View") == 0)
  576. return 1;
  577. }
  578. /* FIXME: Currently we do a very simple check whereby we look
  579. * at the Usage object (an Optional Content Usage Dictionary)
  580. * and check to see if the corresponding 'event' key is on
  581. * or off.
  582. *
  583. * Really we should only look at Usage dictionaries that
  584. * correspond to entries in the AS list in the OCG config.
  585. * Given that we don't handle Zoom or User, or Language
  586. * dicts, this is not really a problem. */
  587. obj = pdf_dict_get(ctx, ocg, PDF_NAME(Usage));
  588. if (!pdf_is_dict(ctx, obj))
  589. return default_value;
  590. /* FIXME: Should look at Zoom (and return hidden if out of
  591. * max/min range) */
  592. /* FIXME: Could provide hooks to the caller to check if
  593. * User is appropriate - if not return hidden. */
  594. obj2 = pdf_dict_gets(ctx, obj, usage);
  595. es = pdf_dict_gets(ctx, obj2, event_state);
  596. if (pdf_name_eq(ctx, es, PDF_NAME(OFF)))
  597. {
  598. return 1;
  599. }
  600. if (pdf_name_eq(ctx, es, PDF_NAME(ON)))
  601. {
  602. return 0;
  603. }
  604. return default_value;
  605. }
  606. else if (pdf_name_eq(ctx, type, PDF_NAME(OCMD)))
  607. {
  608. /* An Optional Content Membership Dictionary */
  609. pdf_obj *name;
  610. int combine, on = 0;
  611. obj = pdf_dict_get(ctx, ocg, PDF_NAME(VE));
  612. if (pdf_is_array(ctx, obj)) {
  613. /* FIXME: Calculate visibility from array */
  614. return 0;
  615. }
  616. name = pdf_dict_get(ctx, ocg, PDF_NAME(P));
  617. /* Set combine; Bit 0 set => AND, Bit 1 set => true means
  618. * Off, otherwise true means On */
  619. if (pdf_name_eq(ctx, name, PDF_NAME(AllOn)))
  620. {
  621. combine = 1;
  622. }
  623. else if (pdf_name_eq(ctx, name, PDF_NAME(AnyOff)))
  624. {
  625. combine = 2;
  626. }
  627. else if (pdf_name_eq(ctx, name, PDF_NAME(AllOff)))
  628. {
  629. combine = 3;
  630. }
  631. else /* Assume it's the default (AnyOn) */
  632. {
  633. combine = 0;
  634. }
  635. obj = pdf_dict_get(ctx, ocg, PDF_NAME(OCGs));
  636. on = combine & 1;
  637. if (pdf_is_array(ctx, obj)) {
  638. int i, len;
  639. len = pdf_array_len(ctx, obj);
  640. for (i = 0; i < len; i++)
  641. {
  642. int hidden = pdf_is_ocg_hidden_imp(ctx, doc, rdb, usage, pdf_array_get(ctx, obj, i), &cycle);
  643. if ((combine & 1) == 0)
  644. hidden = !hidden;
  645. if (combine & 2)
  646. on &= hidden;
  647. else
  648. on |= hidden;
  649. }
  650. }
  651. else
  652. {
  653. on = pdf_is_ocg_hidden_imp(ctx, doc, rdb, usage, obj, &cycle);
  654. if ((combine & 1) == 0)
  655. on = !on;
  656. }
  657. return !on;
  658. }
  659. /* No idea what sort of object this is - be visible */
  660. return 0;
  661. }
  662. int
  663. pdf_is_ocg_hidden(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, const char *usage, pdf_obj *ocg)
  664. {
  665. return pdf_is_ocg_hidden_imp(ctx, doc, rdb, usage, ocg, NULL);
  666. }
  667. pdf_ocg_descriptor *
  668. pdf_read_ocg(fz_context *ctx, pdf_document *doc)
  669. {
  670. pdf_obj *prop, *ocgs, *configs;
  671. int len, i, num_configs;
  672. if (doc->ocg)
  673. return doc->ocg;
  674. fz_try(ctx)
  675. {
  676. prop = pdf_dict_get(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OCProperties));
  677. configs = pdf_dict_get(ctx, prop, PDF_NAME(Configs));
  678. num_configs = pdf_array_len(ctx, configs);
  679. ocgs = pdf_dict_get(ctx, prop, PDF_NAME(OCGs));
  680. len = pdf_array_len(ctx, ocgs);
  681. doc->ocg = fz_malloc_struct(ctx, pdf_ocg_descriptor);
  682. doc->ocg->ocgs = fz_calloc(ctx, len, sizeof(*doc->ocg->ocgs));
  683. doc->ocg->len = len;
  684. doc->ocg->num_configs = num_configs;
  685. for (i = 0; i < len; i++)
  686. {
  687. pdf_obj *o = pdf_array_get(ctx, ocgs, i);
  688. doc->ocg->ocgs[i].obj = pdf_keep_obj(ctx, o);
  689. doc->ocg->ocgs[i].n = pdf_to_num(ctx, o);
  690. doc->ocg->ocgs[i].state = 1;
  691. }
  692. qsort(doc->ocg->ocgs, len, sizeof(doc->ocg->ocgs[0]), ocgcmp);
  693. pdf_select_layer_config(ctx, doc, 0);
  694. }
  695. fz_catch(ctx)
  696. {
  697. pdf_drop_ocg(ctx, doc);
  698. doc->ocg = NULL;
  699. fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
  700. fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
  701. fz_report_error(ctx);
  702. fz_warn(ctx, "Ignoring broken Optional Content configuration");
  703. doc->ocg = fz_malloc_struct(ctx, pdf_ocg_descriptor);
  704. }
  705. return doc->ocg;
  706. }
  707. void
  708. pdf_set_layer_config_as_default(fz_context *ctx, pdf_document *doc)
  709. {
  710. pdf_obj *ocprops, *d, *order, *on, *configs, *rbgroups;
  711. int k;
  712. ocprops = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties");
  713. if (!ocprops)
  714. return;
  715. /* All files with OCGs are required to have a D entry */
  716. d = pdf_dict_get(ctx, ocprops, PDF_NAME(D));
  717. if (d == NULL)
  718. return;
  719. pdf_dict_put(ctx, d, PDF_NAME(BaseState), PDF_NAME(OFF));
  720. /* We are about to delete RBGroups and Order, from D. These are
  721. * both the underlying defaults for other configs, so copy the
  722. * current values out to any config that doesn't have one
  723. * already. */
  724. order = pdf_dict_get(ctx, d, PDF_NAME(Order));
  725. rbgroups = pdf_dict_get(ctx, d, PDF_NAME(RBGroups));
  726. configs = pdf_dict_get(ctx, ocprops, PDF_NAME(Configs));
  727. if (configs)
  728. {
  729. int len = pdf_array_len(ctx, configs);
  730. for (k=0; k < len; k++)
  731. {
  732. pdf_obj *config = pdf_array_get(ctx, configs, k);
  733. if (order && !pdf_dict_get(ctx, config, PDF_NAME(Order)))
  734. pdf_dict_put(ctx, config, PDF_NAME(Order), order);
  735. if (rbgroups && !pdf_dict_get(ctx, config, PDF_NAME(RBGroups)))
  736. pdf_dict_put(ctx, config, PDF_NAME(RBGroups), rbgroups);
  737. }
  738. }
  739. /* Offer all the layers in the UI */
  740. order = pdf_new_array(ctx, doc, 4);
  741. on = pdf_new_array(ctx, doc, 4);
  742. for (k = 0; k < doc->ocg->len; k++)
  743. {
  744. pdf_ocg_entry *s = &doc->ocg->ocgs[k];
  745. pdf_array_push(ctx, order, s->obj);
  746. if (s->state)
  747. pdf_array_push(ctx, on, s->obj);
  748. }
  749. pdf_dict_put(ctx, d, PDF_NAME(Order), order);
  750. pdf_dict_put(ctx, d, PDF_NAME(ON), on);
  751. pdf_dict_del(ctx, d, PDF_NAME(OFF));
  752. pdf_dict_del(ctx, d, PDF_NAME(AS));
  753. pdf_dict_put(ctx, d, PDF_NAME(Intent), PDF_NAME(View));
  754. pdf_dict_del(ctx, d, PDF_NAME(Name));
  755. pdf_dict_del(ctx, d, PDF_NAME(Creator));
  756. pdf_dict_del(ctx, d, PDF_NAME(RBGroups));
  757. pdf_dict_del(ctx, d, PDF_NAME(Locked));
  758. pdf_dict_del(ctx, ocprops, PDF_NAME(Configs));
  759. }