Working tree changes 2024-07-30 01:00
All checks were successful
Test project build / Build (push) Successful in 1m10s
All checks were successful
Test project build / Build (push) Successful in 1m10s
This commit is contained in:
parent
17364b48be
commit
319a33084e
112
niimblue/src/image_editor_utils.ts
Normal file
112
niimblue/src/image_editor_utils.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { fabric } from "fabric";
|
||||||
|
|
||||||
|
export class ImageEditorUtils {
|
||||||
|
static readonly SIZE_DEFAULT: number = 64;
|
||||||
|
static readonly OBJECT_DEFAULTS = {
|
||||||
|
fill: "black",
|
||||||
|
snapAngle: 10,
|
||||||
|
top: 10,
|
||||||
|
left: 10,
|
||||||
|
// objectCaching: false
|
||||||
|
strokeUniform: true,
|
||||||
|
// noScaleCache: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static addSvg(canvas: fabric.Canvas, svgCode: string): void {
|
||||||
|
fabric.loadSVGFromString(svgCode, (objects, options) => {
|
||||||
|
const obj = fabric.util.groupSVGElements(objects, options);
|
||||||
|
|
||||||
|
obj.scaleToWidth(this.SIZE_DEFAULT).scaleToHeight(this.SIZE_DEFAULT);
|
||||||
|
obj.set({ ...this.OBJECT_DEFAULTS, top: 0, left: 0 });
|
||||||
|
|
||||||
|
canvas.add(obj).renderAll();
|
||||||
|
canvas.renderAll();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addImageFile(canvas: fabric.Canvas, file: File) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
if (file.type.startsWith("image/svg")) {
|
||||||
|
reader.readAsText(file, "UTF-8");
|
||||||
|
reader.onload = (readerEvt: ProgressEvent<FileReader>) => {
|
||||||
|
if (readerEvt?.target?.result) {
|
||||||
|
this.addSvg(canvas, readerEvt.target.result as string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = (readerEvt: ProgressEvent<FileReader>) => {
|
||||||
|
console.error(readerEvt);
|
||||||
|
};
|
||||||
|
} else if (file.type === "image/png" || file.type === "image/jpeg") {
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = (readerEvt: ProgressEvent<FileReader>) => {
|
||||||
|
if (readerEvt?.target?.result) {
|
||||||
|
fabric.Image.fromURL(readerEvt.target.result as string, (img: fabric.Image) => {
|
||||||
|
img.set({ left: 0, top: 0, snapAngle: 10 });
|
||||||
|
img.scaleToHeight(this.SIZE_DEFAULT).scaleToHeight(this.SIZE_DEFAULT);
|
||||||
|
canvas.add(img);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = (readerEvt: ProgressEvent<FileReader>) => {
|
||||||
|
console.error(readerEvt);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addText(canvas: fabric.Canvas, text?: string): void {
|
||||||
|
const obj = new fabric.IText(text ?? "Text", {
|
||||||
|
...this.OBJECT_DEFAULTS,
|
||||||
|
fontFamily: "Arial",
|
||||||
|
textAlign: "center",
|
||||||
|
originX: "center", //added
|
||||||
|
originY: "center",
|
||||||
|
lineHeight: 1,
|
||||||
|
});
|
||||||
|
canvas.add(obj);
|
||||||
|
obj.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addHLine(canvas: fabric.Canvas): void {
|
||||||
|
const obj = new fabric.Line([10, 10, 10 + this.SIZE_DEFAULT, 10], {
|
||||||
|
...this.OBJECT_DEFAULTS,
|
||||||
|
stroke: "#000",
|
||||||
|
strokeWidth: 3,
|
||||||
|
});
|
||||||
|
obj.setControlsVisibility({
|
||||||
|
tl: false,
|
||||||
|
bl: false,
|
||||||
|
tr: false,
|
||||||
|
br: false,
|
||||||
|
mt: false,
|
||||||
|
mb: false,
|
||||||
|
});
|
||||||
|
canvas.add(obj);
|
||||||
|
obj.centerV();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addCircle(canvas: fabric.Canvas): void {
|
||||||
|
const obj = new fabric.Circle({
|
||||||
|
...this.OBJECT_DEFAULTS,
|
||||||
|
radius: this.SIZE_DEFAULT / 2,
|
||||||
|
fill: "transparent",
|
||||||
|
stroke: "black",
|
||||||
|
strokeWidth: 3,
|
||||||
|
});
|
||||||
|
canvas.add(obj);
|
||||||
|
obj.centerV();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addRect(canvas: fabric.Canvas): void {
|
||||||
|
const obj = new fabric.Rect({
|
||||||
|
...this.OBJECT_DEFAULTS,
|
||||||
|
width: this.SIZE_DEFAULT,
|
||||||
|
height: this.SIZE_DEFAULT,
|
||||||
|
fill: "transparent",
|
||||||
|
stroke: "black",
|
||||||
|
strokeWidth: 3,
|
||||||
|
});
|
||||||
|
canvas.add(obj);
|
||||||
|
obj.centerV();
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
import PrintPreview from "./PrintPreview.svelte";
|
import PrintPreview from "./PrintPreview.svelte";
|
||||||
import TextParamsPanel from "./TextParamsControls.svelte";
|
import TextParamsPanel from "./TextParamsControls.svelte";
|
||||||
import GenericObjectParamsControls from "./GenericObjectParamsControls.svelte";
|
import GenericObjectParamsControls from "./GenericObjectParamsControls.svelte";
|
||||||
|
import { ImageEditorUtils } from "../image_editor_utils";
|
||||||
|
|
||||||
let htmlCanvas: HTMLCanvasElement;
|
let htmlCanvas: HTMLCanvasElement;
|
||||||
let fabricCanvas: fabric.Canvas;
|
let fabricCanvas: fabric.Canvas;
|
||||||
@ -18,18 +19,6 @@
|
|||||||
let selectedObject: fabric.Object | undefined = undefined;
|
let selectedObject: fabric.Object | undefined = undefined;
|
||||||
let selectedCount: number = 0;
|
let selectedCount: number = 0;
|
||||||
|
|
||||||
const defaultSize = 64;
|
|
||||||
|
|
||||||
const fabricObjectDefaults = {
|
|
||||||
fill: "black",
|
|
||||||
snapAngle: 10,
|
|
||||||
top: 10,
|
|
||||||
left: 10,
|
|
||||||
// objectCaching: false
|
|
||||||
strokeUniform: true,
|
|
||||||
// noScaleCache: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteSelected = () => {
|
const deleteSelected = () => {
|
||||||
fabricCanvas.getActiveObjects().forEach((obj) => {
|
fabricCanvas.getActiveObjects().forEach((obj) => {
|
||||||
fabricCanvas.remove(obj);
|
fabricCanvas.remove(obj);
|
||||||
@ -93,64 +82,16 @@
|
|||||||
|
|
||||||
const onObjectPicked = (name: OjectType) => {
|
const onObjectPicked = (name: OjectType) => {
|
||||||
if (name === "text") {
|
if (name === "text") {
|
||||||
const obj = new fabric.IText("Text", {
|
ImageEditorUtils.addText(fabricCanvas);
|
||||||
...fabricObjectDefaults,
|
|
||||||
fontFamily: "Arial"
|
|
||||||
});
|
|
||||||
fabricCanvas.add(obj);
|
|
||||||
obj.center();
|
|
||||||
} else if (name === "line") {
|
} else if (name === "line") {
|
||||||
const obj = new fabric.Line([10, 10, 10 + defaultSize, 10], {
|
ImageEditorUtils.addHLine(fabricCanvas);
|
||||||
...fabricObjectDefaults,
|
|
||||||
stroke: "#000",
|
|
||||||
strokeWidth: 3,
|
|
||||||
});
|
|
||||||
obj.setControlsVisibility({
|
|
||||||
tl: false,
|
|
||||||
bl: false,
|
|
||||||
tr: false,
|
|
||||||
br: false,
|
|
||||||
mt: false,
|
|
||||||
mb: false,
|
|
||||||
});
|
|
||||||
fabricCanvas.add(obj);
|
|
||||||
obj.centerV();
|
|
||||||
} else if (name === "circle") {
|
} else if (name === "circle") {
|
||||||
const obj = new fabric.Circle({
|
ImageEditorUtils.addCircle(fabricCanvas);
|
||||||
...fabricObjectDefaults,
|
|
||||||
radius: 25,
|
|
||||||
fill: "transparent",
|
|
||||||
stroke: "black",
|
|
||||||
strokeWidth: 3,
|
|
||||||
});
|
|
||||||
fabricCanvas.add(obj);
|
|
||||||
obj.centerV();
|
|
||||||
} else if (name === "rectangle") {
|
} else if (name === "rectangle") {
|
||||||
const obj = new fabric.Rect({
|
ImageEditorUtils.addRect(fabricCanvas);
|
||||||
...fabricObjectDefaults,
|
|
||||||
width: defaultSize,
|
|
||||||
height: defaultSize,
|
|
||||||
fill: "transparent",
|
|
||||||
stroke: "black",
|
|
||||||
strokeWidth: 3,
|
|
||||||
});
|
|
||||||
fabricCanvas.add(obj);
|
|
||||||
obj.centerV();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addSvgXmlToCanvas = (data: string) => {
|
|
||||||
fabric.loadSVGFromString(data, (objects, options) => {
|
|
||||||
const obj = fabric.util.groupSVGElements(objects, options);
|
|
||||||
fabric.Text;
|
|
||||||
obj.scaleToWidth(defaultSize).scaleToHeight(defaultSize);
|
|
||||||
obj.set({ ...fabricObjectDefaults, top: 0, left: 0 });
|
|
||||||
|
|
||||||
fabricCanvas.add(obj).renderAll();
|
|
||||||
fabricCanvas.renderAll();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onIconPicked = (i: IconName) => {
|
const onIconPicked = (i: IconName) => {
|
||||||
const lookup = faParse.icon(i);
|
const lookup = faParse.icon(i);
|
||||||
const iconData = faIcon(lookup);
|
const iconData = faIcon(lookup);
|
||||||
@ -159,8 +100,7 @@
|
|||||||
console.error(`Icon ${i} not found`);
|
console.error(`Icon ${i} not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ImageEditorUtils.addSvg(fabricCanvas, iconData.html.toString());
|
||||||
addSvgXmlToCanvas(iconData.html.toString());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPreviewClosed = () => {
|
const onPreviewClosed = () => {
|
||||||
@ -182,6 +122,9 @@
|
|||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ImageEditorUtils.addText(fabricCanvas);
|
||||||
|
|
||||||
|
|
||||||
fabricCanvas.on("object:moving", (e: fabric.IEvent<MouseEvent>) => {
|
fabricCanvas.on("object:moving", (e: fabric.IEvent<MouseEvent>) => {
|
||||||
const grid = 5;
|
const grid = 5;
|
||||||
if (e.target && e.target.left !== undefined && e.target.top !== undefined) {
|
if (e.target && e.target.left !== undefined && e.target.top !== undefined) {
|
||||||
@ -213,33 +156,7 @@
|
|||||||
|
|
||||||
if (dragEvt.dataTransfer?.files) {
|
if (dragEvt.dataTransfer?.files) {
|
||||||
[...dragEvt.dataTransfer.files].forEach((file: File, idx: number) => {
|
[...dragEvt.dataTransfer.files].forEach((file: File, idx: number) => {
|
||||||
const reader = new FileReader();
|
ImageEditorUtils.addImageFile(fabricCanvas, file);
|
||||||
console.log(file.type);
|
|
||||||
if (file.type.startsWith("image/svg")) {
|
|
||||||
reader.readAsText(file, "UTF-8");
|
|
||||||
reader.onload = (readerEvt: ProgressEvent<FileReader>) => {
|
|
||||||
if (readerEvt.target && readerEvt.target.result) {
|
|
||||||
addSvgXmlToCanvas(readerEvt.target.result.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.onerror = (readerEvt: ProgressEvent<FileReader>) => {
|
|
||||||
console.error(readerEvt);
|
|
||||||
};
|
|
||||||
} else if (file.type === "image/png" || file.type === "image/jpeg") {
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = (readerEvt: ProgressEvent<FileReader>) => {
|
|
||||||
if (readerEvt.target && readerEvt.target.result) {
|
|
||||||
fabric.Image.fromURL(readerEvt.target.result.toString(), (img: fabric.Image) => {
|
|
||||||
img.set({ left: 0, top: 0, snapAngle:10 });
|
|
||||||
img.scaleToHeight(defaultSize).scaleToHeight(defaultSize);
|
|
||||||
fabricCanvas.add(img);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
reader.onerror = (readerEvt: ProgressEvent<FileReader>) => {
|
|
||||||
console.error(readerEvt);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -253,14 +170,6 @@
|
|||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
|
||||||
<div class="image-editor">
|
<div class="image-editor">
|
||||||
<!-- <div class="row mb-1">
|
|
||||||
<div class="col d-flex justify-content-center">
|
|
||||||
<div class="toolbar mb-1 d-flex flex-wrap gap-1 justify-content-center align-items-center">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="row mb-1">
|
<div class="row mb-1">
|
||||||
<div class="col d-flex justify-content-center">
|
<div class="col d-flex justify-content-center">
|
||||||
<div class="canvas-wrapper print-start-{labelProps.startPos}">
|
<div class="canvas-wrapper print-start-{labelProps.startPos}">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PrinterModel, type RfidInfo } from "@mmote/niimbluelib";
|
import { PrinterModel, SoundSettingsItemType, type RfidInfo } from "@mmote/niimbluelib";
|
||||||
import { printerClient, connectedPrinterName, connectionState, initClient, heartbeatData, printerConfig } from "../stores";
|
import { printerClient, connectedPrinterName, connectionState, initClient, heartbeatData, printerConfig } from "../stores";
|
||||||
import type { ConnectionType } from "../types";
|
import type { ConnectionType } from "../types";
|
||||||
import FaIcon from "./FaIcon.svelte";
|
import FaIcon from "./FaIcon.svelte";
|
||||||
@ -26,16 +26,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const startHeartbeat = async () => {
|
const startHeartbeat = async () => {
|
||||||
// timer = setInterval(async () => {
|
|
||||||
// const data = await $printerClient.abstraction.heartbeat();
|
|
||||||
// console.log(data);
|
|
||||||
// }, 1000);
|
|
||||||
$printerClient.startHeartbeat();
|
$printerClient.startHeartbeat();
|
||||||
};
|
};
|
||||||
const stopHeartbeat = async () => {
|
const stopHeartbeat = async () => {
|
||||||
// clearInterval(timer);
|
|
||||||
$printerClient.stopHeartbeat();
|
$printerClient.stopHeartbeat();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const soundOn = async () => {
|
||||||
|
await $printerClient.abstraction.setSoundEnabled(SoundSettingsItemType.BluetoothConnectionSound, true);
|
||||||
|
await $printerClient.abstraction.setSoundEnabled(SoundSettingsItemType.PowerSound, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const soundOff = async () => {
|
||||||
|
await $printerClient.abstraction.setSoundEnabled(SoundSettingsItemType.BluetoothConnectionSound, false);
|
||||||
|
await $printerClient.abstraction.setSoundEnabled(SoundSettingsItemType.PowerSound, false);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="input-group flex-nowrap justify-content-end">
|
<div class="input-group flex-nowrap justify-content-end">
|
||||||
@ -71,6 +76,8 @@
|
|||||||
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>Rfid</button>
|
<button class="btn btn-sm btn-primary" on:click={getRfidInfo}>Rfid</button>
|
||||||
<button class="btn btn-sm btn-primary" on:click={startHeartbeat}>Heartbeat on</button>
|
<button class="btn btn-sm btn-primary" on:click={startHeartbeat}>Heartbeat on</button>
|
||||||
<button class="btn btn-sm btn-primary" on:click={stopHeartbeat}>Heartbeat off</button>
|
<button class="btn btn-sm btn-primary" on:click={stopHeartbeat}>Heartbeat off</button>
|
||||||
|
<button class="btn btn-sm btn-primary" on:click={soundOn}>Sound on</button>
|
||||||
|
<button class="btn btn-sm btn-primary" on:click={soundOff}>Sound off</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="input-group-text">{$connectedPrinterName}</span>
|
<span class="input-group-text">{$connectedPrinterName}</span>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -213,7 +213,6 @@ export class PacketGenerator {
|
|||||||
return new NiimbotPacket(RequestCommandId.PageEnd, [1]);
|
return new NiimbotPacket(RequestCommandId.PageEnd, [1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/ayufan/niimprint-web/blob/main/cmds.js#L215
|
|
||||||
public static printEmptySpace(pos: number, repeats: number): NiimbotPacket {
|
public static printEmptySpace(pos: number, repeats: number): NiimbotPacket {
|
||||||
const packet = new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
|
const packet = new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
|
||||||
packet.oneWay = true;
|
packet.oneWay = true;
|
||||||
|
Reference in New Issue
Block a user