@pdfme/schemas 3.1.5-dev.9 → 3.2.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.
Files changed (159) hide show
  1. package/dist/cjs/__tests__/{renderUtils.test.js → utils.test.js} +38 -12
  2. package/dist/cjs/__tests__/utils.test.js.map +1 -0
  3. package/dist/cjs/src/barcodes/index.js +2 -2
  4. package/dist/cjs/src/barcodes/index.js.map +1 -1
  5. package/dist/cjs/src/barcodes/pdfRender.js +2 -2
  6. package/dist/cjs/src/barcodes/pdfRender.js.map +1 -1
  7. package/dist/cjs/src/barcodes/propPanel.js +1 -1
  8. package/dist/cjs/src/barcodes/propPanel.js.map +1 -1
  9. package/dist/cjs/src/barcodes/uiRender.js +9 -32
  10. package/dist/cjs/src/barcodes/uiRender.js.map +1 -1
  11. package/dist/cjs/src/graphics/helper.js +115 -0
  12. package/dist/cjs/src/graphics/helper.js.map +1 -0
  13. package/dist/cjs/src/graphics/image.js +179 -0
  14. package/dist/cjs/src/graphics/image.js.map +1 -0
  15. package/dist/cjs/src/graphics/svg.js +96 -0
  16. package/dist/cjs/src/graphics/svg.js.map +1 -0
  17. package/dist/cjs/src/index.js +42 -11
  18. package/dist/cjs/src/index.js.map +1 -1
  19. package/dist/cjs/src/shapes/line.js +57 -0
  20. package/dist/cjs/src/shapes/line.js.map +1 -0
  21. package/dist/cjs/src/shapes/rectAndEllipse.js +100 -0
  22. package/dist/cjs/src/shapes/rectAndEllipse.js.map +1 -0
  23. package/dist/cjs/src/text/helper.js.map +1 -1
  24. package/dist/cjs/src/text/index.js +16 -2
  25. package/dist/cjs/src/text/index.js.map +1 -1
  26. package/dist/cjs/src/text/pdfRender.js +5 -5
  27. package/dist/cjs/src/text/pdfRender.js.map +1 -1
  28. package/dist/cjs/src/text/uiRender.js +3 -3
  29. package/dist/cjs/src/text/uiRender.js.map +1 -1
  30. package/dist/cjs/src/{renderUtils.js → utils.js} +55 -7
  31. package/dist/cjs/src/utils.js.map +1 -0
  32. package/dist/esm/__tests__/{renderUtils.test.js → utils.test.js} +34 -8
  33. package/dist/esm/__tests__/utils.test.js.map +1 -0
  34. package/dist/esm/src/barcodes/index.js +2 -2
  35. package/dist/esm/src/barcodes/index.js.map +1 -1
  36. package/dist/esm/src/barcodes/pdfRender.js +1 -1
  37. package/dist/esm/src/barcodes/pdfRender.js.map +1 -1
  38. package/dist/esm/src/barcodes/propPanel.js +1 -1
  39. package/dist/esm/src/barcodes/propPanel.js.map +1 -1
  40. package/dist/esm/src/barcodes/uiRender.js +9 -32
  41. package/dist/esm/src/barcodes/uiRender.js.map +1 -1
  42. package/dist/esm/src/graphics/helper.js +111 -0
  43. package/dist/esm/src/graphics/helper.js.map +1 -0
  44. package/dist/esm/src/graphics/image.js +176 -0
  45. package/dist/esm/src/graphics/image.js.map +1 -0
  46. package/dist/esm/src/graphics/svg.js +93 -0
  47. package/dist/esm/src/graphics/svg.js.map +1 -0
  48. package/dist/esm/src/index.js +7 -5
  49. package/dist/esm/src/index.js.map +1 -1
  50. package/dist/esm/src/shapes/line.js +55 -0
  51. package/dist/esm/src/shapes/line.js.map +1 -0
  52. package/dist/esm/src/shapes/rectAndEllipse.js +97 -0
  53. package/dist/esm/src/shapes/rectAndEllipse.js.map +1 -0
  54. package/dist/esm/src/text/helper.js.map +1 -1
  55. package/dist/esm/src/text/index.js +15 -2
  56. package/dist/esm/src/text/index.js.map +1 -1
  57. package/dist/esm/src/text/pdfRender.js +1 -1
  58. package/dist/esm/src/text/pdfRender.js.map +1 -1
  59. package/dist/esm/src/text/uiRender.js +2 -2
  60. package/dist/esm/src/text/uiRender.js.map +1 -1
  61. package/dist/esm/src/{renderUtils.js → utils.js} +52 -6
  62. package/dist/esm/src/utils.js.map +1 -0
  63. package/dist/types/src/barcodes/index.d.ts +2 -2
  64. package/dist/types/src/graphics/helper.d.ts +5 -0
  65. package/dist/types/src/graphics/image.d.ts +7 -0
  66. package/dist/types/src/graphics/svg.d.ts +6 -0
  67. package/dist/types/src/index.d.ts +7 -5
  68. package/dist/types/src/shapes/line.d.ts +6 -0
  69. package/dist/types/src/shapes/rectAndEllipse.d.ts +144 -0
  70. package/dist/types/src/text/index.d.ts +3 -2
  71. package/dist/types/src/text/pdfRender.d.ts +1 -1
  72. package/dist/types/src/{renderUtils.d.ts → utils.d.ts} +3 -1
  73. package/package.json +4 -3
  74. package/src/barcodes/index.ts +2 -2
  75. package/src/barcodes/pdfRender.ts +1 -1
  76. package/src/barcodes/propPanel.ts +1 -1
  77. package/src/barcodes/uiRender.ts +9 -36
  78. package/src/graphics/helper.ts +147 -0
  79. package/src/graphics/image.ts +207 -0
  80. package/src/graphics/svg.ts +98 -0
  81. package/src/index.ts +21 -5
  82. package/src/shapes/line.ts +67 -0
  83. package/src/shapes/rectAndEllipse.ts +110 -0
  84. package/src/text/helper.ts +0 -1
  85. package/src/text/index.ts +17 -2
  86. package/src/text/pdfRender.ts +2 -2
  87. package/src/text/uiRender.ts +2 -2
  88. package/src/{renderUtils.ts → utils.ts} +61 -5
  89. package/dist/cjs/__tests__/renderUtils.test.js.map +0 -1
  90. package/dist/cjs/src/image/index.js +0 -8
  91. package/dist/cjs/src/image/index.js.map +0 -1
  92. package/dist/cjs/src/image/pdfRender.js +0 -22
  93. package/dist/cjs/src/image/pdfRender.js.map +0 -1
  94. package/dist/cjs/src/image/propPanel.js +0 -19
  95. package/dist/cjs/src/image/propPanel.js.map +0 -1
  96. package/dist/cjs/src/image/types.js +0 -3
  97. package/dist/cjs/src/image/types.js.map +0 -1
  98. package/dist/cjs/src/image/uiRender.js +0 -111
  99. package/dist/cjs/src/image/uiRender.js.map +0 -1
  100. package/dist/cjs/src/line/constants.js +0 -5
  101. package/dist/cjs/src/line/constants.js.map +0 -1
  102. package/dist/cjs/src/line/index.js +0 -8
  103. package/dist/cjs/src/line/index.js.map +0 -1
  104. package/dist/cjs/src/line/pdfRender.js +0 -20
  105. package/dist/cjs/src/line/pdfRender.js.map +0 -1
  106. package/dist/cjs/src/line/propPanel.js +0 -35
  107. package/dist/cjs/src/line/propPanel.js.map +0 -1
  108. package/dist/cjs/src/line/types.js +0 -3
  109. package/dist/cjs/src/line/types.js.map +0 -1
  110. package/dist/cjs/src/line/uiRender.js +0 -14
  111. package/dist/cjs/src/line/uiRender.js.map +0 -1
  112. package/dist/cjs/src/renderUtils.js.map +0 -1
  113. package/dist/esm/__tests__/renderUtils.test.js.map +0 -1
  114. package/dist/esm/src/image/index.js +0 -6
  115. package/dist/esm/src/image/index.js.map +0 -1
  116. package/dist/esm/src/image/pdfRender.js +0 -18
  117. package/dist/esm/src/image/pdfRender.js.map +0 -1
  118. package/dist/esm/src/image/propPanel.js +0 -16
  119. package/dist/esm/src/image/propPanel.js.map +0 -1
  120. package/dist/esm/src/image/types.js +0 -2
  121. package/dist/esm/src/image/types.js.map +0 -1
  122. package/dist/esm/src/image/uiRender.js +0 -107
  123. package/dist/esm/src/image/uiRender.js.map +0 -1
  124. package/dist/esm/src/line/constants.js +0 -2
  125. package/dist/esm/src/line/constants.js.map +0 -1
  126. package/dist/esm/src/line/index.js +0 -6
  127. package/dist/esm/src/line/index.js.map +0 -1
  128. package/dist/esm/src/line/pdfRender.js +0 -16
  129. package/dist/esm/src/line/pdfRender.js.map +0 -1
  130. package/dist/esm/src/line/propPanel.js +0 -32
  131. package/dist/esm/src/line/propPanel.js.map +0 -1
  132. package/dist/esm/src/line/types.js +0 -2
  133. package/dist/esm/src/line/types.js.map +0 -1
  134. package/dist/esm/src/line/uiRender.js +0 -10
  135. package/dist/esm/src/line/uiRender.js.map +0 -1
  136. package/dist/esm/src/renderUtils.js.map +0 -1
  137. package/dist/types/src/image/index.d.ts +0 -4
  138. package/dist/types/src/image/pdfRender.d.ts +0 -3
  139. package/dist/types/src/image/propPanel.d.ts +0 -3
  140. package/dist/types/src/image/types.d.ts +0 -3
  141. package/dist/types/src/image/uiRender.d.ts +0 -3
  142. package/dist/types/src/line/constants.d.ts +0 -1
  143. package/dist/types/src/line/index.d.ts +0 -4
  144. package/dist/types/src/line/pdfRender.d.ts +0 -3
  145. package/dist/types/src/line/propPanel.d.ts +0 -3
  146. package/dist/types/src/line/types.d.ts +0 -4
  147. package/dist/types/src/line/uiRender.d.ts +0 -3
  148. package/src/image/index.ts +0 -8
  149. package/src/image/pdfRender.ts +0 -29
  150. package/src/image/propPanel.ts +0 -19
  151. package/src/image/types.ts +0 -3
  152. package/src/image/uiRender.ts +0 -121
  153. package/src/line/constants.ts +0 -1
  154. package/src/line/index.ts +0 -8
  155. package/src/line/pdfRender.ts +0 -24
  156. package/src/line/propPanel.ts +0 -34
  157. package/src/line/types.ts +0 -5
  158. package/src/line/uiRender.ts +0 -12
  159. /package/dist/types/__tests__/{renderUtils.test.d.ts → utils.test.d.ts} +0 -0
