PHP Classes

File: toastui/src/js/component/text.js

Recommend this page to a friend!
  Classes of Mark de Leon   PHP Document Scanner using SANE or eSCL AirPrint   toastui/src/js/component/text.js   Download  
File: toastui/src/js/component/text.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: PHP Document Scanner using SANE or eSCL AirPrint
Web interface to scan printed documents
Author: By
Last change:
Date: 4 years ago
Size: 18,915 bytes
 

Contents

Class file image Download
/** * @author NHN Ent. FE Development Team <dl_javascript@nhn.com> * @fileoverview Text module */ import fabric from 'fabric/dist/fabric.require'; import snippet from 'tui-code-snippet'; import Promise from 'core-js/library/es6/promise'; import Component from '../interface/component'; import consts from '../consts'; import util from '../util'; const events = consts.eventNames; const defaultStyles = { fill: '#000000', left: 0, top: 0 }; const resetStyles = { fill: '#000000', fontStyle: 'normal', fontWeight: 'normal', textAlign: 'left', textDecoraiton: '' }; const {browser} = snippet; const TEXTAREA_CLASSNAME = 'tui-image-eidtor-textarea'; const TEXTAREA_STYLES = util.makeStyleText({ position: 'absolute', padding: 0, display: 'none', border: '1px dotted red', overflow: 'hidden', resize: 'none', outline: 'none', 'border-radius': 0, 'background-color': 'transparent', '-webkit-appearance': 'none', 'z-index': 9999, 'white-space': 'pre' }); const EXTRA_PIXEL_LINEHEIGHT = 0.1; const DBCLICK_TIME = 500; /** * Text * @class Text * @param {Graphics} graphics - Graphics instance * @extends {Component} * @ignore */ class Text extends Component { constructor(graphics) { super(consts.componentNames.TEXT, graphics); /** * Default text style * @type {Object} */ this._defaultStyles = defaultStyles; /** * Selected state * @type {boolean} */ this._isSelected = false; /** * Selected text object * @type {Object} */ this._selectedObj = {}; /** * Editing text object * @type {Object} */ this._editingObj = {}; /** * Listeners for fabric event * @type {Object} */ this._listeners = { mousedown: this._onFabricMouseDown.bind(this), select: this._onFabricSelect.bind(this), selectClear: this._onFabricSelectClear.bind(this), scaling: this._onFabricScaling.bind(this) }; /** * Textarea element for editing * @type {HTMLElement} */ this._textarea = null; /** * Ratio of current canvas * @type {number} */ this._ratio = 1; /** * Last click time * @type {Date} */ this._lastClickTime = (new Date()).getTime(); /** * Text object infos before editing * @type {Object} */ this._editingObjInfos = {}; /** * Previous state of editing * @type {boolean} */ this.isPrevEditing = false; /** * use itext * @type {boolean} */ this.useItext = graphics.useItext; } /** * Start input text mode */ start() { const canvas = this.getCanvas(); canvas.selection = false; canvas.defaultCursor = 'text'; canvas.on({ 'mouse:down': this._listeners.mousedown, 'object:selected': this._listeners.select, 'before:selection:cleared': this._listeners.selectClear, 'object:scaling': this._listeners.scaling, 'text:editing': this._listeners.modify }); if (this.useItext) { canvas.forEachObject(obj => { if (obj.type === 'i-text') { obj.set({ left: obj.left - (obj.width / 2), top: obj.top - (obj.height / 2), originX: 'left', originY: 'top' }); } }); } else { this._createTextarea(); } this.setCanvasRatio(); } /** * End input text mode */ end() { const canvas = this.getCanvas(); canvas.selection = true; canvas.defaultCursor = 'default'; if (this.useItext) { canvas.forEachObject(obj => { if (obj.type === 'i-text') { if (obj.text === '') { obj.remove(); } else { obj.set({ left: obj.left + (obj.width / 2), top: obj.top + (obj.height / 2), originX: 'center', originY: 'center' }); } } }); } else { canvas.deactivateAllWithDispatch(); this._removeTextarea(); } canvas.off({ 'mouse:down': this._listeners.mousedown, 'object:selected': this._listeners.select, 'before:selection:cleared': this._listeners.selectClear, 'object:scaling': this._listeners.scaling, 'text:editing': this._listeners.modify }); } /** * Add new text on canvas image * @param {string} text - Initial input text * @param {Object} options - Options for generating text * @param {Object} [options.styles] Initial styles * @param {string} [options.styles.fill] Color * @param {string} [options.styles.fontFamily] Font type for text * @param {number} [options.styles.fontSize] Size * @param {string} [options.styles.fontStyle] Type of inclination (normal / italic) * @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold) * @param {string} [options.styles.textAlign] Type of text align (left / center / right) * @param {string} [options.styles.textDecoraiton] Type of line (underline / line-throgh / overline) * @param {{x: number, y: number}} [options.position] - Initial position * @returns {Promise} */ add(text, options) { return new Promise(resolve => { const canvas = this.getCanvas(); let newText = null; let selectionStyle = consts.fObjectOptions.SELECTION_STYLE; let styles = this._defaultStyles; this._setInitPos(options.position); if (options.styles) { styles = snippet.extend(styles, options.styles); } if (this.useItext) { newText = new fabric.IText(text, styles); selectionStyle = snippet.extend({}, selectionStyle, { originX: 'left', originY: 'top' }); } else { newText = new fabric.Text(text, styles); } newText.set(selectionStyle); newText.on({ mouseup: this._onFabricMouseUp.bind(this) }); canvas.add(newText); if (!canvas.getActiveObject()) { canvas.setActiveObject(newText); } this.isPrevEditing = true; resolve(this.graphics.createObjectProperties(newText)); }); } /** * Change text of activate object on canvas image * @param {Object} activeObj - Current selected text object * @param {string} text - Changed text * @returns {Promise} */ change(activeObj, text) { return new Promise(resolve => { activeObj.set('text', text); this.getCanvas().renderAll(); resolve(); }); } /** * Set style * @param {Object} activeObj - Current selected text object * @param {Object} styleObj - Initial styles * @param {string} [styleObj.fill] Color * @param {string} [styleObj.fontFamily] Font type for text * @param {number} [styleObj.fontSize] Size * @param {string} [styleObj.fontStyle] Type of inclination (normal / italic) * @param {string} [styleObj.fontWeight] Type of thicker or thinner looking (normal / bold) * @param {string} [styleObj.textAlign] Type of text align (left / center / right) * @param {string} [styleObj.textDecoraiton] Type of line (underline / line-throgh / overline) * @returns {Promise} */ setStyle(activeObj, styleObj) { return new Promise(resolve => { snippet.forEach(styleObj, (val, key) => { if (activeObj[key] === val) { styleObj[key] = resetStyles[key] || ''; } }, this); activeObj.set(styleObj); this.getCanvas().renderAll(); resolve(); }); } /** * Get the text * @param {Object} activeObj - Current selected text object * @returns {String} text */ getText(activeObj) { return activeObj.getText(); } /** * Set infos of the current selected object * @param {fabric.Text} obj - Current selected text object * @param {boolean} state - State of selecting */ setSelectedInfo(obj, state) { this._selectedObj = obj; this._isSelected = state; } /** * Whether object is selected or not * @returns {boolean} State of selecting */ isSelected() { return this._isSelected; } /** * Get current selected text object * @returns {fabric.Text} Current selected text object */ getSelectedObj() { return this._selectedObj; } /** * Set ratio value of canvas */ setCanvasRatio() { const canvasElement = this.getCanvasElement(); const cssWidth = parseInt(canvasElement.style.maxWidth, 10); const originWidth = canvasElement.width; const ratio = originWidth / cssWidth; this._ratio = ratio; } /** * Get ratio value of canvas * @returns {number} Ratio value */ getCanvasRatio() { return this._ratio; } /** * Set initial position on canvas image * @param {{x: number, y: number}} [position] - Selected position * @private */ _setInitPos(position) { position = position || this.getCanvasImage().getCenterPoint(); this._defaultStyles.left = position.x; this._defaultStyles.top = position.y; } /** * Create textarea element on canvas container * @private */ _createTextarea() { const container = this.getCanvasElement().parentNode; const textarea = document.createElement('textarea'); textarea.className = TEXTAREA_CLASSNAME; textarea.setAttribute('style', TEXTAREA_STYLES); textarea.setAttribute('wrap', 'off'); container.appendChild(textarea); this._textarea = textarea; this._listeners = snippet.extend(this._listeners, { input: this._onInput.bind(this), keydown: this._onKeyDown.bind(this), blur: this._onBlur.bind(this), scroll: this._onScroll.bind(this) }); if (browser.msie && browser.version === 9) { fabric.util.addListener(textarea, 'keydown', this._listeners.keydown); } else { fabric.util.addListener(textarea, 'input', this._listeners.input); } fabric.util.addListener(textarea, 'blur', this._listeners.blur); fabric.util.addListener(textarea, 'scroll', this._listeners.scroll); } /** * Remove textarea element on canvas container * @private */ _removeTextarea() { const container = this.getCanvasElement().parentNode; const textarea = container.querySelector('textarea'); container.removeChild(textarea); this._textarea = null; if (browser.msie && browser.version < 10) { fabric.util.removeListener(textarea, 'keydown', this._listeners.keydown); } else { fabric.util.removeListener(textarea, 'input', this._listeners.input); } fabric.util.removeListener(textarea, 'blur', this._listeners.blur); fabric.util.removeListener(textarea, 'scroll', this._listeners.scroll); } /** * Input event handler * @private */ _onInput() { const ratio = this.getCanvasRatio(); const obj = this._editingObj; const textareaStyle = this._textarea.style; textareaStyle.width = `${Math.ceil(obj.getWidth() / ratio)}px`; textareaStyle.height = `${Math.ceil(obj.getHeight() / ratio)}px`; } /** * Keydown event handler * @private */ _onKeyDown() { const ratio = this.getCanvasRatio(); const obj = this._editingObj; const textareaStyle = this._textarea.style; setTimeout(() => { obj.setText(this._textarea.value); textareaStyle.width = `${Math.ceil(obj.getWidth() / ratio)}px`; textareaStyle.height = `${Math.ceil(obj.getHeight() / ratio)}px`; }, 0); } /** * Blur event handler * @private */ _onBlur() { const ratio = this.getCanvasRatio(); const editingObj = this._editingObj; const editingObjInfos = this._editingObjInfos; const textContent = this._textarea.value; let transWidth = (editingObj.getWidth() / ratio) - (editingObjInfos.width / ratio); let transHeight = (editingObj.getHeight() / ratio) - (editingObjInfos.height / ratio); if (ratio === 1) { transWidth /= 2; transHeight /= 2; } this._textarea.style.display = 'none'; editingObj.set({ left: editingObjInfos.left + transWidth, top: editingObjInfos.top + transHeight }); if (textContent.length) { this.getCanvas().add(editingObj); const params = { id: snippet.stamp(editingObj), type: editingObj.type, text: textContent }; this.fire(events.TEXT_CHANGED, params); } } /** * Scroll event handler * @private */ _onScroll() { this._textarea.scrollLeft = 0; this._textarea.scrollTop = 0; } /** * Fabric scaling event handler * @param {fabric.Event} fEvent - Current scaling event on selected object * @private */ _onFabricScaling(fEvent) { const obj = fEvent.target; const scalingSize = obj.getFontSize() * obj.getScaleY(); obj.setFontSize(scalingSize); obj.setScaleX(1); obj.setScaleY(1); } /** * onSelectClear handler in fabric canvas * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onFabricSelectClear(fEvent) { const obj = this.getSelectedObj(); this.isPrevEditing = true; this.setSelectedInfo(fEvent.target, false); if (obj) { // obj is empty object at initial time, will be set fabric object if (obj.text === '') { obj.remove(); } } } /** * onSelect handler in fabric canvas * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onFabricSelect(fEvent) { this.isPrevEditing = true; this.setSelectedInfo(fEvent.target, true); } /** * Fabric 'mousedown' event handler * @param {fabric.Event} fEvent - Current mousedown event on selected object * @private */ _onFabricMouseDown(fEvent) { const obj = fEvent.target; if (obj && !obj.isType('text')) { return; } if (this.isPrevEditing) { this.isPrevEditing = false; return; } this._fireAddText(fEvent); } /** * Fire 'addText' event if object is not selected. * @param {fabric.Event} fEvent - Current mousedown event on selected object * @private */ _fireAddText(fEvent) { const obj = fEvent.target; const e = fEvent.e || {}; const originPointer = this.getCanvas().getPointer(e); if (!obj) { this.fire(events.ADD_TEXT, { originPosition: { x: originPointer.x, y: originPointer.y }, clientPosition: { x: e.clientX || 0, y: e.clientY || 0 } }); } } /** * Fabric mouseup event handler * @param {fabric.Event} fEvent - Current mousedown event on selected object * @private */ _onFabricMouseUp(fEvent) { const newClickTime = (new Date()).getTime(); if (this._isDoubleClick(newClickTime)) { if (!this.useItext) { this._changeToEditingMode(fEvent.target); } this.fire(events.TEXT_EDITING); // fire editing text event } this._lastClickTime = newClickTime; } /** * Get state of firing double click event * @param {Date} newClickTime - Current clicked time * @returns {boolean} Whether double clicked or not * @private */ _isDoubleClick(newClickTime) { return (newClickTime - this._lastClickTime < DBCLICK_TIME); } /** * Change state of text object for editing * @param {fabric.Text} obj - Text object fired event * @private */ _changeToEditingMode(obj) { const ratio = this.getCanvasRatio(); const textareaStyle = this._textarea.style; this.isPrevEditing = true; obj.remove(); this._editingObj = obj; this._textarea.value = obj.getText(); this._editingObjInfos = { left: this._editingObj.getLeft(), top: this._editingObj.getTop(), width: this._editingObj.getWidth(), height: this._editingObj.getHeight() }; textareaStyle.display = 'block'; textareaStyle.left = `${obj.oCoords.tl.x / ratio}px`; textareaStyle.top = `${obj.oCoords.tl.y / ratio}px`; textareaStyle.width = `${Math.ceil(obj.getWidth() / ratio)}px`; textareaStyle.height = `${Math.ceil(obj.getHeight() / ratio)}px`; textareaStyle.transform = `rotate(${obj.getAngle()}deg)`; textareaStyle.color = obj.getFill(); textareaStyle['font-size'] = `${obj.getFontSize() / ratio}px`; textareaStyle['font-family'] = obj.getFontFamily(); textareaStyle['font-style'] = obj.getFontStyle(); textareaStyle['font-weight'] = obj.getFontWeight(); textareaStyle['text-align'] = obj.getTextAlign(); textareaStyle['line-height'] = obj.getLineHeight() + EXTRA_PIXEL_LINEHEIGHT; textareaStyle['transform-origin'] = 'left top'; this._textarea.focus(); } } module.exports = Text;