Expand FPDF_Font public API

Exposed several properties of CPDF_Font so they are accessible by users.
Some properties (Flags, Weight, ItalicAngle) require no further scaling,
but the rest require a font_size to be properly scaled in the
text_object space. Further, the drawing instructions for each glyph are
made accessible via FPDF_GLYPHPATH which can be used to generate
FPDF_PATHSEGMENTs.

Change-Id: Idb02edc859855b0e9b65db07ae9d81754f55254f
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/84731
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/font/cpdf_font.h b/core/fpdfapi/font/cpdf_font.h
index 1c61b1d..722726c 100644
--- a/core/fpdfapi/font/cpdf_font.h
+++ b/core/fpdfapi/font/cpdf_font.h
@@ -111,6 +111,7 @@
   uint32_t FallbackFontFromCharcode(uint32_t charcode);
   int FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode);
   int GetFontFlags() const { return m_Flags; }
+  int GetItalicAngle() const { return m_ItalicAngle; }
   int GetFontWeight() const;
 
   virtual int GetCharWidthF(uint32_t charcode) = 0;
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index 0f11d1d..6642c50 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -2518,6 +2518,114 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFEditEmbedderTest, TextFontProperties) {
+  // bad object tests
+  EXPECT_FALSE(FPDFTextObj_GetFont(nullptr));
+  EXPECT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 5));
+  EXPECT_EQ(-1, FPDFFont_GetFlags(nullptr));
+  EXPECT_EQ(-1, FPDFFont_GetWeight(nullptr));
+  EXPECT_FALSE(FPDFFont_GetItalicAngle(nullptr, nullptr));
+  EXPECT_FALSE(FPDFFont_GetAscent(nullptr, 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetDescent(nullptr, 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphWidth(nullptr, 's', 12.f, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphPath(nullptr, 's', 12.f));
+
+  // good object tests
+  ASSERT_TRUE(OpenDocument("text_font.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text);
+  float font_size;
+  ASSERT_TRUE(FPDFTextObj_GetFontSize(text, &font_size));
+  FPDF_FONT font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
+
+  // null return pointer tests
+  EXPECT_FALSE(FPDFFont_GetItalicAngle(font, nullptr));
+  EXPECT_FALSE(FPDFFont_GetAscent(font, font_size, nullptr));
+  EXPECT_FALSE(FPDFFont_GetDescent(font, font_size, nullptr));
+  EXPECT_FALSE(FPDFFont_GetGlyphWidth(font, 's', font_size, nullptr));
+
+  // correct property tests
+  {
+    EXPECT_EQ(4, FPDFFont_GetFlags(font));
+    EXPECT_EQ(400, FPDFFont_GetWeight(font));
+
+    int angle;
+    EXPECT_TRUE(FPDFFont_GetItalicAngle(font, &angle));
+    EXPECT_EQ(0, angle);
+
+    float ascent;
+    EXPECT_TRUE(FPDFFont_GetAscent(font, font_size, &ascent));
+    EXPECT_FLOAT_EQ(891 * font_size / 1000.0f, ascent);
+
+    float descent;
+    EXPECT_TRUE(FPDFFont_GetDescent(font, font_size, &descent));
+    EXPECT_FLOAT_EQ(-216 * font_size / 1000.0f, descent);
+
+    float a12;
+    float a24;
+    EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 12.0f, &a12));
+    EXPECT_FLOAT_EQ(a12, 5.316f);
+    EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 24.0f, &a24));
+    EXPECT_FLOAT_EQ(a24, 10.632f);
+  }
+
+  {
+    // FPDFFont_GetFontName() positive testing.
+    unsigned long size = FPDFFont_GetFontName(font, nullptr, 0);
+    const char kExpectedFontName[] = "Liberation Serif";
+    ASSERT_EQ(sizeof(kExpectedFontName), size);
+    std::vector<char> font_name(size);
+    ASSERT_EQ(size, FPDFFont_GetFontName(font, font_name.data(), size));
+    ASSERT_STREQ(kExpectedFontName, font_name.data());
+
+    // FPDFFont_GetFontName() negative testing.
+    ASSERT_EQ(0U, FPDFFont_GetFontName(nullptr, nullptr, 0));
+
+    font_name.resize(2);
+    font_name[0] = 'x';
+    font_name[1] = '\0';
+    size = FPDFFont_GetFontName(font, font_name.data(), font_name.size());
+    ASSERT_EQ(sizeof(kExpectedFontName), size);
+    ASSERT_STREQ("x", font_name.data());
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFEditEmbedderTest, GlyphPaths) {
+  // bad glyphpath
+  EXPECT_EQ(-1, FPDFGlyphPath_CountGlyphSegments(nullptr));
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(nullptr, 1));
+
+  ASSERT_TRUE(OpenDocument("text_font.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+  ASSERT_EQ(1, FPDFPage_CountObjects(page));
+  FPDF_PAGEOBJECT text = FPDFPage_GetObject(page, 0);
+  ASSERT_TRUE(text);
+  FPDF_FONT font = FPDFTextObj_GetFont(text);
+  ASSERT_TRUE(font);
+
+  // good glyphpath
+  FPDF_GLYPHPATH gpath = FPDFFont_GetGlyphPath(font, 's', 12.0f);
+  ASSERT_TRUE(gpath);
+
+  int count = FPDFGlyphPath_CountGlyphSegments(gpath);
+  ASSERT_GT(count, 0);
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, -1));
+  EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, count));
+
+  FPDF_PATHSEGMENT segment = FPDFGlyphPath_GetGlyphPathSegment(gpath, 1);
+  ASSERT_TRUE(segment);
+  EXPECT_EQ(FPDF_SEGMENT_BEZIERTO, FPDFPathSegment_GetType(segment));
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFEditEmbedderTest, FormGetObjects) {
   ASSERT_TRUE(OpenDocument("form_object.pdf"));
   FPDF_PAGE page = LoadPage(0);
diff --git a/fpdfsdk/fpdf_edittext.cpp b/fpdfsdk/fpdf_edittext.cpp
index 3fcd0b3..9c81d0f 100644
--- a/fpdfsdk/fpdf_edittext.cpp
+++ b/fpdfsdk/fpdf_edittext.cpp
@@ -8,8 +8,8 @@
 #include <utility>
 #include <vector>
 
+#include "core/fpdfapi/font/cpdf_cidfont.h"
 #include "core/fpdfapi/font/cpdf_font.h"
-#include "core/fpdfapi/font/cpdf_type1font.h"
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_textobject.h"
 #include "core/fpdfapi/page/cpdf_textstate.h"
@@ -21,10 +21,13 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fpdfapi/render/charposlist.h"
 #include "core/fpdftext/cpdf_textpage.h"
 #include "core/fxcrt/fx_extension.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/cfx_fontmgr.h"
 #include "core/fxge/fx_font.h"
+#include "core/fxge/text_char_pos.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "public/fpdf_edit.h"
 #include "third_party/base/check.h"
@@ -442,6 +445,13 @@
   return obj ? obj->AsText() : nullptr;
 }
 
