@pdfme/ui 5.2.16 → 5.3.0-dev.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.
@@ -10,7 +10,7 @@ export declare abstract class BaseUIClass {
10
10
  private readonly setSize;
11
11
  resizeObserver: ResizeObserver;
12
12
  constructor(props: UIProps);
13
- protected getLang(): "en" | "th" | "pl" | "zh" | "ja" | "ko" | "ar" | "it" | "de" | "es" | "fr";
13
+ protected getLang(): "en" | "pl" | "zh" | "ja" | "ko" | "ar" | "th" | "it" | "de" | "es" | "fr";
14
14
  protected getFont(): Record<string, {
15
15
  data: string | ArrayBuffer | Uint8Array;
16
16
  fallback?: boolean | undefined;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Mode, UIRenderProps, SchemaForUI, BasePdf, Schema, Plugin, UIOptions } from '@pdfme/common';
3
- type RendererProps = Omit<UIRenderProps<Schema>, 'schema' | 'rootElement' | 'options' | 'theme' | 'i18n' | 'pdfJs' | '_cache'> & {
3
+ type RendererProps = Omit<UIRenderProps<Schema>, 'schema' | 'rootElement' | 'options' | 'theme' | 'i18n' | '_cache'> & {
4
4
  basePdf: BasePdf;
5
5
  schema: SchemaForUI;
6
6
  value: string;
@@ -6,3 +6,4 @@ export declare const PAGE_GAP = 10;
6
6
  export declare const LEFT_SIDEBAR_WIDTH = 45;
7
7
  export declare const RIGHT_SIDEBAR_WIDTH = 400;
8
8
  export declare const BACKGROUND_COLOR = "rgb(74, 74, 74)";
9
+ export declare const MAX_ZOOM = 2;
@@ -17,12 +17,7 @@ export declare const initShortCuts: (arg: {
17
17
  selectAll: () => void;
18
18
  }) => void;
19
19
  export declare const destroyShortCuts: () => void;
20
- export declare const getPdfPageSizes: (pdfBlob: Blob) => Promise<{
21
- height: number;
22
- width: number;
23
- }[]>;
24
- export declare const pdf2Pngs: (pdfBlob: Blob, width: number) => Promise<string[]>;
25
- export declare const b64toBlob: (base64: string) => Blob;
20
+ export declare const arrayBufferToBase64: (arrayBuffer: ArrayBuffer) => string;
26
21
  export declare const template2SchemasList: (_template: Template) => Promise<{
27
22
  width: number;
28
23
  height: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/ui",
3
- "version": "5.2.16",
3
+ "version": "5.3.0-dev.1",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -38,12 +38,12 @@
38
38
  "dependencies": {
39
39
  "@dnd-kit/core": "^6.0.8",
40
40
  "@dnd-kit/sortable": "^7.0.2",
41
+ "@pdfme/converter": "file:../converter",
41
42
  "@scena/react-guides": "^0.16.0",
42
43
  "antd": "^5.9.4",
43
44
  "form-render": "^2.2.16",
44
45
  "hotkeys-js": "^3.8.7",
45
46
  "lucide-react": "^0.460.0",
46
- "pdfjs-dist": "^4.8.69",
47
47
  "react": "^16.14.0",
48
48
  "react-dom": "^16.14.0",
49
49
  "react-moveable": "^0.30.3",
@@ -83,13 +83,12 @@
83
83
  ],
84
84
  "moduleNameMapper": {
85
85
  "\\.(png|css)$": "<rootDir>/__mocks__/assetsTransformer.js",
86
+ "^@pdfme/converter$": "<rootDir>/../converter/src/index.node.ts",
86
87
  "^@pdfme/schemas/utils$": "<rootDir>/../schemas/src/utils.ts",
87
88
  "^antd/es/": "antd/lib/",
88
89
  "^form-render/es/": "form-render/lib/",
89
90
  "^rc-picker/es/": "rc-picker/lib/",
90
- "^lodash-es$": "lodash",
91
- "^pdfjs-dist/legacy/build/pdf.js$": "<rootDir>/__mocks__/pdfjs-dist.js",
92
- "^pdfjs-dist/legacy/build/pdf.worker.entry.js$": "<rootDir>/__mocks__/pdfjs-dist.js"
91
+ "^lodash-es$": "lodash"
93
92
  },
94
93
  "resolver": "ts-jest-resolver",
95
94
  "moduleFileExtensions": [
@@ -5,6 +5,7 @@ import { Plus, Minus, ChevronLeft, ChevronRight, Ellipsis } from 'lucide-react';
5
5
  import type { MenuProps } from 'antd';
6
6
  import { theme, Typography, Button, Dropdown } from 'antd';
7
7
  import { I18nContext } from '../contexts';
8
+ import { MAX_ZOOM } from '../constants';
8
9
 
9
10
  const { Text } = Typography;
10
11
 
@@ -17,7 +18,7 @@ type ZoomProps = {
17
18
 
18
19
  const Zoom = ({ zoomLevel, setZoomLevel, style }: ZoomProps) => {
19
20
  const zoomStep = 0.25;
20
- const maxZoom = 2;
21
+ const maxZoom = MAX_ZOOM;
21
22
  const minZoom = 0.25;
22
23
 
23
24
  const nextZoomOut = zoomLevel - zoomStep;
@@ -1,7 +1,6 @@
1
- import { Button, Form } from 'antd';
1
+ import { Button, Form, theme } from 'antd';
2
2
  import React from 'react';
3
3
  import type { PropPanelWidgetProps, SchemaForUI } from '@pdfme/common';
4
-
5
4
  interface ButtonConfig {
6
5
  key: string;
7
6
  icon: string;
@@ -11,6 +10,7 @@ interface ButtonConfig {
11
10
 
12
11
  const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
13
12
  const { activeElements, changeSchemas, schemas, schema } = props;
13
+ const { token } = theme.useToken();
14
14
 
15
15
  const apply = (btn: ButtonConfig) => {
16
16
  const key = btn.key;
@@ -38,8 +38,13 @@ const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
38
38
  return active;
39
39
  };
40
40
 
41
+ const replaceCurrentColor = (svgString: string, color?: string) =>
42
+ color ? svgString.replace(/="currentColor"/g, `="${color}"`) : svgString;
43
+
41
44
  const svgIcon = (svgString: string) => {
42
- const svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
45
+ const svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(
46
+ replaceCurrentColor(svgString, token.colorText)
47
+ )}`;
43
48
  return <img width={17} height={17} src={svgDataUrl} />;
44
49
  };
45
50
 
@@ -3,11 +3,10 @@ import { Dict, Mode, ZOOM, UIRenderProps, SchemaForUI, BasePdf, Schema, Plugin,
3
3
  import { theme as antdTheme } from 'antd';
4
4
  import { SELECTABLE_CLASSNAME } from '../constants';
5
5
  import { PluginsRegistry, OptionsContext, I18nContext } from '../contexts';
6
- import * as pdfJs from 'pdfjs-dist';
7
6
 
8
7
  type RendererProps = Omit<
9
8
  UIRenderProps<Schema>,
10
- 'schema' | 'rootElement' | 'options' | 'theme' | 'i18n' | 'pdfJs' | '_cache'
9
+ 'schema' | 'rootElement' | 'options' | 'theme' | 'i18n' | '_cache'
11
10
  > & {
12
11
  basePdf: BasePdf;
13
12
  schema: SchemaForUI;
@@ -128,7 +127,6 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
128
127
  options,
129
128
  theme,
130
129
  i18n,
131
- pdfJs,
132
130
  _cache: _cache.current,
133
131
  });
134
132
  }
package/src/constants.ts CHANGED
@@ -13,3 +13,5 @@ export const LEFT_SIDEBAR_WIDTH = 45;
13
13
  export const RIGHT_SIDEBAR_WIDTH = 400;
14
14
 
15
15
  export const BACKGROUND_COLOR = 'rgb(74, 74, 74)';
16
+
17
+ export const MAX_ZOOM = 2;
package/src/helper.ts CHANGED
@@ -1,11 +1,9 @@
1
- import { getDocument, GlobalWorkerOptions, version } from 'pdfjs-dist';
2
1
  import hotkeys from 'hotkeys-js';
3
2
  import {
4
3
  cloneDeep,
5
4
  ZOOM,
6
5
  getB64BasePdf,
7
6
  b64toUint8Array,
8
- pt2mm,
9
7
  Template,
10
8
  BasePdf,
11
9
  SchemaForUI,
@@ -13,10 +11,9 @@ import {
13
11
  isBlankPdf,
14
12
  Plugins,
15
13
  } from '@pdfme/common';
14
+ import { pdf2size } from '@pdfme/converter';
16
15
  import { RULER_HEIGHT } from './constants.js';
17
16
 
18
- GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${version}/build/pdf.worker.min.mjs`;
19
-
20
17
  export const uuid = () =>
21
18
  'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
22
19
  const r = (Math.random() * 16) | 0;
@@ -191,61 +188,59 @@ export const destroyShortCuts = () => {
191
188
  hotkeys.unbind(keys.join());
192
189
  };
193
190
 
194
- export const getPdfPageSizes = async (pdfBlob: Blob) => {
195
- const url = URL.createObjectURL(pdfBlob);
196
- const pdfDoc = await getDocument({ url }).promise;
197
-
198
- const promises = Promise.all(
199
- new Array(pdfDoc.numPages).fill('').map(async (_, i) => {
200
- return await pdfDoc.getPage(i + 1).then((page) => {
201
- const { height, width } = page.getViewport({ scale: 1, rotation: 0 });
202
-
203
- return { height: pt2mm(height), width: pt2mm(width) };
204
- });
205
- })
206
- );
207
-
208
- URL.revokeObjectURL(url);
209
-
210
- return promises;
211
- };
191
+ /**
192
+ * Guess the MIME type by checking the first few bytes of the ArrayBuffer.
193
+ * Currently checks for PNG, JPEG, and GIF signatures.
194
+ */
195
+ function detectMimeType(arrayBuffer: ArrayBuffer): string {
196
+ const dataView = new DataView(arrayBuffer);
197
+
198
+ // Check for PNG signature: 0x89 0x50 0x4E 0x47
199
+ if (
200
+ dataView.getUint8(0) === 0x89 &&
201
+ dataView.getUint8(1) === 0x50 &&
202
+ dataView.getUint8(2) === 0x4e &&
203
+ dataView.getUint8(3) === 0x47
204
+ ) {
205
+ return 'image/png';
206
+ }
212
207
 
213
- const pdf2Images = async (pdfBlob: Blob, width: number, imageType: 'png' | 'jpeg') => {
214
- const url = URL.createObjectURL(pdfBlob);
215
- const pdfDoc = await getDocument({ url }).promise;
216
-
217
- const promises = Promise.all(
218
- new Array(pdfDoc.numPages).fill('').map(async (_, i) => {
219
- return await pdfDoc.getPage(i + 1).then((page) => {
220
- const canvas = document.createElement('canvas');
221
- canvas.width = width * 2;
222
- const canvasContext = canvas.getContext('2d')!;
223
- const scaleRequired = canvas.width / page.getViewport({ scale: 1, rotation: 0 }).width;
224
- const viewport = page.getViewport({ scale: scaleRequired, rotation: 0 });
225
- canvas.height = viewport.height;
226
-
227
- return page
228
- .render({ canvasContext, viewport })
229
- .promise.then(() => canvas.toDataURL(`image/${imageType}`));
230
- });
231
- })
232
- );
233
- URL.revokeObjectURL(url);
208
+ // Check for JPEG signature: 0xFF 0xD8 0xFF
209
+ if (
210
+ dataView.getUint8(0) === 0xff &&
211
+ dataView.getUint8(1) === 0xd8 &&
212
+ dataView.getUint8(2) === 0xff
213
+ ) {
214
+ return 'image/jpeg';
215
+ }
234
216
 
235
- return promises;
236
- };
217
+ return ''; // Unknown type
218
+ }
237
219
 
238
- export const pdf2Pngs = (pdfBlob: Blob, width: number) => pdf2Images(pdfBlob, width, 'png');
220
+ export const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer): string => {
221
+ // Detect the MIME type
222
+ const mimeType = detectMimeType(arrayBuffer);
239
223
 
240
- export const b64toBlob = (base64: string) => {
241
- const uint8Array = b64toUint8Array(base64);
242
- const [, , mimeType] = base64.match(/(:)([a-z/]+)(;)/)!;
224
+ // Convert ArrayBuffer to raw Base64
225
+ const bytes = new Uint8Array(arrayBuffer);
226
+ let binary = '';
227
+ for (let i = 0; i < bytes.length; i++) {
228
+ binary += String.fromCharCode(bytes[i]);
229
+ }
230
+ const base64String = btoa(binary);
243
231
 
244
- return new Blob([uint8Array.buffer], { type: mimeType });
232
+ // Optionally prepend a data: URL if a known MIME type is found;
233
+ // otherwise just return the raw Base64.
234
+ if (mimeType) {
235
+ return `data:${mimeType};base64,${base64String}`;
236
+ } else {
237
+ // or you can default to `application/octet-stream` if unknown
238
+ return `data:application/octet-stream;base64,${base64String}`;
239
+ }
245
240
  };
246
241
 
247
242
  const convertSchemasForUI = (template: Template): SchemaForUI[][] => {
248
- template.schemas.forEach((page, i) => {
243
+ template.schemas.forEach((page) => {
249
244
  page.forEach((schema) => {
250
245
  schema.id = uuid();
251
246
  schema.content = schema.content || '';
@@ -268,8 +263,7 @@ export const template2SchemasList = async (_template: Template) => {
268
263
  }));
269
264
  } else {
270
265
  const b64BasePdf = await getB64BasePdf(basePdf);
271
- const pdfBlob = b64toBlob(b64BasePdf);
272
- pageSizes = await getPdfPageSizes(pdfBlob);
266
+ pageSizes = await pdf2size(b64toUint8Array(b64BasePdf));
273
267
  }
274
268
 
275
269
  const ssl = schemasForUI.length;
package/src/hooks.ts CHANGED
@@ -5,23 +5,23 @@ import {
5
5
  Template,
6
6
  Size,
7
7
  getB64BasePdf,
8
+ b64toUint8Array,
8
9
  SchemaForUI,
9
10
  ChangeSchemas,
10
11
  isBlankPdf,
11
12
  } from '@pdfme/common';
13
+ import { pdf2img, pdf2size } from '@pdfme/converter';
12
14
 
13
15
  import {
14
16
  schemasList2template,
15
17
  uuid,
16
18
  getUniqueSchemaName,
17
19
  moveCommandToChangeSchemasArg,
18
- pdf2Pngs,
19
- getPdfPageSizes,
20
- b64toBlob,
20
+ arrayBufferToBase64,
21
21
  initShortCuts,
22
22
  destroyShortCuts,
23
23
  } from './helper.js';
24
- import { RULER_HEIGHT } from './constants.js';
24
+ import { RULER_HEIGHT, MAX_ZOOM } from './constants.js';
25
25
 
26
26
  export const usePrevious = <T>(value: T) => {
27
27
  const ref = useRef<T | null>(null);
@@ -48,7 +48,11 @@ export const useUIPreProcessor = ({ template, size, zoomLevel }: UIPreProcessorP
48
48
  template: { basePdf, schemas },
49
49
  size,
50
50
  } = prop;
51
- let paperWidth, paperHeight, _backgrounds, _pageSizes;
51
+
52
+ let paperWidth: number;
53
+ let paperHeight: number;
54
+ let _backgrounds: string[];
55
+ let _pageSizes: { width: number; height: number }[];
52
56
 
53
57
  if (isBlankPdf(basePdf)) {
54
58
  const { width, height } = basePdf;
@@ -61,18 +65,28 @@ export const useUIPreProcessor = ({ template, size, zoomLevel }: UIPreProcessorP
61
65
  _pageSizes = schemas.map(() => ({ width, height }));
62
66
  } else {
63
67
  const _basePdf = await getB64BasePdf(basePdf);
64
- const pdfBlob = b64toBlob(_basePdf);
65
- _pageSizes = await getPdfPageSizes(pdfBlob);
68
+
69
+ const [_pages, imgBuffers] = await Promise.all([
70
+ pdf2size(b64toUint8Array(_basePdf)),
71
+ pdf2img(b64toUint8Array(_basePdf), { scale: MAX_ZOOM }),
72
+ ]);
73
+
74
+ _pageSizes = _pages;
66
75
  paperWidth = _pageSizes[0].width * ZOOM;
67
76
  paperHeight = _pageSizes[0].height * ZOOM;
68
- _backgrounds = await pdf2Pngs(pdfBlob, paperWidth);
77
+ _backgrounds = imgBuffers.map(arrayBufferToBase64);
69
78
  }
79
+
70
80
  const _scale = Math.min(
71
81
  getScale(size.width, paperWidth),
72
82
  getScale(size.height - RULER_HEIGHT, paperHeight)
73
83
  );
74
84
 
75
- return { backgrounds: _backgrounds, pageSizes: _pageSizes, scale: _scale };
85
+ return {
86
+ backgrounds: _backgrounds,
87
+ pageSizes: _pageSizes,
88
+ scale: _scale,
89
+ };
76
90
  };
77
91
 
78
92
  useEffect(() => {
@@ -82,7 +96,7 @@ export const useUIPreProcessor = ({ template, size, zoomLevel }: UIPreProcessorP
82
96
  })
83
97
  .catch((err: Error) => {
84
98
  setError(err);
85
- console.error(`[@pdfme/ui] ${err}`);
99
+ console.error('[@pdfme/ui]', err);
86
100
  });
87
101
  }, [template, size]);
88
102
 
@@ -230,7 +244,11 @@ export const useInitEvents = ({
230
244
  const stackUniqueSchemaNames: string[] = [];
231
245
  const pasteSchemas = copiedSchemas.current.map((cs) => {
232
246
  const id = uuid();
233
- const name = getUniqueSchemaName({ copiedSchemaName: cs.name, schema, stackUniqueSchemaNames });
247
+ const name = getUniqueSchemaName({
248
+ copiedSchemaName: cs.name,
249
+ schema,
250
+ stackUniqueSchemaNames,
251
+ });
234
252
  const { height, width, position: p } = cs;
235
253
  const ps = pageSizes[pageCursor];
236
254
  const position = {
package/vite.config.ts CHANGED
@@ -16,7 +16,7 @@ export default defineConfig(({ mode }) => {
16
16
  },
17
17
  optimizeDeps: {
18
18
  include: ['react', 'react-dom', 'pdfjs-dist', 'antd'],
19
- exclude: ['@pdfme/common', '@pdfme/schemas'],
19
+ exclude: ['@pdfme/common', '@pdfme/schemas', '@pdfme/converter'],
20
20
  },
21
21
  };
22
22
  });
@@ -1,15 +0,0 @@
1
- const getDocument = jest.fn().mockImplementation(() => {
2
- return {
3
- promise: Promise.resolve({
4
- }),
5
- };
6
- });
7
-
8
- const GlobalWorkerOptions = {
9
- workerSrc: 'mock-worker-src.js',
10
- };
11
-
12
- module.exports = {
13
- getDocument,
14
- GlobalWorkerOptions,
15
- };
@@ -1,4 +0,0 @@
1
- const e = {};
2
- export {
3
- e as default
4
- };