Repository: w3c/epubcheck
Pull request #1583 (2024-12-23 10:18:08)

fix: disallow `epub:prefix` on embedded SVG
Also disallow unknown `epub:*` attributes in SVG


Patch:
diff --git a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java
index 70c47db7..f5b23b7f 100644
--- a/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java
+++ b/src/main/java/com/adobe/epubcheck/messages/DefaultSeverities.java
@@ -204,6 +204,7 @@ class DefaultSeverities implements Severities
     severities.put(MessageId.OPF_007, Severity.WARNING);
     severities.put(MessageId.OPF_007a, Severity.ERROR);
     severities.put(MessageId.OPF_007b, Severity.ERROR);
+    severities.put(MessageId.OPF_007c, Severity.ERROR);
     severities.put(MessageId.OPF_008, Severity.SUPPRESSED);
     severities.put(MessageId.OPF_009, Severity.SUPPRESSED);
     severities.put(MessageId.OPF_010, Severity.ERROR);
diff --git a/src/main/java/com/adobe/epubcheck/messages/MessageId.java b/src/main/java/com/adobe/epubcheck/messages/MessageId.java
index 41befa2b..8c647198 100644
--- a/src/main/java/com/adobe/epubcheck/messages/MessageId.java
+++ b/src/main/java/com/adobe/epubcheck/messages/MessageId.java
@@ -198,6 +198,7 @@ public enum MessageId implements Comparable<MessageId>
   OPF_007("OPF-007"),
   OPF_007a("OPF-007a"),
   OPF_007b("OPF-007b"),
+  OPF_007c("OPF-007c"),
   OPF_008("OPF-008"),
   OPF_009("OPF-009"),
   OPF_010("OPF-010"),
diff --git a/src/main/java/com/adobe/epubcheck/vocab/VocabUtil.java b/src/main/java/com/adobe/epubcheck/vocab/VocabUtil.java
index c86e10da..6a2203c9 100644
--- a/src/main/java/com/adobe/epubcheck/vocab/VocabUtil.java
+++ b/src/main/java/com/adobe/epubcheck/vocab/VocabUtil.java
@@ -41,15 +41,15 @@ public final class VocabUtil
    * Parses a single property value and report validation errors on the fly.
    * 
    * @param value
-   *          the value to parse.
+   *        the value to parse.
    * @param vocabs
-   *          a map of prefix to vocabularies.
+   *        a map of prefix to vocabularies.
    * @param context
-   *          the validation context (report, locale, path, etc).
+   *        the validation context (report, locale, path, etc).
    * @param location
-   *          the location in the validated file.
+   *        the location in the validated file.
    * @return an {@link Optional} containing the property if it was parsed
-   *         successfully or nothing if there was a parsing error
+   *           successfully or nothing if there was a parsing error
    */
   public static Optional<Property> parseProperty(String value, Map<String, Vocab> vocabs,
       ValidationContext context, EPUBLocation location)
@@ -64,13 +64,13 @@ public final class VocabUtil
    * errors on the fly.
    * 
    * @param value
-   *          the value to parse.
+   *        the value to parse.
    * @param vocabs
-   *          a map of prefix to vocabularies.
+   *        a map of prefix to vocabularies.
    * @param context
-   *          the validation context (report, locale, path, etc).
+   *        the validation context (report, locale, path, etc).
    * @param location
