• 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_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