Working tree changes 2024-07-30 01:00
All checks were successful
Test project build / Build (push) Successful in 1m10s

This commit is contained in:
Bot 2024-07-30 01:00:01 +03:00 committed by multimote
parent 17364b48be
commit 319a33084e
4 changed files with 136 additions and 109 deletions

View 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();
}
}

View File

@ -10,6 +10,7 @@
import PrintPreview from "./PrintPreview.svelte";
import TextParamsPanel from "./TextParamsControls.svelte";
import GenericObjectParamsControls from "./GenericObjectParamsControls.svelte";
import { ImageEditorUtils } from "../image_editor_utils";
let htmlCanvas: HTMLCanvasElement;
let fabricCanvas: fabric.Canvas;
@ -18,18 +19,6 @@
let selectedObject: fabric.Object | undefined = undefined;
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 = () => {
fabricCanvas.getActiveObjects().forEach((obj) => {
fabricCanvas.remove(obj);
@ -93,64 +82,16 @@
const onObjectPicked = (name: OjectType) => {
if (name === "text") {
const obj = new fabric.IText("Text", {
...fabricObjectDefaults,
fontFamily: "Arial"
});
fabricCanvas.add(obj);
obj.center();
ImageEditorUtils.addText(fabricCanvas);
} else if (name === "line") {
const obj = new fabric.Line([10, 10, 10 + defaultSize, 10], {
...fabricObjectDefaults,
stroke: "#000",
strokeWidth: 3,
});
obj.setControlsVisibility({
tl: false,
bl: false,
tr: false,
br: false,
mt: false,
mb: false,
});
fabricCanvas.add(obj);
obj.centerV();
ImageEditorUtils.addHLine(fabricCanvas);
} else if (name === "circle") {
const obj = new fabric.Circle({
...fabricObjectDefaults,
radius: 25,
fill: "transparent",
stroke: "black",
strokeWidth: 3,
});
fabricCanvas.add(obj);
obj.centerV();
ImageEditorUtils.addCircle(fabricCanvas);
} else if (name === "rectangle") {
const obj = new fabric.Rect({
...fabricObjectDefaults,
width: defaultSize,
height: defaultSize,
fill: "transparent",
stroke: "black",
strokeWidth: 3,
});
fabricCanvas.add(obj);
obj.centerV();
ImageEditorUtils.addRect(fabricCanvas);
}
};
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 lookup = faParse.icon(i);
const iconData = faIcon(lookup);
@ -159,8 +100,7 @@
console.error(`Icon ${i} not found`);
return;
}
addSvgXmlToCanvas(iconData.html.toString());
ImageEditorUtils.addSvg(fabricCanvas, iconData.html.toString());
};
const onPreviewClosed = () => {
@ -182,6 +122,9 @@
backgroundColor: "#fff",
});
ImageEditorUtils.addText(fabricCanvas);
fabricCanvas.on("object:moving", (e: fabric.IEvent<MouseEvent>) => {
const grid = 5;
if (e.target && e.target.left !== undefined && e.target.top !== undefined) {
@ -213,33 +156,7 @@
if (dragEvt.dataTransfer?.files) {
[...dragEvt.dataTransfer.files].forEach((file: File, idx: number) => {
const reader = new FileReader();
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);
};
}
ImageEditorUtils.addImageFile(fabricCanvas, file);
});
}
});
@ -253,14 +170,6 @@
<svelte:window on:keydown={onKeyDown} />
<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="col d-flex justify-content-center">
<div class="canvas-wrapper print-start-{labelProps.startPos}">

View File

@ -1,5 +1,5 @@
<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 type { ConnectionType } from "../types";
import FaIcon from "./FaIcon.svelte";
@ -26,16 +26,21 @@
};
const startHeartbeat = async () => {
// timer = setInterval(async () => {
// const data = await $printerClient.abstraction.heartbeat();
// console.log(data);
// }, 1000);
$printerClient.startHeartbeat();
};
const stopHeartbeat = async () => {
// clearInterval(timer);
$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>
<div class="input-group flex-nowrap justify-content-end">
@ -67,10 +72,12 @@
<div>
Tests
</div>
<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={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>
<span class="input-group-text">{$connectedPrinterName}</span>
{:else}

View File

@ -213,7 +213,6 @@ export class PacketGenerator {
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 {
const packet = new NiimbotPacket(RequestCommandId.PrintEmptyRow, [...Utils.u16ToBytes(pos), repeats]);
packet.oneWay = true;