+FPDF_GLYPHPATH FPDFGlyphPathFromCFXPath(const CFX_Path* path) {
+  return reinterpret_cast<FPDF_GLYPHPATH>(path);
+}
+const CFX_Path* CFXPathFromFPDFGlyphPath(FPDF_GLYPHPATH path) {
+  return reinterpret_cast<const CFX_Path*>(path);
+}
+
 }  // namespace
 
 FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
@@ -633,3 +643,142 @@
   pTextObj->SetTextRenderMode(static_cast<TextRenderingMode>(render_mode));
   return true;
 }
+
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text) {
+  CPDF_TextObject* pTextObj = CPDFTextObjectFromFPDFPageObject(text);
+  if (!pTextObj)
+    return nullptr;
+
+  return FPDFFontFromCPDFFont(pTextObj->GetFont().Get());
+}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return 0;
+
+  CFX_Font* pCfxFont = pFont->GetFont();
+  ByteString name = pCfxFont->GetFamilyName();
+  unsigned long dwStringLen = name.GetLength() + 1;
+
+  if (buffer && length >= dwStringLen)
+    memcpy(buffer, name.c_str(), dwStringLen);
+
+  return dwStringLen;
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return -1;
+
+  // return only flags from ISO 32000-1 Table 123
+  return pFont->GetFontFlags() & 0x7ffff;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  return pFont ? pFont->GetFontWeight() : -1;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font,
+                                                            int* angle) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !angle)
+    return false;
+
+  *angle = pFont->GetItalicAngle();
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font,
+                                                       float font_size,
+                                                       float* ascent) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !ascent)
+    return false;
+
+  *ascent = pFont->GetTypeAscent() * font_size / 1000.f;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font,
+                                                        float font_size,
+                                                        float* descent) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !descent)
+    return false;
+
+  *descent = pFont->GetTypeDescent() * font_size / 1000.f;
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font,
+                                                           uint32_t glyph,
+                                                           float font_size,
+                                                           float* width) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont || !width)
+    return false;
+
+  uint32_t charcode = pFont->CharCodeFromUnicode(static_cast<wchar_t>(glyph));
+
+  CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+  if (pCIDFont && pCIDFont->IsVertWriting()) {
+    uint16_t cid = pCIDFont->CIDFromCharCode(charcode);
+    *width = pCIDFont->GetVertWidth(cid) * font_size / 1000.f;
+  } else {
+    *width = pFont->GetCharWidthF(charcode) * font_size / 1000.f;
+  }
+
+  return true;
+}
+
+FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV
+FPDFFont_GetGlyphPath(FPDF_FONT font, uint32_t glyph, float font_size) {
+  auto* pFont = CPDFFontFromFPDFFont(font);
+  if (!pFont)
+    return nullptr;
+
+  std::vector<TextCharPos> pos = GetCharPosList(
+      std::vector<uint32_t>{
+          pFont->CharCodeFromUnicode(static_cast<wchar_t>(glyph))},
+      std::vector<float>{0.0f}, pFont, font_size);
+
+  CFX_Font* pCfxFont;
+  if (pos[0].m_FallbackFontPosition == -1) {
+    pCfxFont = pFont->GetFont();
+  } else {
+    pCfxFont = pFont->GetFontFallback(pos[0].m_FallbackFontPosition);
+  }
+  if (!pCfxFont)
+    return nullptr;
+
+  const CFX_Path* pPath =
+      pCfxFont->LoadGlyphPath(pos[0].m_GlyphIndex, pos[0].m_FontCharWidth);
+
+  return FPDFGlyphPathFromCFXPath(pPath);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath) {
+  auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath);
+  if (!pPath)
+    return -1;
+
+  return fxcrt::CollectionSize<int>(pPath->GetPoints());
+}
+
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index) {
+  auto* pPath = CFXPathFromFPDFGlyphPath(glyphpath);
+  if (!pPath)
+    return nullptr;
+
+  pdfium::span<const CFX_Path::Point> points = pPath->GetPoints();
+  if (!fxcrt::IndexInBounds(points, index))
+    return nullptr;
+
+  return FPDFPathSegmentFromFXPathPoint(&points[index]);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index c1fdbfa..b434ae4 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -156,8 +156,18 @@
 
     // fpdf_edit.h
     CHK(FPDFFont_Close);
+    CHK(FPDFFont_GetAscent);
+    CHK(FPDFFont_GetDescent);
+    CHK(FPDFFont_GetFlags);
+    CHK(FPDFFont_GetFontName);
+    CHK(FPDFFont_GetGlyphPath);
+    CHK(FPDFFont_GetGlyphWidth);
+    CHK(FPDFFont_GetItalicAngle);
+    CHK(FPDFFont_GetWeight);
     CHK(FPDFFormObj_CountObjects);
     CHK(FPDFFormObj_GetObject);
+    CHK(FPDFGlyphPath_CountGlyphSegments);
+    CHK(FPDFGlyphPath_GetGlyphPathSegment);
     CHK(FPDFImageObj_GetBitmap);
     CHK(FPDFImageObj_GetImageDataDecoded);
     CHK(FPDFImageObj_GetImageDataRaw);
@@ -234,6 +244,7 @@
     CHK(FPDFPath_LineTo);
     CHK(FPDFPath_MoveTo);
     CHK(FPDFPath_SetDrawMode);
+    CHK(FPDFTextObj_GetFont);
     CHK(FPDFTextObj_GetFontName);
     CHK(FPDFTextObj_GetFontSize);
     CHK(FPDFTextObj_GetText);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 294e7fb..19e19c2 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -1236,6 +1236,139 @@
                     FPDF_WCHAR* buffer,
                     unsigned long length);
 
