web_reader / build /services /canvas.js
Mohammad Shahid
Include pre-built files for HF deployment
f316cce
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CanvasService = void 0;
const tsyringe_1 = require("tsyringe");
const civkit_1 = require("civkit");
const promises_1 = require("fs/promises");
const logger_1 = require("./logger");
const temp_file_1 = require("./temp-file");
const worker_threads_1 = require("worker_threads");
const path_1 = __importDefault(require("path"));
const threaded_1 = require("./threaded");
const downloadFile = async (uri) => {
const resp = await fetch(uri);
if (!(resp.ok && resp.body)) {
throw new Error(`Unexpected response ${resp.statusText}`);
}
const contentLength = parseInt(resp.headers.get('content-length') || '0');
if (contentLength > 1024 * 1024 * 100) {
throw new Error('File too large');
}
const buff = await resp.arrayBuffer();
return { buff, contentType: resp.headers.get('content-type') };
};
let CanvasService = class CanvasService extends civkit_1.AsyncService {
constructor(temp, globalLogger) {
super(...arguments);
this.temp = temp;
this.globalLogger = globalLogger;
this.logger = this.globalLogger.child({ service: this.constructor.name });
}
async init() {
await this.dependencyReady();
if (!worker_threads_1.isMainThread) {
const { createSvg2png, initialize } = require('svg2png-wasm');
const wasmBuff = await (0, promises_1.readFile)(path_1.default.resolve(path_1.default.dirname(require.resolve('svg2png-wasm')), '../svg2png_wasm_bg.wasm'));
const fontBuff = await (0, promises_1.readFile)(path_1.default.resolve(__dirname, '../../licensed/SourceHanSansSC-Regular.otf'));
await initialize(wasmBuff);
this.svg2png = createSvg2png({
fonts: [Uint8Array.from(fontBuff)],
defaultFontFamily: {
serifFamily: 'Source Han Sans SC',
sansSerifFamily: 'Source Han Sans SC',
cursiveFamily: 'Source Han Sans SC',
fantasyFamily: 'Source Han Sans SC',
monospaceFamily: 'Source Han Sans SC',
}
});
}
this.canvas = require('@napi-rs/canvas');
this.emit('ready');
}
async renderSvgToPng(svgContent) {
return this.svg2png(svgContent, { backgroundColor: '#D3D3D3' });
}
async _loadImage(input) {
let buff;
let contentType;
do {
if (typeof input === 'string') {
if (input.startsWith('data:')) {
const firstComma = input.indexOf(',');
const header = input.slice(0, firstComma);
const data = input.slice(firstComma + 1);
const encoding = header.split(';')[1];
contentType = header.split(';')[0].split(':')[1];
if (encoding?.startsWith('base64')) {
buff = Buffer.from(data, 'base64');
}
else {
buff = Buffer.from(decodeURIComponent(data), 'utf-8');
}
break;
}
if (input.startsWith('http')) {
const r = await downloadFile(input);
buff = Buffer.from(r.buff);
contentType = r.contentType;
break;
}
}
if (Buffer.isBuffer(input)) {
buff = input;
const mime = await (0, civkit_1.mimeOf)(buff);
contentType = `${mime.mediaType}/${mime.subType}`;
break;
}
throw new civkit_1.ParamValidationError('Invalid input');
} while (false);
if (!buff) {
throw new civkit_1.ParamValidationError('Invalid input');
}
if (contentType?.includes('svg')) {
buff = await this.renderSvgToPng(buff.toString('utf-8'));
}
const img = await this.canvas.loadImage(buff);
Reflect.set(img, 'contentType', contentType);
return img;
}
async loadImage(uri) {
const t0 = Date.now();
try {
const theImage = await this._loadImage(uri);
const t1 = Date.now();
this.logger.debug(`Image loaded in ${t1 - t0}ms`);
return theImage;
}
catch (err) {
if (err?.message?.includes('Unsupported image type') || err?.message?.includes('unsupported')) {
this.logger.warn(`Failed to load image ${uri.slice(0, 128)}`, { err });
throw new civkit_1.SubmittedDataMalformedError(`Unknown image format for ${uri.slice(0, 128)}`);
}
throw err;
}
}
fitImageToSquareBox(image, size = 1024) {
// this.logger.debug(`Fitting image(${ image.width }x${ image.height }) to ${ size } box`);
// const t0 = Date.now();
if (image.width <= size && image.height <= size) {
if (image instanceof this.canvas.Canvas) {
return image;
}
const canvasInstance = this.canvas.createCanvas(image.width, image.height);
const ctx = canvasInstance.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvasInstance.width, canvasInstance.height);
// this.logger.debug(`No need to resize, copied to canvas in ${ Date.now() - t0 } ms`);
return canvasInstance;
}
const aspectRatio = image.width / image.height;
const resizedWidth = Math.round(aspectRatio > 1 ? size : size * aspectRatio);
const resizedHeight = Math.round(aspectRatio > 1 ? size / aspectRatio : size);
const canvasInstance = this.canvas.createCanvas(resizedWidth, resizedHeight);
const ctx = canvasInstance.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, resizedWidth, resizedHeight);
// this.logger.debug(`Resized to ${ resizedWidth }x${ resizedHeight } in ${ Date.now() - t0 } ms`);
return canvasInstance;
}
corpImage(image, x, y, w, h) {
// this.logger.debug(`Cropping image(${ image.width }x${ image.height }) to ${ w }x${ h } at ${ x },${ y } `);
// const t0 = Date.now();
const canvasInstance = this.canvas.createCanvas(w, h);
const ctx = canvasInstance.getContext('2d');
ctx.drawImage(image, x, y, w, h, 0, 0, w, h);
// this.logger.debug(`Crop complete in ${ Date.now() - t0 } ms`);
return canvasInstance;
}
canvasToDataUrl(canvas, mimeType) {
// this.logger.debug(`Exporting canvas(${ canvas.width }x${ canvas.height })`);
// const t0 = Date.now();
return canvas.toDataURLAsync((mimeType || 'image/png'));
}
async canvasToBuffer(canvas, mimeType) {
// this.logger.debug(`Exporting canvas(${ canvas.width }x${ canvas.height })`);
// const t0 = Date.now();
return canvas.toBuffer((mimeType || 'image/png'));
}
};
exports.CanvasService = CanvasService;
__decorate([
(0, threaded_1.Threaded)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], CanvasService.prototype, "renderSvgToPng", null);
exports.CanvasService = CanvasService = __decorate([
(0, tsyringe_1.singleton)(),
__metadata("design:paramtypes", [temp_file_1.TempFileManager,
logger_1.GlobalLogger])
], CanvasService);
const instance = tsyringe_1.container.resolve(CanvasService);
exports.default = instance;
//# sourceMappingURL=canvas.js.map