Commit 4237a29c9492653e1be869d603e1b5bf87833682

Authored by Jay Berkenbilt
1 parent e57c2581

Refactor Dictionary writing code

Original code was written before we could shallow copy objects, so all
the filtering was done by suppressing the output of certain keys and
replacing them with other keys.  Now we can simplify the code greatly
by modifying shallow copies of dictionaries in place.
include/qpdf/QPDFWriter.hh
... ... @@ -271,7 +271,6 @@ class QPDFWriter
271 271 static int const f_stream = 1 << 0;
272 272 static int const f_filtered = 1 << 1;
273 273 static int const f_in_ostream = 1 << 2;
274   - static int const f_in_extensions = 1 << 3;
275 274  
276 275 enum trailer_e { t_normal, t_lin_first, t_lin_second };
277 276  
... ...
libqpdf/QPDFWriter.cc
... ... @@ -1135,7 +1135,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1135 1135 unsigned int flags, size_t stream_length,
1136 1136 bool compress)
1137 1137 {
1138   - unsigned int child_flags = flags & ~f_stream & ~f_in_extensions;
  1138 + int old_id = object.getObjectID();
  1139 + unsigned int child_flags = flags & ~f_stream;
1139 1140  
1140 1141 std::string indent;
1141 1142 for (int i = 0; i < level; ++i)
... ... @@ -1167,10 +1168,19 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1167 1168 }
1168 1169 else if (object.isDictionary())
1169 1170 {
  1171 + // Make a shallow copy of this object so we can modify it
  1172 + // safely without affecting the original. This code makes
  1173 + // assumptions about things that are made true in
  1174 + // prepareFileForWrite, such as that certain things are direct
  1175 + // objects so that replacing them doesn't leave unreferenced
  1176 + // objects in the output.
  1177 + object = object.shallowCopy();
  1178 +
1170 1179 // Handle special cases for specific dictionaries.
1171 1180  
1172   - // Extensions dictionaries are complicated. We have one of
1173   - // several cases:
  1181 + // Extensions dictionaries.
  1182 +
  1183 + // We have one of several cases:
1174 1184 //
1175 1185 // * We need ADBE
1176 1186 // - We already have Extensions
... ... @@ -1183,16 +1193,16 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1183 1193 // - If it has other things, keep those and remove ADBE
1184 1194 // - We have no extensions: no action required
1185 1195 //
1186   - // We may be in the root dictionary, or we may be inside the
1187   - // extensions dictionary itself. The latter is determined by
1188   - // the presence of the f_in_extensions flag.
  1196 + // Before writing, we guarantee that /Extensions, if present,
  1197 + // is direct through the ADBE dictionary, so we can modify in
  1198 + // place.
1189 1199  
1190 1200 bool is_root = false;
1191 1201 bool have_extensions_other = false;
1192 1202 bool have_extensions_adbe = false;
1193 1203  
1194 1204 QPDFObjectHandle extensions;
1195   - if (object.getObjectID() == pdf.getRoot().getObjectID())
  1205 + if (old_id == pdf.getRoot().getObjectID())
1196 1206 {
1197 1207 is_root = true;
1198 1208 if (object.hasKey("/Extensions") &&
... ... @@ -1201,10 +1211,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1201 1211 extensions = object.getKey("/Extensions");
1202 1212 }
1203 1213 }
1204   - else if (flags & f_in_extensions)
1205   - {
1206   - extensions = object;
1207   - }
  1214 +
1208 1215 if (extensions.isInitialized())
1209 1216 {
1210 1217 std::set<std::string> keys = extensions.getKeys();
... ... @@ -1221,10 +1228,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1221 1228  
1222 1229 bool need_extensions_adbe = (this->final_extension_level > 0);
1223 1230  
1224   - bool write_new_extensions = false;
1225   - bool write_new_adbe = false;
1226   - bool suppress_existing_extensions = false;
1227   - bool suppress_existing_adbe = false;
1228 1231 if (is_root)
1229 1232 {
1230 1233 if (need_extensions_adbe)
... ... @@ -1235,26 +1238,23 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1235 1238 // it here.
1236 1239 QTC::TC("qpdf", "QPDFWriter create Extensions",
1237 1240 this->qdf_mode ? 0 : 1);
1238   - write_new_extensions = true;
1239   - suppress_existing_extensions = true;
1240   - }
1241   - else
1242   - {
1243   - // Preserve existing Extensions and do the work
1244   - // in the extensions dictionary.
  1241 + extensions = QPDFObjectHandle::newDictionary();
  1242 + object.replaceKey("/Extensions", extensions);
1245 1243 }
1246 1244 }
1247 1245 else if (! have_extensions_other)
1248 1246 {
1249 1247 // We have Extensions dictionary and don't want one.
1250   - suppress_existing_extensions = true;
1251 1248 if (have_extensions_adbe)
1252 1249 {
1253 1250 QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
  1251 + object.removeKey("/Extensions");
  1252 + extensions = QPDFObjectHandle(); // uninitialized
1254 1253 }
1255 1254 }
1256 1255 }
1257   - else if (flags & f_in_extensions)
  1256 +
  1257 + if (extensions.isInitialized())