+// Experimental API.
+// Get the font of a text object.
+//
+// text - the handle to the text object.
+//
+// Returns a handle to the font object held by |text| which retains ownership.
+FPDF_EXPORT FPDF_FONT FPDF_CALLCONV FPDFTextObj_GetFont(FPDF_PAGEOBJECT text);
+
+// Experimental API.
+// Get the font name of a font.
+//
+// font   - the handle to the font object.
+// buffer - the address of a buffer that receives the font name.
+// length - the size, in bytes, of |buffer|.
+//
+// Returns the number of bytes in the font name (including the trailing NUL
+// character) on success, 0 on error.
+//
+// Regardless of the platform, the |buffer| is always in UTF-8 encoding.
+// If |length| is less than the returned length, or |buffer| is NULL, |buffer|
+// will not be modified.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFFont_GetFontName(FPDF_FONT font, char* buffer, unsigned long length);
+
+// Experimental API.
+// Get the descriptor flags of a font.
+//
+// font - the handle to the font object.
+//
+// Returns the bit flags specifying various characteristics of the font as
+// defined in ISO 32000-1 Table 123, -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetFlags(FPDF_FONT font);
+
+// Experimental API.
+// Get the font weight of a font.
+//
+// font - the handle to the font object.
+//
+// Returns the font weight, -1 on failure.
+// Typical values are 400 (normal) and 700 (bold).
+FPDF_EXPORT int FPDF_CALLCONV FPDFFont_GetWeight(FPDF_FONT font);
+
+// Experimental API.
+// Get the italic angle of a font.
+//
+// font  - the handle to the font object.
+// angle - pointer where the italic angle will be stored
+//
+// The italic angle of a |font| is defined as degrees counterclockwise
+// from vertical. For a font that slopes to the right, this will be negative.
+//
+// Returns TRUE on success; |angle| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetItalicAngle(FPDF_FONT font,
+                                                            int* angle);
+
+// Experimental API.
+// Get ascent distance of a font.
+//
+// font       - the handle to the font object.
+// font_size  - the size of the |font|.
+// ascent     - pointer where the font ascent will be stored
+//
+// Ascent is the maximum distance in points above the baseline reached by the
+// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm).
+//
+// Returns TRUE on success; |ascent| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetAscent(FPDF_FONT font,
+                                                       float font_size,
+                                                       float* ascent);
+
+// Experimental API.
+// Get descent distance of a font.
+//
+// font       - the handle to the font object.
+// font_size  - the size of the |font|.
+// descent    - pointer where the font descent will be stored
+//
+// Descent is the maximum distance in points below the baseline reached by the
+// glyphs of the |font|. One point is 1/72 inch (around 0.3528 mm).
+//
+// Returns TRUE on success; |descent| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetDescent(FPDF_FONT font,
+                                                        float font_size,
+                                                        float* descent);
+
+// Experimental API.
+// Get the width of a glyph in a font.
+//
+// font       - the handle to the font object.
+// glyph      - the glyph.
+// font_size  - the size of the font.
+// width      - pointer where the glyph width will be stored
+//
+// Glyph width is the distance from the end of the prior glyph to the next
+// glyph. This will be the vertical distance for vertical writing.
+//
+// Returns TRUE on success; |width| unmodified on failure.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFFont_GetGlyphWidth(FPDF_FONT font,
+                                                           uint32_t glyph,
+                                                           float font_size,
+                                                           float* width);
+
+// Experimental API.
+// Get the glyphpath describing how to draw a font glyph.
+//
+// font       - the handle to the font object.
+// glyph      - the glyph being drawn.
+// font_size  - the size of the font.
+//
+// Returns the handle to the segment, or NULL on faiure.
+FPDF_EXPORT FPDF_GLYPHPATH FPDF_CALLCONV FPDFFont_GetGlyphPath(FPDF_FONT font,
+                                                               uint32_t glyph,
+                                                               float font_size);
+
+// Experimental API.
+// Get number of segments inside glyphpath.
+//
+// glyphpath - handle to a glyph path.
+//
+// Returns the number of objects in |glyphpath| or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFGlyphPath_CountGlyphSegments(FPDF_GLYPHPATH glyphpath);
+
+// Experimental API.
+// Get segment in glyphpath at index.
+//
+// glyphpath  - handle to a glyph path.
+// index      - the index of a segment.
+//
+// Returns the handle to the segment, or NULL on faiure.
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFGlyphPath_GetGlyphPathSegment(FPDF_GLYPHPATH glyphpath, int index);
+
 // Get number of page objects inside |form_object|.
 //
 //   form_object - handle to a form object.
diff --git a/public/fpdfview.h b/public/fpdfview.h
index a3c22da..a1d7b93 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -62,6 +62,7 @@
 typedef struct fpdf_document_t__* FPDF_DOCUMENT;
 typedef struct fpdf_font_t__* FPDF_FONT;
 typedef struct fpdf_form_handle_t__* FPDF_FORMHANDLE;
+typedef const struct fpdf_glyphpath_t__* FPDF_GLYPHPATH;
 typedef struct fpdf_javascript_action_t* FPDF_JAVASCRIPT_ACTION;
 typedef struct fpdf_link_t__* FPDF_LINK;
 typedef struct fpdf_page_t__* FPDF_PAGE;