Skip to content

Commit

Permalink
feat: check references between Media Overlays and Content documents
Browse files Browse the repository at this point in the history
Add the following new errors:
- Error MED-010 when an item is missing a media-overlay attribute
- Error MED-011 when the media-overlay attribute references the wrong
  overlay
- Error MED-012 when the media-overlay attribute references an overlay
  that does not reference the underlying content document
- Error MED-013 when a content doc is referenced from multiple overlays

Also Add tests for the new errors.

Implementation note:
- pull the unique references from the text elements while parsing
  the overlay documents
- then run checks on the affected content documents in the post-processing
  function.
  • Loading branch information
mattgarrish authored and rdeltour committed Dec 28, 2020
1 parent 11b652e commit f49aa84
Show file tree
Hide file tree
Showing 46 changed files with 492 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ private void initialize()
severities.put(MessageId.MED_007, Severity.ERROR);
severities.put(MessageId.MED_008, Severity.ERROR);
severities.put(MessageId.MED_009, Severity.ERROR);
severities.put(MessageId.MED_010, Severity.ERROR);
severities.put(MessageId.MED_011, Severity.ERROR);
severities.put(MessageId.MED_012, Severity.ERROR);
severities.put(MessageId.MED_013, Severity.ERROR);

// NAV
severities.put(MessageId.NAV_001, Severity.ERROR);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ public enum MessageId implements Comparable<MessageId>
MED_007("MED_007"),
MED_008("MED-008"),
MED_009("MED-009"),
MED_010("MED_010"),
MED_011("MED_011"),
MED_012("MED_012"),
MED_013("MED_013"),

// Epub3 based table of content errors
NAV_001("NAV-001"),
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/adobe/epubcheck/opf/OPFChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.adobe.epubcheck.ocf.OCFPackage;
import com.adobe.epubcheck.opf.ValidationContext.ValidationContextBuilder;
import com.adobe.epubcheck.ops.OPSCheckerFactory;
import com.adobe.epubcheck.overlay.OverlayTextChecker;
import com.adobe.epubcheck.util.EPUBVersion;
import com.adobe.epubcheck.util.FeatureEnum;
import com.adobe.epubcheck.util.PathUtil;
Expand Down Expand Up @@ -114,6 +115,7 @@ public OPFChecker(ValidationContext context)
newContext.pubTypes(opfData != null ? opfData.getTypes() : null);
newContext.xrefChecker(new XRefChecker(context.ocf.get(), context.report, context.version));
newContext.profile(EPUBProfile.makeOPFCompatible(context.profile, opfData, path, report));
newContext.overlayTextChecker(new OverlayTextChecker());
}
this.context = newContext.build();

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/adobe/epubcheck/opf/OPFChecker30.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.adobe.epubcheck.opf.ResourceCollection.Roles;
import com.adobe.epubcheck.ops.OPSCheckerFactory;
import com.adobe.epubcheck.overlay.OverlayCheckerFactory;
import com.adobe.epubcheck.overlay.OverlayTextChecker;
import com.adobe.epubcheck.util.EPUBVersion;
import com.adobe.epubcheck.util.FeatureEnum;
import com.adobe.epubcheck.util.PathUtil;
Expand Down Expand Up @@ -182,6 +183,29 @@ else if (xrefChecker.getTypes(item.getPath()).isEmpty())
}
}
}

if (isBlessedItemType(mediatype, version)) {
// check whether media-overlay attribute needs to be specified
OverlayTextChecker overlayTextChecker = context.overlayTextChecker.get();
String mo = item.getMediaOverlay();
String docpath = item.getPath();
if (overlayTextChecker.isReferencedByOverlay(docpath)) {
if (Strings.isNullOrEmpty(mo)) {
// missing media-overlay attribute
report.message(MessageId.MED_010, EPUBLocation.create(path, item.getLineNumber(), item.getColumnNumber(), item.getPath()));
}
else if (!overlayTextChecker.isCorrectOverlay(docpath,mo)) {
// media-overlay attribute references the wrong media overlay
report.message(MessageId.MED_012, EPUBLocation.create(path, item.getLineNumber(), item.getColumnNumber(), item.getPath()));
}
}
else {
if (!Strings.isNullOrEmpty(mo)) {
// referenced overlay does not reference this content document
report.message(MessageId.MED_013, EPUBLocation.create(path, item.getLineNumber(), item.getColumnNumber(), item.getPath()));
}
}
}
}

@Override
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/adobe/epubcheck/opf/OPFHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ else if (name.equals("item"))
itemBuilders.put(id.trim(), itemBuilder);
itemBuildersByPath.put(href, itemBuilder);

String mediaOverlay = e.getAttribute("media-overlay");
itemBuilder.mediaOverlay(mediaOverlay);

report.info(href, FeatureEnum.UNIQUE_IDENT, id);
}
}
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/com/adobe/epubcheck/opf/OPFItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ public class OPFItem
private final boolean scripted;
private final boolean linear;
private final boolean fixedLayout;
private final String mediaOverlay;

