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 "xfa/fxfa/parser/cxfa_box.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "fxjs/xfa/cjx_object.h"
13 #include "xfa/fxfa/parser/cxfa_corner.h"
14 #include "xfa/fxfa/parser/cxfa_edge.h"
15 #include "xfa/fxfa/parser/cxfa_fill.h"
16 #include "xfa/fxfa/parser/cxfa_margin.h"
17 #include "xfa/fxfa/parser/cxfa_measurement.h"
18 #include "xfa/fxfa/parser/cxfa_node.h"
19 #include "xfa/fxfa/parser/cxfa_rectangle.h"
20 #include "xfa/fxgraphics/cxfa_gepath.h"
21 #include "xfa/fxgraphics/cxfa_gepattern.h"
22 #include "xfa/fxgraphics/cxfa_geshading.h"
23 #include "xfa/fxgraphics/cxfa_graphics.h"
24
25 namespace {
26
Style3D(const std::vector<CXFA_Stroke * > & strokes)27 std::pair<XFA_AttributeValue, CXFA_Stroke*> Style3D(
28 const std::vector<CXFA_Stroke*>& strokes) {
29 if (strokes.empty())
30 return {XFA_AttributeValue::Unknown, nullptr};
31
32 CXFA_Stroke* stroke = strokes[0];
33 for (size_t i = 1; i < strokes.size(); i++) {
34 CXFA_Stroke* find = strokes[i];
35 if (!find)
36 continue;
37 if (!stroke)
38 stroke = find;
39 else if (stroke->GetStrokeType() != find->GetStrokeType())
40 stroke = find;
41 break;
42 }
43
44 XFA_AttributeValue iType = stroke->GetStrokeType();
45 if (iType == XFA_AttributeValue::Lowered ||
46 iType == XFA_AttributeValue::Raised ||
47 iType == XFA_AttributeValue::Etched ||
48 iType == XFA_AttributeValue::Embossed) {
49 return {iType, stroke};
50 }
51 return {XFA_AttributeValue::Unknown, stroke};
52 }
53
ToRectangle(CXFA_Box * box)54 CXFA_Rectangle* ToRectangle(CXFA_Box* box) {
55 return static_cast<CXFA_Rectangle*>(box);
56 }
57
58 } // namespace
59
CXFA_Box(CXFA_Document * pDoc,XFA_PacketType ePacket,uint32_t validPackets,XFA_ObjectType oType,XFA_Element eType,pdfium::span<const PropertyData> properties,pdfium::span<const AttributeData> attributes,std::unique_ptr<CJX_Object> js_node)60 CXFA_Box::CXFA_Box(CXFA_Document* pDoc,
61 XFA_PacketType ePacket,
62 uint32_t validPackets,
63 XFA_ObjectType oType,
64 XFA_Element eType,
65 pdfium::span<const PropertyData> properties,
66 pdfium::span<const AttributeData> attributes,
67 std::unique_ptr<CJX_Object> js_node)
68 : CXFA_Node(pDoc,
69 ePacket,
70 validPackets,
71 oType,
72 eType,
73 properties,
74 attributes,
75 std::move(js_node)) {}
76
77 CXFA_Box::~CXFA_Box() = default;
78
GetHand()79 XFA_AttributeValue CXFA_Box::GetHand() {
80 return JSObject()->GetEnum(XFA_Attribute::Hand);
81 }
82
GetPresence()83 XFA_AttributeValue CXFA_Box::GetPresence() {
84 return JSObject()
85 ->TryEnum(XFA_Attribute::Presence, true)
86 .value_or(XFA_AttributeValue::Visible);
87 }
88
CountEdges()89 int32_t CXFA_Box::CountEdges() {
90 return CountChildren(XFA_Element::Edge, false);
91 }
92
GetEdgeIfExists(int32_t nIndex)93 CXFA_Edge* CXFA_Box::GetEdgeIfExists(int32_t nIndex) {
94 if (nIndex == 0)
95 return JSObject()->GetOrCreateProperty<CXFA_Edge>(nIndex,
96 XFA_Element::Edge);
97 return JSObject()->GetProperty<CXFA_Edge>(nIndex, XFA_Element::Edge);
98 }
99
GetStrokes()100 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokes() {
101 return GetStrokesInternal(false);
102 }
103
IsCircular()104 bool CXFA_Box::IsCircular() {
105 return JSObject()->GetBoolean(XFA_Attribute::Circular);
106 }
107
GetStartAngle()108 Optional<int32_t> CXFA_Box::GetStartAngle() {
109 return JSObject()->TryInteger(XFA_Attribute::StartAngle, false);
110 }
111
GetSweepAngle()112 Optional<int32_t> CXFA_Box::GetSweepAngle() {
113 return JSObject()->TryInteger(XFA_Attribute::SweepAngle, false);
114 }
115
GetOrCreateFillIfPossible()116 CXFA_Fill* CXFA_Box::GetOrCreateFillIfPossible() {
117 return JSObject()->GetOrCreateProperty<CXFA_Fill>(0, XFA_Element::Fill);
118 }
119
Get3DStyle()120 std::tuple<XFA_AttributeValue, bool, float> CXFA_Box::Get3DStyle() {
121 if (GetElementType() == XFA_Element::Arc)
122 return {XFA_AttributeValue::Unknown, false, 0.0f};
123
124 std::vector<CXFA_Stroke*> strokes = GetStrokesInternal(true);
125 CXFA_Stroke* stroke;
126 XFA_AttributeValue iType;
127
128 std::tie(iType, stroke) = Style3D(strokes);
129 if (iType == XFA_AttributeValue::Unknown)
130 return {XFA_AttributeValue::Unknown, false, 0.0f};
131
132 return {iType, stroke->IsVisible(), stroke->GetThickness()};
133 }
134
GetStrokesInternal(bool bNull)135 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokesInternal(bool bNull) {
136 std::vector<CXFA_Stroke*> strokes;
137 strokes.resize(8);
138
139 for (int32_t i = 0, j = 0; i < 4; i++) {
140 CXFA_Corner* corner;
141 if (i == 0) {
142 corner =
143 JSObject()->GetOrCreateProperty<CXFA_Corner>(i, XFA_Element::Corner);
144 } else {
145 corner = JSObject()->GetProperty<CXFA_Corner>(i, XFA_Element::Corner);
146 }
147
148 // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
149 // with a null corner in the first position.
150 if (corner || i == 0) {
151 strokes[j] = corner;
152 } else if (!bNull) {
153 if (i == 1 || i == 2)
154 strokes[j] = strokes[0];
155 else
156 strokes[j] = strokes[2];
157 }
158 j++;
159
160 CXFA_Edge* edge;
161 if (i == 0)
162 edge = JSObject()->GetOrCreateProperty<CXFA_Edge>(i, XFA_Element::Edge);
163 else
164 edge = JSObject()->GetProperty<CXFA_Edge>(i, XFA_Element::Edge);
165
166 // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
167 // with a null edge in the first position.
168 if (edge || i == 0) {
169 strokes[j] = edge;
170 } else if (!bNull) {
171 if (i == 1 || i == 2)
172 strokes[j] = strokes[1];
173 else
174 strokes[j] = strokes[3];
175 }
176 j++;
177 }
178 return strokes;
179 }
180
Draw(CXFA_Graphics * pGS,const CFX_RectF & rtWidget,const CFX_Matrix & matrix,bool forceRound)181 void CXFA_Box::Draw(CXFA_Graphics* pGS,
182 const CFX_RectF& rtWidget,
183 const CFX_Matrix& matrix,
184 bool forceRound) {
185 if (GetPresence() != XFA_AttributeValue::Visible)
186 return;
187
188 XFA_Element eType = GetElementType();
189 if (eType != XFA_Element::Arc && eType != XFA_Element::Border &&
190 eType != XFA_Element::Rectangle) {
191 return;
192 }
193 std::vector<CXFA_Stroke*> strokes;
194 if (!forceRound && eType != XFA_Element::Arc)
195 strokes = GetStrokes();
196
197 DrawFill(strokes, pGS, rtWidget, matrix, forceRound);
198 XFA_Element type = GetElementType();
199 if (type == XFA_Element::Arc || forceRound) {
200 StrokeArcOrRounded(pGS, rtWidget, matrix, forceRound);
201 } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
202 ToRectangle(this)->Draw(strokes, pGS, rtWidget, matrix);
203 } else {
204 NOTREACHED();
205 }
206 }
207
DrawFill(const std::vector<CXFA_Stroke * > & strokes,CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix,bool forceRound)208 void CXFA_Box::DrawFill(const std::vector<CXFA_Stroke*>& strokes,
209 CXFA_Graphics* pGS,
210 CFX_RectF rtWidget,
211 const CFX_Matrix& matrix,
212 bool forceRound) {
213 CXFA_Fill* fill = JSObject()->GetProperty<CXFA_Fill>(0, XFA_Element::Fill);
214 if (!fill || !fill->IsVisible())
215 return;
216
217 pGS->SaveGraphState();
218
219 CXFA_GEPath fillPath;
220 XFA_Element type = GetElementType();
221 if (type == XFA_Element::Arc || forceRound) {
222 CXFA_Edge* edge = GetEdgeIfExists(0);
223 float fThickness = std::fmax(0.0, edge ? edge->GetThickness() : 0);
224 float fHalf = fThickness / 2;
225 XFA_AttributeValue iHand = GetHand();
226 if (iHand == XFA_AttributeValue::Left)
227 rtWidget.Inflate(fHalf, fHalf);
228 else if (iHand == XFA_AttributeValue::Right)
229 rtWidget.Deflate(fHalf, fHalf);
230
231 GetPathArcOrRounded(rtWidget, forceRound, &fillPath);
232 } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
233 ToRectangle(this)->GetFillPath(strokes, rtWidget, &fillPath);
234 } else {
235 NOTREACHED();
236 }
237 fillPath.Close();
238
239 fill->Draw(pGS, &fillPath, rtWidget, matrix);
240 pGS->RestoreGraphState();
241 }
242
GetPathArcOrRounded(CFX_RectF rtDraw,bool forceRound,CXFA_GEPath * fillPath)243 void CXFA_Box::GetPathArcOrRounded(CFX_RectF rtDraw,
244 bool forceRound,
245 CXFA_GEPath* fillPath) {
246 float a, b;
247 a = rtDraw.width / 2.0f;
248 b = rtDraw.height / 2.0f;
249 if (IsCircular() || forceRound)
250 a = b = std::min(a, b);
251
252 CFX_PointF center = rtDraw.Center();
253 rtDraw.left = center.x - a;
254 rtDraw.top = center.y - b;
255 rtDraw.width = a + a;
256 rtDraw.height = b + b;
257 Optional<int32_t> startAngle = GetStartAngle();
258 Optional<int32_t> sweepAngle = GetSweepAngle();
259 if (!startAngle && !sweepAngle) {
260 fillPath->AddEllipse(rtDraw);
261 return;
262 }
263
264 fillPath->AddArc(rtDraw.TopLeft(), rtDraw.Size(),
265 -startAngle.value_or(0) * FX_PI / 180.0f,
266 -sweepAngle.value_or(360) * FX_PI / 180.0f);
267 }
268
StrokeArcOrRounded(CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix,bool forceRound)269 void CXFA_Box::StrokeArcOrRounded(CXFA_Graphics* pGS,
270 CFX_RectF rtWidget,
271 const CFX_Matrix& matrix,
272 bool forceRound) {
273 CXFA_Edge* edge = GetEdgeIfExists(0);
274 if (!edge || !edge->IsVisible())
275 return;
276
277 bool bVisible;
278 float fThickness;
279 XFA_AttributeValue i3DType;
280 std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
281 bool lowered3d = false;
282 if (i3DType != XFA_AttributeValue::Unknown) {
283 if (bVisible && fThickness >= 0.001f)
284 lowered3d = true;
285 }
286
287 float fHalf = edge->GetThickness() / 2;
288 if (fHalf < 0) {
289 fHalf = 0;
290 }
291
292 XFA_AttributeValue iHand = GetHand();
293 if (iHand == XFA_AttributeValue::Left) {
294 rtWidget.Inflate(fHalf, fHalf);
295 } else if (iHand == XFA_AttributeValue::Right) {
296 rtWidget.Deflate(fHalf, fHalf);
297 }
298 if (!forceRound || !lowered3d) {
299 if (fHalf < 0.001f)
300 return;
301
302 CXFA_GEPath arcPath;
303 GetPathArcOrRounded(rtWidget, forceRound, &arcPath);
304 if (edge)
305 edge->Stroke(&arcPath, pGS, matrix);
306 return;
307 }
308 pGS->SaveGraphState();
309 pGS->SetLineWidth(fHalf);
310
311 float a, b;
312 a = rtWidget.width / 2.0f;
313 b = rtWidget.height / 2.0f;
314 if (forceRound) {
315 a = std::min(a, b);
316 b = a;
317 }
318
319 CFX_PointF center = rtWidget.Center();
320 rtWidget.left = center.x - a;
321 rtWidget.top = center.y - b;
322 rtWidget.width = a + a;
323 rtWidget.height = b + b;
324
325 CXFA_GEPath arcPath;
326 arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
327 FX_PI);
328
329 pGS->SetStrokeColor(CXFA_GEColor(0xFF808080));
330 pGS->StrokePath(&arcPath, &matrix);
331 arcPath.Clear();
332 arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
333 FX_PI);
334
335 pGS->SetStrokeColor(CXFA_GEColor(0xFFFFFFFF));
336 pGS->StrokePath(&arcPath, &matrix);
337 rtWidget.Deflate(fHalf, fHalf);
338 arcPath.Clear();
339 arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
340 FX_PI);
341
342 pGS->SetStrokeColor(CXFA_GEColor(0xFF404040));
343 pGS->StrokePath(&arcPath, &matrix);
344 arcPath.Clear();
345 arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
346 FX_PI);
347
348 pGS->SetStrokeColor(CXFA_GEColor(0xFFC0C0C0));
349 pGS->StrokePath(&arcPath, &matrix);
350 pGS->RestoreGraphState();
351 }
352