bookish-press 0.11.1 → 0.11.2
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/epub/images.d.ts +21 -0
- package/dist/epub/images.js +27 -10
- package/dist/epub/images.test.js +33 -1
- package/package.json +1 -1
package/dist/epub/images.d.ts
CHANGED
|
@@ -74,6 +74,27 @@ export declare function resolveImageURL(url: string, base: string): string;
|
|
|
74
74
|
* keeping its caption.
|
|
75
75
|
*/
|
|
76
76
|
export declare function prepareImage(url: string, size: ImageSize, grayscale?: boolean): Promise<PreparedImage | undefined>;
|
|
77
|
+
/**
|
|
78
|
+
* Whether to package the re-encoded image rather than the original.
|
|
79
|
+
*
|
|
80
|
+
* Keep whichever is smaller, since re-encoding can inflate a file: resizing a
|
|
81
|
+
* diagram resamples its hairlines into shades of grey, and the result can be
|
|
82
|
+
* larger than the crisp original. Three things override that:
|
|
83
|
+
*
|
|
84
|
+
* - An image far past the budget is shrunk regardless, because decoding it
|
|
85
|
+
* costs a small reader memory it may not have.
|
|
86
|
+
* - Greyscale was asked for, and an image small enough to pass through would
|
|
87
|
+
* otherwise come out in colour while everything around it was grey.
|
|
88
|
+
* - WebP is always converted. EPUB 3.3 allows it, but many e-readers still in
|
|
89
|
+
* use predate that and show nothing, and bound books serve WebP by default.
|
|
90
|
+
*/
|
|
91
|
+
export declare function shouldUseReencoded(image: {
|
|
92
|
+
mediaType: string;
|
|
93
|
+
oversize: boolean;
|
|
94
|
+
grayscale: boolean;
|
|
95
|
+
originalBytes: number;
|
|
96
|
+
reencodedBytes: number;
|
|
97
|
+
}): boolean;
|
|
77
98
|
/**
|
|
78
99
|
* Run tasks with a bounded number in flight, so a book with a hundred images
|
|
79
100
|
* doesn't open a hundred connections at once.
|
package/dist/epub/images.js
CHANGED
|
@@ -138,20 +138,37 @@ export async function prepareImage(url, size, grayscale = false) {
|
|
|
138
138
|
const reencoded = await reencode(bytes, mediaType, size, grayscale);
|
|
139
139
|
if (reencoded === undefined)
|
|
140
140
|
return passThrough(bytes, mediaType);
|
|
141
|
-
// Keep whichever is smaller, since re-encoding can inflate a file: resizing
|
|
142
|
-
// a diagram resamples its hairlines into shades of grey, and the result can
|
|
143
|
-
// be larger than the crisp original. But an image far past the budget is
|
|
144
|
-
// shrunk regardless, because decoding it costs a small reader memory it may
|
|
145
|
-
// not have, and a few extra kilobytes are the cheaper price.
|
|
146
|
-
//
|
|
147
|
-
// Greyscale overrides the size comparison too: it was asked for, and an
|
|
148
|
-
// image small enough to pass through untouched would otherwise come out in
|
|
149
|
-
// colour while everything around it was grey.
|
|
150
141
|
const { oversize, ...prepared } = reencoded;
|
|
151
|
-
return
|
|
142
|
+
return shouldUseReencoded({
|
|
143
|
+
mediaType,
|
|
144
|
+
oversize,
|
|
145
|
+
grayscale,
|
|
146
|
+
originalBytes: bytes.length,
|
|
147
|
+
reencodedBytes: prepared.bytes.length,
|
|
148
|
+
})
|
|
152
149
|
? prepared
|
|
153
150
|
: passThrough(bytes, mediaType);
|
|
154
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Whether to package the re-encoded image rather than the original.
|
|
154
|
+
*
|
|
155
|
+
* Keep whichever is smaller, since re-encoding can inflate a file: resizing a
|
|
156
|
+
* diagram resamples its hairlines into shades of grey, and the result can be
|
|
157
|
+
* larger than the crisp original. Three things override that:
|
|
158
|
+
*
|
|
159
|
+
* - An image far past the budget is shrunk regardless, because decoding it
|
|
160
|
+
* costs a small reader memory it may not have.
|
|
161
|
+
* - Greyscale was asked for, and an image small enough to pass through would
|
|
162
|
+
* otherwise come out in colour while everything around it was grey.
|
|
163
|
+
* - WebP is always converted. EPUB 3.3 allows it, but many e-readers still in
|
|
164
|
+
* use predate that and show nothing, and bound books serve WebP by default.
|
|
165
|
+
*/
|
|
166
|
+
export function shouldUseReencoded(image) {
|
|
167
|
+
return (image.mediaType === 'image/webp' ||
|
|
168
|
+
image.oversize ||
|
|
169
|
+
image.grayscale ||
|
|
170
|
+
image.reencodedBytes < image.originalBytes);
|
|
171
|
+
}
|
|
155
172
|
/**
|
|
156
173
|
* Draw the image at its budgeted size and encode it, reporting whether the
|
|
157
174
|
* original was far enough past the budget that it must be shrunk regardless of
|
package/dist/epub/images.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
|
-
import { DEFAULT_SIZE, exceedsBudget, fitWithin, mapWithLimit, resolveImageURL, SIZES, } from './images';
|
|
2
|
+
import { DEFAULT_SIZE, exceedsBudget, fitWithin, mapWithLimit, resolveImageURL, shouldUseReencoded, SIZES, } from './images';
|
|
3
3
|
test('shrinks a landscape image to the width budget', () => {
|
|
4
4
|
expect(fitWithin(2400, 1200, 1200, 1600)).toEqual({
|
|
5
5
|
width: 1200,
|
|
@@ -129,3 +129,35 @@ test('a preset says nothing about colour', () => {
|
|
|
129
129
|
for (const size of Object.values(SIZES))
|
|
130
130
|
expect(size).not.toHaveProperty('grayscale');
|
|
131
131
|
});
|
|
132
|
+
const plain = {
|
|
133
|
+
oversize: false,
|
|
134
|
+
grayscale: false,
|
|
135
|
+
originalBytes: 1000,
|
|
136
|
+
reencodedBytes: 2000,
|
|
137
|
+
};
|
|
138
|
+
test('keeps a PNG or JPEG original when re-encoding would make it larger', () => {
|
|
139
|
+
expect(shouldUseReencoded({ ...plain, mediaType: 'image/png' })).toBe(false);
|
|
140
|
+
expect(shouldUseReencoded({ ...plain, mediaType: 'image/jpeg' })).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
test('uses the re-encoded image when it is smaller', () => {
|
|
143
|
+
expect(shouldUseReencoded({
|
|
144
|
+
...plain,
|
|
145
|
+
mediaType: 'image/png',
|
|
146
|
+
reencodedBytes: 500,
|
|
147
|
+
})).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
test('always converts WebP, even when the conversion is larger', () => {
|
|
150
|
+
expect(shouldUseReencoded({ ...plain, mediaType: 'image/webp' })).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
test('oversize and greyscale images are always re-encoded', () => {
|
|
153
|
+
expect(shouldUseReencoded({
|
|
154
|
+
...plain,
|
|
155
|
+
mediaType: 'image/png',
|
|
156
|
+
oversize: true,
|
|
157
|
+
})).toBe(true);
|
|
158
|
+
expect(shouldUseReencoded({
|
|
159
|
+
...plain,
|
|
160
|
+
mediaType: 'image/png',
|
|
161
|
+
grayscale: true,
|
|
162
|
+
})).toBe(true);
|
|
163
|
+
});
|