UNPKG

@syncfusion/ej2-barcode-generator

Version:

Barcode generator component is a pure JavaScript library which will convert a string to Barcode and show it to the user. This supports major 1D and 2D barcodes including coda bar, code 128, QR Code.

476 lines (448 loc) 27.1 kB
import { BarcodeBase } from './barcode-base'; import { BarcodeRenderer } from './rendering/renderer'; import { BaseAttributes } from './rendering/canvas-interface'; import { Rect } from './primitives/rect'; import { createMeasureElements, measureText } from './utility/dom-util'; import { Size } from './primitives/size'; import { DisplayTextModel } from './primitives/displaytext-model'; import { MarginModel } from './primitives/margin-model'; import { BarcodeType } from './enum/enum'; /** * onedimension class is used to render all type of one dimensional shapes */ export abstract class OneDimension extends BarcodeBase { private getInstance(id: string): BarcodeRenderer { const barCode: HTMLElement = document.getElementById(id); const barcodeRenderer: BarcodeRenderer = new BarcodeRenderer(barCode.id, this.isSvgMode); return barcodeRenderer; } /** * Return the drawable size of the rectangle . * * @returns {Rect} Return the drawable size of the rectangle. * @param {MarginModel} margin - Specifies the filename of the barcode image to be download. * @param {number} w - Specifies the filename of the barcode image to be download. * @param {number} h - Defines the format of the barcode to be exported * @private */ public getDrawableSize(margin: MarginModel, w: number, h: number): Rect { const topMargin: number = ((this.isSvgMode ? margin.bottom : margin.bottom * 1.5) + (this.isSvgMode ? margin.top : margin.top * 1.5)); const rightMargin: number = ((this.isSvgMode ? margin.right : margin.right * 1.5) + (this.isSvgMode ? margin.left : margin.left * 1.5)); const barcodeSize: Rect = new Rect(margin.left, margin.top, (w - rightMargin), h - topMargin); return barcodeSize; } private getBaseAttributes( width: number, height: number, offSetX: number, offsetY: number, color: string, string?: string, stringSize?: number, visibility?: boolean, fontStyle?: string): BaseAttributes { const options: BaseAttributes = { width: width, height: height, x: offSetX, y: offsetY, color: color, string: string, stringSize: stringSize, visibility: visibility, fontStyle: fontStyle }; if (!this.isSvgMode) { options.height = options.height / 1.5; } if (string && !this.isSvgMode) { const scaleValue: number = this.margin.bottom * 1.5 - this.margin.bottom; options.y += scaleValue; } return options; } private getBarLineRatio(code: string[] | string, widthValue: number): number { const type: string = this.type; if (type === 'Code39' || type === 'Code32' || type === 'Code39Extension' || type === 'Code11') { // total number of line for single width lines const singlewidth: number = code.length * ((type === 'Code39' || type === 'Code32' || type === 'Code39Extension') ? 6 : 3); // total number of line for double width lines const doublwidth: number = code.length * ((type === 'Code39' || type === 'Code32' || type === 'Code39Extension') ? 3 : 2) * 2; return (widthValue / (doublwidth + singlewidth + code.length - 1)); } else if (type === 'Code128A' || type === 'Code128B' || type === 'Code128C' || type === 'Code128') { const lineCount: number = (code[0] as string).length; return (widthValue / (lineCount + code.length - 1)); } else if (type === 'Code93Extension') { let count: number = 0; for (let i: number = 0; i < code.length; i++) { const numberOfDigits: string = code[parseInt(i.toString(), 10)]; for (let j: number = 0; j < numberOfDigits.length; j++) { count += Number(numberOfDigits[parseInt(j.toString(), 10)]); } } return widthValue / count; } else { let lineCount: number = 0; for (let i: number = 0; i < code.length; i++) { const numberOfDigits: number = code[parseInt(i.toString(), 10)].length; lineCount += numberOfDigits; } let additionalValue: number; if (type === 'Ean8' || type === 'Ean13' || type === 'UpcA') { additionalValue = 2; } else if (type === 'Code93') { additionalValue = -code.length + 1; } return (widthValue / (additionalValue ? ((lineCount + code.length - 1) + additionalValue) : (lineCount + code.length - 1))); } } private multipleWidth(codeValue: string, k: number, value: number): number { let number: number; if (codeValue[parseInt(k.toString(), 10)] === '1' && codeValue[k + 1] === '1') { number = value + 1; return this.multipleWidth(codeValue, k + 1, number); } return value; } private barCodeType(type: BarcodeType): string { if (type === 'Code39' || type === 'UpcE' || type === 'Code39Extension') { return 'twoBars'; } else if (type === 'UpcA' || type === 'Ean13' || type === 'Ean8') { return 'threeBars'; } else { return 'noBars'; } } private checkStartValueCondition(j: number, k: number, numberOfDigits: number, barType: string): boolean { if ((j === 1 && k === 0 && barType === 'twoBars' && this.type !== 'UpcE' || (((j === 0 && k === numberOfDigits - 1) || j === 2 && k === numberOfDigits - 2) && (this.type === 'Ean8' || this.type === 'Ean13'))) || (this.type === 'UpcE' && j === 2 && k === 0) || (this.type !== 'UpcA' && barType === 'threeBars' && (j === 2 && k === numberOfDigits - 1)) || this.type === 'UpcA' && ((j === 1 && k === numberOfDigits - 2) || (j === 3 && k === numberOfDigits - 2)) || (barType === 'noBars' && j === 0 && k === 0)) { return true; } else { return false; } } private checkEndValueCondition( k: number, j: number, numberOfDigits: number, code: number[] | string[], temp?: boolean, doublwidth?: number): boolean { const type: string = this.type; if ((k === numberOfDigits && j === code.length - 2 && (type === 'Code39' || type === 'Code39Extension')) || (type === 'Code11' && j === code.length - 1 && k === numberOfDigits - 1) || type === 'Code93Extension' && j === code.length - 1 && k === numberOfDigits - 1 || ((type === 'Ean8') && (j === 1 && k === numberOfDigits || j === code.length - 2 && k === numberOfDigits)) || ((this.type === 'Ean13') && ((j === 2 && k === 1) || j === code.length - 2 && k === numberOfDigits)) || (type === 'UpcA' && (j === 3 && k === 0 || j === 5 && (!temp ? (k === 1) : ((k === (doublwidth)))))) || (type === 'UpcE' && (j === code.length - 2 && k === 1)) || (type === 'Code93' && j === code.length - 1 && k === numberOfDigits - 1) || ((type !== 'Code39' && type !== 'Code39Extension' && type !== 'Ean8' && type !== 'Ean13') && j === code.length - 1 && k === numberOfDigits)) { return true; } else { return false; } } private getDisplayText(j: number, textProperty: DisplayTextModel): string { let text: string; if (this.type === 'Ean8') { text = j === 1 ? (this.value.substring(0, 4)) : (this.value.substring(4, 8)); } else if (this.type === 'Ean13') { text = j === 2 ? (this.value.substring(1, 7)) : (this.value.substring(7)); } else if (this.type === 'UpcA') { text = j === 3 ? ((this.value.substring(0, 6))) : (this.value.substring(6, 12)); } else { text = textProperty.text ? textProperty.text : this.value; } return text; } private checkExtraHeight(j: number, type: string, code: number[] | string[]): boolean { if (((j === 0 || j === code.length - 1) && (type === 'Code39' || type === 'Code39Extension')) || ((type === 'Ean8' || type === 'Ean13') && (j === 0 || j === 2 || j === code.length - 1)) || type === 'UpcA' && (j === 1 || j === code.length - 2 || j === code.length - 4) || type === 'UpcE' && (j === 1 || j === code.length - 2 || j === code.length - 4)) { return true; } else { return false; } } private getWidthValue(number: number, width: number, type: string): number { if (this.type !== 'Code93Extension') { if (number) { const dividerValue: number = type === 'Code32' ? 3 : 2; width = number % dividerValue ? 1 : 2; } else { width = 1; } } if (this.type === 'Code93Extension') { if (number && !(number % 4)) { width = 4; } else if (number && !(number % 2)) { width = 2; } else if (number && !(number % 3)) { width = 3; } else { width = 1; } } return width; } /* eslint:disable */ /** * Returns the module name of the barcode * * @param {number[] | string[]} code - Returns the code as string or number collection. * @param {HTMLElement} canvas - Returns the canvas. * @param {string} isUpcE - Returns the UPCE values as string. * @returns {void} Calculate the barcode attribute * @private */ public calculateBarCodeAttributes(code: number[] | string[], canvas: HTMLElement, isUpcE?: string): void { let temp: boolean = false; let canDoubleWidth: number; const barcodeSize: Rect = this.getDrawableSize(this.margin, this.width as number, this.height as number); if (barcodeSize.height > 0 && barcodeSize.width > 0) { let tempBaseAttributes: BaseAttributes; const options: BaseAttributes[] = []; let offsetX: number = barcodeSize.x; let ratio: number = this.getBarLineRatio(code as string[], barcodeSize.width); ratio = this.isSvgMode ? ratio : ratio / 1.5; let startValue: number = 0; let endValue: number; const type: string = this.type; const position: string = this.displayText.position; const scaleValue: number = this.isSvgMode ? 1 : 1.5; let textOptions: BaseAttributes; let textSize: Size; let textHeight: number; let textProperty: DisplayTextModel; for (let j: number = 0; j < code.length; j++) { const codeValue: string = code[parseInt(j.toString(), 10)] as string; const check: boolean = (type !== 'UpcA' && type !== 'UpcE' && type !== 'Code11' && type !== 'Code93' && type !== 'Code93Extension'); const barType: string = this.barCodeType(this.type); const extraHeight: boolean = this.checkExtraHeight(j, type, code); const numberOfDigits: number = codeValue.length; temp = false; for (let k: number = 0; check ? k <= numberOfDigits : k < numberOfDigits; k++) { let renderText: boolean = false; if (this.checkStartValueCondition(j, k, numberOfDigits, barType)) { startValue = offsetX; } else if (this.checkEndValueCondition(k, j, numberOfDigits, code, temp, canDoubleWidth)) { endValue = offsetX; if (this.type === 'UpcA' && temp && canDoubleWidth) { endValue -= canDoubleWidth * ratio; } renderText = true; } const canDrawCheck: boolean = (type === 'Code39' || type === 'Code93Extension' || type === 'Code32' || type === 'Code11' || type === 'Code39Extension'); const candraw: boolean = canDrawCheck ? (k % 2 ? false : true) : (codeValue[parseInt(k.toString(), 10)] === '1' ? true : false); const string: string = codeValue.toString(); const number: number = Number(string[parseInt(k.toString(), 10)]); let width: number; width = this.getWidthValue(number, width, type); width = width * ratio; textProperty = this.displayText; const text: string = this.getDisplayText(j, textProperty); textOptions = this.getBaseAttributes( undefined, undefined, startValue, position === 'Bottom' ? (barcodeSize.y + barcodeSize.height) + 2 : (barcodeSize.y + textHeight) - 2, this.foreColor, isUpcE || text, textProperty.size, textProperty.visibility, textProperty.font); if (!textHeight) { createMeasureElements(); textSize = measureText(textOptions); textHeight = (textSize.height / 2) + 2; } if (extraHeight) { tempBaseAttributes = this.getBaseAttributes( width, position === 'Top' && barType !== 'noBars' ? (barcodeSize.height - textHeight - this.displayText.margin.top) : (barcodeSize.height), offsetX, position === 'Bottom' ? barcodeSize.y : barcodeSize.y + textHeight + this.displayText.margin.top, this.foreColor); } if ((type === 'Ean13') && k === 0 && j === 0 && textProperty.visibility) { textOptions = this.getBaseAttributes( undefined, undefined, startValue, position === 'Bottom' ? (barcodeSize.y + barcodeSize.height) + 2 : ((barcodeSize.y + textHeight + this.displayText.margin.top) - 2) - this.displayText.margin.bottom, this.foreColor, isUpcE || text, textProperty.size, textProperty.visibility, textProperty.font); textOptions.string = this.value[0]; this.drawText(canvas as HTMLCanvasElement, textOptions); } if (!extraHeight || renderText || (type === 'UpcA' && extraHeight)) { const checkCode: boolean = type === 'Code39' || type === 'Code32' || type === 'Code93Extension' || type === 'Code39Extension' || type === 'Code11'; const value: number = barcodeSize.height; let barCodeHeight: number = (((value) - textHeight * scaleValue) > 0 ? ((value) - textHeight * scaleValue) : 0); if (checkCode || type === 'Ean8' || type === 'Ean13') { barCodeHeight = position === 'Top' && barType !== 'noBars' ? (barCodeHeight - textHeight) : barCodeHeight; let height: number = extraHeight ? barcodeSize.height : barCodeHeight; if (this.type !== 'Code39') { height = position === 'Top' && barType !== 'noBars' ? (height - this.displayText.margin.top) - textHeight : height; } tempBaseAttributes = this.getBaseAttributes( width, height, offsetX, position === 'Bottom' ? barcodeSize.y : barcodeSize.y + textHeight + this.displayText.margin.top, this.foreColor); } if ((!checkCode || (!renderText && !checkCode)) && (!renderText || this.type !== 'UpcE')) { canDoubleWidth = this.multipleWidth((codeValue), k, 1); k += canDoubleWidth - 1; if (canDoubleWidth > 1) { temp = true; } const rectWidth: number = canDoubleWidth > 1 ? (canDoubleWidth * width) : width; const rectHeight: number = (barcodeSize.height - textHeight * scaleValue); let height: number = extraHeight ? barcodeSize.height : rectHeight; height = position === 'Top' && barType !== 'noBars' ? (height - this.displayText.margin.top) - textHeight : height; tempBaseAttributes = this.getBaseAttributes( rectWidth, height, offsetX, position === 'Bottom' ? barcodeSize.y : barcodeSize.y + textHeight + this.displayText.margin.top, this.foreColor); offsetX = canDoubleWidth > 1 ? offsetX + (canDoubleWidth * (width)) : offsetX + (1 * (width)); } if (renderText || !extraHeight) { this.verticalTextMargin(textProperty, tempBaseAttributes, textOptions); } if (textProperty.visibility && ((endValue && type !== 'Ean8' && type !== 'Ean13' && type !== 'UpcA' && type !== 'UpcE') || ((type === 'Ean8' || type === 'UpcA' || type === 'UpcE' || type === 'Ean13') && renderText))) { if (!textProperty.margin.left && !textProperty.margin.right && (textProperty.text || type === 'UpcA')) { this.updateOverlappedTextPosition( (endValue - startValue), textOptions, textSize, startValue, textProperty, endValue); } else { this.getAlignmentPosition(textOptions, endValue, startValue, textSize); } if (type === 'UpcA') { const checkVAl: boolean = (textOptions.string === this.value.substr(0, 6)) ? true : false; textOptions.string = checkVAl ? this.value.substr(0, 1) : textOptions.string.substr(0, 5); const xPosition: number = checkVAl ? options[0].x / 2 : options[options.length - 1].x + textOptions.stringSize; let yPosition: number = 0; if (checkVAl) { const tempPosition: number = textOptions.x; textOptions.x = xPosition; yPosition = textOptions.y; this.drawText(canvas as HTMLCanvasElement, textOptions); textOptions.x = tempPosition; if (!this.isSvgMode) { textOptions.y = yPosition; } textOptions.string = this.value.substr(1, 5); this.updateOverlappedTextPosition((endValue - startValue), textOptions, textSize, startValue, textProperty, endValue); } else { this.updateOverlappedTextPosition((endValue - startValue), textOptions, textSize, startValue, textProperty, endValue); yPosition = textOptions.y; this.drawText(canvas as HTMLCanvasElement, textOptions); if (!this.isSvgMode) { textOptions.y = yPosition; } textOptions.string = this.value.substr(11, 12); textOptions.x = xPosition; } } this.alignDisplayText(textOptions, textProperty, startValue, endValue, textSize); this.drawText(canvas as HTMLCanvasElement, textOptions); } } if (candraw) { options.push(tempBaseAttributes); } if (this.canIncrementCheck(type, j, code)) { offsetX += (width); } } } this.drawImage(canvas as HTMLCanvasElement, options); } } /* eslint:enable */ private canIncrementCheck(type: string, j: number, code: number[] | string[]): boolean { if ((type === 'Code39' || type === 'Code32' || type === 'Code39Extension' || type === 'Code93Extension' || type === 'Code11') || (type === 'UpcE' && (j === 1 || j === code.length - 2)) || ((type === 'Ean8' || type === 'Ean13') && (j === 0 || j === code.length - 1 || j === 2))) { return true; } else { return false; } } private verticalTextMargin(textProperty: DisplayTextModel, tempBaseAttributes: BaseAttributes, textOptions: BaseAttributes): void { if (textProperty.margin.top && tempBaseAttributes.height - textProperty.margin.top > 0) { if (textProperty.margin.top > 0 && textProperty.position === 'Bottom') { tempBaseAttributes.height -= textProperty.margin.top; } else { textOptions.y += textProperty.margin.top; } } if (textProperty.margin.bottom && tempBaseAttributes.height - textProperty.margin.bottom > 0) { if (textProperty.margin.bottom > 0) { textOptions.y -= textProperty.margin.bottom; if (this.displayText.position === 'Bottom') { tempBaseAttributes.height -= textProperty.margin.bottom; } } else { textOptions.y -= textProperty.margin.bottom; } } } private getAlignmentPosition(textOptions: BaseAttributes, endValue: number, startValue: number, textSize: Size): void { if (this.displayText.alignment === 'Center') { textOptions.x += (((endValue - startValue)) / 2) - textSize.width * .5; } else if (this.displayText.alignment === 'Left') { textOptions.x = startValue; } else { textOptions.x = endValue - textSize.width; } } /** *Will draw the image for the barcode . * * @param {HTMLCanvasElement} canvas Barcode canvas element. * @param {BaseAttributes []} options Barcode attributes . * @function drawImage * @returns {void} Export the barcode as an image in the specified image type and downloads it in the browser. * @memberof Barcode * @private */ public drawImage( canvas: HTMLCanvasElement, options: BaseAttributes[]) : void { const barcodeRenderer: BarcodeRenderer = this.getInstance(canvas.id); for (let i: number = 0; i < options.length; i++) { barcodeRenderer.renderRectElement(canvas as HTMLCanvasElement, options[parseInt(i.toString(), 10)]); } } private updateDisplayTextSize( options: BaseAttributes, size: Size, endValue: number, startValue: number, textProperty: DisplayTextModel): void { if (options.x + size.width > endValue || (options.x < startValue) && options.stringSize > 2) { // eslint-disable-next-line const rightAlign: boolean = options.x < startValue && textProperty.margin.right ? true : false; if (options.x < startValue && textProperty.margin.right) { // if the displaytext rendering overlaps the barcode then need to reduce the displaytext size gradually by 2 options.stringSize -= 2; const newSize: Size = measureText(options); // used to get the middle value for the text as well the total barcode size options.x += (((endValue - startValue)) / 2) - newSize.width * .5; const diff: number = textProperty.margin.right - (endValue - (options.x + size.width)); options.x -= diff; this.updateDisplayTextSize(options, newSize, endValue, startValue, textProperty); } } } private alignDisplayText( options: BaseAttributes, textProperty: DisplayTextModel, startValue: number, endValue: number, size: Size): void { let leftMargin: boolean = false; // have to adjust the displaytext position during the rendering of default displaytext size if ((textProperty.margin.left || textProperty.margin.right)) { if (options.x - startValue < textProperty.margin.left && textProperty.margin.left) { leftMargin = true; const diff: number = textProperty.margin.left - (options.x - startValue); options.x += diff; this.updateDisplayTextSize(options, size, endValue, startValue, textProperty); } if ((endValue - (options.x + size.width) < textProperty.margin.right) && textProperty.margin.right && !leftMargin) { const diff: number = textProperty.margin.right - (endValue - (options.x + size.width)); options.x -= diff; this.updateDisplayTextSize(options, size, endValue, startValue, textProperty); } else if ((endValue - (options.x + size.width) < textProperty.margin.right)) { const newSize: Size = measureText(options); this.updateOverlappedTextPosition((endValue - startValue), options, newSize, startValue, textProperty, endValue); this.updateDisplayTextSize(options, newSize, endValue, startValue, textProperty); } } } private updateOverlappedTextPosition( width: number, options: BaseAttributes, size: Size, startValue: number, textProperty?: DisplayTextModel, endValue?: number): void { if ((size.width > width || textProperty) && (endValue - (options.x + size.width) <= textProperty.margin.right) && options.stringSize > 2) { options.stringSize -= !textProperty ? 2 : .2; const newSize: Size = measureText(options); this.updateOverlappedTextPosition(width, options, newSize, startValue, textProperty, endValue); } else if (!textProperty.margin.left && !textProperty.margin.right && options.stringSize > 2) { this.getAlignmentPosition(options, endValue, startValue, size); } } private drawText(canvas: HTMLCanvasElement, options: BaseAttributes): void { if (!this.isSvgMode) { options.y /= 1.5; } const barcodeRenderer: BarcodeRenderer = this.getInstance(canvas.id); barcodeRenderer.renderTextElement(canvas as HTMLCanvasElement, options); } }