mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(server): bind dc namespace during epub metadata writes
Preserve well-formed OPF output when publishers scope Dublin Core namespace declarations to individual metadata elements. Fixes #1207
This commit is contained in:
@@ -131,6 +131,58 @@ describe('epub-opf-builder', () => {
|
|||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds xmlns:dc to <package> when source declarations are scoped to replaced metadata elements', () => {
|
||||||
|
const opf = `
|
||||||
|
<package version="3.0" unique-identifier="uid" xmlns="http://www.idpf.org/2007/opf">
|
||||||
|
<metadata xmlns:opf="http://www.idpf.org/2007/opf">
|
||||||
|
<dc:identifier xmlns:dc="http://purl.org/dc/elements/1.1/" id="uid">urn:uuid:abc</dc:identifier>
|
||||||
|
<dc:title xmlns:dc="http://purl.org/dc/elements/1.1/">Old title</dc:title>
|
||||||
|
<dc:publisher xmlns:dc="http://purl.org/dc/elements/1.1/">Old publisher</dc:publisher>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = build(opf, { title: 'Replacement', publisher: 'New publisher', language: 'en' });
|
||||||
|
const packageTag = result.newOpfXml.match(/<package\b[^>]*>/)?.[0];
|
||||||
|
|
||||||
|
expect(packageTag).toContain('xmlns:dc="http://purl.org/dc/elements/1.1/"');
|
||||||
|
expect(result.newOpfXml).toContain('<dc:title id="t-main">Replacement</dc:title>');
|
||||||
|
expect(result.newOpfXml).toContain('<dc:publisher>New publisher</dc:publisher>');
|
||||||
|
expect(result.newOpfXml).toContain('<dc:language>en</dc:language>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not duplicate xmlns:dc when already declared on <package>', () => {
|
||||||
|
const opf = `
|
||||||
|
<package version="3.0" unique-identifier="uid" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata>
|
||||||
|
<dc:identifier id="uid">urn:uuid:abc</dc:identifier>
|
||||||
|
<dc:title>Old title</dc:title>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = build(opf, { title: 'Replacement' });
|
||||||
|
|
||||||
|
expect(result.newOpfXml.match(/xmlns:dc=/g)).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves an existing xmlns:dc binding on <package>', () => {
|
||||||
|
const opf = `
|
||||||
|
<package version="2.0" unique-identifier="uid" xmlns:dc="urn:publisher:dc">
|
||||||
|
<metadata>
|
||||||
|
<dc:identifier id="uid">urn:uuid:abc</dc:identifier>
|
||||||
|
<dc:title>Old title</dc:title>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = build(opf, { title: 'Replacement' });
|
||||||
|
const packageTag = result.newOpfXml.match(/<package\b[^>]*>/)?.[0];
|
||||||
|
|
||||||
|
expect(packageTag).toContain('xmlns:dc="urn:publisher:dc"');
|
||||||
|
expect(packageTag).not.toContain('xmlns:dc="http://purl.org/dc/elements/1.1/"');
|
||||||
|
});
|
||||||
|
|
||||||
it('strips legacy urn: provider identifiers and replaces with opf:scheme format', () => {
|
it('strips legacy urn: provider identifiers and replaces with opf:scheme format', () => {
|
||||||
const opf = `
|
const opf = `
|
||||||
<package version="2.0" unique-identifier="uid" xmlns:opf="http://www.idpf.org/2007/opf">
|
<package version="2.0" unique-identifier="uid" xmlns:opf="http://www.idpf.org/2007/opf">
|
||||||
|
|||||||
@@ -371,6 +371,12 @@ export function build(opfXml: string, payload: BookWritePayload): { newOpfXml: s
|
|||||||
pkgAttrs['@_xmlns:opf'] = 'http://www.idpf.org/2007/opf';
|
pkgAttrs['@_xmlns:opf'] = 'http://www.idpf.org/2007/opf';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some EPUBs scope xmlns:dc to each dc:* element. Rebuilding metadata removes those
|
||||||
|
// declarations, so bind the regenerated elements from their common package ancestor.
|
||||||
|
if (!attr(pkgAttrs, '@_xmlns:dc')) {
|
||||||
|
pkgAttrs['@_xmlns:dc'] = 'http://purl.org/dc/elements/1.1/';
|
||||||
|
}
|
||||||
|
|
||||||
const pkgContent = getPkgContentAll(pkgNode, pkgTag);
|
const pkgContent = getPkgContentAll(pkgNode, pkgTag);
|
||||||
const metadata = findMetadata(pkgContent);
|
const metadata = findMetadata(pkgContent);
|
||||||
if (!metadata) throw new Error('Cannot find <metadata> element in OPF');
|
if (!metadata) throw new Error('Cannot find <metadata> element in OPF');
|
||||||
|
|||||||
Reference in New Issue
Block a user