Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.1.1-android</version>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ private void initialize()
severities.put(MessageId.HTM_056, Severity.ERROR);
severities.put(MessageId.HTM_057, Severity.ERROR);
severities.put(MessageId.HTM_058, Severity.ERROR);
severities.put(MessageId.HTM_059, Severity.ERROR);
severities.put(MessageId.HTM_060a, Severity.USAGE);
severities.put(MessageId.HTM_060b, Severity.USAGE);

// Media
severities.put(MessageId.MED_001, Severity.SUPPRESSED);
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ public enum MessageId implements Comparable<MessageId>
HTM_056("HTM_056"),
HTM_057("HTM_057"),
HTM_058("HTM_058"),
HTM_059("HTM_059"),
HTM_060a("HTM_060a"),
HTM_060b("HTM_060b"),

// Messages associated with media (images, audio and video)
MED_001("MED-001"),
Expand Down
66 changes: 40 additions & 26 deletions src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.adobe.epubcheck.ops;

import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
Expand All @@ -10,6 +11,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.w3c.epubcheck.constants.MIMEType;
import org.w3c.epubcheck.core.references.Reference;
Expand Down Expand Up @@ -763,17 +765,14 @@ protected void processMeta()
String name = e.getAttribute("name");
if ("viewport".equals(Strings.nullToEmpty(name).trim()))
{
// Mark the viewport as seen
// (used when checking the existence of viewport metadata)
hasViewport = true;
// For a fixed-layout documents:
if (context.opfItem.isPresent() && context.opfItem.get().isFixedLayout())
String content = e.getAttribute("content");
// For fixed-layout documents, check the first viewport meta element
if (!hasViewport && context.opfItem.isPresent() && context.opfItem.get().isFixedLayout())
{
String contentAttribute = e.getAttribute("content");

hasViewport = true;
// parse viewport metadata
List<ViewportMeta.ParseError> syntaxErrors = new LinkedList<>();
ViewportMeta viewport = ViewportMeta.parse(contentAttribute,
ViewportMeta viewport = ViewportMeta.parse(content,
new ViewportMeta.ErrorHandler()
{
@Override
Expand All @@ -785,31 +784,46 @@ public void error(ParseError error, int position)
if (!syntaxErrors.isEmpty())
{
// report any syntax error
report.message(MessageId.HTM_047, location(), contentAttribute);
report.message(MessageId.HTM_047, location(), content);
}
else
{
// check that viewport metadata has a valid width value
if (!viewport.hasProperty("width"))
{
report.message(MessageId.HTM_056, location(), "width");
}
else if (!ViewportMeta.isValidWidth(viewport.getValue("width")))
for (String property : Arrays.asList("width", "height"))
{
report.message(MessageId.HTM_057, location(), "width");
// check that viewport metadata has a valid width value
if (!viewport.hasProperty(property))
{
report.message(MessageId.HTM_056, location(), property);
}
else
{
List<String> values = viewport.getValues(property);
if (values.size() > 1)
{
report.message(MessageId.HTM_059, location(), property,
values.stream().map(v -> '"' + v + '"').collect(Collectors.joining(", ")));
}
if (!ViewportMeta.isValidProperty(property, values.get(0)))
{
report.message(MessageId.HTM_057, location(), property);
}
}
}

// check that viewport metadata has a valid height value
if (!viewport.hasProperty("height"))
{
report.message(MessageId.HTM_056, location(), "height");
}
else if (!ViewportMeta.isValidHeight(viewport.getValue("height")))
{
report.message(MessageId.HTM_057, location(), "height");
}
}

}
else
{
// Report ignored secondary viewport meta in fixed-layout documents
if (context.opfItem.isPresent() && context.opfItem.get().isFixedLayout())
{
report.message(MessageId.HTM_060a, location(), content);
}
// Report ignored viewport meta in reflowable documents
else
{
report.message(MessageId.HTM_060b, location(), content);
}
}
}
}
Expand Down
79 changes: 45 additions & 34 deletions src/main/java/org/w3c/epubcheck/util/microsyntax/ViewportMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import static org.w3c.epubcheck.util.infra.InfraUtil.isASCIIWhitespace;

import java.nio.CharBuffer;
import java.util.Map;
import java.util.List;
import java.util.regex.Pattern;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;

public class ViewportMeta
{
Expand All @@ -19,16 +20,18 @@ public static ViewportMeta parse(String string, ErrorHandler errorHandler)
return new Parser(errorHandler).parse(string);
}

public static boolean isValidHeight(String height)
public static boolean isValidProperty(String name, String value)
{
Preconditions.checkArgument(height != null);
return VIEWPORT_HEIGHT_REGEX.matcher(height).matches();
}

public static boolean isValidWidth(String width)
{
Preconditions.checkArgument(width != null);
return VIEWPORT_WIDTH_REGEX.matcher(width).matches();
Preconditions.checkNotNull(value);
switch (Preconditions.checkNotNull(name))
{
case "width":
return VIEWPORT_WIDTH_REGEX.matcher(value).matches();
case "height":
return VIEWPORT_HEIGHT_REGEX.matcher(value).matches();
default:
return true;
}
}

public static enum ParseError
Expand All @@ -51,7 +54,8 @@ public interface ErrorHandler

private final static class Builder
{
public ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
public ImmutableListMultimap.Builder<String, String> properties = ImmutableListMultimap
.builder();

public ViewportMeta build()
{
Expand Down Expand Up @@ -123,40 +127,38 @@ public ViewportMeta parse(CharSequence string)
}
else if (c == '=' || isASCIIWhitespace(c))
{
if (name.length() == 0)
{
error(ParseError.NAME_EMPTY, input.position());
return builder.build();
}
state = State.ASSIGN;
consume = false;
}
else if (c == ',' || c == ';')
{
if (name.length() == 0)
{
error(ParseError.LEADING_SEPARATOR, input.position());
}
else
{
error(ParseError.VALUE_EMPTY, input.position());
}
return builder.build();
state = State.SEPARATOR;
consume = false;
}
else
{
name.append(c);
}
break;
case ASSIGN:
if (isASCIIWhitespace(c))
if (name.length()==0) {
// assign state but no name was found
error(ParseError.NAME_EMPTY, input.position());
return builder.build();
}
else if (isASCIIWhitespace(c))
{
// skip whitespace
}
else if (c == '=')
{
state = State.VALUE;
}
else if (c == ',' || c == ';')
{
state = State.SEPARATOR;
consume = false;
}
else
{
// no '=' was matched (i.e. no value is set)
Expand Down Expand Up @@ -200,6 +202,11 @@ else if (c == '=')
consume = false;
}
case SEPARATOR:
if (name.length() == 0)
{
error(ParseError.LEADING_SEPARATOR, input.position());
return builder.build();
}
if (c == ',' || c == ';' || isASCIIWhitespace(c))
{
// skip repeating separators
Expand All @@ -215,13 +222,12 @@ else if (c == '=')
break;
}
}
if (value.length() != 0)
{
builder.withProperty(name.toString(), value.toString());
}
else if (name.length() != 0)
// finalize, report if unexpected final state
if (state == State.VALUE && value.length() == 0)
{
error(ParseError.VALUE_EMPTY, input.position());
} else {
builder.withProperty(name.toString(), value.toString());
}
if (state == State.SEPARATOR)
{
Expand All @@ -231,7 +237,7 @@ else if (name.length() != 0)
}
}

private final Map<String, String> properties;
private final ImmutableListMultimap<String, String> properties;

private ViewportMeta(Builder builder)
{
Expand All @@ -243,9 +249,14 @@ public boolean hasProperty(String name)
return properties.containsKey(name);
}

public String getValue(String name)
public List<String> getValues(String name)
{
return properties.get(name);
}

public ListMultimap<String, String> asMultimap()
{
return properties;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ HTM_054=Custom attribute namespace ("%1$s") must not include the string "%2$s" i
HTM_055=The "%1$s" element should not be used (discouraged construct)
HTM_056=Viewport metadata has no "%1$s" dimension (both "width" and "height" properties are required)
HTM_057=Viewport "%1$s" value must be a positive number or the keyword "device-%1$s"
HTM_058=HTML documents must be encoded in UTF-8, but UTF-16 was detected.
HTM_058=HTML documents must be encoded in UTF-8, but UTF-16 was detected.
HTM_059=Viewport "%1$s" property must not be defined more than once, but found values [%2$s].
HTM_060a=EPUB reading systems must ignore secondary viewport meta elements in fixed-layout documents; viewport declaration "%1$s" will be ignored.
HTM_060b=EPUB reading systems must ignore viewport meta elements in reflowable documents; viewport declaration "%1$s" will be ignored.

#media
MED_003=Picture "img" elements must reference core media type resources, but found resource "%1$s" of type "%2$s".
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/org/w3c/epubcheck/SharedParameterTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.w3c.epubcheck;

import java.util.Map;

import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Streams;

import io.cucumber.java.ParameterType;

public final class SharedParameterTypes
{

@ParameterType("\".*?\"")
/**
* Splits a string of key/value pairs into an {@link ImmutableListMultimap}.
* Key/value pairs are separated with ';', key and values are separated with
* '=', multiple values are separated with ','.
*
* <p>
* Note: no validation is performed. Callers must make sure the string is well
* formed.
* </p>
*
* @param mapping
* a string specifying key/value pairs
* @return a parsed map
*/
public ImmutableListMultimap<String, String> multimap(String mapping)
{
return Splitter.on(";").withKeyValueSeparator('=')
.split(mapping.substring(1, mapping.length() - 1)).entrySet().stream()
.collect(ImmutableListMultimap.flatteningToImmutableListMultimap(Map.Entry::getKey,
e -> Streams.stream(Splitter.on(',').split(e.getValue()))));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;

Expand All @@ -12,6 +13,7 @@
import org.w3c.epubcheck.util.microsyntax.ViewportMeta.ParseError;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;

import io.cucumber.java.ParameterType;
import io.cucumber.java.en.Then;
Expand All @@ -20,6 +22,8 @@
public class ViewportSteps
{

private ViewportMeta viewport;

public static final class TestErrorHandler implements ErrorHandler
{
public final List<ParseError> errors = new LinkedList<>();
Expand Down Expand Up @@ -58,7 +62,7 @@ public ParseError error(String error)
@When("parsing viewport {string}")
public void parseViewport(String content)
{
ViewportMeta.parse(content, handler);
viewport = ViewportMeta.parse(content, handler);
}

@Then("no error is returned")
Expand All @@ -67,6 +71,12 @@ public void assertValid()
assertThat("Unexpected errors", handler.errors(), is(empty()));
}

@Then("the parsed viewport equals {multimap}")
public void assertResult(ImmutableListMultimap<String, String> multimap)
{
assertThat(viewport.asMultimap(), is(equalTo(multimap)));
}

@Then("error {error} is returned")
public void assertError(ParseError error)
{
Expand Down
Loading