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