Allow CPDF_Font to use fallback fonts

Added a vector of pointers to CFX_Fonts in the class CPDF_Font, so that
fallback fonts may be used. In CPDF_CharPosList::Load, the glyphs for each
character are calculated. When m_Font does not support a character, a fallback
font is selected and the character is rendered using that font. This meant
adding an attribute to FXTEXT_CHARPOS so it knows which font renders it.

Also, methods in fpdf_render_text.cpp now may need to call device drawing
methods multiple times because these only support one font at a time. In
CPDF_TextRenderer::DrawNormalText and in CPDF_TextRenderer::DrawTextPath, the
device drawing method is called as few times as possible by grouping contiguous
characters rendered by the same font. In
CPDF_RenderStatus::DrawTextPathWithPattern, drawing was already done one
character at a time, but precalculating CFX_FaceCache. Now, the face cache is
precalculated for all of the fallback fonts.

The list of fallback fonts does not include tha main font. Otherwise the list
would be of raw pointers to avoid double free problems. For now, the font
Arial is used as fallback. This should fix the issue of not seeing Latin
characters displayed when bad fonts are used. However, this should be improved.

Tested manually using the file in the bug, plus a font directory containing a
font that supports Hangul but not Latin. This font is chosen as the substitute
font, but Latin characters are now being rendered.

Design proposal: go/pdfium_fallbackfonts

BUG=pdfium:358

Review-Url: https://codereview.chromium.org/2276653002
diff --git a/core/fpdfapi/fpdf_font/cpdf_font.cpp b/core/fpdfapi/fpdf_font/cpdf_font.cpp
index 8101bd4..4747093 100644
--- a/core/fpdfapi/fpdf_font/cpdf_font.cpp
+++ b/core/fpdfapi/fpdf_font/cpdf_font.cpp
@@ -22,7 +22,9 @@
 #include "core/fpdfapi/fpdf_parser/include/cpdf_name.h"
 #include "core/fpdfapi/fpdf_parser/include/cpdf_stream_acc.h"
 #include "core/fpdfapi/include/cpdf_modulemgr.h"
+#include "core/fxcrt/include/fx_memory.h"
 #include "core/fxge/include/fx_freetype.h"
+#include "third_party/base/stl_util.h"
 
 namespace {
 
@@ -451,3 +453,25 @@
     name = PDF_CharNameFromPredefinedCharSet(iBaseEncoding, charcode);
   return name && name[0] ? name : nullptr;
 }
+
+uint32_t CPDF_Font::FallbackFontFromCharcode(uint32_t charcode) {
+  if (m_FontFallbacks.empty()) {
+    m_FontFallbacks.push_back(WrapUnique(new CFX_Font()));
+    m_FontFallbacks[0]->LoadSubst("Arial", IsTrueTypeFont(), m_Flags,
+                                  m_StemV * 5, m_ItalicAngle, 0,
+                                  IsVertWriting());
+  }
+  return 0;
+}
+
+int CPDF_Font::FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode) {
+  if (fallbackFont < 0 ||
+      fallbackFont >= pdfium::CollectionSize<int>(m_FontFallbacks)) {
+    return -1;
+  }
+  int glyph =
+      FXFT_Get_Char_Index(m_FontFallbacks[fallbackFont]->GetFace(), charcode);
+  if (glyph == 0 || glyph == 0xffff)
+    return -1;
+  return glyph;
+}
diff --git a/core/fpdfapi/fpdf_font/include/cpdf_font.h b/core/fpdfapi/fpdf_font/include/cpdf_font.h
index a76a6a2..a47d865 100644
--- a/core/fpdfapi/fpdf_font/include/cpdf_font.h
+++ b/core/fpdfapi/fpdf_font/include/cpdf_font.h
@@ -88,12 +88,15 @@
   int GetItalicAngle() const { return m_ItalicAngle; }
   int GetStemV() const { return m_StemV; }
   int GetStringWidth(const FX_CHAR* pString, int size);
