Spaces:
Build error
Build error
| ; | |
| 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 |