From c59fa8895fa6fa8428b9b278eee6f05478ab8f56 Mon Sep 17 00:00:00 2001 From: dsinclair Date: Tue, 8 Nov 2016 06:55:40 -0800 Subject: Add FPDFDest_GetLocationInPage API Add an API to get the value of the /XYZ destination parameter. This CL was originally from https://codereview.chromium.org/1960193003/ by halcanary@. Review-Url: https://codereview.chromium.org/2481743004 --- BUILD.gn | 1 + core/fpdfdoc/cpdf_dest.cpp | 49 ++++++++++++++++++++++++++++++ core/fpdfdoc/cpdf_dest.h | 7 +++++ core/fpdfdoc/cpdf_dest_unittest.cpp | 60 +++++++++++++++++++++++++++++++++++++ fpdfsdk/fpdfdoc.cpp | 27 +++++++++++++++++ fpdfsdk/fpdfdoc_embeddertest.cpp | 25 ++++++++++++++++ fpdfsdk/fpdfdoc_unittest.cpp | 41 +++++++++++++++++++++++++ fpdfsdk/fpdfview_c_api_test.c | 1 + public/fpdf_doc.h | 22 ++++++++++++++ 9 files changed, 233 insertions(+) create mode 100644 core/fpdfdoc/cpdf_dest_unittest.cpp diff --git a/BUILD.gn b/BUILD.gn index 9a8bf07f20..ca9cb7b67a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1652,6 +1652,7 @@ test("pdfium_unittests") { "core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp", "core/fpdfapi/parser/cpdf_syntax_parser_unittest.cpp", "core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp", + "core/fpdfdoc/cpdf_dest_unittest.cpp", "core/fpdfdoc/cpdf_filespec_unittest.cpp", "core/fpdfdoc/cpdf_formfield_unittest.cpp", "core/fpdftext/fpdf_text_int_unittest.cpp", diff --git a/core/fpdfdoc/cpdf_dest.cpp b/core/fpdfdoc/cpdf_dest.cpp index 312ef87346..51e2d0b534 100644 --- a/core/fpdfdoc/cpdf_dest.cpp +++ b/core/fpdfdoc/cpdf_dest.cpp @@ -8,6 +8,8 @@ #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_document.h" +#include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_number.h" namespace { @@ -64,6 +66,53 @@ int CPDF_Dest::GetZoomMode() { return 0; } +bool CPDF_Dest::GetXYZ(bool* pHasX, + bool* pHasY, + bool* pHasZoom, + float* pX, + float* pY, + float* pZoom) const { + *pHasX = false; + *pHasY = false; + *pHasZoom = false; + + CPDF_Array* pArray = ToArray(m_pObj); + if (!pArray) + return false; + + if (pArray->GetCount() < 5) + return false; + + const CPDF_Name* xyz = ToName(pArray->GetDirectObjectAt(1)); + if (!xyz || xyz->GetString() != "XYZ") + return false; + + const CPDF_Number* numX = ToNumber(pArray->GetDirectObjectAt(2)); + const CPDF_Number* numY = ToNumber(pArray->GetDirectObjectAt(3)); + const CPDF_Number* numZoom = ToNumber(pArray->GetDirectObjectAt(4)); + + // If the value is a CPDF_Null then ToNumber will return nullptr. + *pHasX = !!numX; + *pHasY = !!numY; + *pHasZoom = !!numZoom; + + if (numX) + *pX = numX->GetNumber(); + if (numY) + *pY = numY->GetNumber(); + + // A zoom value of 0 is equivalent to a null value, so treat it as a null. + if (numZoom) { + float num = numZoom->GetNumber(); + if (num == 0.0) + *pHasZoom = false; + else + *pZoom = num; + } + + return true; +} + FX_FLOAT CPDF_Dest::GetParam(int index) { CPDF_Array* pArray = ToArray(m_pObj); return pArray ? pArray->GetNumberAt(2 + index) : 0; diff --git a/core/fpdfdoc/cpdf_dest.h b/core/fpdfdoc/cpdf_dest.h index 8f38192fd8..527d1dcf8b 100644 --- a/core/fpdfdoc/cpdf_dest.h +++ b/core/fpdfdoc/cpdf_dest.h @@ -25,6 +25,13 @@ class CPDF_Dest { int GetZoomMode(); FX_FLOAT GetParam(int index); + bool GetXYZ(bool* pHasX, + bool* pHasY, + bool* pHasZoom, + float* pX, + float* pY, + float* pZoom) const; + private: CPDF_Object* m_pObj; }; diff --git a/core/fpdfdoc/cpdf_dest_unittest.cpp b/core/fpdfdoc/cpdf_dest_unittest.cpp new file mode 100644 index 0000000000..d427ab66a0 --- /dev/null +++ b/core/fpdfdoc/cpdf_dest_unittest.cpp @@ -0,0 +1,60 @@ +// Copyright 2016 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/fpdfapi/parser/cpdf_array.h" +#include "core/fpdfapi/parser/cpdf_null.h" +#include "core/fpdfapi/parser/cpdf_number.h" +#include "core/fpdfdoc/cpdf_dest.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/test_support.h" +#include "third_party/base/ptr_util.h" + +TEST(cpdf_dest, GetXYZ) { + bool hasX; + bool hasY; + bool hasZoom; + float x; + float y; + float zoom; + + auto dest = pdfium::MakeUnique(); + EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + + auto array = pdfium::MakeUnique(); + array->AddInteger(0); // Page Index. + array->AddName("XYZ"); + array->AddNumber(4); // X + + // Not enough entries. + dest = pdfium::MakeUnique(array.get()); + EXPECT_FALSE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + + array->AddNumber(5); // Y + array->AddNumber(6); // Zoom. + + dest = pdfium::MakeUnique(array.get()); + EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_TRUE(hasX); + EXPECT_TRUE(hasY); + EXPECT_TRUE(hasZoom); + EXPECT_EQ(4, x); + EXPECT_EQ(5, y); + EXPECT_EQ(6, zoom); + + // Set zoom to 0. + array->SetAt(4, new CPDF_Number(0)); + dest = pdfium::MakeUnique(array.get()); + EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_FALSE(hasZoom); + + // Set values to null. + array->SetAt(2, new CPDF_Null); + array->SetAt(3, new CPDF_Null); + array->SetAt(4, new CPDF_Null); + dest = pdfium::MakeUnique(array.get()); + EXPECT_TRUE(dest->GetXYZ(&hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_FALSE(hasX); + EXPECT_FALSE(hasY); + EXPECT_FALSE(hasZoom); +} diff --git a/fpdfsdk/fpdfdoc.cpp b/fpdfsdk/fpdfdoc.cpp index 5d113596bc..401b3e478b 100644 --- a/fpdfsdk/fpdfdoc.cpp +++ b/fpdfsdk/fpdfdoc.cpp @@ -13,6 +13,7 @@ #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfdoc/cpdf_bookmark.h" #include "core/fpdfdoc/cpdf_bookmarktree.h" +#include "core/fpdfdoc/cpdf_dest.h" #include "fpdfsdk/fsdk_define.h" #include "third_party/base/stl_util.h" @@ -211,6 +212,32 @@ DLLEXPORT unsigned long STDCALL FPDFDest_GetPageIndex(FPDF_DOCUMENT document, return dest.GetPageIndex(pDoc); } +DLLEXPORT FPDF_BOOL STDCALL FPDFDest_GetLocationInPage(FPDF_DEST pDict, + FPDF_BOOL* hasXVal, + FPDF_BOOL* hasYVal, + FPDF_BOOL* hasZoomVal, + FS_FLOAT* x, + FS_FLOAT* y, + FS_FLOAT* zoom) { + if (!pDict) + return false; + + std::unique_ptr dest( + new CPDF_Dest(static_cast(pDict))); + + // FPDF_BOOL is an int, GetXYZ expects bools. + bool bHasX; + bool bHasY; + bool bHasZoom; + if (!dest->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom)) + return false; + + *hasXVal = bHasX; + *hasYVal = bHasY; + *hasZoomVal = bHasZoom; + return true; +} + DLLEXPORT FPDF_LINK STDCALL FPDFLink_GetLinkAtPoint(FPDF_PAGE page, double x, double y) { diff --git a/fpdfsdk/fpdfdoc_embeddertest.cpp b/fpdfsdk/fpdfdoc_embeddertest.cpp index 39b36c4bee..16160936e6 100644 --- a/fpdfsdk/fpdfdoc_embeddertest.cpp +++ b/fpdfsdk/fpdfdoc_embeddertest.cpp @@ -42,6 +42,31 @@ TEST_F(FPDFDocEmbeddertest, DestGetPageIndex) { EXPECT_EQ(0U, FPDFDest_GetPageIndex(document(), dest)); } +TEST_F(FPDFDocEmbeddertest, DestGetLocationInPage) { + EXPECT_TRUE(OpenDocument("named_dests.pdf")); + + // NULL FPDF_DEST case. + EXPECT_EQ(0U, FPDFDest_GetPageIndex(document(), nullptr)); + + FPDF_DEST dest = FPDF_GetNamedDestByName(document(), "First"); + EXPECT_TRUE(dest); + + FPDF_BOOL hasX; + FPDF_BOOL hasY; + FPDF_BOOL hasZoom; + FS_FLOAT x; + FS_FLOAT y; + FS_FLOAT zoom; + EXPECT_TRUE( + FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom)); + EXPECT_TRUE(hasX); + EXPECT_TRUE(hasY); + EXPECT_TRUE(hasZoom); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(1, zoom); +} + TEST_F(FPDFDocEmbeddertest, ActionGetFilePath) { EXPECT_TRUE(OpenDocument("launch_action.pdf")); diff --git a/fpdfsdk/fpdfdoc_unittest.cpp b/fpdfsdk/fpdfdoc_unittest.cpp index 39e81d52e8..fc902570b4 100644 --- a/fpdfsdk/fpdfdoc_unittest.cpp +++ b/fpdfsdk/fpdfdoc_unittest.cpp @@ -8,12 +8,15 @@ #include #include "core/fpdfapi/cpdf_modulemgr.h" +#include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_null.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_parser.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_string.h" +#include "core/fpdfdoc/cpdf_dest.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/test_support.h" #include "third_party/base/ptr_util.h" @@ -230,3 +233,41 @@ TEST_F(PDFDocTest, FindBookmark) { EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get())); } } + +TEST_F(PDFDocTest, GetLocationInPage) { + auto array = pdfium::MakeUnique(); + array->AddInteger(0); // Page Index. + array->AddName("XYZ"); + array->AddNumber(4); // X + array->AddNumber(5); // Y + array->AddNumber(6); // Zoom. + + FPDF_BOOL hasX; + FPDF_BOOL hasY; + FPDF_BOOL hasZoom; + FS_FLOAT x; + FS_FLOAT y; + FS_FLOAT zoom; + + EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom, + &x, &y, &zoom)); + EXPECT_TRUE(hasX); + EXPECT_TRUE(hasY); + EXPECT_TRUE(hasZoom); + EXPECT_EQ(4, x); + EXPECT_EQ(5, y); + EXPECT_EQ(6, zoom); + + array->SetAt(2, new CPDF_Null); + array->SetAt(3, new CPDF_Null); + array->SetAt(4, new CPDF_Null); + EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom, + &x, &y, &zoom)); + EXPECT_FALSE(hasX); + EXPECT_FALSE(hasY); + EXPECT_FALSE(hasZoom); + + array = pdfium::MakeUnique(); + EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom, + &x, &y, &zoom)); +} diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c index 4847180e46..a48ddb981c 100644 --- a/fpdfsdk/fpdfview_c_api_test.c +++ b/fpdfsdk/fpdfview_c_api_test.c @@ -54,6 +54,7 @@ int CheckPDFiumCApi() { CHK(FPDFAction_GetFilePath); CHK(FPDFAction_GetURIPath); CHK(FPDFDest_GetPageIndex); + CHK(FPDFDest_GetLocationInPage); CHK(FPDFLink_GetLinkAtPoint); CHK(FPDFLink_GetLinkZOrderAtPoint); CHK(FPDFLink_GetDest); diff --git a/public/fpdf_doc.h b/public/fpdf_doc.h index c3be0e0598..206dc37a27 100644 --- a/public/fpdf_doc.h +++ b/public/fpdf_doc.h @@ -171,6 +171,28 @@ DLLEXPORT unsigned long STDCALL FPDFAction_GetURIPath(FPDF_DOCUMENT document, DLLEXPORT unsigned long STDCALL FPDFDest_GetPageIndex(FPDF_DOCUMENT document, FPDF_DEST dest); +// Get the (x, y, zoom) location of |dest| in the destination page, if the +// destination is in [page /XYZ x y zoom] syntax. +// +// dest - handle to the destination. +// hasXVal - out parameter; true if the x value is not null +// hasYVal - out parameter; true if the y value is not null +// hasZoomVal - out parameter; true if the zoom value is not null +// x - out parameter; the x coordinate, in page coordinates. +// y - out parameter; the y coordinate, in page coordinates. +// zoom - out parameter; the zoom value. +// Returns TRUE on successfully reading the /XYZ value. +// +// Note the [x, y, zoom] values are only set if the corresponding hasXVal, +// hasYVal or hasZoomVal flags are true. +DLLEXPORT FPDF_BOOL STDCALL FPDFDest_GetLocationInPage(FPDF_DEST dest, + FPDF_BOOL* hasXCoord, + FPDF_BOOL* hasYCoord, + FPDF_BOOL* hasZoom, + FS_FLOAT* x, + FS_FLOAT* y, + FS_FLOAT* zoom); + // Find a link at point (|x|,|y|) on |page|. // // page - handle to the document page. -- cgit v1.2.3