@readium/shared 1.2.1 → 2.0.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 +1256 -1152
- package/dist/index.umd.cjs +3 -3
- package/package.json +11 -10
- 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 +9 -1
- package/src/publication/Manifest.ts +5 -5
- package/src/publication/Metadata.ts +77 -8
- package/src/publication/Profiles.ts +15 -0
- package/src/publication/Properties.ts +10 -0
- package/src/publication/Publication.ts +2 -2
- package/src/publication/ReadingProgression.ts +2 -15
- package/src/publication/TDM.ts +56 -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 +4 -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/Manifest.d.ts +2 -2
- package/types/src/publication/Metadata.d.ts +25 -2
- package/types/src/publication/Profiles.d.ts +10 -0
- package/types/src/publication/Properties.d.ts +6 -0
- package/types/src/publication/Publication.d.ts +1 -1
- 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 +196 -0
- package/types/src/publication/accessibility/AccessibilityMetadataDisplayGuide.d.ts +211 -0
- package/types/src/publication/accessibility/Localization.d.ts +37 -0
- package/types/src/publication/accessibility/index.d.ts +3 -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 +4 -1
- package/types/src/util/JSONParse.d.ts +2 -2
- package/LICENSE +0 -28
- 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/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
|
@@ -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;
|
|
@@ -94,7 +99,8 @@ export class Link {
|
|
|
94
99
|
* Parses a [Link] from its RWPM JSON representation.
|
|
95
100
|
*/
|
|
96
101
|
public static deserialize(json: any): Link | undefined {
|
|
97
|
-
if (!
|
|
102
|
+
if (!json || typeof json.href !== 'string') return;
|
|
103
|
+
|
|
98
104
|
return new Link({
|
|
99
105
|
href: json.href,
|
|
100
106
|
templated: json.templated,
|
|
@@ -108,6 +114,7 @@ export class Link {
|
|
|
108
114
|
properties: Properties.deserialize(json.properties),
|
|
109
115
|
height: positiveNumberfromJSON(json.height),
|
|
110
116
|
width: positiveNumberfromJSON(json.width),
|
|
117
|
+
size: positiveNumberfromJSON(json.size),
|
|
111
118
|
duration: positiveNumberfromJSON(json.duration),
|
|
112
119
|
bitrate: positiveNumberfromJSON(json.bitrate),
|
|
113
120
|
languages: arrayfromJSONorString(json.language),
|
|
@@ -128,6 +135,7 @@ export class Link {
|
|
|
128
135
|
if (this.properties) json.properties = this.properties.serialize();
|
|
129
136
|
if (this.height !== undefined) json.height = this.height;
|
|
130
137
|
if (this.width !== undefined) json.width = this.width;
|
|
138
|
+
if (this.size !== undefined) json.size = this.size;
|
|
131
139
|
if (this.duration !== undefined) json.duration = this.duration;
|
|
132
140
|
if (this.bitrate !== undefined) json.bitrate = this.bitrate;
|
|
133
141
|
if (this.languages) json.language = this.languages;
|
|
@@ -28,7 +28,7 @@ export class Manifest {
|
|
|
28
28
|
public readonly resources?: Links;
|
|
29
29
|
|
|
30
30
|
/** Identifies the collection that contains a table of contents. */
|
|
31
|
-
public readonly
|
|
31
|
+
public readonly toc?: Links;
|
|
32
32
|
|
|
33
33
|
public readonly subcollections?: Map<string, Array<PublicationCollection>>;
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ export class Manifest {
|
|
|
38
38
|
links: Links;
|
|
39
39
|
readingOrder: Links;
|
|
40
40
|
resources?: Links;
|
|
41
|
-
|
|
41
|
+
toc?: Links;
|
|
42
42
|
subcollections?: Map<string, Array<PublicationCollection>>;
|
|
43
43
|
}) {
|
|
44
44
|
this.context = values.context;
|
|
@@ -46,7 +46,7 @@ export class Manifest {
|
|
|
46
46
|
this.links = values.links;
|
|
47
47
|
this.readingOrder = values.readingOrder;
|
|
48
48
|
this.resources = values.resources;
|
|
49
|
-
this.
|
|
49
|
+
this.toc = values.toc;
|
|
50
50
|
this.subcollections = values.subcollections;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -79,7 +79,7 @@ export class Manifest {
|
|
|
79
79
|
links,
|
|
80
80
|
readingOrder,
|
|
81
81
|
resources: Links.deserialize(json.resources),
|
|
82
|
-
|
|
82
|
+
toc: Links.deserialize(json.toc),
|
|
83
83
|
subcollections: PublicationCollection.deserializeCollections({
|
|
84
84
|
sub: json.sub,
|
|
85
85
|
}),
|
|
@@ -96,7 +96,7 @@ export class Manifest {
|
|
|
96
96
|
json.links = this.links.serialize();
|
|
97
97
|
json.readingOrder = this.readingOrder.serialize();
|
|
98
98
|
if (this.resources) json.resources = this.resources.serialize();
|
|
99
|
-
if (this.
|
|
99
|
+
if (this.toc) json.toc = this.toc.serialize();
|
|
100
100
|
PublicationCollection.serializeCollection(json, this.subcollections);
|
|
101
101
|
return json;
|
|
102
102
|
}
|
|
@@ -8,11 +8,15 @@ 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 { TDM } from './TDM';
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* https://readium.org/webpub-manifest/schema/metadata.schema.json
|
|
@@ -25,7 +29,9 @@ import { Subjects } from './Subject';
|
|
|
25
29
|
export class Metadata {
|
|
26
30
|
public title: LocalizedString;
|
|
27
31
|
public typeUri?: string;
|
|
32
|
+
public conformsTo?: Array<Profile>;
|
|
28
33
|
public identifier?: string;
|
|
34
|
+
public altIdentifier?: AltIdentifier;
|
|
29
35
|
public subtitle?: LocalizedString;
|
|
30
36
|
public sortAs?: LocalizedString;
|
|
31
37
|
public artists?: Contributors;
|
|
@@ -49,16 +55,20 @@ export class Metadata {
|
|
|
49
55
|
public belongsTo?: BelongsTo;
|
|
50
56
|
public belongsToCollections?: Contributors;
|
|
51
57
|
public belongsToSeries?: Contributors;
|
|
58
|
+
public layout?: Layout;
|
|
52
59
|
public readingProgression?: ReadingProgression;
|
|
53
60
|
public duration?: number;
|
|
54
61
|
public numberOfPages?: number;
|
|
62
|
+
public tdm?: TDM;
|
|
55
63
|
public otherMetadata?: { [key: string]: any };
|
|
56
64
|
|
|
57
65
|
/**All metadata not in otherMetadata */
|
|
58
66
|
private static readonly mappedProperties = [
|
|
59
67
|
'title',
|
|
60
68
|
'@type',
|
|
69
|
+
'conformsTo',
|
|
61
70
|
'identifier',
|
|
71
|
+
'altIdentifier',
|
|
62
72
|
'subtitle',
|
|
63
73
|
'sortAs',
|
|
64
74
|
'artist',
|
|
@@ -80,16 +90,20 @@ export class Metadata {
|
|
|
80
90
|
'modified',
|
|
81
91
|
'subject',
|
|
82
92
|
'belongsTo',
|
|
93
|
+
'layout',
|
|
83
94
|
'readingProgression',
|
|
84
95
|
'duration',
|
|
85
96
|
'numberOfPages',
|
|
97
|
+
'tdm'
|
|
86
98
|
];
|
|
87
99
|
|
|
88
100
|
/** Creates [Metadata] object */
|
|
89
101
|
constructor(values: {
|
|
90
102
|
title: LocalizedString;
|
|
91
103
|
typeUri?: string;
|
|
104
|
+
conformsTo?: Array<Profile>;
|
|
92
105
|
identifier?: string;
|
|
106
|
+
altIdentifier?: AltIdentifier;
|
|
93
107
|
subtitle?: LocalizedString;
|
|
94
108
|
sortAs?: LocalizedString;
|
|
95
109
|
artists?: Contributors;
|
|
@@ -113,15 +127,19 @@ export class Metadata {
|
|
|
113
127
|
belongsTo?: BelongsTo;
|
|
114
128
|
belongsToCollections?: Contributors;
|
|
115
129
|
belongsToSeries?: Contributors;
|
|
130
|
+
layout?: Layout;
|
|
116
131
|
readingProgression?: ReadingProgression;
|
|
117
132
|
duration?: number;
|
|
118
133
|
numberOfPages?: number;
|
|
134
|
+
tdm?: TDM;
|
|
119
135
|
otherMetadata?: { [key: string]: any };
|
|
120
136
|
}) {
|
|
121
137
|
//title always required
|
|
122
138
|
this.title = values.title as LocalizedString;
|
|
123
139
|
this.typeUri = values.typeUri;
|
|
140
|
+
this.conformsTo = values.conformsTo;
|
|
124
141
|
this.identifier = values.identifier;
|
|
142
|
+
this.altIdentifier = values.altIdentifier;
|
|
125
143
|
this.subtitle = values.subtitle;
|
|
126
144
|
this.sortAs = values.sortAs;
|
|
127
145
|
this.artists = values.artists;
|
|
@@ -145,7 +163,6 @@ export class Metadata {
|
|
|
145
163
|
this.belongsTo = values.belongsTo;
|
|
146
164
|
this.belongsToCollections = values.belongsToCollections;
|
|
147
165
|
this.belongsToSeries = values.belongsToSeries;
|
|
148
|
-
|
|
149
166
|
if (
|
|
150
167
|
this.belongsToCollections &&
|
|
151
168
|
this.belongsToCollections.items.length > 0
|
|
@@ -163,9 +180,11 @@ export class Metadata {
|
|
|
163
180
|
this.belongsTo.items.set('series', this.belongsToSeries);
|
|
164
181
|
}
|
|
165
182
|
|
|
183
|
+
this.layout = values.layout;
|
|
166
184
|
this.readingProgression = values.readingProgression;
|
|
167
185
|
this.duration = values.duration;
|
|
168
186
|
this.numberOfPages = values.numberOfPages;
|
|
187
|
+
this.tdm = values.tdm;
|
|
169
188
|
this.otherMetadata = values.otherMetadata;
|
|
170
189
|
}
|
|
171
190
|
|
|
@@ -179,7 +198,9 @@ export class Metadata {
|
|
|
179
198
|
|
|
180
199
|
const title = LocalizedString.deserialize(json.title) as LocalizedString;
|
|
181
200
|
const typeUri = json['@type'];
|
|
201
|
+
const conformsTo = arrayfromJSONorString(json.conformsTo);
|
|
182
202
|
const identifier = json.identifier;
|
|
203
|
+
const altIdentifier = AltIdentifier.deserialize(json.altIdentifier);
|
|
183
204
|
const subtitle = LocalizedString.deserialize(json.subtitle);
|
|
184
205
|
const sortAs = LocalizedString.deserialize(json.sortAs);
|
|
185
206
|
const artists = Contributors.deserialize(json.artist);
|
|
@@ -201,9 +222,11 @@ export class Metadata {
|
|
|
201
222
|
const modified = datefromJSON(json.modified);
|
|
202
223
|
const subjects = Subjects.deserialize(json.subject);
|
|
203
224
|
const belongsTo = BelongsTo.deserialize(json.belongsTo);
|
|
225
|
+
const layout = json.layout;
|
|
204
226
|
const readingProgression = json.readingProgression;
|
|
205
227
|
const duration = positiveNumberfromJSON(json.duration);
|
|
206
228
|
const numberOfPages = positiveNumberfromJSON(json.numberOfPages);
|
|
229
|
+
const tdm = TDM.deserialize(json.tdm);
|
|
207
230
|
|
|
208
231
|
let otherMetadata = Object.assign({}, json);
|
|
209
232
|
Metadata.mappedProperties.forEach(x => delete otherMetadata[x]);
|
|
@@ -214,7 +237,9 @@ export class Metadata {
|
|
|
214
237
|
return new Metadata({
|
|
215
238
|
title,
|
|
216
239
|
typeUri,
|
|
240
|
+
conformsTo,
|
|
217
241
|
identifier,
|
|
242
|
+
altIdentifier,
|
|
218
243
|
subtitle,
|
|
219
244
|
sortAs,
|
|
220
245
|
artists,
|
|
@@ -236,10 +261,12 @@ export class Metadata {
|
|
|
236
261
|
modified,
|
|
237
262
|
subjects,
|
|
238
263
|
belongsTo,
|
|
264
|
+
layout,
|
|
239
265
|
readingProgression,
|
|
240
266
|
duration,
|
|
241
267
|
numberOfPages,
|
|
242
|
-
|
|
268
|
+
tdm,
|
|
269
|
+
otherMetadata
|
|
243
270
|
});
|
|
244
271
|
}
|
|
245
272
|
|
|
@@ -249,7 +276,9 @@ export class Metadata {
|
|
|
249
276
|
public serialize(): any {
|
|
250
277
|
const json: any = { title: this.title.serialize() };
|
|
251
278
|
if (this.typeUri !== undefined) json['@type'] = this.typeUri;
|
|
279
|
+
if (this.conformsTo) json.conformsTo = this.conformsTo;
|
|
252
280
|
if (this.identifier !== undefined) json.identifier = this.identifier;
|
|
281
|
+
if (this.altIdentifier) json.altIdentifier = this.altIdentifier.serialize();
|
|
253
282
|
if (this.subtitle) json.subtitle = this.subtitle.serialize();
|
|
254
283
|
if (this.sortAs) json.sortAs = this.sortAs.serialize();
|
|
255
284
|
if (this.editors) json.editor = this.editors.serialize();
|
|
@@ -273,11 +302,13 @@ export class Metadata {
|
|
|
273
302
|
json.modified = this.modified.toISOString();
|
|
274
303
|
if (this.subjects) json.subject = this.subjects.serialize();
|
|
275
304
|
if (this.belongsTo) json.belongsTo = this.belongsTo.serialize();
|
|
305
|
+
if (this.layout) json.layout = this.layout;
|
|
276
306
|
if (this.readingProgression)
|
|
277
307
|
json.readingProgression = this.readingProgression;
|
|
278
308
|
if (this.duration !== undefined) json.duration = this.duration;
|
|
279
309
|
if (this.numberOfPages !== undefined)
|
|
280
310
|
json.numberOfPages = this.numberOfPages;
|
|
311
|
+
if (this.tdm) json.tdm = this.tdm.serialize();
|
|
281
312
|
|
|
282
313
|
if (this.otherMetadata) {
|
|
283
314
|
const metadata = this.otherMetadata;
|
|
@@ -288,16 +319,54 @@ export class Metadata {
|
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
/**
|
|
291
|
-
* Computes a [
|
|
292
|
-
*
|
|
322
|
+
* Computes a [Layout] using the [conformsTo] profile and layout property.
|
|
323
|
+
*
|
|
324
|
+
* Special cases:
|
|
325
|
+
* - EPUB profile defaults to reflowable if layout is not present
|
|
326
|
+
* - Divina profile defaults to fixed if layout is not present
|
|
327
|
+
* - Layout is ignored for audiobook and PDF profiles
|
|
328
|
+
* - Layout is ignored if set to reflowable on a Divina profile
|
|
329
|
+
*
|
|
330
|
+
* Note: Stops at the first matching profile.
|
|
331
|
+
*/
|
|
332
|
+
public get effectiveLayout(): Layout | null {
|
|
333
|
+
if (!this.conformsTo) {
|
|
334
|
+
return null; // Default Web Publication behavior
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Check each profile in order, stopping at the first match
|
|
338
|
+
for (const profile of this.conformsTo) {
|
|
339
|
+
switch (profile) {
|
|
340
|
+
case Profile.EPUB:
|
|
341
|
+
// EPUB defaults to reflowable if layout is not set
|
|
342
|
+
return this.layout || Layout.reflowable;
|
|
343
|
+
|
|
344
|
+
case Profile.DIVINA:
|
|
345
|
+
// Divina defaults to fixed if layout is not set
|
|
346
|
+
// Layout is ignored if set to reflowable
|
|
347
|
+
if (this.layout === Layout.reflowable) {
|
|
348
|
+
return Layout.fixed;
|
|
349
|
+
}
|
|
350
|
+
return this.layout || Layout.fixed;
|
|
351
|
+
|
|
352
|
+
case Profile.AUDIOBOOK:
|
|
353
|
+
case Profile.PDF:
|
|
354
|
+
// Layout is ignored for audiobook and PDF profiles
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// If we get here, we have a conformsTo but no matching profile
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Computes a [ReadingProgression] when the value of [readingProgression] is undefined, using the publication language.
|
|
293
365
|
*
|
|
294
366
|
* See this issue for more details: https://github.com/readium/architecture/issues/113
|
|
295
367
|
*/
|
|
296
368
|
public get effectiveReadingProgression(): ReadingProgression {
|
|
297
|
-
if (
|
|
298
|
-
this.readingProgression &&
|
|
299
|
-
this.readingProgression !== ReadingProgression.auto
|
|
300
|
-
) {
|
|
369
|
+
if (this.readingProgression) {
|
|
301
370
|
return this.readingProgression;
|
|
302
371
|
}
|
|
303
372
|
|
|
@@ -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
|
*/
|
|
@@ -30,7 +30,7 @@ export class Publication {
|
|
|
30
30
|
/** Identifies resources that are necessary for rendering the publication. */
|
|
31
31
|
public readonly resources?: Links;
|
|
32
32
|
/** Identifies the collection that contains a table of contents. */
|
|
33
|
-
public readonly
|
|
33
|
+
public readonly toc?: Links;
|
|
34
34
|
/** Identifies the collection that contains sub collections. */
|
|
35
35
|
public readonly subcollections?: Map<string, Array<PublicationCollection>>;
|
|
36
36
|
|
|
@@ -42,7 +42,7 @@ export class Publication {
|
|
|
42
42
|
this.links = values.manifest.links;
|
|
43
43
|
this.readingOrder = values.manifest.readingOrder;
|
|
44
44
|
this.resources = values.manifest.resources;
|
|
45
|
-
this.
|
|
45
|
+
this.toc = values.manifest.toc;
|
|
46
46
|
this.subcollections = values.manifest.subcollections;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -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
|
+
}
|