From 545b7f737342ae847e16b2f949ab57eee7bb0186 Mon Sep 17 00:00:00 2001 From: Romain Deltour Date: Tue, 22 Nov 2022 02:04:06 +0100 Subject: [PATCH] feat: improve fallback detection check This commit updates fallback-related checks and refactors the code used for checking references to publication resources. The following checks are introduced or updated: - `RSC-032` (new): reports foreign resources with no fallback used in content documents. Replaces `MED-001`, `MED-002`, and `CSS-010` - `MED-003` now reports when an `img` element child of a `picture` element is not a core media type. - `MED-001` is suppressed. It was used to report a video `poster` attribute did not reference a core image media type. It is now reported as `RSC-032`. - `MED-002` is suppressed. It was used to report HTML elements referencing foreign resources without fallback. It is now reported as `RSC-032`. - `CSS-010` is suppressed. It was used to report references to foreign stylesheets with no fallback. It is now reported as `RSC-032`. - `OPF_040` is now reported when the `fallback` attribute of a package document `item` element does not points to an existing ID. This was previously implemented as a Schematron check (`RSC-005`) in EPUB 3.x. - `OPF-013` is now a warning. It is reported when a MIME type declared inline in content (for instance with an HTML `type` attribute) does not match the MIME type declared in the package document. The code is refactored as follows: - the reference/resource registry functionality of the `XRefChecker` class is extracted to new top-level classes in the (new) package `org.w3c.epubcheck.references`. - `Resource` represents a publication resource - `ResourceRegistry` is a registry of `Resource` instances - `Reference` represents a reference (URL) used anywhere in content - `ReferenceRegistry` is a registry of `Reference` instances - `XRefChecker` is renamed to `ResourceReferencesChecker`. - `ValidationContext` contains optional references to `ResourceRegistry` and `ReferenceRegistry`. - fallback chain resolution is now done in a new `FallbackChainResolver` class, when building `OPFItem` instances from the builders created when parsing the package document. - `XMLHandler` has convenience methods used to register references to the `ReferenceRegistry`. Fix #1304, Fix #1298. --- OCFCheckerCopy.java | 566 -------------- XRefChecker_copy.java | 608 --------------- .../com/adobe/epubcheck/api/EpubCheck.java | 2 +- .../com/adobe/epubcheck/css/CSSHandler.java | 22 +- .../epubcheck/dict/SearchKeyMapHandler.java | 8 +- .../adobe/epubcheck/dtbook/DTBookHandler.java | 22 +- .../epubcheck/messages/DefaultSeverities.java | 7 +- .../adobe/epubcheck/messages/MessageId.java | 1 + .../com/adobe/epubcheck/nav/NavHandler.java | 12 +- .../com/adobe/epubcheck/ncx/NCXHandler.java | 7 +- .../com/adobe/epubcheck/ocf/OCFChecker.java | 4 - .../com/adobe/epubcheck/ocf/OCFContainer.java | 2 +- .../epubcheck/opf/FallbackChainResolver.java | 119 +++ .../com/adobe/epubcheck/opf/OPFChecker.java | 136 +--- .../com/adobe/epubcheck/opf/OPFChecker30.java | 65 +- .../com/adobe/epubcheck/opf/OPFHandler.java | 53 +- .../com/adobe/epubcheck/opf/OPFHandler30.java | 9 +- .../java/com/adobe/epubcheck/opf/OPFItem.java | 144 +++- .../com/adobe/epubcheck/opf/OPFItems.java | 32 +- .../epubcheck/opf/ValidationContext.java | 70 +- .../com/adobe/epubcheck/opf/XRefChecker.java | 723 ------------------ .../com/adobe/epubcheck/ops/OPSHandler.java | 50 +- .../com/adobe/epubcheck/ops/OPSHandler30.java | 544 +++++++------ .../epubcheck/overlay/OverlayHandler.java | 38 +- .../com/adobe/epubcheck/tool/EpubChecker.java | 2 +- .../xml/handlers/BaseURLHandler.java | 2 +- .../epubcheck/xml/handlers/XMLHandler.java | 50 ++ .../org/w3c/epubcheck/constants/MIMEType.java | 34 +- .../epubcheck/core/CheckAbortException.java | 9 + .../epubcheck/core/references/Reference.java | 66 ++ .../core/references/ReferenceRegistry.java | 88 +++ .../epubcheck/core/references/Resource.java | 111 +++ .../references/ResourceReferencesChecker.java | 475 ++++++++++++ .../core/references/ResourceRegistry.java | 111 +++ .../{url => core/references}/URLChecker.java | 2 +- .../java/org/w3c/epubcheck/url/Reference.java | 44 -- .../w3c/epubcheck/url/ReferencesChecker.java | 87 --- .../org/w3c/epubcheck/url/URLRegistry.java | 61 -- .../epubcheck/{ => util}/url/URLFragment.java | 2 +- .../epubcheck/{ => util}/url/URLUtils.java | 2 +- .../messages/MessageBundle.properties | 23 +- .../adobe/epubcheck/schema/30/package-30.sch | 10 - .../w3c/epubcheck/test/ExecutionSteps.java | 2 +- .../w3c/epubcheck/url/URLFragmentSteps.java | 1 + .../org/w3c/epubcheck/url/URLUtilsTest.java | 1 + .../resources/epub2/opf-publication.feature | 2 +- .../EPUB/content_001.xhtml | 13 - .../EPUB/style.css | 6 - .../EPUB/page-template.xpgt | 44 -- ....opf => fallback-chain-circular-error.opf} | 2 +- .../EPUB/content_001.xhtml | 2 +- .../EPUB/image.abc | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 11 + .../EPUB/image1.jpeg} | Bin .../EPUB/image1a.vnd} | 0 .../EPUB/image1b.vnd} | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 21 + .../META-INF/container.xml | 0 .../mimetype | 0 .../files/fallback-chain-valid.opf | 19 +- .../EPUB/content_001.xhtml | 2 +- .../EPUB/image1.jpeg} | Bin .../EPUB/image1a.vnd} | 0 .../EPUB/image1b.vnd} | 0 .../EPUB/image1c.vnd | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 22 + .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/OpenSans-Regular.ttf | Bin .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../EPUB/style.css | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/foreign.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 2 +- .../EPUB/foreign.xyz} | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 2 +- .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../EPUB/page-template.xpgt | 0 .../EPUB/style.css | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../EPUB/page-template.xpgt | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/audio.mpeg | 0 .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 18 + .../EPUB/subtitles.vtt | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/animation.avi | 0 .../EPUB/content_001.xhtml | 10 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 17 + .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 12 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 2 +- .../EPUB/video.avi | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/audio.foreign | Bin .../EPUB/content_001.xhtml | 3 +- .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/audio.foreign | Bin 0 -> 10363 bytes .../EPUB/audio.mpeg | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 4 +- .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 4 +- .../META-INF/container.xml | 6 + .../mimetype | 0 .../EPUB/audio.foreign | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 12 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 3 +- .../META-INF/container.xml | 6 + .../mimetype | 0 .../EPUB/audio.foreign | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../mimetype | 0 .../EPUB/audio.foreign | Bin 0 -> 10363 bytes .../EPUB/audio.mpeg | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 21 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 18 + .../META-INF/container.xml | 6 + .../mimetype | 0 .../EPUB/audio.foreign | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 11 + .../EPUB/foreign.xyz | 0 .../EPUB/image.jpeg | Bin .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 18 + .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 11 + .../EPUB/foreign.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 17 + .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 0 .../EPUB/image-2x.xyz | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 14 + .../EPUB/foreign.xyz | 0 .../EPUB/nav.xhtml | 0 .../EPUB/package.opf | 17 + .../META-INF/container.xml | 0 .../mimetype | 0 .../EPUB/content_001.xhtml | 13 + .../EPUB/foreign.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 0 .../mimetype | 1 + .../EPUB/content_001.xhtml | 4 +- .../EPUB/foreign.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 1 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../EPUB/slideshow.xml | 0 .../EPUB/video.avi | 0 .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/image-2x.xyz | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/image.jpeg | Bin .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../META-INF/container.xml | 0 .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 19 + .../EPUB/poster.abc} | Bin .../EPUB/poster.png | Bin 0 -> 3654 bytes .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 12 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../EPUB/poster.png | Bin 0 -> 3654 bytes .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/audio.mpeg | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../type-mismatch-in-audio-warning/mimetype | 1 + .../EPUB/content_001.xhtml | 11 + .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../type-mismatch-in-embed-warning/mimetype | 1 + .../EPUB/content_001.xhtml | 2 +- .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 0 .../EPUB/slideshow.xml | 0 .../META-INF/container.xml | 6 + .../type-mismatch-in-object-warning/mimetype | 1 + .../EPUB/content_001.xhtml | 2 +- .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 18 + .../META-INF/container.xml | 6 + .../mimetype | 1 + .../epub3/03-resources/resources.feature | 255 ++++-- .../EPUB/content_001.xhtml | 15 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 1 - .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/content_001.xhtml | 10 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 16 + .../META-INF/container.xml | 6 + .../mimetype | 1 + .../EPUB/audio.mpeg | 0 .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../mimetype | 1 + src/test/resources/epub3/04-ocf/ocf.feature | 18 + .../EPUB/image.abc | 1 - .../EPUB/package.opf | 19 - .../package-document.feature | 17 +- .../content-document-xhtml.feature | 37 +- .../files/canvas-fallback-error.xhtml | 14 - .../EPUB/animation.mp4 | 0 .../EPUB/content_001.xhtml | 10 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../content-xhtml-img-video-valid/mimetype | 1 + .../EPUB/doc.pdf | Bin 8784 -> 0 bytes .../files/svg-aria-valid.xhtml | 2 +- .../EPUB/audio.mpeg | Bin 0 -> 10363 bytes .../EPUB/content_001.xhtml | 14 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../META-INF/container.xml | 6 + .../type-mismatch-in-audio-warning/mimetype | 1 + .../EPUB/content_001.xhtml | 13 + .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 17 + .../EPUB/slideshow.xml | 2 + .../META-INF/container.xml | 6 + .../type-mismatch-in-object-warning/mimetype | 1 + .../EPUB/content_001.xhtml | 13 + .../EPUB/image.jpeg | Bin 0 -> 1962 bytes .../EPUB/image.xyz | 0 .../EPUB/nav.xhtml | 14 + .../EPUB/package.opf | 18 + .../META-INF/container.xml | 6 + .../mimetype | 1 + 351 files changed, 3065 insertions(+), 2960 deletions(-) delete mode 100644 OCFCheckerCopy.java delete mode 100644 XRefChecker_copy.java create mode 100644 src/main/java/com/adobe/epubcheck/opf/FallbackChainResolver.java delete mode 100755 src/main/java/com/adobe/epubcheck/opf/XRefChecker.java create mode 100644 src/main/java/org/w3c/epubcheck/core/CheckAbortException.java create mode 100644 src/main/java/org/w3c/epubcheck/core/references/Reference.java create mode 100644 src/main/java/org/w3c/epubcheck/core/references/ReferenceRegistry.java create mode 100644 src/main/java/org/w3c/epubcheck/core/references/Resource.java create mode 100755 src/main/java/org/w3c/epubcheck/core/references/ResourceReferencesChecker.java create mode 100644 src/main/java/org/w3c/epubcheck/core/references/ResourceRegistry.java rename src/main/java/org/w3c/epubcheck/{url => core/references}/URLChecker.java (99%) delete mode 100644 src/main/java/org/w3c/epubcheck/url/Reference.java delete mode 100644 src/main/java/org/w3c/epubcheck/url/ReferencesChecker.java delete mode 100644 src/main/java/org/w3c/epubcheck/url/URLRegistry.java rename src/main/java/org/w3c/epubcheck/{ => util}/url/URLFragment.java (99%) rename src/main/java/org/w3c/epubcheck/{ => util}/url/URLUtils.java (99%) delete mode 100644 src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/content_001.xhtml delete mode 100644 src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/style.css delete mode 100644 src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/page-template.xpgt rename src/test/resources/epub3/03-resources/files/{fallback-cycle-error.opf => fallback-chain-circular-error.opf} (95%) rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 03-resources/files/fallback-chain-circular-error}/EPUB/content_001.xhtml (84%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => fallback-chain-circular-error}/EPUB/image.abc (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => fallback-chain-circular-error}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => fallback-chain-circular-error}/EPUB/image.xyz (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid => fallback-chain-circular-error}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => fallback-chain-circular-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => fallback-chain-circular-error}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid => fallback-chain-circular-error}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid/EPUB/image.jpeg => fallback-chain-n-to-1-valid/EPUB/image1.jpeg} (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid/EPUB/foreign.xyz => fallback-chain-n-to-1-valid/EPUB/image1a.vnd} (100%) rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid/EPUB/foreign.xyz => fallback-chain-n-to-1-valid/EPUB/image1b.vnd} (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => fallback-chain-n-to-1-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/package.opf rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => fallback-chain-n-to-1-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => fallback-chain-n-to-1-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => fallback-chain-waterfall-valid}/EPUB/content_001.xhtml (83%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error/EPUB/image.jpeg => fallback-chain-waterfall-valid/EPUB/image1.jpeg} (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/slideshow.xml => 03-resources/files/fallback-chain-waterfall-valid/EPUB/image1a.vnd} (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/video.avi => 03-resources/files/fallback-chain-waterfall-valid/EPUB/image1b.vnd} (100%) create mode 100644 src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1c.vnd rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => fallback-chain-waterfall-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/package.opf rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-no-fallback-valid => fallback-chain-waterfall-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => fallback-chain-waterfall-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-exempt-font-valid}/EPUB/OpenSans-Regular.ttf (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-exempt-font-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-exempt-font-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-exempt-font-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-exempt-font-valid}/EPUB/style.css (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-exempt-font-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-exempt-font-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid => foreign-exempt-unused-valid}/EPUB/content_001.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/foreign.xyz rename src/test/resources/epub3/03-resources/files/{content-xhtml-link-no-fallback-valid => foreign-exempt-unused-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid => foreign-exempt-unused-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-foreign-res-in-link-valid => foreign-exempt-unused-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-link-no-fallback-valid => foreign-exempt-unused-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-link-no-fallback-valid => foreign-exempt-xhtml-link-valid}/EPUB/content_001.xhtml (83%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-link-no-fallback-valid/EPUB/test.txt => foreign-exempt-xhtml-link-valid/EPUB/foreign.xyz} (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-exempt-xhtml-link-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid => foreign-exempt-xhtml-link-valid}/EPUB/package.opf (96%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => foreign-exempt-xhtml-link-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-exempt-xhtml-link-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/EPUB/page-template.xpgt (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/EPUB/style.css (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-no-fallback-valid => foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-no-fallback-valid => foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/EPUB/page-template.xpgt (100%) rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-exempt-xhtml-link-xpgt-no-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/audio.mpeg create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-exempt-xhtml-track-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/subtitles.vtt rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => foreign-exempt-xhtml-track-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-exempt-xhtml-track-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/animation.avi create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-exempt-xhtml-video-in-img-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/package.opf rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-exempt-xhtml-video-in-img-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-exempt-xhtml-video-in-img-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-valid/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => foreign-exempt-xhtml-video-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-link-fragment-after-object-valid => 03-resources/files/foreign-exempt-xhtml-video-valid}/EPUB/package.opf (87%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-valid/EPUB/video.avi rename src/test/resources/epub3/03-resources/files/{content-xhtml-link-no-fallback-valid => foreign-exempt-xhtml-video-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => foreign-exempt-xhtml-video-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-in-video-no-fallback-error}/EPUB/audio.foreign (100%) rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid => foreign-xhtml-audio-in-video-no-fallback-error}/EPUB/content_001.xhtml (75%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => foreign-xhtml-audio-in-video-no-fallback-error}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-in-video-no-fallback-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-in-video-no-fallback-error}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-manifest-fallback-valid => foreign-xhtml-audio-in-video-no-fallback-error}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-manifest-fallback-valid/EPUB/audio.foreign create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-manifest-fallback-valid/EPUB/audio.mpeg rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-manifest-fallback-valid}/EPUB/content_001.xhtml (75%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-no-fallback-valid => foreign-xhtml-audio-manifest-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-implicit-fallback-valid => foreign-xhtml-audio-manifest-fallback-valid}/EPUB/package.opf (78%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-manifest-fallback-valid/META-INF/container.xml rename src/test/resources/epub3/03-resources/files/{content-xhtml-xpgt-no-fallback-valid => foreign-xhtml-audio-manifest-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-error/EPUB/audio.foreign create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-error/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-no-fallback-error}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-xhtml-audio-no-fallback-error}/EPUB/package.opf (80%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-error/META-INF/container.xml rename src/test/resources/epub3/03-resources/files/{resources-cmt-audio-foreign-error => foreign-xhtml-audio-no-fallback-error}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-with-flow-content-error/EPUB/audio.foreign create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-with-flow-content-error/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-xhtml-audio-no-fallback-with-flow-content-error}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-with-flow-content-error/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-no-fallback-with-flow-content-error/META-INF/container.xml rename src/test/resources/epub3/03-resources/files/{resources-cmt-font-other-mediatype-valid => foreign-xhtml-audio-no-fallback-with-flow-content-error}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-fallback-valid/EPUB/audio.foreign create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-fallback-valid/EPUB/audio.mpeg create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-fallback-valid/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid => foreign-xhtml-audio-source-fallback-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-fallback-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-fallback-valid/META-INF/container.xml rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid => foreign-xhtml-audio-source-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-no-fallback-error/EPUB/audio.foreign create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-no-fallback-error/EPUB/content_001.xhtml rename src/test/resources/epub3/03-resources/files/{resources-foreign-script-datablock-valid => foreign-xhtml-audio-source-no-fallback-error}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-no-fallback-error/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-audio-source-no-fallback-error/META-INF/container.xml rename src/test/resources/epub3/03-resources/files/{resources-foreign-script-datablock-valid => foreign-xhtml-audio-source-no-fallback-error}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{resources-remote-audio-sources-foreign-valid => foreign-xhtml-audio-source-remote-fallback-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => foreign-xhtml-audio-source-remote-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{resources-remote-audio-sources-foreign-valid => foreign-xhtml-audio-source-remote-fallback-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-xhtml-audio-source-remote-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => foreign-xhtml-audio-source-remote-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-fallback-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-fallback-valid/EPUB/foreign.xyz rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-embed-fallback-valid}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{resources-remote-audio-sources-foreign-valid => foreign-xhtml-embed-fallback-valid}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-fallback-valid/EPUB/package.opf rename src/test/resources/epub3/{06-content-document/files/content-xhtml-link-fragment-after-object-valid => 03-resources/files/foreign-xhtml-embed-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/03-resources/files/{resources-remote-audio-sources-foreign-valid => foreign-xhtml-embed-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-no-fallback-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-no-fallback-error/EPUB/foreign.xyz rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 03-resources/files/foreign-xhtml-embed-no-fallback-error}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-embed-no-fallback-error/EPUB/package.opf rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error => 03-resources/files/foreign-xhtml-embed-no-fallback-error}/META-INF/container.xml (100%) rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 03-resources/files/foreign-xhtml-embed-no-fallback-error}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => foreign-xhtml-img-manifest-fallback-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-xhtml-img-manifest-fallback-valid}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => foreign-xhtml-img-manifest-fallback-valid}/EPUB/image.xyz (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-link-fragment-after-object-valid => 03-resources/files/foreign-xhtml-img-manifest-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-manifest-fallback-valid => foreign-xhtml-img-manifest-fallback-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-img-manifest-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-link-fragment-after-object-valid => 03-resources/files/foreign-xhtml-img-manifest-fallback-valid}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => foreign-xhtml-img-src-no-manifest-fallback-error}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-xhtml-img-src-no-manifest-fallback-error}/EPUB/image.xyz (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/foreign-xhtml-img-src-no-manifest-fallback-error}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-src-no-manifest-fallback-error => foreign-xhtml-img-src-no-manifest-fallback-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-xhtml-img-src-no-manifest-fallback-error}/META-INF/container.xml (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/foreign-xhtml-img-src-no-manifest-fallback-error}/mimetype (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/image-2x.xyz (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/image.xyz (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error => 03-resources/files/foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/nav.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-img-srcset-manifest-fallback-valid => foreign-xhtml-img-srcset-manifest-fallback-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-xhtml-img-srcset-manifest-fallback-valid}/META-INF/container.xml (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error => 03-resources/files/foreign-xhtml-img-srcset-manifest-fallback-valid}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-input-image-no-fallback-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-input-image-no-fallback-error/EPUB/foreign.xyz rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error => 03-resources/files/foreign-xhtml-input-image-no-fallback-error}/EPUB/nav.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-input-image-no-fallback-error/EPUB/package.opf rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-xhtml-input-image-no-fallback-error}/META-INF/container.xml (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error => 03-resources/files/foreign-xhtml-input-image-no-fallback-error}/mimetype (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-math-altimg-no-fallback-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-math-altimg-no-fallback-error/EPUB/foreign.xyz create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-math-altimg-no-fallback-error/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-math-altimg-no-fallback-error/EPUB/package.opf rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error => 03-resources/files/foreign-xhtml-math-altimg-no-fallback-error}/META-INF/container.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-math-altimg-no-fallback-error/mimetype rename src/test/resources/epub3/{06-content-document/files/content-xhtml-link-fragment-after-object-valid => 03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid}/EPUB/content_001.xhtml (75%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid/EPUB/foreign.xyz create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-intrinsic-fallback-valid/mimetype rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error => 03-resources/files/foreign-xhtml-object-no-fallback-error}/EPUB/content_001.xhtml (91%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-no-fallback-error/EPUB/nav.xhtml rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-no-fallback-error => 03-resources/files/foreign-xhtml-object-no-fallback-error}/EPUB/package.opf (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-no-fallback-error/EPUB/slideshow.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-no-fallback-error/EPUB/video.avi create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-no-fallback-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-object-no-fallback-error/mimetype rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-xhtml-picture-img-src-error}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-xhtml-picture-img-src-error}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-picture-img-src-error}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-img-src-error/EPUB/nav.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-src-error => foreign-xhtml-picture-img-src-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{resources-foreign-res-unused-valid => foreign-xhtml-picture-img-src-error}/META-INF/container.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-img-src-error/mimetype rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-picture-img-srcset-error}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-picture-img-srcset-error}/EPUB/image-2x.xyz (100%) rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => foreign-xhtml-picture-img-srcset-error}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-xhtml-picture-img-srcset-error}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-img-srcset-error/EPUB/nav.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-fallback-img-foreign-srcset-error => foreign-xhtml-picture-img-srcset-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{resources-foreign-script-datablock-valid => foreign-xhtml-picture-img-srcset-error}/META-INF/container.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-img-srcset-error/mimetype rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-xhtml-picture-source-no-type-error}/EPUB/content_001.xhtml (100%) rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 03-resources/files/foreign-xhtml-picture-source-no-type-error}/EPUB/image.jpeg (100%) rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => foreign-xhtml-picture-source-no-type-error}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-source-no-type-error/EPUB/nav.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-no-type-error => foreign-xhtml-picture-source-no-type-error}/EPUB/package.opf (100%) rename src/test/resources/epub3/03-resources/files/{resources-remote-audio-sources-foreign-valid => foreign-xhtml-picture-source-no-type-error}/META-INF/container.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-source-no-type-error/mimetype rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-xhtml-picture-source-with-type-valid}/EPUB/content_001.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-source-with-type-valid/EPUB/image.jpeg rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-xhtml-picture-source-with-type-valid}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-source-with-type-valid/EPUB/nav.xhtml rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-type-valid => foreign-xhtml-picture-source-with-type-valid}/EPUB/package.opf (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/foreign-xhtml-picture-source-with-type-valid}/META-INF/container.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-picture-source-with-type-valid/mimetype rename src/test/resources/epub3/03-resources/files/{resources-foreign-script-datablock-valid => foreign-xhtml-script-datablock-valid}/EPUB/content_001.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-script-datablock-valid/EPUB/nav.xhtml rename src/test/resources/epub3/03-resources/files/{resources-foreign-script-datablock-valid => foreign-xhtml-script-datablock-valid}/EPUB/package.opf (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-script-datablock-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-script-datablock-valid/mimetype rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error => 03-resources/files/foreign-xhtml-video-poster-fallback-valid}/EPUB/content_001.xhtml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-fallback-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-fallback-valid/EPUB/package.opf rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error/EPUB/poster.png => 03-resources/files/foreign-xhtml-video-poster-fallback-valid/EPUB/poster.abc} (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-fallback-valid/EPUB/poster.png create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-fallback-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-fallback-valid/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-no-fallback-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-no-fallback-error/EPUB/nav.xhtml rename src/test/resources/epub3/{06-content-document/files/content-xhtml-video-poster-media-type-error => 03-resources/files/foreign-xhtml-video-poster-no-fallback-error}/EPUB/package.opf (100%) create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-no-fallback-error/EPUB/poster.png create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-no-fallback-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/foreign-xhtml-video-poster-no-fallback-error/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/EPUB/audio.mpeg create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-audio-warning/mimetype create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/EPUB/image.jpeg create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-embed-warning/mimetype rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/type-mismatch-in-object-warning}/EPUB/content_001.xhtml (79%) create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-object-warning/EPUB/nav.xhtml rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/type-mismatch-in-object-warning}/EPUB/package.opf (100%) rename src/test/resources/epub3/{06-content-document/files/content-xhtml-object-mediatype-mismatch-error => 03-resources/files/type-mismatch-in-object-warning}/EPUB/slideshow.xml (100%) create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-object-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-object-warning/mimetype rename src/test/resources/epub3/03-resources/files/{content-xhtml-picture-source-foreign-with-cmt-type-error => type-mismatch-in-picture-source-warning}/EPUB/content_001.xhtml (80%) create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-picture-source-warning/EPUB/image.jpeg rename src/test/resources/epub3/03-resources/files/{resources-manifest-fallback-circular-error => type-mismatch-in-picture-source-warning}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-picture-source-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-picture-source-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-picture-source-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/03-resources/files/type-mismatch-in-picture-source-warning/mimetype create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-cite-missing-resource-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-cite-missing-resource-error/EPUB/nav.xhtml rename src/test/resources/epub3/{03-resources/files/content-xhtml-link-no-fallback-valid => 04-ocf/files/url-xhtml-cite-missing-resource-error}/EPUB/package.opf (90%) create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-cite-missing-resource-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-cite-missing-resource-error/mimetype create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-iframe-missing-resource-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-iframe-missing-resource-error/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-iframe-missing-resource-error/EPUB/package.opf create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-iframe-missing-resource-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-iframe-missing-resource-error/mimetype create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/EPUB/audio.mpeg create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/EPUB/package.opf create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/META-INF/container.xml create mode 100644 src/test/resources/epub3/04-ocf/files/url-xhtml-track-missing-resource-error/mimetype delete mode 100644 src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/EPUB/image.abc delete mode 100644 src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/EPUB/package.opf delete mode 100644 src/test/resources/epub3/06-content-document/files/canvas-fallback-error.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/EPUB/animation.mp4 create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/EPUB/package.opf create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/META-INF/container.xml create mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-img-video-valid/mimetype delete mode 100644 src/test/resources/epub3/06-content-document/files/content-xhtml-link-fragment-after-object-valid/EPUB/doc.pdf create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/EPUB/audio.mpeg create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-audio-warning/mimetype create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/EPUB/slideshow.xml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-object-warning/mimetype create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/EPUB/content_001.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/EPUB/image.jpeg rename src/test/resources/epub3/{05-package-document/files/package-manifest-fallback-non-resolving-error => 06-content-document/files/type-mismatch-in-picture-source-warning}/EPUB/image.xyz (100%) create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/EPUB/nav.xhtml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/EPUB/package.opf create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/META-INF/container.xml create mode 100644 src/test/resources/epub3/06-content-document/files/type-mismatch-in-picture-source-warning/mimetype diff --git a/OCFCheckerCopy.java b/OCFCheckerCopy.java deleted file mode 100644 index e750a1bbc..000000000 --- a/OCFCheckerCopy.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright (c) 2007 Adobe Systems Incorporated - * - * 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. - * - */ - -package com.adobe.epubcheck.ocf; - -import static com.adobe.epubcheck.opf.ValidationContext.ValidationContextPredicates.hasProp; -import static com.adobe.epubcheck.opf.ValidationContext.ValidationContextPredicates.path; -import static com.adobe.epubcheck.opf.ValidationContext.ValidationContextPredicates.profile; -import static com.adobe.epubcheck.opf.ValidationContext.ValidationContextPredicates.version; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.text.Normalizer; -import java.text.Normalizer.Form; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import org.w3c.epubcheck.constants.MIMEType; -import org.w3c.epubcheck.core.Checker; -import org.w3c.epubcheck.core.CheckerFactory; -import org.w3c.epubcheck.ocf.OCFContainer; - -import com.adobe.epubcheck.api.EPUBLocation; -import com.adobe.epubcheck.api.EPUBProfile; -import com.adobe.epubcheck.api.FeatureReport; -import com.adobe.epubcheck.api.Report; -import com.adobe.epubcheck.messages.MessageId; -import com.adobe.epubcheck.opf.OPFChecker; -import com.adobe.epubcheck.opf.OPFChecker30; -import com.adobe.epubcheck.opf.OPFData; -import com.adobe.epubcheck.opf.OPFHandler; -import com.adobe.epubcheck.opf.OPFHandler30; -import com.adobe.epubcheck.opf.OPFItem; -import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.ValidationContext.ValidationContextBuilder; -import com.adobe.epubcheck.util.CheckUtil; -import com.adobe.epubcheck.util.EPUBVersion; -import com.adobe.epubcheck.util.FeatureEnum; -import com.adobe.epubcheck.util.ValidatorMap; -import com.adobe.epubcheck.vocab.EpubCheckVocab; -import com.adobe.epubcheck.xml.XMLParser; -import com.adobe.epubcheck.xml.XMLValidator; -import com.adobe.epubcheck.xml.XMLValidators; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Iterables; - -public final class OCFCheckerCopy implements Checker -{ - - private static final ValidatorMap validatorMap = ValidatorMap.builder() - .put(Predicates.and(path(OCFData.containerEntry), version(EPUBVersion.VERSION_2)), - XMLValidators.CONTAINER_20_RNG) - .putAll(Predicates.and(path(OCFData.containerEntry), version(EPUBVersion.VERSION_3)), - XMLValidators.CONTAINER_30_RNC, XMLValidators.CONTAINER_30_RENDITIONS_SCH) - .putAll(Predicates.and(path(OCFData.encryptionEntry), version(EPUBVersion.VERSION_3)), - XMLValidators.ENC_30_RNC, XMLValidators.ENC_30_SCH) - .put(Predicates.and(path(OCFData.encryptionEntry), version(EPUBVersion.VERSION_2)), - XMLValidators.ENC_20_RNG) - .put(Predicates.and(path(OCFData.signatureEntry), version(EPUBVersion.VERSION_2)), - XMLValidators.SIG_20_RNG) - .put(Predicates.and(path(OCFData.signatureEntry), version(EPUBVersion.VERSION_3)), - XMLValidators.SIG_30_RNC) - .put( - Predicates.and(path(OCFData.metadataEntry), - hasProp(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.MULTIPLE_RENDITION))), - XMLValidators.META_30_RNC) - .put( - Predicates.and(path(OCFData.metadataEntry), - hasProp(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.MULTIPLE_RENDITION))), - XMLValidators.META_30_SCH) - .put(Predicates.and(path(OCFData.metadataEntry), - hasProp(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.MULTIPLE_RENDITION)), - profile(EPUBProfile.EDUPUB)), XMLValidators.META_EDUPUB_SCH) - .putAll(hasProp(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.RENDITION_MAPPING)), - XMLValidators.RENDITION_MAPPING_RNC, XMLValidators.RENDITION_MAPPING_SCH) - .build(); - - private final ValidationContext context; - private final OCFPackage ocf; - private final Report report; - - public OCFCheckerCopy(ValidationContext context) - { - // FIXME 2022 romain: checkArgument that context has container media type - Preconditions.checkState(context.ocf.isPresent()); - this.context = context; - this.ocf = context.ocf.get(); - this.report = context.report; - } - - public void check() - { - // Create a new validation context builder from the parent context - // It will be augmented with detected validation version, profile, etc. - ValidationContextBuilder newContextBuilder = new ValidationContextBuilder(context); - - ocf.setReport(report); - if (!ocf.hasEntry(OCFData.containerEntry)) - { - checkZipHeader(); // run ZIP header checks in any case before returning - // do not report the missing container entry if a fatal error was already reported - if (report.getFatalErrorCount() == 0) { - report.message(MessageId.RSC_002, EPUBLocation.create(ocf.getName())); - } - return; - } - long l = ocf.getTimeEntry(OCFData.containerEntry); - if (l > 0) - { - Date d = new Date(l); - String formattedDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(d); - report.info(OCFData.containerEntry, FeatureEnum.CREATION_DATE, formattedDate); - } - OCFData containerData = ocf.getOcfData(); - - // retrieve the paths of root files - List opfPaths = containerData.getEntries(MIMEType.PACKAGE_DOC.toString()); - if (opfPaths == null || opfPaths.isEmpty()) - { - checkZipHeader(); // run ZIP header checks in any case before returning - report.message(MessageId.RSC_003, EPUBLocation.create(OCFData.containerEntry)); - return; - } - else if (opfPaths.size() > 0) - { - if (opfPaths.size() > 1) - { - report.info(null, FeatureEnum.EPUB_RENDITIONS_COUNT, Integer.toString(opfPaths.size())); - } - - // test every element for empty or missing @full-path attribute - // bugfix for issue 236 / issue 95 - int rootfileErrorCounter = 0; - for (String opfPath : opfPaths) - { - if (opfPath == null) - { - ++rootfileErrorCounter; - report.message(MessageId.OPF_016, EPUBLocation.create(OCFData.containerEntry)); - } - else if (opfPath.isEmpty()) - { - ++rootfileErrorCounter; - report.message(MessageId.OPF_017, EPUBLocation.create(OCFData.containerEntry)); - } - else if (!ocf.hasEntry(opfPath)) - { - checkZipHeader(); // run ZIP header checks in any case before returning - report.message(MessageId.OPF_002, EPUBLocation.create(OCFData.containerEntry), opfPath); - return; - } - } - if (rootfileErrorCounter == opfPaths.size()) - { - checkZipHeader(); // run ZIP header checks in any case before returning - // end validation at this point when @full-path attribute is missing in - // container.xml - // otherwise, tons of errors would be thrown - // ("XYZ exists in the zip file, but is not declared in the OPF file") - return; - } - } - - // - // Compute the validation version - // ------------------------------ - // Detect the version of the first root file - // and compare with the asked version (if set) - EPUBVersion detectedVersion = null; - final EPUBVersion validationVersion; - OPFData opfData = ocf.getOpfData().get(opfPaths.get(0)); - if (opfData == null) { - checkZipHeader(); // run ZIP header checks in any case before returning - return;// The error must have been reported during - } - // parsing - detectedVersion = opfData.getVersion(); - report.info(null, FeatureEnum.FORMAT_VERSION, detectedVersion.toString()); - assert(detectedVersion != null); - - if (context.version != EPUBVersion.Unknown && context.version != detectedVersion) - { - report.message(MessageId.PKG_001, EPUBLocation.create(opfPaths.get(0)), context.version, - detectedVersion); - - validationVersion = context.version; - } - else - { - validationVersion = detectedVersion; - } - newContextBuilder.version(validationVersion); - - // - // Check the EPUB file header - // ------------------------------ - checkZipHeader(); - - // - // Compute the validation profile - // ------------------------------ - EPUBProfile validationProfile = context.profile; - // FIXME get profile from metadata.xml if available - if (validationVersion == EPUBVersion.VERSION_2 && validationProfile != EPUBProfile.DEFAULT) - { - // Validation profile is unsupported for EPUB 2.0 - report.message(MessageId.PKG_023, EPUBLocation.create(opfPaths.get(0))); - validationProfile = EPUBProfile.DEFAULT; - } - else if (validationVersion == EPUBVersion.VERSION_3) - { - // Override the given validation profile depending on the primary OPF - // dc:type - validationProfile = EPUBProfile.makeOPFCompatible(validationProfile, opfData, opfPaths.get(0), - report); - } - newContextBuilder.profile(validationProfile); - - // - // Check multiple renditions - // ------------------------------ - // EPUB 2.0 says there SHOULD be only one OPS rendition - if (validationVersion == EPUBVersion.VERSION_2 && opfPaths.size() > 1) - { - report.message(MessageId.PKG_013, EPUBLocation.create(OCFData.containerEntry)); - } - // EPUB 3.0 Multiple Renditions recommends the presence of a metadata file - if (validationVersion == EPUBVersion.VERSION_3 && opfPaths.size() > 1) - { - newContextBuilder - .addProperty(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.MULTIPLE_RENDITION)); - if (!ocf.hasEntry(OCFData.metadataEntry)) - { - report.message(MessageId.RSC_019, EPUBLocation.create(ocf.getName())); - } - if (containerData.getMapping().isPresent()) - { - validateRenditionMapping(new ValidationContextBuilder(newContextBuilder.build()) - .mimetype("application/xhtml+xml").path(containerData.getMapping().get()) - .addProperty(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.RENDITION_MAPPING)) - .build()); - } - } - - // - // Check the mimetype file - // ------------------------------ - // - InputStream mimetype = null; - try - { - mimetype = ocf.getInputStream("mimetype"); - StringBuilder sb = new StringBuilder(2048); - if (ocf.hasEntry("mimetype") - && !CheckUtil.checkTrailingSpaces(mimetype, validationVersion, sb)) - { - report.message(MessageId.PKG_007, EPUBLocation.create("mimetype")); - } - if (sb.length() != 0) - { - report.info(null, FeatureEnum.FORMAT_NAME, sb.toString().trim()); - } - } catch (IOException ignored) - { - // missing file will be reported later - } finally - { - try - { - if (mimetype != null) - { - mimetype.close(); - } - } catch (IOException ignored) - { - // eat it - } - } - - // - // Check the META-INF files - // ------------------------------ - // - validateMetaFiles(newContextBuilder.mimetype("xml").build()); - - // - // Check each OPF (i.e. Rendition) - // ------------------------------- - // - // Validate each OPF and keep a reference of the OPFHandler - List opfHandlers = new LinkedList(); - for (String opfPath : opfPaths) - { - ValidationContext opfContext = newContextBuilder.path(opfPath).mimetype(MIMEType.PACKAGE_DOC.toString()) - .featureReport(new FeatureReport()).build(); - Checker opfChecker = CheckerFactory.newChecker(opfContext); - assert opfChecker instanceof OPFChecker; - opfChecker.check(); - opfHandlers.add(((OPFChecker)opfChecker).getOPFHandler()); - } - - // - // Check container integrity - // ------------------------------- - // - try - { - // report duplicate entries - Set entriesSet = new HashSet(); - Set normalizedEntriesSet = new HashSet(); - // run duplicate check from the LinkedList which may contain duplicates - for (final String entry : ocf.getEntries()) - { - if (!entriesSet.add(entry.toLowerCase(Locale.ENGLISH))) - { - report.message(MessageId.OPF_060, EPUBLocation.create(ocf.getPackagePath()), entry); - } - else if (!normalizedEntriesSet.add(Normalizer.normalize(entry, Form.NFC))) - { - report.message(MessageId.OPF_061, EPUBLocation.create(ocf.getPackagePath()), entry); - } - } - - // check all file entries without duplicates - for (final String entry : ocf.getFileEntries()) - { - ocf.reportMetadata(entry, report); - - // if the entry is not in the whitelist (META-INF/* + mimetype) - // and not declared in (one of) the OPF document(s) - if (!entry.startsWith("META-INF/") && !entry.startsWith("META-INF\\") - && !entry.equals("mimetype") && !containerData.getEntries().contains(entry) - && !entry.equals(containerData.getMapping().orNull()) - && !Iterables.tryFind(opfHandlers, new Predicate() - { - @Override - public boolean apply(OPFHandler opfHandler) - { - // found if declared as an OPF item - // or in an EPUB 3 link element - return opfHandler.getItemByPath(entry).isPresent() - || (validationVersion == EPUBVersion.VERSION_3 - && ((OPFHandler30) opfHandler).getLinkedResources().hasPath(entry)); - } - }).isPresent()) - { - report.message(MessageId.OPF_003, EPUBLocation.create(ocf.getName()), entry); - } - OCFFilenameChecker.checkCompatiblyEscaped(entry, report, validationVersion); - - // check obfuscated resource are Font Core Media Types - if (ocf.isObfuscatedFont(entry)) - { - for (OPFHandler opf : opfHandlers) - { - // try to find the first Package Document where the entry is - // declared - Optional item = opf.getItemByPath(entry); - if (item.isPresent()) - { - // report if it is not a font core media type - if (!OPFChecker30.isBlessedFontType(item.get().getMimeType())) - { - report.message(MessageId.PKG_026, ocf.getObfuscationDeclarationLocation(entry), - item.get().getMimeType(), opf.getPath()); - } - break; - } - } - } - } - - // check all directory entries without duplicates - for (String directory : ocf.getDirectoryEntries()) - { - boolean hasContents = false; - for (String file : ocf.getFileEntries()) - { - if (file.startsWith(directory)) - { - hasContents = true; - break; - } - } - if (!hasContents) - { - report.message(MessageId.PKG_014, EPUBLocation.create(ocf.getName()), directory); - } - } - } catch (IOException e) - { - report.message(MessageId.PKG_015, EPUBLocation.create(ocf.getName()), e.getMessage()); - } - } - - private boolean validateMetaFiles(ValidationContext context) - { - // validate container - validateMetaFile(new ValidationContextBuilder(context).path(OCFData.containerEntry).build()); - - // Validate encryption.xml - if (ocf.hasEntry(OCFData.encryptionEntry)) - { - validateMetaFile(new ValidationContextBuilder(context).path(OCFData.encryptionEntry).build()); - report.info(null, FeatureEnum.HAS_ENCRYPTION, OCFData.encryptionEntry); - } - - // validate signatures.xml - if (ocf.hasEntry(OCFData.signatureEntry)) - { - validateMetaFile(new ValidationContextBuilder(context).path(OCFData.signatureEntry).build()); - report.info(null, FeatureEnum.HAS_SIGNATURES, OCFData.signatureEntry); - } - - // validate signatures.xml - if (ocf.hasEntry(OCFData.metadataEntry)) - { - validateMetaFile(new ValidationContextBuilder(context).path(OCFData.metadataEntry).build()); - } - - return false; - } - - private void validateMetaFile(ValidationContext context) - { - XMLParser parser = new XMLParser(context); - if (context.path.equals(OCFData.encryptionEntry)) - { - parser.addXMLHandler(new EncryptionHandler(ocf, parser)); - } - else - { - parser.addXMLHandler(new OCFHandler(parser)); - } - for (XMLValidator validator : validatorMap.getValidators(context)) - { - parser.addValidator(validator); - } - parser.process(); - } - - private void validateRenditionMapping(ValidationContext context) - { - XMLParser parser = new XMLParser(context); - for (XMLValidator validator : validatorMap.getValidators(context)) - { - parser.addValidator(validator); - } - parser.process(); - } - - private void checkZipHeader() - { - checkZipHeader(new File(context.path), report); - } - - public static void checkZipHeader(File epubFile, Report report) { - - FileInputStream epubIn = null; - try - { - - epubIn = new FileInputStream(epubFile); - - byte[] header = new byte[58]; - - int readCount = epubIn.read(header); - if (readCount != -1) - { - while (readCount < header.length) - { - int read = epubIn.read(header, readCount, header.length - readCount); - // break on eof - if (read == -1) - { - break; - } - readCount += read; - } - } - - if (readCount != header.length) - { - report.message(MessageId.PKG_003, EPUBLocation.create(epubFile.getName(), "")); - } - else - { - int fnsize = getIntFromBytes(header, 26); - int extsize = getIntFromBytes(header, 28); - - if (header[0] != 'P' && header[1] != 'K') - { - report.message(MessageId.PKG_004, EPUBLocation.create(epubFile.getName())); - } - else if (fnsize != 8) - { - report.message(MessageId.PKG_006, EPUBLocation.create(epubFile.getName())); - } - else if (extsize != 0) - { - report.message(MessageId.PKG_005, EPUBLocation.create(epubFile.getName()), extsize); - } - else if (!CheckUtil.checkString(header, 30, "mimetype")) - { - report.message(MessageId.PKG_006, EPUBLocation.create(epubFile.getName())); - } - else if (!CheckUtil.checkString(header, 38, "application/epub+zip")) - { - report.message(MessageId.PKG_007, EPUBLocation.create("mimetype")); - } - } - } catch (IOException e) - { - report.message(MessageId.PKG_008, EPUBLocation.create(epubFile.getName(), ""), - e.getMessage()); - } finally - { - try - { - if (epubIn != null) - { - epubIn.close(); - } - } catch (IOException ignored) - { - } - } - } - - private static int getIntFromBytes(byte[] bytes, int offset) - { - int hi = 0xFF & bytes[offset + 1]; - int lo = 0xFF & bytes[offset]; - return hi << 8 | lo; - } - -} diff --git a/XRefChecker_copy.java b/XRefChecker_copy.java deleted file mode 100644 index c8646ba97..000000000 --- a/XRefChecker_copy.java +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Copyright (c) 2007 Adobe Systems Incorporated - * - * 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. - * - */ - -package com.adobe.epubcheck.opf; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.regex.Pattern; - -import com.adobe.epubcheck.api.EPUBLocation; -import com.adobe.epubcheck.api.LocalizableReport; -import com.adobe.epubcheck.api.Report; -import com.adobe.epubcheck.messages.LocalizedMessages; -import com.adobe.epubcheck.messages.MessageId; -import com.adobe.epubcheck.ocf.OCFContainer; -import com.adobe.epubcheck.util.EPUBVersion; -import com.adobe.epubcheck.util.FeatureEnum; -import com.adobe.epubcheck.util.PathUtil; -import com.adobe.epubcheck.vocab.PackageVocabs; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; - -public class XRefChecker_copy -{ - - public static enum Type - { - GENERIC, - FONT, - HYPERLINK, - LINK, - IMAGE, - OBJECT, - STYLESHEET, - AUDIO, - VIDEO, - SVG_PAINT, - SVG_CLIP_PATH, - SVG_SYMBOL, - REGION_BASED_NAV, - SEARCH_KEY, - NAV_TOC_LINK, - NAV_PAGELIST_LINK, - OVERLAY_TEXT_LINK, - PICTURE_SOURCE, - PICTURE_SOURCE_FOREIGN; - } - - private static class Reference - { - public final String source; - public final int lineNumber; - public final int columnNumber; - public final String value; - public final String refResource; - public final String fragment; - public final Type type; - - public Reference(String srcResource, int srcLineNumber, int srcColumnNumber, String value, - String refResource, String fragment, Type type) - { - this.source = srcResource; - this.lineNumber = srcLineNumber; - this.columnNumber = srcColumnNumber; - this.value = value; - this.refResource = refResource; - this.fragment = fragment; - this.type = type; - } - - } - - private static class Anchor - { - - @SuppressWarnings("unused") - public final String id; - public final Type type; - public final int position; - - public Anchor(String id, int position, Type type) - { - this.id = id; - this.position = position; - this.type = type; - } - - } - - private static class Resource - { - - public final OPFItem item; - public final Hashtable anchors; - public final boolean hasValidItemFallback; - public final boolean hasValidImageFallback; - - Resource(OPFItem item, boolean hasValidItemFallback, boolean hasValidImageFallback) - { - this.item = item; - this.hasValidItemFallback = hasValidItemFallback; - this.hasValidImageFallback = hasValidImageFallback; - this.anchors = new Hashtable(); - } - - /** - * Returns the position of the given ID in the document represented by this - * resource. - * - * @return {@code -1} if the ID wasn't found in the document, or {@code 0} if - * the given ID is {@code null} or an empty string, or the 1-based - * position of the ID otherwise. - */ - public int getAnchorPosition(String id) - { - if (id == null || id.trim().isEmpty()) return 0; - Anchor anchor = anchors.get(id); - return (anchor != null) ? anchor.position : -1; - } - } - - private static final Pattern REGEX_SVG_VIEW = Pattern.compile("svgView\\(.*\\)"); - - private final Map resources = new HashMap(); - - private final HashSet undeclared = new HashSet(); - - private final List references = new LinkedList(); - - private final Map bindings = new HashMap(); - - private final Report report; - - private final OCFContainer container; - - private final EPUBVersion version; - - private final Locale locale; - - public XRefChecker_copy(ValidationContext context) - { - Preconditions.checkArgument(context.container.isPresent()); - this.container = context.container.get(); - this.report = context.report; - this.version = context.version; - this.locale = (report instanceof LocalizableReport) ? ((LocalizableReport) report).getLocale() - : Locale.ENGLISH; - } - - public String getMimeType(String path) - { - return resources.get(path) != null ? resources.get(path).item.getMimeType() : null; - } - - /** - * Returns an {@link Optional} containing a boolean indicating whether the - * resource at the given path has a valid item fallback, or - * {@link Optional#absent()} if no resource has been registered for the given - * path. - */ - public Optional hasValidFallback(String path) - { - return resources.get(path) != null ? Optional.of(resources.get(path).hasValidItemFallback) - : Optional. absent(); - } - - /** - * Returns an {@link Optional} containing the Package Document item for the - * given Publication Resource path, or {@link Optional#absent()} if no resource - * has been registered for the given path. - */ - public Optional getResource(String path) - { - return (path == null || !resources.containsKey(path)) ? Optional. absent() - : Optional.of(resources.get(path).item); - } - - /** - * Returns set (possibly multiple) types of refereences to the given resource - * - * @param path - * the path to a publication resource - * @return an immutable {@link EnumSet} containing the types of references to - * {@code path}. - */ - public Set getTypes(String path) - { - LinkedList types = new LinkedList<>(); - for (Reference reference : references) - { - if (Preconditions.checkNotNull(path).equals(reference.refResource)) - { - types.add(reference.type); - } - } - return Sets.immutableEnumSet(types); - } - - public Set getBindingsMimeTypes() - { - return bindings.keySet(); - } - - public String getBindingHandlerId(String mimeType) - { - return bindings.get(mimeType); - } - - public void registerBinding(String mimeType, String handlerId) - { - bindings.put(mimeType, handlerId); - } - - // FIXME 2022 simplify signature: fallback info can be moved to OPFItem - public void registerResource(OPFItem item, boolean hasValidItemFallback, - boolean hasValidImageFallback) - { - // Note: Duplicate manifest items are already checked in OPFChecker. - if (!resources.containsKey(item.getPath())) - { - resources.put(item.getPath(), - new Resource(item, hasValidItemFallback, hasValidImageFallback)); - } - } - - public void registerAnchor(String path, int lineNumber, int columnNumber, String id, Type type) - { - Resource res = Preconditions.checkNotNull(resources.get(path)); - // Note: duplicate IDs are checked in schematron - if (!res.anchors.contains(id)) - { - res.anchors.put(id, new Anchor(id, res.anchors.size() + 1, type)); - } - } - - public void registerReference(String srcResource, int srcLineNumber, int srcColumnNumber, - String ref, Type type) - { - if (ref.startsWith("data:")) - { - return; - } - // see http://code.google.com/p/epubcheck/issues/detail?id=190 - // see http://code.google.com/p/epubcheck/issues/detail?id=261 - int query = ref.indexOf('?'); - if (query >= 0 && !PathUtil.isRemote(ref)) - { - ref = ref.substring(0, query).trim(); - } - - String refResource = PathUtil.removeFragment(ref); - String refFragment = PathUtil.getFragment(ref); - report.info(srcResource, FeatureEnum.RESOURCE, refResource); - references.add(new Reference(srcResource, srcLineNumber, srcColumnNumber, ref, refResource, - refFragment, type)); - - } - - public void checkReferences() - { - // if (checkReference(reference)) checkReferenceSubtypes(reference); - Queue tocLinks = new LinkedList<>(); - Queue pageListLinks = new LinkedList<>(); - Queue overlayLinks = new LinkedList<>(); - for (Reference reference : references) - { - switch (reference.type) - { - case REGION_BASED_NAV: - checkRegionBasedNav(reference); - break; - case NAV_TOC_LINK: - tocLinks.add(reference); - break; - case NAV_PAGELIST_LINK: - pageListLinks.add(reference); - break; - case OVERLAY_TEXT_LINK: - overlayLinks.add(reference); - break; - default: - checkReference(reference); - break; - } - } - checkReadingOrder(tocLinks, -1, -1); - checkReadingOrder(overlayLinks, -1, -1); - } - - private void checkReference(Reference ref) - { - Resource res = resources.get(ref.refResource); - Resource host = resources.get(ref.source); - - // Check remote resources - if (PathUtil.isRemote(ref.refResource) - // remote links and hyperlinks are not Publication Resources - && !EnumSet.of(Type.LINK, Type.HYPERLINK).contains(ref.type) - // spine items are checked in OPFChecker30 - && !(version == EPUBVersion.VERSION_3 && res != null && res.item.isInSpine()) - // audio, video, and fonts can be remote resources in EPUB 3 - && !(version == EPUBVersion.VERSION_3 - && (res != null - // if the item is declared, check its mime type - && (OPFChecker30.isAudioType(res.item.getMimeType()) - || OPFChecker30.isVideoType(res.item.getMimeType()) - || OPFChecker30.isFontType(res.item.getMimeType())) - // else, check if the reference is a type allowing remote resources - || ref.type == Type.FONT - || ref.type == Type.AUDIO - || ref.type == Type.VIDEO - ) - )) - { - report.message(MessageId.RSC_006, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, ref.refResource)); - return; - } - - // Check undeclared resources - if (res == null) - { - // Report references to missing local resources - if (!container.contains(ref.refResource) && !PathUtil.isRemote(ref.refResource)) - { - // only as a WARNING for 'link' references in EPUB 3 - if (version == EPUBVersion.VERSION_3 && ref.type == Type.LINK) - { - report.message(MessageId.RSC_007w, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, ref.refResource), - ref.refResource); - } - // by default as an ERROR - else - { - report.message(MessageId.RSC_007, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, ref.refResource), - ref.refResource); - } - } - // Report undeclared Publication Resources (once) - else if (!undeclared.contains(ref.refResource) - // links and remote hyperlinks are not Publication Resources - && !(ref.type == Type.LINK - || PathUtil.isRemote(ref.refResource) && ref.type == Type.HYPERLINK)) - { - undeclared.add(ref.refResource); - report.message(MessageId.RSC_008, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, ref.refResource), - ref.refResource); - } - return; - } - - // Type-specific checks - switch (ref.type) - { - case HYPERLINK: - // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedItemType(res.item.getMimeType(), version) - && !OPFChecker.isDeprecatedBlessedItemType(res.item.getMimeType()) - && !res.hasValidItemFallback) - { - report.message(MessageId.RSC_010, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, - ref.refResource + ((ref.fragment != null) ? '#' + ref.fragment : ""))); - return; - } - if (/* !res.mimeType.equals("font/opentype") && */!res.item.isInSpine()) - { - report.message(MessageId.RSC_011, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, - ref.refResource + ((ref.fragment != null) ? '#' + ref.fragment : ""))); - return; - } - break; - case IMAGE: - case PICTURE_SOURCE: - case PICTURE_SOURCE_FOREIGN: - if (ref.fragment != null && !res.item.getMimeType().equals("image/svg+xml")) - { - report.message(MessageId.RSC_009, EPUBLocation.create(ref.source, ref.lineNumber, - ref.columnNumber, ref.refResource + "#" + ref.fragment)); - return; - } - // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedImageType(res.item.getMimeType(), version)) - { - if (version == EPUBVersion.VERSION_3 && ref.type == Type.PICTURE_SOURCE) { - report.message(MessageId.MED_007, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), - ref.refResource, res.item.getMimeType()); - return; - } - else if (ref.type == Type.IMAGE && !res.hasValidImageFallback) { - report.message(MessageId.MED_003, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), - ref.refResource, res.item.getMimeType()); - } - } - break; - case SEARCH_KEY: - // TODO update when we support EPUB CFI - if ((ref.fragment == null || !ref.fragment.startsWith("epubcfi(")) && !res.item.isInSpine()) - { - report.message(MessageId.RSC_021, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), ref.refResource); - return; - } - break; - case STYLESHEET: - if (ref.fragment != null) - { - report.message(MessageId.RSC_013, EPUBLocation.create(ref.source, ref.lineNumber, - ref.columnNumber, ref.refResource + "#" + ref.fragment)); - return; - } - // if mimeType is null, we should have reported an error already - - // Implementations are allowed to process any stylesheet - // language they desire; so this is an - // error only if no fallback is available. - - // Since the presence of a 'text/css' stylesheet link can be considered - // a valid "built-in" fallback for a non-standard stylesheet (e.g. - // XPGT), the fallback chain test is performed in OPSHandler instead. - - // See also: - // https://github.com/IDPF/epubcheck/issues/244 - // https://github.com/IDPF/epubcheck/issues/271 - // https://github.com/IDPF/epubcheck/issues/541 - break; - case SVG_CLIP_PATH: - case SVG_PAINT: - case SVG_SYMBOL: - if (ref.fragment == null) - { - report.message(MessageId.RSC_015, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber, ref.refResource)); - return; - } - break; - default: - break; - } - - // Fragment integrity checks - if (ref.fragment != null) - { - // EPUB CFI - if (ref.fragment.startsWith("epubcfi(")) - { - // FIXME epubcfi currently not supported (see issue 150). - return; - } - // Media fragments in Data Navigation Documents - else if (ref.fragment.contains("=") && host != null && host.item.getProperties() - .contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.DATA_NAV))) - { - // Ignore, - return; - } - // SVG view fragments are ignored - else if (res.item.getMimeType().equals("image/svg+xml") - && REGEX_SVG_VIEW.matcher(ref.fragment).matches()) - { - return; - } - // Fragment Identifier (by default) - else if (!PathUtil.isRemote(ref.refResource)) - { - Anchor anchor = res.anchors.get(ref.fragment); - if (anchor == null) - { - report.message(MessageId.RSC_012, EPUBLocation.create(ref.source, ref.lineNumber, - ref.columnNumber, ref.refResource + "#" + ref.fragment)); - return; - } - switch (ref.type) - { - case SVG_PAINT: - case SVG_CLIP_PATH: - if (anchor.type != ref.type) - { - report.message(MessageId.RSC_014, EPUBLocation.create(ref.source, ref.lineNumber, - ref.columnNumber, ref.refResource + "#" + ref.fragment)); - return; - } - break; - case SVG_SYMBOL: - case HYPERLINK: - if (anchor.type != ref.type && anchor.type != Type.GENERIC) - { - report.message(MessageId.RSC_014, EPUBLocation.create(ref.source, ref.lineNumber, - ref.columnNumber, ref.refResource + "#" + ref.fragment)); - return; - } - break; - default: - break; - } - } - } - } - - private void checkRegionBasedNav(Reference ref) - { - Preconditions.checkArgument(ref.type == Type.REGION_BASED_NAV); - Resource res = resources.get(ref.refResource); - if (!res.item.isFixedLayout()) - { - report.message(MessageId.NAV_009, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber)); - } - } - - private void checkReadingOrder(Queue references, int lastSpinePosition, - int lastAnchorPosition) - { - // de-queue - Reference ref = references.poll(); - if (ref == null) return; - - Preconditions - .checkArgument(ref.type == Type.NAV_PAGELIST_LINK || ref.type == Type.NAV_TOC_LINK || ref.type == Type.OVERLAY_TEXT_LINK); - Resource res = resources.get(ref.refResource); - - // abort early if the link target is not a spine item (checked elsewhere) - if (res == null || !res.item.isInSpine()) return; - - // check that the link is in spine order - int targetSpinePosition = res.item.getSpinePosition(); - if (targetSpinePosition < lastSpinePosition) - { - String orderContext = LocalizedMessages.getInstance(locale).getSuggestion(MessageId.NAV_011, - "spine"); - - if (ref.type == Type.OVERLAY_TEXT_LINK) { - report.message(MessageId.MED_015, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), ref.value, orderContext); - } - else { - report.message(MessageId.NAV_011, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), - (ref.type == Type.NAV_TOC_LINK) ? "toc" : "page-list", ref.value, orderContext); - } - lastSpinePosition = targetSpinePosition; - lastAnchorPosition = -1; - } - else - { - - // if new spine item, reset last positions - if (targetSpinePosition > lastSpinePosition) - { - lastSpinePosition = targetSpinePosition; - lastAnchorPosition = -1; - } - - // check that the fragment is in document order - int targetAnchorPosition = res.getAnchorPosition(ref.fragment); - if (targetAnchorPosition < lastAnchorPosition) - { - String orderContext = LocalizedMessages.getInstance(locale).getSuggestion(MessageId.NAV_011, - "document"); - if (ref.type == Type.OVERLAY_TEXT_LINK) { - report.message(MessageId.MED_015, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), ref.value, orderContext); - } - else { - report.message(MessageId.NAV_011, - EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber), - (ref.type == Type.NAV_TOC_LINK) ? "toc" : "page-list", ref.value, orderContext); - } - } - lastAnchorPosition = targetAnchorPosition; - } - checkReadingOrder(references, lastSpinePosition, lastAnchorPosition); - } -} diff --git a/src/main/java/com/adobe/epubcheck/api/EpubCheck.java b/src/main/java/com/adobe/epubcheck/api/EpubCheck.java index 3c8600edb..a5059b059 100644 --- a/src/main/java/com/adobe/epubcheck/api/EpubCheck.java +++ b/src/main/java/com/adobe/epubcheck/api/EpubCheck.java @@ -32,7 +32,7 @@ import org.w3c.epubcheck.constants.MIMEType; import org.w3c.epubcheck.core.Checker; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.ocf.OCFChecker; diff --git a/src/main/java/com/adobe/epubcheck/css/CSSHandler.java b/src/main/java/com/adobe/epubcheck/css/CSSHandler.java index 80810a178..67397c17b 100644 --- a/src/main/java/com/adobe/epubcheck/css/CSSHandler.java +++ b/src/main/java/com/adobe/epubcheck/css/CSSHandler.java @@ -19,7 +19,8 @@ import org.idpf.epubcheck.util.css.CssGrammar.CssSelector; import org.idpf.epubcheck.util.css.CssGrammar.CssURI; import org.idpf.epubcheck.util.css.CssLocation; -import org.w3c.epubcheck.url.URLChecker; +import org.w3c.epubcheck.core.references.URLChecker; +import org.w3c.epubcheck.core.references.Reference; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.Report; @@ -27,8 +28,6 @@ import com.adobe.epubcheck.opf.OPFChecker; import com.adobe.epubcheck.opf.OPFChecker30; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; -import com.adobe.epubcheck.opf.XRefChecker.Type; import com.adobe.epubcheck.util.EPUBVersion; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.vocab.PackageVocabs; @@ -42,7 +41,6 @@ public class CSSHandler implements CssContentHandler, CssErrorHandler { final ValidationContext context; - final XRefChecker xrefChecker; final Report report; final EPUBVersion version; int startingLineNumber = 0; // append to line info from css parser @@ -69,7 +67,6 @@ public class CSSHandler implements CssContentHandler, CssErrorHandler public CSSHandler(ValidationContext context) { this.context = context; - this.xrefChecker = context.xrefChecker.orNull(); this.report = context.report; this.version = context.version; this.urlChecker = new URLChecker(context); @@ -159,7 +156,7 @@ else if (uriOrString.getType() == CssConstruct.Type.STRING) } if (uri != null) { - resolveAndRegister(uri, line, col, atRule.toCssString(), Type.GENERIC); + resolveAndRegister(uri, line, col, atRule.toCssString(), Reference.Type.GENERIC); } } } @@ -322,10 +319,10 @@ else if (propertyName.equals("src")) if (construct.getType() == CssConstruct.Type.URI) { URL fontURL = parsedURLs.get(((CssURI) construct).toUriString()); - if (fontURL != null) + if (fontURL != null && context.resourceRegistry.isPresent()) { // check font mimetypes - String fontMimeType = xrefChecker.getMimeType(fontURL); + String fontMimeType = context.getMimeType(fontURL); if (fontMimeType != null) { boolean blessed = true; @@ -367,12 +364,13 @@ private void registerURIs(List constructs, int line, int col) if (construct.getType() == CssConstruct.Type.URI) { resolveAndRegister(((CssURI) construct).toUriString(), line, col, construct.toCssString(), - inFontFace ? Type.FONT : Type.GENERIC); + inFontFace ? Reference.Type.FONT : Reference.Type.GENERIC); } } } - private void resolveAndRegister(String uriString, int line, int col, String cssContext, Type type) + private void resolveAndRegister(String uriString, int line, int col, String cssContext, + Reference.Type type) { if (uriString != null && uriString.trim().length() > 0) { @@ -386,9 +384,9 @@ private void resolveAndRegister(String uriString, int line, int col, String cssC URL url = urlChecker.checkURL(uriString, getCorrectedEPUBLocation(line, col, cssContext)); parsedURLs.put(uriString, url); - if (url != null) + if (url != null && context.referenceRegistry.isPresent()) { - xrefChecker.registerReference(url, type, getCorrectedEPUBLocation(line, col, cssContext)); + context.referenceRegistry.get().registerReference(url, type, getCorrectedEPUBLocation(line, col, cssContext)); if (context.isRemote(url)) { detectedProperties.add(ITEM_PROPERTIES.REMOTE_RESOURCES); diff --git a/src/main/java/com/adobe/epubcheck/dict/SearchKeyMapHandler.java b/src/main/java/com/adobe/epubcheck/dict/SearchKeyMapHandler.java index f7c9998d0..ad53f402c 100644 --- a/src/main/java/com/adobe/epubcheck/dict/SearchKeyMapHandler.java +++ b/src/main/java/com/adobe/epubcheck/dict/SearchKeyMapHandler.java @@ -1,7 +1,8 @@ package com.adobe.epubcheck.dict; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker.Type; import com.adobe.epubcheck.xml.handlers.XMLHandler; import com.adobe.epubcheck.xml.model.XMLElement; @@ -39,10 +40,7 @@ else if ("match".equals(name)) private void processRef() { URL ref = checkURL(currentElement().getAttribute("href")); - if (ref != null && context.xrefChecker.isPresent()) - { - context.xrefChecker.get().registerReference(ref, Type.SEARCH_KEY, location()); - } + registerReference(ref, Reference.Type.SEARCH_KEY); } } diff --git a/src/main/java/com/adobe/epubcheck/dtbook/DTBookHandler.java b/src/main/java/com/adobe/epubcheck/dtbook/DTBookHandler.java index 71c37caa7..086779509 100755 --- a/src/main/java/com/adobe/epubcheck/dtbook/DTBookHandler.java +++ b/src/main/java/com/adobe/epubcheck/dtbook/DTBookHandler.java @@ -22,9 +22,10 @@ package com.adobe.epubcheck.dtbook; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.util.URISchemes; import com.adobe.epubcheck.xml.handlers.XMLHandler; @@ -34,12 +35,10 @@ public class DTBookHandler extends XMLHandler { - private final XRefChecker xrefChecker; public DTBookHandler(ValidationContext context) { super(context); - this.xrefChecker = context.xrefChecker.get(); } @Override @@ -51,11 +50,15 @@ public void startElement() if (ns.equals("http://www.daisy.org/z3986/2005/dtbook/")) { // Register IDs - xrefChecker.registerID(e.getAttribute("id"), XRefChecker.Type.GENERIC, location()); + if (context.resourceRegistry.isPresent()) + { + context.resourceRegistry.get().registerID(e.getAttribute("id"), Reference.Type.GENERIC, + location().url); + } // Check cross-references (link@href | a@href | img@src) URL url = null; - XRefChecker.Type type = XRefChecker.Type.GENERIC; + Reference.Type type = Reference.Type.GENERIC; /* * This section checks to see if the references used are registered * schema-types and whether they point to external resources. The @@ -68,8 +71,9 @@ public void startElement() if (url != null && "true".equals(e.getAttribute("external"))) { - //FIXME 2022 check that external attribute is set for remote URLs - if (context.isRemote(url)) { + // FIXME 2022 check that external attribute is set for remote URLs + if (context.isRemote(url)) + { report.info(path, FeatureEnum.REFERENCE, url.toString()); if (!URISchemes.contains(url.scheme())) { @@ -86,12 +90,12 @@ else if (name.equals("link")) else if (name.equals("img")) { url = checkURL(e.getAttribute("src")); - type = XRefChecker.Type.IMAGE; + type = Reference.Type.IMAGE; } if (url != null) { - xrefChecker.registerReference(url, type, location()); + registerReference(url, type); if (context.isRemote(url)) { report.info(path, FeatureEnum.REFERENCE, url.toString()); diff --git a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java index f9811e36f..4d30d5bf5 100644 --- a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java +++ b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java @@ -144,8 +144,8 @@ private void initialize() severities.put(MessageId.HTM_058, Severity.ERROR); // Media - severities.put(MessageId.MED_001, Severity.ERROR); - severities.put(MessageId.MED_002, Severity.ERROR); + severities.put(MessageId.MED_001, Severity.SUPPRESSED); + severities.put(MessageId.MED_002, Severity.SUPPRESSED); severities.put(MessageId.MED_003, Severity.ERROR); severities.put(MessageId.MED_004, Severity.ERROR); severities.put(MessageId.MED_005, Severity.ERROR); @@ -205,7 +205,7 @@ private void initialize() severities.put(MessageId.OPF_010, Severity.ERROR); severities.put(MessageId.OPF_011, Severity.ERROR); severities.put(MessageId.OPF_012, Severity.ERROR); - severities.put(MessageId.OPF_013, Severity.ERROR); + severities.put(MessageId.OPF_013, Severity.WARNING); severities.put(MessageId.OPF_014, Severity.ERROR); severities.put(MessageId.OPF_015, Severity.ERROR); severities.put(MessageId.OPF_016, Severity.ERROR); @@ -348,6 +348,7 @@ private void initialize() severities.put(MessageId.RSC_029, Severity.ERROR); severities.put(MessageId.RSC_030, Severity.ERROR); severities.put(MessageId.RSC_031, Severity.WARNING); + severities.put(MessageId.RSC_032, Severity.ERROR); // Scripting severities.put(MessageId.SCP_001, Severity.SUPPRESSED); // checking scripts is out of scope diff --git a/src/main/java/com/adobe/epubcheck/messages/MessageId.java b/src/main/java/com/adobe/epubcheck/messages/MessageId.java index cb0939912..6e427ee59 100644 --- a/src/main/java/com/adobe/epubcheck/messages/MessageId.java +++ b/src/main/java/com/adobe/epubcheck/messages/MessageId.java @@ -342,6 +342,7 @@ public enum MessageId implements Comparable RSC_029("RSC-029"), RSC_030("RSC-030"), RSC_031("RSC-031"), + RSC_032("RSC-032"), // Messages relating to scripting SCP_001("SCP-001"), diff --git a/src/main/java/com/adobe/epubcheck/nav/NavHandler.java b/src/main/java/com/adobe/epubcheck/nav/NavHandler.java index 64de09bb3..d4c417c1e 100644 --- a/src/main/java/com/adobe/epubcheck/nav/NavHandler.java +++ b/src/main/java/com/adobe/epubcheck/nav/NavHandler.java @@ -3,9 +3,10 @@ import java.util.EnumSet; import java.util.Set; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; import com.adobe.epubcheck.ops.OPSHandler30; import com.adobe.epubcheck.util.EpubConstants; import com.adobe.epubcheck.util.FeatureEnum; @@ -78,12 +79,11 @@ public void startElement() // cross-reference checker, to be able to check that they are in reading // order // after all the Content Documents have been parsed - else if ((NavType.TOC__PAGE_LIST.contains(currentNavType)) && xrefChecker.isPresent()) + else if (NavType.TOC__PAGE_LIST.contains(currentNavType)) { - xrefChecker.get().registerReference(url, - (currentNavType == NavType.TOC) ? XRefChecker.Type.NAV_TOC_LINK - : XRefChecker.Type.NAV_PAGELIST_LINK, - location()); + registerReference(url, + (currentNavType == NavType.TOC) ? Reference.Type.NAV_TOC_LINK + : Reference.Type.NAV_PAGELIST_LINK); } } } diff --git a/src/main/java/com/adobe/epubcheck/ncx/NCXHandler.java b/src/main/java/com/adobe/epubcheck/ncx/NCXHandler.java index 7f3a5ae77..a05cfce23 100755 --- a/src/main/java/com/adobe/epubcheck/ncx/NCXHandler.java +++ b/src/main/java/com/adobe/epubcheck/ncx/NCXHandler.java @@ -22,9 +22,10 @@ package com.adobe.epubcheck.ncx; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.xml.handlers.XMLHandler; import com.adobe.epubcheck.xml.model.XMLElement; @@ -33,14 +34,12 @@ public class NCXHandler extends XMLHandler { - private final XRefChecker xrefChecker; private static final String TEXT = "text"; String uid; public NCXHandler(ValidationContext context) { super(context); - this.xrefChecker = context.xrefChecker.get(); } @Override @@ -76,7 +75,7 @@ public void startElement() { report.info(path, FeatureEnum.REFERENCE, srcURL.toString()); } - xrefChecker.registerReference(srcURL, XRefChecker.Type.HYPERLINK, location()); + registerReference(srcURL, Reference.Type.HYPERLINK); } } else if ("meta".equals(name)) diff --git a/src/main/java/com/adobe/epubcheck/ocf/OCFChecker.java b/src/main/java/com/adobe/epubcheck/ocf/OCFChecker.java index 5839c3fdc..6521d9b70 100755 --- a/src/main/java/com/adobe/epubcheck/ocf/OCFChecker.java +++ b/src/main/java/com/adobe/epubcheck/ocf/OCFChecker.java @@ -48,8 +48,6 @@ import com.adobe.epubcheck.opf.OPFItem; import com.adobe.epubcheck.opf.ValidationContext; import com.adobe.epubcheck.opf.ValidationContext.ValidationContextBuilder; -import com.adobe.epubcheck.opf.XRefChecker; -import com.adobe.epubcheck.overlay.OverlayTextChecker; import com.adobe.epubcheck.util.CheckUtil; import com.adobe.epubcheck.util.EPUBVersion; import com.adobe.epubcheck.util.FeatureEnum; @@ -170,8 +168,6 @@ public void check() opfContext.container(container); opfContext.pubTypes(state.getPublicationTypes(packageDoc)); - opfContext.xrefChecker(new XRefChecker(state.context().build())); - opfContext.overlayTextChecker(new OverlayTextChecker()); Checker opfChecker = CheckerFactory.newChecker(opfContext.build()); assert opfChecker instanceof OPFChecker; diff --git a/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java b/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java index 13baade18..6ebf1710c 100644 --- a/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java +++ b/src/main/java/com/adobe/epubcheck/ocf/OCFContainer.java @@ -7,7 +7,7 @@ import java.util.Set; import java.util.UUID; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.ocf.encryption.EncryptionFilter; import com.adobe.epubcheck.util.GenericResourceProvider; diff --git a/src/main/java/com/adobe/epubcheck/opf/FallbackChainResolver.java b/src/main/java/com/adobe/epubcheck/opf/FallbackChainResolver.java new file mode 100644 index 000000000..da75b17c9 --- /dev/null +++ b/src/main/java/com/adobe/epubcheck/opf/FallbackChainResolver.java @@ -0,0 +1,119 @@ +package com.adobe.epubcheck.opf; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.adobe.epubcheck.api.Report; +import com.adobe.epubcheck.messages.MessageId; +import com.adobe.epubcheck.util.EPUBVersion; +import com.google.common.collect.ImmutableList; + +public final class FallbackChainResolver +{ + + private final Report report; + private final EPUBVersion version; + private final Map items; + + public FallbackChainResolver(Map items, + ValidationContext context) + { + this.report = context.report; + this.version = context.version; + this.items = items; + } + + public List resolve() + { + Deque itemQueue = new LinkedList<>(items.values()); + ImmutableList.Builder resolved = ImmutableList.builderWithExpectedSize( + itemQueue.size()); + int pending = 0; // counter for pending unresolved items + + // Loop through the items to resolve the fallback chain + while (!itemQueue.isEmpty() && pending <= itemQueue.size()) + { + OPFItem.Builder item = itemQueue.pop(); + + // get this item's intrinsic fallback properties + // (i.e. does this item even require a fallback?) + String mimetype = item.mimetype(); + item.hasCoreMediaTypeFallback(item.hasCoreMediaTypeFallback() + || (version == EPUBVersion.VERSION_2 + && OPFChecker.isBlessedImageType(mimetype, version)) + || (OPFChecker30.isCoreMediaType(mimetype))); + item.hasContentDocumentFallback(item.hasContentDocumentFallback() + || OPFChecker.isBlessedItemType(mimetype, version) + || OPFChecker.isDeprecatedBlessedItemType(mimetype)); + + // check the fallback item, if any + if (item.hasFallback()) + { + // get the fallback item builder + OPFItem.Builder fallback = items.get(item.fallback()); + + // report missing fallback item + if (fallback == null) + { + report.message(MessageId.OPF_040, item.location(), item.fallback()); + } + // if fallback is resolved already, get the fallback properties + else if (fallback.isResolved()) + { + item.hasContentDocumentFallback( + item.hasContentDocumentFallback() || fallback.hasContentDocumentFallback()); + item.hasCoreMediaTypeFallback( + item.hasCoreMediaTypeFallback() || fallback.hasCoreMediaTypeFallback()); + } + // else the fallback is not resolved yet, + // re-add this pending item to the queue + else + { + itemQueue.add(item); + pending++; + continue; + } + } + + // in EPUB 2.0.1, check the fallback-style attribute + if (version == EPUBVersion.VERSION_2 && item.hasFallbackStyle()) + { + + // get the fallback item builder + OPFItem.Builder fallbackStyle = items.get(item.fallbackStyle()); + + // report missing fallback style item + if (fallbackStyle == null) + { + report.message(MessageId.OPF_041, item.location(), item.fallback()); + } + // if fallback is resolved already, get the fallback properties + else + { + boolean hasValidFallbackStyle = OPFChecker.isBlessedStyleType(fallbackStyle.mimetype()) + || OPFChecker.isDeprecatedBlessedStyleType(fallbackStyle.mimetype()); + item.hasContentDocumentFallback( + item.hasContentDocumentFallback() || hasValidFallbackStyle); + item.hasCoreMediaTypeFallback( + item.hasCoreMediaTypeFallback() || hasValidFallbackStyle); + } + } + + // mark this item as resolved + resolved.add(item.build()); + item.markResolved(); + pending = 0; + } + // if the queue is not empty, we found a circular chain + if (!itemQueue.isEmpty()) + { + // report and build the remaining items + report.message(MessageId.OPF_045, itemQueue.peek().location()); + itemQueue.stream().forEach(i -> resolved.add(i.build())); + } + return resolved.build(); + } + +} diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java b/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java index 52cd8df50..9f810c936 100755 --- a/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFChecker.java @@ -34,6 +34,7 @@ import org.w3c.epubcheck.core.AbstractChecker; import org.w3c.epubcheck.core.Checker; import org.w3c.epubcheck.core.CheckerFactory; +import org.w3c.epubcheck.core.references.ResourceReferencesChecker; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.EPUBProfile; @@ -101,7 +102,6 @@ protected boolean checkPackage() { Preconditions.checkState(context.container.isPresent()); OCFContainer container = context.container.get(); - XRefChecker xrefChecker = context.xrefChecker.get(); if (!container.contains(context.url)) { report.message(MessageId.PKG_020, EPUBLocation.of(context), path); @@ -116,15 +116,12 @@ protected boolean checkPackage() List items = opfHandler.getItems(); report.info(null, FeatureEnum.ITEMS_COUNT, Integer.toString(items.size())); - + // Register package doc and items to the XRefChecker - xrefChecker.registerResource(context.url, context.mimeType); + context.resourceRegistry.get().registerResource(context.url, context.mimeType); for (OPFItem item : items) { - xrefChecker.registerResource(item, - new FallbackChecker().checkItemFallbacks(item, opfHandler, true), - new FallbackChecker().checkImageFallbacks(item, opfHandler)); - + context.resourceRegistry.get().registerResource(item); report.info(item.getPath(), FeatureEnum.DECLARED_MIMETYPE, item.getMimeType()); } @@ -147,7 +144,8 @@ protected boolean checkPackage() checkItemAfterResourceValidation(item); } - xrefChecker.checkReferences(); + // Check references + new ResourceReferencesChecker(context).check(); return false; } @@ -318,7 +316,6 @@ public static boolean isScriptType(String type) protected void checkItem(OPFItem item, OPFHandler opfHandler) { String mimeType = item.getMimeType(); - Optional fallback = item.getFallback(); if (!mimeType.matches("[a-zA-Z0-9!#$&+-^_]+/[a-zA-Z0-9!#$&+-^_]+")) { /* @@ -344,7 +341,7 @@ else if (opfHandler.getOpf20PackageFile()) report.message(MessageId.OPF_037, item.getLocation().context(item.getId()), mimeType); } } - if (opfHandler.getOpf12PackageFile() && !fallback.isPresent()) + if (opfHandler.getOpf12PackageFile() && !item.hasFallback()) { if (isBlessedItemType(mimeType, version)) { @@ -355,29 +352,13 @@ else if (isBlessedStyleType(mimeType)) report.message(MessageId.OPF_039, item.getLocation().context(item.getId()), mimeType); } } - if (fallback.isPresent()) - { - Optional fallbackItem = opfHandler.getItemById(fallback.get()); - if (!fallbackItem.isPresent()) - { - report.message(MessageId.OPF_040, item.getLocation().context(item.getId())); - } - } - - if (item.getFallbackStyle().isPresent()) - { - Optional fallbackStyleItem = opfHandler.getItemById(item.getFallbackStyle().get()); - if (!fallbackStyleItem.isPresent()) - { - report.message(MessageId.OPF_041, item.getLocation().context(item.getId())); - } - } } protected void checkItemContent(OPFItem item) { // We do not currently support checking resources defined as data URLs - if (item.hasDataURL()) { + if (item.hasDataURL()) + { return; } // Create a new validation context for the OPF item @@ -424,105 +405,16 @@ protected void checkSpineItem(OPFItem item, OPFHandler opfHandler) { report.message(MessageId.OPF_042, item.getLocation(), mimeType); } - else if (!isBlessedItemType(mimeType, version) && !isDeprecatedBlessedItemType(mimeType) - && !item.getFallback().isPresent()) + else if (!isBlessedItemType(mimeType, version) && !isDeprecatedBlessedItemType(mimeType)) { - report.message(MessageId.OPF_043, item.getLocation(), mimeType); - } - else if (!isBlessedItemType(mimeType, version) && !isDeprecatedBlessedItemType(mimeType) - && !new FallbackChecker().checkItemFallbacks(item, opfHandler, true)) - { - report.message(MessageId.OPF_044, item.getLocation(), mimeType); - } - } - - class FallbackChecker - { - private final Set checked; - - public FallbackChecker() - { - checked = new HashSet(); - } - - // FIXME 2022 - move this to OPFItems builder - boolean checkItemFallbacks(OPFItem item, OPFHandler opfHandler, boolean checkFallbackStyle) - { - if (item.getFallback().isPresent()) - { - String fallback = item.getFallback().get(); - if (checked.contains(fallback)) - { - report.message(MessageId.OPF_045, item.getLocation()); - return false; - } - else - { - checked.add(fallback); - } - - Optional fallbackItem = opfHandler.getItemById(fallback); - if (fallbackItem.isPresent()) - { - String mimeType = fallbackItem.get().getMimeType(); - if (isBlessedItemType(mimeType, version) || isDeprecatedBlessedItemType(mimeType)) - { - return true; - } - if (checkItemFallbacks(fallbackItem.get(), opfHandler, checkFallbackStyle)) - { - return true; - } - - } - } - if (!checkFallbackStyle) + if (!item.hasFallback()) { - return false; + report.message(MessageId.OPF_043, item.getLocation(), mimeType); } - if (item.getFallbackStyle().isPresent()) + else if (!item.hasContentDocumentFallback()) { - String fallbackStyle = item.getFallbackStyle().get(); - Optional fallbackStyleItem = opfHandler.getItemById(fallbackStyle); - if (fallbackStyleItem.isPresent()) - { - String mimeType = fallbackStyleItem.get().getMimeType(); - return (isBlessedStyleType(mimeType) || isDeprecatedBlessedStyleType(mimeType)); - } + report.message(MessageId.OPF_044, item.getLocation(), mimeType); } - return false; - } - - // FIXME 2022 - move this to OPFItems builder - boolean checkImageFallbacks(OPFItem item, OPFHandler opfHandler) - { - if (item.getFallback().isPresent()) - { - String fallback = item.getFallback().get(); - if (checked.contains(fallback)) - { - report.message(MessageId.OPF_045, item.getLocation()); - return false; - } - else - { - checked.add(fallback); - } - Optional fallbackItem = opfHandler.getItemById(fallback); - if (fallbackItem.isPresent()) - { - String mimeType = fallbackItem.get().getMimeType(); - if (isBlessedImageType(mimeType, context.version)) - { - return true; - } - if (checkImageFallbacks(fallbackItem.get(), opfHandler)) - { - return true; - } - } - } - return false; } } } diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java b/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java index 7f2cba8f4..f5bcbeede 100644 --- a/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java @@ -24,7 +24,7 @@ import java.util.Set; -import org.w3c.epubcheck.url.URLFragment; +import org.w3c.epubcheck.util.url.URLFragment; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.EPUBProfile; @@ -115,16 +115,11 @@ protected void checkItem(OPFItem item, OPFHandler opfHandler) { report.message(MessageId.OPF_091, item.getLocation()); } - - // Note: item fallback existence is checked in schematron, i.e.: - // opfHandler.getItemById(item.getFallback().get()).isPresent() == true } @Override protected void checkItemAfterResourceValidation(OPFItem item) { - XRefChecker xrefChecker = context.xrefChecker.get(); - // Check remote resources String mediatype = item.getMimeType(); if (item.isRemote() @@ -139,7 +134,7 @@ protected void checkItemAfterResourceValidation(OPFItem item) report.message(MessageId.RSC_006, item.getLocation(), item.getPath()); } // if no direct reference to the resource was found, - else if (xrefChecker.getTypes(item.getURL()).isEmpty()) + else if (!context.referenceRegistry.get().hasReferencesTo(item.getURL())) { // if may be allowed when if the resource is retrieved from a script if (context.featureReport.hasFeature(FeatureEnum.HAS_SCRIPTS)) @@ -187,12 +182,13 @@ else if (!overlayTextChecker.isCorrectOverlay(docURL, mo)) @Override protected void checkSpineItem(OPFItem item, OPFHandler opfHandler) { - // Items with `data:` URLs are not allowed in the spine - if (item.hasDataURL()) { + // Items with `data:` URLs are not allowed in the spine + if (item.hasDataURL()) + { report.message(MessageId.RSC_029, item.getLocation()); return; } - + String mimeType = item.getMimeType(); if (item.getProperties() @@ -201,39 +197,20 @@ protected void checkSpineItem(OPFItem item, OPFHandler opfHandler) report.message(MessageId.OPF_077, item.getLocation()); } - if (isBlessedItemType(mimeType, version)) - { - return; - } - - if (!item.getFallback().isPresent()) + if (!isBlessedItemType(mimeType, version)) { - report.message(MessageId.OPF_043, item.getLocation(), mimeType); + if (!item.hasFallback()) + { + report.message(MessageId.OPF_043, item.getLocation(), mimeType); + } + else if (!item.hasContentDocumentFallback()) + { + report.message(MessageId.OPF_044, item.getLocation(), mimeType); + } } - else if (!new FallbackChecker().checkItemFallbacks(item, opfHandler, false)) - { - report.message(MessageId.OPF_044, item.getLocation(), mimeType); - } } - // protected boolean checkItemFallbacks(OPFItem item, OPFHandler opfHandler) { - // String fallback = item.getFallback(); - // if (fallback != null) { - // OPFItem fallbackItem = opfHandler.getItemById(fallback); - // if (fallbackItem != null) { - // String mimeType = fallbackItem.getMimeType(); - // if (mimeType != null) { - // if (OPFChecker.isBlessedItemType(mimeType, version)) - // return true; - // if (checkItemFallbacks(fallbackItem, opfHandler)) - // return true; - // } - // } - // } - // return false; - // } - private void checkCollections() { for (ResourceCollection collection : ((OPFHandler30) opfHandler).getCollections().asList()) @@ -579,11 +556,13 @@ public static boolean isBlessedScriptType(String type) public static boolean isCoreMediaType(String type) { - return isBlessedAudioType(type) || isBlessedVideoType(type) || isBlessedFontType(type) - || isBlessedItemType(type, EPUBVersion.VERSION_3) - || isBlessedImageType(type, EPUBVersion.VERSION_3) || isBlessedScriptType(type) - || type.equals("application/pls+xml") || type.equals("application/smil+xml") - || type.equals("image/svg+xml"); + return type != null + && (isBlessedAudioType(type) || isBlessedVideoType(type) || isBlessedFontType(type) + || isBlessedItemType(type, EPUBVersion.VERSION_3) + || isBlessedImageType(type, EPUBVersion.VERSION_3) || isBlessedScriptType(type) + || isBlessedStyleType(type) + || type.equals("application/pls+xml") || type.equals("application/smil+xml") + || type.equals("image/svg+xml")); } public static String getPreferredMediaType(String type, String path) diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFHandler.java b/src/main/java/com/adobe/epubcheck/opf/OPFHandler.java index afb194dc5..ed6ff7f16 100755 --- a/src/main/java/com/adobe/epubcheck/opf/OPFHandler.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFHandler.java @@ -32,6 +32,8 @@ import java.util.UUID; import java.util.Vector; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.util.DateParser; @@ -56,8 +58,7 @@ public class OPFHandler extends XMLHandler protected String pageMapId = null; protected EPUBLocation pageMapReferenceLocation = null; - // Map of ID to OPFItem builders - // Final OPFItem objects will be built after parsing + // Maps to OPFItem builders, to be built after parsing protected final Map itemBuilders = Maps.newLinkedHashMap(); protected final Map itemBuildersByURL = Maps.newLinkedHashMap(); // A list of all spine item IDs @@ -138,9 +139,9 @@ public boolean getOpf20PackageFile() * Search the list of item by ID. * * @param id - * the ID of the item to search + * the ID of the item to search * @return an {@link Optional} containing the item of the given ID if found, - * or {@link Optional#absent()} + * or {@link Optional#absent()} */ public Optional getItemById(String id) { @@ -151,9 +152,9 @@ public Optional getItemById(String id) * Search the list of item by URL. * * @param id - * the URL of the item to search + * the URL of the item to search * @return an {@link Optional} containing the item of the given URL if found, - * or {@link Optional#absent()} + * or {@link Optional#absent()} */ public Optional getItemByURL(URL url) { @@ -205,8 +206,8 @@ public OPFReference getReference(int index) * references an existing DC metadata identifier element's id attribute * * @return true if there is an identifier with an id attribute that matches - * the value of the unique-identifier attribute of the package - * element. False otherwise. + * the value of the unique-identifier attribute of the package + * element. False otherwise. */ public boolean checkUniqueIdentExists() { @@ -275,14 +276,7 @@ else if (name.equals("item")) } String mimeType = e.getAttribute("media-type"); String fallback = e.getAttribute("fallback"); - - // dirty fix for issue 271: treat @fallback attribute in EPUB3 like - // fallback-style in EPUB2 - // then all the epubcheck mechanisms on checking stylesheet - // fallbacks will work as in EPUB 2 - String fallbackStyle = (context.version == EPUBVersion.VERSION_3) - ? e.getAttribute("fallback") - : e.getAttribute("fallback-style"); + String fallbackStyle = e.getAttribute("fallback-style"); OPFItem.Builder itemBuilder = new OPFItem.Builder().id(id).url(url).location(location()) .container(context.container).remote(context.isRemote(url)).mimetype(mimeType) @@ -303,25 +297,19 @@ else if (name.equals("reference")) String type = e.getAttribute("type"); String title = e.getAttribute("title"); String href = e.getAttribute("href"); - if (href != null && context.xrefChecker.isPresent()) + URL url = checkURL(href); + if (url != null) { - URL url = checkURL(href); - if (context.isRemote(url)) { + if (context.isRemote(url)) + { report.info(path, FeatureEnum.REFERENCE, href); } - try - { - context.xrefChecker.get().registerReference(url, XRefChecker.Type.GENERIC, location()); - } catch (IllegalArgumentException ex) - { - report.message(MessageId.OPF_010, location().context(href), ex.getMessage()); - return; - } - OPFReference ref = new OPFReference(type, title, url, location().getLine(), - location().getColumn()); - refs.add(ref); + registerReference(url, Reference.Type.GENERIC); } + OPFReference ref = new OPFReference(type, title, url, location().getLine(), + location().getColumn()); + refs.add(ref); } else if (name.equals("spine")) @@ -636,7 +624,8 @@ public void characters(char[] chars, int start, int len) private void buildItems() { Preconditions.checkState(items == null); - items = OPFItems.build(itemBuilders.values(), spineIDs); + + items = OPFItems.build(itemBuilders, spineIDs, context); for (OPFItem item : items.getItems()) { @@ -648,7 +637,7 @@ private void buildItems() * Report features or messages for a given item. * * @param item - * the item to report. + * the item to report. */ protected void reportItem(OPFItem item) { diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFHandler30.java b/src/main/java/com/adobe/epubcheck/opf/OPFHandler30.java index a6950fe5c..60208082b 100644 --- a/src/main/java/com/adobe/epubcheck/opf/OPFHandler30.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFHandler30.java @@ -54,14 +54,14 @@ import java.util.Map; import java.util.Set; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.core.references.Reference; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.QuietReport; import com.adobe.epubcheck.messages.LocalizedMessages; import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.ResourceCollection.Roles; -import com.adobe.epubcheck.opf.XRefChecker.Type; import com.adobe.epubcheck.util.EpubConstants; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.vocab.AccessibilityVocab; @@ -403,10 +403,7 @@ private void processLink() { report.info(path, FeatureEnum.REFERENCE, href); } - if (context.xrefChecker.isPresent()) - { - context.xrefChecker.get().registerReference(url, Type.LINK, location()); - } + registerReference(url, Reference.Type.LINK); // check the 'rel' attribute String rel = e.getAttribute("rel"); diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFItem.java b/src/main/java/com/adobe/epubcheck/opf/OPFItem.java index 6bc72a001..023bdbe93 100755 --- a/src/main/java/com/adobe/epubcheck/opf/OPFItem.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFItem.java @@ -24,7 +24,7 @@ import java.util.Set; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.ocf.OCFContainer; @@ -51,8 +51,9 @@ public class OPFItem private final EPUBLocation location; private final String path; private final String mimetype; - private final Optional fallback; - private final Optional fallbackStyle; + private final boolean hasFallback; + private final boolean hasCoreMediaTypeFallback; + private final boolean hasContentDocumentFallback; private final Set properties; private final boolean ncx; private final boolean inSpine; @@ -82,12 +83,11 @@ private OPFItem(Builder builder) this.id = builder.id.trim(); this.url = builder.url; - this.mimetype = Optional.fromNullable(builder.mimeType).or("undefined").trim(); + this.mimetype = builder.mimetype(); this.location = builder.location; - this.fallback = Optional - .fromNullable(Strings.emptyToNull(Strings.nullToEmpty(builder.fallback).trim())); - this.fallbackStyle = Optional - .fromNullable(Strings.emptyToNull(Strings.nullToEmpty(builder.fallbackStyle).trim())); + this.hasFallback = builder.hasFallback(); + this.hasCoreMediaTypeFallback = builder.hasCoreMediaTypeFallback(); + this.hasContentDocumentFallback = builder.hasContentDocumentFallback(); this.properties = builder.propertiesBuilder.build(); this.ncx = builder.ncx; this.inSpine = builder.spinePosition > -1; @@ -183,27 +183,38 @@ public EPUBLocation getLocation() } /** - * Returns an {@link Optional} containing the ID of the fallback item for this - * item, if it has one. + * Returns whether this package document item defines a fallback to another + * item. * - * @return An optional containing the ID of the fallback item for this item if - * it has one, or {@link Optional#absent()} otherwise. + * @return true iff this item has a fallback item. */ - public Optional getFallback() + public boolean hasFallback() { - return fallback; + return hasFallback; } /** - * Returns An {@link Optional} containing the ID of the fallback stylesheet - * for this item, if it has one. + * Returns whether this item is a core media type resource, or has a core + * media type resource in its fallback chain. * - * @return An optional containing the ID of the fallback stylesheet for this - * item if it has one, or {@link Optional#absent()} otherwise. + * @return true iff a core media type resource was found in the + * fallback chain (can be itself) */ - public Optional getFallbackStyle() + public boolean hasCoreMediaTypeFallback() { - return fallbackStyle; + return hasCoreMediaTypeFallback; + } + + /** + * Returns whether this item is itself an EPUB content document, or has an + * EPUB content document in its fallback chain. + * + * @return true iff an EPUB content document was found in the + * fallback chain (can be itself) + */ + public boolean hasContentDocumentFallback() + { + return hasContentDocumentFallback; } /** @@ -222,7 +233,7 @@ public Set getProperties() * this item is not in the spine. * * @return the position of this item in the spine, or {@code -1} if this item - * is not in the spine. + * is not in the spine. */ public int getSpinePosition() { @@ -275,7 +286,7 @@ public boolean isInSpine() * * @return true iff this item is in the spine and is linear. * @throws IllegalStateException - * if this item is not in the spine. + * if this item is not in the spine. */ public boolean isLinear() { @@ -324,7 +335,7 @@ public String getMediaOverlay() @Override public String toString() { - return url + "[" + id + "]"; + return path + "[" + id + "]"; } @Override @@ -368,9 +379,12 @@ public static final class Builder private EPUBLocation location; private Optional container; private boolean remote = false; - private String mimeType; - private String fallback = null; - private String fallbackStyle = null; + private String mimetype; + private String fallback; + private String fallbackStyle; + private boolean hasContentDocumentFallback = false; + private boolean hasCoreMediaTypeFallback = false; + private boolean isFallbackResolved = false; private boolean ncx = false; private boolean linear = true; private int spinePosition = -1; @@ -384,6 +398,11 @@ public Builder id(String id) return this; } + public String id() + { + return id; + } + public Builder url(URL url) { this.url = url; @@ -396,6 +415,11 @@ public Builder location(EPUBLocation location) return this; } + public EPUBLocation location() + { + return location; + } + public Builder container(Optional container) { this.container = container; @@ -410,22 +434,80 @@ public Builder remote(boolean remote) public Builder mimetype(String mimetype) { - this.mimeType = mimetype; + this.mimetype = Optional.fromNullable(mimetype).or("undefined").trim(); return this; } + public String mimetype() + { + return mimetype; + } + public Builder fallback(String fallback) { - this.fallback = fallback; + this.fallback = Strings.nullToEmpty(fallback).trim(); return this; } + public String fallback() + { + return fallback; + } + + public boolean hasFallback() + { + return !fallback.isEmpty(); + } + public Builder fallbackStyle(String fallbackStyle) { - this.fallbackStyle = fallbackStyle; + this.fallbackStyle = Strings.nullToEmpty(fallbackStyle).trim(); return this; } + public String fallbackStyle() + { + return fallbackStyle; + } + + public boolean hasFallbackStyle() + { + return !fallbackStyle.isEmpty(); + } + + public Builder hasCoreMediaTypeFallback(boolean hasCoreMediaTypeFallback) + { + this.hasCoreMediaTypeFallback = hasCoreMediaTypeFallback; + return this; + } + + public boolean hasCoreMediaTypeFallback() + { + return this.hasCoreMediaTypeFallback; + } + + public Builder hasContentDocumentFallback(boolean hasContentDocumentFallback) + { + this.hasContentDocumentFallback = hasContentDocumentFallback; + return this; + } + + public boolean hasContentDocumentFallback() + { + return hasContentDocumentFallback; + } + + public Builder markResolved() + { + this.isFallbackResolved = true; + return this; + } + + public boolean isResolved() + { + return isFallbackResolved; + } + public Builder fixedLayout() { this.fxl = true; @@ -465,6 +547,10 @@ public Builder properties(Set properties) } return this; } + + public String toString() { + return id; + } /** * Builds a new immutable {@link OPFItem} from this builder. diff --git a/src/main/java/com/adobe/epubcheck/opf/OPFItems.java b/src/main/java/com/adobe/epubcheck/opf/OPFItems.java index 2ca40c73d..a6a058806 100644 --- a/src/main/java/com/adobe/epubcheck/opf/OPFItems.java +++ b/src/main/java/com/adobe/epubcheck/opf/OPFItems.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Map; -import com.adobe.epubcheck.opf.OPFItem.Builder; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; @@ -11,14 +10,12 @@ import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import io.mola.galimatias.URL; /** * Represents the set of Publication Resources in a Package Document (OPF). - * */ public final class OPFItems { @@ -32,9 +29,9 @@ public final class OPFItems * Search the item with the given ID. * * @param id - * the ID of the item to search, can be null. + * the ID of the item to search, can be null. * @return An {@link Optional} containing the item if found, or - * {@link Optional#absent()} if not found. + * {@link Optional#absent()} if not found. */ public Optional getItemById(String id) { @@ -45,9 +42,9 @@ public Optional getItemById(String id) * Search the item with the given path. * * @param id - * the URL of the item to search, can be null. + * the URL of the item to search, can be null. * @return An {@link Optional} containing the item if found, or - * {@link Optional#absent()} if not found. + * {@link Optional#absent()} if not found. */ public Optional getItemByURL(URL url) { @@ -81,7 +78,7 @@ private OPFItems(Iterable items, Iterable spineIDs) // Build the by-ID and by-Paths maps // We use temporary HashMaps to ignore potential duplicate keys Map itemsById = Maps.newHashMap(); - Map itemsByURL= Maps.newHashMap(); + Map itemsByURL = Maps.newHashMap(); for (OPFItem item : this.items) { itemsById.put(item.getId(), item); @@ -105,21 +102,16 @@ public OPFItem apply(final String id) * of spine item IDs. * * @param itemBuilders - * the builders of the {@link OPFItem} in the set. + * the builders of the {@link OPFItem} in the set. * @param spineIDs - * the IDs of the items in the spine. + * the IDs of the items in the spine. + * @param context * @return a consolidated set of {@link OPFItem}s. */ - public static OPFItems build(Iterable itemBuilders, Iterable spineIDs) + public static OPFItems build(Map itemBuilders, Iterable spineIDs, + ValidationContext context) { - return new OPFItems(Iterables.transform(Preconditions.checkNotNull(itemBuilders), - new Function() - { - @Override - public OPFItem apply(Builder builder) - { - return builder.build(); - } - }), Preconditions.checkNotNull(spineIDs)); + return new OPFItems(new FallbackChainResolver(itemBuilders, context).resolve(), + Preconditions.checkNotNull(spineIDs)); } } diff --git a/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java b/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java index d62d5f181..7e65da8e3 100644 --- a/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java +++ b/src/main/java/com/adobe/epubcheck/opf/ValidationContext.java @@ -8,7 +8,9 @@ import java.util.Set; import org.w3c.epubcheck.core.Checker; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.core.references.ReferenceRegistry; +import org.w3c.epubcheck.core.references.ResourceRegistry; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBProfile; import com.adobe.epubcheck.api.FeatureReport; @@ -40,6 +42,11 @@ */ public final class ValidationContext { + + /** + * The URL of the validated resource. Guaranteed non-null. + */ + public final URL url; /** * The path to the validated resource. Guaranteed non-null. */ @@ -86,9 +93,13 @@ public final class ValidationContext */ public final Optional container; /** - * The cross-reference checker, absent for single-file validation. + * The publication resource registry, absent for single-file validation. */ - public final Optional xrefChecker; + public final Optional resourceRegistry; + /** + * The references registry, absent for single-file validation + */ + public final Optional referenceRegistry; /** * The src checker for media overlay text elements, absent for single-file * validation @@ -104,8 +115,6 @@ public final class ValidationContext */ public final Set properties; - public final URL url; - private ValidationContext(ValidationContextBuilder builder) { // FIXME 2022 add a default report if not provided @@ -115,6 +124,8 @@ private ValidationContext(ValidationContextBuilder builder) Preconditions.checkState(builder.report != null, "report must be set"); this.url = builder.url; this.container = Optional.fromNullable(builder.container); + this.resourceRegistry = Optional.fromNullable(builder.resourceRegistry); + this.referenceRegistry = Optional.fromNullable(builder.referenceRegistry); this.mimeType = Strings.nullToEmpty(builder.mimeType); this.version = Optional.fromNullable(builder.version).or(EPUBVersion.Unknown); this.profile = Optional.fromNullable(builder.profile).or(EPUBProfile.DEFAULT); @@ -126,9 +137,9 @@ private ValidationContext(ValidationContextBuilder builder) this.resourceProvider = Iterables.find( Arrays.asList(builder.container, builder.resourceProvider, new URLResourceProvider()), Predicates.notNull()); - this.opfItem = (builder.xrefChecker != null) ? builder.xrefChecker.getResource(builder.url) - : Optional. absent(); - this.xrefChecker = Optional.fromNullable(builder.xrefChecker); + this.opfItem = Optional.fromNullable((builder.resourceRegistry != null) + ? builder.resourceRegistry.getOPFItem(builder.url).orElse(null) + : null); this.overlayTextChecker = Optional.fromNullable(builder.overlayTextChecker); this.pubTypes = (builder.pubTypes != null) ? Sets.immutableEnumSet(builder.pubTypes) : EnumSet.noneOf(PublicationType.class); @@ -230,7 +241,8 @@ public static final class ValidationContextBuilder private GenericResourceProvider resourceProvider = null; private OCFContainer container = null; - private XRefChecker xrefChecker = null; + private ResourceRegistry resourceRegistry = null; + private ReferenceRegistry referenceRegistry = null; private OverlayTextChecker overlayTextChecker = null; private Set pubTypes = null; private ImmutableSet.Builder properties = ImmutableSet. builder(); @@ -254,7 +266,8 @@ public ValidationContextBuilder copy(ValidationContext context) featureReport = context.featureReport; resourceProvider = context.resourceProvider; container = context.container.orNull(); - xrefChecker = context.xrefChecker.orNull(); + resourceRegistry = context.resourceRegistry.orNull(); + referenceRegistry = context.referenceRegistry.orNull(); overlayTextChecker = context.overlayTextChecker.orNull(); pubTypes = context.pubTypes; properties = ImmutableSet. builder().addAll(context.properties); @@ -306,20 +319,12 @@ public ValidationContextBuilder resourceProvider(GenericResourceProvider resourc public ValidationContextBuilder container(OCFContainer container) { this.container = container; - return this; - } - - // FIXME next should be silently done when the container is set - public ValidationContextBuilder xrefChecker(XRefChecker xrefChecker) - { - this.xrefChecker = xrefChecker; - return this; - } - - // FIXME next should be silently done when the container is set - public ValidationContextBuilder overlayTextChecker(OverlayTextChecker overlayTextChecker) - { - this.overlayTextChecker = overlayTextChecker; + if (container != null) + { + this.resourceRegistry = new ResourceRegistry(); + this.referenceRegistry = new ReferenceRegistry(container, resourceRegistry); + this.overlayTextChecker = new OverlayTextChecker(); + } return this; } @@ -460,4 +465,21 @@ private ValidationContextPredicates() } } + public String getMimeType(URL url) + { + if (url == null) return null; + if ("data".equals(url.scheme())) + { + return URLUtils.getDataURLType(url); + } + else if (resourceRegistry.isPresent()) + { + return resourceRegistry.get().getMimeType(url); + } + else + { + return null; + } + } + } diff --git a/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java b/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java deleted file mode 100755 index bcc58982f..000000000 --- a/src/main/java/com/adobe/epubcheck/opf/XRefChecker.java +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Copyright (c) 2007 Adobe Systems Incorporated - * - * 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. - * - */ - -package com.adobe.epubcheck.opf; - -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -import org.w3c.epubcheck.constants.MIMEType; -import org.w3c.epubcheck.url.URLFragment; -import org.w3c.epubcheck.url.URLUtils; - -import com.adobe.epubcheck.api.EPUBLocation; -import com.adobe.epubcheck.api.LocalizableReport; -import com.adobe.epubcheck.api.Report; -import com.adobe.epubcheck.messages.LocalizedMessages; -import com.adobe.epubcheck.messages.MessageId; -import com.adobe.epubcheck.ocf.OCFContainer; -import com.adobe.epubcheck.util.EPUBVersion; -import com.adobe.epubcheck.util.FeatureEnum; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; - -import io.mola.galimatias.GalimatiasParseException; -import io.mola.galimatias.URL; - -public class XRefChecker -{ - - public static enum Type - { - GENERIC, - FONT, - HYPERLINK, - LINK, - IMAGE, - OBJECT, - STYLESHEET, - AUDIO, - VIDEO, - SVG_PAINT, - SVG_CLIP_PATH, - SVG_SYMBOL, - REGION_BASED_NAV, - SEARCH_KEY, - NAV_TOC_LINK, - NAV_PAGELIST_LINK, - OVERLAY_TEXT_LINK, - PICTURE_SOURCE, - PICTURE_SOURCE_FOREIGN; - } - - private static class URLReference - { - public final URL url; - public final URL targetDoc; - public final EPUBLocation location; - public final Type type; - - public URLReference(URL url, Type type, EPUBLocation location) - { - try - { - this.url = url; - this.type = type; - this.location = location; - this.targetDoc = url.withFragment(null); - } catch (GalimatiasParseException e) - { - throw new AssertionError(e); - } - } - - } - - private static class ID - { - - @SuppressWarnings("unused") - public final String id; - public final Type type; - public final int position; - - public ID(String id, int position, Type type) - { - this.id = id; - this.position = position; - this.type = type; - } - - } - - private static class Resource - { - public static final class Builder - { - - private URL url; - private OPFItem item = null; - private boolean hasItemFallback = false; - private boolean hasImageFallback = false; - public String mimetype; - - public Builder url(URL url) - { - this.url = url; - return this; - } - - public Builder item(OPFItem item) - { - this.url = item.getURL(); - this.item = item; - this.mimetype = item.getMimeType(); - return this; - } - - public Builder mimetype(String mimetype) - { - this.mimetype = mimetype; - return this; - } - - public Builder hasItemFallback(boolean hasItemFallback) - { - this.hasItemFallback = hasItemFallback; - return this; - } - - public Builder hasImageFallback(boolean hasImageFallback) - { - this.hasImageFallback = hasImageFallback; - return this; - } - - public Resource build() - { - return new Resource(this); - } - } - - private final URL url; - private final String mimetype; - private final Optional item; - private final Map ids; - private final boolean hasItemFallback; - private final boolean hasImageFallback; - - private Resource(Builder builder) - { - Preconditions.checkState(builder.url != null, "A URL or OPF Item must be provided"); - Preconditions.checkState(builder.mimetype != null, "A MIME type must be provided"); - Preconditions - .checkState(builder.item == null || builder.item.getMimeType().equals(builder.mimetype)); - this.url = builder.url; - this.item = Optional.fromNullable(builder.item); - this.hasItemFallback = builder.hasItemFallback; - this.hasImageFallback = builder.hasImageFallback; - this.ids = new HashMap(); - this.mimetype = builder.mimetype; - - } - - /** - * Returns the position of the given ID in the document represented by this - * resource. - * - * @return {@code -1} if the ID wasn't found in the document, or {@code 0} - * if the given ID is {@code null} or an empty string, or the - * 1-based position of the ID otherwise. - */ - public int getIDPosition(String id) - { - if (id == null || id.trim().isEmpty()) return 0; - ID anchor = ids.get(id); - return (anchor != null) ? anchor.position : -1; - } - - public String getMimeType() - { - return mimetype; - } - - public boolean hasItem() - { - return item.isPresent(); - } - - public OPFItem getItem() - { - return item.orNull(); - } - - public URL getURL() - { - return url; - } - - public boolean hasItemFallback() - { - return hasItemFallback; - } - - public boolean hasImageFallback() - { - return hasImageFallback; - } - - public boolean isInSpine() - { - return item.isPresent() && item.get().isInSpine(); - } - } - - private final Map resources = new HashMap(); - - private final Set undeclared = new HashSet(); - - private final List references = new LinkedList(); - - private final Report report; - - private final OCFContainer container; - - private final EPUBVersion version; - - private final Locale locale; - - public XRefChecker(ValidationContext context) - { - Preconditions.checkArgument(context.container.isPresent()); - this.container = context.container.get(); - this.report = context.report; - this.version = context.version; - this.locale = (report instanceof LocalizableReport) ? ((LocalizableReport) report).getLocale() - : Locale.ENGLISH; - } - - public String getMimeType(URL resource) - { - return resources.containsKey(resource) ? resources.get(resource).getMimeType() : null; - } - - /** - * Returns an {@link Optional} containing the Package Document item for the - * given Publication Resource path, or {@link Optional#absent()} if no - * resource has been registered for the given path. - */ - public Optional getResource(URL url) - { - return (url == null || !resources.containsKey(url)) ? Optional. absent() - : Optional.fromNullable(resources.get(url).getItem()); - } - - /** - * Returns set (possibly multiple) types of references to the given resource - * - * @param path - * the path to a publication resource - * @return an immutable {@link EnumSet} containing the types of references to - * {@code path}. - */ - public Set getTypes(URL resource) - { - Preconditions.checkArgument(resource != null); - ImmutableSet.Builder types = ImmutableSet.builder(); - for (URLReference reference : references) - { - if (resource.equals(URLUtils.docURL(reference.url))) - { - types.add(reference.type); - } - } - return types.build(); - } - - public void registerResource(URL url, String mimetype) - { - Preconditions.checkArgument(url != null); - if (!resources.containsKey(url)) - { - resources.put(url, new Resource.Builder().url(url).mimetype(mimetype).build()); - } - } - - // FIXME 2022 simplify signature: fallback info can be moved to OPFItem - public void registerResource(OPFItem item, boolean hasValidItemFallback, - boolean hasValidImageFallback) - { - Preconditions.checkArgument(item != null); - // Note: Duplicate manifest items are already checked in OPFChecker. - if (!resources.containsKey(item.getURL())) - { - - resources.put(item.getURL(), new Resource.Builder().item(item) - .hasItemFallback(hasValidItemFallback).hasImageFallback(hasValidImageFallback).build()); - } - } - - public void registerID(String id, Type type, EPUBLocation location) - { - if (id == null) return; - Resource res = resources.get(location.url); - Preconditions.checkArgument(res != null, "resource not registered"); - // Note: duplicate IDs are checked in schematron - if (!res.ids.containsKey(id)) - { - res.ids.put(id, new ID(id, res.ids.size() + 1, type)); - } - } - - public void registerReference(URL url, Type type, EPUBLocation location) - { - if (url == null) return; - - // Remove query component of local URLs - // FIXME 2022 check how to deal with local query strings - // see http://code.google.com/p/epubcheck/issues/detail?id=190 - // see http://code.google.com/p/epubcheck/issues/detail?id=261 - if (url.query() != null && !container.isRemote(url)) - { - try - { - url = url.withQuery(null); - } catch (GalimatiasParseException e) - { - new AssertionError("could not remove URL query"); - } - } - - // Create and register a new reference - URLReference xref = new URLReference(url, type, location); - references.add(xref); - report.info(location.getPath(), FeatureEnum.RESOURCE, container.relativize(xref.targetDoc)); - - // If it is a data URL, also register a new resource - if ("data".equals(url.scheme())) - { - registerResource(url, URLUtils.getDataURLType(url)); - } - } - - public void checkReferences() - { - // if (checkReference(reference)) checkReferenceSubtypes(reference); - Queue tocLinks = new LinkedList<>(); - Queue pageListLinks = new LinkedList<>(); - Queue overlayLinks = new LinkedList<>(); - for (URLReference reference : references) - { - switch (reference.type) - { - case REGION_BASED_NAV: - checkRegionBasedNav(reference); - break; - case NAV_TOC_LINK: - tocLinks.add(reference); - break; - case NAV_PAGELIST_LINK: - pageListLinks.add(reference); - break; - case OVERLAY_TEXT_LINK: - overlayLinks.add(reference); - checkReference(reference); - break; - default: - checkReference(reference); - break; - } - } - checkReadingOrder(tocLinks); - checkReadingOrder(overlayLinks); - } - - private void checkReference(URLReference reference) - { - // Retrieve the target resource - Resource targetResource = resources.get(reference.targetDoc); - String targetMimetype = (targetResource != null) ? targetResource.getMimeType() : ""; - - // Parse the URL fragment - URLFragment fragment = URLFragment.parse(reference.url, targetMimetype); - - // Check remote resources - if (container.isRemote(reference.url)) - { - // Check if the remote reference is allowed - if (// remote links and hyperlinks are not Publication Resources - !EnumSet.of(Type.LINK, Type.HYPERLINK).contains(reference.type) - // spine items are checked in OPFChecker30 - && !(version == EPUBVersion.VERSION_3 && targetResource != null - && targetResource.isInSpine()) - // audio, video, and fonts can be remote resources in EPUB 3 - && !(version == EPUBVersion.VERSION_3 && (targetResource != null - // if the item is declared, check its mime type - && (OPFChecker30.isAudioType(targetResource.getMimeType()) - || OPFChecker30.isVideoType(targetResource.getMimeType()) - || OPFChecker30.isFontType(targetResource.getMimeType())) - // else, check if the reference is a type allowing remote - // resources - || reference.type == Type.FONT || reference.type == Type.AUDIO - || reference.type == Type.VIDEO))) - { - report.message(MessageId.RSC_006, - reference.location.context(reference.targetDoc.toString())); - return; - } - // Check if the remote resource is using HTTPS - else if (version == EPUBVersion.VERSION_3 - && !EnumSet.of(Type.LINK, Type.HYPERLINK).contains(reference.type) - && !"https".equals(reference.url.scheme()) - // file URLs are disallowed and reported elsewhere - && !"file".equals(reference.url.scheme())) - { - report.message(MessageId.RSC_031, reference.location, reference.url); - } - } - - // Check undeclared resources - if (targetResource == null) - { - // Report references to missing local resources - if (!container.contains(reference.url) && !container.isRemote(reference.url)) - { - // only as a WARNING for 'link' references in EPUB 3 - if (version == EPUBVersion.VERSION_3 && reference.type == Type.LINK) - { - report.message(MessageId.RSC_007w, reference.location, - container.relativize(reference.targetDoc)); - } - // by default as an ERROR - else - { - report.message(MessageId.RSC_007, reference.location, - container.relativize(reference.targetDoc)); - } - } - // Report undeclared Publication Resources (once) - else if (!undeclared.contains(reference.targetDoc) - // links and remote hyperlinks are not Publication Resources - && !(reference.type == Type.LINK - || container.isRemote(reference.targetDoc) && reference.type == Type.HYPERLINK)) - { - undeclared.add(reference.targetDoc); - report.message(MessageId.RSC_008, reference.location, - container.relativize(reference.targetDoc)); - } - return; - } - - // Type-specific checks - switch (reference.type) - { - case HYPERLINK: - - if ("epubcfi".equals(fragment.getScheme())) - { - break; // EPUB CFI is not supported - } - // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedItemType(targetMimetype, version) - && !OPFChecker.isDeprecatedBlessedItemType(targetMimetype) - && !targetResource.hasItemFallback()) - { - report.message(MessageId.RSC_010, - reference.location.context(container.relativize(reference.url))); - return; - } - if (/* !res.mimeType.equals("font/opentype") && */!targetResource.isInSpine()) - { - report.message(MessageId.RSC_011, - reference.location.context(container.relativize(reference.url))); - return; - } - break; - case IMAGE: - case PICTURE_SOURCE: - case PICTURE_SOURCE_FOREIGN: - if ("epubcfi".equals(fragment.getScheme())) - { - break; // EPUB CFI is not supported - } - if (fragment.exists() && !MIMEType.SVG.is(targetMimetype)) - { - report.message(MessageId.RSC_009, - reference.location.context(container.relativize(reference.url))); - return; - } - // if mimeType is null, we should have reported an error already - if (!OPFChecker.isBlessedImageType(targetMimetype, version)) - { - if (version == EPUBVersion.VERSION_3 && reference.type == Type.PICTURE_SOURCE) - { - report.message(MessageId.MED_007, reference.location, - container.relativize(reference.targetDoc), targetMimetype); - return; - } - else if (reference.type == Type.IMAGE && !targetResource.hasImageFallback()) - { - report.message(MessageId.MED_003, reference.location, - container.relativize(reference.targetDoc), targetMimetype); - } - } - break; - case OVERLAY_TEXT_LINK: - if (!OPFChecker.isBlessedItemType(targetMimetype, version)) - { - report.message(MessageId.RSC_010, - reference.location.context(container.relativize(reference.url))); - return; - } - break; - case SEARCH_KEY: - // TODO update when we support EPUB CFI - if ((!fragment.exists() || !"epubcfi".equals(fragment.getScheme())) - && !targetResource.isInSpine()) - { - report.message(MessageId.RSC_021, reference.location, - container.relativize(reference.targetDoc)); - return; - } - break; - case STYLESHEET: - if (fragment.exists()) - { - report.message(MessageId.RSC_013, - reference.location.context(container.relativize(reference.url))); - return; - } - // if mimeType is null, we should have reported an error already - - // Implementations are allowed to process any stylesheet - // language they desire; so this is an - // error only if no fallback is available. - - // Since the presence of a 'text/css' stylesheet link can be considered - // a valid "built-in" fallback for a non-standard stylesheet (e.g. - // XPGT), the fallback chain test is performed in OPSHandler instead. - - // See also: - // https://github.com/IDPF/epubcheck/issues/244 - // https://github.com/IDPF/epubcheck/issues/271 - // https://github.com/IDPF/epubcheck/issues/541 - break; - case SVG_CLIP_PATH: - case SVG_PAINT: - case SVG_SYMBOL: - if (!fragment.exists()) - { - report.message(MessageId.RSC_015, reference.location.context(reference.url)); - return; - } - break; - default: - break; - } - - // Fragment integrity checks - if (fragment.exists() && !fragment.isEmpty()) - { - // Check media overlays requirements - if (reference.type == Type.OVERLAY_TEXT_LINK) - { - // Check that references to XHTML indicate an element by ID - if (MIMEType.XHTML.is(targetMimetype) && fragment.getId().isEmpty()) - { - report.message(MessageId.MED_017, reference.location, fragment.toString()); - } - // Check that references to SVG use a SVG fragment identifier - else if (MIMEType.SVG.is(targetMimetype) && !fragment.isValid()) - { - report.message(MessageId.MED_018, reference.location, fragment.toString()); - } - } - - // Check ID-based fragments - // Other fragment types (e.g. EPUB CFI) are not currently supported - if (!fragment.getId().isEmpty() && !container.isRemote(reference.targetDoc)) - { - ID targetID = targetResource.ids.get(fragment.getId()); - if (targetID == null) - { - report.message(MessageId.RSC_012, reference.location.context(reference.url.toString())); - return; - } - switch (reference.type) - { - case SVG_PAINT: - case SVG_CLIP_PATH: - if (targetID.type != reference.type) - { - report.message(MessageId.RSC_014, reference.location.context(reference.url.toString())); - return; - } - break; - case SVG_SYMBOL: - case HYPERLINK: - case OVERLAY_TEXT_LINK: - if (targetID.type != reference.type && targetID.type != Type.GENERIC) - { - report.message(MessageId.RSC_014, reference.location.context(reference.url.toString())); - return; - } - break; - default: - break; - } - } - } - } - - private void checkRegionBasedNav(URLReference ref) - { - Preconditions.checkArgument(ref.type == Type.REGION_BASED_NAV); - Resource res = resources.get(ref.targetDoc); - if (res != null && res.hasItem() && !res.getItem().isFixedLayout()) - { - report.message(MessageId.NAV_009, ref.location); - } - } - - private void checkReadingOrder(Queue references) - { - int lastSpinePosition = -1; - int lastAnchorPosition = -1; - while (!references.isEmpty()) - { - URLReference ref = references.poll(); - Preconditions - .checkArgument(ref.type == Type.NAV_PAGELIST_LINK || ref.type == Type.NAV_TOC_LINK - || ref.type == Type.OVERLAY_TEXT_LINK); - Resource res = resources.get(ref.targetDoc); - // abort early if the link target is not a spine item (checked elsewhere) - if (res == null || !res.hasItem() || !res.getItem().isInSpine()) - { - continue; - } - - // check that the link is in spine order - int targetSpinePosition = res.getItem().getSpinePosition(); - if (targetSpinePosition < lastSpinePosition) - { - String orderContext = LocalizedMessages.getInstance(locale).getSuggestion(MessageId.NAV_011, - "spine"); - - if (ref.type == Type.OVERLAY_TEXT_LINK) - { - report.message(MessageId.MED_015, ref.location, container.relativize(ref.url), - orderContext); - } - else - { - report.message(MessageId.NAV_011, ref.location, - (ref.type == Type.NAV_TOC_LINK) ? "toc" : "page-list", container.relativize(ref.url), - orderContext); - } - lastSpinePosition = targetSpinePosition; - lastAnchorPosition = -1; - } - else - { - - // if new spine item, reset last positions - if (targetSpinePosition > lastSpinePosition) - { - lastSpinePosition = targetSpinePosition; - lastAnchorPosition = -1; - } - - // check that the fragment is in document order - URLFragment fragment = URLFragment.parse(ref.url, res.getMimeType()); - int targetAnchorPosition = res.getIDPosition(fragment.getId()); - if (targetAnchorPosition < lastAnchorPosition) - { - String orderContext = LocalizedMessages.getInstance(locale).getSuggestion( - MessageId.NAV_011, - "document"); - if (ref.type == Type.OVERLAY_TEXT_LINK) - { - report.message(MessageId.MED_015, ref.location, container.relativize(ref.url), - orderContext); - } - else - { - report.message(MessageId.NAV_011, ref.location, - (ref.type == Type.NAV_TOC_LINK) ? "toc" : "page-list", - container.relativize(ref.url), - orderContext); - } - } - lastAnchorPosition = targetAnchorPosition; - } - } - - } -} diff --git a/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java b/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java index 464417879..7fba651da 100755 --- a/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java +++ b/src/main/java/com/adobe/epubcheck/ops/OPSHandler.java @@ -25,26 +25,25 @@ import java.util.Locale; import java.util.Stack; +import org.w3c.epubcheck.core.references.Reference; + import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.css.CSSChecker; import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.OPFChecker; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; import com.adobe.epubcheck.util.EPUBVersion; import com.adobe.epubcheck.util.EpubConstants; import com.adobe.epubcheck.util.FeatureEnum; import com.adobe.epubcheck.util.URISchemes; import com.adobe.epubcheck.xml.handlers.XMLHandler; import com.adobe.epubcheck.xml.model.XMLElement; -import com.google.common.base.Optional; import io.mola.galimatias.URL; public class OPSHandler extends XMLHandler { - protected final Optional xrefChecker; protected long openElements; protected long charsCount; protected int tableDepth = 0; @@ -59,34 +58,34 @@ public class OPSHandler extends XMLHandler public OPSHandler(ValidationContext context) { super(context); - this.xrefChecker = context.xrefChecker; } private void checkPaint(String attr) { String paint = currentElement().getAttribute(attr); - if (xrefChecker.isPresent() && paint != null && paint.startsWith("url(") && paint.endsWith(")")) + if (paint != null && paint.startsWith("url(") + && paint.endsWith(")")) { URL url = checkURL(paint.substring(4, paint.length() - 1)); - xrefChecker.get().registerReference(url, XRefChecker.Type.SVG_PAINT, location()); + registerReference(url, Reference.Type.SVG_PAINT); } } protected void checkImage(String attrNS, String attr) { URL imageURL = checkURL(currentElement().getAttributeNS(attrNS, attr)); - if (xrefChecker.isPresent() && imageURL != null) + if (imageURL != null) { - xrefChecker.get().registerReference(imageURL, XRefChecker.Type.IMAGE, location()); + registerReference(imageURL, Reference.Type.IMAGE); } } - private void checkObject() + protected void checkObject() { URL objectURL = checkURL(currentElement().getAttribute("data")); - if (xrefChecker.isPresent() && objectURL != null) + if (objectURL != null) { - xrefChecker.get().registerReference(objectURL, XRefChecker.Type.OBJECT, location()); + registerReference(objectURL, Reference.Type.GENERIC); } } @@ -99,9 +98,7 @@ protected void checkLink() && rel.toLowerCase(Locale.ROOT).contains("stylesheet")) { this.hasCSS = true; - if (xrefChecker.isPresent()) { - xrefChecker.get().registerReference(href, XRefChecker.Type.STYLESHEET, location()); - } + registerReference(href, Reference.Type.STYLESHEET); } } @@ -110,9 +107,9 @@ protected void checkLink() protected void checkSymbol() { URL href = checkURL(currentElement().getAttributeNS("http://www.w3.org/1999/xlink", "href")); - if (xrefChecker.isPresent() && href != null) + if (href != null) { - xrefChecker.get().registerReference(href, XRefChecker.Type.SVG_SYMBOL, location()); + registerReference(href, Reference.Type.SVG_SYMBOL); } } @@ -158,19 +155,16 @@ else if (".".equals(href)) protected URL checkSVGFontFaceURI() { URL href = checkURL(currentElement().getAttributeNS("http://www.w3.org/1999/xlink", "href")); - if (xrefChecker.isPresent() && href != null) + if (href != null) { - xrefChecker.get().registerReference(href, XRefChecker.Type.FONT, location()); + registerReference(href, Reference.Type.FONT); } return href; } protected void processHyperlink(URL href) { - if (xrefChecker.isPresent()) - { - xrefChecker.get().registerReference(href, XRefChecker.Type.HYPERLINK, location()); - } + registerReference(href, Reference.Type.HYPERLINK); } @Override @@ -193,7 +187,7 @@ public void startElement() String ns = e.getNamespace(); String name = e.getName().toLowerCase(Locale.ROOT); - XRefChecker.Type resourceType = XRefChecker.Type.GENERIC; + Reference.Type resourceType = Reference.Type.GENERIC; if (ns != null) { if (ns.equals("http://www.w3.org/2000/svg")) @@ -201,15 +195,15 @@ public void startElement() if (name.equals("lineargradient") || name.equals("radialgradient") || name.equals("pattern")) { - resourceType = XRefChecker.Type.SVG_PAINT; + resourceType = Reference.Type.SVG_PAINT; } else if (name.equals("clippath")) { - resourceType = XRefChecker.Type.SVG_CLIP_PATH; + resourceType = Reference.Type.SVG_CLIP_PATH; } else if (name.equals("symbol")) { - resourceType = XRefChecker.Type.SVG_SYMBOL; + resourceType = Reference.Type.SVG_SYMBOL; } else if (name.equals("a")) { @@ -292,9 +286,9 @@ else if (name.equals("script")) } } } - if (xrefChecker.isPresent() && id != null) + if (context.resourceRegistry.isPresent() && id != null) { - xrefChecker.get().registerID(id, resourceType, location()); + context.resourceRegistry.get().registerID(id, resourceType, location().url); } } diff --git a/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java b/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java index 35a1a9a96..41ff35942 100644 --- a/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java +++ b/src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -9,11 +10,14 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import org.w3c.epubcheck.constants.MIMEType; +import org.w3c.epubcheck.core.references.Reference; +import org.w3c.epubcheck.core.references.Reference.Type; +import org.w3c.epubcheck.core.references.Resource; import org.w3c.epubcheck.util.microsyntax.ViewportMeta; import org.w3c.epubcheck.util.microsyntax.ViewportMeta.ParseError; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.api.EPUBProfile; @@ -21,7 +25,6 @@ import com.adobe.epubcheck.opf.OPFChecker; import com.adobe.epubcheck.opf.OPFChecker30; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker; import com.adobe.epubcheck.util.EPUBVersion; import com.adobe.epubcheck.util.EpubConstants; import com.adobe.epubcheck.util.FeatureEnum; @@ -56,6 +59,7 @@ public class OPSHandler30 extends OPSHandler { + private static final String HAS_PALPABLE_CONTENT = "IS_PALPABLE"; private static Map RESERVED_VOCABS = ImmutableMap. of("", AggregateVocab.of(StructureVocab.VOCAB, StagingEdupubVocab.VOCAB, DataNavVocab.VOCAB, @@ -69,7 +73,6 @@ public class OPSHandler30 extends OPSHandler private static Set DEFAULT_VOCAB_URIS = ImmutableSet.of(StructureVocab.URI); private static final Splitter TOKENIZER = Splitter.onPattern("\\s+").omitEmptyStrings(); - private static final Pattern DATA_URI_PATTERN = Pattern.compile("^data:(.*?)(;.*)?,.*"); private Map vocabs = RESERVED_VOCABS; @@ -78,13 +81,7 @@ public class OPSHandler30 extends OPSHandler private final boolean isLinear; - protected boolean inVideo = false; - protected boolean inAudio = false; protected boolean inPicture = false; - protected boolean hasValidFallback = false; - - protected int imbricatedObjects = 0; - protected int imbricatedCanvases = 0; protected boolean anchorNeedsText = false; protected boolean inMathML = false; @@ -94,7 +91,9 @@ public class OPSHandler30 extends OPSHandler protected boolean isOutermostSVGAlreadyProcessed = false; protected boolean hasAltorAnnotation = false; protected boolean hasLabel = false; + protected boolean hasListItem = false; protected boolean hasViewport = false; + private Map mediaSources; static protected final String[] scriptEventsStrings = { "onafterprint", "onbeforeprint", "onbeforeunload", "onerror", "onhaschange", "onload", "onmessage", "onoffline", "onpagehide", @@ -150,59 +149,72 @@ public OPSHandler30(ValidationContext context) protected void checkImage(String attrNS, String attr) { XMLElement e = currentElement(); - // if it's an SVG image, fall back to super's logic - String ns = e.getNamespace(); - if ("http://www.w3.org/2000/svg".equals(ns)) + + // if it's an SVG image, just register the reference + if ("http://www.w3.org/2000/svg".equals(e.getNamespace())) { - super.checkImage(attrNS, attr); + URL url = checkResourceURL(e.getAttributeNS(attrNS, attr)); + registerReference(url, Reference.Type.IMAGE); } - // else process image source sets in HTML - else if (xrefChecker.isPresent()) + // else process image or image source sets in HTML + else { String src = e.getAttribute("src"); String srcset = e.getAttribute("srcset"); - // if we're in a 'picture' element - if (inPicture) + + // compute a list of image URLs to register + Set imageSources = new TreeSet<>(); + if (src != null) imageSources.add(src); + imageSources.addAll(SourceSet.parse(srcset).getImageURLs()); + + // register all the URLs + for (String urlString : imageSources) { - String type = e.getAttribute("type"); - // if in a 'source' element specifying a foreign MIME type, - // register as foreign picture source - if ("source".equals(e.getName()) && type != null - && !OPFChecker.isBlessedImageType(type, EPUBVersion.VERSION_3)) - { - registerImageSources(src, srcset, XRefChecker.Type.PICTURE_SOURCE_FOREIGN); - } - // else register as regular picture source (must be a CMT) - else - // register as picture source + URL url = checkResourceURL(urlString); + if (url != null && context.referenceRegistry.isPresent()) { - registerImageSources(src, srcset, XRefChecker.Type.PICTURE_SOURCE); + Resource imageResource = context.resourceRegistry.get() + .getResource(URLUtils.docURL(url)).orElse(null); + // check picture-specific fallback rules + if (inPicture && imageResource != null) + { + String mimetype = imageResource.getMimeType(); + URL imageURL = imageResource.getURL(); + switch (e.getName()) + { + case "img": + // an `img` child of `picture` MUST be a core media type resource + if (!OPFChecker.isBlessedImageType(mimetype, EPUBVersion.VERSION_3)) + { + report.message(MessageId.MED_003, location(), + context.relativize(imageURL), mimetype); + } + break; + case "source": + // a `source` child of `picture` MUST be core media type resource + // or have a `type` attribute + String type = Strings.nullToEmpty(e.getAttribute("type")).trim(); + if (type.isEmpty() && !OPFChecker.isBlessedImageType(mimetype, EPUBVersion.VERSION_3)) + { + report.message(MessageId.MED_007, location(), + context.relativize(imageURL), mimetype); + } + else + { + // warn about HTML-declared/EPUB-declared type mismatch + checkMimetypeMatches(url, type); + } + break; + } + } + // register the image resource + // only check manifest fallback if the image is not in `picture` + registerReference(url, Reference.Type.IMAGE, inPicture); } } - // register as regular image sources (must be a CMT or have a manifest - // fallback - else - { - registerImageSources(src, srcset, XRefChecker.Type.IMAGE); - } } } - protected void registerImageSources(String src, String srcset, XRefChecker.Type type) - { - // compute a list of URLs to register - Set imageSources = new TreeSet<>(); - if (src != null) imageSources.add(src); - imageSources.addAll(SourceSet.parse(srcset).getImageURLs()); - // register all the URLs - for (String imageURLString : imageSources) - { - URL imageURL = checkURL(imageURLString); - xrefChecker.get().registerReference(imageURL, type, location()); - } - - } - protected void checkType(String type) { if (type == null) @@ -290,12 +302,14 @@ protected void checkSSMLPh(String ph) public void characters(char[] chars, int arg1, int arg2) { super.characters(chars, arg1, arg2); - String str = new String(chars, arg1, arg2); - str = str.trim(); - if (!str.equals("") && (inAudio || inVideo || imbricatedObjects > 0 || imbricatedCanvases > 0)) + + // set the palpable state + if (!new String(chars, arg1, arg2).trim().isEmpty()) { - hasValidFallback = true; + // FIXME this should only be set for elements allowing flow/phrasing + currentElement().setPrivateData(HAS_PALPABLE_CONTENT, true); } + if (anchorNeedsText) { anchorNeedsText = false; @@ -309,6 +323,9 @@ public void startElement() XMLElement e = currentElement(); + // set this element's initial palpable state to false + e.setPrivateData(HAS_PALPABLE_CONTENT, false); + checkDiscouragedElements(); processSemantics(); processSectioning(); @@ -332,10 +349,6 @@ else if (name.equals("link")) { processLink(); } - else if (name.equals("object")) - { - processObject(); - } else if (name.equals("math")) { requiredProperties.add(ITEM_PROPERTIES.MATHML); @@ -358,11 +371,12 @@ else if (EpubConstants.EpubTypeNamespaceUri.equals(e.getNamespace()) && name.equ } else if (name.equals("audio")) { - processAudio(); + startMediaElement(); } else if (name.equals("video")) { processVideo(); + startMediaElement(); } else if (name.equals("figure")) { @@ -372,13 +386,9 @@ else if (name.equals("table")) { processTable(); } - else if (name.equals("canvas")) + else if (name.equals("track")) { - processCanvas(); - } - else if (name.equals("img")) - { - processImg(); + startTrack(); } else if (name.equals("a")) { @@ -389,13 +399,24 @@ else if (name.equals("annotation-xml")) { hasAltorAnnotation = true; } + else if (name.equals("input")) + { + startInput(); + } else if (name.equals("picture")) { inPicture = true; } else if (name.equals("source")) { - if (inPicture) checkImage(null, null); + if ("picture".equals(e.getParent().getName())) + { + checkImage(null, null); + } + else // audio or video source + { + startMediaSource(); + } } else if ("http://www.w3.org/2000/svg".equals(e.getNamespace()) && name.equals("title")) { @@ -405,18 +426,49 @@ else if ("http://www.w3.org/2000/svg".equals(e.getNamespace()) && name.equals("t { hasLabel = true; } + else if (name.equals("embed")) + { + startEmbed(); + } + else if (name.equals("blockquote") || name.equals("q") || name.equals("ins") + || name.equals("del")) + { + checkCiteAttribute(); + } processInlineScripts(); - // FIXME 2022 this should be moved to checking submethods, to avoid - // duplicate URL checks - processSrc(("source".equals(name)) ? e.getParent().getName() : name, e.getAttribute("src")); - checkType(e.getAttributeNS(EpubConstants.EpubTypeNamespaceUri, "type")); checkSSMLPh(e.getAttributeNS("http://www.w3.org/2001/10/synthesis", "ph")); } + private void checkCiteAttribute() + { + URL url = checkResourceURL(currentElement().getAttribute("cite")); + registerReference(url, Type.CITE); + } + + private void startTrack() + { + URL url = checkResourceURL(currentElement().getAttribute("src")); + registerReference(url, Type.TRACK); + } + + private void startInput() + { + URL url = checkResourceURL(currentElement().getAttribute("src")); + registerReference(url, Type.GENERIC); + } + + private void startEmbed() + { + URL url = checkResourceURL(currentElement().getAttribute("src")); + checkMimetypeMatches(url, currentElement().getAttribute("type")); + registerReference(url, Type.GENERIC); + + } + protected void checkDiscouragedElements() { XMLElement elem = currentElement(); @@ -451,6 +503,14 @@ protected void processInlineScripts() } } + @Override + protected void checkScript() + { + super.checkScript(); + URL url = checkResourceURL(currentElement().getAttribute("src")); + registerReference(url, Type.GENERIC); + } + @Override protected void processJavascript() { @@ -501,48 +561,106 @@ protected void processAnchor(XMLElement e) } } - protected void processImg() + protected void startMediaElement() { - if ((inAudio || inVideo || imbricatedObjects > 0 || imbricatedCanvases > 0)) - { - hasValidFallback = true; - } + assert "audio".equals(currentElement().getName()) || "video".equals(currentElement().getName()); + + mediaSources = new HashMap<>(); + + // check the `src` attribute + // note: schema ensures if `src` is set, the audio has no `source` children + URL url = checkResourceURL(currentElement().getAttribute("src")); + + // register the reference (does nothing if URL is null) + registerMediaResource(url, context.getMimeType(url), false); } - protected void processCanvas() + protected void endMediaElement() { - imbricatedCanvases++; + assert "audio".equals(currentElement().getName()) || "video".equals(currentElement().getName()); + + // set fallback flag + // the media element has an intrinsic fallback if any of its source children + // represent a core media type resource + boolean hasFallback = mediaSources.values().stream() + .anyMatch(mimetype -> OPFChecker30.isCoreMediaType(mimetype)); + + // register the list of audio sources with the fallback flag + mediaSources.forEach((url, mimetype) -> registerMediaResource(url, mimetype, hasFallback)); } - protected void processAudio() + protected void startMediaSource() { - inAudio = true; - context.featureReport.report(FeatureEnum.AUDIO, location()); + XMLElement elem = currentElement(); + assert "source".equals(elem.getName()) + && ("audio".equals(elem.getParent().getName()) + || "video".equals(elem.getParent().getName())) + && elem.getParent().getAttribute("src") == null; + + // check the `src` attribute + URL url = checkResourceURL(elem.getAttribute("src")); + + // check full-publication rules + if (context.container.isPresent()) + { + // get the MIME type of the media resource + String mimetype = checkMimetypeMatches(url, elem.getAttribute("type")); + + // record the type for fallback checking and resource registration, + // when closing `audio` after all `source` elements are parsed + mediaSources.put(url, mimetype); + } } - protected void processVideo() + protected void registerMediaResource(URL url, String mimetype, boolean hasFallback) { - inVideo = true; - context.featureReport.report(FeatureEnum.VIDEO, location()); + if (url == null) return; + if (OPFChecker30.isAudioType(mimetype)) + { + context.featureReport.report(FeatureEnum.AUDIO, location()); + registerReference(url, Type.AUDIO, hasFallback); + } + else + { + context.featureReport.report(FeatureEnum.VIDEO, location()); + registerReference(url, Type.VIDEO); + } + } - URL posterURL = processSrc(currentElement().getName(), currentElement().getAttribute("poster")); + protected String checkMimetypeMatches(URL resource, String mimetype) + { + // get the MIME type of the resource declared in the package document + String resourceMimetype = context.getMimeType(resource); - if (posterURL != null) + if (mimetype == null) + { + return resourceMimetype; + } + else { - hasValidFallback = true; - if (xrefChecker.isPresent()) + // remove any params from the given MIME type string + mimetype = MIMEType.removeParams(mimetype); + + // report any MIME type mismatch as a warning + if (resourceMimetype != null && !resourceMimetype.equals(mimetype)) { - String posterMimeType = xrefChecker.get().getMimeType(posterURL); - if (posterMimeType != null - && !OPFChecker.isBlessedImageType(posterMimeType, EPUBVersion.VERSION_3)) - { - report.message(MessageId.MED_001, location()); - } + report.message(MessageId.OPF_013, location(), context.relativize(resource), mimetype, + resourceMimetype); } + // return the given MIME type (without parameters) + return mimetype; } } + protected void processVideo() + { + + URL posterURL = checkResourceURL(currentElement().getAttribute("poster")); + registerReference(posterURL, Type.IMAGE); + + } + @Override protected void processHyperlink(URL href) { @@ -552,140 +670,77 @@ protected void processHyperlink(URL href) report.message(MessageId.RSC_029, location()); return; } - if (inRegionBasedNav && xrefChecker.isPresent()) + if (inRegionBasedNav) { - xrefChecker.get().registerReference(href, XRefChecker.Type.REGION_BASED_NAV, location()); + registerReference(href, Reference.Type.REGION_BASED_NAV); } } - protected URL processSrc(String elementName, String src) + protected URL checkResourceURL(String src) { - if (src != null) - { - src = src.trim(); - if (src.equals("")) - { - report.message(MessageId.HTM_008, location().context(elementName)); - } - } - - if (src == null) + if (src == null) return null; + // check that the URL is not empty + // TODO this should be moved to a schema check + if (src.trim().isEmpty()) { + report.message(MessageId.HTM_008, location()); return null; } + // parse and check the URL URL url = checkURL(src); - if (url != null) + // the `remote-resources` property MUST be set if a remote resource is + // referenced + if (context.isRemote(url)) { - String srcMimeType = null; - if ("data".equals(url.scheme())) - { - Matcher matcher = DATA_URI_PATTERN.matcher(url.toString()); - matcher.matches(); - srcMimeType = matcher.group(1); - } - else - { - if (context.isRemote(url)) - { - requiredProperties.add(ITEM_PROPERTIES.REMOTE_RESOURCES); - } - - if (xrefChecker.isPresent()) - { - - XRefChecker.Type refType; - if ("audio".equals(elementName)) - { - refType = XRefChecker.Type.AUDIO; - } - else if ("video".equals(elementName)) - { - refType = XRefChecker.Type.VIDEO; - } - else - { - refType = XRefChecker.Type.GENERIC; - } - if (!"img".equals(elementName)) // img already registered in super - // class - { - xrefChecker.get().registerReference(url, refType, location()); - } - - srcMimeType = xrefChecker.get().getMimeType(url); - } - } + requiredProperties.add(ITEM_PROPERTIES.REMOTE_RESOURCES); + } - if (srcMimeType != null) + // get the resource MIME type + String mimeType = context.getMimeType(url); + if (mimeType != null) + { + // the `svg` property MAY be set if an SVG resource is referenced in HTML + if (MIMEType.SVG.is(mimeType) && !MIMEType.SVG.is(context.mimeType)) { - if (!context.mimeType.equals("image/svg+xml") && srcMimeType.equals("image/svg+xml")) - { - allowedProperties.add(ITEM_PROPERTIES.SVG); - } - - if ((inAudio || inVideo || imbricatedObjects > 0 || imbricatedCanvases > 0) - && OPFChecker30.isCoreMediaType(srcMimeType) && !elementName.equals("track")) - { - hasValidFallback = true; - } + allowedProperties.add(ITEM_PROPERTIES.SVG); } } return url; - } - protected void processObject() + @Override + protected void checkObject() { - imbricatedObjects++; - - XMLElement e = currentElement(); + // do nothing, we check this in the closing tag + } - String type = e.getAttribute("type"); - String data = e.getAttribute("data"); + protected void endObject() + { + XMLElement elem = currentElement(); - if (data != null) - { - URL objectURL = processSrc(currentElement().getName(), data); + // check the object resource + URL url = checkResourceURL(elem.getAttribute("data")); - if (objectURL != null) - { + // check the MIME type declared for this object + checkMimetypeMatches(url, elem.getAttribute("type")); - if (type != null && data != null && xrefChecker.isPresent() - && !type.equals(xrefChecker.get().getMimeType(objectURL))) - { - String context = " (boolean) v || isPalpable()); } } @@ -988,4 +1069,5 @@ protected void checkLink() } } } + } diff --git a/src/main/java/com/adobe/epubcheck/overlay/OverlayHandler.java b/src/main/java/com/adobe/epubcheck/overlay/OverlayHandler.java index afba45b3d..748811d45 100644 --- a/src/main/java/com/adobe/epubcheck/overlay/OverlayHandler.java +++ b/src/main/java/com/adobe/epubcheck/overlay/OverlayHandler.java @@ -5,13 +5,13 @@ import java.util.Map; import java.util.Set; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.core.references.Reference; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBLocation; import com.adobe.epubcheck.messages.MessageId; import com.adobe.epubcheck.opf.OPFChecker30; import com.adobe.epubcheck.opf.ValidationContext; -import com.adobe.epubcheck.opf.XRefChecker.Type; import com.adobe.epubcheck.util.EpubConstants; import com.adobe.epubcheck.vocab.AggregateVocab; import com.adobe.epubcheck.vocab.PackageVocabs; @@ -141,21 +141,14 @@ private void checkType(String type) private void processTextSrc() { URL url = checkURL(currentElement().getAttribute("src")); - if (url != null && context.xrefChecker.isPresent()) - { - processContentDocumentLink(url); - } - + processContentDocumentLink(url); } private void processTextRef() { URL url = checkURL( currentElement().getAttributeNS(EpubConstants.EpubTypeNamespaceUri, "textref")); - if (url != null && context.xrefChecker.isPresent()) - { - processContentDocumentLink(url); - } + processContentDocumentLink(url); } private void processAudioSrc() @@ -170,18 +163,18 @@ private void processAudioSrc() url = URLUtils.docURL(url); } - if (url != null && context.xrefChecker.isPresent()) + if (url != null && context.container.isPresent()) { // check that the audio type is a core media type resource - String mimeType = context.xrefChecker.get().getMimeType(url); + String mimeType = context.resourceRegistry.get().getMimeType(url); if (mimeType != null && !OPFChecker30.isBlessedAudioType(mimeType)) { report.message(MessageId.MED_005, location(), context.relativize(url), mimeType); } // register the URL for cross-reference checking - context.xrefChecker.get().registerReference(url, Type.AUDIO, location()); + registerReference(url, Reference.Type.AUDIO, true); // if needed, register we found a remote resource if (context.isRemote(url)) @@ -193,16 +186,17 @@ private void processAudioSrc() private void processContentDocumentLink(URL url) { - assert url != null; - assert context.xrefChecker.isPresent(); - assert context.overlayTextChecker.isPresent(); - URL documentURL = URLUtils.docURL(url); - if (!context.overlayTextChecker.get().registerOverlay(documentURL, - context.opfItem.get().getId())) + if (url != null && context.container.isPresent()) { - report.message(MessageId.MED_011, location(), context.relativize(url)); + assert context.overlayTextChecker.isPresent(); + URL documentURL = URLUtils.docURL(url); + if (!context.overlayTextChecker.get().registerOverlay(documentURL, + context.opfItem.get().getId())) + { + report.message(MessageId.MED_011, location(), context.relativize(url)); + } + registerReference(url, Reference.Type.OVERLAY_TEXT_LINK); } - context.xrefChecker.get().registerReference(url, Type.OVERLAY_TEXT_LINK, location()); } private void processGlobalAttrs() diff --git a/src/main/java/com/adobe/epubcheck/tool/EpubChecker.java b/src/main/java/com/adobe/epubcheck/tool/EpubChecker.java index 3c4dc8c75..c77619779 100644 --- a/src/main/java/com/adobe/epubcheck/tool/EpubChecker.java +++ b/src/main/java/com/adobe/epubcheck/tool/EpubChecker.java @@ -35,7 +35,7 @@ import java.util.regex.Pattern; import org.w3c.epubcheck.core.Checker; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EPUBProfile; import com.adobe.epubcheck.api.EpubCheck; diff --git a/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java b/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java index 243702a56..d043f5633 100644 --- a/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java +++ b/src/main/java/com/adobe/epubcheck/xml/handlers/BaseURLHandler.java @@ -2,7 +2,7 @@ import javax.xml.XMLConstants; -import org.w3c.epubcheck.url.URLChecker; +import org.w3c.epubcheck.core.references.URLChecker; import org.xml.sax.Attributes; import com.adobe.epubcheck.opf.ValidationContext; diff --git a/src/main/java/com/adobe/epubcheck/xml/handlers/XMLHandler.java b/src/main/java/com/adobe/epubcheck/xml/handlers/XMLHandler.java index 3370723d2..a75b83f5b 100755 --- a/src/main/java/com/adobe/epubcheck/xml/handlers/XMLHandler.java +++ b/src/main/java/com/adobe/epubcheck/xml/handlers/XMLHandler.java @@ -21,6 +21,8 @@ */ package com.adobe.epubcheck.xml.handlers; +import org.w3c.epubcheck.core.references.ReferenceRegistry; +import org.w3c.epubcheck.core.references.Reference; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.ext.Locator2; @@ -152,4 +154,52 @@ else if (!"1.0".equals(version)) } } + /** + * Convenience method to register a reference to the + * {@link ReferenceRegistry}. + * + *

