Add API for getting page labels.

BUG=pdfium:479

Review-Url: https://codereview.chromium.org/2521843003
diff --git a/fpdfsdk/fpdfdoc.cpp b/fpdfsdk/fpdfdoc.cpp
index 01d9124..254be3f 100644
--- a/fpdfsdk/fpdfdoc.cpp
+++ b/fpdfsdk/fpdfdoc.cpp
@@ -15,6 +15,7 @@
 #include "core/fpdfdoc/cpdf_bookmark.h"
 #include "core/fpdfdoc/cpdf_bookmarktree.h"
 #include "core/fpdfdoc/cpdf_dest.h"
+#include "core/fpdfdoc/cpdf_pagelabel.h"
 #include "fpdfsdk/fsdk_define.h"
 #include "third_party/base/stl_util.h"
 
@@ -58,6 +59,16 @@
   return pHolder->get();
 }
 
+unsigned long Utf16EncodeMaybeCopyAndReturnLength(const CFX_WideString& text,
+                                                  void* buffer,
+                                                  unsigned long buflen) {
+  CFX_ByteString encodedText = text.UTF16LE_Encode();
+  unsigned long len = encodedText.GetLength();
+  if (buffer && buflen >= len)
+    FXSYS_memcpy(buffer, encodedText.c_str(), len);
+  return len;
+}
+
 }  // namespace
 
 DLLEXPORT FPDF_BOOKMARK STDCALL
@@ -91,12 +102,7 @@
     return 0;
   CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
   CFX_WideString title = bookmark.GetTitle();
-  CFX_ByteString encodedTitle = title.UTF16LE_Encode();
-  unsigned long len = encodedTitle.GetLength();
-  if (buffer && buflen >= len) {
-    FXSYS_memcpy(buffer, encodedTitle.c_str(), len);
-  }
-  return len;
+  return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
 }
 
 DLLEXPORT FPDF_BOOKMARK STDCALL FPDFBookmark_Find(FPDF_DOCUMENT document,
@@ -354,42 +360,53 @@
   CPDF_Dictionary* pAnnotDict =
       ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
   CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
-  if (pArray) {
-    if (quadIndex < 0 ||
-        static_cast<size_t>(quadIndex) >= pArray->GetCount() / 8 ||
-        (static_cast<size_t>(quadIndex * 8 + 7) >= pArray->GetCount()))
-      return false;
-    quadPoints->x1 = pArray->GetNumberAt(quadIndex * 8);
-    quadPoints->y1 = pArray->GetNumberAt(quadIndex * 8 + 1);
-    quadPoints->x2 = pArray->GetNumberAt(quadIndex * 8 + 2);
-    quadPoints->y2 = pArray->GetNumberAt(quadIndex * 8 + 3);
-    quadPoints->x3 = pArray->GetNumberAt(quadIndex * 8 + 4);
-    quadPoints->y3 = pArray->GetNumberAt(quadIndex * 8 + 5);
-    quadPoints->x4 = pArray->GetNumberAt(quadIndex * 8 + 6);
-    quadPoints->y4 = pArray->GetNumberAt(quadIndex * 8 + 7);
-    return true;
+  if (!pArray)
+    return false;
+
+  if (quadIndex < 0 ||
+      static_cast<size_t>(quadIndex) >= pArray->GetCount() / 8 ||
+      (static_cast<size_t>(quadIndex * 8 + 7) >= pArray->GetCount())) {
+    return false;
   }
-  return false;
+
+  quadPoints->x1 = pArray->GetNumberAt(quadIndex * 8);
+  quadPoints->y1 = pArray->GetNumberAt(quadIndex * 8 + 1);
+  quadPoints->x2 = pArray->GetNumberAt(quadIndex * 8 + 2);
+  quadPoints->y2 = pArray->GetNumberAt(quadIndex * 8 + 3);
+  quadPoints->x3 = pArray->GetNumberAt(quadIndex * 8 + 4);
+  quadPoints->y3 = pArray->GetNumberAt(quadIndex * 8 + 5);
+  quadPoints->x4 = pArray->GetNumberAt(quadIndex * 8 + 6);
+  quadPoints->y4 = pArray->GetNumberAt(quadIndex * 8 + 7);
+  return true;
 }
 
-DLLEXPORT unsigned long STDCALL FPDF_GetMetaText(FPDF_DOCUMENT doc,
+DLLEXPORT unsigned long STDCALL FPDF_GetMetaText(FPDF_DOCUMENT document,
                                                  FPDF_BYTESTRING tag,
                                                  void* buffer,
                                                  unsigned long buflen) {
   if (!tag)
     return 0;
-  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pDoc)
     return 0;
   CPDF_Dictionary* pInfo = pDoc->GetInfo();
   if (!pInfo)
     return 0;
   CFX_WideString text = pInfo->GetUnicodeTextFor(tag);
-  // Use UTF-16LE encoding
-  CFX_ByteString encodedText = text.UTF16LE_Encode();
-  unsigned long len = encodedText.GetLength();
-  if (buffer && buflen >= len) {
-    FXSYS_memcpy(buffer, encodedText.c_str(), len);
-  }
-  return len;
+  return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
+}
+
+DLLEXPORT unsigned long STDCALL FPDF_GetPagelLabel(FPDF_DOCUMENT document,
+                                                   int page_index,
+                                                   void* buffer,
+                                                   unsigned long buflen) {
+  if (page_index < 0)
+    return 0;
+
+  // CPDF_PageLabel can deal with NULL |document|.
+  CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
+  CFX_WideString str;
+  if (!label.GetLabel(page_index, &str))
+    return 0;
+  return Utf16EncodeMaybeCopyAndReturnLength(str, buffer, buflen);
 }
diff --git a/fpdfsdk/fpdfdoc_embeddertest.cpp b/fpdfsdk/fpdfdoc_embeddertest.cpp
index d7f1f97..67934c5 100644
--- a/fpdfsdk/fpdfdoc_embeddertest.cpp
+++ b/fpdfsdk/fpdfdoc_embeddertest.cpp
@@ -172,3 +172,57 @@
   FPDFPage_Delete(document(), 0);
   EXPECT_EQ(0, FPDF_GetPageCount(document()));
 }
