[TypeScript] 이미지 편집
45659 단어 typescript
소개
이번에는 고해상도 이미지를 저장하고 이미지를 편집해보도록 하겠습니다.
이미지를 고해상도로 저장
지난번에는 PDF에서 이미지를 가져와서 저장했습니다.
그러나 그들은 96 DPI로 저장되었습니다.
Canvas.toBlob()이 96 DPI로 설정되었기 때문입니다.
더 높은 해상도로 저장하기 위해 "dpi-tools"를 사용했습니다.
d.ts
TypeScript 코드에서 "dpi-tools"를 사용하려고 했을 때 문제가 발생했습니다.
Type Declaration 파일이 없었기 때문에 가져올 수 없었습니다.
그래서 Type Declaration 파일을 직접 추가했습니다.
먼저 아래와 같은 파일을 만들었습니다.
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;
그리고 "node_modules/@types/dpi-tools"디렉토리에 넣었습니다.
이제 "dpi-tools"를 가져올 수 있습니다 :)
이미지 저장
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);
}
결과
사용자 지정 d.ts 파일 저장
업데이트 2021-04-21
node_modules에 index.d.ts 파일이 있으면 안 됩니다.
npm으로 다른 패키지를 설치하면 사용자 지정 d.ts 파일이 삭제되기 때문입니다.
그래서 나는 그것들을 내 프로젝트에서 만든 "types"폴더로 옮겼습니다.
그리고 tsconfig.js를 변경했습니다.
tsconfig.js
...
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {
"*": ["*", "types/*"]
},
...
직사각형 그리기 및 이미지 자르기
1. 캔버스에 사각형 그리기
"fillRect"로 사각형을 그릴 수 있습니다.
const ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(300, 50, 3000, 500);
그리고 "clearRect"로 제거할 수도 있습니다.
const ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.clearRect(300, 50, 3000, 500);
한 가지 문제는 PDF 이미지와 사각형을 구분하지 않는다는 것입니다.
그래서 "fillRect"와 "clearRect"를 여러번 실행하면 이런 이미지가 됩니다.
따라서 사각형을 그리기 위해 다른 캔버스를 추가합니다.
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;
}
}
결과
PDF에서 가져온 이미지를 직사각형 크기와 동일하게 자르기
캔버스 이미지를 직접 자를 수 없기 때문입니다.
이미지를 만들고 자르기 위해 "drawImage"를 사용해야 합니다.
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);
}
}
결과
전에
후에
3. 자른 이미지 저장
자른 이미지도 캔버스에 그려지기 때문입니다.
그래서 나는 그것을 .
Reference
이 문제에 관하여([TypeScript] 이미지 편집), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/masanori_msl/typescript-edit-images-6n4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)