• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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