@@ -0,0 +1,144 @@
1
+ import { Schema } from '@pdfme/common';
2
+ interface Shape extends Schema {
3
+ type: 'ellipse' | 'rectangle';
4
+ borderWidth: number;
5
+ borderColor: string;
6
+ color: string;
7
+ }
8
+ export declare const rectangle: {
9
+ propPanel: {
10
+ defaultSchema: {
11
+ type: "ellipse" | "rectangle";
12
+ borderWidth: number;
13
+ borderColor: string;
14
+ color: string;
15
+ position: {
16
+ x: number;
17
+ y: number;
18
+ };
19
+ width: number;
20
+ height: number;
21
+ readOnly?: boolean | undefined;
22
+ readOnlyValue?: string | undefined;
23
+ rotate?: number | undefined;
24
+ opacity?: number | undefined;
25
+ };
26
+ schema: ((propPanelProps: Omit<{
27
+ rootElement: HTMLDivElement;
28
+ activeSchema: {
29
+ width: number;
30
+ height: number;
31
+ type: string;
32
+ position: {
33
+ x: number;
34
+ y: number;
35
+ };
36
+ data: string;
37
+ id: string;
38
+ key: string;
39
+ opacity?: number | undefined;
40
+ rotate?: number | undefined;
41
+ readOnly?: boolean | undefined;
42
+ readOnlyValue?: string | undefined;
43
+ };
44
+ activeElements: HTMLElement[];
45
+ changeSchemas: import("@pdfme/common").ChangeSchemas;
46
+ schemas: {
47
+ width: number;
48
+ height: number;
49
+ type: string;
50
+ position: {
51
+ x: number;
52
+ y: number;
53
+ };
54
+ data: string;
55
+ id: string;
56
+ key: string;
57
+ opacity?: number | undefined;
58
+ rotate?: number | undefined;
59
+ readOnly?: boolean | undefined;
60
+ readOnlyValue?: string | undefined;
61
+ }[];
62
+ pageSize: {
63
+ width: number;
64
+ height: number;
65
+ };
66
+ options: import("@pdfme/common").UIOptions;
67
+ theme: import("antd").GlobalToken;
68
+ i18n: (key: string) => string;
69
+ }, "rootElement">) => Record<string, Partial<import("form-render").SchemaBase>>) | Record<string, Partial<import("form-render").SchemaBase>>;
70
+ widgets?: Record<string, (props: import("@pdfme/common").PropPanelWidgetProps) => void> | undefined;
71
+ defaultValue: string;
72
+ };
73
+ pdf: (arg: import("@pdfme/common").PDFRenderProps<Shape>) => void | Promise<void>;
74
+ ui: (arg: import("@pdfme/common").UIRenderProps<Shape>) => void | Promise<void>;
75
+ };
76
+ export declare const ellipse: {
77
+ propPanel: {
78
+ defaultSchema: {
79
+ type: "ellipse" | "rectangle";
80
+ borderWidth: number;
81
+ borderColor: string;
82
+ color: string;
83
+ position: {
84
+ x: number;
85
+ y: number;
86
+ };
87
+ width: number;
88
+ height: number;
89
+ readOnly?: boolean | undefined;
90
+ readOnlyValue?: string | undefined;
91
+ rotate?: number | undefined;
92
+ opacity?: number | undefined;
93
+ };
94
+ schema: ((propPanelProps: Omit<{
95
+ rootElement: HTMLDivElement;
96
+ activeSchema: {
97
+ width: number;
98
+ height: number;
99
+ type: string;
100
+ position: {
101
+ x: number;
102
+ y: number;
103
+ };
104
+ data: string;
105
+ id: string;
106
+ key: string;
107
+ opacity?: number | undefined;
108
+ rotate?: number | undefined;
109
+ readOnly?: boolean | undefined;
110
+ readOnlyValue?: string | undefined;
111
+ };
112
+ activeElements: HTMLElement[];
113
+ changeSchemas: import("@pdfme/common").ChangeSchemas;
114
+ schemas: {
115
+ width: number;
116
+ height: number;
117
+ type: string;
118
+ position: {
119
+ x: number;
120
+ y: number;
121
+ };
122
+ data: string;
123
+ id: string;
124
+ key: string;
125
+ opacity?: number | undefined;
126
+ rotate?: number | undefined;
127
+ readOnly?: boolean | undefined;
128
+ readOnlyValue?: string | undefined;
129
+ }[];
130
+ pageSize: {
131
+ width: number;
132
+ height: number;
133
+ };
134
+ options: import("@pdfme/common").UIOptions;
135
+ theme: import("antd").GlobalToken;
136
+ i18n: (key: string) => string;
137
+ }, "rootElement">) => Record<string, Partial<import("form-render").SchemaBase>>) | Record<string, Partial<import("form-render").SchemaBase>>;
138
+ widgets?: Record<string, (props: import("@pdfme/common").PropPanelWidgetProps) => void> | undefined;
139
+ defaultValue: string;
140
+ };
141
+ pdf: (arg: import("@pdfme/common").PDFRenderProps<Shape>) => void | Promise<void>;
142
+ ui: (arg: import("@pdfme/common").UIRenderProps<Shape>) => void | Promise<void>;
143
+ };
144
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { Plugin } from '@pdfme/common';
2
2
  import type { TextSchema } from './types';
