Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import utils from './utils';
- import _ from 'lodash';
- import store from '../store';
- import History from './History';
- import CreatePDF from './pdf/CreatePDF';
- import textbox from './data/textbox';
- import tablePlugin from './tablePlugin';
- window.tablePlugin = tablePlugin;
- import lineArrow from './create-editor/lineArrow';
- // ed.fillWatermarkOnPage(ed.pages[0]);
- window.CreatePDF = CreatePDF;
- import idb from './idb';
- import brushes from './brushes';
- import ScrollPlugin from './scrollPlugin';
- import ImportPDF from './importPDF';
- import pdf_previewer from './pdf_previewer';
- import provider from '../provider';
- export default class {
- constructor(documentElement = false, isConfirmMode = false) {
- this.updatedObjects = [];
- this.isConfirmMode = isConfirmMode;
- this.stackUpdateThumbail = [];
- this.pending_pages = [];
- this.isRenderCache = false;
- this.queueRender = [];
- this.disposed_pages_content = {};
- this.settings = {
- fieldBackgroundColor: 'rgb(255 241 214)', //'rgba(255, 183, 0, 1)',
- errorBackgroundField: 'rgb(255 241 241)', //'rgba(200, 0, 0, 1)'
- };
- this.importPDF = new ImportPDF(this);
- this.scrollPlugin = new ScrollPlugin();
- this.lockUpdateThumbail = false;
- this.has_grid = false;
- this.grid = 15;
- this.payloadPage = {};
- this.loadedTemplate = false;
- this.filled_template = {
- id: 0,
- hash: ''
- };
- this.history = new History(this);
- window.ed = this;
- this.defOptions = {
- padding: 8
- };
- this.defaultFields = [
- 'left', 'top', 'width', 'height', 'styles', 'text',
- 'subtype', 'payload', 'scaleX', 'padding', 'fill', 'id',
- 'scaleY', 'angle', 'stroke', 'strokeWidth', 'rx', 'ry',
- 'unicode_string', 'positions',
- 'from_pdf', 'linesCharSpacing', 'customLineHeights', 'dynamicConfig'
- ];
- this.globalStyles = ['textAlign', 'lineHeight', 'angle', 'scaleY', 'scaleX'];
- this.events = [];
- this.options = { padding: 25 };
- this.documentElement = documentElement;
- this.pages = [];
- this.initPasteboard();
- this.pagesInViewport = _.debounce(this.handlePagesInViewport, 100);
- this.updateThumbailDebounce = _.debounce(this.updateThumbail, 500);
- }
- changeDrawingOptions(key, value) {
- store.state.drawingMode[key] = value;
- this.pages.forEach(page => {
- page.canvas.freeDrawingBrush[key] = value;
- });
- }
- removeAllWatermarks() {
- this.pages.forEach(page => {
- this.history.lockHistory = true;
- try {
- const marks = page.canvas.getObjects().filter(o => o && o.payload && o.payload.byWatermark);
- marks.forEach(m => page.canvas.remove(m));
- } catch (err) {
- console.error(err);
- }
- this.history.lockHistory = false;
- });
- }
- async appendWatermarkJSON(page, objects = []) {
- objects = objects.filter(o => !(o && o.payload && o.payload.byWatermark));
- const waters = store.state.user && store.state.user.plan == 'premium' ? [] : await this.createWatermark(page, true);
- return [...objects, ...waters.map(w => this.setupDefaultOptions(w))];
- }
- createWatermark(page, isJSON = false) {
- const opts = {
- fontFamily: 'Arial',
- fill: '#1155c7',
- fontSize: 10,
- evented: false,
- selectable: false
- };
- const url = 'deftpdf.com';
- const link = new fabric.Text(url, {
- ...opts, styles: [new Array(url.length).fill(false).map(q => (_.cloneDeep(opts)))],
- payload: {
- byWatermark: true, type: 'link',
- url: 'https://deftpdf.com'
- }
- });
- const created = new fabric.Text('created by', {
- ...opts, fill: '#000', payload: { type: 'text', byWatermark: true }
- });
- const padding = 10;
- let canvasWidth = page.canvas.getWidth() / page.canvas.getZoom();
- let canvasHeight = page.canvas.getHeight() / page.canvas.getZoom();
- const top = (canvasHeight - link.height / 2 - padding);
- link.set({ left: (canvasWidth - link.width / 2 - padding), top });
- created.set({ left: (link.left - created.width) - 10, top });
- if (isJSON) return [link.toJSON(this.defaultFields), created.toJSON(this.defaultFields)];
- return { created, link };
- }
- async appendWatermarks(renderOnPage = false) {
- return this.removeAllWatermarks();
- if (store.state.user && store.state.user.plan == 'premium') {
- return this.removeAllWatermarks();
- }
- for (const page of this.pages.filter(page => !page.waitingRender)) {
- if (renderOnPage && renderOnPage.id != page.id) continue;
- this.history.lockHistory = true;
- page.canvas.getObjects().forEach(obj => {
- if (obj && obj.payload && obj.payload.byWatermark) {
- page.canvas.remove(obj);
- }
- });
- const { created, link } = await this.createWatermark(page);
- page.canvas.add(created);
- page.canvas.add(link);
- this.history.lockHistory = false;
- }
- }
- changeEditorMode(mode) {
- switch (mode) {
- case 'drawing':
- case 'drawing-spray': {
- const drawingMode = store.state.drawingMode;
- this.pages.forEach(page => {
- page.canvas.upperCanvasEl.style['pointer-events'] = 'all';
- page.canvas.isDrawingMode = true;
- if (mode == 'drawing') {
- page.canvas.freeDrawingBrush = new fabric.PencilBrush(page.canvas);
- if (store.state.editorMode != mode) {
- store.state.drawingMode.width = 5;
- }
- if (store.state.drawingMode.color.length > 7) {
- store.state.drawingMode.color = store.state.drawingMode.color.substr(0, 7);
- }
- }
- if (mode == 'drawing-spray') {
- page.canvas.freeDrawingBrush = new fabric.PencilBrush(page.canvas);
- if (store.state.editorMode != mode) {
- store.state.drawingMode.width = 10;
- }
- if (store.state.drawingMode.color.length <= 7) {
- store.state.drawingMode.color += '88';
- }
- }
- // page.canvas.freeDrawingBrush = new fabric.SprayBrush(page.canvas);
- page.canvas.freeDrawingBrush.color = drawingMode.color;
- page.canvas.freeDrawingBrush.width = drawingMode.width;
- });
- store.state.editorMode = mode;
- break;
- }
- case 'erase': {
- store.state.editorMode = mode;
- this.pages.forEach(page => {
- page.canvas.isDrawingMode = false;
- page.canvas.upperCanvasEl.style['pointer-events'] = 'none';
- });
- break;
- }
- default:
- this.pages.forEach(page => {
- page.canvas.isDrawingMode = false;
- page.canvas.upperCanvasEl.style['pointer-events'] = 'all';
- });
- store.state.editorMode = '';
- }
- }
- async createImageAppend(padding = 10, width = 200, height = 120) {
- const img = await new Promise(resolve => fabric.Image.fromURL('/pdf-templates/img/image.svg', resolve));
- const text = new fabric.Text('Select image', {
- fontSize: 16, fill: '#8C49F7',
- left: 30, fontFamily: 'Arial'
- });
- const rect = new fabric.Rect({
- left: 0, top: 0, fill: '#f9f9f9',
- width, height
- });
- img.top = rect.height / 2 - img.height / 2;
- img.left = (rect.width - (img.width + text.width + padding)) / 2;
- text.set({
- top: rect.height / 2 - text.height / 2,
- left: img.left + img.width + padding
- });
- const group = new fabric.Group([rect, img, text], { left: 100, top: 100 });
- return group;
- }
- initPasteboard() {
- document.onpaste = async event => {
- if (this._type != 'create' || !this.isConfirmMode) return;
- const activeElement = document.activeElement;
- if (!['input', 'textarea'].includes((activeElement.tagName || '').toLowerCase())) {
- const cd = (event.clipboardData || event.originalEvent.clipboardData);
- const items = cd.items;
- const file = Array.from(items).find(item => item.kind == 'file');
- if (file) {
- const blob = file.getAsFile();
- var reader = new FileReader();
- reader.onload = event => this.addElement('image', { src: event.target.result });
- reader.readAsDataURL(blob);
- } else {
- const text = cd.getData('text') || Array.from(items).find(item => item.kind == 'string');
- const object = await this.addElement('text', { text, options: { fontSize: 12, width: 400 } });
- const pasteElement = $(cd.getData('text/html'))[0];
- if (pasteElement) {
- try {
- const parsed = utils.parserPaste.parseHTMLStyles(pasteElement);
- // const backgroundColor = pasteElement.style.backgroundColor;
- // if (backgroundColor) object.set({ backgroundColor: backgroundColor });
- parsed.styles.forEach((style, index) => {
- object.setSelectionStyles(style, index, index + 1);
- });
- object.canvas.renderAll();
- } catch (err) {
- console.error(err);
- }
- }
- }
- }
- }
- }
- async generatePreviewFirstPage() {
- const page = this.pages[0];
- const multiplier = (page.canvas.width / page.canvas.getZoom()) / page.canvas.width
- const base64 = page.canvas.toDataURL({ multiplier });
- return base64;
- }
- replaceObjectById(objectId, dataJSON = {}, scrollToObject = false) {
- const object = this.pages.reduce((result, page) => {
- const o = page.canvas.getObjects().find(ob => ob.id == objectId);
- if (o) return o;
- return result;
- }, false);
- if (!object) return console.error('Object not found');
- object.set(dataJSON);
- object.canvas.requestRenderAll();
- if (scrollToObject) this.scrollToObjectById(objectId);
- }
- scrollToObjectById(objectId) {
- const object = this.pages.reduce((result, page) => {
- const o = page.canvas.getObjects().find(ob => ob.id == objectId);
- if (o) return o;
- return result;
- }, false);
- if (!object) return console.error('Object not found');
- const calculatePosition = (obj, tooltip, parent) => {
- const zoom = obj.canvas.getZoom();
- const p = 20;// * zoom;
- const width = parent.clientWidth - p;
- const height = parent.clientHeight - p;
- const w = (obj.width * obj.scaleX) * zoom;
- const h = (obj.height * obj.scaleY) * zoom;
- let left = (obj.left * zoom - w / 2 - 10);
- let top = (obj.top * zoom + h / 2 + p);
- if (left + (tooltip.clientWidth) > width) {
- left = width - tooltip.clientWidth;
- }
- if (top + (tooltip.clientHeight) > height) {
- top = top - (tooltip.clientHeight) - h - (p * 2);
- }
- if (left < p) left = p;
- if (top < p) top = p;
- const styles = {
- position: 'absolute',
- left: `${left}px`,
- top: `${top}px`,
- opacity: 0,
- 'pointer-events': 'none',
- 'user-select': 'none'
- };
- tooltip.style = utils.objectStyleToString(styles);
- };
- object.canvas.setActiveObject(object).renderAll();
- const tooltip = document.createElement('div');
- tooltip.setAttribute('class', 'tooltip_document');
- tooltip.setAttribute('data-attach', `${object.id}`);
- const parent = object.canvas.upperCanvasEl.closest('.document--new');
- parent.appendChild(tooltip);
- calculatePosition(object, tooltip, parent);
- tooltip.scrollIntoView({ block: 'center' });
- }
- async exportCurrentState() {
- // const fr = new FileReader();
- // const thumb = await fetch(this.pages[0].thumb).then(r => r.blob());
- //return new Promise(resolve => {
- // fr.onload = async e => {
- const previewBase64 = await this.generatePreviewFirstPage();//e.target.result
- let pages = [];
- for (const page of this.pages) {
- page.canvas.discardActiveObject().requestRenderAll();
- let json = false;
- if (page.waitingRender && !page.viewer) {
- json = _.cloneDeep(page.json);
- } else if (page && page.viewer && !page.viewer.isRendered) {
- const data = await viewer.loadPdfPage(page.viewer.pageNumber);
- const importedData = await this.importPDF.parsePageToJSON(data);
- json = {
- objects: importedData.json.objects.map(object => {
- object = this.setupDefaultOptions(object);
- if (this.initialParseObjects) {
- object = this.initialParseObjects(object);
- }
- return object;
- }),
- width: importedData.data.options.width,
- height: importedData.data.options.height
- }
- page.json = _.cloneDeep(json);
- page.waitingRender = true;
- delete page.viewer;
- } else {
- json = page.canvas.toJSON(this.defaultFields);
- }
- pages.push({
- id: page.id,
- isLockRotate: !!page.isLockRotate,
- rotate: page.rotate || 0,
- json, data: page.data
- });
- }
- const data = {
- id: store.state.activeTemplate.id,
- preview: previewBase64 || require('@/assets/img/preview_page.png'),
- title: store.state.activeTemplate.title,
- categories: store.state.activeTemplate.categories,
- created_at: store.state.activeTemplate.created_at || new Date().toISOString(),
- data: { fonts: store.state.activeTemplate.data.fonts || {}, pages }
- };
- return data;
- // resolve(data);
- // };
- // fr.readAsDataURL(thumb);
- // });
- }
- setZoom(value = 100) {
- const minZoom = 10;
- const maxZoom = 400;
- if (value < minZoom) value = minZoom;
- if (value > maxZoom) value = maxZoom;
- for (const page of this.pages) {
- const w = (value / 100) * page.data.options.width;
- const h = (value / 100) * page.data.options.height;
- page.canvas.setZoom(value / 100);
- page.canvas.setWidth(w);
- page.canvas.setHeight(h);
- $(page.canvas.upperCanvasEl.closest('.document--new')).css({
- width: `${w}px`,
- height: `${h}px`
- });
- page.canvas.getObjects().forEach(o => {
- if (o && o.payload && ['radio_button', 'checkbox', 'dropdown'].includes(o.payload.type)) {
- o.fire('sync:position');
- }
- });
- }
- store.state.zoom = value;
- const active = this.getActiveObject();
- if (active && active.fire) active.fire('next:tooltip');
- }
- togglePreviewMode() {
- const body = document.body;
- const classString = 'workspase--preview';
- const isPreview = body.classList.contains(classString);
- body.classList[isPreview ? 'remove' : 'add'](classString);
- store.state.previewMode = !isPreview;
- this.pages.forEach(page => page.canvas.discardActiveObject().renderAll());
- }
- enlivenObjects(objs = []) {
- return new Promise(resolve => {
- fabric.util.enlivenObjects(objs, resolve);
- });
- }
- async addElement(type, data = {}, forcePage = false, addCallback = false) {
- const page = this.getActivePage(forcePage);
- const defaultSettings = this.defaultSettings;
- if (!page) return false;
- const payload = { type, settings: _.cloneDeep(defaultSettings) };
- const options = { ...(data.options || {}), payload };
- const addElementToPage = addCallback || (data => this.addElementToPage(data, true, forcePage));
- switch (type) {
- case 'text': {
- return addElementToPage(
- new fabric.Textbox(data.text || 'Body text', {
- fontSize: 22, fontFamily: 'Arial', //'sans-serif',
- //left: 200, top: 200,
- width: 200,
- ...options,
- }), true, forcePage
- );
- }
- case 'table': {
- let cols = 3;
- let rows = 4;
- const json = data.jsonData || tablePlugin.createTableJSON(cols, rows);
- const table = await tablePlugin.loadTableJSON(json);
- table.set({ ...options, payload: { ...options.payload, ...table.payload } });
- return addElementToPage(table, true, forcePage);
- }
- case 'heading': {
- payload.heading = 'h1';
- return addElementToPage(
- new fabric.Textbox(data.text || 'Heading', {
- width: 200, fontSize: 32,
- fontFamily: 'Arial', fontWeight: 'bold',
- ...options,
- }), true, forcePage
- );
- }
- case 'image': {
- options.payload.settings.isEditable = true;
- options.left = 0;
- options.top = 0;
- let image = false;
- if (data.src) {
- image = await new Promise((resolve, reject) => {
- fabric.Image.fromURL(data.src, (img, err) => {
- if (err) reject(err);
- resolve(img);
- }, options);
- });
- const minSize = .3 * Math.min(page.canvas.width, page.canvas.height);
- let scale = Math.min(minSize / image.width, minSize / image.height);
- if (scale > 1) scale = 1;
- image.set({ scaleX: scale, scaleY: scale });
- } else {
- image = await this.createImageAppend();
- options.payload.groupImagePreview = true;
- image.set(options);
- }
- /* if (image.left <= 0 && image.top <= 0) {
- image.set({
- left: (image.width * image.scaleX / 2),
- top: (image.height * image.scaleY / 2),
- });
- } */
- return addElementToPage(image, true, forcePage);
- }
- case 'square': {
- return addElementToPage(new fabric.Rect({
- width: 100, height: 100, fill: 'rgba(0, 0, 0, 0)',
- stroke: '#bda6fc', strokeWidth: 1, strokeUniform: true,
- rx: 10, ry: 10, ...options
- }), true, forcePage);
- }
- case 'elipse': {
- return addElementToPage(new fabric.Rect({
- width: 100, height: 100, fill: 'rgba(0, 0, 0, 0)',
- stroke: '#bda6fc', strokeWidth: 1, strokeUniform: true,
- rx: 100, ry: 100, ...options
- }), true, forcePage);
- }
- case 'line': {
- return addElementToPage(new fabric.Line([0, 0, 200, 0], {
- stroke: '#bda6fc', strokeWidth: 3,
- strokeUniform: true, ...options
- }), true, forcePage);
- }
- // case 'sign': {
- // const image = await new Promise((resolve, reject) => {
- // fabric.Image.fromURL(require('@/assets/img/la_signature.svg'), (img, err) => {
- // if (err) reject(err);
- // resolve(img);
- // }, options);
- // });
- // options.payload.settings.isEditable = true;
- // options.payload.settings.isResizable = true;
- // image.set({ scaleX: 5, scaleY: 5, backgroundColor: 'rgba(200, 200, 200, .5)' });
- // return this.addElementToPage(image, true, forcePage);
- // }
- case 'full_name':
- case 'email':
- case 'address':
- case 'phone':
- case 'website':
- case 'custom_field':
- case 'sign':
- case 'number':
- case 'checkbox':
- case 'radio_button':
- case 'dropdown':
- case 'initials':
- case 'currency':
- case 'date': {
- let fieldText = {
- full_name: 'Full Name',
- email: 'email@mail.com',
- address: '4088 Woodbridge Dr Lansing, Michigan(MI), 48911',
- phone: '(517) 887-7086',
- website: location.origin,
- sign: 'Signature',
- custom_field: 'Custom Field',
- date: (() => {
- const date = new Date();
- return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`
- })(),//'01.01.2001',
- number: `${utils.getRandomNumber(1000, 9999)}`,
- initials: 'initials', //'L. Bury.',
- currency: '36.2$',
- };
- let element = false;
- options.payload.settings.isEditable = true;
- if (['sign', 'initials'].includes(type)) {
- const rect = new fabric.Rect({
- left: 0, top: 0, width: 250, height: 80,
- strokeWidth: 0, fill: '#e3e3e3'
- });
- options.payload.type = 'sign';
- if (type == 'initials') {
- options.payload.subtype = 'initials';
- }
- const text = new fabric.Text(fieldText[type], { fontSize: 22, fontFamily: 'Arial' });
- //text.set({ left: (rect.width - text.width) / 2, top: (rect.height - text.height) / 2 });
- element = new fabric.Group([rect, text], options);
- } else if (['checkbox', 'radio_button', 'dropdown'].includes(type)) {
- const id = utils.uuidv4();
- options.payload.settings.name = `input_element#${id}`;
- if (['checkbox', 'radio_button'].includes(type)) {
- options.payload.settings.checked = false;
- } else {
- options.payload.settings.active = '';
- options.payload.settings.options = [
- 'Option 1', 'Option 2', 'Option 3'
- ];
- }
- element = new fabric.Rect({
- id, width: type == 'dropdown' ? 180 : 35,
- fill: '#ffffff', strokeWidth: 0,
- height: type == 'dropdown' ? 40 : 35,
- stroke: '#000', ...options,
- });
- } else {
- element = new fabric.Textbox(fieldText[type], {
- fontSize: 12, fontFamily: 'Arial',//,'sans-serif',
- //left: 200, top: 200,
- width: 200,
- backgroundColor: this.settings.fieldBackgroundColor,//'rgba(255, 183, 0, 0.2)',
- ...options,
- });
- if (type == 'currency') {
- element.payload.settings.currency = '$';
- }
- if (type == 'date') {
- element.payload.date = Date.now();
- element.payload.format = 'dd.mm.yyyy';
- element.editable = false;
- }
- }
- return addElementToPage(element, true, forcePage);
- }
- case 'arrow': {
- const arrow = lineArrow.drawArrow(0, 0, 100, 0, options);
- return addElementToPage(arrow, true, forcePage);
- }
- }
- return addElementToPage(false);
- }
- //TODO import pages here
- async generateImportedDocument(importData = {}, filename = '', isAppendPages = false) {
- const { data, fonts } = await this.importPDF.parseImportData(importData);
- // console.time('t');
- // const previews = importData.file ? await pdf_previewer.parseDocumentThumbails(URL.createObjectURL(importData.file)) : [];
- // console.timeEnd('t');
- const prerenderThumbs = array => {
- return false;
- // PREVIEW OFF
- if (importData.file) {
- pdf_previewer.parseDocumentThumbails(URL.createObjectURL(importData.file), array, this);
- }
- };
- const previewProcessArray = data.reduce((result, page, index) => ([...result, { id: page.id, index, pageNumber: page.viewer.pageNumber }]), []);
- if (isAppendPages) {
- const skip = _.cloneDeep(this.pages.map(q => q.id));
- this.pages.push(...data.map(page => ({ ...page, thumb: false })));
- store.state.activeTemplate.data.fonts = _.assign(store.state.activeTemplate.data.fonts || {}, fonts);
- // store.state.activeTemplate.data.pages = this.pages;
- await this.renderPages(skip);
- const firstIncludePage = this.pages.find(page => data[0] && data[0].id && page.id == data[0].id);
- if (firstIncludePage) this.scrollToPage(firstIncludePage);
- this.setZoom(store.state.zoom);
- prerenderThumbs(previewProcessArray);
- this.appendWatermarks();
- } else {
- await this.saveCurrentTemplate();
- const payloadData = { pages: data, fonts };
- const newTemplate = _.assign(
- _.cloneDeep(store.state.emptyTemplate),
- { data: payloadData, title: filename }
- );
- store.state.lockLoaded = true;
- store.state.lastSavedTemplate = newTemplate;
- store.state.activeTemplate = newTemplate;
- const result = await this.openTemplate(newTemplate);
- //store.state.$router.push('/create-template').catch(err => { });
- prerenderThumbs(previewProcessArray);
- return result;
- }
- }
- async saveCurrentTemplate() {
- const isSave = () => {
- return new Promise(resolve => {
- store.state.swal({
- title: 'Save document', icon: 'warning',
- text: `Do you want to save document "${store.state.activeTemplate.title}"?`,
- showCancelButton: true,
- // confirmButtonColor: '#3085d6',
- cancelButtonColor: '#d33',
- confirmButtonText: 'Save'
- }).then(result => resolve(result.isConfirmed));
- });
- };
- const isNew = !store.state.activeTemplate.hash;
- const isPages = this.pages.length > 1;
- store.state.loading = true;
- if (isNew) {
- if (this.pages.length <= 1 && !this.pages[0].canvas.getObjects().length) {
- store.state.loading = false;
- return false;
- }
- if (await isSave()) {
- const json = await this.exportCurrentState();
- await idb.saveTemplate({
- ...json, ..._.pick(_.cloneDeep(store.state.activeTemplate), ['id', 'hash'])
- }, false);
- }
- } else {
- const json = await this.exportCurrentState();
- await idb.saveTemplate({
- ...json, ..._.pick(_.cloneDeep(store.state.activeTemplate), ['id', 'hash'])
- }, false);
- }
- store.state.loading = false;
- }
- fixTextNull(objects) {
- return objects.reduce((res, obj) => {
- if (obj.objects && obj.objects.length) {
- obj.objects = this.fixTextNull(obj.objects);
- }
- // eslint-disable-next-line no-prototype-builtins
- if ((obj || {}).hasOwnProperty('text')) {
- obj.text = obj.text || ' ';
- }
- res.push(obj);
- return res;
- }, []);
- }
- async openTemplate(template, bodyElement = false) {
- console.timeEnd('openTemplate');
- console.time('RENDER PDF');
- if (template && template.data && template.data.pages && template.data.pages.length) {
- template.data.pages = template.data.pages.reduce((res, page) => {
- if (page.json && page.json.objects && page.json.objects.length) {
- page.json.objects = this.fixTextNull(page.json.objects);
- }
- res.push(page);
- return res;
- }, []);
- }
- this.lockUpdateThumbail = true;
- try {
- this.loadedTemplate = false;
- if (template) {
- store.state.sharedUsers = _.cloneDeep(template.users || []);
- if (this.pages && this.pages.length) {
- this.pages.forEach(page => page.canvas.dispose());
- }
- this.loadedTemplate = _.cloneDeep(template);
- await store.dispatch('addRecent', template);
- try {
- if (template.data.fonts && Object.keys(template.data.fonts).length) {
- Object.values(_.cloneDeep(template.data.fonts))
- .forEach(font => viewer.appendCustomFontOnPage(font, true));
- console.time('Load Fonts');
- await new Promise(resolve => {
- WebFont.load({
- custom: {
- families: Object.keys(template.data.fonts)
- },
- // fontloading(fn) {
- // console.log('Loaded custom font: ', fn);
- // },
- active: resolve,
- });
- });
- console.timeEnd('Load Fonts');
- }
- } catch (err) {
- console.error(err);
- }
- const skipRenderPages = [];
- this.pages = template.data.pages.map((page, i) => {
- const result = { ...page, thumb: page.thumb || false };
- if (i > 2 && !page.viewer) {
- result.waitingRender = true;
- skipRenderPages.push(page.id);
- }
- return result;
- });
- await this.renderPages(skipRenderPages, true);
- await this.fitToScreenPages(bodyElement);
- // if (!bodyElement) bodyElement = document.querySelector('.workspase__body');
- // if (!bodyElement || !this.pages.length) {
- // await this.setZoom(store.state.zoom);
- // } else {
- // const maxZoomSize = bodyElement.getBoundingClientRect().width - 100;
- // const maxPageSize = Math.max(...this.pages.map(p => p.data.options.width));
- // let zoom = maxZoomSize / maxPageSize;
- // // TODO:zoom = 1;
- // if (zoom > 4) zoom = 4;
- // // zoom = 1;
- // await this.setZoom(zoom * 100);
- // }
- // store.state.sharedUsers = [];
- this.history.clearHistory(false);
- this.syncEditableObjects();
- // if (this._type == 'edit' && store.state.user.plan != 'premium') {
- // await this.appendWatermarks();
- // }
- await this.appendWatermarks();
- }
- } catch (err) {
- console.error(err);
- return this;
- }
- this.lockUpdateThumbail = false;
- console.timeEnd('RENDER PDF');
- return this;
- }
- async fitToScreenPages(bodyElement = false) {
- if (!bodyElement) bodyElement = document.querySelector('.workspase__body');
- if (!bodyElement || !this.pages.length) {
- await this.setZoom(store.state.zoom);
- } else {
- const maxZoomSize = bodyElement.getBoundingClientRect().width - 100;
- const maxPageSize = Math.max(...this.pages.map(p => p.data.options.width));
- let zoom = maxZoomSize / maxPageSize;
- // TODO:zoom = 1;
- if (zoom > 4) zoom = 4;
- // zoom = 1;
- await this.setZoom(zoom * 100);
- }
- }
- syncEditableObjects() { }
- deletePage(page) {
- if (this.pages.length == 1) return;// alert('DELETE LAST PAGE?');
- const clonePage = _.cloneDeep(_.omit(page, ['canvas']));
- const canvas = page.canvas;
- const pageIndex = this.getPageIndex(page);
- const element = canvas.upperCanvasEl.closest('.document--new');
- const json = canvas.toJSON(this.defaultFields);
- canvas.dispose();
- element.remove();
- this.pages = this.pages.filter(p => p.id != page.id);
- if (page.id == store.state.createTemplate.activePage) {
- this.selectActivePage(this.pages[pageIndex] || this.pages[pageIndex - 1]);
- }
- this.history.add({ page: { payload: clonePage, json, isAdded: false, index: pageIndex } });
- }
- selectActivePage(page) {
- store.dispatch('selectActiveObject', { target: false, page: page.id });
- setTimeout(() => this.scrollToPage(page), 10);
- }
- async addPage(payloadPage = false, options = {}) {
- const page = payloadPage || {
- id: utils.uuidv4(),
- thumb: false,
- data: {
- options: {
- backgroundColor: '#ffffff',
- width: 612, height: 792,
- controlsAboveOverlay: true
- }
- }
- };
- const clonePage = _.cloneDeep(page);
- if (options && (options.indexAppend || options.indexAppend == 0)) {
- this.pages.splice(options.indexAppend, 0, page);
- } else {
- this.pages.push(page);
- }
- this.renderPage(page, options || {});
- this.selectActivePage(page);
- this.history.add({ page: { payload: clonePage, isAdded: true, json: false, index: this.getPageIndex(page) } });
- this.appendWatermarks();
- this.fitToScreenPages();
- }
- getPageIndex(page) {
- return this.pages.findIndex(p => p.id == page.id);
- return Array.from(page.canvas.upperCanvasEl.closest('.document.document--new'))
- .findIndex(el => el.getAttribute('data-page') == clonePage.id);
- }
- scrollToPage(page) {
- const scrollElement = page.canvas.upperCanvasEl.closest('.document--new');
- scrollElement.scrollIntoView({ block: 'start', behavior: 'smooth' });
- }
- tick() { }
- _setupControls(object, update, def = false) {
- const controls = {
- tl: def, mt: def, tr: def, ml: def,
- mr: def, bl: def, mb: def, br: def,
- mtr: def, ...update
- };
- for (let key in controls) {
- object.setControlVisible(key, controls[key]);
- }
- }
- fillWatermarkOnPage(page = false) {
- if (!page || !page.canvas) return false;
- const canvas = page.canvas;
- fabric.Image.fromURL('/img/logo.png', img => {
- let watermarks = [];
- img.set({ angle: 45, opacity: .2, left: 0, top: 0 });
- const size = utils.getOriginSize(img);
- img.set({
- left: (size.width / 2) - size.width * 1.5,
- top: (size.height / 2) - size.height * 1.5
- });
- while (true) {
- const last = watermarks[watermarks.length - 1] || img;
- const size = utils.getOriginSize(last);
- const right = size.width + size.x;
- const bottom = size.height + size.y;
- if (watermarks.length > 50) break;
- if (right > canvas.getWidth()) {
- if (bottom > canvas.getHeight() + size.height) {
- break;
- } else {
- const clone = fabric.util.object.clone(img);
- clone.set({ left: 0/* size.width / 2 */, top: last.top + size.height * 2 });
- watermarks.push(clone);
- }
- } else {
- const clone = fabric.util.object.clone(img);
- clone.set({ left: last.left + size.width * 2, top: last.top });
- watermarks.push(clone);
- }
- }
- const group = new fabric.Group(watermarks);
- group.set({
- left: group.width / 2,
- top: group.height / 2,
- payload: { type: 'watermark' },
- evented: false,
- selectable: false
- });
- canvas.add(group);
- });
- }
- addObjectHistory(obj, page = false) {
- if (obj && obj.payload && tablePlugin.appends.includes(obj.payload.type)) return;
- if (obj.type === 'activeSelection') {
- if (!page) page = this.getPageByTarget(obj.getObjects()[0]);
- const objs = obj.getObjects().map(o => {
- const json = o.toJSON(this.defaultFields);
- json.left += obj.left;
- json.top += obj.top;
- return json;
- });
- this.history.add({ objs, pageId: page ? page.id : false });
- } else {
- if (!page) page = this.getPageByTarget(obj);
- const JSON = obj.toJSON(this.defaultFields);
- this.history.add({ objs: [JSON], pageId: page ? page.id : false });
- }
- }
- appendElement(obj) {
- let element = false;
- if (obj.payload.type == 'checkbox') {
- const label = document.createElement('label');
- const input = document.createElement('input');
- const icon = document.createElement('span');
- label.setAttribute('class', 'custom-checkbox');
- input.setAttribute('type', 'checkbox');
- input.setAttribute('name', obj.payload.settings.name);
- icon.setAttribute('class', 'custom-checkbox__icon');
- label.append(input);
- label.append(icon);
- element = label;
- if (obj.payload.settings.checked)
- input.setAttribute('checked', obj.payload.settings.checked);
- input.addEventListener('input', e => {
- obj.payload.settings.checked = e.target.checked;
- this.addObjectHistory(obj);
- });
- obj.on('sync:options', e => input.name = obj.payload.settings.name);
- this._setupControls(obj, { tl: true, tr: true, bl: true, br: true });
- }
- if (obj.payload.type == 'radio_button') {
- // <label class="custom-radio mb-12">
- // <input type="radio" name="radio-1">
- // <span class="custom-radio__icon"></span>
- // </label>
- const label = document.createElement('label');
- const input = document.createElement('input');
- const icon = document.createElement('span');
- label.setAttribute('class', 'custom-radio');
- input.setAttribute('type', 'radio');
- icon.setAttribute('class', 'custom-radio__icon');
- input.setAttribute('name', obj.payload.settings.name);
- label.append(input);
- label.append(icon);
- element = label;
- if (obj.payload.settings.checked)
- input.setAttribute('checked', obj.payload.settings.checked);
- input.addEventListener('input', e => {
- const canvas = obj.canvas;
- let objsHistory = [];
- canvas.getObjects().forEach(o => {
- if (o.payload && o.payload.settings && o.payload.settings.name == obj.payload.settings.name) {
- if (o.id != obj.id) {
- o.payload.settings.checked = false;
- objsHistory.push(o);
- }
- }
- });
- obj.payload.settings.checked = e.target.checked;
- const page = this.getPageByTarget(obj);
- const JSON = [obj, ...objsHistory].map(obj => obj.toJSON(this.defaultFields));
- this.history.add({ objs: JSON, pageId: page ? page.id : false });
- });
- obj.on('sync:options', e => input.name = obj.payload.settings.name);
- this._setupControls(obj, { tl: true, tr: true, bl: true, br: true });
- }
- if (obj.payload.type == 'dropdown') {
- this._setupControls(obj, { /*ml: true, mr: true,*/ br: true });
- const outer = document.createElement('div');
- outer.setAttribute('class', 'zindex-3');
- const select = document.createElement('select');
- select.setAttribute('class', 'select-default');
- outer.append(select);
- element = outer;
- }
- if (element) {
- // element.setAttribute('name', obj.payload.settings.name);
- element.setAttribute('class', 'data-element-editor ' + element.classList.toString());
- element.setAttribute('id', `element_${obj.id}`);
- const updatePosition = () => {
- const zoom = obj.canvas.getZoom();
- const w = (obj.width * obj.scaleX);// * zoom;
- const h = (obj.height * obj.scaleY);// * zoom;
- let bounding_rect = obj.getBoundingRect();
- //console.log("obj.scaleX", obj.scaleX, obj.scaleY);
- let styles = {
- position: 'absolute',
- left: `${obj.left * zoom - w * zoom / 2}px`,
- top: `${obj.top * zoom - h * zoom / 2}px`,
- 'pointer-events': 'none',
- // width: `${w}px`,
- // height: `${h}px`,
- width: `${bounding_rect.width - (obj.padding * 2)}px`,
- height: `${bounding_rect.height - (obj.padding * 2)}px`,
- //transform: `scale(${zoom})`,
- 'transform-origin': 'top left',
- 'font-size': `${obj.fontSize || 14}px`
- };
- // if(obj.payload.type=='dropdown'){
- //
- // styles['transform'] = `scale(${obj.scaleX}, ${obj.scaleY})`;
- // }
- element.setAttribute('style', utils.objectStyleToString(styles));
- };
- updatePosition();
- // if (obj.payload.type == 'dropdown') {
- // element.addEventListener('click', e => updatePosition());
- // }
- obj.on('sync:position', e => updatePosition());
- ['moving', 'scaling', 'resizing', 'rotating', 'skewing'].forEach(ev => {
- obj.on(ev, e => updatePosition());
- });
- if (obj.payload.type != 'dropdown') {
- const clickToElement = () => {
- element.style['pointer-events'] = 'all';
- element.click();
- updatePosition();
- };
- if (this._type == 'create' && !this.isConfirmMode) {
- obj.on('mousedblclick', e => clickToElement());
- }
- if (obj.payload.settings.isEditable && this._type == 'edit') {
- obj.on('mouseup', e => {
- if (this.isClickByMouseUpFabric(e)) clickToElement();
- });
- }
- }
- const canvas = obj.canvas;
- const upperCanvasEl = canvas.upperCanvasEl;
- const parent = upperCanvasEl.closest('.document--new');
- parent.appendChild(element);
- if (obj.payload.type == 'dropdown') {
- try {
- element.children[0].addEventListener('change', e => {
- const value = e.target.value;
- obj.payload.settings.active = value;
- this.addObjectHistory(obj);
- });
- const choice = new Choices(element.children[0], {
- searchChoices: false, searchEnabled: false,
- allowHTML: true, position: 'top',
- shouldSort: false, classNames: {
- containerOuter: 'choices select-default',
- selectedState: 'is-selected',
- }
- });
- if (this._type == 'create' && !this.isConfirmMode) {
- obj.on('mousedblclick', e => {
- choice.dropdown.show();
- Array.from(document.querySelectorAll('.data-element-editor.zindex-9')).forEach(dee => {
- dee.classList.remove('zindex-9');
- });
- element.classList.add('zindex-9');
- updatePosition();
- });
- }
- if (obj.payload.settings.isEditable && this._type == 'edit') {
- obj.on('mouseup', e => {
- if (this.isClickByMouseUpFabric(e)) {
- setTimeout(() => {
- choice.dropdown.show();
- updatePosition();
- }, 5);
- }
- });
- }
- const renderOptions = () => {
- if (obj.payload.settings.options && obj.payload.settings.options.length) {
- const list = obj.payload.settings.options.map(q => ({ label: q, value: q }));
- choice.setChoices(list, undefined, undefined, true);
- }
- choice.setChoiceByValue(obj.payload.settings.active);
- };
- renderOptions();
- obj.on('sync:options', e => renderOptions());
- } catch (err) {
- console.error(err);
- }
- }
- }
- }
- appendErrorMessage(obj = false, isError = false, message) {
- if (!obj) return false;
- const isRequired = obj && obj.payload && obj.payload.settings ? !!obj.payload.settings.isRequeired : true;
- if (((this._type == 'create' && !this.isConfirmMode) || !isRequired) && !obj.text) {
- isError = false;
- // fix todo:
- }
- obj.set({
- backgroundColor: !isError ? this.settings.fieldBackgroundColor : this.settings.errorBackgroundField,
- payload: {
- ...obj.payload,
- errorMessage: !isError ? '' : message
- }
- });
- try {
- store.state.editTemplate.editableObjects = store.state.editTemplate.editableObjects.map(eo => {
- if (eo.id == obj.id) eo.payload = obj.payload;
- return eo;
- });
- } catch (err) {
- console.error(err);
- }
- if (isError) store.state.$toast({ message: message, type: 'error' });
- obj.canvas.renderAll();
- }
- isEditableObject(obj) {
- return obj && obj.payload && obj.payload.settings && obj.payload.settings.isEditable;
- }
- isRequiredEdit(obj) {
- return this.isEditableObject(obj) && obj.payload.settings.isRequeired;
- }
- setupDefaultOptions(obj) {
- const isFabricObject = !!obj.on;
- if (obj && obj.payload && obj.payload.type == 'image' && isFabricObject && !this.isConfirmMode) {
- const openReplace = () => {
- store.state.activeChangedImage = true;
- store.state.sidebar = 'image';
- store.state.sidebarTabs.image = 'images';
- };
- obj.on('mousedblclick', e => {
- utils.openUploadFile('image/png, image/jpg, image/jpeg').then(async files => {
- if (files && files.length) {
- await idb.uploadImage(files[0]);
- await this.replaceSelectedImage(files[0]);
- if (this._type != 'edit') setTimeout(() => {
- store.state.createTemplate.ticker += 1;
- }, 100);
- }
- });
- });
- }
- if (isFabricObject) {
- obj.on('modified', e => {
- let findedErrorMessages = [];
- for (const page of this.pages) {
- const errorObjects = page.canvas.getObjects().filter(o => o && o.payload && o.payload.errorMessage);
- if (errorObjects.length) {
- findedErrorMessages.push({ page: page.id, objects: errorObjects });
- }
- }
- store.state.beforeSave.errors = findedErrorMessages;
- });
- }
- if (this._type == 'edit' && isFabricObject) {
- if (obj && obj.payload && obj.payload.type && tablePlugin.appends.includes(obj.payload.type)) {
- } else {
- this.setupFieldsSwitching(obj);
- if (!this.isEditableObject(obj)) {
- obj.backgroundColor = 'rgba(0, 0, 0, 0)';
- } else {
- obj.backgroundColor = this.settings.fieldBackgroundColor;//'rgba(255, 183, 0, 0.2)';
- }
- if (this.isRequiredEdit(obj)) {
- obj.backgroundColor = '#ffeddf';
- store.state.beforeSave.requiredObjects.push(obj.id);
- obj.on('modified', e => {
- obj.backgroundColor = this.settings.fieldBackgroundColor;//'rgba(255, 183, 0, 0.2)';
- store.state.beforeSave.requiredObjects = store.state.beforeSave.requiredObjects.filter(r => r != obj.id);
- obj.canvas.renderAll();
- });
- }
- }
- }
- if (!obj.payload) obj.payload = { type: 'unnamed' };
- if (isFabricObject) {
- const isTextElement = ['i-text', 'textbox', 'line'].includes(obj.type);
- obj.setControlVisible('mtr', false);
- if (isTextElement) {
- obj.setControlVisible('mb', false);
- obj.setControlVisible('tl', false);
- obj.setControlVisible('tr', false);
- obj.setControlVisible('mt', false);
- obj.setControlVisible('bl', false);
- obj.setControlVisible('br', false);
- if (obj.type == 'i-text' || (obj && obj.payload && obj.payload.fromPDF)) {
- obj.setControlVisible('ml', false);
- obj.setControlVisible('mr', false);
- obj.on('mousedown', e => {
- if (obj.objectCaching)
- obj.objectCaching = false;
- })
- }
- }
- const formList = ['date', 'email', 'website', 'phone', 'number', 'currency'];
- if (obj.payload && ['checkbox', 'radio_button', 'dropdown'].includes(obj.payload.type)) {
- this.appendElement(obj);
- } else if (obj.payload && formList.includes(obj.payload.type)) {
- if ((obj.payload.settings || obj.payload.settings.isEditable) || (this._type == 'create' || !this.isConfirmMode)) {
- switch (obj.payload.type) {
- case 'date':
- if (this._type == 'edit') {
- obj.on('mouseup', e => store.state.widget = 'date');
- } else {
- obj.on('mousedblclick', e => store.state.widget = 'date');
- }
- break;
- case 'number':
- obj.on('editing:exited', e => {
- const numberValue = parseInt(obj.text);
- const isError = isNaN(numberValue);
- if (obj.text != numberValue && !isError) {
- obj.text = String(numberValue);
- }
- this.appendErrorMessage(obj, isError, 'Invalid number value!');
- });
- break;
- case 'currency':
- obj.on('editing:exited', e => {
- const numberValue = parseFloat(obj.text);
- const isError = isNaN(numberValue);
- if (!isError) {
- obj.text = String(numberValue) + `${obj.payload.settings.currency || '$'}`;
- }
- this.appendErrorMessage(obj, isError, 'Invalid currency value!');
- });
- break;
- case 'email':
- obj.on('editing:exited', e => {
- const isEmail = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g.test(obj.text);
- this.appendErrorMessage(obj, !isEmail, 'Incorrect email address entered!');
- });
- break;
- case 'website':
- obj.on('editing:exited', e => {
- const isUrl = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi.test(obj.text);
- obj.set({
- backgroundColor: isUrl ? this.settings.fieldBackgroundColor : this.settings.errorBackgroundField,
- payload: {
- ...obj.payload,
- errorMessage: isUrl
- ? '' : 'Incorrect URL adress!'
- }
- });
- obj.canvas.renderAll();
- });
- break;
- }
- }
- }
- if (obj.payload && ['sign'].includes(obj.payload.type)) {
- if (obj.type == 'textbox') {
- obj.setControlVisible('mb', false);
- obj.setControlVisible('tl', false);
- obj.setControlVisible('tr', false);
- obj.setControlVisible('mt', false);
- obj.setControlVisible('bl', false);
- obj.setControlVisible('br', false);
- } else {
- obj.setControlVisible('mb', false);
- obj.setControlVisible('mr', false);
- obj.setControlVisible('mt', false);
- obj.setControlVisible('ml', false);
- }
- }
- if (obj.payload && obj.payload.isElement) {
- // this.fixElementData(obj);
- obj.setControlVisible('mb', false);
- obj.setControlVisible('tl', false);
- obj.setControlVisible('tr', false);
- obj.setControlVisible('mt', false);
- obj.setControlVisible('bl', false);
- obj.setControlVisible('br', false);
- obj.setControlVisible('ml', false);
- obj.setControlVisible('mr', false);
- }
- if (obj.payload && ['arrow', 'line'].includes(obj.payload.type)) {
- obj.setControlVisible('mb', false);
- obj.setControlVisible('tl', false);
- obj.setControlVisible('tr', false);
- obj.setControlVisible('mt', false);
- obj.setControlVisible('bl', false);
- obj.setControlVisible('br', false);
- obj.setControlVisible('mtr', true);
- lineArrow.initArrowScaling(obj);
- }
- }
- if (!tablePlugin.appends.includes(obj.payload.type)) {
- const dataFields = {
- id: obj.id || utils.uuidv4(),
- transparentCorners: false, borderColor: '#7C4DFF',
- centeredRotation: true, borderDashArray: [4, 2], cornerColor: '#7C4DFF',
- cornerSize: 8, strokeUniform: true,
- originX: 'center', originY: 'center'
- };
- if (isFabricObject) {
- obj.set(dataFields);
- } else {
- for (const key in dataFields) {
- obj[key] = dataFields[key];
- }
- }
- if (obj.payload.type == 'table' && isFabricObject) {
- obj.set({ originX: 'left', originY: 'top' });
- tablePlugin.initTableFromJSON(obj, true);
- setTimeout(() => tablePlugin.calculateGroupPrint(obj), 500);
- }
- }
- if (obj.setCoords) obj.setCoords();
- return obj;
- }
- setupEraseCanvas(canvas) {
- const lowerCanvasEl = canvas.lowerCanvasEl;
- let objectToDrawing = false;
- let origX = false, origY = false;
- lowerCanvasEl.addEventListener('mousedown', e => {
- const layerX = e.layerX / canvas.getZoom();
- const layerY = e.layerY / canvas.getZoom();
- if (store.state.editorMode == 'erase') {
- origX = layerX;
- origY = layerY;
- objectToDrawing = new fabric.Rect({
- left: layerX,
- top: layerY,
- width: 1, height: 1,
- strokeWidth: 0,
- fill: '#ffffff',
- payload: { type: 'erase' }
- });
- canvas.add(objectToDrawing);
- }
- });
- lowerCanvasEl.addEventListener('mousemove', e => {
- const layerX = e.layerX / canvas.getZoom();
- const layerY = e.layerY / canvas.getZoom();
- if (objectToDrawing) {
- if (origX > layerX)
- objectToDrawing.set({ left: Math.abs(layerX) });
- if (origY > layerY)
- objectToDrawing.set({ top: Math.abs(layerY) });
- objectToDrawing.set({
- height: Math.abs(origY - layerY),
- width: Math.abs(origX - layerX),
- originX: 'left', originY: 'top'
- });
- if (objectToDrawing.canvas)
- objectToDrawing.canvas.renderAll();
- }
- });
- const stopErase = () => {
- if (objectToDrawing) {
- objectToDrawing.set({
- left: objectToDrawing.left + objectToDrawing.width / 2,
- top: objectToDrawing.top + objectToDrawing.height / 2,
- originX: 'center', originY: 'center'
- });
- objectToDrawing.setCoords();
- if (objectToDrawing.canvas)
- objectToDrawing.canvas.renderAll();
- //objectToDrawing.canvas.remove(objectToDrawing);
- objectToDrawing = false;
- }
- };
- lowerCanvasEl.addEventListener('mouseup', e => stopErase());
- }
- createCanvas(page, pageElement) {
- page.canvas = new fabric.Canvas(pageElement, {
- ...this.payloadPage,
- ...page.data.options,
- uniformScaling: false
- });
- //!this.isConfirmMode
- this.setupEraseCanvas(page.canvas);
- store.state.canvas = page.canvas;
- this.setupEvents(page);
- if (this._type == 'edit') {
- page.canvas.on('mouse:up', e => {
- if (!e.target) return;
- if (this.isClickByMouseUpFabric(e)) {
- const obj = e.target;
- if (obj && obj.payload && obj.payload.settings && obj.payload.settings.isEditable) {
- if (obj.payload.type == 'sign') {
- if (obj.type != 'textbox')
- return store.state.popup = 'signature';
- }
- if (obj.type == 'textbox') {
- obj.enterEditing();
- obj.selectAll();
- return;
- }
- }
- }
- });
- }
- page.canvas.on('path:created', e => {
- const path = e.path;
- path.payload = { type: 'image', settings: _.cloneDeep(this.defaultSettings || {}) };
- path.set({
- left: (path.left + path.width / 2) + path.strokeWidth / 2,
- top: (path.top + path.height / 2) + path.strokeWidth / 2
- });
- path.setCoords();
- page.canvas.renderAll();
- });
- return page;
- }
- isClickByMouseUpFabric(e) {
- try {
- const isTransform = e.transform && e.transform.original;
- if (isTransform) {
- const original = e.transform.original;
- const keys = ['left', 'top', 'scaleX', 'scaleY'];
- const isClick = keys.reduce((res, key) => {
- if (!res) return false;
- if (original[key] != e.target[key]) return false;
- return res;
- }, true);
- return isClick;
- }
- return false;
- } catch (err) {
- console.error(err);
- }
- return false;
- }
- setupEvents(page) { }
- showGrid(canvas) {
- let grid = this.grid;
- if (this.has_grid) {
- return;
- }
- this.has_grid = true;
- for (var i = 0; i < (canvas.width / grid); i++) {
- canvas.add(new fabric.Line([i * grid, 0, i * grid, canvas.height], { type: 'line', stroke: '#ccc', selectable: false, grid_element: true }));
- canvas.add(new fabric.Line([0, i * grid, canvas.width, i * grid], { type: 'line', stroke: '#ccc', selectable: false, grid_element: true }));
- }
- // let l2 = new fabric.Line([0, i * grid, 600, i * grid], {
- // stroke: '#ccc',
- // selectable: false
- // });
- // l2.grid_element = true;
- // canvas.add(l2);
- }
- hideGrid(canvas) {
- if (!canvas) {
- return;
- }
- canvas.getObjects().forEach(function (el) {
- if (el.grid_element) {
- canvas.remove(el);
- }
- });
- this.has_grid = false;
- }
- snapToGrid(options) {
- let grid = this.grid;
- const target = options.target;
- let incrementOriginX = 0;
- let incrementOriginY = 0;
- if (target.originX == 'center') incrementOriginX = (target.width / 2);
- if (target.originY == 'center') incrementOriginY = (target.height / 2);
- if (target.padding > 0) {
- incrementOriginX += target.padding / 2;
- incrementOriginY += target.padding / 2;
- }
- let left = target.left - incrementOriginX;
- let top = target.top - incrementOriginY;
- if (Math.round(left / grid * 4) % 4 == 0 && Math.round(top / grid * 4) % 4 == 0) {
- options.target.set({
- left: (Math.round(left / grid) * grid) + incrementOriginX,
- top: (Math.round(top / grid) * grid) + incrementOriginY
- }).setCoords();
- }
- }
- async renderPage(page, options = {}, renderEmptyPage = false) {
- const doc = document.createElement('div');
- doc.setAttribute('data-page', page.id);
- doc.setAttribute('class', 'document document--new');
- const pageElement = document.createElement('canvas');
- pageElement.setAttribute('class', 'document-container');
- doc.appendChild(pageElement);
- if (this.documentElement.children.length > options.indexAppend) {
- const element = this.documentElement.children[options.indexAppend];
- this.documentElement.insertBefore(doc, element);
- } else {
- this.documentElement.appendChild(doc);
- }
- this.createCanvas(page, pageElement);
- doc.setAttribute('style', `width:${page.canvas.width}px;height:${page.canvas.height}px;max-width:fit-content;`);
- const isDroppable = e => {
- if (store.state.dragging) {
- return ['element_sidebar', 'image_sidebar'].includes(store.state.dragging.type);
- } else {
- // console.log('else....');
- }
- };
- doc.addEventListener('dragover', e => {
- if (isDroppable(e)) doc.style['box-shadow'] = '0 0 5px 0px #7C4DFF';
- });
- doc.addEventListener('dragleave', e => {
- if (isDroppable(e)) doc.style['box-shadow'] = 'none';
- });
- doc.addEventListener('drop', e => {
- doc.style['box-shadow'] = 'none';
- if (isDroppable(e)) {
- const left = e.layerX / page.canvas.getZoom();
- const top = e.layerY / page.canvas.getZoom();
- const opts = { options: { left, top } };
- if (store.state.dragging.type == 'image_sidebar') {
- this.openImage(store.state.dragging.data.file, opts, page.id);
- } else {
- const payload = store.state.dragging.data.payload || {};
- if (payload.options && typeof payload.options === 'object') {
- payload.options = {
- ...payload.options,
- ...opts.options
- };
- } else {
- payload.options = opts.options;
- }
- this.addElement(store.state.dragging.data.key, payload, page.id);
- }
- }
- store.state.dragging = false;
- });
- if (!renderEmptyPage) await this.loadPageDataFromJson(page);
- return page;
- }
- async loadPageDataFromJson(page) {
- console.time('RENDER PAGE: ');
- if (!page || !page.json) return false;
- if (page.json && page.json.objects) {
- const parseTexts = objects => {
- return objects.map(o => {
- if (['textbox', 'i-text'].includes(o.type)) {
- // if (!o.text) o.text = ' ';
- if (o.text == ' ') o.text = '';
- }
- if (o.type == 'group') {
- o.objects = parseTexts(o.objects);
- }
- return o;
- });
- };
- page.json.objects = parseTexts(page.json.objects);
- }
- page = tablePlugin.parseTableFillData(page);
- const json = JSON.parse(JSON.stringify(page.json));
- await new Promise(resolve => {
- page.canvas.loadFromJSON(json, () => {
- page.canvas.renderAll.bind(page.canvas);
- if (this.initialParseObjects) {
- page.canvas.getObjects().forEach(obj => this.initialParseObjects(obj));
- }
- resolve();
- });
- });
- setTimeout(() => this.updateThumbail(page, true), 900);
- console.timeEnd('RENDER PAGE: ');
- return page;
- }
- get activeObject() {
- const create = store.state.createTemplate.editor;
- const editor = store.state.editTemplate.editor;
- if (create) return store.state.createTemplate.activeObject;
- if (editor) return store.state.editTemplate.activeObject;
- return false;
- }
- updateThumbail(page, force = false) {
- if (page && page.viewer && !page.viewer.isRendered) return;
- if (this.lockUpdateThumbail && !force) return false;
- const canvas = page.canvas;
- if (!page) return;
- const sc = Math.min(135 / (canvas.width * canvas.getZoom()), 185 / (canvas.height * canvas.getZoom()));
- const base64 = canvas.toDataURL({ multiplier: sc * 3 });
- fetch(base64).then(async res => {
- page.thumb = URL.createObjectURL(await res.blob());
- });
- return base64;
- }
- async loadFileToBase64(file) {
- return new Promise((resolve, reject) => {
- const fileReader = new FileReader();
- fileReader.onload = e => resolve(e.target.result);
- fileReader.onerror = e => reject(e);
- fileReader.readAsDataURL(file);
- });
- }
- cloneActiveElement() {
- const active = this.getActiveObject();
- if (active.type != 'activeSelection') {
- active.clone(clone => {
- clone.left += 10;
- clone.top += 10;
- clone.id = utils.uuidv4();
- // if(clone.payload.type == 'table') {
- // tablePlugin.initTableFromJSON(clone, true);
- // }
- active.canvas.add(clone).setActiveObject(clone);
- }, this.defaultFields);
- } else {
- // const clone = fabric.util.object.clone(active);
- }
- }
- getActiveObject() {
- const activePage = this.getActivePage();
- if (!activePage) return false;
- const active = activePage.canvas.getActiveObject();
- if (!active) return false;
- return active;
- }
- async replaceSelectedImage(file, options = {}, pageId = false) {
- const base64 = typeof file == 'string' ? file : await this.loadFileToBase64(file);
- const activeObject = this.activeObject;
- const w = activeObject.width * activeObject.scaleX;
- const h = activeObject.height * activeObject.scaleY;
- if (activeObject.type == 'image') {
- activeObject.setSrc(base64, target => {
- const scale = Math.min(w / target.width, h / target.height);
- target.set({ scaleX: scale, scaleY: scale });
- this.addObjectHistory(target);
- if (this.initialParseObjects) this.initialParseObjects(target);
- target.canvas.requestRenderAll();
- });
- } else {
- const image = await new Promise(resolve => fabric.Image.fromURL(base64, object => {
- const payload = _.cloneDeep(activeObject.payload);
- const scale = Math.min(w / object.width, h / object.height);
- object.set({
- scaleX: scale,
- scaleY: scale,
- left: activeObject.left,
- top: activeObject.top,
- id: activeObject.id,
- payload
- });
- resolve(object);
- }));
- if (activeObject.canvas) {
- const canvas = activeObject.canvas;
- canvas.remove(activeObject);
- canvas.add(image).setActiveObject(image).requestRenderAll();
- if (this.initialParseObjects) this.initialParseObjects(image);
- }
- }
- return activeObject;
- }
- addElementToPage(object, toActive = true, forcePage = false, lockPositions = false) {
- if (!object) return false;
- const page = this.getActivePage(forcePage);
- if (!page) return false;
- // page rotate
- if (!this.history.lockHistory) {
- this.pages = this.pages.map(p => {
- if (p.id == page.id) p.isLockRotate = true;
- return p;
- });
- }
- // page rotate
- let left = object.left;
- let top = object.top;
- const canvasWidth = page.canvas.getWidth() / page.canvas.getZoom();
- const canvasHeight = page.canvas.getHeight() / page.canvas.getZoom();
- if (left <= 0 && top <= 0) {
- left = (canvasWidth - object.width / object.scaleX) / 2;
- top = (canvasHeight - object.height / object.scaleY) / 2;
- if (object.payload.type == 'table') {
- left = (canvasWidth - 402) / 2;
- top = (canvasHeight - 116) / 2
- }
- }
- if (object.dynamicConfig && object.dynamicConfig.type == 'table') {
- object.set({ left: 0, top: 0, padding: this.defOptions });
- } else if (!lockPositions) {
- object.set({
- left: object.width * object.scaleX / 2 + left,
- top: object.height * object.scaleY / 2 + top,
- padding: this.defOptions.padding
- });
- }
- page.canvas.add(object);
- if (toActive) page.canvas.setActiveObject(object).renderAll();
- return object;
- }
- async downloadCurrentDocument(isWindowOpen = false) {
- store.state.loading = true;
- setTimeout(async () => {
- try {
- const zoomBefore = store.state.zoom;
- await this.setZoom(100);
- const blob = await new CreatePDF(_.cloneDeep(this.pages)).createDoc(this.pages, this);
- this.setZoom(zoomBefore);
- utils.downloadFile(URL.createObjectURL(blob), `${store.state.activeTemplate.title}.pdf`, isWindowOpen);
- } catch (err) {
- console.error(err);
- store.state.$toast({ message: 'Something went wrong', type: 'error' });
- }
- store.state.loading = false;
- }, 500);
- }
- async exportPDF(isDownload = false, toPreview = false) {
- const zoomBefore = store.state.zoom;
- await this.setZoom(100);
- const blob = await new CreatePDF(_.cloneDeep(this.pages)).createDoc(this.pages, this);
- let upload_url = provider.routers.save_filled.replace('%d', this.loadedTemplate.id);
- if (this.filled_template.id) { }
- const json = await this.exportCurrentState();
- let resp = await utils.uploadFile(upload_url, utils.blobToFile(blob), this.filled_template, json);
- if (resp.success) {
- this.filled_template.id = resp.id;
- this.filled_template.hash = resp.hash;
- } else {
- utils.showError("Error", resp.message ? resp.message : "Unknown error");
- return false;
- }
- //if (toPreview) return store.state.$router.push(`/preview-template/${resp.hash}?access_key=${resp.access_key}`);
- if (!isDownload) return blob;
- this.setZoom(zoomBefore);
- utils.downloadFile(URL.createObjectURL(blob), `${store.state.activeTemplate.title}.pdf`);
- return blob;
- }
- modifyObject(obj) {
- if (!obj) return false;
- store.state.editTemplate.editableObjects = store.state.editTemplate.editableObjects.map(eo => {
- if (eo && eo.payload && eo.payload.type) {
- switch (eo.payload.type) {
- case 'sign': {
- const object = this.pages.reduce((obj, page) => {
- const o = page.canvas.getObjects().find(ob => ob.id == eo.id);
- if (o) return o;
- return obj;
- }, false);
- if (object.type == 'group' && object.getObjects) {
- if (object.getObjects().find(q => q.type != 'path')) return eo;
- }
- break;
- }
- }
- }
- if (eo.id == obj.id) eo.isModified = true;
- return eo;
- });
- }
- async addSignature(sign = false, json = false) {
- // debugger;
- let isNew = false;
- if (!sign) {
- isNew = true;
- sign = await new Promise(resolve => this.addElement('sign', {}, false, resolve));
- }
- if (!sign || !json) return false;
- let object = (await this.enlivenObjects([json]))[0];
- const sw = sign.width * sign.scaleX;
- const sh = sign.height * sign.scaleY;
- const rect = new fabric.Rect({
- left: 0, top: 0,
- width: sw, height: sh,
- fill: 'rgba(0, 0, 0, 0)',
- stroke: 'rgba(0, 0, 0, 0)',
- strokeWidth: 0
- });
- let scale = Math.min(sw / object.width, sh / object.height);
- if (object.type == 'textbox' && scale > 1) scale = 1;
- object.set({
- scaleX: scale, scaleY: scale,
- left: (rect.width - object.width * scale) / 2,
- top: (rect.height - object.height * scale) / 2
- });
- let gr = new fabric.Group([rect, object], {
- left: sign.left,
- top: sign.top,
- id: sign.id,
- payload: _.cloneDeep({ ...sign.payload, isUpdated: true }),
- editable: true,
- backgroundColor: sign.backgroundColor,
- originX: 'center', originY: 'center'
- });
- object = gr;
- // const scale = /* object.type == 'textbox' ? 1 : */Math.min(
- // // (sign.width * sign.scaleX) / object.width,
- // (sign.height * sign.scaleY) / object.height
- // );
- // const ow = object.width;// * scale;
- // const oh = object.height;// * scale;
- // const sw = sign.width * sign.scaleX;
- // const sh = sign.height * sign.scaleY;
- // console.log('????sign.left', sign.left, sw, ow);
- // object.set({
- // id: sign.id,
- // scaleX: scale, scaleY: scale,
- // left: sign.left - (sw / 2) + ow / 2,
- // top: sign.top - (sh / 2) + oh / 2,
- // // top: sign.top - sh / 2 + (object.height * scale) / 2,
- // payload: _.cloneDeep({ ...sign.payload, isUpdated: true }),
- // editable: true,
- // backgroundColor: sign.backgroundColor
- // });
- if (sign.canvas) sign.canvas.remove(sign);
- // if (isNew) object.set({ left: object.width / 2 + 100, top: object.height / 2 + 100 });
- const obj = await this.addElementToPage(object, true, false, !isNew);
- this.modifyObject(obj);
- if (this.initialParseObjects) this.initialParseObjects(obj);
- return obj;
- }
- async openImage(file, options = {}, pageId = false) {
- const base64 = await this.loadFileToBase64(file);
- return this.addElement('image', { src: base64, ...options }, pageId);
- }
- setListStyle(textObject, type, canvas, allStyles) {
- var styles = ['\u25CF', '\u25C8', '\u25D8', '\u25BA', '\u25CB', '\u25A0', '\u27F6', '-'];
- var text = textObject.text;
- var textArray = text.split('\n')
- var tempStr = [];
- textArray.forEach((text, i) => {
- if (styles.includes(text.substr(0, 1))) {
- tempStr.push(text.replace(text.substr(0, 1), allStyles[type]));
- } else {
- tempStr.push(allStyles[type] + '' + text);
- }
- })
- textObject['text'] = tempStr.join('\n');
- canvas.renderAll();
- }
- setSelectionStyles(styles = {}, isUpdate = false) {
- const target = this.activeObject;
- if (!target) return false;
- let payload = {};
- const isFields = [
- 'full_name', 'email',
- 'address', 'date', 'phone',
- 'website', 'custom_field', 'sign',
- 'number', 'initials', 'currency'
- ].includes(target.payload ? (target.payload.type || '') : '');
- const getValueByKey = (key, value) => {
- switch (key) {
- case 'color':
- return { fill: value };
- case 'font':
- return { fontFamily: value };
- case 'bold':
- return { fontWeight: value ? 'bold' : 'normal' };
- case 'italic':
- return { fontStyle: value ? 'italic' : 'normal' };
- case 'header': {
- target.payload.heading = styles[key];
- const stylesHeading = {
- h1: { fontSize: 32, fontWeight: 'bold' },
- h2: { fontSize: 24, fontWeight: 'bold' },
- h3: { fontSize: 18, fontWeight: 'bold' },
- h4: { fontSize: 16, fontWeight: 'bold' },
- h5: { fontSize: 14, fontWeight: 'bold' },
- h6: { fontSize: 10, fontWeight: 'bold' },
- };
- if (stylesHeading[value]) {
- return { ...payload, ...stylesHeading[styles[key]] };
- } else {
- return {};
- }
- }
- default: return { [key]: value };
- }
- };
- for (const key in styles) {
- if (this.globalStyles.find(gs => gs == key) || isFields) {
- target.set(getValueByKey(key, styles[key]));
- target.canvas.requestRenderAll();
- } else {
- payload = { ...payload, ...getValueByKey(key, styles[key]) };
- }
- }
- if (Object.keys(payload).length) {
- if (target.isEditing) {
- let s = target.selectionStart, e = target.selectionEnd;
- if (target.selectionStart == target.selectionEnd) {
- s = target.selectionStart - 1;
- if (s < 0) s = 0, e = 1;
- }
- target.setSelectionStyles(payload, s, e);
- } else {
- target.set(payload);
- }
- target.canvas.requestRenderAll();
- }
- this.tick();
- if (isUpdate) this.addObjectHistory(target);
- }
- async renderPages(skipRenderPages = [], renderEmpty = false) {
- if (!skipRenderPages.length || renderEmpty) this.documentElement.innerHTML = '';
- for (const page of this.pages) {
- if (skipRenderPages.find(sp => sp == page.id)) {
- if (!renderEmpty) continue;
- await this.renderPage(page, {}, true);
- } else {
- await this.renderPage(page);
- }
- }
- }
- async url2file(dataurl, filename) {
- let arr = dataurl.split(','),
- mime = arr[0].match(/:(.*?);/)[1],
- bstr = atob(arr[1]),
- n = bstr.length,
- u8arr = new Uint8Array(n);
- while (n--) {
- u8arr[n] = bstr.charCodeAt(n);
- }
- return new File([u8arr], filename, { type: mime });
- }
- isPageReadyRendered(page) {
- if (!page) return false;
- if (page.viewer && page.viewer && !page.viewer.isRendered) return true;
- if (page.waitingRender) return true;
- return false;
- }
- handlePagesInViewport(ev) {
- const viewportElement = this.scrollPlugin.determineVisibleRows();
- if (viewportElement) this.renderCachePage(viewportElement);
- // document--new
- // const viewport = this.scrollPlugin.determineVisibleRows();
- // setAttribute('style', `width:${page.canvas.width}px;height:${page.canvas.height}px;max-width:fit-content;`);
- }
- async removeUnnecessaryPages(pageElement) {
- const pageIndex = this.pages.findIndex(page => page.id == pageElement.getAttribute('data-page'));
- if (pageIndex == -1) return false;
- let viewed_pages = [pageIndex - 1, pageIndex, pageIndex + 1, pageIndex + 2];
- for (let i = 0; i < this.pages.length; i++) {
- const page = this.pages[i];
- if (!viewed_pages.includes(i) && !page.disposed) {
- // Remove page and save json
- page.disposed = true;
- page.cacheJSON = {
- ...page.canvas.toJSON(this.defaultFields),
- zoom: page.canvas.getZoom()
- };
- // page.canvas.dispose();
- page.canvas.clear();
- }
- }
- }
- async renderCachePage(pageElement) {
- const zoom = store.state.zoom / 100;
- const page = this.pages.find(page => page.id == pageElement.getAttribute('data-page'));
- if ((this.isRenderCache && !page.viewer) || !page) return false;
- if (page.waitingRender && !page.viewer) {
- this.isRenderCache = true;
- this.history.lockHistory = true;
- page.waitingRender = false;
- try {
- await this.loadPageDataFromJson(page);
- const width = page.data.options.width * zoom;
- const height = page.data.options.height * zoom;
- page.canvas.setWidth(width);
- page.canvas.setHeight(height);
- pageElement.style.width = `${width}px`;
- pageElement.style.height = `${height}px`;
- const pageNumber = this.pages.findIndex(p => p.id == page.id);
- $(document).trigger("after_page_render", [[pageNumber], page]);
- } catch (err) {
- page.waitingRender = true;
- console.error(err);
- }
- this.history.lockHistory = false;
- this.isRenderCache = false;
- this.appendWatermarks(page);
- return;
- }
- if (page && page.viewer && page.viewer.document && !page.viewer.isRendered) {
- if (this.queueRender.find(qr => qr.document == page.viewer.document && qr.pageNumber == page.viewer.pageNumber))
- return false;
- if (viewer.now_render) return false;
- this.history.lockHistory = true;
- this.queueRender.push({
- document: page.viewer.document,
- pageNumber: page.viewer.pageNumber
- });
- // if(page.cacheJSON && page.disposed) {
- // console.log('????');
- // await this.loadPageDataFromJson(page.cacheJSON);
- // page.disposed = false;
- // } else {
- try {
- const loaded = await viewer.loadPDFByDocumentId(page.viewer.document);
- if (loaded) {
- const data = await viewer.loadPdfPage(page.viewer.pageNumber);
- const importedData = await this.importPDF.parsePageToJSON(data);
- page.json = importedData.json;
- page.data.options = importedData.data.options;
- const width = importedData.data.options.width * zoom;
- const height = importedData.data.options.height * zoom;
- page.canvas.setWidth(width);
- page.canvas.setHeight(height);
- pageElement.style.width = `${width}px`;
- pageElement.style.height = `${height}px`;
- await this.loadPageDataFromJson(page);
- //this.removeUnnecessaryPages(pageElement);
- this.appendWatermarks(page);
- page.viewer.isRendered = true;
- }
- } catch (err) {
- console.error(err);
- }
- //}
- this.history.lockHistory = false;
- }
- }
- }
Add Comment
Please, Sign In to add comment