Intro
This time, I will try saving high resolution images and editing images.
- electron ver.12.0.2
- typescript ver.4.2.4
- webpack ver.5.31.2
- webpack-cli ver.4.6.0
- pdfjs-dist ver.2.7.570
- dpi-tools ver.1.0.7
Save images as high resolution
Last time, I got images from PDF and saved them.
But they were saved as 96 DPI.
Because Canvas.toBlob() set as 96 DPI.
To save as more higher resolution, I used "dpi-tools".
d.ts
When I tried using "dpi-tools" in TypeScript code, I got a problem.
Because it hadn't had Type Declaration files, I couldn't import.
So I added a Type Declaration file by myself.
First, I created a file like below.
index.d.ts
export function changeDpiDataUrl(base64Image: string, dpi: number): Array<string>;
export function changeDpiBlob(blob: Blob, dpi: number): Promise<Blob>;
export function changeDpiBuffer(buffer: Buffer, dpi: number): Buffer;
And I put it into a directory of "node_modules/@types/dpi-tools".
Now I can import "dpi-tools" :)
Save images
preload.ts
import * as dpiTools from 'dpi-tools';
...
function saveFile() {
const canvas = document.getElementById('sample_page') as HTMLCanvasElement;
canvas.toBlob(async (blob) => {
// save as 300 DPI
const updatedBlob = await dpiTools.changeDpiBlob(blob!, 300);
const fileReader = new FileReader();
fileReader.onload = async (ev) => {
const buffer = Buffer.from(fileReader.result as ArrayBuffer);
ipcRenderer.send('save_file', "sample.jpg", buffer);
};
fileReader.readAsArrayBuffer(updatedBlob);
}, 'image/jpeg', 1);
}
Result
Save custom d.ts files
update 2021-04-21
I shouldn't the index.d.ts file in node_modules.
Because when I install another package by npm, my custom d.ts files would be deleted.
So I moved them into "types" folder what I had created in my project.
And I changed tsconfig.js.
tsconfig.js
...
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {
"*": ["*", "types/*"]
},
...
draw rectangle and crop images
- Draw a rectangle on Canvas
- Crop the image what is gotten from PDF as same as the rectangle size
- Save cropped image
1. Draw a rectangle on Canvas
I can draw rectangles by "fillRect".
const ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(300, 50, 3000, 500);
And I also can remove them by "clearRect".
const ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.clearRect(300, 50, 3000, 500);
One problem is they don't distinguish between the PDF image and the rectangle.
So if I execute "fillRect" and "clearRect" several time, the image will be like this.
Thus, I add another canvas to draw rectangles.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link rel="stylesheet" href="../css/main.page.css" />
</head>
<body style="background: white;">
<button id="change_page">Change</button>
<div>
<button id="save_button">Click</button>
<button onclick="Page.crop()">Crop</button>
<div id="area">
<div class="draw_canvas_area">
<canvas id="image_canvas"></canvas>
<canvas id="cropbox_canvas"></canvas>
</div>
<div id="cropped_image_area"><canvas id="cropped_image_canvas"></canvas></div>
</div>
</div>
<script src="../js/clients/main.page.js"></script>
</body>
</html>
main.page.css
.draw_canvas_area{
border: 1px black solid;
position: absolute;
height: 90vh;
width: 90vw;
overflow: auto;
}
.draw_canvas_area canvas {
position: absolute;
}
#cropped_image_area {
display: none;
}
main.page.ts
import { ImageEditor } from "./imageEditor";
let imageEditor = new ImageEditor();
...
export async function load(filePath: string) {
const pageCount = await imageEditor.loadDocument(filePath);
if(pageCount > 0) {
await imageEditor.loadPage(1);
// draw a rectangle for two times
imageEditor.drawRectangle({x: 300, y: 50, width: 3000, height: 500});
imageEditor.drawRectangle({x: 400, y: 500, width: 200, height: 1200});
}
}
imageEditor.ts
import * as pdf from 'pdfjs-dist';
import { SizeConverter } from './sizeConverter';
import { PDFDocumentProxy } from 'pdfjs-dist/types/display/api';
type Rect = {
x: number,
y: number,
width: number,
height: number
};
const defaultCanvasDpi = 72;
export class ImageEditor {
private imageCanvas: HTMLCanvasElement;
private rectangleCanvas: HTMLCanvasElement;
private pdfDocument: PDFDocumentProxy|null = null;
private lastRectangle: Rect|null = null;
public constructor() {
this.imageCanvas = document.getElementById('image_canvas') as HTMLCanvasElement;
this.rectangleCanvas = document.getElementById('cropbox_canvas') as HTMLCanvasElement;
}
public async loadDocument(filePath: string): Promise<number> {
pdf.GlobalWorkerOptions.workerSrc =
'../node_modules/pdfjs-dist/build/pdf.worker.js';
this.pdfDocument = await pdf.getDocument(filePath).promise;
if(this.pdfDocument == null) {
console.error('failed loading document');
return 0;
}
return this.pdfDocument.numPages;
}
public async loadPage(pageNumber: number): Promise<boolean> {
if(this.pdfDocument == null) {
return false;
}
const pdfPage = await this.pdfDocument.getPage(pageNumber);
if(pdfPage == null) {
return false;
}
// Display page on the existing canvas with 100% scale.
const viewport = pdfPage.getViewport({ scale: 1.0, rotation: 0 });
const horizontalMm = SizeConverter.ConvertFromPxToMm(viewport.width, defaultCanvasDpi);
const verticalMm = SizeConverter.ConvertFromPxToMm(viewport.height, defaultCanvasDpi);
const actualWidth = SizeConverter.ConvertFromMmToPx(horizontalMm, 300);
const actualHeight = SizeConverter.ConvertFromMmToPx(verticalMm, 300);
this.imageCanvas.width = actualWidth;
this.imageCanvas.height = actualHeight;
this.imageCanvas.style.width = `${viewport.width}px`;
this.imageCanvas.style.height = `${viewport.height}px`;
this.rectangleCanvas.width = actualWidth;
this.rectangleCanvas.height = actualHeight;
this.rectangleCanvas.style.width = `${viewport.width}px`;
this.rectangleCanvas.style.height = `${viewport.height}px`;
const scale = Math.min(actualWidth / viewport.width, actualHeight / viewport.height);
const ctx = this.imageCanvas.getContext("2d") as CanvasRenderingContext2D;
await pdfPage.render({
canvasContext: ctx,
viewport: pdfPage.getViewport({ scale: scale, rotation: 0 }),
}).promise;
return true;
}
public drawRectangle(rect: Rect) {
this.clearRectangle();
const ctx = this.rectangleCanvas.getContext("2d") as CanvasRenderingContext2D;
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(rect.x, rect.y,
rect.width, rect.height);
this.lastRectangle = rect;
}
public clearRectangle() {
if(this.lastRectangle == null) {
return;
}
const ctx = this.rectangleCanvas.getContext("2d") as CanvasRenderingContext2D;
ctx.clearRect(this.lastRectangle.x, this.lastRectangle.y,
this.lastRectangle.width, this.lastRectangle.height);
this.lastRectangle = null;
}
}
Result
Crop the image what is gotten from PDF as same as the rectangle size
Because I can't crop Canvas image directly.
I have to create an image and use "drawImage" to crop.
imageEditor.ts
...
export class ImageEditor {
private imageCanvas: HTMLCanvasElement;
private rectangleCanvas: HTMLCanvasElement;
private croppedImageCanvas: HTMLCanvasElement;
private pdfDocument: PDFDocumentProxy|null = null;
private lastRectangle: Rect|null = null;
public constructor() {
...
this.croppedImageCanvas = document.getElementById('cropped_image_canvas') as HTMLCanvasElement;
}
...
public crop() {
if(this.lastRectangle == null) {
console.error("no rectangle");
return;
}
// Convert Canvas image to Image
const newImage = new Image();
newImage.onload = _ => {
if(this.lastRectangle == null) {
console.error("no rectangle");
return;
}
// scale Canvas display size
const horizontalMm = SizeConverter.ConvertFromPxToMm(this.lastRectangle.width, 300);
const verticalMm = SizeConverter.ConvertFromPxToMm(this.lastRectangle.height, 300);
const scaledWidth = SizeConverter.ConvertFromMmToPx(horizontalMm, defaultCanvasDpi);
const scaledHeight = SizeConverter.ConvertFromMmToPx(verticalMm, defaultCanvasDpi);
this.croppedImageCanvas.width = this.lastRectangle.width ;
this.croppedImageCanvas.height = this.lastRectangle.height;
this.croppedImageCanvas.style.width = `${scaledWidth}px`;
this.croppedImageCanvas.style.height = `${scaledHeight}px`;
const ctx = this.croppedImageCanvas.getContext("2d") as CanvasRenderingContext2D;
// in "drawImage", I can't use scaled values because Canvas has already scaled.
ctx.drawImage(newImage, 0, 0, this.imageCanvas.width, this.imageCanvas.height,
-this.lastRectangle.x, -this.lastRectangle.y, this.imageCanvas.width, this.imageCanvas.height);
};
newImage.src = this.imageCanvas.toDataURL('image/png', 1);
}
}
Result
Before
After
3. Save cropped image
Because the cropped image also drawn on a Canvas.
So I can save it as same as last time.
Top comments (0)