diff --git a/niimblue/src/fabric-object/barcode.class.ts b/niimblue/src/fabric-object/barcode.class.ts
new file mode 100644
index 0000000..ceaf906
--- /dev/null
+++ b/niimblue/src/fabric-object/barcode.class.ts
@@ -0,0 +1,134 @@
+import { fabric } from 'fabric'
+import { code128b, ean13 } from '../utils/barcode'
+import { equalSpacingFillText } from '../utils/canvas'
+
+const PRESERVE_PROPERTIES = ['text', 'encoding', 'printText']
+const EAN13_LONG_IDX: number[] = [0, 1, 2, 45, 46, 47, 48, 49, 92, 93, 94]
+export type BarcodeCoding = 'EAN13' | 'CODE128B'
+export interface IBarcodeOptions extends fabric.IObjectOptions {
+ text?: string
+ encoding?: BarcodeCoding
+ printText?: boolean
+}
+
+export interface Barcode extends fabric.Object {
+ text: string
+ encoding: BarcodeCoding
+ printText: boolean
+ constructor(options?: IBarcodeOptions): void
+ initialize(options?: IBarcodeOptions): Barcode
+}
+
+export const Barcode = fabric.util.createClass(fabric.Object, {
+ type: 'Barcode',
+ stateProperties: PRESERVE_PROPERTIES.concat(...(fabric.Object.prototype.stateProperties ?? [])),
+ cacheProperties: PRESERVE_PROPERTIES.concat(...(fabric.Object.prototype.cacheProperties ?? [])),
+ bandcode: '',
+
+ /**
+ * QRCode text
+ * @var {string}
+ */
+ text: '',
+ /**
+ * Encoding
+ * @type {BarcodeCoding}
+ */
+ encoding: 'EAN13',
+ /**
+ * Print text
+ * @type {boolean}
+ */
+ printText: true,
+
+ initialize(options = {}) {
+ this.callSuper('initialize', options);
+ this._createBandCode();
+ },
+
+ _set(key: any, value: any) {
+ this.callSuper('_set', key, value);
+ switch (key) {
+ case 'text':
+ case 'encoding':
+ this._createBandCode();
+ break;
+ }
+ return this
+ },
+
+ _createBandCode() {
+ if (this.encoding === 'EAN13') {
+ const { text, bandcode } = ean13(this.text)
+ this.text = text
+ this.bandcode = bandcode
+ } else {
+ this.bandcode = code128b(this.text)
+ }
+ return this
+ },
+
+ _render(ctx: CanvasRenderingContext2D) {
+ if (this.bandcode === '') return
+
+ ctx.save();
+
+ ctx.fillStyle = this.fill;
+ const fontHeight = Math.round(Math.max(Math.min(this.height / 10, this.width / this.text.length * 1.5), 12))
+ ctx.font = `bold ${fontHeight}px Courier New`
+ ctx.textBaseline = 'top'
+ const fontWidth = ctx.measureText('0').width
+ const w2 = this.width / 2, h2 = this.height / 2
+ const realX = (x: number) => x - w2
+ const realY = (y: number) => y - h2
+
+ // render bandcode
+ let dh = this.height, dp = 0, dw = this.width / this.bandcode.length
+ if (this.printText) {
+ if (this.encoding === 'EAN13') {
+ dp = 2 * fontWidth
+ }
+ dh = this.height - fontHeight * 1.2
+ dw = (this.width - dp * 2) / this.bandcode.length
+ }
+ for (let i = 0; i < this.bandcode.length; i++) {
+ const d = this.bandcode[i]
+ if (d === '1') {
+ if (this.encoding === 'EAN13' && EAN13_LONG_IDX.includes(i))
+ ctx.fillRect(realX(dp + i * dw), realY(0), dw, dh + fontHeight * 0.7)
+ else
+ ctx.fillRect(realX(dp + i * dw), realY(0), dw, dh)
+ }
+ }
+
+ // render text
+ if (this.printText) {
+ const fastY = realY(this.height - fontHeight)
+ if (this.encoding === 'EAN13') {
+ let partW = 41 * dw
+ ctx.fillText(this.text[0], realX(fontWidth * 0.5), fastY) // first digit
+ equalSpacingFillText(ctx, this.text.slice(1, 7), realX(fontWidth * 2 + dw * 4), fastY, partW) // part 1
+ equalSpacingFillText(ctx, this.text.slice(7, 13), realX(fontWidth * 2 + dw * 9 + partW), fastY, partW) // part 2
+ ctx.fillText('>', realX(this.width - fontWidth * 1.5), fastY) // last digit
+ } else {
+ equalSpacingFillText(ctx, this.text, realX(0), fastY, this.width)
+ }
+ }
+
+ ctx.restore();
+ },
+
+ toObject(propertiesToInclude = []) {
+ return this.callSuper('toObject', PRESERVE_PROPERTIES.concat(...propertiesToInclude));
+ },
+})
+Barcode.ATTRIBUTE_NAMES = [];
+Barcode.fromElement = () => { };
+Barcode.fromObject = (object: any, callback: any) => {
+ return fabric.Object._fromObject('Barcode', object, callback);
+};
+
+// @ts-ignore
+fabric.Barcode = Barcode
+
+export default Barcode
\ No newline at end of file
diff --git a/niimblue/src/image_editor_utils.ts b/niimblue/src/image_editor_utils.ts
index 81c82fa..b10fa2e 100644
--- a/niimblue/src/image_editor_utils.ts
+++ b/niimblue/src/image_editor_utils.ts
@@ -1,5 +1,6 @@
import { fabric } from "fabric";
import { QRCode } from "./fabric-object/qrcode.class";
+import Barcode from "./fabric-object/barcode.class";
export class ImageEditorUtils {
static readonly SIZE_DEFAULT: number = 64;
@@ -134,4 +135,14 @@ export class ImageEditorUtils {
});
canvas.add(qr);
}
-}
+
+ public static addBarcode(canvas: fabric.Canvas): void {
+ const barcode = new Barcode({
+ text: "1234567890128",
+ width: ImageEditorUtils.SIZE_DEFAULT * 2,
+ height: ImageEditorUtils.SIZE_DEFAULT,
+ encoding: "EAN13",
+ })
+ canvas.add(barcode);
+ }
+}
\ No newline at end of file
diff --git a/niimblue/src/lib/BarcodeParamsControls.svelte b/niimblue/src/lib/BarcodeParamsControls.svelte
new file mode 100644
index 0000000..11f3830
--- /dev/null
+++ b/niimblue/src/lib/BarcodeParamsControls.svelte
@@ -0,0 +1,75 @@
+
+
+{#if selectedBarcode}
+
+
+
+
+
+
+{/if}
+
+
diff --git a/niimblue/src/lib/ImageEditor.svelte b/niimblue/src/lib/ImageEditor.svelte
index 746ce1b..6eadea8 100644
--- a/niimblue/src/lib/ImageEditor.svelte
+++ b/niimblue/src/lib/ImageEditor.svelte
@@ -13,6 +13,8 @@
import { ImageEditorUtils } from "../image_editor_utils";
import { QRCode } from "../fabric-object/qrcode.class";
import QrCodeParamsPanel from "./QRCodeParamsControls.svelte";
+ import { Barcode } from "../fabric-object/barcode.class";
+ import BarcodeParamsPanel from './BarcodeParamsControls.svelte';
let htmlCanvas: HTMLCanvasElement;
let fabricCanvas: fabric.Canvas;
@@ -153,6 +155,8 @@
ImageEditorUtils.addImageWithFilePicker(fabricCanvas);
} else if (name === "qrcode") {
ImageEditorUtils.addQrCode(fabricCanvas);
+ } else if (name === 'barcode') {
+ ImageEditorUtils.addBarcode(fabricCanvas);
}
};
@@ -241,6 +245,18 @@
});
}
});
+
+ fabricCanvas.on('object:scaling', (e) => {
+ const grid = 5;
+ if (e.target && e.target instanceof Barcode && e.target.width !== undefined && e.target.height !== undefined) {
+ e.target.set({
+ width: Math.round(e.target.width * (e.target.scaleX ?? 1)),
+ height: Math.round(e.target.height * (e.target.scaleY ?? 1)),
+ scaleX: 1,
+ scaleY: 1,
+ });
+ }
+ })
});
onDestroy(() => {
@@ -307,6 +323,9 @@
{#if selectedObject instanceof QRCode}
fabricCanvas.requestRenderAll()} />
{/if}
+ {#if selectedObject instanceof Barcode}
+ fabricCanvas.requestRenderAll()} />
+ {/if}
diff --git a/niimblue/src/lib/ObjectPicker.svelte b/niimblue/src/lib/ObjectPicker.svelte
index adc2e1c..40cb9a8 100644
--- a/niimblue/src/lib/ObjectPicker.svelte
+++ b/niimblue/src/lib/ObjectPicker.svelte
@@ -30,9 +30,12 @@
-