xref: /aosp_15_r20/external/pdfium/xfa/fxfa/parser/cxfa_box.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 "fxjs/xfa/cjx_object.h"
15 #include "third_party/base/notreached.h"
16 #include "third_party/base/numerics/safe_conversions.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::base::checked_cast<int32_t>(nIndex), XFA_Element::Edge);
101   }
102   return JSObject()->GetProperty<CXFA_Edge>(
103       pdfium::base::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 absl::optional<int32_t> CXFA_Box::GetStartAngle() {
115   return JSObject()->TryInteger(XFA_Attribute::StartAngle, false);
116 }
117 
GetSweepAngle()118 absl::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   std::vector<CXFA_Stroke*> strokes = GetStrokesInternal(true);
131   CXFA_Stroke* stroke;
132   XFA_AttributeValue iType;
133 
134   std::tie(iType, stroke) = Style3D(strokes);
135   if (iType == XFA_AttributeValue::Unknown)
136     return {XFA_AttributeValue::Unknown, false, 0.0f};
137 
138   return {iType, stroke->IsVisible(), stroke->GetThickness()};
139 }
140 
GetStrokesInternal(bool bNull)141 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokesInternal(bool bNull) {
142   std::vector<CXFA_Stroke*> strokes;
143   strokes.resize(8);
144 
145   for (int32_t i = 0, j = 0; i < 4; i++) {
146     CXFA_Corner* corner;
147     if (i == 0) {
148       corner =
149           JSObject()->GetOrCreateProperty<CXFA_Corner>(i, XFA_Element::Corner);
150     } else {
151       corner = JSObject()->GetProperty<CXFA_Corner>(i, XFA_Element::Corner);
152     }
153 
154     // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
155     // with a null corner in the first position.
156     if (corner || i == 0) {
157       strokes[j] = corner;
158     } else if (!bNull) {
159       if (i == 1 || i == 2)
160         strokes[j] = strokes[0];
161       else
162         strokes[j] = strokes[2];
163     }
164     j++;
165 
166     CXFA_Edge* edge;
167     if (i == 0)
168       edge = JSObject()->GetOrCreateProperty<CXFA_Edge>(i, XFA_Element::Edge);
169     else
170       edge = JSObject()->GetProperty<CXFA_Edge>(i, XFA_Element::Edge);
171 
172     // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
173     // with a null edge in the first position.
174     if (edge || i == 0) {
175       strokes[j] = edge;
176     } else if (!bNull) {
177       if (i == 1 || i == 2)
178         strokes[j] = strokes[1];
179       else
180         strokes[j] = strokes[3];
181     }
182     j++;
183   }
184   return strokes;
185 }
186 
Draw(CFGAS_GEGraphics * pGS,const CFX_RectF & rtWidget,const CFX_Matrix & matrix,bool forceRound)187 void CXFA_Box::Draw(CFGAS_GEGraphics* pGS,
188                     const CFX_RectF& rtWidget,
189                     const CFX_Matrix& matrix,
190                     bool forceRound) {
191   if (GetPresence() != XFA_AttributeValue::Visible)
192     return;
193 
194   XFA_Element eType = GetElementType();
195   if (eType != XFA_Element::Arc && eType != XFA_Element::Border &&
196       eType != XFA_Element::Rectangle) {
197     return;
198   }
199   std::vector<CXFA_Stroke*> strokes;
200   if (!forceRound && eType != XFA_Element::Arc)
201     strokes = GetStrokes();
202 
203   DrawFill(strokes, pGS, rtWidget, matrix, forceRound);
204   XFA_Element type = GetElementType();
205   if (type == XFA_Element::Arc || forceRound) {
206     StrokeArcOrRounded(pGS, rtWidget, matrix, forceRound);
207   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
208     ToRectangle(this)->Draw(strokes, pGS, rtWidget, matrix);
209   } else {
210     NOTREACHED_NORETURN();
211   }
212 }
213 
DrawFill(const std::vector<CXFA_Stroke * > & strokes,CFGAS_GEGraphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix,bool forceRound)214 void CXFA_Box::DrawFill(const std::vector<CXFA_Stroke*>& strokes,
215                         CFGAS_GEGraphics* pGS,
216                         CFX_RectF rtWidget,
217                         const CFX_Matrix& matrix,
218                         bool forceRound) {
219   CXFA_Fill* fill = JSObject()->GetProperty<CXFA_Fill>(0, XFA_Element::Fill);
220   if (!fill || !fill->IsVisible())
221     return;
222 
223   CFGAS_GEPath fillPath;
224   XFA_Element type = GetElementType();
225   CFGAS_GEGraphics::StateRestorer restorer(pGS);
226   if (type == XFA_Element::Arc || forceRound) {
227     CXFA_Edge* edge = GetEdgeIfExists(0);
228     float fThickness = fmax(0.0, edge ? edge->GetThickness() : 0);
229     float fHalf = fThickness / 2;
230     XFA_AttributeValue iHand = GetHand();
231     if (iHand == XFA_AttributeValue::Left)
232       rtWidget.Inflate(fHalf, fHalf);
233     else if (iHand == XFA_AttributeValue::Right)
234       rtWidget.Deflate(fHalf, fHalf);
235 
236     GetPathArcOrRounded(rtWidget, forceRound, &fillPath);
237   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
238     ToRectangle(this)->GetFillPath(strokes, rtWidget, &fillPath);
239   } else {
240     NOTREACHED_NORETURN();
241   }
242   fillPath.Close();
243   fill->Draw(pGS, fillPath, rtWidget, matrix);
244 }
245 
GetPathArcOrRounded(CFX_RectF rtDraw,bool forceRound,CFGAS_GEPath * fillPath)246 void CXFA_Box::GetPathArcOrRounded(CFX_RectF rtDraw,
247                                    bool forceRound,
248                                    CFGAS_GEPath* fillPath) {
249   float a = rtDraw.width / 2.0f;
250   float 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   absl::optional<int32_t> startAngle = GetStartAngle();
260   absl::optional<int32_t> sweepAngle = GetSweepAngle();
261   if (!startAngle.has_value() && !sweepAngle.has_value()) {
262     fillPath->AddEllipse(rtDraw);
263     return;
264   }
265 
266   fillPath->AddArc(rtDraw.TopLeft(), rtDraw.Size(),
267                    -startAngle.value_or(0) * FXSYS_PI / 180.0f,
268                    -sweepAngle.value_or(360) * FXSYS_PI / 180.0f);
269 }
270 
StrokeArcOrRounded(CFGAS_GEGraphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix,bool forceRound)271 void CXFA_Box::StrokeArcOrRounded(CFGAS_GEGraphics* 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_AttributeValue i3DType;
282   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
283   bool lowered3d = false;
284   if (i3DType != XFA_AttributeValue::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_AttributeValue iHand = GetHand();
295   if (iHand == XFA_AttributeValue::Left) {
296     rtWidget.Inflate(fHalf, fHalf);
297   } else if (iHand == XFA_AttributeValue::Right) {
298     rtWidget.Deflate(fHalf, fHalf);
299   }
300   if (!forceRound || !lowered3d) {
301     if (fHalf < 0.001f)
302       return;
303 
304     CFGAS_GEPath arcPath;
305     GetPathArcOrRounded(rtWidget, forceRound, &arcPath);
306     if (edge)
307       edge->Stroke(pGS, arcPath, matrix);
308     return;
309   }
310   CFGAS_GEGraphics::StateRestorer restorer(pGS);
311   pGS->SetLineWidth(fHalf);
312 
313   float a = rtWidget.width / 2.0f;
314   float b = rtWidget.height / 2.0f;
315   if (forceRound) {
316     a = std::min(a, b);
317     b = a;
318   }
319 
320   CFX_PointF center = rtWidget.Center();
321   rtWidget.left = center.x - a;
322   rtWidget.top = center.y - b;
323   rtWidget.width = a + a;
324   rtWidget.height = b + b;
325 
326   CFGAS_GEPath arcPath;
327   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FXSYS_PI / 4.0f,
328                  FXSYS_PI);
329 
330   pGS->SetStrokeColor(CFGAS_GEColor(0xFF808080));
331   pGS->StrokePath(arcPath, matrix);
332   arcPath.Clear();
333   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FXSYS_PI / 4.0f,
334                  FXSYS_PI);
335 
336   pGS->SetStrokeColor(CFGAS_GEColor(0xFFFFFFFF));
337   pGS->StrokePath(arcPath, matrix);
338   rtWidget.Deflate(fHalf, fHalf);
339   arcPath.Clear();
340   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FXSYS_PI / 4.0f,
341                  FXSYS_PI);
342 
343   pGS->SetStrokeColor(CFGAS_GEColor(0xFF404040));
344   pGS->StrokePath(arcPath, matrix);
345   arcPath.Clear();
346   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FXSYS_PI / 4.0f,
347                  FXSYS_PI);
348 
349   pGS->SetStrokeColor(CFGAS_GEColor(0xFFC0C0C0));
350   pGS->StrokePath(arcPath, matrix);
351 }
352