/// <summary>
    /// Parses a PathGeometry element.
    /// </summary>
    PathGeometry ParsePathGeometry()
    {
      Debug.Assert(this.reader.Name == "PathGeometry");
      bool isEmptyElement = this.reader.IsEmptyElement;
      PathGeometry geo = new PathGeometry();
      while (MoveToNextAttribute())
      {
        switch (this.reader.Name)
        {
          case "Transform":
            geo.Transform = ParseMatrixTransform(this.reader.Value);
            break;

          case "Figures":
            geo.Figures = ParsePathGeometry(this.reader.Value).Figures;
            break;

          case "FillRule":
            geo.FillRule = ParseEnum<FillRule>(this.reader.Value);
            break;

          case "x:Key":
            geo.Key = this.reader.Value;
            break;

          default:
            UnexpectedAttribute(this.reader.Name);
            break;
        }
      }
      if (!isEmptyElement)
      {
        MoveToNextElement();
        while (this.reader.IsStartElement())
        {
          switch (this.reader.Name)
          {
            case "PathGeometry.RenderTransform":
              MoveToNextElement();
              geo.Transform = ParseMatrixTransform();
              MoveToNextElement();
              break;

            case "PathFigure":
              geo.Figures.Add(ParsePathFigure());
              break;

            default:
              Debugger.Break();
              break;
          }
        }
      }
      MoveToNextElement();
      return geo;
    }
Ejemplo n.º 2
0
    /// <summary>
    /// Writes the specified PathGeometry to the content stream.
    /// </summary>
    internal void WriteGeometry(PathGeometry geo)
    {
      BeginGraphic();
      foreach (PathFigure figure in geo.Figures)
      {
        PolyLineSegment pseg;
        PolyBezierSegment bseg;
        ArcSegment aseg;
        PolyQuadraticBezierSegment qseg;

        // And now for the most superfluous and unnecessary optimization within the whole PDFsharp library
        if (figure.IsClosed && figure.Segments.Count == 1 && (pseg = figure.Segments[0] as PolyLineSegment) != null && pseg.Points.Count == 3)
        {
          // Identify rectangles
          Point pt0 = figure.StartPoint;
          Point pt1 = pseg.Points[0];
          Point pt2 = pseg.Points[1];
          Point pt3 = pseg.Points[2];
          // This
          //   M16,0 L24,0 24,144 16,144 Z
          // becomes
          //   16 0 m  24 0 l  24 144 l  16 144 l  h
          // but shorter is this
          //   16 0 8 144 re
          if (pt0.X == pt3.X && pt0.Y == pt1.Y && pt1.X == pt2.X && pt2.Y == pt3.Y)
          {
            WriteLiteral("{0:0.###} {1:0.###} {2:0.###} {3:0.###} re \n", pt0.X, pt0.Y, pt2.X - pt0.X, pt2.Y - pt1.Y);
            continue;
          }
        }
        WriteMoveStart(figure.StartPoint);
        foreach (PathSegment seg in figure.Segments)
        {
          if ((pseg = seg as PolyLineSegment) != null)
            WriteSegment(pseg);
          else if ((bseg = seg as PolyBezierSegment) != null)
            WriteSegment(bseg);
          else if ((aseg = seg as ArcSegment) != null)
            WriteSegment(aseg);
          else if ((qseg = seg as PolyQuadraticBezierSegment) != null)
            WriteSegment(qseg);
        }
        if (figure.IsClosed)
          WriteLiteral("h\n"); // Close current figure
      }
    }
