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