+ * Does nothing if the context has no registry (single-file validaiton) or if + * the URL is null; else calls + * {@link ReferenceRegistry#registerReference(URL, Reference.Type, EPUBLocation)} + *

+ * + * @param url + * the URL of to register + * @param type + * the type of the reference + */ + protected final void registerReference(URL url, Reference.Type type) + { + registerReference(url, type, false); + } + + /** + * Convenience method to register a reference to the + * {@link ReferenceRegistry}. + * + *

+ * Does nothing if the context has no registry (single-file validaiton) or if + * the URL is null; else calls + * {@link ReferenceRegistry#registerReference(URL, Reference.Type, EPUBLocation, boolean)} + *

+ * + * @param url + * the URL of to register + * @param type + * the type of the reference + * @param hasIntrinsicFallback + * if the reference has an intrinsic fallback + */ + protected final void registerReference(URL url, Reference.Type type, + boolean hasIntrinsicFallback) + { + if (url == null) return; + if (context.referenceRegistry.isPresent()) + { + context.referenceRegistry.get().registerReference(url, type, location(), + hasIntrinsicFallback); + } + } + } \ No newline at end of file diff --git a/src/main/java/org/w3c/epubcheck/constants/MIMEType.java b/src/main/java/org/w3c/epubcheck/constants/MIMEType.java index ced86f640..ee078a0eb 100644 --- a/src/main/java/org/w3c/epubcheck/constants/MIMEType.java +++ b/src/main/java/org/w3c/epubcheck/constants/MIMEType.java @@ -21,7 +21,7 @@ public enum MIMEType SMIL("application/smil+xml"), SVG("image/svg+xml"), XHTML("application/xhtml+xml"), - OTHER(""); + OTHER("*/*"); private static final Map ENUM_MAP; @@ -56,4 +56,36 @@ public static MIMEType get(String name) { return (name != null) ? ENUM_MAP.getOrDefault(name.toLowerCase(Locale.ROOT), OTHER) : OTHER; } + + /** + * Removes the (optional) parameters from a valid MIME type string, as defined + * in the MIME + * Sniffing standard. In other words, this returns the essence + * ("type/subtype") of + * the MIME type described in the given string. + * + *

+ * For instance, calling this method on MIME type string + * {@code "audio/ogg; codecs=speex"} will return the string + * {@code "audio/ogg"}. + *

+ * + *

+ * Note: no validation is performed on the MIME type string itself; it + * simply returns the substring before the first ";" + * character. + *

+ * + * @param typeString + * a MIME type string + * @return a MIME type string with any parameter removed + */ + public static String removeParams(String typeString) + { + if (typeString == null) return null; + typeString = typeString.trim(); + int semicolon = typeString.indexOf(';'); + return (semicolon > 0) ? typeString.substring(0, semicolon) : typeString; + } } diff --git a/src/main/java/org/w3c/epubcheck/core/CheckAbortException.java b/src/main/java/org/w3c/epubcheck/core/CheckAbortException.java new file mode 100644 index 000000000..82ccc532f --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/CheckAbortException.java @@ -0,0 +1,9 @@ +package org.w3c.epubcheck.core; + +public class CheckAbortException extends Exception +{ + + private static final long serialVersionUID = 8944219124382959771L; + + +} diff --git a/src/main/java/org/w3c/epubcheck/core/references/Reference.java b/src/main/java/org/w3c/epubcheck/core/references/Reference.java new file mode 100644 index 000000000..aa30babce --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/references/Reference.java @@ -0,0 +1,66 @@ +package org.w3c.epubcheck.core.references; + +import com.adobe.epubcheck.api.EPUBLocation; +import com.google.common.base.Preconditions; + +import io.mola.galimatias.GalimatiasParseException; +import io.mola.galimatias.URL; + +public final class Reference +{ + public static enum Type + { + // Linked resources + LINK, + // Publication resources + GENERIC, + STYLESHEET, + HYPERLINK, + FONT, + IMAGE, + AUDIO, + VIDEO, + TRACK, + CITE, + // Others, used for internal checks + SVG_PAINT, + SVG_CLIP_PATH, + SVG_SYMBOL, + REGION_BASED_NAV, + SEARCH_KEY, + NAV_TOC_LINK, + NAV_PAGELIST_LINK, + OVERLAY_TEXT_LINK, + } + + public final URL url; + public final URL targetResource; + public final EPUBLocation location; + public final Type type; + public final boolean hasIntrinsicFallback; + + public Reference(URL url, Type type, EPUBLocation location, boolean hasIntrinsicFallback) + { + Preconditions.checkArgument(url != null); + Preconditions.checkArgument(type != null); + Preconditions.checkArgument(location != null); + try + { + this.url = url; + this.type = type; + this.location = location; + this.targetResource = url.withFragment(null); + this.hasIntrinsicFallback = hasIntrinsicFallback; + } catch (GalimatiasParseException e) + { + throw new AssertionError(e); + } + } + + @Override + public String toString() + { + return url.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/w3c/epubcheck/core/references/ReferenceRegistry.java b/src/main/java/org/w3c/epubcheck/core/references/ReferenceRegistry.java new file mode 100644 index 000000000..7c7662d06 --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/references/ReferenceRegistry.java @@ -0,0 +1,88 @@ +package org.w3c.epubcheck.core.references; + +import java.util.List; + +import org.w3c.epubcheck.core.references.Reference.Type; +import org.w3c.epubcheck.util.url.URLUtils; + +import com.adobe.epubcheck.api.EPUBLocation; +import com.adobe.epubcheck.ocf.OCFContainer; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import io.mola.galimatias.GalimatiasParseException; +import io.mola.galimatias.URL; + +public final class ReferenceRegistry +{ + + private final OCFContainer container; + private final ImmutableList.Builder references = ImmutableList.builder(); + private final ResourceRegistry resourceRegistry; + + public ReferenceRegistry(OCFContainer container, ResourceRegistry resourceRegistry) + { + Preconditions.checkArgument(container != null); + Preconditions.checkArgument(resourceRegistry != null); + this.container = container; + this.resourceRegistry = resourceRegistry; + } + + public List asList() + { + return references.build(); + } + + public boolean isReferenced(URL resource) + { + return references.build().stream().anyMatch(ref -> ref.url.equals(resource)); + } + + public void registerReference(URL url, Type type, EPUBLocation location) + { + registerReference(url, type, location, false); + } + + public void registerReference(URL url, Type type, EPUBLocation location, + boolean hasIntrinsicFallback) + { + if (url == null) return; + + // Remove query component of local URLs + if (url.query() != null && !container.isRemote(url)) + { + try + { + url = url.withQuery(null); + } catch (GalimatiasParseException e) + { + new AssertionError("could not remove URL query"); + } + } + + // Create and register a new reference + Reference xref = new Reference(url, type, location, hasIntrinsicFallback); + references.add(xref); + + // If it is a data URL, also register a new resource + // as the URL may not have been listed in the manifest + if ("data".equals(url.scheme())) + { + resourceRegistry.registerResource(url, URLUtils.getDataURLType(url)); + } + } + + /** + * Returns if any references to the given resources were registered. + * + * @param resousrce + * the URL to a publication resource + * @return true iff a reference to the given resource was found + */ + public boolean hasReferencesTo(URL resource) + { + return (resource != null) + && references.build().stream().anyMatch(ref -> resource.equals(ref.targetResource)); + } + +} diff --git a/src/main/java/org/w3c/epubcheck/core/references/Resource.java b/src/main/java/org/w3c/epubcheck/core/references/Resource.java new file mode 100644 index 000000000..ea81b1166 --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/references/Resource.java @@ -0,0 +1,111 @@ +package org.w3c.epubcheck.core.references; + +import com.adobe.epubcheck.opf.OPFItem; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; + +import io.mola.galimatias.URL; + +public class Resource +{ + private static final class Builder + { + + private URL url; + private OPFItem item = null; + public String mimetype; + + public Resource.Builder url(URL url) + { + this.url = url; + return this; + } + + public Resource.Builder item(OPFItem item) + { + this.url = item.getURL(); + this.item = item; + this.mimetype = item.getMimeType(); + return this; + } + + public Resource.Builder mimetype(String mimetype) + { + this.mimetype = mimetype; + return this; + } + + public Resource build() + { + return new Resource(this); + } + } + + public static Resource fromItem(OPFItem item) + { + return new Builder().item(item).build(); + } + + public static Resource fromURL(URL url, String mimetype) + { + return new Builder().url(url).mimetype(mimetype).build(); + } + + private final URL url; + private final String mimetype; + private final Optional item; + + private Resource(Resource.Builder builder) + { + Preconditions.checkState(builder.url != null, "A URL or OPF Item must be provided"); + Preconditions.checkState(builder.mimetype != null, "A MIME type must be provided"); + Preconditions + .checkState(builder.item == null || builder.item.getMimeType().equals(builder.mimetype)); + this.url = builder.url; + this.item = Optional.fromNullable(builder.item); + this.mimetype = builder.mimetype; + + } + + public String getMimeType() + { + return mimetype; + } + + public boolean hasItem() + { + return item.isPresent(); + } + + public OPFItem getItem() + { + return item.orNull(); + } + + public URL getURL() + { + return url; + } + + public boolean hasCoreMediaTypeFallback() + { + return item.isPresent() && item.get().hasCoreMediaTypeFallback(); + } + + public boolean hasContentDocumentFallback() + { + // TODO Auto-generated method stub + return item.isPresent() && item.get().hasContentDocumentFallback(); + } + + public boolean isInSpine() + { + return item.isPresent() && item.get().isInSpine(); + } + + @Override + public String toString() + { + return url.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/w3c/epubcheck/core/references/ResourceReferencesChecker.java b/src/main/java/org/w3c/epubcheck/core/references/ResourceReferencesChecker.java new file mode 100755 index 000000000..8169de7a1 --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/references/ResourceReferencesChecker.java @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2007 Adobe Systems Incorporated + * + * 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. + * + */ + +package org.w3c.epubcheck.core.references; + +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Locale; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; + +import org.w3c.epubcheck.constants.MIMEType; +import org.w3c.epubcheck.core.CheckAbortException; +import org.w3c.epubcheck.util.url.URLFragment; + +import com.adobe.epubcheck.api.LocalizableReport; +import com.adobe.epubcheck.api.Report; +import com.adobe.epubcheck.messages.LocalizedMessages; +import com.adobe.epubcheck.messages.MessageId; +import com.adobe.epubcheck.ocf.OCFContainer; +import com.adobe.epubcheck.opf.OPFChecker; +import com.adobe.epubcheck.opf.OPFChecker30; +import com.adobe.epubcheck.opf.ValidationContext; +import com.adobe.epubcheck.util.EPUBVersion; +import com.google.common.base.Preconditions; + +import io.mola.galimatias.URL; + +public class ResourceReferencesChecker +{ + + private final Report report; + private final OCFContainer container; + private final EPUBVersion version; + private final ReferenceRegistry referenceRegistry; + private final ResourceRegistry resourceRegistry; + private Locale locale; + + private Set undeclared; + + public ResourceReferencesChecker(ValidationContext context) + { + Preconditions.checkArgument(context.container.isPresent()); + Preconditions.checkArgument(context.resourceRegistry.isPresent()); + Preconditions.checkArgument(context.referenceRegistry.isPresent()); + this.report = context.report; + this.container = context.container.get(); + this.version = context.version; + this.referenceRegistry = context.referenceRegistry.get(); + this.resourceRegistry = context.resourceRegistry.get(); + this.locale = (report instanceof LocalizableReport) + ? ((LocalizableReport) report).getLocale() + : Locale.ENGLISH; + } + + public void check() + { + undeclared = new HashSet(); + Queue tocLinks = new LinkedList<>(); + Queue pageListLinks = new LinkedList<>(); + Queue overlayLinks = new LinkedList<>(); + for (Reference reference : referenceRegistry.asList()) + { + switch (reference.type) + { + case REGION_BASED_NAV: + checkRegionBasedNav(reference); + break; + case NAV_TOC_LINK: + tocLinks.add(reference); + break; + case NAV_PAGELIST_LINK: + pageListLinks.add(reference); + break; + case OVERLAY_TEXT_LINK: + overlayLinks.add(reference); + checkReference(reference); + break; + default: + checkReference(reference); + break; + } + } + checkReadingOrder(tocLinks); + checkReadingOrder(overlayLinks); + } + + private void checkReference(Reference reference) + { + // Retrieve the target resource + Optional targetResource = resourceRegistry.getResource(reference.targetResource); + try + { + // Check remote resources + if (container.isRemote(reference.url)) + { + checkRemoteReference(reference, targetResource); + } + + // Check undeclared resources + if (!targetResource.isPresent()) + { + checkUndeclaredReference(reference); + } + assert targetResource.isPresent(); + + // Check fallbacks + checkFallbacks(reference, targetResource.get()); + + // Parse the URL fragment + URLFragment fragment = URLFragment.parse(reference.url, targetResource.get().getMimeType()); + + // Check reference type + checkReferenceType(reference, targetResource.get(), fragment); + + // Fragment integrity checks + checkFragment(reference, targetResource.get(), fragment); + } catch (CheckAbortException e) + { + // an error was reported, abort early + } + } + + private void checkFragment(Reference reference, Resource targetResource, URLFragment fragment) + throws CheckAbortException + { + String targetMimetype = targetResource.getMimeType(); + if (fragment.exists() && !fragment.isEmpty()) + { + // Check media overlays requirements + if (reference.type == Reference.Type.OVERLAY_TEXT_LINK) + { + // Check that references to XHTML indicate an element by ID + if (MIMEType.XHTML.is(targetMimetype) && fragment.getId().isEmpty()) + { + report.message(MessageId.MED_017, reference.location, fragment.toString()); + } + // Check that references to SVG use a SVG fragment identifier + else if (MIMEType.SVG.is(targetMimetype) && !fragment.isValid()) + { + report.message(MessageId.MED_018, reference.location, fragment.toString()); + } + } + + // Check ID-based fragments + // Other fragment types (e.g. EPUB CFI) are not currently supported + if (!fragment.getId().isEmpty() && !container.isRemote(reference.targetResource)) + { + Reference.Type targetIDType = resourceRegistry.getIDType(fragment.getId(), + targetResource); + if (targetIDType == null) + { + report.message(MessageId.RSC_012, reference.location.context(reference.url.toString())); + throw new CheckAbortException(); + } + switch (reference.type) + { + case SVG_PAINT: + case SVG_CLIP_PATH: + if (targetIDType != reference.type) + { + report.message(MessageId.RSC_014, reference.location.context(reference.url.toString())); + throw new CheckAbortException(); + } + break; + case SVG_SYMBOL: + case HYPERLINK: + case OVERLAY_TEXT_LINK: + if (targetIDType != reference.type && targetIDType != Reference.Type.GENERIC) + { + report.message(MessageId.RSC_014, reference.location.context(reference.url.toString())); + throw new CheckAbortException(); + } + break; + default: + break; + } + } + } + + } + + private void checkReferenceType(Reference reference, Resource targetResource, + URLFragment fragment) + throws CheckAbortException + { + String targetMimetype = targetResource.getMimeType(); + switch (reference.type) + { + case HYPERLINK: + + if ("epubcfi".equals(fragment.getScheme())) + { + break; // EPUB CFI is not supported + } + // if mimeType is null, we should have reported an error already + if (!OPFChecker.isBlessedItemType(targetMimetype, version) + && !OPFChecker.isDeprecatedBlessedItemType(targetMimetype) + && !targetResource.hasContentDocumentFallback()) + { + report.message(MessageId.RSC_010, + reference.location.context(container.relativize(reference.url))); + throw new CheckAbortException(); + } + if (/* !res.mimeType.equals("font/opentype") && */!targetResource.isInSpine()) + { + report.message(MessageId.RSC_011, + reference.location.context(container.relativize(reference.url))); + throw new CheckAbortException(); + } + break; + case IMAGE: + if ("epubcfi".equals(fragment.getScheme())) + { + break; // EPUB CFI is not supported + } + if (fragment.exists() && !MIMEType.SVG.is(targetMimetype)) + { + report.message(MessageId.RSC_009, + reference.location.context(container.relativize(reference.url))); + throw new CheckAbortException(); + } + break; + case OVERLAY_TEXT_LINK: + if (!OPFChecker.isBlessedItemType(targetMimetype, version)) + { + report.message(MessageId.RSC_010, + reference.location.context(container.relativize(reference.url))); + throw new CheckAbortException(); + } + break; + case SEARCH_KEY: + if ((!fragment.exists() || !"epubcfi".equals(fragment.getScheme())) + && !targetResource.isInSpine()) + { + report.message(MessageId.RSC_021, reference.location, + container.relativize(reference.targetResource)); + throw new CheckAbortException(); + } + break; + case STYLESHEET: + if (fragment.exists()) + { + report.message(MessageId.RSC_013, + reference.location.context(container.relativize(reference.url))); + throw new CheckAbortException(); + } + break; + case SVG_CLIP_PATH: + case SVG_PAINT: + case SVG_SYMBOL: + if (!fragment.exists()) + { + report.message(MessageId.RSC_015, reference.location.context(reference.url)); + throw new CheckAbortException(); + } + break; + default: + break; + } + + } + + private void checkFallbacks(Reference reference, Resource targetResource) + { + String targetMimetype = targetResource.getMimeType(); + switch (reference.type) + { + case IMAGE: + case AUDIO: + case VIDEO: + case GENERIC: + if (!reference.hasIntrinsicFallback && !OPFChecker30.isCoreMediaType(targetMimetype) + && !targetResource.hasCoreMediaTypeFallback()) + { + report.message(MessageId.RSC_032, reference.location, + container.relativize(reference.targetResource), targetMimetype); + } + break; + default: + break; + } + + } + + private void checkUndeclaredReference(Reference reference) + throws CheckAbortException + { + assert !resourceRegistry.getResource(reference.targetResource).isPresent(); + + // Report references to missing local resources + if (!container.contains(reference.url) && !container.isRemote(reference.url)) + { + // only as a WARNING for 'link' references in EPUB 3 + if (version == EPUBVersion.VERSION_3 && reference.type == Reference.Type.LINK) + { + report.message(MessageId.RSC_007w, reference.location, + container.relativize(reference.targetResource)); + } + // by default as an ERROR + else + { + report.message(MessageId.RSC_007, reference.location, + container.relativize(reference.targetResource)); + } + } + // Report undeclared Publication Resources (once) + else if (!undeclared.contains(reference.targetResource) + // links and remote hyperlinks are not Publication Resources + && !(reference.type == Reference.Type.LINK + || container.isRemote(reference.targetResource) + && reference.type == Reference.Type.HYPERLINK)) + { + undeclared.add(reference.targetResource); + report.message(MessageId.RSC_008, reference.location, + container.relativize(reference.targetResource)); + } + throw new CheckAbortException(); + + } + + private void checkRemoteReference(Reference reference, Optional targetResource) + throws CheckAbortException + { + assert container.isRemote(reference.url); + + // Check if the remote reference is allowed + if (// remote links and hyperlinks are not Publication Resources + !EnumSet.of(Reference.Type.LINK, Reference.Type.HYPERLINK).contains(reference.type) + // spine items are checked in OPFChecker30 + && !(version == EPUBVersion.VERSION_3 && targetResource.isPresent() + && targetResource.get().isInSpine()) + // audio, video, and fonts can be remote resources in EPUB 3 + && !(version == EPUBVersion.VERSION_3 && (targetResource.isPresent() + // if the item is declared, check its mime type + && (OPFChecker30.isAudioType(targetResource.get().getMimeType()) + || OPFChecker30.isVideoType(targetResource.get().getMimeType()) + || OPFChecker30.isFontType(targetResource.get().getMimeType())) + // else, check if the reference is a type allowing remote + // resources + || reference.type == Reference.Type.FONT + || reference.type == Reference.Type.AUDIO + || reference.type == Reference.Type.VIDEO))) + { + report.message(MessageId.RSC_006, reference.location, reference.url); + throw new CheckAbortException(); + } + + // Check if the remote resource is using HTTPS + else if (version == EPUBVersion.VERSION_3 + && !EnumSet.of(Reference.Type.LINK, Reference.Type.HYPERLINK) + .contains(reference.type) + && !"https".equals(reference.url.scheme()) + // file URLs are disallowed and reported elsewhere + && !"file".equals(reference.url.scheme())) + { + report.message(MessageId.RSC_031, reference.location, reference.url); + } + + } + + private void checkRegionBasedNav(Reference ref) + { + Preconditions.checkArgument(ref.type == Reference.Type.REGION_BASED_NAV); + Optional optionalResource = resourceRegistry.getResource(ref.targetResource); + // abort early if the link target is not a spine item (checked elsewhere) + if (!optionalResource.isPresent() || !optionalResource.get().hasItem() + || !optionalResource.get().getItem().isFixedLayout()) + { + report.message(MessageId.NAV_009, ref.location); + } + } + + private void checkReadingOrder(Queue references) + { + int lastSpinePosition = -1; + int lastAnchorPosition = -1; + while (!references.isEmpty()) + { + Reference ref = references.poll(); + Preconditions + .checkArgument(ref.type == Reference.Type.NAV_PAGELIST_LINK + || ref.type == Reference.Type.NAV_TOC_LINK + || ref.type == Reference.Type.OVERLAY_TEXT_LINK); + + // Retrieve the target resource + Optional optionalTarget = resourceRegistry.getResource(ref.targetResource); + // abort early if the link target is not a spine item (checked elsewhere) + if (!optionalTarget.isPresent() || !optionalTarget.get().hasItem() + || !optionalTarget.get().getItem().isInSpine()) + { + continue; + } + Resource res = optionalTarget.get(); + + // check that the link is in spine order + int targetSpinePosition = res.getItem().getSpinePosition(); + if (targetSpinePosition < lastSpinePosition) + { + String orderContext = LocalizedMessages.getInstance(locale).getSuggestion(MessageId.NAV_011, + "spine"); + + if (ref.type == Reference.Type.OVERLAY_TEXT_LINK) + { + report.message(MessageId.MED_015, ref.location, container.relativize(ref.url), + orderContext); + } + else + { + report.message(MessageId.NAV_011, ref.location, + (ref.type == Reference.Type.NAV_TOC_LINK) ? "toc" : "page-list", + container.relativize(ref.url), + orderContext); + } + lastSpinePosition = targetSpinePosition; + lastAnchorPosition = -1; + } + else + { + + // if new spine item, reset last positions + if (targetSpinePosition > lastSpinePosition) + { + lastSpinePosition = targetSpinePosition; + lastAnchorPosition = -1; + } + + // check that the fragment is in document order + URLFragment fragment = URLFragment.parse(ref.url, res.getMimeType()); + int targetAnchorPosition = resourceRegistry.getIDPosition(fragment.getId(), res); + if (targetAnchorPosition < lastAnchorPosition) + { + String orderContext = LocalizedMessages.getInstance(locale).getSuggestion( + MessageId.NAV_011, + "document"); + if (ref.type == Reference.Type.OVERLAY_TEXT_LINK) + { + report.message(MessageId.MED_015, ref.location, container.relativize(ref.url), + orderContext); + } + else + { + report.message(MessageId.NAV_011, ref.location, + (ref.type == Reference.Type.NAV_TOC_LINK) ? "toc" : "page-list", + container.relativize(ref.url), + orderContext); + } + } + lastAnchorPosition = targetAnchorPosition; + } + } + + } +} diff --git a/src/main/java/org/w3c/epubcheck/core/references/ResourceRegistry.java b/src/main/java/org/w3c/epubcheck/core/references/ResourceRegistry.java new file mode 100644 index 000000000..6a3d04b0c --- /dev/null +++ b/src/main/java/org/w3c/epubcheck/core/references/ResourceRegistry.java @@ -0,0 +1,111 @@ +package org.w3c.epubcheck.core.references; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.w3c.epubcheck.core.references.Reference.Type; + +import com.adobe.epubcheck.opf.OPFItem; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; + +import io.mola.galimatias.URL; + +public final class ResourceRegistry +{ + + private Map resources = new HashMap<>(); + private Map> ids = new HashMap<>(); + private Table idTypes = HashBasedTable.create(); + + public void registerID(String id, Type type, URL resourceURL) + { + if (id == null) return; + Preconditions.checkArgument(resourceURL != null && resources.containsKey(resourceURL), + "resource not registered"); + synchronized (ids) + { + ids.putIfAbsent(resourceURL, new LinkedList<>()); + List resourceIDs = ids.get(resourceURL); + // Note: duplicate IDs are checked in schematron + if (!resourceIDs.contains(id)) + { + resourceIDs.add(id); + idTypes.put(resourceURL, id, type); + } + } + } + + /** + * Returns the position of the given ID in the document represented by this + * resource. + * + * @param resource + * + * @return {@code -1} if the ID wasn't found in the document, or {@code 0} if + * the given ID is {@code null} or an empty string, or the 1-based + * position of the ID otherwise. + */ + public int getIDPosition(String id, Resource resource) + { + Preconditions.checkArgument(resource != null); + if (id == null || id.trim().isEmpty()) return 0; + int position = ids.get(resource.getURL()).indexOf(id); + return (position == -1) ? -1 : position + 1; + } + + public Type getIDType(String id, Resource resource) + { + Preconditions.checkArgument(resource != null); + return idTypes.get(resource.getURL(), id); + } + + public void registerResource(OPFItem item) + { + Preconditions.checkArgument(item != null); + // Note: Duplicate manifest items are already checked in OPFChecker. + if (!resources.containsKey(item.getURL())) + { + + resources.put(item.getURL(), Resource.fromItem(item)); + } + } + + public void registerResource(URL url, String mimetype) + { + Preconditions.checkArgument(url != null); + if (!resources.containsKey(url)) + { + resources.put(url, Resource.fromURL(url, mimetype)); + } + } + + public String getMimeType(URL resource) + { + return resources.containsKey(resource) ? resources.get(resource).getMimeType() : null; + } + + /** + * Returns an {@link Optional} containing the Package Document item for the + * given Publication Resource path, or {@link Optional#absent()} if no + * resource has been registered for the given path. + */ + public Optional getOPFItem(URL url) + { + return Optional.ofNullable(resources.get(url)).map(r -> r.getItem()); + } + + /** + * Returns an {@link Optional} containing the Package Document item for the + * given Publication Resource path, or {@link Optional#absent()} if no + * resource has been registered for the given path. + */ + public Optional getResource(URL url) + { + return Optional.ofNullable(resources.get(url)); + } +} diff --git a/src/main/java/org/w3c/epubcheck/url/URLChecker.java b/src/main/java/org/w3c/epubcheck/core/references/URLChecker.java similarity index 99% rename from src/main/java/org/w3c/epubcheck/url/URLChecker.java rename to src/main/java/org/w3c/epubcheck/core/references/URLChecker.java index 3a4b18907..c1527b24f 100644 --- a/src/main/java/org/w3c/epubcheck/url/URLChecker.java +++ b/src/main/java/org/w3c/epubcheck/core/references/URLChecker.java @@ -1,4 +1,4 @@ -package org.w3c.epubcheck.url; +package org.w3c.epubcheck.core.references; import java.net.URI; diff --git a/src/main/java/org/w3c/epubcheck/url/Reference.java b/src/main/java/org/w3c/epubcheck/url/Reference.java deleted file mode 100644 index 9ab8c99c4..000000000 --- a/src/main/java/org/w3c/epubcheck/url/Reference.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.w3c.epubcheck.url; - -import com.adobe.epubcheck.api.EPUBLocation; - -import io.mola.galimatias.URL; - -public final class Reference -{ - - public static enum Type - { - GENERIC, - FONT, - HYPERLINK, - LINK, - IMAGE, - OBJECT, - STYLESHEET, - AUDIO, - VIDEO, - SVG_PAINT, - SVG_CLIP_PATH, - SVG_SYMBOL, - REGION_BASED_NAV, - SEARCH_KEY, - NAV_TOC_LINK, - NAV_PAGELIST_LINK, - OVERLAY_TEXT_LINK, - PICTURE_SOURCE, - PICTURE_SOURCE_FOREIGN; - }; - - public final URL url; - public final EPUBLocation location; - public final Type type; - - public Reference(URL url, EPUBLocation location, Type type) - { - this.url = url; - this.location = location; - this.type = type; - } - -} diff --git a/src/main/java/org/w3c/epubcheck/url/ReferencesChecker.java b/src/main/java/org/w3c/epubcheck/url/ReferencesChecker.java deleted file mode 100644 index f028f6235..000000000 --- a/src/main/java/org/w3c/epubcheck/url/ReferencesChecker.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.w3c.epubcheck.url; - -import java.util.HashSet; -import java.util.Set; - -import org.w3c.epubcheck.core.Checker; - -import com.adobe.epubcheck.opf.ValidationContext; - -import io.mola.galimatias.URL; - -public class ReferencesChecker implements Checker -{ - - - private final Iterable references; - private final ValidationContext context; - - public ReferencesChecker(ValidationContext context, Iterable references) - { - this.context = context; - this.references = references; - } - - @Override - public void check() - { - // TODO init - Set undeclared = new HashSet<>(); - for (Reference reference : references) - { - // TODO add type-specific checks - checkReference(reference, undeclared); - } - // TODO post-loop checks - } - - private void checkReference(Reference reference, Set undeclared) - { -// // TODO get item matching reference -// OPFItem item = null; -// -// // TODO check remote resources -// if (reference.resource.isRemote()) -// // or if (container.isRemote(reference.url)) ??? -// { -// // if (allowed condition 1) { -// // // do nothing -// // } else if allowed condition 2() { -// // // do nothing -// // } else { -// //// report RSC_006 -// // } -// } -// -// // Check undeclared reference -// if (item == null) -// { -// // TODO introduce "notfound" set to check which resources have been tested -// if (!container.contains(reference.resource) && !reference.resource.isRemote()) -// { -// // if EPUB 3 and reference.type = link, report RSC_007w -// // else report RSC_007 -// } -// // TODO not sure "else" is required: -// // We can report both RSC-007 and RSC-008 for the same reference -// else if (true -// // if resource has not aleady been checked -// // and ref is not a link -// // and ref is not a remote hyperlink -// ) -// { -// // TODO report RSC-008 -// } -// return; -// } -// -// // Type-specific checks -// -// // Check fragment -// Preconditions.checkState(item != null); -// if (reference.url.fragment() != null) -// { -// -// } - } -} diff --git a/src/main/java/org/w3c/epubcheck/url/URLRegistry.java b/src/main/java/org/w3c/epubcheck/url/URLRegistry.java deleted file mode 100644 index d87b40fa0..000000000 --- a/src/main/java/org/w3c/epubcheck/url/URLRegistry.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.w3c.epubcheck.url; - -import com.adobe.epubcheck.api.EPUBLocation; -import com.adobe.epubcheck.opf.OPFItem; - -import io.mola.galimatias.URL; - -// TODO decide: -// - where to create this -// - where to store this (in validation context? in Publication?) -public class URLRegistry -{ - - // private final Set references = new LinkedHashSet<>(); - // - // public void registerReference(String string, EPUBLocation location, Type - // type) - // { - // // parse URL - // // TODO configure URL error handler (see Galimatias doc) - // URL url = URL.parse(location.getURL(), string); - // - // // check data URL - // if ("data".equals(url.scheme())) - // { - // // TODO check data URLs have fallback, issue #1239 - // } - // - // // TODO remove query string for in-container URLs - // - // // else register reference - // references.add(new Reference(url, location, type)); - // - // } - // - // Set getReferences() - // { - // return ImmutableSet.copyOf(references); - // } - - public boolean isReferenced(URL resource) - { - return false; - } - - public void registerID(String id, Reference.Type refType, URL documentURL, EPUBLocation location) - { - - } - - public void registerReference(String reference, Reference.Type refType, URL documentURL, - EPUBLocation location) - { - - } - - public void registerResource(OPFItem item) - { - - } -} diff --git a/src/main/java/org/w3c/epubcheck/url/URLFragment.java b/src/main/java/org/w3c/epubcheck/util/url/URLFragment.java similarity index 99% rename from src/main/java/org/w3c/epubcheck/url/URLFragment.java rename to src/main/java/org/w3c/epubcheck/util/url/URLFragment.java index 6baf498ee..4f30242f0 100644 --- a/src/main/java/org/w3c/epubcheck/url/URLFragment.java +++ b/src/main/java/org/w3c/epubcheck/util/url/URLFragment.java @@ -1,4 +1,4 @@ -package org.w3c.epubcheck.url; +package org.w3c.epubcheck.util.url; import java.util.Iterator; import java.util.Objects; diff --git a/src/main/java/org/w3c/epubcheck/url/URLUtils.java b/src/main/java/org/w3c/epubcheck/util/url/URLUtils.java similarity index 99% rename from src/main/java/org/w3c/epubcheck/url/URLUtils.java rename to src/main/java/org/w3c/epubcheck/util/url/URLUtils.java index 9c11d8bb5..526e869d5 100644 --- a/src/main/java/org/w3c/epubcheck/url/URLUtils.java +++ b/src/main/java/org/w3c/epubcheck/util/url/URLUtils.java @@ -1,4 +1,4 @@ -package org.w3c.epubcheck.url; +package org.w3c.epubcheck.util.url; import static io.mola.galimatias.URLUtils.UTF_8; import static io.mola.galimatias.URLUtils.isURLCodePoint; diff --git a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties index 93367222b..8a034dedf 100644 --- a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties +++ b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties @@ -47,7 +47,7 @@ CSS_006=CSS selector specifies fixed position. CSS_007=Font-face reference "%1$s" refers to non-standard font type "%2$s". CSS_008=An error occurred while parsing the CSS: %1$s. CSS_009=Use of certain CSS such as Columns, Transforms, Transitions, box-sizing or KeyFrames can cause pagination issues. -CSS_010=Stylesheet of type other than "text/css" must have a fallback. +CSS_010= CSS_011=Excessive number of css files. CSS_011_SUG=Consider merging CSS files to reduce the number of CSS files. CSS_012=Document links to multiple CSS files. @@ -78,7 +78,7 @@ HTM_004=Irregular DOCTYPE: found "%1$s", expected "%2$s". HTM_005=An external reference was found. HTM_006=An XHTML Named Entity was found. HTM_007=Empty or whitespace-only value of attribute ssml:ph. -HTM_008=The src attribute is required. +HTM_008=The resource URL must not be empty HTM_009=The DOCTYPE provided is obsolete or irregular and can be removed. HTM_010=Namespace uri "%1$s" was found. HTM_011=Entity is undeclared. @@ -128,13 +128,13 @@ HTM_057=Viewport "%1$s" value must be a positive number or the keyword "device-% HTM_058=HTML documents must be encoded in UTF-8, but UTF-16 was detected. #media -MED_001=Video poster must have core media image type. -MED_002=%1$s element doesn’t provide fallback. -MED_003=A manifest fallback must be provided for image resource "%1$s" of type "%2$s". +MED_001= +MED_002= +MED_003=Picture "img" elements must reference core media type resources, but found resource "%1$s" of type "%2$s". MED_004=Image file header may be corrupted. MED_005=Media Overlay audio reference %1$s to non-standard audio type %2$s found. MED_006=Some browsers do not support rendering SVG images which use a filename in the xlink:href property. -MED_007=Foreign resources can only be referenced from "source" elements with an explicit "type" attribute; found resource "%1$s" of foreign type "%2$s". +MED_007=Picture "source" elements must define a "type" attribute when they reference foreign resources; found resource "%1$s" of type "%2$s". MED_008=The time specified in the clipBegin attribute must not be after clipEnd. MED_009=The time specified in the clipBegin attribute must not be the same as clipEnd. MED_010=EPUB Content Documents referenced from a Media Overlay must specify the "media-overlay" attribute. @@ -191,7 +191,7 @@ OPF_009= OPF_010=Error resolving reference: "%1$s". OPF_011=itemref can’t have both page-spread-right and page-spread-left properties. OPF_012=Item property "%1$s" is not defined for media type "%2$s". -OPF_013=The type property "%1$s" on the object tag does not match the declared media-type "%2$s" in the OPF manifest. +OPF_013=Resource "%1$s" is declared with MIME type "%2$s" in content, but has MIME type "%3$s" in the package document. OPF_014=The property "%1$s" should be declared in the OPF file. OPF_015=The property "%1$s" should not be declared in the OPF file. OPF_016=The element "rootfile" is missing its required attribute "full-path". @@ -218,11 +218,11 @@ OPF_036_SUG=Use "video/mp4", "video/h264" or "video/webm" instead. OPF_037=Found deprecated media-type "%1$s". OPF_038=Media type "%1$s" is not appropriate for an OEBPS 1.2 context; Use "text/x-oeb1-document" instead. OPF_039=Media-type "%1$s" is not appropriate in an OEBPS 1.2 context. Use "text/x-oeb1-css" instead. -OPF_040=Fallback item could not be found. -OPF_041=Fallback-style item could not be found. +OPF_040=Fallback item with id "%1$s" could not be found. +OPF_041=Fallback-style item with id "%1$s" could not be found. OPF_042="%1$s" is not a permissible spine media-type. OPF_043=Spine item with non-standard media-type "%1$s" has no fallback. -OPF_044=Spine item with non-standard media-type "%1$s" has a fallback to non-standard media-type. +OPF_044=Spine item with non-standard media-type "%1$s" has no EPUB content document fallback. OPF_045=Encountered circular reference in fallback chain. OPF_046= OPF_047=OPF file is using OEBPS 1.2 syntax allowing backwards compatibility. @@ -329,7 +329,7 @@ RSC_002=Required META-INF/container.xml resource could not be found. RSC_003=No rootfile tag with media type "application/oebps-package+xml" was found in the container. RSC_004=File "%1$s" could not be decrypted. RSC_005=Error while parsing file: %1$s -RSC_006=Remote resource reference not allowed; resource must be placed in the OCF. +RSC_006=Remote resource reference is not allowed in this context; resource "%1$s" must be located in the EPUB container. RSC_006b=Resource "%1$s" is located outside the EPUB Container; please check the resource is retrieved in scripted content. RSC_007=Referenced resource "%1$s" could not be found in the EPUB. RSC_007w=Referenced resource "%1$s" could not be found in the EPUB. @@ -357,6 +357,7 @@ RSC_028=XML documents must be encoded in UTF-8, but %1%s was detected. RSC_029=Data URL is not allowed in this context. RSC_030=File URLs are not allowed in EPUB, but found "%1$s". RSC_031=Remote resource references should use HTTPS, but found "%1$s". +RSC_032=Fallback must be provided for foreign resources, but found none for resource "%1$s" of type "%2$s". #Scripting SCP_001=Use of Javascript eval() function in EPUB scripts is a security risk. diff --git a/src/main/resources/com/adobe/epubcheck/schema/30/package-30.sch b/src/main/resources/com/adobe/epubcheck/schema/30/package-30.sch index 3bc13bf36..fb8df287e 100644 --- a/src/main/resources/com/adobe/epubcheck/schema/30/package-30.sch +++ b/src/main/resources/com/adobe/epubcheck/schema/30/package-30.sch @@ -187,16 +187,6 @@ - - - - - manifest item element fallback attribute - must resolve to another manifest item (given reference was "") - - - diff --git a/src/test/java/org/w3c/epubcheck/test/ExecutionSteps.java b/src/test/java/org/w3c/epubcheck/test/ExecutionSteps.java index 41998848d..a8ebdfd43 100644 --- a/src/test/java/org/w3c/epubcheck/test/ExecutionSteps.java +++ b/src/test/java/org/w3c/epubcheck/test/ExecutionSteps.java @@ -11,7 +11,7 @@ import org.w3c.epubcheck.core.Checker; import org.w3c.epubcheck.test.TestConfiguration.CheckerMode; -import org.w3c.epubcheck.url.URLUtils; +import org.w3c.epubcheck.util.url.URLUtils; import com.adobe.epubcheck.api.EpubCheck; import com.adobe.epubcheck.nav.NavChecker; diff --git a/src/test/java/org/w3c/epubcheck/url/URLFragmentSteps.java b/src/test/java/org/w3c/epubcheck/url/URLFragmentSteps.java index 4f62a0b74..7cb3928db 100644 --- a/src/test/java/org/w3c/epubcheck/url/URLFragmentSteps.java +++ b/src/test/java/org/w3c/epubcheck/url/URLFragmentSteps.java @@ -8,6 +8,7 @@ import java.net.URI; import org.w3c.epubcheck.constants.MIMEType; +import org.w3c.epubcheck.util.url.URLFragment; import com.google.common.base.Enums; diff --git a/src/test/java/org/w3c/epubcheck/url/URLUtilsTest.java b/src/test/java/org/w3c/epubcheck/url/URLUtilsTest.java index e1d2246ec..3475d09f8 100644 --- a/src/test/java/org/w3c/epubcheck/url/URLUtilsTest.java +++ b/src/test/java/org/w3c/epubcheck/url/URLUtilsTest.java @@ -10,6 +10,7 @@ import java.nio.file.FileSystems; import org.junit.Test; +import org.w3c.epubcheck.util.url.URLUtils; import io.mola.galimatias.GalimatiasParseException; import io.mola.galimatias.URL; diff --git a/src/test/resources/epub2/opf-publication.feature b/src/test/resources/epub2/opf-publication.feature index 333b5dfe0..fe50cbc40 100644 --- a/src/test/resources/epub2/opf-publication.feature +++ b/src/test/resources/epub2/opf-publication.feature @@ -94,7 +94,7 @@ Feature: EPUB 2 ▸ Open Packaging Format ▸ Full Publication Checks Scenario: Report a manifest fallback that does not resolve to a resource in the publication When checking EPUB 'opf-fallback-non-resolving-error' Then error OPF-040 is reported (missing resource) - And error MED-003 is reported (fallback isn't provided) + And error RSC-032 is reported (fallback isn't provided) And no other errors or warnings are reported diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/content_001.xhtml deleted file mode 100644 index 17d868eac..000000000 --- a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/content_001.xhtml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Minimal EPUB - - - - -

Loomings

-

Call me Ishmael.

- - diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/style.css b/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/style.css deleted file mode 100644 index 0823b7849..000000000 --- a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/style.css +++ /dev/null @@ -1,6 +0,0 @@ -body { - margin-left : 6em; - margin-right : 16em; - color:black; - font-family: arial, helvetica, sans-serif; -} \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/page-template.xpgt b/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/page-template.xpgt deleted file mode 100644 index d2605894f..000000000 --- a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/page-template.xpgt +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/epub3/03-resources/files/fallback-cycle-error.opf b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error.opf similarity index 95% rename from src/test/resources/epub3/03-resources/files/fallback-cycle-error.opf rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error.opf index 6c3dfa713..884bc6b0d 100644 --- a/src/test/resources/epub3/03-resources/files/fallback-cycle-error.opf +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error.opf @@ -13,6 +13,6 @@ - + diff --git a/src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/content_001.xhtml similarity index 84% rename from src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/content_001.xhtml index 039fed604..2c6bd3a9e 100644 --- a/src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/EPUB/content_001.xhtml +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/content_001.xhtml @@ -5,6 +5,6 @@ Minimal EPUB - + diff --git a/src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/image.abc b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.abc similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/image.abc rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.abc diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/image.jpeg b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.jpeg similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/image.jpeg rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.jpeg diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/image.xyz b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.xyz similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/image.xyz rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/image.xyz diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/package.opf similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/EPUB/package.opf diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/mimetype b/src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/mimetype rename to src/test/resources/epub3/03-resources/files/fallback-chain-circular-error/mimetype diff --git a/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..96ab50048 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/content_001.xhtml @@ -0,0 +1,11 @@ + + + + + Minimal EPUB + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/EPUB/image.jpeg b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1.jpeg similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/EPUB/image.jpeg rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1.jpeg diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/foreign.xyz b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1a.vnd similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/foreign.xyz rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1a.vnd diff --git a/src/test/resources/epub3/03-resources/files/resources-foreign-res-unused-valid/EPUB/foreign.xyz b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1b.vnd similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-foreign-res-unused-valid/EPUB/foreign.xyz rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/image1b.vnd diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/package.opf new file mode 100644 index 000000000..f53ef72d0 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/EPUB/package.opf @@ -0,0 +1,21 @@ + + + + Title + en + NOID + 2019-01-01T12:00:00Z + + + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/mimetype b/src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/mimetype rename to src/test/resources/epub3/03-resources/files/fallback-chain-n-to-1-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/fallback-chain-valid.opf b/src/test/resources/epub3/03-resources/files/fallback-chain-valid.opf index 7f35b26be..fcc0fc209 100644 --- a/src/test/resources/epub3/03-resources/files/fallback-chain-valid.opf +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-valid.opf @@ -9,11 +9,22 @@ - - - + + + + + + + + + - + diff --git a/src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/content_001.xhtml similarity index 83% rename from src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/content_001.xhtml index 039fed604..4cea05870 100644 --- a/src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/EPUB/content_001.xhtml +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/content_001.xhtml @@ -5,6 +5,6 @@ Minimal EPUB - + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/EPUB/image.jpeg b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1.jpeg similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/EPUB/image.jpeg rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1.jpeg diff --git a/src/test/resources/epub3/06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/slideshow.xml b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1a.vnd similarity index 100% rename from src/test/resources/epub3/06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/slideshow.xml rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1a.vnd diff --git a/src/test/resources/epub3/06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/video.avi b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1b.vnd similarity index 100% rename from src/test/resources/epub3/06-content-document/files/content-xhtml-object-no-fallback-error/EPUB/video.avi rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1b.vnd diff --git a/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1c.vnd b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/image1c.vnd new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/package.opf new file mode 100644 index 000000000..d5cfde67c --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/EPUB/package.opf @@ -0,0 +1,22 @@ + + + + Title + en + NOID + 2019-01-01T12:00:00Z + + + + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/mimetype b/src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/mimetype rename to src/test/resources/epub3/03-resources/files/fallback-chain-waterfall-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/OpenSans-Regular.ttf b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/OpenSans-Regular.ttf similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/OpenSans-Regular.ttf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/OpenSans-Regular.ttf diff --git a/src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/content_001.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/content_001.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/package.opf similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/package.opf diff --git a/src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/style.css b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/style.css similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/EPUB/style.css rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/EPUB/style.css diff --git a/src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-cmt-font-other-mediatype-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-font-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/content_001.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/content_001.xhtml diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/foreign.xyz b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/foreign.xyz new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/package.opf similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/EPUB/package.opf diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-foreign-res-in-link-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-unused-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/content_001.xhtml similarity index 83% rename from src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/content_001.xhtml index 03936f8d6..b966c55e9 100644 --- a/src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/content_001.xhtml +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/content_001.xhtml @@ -3,7 +3,7 @@ Minimal EPUB - +

Loomings

diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/test.txt b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/foreign.xyz similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-link-no-fallback-valid/EPUB/test.txt rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/foreign.xyz diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/resources-foreign-res-unused-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/package.opf similarity index 96% rename from src/test/resources/epub3/03-resources/files/resources-foreign-res-unused-valid/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/package.opf index c4605a514..b73d4be3d 100644 --- a/src/test/resources/epub3/03-resources/files/resources-foreign-res-unused-valid/EPUB/package.opf +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/EPUB/package.opf @@ -9,7 +9,7 @@ - + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-manifest-fallback-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-src-error/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/content_001.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/content_001.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-srcset-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-srcset-error/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/package.opf similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/package.opf diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/page-template.xpgt b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/page-template.xpgt similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/page-template.xpgt rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/page-template.xpgt diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/style.css b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/style.css similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-implicit-fallback-valid/EPUB/style.css rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/EPUB/style.css diff --git a/src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/resources-manifest-fallback-circular-error/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-srcset-error/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-fallback-img-foreign-srcset-error/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-manifest-fallback-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/content_001.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/content_001.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/content_001.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-no-type-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-no-type-error/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/package.opf similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-no-fallback-valid/EPUB/package.opf rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/package.opf diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/page-template.xpgt b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/page-template.xpgt similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-xpgt-manifest-fallback-valid/EPUB/page-template.xpgt rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/EPUB/page-template.xpgt diff --git a/src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/05-package-document/files/package-manifest-fallback-non-resolving-error/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-no-type-error/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-no-type-error/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-link-xpgt-no-fallback-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/audio.mpeg b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/audio.mpeg new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..beac98a5f --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/content_001.xhtml @@ -0,0 +1,14 @@ + + + + + Minimal EPUB + + +

Loomings

+

Call me Ishmael.

+ + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-cmt-type-error/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-cmt-type-error/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/package.opf new file mode 100644 index 000000000..62d14a5ed --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/package.opf @@ -0,0 +1,18 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/subtitles.vtt b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/EPUB/subtitles.vtt new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-src-no-manifest-fallback-error/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-cmt-type-error/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-cmt-type-error/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-track-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/animation.avi b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/animation.avi new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..2ed4b18ad --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/content_001.xhtml @@ -0,0 +1,10 @@ + + + + + Minimal EPUB + + + the whale + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-type-valid/EPUB/nav.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/nav.xhtml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-type-valid/EPUB/nav.xhtml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/nav.xhtml diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/package.opf b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/package.opf new file mode 100644 index 000000000..9060148f0 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/EPUB/package.opf @@ -0,0 +1,17 @@ + + + + Minimal EPUB 3.0 + en + NOID + 2017-06-14T00:00:01Z + + + + + + + + + + diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/META-INF/container.xml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/META-INF/container.xml similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-img-srcset-manifest-fallback-valid/META-INF/container.xml rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/META-INF/container.xml diff --git a/src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-type-valid/mimetype b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/mimetype similarity index 100% rename from src/test/resources/epub3/03-resources/files/content-xhtml-picture-source-foreign-with-type-valid/mimetype rename to src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-in-img-valid/mimetype diff --git a/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-valid/EPUB/content_001.xhtml b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-valid/EPUB/content_001.xhtml new file mode 100644 index 000000000..b0c1346a3 --- /dev/null +++ b/src/test/resources/epub3/03-resources/files/foreign-exempt-xhtml-video-valid/EPUB/content_001.xhtml @@ -0,0 +1,12 @@ + + + + + Minimal EPUB + + +

Loomings

+

Call me Ishmael.

+