+  uint32_t FallbackFontFromCharcode(uint32_t charcode);
+  int FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode);
 
   virtual int GetCharWidthF(uint32_t charcode, int level = 0) = 0;
   virtual FX_RECT GetCharBBox(uint32_t charcode, int level = 0) = 0;
 
   CPDF_Document* m_pDocument;
   CFX_Font m_Font;
+  std::vector<std::unique_ptr<CFX_Font>> m_FontFallbacks;
 
  protected:
   CPDF_Font();
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_text.cpp b/core/fpdfapi/fpdf_render/fpdf_render_text.cpp
index 641d0b470..16d1235 100644
--- a/core/fpdfapi/fpdf_render/fpdf_render_text.cpp
+++ b/core/fpdfapi/fpdf_render/fpdf_render_text.cpp
@@ -371,6 +371,7 @@
   CPDF_Type3Font* const m_pType3Font;
 };
 
+// TODO(npm): Font fallback for type 3 fonts? (Completely separate code!!)
 FX_BOOL CPDF_RenderStatus::ProcessType3Text(const CPDF_TextObject* textobj,
                                             const CFX_Matrix* pObj2Device) {
   CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font();
@@ -572,6 +573,15 @@
       charpos.m_bFontStyle = true;
     }
     charpos.m_GlyphIndex = pFont->GlyphFromCharCode(CharCode, &bVert);
+    if (charpos.m_GlyphIndex != static_cast<uint32_t>(-1)) {
+      charpos.m_FallbackFontPosition = -1;
+    } else {
+      charpos.m_FallbackFontPosition =
+          pFont->FallbackFontFromCharcode(CharCode);
+      charpos.m_GlyphIndex = pFont->FallbackGlyphFromCharcode(
+          charpos.m_FallbackFontPosition, CharCode);
+    }
+// TODO(npm): Figure out how this affects m_ExtGID
 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
     charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode);
 #endif
@@ -629,10 +639,36 @@
                          : nullptr;
   CPDF_CharPosList CharPosList;
   CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
-  return pDevice->DrawTextPath(CharPosList.m_nChars, CharPosList.m_pCharPos,
-                               &pFont->m_Font, pCache, font_size, pText2User,
-                               pUser2Device, pGraphState, fill_argb,
-                               stroke_argb, pClippingPath, nFlag);
+  if (CharPosList.m_nChars == 0)
+    return TRUE;
+  bool bDraw = true;
+  int32_t fontPosition = CharPosList.m_pCharPos[0].m_FallbackFontPosition;
+  uint32_t startIndex = 0;
+  for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
+    int32_t curFontPosition = CharPosList.m_pCharPos[i].m_FallbackFontPosition;
+    if (fontPosition == curFontPosition)
+      continue;
+    auto* font = fontPosition == -1
+                     ? &pFont->m_Font
+                     : pFont->m_FontFallbacks[fontPosition].get();
+    if (!pDevice->DrawTextPath(
+            i - startIndex, CharPosList.m_pCharPos + startIndex, font, pCache,
+            font_size, pText2User, pUser2Device, pGraphState, fill_argb,
+            stroke_argb, pClippingPath, nFlag)) {
+      bDraw = false;
+    }
+    fontPosition = curFontPosition;
+    startIndex = i;
+  }
+  auto* font = fontPosition == -1 ? &pFont->m_Font
+                                  : pFont->m_FontFallbacks[fontPosition].get();
+  if (!pDevice->DrawTextPath(CharPosList.m_nChars - startIndex,
+                             CharPosList.m_pCharPos + startIndex, font, pCache,
+                             font_size, pText2User, pUser2Device, pGraphState,
+                             fill_argb, stroke_argb, pClippingPath, nFlag)) {
+    bDraw = false;
+  }
+  return bDraw;
 }
 
 // static
@@ -708,6 +744,8 @@
                          : nullptr;
   CPDF_CharPosList CharPosList;
   CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