3
- declare const schema: Plugin<TextSchema>;
4
- export default schema;
3
+ declare const textSchema: Plugin<TextSchema>;
4
+ export default textSchema;
5
+ export declare const readOnlyText: Plugin<TextSchema>;
@@ -1,3 +1,3 @@
1
- import { PDFRenderProps } from '@pdfme/common';
2
1
  import type { TextSchema } from './types';
2
+ import { PDFRenderProps } from '@pdfme/common';
3
3
  export declare const pdfRender: (arg: PDFRenderProps<TextSchema>) => Promise<void>;
@@ -24,5 +24,7 @@ export declare const rotatePoint: (point: {
24
24
  y: number;
25
25
  };
26
26
  export declare const addAlphaToHex: (hex: string, alphaPercentage: number) => string;
27
- export declare const isEditable: (mode: Mode) => boolean;
27
+ export declare const isEditable: (mode: Mode, schema: Schema) => boolean;
28
28
  export declare const hex2RgbColor: (hexString: string | undefined) => import("@pdfme/pdf-lib").RGB | undefined;
29
+ export declare const readFile: (input: File | FileList | null) => Promise<string | ArrayBuffer>;
30
+ export declare const createErrorElm: () => HTMLDivElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pdfme/schemas",
3
- "version": "3.1.5-dev.9",
3
+ "version": "3.2.0",
4
4
  "sideEffects": false,
5
5
  "author": "hand-dot",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  }
