From a3935bb90ad885a0c819d6760953de6066a2e6cf Mon Sep 17 00:00:00 2001 From: Daniel Petri Rocha Date: Wed, 27 Jul 2022 20:54:16 +0200 Subject: [PATCH] Implement SonarCloud generic fix --- bbb-export-annotations/.eslintrc.js | 2 +- bbb-export-annotations/config/settings.json | 6 +- bbb-export-annotations/workers/collector.js | 7 +- bbb-export-annotations/workers/notifier.js | 6 +- bbb-export-annotations/workers/process.js | 73 ++++++++++----------- 5 files changed, 45 insertions(+), 49 deletions(-) diff --git a/bbb-export-annotations/.eslintrc.js b/bbb-export-annotations/.eslintrc.js index f8e1965746..f0220a73e5 100644 --- a/bbb-export-annotations/.eslintrc.js +++ b/bbb-export-annotations/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { 'node': true, }, 'extends': [ - 'eslint:recommended', + 'google', ], 'parserOptions': { 'ecmaVersion': 'latest', diff --git a/bbb-export-annotations/config/settings.json b/bbb-export-annotations/config/settings.json index 2eb6f24c35..138961c510 100644 --- a/bbb-export-annotations/config/settings.json +++ b/bbb-export-annotations/config/settings.json @@ -4,7 +4,11 @@ }, "shared": { "presDir": "/var/bigbluebutton", - "presAnnDropboxDir": "/tmp/pres-ann-dropbox" + "presAnnDropboxDir": "/tmp/pres-ann-dropbox", + "cairosvg": "/usr/bin/cairosvg", + "ghostscript": "/usr/bin/gs", + "imagemagick": "/usr/bin/convert", + "pdftocairo": "/usr/bin/pdftocairo" }, "collector": { "pngWidthRasterizedSlides": 2560 diff --git a/bbb-export-annotations/workers/collector.js b/bbb-export-annotations/workers/collector.js index 9618561306..02e42d3f13 100644 --- a/bbb-export-annotations/workers/collector.js +++ b/bbb-export-annotations/workers/collector.js @@ -4,7 +4,7 @@ const fs = require('fs'); const redis = require('redis'); const {Worker, workerData, parentPort} = require('worker_threads'); const path = require('path'); -const {execSync} = require('child_process'); +const cp = require('child_process'); const jobId = workerData; @@ -77,7 +77,6 @@ const exportJob = JSON.parse(job); // so it matches what was shown in the browser. const extract_png_from_pdf = [ - 'pdftocairo', '-png', '-f', pageNumber, '-l', pageNumber, @@ -85,9 +84,9 @@ const exportJob = JSON.parse(job); '-singlefile', '-cropbox', pdfFile, outputFile, - ].join(' '); + ]; - execSync(extract_png_from_pdf); + cp.spawnSync(config.shared.pdftocairo, extract_png_from_pdf, {shell: false}); } // If PNG file already available } else if (fs.existsSync(`${presFile}.png`)) { diff --git a/bbb-export-annotations/workers/notifier.js b/bbb-export-annotations/workers/notifier.js index de143ba495..5c77ae9369 100644 --- a/bbb-export-annotations/workers/notifier.js +++ b/bbb-export-annotations/workers/notifier.js @@ -8,7 +8,7 @@ const path = require('path'); const {workerData, parentPort} = require('worker_threads'); -const [jobType, jobId, filename] = workerData; +const [jobType, jobId, filename_with_extension] = workerData; const logger = new Logger('presAnn Notifier Worker'); @@ -27,7 +27,7 @@ async function notifyMeetingActor() { await client.connect(); client.on('error', (err) => logger.info('Redis Client Error', err)); - const link = `${config.notifier.protocol}://${config.notifier.host}/bigbluebutton/presentation/${exportJob.parentMeetingId}/${exportJob.parentMeetingId}/${exportJob.presId}/pdf/${jobId}/${filename}.pdf`; + const link = `${config.notifier.protocol}://${config.notifier.host}/bigbluebutton/presentation/${exportJob.parentMeetingId}/${exportJob.parentMeetingId}/${exportJob.presId}/pdf/${jobId}/${filename_with_extension}`; const notification = { envelope: { name: config.notifier.msgName, @@ -59,7 +59,7 @@ async function notifyMeetingActor() { async function upload() { const callbackUrl = `http://${config.bbbWeb.host}:${config.bbbWeb.port}/bigbluebutton/presentation/${exportJob.presentationUploadToken}/upload`; const formData = new FormData(); - const file = `${exportJob.presLocation}/pdfs/${jobId}/${filename}.pdf`; + const file = `${exportJob.presLocation}/pdfs/${jobId}/${filename_with_extension}`; formData.append('conference', exportJob.parentMeetingId); formData.append('pod_id', config.notifier.pod_id); diff --git a/bbb-export-annotations/workers/process.js b/bbb-export-annotations/workers/process.js index a099c2b7c9..cdd7b06cf2 100644 --- a/bbb-export-annotations/workers/process.js +++ b/bbb-export-annotations/workers/process.js @@ -2,7 +2,7 @@ const Logger = require('../lib/utils/logger'); const config = require('../config'); const fs = require('fs'); const {create} = require('xmlbuilder2', {encoding: 'utf-8'}); -const {execSync} = require('child_process'); +const cp = require('child_process'); const {Worker, workerData, parentPort} = require('worker_threads'); const path = require('path'); const sanitize = require('sanitize-filename'); @@ -130,12 +130,13 @@ function to_px(pt) { // the escape-string-regexp npm package, and Pango markup. function escapeText(string) { return string + .replace(/[~`!.*+?%^${}()|[\]\\/]/g, '\\$&') + .replace(/-/g, '\\-') .replace(/&/g, '\\&') .replace(/'/g, '\\'') + .replace(/"/g, '\\"') .replace(/>/g, '\\>') - .replace(/${text}"`; @@ -152,19 +153,18 @@ function render_textbox(textColor, font, fontSize, textAlign, text, id, textBoxW textAlign = justify ? 'left' : textAlign; const commands = [ - 'convert', '-encoding', `${config.process.whiteboardTextEncoding}`, '-density', config.process.pixelsPerInch, - '-background', 'transparent', - size, - '-define', `pango:align=${textAlign}`, - '-define', `pango:justify=${justify}`, - '-define', 'pango:wrap=word-char', - pangoText, - path.join(dropbox, `text${id}.png`), - ].join(' '); + '-background', 'transparent'].concat(size, + [ + '-define', `pango:align=${textAlign}`, + '-define', `pango:justify=${justify}`, + '-define', 'pango:wrap=word-char', + pangoText, + path.join(dropbox, `text${id}.png`), + ]); - execSync(commands); + cp.spawnSync(config.shared.imagemagick, commands, {shell: false}); } function get_gap(dash, size) { @@ -244,7 +244,7 @@ function getPath(annotationPoints) { .map((strokePoint) => strokePoint.point); let [max_x, max_y] = [0, 0]; - const path = stroke.reduce( + const inner_path = stroke.reduce( (acc, [x0, y0], i, arr) => { if (!arr[i + 1]) return acc; const [x1, y1] = arr[i + 1]; @@ -261,8 +261,7 @@ function getPath(annotationPoints) { ['M', ...stroke[0], 'Q'], ); - path.join(' '); - return [path, max_x, max_y]; + return [inner_path, max_x, max_y]; } function getOutlinePath(annotationPoints) { @@ -273,7 +272,7 @@ function getOutlinePath(annotationPoints) { }); let [max_x, max_y] = [0, 0]; - const path = stroke.reduce( + const outline_path = stroke.reduce( (acc, [x0, y0], i, arr) => { const [x1, y1] = arr[(i + 1) % arr.length]; if (x1 >= max_x) { @@ -289,10 +288,9 @@ function getOutlinePath(annotationPoints) { ['M', ...stroke[0], 'Q'], ); - path.push('Z'); - path.join(' '); + outline_path.push('Z'); - return [path, max_x, max_y]; + return [outline_path, max_x, max_y]; } function circleFromThreePoints(A, B, C) { @@ -474,10 +472,9 @@ function overlay_arrow(svg, annotation) { function overlay_draw(svg, annotation) { const dash = annotation.style.dash; + const [calculated_path, max_x, max_y] = (dash == 'draw') ? getOutlinePath(annotation.points) : getPath(annotation.points); - const [path, max_x, max_y] = (dash == 'draw') ? getOutlinePath(annotation.points) : getPath(annotation.points); - - if (!path.length) return; + if (!calculated_path.length) return; const shapeColor = color_to_hex(annotation.style.color); const rotation = rad_to_degree(annotation.rotation); @@ -514,7 +511,7 @@ function overlay_draw(svg, annotation) { svg.ele('path', { style: `stroke:${shapeColor};stroke-width:${thickness};fill:${fill};${stroke_dasharray}`, - d: path, + d: calculated_path, transform: shapeTransform, }); } @@ -780,7 +777,7 @@ const exportJob = JSON.parse(job); const annotations = fs.readFileSync(path.join(dropbox, 'whiteboard')); const whiteboard = JSON.parse(annotations); const pages = JSON.parse(whiteboard.pages); -let ghostScriptInput = ''; +const ghostScriptInput = []; // 3. Convert annotations to SVG for (const currentSlide of pages) { @@ -848,16 +845,15 @@ for (const currentSlide of pages) { // generates with the original size in pt. const convertAnnotatedSlide = [ - 'cairosvg', SVGfile, '--output-width', to_px(slideWidth), '--output-height', to_px(slideHeight), '-o', PDFfile, - ].join(' '); + ]; - execSync(convertAnnotatedSlide); + cp.spawnSync(config.shared.cairosvg, convertAnnotatedSlide, {shell: false}); - ghostScriptInput += `${PDFfile} `; + ghostScriptInput.push(PDFfile); } // Create PDF output directory if it doesn't exist @@ -867,23 +863,20 @@ if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, {recursive: true}); } -const filename = sanitize(exportJob.filename.replace(/\s/g, '_')); +const filename_with_extension = `${sanitize(exportJob.filename.replace(/\s/g, '_'))}.pdf`; const mergePDFs = [ - 'gs', '-dNOPAUSE', '-sDEVICE=pdfwrite', - `-sOUTPUTFILE="${path.join(outputDir, `${filename}.pdf`)}"`, - `-dBATCH`, - ghostScriptInput, -].join(' '); + `-sOUTPUTFILE="${path.join(outputDir, filename_with_extension)}"`, + `-dBATCH`].concat(ghostScriptInput); // Resulting PDF file is stored in the presentation dir -execSync(mergePDFs); +cp.spawnSync(config.shared.ghostscript, mergePDFs, {shell: false}); // Launch Notifier Worker depending on job type -logger.info(`Saved PDF at ${outputDir}/${jobId}/${filename}.pdf`); +logger.info(`Saved PDF at ${outputDir}/${jobId}/${filename_with_extension}`); -kickOffNotifierWorker(exportJob.jobType, filename); +kickOffNotifierWorker(exportJob.jobType, filename_with_extension); parentPort.postMessage({message: workerData});