Skip to content

Commit

Permalink
feat: update viewport meta element requirements
Browse files Browse the repository at this point in the history
This commit implements the post-CR changes on the requirements for the
viewport meta element used to define the ICB in fixed-layout documents.

Viewport syntax:
- properties can now have several values
- the `ViewportMeta` object now return a list of string values when
  looking up values for a name.

ICB definition checks:
- only the first `viewport` `meta` element of fixed-layout documents
  is checked
- new `HTM-059` error is reported when `height` or `width` dimensions
  are specified more than once
- new `HTM-060a` usage is reported for any subsequent `viewport` `meta`
  element found in fixed-layout documents
- new `HTM-060b` usage is reported fo any `viewport` `meta` element
  found in reflowable documents

Fix #1401, Fix #1449
  • Loading branch information
rdeltour committed Dec 13, 2022
1 parent 5fca49f commit 9f75a1d
Show file tree
Hide file tree
Showing 35 changed files with 396 additions and 113 deletions.
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=600,height=1200" />
<meta name="viewport" content="width=600,height=1200" />
<meta name="viewport" content="width=600,height=1200,width=device-width,height=device-height" />
<title>Minimal EPUB</title>
</head>
<body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=600,height=" />
<meta name="viewport" content="width=600,height" />
<title>Minimal EPUB</title>
</head>
<body>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="inital-scale=1.0" />
<!-- ICB dimensions must be defined in the first viewport meta element -->
<meta name="viewport" content="width=600,height=600" />
<title>Minimal EPUB</title>
</head>
<body>
<h1>Loomings</h1>
<p>Call me Ishmael.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -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>
Loading

0 comments on commit 9f75a1d

Please sign in to comment.