1258 1258 {
1259 1259 QTC::TC("qpdf", "QPDFWriter preserve Extensions");
1260 1260 QPDFObjectHandle adbe = extensions.getKey("/ADBE");
... ... @@ -1272,77 +1272,54 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1272 1272 }
1273 1273 else
1274 1274 {
1275   - suppress_existing_adbe = true;
1276 1275 if (need_extensions_adbe)
1277 1276 {
1278   - write_new_adbe = true;
  1277 + extensions.replaceKey(
  1278 + "/ADBE",
  1279 + QPDFObjectHandle::parse(
  1280 + "<< /BaseVersion /" + this->final_pdf_version +
  1281 + " /ExtensionLevel " +
  1282 + QUtil::int_to_string(this->final_extension_level) +
  1283 + " >>"));
  1284 + }
  1285 + else
  1286 + {
  1287 + QTC::TC("qpdf", "QPDFWriter remove ADBE");
  1288 + extensions.removeKey("/ADBE");
1279 1289 }
1280 1290 }
1281 1291 }
1282 1292  
1283   - writeString("<<");
1284   - writeStringQDF("\n");
  1293 + // Stream dictionaries.
1285 1294  
1286   - if (write_new_extensions || write_new_adbe)
  1295 + if (flags & f_stream)
1287 1296 {
1288   - writeStringQDF(indent);
1289   - writeStringQDF(" ");
1290   - writeStringNoQDF(" ");
1291   - if (write_new_extensions)
1292   - {
1293   - writeString("/Extensions << ");
1294   - }
1295   - writeString("/ADBE << /BaseVersion /");
1296   - writeString(this->final_pdf_version);
1297   - writeString(" /ExtensionLevel ");
1298   - writeString(QUtil::int_to_string(this->final_extension_level));
1299   - writeString(" >>");
1300   - if (write_new_extensions)
  1297 + // Suppress /Length since we will write it manually
  1298 + object.removeKey("/Length");
  1299 +
  1300 + // XXX BUG: /Crypt filters should always be removed.
  1301 + if (flags & f_filtered)
1301 1302 {
1302   - writeString(" >>");
  1303 + object.removeKey("/Filter");
  1304 + object.removeKey("/DecodeParms");
1303 1305 }
1304   - writeStringQDF("\n");
1305 1306 }
1306 1307  
  1308 + writeString("<<");
  1309 + writeStringQDF("\n");
  1310 +
