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_stroke.h"
8 
9 #include <utility>
10 
11 #include "fxjs/xfa/cjx_object.h"
12 #include "xfa/fgas/graphics/cfgas_gegraphics.h"
13 #include "xfa/fxfa/cxfa_ffwidget.h"
14 #include "xfa/fxfa/parser/cxfa_color.h"
15 #include "xfa/fxfa/parser/cxfa_document.h"
16 #include "xfa/fxfa/parser/cxfa_measurement.h"
17 #include "xfa/fxfa/parser/cxfa_node.h"
18 #include "xfa/fxfa/parser/xfa_utils.h"
19 
XFA_StrokeTypeSetLineDash(CFGAS_GEGraphics * pGraphics,XFA_AttributeValue iStrokeType,XFA_AttributeValue iCapType)20 void XFA_StrokeTypeSetLineDash(CFGAS_GEGraphics* pGraphics,
21                                XFA_AttributeValue iStrokeType,
22                                XFA_AttributeValue iCapType) {
23   switch (iStrokeType) {
24     case XFA_AttributeValue::DashDot: {
25       float dashArray[] = {4, 1, 2, 1};
26       if (iCapType != XFA_AttributeValue::Butt) {
27         dashArray[1] = 2;
28         dashArray[3] = 2;
29       }
30       pGraphics->SetLineDash(0, dashArray);
31       break;
32     }
33     case XFA_AttributeValue::DashDotDot: {
34       float dashArray[] = {4, 1, 2, 1, 2, 1};
35       if (iCapType != XFA_AttributeValue::Butt) {
36         dashArray[1] = 2;
37         dashArray[3] = 2;
38         dashArray[5] = 2;
39       }
40       pGraphics->SetLineDash(0, dashArray);
41       break;
42     }
43     case XFA_AttributeValue::Dashed: {
44       float dashArray[] = {5, 1};
45       if (iCapType != XFA_AttributeValue::Butt)
46         dashArray[1] = 2;
47 
48       pGraphics->SetLineDash(0, dashArray);
49       break;
50     }
51     case XFA_AttributeValue::Dotted: {
52       float dashArray[] = {2, 1};
53       if (iCapType != XFA_AttributeValue::Butt)
54         dashArray[1] = 2;
55 
56       pGraphics->SetLineDash(0, dashArray);
57       break;
58     }
59     default:
60       pGraphics->SetSolidLineDash();
61       break;
62   }
63 }
64 
CXFA_Stroke(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,CJX_Object * js_node)65 CXFA_Stroke::CXFA_Stroke(CXFA_Document* pDoc,
66                          XFA_PacketType ePacket,
67                          uint32_t validPackets,
68                          XFA_ObjectType oType,
69                          XFA_Element eType,
70                          pdfium::span<const PropertyData> properties,
71                          pdfium::span<const AttributeData> attributes,
72                          CJX_Object* js_node)
73     : CXFA_Node(pDoc,
74                 ePacket,
75                 validPackets,
76                 oType,
77                 eType,
78                 properties,
79                 attributes,
80                 js_node) {}
81 
82 CXFA_Stroke::~CXFA_Stroke() = default;
83 
IsVisible()84 bool CXFA_Stroke::IsVisible() {
85   XFA_AttributeValue presence = JSObject()
86                                     ->TryEnum(XFA_Attribute::Presence, true)
87                                     .value_or(XFA_AttributeValue::Visible);
88   return presence == XFA_AttributeValue::Visible;
89 }
90 
GetCapType()91 XFA_AttributeValue CXFA_Stroke::GetCapType() {
92   return JSObject()->GetEnum(XFA_Attribute::Cap);
93 }
94 
GetStrokeType()95 XFA_AttributeValue CXFA_Stroke::GetStrokeType() {
96   return JSObject()->GetEnum(XFA_Attribute::Stroke);
97 }
98 
GetThickness() const99 float CXFA_Stroke::GetThickness() const {
100   return GetMSThickness().ToUnit(XFA_Unit::Pt);
101 }
102 
GetMSThickness() const103 CXFA_Measurement CXFA_Stroke::GetMSThickness() const {
104   return JSObject()->GetMeasure(XFA_Attribute::Thickness);
105 }
106 
SetMSThickness(CXFA_Measurement msThinkness)107 void CXFA_Stroke::SetMSThickness(CXFA_Measurement msThinkness) {
108   JSObject()->SetMeasure(XFA_Attribute::Thickness, msThinkness, false);
109 }
110 
GetColor()111 FX_ARGB CXFA_Stroke::GetColor() {
112   CXFA_Color* pNode = GetChild<CXFA_Color>(0, XFA_Element::Color, false);
113   if (!pNode)
114     return 0xFF000000;
115 
116   return StringToFXARGB(
117       pNode->JSObject()->GetCData(XFA_Attribute::Value).AsStringView());
118 }
119 
SetColor(FX_ARGB argb)120 void CXFA_Stroke::SetColor(FX_ARGB argb) {
121   CXFA_Color* pNode =
122       JSObject()->GetOrCreateProperty<CXFA_Color>(0, XFA_Element::Color);
123   if (!pNode)
124     return;
125 
126   int a;
127   int r;
128   int g;
129   int b;
130   std::tie(a, r, g, b) = ArgbDecode(argb);
131   pNode->JSObject()->SetCData(XFA_Attribute::Value,
132                               WideString::Format(L"%d,%d,%d", r, g, b));
133 }
134 
GetJoinType()135 XFA_AttributeValue CXFA_Stroke::GetJoinType() {
136   return JSObject()->GetEnum(XFA_Attribute::Join);
137 }
138 
IsInverted()139 bool CXFA_Stroke::IsInverted() {
140   return JSObject()->GetBoolean(XFA_Attribute::Inverted);
141 }
142 
GetRadius() const143 float CXFA_Stroke::GetRadius() const {
144   return JSObject()
145       ->TryMeasure(XFA_Attribute::Radius, true)
146       .value_or(CXFA_Measurement(0, XFA_Unit::In))
147       .ToUnit(XFA_Unit::Pt);
148 }
149 
SameStyles(CXFA_Stroke * stroke,uint32_t dwFlags)150 bool CXFA_Stroke::SameStyles(CXFA_Stroke* stroke, uint32_t dwFlags) {
151   if (this == stroke)
152     return true;
153   if (fabs(GetThickness() - stroke->GetThickness()) >= 0.01f)
154     return false;
155   if ((dwFlags & XFA_STROKE_SAMESTYLE_NoPresence) == 0 &&
156       IsVisible() != stroke->IsVisible()) {
157     return false;
158   }
159   if (GetStrokeType() != stroke->GetStrokeType())
160     return false;
161   if (GetColor() != stroke->GetColor())
162     return false;
163   if ((dwFlags & XFA_STROKE_SAMESTYLE_Corner) != 0 &&
164       fabs(GetRadius() - stroke->GetRadius()) >= 0.01f) {
165     return false;
166   }
167   return true;
168 }
169 
Stroke(CFGAS_GEPath * pPath,CFGAS_GEGraphics * pGS,const CFX_Matrix & matrix)170 void CXFA_Stroke::Stroke(CFGAS_GEPath* pPath,
171                          CFGAS_GEGraphics* pGS,
172                          const CFX_Matrix& matrix) {
173   if (!IsVisible())
174     return;
175 
176   float fThickness = GetThickness();
177   if (fThickness < 0.001f)
178     return;
179 
180   pGS->SaveGraphState();
181   if (IsCorner() && fThickness > 2 * GetRadius())
182     fThickness = 2 * GetRadius();
183 
184   pGS->SetLineWidth(fThickness);
185   pGS->EnableActOnDash();
186   pGS->SetLineCap(CFX_GraphStateData::LineCapButt);
187   XFA_StrokeTypeSetLineDash(pGS, GetStrokeType(), XFA_AttributeValue::Butt);
188   pGS->SetStrokeColor(CFGAS_GEColor(GetColor()));
189   pGS->StrokePath(pPath, &matrix);
190   pGS->RestoreGraphState();
191 }
192