Implement FPDF_VIEWERREF_GetName() API.

This is a generic API function to retrieve any viewer preference of type
name.

BUG=pdfium:414

Review-Url: https://codereview.chromium.org/2475923003
diff --git a/core/fpdfdoc/cpdf_viewerpreferences.cpp b/core/fpdfdoc/cpdf_viewerpreferences.cpp
index 799f2d5..f1fc4b0 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.cpp
+++ b/core/fpdfdoc/cpdf_viewerpreferences.cpp
@@ -7,6 +7,7 @@
 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
 
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
 
 CPDF_ViewerPreferences::CPDF_ViewerPreferences(CPDF_Document* pDoc)
     : m_pDoc(pDoc) {}
@@ -38,6 +39,21 @@
   return pDict ? pDict->GetStringFor("Duplex") : CFX_ByteString("None");
 }
 
+bool CPDF_ViewerPreferences::GenericName(const CFX_ByteString& bsKey,
+                                         CFX_ByteString* bsVal) const {
+  ASSERT(bsVal);
+  CPDF_Dictionary* pDict = GetViewerPreferences();
+  if (!pDict)
+    return false;
+
+  const CPDF_Name* pName = ToName(pDict->GetObjectFor(bsKey));
+  if (!pName)
+    return false;
+
+  *bsVal = pName->GetString();
+  return true;
+}
+
 CPDF_Dictionary* CPDF_ViewerPreferences::GetViewerPreferences() const {
   CPDF_Dictionary* pDict = m_pDoc->GetRoot();
   return pDict ? pDict->GetDictFor("ViewerPreferences") : nullptr;
diff --git a/core/fpdfdoc/cpdf_viewerpreferences.h b/core/fpdfdoc/cpdf_viewerpreferences.h
index c64292d..c7e9112 100644
--- a/core/fpdfdoc/cpdf_viewerpreferences.h
+++ b/core/fpdfdoc/cpdf_viewerpreferences.h
@@ -25,6 +25,11 @@
   CPDF_Array* PrintPageRange() const;
   CFX_ByteString Duplex() const;
 
+  // Gets the entry for |bsKey|. If the entry exists and it is of type name,
+  // then this method writes the value into |bsVal| and returns true. Otherwise
+  // returns false and |bsVal| is untouched. |bsVal| must not be NULL.
+  bool GenericName(const CFX_ByteString& bsKey, CFX_ByteString* bsVal) const;
+
  private:
   CPDF_Dictionary* GetViewerPreferences() const;
 
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
index cde15df..9142dc7 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -942,6 +942,25 @@
   return DuplexUndefined;
 }
 
+DLLEXPORT unsigned long STDCALL FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
+                                                       FPDF_BYTESTRING key,
+                                                       char* buffer,
+                                                       unsigned long length) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return 0;
+
+  CPDF_ViewerPreferences viewRef(pDoc);
+  CFX_ByteString bsVal;
+  if (!viewRef.GenericName(key, &bsVal))
+    return 0;
+
+  unsigned long dwStringLen = bsVal.GetLength() + 1;
+  if (buffer && length >= dwStringLen)
+    memcpy(buffer, bsVal.c_str(), dwStringLen);
+  return dwStringLen;
+}
+
 DLLEXPORT FPDF_DWORD STDCALL FPDF_CountNamedDests(FPDF_DOCUMENT document) {
   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
   if (!pDoc)
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
index 2f0cb49..4847180 100644
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ b/fpdfsdk/fpdfview_c_api_test.c
@@ -228,6 +228,7 @@
     CHK(FPDF_VIEWERREF_GetNumCopies);
     CHK(FPDF_VIEWERREF_GetPrintPageRange);
     CHK(FPDF_VIEWERREF_GetDuplex);
+    CHK(FPDF_VIEWERREF_GetName);
     CHK(FPDF_CountNamedDests);
     CHK(FPDF_GetNamedDestByName);
     CHK(FPDF_GetNamedDest);
diff --git a/fpdfsdk/fpdfview_embeddertest.cpp b/fpdfsdk/fpdfview_embeddertest.cpp
index 820d496..e712edb 100644
--- a/fpdfsdk/fpdfview_embeddertest.cpp
+++ b/fpdfsdk/fpdfview_embeddertest.cpp
@@ -56,6 +56,10 @@
   EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
   EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
 
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+
   EXPECT_EQ(0u, FPDF_CountNamedDests(document()));
 }
 
@@ -69,11 +73,53 @@
   EXPECT_EQ(nullptr, LoadPage(1));
 }
 
