Commit 0ab45001b52ddc79d0e9742e5f38b842f3216c3e

Authored by m-holger
1 parent 4a2a8283

Refactor JSON schema handling in QPDFJob::json_schema

Consolidate and replace inline JSON schema strings with static constexpr constants for better organization and maintainability. Functionality remains unchanged.
Showing 1 changed file with 171 additions and 159 deletions
libqpdf/QPDFJob.cc
... ... @@ -1389,6 +1389,167 @@ QPDFJob::json_schema(int json_version, std::set<std::string>* keys)
1389 1389 // mismatch is a bug in qpdf. This helps to enforce our policy of consistently providing a known
1390 1390 // structure where every documented key will always be present, which makes it easier to consume
1391 1391 // our JSON. This is discussed in more depth in the manual.
  1392 +
  1393 + static constexpr const char* objects_schema_v1 = R"({
  1394 + "<n n R|trailer>": "json representation of object"
  1395 + })";
  1396 +
  1397 + static constexpr const char* objectinfo_schema_v1 = R"({
  1398 + "<object-id>": {
  1399 + "stream": {
  1400 + "filter": "if stream, its filters, otherwise null",
  1401 + "is": "whether the object is a stream",
  1402 + "length": "if stream, its length, otherwise null"
  1403 + }
  1404 + }
  1405 + })";
  1406 +
  1407 + static constexpr const char* qpdf_schema = R"([{
  1408 + "jsonversion": "numeric JSON version",
  1409 + "pdfversion": "PDF version as x.y",
  1410 + "pushedinheritedpageresources": "whether inherited attributes were pushed to the page level",
  1411 + "calledgetallpages": "whether getAllPages was called",
  1412 + "maxobjectid": "highest object ID in output, ignored on input"
  1413 + },
  1414 + {
  1415 + "<obj:n n R|trailer>": "json representation of object"
  1416 + }])";
  1417 +
  1418 + static constexpr const char* pages_schema = R"([
  1419 + {
  1420 + "contents": [
  1421 + "reference to each content stream"
  1422 + ],
  1423 + "images": [
  1424 + {
  1425 + "bitspercomponent": "bits per component",
  1426 + "colorspace": "color space",
  1427 + "decodeparms": [
  1428 + "decode parameters for image data"
  1429 + ],
  1430 + "filter": [
  1431 + "filters applied to image data"
  1432 + ],
  1433 + "filterable": "whether image data can be decoded using the decode level qpdf was invoked with",
  1434 + "height": "image height",
  1435 + "name": "name of image in XObject table",
  1436 + "object": "reference to image stream",
  1437 + "width": "image width"
  1438 + }
  1439 + ],
  1440 + "label": "page label dictionary, or null if none",
  1441 + "object": "reference to original page object",
  1442 + "outlines": [
  1443 + {
  1444 + "dest": "outline destination dictionary",
  1445 + "object": "reference to outline that targets this page",
  1446 + "title": "outline title"
  1447 + }
  1448 + ],
  1449 + "pageposfrom1": "position of page in document numbering from 1"
  1450 + }
  1451 + ])";
  1452 +
  1453 + static constexpr const char* pagelabels_schema = R"([
  1454 + {
  1455 + "index": "starting page position starting from zero",
  1456 + "label": "page label dictionary"
  1457 + }
  1458 + ])";
  1459 +
  1460 + static constexpr const char* outlines_schema = R"([
  1461 + {
  1462 + "dest": "outline destination dictionary",
  1463 + "destpageposfrom1": "position of destination page in document numbered from 1; null if not known",
  1464 + "kids": "array of descendent outlines",
  1465 + "object": "reference to this outline",
  1466 + "open": "whether the outline is displayed expanded",
  1467 + "title": "outline title"
  1468 + }
  1469 + ])";
  1470 +
  1471 + static constexpr const char* acroform_schema = R"({
  1472 + "fields": [
  1473 + {
  1474 + "alternativename": "alternative name of field -- this is the one usually shown to users",
  1475 + "annotation": {
  1476 + "annotationflags": "annotation flags from /F -- see pdf_annotation_flag_e in qpdf/Constants.h",
  1477 + "appearancestate": "appearance state -- can be used to determine value for checkboxes and radio buttons",
  1478 + "object": "reference to the annotation object"
  1479 + },
  1480 + "choices": "for choices fields, the list of choices presented to the user",
  1481 + "defaultvalue": "default value of field",
  1482 + "fieldflags": "form field flags from /Ff -- see pdf_form_field_flag_e in qpdf/Constants.h",
  1483 + "fieldtype": "field type",
  1484 + "fullname": "full name of field",
  1485 + "ischeckbox": "whether field is a checkbox",
  1486 + "ischoice": "whether field is a list, combo, or dropdown",
  1487 + "isradiobutton": "whether field is a radio button -- buttons in a single group share a parent",
  1488 + "istext": "whether field is a text field",
  1489 + "mappingname": "mapping name of field",
  1490 + "object": "reference to this form field",
  1491 + "pageposfrom1": "position of containing page numbered from 1",
  1492 + "parent": "reference to this field's parent",
  1493 + "partialname": "partial name of field",
  1494 + "quadding": "field quadding -- number indicating left, center, or right",
  1495 + "value": "value of field"
  1496 + }
  1497 + ],
  1498 + "hasacroform": "whether the document has interactive forms",
  1499 + "needappearances": "whether the form fields' appearance streams need to be regenerated"
  1500 + })";
  1501 +
  1502 + static constexpr const char* encrypt_schema1 = R"({
  1503 + "capabilities": {
  1504 + "accessibility": "allow extraction for accessibility?",
  1505 + "extract": "allow extraction?",
  1506 + ")";
  1507 +
  1508 + static constexpr const char* encrypt_schema2 = R"(": "allow modifying annotations?",
  1509 + "modify": "allow all modifications?",
  1510 + "modifyassembly": "allow modifying document assembly?",
  1511 + "modifyforms": "allow modifying forms?",
  1512 + "modifyother": "allow other modifications?",
  1513 + "printhigh": "allow high resolution printing?",
  1514 + "printlow": "allow low resolution printing?"
  1515 + },
  1516 + "encrypted": "whether the document is encrypted",
  1517 + "ownerpasswordmatched": "whether supplied password matched owner password; always false for non-encrypted files",
  1518 + "recovereduserpassword": "If the owner password was used to recover the user password, reveal user password; otherwise null",
  1519 + "parameters": {
  1520 + "P": "P value from Encrypt dictionary",
  1521 + "R": "R value from Encrypt dictionary",
  1522 + "V": "V value from Encrypt dictionary",
  1523 + "bits": "encryption key bit length",
  1524 + "filemethod": "encryption method for attachments",
  1525 + "key": "encryption key; will be null unless --show-encryption-key was specified",
  1526 + "method": "overall encryption method: none, mixed, RC4, AESv2, AESv3",
  1527 + "streammethod": "encryption method for streams",
  1528 + "stringmethod": "encryption method for string"
  1529 + },
  1530 + "userpasswordmatched": "whether supplied password matched user password; always false for non-encrypted files"
  1531 + })";
  1532 +
  1533 + static constexpr const char* attachments_schema = R"({
  1534 + "<attachment-key>": {
  1535 + "filespec": "object containing the file spec",
  1536 + "preferredcontents": "most preferred embedded file stream",
  1537 + "preferredname": "most preferred file name",
  1538 + "description": "description of attachment",
  1539 + "names": {
  1540 + "<name-key>": "file name for key"
  1541 + },
  1542 + "streams": {
  1543 + "<stream-key>": {
  1544 + "creationdate": "ISO-8601 creation date or null",
  1545 + "modificationdate": "ISO-8601 modification date or null",
  1546 + "mimetype": "mime type or null",
  1547 + "checksum": "MD5 checksum or null"
  1548 + }
  1549 + }
  1550 + }
  1551 + })";
  1552 +
