@pollium/vyva-convert 0.4.0 → 0.5.1

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
@@ -81,7 +81,9 @@ const { title, content } = await importFile(upload.bytes, upload.filename, {
81
81
  keeps its theme colors and the font most of its text uses. What its slide master draws comes in
82
82
  locked, behind the slide's own elements, and freeform shapes come in as polygons.
83
83
  - A PDF becomes a deck of its own pages: one slide for each page, at the size of the first page,
84
- with that page behind it. Nothing is converted, so it looks exactly like the original.
84
+ with that page behind it. Nothing is converted, so it looks exactly like the original. The deck
85
+ takes the font most of the text of its first five pages uses, so text written over a page
86
+ matches it.
85
87
  `saveImage` stores the PDF once, as `application/pdf`; return `null` and the import is refused as
86
88
  `empty`. A PDF Vyva cannot open is refused as `unsupported`, and one of more than 1000 pages as
87
89
  `too-large`.
@@ -1,12 +1,43 @@
1
1
  import { ConvertError } from "../errors.js";
2
2
  import { titleOf } from "./titles.js";
3
3
  import { PLAIN_THEME } from "./plain-theme.js";
4
+ import { familyOf } from "./pptx-theme.js";
4
5
  import { MAX_EXTENT, MAX_SLIDES } from "@pollium/vyva-core/content";
5
6
  import { newId } from "@pollium/vyva-core/ids";
6
7
  import { getDocumentProxy } from "unpdf";
7
8
  //#region src/import/pdf.ts
8
9
  const PX_PER_POINT = 2;
9
10
  const PDF_TYPE = "application/pdf";
11
+ const FONT_PAGES = 5;
12
+ const SUBSET = /^[A-Z]{6}\+/;
13
+ const STYLE = /[,-].*$/;
14
+ const MAKER = /(PSMT|PS|MT)$/;
15
+ const WORDS = /([a-z])([A-Z])/g;
16
+ const LATEX_SERIF = /^(CM(R|BX|TI|SL)\d|SF(RM|BX|TI)\d|NimbusRom|LMRoman)/;
17
+ const GENERIC = {
18
+ serif: "Lora",
19
+ monospace: "JetBrains Mono"
20
+ };
21
+ const familyOfPdf = (name, generic) => {
22
+ const base = name.replace(SUBSET, "").replace(STYLE, "");
23
+ if (LATEX_SERIF.test(base)) return "Lora";
24
+ return familyOf(base.replace(MAKER, "").replace(WORDS, "$1 $2")) ?? GENERIC[generic] ?? PLAIN_THEME.font;
25
+ };
26
+ const dominantFont = async (pdf) => {
27
+ const counts = /* @__PURE__ */ new Map();
28
+ for (let number = 1; number <= Math.min(pdf.numPages, FONT_PAGES); number++) {
29
+ const page = await pdf.getPage(number);
30
+ await page.getOperatorList();
31
+ const content = await page.getTextContent();
32
+ for (const item of content.items) {
33
+ if (!("str" in item)) continue;
34
+ const font = page.commonObjs.has(item.fontName) ? page.commonObjs.get(item.fontName) : {};
35
+ const family = familyOfPdf(typeof font.name === "string" ? font.name : "", content.styles[item.fontName]?.fontFamily ?? "");
36
+ counts.set(family, (counts.get(family) ?? 0) + item.str.length);
37
+ }
38
+ }
39
+ return [...counts].sort((a, b) => b[1] - a[1])[0]?.[0] ?? PLAIN_THEME.font;
40
+ };
10
41
  const slideSizeOf = (width, height) => {
11
42
  const scale = Math.min(PX_PER_POINT, MAX_EXTENT / Math.max(width, height));
12
43
  return {
@@ -14,7 +45,7 @@ const slideSizeOf = (width, height) => {
14
45
  height: Math.max(1, Math.round(height * scale))
15
46
  };
16
47
  };
17
- const opened = (buffer, filename) => getDocumentProxy(new Uint8Array(buffer)).catch((error) => {
48
+ const opened = (buffer, filename) => getDocumentProxy(new Uint8Array(buffer), { fontExtraProperties: true }).catch((error) => {
18
49
  throw new ConvertError("unsupported", `${filename} is a PDF Vyva cannot open`, { cause: error });
19
50
  });
20
51
  const importPdf = async (buffer, filename, saveImage) => {
@@ -32,7 +63,10 @@ const importPdf = async (buffer, filename, saveImage) => {
32
63
  content: {
33
64
  kind: "deck",
34
65
  format: slideSizeOf(width, height),
35
- theme: PLAIN_THEME,
66
+ theme: {
67
+ ...PLAIN_THEME,
68
+ font: await dominantFont(pdf)
69
+ },
36
70
  slides: Array.from({ length: pdf.numPages }, (_, index) => ({
37
71
  id: newId(),
38
72
  background: {
@@ -37,8 +37,10 @@ const FONT_SUBSTITUTES = {
37
37
  "gill sans mt": "Montserrat",
38
38
  georgia: "Merriweather",
39
39
  cambria: "Lora",
40
+ times: "Lora",
40
41
  "times new roman": "Lora",
41
42
  garamond: "Lora",
43
+ courier: "JetBrains Mono",
42
44
  "courier new": "JetBrains Mono",
43
45
  consolas: "JetBrains Mono"
44
46
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pollium/vyva-convert",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Server-side tools for Vyva content: clean what clients send, import Word, PowerPoint, PDF, OpenDocument, Markdown and image files, and export to PDF, Word, PowerPoint and HTML.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -37,7 +37,7 @@
37
37
  "pdf-lib": "1.17.1",
38
38
  "pptxgenjs": "^4.0.1",
39
39
  "sanitize-html": "^2.18.0",
40
- "unpdf": "^1.8.1",
40
+ "unpdf": "^1.9.1",
41
41
  "@pollium/vyva-core": "0.3.0"
42
42
  },
43
43
  "devDependencies": {