Skip to content

Commit

Permalink
feat: check that non-linear content is reachable
Browse files Browse the repository at this point in the history
EPUB 3.3 says:
> EPUB creators MUST provide a means of accessing all non-linear content
> (e.g., hyperlinks in the content or from the EPUB navigation document).

This commit adds a check for the above statement.
- new error `OPF-096` is reported when no hyperlink was found to
  non-linear content in an EPUB with no script
- new usage `OPF-096b` is reported when no hyperlink was found to
  non-linear content in a scripted EPUB (a link may be added via
  scripting, so we cannot report this as an error)

Fix #1451
  • Loading branch information
rdeltour committed Dec 15, 2022
1 parent 0175818 commit f20993e
Show file tree
Hide file tree
Showing 54 changed files with 402 additions and 22 deletions.
Expand Up @@ -289,6 +289,8 @@ private void initialize()
severities.put(MessageId.OPF_093, Severity.ERROR);
severities.put(MessageId.OPF_094, Severity.ERROR);
severities.put(MessageId.OPF_095, Severity.ERROR);
severities.put(MessageId.OPF_096, Severity.ERROR);
severities.put(MessageId.OPF_096b, Severity.USAGE);

// PKG
severities.put(MessageId.PKG_001, Severity.WARNING);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Expand Up @@ -283,6 +283,8 @@ public enum MessageId implements Comparable<MessageId>
OPF_093("OPF-093"),
OPF_094("OPF-094"),
OPF_095("OPF-095"),
OPF_096("OPF-096"),
OPF_096b("OPF-096b"),

// Messages relating to the entire package
PKG_001("PKG-001"),
Expand Down
27 changes: 24 additions & 3 deletions src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java
Expand Up @@ -24,6 +24,7 @@

import java.util.Set;

import org.w3c.epubcheck.core.references.Reference;
import org.w3c.epubcheck.util.url.URLFragment;

import com.adobe.epubcheck.api.EPUBLocation;
Expand Down Expand Up @@ -183,6 +184,26 @@ else if (!overlayTextChecker.isCorrectOverlay(docURL, mo))
}
}
}

// check that non-linear content documents are reachable
if (item.isInSpine() && !item.isLinear() && context.referenceRegistry.isPresent()
// search the reference registry for any hyperlink pointing to this item
&& !context.referenceRegistry.get().asList().stream()
.anyMatch(ref -> ref.type == Reference.Type.HYPERLINK
&& ref.targetResource.equals(item.getURL())))
{
// if content is scripted, references can be added by scripting
// se we only report a usage
if (context.featureReport.hasFeature(FeatureEnum.HAS_SCRIPTS))
{
report.message(MessageId.OPF_096b, item.getLocation(), item.getPath());
}
// else, report an error if no hyperlink were found
else
{
report.message(MessageId.OPF_096, item.getLocation(), item.getPath());
}
}
}

@Override
Expand All @@ -194,14 +215,15 @@ protected void checkSpineItem(OPFItem item, OPFHandler opfHandler)
return;
}

String mimeType = item.getMimeType();

// check properties
if (item.getProperties()
.contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.DATA_NAV)))
{
report.message(MessageId.OPF_077, item.getLocation());
}

// check that spine items have content document fallback
String mimeType = item.getMimeType();
if (!isBlessedItemType(mimeType, version))
{
if (!item.hasFallback())
Expand All @@ -213,7 +235,6 @@ else if (!item.hasContentDocumentFallback())
report.message(MessageId.OPF_044, item.getLocation(), mimeType);
}
}

}

private void checkCollections()
Expand Down
Expand Up @@ -210,7 +210,9 @@ OPF_091=The item href URL must not have a fragment identifier.
OPF_092=Language tag "%1$s" is not well-formed: %2$s
OPF_093=The "media-type" attribute is required for linked resources located in the EPUB container
OPF_094=The "media-type" attribute is required for "%1$s" links.
OPF_095=The "media-type" attribute of "voicing" links must be an audio MIME type, but found "%1$s".
OPF_095=The "media-type" attribute of "voicing" links must be an audio MIME type, but found "%1$s".
OPF_096=Non-linear content must be reachable, but found no hyperlink to "%1$s".
OPF_096b=No hyperlink was found to non-linear document "%1$s", please check that it can be reached from scripted content.

#Package
PKG_001=Validating the EPUB against version %1$s but detected version %2$s.
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/epub-edupub/edupub-publication.feature
Expand Up @@ -51,7 +51,7 @@ Feature: EPUB for Education ▸ Full Publication Checks

## 4.2 Sectioning

Scenario: Verify an non-linear content does not have to follow the sectioning rules
Scenario: Verify that non-linear content does not have to follow the sectioning rules
When checking EPUB 'edupub-non-linear-valid'
Then no errors or warnings are reported

Expand Down
Expand Up @@ -8,6 +8,7 @@
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
<li><a href="nonlinear.xhtml">non-linear</a></li>
</ol>
</nav>
</body>
Expand Down
Expand Up @@ -11,10 +11,10 @@
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="non" href="nonlinear.xhtml" media-type="application/xhtml+xml" />
<item id="nonlinear" href="nonlinear.xhtml" media-type="application/xhtml+xml" />
</manifest>
<spine>
<itemref idref="content_001" />
<itemref idref="non" linear="no" />
<itemref idref="nonlinear" linear="no" />
</spine>
</package>
Expand Up @@ -8,6 +8,7 @@
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
<li><a href="data-nav.xhtml">data nav</a></li>
</ol>
</nav>
</body>
Expand Down
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<!-- <itemref idref="content_001" />-->
</spine>
</package>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<itemref idref="content_001" linear=" no "/>
</spine>
</package>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="content_002" href="content_002.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<itemref idref="content_001" />
<itemref idref="content_002" linear="no"/>
</spine>
</package>
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
<a href="content_002.xhtml">link</a>
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="content_002" href="content_002.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<itemref idref="content_001" />
<itemref idref="content_002" linear="no"/>
</spine>
</package>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="EPUB/package.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal Nav</title>
</head>
<body>
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
<li><a href="content_002.xhtml">content 002</a></li>
</ol>
</nav>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml"/>
<item id="content_002" href="content_002.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<itemref idref="content_001" />
<itemref idref="content_002" linear="no"/>
</spine>
</package>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="EPUB/package.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
@@ -0,0 +1 @@
application/epub+zip
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal EPUB</title>
<script>
window.addEventListener("load", function(){
var link = document.createElement('a');
link.setAttribute('href', 'content_002.xhtml');
link.innerHTML = "link";
document.body.appendChild(link);
});
</script>
</head>
<body id="body">
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:epub="http://www.idpf.org/2007/ops" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<title>Minimal Nav</title>
</head>
<body>
<nav epub:type="toc">
<ol>
<li><a href="content_001.xhtml">content 001</a></li>
</ol>
</nav>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="en" unique-identifier="q">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title id="title">Minimal EPUB 3.0</dc:title>
<dc:language>en</dc:language>
<dc:identifier id="q">NOID</dc:identifier>
<meta property="dcterms:modified">2017-06-14T00:00:01Z</meta>
</metadata>
<manifest>
<item id="content_001" href="content_001.xhtml" media-type="application/xhtml+xml" properties="scripted"/>
<item id="content_002" href="content_002.xhtml" media-type="application/xhtml+xml"/>
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
</manifest>
<spine>
<itemref idref="content_001" />
<itemref idref="content_002" linear="no"/>
</spine>
</package>

0 comments on commit f20993e

Please sign in to comment.