Home | History | Annotate | Download | only in parser
      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 
     27 std::pair<XFA_AttributeEnum, CXFA_Stroke*> Style3D(
     28     const std::vector<CXFA_Stroke*>& strokes) {
     29   if (strokes.empty())
     30     return {XFA_AttributeEnum::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_AttributeEnum iType = stroke->GetStrokeType();
     45   if (iType == XFA_AttributeEnum::Lowered ||
     46       iType == XFA_AttributeEnum::Raised ||
     47       iType == XFA_AttributeEnum::Etched ||
     48       iType == XFA_AttributeEnum::Embossed) {
     49     return {iType, stroke};
     50   }
     51   return {XFA_AttributeEnum::Unknown, stroke};
     52 }
     53 
     54 CXFA_Rectangle* ToRectangle(CXFA_Box* box) {
     55   return static_cast<CXFA_Rectangle*>(box);
     56 }
     57 
     58 }  // namespace
     59 
     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                    const PropertyData* properties,
     66                    const AttributeData* attributes,
     67                    const WideStringView& elementName,
     68                    std::unique_ptr<CJX_Object> js_node)
     69     : CXFA_Node(pDoc,
     70                 ePacket,
     71                 validPackets,
     72                 oType,
     73                 eType,
     74                 properties,
     75                 attributes,
     76                 elementName,
     77                 std::move(js_node)) {}
     78 
     79 CXFA_Box::~CXFA_Box() = default;
     80 
     81 XFA_AttributeEnum CXFA_Box::GetHand() {
     82   return JSObject()->GetEnum(XFA_Attribute::Hand);
     83 }
     84 
     85 XFA_AttributeEnum CXFA_Box::GetPresence() {
     86   return JSObject()
     87       ->TryEnum(XFA_Attribute::Presence, true)
     88       .value_or(XFA_AttributeEnum::Visible);
     89 }
     90 
     91 int32_t CXFA_Box::CountEdges() {
     92   return CountChildren(XFA_Element::Edge, false);
     93 }
     94 
     95 CXFA_Edge* CXFA_Box::GetEdgeIfExists(int32_t nIndex) {
     96   if (nIndex == 0)
     97     return JSObject()->GetOrCreateProperty<CXFA_Edge>(nIndex,
     98                                                       XFA_Element::Edge);
     99   return JSObject()->GetProperty<CXFA_Edge>(nIndex, XFA_Element::Edge);
    100 }
    101 
    102 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokes() {
    103   return GetStrokesInternal(false);
    104 }
    105 
    106 bool CXFA_Box::IsCircular() {
    107   return JSObject()->GetBoolean(XFA_Attribute::Circular);
    108 }
    109 
    110 Optional<int32_t> CXFA_Box::GetStartAngle() {
    111   return JSObject()->TryInteger(XFA_Attribute::StartAngle, false);
    112 }
    113 
    114 Optional<int32_t> CXFA_Box::GetSweepAngle() {
    115   return JSObject()->TryInteger(XFA_Attribute::SweepAngle, false);
    116 }
    117 
    118 CXFA_Fill* CXFA_Box::GetOrCreateFillIfPossible() {
    119   return JSObject()->GetOrCreateProperty<CXFA_Fill>(0, XFA_Element::Fill);
    120 }
    121 
    122 std::tuple<XFA_AttributeEnum, bool, float> CXFA_Box::Get3DStyle() {
    123   if (GetElementType() == XFA_Element::Arc)
    124     return {XFA_AttributeEnum::Unknown, false, 0.0f};
    125 
    126   std::vector<CXFA_Stroke*> strokes = GetStrokesInternal(true);
    127   CXFA_Stroke* stroke;
    128   XFA_AttributeEnum iType;
    129 
    130   std::tie(iType, stroke) = Style3D(strokes);
    131   if (iType == XFA_AttributeEnum::Unknown)
    132     return {XFA_AttributeEnum::Unknown, false, 0.0f};
    133 
    134   return {iType, stroke->IsVisible(), stroke->GetThickness()};
    135 }
    136 
    137 std::vector<CXFA_Stroke*> CXFA_Box::GetStrokesInternal(bool bNull) {
    138   std::vector<CXFA_Stroke*> strokes;
    139   strokes.resize(8);
    140 
    141   for (int32_t i = 0, j = 0; i < 4; i++) {
    142     CXFA_Corner* corner;
    143     if (i == 0) {
    144       corner =
    145           JSObject()->GetOrCreateProperty<CXFA_Corner>(i, XFA_Element::Corner);
    146     } else {
    147       corner = JSObject()->GetProperty<CXFA_Corner>(i, XFA_Element::Corner);
    148     }
    149 
    150     // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
    151     // with a null corner in the first position.
    152     if (corner || i == 0) {
    153       strokes[j] = corner;
    154     } else if (!bNull) {
    155       if (i == 1 || i == 2)
    156         strokes[j] = strokes[0];
    157       else
    158         strokes[j] = strokes[2];
    159     }
    160     j++;
    161 
    162     CXFA_Edge* edge;
    163     if (i == 0)
    164       edge = JSObject()->GetOrCreateProperty<CXFA_Edge>(i, XFA_Element::Edge);
    165     else
    166       edge = JSObject()->GetProperty<CXFA_Edge>(i, XFA_Element::Edge);
    167 
    168     // TODO(dsinclair): If i == 0 and GetOrCreateProperty failed, we can end up
    169     // with a null edge in the first position.
    170     if (edge || i == 0) {
    171       strokes[j] = edge;
    172     } else if (!bNull) {
    173       if (i == 1 || i == 2)
    174         strokes[j] = strokes[1];
    175       else
    176         strokes[j] = strokes[3];
    177     }
    178     j++;
    179   }
    180   return strokes;
    181 }
    182 
    183 void CXFA_Box::Draw(CXFA_Graphics* pGS,
    184                     const CFX_RectF& rtWidget,
    185                     const CFX_Matrix& matrix,
    186                     bool forceRound) {
    187   if (GetPresence() != XFA_AttributeEnum::Visible)
    188     return;
    189 
    190   XFA_Element eType = GetElementType();
    191   if (eType != XFA_Element::Arc && eType != XFA_Element::Border &&
    192       eType != XFA_Element::Rectangle) {
    193     return;
    194   }
    195   std::vector<CXFA_Stroke*> strokes;
    196   if (!forceRound && eType != XFA_Element::Arc)
    197     strokes = GetStrokes();
    198 
    199   DrawFill(strokes, pGS, rtWidget, matrix, forceRound);
    200   XFA_Element type = GetElementType();
    201   if (type == XFA_Element::Arc || forceRound) {
    202     StrokeArcOrRounded(pGS, rtWidget, matrix, forceRound);
    203   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
    204     ToRectangle(this)->Draw(strokes, pGS, rtWidget, matrix);
    205   } else {
    206     NOTREACHED();
    207   }
    208 }
    209 
    210 void CXFA_Box::DrawFill(const std::vector<CXFA_Stroke*>& strokes,
    211                         CXFA_Graphics* pGS,
    212                         CFX_RectF rtWidget,
    213                         const CFX_Matrix& matrix,
    214                         bool forceRound) {
    215   CXFA_Fill* fill = JSObject()->GetProperty<CXFA_Fill>(0, XFA_Element::Fill);
    216   if (!fill || !fill->IsVisible())
    217     return;
    218 
    219   pGS->SaveGraphState();
    220 
    221   CXFA_GEPath fillPath;
    222   XFA_Element type = GetElementType();
    223   if (type == XFA_Element::Arc || forceRound) {
    224     CXFA_Edge* edge = GetEdgeIfExists(0);
    225     float fThickness = std::fmax(0.0, edge ? edge->GetThickness() : 0);
    226     float fHalf = fThickness / 2;
    227     XFA_AttributeEnum iHand = GetHand();
    228     if (iHand == XFA_AttributeEnum::Left)
    229       rtWidget.Inflate(fHalf, fHalf);
    230     else if (iHand == XFA_AttributeEnum::Right)
    231       rtWidget.Deflate(fHalf, fHalf);
    232 
    233     GetPathArcOrRounded(rtWidget, fillPath, forceRound);
    234   } else if (type == XFA_Element::Rectangle || type == XFA_Element::Border) {
    235     ToRectangle(this)->GetFillPath(strokes, rtWidget, &fillPath);
    236   } else {
    237     NOTREACHED();
    238   }
    239   fillPath.Close();
    240 
    241   fill->Draw(pGS, &fillPath, rtWidget, matrix);
    242   pGS->RestoreGraphState();
    243 }
    244 
    245 void CXFA_Box::GetPathArcOrRounded(CFX_RectF rtDraw,
    246                                    CXFA_GEPath& fillPath,
    247                                    bool forceRound) {
    248   float a, b;
    249   a = rtDraw.width / 2.0f;
    250   b = rtDraw.height / 2.0f;
    251   if (IsCircular() || forceRound)
    252     a = b = std::min(a, b);
    253 
    254   CFX_PointF center = rtDraw.Center();
    255   rtDraw.left = center.x - a;
    256   rtDraw.top = center.y - b;
    257   rtDraw.width = a + a;
    258   rtDraw.height = b + b;
    259   Optional<int32_t> startAngle = GetStartAngle();
    260   Optional<int32_t> sweepAngle = GetSweepAngle();
    261   if (!startAngle && !sweepAngle) {
    262     fillPath.AddEllipse(rtDraw);
    263     return;
    264   }
    265 
    266   fillPath.AddArc(rtDraw.TopLeft(), rtDraw.Size(),
    267                   -startAngle.value_or(0) * FX_PI / 180.0f,
    268                   -sweepAngle.value_or(360) * FX_PI / 180.0f);
    269 }
    270 
    271 void CXFA_Box::StrokeArcOrRounded(CXFA_Graphics* pGS,
    272                                   CFX_RectF rtWidget,
    273                                   const CFX_Matrix& matrix,
    274                                   bool forceRound) {
    275   CXFA_Edge* edge = GetEdgeIfExists(0);
    276   if (!edge || !edge->IsVisible())
    277     return;
    278 
    279   bool bVisible;
    280   float fThickness;
    281   XFA_AttributeEnum i3DType;
    282   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
    283   bool lowered3d = false;
    284   if (i3DType != XFA_AttributeEnum::Unknown) {
    285     if (bVisible && fThickness >= 0.001f)
    286       lowered3d = true;
    287   }
    288 
    289   float fHalf = edge->GetThickness() / 2;
    290   if (fHalf < 0) {
    291     fHalf = 0;
    292   }
    293 
    294   XFA_AttributeEnum iHand = GetHand();
    295   if (iHand == XFA_AttributeEnum::Left) {
    296     rtWidget.Inflate(fHalf, fHalf);
    297   } else if (iHand == XFA_AttributeEnum::Right) {
    298     rtWidget.Deflate(fHalf, fHalf);
    299   }
    300   if (!forceRound || !lowered3d) {
    301     if (fHalf < 0.001f)
    302       return;
    303 
    304     CXFA_GEPath arcPath;
    305     GetPathArcOrRounded(rtWidget, arcPath, forceRound);
    306     if (edge)
    307       edge->Stroke(&arcPath, pGS, matrix);
    308     return;
    309   }
    310   pGS->SaveGraphState();
    311   pGS->SetLineWidth(fHalf);
    312 
    313   float a, b;
    314   a = rtWidget.width / 2.0f;
    315   b = rtWidget.height / 2.0f;
    316   if (forceRound) {
    317     a = std::min(a, b);
    318     b = a;
    319   }
    320 
    321   CFX_PointF center = rtWidget.Center();
    322   rtWidget.left = center.x - a;
    323   rtWidget.top = center.y - b;
    324   rtWidget.width = a + a;
    325   rtWidget.height = b + b;
    326 
    327   float startAngle = 0, sweepAngle = 360;
    328   startAngle = startAngle * FX_PI / 180.0f;
    329   sweepAngle = -sweepAngle * FX_PI / 180.0f;
    330 
    331   CXFA_GEPath arcPath;
    332   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
    333                  FX_PI);
    334 
    335   pGS->SetStrokeColor(CXFA_GEColor(0xFF808080));
    336   pGS->StrokePath(&arcPath, &matrix);
    337   arcPath.Clear();
    338   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
    339                  FX_PI);
    340 
    341   pGS->SetStrokeColor(CXFA_GEColor(0xFFFFFFFF));
    342   pGS->StrokePath(&arcPath, &matrix);
    343   rtWidget.Deflate(fHalf, fHalf);
    344   arcPath.Clear();
    345   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), 3.0f * FX_PI / 4.0f,
    346                  FX_PI);
    347 
    348   pGS->SetStrokeColor(CXFA_GEColor(0xFF404040));
    349   pGS->StrokePath(&arcPath, &matrix);
    350   arcPath.Clear();
    351   arcPath.AddArc(rtWidget.TopLeft(), rtWidget.Size(), -1.0f * FX_PI / 4.0f,
    352                  FX_PI);
    353 
    354   pGS->SetStrokeColor(CXFA_GEColor(0xFFC0C0C0));
    355   pGS->StrokePath(&arcPath, &matrix);
    356   pGS->RestoreGraphState();
    357 }
    358