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,6 +1389,167 @@ QPDFJob::json_schema(int json_version, std::set<std::string>* keys)
1389 // mismatch is a bug in qpdf. This helps to enforce our policy of consistently providing a known 1389 // mismatch is a bug in qpdf. This helps to enforce our policy of consistently providing a known
1390 // structure where every documented key will always be present, which makes it easier to consume 1390 // structure where every documented key will always be present, which makes it easier to consume
1391 // our JSON. This is discussed in more depth in the manual. 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 JSON schema = JSON::makeDictionary(); 1553 JSON schema = JSON::makeDictionary();
1393 schema.addDictionaryMember( 1554 schema.addDictionaryMember(
1394 "version", 1555 "version",
@@ -1408,169 +1569,20 @@ QPDFJob::json_schema(int json_version, std::set&lt;std::string&gt;* keys) @@ -1408,169 +1569,20 @@ QPDFJob::json_schema(int json_version, std::set&lt;std::string&gt;* keys)
1408 // The list of selectable top-level keys id duplicated in the following places: job.yml, 1569 // The list of selectable top-level keys id duplicated in the following places: job.yml,
1409 // QPDFJob::json_schema, and QPDFJob::doJSON. 1570 // QPDFJob::json_schema, and QPDFJob::doJSON.
1410 if (json_version == 1) { 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 } else { 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 std::string MODIFY_ANNOTATIONS = 1582 std::string MODIFY_ANNOTATIONS =
1522 (json_version == 1 ? "moddifyannotations" : "modifyannotations"); 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 return schema; 1587 return schema;
1576 } 1588 }