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