From 2a36344375e1561310a0f061ebadaf1a42004ab5 Mon Sep 17 00:00:00 2001
From: NaitLee
Date: Wed, 8 Sep 2021 16:51:48 +0800
Subject: [PATCH] Custom print feature; fixes
---
.gitignore | 2 +
README.md | 5 +-
README.zh-CN.md | 5 +-
www/custom-print.html | 49 ++++++++++++++
www/custom-print.js | 138 ++++++++++++++++++++++++++++++++++++++++
www/i18n.js | 2 +-
www/main.css | 1 +
www/main.d.ts | 4 +-
www/print-document.html | 8 +--
www/print-document.js | 8 +--
www/print-image.js | 14 ++--
11 files changed, 213 insertions(+), 23 deletions(-)
diff --git a/.gitignore b/.gitignore
index b3c07e2..4e23deb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,8 @@ www/fabric.js
www/fabric.min.js
www/html2canvas.js
www/html2canvas.min.js
+www/qrcode.js
+www/qrcode.min.js
__pycache__
# Byte-compiled / optimized / DLL files
diff --git a/README.md b/README.md
index 4f5fdd3..20abed2 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,8 @@ This application uses server/client module, and have fewest possible dependencie
- Python3 & Browser
- [fabric.min.js](https://github.com/fabricjs/fabric.js/tree/master/dist)
- [html2canvas.min.js](https://html2canvas.hertzen.com/)
-- Any css for plain webpage, e.g. [minicss](https://minicss.org/), rename to `skin.css`
+- [qrcode.min.js](https://davidshimjs.github.io/qrcodejs/)
+- (Optional) Any css for plain webpage, e.g. [minicss](https://minicss.org/), rename to `skin.css`
Put any web-related files to folder `www`.
@@ -47,7 +48,7 @@ Support for both Windows and GNU/Linux are included. And Windows release package
### Plans
-- Support rich edit features as the official app for cat printers (iPrint & 精准学习)
+- Smoother mono-color converting
- Make remote-print by web interface more standard/compatible/secure
Possible features:
diff --git a/README.zh-CN.md b/README.zh-CN.md
index eb9805d..e260c4c 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -37,7 +37,8 @@ TODO
- Python3 与浏览器
- [fabric.min.js](https://github.com/fabricjs/fabric.js/tree/master/dist)
- [html2canvas.min.js](https://html2canvas.hertzen.com/)
-- 任何纯网页可用的 css,如 [minicss](https://minicss.org/),重命名为 `skin.css`
+- [qrcode.min.js](https://davidshimjs.github.io/qrcodejs/)
+- (可选)任何纯网页可用的 css,如 [minicss](https://minicss.org/),重命名为 `skin.css`
将 web 相关的文件放在 `www` 文件夹中。
@@ -47,7 +48,7 @@ TODO
### 计划
-- 支持高级编辑,如同官方应用 (iPrint & 精准学习)
+- 更好的双色转换
- 使 web 界面的远程打印标准化/兼容/安全
可能的功能:
diff --git a/www/custom-print.html b/www/custom-print.html
index d04fb09..8d1da2a 100644
--- a/www/custom-print.html
+++ b/www/custom-print.html
@@ -18,6 +18,40 @@
Select device:
+
+
+ Operate directly on the box canvas
+
+
+
+ Canvas Height:
+
+
+ Threshold:
+
+
+
+
+ Insert:
+
+
+
+
+ Action:
+
+
+
+
+
+
+
+
+
+
+
+ Preview
+
+
+
diff --git a/www/custom-print.js b/www/custom-print.js
index e69de29..9c6a938 100644
--- a/www/custom-print.js
+++ b/www/custom-print.js
@@ -0,0 +1,138 @@
+///
+///
+
+class CustomPrinter {
+ WIDTH = 384;
+ threshold = 0.6;
+ bluetoothMACInput = document.getElementById('bt_mac');
+ thresholdInput = document.getElementById('filter_threshold');
+ fileSelection = document.createElement('input');
+ dummyImage = new Image();
+ canvasPreview = document.getElementById('image_preview');
+ previewButton = document.getElementById('preview_button');
+ printButton = document.getElementById('print_button');
+ actionInsertText = document.getElementById('action_insert_text');
+ actionInsertImage = document.getElementById('action_insert_image');
+ actionInsertQR = document.getElementById('action_insert_qr');
+ canvasHeightInput = document.getElementById('canvas_height');
+ fabricCanvas = new fabric.Canvas('work_canvas', {
+ backgroundColor: 'white'
+ });
+ monoMethod = imageDataColorToMonoSquare;
+ insertImage() {
+ this.fileSelection.click();
+ }
+ preview() {
+ let context = this.fabricCanvas.getContext('2d');
+ let imagedata = context.getImageData(0, 0, this.WIDTH, this.fabricCanvas.height);
+ this.canvasPreview.height = this.fabricCanvas.height;
+ this.canvasPreview.getContext('2d').putImageData(this.monoMethod(imagedata, this.threshold), 0, 0);
+ }
+ constructor() {
+ this.fileSelection.type = 'file';
+ this.monoMethod = imageDataColorToMonoSquare;
+ this.fileSelection.addEventListener('input', this.preview.bind(this));
+ this.previewButton.addEventListener('click', this.preview.bind(this));
+ this.thresholdInput.onchange = event => {
+ this.threshold = this.thresholdInput.value;
+ }
+ this.canvasHeightInput.onchange = event => {
+ this.fabricCanvas.setHeight(this.canvasHeightInput.value);
+ }
+ this.actionInsertText.addEventListener('click', event => {
+ let text = new fabric.Textbox(i18N.get('Double click to edit'), {
+ color: 'black',
+ fontSize: 24,
+ });
+ this.fabricCanvas.add(text);
+ });
+ this.fileSelection.addEventListener('input', event => {
+ let reader = new FileReader();
+ reader.onload = event1 => {
+ this.dummyImage.src = event1.target.result;
+ let fimage = new fabric.Image(this.dummyImage, {});
+ fimage.scale(this.WIDTH / this.dummyImage.width);
+ this.fabricCanvas.add(fimage);
+ }
+ reader.readAsDataURL(this.fileSelection.files[0]);
+ });
+ this.actionInsertImage.addEventListener('click', this.insertImage.bind(this));
+ this.actionInsertQR.addEventListener('click', event => {
+ let div = document.createElement('div');
+ new QRCode(div, prompt(i18N.get('Content of QRCode:')));
+ // QRCode generation is async, currently no better way than wait for a while
+ setTimeout(() => {
+ let fimage = new fabric.Image(div.lastChild, {});
+ fimage.scale(this.WIDTH / div.lastChild.width);
+ this.fabricCanvas.add(fimage);
+ }, 1000);
+ });
+ this.printButton.addEventListener('click', event => {
+ // this.preview();
+ if (this.canvasPreview.height == 0) {
+ notice(i18N.get('Please preview image first'));
+ return;
+ }
+ let mac_address = this.bluetoothMACInput.value;
+ if (mac_address == '') {
+ notice(i18N.get('Please select a device'));
+ return;
+ }
+ notice(i18N.get('Printing, please wait.'));
+ let context = this.canvasPreview.getContext('2d');
+ let pbm_data = imageDataMonoToPBM(context.getImageData(0, 0, this.WIDTH, this.canvasPreview.height));
+ let xhr = new XMLHttpRequest();
+ xhr.open('POST', '/~print?address=' + mac_address);
+ xhr.setRequestHeader('Content-Type', 'application-octet-stream');
+ xhr.onload = () => {
+ notice(i18N.get(xhr.responseText));
+ }
+ xhr.send(pbm_data);
+ });
+ let boldFunction = () => {
+ let object = this.fabricCanvas.getActiveObject();
+ if (!object) return;
+ if (object.type == 'textbox') {
+ if (object.fontWeight == 'normal') object.fontWeight = 'bold';
+ else if (object.fontWeight == 'bold') object.fontWeight = 'normal';
+ this.fabricCanvas.renderAll();
+ }
+ }
+ document.getElementById('action_make_bold').addEventListener('click', boldFunction.bind(this));
+ let italicFunction = () => {
+ let object = this.fabricCanvas.getActiveObject();
+ if (!object) return;
+ if (object.type == 'textbox') {
+ if (object.fontStyle == 'normal') object.fontStyle = 'italic';
+ else if (object.fontStyle == 'italic') object.fontStyle = 'normal';
+ this.fabricCanvas.renderAll();
+ }
+ }
+ document.getElementById('action_make_italic').addEventListener('click', italicFunction.bind(this));
+ document.getElementById('action_make_underline').addEventListener('click', event => {
+ let object = this.fabricCanvas.getActiveObject();
+ if (!object) return;
+ if (object.type == 'textbox') {
+ object.underline = !object.underline;
+ // Seems there's a bug in fabric, underline cannot be rendered before changing bold/italic
+ boldFunction();
+ this.fabricCanvas.renderAll();
+ boldFunction();
+ this.fabricCanvas.renderAll();
+ }
+ });
+ document.getElementById('action_delete').addEventListener('click', event => {
+ let object = this.fabricCanvas.getActiveObject();
+ if (!object) return;
+ this.fabricCanvas.remove(object);
+ this.fabricCanvas.renderAll();
+ });
+ this.fabricCanvas.freeDrawingBrush.color = 'black';
+ this.fabricCanvas.freeDrawingBrush.width = 6;
+ document.getElementById('action_switch_paint').addEventListener('click', event => {
+ this.fabricCanvas.isDrawingMode = !this.fabricCanvas.isDrawingMode;
+ })
+ }
+}
+
+var custom_printer = new CustomPrinter();
diff --git a/www/i18n.js b/www/i18n.js
index f33245e..a97199e 100644
--- a/www/i18n.js
+++ b/www/i18n.js
@@ -67,8 +67,8 @@
}
function get(originaltext, language) {
- if (lang[language] == undefined) return originaltext;
language = language || userlang || defaultlang;
+ if (lang[language] == undefined) return originaltext;
return lang[language][originaltext] || originaltext;
}
diff --git a/www/main.css b/www/main.css
index 7fa62d9..91a9268 100644
--- a/www/main.css
+++ b/www/main.css
@@ -15,6 +15,7 @@ main.mainpage p a {
display: inline-block;
font-size: larger;
width: 10em;
+ padding: 0.5em 0;
}
a:link, a:visited {
diff --git a/www/main.d.ts b/www/main.d.ts
index 60f34b2..dd53c0a 100644
--- a/www/main.d.ts
+++ b/www/main.d.ts
@@ -13,7 +13,7 @@ declare interface ImagePrinter {
bluetoothMACInput: HTMLInputElement;
fileSelection: HTMLInputElement;
dummyImage: HTMLImageElement;
- imagePreview: HTMLCanvasElement;
+ canvasPreview: HTMLCanvasElement;
previewButton: HTMLButtonElement;
printButton: HTMLButtonElement;
monoMethod: Function;
@@ -24,7 +24,7 @@ declare interface DocumentPrinter {
bluetoothMACInput: HTMLInputElement;
container: HTMLDivElement;
printButton: HTMLButtonElement;
- imagePreview: HTMLDivElement;
+ canvasPreview: HTMLDivElement;
monoMethod: Function;
}
diff --git a/www/print-document.html b/www/print-document.html
index e793bf1..0061187 100644
--- a/www/print-document.html
+++ b/www/print-document.html
@@ -28,11 +28,9 @@
- From MS Office/WPS/LibreOffice to here, pasted data shall be formatted
+ From Webpage/MS Office/WPS/LibreOffice to here, pasted data shall be formatted
Preview
-
+