@polotno/pdf-export 0.1.25 → 0.1.27

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.
Files changed (2) hide show
  1. package/lib/text.js +37 -12
  2. package/package.json +2 -1
package/lib/text.js CHANGED
@@ -27,6 +27,8 @@ function normalizeRichText(text) {
27
27
  normalized = normalized.replace(/\n{3,}/g, '\n\n');
28
28
  // Trim stray leading/trailing newlines introduced by paragraph conversion
29
29
  normalized = normalized.replace(/^\n+/, '').replace(/\n+$/, '');
30
+ // Decode common HTML non-breaking space entities into their unicode counterpart
31
+ normalized = normalized.replace(/&(nbsp|#160|#xA0);/gi, '\u00A0');
30
32
  return normalized;
31
33
  }
32
34
  /**
@@ -277,11 +279,23 @@ function serializeAttributes(attributes) {
277
279
  .map(([key, value]) => value === '' ? key : `${key}="${value.replace(/"/g, '"')}"`)
278
280
  .join(' '));
279
281
  }
282
+ function shouldSkipNode(node) {
283
+ if (node.type === 'element' && node.tagName === 'span') {
284
+ const classAttr = node.attributes['class'] || node.attributes['className'] || '';
285
+ if (/\bql-cursor\b/.test(classAttr)) {
286
+ return true;
287
+ }
288
+ }
289
+ return false;
290
+ }
280
291
  function serializeNodes(nodes) {
281
292
  return nodes
282
293
  .map((node) => {
294
+ if (shouldSkipNode(node)) {
295
+ return '';
296
+ }
283
297
  if (node.type === 'text') {
284
- return node.content;
298
+ return node.content.replace(/\uFEFF/g, '');
285
299
  }
286
300
  const attrs = serializeAttributes(node.attributes);
287
301
  if (VOID_ELEMENTS.has(node.tagName)) {
@@ -325,23 +339,34 @@ function nodesToParagraphs(nodes, baseIndentLevel = 0) {
325
339
  pendingInline = [];
326
340
  };
327
341
  for (const node of nodes) {
342
+ if (shouldSkipNode(node)) {
343
+ continue;
344
+ }
328
345
  if (node.type === 'text') {
329
- const parts = node.content.split('\n');
330
- for (let partIndex = 0; partIndex < parts.length; partIndex++) {
331
- const part = parts[partIndex];
332
- if (part !== '' &&
333
- !(pendingInline.length === 0 && /^\s*$/.test(part))) {
346
+ const newlineRegex = /\n+/g;
347
+ let lastIndex = 0;
348
+ let match;
349
+ while ((match = newlineRegex.exec(node.content)) !== null) {
350
+ const chunk = node.content.slice(lastIndex, match.index);
351
+ if (chunk.length > 0) {
334
352
  pendingInline.push({
335
353
  type: 'text',
336
- content: part,
354
+ content: chunk,
337
355
  });
338
356
  }
339
- if (partIndex < parts.length - 1) {
340
- flushInline();
341
- if (parts[partIndex + 1] === '') {
342
- paragraphs.push({ html: '' });
343
- }
357
+ flushInline();
358
+ const extraBreaks = match[0].length - 1;
359
+ for (let extra = 0; extra < extraBreaks; extra++) {
360
+ paragraphs.push({ html: '' });
344
361
  }
362
+ lastIndex = newlineRegex.lastIndex;
363
+ }
364
+ const rest = node.content.slice(lastIndex);
365
+ if (rest.length > 0) {
366
+ pendingInline.push({
367
+ type: 'text',
368
+ content: rest,
369
+ });
345
370
  }
346
371
  continue;
347
372
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polotno/pdf-export",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Convert Polotno JSON into vector PDF",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "scripts": {
15
15
  "build": "tsc",
16
+ "prepublishOnly": "npm run build",
16
17
  "test": "vitest",
17
18
  "test:update-snapshots": "vitest -u",
18
19
  "dev": "vite client",