+
+TEST_F(FPDFDocEmbeddertest, NoPageLabels) {
+  EXPECT_TRUE(OpenDocument("about_blank.pdf"));
+  EXPECT_EQ(1, FPDF_GetPageCount(document()));
+
+  ASSERT_EQ(0u, FPDF_GetPagelLabel(document(), 0, nullptr, 0));
+}
+
+TEST_F(FPDFDocEmbeddertest, GetPageLabels) {
+  EXPECT_TRUE(OpenDocument("page_labels.pdf"));
+  EXPECT_EQ(7, FPDF_GetPageCount(document()));
+
+  unsigned short buf[128];
+  EXPECT_EQ(0u, FPDF_GetPagelLabel(document(), -2, buf, sizeof(buf)));
+  EXPECT_EQ(0u, FPDF_GetPagelLabel(document(), -1, buf, sizeof(buf)));
+
+  const FX_WCHAR kExpectedPageLabel0[] = L"i";
+  ASSERT_EQ(4u, FPDF_GetPagelLabel(document(), 0, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel0),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel0)));
+
+  const FX_WCHAR kExpectedPageLabel1[] = L"ii";
+  ASSERT_EQ(6u, FPDF_GetPagelLabel(document(), 1, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel1),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel1)));
+
+  const FX_WCHAR kExpectedPageLabel2[] = L"1";
+  ASSERT_EQ(4u, FPDF_GetPagelLabel(document(), 2, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel2),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel2)));
+
+  const FX_WCHAR kExpectedPageLabel3[] = L"2";
+  ASSERT_EQ(4u, FPDF_GetPagelLabel(document(), 3, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel3),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel3)));
+
+  const FX_WCHAR kExpectedPageLabel4[] = L"zzA";
+  ASSERT_EQ(8u, FPDF_GetPagelLabel(document(), 4, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel4),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel4)));
+
+  const FX_WCHAR kExpectedPageLabel5[] = L"zzB";
+  ASSERT_EQ(8u, FPDF_GetPagelLabel(document(), 5, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel5),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel5)));
+
+  const FX_WCHAR kExpectedPageLabel6[] = L"";
+  ASSERT_EQ(2u, FPDF_GetPagelLabel(document(), 6, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(kExpectedPageLabel6),
+            CFX_WideString::FromUTF16LE(buf, FXSYS_len(kExpectedPageLabel6)));
+
+  ASSERT_EQ(0u, FPDF_GetPagelLabel(document(), 7, buf, sizeof(buf)));
+  ASSERT_EQ(0u, FPDF_GetPagelLabel(document(), 8, buf, sizeof(buf)));
+}
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
index afc357f..5e6c36f 100644
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ b/fpdfsdk/fpdfview_c_api_test.c
@@ -64,6 +64,7 @@
     CHK(FPDFLink_CountQuadPoints);
     CHK(FPDFLink_GetQuadPoints);
     CHK(FPDF_GetMetaText);
+    CHK(FPDF_GetPagelLabel);
 
     // fpdf_edit.h
     CHK(FPDF_CreateNewDocument);