1392 1553 JSON schema = JSON::makeDictionary();
1393 1554 schema.addDictionaryMember(
1394 1555 "version",
... ... @@ -1408,169 +1569,20 @@ QPDFJob::json_schema(int json_version, std::set&lt;std::string&gt;* keys)
1408 1569 // The list of selectable top-level keys id duplicated in the following places: job.yml,
1409 1570 // QPDFJob::json_schema, and QPDFJob::doJSON.
1410 1571 if (json_version == 1) {
1411   - add_if_want_key("objects", R"({
1412   - "<n n R|trailer>": "json representation of object"
1413   -})");
1414   -
1415   - add_if_want_key("objectinfo", R"({
1416   - "<object-id>": {
1417   - "stream": {
1418   - "filter": "if stream, its filters, otherwise null",
1419   - "is": "whether the object is a stream",
1420   - "length": "if stream, its length, otherwise null"
1421   - }
1422   - }
1423   -})");
1424   -
  1572 + add_if_want_key("objects", objects_schema_v1);
  1573 + add_if_want_key("objectinfo", objectinfo_schema_v1);
1425 1574 } else {
1426   - add_if_want_key("qpdf", R"([{
1427   - "jsonversion": "numeric JSON version",
1428   - "pdfversion": "PDF version as x.y",
1429   - "pushedinheritedpageresources": "whether inherited attributes were pushed to the page level",
1430   - "calledgetallpages": "whether getAllPages was called",
1431   - "maxobjectid": "highest object ID in output, ignored on input"
1432   -},
1433   -{
1434   - "<obj:n n R|trailer>": "json representation of object"
1435   -}])");
1436   - }
1437   - add_if_want_key("pages", R"([
1438   - {
1439   - "contents": [
1440   - "reference to each content stream"
1441   - ],
1442   - "images": [
1443   - {
1444   - "bitspercomponent": "bits per component",
1445   - "colorspace": "color space",
1446   - "decodeparms": [
1447   - "decode parameters for image data"
1448   - ],
1449   - "filter": [
1450   - "filters applied to image data"
1451   - ],
1452   - "filterable": "whether image data can be decoded using the decode level qpdf was invoked with",
1453   - "height": "image height",
1454   - "name": "name of image in XObject table",
1455   - "object": "reference to image stream",
1456   - "width": "image width"
1457   - }
1458   - ],
1459   - "label": "page label dictionary, or null if none",
1460   - "object": "reference to original page object",
1461   - "outlines": [
1462   - {
1463   - "dest": "outline destination dictionary",
1464   - "object": "reference to outline that targets this page",
1465   - "title": "outline title"
1466   - }
1467   - ],
1468   - "pageposfrom1": "position of page in document numbering from 1"
1469   - }
1470   -])");
1471   -
1472   - add_if_want_key("pagelabels", R"([
1473   - {
1474   - "index": "starting page position starting from zero",
1475   - "label": "page label dictionary"
1476   - }
1477   -])");
1478   -
1479   - add_if_want_key("outlines", R"([
1480   - {
1481   - "dest": "outline destination dictionary",
1482   - "destpageposfrom1": "position of destination page in document numbered from 1; null if not known",
1483   - "kids": "array of descendent outlines",
1484   - "object": "reference to this outline",
1485   - "open": "whether the outline is displayed expanded",
1486   - "title": "outline title"
1487   - }
1488   -])");
1489   -
1490   - add_if_want_key("acroform", R"({
1491   - "fields": [
1492   - {
1493   - "alternativename": "alternative name of field -- this is the one usually shown to users",
1494   - "annotation": {
1495   - "annotationflags": "annotation flags from /F -- see pdf_annotation_flag_e in qpdf/Constants.h",
1496   - "appearancestate": "appearance state -- can be used to determine value for checkboxes and radio buttons",
1497   - "object": "reference to the annotation object"
1498   - },
1499   - "choices": "for choices fields, the list of choices presented to the user",
1500   - "defaultvalue": "default value of field",
1501   - "fieldflags": "form field flags from /Ff -- see pdf_form_field_flag_e in qpdf/Constants.h",
1502   - "fieldtype": "field type",
1503   - "fullname": "full name of field",
1504   - "ischeckbox": "whether field is a checkbox",
1505   - "ischoice": "whether field is a list, combo, or dropdown",
1506   - "isradiobutton": "whether field is a radio button -- buttons in a single group share a parent",
1507   - "istext": "whether field is a text field",
1508   - "mappingname": "mapping name of field",
1509   - "object": "reference to this form field",
1510   - "pageposfrom1": "position of containing page numbered from 1",
1511   - "parent": "reference to this field's parent",
1512   - "partialname": "partial name of field",
1513   - "quadding": "field quadding -- number indicating left, center, or right",
1514   - "value": "value of field"
1515   - }
1516   - ],
1517   - "hasacroform": "whether the document has interactive forms",
1518   - "needappearances": "whether the form fields' appearance streams need to be regenerated"
1519   -})");
  1575 + add_if_want_key("qpdf", qpdf_schema);
  1576 + }
  1577 + add_if_want_key("pages", pages_schema);
  1578 + add_if_want_key("pagelabels", pagelabels_schema);
  1579 + add_if_want_key("outlines", outlines_schema);
  1580 + add_if_want_key("acroform", acroform_schema);