private OPFItem(String id, String path, String mimetype, int lineNumber, int columnNumber,
Optional<String> fallback, Optional<String> fallbackStyle, Set<Property> properties,
boolean ncx, int spinePosition, boolean nav, boolean scripted, boolean linear, boolean fxl)
boolean ncx, int spinePosition, boolean nav, boolean scripted, boolean linear, boolean fxl, String mediaOverlay)
{
this.id = id;
this.path = path;
Expand All @@ -74,6 +75,7 @@ private OPFItem(String id, String path, String mimetype, int lineNumber, int col
this.scripted = scripted;
this.linear = linear;
this.fixedLayout = fxl;
this.mediaOverlay = mediaOverlay;
}

/**
Expand Down Expand Up @@ -240,6 +242,11 @@ public boolean isFixedLayout()
return fixedLayout;
}

public String getMediaOverlay()
{
return mediaOverlay;
}

@Override
public String toString()
{
Expand Down Expand Up @@ -293,6 +300,7 @@ public static final class Builder
private boolean linear = true;
private int spinePosition = -1;
private boolean fxl = false;
private String mediaOverlay;
private ImmutableSet.Builder<Property> propertiesBuilder = new ImmutableSet.Builder<Property>();

/**
Expand Down Expand Up @@ -339,6 +347,12 @@ public Builder fixedLayout()

}

public Builder mediaOverlay(String path)
{
this.mediaOverlay = path;
return this;
}

public Builder ncx()
{
this.ncx = true;
Expand Down Expand Up @@ -388,7 +402,7 @@ public OPFItem build()
properties, ncx, spinePosition,
properties.contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.NAV)),
properties.contains(PackageVocabs.ITEM_VOCAB.get(PackageVocabs.ITEM_PROPERTIES.SCRIPTED)),
linear, fxl);
linear, fxl, mediaOverlay);
}
}
}
18 changes: 16 additions & 2 deletions src/main/java/com/adobe/epubcheck/opf/ValidationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.adobe.epubcheck.api.LocalizableReport;
import com.adobe.epubcheck.api.Report;
import com.adobe.epubcheck.ocf.OCFPackage;
import com.adobe.epubcheck.overlay.OverlayTextChecker;
import com.adobe.epubcheck.util.EPUBVersion;
import com.adobe.epubcheck.util.GenericResourceProvider;
import com.adobe.epubcheck.vocab.Property;
Expand Down Expand Up @@ -77,6 +78,10 @@ public final class ValidationContext
* The cross-reference checker, absent for single-file validation.
*/
public final Optional<XRefChecker> xrefChecker;
/**
* The src checker for media overlay text elements, absent for single-file validation
*/
public final Optional<OverlayTextChecker> overlayTextChecker;
/**
* The set of 'dc:type' values declared at the OPF level. Guaranteed non-null,
* can be empty.
Expand All @@ -90,7 +95,7 @@ public final class ValidationContext
private ValidationContext(String path, String mimeType, EPUBVersion version, EPUBProfile profile,
Report report, Locale locale, FeatureReport featureReport,
GenericResourceProvider resourceProvider, Optional<OPFItem> opfItem, Optional<OCFPackage> ocf,
Optional<XRefChecker> xrefChecker, Set<String> pubTypes, Set<Property> properties)
Optional<XRefChecker> xrefChecker, Optional<OverlayTextChecker> overlayTextChecker, Set<String> pubTypes, Set<Property> properties)
{
super();
this.path = path;
Expand All @@ -104,6 +109,7 @@ private ValidationContext(String path, String mimeType, EPUBVersion version, EPU
this.opfItem = opfItem;
this.ocf = ocf;
this.xrefChecker = xrefChecker;
this.overlayTextChecker = overlayTextChecker;
this.pubTypes = pubTypes;
this.properties = properties;
}
Expand All @@ -125,6 +131,7 @@ public static final class ValidationContextBuilder
private GenericResourceProvider resourceProvider = null;
private OCFPackage ocf = null;
private XRefChecker xrefChecker = null;
private OverlayTextChecker overlayTextChecker = null;
private Set<String> pubTypes = null;
private ImmutableSet.Builder<Property> properties = ImmutableSet.<Property> builder();

Expand All @@ -148,6 +155,7 @@ public ValidationContextBuilder copy(ValidationContext context)
resourceProvider = context.resourceProvider;
ocf = context.ocf.orNull();
xrefChecker = context.xrefChecker.orNull();
overlayTextChecker = context.overlayTextChecker.orNull();
pubTypes = context.pubTypes;
properties = ImmutableSet.<Property> builder().addAll(context.properties);
return this;
Expand Down Expand Up @@ -207,6 +215,12 @@ public ValidationContextBuilder xrefChecker(XRefChecker xrefChecker)
return this;
}

public ValidationContextBuilder overlayTextChecker(OverlayTextChecker overlayTextChecker)
{
this.overlayTextChecker = overlayTextChecker;
return this;
}

public ValidationContextBuilder pubTypes(Set<String> pubTypes)
{
this.pubTypes = pubTypes;
Expand Down Expand Up @@ -243,7 +257,7 @@ public ValidationContext build()
profile != null ? profile : EPUBProfile.DEFAULT, report, locale,
featureReport != null ? featureReport : new FeatureReport(), resourceProvider,
(xrefChecker != null) ? xrefChecker.getResource(path) : Optional.<OPFItem> absent(),
Optional.fromNullable(ocf), Optional.fromNullable(xrefChecker),
Optional.fromNullable(ocf), Optional.fromNullable(xrefChecker), Optional.fromNullable(overlayTextChecker),
pubTypes != null ? ImmutableSet.copyOf(pubTypes) : ImmutableSet.<String> of(),
properties.build());
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/adobe/epubcheck/overlay/OverlayHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.adobe.epubcheck.overlay;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

Expand All @@ -20,6 +21,7 @@
import com.adobe.epubcheck.xml.XMLElement;
import com.adobe.epubcheck.xml.XMLHandler;
import com.adobe.epubcheck.xml.XMLParser;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

Expand All @@ -40,6 +42,8 @@ public class OverlayHandler implements XMLHandler

private Map<String, Vocab> vocabs = RESERVED_VOCABS;

private Set<String> resourceRefs = new HashSet<String>();

public OverlayHandler(ValidationContext context, XMLParser parser)
{
this.context = context;
Expand Down Expand Up @@ -158,6 +162,14 @@ private void processRef(String ref, XRefChecker.Type type)
report.message(MessageId.MED_005, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()), ref, mimeType);
}
}
else {
String uniqueResource = PathUtil.removeFragment(ref);
if (!Strings.isNullOrEmpty(uniqueResource)) {
if (!context.overlayTextChecker.get().add(uniqueResource, context.opfItem.get().getId())) {
report.message(MessageId.MED_011, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()), ref);
}
}
}
context.xrefChecker.get().registerReference(path, parser.getLineNumber(),
parser.getColumnNumber(), ref, type);
}
Expand All @@ -176,6 +188,12 @@ public void characters(char[] chars, int arg1, int arg2)

public void endElement()
{
XMLElement e = parser.getCurrentElement();
String name = e.getName();
if (name.equals("smil"))
{
checkItemReferences();
}
}

public void ignorableWhitespace(char[] chars, int arg1, int arg2)
Expand All @@ -185,4 +203,13 @@ public void ignorableWhitespace(char[] chars, int arg1, int arg2)
public void processingInstruction(String arg0, String arg1)
{
}

private void checkItemReferences() {

if(this.resourceRefs.isEmpty()) {
return;
}

}

}
35 changes: 35 additions & 0 deletions src/main/java/com/adobe/epubcheck/overlay/OverlayTextChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.adobe.epubcheck.overlay;

import java.util.Map;
import java.util.HashMap;

public class OverlayTextChecker {

private Map<String,String> refs;

public OverlayTextChecker() {
refs = new HashMap<String,String>();
}

public boolean add(String ref, String overlay) {
if (!refs.containsKey(ref)) {
refs.put(ref, overlay);
return true;
}
else if (!refs.get(ref).equalsIgnoreCase(overlay)) {
return false;
}
return true;
}

public boolean isReferencedByOverlay(String path) {
if (path == null || path.equals("")) {
return false;
}
return refs.containsKey(path) ? true : false;
}

public boolean isCorrectOverlay(String path, String overlay) {
return overlay.equalsIgnoreCase(refs.get(path)) ? true : false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ MED_006=Some browsers do not support rendering SVG images which use a filename i
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_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.
MED_011=EPUB Content Document referenced from multiple Media Overlay Documents.
MED_012=The "media-overlay" attribute does not match the ID of the Media Overlay that refers to this document.
MED_013=Media Overlay Document referenced from the "media-overlay" attribute does not contain a reference to this Content Document.

#NAV EPUB v3 Table of contents
NAV_001=The nav file is not supported for EPUB v2.
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<smil xmlns="http://www.w3.org/ns/SMIL" xmlns:epub="http://www.idpf.org/2007/ops" version="3.0">
<body>
<par id="par1">
<text src="content_001.xhtml#c01"/>
<audio src="content.mp3"/>
</par>
<par id="par2">
<text src="content_002.xhtml#c02"/>
<audio src="content.mp3"/>
</par>
</body>
</smil>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal EPUB</title>
</head>
<body>
<h1 id="c01">Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<smil xmlns="http://www.w3.org/ns/SMIL" xmlns:epub="http://www.idpf.org/2007/ops" version="3.0">
<body>
<par id="par1">
<text src="content_003.xhtml#c03"/>
<audio src="content.mp3"/>
</par>
</body>
</smil>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal EPUB</title>
</head>
<body>
<h1 id="c02">Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal EPUB</title>
</head>
<body>
<h1 id="c03">Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
Loading

0 comments on commit f49aa84

Please sign in to comment.