fullbleed 0.4.1 → 0.5.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/README.md CHANGED
@@ -15,7 +15,7 @@ Using Vue? The [Vue and TypeScript starter](examples/vue) includes editable
15
15
  templates, automatic previews, downloads, and a reusable `usePdfPreview` composable.
16
16
 
17
17
  This is an optional integration around the published **Fullbleed 2.5.22** engine.
18
- The package has its own version, **0.4.1**. Node applications can use a shared
18
+ The package has its own version, **0.5.0**. Node applications can use a shared
19
19
  render queue to bound active renders and waiting requests, include queue time in
20
20
  deadlines, cancel waiting jobs, and stop rendering cleanly during shutdown.
21
21
  The browser entry renders in a Web Worker, and the Node API supports worker and
@@ -25,7 +25,12 @@ process isolation. See the [browser guide](docs/browser.md),
25
25
  [font-family checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/font-families.md), and
26
26
  [embedded-font checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/font-subsets.md) for the retained fixtures.
27
27
 
28
- Version 0.4.1 brings the engine's recent layout fixes to both JavaScript entries.
28
+ Version 0.5.0 adds `defaultStyles: false` to both JavaScript entries. Use it to
29
+ preserve a template's original CSS, including font inheritance from `html`,
30
+ low-specificity selectors, and cascade layers. Existing calls keep the Inter
31
+ body rule. [Retained styling checks](docs/default-styles.md) cover both choices.
32
+
33
+ Version 0.4.1 brought the engine's recent layout fixes to both JavaScript entries.
29
34
  The [engine update checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/engine-2.5.22.md)
30
35
  cover nested counters, clipped effects, border-image tiling, and floated initials
31
36
  with independent text, color, and Chrome print expectations.
