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