Skip to content

Commit b192f08

Browse files
authored
Fixes for A11y metadata (#208)
1 parent 6cc1f5e commit b192f08

File tree

4 files changed

+121
-34
lines changed

4 files changed

+121
-34
lines changed

pkg/manifest/contributor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
// Contributor
1111
// https://github.com/readium/webpub-manifest/tree/master/contexts/default#contributors
12-
// https://github.com/readium/webpub-manifest/schema/contributor-object.schema.json
12+
// https://github.com/readium/webpub-manifest/blob/master/schema/contributor-object.schema.json
1313
type Contributor struct {
1414
LocalizedName LocalizedString `json:"name" validate:"required"` // The name of the contributor.
1515
LocalizedSortAs *LocalizedString `json:"sortAs,omitempty"` // The string used to sort the name of the contributor.

pkg/parser/epub/metadata.go

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func (m MetadataParser) parseMetaElement(element *xmlquery.Node) *MetadataItem {
205205
id: element.SelectAttr("id"),
206206
}
207207
} else {
208-
propName := strings.TrimSpace(element.SelectAttr("property"))
208+
propName := strings.TrimSpace(property)
209209
if propName == "" {
210210
return nil
211211
}
@@ -238,7 +238,7 @@ func (m MetadataParser) parseDcElement(element *xmlquery.Node) *MetadataItem {
238238
}
239239

240240
data := strings.ToLower(element.Data)
241-
propName := VocabularyDCTerms + data
241+
propName := VocabularyDCTerms + element.Data
242242
switch data {
243243
case "creator", "contributor", "publisher":
244244
c := m.contributorWithLegacyAttr(element, propName, propValue)
@@ -399,13 +399,18 @@ func (m metadataAdapter) FirstValue(property string) string {
399399
return item.value
400400
}
401401

402+
func itemsValues(items []MetadataItem) []string {
403+
values := make([]string, len(items))
404+
for i, item := range items {
405+
values[i] = item.value
406+
}
407+
return values
408+
}
409+
402410
func (m metadataAdapter) Values(property string) []string {
403411
var values []string
404412
if items, ok := m.items[property]; ok {
405-
values = make([]string, len(items))
406-
for i, item := range items {
407-
values[i] = item.value
408-
}
413+
values = itemsValues(items)
409414
}
410415
return values
411416
}
@@ -636,18 +641,38 @@ func (m PubMetadataAdapter) LocalizedSortAs() *manifest.LocalizedString {
636641

637642
func (m PubMetadataAdapter) Accessibility() *manifest.A11y {
638643
a11y := manifest.NewA11y()
639-
a11y.ConformsTo = m.a11yConformsTo()
640-
a11y.Certification = m.a11yCertification()
641-
a11y.Summary = m.a11ySummary()
642-
a11y.AccessModes = m.a11yAccessModes()
643-
a11y.AccessModesSufficient = m.a11yAccessModesSufficient()
644-
a11y.Features = m.a11yFeatures()
645-
a11y.Hazards = m.a11yHazards()
646-
a11y.Exemptions = m.a11yExemptions()
644+
ct, refinedA11y := m.a11yConformsTo()
645+
a11y.ConformsTo = ct
646+
647+
certifierItem, _ := m.First(VocabularyA11Y + "certifiedBy")
648+
a11y.Certification = m.a11yCertification(certifierItem)
649+
650+
a11y.Summary = m.FirstValue(VocabularySchema + "accessibilitySummary")
651+
652+
modeValues := m.Values(VocabularySchema + "accessMode")
653+
a11y.AccessModes = valuesToAccessModes(modeValues)
654+
655+
sufficientValues := m.Values(VocabularySchema + "accessModeSufficient")
656+
a11y.AccessModesSufficient = valuesToAccessModesSufficient(sufficientValues)
657+
658+
featureValues := m.Values(VocabularySchema + "accessibilityFeature")
659+
a11y.Features = valuesToA11yFeatures(featureValues)
660+
661+
hazardValues := m.Values(VocabularySchema + "accessibilityHazard")
662+
a11y.Hazards = valuesToA11yHazards(hazardValues)
663+
664+
exemptionValues := m.Values(VocabularyA11Y + "exemption")
665+
a11y.Exemptions = valuesToA11yExemptions(exemptionValues)
647666

648667
if a11y.IsEmpty() {
649668
return nil
650669
}
670+
671+
// ConformsTo refinements merge with the main a11y data
672+
if !refinedA11y.IsEmpty() {
673+
a11y.Merge(&refinedA11y)
674+
}
675+
651676
return &a11y
652677
}
653678

@@ -670,13 +695,32 @@ func (m PubMetadataAdapter) TDM() *manifest.TDM {
670695
return tdm
671696
}
672697

673-
func (m PubMetadataAdapter) a11yConformsTo() []manifest.A11yProfile {
698+
func (m PubMetadataAdapter) a11yConformsTo() ([]manifest.A11yProfile, manifest.A11y) {
674699
profiles := manifest.A11yProfileList{}
700+
a11y := manifest.NewA11y()
675701

676-
if items, ok := m.items[VocabularyDCTerms+"conformsto"]; ok {
702+
if items, ok := m.items[VocabularyDCTerms+"conformsTo"]; ok {
677703
for _, item := range items {
678704
if profile := a11yProfile(item.value); profile != "" {
679705
profiles = append(profiles, profile)
706+
for k, v := range item.children {
707+
switch k {
708+
case VocabularyA11Y + "certifiedBy":
709+
a11y.Certification = m.a11yCertification(v[0])
710+
case VocabularySchema + "accessibilitySummary":
711+
a11y.Summary = v[0].value
712+
case VocabularySchema + "accessMode":
713+
a11y.AccessModes = valuesToAccessModes(itemsValues(v))
714+
case VocabularySchema + "accessModeSufficient":
715+
a11y.AccessModesSufficient = valuesToAccessModesSufficient(itemsValues(v))
716+
case VocabularySchema + "accessibilityFeature":
717+
a11y.Features = valuesToA11yFeatures(itemsValues(v))
718+
case VocabularySchema + "accessibilityHazard":
719+
a11y.Hazards = valuesToA11yHazards(itemsValues(v))
720+
case VocabularyA11Y + "exemption":
721+
a11y.Exemptions = valuesToA11yExemptions(itemsValues(v))
722+
}
723+
}
680724
}
681725
}
682726
}
@@ -688,7 +732,7 @@ func (m PubMetadataAdapter) a11yConformsTo() []manifest.A11yProfile {
688732
}
689733

690734
profiles.Sort()
691-
return profiles
735+
return profiles, a11y
692736
}
693737

694738
func a11yProfile(value string) manifest.A11yProfile {
@@ -722,13 +766,18 @@ func a11yProfile(value string) manifest.A11yProfile {
722766
return manifest.EPUBA11y11WCAG21AA
723767
case "EPUB Accessibility 1.1 - WCAG 2.1 Level AAA":
724768
return manifest.EPUBA11y11WCAG21AAA
769+
case "EPUB Accessibility 1.1 - WCAG 2.2 Level A":
770+
return manifest.EPUBA11y11WCAG22A
771+
case "EPUB Accessibility 1.1 - WCAG 2.2 Level AA":
772+
return manifest.EPUBA11y11WCAG22AA
773+
case "EPUB Accessibility 1.1 - WCAG 2.2 Level AAA":
774+
return manifest.EPUBA11y11WCAG22AAA
725775
default:
726776
return ""
727777
}
728778
}
729779

730-
func (m PubMetadataAdapter) a11yCertification() *manifest.A11yCertification {
731-
certifierItem, _ := m.First(VocabularyA11Y + "certifiedBy")
780+
func (m PubMetadataAdapter) a11yCertification(certifierItem MetadataItem) *manifest.A11yCertification {
732781
c := manifest.A11yCertification{
733782
CertifiedBy: certifierItem.value,
734783
}
@@ -756,21 +805,15 @@ func (m PubMetadataAdapter) a11yCertification() *manifest.A11yCertification {
756805
return &c
757806
}
758807

759-
func (m PubMetadataAdapter) a11ySummary() string {
760-
return m.FirstValue(VocabularySchema + "accessibilitySummary")
761-
}
762-
763-
func (m PubMetadataAdapter) a11yAccessModes() []manifest.A11yAccessMode {
764-
values := m.Values(VocabularySchema + "accessMode")
808+
func valuesToAccessModes(values []string) []manifest.A11yAccessMode {
765809
am := make([]manifest.A11yAccessMode, len(values))
766810
for i, v := range values {
767811
am[i] = manifest.A11yAccessMode(v)
768812
}
769813
return am
770814
}
771815

772-
func (m PubMetadataAdapter) a11yAccessModesSufficient() [][]manifest.A11yPrimaryAccessMode {
773-
values := m.Values(VocabularySchema + "accessModeSufficient")
816+
func valuesToAccessModesSufficient(values []string) [][]manifest.A11yPrimaryAccessMode {
774817
ams := make([][]manifest.A11yPrimaryAccessMode, 0, len(values))
775818
for _, v := range values {
776819
c := a11yAccessModesSufficient(v)
@@ -793,26 +836,23 @@ func a11yAccessModesSufficient(value string) []manifest.A11yPrimaryAccessMode {
793836
return ams
794837
}
795838

796-
func (m PubMetadataAdapter) a11yFeatures() []manifest.A11yFeature {
797-
values := m.Values(VocabularySchema + "accessibilityFeature")
839+
func valuesToA11yFeatures(values []string) []manifest.A11yFeature {
798840
features := make([]manifest.A11yFeature, len(values))
799841
for i, v := range values {
800842
features[i] = manifest.A11yFeature(v)
801843
}
802844
return features
803845
}
804846

805-
func (m PubMetadataAdapter) a11yHazards() []manifest.A11yHazard {
806-
values := m.Values(VocabularySchema + "accessibilityHazard")
847+
func valuesToA11yHazards(values []string) []manifest.A11yHazard {
807848
hazards := make([]manifest.A11yHazard, len(values))
808849
for i, v := range values {
809850
hazards[i] = manifest.A11yHazard(v)
810851
}
811852
return hazards
812853
}
813854

814-
func (m PubMetadataAdapter) a11yExemptions() []manifest.A11yExemption {
815-
values := m.Values(VocabularyA11Y + "exemption")
855+
func valuesToA11yExemptions(values []string) []manifest.A11yExemption {
816856
exemptions := make([]manifest.A11yExemption, len(values))
817857
for i, v := range values {
818858
exemptions[i] = manifest.A11yExemption(v)

pkg/parser/epub/metadata_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/readium/go-toolkit/pkg/mediatype"
1111
"github.com/readium/go-toolkit/pkg/util/url"
1212
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
1314
)
1415

1516
func loadMetadata(ctx context.Context, name string) (*manifest.Metadata, error) {
@@ -361,6 +362,25 @@ func TestMetadataEPUB3Accessibility(t *testing.T) {
361362
assert.Nil(t, m.OtherMetadata["accessibility"])
362363
}
363364

365+
func TestMetadataEPUB3AccessibilityRefines(t *testing.T) {
366+
m, err := loadMetadata(t.Context(), "accessibility-refines")
367+
require.NoError(t, err)
368+
e := manifest.NewA11y()
369+
e.Summary = "This publication conforms to WCAG 2.2 Level AA."
370+
e.ConformsTo = []manifest.A11yProfile{manifest.EPUBA11y11WCAG22AA}
371+
e.Certification = &manifest.A11yCertification{
372+
CertifiedBy: "Standard Ebooks",
373+
}
374+
e.AccessModes = manifest.A11yAccessModesFromStrings([]string{"textual"})
375+
e.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{
376+
a11yAccessModesSufficient("textual"),
377+
}
378+
e.Features = valuesToA11yFeatures([]string{"readingOrder", "structuralNavigation", "tableOfContents", "unlocked"})
379+
e.Hazards = valuesToA11yHazards([]string{"none"})
380+
assert.Equal(t, &e, m.Accessibility)
381+
assert.Nil(t, m.OtherMetadata["accessibility"])
382+
}
383+
364384
func TestMetadataEPUB3TDM(t *testing.T) {
365385
m, err := loadMetadata(t.Context(), "tdm-epub3")
366386
assert.NoError(t, err)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://www.idpf.org/2007/opf" dir="ltr" prefix="se: https://standardebooks.org/vocab/1.0" unique-identifier="uid" version="3.0" xml:lang="en-US">
3+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
4+
<dc:identifier id="uid">url:https://standardebooks.org/ebooks/nathaniel-hawthorne/the-house-of-the-seven-gables</dc:identifier>
5+
<meta property="dcterms:modified">2024-10-02T02:28:18Z</meta>
6+
<dc:publisher id="publisher">Standard Ebooks</dc:publisher>
7+
<meta id="conformance-statement" property="dcterms:conformsTo">EPUB Accessibility 1.1 - WCAG 2.2 Level AA</meta>
8+
<meta property="a11y:certifiedBy" refines="#conformance-statement">Standard Ebooks</meta>
9+
<meta property="schema:accessMode">textual</meta>
10+
<meta property="schema:accessModeSufficient">textual</meta>
11+
<meta property="schema:accessibilityFeature">readingOrder</meta>
12+
<meta property="schema:accessibilityFeature">structuralNavigation</meta>
13+
<meta property="schema:accessibilityFeature">tableOfContents</meta>
14+
<meta property="schema:accessibilityFeature">unlocked</meta>
15+
<meta property="schema:accessibilityHazard">none</meta>
16+
<meta property="schema:accessibilitySummary">This publication conforms to WCAG 2.2 Level AA.</meta>
17+
<dc:title id="title">The House of the Seven Gables</dc:title>
18+
<!--Deleted-->
19+
<dc:language>en-US</dc:language>
20+
</metadata>
21+
<manifest>
22+
<item href="text/titlepage.xhtml" id="titlepage.xhtml" media-type="application/xhtml+xml" />
23+
</manifest>
24+
<spine>
25+
<itemref idref="titlepage.xhtml"/>
26+
</spine>
27+
</package>

0 commit comments

Comments
 (0)