From 61b5e5d0b98db2ec6f4423b415bb741be986fb06 Mon Sep 17 00:00:00 2001 From: Sergey Konovalov Date: Wed, 26 Jun 2024 20:11:39 +0300 Subject: [PATCH] [bug] Add tiff to supported image formats; for bug 45401 --- Common/config/default.json | 2 +- DocService/sources/canvasservice.js | 5 + DocService/sources/fileuploaderservice.js | 5 + DocService/sources/utilsDocService.js | 35 ++++-- package.json | 1 + tests/perf/convertImageToPng.js | 127 ++++++++++++++++++++++ tests/perf/fixImageExifRotation.js | 6 +- 7 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 tests/perf/convertImageToPng.js diff --git a/Common/config/default.json b/Common/config/default.json index 57d1c12b0..a65e83990 100644 --- a/Common/config/default.json +++ b/Common/config/default.json @@ -188,7 +188,7 @@ "utils": { "utils_common_fontdir": "null", "utils_fonts_search_patterns": "*.ttf;*.ttc;*.otf", - "limits_image_types_upload": "jpg;jpeg;jpe;png;gif;bmp;svg" + "limits_image_types_upload": "jpg;jpeg;jpe;png;gif;bmp;svg;tiff;tif" }, "sql": { "type": "postgres", diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index 1b10d91fe..342447ab4 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -769,6 +769,11 @@ function* commandImgurls(ctx, conn, cmd, outputData) { } } if (isAllow) { + if (format === constants.AVS_OFFICESTUDIO_FILE_IMAGE_TIFF) { + data = yield utilsDocService.convertImageToPng(ctx, data); + format = constants.AVS_OFFICESTUDIO_FILE_IMAGE_PNG; + formatStr = formatChecker.getStringFromFormat(format); + } let strLocalPath = 'media/' + crypto.randomBytes(16).toString("hex") + '_'; if (urlParsed) { var urlBasename = pathModule.basename(urlParsed.pathname); diff --git a/DocService/sources/fileuploaderservice.js b/DocService/sources/fileuploaderservice.js index c843ae0d5..15b90a051 100644 --- a/DocService/sources/fileuploaderservice.js +++ b/DocService/sources/fileuploaderservice.js @@ -252,6 +252,11 @@ exports.uploadImageFile = function(req, res) { var supportedFormats = tenTypesUpload || 'jpg'; let formatLimit = formatStr && -1 !== supportedFormats.indexOf(formatStr); if (formatLimit) { + if (format === constants.AVS_OFFICESTUDIO_FILE_IMAGE_TIFF) { + buffer = yield utilsDocService.convertImageToPng(ctx, buffer); + format = constants.AVS_OFFICESTUDIO_FILE_IMAGE_PNG; + formatStr = formatChecker.getStringFromFormat(format); + } //a hash is written at the beginning to avoid errors during parallel upload in co-editing var strImageName = crypto.randomBytes(16).toString("hex"); var strPathRel = 'media/' + strImageName + '.' + formatStr; diff --git a/DocService/sources/utilsDocService.js b/DocService/sources/utilsDocService.js index 378b357d8..c2d3ee474 100644 --- a/DocService/sources/utilsDocService.js +++ b/DocService/sources/utilsDocService.js @@ -46,20 +46,32 @@ async function fixImageExifRotation(ctx, buffer) { let exif = parser.parse(); if (exif.tags?.Orientation > 1) { ctx.logger.debug('fixImageExifRotation remove exif and rotate:%j', exif); - let image = await Jimp.read(buffer); - //remove exif - image.bitmap.exifBuffer = undefined; - //set jpeg and png quality - //https://www.imagemagick.org/script/command-line-options.php#quality - image.quality(90); - image.deflateLevel(7); - buffer = await image.getBufferAsync(Jimp.AUTO); + buffer = convertImageTo(ctx, buffer, Jimp.AUTO); } } catch (e) { ctx.logger.debug('fixImageExifRotation error:%s', e.stack); } return buffer; } +async function convertImageToPng(ctx, buffer) { + return await convertImageTo(ctx, buffer, Jimp.MIME_PNG); +} +async function convertImageTo(ctx, buffer, mime) { + try { + ctx.logger.debug('convertImageTo %s', mime); + let image = await Jimp.read(buffer); + //remove exif + image.bitmap.exifBuffer = undefined; + //set jpeg and png quality + //https://www.imagemagick.org/script/command-line-options.php#quality + image.quality(90); + image.deflateLevel(7); + buffer = await image.getBufferAsync(mime); + } catch (e) { + ctx.logger.debug('convertImageTo error:%s', e.stack); + } + return buffer; +} /** * * @param {string} lang @@ -70,7 +82,6 @@ function localeToLCID(lang) { return elem && elem.id; } -module.exports = { - fixImageExifRotation, - localeToLCID -}; \ No newline at end of file +module.exports.fixImageExifRotation = fixImageExifRotation; +module.exports.convertImageToPng = convertImageToPng; +module.exports.localeToLCID = localeToLCID; \ No newline at end of file diff --git a/package.json b/package.json index 33b596698..78c3c50d4 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "scripts": { "perf-expired": "cd ./DocService&& cross-env NODE_ENV=development-windows NODE_CONFIG_DIR=../Common/config node ../tests/perf/checkFileExpire.js", "perf-exif": "cd ./DocService&& cross-env NODE_ENV=development-windows NODE_CONFIG_DIR=../Common/config node ../tests/perf/fixImageExifRotation.js", + "perf-png": "cd ./DocService&& cross-env NODE_ENV=development-windows NODE_CONFIG_DIR=../Common/config node ../tests/perf/convertImageToPng.js", "unit tests": "cd ./DocService && jest unit --inject-globals=false --config=../tests/jest.config.js", "integration tests with server instance": "cd ./DocService && jest integration/withServerInstance --inject-globals=false --config=../tests/jest.config.js", "integration database tests": "cd ./DocService && jest integration/databaseTests --inject-globals=false --config=../tests/jest.config.js", diff --git a/tests/perf/convertImageToPng.js b/tests/perf/convertImageToPng.js new file mode 100644 index 000000000..2e839d65f --- /dev/null +++ b/tests/perf/convertImageToPng.js @@ -0,0 +1,127 @@ +/* + * (c) Copyright Ascensio System SIA 2010-2023 + * + * This program is a free software product. You can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License (AGPL) + * version 3 as published by the Free Software Foundation. In accordance with + * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect + * that Ascensio System SIA expressly excludes the warranty of non-infringement + * of any third-party rights. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For + * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html + * + * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish + * street, Riga, Latvia, EU, LV-1050. + * + * The interactive user interfaces in modified source and object code versions + * of the Program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU AGPL version 3. + * + * Pursuant to Section 7(b) of the License you must retain the original Product + * logo when distributing the program. Pursuant to Section 7(e) we decline to + * grant you any rights under trademark law for use of our trademarks. + * + * All the Product's GUI elements, including illustrations and icon sets, as + * well as technical writing content are licensed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International. See the License + * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + * + */ + +'use strict'; + +const { + createHistogram, + performance, + PerformanceObserver, +} = require('node:perf_hooks'); + +const { readdir, mkdir, readFile, writeFile } = require("node:fs/promises"); +const path = require("path"); +// const Jimp = require('Jimp'); +const utils = require('./../../Common/sources/utils'); +const operationContext = require('./../../Common/sources/operationContext'); +const utilsDocService = require("./../../DocService/sources/utilsDocService"); + +let ctx = operationContext.global; + +let histograms = {}; + +async function beforeStart() { + let timerify = function (func) { + let histogram = createHistogram(); + histograms[func.name] = histogram; + return performance.timerify(func, {histogram: histogram}); + } + utilsDocService.convertImageToPng = timerify(utilsDocService.convertImageToPng); + // Jimp.read = timerify(Jimp.read); + + const obs = new PerformanceObserver((list) => { + const entries = list.getEntries(); + entries.forEach((entry) => { + let duration = Math.round(entry.duration * 1000) / 1000; + console.log(`${entry.name}:${duration}ms`); + }); + }); + obs.observe({ entryTypes: ['function']}); +} + +async function beforeEnd() { + let logHistogram = function (histogram, name) { + let mean = Math.round(histogram.mean / 1000) / 1000; + let min = Math.round(histogram.min / 1000) / 1000; + let max = Math.round(histogram.max / 1000) / 1000; + let count = histogram.count; + ctx.logger.info(`histogram ${name}: count=${count}, mean=${mean}ms, min=${min}ms, max=${max}ms`); + } + await utils.sleep(1000); + for (let name in histograms) { + logHistogram(histograms[name], name); + } +} + +async function fixInDir(dirIn, dirOut) { + ctx.logger.info("dirIn:%s", dirIn); + ctx.logger.info("dirOut:%s", dirOut); + let dirents = await readdir(dirIn, {withFileTypes : true, recursive: true}); + for (let dirent of dirents) { + if (dirent.isFile()) { + let file = dirent.name; + ctx.logger.info("fixInDir:%s", file); + let buffer = await readFile(path.join(dirent.path, file)); + let bufferNew = await utilsDocService.convertImageToPng(ctx, buffer); + if (buffer !== bufferNew) { + let outputPath = path.join(dirOut, dirent.path.substring(dirIn.length), path.basename(file, path.extname(file)) + '.png'); + await mkdir(path.dirname(outputPath), {recursive: true}); + await writeFile(outputPath, bufferNew); + } + } + } +} + +async function startTest() { + let args = process.argv.slice(2); + if (args.length < 2) { + ctx.logger.error('missing arguments.USAGE: convertImageToPng.js "dirIn" "dirOut"'); + return; + } + ctx.logger.info("test started"); + await beforeStart(); + + + await fixInDir(args[0], args[1]); + + await beforeEnd(); + ctx.logger.info("test finished"); +} + +startTest().then(()=>{ + //delay to log observer events + return utils.sleep(1000); +}).catch((err) => { + ctx.logger.error(err.stack); +}).finally(() => { + process.exit(0); +}); diff --git a/tests/perf/fixImageExifRotation.js b/tests/perf/fixImageExifRotation.js index f2dd2ba33..0c7ad28e2 100644 --- a/tests/perf/fixImageExifRotation.js +++ b/tests/perf/fixImageExifRotation.js @@ -43,7 +43,7 @@ const path = require("path"); // const Jimp = require('Jimp'); const utils = require('./../../Common/sources/utils'); const operationContext = require('./../../Common/sources/operationContext'); -const docsCoServer = require("./../../DocService/sources/DocsCoServer"); +const utilsDocService = require("./../../DocService/sources/utilsDocService"); let ctx = operationContext.global; @@ -55,7 +55,7 @@ async function beforeStart() { histograms[func.name] = histogram; return performance.timerify(func, {histogram: histogram}); } - docsCoServer.fixImageExifRotation = timerify(docsCoServer.fixImageExifRotation); + utilsDocService.fixImageExifRotation = timerify(utilsDocService.fixImageExifRotation); // Jimp.read = timerify(Jimp.read); const obs = new PerformanceObserver((list) => { @@ -91,7 +91,7 @@ async function fixInDir(dirIn, dirOut) { let file = dirent.name; ctx.logger.info("fixInDir:%s", file); let buffer = await readFile(path.join(dirent.path, file)); - let bufferNew = await docsCoServer.fixImageExifRotation(ctx, buffer); + let bufferNew = await utilsDocService.fixImageExifRotation(ctx, buffer); if (buffer !== bufferNew) { let outputPath = path.join(dirOut, dirent.path.substring(dirIn.length), file); await mkdir(path.dirname(outputPath), {recursive: true});