@@ -38,7 +43,7 @@ Use Node.js 22 or newer. Install the [npm package](https://www.npmjs.com/package
38
43
  npm install fullbleed
39
44
  ```
40
45
 
41
- For a version-pinned installation, use `npm install --save-exact fullbleed@0.4.1`.
46
+ For a version-pinned installation, use `npm install --save-exact fullbleed@0.5.0`.
42
47
  Package archives and retained PDF/PNG evidence are attached to each
43
48
  [GitHub release](https://github.com/fullbleed-engine/fullbleed-node/releases).
44
49
  The [installation verification records](https://github.com/fullbleed-engine/fullbleed-node/tree/main/verification)
@@ -208,7 +213,8 @@ native engine.
208
213
  | Option | Behavior |
209
214
  | --- | --- |
210
215
  | `html` | Required nonempty HTML string. |
211
- | `css` | CSS string; defaults to empty. The default Inter rule precedes it. |
216
+ | `css` | CSS string; defaults to empty. |
217
+ | `defaultStyles` | Default `true`: prepend `body { font-family: Inter; }`. Set `false` to pass CSS unchanged; bundled fonts remain available. |
212
218
  | `fonts` | Additional TrueType font bytes as `Uint8Array[]` or `Buffer[]`. |
213
219
  | `assets` | Map relative asset names to nonempty byte arrays. |
214
220
  | `previewDpi` | `0` disables previews; otherwise an integer from 36 to 300. |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "fullbleed.browser-assets.v1",
3
- "packageVersion": "0.4.1",
3
+ "packageVersion": "0.5.0",
4
4
  "engineVersion": "2.5.22",
5
5
  "engineFeatures": [
6
6
  "svg_raster"
@@ -8,18 +8,18 @@
8
8
  "files": {
9
9
  "client.js": {
10
10
  "source": "dist/browser/client.js",
11
- "bytes": 12043,
12
- "sha256": "7cf701296f165f39bec30d992f6e9eacf2f0d09a6fa79bca84a598baa5fc3de9"
11
+ "bytes": 12323,
12
+ "sha256": "9a6287ef90efea5efd08f8c7ce5a068eddf612a55fb22d3602d0d12c1a33f65d"
13
13
  },
14
14
  "worker.js": {
15
15
  "source": "dist/browser/worker.js",
16
16
  "bytes": 52109,
17
- "sha256": "44152f218784ffd37f891ca10681264074d4eb6a08380cd15e3548aead3ad675"
17
+ "sha256": "7ff41b156cb8b21c66719ab86691e0f31c7e95fd3ca11922e5ee0692b2dcc8b0"
18
18
  },
19
19
  "engine.wasm": {
20
20
  "source": "dist/engine.wasm",
21
21
  "bytes": 5640040,
22
- "sha256": "508c3dc58b51ec5568610cd5b8a329683f9e6d3677a464d8fc422ea9071b450d"
22
+ "sha256": "a739438e01e9b52db48e47f1a604b700d98119b40db41361cc1563da059d1cbf"
23
23
  },
24
24
  "THIRD_PARTY_NOTICES.txt": {
25
25
  "source": "dist/THIRD_PARTY_NOTICES.txt",
@@ -68,12 +68,13 @@ var require_render_input = __commonJS({
68
68
  var assetLimit = 64 * 1024 * 1024;
69
69
  function normalize(options, { browser = false } = {}) {
70
70
  if (!options || typeof options !== "object" || Array.isArray(options)) invalid2("Pass an options object with an html string.");
71
- const supported = /* @__PURE__ */ new Set(["html", "css", "fonts", "assets", "previewDpi", "timeoutMs", "maxPages", "allowMissingGlyphs", "signal"]);
71
+ const supported = /* @__PURE__ */ new Set(["html", "css", "defaultStyles", "fonts", "assets", "previewDpi", "timeoutMs", "maxPages", "allowMissingGlyphs", "signal"]);
72
72
  if (!browser) supported.add("isolation");
73
73
  for (const key of Object.keys(options)) if (!supported.has(key)) invalid2(`Unknown option: ${key}`);
74
- const { html, css = "", fonts = [], assets = {}, previewDpi = 0, timeoutMs = 3e4, maxPages = 1e3, allowMissingGlyphs = false, signal, isolation = "worker" } = options;
74
+ const { html, css = "", defaultStyles = true, fonts = [], assets = {}, previewDpi = 0, timeoutMs = 3e4, maxPages = 1e3, allowMissingGlyphs = false, signal, isolation = "worker" } = options;
75
75
  if (typeof html !== "string" || !html.trim()) invalid2("html must be a nonempty string.");
76
76
  if (typeof css !== "string") invalid2("css must be a string.");
77
+ if (typeof defaultStyles !== "boolean") invalid2("defaultStyles must be a boolean.");
77
78
  if (new TextEncoder().encode(html).byteLength + new TextEncoder().encode(css).byteLength > sourceLimit) invalid2("HTML and CSS together must not exceed 4,000,000 UTF-8 bytes.");
78
79
  if (!Number.isInteger(previewDpi) || previewDpi !== 0 && (previewDpi < 36 || previewDpi > 300)) invalid2("previewDpi must be 0 (disabled) or an integer from 36 to 300.");
79
80
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) invalid2("timeoutMs must be a positive integer no greater than 2147483647.");
@@ -99,9 +100,12 @@ var require_render_input = __commonJS({
99
100
  for (let i = 1; i < segments.length; i++) if (fileNames.has(segments.slice(0, i).join("/"))) invalid2(`Asset is both a file and a directory: ${name}`);
100
101
  return { name: "assets/" + name, data: bytes(assets[name], `assets[${name}]`) };
101
102
  });
102
- return { html, css, customFonts, assetFiles, previewDpi, timeoutMs, maxPages, allowMissingGlyphs, signal, isolation };
103
+ return { html, css, defaultStyles, customFonts, assetFiles, previewDpi, timeoutMs, maxPages, allowMissingGlyphs, signal, isolation };
103
104
  }
104
- module.exports = { FullbleedError: FullbleedError2, normalize };
105
+ function stylesheet(job) {
106
+ return (job.defaultStyles ? "body { font-family: Inter; }\n" : "") + job.css;
107
+ }
108
+ module.exports = { FullbleedError: FullbleedError2, normalize, stylesheet };
105
109
  }
106
110
  });
107
111
 
@@ -109,7 +113,7 @@ var require_render_input = __commonJS({
109
113
  init_define_FULLBLEED_FONT_NAMES();
110
114
  init_define_FULLBLEED_RUNTIME_FILES();
111
115
  var import_render_input = __toESM(require_render_input(), 1);
112
- var version = "0.4.1";
116
+ var version = "0.5.0";
113
117
  var engineVersion = "2.5.22";
114
118
  var FullbleedError = import_render_input.default.FullbleedError;
115
119
  var invalid = (message) => {
@@ -183,7 +187,7 @@ function createRenderer(options) {
183
187
  try {
184
188
  worker.postMessage({ type: "render", job: {
185
189
  html: job.html,
186
- css: "body { font-family: Inter; }\n" + job.css,
190
+ css: import_render_input.default.stylesheet(job),
187
191
  fonts: job.customFonts,
188
192
  assets: job.assetFiles,
189
193
  previewDpi: job.previewDpi,
@@ -2,7 +2,7 @@
2
2
  var define_FULLBLEED_FONT_NAMES_default = ["Inter-Variable.ttf", "DMSerifDisplay-Regular.ttf", "DMSerifDisplay-Italic.ttf", "BebasNeue-Regular.ttf"];
3
3
 
4
4
  // <define:__FULLBLEED_RUNTIME_FILES__>
5
- var define_FULLBLEED_RUNTIME_FILES_default = { "engine.wasm": { bytes: 5640040, sha256: "508c3dc58b51ec5568610cd5b8a329683f9e6d3677a464d8fc422ea9071b450d" }, "fonts/Inter-Variable.ttf": { bytes: 876576, sha256: "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031" }, "fonts/DMSerifDisplay-Regular.ttf": { bytes: 76580, sha256: "8cc3643535edf039aa5d95440a8542735e9197e4f4b8d9303e980fefbf5ab616" }, "fonts/DMSerifDisplay-Italic.ttf": { bytes: 71208, sha256: "df74c0ac387baeaeb0fe4f2324e1668e6a3ed8c09cd9796fe162c71753e19e45" }, "fonts/BebasNeue-Regular.ttf": { bytes: 61400, sha256: "08e4623805102d819f58601e46e345648846075e363b2ceb23313c2d1c83ec73" } };
5
+ var define_FULLBLEED_RUNTIME_FILES_default = { "engine.wasm": { bytes: 5640040, sha256: "a739438e01e9b52db48e47f1a604b700d98119b40db41361cc1563da059d1cbf" }, "fonts/Inter-Variable.ttf": { bytes: 876576, sha256: "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031" }, "fonts/DMSerifDisplay-Regular.ttf": { bytes: 76580, sha256: "8cc3643535edf039aa5d95440a8542735e9197e4f4b8d9303e980fefbf5ab616" }, "fonts/DMSerifDisplay-Italic.ttf": { bytes: 71208, sha256: "df74c0ac387baeaeb0fe4f2324e1668e6a3ed8c09cd9796fe162c71753e19e45" }, "fonts/BebasNeue-Regular.ttf": { bytes: 61400, sha256: "08e4623805102d819f58601e46e345648846075e363b2ceb23313c2d1c83ec73" } };
6
6
 
7
7
  // node_modules/@bjorn3/browser_wasi_shim/dist/wasi_defs.js
8
8
  var CLOCKID_REALTIME = 0;
@@ -1438,7 +1438,7 @@ async function runEngine(job) {
1438
1438
  }
1439
1439
 
1440
1440
  // src/browser-worker.js
1441
- var version = "0.4.1";
1441
+ var version = "0.5.0";
1442
1442
  var engineVersion = "2.5.22";
1443
1443
  var files = define_FULLBLEED_RUNTIME_FILES_default;
1444
1444
  var fontNames = define_FULLBLEED_FONT_NAMES_default;
package/dist/build.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "packageVersion": "0.4.1",
2
+ "packageVersion": "0.5.0",
3
3
  "engineVersion": "2.5.22",
4
4
  "engineSource": "https://crates.io/crates/fullbleed/2.5.22",
5
5
  "engineFeatures": [
@@ -48,7 +48,7 @@
48
48
  "files": {
49
49
  "dist/engine.wasm": {
50
50
  "bytes": 5640040,
51
- "sha256": "508c3dc58b51ec5568610cd5b8a329683f9e6d3677a464d8fc422ea9071b450d"
51
+ "sha256": "a739438e01e9b52db48e47f1a604b700d98119b40db41361cc1563da059d1cbf"
52
52
  },
53
53
  "dist/THIRD_PARTY_NOTICES.txt": {
54
54
  "bytes": 81572,
@@ -103,16 +103,16 @@
103
103
  "sha256": "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031"
104
104
  },
105
105
  "dist/browser/client.js": {
106
- "bytes": 12043,
107
- "sha256": "7cf701296f165f39bec30d992f6e9eacf2f0d09a6fa79bca84a598baa5fc3de9"
106
+ "bytes": 12323,
107
+ "sha256": "9a6287ef90efea5efd08f8c7ce5a068eddf612a55fb22d3602d0d12c1a33f65d"
108
108
  },
109
109
  "dist/browser/worker.js": {
110
110
  "bytes": 52109,
111
- "sha256": "44152f218784ffd37f891ca10681264074d4eb6a08380cd15e3548aead3ad675"
111
+ "sha256": "7ff41b156cb8b21c66719ab86691e0f31c7e95fd3ca11922e5ee0692b2dcc8b0"
112
112
  },
113
113
  "dist/browser/asset-manifest.json": {
114
114
  "bytes": 2443,
115
- "sha256": "211481ebdda0022296dbce43d95b0dd49848c8a4e7298aa075fda1f3701c6a46"
115
+ "sha256": "8eeccbcac3bc80594f9cbae279c9ad710dc34baaac5996f47ddf36eb93a4d967"
116
116
  }
117
117
  }
118
118
  }
package/dist/engine.wasm CHANGED
Binary file
package/dist/index.d.cts CHANGED
@@ -4,6 +4,8 @@ import type { Buffer } from 'node:buffer';
4
4
  export interface RenderOptions {
5
5
  html: string;
6
6
  css?: string;
7
+ /** Default true: prepend the package's Inter body rule. False passes CSS unchanged. */
8
+ defaultStyles?: boolean;
7
9
  /** Additional TrueType font files, as bytes. Use their family names in CSS. */
8
10
  fonts?: readonly Uint8Array[];
9
11
  /** Relative asset names; reference them as assets/name.svg in HTML/CSS. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullbleed",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Generate PDFs from HTML and CSS in Node.js or a browser worker with the Fullbleed Rust engine, bundled fonts, and optional PNG previews.",
5
5
  "type": "module",
6
6
  "main": "./src/index.cjs",
package/src/browser.d.ts CHANGED
@@ -6,6 +6,8 @@ export interface BrowserRendererOptions {
6
6
  export interface BrowserRenderOptions {
7
7
  html: string;
8
8
  css?: string;
9
+ /** Default true: prepend the package's Inter body rule. False passes CSS unchanged. */
10
+ defaultStyles?: boolean;
9
11
  /** Additional TrueType font bytes; use the font family names in CSS. */
10
12
  fonts?: readonly Uint8Array[];
11
13
  /** Relative names referenced as assets/name.svg in document HTML/CSS. */
package/src/browser.js CHANGED
@@ -66,7 +66,7 @@ export function createRenderer(options) {
66
66
  ready = true;
67
67
  try {
68
68
  worker.postMessage({ type: 'render', job: {
69
- html: job.html, css: 'body { font-family: Inter; }\n' + job.css,
69
+ html: job.html, css: input.stylesheet(job),
70
70
  fonts: job.customFonts, assets: job.assetFiles, previewDpi: job.previewDpi,
71
71
  maxPages: job.maxPages, allowMissingGlyphs: job.allowMissingGlyphs,
72
72
  } });
package/src/index.cjs CHANGED
@@ -8,7 +8,7 @@ const { fork } = require('node:child_process');
8
8
  const { pathToFileURL } = require('node:url');
9
9
  const pkg = require('../package.json');
10
10
 
11
- const { FullbleedError, normalize } = require('./render-input.cjs');
11
+ const { FullbleedError, normalize, stylesheet } = require('./render-input.cjs');
12
12
  const { createQueue } = require('./render-queue.cjs');
13
13
  const hash = bytes => createHash('sha256').update(bytes).digest('hex');
14
14
  const root = join(__dirname, '..');
@@ -136,7 +136,7 @@ function renderProcess(job, remaining) {
136
136
  child.send({ type: 'render', options: {
137
137
  html: job.html, css: job.css, fonts: job.customFonts.map(font => font.data),
138
138
  assets: Object.fromEntries(job.assetFiles.map(file => [file.name.slice('assets/'.length), file.data])),
139
- previewDpi: job.previewDpi, maxPages: job.maxPages, allowMissingGlyphs: job.allowMissingGlyphs,
139
+ previewDpi: job.previewDpi, maxPages: job.maxPages, allowMissingGlyphs: job.allowMissingGlyphs, defaultStyles: job.defaultStyles,
140
140
  timeoutMs: remaining,
141
141
  } }, error => {
142
142
  if (error) stop(new FullbleedError('PROCESS_FAILED', 'Could not send the rendering request.', { cause: error }));
@@ -198,7 +198,7 @@ async function renderJob(job, start) {
198
198
  eval: true,
199
199
  workerData: {
200
200
  module: loaded.module, fonts: [...loaded.fonts, ...job.customFonts], assets: job.assetFiles,
201
- html: job.html, css: 'body { font-family: Inter; }\n' + job.css, previewDpi: job.previewDpi, maxPages: job.maxPages,
201
+ html: job.html, css: stylesheet(job), previewDpi: job.previewDpi, maxPages: job.maxPages,
202
202
  allowMissingGlyphs: job.allowMissingGlyphs,
203
203
  },
204
204
  });
package/src/index.d.ts CHANGED
@@ -4,6 +4,8 @@ import type { Buffer } from 'node:buffer';
4
4
  export interface RenderOptions {
5
5
  html: string;
6
6
  css?: string;
7
+ /** Default true: prepend the package's Inter body rule. False passes CSS unchanged. */
8
+ defaultStyles?: boolean;
7
9
  /** Additional TrueType font files, as bytes. Use their family names in CSS. */
8
10
  fonts?: readonly Uint8Array[];
9
11
  /** Relative asset names; reference them as assets/name.svg in HTML/CSS. */
@@ -14,12 +14,13 @@ const assetLimit = 64 * 1024 * 1024;
14
14
 
15
15
  function normalize(options, { browser = false } = {}) {
16
16
  if (!options || typeof options !== 'object' || Array.isArray(options)) invalid('Pass an options object with an html string.');
17
- const supported = new Set(['html', 'css', 'fonts', 'assets', 'previewDpi', 'timeoutMs', 'maxPages', 'allowMissingGlyphs', 'signal']);
17
+ const supported = new Set(['html', 'css', 'defaultStyles', 'fonts', 'assets', 'previewDpi', 'timeoutMs', 'maxPages', 'allowMissingGlyphs', 'signal']);
18
18
  if (!browser) supported.add('isolation');
19
19
  for (const key of Object.keys(options)) if (!supported.has(key)) invalid(`Unknown option: ${key}`);
20
- const { html, css = '', fonts = [], assets = {}, previewDpi = 0, timeoutMs = 30000, maxPages = 1000, allowMissingGlyphs = false, signal, isolation = 'worker' } = options;
20
+ const { html, css = '', defaultStyles = true, fonts = [], assets = {}, previewDpi = 0, timeoutMs = 30000, maxPages = 1000, allowMissingGlyphs = false, signal, isolation = 'worker' } = options;
21
21
  if (typeof html !== 'string' || !html.trim()) invalid('html must be a nonempty string.');
22
22
  if (typeof css !== 'string') invalid('css must be a string.');
23
+ if (typeof defaultStyles !== 'boolean') invalid('defaultStyles must be a boolean.');
23
24
  if (new TextEncoder().encode(html).byteLength + new TextEncoder().encode(css).byteLength > sourceLimit) invalid('HTML and CSS together must not exceed 4,000,000 UTF-8 bytes.');
24
25
  if (!Number.isInteger(previewDpi) || (previewDpi !== 0 && (previewDpi < 36 || previewDpi > 300))) invalid('previewDpi must be 0 (disabled) or an integer from 36 to 300.');
25
26
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) invalid('timeoutMs must be a positive integer no greater than 2147483647.');
@@ -45,7 +46,11 @@ function normalize(options, { browser = false } = {}) {
45
46
  for (let i = 1; i < segments.length; i++) if (fileNames.has(segments.slice(0, i).join('/'))) invalid(`Asset is both a file and a directory: ${name}`);
46
47
  return { name: 'assets/' + name, data: bytes(assets[name], `assets[${name}]`) };
47
48
  });
48
- return { html, css, customFonts, assetFiles, previewDpi, timeoutMs, maxPages, allowMissingGlyphs, signal, isolation };
49
+ return { html, css, defaultStyles, customFonts, assetFiles, previewDpi, timeoutMs, maxPages, allowMissingGlyphs, signal, isolation };
49
50
  }
50
51
 
51
- module.exports = { FullbleedError, normalize };
52
+ function stylesheet(job) {
53
+ return (job.defaultStyles ? 'body { font-family: Inter; }\n' : '') + job.css;
54
+ }
55
+
56
+ module.exports = { FullbleedError, normalize, stylesheet };