-   *          the location in the validated file.
+   *        the location in the validated file.
    * @return
    */
   public static Set<Property> parsePropertyList(String value, Map<String, ? extends Vocab> vocabs,
@@ -158,17 +158,17 @@ public final class VocabUtil
    * vocabularies, and default vocabularies that cannot be re-declared.
    * 
    * @param value
-   *          the prefix declaration to parse.
+   *        the prefix declaration to parse.
    * @param predefined
-   *          a map of reserved prefixes to associated vocabularies.
+   *        a map of reserved prefixes to associated vocabularies.
    * @param known
-   *          a map of known URIs to known vocabularies.
+   *        a map of known URIs to known vocabularies.
    * @param forbidden
-   *          a set of URIs of default vocabularies that cannot be re-declared.
+   *        a set of URIs of default vocabularies that cannot be re-declared.
    * @param report
-   *          to report errors on the fly.
+   *        to report errors on the fly.
    * @param location
-   *          the location of the attribute in the source file.
+   *        the location of the attribute in the source file.
    * @return
    */
   public static Map<String, Vocab> parsePrefixDeclaration(String value,
@@ -192,6 +192,10 @@ public final class VocabUtil
         // must not declare a default vocab
         report.message(MessageId.OPF_007b, location, prefix);
       }
+      else if ("http://purl.org/dc/elements/1.1/".equals(uri))
+      {
+        report.message(MessageId.OPF_007c, location);
+      }
       else
       {
         if (predefined.containsKey(prefix)
diff --git a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties
index 4a168c1d..bbc50028 100644
--- a/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties
+++ b/src/main/resources/com/adobe/epubcheck/messages/MessageBundle.properties
@@ -120,6 +120,7 @@ OPF_006=Invalid prefix declaration: URI "%1$s" is not a valid URI.
 OPF_007=Re-declaration of reserved prefix "%1$s".
 OPF_007a=Invalid prefix mapping: prefix "_" must not be declared.
 OPF_007b=Invalid prefix mapping: default vocabulary "%1$s" must not be re-declared.
+OPF_007c=Invalid prefix mapping: a prefix to the Dublin Core /elements/1.1/ namespace must not be declared
 OPF_010=Error resolving reference: "%1$s".
 OPF_011=itemref can’t have both page-spread-right and page-spread-left properties.
 OPF_012=Item property "%1$s" is not defined for media type "%2$s".
diff --git a/src/main/resources/com/adobe/epubcheck/schema/30/epub-svg-30.rnc b/src/main/resources/com/adobe/epubcheck/schema/30/epub-svg-30.rnc
index f38bac08..5332da76 100644
--- a/src/main/resources/com/adobe/epubcheck/schema/30/epub-svg-30.rnc
+++ b/src/main/resources/com/adobe/epubcheck/schema/30/epub-svg-30.rnc
@@ -12,3 +12,6 @@ include "./mod/epub-xhtml-svg-mathml.rnc" {
 
 # Allow `body` element as a child of `foreignObject` 
 svg.foreignObject.inner |= body.elem 
+
+# Allow `epub:prefix` attribute on `svg` root
+svg.attrs &= epub.prefix.attr.ns?
diff --git a/src/main/resources/com/adobe/epubcheck/schema/30/mod/epub-svg-forgiving-inc.rnc b/src/main/resources/com/adobe/epubcheck/schema/30/mod/epub-svg-forgiving-inc.rnc
index c08a94ac..bda63460 100644
--- a/src/main/resources/com/adobe/epubcheck/schema/30/mod/epub-svg-forgiving-inc.rnc
+++ b/src/main/resources/com/adobe/epubcheck/schema/30/mod/epub-svg-forgiving-inc.rnc
@@ -35,7 +35,7 @@
   svg.generic.attr =
     attribute *
       - ( id
-        | epub:type
+        | epub:*
         ) { text }
 
   # Attribute list of elements where `epub:type` is allowed


Test patch:
diff --git a/src/test/resources/epub3/00-minimal/files/minimal.svg b/src/test/resources/epub3/00-minimal/files/minimal.svg
new file mode 100644
index 00000000..a4404608
--- /dev/null
+++ b/src/test/resources/epub3/00-minimal/files/minimal.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+	<title>Test</title>
+	<desc>Description</desc>
+	<circle r="10"/>
+</svg>
diff --git a/src/test/resources/epub3/00-minimal/minimal.feature b/src/test/resources/epub3/00-minimal/minimal.feature
index 9739651c..d776fdc8 100644
--- a/src/test/resources/epub3/00-minimal/minimal.feature
+++ b/src/test/resources/epub3/00-minimal/minimal.feature
@@ -23,3 +23,7 @@
   Scenario: Verify a minimal XHTML content document 
     When checking file 'minimal.xhtml'
     Then no errors or warnings are reported
+
+  Scenario: Verify a minimal SVG content document 
+    When checking file 'minimal.svg'
+    Then no errors or warnings are reported
diff --git a/src/test/resources/epub3/06-content-document/content-document-svg.feature b/src/test/resources/epub3/06-content-document/content-document-svg.feature
index 413677a9..20f65df4 100644
--- a/src/test/resources/epub3/06-content-document/content-document-svg.feature
+++ b/src/test/resources/epub3/06-content-document/content-document-svg.feature
@@ -51,6 +51,13 @@ Feature: EPUB 3 — Content Documents — SVG
     And the message contains '"epub:type" not allowed'
     And no other errors or warnings are reported
 
+  Scenario: Report an unknown `epub:*` attribute used in SVG
+    Given the reporting level is set to usage
+    When checking document 'unknown-epub-attribute-error.svg'
+    Then error RSC-005 is reported
+    And the message contains '"epub:unknown" not allowed'
+    And no other errors or warnings are reported
+
   
   ### 6.2.2 SVG requirements
 
diff --git a/src/test/resources/epub3/06-content-document/content-document-xhtml.feature b/src/test/resources/epub3/06-content-document/content-document-xhtml.feature
index c417d939..62321439 100644
--- a/src/test/resources/epub3/06-content-document/content-document-xhtml.feature
+++ b/src/test/resources/epub3/06-content-document/content-document-xhtml.feature
@@ -694,7 +694,6 @@ Feature: EPUB 3 — Content Documents — XHTML
     When checking document 'epubtype-reserved-vocab-valid.xhtml'
     Then no errors or warnings are reported
 
-  @spec @xref:sec-prefix-attr
   Scenario: Verify `epub:type` attribute with author-declared vocabulary
     When checking document 'epubtype-declared-vocab-valid.xhtml'
     Then no errors or warnings are reported
@@ -717,13 +716,6 @@ Feature: EPUB 3 — Content Documents — XHTML
     Then usage OPF-087 is reported 7 times
     And no other errors or warnings are reported
 
-  @spec @xref:sec-prefix-attr
-  Scenario: Report `epub:type` attribute with a semantic from an undeclared vocabulary
-    When checking document 'epubtype-prefix-undeclared-error.xhtml'
-    Then error OPF-028 is reported
-    And no other errors or warnings are reported
-
-
   #### 6.1.3.2 RDFa
 
   @spec @xref:sec-xhtml-rdfa
diff --git a/src/test/resources/epub3/06-content-document/files/epubtype-declared-vocab-valid.xhtml b/src/test/resources/epub3/06-content-document/files/epubtype-declared-vocab-valid.xhtml
index 97e75680..993e1fa8 100644
--- a/src/test/resources/epub3/06-content-document/files/epubtype-declared-vocab-valid.xhtml
+++ b/src/test/resources/epub3/06-content-document/files/epubtype-declared-vocab-valid.xhtml
@@ -6,10 +6,6 @@
 		<title>epub:type with declared vocabulary</title>
 	</head>
 	<!-- this property is part of a declared vocab -->
-	<!-- 
-    note: 'subchatper' is deprecated in the SSV,
-          but not here as it comes from another vocab
-  -->
 	<body epub:type="my:subchapter">
 		<h1>Test</h1>
 	</body>
diff --git a/src/test/resources/epub3/06-content-document/files/unknown-epub-attribute-error.svg b/src/test/resources/epub3/06-content-document/files/unknown-epub-attribute-error.svg
new file mode 100644
index 00000000..aee63074
--- /dev/null
+++ b/src/test/resources/epub3/06-content-document/files/unknown-epub-attribute-error.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:epub="http://www.idpf.org/2007/ops" epub:unknown=""
+	version="1.1">
+	<title>Test</title>
+	<desc>Description</desc>
+	<circle r="10"/>
+</svg>
diff --git a/src/test/resources/epub3/09-media-overlays/media-overlays.feature b/src/test/resources/epub3/09-media-overlays/media-overlays.feature
index 9d63ea91..34e16d54 100644
--- a/src/test/resources/epub3/09-media-overlays/media-overlays.feature
+++ b/src/test/resources/epub3/09-media-overlays/media-overlays.feature
@@ -188,12 +188,6 @@ Feature: EPUB 3 — Media Overlays
     When checking document 'epubtype-prefix-declared-valid.smil'
     Then no errors or warnings are reported
 
-  Scenario: Report an epub:type property with an undeclared prefix
-    When checking document 'epubtype-prefix-undeclared-error.smil'
-    Then error OPF-028 is reported
-    And the message contains 'Undeclared prefix: "my"'
-    And no other errors or warnings are reported
-
   Scenario: Allow unknown epub:type properties in the default vocabulary
     Given the reporting level set to usage
     When checking document 'epubtype-unknown-usage.smil'
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.smil b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.smil
new file mode 100644
index 00000000..61b593eb
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.smil
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<smil xmlns="http://www.w3.org/ns/SMIL" xmlns:epub="http://www.idpf.org/2007/ops"
+    prefix="my: https://example.org/vocab/#" version="3.0">
+    <body epub:type="my:type">
+        <par id="par1">
+            <text src="chapter1.xhtml#text1"/>
+            <audio src="chapter1.mp3" clipBegin="0s" clipEnd="10s"/>
+        </par>
+        <par id="par2">
+            <text src="chapter1.xhtml#text2"/>
+            <audio src="chapter1.mp3" clipBegin="10s" clipEnd="20s"/>
+        </par>
+    </body>
+</smil>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.xhtml
new file mode 100644
index 00000000..2ed20a4e
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-no-namespace-unrecognized-error.xhtml
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	prefix="my: https://example.org/vocab/#"
+	xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body epub:type="my:type">
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-head-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-head-error.xhtml
new file mode 100644
index 00000000..d2ee6dde
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-head-error.xhtml
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en"
+	lang="en">
+	<head epub:prefix="ex1: http://example.org/vocab1
+		ex2: http://example.org/vocab2">
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-svg-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-svg-error.xhtml
new file mode 100644
index 00000000..20e0e45f
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-on-svg-error.xhtml
@@ -0,0 +1,17 @@
+<!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>Test</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+		<svg xmlns="http://www.w3.org/2000/svg" epub:prefix="my: http://example.org/vocab/">
+			<title>SVG</title>
+			<desc>Description</desc>
+			<circle r="10"/>
+		</svg>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-syntax-error.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-syntax-error.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-syntax-error.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-attribute-syntax-error.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-valid.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-valid.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.smil b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.smil
new file mode 100644
index 00000000..6e92caad
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.smil
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<smil xmlns="http://www.w3.org/ns/SMIL" xmlns:epub="http://www.idpf.org/2007/ops"
+    epub:prefix="my: https://example.org/vocab/#" version="3.0">
+    <body>
+        <par id="par1">
+            <text src="chapter1.xhtml#text1"/>
+            <audio src="chapter1.mp3" clipBegin="0s" clipEnd="10s"/>
+        </par>
+        <par id="par2">
+            <text src="chapter1.xhtml#text2"/>
+            <audio src="chapter1.mp3" clipBegin="10s" clipEnd="20s"/>
+        </par>
+    </body>
+</smil>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.svg b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.svg
new file mode 100644
index 00000000..ac8cd1fc
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" 
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="ex1: http://example.org/vocab1
+	             ex2: http://example.org/vocab2"
+	version="1.1">
+	<title>Test</title>
+	<desc>Description</desc>
+	<circle r="10"/>
+</svg>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.xhtml
new file mode 100644
index 00000000..253a9273
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-attribute-valid.xhtml
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="ex1: http://example.org/vocab1
+               ex2: http://example.org/vocab2"
+	xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-in-epub-type-valid.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-in-epub-type-valid.xhtml
new file mode 100644
index 00000000..c36c3514
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-in-epub-type-valid.xhtml
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="my: https://example.org/vocab/#"
+	xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<!-- 
+    note: 'subchatper' is deprecated in the SSV,
+          but not here as it comes from another vocab
+  -->
+	<body epub:type="my:subchapter">
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-dc-elements-error.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-dc-elements-error.opf
new file mode 100644
index 00000000..f848b309
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-dc-elements-error.opf
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="uid"
+    xmlns:dc="http://purl.org/dc/elements/1.1/"
+    prefix="dc: http://purl.org/dc/elements/1.1/">
+    <metadata>
+        <dc:title>Title</dc:title>
+        <dc:language>en</dc:language>
+        <dc:identifier id="uid">NOID</dc:identifier>
+        <meta property="dcterms:modified">2019-01-01T12:00:00Z</meta>
+    </metadata>
+    <manifest>
+        <item id="t001" href="contents.xhtml" properties="nav" media-type="application/xhtml+xml"/>
+    </manifest>
+    <spine>
+        <itemref idref="t001"/>
+    </spine>
+</package>
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-default-vocabs-error.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-default-vocabs-error.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-default-vocabs-error.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-mapping-default-vocabs-error.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-default-vocaps-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-default-vocaps-error.xhtml
new file mode 100644
index 00000000..d093dfd2
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-default-vocaps-error.xhtml
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="my: https://example.org/vocab/#"
+	xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Valid `epub:prefix` attribute</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-reserved-explicit-valid.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-reserved-valid.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-reserved-explicit-valid.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-mapping-reserved-valid.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-reserved-valid.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-reserved-valid.xhtml
new file mode 100644
index 00000000..c1383118
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-mapping-reserved-valid.xhtml
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="msv:   http://www.idpf.org/epub/vocab/structure/magazine/#
+	             prism: http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.htm#"
+	xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-reserved-overridden-warning.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-overridden-warning.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-reserved-overridden-warning.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-reserved-overridden-warning.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-overridden-warning.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-overridden-warning.xhtml
new file mode 100644
index 00000000..3fd2bddc
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-overridden-warning.xhtml
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en"
+	epub:prefix="msv:   http://example.org/msv/
+	             prism: http://example.org/prism/" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body>
+		<h1 epub:type="prism:title">Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-prism-undeclared-valid.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-prism-undeclared-valid.xhtml
new file mode 100644
index 00000000..54e9f6d1
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-prism-undeclared-valid.xhtml
@@ -0,0 +1,12 @@
+<!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>Test</title>
+	</head>
+	<body>
+		<h1 epub:type="prism:title">Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-schema-not-declared-valid.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-reserved-schema-undeclared-valid.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-schema-not-declared-valid.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-reserved-schema-undeclared-valid.opf
diff --git a/src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-missing-error.opf b/src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.opf
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/files/property-prefix-declaration-missing-error.opf
rename to src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.opf
diff --git a/src/test/resources/epub3/09-media-overlays/files/epubtype-prefix-undeclared-error.smil b/src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.smil
similarity index 100%
rename from src/test/resources/epub3/09-media-overlays/files/epubtype-prefix-undeclared-error.smil
rename to src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.smil
diff --git a/src/test/resources/epub3/06-content-document/files/epubtype-prefix-undeclared-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.xhtml
similarity index 100%
rename from src/test/resources/epub3/06-content-document/files/epubtype-prefix-undeclared-error.xhtml
rename to src/test/resources/epub3/D-vocabularies/files/prefix-undeclared-error.xhtml
diff --git a/src/test/resources/epub3/D-vocabularies/files/prefix-underscore-error.xhtml b/src/test/resources/epub3/D-vocabularies/files/prefix-underscore-error.xhtml
new file mode 100644
index 00000000..2099c509
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/files/prefix-underscore-error.xhtml
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"
+	epub:prefix="_: http://example.org/vocab#" xml:lang="en" lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<title>Test</title>
+	</head>
+	<body>
+		<h1>Loomings</h1>
+		<p>Call me Ishmael.</p>
+	</body>
+</html>
diff --git a/src/test/resources/epub3/D-vocabularies/media-overlays.feature b/src/test/resources/epub3/D-vocabularies/media-overlays-vocab.feature
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/media-overlays.feature
rename to src/test/resources/epub3/D-vocabularies/media-overlays-vocab.feature
diff --git a/src/test/resources/epub3/D-vocabularies/meta-properties.feature b/src/test/resources/epub3/D-vocabularies/meta-properties-vocab.feature
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/meta-properties.feature
rename to src/test/resources/epub3/D-vocabularies/meta-properties-vocab.feature
diff --git a/src/test/resources/epub3/D-vocabularies/metadata-link.feature b/src/test/resources/epub3/D-vocabularies/metadata-link-vocab.feature
similarity index 100%
rename from src/test/resources/epub3/D-vocabularies/metadata-link.feature
rename to src/test/resources/epub3/D-vocabularies/metadata-link-vocab.feature
diff --git a/src/test/resources/epub3/D-vocabularies/vocabularies.feature b/src/test/resources/epub3/D-vocabularies/vocabularies.feature
new file mode 100644
index 00000000..44442746
--- /dev/null
+++ b/src/test/resources/epub3/D-vocabularies/vocabularies.feature
@@ -0,0 +1,184 @@
+Feature: EPUB 3 — Vocabularies — Vocabulary association
+
+
+	Checks conformance to the "Vocabulary association" section of the EPUB 3.3 specification:
+	https://www.w3.org/TR/epub-33/#sec-vocab-assoc
+
+
+	Background:
+		Given EPUB test files located at '/epub3/D-vocabularies/files/'
+		And EPUBCheck with default settings
+
+
+	# D.1 Vocabulary association mechanisms
+
+	## D.1.3 Default vocabularies
+
+	Rule: EPUB creators MUST NOT assign a prefix to the URLs associated with default vocabularies using the `prefix` attribute.
+
+		@spec @xref:sec-default-vocab
+		Example: Report `prefix` re-mapping default vocabularies in a package document
+			When checking file 'prefix-mapping-default-vocabs-error.opf'
+			Then error OPF-007b is reported 4 times (once for each default vocabulary)
+			And no other errors or warnings are reported
+
+		@spec @xref:sec-default-vocab
+		Example: Report `prefix` re-mapping default vocabularies in XHTML
+			When checking file 'prefix-mapping-default-vocabs-error.opf'
+			Then error OPF-007b is reported 4 times (once for each default vocabulary)
+			And no errors or warnings are reported
+
+
+	## D.1.4 The prefix attribute
+
+	Rule: The value of the `prefix` attribute is a whitespace-separated list of one or more prefix-to-URL mappings
+
+		@spec @xref:sec-prefix-attr
+		Example: Allow valid `prefix` attribute mappings in package documents
+			When checking file 'prefix-attribute-valid.opf'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Allow valid `prefix` attribute mappings in XHTML content documents
+			When checking file 'prefix-attribute-valid.xhtml'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Allow valid `prefix` attribute mappings in media overlays
+			When checking file 'prefix-attribute-valid.smil'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Allow valid `prefix` attribute mappings in SVG content documents
+			When checking file 'prefix-attribute-valid.svg'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Report syntax errors in the `prefix` attribute value
+			When checking file 'prefix-attribute-syntax-error.opf'
+			Then error OPF-004c is reported 2 times (the test file contains 2 syntax errors)
+			And no other errors or warnings are reported
+			Then no errors or warnings are reported
+
+
+	Rule: With the exception of reserved prefixes, EPUB creators MUST declare all prefixes used in a document
+
+		@spec @xref:sec-prefix-attr
+		Example: Allow declared prefixes used in the XHTML `epub:type` attribute
+			When checking document 'prefix-in-epub-type-valid.xhtml'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Scenario: Report undeclared prefixes used in the XHTML `epub:type` attribute
+			When checking document 'prefix-undeclared-error.xhtml'
+			Then error OPF-028 is reported
+			And no other errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Report undeclared prefixes used in package metadata properties
+			When checking file 'prefix-undeclared-error.opf'
+			Then error OPF-028 is reported
+			And no errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Scenario: Report undeclared prefixes used in a Media Overlays `epub:type` attribute
+			When checking document 'prefix-undeclared-error.smil'
+			Then error OPF-028 is reported
+			And no other errors or warnings are reported
+
+
+	Rule: EPUB creators MUST declare the `prefix` attribute in the namespace `http://www.idpf.org/2007/ops` in EPUB content documents and media overlay documents.
+
+		@spec @xref:sec-prefix-attr
+		Example: Report a `epub:type` using a prefix declared in a `prefix` attribute with no namespace, in an XHTML content document.
+			When checking file 'prefix-attribute-no-namespace-unrecognized-error.xhtml'
+			Then error OPF-028 is reported (undeclared prefix)
+			And no other errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Report an `epub:type` using a prefix declared in a `prefix` attribute with no namespace, in a media overlay.
+			When checking file 'prefix-attribute-no-namespace-unrecognized-error.smil'
+			Then error RSC-005 is reported
+			And the message contains 'attribute "prefix" not allowed here'
+			Then error OPF-028 is reported (undeclared prefix)
+			And no other errors or warnings are reported
+
+
+	Rule: EPUB creators MUST only specify the `prefix` attribute on the root element of the respective format.
+
+		Note that for SVG embedded by inclusion, prefixes MUST be declared on the root html element.
+
+		@spec @xref:sec-prefix-attr
+		Example: Report a `prefix` attribute on an XHTML `head` element
+			When checking file 'prefix-attribute-on-head-error.xhtml'
+			Then error RSC-005 is reported
+			And the message contains 'attribute "epub:prefix" not allowed here'
+			And no other errors or warnings are reported
+
+		@spec @xref:sec-prefix-attr
+		Example: Report a `prefix` attribute on an XHTML embedded `svg` element
+			When checking file 'prefix-attribute-on-svg-error.xhtml'
+			Then error RSC-005 is reported
+		  And the message contains 'attribute "epub:prefix" not allowed here'
+			And no other errors or warnings are reported
+
+
+	Rule: EPUB creators MUST NOT declare the prefix '_'.
+
+		@spec @xref:sec-prefix-attr
+		Example: Report a declaration of the prefix '_' in XHTML content documents
+			When checking file 'prefix-underscore-error.xhtml'
+			Then error OPF-007a is reported
+			And no other errors or warnings are reported
+
+
+	Rule: EPUB creators MUST NOT declare a prefix for the Dublin Core `/elements/1.1/` namespace.
+
+		@spec @xref:sec-prefix-attr
+		Example: Report a prefix mapping to the Dublic Core elements namespace
+			When checking file 'prefix-mapping-dc-elements-error.opf'
+			Then error OPF-007c is reported
+			And no other errors or warnings are reported
+
+
+	## D.1.5 Reserved prefixes
+
+	Rule: (IMPLIED) EPUB creators can explicitly declare reserved prefixes.
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Allow explicit declarations of reserved prefixes in package documents
+			When checking file 'prefix-mapping-reserved-valid.opf'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Allow explicit declarations of reserved prefixes in content documents
+			When checking file 'prefix-mapping-reserved-valid.xhtml'
+			Then no errors or warnings are reported
+
+
+	Rule: EPUB creators MAY use reserved prefixes in attributes that expect a property value without declaring them in a `prefix` attribute.
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Allow the reserved 'schema' prefix in package metadata properties
+			When checking file 'prefix-reserved-schema-undeclared-valid.opf'
+			Then no errors or warnings are reported
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Allow the reserved 'prism' prefix in XHTML `epub:type` attributes
+			When checking file 'prefix-reserved-prism-undeclared-valid.xhtml'
+			Then no errors or warnings are reported
+
+
+	Rule: EPUB creators SHOULD NOT override reserved prefixes in the `prefix` attribute.
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Report overriding of reserved prefixes in package documents
+			When checking file 'prefix-reserved-overridden-warning.opf'
+			Then warning OPF-007 is reported 8 times (once for each reserved prefix)
+			And no other errors or warnings are reported
+
+		@spec @xref:sec-reserved-prefixes
+		Example: Report overriding of reserved prefixes in XHTML content documents
+			When checking file 'prefix-reserved-overridden-warning.xhtml'
+			Then warning OPF-007 is reported 2 times (once for each reserved prefix)
+			And no other errors or warnings are reported
diff --git a/src/test/resources/epub3/D-vocabularies/vocabulary-association.feature b/src/test/resources/epub3/D-vocabularies/vocabulary-association.feature
deleted file mode 100644
index 20deee10..00000000
--- a/src/test/resources/epub3/D-vocabularies/vocabulary-association.feature
+++ /dev/null
@@ -1,54 +0,0 @@
-Feature: EPUB 3 — Vocabularies — Vocabulary association
-
-
-  Checks conformance to the "Vocabulary association" section of the EPUB 3.3 specification:
-    https://www.w3.org/TR/epub-33/#sec-vocab-assoc
-
-
-  Background: 
-    Given EPUB test files located at '/epub3/D-vocabularies/files/'
-    And EPUBCheck with default settings
-
-
-  # D.1 Vocabulary association mechanisms
-
-  ## D.1.4 The prefix attribute
-  
-  @spec @xref:sec-prefix-attr
-  Scenario: the 'prefix' attribute can be used to define new prefix mappings 
-    When checking file 'property-prefix-declaration-valid.opf'
-    Then no errors or warnings are reported
-
-  @spec @xref:sec-prefix-attr
-  Scenario: syntax errors in the 'prefix' attribute are reported
-    When checking file 'property-prefix-declaration-syntax-error.opf'
-    Then error OPF-004c is reported 2 times (the test file contains 2 syntax errors)
-    And no other errors or warnings are reported
-
-  @spec @xref:sec-prefix-attr
-  Scenario: default vocabularies must not be assigned a prefix
-    When checking file 'property-prefix-declaration-default-vocabs-error.opf'
-    Then error OPF-007b is reported 4 times (once for each default vocabulary)
-    And no other errors or warnings are reported
-
-  @spec @xref:sec-prefix-attr
-  Scenario: A metadata property with an unknown prefix is reported
-    When checking file 'property-prefix-declaration-missing-error.opf'
-    Then error OPF-028 is reported
-    And no errors or warnings are reported
-
-
-  ## D.1.5 Reserved prefixes
-  
-  Scenario: reserved prefixes can be explicitly declared
-    When checking file 'property-prefix-declaration-reserved-explicit-valid.opf'
-    Then no errors or warnings are reported
-
-  Scenario: reserved prefixes should not be overridden to other vocabularies 
-    When checking file 'property-prefix-declaration-reserved-overridden-warning.opf'
-    Then warning OPF-007 is reported 8 times (once for each reserved prefix)
-    And no other errors or warnings are reported
-
-  Scenario: The 'schema' prefix can be used in metadata properties without being declared
-    When checking file 'property-prefix-schema-not-declared-valid.opf'
-    Then no errors or warnings are reported