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