1520 1581  
1521 1582 std::string MODIFY_ANNOTATIONS =
1522 1583 (json_version == 1 ? "moddifyannotations" : "modifyannotations");
1523   - add_if_want_key(
1524   - "encrypt",
1525   - R"({
1526   - "capabilities": {
1527   - "accessibility": "allow extraction for accessibility?",
1528   - "extract": "allow extraction?",
1529   - ")" + MODIFY_ANNOTATIONS +
1530   - R"(": "allow modifying annotations?",
1531   - "modify": "allow all modifications?",
1532   - "modifyassembly": "allow modifying document assembly?",
1533   - "modifyforms": "allow modifying forms?",
1534   - "modifyother": "allow other modifications?",
1535   - "printhigh": "allow high resolution printing?",
1536   - "printlow": "allow low resolution printing?"
1537   - },
1538   - "encrypted": "whether the document is encrypted",
1539   - "ownerpasswordmatched": "whether supplied password matched owner password; always false for non-encrypted files",
1540   - "recovereduserpassword": "If the owner password was used to recover the user password, reveal user password; otherwise null",
1541   - "parameters": {
1542   - "P": "P value from Encrypt dictionary",
1543   - "R": "R value from Encrypt dictionary",
1544   - "V": "V value from Encrypt dictionary",
1545   - "bits": "encryption key bit length",
1546   - "filemethod": "encryption method for attachments",
1547   - "key": "encryption key; will be null unless --show-encryption-key was specified",
1548   - "method": "overall encryption method: none, mixed, RC4, AESv2, AESv3",
1549   - "streammethod": "encryption method for streams",
1550   - "stringmethod": "encryption method for string"
1551   - },
1552   - "userpasswordmatched": "whether supplied password matched user password; always false for non-encrypted files"
1553   -})");
1554   -
1555   - add_if_want_key("attachments", R"({
1556   - "<attachment-key>": {
1557   - "filespec": "object containing the file spec",
1558   - "preferredcontents": "most preferred embedded file stream",
1559   - "preferredname": "most preferred file name",
1560   - "description": "description of attachment",
1561   - "names": {
1562   - "<name-key>": "file name for key"
1563   - },
1564   - "streams": {
1565   - "<stream-key>": {
1566   - "creationdate": "ISO-8601 creation date or null",
1567   - "modificationdate": "ISO-8601 modification date or null",
1568   - "mimetype": "mime type or null",
1569   - "checksum": "MD5 checksum or null"
1570   - }
1571   - }
1572   - }
1573   -})");
  1584 + add_if_want_key("encrypt", encrypt_schema1 + MODIFY_ANNOTATIONS + encrypt_schema2);
  1585 + add_if_want_key("attachments", attachments_schema);
1574 1586  
1575 1587 return schema;
1576 1588 }
... ...