Merge optimize

This commit is contained in:
James R. Barlow
2018-05-10 21:05:32 -07:00
11 changed files with 890 additions and 247 deletions
+68 -14
View File
@@ -27,8 +27,8 @@ If you want to adjust the amount of time spent on OCR, change ``--tesseract-time
.. code-block:: bash
# Allow 300 seconds for OCR; skip any page larger than 50 megapixels
ocrmypdf --tesseract-timeout 300 --skip-big 50 bigfile.pdf output.pdf
# Allow 300 seconds for OCR; skip any page larger than 50 megapixels
ocrmypdf --tesseract-timeout 300 --skip-big 50 bigfile.pdf output.pdf
Overriding default tesseract
""""""""""""""""""""""""""""
@@ -39,20 +39,20 @@ Some relevant environment variables that influence Tesseract's behavior include:
.. envvar:: TESSDATA_PREFIX
Overrides the path to Tesseract's data files. This can allow simultaneous installation of the "best" and "fast" training data sets. OCRmyPDF does not manage this environment variable.
Overrides the path to Tesseract's data files. This can allow simultaneous installation of the "best" and "fast" training data sets. OCRmyPDF does not manage this environment variable.
.. envvar:: OMP_THREAD_LIMIT
Controls the number of threads Tesseract will use. OCRmyPDF will manage this environment if it is not already set. (Currently, it will set it to 1 because this gives the best results in testing.)
Controls the number of threads Tesseract will use. OCRmyPDF will manage this environment if it is not already set. (Currently, it will set it to 1 because this gives the best results in testing.)
For example, if you are testing tesseract 4.00 and don't wish to use an existing tesseract 3.04 installation, you can launch OCRmyPDF as follows:
.. code-block:: bash
env \
PATH=/home/user/src/tesseract4/api:$PATH \
TESSDATA_PREFIX=/home/user/src/tesseract4 \
ocrmypdf --tesseract-oem 2 input.pdf output.pdf
env \
PATH=/home/user/src/tesseract4/api:$PATH \
TESSDATA_PREFIX=/home/user/src/tesseract4 \
ocrmypdf --tesseract-oem 2 input.pdf output.pdf
In this example ``TESSDATA_PREFIX`` directs Tesseract 4.0 to use LSTM training data. ``--tesseract-oem 1`` requests tesseract 4.0's new LSTM engine. (Tesseract 4.0 only.)
@@ -80,19 +80,19 @@ Create a file named "no-dict.cfg" with these contents:
::
load_system_dawg 0
language_model_penalty_non_dict_word 0
language_model_penalty_non_freq_dict_word 0
load_system_dawg 0
language_model_penalty_non_dict_word 0
language_model_penalty_non_freq_dict_word 0
then run ocrmypdf as follows (along with any other desired arguments):
.. code-block:: bash
ocrmypdf --tesseract-config no-dict.cfg input.pdf output.pdf
ocrmypdf --tesseract-config no-dict.cfg input.pdf output.pdf
.. warning::
Some combinations of control parameters will break Tesseract or break assumptions that OCRmyPDF makes about Tesseract's output.
Some combinations of control parameters will break Tesseract or break assumptions that OCRmyPDF makes about Tesseract's output.
Changing the PDF renderer
@@ -132,4 +132,58 @@ This works in all versions of Tesseract.
The ``tesseract`` renderer
""""""""""""""""""""""""""
The ``tesseract`` renderer was removed. OCRmyPDF's new approach to text layer grafting makes it functionally equivalent to ``sandwich``.
The ``tesseract`` renderer was removed. OCRmyPDF's new approach to text layer grafting makes it functionally equivalent to ``sandwich``.
Return code policy
------------------
OCRmyPDF writes all messages to ``stderr``. ``stdout`` is reserved for piping
output files. ``stdin`` is reserved for piping input files.
The return codes generated by the OCRmyPDF are considered part of the stable
user interface.
.. list-table:: Return codes
:widths: 5 35 60
:header-rows: 1
* - Code
- Name
- Interpretation
* - 0
- ``ocrmypdf.exceptions.ExitCode.ok``
- Everything worked as expected.
* - 1
- ``ocrmypdf.exceptions.ExitCode.bad_args``
- Invalid arguments, exited with an error.
* - 2
- ``ocrmypdf.exceptions.ExitCode.input_file``
- The input file does not seem to be a valid PDF.
* - 3
- ``ocrmypdf.exceptions.missing_dependency``
- An external program required by OCRmyPDF is missing.
* - 4
- ``ocrmypdf.exceptions.invalid_output_pdf``
- An output file was created, but it does not seem to be a valid PDF or
PDF/A. The file will be available.
* - 5
- ``ocrmypdf.exceptions.file_access_error``
- The user running OCRmyPDF does not have sufficient permissions to read the input file and write the output file.
* - 6
- ``ocrmypdf.exceptions.already_done_ocr``
- The file already appears to contain text so it may not need OCR. See output message.
* - 7
- ``ocrmypdf.exceptions.child_process_error``
- An error occurred in an external program (child process) and OCRmyPDF cannot continue.
* - 8
- ``ocrmypdf.exceptions.encrypted_pdf``
- The input PDF is encrypted. OCRmyPDF does not read encrypted PDFs. Use another program such as ``qpdf`` to remove encryption.
* - 9
- ``ocrmypdf.exceptions.invalid_config``
- A custom configuration file was forwarded to Tesseract using ``--tesseract-config``, and Tesseract rejected this file.
* - 15
- ``ocrmypdf.exceptions.other_error``
- Some other error occurred.
* - 130
- ``ocrmypdf.exceptions.ctrl_c``
- The program was interrupted by pressing Ctrl+C.
-1
View File
@@ -198,4 +198,3 @@ The `Image processing`_ features can improve OCR quality.
Rotating pages and deskewing helps to ensure that the page orientation is correct before OCR begins. Removing the background and/or cleaning the page can also improve results. The ``--oversample DPI`` argument can be specified to resample images to higher resolution before attempting OCR; this can improve results as well.
OCR quality will suffer if the resolution of input images is not correct (since the range of pixel sizes that will be checked for possible fonts will also be incorrect).
+210 -222
View File
@@ -4,401 +4,389 @@
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: Pipeline: Pages: 1 -->
<svg width="1504pt" height="1219pt"
viewBox="0.00 0.00 1504.00 1219.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1215.1977)">
<svg width="1484pt" height="1277pt"
viewBox="0.00 0.00 1484.00 1277.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1273.1977)">
<title>Pipeline:</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1215.1977 1500,-1215.1977 1500,4 -4,4"/>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1273.1977 1480,-1273.1977 1480,4 -4,4"/>
<g id="clust1" class="cluster">
<title>clustertasks</title>
<polygon fill="none" stroke="#000000" points="8,-8 8,-1203.1977 1488,-1203.1977 1488,-8 8,-8"/>
<text text-anchor="middle" x="748" y="-1175.1977" font-family="Times,serif" font-size="30.00" fill="#ff3232">Pipeline:</text>
<polygon fill="none" stroke="#000000" points="8,-8 8,-1261.1977 1468,-1261.1977 1468,-8 8,-8"/>
<text text-anchor="middle" x="738" y="-1233.1977" font-family="Times,serif" font-size="30.00" fill="#ff3232">Pipeline:</text>
</g>
<!-- t0 -->
<g id="node1" class="node">
<title>t0</title>
<polygon fill="#efa03b" stroke="#000000" points="1143.7053,-1157.1977 934.2947,-1157.1977 930.2947,-1153.1977 930.2947,-1121.1977 1139.7053,-1121.1977 1143.7053,-1125.1977 1143.7053,-1157.1977"/>
<polyline fill="none" stroke="#000000" points="1139.7053,-1153.1977 930.2947,-1153.1977 "/>
<polyline fill="none" stroke="#000000" points="1139.7053,-1153.1977 1139.7053,-1121.1977 "/>
<polyline fill="none" stroke="#000000" points="1139.7053,-1153.1977 1143.7053,-1157.1977 "/>
<text text-anchor="middle" x="1037" y="-1133.1977" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.triage</text>
<polygon fill="#efa03b" stroke="#000000" points="1215.7053,-1215.1977 1006.2947,-1215.1977 1002.2947,-1211.1977 1002.2947,-1179.1977 1211.7053,-1179.1977 1215.7053,-1183.1977 1215.7053,-1215.1977"/>
<polyline fill="none" stroke="#000000" points="1211.7053,-1211.1977 1002.2947,-1211.1977 "/>
<polyline fill="none" stroke="#000000" points="1211.7053,-1211.1977 1211.7053,-1179.1977 "/>
<polyline fill="none" stroke="#000000" points="1211.7053,-1211.1977 1215.7053,-1215.1977 "/>
<text text-anchor="middle" x="1109" y="-1191.1977" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.triage</text>
</g>
<!-- t1 -->
<g id="node2" class="node">
<title>t1</title>
<polygon fill="#efa03b" stroke="#000000" points="1208.0433,-1099.1977 869.9567,-1099.1977 865.9567,-1095.1977 865.9567,-1063.1977 1204.0433,-1063.1977 1208.0433,-1067.1977 1208.0433,-1099.1977"/>
<polyline fill="none" stroke="#000000" points="1204.0433,-1095.1977 865.9567,-1095.1977 "/>
<polyline fill="none" stroke="#000000" points="1204.0433,-1095.1977 1204.0433,-1063.1977 "/>
<polyline fill="none" stroke="#000000" points="1204.0433,-1095.1977 1208.0433,-1099.1977 "/>
<text text-anchor="middle" x="1037" y="-1075.1977" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.repair_and_parse_pdf</text>
<polygon fill="#efa03b" stroke="#000000" points="1280.0433,-1157.1977 941.9567,-1157.1977 937.9567,-1153.1977 937.9567,-1121.1977 1276.0433,-1121.1977 1280.0433,-1125.1977 1280.0433,-1157.1977"/>
<polyline fill="none" stroke="#000000" points="1276.0433,-1153.1977 937.9567,-1153.1977 "/>
<polyline fill="none" stroke="#000000" points="1276.0433,-1153.1977 1276.0433,-1121.1977 "/>
<polyline fill="none" stroke="#000000" points="1276.0433,-1153.1977 1280.0433,-1157.1977 "/>
<text text-anchor="middle" x="1109" y="-1133.1977" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.repair_and_parse_pdf</text>
</g>
<!-- t0&#45;&gt;t1 -->
<g id="edge1" class="edge">
<title>t0&#45;&gt;t1</title>
<path fill="none" stroke="#0044a0" d="M1037,-1121.1641C1037,-1117.4895 1037,-1113.5395 1037,-1109.604"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1040.5001,-1109.4199 1037,-1099.4199 1033.5001,-1109.42 1040.5001,-1109.4199"/>
<path fill="none" stroke="#0044a0" d="M1109,-1179.1641C1109,-1175.4895 1109,-1171.5395 1109,-1167.604"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1112.5001,-1167.4199 1109,-1157.4199 1105.5001,-1167.42 1112.5001,-1167.4199"/>
</g>
<!-- t2 -->
<g id="node3" class="node">
<title>t2</title>
<polygon fill="#efa03b" stroke="#000000" points="1127.0926,-1020.2732 882,-1037.1731 636.9074,-1020.2732 637.1362,-992.9285 1126.8638,-992.9285 1127.0926,-1020.2732"/>
<polygon fill="none" stroke="#000000" points="1131.1249,-1024.0021 882,-1041.1801 632.8751,-1024.0021 633.1685,-988.931 1130.8315,-988.931 1131.1249,-1024.0021"/>
<text text-anchor="middle" x="882" y="-1006.7153" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.marker_pages</text>
<polygon fill="#efa03b" stroke="#000000" points="1030.0926,-1078.2732 785,-1095.1731 539.9074,-1078.2732 540.1362,-1050.9285 1029.8638,-1050.9285 1030.0926,-1078.2732"/>
<polygon fill="none" stroke="#000000" points="1034.1249,-1082.0021 785,-1099.1801 535.8751,-1082.0021 536.1685,-1046.931 1033.8315,-1046.931 1034.1249,-1082.0021"/>
<text text-anchor="middle" x="785" y="-1064.7153" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.marker_pages</text>
</g>
<!-- t1&#45;&gt;t2 -->
<g id="edge2" class="edge">
<title>t1&#45;&gt;t2</title>
<path fill="none" stroke="#0044a0" d="M995.8698,-1063.0255C981.0253,-1056.4668 963.8784,-1048.891 947.4691,-1041.641"/>
<polygon fill="#0044a0" stroke="#0044a0" points="948.4773,-1038.26 937.9158,-1037.4201 945.6483,-1044.663 948.4773,-1038.26"/>
<path fill="none" stroke="#0044a0" d="M1023.449,-1121.1152C984.5695,-1112.8974 938.0675,-1103.0685 896.4957,-1094.2816"/>
<polygon fill="#0044a0" stroke="#0044a0" points="897.1066,-1090.8335 886.5989,-1092.1898 895.6589,-1097.6822 897.1066,-1090.8335"/>
</g>
<!-- t16 -->
<g id="node17" class="node">
<title>t16</title>
<polygon fill="#00cc66" stroke="#000000" points="1215.5059,-328.1861 942.4941,-328.1861 938.4941,-324.1861 938.4941,-292.1861 1211.5059,-292.1861 1215.5059,-296.1861 1215.5059,-328.1861"/>
<polyline fill="none" stroke="#000000" points="1211.5059,-324.1861 938.4941,-324.1861 "/>
<polyline fill="none" stroke="#000000" points="1211.5059,-324.1861 1211.5059,-292.1861 "/>
<polyline fill="none" stroke="#000000" points="1211.5059,-324.1861 1215.5059,-328.1861 "/>
<text text-anchor="middle" x="1077" y="-304.1861" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.weave_layers</text>
</g>
<!-- t1&#45;&gt;t16 -->
<g id="edge24" class="edge">
<title>t1&#45;&gt;t16</title>
<path fill="none" stroke="#0044a0" d="M1097.2373,-1120.9576C1077.987,-1088.7184 1043.7317,-1019.7372 1063,-963.2656 1106.87,-834.6914 1482.8098,-573.3183 1409,-459.2656 1362.7595,-387.8137 1273.2613,-350.2799 1198.8305,-330.7528"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1199.4555,-327.3005 1188.9015,-328.2301 1197.7316,-334.0849 1199.4555,-327.3005"/>
</g>
<!-- t17 -->
<g id="node17" class="node">
<g id="node18" class="node">
<title>t17</title>
<polygon fill="#00cc66" stroke="#000000" points="762.5059,-270.1861 489.4941,-270.1861 485.4941,-266.1861 485.4941,-234.1861 758.5059,-234.1861 762.5059,-238.1861 762.5059,-270.1861"/>
<polyline fill="none" stroke="#000000" points="758.5059,-266.1861 485.4941,-266.1861 "/>
<polyline fill="none" stroke="#000000" points="758.5059,-266.1861 758.5059,-234.1861 "/>
<polyline fill="none" stroke="#000000" points="758.5059,-266.1861 762.5059,-270.1861 "/>
<text text-anchor="middle" x="624" y="-246.1861" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.weave_layers</text>
<polygon fill="#efa03b" stroke="#000000" points="1439.5156,-1009.7492 1076.4844,-1009.7492 1072.4844,-1005.7492 1072.4844,-973.7492 1435.5156,-973.7492 1439.5156,-977.7492 1439.5156,-1009.7492"/>
<polyline fill="none" stroke="#000000" points="1435.5156,-1005.7492 1072.4844,-1005.7492 "/>
<polyline fill="none" stroke="#000000" points="1435.5156,-1005.7492 1435.5156,-973.7492 "/>
<polyline fill="none" stroke="#000000" points="1435.5156,-1005.7492 1439.5156,-1009.7492 "/>
<text text-anchor="middle" x="1256" y="-985.7492" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.generate_postscript_stub</text>
</g>
<!-- t1&#45;&gt;t17 -->
<g id="edge24" class="edge">
<g id="edge25" class="edge">
<title>t1&#45;&gt;t17</title>
<path fill="none" stroke="#0044a0" d="M865.7,-1073.6476C575.1688,-1058.5285 17,-1018.4586 17,-933.7492 17,-933.7492 17,-933.7492 17,-429.2656 17,-368.2377 -17.9719,-335.5198 25,-292.1861 40.6952,-276.3587 302.9615,-263.8206 475.2081,-257.2446"/>
<polygon fill="#0044a0" stroke="#0044a0" points="475.5316,-260.735 485.3919,-256.8588 475.2665,-253.74 475.5316,-260.735"/>
<path fill="none" stroke="#0044a0" d="M1127.0251,-1121.1176C1153.0342,-1095.0292 1201.2007,-1046.7157 1230.7429,-1017.0834"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1233.2458,-1019.5302 1237.8275,-1009.9772 1228.2885,-1014.5879 1233.2458,-1019.5302"/>
</g>
<!-- t18 -->
<g id="node19" class="node">
<title>t18</title>
<polygon fill="#efa03b" stroke="#000000" points="1459.5156,-951.7492 1096.4844,-951.7492 1092.4844,-947.7492 1092.4844,-915.7492 1455.5156,-915.7492 1459.5156,-919.7492 1459.5156,-951.7492"/>
<polyline fill="none" stroke="#000000" points="1455.5156,-947.7492 1092.4844,-947.7492 "/>
<polyline fill="none" stroke="#000000" points="1455.5156,-947.7492 1455.5156,-915.7492 "/>
<polyline fill="none" stroke="#000000" points="1455.5156,-947.7492 1459.5156,-951.7492 "/>
<text text-anchor="middle" x="1276" y="-927.7492" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.generate_postscript_stub</text>
<polygon fill="#efa03b" stroke="#000000" points="1070.189,-214.4288 1263,-187.1099 1455.811,-214.4288 1455.631,-258.6317 1070.369,-258.6317 1070.189,-214.4288"/>
<polygon fill="none" stroke="#000000" points="1066.1715,-210.9543 1263,-183.0662 1459.8285,-210.9543 1459.6181,-262.6353 1066.3819,-262.6353 1066.1715,-210.9543"/>
<text text-anchor="middle" x="1263" y="-220.6461" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.metadata_fixup</text>
</g>
<!-- t1&#45;&gt;t18 -->
<g id="edge27" class="edge">
<g id="edge28" class="edge">
<title>t1&#45;&gt;t18</title>
<path fill="none" stroke="#0044a0" d="M1092.3885,-1063.1613C1108.2153,-1057.0913 1125.1799,-1049.674 1140,-1041.1977 1181.1886,-1017.6399 1223.2361,-982.3362 1249.4567,-958.6949"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1252.0343,-961.0813 1257.0676,-951.7583 1247.319,-955.9076 1252.0343,-961.0813"/>
</g>
<!-- t19 -->
<g id="node20" class="node">
<title>t19</title>
<polygon fill="#efa03b" stroke="#000000" points="1015.189,-156.4288 1208,-129.1099 1400.811,-156.4288 1400.631,-200.6317 1015.369,-200.6317 1015.189,-156.4288"/>
<polygon fill="none" stroke="#000000" points="1011.1715,-152.9543 1208,-125.0662 1404.8285,-152.9543 1404.6181,-204.6353 1011.3819,-204.6353 1011.1715,-152.9543"/>
<text text-anchor="middle" x="1208" y="-162.6461" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.metadata_fixup</text>
</g>
<!-- t1&#45;&gt;t19 -->
<g id="edge31" class="edge">
<title>t1&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M1103.146,-1063.1053C1128.1671,-1056.2536 1156.8994,-1048.3763 1183,-1041.1977 1317.9441,-1004.0832 1479,-1073.7042 1479,-933.7492 1479,-933.7492 1479,-933.7492 1479,-335.7258 1479,-283.5318 1462.7563,-264.1212 1420,-234.1861 1405.9522,-224.3508 1390.38,-215.9542 1374.2381,-208.7935"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1375.2471,-205.419 1364.6755,-204.7186 1372.503,-211.8587 1375.2471,-205.419"/>
<path fill="none" stroke="#0044a0" d="M1241.1115,-1121.1192C1311.0076,-1105.2558 1393.5851,-1075.5895 1449,-1020.2329 1458.4922,-1010.7507 1459,-1005.1661 1459,-991.7492 1459,-991.7492 1459,-991.7492 1459,-393.7258 1459,-341.9727 1441.8377,-325.2216 1402,-292.1861 1390.9869,-283.0534 1378.4885,-274.871 1365.6104,-267.6415"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1367.1079,-264.4723 1356.6476,-262.7888 1363.775,-270.6279 1367.1079,-264.4723"/>
</g>
<!-- t3 -->
<g id="node4" class="node">
<title>t3</title>
<polygon fill="#efa03b" stroke="#000000" points="1070.5194,-941.3074 840,-958.2081 609.4806,-941.3074 609.6958,-913.9616 1070.3042,-913.9616 1070.5194,-941.3074"/>
<polygon fill="none" stroke="#000000" points="1074.5491,-945.0204 840,-962.2165 605.4509,-945.0204 605.7267,-909.9639 1074.2733,-909.9639 1074.5491,-945.0204"/>
<text text-anchor="middle" x="840" y="-927.7492" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_or_skip</text>
<polygon fill="#efa03b" stroke="#000000" points="1015.5194,-999.3074 785,-1016.2081 554.4806,-999.3074 554.6958,-971.9616 1015.3042,-971.9616 1015.5194,-999.3074"/>
<polygon fill="none" stroke="#000000" points="1019.5491,-1003.0204 785,-1020.2165 550.4509,-1003.0204 550.7267,-967.9639 1019.2733,-967.9639 1019.5491,-1003.0204"/>
<text text-anchor="middle" x="785" y="-985.7492" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_or_skip</text>
</g>
<!-- t2&#45;&gt;t3 -->
<g id="edge3" class="edge">
<title>t2&#45;&gt;t3</title>
<path fill="none" stroke="#0044a0" d="M869.1704,-988.5938C866.0908,-982.8038 862.7335,-976.4916 859.4285,-970.2775"/>
<polygon fill="#0044a0" stroke="#0044a0" points="862.4479,-968.5011 854.6619,-961.3157 856.2677,-971.7882 862.4479,-968.5011"/>
<path fill="none" stroke="#0044a0" d="M785,-1046.5938C785,-1041.5635 785,-1036.139 785,-1030.7273"/>
<polygon fill="#0044a0" stroke="#0044a0" points="788.5001,-1030.4427 785,-1020.4427 781.5001,-1030.4428 788.5001,-1030.4427"/>
</g>
<!-- t4 -->
<g id="node5" class="node">
<title>t4</title>
<polygon fill="#efa03b" stroke="#000000" points="1160.3166,-883.2656 853.6834,-883.2656 849.6834,-879.2656 849.6834,-847.2656 1156.3166,-847.2656 1160.3166,-851.2656 1160.3166,-883.2656"/>
<polyline fill="none" stroke="#000000" points="1156.3166,-879.2656 849.6834,-879.2656 "/>
<polyline fill="none" stroke="#000000" points="1156.3166,-879.2656 1156.3166,-847.2656 "/>
<polyline fill="none" stroke="#000000" points="1156.3166,-879.2656 1160.3166,-883.2656 "/>
<text text-anchor="middle" x="1005" y="-859.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.rasterize_preview</text>
<polygon fill="#efa03b" stroke="#000000" points="889.3166,-941.2656 582.6834,-941.2656 578.6834,-937.2656 578.6834,-905.2656 885.3166,-905.2656 889.3166,-909.2656 889.3166,-941.2656"/>
<polyline fill="none" stroke="#000000" points="885.3166,-937.2656 578.6834,-937.2656 "/>
<polyline fill="none" stroke="#000000" points="885.3166,-937.2656 885.3166,-905.2656 "/>
<polyline fill="none" stroke="#000000" points="885.3166,-937.2656 889.3166,-941.2656 "/>
<text text-anchor="middle" x="734" y="-917.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.rasterize_preview</text>
</g>
<!-- t3&#45;&gt;t4 -->
<g id="edge4" class="edge">
<title>t3&#45;&gt;t4</title>
<path fill="none" stroke="#0044a0" d="M897.709,-909.797C915.43,-902.4418 934.8333,-894.3884 952.2078,-887.1771"/>
<polygon fill="#0044a0" stroke="#0044a0" points="953.7336,-890.3334 961.628,-883.2672 951.0502,-883.8681 953.7336,-890.3334"/>
<path fill="none" stroke="#0044a0" d="M767.1627,-967.797C762.8396,-961.9919 758.1927,-955.7519 753.7858,-949.8343"/>
<polygon fill="#0044a0" stroke="#0044a0" points="756.3465,-947.4129 747.5666,-941.483 750.7323,-951.5939 756.3465,-947.4129"/>
</g>
<!-- t5 -->
<g id="node6" class="node">
<title>t5</title>
<polygon fill="#efa03b" stroke="#000000" points="1118.0824,-825.2656 859.9176,-825.2656 855.9176,-821.2656 855.9176,-789.2656 1114.0824,-789.2656 1118.0824,-793.2656 1118.0824,-825.2656"/>
<polyline fill="none" stroke="#000000" points="1114.0824,-821.2656 855.9176,-821.2656 "/>
<polyline fill="none" stroke="#000000" points="1114.0824,-821.2656 1114.0824,-789.2656 "/>
<polyline fill="none" stroke="#000000" points="1114.0824,-821.2656 1118.0824,-825.2656 "/>
<text text-anchor="middle" x="987" y="-801.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.orient_page</text>
<polygon fill="#efa03b" stroke="#000000" points="1028.0824,-883.2656 769.9176,-883.2656 765.9176,-879.2656 765.9176,-847.2656 1024.0824,-847.2656 1028.0824,-851.2656 1028.0824,-883.2656"/>
<polyline fill="none" stroke="#000000" points="1024.0824,-879.2656 765.9176,-879.2656 "/>
<polyline fill="none" stroke="#000000" points="1024.0824,-879.2656 1024.0824,-847.2656 "/>
<polyline fill="none" stroke="#000000" points="1024.0824,-879.2656 1028.0824,-883.2656 "/>
<text text-anchor="middle" x="897" y="-859.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.orient_page</text>
</g>
<!-- t3&#45;&gt;t5 -->
<g id="edge6" class="edge">
<title>t3&#45;&gt;t5</title>
<path fill="none" stroke="#0044a0" d="M833.1324,-909.9518C829.2069,-891.0127 827.4191,-864.8559 841,-847.2656 846.6916,-839.8936 860.4225,-833.5377 877.3049,-828.2001"/>
<polygon fill="#0044a0" stroke="#0044a0" points="878.3804,-831.532 886.9772,-825.3397 876.3952,-824.8194 878.3804,-831.532"/>
<path fill="none" stroke="#0044a0" d="M869.1177,-967.8063C880.553,-961.0264 890.81,-952.3527 898,-941.2656 907.042,-927.3226 906.9457,-908.7053 904.5139,-893.387"/>
<polygon fill="#0044a0" stroke="#0044a0" points="907.914,-892.5383 902.5408,-883.4071 901.0469,-893.8961 907.914,-892.5383"/>
</g>
<!-- t4&#45;&gt;t5 -->
<g id="edge5" class="edge">
<title>t4&#45;&gt;t5</title>
<path fill="none" stroke="#0044a0" d="M999.4034,-847.2319C998.2294,-843.4493 996.9649,-839.3747 995.708,-835.3247"/>
<polygon fill="#0044a0" stroke="#0044a0" points="998.962,-834.001 992.6552,-825.4878 992.2765,-836.0759 998.962,-834.001"/>
<path fill="none" stroke="#0044a0" d="M784.6807,-905.2319C801.0865,-899.3943 819.4462,-892.8614 836.4913,-886.7963"/>
<polygon fill="#0044a0" stroke="#0044a0" points="838.1026,-889.938 846.3506,-883.288 835.7559,-883.343 838.1026,-889.938"/>
</g>
<!-- t6 -->
<g id="node7" class="node">
<title>t6</title>
<polygon fill="#efa03b" stroke="#000000" points="1166.7325,-767.2656 791.2675,-767.2656 787.2675,-763.2656 787.2675,-731.2656 1162.7325,-731.2656 1166.7325,-735.2656 1166.7325,-767.2656"/>
<polyline fill="none" stroke="#000000" points="1162.7325,-763.2656 787.2675,-763.2656 "/>
<polyline fill="none" stroke="#000000" points="1162.7325,-763.2656 1162.7325,-731.2656 "/>
<polyline fill="none" stroke="#000000" points="1162.7325,-763.2656 1166.7325,-767.2656 "/>
<text text-anchor="middle" x="977" y="-743.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.rasterize_with_ghostscript</text>
<polygon fill="#efa03b" stroke="#000000" points="1064.7325,-825.2656 689.2675,-825.2656 685.2675,-821.2656 685.2675,-789.2656 1060.7325,-789.2656 1064.7325,-793.2656 1064.7325,-825.2656"/>
<polyline fill="none" stroke="#000000" points="1060.7325,-821.2656 685.2675,-821.2656 "/>
<polyline fill="none" stroke="#000000" points="1060.7325,-821.2656 1060.7325,-789.2656 "/>
<polyline fill="none" stroke="#000000" points="1060.7325,-821.2656 1064.7325,-825.2656 "/>
<text text-anchor="middle" x="875" y="-801.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.rasterize_with_ghostscript</text>
</g>
<!-- t5&#45;&gt;t6 -->
<g id="edge7" class="edge">
<title>t5&#45;&gt;t6</title>
<path fill="none" stroke="#0044a0" d="M983.8908,-789.2319C983.2572,-785.5574 982.5762,-781.6073 981.8976,-777.6719"/>
<polygon fill="#0044a0" stroke="#0044a0" points="985.29,-776.7477 980.1418,-767.4878 978.3918,-777.9371 985.29,-776.7477"/>
<path fill="none" stroke="#0044a0" d="M890.1597,-847.2319C888.6839,-843.3412 887.0909,-839.1417 885.5115,-834.9776"/>
<polygon fill="#0044a0" stroke="#0044a0" points="888.731,-833.5965 881.9119,-825.4878 882.186,-836.0791 888.731,-833.5965"/>
</g>
<!-- t13 -->
<g id="node16" class="node">
<title>t13</title>
<polygon fill="#00cc66" stroke="#000000" points="1117,-457.2656 794.4141,-429.2656 1117,-401.2656 1439.5859,-429.2656 1117,-457.2656"/>
<text text-anchor="middle" x="1117" y="-423.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_image_layer</text>
<polygon fill="#00cc66" stroke="#000000" points="1077,-515.2656 754.4141,-487.2656 1077,-459.2656 1399.5859,-487.2656 1077,-515.2656"/>
<text text-anchor="middle" x="1077" y="-481.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_image_layer</text>
</g>
<!-- t5&#45;&gt;t13 -->
<g id="edge20" class="edge">
<title>t5&#45;&gt;t13</title>
<path fill="none" stroke="#0044a0" d="M1118.1879,-790.6395C1144.3794,-784.8205 1166.7114,-777.1767 1176,-767.2656 1199.3386,-742.3628 1187,-725.3953 1187,-691.2656 1187,-691.2656 1187,-691.2656 1187,-575.2656 1187,-532.368 1197.1859,-517.117 1177,-479.2656 1173.4907,-472.6851 1168.7198,-466.6243 1163.3915,-461.1636"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1165.5374,-458.3781 1155.8461,-454.0967 1160.7522,-463.4872 1165.5374,-458.3781"/>
<path fill="none" stroke="#0044a0" d="M1000.2704,-847.241C1031.2359,-840.6873 1060.5415,-832.9862 1074,-825.2656 1114.0954,-802.2644 1145,-795.4899 1145,-749.2656 1145,-749.2656 1145,-749.2656 1145,-633.2656 1145,-590.368 1154.8687,-575.2844 1135,-537.2656 1131.571,-530.7041 1126.8893,-524.6314 1121.6622,-519.1451"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1123.8989,-516.4405 1114.2626,-512.0367 1119.0494,-521.4885 1123.8989,-516.4405"/>
</g>
<!-- t7 -->
<g id="node8" class="node">
<title>t7</title>
<polygon fill="#efa03b" stroke="#000000" points="958.0801,-709.2656 533.9199,-709.2656 529.9199,-705.2656 529.9199,-673.2656 954.0801,-673.2656 958.0801,-677.2656 958.0801,-709.2656"/>
<polyline fill="none" stroke="#000000" points="954.0801,-705.2656 529.9199,-705.2656 "/>
<polyline fill="none" stroke="#000000" points="954.0801,-705.2656 954.0801,-673.2656 "/>
<polyline fill="none" stroke="#000000" points="954.0801,-705.2656 958.0801,-709.2656 "/>
<text text-anchor="middle" x="744" y="-685.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_remove_background</text>
<polygon fill="#efa03b" stroke="#000000" points="856.0801,-767.2656 431.9199,-767.2656 427.9199,-763.2656 427.9199,-731.2656 852.0801,-731.2656 856.0801,-735.2656 856.0801,-767.2656"/>
<polyline fill="none" stroke="#000000" points="852.0801,-763.2656 427.9199,-763.2656 "/>
<polyline fill="none" stroke="#000000" points="852.0801,-763.2656 852.0801,-731.2656 "/>
<polyline fill="none" stroke="#000000" points="852.0801,-763.2656 856.0801,-767.2656 "/>
<text text-anchor="middle" x="642" y="-743.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_remove_background</text>
</g>
<!-- t6&#45;&gt;t7 -->
<g id="edge8" class="edge">
<title>t6&#45;&gt;t7</title>
<path fill="none" stroke="#0044a0" d="M904.5546,-731.2319C879.7884,-725.067 851.9071,-718.1266 826.4138,-711.7806"/>
<polygon fill="#0044a0" stroke="#0044a0" points="826.95,-708.3073 816.4006,-709.288 825.259,-715.1 826.95,-708.3073"/>
<path fill="none" stroke="#0044a0" d="M802.5546,-789.2319C777.7884,-783.067 749.9071,-776.1266 724.4138,-769.7806"/>
<polygon fill="#0044a0" stroke="#0044a0" points="724.95,-766.3073 714.4006,-767.288 723.259,-773.1 724.95,-766.3073"/>
</g>
<!-- t12 -->
<g id="node15" class="node">
<title>t12</title>
<polygon fill="#efa03b" stroke="#000000" points="784,-535.2656 400.1874,-507.2656 784,-479.2656 1167.8126,-507.2656 784,-535.2656"/>
<text text-anchor="middle" x="784" y="-501.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_visible_page_image</text>
<polygon fill="#efa03b" stroke="#000000" points="742,-593.2656 358.1874,-565.2656 742,-537.2656 1125.8126,-565.2656 742,-593.2656"/>
<text text-anchor="middle" x="742" y="-559.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_visible_page_image</text>
</g>
<!-- t6&#45;&gt;t12 -->
<g id="edge18" class="edge">
<title>t6&#45;&gt;t12</title>
<path fill="none" stroke="#0044a0" d="M978.1978,-731.0693C979.6326,-693.2308 977.2953,-605.5735 930,-557.2656 919.8727,-546.9215 907.5166,-538.7165 894.3237,-532.2085"/>
<polygon fill="#0044a0" stroke="#0044a0" points="895.6224,-528.9537 885.0735,-527.9686 892.7057,-535.3171 895.6224,-528.9537"/>
<path fill="none" stroke="#0044a0" d="M875.1443,-789.1129C874.6867,-773.2956 872.5831,-750.0189 865,-731.2656 844.2991,-680.0716 804.0126,-630.4165 775.1838,-598.9641"/>
<polygon fill="#0044a0" stroke="#0044a0" points="777.4937,-596.3085 768.1202,-591.3701 772.3682,-601.076 777.4937,-596.3085"/>
</g>
<!-- t8 -->
<g id="node9" class="node">
<title>t8</title>
<polygon fill="#efa03b" stroke="#000000" points="930.6759,-651.2656 611.3241,-651.2656 607.3241,-647.2656 607.3241,-615.2656 926.6759,-615.2656 930.6759,-619.2656 930.6759,-651.2656"/>
<polyline fill="none" stroke="#000000" points="926.6759,-647.2656 607.3241,-647.2656 "/>
<polyline fill="none" stroke="#000000" points="926.6759,-647.2656 926.6759,-615.2656 "/>
<polyline fill="none" stroke="#000000" points="926.6759,-647.2656 930.6759,-651.2656 "/>
<text text-anchor="middle" x="769" y="-627.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_deskew</text>
<polygon fill="#efa03b" stroke="#000000" points="732.6759,-709.2656 413.3241,-709.2656 409.3241,-705.2656 409.3241,-673.2656 728.6759,-673.2656 732.6759,-677.2656 732.6759,-709.2656"/>
<polyline fill="none" stroke="#000000" points="728.6759,-705.2656 409.3241,-705.2656 "/>
<polyline fill="none" stroke="#000000" points="728.6759,-705.2656 728.6759,-673.2656 "/>
<polyline fill="none" stroke="#000000" points="728.6759,-705.2656 732.6759,-709.2656 "/>
<text text-anchor="middle" x="571" y="-685.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_deskew</text>
</g>
<!-- t7&#45;&gt;t8 -->
<g id="edge9" class="edge">
<title>t7&#45;&gt;t8</title>
<path fill="none" stroke="#0044a0" d="M751.7731,-673.2319C753.4501,-669.3412 755.2603,-665.1417 757.0552,-660.9776"/>
<polygon fill="#0044a0" stroke="#0044a0" points="760.4014,-662.0565 761.1456,-651.4878 753.9731,-659.2856 760.4014,-662.0565"/>
<path fill="none" stroke="#0044a0" d="M619.9243,-731.2319C613.9709,-726.3685 607.4267,-721.0226 601.115,-715.8666"/>
<polygon fill="#0044a0" stroke="#0044a0" points="603.2653,-713.1037 593.3065,-709.4878 598.8367,-718.5248 603.2653,-713.1037"/>
</g>
<!-- t7&#45;&gt;t12 -->
<g id="edge17" class="edge">
<title>t7&#45;&gt;t12</title>
<path fill="none" stroke="#0044a0" d="M643.9087,-673.2441C622.7156,-667.3649 604.695,-660.0415 598,-651.2656 572.6604,-618.0498 572.5807,-590.4204 598,-557.2656 606.6447,-545.9902 630.0462,-536.6909 657.4547,-529.2983"/>
<polygon fill="#0044a0" stroke="#0044a0" points="658.4879,-532.6465 667.2989,-526.7629 656.7419,-525.8677 658.4879,-532.6465"/>
<path fill="none" stroke="#0044a0" d="M717.0346,-731.1646C726.8757,-725.6884 735.7103,-718.5557 742,-709.2656 763.0291,-678.205 759.4083,-634.0888 752.8082,-602.8898"/>
<polygon fill="#0044a0" stroke="#0044a0" points="756.1481,-601.7976 750.487,-592.842 749.3278,-603.3733 756.1481,-601.7976"/>
</g>
<!-- t9 -->
<g id="node10" class="node">
<title>t9</title>
<polygon fill="#efa03b" stroke="#000000" points="912.8828,-593.2656 611.1172,-593.2656 607.1172,-589.2656 607.1172,-557.2656 908.8828,-557.2656 912.8828,-561.2656 912.8828,-593.2656"/>
<polyline fill="none" stroke="#000000" points="908.8828,-589.2656 607.1172,-589.2656 "/>
<polyline fill="none" stroke="#000000" points="908.8828,-589.2656 908.8828,-557.2656 "/>
<polyline fill="none" stroke="#000000" points="908.8828,-589.2656 912.8828,-593.2656 "/>
<text text-anchor="middle" x="760" y="-569.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_clean</text>
<polygon fill="#efa03b" stroke="#000000" points="712.8828,-651.2656 411.1172,-651.2656 407.1172,-647.2656 407.1172,-615.2656 708.8828,-615.2656 712.8828,-619.2656 712.8828,-651.2656"/>
<polyline fill="none" stroke="#000000" points="708.8828,-647.2656 407.1172,-647.2656 "/>
<polyline fill="none" stroke="#000000" points="708.8828,-647.2656 708.8828,-615.2656 "/>
<polyline fill="none" stroke="#000000" points="708.8828,-647.2656 712.8828,-651.2656 "/>
<text text-anchor="middle" x="560" y="-627.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.preprocess_clean</text>
</g>
<!-- t8&#45;&gt;t9 -->
<g id="edge10" class="edge">
<title>t8&#45;&gt;t9</title>
<path fill="none" stroke="#0044a0" d="M766.2017,-615.2319C765.6315,-611.5574 765.0185,-607.6073 764.4079,-603.6719"/>
<polygon fill="#0044a0" stroke="#0044a0" points="767.8197,-602.8328 762.8276,-593.4878 760.9024,-603.9063 767.8197,-602.8328"/>
<path fill="none" stroke="#0044a0" d="M567.5798,-673.2319C566.8624,-669.4493 566.0897,-665.3747 565.3216,-661.3247"/>
<polygon fill="#0044a0" stroke="#0044a0" points="568.7581,-660.6605 563.4559,-651.4878 561.8807,-661.9649 568.7581,-660.6605"/>
</g>
<!-- t8&#45;&gt;t12 -->
<g id="edge16" class="edge">
<title>t8&#45;&gt;t12</title>
<path fill="none" stroke="#0044a0" d="M875.8215,-615.19C897.1712,-609.3919 915.1622,-602.1209 922,-593.2656 931.7788,-580.6016 931.1868,-570.3653 922,-557.2656 915.7828,-548.4003 898.4853,-539.9108 878.1493,-532.5246"/>
<polygon fill="#0044a0" stroke="#0044a0" points="879.2156,-529.1899 868.6208,-529.2112 876.9164,-535.8016 879.2156,-529.1899"/>
<path fill="none" stroke="#0044a0" d="M670.0045,-673.2618C693.2528,-667.2338 713.6919,-659.8307 722,-651.2656 734.1971,-638.6912 739.4901,-620.2051 741.6123,-603.5914"/>
<polygon fill="#0044a0" stroke="#0044a0" points="745.1041,-603.8427 742.5574,-593.5585 738.1349,-603.1862 745.1041,-603.8427"/>
</g>
<!-- t10 -->
<g id="node11" class="node">
<title>t10</title>
<polygon fill="#efa03b" stroke="#000000" points="382.1094,-525.2656 77.8906,-525.2656 73.8906,-521.2656 73.8906,-489.2656 378.1094,-489.2656 382.1094,-493.2656 382.1094,-525.2656"/>
<polyline fill="none" stroke="#000000" points="378.1094,-521.2656 73.8906,-521.2656 "/>
<polyline fill="none" stroke="#000000" points="378.1094,-521.2656 378.1094,-489.2656 "/>
<polyline fill="none" stroke="#000000" points="378.1094,-521.2656 382.1094,-525.2656 "/>
<text text-anchor="middle" x="228" y="-501.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_ocr_image</text>
<polygon fill="#efa03b" stroke="#000000" points="340.1094,-583.2656 35.8906,-583.2656 31.8906,-579.2656 31.8906,-547.2656 336.1094,-547.2656 340.1094,-551.2656 340.1094,-583.2656"/>
<polyline fill="none" stroke="#000000" points="336.1094,-579.2656 31.8906,-579.2656 "/>
<polyline fill="none" stroke="#000000" points="336.1094,-579.2656 336.1094,-547.2656 "/>
<polyline fill="none" stroke="#000000" points="336.1094,-579.2656 340.1094,-583.2656 "/>
<text text-anchor="middle" x="186" y="-559.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.select_ocr_image</text>
</g>
<!-- t9&#45;&gt;t10 -->
<g id="edge11" class="edge">
<title>t9&#45;&gt;t10</title>
<path fill="none" stroke="#0044a0" d="M606.6862,-560.7998C540.2619,-553.9762 461.6761,-545.1568 391,-535.2656 373.4852,-532.8143 354.9627,-529.9218 336.9313,-526.9421"/>
<polygon fill="#0044a0" stroke="#0044a0" points="337.4138,-523.4743 326.9739,-525.28 336.2612,-530.3788 337.4138,-523.4743"/>
<path fill="none" stroke="#0044a0" d="M460.7568,-615.2213C409.7279,-605.9434 347.4237,-594.6153 295.0537,-585.0935"/>
<polygon fill="#0044a0" stroke="#0044a0" points="295.5223,-581.6214 285.0575,-583.276 294.2701,-588.5085 295.5223,-581.6214"/>
</g>
<!-- t9&#45;&gt;t12 -->
<g id="edge15" class="edge">
<title>t9&#45;&gt;t12</title>
<path fill="none" stroke="#0044a0" d="M766.4316,-557.0428C767.8283,-553.0854 769.3544,-548.7615 770.905,-544.368"/>
<polygon fill="#0044a0" stroke="#0044a0" points="774.2705,-545.3486 774.2983,-534.7538 767.6695,-543.0188 774.2705,-545.3486"/>
<path fill="none" stroke="#0044a0" d="M608.2948,-615.2213C627.0226,-608.2242 648.8712,-600.0609 669.4134,-592.3859"/>
<polygon fill="#0044a0" stroke="#0044a0" points="670.9148,-595.5613 679.0573,-588.7826 668.4648,-589.004 670.9148,-595.5613"/>
</g>
<!-- t11 -->
<g id="node12" class="node">
<title>t11</title>
<polygon fill="#00cc66" stroke="#000000" points="756.0996,-447.2656 441.9004,-447.2656 437.9004,-443.2656 437.9004,-411.2656 752.0996,-411.2656 756.0996,-415.2656 756.0996,-447.2656"/>
<polyline fill="none" stroke="#000000" points="752.0996,-443.2656 437.9004,-443.2656 "/>
<polyline fill="none" stroke="#000000" points="752.0996,-443.2656 752.0996,-411.2656 "/>
<polyline fill="none" stroke="#000000" points="752.0996,-443.2656 756.0996,-447.2656 "/>
<text text-anchor="middle" x="597" y="-423.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_tesseract_hocr</text>
<polygon fill="#00cc66" stroke="#000000" points="334.0996,-505.2656 19.9004,-505.2656 15.9004,-501.2656 15.9004,-469.2656 330.0996,-469.2656 334.0996,-473.2656 334.0996,-505.2656"/>
<polyline fill="none" stroke="#000000" points="330.0996,-501.2656 15.9004,-501.2656 "/>
<polyline fill="none" stroke="#000000" points="330.0996,-501.2656 330.0996,-469.2656 "/>
<polyline fill="none" stroke="#000000" points="330.0996,-501.2656 334.0996,-505.2656 "/>
<text text-anchor="middle" x="175" y="-481.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_tesseract_hocr</text>
</g>
<!-- t10&#45;&gt;t11 -->
<g id="edge12" class="edge">
<title>t10&#45;&gt;t11</title>
<path fill="none" stroke="#0044a0" d="M313.5835,-489.1748C369.8356,-477.2841 443.5049,-461.7117 501.6353,-449.424"/>
<polygon fill="#0044a0" stroke="#0044a0" points="502.5017,-452.8182 511.5616,-447.3257 501.0539,-445.9696 502.5017,-452.8182"/>
<path fill="none" stroke="#0044a0" d="M183.4487,-547.1748C182.1254,-537.7914 180.4788,-526.1154 178.9917,-515.5701"/>
<polygon fill="#0044a0" stroke="#0044a0" points="182.4378,-514.9417 177.5755,-505.5285 175.5064,-515.9193 182.4378,-514.9417"/>
</g>
<!-- t16 -->
<!-- t15 -->
<g id="node14" class="node">
<title>t16</title>
<polygon fill="#ff69b4" stroke="#000000" points="420.2696,-447.2656 39.7304,-447.2656 35.7304,-443.2656 35.7304,-411.2656 416.2696,-411.2656 420.2696,-415.2656 420.2696,-447.2656"/>
<polyline fill="none" stroke="#000000" points="416.2696,-443.2656 35.7304,-443.2656 "/>
<polyline fill="none" stroke="#000000" points="416.2696,-443.2656 416.2696,-411.2656 "/>
<polyline fill="none" stroke="#000000" points="416.2696,-443.2656 420.2696,-447.2656 "/>
<text text-anchor="middle" x="228" y="-423.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_tesseract_textonly_pdf</text>
<title>t15</title>
<polygon fill="#ff69b4" stroke="#000000" points="736.2696,-505.2656 355.7304,-505.2656 351.7304,-501.2656 351.7304,-469.2656 732.2696,-469.2656 736.2696,-473.2656 736.2696,-505.2656"/>
<polyline fill="none" stroke="#000000" points="732.2696,-501.2656 351.7304,-501.2656 "/>
<polyline fill="none" stroke="#000000" points="732.2696,-501.2656 732.2696,-469.2656 "/>
<polyline fill="none" stroke="#000000" points="732.2696,-501.2656 736.2696,-505.2656 "/>
<text text-anchor="middle" x="544" y="-481.2656" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.ocr_tesseract_textonly_pdf</text>
</g>
<!-- t10&#45;&gt;t16 -->
<!-- t10&#45;&gt;t15 -->
<g id="edge14" class="edge">
<title>t10&#45;&gt;t16</title>
<path fill="none" stroke="#0044a0" d="M228,-489.1748C228,-479.7914 228,-468.1154 228,-457.5701"/>
<polygon fill="#0044a0" stroke="#0044a0" points="231.5001,-457.5284 228,-447.5285 224.5001,-457.5285 231.5001,-457.5284"/>
<title>t10&#45;&gt;t15</title>
<path fill="none" stroke="#0044a0" d="M269.0322,-547.1748C323.4947,-535.3086 394.7854,-519.776 451.1284,-507.5002"/>
<polygon fill="#0044a0" stroke="#0044a0" points="452.0829,-510.8744 461.1086,-505.3257 450.5927,-504.0348 452.0829,-510.8744"/>
</g>
<!-- t14 -->
<g id="node13" class="node">
<title>t14</title>
<polygon fill="#00cc66" stroke="#000000" points="780.0433,-353.7258 471.9567,-353.7258 467.9567,-349.7258 467.9567,-317.7258 776.0433,-317.7258 780.0433,-321.7258 780.0433,-353.7258"/>
<polyline fill="none" stroke="#000000" points="776.0433,-349.7258 467.9567,-349.7258 "/>
<polyline fill="none" stroke="#000000" points="776.0433,-349.7258 776.0433,-317.7258 "/>
<polyline fill="none" stroke="#000000" points="776.0433,-349.7258 780.0433,-353.7258 "/>
<text text-anchor="middle" x="624" y="-329.7258" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.render_hocr_page</text>
<polygon fill="#00cc66" stroke="#000000" points="330.0433,-411.7258 21.9567,-411.7258 17.9567,-407.7258 17.9567,-375.7258 326.0433,-375.7258 330.0433,-379.7258 330.0433,-411.7258"/>
<polyline fill="none" stroke="#000000" points="326.0433,-407.7258 17.9567,-407.7258 "/>
<polyline fill="none" stroke="#000000" points="326.0433,-407.7258 326.0433,-375.7258 "/>
<polyline fill="none" stroke="#000000" points="326.0433,-407.7258 330.0433,-411.7258 "/>
<text text-anchor="middle" x="174" y="-387.7258" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.render_hocr_page</text>
</g>
<!-- t11&#45;&gt;t14 -->
<g id="edge13" class="edge">
<title>t11&#45;&gt;t14</title>
<path fill="none" stroke="#0044a0" d="M602.2059,-411.2302C606.0656,-397.8584 611.4022,-379.3702 615.8284,-364.0357"/>
<polygon fill="#0044a0" stroke="#0044a0" points="619.3074,-364.6035 618.718,-354.025 612.5819,-362.6621 619.3074,-364.6035"/>
<path fill="none" stroke="#0044a0" d="M174.8072,-469.2302C174.6642,-455.8584 174.4666,-437.3702 174.3027,-422.0357"/>
<polygon fill="#0044a0" stroke="#0044a0" points="177.8024,-421.987 174.1956,-412.025 170.8028,-422.0619 177.8024,-421.987"/>
</g>
<!-- t15 -->
<g id="node18" class="node">
<title>t15</title>
<polygon fill="#00cc66" stroke="#000000" points="1169.9202,-353.7258 802.0798,-353.7258 798.0798,-349.7258 798.0798,-317.7258 1165.9202,-317.7258 1169.9202,-321.7258 1169.9202,-353.7258"/>
<polyline fill="none" stroke="#000000" points="1165.9202,-349.7258 798.0798,-349.7258 "/>
<polyline fill="none" stroke="#000000" points="1165.9202,-349.7258 1165.9202,-317.7258 "/>
<polyline fill="none" stroke="#000000" points="1165.9202,-349.7258 1169.9202,-353.7258 "/>
<text text-anchor="middle" x="984" y="-329.7258" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.render_hocr_debug_page</text>
</g>
<!-- t11&#45;&gt;t15 -->
<g id="edge26" class="edge">
<title>t11&#45;&gt;t15</title>
<path fill="none" stroke="#0044a0" d="M671.6175,-411.2302C736.8294,-395.4681 831.4531,-372.5971 899.4498,-356.162"/>
<polygon fill="#0044a0" stroke="#0044a0" points="900.3844,-359.537 909.2822,-353.7855 898.7398,-352.7329 900.3844,-359.537"/>
</g>
<!-- t20 -->
<!-- t19 -->
<g id="node22" class="node">
<title>t20</title>
<polygon fill="#efa03b" stroke="#000000" points="59.7205,-323.5085 253,-296.1897 446.2795,-323.5085 446.0991,-367.7112 59.9009,-367.7112 59.7205,-323.5085"/>
<polygon fill="none" stroke="#000000" points="55.7033,-320.0329 253,-292.1463 450.2967,-320.0329 450.0858,-371.7148 55.9142,-371.7148 55.7033,-320.0329"/>
<text text-anchor="middle" x="253" y="-329.7258" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.merge_sidecars</text>
<title>t19</title>
<polygon fill="#efa03b" stroke="#000000" points="351.7205,-381.5085 545,-354.1897 738.2795,-381.5085 738.0991,-425.7112 351.9009,-425.7112 351.7205,-381.5085"/>
<polygon fill="none" stroke="#000000" points="347.7033,-378.0329 545,-350.1463 742.2967,-378.0329 742.0858,-429.7148 347.9142,-429.7148 347.7033,-378.0329"/>
<text text-anchor="middle" x="545" y="-387.7258" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.merge_sidecars</text>
</g>
<!-- t11&#45;&gt;t20 -->
<g id="edge34" class="edge">
<title>t11&#45;&gt;t20</title>
<path fill="none" stroke="#0044a0" d="M530.6733,-411.2302C492.1331,-400.7504 442.0357,-387.128 395.0218,-374.3441"/>
<polygon fill="#0044a0" stroke="#0044a0" points="395.9216,-370.9617 385.3536,-371.7151 394.0848,-377.7165 395.9216,-370.9617"/>
<!-- t11&#45;&gt;t19 -->
<g id="edge32" class="edge">
<title>t11&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M246.3397,-469.2302C287.966,-458.7066 342.1269,-445.0142 392.8772,-432.184"/>
<polygon fill="#0044a0" stroke="#0044a0" points="393.8058,-435.5594 402.6429,-429.7151 392.0901,-428.773 393.8058,-435.5594"/>
</g>
<!-- t14&#45;&gt;t17 -->
<!-- t14&#45;&gt;t16 -->
<g id="edge22" class="edge">
<title>t14&#45;&gt;t17</title>
<path fill="none" stroke="#0044a0" d="M624,-317.5989C624,-306.7612 624,-292.7572 624,-280.4888"/>
<polygon fill="#0044a0" stroke="#0044a0" points="627.5001,-280.2775 624,-270.2776 620.5001,-280.2776 627.5001,-280.2775"/>
<title>t14&#45;&gt;t16</title>
<path fill="none" stroke="#0044a0" d="M230.6615,-375.7128C262.2207,-366.4581 302.4172,-355.9425 339,-350.1861 448.3957,-332.9725 745.7869,-320.7866 927.9215,-314.6743"/>
<polygon fill="#0044a0" stroke="#0044a0" points="928.248,-318.1655 938.1258,-314.3342 928.0148,-311.1693 928.248,-318.1655"/>
</g>
<!-- t16&#45;&gt;t17 -->
<!-- t15&#45;&gt;t16 -->
<g id="edge21" class="edge">
<title>t16&#45;&gt;t17</title>
<path fill="none" stroke="#0044a0" d="M121.729,-411.2329C88.1524,-403.224 57.1198,-392.5599 47,-379.2656 23.5584,-348.4705 20.5968,-320.483 47,-292.1861 61.4893,-276.6576 308.9021,-264.1817 474.9995,-257.507"/>
<polygon fill="#0044a0" stroke="#0044a0" points="475.4699,-260.9912 485.3225,-257.0955 475.1909,-253.9967 475.4699,-260.9912"/>
<title>t15&#45;&gt;t16</title>
<path fill="none" stroke="#0044a0" d="M630.0636,-469.2083C667.5329,-460.6467 711.76,-449.5912 751,-437.2656 850.4985,-406.0121 963.1653,-359.5222 1026.586,-332.2894"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1028.1566,-335.4238 1035.9553,-328.2523 1025.3866,-328.9951 1028.1566,-335.4238"/>
</g>
<!-- t16&#45;&gt;t20 -->
<g id="edge33" class="edge">
<title>t16&#45;&gt;t20</title>
<path fill="none" stroke="#0044a0" d="M232.8203,-411.2302C235.0783,-402.7816 237.8822,-392.2905 240.6745,-381.8429"/>
<polygon fill="#0044a0" stroke="#0044a0" points="244.1291,-382.4721 243.3299,-371.9075 237.3665,-380.6646 244.1291,-382.4721"/>
<!-- t15&#45;&gt;t19 -->
<g id="edge31" class="edge">
<title>t15&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M544.1928,-469.2302C544.2822,-460.8687 544.393,-450.5066 544.5035,-440.166"/>
<polygon fill="#0044a0" stroke="#0044a0" points="548.006,-439.9443 544.6132,-429.9075 541.0064,-439.8694 548.006,-439.9443"/>
</g>
<!-- t12&#45;&gt;t13 -->
<g id="edge19" class="edge">
<title>t12&#45;&gt;t13</title>
<path fill="none" stroke="#0044a0" d="M875.4342,-485.8486C920.1893,-475.3654 974.0965,-462.7385 1019.6212,-452.075"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1020.5619,-455.4495 1029.5001,-449.761 1018.9654,-448.634 1020.5619,-455.4495"/>
<path fill="none" stroke="#0044a0" d="M833.9833,-543.8486C879.0975,-533.3444 933.4556,-520.6879 979.3116,-510.0109"/>
<polygon fill="#0044a0" stroke="#0044a0" points="980.3156,-513.3709 989.2614,-507.6943 978.7282,-506.5532 980.3156,-513.3709"/>
</g>
<!-- t12&#45;&gt;t15 -->
<g id="edge25" class="edge">
<title>t12&#45;&gt;t15</title>
<path fill="none" stroke="#0044a0" d="M776.0923,-479.7209C771.2028,-456.5505 768.6047,-423.7032 785,-401.2656 797.9932,-383.4838 843.2193,-367.8601 887.2779,-356.3313"/>
<polygon fill="#0044a0" stroke="#0044a0" points="888.4172,-359.6528 897.2399,-353.7867 886.6847,-352.8705 888.4172,-359.6528"/>
</g>
<!-- t13&#45;&gt;t17 -->
<!-- t13&#45;&gt;t16 -->
<g id="edge23" class="edge">
<title>t13&#45;&gt;t17</title>
<path fill="none" stroke="#0044a0" d="M1158.1724,-404.745C1166.5787,-397.655 1174.2871,-389.1208 1179,-379.2656 1195.6968,-344.3505 1205.3645,-320.5191 1179,-292.1861 1165.2457,-277.4048 932.6999,-264.8684 773.033,-257.9499"/>
<polygon fill="#0044a0" stroke="#0044a0" points="772.7656,-254.4353 762.6245,-257.5024 772.4649,-261.4288 772.7656,-254.4353"/>
<title>t13&#45;&gt;t16</title>
<path fill="none" stroke="#0044a0" d="M1077,-459.2068C1077,-426.2624 1077,-371.9187 1077,-338.7552"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1080.5001,-338.3818 1077,-328.3819 1073.5001,-338.3819 1080.5001,-338.3818"/>
</g>
<!-- t17&#45;&gt;t19 -->
<!-- t16&#45;&gt;t18 -->
<g id="edge27" class="edge">
<title>t16&#45;&gt;t18</title>
<path fill="none" stroke="#0044a0" d="M1117.3592,-292.0592C1133.8695,-284.6437 1153.6803,-275.7459 1173.2953,-266.9361"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1174.8979,-270.0531 1182.586,-262.7632 1172.0299,-263.6676 1174.8979,-270.0531"/>
</g>
<!-- t17&#45;&gt;t18 -->
<g id="edge26" class="edge">
<title>t17&#45;&gt;t18</title>
<path fill="none" stroke="#0044a0" d="M1326.2292,-973.7283C1377.8362,-955.768 1439,-922.4247 1439,-865.2656 1439,-865.2656 1439,-865.2656 1439,-393.7258 1439,-338.7092 1393.2056,-296.3464 1348.3356,-268.0433"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1350.0403,-264.9831 1339.6842,-262.7465 1346.3852,-270.9531 1350.0403,-264.9831"/>
</g>
<!-- t20 -->
<g id="node20" class="node">
<title>t20</title>
<polygon fill="#efa03b" stroke="#000000" points="1400.3184,-161.1062 1129.6816,-161.1062 1125.6816,-157.1062 1125.6816,-125.1062 1396.3184,-125.1062 1400.3184,-129.1062 1400.3184,-161.1062"/>
<polyline fill="none" stroke="#000000" points="1396.3184,-157.1062 1125.6816,-157.1062 "/>
<polyline fill="none" stroke="#000000" points="1396.3184,-157.1062 1396.3184,-125.1062 "/>
<polyline fill="none" stroke="#000000" points="1396.3184,-157.1062 1400.3184,-161.1062 "/>
<text text-anchor="middle" x="1263" y="-137.1062" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.optimize_pdf</text>
</g>
<!-- t18&#45;&gt;t20 -->
<g id="edge29" class="edge">
<title>t17&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M750.0003,-234.162C823.2979,-223.6769 917.4921,-210.2027 1001.3789,-198.2028"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1001.8891,-201.6656 1011.2927,-196.7847 1000.8978,-194.7361 1001.8891,-201.6656"/>
</g>
<!-- t15&#45;&gt;t19 -->
<g id="edge30" class="edge">
<title>t15&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M1008.3983,-317.5273C1042.5211,-292.0754 1105.4861,-245.1103 1151.601,-210.7137"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1153.7921,-213.4458 1159.7152,-204.6613 1149.6068,-207.8347 1153.7921,-213.4458"/>
</g>
<!-- t18&#45;&gt;t19 -->
<g id="edge28" class="edge">
<title>t18&#45;&gt;t19</title>
<path fill="none" stroke="#0044a0" d="M1346.2292,-915.7283C1397.8362,-897.768 1459,-864.4247 1459,-807.2656 1459,-807.2656 1459,-807.2656 1459,-335.7258 1459,-274.6094 1406.7951,-234.4876 1350.0021,-208.9223"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1351.0491,-205.5613 1340.4826,-204.7869 1348.26,-211.9816 1351.0491,-205.5613"/>
<title>t18&#45;&gt;t20</title>
<path fill="none" stroke="#0044a0" d="M1263,-182.9188C1263,-179.129 1263,-175.3607 1263,-171.739"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1266.5001,-171.4394 1263,-161.4394 1259.5001,-171.4394 1266.5001,-171.4394"/>
</g>
<!-- t21 -->
<g id="node21" class="node">
<title>t21</title>
<polygon fill="#efa03b" stroke="#000000" points="1041.0652,-47.332 1208,-20.0049 1374.9348,-47.332 1374.779,-91.5483 1041.221,-91.5483 1041.0652,-47.332"/>
<polygon fill="none" stroke="#000000" points="1037.0538,-43.9306 1208,-15.9468 1378.9462,-43.9306 1378.7643,-95.5532 1037.2357,-95.5532 1037.0538,-43.9306"/>
<text text-anchor="middle" x="1208" y="-53.5531" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.copy_final</text>
<polygon fill="#efa03b" stroke="#000000" points="1096.0652,-47.332 1263,-20.0049 1429.9348,-47.332 1429.779,-91.5483 1096.221,-91.5483 1096.0652,-47.332"/>
<polygon fill="none" stroke="#000000" points="1092.0538,-43.9306 1263,-15.9468 1433.9462,-43.9306 1433.7643,-95.5532 1092.2357,-95.5532 1092.0538,-43.9306"/>
<text text-anchor="middle" x="1263" y="-53.5531" font-family="Times,serif" font-size="20.00" fill="#000000">ocrmypdf.pipeline.copy_final</text>
</g>
<!-- t19&#45;&gt;t21 -->
<g id="edge32" class="edge">
<title>t19&#45;&gt;t21</title>
<path fill="none" stroke="#0044a0" d="M1208,-124.9024C1208,-118.6745 1208,-112.2508 1208,-105.974"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1211.5001,-105.6096 1208,-95.6097 1204.5001,-105.6097 1211.5001,-105.6096"/>
<!-- t20&#45;&gt;t21 -->
<g id="edge30" class="edge">
<title>t20&#45;&gt;t21</title>
<path fill="none" stroke="#0044a0" d="M1263,-124.9764C1263,-119.37 1263,-112.9163 1263,-106.2938"/>
<polygon fill="#0044a0" stroke="#0044a0" points="1266.5001,-105.8456 1263,-95.8457 1259.5001,-105.8457 1266.5001,-105.8456"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+4 -3
View File
@@ -826,9 +826,10 @@ def run_pipeline():
# Performance is improved by setting Tesseract to single threaded. In tests
# this gives better throughput than letting a smaller number of Tesseract
# jobs run multithreaded.
if tesseract.v4():
os.environ.setdefault('OMP_THREAD_LIMIT', '1')
# jobs run multithreaded. Same story for pngquant. Tess <4 ignores this
# variable, but harmless to set if ignored.
os.environ.setdefault('OMP_THREAD_LIMIT', '1')
check_environ(options, _log)
if os.environ.get('PYTEST_CURRENT_TEST'):
os.environ['_OCRMYPDF_TEST_INFILE'] = options.input_file
+389
View File
@@ -0,0 +1,389 @@
# © 2018 James R. Barlow: github.com/jbarlow83
#
# This file is part of OCRmyPDF.
#
# OCRmyPDF is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OCRmyPDF is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
from pathlib import Path
from subprocess import run, PIPE
import concurrent.futures
from collections import defaultdict
import struct
from io import BytesIO
from PIL import Image
from .lib import fitz
import pikepdf
from . import leptonica
from .helpers import re_symlink
from .exec import pngquant
PAGE_GROUP_SIZE = 10
SIMPLE_COLORSPACES = ('/DeviceRGB', '/DeviceGray', '/CalRGB', '/CalGray')
def filter_decodeparms(obj):
"""
PDF has a lot of optional data structures concerning /Filter and
/DecodeParms. /Filter can be absent or a name or an array, /DecodeParms
can be absent or a dictionary (if /Filter is a name) or an array (if
/Filter is an array). When both are arrays the lengths match.
Normalize this into:
[(/FilterName, {/DecodeParmName: Value, ...}), ...]
If there are no filters then the return is
[('', {})]
The order of /Filter matters as indicates the encoding/decoding sequence.
"""
normalized = []
filters = []
filt = obj.get('/Filter', None)
if filt is None:
return [('', {})]
if filt.type_code == pikepdf.ObjectType.array:
filters.extend(filt)
elif filt.type_code == pikepdf.ObjectType.name:
filters.append(filt)
decodeparms = obj.get('/DecodeParms', pikepdf.Array([]))
if decodeparms.type_code == pikepdf.ObjectType.dictionary:
decodeparms = pikepdf.Array([decodeparms])
for n, dp in enumerate(decodeparms):
filt_parm = (filters[n], dp)
normalized.append(filt_parm)
if len(normalized) == 0:
for filt in filters:
filt_parm = (filt, {})
normalized.append(filt_parm)
if len(normalized) == 0:
return [('', {})]
return normalized
def generate_ccitt_header(data, w, h, decode_parms):
# https://stackoverflow.com/questions/2641770/
# https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf
if not decode_parms:
raise ValueError("/CCITTFaxDecode without /DecodeParms")
if decode_parms.get("/K", 1) < 0:
ccitt_group = 4 # Pure two-dimensional encoding (Group 4)
else:
ccitt_group = 3
img_size = len(data)
tiff_header_struct = '<' + '2s' + 'H' + 'L' + 'H' + 'HHLL' * 8 + 'L'
tiff_header = struct.pack(
tiff_header_struct,
b'II', # Byte order indication: Little endian
42, # Version number (always 42)
8, # Offset to first IFD
8, # Number of tags in IFD
256, 4, 1, w, # ImageWidth, LONG, 1, width
257, 4, 1, h, # ImageLength, LONG, 1, length
258, 3, 1, 1, # BitsPerSample, SHORT, 1, 1
259, 3, 1, ccitt_group, # Compression, SHORT, 1, 4 = CCITT Group 4 fax encoding
262, 3, 1, 0, # Thresholding, SHORT, 1, 0 = WhiteIsZero
273, 4, 1, struct.calcsize(tiff_header_struct), # StripOffsets, LONG, 1, length of header
278, 4, 1, h, # RowsPerStrip, LONG, 1, length
279, 4, 1, img_size, # StripByteCounts, LONG, 1, size of image
0 # last IFD
)
return tiff_header
def make_img_name(root, xref):
return str(root / '{:08d}.png'.format(xref))
def extract_image(doc, pike, root, log, image, xref, jbig2s,
pngs, jpegs):
if image.Subtype != '/Image':
return False
if image.Length < 100:
log.debug("Skipping small image, xref {}".format(xref))
return False
bpc = image.get('/BitsPerComponent', 8)
cs = image.get('/ColorSpace', '')
w = int(image.Width)
h = int(image.Height)
filtdps = filter_decodeparms(image)
if len(filtdps) > 1:
log.debug("Skipping multiply filtered, xref {}".format(xref))
return False
filtdp = filtdps[0]
if bpc == 1 and filtdp[0] != '/JBIG2Decode':
if filtdp[0] == '/CCITTFaxDecode':
data = image.read_raw_bytes()
try:
header = generate_ccitt_header(data, w, h, filtdp[1])
except ValueError as e:
log.info(e)
return False
stream = BytesIO()
stream.write(header)
stream.write(data)
stream.seek(0)
with Image.open(stream) as im:
im.save(make_img_name(root, xref))
else:
return False
jbig2s.append(xref)
elif filtdp[0] == '/JPXDecode':
return False
elif filtdp[0] == '/DCTDecode' and cs in SIMPLE_COLORSPACES:
raw_jpeg = pike._get_object_id(xref, 0)
color_transform = filtdp[1].get('/ColorTransform', 1)
if color_transform != 1:
return False # Don't mess with JPEGs other than YUV
# This is a simple heuristic derived from some training data, that has
# about a 70% chance of guessing whether the JPEG is high quality,
# and possibly recompressible, or not. The number itself doesn't mean
# anything.
# bytes_per_pixel = int(raw_jpeg.Length) / (w * h)
# jpeg_quality_estimate = 117.0 * (bytes_per_pixel ** 0.213)
# if jpeg_quality_estimate < 65:
# return False
raw_jpeg_data = raw_jpeg.read_raw_bytes()
(root / '{:08d}.jpg'.format(xref)).write_bytes(raw_jpeg_data)
jpegs.append(xref)
elif cs in SIMPLE_COLORSPACES:
# For any 'inferior' filter include /FlateDecode we extract
# and recode as /FlateDecode
# raw_png = pike._get_object_id(xref, 0)
# raw_png_data = raw_png.read_raw_bytes()
# (root / '{:08d}.png'.format(xref)).write_bytes(raw_png_data)
pix = fitz.Pixmap(doc, xref)
pix.writePNG(make_img_name(root, xref), savealpha=False)
pngs.append(xref)
else:
return False
return True
def extract_images(doc, pike, root, log):
# Extract images we can improve
changed_xrefs = set()
jbig2_groups = defaultdict(lambda: [])
jpegs = []
pngs = []
errors = 0
for pageno, page in enumerate(pike.pages):
group, _ = divmod(pageno, PAGE_GROUP_SIZE)
try:
xobjs = page.Resources.XObject
except AttributeError:
continue
for imname, image in dict(xobjs).items():
xref = image._objgen[0]
if xref in changed_xrefs:
continue # Don't improve same image twice
try:
result = extract_image(
doc, pike, root, log, image, xref,
jbig2_groups[group], pngs, jpegs)
if result:
changed_xrefs.add(xref)
except Exception as e:
log.debug("Image {} xref {}".format(imname, xref))
log.debug(repr(e))
errors += 1
# Elide empty groups
jbig2_groups = {group: xrefs for group, xrefs in jbig2_groups.items()
if len(xrefs) > 0}
log.debug(
"Optimizable images: "
"JBIG2 groups: {} JPEGs: {} PNGs: {} Errors: {}".format(
len(jbig2_groups), len(jpegs), len(pngs), errors
))
return changed_xrefs, jbig2_groups, jpegs, pngs
def convert_to_jbig2(pike, jbig2_groups, root, log, options):
"""
Convert a group of JBIG2 images and insert into PDF.
We use a group because JBIG2 works best with a symbol dictionary that spans
multiple pages. When inserted back into the PDF, each JBIG2 must reference
the symbol dictionary it is associated with. So convert a group at a time,
and replace their streams with a parameter set that points to the
appropriate dictionary.
If too many pages shared the same dictionary JBIG2 encoding becomes more
expensive and less efficient.
"""
with concurrent.futures.ThreadPoolExecutor(
max_workers=options.jobs) as executor:
futures = []
for group, xrefs in jbig2_groups.items():
prefix = 'group{:08d}'.format(group)
cmd = ['jbig2', '-b', prefix, '-s', '-p']
cmd.extend(make_img_name(root, xref) for xref in xrefs)
future = executor.submit(
run, cmd, cwd=str(root), stdout=PIPE, stderr=PIPE)
futures.append(future)
for future in concurrent.futures.as_completed(futures):
proc = future.result()
proc.check_returncode()
log.debug(proc.stderr)
for group, xrefs in jbig2_groups.items():
prefix = 'group{:08d}'.format(group)
jbig2_globals_data = (root / (prefix + '.sym')).read_bytes()
jbig2_globals = pikepdf.Stream(pike, jbig2_globals_data)
for n, xref in enumerate(xrefs):
jbig2_im_file = root / (prefix + '.{:04d}'.format(n))
jbig2_im_data = jbig2_im_file.read_bytes()
im_obj = pike._get_object_id(xref, 0)
im_obj.write(
jbig2_im_data, pikepdf.Name('/JBIG2Decode'),
pikepdf.Dictionary({
'/JBIG2Globals': jbig2_globals
})
)
def transcode_jpegs(pike, jpegs, root, log, options):
for xref in jpegs:
in_jpg = root / '{:08d}.jpg'.format(xref)
opt_jpg = root / '{:08d}_opt.jpg'.format(xref)
with Image.open(str(in_jpg)) as im:
im.save(str(opt_jpg),
optimize=True,
quality=70)
if opt_jpg.stat().st_size > in_jpg.stat().st_size:
log.debug("xref {}, jpeg, made larger - skip".format(xref))
continue
compdata = leptonica.CompressedData.open(opt_jpg)
im_obj = pike._get_object_id(xref, 0)
im_obj.write(
compdata.read(), pikepdf.Name('/DCTDecode'),
pikepdf.Null()
)
def transcode_pngs(pike, pngs, root, options):
with concurrent.futures.ThreadPoolExecutor(
max_workers=options.jobs) as executor:
for xref in pngs:
executor.submit(
pngquant.quantize,
make_img_name(root, xref), make_img_name(root, xref), 65, 80)
for xref in pngs:
im_obj = pike._get_object_id(xref, 0)
pix = leptonica.Pix.read(make_img_name(root, xref))
compdata = pix.generate_pdf_ci_data(leptonica.lept.L_FLATE_ENCODE, 0)
predictor = pikepdf.Null()
if compdata.predictor > 0:
predictor = pikepdf.Dictionary({'/Predictor': compdata.predictor})
im_obj.BitsPerComponent = compdata.bps
im_obj.Width = compdata.w
im_obj.Height = compdata.h
if compdata.ncolors > 0:
palette_pdf_string = compdata.get_palette_pdf_string()
palette_data = pikepdf.Object.parse(palette_pdf_string)
palette_stream = pikepdf.Stream(pike, bytes(palette_data))
palette = [pikepdf.Name('/Indexed'), pikepdf.Name('/DeviceRGB'),
compdata.ncolors - 1, palette_stream]
cs = palette
else:
if compdata.spp == 1:
cs = pikepdf.Name('/DeviceGray')
elif compdata.spp == 3:
cs = pikepdf.Name('/DeviceRGB')
elif compdata.spp == 4:
cs = pikepdf.Name('/DeviceCMYK')
im_obj.ColorSpace = cs
im_obj.write(compdata.read(), pikepdf.Name('/FlateDecode'), predictor)
def optimize(
input_file,
output_file,
log,
context):
if not fitz:
re_symlink(input_file, output_file)
return
options = context.get_options()
doc = fitz.open(input_file)
pike = pikepdf.Pdf.open(input_file)
root = Path(output_file).parent / 'images'
root.mkdir(exist_ok=True)
changed_xrefs, jbig2_groups, jpegs, pngs = extract_images(
doc, pike, root, log)
convert_to_jbig2(pike, jbig2_groups, root, log, options)
transcode_jpegs(pike, jpegs, root, log, options)
transcode_pngs(pike, pngs, root, options)
# Not object_stream_mode + preserve_pdfa generates noncompliant PDFs
target_file = output_file + '_opt.pdf'
pike.save(target_file, preserve_pdfa=True)
input_size = Path(input_file).stat().st_size
output_size = Path(target_file).stat().st_size
ratio = input_size / output_size
savings = 1 - output_size / input_size
log.info("Optimize ratio: {:.2f} savings: {:.1f}%".format(
ratio, 100 * savings))
if savings < 0:
log.info("Optimize did not improve the file - discarded")
re_symlink(input_file, output_file)
else:
re_symlink(target_file, output_file)
if __name__ == '__main__':
import logging
import sys
from .pipeline import JobContext
from collections import namedtuple
Options = namedtuple('Options', 'jobs')
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger()
ctx = JobContext()
options = Options(jobs=4)
ctx.set_options(options)
optimize(sys.argv[1], sys.argv[2], log, ctx)
+39
View File
@@ -0,0 +1,39 @@
# © 2018 James R. Barlow: github.com/jbarlow83
#
# This file is part of OCRmyPDF.
#
# OCRmyPDF is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OCRmyPDF is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
from subprocess import CalledProcessError, run
from tempfile import TemporaryFile
import sys
import os
import shutil
from ..exceptions import ExitCode
def convert(input_file, output_file):
args_jbig2 = [
'jbig2',
'-s',
input_file
]
with TemporaryFile(mode='w+b') as tmp:
proc = run(args_jbig2, stdout=tmp)
if proc.returncode == 0:
with open(output_file, 'wb') as output:
shutil.copyfileobj(tmp, output)
+45
View File
@@ -0,0 +1,45 @@
# © 2018 James R. Barlow: github.com/jbarlow83
#
# This file is part of OCRmyPDF.
#
# OCRmyPDF is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OCRmyPDF is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
from subprocess import CalledProcessError, run
from tempfile import TemporaryFile
from functools import lru_cache
import sys
import os
import shutil
from . import get_version
from ..exceptions import ExitCode
@lru_cache(maxsize=1)
def version():
return get_version('pngquant', regex=r'(\d+(\.\d+)*).*')
def quantize(input_file, output_file, quality_min, quality_max):
args = [
'pngquant',
'--force',
'--skip-if-larger',
'--output', output_file,
'--quality', '{}-{}'.format(quality_min, quality_max),
'--',
input_file
]
proc = run(args)
proc.check_returncode()
+52
View File
@@ -24,6 +24,7 @@ import argparse
import sys
import os
import logging
import warnings
from tempfile import TemporaryFile
from ctypes.util import find_library
from .lib._leptonica import ffi
@@ -219,6 +220,11 @@ class Pix:
@classmethod
def read(cls, path):
warnings.warn('Use Pix.open() instead', DeprecationWarning)
return cls.open(path)
@classmethod
def open(cls, path):
"""Load an image file into a PIX object.
Leptonica can load TIFF, PNM (PBM, PGM, PPM), PNG, and JPEG. If
@@ -476,6 +482,15 @@ class Pix:
raise LeptonicaError("Correlation failed")
return correlation[0]
def generate_pdf_ci_data(self, type_, quality):
"Convert to PDF data, with transcoding"
p_compdata = ffi.new('L_COMP_DATA **')
result = lept.pixGenerateCIData(self._pix, type_, quality, 0,
p_compdata)
if result != 0:
raise LeptonicaError("Generate PDF data failed")
return CompressedData(p_compdata[0])
@staticmethod
def _pix_destroy(pix):
p_pix = ffi.new('PIX **', pix)
@@ -483,6 +498,43 @@ class Pix:
# print('pix destroy ' + repr(pix))
class CompressedData:
def __init__(self, compdata):
self._compdata = ffi.gc(compdata, CompressedData._destroy)
@classmethod
def open(cls, path, jpeg_quality=75):
"Open compressed data, without transcoding"
filename = fspath(path)
p_compdata = ffi.new('L_COMP_DATA **')
result = lept.l_generateCIDataForPdf(
os.fsencode(filename), ffi.NULL, jpeg_quality, p_compdata)
if result != 0:
raise LeptonicaError("CompressedData.open")
return CompressedData(p_compdata[0])
def read(self):
buf = ffi.buffer(self._compdata.datacomp, self._compdata.nbytescomp)
return bytes(buf)
def __getattr__(self, name):
if hasattr(self._compdata, name):
return getattr(self._compdata, name)
raise AttributeError(name)
def get_palette_pdf_string(self):
"Returns palette pre-formatted for use in PDF"
buflen = len('< ') + len(' rrggbb') * self._compdata.ncolors + len('>')
buf = ffi.buffer(self._compdata.cmapdatahex, buflen)
return bytes(buf)
@staticmethod
def _destroy(compdata):
pp = ffi.new('L_COMP_DATA **', compdata)
lept.l_CIDataDestroy(pp)
class Box:
"""Wrapper around Leptonica's BOX objects.
+5 -5
View File
@@ -3,9 +3,9 @@ import _cffi_backend
ffi = _cffi_backend.FFI('ocrmypdf.lib._leptonica',
_version = 0x2601,
_types = b'\x00\x00\x0F\x0D\x00\x00\xBB\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xBE\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\xBA\x03\x00\x00\x0F\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x02\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x0D\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x08\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xC9\x03\x00\x00\x1C\x01\x00\x00\x00\x0F\x00\x00\xBD\x0D\x00\x00\x00\x0F\x00\x00\x49\x0D\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x1F\x03\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x49\x11\x00\x00\x49\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x49\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x01\x03\x00\x00\x9D\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\xC8\x03\x00\x00\x65\x03\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x08\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\xC6\x03\x00\x00\xA3\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x00\x0F\x00\x00\xCC\x0D\x00\x00\x10\x11\x00\x00\x00\x0F\x00\x00\xCC\x0D\x00\x00\x9D\x11\x00\x00\x00\x0F\x00\x00\xCC\x0D\x00\x00\xCC\x03\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x01\x09\x00\x00\x02\x09\x00\x00\xBE\x03\x00\x00\x02\x01\x00\x00\x0E\x01\x00\x00\x00\x0B\x00\x00\x01\x0B\x00\x00\x0B\x01\x00\x00\x05\x01\x00\x00\x03\x01\x00\x00\xBC\x03\x00\x00\xC7\x03\x00\x00\x04\x01\x00\x00\xC9\x03\x00\x00\x08\x01\x00\x00\x0C\x01\x00\x00\x06\x01\x00\x00\x00\x01',
_globals = (b'\xFF\xFF\xFF\x0BL_CLONE',2,b'\xFF\xFF\xFF\x0BL_COPY',1,b'\xFF\xFF\xFF\x0BL_COPY_CLONE',3,b'\xFF\xFF\xFF\x0BL_INSERT',0,b'\xFF\xFF\xFF\x0BL_NOCOPY',0,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_BASED_ON_SRC',4,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_BINARY',0,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_FULL_COLOR',2,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_GRAYSCALE',1,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_WITH_ALPHA',3,b'\x00\x00\xB1\x23boxDestroy',0,b'\x00\x00\x67\x23getLeptonicaVersion',0,b'\x00\x00\xB7\x23lept_free',0,b'\x00\x00\x69\x23makePixelSumTab8',0,b'\x00\x00\x16\x23pixAnd',0,b'\x00\x00\x23\x23pixBackgroundNorm',0,b'\x00\x00\x1B\x23pixCleanBackgroundToWhite',0,b'\x00\x00\x0D\x23pixClipRectangle',0,b'\x00\x00\x8D\x23pixColorFraction',0,b'\x00\x00\x59\x23pixColorMagnitude',0,b'\x00\x00\x0A\x23pixConvertRGBToLuminance',0,b'\x00\x00\x6B\x23pixCorrelationBinary',0,b'\x00\x00\x80\x23pixCountPixels',0,b'\x00\x00\x63\x23pixDeserializeFromMemory',0,b'\x00\x00\x50\x23pixDeskew',0,b'\x00\x00\xB4\x23pixDestroy',0,b'\x00\x00\x0A\x23pixEndianByteSwapNew',0,b'\x00\x00\x00\x23pixFindPageForeground',0,b'\x00\x00\x7B\x23pixFindSkew',0,b'\x00\x00\x2F\x23pixGammaTRC',0,b'\x00\x00\x70\x23pixGetAverageMaskedRGB',0,b'\x00\x00\x36\x23pixGlobalNormRGB',0,b'\x00\x00\x12\x23pixInvert',0,b'\x00\x00\x54\x23pixMaskOverColorPixels',0,b'\x00\x00\x85\x23pixNumSignificantGrayColors',0,b'\x00\x00\x96\x23pixOtsuAdaptiveThreshold',0,b'\x00\x00\x3E\x23pixOtsuThreshOnBackgroundNorm',0,b'\x00\x00\x60\x23pixRead',0,b'\x00\x00\x50\x23pixRemoveColormap',0,b'\x00\x00\x54\x23pixRemoveColormapGeneral',0,b'\x00\x00\x12\x23pixRotate180',0,b'\x00\x00\x50\x23pixRotateOrth',0,b'\x00\x00\x4B\x23pixScale',0,b'\x00\x00\xA0\x23pixSerializeToMemory',0,b'\x00\x00\xA5\x23pixWriteImpliedFormat',0,b'\x00\x00\xAB\x23pixWriteMemPng',0),
_struct_unions = ((b'\x00\x00\x00\xBA\x00\x00\x00\x02Box',b'\x00\x00\x02\x11x',b'\x00\x00\x02\x11y',b'\x00\x00\x02\x11w',b'\x00\x00\x02\x11h',b'\x00\x00\xC9\x11refcount'),(b'\x00\x00\x00\xBB\x00\x00\x00\x02Pix',b'\x00\x00\xC9\x11w',b'\x00\x00\xC9\x11h',b'\x00\x00\xC9\x11d',b'\x00\x00\xC9\x11spp',b'\x00\x00\xC9\x11wpl',b'\x00\x00\xC9\x11refcount',b'\x00\x00\x02\x11xres',b'\x00\x00\x02\x11yres',b'\x00\x00\x02\x11informat',b'\x00\x00\x02\x11special',b'\x00\x00\xBD\x11text',b'\x00\x00\xC5\x11colormap',b'\x00\x00\xC8\x11data'),(b'\x00\x00\x00\xBC\x00\x00\x00\x02PixColormap',b'\x00\x00\xB8\x11array',b'\x00\x00\x02\x11depth',b'\x00\x00\x02\x11nalloc',b'\x00\x00\x02\x11n')),
_enums = (b'\x00\x00\x00\xC0\x00\x00\x00\x16$1\x00REMOVE_CMAP_TO_BINARY,REMOVE_CMAP_TO_GRAYSCALE,REMOVE_CMAP_TO_FULL_COLOR,REMOVE_CMAP_WITH_ALPHA,REMOVE_CMAP_BASED_ON_SRC',b'\x00\x00\x00\xC1\x00\x00\x00\x16$2\x00L_NOCOPY,L_INSERT,L_COPY,L_CLONE,L_COPY_CLONE'),
_typenames = (b'\x00\x00\x00\xBABOX',b'\x00\x00\x00\xBBPIX',b'\x00\x00\x00\xBCPIXCMAP',b'\x00\x00\x00\x1Fl_float32',b'\x00\x00\x00\xBFl_float64',b'\x00\x00\x00\xC3l_int16',b'\x00\x00\x00\x02l_int32',b'\x00\x00\x00\xC2l_int64',b'\x00\x00\x00\xC4l_int8',b'\x00\x00\x00\xCBl_uint16',b'\x00\x00\x00\xC9l_uint32',b'\x00\x00\x00\xCAl_uint64',b'\x00\x00\x00\xC7l_uint8'),
_types = b'\x00\x00\x0F\x0D\x00\x00\xCD\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xD0\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\xCA\x03\x00\x00\x0F\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x02\x03\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x0D\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x08\x11\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xDC\x03\x00\x00\x1C\x01\x00\x00\x00\x0F\x00\x00\xCF\x0D\x00\x00\x00\x0F\x00\x00\x49\x0D\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x1F\x03\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x49\x11\x00\x00\x49\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x07\x01\x00\x00\x49\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xCB\x03\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6E\x11\x00\x00\x6E\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x0D\x01\x00\x00\x01\x03\x00\x00\xA4\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x01\x11\x00\x00\xDB\x03\x00\x00\x65\x03\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x08\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x92\x11\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\x08\x11\x00\x00\x01\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x02\x0D\x00\x00\xD9\x03\x00\x00\xAA\x11\x00\x00\x01\x11\x00\x00\x0D\x01\x00\x00\x00\x0F\x00\x00\xDF\x0D\x00\x00\x10\x11\x00\x00\x00\x0F\x00\x00\xDF\x0D\x00\x00\x92\x11\x00\x00\x00\x0F\x00\x00\xDF\x0D\x00\x00\xA4\x11\x00\x00\x00\x0F\x00\x00\xDF\x0D\x00\x00\xDF\x03\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\xCC\x03\x00\x00\x01\x09\x00\x00\x02\x09\x00\x00\x03\x09\x00\x00\xD0\x03\x00\x00\x02\x01\x00\x00\x0E\x01\x00\x00\x00\x0B\x00\x00\x01\x0B\x00\x00\x02\x0B\x00\x00\x0B\x01\x00\x00\x05\x01\x00\x00\x03\x01\x00\x00\xCE\x03\x00\x00\xDA\x03\x00\x00\x04\x01\x00\x00\xDC\x03\x00\x00\x08\x01\x00\x00\x0C\x01\x00\x00\x06\x01\x00\x00\x00\x01',
_globals = (b'\xFF\xFF\xFF\x0BL_CLONE',2,b'\xFF\xFF\xFF\x0BL_COPY',1,b'\xFF\xFF\xFF\x0BL_COPY_CLONE',3,b'\xFF\xFF\xFF\x0BL_DEFAULT_ENCODE',0,b'\xFF\xFF\xFF\x0BL_FLATE_ENCODE',3,b'\xFF\xFF\xFF\x0BL_G4_ENCODE',2,b'\xFF\xFF\xFF\x0BL_INSERT',0,b'\xFF\xFF\xFF\x0BL_JP2K_ENCODE',4,b'\xFF\xFF\xFF\x0BL_JPEG_ENCODE',1,b'\xFF\xFF\xFF\x0BL_NOCOPY',0,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_BASED_ON_SRC',4,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_BINARY',0,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_FULL_COLOR',2,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_TO_GRAYSCALE',1,b'\xFF\xFF\xFF\x0BREMOVE_CMAP_WITH_ALPHA',3,b'\x00\x00\xBE\x23boxDestroy',0,b'\x00\x00\x67\x23getLeptonicaVersion',0,b'\x00\x00\xC1\x23l_CIDataDestroy',0,b'\x00\x00\xAC\x23l_generateCIDataForPdf',0,b'\x00\x00\xC7\x23lept_free',0,b'\x00\x00\x69\x23makePixelSumTab8',0,b'\x00\x00\x16\x23pixAnd',0,b'\x00\x00\x23\x23pixBackgroundNorm',0,b'\x00\x00\x1B\x23pixCleanBackgroundToWhite',0,b'\x00\x00\x0D\x23pixClipRectangle',0,b'\x00\x00\x94\x23pixColorFraction',0,b'\x00\x00\x59\x23pixColorMagnitude',0,b'\x00\x00\x0A\x23pixConvertRGBToLuminance',0,b'\x00\x00\x6B\x23pixCorrelationBinary',0,b'\x00\x00\x80\x23pixCountPixels',0,b'\x00\x00\x63\x23pixDeserializeFromMemory',0,b'\x00\x00\x50\x23pixDeskew',0,b'\x00\x00\xC4\x23pixDestroy',0,b'\x00\x00\x0A\x23pixEndianByteSwapNew',0,b'\x00\x00\x00\x23pixFindPageForeground',0,b'\x00\x00\x7B\x23pixFindSkew',0,b'\x00\x00\x2F\x23pixGammaTRC',0,b'\x00\x00\x8D\x23pixGenerateCIData',0,b'\x00\x00\x70\x23pixGetAverageMaskedRGB',0,b'\x00\x00\x36\x23pixGlobalNormRGB',0,b'\x00\x00\x12\x23pixInvert',0,b'\x00\x00\x54\x23pixMaskOverColorPixels',0,b'\x00\x00\x85\x23pixNumSignificantGrayColors',0,b'\x00\x00\x9D\x23pixOtsuAdaptiveThreshold',0,b'\x00\x00\x3E\x23pixOtsuThreshOnBackgroundNorm',0,b'\x00\x00\x60\x23pixRead',0,b'\x00\x00\x50\x23pixRemoveColormap',0,b'\x00\x00\x54\x23pixRemoveColormapGeneral',0,b'\x00\x00\x12\x23pixRotate180',0,b'\x00\x00\x50\x23pixRotateOrth',0,b'\x00\x00\x4B\x23pixScale',0,b'\x00\x00\xA7\x23pixSerializeToMemory',0,b'\x00\x00\xB2\x23pixWriteImpliedFormat',0,b'\x00\x00\xB8\x23pixWriteMemPng',0),
_struct_unions = ((b'\x00\x00\x00\xCA\x00\x00\x00\x02Box',b'\x00\x00\x02\x11x',b'\x00\x00\x02\x11y',b'\x00\x00\x02\x11w',b'\x00\x00\x02\x11h',b'\x00\x00\xDC\x11refcount'),(b'\x00\x00\x00\xCC\x00\x00\x00\x02L_Compressed_Data',b'\x00\x00\x02\x11type',b'\x00\x00\xD9\x11datacomp',b'\x00\x00\x65\x11nbytescomp',b'\x00\x00\xCF\x11data85',b'\x00\x00\x65\x11nbytes85',b'\x00\x00\xCF\x11cmapdata85',b'\x00\x00\xCF\x11cmapdatahex',b'\x00\x00\x02\x11ncolors',b'\x00\x00\x02\x11w',b'\x00\x00\x02\x11h',b'\x00\x00\x02\x11bps',b'\x00\x00\x02\x11spp',b'\x00\x00\x02\x11minisblack',b'\x00\x00\x02\x11predictor',b'\x00\x00\x65\x11nbytescomp',b'\x00\x00\xCF\x11data85',b'\x00\x00\x65\x11nbytes85',b'\x00\x00\xCF\x11cmapdata85',b'\x00\x00\xCF\x11cmapdatahex',b'\x00\x00\x02\x11ncolors',b'\x00\x00\x02\x11w',b'\x00\x00\x02\x11h',b'\x00\x00\x02\x11bps',b'\x00\x00\x02\x11spp',b'\x00\x00\x02\x11minisblack',b'\x00\x00\x02\x11predictor',b'\x00\x00\x65\x11nbytes',b'\x00\x00\x02\x11res'),(b'\x00\x00\x00\xCD\x00\x00\x00\x02Pix',b'\x00\x00\xDC\x11w',b'\x00\x00\xDC\x11h',b'\x00\x00\xDC\x11d',b'\x00\x00\xDC\x11spp',b'\x00\x00\xDC\x11wpl',b'\x00\x00\xDC\x11refcount',b'\x00\x00\x02\x11xres',b'\x00\x00\x02\x11yres',b'\x00\x00\x02\x11informat',b'\x00\x00\x02\x11special',b'\x00\x00\xCF\x11text',b'\x00\x00\xD8\x11colormap',b'\x00\x00\xDB\x11data'),(b'\x00\x00\x00\xCE\x00\x00\x00\x02PixColormap',b'\x00\x00\xC8\x11array',b'\x00\x00\x02\x11depth',b'\x00\x00\x02\x11nalloc',b'\x00\x00\x02\x11n')),
_enums = (b'\x00\x00\x00\xD2\x00\x00\x00\x16$1\x00L_DEFAULT_ENCODE,L_JPEG_ENCODE,L_G4_ENCODE,L_FLATE_ENCODE,L_JP2K_ENCODE',b'\x00\x00\x00\xD3\x00\x00\x00\x16$2\x00REMOVE_CMAP_TO_BINARY,REMOVE_CMAP_TO_GRAYSCALE,REMOVE_CMAP_TO_FULL_COLOR,REMOVE_CMAP_WITH_ALPHA,REMOVE_CMAP_BASED_ON_SRC',b'\x00\x00\x00\xD4\x00\x00\x00\x16$3\x00L_NOCOPY,L_INSERT,L_COPY,L_CLONE,L_COPY_CLONE'),
_typenames = (b'\x00\x00\x00\xCABOX',b'\x00\x00\x00\xCCL_COMP_DATA',b'\x00\x00\x00\xCDPIX',b'\x00\x00\x00\xCEPIXCMAP',b'\x00\x00\x00\x1Fl_float32',b'\x00\x00\x00\xD1l_float64',b'\x00\x00\x00\xD6l_int16',b'\x00\x00\x00\x02l_int32',b'\x00\x00\x00\xD5l_int64',b'\x00\x00\x00\xD7l_int8',b'\x00\x00\x00\xDEl_uint16',b'\x00\x00\x00\xDCl_uint32',b'\x00\x00\x00\xDDl_uint64',b'\x00\x00\x00\xDAl_uint8'),
)
+59
View File
@@ -72,6 +72,49 @@ struct Box
};
typedef struct Box BOX;
/*! Pdf formatted encoding types */
enum {
L_DEFAULT_ENCODE = 0, /*!< use default encoding based on image */
L_JPEG_ENCODE = 1, /*!< use dct encoding: 8 and 32 bpp, no cmap */
L_G4_ENCODE = 2, /*!< use ccitt g4 fax encoding: 1 bpp */
L_FLATE_ENCODE = 3, /*!< use flate encoding: any depth, cmap ok */
L_JP2K_ENCODE = 4 /*!< use jp2k encoding: 8 and 32 bpp, no cmap */
};
struct L_Compressed_Data
{
l_int32 type; /*!< encoding type: L_JPEG_ENCODE, etc */
l_uint8 *datacomp; /*!< gzipped raster data */
size_t nbytescomp; /*!< number of compressed bytes */
char *data85; /*!< ascii85-encoded gzipped raster data */
size_t nbytes85; /*!< number of ascii85 encoded bytes */
char *cmapdata85; /*!< ascii85-encoded uncompressed cmap */
char *cmapdatahex; /*!< hex pdf array for the cmap */
l_int32 ncolors; /*!< number of colors in cmap */
l_int32 w; /*!< image width */
l_int32 h; /*!< image height */
l_int32 bps; /*!< bits/sample; typ. 1, 2, 4 or 8 */
l_int32 spp; /*!< samples/pixel; typ. 1 or 3 */
l_int32 minisblack; /*!< tiff g4 photometry */
l_int32 predictor; /*!< flate data has PNG predictors */
size_t nbytescomp; /*!< number of compressed bytes */
char *data85; /*!< ascii85-encoded gzipped raster data */
size_t nbytes85; /*!< number of ascii85 encoded bytes */
char *cmapdata85; /*!< ascii85-encoded uncompressed cmap */
char *cmapdatahex; /*!< hex pdf array for the cmap */
l_int32 ncolors; /*!< number of colors in cmap */
l_int32 w; /*!< image width */
l_int32 h; /*!< image height */
l_int32 bps; /*!< bits/sample; typ. 1, 2, 4 or 8 */
l_int32 spp; /*!< samples/pixel; typ. 1 or 3 */
l_int32 minisblack; /*!< tiff g4 photometry */
l_int32 predictor; /*!< flate data has PNG predictors */
size_t nbytes; /*!< number of uncompressed raster bytes */
l_int32 res; /*!< resolution (ppi) */
};
typedef struct L_Compressed_Data L_COMP_DATA;
enum {
REMOVE_CMAP_TO_BINARY = 0, /*!< remove colormap for conv to 1 bpp */
REMOVE_CMAP_TO_GRAYSCALE = 1, /*!< remove colormap for conv to 8 bpp */
@@ -250,9 +293,25 @@ pixRemoveColormapGeneral(PIX *pixs,
l_int32 type,
l_int32 ifnocmap);
l_int32
pixGenerateCIData(PIX *pixs,
l_int32 type,
l_int32 quality,
l_int32 ascii85,
L_COMP_DATA **pcid);
l_int32
l_generateCIDataForPdf(const char *fname,
PIX *pix,
l_int32 quality,
L_COMP_DATA **pcid);
void
boxDestroy(BOX **pbox);
void
l_CIDataDestroy(L_COMP_DATA **pcid);
void
lept_free(void *ptr);
""")
+19 -2
View File
@@ -43,7 +43,7 @@ from .exceptions import PdfMergeFailedError, UnsupportedImageFormatError, \
DpiError, PriorOcrFoundError, InputFileError
from . import leptonica
from . import PROGRAM_NAME, VERSION
from ._optimize import optimize
VECTOR_PAGE_DPI = 400
@@ -1144,6 +1144,14 @@ def _do_merge_mupdf(
doc.save(output_file, clean=False, garbage=4, deflate=True)
def optimize_pdf(
input_file,
output_file,
log,
context):
optimize(input_file, output_file, log, context)
def merge_sidecars(
input_files_groups,
output_file,
@@ -1390,9 +1398,18 @@ def build_pipeline(options, work_folder, log, context):
extras=[log, context])
task_merge_sidecars.active_if(options.sidecar)
# Optimize
task_optimize_pdf = main_pipeline.transform(
task_func=optimize_pdf,
input=task_metadata_fixup,
filter=suffix('.pdf'),
output='.optimized.pdf',
output_dir=work_folder,
extras=[log, context])
# Finalize
main_pipeline.merge(
task_func=copy_final,
input=[task_metadata_fixup],
input=[task_optimize_pdf],
output=options.output_file,
extras=[log, context])