UNPKG

@pdfme/common

Version:

TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!

618 lines 21.2 kB
import { readFileSync } from 'fs'; import * as path from 'path'; import { mm2pt, pt2mm, pt2px, checkGenerateProps, checkFont, checkPlugins, isHexValid, migrateTemplate, } from '../src/helper.js'; import { PT_TO_PX_RATIO, BLANK_PDF, getB64BasePdf, } from '../src/index.js'; const sansData = readFileSync(path.join(__dirname, `/assets/fonts/NotoSans-Regular.ttf`)); const serifData = readFileSync(path.join(__dirname, `/assets/fonts/NotoSerif-Regular.ttf`)); const getSampleFont = () => ({ NotoSans: { fallback: true, data: sansData }, NotoSerif: { data: serifData }, }); const getTemplate = () => ({ basePdf: BLANK_PDF, schemas: [ [ { name: 'a', content: 'a', type: 'text', fontName: 'NotoSans', position: { x: 0, y: 0 }, width: 100, height: 100, }, { name: 'b', content: 'b', type: 'text', position: { x: 0, y: 0 }, width: 100, height: 100, }, ], ], }); describe('mm2pt test', () => { it('converts millimeters to points', () => { expect(mm2pt(1)).toEqual(2.8346); expect(mm2pt(10)).toEqual(28.346); expect(mm2pt(4395.12)).toEqual(12458.407152); }); }); describe('pt2mm test', () => { it('converts points to millimeters', () => { expect(pt2mm(1)).toEqual(0.3528); expect(pt2mm(2.8346)).toEqual(1.00004688); // close enough! expect(pt2mm(10)).toEqual(3.528); expect(pt2mm(5322.98)).toEqual(1877.947344); }); }); describe('pt2px test', () => { it('converts points to pixels', () => { expect(pt2px(1)).toEqual(PT_TO_PX_RATIO); expect(pt2px(1)).toEqual(1.333); expect(pt2px(2.8346)).toEqual(3.7785218); expect(pt2px(10)).toEqual(13.33); expect(pt2px(5322.98)).toEqual(7095.532339999999); }); }); describe('isHexValid test', () => { test('valid hex', () => { expect(isHexValid('#fff')).toEqual(true); expect(isHexValid('#ffffff')).toEqual(true); expect(isHexValid('#ffffff00')).toEqual(true); expect(isHexValid('#ffffff00')).toEqual(true); }); test('invalid hex', () => { expect(isHexValid('#ff')).toEqual(false); expect(isHexValid('#fffff')).toEqual(false); expect(isHexValid('#ffffff000')).toEqual(false); expect(isHexValid('#ffffff0000')).toEqual(false); expect(isHexValid('#ffffff00000')).toEqual(false); expect(isHexValid('#ffffff000000')).toEqual(false); expect(isHexValid('#pdfme123')).toEqual(false); }); }); // NOTE: We only test the generator check function because the others // have a schema that checks for HTMLElement, which isn't available in the test environment describe('checkGenerateProps', () => { test('accepts valid generator schema', () => { const validProps = { inputs: [{ a: 'a', b: 'b' }], template: { basePdf: 'data:application/pdf;base64,abc123', schemas: [ [ { name: 'field1', type: 'text', position: { x: 0, y: 0 }, width: 100, height: 100, content: 'Test' } ] ] }, options: { font: getSampleFont() }, plugins: { text: { pdf: async () => { }, ui: async () => { }, propPanel: { schema: {}, defaultSchema: { type: 'text', name: 'myText', content: '', position: { x: 0, y: 0 }, width: 100, height: 100 } } } } }; expect(() => checkGenerateProps(validProps)).not.toThrow(); }); test('throws for invalid template structure', () => { const invalidProps = { template: { schemas: 'not-an-array' // Invalid type } }; expect(() => checkGenerateProps(invalidProps)).toThrow("[@pdfme/common] Invalid argument:\n" + "--------------------------\n" + "ERROR POSITION: template.schemas\n" + "ERROR MESSAGE: Expected array, received string\n" + "--------------------------\n" + "ERROR POSITION: template.basePdf\n" + "ERROR MESSAGE: Invalid input\n" + "--------------------------\n" + "ERROR POSITION: inputs\n" + "ERROR MESSAGE: Required\n" + "--------------------------"); }); test('throws for missing required template field and empty inputs', () => { const missingSchemaProps = { inputs: [], template: { basePdf: 'data:application/pdf;base64,abc123', // schemas is missing } }; expect(() => checkGenerateProps(missingSchemaProps)).toThrow("[@pdfme/common] Invalid argument:\n" + "--------------------------\n" + "ERROR POSITION: template.schemas\n" + "ERROR MESSAGE: Required\n" + "--------------------------\n" + "ERROR POSITION: inputs\n" + "ERROR MESSAGE: Array must contain at least 1 element(s)\n" + "--------------------------"); }); test('throws for invalid plugin definitions', () => { const invalidPluginProps = { inputs: [{ a: 'a' }], template: { basePdf: 'data:application/pdf;base64,abc123', schemas: [[]] }, plugins: { invalid: { // missing pdf // missing ui propPanel: { schema: {}, defaultSchema: { // missing type name: 'myText', content: '', position: { x: 0, y: 0 }, width: 100, height: 100 } } }, missingPanel: { pdf: async () => { }, ui: async () => { }, // missing propPanel }, missingDefaultSchema: { pdf: async () => { }, ui: async () => { }, propPanel: {} } } }; expect(() => checkGenerateProps(invalidPluginProps)).toThrow("[@pdfme/common] Invalid argument:\n" + "--------------------------\n" + "ERROR POSITION: plugins.invalid.ui\n" + "ERROR MESSAGE: Required\n" + "--------------------------\n" + "ERROR POSITION: plugins.invalid.pdf\n" + "ERROR MESSAGE: Required\n" + "--------------------------\n" + "ERROR POSITION: plugins.invalid.propPanel.defaultSchema.type\n" + "ERROR MESSAGE: Required\n" + "--------------------------\n" + "ERROR POSITION: plugins.missingPanel.propPanel\n" + "ERROR MESSAGE: Required\n" + "--------------------------\n" + "ERROR POSITION: plugins.missingDefaultSchema.propPanel.defaultSchema\n" + "ERROR MESSAGE: Required\n" + "--------------------------"); }); test('calls checkFont when font option is provided', () => { const propsWithFont = { inputs: [{ a: 'a' }], template: { basePdf: 'data:application/pdf;base64,abc123', schemas: [[]] }, options: { font: { Roboto: { data: new Uint8Array(), fallback: true } } } }; checkGenerateProps(propsWithFont); }); }); describe('checkFont test', () => { test('success test: no fontName in Schemas', () => { const _getTemplate = () => ({ basePdf: BLANK_PDF, schemas: [ [ { name: 'a', content: 'a', type: 'text', position: { x: 0, y: 0 }, width: 100, height: 100, }, { name: 'b', content: 'b', type: 'text', position: { x: 0, y: 0 }, width: 100, height: 100, }, ], ], }); try { checkFont({ template: _getTemplate(), font: getSampleFont() }); expect.anything(); } catch (e) { fail(); } }); test('success test: fontName in Schemas(fallback font)', () => { try { checkFont({ template: getTemplate(), font: getSampleFont() }); expect.anything(); } catch (e) { fail(); } }); test('success test: fontName in Schemas(not fallback font)', () => { const getFont = () => ({ NotoSans: { data: sansData }, NotoSerif: { fallback: true, data: serifData }, }); try { checkFont({ template: getTemplate(), font: getFont() }); expect.anything(); } catch (e) { fail(); } }); test('fail test: no fallback font', () => { const getFont = () => ({ NotoSans: { data: sansData }, NotoSerif: { data: serifData }, }); try { checkFont({ template: getTemplate(), font: getFont() }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] fallback flag is not found in font. true fallback flag must be only one. Check this document: https://pdfme.com/docs/custom-fonts#about-font-type`); } }); test('fail test: too many fallback font', () => { const getFont = () => ({ NotoSans: { data: sansData, fallback: true }, NotoSerif: { data: serifData, fallback: true }, }); try { checkFont({ template: getTemplate(), font: getFont() }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] 2 fallback flags found in font. true fallback flag must be only one. Check this document: https://pdfme.com/docs/custom-fonts#about-font-type`); } }); test('fail test: fontName in Schemas not found in font(single)', () => { const _getTemplate = () => ({ basePdf: BLANK_PDF, schemas: [ [ { name: 'a', type: 'text', content: 'a', fontName: 'NotoSans2', position: { x: 0, y: 0 }, width: 100, height: 100, }, { name: 'b', type: 'text', content: 'b', position: { x: 0, y: 0 }, width: 100, height: 100, }, ], ], }); try { checkFont({ template: _getTemplate(), font: getSampleFont() }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] NotoSans2 of template.schemas is not found in font. Check this document: https://pdfme.com/docs/custom-fonts`); } }); test('fail test: fontName in Schemas not found in font(single)', () => { const _getTemplate = () => ({ basePdf: BLANK_PDF, schemas: [ [ { name: 'a', type: 'text', content: 'a', fontName: 'NotoSans2', position: { x: 0, y: 0 }, width: 100, height: 100, }, { name: 'b', type: 'text', content: 'b', fontName: 'NotoSerif2', position: { x: 0, y: 0 }, width: 100, height: 100, }, ], ], }); try { checkFont({ template: _getTemplate(), font: getSampleFont() }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] NotoSans2,NotoSerif2 of template.schemas is not found in font. Check this document: https://pdfme.com/docs/custom-fonts`); } }); }); describe('checkPlugins test', () => { const plugins = { myText: { pdf: async () => { }, ui: async () => { }, propPanel: { schema: {}, defaultSchema: { type: 'myText', name: 'myText', content: '', position: { x: 0, y: 0 }, width: 100, height: 100, }, }, }, myImage: { pdf: async () => { }, ui: async () => { }, propPanel: { schema: {}, defaultSchema: { type: 'myImage', name: 'myImage', content: '', position: { x: 0, y: 0 }, width: 100, height: 100, }, }, }, }; test('success test: no type in Schemas(no plugins)', () => { try { const template = getTemplate(); template.schemas = []; checkPlugins({ template, plugins: {} }); expect.anything(); } catch (e) { fail(); } }); test('success test: no type in Schemas(with plugins)', () => { try { const template = getTemplate(); template.schemas = []; checkPlugins({ template, plugins }); expect.anything(); } catch (e) { fail(); } }); test('success test: type in Schemas(single)', () => { try { const template = getTemplate(); template.schemas[0][0].type = 'myText'; template.schemas[0][1].type = 'myText'; checkPlugins({ template, plugins }); expect.anything(); } catch (e) { fail(); } }); test('success test: type in Schemas(multiple)', () => { try { const template = getTemplate(); template.schemas[0][0].type = 'myText'; template.schemas[0][1].type = 'myImage'; checkPlugins({ template, plugins }); expect.anything(); } catch (e) { fail(); } }); test('fail test: type in Schemas not found in plugins(single)', () => { try { const template = getTemplate(); template.schemas[0][0].type = 'fail'; template.schemas[0][1].type = 'myImage'; checkPlugins({ template, plugins }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] fail of template.schemas is not found in plugins.`); } }); test('fail test: type in Schemas not found in plugins(multiple)', () => { try { const template = getTemplate(); template.schemas[0][0].type = 'fail'; template.schemas[0][1].type = 'fail2'; checkPlugins({ template, plugins }); fail(); } catch (e) { expect(e.message).toEqual(`[@pdfme/common] fail,fail2 of template.schemas is not found in plugins.`); } }); }); describe('migrateTemplate', () => { it('should convert LegacySchemaPageArray to SchemaPageArray', () => { const legacyTemplate = { schemas: [ { field1: { type: 'text', content: 'Field 1', width: 45, height: 10, position: { x: 0, y: 0, }, }, field2: { type: 'text', content: 'Field 2', width: 45, height: 10, position: { x: 0, y: 0, }, }, }, { field3: { type: 'text', content: 'Field 3', width: 45, height: 10, position: { x: 0, y: 0, }, }, }, ], }; migrateTemplate(legacyTemplate); const expectedSchemaPageArray = [ [ { name: 'field1', type: 'text', content: 'Field 1', width: 45, height: 10, position: { x: 0, y: 0, }, }, { name: 'field2', type: 'text', content: 'Field 2', width: 45, height: 10, position: { x: 0, y: 0, }, }, ], [ { name: 'field3', type: 'text', content: 'Field 3', width: 45, height: 10, position: { x: 0, y: 0, }, }, ], ]; expect(legacyTemplate.schemas).toEqual(expectedSchemaPageArray); }); it('should not modify already SchemaPageArray', () => { const pagedTemplate = { schemas: [ [ { name: 'field1', type: 'text', content: 'Field 1', width: 45, height: 10, position: { x: 0, y: 0, }, }, { name: 'field2', type: 'text', content: 'Field 2', width: 45, height: 10, position: { x: 0, y: 0, }, }, ], [ { name: 'field3', type: 'text', content: 'Field 3', width: 45, height: 10, position: { x: 0, y: 0, }, }, ], ], }; const before = JSON.parse(JSON.stringify(pagedTemplate)); migrateTemplate(pagedTemplate); expect(pagedTemplate.schemas).toEqual(before.schemas); }); }); describe('getB64BasePdf', () => { test('base64 string', async () => { const result = await getB64BasePdf(BLANK_PDF); expect(typeof result).toBe('string'); }); test('Uint8Array', async () => { const result = await getB64BasePdf(new Uint8Array([10, 20, 30, 40, 50])); expect(typeof result).toBe('string'); }); }); //# sourceMappingURL=helper.test.js.map