1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/page/cpdf_page.h"
8
9 #include <set>
10 #include <utility>
11
12 #include "constants/page_object.h"
13 #include "core/fpdfapi/page/cpdf_contentparser.h"
14 #include "core/fpdfapi/page/cpdf_pageobject.h"
15 #include "core/fpdfapi/parser/cpdf_array.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_object.h"
18 #include "third_party/base/ptr_util.h"
19 #include "third_party/base/stl_util.h"
20
CPDF_Page(CPDF_Document * pDocument,CPDF_Dictionary * pPageDict)21 CPDF_Page::CPDF_Page(CPDF_Document* pDocument, CPDF_Dictionary* pPageDict)
22 : CPDF_PageObjectHolder(pDocument, pPageDict, nullptr, nullptr),
23 m_PageSize(100, 100),
24 m_pPDFDocument(pDocument) {
25 ASSERT(pPageDict);
26
27 // Cannot initialize |m_pResources| and |m_pPageResources| via the
28 // CPDF_PageObjectHolder ctor because GetPageAttr() requires
29 // CPDF_PageObjectHolder to finish initializing first.
30 CPDF_Object* pPageAttr = GetPageAttr(pdfium::page_object::kResources);
31 m_pResources.Reset(pPageAttr ? pPageAttr->GetDict() : nullptr);
32 m_pPageResources = m_pResources;
33
34 UpdateDimensions();
35 m_Transparency.SetIsolated();
36 LoadTransparencyInfo();
37 }
38
~CPDF_Page()39 CPDF_Page::~CPDF_Page() {}
40
AsPDFPage()41 CPDF_Page* CPDF_Page::AsPDFPage() {
42 return this;
43 }
44
AsXFAPage()45 CPDFXFA_Page* CPDF_Page::AsXFAPage() {
46 return nullptr;
47 }
48
GetDocument() const49 CPDF_Document* CPDF_Page::GetDocument() const {
50 return GetPDFDocument();
51 }
52
GetPageWidth() const53 float CPDF_Page::GetPageWidth() const {
54 return m_PageSize.width;
55 }
56
GetPageHeight() const57 float CPDF_Page::GetPageHeight() const {
58 return m_PageSize.height;
59 }
60
IsPage() const61 bool CPDF_Page::IsPage() const {
62 return true;
63 }
64
ParseContent()65 void CPDF_Page::ParseContent() {
66 if (GetParseState() == ParseState::kParsed)
67 return;
68
69 if (GetParseState() == ParseState::kNotParsed)
70 StartParse(pdfium::MakeUnique<CPDF_ContentParser>(this));
71
72 ASSERT(GetParseState() == ParseState::kParsing);
73 ContinueParse(nullptr);
74 }
75
GetPageAttr(const ByteString & name) const76 CPDF_Object* CPDF_Page::GetPageAttr(const ByteString& name) const {
77 CPDF_Dictionary* pPageDict = GetDict();
78 std::set<CPDF_Dictionary*> visited;
79 while (1) {
80 visited.insert(pPageDict);
81 if (CPDF_Object* pObj = pPageDict->GetDirectObjectFor(name))
82 return pObj;
83
84 pPageDict = pPageDict->GetDictFor(pdfium::page_object::kParent);
85 if (!pPageDict || pdfium::ContainsKey(visited, pPageDict))
86 break;
87 }
88 return nullptr;
89 }
90
GetBox(const ByteString & name) const91 CFX_FloatRect CPDF_Page::GetBox(const ByteString& name) const {
92 CFX_FloatRect box;
93 CPDF_Array* pBox = ToArray(GetPageAttr(name));
94 if (pBox) {
95 box = pBox->GetRect();
96 box.Normalize();
97 }
98 return box;
99 }
100
DeviceToPage(const FX_RECT & rect,int rotate,const CFX_PointF & device_point) const101 Optional<CFX_PointF> CPDF_Page::DeviceToPage(
102 const FX_RECT& rect,
103 int rotate,
104 const CFX_PointF& device_point) const {
105 CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
106 return page2device.GetInverse().Transform(device_point);
107 }
108
PageToDevice(const FX_RECT & rect,int rotate,const CFX_PointF & page_point) const109 Optional<CFX_PointF> CPDF_Page::PageToDevice(
110 const FX_RECT& rect,
111 int rotate,
112 const CFX_PointF& page_point) const {
113 CFX_Matrix page2device = GetDisplayMatrix(rect, rotate);
114 return page2device.Transform(page_point);
115 }
116
GetDisplayMatrix(const FX_RECT & rect,int iRotate) const117 CFX_Matrix CPDF_Page::GetDisplayMatrix(const FX_RECT& rect, int iRotate) const {
118 if (m_PageSize.width == 0 || m_PageSize.height == 0)
119 return CFX_Matrix();
120
121 float x0 = 0;
122 float y0 = 0;
123 float x1 = 0;
124 float y1 = 0;
125 float x2 = 0;
126 float y2 = 0;
127 iRotate %= 4;
128 // This code implicitly inverts the y-axis to account for page coordinates
129 // pointing up and bitmap coordinates pointing down. (x0, y0) is the base
130 // point, (x1, y1) is that point translated on y and (x2, y2) is the point
131 // translated on x. On iRotate = 0, y0 is rect.bottom and the translation
132 // to get y1 is performed as negative. This results in the desired
133 // transformation.
134 switch (iRotate) {
135 case 0:
136 x0 = rect.left;
137 y0 = rect.bottom;
138 x1 = rect.left;
139 y1 = rect.top;
140 x2 = rect.right;
141 y2 = rect.bottom;
142 break;
143 case 1:
144 x0 = rect.left;
145 y0 = rect.top;
146 x1 = rect.right;
147 y1 = rect.top;
148 x2 = rect.left;
149 y2 = rect.bottom;
150 break;
151 case 2:
152 x0 = rect.right;
153 y0 = rect.top;
154 x1 = rect.right;
155 y1 = rect.bottom;
156 x2 = rect.left;
157 y2 = rect.top;
158 break;
159 case 3:
160 x0 = rect.right;
161 y0 = rect.bottom;
162 x1 = rect.left;
163 y1 = rect.bottom;
164 x2 = rect.right;
165 y2 = rect.top;
166 break;
167 }
168 CFX_Matrix matrix((x2 - x0) / m_PageSize.width, (y2 - y0) / m_PageSize.width,
169 (x1 - x0) / m_PageSize.height,
170 (y1 - y0) / m_PageSize.height, x0, y0);
171 return m_PageMatrix * matrix;
172 }
173
GetPageRotation() const174 int CPDF_Page::GetPageRotation() const {
175 CPDF_Object* pRotate = GetPageAttr(pdfium::page_object::kRotate);
176 int rotate = pRotate ? (pRotate->GetInteger() / 90) % 4 : 0;
177 return (rotate < 0) ? (rotate + 4) : rotate;
178 }
179
UpdateDimensions()180 void CPDF_Page::UpdateDimensions() {
181 CFX_FloatRect mediabox = GetBox(pdfium::page_object::kMediaBox);
182 if (mediabox.IsEmpty())
183 mediabox = CFX_FloatRect(0, 0, 612, 792);
184
185 m_BBox = GetBox(pdfium::page_object::kCropBox);
186 if (m_BBox.IsEmpty())
187 m_BBox = mediabox;
188 else
189 m_BBox.Intersect(mediabox);
190
191 m_PageSize.width = m_BBox.Width();
192 m_PageSize.height = m_BBox.Height();
193
194 switch (GetPageRotation()) {
195 case 0:
196 m_PageMatrix = CFX_Matrix(1.0f, 0, 0, 1.0f, -m_BBox.left, -m_BBox.bottom);
197 break;
198 case 1:
199 std::swap(m_PageSize.width, m_PageSize.height);
200 m_PageMatrix =
201 CFX_Matrix(0, -1.0f, 1.0f, 0, -m_BBox.bottom, m_BBox.right);
202 break;
203 case 2:
204 m_PageMatrix = CFX_Matrix(-1.0f, 0, 0, -1.0f, m_BBox.right, m_BBox.top);
205 break;
206 case 3:
207 std::swap(m_PageSize.width, m_PageSize.height);
208 m_PageMatrix = CFX_Matrix(0, 1.0f, -1.0f, 0, m_BBox.top, -m_BBox.left);
209 break;
210 }
211 }
212
RenderContextClearer(CPDF_Page * pPage)213 CPDF_Page::RenderContextClearer::RenderContextClearer(CPDF_Page* pPage)
214 : m_pPage(pPage) {}
215
~RenderContextClearer()216 CPDF_Page::RenderContextClearer::~RenderContextClearer() {
217 m_pPage->SetRenderContext(nullptr);
218 }
219