Ejemplo n.º 3
0
    /// <summary>
    /// Writes the specified PathGeometry as intersection with the current clip path to the content stream.
    /// </summary>
    public void WriteClip(PathGeometry geo)
    {
      BeginGraphic();

      WriteGeometry(geo);

      if (geo.FillRule == FillRule.NonZero)
        WriteLiteral("W n\n");
      else
        WriteLiteral("W* n\n");
    }
    /// <summary>
    /// Builds a PdfFormXObject from the specified brush. 
    /// // If a gradient contains transparency, a soft mask is created an added to the specified graphic state.
    /// </summary>
    PdfFormXObject BuildForm(RadialGradientBrush brush, PathGeometry geometry)
    {
      PdfFormXObject pdfForm = Context.PdfDocument.Internals.CreateIndirectObject<PdfFormXObject>();

      // HACK
      pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, new PdfRectangle(0, 640, 480, 0));

      // Transparency group of the form
      //<<
      //  /I true
      //  /K false
      //  /S /Transparency
      //  /Type /Group
      //>>
      PdfTransparencyGroupAttributes tgPrimaryForm = Context.PdfDocument.Internals.CreateIndirectObject<PdfTransparencyGroupAttributes>();
      // not set by Acrobat: tgAttributes.Elements.SetName(PdfTransparencyGroupAttributes.Keys.CS, "/DeviceRGB");
      tgPrimaryForm.Elements.SetBoolean(PdfTransparencyGroupAttributes.Keys.I, true);
      tgPrimaryForm.Elements.SetBoolean(PdfTransparencyGroupAttributes.Keys.K, false);
      pdfForm.Elements.SetReference(PdfFormXObject.Keys.Group, tgPrimaryForm);

      // Shading
      PdfShading shading = BuildShading(brush, 1, 1);

      // ExtGState 
      //<<
      //  /AIS false
      //  /BM /Normal
      //  /ca 1
      //  /CA 1
      //  /op false
      //  /OP false
      //  /OPM 1
      //  /SA true
      //  /SMask 22 0 R
      //  /Type /ExtGState
      //>>
      PdfExtGState pdfExtGState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>();
      pdfExtGState.SetDefault1();

      // Soft mask
      PdfSoftMask softmask = BuildSoftMask(brush);
      pdfExtGState.Elements.SetReference(PdfExtGState.Keys.SMask, softmask);

      // PdfFormXObject
      //<<
      //  /BBox [200.118 369.142 582.795 -141.094]
      //  /Group 11 0 R
      //  /Length 117
      //  /Matrix [1 0 0 1 0 0]
      //  /Resources
      //  <<
      //    /ColorSpace
      //    <<
      //      /CS0 8 0 R
      //    >>
      //    /ExtGState
      //    <<
      //      /GS0 23 0 R
      //    >>
      //    /Shading
      //    <<
      //      /Sh0 14 0 R
      //    >>
      //  >>
      //  /Subtype /Form
      //>>
      //stream
      //q
      //203.868 365.392 157.5 -97.5 re
      //W* n
      //q
      //0 g
      //1 i 
      ///GS0 gs
      //0.75 0 0 -0.75 200.1181183 369.1417236 cm
      //BX /Sh0 sh EX Q
      //Q
      //endstream
      PdfContentWriter writer = new PdfContentWriter(Context, pdfForm);
      writer.BeginContentRaw();
      // Acrobat 8 clips to bounding box, so we should do
      writer.WriteClip(geometry);
      //writer.WriteGraphicsState(extGState);
      //writer.WriteLiteral("0 g\n");
      writer.WriteLiteral(writer.Resources.AddExtGState(pdfExtGState) + " gs\n");

      XMatrix transform = new XMatrix(); //(brush.Viewport.Width / viewBoxForm.width, 0, 0, brush.Viewport.Height / viewBoxForm.height, 0, 0);
      writer.WriteMatrix(transform);
      writer.WriteLiteral("BX " + writer.Resources.AddShading(shading) + " sh EX\n");
      writer.EndContent();

      return pdfForm;
    }
    /// <summary>
    /// Parses a PathGeometry from a data string element.
    /// </summary>
    PathGeometry ParsePathGeometry(string data)
    {
#if DEBUG_
      // XPS = M 20,100 C 45,50 70,150 95,100 S 145,150 170,100 220,150 245,100 C 220,50 195,150 170,100 S 120,150 95,100 45,150 20,100
      // XXX = M20,100C45,50 70,150 95,100 120,50 145,150 170,100 195,50 220,150 245,100 220,50 195,150 170,100 145,50 120,150 95,100 70,50 45,150 20,100
      if (data.StartsWith("M 20,100 C 45,50 70,150 95,100 S 145,"))
        Debugger.Break();
#endif
      PathGeometry geo = TryParseStaticResource<PathGeometry>(data);
      if (geo != null)
        return geo;

      data = FixHack(data);
      // From the algorithm on page 365 in XPS 1.0 specs
      // See Petzold page 813
      geo = new PathGeometry();
      Point point = new Point();
      PathFigure figure = null;
      TokenizerHelper helper = new TokenizerHelper(data);
      helper.NextTokenRequired();
      do
      {
        string token = helper.GetCurrentToken();
        switch (token[0])
        {
          // FillRule
          case 'F':
            geo.FillRule = helper.NextTokenRequired() == "1" ? FillRule.NonZero : FillRule.EvenOdd;
            break;

          // Move 
          case 'M':
            {
              figure = new PathFigure();
              geo.Figures.Add(figure);
              point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
              figure.StartPoint = point;
            }
            break;

          // Move 
          case 'm':
            {
              figure = new PathFigure();
              geo.Figures.Add(figure);
              point = new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired()));
              figure.StartPoint = point;
            }
            break;

          // Line 
          case 'L':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Line 
          case 'l':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Horizontal Line 
          case 'H':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point.X = ParseDouble(helper.NextTokenRequired());
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Horizontal Line 
          case 'h':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point.X += ParseDouble(helper.NextTokenRequired());
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Vertical Line 
          case 'V':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point.Y = ParseDouble(helper.NextTokenRequired());
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Vertical Line 
          case 'v':
            {
              PolyLineSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyLineSegment) != null)
              { }
              else
              {
                seg = new PolyLineSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                point.Y += ParseDouble(helper.NextTokenRequired());
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Elliptical Arc
          case 'A':
            do
            {
              // I cannot believe it: "A70.1,50.1 1,34 0 0 170.1,30.1"
              // The rotation angle "1,34" uses a ',' instead of a '.' in my German Windows Vista!
              //A70.1,50.1    1,34   0   0   170.1,30.1
              ArcSegment seg = new ArcSegment();
              figure.Segments.Add(seg);
              seg.Size = new Size(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
              seg.RotationAngle = ParseDouble(helper.NextTokenRequired());
              seg.IsLargeArc = helper.NextTokenRequired() == "1";
              seg.SweepDirection = helper.NextTokenRequired() == "1" ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
              point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
              seg.Point = point;
            } while (!Char.IsLetter(helper.PeekNextCharacter()));
            break;

          // Elliptical Arc
          case 'a':
            do
            {
              ArcSegment seg = new ArcSegment();
              figure.Segments.Add(seg);
              seg.Size = new Size(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
              seg.RotationAngle = ParseDouble(helper.NextTokenRequired());
              seg.IsLargeArc = helper.NextTokenRequired() == "1";
              seg.SweepDirection = helper.NextTokenRequired() == "1" ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
              point = new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired()));
              seg.Point = point;
            } while (!Char.IsLetter(helper.PeekNextCharacter()));
            break;

          // Cubic Bézier Curve
          case 'C':
            {
              PolyBezierSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyBezierSegment) != null)
              { }
              else
              {
                seg = new PolyBezierSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                seg.Points.Add(new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired())));
                seg.Points.Add(new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired())));
                point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Cubic Bézier Curve
          case 'c':
            {
              PolyBezierSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyBezierSegment) != null)
              { }
              else
              {
                seg = new PolyBezierSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                seg.Points.Add(new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired())));
                seg.Points.Add(new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired())));
                point = new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Smooth Cubic Bézier Curve
          case 'S':
            {
              PolyBezierSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyBezierSegment) != null)
              { }
              else
              {
                seg = new PolyBezierSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                Point pt = new Point();
                int count = seg.Points.Count;
                segCount = figure.Segments.Count;
                if (count > 0)
                {
                  Point lastCtrlPoint = seg.Points[count - 2];
                  pt.X = 2 * point.X - lastCtrlPoint.X;
                  pt.Y = 2 * point.Y - lastCtrlPoint.Y;
                }
                else if (segCount > 1 && figure.Segments[count - 2] is PolyBezierSegment)
                {
                  PolyBezierSegment lastSeg = (PolyBezierSegment)figure.Segments[count - 2];
                  count = lastSeg.Points.Count;
                  Point lastCtrlPoint = lastSeg.Points[count - 2];
                  pt.X = 2 * point.X - lastCtrlPoint.X;
                  pt.Y = 2 * point.Y - lastCtrlPoint.Y;
                }
                else
                {
                  pt = point;
                }
                seg.Points.Add(pt);
                seg.Points.Add(new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired())));
                point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Smooth Cubic Bézier Curve
          case 's':
            {
              PolyBezierSegment seg;
              int segCount = figure.Segments.Count;
              if (segCount > 0 && (seg = figure.Segments[segCount - 1] as PolyBezierSegment) != null)
              { }
              else
              {
                seg = new PolyBezierSegment();
                figure.Segments.Add(seg);
              }
              do
              {
                Point pt = new Point();
                int count = seg.Points.Count;
                segCount = figure.Segments.Count;
                if (count > 0)
                {
                  Point lastCtrlPoint = seg.Points[count - 2];
                  pt.X = 2 * point.X - lastCtrlPoint.X;
                  pt.Y = 2 * point.Y - lastCtrlPoint.Y;
                }
                else if (segCount > 1 && figure.Segments[count - 2] is PolyBezierSegment)
                {
                  PolyBezierSegment lastSeg = (PolyBezierSegment)figure.Segments[count - 2];
                  count = lastSeg.Points.Count;
                  Point lastCtrlPoint = lastSeg.Points[count - 2];
                  pt.X = 2 * point.X - lastCtrlPoint.X;
                  pt.Y = 2 * point.Y - lastCtrlPoint.Y;
                }
                else
                {
                  pt = point;
                }
                seg.Points.Add(pt);
                seg.Points.Add(new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired())));
                point = new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Quadratic Bézier Curve
          case 'Q':
            {
              PolyQuadraticBezierSegment seg = new PolyQuadraticBezierSegment();
              figure.Segments.Add(seg);
              do
              {
                seg.Points.Add(new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired())));
                point = new Point(ParseDouble(helper.NextTokenRequired()), ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Quadratic Bézier Curve
          case 'q':
            {
              PolyQuadraticBezierSegment seg = new PolyQuadraticBezierSegment();
              figure.Segments.Add(seg);
              do
              {
                seg.Points.Add(new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired())));
                point = new Point(point.X + ParseDouble(helper.NextTokenRequired()), point.Y + ParseDouble(helper.NextTokenRequired()));
                seg.Points.Add(point);
              } while (!Char.IsLetter(helper.PeekNextCharacter()));
            }
            break;

          // Close
          case 'Z':
          case 'z':
            {
              figure.IsClosed = true;
              if (figure.Segments.Count > 0)
              {
                PathSegment seg = figure.Segments[0];
              }
              point = figure.StartPoint;
              figure = null;
            }
            break;

          default:
            Debug.Assert(false);
            break;
        }
      } while (helper.NextToken());
      return geo;
    }
 /// <summary>
 /// Builds a form XObject from a linear gradient brush that uses transparency.
 /// </summary>
 public static PdfFormXObject BuildFormFromLinearGradientBrush(DocumentRenderingContext context, LinearGradientBrush brush, PathGeometry geometry)
 {
   LinearShadingBuilder builder = new LinearShadingBuilder(context);
   PdfFormXObject pdfForm = builder.BuildForm(brush, geometry);
   return pdfForm;
 }