Add print modes for PostScript level 3 + Type 42 fonts.

For use with FPDF_SetPrintMode(), in both regular and passthrough
varieties.

- Expose the new print modes via pdfium_test.
- Plumb print mode into CFX_PSRenderer.

Bug: chromium:1232526
Change-Id: I5099716c473eefe99738d9a5457fe3c41bd51bc8
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/84876
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxge/cfx_windowsrenderdevice.h b/core/fxge/cfx_windowsrenderdevice.h
index 635c72b..8aa3ca3 100644
--- a/core/fxge/cfx_windowsrenderdevice.h
+++ b/core/fxge/cfx_windowsrenderdevice.h
@@ -19,6 +19,8 @@
   kModePostScript2PassThrough = 4,
   kModePostScript3PassThrough = 5,
   kModeEmfImageMasks = 6,
+  kModePostScript3Type42 = 7,
+  kModePostScript3Type42PassThrough = 8,
 };
 
 struct EncoderIface;
diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp
index e456989..f51ec05 100644
--- a/core/fxge/win32/cfx_psrenderer.cpp
+++ b/core/fxge/win32/cfx_psrenderer.cpp
@@ -57,10 +57,10 @@
 CFX_PSRenderer::~CFX_PSRenderer() = default;
 
 void CFX_PSRenderer::Init(const RetainPtr<IFX_RetainableWriteStream>& pStream,
-                          int pslevel,
+                          RenderingLevel level,
                           int width,
                           int height) {
-  m_PSLevel = pslevel;
+  m_Level = level;
   m_pStream = pStream;
   m_ClipBox.left = 0;
   m_ClipBox.top = 0;
@@ -398,7 +398,7 @@
     uint8_t* output_buf = nullptr;
     size_t output_size = 0;
     const char* filter = nullptr;
-    if ((m_PSLevel == 2 || options.bLossy) &&
+    if ((m_Level.value() == RenderingLevel::kLevel2 || options.bLossy) &&
         m_pEncoderIface->pJpegEncodeFunc(pConverted, &output_buf,
                                          &output_size)) {
       filter = "/DCTDecode filter ";
@@ -585,7 +585,7 @@
                                           CFX_Font* font,
                                           float font_size,
                                           std::ostringstream& buf) {
-  if (!CanEmbed(font))
+  if (m_Level != RenderingLevel::kLevel3Type42 || !CanEmbed(font))
     return false;
 
   if (font->GetFontType() != CFX_Font::FontType::kCIDTrueType)
@@ -664,7 +664,8 @@
 
   uint8_t* dest_buf = nullptr;
   uint32_t dest_size = src_size;
-  if (m_PSLevel >= 3) {
+  if (m_Level.value() == RenderingLevel::kLevel3 ||
+      m_Level.value() == RenderingLevel::kLevel3Type42) {
     std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf_unique;
     if (m_pEncoderIface->pFlateEncodeFunc(src_buf, src_size, &dest_buf_unique,
                                           &dest_size)) {
diff --git a/core/fxge/win32/cfx_psrenderer.h b/core/fxge/win32/cfx_psrenderer.h
index 173e504..6716f7e 100644
--- a/core/fxge/win32/cfx_psrenderer.h
+++ b/core/fxge/win32/cfx_psrenderer.h
@@ -20,6 +20,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_graphstatedata.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/span.h"
 
 class CFX_DIBBase;
@@ -55,11 +56,17 @@
 
 class CFX_PSRenderer {
  public:
+  enum class RenderingLevel {
+    kLevel2,
+    kLevel3,
+    kLevel3Type42,
+  };
+
   explicit CFX_PSRenderer(const EncoderIface* pEncoderIface);
   ~CFX_PSRenderer();
 
   void Init(const RetainPtr<IFX_RetainableWriteStream>& stream,
-            int pslevel,
+            RenderingLevel level,
             int width,
             int height);
   void StartRendering();
@@ -139,7 +146,7 @@
   bool m_bInited = false;
   bool m_bGraphStateSet = false;
   bool m_bColorSet = false;
-  int m_PSLevel = 0;
+  Optional<RenderingLevel> m_Level;
   uint32_t m_LastColor = 0;
   FX_RECT m_ClipBox;
   CFX_GraphStateData m_CurGraphState;
diff --git a/core/fxge/win32/cps_printer_driver.cpp b/core/fxge/win32/cps_printer_driver.cpp
index 6fbe528..efc65ff 100644
--- a/core/fxge/win32/cps_printer_driver.cpp
+++ b/core/fxge/win32/cps_printer_driver.cpp
@@ -19,22 +19,39 @@
 #include "third_party/base/check.h"
 #include "third_party/base/notreached.h"
 
+namespace {
+
+CFX_PSRenderer::RenderingLevel RenderingLevelFromWindowsPrintMode(
+    WindowsPrintMode mode) {
+  switch (mode) {
+    case WindowsPrintMode::kModePostScript2:
+    case WindowsPrintMode::kModePostScript2PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel2;
+    case WindowsPrintMode::kModePostScript3:
+    case WindowsPrintMode::kModePostScript3PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel3;
+    case WindowsPrintMode::kModePostScript3Type42:
+    case WindowsPrintMode::kModePostScript3Type42PassThrough:
+      return CFX_PSRenderer::RenderingLevel::kLevel3Type42;
+    default:
+      // |mode| should be PostScript.
+      NOTREACHED();
+      return CFX_PSRenderer::RenderingLevel::kLevel2;
+  }
+}
+
+}  // namespace
+
 CPSPrinterDriver::CPSPrinterDriver(HDC hDC,
                                    WindowsPrintMode mode,
                                    const EncoderIface* pEncoderIface)
     : m_hDC(hDC), m_PSRenderer(pEncoderIface) {
-  // |mode| should be PostScript.
-  DCHECK(mode == WindowsPrintMode::kModePostScript2 ||
-         mode == WindowsPrintMode::kModePostScript3 ||
-         mode == WindowsPrintMode::kModePostScript2PassThrough ||
-         mode == WindowsPrintMode::kModePostScript3PassThrough);
-  int pslevel = (mode == WindowsPrintMode::kModePostScript2 ||
-                 mode == WindowsPrintMode::kModePostScript2PassThrough)
-                    ? 2
-                    : 3;
+  CFX_PSRenderer::RenderingLevel level =
+      RenderingLevelFromWindowsPrintMode(mode);
   CPSOutput::OutputMode output_mode =
       (mode == WindowsPrintMode::kModePostScript2 ||
-       mode == WindowsPrintMode::kModePostScript3)
+       mode == WindowsPrintMode::kModePostScript3 ||
+       mode == WindowsPrintMode::kModePostScript3Type42)
           ? CPSOutput::OutputMode::kGdiComment
           : CPSOutput::OutputMode::kExtEscape;
 
@@ -44,7 +61,7 @@
   m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
   m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
 
-  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC, output_mode), pslevel,
+  m_PSRenderer.Init(pdfium::MakeRetain<CPSOutput>(m_hDC, output_mode), level,
                     m_Width, m_Height);
   HRGN hRgn = ::CreateRectRgn(0, 0, 1, 1);
   if (::GetClipRgn(m_hDC, hRgn) == 1) {
diff --git a/fpdfsdk/fpdf_view.cpp b/fpdfsdk/fpdf_view.cpp
index 3e77099..998c127 100644
--- a/fpdfsdk/fpdf_view.cpp
+++ b/fpdfsdk/fpdf_view.cpp
@@ -83,6 +83,13 @@
 static_assert(WindowsPrintMode::kModeEmfImageMasks ==
                   FPDF_PRINTMODE_EMF_IMAGE_MASKS,
               "WindowsPrintMode::kModeEmfImageMasks value mismatch");
+static_assert(WindowsPrintMode::kModePostScript3Type42 ==
+                  FPDF_PRINTMODE_POSTSCRIPT3_TYPE42,
+              "WindowsPrintMode::kModePostScript3Type42 value mismatch");
+static_assert(
+    WindowsPrintMode::kModePostScript3Type42PassThrough ==
+        FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH,
+    "WindowsPrintMode::kModePostScript3Type42PassThrough value mismatch");
 #endif  // defined(OS_WIN)
 
 namespace {
@@ -219,8 +226,10 @@
 #endif  // PDFIUM_PRINT_TEXT_WITH_GDI
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
-  if (mode < FPDF_PRINTMODE_EMF || mode > FPDF_PRINTMODE_EMF_IMAGE_MASKS)
+  if (mode < FPDF_PRINTMODE_EMF ||
+      mode > FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH) {
     return FALSE;
+  }
 
   g_pdfium_print_mode = static_cast<WindowsPrintMode>(mode);
   return TRUE;
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 40bb11f..294e7fb 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -71,6 +71,8 @@
 #define FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH 4
 #define FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH 5
 #define FPDF_PRINTMODE_EMF_IMAGE_MASKS 6
+#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 7
+#define FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH 8
 
 typedef struct FPDF_IMAGEOBJ_METADATA {
   // The image width in pixels.
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 1cc7bd9..a3c22da 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -337,6 +337,12 @@
 //                 PostScript via ExtEscape() in PASSTHROUGH mode.
 //                 FPDF_PRINTMODE_EMF_IMAGE_MASKS to output EMF, with more
 //                 efficient processing of documents containing image masks.
+//                 FPDF_PRINTMODE_POSTSCRIPT3_TYPE42 to output level 3
+//                 PostScript with embedded Type 42 fonts, when applicable, into
+//                 EMF as a series of GDI comments.
+//                 FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH to output level
+//                 3 PostScript with embedded Type 42 fonts, when applicable,
+//                 via ExtEscape() in PASSTHROUGH mode.
 // Return value:
 //          True if successful, false if unsuccessful (typically invalid input).
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode);
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index 8f31b1d..4414a80 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -88,6 +88,7 @@
   kEmf,
   kPs2,
   kPs3,
+  kPs3Type42,
 #endif
 #ifdef PDF_ENABLE_SKIA
   kSkp,
@@ -565,6 +566,12 @@
         return false;
       }
       options->output_format = OutputFormat::kPs3;
+    } else if (cur_arg == "--ps3-type42") {
+      if (options->output_format != OutputFormat::kNone) {
+        fprintf(stderr, "Duplicate or conflicting --ps3-type42 argument\n");
+        return false;
+      }
+      options->output_format = OutputFormat::kPs3Type42;
     } else if (cur_arg == "--bmp") {
       if (options->output_format != OutputFormat::kNone) {
         fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
@@ -1003,6 +1010,8 @@
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
   else if (options.output_format == OutputFormat::kPs3)
     FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
+  else if (options.output_format == OutputFormat::kPs3Type42)
+    FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3_TYPE42);
 #endif
 
   int page_count = FPDF_GetPageCount(doc.get());
@@ -1136,6 +1145,8 @@
     "<pdf-name>.<page-number>.ps\n"
     "  --ps3   - write page raw PostScript (Lvl 3) "
     "<pdf-name>.<page-number>.ps\n"
+    "  --ps3-type42 - write page raw PostScript (Lvl 3 with Type 42 fonts) "
+    "<pdf-name>.<page-number>.ps\n"
 #endif
     "  --txt   - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
     "  --png   - write page images <pdf-name>.<page-number>.png\n"