Commit fa3051d977ab21da7bde9d9613dca600c83123a5
1 parent
2008d037
Implement --json-keys
Showing
1 changed file
with
110 additions
and
59 deletions
qpdf/qpdf.cc
| ... | ... | @@ -194,6 +194,7 @@ struct Options |
| 194 | 194 | bool show_pages; |
| 195 | 195 | bool show_page_images; |
| 196 | 196 | bool json; |
| 197 | + std::set<std::string> json_keys; | |
| 197 | 198 | bool check; |
| 198 | 199 | std::vector<PageSpec> page_specs; |
| 199 | 200 | std::map<std::string, RotationSpec> rotations; |
| ... | ... | @@ -243,7 +244,7 @@ ProgressReporter::reportProgress(int percentage) |
| 243 | 244 | << percentage << "%" << std::endl; |
| 244 | 245 | } |
| 245 | 246 | |
| 246 | -static JSON json_schema() | |
| 247 | +static JSON json_schema(std::set<std::string>* keys = 0) | |
| 247 | 248 | { |
| 248 | 249 | // Style: use all lower-case keys with no dashes or underscores. |
| 249 | 250 | // Choose array or dictionary based on indexing. For example, we |
| ... | ... | @@ -270,60 +271,76 @@ static JSON json_schema() |
| 270 | 271 | j_params.addDictionaryMember( |
| 271 | 272 | "decodelevel", JSON::makeString( |
| 272 | 273 | "decode level used to determine stream filterability")); |
| 273 | - schema.addDictionaryMember( | |
| 274 | - "objects", JSON::makeString( | |
| 275 | - "dictionary of original objects; keys are 'trailer' or 'n n R'")); | |
| 276 | - JSON page = schema.addDictionaryMember("pages", JSON::makeArray()). | |
| 277 | - addArrayElement(JSON::makeDictionary()); | |
| 278 | - page.addDictionaryMember( | |
| 279 | - "object", | |
| 280 | - JSON::makeString("reference to original page object")); | |
| 281 | - JSON image = page.addDictionaryMember("images", JSON::makeArray()). | |
| 282 | - addArrayElement(JSON::makeDictionary()); | |
| 283 | - image.addDictionaryMember( | |
| 284 | - "object", | |
| 285 | - JSON::makeString("reference to image stream")); | |
| 286 | - image.addDictionaryMember( | |
| 287 | - "width", | |
| 288 | - JSON::makeString("image width")); | |
| 289 | - image.addDictionaryMember( | |
| 290 | - "height", | |
| 291 | - JSON::makeString("image height")); | |
| 292 | - image.addDictionaryMember("filter", JSON::makeArray()). | |
| 293 | - addArrayElement( | |
| 294 | - JSON::makeString("filters applied to image data")); | |
| 295 | - image.addDictionaryMember("decodeparms", JSON::makeArray()). | |
| 296 | - addArrayElement( | |
| 297 | - JSON::makeString("decode parameters for image data")); | |
| 298 | - image.addDictionaryMember( | |
| 299 | - "filterable", | |
| 300 | - JSON::makeString("whether image data can be decoded" | |
| 301 | - " using the decode level qpdf was invoked with")); | |
| 302 | - page.addDictionaryMember("contents", JSON::makeArray()). | |
| 303 | - addArrayElement( | |
| 304 | - JSON::makeString("reference to each content stream")); | |
| 305 | - page.addDictionaryMember( | |
| 306 | - "label", | |
| 307 | - JSON::makeString("page label dictionary, or null if none")); | |
| 308 | - JSON labels = schema.addDictionaryMember("pagelabels", JSON::makeArray()). | |
| 309 | - addArrayElement(JSON::makeDictionary()); | |
| 310 | - labels.addDictionaryMember( | |
| 311 | - "index", | |
| 312 | - JSON::makeString("starting page position starting from zero")); | |
| 313 | - labels.addDictionaryMember( | |
| 314 | - "label", | |
| 315 | - JSON::makeString("page label dictionary")); | |
| 316 | - JSON outline = page.addDictionaryMember("outlines", JSON::makeArray()). | |
| 317 | - addArrayElement(JSON::makeDictionary()); | |
| 318 | - outline.addDictionaryMember( | |
| 319 | - "object", | |
| 320 | - JSON::makeString("reference to outline that targets this page")); | |
| 321 | - outline.addDictionaryMember( | |
| 322 | - "title", | |
| 323 | - JSON::makeString("outline title")); | |
| 324 | - outline.addDictionaryMember( | |
| 325 | - "dest", | |
| 326 | - JSON::makeString("outline destination dictionary")); | |
| 274 | + | |
| 275 | + bool all_keys = ((keys == 0) || keys->empty()); | |
| 276 | + | |
| 277 | + // The list of selectable top-level keys id duplicated in three | |
| 278 | + // places: json_schema, do_json, and initOptionTable. | |
| 279 | + if (all_keys || keys->count("objects")) | |
| 280 | + { | |
| 281 | + schema.addDictionaryMember( | |
| 282 | + "objects", JSON::makeString( | |
| 283 | + "dictionary of original objects;" | |
| 284 | + " keys are 'trailer' or 'n n R'")); | |
| 285 | + } | |
| 286 | + if (all_keys || keys->count("pages")) | |
| 287 | + { | |
| 288 | + JSON page = schema.addDictionaryMember("pages", JSON::makeArray()). | |
| 289 | + addArrayElement(JSON::makeDictionary()); | |
| 290 | + page.addDictionaryMember( | |
| 291 | + "object", | |
| 292 | + JSON::makeString("reference to original page object")); | |
| 293 | + JSON image = page.addDictionaryMember("images", JSON::makeArray()). | |
| 294 | + addArrayElement(JSON::makeDictionary()); | |
| 295 | + image.addDictionaryMember( | |
| 296 | + "object", | |
| 297 | + JSON::makeString("reference to image stream")); | |
| 298 | + image.addDictionaryMember( | |
| 299 | + "width", | |
| 300 | + JSON::makeString("image width")); | |
| 301 | + image.addDictionaryMember( | |
| 302 | + "height", | |
| 303 | + JSON::makeString("image height")); | |
| 304 | + image.addDictionaryMember("filter", JSON::makeArray()). | |
| 305 | + addArrayElement( | |
| 306 | + JSON::makeString("filters applied to image data")); | |
| 307 | + image.addDictionaryMember("decodeparms", JSON::makeArray()). | |
| 308 | + addArrayElement( | |
| 309 | + JSON::makeString("decode parameters for image data")); | |
| 310 | + image.addDictionaryMember( | |
| 311 | + "filterable", | |
| 312 | + JSON::makeString("whether image data can be decoded" | |
| 313 | + " using the decode level qpdf was invoked with")); | |
| 314 | + page.addDictionaryMember("contents", JSON::makeArray()). | |
| 315 | + addArrayElement( | |
| 316 | + JSON::makeString("reference to each content stream")); | |
| 317 | + page.addDictionaryMember( | |
| 318 | + "label", | |
| 319 | + JSON::makeString("page label dictionary, or null if none")); | |
| 320 | + JSON outline = page.addDictionaryMember("outlines", JSON::makeArray()). | |
| 321 | + addArrayElement(JSON::makeDictionary()); | |
| 322 | + outline.addDictionaryMember( | |
| 323 | + "object", | |
| 324 | + JSON::makeString("reference to outline that targets this page")); | |
| 325 | + outline.addDictionaryMember( | |
| 326 | + "title", | |
| 327 | + JSON::makeString("outline title")); | |
| 328 | + outline.addDictionaryMember( | |
| 329 | + "dest", | |
| 330 | + JSON::makeString("outline destination dictionary")); | |
| 331 | + } | |
| 332 | + if (all_keys || keys->count("pagelabels")) | |
| 333 | + { | |
| 334 | + JSON labels = schema.addDictionaryMember( | |
| 335 | + "pagelabels", JSON::makeArray()). | |
| 336 | + addArrayElement(JSON::makeDictionary()); | |
| 337 | + labels.addDictionaryMember( | |
| 338 | + "index", | |
| 339 | + JSON::makeString("starting page position starting from zero")); | |
| 340 | + labels.addDictionaryMember( | |
| 341 | + "label", | |
| 342 | + JSON::makeString("page label dictionary")); | |
| 343 | + } | |
| 327 | 344 | return schema; |
| 328 | 345 | } |
| 329 | 346 | |
| ... | ... | @@ -419,6 +436,7 @@ class ArgParser |
| 419 | 436 | void argShowPages(); |
| 420 | 437 | void argWithImages(); |
| 421 | 438 | void argJson(); |
| 439 | + void argJsonKey(char* parameter); | |
| 422 | 440 | void argCheck(); |
| 423 | 441 | void arg40Print(char* parameter); |
| 424 | 442 | void arg40Modify(char* parameter); |
| ... | ... | @@ -615,6 +633,11 @@ ArgParser::initOptionTable() |
| 615 | 633 | (*t)["show-pages"] = oe_bare(&ArgParser::argShowPages); |
| 616 | 634 | (*t)["with-images"] = oe_bare(&ArgParser::argWithImages); |
| 617 | 635 | (*t)["json"] = oe_bare(&ArgParser::argJson); |
| 636 | + // The list of selectable top-level keys id duplicated in three | |
| 637 | + // places: json_schema, do_json, and initOptionTable. | |
| 638 | + char const* jsonKeyChoices[] = {"objects", "pages", "pagelabels", 0}; | |
| 639 | + (*t)["json-key"] = oe_requiredChoices( | |
| 640 | + &ArgParser::argJsonKey, jsonKeyChoices); | |
| 618 | 641 | (*t)["check"] = oe_bare(&ArgParser::argCheck); |
| 619 | 642 | |
| 620 | 643 | t = &this->encrypt40_option_table; |
| ... | ... | @@ -1178,6 +1201,12 @@ ArgParser::argJson() |
| 1178 | 1201 | } |
| 1179 | 1202 | |
| 1180 | 1203 | void |
| 1204 | +ArgParser::argJsonKey(char* parameter) | |
| 1205 | +{ | |
| 1206 | + o.json_keys.insert(parameter); | |
| 1207 | +} | |
| 1208 | + | |
| 1209 | +void | |
| 1181 | 1210 | ArgParser::argCheck() |
| 1182 | 1211 | { |
| 1183 | 1212 | o.check = true; |
| ... | ... | @@ -1659,6 +1688,16 @@ automated test suites for software that uses the qpdf library.\n\ |
| 1659 | 1688 | --show-pages shows the object/generation number for each page\n\ |
| 1660 | 1689 | --with-images also shows the object IDs for images on each page\n\ |
| 1661 | 1690 | --check check file structure + encryption, linearization\n\ |
| 1691 | +--json generate a json representation of the file\n\ | |
| 1692 | +--json-help describe the format of the json representation\n\ | |
| 1693 | +--json-key=key repeatable; prune json structure to include only\n\ | |
| 1694 | + specified keys\n\ | |
| 1695 | +\n\ | |
| 1696 | +The json representation generated by qpdf is designed to facilitate\n\ | |
| 1697 | +processing of qpdf from other programming languages that have a hard\n\ | |
| 1698 | +time calling C++ APIs. Run qpdf --json-help for details on the format.\n\ | |
| 1699 | +The manual has more in-depth information about the json representation\n\ | |
| 1700 | +and certain compatibility guarantees that qpdf provides.\n\ | |
| 1662 | 1701 | \n\ |
| 1663 | 1702 | The --raw-stream-data and --filtered-stream-data options are ignored\n\ |
| 1664 | 1703 | unless --show-object is given. Either of these options will cause the\n\ |
| ... | ... | @@ -2704,13 +2743,25 @@ static void do_json(QPDF& pdf, Options& o) |
| 2704 | 2743 | j_params.addDictionaryMember( |
| 2705 | 2744 | "decodelevel", JSON::makeString(decode_level_str)); |
| 2706 | 2745 | |
| 2707 | - do_json_objects(pdf, o, j); | |
| 2708 | - do_json_pages(pdf, o, j); | |
| 2709 | - do_json_page_labels(pdf, o, j); | |
| 2746 | + bool all_keys = o.json_keys.empty(); | |
| 2747 | + // The list of selectable top-level keys id duplicated in three | |
| 2748 | + // places: json_schema, do_json, and initOptionTable. | |
| 2749 | + if (all_keys || o.json_keys.count("objects")) | |
| 2750 | + { | |
| 2751 | + do_json_objects(pdf, o, j); | |
| 2752 | + } | |
| 2753 | + if (all_keys || o.json_keys.count("pages")) | |
| 2754 | + { | |
| 2755 | + do_json_pages(pdf, o, j); | |
| 2756 | + } | |
| 2757 | + if (all_keys || o.json_keys.count("pagelabels")) | |
| 2758 | + { | |
| 2759 | + do_json_page_labels(pdf, o, j); | |
| 2760 | + } | |
| 2710 | 2761 | |
| 2711 | 2762 | // Check against schema |
| 2712 | 2763 | |
| 2713 | - JSON schema = json_schema(); | |
| 2764 | + JSON schema = json_schema(&o.json_keys); | |
| 2714 | 2765 | std::list<std::string> errors; |
| 2715 | 2766 | if (! j.checkSchema(schema, errors)) |
| 2716 | 2767 | { | ... | ... |