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