Working tree changes 2024-07-31 01:00
All checks were successful
Test project build / Build (push) Successful in 1m17s
All checks were successful
Test project build / Build (push) Successful in 1m17s
This commit is contained in:
parent
319a33084e
commit
2e7cfe46b0
@ -54,6 +54,22 @@ export class ImageEditorUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static addImageWithFilePicker(fabricCanvas: fabric.Canvas) {
|
||||
const input: HTMLInputElement = document.createElement("input");
|
||||
|
||||
input.type = "file";
|
||||
|
||||
input.onchange = (e: Event) => {
|
||||
let target = e.target as HTMLInputElement;
|
||||
if (target.files !== null) {
|
||||
let file: File = target.files[0];
|
||||
this.addImageFile(fabricCanvas, file);
|
||||
}
|
||||
};
|
||||
|
||||
input.click();
|
||||
}
|
||||
|
||||
public static addText(canvas: fabric.Canvas, text?: string): void {
|
||||
const obj = new fabric.IText(text ?? "Text", {
|
||||
...this.OBJECT_DEFAULTS,
|
||||
|
@ -51,23 +51,24 @@
|
||||
const onUpdateLabelProps = () => {
|
||||
labelProps = labelProps; // trigger update
|
||||
fabricCanvas.setDimensions(labelProps.size);
|
||||
localStorage.setItem("last_label_props", JSON.stringify(labelProps));
|
||||
};
|
||||
|
||||
const onSaveClicked = () => {
|
||||
const data = fabricCanvas.toJSON();
|
||||
localStorage.setItem("canvas_data", JSON.stringify(data));
|
||||
localStorage.setItem("canvas_props", JSON.stringify(labelProps));
|
||||
localStorage.setItem("saved_canvas_data", JSON.stringify(data));
|
||||
localStorage.setItem("saved_canvas_props", JSON.stringify(labelProps));
|
||||
};
|
||||
|
||||
const onLoadClicked = () => {
|
||||
const props = localStorage.getItem("canvas_props");
|
||||
const props = localStorage.getItem("saved_canvas_props");
|
||||
if (props) {
|
||||
const parsedProps = JSON.parse(props);
|
||||
labelProps = parsedProps;
|
||||
onUpdateLabelProps();
|
||||
}
|
||||
|
||||
const data = localStorage.getItem("canvas_data");
|
||||
const data = localStorage.getItem("saved_canvas_data");
|
||||
fabricCanvas.loadFromJSON(
|
||||
data,
|
||||
() => {
|
||||
@ -89,6 +90,8 @@
|
||||
ImageEditorUtils.addCircle(fabricCanvas);
|
||||
} else if (name === "rectangle") {
|
||||
ImageEditorUtils.addRect(fabricCanvas);
|
||||
} else if (name === "image") {
|
||||
ImageEditorUtils.addImageWithFilePicker(fabricCanvas);
|
||||
}
|
||||
};
|
||||
|
||||
@ -116,6 +119,19 @@
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const savedLabelPropsStr: string | null = localStorage.getItem("last_label_props");
|
||||
|
||||
if (savedLabelPropsStr != null) {
|
||||
try {
|
||||
const obj = JSON.parse(savedLabelPropsStr);
|
||||
if ("size" in obj && "width" in obj.size && "height" in obj.size && ["top", "left"].includes(obj.startPos)) {
|
||||
labelProps = obj as LabelProps;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
fabricCanvas = new fabric.Canvas(htmlCanvas, {
|
||||
width: labelProps.size.width,
|
||||
height: labelProps.size.height,
|
||||
@ -124,7 +140,6 @@
|
||||
|
||||
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) {
|
||||
@ -198,7 +213,7 @@
|
||||
<div class="col d-flex justify-content-center">
|
||||
<div class="toolbar d-flex flex-wrap gap-1 justify-content-center align-items-center">
|
||||
{#if selectedCount > 0}
|
||||
<button class="btn btn-sm btn-danger me-1" on:click={deleteSelected}><FaIcon icon="trash" /></button>
|
||||
<button class="btn btn-sm btn-danger me-1" on:click={deleteSelected}><FaIcon icon="trash" /></button>
|
||||
{/if}
|
||||
|
||||
{#if selectedObject && selectedCount === 1}
|
||||
|
@ -26,6 +26,10 @@
|
||||
<button class="btn me-1" on:click={() => onSubmit("circle")}>
|
||||
<FaIcon icon="circle-dot" /> Circle
|
||||
</button>
|
||||
|
||||
<button class="btn me-1" on:click={() => onSubmit("image")}>
|
||||
<FaIcon icon="image" /> Image
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,12 +4,7 @@
|
||||
import Modal from "bootstrap/js/dist/modal";
|
||||
import { connectionState, printerClient } from "../stores";
|
||||
import { copyImageData, threshold, atkinson } from "../post_process";
|
||||
import {
|
||||
type EncodedImage,
|
||||
ImageEncoder,
|
||||
PacketGenerator,
|
||||
ProtocolVersion,
|
||||
} from "@mmote/niimbluelib";
|
||||
import { type EncodedImage, ImageEncoder, PacketGenerator, ProtocolVersion } from "@mmote/niimbluelib";
|
||||
import type { LabelProps } from "../types";
|
||||
import FaIcon from "./FaIcon.svelte";
|
||||
|
||||
@ -19,59 +14,64 @@
|
||||
|
||||
let modalElement: HTMLElement;
|
||||
let previewCanvas: HTMLCanvasElement;
|
||||
let printState: "idle" | "sending" | "printing" = "idle";
|
||||
let modal: Modal;
|
||||
let sendProgress: number = 0;
|
||||
let printProgress: number = 0; // todo: more progress data
|
||||
let density: number = 3;
|
||||
let quantity: number = 1;
|
||||
let printed: boolean = false;
|
||||
let postProcessType: "threshold" | "dither";
|
||||
let thresholdValue: number = 140;
|
||||
let imgData: ImageData;
|
||||
let imgContext: CanvasRenderingContext2D;
|
||||
let printTaskVersion: ProtocolVersion = ProtocolVersion.V3;
|
||||
let statusTimer: NodeJS.Timeout | undefined = undefined;
|
||||
let printError: boolean = false;
|
||||
let error: string = "";
|
||||
|
||||
const disconnected = derived(connectionState, ($connectionState) => $connectionState !== "connected");
|
||||
|
||||
const cancelPrint = async () => {
|
||||
const endPrint = async () => {
|
||||
clearInterval(statusTimer);
|
||||
|
||||
if (!$disconnected && printed) {
|
||||
await $printerClient.sendPacket(PacketGenerator.printEnd());
|
||||
if (!$disconnected && printState !== "idle") {
|
||||
await $printerClient.abstraction.printEnd();
|
||||
}
|
||||
|
||||
printed = false;
|
||||
printState = "idle";
|
||||
printProgress = 0;
|
||||
};
|
||||
|
||||
const onPrint = async () => {
|
||||
printError = false;
|
||||
const encoded: EncodedImage = ImageEncoder.encodeCanvas(previewCanvas, labelProps.startPos);
|
||||
const packets = PacketGenerator.generatePrintSequence(printTaskVersion, encoded, { quantity, density });
|
||||
|
||||
for (let i = 0; i < packets.length; i++) {
|
||||
sendProgress = Math.round(((i + 1) / packets.length) * 100);
|
||||
await $printerClient.sendPacketWaitResponse(packets[i], 10_000);
|
||||
printState = "sending";
|
||||
error = "";
|
||||
|
||||
try {
|
||||
await $printerClient.abstraction.print(printTaskVersion, encoded, { quantity, density });
|
||||
} catch (e) {
|
||||
error = `${e}`;
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
printState = "printing";
|
||||
|
||||
statusTimer = setInterval(async () => {
|
||||
try {
|
||||
const status = await $printerClient.abstraction.getPrintStatus();
|
||||
printProgress = status.pagePrintProgress;
|
||||
|
||||
if (status.page === quantity && status.pagePrintProgress === 100 && status.pageFeedProgress === 100) {
|
||||
await cancelPrint();
|
||||
await endPrint();
|
||||
}
|
||||
} catch (e) {
|
||||
error = `${e}`;
|
||||
console.error(e);
|
||||
await cancelPrint();
|
||||
printError = true;
|
||||
await endPrint();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
printed = true;
|
||||
printState = "idle";
|
||||
};
|
||||
|
||||
const updatePreview = () => {
|
||||
@ -102,13 +102,13 @@
|
||||
modal = new Modal(modalElement);
|
||||
modal.show();
|
||||
modalElement.addEventListener("hidden.bs.modal", async () => {
|
||||
cancelPrint();
|
||||
endPrint();
|
||||
onClosed();
|
||||
});
|
||||
|
||||
const taskVer = $printerClient?.getCapabilities().printTaskVersion;
|
||||
if (taskVer !== undefined && taskVer !== ProtocolVersion.UNKNOWN) {
|
||||
console.log(`Detected print task version: ${ProtocolVersion[taskVer]}`)
|
||||
console.log(`Detected print task version: ${ProtocolVersion[taskVer]}`);
|
||||
printTaskVersion = taskVer;
|
||||
}
|
||||
});
|
||||
@ -139,13 +139,8 @@
|
||||
<div class="modal-body text-center">
|
||||
<canvas class="print-start-{labelProps.startPos}" bind:this={previewCanvas}></canvas>
|
||||
|
||||
{#if sendProgress != 0 && sendProgress != 100}
|
||||
<div>
|
||||
Sending...
|
||||
<div class="progress" role="progressbar">
|
||||
<div class="progress-bar" style="width: {sendProgress}%">{sendProgress}%</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if printState === "sending"}
|
||||
<div>Sending...</div>
|
||||
{/if}
|
||||
{#if printProgress != 0 && printProgress != 100}
|
||||
<div>
|
||||
@ -157,8 +152,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if printError}
|
||||
<div class="alert alert-danger" role="alert">Print error</div>
|
||||
{#if error}
|
||||
<div class="alert alert-danger" role="alert">{error}</div>
|
||||
{/if}
|
||||
|
||||
<div class="modal-footer">
|
||||
@ -204,13 +199,13 @@
|
||||
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
|
||||
{#if printed}
|
||||
<button type="button" class="btn btn-primary" disabled={$disconnected} on:click={cancelPrint}>
|
||||
{#if printState !== "idle"}
|
||||
<button type="button" class="btn btn-primary" disabled={$disconnected} on:click={endPrint}>
|
||||
Cancel print
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button type="button" class="btn btn-primary" disabled={$disconnected || printed} on:click={onPrint}>
|
||||
<button type="button" class="btn btn-primary" disabled={$disconnected || printState !== "idle"} on:click={onPrint}>
|
||||
{#if $disconnected}
|
||||
Printer is not connected
|
||||
{:else}
|
||||
|
@ -15,4 +15,4 @@ export type LabelPreset = {
|
||||
startPosition: HeadPosition;
|
||||
};
|
||||
|
||||
export type OjectType = "text" | "rectangle" | "line" | "circle"
|
||||
export type OjectType = "text" | "rectangle" | "line" | "circle" | "image"
|
||||
|
@ -7,12 +7,17 @@ import {
|
||||
NiimbotPacket,
|
||||
PacketGenerator,
|
||||
PrinterInfoType,
|
||||
PrintOptions,
|
||||
ResponseCommandId,
|
||||
SoundSettingsItemType,
|
||||
SoundSettingsType,
|
||||
} from ".";
|
||||
import { NiimbotAbstractClient, Utils, Validators } from "..";
|
||||
import { PrinterModel } from "../printers";
|
||||
EncodedImage,
|
||||
NiimbotAbstractClient,
|
||||
Utils,
|
||||
Validators,
|
||||
PrinterModel,
|
||||
ProtocolVersion,
|
||||
} from "..";
|
||||
import { SequentialDataReader } from "./data_reader";
|
||||
|
||||
export class PrintError extends Error {
|
||||
@ -64,8 +69,9 @@ export interface PrinterStatusData {
|
||||
|
||||
/** Not sure for name. */
|
||||
export class Abstraction {
|
||||
private readonly DEFAULT_TIMEOUT: number = 1_000;
|
||||
private client: NiimbotAbstractClient;
|
||||
private timeout: number = 1000;
|
||||
private timeout: number = this.DEFAULT_TIMEOUT;
|
||||
|
||||
constructor(client: NiimbotAbstractClient) {
|
||||
this.client = client;
|
||||
@ -79,6 +85,10 @@ export class Abstraction {
|
||||
this.timeout = value;
|
||||
}
|
||||
|
||||
public setDefaultTimeout() {
|
||||
this.timeout = this.DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
/** Send packet and wait for response */
|
||||
private async send(packet: NiimbotPacket): Promise<NiimbotPacket> {
|
||||
return this.client.sendPacketWaitResponse(packet, this.timeout);
|
||||
@ -277,4 +287,20 @@ export class Abstraction {
|
||||
public async setSoundEnabled(soundType: SoundSettingsItemType, value: boolean): Promise<void> {
|
||||
await this.send(PacketGenerator.setSoundSettings(soundType, value));
|
||||
}
|
||||
|
||||
public async print(protoVersion: ProtocolVersion, image: EncodedImage, options?: PrintOptions, timeout?:number): Promise<void> {
|
||||
this.setTimeout(timeout ?? 10_000);
|
||||
const packets: NiimbotPacket[] = PacketGenerator.generatePrintSequence(protoVersion, image, options);
|
||||
try {
|
||||
for (const element of packets) {
|
||||
await this.send(element);
|
||||
}
|
||||
} finally {
|
||||
this.setDefaultTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
public async printEnd(): Promise<void> {
|
||||
await this.send(PacketGenerator.printEnd());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user