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