+  if (CharPosList.m_nChars == 0)
+    return TRUE;
   int FXGE_flags = 0;
   if (pOptions) {
     uint32_t dwFlags = pOptions->m_Flags;
@@ -735,9 +773,33 @@
   if (pFont->IsCIDFont()) {
     FXGE_flags |= FXFONT_CIDFONT;
   }
-  return pDevice->DrawNormalText(CharPosList.m_nChars, CharPosList.m_pCharPos,
-                                 &pFont->m_Font, pCache, font_size,
-                                 pText2Device, fill_argb, FXGE_flags);
+  bool bDraw = true;
+  int32_t fontPosition = CharPosList.m_pCharPos[0].m_FallbackFontPosition;
+  uint32_t startIndex = 0;
+  for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
+    int32_t curFontPosition = CharPosList.m_pCharPos[i].m_FallbackFontPosition;
+    if (fontPosition == curFontPosition)
+      continue;
+    auto* font = fontPosition == -1
+                     ? &pFont->m_Font
+                     : pFont->m_FontFallbacks[fontPosition].get();
+    if (!pDevice->DrawNormalText(
+            i - startIndex, CharPosList.m_pCharPos + startIndex, font, pCache,
+            font_size, pText2Device, fill_argb, FXGE_flags)) {
+      bDraw = false;
+    }
+    fontPosition = curFontPosition;
+    startIndex = i;
+  }
+  auto* font = fontPosition == -1 ? &pFont->m_Font
+                                  : pFont->m_FontFallbacks[fontPosition].get();
+  if (!pDevice->DrawNormalText(CharPosList.m_nChars - startIndex,
+                               CharPosList.m_pCharPos + startIndex, font,
+                               pCache, font_size, pText2Device, fill_argb,
+                               FXGE_flags)) {
+    bDraw = false;
+  }
+  return bDraw;
 }
 
 void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
@@ -770,15 +832,26 @@
   } else {
     pCache = CFX_GEModule::Get()->GetFontCache();
   }
-  CFX_FaceCache* pFaceCache = pCache->GetCachedFace(&pFont->m_Font);
-  CFX_AutoFontCache autoFontCache(pCache, &pFont->m_Font);
   CPDF_CharPosList CharPosList;
   CharPosList.Load(textobj->m_nChars, textobj->m_pCharCodes,
                    textobj->m_pCharPos, pFont, font_size);
+  std::vector<CFX_FaceCache*> faceCaches;
+  std::vector<CFX_AutoFontCache> autoFontCaches;
+  faceCaches.push_back(pCache->GetCachedFace(&pFont->m_Font));
+  autoFontCaches.push_back(CFX_AutoFontCache(pCache, &pFont->m_Font));
+  for (const auto& font : pFont->m_FontFallbacks) {
+    faceCaches.push_back(pCache->GetCachedFace(font.get()));
+    autoFontCaches.push_back(CFX_AutoFontCache(pCache, font.get()));
+  }
   for (uint32_t i = 0; i < CharPosList.m_nChars; i++) {
     FXTEXT_CHARPOS& charpos = CharPosList.m_pCharPos[i];
-    const CFX_PathData* pPath = pFaceCache->LoadGlyphPath(
-        &pFont->m_Font, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
+    auto font =
+        charpos.m_FallbackFontPosition == -1
+            ? &pFont->m_Font
+            : pFont->m_FontFallbacks[charpos.m_FallbackFontPosition].get();
+    const CFX_PathData* pPath =
+        faceCaches[charpos.m_FallbackFontPosition + 1]->LoadGlyphPath(
+            font, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
     if (!pPath) {
       continue;
     }
diff --git a/core/fxge/include/cfx_renderdevice.h b/core/fxge/include/cfx_renderdevice.h
index 6607b47..9ca9a07 100644
--- a/core/fxge/include/cfx_renderdevice.h
+++ b/core/fxge/include/cfx_renderdevice.h
@@ -76,6 +76,7 @@
 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
   uint32_t m_ExtGID;
 #endif
+  int32_t m_FallbackFontPosition;
   bool m_bGlyphAdjust;
   bool m_bFontStyle;
 };