1307 1311 std::set<std::string> keys = object.getKeys();
1308 1312 for (std::set<std::string>::iterator iter = keys.begin();
1309 1313 iter != keys.end(); ++iter)
1310 1314 {
1311   - // I'm not fully clear on /Crypt keys in /DecodeParms. If
1312   - // one is found, we refuse to filter, so we should be
1313   - // safe.
1314 1315 std::string const& key = *iter;
1315   - if ((flags & f_filtered) &&
1316   - ((key == "/Filter") ||
1317   - (key == "/DecodeParms")))
1318   - {
1319   - continue;
1320   - }
1321   - if ((flags & f_stream) && (key == "/Length"))
1322   - {
1323   - continue;
1324   - }
1325   -
1326   - bool is_extensions = (is_root && (key == "/Extensions"));
1327   - if (suppress_existing_extensions && is_extensions)
1328   - {
1329   - QTC::TC("qpdf", "QPDFWriter skip Extensions");
1330   - continue;
1331   - }
1332   - if (suppress_existing_adbe && (key == "/ADBE"))
1333   - {
1334   - QTC::TC("qpdf", "QPDFWriter skip ADBE");
1335   - continue;
1336   - }
1337   -
1338 1316  
1339 1317 writeStringQDF(indent);
1340 1318 writeStringQDF(" ");
1341 1319 writeStringNoQDF(" ");
1342 1320 writeString(QPDF_Name::normalizeName(key));
1343 1321 writeString(" ");
1344   - unparseChild(object.getKey(key), level + 1,
1345   - child_flags | (is_extensions ? f_in_extensions : 0));
  1322 + unparseChild(object.getKey(key), level + 1, child_flags);
1346 1323 writeStringQDF("\n");
1347 1324 }
1348 1325  
... ... @@ -1379,7 +1356,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1379 1356 else if (object.isStream())
1380 1357 {
1381 1358 // Write stream data to a buffer.
1382   - int old_id = object.getObjectID();
1383 1359 int new_id = obj_renumber[old_id];
1384 1360 if (! this->direct_stream_lengths)
1385 1361 {
... ...
qpdf/qpdf.testcov
... ... @@ -249,9 +249,8 @@ QPDFWriter make Extensions direct 0
249 249 QPDFWriter make ADBE direct 1
250 250 QPDFWriter preserve Extensions 0
251 251 QPDFWriter create Extensions 1
252   -QPDFWriter skip ADBE 0
  252 +QPDFWriter remove ADBE 0
253 253 QPDFWriter remove existing Extensions 0
254   -QPDFWriter skip Extensions 0
255 254 QPDFWriter preserve ADBE 0
256 255 QPDF_encryption skip 0x28 0
257 256 QPDF_encrypt crypt array 0
... ...
qpdf/qtest/qpdf.test
... ... @@ -134,7 +134,7 @@ foreach my $input (@ext_inputs)
134 134 "qpdf --static-id" .
135 135 " --$op-version=$version $input a.pdf"},
136 136 {$td->STRING => "", $td->EXIT_STATUS => 0});
137   - $td->runtest("check version information",
  137 + $td->runtest("check version information ($op $version)",
138 138 {$td->COMMAND => "test_driver 34 a.pdf"},
139 139 {$td->FILE => "$base-$op-$version.out",
140 140 $td->EXIT_STATUS => 0},
... ...
qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf
... ... @@ -6,7 +6,10 @@
6 6 1 0 obj
7 7 <<
8 8 /Extensions <<
9   - /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
  9 + /ADBE <<
  10 + /BaseVersion /1.8
  11 + /ExtensionLevel 5
  12 + >>
10 13 >>
11 14 /Pages 2 0 R
12 15 /Type /Catalog
... ... @@ -88,17 +91,17 @@ xref
88 91 0 8
89 92 0000000000 65535 f
90 93 0000000052 00000 n
91   -0000000207 00000 n
92   -0000000316 00000 n
93   -0000000558 00000 n
94   -0000000657 00000 n
95   -0000000703 00000 n
96   -0000000848 00000 n
  94 +0000000223 00000 n
  95 +0000000332 00000 n
  96 +0000000574 00000 n
  97 +0000000673 00000 n
  98 +0000000719 00000 n
  99 +0000000864 00000 n
97 100 trailer <<
98 101 /Root 1 0 R
99 102 /Size 8
100 103 /ID [<e42c124696c09bd2cacaf7196e9c88a0><31415926535897932384626433832795>]
101 104 >>
102 105 startxref
103   -883
  106 +899
104 107 %%EOF
... ...
qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf
... ... @@ -6,7 +6,10 @@
6 6 1 0 obj
7 7 <<
8 8 /Extensions <<
9   - /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
  9 + /ADBE <<
  10 + /BaseVersion /1.8
  11 + /ExtensionLevel 5
  12 + >>
10 13 /Potato <<
11 14 /BaseVersion /3.14159
12 15 /ExtensionLevel 16059
... ... @@ -92,17 +95,17 @@ xref
92 95 0 8
93 96 0000000000 65535 f
94 97 0000000052 00000 n
95   -0000000285 00000 n
96   -0000000394 00000 n
97   -0000000636 00000 n
98   -0000000735 00000 n
99   -0000000781 00000 n
100   -0000000926 00000 n
  98 +0000000301 00000 n
  99 +0000000410 00000 n
  100 +0000000652 00000 n
  101 +0000000751 00000 n
  102 +0000000797 00000 n
  103 +0000000942 00000 n
101 104 trailer <<
102 105 /Root 1 0 R
103 106 /Size 8
104 107 /ID [<484577389048fa45fc00a1f5b434efa5><31415926535897932384626433832795>]
105 108 >>
106 109 startxref
107   -961
  110 +977
108 111 %%EOF
... ...
qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf
... ... @@ -5,7 +5,12 @@
5 5 %% Original object ID: 1 0
6 6 1 0 obj
7 7 <<
8   - /Extensions << /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
  8 + /Extensions <<
  9 + /ADBE <<
  10 + /BaseVersion /1.8
  11 + /ExtensionLevel 5
  12 + >>
  13 + >>
9 14 /Pages 2 0 R
10 15 /Type /Catalog
11 16 >>
... ... @@ -86,17 +91,17 @@ xref
86 91 0 8
87 92 0000000000 65535 f
88 93 0000000052 00000 n
89   -0000000201 00000 n
90   -0000000310 00000 n
91   -0000000552 00000 n
92   -0000000651 00000 n
93   -0000000697 00000 n
94   -0000000842 00000 n
  94 +0000000223 00000 n
  95 +0000000332 00000 n
  96 +0000000574 00000 n
  97 +0000000673 00000 n
  98 +0000000719 00000 n
  99 +0000000864 00000 n
95 100 trailer <<
96 101 /Root 1 0 R
97 102 /Size 8
98 103 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
99 104 >>
100 105 startxref
101   -877
  106 +899
102 107 %%EOF
... ...
qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf
... ... @@ -6,7 +6,10 @@
6 6 1 0 obj
7 7 <<
8 8 /Extensions <<
9   - /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
  9 + /ADBE <<
  10 + /BaseVersion /1.8
  11 + /ExtensionLevel 5
  12 + >>
10 13 /Potato <<
11 14 /BaseVersion /3.14159
12 15 /ExtensionLevel 16059
... ... @@ -92,17 +95,17 @@ xref
92 95 0 8
93 96 0000000000 65535 f
94 97 0000000052 00000 n
95   -0000000285 00000 n
96   -0000000394 00000 n
97   -0000000636 00000 n
98   -0000000735 00000 n
99   -0000000781 00000 n
100   -0000000926 00000 n
  98 +0000000301 00000 n
  99 +0000000410 00000 n
  100 +0000000652 00000 n
  101 +0000000751 00000 n
  102 +0000000797 00000 n
  103 +0000000942 00000 n
101 104 trailer <<
102 105 /Root 1 0 R
103 106 /Size 8
104 107 /ID [<369e89600ee1a6c4c7e73533610180c2><31415926535897932384626433832795>]
105 108 >>
106 109 startxref
107   -961
  110 +977
108 111 %%EOF
... ...