@readium/shared 1.3.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3898 -2382
- package/dist/index.umd.cjs +3 -3
- package/package.json +3 -1
- package/src/publication/AltIdentifier.ts +58 -0
- package/src/publication/Contributor.ts +15 -0
- package/src/publication/GuidedNavigation.ts +126 -15
- package/src/publication/Layout.ts +13 -0
- package/src/publication/Link.ts +7 -0
- package/src/publication/Metadata.ts +86 -9
- package/src/publication/Profiles.ts +15 -0
- package/src/publication/Properties.ts +10 -0
- package/src/publication/ReadingProgression.ts +2 -15
- package/src/publication/TDM.ts +56 -0
- package/src/publication/accessibility/Accessibility.ts +860 -0
- package/src/publication/accessibility/AccessibilityMetadataDisplayGuide.ts +1055 -0
- package/src/publication/accessibility/Localization.md +203 -0
- package/src/publication/accessibility/Localization.ts +115 -0
- package/src/publication/accessibility/index.ts +3 -0
- package/src/publication/accessibility/locales/en.json +231 -0
- package/src/publication/accessibility/locales/fr.json +231 -0
- package/src/publication/epub/MediaOverlay.ts +43 -0
- package/src/publication/epub/Metadata.ts +16 -0
- package/src/publication/epub/Properties.ts +0 -10
- package/src/publication/epub/index.ts +2 -2
- package/src/publication/index.ts +5 -1
- package/src/util/JSONParse.ts +3 -3
- package/types/src/publication/AltIdentifier.d.ts +23 -0
- package/types/src/publication/Contributor.d.ts +4 -0
- package/types/src/publication/GuidedNavigation.d.ts +62 -9
- package/types/src/publication/Layout.d.ts +8 -0
- package/types/src/publication/Link.d.ts +3 -0
- package/types/src/publication/Metadata.d.ts +28 -2
- package/types/src/publication/Profiles.d.ts +10 -0
- package/types/src/publication/Properties.d.ts +6 -0
- package/types/src/publication/ReadingProgression.d.ts +2 -5
- package/types/src/publication/TDM.d.ts +30 -0
- package/types/src/publication/accessibility/Accessibility.d.ts +337 -16
- package/types/src/publication/accessibility/AccessibilityMetadataDisplayGuide.d.ts +22 -25
- package/types/src/publication/accessibility/Localization.d.ts +45 -0
- package/types/src/publication/accessibility/index.d.ts +1 -0
- package/types/src/publication/epub/MediaOverlay.d.ts +19 -0
- package/types/src/publication/epub/Metadata.d.ts +6 -0
- package/types/src/publication/epub/Properties.d.ts +1 -5
- package/types/src/publication/epub/index.d.ts +2 -2
- package/types/src/publication/index.d.ts +5 -1
- package/types/src/util/JSONParse.d.ts +2 -2
- package/src/publication/epub/EPUBLayout.ts +0 -10
- package/src/publication/epub/Presentation.ts +0 -16
- package/src/publication/presentation/Metadata.ts +0 -19
- package/src/publication/presentation/Presentation.ts +0 -155
- package/src/publication/presentation/Properties.ts +0 -70
- package/src/publication/presentation/index.ts +0 -3
- package/types/src/publication/accessibility/AccessibilityDisplayString.d.ts +0 -56
- package/types/src/publication/epub/EPUBLayout.d.ts +0 -5
- package/types/src/publication/epub/Presentation.d.ts +0 -7
- package/types/src/publication/presentation/Metadata.d.ts +0 -6
- package/types/src/publication/presentation/Presentation.d.ts +0 -95
- package/types/src/publication/presentation/Properties.d.ts +0 -32
- package/types/src/publication/presentation/index.d.ts +0 -3
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
setToArray,
|
|
11
11
|
} from '../util/JSONParse';
|
|
12
12
|
import { LocalizedString } from './LocalizedString';
|
|
13
|
+
import { AltIdentifier } from './AltIdentifier';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Contributor Object for the Readium Web Publication Manifest.
|
|
@@ -25,6 +26,9 @@ export class Contributor {
|
|
|
25
26
|
/** An unambiguous reference to this contributor. */
|
|
26
27
|
public readonly identifier?: string;
|
|
27
28
|
|
|
29
|
+
/** Alternate identifiers for this contributor. */
|
|
30
|
+
public readonly altIdentifiers?: Set<AltIdentifier>;
|
|
31
|
+
|
|
28
32
|
/** The role of the contributor in the publication making. */
|
|
29
33
|
public readonly roles?: Set<string>;
|
|
30
34
|
|
|
@@ -41,6 +45,7 @@ export class Contributor {
|
|
|
41
45
|
name: LocalizedString;
|
|
42
46
|
sortAs?: string;
|
|
43
47
|
identifier?: string;
|
|
48
|
+
altIdentifiers?: Set<AltIdentifier>;
|
|
44
49
|
roles?: Set<string>;
|
|
45
50
|
links?: Links;
|
|
46
51
|
position?: number;
|
|
@@ -48,6 +53,7 @@ export class Contributor {
|
|
|
48
53
|
this.name = values.name;
|
|
49
54
|
this.sortAs = values.sortAs;
|
|
50
55
|
this.identifier = values.identifier;
|
|
56
|
+
this.altIdentifiers = values.altIdentifiers;
|
|
51
57
|
this.roles = values.roles;
|
|
52
58
|
this.links = values.links;
|
|
53
59
|
this.position = values.position;
|
|
@@ -70,6 +76,14 @@ export class Contributor {
|
|
|
70
76
|
name: LocalizedString.deserialize(json.name) as LocalizedString,
|
|
71
77
|
sortAs: json.sortAs,
|
|
72
78
|
identifier: json.identifier,
|
|
79
|
+
altIdentifiers: json.altIdentifier
|
|
80
|
+
? (json.altIdentifier instanceof Array
|
|
81
|
+
? new Set<AltIdentifier>(json.altIdentifier
|
|
82
|
+
.map((x: string | { value: string; scheme?: string }) => AltIdentifier.deserialize(x))
|
|
83
|
+
.filter((x: AltIdentifier | undefined): x is AltIdentifier => x !== undefined))
|
|
84
|
+
: new Set<AltIdentifier>([AltIdentifier.deserialize(json.altIdentifier as string | { value: string; scheme?: string })]
|
|
85
|
+
.filter((x: AltIdentifier | undefined): x is AltIdentifier => x !== undefined)))
|
|
86
|
+
: undefined,
|
|
73
87
|
roles: json.role
|
|
74
88
|
? new Set<string>(arrayfromJSONorString(json.role))
|
|
75
89
|
: undefined,
|
|
@@ -86,6 +100,7 @@ export class Contributor {
|
|
|
86
100
|
const json: any = { name: this.name.serialize() };
|
|
87
101
|
if (this.sortAs !== undefined) json.sortAs = this.sortAs;
|
|
88
102
|
if (this.identifier !== undefined) json.identifier = this.identifier;
|
|
103
|
+
if (this.altIdentifiers) json.altIdentifier = setToArray(this.altIdentifiers).map(altId => altId.serialize());
|
|
89
104
|
if (this.roles) json.role = setToArray(this.roles);
|
|
90
105
|
if (this.links) json.links = this.links.serialize();
|
|
91
106
|
if (this.position !== undefined) json.position = this.position;
|
|
@@ -43,6 +43,66 @@ export class GuidedNavigationDocument {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Represents a text value containing plain text, SSML, and language information.
|
|
48
|
+
*/
|
|
49
|
+
export class GuidedNavigationText {
|
|
50
|
+
/** Plain text content */
|
|
51
|
+
public readonly plain?: string;
|
|
52
|
+
|
|
53
|
+
/** SSML (Speech Synthesis Markup Language) content */
|
|
54
|
+
public readonly ssml?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* BCP 47 language tag
|
|
58
|
+
* @pattern ^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUse>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUse2>x(-[A-Za-z0-9]{1,8})+))$
|
|
59
|
+
*/
|
|
60
|
+
public readonly language?: string;
|
|
61
|
+
|
|
62
|
+
constructor(values: {
|
|
63
|
+
plain?: string;
|
|
64
|
+
ssml?: string;
|
|
65
|
+
language?: string;
|
|
66
|
+
}) {
|
|
67
|
+
this.plain = values.plain;
|
|
68
|
+
this.ssml = values.ssml;
|
|
69
|
+
this.language = values.language;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Deserializes a GuidedNavigationText from JSON
|
|
74
|
+
*/
|
|
75
|
+
public static deserialize(json: any): GuidedNavigationText | undefined {
|
|
76
|
+
if (json === undefined || json === null) return undefined;
|
|
77
|
+
|
|
78
|
+
if (typeof json === 'string') {
|
|
79
|
+
return new GuidedNavigationText({ plain: json });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Only create if there are actual values
|
|
83
|
+
if (json.plain || json.ssml || json.language) {
|
|
84
|
+
return new GuidedNavigationText({
|
|
85
|
+
plain: json.plain,
|
|
86
|
+
ssml: json.ssml,
|
|
87
|
+
language: json.language
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Serializes the text to a plain object
|
|
96
|
+
*/
|
|
97
|
+
public serialize(): any {
|
|
98
|
+
const result: any = {};
|
|
99
|
+
if (this.plain !== undefined) result.plain = this.plain;
|
|
100
|
+
if (this.ssml !== undefined) result.ssml = this.ssml;
|
|
101
|
+
if (this.language !== undefined) result.language = this.language;
|
|
102
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
46
106
|
/**
|
|
47
107
|
* Guided Navigation Object
|
|
48
108
|
* https://github.com/readium/guided-navigation/blob/main/schema/object.schema.json
|
|
@@ -61,12 +121,27 @@ export class GuidedNavigationObject {
|
|
|
61
121
|
/** Convey the structural semantics of a publication. */
|
|
62
122
|
public readonly role?: Set<string>;
|
|
63
123
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Indicates the heading level (1-6) for the navigation object.
|
|
126
|
+
* @minimum 1
|
|
127
|
+
* @maximum 6
|
|
128
|
+
*/
|
|
129
|
+
public readonly level?: number;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Textual equivalent of the resources or fragment of the resources referenced by the current Guided Navigation Object.
|
|
133
|
+
*/
|
|
134
|
+
public readonly text?: GuidedNavigationText;
|
|
66
135
|
|
|
67
136
|
/** References a textual resource or a fragment of it. */
|
|
68
137
|
public readonly textref?: string;
|
|
69
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Describes the image referenced by the current Guided Navigation Object.
|
|
141
|
+
* This is a GuidedNavigationObject that should not contain 'level' or 'children' properties.
|
|
142
|
+
*/
|
|
143
|
+
public readonly description?: Omit<GuidedNavigationObject, 'level' | 'children'>;
|
|
144
|
+
|
|
70
145
|
/**
|
|
71
146
|
* Creates a [GuidedNavigation] object.
|
|
72
147
|
*/
|
|
@@ -75,41 +150,68 @@ export class GuidedNavigationObject {
|
|
|
75
150
|
children?: GuidedNavigationObject[];
|
|
76
151
|
imgref?: string;
|
|
77
152
|
role?: Set<string>;
|
|
78
|
-
|
|
153
|
+
level?: number;
|
|
154
|
+
text?: GuidedNavigationText;
|
|
79
155
|
textref?: string;
|
|
156
|
+
description?: Omit<GuidedNavigationObject, 'level' | 'children'>;
|
|
80
157
|
}) {
|
|
81
158
|
this.audioref = values.audioref;
|
|
82
159
|
this.children = values.children;
|
|
83
160
|
this.imgref = values.imgref;
|
|
84
161
|
this.role = values.role;
|
|
162
|
+
this.level = values.level !== undefined ? Math.min(6, Math.max(1, values.level)) : undefined;
|
|
85
163
|
this.text = values.text;
|
|
86
164
|
this.textref = values.textref;
|
|
165
|
+
this.description = values.description;
|
|
87
166
|
}
|
|
88
167
|
|
|
89
168
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
169
|
+
* Gets the plain text content.
|
|
170
|
+
* Returns undefined if no text is available.
|
|
171
|
+
*/
|
|
172
|
+
public get plainText(): string | undefined {
|
|
173
|
+
return this.text?.plain;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets the SSML content if available.
|
|
178
|
+
*/
|
|
179
|
+
public get ssmlText(): string | undefined {
|
|
180
|
+
return this.text?.ssml;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Gets the language of the text if available.
|
|
185
|
+
*/
|
|
186
|
+
public get textLanguage(): string | undefined {
|
|
187
|
+
return this.text?.language;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Deserializes a GuidedNavigationObject from JSON
|
|
192
|
+
*/
|
|
94
193
|
public static deserialize(json: any): GuidedNavigationObject | undefined {
|
|
95
|
-
if (!json) return;
|
|
194
|
+
if (!json) return undefined;
|
|
195
|
+
|
|
96
196
|
return new GuidedNavigationObject({
|
|
97
197
|
audioref: json.audioref,
|
|
98
198
|
children: GuidedNavigationObject.deserializeArray(json.children),
|
|
99
199
|
imgref: json.imgref,
|
|
100
200
|
role: json.role
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
201
|
+
? new Set<string>(arrayfromJSONorString(json.role))
|
|
202
|
+
: undefined,
|
|
203
|
+
level: typeof json.level === 'number' ? json.level : undefined,
|
|
204
|
+
text: GuidedNavigationText.deserialize(json.text),
|
|
104
205
|
textref: json.textref,
|
|
206
|
+
description: GuidedNavigationObject.deserialize(json.description)
|
|
105
207
|
});
|
|
106
208
|
}
|
|
107
209
|
|
|
108
210
|
/**
|
|
109
211
|
* Parses a [GuidedNavigationObject] array from its RWPM JSON representation.
|
|
110
|
-
|
|
212
|
+
*/
|
|
111
213
|
public static deserializeArray(json: any): GuidedNavigationObject[] | undefined {
|
|
112
|
-
if (!(json instanceof Array)) return;
|
|
214
|
+
if (!(json instanceof Array)) return undefined;
|
|
113
215
|
return json
|
|
114
216
|
.map<GuidedNavigationObject>((item) => GuidedNavigationObject.deserialize(item) as GuidedNavigationObject)
|
|
115
217
|
.filter((x) => x !== undefined);
|
|
@@ -117,15 +219,24 @@ export class GuidedNavigationObject {
|
|
|
117
219
|
|
|
118
220
|
/**
|
|
119
221
|
* Serializes a [GuidedNavigationObject] to its RWPM JSON representation.
|
|
120
|
-
|
|
222
|
+
*/
|
|
121
223
|
public serialize(): any {
|
|
122
224
|
const json: any = {};
|
|
123
225
|
if (this.audioref !== undefined) json.audioref = this.audioref;
|
|
124
226
|
if (this.children !== undefined) json.children = this.children.map(x => x.serialize());
|
|
125
227
|
if (this.imgref !== undefined) json.imgref = this.imgref;
|
|
126
228
|
if (this.role !== undefined) json.role = setToArray(this.role);
|
|
127
|
-
if (this.
|
|
229
|
+
if (this.level !== undefined) json.level = this.level;
|
|
230
|
+
if (this.text !== undefined) {
|
|
231
|
+
const serializedText = this.text.serialize();
|
|
232
|
+
if (serializedText !== undefined) {
|
|
233
|
+
json.text = serializedText;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
128
236
|
if (this.textref !== undefined) json.textref = this.textref;
|
|
237
|
+
if (this.description) {
|
|
238
|
+
json.description = this.description.serialize();
|
|
239
|
+
}
|
|
129
240
|
return json;
|
|
130
241
|
}
|
|
131
242
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* Copyright 2025 Readium Foundation. All rights reserved.
|
|
2
|
+
* Use of this source code is governed by a BSD-style license,
|
|
3
|
+
* available in the LICENSE file present in the Github repository of the project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Hint about the nature of the layout for the publication.
|
|
7
|
+
* https://readium.org/webpub-manifest/contexts/default/#layout-and-reading-progression
|
|
8
|
+
*/
|
|
9
|
+
export enum Layout {
|
|
10
|
+
reflowable = 'reflowable',
|
|
11
|
+
fixed = 'fixed',
|
|
12
|
+
scrolled = 'scrolled'
|
|
13
|
+
}
|
package/src/publication/Link.ts
CHANGED
|
@@ -42,6 +42,9 @@ export class Link {
|
|
|
42
42
|
/** Width of the linked resource in pixels. */
|
|
43
43
|
public readonly width?: number;
|
|
44
44
|
|
|
45
|
+
/** Size of the linked resource in bytes. */
|
|
46
|
+
public readonly size?: number;
|
|
47
|
+
|
|
45
48
|
/** Length of the linked resource in seconds. */
|
|
46
49
|
public readonly duration?: number;
|
|
47
50
|
|
|
@@ -69,6 +72,7 @@ export class Link {
|
|
|
69
72
|
properties?: Properties;
|
|
70
73
|
height?: number;
|
|
71
74
|
width?: number;
|
|
75
|
+
size?: number;
|
|
72
76
|
duration?: number;
|
|
73
77
|
bitrate?: number;
|
|
74
78
|
languages?: Array<string>;
|
|
@@ -83,6 +87,7 @@ export class Link {
|
|
|
83
87
|
this.properties = values.properties;
|
|
84
88
|
this.height = values.height;
|
|
85
89
|
this.width = values.width;
|
|
90
|
+
this.size = values.size;
|
|
86
91
|
this.duration = values.duration;
|
|
87
92
|
this.bitrate = values.bitrate;
|
|
88
93
|
this.languages = values.languages;
|
|
@@ -109,6 +114,7 @@ export class Link {
|
|
|
109
114
|
properties: Properties.deserialize(json.properties),
|
|
110
115
|
height: positiveNumberfromJSON(json.height),
|
|
111
116
|
width: positiveNumberfromJSON(json.width),
|
|
117
|
+
size: positiveNumberfromJSON(json.size),
|
|
112
118
|
duration: positiveNumberfromJSON(json.duration),
|
|
113
119
|
bitrate: positiveNumberfromJSON(json.bitrate),
|
|
114
120
|
languages: arrayfromJSONorString(json.language),
|
|
@@ -129,6 +135,7 @@ export class Link {
|
|
|
129
135
|
if (this.properties) json.properties = this.properties.serialize();
|
|
130
136
|
if (this.height !== undefined) json.height = this.height;
|
|
131
137
|
if (this.width !== undefined) json.width = this.width;
|
|
138
|
+
if (this.size !== undefined) json.size = this.size;
|
|
132
139
|
if (this.duration !== undefined) json.duration = this.duration;
|
|
133
140
|
if (this.bitrate !== undefined) json.bitrate = this.bitrate;
|
|
134
141
|
if (this.languages) json.language = this.languages;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Copyright
|
|
1
|
+
/* Copyright 2025 Readium Foundation. All rights reserved.
|
|
2
2
|
* Use of this source code is governed by a BSD-style license,
|
|
3
3
|
* available in the LICENSE file present in the Github repository of the project.
|
|
4
4
|
*/
|
|
@@ -8,11 +8,16 @@ import {
|
|
|
8
8
|
datefromJSON,
|
|
9
9
|
positiveNumberfromJSON,
|
|
10
10
|
} from '../util/JSONParse';
|
|
11
|
+
import { AltIdentifier } from './AltIdentifier';
|
|
11
12
|
import { BelongsTo } from './BelongsTo';
|
|
12
13
|
import { Contributors } from './Contributor';
|
|
14
|
+
import { Layout } from './Layout';
|
|
13
15
|
import { LocalizedString } from './LocalizedString';
|
|
16
|
+
import { Profile } from './Profiles';
|
|
14
17
|
import { ReadingProgression } from './ReadingProgression';
|
|
15
18
|
import { Subjects } from './Subject';
|
|
19
|
+
import { Accessibility } from './accessibility/Accessibility';
|
|
20
|
+
import { TDM } from './TDM';
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* https://readium.org/webpub-manifest/schema/metadata.schema.json
|
|
@@ -25,7 +30,9 @@ import { Subjects } from './Subject';
|
|
|
25
30
|
export class Metadata {
|
|
26
31
|
public title: LocalizedString;
|
|
27
32
|
public typeUri?: string;
|
|
33
|
+
public conformsTo?: Array<Profile>;
|
|
28
34
|
public identifier?: string;
|
|
35
|
+
public altIdentifier?: AltIdentifier;
|
|
29
36
|
public subtitle?: LocalizedString;
|
|
30
37
|
public sortAs?: LocalizedString;
|
|
31
38
|
public artists?: Contributors;
|
|
@@ -49,16 +56,21 @@ export class Metadata {
|
|
|
49
56
|
public belongsTo?: BelongsTo;
|
|
50
57
|
public belongsToCollections?: Contributors;
|
|
51
58
|
public belongsToSeries?: Contributors;
|
|
59
|
+
public layout?: Layout;
|
|
52
60
|
public readingProgression?: ReadingProgression;
|
|
53
61
|
public duration?: number;
|
|
54
62
|
public numberOfPages?: number;
|
|
63
|
+
public accessibility?: Accessibility;
|
|
64
|
+
public tdm?: TDM;
|
|
55
65
|
public otherMetadata?: { [key: string]: any };
|
|
56
66
|
|
|
57
67
|
/**All metadata not in otherMetadata */
|
|
58
68
|
private static readonly mappedProperties = [
|
|
59
69
|
'title',
|
|
60
70
|
'@type',
|
|
71
|
+
'conformsTo',
|
|
61
72
|
'identifier',
|
|
73
|
+
'altIdentifier',
|
|
62
74
|
'subtitle',
|
|
63
75
|
'sortAs',
|
|
64
76
|
'artist',
|
|
@@ -80,16 +92,21 @@ export class Metadata {
|
|
|
80
92
|
'modified',
|
|
81
93
|
'subject',
|
|
82
94
|
'belongsTo',
|
|
95
|
+
'layout',
|
|
83
96
|
'readingProgression',
|
|
84
97
|
'duration',
|
|
85
98
|
'numberOfPages',
|
|
99
|
+
'accessibility',
|
|
100
|
+
'tdm'
|
|
86
101
|
];
|
|
87
102
|
|
|
88
103
|
/** Creates [Metadata] object */
|
|
89
104
|
constructor(values: {
|
|
90
105
|
title: LocalizedString;
|
|
91
106
|
typeUri?: string;
|
|
107
|
+
conformsTo?: Array<Profile>;
|
|
92
108
|
identifier?: string;
|
|
109
|
+
altIdentifier?: AltIdentifier;
|
|
93
110
|
subtitle?: LocalizedString;
|
|
94
111
|
sortAs?: LocalizedString;
|
|
95
112
|
artists?: Contributors;
|
|
@@ -113,15 +130,20 @@ export class Metadata {
|
|
|
113
130
|
belongsTo?: BelongsTo;
|
|
114
131
|
belongsToCollections?: Contributors;
|
|
115
132
|
belongsToSeries?: Contributors;
|
|
133
|
+
layout?: Layout;
|
|
116
134
|
readingProgression?: ReadingProgression;
|
|
117
135
|
duration?: number;
|
|
118
136
|
numberOfPages?: number;
|
|
137
|
+
accessibility?: Accessibility;
|
|
138
|
+
tdm?: TDM;
|
|
119
139
|
otherMetadata?: { [key: string]: any };
|
|
120
140
|
}) {
|
|
121
141
|
//title always required
|
|
122
142
|
this.title = values.title as LocalizedString;
|
|
123
143
|
this.typeUri = values.typeUri;
|
|
144
|
+
this.conformsTo = values.conformsTo;
|
|
124
145
|
this.identifier = values.identifier;
|
|
146
|
+
this.altIdentifier = values.altIdentifier;
|
|
125
147
|
this.subtitle = values.subtitle;
|
|
126
148
|
this.sortAs = values.sortAs;
|
|
127
149
|
this.artists = values.artists;
|
|
@@ -145,7 +167,6 @@ export class Metadata {
|
|
|
145
167
|
this.belongsTo = values.belongsTo;
|
|
146
168
|
this.belongsToCollections = values.belongsToCollections;
|
|
147
169
|
this.belongsToSeries = values.belongsToSeries;
|
|
148
|
-
|
|
149
170
|
if (
|
|
150
171
|
this.belongsToCollections &&
|
|
151
172
|
this.belongsToCollections.items.length > 0
|
|
@@ -163,9 +184,12 @@ export class Metadata {
|
|
|
163
184
|
this.belongsTo.items.set('series', this.belongsToSeries);
|
|
164
185
|
}
|
|
165
186
|
|
|
187
|
+
this.layout = values.layout;
|
|
166
188
|
this.readingProgression = values.readingProgression;
|
|
167
189
|
this.duration = values.duration;
|
|
168
190
|
this.numberOfPages = values.numberOfPages;
|
|
191
|
+
this.accessibility = values.accessibility;
|
|
192
|
+
this.tdm = values.tdm;
|
|
169
193
|
this.otherMetadata = values.otherMetadata;
|
|
170
194
|
}
|
|
171
195
|
|
|
@@ -179,7 +203,9 @@ export class Metadata {
|
|
|
179
203
|
|
|
180
204
|
const title = LocalizedString.deserialize(json.title) as LocalizedString;
|
|
181
205
|
const typeUri = json['@type'];
|
|
206
|
+
const conformsTo = arrayfromJSONorString(json.conformsTo);
|
|
182
207
|
const identifier = json.identifier;
|
|
208
|
+
const altIdentifier = AltIdentifier.deserialize(json.altIdentifier);
|
|
183
209
|
const subtitle = LocalizedString.deserialize(json.subtitle);
|
|
184
210
|
const sortAs = LocalizedString.deserialize(json.sortAs);
|
|
185
211
|
const artists = Contributors.deserialize(json.artist);
|
|
@@ -201,9 +227,12 @@ export class Metadata {
|
|
|
201
227
|
const modified = datefromJSON(json.modified);
|
|
202
228
|
const subjects = Subjects.deserialize(json.subject);
|
|
203
229
|
const belongsTo = BelongsTo.deserialize(json.belongsTo);
|
|
230
|
+
const accessibility = Accessibility.deserialize(json.accessibility);
|
|
231
|
+
const layout = json.layout;
|
|
204
232
|
const readingProgression = json.readingProgression;
|
|
205
233
|
const duration = positiveNumberfromJSON(json.duration);
|
|
206
234
|
const numberOfPages = positiveNumberfromJSON(json.numberOfPages);
|
|
235
|
+
const tdm = TDM.deserialize(json.tdm);
|
|
207
236
|
|
|
208
237
|
let otherMetadata = Object.assign({}, json);
|
|
209
238
|
Metadata.mappedProperties.forEach(x => delete otherMetadata[x]);
|
|
@@ -214,7 +243,9 @@ export class Metadata {
|
|
|
214
243
|
return new Metadata({
|
|
215
244
|
title,
|
|
216
245
|
typeUri,
|
|
246
|
+
conformsTo,
|
|
217
247
|
identifier,
|
|
248
|
+
altIdentifier,
|
|
218
249
|
subtitle,
|
|
219
250
|
sortAs,
|
|
220
251
|
artists,
|
|
@@ -236,10 +267,13 @@ export class Metadata {
|
|
|
236
267
|
modified,
|
|
237
268
|
subjects,
|
|
238
269
|
belongsTo,
|
|
270
|
+
layout,
|
|
239
271
|
readingProgression,
|
|
240
272
|
duration,
|
|
241
273
|
numberOfPages,
|
|
242
|
-
|
|
274
|
+
accessibility,
|
|
275
|
+
tdm,
|
|
276
|
+
otherMetadata
|
|
243
277
|
});
|
|
244
278
|
}
|
|
245
279
|
|
|
@@ -249,7 +283,9 @@ export class Metadata {
|
|
|
249
283
|
public serialize(): any {
|
|
250
284
|
const json: any = { title: this.title.serialize() };
|
|
251
285
|
if (this.typeUri !== undefined) json['@type'] = this.typeUri;
|
|
286
|
+
if (this.conformsTo) json.conformsTo = this.conformsTo;
|
|
252
287
|
if (this.identifier !== undefined) json.identifier = this.identifier;
|
|
288
|
+
if (this.altIdentifier) json.altIdentifier = this.altIdentifier.serialize();
|
|
253
289
|
if (this.subtitle) json.subtitle = this.subtitle.serialize();
|
|
254
290
|
if (this.sortAs) json.sortAs = this.sortAs.serialize();
|
|
255
291
|
if (this.editors) json.editor = this.editors.serialize();
|
|
@@ -273,11 +309,14 @@ export class Metadata {
|
|
|
273
309
|
json.modified = this.modified.toISOString();
|
|
274
310
|
if (this.subjects) json.subject = this.subjects.serialize();
|
|
275
311
|
if (this.belongsTo) json.belongsTo = this.belongsTo.serialize();
|
|
312
|
+
if (this.layout) json.layout = this.layout;
|
|
276
313
|
if (this.readingProgression)
|
|
277
314
|
json.readingProgression = this.readingProgression;
|
|
278
315
|
if (this.duration !== undefined) json.duration = this.duration;
|
|
279
316
|
if (this.numberOfPages !== undefined)
|
|
280
317
|
json.numberOfPages = this.numberOfPages;
|
|
318
|
+
if (this.accessibility) json.accessibility = this.accessibility.serialize();
|
|
319
|
+
if (this.tdm) json.tdm = this.tdm.serialize();
|
|
281
320
|
|
|
282
321
|
if (this.otherMetadata) {
|
|
283
322
|
const metadata = this.otherMetadata;
|
|
@@ -288,16 +327,54 @@ export class Metadata {
|
|
|
288
327
|
}
|
|
289
328
|
|
|
290
329
|
/**
|
|
291
|
-
* Computes a [
|
|
292
|
-
*
|
|
330
|
+
* Computes a [Layout] using the [conformsTo] profile and layout property.
|
|
331
|
+
*
|
|
332
|
+
* Special cases:
|
|
333
|
+
* - EPUB profile defaults to reflowable if layout is not present
|
|
334
|
+
* - Divina profile defaults to fixed if layout is not present
|
|
335
|
+
* - Layout is ignored for audiobook and PDF profiles
|
|
336
|
+
* - Layout is ignored if set to reflowable on a Divina profile
|
|
337
|
+
*
|
|
338
|
+
* Note: Stops at the first matching profile.
|
|
339
|
+
*/
|
|
340
|
+
public get effectiveLayout(): Layout | null {
|
|
341
|
+
if (!this.conformsTo) {
|
|
342
|
+
return null; // Default Web Publication behavior
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Check each profile in order, stopping at the first match
|
|
346
|
+
for (const profile of this.conformsTo) {
|
|
347
|
+
switch (profile) {
|
|
348
|
+
case Profile.EPUB:
|
|
349
|
+
// EPUB defaults to reflowable if layout is not set
|
|
350
|
+
return this.layout || Layout.reflowable;
|
|
351
|
+
|
|
352
|
+
case Profile.DIVINA:
|
|
353
|
+
// Divina defaults to fixed if layout is not set
|
|
354
|
+
// Layout is ignored if set to reflowable
|
|
355
|
+
if (this.layout === Layout.reflowable) {
|
|
356
|
+
return Layout.fixed;
|
|
357
|
+
}
|
|
358
|
+
return this.layout || Layout.fixed;
|
|
359
|
+
|
|
360
|
+
case Profile.AUDIOBOOK:
|
|
361
|
+
case Profile.PDF:
|
|
362
|
+
// Layout is ignored for audiobook and PDF profiles
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// If we get here, we have a conformsTo but no matching profile
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Computes a [ReadingProgression] when the value of [readingProgression] is undefined, using the publication language.
|
|
293
373
|
*
|
|
294
374
|
* See this issue for more details: https://github.com/readium/architecture/issues/113
|
|
295
375
|
*/
|
|
296
376
|
public get effectiveReadingProgression(): ReadingProgression {
|
|
297
|
-
if (
|
|
298
|
-
this.readingProgression &&
|
|
299
|
-
this.readingProgression !== ReadingProgression.auto
|
|
300
|
-
) {
|
|
377
|
+
if (this.readingProgression) {
|
|
301
378
|
return this.readingProgression;
|
|
302
379
|
}
|
|
303
380
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Copyright 2025 Readium Foundation. All rights reserved.
|
|
2
|
+
* Use of this source code is governed by a BSD-style license,
|
|
3
|
+
* available in the LICENSE file present in the Github repository of the project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Registered profiles.
|
|
8
|
+
* https://readium.org/webpub-manifest/profiles/
|
|
9
|
+
*/
|
|
10
|
+
export enum Profile {
|
|
11
|
+
EPUB = 'https://readium.org/webpub-manifest/profiles/epub',
|
|
12
|
+
AUDIOBOOK = 'https://readium.org/webpub-manifest/profiles/audiobook',
|
|
13
|
+
DIVINA = 'https://readium.org/webpub-manifest/profiles/divina',
|
|
14
|
+
PDF = 'https://readium.org/webpub-manifest/profiles/pdf'
|
|
15
|
+
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* available in the LICENSE file present in the Github repository of the project.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export enum Page {
|
|
7
|
+
left = 'left',
|
|
8
|
+
right = 'right',
|
|
9
|
+
center = 'center'
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* Properties associated to the linked resource.
|
|
8
14
|
*
|
|
@@ -16,6 +22,10 @@ export class Properties {
|
|
|
16
22
|
this.otherProperties = values;
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
get page(): Page | undefined {
|
|
26
|
+
return this.otherProperties['page'] as Page | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* Creates a [Properties] from its RWPM JSON representation.
|
|
21
31
|
*/
|
|
@@ -3,26 +3,13 @@
|
|
|
3
3
|
* available in the LICENSE file present in the Github repository of the project.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Page } from
|
|
6
|
+
import { Page } from "./Properties";
|
|
7
7
|
|
|
8
8
|
export enum ReadingProgression {
|
|
9
|
-
auto = 'auto',
|
|
10
|
-
btt = 'btt',
|
|
11
9
|
ltr = 'ltr',
|
|
12
10
|
rtl = 'rtl',
|
|
13
|
-
ttb = 'ttb',
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
export function leadingPage(readingProgression: ReadingProgression): Page {
|
|
17
|
-
|
|
18
|
-
case ReadingProgression.auto:
|
|
19
|
-
case ReadingProgression.ttb:
|
|
20
|
-
case ReadingProgression.ltr:
|
|
21
|
-
return Page.left;
|
|
22
|
-
case ReadingProgression.rtl:
|
|
23
|
-
case ReadingProgression.btt:
|
|
24
|
-
return Page.right;
|
|
25
|
-
default:
|
|
26
|
-
return Page.left;
|
|
27
|
-
}
|
|
14
|
+
return readingProgression === ReadingProgression.rtl ? Page.right : Page.left;
|
|
28
15
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Copyright 2025 Readium Foundation. All rights reserved.
|
|
2
|
+
* Use of this source code is governed by a BSD-style license,
|
|
3
|
+
* available in the LICENSE file present in the Github repository of the project.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* https://readium.org/webpub-manifest/contexts/default/#text-and-data-mining
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export enum TDMReservation {
|
|
11
|
+
all = 'all',
|
|
12
|
+
none = 'none'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class TDM {
|
|
16
|
+
/**
|
|
17
|
+
* Indicates whether the publication allows text and data mining.
|
|
18
|
+
*/
|
|
19
|
+
public readonly reservation?: TDMReservation;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Additional policy information about text and data mining usage.
|
|
23
|
+
*/
|
|
24
|
+
public readonly policy?: string;
|
|
25
|
+
|
|
26
|
+
/** Creates a [TDM] object */
|
|
27
|
+
constructor(values: {
|
|
28
|
+
reservation?: TDMReservation;
|
|
29
|
+
policy?: string;
|
|
30
|
+
}) {
|
|
31
|
+
this.reservation = values.reservation;
|
|
32
|
+
this.policy = values.policy;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parses a [TDM] from its RWPM JSON representation.
|
|
37
|
+
*/
|
|
38
|
+
public static deserialize(json: any): TDM | undefined {
|
|
39
|
+
if (!json) return;
|
|
40
|
+
|
|
41
|
+
return new TDM({
|
|
42
|
+
reservation: json.reservation as TDMReservation,
|
|
43
|
+
policy: json.policy,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Serializes a [TDM] to its RWPM JSON representation.
|
|
49
|
+
*/
|
|
50
|
+
public serialize(): any {
|
|
51
|
+
const json: any = {};
|
|
52
|
+
if (this.reservation !== undefined) json.reservation = this.reservation;
|
|
53
|
+
if (this.policy !== undefined) json.policy = this.policy;
|
|
54
|
+
return json;
|
|
55
|
+
}
|
|
56
|
+
}
|