Commit fbcbb781d79a98dfc74df7fc10693ee00606e953

Authored by m-holger
1 parent dbd437e2

Refactor ADBE handling logic in QPDFWriter.

Reorganized and cleaned up the logic for managing ADBE entries in the /Extensions dictionary, improving clarity and maintainability. Adjusted object shallow copying to avoid unnecessary copying.
Showing 1 changed file with 81 additions and 83 deletions
libqpdf/QPDFWriter.cc
... ... @@ -1375,93 +1375,84 @@ QPDFWriter::unparseObject(
1375 1375 writeString(indent);
1376 1376 writeString("]");
1377 1377 } else if (tc == ::ot_dictionary) {
1378   - // Make a shallow copy of this object so we can modify it safely without affecting the
1379   - // original. This code has logic to skip certain keys in agreement with prepareFileForWrite
1380   - // and with skip_stream_parameters so that replacing them doesn't leave unreferenced objects
1381   - // in the output. We can use unsafeShallowCopy here because all we are doing is removing or
1382   - // replacing top-level keys.
1383   - object = object.unsafeShallowCopy();
1384   -
1385 1378 // Handle special cases for specific dictionaries.
1386 1379  
1387   - // Extensions dictionaries.
1388   -
1389   - // We have one of several cases:
1390   - //
1391   - // * We need ADBE
1392   - // - We already have Extensions
1393   - // - If it has the right ADBE, preserve it
1394   - // - Otherwise, replace ADBE
1395   - // - We don't have Extensions: create one from scratch
1396   - // * We don't want ADBE
1397   - // - We already have Extensions
1398   - // - If it only has ADBE, remove it
1399   - // - If it has other things, keep those and remove ADBE
1400   - // - We have no extensions: no action required
1401   - //
1402   - // Before writing, we guarantee that /Extensions, if present, is direct through the ADBE
1403   - // dictionary, so we can modify in place.
1404   -
1405   - const bool is_root = (old_og == m->root_og);
1406   - bool have_extensions_other = false;
1407   - bool have_extensions_adbe = false;
1408   -
1409   - QPDFObjectHandle extensions;
1410   - if (is_root) {
1411   - if (object.hasKey("/Extensions") && object.getKey("/Extensions").isDictionary()) {
1412   - extensions = object.getKey("/Extensions");
1413   - }
1414   - }
1415   -
1416   - if (extensions) {
1417   - std::set<std::string> keys = extensions.getKeys();
1418   - if (keys.contains("/ADBE")) {
1419   - have_extensions_adbe = true;
1420   - keys.erase("/ADBE");
1421   - }
1422   - if (!keys.empty()) {
1423   - have_extensions_other = true;
1424   - }
1425   - }
  1380 + if (old_og == m->root_og) {
  1381 + // Extensions dictionaries.
  1382 +
  1383 + // We have one of several cases:
  1384 + //
  1385 + // * We need ADBE
  1386 + // - We already have Extensions
  1387 + // - If it has the right ADBE, preserve it
  1388 + // - Otherwise, replace ADBE
  1389 + // - We don't have Extensions: create one from scratch
  1390 + // * We don't want ADBE
  1391 + // - We already have Extensions
  1392 + // - If it only has ADBE, remove it
  1393 + // - If it has other things, keep those and remove ADBE
  1394 + // - We have no extensions: no action required
  1395 + //
  1396 + // Before writing, we guarantee that /Extensions, if present, is direct through the ADBE
  1397 + // dictionary, so we can modify in place.
  1398 +
  1399 + auto extensions = object.getKey("/Extensions");
  1400 + const bool has_extensions = extensions.isDictionary();
  1401 + const bool need_extensions_adbe = m->final_extension_level > 0;
  1402 +
  1403 + if (has_extensions || need_extensions_adbe) {
  1404 + // Make a shallow copy of this object so we can modify it safely without affecting
  1405 + // the original. This code has logic to skip certain keys in agreement with
  1406 + // prepareFileForWrite and with skip_stream_parameters so that replacing them
  1407 + // doesn't leave unreferenced objects in the output. We can use unsafeShallowCopy
  1408 + // here because all we are doing is removing or replacing top-level keys.
  1409 + object = object.unsafeShallowCopy();
  1410 + if (!has_extensions) {
  1411 + extensions = QPDFObjectHandle();
  1412 + }
1426 1413  
1427   - bool need_extensions_adbe = (m->final_extension_level > 0);
  1414 + const bool have_extensions_adbe = extensions && extensions.hasKey("/ADBE");
  1415 + const bool have_extensions_other =
  1416 + extensions && extensions.getKeys().size() > (have_extensions_adbe ? 1u : 0u);
1428 1417  
1429   - if (is_root) {
1430   - if (need_extensions_adbe) {
1431   - if (!(have_extensions_other || have_extensions_adbe)) {
1432   - // We need Extensions and don't have it. Create it here.
1433   - QTC::TC("qpdf", "QPDFWriter create Extensions", m->qdf_mode ? 0 : 1);
1434   - extensions = object.replaceKeyAndGetNew(
1435   - "/Extensions", QPDFObjectHandle::newDictionary());
1436   - }
1437   - } else if (!have_extensions_other) {
1438   - // We have Extensions dictionary and don't want one.
1439   - if (have_extensions_adbe) {
1440   - QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
1441   - object.removeKey("/Extensions");
1442   - extensions = QPDFObjectHandle(); // uninitialized
  1418 + if (need_extensions_adbe) {
  1419 + if (!(have_extensions_other || have_extensions_adbe)) {
  1420 + // We need Extensions and don't have it. Create it here.
  1421 + QTC::TC("qpdf", "QPDFWriter create Extensions", m->qdf_mode ? 0 : 1);
  1422 + extensions = object.replaceKeyAndGetNew(
  1423 + "/Extensions", QPDFObjectHandle::newDictionary());
  1424 + }
  1425 + } else if (!have_extensions_other) {
  1426 + // We have Extensions dictionary and don't want one.
  1427 + if (have_extensions_adbe) {
  1428 + QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
  1429 + object.removeKey("/Extensions");
  1430 + extensions = QPDFObjectHandle(); // uninitialized
  1431 + }
1443 1432 }
1444   - }
1445   - }
1446 1433  
1447   - if (extensions) {
1448   - QTC::TC("qpdf", "QPDFWriter preserve Extensions");
1449   - QPDFObjectHandle adbe = extensions.getKey("/ADBE");
1450   - if (adbe.isDictionary() &&
1451   - adbe.getKey("/BaseVersion").isNameAndEquals("/" + m->final_pdf_version) &&
1452   - adbe.getKey("/ExtensionLevel").isInteger() &&
1453   - (adbe.getKey("/ExtensionLevel").getIntValue() == m->final_extension_level)) {
1454   - QTC::TC("qpdf", "QPDFWriter preserve ADBE");
1455   - } else {
1456   - if (need_extensions_adbe) {
1457   - extensions.replaceKey(
1458   - "/ADBE",
1459   - QPDFObjectHandle::parse(
1460   - "<< /BaseVersion /" + m->final_pdf_version + " /ExtensionLevel " +
1461   - std::to_string(m->final_extension_level) + " >>"));
1462   - } else {
1463   - QTC::TC("qpdf", "QPDFWriter remove ADBE");
1464   - extensions.removeKey("/ADBE");
  1434 + if (extensions) {
  1435 + QTC::TC("qpdf", "QPDFWriter preserve Extensions");
  1436 + QPDFObjectHandle adbe = extensions.getKey("/ADBE");
  1437 + if (adbe.isDictionary() &&
  1438 + adbe.getKey("/BaseVersion").isNameAndEquals("/" + m->final_pdf_version) &&
  1439 + adbe.getKey("/ExtensionLevel").isInteger() &&
  1440 + (adbe.getKey("/ExtensionLevel").getIntValue() ==
  1441 + m->final_extension_level)) {
  1442 + QTC::TC("qpdf", "QPDFWriter preserve ADBE");
  1443 + } else {
  1444 + if (need_extensions_adbe) {
  1445 + extensions.replaceKey(
  1446 + "/ADBE",
  1447 + QPDFObjectHandle::parse(
  1448 + "<< /BaseVersion /" + m->final_pdf_version +
  1449 + " /ExtensionLevel " + std::to_string(m->final_extension_level) +
  1450 + " >>"));
  1451 + } else {
  1452 + QTC::TC("qpdf", "QPDFWriter remove ADBE");
  1453 + extensions.removeKey("/ADBE");
  1454 + }
  1455 + }
1465 1456 }
1466 1457 }
1467 1458 }
... ... @@ -1470,6 +1461,14 @@ QPDFWriter::unparseObject(
1470 1461  
1471 1462 if (flags & f_stream) {
1472 1463 // Suppress /Length since we will write it manually
  1464 +
  1465 + // Make a shallow copy of this object so we can modify it safely without affecting the
  1466 + // original. This code has logic to skip certain keys in agreement with
  1467 + // prepareFileForWrite and with skip_stream_parameters so that replacing them doesn't
  1468 + // leave unreferenced objects in the output. We can use unsafeShallowCopy here because
  1469 + // all we are doing is removing or replacing top-level keys.
  1470 + object = object.unsafeShallowCopy();
  1471 +
1473 1472 object.removeKey("/Length");
1474 1473  
1475 1474 // If /DecodeParms is an empty list, remove it.
... ... @@ -1480,8 +1479,7 @@ QPDFWriter::unparseObject(
1480 1479 }
1481 1480  
1482 1481 if (flags & f_filtered) {
1483   - // We will supply our own filter and decode
1484   - // parameters.
  1482 + // We will supply our own filter and decode parameters.
1485 1483 object.removeKey("/Filter");
1486 1484 object.removeKey("/DecodeParms");
1487 1485 } else {
... ...