Skip to content

Commit

Permalink
feat: check image sources in picture and srcset
Browse files Browse the repository at this point in the history
Message changes:

- new `MED-007` (`ERROR`) message is reported when an image source
  references a foreign resource (unless on `source` elements which
  `type` attribute specify non-core media type).
- reworded `MED-003`; it now includes the resource path.

Internal changes:

- add a `SourceSet` class to represent an image source set, along with
  a parser folloing the "parsing a srcset attribute" algorithm from HTML.
  See https://html.spec.whatwg.org/#parsing-a-srcset-attribute
- add to new reference types to the `XRefChecker` class, `PICTURE_SOURCE`
  and `PICTURE_SOURCE_FOREIGN`, to identify image source references in
  `picture` elements.
- add tests, for both the `srcset` parser and the new fallback rules.

Fix #781
  • Loading branch information
rdeltour authored Mar 17, 2019
1 parent 2cafe64 commit 11bf628
Show file tree
Hide file tree
Showing 113 changed files with 1,549 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ private void initialize()
severities.put(MessageId.MED_004, Severity.ERROR);
severities.put(MessageId.MED_005, Severity.ERROR);
severities.put(MessageId.MED_006, Severity.USAGE);
severities.put(MessageId.MED_007, Severity.ERROR);

// NAV
severities.put(MessageId.NAV_001, Severity.ERROR);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public enum MessageId implements Comparable<MessageId>
MED_004("MED-004"),
MED_005("MED-005"),
MED_006("MED_006"),
MED_007("MED_007"),

// Epub3 based table of content errors
NAV_001("NAV-001"),
Expand Down
24 changes: 18 additions & 6 deletions src/main/java/com/adobe/epubcheck/opf/XRefChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ public static enum Type
REGION_BASED_NAV,
SEARCH_KEY,
NAV_TOC_LINK,
NAV_PAGELIST_LINK;
NAV_PAGELIST_LINK,
PICTURE_SOURCE,
PICTURE_SOURCE_FOREIGN;
}

private static class Reference
Expand Down Expand Up @@ -98,6 +100,7 @@ public Reference(String srcResource, int srcLineNumber, int srcColumnNumber, Str
private static class Anchor
{

@SuppressWarnings("unused")
public final String id;
public final Type type;
public final int position;
Expand Down Expand Up @@ -383,19 +386,28 @@ else if (!undeclared.contains(ref.refResource)
}
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()) && !res.hasValidImageFallback)
if (!OPFChecker.isBlessedImageType(res.item.getMimeType()))
{
report.message(MessageId.MED_003,
EPUBLocation.create(ref.source, ref.lineNumber, ref.columnNumber),
res.item.getMimeType());
return;
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:
Expand Down
68 changes: 68 additions & 0 deletions src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -19,6 +20,7 @@
import com.adobe.epubcheck.util.EpubConstants;
import com.adobe.epubcheck.util.FeatureEnum;
import com.adobe.epubcheck.util.PathUtil;
import com.adobe.epubcheck.util.SourceSet;
import com.adobe.epubcheck.vocab.AggregateVocab;
import com.adobe.epubcheck.vocab.AltStylesheetVocab;
import com.adobe.epubcheck.vocab.ComicsVocab;
Expand Down Expand Up @@ -70,6 +72,7 @@ public class OPSHandler30 extends OPSHandler

protected boolean inVideo = false;
protected boolean inAudio = false;
protected boolean inPicture = false;
protected boolean hasValidFallback = false;

protected int imbricatedObjects = 0;
Expand Down Expand Up @@ -134,6 +137,59 @@ public OPSHandler30(ValidationContext context, XMLParser parser)
.contains(EpubCheckVocab.VOCAB.get(EpubCheckVocab.PROPERTIES.NON_LINEAR));
}

protected void checkImage(XMLElement e, String attrNS, String attr)
{
// 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))
{
super.checkImage(e, attrNS, attr);
}
// else process image source sets in HTML
else if (xrefChecker.isPresent())
{
String src = e.getAttribute("src");
String srcset = e.getAttribute("srcset");
// if we're in a 'picture' element
if (inPicture)
{
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))
{
registerImageSources(src, srcset, XRefChecker.Type.PICTURE_SOURCE_FOREIGN);
}
// else register as regular picture source (must be a CMT)
else
// register as picture source
{
registerImageSources(src, srcset, XRefChecker.Type.PICTURE_SOURCE);
}
}
// 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<String> urls = new TreeSet<>();
if (src != null) urls.add(src);
urls.addAll(SourceSet.parse(srcset).getImageURLs());
// register all the URLs
for (String url : urls)
{
xrefChecker.get().registerReference(path, parser.getLineNumber(), parser.getColumnNumber(),
PathUtil.resolveRelativeReference(base, url), type);
}

}

protected void checkType(XMLElement e, String type)
{
if (type == null)
Expand Down Expand Up @@ -305,6 +361,14 @@ else if (name.equals("annotation-xml"))
{
hasAltorAnnotation = true;
}
else if (name.equals("picture"))
{
inPicture = true;
}
else if (name.equals("source"))
{
if (inPicture) checkImage(e, null, null);
}
else if ("http://www.w3.org/2000/svg".equals(e.getNamespace()) && name.equals("title"))
{
hasTitle = true;
Expand Down Expand Up @@ -709,6 +773,10 @@ else if (name.equals("nav") && inRegionBasedNav)
{
inRegionBasedNav = false;
}
else if (name.equals("picture"))
{
inPicture = false;
}
else if (name.equals("svg"))
{
inSvg = false;
Expand Down
Loading

0 comments on commit 11bf628

Please sign in to comment.