33
33
  },
34
34
  "scripts": {
35
- "develop": "tsc -p tsconfig.esm.json -w",
35
+ "dev": "tsc -p tsconfig.esm.json -w",
36
36
  "build": "npm-run-all --parallel build:cjs build:esm",
37
37
  "build:cjs": "tsc -p tsconfig.cjs.json",
38
38
  "build:esm": "tsc -p tsconfig.esm.json",
@@ -43,8 +43,9 @@
43
43
  "prettier": "prettier --write 'src/**/*.ts'"
44
44
  },
45
45
  "dependencies": {
46
- "@pdfme/pdf-lib": "^1.17.3",
46
+ "@pdfme/pdf-lib": "^1.18.3",
47
47
  "bwip-js": "^4.1.1",
48
+ "fast-xml-parser": "^4.3.2",
48
49
  "fontkit": "^2.0.2"
49
50
  },
50
51
  "devDependencies": {
@@ -5,7 +5,7 @@ import type { BarcodeSchema, BarcodeTypes } from './types';
5
5
  import { BARCODE_TYPES } from './constants.js';
6
6
  import { Plugin } from '@pdfme/common';
7
7
 
8
- const schemas = BARCODE_TYPES.reduce(
8
+ const barcodes = BARCODE_TYPES.reduce(
9
9
  (acc, type) =>
10
10
  Object.assign(acc, {
11
11
  [type]: { pdf: pdfRender, ui: uiRender, propPanel: getPropPanelByBarcodeType(type) },
@@ -13,4 +13,4 @@ const schemas = BARCODE_TYPES.reduce(
13
13
  {} as Record<BarcodeTypes, Plugin<BarcodeSchema>>
14
14
  );
15
15
 
16
- export default schemas;
16
+ export default barcodes;
@@ -1,5 +1,5 @@
1
1
  import { PDFRenderProps } from '@pdfme/common';
2
- import { convertForPdfLayoutProps } from '../renderUtils.js';
2
+ import { convertForPdfLayoutProps } from '../utils.js';
3
3
  import type { BarcodeSchema } from './types';
4
4
  import { createBarCode, validateBarcodeInput } from './helper.js';
5
5
 
@@ -154,7 +154,7 @@ export const getPropPanelByBarcodeType = (barcodeType: string): PropPanel<Barcod
154
154
 
155
155
  const defaults = barcodeDefaults.find(({ defaultSchema }) => defaultSchema.type === barcodeType);
156
156
 
157
- if (!defaults) throw new Error(`[@pdfme/schemas] No default for barcode type ${barcodeType}`);
157
+ if (!defaults) throw new Error(`[@pdfme/schemas/barcodes] No default for barcode type ${barcodeType}`);
158
158
 
159
159
  return {
160
160
  schema: ({ i18n }) => ({
@@ -2,37 +2,10 @@ import type * as CSS from 'csstype';
2
2
  import { UIRenderProps } from '@pdfme/common';
3
3
  import type { BarcodeSchema } from './types';
4
4
  import { validateBarcodeInput, createBarCode } from './helper.js';
5
- import { addAlphaToHex, isEditable } from '../renderUtils.js';
5
+ import { addAlphaToHex, isEditable, createErrorElm } from '../utils.js';
6
6
 
7
7
  const fullSize = { width: '100%', height: '100%' };
8
8
 
9
- const createErrorBarcodeElm = () => {
10
- const container = document.createElement('div');
11
- const containerStyle: CSS.Properties = {
12
- display: 'flex',
13
- alignItems: 'center',
14
- justifyContent: 'center',
15
- ...fullSize,
16
- };
17
- Object.assign(container.style, containerStyle);
18
-
19
- const span = document.createElement('span');
20
- const spanStyle: CSS.Properties = {
21
- color: 'white',
22
- background: 'red',
23
- padding: '0.25rem',
24
- fontSize: '12pt',
25
- fontWeight: 'bold',
26
- borderRadius: '2px',
27
- };
28
- Object.assign(span.style, spanStyle);
29
-
30
- span.textContent = 'ERROR';
31
- container.appendChild(span);
32
-
33
- return container;
34
- };
35
-
36
9
  const blobToDataURL = (blob: Blob): Promise<string> =>
37
10
  new Promise((resolve, reject) => {
38
11
  const reader = new FileReader();
@@ -74,16 +47,17 @@ export const uiRender = async (arg: UIRenderProps<BarcodeSchema>) => {
74
47
  };
75
48
  Object.assign(container.style, containerStyle);
76
49
  rootElement.appendChild(container);
77
- const editable = isEditable(mode);
50
+ const editable = isEditable(mode, schema);
78
51
  if (editable) {
79
52
  const input = document.createElement('input');
80
53
  const inputStyle: CSS.Properties = {
81
- ...fullSize,
54
+ width: '100%',
82
55
  position: 'absolute',
83
56
  textAlign: 'center',
84
- fontSize: '1rem',
85
- color: '#000',
86
- backgroundColor: editable || value ? addAlphaToHex(theme.colorPrimaryBg, 30) : 'none',
57
+ fontSize: '12pt',
58
+ fontWeight: 'bold',
59
+ color: theme.colorWhite,
60
+ backgroundColor: editable || value ? addAlphaToHex('#000000', 80) : 'none',
87
61
  border: 'none',
88
62
  display: 'flex',
89
63
  alignItems: 'center',
@@ -108,12 +82,11 @@ export const uiRender = async (arg: UIRenderProps<BarcodeSchema>) => {
108
82
  if (!value) return;
109
83
  try {
110
84
  if (!validateBarcodeInput(schema.type, value))
111
- throw new Error('[@pdfme/schemas] Invalid barcode input');
85
+ throw new Error('[@pdfme/schemas/barcodes] Invalid barcode input');
112
86
  const imgElm = await createBarcodeImageElm(schema, value);
113
87
  container.appendChild(imgElm);
114
88
  } catch (err) {
115
89
  console.error(`[@pdfme/ui] ${err}`);
116
- const errorBarcodeElm = createErrorBarcodeElm();
117
- container.appendChild(errorBarcodeElm);
90
+ container.appendChild(createErrorElm());
118
91
  }
119
92
  };
@@ -0,0 +1,147 @@
1
+ // ref: https://github.com/image-size/image-size ----------------------------
2
+ // The following code is adapted from the image-size code. Unnecessary formats and dependencies on Node have been removed.
3
+ type IImage = {
4
+ validate: (input: Uint8Array) => boolean;
5
+ calculate: (input: Uint8Array) => { width: number; height: number } | undefined;
6
+ };
7
+
8
+ const decoder = new TextDecoder();
9
+ const toUTF8String = (input: Uint8Array, start = 0, end = input.length) =>
10
+ decoder.decode(input.slice(start, end));
11
+
12
+ const toHexString = (input: Uint8Array, start = 0, end = input.length) =>
13
+ input.slice(start, end).reduce((memo, i) => memo + ('0' + i.toString(16)).slice(-2), '');
14
+
15
+ const readUInt16BE = (input: Uint8Array, offset = 0) => input[offset] * 2 ** 8 + input[offset + 1];
16
+
17
+ const readUInt32BE = (input: Uint8Array, offset = 0) =>
18
+ input[offset] * 2 ** 24 +
19
+ input[offset + 1] * 2 ** 16 +
20
+ input[offset + 2] * 2 ** 8 +
21
+ input[offset + 3];
22
+
23
+ const extractSize = (input: Uint8Array, index: number) => {
24
+ return {
25
+ height: readUInt16BE(input, index),
26
+ width: readUInt16BE(input, index + 2),
27
+ };
28
+ };
29
+
30
+ const validateInput = (input: Uint8Array, index: number): void => {
31
+ // index should be within buffer limits
32
+ if (index > input.length) {
33
+ throw new TypeError('Corrupt JPG, exceeded buffer limits');
34
+ }
35
+ // Every JPEG block must begin with a 0xFF
36
+ if (input[index] !== 0xff) {
37
+ throw new TypeError('Invalid JPG, marker table corrupted');
38
+ }
39
+ };
40
+
41
+ const JPG: IImage = {
42
+ validate: (input) => toHexString(input, 0, 2) === 'ffd8',
43
+
44
+ calculate(input) {
45
+ // Skip 4 chars, they are for signature
46
+ input = input.slice(4);
47
+
48
+ let next: number;
49
+ while (input.length) {
50
+ // read length of the next block
51
+ const i = readUInt16BE(input, 0);
52
+
53
+ // ensure correct format
54
+ validateInput(input, i);
55
+
56
+ // 0xFFC0 is baseline standard(SOF)
57
+ // 0xFFC1 is baseline optimized(SOF)
58
+ // 0xFFC2 is progressive(SOF2)
59
+ next = input[i + 1];
60
+ if (next === 0xc0 || next === 0xc1 || next === 0xc2) {
61
+ const size = extractSize(input, i + 5);
62
+
63
+ return size;
64
+ }
65
+
66
+ // move to the next block
67
+ input = input.slice(i + 2);
68
+ }
69
+
70
+ throw new TypeError('Invalid JPG, no size found');
71
+ },
72
+ };
73
+
74
+ const pngSignature = 'PNG\r\n\x1a\n';
75
+ const pngImageHeaderChunkName = 'IHDR';
76
+
77
+ // Used to detect "fried" png's: http://www.jongware.com/pngdefry.html
78
+ const pngFriedChunkName = 'CgBI';
79
+
80
+ const PNG: IImage = {
81
+ validate(input) {
82
+ if (pngSignature === toUTF8String(input, 1, 8)) {
83
+ let chunkName = toUTF8String(input, 12, 16);
84
+ if (chunkName === pngFriedChunkName) {
85
+ chunkName = toUTF8String(input, 28, 32);
86
+ }
87
+ if (chunkName !== pngImageHeaderChunkName) {
88
+ throw new TypeError('Invalid PNG');
89
+ }
90
+ return true;
91
+ }
92
+ return false;
93
+ },
94
+
95
+ calculate(input) {
96
+ if (toUTF8String(input, 12, 16) === pngFriedChunkName) {
97
+ return {
98
+ height: readUInt32BE(input, 36),
99
+ width: readUInt32BE(input, 32),
100
+ };
101
+ }
102
+ return {
103
+ height: readUInt32BE(input, 20),
104
+ width: readUInt32BE(input, 16),
105
+ };
106
+ },
107
+ };
108
+
109
+ const typeHandlers = {
110
+ jpg: JPG,
111
+ png: PNG,
112
+ };
113
+
114
+ type imageType = keyof typeof typeHandlers;
115
+
116
+ function detector(input: Uint8Array): imageType | undefined {
117
+ const firstBytes: { [byte: number]: imageType } = {
118
+ 0x89: 'png',
119
+ 0xff: 'jpg',
120
+ };
121
+ const byte = input[0];
122
+ if (byte in firstBytes) {
123
+ const type = firstBytes[byte];
124
+ if (type && typeHandlers[type].validate(input)) {
125
+ return type;
126
+ }
127
+ }
128
+
129
+ const keys = Object.keys(typeHandlers) as imageType[];
130
+ return keys.find((key: imageType) => typeHandlers[key].validate(input));
131
+ }
132
+
133
+ export const imageSize = (imgBuffer: Buffer): { height: number; width: number } => {
134
+ const type = detector(imgBuffer);
135
+
136
+ if (typeof type !== 'undefined' && type in typeHandlers) {
137
+ const size = typeHandlers[type].calculate(imgBuffer);
138
+ if (size !== undefined) {
139
+ return size;
140
+ }
141
+ }
142
+
143
+ throw new TypeError(
144
+ '[@pdfme/schemas/images] Unsupported file type: ' + (type === undefined ? 'undefined' : type)
145
+ );
146
+ };
147
+ // ----------------------------
@@ -0,0 +1,207 @@
1
+ import type { ChangeEvent } from 'react';
2
+ import type { PDFImage } from '@pdfme/pdf-lib';
3
+ import type { Plugin } from '@pdfme/common';
4
+ import type { PDFRenderProps, Schema } from '@pdfme/common';
5
+ import type * as CSS from 'csstype';
6
+ import { Buffer } from 'buffer';
7
+ import { UIRenderProps } from '@pdfme/common';
8
+ import { convertForPdfLayoutProps, addAlphaToHex, isEditable, readFile } from '../utils.js';
9
+ import { DEFAULT_OPACITY } from '../constants.js';
10
+ import { imageSize } from './helper.js';
11
+
12
+ const px2mm = (px: number): number => {
13
+ // http://www.endmemo.com/sconvert/millimeterpixel.php
14
+ const ratio = 0.26458333333333;
15
+ return parseFloat(String(px)) * ratio;
16
+ };
17
+
18
+ const getCacheKey = (schema: Schema, input: string) => `${schema.type}${input}`;
19
+ const fullSize = { width: '100%', height: '100%' };
20
+ const defaultValue =
21
+ 'data:image/png;base64,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';
22
+
23
+ interface ImageSchema extends Schema {}
24
+
25
+ const imageSchema: Plugin<ImageSchema> = {
26
+ pdf: async (arg: PDFRenderProps<ImageSchema>) => {
27
+ const { value, schema, pdfDoc, page, _cache } = arg;
28
+ if (!value || !value.startsWith('data:image/')) return;
29
+
30
+ const inputImageCacheKey = getCacheKey(schema, value);
31
+ let image = _cache.get(inputImageCacheKey) as PDFImage;
32
+ if (!image) {
33
+ const isPng = value.startsWith('data:image/png;');
34
+ image = await (isPng ? pdfDoc.embedPng(value) : pdfDoc.embedJpg(value));
35
+ _cache.set(inputImageCacheKey, image);
36
+ }
37
+
38
+ const _schema = { ...schema, position: { ...schema.position } };
39
+ const dataUriPrefix = ';base64,';
40
+ const idx = value.indexOf(dataUriPrefix);
41
+ const imgBase64 = value.substring(idx + dataUriPrefix.length, value.length);
42
+ const dimension = imageSize(Buffer.from(imgBase64, 'base64'));
43
+
44
+ const imageWidth = px2mm(dimension.width);
45
+ const imageHeight = px2mm(dimension.height);
46
+ const boxWidth = _schema.width;
47
+ const boxHeight = _schema.height;
48
+
49
+ const imageRatio = imageWidth / imageHeight;
50
+ const boxRatio = boxWidth / boxHeight;
51
+
52
+ if (imageRatio > boxRatio) {
53
+ _schema.width = boxWidth;
54
+ _schema.height = boxWidth / imageRatio;
55
+ _schema.position.y += (boxHeight - _schema.height) / 2;
56
+ } else {
57
+ _schema.width = boxHeight * imageRatio;
58
+ _schema.height = boxHeight;
59
+ _schema.position.x += (boxWidth - _schema.width) / 2;
60
+ }
61
+
62
+ const pageHeight = page.getHeight();
63
+ const lProps = convertForPdfLayoutProps({ schema: _schema, pageHeight });
64
+ const { width, height, rotate, position, opacity } = lProps;
65
+ const { x, y } = position;
66
+ page.drawImage(image, { x, y, rotate, width, height, opacity });
67
+ },
68
+ ui: (arg: UIRenderProps<ImageSchema>) => {
69
+ const {
70
+ value,
71
+ rootElement,
72
+ mode,
73
+ onChange,
74
+ stopEditing,
75
+ tabIndex,
76
+ placeholder,
77
+ theme,
78
+ schema,
79
+ } = arg;
80
+ const editable = isEditable(mode, schema);
81
+ const isDefault = value === defaultValue;
82
+
83
+ const container = document.createElement('div');
84
+ const backgroundStyle = placeholder ? `url(${placeholder})` : 'none';
85
+ const containerStyle: CSS.Properties = {
86
+ ...fullSize,
87
+ backgroundImage: value ? 'none' : backgroundStyle,
88
+ backgroundSize: `contain`,
89
+ backgroundRepeat: 'no-repeat',
90
+ backgroundPosition: 'center',
91
+ };
92
+ Object.assign(container.style, containerStyle);
93
+ container.addEventListener('click', (e) => {
94
+ if (editable) {
95
+ e.stopPropagation();
96
+ }
97
+ });
98
+ rootElement.appendChild(container);
99
+
100
+ // image tag
101
+ if (value) {
102
+ const img = document.createElement('img');
103
+ const imgStyle: CSS.Properties = {
104
+ height: '100%',
105
+ width: '100%',
106
+ borderRadius: 0,
107
+ objectFit: 'contain',
108
+ };
109
+ Object.assign(img.style, imgStyle);
110
+ img.src = value;
111
+ container.appendChild(img);
112
+ }
113
+
114
+ // remove button
115
+ if (value && !isDefault && editable) {
116
+ const button = document.createElement('button');
117
+ button.textContent = 'x';
118
+ const buttonStyle: CSS.Properties = {
119
+ position: 'absolute',
120
+ top: 0,
121
+ left: 0,
122
+ zIndex: 1,
123
+ display: 'flex',
124
+ justifyContent: 'center',
125
+ alignItems: 'center',
126
+ color: '#333',
127
+ background: '#f2f2f2',
128
+ borderRadius: '2px',
129
+ border: '1px solid #767676',
130
+ cursor: 'pointer',
131
+ height: '24px',
132
+ width: '24px',
133
+ };
134
+ Object.assign(button.style, buttonStyle);
135
+ button.addEventListener('click', () => {
136
+ onChange && onChange('');
137
+ });
138
+ container.appendChild(button);
139
+ }
140
+
141
+ // file input
142
+ if ((!value || isDefault) && editable) {
143
+ const label = document.createElement('label');
144
+ const labelStyle: CSS.Properties = {
145
+ ...fullSize,
146
+ display: editable ? 'flex' : 'none',
147
+ position: 'absolute',
148
+ top: 0,
149
+ backgroundColor: editable || value ? addAlphaToHex(theme.colorPrimaryBg, 30) : 'none',
150
+ cursor: 'pointer',
151
+ };
152
+ Object.assign(label.style, labelStyle);
153
+ container.appendChild(label);
154
+ const input = document.createElement('input');
155
+ const inputStyle: CSS.Properties = {
156
+ ...fullSize,
157
+ position: 'absolute',
158
+ top: '50%',
159
+ left: '50%',
160
+ width: '180px',
161
+ height: '30px',
162
+ marginLeft: '-90px',
163
+ marginTop: '-15px',
164
+ };
165
+ Object.assign(input.style, inputStyle);
166
+ input.tabIndex = tabIndex || 0;
167
+ input.type = 'file';
168
+ input.accept = 'image/jpeg, image/png';
169
+ input.addEventListener('change', (event: Event) => {
170
+ const changeEvent = event as unknown as ChangeEvent<HTMLInputElement>;
171
+ readFile(changeEvent.target.files).then((result) => onChange && onChange(result as string));
172
+ });
173
+ input.addEventListener('blur', () => stopEditing && stopEditing());
174
+ label.appendChild(input);
175
+ }
176
+ },
177
+ propPanel: {
178
+ schema: {},
179
+ defaultValue,
180
+ defaultSchema: {
181
+ type: 'image',
182
+ position: { x: 0, y: 0 },
183
+ width: 40,
184
+ height: 40,
185
+ // If the value of "rotate" is set to undefined or not set at all, rotation will be disabled in the UI.
186
+ // Check this document: https://pdfme.com//docs/custom-schemas#learning-how-to-create-from-pdfmeschemas-code
187
+ rotate: 0,
188
+ opacity: DEFAULT_OPACITY,
189
+ },
190
+ },
191
+ };
192
+
193
+ export default imageSchema;
194
+
195
+ export const readOnlyImage: Plugin<ImageSchema> = {
196
+ pdf: imageSchema.pdf,
197
+ ui: imageSchema.ui,
198
+ propPanel: {
199
+ ...imageSchema.propPanel,
200
+ defaultSchema: {
201
+ ...imageSchema.propPanel.defaultSchema,
202
+ type: 'readOnlyImage',
203
+ readOnly: true,
204
+ readOnlyValue: defaultValue,
205
+ },
206
+ },
207
+ };