From 7ab6a2c4c9c88c73f416946625b411e3579f4245 Mon Sep 17 00:00:00 2001
From: Gustavo Trott <gustavo@trott.com.br>
Date: Wed, 28 Apr 2021 15:47:55 -0300
Subject: [PATCH] Detects if PDF contains font Type 3 and rasterize
 automatically

---
 .../handlers/AbstractCommandHandler.java      |  4 ++
 .../handlers/PdfFontType3DetectorHandler.java | 42 ++++++++++++++++++
 .../presentation/imp/SvgImageCreatorImp.java  | 43 +++++++++++++++++--
 3 files changed, 86 insertions(+), 3 deletions(-)
 create mode 100644 bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/PdfFontType3DetectorHandler.java

diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/AbstractCommandHandler.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/AbstractCommandHandler.java
index d15172569b..fe0e8c95d9 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/AbstractCommandHandler.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/AbstractCommandHandler.java
@@ -82,6 +82,10 @@ public abstract class AbstractCommandHandler extends
     return stdoutBuilder.indexOf(value) > -1;
   }
 
+  protected Boolean stdoutEquals(String value) {
+    return stdoutBuilder.toString().trim().equals(value);
+  }
+
   protected Boolean stderrContains(String value) {
     return stderrBuilder.indexOf(value) > -1;
   }
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/PdfFontType3DetectorHandler.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/PdfFontType3DetectorHandler.java
new file mode 100644
index 0000000000..5d3e04750f
--- /dev/null
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/handlers/PdfFontType3DetectorHandler.java
@@ -0,0 +1,42 @@
+/**
+ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+ * 
+ * Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation; either version 3.0 of the License, or (at your option) any later
+ * version.
+ * 
+ * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along
+ * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.bigbluebutton.presentation.handlers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PdfFontType3DetectorHandler extends AbstractCommandHandler {
+
+  private static Logger log = LoggerFactory
+      .getLogger(PdfFontType3DetectorHandler.class);
+
+  /**
+   *
+   * @return If pdf page contains one or more texts with font Type 3.
+   */
+  public boolean hasFontType3() {
+    if (stdoutEquals("1")) {
+      return true;
+    }
+
+    return false;
+  }
+
+}
diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
index 50e654e902..5cb07b8ffb 100755
--- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
+++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/SvgImageCreatorImp.java
@@ -16,6 +16,7 @@ import org.bigbluebutton.presentation.handlers.AddNamespaceToSvgHandler;
 import org.bigbluebutton.presentation.handlers.Pdf2PngPageConverterHandler;
 import org.bigbluebutton.presentation.handlers.Png2SvgConversionHandler;
 import org.bigbluebutton.presentation.handlers.SvgConversionHandler;
+import org.bigbluebutton.presentation.handlers.PdfFontType3DetectorHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,6 +60,7 @@ public class SvgImageCreatorImp implements SvgImageCreator {
 
         int numSlides = 1;
         boolean done = false;
+        Boolean rasterizeCurrSlide = this.forceRasterizeSlides;
 
         // Convert single image file
         if (SupportedFileTypes.isImageFile(pres.getFileType())) {
@@ -93,11 +95,38 @@ public class SvgImageCreatorImp implements SvgImageCreator {
         // Continue image processing
         long startConv = System.currentTimeMillis();
 
+
+        //Detect if PDF contains text with font Type 3
+        //Pdftocairo has problem to convert Pdf to Svg when text contains font Type 3
+        //Case detects type 3, rasterize will be forced to avoid the problem
+        NuProcessBuilder detectFontType3Process = this.createDetectFontType3Process(source,page);
+        PdfFontType3DetectorHandler detectFontType3tHandler = new PdfFontType3DetectorHandler();
+        detectFontType3Process.setProcessListener(detectFontType3tHandler);
+
+        NuProcess processDetectFontType3 = detectFontType3Process.start();
+        try {
+            processDetectFontType3.waitFor(convPdfToSvgTimeout + 1, TimeUnit.SECONDS);
+            done = true;
+        } catch (InterruptedException e) {
+            done = false;
+            log.error("InterruptedException while verifing font type 3 on {} page {}: {}", pres.getName(), page, e);
+        }
+
+        if(detectFontType3tHandler.isCommandTimeout()) {
+            log.error("Command execution (detectFontType3) exceeded the {} secs timeout for {} page {}.", convPdfToSvgTimeout, pres.getName(), page);
+        }
+
+        if(detectFontType3tHandler.hasFontType3()) {
+            log.info("Font Type 3 identified on {} page {}, slide will be rasterized.", pres.getName(), page);
+            rasterizeCurrSlide = true;
+        }
+
+
         File destsvg = new File(imagePresentationDir.getAbsolutePath() + File.separatorChar + "slide" + page + ".svg");
 
         SvgConversionHandler pHandler = new SvgConversionHandler();
 
-        if(this.forceRasterizeSlides == false) {
+        if(rasterizeCurrSlide == false) {
             NuProcessBuilder convertPdfToSvg = createConversionProcess("-svg", page, source, destsvg.getAbsolutePath(),
                     true);
 
@@ -124,7 +153,7 @@ public class SvgImageCreatorImp implements SvgImageCreator {
         if (destsvg.length() == 0 ||
                 pHandler.numberOfImageTags() > imageTagThreshold ||
                 pHandler.numberOfPaths() > pathsThreshold ||
-                this.forceRasterizeSlides) {
+                rasterizeCurrSlide) {
 
             // We need t delete the destination file as we are starting a
             // new conversion process
@@ -134,7 +163,7 @@ public class SvgImageCreatorImp implements SvgImageCreator {
 
             done = false;
 
-            if(!this.forceRasterizeSlides) {
+            if(!rasterizeCurrSlide) {
                 Map<String, Object> logData = new HashMap<String, Object>();
                 logData.put("meetingId", pres.getMeetingId());
                 logData.put("presId", pres.getId());
@@ -282,6 +311,14 @@ public class SvgImageCreatorImp implements SvgImageCreator {
         return new NuProcessBuilder(Arrays.asList("timeout", convPdfToSvgTimeout + "s", "/bin/sh", "-c", rawCommand));
     }
 
+    private NuProcessBuilder createDetectFontType3Process(String source, int page) {
+        String rawCommand  = "pdffonts -f " + String.valueOf(page) + " -l " + String.valueOf(page) + " " + source;
+        rawCommand += " | grep -m 1 'Type 3'";
+        rawCommand += " | wc -l";
+
+        return new NuProcessBuilder(Arrays.asList("timeout", convPdfToSvgTimeout + "s", "/bin/sh", "-c", rawCommand));
+    }
+
     private File determineSvgImagesDirectory(File presentationFile) {
         return new File(presentationFile.getParent() + File.separatorChar + "svgs");
     }
-- 
GitLab