From fb1e1f0a1feb9d00bb8cd3697247c1ba4a20b7fe Mon Sep 17 00:00:00 2001 From: Henry <chocolatkey@gmail.com> Date: Tue, 18 Mar 2025 12:12:30 -0700 Subject: [PATCH 1/4] fixes --- pkg/parser/epub/metadata.go | 102 +++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/pkg/parser/epub/metadata.go b/pkg/parser/epub/metadata.go index ec32da2..bc7b221 100644 --- a/pkg/parser/epub/metadata.go +++ b/pkg/parser/epub/metadata.go @@ -403,13 +403,18 @@ func (m metadataAdapter) FirstValue(property string) string { return item.value } +func itemsValues(items []MetadataItem) []string { + values := make([]string, len(items)) + for i, item := range items { + values[i] = item.value + } + return values +} + func (m metadataAdapter) Values(property string) []string { var values []string if items, ok := m.items[property]; ok { - values = make([]string, len(items)) - for i, item := range items { - values[i] = item.value - } + values = itemsValues(items) } return values } @@ -640,18 +645,38 @@ func (m PubMetadataAdapter) LocalizedSortAs() *manifest.LocalizedString { func (m PubMetadataAdapter) Accessibility() *manifest.A11y { a11y := manifest.NewA11y() - a11y.ConformsTo = m.a11yConformsTo() - a11y.Certification = m.a11yCertification() - a11y.Summary = m.a11ySummary() - a11y.AccessModes = m.a11yAccessModes() - a11y.AccessModesSufficient = m.a11yAccessModesSufficient() - a11y.Features = m.a11yFeatures() - a11y.Hazards = m.a11yHazards() - a11y.Exemptions = m.a11yExemptions() + ct, refinedA11y := m.a11yConformsTo() + a11y.ConformsTo = ct + + certifierItem, _ := m.First(VocabularyA11Y + "certifiedBy") + a11y.Certification = m.a11yCertification(certifierItem) + + a11y.Summary = m.FirstValue(VocabularySchema + "accessibilitySummary") + + modeValues := m.Values(VocabularySchema + "accessMode") + a11y.AccessModes = valuesToAccessModes(modeValues) + + sufficientValues := m.Values(VocabularySchema + "accessModeSufficient") + a11y.AccessModesSufficient = valuesToAccessModesSufficient(sufficientValues) + + featureValues := m.Values(VocabularySchema + "accessibilityFeature") + a11y.Features = valuesToA11yFeatures(featureValues) + + hazardValues := m.Values(VocabularySchema + "accessibilityHazard") + a11y.Hazards = valuesToA11yHazards(hazardValues) + + exemptionValues := m.Values(VocabularyA11Y + "exemption") + a11y.Exemptions = valuesToA11yExemptions(exemptionValues) if a11y.IsEmpty() { return nil } + + // ConformsTo refinements merge with the main a11y data + if !refinedA11y.IsEmpty() { + a11y.Merge(&refinedA11y) + } + return &a11y } @@ -674,13 +699,32 @@ func (m PubMetadataAdapter) TDM() *manifest.TDM { return tdm } -func (m PubMetadataAdapter) a11yConformsTo() []manifest.A11yProfile { +func (m PubMetadataAdapter) a11yConformsTo() ([]manifest.A11yProfile, manifest.A11y) { profiles := manifest.A11yProfileList{} + a11y := manifest.NewA11y() - if items, ok := m.items[VocabularyDCTerms+"conformsto"]; ok { + if items, ok := m.items[VocabularyDCTerms+"conformsTo"]; ok { for _, item := range items { if profile := a11yProfile(item.value); profile != "" { profiles = append(profiles, profile) + for k, v := range item.children { + switch k { + case VocabularyA11Y + "certifiedBy": + a11y.Certification = m.a11yCertification(v[0]) + case VocabularySchema + "accessibilitySummary": + a11y.Summary = v[0].value + case VocabularySchema + "accessMode": + a11y.AccessModes = valuesToAccessModes(itemsValues(v)) + case VocabularySchema + "accessModeSufficient": + a11y.AccessModesSufficient = valuesToAccessModesSufficient(itemsValues(v)) + case VocabularySchema + "accessibilityFeature": + a11y.Features = valuesToA11yFeatures(itemsValues(v)) + case VocabularySchema + "accessibilityHazard": + a11y.Hazards = valuesToA11yHazards(itemsValues(v)) + case VocabularyA11Y + "exemption": + a11y.Exemptions = valuesToA11yExemptions(itemsValues(v)) + } + } } } } @@ -692,7 +736,7 @@ func (m PubMetadataAdapter) a11yConformsTo() []manifest.A11yProfile { } profiles.Sort() - return profiles + return profiles, a11y } func a11yProfile(value string) manifest.A11yProfile { @@ -726,13 +770,18 @@ func a11yProfile(value string) manifest.A11yProfile { return manifest.EPUBA11y11WCAG21AA case "EPUB Accessibility 1.1 - WCAG 2.1 Level AAA": return manifest.EPUBA11y11WCAG21AAA + case "EPUB Accessibility 1.1 - WCAG 2.2 Level A": + return manifest.EPUBA11y11WCAG22A + case "EPUB Accessibility 1.1 - WCAG 2.2 Level AA": + return manifest.EPUBA11y11WCAG22AA + case "EPUB Accessibility 1.1 - WCAG 2.2 Level AAA": + return manifest.EPUBA11y11WCAG22AAA default: return "" } } -func (m PubMetadataAdapter) a11yCertification() *manifest.A11yCertification { - certifierItem, _ := m.First(VocabularyA11Y + "certifiedBy") +func (m PubMetadataAdapter) a11yCertification(certifierItem MetadataItem) *manifest.A11yCertification { c := manifest.A11yCertification{ CertifiedBy: certifierItem.value, } @@ -760,12 +809,7 @@ func (m PubMetadataAdapter) a11yCertification() *manifest.A11yCertification { return &c } -func (m PubMetadataAdapter) a11ySummary() string { - return m.FirstValue(VocabularySchema + "accessibilitySummary") -} - -func (m PubMetadataAdapter) a11yAccessModes() []manifest.A11yAccessMode { - values := m.Values(VocabularySchema + "accessMode") +func valuesToAccessModes(values []string) []manifest.A11yAccessMode { am := make([]manifest.A11yAccessMode, len(values)) for i, v := range values { am[i] = manifest.A11yAccessMode(v) @@ -773,8 +817,7 @@ func (m PubMetadataAdapter) a11yAccessModes() []manifest.A11yAccessMode { return am } -func (m PubMetadataAdapter) a11yAccessModesSufficient() [][]manifest.A11yPrimaryAccessMode { - values := m.Values(VocabularySchema + "accessModeSufficient") +func valuesToAccessModesSufficient(values []string) [][]manifest.A11yPrimaryAccessMode { ams := make([][]manifest.A11yPrimaryAccessMode, 0, len(values)) for _, v := range values { c := a11yAccessModesSufficient(v) @@ -797,8 +840,7 @@ func a11yAccessModesSufficient(value string) []manifest.A11yPrimaryAccessMode { return ams } -func (m PubMetadataAdapter) a11yFeatures() []manifest.A11yFeature { - values := m.Values(VocabularySchema + "accessibilityFeature") +func valuesToA11yFeatures(values []string) []manifest.A11yFeature { features := make([]manifest.A11yFeature, len(values)) for i, v := range values { features[i] = manifest.A11yFeature(v) @@ -806,8 +848,7 @@ func (m PubMetadataAdapter) a11yFeatures() []manifest.A11yFeature { return features } -func (m PubMetadataAdapter) a11yHazards() []manifest.A11yHazard { - values := m.Values(VocabularySchema + "accessibilityHazard") +func valuesToA11yHazards(values []string) []manifest.A11yHazard { hazards := make([]manifest.A11yHazard, len(values)) for i, v := range values { hazards[i] = manifest.A11yHazard(v) @@ -815,8 +856,7 @@ func (m PubMetadataAdapter) a11yHazards() []manifest.A11yHazard { return hazards } -func (m PubMetadataAdapter) a11yExemptions() []manifest.A11yExemption { - values := m.Values(VocabularyA11Y + "exemption") +func valuesToA11yExemptions(values []string) []manifest.A11yExemption { exemptions := make([]manifest.A11yExemption, len(values)) for i, v := range values { exemptions[i] = manifest.A11yExemption(v) From 917784a8d6c51e78a518d3cc81cf59e2c65ca03b Mon Sep 17 00:00:00 2001 From: Henry <chocolatkey@gmail.com> Date: Tue, 18 Mar 2025 14:40:32 -0700 Subject: [PATCH 2/4] fix incorrect link --- pkg/manifest/contributor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/manifest/contributor.go b/pkg/manifest/contributor.go index 9296835..d53c399 100644 --- a/pkg/manifest/contributor.go +++ b/pkg/manifest/contributor.go @@ -9,7 +9,7 @@ import ( // Contributor // https://github.com/readium/webpub-manifest/tree/master/contexts/default#contributors -// https://github.com/readium/webpub-manifest/schema/contributor-object.schema.json +// https://github.com/readium/webpub-manifest/blob/master/schema/contributor-object.schema.json type Contributor struct { LocalizedName LocalizedString `json:"name" validate:"required"` // The name of the contributor. LocalizedSortAs *LocalizedString `json:"sortAs,omitempty"` // The string used to sort the name of the contributor. From d3a7463d6a863e5e29334977dd27a2512c376492 Mon Sep 17 00:00:00 2001 From: Henry <chocolatkey@gmail.com> Date: Tue, 18 Mar 2025 14:42:18 -0700 Subject: [PATCH 3/4] fix bug in casing of DC element data --- pkg/parser/epub/metadata.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/parser/epub/metadata.go b/pkg/parser/epub/metadata.go index bc7b221..e25c60d 100644 --- a/pkg/parser/epub/metadata.go +++ b/pkg/parser/epub/metadata.go @@ -205,7 +205,7 @@ func (m MetadataParser) parseMetaElement(element *xmlquery.Node) *MetadataItem { id: element.SelectAttr("id"), } } else { - propName := strings.TrimSpace(element.SelectAttr("property")) + propName := strings.TrimSpace(property) if propName == "" { return nil } @@ -238,7 +238,7 @@ func (m MetadataParser) parseDcElement(element *xmlquery.Node) *MetadataItem { } data := strings.ToLower(element.Data) - propName := VocabularyDCTerms + data + propName := VocabularyDCTerms + element.Data switch data { case "creator": fallthrough From 79abb2675421c1c7dab029f02f92c2ec8455dea4 Mon Sep 17 00:00:00 2001 From: Henry <chocolatkey@gmail.com> Date: Wed, 7 May 2025 23:31:21 -0700 Subject: [PATCH 4/4] added test nested a11y data --- pkg/parser/epub/metadata_test.go | 20 ++++++++++++++ .../package/accessibility-refines.opf | 27 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 pkg/parser/epub/testdata/package/accessibility-refines.opf diff --git a/pkg/parser/epub/metadata_test.go b/pkg/parser/epub/metadata_test.go index 4765cf7..8bb7121 100644 --- a/pkg/parser/epub/metadata_test.go +++ b/pkg/parser/epub/metadata_test.go @@ -10,6 +10,7 @@ import ( "github.com/readium/go-toolkit/pkg/mediatype" "github.com/readium/go-toolkit/pkg/util/url" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func loadMetadata(ctx context.Context, name string) (*manifest.Metadata, error) { @@ -361,6 +362,25 @@ func TestMetadataEPUB3Accessibility(t *testing.T) { assert.Nil(t, m.OtherMetadata["accessibility"]) } +func TestMetadataEPUB3AccessibilityRefines(t *testing.T) { + m, err := loadMetadata(t.Context(), "accessibility-refines") + require.NoError(t, err) + e := manifest.NewA11y() + e.Summary = "This publication conforms to WCAG 2.2 Level AA." + e.ConformsTo = []manifest.A11yProfile{manifest.EPUBA11y11WCAG22AA} + e.Certification = &manifest.A11yCertification{ + CertifiedBy: "Standard Ebooks", + } + e.AccessModes = manifest.A11yAccessModesFromStrings([]string{"textual"}) + e.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{ + a11yAccessModesSufficient("textual"), + } + e.Features = valuesToA11yFeatures([]string{"readingOrder", "structuralNavigation", "tableOfContents", "unlocked"}) + e.Hazards = valuesToA11yHazards([]string{"none"}) + assert.Equal(t, &e, m.Accessibility) + assert.Nil(t, m.OtherMetadata["accessibility"]) +} + func TestMetadataEPUB3TDM(t *testing.T) { m, err := loadMetadata(t.Context(), "tdm-epub3") assert.NoError(t, err) diff --git a/pkg/parser/epub/testdata/package/accessibility-refines.opf b/pkg/parser/epub/testdata/package/accessibility-refines.opf new file mode 100644 index 0000000..c468613 --- /dev/null +++ b/pkg/parser/epub/testdata/package/accessibility-refines.opf @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<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"> + <metadata xmlns:dc="http://purl.org/dc/elements/1.1/"> + <dc:identifier id="uid">url:https://standardebooks.org/ebooks/nathaniel-hawthorne/the-house-of-the-seven-gables</dc:identifier> + <meta property="dcterms:modified">2024-10-02T02:28:18Z</meta> + <dc:publisher id="publisher">Standard Ebooks</dc:publisher> + <meta id="conformance-statement" property="dcterms:conformsTo">EPUB Accessibility 1.1 - WCAG 2.2 Level AA</meta> + <meta property="a11y:certifiedBy" refines="#conformance-statement">Standard Ebooks</meta> + <meta property="schema:accessMode">textual</meta> + <meta property="schema:accessModeSufficient">textual</meta> + <meta property="schema:accessibilityFeature">readingOrder</meta> + <meta property="schema:accessibilityFeature">structuralNavigation</meta> + <meta property="schema:accessibilityFeature">tableOfContents</meta> + <meta property="schema:accessibilityFeature">unlocked</meta> + <meta property="schema:accessibilityHazard">none</meta> + <meta property="schema:accessibilitySummary">This publication conforms to WCAG 2.2 Level AA.</meta> + <dc:title id="title">The House of the Seven Gables</dc:title> + <!--Deleted--> + <dc:language>en-US</dc:language> + </metadata> + <manifest> + <item href="text/titlepage.xhtml" id="titlepage.xhtml" media-type="application/xhtml+xml" /> + </manifest> + <spine> + <itemref idref="titlepage.xhtml"/> + </spine> +</package> \ No newline at end of file