diff --git a/pkg/manifest/a11y.go b/pkg/manifest/a11y.go
index cf443064..d28a55d3 100644
--- a/pkg/manifest/a11y.go
+++ b/pkg/manifest/a11y.go
@@ -19,6 +19,7 @@ type A11y struct {
AccessModesSufficient [][]A11yPrimaryAccessMode `json:"accessModeSufficient,omitempty"` // A list of single or combined accessModes that are sufficient to understand all the intellectual content of a resource.
Features []A11yFeature `json:"feature,omitempty"` // Content features of the resource, such as accessible media, alternatives and supported enhancements for accessibility.
Hazards []A11yHazard `json:"hazard,omitempty"` // A characteristic of the described resource that is physiologically dangerous to some users.
+ Exemptions []A11yExemption `json:"exemption,omitempty"` // Justifications for non-conformance based on exemptions in a given jurisdiction.
}
// NewA11y creates a new empty A11y.
@@ -29,6 +30,7 @@ func NewA11y() A11y {
AccessModesSufficient: [][]A11yPrimaryAccessMode{},
Features: []A11yFeature{},
Hazards: []A11yHazard{},
+ Exemptions: []A11yExemption{},
}
}
@@ -130,6 +132,12 @@ func A11yFromJSON(rawJSON map[string]interface{}) (*A11y, error) {
}
a.Hazards = A11yHazardsFromStrings(hazards)
+ examptions, err := parseSliceOrString(rawJSON["exemption"], true)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed unmarshalling 'exemption'")
+ }
+ a.Exemptions = A11yExemptionsFromStrings(examptions)
+
return a, nil
}
@@ -433,6 +441,21 @@ func A11yHazardsFromStrings(strings []string) []A11yHazard {
})
}
+// A11yExemption is a justification for non-conformance based on an exemption in a given jurisdiction.
+type A11yExemption string
+
+const (
+ A11yExemptionEAASisproportionateBurden A11yExemption = "eaa-disproportionate-burden"
+ A11yExemptionEAAFundamentalAlteration A11yExemption = "eaa-fundamental-alteration"
+ A11yExemptionEAAMicroenterprise A11yExemption = "eaa-microenterprise"
+)
+
+func A11yExemptionsFromStrings(strings []string) []A11yExemption {
+ return fromStrings(strings, func(str string) A11yExemption {
+ return A11yExemption(str)
+ })
+}
+
func fromStrings[T any](strings []string, transform func(string) T) []T {
res := make([]T, 0, len(strings))
for _, s := range strings {
diff --git a/pkg/manifest/a11y_test.go b/pkg/manifest/a11y_test.go
index cb979329..c569cad6 100644
--- a/pkg/manifest/a11y_test.go
+++ b/pkg/manifest/a11y_test.go
@@ -26,7 +26,8 @@ func TestA11yUnmarshalFullJSON(t *testing.T) {
"accessMode": ["auditory", "chartOnVisual"],
"accessModeSufficient": [["visual", "tactile"]],
"feature": ["readingOrder", "alternativeText"],
- "hazard": ["flashing", "motionSimulation"]
+ "hazard": ["flashing", "motionSimulation"],
+ "exemption": ["eaa-fundamental-alteration", "eaa-microenterprise"]
}`), &m))
assert.Equal(t, A11y{
ConformsTo: []A11yProfile{
@@ -57,6 +58,10 @@ func TestA11yUnmarshalFullJSON(t *testing.T) {
A11yHazardFlashing,
A11yHazardMotionSimulation,
},
+ Exemptions: []A11yExemption{
+ A11yExemptionEAAFundamentalAlteration,
+ A11yExemptionEAAMicroenterprise,
+ },
}, m, "unmarshalled JSON object should be equal to A11y object")
}
@@ -101,6 +106,7 @@ func TestA11yMarshalMinimalJSON(t *testing.T) {
AccessModesSufficient: [][]A11yPrimaryAccessMode{},
Features: []A11yFeature{},
Hazards: []A11yHazard{},
+ Exemptions: []A11yExemption{},
}
data, err := json.Marshal(m)
assert.NoError(t, err)
@@ -139,12 +145,16 @@ func TestA11yMarshalFullJSON(t *testing.T) {
A11yHazardFlashing,
A11yHazardMotionSimulation,
},
+ Exemptions: []A11yExemption{
+ A11yExemptionEAAFundamentalAlteration,
+ A11yExemptionEAAMicroenterprise,
+ },
}
data, err := json.Marshal(m)
assert.NoError(t, err)
assert.Equal(
t,
data,
- []byte(`{"conformsTo":["http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a","https://profile2"],"certification":{"certifiedBy":"company1","credential":"credential1","report":"https://report1"},"summary":"Summary","accessMode":["auditory","chartOnVisual"],"accessModeSufficient":[["auditory"],["visual","tactile"],["visual"]],"feature":["readingOrder","alternativeText"],"hazard":["flashing","motionSimulation"]}`),
+ []byte(`{"conformsTo":["http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a","https://profile2"],"certification":{"certifiedBy":"company1","credential":"credential1","report":"https://report1"},"summary":"Summary","accessMode":["auditory","chartOnVisual"],"accessModeSufficient":[["auditory"],["visual","tactile"],["visual"]],"feature":["readingOrder","alternativeText"],"hazard":["flashing","motionSimulation"],"exemption":["eaa-fundamental-alteration","eaa-microenterprise"]}`),
)
}
diff --git a/pkg/parser/epub/metadata.go b/pkg/parser/epub/metadata.go
index 85ffc7d2..6a2c82d6 100644
--- a/pkg/parser/epub/metadata.go
+++ b/pkg/parser/epub/metadata.go
@@ -646,6 +646,7 @@ func (m PubMetadataAdapter) Accessibility() *manifest.A11y {
a11y.AccessModesSufficient = m.a11yAccessModesSufficient()
a11y.Features = m.a11yFeatures()
a11y.Hazards = m.a11yHazards()
+ a11y.Exemptions = m.a11yExemptions()
if a11y.IsEmpty() {
return nil
@@ -785,6 +786,15 @@ func (m PubMetadataAdapter) a11yHazards() []manifest.A11yHazard {
return hazards
}
+func (m PubMetadataAdapter) a11yExemptions() []manifest.A11yExemption {
+ values := m.Values(VocabularyA11Y + "exemption")
+ hazards := make([]manifest.A11yExemption, len(values))
+ for i, v := range values {
+ hazards[i] = manifest.A11yExemption(v)
+ }
+ return hazards
+}
+
func (m *PubMetadataAdapter) seedBelongsToData() {
if m._belongsToSeeded {
return
diff --git a/pkg/parser/epub/metadata_test.go b/pkg/parser/epub/metadata_test.go
index d04de26f..d7c91ab9 100644
--- a/pkg/parser/epub/metadata_test.go
+++ b/pkg/parser/epub/metadata_test.go
@@ -323,6 +323,7 @@ func TestMetadataEPUB2Accessibility(t *testing.T) {
}
e.Features = []manifest.A11yFeature{manifest.A11yFeatureStructuralNavigation, manifest.A11yFeatureAlternativeText}
e.Hazards = []manifest.A11yHazard{manifest.A11yHazardMotionSimulation, manifest.A11yHazardNoSoundHazard}
+ e.Exemptions = []manifest.A11yExemption{manifest.A11yExemptionEAAMicroenterprise, manifest.A11yExemptionEAAFundamentalAlteration}
assert.Equal(t, &e, m.Accessibility)
assert.Nil(t, m.OtherMetadata["accessibility"])
}
@@ -345,6 +346,7 @@ func TestMetadataEPUB3Accessibility(t *testing.T) {
}
e.Features = []manifest.A11yFeature{manifest.A11yFeatureStructuralNavigation, manifest.A11yFeatureAlternativeText}
e.Hazards = []manifest.A11yHazard{manifest.A11yHazardMotionSimulation, manifest.A11yHazardNoSoundHazard}
+ e.Exemptions = []manifest.A11yExemption{manifest.A11yExemptionEAAMicroenterprise, manifest.A11yExemptionEAAFundamentalAlteration}
assert.Equal(t, &e, m.Accessibility)
assert.Nil(t, m.OtherMetadata["accessibility"])
}
diff --git a/pkg/parser/epub/testdata/package/accessibility-epub2.opf b/pkg/parser/epub/testdata/package/accessibility-epub2.opf
index 89264313..61ed8f3c 100644
--- a/pkg/parser/epub/testdata/package/accessibility-epub2.opf
+++ b/pkg/parser/epub/testdata/package/accessibility-epub2.opf
@@ -20,6 +20,9 @@
+
+ eaa-microenterprise
+ eaa-fundamental-alteration
diff --git a/pkg/parser/epub/testdata/package/accessibility-epub3.opf b/pkg/parser/epub/testdata/package/accessibility-epub3.opf
index 17d4db60..588fce84 100644
--- a/pkg/parser/epub/testdata/package/accessibility-epub3.opf
+++ b/pkg/parser/epub/testdata/package/accessibility-epub3.opf
@@ -24,6 +24,9 @@
+ eaa-microenterprise
+ eaa-fundamental-alteration
+