Compare commits

...
1549 Commits
Author SHA1 Message Date
James R. Barlow 8fff496ffd Fix Py3.5 not understanding os.path.exists(Path(...)) 2018-05-26 22:55:22 -07:00
James R. Barlow edf75c519c Update v7 release notes 2018-05-26 02:08:49 -07:00
James R. Barlow 9608b22d34 Remove all uses of PyPDF2 except PDF/A check
Leave PDF/A check alone for now, since pikepdf has no equivalent.
2018-05-26 02:07:18 -07:00
James R. Barlow 8ba4968c48 pdfinfo: more robustness 2018-05-26 01:54:25 -07:00
James R. Barlow ffdd78f1a5 pdfinfo: Fix text_operators type not changed in related commit 2018-05-25 02:10:39 -07:00
James R. Barlow ad9f8ca78e pdfinfo: reinstate stack normalization for q/Q 2018-05-25 01:28:26 -07:00
James R. Barlow 78a686ecb4 Consider qpdf behavior on algo4 a pass
qpdf opens files with null user password, so do the same.
2018-05-25 00:33:31 -07:00
James R. Barlow 59e786eb3c Remove old code to deal with single page only things 2018-05-25 00:32:55 -07:00
James R. Barlow 6d0461435f Use OperandGrouper whitelist 2018-05-24 22:52:33 -07:00
James R. Barlow 0a04a60f69 Document need for pdfinfo to be pickleable 2018-05-24 22:24:13 -07:00
James R. Barlow 68d8642988 Found out this test was extremely slow - no reason to actual use a large file 2018-05-24 22:22:51 -07:00
James R. Barlow 16f70ff054 Main changeset for pikepdf-based refactor pdfinfo 2018-05-24 22:22:01 -07:00
James R. Barlow c00aeafff0 Add scratch file 2018-05-24 22:20:15 -07:00
James R. Barlow 83f35e00f3 Start removing PyPDF2 2018-05-21 01:28:21 -07:00
James R. Barlow 786a2ad65a Make optimize test do a little more 2018-05-18 17:50:39 -07:00
James R. Barlow 9425506c2a Use pikepdf to handle paletted images
Removes all use of PyMuPDF in optimize
2018-05-18 17:44:29 -07:00
James R. Barlow 93b858afd1 Remove qpdf appimage support for now, check for pngquant 2018-05-18 16:24:33 -07:00
James R. Barlow 7b0a3ec365 Add notes for v7 2018-05-18 00:20:45 -07:00
James R. Barlow 083d442529 main: wording change 2018-05-18 00:20:24 -07:00
James R. Barlow b52eb95cf8 optimize: use pikepdf to save PIL images
Eliminates another usage of PyMuPDF in the main path.
2018-05-18 00:18:44 -07:00
James R. Barlow f4571e2508 Ensure we try compress anything that's not compressed when saving 2018-05-17 22:05:01 -07:00
James R. Barlow b06ef03aac pipeline: use the resolution of the OCR image rather than recalculating
(Recalculating would fail if the image is not centered.)
2018-05-17 16:51:53 -07:00
James R. Barlow 1d1962a106 weave: fix rescaling logic
rotation % 90 == 0 is always true.
2018-05-17 16:50:01 -07:00
James R. Barlow 4b98e9ff08 weave: if we don't have textonly_pdf, delete instruction to draw image 2018-05-17 16:49:20 -07:00
James R. Barlow f83ca5d8ac weave: whitespace 2018-05-17 16:06:36 -07:00
James R. Barlow 95cb4d22d7 pipeline: make /Info from indirect object as required 2018-05-17 16:06:13 -07:00
James R. Barlow 0c279b01a4 Fix test failure on missing JobContext 2018-05-17 01:16:58 -07:00
James R. Barlow 3b820ffa7b test_metadata: change from xfail to skipif without fitz 2018-05-17 00:14:57 -07:00
James R. Barlow 35cb416563 pipeline: remove fitz-based attempt to repair table of contents
Prior to unsplit, if we were rebuilding the PDF we'd lose the
table of contents. With unsplit we keep the original file and patch
the table of contents as necessary, adn that works fine.
This remaining bit of code from PyMuPDF actually damages the
table of contents and removing it fixes the test suite. G'bye.
2018-05-16 23:24:57 -07:00
James R. Barlow cdb737259c pipeline: remove old page merge strategies 2018-05-16 22:16:54 -07:00
James R. Barlow 0843b5939c pipeline: Move weave* to its own file 2018-05-16 22:08:31 -07:00
James R. Barlow 2b5f23a2d1 Add code to repair ToC with pikepdf 2018-05-16 21:39:23 -07:00
James R. Barlow 5e20d1d554 metadata: Fix failing test on __getitem__['/CreationDate'] 2018-05-16 13:46:07 -07:00
James R. Barlow 18595ca86a Use pikepdf for get_pdfmark
It does fine.
2018-05-16 12:24:35 -07:00
James R. Barlow 3e269fa188 Ubuntu 14.04 has a qpdf 8.0.2 backport, making life easier 2018-05-15 21:43:19 -07:00
James R. Barlow 65405c2cb9 Try getting qpdf from Ubuntu 18.04 2018-05-15 21:27:27 -07:00
James R. Barlow 442cf8897a Travis: maybe upgrading wheel? 2018-05-15 18:12:35 -07:00
James R. Barlow d5fb275e9e Travis: hack in qpdf appimage version
qpdf from appimage does not report its version with --version if renamed
or accessed via symlink. Use an environment variable to supply it
where needed.
2018-05-15 17:45:58 -07:00
James R. Barlow e60aec81ca Travis: why can't we use qpdf appimage? 2018-05-15 16:59:16 -07:00
James R. Barlow 398e9e535e optimize: Changed pikepdf API 2018-05-15 16:29:57 -07:00
James R. Barlow 08bf651ef2 Refactor JBIG2 path for non-CCITT monochrome images 2018-05-15 15:32:15 -07:00
James R. Barlow 6171de41bf optimize: move a lot of image scanning code to pikepdf 2018-05-14 22:21:53 -07:00
James R. Barlow f0a56592e2 Pull JobContext out of pipeline.py to avoid circular reference 2018-05-14 14:01:25 -07:00
James R. Barlow 87a7d4d1a8 Another fitz failure - incorrect object reference introduced
MuPDF/fitz changed some font references to point to table of contents
entries, corrupting the page.  It no longer gets to save.
2018-05-14 13:58:49 -07:00
James R. Barlow 05287902a2 Travis: again 2018-05-13 11:02:25 -07:00
James R. Barlow 96e453feb6 Travis: Tweak setup so it can run 2018-05-13 01:21:24 -07:00
James R. Barlow 9c0fa9fc04 Travis: again 2018-05-13 01:17:04 -07:00
James R. Barlow 3bde0715b0 Move qpdf to before_script 2018-05-13 01:01:48 -07:00
James R. Barlow e2ec3d8b9b Travis: adjust qpdf appimage 2018-05-13 00:53:31 -07:00
James R. Barlow ad91eaf8a7 Travis: try using qpdf appimage to speed up build 2018-05-13 00:42:48 -07:00
James R. Barlow b6d30214fd PyMuPDF 1.13.4 looks good, use it 2018-05-12 12:35:46 -07:00
James R. Barlow c4ab01d63d Fix "AttributeError: 'ImageInfo' object has no attribute '_type'"
Also deal with 'fixme' imagemask comment.

Also fix bpc incorrectly set to 8 by default on stencil masks.
2018-05-12 12:14:57 -07:00
James R. Barlow 4ba3b3f55a Fix rotate_pages_threshold test failure 2018-05-12 11:47:46 -07:00
James R. Barlow 52d2706a9e optimize: Fix error causing many images to be skipped 2018-05-12 01:37:30 -07:00
James R. Barlow 964afc69f6 leptonica: ErrorTrap is an implementation detail 2018-05-12 01:21:45 -07:00
James R. Barlow 3ddf545ccd optimize: leptonica can fail to open PNG
ERROR - Info in pixReadStreamPng: converting (cmap + alpha) ==> RGBA
Error in pixReadStreamPng: spp == 1, cmap, trans array, invalid depth: 4

To investigate later....
2018-05-12 01:21:19 -07:00
James R. Barlow f9374733bb optimize: process ICCBased images that declare an /Alternate we recognize 2018-05-12 00:43:36 -07:00
James R. Barlow 5930135f45 optimize: Refactor naming helpers 2018-05-12 00:42:24 -07:00
James R. Barlow f03f6bc128 optimize: document problem with transcode free compressed image data 2018-05-11 23:43:06 -07:00
James R. Barlow 6c50c70235 Try to optimize paletted images 2018-05-11 23:42:26 -07:00
James R. Barlow 8790fc2c1b optimize: add knobs to control image quality but don't show the user yet 2018-05-11 23:41:49 -07:00
James R. Barlow f86c4fccf4 optimize: don't alter >8 bpc images 2018-05-11 22:31:24 -07:00
James R. Barlow 7d0785e9ed main: do better parameter validation 2018-05-11 22:31:09 -07:00
James R. Barlow 2cac88162c Ignore masks when deciding what color to rasterize at 2018-05-11 21:27:57 -07:00
James R. Barlow 4809627d8a Fix jbig2enc name 2018-05-11 17:51:08 -07:00
James R. Barlow 871979abd6 Temporarily unbreak without fitz mode 2018-05-11 17:32:15 -07:00
James R. Barlow efb95722ca Travis: Use declarative APT for Tesseract too 2018-05-11 12:46:10 -07:00
James R. Barlow d9bbb80a6b Don't try to run jbig2 when not available 2018-05-11 12:42:00 -07:00
James R. Barlow 3254315127 Update test cache 2018-05-11 12:19:50 -07:00
James R. Barlow ca297fd26b Update tests 2018-05-11 02:33:44 -07:00
James R. Barlow ac36a43cef Warn about --user-words not having any effect
Might be available in full release of Tess4
2018-05-11 02:31:07 -07:00
James R. Barlow f00183115d Update our dependencies 2018-05-11 02:11:55 -07:00
James R. Barlow 161b29a899 Check jbig2 when optimizing is requested 2018-05-11 02:11:01 -07:00
James R. Barlow 72253d09fa Add arguments to control optimization 2018-05-10 22:23:24 -07:00
James R. Barlow 40d09ddb23 Fix merge error in Leptonica 2018-05-10 21:17:47 -07:00
James R. Barlow 3026d86a9e Remove jbig2enc.py 2018-05-10 21:15:07 -07:00
James R. Barlow 0661a7edc3 Merge optimize 2018-05-10 21:05:32 -07:00
James R. Barlow 24b0adfacc Merge branch 'master' into develop 2018-05-10 20:54:55 -07:00
James R. Barlow acc6698ab3 Make XML metadata test actually work 2018-05-10 20:37:10 -07:00
James R. Barlow 606d3e6aa1 Remove tests that exercise obsolete features (tesseract, -g) 2018-05-10 20:33:32 -07:00
James R. Barlow 687a7954d6 test_main: uses leptonica 2018-05-10 19:05:31 -07:00
James R. Barlow 36a53a7b37 Weave: Unconditionally rotate and scale the text layerThis solves two issues. First, the text layer can end up being adifferent size, probably if the DPI is not an integer; scaling helps itfit slightly better. Second, other printable text on the page can end uphorizontally scaled or misaligned if we don't all of our drawing in aq/Q pair. 2018-05-10 19:03:31 -07:00
James R. Barlow 0a5982a902 PyMuPDF tweaks: don't clean
In MuPDF 1.13 clean might be unreliable, so explicitly don't do it,
even though it doesn't cause trouble in 1.12.
2018-05-10 18:50:52 -07:00
James R. Barlow 601863f9e9 Return to PyMuPDF 1.12.5 2018-05-10 18:47:10 -07:00
James R. Barlow c9ce731119 Fix DPI mismatch between OCR page and source page 2018-05-10 17:34:08 -07:00
James R. Barlow abed8e034e Add metadata preservation test from stash 2018-05-10 16:43:28 -07:00
James R. Barlow 63032d304d Revert "Since PyMuPDF 1.13.3 corrupts text, pin 1.12.5 and work around it"
This reverts commit b0ce7c63dd.
2018-05-10 16:27:17 -07:00
James R. Barlow a57ecede78 Refactor textareas to remove duplicate code 2018-05-10 16:26:52 -07:00
James R. Barlow b0ce7c63dd Since PyMuPDF 1.13.3 corrupts text, pin 1.12.5 and work around it 2018-05-10 16:10:24 -07:00
James R. Barlow d139a11c16 Weave: periodically save to prevent indefinite growth of open file list 2018-05-10 15:08:57 -07:00
James R. Barlow aef043db0b Revise parameter validation for output-type, pdf-renderer, lang 2018-05-10 14:53:22 -07:00
James R. Barlow b8f3ead541 Remove tesseract renderer entirely
Grafting lets us work with older Tesseract versions as if they could use
sandwich, so there is no point in keeping it. It's been deprecated for a
long time now anyway.
2018-05-10 14:06:13 -07:00
James R. Barlow e0bb898f29 Remove hocr debug renderer (-g)
The fact that this produces additional pages makes it a maintenance
burden. hocr can be debugged using hocrtransform.
2018-05-10 13:48:39 -07:00
James R. Barlow 45336c7c28 textareas: filter out images 2018-05-10 01:17:28 -07:00
James R. Barlow 20aabb2e83 When deciding if there is a text on a page, ignore the margins
Margins may include watermarks or digital stamps on otherwise
text-free pages.
2018-05-10 01:16:11 -07:00
Fabian Rodriguezandjbarlow83 c7cf041e4a Fixed language option example (French) (#266)
Replace fre to fra.
2018-05-10 00:10:27 -07:00
James R. Barlow da80d3f354 Add unconditional (for now) whiteout of text areas 2018-05-07 17:37:46 -07:00
James R. Barlow 001c8d7678 Upgrade PyMuPDF version 2018-05-07 16:24:26 -07:00
James R. Barlow 38ab03655b Restore unpaper
It's a suggested/recommended dep not required in Deb/Ubu.
2018-05-06 21:36:12 -07:00
James R. Barlow 9226f8a5d1 Trap PDF/A-3 errors on old Ghostscript 2018-05-04 15:29:43 -07:00
James R. Barlow 5c8a007f3e Fix failure to prevent use of Ghostscript on /UserUnit files 2018-05-04 13:34:34 -07:00
James R. Barlow b3ad3e297d v6.2.0 fixes 2018-05-03 17:04:23 -07:00
James R. Barlow d607553e48 v6.2.0 Release notes 2018-05-03 16:47:21 -07:00
James R. Barlow 7cf83c77ca Merge branch 'feature/pdfa3' 2018-05-03 16:45:57 -07:00
James R. Barlow 8a9f174f63 Fix XMP validation issue with /CreationDate
Related to previous validation issue. If the /CreationDate had no
timezone, Ghostscript also creates invalid metadata. Work around this.
Also fix up PDF date decoding, and transcode dates to standardize them.
2018-05-03 16:30:20 -07:00
James R. Barlow 98a0786c32 Add 18.04 update procedure 2018-05-03 13:55:16 -07:00
James R. Barlow df1129724c Update Dockerfile for Ubuntu 18.04 2018-05-03 01:27:13 -07:00
James R. Barlow 423cef08bf Handle procset properly 2018-05-02 14:48:02 -07:00
James R. Barlow 04580accb4 Document aliasing of tesseract renderer 2018-05-02 14:47:38 -07:00
James R. Barlow 6376f77b8c Refactor, remove trigonometry 2018-05-02 12:30:34 -07:00
James R. Barlow e27e614ed9 Fixed rotation hard case 2018-05-02 01:32:11 -07:00
James R. Barlow b0c04704a1 Fixed all but one rotation case 2018-05-02 01:24:21 -07:00
James R. Barlow 6bb6bf8323 Fix correction angle used from wrong page 2018-05-02 01:00:30 -07:00
James R. Barlow e22fe8aefc Silence debug messages 2018-05-01 23:51:54 -07:00
James R. Barlow 76276f61e5 Split out rotation related tests 2018-05-01 23:51:35 -07:00
James R. Barlow bfd26e6ec6 Tests: confirm OCR layer copied 2018-05-01 23:16:41 -07:00
James R. Barlow d787e1ea0f ghostscript.py not saved in last commit
Given importance of last one, confirmed that when the file is saved all tests pass too.
Passing is invariant with this change.
2018-05-01 22:59:22 -07:00
James R. Barlow b5d7e9cbb0 Fix all issues with rotations
All tests now pass
2018-05-01 22:50:20 -07:00
James R. Barlow f3b6d9dcdf Fix a comment about Tesseract behavior in certain versions 2018-05-01 21:31:09 -07:00
James R. Barlow a9abe13185 Remove the old tesseract pdf_renderer 2018-05-01 17:31:34 -07:00
James R. Barlow 6b315e8315 Add ability to disable cache 2018-05-01 15:52:00 -07:00
James R. Barlow 37677de884 Fix regressions: pdfa.ps not used, PDF/A failures, handling of text layers with no font 2018-05-01 15:51:46 -07:00
James R. Barlow c7387de325 Fix auto rotate 2018-05-01 15:18:28 -07:00
James R. Barlow 2495b1e038 Refactor find font, get test cases working again 2018-05-01 14:48:41 -07:00
James R. Barlow 073ee52ce7 Use hocr and weave; eliminate old combine layers and merge pages 2018-05-01 14:21:53 -07:00
James R. Barlow 54150a14e9 Further elimination of tesseract renderer special casing
We don't need to keep a "skip page" around anymore since
skipping means just not grafting on the text layer.
2018-05-01 13:36:20 -07:00
James R. Barlow 88ff091cce Unify tesseract and sandwich renderer paths
Since the new weaving method copies the font and content
stream from the Tesseract PDF, it doesn't matter if Tesseract
happens to have an image or not.
If Tesseract is text-only capable we use that feature for efficiency,
but ignore the image either way.
2018-05-01 13:24:20 -07:00
James R. Barlow e87a5776f1 Remove now-unnecessary code to rotate pages
Track only the decision to change rotation.
2018-05-01 13:01:25 -07:00
James R. Barlow 0806ce6406 Fix rotation for unsplit (modulo --rotate-pages) 2018-04-30 20:58:42 -07:00
James R. Barlow 6409894a71 feature/unsplit-try-imagerotate 2018-04-30 20:48:59 -07:00
James R. Barlow e7286f6129 Unsplit now works with multipage, --force-ocr 2018-04-30 14:46:20 -07:00
James R. Barlow 2ab94b3151 unsplit: it's alive
First successful file output.
2018-04-28 01:57:41 -07:00
James R. Barlow 7ee90890ec Add copying of essential information from Tesseract textonly 2018-04-27 23:19:08 -07:00
James R. Barlow 383e726d65 Expand size growth reasons to other arguments that trigger transcoding 2018-04-27 19:34:57 -07:00
James R. Barlow e046f70642 Set OMP_THREAD_LIMIT unconditionally, for pngquant 2018-04-27 19:19:30 -07:00
James R. Barlow 2131ad4670 Fix --remove-background error on PDFs with colormapped images
It's unclear how exactly a
colormapped image gets to this
spot given the tendency of other
image processing tools to flatten
such images, but someone made it happen, so now we make sure
the image is okay.

Closes #262
2018-04-27 17:21:01 -07:00
James R. Barlow 219fe2155b test_pageinfo: remove duplicate import 2018-04-27 17:16:42 -07:00
James R. Barlow 4209034d20 Add gpg key to issue template 2018-04-27 15:51:26 -07:00
James R. Barlow abcae0c2a4 Fix helpers.py again 2018-04-25 22:10:51 -07:00
James R. Barlow 0934905493 Don't suppress error message from config_notfound
Since it showed up in s390x bionic
2018-04-25 21:58:18 -07:00
James R. Barlow 11cd6201d9 helpers: fix missing call to complain()
In practice this is probably unreachable.
2018-04-25 21:57:50 -07:00
James R. Barlow 8d2a917676 Page unsplit, development 2018-04-25 21:56:43 -07:00
James R. Barlow 44b4afa534 Begin conversion from page splititng to page markers 2018-04-23 22:57:50 -07:00
James R. Barlow 775be3933c Cherrypick merge_pages unification 2018-04-20 23:08:15 -07:00
James R. Barlow df87e21c85 Add support for PDF/A-3
No ability to attach files however
2018-04-20 00:06:55 -07:00
Hugoandjbarlow83 d761d80750 Use more standard __version__ rather than PILLOW_VERSION (#257) 2018-04-19 23:35:32 -07:00
James R. Barlow 8052019dde optimize: fix reporting of jbig2 groups 2018-04-19 01:54:44 -07:00
James R. Barlow a3d8950088 optimize: Don't save JPEGs if larger 2018-04-19 01:25:49 -07:00
James R. Barlow 004f5d3bf1 optimize: further improve decodeparms handling 2018-04-18 15:52:25 -07:00
James R. Barlow f5d308a156 optimize: refactor tricky /Filter and /DecodeParms handling 2018-04-18 15:30:21 -07:00
James R. Barlow 3869996758 optimize: jbig2 error 2018-04-18 01:36:38 -07:00
James R. Barlow cdb2107c4e optimize: jbigs2 fix 2018-04-18 01:31:35 -07:00
James R. Barlow 4db2b3413b optimize: more robustness 2018-04-18 01:25:34 -07:00
James R. Barlow b2f31bec79 Make optimize a lot safer 2018-04-18 00:20:06 -07:00
James R. Barlow 78f9f4a266 Be more defensive about accessing 2018-04-18 00:11:39 -07:00
James R. Barlow ad6087c342 optimize: more fixes 2018-04-17 23:58:10 -07:00
James R. Barlow 0d6ef430de optimize: fix "length not defined" 2018-04-17 23:38:00 -07:00
James R. Barlow a5942209e8 optimize: fix error on missing /Filter 2018-04-17 23:27:56 -07:00
James R. Barlow 9a60694cfc optimize: ccitt header fixes
Changed to match TIFF spec's use of unsigned types, eliminated check for
/Columns.

There is some complex behavior for /Width != /Columns and
(/Width, /Columns) mod 8 != 0
that is not described well in the PDF spec.
2018-04-17 23:27:25 -07:00
James R. Barlow 4bf13f4737 optimize: be less chatty 2018-04-17 23:25:41 -07:00
James R. Barlow 9e89b75186 Merge v6.1.5 2018-04-17 22:51:13 -07:00
James R. Barlow 0b10db91be Fix regression: Disable Ghostscript JPEG passthrough entirely 2018-04-17 17:00:24 -07:00
James R. Barlow 1a516b2af9 Fix regression: time stamp test suite failures 2018-04-17 16:59:21 -07:00
James R. Barlow 076363d78e Disable JPEG passthrough for Ghostscript 9.23
Seems to corrupt JPEGs involved in image masks?
2018-04-17 16:31:03 -07:00
James R. Barlow 5fde214290 Update notes for v6.1.5 2018-04-17 15:23:35 -07:00
James R. Barlow a620724d6a Fix PDF/A validation failure due to timezone being omitted from /ModDate 2018-04-17 15:16:48 -07:00
James R. Barlow 640b953ec7 Fix PDF/A validation failure due to timezone being omitted from /ModDate 2018-04-17 14:55:32 -07:00
James R. Barlow a009ca7597 Disable JPEG passthrough for Ghostscript 9.23
Seems to corrupt JPEGs involved in image masks?
2018-04-17 13:54:34 -07:00
James R. Barlow 7368399f8b Clarify license of two test files - https://github.com/jbarlow83/OCRmyPDF/issues/254 2018-04-17 11:56:36 -07:00
James R. Barlow c974aec934 Search for image masks too 2018-04-17 02:06:08 -07:00
James R. Barlow 3033f03f64 Iterate images with pikepdf / fix mono PNG corruption
To work around PNG corruption problem in PyMuPDF for monochrome images,
extract and save monochrome CCITT with synthetic TIFF header.

Works better but currently skips /ImageMask due to qpdf
implementation, which affects many files.
2018-04-17 01:50:37 -07:00
James R. Barlow 72723e0bb5 optimize: be quieter 2018-04-16 18:06:02 -07:00
James R. Barlow 2fb6ab3939 Trap writePNG error 2018-04-16 17:29:10 -07:00
James R. Barlow 25c1c160b8 Move optimize to new file 2018-04-16 17:22:06 -07:00
James R. Barlow 7e92895471 Parallelize pngquant 2018-04-16 12:37:51 -07:00
James R. Barlow d291d48991 PNG palette: parse PDF string from leptonica instead
Seems better to accept whatever leptonica rather than make detailed
assumptions about how it encodes the palette.

Experimented with setting FlateDecode on the palette but it seems to
expand it.
2018-04-16 12:16:13 -07:00
James R. Barlow 0e6b8042b0 Implement PNG palettization 2018-04-16 11:18:52 -07:00
James R. Barlow 34c78a892a Fix list table for tests/resources
[ci skip]
2018-04-15 23:52:19 -07:00
James R. Barlow 9d28879505 Update Ubuntu 14.04 instructions
Closes #252
2018-04-14 17:30:33 -07:00
James R. Barlow 2482296e2b hocr: avoid division by zero
Issue #253 - PDF that produces the error is not available, but if font_width
is zero, chances are the text is nonprinting characters, so suppress it.
2018-04-14 17:24:21 -07:00
James R. Barlow f755fb76ee Try pngquant 2018-04-14 01:37:14 -07:00
James R. Barlow c61b5dcb62 Fix PDF/A validation error from setting /Predictor 0 2018-04-14 01:36:46 -07:00
James R. Barlow fae893b9d9 Reinstate transcoding of PNG 2018-04-14 00:19:24 -07:00
James R. Barlow 10aadefd6a Document return codes 2018-04-14 00:18:58 -07:00
James R. Barlow e75b6280fd Try reading compressed data directly to see if Leptonica will add predictor
Turns out it does not transcode at all in this case, so probably going
to revert to transcoding PNG -> PNG. However if pngquant or similar is
done, this API will still be useful.
2018-04-13 23:55:23 -07:00
James R. Barlow 8c4023165a Release L_COMP_DATA properly 2018-04-13 23:53:41 -07:00
James R. Barlow b7d403f106 Deprecate Pix.read() behaving as an open function 2018-04-13 23:52:46 -07:00
James R. Barlow b069de0caa Use Leptonica to rewrite all PNGs with predictor
Leptonica does a better job of encoding them than Ghostscript, about -15%.
For a test file 450k worth of
PNGs was reduced to 388k with no loss of quality.
2018-04-13 16:35:50 -07:00
James R. Barlow 136da74bfa Update branch with v6.1.4 2018-04-13 12:57:21 -07:00
James R. Barlow 7fc897e6dc Fix NameError 'ghostscript' 2018-04-12 21:24:05 -07:00
James R. Barlow 9b731d63b8 Set Ghostscript -sColorConversionStrategy the way old/new versions expect 2018-04-12 16:28:48 -07:00
James R. Barlow 10aa59f674 v6.1.4 fix test suite regression with Ghostscript 9.23 2018-04-12 15:16:54 -07:00
James R. Barlow 1f7837e7b1 v6.1.4 release notes update 2018-04-12 00:55:45 -07:00
James R. Barlow ba0535e3fb Update test cache to account for unpaper --layout none change 2018-04-12 00:48:21 -07:00
James R. Barlow 49fa7f6b5c tesseract_cache: don't reveal host system file paths in manifest file 2018-04-12 00:47:28 -07:00
James R. Barlow c95db246d4 v6.1.4 merge 2018-04-11 15:58:00 -07:00
James R. Barlow 1ba93371ce docs: Update installation to reflect qpdf 7.0.0 requirement 2018-04-11 15:40:50 -07:00
James R. Barlow fedbbdb575 Travis: compile qpdf from source
The older version in Travis's Ubuntu 14.04 can't pass the test suite anymore.
2018-04-11 15:40:45 -07:00
James R. Barlow 85ebba72bc Fix setup.py syntax 2018-04-10 18:30:48 -07:00
James R. Barlow b6cd436d5d setup: Blacklist Pillow 5.1.0 on macos
https://github.com/python-pillow/Pillow/issues/3068
2018-04-10 18:15:37 -07:00
James R. Barlow ec170c7e1e Travis: use setup.py for requirements, don't override with .txt 2018-04-10 17:52:19 -07:00
James R. Barlow f6399eb90f optimize: use Leptonica to compact JPEGs
Pillow could do it too, but Leptonica is somewhat more PDF aware.
2018-04-10 17:45:05 -07:00
James R. Barlow 77f2448e59 Leptonica: add L_COMP_DATA compressed data manager 2018-04-10 17:44:03 -07:00
James R. Barlow 3d69b46fca Release notes 2018-04-10 15:53:02 -07:00
James R. Barlow 4b6153ad18 Use defusedxml for XML parsing when reading XMP 2018-04-10 14:25:13 -07:00
James R. Barlow 75d37eb103 docs: expand ocr of image usage 2018-04-09 13:06:09 -07:00
James R. Barlow 11b6f77df0 unpaper: close images on error paths 2018-04-09 13:05:12 -07:00
James R. Barlow db8b0319dd get_version: repeat system error messages if the process exists with a signal 2018-04-09 13:04:51 -07:00
James R. Barlow c9dd330766 JBIG2: refactor, don't recompress existing JBIG2 2018-04-09 13:04:10 -07:00
James R. Barlow e40228102c JBIG2: Streams created in this manner are already indirect objects 2018-04-06 17:11:17 -07:00
James R. Barlow 7889c6fb4c Parallelize JBIG2 execution with thread pools 2018-04-06 17:00:23 -07:00
James R. Barlow 6eb1773110 Fix JBIG2Globals included multiple times in output 2018-04-06 17:00:03 -07:00
James R. Barlow 1d25823746 Implement functional, single threaded optimize
Passes verapdf
2018-04-06 15:49:16 -07:00
James R. Barlow d1d4f1e198 Add issue links to release notes 2018-04-06 14:52:40 -07:00
James R. Barlow 709c01c7a1 Regroup three merge steps into a single step
All take the same inputs and deliver similar outputs, so it makes sense.
2018-04-06 01:07:02 -07:00
James R. Barlow 4a341c9034 Merge branch 'master' into feature/jbig2-2018 2018-04-05 21:29:39 -07:00
James R. Barlow be41ff6d54 Update flowchart
[ci skip]
2018-04-05 21:26:37 -07:00
James R. Barlow 1dbb6f1746 Notes on relevant envvars, repology 2018-04-05 02:15:01 -07:00
James R. Barlow 753e6274ab Tell unpaper to use --layout none so it won't blank out multi column text 2018-04-05 02:14:33 -07:00
James R. Barlow 7f462c618b v6.1.3 notes 2018-04-03 00:11:20 -07:00
James R. Barlow d8ac6e28ab Convert monochrome images to JBIG2
Awkwardly using fitz and pikepdf, transcode monochrome to CCITT.
This requires _OCRMYPDF_NO_FITZ=1.

00000x.opt.pdf can be checked for JBIG2 to confirm, but this file is
not passed to the output since it's not all wired up yet.
2018-04-03 00:00:53 -07:00
James R. Barlow a95ffcdc46 Experimental add jbig2
It appears that fitz forces conversion of jbig2 to ccitt no matter what,
so pikepdf will be needed to patch jbig2 images.
2018-04-03 00:00:53 -07:00
James R. Barlow 1b01d45dd2 Warn about Python 3.5 page count issue 2018-04-02 19:29:17 -07:00
James R. Barlow 7a1cd39b21 Fix creation date metadata lost from input
Closes #247
2018-04-02 17:53:39 -07:00
James R. Barlow 1c1fd9616a Don't depend on pytest-xdist in setup.cfg 2018-04-02 11:45:03 -07:00
Sean Whittonandjbarlow83 11e19e4085 remove addopts key from tool:pytest section of setup.cfg (#246)
The '-n' command line argument is not supported by recent pytest.
2018-04-02 14:43:38 -04:00
James R. Barlow 2a43f73228 Update installation.rst, further info on fitz 2018-04-02 11:32:57 -07:00
James R. Barlow b1d1310a75 Dockerfile: use fitz 2018-04-02 11:08:03 -07:00
James R. Barlow 0e7fa78e65 Remove inaccurate statement from setup.py 2018-04-01 13:20:17 -07:00
James R. Barlow 4032570d97 Change docs for fitz/PyMuPDF 2018-04-01 13:19:57 -07:00
James R. Barlow 90644a3017 pipeline: refactoring, use with block for images 2018-03-31 13:26:40 -07:00
James R. Barlow 4f6bffb477 Update copyrights 2018-03-31 11:54:38 -07:00
James R. Barlow 158f902c3b Fixed setup.py syntax error 2018-03-30 14:00:36 -07:00
James R. Barlow 6dc25ddc6e v6.1.2: add license to wheels, depend on defusedxml 2018-03-30 13:22:35 -07:00
James R. Barlow 7f6aaeaecf v6.1.2 2018-03-30 12:39:33 -07:00
James R. Barlow ace439910e Remove PyMuPDF 1.12.4 shim 2018-03-30 12:33:27 -07:00
James R. Barlow 7f038568de Add envvar to ease testing without PyMuPDF 2018-03-30 12:32:48 -07:00
James R. Barlow af777c0b6a Test macos without fitz too 2018-03-30 00:13:09 -07:00
James R. Barlow fc299032a4 v6.1.1 release notes
Better get the last one out
2018-03-30 00:11:52 -07:00
James R. Barlow e0f3f07907 Fix text reported as found on all pages when PyMuPDF is not available 2018-03-30 00:10:53 -07:00
James R. Barlow b36df9cf9e pdfa: codecs.encode -> hexlify (simpler) 2018-03-29 22:17:23 -07:00
James R. Barlow 81c3f780d4 Travis: Should test 3.6 Linux without fitz too 2018-03-28 23:54:43 -07:00
James R. Barlow b51efdd3e3 Travis: don't upload to legacy PyPI anymore, it will stop working soon 2018-03-28 23:40:29 -07:00
James R. Barlow 610b769df9 Update release notes 2018-03-28 23:33:34 -07:00
James R. Barlow 527f4d0101 Workaround fitz not escaping parentheses
Closes #239
2018-03-28 23:23:34 -07:00
James R. Barlow 8d9be43c60 test_bookmarks_preserved won't raise ImportError any more
Due to trapping this in ocrmypdf.lib
2018-03-28 23:22:55 -07:00
James R. Barlow 40ef4f0bbe Add new argument --skip-repair to skip the repair step 2018-03-28 00:54:58 -07:00
James R. Barlow d0271d5049 More debug messages on repair; update notes 2018-03-28 00:39:38 -07:00
James R. Barlow 5becfcf8ea Refactor fitz ImportError trap 2018-03-27 21:38:02 -07:00
James R. Barlow 112e8d6c18 Fix regression: PDF/A broken without fitz 2018-03-27 21:33:10 -07:00
James R. Barlow 1d8d49a01d Add PyMuPDF to preamble 2018-03-27 21:32:38 -07:00
James R. Barlow 5050155685 Add warning for large file size increases 2018-03-27 15:49:16 -07:00
James R. Barlow a9bd494cc0 Merge branch 'optional-fitz' 2018-03-27 13:36:33 -07:00
James R. Barlow 6a4df78bc0 Add _naive_find_text to search for text when fitz is not available 2018-03-27 13:36:17 -07:00
James R. Barlow 530eae3898 Fix test_main missing file_claims_pdfa 2018-03-26 15:33:53 -07:00
James R. Barlow 3e444f6a90 Make fitz optional 2018-03-26 13:22:09 -07:00
James R. Barlow 45dbff6401 Fix table of contents not preserved in PDF/A 2018-03-26 02:23:19 -07:00
James R. Barlow bc56b8e058 Move metadata tests to new test_metadata 2018-03-26 01:49:25 -07:00
James R. Barlow d86e315c48 v6.0.1 start release notes 2018-03-26 01:44:01 -07:00
James R. Barlow 746969207a Remove deprecated --pdf-renderer tess4, which was renamed to sandwich
Should have been cut in v6.0.0
2018-03-26 01:17:22 -07:00
James R. Barlow 1caebaefb5 tesseract: Fix FileExistsError on if output file was created at timeout 2018-03-25 21:38:20 -07:00
James R. Barlow 2d10fdcf0f Fix typo in release notes 2018-03-25 21:37:06 -07:00
James R. Barlow 355ec70a80 Note other web frontends 2018-03-25 21:36:39 -07:00
James R. Barlow a2f499de01 Remove pageinfo.py which release notes said was gone for v6 2018-03-25 12:16:56 -07:00
James R. Barlow f4bca89722 Remove Tesseract 4 message 2018-03-25 12:16:31 -07:00
James R. Barlow 9fbc69df3f v6.0.0 release 2018-03-25 01:34:26 -07:00
James R. Barlow 230d301268 conftest: py3.5 path issue 2018-03-25 00:52:45 -07:00
James R. Barlow 1ce7b02d94 Travis: don't cache tests/cache anymore, you get it with git 2018-03-25 00:52:19 -07:00
James R. Barlow a2d00f5f1d tess cache: fix tess3 error for -psm instead of --psm 2018-03-25 00:43:02 -07:00
James R. Barlow f68eaa3b46 Fix PyMuPDF version for Travis 2018-03-25 00:36:26 -07:00
James R. Barlow 0199ab220e Tweak Manifest and .travis once more
Travis "do_not_include" moving around no longer needed, thankfully.
Manifest needed LICENSE.
2018-03-25 00:19:45 -07:00
James R. Barlow 656045610a Update release notes 2018-03-25 00:17:23 -07:00
James R. Barlow 8c1c61f207 test cache: fix Path + str error 2018-03-25 00:02:03 -07:00
James R. Barlow af085b79dd Move ocrmypdf to src/ocrmypdf 2018-03-24 23:59:08 -07:00
James R. Barlow 77476965ae test cache: use .bin extension, fix .gitignore .gitattributes 2018-03-24 23:54:16 -07:00
James R. Barlow 961c1365f9 Update manifest.in 2018-03-24 23:50:58 -07:00
James R. Barlow ca51514046 Add test cache 2018-03-24 23:50:41 -07:00
James R. Barlow 8975b72a01 Fix test_testonly_pdf generating an output file in pwd 2018-03-24 22:34:35 -07:00
James R. Barlow 874ec6a87f Add missing fixture to test_unpaper 2018-03-24 22:24:14 -07:00
James R. Barlow 909eaeeead spoof: Allow tesseract cache to share cache
Previous incarnation was only suitable for generating a local cache
where the suite was executed repeatedly. Now the cache ignores
differences, so it can be checked into Github and shared.
2018-03-24 22:17:36 -07:00
James R. Barlow c138161fae Tests: more cleanup 2018-03-24 15:35:57 -07:00
James R. Barlow e48590d66c Refactor out unpaper-specific tests 2018-03-24 15:21:44 -07:00
James R. Barlow 5b1c8541fc Review some skipped tests to make sure reasons still valid 2018-03-24 15:13:23 -07:00
James R. Barlow e5e011021b Remove the OCRMYPDF_program environment variables
Really, this was just replicating the functionality of the PATH
environment variable, and users probably do that anyway.
2018-03-24 15:09:08 -07:00
James R. Barlow 11d74dea09 Remove the OCRMYPDF_program environment variables
Really, this was just replicating the functionality of the PATH
environment variable, and users probably do that anyway.
2018-03-24 15:07:02 -07:00
James R. Barlow cbdf9c88c5 Update requirements 2018-03-24 14:03:34 -07:00
James R. Barlow 46601b1350 setup: skip 1.12.4.1 since it does not provide wheels 2018-03-24 02:59:58 -07:00
James R. Barlow 6f1a40b2ca v6.0.0 notes, build machinery changes 2018-03-24 02:52:56 -07:00
James R. Barlow a2b1f54eb2 Update documentation license info 2018-03-24 02:33:24 -07:00
James R. Barlow 6756016572 Add license notice to all files
Source files to GPL3

Exceptions:
-tests/spoof/* to MIT
-hocrtransform.py
-_unicodefun.py

Test resources to CC BY-SA 4.0 except when otherwise noted.

Add GPL license.
2018-03-24 02:33:24 -07:00
James R. Barlow f42123afc3 pipeline: make removal of merge_qpdf more explicit 2018-03-24 02:30:05 -07:00
James R. Barlow 1425ffd274 pipeline: Merge branch 'feature/mumerge' into test
Replaces qpdf page merging
2018-03-24 02:26:01 -07:00
James R. Barlow d700154e0e Fix regressions after --skip-text improvements 2018-03-24 02:24:45 -07:00
James R. Barlow efecf42566 Add PyMuPDF and use to detect text on pages 2018-03-24 02:16:53 -07:00
James R. Barlow 74bdfc07fb mumerge: fix regressions 2018-03-24 01:18:22 -07:00
James R. Barlow 376dfdba1c Fix text/image files not closed in combine_layers 2018-03-23 13:48:37 -07:00
James R. Barlow 3795d6720f Try out pymupdf merging
With garbage collection it reduces waste on the worst case file.
That's nice. 1 MB -> 105 MB -> 1.5 MB.

Indicates really problem is using PyPDF2 to watermark.

Currently hacked into --output-type pdf.
2018-03-23 13:39:16 -07:00
James R. Barlow 537aaf56d7 Remove duplication between page merge functions 2018-03-23 13:38:53 -07:00
James R. Barlow 34d51b5d3d Merge branch 'feature/faster-split' 2018-03-23 13:10:53 -07:00
James R. Barlow dea8fcfb5b Optimize page splitting by multiprocessing
Previously page splitting occurred in a single process because it was
not believed to affect performance much. It turned out to be an expensive
operation.

It now scales better with large page sizes although this has a negative
effect on small files.

Overall time changes as follows:

7 page file, 9.02s -> 9.56s
731 page file, 213s -> 97s

WITH --tesseract-timeout 0 --output-type pdf --skip-text

i.e. you don't get a 2.2x speed gain when OCR is available.

Squashed a commit to ix test suite failure on --rotate-pages
Squashed a commit to remove debug code
2018-03-23 13:07:51 -07:00
James R. Barlow 4f1f3b9b51 Move available_cpu_count to helpers 2018-03-23 13:07:51 -07:00
James R. Barlow dfeb8812ad Document some instances of 0 vs 1-based page numbering, import cleanup 2018-03-23 13:07:31 -07:00
James R. Barlow 63e2b4273a Travis: avoid using set -e since it interferes with Travis
https://github.com/travis-ci/docs-travis-ci-com/issues/1672
2018-03-23 12:49:12 -07:00
James R. Barlow 5790dbc085 Merge commit '9e2105e08d5fc765dbf636d108809bb66ab562a5' 2018-03-20 18:15:57 -07:00
jbarlow83andGitHub 9e2105e08d Update readme shields
Drop Docker Hub for now, add homebrew
2018-03-20 17:16:28 -07:00
James R. Barlow 22582bbd1c Travis: don't trigger Docker Hub anymore
Docker Cloud is set up to build on pushes to master and tagged releases.
Hopefully that will work out.
2018-03-19 21:09:31 -07:00
James R. Barlow e5f27b7a12 Solve text detection issue with PyMuPDF 2018-03-15 22:29:56 -07:00
James R. Barlow e88ec9822b Tweak release notes 2018-03-15 17:09:43 -07:00
James R. Barlow 5ffd2f5c96 Not ending Py3.5 support just yet 2018-03-15 17:06:11 -07:00
James R. Barlow 11fdb4c5d8 Update release notes for v5.7.0 2018-03-15 17:06:04 -07:00
James R. Barlow 319aff6d09 Merge better-hocr 2018-03-15 16:59:59 -07:00
James R. Barlow a614fa3400 hocr: simplify some math expressions and add comments 2018-03-14 17:05:40 -07:00
endolithandjbarlow83 8d691391ac Fix typos in advanced.rst (#228) 2018-03-14 15:54:55 -04:00
James R. Barlow 0089a84c94 hocr: Make interword spaces default and non-optional for hocr
Update documentation to match.
2018-03-13 14:51:47 -07:00
James R. Barlow 90676e1c6a hocr: Remove baseline dashes 2018-03-13 14:45:31 -07:00
James R. Barlow 062901be43 Some cleanup and variable renaming 2018-03-13 14:29:22 -07:00
James R. Barlow b195d79b50 Refactoring 2018-03-13 11:04:34 -07:00
James R. Barlow 6d7ee98721 Force Tesseract 4 to be single threaded
Gives better performance (throughput basis) than the existing solution
and scales better on powerful boxes.
2018-03-13 08:54:52 -07:00
James R. Barlow fc0800ed5d v5.6.3 notes 2018-03-12 03:41:12 -07:00
James R. Barlow f4e3a0e5b2 v5.6.2 notes 2018-03-09 15:37:08 -08:00
James R. Barlow d631c80024 Suppress debug message when merging large files 2018-03-09 11:10:45 -08:00
James R. Barlow f1f0033875 Suppress spurious debug message in --output-type pdf 2018-03-09 11:07:28 -08:00
James R. Barlow 84d120e850 v5.6.1 notes 2018-03-09 08:00:42 -08:00
James R. Barlow 8159cc6b88 Skip one test that fails for qpdf 8.0.[0,1], due to qpdf regression 2018-03-09 07:57:22 -08:00
James R. Barlow 995f8c106b hocr: account for baseline offset to position text more accurately 2018-03-09 07:45:41 -08:00
Jim Barlow 7cc104b138 hocr: account for skewed baseline 2018-03-05 11:16:40 -05:00
James R. Barlow b3a7299a62 hocr: refactor/improve PEP8 a bit 2018-03-05 10:47:36 -05:00
James R. Barlow 0e7a4deaec hocr: add baseline function, hocr doc link 2018-03-05 10:47:36 -05:00
James R. Barlow b4d66650bd hocr: adjust text cursor with relative moves 2018-03-05 10:47:36 -05:00
James R. Barlow 4986afca28 hocr: Refactor use of text object
We don't need to declare the font on each word.

No improvement for removing trailing space or adding \n
2018-03-05 10:47:36 -05:00
James R. Barlow 2b6004a82b hocr: Make words on line use the line height
Seems to improve the behavior and appearance
of selected text a fair bit.
2018-03-05 10:47:36 -05:00
James R. Barlow 04c54a7c31 Suppress spurious debug message in --output-type pdf 2018-03-05 10:47:36 -05:00
James R. Barlow 7ae6c5ae87 Trial merge interword-spaces 2018-03-02 23:47:06 -08:00
James R. Barlow 4046766ca5 Fix Python 3.5 test suite failure on symlinks
Did not account for API difference in pathlib
2018-03-02 16:57:46 -08:00
James R. Barlow 810390df0b pipeline: Refactor duplicate with clause 2018-03-02 16:57:15 -08:00
James R. Barlow de749bc7ae Fix regression - output to stdout broken 2018-03-02 15:35:51 -08:00
James R. Barlow 9965b8800c Move Dockerfiles out of the way 2018-03-02 15:26:41 -08:00
James R. Barlow ab870fddd6 Dockerignore: glob supported now 2018-03-02 15:24:42 -08:00
James R. Barlow a79d6807cf Dockerfiles: remove deprecated MAINTAINER tag 2018-03-02 15:24:28 -08:00
James R. Barlow 8185fb7e43 Migrate Travis CI setup to Brewfile 2018-03-02 15:16:05 -08:00
Tucker Barbourandjbarlow83 4b10929b25 Fix Homebrew python package (#227)
Homebrew removed python3 and python now defaults to version 3. Here we
use `brew upgrade python` to upgrade the pre-installed version of
python to python3.
2018-03-02 14:59:23 -08:00
Tucker Barbour f6c70312c9 Fix Homebrew python package
Homebrew removed python3 and python now defaults to version 3. Here we
use `brew upgrade python` to upgrade the pre-installed version of
python to python3.
2018-03-02 14:26:13 +00:00
Tucker Barbour 9fd9c7a51f Scale BoundingBox and Text elements to account for additional space.
Here we are manually scaling the pt width used for the BoundingBox and
the Text element when manually adding whitespace to account for
limitations of the PDF.js viewer. This fixes an initial regression
noticed when selecting text elements in Chrome and PDFium. The width
of the Text element and BoundBox had not been adjusted for the
additional whitespace so the highlighting was offset slightly.
2018-03-02 11:18:47 +00:00
Charles Forcey 422e619978 Add a note to the documentation about interword-spaces 2018-03-01 13:15:03 -05:00
Tucker BarbourandCharles Forcey e6e34251c6 Add option to explicitly add interword spaces to HOCR pdf-renderer
This commit includes an optional work around for limitations of the
PDF.js viewer described in
https://github.com/jbarlow83/OCRmyPDF/issues/133. Here is explicitly
add an addition space to text elements before drawing them on the PDF
canvas when using the HOCR renderer. This option does not apply to
other pdf renderers in OCRmyPDF and is turned off by default.
2018-03-01 13:15:03 -05:00
James R. Barlow 2d8aad1086 Improve docs 2018-03-01 00:24:38 -08:00
James R. Barlow 74ca736333 Issue #223: improve text of encrypted PDF error message 2018-02-27 15:08:22 -08:00
James R. Barlow 5e4fd8b0b9 compile_leptonica should have shebang 2018-02-24 12:49:36 -08:00
James R. Barlow 8ab8132411 lint: unused variables, wildcard imports 2018-02-24 12:48:52 -08:00
James R. Barlow 6899dd46e4 lint: remove extraneous backslash 2018-02-24 12:42:37 -08:00
James R. Barlow 8ad0697a20 lint: remove duplicate property definition 2018-02-24 12:42:03 -08:00
James R. Barlow b47e5672e6 Remove old test case that no longer works 2018-02-24 12:40:14 -08:00
James R. Barlow 45c7bd9a60 lint: Remove shebangs from non-executable files 2018-02-24 12:38:58 -08:00
James R. Barlow e7bcb95635 Fix pylint errors 2018-02-24 11:59:01 -08:00
James R. Barlow 3de83627a9 Handle output to /dev/null or directory (#219)
Previously we threw an exception if the output name was a directory (only after doing OCR) and would trigger a PermissionError on trying to flip permission bits of /dev/null due to shutil.copyfile implementation. Instead of copying file use shutil.copyfileobj which should also respect umask etc.
2018-02-19 22:15:07 -08:00
James R. Barlow f248576994 Change instructions to point away from private tap 2018-02-19 17:33:58 -08:00
James R. Barlow aac5b6de3b Update autobrew script to match final changes 2018-02-17 00:12:03 -08:00
James R. Barlow 24435f11e0 We are now in homebrew 2018-02-15 17:42:16 -08:00
James R. Barlow a9da839c39 Add vector-only PDF test case 2018-02-08 00:17:35 -08:00
James R. Barlow fa2c0296d6 v5.6.0 release notes, docs 2018-02-07 16:48:04 -08:00
James R. Barlow 1dfc32d7e6 Preserve "text as curves" vector content
Never updated the checking logic to deal with a pure vector file with no text that needs an OCR layer. This is doable, so allow it.
2018-02-07 16:05:48 -08:00
James R. Barlow 4a61beae41 autobrew: use homebrew's built-in test fixture 2018-02-05 11:10:33 -08:00
James R. Barlow bd30587bf1 Update depends_on order 2018-01-29 13:16:07 -08:00
James R. Barlow e0070e3e18 Update Dockerfile to use Ubuntu 17.10 (issue #214) 2018-01-28 15:30:41 -08:00
James R. Barlow 019513696b Ghostscript spoof scripts did not report their --version correctly 2018-01-10 17:08:14 -08:00
James R. Barlow ad7a4476db hugemono.pdf needs --max-image-mpixels to pass with Pillow 5.0 2018-01-10 16:55:18 -08:00
James R. Barlow 4812b20fb2 Fix tesseract_noop.py generating wrong size of output PDF in tests
This caused trouble before with test_deskew
2018-01-10 16:35:31 -08:00
James R. Barlow f5e07c9427 Fix Ghostscript parameter order 2018-01-10 16:33:26 -08:00
James R. Barlow 75dcb90621 Niceties: when environment variable overload is used clarify we're not checking the PATH 2018-01-10 16:33:03 -08:00
James R. Barlow dfc0434cc2 Update requirements to set Pillow to 5.0 2018-01-10 15:45:23 -08:00
James R. Barlow 882fc2257c Add --max-image-mpixels argument to support Pillow 5.0 2018-01-10 15:43:59 -08:00
James R. Barlow 41e83b52fc Document process for redoing OCR 2018-01-10 15:39:58 -08:00
James R. Barlow 47758b4d8f Reactivate autobrew 2018-01-10 15:39:36 -08:00
James R. Barlow 6bf1f970a0 Fix some parameter validation for --output-type pdfa-1 and pdfa-2 2018-01-10 11:50:08 -08:00
James R. Barlow 7d451f101f Detect old versions of Ghostscript and warn about them (#208) 2018-01-10 11:47:39 -08:00
James R. Barlow 7edbfe0e40 Update autobrew template 2018-01-09 12:36:14 -08:00
James R. Barlow 91b42cbfa8 Fix issue in sandwich renderer when skipping OCR on a rotated and deskewed page
If OCR is skipped due to --tesseract-timeout or similar, and the skip page is rotated with /Rotate, and the skip page was deskewed or had other image processing, then the skip page was created with the wrong dimensions causing the output page to be cropped.
2018-01-09 00:17:53 -08:00
James R. Barlow 6907df41b4 Disable autobrew until homebrew accepts the official release 2018-01-08 23:26:58 -08:00
James R. Barlow 2cebd90cbd Fix brew audit --strict warnings 2017-12-09 12:14:28 -08:00
James R. Barlow 376a121aaa Re-enable macos 2017-11-29 15:06:19 -08:00
James R. Barlow da11fd17ee qpdf dummy: needs to return version now 2017-11-29 14:35:37 -08:00
James R. Barlow a40689a0ff tesseract: handle return of bytes properly in error cases 2017-11-29 14:35:26 -08:00
James R. Barlow 44a45fc3fb Add "bad UTF8 output from Tesseract" test 2017-11-29 14:08:07 -08:00
James R. Barlow ec4bb5359a Read tesseract's output as binary to avoid UnicodeDecodeErrors if it messes up 2017-11-29 13:44:40 -08:00
James R. Barlow d2217632df Rename _verify_python3_env 2017-11-29 13:43:18 -08:00
James R. Barlow 2cc044feed Move qpdf complaint to after options checking so that it won't break ocrmypdf --version 2017-11-29 13:42:55 -08:00
James R. Barlow 64fd0cb54f Remove test_qpdf.py only from travis 2017-11-29 12:54:48 -08:00
James R. Barlow c5a1d22e81 That fixed it. Complain about old versions of qpdf now 2017-11-29 12:53:34 -08:00
James R. Barlow a7b307af04 Looks like issue was negzero.pdf with qpdf 5.1.1 on travis, which is why osx passes
Reorganize and see if this is better now
2017-11-29 12:47:09 -08:00
James R. Barlow 3269eba16c Is it negzero.pdf? 2017-11-29 12:02:37 -08:00
James R. Barlow d472860e3b Try to diagnose travis-only failure of qpdf test 2017-11-29 11:41:06 -08:00
James R. Barlow 731c9ea55e Set timeouts on the tests that seem to be stalling on travis (but not elsewhere) 2017-11-27 14:46:10 -08:00
James R. Barlow 0b6af8d965 Clarify hocrtransform license/copyright 2017-11-27 13:41:46 -08:00
James R. Barlow 92ca9e954c Fix test warning/failures, hopefully 2017-11-27 13:41:32 -08:00
James R. Barlow 67aa00e2c5 Remove py3.5 deprecation
It'll be in Debian stable for a long time, so maybe not such a good idea
2017-11-27 01:10:34 -08:00
James R. Barlow a1bf161eb7 Update v5.4.4 release notes 2017-11-27 01:09:35 -08:00
James R. Barlow 56614fcaa4 Add support and tests for handling page count > ulimit - fixes issue #181 2017-11-27 00:32:35 -08:00
James R. Barlow 4d9169e15f Add merge ulimit test case 2017-11-26 23:34:36 -08:00
James R. Barlow 3a167af2c4 Nearly smallest possible PDF-1.3 with all required fields 2017-11-26 23:32:21 -08:00
James R. Barlow 64b9005030 v5.4.4 release notes; mention deprecation of 3.5 2017-11-26 23:08:55 -08:00
James R. Barlow 965de3a235 Test case for issue #200 2017-11-26 22:52:53 -08:00
James R. Barlow 2040ae4856 Fix issue #200, uncommon but valid decimal syntax treated as error
Also replace check_output() calls with run() in qpdf.py
2017-11-26 22:52:43 -08:00
James R. Barlow 31a1f9f5ea pytest-runner should be a setup requirement 2017-11-26 22:51:51 -08:00
James R. Barlow 7eadd82048 Update docker script based on uid issue
issue #180, fix reported by @Atredis76
2017-11-26 15:11:17 -08:00
James R. Barlow 31d0eaac8e Ensure intermediate metadata holder PDF has same version as its input file
While not known to cause problems, the absence of this could confuse parsers
2017-11-24 00:09:13 -08:00
James R. Barlow f45d012635 Update docs: security, pip install --user 2017-11-21 16:50:14 -08:00
James R. Barlow 5a777ee9bc v5.4.3 release notes 2017-11-17 02:28:02 -08:00
James R. Barlow 7bbf6bc7f4 Travis didn't like LANG, use LC_ALL 2017-11-16 20:37:30 -08:00
James R. Barlow 9bfc45702d Update copyright in license 2017-11-16 17:18:29 -08:00
James R. Barlow 40aa82ab41 Check that the locale is sane before allowing OCR to proceed 2017-11-16 17:18:02 -08:00
James R. Barlow 5d2c67c62b Declare in yet another place that this shouldn't run on py2 2017-11-16 14:54:28 -08:00
James R. Barlow a00ef4836d autobrew: declare conflict with mactex
As reported in https://github.com/jbarlow83/homebrew-ocrmypdf/issues/4#issuecomment-345029899
2017-11-16 14:53:42 -08:00
James R. Barlow 3ef766bb93 Remove bare 'except:' 2017-11-01 01:52:13 -07:00
James R. Barlow 44b5a18462 Declare our __version__ properly 2017-11-01 01:49:51 -07:00
James R. Barlow 4df716f0e8 Update requirements.txt 2017-11-01 01:49:35 -07:00
James R. Barlow fcbf34a4d3 Refactor obtaining version from subprocesses
Issue #196 raised the need to deal with linker warnings on --version.
2017-11-01 01:44:36 -07:00
James R. Barlow c7b8b6e18b Fix issue #194 - --sidecar creates blank txt file 2017-10-26 18:15:31 -07:00
James R. Barlow 9fd165bf4b Add 'zlib' to travis recipe 2017-10-26 18:06:12 -07:00
James R. Barlow 2061092ec5 Describe how to stream with Docker
[ci skip]
2017-10-18 12:37:27 -07:00
James R. Barlow 57466a31ba v5.4.1 release notes 2017-10-12 14:04:45 -07:00
James R. Barlow 4213c69bef Try to make travis actually deploy macOS 2017-10-12 12:45:40 -07:00
James R. Barlow cc5578488a Remove workaround for OSD crash and use explicit -l osd
Suggested by amitdo in https://github.com/tesseract-ocr/tesseract/issues/1167
2017-10-12 12:45:18 -07:00
James R. Barlow 4b7135f0e5 Add option to produce PDF/A-1B 2017-10-11 14:32:58 -07:00
James R. Barlow 984df023b1 Fixup manifest 2017-10-11 13:12:14 -07:00
James R. Barlow 6f8682f558 Track .coveragerc for future reference 2017-10-11 13:05:19 -07:00
James R. Barlow 51defa6d66 Import cleanup and some pylint fixes 2017-10-11 12:58:35 -07:00
James R. Barlow 7d73098d6e ghostscript.py: fix missing imports 2017-10-11 12:39:16 -07:00
James R. Barlow c580aa4683 Revert to pre-build stages travis.yml
It's still in beta for a reason apparently
2017-10-11 11:32:47 -07:00
James R. Barlow 47d1199360 Fix travis.yml typo 2017-10-11 11:26:08 -07:00
James R. Barlow 61ab435b31 Try disabling the matrix and osx for build stages 2017-10-11 11:18:32 -07:00
James R. Barlow aa8f534b45 pipeline: fix variable not defined in __init__ 2017-10-11 11:17:33 -07:00
James R. Barlow dc9589aca8 travis: Explicitly set python versions 2017-10-11 00:37:13 -07:00
James R. Barlow dd6eaacc6b More travis build tweaks 2017-10-11 00:28:33 -07:00
James R. Barlow 37dc03eec6 Add workaround for tess4 form feed behavior change 2017-10-10 23:33:54 -07:00
James R. Barlow 6b478172f6 Try using plain tessdata instead of tessdata_best 2017-10-10 20:52:03 -07:00
James R. Barlow c6e73bcfd6 Still failing, did the cp work at all? 2017-10-10 15:15:26 -07:00
James R. Barlow a2d62938ce cp overwrite needs sudo 2017-10-10 14:54:52 -07:00
James R. Barlow d7ae1f3cca travis: Replacement problematic traineddate file 2017-10-10 14:39:05 -07:00
James R. Barlow 70219581c4 Disable tesseract 4 so tests can succeed
tess4 -psm 0 is broken right now
2017-10-10 14:18:02 -07:00
James R. Barlow f70ac9fb89 Workaround travis issues in build stages... maybe 2017-10-10 12:36:50 -07:00
James R. Barlow 235b9fbaf0 Resolve merge conflicts 2017-10-10 12:24:10 -07:00
James R. Barlow ebda7f42db travis: need script for each stage 2017-10-10 12:22:23 -07:00
James R. Barlow 0b04e4b977 Try out travis build matrix 2017-10-10 12:14:50 -07:00
James R. Barlow 9498601a37 Add docs on adding to docker iamge 2017-10-10 12:13:20 -07:00
James R. Barlow ef5d320e06 Ignore .vscode too 2017-10-09 16:19:41 -07:00
James R. Barlow b00c9a562d Remove meaningless version from Dockerfile.polyglot 2017-10-09 16:18:02 -07:00
James R. Barlow 5372656893 Don't say tess4 support is experimental - it's pretty good now 2017-10-09 16:17:42 -07:00
James R. Barlow 571de0e368 Update release notes 2017-10-08 12:41:03 -07:00
James R. Barlow 82cea2fd85 Update batch processing docs to include Synology script 2017-10-08 12:34:36 -07:00
James R. Barlow aed9814345 Use Ubuntu 17.04 instead of 16.10 for Docker image (issue #191)
Due to 16.10 PPAs no longer being generated by alex-p
2017-10-08 12:13:20 -07:00
James R. Barlow 34fc1f5fd7 Add reminder that blank.pdf is not trivial 2017-09-13 01:19:18 -07:00
James R. Barlow 87c2ed8b27 Improve clarity of --pdf-renderer=tesseract deprecation warning 2017-09-12 14:34:53 -07:00
James R. Barlow 1467d118ab Add more leptonica functions 2017-09-06 00:27:02 -07:00
James R. Barlow 922dbe83c3 Update MANIFEST rules 2017-09-02 20:05:57 -07:00
James R. Barlow 6af7d61ee5 Fix CI failure due to spoofers not being updated to Tesseract 3.05 strings 2017-09-01 16:17:26 -07:00
James R. Barlow bafd08391d Update release notes 2017-09-01 12:50:45 -07:00
James R. Barlow 82ebd8ef1a Fix missing error message about trying to use sandwich on old tesseract 2017-09-01 12:50:36 -07:00
James R. Barlow 4ed1aa4d23 Release notes: fix indentation 2017-09-01 12:47:22 -07:00
James R. Barlow d04e43d46d Update copyright info for test files
[ci skip]
2017-09-01 01:00:32 -07:00
James R. Barlow 952f0cca15 Dockerfiles: set LANG=C.UTF-8
Issue #184 to avoid issue with printing UTF-8 text to sidecar
2017-08-30 13:25:54 -07:00
James R. Barlow f6a4d8f1f8 Fix Ubuntu 14.04 install instructions to account for dropping Py3.4 support
[ci skip]
2017-08-27 13:53:36 -07:00
James R. Barlow b3097a2384 Fix broken test case related to language packs 2017-08-24 13:01:02 -07:00
James R. Barlow 6d9ddbe98b v5.3.1 notes 2017-08-24 01:09:19 -07:00
James R. Barlow 9bb42c0229 Wrong error type used for missing language 2017-08-24 01:07:23 -07:00
James R. Barlow bd7226b27a Merge branch 'master' of github.com:jbarlow83/OCRmyPDF 2017-08-23 23:30:19 -07:00
James R. Barlow 5b413e3873 Cookbook: add "don't OCR" examples 2017-08-23 23:29:41 -07:00
James R. Barlow be5831a629 Offer the readme as a long description for new PyPI 2017-08-23 23:29:21 -07:00
jbarlow83andGitHub 084d2bf8e2 More badges 2017-08-23 23:19:29 -07:00
James R. Barlow da79e6bac7 macos: Skip brew audit because it seems to crash ruby on travis 2017-07-27 16:00:41 -07:00
James R. Barlow c4831ac00c v5.3 release notes 2017-07-27 00:11:12 -07:00
James R. Barlow 93a954ef9f Fix missing import for Py3.5 2017-07-26 23:40:01 -07:00
James R. Barlow f7ce8f44e9 Weaken the --user-words test so it will pass on Travis 2017-07-26 21:03:51 -07:00
James R. Barlow 0b012697e5 Whitelist the Latin-1 languages that work with HOCR
Omitted French because the rare 'oe' and 'ÿ' glyphs are not in Latin-1.
Basically steer people away from HOCR renderer but avoid a potential
disruptive behavior change.
2017-07-26 21:03:18 -07:00
James R. Barlow 58e357c992 Report location of attempted output_file that fails to write 2017-07-22 17:49:56 -07:00
James R. Barlow 71fbad83ad Fix py3.5 test 2017-07-21 17:01:06 -07:00
James R. Barlow 52483072dc Add a differential test that checks tesseract uses supplied word list 2017-07-21 16:40:20 -07:00
James R. Barlow 7f0b8621f3 Tests: accept rich path objects without having to str() everything 2017-07-21 16:39:22 -07:00
James R. Barlow cd8db60b06 Crash test all renderers, not just two 2017-07-21 14:10:02 -07:00
James R. Barlow 1aa34f5d2e Make some interfaces accepting of both str-paths and Path objects 2017-07-21 13:28:30 -07:00
James R. Barlow dfa1d88ce9 Fix missing user_words/user_patterns from textonly_pdf case 2017-07-20 17:14:04 -07:00
James R. Barlow dd38519f07 Merge branch 'feature/user-words' into develop
# Conflicts:
#	ocrmypdf/exec/tesseract.py
2017-07-20 16:25:20 -07:00
James R. Barlow 098f5d4f0b docs: remove deprecated example of pdftotext 2017-07-20 16:20:17 -07:00
James R. Barlow ffc685d536 docs: envvar markup 2017-07-20 16:19:57 -07:00
James R. Barlow cd1a99a0de Refactor int(os.path.basename(s)[0:6]) -> page_number(s) 2017-06-26 13:29:40 -07:00
James R. Barlow 48e3b267fc Accept PDFs with whitespace ahead of %PDF marker
Noticed in @aagahi 's fork
2017-06-26 13:17:47 -07:00
James R. Barlow 3a7c3417bb Don’t check tags and branch at the same time as Travis doesn’t get this
Travis is weird
2017-06-13 13:14:34 -07:00
James R. Barlow d792ef7222 Give the ‘auto’ renderer setting more test covfefe 2017-06-13 13:13:58 -07:00
James R. Barlow 2c24f67deb Rename “tess4” renderer to “sandwich” and make it default in Tess 3.05.01
Tesseract 3.05.01 backported the textonly_pdf=1 which allows the use
of this superior PDF renderer prior to 4.00 alpha. This means that
the tess4 name is no longer accurate, so call it a sandwich because of
its merge-preserve characteristic. Preserve the tess4 name. Fix the
documentation and tests to reflect this.

Make it the default, because it’s better. It does not have the issues
the “tesseract” renderer does prior to Tess 3.05.00 with rendering
PDFs that Ghostscript corrupts, and it produces better output without
re-rastering.

Deprecate some old stuff to avoid the test suite growing obscenely
large.
2017-06-13 13:09:12 -07:00
James R. Barlow 9e75e28d0c Homebrew needs x11 to compile Pillow 2017-06-13 11:03:26 -07:00
James R. Barlow 3232643809 Support “textonly PDF” renderer in Tesseract 3.05.01 2017-06-13 10:18:08 -07:00
James R. Barlow f7ee9e90ce Document what is meant by the ocrmypdf “API” 2017-06-13 10:15:11 -07:00
James R. Barlow 47298be132 Remove Python <3.5 test 2017-06-13 10:14:28 -07:00
James R. Barlow a88fa83515 Travis: fix deploy conditions for homebrew autobrew 2017-05-31 02:29:32 -07:00
James R. Barlow 12bfe20385 v5.1 release notes 2017-05-29 14:36:50 -07:00
James R. Barlow 3d2f6f0772 Fix tess4 test using old-style pageinfo API 2017-05-29 13:51:21 -07:00
James R. Barlow 1cb607f64b Merge UserUnit 2017-05-29 13:22:55 -07:00
James R. Barlow d3c54fbbde For —rotate-pages, rasterize preview at half DPI instead of 200 DPI
Ensures that time is not wasted on previews at higher resolution than
the input as was sometimes the case
2017-05-29 13:01:18 -07:00
James R. Barlow 28341b755f Refactor common test fixtures 2017-05-29 12:47:55 -07:00
James R. Barlow 4b5cd420e1 Add new test file 2017-05-29 12:16:08 -07:00
James R. Barlow 1d57bcc99e Fix Ghostscript rasterizing of UserUnit pages and related sizing issues 2017-05-29 12:14:10 -07:00
James R. Barlow facdd13879 Ghostscript: refactor image output resizing 2017-05-29 11:42:27 -07:00
James R. Barlow 6e891f91d3 ghostscript, qpdf: Restore API backward compatibility 2017-05-29 11:13:06 -07:00
James R. Barlow 9b50ede977 Partially solve ghostscript rasterize_pdf producing wrong file size
Kludge. Assumes JPEG for now. Messy.
2017-05-25 01:17:43 -07:00
James R. Barlow 82cf010333 Error out if trying to produce PDF/A >200” due to Ghostscript limitation 2017-05-25 00:07:29 -07:00
James R. Barlow 6ff6c8614f —output-type=pdf now outputs /UserUnit PDFs at the correct size
This currently distorts the output size because Tesseract assumes it
 knows the DPI better than we do.

Does not work for Ghostscript, because it emerges that Ghostscript
honors /UserUnit for rasterizing but not in pdfwrite (resolve/wontfix).

https://bugs.ghostscript.com/show_bug.cgi?id=690781

Ghostscript’s output would need to be patched in a PDF/A safe way for
this to work. Temporary route may be to block Ghostscript if
/UserUnit.
2017-05-24 23:26:07 -07:00
James R. Barlow eb1cd38f6c Add an open helper that is compatible with pathlib 2017-05-24 16:19:15 -07:00
James R. Barlow 148b632b4f Prove multiprocessing works, although it is still racy in some places 2017-05-23 16:32:13 -07:00
James R. Barlow 591e213713 Add more dependencies for autobrew 2017-05-23 13:52:28 -07:00
James R. Barlow 75f2262659 Ensure JobContext stuff is actually tested for IPC consistency 2017-05-19 17:57:07 -07:00
James R. Barlow d9005a1074 pdfinfo: replace most remaining dict-style access 2017-05-19 16:17:36 -07:00
James R. Barlow 3e73fa81bf pageinfo: deprecation warning 2017-05-19 16:17:07 -07:00
James R. Barlow ba6e290231 Restore old pageinfo.py to avoid breaking compatibility 2017-05-19 15:49:23 -07:00
James R. Barlow 08e47117a3 Rename pageinfo to pdfinfo 2017-05-19 15:48:23 -07:00
James R. Barlow 532ef38157 /UserUnit is a scalar, not an array 2017-05-19 14:19:50 -07:00
James R. Barlow 4c09875890 docs: upload unpaper Dropbox link, .rst typo blocking macOS install
[ci skip]
2017-05-19 12:18:09 -07:00
James R. Barlow 0e98139712 Upload to upload.pypi.org/legacy as recommend by PyPA
https://github.com/pypa/warehouse/issues/1996#issuecomment-302784126
2017-05-19 12:06:24 -07:00
James R. Barlow 4c04d802d7 Introduce /UserUnit checking 2017-05-19 12:01:19 -07:00
James R. Barlow b3dc404571 Update unpaper.deb link (fixes #171)
*Shakes fist a Dropbox*
2017-05-19 11:28:45 -07:00
James R. Barlow 8694f8d2eb Replace magic strings colorspace and encoding with Enums 2017-05-18 22:32:27 -07:00
James R. Barlow 263f9b79f4 pageinfo: debug stuff 2017-05-18 21:52:55 -07:00
James R. Barlow 56d2aae963 Refactor from ImageInfo index to attribute accessing 2017-05-18 18:39:14 -07:00
James R. Barlow 127706153d Refactor dictionary based image info to ImageInfo 2017-05-18 18:26:31 -07:00
James R. Barlow caee5b1428 Access PageInfo instance variables instead of dictionary 2017-05-18 17:12:04 -07:00
James R. Barlow 6c12e7e944 Refactor pageinfo dictionary to PageInfo() 2017-05-18 16:53:38 -07:00
James R. Barlow cd04ae6949 Refactor PdfInfo(str(filename)) -> PdfInfo(filename) 2017-05-18 16:43:50 -07:00
James R. Barlow 6a0b68298f Refactor pdf_get_all_pageinfo to PdfInfo 2017-05-18 16:31:18 -07:00
James R. Barlow 0a2f732267 docs: Fix restructured text typos 2017-05-16 23:27:10 -07:00
James R. Barlow 4bade99f27 docs: Remark that someone got bash on Windows working 2017-05-16 23:24:34 -07:00
James R. Barlow 0b048cd24e Join the build badge club 2017-05-16 23:24:05 -07:00
James R. Barlow c69ee63d82 Travis, true is a program, not a keyword 2017-05-15 15:12:14 -07:00
James R. Barlow 744fa104d7 v5.0.1 release notes (anticipating) 2017-05-14 23:59:09 -07:00
James R. Barlow e24ff0fd64 Travis: don’t update the homebrew version because we pushed to testpypi 2017-05-14 23:55:40 -07:00
James R. Barlow 5de107d44c tesseract_cache: update explanatory notes 2017-05-14 23:54:09 -07:00
James R. Barlow 131a5b741d tesseract.py: update canned HOCR template to tess 3.05 output
Seems better to not claim the existence of several entities that don’t
exist as the older one does
2017-05-14 23:40:09 -07:00
James R. Barlow 65b89687a9 ghostscript: fix missing “import sys”, only applicable for an exception 2017-05-14 23:38:52 -07:00
James R. Barlow 048ae40e75 Update copyrights 2017-05-14 23:38:28 -07:00
James R. Barlow 234183ecd2 Fix: Tesseract 3.04 is sensitive to order of configuration commands
“txt hocr” is not acceptable and does not produce expected output .txt
while “hocr text” works fine, so switch the order everywhere.

Should fix #169
2017-05-14 23:27:46 -07:00
James R. Barlow fb067dc97b cookbook: more on improving OCR 2017-05-14 23:16:47 -07:00
James R. Barlow a1fea0ce16 docs: link to OCRmyPDF-web 2017-05-14 23:16:30 -07:00
James R. Barlow e1e9135e93 Test suite: tidy up imports 2017-05-14 23:15:29 -07:00
James R. Barlow aff982036b autobrew: fix brew audit error on double blank line 2017-05-14 23:15:02 -07:00
James R. Barlow d087649eab Remove “null deploy script” since “/usr/bin/true” is equivalent 2017-05-12 15:37:02 -07:00
James R. Barlow 7f3fa46a40 v5.0 release notes 2017-05-12 14:14:28 -07:00
James R. Barlow b1f79e4d97 Disable other use redo_ocr 2017-05-12 13:24:30 -07:00
James R. Barlow 115d6df94f Warn user when —image-dpi is supplied but ignored 2017-05-12 12:09:53 -07:00
James R. Barlow 559af9635f —redo-ocr is not implemented, so disable 2017-05-12 12:08:16 -07:00
James R. Barlow cb06359c0b Turn on Tesseract 4 cache in test suite
Travis is too slow without it, and perhaps it’s overly paranoid to
never cache Tess4. Maybe nuke the cache occasionally to be safe…
2017-05-12 11:42:27 -07:00
James R. Barlow 5e26bb29d9 Update requirements files 2017-05-12 11:41:15 -07:00
James R. Barlow b0e95842b8 Fix Travis CI errors while looking around for Tess4 2017-05-12 00:40:00 -07:00
James R. Barlow 08e678f21f rst: Clean up indentation 2017-05-12 00:12:06 -07:00
James R. Barlow c17817810f Update documentation for 3.03 support removal 2017-05-12 00:08:22 -07:00
James R. Barlow ff5c38b1f7 Tell Travis to download Tesseract 4.00 from a PPA for testing 2017-05-11 23:52:13 -07:00
James R. Barlow 64314c1b82 Insist on Python 3.5 wherever we check for it 2017-05-11 23:51:45 -07:00
James R. Barlow 83230097ae Insist on Tesseract 3.04 wherever we check for it 2017-05-11 23:51:28 -07:00
James R. Barlow 8f91acf956 Remove Tesseract 3.02 and 3.03 compatibility shims 2017-05-11 23:50:52 -07:00
James R. Barlow d211722a2f .gitignore the docs Makefile 2017-05-11 23:28:52 -07:00
James R. Barlow 56e6ed1249 Fix missing import; all tests passing! 2017-05-11 23:28:05 -07:00
James R. Barlow 21982cf1cb baiona_gray remove alpha channel 2017-05-11 23:23:37 -07:00
James R. Barlow edc01408da Update the .png files, again, hopefully without corruption 2017-05-11 23:20:50 -07:00
James R. Barlow aee33c87ed Merge release notes 2017-05-11 23:11:12 -07:00
James R. Barlow 0dae1602c7 Fix missing import PIPE 2017-05-11 23:07:20 -07:00
James R. Barlow d926f07ac1 Stop git from corrupting .pngs
Grrr.
2017-05-11 23:07:06 -07:00
James R. Barlow 96045e98f4 Update develop with master changes
We’re well out of the “trivial updates” zone
2017-05-11 22:54:27 -07:00
James R. Barlow 01b7205e2c Ensure skipped pages are explained in sidecars 2017-05-11 00:43:36 -07:00
James R. Barlow c8a4cbcf17 Fix test suite breakage after sidecar feature added
Forgot to update tesseract spoofers to account for change in tesseract
parameters.  Also the change to outputting multiple files in the collate
steps affected how ruffus passes information into downstream consumers
of those files.
2017-05-11 00:17:24 -07:00
James R. Barlow 16b6442b23 Add changes to __main__.py that should have been in last commit 2017-05-10 17:55:42 -07:00
James R. Barlow 183eafa587 Implement sidecar text files (#126) 2017-05-10 15:22:44 -07:00
James R. Barlow 47a2997538 Reorganize —help text 2017-05-10 12:19:56 -07:00
James R. Barlow 37ebcadfa1 Implement —user-words, —user-patterns 2017-05-09 17:54:56 -07:00
James R. Barlow 74d98216f1 Update documentation for Ghostscript behavior 2017-05-09 17:43:39 -07:00
James R. Barlow 4bdebf573e Tell Travis CI to use multiple cores
Let’s see if this helps the build go faster
2017-05-09 17:24:32 -07:00
James R. Barlow 1606b6a383 Add —quiet (fixes #143), stop using ruffus to partially generate argparser 2017-05-09 17:24:06 -07:00
James R. Barlow 2a61902df5 Merge commit 'c4f01de231d22da5cea02c25aa581a965a37640b' 2017-05-09 16:37:55 -07:00
James R. Barlow 01a1c2b576 Implement —pdfa-image-compression to control Ghostscript’s compression
Fixes #163
2017-05-09 16:37:29 -07:00
Ingo Feinererandjbarlow83 c4f01de231 Fix typo "cutput" -> "output" (#164)
[ci skip]
2017-05-09 16:22:10 -07:00
James R. Barlow 63a4a761dd Revert "v4.5.7 release notes"
The change introduced regressions, so find another way to fix.

This reverts commit d077c03686.

[ci skip]
2017-05-08 14:38:04 -07:00
James R. Barlow d077c03686 v4.5.7 release notes 2017-05-06 22:34:54 -07:00
James R. Barlow c97ea1f2a9 Update high DPI test case to confirm the output image is not downsampled 2017-05-06 22:34:01 -07:00
James R. Barlow fd27df2abb Update documentation to warn that transparency is not tested 2017-05-06 22:33:24 -07:00
James R. Barlow bf04f03c4c Fix corrupt test file “typewriter.png”
This file is not currently used in any tests, but could be, so replace
corrupt version with a useful one.
2017-05-06 22:28:34 -07:00
James R. Barlow 93e802f473 Fix issue #163, color and grayscale images JPEG compressed when not needed 2017-05-06 22:27:25 -07:00
James R. Barlow 1464b9087a Try Travis again with null deploy for OSX 2017-05-01 17:37:59 -07:00
James R. Barlow e8cc8fc879 Add travis null_deploy for osx 2017-05-01 17:24:43 -07:00
James R. Barlow fae2119b1e v4.5.6 release notes 2017-05-01 15:55:02 -07:00
James R. Barlow aa859a4139 Fix #156 - NoneType has no ‘getObject’ for pages with no /Contents 2017-05-01 15:46:15 -07:00
James R. Barlow b9b12e2879 Ensure that ocrmypdf stops and reports an error if Ghostscript fails
Past behavior was to continue and let ruffus puke eventually
2017-05-01 15:44:21 -07:00
James R. Barlow cf643c9f43 Fix argparse.ArgumentError needs two positional args 2017-05-01 14:55:47 -07:00
James R. Barlow 5b1a7880a9 Switch to Travis triggering Docker build to skip race condition with PyPI 2017-04-28 17:18:01 -07:00
James R. Barlow 474b6b0500 v4.5.5 release notes 2017-04-28 15:27:41 -07:00
James R. Barlow 6c8c1d8173 Fix #154: KeyError ‘/Contents’ on blank pages with /Contents record 2017-04-28 15:22:05 -07:00
James R. Barlow 6a91fa637f Squash merge improvements to auto-homebrewing macOS version 2017-04-28 15:17:38 -07:00
Jakub Wilkandjbarlow83 2846fb4e31 Remove misplaced flags from re.sub() call (#153)
The 4th argument of re.sub() is maximum number of substitutions,
not flags.

Moreover, re.MULTILINE affects only semantics of ^ and $,
so it wouldn't have any effect on this regular expression.
2017-04-20 00:38:23 -07:00
James R. Barlow a1033cdc64 osx_brew: show output before letting “brew audit” check it 2017-04-18 23:38:07 -07:00
James R. Barlow 204336e1a5 Move release notes into the rest of documentation 2017-04-18 18:26:31 -07:00
James R. Barlow 8954e6c3b9 v4.5.4 Update release notes 2017-04-18 18:09:15 -07:00
James R. Barlow fee22b6b0b Fix #151, cannot write mode P as JPEG
all(<empty generator>) is True.
2017-04-18 18:08:45 -07:00
James R. Barlow 2b82c31b85 Update documentation 2017-04-18 18:07:19 -07:00
James R. Barlow 9a4813089c autobrew: remove homebrew dependency “zlib”, causes audit failure 2017-04-18 15:28:30 -07:00
James R. Barlow 554fcc8b9d Add test case for #152 2017-04-18 15:20:25 -07:00
Tomandjbarlow83 345256ee99 Fix --skip-big when there are no images in pdf (#152)
* fixed skip-big when there are no images in pdf

* added only_text pdf

* updated only_text pdf

* Delete only_text.pdf
2017-04-18 15:19:24 -07:00
James R. Barlow 58d1042147 v4.5.3 release notes update 2017-03-29 23:50:31 -07:00
James R. Barlow 7b7e3a3e03 Enable lossless reconstruction for —pdf-renderer tess4 where appropriate 2017-03-29 23:44:12 -07:00
James R. Barlow 1e7fbd4202 Fix issues with —pdf-renderer tess4 page skipping
If tess4 renderer needed to skip OCR on a page it would end up
duplicating the page contents onto the new page, rather than creating
a blank OCR layer and placing it on the output page. This created
duplicated content in output files.
2017-03-29 23:43:26 -07:00
James R. Barlow 6e907856f2 Refresh requirements 2017-03-29 18:15:29 -07:00
James R. Barlow 8bc6019172 Begin adding new option to redo ocr 2017-03-29 18:15:20 -07:00
James R. Barlow 059f79242e Phase out subprocess.Popen 2017-03-29 18:15:02 -07:00
James R. Barlow 89599b4812 Drop Python 3.4 compatibility 2017-03-29 15:46:53 -07:00
James R. Barlow a9f4047a97 Remove backward compatible API deprecations from v4.x 2017-03-29 15:42:54 -07:00
James R. Barlow 23227ae763 Deprecate old files 2017-03-29 15:41:52 -07:00
James R. Barlow 4a9e9e9db2 v4.5.3 release notes 2017-03-29 13:19:34 -07:00
James R. Barlow 88ef2718f1 Reject high Unicode metadata at command line
Ghostscript 9.21 does not seem to accept Unicode above U+FFFF. Previous
versions did, but it now exits with a rangecheck error (-15).

Reject on the command line for now. Complete fix would also need to
check input PDF’s metadata.
2017-03-28 11:08:38 -07:00
James R. Barlow e71e8ca3ad Workaround for GS VMerror -25 bug
Avoid inserting docinfo keys that would be translated to null strings,
to avoid running afoul of
https://bugs.ghostscript.com/show_bug.cgi?id=697684
2017-03-28 11:05:43 -07:00
James R. Barlow 45e9257d6e Don’t use filename “pdfa_def.ps” for GS file
At recommendation of Artifex people, don’t use the filename pdfa_def.ps
because if given without a full path Ghostscript will prioritize
searching its own resources over the current directory, and will pick up
 the example file pdfa_def.ps instead.  We used the full path anyway,
but this is a prudent defensive change to avoid odd Ghostscript behavior.
2017-03-28 11:02:46 -07:00
James R. Barlow 2954e72652 Some examples of Ghostscript and Tesseract warnings/errors were not tagged properly 2017-03-28 10:59:53 -07:00
James R. Barlow 199de96cff Ghostcript 9.21 seems to have a regression related to Unicode metadata 2017-03-24 15:15:46 -07:00
James R. Barlow 8ddbe81513 Fix issue #147: unpaper loses DPI information, affects —pdf-renderer tess4 2017-03-24 13:23:03 -07:00
James R. Barlow a3e26e0498 Make —pdf-renderer tess4 more informative, less FUD 2017-03-24 12:58:40 -07:00
James R. Barlow 4ad129d8d8 docs: Don't recommend system-site-packages anymore (Ubuntu 16.04)
Not needed since reportlab 3.4 comes with a wheel, and that was the main difficulty.
2017-03-23 08:13:28 -07:00
James R. Barlow dfb9fa0736 autobrew: missing deps 2017-03-16 00:18:07 -07:00
James R. Barlow eb036898e9 Update documentation with macOS homebrew tap
[ci skip]
2017-03-13 22:25:25 -07:00
James R. Barlow 7c6aa76a2a Fix brew audit failure 2017-03-13 17:58:31 -07:00
James R. Barlow f035cb1088 Fixed issue #142 — closed streams raise an exception on fork attempt 2017-03-13 15:52:57 -07:00
James R. Barlow 35162166c5 Fix UnboundLocalError in autobrew.py 2017-03-13 15:51:39 -07:00
James R. Barlow 107f6abcb1 Fix autobrew build issues - missing deps 2017-03-12 17:07:38 -07:00
James R. Barlow 760a939e7d Further autobrew tweaks 2017-03-11 17:05:26 -08:00
James R. Barlow 72660d0dec MacOS skip the one test that needs poppler, to save installing poppler 2017-03-11 17:03:26 -08:00
James R. Barlow 8444a8f211 before_deploy doesn’t run unless something is going to be deployed 2017-03-11 17:03:20 -08:00
James R. Barlow 4a1fec8328 Improvements to macOS test and work on homebrew tap autobrew
Squashed commits:
[3f06c1e] Try setting up homebrew tap autobuilding
[01532f1] Strict mode error in brew
2017-03-11 17:00:54 -08:00
James R. Barlow 42547f6017 Revert "Finalize Dockerfile move; unfortunately not supported by Docker Hub"
Unfortunately because of this issue
https://github.com/docker/hub-feedback/issues/292

Docker Hub effectively automatically changes directory to the location
of Dockerfile, meaning needed files are not in the right places, so
this has to be reverted.

This reverts commit 65c9a07dde.

[ci skip]
2017-03-10 22:49:08 -08:00
James R. Barlow 0ccf564f03 Revert "Move Dockerfiles out of root"
This reverts commit 3d3b3abc1b.
2017-03-10 22:46:03 -08:00
James R. Barlow 65c9a07dde Finalize Dockerfile move; unfortunately not supported by Docker Hub
Unfortunately because of this issue
https://github.com/docker/hub-feedback/issues/292

Docker Hub effectively automatically changes directory to the location
of Dockerfile, meaning needed files are not in the right places, so
this has to be reverted.
2017-03-10 22:45:31 -08:00
James R. Barlow 4700a19322 Move pipeline.svg out of root 2017-03-10 17:18:38 -08:00
James R. Barlow 3d3b3abc1b Move Dockerfiles out of root 2017-03-10 17:17:36 -08:00
James R. Barlow 7cd2770a13 Fix issue #137 - proportions of non-square resolution distorted
Distortion mainly affected —force-ocr
2017-02-26 17:13:16 -08:00
James R. Barlow 7b94129d9e v4.5 notes 2017-02-14 13:03:48 -08:00
James R. Barlow d1a0065ef8 Create test case for Form XObjects 2017-02-14 12:51:15 -08:00
James R. Barlow 5a817370fd Warn more strongly about —pdf-renderer tesseract until fix is widely propagated 2017-02-14 11:33:07 -08:00
James R. Barlow ab0a210763 Update dockerfile.tess4 yet again
Installing Tess4 PPA over Tess3 proved too much pain, so sever the link
between this and the jbarlow83/ocrmypdf image, starting each from
scratch. Also the complete set of language packs proves too much - the
build seems likely to fail when trying to install so many.
2017-02-14 11:32:05 -08:00
James R. Barlow 9f800736bc Fix running_in_docker() check failing on newer Docker
This test has to work to ensure spoof/tesseract_cache.py has a writable
directory to put cache into. Otherwise those tests fail.
2017-02-13 02:16:06 -08:00
James R. Barlow c9a83afad6 Improve batch processing examples 2017-02-13 02:14:32 -08:00
James R. Barlow 5e14274f10 pageinfo: learn to extract image information from Form XObjects 2017-02-11 16:48:59 -08:00
James R. Barlow 167470b4bd Re-fix Dockerfile.tess4
[ci skip]
2017-02-10 15:43:49 -08:00
James R. Barlow f06d3c2ec2 Fix tesseract 3.04 on tesseract 4 on image
[skip ci]
2017-02-10 08:38:22 -08:00
James R. Barlow 74c99a8a77 v4.4.2 release notes 2017-02-06 21:56:55 -08:00
James R. Barlow 0e4d312ee2 Adjust Travis deploy to PyPI settings
-only on master branch
-only Python 3.6 build uploads, so the others don’t compete
-don’t upload docs to PyPI
2017-02-06 21:27:59 -08:00
James R. Barlow 589f19559d Rewrite Dockerfiles to use ubuntu 16.10 base system
Debian now has a few disadvantages:
-there is no convenient PPA for Debian tesseract 4.0, but there is for
Ubuntu
-Ubuntu sets locale to UTF-8 automatically removing the need to do this

All three ocrmypdf docker images are now based on a common Ubuntu
16.10 image, derived from the one used to build ocrmypdf-tess4.
-polyglot now differs from -tess4 only by opting into the tess4 PPA.

Both Ubuntu 16.10 and Debian stretch use tesseract 3.04.01 now making
the sharp.ttf patch unnecessary. /etc/apt/sources has been unused for a
while now both have newer Ghostscripts.
2017-02-06 14:39:29 -08:00
James R. Barlow f28bc25dc0 Configure travis to handle deployment to PyPI; also lint .travis.yml 2017-02-06 13:50:53 -08:00
James R. Barlow a0657ad937 Prevent use of —pdf-renderer tess4 on tesseract 3 2017-02-06 13:49:43 -08:00
James R. Barlow 5b8d88af4c Suggest use of aliases to hide docker run 2017-01-30 15:08:02 -08:00
James R. Barlow fa82b50340 Adding missing file Dockerfile.tess4 2017-01-29 18:34:01 -08:00
James R. Barlow 005216bc57 Support ocrmypdf-tess4 2017-01-29 18:26:52 -08:00
James R. Barlow e748fdcf6f v4.4.1 release notes 2017-01-28 22:23:35 -08:00
James R. Barlow 8c17c9918e Add documentation and test cases for —tesseract-config
This parameter has existed for along time but never really got any
attention.
2017-01-28 22:06:51 -08:00
James R. Barlow ea0dd99d0b More documentation updates 2017-01-28 15:35:59 -08:00
James R. Barlow e0cc67afae docs: suggest —oem 1 2017-01-28 14:58:25 -08:00
James R. Barlow 04f9cbe364 Describe how to use tesseract 4.0 while 3.04 is installed 2017-01-27 18:13:59 -08:00
James R. Barlow 99afebd033 tesseract jobs_limit(2)
At least on macOS with my quadcore performance improves with two
tesseracts in parallel (20% gain). Hard to say how this will affect
Linux, but stepping up to 2 jobs seems justifiable.
2017-01-27 18:13:12 -08:00
James R. Barlow a6feacc810 travis: fix ‘pip install’ by moving working code out of the way 2017-01-27 14:33:23 -08:00
James R. Barlow 65e4b1672f cffi: verbose=True 2017-01-27 14:17:13 -08:00
James R. Barlow 46cc0dd190 Revert "Do we need to exclude ocrmypdf.lib?"
This reverts commit 678b9fb603.
2017-01-27 13:51:30 -08:00
James R. Barlow 678b9fb603 Do we need to exclude ocrmypdf.lib? 2017-01-27 13:49:11 -08:00
James R. Barlow 49ab0c1f0b setup.py: cffi is definitely needed in setup_requires 2017-01-27 13:43:47 -08:00
James R. Barlow ab490a7736 Experiment: update *requirements.txt, use more current travis build steps
Perhaps this works around the pip/setup.py asymmetry that broke the
4.4 release.
2017-01-27 13:13:14 -08:00
James R. Barlow e4ce1dae35 setup.py: for some reason, subpackages must be explicitly specified 2017-01-27 00:37:05 -08:00
James R. Barlow 179b812acb Fix readthedocs build error 2017-01-26 23:57:51 -08:00
jbarlow83andGitHub 7f170517ec Note about pytest-helpers-namespace 2017-01-26 23:15:32 -08:00
James R. Barlow 5480da4f04 Additional docs updates for v4.4 2017-01-26 23:02:44 -08:00
James R. Barlow 9a15a4db10 Ensure specified destination is writable before starting pipeline process 2017-01-26 22:08:24 -08:00
James R. Barlow 55aeaec293 Autorotation check: Replace duplicated tests with parameterized test 2017-01-26 18:07:59 -08:00
James R. Barlow f6df1fb40c Fix test suite regression: output files dumped in tests/resources 2017-01-26 18:07:09 -08:00
James R. Barlow b889a89c36 Fix remaining 3.4/3.5 regressions 2017-01-26 17:53:27 -08:00
James R. Barlow 1976dc6f30 Fix issue #121 “pop from empty list” (content stream parsing error) 2017-01-26 17:24:40 -08:00
James R. Barlow e864c65d26 (Hopefully) Fix Path <-> py.path conversion on Py3.4/3.5 2017-01-26 17:19:15 -08:00
James R. Barlow 02fba02d31 Refactor test suite to use fixtures to manage paths 2017-01-26 16:38:59 -08:00
James R. Barlow fb9e7c82f6 Move duplicate test code into common namespace 2017-01-26 13:36:52 -08:00
James R. Barlow 77d31bf646 Add renderers page (missed from previous) 2017-01-26 13:20:44 -08:00
James R. Barlow 29ca799bcf Move pytest.ini into setup.cfg 2017-01-26 12:45:38 -08:00
James R. Barlow 467b7f0163 Update docs for eventual v4.4 release 2017-01-26 12:29:11 -08:00
James R. Barlow bad67c6dc5 Rename ‘tesstop’ to ‘tess4’
There’s no reason text-only PDF shouldn’t become the default for
tesseract 4.
2017-01-26 12:28:51 -08:00
James R. Barlow ac40426971 Implement “tesstop” (tesseract v4 text-only pages - working name) 2017-01-20 17:16:01 -08:00
James R. Barlow 7acfaf6d34 pipeline: rename some of the stages, for clarity 2017-01-20 17:15:00 -08:00
James R. Barlow 99e47c9c04 tesseract: add support for using v4 textonly_pdf feature 2017-01-20 17:06:23 -08:00
James R. Barlow d7904e2251 Travis now has Python 3.6, test against it 2017-01-20 14:26:17 -08:00
James R. Barlow 68aef489de Merge branch 'master' (4.3.5, Python 3.6 support) into develop
# Conflicts:
#	dev_requirements.txt
#	requirements.txt
2017-01-20 14:25:28 -08:00
James R. Barlow 3f9adcd5e0 Document idea for producing companion text files 2017-01-19 16:48:05 -08:00
James R. Barlow 6cc5135d2d Output to stdout: ensure stdout is flushed to prevent truncation errors 2017-01-19 16:41:10 -08:00
James R. Barlow d4c72b371f Forward --oem argument to tesseract 4 2017-01-18 21:37:50 -08:00
James R. Barlow 18b6f05657 Resolve issue #124 - poor performance with Tesseract v4
It seems that Tesseract v4 on a platform with OpenMP working correctly
while perform poorly with ocrmypdf because each will also soak up all
available CPUs. Running N^2 processes/threads on a N-core CPU where
each wants 100% of CPU turns out to be detrimental.

So, we restrict ocrmypdf w/tessv4 to a single Tesseract process at a
time, for now. Alternative may be to limit OpenMP threads if throughput
is higher.
2017-01-18 17:52:12 -08:00
James R. Barlow c42d9baa26 tesseract: for v4, use --psm while keeping -psm for v3
At the moment v4 accepts both but who knows if this will get dropped,
so do as document for each version.
2017-01-18 17:43:47 -08:00
James R. Barlow 6e27ecd2b9 Finalize ‘exec’ migration and make it backward compatibility for now 2017-01-18 17:40:50 -08:00
James R. Barlow 482692396e Add installation instructions for Ubuntu 16.04 2017-01-17 08:17:56 -08:00
James R. Barlow c48acf165a v4.3.5: Python 3.6 compatibility 2017-01-03 00:45:33 -08:00
James R. Barlow 9e004c3ec0 Another attempt at py 3.4/3.5
Revert to exactly what the previous passing build specified.
2017-01-03 00:34:26 -08:00
James R. Barlow 7be4e9c919 fix setuptools-scm for py 3.4, 3.5 2017-01-03 00:25:57 -08:00
James R. Barlow 5ec38a4bed Update requirements files and documentation for Python 3.6 - no code changes 2017-01-03 00:11:34 -08:00
James R. Barlow f246779b8e pdfa: documentation, remove from __future__ 2016-12-12 15:10:10 -08:00
James R. Barlow a7d8cdf061 Don’t copy pageinfo - job manager already provides a copy of real pdfinfo 2016-12-12 15:09:41 -08:00
James R. Barlow 620745c812 pipeline: don’t use qpdf to check page count again
We already know the number of pages at this stage.
2016-12-12 15:09:11 -08:00
James R. Barlow b8767e5ba9 Rename exe -> exec, more Unix-y and suggestive 2016-12-10 15:34:00 -08:00
James R. Barlow d33a50660d Replace most sys.exit() with raising exceptions
Because ruffus doesn’t handle exceptions well I tended to call sys.exit
to make sure we got out of dodge when needed.  However, sys.exit is not
ideal for the Python API this is moving towards, so this introduces
proper exceptions for the various cases that retain suggested error
codes. Only __main__.py should call sys.exit now, everyone else has to
throw an exception.

For now the worker raising a fatal exception is logging messages rather
than passing an exception object with the fatal error message, mainly
because ruffus doesn’t properly marshall the exception object so we
just check “what is the name of the exception class that caused ruffus
to thrown an RethrownJobError”?

Also fixed along the way was the wrong return code being shown for
encrypted PDF checking, and incorrect use of str.find (e.output.find)
in boolean logic (str.find returns -1 on failure to find, which is True).
2016-12-10 15:24:24 -08:00
James R. Barlow 4ee9658e97 Move external program wrappers to ocrmypdf.exe package 2016-12-09 16:54:24 -08:00
James R. Barlow dd1b84e7ba More refactoring - helpers.py 2016-12-09 16:31:08 -08:00
James R. Barlow 4c677e6c47 Extract pipeline out of __main__.py and into pipeline.py
This leaves __main__.py to handle command line arguments while pipeline.py
runs the pipeline - mostly. They are still somewhat intertwined, with
__main__.py doing essential things for pipeline.py, etc., and some
helper functions that could go in their own module.

All tests pass after this major refactor.
2016-12-09 16:17:12 -08:00
James R. Barlow f0f889440b Merge branch 'master' into feature/ooruffus 2016-12-08 16:36:03 -08:00
James R. Barlow cc9ceaeb74 v4.3.4: release notes 2016-12-08 16:34:09 -08:00
James R. Barlow ad2fa8d1d7 Fix MANIFEST for .png 2016-12-08 16:25:04 -08:00
James R. Barlow adc1580742 Help py.test collect output in more cases 2016-12-08 16:21:07 -08:00
James R. Barlow 4d3b44d6df ghostscript: cleanup harmless error message printed for overprint
Redirect stderr->stdout to hopefully make GS output easier to work with
overall, since the previous code didn’t seem to account for mixed used
properly.
2016-12-08 16:19:15 -08:00
James R. Barlow e57aa0eee2 pageinfo: fix “decimal.InvalidOperation: quantize result has too many digits”
And add new test case for this.
2016-12-08 16:06:53 -08:00
James R. Barlow 1ae1d116c7 Make setup.py license internally consistent 2016-12-08 16:06:31 -08:00
James R. Barlow 097a69d07f pageinfo: fix “decimal.InvalidOperation: quantize result has too many digits”
And add new test case for this.
2016-12-08 16:04:14 -08:00
James R. Barlow a81ce87a50 Remove non-reentrant options checking and logging setup 2016-12-05 14:13:36 -08:00
James R. Barlow 88be0d43a0 Make setup.py license internally consistent 2016-12-03 21:37:24 -08:00
James R. Barlow ff16a00a3d Remove test for Pillow JPEG and PNG
As of 3.1.1, our minimum version, these codecs are now required by
default for a successful installation, effectively solving the problem
of Pillow installed without libjpeg/libpng.
2016-12-03 14:25:46 -08:00
James R. Barlow 8982b3e1e2 Update requirements
-update requirements.txt and dev_requirements.txt to more recent version
-setup.py updated to Ubuntu 14.04 rather than 12.04 backports
-request at least Pillow 3.1.1 now (since this makes jpeg/png mandatory)
2016-12-03 14:14:07 -08:00
James R. Barlow be0fa35d14 Merge branch 'master' into feature/ooruffus 2016-12-03 14:02:43 -08:00
James R. Barlow 9f51ed9d01 Finalize v4.3.3 release notes 2016-12-03 00:39:24 -08:00
James R. Barlow 731e6792c7 Add test cases for Ghostscript PDF/A warnings 2016-12-03 00:32:09 -08:00
James R. Barlow c35ec0b4aa ghostscript: more effort at error logging 2016-12-03 00:22:03 -08:00
James R. Barlow 03aaf575dc v4.3.3 release notes, fix more gs 9.20 issues 2016-12-02 16:26:34 -08:00
James R. Barlow 9a060579ba Move work_folder into multiprocessing manager 2016-12-02 01:39:17 -08:00
James R. Barlow d40a5c4f7a Remove all remaining traces of ‘options’ global state from task runners 2016-12-02 01:31:57 -08:00
James R. Barlow 21f7dc3377 Distribute ‘options’ to worker processes via the multiprocessing manager 2016-12-02 01:06:11 -08:00
James R. Barlow 43c13a1ed9 Replace pdfinfo, pdfinfo_lock with multiprocessing manager
Using a context manager to guard the pdfinfo list makes the lock
unnecessary. (Although it was probably unnecessary in the first place
anyway.)
2016-12-01 23:36:30 -08:00
James R. Barlow 6bc3f189e1 Remove “WrappedLogger” - does not do anything useful
Never really investigated the reason why ruffus returns a mutex to go
along with its logger. It seems that the mutex is only needed if one
wanted to make multiple successive calls to a log function and have
them appear appear atomically. It is not needed to protect the logger
proxy because accessing the proxy triggers IPC in the child process
that handles the multiprocessing.Manager() object.

The logging wrapper only logs one line at a time, so the mutex does not
actually protect logging sequence. Cut it.

Also manager.Lock() returns a threading.Lock object so the purpose of it
is actually to help processes share a thread-level lock. It would be
more appropriate to use a semaphore based multiprocessing.Lock.
2016-12-01 15:27:07 -08:00
James R. Barlow 2c5437135c Remove temporary re_symlink logging shim 2016-12-01 00:31:42 -08:00
James R. Barlow 444da02523 Fix mistake made in converting pipeline; incredibly, all tests pass now 2016-12-01 00:30:19 -08:00
James R. Barlow 00e8af2381 Reactivate the pipeline; surprisingly works in quick test 2016-12-01 00:03:03 -08:00
James R. Barlow 401b21864f Convert to object oriented ruffus syntax (does not run)
I experimented with the idea of using asyncio-based processing but
realized that that does not solve the import time binding problem
that is the real issue. Therefore the simpler refactoring is to convert
to ruffus-oo syntax and get things working again.

build_pipeline() is really ugly at the moment. The old syntax had its
advantages.

This test reproduces the complete pipeline graph but does not work
otherwise.
2016-11-30 23:58:26 -08:00
James R. Barlow de939951d4 Record version in debug log 2016-11-29 15:30:50 -08:00
James R. Barlow 7725d16a26 Fix exception on inline stencil masks with no /CS attribute 2016-11-24 22:37:00 -08:00
James R. Barlow 8a74408d83 Add security suggestions 2016-11-21 20:58:31 -08:00
James R. Barlow 3d0dc95a06 Moved venvs 2016-11-21 20:40:22 -08:00
James R. Barlow 04a57a3cc2 OS X -> macOS 2016-11-21 20:40:06 -08:00
James R. Barlow d0c22ce01d v4.3.2 release notes 2016-11-10 23:16:08 -08:00
James R. Barlow 23c95e9660 ghostscript: elide overprinting to fix PDF/A errors in GS 9.20
It looks like GS 9.19 can incorrectly set overprinting for the text layer
even though this makes no sense in PDF/A, or at least someone produced
PDFs that have this after a Tesseract PDF -> GS PDF/A conversion. GS 9.20
complains about this. Instead of aborting, elide the feature.

See
http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=094d5a1880f1cb9ed320ca9353eb69436e09b594
and
issue #107.

It looks like it is better to elide features and warn about elision rather
than abort with an error.
2016-11-10 14:48:02 -08:00
James R. Barlow eecab9b95d pdfa: fix KeyError on pdfa_dict if document has some xmp metadata but
not exactly what we’re looking for
2016-11-09 05:41:12 -08:00
James R. Barlow 8abc2f113c Merge branch 'develop' 2016-11-07 14:36:50 -08:00
James R. Barlow 949d2ff1c2 v4.3.1 release notes 2016-11-07 14:36:08 -08:00
James R. Barlow 1c8b763d53 test_pageinfo: Remove bits per component test
The behavior of this test will ultimately depend on what version of
img2pdf is installed, since after my patch it will be able to produce
1bpp images.
2016-11-07 14:35:54 -08:00
James R. Barlow bb91393b85 Fix “deskew-rotate” bug.
Turns out this occurred in any case where pdf-renderer hocr was used
and a tesseract timeout or error occurred. We created a replacement
page based on the unrotated page dimensions instead of the input image’s
dimensions.
2016-11-07 14:17:31 -08:00
James R. Barlow cc9c0d819e Add test case for documents that get rotated incorrectly after deskew 2016-11-07 14:15:03 -08:00
James R. Barlow a72b8caf47 Update documentation on other languages, multilingual documents 2016-11-07 14:14:06 -08:00
James R. Barlow fdd9b8b8ce Optimize some of the test resources to reduce file sizes
Mostly by reducing RGB -> monochrome and applying JBIG2 compression
2016-11-07 14:01:23 -08:00
James R. Barlow c096b4ca8c Make debug dump of pageinfo at the end of processing readable 2016-11-04 02:23:02 -07:00
James R. Barlow 427add3008 Add @posttask debug hooks 2016-11-03 18:15:21 -07:00
James R. Barlow c45871700d Fix bug: LeptonicaErrorTrap() leaks file handles 2016-11-03 15:51:27 -07:00
Sean Whittonandjbarlow83 6821e8eeb2 disable mathjax sphinx extension (#103)
Mathjax isn't actually needed for OCRmyPDF's docs, but enabling this
extension causes the browser to download a copy of mathjax.js from
cdn.mathjax.org anyway.

I have to disable this for the offline docs bundled with Debian, but
since you're not using mathjax, it would be nice to have the diff merged
upstream.
2016-11-01 21:56:57 -07:00
James R. Barlow a4f07756a5 tesseract caching: don't transcode tesseract's output, hash source file
For sanity's sake, deal with tesseract streams in binary without
transcoding (via universal_newlines, etc.). The only differences are
printing messages regarding spoofing.

Also hash the source file so that changes to the cache mechanism
invalidate old cache automatically. That is probably too aggressive,
but simple and safer than the previous approach.
2016-10-28 16:44:12 -07:00
James R. Barlow f24fb0e0c5 Obligatory MANIFEST.in repair 2016-10-28 01:28:46 -07:00
James R. Barlow 73b88a0a6f More work on documentation 2016-10-28 01:22:40 -07:00
James R. Barlow c42f39e2d4 Update README to point to ReadTheDocs 2016-10-28 00:33:17 -07:00
James R. Barlow 5e5fe3175f docs: OS X -> macOS branding change 2016-10-28 00:32:57 -07:00
James R. Barlow cab65d1f11 pageinfo: add a python3.4 implementation of isclose() 2016-10-28 00:31:04 -07:00
James R. Barlow 245f05d5f4 docs: allow python setup.py install --force to bypass checks
ReadTheDocs needs this.
2016-10-28 00:07:26 -07:00
James R. Barlow dda751f9e3 Merge branch 'feature/docs' into develop
# Conflicts:
#	ocrmypdf/__main__.py
2016-10-27 23:50:08 -07:00
James R. Barlow 3d37ae988a Update release notes for 4.3 2016-10-27 23:48:12 -07:00
James R. Barlow 717acd9855 Prevent dumping binary PDFs to stdout 2016-10-27 16:20:53 -07:00
James R. Barlow 2e4431cc63 Allow piping output to stdout 2016-10-27 16:14:42 -07:00
James R. Barlow f7387b0859 test_stdin: simplify this test
No need to involve 'cat', just hook the file up to stdin.
2016-10-27 16:01:07 -07:00
James R. Barlow a09f6b8977 Test cases: check that stdout is clear of output
To ensure piping to stdout is possible.
2016-10-27 15:58:24 -07:00
James R. Barlow d63449c214 main: don't print output file location to stdout, use stderr 2016-10-27 15:57:33 -07:00
James R. Barlow a86805f0d9 Remove possibly non-free page from "multipage.pdf" 2016-10-27 15:56:43 -07:00
James R. Barlow 7d2009ccef ghostscript: log errors from stdout 2016-10-27 15:36:20 -07:00
James R. Barlow 18ae5db06d ghostscript: ensure raster resolution is specified in integer units 2016-10-27 15:35:33 -07:00
James R. Barlow 9a1838f102 pageinfo: accept "cm/Do" image drawing without the usual "q/Q" wrapper
Some PDFs omit the traditional q/Q wrapper and alter ctm with a stack
depth of zero, so make our test for stack depth specifically test for
the case where the PDF calls for rendering to an uninitialized ctm.

Probably related to #97.
2016-10-27 15:35:00 -07:00
James R. Barlow e20346032d leptonica: add color testing functions for future experiments 2016-10-27 14:49:49 -07:00
James R. Barlow 693a27d76c leptonica: add iPython display hook and equality test 2016-10-26 14:44:41 -07:00
James R. Barlow 203966d86b leptonica: fix Pillow conversion for 1-bit and 8-bit gray images 2016-10-26 13:10:13 -07:00
James R. Barlow 7eca8508fd Implement new preprocessing feature, background removal 2016-10-14 17:23:34 -07:00
James R. Barlow b85270df1c Merge branch 'master' into develop 2016-10-14 15:56:58 -07:00
James R. Barlow aff597cef4 v4.2.5: update release notes, fix silly typo in pageinfo.py 2016-10-13 13:26:39 -07:00
James R. Barlow 61b05b3dee Fix issue: BitsPerComponent is an optional field, sometimes omitted 2016-10-13 13:15:27 -07:00
Julian Kahnertandjbarlow83 453c4ef602 Update README.rst (#98)
`brew install tesseract` just installed the english language pack not French, German or Spanish
2016-10-12 11:20:58 -07:00
James R. Barlow cf4b04f92d The main 'quick' test should be a file that OCRs to recognizable text 2016-10-07 16:25:34 -07:00
James R. Barlow 06c6999987 Merge commit '07891d994aab92e7a14aebe1ac509aab2d4f170c' 2016-10-07 12:45:56 -07:00
James R. Barlow 013c5a369f Replace redacted file with an OCR-able file 2016-10-07 12:45:22 -07:00
James R. Barlow 07891d994a Replace redacted file with an OCR-able file 2016-10-07 12:44:49 -07:00
James R. Barlow 6baf8668a6 Replace with non-free file milk.pdf with free equivalent 2016-10-06 13:10:28 -07:00
James R. Barlow 4ba2962c56 Comment on non-free files 2016-10-05 16:48:16 -07:00
James R. Barlow 7ad92f5db4 Merge branch 'master' of https://github.com/jbarlow83/OCRmyPDF 2016-10-05 16:39:00 -07:00
James R. Barlow 4dad09cc91 resources/README: replace the other large table with a list table 2016-10-05 16:38:51 -07:00
Sean Whittonandjbarlow83 7b2e0c7a7a also exclude .git in pytest.ini (#94) 2016-09-15 08:56:14 -07:00
Sean Whittonandjbarlow83 7f08f15fc9 pytest skipif for milk.pdf test (#95)
Skip the test if the fair use restricted milk.pdf is not present.
2016-09-15 08:55:31 -07:00
James R. Barlow 825c0f8b2a Note that milk.pdf is non-free, start using list-tables 2016-09-10 14:44:00 -07:00
James R. Barlow dbe880bc41 Update tesseract supported languages 2016-09-09 12:55:07 -07:00
James R. Barlow 2ec516b6ff leptonica: learn a few new tricks
Found some interesting options for background norm.
2016-09-09 12:54:36 -07:00
James R. Barlow 7942a01e50 leptonica: This is not a Py2 module anymore 2016-09-08 20:59:38 -07:00
James R. Barlow df684f9344 Update tesseract supported languages 2016-09-08 15:52:25 -07:00
James R. Barlow ae16e95e42 leptonica: scale should be a tuple for consistency 2016-09-08 11:38:10 -07:00
James R. Barlow 2ac8e8a0cc More doc tweaks, mainly introduction 2016-09-08 11:29:53 -07:00
James R. Barlow 0a0ceda71f Start the documentation 2016-09-06 13:52:40 -07:00
James R. Barlow 220f1ce161 tasks.py: stop tracking this file for now
This helper script is still in development and needs to be changed each
release, which breaks the release.

It shouldn't be in MANIFEST.in at all because it's not part of a
distribution.
2016-09-04 10:55:57 -07:00
James R. Barlow c62a8a97c9 v4.2.4 release notes 2016-09-01 21:33:38 -07:00
James R. Barlow f8a1136979 tasks: show logging info 2016-09-01 21:24:13 -07:00
James R. Barlow 9ca29c787b Update description of masks.pdf to reflect what it actually tests 2016-09-01 21:21:14 -07:00
James R. Barlow 6af748a251 pageinfo: regression - didn't add inline images to list 2016-09-01 15:27:51 -07:00
James R. Barlow 9041867f86 pageinfo: exclude images from DPI calculation if drawn at stack depth 0
More thorough testing showed that Acrobat do not presume that images
fill the page if the CTM is unspecified, as tests/resources/masks.pdf
seems to want.  Instead they treat it literally and draw the image
as 1x1 PDF units or 1/72" square in the bottom left corner of the page.

Seems like the best thing to do is ignore any such images for the purpose
of DPI calculation.  masks.pdf still works out okay because it has
other images.

For more robustness we could consider invalidating any DPI above some
limit, or warning the user about these microdot thumbnails.
2016-09-01 14:23:31 -07:00
James R. Barlow 04099b087c pageinfo: handle stencil masks when stack depth > 0 2016-09-01 14:03:30 -07:00
James R. Barlow 6d6234714c tasks: fix logic error and make magic numbers disappear 2016-09-01 14:03:08 -07:00
James R. Barlow 520be23481 Add release helper script 2016-08-31 20:33:04 -07:00
James R. Barlow 346c3c8dd3 Start tracking development requirements 2016-08-31 20:31:31 -07:00
James R. Barlow bd534c3313 main.py -> __main__.py
Executing a package with python -m packagename will check for
__main__.py inside the package.  In other words main.py should have
always been named __main__.py.

In the unlikely event that someone depends on "import ocrmypdf.main"
being meaningful, main.py continues to exist and replicates the
behavior of __main__.  (It's unlikely because import ocrmypdf.main does
unpythonic ruffus-related things at things import time, essentially
configuring itself to work with sys.argv.  To fix another day.)

This should solve the problem of Debian needing to run test suites
before installation and afterwards for continuous integration without
having to patch either file, as python -m ocrmypdf will follow import
order.  That is, if the current directory contains "ocrmypdf/" (e.g.
staging a new version) then that will be tested, else sys.path will
be checked.
2016-08-31 17:01:42 -07:00
James R. Barlow 2625368aed link: more MANIFEST.in tweaks 2016-08-31 16:28:39 -07:00
James R. Barlow 8ac94879f1 lint: no need to check for DEVNULL; all supported versions have it 2016-08-31 16:28:18 -07:00
James R. Barlow dd8c0f3756 Merge branch 'master' of https://github.com/jbarlow83/OCRmyPDF 2016-08-31 13:19:46 -07:00
James R. Barlow 010f353a5e v4.2.3 release notes 2016-08-31 13:19:27 -07:00
James R. Barlow e0a18edb92 Fix MANIFEST.in, as Python packages require 2016-08-31 13:19:17 -07:00
James R. Barlow c6f2eea058 Reinstate OCRmyPDF.sh with a deprecation warning 2016-08-31 11:57:02 -07:00
James R. Barlow bf89e38c69 Add milk.pdf test case 2016-08-31 11:42:21 -07:00
jbarlow83andGitHub e1f0640d42 Create issue template 2016-08-31 11:26:29 -07:00
James R. Barlow 71b54035ba Bug fix issue #89: trying to perform arithmetic on IndirectObject
TypeError: bad operand type for unary -: 'IndirectObject'
2016-08-31 10:25:58 -07:00
James R. Barlow 325cc0beca Allow test cases to run without installing first
As @spwhitton found:

The test suite needs to call "python3 -m ocrmypdf.main" instead of
just "ocrmypdf" because this /usr/bin/ocrmypdf script has not yet been
generated when dh runs the test suite.

---

Seems reasonable to perform in-place testing independent of installation.

Source:
https://sources.debian.net/src/ocrmypdf/4.2.1%2Bgit.20160824.1.5d67cc7-1/debian/patches/0001-patch-test-suite-executable.patch/
2016-08-26 15:23:26 -07:00
James R. Barlow 1a9f09c4d5 Remove OCRmyPDF.sh and its usage in all test cases 2016-08-26 15:18:38 -07:00
James R. Barlow 4fed4e2af3 tests: don't try to pass Unicode arguments on command line on Linux
Depends on locale being configured properly, and it's not necessary
to be able to do this.
2016-08-26 15:08:56 -07:00
James R. Barlow 74cc2346a5 pytest.ini: apply patch from Debian to exclude .pc dir
https://sources.debian.net/src/ocrmypdf/4.2.1%2Bgit.20160824.1.5d67cc7-1/debian/patches/0003-pytest-exclude-.pc-dir.patch/

From: Sean Whitton <spwhitton@spwhitton.name>
Date: Thu, 12 May 2016 08:21:39 -0700
Subject: pytest exclude .pc dir
Forwarded: not-needed

Patches to the test suite present in .pc confuse pytest.
2016-08-26 15:07:11 -07:00
James R. Barlow cc7e328358 Improve some documentation for tests 2016-08-26 15:04:08 -07:00
James R. Barlow d25397e2b0 Add test case for PDFs with masks and stencil masks 2016-08-26 15:03:27 -07:00
James R. Barlow bc11454e1c Help text: example of shell pipeline with img2pdf 2016-08-25 14:58:25 -07:00
James R. Barlow 2025a096c3 Test case for stdin streaming 2016-08-25 14:46:54 -07:00
James R. Barlow 38fe14b108 Make final PDF/A output message less obtuse 2016-08-25 14:46:40 -07:00
James R. Barlow 1b7b2f3695 v4.2.2 release notes, documentation improvements 2016-08-25 14:46:09 -07:00
James R. Barlow 5d67cc76cc Update 4.2.1 release notes 2016-08-24 14:16:22 -07:00
James R. Barlow 27a3813207 Recover input filename from symlink on error message
The recent commit to accept files from stdin broken the feature of
returning the input filename on an error, returning the temp filename
instead, which is confusing.
2016-08-23 17:38:28 -07:00
James R. Barlow b06e0bfdcd Merge branch 'develop' 2016-08-23 16:03:07 -07:00
James R. Barlow d616f25324 Implement DPI checking for stencil masks 2016-08-23 15:59:34 -07:00
James R. Barlow b03028e31f setup.py -> license is MIT 2016-08-19 10:14:33 -07:00
James R. Barlow e08c42fd3d Tweak pipeline again 2016-08-09 22:40:29 -07:00
James R. Barlow 16901f7134 Accept input from stdin if input filename is '-' 2016-08-09 15:46:24 -07:00
James R. Barlow dffceedd85 Update the pipeline image 2016-08-09 15:45:19 -07:00
James R. Barlow e5541e435c New test to confirm we can emit JBIG2 with appropriate settings 2016-08-03 11:35:48 -07:00
James R. Barlow b969aad67b Tweak release notes 2016-08-03 03:36:45 -07:00
James R. Barlow e70387b1af Add a simple test for image to PDF 2016-08-03 03:35:30 -07:00
James R. Barlow 44f47fba21 PDF/A: handle case of no XMP metadata gracefully 2016-08-03 02:57:25 -07:00
James R. Barlow 02584094a1 Suppress NUL bytes in metadata from input files 2016-08-03 02:47:44 -07:00
James R. Barlow 91d715ac93 Add test cases for --output-type 2016-08-03 02:47:18 -07:00
James R. Barlow 35addb8a33 Complain if Chinese is requested with settings known to not work
Should extend test for other Asian languages
2016-08-03 01:29:12 -07:00
James R. Barlow d32ea8d0dd Remove dead code from qpdf merge + PyPDF2 metadata patching
I tried "qpdf merge + PyPDF2 metadata patching" first. The problem is
that PyPDF2 produces a 1.3 by default and generally I have less
confidence it.

New approach is to stuff the Document Info metadata in the first page
with PyPdf2, cross fingers and use qpdf to merge. It's not quite as
clean and might harm the first page, but it's better than shipping
files produced by PyPDF2.
2016-08-03 01:28:27 -07:00
James R. Barlow 12575d594a Improve PDF/A validity checking at end 2016-08-03 01:26:16 -07:00
James R. Barlow 0746083301 Fix failing test case - unbound local variable in finally block 2016-08-03 01:00:38 -07:00
James R. Barlow 5c99acf6d1 Experimental change to use qpdf to merge files (disables Ghostscript)
All but one tests pass, test_input_file_not_a_pdf

Not sure if PyPDF2 metadata generation will mangle the first page.
2016-08-03 00:56:44 -07:00
James R. Barlow 2b10df7b74 leptonica: note about when it may be safe to drop <1.72 workaround 2016-08-03 00:54:37 -07:00
James R. Barlow ebe68de4ff Functional qpdfmerge with PyPDF2 for DocumentInfo block
Tests mostly passing. For the moment this is the new default.

Although PyPDF2 produces a PDF-1.3 which will be wrong for some contents
and possible should be repaired with qpdf. Again.

Looks like it could work better to merge PyPDF2 and fix everything
with qpdf.
2016-08-02 16:48:13 -07:00
James R. Barlow b17c6a146d Experimental qpdf merging
Does not copy /Catalog metadata, but otherwise functional
2016-08-02 02:19:02 -07:00
James R. Barlow 46d837c866 Clarify trusty/precise stuff 2016-08-02 01:29:33 -07:00
James R. Barlow 24856b61e4 Fix typo in readme 2016-08-02 01:29:22 -07:00
James R. Barlow 8d0c6ff616 pyvenv -> python3 -m venv
Sadly the Python developers are removing this script
2016-08-02 01:27:50 -07:00
James R. Barlow 0b24f971cd ocrmyimage: complain about ICC profiles being presumed 2016-08-02 01:22:36 -07:00
James R. Barlow bc5d3824bd Don't overload --oversample, use --image-dpi instead for images 2016-07-31 02:09:30 -07:00
James R. Barlow 4356983707 Suppress overly long stack traces on traverse_ruffus_exception 2016-07-31 02:06:44 -07:00
James R. Barlow 2414b79ee6 More cleanup of exception related errors 2016-07-31 01:48:13 -07:00
James R. Barlow 968e1546f0 Refactor image file triage 2016-07-31 01:47:57 -07:00
James R. Barlow 48213c9c3f Update release notes and readme 2016-07-29 15:25:16 -07:00
James R. Barlow f385772d21 Refactor "is this an iterable that's not a string?" test 2016-07-29 15:25:02 -07:00
James R. Barlow d257c83520 Most tests were failing at split_pages()
It seems that ruffus sometimes decides to send a ['inputfile.pdf']
instead of a bare string.
2016-07-29 14:59:17 -07:00
James R. Barlow 7b72ffec4f ocrmyimage: better handling of missing/invalid DPI 2016-07-29 14:38:07 -07:00
James R. Barlow 757f6826dc ocrmyimage - Attempt conversion to PDF if input file is not a PDF
First cut.

May have broken ruffus errors again too.
2016-07-29 14:03:19 -07:00
James R. Barlow 5df83a0d30 Travis: use Python 3.5 too 2016-07-29 13:31:40 -07:00
James R. Barlow d70e3d3753 ruffus exceptions: for clarity only, don't iterate strings
It's a good habit to ensure any iterator test is explicit about
allowing or disallowing strings.
2016-07-29 13:31:24 -07:00
James R. Barlow 0dfceedcfb Remove old OCRmyPDF 2.x from release notes; update 4.2 notes 2016-07-29 03:08:59 -07:00
James R. Barlow 2c30f4bfc5 Travis: build partly working on trusty; tweak requirements again
The build is #122
https://travis-ci.org/jbarlow83/OCRmyPDF/builds/148255615

Errors seem to be related to either Ghostscript or leptonica? Maybe
-dSAFER?
2016-07-29 03:08:01 -07:00
James R. Barlow 9e7fb52b47 Travis: add PPA to support unpaper 2016-07-29 01:57:12 -07:00
James R. Barlow bb5fd38e38 Remove additional PPA's and try again 2016-07-29 01:47:56 -07:00
James R. Barlow 7c8cf5cfa2 Try travis-trusty
This removes some backports for packages that Ubuntu trusty offers but
for which Ubuntu precise needed help.
2016-07-29 01:44:57 -07:00
James R. Barlow fef35e4eb2 Fix handling of DPI for rare case of JPEG recompression after deskew/clean
This test is exercised by page 4 of multipage.pdf. If all images are
JPEGs, and one of deskew/clean removes DPI information, make sure that
we can get the right information back and that the DPI stays square.
2016-07-29 01:34:52 -07:00
James R. Barlow 8f77576dc4 Fix non-square image resolution for "hocr" case; use img2pdf 0.2.1
Tesseract renderer not immediately fixable.
2016-07-28 16:43:51 -07:00
James R. Barlow b3fcf24a26 Refactor DPI: fix regressions in test suite
Some called functions are particular about the data format of DPI and
don't like to deal with the Decimal() returned by PyPDF2. Convert to
float and int where needed.
2016-07-28 00:19:32 -07:00
James R. Barlow 16e4d342d2 Bug fix: --force-ocr should still run on pages with no images
Useful for people who want to reprocess text.

This also requires --oversample because DPI is undefined. To be fixed
in next commit.
2016-07-27 15:06:49 -07:00
James R. Barlow 8458a51860 Tighten requirements and dependencies 2016-07-27 14:47:59 -07:00
James R. Barlow 636d1903b3 Ghostscript: do raster output with -dSAFER
-dSAFER does not work when rendering PDF/A, because that needs to load
the ICC file, and -dSAFER prevents access to external files.
2016-07-27 00:54:40 -07:00
jbarlow83andGitHub 514efa36fc Readme: Add table of contents, brew install tesseract --with-language packs 2016-07-24 11:21:46 -07:00
James R. Barlow bd48f40d3d v4.1.4 release notes 2016-07-17 00:35:06 -07:00
James R. Barlow c02dbc809a Merge commit '68cf9cbd87c188823027f9d1bfe9029017e7281f' into develop 2016-07-17 00:29:48 -07:00
James R. Barlow 410111d6fb Bug fix: Monochrome images with ICC treated as full color images
Issue #79.
User submitted PDF with ICC profile attached to the monochrome image
in the input file, which is not common but useful for PDFs that want to
define how light the paper is or how dark the black is. The code was
written to assume unusual images are full color unless it can prove
otherwise. Handle this simple case. Other ICC cases should be tested.
2016-07-17 00:29:32 -07:00
jbarlow83andGitHub 68cf9cbd87 .rst: add code-block markup 2016-07-05 14:03:55 -07:00
jbarlow83andGitHub c9b2540d9d Fix some .rst formatting errors 2016-07-05 13:48:19 -07:00
jbarlow83andGitHub 1bacf35a2c Update license information for encrypted_algo4.pdf 2016-06-24 14:25:15 -07:00
jbarlow83andGitHub 8aef0d9277 Merge pull request #76 from Jmuccigr/patch-2
Adding explicit reference to help
2016-06-24 14:21:23 -07:00
John MuccigrossoandGitHub b2fa8645ba Adding explicit reference to help 2016-06-24 13:44:12 -05:00
James R. Barlow c96823a648 v4.1.3 release notes 2016-06-23 13:47:56 -07:00
James R. Barlow 3807b7d655 Merge branch 'feature/leptfun' into develop 2016-06-23 13:45:35 -07:00
James R. Barlow a45505cf1d Fix order of operations in matrix multiplication
Issue #73. The order of operations happens to not matter for scaling
but does matter for translation. We only need scaling to find the DPI,
so the error was not noticed. Mainly useful to other uses of this
library.
2016-06-23 13:36:23 -07:00
James R. Barlow b4a734fc0d Test case for "algorithm 4" test
Algorithm 4 -> PDF version 1.6
2016-06-23 13:21:26 -07:00
James R. Barlow bbd02926e1 Add helpful error message for PDFs that use algorithm 4 2016-06-23 13:13:17 -07:00
jbarlow83andGitHub 5022ded276 Update Windows directions 2016-06-16 15:15:46 -07:00
James R. Barlow c7612152ef leptonica: pillow interop 2016-06-06 23:55:23 -07:00
James R. Barlow af91642cd1 lept: fix __getstate/__setstate 2016-06-06 23:55:23 -07:00
James R. Barlow 9c66334c38 Leptonica - ortho rotate, background norm 2016-06-06 23:55:23 -07:00
James R. Barlow 8d79b94b84 cpix -> _pix 2016-06-06 23:55:23 -07:00
James R. Barlow d7f60b96c1 More leptonica functions for page manipulation 2016-06-06 23:55:23 -07:00
James R. Barlow b964999427 Update filename references from sRGB_IEC to sRGB 2016-05-10 21:58:04 -07:00
James R. Barlow 3473345ea6 Replace sRGB_IEC with MIT license compatible sRGB
New file is from Debian package icc-profiles-free
2016-05-10 21:48:32 -07:00
James R. Barlow 349ec5c81f Provide more helpful error message if pypdf can't merge pages 2016-04-28 14:02:12 -07:00
James R. Barlow ff78d7c56c v4.1 release notes 2016-04-28 00:46:16 -07:00
James R. Barlow ff092c8629 Fix race condition between these tests when run in parallel 2016-04-28 00:39:15 -07:00
James R. Barlow fe14cb57c0 Fix ruffus exception output
I found this issue in ruffus 2.6.3
https://github.com/bunbun/ruffus/issues/65
also discussed here
https://github.com/bunbun/ruffus/pull/67

ruffus 2.6.3 RethrownJobError don't follow the normal conventions and
so its exception causes problems when they cross process boundaries.
This change carefully examines the various forms of ruffus exception
objects that can appear in 2.6.3 and parses them more carefully. It
also removes any direct posting of the exception to the logger because
this triggers another serializing of the exception object, mutating it
further.
2016-04-28 00:38:50 -07:00
James R. Barlow 507fbc01d5 Refactor _find_page_images 2016-04-27 13:13:43 -07:00
James R. Barlow 325479e5be Fix test failure: inline images with multiple image filters specified 2016-04-27 13:09:41 -07:00
James R. Barlow e926ecb8b2 Fuzzing: check for graphics stack overflow
Very unlikely to occur
2016-04-18 13:08:35 -07:00
James R. Barlow d0cb6c0e92 Replace private hypotenuse formula with hypot() 2016-04-18 13:07:53 -07:00
James R. Barlow 5b7c8cf5d3 Remove check for /ImageMask
/ImageMask means the the image is a stencil mask for a grayscale or
color image. From issue #63 a user has a PDF apparently with only a
stencil mask and no other images. According to PDF spec you're supposed
to use masks to help draw other images rather than draw masks
explicitly, although drawing masks is not forbidden.

In the event that the image mask is higher resolution than the input
 image, the composite image should be drawn at the highest DPI to a
ccurately capture the effect of the mask. It looks like the right to
 do is take masks into account rather than ignore them (a holdover
from earlier, less robust versions), or don't treat it different.
2016-04-14 14:24:33 -07:00
James R. Barlow 40baab32ac Remove dead code "import stuff in testcase" 2016-04-14 14:22:34 -07:00
James R. Barlow e877d37ac8 --rotate-pages: Only apply rotation if we're reasonable confident
Take the threshold from tesseract's default value for -psm 1.
2016-04-14 13:49:44 -07:00
James R. Barlow 5a9f77e438 Merge commit '1605408c23fa1b9252c5d3f10f279b43733b0728' into develop 2016-04-14 13:07:10 -07:00
James R. Barlow 8ddd67d1e2 Check encoding of inline images 2016-03-24 15:11:10 -07:00
jbarlow83 1605408c23 README: add libffi-dev 2016-03-22 23:39:02 -07:00
James R. Barlow 2d3b1ebf6e Simplify DPI calculation with algebraic derivation
Needs testing
2016-03-20 12:35:41 -07:00
James R. Barlow c74eaab7f5 Update license: sRGB ICC 2016-03-20 12:34:34 -07:00
James R. Barlow c21d231388 Merge commit 'a73afc4e769202b916d35dee481d741cf6bb7224' 2016-03-20 12:33:15 -07:00
jbarlow83 a73afc4e76 Merge pull request #59 from spwhitton/apt-get
README: Debian and Ubuntu installation option
2016-03-14 00:19:59 -07:00
Sean Whitton 76c364150d README: Debian and Ubuntu installation option 2016-03-13 23:15:00 -07:00
James R. Barlow 94a3e447cc Add otsu threshold to leptonica 2016-03-12 00:09:20 -08:00
James R. Barlow 12868b461a Travis: install unpaper.deb instead of compiling from source 2016-03-11 21:16:01 -08:00
James R. Barlow 322085933b unpaper: fix check for missing and old versions, add test case 2016-03-10 15:37:09 -08:00
James R. Barlow 3fed94bb79 v4.0.7 2016-03-02 06:27:01 -08:00
James R. Barlow 8c877482bd Fix leptonica initializers 2016-03-02 06:26:25 -08:00
James R. Barlow b17d589e84 Don't set -sOutputICCProfile
Ghostscript dev advised against. It appears that this is for
creating target for a device that colors in a particular format.
2016-03-02 06:25:34 -08:00
James R. Barlow 368252a243 setuptools_scm_git_archive seems suddenly broken 2016-03-01 02:09:45 -08:00
James R. Barlow ccefda1bee v4.0.6 notes 2016-03-01 01:58:32 -08:00
James R. Barlow 3d0e8c9629 Provide our own sRGB profile instead of Ghostscript's 2016-03-01 01:27:40 -08:00
James R. Barlow 313bbbb94c setup_scm_git_archive: add additional files 2016-02-29 12:46:27 -08:00
James R. Barlow 0360f078de get_postscript_icc_path: don't check the same path multiple times 2016-02-29 12:45:58 -08:00
James R. Barlow c8901666c4 Merge branch 'master' of https://github.com/jbarlow83/OCRmyPDF 2016-02-29 00:06:07 -08:00
James R. Barlow 7430006596 Improve install instructions for OS X (unpaper) 2016-02-29 00:05:31 -08:00
James R. Barlow f3e06b2dbd Add bookmarks to file for more testing 2016-02-29 00:05:07 -08:00
jbarlow83 e97df307ff Merge pull request #54 from stweil/master
Replace broken link to c't article by permalink
2016-02-28 07:18:40 -08:00
Stefan Weil 1443354aa2 Replace broken link to c't article by permalink
Update also the 2nd article link to use a permalink, too.

Signed-off-by: Stefan Weil <sw@weilnetz.de>
2016-02-28 13:57:42 +01:00
James R. Barlow 250e68c1cd v4.0.5 release notes 2016-02-27 01:01:38 -08:00
James R. Barlow 6a380ee99c Fix temporary file placed in wrong folder 2016-02-27 00:51:47 -08:00
James R. Barlow 3c90bd96a9 Remove extraneous debug print() messages 2016-02-27 00:50:58 -08:00
James R. Barlow 06a7ceb25a v4.0.4 Updates release notes 2016-02-27 00:22:37 -08:00
James R. Barlow 733a8e7d58 Merge branch 'feature/parsecontent' 2016-02-27 00:19:19 -08:00
James R. Barlow 570bbe9a05 Add comments and remove debugging, improve inline handling
Squashed commits:
[bfff3c9] pageinfo, have a main()
2016-02-27 00:18:36 -08:00
James R. Barlow 5cc3adb39a Add support for inline images 2016-02-27 00:18:36 -08:00
James R. Barlow 3957a0606c Compute image pixel density without performing rectangle intersection (+5 squashed commits)
Squashed commits:
[0e27904] Partially implement DPI calculation with rotation of the image

Fixes test suite
[a64f662] pageinfo: all tests pass
[c5b811a] Fix typos
[cdd2286] Can now find inline images for efficiently
[60dde8d] First cut at implementing intelligent DPI detection based on content stream

Broke many of the test cases
2016-02-27 00:18:36 -08:00
James R. Barlow 11a561dbce v4.0.3 release notes 2016-02-26 01:12:15 -08:00
James R. Barlow dad2198394 Log information about detected page orientations in a summary line 2016-02-26 01:07:59 -08:00
James R. Barlow e40fdc502d Always dump stack trace for unexpected errors 2016-02-26 01:06:59 -08:00
James R. Barlow d446fe5922 Fix "too few characters" reported as error by tesseract -psm 0 2016-02-21 08:53:34 -08:00
James R. Barlow 4ca90c106d Docker: fix blank JPEG2000 PDF issue 2016-02-21 04:24:21 -08:00
James R. Barlow 7c5e58a497 Fix test cases that break in Docker, improve test for running in Docker 2016-02-20 23:47:37 -08:00
James R. Barlow 323b9a5f8e Add other missing files 2016-02-20 05:34:21 -08:00
James R. Barlow cab381a339 Add JPEG 2000 test case 2016-02-20 05:13:19 -08:00
James R. Barlow fe4d4c39cd Merge commit '6f3ac46b1c176d48782347cfa14d9ef6ce773f37' into develop 2016-02-20 04:56:12 -08:00
James R. Barlow ad188d7ae1 Docker: supply openjpeg to address JPXDecode errors 2016-02-20 04:54:55 -08:00
James R. Barlow 8246cc0538 Gracefully recover from tesseract's failure to process very large images
And test cases to check this
2016-02-20 04:53:23 -08:00
James R. Barlow 6f3ac46b1c Gracefully recover from tesseract's failure to process very large images
And test cases to check this
2016-02-20 04:53:02 -08:00
James R. Barlow ac71c3be63 4.0.2rc1 - release notes, add missing file caught by Travis 2016-02-20 03:36:37 -08:00
James R. Barlow ecc0ac9b19 Fix error on --tesseract-timeout timing out 2016-02-20 03:13:23 -08:00
James R. Barlow ea4e6bf67d leptonica: serialization tweaks, memory handling 2016-02-20 02:54:53 -08:00
James R. Barlow 46c204f533 Fix leptonica pickling 2016-02-20 02:35:34 -08:00
James R. Barlow 71fbda8bf6 Adjust page orientation parsing to deal with change in Tess 3.04.01 2016-02-20 01:32:56 -08:00
James R. Barlow 9b79b4a7c8 Leptonica: documentation, helper functions 2016-02-20 01:20:06 -08:00
James R. Barlow c04cc853d7 leptonica: remove special PNM handling
We no longer use PNM as an intermediate format, so there's no need to
handle leptonica's PNM quirks.
2016-02-19 15:13:14 -08:00
James R. Barlow dd41e70ccc leptonica: nit 2016-02-19 15:11:48 -08:00
James R. Barlow 4206e74f42 tests: also check that monochrome correlation correctly detects matches 2016-02-19 14:35:31 -08:00
James R. Barlow 68c3ce56a9 Don't do chmod unless necessarily (breaks py.test on Docker) 2016-02-19 14:09:56 -08:00
James R. Barlow ab0e5fa425 Improve error checking for tesseract -psm 0 (orientation) errors 2016-02-19 03:58:39 -08:00
James R. Barlow f3b0434a87 Improve ability to capture error messages from tesseract on a crash 2016-02-19 03:48:49 -08:00
James R. Barlow aa394440db Just use the PyPI version of ocrmypdf in dockerfile
Apparently setuptools_scm_git_archive is ineffective on hub.docker.com
automatic build, it still can't find a version.
2016-02-17 15:14:23 -08:00
James R. Barlow 3b98a1a04b Fix KeyError on unexpected tess output 2016-02-17 06:05:27 -08:00
James R. Barlow fcb89b0c58 Forgot to save release notes 2016-02-17 01:48:25 -08:00
James R. Barlow ac65d6a03a v4.0: release notes 2016-02-17 01:21:17 -08:00
James R. Barlow 2103f60906 Merge branch 'release/v4.0.0' 2016-02-17 01:13:24 -08:00
James R. Barlow e3c3d848c1 Save Dockerfile comment 2016-02-17 01:11:41 -08:00
James R. Barlow d4ef3411e0 Suppress --pdf-renderer tesseract warning in Docker image
Since the corrected font is provided in the Docker image, there's no
reason to show the warning.
2016-02-17 01:03:20 -08:00
James R. Barlow 71d616e413 Restore Dockerfile on local and probably on automated build as well 2016-02-17 00:13:45 -08:00
James R. Barlow fe651d1bf5 Overwrite Tesseract 3.04 default pdf font with better pdf font 2016-02-16 21:45:44 -08:00
James R. Barlow 582ba8cfad Provide sharp2.ttf for Docker images 2016-02-16 21:45:17 -08:00
James R. Barlow d23291650a Remove duplicate line from documentation 2016-02-16 14:30:15 -08:00
James R. Barlow 812fd745b6 Remove redundant line from resources 2016-02-16 14:29:56 -08:00
James R. Barlow a87aa71d85 Remove old documentation about Pillow not linking jpeg, zlib
As of Pillow 3.0.0 this is fixed, so make Pillow 3 a requirement
2016-02-16 14:29:31 -08:00
James R. Barlow 60b2eb1455 Fix JPEG DPI: Pillow expects dpi=(x,y) 2016-02-16 07:29:20 -08:00
James R. Barlow ab3c1988c1 Work around Leptonica < 1.72 bug that breaks Travis 2016-02-16 07:03:50 -08:00
James R. Barlow ee5223eea8 Travis again: are invalid correlation measurements a use-after-free?
Try explicitly casting the value to a float.
2016-02-16 06:09:48 -08:00
James R. Barlow edd2185268 Fix pytest-runner not understanding 'norecursedirs'
As discussed here
https://github.com/pytest-dev/pytest-runner/issues/7
and sort of
https://github.com/pytest-dev/pytest/issues/567
2016-02-16 05:44:55 -08:00
James R. Barlow 35b1ca2be2 Travis: try replacing non-standard invocation of py.test
It seems the normal thing to wire up python setup.py test to invoke
the test suite rather than py.test. This may be the reason for the
past chain of cffi-related commits.
2016-02-16 05:36:14 -08:00
James R. Barlow 71e493a810 Fix case of JPEG missing DPI field 2016-02-16 05:29:32 -08:00
James R. Barlow 6178e22e7f Travis: force compile leptonica? 2016-02-16 05:24:46 -08:00
James R. Barlow ef0aab060a Make debug output more verbose on failure 2016-02-16 05:17:18 -08:00
James R. Barlow d70ce61cfd Travis: maybe it's just the missing __init__.py? 2016-02-16 05:09:12 -08:00
James R. Barlow 8cd84afac8 Revert "Try moving leptonica build script, playing with wheels a bit"
This reverts commit ec2c6c312b.
2016-02-16 05:04:20 -08:00
James R. Barlow ec2c6c312b Try moving leptonica build script, playing with wheels a bit 2016-02-16 04:05:58 -08:00
James R. Barlow 3946bba318 Too soon, try again 2016-02-16 03:51:08 -08:00
James R. Barlow 2ed0b78a7b Travis: are you creating _leptonica.py? 2016-02-16 03:47:35 -08:00
James R. Barlow ed346d032c Does Travis need explicit install libffi-dev? 2016-02-16 03:41:11 -08:00
James R. Barlow acd645f192 Fix travis syntax error 2016-02-16 02:42:47 -08:00
James R. Barlow 88433e4c34 Fiddle with travis, try to get better debug output
Essentially cffi failed somehow, not clear how
2016-02-16 02:12:14 -08:00
James R. Barlow 1224af1780 Update test resources to address files with unknown source
-Remove Test_Issue_28.pdf (inherited from fritz-hh, source unknown)
-Replace missing_docinfo.pdf (received from user, but it's a printout of
a website; unclear status, so created a new PDF with the same effect)
-Others are okay
2016-02-16 00:28:28 -08:00
James R. Barlow ab13342931 Revise rotation tests in prep for adding a few more 2016-02-15 17:17:43 -08:00
James R. Barlow d7913da484 Test case: remove filename conflict 2016-02-15 16:49:28 -08:00
James R. Barlow c50e3f1329 Complain about older tesseracts that don't have sharp2.ttf installed 2016-02-15 16:43:41 -08:00
James R. Barlow a62f86dbd7 Update release notes 2016-02-15 16:43:14 -08:00
James R. Barlow 33b88b18db Update the notes 2016-02-15 14:03:59 -08:00
James R. Barlow 7c691c21ab Fix image layer rotation for pages with nonzero crop boxes 2016-02-10 17:48:33 -08:00
James R. Barlow 4ec51729d8 Partial fix for images not anchored to (0, 0) 2016-02-10 17:14:48 -08:00
James R. Barlow 07b41e479a Cleaner access to mediabox 2016-02-09 02:19:05 -08:00
James R. Barlow 6510bcad19 DPI information not transferred automatically from PNG to JPEG 2016-02-09 02:18:54 -08:00
James R. Barlow 265d2ce39b Better skewed image 2016-02-08 23:44:46 -08:00
James R. Barlow 1928a64cae Better logging output for autorotation 2016-02-08 23:42:25 -08:00
James R. Barlow 11e575a5a3 leptonica: suppress debug output 2016-02-08 23:41:45 -08:00
James R. Barlow 7fbc0d6460 tesseract: unify logging function 2016-02-08 23:40:36 -08:00
James R. Barlow 1ba8b1aa4b unpaper is lousy at deskewing, so let leptonica do it 2016-02-08 15:26:33 -08:00
James R. Barlow 3569c76c0f Also include cardinal.pdf 2016-02-08 15:23:04 -08:00
James R. Barlow 16c7ac2582 Fix test_deskew for new Leptonica API 2016-02-08 15:20:01 -08:00
James R. Barlow 4ceb59215f Leptonica: classes are better 2016-02-08 15:14:44 -08:00
James R. Barlow 2e6879ee51 Introduce Leptonica class for Pix 2016-02-08 14:52:01 -08:00
James R. Barlow 66fc2e9d7d Add rotate 180 correlation sanity check 2016-02-08 13:10:11 -08:00
James R. Barlow 2c7a6e574f Shorten names of _make_input/output 2016-02-08 12:57:26 -08:00
James R. Barlow 78c3bf5dba Check autorotate using leptonica correlation 2016-02-08 12:55:50 -08:00
James R. Barlow 98c115e3bb Cache wasn't enabled properly for test_autorotate 2016-02-08 12:55:28 -08:00
James R. Barlow 2752bda80b Merge branch 'feature/leptdeskew' into feature/logging
Need leptonica for testing now, I think
# Conflicts:
#	ocrmypdf/tesseract.py
#	requirements.txt
#	setup.py
2016-02-08 12:34:48 -08:00
James R. Barlow 7c0940609a Take a stab at writing test case for autorotate 2016-02-08 12:32:39 -08:00
James R. Barlow d30a879e2d Fix test suite by running select_image_for_pdf unconditionally
The purpose of this change that caused the problem was a minor
optimization for the tesseract renderer path that had it pull an image
from select_image_for_pdf so that it could use a JPEG instead of PNG,
instead of taking it from preprocess_clean where it would only get a PNG
and make large files.
2016-02-08 02:33:03 -08:00
James R. Barlow b907234d5c Update tesseract spoofing to cache orientation and script detection checks
No cache: 269 s
With cache: 144 s

test_oversample[tesseract] now fails, all others good
2016-02-08 02:21:56 -08:00
James R. Barlow b0114c9174 More logging improvements 2016-02-08 01:31:15 -08:00
James R. Barlow d2ba8c501f Restore invisibletext for normal output 2016-02-08 01:14:39 -08:00
James R. Barlow 6a7ed7d359 Make logging output a lot more useful 2016-02-08 00:58:14 -08:00
James R. Barlow 6289afa1a6 Better: custom logging factory to avoid whatever ruffus is doing 2016-02-08 00:18:52 -08:00
James R. Barlow 9bb6fa04cb Return logging to a semblance of normalcy 2016-02-08 00:09:31 -08:00
James R. Barlow afb6f6f5c9 Render preview as .jpg instead of .png
Smaller file size of JPEG seems to help performance, although the
difference is only about 1%.
2016-02-07 15:49:10 -08:00
James R. Barlow 8a69671dbd Suppress debug message 2016-02-07 15:43:57 -08:00
James R. Barlow 178aee4687 Make rotation optional (for now it's off, possibly should be on) 2016-02-07 15:43:45 -08:00
James R. Barlow 8484caddfb Tweak pipeline, allowing --pdf-renderer to use JPEGs instead of PNGs 2016-02-07 15:36:51 -08:00
James R. Barlow 08313316de Cleanup auto-rotation 2016-02-07 15:06:54 -08:00
James R. Barlow 1d0eca5c63 All four rotation directions working 2016-02-07 06:09:01 -08:00
James R. Barlow fe89232a30 Fix autorotate for some lossless cases 2016-02-07 05:59:46 -08:00
James R. Barlow 4b51b521e2 Implement autorotate (provided lossless reconstruction is disabled)
Works for a single page file, probably

Although arguably rotation is not quite lossless, and the two could be
mutually exclusive anyway, so maybe this is it. Did not check in some
debugging changes (lossless=False, text debugging=True)

PyPDF seems to get merging wrong when one of the pages is rotated.
2016-02-07 03:27:33 -08:00
James R. Barlow e9ec458304 tesseract: add command to access OSD values 2016-02-07 03:21:32 -08:00
James R. Barlow 54b0ddd787 ghostscript: don't try to "help" autorotation
It uses text direction alone -- unreliable guide.
2016-02-07 03:20:42 -08:00
jbarlow83 93bec22f9c README: mention polyglot, fix container vs image 2016-02-07 00:32:20 -08:00
James R. Barlow 0dc96442d8 Fix img2pdf usage in test case (to make Travis CI happy again) 2016-02-06 23:41:32 -08:00
James R. Barlow 58f4582517 More Dockerfile repair
I'm not fully happy with this arrangement, as it effectively downloads
OCRmyPDF twice, not to mention the lengthy setup time overall.

Will need to try separate build/run images in the future, but now just
get it working again.
2016-02-06 23:13:16 -08:00
James R. Barlow 2d15c09cca Merge branch 'develop' 2016-02-06 18:18:49 -08:00
James R. Barlow 04cb8865b0 Fetch application from PyPI instead of local
setuptools_scm barfs because it can't find the version, because Docker hub
retrieves the application from Github in a way that omits the necessary
details.

I suppose there is a certain logic to Docker only using the tagged
released versions from PyPI, so go with it.  The other attractive option
is to nix setuptools_scm.
2016-02-06 18:18:30 -08:00
James R. Barlow 6fe32bbaf7 v3.2.1 2016-02-05 16:10:18 -08:00
James R. Barlow 4abb20390d Bump Dockerfile versions 2016-02-05 16:08:26 -08:00
James R. Barlow daa3916430 Fix img2pdf 0.2 usage
All tests pass when forced to rely on img2pdf, so seems okay
2016-02-05 15:13:26 -08:00
James R. Barlow e9b87cefcc Try img2pdf 0.2 2016-02-05 14:38:37 -08:00
James R. Barlow 60593b5ad3 Tighten up package requirements to deal with incompatible img2pdf 0.2 release 2016-02-05 14:37:05 -08:00
James R. Barlow f708b11ea4 Fix Python 2.7 warning 2016-02-05 02:34:49 -08:00
James R. Barlow 7982f58b2e Try tweaking Dockerfile for automated build again 2016-02-05 01:38:59 -08:00
James R. Barlow e805c1908a Minor fix for Dockerfile polyglot 2016-02-05 00:52:27 -08:00
James R. Barlow cb3ba8e973 Merge branch 'release/v3.2' into develop 2016-02-05 00:10:41 -08:00
James R. Barlow 344fc40cbc Merge branch 'release/v3.2' 2016-02-05 00:10:41 -08:00
James R. Barlow 7e5c37137b Merge branch 'develop' into release/v3.2 2016-02-04 23:42:06 -08:00
James R. Barlow 1aae11714b Update release notes for v3.2 2016-02-04 23:41:33 -08:00
James R. Barlow d82f14a7aa Update .gitignore 2016-02-04 18:51:41 -08:00
James R. Barlow 4b65e0b093 Set JPEG output quality to 95 for better transcoding 2016-02-04 18:49:09 -08:00
James R. Barlow 43b0faa830 Bug in tesseract_noop spoof: produced wrong page sizes
Now checks input image to ensure the implied page size of its .hocr file
matches the rest of the PDF.
2016-02-04 18:48:22 -08:00
James R. Barlow 8674c9fb20 Merge commit 'ccfbb54e8c26784e438ba2fcac2179f21e7d857b' into release/v3.2 2016-02-04 17:39:36 -08:00
jbarlow83 ccfbb54e8c Update release notes for v3.2
Fix the notes
2016-02-04 17:37:30 -08:00
James R. Barlow 9893ebf889 Suppress tesseract argument printout 2016-02-04 17:26:36 -08:00
James R. Barlow 303eb3e93a Merge commit 'ca546d70e5bff9e9b115371f7813f3c326822bd8' into release/v3.2 2016-02-04 17:25:56 -08:00
jbarlow83 ca546d70e5 Merge pull request #45 from spwhitton/hocrtransform-shebang-fix
fix shebang in hocrtransform.py
2016-02-04 17:21:33 -08:00
Sean Whitton 6a5ea2d64a fix shebang in hocrtransform.py 2016-02-03 17:48:35 -07:00
James R. Barlow ec3d92ad8e Reorg gitignore 2016-01-30 15:28:24 -08:00
James R. Barlow 66a095d7de Improve organization of CFFI setup 2016-01-30 15:19:40 -08:00
James R. Barlow 411981efbc Experiment with CFFI instead of ctypes 2016-01-30 15:06:25 -08:00
James R. Barlow 350ad5210e Leptonica: convert to CFFI 2016-01-20 15:03:07 -08:00
James R. Barlow f3b588764e Suppress tesseract argument printout 2016-01-20 15:02:48 -08:00
James R. Barlow b49f5a7d77 Support optionally using leptonica to deskew
unpaper doesn't seem to be good at deskewing. It fails on test case
with a lot of italics. I think it also struggles on pages with a lot
of whitespace. Leptonica continues to shine here.

However, this is only a first crack at Leptonica. The leptonica module
should be redone to use cffi (more extensible).

Also considering the possibility of making all Lept calls in a forked
process to insulate the calling process from C code crashes and the
messy redirect of stdout/stderr to read Leptonica's errors.

I don't think the redirect is a huge problem as long as multiprocesses
rather than multithreads are used. The ruffus child process that is
handling a page is single threaded and will not be affected by the
redirection. It just feels dirty. The main reason to consider a child
process is crash isolation.
2016-01-19 17:43:40 -08:00
James R. Barlow bacbcba58a Merge branch 'release/v3.2-rc1' 2016-01-19 16:58:37 -08:00
James R. Barlow 52e8aa434f Update release notes for v3.2-rc1 2016-01-19 16:49:49 -08:00
James R. Barlow 37c508f3f8 Better versioning: no silly version files, but wrong ver in development
Small price to pay.
2016-01-19 16:07:52 -08:00
James R. Barlow 26e36422cc More fiddling with version 2016-01-19 15:07:21 -08:00
James R. Barlow f82cb002bc Try automatic versioning with setuptools_scm 2016-01-19 13:27:18 -08:00
James R. Barlow c1eb047a4b Fix name of pdfa_def.ps
Used to include a copy of the parent dir's name.
2016-01-19 13:11:03 -08:00
James R. Barlow 626ca18f5c Remove stale comment 2016-01-19 13:02:35 -08:00
James R. Barlow 9058dedfbe New tests for ccitt, jbig2 encodings 2016-01-19 13:01:56 -08:00
James R. Barlow a0952bfca3 Optimize: use img2pdf stream instead of repeated copies 2016-01-18 20:24:46 -08:00
James R. Barlow 354e61946e Use os.makedirs for test output directories
Broke Travis
2016-01-16 02:47:56 -08:00
James R. Barlow fd6d1d748a Merge branch 'feature/pypdf-page-merge' into develop 2016-01-16 02:33:23 -08:00
James R. Barlow 360acd1e2c Adjust test_oversample test case
Add -f to force generation of the background image at the desired
oversample resolution.  Our new behavior is to only send the oversampled
image to Tesseract while leaving the main page intact unless asked to
deskew, clean, etc.
2016-01-15 15:55:23 -08:00
James R. Barlow fc0479f110 Fix all but test_oversample[hocr] 2016-01-15 15:46:47 -08:00
James R. Barlow 62728205b6 Implement image+text merging in other cases
5 failed, 28 passed

failures:
test_oversample[hocr], test_skip_ocr, test_skip_big, test_maximum_options[hocr],
test_blank_input_pdf,
2016-01-15 15:38:08 -08:00
James R. Barlow dc0fb25e64 Render hocr page: no longer needs an image as input 2016-01-15 15:16:47 -08:00
James R. Barlow f3e04cce56 Update pipeline.svg 2016-01-15 14:56:16 -08:00
James R. Barlow 7067110308 Add safety check to prevent merge from running when not sensible 2016-01-15 14:54:45 -08:00
James R. Barlow 599d889703 Implement "perfect reconstruction" - transfer page and watermark OCR layer
Works, does not account for changes to clean/deskew, etc.
Surprisingly, it works. PyPDF2 fixes since last attempt?
2016-01-15 14:39:12 -08:00
James R. Barlow 2fa8366632 Merge branch 'feature/test-pageinfo-cleanup' into develop 2016-01-15 14:18:01 -08:00
James R. Barlow c368c51bad New hocrtransform test 2016-01-15 14:14:08 -08:00
James R. Barlow 7c558b3713 Move pageinfo test into tests folder 2016-01-11 17:40:44 -08:00
James R. Barlow 8d323ae510 Merge branch 'feature/pagesegmode' into develop 2016-01-11 17:23:00 -08:00
James R. Barlow 3b53e9adac Use tesseract cache for -psm 2016-01-11 17:22:50 -08:00
James R. Barlow 074c1d71b4 Activate --tesseract-pagesegmode 2016-01-11 17:19:32 -08:00
James R. Barlow 1fca9a004d Adjust command line parameters
Was splitting each argument to --tesseract-config into a list of single
character strings
2016-01-11 16:57:19 -08:00
James R. Barlow b485a1ef78 Override ruffus' handling of --jobs
Ruffus treats omitted parameter as -j1. For our purposes it makes more
sense for omitting the parameter to mean "use all CPUs". As such we
must be able to distinguish -j1 from the parameter -j being omitted.

Telling ruffus to ignore the argument actually just makes it not auto
generate the argument. We can add an argument back with the same name.
2016-01-09 19:07:48 -08:00
James R. Barlow 326ef7a3ac Merge branch 'hotfix/v3.1.1' into develop
# Conflicts:
#	RELEASE_NOTES.rst
2016-01-09 18:55:04 -08:00
James R. Barlow 12bc58b5b6 Merge branch 'hotfix/v3.1.1' 2016-01-09 18:45:40 -08:00
James R. Barlow 6af0815681 Bump version 2016-01-09 18:45:06 -08:00
James R. Barlow 66c2b9b78e Merge branch 'hotfix/v3.1.1' into develop 2016-01-09 18:38:09 -08:00
James R. Barlow d03c056cb1 Supporting all languages bloats the image by an extra 1 GB
Make it a special image
2016-01-04 16:49:06 -08:00
James R. Barlow 3f94d628fa Dockerfile: remove manual build of unpaper
Fortunately unpaper now exists as binary package, eliminating the need
to install all of the build machinery and build it from source.
2016-01-04 15:07:12 -08:00
James R. Barlow a64c7dbe99 Update dockerfile: include all languages
Also update ignore files
2016-01-04 14:27:16 -08:00
James R. Barlow 61b3ccb57c Place ruffus database in temporary folder
Because we don't really use ruffus checkpoint feature, putting the
database in a permanent location does not help anything, but does cause
large database files and problems if the .ruffus_history.sqlite wanted
to be in a writable location.
2016-01-04 13:23:47 -08:00
James R. Barlow 424b4b33b1 Just go right ahead and demand Python 3.4 2016-01-04 12:56:51 -08:00
James R. Barlow e510f89792 Python 2 warning message 2015-12-21 09:38:38 -08:00
James R. Barlow 49cd6cc619 Off by one error in page info calculation 2015-12-21 09:35:02 -08:00
James R. Barlow 9aa3d340d4 Tell Travis about the cache 2015-12-17 14:02:13 -08:00
James R. Barlow 09782242c8 Adjust test cases to use cache and noop more effectively
This reduces total execution time to 164s on my machine, down from
about double that.
2015-12-17 14:00:17 -08:00
James R. Barlow 9ec4aa039d Add tesseract caching to speed up tests 2015-12-17 12:52:12 -08:00
James R. Barlow ecebe2f24b Let some tests use the spoofed tesseract
Where getting OCR doesn't matter
2015-12-17 11:56:09 -08:00
James R. Barlow 7313a77c2a Implement pdf renderer side of tess spoof 2015-12-17 11:41:54 -08:00
James R. Barlow 45113676a3 Add Tesseract spoofing 2015-12-17 11:36:47 -08:00
James R. Barlow 102bd07019 Check for encrypted PDF and complain appropriately 2015-12-17 10:37:54 -08:00
James R. Barlow 9622e31da9 Use envvars in a new test case
And get rid of the messy binary replacement spoofing
2015-12-17 09:29:01 -08:00
James R. Barlow 1731ce2a44 Environment variables can now override default programs 2015-12-17 09:05:10 -08:00
James R. Barlow 276f421c44 Did a quick test of Ghostscript vs QPDF at PDF page splitting
qpdf won so hard it wasn't funny, even though it must be called once
per page to do the job. Perhaps Ghostscript interprets it as a call to
render the page?

time bash qpdf-test.fish ../tests/resources/multipage.pdf
        0.07 real         0.02 user         0.03 sys

time gs -sDEVICE=pdfwrite -dSAFER -o '%06d.pdf' ../tests/resources/multipage.pdf
        5.12 real         5.06 user         0.04 sys
2015-12-17 08:49:08 -08:00
James R. Barlow 133357779a All subprocess invocations refactored out of main.py 2015-12-17 08:31:18 -08:00
James R. Barlow 5d8167b232 Move PDF validation check to qpdf.py 2015-12-17 08:28:00 -08:00
James R. Barlow e76ae8c46c Move more qpdf calls into qpdf.py 2015-12-17 08:24:48 -08:00
James R. Barlow 53a7c0e668 Refactor qpdf subprocess calls into module 2015-12-17 08:19:53 -08:00
James R. Barlow 4ca243e490 Merge commit '9f374461559460527e47237323e511123f31b6b0' into feature/envvars 2015-12-17 07:27:26 -08:00
jbarlow83 9f37446155 Merge pull request #34 from shemgp/master
Don't exit when qpdf repairs the file successfully but displays warning
2015-12-16 20:46:47 -08:00
Shem Pasamba d7c7559b05 Use boolean instead of integers 2015-12-17 11:23:27 +08:00
Shem Pasamba b2b66d1344 Don't exit when qpdf repair was successful 2015-12-17 11:20:20 +08:00
James R. Barlow 5d111a3c04 Refactor tesseract --pdfrenderer calls to tesseract.py 2015-12-16 17:48:26 -08:00
James R. Barlow 10416f847f Migrate tesseract-hocr code to tesseract module, because modularity 2015-12-16 17:36:11 -08:00
James R. Barlow 79b3472b26 All tests passed, bump version 2015-12-04 04:31:01 -08:00
James R. Barlow f1b2f1ae08 Merge branch 'feature/pdfa-2' into develop 2015-12-04 04:04:08 -08:00
James R. Barlow ee7d97ae8c Trivial 2015-12-04 04:03:38 -08:00
James R. Barlow 7d9f473bb1 Remove eval() call by introspecting ExitCode 2015-12-04 03:34:53 -08:00
James R. Barlow e77a5e5e75 We don't want threads. Really. Do. Not. Want. 2015-12-04 03:11:38 -08:00
James R. Barlow 6ab19af122 Comments 2015-12-04 03:09:39 -08:00
James R. Barlow 276fe49867 Better error messages for input file not found or invalid
Not as good finding a general way to deal with ruffus exceptions, but
better than nil.
2015-12-04 03:07:53 -08:00
James R. Barlow acb31abe86 Fix issue #20 - fails on uppercase .PDF 2015-12-04 02:14:09 -08:00
James R. Barlow 4f964a3c8a Introduce --pdf-renderer auto
Tess 3.03's has various quality problems like wrong DPI that are fixed
in Tess 3.04. Idea here is to introduce an option to let OCRmyPDF
select the rendering backend based on the options and system.

However, we're not ready for tesseract as the main renderer.
Setting pdf-renderer to tesseract does not pass all test cases, mainly
the one where --tesseract-timeout is triggered, and some others.
2015-12-02 23:20:31 -08:00
James R. Barlow df1fda7438 pageinfo: workaround PyPDF extractText limitations on hidden text
It appears that extractText() does not find all text. At a glance it
may be that Tesseract's PDF renderer generates a font and uses glyphs
that map to different Unicode code points that PyPDF expects, so it
discards the content and finds nothing. As a proxy in lieu of better
PDF parsing, assume that a "GlyphLessFont" means there is a text there.

I had previously found it does not work to check for the presence of a
font on page. Some PDF generators create a font resource entry even if
the font is never called for.
2015-12-02 23:16:36 -08:00
James R. Barlow d6124c1787 pageinfo: improve robustness of text test for Tesseract produced PDFs 2015-12-02 03:12:52 -08:00
James R. Barlow 80d89b5420 Set /Creator metadata to OCRmyPDF
with reference to Tess version and settings
2015-12-02 02:19:39 -08:00
James R. Barlow 74059eecf1 Choose PDF/A-2b by default instead of A-1b 2015-12-02 01:48:10 -08:00
James R. Barlow 78697341a2 pytest: don't run tests that happened to be part of pyvenv 2015-12-02 01:19:43 -08:00
James R. Barlow cfb56dd8ff Merge commit 'b1769cbe18e6380ddfe96b3b22e6d02cb603338b' into develop 2015-12-01 00:40:43 -08:00
jbarlow83 b1769cbe18 README: El Capitan supported now, Py3.5 supported 2015-11-26 16:31:33 -08:00
James R. Barlow 955b801e7f Merge branch 'master' into develop 2015-09-14 00:34:21 -07:00
James R. Barlow 3cea3f1afe Try to work around git binary file bug again 2015-09-14 00:34:16 -07:00
James R. Barlow fd4a227ccb Force this file to stop thinking it was modified 2015-09-13 17:53:01 -07:00
James R. Barlow 19c3097483 Update notes 2015-09-13 17:51:18 -07:00
James R. Barlow cdd1a6d03c Suppress failing test 2015-09-10 07:01:14 -07:00
James R. Barlow 5fb8411571 Try new PPA for libav 2015-09-10 06:01:59 -07:00
James R. Barlow 334a15b8c7 typo fix 2015-09-10 05:01:44 -07:00
James R. Barlow 6390736577 ffmpeg-dev instead? 2015-09-10 04:27:57 -07:00
James R. Barlow d55a214516 Autoreconf? 2015-09-10 04:10:12 -07:00
James R. Barlow 0994164b9a travis: apt-get install in wrong place 2015-09-06 01:43:47 -07:00
James R. Barlow 54ee0dd147 travis: fix typo 2015-09-06 01:39:54 -07:00
James R. Barlow 47c7990fb3 travis: build unpaper with cache 2015-09-06 01:38:01 -07:00
James R. Barlow 997e95de4d travis: build unpaper 2015-09-06 01:29:07 -07:00
James R. Barlow 44204be256 Fix order of PPAs 2015-09-06 00:54:50 -07:00
James R. Barlow 9b1d9aa88a travis: improve, add new PPA, etc. 2015-09-06 00:41:23 -07:00
James R. Barlow b775762f6a travis: doesn't like gcc-4.8, try just gcc 2015-09-06 00:23:05 -07:00
James R. Barlow df1a28e319 Travis needs sudo mode 2015-09-06 00:21:20 -07:00
James R. Barlow c300b2802a travis: tabs -> spaces 2015-09-06 00:08:25 -07:00
James R. Barlow 01040ace4c More complete travis.yml 2015-09-06 00:02:58 -07:00
James R. Barlow 8367172e0b Start setting up Travis CI 2015-09-05 23:44:43 -07:00
James R. Barlow 09afd8d25d Move to my repo: github.com/fritz-hh => jbarlow83
I made several efforts to contact fritz but he is no longer
communicating, and to set up Github integrations with Docker and Travis
CI I need admin access. Which I don't have. So I'm moving it to my own
and aiming the old one at me.
2015-09-05 01:14:54 -07:00
James R. Barlow 7ed60429b3 Test case: No longer using JHOVE
So JHOVE will not claim this is an invalid PDF and we should see it
reported as valid.
2015-09-05 01:12:33 -07:00
James R. Barlow 281eafada0 bump to v3.0 and move repos 2015-09-05 00:53:14 -07:00
James R. Barlow c14e10128a Bump version to -rc9 2015-08-29 16:43:22 -07:00
James R. Barlow 3270635192 ghostscript: quiet startup on rasterize 2015-08-28 04:51:36 -07:00
James R. Barlow 3d26257710 Add test cases for additional image formats 2015-08-28 04:51:11 -07:00
James R. Barlow c4f134d694 Prevent running validation on missing file after an exception is thrown 2015-08-28 04:48:29 -07:00
James R. Barlow 83f9dfbac4 Use png256 raster device when possible
Someone reported a bug where the .png input to unpaper ended up being
type 'P' (palette) for some reason, which was not supported in unpaper.

Not sure how it happened, but seemed easier to fix by explicitly
supporting. Here we use png256 if it would capture all colors in the
input file. It's up to tesseract/reportlab to make use of the palette
PNG when rendering.
2015-08-28 04:47:57 -07:00
James R. Barlow 3a445ad5f7 unpaper: support paletted files by conversion instead of bailing 2015-08-28 04:44:26 -07:00
James R. Barlow c6d106ec33 Throw exception if iccprofiles not found instead of returning None
So far iccprofiles were only missing for a user who had a custom and
possibly broken ghostscript installation.
2015-08-28 03:59:35 -07:00
James R. Barlow 2ce6834be4 Bump to -rc8 2015-08-24 01:25:01 -07:00
James R. Barlow b376672dbc Bug fix: exception thrown if input PDF was missing DocumentInfo block 2015-08-24 01:23:30 -07:00
James R. Barlow d07db8547f Merge branch 'master' of https://github.com/fritz-hh/OCRmyPDF 2015-08-23 12:30:46 -07:00
James R. Barlow aab08bfcc7 Fix requirements.txt problem 2015-08-23 12:30:40 -07:00
jbarlow83 e0a25494ee Explain the need for multi core, etc 2015-08-22 13:34:42 -07:00
James R. Barlow fd876d5e4e Merge branch 'develop' 2015-08-22 01:51:44 -07:00
James R. Barlow ee7f008ff5 Require unpaper 6.1; no messing around with broken versions 2015-08-22 01:51:08 -07:00
jbarlow83 d9161a6ddb Update README: docker run instructions 2015-08-22 01:50:13 -07:00
jbarlow83 f8d66768e3 Update README with docker install instructions 2015-08-22 01:33:12 -07:00
James R. Barlow 4f3673d14d Update notes for -rc6 2015-08-22 00:40:07 -07:00
James R. Barlow 1712fdb74a Merge branch 'feature/docker-debian' 2015-08-22 00:32:27 -07:00
James R. Barlow 3a5ffc79e0 Stock debian unpaper is no good; replace with 6.1 built from source
debian and ubuntu both install unpaper 0.4.2 or so. No .deb packages
available at higher version numbers although ArchLinux had something.
Considered making a separate image to handle building and install but
decided that was a premature optimization at this point, so just build
the unpaper that works. All tests pass.
2015-08-22 00:30:39 -07:00
James R. Barlow 859b063444 Fixup other docker test suite errors
Outstanding failures:
test_pageinfo::test_jpeg
tests involving unpaper due to version <6.1 failures
2015-08-20 02:37:03 -07:00
James R. Barlow bd61e7c644 dockerignore *.pyc
https://github.com/docker/docker/issues/13113
Docker kinda sucks. No recursive exclusion.
2015-08-20 02:27:07 -07:00
James R. Barlow c9abf282b5 Set docker locale to utf-8
Shocked, shocked, that there's a Linux distribution out that there isn't
doing the right thing and setting up utf-8 by default. (Many tests failed)
2015-08-20 01:44:30 -07:00
James R. Barlow 9dad40b5a3 Major overhaul of the Dockerfile
Switched from Ubuntu to debian:stretch because stretch has more recent
versions of our binary packages and starts smaller.  In particular,
stretch has both pillow==2.9.0 and reportlab==3.2.0 available as system
packages which saves the considerable hassle of install a toolchain.

Instead, a pyvenv is set up with access to system's site-packages (note:
needs two steps), making the binary-dependent packages available.  Then
the remaining packages are installed into the pyvenv with --no-cache-dir
to avoid saving files. And there we are.

Image is still very large (>500 MB), but programs like reportlab require
font rendering capabilities so they pull in large portions of the Linux
graphics stack. Not much will shrink that.
2015-08-20 01:25:31 -07:00
James R. Barlow 8e2d690cb0 Rework Dockerfile, setup.py to work with wheels for better cache use 2015-08-19 13:43:32 -07:00
James R. Barlow c132e091e1 Dockerfile: use local copy of application 2015-08-19 13:10:58 -07:00
James R. Barlow 630e6cbf1e pip chokes on Unicode filenames? 2015-08-18 23:56:30 -07:00
James R. Barlow 83ff5760a8 Dockerfile comment cleanup 2015-08-18 23:41:41 -07:00
James R. Barlow fed0ee638e Fix ruffus writing to RO directory in container 2015-08-18 23:30:06 -07:00
James R. Barlow cc161780df Replace fileinput with regular open-replace
fileinput is supposed to save time in these cases but it's not capable
of doing both in-place rewrites and working with a non-ascii encoding.
This was not noticed until characters outside of ASCII were picked up
by tesseract and saved in a HOCR file. Rework some surrounding code as
well and add multilingual test cases.
2015-08-18 23:27:50 -07:00
James R. Barlow 898b2b000a Works 2015-08-18 05:38:05 -07:00
James R. Barlow b3ee743ed7 WIP on docker 2015-08-18 04:46:25 -07:00
James R. Barlow ef17b669fe README needs ghostscript 2015-08-18 03:27:39 -07:00
James R. Barlow 2dff3e07ce Drop libxml2 dependency
It seems that Python's internal XML parser is good enough to do the job.
2015-08-17 15:26:07 -07:00
James R. Barlow 53c88093ad Bump to -rc5 2015-08-16 02:19:04 -07:00
James R. Barlow 0ec13d3a17 Fix test cases: minor issues
-os.environ directly modified when whole suite run, breaking subsequent
tests
-no longer trusting JHOVE for PDF/A validation
2015-08-16 01:57:35 -07:00
jbarlow83 0d5104049a Update README with better install instructions 2015-08-16 01:28:28 -07:00
James R. Barlow ce8fa69785 Update readme 2015-08-16 00:59:57 -07:00
James R. Barlow 30072e0c70 Pillow sucks
Far from being fluffy or friendly, Pillow silently allows installation
of itself without support for major image types.  Reportlab calls for
pillow 2.4.0.  On Ubuntu 14.04 LTS this will trigger an upgrade of
pillow that will be built without JPEG or ZLIB so it is effectively
neutered, and unfortunately Pillow will not detect this situation at
install time and guide users to a resolution.  Instead, you see nasty
stack traces.

So add a run-time check to ensure that Pillow is sane and capable of JPEG
and PNG support since both may be used internally.
2015-08-16 00:54:03 -07:00
James R. Barlow eb04a890b2 Relax Pillow requirement for Ubuntu 14.04 LTS 2015-08-15 15:55:56 -07:00
James R. Barlow 0c53adb04f setup: rollback lxml version to 3.3.3 - that's the latest in Ubuntu 14.04 2015-08-15 15:25:58 -07:00
James R. Barlow ee5a43fd47 setup: suppress jhove errors 2015-08-15 15:25:30 -07:00
James R. Barlow c43d6c2cbe Merge branch 'develop' of https://github.com/fritz-hh/OCRmyPDF into develop
Conflicts:
	setup.py
2015-08-15 15:18:41 -07:00
James R. Barlow 87aeeacb04 Fix erroneous instruction to "apt-get install tesseract"
Should be tesseract-ocr
2015-08-15 15:17:38 -07:00
James R. Barlow 6b26e9cad6 Fix erroneous instruction to "apt-get install tesseract"
Should be tesseract-ocr
2015-08-15 15:12:05 -07:00
James R. Barlow 85af0f0d03 Add test case for blank PDF page 2015-08-14 00:46:50 -07:00
James R. Barlow f6f4705ea3 Remove Java from setup.py 2015-08-14 00:44:56 -07:00
James R. Barlow a4702bff22 Possible fix for issue #111 2015-08-13 23:10:22 -07:00
James R. Barlow 73c5c48f79 Update notes 2015-08-13 23:08:29 -07:00
James R. Barlow adf495e8cc Remove JHOVE
JHOVE is not an effective PDF/A validator, as detailed in this article:
http://www.pdfa.org/2014/12/ensuring-long-term-access-pdf-validation-with-jhove/

In short, it's buggy. Out of 670 invalid PDF/A files in a test suite,
it only flagged 5.  It only looks for certain problems that Ghostscript
generated PDFs are unlikely to have.  So use qpdf as a final check for
general ill-formed PDF problems since it is quite reliable.

JHOVE 1 is no longer maintained. There's a JHOVE 2 but it has no PDF
support.  I also don't know if it's appropriate to bundle JHOVE, with an
LGPL, under this project and its current license.

Removing a dependency on Java is a huge win.  A world with less Java is
a world with less AbstractFactoryConstructorInterfaces.
2015-08-11 15:31:32 -07:00
James R. Barlow 9247ea00bf Improve ruffus exception handling
ruffus swallows the return code if the process of handling an exception
we hit an error in ruffus' own code, which can happen.  So pick through
its error stack and find out if there's an interesting return code in
there.  Had to use eval() of all things.

Also suppress the stack trace for normal error conditions that don't
need one.
2015-08-11 02:19:46 -07:00
James R. Barlow a1238d7bf9 Document override binary test 2015-08-11 00:44:43 -07:00
James R. Barlow 2d63268f0f Work around JHOVE bug for now, so that the test passes 2015-08-11 00:23:48 -07:00
James R. Barlow 1cb5f6a90d Refactor exit codes; test for missing tessdata
Some versions of tesseract installed by homebrew end up without a
functional tessdata folder, and tesseract is not helpful in this
situation, so add a new test to make sure our output is at least
indicative of the problem.

In the process of properly handling return codes I discovered
test_override_metadata triggers a NPE inside JHOVE probably due to the
Unicode character checking.  This could be specific to my JRE (1.6.0_65,
Oracle) but it's probably JHOVE's fault.  A valid PDF/A (per Acrobat)
is still generated.
2015-08-11 00:17:02 -07:00
James R. Barlow 8d848284df Fix code, test case: complain when GS fails to produce PDF/A
Modified pipeline to fix regression and return the proper error code if
we did not produce a PDF/A as expected.  The wrapper forces the output
to be PDF 1.3 which is not PDF/A compliant.

The funny thing is that in some cases JHOVE incorrectly states that a
file is PDF/A-1b compliant, well formed and valid, even when it is not
according to Acrobat XI and is missing the PDF/A metadata marker, as
far as I can tell.  JHOVE may not be as beneficial as hoped.
2015-08-10 16:05:00 -07:00
James R. Barlow 8fe54d1a5c Add new test case to check invalid PDF/A case
It revealed a regression - return code not the same as v2.x for invalid
PDF/A.  It's also not easy to get the return code out of ruffus.  Will
need to tweak the final step of the pipeline.
2015-08-10 13:57:28 -07:00
James R. Barlow 11dd9f14c3 setup.py: block unsafe 'upload', say to use twine instead 2015-08-09 14:16:30 -07:00
James R. Barlow 16d24f1166 Bump version to -rc4 2015-08-05 23:26:38 -07:00
James R. Barlow 97015ef775 Add a test case to check on the @argumentsfile syntax 2015-08-05 23:17:38 -07:00
James R. Barlow 2744dafb74 New test case: ensure metadata is preserved from input to output 2015-08-05 17:09:38 -07:00
James R. Barlow 7b268dbe1a Remove duplication in test case 2015-08-05 16:57:04 -07:00
James R. Barlow 8fcbbcef94 Improve usage text 2015-08-05 16:56:53 -07:00
James R. Barlow 8f93f0a06e Tidy docs 2015-08-05 16:56:30 -07:00
James R. Barlow 387142488c Kill duplicate file 2015-07-31 01:57:16 -07:00
James R. Barlow 6887e232fc Bug fix: exception from process timeout should be TimeoutExpired 2015-07-31 00:06:58 -07:00
James R. Barlow 6ac7ffd77b Merge branch 'feature/drop-mupdf-poppler' into develop 2015-07-30 23:38:27 -07:00
James R. Barlow b28faa582a Automatically use all available cores unless told not to 2015-07-30 23:20:21 -07:00
James R. Barlow 454ee029c8 Run final ghostscript in multithreaded mode
This step is serialized so all cores are not busy at this stage.
2015-07-30 23:20:04 -07:00
James R. Barlow a036de318e Replace mupdf and poppler with qpdf
Drop two dependencies and replace them with one that does the job of
both.  Smells like progress.

mupdf does PDF file repair and rendering
poppler does rendering and page splitting
qpdf does PDF file repair and page splitting
ghostscript does PDF file repair, rendering, and page splitting (sort of)

So we use qpdf.  Ghostscript's page splitting is supposed is less
efficient because it reprints the page (PDF -> Postscript -> PDF) and
possibly loses quality.  qpdf's library could be used to improve
performance.

This causes a slight performance regression:

py.test tests/test_main.py::test_maximum_options went from 187 seconds
up to 192.  This is likely due to O(n) serialized invocations of qpdf
compared to a single serialized call to pdfseparate.  Could improve on
this situation by using the example code in qpdf: pdf-split-pages.cc
or create marker files in split_pages() and then write a new @transform
function that would split pages on each CPU.  Probably not worth it,
overall, unless this causes problems on files with hundreds of pages.
2015-07-30 04:16:35 -07:00
James R. Barlow 9918c4020e Use img2pdf in test case because it does a better job 2015-07-30 03:35:56 -07:00
jbarlow83 3d6264e1b8 Fix formatting of 'motivation' 2015-07-28 17:58:26 -07:00
jbarlow83 1c25270503 Improve instructions for users that need sudo or venv 2015-07-28 17:55:56 -07:00
James R. Barlow 47e50f82c4 setup.py: allow mutool 1.7 2015-07-28 13:37:32 -07:00
James R. Barlow 27ecdfbba8 More fixes to error cases in setup.py 2015-07-28 13:05:23 -07:00
James R. Barlow 6901550065 Fix some installer issues 2015-07-28 12:41:24 -07:00
jbarlow83 6e6f918630 Actually link the release notes 2015-07-28 12:21:57 -07:00
jbarlow83 4633812246 Fix git clone command with one I tested ;) 2015-07-28 12:20:09 -07:00
jbarlow83 14bd1555aa Update README with more detailed instructions 2015-07-28 12:15:37 -07:00
James R. Barlow b9d7687fa0 Fixes: clarify install instructions and reactivate external program checks 2015-07-28 05:44:15 -07:00
James R. Barlow 93b36965e2 Merge branch 'develop'
# Conflicts:
#	RELEASE_NOTES.md
#	src/config.sh
#	src/hocrTransform.py
#	src/ocrPage.sh
2015-07-28 04:59:49 -07:00
James R. Barlow 9e0c443c2f -rc2: because pypi won't accept -rc1 2015-07-28 04:55:10 -07:00
James R. Barlow 60832152b1 Don't mess with options 2015-07-28 04:46:21 -07:00
James R. Barlow 6a160d22fe Update release notes, add copyrights 2015-07-28 04:36:58 -07:00
James R. Barlow e35526192c More test cases 2015-07-28 03:02:35 -07:00
James R. Barlow bea57bdded More test cases for other parameters 2015-07-28 02:31:18 -07:00
James R. Barlow 2a9da225e4 Minor tweaks to uncommon arguments 2015-07-28 02:25:50 -07:00
James R. Barlow a3f37de9b5 Test cases for --tesseract-timeout 2015-07-28 01:47:30 -07:00
James R. Barlow 6064160953 Get rid of subprocess call on import of tesseract, unpaper -- bit nasty 2015-07-28 01:00:29 -07:00
James R. Barlow 8508141314 Drop nose, all tests working reasonably again
Although the real issue was that the ruffus pipeline cannot be executed
twice in the same process due to its reliance on global variables.

The new OO pipeline in ruffus 2.6 would be one resolution that would
allow for more comprehensive testing as opposed to farming out the
execution to subprocess and inspecting the results, as is currently
done.
2015-07-28 00:43:22 -07:00
James R. Barlow 1c95597882 nose can't really handle external tests so looking into py.test instead
Specifically it trips over the need to reimport ocrmypdf.main.  That in
turn raises questions about whether to make that function into an
external script that imports ocrmypdf... or something else.  Would be
possible with a loop that manipulates sys_argv and then reloads
ocrmypdf.main; might need that anyway.
2015-07-27 22:07:04 -07:00
James R. Barlow 587fa63c8e --oversample: Default to 0 2015-07-27 20:42:16 -07:00
James R. Barlow b40eec4cb0 Add --oversample test for hocr rendering 2015-07-27 17:18:02 -07:00
James R. Barlow 7bcd48c269 Add test to confirm that metadata is transferred to final PDF/A 2015-07-27 16:11:51 -07:00
James R. Barlow 2e7cd52c0f Improve argument handling, test cases 2015-07-27 15:39:54 -07:00
James R. Barlow 77d4cb367e Put ghostscript in a module 2015-07-27 15:22:00 -07:00
James R. Barlow 2c45c5abc6 Implement tesseract timeout 2015-07-27 04:23:37 -07:00
James R. Barlow a89afabd79 Implement tesseract PDF rendering as an alternative
It's much better a rendering text baselines than hocr and seems to
produce small file sizes, so it's progress.  Not available for
Tesseract 3.02 obviously, so both modes need to remove available.
2015-07-27 04:20:49 -07:00
James R. Barlow 03f7c9bf07 setup.py: Only do program checks when installing 2015-07-27 02:14:51 -07:00
James R. Barlow d5f4862749 setup.py: check for third party program requirements 2015-07-27 01:45:17 -07:00
James R. Barlow 8aced0b6d3 More testing: JPEG 2015-07-27 00:25:43 -07:00
James R. Barlow 6b9adef684 Don't create inline images in output PDFs
...except that Ghostscript will sometimes turn out of line images into
inline images on its own, possibly if file size is small.
2015-07-26 21:43:49 -07:00
James R. Barlow 5440d988fc Make this PDF a whole image page
Originally it had a smaller image centred in a page, which is not quite
supported.
2015-07-26 18:32:50 -07:00
James R. Barlow 30da4fc569 pageinfo: drop pdftotext and use PyPDF instead 2015-07-26 18:23:37 -07:00
James R. Barlow 2c1b5e100b Test cases for pageinfo; complain about inline images 2015-07-26 18:18:41 -07:00
James R. Barlow 3684f278ed Add some pageinfo test cases; found problem with inline images 2015-07-26 15:24:42 -07:00
James R. Barlow 6c3cb6acba Remove redundant *res_render 2015-07-26 12:56:10 -07:00
James R. Barlow b98ba8d174 Replace .md with .rst
Github supports both, and PyPI expects .rst files, so use .rst and make
everyone happy.

Auto-converted using pandoc
find . -name '*.md' | parallel pandoc --from=markdown --to=rst --output='{.}.rst' '{}'
http://bfroehle.com/2013/04/26/converting-md-to-rst/
2015-07-26 03:01:18 -07:00
James R. Barlow d3088829af More packaging changes: move jhove, fix console script 2015-07-26 01:52:08 -07:00
James R. Barlow 9aaaba1714 Packaging stuff 2015-07-25 23:45:13 -07:00
Jim Barlow 9adb0d696f Prepare for Python packaging - move to ocrmypdf folder 2015-07-25 18:22:04 -07:00
Jim Barlow c270f1ba5f Update release notes so far 2015-07-25 18:18:37 -07:00
Jim Barlow 7b255b575a Metadata override from command lien 2015-07-25 18:12:25 -07:00
Jim Barlow d7a9f3a2ab Transfer Unicode document information from input PDF to output PDF
What a pain getting Unicode right, but there it is.

I cannot find anything to confirm that it is acceptable to put the PDF/A
definition file at the end of the Ghostscript inputs.  I did this because
Ghostscript seems to copy document info from the last document on the
list so reportlab's information "wins" in normal order, so it fixes that
issue, and reportlab 'helpfully' fills in all of those fields even if it
does not have information.

It could also work to pass document information along to reportlab, and
set it in each output PDF: .debug.pdf, .rendered.pdf, and .page.pdf to
ensure that whatever page is last in the pipeline has the right
information. Or perhaps it's possible to write a Postscript trailer that
overwrites any previous docinfo with no side effects, but I can't find
any information on how to do that.  I don't think it's worth pursuing
unless this arrangement causes some problem with PDF/A generation.

On a minor note, Jhove misreads the way I have encoded the strings in
producing its validation log.  It reads them as UTF-16 little endian, so
will tend to produce a string of Asian characters in place of the real
data.
2015-07-25 18:05:25 -07:00
Jim Barlow abf2e7e9bb Copy document metadata from source document into output (untested)
This works for ASCII only; will do Unicode version.
2015-07-25 15:31:02 -07:00
Jim Barlow 72e5fa9ba0 Reimplement debug pages 2015-07-25 14:14:02 -07:00
Jim Barlow 32c1078d2c Reimplement skip text pages 2015-07-25 14:13:32 -07:00
Jim Barlow 133f901a69 Change @subdivide to @split
@split is for "1 to many" operations, so it's the right tool for this
case.
2015-07-25 02:58:34 -07:00
Jim Barlow 42cd683ec0 Try to make pdfinfo less obnoxious by printing too many decimals 2015-07-25 02:47:59 -07:00
Jim Barlow 151eb05377 For now, unpaper is the only deskew provider 2015-07-25 01:46:16 -07:00
Jim Barlow 16177d0a52 Remove ability to override temporary (working) folder
Little point to this feature - on most platforms the environment
variable can be overridden if desired to set a new root location.

At the same time, this change removes the ability to resume a partially
executed pipeline by deleting all of the results on failure.  If -k is
provided then the temporary files will survive but there's no way to
resume from them.  Because resuming doesn't really work away and would
only be useful to users experiencing very specific problems, this is
probably not worth it, so no major loss.  The intent of -k is to assist
debugging.
2015-07-25 01:45:26 -07:00
Jim Barlow 5ce544289f Automatically try to use all available CPUs 2015-07-25 01:10:14 -07:00
Jim Barlow 77bd35c3c7 Remove duplicate test folder 2015-07-25 01:00:40 -07:00
Jim Barlow 0c5c208db0 Goodbye, so long, farewell, shell... 2015-07-25 00:57:07 -07:00
Jim Barlow 60eb745331 Split selecting final image and render PDF result into separate tasks
Simplifies the logic - one deals with all images, the other details
with an image and .hocr. Also add JPEG reconversion.
2015-07-25 00:54:00 -07:00
Jim Barlow 9f90b5cb0a Modularize unpaper; get -d and -c working again 2015-07-25 00:22:56 -07:00
Jim Barlow 5adff94545 Remove more dead/old code 2015-07-24 15:41:24 -07:00
Jim Barlow aa2baabfa9 Implement deskew and clean using unpaper 2015-07-24 15:19:37 -07:00
Jim Barlow 75c2b23efc Cleanup externals 2015-07-24 02:01:19 -07:00
Jim Barlow 6451017962 Implement oversample 2015-07-24 01:56:44 -07:00
Jim Barlow 0f857a6a34 Put .rendered.pdf files into temp folder 2015-07-24 01:56:19 -07:00
Jim Barlow 7638a88a6a Change 'clean' to 'repair' for clarity since 'clean' is what unpaper does 2015-07-24 01:55:54 -07:00
Jim Barlow bed12d2021 Remove 'pdftoppm' renderer
Ghostscript is more reliable than Poppler's pdftoppm renderer. gs is
also a hard dependency, as the only open source tool that can produce
a PDF/A file, while Poppler could be removed.  pdftoppm has awkward
syntax with some special handling needed for different versions.  I have
found isolated rendering bugs with pdftoppm as well.

With that, I'm removing supporting for multiple rasterizers.

A minor advantage of pdftoppm is that its code produced JPEGs where
possible, but this can be achieved with gs.
2015-07-24 01:35:33 -07:00
Jim Barlow 587569fcb6 Tidy up 2015-07-24 01:27:01 -07:00
Jim Barlow 8c0dc9a06d Platform independent search for iccprofiles for PDF/A 2015-07-24 01:18:46 -07:00
Jim Barlow 289e4025ad First successful PDF/A produced by new pipeline 2015-07-23 23:28:32 -07:00
Jim Barlow 5476eafe4c Rasterize PDF pages and generate .hocr files 2015-07-23 23:09:29 -07:00
Jim Barlow df32f283cd Langauge checking 2015-07-23 18:38:59 -07:00
Jim Barlow 68ecaac9cc Add tesseract version check 2015-07-23 17:06:00 -07:00
Jim Barlow cffd4623ca Add PDF/A validation 2015-07-23 17:05:34 -07:00
Jim Barlow 6dc2782e80 Can now generate PDF/A files, multipage and single page 2015-07-23 04:57:31 -07:00
Jim Barlow 5df187c086 Wrap a proxy around pdfinfo block so it can be passed around processes 2015-07-23 03:49:30 -07:00
Jim Barlow 7fd172e41e Get rid of chdir, replace deprecated @split with @subdivide 2015-07-23 03:09:03 -07:00
Jim Barlow 619528a1b5 Try a method for passing along the pdfinfo struct 2015-07-23 02:39:42 -07:00
Jim Barlow 596d468c14 Reinstate WrapperLogger with more multiprocessing fixes 2015-07-23 02:26:09 -07:00
Jim Barlow eddbf1060a diff --git a/src/ocrmypdf.py b/src/ocrmypdf.py
index 68d1591..95afa8f 100755
--- a/src/ocrmypdf.py
+++ b/src/ocrmypdf.py
@@ -24,6 +24,7 @@ import ruffus.cmdline as cmdline
 from .hocrtransform import HocrTransform

 import warnings
+import multiprocessing

 warnings.simplefilter('ignore', pypdf.utils.PdfReadWarning)

@@ -96,7 +97,7 @@ debugging.add_argument(
     '-k', '--keep-temporary-files', action='store_true',
     help="keep temporary files (helpful for debugging)")
 debugging.add_argument(
-    '-g' ,'--debug-rendering', action='store_true',
+    '-g', '--debug-rendering', action='store_true',
     help="render each page twice with debug information on second page")

@@ -106,51 +107,19 @@ if not options.temp_folder:
     options.temp_folder = 'tmp'

-_logger, _logger_mutex = cmdline.setup_logging(__name__, options.log_file,
-                                               options.verbose)
+log, log_mutex = cmdline.setup_logging(__name__, options.log_file,
+                                       options.verbose)

-class WrappedLogger:
-
-    def __init__(self, my_logger, my_mutex):
-        self.logger = my_logger
-        self.mutex = my_mutex
-
-    def log(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.log(*args, **kwargs)
-
-    def debug(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.debug(*args, **kwargs)
-
-    def info(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.info(*args, **kwargs)
-
-    def warning(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.warning(*args, **kwargs)
-
-    def error(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.error(*args, **kwargs)
-
-    def critical(self, *args, **kwargs):
-        with self.mutex:
-            self.logger.critical(*args, **kwargs)
-
-log = WrappedLogger(_logger, _logger_mutex)
-
-
-def re_symlink(input_file, soft_link_name, log=log):
+def re_symlink(input_file, soft_link_name, log, mutex):
     """
     Helper function: relinks soft symbolic link if necessary
     """
     if input_file == soft_link_name:
-        log.debug("Warning: No symbolic link made. You are using " +
-                     "the original data directory as the working directory.")
+        with mutex:
+            log.debug("Warning: No symbolic link made. You are using " +
+                      "the original data directory as the working directory.")
         return

@@ -161,12 +130,14 @@ def re_symlink(input_file, soft_link_name, log=log):
         try:
             os.unlink(soft_link_name)
         except:
-            log.debug("Can't unlink %s" % (soft_link_name))
+            with mutex:
+                log.debug("Can't unlink %s" % (soft_link_name))

     if not os.path.exists(input_file):
         raise Exception("trying to create a broken symlink to %s" % input_file)

-    log.debug("os.symlink(%s, %s)" % (input_file, soft_link_name))
+    with mutex:
+        log.debug("os.symlink(%s, %s)" % (input_file, soft_link_name))

     os.symlink(
2015-07-23 02:22:12 -07:00
Jim Barlow 33731a6864 Move pageinfo code out of the pipeline 2015-07-23 02:17:13 -07:00
Jim Barlow 0c36cd2e24 Fix errors related to use working directory
Mainly workaround lack of @split(...output_dir) in ruffus
2015-07-23 01:16:05 -07:00
Jim Barlow 5cef1be26d New pipeline runs, splits pages 2015-07-22 22:58:13 -07:00
Jim Barlow e89f482c3d Fixes from early testing of new pipeline 2015-07-22 22:51:38 -07:00
Jim Barlow fe3e40305d Learn to split PDF into pages 2015-07-22 22:46:00 -07:00
Jim Barlow a92b5ceb6b Begin unifying main script and page script 2015-07-22 22:30:00 -07:00
Jim Barlow 0e7e7d8437 Suppress the xref warning for now 2015-07-22 11:24:14 -07:00
Jim Barlow f47fa98f33 Fixes to colorspace and other inquiries 2015-07-22 11:24:06 -07:00
Jim Barlow d3d5879911 Replace pdfimages -list call to poppler with PyPDF test for image
The immediate reason for doing this is that (newer?) versions of parse()
seem to choke on the parse string. It appears to trigger exponential
behavior in the underlying regex. In any case, replacing subprocesses
with native Python is usually better.
2015-07-22 11:22:12 -07:00
Jim Barlow b2168e11db Require Py3 for tests 2015-07-22 11:21:33 -07:00
Jim Barlow 6d5d8be708 New test: check skew 2015-07-22 04:00:59 -07:00
Jim Barlow ce2dbdf372 Add another test 2015-07-22 03:16:19 -07:00
Jim Barlow ec8a35a7a6 Basic test cases 2015-07-22 02:59:25 -07:00
Jim Barlow f6577c22c3 Complete wrapping of logger/logger_mutex 2015-07-22 02:57:13 -07:00
Jim Barlow 43d6c03093 Implement oversampling in ocrpage.py 2015-03-27 18:32:55 -07:00
Jim Barlow 1870f116bb More consistent spacing 2015-03-24 23:05:42 -07:00
Jim Barlow 8b87def013 Don't presume two jobs 2015-03-24 23:04:49 -07:00
Jim Barlow de599d97b5 Tidy up readme 2015-03-24 23:04:33 -07:00
Jim Barlow 5d7e6b45c4 Cleanup logger 2015-03-24 22:46:33 -07:00
Jim Barlow c6091bcfe1 Change python2 -> python3 for readlink() 2015-03-24 22:36:13 -07:00
Jim Barlow 466a8a1318 It's now py3 that uses lxml, reportlab 2015-03-19 17:12:32 -07:00
Jim Barlow a99ba3b696 Add rudimentary support for combining OCR layer with existing content
It appears to be very fragile due to weaknesses in PyPDF. Better
option is probably to use pdftk's watermark feature.
2015-03-10 14:28:38 -07:00
Jim Barlow 9229f7c6cc Add option to render text as invisible OCR text
Prior to this change, hocrtransform would render printable text (black
on white) and then a fully opaque image on top of the text. According to
the PDF spec, text that is the output of OCR should be marked invisible,
so that PDF viewers /know/ it's OCR output in a document that might mix
OCR and text overlays. Another benefit is that PDF viewers would know
to skip rendering text if they are not smart enough to figure out the
image will completely overwrite it.

However, for debug, visible text is nice, so retain it as an option.
2015-02-22 12:43:27 -08:00
Jim Barlow bf114bb188 Clean up pixel transform logic with namedtuple 2015-02-21 14:14:34 -08:00
Jim Barlow b8eed2f861 More PEP8/lint 2015-02-21 13:00:46 -08:00
Jim Barlow ccb1e347be Call HocrTransform directly instead of through a subprocess 2015-02-20 17:20:48 -08:00
Jim Barlow 8698974f11 Rename hocrTransform -> hocrtransform 2015-02-20 16:47:36 -08:00
Jim Barlow f2c79c4341 Convert hocrtransform to py3 2015-02-20 16:38:24 -08:00
Jim Barlow 4966d1346b Module marker for src folder 2015-02-20 15:43:05 -08:00
Jim Barlow 4a9337f757 PEP8 2015-02-20 15:42:06 -08:00
Jim Barlow db311fb6a2 Add support for -b (skip big pages) 2015-02-20 15:26:33 -08:00
Jim Barlow 02c1dcec8e Remove filenames from .hocr files
As documented, Tesseract does not escape the filename when inserting it
into .hocr, potentially creating an invalid XML file as a result. Since
there is no use for the title, regex it and nuke it.
2015-02-13 13:41:14 -08:00
Jim Barlow 52dc74d3ce Support Tesseract 3.03 quirk: .html vs .hocr extension 2015-02-11 10:24:10 -08:00
Jim Barlow cc2af2bc15 Convert the final image to a JPEG if the original image was a JPEG
Of course, this introduces recompression artifacts, and is unnecessary
if no options are given that modify the final image (no -d, -c, -i).
But rather than worry about that, it would be better to ultimately find
a way to combine the original PDF page with the output PDF text in the
case where we want no changes to the original. This is good enough for
now.

The better option can apparently be achieved using pdftk background, or
probably better, PyPDF2's merge. If Tesseract PDF generation is used
then we need a way to remove the image. Tesseract PDF generation at 3.03
does layout better (I think) and also properly encodes the hidden layer,
which is less likely to give display issues (I think).
2015-02-11 10:23:45 -08:00
Jim Barlow 638c6db05d Use the appropriate PNG rendered given the types of image present 2015-02-11 03:32:00 -08:00
Jim Barlow f7db8d9aff Use Ghostscript -> PNG instead of pdftoppm for rendering
Ghostscript has the clunkiest imaginable syntax, obtuse documentation,
quirky behavior, and poor diagnostics... but it *actually works* unlike
pdftoppm/poppler which gets things wrong.

In this case I observed poppler incorrectly decompresses certain CCITT
encoded monochrome PDFs. So set up Ghostscript to do the job instead.

For the moment this performs monochrome -> RGB conversion via reportlab.
2015-02-11 03:13:07 -08:00
Jim Barlow 564fb7a87e Support Ghostscript 9.14's new color conversion engine (not portable)
The flag -dUseCIEColor is now deprecated, as it invokes the old engine
which introduces color errors. The new engine requires a PDF/A file
header with hardcoded location of a ICC profile to use, now included in
the project. Portable iterations should generate a PDFA_def.ps based on
the target system; for now OS X with homebrew is presumed.

I have selected sRGB since scanners tend to capture RGB and printing
is not a major consideration for PDF/A.

Also note all file paths given to gs must be absolute. May its creators
be forever haunted for their failure to document this unexpected quirk.
2015-02-09 15:33:49 -08:00
Jim Barlow 4d88e64774 Standardize tmpfile prefix 2015-02-09 15:02:49 -08:00
Jim Barlow 26f1163b46 Handle case where a page contains no images - don't OCR
It doesn't make much sense to do anything with an all vector page
except extract the page unmodified.
2015-02-08 20:05:54 -08:00
Jim Barlow 40058e99e0 Implement debug text only page option 2015-02-08 19:51:41 -08:00
Jim Barlow bece4c3e02 Describe what decision was made based on -f and -s and presence of text 2015-02-08 19:51:18 -08:00
Jim Barlow f0f6b57c87 When deciding on OCR, check for presence of text rather than a font
It appears to be possible to have a PDF with an embedded font that is
either unused or used only for whitespace. So check for some amount of
actual text instead.
2015-02-08 17:38:27 -08:00
Jim Barlow dc2a4ab044 Logic error 2015-02-08 17:33:35 -08:00
Jim Barlow b16d6f5b81 Implement skipping OCR when -s is specified
Appears to be necessary to disable each state of the pipeline that is
inactive, not just initial and terminal stages of an inactive segment.
If nothing else this makes what is going on more explicit.
2015-02-08 17:26:16 -08:00
Jim Barlow 69ce6ff7b5 Not a named param 2014-11-22 15:35:05 -08:00
Jim Barlow 32ba50b8dc Add Tesseract timeout to keep things reasonable 2014-11-14 02:06:23 -08:00
Jim Barlow 36aca45f35 The -dci options now work (and valid combinations thereof) 2014-11-14 00:23:22 -08:00
Jim Barlow 925290342d Leptonica deskew can handle .pnm input, unlike imagemagick 2014-11-13 23:20:25 -08:00
Jim Barlow 4dc0370c57 Add leptonica deskew 2014-11-13 16:53:26 -08:00
Jim Barlow b92f8e43f2 Run as a module instead 2014-11-13 16:52:53 -08:00
Jim Barlow 22b0733a1d Merge branch 'feature/findskew' into develop 2014-11-13 16:00:27 -08:00
Jim Barlow 6021684ab6 Attempt to fix multiprocessing pickling error 2014-11-13 15:58:57 -08:00
Jim Barlow f4b1d0cdfe Fix symlink error that occurs in multipage processing 2014-11-13 15:58:36 -08:00
Jim Barlow d0d8048621 Comments 2014-10-17 17:28:31 -07:00
Jim Barlow cfd119325d Use abspath instead of relpath for temporary directory symlink 2014-10-11 17:48:56 -07:00
Jim Barlow ad30833ffc Support missing tess_cfg_files parameter when omitted by OCRmyPDF.sh 2014-10-11 17:48:33 -07:00
Jim Barlow e5c79a6666 Use TIFFs as intermediates
pdftoppm in recent versions (0.26.4,5) seems to be incapable of
producing valid TIFFs, so have it dump a .pnm file and let ImageMagick
figure out how to convert it to TIFF. This is not ideal, but at least
it works.
2014-10-10 01:54:16 -07:00
Jim Barlow 63dc753c1b Standardize intermediate filenames better
convert .pnm -deskew <...> .pnm seems to have a bug that produces an
invalid .pnm file which later causes tesseract (specifically,
leptonica) to choke (using 3.02/1.71 as versions, respectively). Will
change pipeline to use tiffs internally since they are less stupid.
2014-10-10 01:30:43 -07:00
Jim Barlow 017bc1f252 Basic error handling 2014-10-10 01:07:46 -07:00
Jim Barlow bcd67c009d Sort of working, but fragile; uses tmp folder properly now 2014-10-10 00:35:49 -07:00
fritz-hh 635358884e start rewrite ocrmypdf in python 2014-10-09 22:53:08 +02:00
Jim Barlow 2f6cfafdfc Now produces a finished OCR-PDF page 2014-10-08 03:54:06 -07:00
Jim Barlow 25234fa30b First crack at Ruffus, working well 2014-10-08 03:21:28 -07:00
fritz-hh 5b17341804 Merge remote-tracking branch 'origin/v2.x' into v3.x 2014-10-07 22:06:05 +02:00
fritz-hh 9bedfa9a72 fixes #95
Exit if the output path points to a folder
Exit if the output path point to an existing file
2014-10-07 16:42:10 +02:00
fritz-hh e1f1220970 make clear it is a draft from v3.x branch 2014-10-03 16:23:02 +02:00
fritz-hh 5855bcd1fe Merge remote-tracking branch 'origin/v2.x' into v3.x 2014-10-03 16:21:49 +02:00
fritz-hh a14af5b9ee make clear it is a draft from v2.x branch 2014-10-03 16:18:20 +02:00
fritz-hh f11c03750e typo 2014-10-03 16:16:17 +02:00
fritz-hh ea5cfa40c1 Update ROADMAP.md 2014-10-03 16:13:56 +02:00
fritz-hh c562754d81 typo 2014-10-03 16:11:26 +02:00
fritz-hh 90d892512a roadmap usage updated 2014-10-03 16:09:59 +02:00
fritz-hh 9c6fedb15b usage corrected [-f|-s] 2014-10-03 16:07:06 +02:00
fritz-hh 3a7175115f roadmap arguments specified 2014-10-03 16:03:02 +02:00
fritz-hh 98c41f3223 typo in usage 2014-10-03 15:44:14 +02:00
fritz-hh d101e96e16 roadmap: better layout 2014-10-03 15:30:29 +02:00
fritz-hh a446b6c440 roadmap rename steps 2014-10-03 15:19:20 +02:00
fritz-hh b1fec0f1b1 roadmap detailed 2014-10-03 15:17:53 +02:00
fritz-hh 1dfdc93745 draft roadmap for v3.x 2014-10-03 15:02:17 +02:00
fritz-hh 6c5ee4095c default language now set in the config.sh file 2014-09-30 23:28:22 +02:00
fritz-hh 986fbf63a4 Introduce -s option + fix bug when -C no set
- Introduce -s option to no ocr pages containing fonts
- Solve issue with -f and -s if -C is not set
2014-09-30 23:16:31 +02:00
fritz-hh 2612105d32 correct download path 2014-09-29 22:29:25 +02:00
fritz-hh 954fe13f54 update release notes for v2.2-stable 2014-09-29 22:25:02 +02:00
fritz-hh bb5a00685e Make clear this is a draft 2014-09-28 21:10:04 +02:00
Jim Barlow dabbddb04e deskew and clean 2014-09-27 15:03:07 -07:00
fritz-hh 5f173e5acb return right return code
Python does not map the expression to its return code automatically, so
this line returns success regardless of the reportlab version installed.
(I also realized that hasattr is superfluous).
2014-09-27 00:53:10 +02:00
fritz-hh b28ff40aea remove reportlab patch. fixes #91
remove patch that was required for versions of reportlab <3.0 (fixed in
3.0 now)
patch was necessary in order to reduce size of graysclage / b&w images
in pdf
2014-09-26 23:58:19 +02:00
Jim Barlow fccfb4589e Moving quickly - we can now output .ppm files at correct resolution 2014-09-26 04:43:15 -07:00
Jim Barlow 5384c98013 Initial ocrpage.py rewrite into python3 2014-09-26 04:19:41 -07:00
fritz-hh 2ed2307573 Merge pull request #89 from jbarlow83/feature/readlink-osx
More portable solution (works also on OS X) to get OCRmyPDF.sh path (following simlinks)
2014-09-25 23:09:26 +02:00
Jim Barlow 3f8a2d8d3e Eliminate readlink entirely and do the same thing on all platforms 2014-09-25 13:47:35 -07:00
fritz-hh 1a13b7c85f Check if the input file exist
Previously I checked only if the folder in which the input file should
be exists
2014-09-25 22:03:45 +02:00
Jim Barlow d7130a1e56 Merge branch 'feature/keep-text-pages' into develop 2014-09-25 03:50:21 -07:00
Jim Barlow f69054cb17 Fix parameter order problems
Put TESS_CFG_FILES last because it is optional and can be blank. If
omitted it breaks the sequence of subsequent parameters. Also cleanup
text output in this new mode.
2014-09-25 03:50:01 -07:00
Jim Barlow 80dc6eca2c Merge branches 'feature/readlink-osx' and 'feature/keep-text-pages' into develop
Conflicts:
	OCRmyPDF.sh
2014-09-25 03:14:10 -07:00
Jim Barlow d250fbb3d6 Fix call to readlink on OS X
readlink -f is a GNU coreutils extension, so not available on OS X and
other platforms.
2014-09-25 03:11:27 -07:00
Jim Barlow 09bbe92611 Add command line option to skip pages that contain font data
If a page contains font data, the script would abort, unless -f was given,
in which case it would use pdftoppm to rasterize the font into a bitmap
and then attempt to OCR it. -f is almost certainly not what users want
unless they want to debug OCR or something.

If a PDF already has fonts it either was OCR'd already, or it is
a composite file containing, for example, some scanned documents appended
to a text report.  In the latter case, this -s option provides OCR on
pages that don't have it without changing those that do, and if a PDF
was completely OCRed it will be converted to PDF/A.  In batch jobs with
a mix of OCR and non-OCR the implicit conversion to PDF/A is also useful.
2014-09-25 02:43:40 -07:00
Jim Barlow 69d922e096 Check for missing pdftoppm when poppler installed with --disable-splash-output
When I upgraded to poppler 0.24.5, pdftoppm was not compiled because the
script had --disable-splash-output set for some reason.

For OS X Homebrew the solution is:
brew uninstall poppler
brew install poppler --with-splash-output
2014-09-25 02:30:29 -07:00
fritz-hh d510e7e4ae prevent new spurious jhove message to be displayed 2014-09-24 23:43:37 +02:00
fritz-hh 5893290dd9 update to jhove v1.11 2014-09-24 23:17:39 +02:00
fritz-hh 5c3bbc4031 typo in OCRmyPDF.sh 2014-09-22 21:22:38 +02:00
fritz-hh 27cd8cf0db add link to heise open source 2014-09-20 20:47:02 +02:00
fritz-hh b403016d5b Release notes updated for v2.1-stable 2014-09-20 19:50:32 +02:00
fritz-hh 5a81823969 Merge pull request #82 from orbitcowboy/v2.x
Fixed typo
2014-09-20 19:02:33 +02:00
fritz-hh 17801401cd Merge pull request #83 from DorianScholz/v2.x
- small changes to make this work on Ubuntu 12.04 called via symlink
- lowered minimum parallel version
2014-09-20 18:59:57 +02:00
Dorian Scholz 5c7b2a2a36 lowered minimum version for parallel to 20121122 2014-09-10 13:27:59 +02:00
Dorian Scholz 1db06de287 added BASEPATH to allow for execution via symlink 2014-09-10 13:26:14 +02:00
Martin Ettl 3904178d44 Fixed typo 2014-09-09 07:01:04 +02:00
fritz-hh 8bb9c3610c Merge pull request #81 from MoritzFago/v2.x
fixed tipo ghostcript to ghostscript
2014-09-08 18:31:00 +02:00
MoritzFago 7dcc382ccc fixed tipo ghostcript to ghostscript 2014-09-08 16:52:49 +02:00
fritz-hh b71fc807d2 Merge pull request #77 from andysigner/v2.x
Fixed typo in help text
2014-05-23 19:51:20 +02:00
Andy Signer 15d28d970a Fixed typo in help text 2014-05-23 12:41:31 +02:00
fritz-hh e083a860e9 Merge pull request #73 from andreas-christ/v2.x
Fixed typo in import of reportlab.
2014-04-27 23:20:48 +02:00
Andreas Christ 6463b9dd84 Fixed typo in import of reportlab. 2014-04-27 19:15:46 +02:00
fritz-hh c873de6ca4 Consider that the hocr file has not always the same name
Closes #72
2014-04-27 16:01:11 +02:00
fritz-hh b70863b47e support both older and newer versions of reportlab
closes #71
2014-04-27 15:53:20 +02:00
fritz-hh 3546f84c6d ignore *.pyc files 2014-04-27 15:46:55 +02:00
Jim Barlow 1d98917db9 Add command line option to skip pages that contain font data
If a page contains font data, the script would abort, unless -f was given,
in which case it would use pdftoppm to rasterize the font into a bitmap
and then attempt to OCR it. -f is almost certainly not what users want
unless they want to debug OCR or something.

If a PDF already has fonts it either was OCR'd already, or it is
a composite file containing, for example, some scanned documents appended
to a text report.  In the latter case, this -s option provides OCR on
pages that don't have it without changing those that do, and if a PDF
was completely OCRed it will be converted to PDF/A.  In batch jobs with
a mix of OCR and non-OCR the implicit conversion to PDF/A is also useful.
2014-02-06 23:11:54 -08:00
fritz-hh 1c34fd69cf RELEASE_NOTES update prior delivery of v2.0-stable 2014-01-25 22:14:05 +01:00
fritz-hh 4cf38404cc fixes #51
Allow tesseract 3.02.01 to be used.
Even 3.02.01 fails in few cases (see issue #28). I decided to allow this
version anyway because 3.02.02 is not yet available for some widespread
linux distributions
2014-01-25 21:58:50 +01:00
Jim Barlow 112fb5098b Expose pixFindSkew API 2014-01-21 21:36:41 -08:00
Jim Barlow 5ace6906c7 Bug fix: leptonica generates .png when asked to produce .pbm/pgm/ppm
Leptonica does not interpret those extensions correctly.  However, when
asked to produce a .pnm file, it will produce the expected .pbm/pgm/ppm
file depending on the input.  So ask it to produce a .pnm and then
adjust the extension.

And add a test case.
2014-01-21 21:35:58 -08:00
Jim Barlow 8cfbdaf0d0 Fix a silly typo, and other minor cleanup 2014-01-19 19:06:19 -08:00
Jim Barlow 6703434976 Replace ImageMagick-convert with Leptonica 2014-01-19 14:47:51 -08:00
Jim Barlow 62edc15cd7 Implement ctypes wrapper around Leptonica to access its deskew function
A few design notes:
Leptonica's deskew is far superior to ImageMagick's convert -deskew command --
around 30-40x faster.  Subjectively the output appears to this contributor to
be of higher quality as well.  The difference is the algorithm: ImageMagick
uses the complex Hough transform to find the skew angle, while Leptonica uses
the simpler method, Postl's variance of differential line sums -- conceptually, shear the image and check for straight horizontal.  In this case
simplicity wins.  Finding the skew angle is the bulk of the work.

Leptonica's author explains the advantages of his approach here:
http://www.leptonica.com/skew-measurement.html

Leptonica is the low-level library that Tesseract depends on.  Hence, this
project already depends on Leptonica.  Leptonica can read and write most
common image file types on its own.

Unfortunately its error handling is poor: it seldom returns any meaningful
error codes.  The best it manages is writing messages to stderr, which in
the context of a verbose script is just confusing since the error's source
is not indicated.  The problem is compounded by Tesseract's use of Leptonica,
which will produce exactly the same errors in some cases.  So we trap stderr
between calls to Leptonica and parse it for a few different types of error
message.

leptonica.py is Python 2/3 compatible and set up to provide access to other
Leptonica functions as needed.  Of particular interest are its orientation
detection (including flip and rotation errors) which it does by comparing
text ascenders to descenders.

There is a PyPI "pylepthonica" package, however it is out of date by a few
years, and it implements all of Leptonica with Python wrappers -- so it is
massive, with one .py file at 2.5 MB.  This module is loosely inspired by
pyleptonica but more modern, up to date, and contains only limited
functionality.
2014-01-19 14:28:52 -08:00
fritz-hh be830ddc31 List supported languages
In case lan is not supported, list the supported languages in the error
message
2014-01-18 22:22:19 +01:00
fritz-hh 18322b424f fixes #60
Check if the languages option provided to tesseract (-l) are supported
2014-01-18 21:38:22 +01:00
fritz-hh 6901c60db4 more robust way to check tesseract version
better way of checking if the tesseract version is compatible with the
script.
If the required tess version is 3.02.02, and the actual version is 3.03,
the script would have told before that the version is too old, because
303<30202, now it compares 3.03>3.0202
2014-01-18 21:02:15 +01:00
fritz-hh e369ce6766 config file: version updated to v2.0-rc2 2014-01-16 21:22:24 +01:00
fritz-hh 64e4e5d91e release notes updated for v2.0-rc2 2014-01-16 21:19:15 +01:00
fritz-hh efce7de9ae wording corrected 2014-01-15 23:08:26 +01:00
fritz-hh 38c64ac689 dependency to pdftk removed
concatenation is now done also with ghostscript
2014-01-15 21:23:42 +01:00
fritz-hh 6d203e3eee portability improvements + minor changes 2014-01-15 21:23:41 +01:00
fritz-hh 81f461e557 disclaimer added 2014-01-14 23:46:33 +01:00
fritz-hh 988bde1387 tmpfiles to $TMPDIR + better portability (mktemp)
mktemp: consider both FreeBSD/OSX and Linux OS having incompatible
syntax
From now on temporary files are saved in the folder specified by the
environment variable $TMPDIR
2014-01-14 22:57:10 +01:00
fritz-hh aedbabdbe8 merged pull request from oxplot 2014-01-14 22:29:41 +01:00
fritz-hh 6ed53e53c7 Readme improved 2014-01-14 19:47:28 +01:00
Mansour Behabadi a78630ce99 Make src scripts executable
Signed-off-by: Mansour Behabadi <mansour@oxplot.com>
2014-01-14 17:50:46 +11:00
Mansour Behabadi 6653066784 Use --gnu in parralell and XX for mktemp
Signed-off-by: Mansour Behabadi <mansour@oxplot.com>
2014-01-14 17:49:24 +11:00
fritz-hh e40f1fa081 better handling of ligatures: fixes #58 2014-01-13 23:13:15 +01:00
fritz-hh a872ce751d config file restructured
to be make which parameters are allowed to be changed by the user
2014-01-13 22:11:28 +01:00
fritz-hh 317846fbdc Check of tmp folder creation was successful 2014-01-13 22:05:26 +01:00
fritz-hh f581a55544 Merge pull request #57 from jbarlow83/for-upstream/tmpfolder
Fix temporary folder name generation collisions
2014-01-13 12:31:02 -08:00
fritz-hh 447b291e70 minor changes 2014-01-13 18:03:44 +01:00
fritz-hh 01d07253e8 indicate python2 to be used in header 2014-01-13 18:03:43 +01:00
fritz-hh 034a466094 Merge pull request #56 from jbarlow83/for-upstream/hocr-selfwidth
Fix AttributeError on self.width if Tesseract finds no OCR text
2014-01-13 08:44:16 -08:00
fritz-hh c6211e2335 Merge pull request #55 from jbarlow83/for-upstream/check-poppler
Verify that pdftoppm is the Poppler version, not xpdf version
2014-01-13 08:42:33 -08:00
Jim Barlow 1d03a6417d Verify that pdftoppm is the Poppler version, not xpdf version 2014-01-12 22:12:09 -08:00
Jim Barlow 1d62ef27a2 Fix AttributeError on self.width if Tesseract finds no OCR text
self.width remains undefined unless hOCR finds text.  It might not, if
a page contains only an image for example.

Full error message is:
AttributeError: ‘hocrTransform’ object has no attribute ‘width’
2014-01-12 22:10:15 -08:00
Jim Barlow 996048dc08 Fix temporary folder name generation collisions
First, the regular expression matches everything after the first period
in a filename.  Adding the $ make it match the last, so that filenames
such as “Report.1.pdf” get trimmed to “Report.1”.

Next use mktemp to get the OS to create a temporary folder.  It will
guarantee a unique directory name beginning with prefix, even if parallel
processes are at work.
2014-01-12 22:05:11 -08:00
fritz-hh bf02ee3bdc Resolved conflits with jbarlow83 pull request 2014-01-12 15:37:14 +01:00
fritz-hh a3c7fba02d minor changes (comments) 2014-01-11 22:26:29 +01:00
fritz-hh a8cd7febf6 remove spurious space in img number
Tell the script that "nbImg" is a number, so that leading/trailing
spaces are removed
2014-01-11 22:15:53 +01:00
fritz-hh 20c008b84f avoid spurious error msg if no image in pdf 2014-01-11 22:05:19 +01:00
fritz-hh 7cd73566be check if python libs are installed
Check if reportlab and lxml are installed, otherwise exist with an error
2014-01-11 17:08:26 +01:00
fritz-hh e56fd53d06 poppler syntax (rather than xpdf syntax) 2014-01-11 16:19:52 +01:00
fritz-hh 810b1b3b3e Merge pull request #48 from jbarlow83/for-upstream/osx-errors
Fix pdffonts error when filename contains a space
2014-01-11 07:10:12 -08:00
fritz-hh cb0b033fe7 Merge branch 'v2.x' of https://github.com/fritz-hh/OCRmyPDF into v2.x 2014-01-11 15:52:01 +01:00
fritz-hh 46f673a3b7 exit if bad parallel/tesseract version installed 2014-01-10 22:59:33 +01:00
fritz-hh 455303b3d4 parallel version added in RELEASE_NOTES 2014-01-10 22:12:58 +01:00
Jim Barlow 24a84d6380 Fix pdffonts error when filename contains a space 2014-01-09 16:44:24 -08:00
Jim Barlow 9aa2171052 Monkeypatch reportlab to output grayscale and monochrome colorspaces 2014-01-09 16:36:26 -08:00
Jim Barlow 3a46ea1f36 Merge branch 'for-upstream/pdftoppm-error' into for-upstream/mono 2014-01-09 16:20:05 -08:00
Jim Barlow d33779f301 Detect monochrome images and extract them as PBM (1 bpp) 2014-01-09 16:15:24 -08:00
Jim Barlow d6ea0793b8 Fix ocrPage.sh pdftoppm error on OS X 10.9 2014-01-09 16:04:37 -08:00
fritz-hh 4e5e5bb925 version changed to v2.x 2014-01-08 20:57:55 +01:00
fritz-hh 3232ed8e38 link to releases updated 2014-01-08 20:56:34 +01:00
fritz-hh 29d6748af8 release_notes and readme updated for v2.0-rc1 2014-01-07 23:13:42 +01:00
fritz-hh 828f195071 erroneous exit code corrected 2014-01-07 21:57:18 +01:00
fritz-hh b0b7e32783 fixes #40 and code cleanup 2014-01-07 21:51:15 +01:00
fritz-hh c1103c0248 check tesseract version
fixes #41
versions older than 3.02.02 are known to produce invalid hocr output (in
some cases)
2014-01-07 21:04:28 +01:00
fritz-hh 940a016e95 link to issue tracking system added 2014-01-06 23:12:15 +01:00
fritz-hh c6cc098e47 create symbolic links and not copy
If deskew and/or cleanup is not requested, do not copy the files, but
just create symbolic link.
This saves disk place and makes the script slightly quicker
2014-01-06 23:08:35 +01:00
fritz-hh 54f47ab89b Minor change 2014-01-06 22:41:43 +01:00
fritz-hh fc3de64dce Changed debug page name
In order to have the debug page after the normal panel in the final PDF
file
2014-01-06 22:41:29 +01:00
fritz-hh 414c4e3f3c round dpi value correctly 2014-01-06 22:32:11 +01:00
fritz-hh 6a9f38d31e removed unused variables 2014-01-06 22:23:41 +01:00
fritz-hh aa4256d35c fixes #44
The x/y resolutions are not computed separately anymore.
We do not check anymore if x and y resolutions are different (not
measure could anyway be taken if they were not equal...)
2014-01-06 22:23:00 +01:00
fritz-hh 8a1241ba44 minor changes (indentation and fct name) 2014-01-06 22:05:49 +01:00
fritz-hh 7eab052e0f Improved consistency of tmp file names 2014-01-06 22:00:58 +01:00
fritz-hh 552d19e36b v1.1-stable added in release notes 2014-01-06 20:09:46 +01:00
fritz-hh 463b04e795 typo 2014-01-06 20:09:12 +01:00
fritz-hh c0d8508264 minor change in log msg 2014-01-06 19:30:19 +01:00
fritz-hh 6ef4ba31e2 help and documentation improved 2014-01-05 22:02:12 +01:00
fritz-hh 10a3d26291 default PDI definition moved to cfg file 2014-01-05 22:01:45 +01:00
fritz-hh ab994b32ee explanations added for no_ligature cfg file 2014-01-05 22:01:10 +01:00
fritz-hh 9352b71d78 Copyright added 2014-01-05 22:00:12 +01:00
fritz-hh 71593421ed minor change 2014-01-05 21:22:31 +01:00
fritz-hh 2754970f37 Echo arguments of script in debug mode 2014-01-04 21:43:41 +01:00
fritz-hh 5945454597 Support for -f option
Fixes #16
2014-01-04 21:24:33 +01:00
fritz-hh 884dbce712 copyright years updated 2014-01-04 21:20:29 +01:00
fritz-hh 8ee1bc6598 Minor change 2014-01-04 21:19:51 +01:00
fritz-hh 7d76c46731 Check if page already contains a font 2014-01-04 18:05:21 +01:00
fritz-hh f8ccf42c06 path to tmp folder now defined in config.sh 2014-01-04 17:24:35 +01:00
fritz-hh 0abe0f1f10 minor change 2014-01-03 17:00:35 +01:00
fritz-hh ee8a5d80ff echo also java version in debug mode 2014-01-03 16:27:11 +01:00
fritz-hh f08893b5c8 Support section added 2014-01-03 16:13:17 +01:00
fritz-hh 41cd88506e Echo version of the used tools
Fixes #35
2014-01-03 15:59:51 +01:00
fritz-hh 081223b138 Delete 2013_09_LED_und_Energiesparlampen.pdf
file committed by mistake... So deleting it now
2013-12-31 23:38:38 +01:00
fritz-hh 4e60c9ba09 Warn user in case of low resolution 2013-12-30 23:55:26 +01:00
fritz-hh 95fe7cd3bc Oversampling + more than 1 img
- Oversampling resolution can now be set from the cmd line (-o option)
- If a page contains more than one image, warn the user but process the
page anyway with a default resolution
2013-12-30 23:44:38 +01:00
fritz-hh 79ec1d994e Automatic oversampling
- If resolution is too low (<250dpi) perform automatic oversampling of
the image
- comments improved
- log messages improved
2013-12-30 22:27:10 +01:00
fritz-hh 045362425f minor change 2013-12-30 19:16:29 +01:00
fritz-hh 2b2637fbc3 minor change 2013-12-30 18:21:03 +01:00
fritz-hh bfc4f7a28d better resolution handling (fixes #38)
- dpi computation moved to in dedicated function
- do not exit in case of resolution mismatch (fixes #38)
- comments improved
2013-11-29 10:37:09 +01:00
fritz-hh 407670e1f3 Minor change 2013-11-29 10:34:05 +01:00
fritz-hh d0671d81b5 New log level added (LOG_WARN) 2013-11-29 10:33:46 +01:00
fritz-hh 7a74ebbcc3 comments and log messages improved 2013-11-29 00:39:44 +01:00
fritz-hh 9e69800332 typo 2013-11-28 00:37:57 +01:00
fritz-hh 7542188592 Removed bashism
== does not exist in bourne shell
2013-11-27 23:44:45 +01:00
fritz-hh b4a23c005d fixes #34
tell GNU parallel to protect against evaluation by the sub shell (-q
flag).
This is required in case the file name passed as argument contains
special characters like "#"
2013-11-27 23:15:54 +01:00
fritz-hh 5e0f8be4b1 Various improvements
-Constants moved to config.sh
- Use "python2" cmd instead of "python"
- few other minor changes
2013-11-27 22:34:21 +01:00
fritz-hh 50dee55606 File Test_Issue_#28 renamed 2013-11-27 22:30:43 +01:00
fritz-hh da5cd01fe4 copyright line added 2013-05-06 23:13:29 +03:00
fritz-hh d3fb317d41 readme updated
new feature: Process several pages in parallel if more than one CPU core
is available
2013-05-06 21:54:41 +02:00
fritz-hh 88ddeb1fb6 OCRmyPDF.sh: added dependency to GNU parallel 2013-05-06 21:54:05 +02:00
fritz-hh f9e2e74bf3 Merge remote-tracking branch 'origin/v1.x' into v2.x 2013-05-06 21:35:34 +02:00
fritz-hh 87e01aff60 readme updated for v1.0-stable 2013-05-06 21:29:15 +02:00
fritz-hh 7e8481186a OCRmyPDF.sh: metadata not added anymore
Removed feature to add metadata in final pdf file (because it lead to to
final PDF file that does not comply to the PDF/A-1 format)
2013-05-06 21:26:33 +02:00
fritz-hh 2b0103a4e6 basic implementation of parallel page processing
- basic implementation of parallel page processing using GNU parallel
- processing around 40% faster on dual core processor
2013-05-05 22:33:54 +02:00
fritz-hh 064d4be83c Merge remote-tracking branch 'origin/v1.x' into v2.x
Conflicts:
	OCRmyPDF.sh

Fixes #31
2013-05-05 21:01:17 +02:00
fritz-hh ab536d5678 OCRmyPDF.sh: fixes issue for files having spaces
fixes #31
2013-05-05 20:56:45 +02:00
fritz-hh 9db805c4ad new file to OCR one page
Required to perform OCR of several pages in parallal (using GNU
parallel)
2013-05-05 20:45:27 +02:00
fritz-hh f7923a9761 OCRmyPDF.sh: few variables renamed for clarity 2013-05-05 20:44:03 +02:00
fritz-hh fd52650255 .gitattribute: handle *.jar and *.pdf as binary 2013-05-05 16:54:41 +02:00
fritz-hh f0fe295175 jhove config: fixes #29 2013-05-05 16:36:54 +02:00
fritz-hh 2f89aa3935 .gitignore corrected + jhove jar files added
.gitignore file corrected, because it prevented some required jhove
binary files from being checked in (jar files)
2013-05-04 22:01:03 +02:00
fritz-hh 5aa27343e0 delete test file 2013-05-04 21:58:01 +02:00
fritz-hh 5ce2841389 JHove: deleted doc + source
Deleted number of jhove files that are not required
(documentation and java source code mainly)
Goal: reduce size of the package
2013-05-04 21:55:39 +02:00
fritz-hh e4ffb58269 OCRmyPDF.sh: provision for parallel pages processing 2013-05-02 22:06:16 +02:00
fritz-hh 2ce3d9e19d added file to reproduce #28 2013-05-02 17:21:17 +02:00
fritz-hh 9271fe73a8 OCRmyPDF.sh: fixes #27
The fix should now be compatible to most implementation of grep
2013-05-02 16:51:46 +02:00
fritz-hh edaa70b97f OCRmyPDF.sh: fixes #25 and fixes #26
- In debug mode: compute and echo time required for processing
- Resolutions (x/y) that are nearly equal are not supported (because the
test did not take into account imprecision due to trauncation)
2013-05-01 15:58:55 +02:00
fritz-hh ab07f4deea OCRmyPDF.sh: handling of path with spaces
- corrected fct absolutePath() to handle path with spaces correctly
- pdf title metadata: split on case change file name
- change of owner/group/permission removed from code
- improved logging
2013-05-01 13:44:20 +02:00
fritz-hh beb1d7ab54 release notes: updated for v1.0-rc2 2013-04-29 12:27:43 +03:00
fritz-hh 5ce3e9bfec OCRmyPDF.sh: Version number updated 2013-04-29 12:19:19 +03:00
fritz-hh 2bed210a30 OCRmyPDF.sh: added metadata in final pdf file
- added metadata in final pdf file: fixes #4
- improved logging of PDF/A validation results
2013-04-28 22:18:34 +02:00
fritz-hh 2441551156 OCRmyPDF.sh: final pdf same owner & permissions
fixes #9
2013-04-28 15:54:31 +02:00
fritz-hh 15baca5e08 HocrTransform.py: exist if page size if not found
fixes #21
2013-04-28 14:56:14 +02:00
fritz-hh 062ef0ca3a OCRmyPDF.sh: keep tmp files in debug mode
fixes #22
2013-04-28 14:43:21 +02:00
fritz-hh 24b4686944 release notes: unpaper version added 2013-04-27 14:03:17 +03:00
fritz-hh d3d1c20ca2 Correct version number
fixes #19
2013-04-27 14:00:59 +03:00
fritz-hh 7f7b81154f Merge branch 'master' of https://github.com/fritz-hh/OCRmyPDF 2013-04-26 19:37:26 +02:00
fritz-hh 6372cec6b8 OCRmyPDF.sh: Fixed major problem with deskew
After deskew the images was cropped to the wrong size
2013-04-26 19:37:02 +02:00
fritz-hh 5ec875325e Update README.md 2013-04-26 18:46:18 +03:00
fritz-hh 4d80709cfd Update README.md 2013-04-26 18:43:15 +03:00
fritz-hh 2642c1b3d3 Update README.md 2013-04-26 18:00:58 +03:00
1192 changed files with 27364 additions and 312643 deletions
+16
View File
@@ -0,0 +1,16 @@
# Coverage isn't really compatible with subprocesses so results are unreliable
[run]
branch = True
#concurrency = multiprocessing
source = ocrmypdf/
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if 0:
if False:
if __name__ == .__main__.:
+50
View File
@@ -0,0 +1,50 @@
# OCRmyPDF
#
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y --no-install-recommends \
ocrmypdf \
unpaper \
tesseract-ocr-eng \
tesseract-ocr-fra \
tesseract-ocr-deu \
tesseract-ocr-spa \
tesseract-ocr-por \
tesseract-ocr-chi-sim \
python3-venv \
python3-pip
ENV LANG=C.UTF-8
RUN python3 -m venv --system-site-packages /appenv
# This installs the latest binary wheel instead of the code in the current
# folder. Installing from source will fail, apparently because cffi needs
# build-essentials (gcc) to do a source installation
# (i.e. "pip install ."). It's unclear to me why this is the case.
RUN . /appenv/bin/activate; \
pip install --upgrade pip \
&& pip install ocrmypdf[fitz]
# Now copy the application in, mainly to get the test suite.
# Do this now to make the best use of Docker cache.
COPY . /application
RUN . /appenv/bin/activate; \
pip install -r /application/test_requirements.txt
# Remove the junk, including the source version of application since it was
# already installed
RUN rm -rf /tmp/* /var/tmp/* /root/* /application/ocrmypdf \
&& apt-get autoremove -y \
&& apt-get autoclean -y
RUN useradd docker \
&& mkdir /home/docker \
&& chown docker:docker /home/docker
USER docker
WORKDIR /home/docker
# Must use array form of ENTRYPOINT
# Non-array form does not append other arguments, because that is "intuitive"
ENTRYPOINT ["/application/.docker/docker-wrapper.sh"]
+17
View File
@@ -0,0 +1,17 @@
# OCRmyPDF polyglot
#
FROM jbarlow83/ocrmypdf:latest
USER root
# Update system and install our dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr-all
RUN apt-get autoremove -y && apt-get clean -y
USER docker
# Must use array form of ENTRYPOINT
# Non-array form does not append other arguments, because that is "intuitive"
ENTRYPOINT ["/application/.docker/docker-wrapper.sh"]
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
. /appenv/bin/activate
cd /home/docker
exec ocrmypdf "$@"
+25
View File
@@ -0,0 +1,25 @@
*.ipynb
*.pdf
*.pyc
*.rst
*.sublime*
**/*.pyc
.*/
!.docker/
.ruffus_history.sqlite
bin/
build/
docs/
dist/
htmlcov/
include/
lib/
MANIFEST.in
ocrmypdf.egg-info/
staging/
tests/cache/
tests/output/
tests/resources/private/
tmp/
venv*/
wheelhouse/
+1
View File
@@ -0,0 +1 @@
ref-names: $Format:%D$
+9 -18
View File
@@ -1,22 +1,13 @@
# Always use Unix convention for new lines
* text eol=lf
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.jar binary
*.pdf binary
*.PDF binary
*.png binary
*.jpg binary
*.bin binary
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
.git_archival.txt export-subst
+12
View File
@@ -0,0 +1,12 @@
Please include the command line and a test file with your issue report.
If possible, please use a test file that we can include in future test cases (no personal information, no copyrighted material).
If you wish to encrypt a test file so that only the maintainer of OCRmyPDF can view it, you may use:
```bash
gpg --recv-keys 4434eb74c4a35f7f --keyserver pgp.mit.edu
gpg --output test.pdf.gpg --encrypt --receipient 4434eb74c4a35f7f test.pdf
```
+40 -162
View File
@@ -1,167 +1,45 @@
releasePackage.sh
tmp/
log/
# Development environment
*.pyc
*.sublime-*
venv*/
pyvenv.cfg
tasks.py
.bash_history
.ruffus_history.sqlite
.idea/
.pytest_cache/
#################
## Eclipse
#################
# Package building
*.egg-info/
.cache/
.eggs/
build/
dist/
wheelhouse/
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# Automatically generated files
ocrmypdf/lib/_*.py
ocrmypdf/version.py
docs/_build/
docs/_static/
docs/_templates/
docs/Makefile
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
*.dotCover
## TODO: If you have NuGet Package Restore enabled, uncomment this
#packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
# Code coverage
.coverage
.tox
htmlcov/
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store
# Testing
log/
/*.pdf
/*.qdf
*.ipynb
.ipynb_checkpoints/
tests/output/
tests/resources/private/
tmp/
pdfbox-app*.jar
.vscode/
IDEAS
_Dockerfile.local
scratch.py
+129
View File
@@ -0,0 +1,129 @@
dist: trusty
language: python
cache:
pip: true
directories:
- $HOME/Library/Caches/Homebrew
addons:
apt:
update: true
sources:
- sourceline: 'ppa:alex-p/tesseract-ocr'
- sourceline: 'ppa:heyarje/libav-11'
- sourceline: 'ppa:vshn/ghostscript'
packages:
- ghostscript
- libavcodec56
- libavformat56
- libavutil54
- libffi-dev
- pngquant
- poppler-utils
- qpdf
- tesseract-ocr
- tesseract-ocr-deu
- tesseract-ocr-eng
- tesseract-ocr-fra
env:
global:
- secure: "hsf6MT+n2x3OiDM2fQyJZdV0/PWYmv81LdVqC6cfnHBE/8N3DloJRqQ7WfO14TxhiK9PEC7MpyCj0lSabUHEO7gSH6Vks6I1asoSkt8S9/bSMlhT4hei+pwVpeGEiU5xHVATNjY+D919VC3IFvc3XmjT74h/2SLhaZ+jhEmDggM=" # HOMEBREW_OCRMYPDF_TOKEN
matrix:
include:
- os: linux
sudo: required
language: python
python: 3.5
env: EXTRAS=
- os: linux
sudo: required
language: python
python: 3.6
env: EXTRAS=
- os: linux
sudo: required
language: python
python: 3.6
env: EXTRAS=[fitz]
- os: osx
osx_image: xcode8
language: generic
env: EXTRAS=
- os: osx
osx_image: xcode8
language: generic
env: EXTRAS=[fitz]
before_cache:
- rm -f $HOME/.cache/pip/log/debug.log
before_install: |
mkdir -p bin
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
pip3 install --upgrade pip
pip3 install --upgrade wheel
mkdir -p packages
wget -q 'https://www.dropbox.com/s/vaq0kbwi6e6au80/unpaper_6.1-1.deb?raw=1' -O packages/unpaper_6.1-1.deb
sudo dpkg -i packages/unpaper_6.1-1.deb
elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update && brew bundle --file=.travis/Brewfile
pip3 install --upgrade pip
pip3 install wheel
fi
install:
- export PATH=$PWD/bin:$PATH
- pip3 install ".$EXTRAS"
- pip3 install -r test_requirements.txt
script:
- tesseract --version
- qpdf --version
- pytest -n auto
deploy:
# release for main pypi
# 3.6 w/o fitz is considered the build leader and does the deploy, otherwise there is
# a race and all versions will try to deploy
# OTOH if we ever need separate binary wheels then each version needs its
# own deploy
- provider: pypi
user: ocrmypdf-travis
password:
secure: "DTFOmmNL6olA0+yXvp4u9jXZlZeqrJsJ0526jzqf4a3gZ6jnGTq5UI6WzRsslSyoMMfXKtHQebqHM6ogSgCZinyZ3ufHJo8fn9brxbEc2gsiWkbj5o3bGwdWMT1vNNE7XW0VCpw87rZ1EEwjl4FJHFudMlPR1yfU5+uq0k0PACo="
distributions: "sdist bdist_wheel"
on:
branch: master
tags: true
condition: $TRAVIS_PYTHON_VERSION == "3.6" && $TRAVIS_OS_NAME == "linux" && $EXTRAS == ""
skip_upload_docs: true
# test pypi
- provider: pypi
server: https://testpypi.pypi.org/legacy/
user: ocrmypdf-travis
password:
secure: "DTFOmmNL6olA0+yXvp4u9jXZlZeqrJsJ0526jzqf4a3gZ6jnGTq5UI6WzRsslSyoMMfXKtHQebqHM6ogSgCZinyZ3ufHJo8fn9brxbEc2gsiWkbj5o3bGwdWMT1vNNE7XW0VCpw87rZ1EEwjl4FJHFudMlPR1yfU5+uq0k0PACo="
distributions: "sdist"
on:
branch: develop
tags: false
condition: $TRAVIS_OS_NAME == "osx"
skip_upload_docs: true
# null deploy for osx
# we really just want to run after_deploy *after* pypi upload is done, but
# after_deploy on runs if a given box deployed
- provider: script
script: /usr/bin/true
on:
branch: master
tags: true
condition: $TRAVIS_OS_NAME == "osx"
after_deploy: |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
bash .travis/osx_brew.sh
fi
+15
View File
@@ -0,0 +1,15 @@
brew 'python'
brew 'ghostscript'
brew 'jbig2dec'
brew 'jbig2enc'
brew 'leptonica'
brew 'libffi'
brew 'libtiff'
brew 'libxml2'
brew 'mupdf-tools'
brew 'openjpeg'
brew 'pngquant'
brew 'qpdf'
brew 'tesseract'
brew 'unpaper'
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
# © 2017-18 James R. Barlow: github.com/jbarlow83
from string import Template
from subprocess import run, PIPE
import re
recipe_template = Template("""
class Ocrmypdf < Formula
include Language::Python::Virtualenv
desc "Adds an OCR text layer to scanned PDF files"
homepage "https://github.com/jbarlow83/OCRmyPDF"
${ocrmypdf_url}
${ocrmypdf_sha256}
depends_on "pkg-config" => :build
depends_on "mupdf-tools" => :build # statically links libmupdf.a
depends_on "freetype"
depends_on "ghostscript"
depends_on "jpeg"
depends_on "libpng"
depends_on "python"
depends_on "qpdf"
depends_on "tesseract"
depends_on "unpaper"
${resources}
def install
venv = virtualenv_create(libexec, "python3")
resource("Pillow").stage do
inreplace "setup.py" do |s|
sdkprefix = MacOS::CLT.installed? ? "" : MacOS.sdk_path
s.gsub! "openjpeg.h", "probably_not_a_header_called_this_eh.h"
s.gsub! "ZLIB_ROOT = None", "ZLIB_ROOT = ('#{sdkprefix}/usr/lib', '#{sdkprefix}/usr/include')"
s.gsub! "JPEG_ROOT = None", "JPEG_ROOT = ('#{Formula["jpeg"].opt_prefix}/lib', '#{Formula["jpeg"].opt_prefix}/include')"
s.gsub! "FREETYPE_ROOT = None", "FREETYPE_ROOT = ('#{Formula["freetype"].opt_prefix}/lib', '#{Formula["freetype"].opt_prefix}/include')"
end
# avoid triggering "helpful" distutils code that doesn't recognize Xcode 7 .tbd stubs
ENV.append "CFLAGS", "-I#{MacOS.sdk_path}/System/Library/Frameworks/Tk.framework/Versions/8.5/Headers" unless MacOS::CLT.installed?
venv.pip_install Pathname.pwd
end
res = resources.map(&:name).to_set - ["Pillow"]
res.each do |r|
venv.pip_install resource(r)
end
venv.pip_install_and_link buildpath
end
test do
# Since we use Python 3, we require a UTF-8 locale
ENV["LC_ALL"] = "en_US.UTF-8"
system "#{bin}/ocrmypdf", "-f", "-q", "--deskew",
test_fixtures("test.pdf"), "ocr.pdf"
assert_predicate testpath/"ocr.pdf", :exist?
end
end
""")
def main():
p = run(['poet', '--single', 'ocrmypdf'],
encoding='utf-8', stdout=PIPE, check=True)
ocrmypdf_lines = p.stdout.splitlines()
ocrmypdf_url = ocrmypdf_lines[1].strip()
ocrmypdf_sha256 = ocrmypdf_lines[2].strip()
ocrmypdf_version = re.search(
r'ocrmypdf-(.+)\.tar.*', ocrmypdf_url).group(1)
print(f"Autobrewing {ocrmypdf_version}")
p = run(['poet', '--resources', 'ocrmypdf'],
encoding='utf-8', stdout=PIPE, check=True)
poet_resources = p.stdout
# Remove the duplicate "ocrmypdf" resource block
all_resources = poet_resources.split('resource')
kept_resources = [block for block in all_resources if 'ocrmypdf' not in block]
resources = 'resource'.join(kept_resources)
with open('ocrmypdf.rb', 'w') as out:
out.write(recipe_template.substitute(**locals()))
if __name__ == '__main__':
main()
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# © 2017 James R. Barlow: github.com/jbarlow83
set -uo pipefail
set -x
pip3 install homebrew-pypi-poet
python3 .travis/autobrew.py
cat ocrmypdf.rb
# brew audit crashes Travis
#brew audit ocrmypdf.rb
# Important: disable debug output so token is hidden
set +x
git clone https://$HOMEBREW_OCRMYPDF_TOKEN@github.com/jbarlow83/homebrew-ocrmypdf.git
set -x
pushd homebrew-ocrmypdf
cp ../ocrmypdf.rb Formula/ocrmypdf.rb
git add Formula/ocrmypdf.rb
git commit -m "homebrew-ocrmypdf: automatic release $TRAVIS_BUILD_NUMBER $TRAVIS_TAG"
git push origin master
popd
-17
View File
@@ -1,17 +0,0 @@
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+49
View File
@@ -0,0 +1,49 @@
# requirements
include requirements.txt
include test_requirements.txt
include dev_requirements.txt
# git
include .git_archival.txt
# docker
include .dockerignore
recursive-include .docker *
# tests
include .coveragerc
recursive-include tests *.jpg
recursive-include tests *.png
recursive-include tests *.pdf
recursive-include tests *.py
recursive-include tests *.rst
recursive-include tests *.txt
recursive-include tests/cache *
recursive-exclude tests/output *
recursive-exclude tests/output_pageinfo *
recursive-exclude tests/resources/private *
# documentation
include LICENSE
include *.rst
recursive-exclude .github *
recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs *.svg
recursive-exclude docs/_build *
# support files
recursive-include src/ocrmypdf/data *
include *.py
exclude tasks.py
recursive-exclude .travis *
exclude .travis*
# code
recursive-include src/ocrmypdf *.py
exclude ocrmypdf/lib/_leptonica.py
-336
View File
@@ -1,336 +0,0 @@
#!/bin/sh
##############################################################################
# Copyright (c) 2013: fritz-hh from Github (https://github.com/fritz-hh)
##############################################################################
VERSION="alpha0"
usage() {
cat << EOF
--------------------------------------------------------------------------------------
Script aimed at generating a searchable PDF file from a PDF file containing only images.
(The script performs optical character recognition of each respective page using the
tesseract engine)
Copyright: fritz from NAS4Free forum
Version: $VERSION
Usage: OCRmyPDF.sh [-h] [-v] [-g] [-k] [-d] [-c] [-i] [-l language] [-C filename] inputfile outputfile
-h : Display this help message
-v : Increase the verbosity (this option can be used more than once)
-g : Activate debug mode:
- Generates a PDF file containing each page twice (once with the image, once without the image
but with the OCRed text as well as the detected bounding boxes)
- Set the verbosity to the highest possible
-k : Do not delete the temporary files
-d : Deskew each page before performing OCR
-c : Clean each page before performing OCR
-i : Incorporate the cleaned image in the final PDF file (by default the original image
image, or the deskewed image if the -d option is set, is incorporated)
-l : Set the language of the PDF file in order to improve OCR results (default "eng")
Any language supported by tesseract is supported.
-C : Pass an additional configuration file to the tesseract OCR engine.
(this option can be used more than once)
Note: The configuration file must be available in the "tessdata/configs" folder
of your tesseract installation
inputfile : PDF file to be OCRed
outputfile : The PDF/A file to be generated
--------------------------------------------------------------------------------------
EOF
}
#################################################
# Get an absolute path from a relative path to a file
#
# Param1 : Relative path
# Returns: 1 if the folder in which the file is located does not exist
# 0 otherwise
#################################################
absolutePath() {
local wdsave absolutepath
wdsave="`pwd`"
! cd "`dirname "$1"`" 1> /dev/null 2> /dev/null && return 1
absolutepath="`pwd`/`basename $1`"
cd "$wdsave"
echo "$absolutepath"
return 0
}
# Initialization of constants
EXIT_BAD_ARGS="1" # possible exit codes
EXIT_BAD_INPUT_FILE="2"
EXIT_MISSING_DEPENDENCY="3"
EXIT_INVALID_OUPUT_PDFA="4"
EXIT_OTHER_ERROR="5"
LOG_ERR="0" # 0=only error messages
LOG_INFO="1" # 1=error messages and some infos
LOG_DEBUG="2" # 2=debug level logging
SRC="./src" # location of the source folder (except source of external tools like jhove)
JHOVE="./jhove/bin/JhoveApp.jar" # java SW for validating the final PDF/A
JHOVE_CFG="./jhove/conf/jhove.conf" # location of the jhove config file
# Initialization the configuration parameters with default values
VERBOSITY="$LOG_ERR" # default verbosity level
LAN="eng" # default language of the PDF file (required to get good OCR results)
KEEP_TMP="0" # do not delete the temporary files (default)
PREPROCESS_DESKEW="0" # 0=no, 1=yes (deskew image)
PREPROCESS_CLEAN="0" # 0=no, 1=yes (clean image to improve OCR)
PREPROCESS_CLEANTOPDF="0" # 0=no, 1=yes (put cleaned image in final PDF)
DEBUG_MODE="0" # 0=no, 1=yes (generates each PDF page twice, with and without image)
TESS_CFG_FILES="" # list of additional configuration files to be used by tesseract
# Parse optional command line arguments
while getopts ":hvgkdcil:C:" opt; do
case $opt in
h) usage ; exit 0 ;;
v) VERBOSITY=$(($VERBOSITY+1)) ;;
g) VERBOSITY="10"; DEBUG_MODE="1" ;;
k) KEEP_TMP="1" ;;
d) PREPROCESS_DESKEW="1" ;;
c) PREPROCESS_CLEAN="1" ;;
i) PREPROCESS_CLEANTOPDF="1" ;;
l) LAN="$OPTARG" ;;
C) TESS_CFG_FILES="$OPTARG $TESS_CFG_FILES" ;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
exit $EXIT_BAD_ARGS ;;
:)
echo "Option -$OPTARG requires an argument" >&2
usage
exit $EXIT_BAD_ARGS ;;
esac
done
# Remove the optional arguments parsed above.
shift $((OPTIND-1))
# Check if the number of mandatory parameters
# provided is as expected
if [ "$#" -ne "2" ]; then
echo "Exactly one mandatory argument shall be provided" >&2
usage
exit $EXIT_BAD_ARGS
fi
! absolutePath "$1" > /dev/null && echo "The folder in which the input file should be located does not exist. Exiting..." >&2 && exit $EXIT_BAD_ARGS
FILE_INPUT_PDF="`absolutePath "$1"`"
! absolutePath "$2" > /dev/null && echo "The folder in which the output file should be generated does not exist. Exiting..." >&2 && exit $EXIT_BAD_ARGS
FILE_OUTPUT_PDFA="`absolutePath "$2"`"
# set script path as working directory
cd "`dirname $0`"
[ $VERBOSITY -ge $LOG_INFO ] && echo "OCRmyPDF version: $VERSION"
# check if the required utilities are installed
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Checking if all dependencies are installed"
! command -v identify > /dev/null && echo "Please install ImageMagick. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v pdfimages > /dev/null && echo "Please install poppler-utils. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v pdftoppm > /dev/null && echo "Please install poppler-utils. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v pdftk > /dev/null && echo "Please install pdftk. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
[ $PREPROCESS_CLEAN -eq 1 ] && ! command -v unpaper > /dev/null && echo "Please install unpaper. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v tesseract > /dev/null && echo "Please install tesseract and tesseract-data. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v python > /dev/null && echo "Please install python, and the python libraries: reportlab, lxml. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v gs > /dev/null && echo "Please install ghostcript. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
! command -v java > /dev/null && echo "Please install java. Exiting..." >&2 && exit $EXIT_MISSING_DEPENDENCY
# Initialize path to temporary files
TMP_FLD="./tmp/`date +"%Y%m%d_%H%M"`.filename.`basename "$FILE_INPUT_PDF" | sed s/[.][^.]*//`"
FILE_SIZE_PAGES="$TMP_FLD/page-sizes.txt" # size in pt of the respective page of the input PDF file
FILES_OCRed_PDFS="${TMP_FLD}/*-ocred.pdf" # string matching all 1 page PDF files that need to be merged
FILE_OUTPUT_PDF="${TMP_FLD}/ocred.pdf" # name of the OCRed PDF file before conversion to PDF/A
FILE_VALIDATION_LOG="${TMP_FLD}/pdf_validation.log" # log file containing the results of the validation of the PDF/A file
# Create tmp folder
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Creating temporary folder"
rm -r -f "${TMP_FLD}"
mkdir -p "${TMP_FLD}"
# get the size of each pdf page (width / height) in pt (inch*72)
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Input file: Extracting size of each page (in pt)"
! identify -format "%w %h\n" "$FILE_INPUT_PDF" > "$FILE_SIZE_PAGES" \
&& echo "Could not get size of PDF pages. Exiting..." >&2 && exit $EXIT_BAD_INPUT_FILE
sed -I "" '/^$/d' "$FILE_SIZE_PAGES" # removing empty lines (last one should be)
numpages=`cat "$FILE_SIZE_PAGES" | wc -l | sed 's/^ *//g'`
[ $VERBOSITY -ge $LOG_INFO ] && echo "Input file: The file has $numpages pages"
# Itterate the pages of the input pdf file
cpt="1"
while read pageSize ; do
# add leading zeros to the page number
page=`printf "%04d" $cpt`
[ $VERBOSITY -ge $LOG_INFO ] && echo "Processing page $page"
# create the name of the required file
curOrigImg="$TMP_FLD/${page}_Image" # original image available in the current PDF page
# (the image file may have a different orientation than in the pdf file)
curHocr="$TMP_FLD/$page.hocr" # hocr file to be generated by the OCR SW for the current page
curOCRedPDF="$TMP_FLD/${page}-ocred.pdf" # PDF file containing the image + the OCRed text for the current page
curOCRedPDFDebug="$TMP_FLD/${page}-debug-ocred.pdf" # PDF file containing data required to find out if OCR worked correctly
# get width / height of PDF page (in pt)
widthPDF=`echo $pageSize | cut -f1 -d" "`
heightPDF=`echo $pageSize | cut -f2 -d" "`
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: size ${heightPDF}x${widthPDF} (h*w in pt)"
# extract raw image from pdf file to compute resolution
# unfortunatelly this image can have another orientation than in the pdf...
# so we will have to extract it again later
pdfimages -f $page -l $page -j "$FILE_INPUT_PDF" "$curOrigImg" 1>&2
# count number of extracted images
nbImg=`ls -1 "$curOrigImg"* | wc -l`
[ $nbImg -ne "1" ] && echo "Expecting exactly 1 image on page $page (found $nbImg). Exiting..." >&2 && exit $EXIT_BAD_INPUT_FILE
# Get characteristics of the extracted image
curImg=`ls -1 "$curOrigImg"*`
propCurImg=`identify -format "%w %h %[colorspace]" "$curImg"`
widthCurImg=`echo "$propCurImg" | cut -f1 -d" "`
heightCurImg=`echo "$propCurImg" | cut -f2 -d" "`
colorspaceCurImg=`echo "$propCurImg" | cut -f3 -d" "`
# switch height/width values if the image has not the right orientation
# we make here the assumption that vertical/horizontal dpi are equal
# we will check that later
if [ $((($heightPDF-$widthPDF)*($heightCurImg-$widthCurImg))) -lt 0 ]; then
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Extracted image has wrong orientation. Inverting image height/width values"
tmpval=$heightCurImg
heightCurImg=$widthCurImg
widthCurImg=$tmpval
fi
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: size ${heightCurImg}x${widthCurImg} (h*w pixel)"
# compute the resolution of the image
dpi_x=$(($widthCurImg*72/$widthPDF))
dpi_y=$(($heightCurImg*72/$heightPDF))
[ "$dpi_x" -ne "$dpi_y" ] && echo "X/Y resolutions not equal ($dpi_x/$dpi_y). Exiting..." >&2 && exit $EXIT_BAD_INPUT_FILE
dpi="$dpi_x"
# Identify if page image should be saved as ppm (color) or pgm (gray)
ext="ppm"
opt=""
if [ $colorspaceCurImg == "Gray" ]; then
ext="pgm"
opt="-gray"
fi
curImgPixmap="$TMP_FLD/$page.$ext"
curImgPixmapDeskewed="$TMP_FLD/$page.deskewed.$ext"
curImgPixmapClean="$TMP_FLD/$page.cleaned.$ext"
# extract current page as image with right orientation and resoltution
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Extracting image as $ext file (${dpi} dpi)"
! pdftoppm -f $page -l $page -r $dpi $opt "$FILE_INPUT_PDF" > "$curImgPixmap" \
&& echo "Could not extract page $page as $ext from $FILE_INPUT_PDF. Exiting..." >&2 && exit $EXIT_OTHER_ERROR
# if requested deskew image (without changing its size in pixel)
if [ "$PREPROCESS_DESKEW" -eq "1" ]; then
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Deskewing image"
! convert "$curImgPixmap" -deskew 40% -gravity center -extent ${heightCurImg}x${widthCurImg} "$curImgPixmapDeskewed" \
&& echo "Could not deskew \"$curImgPixmap\". Exiting..." >&2 && exit $EXIT_OTHER_ERROR
else
cp "$curImgPixmap" "$curImgPixmapDeskewed"
fi
# if requested clean image with unpaper to get better OCR results
if [ "$PREPROCESS_CLEAN" -eq "1" ]; then
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Cleaning image with unpaper"
! unpaper --dpi $dpi --mask-scan-size 100 \
--no-deskew --no-grayfilter --no-blackfilter --no-mask-center --no-border-align \
"$curImgPixmapDeskewed" "$curImgPixmapClean" 1> /dev/null \
&& echo "Could not clean \"$curImgPixmapDeskewed\". Exiting..." >&2 && exit $EXIT_OTHER_ERROR
else
cp "$curImgPixmapDeskewed" "$curImgPixmapClean"
fi
# perform OCR
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Performing OCR"
! tesseract -l "$LAN" "$curImgPixmapClean" "$curHocr" hocr $TESS_CFG_FILES 1> /dev/null 2> /dev/null \
&& echo "Could not OCR file \"$curImgPixmapClean\". Exiting..." >&2 && exit $EXIT_OTHER_ERROR
mv "$curHocr.html" "$curHocr"
# embed text and image to new pdf file
if [ "$PREPROCESS_CLEANTOPDF" -eq "1" ]; then
image4finalPDF="$curImgPixmapClean"
else
image4finalPDF="$curImgPixmapDeskewed"
fi
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Embedding text in PDF"
! python $SRC/hocrTransform.py -r $dpi -i "$image4finalPDF" "$curHocr" "$curOCRedPDF" \
&& echo "Could not create PDF file from \"$curHocr\". Exiting..." >&2 && exit $EXIT_OTHER_ERROR
# if requested generate special debug PDF page with visible OCR text
if [ $DEBUG_MODE -eq "1" ] ; then
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Page $page: Embedding text in PDF (debug page)"
! python $SRC/hocrTransform.py -b -r $dpi "$curHocr" "$curOCRedPDFDebug" \
&& echo "Could not create PDF file from \"$curHocr\". Exiting..." >&2 && exit $EXIT_OTHER_ERROR
fi
# delete temporary files created for the current page
# to avoid using to much disk space in case of PDF files having many pages
if [ $KEEP_TMP -eq 0 ]; then
rm "$curOrigImg"*.*
rm "$curHocr"
rm "$curImgPixmap"
rm "$curImgPixmapDeskewed"
rm "$curImgPixmapClean"
fi
# go to next page of the pdf
cpt=$(($cpt+1))
done < "$FILE_SIZE_PAGES"
# concatenate all pages
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Output file: Concatenating all pages"
! pdftk $FILES_OCRed_PDFS cat output "$FILE_OUTPUT_PDF" \
&& echo "Could not concatenate individual PDF pages (\"$FILES_OCRed_PDFS\") to one file. Exiting..." >&2 && exit $EXIT_OTHER_ERROR
# insert metadata (copy metadata from input file)
#echo "Output file: Inserting metadata"
# TODO (may work with pdftk update_info)
# the name of the file may be used as title
# convert the pdf file to match PDF/A format
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Output file: Converting to PDF/A"
! gs -dQUIET -dPDFA -dBATCH -dNOPAUSE -dUseCIEColor \
-sProcessColorModel=DeviceCMYK -sDEVICE=pdfwrite -sPDFACompatibilityPolicy=2 \
-sOutputFile=$FILE_OUTPUT_PDFA "$FILE_OUTPUT_PDF" 1> /dev/null 2> /dev/null \
&& echo "Could not convert PDF file \"$FILE_OUTPUT_PDF\" to PDF/A. Exiting..." >&2 && exit $EXIT_OTHER_ERROR
# validate generated pdf file (compliance to PDF/A)
[ $VERBOSITY -ge $LOG_DEBUG ] && echo "Output file: Checking compliance to PDF/A standard"
java -jar "$JHOVE" -c "$JHOVE_CFG" -m PDF-hul "$FILE_OUTPUT_PDFA" > "$FILE_VALIDATION_LOG"
grep -i "Status|Message" "$FILE_VALIDATION_LOG" # summary of the validation
[ $VERBOSITY -ge $LOG_DEBUG ] && cat "$FILE_VALIDATION_LOG"
# check the validation results
pdf_valid=1
grep -i 'ErrorMessage' "$FILE_VALIDATION_LOG" && pdf_valid=0
grep -i 'Status.*not valid' "$FILE_VALIDATION_LOG" && pdf_valid=0
grep -i 'Status.*Not well-formed' "$FILE_VALIDATION_LOG" && pdf_valid=0
! grep -i 'Profile:.*PDF/A-1' "$FILE_VALIDATION_LOG" && pdf_valid=0
# delete temporary files
if [ $KEEP_TMP -eq 0 ]; then
rm -r -f "${TMP_FLD}"
fi
[ $pdf_valid -ne 1 ] && echo "Output file: The generated PDF/A file is INVALID" >&2 && exit $EXIT_INVALID_OUPUT_PDFA
[ $VERBOSITY -ge $LOG_INFO ] && echo "Output file: The generated PDF/A file is VALID"
exit 0
-40
View File
@@ -1,40 +0,0 @@
OCRmyPDF
========
OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched
To get the script usage, call: ./OCmyPDF.sh -h
Features
--------
- Generate a searchable PDF/A file from a PDF file containing only images
- Keep the exact resolution of the original embedded images
- If requested deskew and / or clean the image before performing OCR
- Validate the generated file against the PDF/A specification using jhove
- Provides debug mode to enable easy verification of the OCR results
Motivation
----------
I searched the web for a free command line tool to OCR PDF files on linux/unix:
I found many, but none of them were really satisfying.
- Either they produced PDF files with misplaced text under the image (making copy/paste impossible)
- Or they changed the resolution of the embedded images
- Or they generated PDF file having a ridiculous big size
- Or they crashed when trying to OCR some of my PDF files
- Or they did not produce valid PDF files (even though they were readable with my current PDF reader)
- On top of that none of them produced PDF/A files (format dedicated for long time storage / archiving)
... so I decided to develop my own tool (using various existing scripts as an inspiration)
Install
--------
Download OCRmyPDF here: https://github.com/fritz-hh/OCRmyPDF/tags
Copy the file in onto your linux/unix machine and extract it.
Run: "sh ./OCRmyPDF.sh" to get the script usage
If not yet installed, the script will notify you about dependencies that need to be installed
+154
View File
@@ -0,0 +1,154 @@
OCRmyPDF
========
.. image:: https://travis-ci.org/jbarlow83/OCRmyPDF.svg?branch=master
:target: https://travis-ci.org/jbarlow83/OCRmyPDF
.. image:: https://img.shields.io/pypi/v/ocrmypdf.svg
:target: https://pypi.org/project/ocrmypdf/
.. image:: https://img.shields.io/homebrew/v/ocrmypdf.svg
:alt: homebrew
:target: http://brewformulas.org/Ocrmypdf
OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to
be searched or copy-pasted.
.. code-block:: bash
ocrmypdf # it's a scriptable command line program
-l eng+fra # it supports multiple languages
--rotate-pages # it can fix pages that are misrotated
--deskew # it can deskew crooked PDFs!
--title "My PDF" # it can change output metadata
--jobs 4 # it uses multiple cores by default
--output-type pdfa # it produces PDF/A by default
input_scanned.pdf # takes PDF input (or images)
output_searchable.pdf # produces validated PDF output
Main features
-------------
- Generates a searchable
`PDF/A <https://en.wikipedia.org/?title=PDF/A>`_ file from a regular PDF
- Places OCR text accurately below the image to ease copy / paste
- Keeps the exact resolution of the original embedded images
- When possible, inserts OCR information as a "lossless" operation without rendering vector information
- Keeps file size about the same
- If requested deskews and/or cleans the image before performing OCR
- Validates input and output files
- Processes pages in parallel when more than one CPU core is
available
- Uses `Tesseract OCR <https://github.com/tesseract-ocr/tesseract>`_ engine
- Supports more than `100 languages <https://github.com/tesseract-ocr/tessdata>`_ recognized by Tesseract
- Battle-tested on thousands of PDFs, a test suite and continuous integration
For details: please consult the `documentation <https://ocrmypdf.readthedocs.io/en/latest/>`_.
Motivation
----------
I searched the web for a free command line tool to OCR PDF files on
Linux/UNIX: I found many, but none of them were really satisfying.
- Either they produced PDF files with misplaced text under the image (making copy/paste impossible)
- Or they did not handle accents and multilingual characters
- Or they changed the resolution of the embedded images
- Or they generated ridiculously large PDF files
- Or they crashed when trying to OCR some of my PDF files
- Or they did not produce valid PDF files (even though they were readable with my current PDF reader)
- On top of that none of them produced PDF/A files (format dedicated for long time storage)
...so I decided to develop my own tool (using various existing scripts
as an inspiration).
Installation
------------
Linux, UNIX, and macOS are supported. Windows is not directly supported but there is a Docker image available that runs on Windows.
Users of Debian 9 or later or Ubuntu 16.10 or later may simply
.. code-block:: bash
apt-get install ocrmypdf
and macOS users with Homebrew may simply
.. code-block:: bash
brew install ocrmypdf
For everyone else, `see our documentation <https://ocrmypdf.readthedocs.io/en/latest/installation.html>`_ for installation steps.
Languages
---------
OCRmyPDF uses Tesseract for OCR, and relies on its language packs. For Linux users,
you can often find packages that provide language packs:
.. code-block:: bash
# Display a list of all Tesseract language packs
apt-cache search tesseract-ocr
# Debian/Ubuntu users
apt-get install tesseract-ocr-chi-sim # Example: Install Chinese Simplified language back
You can then pass the ``-l LANG`` argument to OCRmyPDF to give a hint as to what languages it should search for. Multiple
languages can be requested.
Documentation and support
-------------------------
Once ocrmypdf is installed, the built-in help which explains the command syntax and options can be accessed via:
.. code-block:: bash
ocrmypdf --help
Our `documentation is served on Read the Docs <https://ocrmypdf.readthedocs.io/en/latest/index.html>`_.
If you detect an issue, please:
- Check whether your issue is already known
- If no problem report exists on github, please create one here:
https://github.com/jbarlow83/OCRmyPDF/issues
- Describe your problem thoroughly
- Append the console output of the script when running the debug mode
(``-v 1`` option)
- If possible provide your input PDF file as well as the content of the
temporary folder (using a file sharing service like Dropbox)
Requirements
------------
Runs on CPython 3.6, and requires external program installations of Ghostscript, Tesseract OCR, QPDF, and Leptonica. ocrmypdf is pure Python, but uses CFFI to portably generate library bindings.
Python 3.5 is also supported.
Press & Media
-------------
- `c't 1-2014, page 59 <http://heise.de/-2279695>`_:
Detailed presentation of OCRmyPDF v1.0 in the leading German IT
magazine c't
- `heise Open Source, 09/2014: Texterkennung mit
OCRmyPDF <http://heise.de/-2356670>`_
License
-------
The OCRmyPDF software is licensed under the GNU GPLv3. Certain files are covered by other licenses, as noted in their source files.
The license for each test file varies, and is noted in tests/resources/README.rst. The documentation is licensed under Creative Commons Attribution-ShareAlike 4.0 (CC-BY-SA 4.0).
OCRmyPDF versions prior to 6.0 were licensed under the MIT License.
Disclaimer
----------
The software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
-37
View File
@@ -1,37 +0,0 @@
RELEASE NOTES
=============
Please always read this file before installing the package
Download software here: https://github.com/fritz-hh/OCRmyPDF/tags
v1.0-rc1 (2013-04-26):
====
New features
------------
- First release candidate
Changes
-------
- N/A
Fixes
-----
- N/A
Tested with
-----------
- Operating system: FreeBSD 9.1
- Dependencies:
- poppler-utils 0.22.2
- ImageMagick 6.8.0-7 2013-03-30
- tesseract 3.02.02
- Python 2.7.3
- pdftk 1.45
- ghoscript (gs): 9.06
- java: openjdk version "1.7.0_17"
+4
View File
@@ -0,0 +1,4 @@
check-manifest >= 0.35
twine >= 1.8.1
coverage >= 4.4
GitPython == 2.1.3
+189
View File
@@ -0,0 +1,189 @@
Advanced features
=================
Control of OCR options
----------------------
OCRmyPDF provides many features to control the behavior of the OCR engine, Tesseract.
When OCR is skipped
"""""""""""""""""""
If a page in a PDF seems to have text, by default OCRmyPDF will exit without modifying the PDF. This is to ensure that PDFs that were previously OCRed or were "born digital" rather than scanned are not processed.
If ``--skip-text`` is issued, then no OCR will be performed on pages that already have text. The page will be copied to the output. This may be useful for documents that contain both "born digital" and scanned content, or to use OCRmyPDF to normalize and convert to PDF/A regardless of their contents.
If ``--force-ocr`` is issued, then all pages will be rasterized to images, discarding any hidden OCR text, and rasterizing any printable text. This is useful for redoing OCR, for fixing OCR text with a damaged character map (text is selectable but not searchable), and destroying redacted information.
Time and image size limits
""""""""""""""""""""""""""
By default, OCRmyPDF permits tesseract to run for three minutes (180 seconds) per page. This is usually more than enough time to find all text on a reasonably sized page with modern hardware.
If a page is skipped, it will be inserted without OCR. If preprocessing was requested, the preprocessed image layer will be inserted.
If you want to adjust the amount of time spent on OCR, change ``--tesseract-timeout``. You can also automatically skip images that exceed a certain number of megapixels with ``--skip-big``. (A 300 DPI, 8.5×11" page is 8.4 megapixels.)
.. 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
Overriding default tesseract
""""""""""""""""""""""""""""
OCRmyPDF checks the system ``PATH`` for the ``tesseract`` binary.
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.
.. 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.)
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
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.)
Overriding other support programs
"""""""""""""""""""""""""""""""""
In addition to tesseract, OCRmyPDF uses the following external binaries:
* ``gs`` (Ghostscript)
* ``unpaper``
* ``qpdf``
In each case OCRmyPDF will search the ``PATH`` environment variable to locate the binaries.
Changing tesseract configuration variables
""""""""""""""""""""""""""""""""""""""""""
You can override tesseract's default `control parameters <https://github.com/tesseract-ocr/tesseract/wiki/ControlParams>`_ with a configuration file.
As an example, this configuration will disable Tesseract's dictionary for current language. Normally the dictionary is helpful for interpolating words that are unclear, but it may interfere with OCR if the document does not contain many words (for example, a list of part numbers).
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
then run ocrmypdf as follows (along with any other desired arguments):
.. code-block:: bash
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.
Changing the PDF renderer
-------------------------
rasterizing
Converting a PDF to an image for display.
rendering
Creating a new PDF from other data (such as an existing PDF).
OCRmyPDF has these PDF renderers: ``sandwich`` and ``hocr``. The renderer may be selected using ``--pdf-renderer``. The default is ``auto`` which lets OCRmyPDF select the renderer to use. Currently, ``auto`` selects ``sandwich`` for Tesseract 3.05.01 or newer, or ``hocr`` for older versions of Tesseract.
The ``sandwich`` renderer
"""""""""""""""""""""""""
The ``sandwich`` renderer uses Tesseract's new text-only PDF feature, which produces a PDF page that lays out the OCR in invisible text. This page is then "sandwiched" onto the original PDF page, allowing lossless application of OCR even to PDF pages that contain other vector objects.
Currently this is the best renderer for most uses, however it is implemented in Tesseract so OCRmyPDF cannot influence it. Currently some problematic PDF viewers like Mozilla PDF.js and macOS Preview have problems with segmenting its text output, and mightrunseveralwordstogether.
When image preprocessing features like ``--deskew`` are used, the original PDF will be rendered as a full page and the OCR layer will be placed on top.
If a PDF created with this renderer using Tesseract versions older than 3.05.00 is then passed through Ghostscript's pdfwrite feature, the OCR text *may* be corrupted. The ``--output-type=pdfa`` argument will produce a warning in this situation. For this reason, OCRmyPDF automatically selects the ``hocr`` for older Tesseract versions.
The ``hocr`` renderer
"""""""""""""""""""""
The ``hocr`` renderer works with older versions of Tesseract. The image layer is copied from the original PDF page if possible, avoiding potentially lossy transcoding or loss of other PDF information. If preprocessing is specified, then the image layer is a new PDF.
Unlike ``sandwich`` this renderer is implemented within OCRmyPDF; anyone looking to customize how OCR is presented should look here. A major disadvantage of this renderer is it not capable of correctly handling text outside the Latin alphabet. Pull requests to improve the situation are welcome.
Currently, this renderer has the best compatibility with Mozilla's PDF.js viewer.
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``.
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.
+214
View File
@@ -0,0 +1,214 @@
Batch processing
================
This article provides information about running OCRmyPDF on multiple files or configuring it as a service triggered by file system events.
Batch jobs
----------
Consider using the excellent `GNU Parallel <https://www.gnu.org/software/parallel/>`_ to apply OCRmyPDF to multiple files at once.
Both ``parallel`` and ``ocrmypdf`` will try to use all available processors. To maximize parallelism without overloading your system with processes, consider using ``parallel -j 2`` to limit parallel to running two jobs at once.
This command will run all ocrmypdf all files named ``*.pdf`` in the current directory and write them to the previous created ``output/`` folder. It will not search subdirectories.
The ``--tag`` argument tells parallel to print the filename as a prefix whenever a message is printed, so that one can trace any errors to the file that produced them.
.. code-block:: bash
parallel --tag -j 2 ocrmypdf '{}' 'output/{}' ::: *.pdf
OCRmyPDF automatically repairs PDFs before parsing and gathering information from them. If you are already repairing PDFs with ``qpdf`` prior to attempting OCR, or you can use ``--skip-repair`` to skip this step. It may improve performance for large files, since repairing PDFs is single-threaded.
Directory trees
---------------
This will walk through a directory tree and run OCR on all files in place, printing the output in a way that makes
.. code-block:: bash
find . --printf '%p' -name '*.pdf' -exec ocrmypdf '{}' '{}' \;
This only runs one ``ocrmypdf`` process at a time. This variation uses ``find`` to create a directory list and ``parallel`` to parallelize runs of ``ocrmypdf``, again updating files in place.
.. code-block:: bash
find . -name '*.pdf' | parallel --tag -j 2 ocrmypdf '{}' '{}'
Sample script
"""""""""""""
This user contributed script also provides an example of batch processing.
.. code-block:: python
#!/usr/bin/env python3
# Walk through directory tree, replacing all files with OCR'd version
# Contributed by DeliciousPickle@github
import logging
import os
import subprocess
import sys
script_dir = os.path.dirname(os.path.realpath(__file__))
print(script_dir + '/ocr-tree.py: Start')
if len(sys.argv) > 1:
start_dir = sys.argv[1]
else:
start_dir = '.'
if len(sys.argv) > 2:
log_file = sys.argv[2]
else:
log_file = script_dir + '/ocr-tree.log'
logging.basicConfig(
level=logging.INFO, format='%(asctime)s %(message)s',
filename=log_file, filemode='w')
for dir_name, subdirs, file_list in os.walk(start_dir):
logging.info('\n')
logging.info(dir_name + '\n')
os.chdir(dir_name)
for filename in file_list:
file_ext = os.path.splitext(filename)[1]
if file_ext == '.pdf':
full_path = dir_name + '/' + filename
print(full_path)
cmd = ["ocrmypdf", "--deskew", filename, filename]
logging.info(cmd)
proc = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
result = proc.stdout.read()
if proc.returncode == 6:
print("Skipped document because it already contained text")
elif proc.returncode == 0:
print("OCR complete")
logging.info(result)
API
"""
OCRmyPDF is currently supported as a command line interface. This means that even if you are using OCRmyPDF in a Python script, you should run it in a subprocess rather importing the ocrmypdf package.
The reason for this limitation is that the `ruffus <https://github.com/bunbun/ruffus/>`_ library that OCRmyPDF depends on is unfortunately not reentrant. OCRmyPDF works by defining each operation it does as a ruffus task that takes one or more files as input and generates one or more files as output. As such ruffus is fairly fundamental.
(If you find individual functions implemented in OCRmyPDF useful (such as ``ocrmypdf.pdfinfo``), you can use these if you wish to.)
Synology DiskStations
"""""""""""""""""""""
Synology DiskStations (Network Attached Storage devices) can run the Docker image of OCRmyPDF if the Synology `Docker package <https://www.synology.com/en-global/dsm/packages/Docker>`_ is installed. Attached is a script to address particular quirks of using OCRmyPDF on one of these devices.
This is only possible for x86-based Synology products. Some Synology products use ARM or Power processors and do not support Docker. Further adjustments might be needed to deal with the Synology's relatively limited CPU and RAM.
.. code-block:: python
#!/bin/env python3
# Contributed by github.com/Enantiomerie
# script needs 2 arguments
# 1. source dir with *.pdf - default is location of script
# 2. move dir where *.pdf and *_OCR.pdf are moved to
import logging
import os
import subprocess
import sys
import time
import shutil
script_dir = os.path.dirname(os.path.realpath(__file__))
timestamp = time.strftime("%Y-%m-%d-%H%M_")
log_file = script_dir + '/' + timestamp + 'ocrmypdf.log'
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', filename=log_file, filemode='w')
if len(sys.argv) > 1:
start_dir = sys.argv[1]
else:
start_dir = '.'
for dir_name, subdirs, file_list in os.walk(start_dir):
logging.info('\n')
logging.info(dir_name + '\n')
os.chdir(dir_name)
for filename in file_list:
file_ext = os.path.splitext(filename)[1]
if file_ext == '.pdf':
full_path = dir_name + '/' + filename
file_noext = os.path.splitext(filename)[0]
timestamp_OCR = time.strftime("%Y-%m-%d-%H%M_OCR_")
filename_OCR = timestamp_OCR + file_noext + '.pdf'
docker_mount = dir_name + ':/home/docker'
# create string for pdf processing
# diskstation needs a user:group docker:docker. find uid:gid of your diskstation docker:docker with id docker.
# use this uid:gid in -u flag
# rw rights for docker:docker at source dir are also necessary
# the script is processed as root user via chron
cmd = ['docker', 'run', '--rm', '-v', docker_mount, '-u=1030:65538', 'jbarlow83/ocrmypdf', , '--deskew' , filename, filename_OCR]
logging.info(cmd)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
result = proc.stdout.read()
logging.info(result)
full_path_OCR = dir_name + '/' + filename_OCR
os.chmod(full_path_OCR, 0o666)
os.chmod(full_path, 0o666)
full_path_OCR_archive = sys.argv[2]
full_path_archive = sys.argv[2] + '/no_ocr'
shutil.move(full_path_OCR,full_path_OCR_archive)
shutil.move(full_path, full_path_archive)
logging.info('Finished.\n')
Huge batch jobs
"""""""""""""""
If you have thousands of files to work with, contact the author. Consulting work related to OCRmyPDF helps fund this open source project and all inquiries are appreciated.
Hot (watched) folders
---------------------
To set up a "hot folder" that will trigger OCR for every file inserted, use a program like Python `watchdog <https://pypi.python.org/pypi/watchdog>`_ (supports all major OS).
One could then configure a scanner to automatically place scanned files in a hot folder, so that they will be queued for OCR and copied to the destination.
.. code-block:: bash
pip install watchdog
watchdog installs the command line program ``watchmedo``, which can be told to run ``ocrmypdf`` on any .pdf added to the current directory (``.``) and place the result in the previously created ``out/`` folder.
.. code-block:: bash
cd hot-folder
mkdir out
watchmedo shell-command \
--patterns="*.pdf" \
--ignore-directories \
--command='ocrmypdf "${watch_src_path}" "out/${watch_src_path}" ' \
. # don't forget the final dot
For more complex behavior you can write a Python script around to use the watchdog API.
On file servers, you could configure watchmedo as a system service so it will run all the time.
Caveats
"""""""
* ``watchmedo`` may not work properly on a networked file system, depending on the capabilities of the file system client and server.
* This simple recipe does not filter for the type of file system event, so file copies, deletes and moves, and directory operations, will all be sent to ocrmypdf, producing errors in several cases. Disable your watched folder if you are doing anything other than copying files to it.
* If the source and destination directory are the same, watchmedo may create an infinite loop.
* On BSD, FreeBSD and older versions of macOS, you may need to increase the number of file descriptors to monitor more files, using ``ulimit -n 1024`` to watch a folder of up to 1024 files.
Alternatives
""""""""""""
* `Watchman <https://facebook.github.io/watchman/>`_ is a more powerful alternative to ``watchmedo``.
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 915 585" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="b" y2="445" gradientUnits="userSpaceOnUse" y1="179" gradientTransform="translate(0 -2.06)" x2="-29.7" x1="322">
<stop stop-color="#333" offset="0"/>
<stop stop-color="#fff" stop-opacity="0" offset="1"/>
</linearGradient>
<linearGradient id="a" y2="414" gradientUnits="userSpaceOnUse" y1="159" x2="490" x1="815">
<stop stop-color="#33f" offset="0"/>
<stop stop-color="#3f3fff" stop-opacity="0" offset="1"/>
</linearGradient>
</defs>
<path fill="url(#b)" d="m403 247c-12 115-135 122-368 123-4.3-1.07-7.32-7.33-6-41 76-37 151-124 167-236 93 123 201 40.9 207 154z"/>
<g stroke-width="3.45" fill="none">
<path stroke="#000" d="m11.8 11.8h411v411l-411 0.01v-411z"/>
<path stroke="#448" d="m489 11.7h415v411h-415v-411z"/>
</g>
<path d="m876 244c-12 115-133 120-366 121-6-14-10-40-3-43 76-37.3 136-106 152-218 38 48 209 101 217 140z" fill="url(#a)"/>
<g id="RasterLarge" transform="matrix(1.36 0 0 1.28 -161 -636)">
<path fill="#999" d="m287 730h-30v-60h20v20h10v10h10v10h20v10h40v-10h20v-20h10v-20h-10v-10h-10v-10h-40v-10h-30v-10h-20v-10h-10v-10h-10v-60h10v-10h10v-10h10v-10h90v10h30v60h-20v-10h-10v-20h-10v-10h-20v-10h-30v10h-20v10h-10v20h10v20h30v10h30v10h30v10h10v10h10v10h10v60h-10v10h-10v10h-10v10h-100v-10z"/>
<path fill="#555" d="m297 730h-30v-10h-10v-50h20v20h10v20h10 10v10h60v-10h10v-10h10v-40h-20v-10h-20v-10h-40v-10h-20v-10h-20v-20h-10v-50h10v-10h10v-10h20v-10h70v10h30v10h10v50h-20v-20h-10v-20h-20v-10h-50v10h-10v10h-10v30h10v10h20v10h30v10h30v10h20v10h10v10h10v50h-10v20h-10v10h-20v10h-80z"/>
<path d="m307 730h-30v-10h-20v-50h20v30h10v10h10v10h70v-10h20v-50h-10v-10h-20v-10h-40v-10h-30v-10h-10v-10h-10v-20h-10v-30h10v-20h20v-10h20v-10h50v10h30v10h20v50h-20v-30h-10v-10h-10v-10h-70v10h-10v40h10v10h10v10h30v10h40v10h20v10h10v20h10v40h-10v20h-20v10h-30v10h-50v-10z"/>
</g>
<g font-size="40" font-family="sans-serif" text-anchor="middle">
<g font-size="100">
<text y="518" x="210">Raster</text>
<text y="518" x="695" fill="#338">Vector</text>
</g>
<text y="563" x="210">.jpeg .gif .png</text>
<text y="563" x="696" fill="#338">.svg</text>
</g>
<path id="VectorLarge" fill="#005" d="m661 294v-62.5l23.4 0.184c0.678 20.8 7.32 36.3 19.9 46.4 12.7 9.93 32.1 14.9 57.9 14.9 24.1 0 42.5-4.29 55.1-12.9 12.7-8.71 19.1-21.3 19.1-37.9 0-13.2-3.86-23.4-11.6-30.5-7.59-7.11-23.7-14-48.4-20.8l-40.1-10.9c-29-7.97-49.5-17.9-61.4-29.8-11.8-11.9-17.7-28.2-17.7-48.9 0-23.3 9.15-41.4 27.4-54.3 18.3-12.9 44-19.3 77.1-19.3 14.1 0 29.6 1.41 46.4 4.23 16.8 2.7 34.7 6.68 53.7 12v58.5h-23c-2.3-19.4-9.49-33.4-21.6-41.9-11.9-8.71-30.2-13.1-54.7-13.1-21.4 0-37.7 3.99-49 12-11.1 7.85-16.7 19.3-16.7 34.4 0 13.1 4.2 23.4 12.6 30.9 8.4 7.48 26.2 14.9 53.5 22.3l37.6 10.1c27.5 7.48 47.1 17 58.8 28.7 11.8 11.5 17.7 27 17.7 46.5 0 26.6-9.42 46.7-28.3 60.2s-46.9 20.2-84.2 20.2c-16.7 0-33.7-1.53-51-4.6-18-3-35-7-53-13z"/>
<use xlink:href="#VectorLarge" transform="matrix(.17 0 0 .17 392 313)"/>
<use xlink:href="#RasterLarge" transform="matrix(.173 0 0 .173 -8.25 314)" height="100%" width="100%" y="0" x="0"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Executable
+350
View File
@@ -0,0 +1,350 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# ocrmypdf documentation build configuration file, created by
# sphinx-quickstart on Sun Sep 4 14:29:43 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
# 'sphinx.ext.mathjax',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'ocrmypdf'
copyright = '2018, James R. Barlow. Licensed under Creative Commons Attribution-ShareAlike 4.0.'
author = 'James R. Barlow'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
from ocrmypdf import __version__ as OCRMYPDF_VERSION
_version_parts = OCRMYPDF_VERSION.split('.')
version = '.'.join(_version_parts[0:2])
# The full version, including alpha/beta/rc tags.
release = OCRMYPDF_VERSION
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#
# today = ''
#
# Else, today_fmt is used as the format for a strftime call.
#
today_fmt = '%Y-%m-%d'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
import sphinx_rtd_theme
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'display_version': False
}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#
# html_title = 'ocrmypdf v4.2'
# A shorter title for the navigation bar. Default is the same as html_title.
#
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#
# html_logo = None
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#
# html_extra_path = []
# If not None, a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'.
#
# html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#
# html_additional_pages = {}
# If false, no module index is generated.
#
# html_domain_indices = True
# If false, no index is generated.
#
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
#
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# 'ja' uses this config value.
# 'zh' user can custom change `jieba` dictionary path.
#
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'ocrmypdfdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'ocrmypdf.tex', 'ocrmypdf Documentation',
'James R. Barlow', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#
# latex_use_parts = False
# If true, show page references after internal links.
#
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#
# latex_appendices = []
# It false, will not define \strong, \code, itleref, \crossref ... but only
# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
# packages.
#
# latex_keep_old_macro_names = True
# If false, no module index is generated.
#
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'ocrmypdf', 'ocrmypdf Documentation',
[author], 1)
]
# If true, show URL addresses after external links.
#
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'ocrmypdf', 'ocrmypdf Documentation',
author, 'ocrmypdf', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#
# texinfo_appendices = []
# If false, no module index is generated.
#
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#
# texinfo_no_detailmenu = False
+200
View File
@@ -0,0 +1,200 @@
Cookbook
========
Basic examples
--------------
Help!
"""""
ocrmypdf has built-in help.
.. code-block:: bash
ocrmypdf --help
Add an OCR layer and convert to PDF/A
"""""""""""""""""""""""""""""""""""""
.. code-block:: bash
ocrmypdf input.pdf output.pdf
Add an OCR layer and output a standard PDF
""""""""""""""""""""""""""""""""""""""""""
.. code-block:: bash
ocrmypdf --output-type pdf input.pdf output.pdf
Create a PDF/A with all color and grayscale images converted to JPEG
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.. code-block:: bash
ocrmypdf --output-type pdfa --pdfa-image-compression jpeg input.pdf output.pdf
Modify a file in place
""""""""""""""""""""""
The file will only be overwritten if OCRmyPDF is successful.
.. code-block:: bash
ocrmypdf myfile.pdf myfile.pdf
Correct page rotation
"""""""""""""""""""""
OCR will attempt to automatic correct the rotation of each page. This can help fix a scanning job that contains a mix of landscape and portrait pages.
.. code-block:: bash
ocrmypdf --rotate-pages myfile.pdf myfile.pdf
You can increase (decrease) the parameter ``--rotate-pages-threshold`` to make page rotation more (less) aggressive.
If the page is "just a little off horizontal", like a crooked picture, then you want ``--deskew``. ``--rotate-pages`` is for when the cardinal angle is wrong.
OCR languages other than English
""""""""""""""""""""""""""""""""
By default OCRmyPDF assumes the document is English.
.. code-block:: bash
ocrmypdf -l fra LeParisien.pdf LeParisien.pdf
ocrmypdf -l eng+fra Bilingual-English-French.pdf Bilingual-English-French.pdf
Language packs must be installed for all languages specified. See :ref:`Installing additional language packs <lang-packs>`.
Produce PDF and text file containing OCR text
"""""""""""""""""""""""""""""""""""""""""""""
This produces a file named "output.pdf" and a companion text file named "output.txt".
.. code-block:: bash
ocrmypdf --sidecar output.txt input.pdf output.pdf
OCR images, not PDFs
--------------------
If you are starting with images, you can just use Tesseract 3.04 or later directly to convert images to PDFs:
.. code-block:: bash
tesseract my-image.jpg output-prefix pdf
.. code-block:: bash
# When there are multiple images
tesseract text-file-containing-list-of-image-filenames.txt output-prefix pdf
Tesseract's PDF output is quite good  OCRmyPDF uses it by internally by default. However, OCRmyPDF has many features not available in Tesseract like like image processing, metadata control, and PDF/A generation.
Use a program like `img2pdf <https://gitlab.mister-muffin.de/josch/img2pdf>`_ to convert your images to PDFs, and then pipe the results to run ocrmypdf. The `-` tells ocrmypdf to read standard input.
.. code-block:: bash
img2pdf my-images*.jpg | ocrmypdf - myfile.pdf
``img2pdf`` is recommended because it does an excellent job at generating PDFs without transcoding images.
For convenience, OCRmyPDF can also convert single images to PDFs on its own. If the resolution (dots per inch, DPI) of an image is not set or is incorrect, it can be overridden with ``--image-dpi``. (As 1 inch is 2.54 cm, 1 dpi = 0.39 dpcm).
.. code-block:: bash
ocrmypdf --image-dpi 300 image.png myfile.pdf
If you have multiple images, you must use ``img2pdf`` to convert the images to PDF.
.. note::
ImageMagick ``convert`` can also convert a group of images to PDF, but in the author's experience it takes a long time, transcodes unnecessarily and gives poor results.
Image processing
----------------
OCRmyPDF perform some image processing on each page of a PDF, if desired. The same processing is applied to each page. It is suggested that the user review files after image processing as these commands might remove desirable content, especially from poor quality scans.
* ``--rotate-pages`` attempts to determine the correct orientation for each page and rotates the page if necessary.
* ``--remove-background`` attempts to detect and remove a noisy background from grayscale or color images. Monochrome images are ignored. This should not be used on documents that contain color photos as it may remove them.
* ``--deskew`` will correct pages were scanned at a skewed angle by rotating them back into place. Skew determination and correction is performed using `Postl's variance of line sums <http://www.leptonica.com/skew-measurement.html>`_ algorithm as implemented in `Leptonica <http://www.leptonica.com/index.html>`_.
* ``--clean`` uses `unpaper <https://www.flameeyes.eu/projects/unpaper>`_ to clean up pages before OCR, but does not alter the final output. This makes it less likely that OCR will try to find text in background noise.
* ``--clean-final`` uses unpaper to clean up pages before OCR and inserts the page into the final output. You will want to review each page to ensure that unpaper did not remove something important.
.. note::
In many cases image processing will rasterize PDF pages as images, potentially losing quality.
.. warning::
``--clean-final`` and ``-remove-background`` may leave undesirable visual artifacts in some images where their algorithms have shortcomings. Files should be visually reviewed after using these options.
OCR and correct document skew (crooked scan)
""""""""""""""""""""""""""""""""""""""""""""
Deskew:
.. code-block:: bash
ocrmypdf --deskew input.pdf output.pdf
Image processing commands can be combined. The order in which options are given does not matter. OCRmyPDF always applies the steps of the image processing pipeline in the same order (rotate, remove background, deskew, clean).
.. code-block:: bash
ocrmypdf --deskew --clean --rotate-pages input.pdf output.pdf
Don't actually OCR my PDF
"""""""""""""""""""""""""
If you set ``--tesseract-timeout 0`` OCRmyPDF will apply its image processing without performing OCR, if all you want to is to apply image processing or PDF/A conversion.
.. code-block:: bash
ocrmypdf --tesseract-timeout=0 --remove-background input.pdf output.pdf
Redo OCR
""""""""
To redo OCR on a file OCRed with other OCR software or a previous version of OCRmyPDF and/or Tesseract, you may use the ``--force-ocr`` argument. Normally, OCRmyPDF does not modify files that already appear to contain OCR text.
.. code-block:: bash
ocrmypdf --force-ocr input.pdf output.pdf
Note that the method above will force rasterization of all pages, potentially reducing quality or losing vector content.
To ensure quality is preserved, one could extract all of the images and rebuild the PDF for a lossless transformation. This recipe does not work when PDFs contain multiple images per page, as many do in practice. It will also lose any page rotation information.
.. code-block:: bash
pdfimages -all old-ocr.pdf prefix # extract all images
img2pdf -o temp.pdf prefix* # construct new PDF from the images
# review the new PDF to ensure it visually matches the old one
ocrmypdf --output-type pdf temp.pdf new-ocr.pdf
``--output-type pdf`` is used here to avoid using Ghostscript which will also rasterize images.
Improving OCR quality
---------------------
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).
+33
View File
@@ -0,0 +1,33 @@
Common error messages
=====================
Page already has text
---------------------
.. code::
ERROR - 1: page already has text! aborting (use --force-ocr to force OCR)
You ran ocrmypdf on a file that already contains printable text or a hidden OCR text layer (it can't quite tell the difference). You probably don't want to do this, because the file is already searchable.
As the error message suggests, your options are:
- ``ocrmypdf --force-ocr`` to :ref:`rasterize <raster-vector>` all vector content and run OCR on the images. This is useful if a previous OCR program failed, or if the document contains a text watermark.
- ``ocrmypdf --skip-text`` to skip OCR and other processing on any pages that contain text. Text pages will be copied into the output PDF without modification.
Input file 'filename' is not a valid PDF
----------------------------------------
OCRmyPDF passes files through qpdf, a program that fixes errors in PDFs, before it tries to work on them. In most cases this happens because the PDF is corrupt and
truncated (incomplete file copying) and not much can be done.
You can try rewriting the file with Ghostscript or pdftk:
- ``gs -o output.pdf -dSAFER -sDEVICE=pdfwrite input.pdf``
- ``pdftk input.pdf cat output output.pdf``
Sometimes Acrobat can repair PDFs with its `Preflight tool <https://helpx.adobe.com/acrobat/using/correcting-problem-areas-preflight-tool.html>`_.
+39
View File
@@ -0,0 +1,39 @@
.. ocrmypdf documentation master file, created by
sphinx-quickstart on Sun Sep 4 14:29:43 2016.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
OCRmyPDF documentation
======================
OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to
be searched.
PDF is the best format for storing and exchanging scanned documents. Unfortunately, PDFs can be difficult to modify. OCRmyPDF makes it easy to apply image processing and OCR to existing PDFs.
.. toctree::
:maxdepth: 1
introduction
release_notes
installation
languages
.. toctree::
:caption: Usage
:maxdepth: 2
cookbook
advanced
batch
security
errors
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+453
View File
@@ -0,0 +1,453 @@
Installation
============
The easiest way to install OCRmyPDF to follow the steps for your operating system/platform.
If you want to use the latest version of OCRmyPDF, your best bet is to install the most recent version your platform provides, and then upgrade that version by installing the Python binary wheels.
.. contents:: Platform-specific steps
:depth: 1
:local:
Installing on Debian and Ubuntu 16.10 or newer
----------------------------------------------
Users of Debian 9 ("stretch") or later or Ubuntu 16.10 or later may simply
.. code-block:: bash
apt-get install ocrmypdf
To see what versions are available, check the `Debian Package Tracker <https://tracker.debian.org/pkg/ocrmypdf>`_ or `Ubuntu launchpad.net <https://launchpad.net/ocrmypdf>`_.
Installing on macOS with Homebrew
---------------------------------
.. image:: https://img.shields.io/homebrew/v/ocrmypdf.svg
:alt: homebrew
:target: http://brewformulas.org/Ocrmypdf
OCRmyPDF is now a standard `Homebrew <https://brew.sh>`_ formula. To install on macOS:
.. code-block:: bash
brew install ocrmypdf
.. note::
Users who previously installed OCRmyPDF on macOS using ``pip install ocrmypdf`` should remove the pip version (``pip3 uninstall ocrmypdf``) before switching to the Homebrew version.
.. note::
Users who previously installed OCRmyPDF from the private tap should switch to the mainline version (``brew untap jbarlow83/ocrmypdf``) and install from there.
.. _Docker-install:
Installing the Docker image
---------------------------
For many users, installing the Docker image will be easier than installing all of OCRmyPDF's dependencies. For Windows, it is the only option.
If you have `Docker <https://docs.docker.com/>`_ installed on your system, you can install a Docker image of the latest release.
Follow the Docker installation instructions for your platform. If you can run this command successfully, your system is ready to download and execute the image:
.. code-block:: bash
docker run hello-world
OCRmyPDF will use all available CPU cores. By default, the VirtualBox machine instance on Windows and macOS has only a single CPU core enabled. Use the VirtualBox Manager to determine the name of your Docker engine host, and then follow these optional steps to enable multiple CPUs:
.. code-block:: bash
# Optional step for Mac OS X users
docker-machine stop "yourVM"
VBoxManage modifyvm "yourVM" --cpus 2 # or whatever number of core is desired
docker-machine start "yourVM"
eval $(docker-machine env "yourVM")
Assuming you have a Docker engine running, you can download one of the three available images:
.. list-table::
:widths: auto
:header-rows: 1
* - Image name
- Download command
- Notes
* - ocrmypdf
- ``docker pull jbarlow83/ocrmypdf``
- Latest ocrmypdf with Tesseract 4.0.0-beta1 on Ubuntu 18.04. Includes English, French, German, Spanish, Portugeuse and Simplified Chinese.
* - ocrmypdf-polyglot
- ``docker pull jbarlow83/ocrmypdf-polyglot``
- As above, with all available language packs.
For example:
.. code-block:: bash
docker pull jbarlow83/ocrmypdf
Then tag it to give a more convenient name, just ocrmypdf:
.. code-block:: bash
docker tag jbarlow83/ocrmypdf ocrmypdf
.. _docker-polyglot:
The alternative "polyglot" image provides `all available language packs <https://github.com/tesseract-ocr/tesseract/blob/master/doc/tesseract.1.asc#languages>`_.
You can then run ocrmypdf using the command:
.. code-block:: bash
docker run --rm ocrmypdf --help
To execute the OCRmyPDF on a local file, you must `provide a writable volume to the Docker image <https://docs.docker.com/userguide/dockervolumes/>`_, and both the input and output file must be inside the writable volume. This example command uses the current working directory as the writable volume:
.. code-block:: bash
docker run --rm -v "$(pwd):/home/docker" <other docker arguments> ocrmypdf <your arguments to ocrmypdf>
In this worked example, the current working directory contains an input file called ``test.pdf`` and the output will go to ``output.pdf``:
.. code-block:: bash
docker run --rm -v "$(pwd):/home/docker" ocrmypdf --skip-text test.pdf output.pdf
.. note:: The working directory should be a writable local volume or Docker may not have permission to access it.
Note that ``ocrmypdf`` has its own separate ``-v VERBOSITYLEVEL`` argument to control debug verbosity. All Docker arguments should before the ``ocrmypdf`` image name and all arguments to ``ocrmypdf`` should be listed after.
In some environments the permissions associated with Docker can be complex to configure. The process that executes Docker may end up not having the permissions to write the specified file system. In that case one can stream the file into and out of the Docker process and avoid all permission hassles, using ``-`` as the input and output filename:
.. code-block:: bash
docker run --rm -i ocrmypdf <other arguments to ocrmypdf> - - <input.pdf >output.pdf
For convenience, a shell alias can hide the docker command:
.. code-block:: bash
alias ocrmypdf='docker run --rm -v "$(pwd):/home/docker" ocrmypdf'
ocrmypdf --version # runs docker version
Or in the wonderful `fish shell <https://fishshell.com/>`_:
.. code-block:: fish
alias ocrmypdf 'docker run --rm -v (pwd):/home/docker ocrmypdf'
funcsave ocrmypdf
.. note::
The ocrmypdf Docker containers are designed to be used for a single OCR job. The ``docker run --rm`` argument tells Docker to delete temporary storage associated with container when it is done executing.
Manual installation on macOS
----------------------------
These instructions probably work on all macOS supported by Homebrew.
If it's not already present, `install Homebrew <http://brew.sh/>`_.
Update Homebrew:
.. code-block:: bash
brew update
Install or upgrade the required Homebrew packages, if any are missing:
.. code-block:: bash
brew install libpng openjpeg jbig2dec libtiff # image libraries
brew install qpdf
brew install ghostscript
brew install python3
brew install libxml2 libffi leptonica
brew install unpaper # optional
Python 3.5 and 3.6 are supported.
Install the required Tesseract OCR engine with the language packs you plan to use:
.. code-block:: bash
brew install tesseract # Option 1: for English, French, German, Spanish
.. _macos-all-languages:
.. code-block:: bash
brew install tesseract --with-all-languages # Option 2: for all language packs
Update the homebrew pip:
.. code-block:: bash
pip3 install --upgrade pip
You can then install OCRmyPDF from PyPI, for the current user:
.. code-block:: bash
pip3 install --user ocrmypdf[fitz]
or system-wide:
.. code-block:: bash
pip3 install ocrmypdf[fitz]
``[fitz]`` includes the optional dependency on PyMuPDF, which improves OCRmyPDF's output in many cases.
The command line program should now be available:
.. code-block:: bash
ocrmypdf --help
Installing the latest version on Ubuntu 18.04 LTS
-------------------------------------------------
Ubuntu 18.04 includes ocrmypdf 6.1.2. To install a more recent version, first
install the system version to get all the dependencies:
.. code-block:: bash
sudo apt-get update
sudo apt-get install \
ocrmypdf \
python3-pip
Then install ocrmypdf 6.1.5 for the local user and set the user's ``PATH`` to check for the user's Python packages.
.. code-block:: bash
export PATH=$HOME/.local/bin:$PATH
pip3 install --user ocrmypdf[fitz]
Installing on Ubuntu 16.04 LTS
------------------------------
No package is currently available for Ubuntu 16.04, but you can install the dependencies manually:
.. code-block:: bash
sudo apt-get update
sudo apt-get install \
unpaper \
ghostscript \
tesseract-ocr \
qpdf \
python3-pip \
python3-cffi
If you wish install OCRmyPDF for the current user:
.. code-block:: bash
pip3 install --user ocrmypdf[fitz]
Alternately, system-wide. Note that this may modify the system Python environment:
.. code-block:: bash
sudo pip3 install ocrmypdf[fitz]
If you wish to install OCRmyPDF to a virtual environment to isolate the system Python, you can follow these steps.
.. code-block:: bash
python3 -m venv venv-ocrmypdf
source venv-ocrmypdf/bin/activate
pip3 install ocrmypdf[fitz]
``[fitz]`` includes the optional dependency on PyMuPDF, which improves OCRmyPDF's output in many cases.
Installing on Ubuntu 14.04 LTS
------------------------------
Installing on Ubuntu 14.04 LTS (trusty) is more difficult than some other options, because it is older and does not provide ``pip``.
Update apt-get:
.. code-block:: bash
sudo apt-get update
Install system dependencies:
.. code-block:: bash
sudo apt-get install \
software-properties-common python-software-properties \
zlib1g-dev \
libjpeg-dev \
libffi-dev \
qpdf
We will need backports of Ghostscript 9.16, libav-11 (for unpaper 6.1), Tesseract 4.00 (alpha), and Python 3.6. This will replace Ghostscript and Tesseract 3.x on your system. Python 3.6 will be installed alongside the system Python 3.
If you prefer to not modify your system in this matter, consider using a Docker container.
.. code-block:: bash
sudo add-apt-repository ppa:vshn/ghostscript -y
sudo add-apt-repository ppa:heyarje/libav-11 -y
sudo add-apt-repository ppa:alex-p/tesseract-ocr -y
sudo add-apt-repository ppa:jonathonf/python-3.6 -y
sudo apt-get update
sudo apt-get install \
python3.6-dev \
ghostscript \
tesseract-ocr \
tesseract-ocr-eng \
libavformat56 libavcodec56 libavutil54 \
wget
Now we need to install ``pip`` and let it install ocrmypdf:
.. code-block:: bash
curl https://bootstrap.pypa.io/ez_setup.py -o - | python3.6 && python3.6 -m easy_install pip
pip3.6 install ocrmypdf[fitz]
The ``wget`` command will download a program and run it.
These installation instructions omit the optional dependency ``unpaper``, which is only available at version 0.4.2 in Ubuntu 14.04. The author could not find a backport of ``unpaper``, and created a .deb package to do the job of installing unpaper 6.1 (for x86 64-bit only):
.. code-block:: bash
wget -q 'https://www.dropbox.com/s/vaq0kbwi6e6au80/unpaper_6.1-1.deb?raw=1' -O unpaper_6.1-1.deb
sudo dpkg -i unpaper_6.1-1.deb
Installing on ArchLinux
-----------------------
The author is aware of an `ArchLinux package for ocrmypdf <https://aur.archlinux.org/packages/ocrmypdf/>`_. It seems like the following command might work.
.. code-block:: bash
pacman -S ocrmypdf
Installing on Windows
---------------------
Direct installation on Windows is not possible. Install the _`Docker` container as described above. Ensure that your command prompt can run the docker "hello world" container.
It would probably not be too difficult to run on Windows. The main reason this has been avoided is the difficulty of packaging and installing the various non-Python dependencies: Tesseract, QPDF, Ghostscript, Leptonica. Pull requests to add or improve Windows support would be quite welcome.
Running on Windows
~~~~~~~~~~~~~~~~~~
The command line syntax to run ocrmypdf from a command prompt will resemble:
.. code-block:: bat
docker run -v /c/Users/sampleuser:/home/docker ocrmypdf --skip-text test.pdf output.pdf
where /c/Users/sampleuser is a Unix representation of the Windows path C:\\Users\\sampleuser, assuming a user named "sampleuser" is running ocrmypdf on a file in their home directory, and the files "test.pdf" and "output.pdf" are in the sampleuser folder. The Windows user must have read and write permissions.
`Bash on Ubuntu on Windows <https://github.com/Microsoft/BashOnWindows>`_ should also be a viable route for running the OCRmyPDF Docker container.
Installing with Python pip
--------------------------
First, install `your platform's version <https://repology.org/metapackage/ocrmypdf/versions>`_ of ``ocrmypdf``, if available, as a way of ensuring that external dependencies are (mostly) satisified, even though the platform version may be out of date. Use ``ocrmypdf --version`` to confirm what version was installed.
Then you can install the latest OCRmyPDF from the Python wheels. First try:
.. code-block:: bash
pip3 install --user ocrmypdf
You should then be able to run ``ocrmypdf --version`` and see that the latest version was located.
Since ``pip3 install --user`` does not work correctly on some platforms, notably Ubuntu 16.04 and older, and the Homebrew version of Python, instead use this for a system wide installation:
.. code-block:: bash
pip3 install ocrmypdf
Requirements for pip and HEAD install
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OCRmyPDF currently requires these external programs to be installed:
- Python 3.5 or newer
- Tesseract 3.04 or newer
- Ghostscript 9.15 or newer
- qpdf 7.0.0 or newer
The following dependencies are recommended:
- Python 3.6
- Tesseract 4.00 or newer
- Ghostscript 9.22 or newer
- qpdf 8.0.2 or newer
- unpaper 6.1
- PyMuPDF 1.12.5 or newer
These are in addition to the Python packaging dependencies, meaning that unfortunately, the ``pip install`` command cannot satisfy all of them.
Python 3.6 and Tesseract 4.0.0-beta.1 are recommended for best OCR results and best performance.
The library PyMuPDF is not widely available in platform distributions, and it improves OCRmyPDF in certain conditions. Consider installing OCRmyPDF from the Python binary wheels, which include a precompiled version of this library.
Installing HEAD revision from sources
-------------------------------------
If you have ``git`` and Python 3.5 or newer installed, you can install from source. When the ``pip`` installer runs, it will alert you if dependencies are missing.
To install the HEAD revision from sources in the current Python 3 environment:
.. code-block:: bash
pip3 install git+https://github.com/jbarlow83/OCRmyPDF.git
Or, to install in `development mode <https://pythonhosted.org/setuptools/setuptools.html#development-mode>`_, allowing customization of OCRmyPDF, use the ``-e`` flag:
.. code-block:: bash
pip3 install -e git+https://github.com/jbarlow83/OCRmyPDF.git
You may find it easiest to install in a virtual environment, rather than system-wide:
.. code-block:: bash
git clone -b master https://github.com/jbarlow83/OCRmyPDF.git
python3 -m venv
source venv/bin/activate
cd OCRmyPDF
pip3 install .
However, ``ocrmypdf`` will only be accessible on the system PATH after
you activate the virtual environment.
To run the program:
.. code-block:: bash
ocrmypdf --help
If not yet installed, the script will notify you about dependencies that
need to be installed. The script requires specific versions of the
dependencies. Older version than the ones mentioned in the release notes
are likely not to be compatible to OCRmyPDF.
Other Linux packages
--------------------
See the `Repology <https://repology.org/metapackage/ocrmypdf/versions>`_ page.
+115
View File
@@ -0,0 +1,115 @@
Introduction
============
OCRmyPDF is a Python 3 package that adds OCR layers to PDFs.
About OCR
---------
`Optical character recognition <https://en.wikipedia.org/wiki/Optical_character_recognition>`_ is technology that converts images of typed or handwritten text, such as in a scanned document, to computer text that can be searched and copied.
OCRmyPDF uses `Tesseract <https://github.com/tesseract-ocr/tesseract>`_, the best available open source OCR engine, to perform OCR.
.. _raster-vector:
About PDFs
----------
PDFs are page description files that attempts to preserve a layout exactly. They contain `vector graphics <http://vector-conversions.com/vectorizing/raster_vs_vector.html>`_ that can contain raster objects such as scanned images. Because PDFs can contain multiple pages (unlike many image formats) and can contain fonts and text, it is a good formats for exchanging scanned documents.
.. image:: bitmap_vs_svg.svg
A PDF page might contain multiple images, even if it only appears to have one image. Some scanners or scanning software will segment pages into monochromatic text and color regions for example, to improve the compression ratio and appearance of the page.
Rasterizing a PDF is the process of generating an image suitable for display or analyzing with an OCR engine. OCR engines like Tesseract work with images, not vector objects.
About PDF/A
-----------
`PDF/A <https://en.wikipedia.org/wiki/PDF/A>`_ is an ISO-standardized subset of the full PDF specification that is designed for archiving (the 'A' stands for Archive). PDF/A differs from PDF primarily by omitting features that would make it difficult to read the file in the future, such as embedded Javascript, video, audio and references to external fonts. All fonts and resources needed to interpret the PDF must be contained within it. Because PDF/A disables Javascript and other types of embedded content, it is probably more secure.
There are various conformance levels and versions, such as "PDF/A-2b".
Generally speaking, the best format for scanned documents is PDF/A. Some governments and jurisdictions, US Courts in particular, `mandate the use of PDF/A <https://pdfblog.com/2012/02/13/what-is-pdfa/>`_ for scanned documents.
Since most people who scan documents are interested in reading them indefinitely into the future, OCRmyPDF generates PDF/A-2b by default.
PDF/A has a few drawbacks. Some PDF viewers include an alert that the file is a PDF/A, which may confuse some users. It also tends to produce larger files than PDF, because it embeds certain resources even if they are commonly available. PDF/A files can be digitally signed, but may not be encrypted, to ensure they can be read in the future. Fortunately, converting from PDF/A to a regular PDF is trivial, and any PDF viewer can view PDF/A.
What OCRmyPDF does
------------------
OCRmyPDF analyzes each page of a PDF to determine the colorspace and resolution (DPI) needed to capture all of the information on that page without losing content. It uses `Ghostscript <http://ghostscript.com/>`_ to rasterize the page, and then performs on OCR on the rasterized image to create an OCR "layer". The layer is then grafted back onto the original PDF.
While one can use a program like Ghostscript or ImageMagick to get an image and put the image through Tesseract, that actually creates a new PDF and many details may be lost. OCRmyPDF can produce a minimally changed PDF as output.
OCRmyPDF also some image processing options like deskew which improve the appearance of files and quality of OCR. When these are used, the OCR layer is grafted onto the processed image instead.
By default, OCRmyPDF produces archival PDFs PDF/A, which are a stricter subset of PDF features designed for long term archives. If regular PDFs are desired, this can be disabled with ``--output-type pdf``.
Why you shouldn't do this manually
----------------------------------
A PDF is similar to an HTML file, in that it contains document structure along with images. Sometimes a PDF does nothing more than present a full page image, but often there is additional content that would be lost.
A manual process could work like either of these:
1. Rasterize each page as an image, OCR the images, and combine the output into a PDF. This preserves the layout of each page, but resamples all images (possibly losing quality, increasing file size, introducing compression artifacts, etc.).
2. Extract each image, OCR, and combine the output into a PDF. This loses the context in which images are used in the PDF, meaning that cropping, rotation and scaling of pages may be lost. Some scanned PDFs use multiple images segmented into black and white, grayscale and color regions, with stencil masks to prevent overlap, as this can enhance the appearance of a file while reducing file size. Clearly, reassembling these images will be easy. This also loses and text or vector art on any pages in a PDF with both scanned and pure digital content.
In the case of a PDF that is nothing other than a container of images (no rotation, scaling, cropping, one image per page), the second approach can be lossless.
OCRmyPDF uses several strategies depending on input options and the input PDF itself, but generally speaking it rasterizes a page for OCR and then grafts the OCR back onto the original. As such it can handle complex PDFs and still preserve their contents as much as possible.
Limitations
-----------
OCRmyPDF is limited by the Tesseract OCR engine. As such it experiences these limitations, as do any other programs that rely on Tesseract:
* The OCR is not as accurate as commercial solutions such as Abbyy.
* It is not capable of recognizing handwriting.
* It may find gibberish and report this as OCR output.
* If a document contains languages outside of those given in the ``-l LANG`` arguments, results may be poor.
* It is not always good at analyzing the natural reading order of documents. For example, it may fail to recognize that a document contains two columns and join text across the columns.
* Poor quality scans may produce poor quality OCR. Garbage in, garbage out.
* PDFs that use transparent layers are not currently checked in the test suite, so they may not work correctly.
* It does not expose information about what font family text belongs to.
OCRmyPDF is also limited by the PDF specification:
* PDF encodes the position of text glyphs but does not encode document structure. There is no markup that divides a document in sections, paragraphs, sentences, or even words (since blank spaces are not represented). As such all elements of document structure including the spaces between words must be derived heuristically. Some PDF viewers do a better job of this than others.
* Because some popular open source PDF viewers have a particularly hard time with spaces betweem words, OCRmyPDF appends a space to each text element as a workaround. While this mixes document structure with graphical information that ideally should be left to the PDF viewer to interpret, it improves compatibility with some viewers and does not cause problems for better ones.
Ghostscript also imposes some limitations:
* PDFs containing JBIG2-encoded content will be converted to CCITT Group4 encoding, which has lower compression ratios, if Ghostscript PDF/A is enabled.
* PDFs containing JPEG 2000-encoded content will be converted to JPEG encoding, which may introduce compression artifacts, if Ghostscript PDF/A is enabled.
* Ghostscript may transcode grayscale and color images, either lossy to lossless or lossless to lossy, based on an internal algorithm. This behavior can be suppressed by setting ``--pdfa-image-compression`` to ``jpeg`` or ``lossless`` to set all images to one type or the other. Ghostscript has no option to maintain the input image's format.
OCRmyPDF is currently not designed to be used as a Python API; it is designed to be run as a command line tool. ``import ocrmypf`` currently attempts to process the command line on ``sys.argv`` at import time so it has side effects that will interfere with its use as a package. The API it presents should not be considered stable.
Similar programs
----------------
To the author's knowledge, OCRmyPDF is the most feature-rich and thoroughly tested command line OCR PDF conversion tool. If it does not meet your needs, contributions and suggestions are welcome. If not, consider one of these similar open source programs:
* pdf2pdfocr
* pdfsandwich
* pypdfocr
* pdfbeads
Web front-ends
--------------
* `Nextcloud OCR <https://github.com/janis91/ocr>`_ is a free software plugin for the Nextcloud private cloud software
* `OCRmyPDF-web <https://github.com/sseemayer/OCRmyPDF-web>`_, a micro web-frontend for OCRmyPDF (third-party, not actively maintained)
Bear in mind that OCRmyPDF is not designed to be secure against malware-bearing PDFs (see `Using OCRmyPDF online`_).
+61
View File
@@ -0,0 +1,61 @@
.. _lang-packs:
Installing additional language packs
====================================
OCRmyPDF uses Tesseract for OCR, and relies on its language packs for languages other than English.
Tesseract supports `most languages <https://github.com/tesseract-ocr/tesseract/blob/master/doc/tesseract.1.asc#languages>`_.
For Linux users, you can often find packages that provide language packs:
Debian and Ubuntu users
-----------------------
.. code-block:: bash
# Display a list of all Tesseract language packs
apt-cache search tesseract-ocr
# Debian/Ubuntu users
apt-get install tesseract-ocr-chi-sim # Example: Install Chinese Simplified language back
You can then pass the ``-l LANG`` argument to OCRmyPDF to give a hint as to what languages it should search for. Multiple
languages can be requested using either ``-l eng+fre`` (English and French) or ``-l eng -l fre``.
macOS users
-----------
You can install additional language packs by :ref:`installing Tesseract using Homebrew with all language packs <macos-all-languages>`.
Docker users
------------
Users of the Docker image may use the alternative :ref:`"polyglot" container <docker-polyglot>` which includes all languages.
Adding individual language packs to a Docker image
""""""""""""""""""""""""""""""""""""""""""""""""""
If you wish to add a single language pack, you could do the following:
* Download the desired ``.trainedata`` file from the `tessdata <https://github.com/tesseract-ocr/tessdata>`_ repository. Let's use Hebrew in this example (``heb.traineddata``)
* Copy the file to ``/home/user/downloads/heb.traineddata``.
* Create a new container based on the ocrmypdf-tess4 image and jump into it with a terminal:
.. code-block:: bash
host$ docker run -v /home/user/downloads:/home/docker -it --entrypoint /bin/bash ocrmypdf-tess4
* Put the file where Tesseract expects it:
.. code-block:: bash
docker$ cp /home/docker/heb.traineddata /usr/share/tesseract-ocr/tessdata
* Note the container id, and save it as a new image (in this example, ``ocrmypdf-tess4-heb``)
.. code-block:: bash
host$ docker commit <container_id> ocrmypdf-tess4-heb
+392
View File
@@ -0,0 +1,392 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: Pipeline: Pages: 1 -->
<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,-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,-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="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="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="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="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="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="node18" class="node">
<title>t17</title>
<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="edge25" class="edge">
<title>t1&#45;&gt;t17</title>
<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="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="edge28" class="edge">
<title>t1&#45;&gt;t18</title>
<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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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>
<!-- t15 -->
<g id="node14" class="node">
<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;t15 -->
<g id="edge14" class="edge">
<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="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="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>
<!-- t19 -->
<g id="node22" class="node">
<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;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;t16 -->
<g id="edge22" class="edge">
<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>
<!-- t15&#45;&gt;t16 -->
<g id="edge21" class="edge">
<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>
<!-- 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="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>
<!-- t13&#45;&gt;t16 -->
<g id="edge23" class="edge">
<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>
<!-- 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>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="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>
<!-- 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>

After

Width:  |  Height:  |  Size: 25 KiB

+864
View File
@@ -0,0 +1,864 @@
Release notes
=============
OCRmyPDF uses `semantic versioning <http://semver.org/>`_ for its command line interface.
The OCRmyPDF package itself does not contain a public API, although it is fairly stable and breaking changes are usually timed with a major release. A future release will clearly define the stable public API.
.. Issue regex
find: [^`]\#([0-9]{1,3})[^0-9]
replace: `#$1 <https://github.com/jbarlow83/OCRmyPDF/issues/$1>`_
v7
--
- The core algorithm for combining OCR layers with existing PDF pages has been rewritten and improved considerably. It works directly with the input file to minimize changes. The new algorithm uses less temporary disk space and eliminates cost object duplication and deduplication that used to be a requirement.
- New feature: PDF optimization with ``-O`` or ``--optimize``. After OCR, OCRmyPDF will automatically performs image optimizations relevant to OCR PDFs.
+ If a JBIG2 encoder is available, then monochrome images will be converted, with the potential for huge savings on large black and white files, since JBIG2 is far more efficient than any other monochrome format. (All known US patents related to JBIG2 have probably expired, but it remains the responsibility of the user to supply a JBIG2 encoder such as `jbig2enc <https://github.com/agl/jbig2enc>`_. OCRmyPDF does not contain any portion of JBIG2.)
+ If ``pngquant`` is installed, OCRmyPDF will use to perform lossy compression of PNG images.
+ The quality of JPEGs can also be lowered, on the assumption that a lower quality image may be suitable for storage after OCR.
+ It is likely this component will be offered as a separate command line utility.
- New dependency: `pikepdf <https://github.com/pikepdf>`_. pikepdf is a powerful new Python PDF library driving the latest OCRmyPDF features, built on the proven and mature libqpdf C++ library.
- Small amounts of text in the margins of a page, such as watermarks, page numbers, or digital stamps, will no longer prevent the rest of a page from being OCRed when ``--skip-text`` is issued. This behavior is based on a heuristic.
- Removed features
+ The deprecated ``tesseract`` PDF renderer was removed, because due to changes in how we construct PDFs.
+ ``-g``, the option to generate debug text pages, was removed because the extra pages it generates were too much of a maintenance burden and it never worked with the ``sandwich`` renderer (Tesseract) anyway. HOCR pages can still be previewed by running the hocrtransform.py with appropriate settings.
- The ``sandwich`` PDF renderer can be used with all supported versions of Tesseract, including that those priority to v3.05 which don't support ``-c textonly``
- ``--pdf-renderer auto`` and the diagnostics regarding the selection of PDF renderer are now more consistent.
v6.2.0
------
- **Docker**: The Docker image ``ocrmypdf-tess4`` has been removed. The main Docker images, ``ocrmypdf`` and ``ocrmypdf-polyglot`` now use Ubuntu 18.04 as a base image, and as such Tesseract 4.0.0-beta1 is now the Tesseract version they use. There is no Docker image based on Tesseract 3.05 anymore.
- Creation of PDF/A-3 is now supported. However, there is no ability to attach files to PDF/A-3.
- List more reasons why the file size might grow.
- Fix issue `#262 <https://github.com/ppjbarlow83/OCRmyPDF/issues/262>`_, ``--remove-background`` error on PDFs contained colormapped (paletted) images.
- Fix another XMP metadata validation issue, in cases where the input file's creation date has no timezone and the creation date is not overridden.
v6.1.5
------
- Fix issue `#253 <https://github.com/jbarlow83/OCRmyPDF/issues/253>`_, a possible division by zero when using the ``hocr`` renderer.
- Fix incorrectly formatted ``<xmp:ModifyDate>`` field inside XMP metadata for PDF/As. veraPDF flags this as a PDF/A validation failure. The error is caused the timezone and final digit of the seconds of modified time to be omitted, so at worst the modification time stamp is rounded to the nearest 10 seconds.
v6.1.4
------
- Fix issue `#248 <https://github.com/jbarlow83/OCRmyPDF/issues/248>`_ ``--clean`` argument may remove OCR from left column of text on certain documents. We now set ``--layout none`` to suppress this.
- The test cache was updated to reflect the change above.
- Change test suite to accommodate Ghostscript 9.23's new ability to insert JPEGs into PDFs without transcoding.
- XMP metadata in PDFs is now examined using ``defusedxml`` for safety.
- If an external process exits with a signal when asked to report its version, we now print the system error message instead of suppressing it. This occurred when the required executable was found but was missing a shared library.
- qpdf 7.0.0 or newer is now required as the test suite can no longer pass without it.
Notes
~~~~~
- An apparent `regression in Ghostscript 9.23 <https://bugs.ghostscript.com/show_bug.cgi?id=699216>`_ will cause some ocrmypdf output files to become invalid in rare cases; the workaround for the moment is to set ``--force-ocr``.
v6.1.3
------
- Fix issue `#247 <https://github.com/jbarlow83/OCRmyPDF/issues/247>`_, ``/CreationDate`` metadata not copied from input to output.
- A warning is now issued when Python 3.5 is used on files with a large page count, as this case is known to regress to single core performance. The cause of this problem is unknown.
v6.1.2
------
- Upgrade to PyMuPDF v1.12.5 which includes a more complete fix to `#239 <https://github.com/jbarlow83/OCRmyPDF/issues/239>`_.
- Add ``defusedxml`` dependency.
v6.1.1
------
- Fix text being reported as found on all pages if PyMuPDF is not installed.
v6.1.0
------
- PyMuPDF is now an optional but recommended dependency, to alleviate installation difficulties on platforms that have less access to PyMuPDF than the author anticipated. Install OCRmyPDF with ``pip install ocrmypdf[fitz]`` to use it to its full potential.
- Fix ``FileExistsError`` that could occur if OCR timed out while it was generating the output file. (`#218 <https://github.com/jbarlow83/OCRmyPDF/issues/218>`_)
- Fix table of contents/bookmarks all being redirected to page 1 when generating a PDF/A (with PyMuPDF). (Without PyMuPDF the table of contents is removed in PDF/A mode.)
- Fix "RuntimeError: invalid key in dict" when table of contents/bookmarks titles contained the character ``)``. (`#239 <https://github.com/jbarlow83/OCRmyPDF/issues/239>`_)
- Added a new argument ``--skip-repair`` to skip the initial PDF repair step if the PDF is already well-formed (because another program repaired it).
v6.0.0
------
- The software license has been changed to GPLv3. Test resource files and some individual sources may have other licenses.
- OCRmyPDF now depends on `PyMuPDF <https://pymupdf.readthedocs.io/en/latest/installation/>`_. Including PyMuPDF is the primary reason for the change to GPLv3.
- Other backward incompatible changes
+ The ``OCRMYPDF_TESSERACT``, ``OCRMYPDF_QPDF``, ``OCRMYPDF_GS`` and ``OCRMYPDF_UNPAPER`` environment variables are no longer used. Change ``PATH`` if you need to override the external programs OCRmyPDF uses.
+ The ``ocrmypdf`` package has been moved to ``src/ocrmypdf`` to avoid issues with accidental import.
+ The function ``ocrmypdf.exec.get_program`` was removed.
+ The deprecated module ``ocrmypdf.pageinfo`` was removed.
+ The ``--pdf-renderer tess4`` alias for ``sandwich`` was removed.
- Fixed an issue where OCRmyPDF failed to detect existing text on pages, depending on how the text and fonts were encoded within the PDF. (`#233 <https://github.com/jbarlow83/OCRmyPDF/issues/233>`_, `#232 <https://github.com/jbarlow83/OCRmyPDF/issues/232>`_)
- Fixed an issue that caused dramatic inflation of file sizes when ``--skip-text --output-type pdf`` was used. OCRmyPDF now removes duplicate resources such as fonts, images and other objects that it generates. (`#237 <https://github.com/jbarlow83/OCRmyPDF/issues/237>`_)
- Improved performance of the initial page splitting step. Originally this step was not believed to be expensive and ran in a process. Large file testing revealed it to be a bottleneck, so it is now parallelized. On a 700 page file with quad core machine, this change saves about 2 minutes. (`#234 <https://github.com/jbarlow83/OCRmyPDF/issues/234>`_)
- The test suite now includes a cache that can be used to speed up test runs across platforms. This also does not require computing checksums, so it's faster. (`#217 <https://github.com/jbarlow83/OCRmyPDF/issues/217>`_)
v5.7.0
------
- Fixed an issue that caused poor CPU utilization on machines more than 4 cores when running Tesseract 4. (Related to issue `#217 <https://github.com/jbarlow83/OCRmyPDF/issues/217>`_.)
- The 'hocr' renderer has been improved. The 'sandwich' and 'tesseract' renderers are still better for most use cases, but 'hocr' may be useful for people who work with the PDF.js renderer in English/ASCII languages. (`#225 <https://github.com/jbarlow83/OCRmyPDF/issues/225>`_)
+ It now formats text in a matter that is easier for certain PDF viewers to select and extract copy and paste text. This should help macOS Preview and PDF.js in particular.
+ The appearance of selected text and behavior of selecting text is improved.
+ The PDF content stream now uses relative moves, making it more compact and easier for viewers to determine when two words on the same line.
+ It can now deal with text on a skewed baseline.
+ Thanks to @cforcey for the pull request, @jbreiden for many helpful suggestions, @ctbarbour for another round of improvements, and @acaloiaro for an independent review.
v5.6.3
------
- Suppress two debug messages that were too verbose
v5.6.2
------
- Development branch accidentally tagged as release. Do not use.
v5.6.1
------
- Fix issue `#219 <https://github.com/jbarlow83/OCRmyPDF/issues/219>`_: change how the final output file is created to avoid triggering permission errors when the output is a special file such as ``/dev/null``
- Fix test suite failures due to a qpdf 8.0.0 regression and Python 3.5's handling of symlink
- The "encrypted PDF" error message was different depending on the type of PDF encryption. Now a single clear message appears for all types of PDF encryption.
- ocrmypdf is now in Homebrew. Homebrew users are advised to the version of ocrmypdf in the official homebrew-core formulas rather than the private tap.
- Some linting
v5.6.0
------
- Fix issue `#216 <https://github.com/jbarlow83/OCRmyPDF/issues/216>`_: preserve "text as curves" PDFs without rasterizing file
- Related to the above, messages about rasterizing are more consistent
- For consistency versions minor releases will now get the trailing .0 they always should have had.
v5.5
----
- Add new argument ``--max-image-mpixels``. Pillow 5.0 now raises an exception when images may be decompression bombs. This argument can be used to override the limit Pillow sets.
- Fix output page cropped when using the sandwich renderer and OCR is skipped on a rotated and image-processed page
- A warning is now issued when old versions of Ghostscript are used in cases known to cause issues with non-Latin characters
- Fix a few parameter validation checks for ``-output-type pdfa-1`` and ``pdfa-2``
v5.4.4
------
- Fix issue `#181 <https://github.com/jbarlow83/OCRmyPDF/issues/181>`_: fix final merge failure for PDFs with more pages than the system file handle limit (``ulimit -n``)
- Fix issue `#200 <https://github.com/jbarlow83/OCRmyPDF/issues/200>`_: an uncommon syntax for formatting decimal numbers in a PDF would cause qpdf to issue a warning, which ocrmypdf treated as an error. Now this the warning is relayed.
- Fix an issue where intermediate PDFs would be created at version 1.3 instead of the version of the original file. It's possible but unlikely this had side effects.
- A warning is now issued when older versions of qpdf are used since issues like `#200 <https://github.com/jbarlow83/OCRmyPDF/issues/200>`_ cause qpdf to infinite-loop
- Address issue `#140 <https://github.com/jbarlow83/OCRmyPDF/issues/140>`_: if Tesseract outputs invalid UTF-8, escape it and print its message instead of aborting with a Unicode error
- Adding previously unlisted setup requirement, pytest-runner
- Update documentation: fix an error in the example script for Synology with Docker images, improved security guidance, advised ``pip install --user``
v5.4.3
------
- If a subprocess fails to report its version when queried, exit cleanly with an error instead of throwing an exception
- Added test to confirm that the system locale is Unicode-aware and fail early if it's not
- Clarified some copyright information
- Updated pinned requirements.txt so the homebrew formula captures more recent versions
v5.4.2
------
- Fixed a regression from v5.4.1 that caused sidecar files to be created as empty files
v5.4.1
------
- Add workaround for Tesseract v4.00alpha crash when trying to obtain orientation and the latest language packs are installed
v5.4
----
- Change wording of a deprecation warning to improve clarity
- Added option to generate PDF/A-1b output if desired (``--output-type pdfa-1``); default remains PDF/A-2b generation
- Update documentation
v5.3.3
------
- Fixed missing error message that should occur when trying to force ``--pdf-renderer sandwich`` on old versions of Tesseract
- Update copyright information in test files
- Set system ``LANG`` to UTF-8 in Dockerfiles to avoid UTF-8 encoding errors
v5.3.2
------
- Fixed a broken test case related to language packs
v5.3.1
------
- Fixed wrong return code given for missing Tesseract language packs
- Fixed "brew audit" crashing on Travis when trying to auto-brew
v5.3
----
- Added ``--user-words`` and ``--user-patterns`` arguments which are forwarded to Tesseract OCR as words and regular expressions respective to use to guide OCR. Supplying a list of subject-domain words should assist Tesseract with resolving words. (`#165 <https://github.com/jbarlow83/OCRmyPDF/issues/165>`_)
- Using a non Latin-1 language with the "hocr" renderer now warns about possible OCR quality and recommends workarounds (`#176 <https://github.com/jbarlow83/OCRmyPDF/issues/176>`_)
- Output file path added to error message when that location is not writable (`#175 <https://github.com/jbarlow83/OCRmyPDF/issues/175>`_)
- Otherwise valid PDFs with leading whitespace at the beginning of the file are now accepted
v5.2
----
- When using Tesseract 3.05.01 or newer, OCRmyPDF will select the "sandwich" PDF renderer by default, unless another PDF renderer is specified with the ``--pdf-renderer`` argument. The previous behavior was to select ``--pdf-renderer=hocr``.
- The "tesseract" PDF renderer is now deprecated, since it can cause problems with Ghostscript on Tesseract 3.05.00
- The "tess4" PDF renderer has been renamed to "sandwich". "tess4" is now a deprecated alias for "sandwich".
v5.1
----
- Files with pages larger than 200" (5080 mm) in either dimension are now supported with ``--output-type=pdf`` with the page size preserved (in the PDF specification this feature is called UserUnit scaling). Due to Ghostscript limitations this is not available in conjunction with PDF/A output.
v5.0.1
------
- Fixed issue `#169 <https://github.com/jbarlow83/OCRmyPDF/issues/169>`_, exception due to failure to create sidecar text files on some versions of Tesseract 3.04, including the jbarlow83/ocrmypdf Docker image
v5.0
----
- Backward incompatible changes
+ Support for Python 3.4 dropped. Python 3.5 is now required.
+ Support for Tesseract 3.02 and 3.03 dropped. Tesseract 3.04 or newer is required. Tesseract 4.00 (alpha) is supported.
+ The OCRmyPDF.sh script was removed.
- Add a new feature, ``--sidecar``, which allows creating "sidecar" text files which contain the OCR results in plain text. These OCR text is more reliable than extracting text from PDFs. Closes `#126 <https://github.com/jbarlow83/OCRmyPDF/issues/126>`_.
- New feature: ``--pdfa-image-compression``, which allows overriding Ghostscript's lossy-or-lossless image encoding heuristic and making all images JPEG encoded or lossless encoded as desired. Fixes `#163 <https://github.com/jbarlow83/OCRmyPDF/issues/163>`_.
- Fixed issue `#143 <https://github.com/jbarlow83/OCRmyPDF/issues/143>`_, added ``--quiet`` to suppress "INFO" messages
- Fixed issue `#164 <https://github.com/jbarlow83/OCRmyPDF/issues/164>`_, a typo
- Removed the command line parameters ``-n`` and ``--just-print`` since they have not worked for some time (reported as Ubuntu bug `#1687308 <https://bugs.launchpad.net/ubuntu/+source/ocrmypdf/+bug/1687308>`_)
v4.5.6
------
- Fixed issue `#156 <https://github.com/jbarlow83/OCRmyPDF/issues/156>`_, 'NoneType' object has no attribute 'getObject' on pages with no optional /Contents record. This should resolve all issues related to pages with no /Contents record.
- Fixed issue `#158 <https://github.com/jbarlow83/OCRmyPDF/issues/158>`_, ocrmypdf now stops and terminates if Ghostscript fails on an intermediate step, as it is not possible to proceed.
- Fixed issue `#160 <https://github.com/jbarlow83/OCRmyPDF/issues/160>`_, exception thrown on certain invalid arguments instead of error message
v4.5.5
------
- Automated update of macOS homebrew tap
- Fixed issue `#154 <https://github.com/jbarlow83/OCRmyPDF/issues/154>`_, KeyError '/Contents' when searching for text on blank pages that have no /Contents record. Note: incomplete fix for this issue.
v4.5.4
------
- Fix ``--skip-big`` raising an exception if a page contains no images (`#152 <https://github.com/jbarlow83/OCRmyPDF/issues/152>`_) (thanks to @TomRaz)
- Fix an issue where pages with no images might trigger "cannot write mode P as JPEG" (`#151 <https://github.com/jbarlow83/OCRmyPDF/issues/151>`_)
v4.5.3
------
- Added a workaround for Ghostscript 9.21 and probably earlier versions would fail with the error message "VMerror -25", due to a Ghostscript bug in XMP metadata handling
- High Unicode characters (U+10000 and up) are no longer accepted for setting metadata on the command line, as Ghostscript may not handle them correctly.
- Fixed an issue where the ``tess4`` renderer would duplicate content onto output pages if tesseract failed or timed out
- Fixed ``tess4`` renderer not recognized when lossless reconstruction is possible
v4.5.2
------
- Fix issue `#147 <https://github.com/jbarlow83/OCRmyPDF/issues/147>`_. ``--pdf-renderer tess4 --clean`` will produce an oversized page containing the original image in the bottom left corner, due to loss DPI information.
- Make "using Tesseract 4.0" warning less ominous
- Set up machinery for homebrew OCRmyPDF tap
v4.5.1
------
- Fix issue `#137 <https://github.com/jbarlow83/OCRmyPDF/issues/137>`_, proportions of images with a non-square pixel aspect ratio would be distorted in output for ``--force-ocr`` and some other combinations of flags
v4.5
----
- Exotic PDFs containing "Form XObjects" are now supported (issue `#134 <https://github.com/jbarlow83/OCRmyPDF/issues/134>`_; PDF reference manual 8.10), and images they contain are taken into account when determining the resolution for rasterizing
- The Tesseract 4 Docker image no longer includes all languages, because it took so long to build something would tend to fail
- OCRmyPDF now warns about using ``--pdf-renderer tesseract`` with Tesseract 3.04 or lower due to issues with Ghostscript corrupting the OCR text in these cases
v4.4.2
------
- The Docker images (ocrmypdf, ocrmypdf-polyglot, ocrmypdf-tess4) are now based on Ubuntu 16.10 instead of Debian stretch
+ This makes supporting the Tesseract 4 image easier
+ This could be a disruptive change for any Docker users who built customized these images with their own changes, and made those changes in a way that depends on Debian and not Ubuntu
- OCRmyPDF now prevents running the Tesseract 4 renderer with Tesseract 3.04, which was permitted in v4.4 and v4.4.1 but will not work
v4.4.1
------
- To prevent a `TIFF output error <https://github.com/python-pillow/Pillow/issues/2206>`_ caused by img2pdf >= 0.2.1 and Pillow <= 3.4.2, dependencies have been tightened
- The Tesseract 4.00 simultaneous process limit was increased from 1 to 2, since it was observed that 1 lowers performance
- Documentation improvements to describe the ``--tesseract-config`` feature
- Added test cases and fixed error handling for ``--tesseract-config``
- Tweaks to setup.py to deal with issues in the v4.4 release
v4.4
----
- Tesseract 4.00 is now supported on an experimental basis.
+ A new rendering option ``--pdf-renderer tess4`` exploits Tesseract 4's new text-only output PDF mode. See the documentation on PDF Renderers for details.
+ The ``--tesseract-oem`` argument allows control over the Tesseract 4 OCR engine mode (tesseract's ``--oem``). Use ``--tesseract-oem 2`` to enforce the new LSTM mode.
+ Fixed poor performance with Tesseract 4.00 on Linux
- Fixed an issue that caused corruption of output to stdout in some cases
- Removed test for Pillow JPEG and PNG support, as the minimum supported version of Pillow now enforces this
- OCRmyPDF now tests that the intended destination file is writable before proceeding
- The test suite now requires ``pytest-helpers-namespace`` to run (but not install)
- Significant code reorganization to make OCRmyPDF re-entrant and improve performance. All changes should be backward compatible for the v4.x series.
+ However, OCRmyPDF's dependency "ruffus" is not re-entrant, so no Python API is available. Scripts should continue to use the command line interface.
v4.3.5
------
- Update documentation to confirm Python 3.6.0 compatibility. No code changes were needed, so many earlier versions are likely supported.
v4.3.4
------
- Fixed "decimal.InvalidOperation: quantize result has too many digits" for high DPI images
v4.3.3
------
- Fixed PDF/A creation with Ghostscript 9.20 properly
- Fixed an exception on inline stencil masks with a missing optional parameter
v4.3.2
------
- Fixed a PDF/A creation issue with Ghostscript 9.20 (note: this fix did not actually work)
v4.3.1
------
- Fixed an issue where pages produced by the "hocr" renderer after a Tesseract timeout would be rotated incorrectly if the input page was rotated with a /Rotate marker
- Fixed a file handle leak in LeptonicaErrorTrap that would cause a "too many open files" error for files around hundred pages of pages long when ``--deskew`` or ``--remove-background`` or other Leptonica based image processing features were in use, depending on the system value of ``ulimit -n``
- Ability to specify multiple languages for multilingual documents is now advertised in documentation
- Reduced the file sizes of some test resources
- Cleaned up debug output
- Tesseract caching in test cases is now more cautious about false cache hits and reproducing exact output, not that any problems were observed
v4.3
----
- New feature ``--remove-background`` to detect and erase the background of color and grayscale images
- Better documentation
- Fixed an issue with PDFs that draw images when the raster stack depth is zero
- ocrmypdf can now redirect its output to stdout for use in a shell pipeline
+ This does not improve performance since temporary files are still used for buffering
+ Some output validation is disabled in this mode
v4.2.5
------
- Fixed an issue (`#100 <https://github.com/jbarlow83/OCRmyPDF/issues/100>`_) with PDFs that omit the optional /BitsPerComponent parameter on images
- Removed non-free file milk.pdf
v4.2.4
------
- Fixed an error (`#90 <https://github.com/jbarlow83/OCRmyPDF/issues/90>`_) caused by PDFs that use stencil masks properly
- Fixed handling of PDFs that try to draw images or stencil masks without properly setting up the graphics state (such images are now ignored for the purposes of calculating DPI)
v4.2.3
------
- Fixed an issue with PDFs that store page rotation (/Rotate) in an indirect object
- Integrated a few fixes to simplify downstream packaging (Debian)
+ The test suite no longer assumes it is installed
+ If running Linux, skip a test that passes Unicode on the command line
- Added a test case to check explicit masks and stencil masks
- Added a test case for indirect objects and linearized PDFs
- Deprecated the OCRmyPDF.sh shell script
v4.2.2
------
- Improvements to documentation
v4.2.1
------
- Fixed an issue where PDF pages that contained stencil masks would report an incorrect DPI and cause Ghostscript to abort
- Implemented stdin streaming
v4.2
----
- ocrmypdf will now try to convert single image files to PDFs if they are provided as input (`#15 <https://github.com/jbarlow83/OCRmyPDF/issues/15>`_)
+ This is a basic convenience feature. It only supports a single image and always makes the image fill the whole page.
+ For better control over image to PDF conversion, use ``img2pdf`` (one of ocrmypdf's dependencies)
- New argument ``--output-type {pdf|pdfa}`` allows disabling Ghostscript PDF/A generation
+ ``pdfa`` is the default, consistent with past behavior
+ ``pdf`` provides a workaround for users concerned about the increase in file size from Ghostscript forcing JBIG2 images to CCITT and transcoding JPEGs
+ ``pdf`` preserves as much as it can about the original file, including problems that PDF/A conversion fixes
- PDFs containing images with "non-square" pixel aspect ratios, such as 200x100 DPI, are now handled and converted properly (fixing a bug that caused to be cropped)
- ``--force-ocr`` rasterizes pages even if they contain no images
+ supports users who want to use OCRmyPDF to reconstruct text information in PDFs with damaged Unicode maps (copy and paste text does not match displayed text)
+ supports reinterpreting PDFs where text was rendered as curves for printing, and text needs to be recovered
+ fixes issue `#82 <https://github.com/jbarlow83/OCRmyPDF/issues/82>`_
- Fixes an issue where, with certain settings, monochrome images in PDFs would be converted to 8-bit grayscale, increasing file size (`#79 <https://github.com/jbarlow83/OCRmyPDF/issues/79>`_)
- Support for Ubuntu 12.04 LTS "precise" has been dropped in favor of (roughly) Ubuntu 14.04 LTS "trusty"
+ Some Ubuntu "PPAs" (backports) are needed to make it work
- Support for some older dependencies dropped
+ Ghostscript 9.15 or later is now required (available in Ubuntu trusty with backports)
+ Tesseract 3.03 or later is now required (available in Ubuntu trusty)
- Ghostscript now runs in "safer" mode where possible
v4.1.4
------
- Bug fix: monochrome images with an ICC profile attached were incorrectly converted to full color images if lossless reconstruction was not possible due to other settings; consequence was increased file size for these images
v4.1.3
------
- More helpful error message for PDFs with version 4 security handler
- Update usage instructions for Windows/Docker users
- Fix order of operations for matrix multiplication (no effect on most users)
- Add a few leptonica wrapper functions (no effect on most users)
v4.1.2
------
- Replace IEC sRGB ICC profile with Debian's sRGB (from icc-profiles-free) which is more compatible with the MIT license
- More helpful error message for an error related to certain types of malformed PDFs
v4.1
----
- ``--rotate-pages`` now only rotates pages when reasonably confidence in the orientation. This behavior can be adjusted with the new argument ``--rotate-pages-threshold``
- Fixed problems in error checking if ``unpaper`` is uninstalled or missing at run-time
- Fixed problems with "RethrownJobError" errors during error handling that suppressed the useful error messages
v4.0.7
------
- Minor correction to Ghostscript output settings
v4.0.6
------
- Update install instructions
- Provide a sRGB profile instead of using Ghostscript's
v4.0.5
------
- Remove some verbose debug messages from v4.0.4
- Fixed temporary that wasn't being deleted
- DPI is now calculated correctly for cropped images, along with other image transformations
- Inline images are now checked during DPI calculation instead of rejecting the image
v4.0.4
------
Released with verbose debug message turned on. Do not use. Skip to v4.0.5.
v4.0.3
------
New features
^^^^^^^^^^^^
- Page orientations detected are now reported in a summary comment
Fixes
^^^^^
- Show stack trace if unexpected errors occur
- Treat "too few characters" error message from Tesseract as a reason to skip that page rather than
abort the file
- Docker: fix blank JPEG2000 issue by insisting on Ghostscript versions that have this fixed
v4.0.2
------
Fixes
^^^^^
- Fixed compatibility with Tesseract 3.04.01 release, particularly its different way of outputting
orientation information
- Improved handling of Tesseract errors and crashes
- Fixed use of chmod on Docker that broke most test cases
v4.0.1
------
Fixes
^^^^^
- Fixed a KeyError if tesseract fails to find page orientation information
v4.0
----
New features
^^^^^^^^^^^^
- Automatic page rotation (``-r``) is now available. It uses ignores any prior rotation information
on PDFs and sets rotation based on the dominant orientation of detectable text. This feature is
fairly reliable but some false positives occur especially if there is not much text to work with. (`#4 <https://github.com/jbarlow83/OCRmyPDF/issues/4>`_)
- Deskewing is now performed using Leptonica instead of unpaper. Leptonica is faster and more reliable
at image deskewing than unpaper.
Fixes
^^^^^
- Fixed an issue where lossless reconstruction could cause some pages to be appear incorrectly
if the page was rotated by the user in Acrobat after being scanned (specifically if it a /Rotate tag)
- Fixed an issue where lossless reconstruction could misalign the graphics layer with respect to
text layer if the page had been cropped such that its origin is not (0, 0) (`#49 <https://github.com/jbarlow83/OCRmyPDF/issues/49>`_)
Changes
^^^^^^^
- Logging output is now much easier to read
- ``--deskew`` is now performed by Leptonica instead of unpaper (`#25 <https://github.com/jbarlow83/OCRmyPDF/issues/25>`_)
- libffi is now required
- Some changes were made to the Docker and Travis build environments to support libffi
- ``--pdf-renderer=tesseract`` now displays a warning if the Tesseract version is less than 3.04.01,
the planned release that will include fixes to an important OCR text rendering bug in Tesseract 3.04.00.
You can also manually install ./share/sharp2.ttf on top of pdf.ttf in your Tesseract tessdata folder
to correct the problem.
v3.2.1
------
Changes
^^^^^^^
- Fixed issue `#47 <https://github.com/jbarlow83/OCRmyPDF/issues/47>`_ "convert() got and unexpected keyword argument 'dpi'" by upgrading to img2pdf 0.2
- Tweaked the Dockerfiles
v3.2
----
New features
^^^^^^^^^^^^
- Lossless reconstruction: when possible, OCRmyPDF will inject text layers without
otherwise manipulating the content and layout of a PDF page. For example, a PDF containing a mix
of vector and raster content would see the vector content preserved. Images may still be transcoded
during PDF/A conversion. (``--deskew`` and ``--clean-final`` disable this mode, necessarily.)
- New argument ``--tesseract-pagesegmode`` allows you to pass page segmentation arguments to Tesseract OCR.
This helps for two column text and other situations that confuse Tesseract.
- Added a new "polyglot" version of the Docker image, that generates Tesseract with all languages packs installed,
for the polyglots among us. It is much larger.
Changes
^^^^^^^
- JPEG transcoding quality is now 95 instead of the default 75. Bigger file sizes for less degradation.
v3.1.1
------
Changes
^^^^^^^
- Fixed bug that caused incorrect page size and DPI calculations on documents with mixed page sizes
v3.1
----
Changes
^^^^^^^
- Default output format is now PDF/A-2b instead of PDF/A-1b
- Python 3.5 and macOS El Capitan are now supported platforms - no changes were
needed to implement support
- Improved some error messages related to missing input files
- Fixed issue `#20 <https://github.com/jbarlow83/OCRmyPDF/issues/20>`_ - uppercase .PDF extension not accepted
- Fixed an issue where OCRmyPDF failed to text that certain pages contained previously OCR'ed text,
such as OCR text produced by Tesseract 3.04
- Inserts /Creator tag into PDFs so that errors can be traced back to this project
- Added new option ``--pdf-renderer=auto``, to let OCRmyPDF pick the best PDF renderer.
Currently it always chooses the 'hocrtransform' renderer but that behavior may change.
- Set up Travis CI automatic integration testing
v3.0
----
New features
^^^^^^^^^^^^
- Easier installation with a Docker container or Python's ``pip`` package manager
- Eliminated many external dependencies, so it's easier to setup
- Now installs ``ocrmypdf`` to ``/usr/local/bin`` or equivalent for system-wide
access and easier typing
- Improved command line syntax and usage help (``--help``)
- Tesseract 3.03+ PDF page rendering can be used instead for better positioning
of recognized text (``--pdf-renderer tesseract``)
- PDF metadata (title, author, keywords) are now transferred to the
output PDF
- PDF metadata can also be set from the command line (``--title``, etc.)
- Automatic repairs malformed input PDFs if possible
- Added test cases to confirm everything is working
- Added option to skip extremely large pages that take too long to OCR and are
often not OCRable (e.g. large scanned maps or diagrams); other pages are still
processed (``--skip-big``)
- Added option to kill Tesseract OCR process if it seems to be taking too long on
a page, while still processing other pages (``--tesseract-timeout``)
- Less common colorspaces (CMYK, palette) are now supported by conversion to RGB
- Multiple images on the same PDF page are now supported
Changes
^^^^^^^
- New, robust rewrite in Python 3.4+ with ruffus_ pipelines
- Now uses Ghostscript 9.14's improved color conversion model to preserve PDF colors
- OCR text is now rendered in the PDF as invisible text. Previous versions of OCRmyPDF
incorrectly rendered visible text with an image on top.
- All "tasks" in the pipeline can be executed in parallel on any
available CPUs, increasing performance
- The ``-o DPI`` argument has been phased out, in favor of ``--oversample DPI``, in
case we need ``-o OUTPUTFILE`` in the future
- Removed several dependencies, so it's easier to install. We no
longer use:
- GNU parallel_
- ImageMagick_
- Python 2.7
- Poppler
- MuPDF_ tools
- shell scripts
- Java and JHOVE_
- libxml2
- Some new external dependencies are required or optional, compared to v2.x:
- Ghostscript 9.14+
- qpdf_ 5.0.0+
- Unpaper_ 6.1 (optional)
- some automatically managed Python packages
.. _ruffus: http://www.ruffus.org.uk/index.html
.. _parallel: https://www.gnu.org/software/parallel/
.. _ImageMagick: http://www.imagemagick.org/script/index.php
.. _MuPDF: http://mupdf.com/docs/
.. _qpdf: http://qpdf.sourceforge.net/
.. _Unpaper: https://github.com/Flameeyes/unpaper
.. _JHOVE: http://jhove.sourceforge.net/
Release candidates
^^^^^^^^^^^^^^^^^^
- rc9:
- fix issue `#118 <https://github.com/jbarlow83/OCRmyPDF/issues/118>`_: report error if ghostscript iccprofiles are missing
- fixed another issue related to `#111 <https://github.com/jbarlow83/OCRmyPDF/issues/111>`_: PDF rasterized to palette file
- add support image files with a palette
- don't try to validate PDF file after an exception occurs
- rc8:
- fix issue `#111 <https://github.com/jbarlow83/OCRmyPDF/issues/111>`_: exception thrown if PDF is missing DocumentInfo dictionary
- rc7:
- fix error when installing direct from pip, "no such file 'requirements.txt'"
- rc6:
- dropped libxml2 (Python lxml) since Python 3's internal XML parser is sufficient
- set up Docker container
- fix Unicode errors if recognized text contains Unicode characters and system locale is not UTF-8
- rc5:
- dropped Java and JHOVE in favour of qpdf
- improved command line error output
- additional tests and bug fixes
- tested on Ubuntu 14.04 LTS
- rc4:
- dropped MuPDF in favour of qpdf
- fixed some installer issues and errors in installation instructions
- improve performance: run Ghostscript with multithreaded rendering
- improve performance: use multiple cores by default
- bug fix: checking for wrong exception on process timeout
- rc3: skipping version number intentionally to avoid confusion with Tesseract
- rc2: first release for public testing to test-PyPI, Github
- rc1: testing release process
Compatibility notes
-------------------
- ``./OCRmyPDF.sh`` script is still available for now
- Stacking the verbosity option like ``-vvv`` is no longer supported
- The configuration file ``config.sh`` has been removed. Instead, you can
feed a file to the arguments for common settings:
::
ocrmypdf input.pdf output.pdf @settings.txt
where ``settings.txt`` contains *one argument per line*, for example:
::
-l
deu
--author
A. Merkel
--pdf-renderer
tesseract
Fixes
^^^^^
- Handling of filenames containing spaces: fixed
Notes and known issues
^^^^^^^^^^^^^^^^^^^^^^
- Some dependencies may work with lower versions than tested, so try
overriding dependencies if they are "in the way" to see if they work.
- ``--pdf-renderer tesseract`` will output files with an incorrect page size in Tesseract 3.03,
due to a bug in Tesseract.
- PDF files containing "inline images" are not supported and won't be for the 3.0 release. Scanned
images almost never contain inline images.
v2.2-stable (2014-09-29)
------------------------
OCRmyPDF versions 1 and 2 were implemented as shell scripts. OCRmyPDF 3.0+ is a fork that gradually replaced all shell scripts with Python while maintaining the existing command line arguments. No one is maintaining old versions.
For details on older versions, see the `final version of its release notes <https://github.com/fritz-hh/OCRmyPDF/blob/7fd3dbdf42ca53a619412ce8add7532c5e81a9d1/RELEASE_NOTES.md>`_.
+77
View File
@@ -0,0 +1,77 @@
PDF security issues
===================
OCRmyPDF should only be used on PDFs you trust. It is not designed to protect you against malware.
Recognizing that many users have an interest in handling PDFs and applying OCR to PDFs they did not generate themselves, this article discusses the security implications of PDFs and how users can protect themselves.
The disclaimer applies: this software has no warranties of any kind.
PDFs may contain malware
------------------------
PDF is a rich, complex file format. The official PDF 1.7 specification, ISO 32000:2008, is hundreds of pages long and references several annexes each of which are similar in length. PDFs can contain video, audio, XML, JavaScript and other programming, and forms. In some cases, they can open internet connections to pre-selected URLs. All of these possible attack vectors.
In short, PDFs `may contain viruses <https://security.stackexchange.com/questions/64052/can-a-pdf-file-contain-a-virus>`_.
This `article <https://theinvisiblethings.blogspot.ca/2013/02/converting-untrusted-pdfs-into-trusted.html>`_ describes a high-paranoia method which allows potentially hostile PDFs to be viewed and rasterized safely in a disposable virtual machine. A trusted PDF created in this manner is converted to images and loses all information making it searchable and losing all compression. OCRmyPDF could be used restore searchability.
How OCRmyPDF processes PDFs
---------------------------
OCRmyPDF must open and interpret your PDF in order to insert an OCR layer. First, it runs all PDFs through `pikepdf <https://github.com/pikepdf/pikepdf>`_, a library based on `qpdf <https://github.com/qpdf/qpdf>`_, a program that repairs PDFs with syntax errors. This is done because, in the author's experience, a significant number of PDFs in the wild especially those created by scanners are not well-formed files. qpdf makes it more likely that OCRmyPDF will succeed, but offers no security guarantees. qpdf is also used to split the PDF into single page PDFs.
Finally, OCRmyPDF rasterizes each page of the PDF using `Ghostscript <http://ghostscript.com/>`_ in ``-dSAFER`` mode.
Depending on the options specified, OCRmyPDF may graft the OCR layer into the existing PDF or it may essentially reconstruct ("re-fry") a visually identical PDF that may be quite different at the binary level. That said, OCRmyPDF is not a tool designed for sanitizing PDFs.
Using OCRmyPDF online or as a service
-------------------------------------
OCRmyPDF should not be deployed as a public-facing service, such as a website where a potential attacker could upload a PDF of their choice for OCR. OCRmyPDF is not designed to be secure against PDF malware. Another concern is PDFs specifically designed to be a denial of service attack: PDFs can contain recursive data structures that sometimes send parsers into infinite loops, and issue complex graphics drawing commands.
Setting aside these concerns, a side effect of OCRmyPDF is it may incidentally sanitize PDFs that contain malware. It runs ``qpdf`` to repair the PDF, which could correct malformed PDF structures that are part of an attack. When PDF/A output is selected (the default), the input PDF is partially reconstructed by Ghostscript. When ``--force-ocr`` is used, all pages are rasterized and reconverted to PDF, which could remove malware in embedded images. No guarantees.
OCRmyPDF should be relatively safe to use in a trusted intranet, with some considerations:
Limiting CPU usage
^^^^^^^^^^^^^^^^^^
OCRmyPDF will attempt to use all available CPUs and storage, so executing ``nice ocrmypdf`` or limiting the number of jobs with the ``-j`` argument may ensure the server remains available. Another option would be run OCRmyPDF jobs inside a Docker container, a virtual machine, or a cloud instance, which can impose its own limits on CPU usage and be terminated "from orbit" if it fails to complete.
Temporary storage requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OCRmyPDF will use a large amount of temporary storage for its work, proportional to the total number of pixels needed to rasterize the PDF. The raster image of a 8.5×11" color page at 300 DPI takes 25 MB uncompressed; OCRmyPDF saves its intermediates as PNG, but that still means it requires about 9 MB per intermediate based on average compression ratios. Multiple intermediates per page are also required, depending on the command line given. A rule of thumb would be to allow 100 MB of temporary storage per page in a file meaning that a small cloud servers or small VM partitions should be provisioned with plenty of extra space, if say, a 500 page file might be sent.
To check temporary storage usage on actual files, run ``ocrmypdf -k ...`` which will preserve and print the path to temporary storage when the job is done.
To change where temporary files are stored, change the ``TMPDIR`` environment variable for ocrmypdf's environment. (Python's ``tempfile.gettempdir()`` returns the root directory in which temporary files will be stored.) For example, one could redirect ``TMPDIR`` to a large RAM disk to avoid wear on HDD/SSD and potentially improve performance. On Amazon Web Services, ``TMPDIR`` can be set to `empheral storage <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html>`_.
Timeouts
^^^^^^^^
To prevent excessively long OCR jobs consider setting ``--tesseract-timeout`` and/or ``--skip-big`` arguments. ``--skip-big`` is particularly helpful if your PDFs include documents such as reports on standard page sizes with large images attached - often large images are not worth OCR'ing anyway.
Commercial alternatives
^^^^^^^^^^^^^^^^^^^^^^^
The author also provides professional services that include OCR and building databases around PDFs, and is happy to provide consultation.
Abbyy Cloud OCR is a viable commercial alternative with a web services API.
Password protection, digital signatures and certification
---------------------------------------------------------
Password protected PDFs usually have two passwords, and owner and user password. When the user password is set to empty, PDF readers will open the file automatically and marked it as "(SECURED)". While not as reliable as a digital signature, this indicates that whoever set the password approved of the file at that time. When the user password is set, the document cannot be viewed without the password.
Either way, OCRmyPDF does not remove passwords from PDFs and exits with an error on encountering them.
``qpdf``, one of OCRmyPDF's dependencies, can remove passwords. If the owner and user password are set, a password is required for ``qpdf``. If only the owner password is set, then the password can be stripped, even if one does not have the owner password.
After OCR is applied, password protection is not permitted on PDF/A documents but the file can be converted to regular PDF.
Many programs exist which are capable of inserting an image of someone's signature. On its own, this offers no security guarantees. It is trivial to remove the signature image and apply it to other files. This practice offers no real security.
Important documents can be digitally signed and certified to attest to their authorship. OCRmyPDF cannot do this. Open source tools such as pdfbox (Java) have this capability as does Adobe Acrobat.
-502
View File
@@ -1,502 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
-17
View File
@@ -1,17 +0,0 @@
JHOVE - JSTOR/Harvard Object Validation Environment
Copyright 2003-2008 by JSTOR and the President and Fellows of Harvard College
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lessor General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This program 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
-227
View File
@@ -1,227 +0,0 @@
JHOVE - JSTOR/Harvard Object Validation Environment
Copyright 2003-2012 by JSTOR and the President and Fellows of Harvard College
JHOVE is made available under the GNU Lesser General Public License (LGPL;
see the file LICENSE for details)
Rev. 1.9, 2012-12-17
JHOVE (the JSTOR/Harvard Object Validation Environment, pronounced "jhove")
is an extensible software framework for performing format identification,
validation, and characterization of digital objects.
o Format identification is the process of determining the format to which a
digital object conforms: "I have a digital object; what format is it?"
o Format validation is the process of determining the level of compliance of a
digital object to the specification for its purported format: "I have an
object purportedly of format F; is it?"
o Format characterization is the process of determing the format-specific
significant properties of an object of a given format: "I have an object of
format F; what are its salient properties?"
These actions are frequently necessary during routine operation of digital
repositories and for digital preservation activities.
The output from JHOVE is controlled by output handlers. JHOVE uses an
extensible plug-in architecture; it can be configured at the time of its
invocation to include whatever specific format modules and output handlers
that are desired. The initial release of JHOVE includes modules for
arbitrary byte streams, ASCII and UTF-8 encoded text, AIFF and WAVE audio,
GIF, JPEG, JPEG 2000, TIFF, and PDF; and text and XML output handlers.
The JHOVE project is a collaboration of JSTOR and the Harvard University
Library. Development of JHOVE was funded in part by the Andrew W. Mellon
Foundation. JHOVE is made available under the GNU Lesser General Public
License (LGPL; see the file LICENSE for details).
JHOVE is currently being maintained by indpendent developers.
REQUIREMENTS
1. Java J2SE 1.5
(JHOVE was originally implemented using the Sun J2SE SDK 1.4.1 and has
been tested to work with 1.5)
2. If you would like to compile the JHOVE source code, then
Apache Ant, a Java-based build tool <http://ant.apache.org/> is necessary.
Note that the JAVA_HOME environment variable must be set appropriately for
Ant to work properly.
(JHOVE was implemented and tested using Ant 1.5.1.)
DISTRIBUTION
The JHOVE distribution package includes:
jhove/ # JHOVE home directory
COPYING # GNU Lesser General Public License
LICENSE # JHOVE license information
README
RELEASENOTES # JHOVE release notes
bin/
jhove.jar # JHOVE API package
jhove-handler.jar # Standard output handler package
jhove-module.jar # Standard module package
JhoveApp.jar # JHOVE command line application
JhoveView.jar # JHOVE with Swing GUI front-end
build.xml # Ant configuration file
classes/
build.xml # Ant configuration file
edu/ ... # JHOVE API packages
ADump.* # AIFF dump utility class
GDump.* # GIF dump utility class
Jhove.* # JHOVE main class
JDump.* # JPEG dump utility class
J2Dump.* # JPEG 2000 dump utility class
PDump.* # PDF dump utility class
TDump.* # TIFF dump utility class
UserHome.* # user.home property utility class
WDump.* # WAVE dump utility class
conf/
jhove.conf # JHOVE configuration file
jhove.xsd # JHOVE output schema
jhoveConfig.xsd # JHOVE configuration file schema
doc/
*.html # API documentation
...
examples/ # Sample files
ascii/ ...
gif/ ...
jpeg/ ...
jpeg2000/ ...
pdf/ ...
tiff/ ...
utf-8/ ...
adump* # AIFF dump Bourne shell driver
adump.bat* # AIFF dump DOS shell driver script
gdump* # GIF dump Bourne shell driver
gdump.bat* # GIF dump DOS shell driver script
jdump* # JPEG dump Bourne shell driver
jdump.bat* # JPEG dump DOS shell driver script
j2dump* # JPEG 2000 dump Bourne shell driver
j2dump.bat* # JPEG 2000 dump DOS shell driver
jhove.tmpl* # Template for JHOVE Bourne shell driver script
jhove_bat.tmpl* # Template for JHOVE DOS shell driver script
pdump* # PDF dump Bourne shell driver
pdump.bat* # PDF dump DOS shell driver script
tdump* # TIFF dump Bourne shell driver
tdump.bat* # TIFF dump DOS shell driver script
userhome* # user.home Bourne shell driver
userhome.bat* # user.home DOS shell driver script
wdump* # WAVE dump Bourne shell driver
wdump.bat* # WAVE dump DOS shell driver script
INSTALLATION
Edit the configuration file, jhove/conf/jhove.conf, and set the absolute
pathname of the JHOVE home directory and the temporary directory (in which
temporary files are created):
<jhoveHome>jhove-home-directory</jhoveHome>
<tempDirectory>temporary-directory</tempDirectory>
The JHOVE home directory is the top-most directory in the distribution TAR
or ZIP file. On Unix systems, "/var/tmp" is an appropriate temporary
directory; on Windows, "C:\Temp". For example, if the distribution TAR
file is disaggregated on a Unix system in the directory "/users/stephen/
projects", then the configuration file should read:
<jhoveHome>/users/stephen/projects/jhove</jhoveHome>
<tempDirectory>/var/tmp</jhoveHome>
In the JHOVE home directory, copy the JHOVE Bourne shell driver script
template, "jhove.tmpl", to "jhove" (or the equivalent Windows shell
script, "jhove_bat.tmpl" to "jhove.bat"), and set the
JHOVE home directory, Java home directory, and Java interpreter:
JHOVE_HOME=jhove-home-directory
JAVA_HOME=java-home-directory
JAVA=java-interpreter
The JAVA_HOME property should provide the absolute pathname of the Java
runtime or SDK installation; JAVA should provide the absolute pathname of the
Java interpreter. For example:
JHOVE_HOME=/users/stephen/projects/jhove
JAVA_HOME=/usr/local/j2re1.4.1_02
JAVA=$JAVA_HOME/bin/java
In the DOS shell driver script, jhove.bat, the equivalent three
variables are:
SET JHOVE_HOME=jhove-home-directory
SET JAVA_HOME=java-home-directory
SET JAVA=%JAVA_HOME%\bin\java
For example:
SET JHOVE_HOME="C:\Program Files\jhove"
SET JAVA_HOME="C:\Program Files\java\j2re1.4.1_02"
SET JAVA=%JAVA_HOME%\bin\java
The quotation marks are necessary because of the embedded space characters.
On Windows platforms it may also be necessary to add the Java bin subdirectory
to the System PATH environment variable:
PATH=C:\Program Files\java\j2re1.4.1_02\bin;...
(For information on setting a Windows environment variable, consult your local
documentation or system administrator.)
USAGE
java Jhove [-c config] [-m module] [-h handler] [-e encoding] [-H handler]
[-o output] [-x saxclass] [-t tempdir] [-b bufsize]
[-l loglevel] [[-krs] dir-file-or-uri [...]]
where -c config Configuration file pathname
-m module Module name
-h handler Output handler name (defaults to TEXT)
-e encoding Character encoding used by output handler (defaults to UTF-8)
-H handler About handler name
-o output Output file pathname (defaults to standard output)
-x saxclass SAX parser class (defaults to J2SE default)
-t tempdir Temporary directory in which to create temporary files
-b bufsize Buffer size for buffered I/O (defaults to J2SE 1.4 default)
-l loglevel Logging level
-k Calculate CRC32, MD5, and SHA-1 checksums
-r Display raw data flags, not textual equivalents
-s Format identification based on internal signatures only
dir-file-or-uri Directory or file pathname or URI of formated content
stream
All named modules and output handlers must be found on the Java CLASSPATH at
the time of invocation. The JHOVE driver script, jhove/jhove, automatically
sets the CLASSPATH and invokes the Jhove main class:
jhove [-c config] [-m module] [-h handler] [-e encoding] [-H handler]
[-o output] [-x saxclass] [-t tempdir] [-b bufsize] [-l loglevel]
[[-krs] dir-file-or-uri [...]]
The following additional programs are available, primarily for testing
and debugging purposes. They display a minimally processed, human-readable
version of the contents of AIFF, GIF, JPEG, JPEG 2000, PDF, TIFF, and WAVE
files:
java ADump aiff-file
java GDump gif-file
java JDump jpeg-file
java J2Dump jpeg2000-file
java PDump pdf-file
java TDump tiff-file
java WDump wave-file
For convenience, the following driver scripts are also available:
adump aiff-file
gdump gif-file
jdump jpeg-file
j2dump jpeg2000-file
pdump pdf-file
tdump tiff-file
wdump wave-file
The JHOVE Swing-based GUI interface can be invoked from a command shell from
the jhove/bin sub-directory:
java -jar JhoveView.jar -c <configFile>
where <configFile> is the pathname of the JHOVE configuration file.
-1676
View File
File diff suppressed because it is too large Load Diff
-78
View File
@@ -1,78 +0,0 @@
<project name="Jhove" default="dist" basedir=".">
<description>Project build file
Jhove - JSTOR/Harvard Object Validation Environment
Version 1.0 2004-09-10
Copyright 2004 by JSTOR and the President and Fellows of Harvard College
</description>
<!-- ant (or ant dist) Build everything
ant debug Build everything with debug enabled
ant clean Delete backup files
ant cleanclass Delete backup and class files
ant cleandist Delete backup, class, and jar files
ant javadoc Build javadocs
-->
<!-- set global properties for this build -->
<property name="bin" location="bin"/>
<property name="classes" location="classes"/>
<property name="doc" location="doc"/>
<target name="dist" description="Create distribution">
<ant dir="${classes}" inheritAll="false">
<property name="dbg" value="off"/>
</ant>
<chmod file="jhove" perm="ugo+x"/>
<chmod file="${bin}/JhoveView.jar" perm="ugo+x"/>
</target>
<target name="debug" description="Create distribution with debug enabled">
<ant dir="${classes}" inheritAll="false">
<property name="dbg" value="on"/>
</ant>
</target>
<target name="view" description="Create JhoveView application">
<ant dir="${classes}" target="view" inheritAll="false">
<property name="dbs" value="on"/>
</ant>
</target>
<target name="clean" description="Delete backup files">
<ant dir="${classes}" target="main-clean" inheritAll="false"/>
</target>
<target name="cleanclass" depends="clean">
<ant dir="${classes}" target="main-cleanclass" inheritAll="false"/>
</target>
<target name="cleandist" depends="cleanclass">
<delete file="${bin}/jhove.jar"/>
<delete file="${bin}/jhove-handler.jar"/>
<delete file="${bin}/jhove-module.jar"/>
<delete file="${bin}/JhoveApp.jar"/>
<delete file="${bin}/JhoveView.jar"/>
</target>
<target name="javadoc">
<javadoc sourcepath="${classes}" destdir="${doc}"
windowtitle="JHOVE Documentation"
Overview="${classes}/overview.html">
<package name="edu.harvard.hul.ois.jhove"/>
<package name="edu.harvard.hul.ois.jhove.handler"/>
<package name="edu.harvard.hul.ois.jhove.handler.audit"/>
<package name="edu.harvard.hul.ois.jhove.module"/>
<package name="edu.harvard.hul.ois.jhove.module.aiff"/>
<package name="edu.harvard.hul.ois.jhove.module.gif"/>
<package name="edu.harvard.hul.ois.jhove.module.html"/>
<package name="edu.harvard.hul.ois.jhove.module.iff"/>
<package name="edu.harvard.hul.ois.jhove.module.jpeg"/>
<package name="edu.harvard.hul.ois.jhove.module.jpeg2000"/>
<package name="edu.harvard.hul.ois.jhove.module.pdf"/>
<package name="edu.harvard.hul.ois.jhove.module.tiff"/>
<package name="edu.harvard.hul.ois.jhove.module.wave"/>
<package name="edu.harvard.hul.ois.jhove.module.xml"/>
<package name="edu.harvard.hul.ois.jhove.viewer"/>
</javadoc>
</target>
</project>
-235
View File
@@ -1,235 +0,0 @@
/**********************************************************************
* JDump - JSTOR/Harvard Object Validation Environment
* Copyright 2004-2005 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import edu.harvard.hul.ois.jhove.module.aiff.*;
import java.io.*;
/**
* Dump contents of AIFF file in human-readable format.
* @author Gary McGath
*/
public class ADump extends Dump {
/* Fixed value for first 4 bytes */
private static final int[] sigByte =
{ 0X46, 0X4F, 0X52, 0X4D };
private static final boolean ENDIAN = true; /* bigEndian */
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java ADump file");
System.exit (-1);
}
try {
FileInputStream file = new FileInputStream (args[0]);
BufferedInputStream buffer = new BufferedInputStream (file);
DataInputStream stream = new DataInputStream (buffer);
ADump dump = new ADump (); // Just to access contained classes
long os = 0;
for (int i=0; i<4; i++) {
int ch;
ch = stream.readUnsignedByte();
if (ch != sigByte[i]) {
System.out.println ("No AIFF FORM header");
System.exit (-2);
}
}
os += 4;
long ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN);
// Read the file type
StringBuffer formType = new StringBuffer (4);
for (int i=0; i<4; i++) {
int ch = ModuleBase.readUnsignedByte(stream);
formType.append((char) ch);
}
System.out.println ("00000000: FORM " + ckSize + ": " + formType);
boolean aiff_c = false;
if (formType.toString ().equals ("AIFC")) {
aiff_c = true;
}
StringBuffer sbuf = new StringBuffer ();
boolean endOfFile = false;
while (!endOfFile) {
// Read chunks
try {
sbuf.setLength(0);
// Read chunk name.
for (int i=0; i<4; i++) {
int ch = ModuleBase.readUnsignedByte(stream);
sbuf.append((char) ch);
}
String ckID = sbuf.toString ();
// Read size (excluding chunk name and size fields)
ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN);
System.out.print (leading (os, 8) + os + ": " + ckID +
" " + ckSize);
long alreadyRead = 0;
if (ckID.equals ("AESD")) {
int [] aes = new int[24];
for (int i=0; i<24; i++) {
aes[i] = ModuleBase.readUnsignedByte (stream);
}
System.out.print (": " + aes[0]);
for (int i=1; i<24; i++) {
System.out.print ("," + aes[i]);
}
alreadyRead = 24;
}
else if (ckID.equals ("ANNO") ||
ckID.equals ("AUTH") ||
ckID.equals ("(c) ") ||
ckID.equals ("NAME")) {
sbuf.setLength (0);
for (int i=0; i<ckSize; i++) {
int ch = ModuleBase.readUnsignedByte (stream);
sbuf.append ((char) ch);
}
System.out.print (": \"" + sbuf.toString () + "\"");
alreadyRead = ckSize;
}
else if (ckID.equals ("APPL")) {
sbuf.setLength (0);
for (int i=0; i<4; i++) {
int ch = ModuleBase.readUnsignedByte (stream);
sbuf.append ((char) ch);
}
System.out.print (": " + sbuf.toString ());
alreadyRead = 4;
}
else if (ckID.equals ("COMM")) {
int numChannels = ModuleBase.readSignedShort (stream,
ENDIAN);
long numSampleFrames =
ModuleBase.readUnsignedInt (stream, ENDIAN);
int sampleSize = ModuleBase.readSignedShort (stream,
ENDIAN);
byte [] buf = new byte[10];
for (int i=0; i<10; i++) {
buf[i] = (byte) ModuleBase.readSignedByte (stream);
}
ExtDouble xd = new ExtDouble (buf);
double sampleRate = xd.toDouble ();
System.out.print (": " + numChannels + " " +
numSampleFrames + " " + sampleSize +
" " + sampleRate);
alreadyRead = 18;
if (aiff_c) {
sbuf.setLength (0);
for (int i=0; i<4; i++) {
int ch = ModuleBase.readUnsignedByte (stream,
null);
sbuf.append ((char) ch);
}
System.out.print (" " + sbuf.toString ());
int count = ModuleBase.readUnsignedByte (stream,
null);
alreadyRead = 23;
sbuf.setLength (0);
for (int i=0; i<count; i++) {
int ch = ModuleBase.readUnsignedByte (stream,
null);
sbuf.append ((char) ch);
alreadyRead++;
}
System.out.print (" \"" + sbuf.toString () + "\"");
}
}
else if (ckID.equals ("COMT")) {
int numComments = ModuleBase.readUnsignedShort (stream,
ENDIAN,
null);
System.out.print (": " + numComments);
alreadyRead = 2;
}
else if (ckID.equals ("FVER")) {
long timestamp = ModuleBase.readUnsignedInt (stream,
ENDIAN,
null);
System.out.print (": " + timestamp);
alreadyRead = 4;
}
else if (ckID.equals ("INST")) {
int baseNote = ModuleBase.readSignedByte (stream);
int detune = ModuleBase.readSignedByte (stream);
int lowNote = ModuleBase.readSignedByte (stream);
int highNote = ModuleBase.readSignedByte (stream);
int lowVelocity = ModuleBase.readSignedByte (stream);
int highVelocity = ModuleBase.readSignedByte (stream);
int gain = ModuleBase.readSignedShort (stream, ENDIAN);
System.out.print (": " + baseNote + " " + detune +
" " + lowNote + "," + highNote +
" " + lowVelocity + "," +
highVelocity + " " + gain);
alreadyRead = 8;
}
else if (ckID.equals ("MARK")) {
int numMarkers = ModuleBase.readUnsignedShort (stream,
ENDIAN);
System.out.print (": " + numMarkers);
alreadyRead = 2;
}
else if (ckID.equals ("SSND")) {
long offset = ModuleBase.readUnsignedInt (stream,
ENDIAN);
long blockSize = ModuleBase.readUnsignedInt (stream,
ENDIAN);
System.out.print (": " + offset + " " + blockSize);
alreadyRead = 8;
}
System.out.println ();
// Actual number of bytes to skip must be even.
long actSize = ckSize;
if ((actSize & 1) != 0) {
actSize++;
}
actSize -= alreadyRead;
stream.skipBytes ((int) actSize);
os += ckSize + 8;
}
catch (EOFException e) {
endOfFile = true;
}
}
}
catch (Exception e) {
}
}
}
-264
View File
@@ -1,264 +0,0 @@
/**********************************************************************
* GDump - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import java.io.*;
//import java.util.*;
/**
* Dump contents of GIF file in human-readable format.
*/
public class GDump
extends Dump
{
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java GDump file");
System.exit (-1);
}
try {
FileInputStream file = new FileInputStream (args[0]);
BufferedInputStream buffer = new BufferedInputStream (file);
DataInputStream stream = new DataInputStream (buffer);
boolean bigEndian = false;
String signature = readChars (stream, 3);
String version = readChars (stream, 3);
System.out.println ("00000000: \"" + signature + version + "\"");
int width = ModuleBase.readUnsignedShort(stream, bigEndian, null);
int height = ModuleBase.readUnsignedShort(stream, bigEndian, null);
int packedFields = stream.readUnsignedByte ();
int backgroundColor = stream.readUnsignedByte ();
int aspectRatio = stream.readUnsignedByte ();
System.out.println ("00000006: LogicalScreenDescriptor: " +
width + "x" + height + " 0x" +
leading (packedFields, 2) +
Integer.toHexString (packedFields) + " " +
backgroundColor + " " + aspectRatio);
long os = 13;
boolean globalColorTable = (packedFields & 0x80) != 0;
int globalColorTableSize = packedFields & 0x07;
if (globalColorTable) {
System.out.println (leading (os, 8) + os +
": GlobalColorTable:");
os = colorTable (stream, os, globalColorTableSize);
}
for (int op = 0; (op = stream.readUnsignedByte ()) != -1;) {
if (op == 0x00) {
System.out.println (leading (os, 8) + os +
": BlockTerminator");
os++;
}
else if (op == 0x2c) {
int imageLeft = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int imageTop = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int imageWidth = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int imageHeight = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
packedFields = stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os +
": ImageDescriptor: " + imageLeft +
"," + imageTop + " " + imageWidth +
"x" + imageHeight + " 0x" +
leading (packedFields, 2) +
Integer.toHexString (packedFields));
os += 10;
boolean localColorTable = (packedFields & 0x80) != 0;
int localColorTableSize = packedFields & 0x07;
if (localColorTable) {
System.out.println (leading (os, 8) + os +
": LocalColorTable:");
os = colorTable (stream, os, localColorTableSize);
}
int size = stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os +
": ImageData: " + size);
os = subBlocks (stream, ++os);
}
else if (op == 0x21) {
int label = stream.readUnsignedByte ();
if (label == 0x01) {
int blockSize = stream.readUnsignedByte ();
int gridLeft = ModuleBase.readUnsignedShort(stream,
bigEndian,
null);
int gridTop = ModuleBase.readUnsignedShort(stream,
bigEndian,
null);
int gridWidth = ModuleBase.readUnsignedShort(stream,
bigEndian,
null);
int gridHeight= ModuleBase.readUnsignedShort(stream,
bigEndian,
null);
int cellWidth = stream.readUnsignedByte ();
int cellHeight = stream.readUnsignedByte ();
int foreground = stream.readUnsignedByte ();
int background = stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os +
": PlainTextExtension: " +
blockSize + " " + gridLeft + "," +
gridTop + " " + gridWidth + "x" +
gridHeight + " " + cellWidth +
"x" + cellHeight + " " +
foreground + "," + background);
os += 15;
os = subBlocks (stream, os);
}
else if (label == 0xf9) {
int blockSize = stream.readUnsignedByte ();
packedFields = stream.readUnsignedByte ();
int delayTime = ModuleBase.readUnsignedShort(stream,
bigEndian,
null);
int colorIndex= stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os +
": GraphicControlExtension: " +
blockSize + " 0x" +
leading (packedFields, 2) +
packedFields + " " + delayTime +
" " + colorIndex);
os += 7;
}
else if (label == 0xfe) {
System.out.println (leading (os, 8) + os +
": CommentExtension: \"" +
"\"");
os += 2;
os = subBlocks (stream, os);
}
else if (label == 0xff) {
int blockSize = stream.readUnsignedByte ();
String appId = readChars (stream, 8);
int [] authCode = new int [3];
authCode[0] = stream.readUnsignedByte ();
authCode[1] = stream.readUnsignedByte ();
authCode[2] = stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os +
": ApplicationExtension: " +
blockSize + " \"" + appId +
"\" " + authCode[0] + "," +
authCode[1] + "," + authCode[2]);
os += 14;
os = subBlocks (stream, os);
}
else {
String hex = Integer.toHexString (label);
System.out.println (leading (os-1, 8) + (os-1) +
": Unknown extension block: 0x" +
leading (hex, 2) + hex);
os += 2;
}
}
else if (op == 0x3b) {
System.out.println (leading (os, 8) + os +
": Trailer: 0x3b");
os++;
break;
}
else {
String hex = Integer.toHexString (op);
System.out.println (leading (os, 8) + os +
": Unknown block: 0x" +
leading (hex, 2) + hex);
os++;
}
}
stream.close ();
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (-2);
}
}
/**
* Read and display a color table.
* @param stream Data input stream
* @param os Current byte offset
* @param size Color table size
* @return Updated byte offset
*/
private static long colorTable (DataInputStream stream, long os, int size)
throws IOException
{
int n = 2<<size;
for (int i=0; i<n; i++) {
long r = stream.readUnsignedByte ();
long g = stream.readUnsignedByte ();
long b = stream.readUnsignedByte ();
System.out.println (leading (os, 8) + os + ": " +
leading (i, 3, ' ') + i + ": " +
leading (r, 3, ' ') + r + "," +
leading (g, 3, ' ') + g + "," +
leading (b, 3, ' ') + b);
os += 3;
}
return os;
}
/**
* Read and display a sequence of sub-blocks.
* @param stream Data input stream
* @param os Current byte offset
* @return Updated byte offset
*/
private static long subBlocks (DataInputStream stream, long os)
throws IOException
{
int size;
while ((size = stream.readUnsignedByte ()) != 0) {
System.out.println (leading (os, 8) + os + ": SubBlock: " + size);
os += 1;
for (int i=0; i<size; i++) {
stream.readUnsignedByte ();
os += 1;
}
}
System.out.println (leading (os, 8) + os + ": BlockTerminator");
return os + 1;
}
}
-216
View File
@@ -1,216 +0,0 @@
/**********************************************************************
* JDump - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import java.io.*;
import java.util.*;
/**
* Dump contents of JPEG2000 file in human-readable format.
*/
public class J2Dump extends Dump {
/* Fixed value for first 12 bytes */
private static final int[] sigByte =
{
0X00,
0X00,
0X00,
0X0C,
0X6A,
0X50,
0X20,
0X20,
0X0D,
0X0A,
0X87,
0X0A };
private static final boolean ENDIAN = true; /* bigEndian */
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java J2Dump file");
System.exit (-1);
}
try {
FileInputStream file = new FileInputStream (args[0]);
BufferedInputStream buffer = new BufferedInputStream (file);
DataInputStream stream = new DataInputStream (buffer);
J2Dump dump = new J2Dump (); // Just to access contained classes
long os = 0;
boolean bigEndian = true;
int i;
for (i = 0; i < 12; i++) {
int ch;
ch = stream.readUnsignedByte();
if (ch != sigByte[i]) {
System.out.println ("No JPEG 2000 header");
System.exit (-2);
}
}
os += 12;
boolean endOfFile = false;
Stack boxStack = new Stack ();
while (!endOfFile) {
//If there are boxes on the stack, see if there's space
//left in the top one.
Box boxtop;
for (;;) {
boxtop = null;
if (boxStack.isEmpty ()) {
break;
}
boxtop = (Box) boxStack.peek ();
if (boxtop.bytesLeft > 0) {
break;
}
else {
boxStack.pop ();
}
}
// Read the header of a JP2 box
Box box = dump.new Box (stream);
try {
box.read ();
}
catch (EOFException e) {
endOfFile = true;
break;
}
os += box.length - box.bytesLeft;
// If it's contained in a superbox, subtract
// this box from its remaining length
if (boxtop != null) {
boxtop.bytesLeft -= box.length;
}
System.out.println (leading (os, 8) + os + ": " +
stackPrefix (boxStack) + box.type + " " + box.length);
if (box.isSuperbox ()) {
boxStack.push (box);
}
else {
os += box.bytesLeft;
stream.skipBytes((int) box.bytesLeft);
}
// A "length" of 0 means the box occupies the rest of the file.
if (box.length == 0) {
endOfFile = true;
}
}
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (-2);
}
}
/* Constructs a qualifying prefix to indicate nested boxes. */
private static String stackPrefix (Stack boxStack)
{
StringBuffer retval = new StringBuffer ();
// In defiance of gravity, we rummage through the stack
// of boxes starting at the bottom.
for (int i = 0; i < boxStack.size(); i++) {
Box box = (Box) boxStack.elementAt (i);
// Remove trailing spaces from types for better readability
retval.append (box.type.trim() + "/");
}
return retval.toString ();
}
/** Local class for defining JPEG2000 boxes. */
class Box {
public String type;
public long length;
public long bytesLeft;
public boolean hasBoxes;
DataInputStream dstream;
public Box (DataInputStream stream)
{
this.dstream = stream;
}
/** Reads a box header and sets up for reading contents. */
public void read () throws IOException
{
length = ModuleBase.readUnsignedInt (dstream, ENDIAN, null);
long headerLength = 8;
type = read4Chars ();
// If the length field is 1, there is an 8-byte extended
// length field.
if (length == 1) {
length = ModuleBase.readSignedLong(dstream, true, null);
headerLength = 16;
}
bytesLeft = length - headerLength;
}
/* Reads a 4-character name */
private String read4Chars() throws IOException {
StringBuffer sbuf = new StringBuffer(4);
for (int i = 0; i < 4; i++) {
int ch = ModuleBase.readUnsignedByte(dstream, null);
sbuf.append((char) ch);
}
return sbuf.toString();
}
/** Returns true if this box contains other boxes.
* At present, we don't deal with the insides of boxes
* that contain both data and boxes (e.g., cref). */
public boolean isSuperbox ()
{
// If it's a known superbox type, we return true.
// If we've left any out, that will merely make us
// lose the subboxes of that type.
String [] supertypes = { "asoc", "cgrp", "comp", "drep",
"ftbl", "jp2h", "jpch",
"jplh", "res ", "uuid" };
for (int i = 0; i < supertypes.length; i++) {
if (supertypes[i].equals (type)) {
return true;
}
}
return false;
}
}
}
-404
View File
@@ -1,404 +0,0 @@
/**********************************************************************
* JDump - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import java.io.*;
//import java.util.*;
/**
* Dump contents of JPEG file in human-readable format.
*/
public class JDump
extends Dump
{
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java JDump file");
System.exit (-1);
}
try {
FileInputStream file = new FileInputStream (args[0]);
BufferedInputStream buffer = new BufferedInputStream (file);
DataInputStream stream = new DataInputStream (buffer);
boolean bigEndian = true;
long os = 0;
boolean endOfImage = false;
boolean readingECS = false;
boolean haveCode = false;;
int nECS = 0;
int code = 0;
while (!endOfImage) {
if (!readingECS) {
if (!haveCode) {
code = stream.readUnsignedByte ();
for (int i=0;
(code = stream.readUnsignedByte ()) == 0xff;
i++) {
System.out.println (leading (os, 8) + os +
": fill 0xff");
os++;
}
}
}
else {
boolean ff = false;
int length = 0;
while (true) {
code = stream.readUnsignedByte ();
length++;
if (code == 0xff) {
ff = true;
}
else if (ff) {
if (code != 0x00) {
length -= 2;
break;
}
else {
ff = false;
}
}
}
System.out.println (leading (os, 8) + os + ": ECS" +
nECS + " " + length + " ...");
os += length;
nECS++;
readingECS = false;
haveCode = true;
continue;
}
if (code == 0x01) {
System.out.println (leading (os, 8) + os + ": TEM");
}
else if ((code >= 0xc0 && code <= 0xc3) ||
(code >= 0xc5 && code <= 0xc7) ||
(code >= 0xc9 && code <= 0xcb) ||
(code >= 0xcd && code <= 0xcf)) {
int n = code - 0xc0;
/****************** int length = markerSegment (stream, bigEndian); */
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int P = stream.readUnsignedByte ();
int Y = ModuleBase.readUnsignedShort (stream, bigEndian,
null);
int X = ModuleBase.readUnsignedShort (stream, bigEndian,
null);
int Nf = stream.readUnsignedByte ();
int [] Ci = new int [Nf];
int [] Hi = new int [Nf];
int [] Vi = new int [Nf];
int [] Tqi = new int [Nf];
for (int i=0; i<Nf; i++) {
Ci[i] = stream.readUnsignedByte ();
Hi[i] = stream.readUnsignedByte ();
Tqi[i] = stream.readUnsignedByte ();
}
System.out.print (leading (os, 8) + os + ": SOF" + n +
" " + length + ": " + P + " " + X +
"x" + Y + " " + Nf + ":");
for (int i=0; i<Nf; i++) {
System.out.print (" " + Ci[i] + "," + Hi[i] + "," +
Tqi[i]);
}
System.out.println ();
os += length;
}
else if (code == 0xc4) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DHT" + " " +
length + " ...");
os += length;
}
else if (code == 0xc8) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": JPG" + " " +
length + " ...");
os += length;
}
else if (code == 0xcc) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DAC" + " " +
length + " ...");
os += length;
}
else if (code >= 0xd0 && code <= 0xd7) {
int m = code - 0xd0;
System.out.println (leading (os, 8) + os + ": RST" + m);
readingECS = true;
}
else if (code == 0xd8) {
System.out.println (leading (os, 8) + os + ": SOI");
}
else if (code == 0xd9) {
System.out.println (leading (os, 8) + os + ": EOI");
endOfImage = true;
break;
}
else if (code == 0xda) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": SOS" + " " +
length + " ...");
os += length;
readingECS = true;
}
else if (code == 0xdb) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DQT" + " " +
length + " ...");
os += length;
}
else if (code == 0xdc) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DNL" + " " +
length + " ...");
os += length;
}
else if (code == 0xdd) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DRI" + " " +
length + " ...");
os += length;
}
else if (code == 0xde) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DHP" + " " +
length + " ...");
os += length;
}
else if (code == 0xdf) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": EXP" + " " +
length + " ...");
os += length;
}
else if (code == 0xe0) {
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
String id = readChars (stream, 5);
if (id.equals ("JFIF\0")) {
int major = stream.readUnsignedByte ();
int minor = stream.readUnsignedByte ();
int units = stream.readUnsignedByte ();
int xDensity = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int yDensity = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int xThumbnail = stream.readUnsignedByte ();
int yThumbnail = stream.readUnsignedByte ();
System.out.print (leading (os, 8) + os + ": APP0 " +
"\"" + id + "\" " + major + "." +
minor + " " + units + " " +
xDensity + "x" + yDensity + " "
+ xThumbnail + "x" + yThumbnail);
int n = length - 16;
if (n > 0) {
for (int i=0; i<n; i++) {
stream.readUnsignedByte ();
}
System.out.print (" " + n + " ...");
}
System.out.println ();
}
else if (id.equals ("JFXX\0")) {
int ext = stream.readUnsignedByte ();
String hex = Integer.toHexString (ext);
System.out.print (leading (os, 8) + os + ": APP0 " +
"0x" + leading (hex, 2) + hex);
int n = length-3;
if (n > 0) {
for (int i=0; i<n; i++) {
stream.readUnsignedByte ();
}
System.out.print (" " + n + " ...");
}
System.out.println ();
}
else {
System.out.println (leading (os, 8) + os + ": APP0 " +
length + " ...");
for (int i=2; i<length; i++) {
stream.readUnsignedByte ();
}
}
os += length;
}
else if (code >= 0xe1 && code <= 0xef) {
int n = code - 0xe0;
/*
int length = markerSegment (stream, bigEndian);
*/
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
if ((n == 1 || n == 2) && length >= 8) {
String id = readChars (stream, 4);
int NULL = stream.readUnsignedByte ();
int padding = stream.readUnsignedByte ();
if (id.equals ("Exif") || id.equals ("FPXR")) {
System.out.println (leading (os, 8) + os +
": APP" + n + " \"" + id +
"\" " + NULL + " " + padding +
" " + (length-8) + ": ...");
}
else {
System.out.println (leading (os, 8) + os +
": APP" + n + " " + length +
" ...");
}
for (int i=8; i<length; i++) {
stream.readUnsignedByte ();
}
}
else {
System.out.println (leading (os, 8) + os + ": APP" +
n + " " + length + " ...");
for (int i=2; i<length; i++) {
stream.readUnsignedByte ();
}
}
os += length;
}
else if (code == 0xf0) {
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
int v = stream.readUnsignedByte ();
int rev = stream.readUnsignedByte ();
System.out.print (leading (os, 8) + os + ": VER " +
v + "." + rev);
int n = length - 4;
if (n > 0) {
int [] cap = new int [n];
for (int i=0; i<n; i++) {
cap[i] = stream.readUnsignedByte ();
System.out.print (((i==0) ? " " : ",") + cap[i]);
}
}
System.out.println ();
os += length;
}
else if (code == 0xf1) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DTI " +
length + " ...");
os += length;
}
else if (code == 0xf2) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DTT " +
length + " ...");
os += length;
}
else if (code == 0xf3) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": SRF " +
length + " ...");
os += length;
}
else if (code == 0xf4) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": SRS " +
length + " ...");
os += length;
}
else if (code == 0xf5) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DCR " +
length + " ...");
os += length;
}
else if (code == 0xf6) {
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": DQS " +
length + " ...");
os += length;
}
else if (code >= 0xf7 && code <= 0xfd) {
int n = code - 0xf0;
int length = markerSegment (stream, bigEndian);
System.out.println (leading (os, 8) + os + ": JPG" + n +
" " + length + " ...");
os += length;
}
else if (code == 0xfe) {
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
String comment = readChars (stream, length-2);
System.out.println (leading (os, 8) + os + ": COM \"" +
comment + "\"");
os += length;
}
else {
int length = markerSegment (stream, bigEndian);
String hex = Integer.toHexString (code);
System.out.println (leading (os, 8) + os + ": RES (0x" +
leading (hex, 2) + hex + ") " +
length + " ...");
}
os += 2;
}
stream.close ();
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (-2);
}
}
/**
* Read marker segment data
* @param stream Data input stream
* @param bigEndian True if big-endian
* @return Length of marker segment
*/
private static int markerSegment (DataInputStream stream,
boolean bigEndian)
throws IOException
{
int length = ModuleBase.readUnsignedShort (stream,
bigEndian,
null);
for (int i=2; i<length; i++) {
stream.readUnsignedByte ();
}
return length;
}
}
-313
View File
@@ -1,313 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004-2007 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
//import java.io.*;
import java.util.*;
public class Jhove
{
/******************************************************************
* PRIVATE CLASS FIELDS.
******************************************************************/
/** Application name. */
private static final String NAME = "Jhove";
/** Application build date, YYYY, MM, DD. */
private static int [] DATE;
/** Application release number. */
private static String RELEASE;
/** Application invocation syntax. */
private static final String USAGE = "java " + NAME + " [-c config] " +
"[-m module] [-h handler] [-e encoding] [-H handler] [-o output] " +
"[-x saxclass] [-t tempdir] [-b bufsize] [-l loglevel] [[-krs] " +
"dir-file-or-uri [...]]";
/** Copyright information. */
private static final String RIGHTS =
"Derived from software Copyright 2004-2011 " +
"by the President and Fellows of Harvard College. " +
"Version 1.7 and higher independently released. " +
"Released under the GNU Lesser General Public License.";
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
public static void main (String [] args)
{
RELEASE = JhoveBase._release; // possibly safer than final static init
DATE = JhoveBase.DATE;
/* Make sure we have a satisfactory version of Java. */
String version = System.getProperty ("java.vm.version");
if (version.compareTo ("1.5.0") < 0) {
//System.err.println (NAME + ": Java 1.4 or higher is required");
System.out.println ("Java 1.5 or higher is required");
System.exit (-1);
}
try {
/**********************************************************
* Initialize the application state object.
**********************************************************/
App app = new App (NAME, RELEASE, DATE, USAGE, RIGHTS);
/**********************************************************
* Retrieve the configuration file.
**********************************************************/
String configFile = JhoveBase.getConfigFileFromProperties ();
String saxClass = JhoveBase.getSaxClassFromProperties ();
/* Pre-parse the command line for -c and -x config options.
* With Windows, we have to deal with quote marks on our own. With Unix,
* the shell takes care of quotes for us. */
boolean quoted = false;
for (int i=0; i<args.length; i++) {
if (quoted) {
int len = args[i].length ();
if (args[i].charAt (len-1) == '"') {
quoted = false;
}
}
else {
if (args[i].equals ("-c")) {
if (i < args.length-1) {
configFile = args[++i];
}
}
else if (args[i].equals ("-x")) {
if (i <args.length-1) {
saxClass = args[++i];
}
}
else if (args[i].charAt (0) == '"') {
quoted = true;
}
}
}
/**********************************************************
* Initialize the JHOVE engine.
**********************************************************/
String encoding = null;
String tempDir = null;
int bufferSize = -1;
String moduleName = null;
String handlerName = null;
String aboutHandler = null;
String logLevel = "SEVERE";
String outputFile = null;
boolean checksum = false;
boolean showRaw = false;
boolean signature = false;
List<String> list = new ArrayList<String> ();
/**********************************************************
* Parse command line arguments:
* -m module Module name
* -h handler Output handler
* -e encoding Output encoding
* -H handler About handler
* -o output Output file pathname
* -t tempdir Directory for temp files
* -b bufsize Buffer size for buffered I/O
* -k Calculate checksums
* -r Display raw numeric flags
* -s Check internal signatures only
* dirFileOrUri Directories, file pathnames, or URIs
*
* The following arguments were defined in previous
* versions, but are now obsolete
* -p param OBSOLETE
* -P param OBSOLETE
**********************************************************/
quoted = false;
StringBuffer filename = null;
for (int i=0; i<args.length; i++) {
if (quoted) {
int len = args[i].length ();
if (args[i].charAt (len-1) == '"') {
filename.append (" " + args[i].substring (0, len-1));
list.add (filename.toString ());
quoted = false;
}
else {
filename.append (" " + args[i]);
}
}
else {
if (args[i].equals ("-c")) {
i++;
}
else if (args[i].equals ("-m")) {
if (i < args.length-1) {
moduleName = args[++i];
}
}
else if (args[i].equals ("-p")) {
// Obsolete -- but eat the next arg for compatibility
if (i <args.length-1) {
@SuppressWarnings("unused")
String moduleParam = args[++i];
}
}
else if (args[i].equals ("-h")) {
if (i < args.length-1) {
handlerName = args[++i];
}
}
else if (args[i].equals ("-P")) {
// Obsolete -- but eat the next arg for compatibility
if (i <args.length-1) {
@SuppressWarnings("unused")
String handlerParam = args[++i];
}
}
else if (args[i].equals ("-e")) {
if (i < args.length-1) {
encoding = args[++i];
}
}
else if (args[i].equals ("-H")) {
if (i < args.length-1) {
aboutHandler = args[++i];
}
}
else if (args[i].equals ("-l")) {
if (i < args.length-1) {
logLevel = args[++i];
}
}
else if (args[i].equals ("-o")) {
if (i < args.length-1) {
outputFile = args[++i];
}
}
else if (args[i].equals ("-x")) {
i++;
}
else if (args[i].equals ("-t")) {
if (i <args.length-1) {
tempDir = args[++i];
}
}
else if (args[i].equals ("-b")) {
if (i <args.length-1) {
try {
bufferSize = Integer.parseInt (args[++i]);
}
catch (NumberFormatException e) {
System.err.println ("Invalid buffer size, using default.");
}
}
}
else if (args[i].equals ("-k")) {
checksum = true;
}
else if (args[i].equals ("-r")) {
showRaw = true;
}
else if (args[i].equals ("-s")) {
signature = true;
}
else if (args[i].charAt (0) != '-') {
if (args[i].charAt (0) == '"') {
filename = new StringBuffer ();
filename.append (args[i].substring (1));
quoted = true;
}
else {
list.add (args[i]);
}
}
}
}
if (quoted) {
list.add (filename.toString ());
}
JhoveBase je = new JhoveBase ();
je.setLogLevel (logLevel);
je.init (configFile, saxClass);
if (encoding == null) {
encoding = je.getEncoding ();
}
if (tempDir == null) {
tempDir = je.getTempDirectory ();
}
if (bufferSize < 0) {
bufferSize = je.getBufferSize ();
}
Module module = je.getModule (moduleName);
if (module == null && moduleName != null) {
System.out.println ("Module '" + moduleName + "' not found");
System.exit (-1);
}
OutputHandler about = je.getHandler (aboutHandler);
if (about == null && aboutHandler != null) {
System.out.println ("Handler '" + aboutHandler + "' not found");
System.exit (-1);
}
OutputHandler handler = je.getHandler (handlerName);
if (handler == null && handlerName != null) {
System.out.println ("Handler '" + handlerName + "' not found");
System.exit (-1);
}
String [] dirFileOrUri = null;
int len = list.size ();
if (len > 0) {
dirFileOrUri = new String [len];
for (int i=0; i<len; i++) {
dirFileOrUri[i] = (String) list.get (i);
}
}
/**********************************************************
* Invoke the JHOVE engine.
**********************************************************/
je.setEncoding (encoding);
je.setTempDirectory (tempDir);
je.setBufferSize (bufferSize);
je.setChecksumFlag (checksum);
je.setShowRawFlag (showRaw);
je.setSignatureFlag (signature);
je.dispatch (app, module, about, handler, outputFile,
dirFileOrUri);
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (-1);
}
}
}
-229
View File
@@ -1,229 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004-2008 by JSTOR and the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import edu.harvard.hul.ois.jhove.viewer.*;
import javax.swing.*;
/**
* JhoveView - JSTOR/Harvard Object Validation Environment.
*/
public class JhoveView
{
/******************************************************************
* PRIVATE CLASS FIELDS.
*
* Application constants.
******************************************************************/
/** Application name. */
private static final String NAME = "JhoveView";
/** Application build date, YYYY, MM, DD. */
private static int [] _date;
/** Usage string is meaningless here. */
private static final String USAGE = null;
/** Application release number. */
private static String _release;
/** Default character encoding */
//private static final String DEFAULT_ENCODING = "UTF-8";
/** Copyright information. */
private static final String RIGHTS =
"Derived from software Copyright 2004-2011 " +
"by the President and Fellows of Harvard College. " +
"Version 1.7 and higher independently released. " +
"Released under the GNU Lesser General Public License.";
/******************************************************************
* Configuration constants.
******************************************************************/
/** Configuration file property. */
// private static final String CONFIG_PROPERTY = "edu.harvard.hul.ois." +
// "jhove.config";
/** Jhove directory */
// private static final String JHOVE_DIR = "jhove";
/** Config directory */
// private static final String CONFIG_DIR = "conf";
/** SAX parser class property. */
// private static final String SAX_PROPERTY = "edu.harvard.hul.ois.jhove." +
// "saxClass";
/******************************************************************
* Action constants.
******************************************************************/
/******************************************************************
* Exit code constants.
******************************************************************/
/** General error. */
private static final int ERROR = -1;
/** Incompatible Java VM. */
private static final int INCOMPATIBLE_VM = -2;
/** File not writable. */
//private static final int FILE_NOT_WRITABLE = -11;
/** No module specified. */
//private static final int NO_MODULE = -21;
/** Module not found. */
//private static final int MODULE_NOT_FOUND = -22;
/** Output handler not found. */
//private static final int HANDLER_NOT_FOUND = -31;
/** No object specified. */
//private static final int NO_OBJECT = -41;
/** URL not accessible. */
//private static final int URL_NOT_ACCESSIBLE = -42;
/** File not found. */
//private static final int FILE_NOT_FOUND = -43;
/** File not readable. */
//private static final int FILE_NOT_READABLE = -44;
/** SAX parser not found. */
//private static final int PARSER_NOT_FOUND = -51;
/******************************************************************
* PUBLIC CLASS METHODS.
******************************************************************/
/**
* Stub constructor.
*/
private JhoveView ()
{
}
/**
* Application main entry point.
* @parm args Command line arguments
*/
public static void main (String [] args)
{
_release = JhoveBase._release; // Seems safer than final init
_date = JhoveBase.DATE;
/* Make sure we have a satisfactory version of Java. */
String version = System.getProperty ("java.vm.version");
if (version.compareTo ("1.4.0") < 0) {
//System.err.println (NAME + ": Java 1.4 or higher is required");
errorAlert ("Java 1.4 or higher is required");
System.exit (INCOMPATIBLE_VM);
}
// If we're running on a Macintosh, put the menubar at the top
// of the screen where it belongs.
System.setProperty ("apple.laf.useScreenMenuBar", "true");
App app = new App (NAME, _release, _date, USAGE, RIGHTS);
try {
/**********************************************************
* Retrieve the configuration file.
**********************************************************/
String configFile = JhoveBase.getConfigFileFromProperties ();
String saxClass = JhoveBase.getSaxClassFromProperties ();
/**********************************************************
* Initialization:
* configFile Configuration file pathname
* saxClass SAX parser class
**********************************************************/
/* Pre-parse the command line for -c and -x config options. */
boolean quoted = false;
for (int i=0; i<args.length; i++) {
if (quoted) {
int len = args[i].length ();
if (args[i].charAt (len-1) == '"') {
quoted = false;
}
}
else {
if (args[i].equals ("-c")) {
if (i < args.length-1) {
configFile = args[++i];
}
}
else if (args[i].equals ("-x")) {
if (i <args.length-1) {
saxClass = args[++i];
}
}
else if (args[i].charAt (0) == '"') {
quoted = true;
}
}
}
/**********************************************************
* Initialize the JHOVE engine.
**********************************************************/
JhoveBase je = new JhoveBase ();
try {
je.init (configFile, saxClass);
}
catch (JhoveException e) {
errorAlert (e.getMessage ());
// Keep going, so user can correct in editor
}
// Create the main window to select a file.
JhoveWindow jwin = new JhoveWindow (app, je);
jwin.setVisible (true);
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (ERROR);
}
}
/* Displays an error alert. */
private static void errorAlert (String msg)
{
JFrame hiddenFrame = new JFrame ();
// Truncate long messages so the alert isn't wider
// than the screen
if (msg.length() > 80) {
msg = msg.substring (0, 79) + "...";
}
JOptionPane.showMessageDialog (hiddenFrame,
msg, "Jhove Error", JOptionPane.ERROR_MESSAGE);
}
}
-115
View File
@@ -1,115 +0,0 @@
/**********************************************************************
* PDump - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import edu.harvard.hul.ois.jhove.module.pdf.*;
import java.io.*;
/**
* Dump contents of PDF file in human-readable format.
*/
public class PDump
extends Dump
{
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java PDump file");
System.exit (-1);
}
try {
RandomAccessFile file = new RandomAccessFile (args[0], "r");
Tokenizer tokenizer = new FileTokenizer (file);
Token token = null;
long offset = 0;
while ((token = tokenizer.getNext ()) != null) {
System.out.print (leading (offset, 8) + offset + ": ");
if (token instanceof ArrayEnd) {
System.out.println ("ArrayEnd");
}
else if (token instanceof ArrayStart) {
System.out.println ("ArrayStart");
}
else if (token instanceof Comment) {
System.out.println ("Comment \"" +
((Comment) token).getValue () + "\"");
}
else if (token instanceof DictionaryEnd) {
System.out.println ("DictionaryEnd");
}
else if (token instanceof DictionaryStart) {
System.out.println ("DictionaryStart");
}
// else if (token instanceof Hexadecimal) {
// System.out.println ("Hexadecimal[" +
// (((Hexadecimal) token).isPDFDocEncoding () ?
// "PDF" : "UTF-16") + "] \"" +
// ((Hexadecimal) token).getValue () +
// "\"");
// }
else if (token instanceof Keyword) {
System.out.println ("Keyword \"" +
((Keyword) token).getValue () + "\"");
}
else if (token instanceof Literal) {
System.out.println ("Literal[" +
(((Literal)token).isPDFDocEncoding () ?
"PDF" : "UTF-16") + "] \"" +
((Literal) token).getValue () + "\"");
}
else if (token instanceof Name) {
System.out.println ("Name \"" +
((Name) token).getValue () + "\"");
}
else if (token instanceof Numeric) {
Numeric numeric = (Numeric) token;
if (numeric.isReal ()) {
System.out.println ("Numeric " + numeric.getValue ());
}
else {
System.out.println ("Numeric " +
numeric.getIntegerValue ());
}
}
else if (token instanceof Stream) {
System.out.println ("Stream " +
((Stream) token).getLength ());
}
else {
System.out.println (token);
}
offset = tokenizer.getOffset ();
}
}
catch (Exception e) {
e.printStackTrace (System.err);
System.exit (-2);
}
}
}
-361
View File
@@ -1,361 +0,0 @@
/**********************************************************************
* TDump - JSTOR/Harvard Object Validation Environment
* Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import edu.harvard.hul.ois.jhove.module.tiff.*;
import java.io.*;
import java.util.*;
/**
* Dump contents of TIFF file in human-readable format.
*/
public class TDump
extends Dump
{
/******************************************************************
* PRIVATE CLASS FIELDS.
******************************************************************/
/** Count of IFDs. */
private static int _nIFDs;
/** Sorted associative map of tags. */
private static Map _tags;
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java TDump [-bs] file");
System.exit (-1);
}
String tiff = null;
boolean nobyte = false;
boolean nosub = false;
for (int i=0; i<args.length; i++) {
if (args[i].equals ("-b")) {
nobyte = true;
}
else if (args[i].equals ("-s")) {
nosub = true;
}
else if (tiff == null) {
tiff = args[i];
}
}
/* Accumlate all tag definitions in a sorted map. */
_tags = new TreeMap ();
int err = 0;
try {
/* Read TIFF header. */
RandomAccessFile file = new RandomAccessFile (tiff, "r");
boolean bigEndian = true;
int b1 = ModuleBase.readUnsignedByte (file);
int b2 = ModuleBase.readUnsignedByte (file);
if (b1 == 0x49 && b2 == b1) {
bigEndian = false;
}
int magic = ModuleBase.readUnsignedShort (file, bigEndian);
long offset = ModuleBase.readUnsignedInt (file, bigEndian);
_tags.put ("00000000", "\"" + ((char) b1) + ((char) b2) + "\" (" +
(bigEndian ? "big" : "little") + " endian) " + magic +
" LONG @" + offset);
/* Read IFDs. */
_nIFDs = 0;
while ((offset = readIFD (file, bigEndian, offset, nobyte,
nosub)) > 0) {
}
file.close ();
}
catch (Exception e) {
e.printStackTrace (System.out);
err = -2;
}
finally {
/* Display all tags in offset-sorted order. */
Iterator iter = _tags.keySet ().iterator ();
while (iter.hasNext ()) {
String os = (String) iter.next ();
System.out.println (os + ": " + (String) _tags.get (os));
}
if (err != 0) {
System.exit (err);
}
}
}
/**
* Read IFDs.
* @param file Open TIFF file
* @param bigEndian True if big-endian
* @param offset Byte offset of IFD
* @param nobyte If true, only display the first 8 bytes of data of
* type BYTE
* @param nosub If true, do not parse subIFDs
*/
private static long readIFD (RandomAccessFile file, boolean bigEndian,
long offset, boolean nobyte, boolean nosub)
throws Exception
{
int nIFD = ++_nIFDs;
List subIFDs = new ArrayList ();
List stripByteCounts = new ArrayList ();
List stripOffsets = new ArrayList ();
file.seek (offset);
int nEntries = ModuleBase.readUnsignedShort (file, bigEndian);
_tags.put (leading (offset, 8) + offset, "IFD " + nIFD + " with " +
nEntries + " entries");
String name = null;
for (int i=0; i<nEntries; i++) {
long os = offset + 2 + i*12;
file.seek (os);
int tag = ModuleBase.readUnsignedShort (file, bigEndian);
int type = ModuleBase.readUnsignedShort (file, bigEndian);
long count = ModuleBase.readUnsignedInt (file, bigEndian);
StringBuffer buffer = new StringBuffer (tag + " (" +
TiffTags.tagName(tag) +
") " + IFD.TYPE[type] +
" " + count);
if (type == IFD.ASCII) {
if (count > 4) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
StringBuffer ascii = new StringBuffer ();
for (int j=0; j<count-1; j++) {
ascii.append ((char) ModuleBase.readUnsignedByte (file));
}
buffer.append (" = \"" + ascii.toString () + "\"");
}
else if (type == IFD.BYTE ||
type == IFD.UNDEFINED) {
if (count > 4) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
long ct = count;
if (nobyte && count > 8) {
ct = 8;
}
for (int j=0; j<ct; j++) {
long by = ModuleBase.readUnsignedByte (file);
buffer.append (" " + by);
}
if (nobyte && count > 8) {
buffer.append (" ...");
}
}
else if (type == IFD.DOUBLE) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo + " =");
for (int j=0; j<count; j++) {
double db = ModuleBase.readDouble (file, bigEndian);
buffer.append (" " + db);
}
}
else if (type == IFD.FLOAT) {
if (count > 1) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
for (int j=0; j<count; j++) {
float fl = ModuleBase.readFloat (file, bigEndian);
buffer.append (" " + fl);
}
}
else if (type == IFD.LONG || type == IFD.IFD) {
if (count > 1) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo + " = ");
}
else if (tag == 330 || /* Sub IFD */
tag == 34665 || /* EXIF IFD */
tag == 34853 || /* EXIF GPS IFD */
tag == 40965) { /* EXIF Interoperability IFD */
buffer.append (" @");
}
else {
buffer.append (" = ");
}
for (int j=0; j<count; j++) {
long in = ModuleBase.readUnsignedInt (file, bigEndian);
if (j > 0) {
buffer.append (" ");
}
buffer.append (in);
if (tag == 330 || /* Sub IFD */
tag == 34665 || /* EXIF IFD */
tag == 34853 || /* EXIF GPS IFD */
tag == 40965) { /* EXIF Interoperability IFD */
subIFDs.add (new Long (in));
}
else if (tag == 273) {
stripOffsets.add (new Long (in));
}
else if (tag == 279) {
stripByteCounts.add (new Long (in));
}
}
}
else if (type == IFD.RATIONAL) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo + " =");
for (int j=0; j<count; j++) {
long numer = ModuleBase.readUnsignedInt (file, bigEndian);
long denom = ModuleBase.readUnsignedInt (file, bigEndian);
buffer.append (" " + numer + "/" + denom);
}
}
else if (type == IFD.SBYTE) {
if (count > 4) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
for (int j=0; j<count; j++) {
long by = ModuleBase.readSignedByte (file);
buffer.append (" " + by);
}
}
else if (type == IFD.SHORT) {
if (count > 2) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
for (int j=0; j<count; j++) {
int sh = ModuleBase.readUnsignedShort (file, bigEndian);
buffer.append (" " + sh);
if (tag == 273) {
stripOffsets.add (new Long ((long) sh));
}
else if (tag == 279) {
stripByteCounts.add (new Long ((long) sh));
}
}
}
else if (type == IFD.SLONG) {
if (count > 1) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
for (int j=0; j<count; j++) {
int in = ModuleBase.readSignedInt (file, bigEndian);
buffer.append (" " + in);
}
}
else if (type == IFD.SRATIONAL) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo + " =");
for (int j=0; j<count; j++) {
long numer = ModuleBase.readSignedInt (file, bigEndian);
long denom = ModuleBase.readSignedInt (file, bigEndian);
buffer.append (" " + numer + "/" + denom);
}
}
else if (type == IFD.SSHORT) {
if (count > 2) {
long vo = ModuleBase.readUnsignedInt (file, bigEndian);
file.seek (vo);
buffer.append (" @" + vo);
}
buffer.append (" =");
for (int j=0; j<count; j++) {
int sh = ModuleBase.readSignedShort (file, bigEndian);
buffer.append (" " + sh);
}
}
_tags.put (leading (os, 8) + os, buffer.toString ());
}
if (!nosub) {
for (int i=0; i<subIFDs.size (); i++) {
long os = ((Long) subIFDs.get (i)).longValue ();
while ((os = readIFD (file, bigEndian, os, nobyte, nosub)) > 0) {
}
}
}
long os = offset + 2 + nEntries*12;
file.seek (os);
long next = ModuleBase.readUnsignedInt (file, bigEndian);
_tags.put (leading (os, 8) + os, "NextIFDOffset LONG @" + next);
int len = stripOffsets.size ();
if (len > 0) {
for (int j=0; j<len; j++) {
long start = ((Long) stripOffsets.get (j)).longValue ();
long count = ((Long) stripByteCounts.get (j)).longValue ();
long finish= start + count - 1;
_tags.put (leading (start, 8) + start, "(Image " + nIFD +
",strip " + (j+1) + ") IMAGEDATA " + count +
": ... " + leading (finish, 8) + finish);
}
}
return next;
}
}
-33
View File
@@ -1,33 +0,0 @@
/**********************************************************************
* UserHome - JSTOR/Harvard Object Validation Environment
* Copyright 2006 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import java.io.*;
/**
* Determine the default value of the Java user.home property.
*/
public class UserHome
{
public static void main (String [] args)
{
System.out.println ("Default user.home is \"" +
System.getProperty ("user.home") + "\"");
}
}
-196
View File
@@ -1,196 +0,0 @@
/**********************************************************************
* JDump - JSTOR/Harvard Object Validation Environment
* Copyright 2004-2005 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
import edu.harvard.hul.ois.jhove.*;
import java.io.*;
/**
* Dump contents of WAVE file in human-readable format.
*
* @author Gary McGath
*
*/
public class WDump extends Dump {
/* Fixed value for first 4 bytes */
private static final int[] sigByte =
{ 0X52, 0X49, 0X46, 0X46 };
private static final boolean ENDIAN = false; /* little endian */
/******************************************************************
* MAIN ENTRY POINT.
******************************************************************/
/**
* Main entry point.
* @param args Command line arguments
*/
public static void main (String [] args)
{
if (args.length < 1) {
System.err.println ("usage: java WDump file");
System.exit (-1);
}
try {
FileInputStream file = new FileInputStream (args[0]);
BufferedInputStream buffer = new BufferedInputStream (file);
DataInputStream stream = new DataInputStream (buffer);
ADump dump = new ADump (); // Just to access contained classes
long os = 0;
for (int i=0; i<4; i++) {
int ch;
ch = stream.readUnsignedByte();
if (ch != sigByte[i]) {
System.out.println ("No AIFF FORM header");
System.exit (-2);
}
os++;
}
long ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN);
os += 4;
// Read the file type
String formType = read4Chars (stream);
os += 4;
System.out.println ("00000000: RIFF " + ckSize + ": " + formType);
boolean endOfFile = false;
while (!endOfFile) {
// Read chunks
try {
// Read chunk name.
String ckID = read4Chars (stream);
// Read size (excluding chunk name and size fields)
ckSize = ModuleBase.readUnsignedInt (stream, ENDIAN);
System.out.print (leading (os, 8) + os + ": " + ckID +
" " + ckSize);
long alreadyRead = 0;
if (ckID.equals ("fact")) {
long sampleLength =
ModuleBase.readUnsignedInt (stream, ENDIAN);
System.out.print (": " + sampleLength);
alreadyRead = 4;
}
else if (ckID.equals ("fmt ")) {
int formatTag = ModuleBase.readUnsignedShort (stream,
ENDIAN);
int channels = ModuleBase.readUnsignedShort (stream,
ENDIAN);
long samplesPerSec =
ModuleBase.readUnsignedInt (stream, ENDIAN);
long avgBytesPerSec =
ModuleBase.readUnsignedInt (stream, ENDIAN);
int blockAlign = ModuleBase.readUnsignedShort (stream,
ENDIAN);
String hex = Integer.toHexString (formatTag);
System.out.print (": 0x" + leading (hex, 4) + hex +
" " + channels + " " +
samplesPerSec + " " +
avgBytesPerSec + " " + blockAlign);
alreadyRead = 14;
if (ckSize > 14) {
int bitsPerSample =
ModuleBase.readUnsignedShort (stream, ENDIAN);
System.out.print (" " + bitsPerSample);
alreadyRead = 16;
if (ckSize > 16) {
int size =
ModuleBase.readUnsignedShort (stream,
ENDIAN);
System.out.print (" " + size);
alreadyRead = 18;
if (size == 22) {
int validBitsPerSample =
ModuleBase.readUnsignedShort (stream,
ENDIAN);
long channelMask =
ModuleBase.readUnsignedInt (stream,
ENDIAN);
hex = Long.toHexString (channelMask);
System.out.print (" " +
validBitsPerSample +
" 0x" +
leading (hex, 8) + hex +
" 0x");
for (int i=0; i<4; i++) {
long guid =
ModuleBase.readUnsignedInt (
stream,
ENDIAN);
hex = Long.toHexString (guid);
System.out.print (leading (hex, 8) +
hex);
}
alreadyRead = 40;
}
}
}
}
System.out.println ();
if (ckID.equals ("list") || ckID.equals ("LIST")) {
readNestedChunks (ckID, stream, ckSize, os + 8);
}
else {
stream.skipBytes ((int) (ckSize - alreadyRead));
}
os += ckSize + 8;
}
catch (EOFException e) {
endOfFile = true;
}
}
}
catch (Exception e) {
}
}
/* The "list" and "LIST" chunks (which are two distinct chunk types)
hold nested chunks. */
private static void readNestedChunks
(String ckID, DataInputStream stream, long ckSize, long os)
throws IOException
{
String listType = read4Chars (stream);
System.out.println ("List type = " + listType);
while (ckSize > 0) {
String subCkID = read4Chars (stream);
long subCkSize = ModuleBase.readUnsignedInt (stream, ENDIAN, null);
System.out.println (leading (os, 8) + os + ": " +
ckID + "/" + subCkID + " " + subCkSize);
stream.skipBytes ((int) subCkSize);
os += subCkSize + 8;
ckSize -= subCkSize + 8;
}
}
private static String read4Chars (DataInputStream stream)
throws IOException
{
StringBuffer sbuf = new StringBuffer(4);
for (int i = 0; i < 4; i++) {
int ch = ModuleBase.readUnsignedByte(stream, null);
sbuf.append((char) ch);
}
return sbuf.toString ();
}
}
-128
View File
@@ -1,128 +0,0 @@
<project name="Jhove" default="main-dist" basedir=".">
<description>Main build file
Jhove - JSTOR/Harvard Object Validation Environment
Version 1.1 2006-02-13
Copyright 2004-2006 by JSTOR and the President and Fellows of Harvard College
</description>
<!-- set global properties for this build -->
<property name="classpath" value=".:../bin/jhove.jar"/>
<property name="api" location="edu/harvard/hul/ois/jhove"/>
<property name="viewer" location="edu/harvard/hul/ois/jhove/viewer"/>
<property name="root" location=".."/>
<property name="manifest" location="${viewer}/META-INF"/>
<target name="main-init" description="Create the time stamp">
<tstamp>
<format property="timestamp.isoformat" pattern="yyyy-MM-dd'T'HH:mm:ss"/>
</tstamp>
</target>
<target name="main-dist" depends="api,main,view"/>
<target name="api" description="Compile the API">
<ant dir="${api}" inheritAll="false">
<property name="dbg" value="${dbg}"/>
</ant>
</target>
<target name="main-compile" depends="main-init"
description="Compile the main file">
<javac compiler="javac1.5" target="1.5" srcdir="." debug="${dbg}" classpath="${classpath}"/>
</target>
<target name="main" depends="main-compile, app-manifest"
description="Build the app jar">
<jar destfile="${root}/bin/JhoveApp.jar" basedir="." index="true"
manifest="./META-INF/MANIFEST.MF"
includes="Jhove.class,
GDump.class, JDump.class, PDump.class, TDump.class,
J2Dump.class,J2Dump$Box.class,
ADump.class,
WDump.class, UserHome.class,
edu/harvard/hul/ois/jhove/*.class,
edu/harvard/hul/ois/jhove/*.xsd,
edu/harvard/hul/ois/jhove/handler/*.class,
edu/harvard/hul/ois/jhove/handler/audit/*.class,
edu/harvard/hul/ois/jhove/module/*.class,
edu/harvard/hul/ois/jhove/module/aiff/*.class,
edu/harvard/hul/ois/jhove/module/gif/*.class,
edu/harvard/hul/ois/jhove/module/html/*.class,
edu/harvard/hul/ois/jhove/module/html/*.dtd,
edu/harvard/hul/ois/jhove/module/html/*.ent,
edu/harvard/hul/ois/jhove/module/iff/*.class,
edu/harvard/hul/ois/jhove/module/jpeg/*.class,
edu/harvard/hul/ois/jhove/module/jpeg2000/*.class,
edu/harvard/hul/ois/jhove/module/pdf/*.class,
edu/harvard/hul/ois/jhove/module/tiff/*.class,
edu/harvard/hul/ois/jhove/module/wave/*.class,
edu/harvard/hul/ois/jhove/module/xml/*.class"/>
</target>
<target name="app-manifest" depends="main-init">
<mkdir dir="${manifest}"/>
<manifest file="./META-INF/MANIFEST.MF">
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Built-On" value="${timestamp.isoformat}"/>
<attribute name="Main-Class" value="Jhove"/>
<section name="JHOVE">
<attribute name="Version" value="1.0"/>
<attribute name="Package" value="Jhove app"/>
<attribute name="Rights" value="Copyright 2004-2006 by JSTOR and the President and Fellows of Harvard College"/>
</section>
</manifest>
</target>
<target name="view-manifest" depends="main-init">
<mkdir dir="${manifest}"/>
<manifest file="${manifest}/MANIFEST.MF">
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Built-On" value="${timestamp.isoformat}"/>
<attribute name="Main-Class" value="JhoveView"/>
<section name="JHOVE">
<attribute name="Version" value="1.0"/>
<attribute name="Package" value="Swing Viewer"/>
<attribute name="Rights" value="Copyright 2004-2006 by JSTOR and the President and Fellows of Harvard College"/>
</section>
</manifest>
</target>
<target name="view" description="Compile the viewer application"
depends="api,main,view-manifest">
<jar destfile="${root}/bin/JhoveView.jar" basedir="." index="true"
manifest="${manifest}/MANIFEST.MF"
includes="JhoveView.class,
edu/harvard/hul/ois/jhove/*.class,
edu/harvard/hul/ois/jhove/handler/*.class,
edu/harvard/hul/ois/jhove/handler/audit/*.class,
edu/harvard/hul/ois/jhove/module/*.class,
edu/harvard/hul/ois/jhove/module/aiff/*.class,
edu/harvard/hul/ois/jhove/module/gif/*.class,
edu/harvard/hul/ois/jhove/module/html/*.class,
edu/harvard/hul/ois/jhove/module/html/*.dtd,
edu/harvard/hul/ois/jhove/module/html/*.ent,
edu/harvard/hul/ois/jhove/module/iff/*.class,
edu/harvard/hul/ois/jhove/module/jpeg/*.class,
edu/harvard/hul/ois/jhove/module/jpeg2000/*.class,
edu/harvard/hul/ois/jhove/module/pdf/*.class,
edu/harvard/hul/ois/jhove/module/tiff/*.class,
edu/harvard/hul/ois/jhove/module/wave/*.class,
edu/harvard/hul/ois/jhove/module/xml/*.class,
edu/harvard/hul/ois/jhove/viewer/*.gif,
edu/harvard/hul/ois/jhove/viewer/*.class"/>
</target>
<target name="main-clean">
<delete>
<fileset dir="." includes="*~ \#*"/>
</delete>
<ant dir="${api}" target="api-clean" inheritAll="false"/>
</target>
<target name="main-cleanclass">
<delete>
<fileset dir="." includes="*.class*"/>
</delete>
<ant dir="${api}" target="api-cleanclass" inheritAll="false"/>
</target>
</project>
@@ -1,940 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004-2005 by JSTOR and the President and Fellows of Harvard College
*
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.util.*;
/**
* Encapsulation of the AES Metadata for Audio documents
*
* @author Gary McGath
*
*/
public class AESAudioMetadata
{
/******************************************************************
* PUBLIC CLASS FIELDS.
******************************************************************/
/** Big-endian constant. */
public static final int BIG_ENDIAN = 0;
/** Little-endian constant. */
public static final int LITTLE_ENDIAN = 1;
/** Analog / digital labels. */
public static final String [] A_D = {
"ANALOG", "PHYS_DIGITAL", "FILE_DIGITAL"
};
/** Values for primary identifier type */
public static final String
FILE_NAME = "FILE_NAME",
OTHER = "OTHER";
/** Constant for an undefined integer value. */
public static final int NULL = -1;
/** Constant for an undefined floating-point value. */
public static final double NILL = -1.0;
/******************************************************************
* PRIVATE INSTANCE FIELDS.
*
******************************************************************/
/** Constant value for the SchemaVersion field */
public static final String SCHEMA_VERSION = "1.02b";
/** Constant value for the disposition field */
private static final String DEFAULT_DISPOSITION = "validation";
private String _analogDigitalFlag;
private String _appSpecificData;
private String _audioDataEncoding;
private int _byteOrder;
private String _disposition;
private List _faceList;
private long _firstSampleOffset;
private String _format;
private List _formatList;
private int _numChannels;
private String _primaryIdentifier;
private String _primaryIdentifierType;
private String _primaryIdentifierOtherType;
private String _schemaVersion;
private String _specificationVersion;
private String[] _use;
/* Most recently added FormatRegion */
private FormatRegion _curFormatRegion;
/* Most recently added Face */
private Face _curFace;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/** Instantiate a <code>NisoImageMetadata</code> object.
*/
public AESAudioMetadata ()
{
_schemaVersion = SCHEMA_VERSION;
_disposition = DEFAULT_DISPOSITION;
_analogDigitalFlag = null;
_format = null;
_specificationVersion = null;
_audioDataEncoding = null;
_primaryIdentifier = null;
_primaryIdentifierType = null;
_use = null;
// We add one format region to get started. In practice,
// that one is all we're likely to need. but more can be
// added if necessary.
_formatList = new LinkedList ();
_faceList = new LinkedList ();
addFormatRegion ();
addFace ();
_numChannels = NULL;
_byteOrder = NULL;
_firstSampleOffset = NULL;
}
/******************************************************************
* PUBLIC STATIC INTERFACES.
*
******************************************************************/
/**
* Public interface to the nested FormatRegion object. Instances
* of this should be created only by addFormatRegion, but can be
* accessed through the public methods of this interface.
*/
public static interface FormatRegion {
/** Returns the bit depth. */
public int getBitDepth ();
/** Returns the bitrate reduction (compression information).
* This will be an array of seven strings (which may be
* empty, but should never be null) interpreted as follows:
* <ul>
* <li>0: codecName
* <li>1: codecNameVersion
* <li>2: codecCreatorApplication
* <li>3: codecCreatorApplicationVersion
* <li>4: codecQuality
* <li>5: dataRate
* <li>6: dataRateMode
* </ul>
*/
public String[] getBitrateReduction ();
/** Returns the sample rate. */
public double getSampleRate ();
/** Returns the word size. */
int getWordSize ();
/** Returns <code>true</code> if the region is empty. */
public boolean isEmpty ();
/** Sets the bit depth value. */
public void setBitDepth (int bitDepth);
/** Sets the bitrate reduction information to null (no compression). */
public void clearBitrateReduction ();
/** Sets the bitrate reduction (aka compression type). */
public void setBitrateReduction (String codecName,
String codecNameVersion,
String codecCreatorApplication,
String codecCreatorApplicationVersion,
String codecQuality,
String dataRate,
String dataRateMode);
/** Sets the sample rate. */
public void setSampleRate (double sampleRate);
/** Sets the word size. */
public void setWordSize (int wordSize);
}
/**
* Public interface to the nested TimeDesc object. Instances
* of this should be created only by appropriate methods, but can be
* accessed through the public methods of this interface.
*/
public static interface TimeDesc {
/** Returns the hours component. */
public int getHours ();
/** Returns the minutes component. */
public int getMinutes ();
/** Returns the seconds component. */
public int getSeconds ();
/** Returns the frames component of the fraction of a second.
* We always consider frames to be thirtieths of a second. */
public int getFrames ();
/** Returns the samples remaining after the frames part of
* the fractional second. */
public int getSamples ();
/** Returns the sample rate on which the samples remainder
* is based. */
public double getSampleRate ();
}
/** Public interface to the nested Face object. Instances
* of this should be created only by appropriate methods, but can be
* accessed through the public methods of this interface. */
public static interface Face {
/** Returns an indexed FaceRegion. */
public FaceRegion getFaceRegion (int i);
/** Adds a FaceRegion. This may be called repeatedly to
* add multiple FaceRegions. */
public void addFaceRegion ();
/** Returns the starting time. */
public TimeDesc getStartTime ();
/** Returns the duration. */
public TimeDesc getDuration ();
/** Returns the direction. */
public String getDirection ();
/** Sets the starting time. This will be converted
* into a TimeDesc. */
public void setStartTime (long samples);
/** Sets the duration. This will be converted
* into a TimeDesc. */
public void setDuration (long samples);
/** Sets the direction. This must be one of the
* directionTypes. FORWARD is recommended for most
* or all cases.
*/
public void setDirection (String direction);
/* End of interface Face */
}
/** Public interface to the nested FaceRegion object. Instances
* of this should be created only by appropriate methods, but can be
* accessed through the public methods of this interface. */
public static interface FaceRegion {
/** Returns the starting time. */
public TimeDesc getStartTime ();
/** Returns the duration. */
public TimeDesc getDuration ();
/** Returns the channel map locations. The array length must
* equal the number of channels. */
public String[] getMapLocations ();
/** Sets the starting time. */
public void setStartTime (long samples);
/** Sets the duration. */
public void setDuration (long samples);
/** Sets the channel map locations. The array length must
* equal the number of channels. */
public void setMapLocations (String[] locations);
/* End of interface FaceRegion */
}
/******************************************************************
* STATIC MEMBER CLASSES.
*
******************************************************************/
/** The implementation of the FormatRegion interface. The combination
* of a public interface and a private implementation is suggested
* in _Java in a Nutshell_.
*/
class FormatRegionImpl implements FormatRegion {
private int _bitDepth;
private double _sampleRate;
private int _wordSize;
private String[] _bitrateReduction;
public FormatRegionImpl () {
_bitDepth = NULL;
_sampleRate = NILL;
_wordSize = NULL;
_bitrateReduction = null;
}
/** Returns bit depth. */
public int getBitDepth ()
{
return _bitDepth;
}
/** Returns the bitrate reduction (compression information).
* This will be an array of seven strings (which may be
* empty but not null) interpreted respectively as follows:
* <ul>
* <li>0: codecName
* <li>1: codecNameVersion
* <li>2: codecCreatorApplication
* <li>3: codecCreatorApplicationVersion
* <li>4: codecQuality
* <li>5: dataRate
* <li>6: dataRateMode
* </ul>
*/
public String[] getBitrateReduction ()
{
return _bitrateReduction;
}
/** Returns sample rate. */
public double getSampleRate ()
{
return _sampleRate;
}
/** Returns word size. */
public int getWordSize ()
{
return _wordSize;
}
/** Returns true if the FormatRegion contains only
* default values. */
public boolean isEmpty ()
{
return _bitDepth == NULL &&
_sampleRate == NILL &&
_wordSize == NULL;
}
/** Sets bit depth. */
public void setBitDepth (int bitDepth)
{
_bitDepth = bitDepth;
}
/** Sets the bitrate reduction information to null (no compression). */
public void clearBitrateReduction ()
{
_bitrateReduction = null;
}
/** Sets the bitrate reduction (compression type). */
public void setBitrateReduction (String codecName,
String codecNameVersion,
String codecCreatorApplication,
String codecCreatorApplicationVersion,
String codecQuality,
String dataRate,
String dataRateMode)
{
_bitrateReduction = new String[7];
_bitrateReduction[0] = codecName;
_bitrateReduction[1] = codecNameVersion;
_bitrateReduction[2] = codecCreatorApplication;
_bitrateReduction[3] = codecCreatorApplicationVersion;
_bitrateReduction[4] = codecQuality;
_bitrateReduction[5] = dataRate;
_bitrateReduction[6] = dataRateMode;
}
/** Sets sample rate. */
public void setSampleRate (double sampleRate)
{
_sampleRate = sampleRate;
}
/** Sets word size. */
public void setWordSize (int wordSize)
{
_wordSize = wordSize;
}
/* End of FormatRegionImpl */
}
/** The implementation of the TimeDesc interface. The combination
* of a public interface and a private implementation is suggested
* in _Java in a Nutshell_.
*/
class TimeDescImpl implements TimeDesc
{
private int _hours;
private int _minutes;
private int _seconds;
private int _frames;
private int _samples;
private double _sampleRate;
private int _frameCount;
/* Constructor rewritten to avoid rounding errors when converting to
* TCF. Now uses integer remainder math instead of floating point.
* Changed the base unit from a double representing seconds to a long
* representing samples. Changed all existing calls (that I could find)
* to this method to accomodate this change.
*
* @author David Ackerman
*/
public TimeDescImpl (long samples)
{
long _sample_count = samples;
_frameCount = 30;
_sampleRate = _curFormatRegion.getSampleRate ();
/* It seems that this method is initially called before a valid
* sample rate has been established, causing a divide by zero
* error.
*/
if (_sampleRate < 0) {
_sampleRate = 44100.0; //reasonable default value
}
long sample_in_1_frame = (long)(_sampleRate/_frameCount);
long sample_in_1_second = sample_in_1_frame * _frameCount;
long sample_in_1_minute = sample_in_1_frame * _frameCount * 60;
long sample_in_1_hour = sample_in_1_frame * _frameCount * 60 * 60;
long sample_in_1_day = sample_in_1_frame * _frameCount * 60 * 60 * 24;
// BWF allows for a negative timestamp but tcf does not, so adjust
// time accordingly
// this might be a good place to report a warning during validation
if (_sample_count < 0) {
_sample_count += sample_in_1_day;
_sample_count = (_sample_count % sample_in_1_day);
}
_hours = (int)(_sample_count / sample_in_1_hour);
_sample_count -= (_hours * sample_in_1_hour);
_minutes = (int)(_sample_count / sample_in_1_minute);
_sample_count -= (_minutes * sample_in_1_minute);
_seconds = (int)(_sample_count / sample_in_1_second);
_sample_count -= (_seconds * sample_in_1_second);
_frames = (int)(_sample_count / sample_in_1_frame);
_sample_count -= (_frames * sample_in_1_frame);
_samples = (int)_sample_count;
/* At present TCF does not have the ability to handle time stamps
* > midnight. Industry practice is to roll the clock forward to
* zero or back to 23:59:59:29... when crossing this boundary
* condition.
*/
_hours = _hours % 24;
}
/** Returns the hours component. */
public int getHours () {
return _hours;
}
/** Returns the minutes component. */
public int getMinutes () {
return _minutes;
}
/** Returns the seconds component. */
public int getSeconds () {
return _seconds;
}
/** Returns the frames component of the fraction of a second.
* We always consider frames to be thirtieths of a second. */
public int getFrames () {
return _frames;
}
/** Returns the samples remaining after the frames part of
* the fractional second. */
public int getSamples () {
return _samples;
}
/** Returns the sample rate on which the samples remainder
* is based. */
public double getSampleRate () {
return _sampleRate;
}
} /* End of TimeDescImpl */
/** The implementation of the Face interface. The combination
* of a public interface and a private implementation is suggested
* in _Java in a Nutshell_.
*/
class FaceImpl implements Face {
List _regionList;
TimeDesc _startTime;
TimeDesc _duration;
String _direction;
/** Constructor. Initially the duration is set
* to null, indicating unknown value. */
public FaceImpl ()
{
_regionList = new ArrayList ();
_startTime = new TimeDescImpl (0);
_duration = null;
}
/** Returns an indexed FaceRegion. */
public FaceRegion getFaceRegion (int i) {
return (FaceRegion) _regionList.get (i);
}
/** Adds a FaceRegion. This may be called repeatedly to
* add multiple FaceRegions. */
public void addFaceRegion () {
_regionList.add (new FaceRegionImpl ());
}
/** Returns the starting time. Will be zero if not
* explicitly specified. */
public TimeDesc getStartTime () {
return _startTime;
}
/** Returns the duration. May be null if the duration
* is unspecified. */
public TimeDesc getDuration () {
return _duration;
}
/** Returns the direction. */
public String getDirection ()
{
return _direction;
}
/** Sets the starting time. This will be converted
* into a TimeDesc. */
public void setStartTime (long samples)
{
_startTime = new TimeDescImpl (samples);
}
/** Sets the duration. This will be converted
* into a TimeDesc. */
public void setDuration (long samples)
{
_duration = new TimeDescImpl (samples);
}
/** Sets the direction. This must be one of the
* directionTypes. FORWARD is recommended for most
* or all cases.
*/
public void setDirection (String direction)
{
_direction = direction;
}
/* End of FaceImpl */
}
/** The implementation of the Face interface. The combination
* of a public interface and a private implementation is suggested
* in _Java in a Nutshell_.
*/
class FaceRegionImpl implements FaceRegion {
private TimeDesc _startTime;
private TimeDesc _duration;
private String[] _mapLocations;
public FaceRegionImpl ()
{
_startTime = new TimeDescImpl (0);
_duration = null;
}
/** Returns the starting time. */
public TimeDesc getStartTime () {
return _startTime;
}
/** Returns the duration. */
public TimeDesc getDuration () {
return _duration;
}
/** Returns the channel map locations. The array length
* will equal the number of channels. */
public String[] getMapLocations ()
{
return _mapLocations;
}
/** Sets the duration. This will be converted
* into a TimeDesc. */
public void setStartTime (long samples) {
_startTime = new TimeDescImpl (samples);
}
/** Sets the duration. This will be converted
* into a TimeDesc. */
public void setDuration (long samples)
{
_duration = new TimeDescImpl (samples);
}
/** Sets the channel map locations. The array length must
* equal the number of channels. */
public void setMapLocations (String[] locations)
{
_mapLocations = locations;
}
/* End of FaceRegionImpl */
}
/* End of inner classes */
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/** Returns analog/digital flag. Value will always be
* "FILE_DIGITAL" in practice. */
public String getAnalogDigitalFlag ()
{
return _analogDigitalFlag;
}
/** Returns application-specific data. We assume this is
* representable in String format.
*/
public String getAppSpecificData ()
{
return _appSpecificData;
}
/** Returns audio data encoding. */
public String getAudioDataEncoding ()
{
return _audioDataEncoding;
}
/** Returns the bitrate reduction (compression information).
* This will be an array of seven strings (which may be
* empty, but should never be null) interpreted as follows:
* <ul>
* <li>0: codecName
* <li>1: codecNameVersion
* <li>2: codecCreatorApplication
* <li>3: codecCreatorApplicationVersion
* <li>4: codecQuality
* <li>5: dataRate
* <li>6: dataRateMode
* </ul>
*/
public String[] getBitrateReduction ()
{
return _curFormatRegion.getBitrateReduction();
}
/* Returns the sample rate. */
public double getSampleRate ()
{
return _curFormatRegion.getSampleRate ();
}
/** Return the byte order: 0 = big-endian; 1 = little-endian. */
public int getByteOrder ()
{
return _byteOrder;
}
/** Returns disposition. */
public String getDisposition ()
{
return _disposition;
}
/** Gets the list of Faces. Normally there will be only one face
* in a digital file. */
public List getFaceList ()
{
return _faceList;
}
/** Return the offset of the first byte of sample data. */
public long getFirstSampleOffset ()
{
return _firstSampleOffset;
}
/** Returns format name. */
public String getFormat ()
{
return _format;
}
/** Gets the list of Format Regions. Since one is created
* automatically on initialization, it's possible that the
* list will contain a Format Region with only default values.
* This should be checked with isEmpty ().
*/
public List getFormatList ()
{
return _formatList;
}
/** Returns the names of the map locations.
* The returned
* value is an array whose length equals the number of
* channels and whose elements correspond to channels 0, 1,
* etc.
*/
public String[] getMapLocations() {
return _curFace.getFaceRegion(0).getMapLocations();
}
/** Returns number of channels. */
public int getNumChannels ()
{
return _numChannels;
}
/** Returns primary identifier. */
public String getPrimaryIdentifier ()
{
return _primaryIdentifier;
}
/** Returns primary identifier type. */
public String getPrimaryIdentifierType ()
{
return _primaryIdentifierType;
}
/** Returns schema version. */
public String getSchemaVersion ()
{
return _schemaVersion;
}
/** Returns specification version of the document format. */
public String getSpecificationVersion ()
{
return _specificationVersion;
}
/** Returns the use (role of the document).
* The value returned is an array of two strings,
* the useType and the otherType. */
public String[] getUse ()
{
return _use;
}
/******************************************************************
* Mutator methods.
******************************************************************/
/** Sets the analog/digital flag. The value set should always
* be "FILE_DIGITAL". */
public void setAnalogDigitalFlag (String flagType)
{
_analogDigitalFlag = flagType;
}
/** Sets the bitrate reduction (compression type). */
public void setBitrateReduction (String codecName,
String codecNameVersion,
String codecCreatorApplication,
String codecCreatorApplicationVersion,
String codecQuality,
String dataRate,
String dataRateMode)
{
_curFormatRegion.setBitrateReduction (codecName,
codecNameVersion, codecCreatorApplication,
codecCreatorApplicationVersion,
codecQuality, dataRate, dataRateMode);
}
/** Set the bitrate reduction information to null (no compression). */
public void clearBitrateReduction ()
{
_curFormatRegion.clearBitrateReduction ();
}
/** Sets the byte order.
* @param order Byte order: 0 = big-endian, 1 = little-endian
*/
public void setByteOrder (int order)
{
_byteOrder = order;
}
/** Sets the byte order.
*/
public void setByteOrder (String order)
{
if (order.substring (0, 3).toLowerCase ().equals ("big")) {
_byteOrder = BIG_ENDIAN;
}
else if (order.substring (0, 6).toLowerCase ().equals ("little")) {
_byteOrder = LITTLE_ENDIAN;
}
}
/** Sets the audio data encoding. */
public void setAudioDataEncoding (String audioDataEncoding)
{
_audioDataEncoding = audioDataEncoding;
}
/** Set the application-specific data. For present purposes,
* we assume this is representable as a text string. */
public void setAppSpecificData (String data)
{
_appSpecificData = data;
}
/** Sets the bit depth. */
public void setBitDepth (int bitDepth)
{
_curFormatRegion.setBitDepth (bitDepth);
}
/** Sets the disposition. */
public void setDisposition (String disposition)
{
_disposition = disposition;
}
/** Sets the direction.
* This must be one of the values
* FORWARD, REVERSE, A_WIND, B_WIND, C_WIND, D_WIND,
* FRONT, BACK. FORWARD may be the only one that
* makes sense for digital formats.
*/
public void setDirection (String direction)
{
_curFace.setDirection (direction);
}
/** Sets the duration in samples.
* This affects the current face and its first FaceRegion.
*/
public void setDuration (long duration)
{
_curFace.setDuration (duration);
_curFace.getFaceRegion(0).setDuration (duration);
}
/** Sets the offset of the first byte of sample data. */
public void setFirstSampleOffset (long offset)
{
_firstSampleOffset = offset;
}
/** Sets the format name. */
public void setFormat (String format)
{
_format = format;
}
/** Sets the array of channel map locations. The length
* of the array must equal the number of channels. */
public void setMapLocations (String[] locations) {
_curFace.getFaceRegion(0).setMapLocations (locations);
}
/** Sets the number of channels. */
public void setNumChannels (int numChannels)
{
_numChannels = numChannels;
}
/** Sets the primary identifier. */
public void setPrimaryIdentifier (String primaryIdentifier)
{
_primaryIdentifier = primaryIdentifier;
}
/** Sets the primary identifier type. If the primary identifier
* type is OTHER, use setOtherPrimaryIdentifierType instead.
*/
public void setPrimaryIdentifierType (String primaryIdentifierType)
{
_primaryIdentifierType = primaryIdentifierType;
}
/** Sets the primary identifier type as "OTHER", and
* set the otherType.
*/
public void setOtherPrimaryIdentifierType (String otherType)
{
_primaryIdentifierType = "OTHER";
_primaryIdentifierOtherType = otherType;
}
/** Sets the sample rate. */
public void setSampleRate (double sampleRate)
{
_curFormatRegion.setSampleRate (sampleRate);
}
/** Sets the specification version of the document format.*/
public void setSpecificationVersion (String specificationVersion)
{
_specificationVersion = specificationVersion;
}
/** Sets the start time in samples.
* This affects the current face and its first FaceRegion.
*/
public void setStartTime (long samples)
{
_curFace.setStartTime (samples);
_curFace.getFaceRegion(0).setStartTime (samples);
}
/** Sets the role of the document. Permitted values are
* ORIGINAL_MASTER, PRESERVATION_MASTER, PRODUCTION_MASTER,
* SERVICE, PREVIEW, or OTHER.
* If useType is "OTHER", then otherType
* is significant. Since OTHER is the only meaningful
* value for a digital document, the code assumes this will always
* be the case and uses otherType. */
public void setUse (String useType, String otherType)
{
_use = new String[] {useType, otherType};
}
/** Sets the word size. */
public void setWordSize (int wordSize)
{
_curFormatRegion.setWordSize (wordSize);
}
/** Adds a FormatRegion object to a FormatSize list.
* The most recently added FormatRegion object will
* be filled in by setBitDepth, setSampleRate, and
* setWordSize.
*/
public void addFormatRegion ()
{
_curFormatRegion = new FormatRegionImpl ();
_formatList.add (_curFormatRegion);
}
/** Adds a Face.
*/
public void addFace ()
{
_curFace = new FaceImpl ();
_faceList.add (_curFace);
_curFace.addFaceRegion();
}
}
@@ -1,167 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* Encapsulates information about agents, either individual
* persons or corporate bodies.
*/
public class Agent
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
private String _name;
private AgentType _type;
private String _address;
private String _telephone;
private String _fax;
private String _email;
private String _web;
private String _note;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates an Agent given a name and an AgentType.
*/
public Agent (String name, AgentType type)
{
_name = name;
_type = type;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
******************************************************************/
/**
* Returns the value of the address property.
*/
public String getAddress ()
{
return _address;
}
/**
* Returns the value of the email property.
*/
public String getEmail ()
{
return _email;
}
/**
* Returns the value of the fax property.
*/
public String getFax ()
{
return _fax;
}
/**
* Returns the value of the name property.
*/
public String getName ()
{
return _name;
}
/**
* Returns the value of the note property.
*/
public String getNote ()
{
return _note;
}
/**
* Returns the value of the telephone property.
*/
public String getTelephone ()
{
return _telephone;
}
/**
* Returns the value of the type property.
*/
public AgentType getType ()
{
return _type;
}
/**
* Returns the value of the web property.
*/
public String getWeb ()
{
return _web;
}
/******************************************************************
* Mutator methods.
******************************************************************/
/**
* Sets the value of the address property.
*/
public void setAddress (String address)
{
_address = address;
}
/**
* Sets the value of the email property.
*/
public void setEmail (String email)
{
_email = email;
}
/**
* Sets the value of the fax property.
*/
public void setFax (String fax)
{
_fax = fax;
}
/**
* Sets the value of the name property.
*/
public void setName (String name)
{
_name = name;
}
/**
* Sets the value of the note property.
*/
public void setNote (String note)
{
_note = note;
}
/**
* Sets the value of the telephone property.
*/
public void setTelephone (String telephone)
{
_telephone = telephone;
}
/**
* Sets the value of the web property.
*/
public void setWeb (String web)
{
_web = web;
}
}
@@ -1,60 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for an Agent.
* Applications will not create or modify AgentTypes, but will
* use one of the predefined AgentType instances COMMERCIAL, GOVERNMENT,
* EDUCATIONAL, NONPROFIT, STANDARD, or OTHER.
*
* @see Agent
*/
public final class AgentType
extends EnumerationType
{
/******************************************************************
* PUBLIC STATIC INSTANCES.
******************************************************************/
/**
* Agent type for a commercial entity.
*/
public static final AgentType COMMERCIAL = new AgentType ("Commercial");
/**
* Agent type for a governmental body.
*/
public static final AgentType GOVERNMENT = new AgentType ("Government");
/**
* Agent type for an educational institution.
*/
public static final AgentType EDUCATIONAL = new AgentType ("Educational");
/**
* Agent type for a non-profit organization.
*/
public static final AgentType NONPROFIT = new AgentType ("Non-profit");
/**
* Agent type for a standards body.
*/
public static final AgentType STANDARD = new AgentType ("Standards body");
/**
* Agent type that doesn't fit the other categories.
*/
public static final AgentType OTHER = new AgentType ("Other");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create AgentTypes directly.
**/
private AgentType (String value)
{
super (value);
}
}
@@ -1,40 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
*
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for the analog/digital
* flag of AESAudioMetadata.
* Applications will not create or modify instances of this class, but will
* use one of the predefined AnalogDigitalFlagType instances.
*
* @author Gary McGath
*
*/
public class AnalogDigitalFlagType extends EnumerationType {
/** Enumeration instance for analog data */
public static final AnalogDigitalFlagType ANALOG =
new AnalogDigitalFlagType ("ANALOG");
/** Enumeration instance for physical digital data */
public static final AnalogDigitalFlagType PHYS_DIGITAL =
new AnalogDigitalFlagType ("PHYS_DIGITAL");
/** Enumeration instance for FILE digital data */
public static final AnalogDigitalFlagType FILE_DIGITAL =
new AnalogDigitalFlagType ("FILE_DIGITAL");
/**
* Applications will never create PropertyTypes directly.
**/
private AnalogDigitalFlagType (String value)
{
super (value);
}
}
@@ -1,128 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
//import java.io.*;
import java.util.*;
/**
* The application class for JHOVE. One instance of this class is
* generated by the application. Various utility and informational
* methods are contained here.
*/
public class App
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** Application build date. */
private Date _date;
/** Application name. */
private String _name;
/** Application release identifier. */
private String _release;
/** Application rights statement. */
private String _rights;
/** Application invocation syntax. */
private String _usage;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates an App object.
* @param name Name of the application.
* @param release Release identifier.
* @param date Last modification date of the application code,
* in the form of an array of three numbers.
* <code>date[0]</code> is the year,
* <code>date[1]</code> the month, and
* <code>date[2]</code> the day.
* @param usage String summarizing command line usage, to
* output when app name is typed with no
* arguments or flags.
* @param rights Copyright or other rights statement.
*/
public App (String name, String release, int [] date, String usage,
String rights)
{
Calendar calendar = new GregorianCalendar ();
calendar.set (date[0], date[1]-1, date[2]);
_name = name;
_release = release;
_date = calendar.getTime ();
_usage = usage;
_rights = rights;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/**
* Returns application code creation date
*/
public Date getDate ()
{
return _date;
}
/**
* Returns the name of this application
*/
public String getName ()
{
return _name;
}
/**
* Returns the release identifier for this application.
*/
public String getRelease ()
{
return _release;
}
/**
* Returns the rights string.
*/
public String getRights ()
{
return _rights;
}
/**
* Returns the usage string, which should be output when the user
* enters a command line with only the application name.
*/
public String getUsage ()
{
return _usage;
}
/******************************************************************
* Serialization methods.
******************************************************************/
/**
* Outputs detailed information about the application,
* including configuration, available modules and handlers,
* etc.
*/
public void show (OutputHandler handler)
{
handler.show (this);
}
}
@@ -1,66 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
*
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
/**
* Class for providing an InputSource to XMPHandler,
* with a ByteArrayInputStream as the basis of the
* InputSource. This is suitable for a number of modules.
*
* @author Gary McGath
*
*/
public class ByteArrayXMPSource extends XMPSource {
/* The underlying ByteArrayInputStream. */
ByteArrayInputStream _instrm;
/**
* Constructor based on ByteArrayInputStream.
*
* @param instrm ByteArrayInputStream containing the XMP
*/
public ByteArrayXMPSource (ByteArrayInputStream instrm)
throws IOException
{
super (new InputStreamReader
(new XMLWrapperStream (instrm)));
_instrm = instrm;
// Prepare for resetting.
instrm.mark (instrm.available ());
}
/**
* Constructor based on ByteArrayInputStream with encoding.
*
* @param instrm ByteArrayInputStream containing the XMP
*/
public ByteArrayXMPSource (ByteArrayInputStream instrm,
String encoding)
throws IOException
{
super (new InputStreamReader
(new XMLWrapperStream (instrm), encoding));
_instrm = instrm;
// Prepare for resetting.
instrm.mark (instrm.available ());
}
/* (non-Javadoc)
* @see edu.harvard.hul.ois.jhove.XMPSource#resetReader()
*/
protected void resetReader() {
_instrm.reset ();
_reader = new InputStreamReader (_instrm);
}
}
@@ -1,25 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* An interface for supporting a general, simple callback function.
*/
public interface Callback
{
/**
* A generic callback function. Any class which needs to support
* callback can implement callback and pass a reference to itself
* to the function that does the callback.
*
* @param selector An indicator of the function to be performed.
* Interpretation is determined by the implementing class.
* @param parm Whatever data may be appropriate to the callback.
*
* @return As specified by the implementing class.
*/
public int callback (int selector, Object parm);
}
@@ -1,93 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates the result of calculations which provide a greater
* or lesser degree of confirmation of the integrity of a digital
* object's content, including checksums, CRC's, message digests,
* etc.
*
* @see ChecksumType
* @see Checksummer
*/
public class Checksum
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
private ChecksumType _type;
private String _value;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates a Checksum with a given value and type
*/
public Checksum (String value, ChecksumType type)
{
_value = value;
_type = type;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/**
* Returns this Checksum's type
*/
public ChecksumType getType ()
{
return _type;
}
/**
* Returns this Checksum's value
*/
public String getValue ()
{
return _value;
}
/******************************************************************
* Mutator methods.
******************************************************************/
/**
* Sets the type of this Checksum
*/
public void setType (ChecksumType type)
{
_type = type;
}
/**
* Sets the value of this Checksum
*/
public void setValue (String value)
{
_value = value;
}
/******************************************************************
* Put here as a convenience for checksum calculators
*****************************************************************/
/**
* Maps unsigned byte value (0 to 256) to signed byte value (-128 to 127).
*/
public static byte unsignedByteToByte (int value)
{
return (byte) ((value < 127) ? value : value - 256);
}
}
@@ -1,145 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.IOException;
import java.io.*;
/**
* A ChecksumInputStream is a FilterInputStream with the added
* functionality of calculating checksums as it goes.
*
* The idea of replacing this with java.util.zip.CheckedInputStream
* looks very tempting, but we need the byte count, which
* CheckedInputStream doesn't provide.
*
* @author Gary McGath
*
*/
public class ChecksumInputStream extends FilterInputStream {
private InputStream subsumedStream;
private Checksummer _cksummer;
private long _nBytes;
/**
* Constructor.
*
* @param stream Stream to be filtered
* @param cksummer Object to calculate checksum on the bytes
* as they are read
*/
public ChecksumInputStream(InputStream stream, Checksummer cksummer) {
super (stream);
subsumedStream = stream;
_cksummer = cksummer;
_nBytes = 0;
}
/**
* Reads a byte from the subsumed stream, updating
* the byte count and the checksums.
*/
public int read() throws IOException {
int ch = subsumedStream.read ();
if (ch >= 0) {
_nBytes++;
if (_cksummer != null) {
_cksummer.update (ch);
}
}
return ch;
}
/**
* Reads some number of bytes from the input stream and
* stores them into the buffer array b. The number of
* bytes actually read is returned as an integer.
*
* All bytes read are fed through the checksummer.
*/
public int read(byte[] b) throws IOException
{
int len = subsumedStream.read (b);
// Careful here -- don't want to add -1 bytes at EOF
if (len > 0) {
if (_cksummer != null) {
_cksummer.update (b);
}
_nBytes += len;
}
return len;
}
/**
* Reads up to len bytes of data from the input stream
* into an array of bytes. An attempt is made to read as
* many as len bytes, but a smaller number may be read,
* possibly zero. The number of bytes actually read is
* returned as an integer.
*
* All bytes read are fed through the checksummer.
*/
public int read(byte[] b, int off, int len) throws IOException
{
len = subsumedStream.read (b, off, len);
// Careful here -- don't want to add -1 bytes at EOF
if (len > 0) {
if (_cksummer != null) {
_cksummer.update (b, off, len);
}
_nBytes += len;
}
return len;
}
/**
* Skips n bytes.
* Reads them and feeds them through the checksummer.
*/
public long skip (long n) throws IOException {
long nret = 0;
while (n > 0) {
// grab the data in reasonable buffer-sized chunks.
int bufsize = (int) (n > 8192 ? 8192 : n);
byte[] buf = new byte[bufsize];
int nread = read (buf);
if (nread <= 0) {
break;
}
nret += nread;
n -= nread;
}
return nret;
}
/**
* Closes the subsumed stream.
*/
public void close () throws IOException
{
subsumedStream.close();
}
/**
* Returns the byte count to date on the stream.
* This returns the number of bytes read.
* Because of buffering, this is not a reliable
* indicator of how many bytes have actually been processed.
*/
public long getNBytes ()
{
return _nBytes;
}
/**
* Returns the Checksummer object.
*/
public Checksummer getChecksummer ()
{
return _cksummer;
}
}
@@ -1,37 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for a Checksum on a content
* stream or file.
* Applications will not create or modify ChecksumTypes, but will
* use one of the predefined ChecksumType instances
* CRC32, MD5, or SHA1.
*
* @see Checksum
*/
public final class ChecksumType
extends EnumerationType
{
/** 32-bit Cyclical Redundancy Checksum. */
public static final ChecksumType CRC32 = new ChecksumType ("CRC32");
/** 128-bit Message Digest 5. */
public static final ChecksumType MD5 = new ChecksumType ("MD5");
/** 160-bit Secure Hash Algorithm. */
public static final ChecksumType SHA1 = new ChecksumType ("SHA-1");
/**
* Applications will never create ChecksumTypes directly.
**/
private ChecksumType (String value)
{
super (value);
}
}
@@ -1,193 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-2006 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.security.*;
import java.util.zip.*;
/**
* The Checksummer class encapsulates the calculation of the
* CRC32, MD5, and SHA-1 checksums.
*/
public class Checksummer implements java.util.zip.Checksum
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** Byte count. */
protected long _nByte;
/** CRC32 object. */
private CRC32 _crc32;
/** MD5 message digest. */
private MessageDigest _md5;
/** SHA-1 message digest. */
private MessageDigest _sha1;
/**
* Creates a Checksummer, with instances of each of
* CRC32, MD5 MessageDigest, and SHA-1 MessageDigest.
* If one or both of the MessageDigests aren't supported
* on the current platform, they are left as null.
*
* @see CRC32
* @see MessageDigest
*/
public Checksummer ()
{
reset ();
}
/** Resets all checksums and the byte count to their
* initial values.
*/
public void reset ()
{
_nByte = 0;
_crc32 = new CRC32 ();
try {
_md5 = MessageDigest.getInstance ("MD5");
_sha1 = MessageDigest.getInstance ("SHA-1");
}
catch (NoSuchAlgorithmException e) {
}
}
/** getValue is required by the Checksum interface, but
* we can return only one of the three values. We
* return the CRC32 value, since that's the one which
* is guaranteed to be available.
*/
public long getValue ()
{
return _crc32.getValue ();
}
/**
* Updates the checksum with the argument.
* Called when a signed byte is available.
*/
public void update (byte b)
{
_crc32.update (b);
if (_md5 != null) {
_md5.update (b);
}
if (_sha1 != null) {
_sha1.update (b);
}
}
/**
* Updates the checksum with the argument.
* Called when an unsigned byte is available.
*/
public void update (int b)
{
byte sb;
if (b > 127) {
sb = (byte) (b - 256);
}
else {
sb = (byte) b;
}
update (sb);
}
/**
* Updates the checksum with the argument.
* Called when a byte array is available.
*/
public void update (byte[] b)
{
_crc32.update (b);
if (_md5 != null) {
_md5.update (b);
}
if (_sha1 != null) {
_sha1.update (b);
}
}
/**
* Updates the checksum with the argument.
* Called when a byte array is available.
*/
public void update (byte[] b, int off, int len)
{
_crc32.update (b, off, len);
if (_md5 != null) {
_md5.update (b, off, len);
}
if (_sha1 != null) {
_sha1.update (b, off, len);
}
}
/**
* Returns the value of the CRC32 as a hex string.
*/
public String getCRC32 ()
{
return padLeadingZeroes
(Long.toHexString (_crc32.getValue ()), 8);
}
/**
* Returns the value of the MD5 digest as a hex string.
* Returns null if the digest is not available.
*/
public String getMD5 ()
{
String value = null;
if (_md5 != null) {
StringBuffer buffer = new StringBuffer ();
byte [] digest = _md5.digest ();
for (int i=0; i<digest.length; i++) {
int un = (digest[i] >= 0) ? digest[i] : 256+digest[i];
buffer.append (padLeadingZeroes
(Integer.toHexString (un), 2));
}
value = buffer.toString ();
}
return value;
}
/**
* Returns the value of the SHA-1 digest as a hex string.
* Returns null if the digest is not available.
*/
public String getSHA1 ()
{
String value = null;
if (_sha1 != null) {
StringBuffer buffer = new StringBuffer ();
byte [] digest = _sha1.digest ();
for (int i=0; i<digest.length; i++) {
int un = (digest[i] >= 0) ? digest[i] : 256+digest[i];
buffer.append (padLeadingZeroes
(Integer.toHexString (un), 2));
}
value = buffer.toString ();
}
return value;
}
/** Pad a hexadecimal (or other numeric) string out to
* the specified length with leading zeroes. */
private String padLeadingZeroes (String str, int len)
{
// This is optimized for adding just one leading zero
// or none, which will be the usual case.
while (str.length () < len) {
str = "0" + str;
}
return str;
}
}
@@ -1,395 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import org.xml.sax.*;
/**
* SAX Parser for the configuration file.
*/
public class ConfigHandler
extends org.xml.sax.helpers.DefaultHandler
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
private String _class;
protected StringBuffer _content;
private Map<String, String> _extension;
/** The schema name */
private final static String configSchemaName =
"jhoveConfig.xsd";
/** The list of handlers. Each element in the List is an
* array of two Strings representing the class and the initialization
* string. */
private List<String[]> _handler;
/** The list of handler parameters. Each element in the List is
* a List of Strings (which may be empty but not null) representing
* parameters to be passed to the module. List elements are in
* one-to-one correspondence with _handler.
*/
protected List<List<String>> _handlerParams;
private String _init;
private List<String> _param;
private String _tempDir;
private String _mixVsn;
private String _encoding;
private String _logLevel;
private int _bufferSize;
private String _jhoveHome;
private int _sigBytes;
protected boolean _isHandler;
/* _isModule is protected rather than private so that subclasses
* can add elements to the module element. */
protected boolean _isModule;
private boolean _isTempDir;
private boolean _isMixVsn;
private boolean _isEncoding;
private boolean _isBufferSize;
private boolean _isJhoveHome;
private boolean _isLogLevel;
private boolean _isSigBytes;
/** The list of modules. Each element in the List is an
* array of two Strings representing the class and the initialization
* string. */
protected List<ModuleInfo> _module;
/** The list of module parameters. Each element in the List is
* a List of Strings (which may be empty but not null) representing
* parameters to be passed to the module. List elements are in
* one-to-one correspondence with _module.
*/
protected List<List<String>> _modParams;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates a ConfigHandler.
*/
public ConfigHandler ()
{
_module = new ArrayList<ModuleInfo> ();
_handler = new ArrayList<String[]> ();
_modParams = new ArrayList<List<String>> ();
_handlerParams = new ArrayList<List<String>> ();
_isModule = false;
_isHandler = false;
_isTempDir = false;
_isEncoding = false;
_isBufferSize = false;
_isJhoveHome = false;
_isLogLevel = false;
_bufferSize = -1;
_encoding = null;
_extension = new Hashtable<String, String> ();
_tempDir = null;
_mixVsn = null;
_sigBytes = 1024;
_logLevel = null;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/**
* Returns the List of Modules specified by the config file.
* Each element of the List is a String[2] whose elements are
* the module class name and initialization value.
*
* @see Module
*/
public List<ModuleInfo> getModule ()
{
return _module;
}
/**
* Returns the List of module parameters specified by the config file.
* Each element of the List is a List (possibly empty) of Strings
* whose elements are parameters to pass to the module. The
* values returned by <code>getModuleParams()</code> are in
* one-to-one correspondence with those return by <code>getModule()</code>.
*/
public List<List<String>> getModuleParams ()
{
return _modParams;
}
/**
* Returns the List of handler parameters specified by the config file.
* Each element of the List is a List (possibly empty) of Strings
* whose elements are parameters to pass to the output handler. The
* values returned by <code>getHandlerParams()</code> are in
* one-to-one correspondence with those return by <code>getHandler()</code>.
*/
public List<List<String>> getHandlerParams ()
{
return _handlerParams;
}
/**
* Returns the List of OutputHandlers specified by the config file.
*
* @see OutputHandler
*/
public List<String[]> getHandler ()
{
return _handler;
}
/**
* Returns the temporary directory path specified by the config file,
* with final path separator.
*/
public String getTempDir ()
{
return _tempDir;
}
/** Returns the MIX schema version specified by the config file.
* Acceptable values are "0.2" and "1.0" and "2.0".
*/
public String getMixVsn ()
{
return _mixVsn;
}
/** Returns the number of bytes to examine when looking for an
* indefinitely positioned signature, or checking the first
* sigBytes bytes of a file in lieu of a signature.
*/
public int getSigBytes ()
{
return _sigBytes;
}
/**
* Returns the character encoding specified by the config file.
*/
public String getEncoding ()
{
return _encoding;
}
/**
* Returns the buffer size specified in the config file.
*
* @return the buffer size, or -1 if none specified
*/
public int getBufferSize ()
{
return _bufferSize;
}
/**
* Return an associative map of configuration extensions.
*/
public Map<String, String> getExtensions ()
{
return _extension;
}
/**
* Returns the path to the application's home directory,
* with final path separator.
*/
public String getJhoveHome ()
{
return _jhoveHome;
}
/**
* Returns the name of the desired log level. This should be the
* name of one of the predefined values of java.util.logging.Level,
* e.g., "WARNING", "INFO", "ALL". The default level is SEVERE.
*/
public String getLogLevel ()
{
return _logLevel;
}
/******************************************************************
* SAX parser methods.
******************************************************************/
/**
* SAX parser callback method.
*/
public void startElement (String namespaceURI, String localName,
String rawName, Attributes atts)
throws SAXException
{
_content = new StringBuffer ();
if (rawName.equals ("module")) {
_isModule = true;
_init = null;
_param = new ArrayList<String> (1);
_class = null;
}
else if (rawName.equals ("outputHandler")) {
_isHandler = true;
_init = null;
_param = new ArrayList<String> (1);
_class = null;
}
else if (rawName.equals ("tempDirectory")) {
_isTempDir = true;
}
else if (rawName.equals ("mixVersion")) {
_isMixVsn = true;
}
else if (rawName.equals ("defaultEncoding")) {
_isEncoding = true;
}
else if (rawName.equals ("bufferSize")) {
_isBufferSize = true;
}
else if (rawName.equals ("jhoveHome")) {
_isJhoveHome = true;
}
else if (rawName.equals ("logLevel")) {
_isLogLevel = true;
}
else if (rawName.equals ("sigBytes")) {
_isSigBytes = true;
}
}
/**
* SAX parser callback method.
*/
public void characters (char [] ch, int start, int length)
throws SAXException
{
_content.append (ch, start, length);
}
/**
* SAX parser callback method.
*/
public void endElement (String namespaceURI, String localName,
String rawName)
throws SAXException
{
if (_isModule) {
if (rawName.equals ("class")) {
_class = _content.toString ();
}
else if (rawName.equals ("init")) {
_init = _content.toString ();
}
else if (rawName.equals ("param")) {
_param.add (_content.toString ());
}
else if (rawName.equals ("module")) {
ModuleInfo modInfo = new ModuleInfo( _class, _init);
_module.add (modInfo);
_modParams.add (_param);
_isModule = false;
}
}
else if (_isHandler) {
if (rawName.equals ("class")) {
_class = _content.toString ();
}
else if (rawName.equals ("init")) {
_init = _content.toString ();
}
else if (rawName.equals ("param")) {
_param.add (_content.toString ());
}
else if (rawName.equals ("outputHandler")) {
String [] tuple = { _class, _init };
_handler.add (tuple);
_handlerParams.add (_param);
_isHandler = false;
}
}
else if (_isTempDir) {
_tempDir = _content.toString ().trim ();
_isTempDir = false;
}
else if (_isMixVsn) {
_mixVsn = _content.toString ().trim ();
_isMixVsn = false;
}
else if (_isSigBytes) {
try {
_sigBytes = Integer.parseInt (_content.toString ().trim ());
}
catch (NumberFormatException e) {}
_isSigBytes = false;
}
else if (_isEncoding) {
_encoding = _content.toString ().trim ();
_isEncoding = false;
}
else if (_isJhoveHome) {
_jhoveHome = _content.toString ().trim ();
_isJhoveHome = false;
}
else if (_isLogLevel) {
_logLevel = _content.toString ().trim ();
_isLogLevel = false;
}
else if (_isBufferSize) {
try {
_bufferSize = Integer.parseInt (_content.toString ().trim ());
}
catch (NumberFormatException e) {
/* Just ignore a malformed number */
}
_isBufferSize = false;
}
else if (!rawName.equals ("jhoveConfig")) {
_extension.put (rawName, _content.toString ().trim ());
}
}
/** EntityResolver designed to locate the config schema. It tries to find it
* as a local resource.
*
* It appears that not all SAX implementations will actually call this
* function for schema resolution, so this isn't a guarantee that the
* schema in the config file won't be called directly. But hopefully
* it will cut down on the burden on the server with the official
* schema copy.
*/
public InputSource resolveEntity (String publicId, String systemId)
throws SAXException, IOException {
if (systemId.endsWith (configSchemaName)) {
try {
URL resURL = this.getClass().getResource("jhoveConfig.xsd");
InputStream strm = resURL.openStream ();
return new InputSource (strm);
}
catch (Exception e) {}
}
// If we couldn't get the local resource, use default location methods
return super.resolveEntity (publicId, systemId);
}
}
@@ -1,212 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
*
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.util.*;
import javax.swing.*;
import edu.harvard.hul.ois.jhove.viewer.ConfigWindow;
/**
*
* Class to write out configuration information to the configuration file.
* To minimize the chance of getting into a bad state, it writes to a
* temporary file, then replaces the old config file with that file,
* rather than directly overwriting the existing file.
*
* @author Gary McGath
*
*/
public class ConfigWriter {
private PrintWriter _confOut;
private File _tempFile;
private File _confFile;
ConfigWindow _parent;
/**
* Constructor.
* Creates a temporary file for writing and creates an OutputStreamWriter
* to write to it. If there is already a file located by
* <code>file</code>, it will not be replaced or overwritten
* until <code>writeFile</code> has successfully written out
* the temporary file.
*
* @param file Location of the configuration file
*
* @param parent The ConfigWindow which invoked this instance.
* May be null if invoked to write a default config file.
*/
public ConfigWriter (File file, ConfigWindow parent) throws IOException
{
_confFile = file;
_parent = parent;
// Set up a temporary file to write to.
String path = file.getParent();
file.getParentFile().mkdirs(); // Make sure the directory exists
_tempFile = File.createTempFile ("jho", ".conf", new File (path));
//_tempFile.createNewFile();
FileOutputStream ostrm = new FileOutputStream (_tempFile);
OutputStreamWriter osw = new OutputStreamWriter (ostrm, "UTF-8");
_confOut = new PrintWriter (osw);
}
/**
* Writes out the content of the file to the temporary file,
* then deletes the existing configuration file (as specified
* by the constructor parameter) and renames the temporary file
* to the configuration file.
*
* If the temporary file can't be written, or the configuration
* file can't be replaced, a warning dialog is put up and the
* configuration file remains unchanged.
*/
public void writeFile (List<ModuleInfo> modules,
List<String[]> handlers,
File homeDir,
File tempDir,
String encoding,
int bufferSize) throws IOException
{
writeHead ();
// Write the home and temp directories. Home must always be valid.
_confOut.println (" <jhoveHome>" +
encodeContent (homeDir.getPath ()) +
"</jhoveHome>");
// Write out the encoding
if (encoding != null && encoding.length() > 0) {
_confOut.println (" <defaultEncoding>" +
encodeContent (encoding) + "</defaultEncoding>");
}
if (tempDir != null) {
_confOut.println (" <tempDirectory>" +
encodeContent (tempDir.getPath ()) +
"</tempDirectory>");
}
// Write the buffer size if not default
if (bufferSize > 0) {
_confOut.println (" <bufferSize>" + bufferSize +
"</bufferSize>");
}
// Write out the modules
ListIterator<ModuleInfo> iter = modules.listIterator ();
while (iter.hasNext ()) {
ModuleInfo minfo = iter.next ();
// The class must be non-null, but init may be null.
// If the class is empty, it's a user error (probably
// clicked "Add" and then lost track of it). Don't
// write it out.
if (!"".equals (minfo.clas)) {
_confOut.println (" <module>");
_confOut.println (" <class>" + encodeContent (minfo.clas) +
"</class>");
if (minfo.init != null && minfo.init.length () > 0) {
_confOut.println (" <init>" + encodeContent (minfo.init) +
"</init>");
}
/** tuple[2] and beyond are parameters */
if (minfo.params != null) {
for (int i = 0; i < minfo.params.length; i++) {
_confOut.println (" <param>" + encodeContent(minfo.params[i]) +
"</param>");
}
}
_confOut.println (" </module>");
}
}
// Write out the handlers
ListIterator<String[]> hiter = handlers.listIterator ();
while (hiter.hasNext ()) {
String handler = hiter.next ()[0];
if (handler.length() > 0) { // Don't write out blank handler names
_confOut.println (" <outputHandler>");
_confOut.println (" <class>" + encodeContent (handler) +
"</class>");
_confOut.println (" </outputHandler>");
}
}
writeTail ();
_confOut.close ();
// Replace the old file with the new.
if (_confFile.exists () && !_confFile.delete ()) {
if (_parent != null) {
JOptionPane.showMessageDialog(_parent,
"Can't replace old config file",
"Error",
JOptionPane.ERROR_MESSAGE);
}
_tempFile.delete ();
}
else {
_tempFile.renameTo (_confFile);
}
}
/* Write the fixed lines which begin the config file */
private void writeHead () throws IOException
{
_confOut.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
_confOut.println("<jhoveConfig version=\"1.0\"");
_confOut.println(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
_confOut.println(" xmlns=\"http://hul.harvard.edu/ois/xml/ns/jhove/jhoveConfig\"");
// Note that the open quote of this line isn't closed till the next line; that's intentional.
_confOut.println("xsi:schemaLocation=\"http://hul.harvard.edu/ois/xml/ns/jhove/jhoveConfig");
_confOut.println(" http://hul.harvard.edu/ois/xml/xsd/jhove/jhoveConfig.xsd\">");
}
/* Write out the fixed end of the config file */
private void writeTail () throws IOException
{
_confOut.println("</jhoveConfig>");
}
/**
* Encodes a content String in XML-clean form, converting characters
* to entities as necessary. The null string will be
* converted to an empty string.
*/
private static String encodeContent (String content)
{
if (content == null) {
content = "";
}
StringBuffer buffer = new StringBuffer (content);
int n = 0;
while ((n = buffer.indexOf ("&", n)) > -1) {
buffer.insert (n+1, "amp;");
n +=5;
}
n = 0;
while ((n = buffer.indexOf ("<", n)) > -1) {
buffer.replace (n, n+1, "&lt;");
n += 4;
}
n = 0;
while ((n = buffer.indexOf (">", n)) > -1) {
buffer.replace (n, n+1, "&gt;");
n += 4;
}
return buffer.toString ();
}
}
@@ -1,114 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
/**
* A FilterInputStream which passes only a specified
* number of bytes, then returns an EOF condition.
*
* @author Gary McGath
*
*/
public class CountedInputStream extends FilterInputStream {
private int bytesLeft;
/**
* @param instrm The InputStream being counted
* @param count The number of bytes to be allowed
*/
public CountedInputStream(InputStream instrm, int count) {
super(instrm);
bytesLeft = count;
}
/** Reads a single byte from the stream and decrements
* the count of remaining bytes. If the count
* is exhausted, returns -1 to signify end of file. */
public int read() throws IOException {
if (bytesLeft <= 0) {
return -1;
}
else {
int ch = super.read ();
if (ch != -1) {
--bytesLeft;
}
return ch;
}
}
/**
* Reads some number of bytes from the input stream and
* stores them into the buffer array b. The number of
* bytes actually read is returned as an integer.
*
* The number of bytes read will not exceed the number
* of bytes remaining in the count. The count is
* decremented by the number of bytes actually read.
*/
public int read(byte[] b) throws IOException
{
int len = b.length;
int bytesRead;
if (len <= bytesLeft) {
// Limit doesn't affect us, do normal read
bytesRead = super.read (b);
}
else {
// Limit the read to bytesLeft
bytesRead = super.read (b, 0, bytesLeft);
}
bytesLeft -= bytesRead;
return bytesRead;
}
/**
* Reads up to len bytes of data from the input stream
* into an array of bytes. An attempt is made to read as
* many as len bytes, but a smaller number may be read,
* possibly zero. The number of bytes actually read is
* returned as an integer.
*
* The number of bytes read will not exceed the number
* of bytes remaining in the count. The count is
* decremented by the number of bytes actually read.
*/
public int read(byte[] b, int off, int len) throws IOException
{
int bytesRead;
if (len <= bytesLeft) {
// Limit doesn't affect us, do normal read
bytesRead = super.read (b, off, len);
}
else {
bytesRead = super.read (b, off, bytesLeft);
}
bytesLeft -= bytesRead;
return bytesRead;
}
/**
* Skips n bytes.
* Decrements the count by the number of bytes
* actually skipped.
*/
public long skip (long n) throws IOException {
long bytesRead = super.skip (n);
if (bytesLeft < bytesRead) {
bytesLeft = 0;
}
else {
bytesLeft -= bytesRead;
}
return bytesRead;
}
}
@@ -1,128 +0,0 @@
package edu.harvard.hul.ois.jhove;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import edu.harvard.hul.ois.jhove.module.*;
/** This class creates a default configuration if no valid configuration file
* is found. */
public class DefaultConfigurationBuilder {
private final static String FILE_SEP = System.getProperty ("file.separator");
private final static String HOME_DIR = System.getProperty ("user.home");
private final static String JHOVE_DIR = HOME_DIR + FILE_SEP + "jhove";
private final static String TEMP_DIR = System.getProperty("java.io.tmpdir");
private final static String DEFAULT_ENCODING = "utf-8";
private final static int DEFAULT_BUFFER_SIZE = 131072;
private File configFile;
/** Constructor. A location for the file may be specified or
* left null, */
public DefaultConfigurationBuilder (File location) {
if (location != null) {
configFile = location;
}
else {
configFile = new File (JHOVE_DIR +
FILE_SEP + "conf" +
FILE_SEP + "jhove.conf");
}
}
public void writeDefaultConfigFile () throws IOException {
// if (TEMP_DEBUG) {
// String configFileName = "null";
// if (configFile != null)
// configFileName = configFile.getAbsolutePath();
// System.out.println ("writeDefaultConfigFile: path is " + configFileName);
// }
ConfigWriter cw = new ConfigWriter (configFile, null);
List<ModuleInfo> modules = getModules();
// TextHandler, XmlHandler, and AuditHandler are loaded by
// default, so there are no handlers to put in the config file.
List<String[]> handlers = new ArrayList<String[]> ();
File homeDir = new File (JHOVE_DIR);
File tempDir = new File (TEMP_DIR);
try {
cw.writeFile(modules, handlers, homeDir, tempDir,
DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
}
catch (IOException e) {
// if (TEMP_DEBUG)
// e.printStackTrace();
throw e;
}
}
// public void writeDefaultConfigFile () throws IOException {
// ConfigWriter cw = new ConfigWriter (configFile, null);
// List<ConfigWriter.ModuleInfo> modules = getModules();
// // TextHandler, XmlHandler, and AuditHandler are loaded by
// // default, so there are no handlers to put in the config file.
// List<String> handlers = new ArrayList<String> ();
// File homeDir = new File (JHOVE_DIR);
// File tempDir = new File (TEMP_DIR);
// cw.writeFile(modules, handlers, homeDir, tempDir,
// DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
// }
public File getConfigFile () {
return configFile;
}
protected List<ModuleInfo> getModules () {
int nModules = builtInModules.length;
ArrayList<ModuleInfo> mods = new ArrayList<ModuleInfo> (nModules);
try {
for (int i = 0; i < nModules; i++) {
Class<?> cls = builtInModules[i];
ModuleInfo minfo = new ModuleInfo(cls.getName());
minfo.init = null; // Never used at present
minfo.params = getDefaultConfigParameters (cls);
mods.add(minfo);
}
}
catch (Exception e) {}
return mods;
}
/** We can't have a static method in an Interface and override it, so we
* have to get a bit ugly to fake static inheritance. The advantage of
* this is that only the Modules with non-null default config file
* parameters have to implement the defaultConfigParams static field. */
protected String[] getDefaultConfigParameters (Class<?> c) {
try {
Field dcpField = c.getField("defaultConfigParams");
return (String[]) dcpField.get(null);
}
catch (Exception e) {
return new String [] {};
}
}
/** The array of build-in modules. If a module is added to or removed from
* the build, this array must be changed. The Bytestream module is
* loaded by default and so is not listed here. */
private Class<?>[] builtInModules = {
AiffModule.class,
AsciiModule.class,
GifModule.class,
HtmlModule.class,
Jpeg2000Module.class,
JpegModule.class,
PdfModule.class,
TiffModule.class,
Utf8Module.class,
WaveModule.class,
XmlModule.class
};
}
@@ -1,194 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.util.*;
/**
* This class encapsulates information about format specification documents.
*
* @see DocumentType
*/
public class Document
{
private List _author;
private String _date;
private String _edition;
private String _enum;
private List _identifier;
private String _note;
private String _pages;
private List _publisher;
private String _title;
private DocumentType _type;
/**
* Creates a Document with a given title and one of the predefined
* DocumentTypes.
*/
public Document (String title, DocumentType type)
{
_title = title;
_type = type;
_author = new ArrayList ();
_identifier = new ArrayList ();
_publisher = new ArrayList ();
}
/**
* Returns a List of Agents, each representing an author of this
* Document.
* Returns an empty list if no authors have been listed.
*
* @see Agent
*/
public List getAuthor ()
{
return _author;
}
/**
* Returns the date of this Document
*/
public String getDate ()
{
return _date;
}
/**
* Returns informaton on the edition of this Document
*/
public String getEdition ()
{
return _edition;
}
/**
* Returns the enumeration (e.g., serial volume and number)
* of this Document
*/
public String getEnumeration ()
{
return _enum;
}
/**
* Returns the list of formal Identifiers for this Document.
* If no Identifiers are given, returns an empty list.
*/
public List getIdentifier ()
{
return _identifier;
}
/**
* Returns the note associated with this Document
*/
public String getNote ()
{
return _note;
}
/**
* Returns pagination information for this Document
*/
public String getPages ()
{
return _pages;
}
/**
* Returns a List of Agents, each representing a publisher of this
* Document. If no publishers are listed, returns an empty list.
*/
public List getPublisher ()
{
return _publisher;
}
/**
* Returns the title of this Document
*/
public String getTitle ()
{
return _title;
}
/**
* Returns one of the predefined DocumentTypes as the type of
* this Document
*/
public DocumentType getType ()
{
return _type;
}
/**
* Adds an author to the list of authors
*/
public void setAuthor (Agent author)
{
_author.add (author);
}
/**
* Sets the date of this Document
*/
public void setDate (String date)
{
_date = date;
}
/**
* Sets edition information for this Document
*/
public void setEdition (String edition)
{
_edition = edition;
}
/**
* Sets enumeration information (e.g., serial volume and number)
* for this Document
*/
public void setEnumeration (String enm)
{
_enum = enm;
}
/**
* Adds an Identifier to the list of identifiers
*/
public void setIdentifier (Identifier identifier)
{
_identifier.add (identifier);
}
/**
* Sets a note giving additional information about this Document
*/
public void setNote (String note)
{
_note = note;
}
/**
* Sets pagination information for this Document
*/
public void setPages (String pages)
{
_pages = pages;
}
/**
* Adds a publisher to the list of publishers
*/
public void setPublisher (Agent publisher)
{
_publisher.add (publisher);
}
}
@@ -1,61 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for a Document.
* Applications will not create or modify DocumentTypes, but will
* use one of the predefined DocumentType instances
* ARTICLE, BOOK, REPORT, RFC, STANDARD, WEB, or OTHER.
*
* @see Document
*
*/
public final class DocumentType
extends EnumerationType
{
/**
* Document type for a printed article.
*/
public static final DocumentType ARTICLE = new DocumentType ("Article");
/**
* Document type for an book.
*/
public static final DocumentType BOOK = new DocumentType ("Book");
/**
* Document type for a report.
*/
public static final DocumentType REPORT = new DocumentType ("Report");
/**
* Document type for an IETF Request for Comment.
*/
public static final DocumentType RFC = new DocumentType ("RFC");
/**
* Document type for a standards body publication.
*/
public static final DocumentType STANDARD = new DocumentType ("Standard");
/**
* Document type for a Web page.
*/
public static final DocumentType WEB = new DocumentType ("Web");
/**
* Document type that doesn't fit the other categories.
*/
public static final DocumentType OTHER = new DocumentType ("Other");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create DocumentTypes directly.
**/
private DocumentType (String value)
{
super (value);
}
}
@@ -1,134 +0,0 @@
/**********************************************************************
* JHOVE - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by the President and Fellows of Harvard College
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
/**
* Common methods for dump utilities.
*/
public class Dump
{
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
public Dump ()
{
}
/******************************************************************
* PUBLIC CLASS METHODS.
******************************************************************/
/**
* Return leading characters to pad out the byte offset to field width.
* @param os Byte offset
* @param width Field width
* @return String of leading zeros
*/
protected static String leading (int os, int width)
{
return leading (os, width, '0');
}
/**
* Return leading characters to pad out the byte offset to field width.
* @param os Byte offset
* @param width Field width
* @return String of leading zeros
*/
protected static String leading (long os, int width)
{
return leading (os, width, '0');
}
/**
* Return leading characters to pad out the byte offset to field width.
* @param os Byte offset
* @param width Field width
* @param pad Padding character
* @return String of leading characters
*/
protected static String leading (int os, int width, char pad)
{
return leading ((long) os, width, pad);
}
/**
* Return leading characters to pad out the byte offset to field width.
* @param os Byte offset
* @param width Field width
* @param pad Padding character
* @return String of leading characters
*/
protected static String leading (long os, int width, char pad)
{
String ss = Long.toString (os);
StringBuffer buffer = new StringBuffer ();
for (int j=0; j<width-ss.length (); j++) {
buffer.append (pad);
}
return buffer.toString ();
}
/**
* Return leading characters to pad out the string to field width.
* @param s String
* @param width Field width
* @return String of leading characters
*/
protected static String leading (String s, int width)
{
return leading (s, width, '0');
}
/**
* Return leading characters to pad out the string to field width.
* @param s String
* @param width Field width
* @param pad Padding character
* @return String of leading characters
*/
protected static String leading (String s, int width, char pad)
{
StringBuffer buffer = new StringBuffer ();
for (int j=0; j<width-s.length (); j++) {
buffer.append (pad);
}
return buffer.toString ();
}
/**
* Read and display a sequence of characters.
* @param stream Data input stream
* @param length Number of characters
* @return Character string
*/
protected static String readChars (DataInputStream stream, int length)
throws IOException
{
byte [] buffer = new byte [length];
for (int i=0; i<length; i++) {
buffer[i] = stream.readByte ();
}
return new String (buffer);
}
}
@@ -1,70 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* Enumerated data type. To create an emumeration, define a subclass
* of EnumerationType with a private constructor, and define as
* many <code>public final static</code> instances of the subclass,
* within the subclass definition, as there are enumeration cases.
* No other instances of an EnumerationType should ever be created,
* and the only operations which should ever be performed on an
* EnumerationType are assignment and equality testing.
*
*/
public abstract class EnumerationType
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** Enumeration value. */
private String _value;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Instantiate an <tt>EnumerationType</tt> object.
* @param value Enumeration value
*/
protected EnumerationType (String value)
{
_value = value;
}
/**
* Private no-argument constructor to close off the default
* constructor.
*/
private EnumerationType ()
{
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
******************************************************************/
/**
* Type equality test.
* @param enm Enumerated type
* @return True, if equal
*/
public boolean equals (EnumerationType enm)
{
return this == enm;
}
/**
* Return enumeration value.
* @return Value
*/
public String toString ()
{
return _value;
}
}
@@ -1,58 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates an error message from a Module, representing
* a problem in the content being analyzed.
*/
public class ErrorMessage
extends Message
{
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Create an ErrorMessage.
* @param message Human-readable string describing the problem.
*/
public ErrorMessage (String message)
{
super (message);
}
/**
* Create an ErrorMessage.
* @param message Human-readable string describing the problem.
* @param offset The offset in the file at which the problem
* was detected.
*/
public ErrorMessage (String message, long offset)
{
super (message, offset);
}
/**
* Create an ErrorMessage.
* @param message Human-readable string describing the problem.
*/
public ErrorMessage (String message, String subMessage)
{
super (message, subMessage);
}
/**
* Create an ErrorMessage.
* @param message Human-readable string describing the problem.
* @param subMessage Human-readable additional information.
* @param offset The offset in the file at which the problem
* was detected.
*/
public ErrorMessage (String message, String subMessage, long offset)
{
super (message, subMessage, offset);
}
}
@@ -1,60 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates information about external format signatures.
* The value of a Signature may be either a String or a byte array
* (stored as an int array to avoid signed byte problems).
*/
public class ExternalSignature
extends Signature
{
/**
* Creates an ExternalSignature given a string value, a type,
* and a use requirement.
*/
public ExternalSignature (String value, SignatureType type,
SignatureUseType use)
{
super (value, type, use);
}
/**
* Creates an ExternalSignature given a byte array, a type,
* and a use requirement.
*/
public ExternalSignature (int[] value, SignatureType type,
SignatureUseType use)
{
super (value, type, use);
}
/**
* Creates an ExternalSignature given a string value, a type,
* a use requirement, and a note.
*/
public ExternalSignature (String value, SignatureType type,
SignatureUseType use,
String note)
{
super (value, type, use, note);
}
/**
* Creates an ExternalSignature given a byte array, a type,
* a use requirement, and a note.
*/
public ExternalSignature (int[] value, SignatureType type,
SignatureUseType use,
String note)
{
super (value, type, use, note);
}
}
@@ -1,812 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.logging.*;
/**
* Abstract base class for Jhove output handlers.
* Output handlers should normally subclass HandlerBase.
*/
public abstract class HandlerBase
implements OutputHandler
{
/******************************************************************
* PUBLIC CLASS FIELDS.
******************************************************************/
/**
* A DateFormat for representing a Date in yyyy-MM-dd
* (e.g., 2003-07-31) format.
*/
public static SynchronizedDateFormat date =
new SynchronizedDateFormat ("yyyy-MM-dd");
/**
* A DateFormat for representing a Date in yyyy-MM-dd HH:mm:ss z
* (e.g., 2003-07-31 15:31:12 EDT) format.
*/
public static SynchronizedDateFormat dateTime =
new SynchronizedDateFormat ("yyyy-MM-dd HH:mm:ss z");
/**
* A DateFormat for representing a Date in ISO 8601
* (e.g., 2003-07-31T15:31:12-0400) format.
* We subclass SimpleDateFormat to make it thread-safe.
*/
public static SynchronizedDateFormat iso8601 =
new SynchronizedDateFormat ("yyyy-MM-dd'T'HH:mm:ssZ");
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** The application object */
protected App _app;
/** The Jhove engine */
protected JhoveBase _base;
/** Handler last modification date */
protected Date _date;
/** Character encoding for writer */
protected String _encoding;
/** Initialization value. */
protected String _init;
/** List of default parameters. */
protected List<String> _defaultParams;
/** JHOVE engine. */
protected JhoveBase _je;
/** Indentation level */
protected int _level;
/** Handler name */
protected String _name;
/** Handler note */
protected String _note;
/** Handler release description. */
protected String _release;
/** Handler-specific parameter. */
protected String _param;
/** Copyright notice */
protected String _rights;
/** Handler specification document list */
protected List<Document> _specification;
/** Handler vendor */
protected Agent _vendor;
/** Writer for doing output */
protected PrintWriter _writer;
/** Logger for a handler class. */
protected Logger _logger;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Constructors of all subclasses of HandlerBase should call
* this as a <code>super</code> constructor.
*
* @param name Name of the handler
* @param release Release identifier
* @param date Last modification date of the handler code,
* in the form of an array of three numbers.
* <code>date[0]</code> is the year,
* <code>date[1]</code> the month, and
* <code>date[2]</code> the day.
* @param note Additional information about the handler
* (may be null)
* @param rights Copyright notice for the handler
*/
protected HandlerBase (String name, String release, int [] date,
String note, String rights)
{
// Though we're actually in the jhove package, all the related
// action logically belongs in the handler package, so we name
// this logger accordingly.
_logger = Logger.getLogger ("edu.harvard.hul.ois.jhove.handler");
_logger.info ("Initializing " + name);
_name = name;
_release = release;
_encoding = "UTF-8";
Calendar calendar = new GregorianCalendar ();
calendar.set (date[0], date[1]-1, date[2]);
_date = calendar.getTime ();
_note = note;
_rights = rights;
_specification = new ArrayList<Document> ();
_level = -1;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Initialization methods.
******************************************************************/
/**
* Reset the handler. This needs to be called before each invocation.
*/
public void reset () {
_level = -1;
}
/**
* Set a a List of default parameters for the module.
*
* @param params A List whose elements are Strings.
* May be empty.
*/
public void setDefaultParams (List<String> params)
{
_defaultParams = params;
}
/**
* Applies the default parameters.
* Calling this clears any prior parameters.
*/
public void applyDefaultParams ()
throws Exception
{
resetParams ();
Iterator<String> iter = _defaultParams.iterator ();
while (iter.hasNext ()) {
String parm = iter.next ();
param (parm);
}
}
/** Reset parameter settings.
* Returns to a default state without any parameters.
* The default method clears the saved parameter.
*/
public void resetParams ()
throws Exception
{
_param = null;
}
/**
* Per-instantiation initialization.
* The default method does nothing.
*/
public void init (String init)
throws Exception
{
_init = init;
}
/**
* Per-action initialization.
* The default method does nothing.
*/
public void param (String param)
throws Exception
{
_param = param;
}
/******************************************************************
* Accessor methods.
******************************************************************/
/**
* Return the last modification date of this OutputHandler, as a
* Java Date object
*/
public final Date getDate ()
{
return _date;
}
/**
* Return the OutputHandler name
*/
public final String getName ()
{
return _name;
}
/**
* Return the OutputHandler note
*/
public final String getNote ()
{
return _note;
}
/**
* Return the release identifier
*/
public final String getRelease ()
{
return _release;
}
/**
* Return the copyright information string
*/
public final String getRights ()
{
return _rights;
}
/**
* Returns a list of <code>Document</code> objects (one for each
* specification document). The specification
* list is generated by the OutputHandler, and specifications cannot
* be added by callers.
*
* @see Document
*/
public final List<Document> getSpecification ()
{
return _specification;
}
/**
* Return the vendor information
*/
public final Agent getVendor ()
{
return _vendor;
}
/**
* Returns this handler's encoding.
*/
public String getEncoding ()
{
return _encoding;
}
/******************************************************************
* Mutator methods.
******************************************************************/
/**
* Pass the associated App object to this Module.
* The App makes various services available.
*/
public final void setApp (App app)
{
_app = app;
}
/**
* Assigns the JHOVE engine object to provide services to this handler
*/
public final void setBase (JhoveBase je)
{
_je = je;
}
/**
* Assigns the encoding to be used by this OutputHandler
*/
public void setEncoding (String encoding)
{
_encoding = encoding;
}
/**
* Assigns a PrintWriter to do output for this OutputHandler
*/
public final void setWriter (PrintWriter writer)
{
_writer = writer;
}
/******************************************************************
* Serialization methods.
******************************************************************/
/**
* Callback allowing post-parse, pre-show analysis of object
* representation information.
* @param info Object representation information
*/
public void analyze (RepInfo info)
{
/* Do nothing, which is sufficient for most handlers. */
}
/**
* Callback indicating a directory is finished being processed.
*/
public void endDirectory ()
{
/* Do nothing, which is sufficient for most handlers. */
}
/**
* Callback to give the handler the opportunity to decide whether or
* not to process a file. Most handlers will always return true.
* @param filepath File pathname
*/
public boolean okToProcess (String filepath)
{
return true;
}
/**
* Outputs information about a Module
*/
public abstract void show (Module module);
/**
* Outputs the information contained in a RepInfo object
*/
public abstract void show (RepInfo info);
/**
* Outputs information about the OutputHandler specified
* in the parameter
*/
public abstract void show (OutputHandler handler);
/**
* Outputs minimal information about the application
*/
public abstract void show ();
/**
* Outputs detailed information about the application,
* including configuration, available modules and handlers,
* etc.
*/
public abstract void show (App app);
/**
* Do the initial output. This should be in a suitable format
* for including multiple files between the header and the footer.
*/
public abstract void showHeader ();
/**
* Do the final output. This should be in a suitable format
* for including multiple files between the header and the footer.
*/
public abstract void showFooter ();
/**
* Close the writer after all output has been done.
*/
public void close ()
{
_writer.close ();
}
/**
* Callback indicating a new directory is being processed.
* @param directory Directory path
*/
public void startDirectory (String directory)
{
/* Do nothing, which is sufficient for most handlers. */
}
/******************************************************************
* PRIVATE CLASS METHODS.
*
* XML methods.
******************************************************************/
/**
* Return the XML DOCTYPE instruction.
* @param root Root element of the DTD
* @param uri URI of the DTD
*/
protected static String doctype (String root, String uri)
{
return doctype (root, null, uri);
}
/**
* Return the XML DOCTYPE instruction.
* @param root Root element of the DTD
* @param name Public name of the DTD
* @param uri URI of the DTD
*/
protected static String doctype (String root, String name, String uri)
{
StringBuffer s = new StringBuffer ("<!DOCTYPE " + root);
if (name != null) {
s.append (" PUBLIC \"" + name + "\" \"");
}
else {
s.append (" SYSTEM \"");
}
s.append (uri + "\">");
return s.toString ();
}
/**
* Returns, as a String, an empty XML.
*
* @param tag XML tag
*/
protected static String element (String tag)
{
return "<" + tag + "/>";
}
/**
* Returns, as a String, an XML element with a given tag and content
*
* @param tag An XML tag
* @param content Content string. Characters requiring
* conversion to entitites will be converted.
*/
protected static String element (String tag, String content)
{
return elementStart (tag) + encodeContent (content) + elementEnd (tag);
}
/**
* Returns, as a String,
* an XML element with a given tag and attributes
*
* @param tag An XML tag
* @param attrs An array of String[2] elements, where for each
* element, attrs[i][0] is the attribute key and
* attrs[i][1] is the attribute value.
* Null values are skipped.
*/
protected static String element (String tag, String [][] attrs)
{
StringBuffer buffer = new StringBuffer ("<");
buffer.append (tag);
for (int i=0; i<attrs.length; i++) {
if (attrs[i][0] != null && attrs[i][1] != null) {
buffer.append (" ");
buffer.append (attrs[i][0]);
buffer.append ("=\"");
buffer.append (encodeValue (attrs[i][1]));
buffer.append ("\"");
}
}
buffer.append ("/>");
return buffer.toString ();
}
/**
* Returns, as a String,
* an XML element with a given tag, content and attributes
*
* @param tag An XML tag
* @param content Content string. Characters requiring
* conversion to entitites will be converted.
* @param attrs An array of String[2] elements, where for each
* element, attrs[i][0] is the attribute key and
* attrs[i][1] is the attribute value.
* Null values are skipped.
*
*/
protected static String element (String tag, String [][] attrs,
String content)
{
StringBuffer buffer = new StringBuffer ("<");
buffer.append (tag);
for (int i=0; i<attrs.length; i++) {
if (attrs[i][0] != null && attrs[i][1] != null) {
buffer.append (" ");
buffer.append (attrs[i][0]);
buffer.append ("=\"");
buffer.append (encodeValue (attrs[i][1]));
buffer.append ("\"");
}
}
buffer.append (">");
buffer.append (encodeContent (content));
buffer.append (elementEnd (tag));
return buffer.toString ();
}
/**
* Returns, as a String, the closing tag of an element.
* No checking is done that opening and closing tags match.
*
* @param tag An XML tag
*/
protected static String elementEnd (String tag)
{
return "</" + tag + ">";
}
/**
* Returns, as a String, the opening tag of an element.
*
* @param tag An XML tag
*/
protected static String elementStart (String tag)
{
return "<" + tag + ">";
}
/**
* Returns, as a String, the opening tag of an element with
* specified attributes.
*
* @param tag An XML tag
* @param attrs An array of String[2] elements, where for each
* element, attrs[i][0] is the attribute key and
* attrs[i][1] is the attribute value.
*/
protected static String elementStart (String tag, String [][] attrs)
{
StringBuffer buffer = new StringBuffer ("<");
buffer.append (tag);
for (int i=0; i<attrs.length; i++) {
buffer.append (" ");
buffer.append (attrs[i][0]);
buffer.append ("=\"");
buffer.append (encodeValue (attrs[i][1]));
buffer.append ("\"");
}
buffer.append (">");
return buffer.toString ();
}
/**
* Encodes a content String in XML-clean form, converting characters
* to entities as necessary and removing control characters disallowed
* by XML. The null string will be converted to an empty string.
*/
private static String encodeContent (String content)
{
if (content == null) {
content = "";
}
StringBuffer buffer = new StringBuffer (content);
/* Remove disallowed control characters from the content string. */
int n = buffer.length ();
for (int i=0; i<n; i++) {
char ch = buffer.charAt (i);
if ((0x00 <= ch && ch <= 0x08) || (0x0b <= ch && ch <= 0x0c) ||
(0x0e <= ch && ch <= 0x1f) || 0x7f == ch) {
buffer.deleteCharAt (i--);
n--;
}
}
n = 0;
while ((n = buffer.indexOf ("&", n)) > -1) {
buffer.insert (n+1, "amp;");
n +=5;
}
n = 0;
while ((n = buffer.indexOf ("<", n)) > -1) {
buffer.replace (n, n+1, "&lt;");
n += 4;
}
n = 0;
while ((n = buffer.indexOf (">", n)) > -1) {
buffer.replace (n, n+1, "&gt;");
n += 4;
}
return buffer.toString ();
}
/**
* Encodes an attribute value String in XML-clean form,
* converting quote characters to entities and removing control
* characters disallowed by XML.
*/
private static String encodeValue (String value)
{
StringBuffer buffer = new StringBuffer (value);
/* Remove disallowed control characters from the value string. */
int n = buffer.length ();
for (int i=0; i<n; i++) {
char ch = buffer.charAt (i);
if ((0x00 <= ch && ch <= 0x08) || (0x0b <= ch && ch <= 0x0c) ||
(0x0e <= ch && ch <= 0x1f) || 0x7f == ch) {
buffer.deleteCharAt (i--);
n--;
}
}
// [CC], escape &, < and > characters which are disallowed in xml
n = 0;
while ((n = buffer.indexOf ("&", n)) > -1) {
buffer.insert (n+1, "amp;");
n +=5;
}
n = 0;
while ((n = buffer.indexOf ("<", n)) > -1) {
buffer.replace (n, n+1, "&lt;");
n += 4;
}
n = 0;
while ((n = buffer.indexOf (">", n)) > -1) {
buffer.replace (n, n+1, "&gt;");
n += 4;
}
n = 0;
while ((n = buffer.indexOf ("\"", n)) > -1) {
// [LP] fix for invalid escaping, "" quotes were accidentally left in place.
buffer.replace (n, n+1, "&quot;");
n +=7;
}
return buffer.toString ();
}
/**
* Return a canonical XML declaration with default encoding.
*/
protected static String xmlDecl ()
{
return "<?xml version=\"1.0\"?>";
}
/**
* Return a canonical XML declaration with specified encoding.
*/
protected static String xmlDecl (String encoding)
{
return "<?xml version=\"1.0\" encoding=\"" +
encoding +
"\"?>";
}
/******************************************************************
* Nesting level methods.
******************************************************************/
/**
* Returns a String containing a number of spaces equal
* to the current indent level.
*/
protected static String getIndent (int level)
{
StringBuffer s = new StringBuffer ();
for (int i=0; i<level; i++) {
s.append (" ");
}
return s.toString ();
}
/******************************************************************
* Array methods.
******************************************************************/
/**
* Return String representation of an integer array.
*/
protected static String integerArray (int [] iarray)
{
return integerArray (iarray, ' ');
}
/**
* Return String representation of an integer array with
* specified separator.
*/
protected static String integerArray (int [] iarray, char separator)
{
StringBuffer buffer = new StringBuffer ();
for (int i=0; i<iarray.length; i++) {
if (i > 0) {
buffer.append (separator);
}
buffer.append (Integer.toString (iarray[i]));
}
return buffer.toString ();
}
/**
* Return String representation of an array of long with
* space separator.
*/
protected static String longArray (long [] larray)
{
StringBuffer buffer = new StringBuffer ();
for (int i=0; i<larray.length; i++) {
if (i > 0) {
buffer.append (" ");
}
buffer.append (Long.toString (larray[i]));
}
return buffer.toString ();
}
/**
* Return String representation of an array of Rational, each evaluated
* as a double, with space separator.
*/
protected static String rationalArray (Rational [] rarray)
{
StringBuffer buffer = new StringBuffer ();
for (int i=0; i<rarray.length; i++) {
if (i > 0) {
buffer.append (" ");
}
buffer.append (rarray[i].toDouble ());
}
return buffer.toString ();
}
/**
* Return String representation of an array of Rational, each as
* two integers, with space separator.
*/
protected static String rationalArray10 (Rational [] rarray)
{
StringBuffer buffer = new StringBuffer ();
for (int i=0; i<rarray.length; i++) {
if (i > 0) {
buffer.append (" ");
}
buffer.append (rarray[i].getNumerator ());
buffer.append (" ");
buffer.append (rarray[i].getNumerator ());
}
return buffer.toString ();
}
/**
* Return String representation of an array of double.
*/
protected static String doubleArray (double [] darray)
{
StringBuffer buffer = new StringBuffer ();
for (int i=0; i<darray.length; i++) {
if (i > 0) {
buffer.append (" ");
}
buffer.append (Double.toString (darray[i]));
}
return buffer.toString ();
}
/* Text formatting methods. */
/* Convert a date to the dateTime format used by the
* XML schema. This is ISO 8610 with a colon between
* the hour and minute fields of the time zone. Unfortunately,
* SimpleDateFormat generates the time zone without the
* colon; this also conforms to 8601, but doesn't conform
* to the schema, so we have to diddle it.
*/
protected String toDateTime (Date date)
{
String isoStr = iso8601.format (date);
// We can't directly use a SimpleDateFormat, because
// the 'z' field gives us the colonless time zone.
int len = isoStr.length ();
// Add the colon before the last two characters.
return isoStr.substring (0, len - 2) +
":" +
isoStr.substring (len - 2);
}
/** A DateFormat class to address an issue of thread safety. */
public static class SynchronizedDateFormat extends SimpleDateFormat
{
public SynchronizedDateFormat(String pattern) {
super(pattern);
}
public synchronized StringBuffer format(Date date,
StringBuffer toAppendTo, FieldPosition pos) {
return super.format(date, toAppendTo, pos);
}
}
}
@@ -1,64 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates information about an identifier
* for a specification document.
*/
public class Identifier
{
private IdentifierType _type;
private String _value;
private String _note;
/**
* Create an Identifier.
* @param value The text displayed for this Identifier.
* @param type The type of identification.
*/
public Identifier (String value, IdentifierType type)
{
_value = value;
_type = type;
}
/**
* Create an Identifier.
* @param value The text displayed for this Identifier.
* @param type The type of identification.
* @param note A note giving supplementary information.
*/
public Identifier (String value, IdentifierType type, String note)
{
this (value, type);
_note = note;
}
/**
* Return the identifier type.
*/
public IdentifierType getType ()
{
return _type;
}
/**
* Return the displayable string.
*/
public String getValue ()
{
return _value;
}
/**
* Return the note, which will be null if none was specified.
*/
public String getNote()
{
return _note;
}
}
@@ -1,115 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for an Identifier of a
* format specification document.
* Applications will not create or modify IdentifierTypes, but will
* use one of the predefined IdentifierType instances
* ANSI, DDC, DOI, ECMA, HANDLE, ISO, ISBN, LC, LCCN,
* NISO, PII, RFC, SICI, URI, URL, URN, or OTHER.
*
* @see Identifier
*/
public final class IdentifierType
extends EnumerationType
{
/**
* Identifier type for American National Standards Institute.
*/
public static final IdentifierType ANSI = new IdentifierType ("ANSI");
/**
* Identifier type for Dewey Decimal Classification.
*/
public static final IdentifierType DDC = new IdentifierType ("DDC");
/**
* Identifier type for Digital Object Identifier.
*/
public static final IdentifierType DOI = new IdentifierType ("DOI");
/**
* Identifier type for ECMA.
*/
public static final IdentifierType ECMA = new IdentifierType ("ECMA");
/**
* Identifier type for CNRI Handle.
*/
public static final IdentifierType HANDLE = new IdentifierType ("Handle");
/**
* Identifier type for International Standards Organization.
*/
public static final IdentifierType ISO = new IdentifierType ("ISO");
/**
* Identifier type for International Standard Book Number.
*/
public static final IdentifierType ISBN = new IdentifierType ("ISBN");
/**
* Identifier type for Library of Congress classification.
*/
public static final IdentifierType LC = new IdentifierType ("LC");
/**
* Identifier type for Library of Congress catalogue number.
*/
public static final IdentifierType LCCN = new IdentifierType ("LCCN");
/**
* Identifier type for NISO standard number.
*/
public static final IdentifierType NISO = new IdentifierType ("NISO");
/**
* Identifier type for Publisher Item Identifier.
*/
public static final IdentifierType PII = new IdentifierType ("PII");
/**
* Identifier type for IETF Request for Comment.
*/
public static final IdentifierType RFC = new IdentifierType ("RFC");
/**
* Identifier type for Serial Item and Contribution Identifier.
*/
public static final IdentifierType SICI = new IdentifierType ("SICI");
/**
* Identifier type for Uniform Resource Identifier.
*/
public static final IdentifierType URI = new IdentifierType ("URI");
/**
* Identifier type for Uniform Resource Locator.
*/
public static final IdentifierType URL = new IdentifierType ("URL");
/**
* Identifier type for Uniform Resource Name.
*/
public static final IdentifierType URN = new IdentifierType ("URN");
/**
* Identifier type for CCITT.
*/
public static final IdentifierType CCITT = new IdentifierType ("CCITT");
/**
* Identifier type for International Telecommunication Union.
*/
public static final IdentifierType ITU = new IdentifierType ("ITU");
/**
* Identifier type for Japan Electronics and Information Technology
* Industries Association.
*/
public static final IdentifierType JEITA = new IdentifierType ("JEITA");
/**
* Identifier type for whatever doesn't fit other categories.
*/
public static final IdentifierType OTHER = new IdentifierType ("Other");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create SignatureTypes directly.
**/
private IdentifierType (String value)
{
super (value);
}
}
@@ -1,62 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates an informational message from a Module, giving
* information (not necessarily a problem)
* about the content being analyzed or the way that Jhove
* deals with it.
*/
public class InfoMessage
extends Message
{
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Create an InfoMessage.
* @param message Human-readable string giving the information.
*/
public InfoMessage (String message)
{
super (message);
}
/**
* Create an InfoMessage.
* @param message Human-readable string giving the information.
* @param offset The offset in the file relevant to the
* situation being described
*/
public InfoMessage (String message, long offset)
{
super (message, offset);
}
/**
* Create an InfoMessage.
* @param message Human-readable string giving the information.
* @param subMessage Human-readable additional information.
*/
public InfoMessage (String message, String subMessage)
{
super (message, subMessage);
}
/**
* Create an InfoMessage.
* @param message Human-readable string giving the information.
* @param subMessage Human-readable additional information.
* @param offset The offset in the file relevant to the
* situation being described
*/
public InfoMessage (String message, String subMessage, long offset)
{
super (message, subMessage, offset);
}
}
@@ -1,140 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates information about internal format signatures.
* The value of a Signature may be either a String or a byte array
* (stored as an int array to avoid signed byte problems).
*/
public class InternalSignature
extends Signature
{
private boolean _hasFixedOffset;
private int _offset;
/**
* Creates an InternalSignature given a string value, a type,
* and a use requirement. An InternalSignature created with
* this constructor does not have a fixed byte offset.
*/
public InternalSignature (String value, SignatureType type,
SignatureUseType use)
{
super (value, type, use);
_hasFixedOffset = false;
}
/**
* Creates an InternalSignature given a byte array, a type,
* and a use requirement. An InternalSignature created with
* this constructor does not have a fixed byte offset.
*/
public InternalSignature (int[] value, SignatureType type,
SignatureUseType use)
{
super (value, type, use);
_hasFixedOffset = false;
}
/**
* Creates an InternalSignature given a string value, a type,
* a use requirement, and a byte offset. An InternalSignature
* created with this constructor has a fixed byte offset.
*/
public InternalSignature (String value, SignatureType type,
SignatureUseType use, int offset)
{
super (value, type, use);
_offset = offset;
_hasFixedOffset = true;
}
/**
* Creates an InternalSignature given a byte array, a type,
* a use requirement, and a byte offset. An InternalSignature
* created with this constructor has a fixed byte offset.
*/
public InternalSignature (int[] value, SignatureType type,
SignatureUseType use, int offset)
{
super (value, type, use);
_offset = offset;
_hasFixedOffset = true;
}
/**
* Creates an InternalSignature given a string value, a type,
* a use requirement, and a note. An InternalSignature created with
* this constructor does not have a fixed byte offset.
*/
public InternalSignature (String value, SignatureType type,
SignatureUseType use, String note)
{
super (value, type, use, note);
_hasFixedOffset = false;
}
/**
* Creates an InternalSignature given a byte array, a type,
* a use requirement, and a note. An InternalSignature created with
* this constructor does not have a fixed byte offset.
*/
public InternalSignature (int[] value, SignatureType type,
SignatureUseType use, String note)
{
super (value, type, use, note);
_hasFixedOffset = false;
}
/**
* Creates an InternalSignature given a string value, a type,
* a use requirement, a byte offset, and a note.
* An InternalSignature created with
* this constructor has a fixed byte offset.
*/
public InternalSignature (String value, SignatureType type,
SignatureUseType use, int offset,
String note)
{
super (value, type, use, note);
_offset = offset;
_hasFixedOffset = true;
}
/**
* Creates an InternalSignature given a string value, a type,
* a use requirement, a byte offset, and a note.
* An InternalSignature created with
* this constructor has a fixed byte offset.
*/
public InternalSignature (int[] value, SignatureType type,
SignatureUseType use, int offset,
String note)
{
super (value, type, use, note);
_offset = offset;
_hasFixedOffset = true;
}
/**
* Returns the byte offset. This value is meaningful only
* if this InternalSignature has a fixed byte offset.
*/
public int getOffset ()
{
return _offset;
}
/**
* Returns <code>true</code> if this InternalSignature
* has a fixed byte offset.
*/
public boolean hasFixedOffset ()
{
return _hasFixedOffset;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,19 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
public class JhoveException
extends Exception
{
public JhoveException ()
{
super ();
}
public JhoveException (String message)
{
super (message);
}
}
@@ -1,96 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.lang.reflect.*;
//import com.apple.eio.FileManager;
/**
* Code specific to Macintosh Java. This class consists of static
* methods, and should not be instantiated. Its methods should be
* called only on the Macintosh OS X platform. It requires the
* package com.apple.eio.FileManager.
*
* @author Gary McGath
*
*/
public class MacStuff {
/** Private constructor to prevent instantiation.*/
private MacStuff ()
{
}
/**
* Determines if we're running on a Macintosh,
* so appropriate UI adjustments can be made. In accordance
* with Apple's recommendations, this checks for the existence
* of the mrj.version property rather than checking the os.name
* property.
*/
public static boolean isMacintosh ()
{
return (System.getProperty ("mrj.version") != null);
}
/**
* Returns true if a file has the given file type.
* This method uses FileManager in a dynamic way, so
* that it will merely throw a ClassNotFound exception
* if it fails.
*
* Currently this code isn't actually used, since the
* Jhove application is specified as checking only internal
* signatures. Should some future version or add-on
* code wish to use it, the code should
* look something like this:
*
* <code><pre>
* try {
* if (sig.getType() == SignatureType.FILETYPE &&
* MacStuff.isMacintosh ()) {
* if (!MacStuff.fileHasType(file, sig.getValueString())) {
* info.setConsistent (false);
* }
* }
* }
* catch (ClassNotFoundException e) {
* // Mac classes missing -- can't check filetype.
* }
* </pre></code>
*/
public static boolean fileHasType(File file, String type)
throws ClassNotFoundException
{
// Need to make this completely dynamic.
//return type.equals (FileManager.getFileType (file));
try {
if (type == null) {
return false;
}
Class fmclass = Class.forName ("com.apple.eio.FileManager");
Class[] params = new Class[1];
params[0] = Class.forName ("java.io.File");
Object[] args = new Object[1];
args[0] = file;
Method method = fmclass.getMethod ("getFileType", params);
String ftype = (String) method.invoke (null, args);
return (type.equals (ftype));
}
catch (ClassNotFoundException e) {
throw e;
}
catch (Exception f) {
return false;
}
}
}
@@ -1,131 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates a String to be displayed.
*/
public abstract class Message
{
/******************************************************************
* PUBLIC CLASS FIELDS.
******************************************************************/
/** Value indicating a null offset. */
public static final long NULL = -1;
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** Message text. */
protected String _message;
/** Additional information. */
protected String _subMessage;
/** Byte offset to which message applies. */
protected long _offset;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Create a Message. This constructor cannot be invoked directly,
* since Message is abstract.
* @param message Human-readable string.
*/
protected Message (String message)
{
init (message, null, NULL);
}
/**
* Create a Message. This constructor cannot be invoked directly,
* since Message is abstract. The second argument
* adds secondary details to the primary message;
* the message will typically be displayed in the
* form "message:subMessage".
* @param message Human-readable string.
* @param subMessage Human-readable additional information.
*/
protected Message (String message, String subMessage)
{
init (message, subMessage, NULL);
}
/**
* Create a Message. This constructor cannot be invoked directly,
* since Message is abstract. The second argument
* adds secondary details to the primary message;
* the message will typically be displayed in the
* form "message:subMessage".
* @param message Human-readable string.
* @param offset Byte offset associated with the message.
*/
protected Message (String message, long offset)
{
init (message, null, offset);
}
/**
* Create a Message. This constructor cannot be invoked directly,
* since Message is abstract. The second argument
* adds secondary details to the primary message;
* the message will typically be displayed in the
* form "message:subMessage".
* @param message Human-readable string.
* @param subMessage Human-readable additional information.
* @param offset Byte offset associated with the message.
*/
protected Message (String message, String subMessage, long offset)
{
init (message, subMessage, offset);
}
/**
* Initialize the <tt>Message</tt> object.
* @param message Human-readable string.
* @param subMessage Human-readable additional information.
*/
private void init (String message, String subMessage, long offset)
{
_message = message;
_subMessage = subMessage;
_offset = offset;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/**
* Get the message string.
*/
public String getMessage ()
{
return _message;
}
/**
* Get the submessage string.
*/
public String getSubMessage ()
{
return _subMessage;
}
/**
* Return the offset to which the information is related.
*/
public long getOffset ()
{
return _offset;
}
}
@@ -1,264 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.util.*;
/**
* Public interface for Jhove format-specific plug-in modules.
*
* All format modules must implement the Module interface;
* usually the best way to do this will be to subclass ModuleBase.
* Modules must be declared in the configuration file and present
* in the Classpath to be recognized by Jhove.
*
* @see ModuleBase
*/
public interface Module
{
public static final int
MAXIMUM_VERBOSITY = 1,
MINIMUM_VERBOSITY = 2;
/**
* Per-instantiation initialization.
*
* @param init Initialization parameter. This is typically obtained
* from the configuration file.
*/
public void init (String init)
throws Exception;
/**
* Sets list of default parameters.
*
* @param params A List whose elements are Strings.
* May be empty.
*/
public void setDefaultParams (List<String> params);
/**
* Applies the default parameters.
*/
public void applyDefaultParams ()
throws Exception;
/** Reset parameter settings.
* Returns to a default state without any parameters.
*/
public void resetParams ()
throws Exception;
/**
* Per-action initialization.
* May be called multiple times.
*
* @param param Initialization parameter.
*/
public void param (String param)
throws Exception;
/**
* Pass the associated App object to this Module.
* The App makes various services available.
*/
public void setApp (App app);
/**
* Pass the JHOVE engine object to this Module.
*/
public void setBase (JhoveBase je);
/**
* Set the degree of verbosity desired from the module.
*
* @param verbosity The requested verbosity value.
*/
public void setVerbosity (int verbosity);
/**
* Return the name of this Module.
*/
public String getName ();
/**
* Return the release identifier
*/
public String getRelease ();
/**
* Return the last modification date of the Module, as a
* Java Date object
*/
public Date getDate ();
/**
* Return the vendor information
*/
public Agent getVendor ();
/**
* Return the copyright information string
*/
public String getRights ();
/**
* Return the Module note
*/
public String getNote ();
/**
* Return the array of format names supported by this Module
*/
public String [] getFormat ();
/**
* Return the array of MIME type strings for formats supported
* by this Module
*/
public String [] getMimeType ();
/**
* Return details as to the specific format versions or
* variants that are supported by this Module
*/
public String getCoverage ();
/**
* Return the List of Signatures recognized by this Module
*/
public List<Signature> getSignature ();
/**
* Returns a list of <code>Document</code> objects (one for each
* specification document of the format). The specification
* list is generated by the Module, and specifications cannot
* be added by callers.
*
* @see Document
*/
public List<Document> getSpecification ();
/**
* Return the string describing well-formedness criteria
*/
public String getWellFormedNote ();
/**
* Returns the RepInfo note
*/
public String getRepInfoNote ();
/**
* Returns the string describing validity criteria
*/
public String getValidityNote ();
/**
* Returns the random access flag (true if the module operates
* on random access files, false if it operates on streams)
*/
public boolean isRandomAccess ();
/**
* Returns <code>true</code> if the module supports a given
* named feature, and <code>false</code> if the feature is
* unsupported or unknown.
*/
public boolean hasFeature (String feature);
/**
* Returns the full list of features.
*/
public List<String> getFeatures ();
/**
* Returns the list of default parameters.
*/
public List<String> getDefaultParams ();
/**
* Parse the content of a stream digital object and store the
* results in RepInfo.
* A given Module will normally implement only one of the two
* parse methods, leaving the other as a stub.
*
* @param stream An InputStream, positioned at its beginning,
* which is generated from the object to be parsed.
* If multiple calls to <code>parse</code> are made
* on the basis of a nonzero value being returned,
* a new InputStream must be provided each time.
*
* @param info A fresh (on the first call) RepInfo object
* which will be modified
* to reflect the results of the parsing
* If multiple calls to <code>parse</code> are made
* on the basis of a nonzero value being returned,
* the same RepInfo object should be passed with each
* call.
*
* @param parseIndex Must be 0 in first call to <code>parse</code>. If
* <code>parse</code> returns a nonzero value, it must be
* called again with <code>parseIndex</code>
* equal to that return value.
*/
public int parse (InputStream stream, RepInfo info, int parseIndex)
throws IOException;
/**
* Parse the content of a random access digital object and store the
* results in RepInfo.
* A given Module will normally implement only one of the two
* parse methods, leaving the other as a stub.
*
* @param file A RandomAccessFile, positioned at its beginning,
* which is generated from the object to be parsed
* @param info A fresh RepInfo object which will be modified
* to reflect the results of the parsing
*/
public void parse (RandomAccessFile file, RepInfo info)
throws IOException;
/**
* Check if the digital object conforms to this Module's
* internal signature information.
*
* @param file A File object for the object being parsed
* @param stream An InputStream, positioned at its beginning,
* which is generated from the object to be parsed
* @param info A fresh RepInfo object which will be modified
* to reflect the results of the test
*/
public void checkSignatures (File file,
InputStream stream,
RepInfo info)
throws IOException;
/**
* Check if the digital object conforms to this Module's
* internal signature information.
*
* @param file A File object for the object being parsed
* @param raf A RandomAccessFile, positioned at its beginning,
* which is generated from the object to be parsed
* @param info A fresh RepInfo object which will be modified
* to reflect the results of the test
*/
public void checkSignatures (File file,
RandomAccessFile raf,
RepInfo info)
throws IOException;
/**
* Generates information about this Module.
* The format of the output depends on the OutputHandler.
*/
public void show (OutputHandler handler);
}
File diff suppressed because it is too large Load Diff
@@ -1,17 +0,0 @@
package edu.harvard.hul.ois.jhove;
/** A small class to hold information about a module. */
public class ModuleInfo {
public String clas;
public String init;
public String[] params;
public ModuleInfo (String className) {
clas = className;
}
public ModuleInfo (String className, String init) {
clas = className;
this.init = init;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,118 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-4 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.util.*;
/**
* Module for identification of a document. "Identification"
* means determining, by querying modules successively, what the format
* of a document is. The Bytestream module is always queried last,
* so a document will by identified as a Bytestream if all else fails.
*/
public class ObjectIdentifier
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
private List _moduleList;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
public ObjectIdentifier (List moduleList)
{
_moduleList = moduleList;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Processing methods.
*
******************************************************************/
/**
* Perform identification on a file. The file is parsed by
* each of the modules in the module list until one
* declares that the file is well-formed. It is assumed
* that there is a module in the list (normally Bytestream
* at the end) which will always consider a file well-formed.
*/
public void identify (File file, RepInfo info,
String parm, boolean verbose,
boolean shortCheck)
throws IOException
{
/******************************************************
* Go through all modules, in the order in the config
* file, calling the parse method till we find one
* which matches.
******************************************************/
ListIterator modIter = _moduleList.listIterator();
while (modIter.hasNext ()) {
/* We need clean RepInfo for each run */
RepInfo info1;
info1 = (RepInfo) info.clone ();
Module mod = (Module) modIter.next ();
try {
if (!mod.hasFeature("edu.harvard.hul.ois.jhove.canValidate")) {
continue;
}
if (mod.isRandomAccess ()) {
RandomAccessFile raf =
new RandomAccessFile (file, "r");
mod.param (parm);
if (verbose) {
mod.setVerbosity (Module.MAXIMUM_VERBOSITY);
}
if (shortCheck) {
mod.checkSignatures (file, raf, info1);
}
else {
mod.parse (raf, info1);
}
raf.close ();
}
else {
InputStream stream = new FileInputStream (file);
mod.param (parm);
if (shortCheck) {
mod.checkSignatures (file, stream, info1);
}
else {
int parseIndex = mod.parse (stream, info1, 0);
while (parseIndex != 0) {
stream.close ();
stream = new FileInputStream (file);
parseIndex = mod.parse (stream, info1, parseIndex);
}
}
stream.close ();
}
}
catch (Exception e) {
/* The assumption is that in trying to analyze
the wrong type of file, the module may go
off its track and throw an exception, so we
just continue on to the next module.
*/
continue;
}
if (info1.getWellFormed () == RepInfo.TRUE) {
info.copy (info1);
break;
}
}
}
}
@@ -1,196 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
import java.util.*;
/**
* Public interface for Jhove output handlers.
* All output handlers must implement OutputHandler, and in
* normal cases should subclass HandlerBase.
*/
public interface OutputHandler
{
/**
* Reset the handler. This needs to be called before each invocation.
*/
public void reset ();
/**
* Callback allowing post-parse, pre-show analysis of object
* representation information.
* @param info Object representation information
*/
public void analyze (RepInfo info);
/**
* Callback indicating a directory is finished being processed.
* Most handlers will do nothing.
*/
public void endDirectory ();
/**
* Returns the name of this handler
*/
public String getName ();
/**
* Returns release information for this handler
*/
public String getRelease ();
/**
* Returns the last modification date of this handler
*/
public Date getDate ();
/**
* Returns a List of Document objects giving the format
* specification documentation
*
* @see Document
*/
public List<Document> getSpecification ();
/**
* Returns a List of Agent objects giving the vendor(s)
* of this handler.
*/
public Agent getVendor ();
/**
* Returns this handler's note
*/
public String getNote ();
/**
* Returns this handler's copyright information
*/
public String getRights ();
/**
* Returns this handler's encoding.
*/
public String getEncoding ();
/**
* Per-instantiation initialization.
*
* @param init Initialization parameter. This is typically obtained
* from the configuration file.
*/
public void init (String init)
throws Exception;
/**
* Callback to give the handler the opportunity to decide whether or
* not to process a file. Most handlers will always return true.
* @param filepath File pathname
*/
public boolean okToProcess (String filepath);
/**
* Sets list of default parameters.
*
* @param params A List whose elements are Strings.
* May be empty.
*/
public void setDefaultParams (List<String> params);
/**
* Applies the default parameters.
*/
public void applyDefaultParams ()
throws Exception;
/** Reset parameter settings.
* Returns to a default state without any parameters.
*/
public void resetParams ()
throws Exception;
/**
* Per-action initialization.
*
* @param param Initialization parameter.
*/
public void param (String param)
throws Exception;
/**
* Assigns an application object to provide services to this handler
*/
public void setApp (App app);
/**
* Assigns the JHOVE engine object to provide services to this handler
*/
public void setBase (JhoveBase je);
/**
* Assigns the encoding to be used by this OutputHandler
*/
public void setEncoding (String encoding);
/**
* Assigns a PrintWriter to do output for this OutputHandler
*/
public void setWriter (PrintWriter output);
/**
* Outputs information about a Module
*/
public void show (Module module);
/**
* Outputs the information contained in a RepInfo object
*/
public void show (RepInfo info);
/**
* Outputs information about the OutputHandler specified
* in the parameter
*/
public void show (OutputHandler handler);
/**
* Outputs minimal information about the application
*/
public void show ();
/**
* Outputs detailed information about the application,
* including configuration, available modules and handlers,
* etc.
*/
public void show (App app);
/**
* Do the initial output. This should be in a suitable format
* for including multiple files between the header and the footer.
*/
public void showHeader ();
/**
* Do the final output. This should be in a suitable format
* for including multiple files between the header and the footer.
*/
public void showFooter ();
/**
* Do appropriate finalization after all output is complete.
*/
public void close ();
/**
* Callback indicating a new directory is being processed.
* Most handlers will do nothing.
* @param directory Directory path
*/
public void startDirectory (String directory);
}
@@ -1,214 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.util.*;
/**
* This class encapsulates arbitrary format-specific properties.
* A Property's value can be a simple object or a structure.
* If it is a simple object, it has arity SCALAR. If it is
* a structure, its must be a Map, a List, a Set, or an array,
* with corresponding Arity. The simple object (in the case of
* arity SCALAR) or the components of the structure must have a
* type corresponding to one of the enumerations given by
* <code>PropertyType</code>.
*
* The components of a Property may themselves be Property
* objects, allowing nested structures.
*
* @see PropertyType
* @see PropertyArity
*/
public class Property
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
private String _name;
private PropertyType _type;
private PropertyArity _arity;
private Object _value;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates a Property with arity SCALAR.
*
* @param name The displayable name of the property
* @param type The type of property
* @param value The value of the property. The type of the
* parameter must agree with <code>type</code>.
*/
public Property (String name, PropertyType type, Object value)
{
init (name, type, PropertyArity.SCALAR, value);
}
/**
* Creates a Property with a given name, type, arity and value.
*
* @param name The displayable name of the property.
* @param type The type of property.
* @param arity The arity of the property.
* @param value The value of the property. If the arity is
* SCALAR, the type of this parameter must agree
* with <code>type</code>. Otherwise, the arity
* must agree with <code>arity</code>, and its
* components must agree with <code>type</code>.
*/
public Property (String name, PropertyType type, PropertyArity arity,
Object value)
{
init (name, type, arity, value);
}
private void init (String name, PropertyType type, PropertyArity arity,
Object value)
{
/* Some limited type checking. Checking for mismatched
types here may help avoid difficult chasing down
of the bugs such mismatches cause. */
if (value == null) {
throw new NullPointerException ("Null value for Property not permitted");
}
if (arity == PropertyArity.SCALAR) {
if (value instanceof List ||
value instanceof Map ||
value instanceof Set) {
throw new IncompatibleClassChangeError
("Wrong class for Scalar Property");
}
}
else if (arity == PropertyArity.MAP) {
if (!(value instanceof Map)) {
throw new IncompatibleClassChangeError
("Wrong class for Map Property");
}
}
else if (arity == PropertyArity.SET) {
if (!(value instanceof Set)) {
throw new IncompatibleClassChangeError
("Wrong class for Set Property");
}
}
else if (arity == PropertyArity.LIST) {
if (!(value instanceof List)) {
throw new IncompatibleClassChangeError
("Wrong class for List Property");
}
}
_name = name;
_type = type;
_arity = arity;
_value = value;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
*
* Accessor methods.
******************************************************************/
/**
* Returns the arity (type of structure) of this Property.
*/
public PropertyArity getArity ()
{
return _arity;
}
/**
* Return a property by its name, regardless of its position in the
* structural hierarchy of properties.
* @param name Property name
* @return Named property (or null)
*/
public Property getByName (String name)
{
if (_name.equals (name)) {
return this;
}
if (!_arity.equals (PropertyArity.SCALAR) &&
_type.equals (PropertyType.PROPERTY)) {
if (_arity.equals (PropertyArity.ARRAY)) {
Property [] array = (Property []) _value;
for (int i=0; i<array.length; i++) {
Property prop = array[i].getByName (name);
if (prop != null) {
return prop;
}
}
}
else if (_arity.equals (PropertyArity.LIST)) {
List list = (List) _value;
int len = list.size ();
for (int i=0; i<len; i++) {
Property prop = ((Property) list.get (i)).getByName (name);
if (prop != null) {
return prop;
}
}
}
else if (_arity.equals (PropertyArity.MAP)) {
Collection coll = ((Map) _value).values ();
Iterator iter = coll.iterator ();
while (iter.hasNext ()) {
Property prop = ((Property) iter.next ()).getByName (name);
if (prop != null) {
return prop;
}
}
}
else if (_arity.equals (PropertyArity.SET)) {
Iterator iter = ((Set) _value).iterator ();
while (iter.hasNext ()) {
Property prop = ((Property) iter.next ()).getByName (name);
if (prop != null) {
return prop;
}
}
}
}
return null;
}
/**
* Returns the displayable name of this Property.
*/
public String getName ()
{
return _name;
}
/**
* Returns the type of this Property.
* If the arity is other than SCALAR, the type refers to the
* compononents of the Property structure.
*/
public PropertyType getType ()
{
return _type;
}
/**
* Returns the Object which is the Property's value.
*/
public Object getValue ()
{
return _value;
}
}
@@ -1,60 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class represents the arity (structure type) of a Property.
* Applications will not create or modify PropertyArities, but will
* use one of the predefined PropertyArity instances
* ARRAY, LIST, MAP, SCALAR, or SET.
*
* @see Property
*/
public final class PropertyArity
extends EnumerationType
{
/**
* An Arity corresponding to a Java array. For the Java types
* <code>Integer</code>, <code>Boolean</code>, <code>Byte</code>,
* <code>Character</code>, <code>Double</code>,
* <code>Float</code>, <code>Long</code>, and
* <code>Short</code>, a Property with arity ARRAY is an array of primitive
* Java types rather than Objects (e.g., <code>int</code>
* rather than <code>Integer</code>).
*/
public static final PropertyArity ARRAY = new PropertyArity ("Array");
/**
* An Arity corresponding to java.util.List or any of its derived classes.
*/
public static final PropertyArity LIST = new PropertyArity ("List");
/**
* An Arity corresponding to java.util.Map or any of its derived classes.
*/
public static final PropertyArity MAP = new PropertyArity ("Map");
/**
* An Arity corresponding to a simple object, which must be of a
* type corresponding to one of the instances of
* <code>PropertyType</code>.
*/
public static final PropertyArity SCALAR = new PropertyArity ("Scalar");
/**
* An Arity corresponding to java.util.Set or any of its derived classes.
*/
public static final PropertyArity SET = new PropertyArity ("Set");
/**
* Applications will never create PropertyArities directly.
**/
private PropertyArity (String value)
{
super (value);
}
}
@@ -1,278 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.util.*;
/**
* A description of the location of a Property in a
* RepInfo object. Properties can be nested under other
* properties, in lists, maps, or subproperties. This class
* provides a general way to specify their location.
*
* For the moment, only paths by name are fully supported.
*
* @author Gary McGath
*
*/
public class PropertyPath {
private List pathInfo;
/**
* No-argument constructor.
* Creates an empty path.
*/
public PropertyPath ()
{
pathInfo = new LinkedList ();
}
/**
* Cloning constructor.
* This creates a new pathInfo list, whose elements
* are shared with the original PropertyPath's list.
*/
public PropertyPath (PropertyPath path)
{
pathInfo = new LinkedList ();
pathInfo.addAll (path.pathInfo);
}
/**
* String array constructor.
* This allows creation of a path in a common and simple
* case: a hierarchy of named subproperties. It's the
* equivalent of creating an empty PropertyPath and then
* calling addElement successively with each of the strings
* in the array.
*/
public PropertyPath (String[] pathArray)
{
pathInfo = new LinkedList ();
for (int i = 0; i < pathArray.length; i++) {
pathInfo.add (pathArray[i]);
}
}
/**
* Adds a property name to the path.
*/
public void addPropertyName (String str)
{
pathInfo.add (str);
// Now -- how do we add a key and distinguish it from a
// property name? We could define an internal type for
// keys. Do that for the moment and see what that leads
// to in retrieval. Alternatively, it isn't needed because
// we can see that a property has arity Map.
}
/**
* Adds a key to the path, for a property map.
*/
public void addPropertyKey (Object obj)
{
pathInfo.add (new PropertyKey (obj));
}
/**
* Adds an index to the path, for an indexed property.
*/
public void addPropertyIndex (int idx)
{
pathInfo.add (new Integer (idx));
}
/**
* Walk down the path and return the specified Property.
*
* @param info The RepInfo object to search
*
* @return The specified Property if found, otherwise null.
*/
public Property locateProperty (RepInfo info)
{
return locateProperty (info, false);
}
/**
* Walk down the path and return the specified Property.
*
* @param info The RepInfo object to search
* @param trace If <code>true</code>, write debugging information
* to standard output.
*
* @return The specified Property if found, otherwise null.
*/
public Property locateProperty (RepInfo info, boolean trace)
{
if (pathInfo.isEmpty ()) {
// An empty path can't reach any property
if (trace) {
System.out.println ("Empty property path");
}
return null;
}
Object obj = pathInfo.get(0);
if (!(obj instanceof String)) {
// The initial qualifier must be a property name
if (trace) {
System.out.println ("Not a property name");
}
return null;
}
String top = (String) obj;
if (trace) {
System.out.println ("Getting proprerty " + top);
}
Property prop = info.getProperty (top);
if (prop == null) {
// No property of that name in RepInfo
if (trace) {
System.out.println ("Property is null");
}
return null;
}
int pathLen = pathInfo.size ();
// Pass the CDR of the list to locateSubProperty.
return locateSubProperty (prop, pathInfo.subList (1, pathLen), trace);
}
/* Recursive function for extracting a subproperty of a property. */
private Property locateSubProperty (Property property, List path, boolean trace)
{
// If there's nothing left of the path, we're done.
if (path.isEmpty ()) {
return property;
}
List cdr = path.subList (1, path.size());
PropertyArity arity = property.getArity ();
PropertyType type = property.getType ();
Object val = property.getValue ();
if (trace) {
System.out.println ("Property arity = " + arity + ", type = " + type);
}
// If the type isn't PROPERTY, then there are no subproperties.
if (type != PropertyType.PROPERTY) {
if (trace) {
System.out.println ("Not a property, type is " + type.toString ());
}
return null;
}
Object obj = path.get (0);
if (obj instanceof String) {
Iterator iter;
// Iterate through the property and see if any of the elements
// are Properties that match the name.
String name = (String) obj;
if (trace) {
System.out.println ("Looking for subproperty " + name +
" arity= " + arity.toString ());
}
if (arity.equals (PropertyArity.SCALAR)) {
// There's just one shot at matching a scalar.
Property p = (Property) property.getValue ();
if (p.getName().equals (name)) {
return locateSubProperty (p, cdr, trace);
}
else {
return null;
}
}
else if (arity.equals (PropertyArity.ARRAY)) {
// We know it's an array of Properties, which saves much
// hair-tearing.
Property[] parray = (Property []) val;
for (int i = 0; i < parray.length; i++) {
Property p = parray[i];
if (p.getName ().equals (name)) {
return locateSubProperty (p, cdr, trace);
}
}
return null;
}
else if (arity.equals (PropertyArity.LIST)) {
iter = ((List) val).listIterator ();
return getIteratedSubProperty (iter, name, cdr, trace);
}
else if (arity.equals (PropertyArity.SET)) {
iter = ((Set) val).iterator ();
return getIteratedSubProperty (iter, name, cdr, trace);
}
else if (arity.equals (PropertyArity.MAP)) {
iter = ((Map) val).values().iterator();
return getIteratedSubProperty (iter, name, cdr, trace);
}
else {
// Should never happen, but keep compiler happy
//System.out.println ("Unknown arity");
return null;
}
}
else if (obj instanceof Integer) {
int idx = ((Integer) obj).intValue();
//System.out.println ("Property index = " + idx + ", arity= " + arity.toString ());
if (arity.equals (PropertyArity.LIST)) {
List propList = (List) val;
return locateSubProperty
((Property) propList.get (idx), cdr, trace);
}
else if (arity.equals (PropertyArity.ARRAY)) {
Property[] propArr = (Property []) val;
return locateSubProperty (propArr[idx], cdr, trace);
}
else {
// Other arities are not indexable
return null;
}
}
else if (obj instanceof PropertyKey) {
// This is applicable only to a Map.
if (arity != PropertyArity.MAP) {
return null;
}
return null; // I'm not sure this case is even meaningful
}
else {
// We should never get here
return null;
}
}
/* Walk through an Iterator, whose elements are Properties,
* and return the subproperty by <code>path</code> of the first element
* whose name matches <code>name</code>.
*/
private Property getIteratedSubProperty (Iterator iter,
String name,
List path,
boolean trace)
{
while (iter.hasNext ()) {
Property p = (Property) iter.next ();
if (p.getName ().equals (name)) {
return locateSubProperty (p, path, trace);
}
}
return null;
}
private class PropertyKey
{
public Object key;
public PropertyKey (Object obj)
{
key = obj;
}
}
}
@@ -1,110 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-2009 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for an Property of
* some given content.
* Applications will not create or modify PropertyTypes, but will
* use one of the predefined PropertyType instances
* BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE, FLOAT, INTEGER,
* LONG, OBJECT, PROPERTY, SHORT, STRING, RATIONAL, or
* NISOIMAGEMETADATA.
*
* @see Property
*/
public final class PropertyType
extends EnumerationType
{
/**
* Property type for a <code>Boolean</code> object, or a
* <code>boolean</code> if the Arity is Array.
*/
public static final PropertyType BOOLEAN = new PropertyType ("Boolean");
/**
* Property type for a <code>Byte</code> object, or a <code>byte</code>
* if the Arity is Array.
*/
public static final PropertyType BYTE = new PropertyType ("Byte");
/**
* Property type for a <code>Character</code> object, or a
* <code>char</code> if the Arity is Array.
*/
public static final PropertyType CHARACTER = new PropertyType("Character");
/**
* Property type for a <code>Date</code> object.
*/
public static final PropertyType DATE = new PropertyType ("Date");
/**
* Property type for a <code>Double</code> object, or
*a <code>double</code> if the Arity is Array.
*/
public static final PropertyType DOUBLE = new PropertyType ("Double");
/**
* Property type for a <code>Float</code> object, or a
* <code>float</code> if the Arity is Array.
*/
public static final PropertyType FLOAT = new PropertyType ("Float");
/**
* Property type for an <code>Integer</code> object, or an
* <code>integer</code> if the Arity is Array.
*/
public static final PropertyType INTEGER = new PropertyType ("Integer");
/**
* Property type for a <code>Long</code> object, or a
* <code>long</code> if the Arity is Array.
*/
public static final PropertyType LONG = new PropertyType ("Long");
/**
* Property type for an <code>Object</code>.
*/
public static final PropertyType OBJECT = new PropertyType ("Object");
/**
* Property type for an <code>AESAudioMetadata</code>.
*/
public static final PropertyType AESAUDIOMETADATA =
new PropertyType ("AESAudioMetadata");
/**
* Property type for a <code>NisoImageMetadata</code>.
*/
public static final PropertyType NISOIMAGEMETADATA =
new PropertyType ("NISOImageMetadata");
/**
* Property type for a <code>TextMDMetadata</code>.
*/
public static final PropertyType TEXTMDMETADATA =
new PropertyType ("TextMDMetadata");
/**
* Property type for a <code>Property</code> object.
*/
public static final PropertyType PROPERTY = new PropertyType ("Property");
/**
* Property type for a <code>Short</code> object, or a
* <code>short</code> if the Arity is Array.
*/
public static final PropertyType SHORT = new PropertyType ("Short");
/**
* Property type for a <code>String</code> object.
*/
public static final PropertyType STRING = new PropertyType ("String");
/**
* Property type for a <code>Rational</code> object.
*/
public static final PropertyType RATIONAL = new PropertyType ("Rational");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create PropertyTypes directly.
**/
private PropertyType (String value)
{
super (value);
}
}
@@ -1,261 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.*;
/**
* An InputStream layered on top of a RandomAccessFile.
* This is useful for a Module which has requirements that
* force it to use a RandomAccessFile, but is usually
* accessed sequentially.
*
* An RAFInputStream maintains its own position information
* in the file, so multiple RAFInputStreams in the same file
* will work without interference. However, this class is
* not thread-safe.
*
* @author Gary McGath
*
*/
public class RAFInputStream extends InputStream {
/* The file on which the stream is based */
private RandomAccessFile _raf;
/* Size of fileBuf */
private int fileBufSize;
/* Buffer for reading from the file */
private byte[] fileBuf;
/* Offset for reading next byte from filebuf */
private int fileBufPos;
/* Number of valid bytes in fileBuf */
private int fileBufBytes;
/* Position in file for next read from RandomAccessFile */
private long fileOffset;
/* EOF flag */
boolean eof;
/**
* Constructor with default buffer size.
* The stream starts at the current position of the
* RandomAccessFile.
*
* @param raf The file on which the
* stream is to be based.
*/
public RAFInputStream(RandomAccessFile raf) {
super();
_raf = raf;
fileBufSize = 65536; // pick a size, any size
init ();
}
/**
* Constructor with buffer size.
* The stream starts at the current position of the
* RandomAccessFile.
*
* @param raf The file on which the
* stream is to be based.
*
* @param bufferSize The buffer size to be used.
* If less than or equal to 0, the
* default buffer size is used.
*/
public RAFInputStream(RandomAccessFile raf, int bufferSize) {
super();
_raf = raf;
fileBufSize = (bufferSize <= 0 ? 65536 : bufferSize);
init ();
}
private void init ()
{
fileBufBytes = 0;
fileBufPos = 0;
fileBuf = new byte[fileBufSize];
try {
fileOffset = _raf.getFilePointer ();
}
catch (IOException e) {}
eof = false;
}
/**
* Reads a single byte from the file.
*/
public int read() throws IOException {
if (eof) {
return -1;
}
if (fileBufPos >= fileBufBytes) {
// Need to read another bufferful
_raf.seek (fileOffset);
fileBufBytes = _raf.read (fileBuf);
fileBufPos = 0;
if (fileBufBytes <= 0) {
// No more in file
eof = true;
return -1;
}
else {
fileOffset += fileBufBytes;
}
}
return ((int) fileBuf[fileBufPos++] & 0XFF);
}
/**
* Reads some number of bytes from the input stream and
* stores them into the buffer array b. The number of
* bytes actually read is returned as an integer.
*/
public int read (byte[] b) throws IOException
{
int bytesToRead = b.length;
int bytesRead = 0;
for (;;) {
// See how many bytes are available in fileBuf.
int fbAvail = fileBufBytes - fileBufPos;
if (fbAvail <= 0) {
// Need to read another bufferful
_raf.seek (fileOffset);
fileBufBytes = _raf.read (fileBuf);
fileBufPos = 0;
if (fileBufBytes <= 0) {
// No more in file -- return what we have
eof = true;
return bytesRead;
}
fbAvail = fileBufBytes;
fileOffset += fileBufBytes;
}
if (fbAvail > bytesToRead) {
// We have more than enough bytes.
fbAvail = bytesToRead;
}
for (int i = 0; i < fbAvail; i++) {
b[bytesRead++] = fileBuf[fileBufPos++];
bytesToRead--;
}
if (bytesToRead == 0) {
return bytesRead;
}
}
}
/**
* Reads up to len bytes of data from the input stream
* into an array of bytes. An attempt is made to read as
* many as len bytes, but a smaller number may be read,
* possibly zero. The number of bytes actually read is
* returned as an integer.
*
*/
public int read(byte[] b, int off, int len) throws IOException
{
int bytesToRead = len;
int bytesRead = 0;
for (;;) {
// See how many bytes are available in fileBuf.
int fbAvail = fileBufBytes - fileBufPos;
if (fbAvail <= 0) {
// Need to read another bufferful
_raf.seek (fileOffset);
fileBufBytes = _raf.read (fileBuf);
fileBufPos = 0;
if (fileBufBytes <= 0) {
// No more in file -- return what we have
eof = true;
return bytesRead;
}
fbAvail = fileBufBytes;
fileOffset += fileBufBytes;
}
if (fbAvail > bytesToRead) {
// We have more than enough bytes.
fbAvail = bytesToRead;
}
for (int i = 0; i < fbAvail; i++) {
b[off + bytesRead++] = fileBuf[fileBufPos++];
bytesToRead--;
}
if (bytesToRead == 0) {
return bytesRead;
}
}
}
/** Skips some number of bytes.
*
* @return The number of bytes actually skipped.
*/
public long skip (long n) throws IOException
{
// If the range of the skip lies
// within the current buffer, we just adjust
// the buffer offset.
int bytesLeft = fileBufBytes - fileBufPos;
if (bytesLeft > n) {
fileBufPos += (int) n;
}
else {
// doesn't fit within the buffer.
// Set up to seek to the new position.
//long curPos = _raf.getFilePointer ();
if (fileOffset + n - bytesLeft > _raf.length ()) {
fileOffset = _raf.length ();
}
//seek (curPos + n - bytesLeft);
else {
fileOffset += n - bytesLeft;
}
fileBufBytes = 0; // Invalidate current buffer
}
return n;
}
/**
* Returns the RandomAccessFile object.
*/
public RandomAccessFile getRAF ()
{
return _raf;
}
/** Positions the stream to a different point in the file.
* This invalidates the buffer.
*/
public void seek (long offset) throws IOException
{
_raf.seek (offset);
fileBufBytes = 0;
fileBufPos = 0;
eof = false;
}
/** Returns the current position in the file.
* What is reported is the position of the byte
* in the file which was last extracted from
* the buffer.
*/
public long getFilePos () throws IOException
{
return _raf.getFilePointer() -
(fileBufBytes - fileBufPos);
}
}
@@ -1,92 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* Class encapsulating RFC-1766 language codes.
*/
public final class RFC1766Lang
{
String _langCode;
/**
* Constructor.
*
* @param str The ASCII string for the language code.
*/
public RFC1766Lang (String str)
{
_langCode = str;
}
/**
* Returns the language code string.
*/
public String getLangCode ()
{
return _langCode;
}
/**
* Returns <code>true</code> if the language code
* string is syntactically compliant.
* The primary tag must be either a two-letter code,
* the letter i, or the letter x (case insensitive);
* no checking is done against registry lists.
*/
public boolean isSyntaxCorrect ()
{
int i;
if (_langCode == null) {
return false;
}
char[] chrs = _langCode.toLowerCase().toCharArray();
char firstChar = '\0';
int ntags = 0;
int taglength = 0;
for (i = 0; i < chrs.length; i++) {
char ch = chrs[i];
if (i == 0) {
firstChar = ch;
}
if (!Character.isLetter (ch) && ch != '-') {
return false;
}
if (ch == '-') {
taglength = 0;
// If this is the primary tag, do some checks
if (ntags++ == 0) {
if (taglength == 1) {
if (firstChar != 'i' && firstChar != 'x') {
return false;
}
else if (taglength != 2) {
return false;
}
}
}
}
else {
taglength++;
if (taglength > 8) {
return false;
}
}
}
// A dangling hyphen at the end isn't allowed.
if (taglength == 0) {
return false;
}
return true;
}
}
@@ -1,100 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates a number which is defined as the ratio
* of two 32-bit unsigned integers, in accordance with the TIFF
* specification.
*/
public class Rational
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** Numerator of ratio. */
private long _numerator;
/** Denominator of ratio. */
private long _denominator;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* The arguments to this constructor are long in order to
* represent all possible 32-bit unsigned integers. Parameters
* greater than 2 ^ 32 - 1 are not meaningful.
* @param numerator numerator of the Rational value
* @param denominator denominator of the Rational value
*/
public Rational (long numerator, long denominator)
{
_numerator = numerator;
_denominator = denominator;
}
/**
* The arguments to the int constructor are treated as
* 32-bit unsigned integers.
* @param numerator numerator of the Rational value
* @param denominator denominator of the Rational value
*/
public Rational (int numerator, int denominator)
{
_numerator = (long) numerator & 0XFFFFFFFF;
_denominator = (long) denominator & 0XFFFFFFFF;
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
******************************************************************/
/**
* Returns the Numerator property.
*/
public long getNumerator ()
{
return _numerator;
}
/**
* Returns the Denominator property.
*/
public long getDenominator()
{
return _denominator;
}
/**
* Converts to a floating-point value (numerator/denominator).
* May throw an ArithmeticException.
**/
public double toDouble ()
{
return ((double) _numerator / (double) _denominator);
}
/**
* Converts to a long value (numerator/denominator).
* May throw an ArithmeticException.
**/
public long toLong ()
{
return (long) ((double) _numerator / (double) _denominator);
}
/**
* Represents the Rational as a String in the form of
* "numerator/denominator".
*/
public String toString ()
{
return Long.toString (_numerator) + "/" +
Long.toString (_denominator);
}
}
@@ -1,599 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003-2005 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.util.*;
/**
* This class encapsulates representation information, as defined
* by ISO/IEC 14721, about a content stream.
*
* @see <a href="http://wwwclassic.ccsds.org/documents/pdf/CCSDS-650.0-B-1.pdf">ISO/IEC
* 14721 (PDF)</a>
*/
public class RepInfo implements Cloneable
{
/******************************************************************
* PRIVATE INSTANCE FIELDS.
******************************************************************/
/** List of checksums. */
private List<Checksum> _checksum;
/** Consistency flag. */
private boolean _consistent;
/** Validity flag. A ternary variable which can have a value
* of TRUE, FALSE, or UNDETERMINED. */
private int _valid;
/** Values for _valid */
public final static int
TRUE = 1,
FALSE = 0,
UNDETERMINED = -1;
/** Creation date. */
private Date _created;
/** External representation information. */
private RepInfo _external;
/** Format identifier. */
private String _format;
/** Modification date. */
private Date _lastModified;
/** List of diagnostic and informative messages. */
private List<Message> _message;
/** MIME media type. */
private String _mimeType;
/** The module used to populate this representation information. */
private Module _module;
/** List of conforming format profiles. */
private List<String> _profile;
/** List of modules for which signature matches. */
private List<String> _sigMatch;
/** Associative map of module-specific representation information. */
private Map<String, Property> _property;
/** Object size. */
private long _size;
/** Object file pathname or URI. */
private String _uri;
/** Flag indicating _uri is a URL if true. */
private boolean _urlFlag;
/** Well-formed flag. A ternary variable which can have a value
* of TRUE, FALSE, or UNDETERMINED. */
private int _wellFormed;
/** Version of format which applies. */
private String _version;
/** Note. */
private String _note;
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Creates a RepInfo with a URI reference
*
* @param uri Object file pathname or URI
*/
public RepInfo (String uri)
{
init (uri);
}
/**
* Creates a RepInfo with a URI reference and an external RepInfo.
*
* By default, urlFlag is <code>false</code>.
*
* @param uri Object file pathname or URI
* @param external External representation information
*/
public RepInfo (String uri, RepInfo external)
{
init (uri);
_external = external;
}
private void init (String uri)
{
_uri = uri;
_size = -1;
_wellFormed = TRUE;
_consistent = true;
_urlFlag = false;
_valid = TRUE;
_checksum = new ArrayList<Checksum> ();
_message = new ArrayList<Message> ();
_profile = new ArrayList<String> ();
_property = new TreeMap<String, Property> ();
_sigMatch = new ArrayList<String> ();
}
/******************************************************************
* PUBLIC INSTANCE METHODS.
******************************************************************/
/**
* Clones the RepInfo one level deep, making fresh copies
* of the checksum, message, profile, signature match,
* and property fields.
* The external RepInfo (if any) is not cloned, but
* is attached directly to the clone.
*/
public Object clone ()
{
RepInfo newri;
try {
newri = (RepInfo) super.clone ();
}
catch (CloneNotSupportedException e) {
return null; // should never happen
}
newri._checksum = new ArrayList<Checksum> (_checksum);
newri._message = new ArrayList<Message>(_message);
newri._profile = new ArrayList<String> (_profile);
newri._sigMatch = new ArrayList<String> (_sigMatch);
newri._property = new TreeMap<String, Property> (_property);
return (Object) newri;
}
/**
* Copies all the information out of the parameter object.
* This is a "shallow" copy; it is assumed that the parameter
* object is a temporary one that will not be further modified.
*/
public void copy (RepInfo info)
{
_checksum = info._checksum;
_consistent = info._consistent;
_created = info._created;
_external = info._external;
_format = info._format;
_lastModified = info._lastModified;
_message = info._message;
_mimeType = info._mimeType;
_profile = info._profile;
_property = info._property;
_size = info._size;
_uri = info._uri;
_urlFlag = info._urlFlag;
_wellFormed = info._wellFormed;
_valid = info._valid;
_version = info._version;
_note = info._note;
_module = info._module;
_sigMatch = info._sigMatch;
}
/******************************************************************
*
* Accessor methods.
******************************************************************/
/**
* Returns this object's list of Checksums
*/
public List<Checksum> getChecksum ()
{
return _checksum;
}
/**
* Returns the creation date stored in this object. A creation
* date is not automatically generated, but must be explicitly
* stored.
**/
public Date getCreated ()
{
return _created;
}
/**
* Return the format identifier
*/
public String getFormat ()
{
return _format;
}
/**
* Returns the last modified date stored in this object. A
* date is not automatically generated, but must be explicitly
* stored.
**/
public Date getLastModified ()
{
return _lastModified;
}
/**
* Returns the message list stored in this object
*/
public List<Message> getMessage ()
{
return _message;
}
/**
* Returns the MIME type string stored in this object
*/
public String getMimeType ()
{
return _mimeType;
}
/**
* Return the module.
*/
public Module getModule ()
{
return _module;
}
/**
* Returns the list of profiles (Strings) stored in this object
*/
public List<String> getProfile ()
{
return _profile;
}
/**
* Returns the Property map stored in this object. The
* Property map contains key-value pairs whose key is a
* <code>String</code> and whose value is a <code>Property</code>.
*/
public Map<String, Property> getProperty ()
{
return _property;
}
/**
* Returns a named Property from the Property map
*
* @param name The name of the Property.
*/
public Property getProperty (String name)
{
Property property = null;
if (_property.size () > 0) {
property = (Property) _property.get (name);
}
return property;
}
/**
* Returns the size property stored in this object.
*/
public long getSize ()
{
return _size;
}
/**
* Returns the URI property stored in this object.
*/
public String getUri ()
{
return _uri;
}
/**
* Returns a flag which, if <code>true</code>, indicates
* the object is a URL.
*/
public boolean getURLFlag ()
{
return _urlFlag;
}
/**
* Returns the value of the consistency flag.
*/
public boolean isConsistent ()
{
return _consistent;
}
/**
* Returns the value of the well-formed flag.
* Can return TRUE, FALSE, or UNDETERMINED.
*/
public int getWellFormed ()
{
return _wellFormed;
}
/**
* Returns the value of the validity flag.
* Can return TRUE, FALSE, or UNDETERMINED.
*/
public int getValid ()
{
return _valid;
}
/**
* Returns the version property stored in this object
*/
public String getVersion ()
{
return _version;
}
/**
* Returns the note property stored in this object
*/
public String getNote ()
{
return _note;
}
/**
* Returns the list of matching signatures.
* JhoveBase will make this value persistent across
* module invocations for a given document, so the list
* returned will reflect all modules that have looked
* at the document so far.
*/
public List<String> getSigMatch ()
{
return _sigMatch;
}
/**
* Return property by name, regardless of its position in the
* property hierarchy.
* @param name Property name
* @return Named property (or null)
*/
public Property getByName (String name)
{
Property prop = null;
Collection<Property> coll = _property.values ();
Iterator<Property> iter = coll.iterator ();
while (iter.hasNext ()) {
prop = (Property) iter.next ();
if ((prop = prop.getByName (name)) != null) {
break;
}
}
return prop;
}
/******************************************************************
* Mutator methods.
******************************************************************/
/**
* Append a Checksum object to the checksum list.
*/
public void setChecksum (Checksum checksum)
{
_checksum.add (checksum);
}
/**
* Set the value of the consistency flag
*/
public void setConsistent (boolean consistent)
{
_consistent = consistent;
}
/**
* Set the creation date
*/
public void setCreated (Date created)
{
_created = created;
}
/**
* Set the format identifier
*/
public void setFormat (String format)
{
_format = format;
}
/**
* Set the last modified date
*/
public void setLastModified (Date lastModified)
{
_lastModified = lastModified;
}
/**
* Append a Message object to the message list
*/
public void setMessage (Message message)
{
_message.add (message);
}
/**
* Set the MIME type string
*/
public void setMimeType (String mimeType)
{
_mimeType = mimeType;
}
/**
* Add the module.
*/
public void setModule (Module module)
{
_module = module;
}
/**
* Append a profile String to the profile list
*/
public void setProfile (String profile)
{
_profile.add (profile);
}
/**
* Add a Property to the property map. The name of the Property
* becomes its key in the map.
*/
public void setProperty (Property property)
{
_property.put (property.getName (), property);
}
/**
* Set the size property
*/
public void setSize (long size)
{
_size = size;
}
/**
* Set the flag to indicate whether this is a URL (true)
* or a file (false)
*/
public void setURLFlag (boolean flag)
{
_urlFlag = flag;
}
/**
* Set the well-formed flag
*
* @param wellFormed Boolean argument that maps to
* an integer value:
* true maps to TRUE, and false to FALSE.
*/
public void setWellFormed (boolean wellFormed)
{
_wellFormed = wellFormed ? TRUE : FALSE;
if (!wellFormed) {
_consistent = false;
_valid = FALSE;
}
}
/**
* Set the wellFormed flag.
* Setting wellFormed to false forces the consistent and
* valid flags to be false as well.
*/
public void setWellFormed (int wellFormed)
{
_wellFormed = wellFormed;
if (wellFormed == FALSE) {
_consistent = false;
_valid = FALSE;
}
if (wellFormed == UNDETERMINED) {
_valid = UNDETERMINED;
}
}
/**
* Set the validity flag
*
* @param valid Boolean argument that maps to
* an integer value:
* true maps to TRUE, and false to FALSE.
*/
public void setValid (boolean valid)
{
_valid = valid ? TRUE : FALSE;
}
/**
* Set the validity flag
*
* @param valid Permitted values are TRUE, FALSE, AND
* UNDETERMINED. The effect of using
* other values is undefined.
*/
public void setValid (int valid)
{
_valid = valid;
}
/**
* Set the version string
*/
public void setVersion (String version)
{
_version = version;
}
/**
* Set the note string
*/
public void setNote (String note)
{
_note = note;
}
/** Adds the name of a module, signifying that the document
* signature matched the module's requirements.
* JhoveBase will make this value persistent across
* module invocations for a given document.
*/
public void setSigMatch (String modname)
{
_sigMatch.add (modname);
}
/** Adds a list of module names, signifying that the document
* signature matched the module's requirements.
* Any previous list is lost.
* JhoveBase will make this value persistent across
* module invocations for a given document.
*/
public void setSigMatch (List<String> modnames)
{
_sigMatch = modnames;
}
/******************************************************************
* Serialization methods.
******************************************************************/
/**
* Output the information in this object. The format and
* destination of the output are determined by the
* OutputHandler.
*/
public void show (OutputHandler handler)
{
handler.analyze (this);
handler.show (this);
}
}
@@ -1,157 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class encapsulates information about format signatures,
* both internal and external.
* The value of a Signature may be either a String or a byte array
* (stored as an int array to avoid signed byte problems).
*/
public abstract class Signature
{
private int[] _value;
private String _stringValue;
private SignatureType _type;
private SignatureUseType _use;
private String _note;
/**
* A Signature cannot be created directly; this constructor
* can be called as the superclass constructor from a subclass.
* This constructor uses a String value.
*/
protected Signature (String value, SignatureType type,
SignatureUseType use)
{
this (new int[value.length ()], type, use);
int len = value.length ();
_stringValue = value;
for (int i = 0; i < len; i++) {
_value[i] = value.charAt(i);
}
}
/**
* A Signature cannot be created directly; this constructor
* can be called as the superclass constructor from a subclass.
* This constructor uses a byte array (stored as an int array) value.
*/
protected Signature (int[] value, SignatureType type,
SignatureUseType use)
{
_value = value;
_type = type;
_use = use;
_stringValue = null;
}
/**
* A Signature cannot be created directly; this constructor
* can be called as the superclass constructor from a subclass.
* This constructor uses a String value and allows specification
* of a note.
*/
protected Signature (String value, SignatureType type,
SignatureUseType use,
String note)
{
this (new int[value.length ()], type, use, note);
int len = value.length ();
for (int i = 0; i < len; i++) {
_value[i] = value.charAt(i);
}
_stringValue = value;
}
/**
* A Signature cannot be created directly; this constructor
* can be called as the superclass constructor from a subclass.
* This constructor uses a byte array (stored as an int array) value
* and allows specification of a note.
*/
protected Signature (int[] value, SignatureType type,
SignatureUseType use,
String note)
{
this (value, type, use);
_note = note;
}
/**
* Returns the type of this Signature
*/
public SignatureType getType ()
{
return _type;
}
/**
* Returns the use requirement for this Signature
*/
public SignatureUseType getUse ()
{
return _use;
}
/**
* Returns the byte array value for this Signature.
* If this Signature was constructed from a String, it
* returns the characters of the String as the bytes of
* the array.
*/
public int[] getValue ()
{
return _value;
}
/**
* Returns the note specified for this Signature, or null
* if no note was specified.
*/
public String getNote ()
{
return _note;
}
/**
* Returns true if this Signature's value was provided as a
* String, false if as an array.
*/
public boolean isStringValue ()
{
return (_stringValue != null);
}
/**
* Returns the string value of this Signature. Returns null
* if this Signature was constructed with an array.
*/
public String getValueString ()
{
return _stringValue;
}
/**
* Returns the value of this Signature as a hexadecimal string.
* The length of the string is twice the length of the array
* or string from which this Signature was created, and all
* alphabetic characters are lower case.
*/
public String getValueHexString ()
{
StringBuffer valBuf = new StringBuffer ("0x");
for (int i = 0; i < _value.length; i++) {
/* Make each byte exactly two digits */
int b = _value[i];
if (b < 16) {
valBuf.append ('0');
}
valBuf.append (Integer.toHexString (b));
}
return valBuf.toString ();
}
}
@@ -1,54 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated types for a Signature in a module.
* Applications will not create or modify SignatureTypes, but will
* use one of the predefined SignatureType instances
* EXTENSION, FILETYPE, or MAGIC.
*
* @see Signature
*/
public final class SignatureType
extends EnumerationType
{
/******************************************************************
* PUBLIC STATIC INSTANCES.
******************************************************************/
/**
* Signature type for a file extension, i.e., a sequence of
* characters following a period character in a file name.
*/
public static final SignatureType EXTENSION =
new SignatureType ("File extension");
/**
* Signature type for a Macintosh OS file type. This applies
* only to Mac OS files, and is always a four-character code.
*/
public static final SignatureType FILETYPE =
new SignatureType ("Mac OS file type");
/**
* Signature type for a "magic number" stored in the file.
*/
public static final SignatureType MAGIC =
new SignatureType ("Magic number");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create SignatureTypes directly.
**/
private SignatureType (String value)
{
super (value);
}
}
@@ -1,55 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
/**
* This class defines enumerated use types for a Signature in a module.
* These give information on whether a signature is required in
* valid content.
* Applications will not create or modify SignatureUseTypes, but will
* use one of the predefined SignatureUseType instances
* MANDATORY, MANDATORY_IF_APPLICABLE, or OPTIONAL.
*
* @see Signature
*/
public final class SignatureUseType
extends EnumerationType
{
/******************************************************************
* PRIVATE CLASS FIELDS.
******************************************************************/
/**
* Use type for a required signature.
*/
public static final SignatureUseType MANDATORY =
new SignatureUseType ("Mandatory");
/**
* Use type for a conditionally required signature.
*/
public static final SignatureUseType MANDATORY_IF_APPLICABLE =
new SignatureUseType ("Mandatory if applicable");
/**
* Use type for an optional signature.
*/
public static final SignatureUseType OPTIONAL =
new SignatureUseType ("Optional");
/******************************************************************
* CLASS CONSTRUCTOR.
******************************************************************/
/**
* Applications will never create SignatureUseTypes directly.
**/
private SignatureUseType (String value)
{
super (value);
}
}
@@ -1,438 +0,0 @@
package edu.harvard.hul.ois.jhove;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* Encapsulation of the textMD metadata for text files.
* See http://www.loc.gov/standards/textMd for more information.
*
* @author Thomas Ledoux
*
*/
public class TextMDMetadata {
/**
* textMD namespace and version
*/
public static final String NAMESPACE = "info:lc/xmlns/textMD-v3";
public static final String DEFAULT_LOCATION =
"http://www.loc.gov/standards/textMD/textMD-v3.01a.xsd";
public static final String VERSION = "3.0";
/**
* Uses enumerated values of 'big', 'little', and 'middle' endian.
*/
public static final String[] BYTE_ORDER = {
"big", "little", "middle"
};
public static final int BYTE_ORDER_BIG = 0;
public static final int BYTE_ORDER_LITTLE = 1;
public static final int BYTE_ORDER_MIDDLE= 2;
/**
* Uses enumerated values of 'CR', 'LF' and 'CR/LF' for the idenntification of the linebreak.
*/
public static final String[] LINEBREAK = {
"CR", "LF", "CR/LF"
};
public static final int LINEBREAK_CR = 0;
public static final int LINEBREAK_LF = 1;
public static final int LINEBREAK_CRLF = 2;
/**
* Array of textMD charsets unknown by java.nio.charset.Charsets
*/
protected static final String[] UNKNOWN_JAVA_CHARSET =
new String[] {
"ISO-10646-UTF-1", "ISO_646.basic:1983", "INVARIANT", "BS_4730", "NATS-SEFI",
"NATS-SEFI-ADD", "NATS-DANO", "NATS-DANO-ADD", "SEN_850200_B", "SEN_850200_C", "ISO-2022-CN-EXT",
"JIS_C6220-1969-jp", "JIS_C6220-1969-ro", "IT", "PT", "ES", "greek7-old", "latin-greek",
"DIN_66003", "NF_Z_62-010_(1973)", "latin-greek-1", "ISO_5427", "JIS_C6226-1978", "BS_viewdata",
"INIS", "INIS-8", "INIS-cyrillic", "ISO_5427:1981", "ISO_5428:1980", "GB_1988-80", "GB_2312-80",
"NS_4551-1", "NS_4551-2", "NF_Z_62-010", "videotex-suppl", "PT2", "ES2", "MSZ_7795.3", "greek7",
"ASMO_449", "iso-ir-90", "JIS_C6229-1984-a", "JIS_C6229-1984-b", "JIS_C6229-1984-b-add",
"JIS_C6229-1984-hand", "JIS_C6229-1984-hand-add", "JIS_C6229-1984-kana", "ISO_2033-1983",
"ANSI_X3.110-1983", "T.61-7bit", "ECMA-cyrillic", "CSA_Z243.4-1985-1", "CSA_Z243.4-1985-2",
"CSA_Z243.4-1985-gr", "ISO_8859-6-E", "ISO_8859-6-I", "T.101-G2", "ISO_8859-8-E", "ISO_8859-8-I",
"CSN_369103", "JUS_I.B1.002", "ISO_6937-2-add", "IEC_P27-1", "JUS_I.B1.003-serb",
"JUS_I.B1.003-mac", "greek-ccitt", "NC_NC00-10:81", "ISO_6937-2-25", "GOST_19768-74",
"ISO_8859-supp", "ISO_10367-box", "ISO-8859-10", "latin-lap", "DS_2089", "us-dk", "dk-us",
"KSC5636", "ISO-10646-UCS-4", "DEC-MCS", "hp-roman8", "macintosh", "IBM038", "IBM274", "IBM275",
"IBM281", "IBM290", "IBM423", "IBM851", "IBM880", "IBM891", "IBM903", "IBM904", "IBM905",
"EBCDIC-AT-DE", "EBCDIC-AT-DE-A", "EBCDIC-CA-FR", "EBCDIC-DK-NO", "EBCDIC-DK-NO-A", "EBCDIC-FI-SE",
"EBCDIC-FI-SE-A", "EBCDIC-FR", "EBCDIC-IT", "EBCDIC-PT", "EBCDIC-ES", "EBCDIC-ES-A", "EBCDIC-ES-S",
"EBCDIC-UK", "EBCDIC-US", "UNKNOWN-8BIT", "MNEMONIC", "MNEM", "VISCII", "VIQR", "IBM00924",
"UNICODE-1-1", "SCSU", "UTF-7", "CESU-8", "UNICODE-1-1-UTF-7", "ISO-8859-14", "ISO-8859-16",
"Extended_UNIX_Code_Fixed_Width_for_Japanese", "ISO-10646-UCS-Basic", "ISO-10646-Unicode-Latin1",
"ISO-10646-J-1", "ISO-Unicode-IBM-1268", "ISO-Unicode-IBM-1276", "ISO-Unicode-IBM-1264",
"ISO-Unicode-IBM-1265", "ISO-8859-1-Windows-3.0-Latin-1", "ISO-8859-1-Windows-3.1-Latin-1",
"ISO-8859-2-Windows-Latin-2", "ISO-8859-9-Windows-Latin-5", "Adobe-Standard-Encoding",
"Ventura-US", "Ventura-International", "PC8-Danish-Norwegian", "PC8-Turkish", "IBM-Symbols",
"HP-Legal", "HP-Pi-font", "HP-Math8", "Adobe-Symbol-Encoding", "HP-DeskTop", "Ventura-Math",
"Microsoft-Publishing", "HZ-GB-2312", };
/**
* Set of unknown charsets in Java
*/
protected static Set setOfUnknownJavaCharset;
/**
* Map from ISO 639/2 T to ISO 639/2 B
*/
protected static Map<String, String> fromISO_639_2_T2B;
public static final String CHARSET_ASCII = "US-ASCII";
public static final String CHARSET_UTF8 = "UTF-8";
public static final String CHARSET_ISO8859_1 = "ISO-8859-1";
/**
* To represent the unknown
*/
public static final int NILL = -1;
static {
setOfUnknownJavaCharset = new HashSet(Arrays.asList(UNKNOWN_JAVA_CHARSET));
// Map to transform from the terminology code to the bibliographic one
fromISO_639_2_T2B = new HashMap<String, String>();
fromISO_639_2_T2B.put("sqi", "alb");
fromISO_639_2_T2B.put("hye", "arm");
fromISO_639_2_T2B.put("eus", "baq");
fromISO_639_2_T2B.put("mya", "bur");
fromISO_639_2_T2B.put("zho", "chi");
fromISO_639_2_T2B.put("ces", "cze");
fromISO_639_2_T2B.put("nld", "dut");
fromISO_639_2_T2B.put("fra", "fre");
fromISO_639_2_T2B.put("kat", "geo");
fromISO_639_2_T2B.put("deu", "ger");
fromISO_639_2_T2B.put("ell", "gre");
fromISO_639_2_T2B.put("isl", "ice");
fromISO_639_2_T2B.put("mkd", "mac");
fromISO_639_2_T2B.put("mri", "mao");
fromISO_639_2_T2B.put("msa", "may");
fromISO_639_2_T2B.put("fas", "per");
fromISO_639_2_T2B.put("ron", "rum");
fromISO_639_2_T2B.put("slk", "slo");
fromISO_639_2_T2B.put("bod", "tib");
fromISO_639_2_T2B.put("cym", "wel");
}
/**
charset
Usage: The character set employed by the text. Controlled vocab using IANA names for character sets.
Attributes: none.
Contains: none.
Contained by: character_info.
*/
private String charset;
/**
byte_order
Usage: Byte order, primarily useful for cases where it's not clear just by specifying an IANA character set.
Uses enumerated values of big, little, and middle' endian.
Attributes: none.
Contains: none.
Contained by: character_info.
*/
private int byte_order = NILL;
/**
byte_size
Usage: The size of an individual byte within the expressed as a number of bits (as integer). This does not necessarily equal the character size, as a character may have more than one, or a variable number of bytes per character.
Attributes: none.
Contains: none.
Contained by: character_info.
*/
private String byte_size;
/**
character_size
Usage: The size of an individual character within the character set as a number of bytes of the size expressed in the byte_size. In the case of variable encodings, such as UTF-8 for Unicode, the character_size element should state "variable" and also identify the specific variable character set encoding in the encoding attribute.
Attributes: encoding.
Contains: none.
Contained by: character_info.
*/
private String character_size;
/**
linebreak
Usage: How line breaks are represented in current file (which may differ from how they were originally encoded). Either carriage return, line feed, or carriage return/line feed.
Attributes: none.
Contains: none.
Contained by: character_info.
*/
private int linebreak = NILL;
/**
language
Usage: Language(s) used in work. Use ISO 639-2 codes, which are enumerated in the schema as valid text values.
Attributes: none.
Contains: none.
Contained by: textMD.
*/
private String language;
/**
markup_basis
Usage: The metalanguage used to create the markup language, such as SGML, XML, GML, etc.
Attributes: version.
Contains: none.
Contained by: textMD.
*/
private String markup_basis;
/**
version
Usage: Used to record the version number (as a string) for a given piece of software, a markup language, or a schema version.
*/
private String markup_basis_version;
/**
markup_language
Usage: Markup language employed on the text (i.e., the specific schema or dtd). May be a URI for schema or dtd, but not mandatory.
Attributes: version.
Contains: none.
Contained by: textMD.
*/
private String markup_language;
/**
version
Usage: Used to record the version number (as a string) for a given piece of software, a markup language, or a schema version.
*/
private String markup_language_version;
/**
* @return the charset
*/
public String getCharset() {
return charset;
}
/**
* @param charset the charset to set
*/
public void setCharset(String charset) {
this.charset = toTextMDCharset(charset);
}
/**
* @return the byte_order
*/
public int getByte_order() {
return byte_order;
}
public String getByte_orderString() {
if (byte_order == NILL) {
return BYTE_ORDER[BYTE_ORDER_BIG]; // default !!!
}
return BYTE_ORDER[byte_order];
}
/**
* @param byte_order the byte_order to set
*/
public void setByte_order(int byte_order) {
this.byte_order = byte_order;
}
/**
* @return the byte_size
*/
public String getByte_size() {
return byte_size;
}
/**
* @param byte_size the byte_size to set
*/
public void setByte_size(String byte_size) {
this.byte_size = byte_size;
}
/**
* @return the character_size
*/
public String getCharacter_size() {
return character_size;
}
/**
* @param character_size the character_size to set
*/
public void setCharacter_size(String character_size) {
this.character_size = character_size;
}
/**
* @return the linebreak
*/
public int getLinebreak() {
return linebreak;
}
/**
* @return the linebreak in String form
*/
public String getLinebreakString() {
if (linebreak == NILL) {
return LINEBREAK[LINEBREAK_CRLF]; // default !!!
}
return LINEBREAK[linebreak];
}
/**
* @param linebreak the linebreak to set
*/
public void setLinebreak(int linebreak) {
this.linebreak = linebreak;
}
/**
* @return the language
*/
public String getLanguage() {
return language;
}
/**
* @param language the language to set
*/
public void setLanguage(String language) {
this.language = toISO_639_2(language);
}
/**
* @return the markup_basis
*/
public String getMarkup_basis() {
return markup_basis;
}
/**
* @param markup_basis the markup_basis to set
*/
public void setMarkup_basis(String markup_basis) {
this.markup_basis = markup_basis;
}
/**
* @return the markup_basis_version
*/
public String getMarkup_basis_version() {
return markup_basis_version;
}
/**
* @param markup_basis_version the markup_basis_version to set
*/
public void setMarkup_basis_version(String markup_basis_version) {
this.markup_basis_version = markup_basis_version;
}
/**
* @return the markup_language
*/
public String getMarkup_language() {
return markup_language;
}
/**
* @param markup_language the markup_language to set
*/
public void setMarkup_language(String markup_language) {
this.markup_language = markup_language;
}
/**
* @return the markup_language_version
*/
public String getMarkup_language_version() {
return markup_language_version;
}
/**
* @param markup_language_version the markup_language_version to set
*/
public void setMarkup_language_version(String markup_language_version) {
this.markup_language_version = markup_language_version;
}
/**
* Transform a given charset in the "authorized" list given in the textMD schema enumeration.
* From the schema documentation on charset (http://www.loc.gov/standards/textMD/elementSet/index.html#element_charset).
* The character set employed by the text. Controlled vocab using IANA names for character sets:
* http://www.iana.org/assignments/character-sets.
* The problem arises because the java Charset uses the (preferred MIME name) where textMD uses the Name ...
* @param srcCharset charset from the file
* @return normalized charset
*/
public static String toTextMDCharset(String srcCharset) {
if (srcCharset == null) return null;
Charset cs = null;
String textMDCharset = null;
try {
cs = Charset.forName(srcCharset);
textMDCharset = cs.name();
} catch (Exception e) {
// Try a unknown one
if (setOfUnknownJavaCharset.contains(srcCharset)) {
textMDCharset = srcCharset;
} else {
// Downgrade to default
textMDCharset = CHARSET_ISO8859_1;
}
}
if (textMDCharset != null) {
return textMDCharset;
} else {
// Downgrade to default
return CHARSET_ISO8859_1;
}
}
/**
* Transform a language to the ISO_639-2 language (only enumeration allowed in textMD schema).
* @param srcLang language in the file
* @return normalized language in 3 letters (except qaa-qtz)
*/
public static String toISO_639_2(String srcLang) {
if (srcLang == null) return null;
if ("qaa-qtz".equals(srcLang)) return srcLang;
String textMDLang = null;
if (srcLang.length() == 3) {
textMDLang = srcLang;
}
else if (srcLang.length() == 2) {
try {
Locale loc = new Locale(srcLang);
textMDLang = loc.getISO3Language();
} catch (Exception e) {
// Unknown language
}
}
else if (srcLang.length() > 3) {
// Just try with the first 2 characters
try {
Locale loc = new Locale(srcLang.substring(0, 2));
srcLang = loc.getISO3Language();
} catch (Exception e) {
// Unknown language
}
}
if (textMDLang != null && textMDLang.length() == 3) {
// From ISO 639-2/T to ISO 639-2/B
if (fromISO_639_2_T2B.containsKey(textMDLang)) {
textMDLang = (String)fromISO_639_2_T2B.get(textMDLang);
}
return textMDLang;
}
return null;
}
}
@@ -1,62 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2004 by JSTOR and the President and Fellows of Harvard College
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import edu.harvard.hul.ois.jhove.module.Utf8Block;
import java.util.*;
/**
*
* @author Gary McGath
*
*/
public class Utf8BlockMarker {
private boolean[] blocksUsed;
public Utf8BlockMarker () {
blocksUsed = new boolean [Utf8Block.unicodeBlock.length];
}
public void markBlock (int code) {
for (int i=0; i<Utf8Block.unicodeBlock.length; i++) {
if (Utf8Block.unicodeBlock[i].getStart () <= code &&
Utf8Block.unicodeBlock[i].getEnd () >= code) {
blocksUsed[i] = true;
break;
}
}
}
/** Returns a Property listing the blocks that have been
* marked as used. If no blocks have been marked,
* returns null. */
public Property getBlocksUsedProperty (String name)
{
List<String> block = new ArrayList<String> (blocksUsed.length);
for (int i=0; i<blocksUsed.length; i++) {
if (blocksUsed[i]) {
block.add (Utf8Block.unicodeBlock[i].getName ());
}
}
if (block.isEmpty ()) {
return null;
}
else {
return new Property (name,
PropertyType.STRING,
PropertyArity.LIST,
block);
}
}
/** Clears all marked blocks. */
public void reset ()
{
for (int i=0; i<Utf8Block.unicodeBlock.length; i++) {
blocksUsed[i] = false;
}
}
}
@@ -1,161 +0,0 @@
/**********************************************************************
* Jhove - JSTOR/Harvard Object Validation Environment
* Copyright 2003 by JSTOR and the President and Fellows of Harvard College
*
**********************************************************************/
package edu.harvard.hul.ois.jhove;
import java.io.IOException;
import java.io.InputStream;
/**
* This class serves to provide an InputStream for an XML
* fragment (e.g., embedded XMP metadata).
*
* @author Gary McGath
*/
public class XMLWrapperStream extends InputStream
{
private String xmlDecl;
private String rootStart;
private String rootEnd;
private int strIndex;
private int state;
/* Values for state */
private final static int DECL = 0,
ROOT_START = 1,
CONTENT = 2,
ROOT_END = 3;
private InputStream _wrappedStream;
/**
* Constructor.
*
* @param wrappedStream The stream which this stream will subsume.
* @param rootName The name of the root element. May be null
* if <code>wrappedStream</code> already contains a root element.
* @param version The XML version. Should be null or "1.0" unless
* there's a really good reason.
* @param encoding The name of the character encoding. May be null.
* @param standalone The value of the <code>standalone</code> attribute. May be null.
*/
public XMLWrapperStream (InputStream wrappedStream,
String rootName,
String version,
String encoding,
String standalone)
{
_wrappedStream = wrappedStream;
xmlDecl = "<?xml version='";
if (version == null) {
xmlDecl += "1.0'";
}
else {
xmlDecl += version + "'";
}
if (encoding != null) {
xmlDecl += " encoding='" + encoding + "' ";
}
if (standalone != null) {
xmlDecl += " standalone='" + standalone + "' ";
}
xmlDecl += "?>";
state = DECL;
strIndex = 0;
if (rootName != null) {
rootStart = "<" + rootName + ">";
rootEnd = "</" + rootName + ">";
}
else {
rootStart = "";
rootEnd = "";
}
}
/**
* Constructor. Equivalent to
* <code>XMLWrapperStream (wrappedStream, null, null, null, null)</code>
*
* @param wrappedStream The stream which this stream will subsume.
*/
public XMLWrapperStream (InputStream wrappedStream)
{
this (wrappedStream, null, null, null, null);
}
/**
* Constructor. Equivalent to
* <code>XMLWrapperStream (wrappedStream, rootName, null, null, null)</code>
*
* @param wrappedStream The stream which this stream will subsume.
* @param rootName The name of the root element. May be null.
*/
public XMLWrapperStream (InputStream wrappedStream, String rootName)
{
this (wrappedStream, rootName, null, null, null);
}
/**
* Get a byte. Successive calls will return the
* XML declaration, then the wrapped stream.
*
* @see java.io.InputStream#read()
*/
public int read() throws IOException
{
int retval;
if (state == DECL) {
if (strIndex >= xmlDecl.length()) {
// We have finished the declaration string now.
state = ROOT_START;
strIndex = 0;
}
else {
// We haven't finished returning the declaration string.
return (int) xmlDecl.charAt(strIndex++);
}
}
// Each state can fall through to the next
if (state == ROOT_START) {
if (strIndex >= rootStart.length()) {
// We have finished the root element start now.
state = CONTENT;
}
else {
return (int) rootStart.charAt(strIndex++);
}
}
if (state == CONTENT) {
// Metadata alleged stream doesn't look remotely like metadata --
// probably looking at wrong part of file!
retval = _wrappedStream.read ();
if (retval == -1) {
state = ROOT_END;
strIndex = 0;
}
else {
return retval;
}
}
// Must be ROOT_END if it gets here
if (strIndex >= rootEnd.length()) {
// We have finished the root element end and the document now.
return -1;
}
else {
return (int) rootEnd.charAt(strIndex++);
}
}
}

Some files were not shown because too many files have changed in this diff Show More