-TEST_F(FPDFViewEmbeddertest, ViewerRef) {
+TEST_F(FPDFViewEmbeddertest, ViewerRefDummy) {
   EXPECT_TRUE(OpenDocument("about_blank.pdf"));
   EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
   EXPECT_EQ(1, FPDF_VIEWERREF_GetNumCopies(document()));
   EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
+
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+}
+
+TEST_F(FPDFViewEmbeddertest, ViewerRef) {
+  EXPECT_TRUE(OpenDocument("viewer_ref.pdf"));
+  EXPECT_TRUE(FPDF_VIEWERREF_GetPrintScaling(document()));
+  EXPECT_EQ(5, FPDF_VIEWERREF_GetNumCopies(document()));
+  EXPECT_EQ(DuplexUndefined, FPDF_VIEWERREF_GetDuplex(document()));
+
+  // Test some corner cases.
+  char buf[100];
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "", buf, sizeof(buf)));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", nullptr, 0));
+  EXPECT_EQ(0U, FPDF_VIEWERREF_GetName(document(), "foo", buf, sizeof(buf)));
+
+  // Make sure |buf| does not get written into when it appears to be too small.
+  strcpy(buf, "ABCD");
+  EXPECT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, 1));
+  EXPECT_STREQ("ABCD", buf);
+
+  // Note "Foo" is a different key from "foo".
+  EXPECT_EQ(4U,
+            FPDF_VIEWERREF_GetName(document(), "Foo", nullptr, sizeof(buf)));
+  ASSERT_EQ(4U, FPDF_VIEWERREF_GetName(document(), "Foo", buf, sizeof(buf)));
+  EXPECT_STREQ("foo", buf);
+
+  // Try to retrieve a boolean and an integer.
+  EXPECT_EQ(
+      0U, FPDF_VIEWERREF_GetName(document(), "HideToolbar", buf, sizeof(buf)));
+  EXPECT_EQ(0U,
+            FPDF_VIEWERREF_GetName(document(), "NumCopies", buf, sizeof(buf)));
+
+  // Try more valid cases.
+  ASSERT_EQ(4U,
+            FPDF_VIEWERREF_GetName(document(), "Direction", buf, sizeof(buf)));
+  EXPECT_STREQ("R2L", buf);
+  ASSERT_EQ(8U,
+            FPDF_VIEWERREF_GetName(document(), "ViewArea", buf, sizeof(buf)));
+  EXPECT_STREQ("CropBox", buf);
 }
 
 TEST_F(FPDFViewEmbeddertest, NamedDests) {
diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h
index 0b78743..c3be0e0 100644
--- a/public/fpdf_doc.h
+++ b/public/fpdf_doc.h
@@ -271,7 +271,7 @@
 // Returns the number of bytes in the title, including trailing zeros.
 //
 // The |buffer| is always encoded in UTF-16LE. The |buffer| is followed by two
-// bytes of zeros indicating the end of the string.  If |buflen| is less then
+// bytes of zeros indicating the end of the string.  If |buflen| is less than
 // the returned length, or |buffer| is NULL, |buffer| will not be modified.
 DLLEXPORT unsigned long STDCALL FPDF_GetMetaText(FPDF_DOCUMENT doc,
                                                  FPDF_BYTESTRING tag,
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 499124d..469053c 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -914,6 +914,25 @@
 DLLEXPORT FPDF_DUPLEXTYPE STDCALL
 FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document);
 
+// Function: FPDF_VIEWERREF_GetName
+//          Gets the contents for a viewer ref, with a given key. The value must
+//          be of type "name".
+// Parameters:
+//          document    -   Handle to the loaded document.
+//          key         -   Name of the key in the viewer pref dictionary.
+//          buffer      -   A string to write the contents of the key to.
+//          length      -   Length of the buffer.
+// Return value:
+//          The number of bytes in the contents, including the NULL terminator.
+//          Thus if the return value is 0, then that indicates an error, such
+//          as when |document| is invalid or |buffer| is NULL. If |length| is
+//          less than the returned length, or |buffer| is NULL, |buffer| will
+//          not be modified.
+DLLEXPORT unsigned long STDCALL FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
+                                                       FPDF_BYTESTRING key,
+                                                       char* buffer,
+                                                       unsigned long length);
+
 // Function: FPDF_CountNamedDests
 //          Get the count of named destinations in the PDF document.
 // Parameters:
diff --git a/testing/resources/viewer_ref.in b/testing/resources/viewer_ref.in
new file mode 100644
index 0000000..62ae372
--- /dev/null
+++ b/testing/resources/viewer_ref.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /ViewerPreferences <<
+    /Foo /foo
+    /HideToolbar true
+    /Direction /R2L
+    /ViewArea /CropBox
+    /NumCopies 5
+  >>
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% Font resource.
+{{object 15 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Content for page 0.
+{{object 21 0}} <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Size 6
+  /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/viewer_ref.pdf b/testing/resources/viewer_ref.pdf
new file mode 100644
index 0000000..fb72107
--- /dev/null
+++ b/testing/resources/viewer_ref.pdf
@@ -0,0 +1,82 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /ViewerPreferences <<
+    /Foo /foo
+    /HideToolbar true
+    /Direction /R2L
+    /ViewArea /CropBox
+    /NumCopies 5
+  >>
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 1
+  /Kids [
+    3 0 R
+  ]
+>>
+endobj
+% Page number 0.
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% Font resource.
+15 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Content for page 0.
+21 0 obj <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+xref
+0 22
+0000000000 65535 f 
+0000000015 00000 n 
+0000000193 00000 n 
+0000000281 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000442 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000537 00000 n 
+trailer <<
+  /Size 6
+  /Root 1 0 R
+>>
+startxref
+625
+%%EOF