/// <summary>
    /// Builds a PdfShading from the specified brush. If a gradient contains transparency, a soft mask is created an added to the 
    /// specified graphic state.
    /// </summary>
    PdfShading BuildShading(LinearGradientBrush brush)
    {
      // Setup shading
      PdfShading shading = new PdfShading(Context.PdfDocument);
#if DEBUG
      if (DevHelper.RenderComments)
        shading.Elements.SetString("/@comment", "This is the shading of a LinearGradientBrush");
#endif
      PdfColorMode colorMode = PdfColorMode.Rgb; //this.document.Options.ColorMode;

      PdfDictionary function = BuildShadingFunction(brush.GradientStops, false, colorMode);
#if DEBUG
      if (DevHelper.RenderComments)
        function.Elements.SetString("/@comment", "This is the shading function of a LinearGradientBrush");
#endif
      shading.Elements.SetBoolean(PdfShading.Keys.AntiAlias, false);
      shading.Elements[PdfShading.Keys.Function] = function;
      shading.Elements.SetInteger(PdfShading.Keys.ShadingType, 2); // Axial shading
      shading.Elements.SetName(PdfShading.Keys.ColorSpace, "/DeviceRGB"); // TODO: respect ColorMode

      double x1 = brush.StartPoint.X;
      double y1 = brush.StartPoint.Y;
      double x2 = brush.EndPoint.X;
      double y2 = brush.EndPoint.Y;
      shading.Elements[PdfShading.Keys.Coords] = new PdfLiteral("[{0:0.####} {1:0.####} {2:0.####} {3:0.####}]", x1, y1, x2, y2);

      shading.Elements[PdfShading.Keys.Domain] = new PdfLiteral("[0 1]");
      if (brush.SpreadMethod == SpreadMethod.Pad)
        shading.Elements[PdfShading.Keys.Extend] = new PdfLiteral("[true true]");
      else
      {
        DevHelper.NotImplemented("SpreadMethod." + brush.SpreadMethod.ToString() + " Background painted in green.");
#if DEBUG
        // Note from PDF reference: The background color is applied only when the shading is used as part of
        // a shading pattern, not when it is painted directly with the sh operator.
        shading.Elements[PdfShading.Keys.Background] = new PdfLiteral("[0 1 0]"); // TODO: respect ColorMode
#else
        // Best we can currently do
        shading.Elements[PdfShading.Keys.Extend] = new PdfLiteral("[true true]");
#endif
      }
      return shading;
    }
 protected XPLinearGradientBrush(LinearGradientBrush brush)
   : base(brush)
 {
 }
    /// <summary>
    /// Parses a LinearGradientBrush element.
    /// </summary>
    LinearGradientBrush ParseLinearGradientBrush()
    {
      Debug.Assert(this.reader.Name == "LinearGradientBrush");
      bool isEmptyElement = this.reader.IsEmptyElement;
      LinearGradientBrush brush = new LinearGradientBrush();
      while (MoveToNextAttribute())
      {
        switch (this.reader.Name)
        {
          case "Opacity":
            brush.Opacity = ParseDouble(this.reader.Value);
            break;

          case "Transform":
            brush.Transform = ParseMatrixTransform(this.reader.Value);
            break;

          case "ColorInterpolationMode":
            brush.ColorInterpolationMode = ParseEnum<ClrIntMode>(this.reader.Value);
            break;

          case "SpreadMethod":
            brush.SpreadMethod = ParseEnum<SpreadMethod>(this.reader.Value);
            break;

          case "MappingMode":
            brush.MappingMode = ParseEnum<MappingMode>(this.reader.Value);
            break;

          case "StartPoint":
            brush.StartPoint = Point.Parse(this.reader.Value);
            break;

          case "EndPoint":
            brush.EndPoint = Point.Parse(this.reader.Value);
            break;

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

          default:
            Debugger.Break();
            break;
        }
      }
      if (!isEmptyElement)
      {
        MoveToNextElement();
        while (this.reader.IsStartElement())
        {
          switch (this.reader.Name)
          {
            case "LinearGradientBrush.Transform":
              MoveToNextElement();
              brush.Transform = ParseMatrixTransform();
              MoveToNextElement();
              break;

            case "LinearGradientBrush.GradientStops":
              // do not MoveToNextElement();
              brush.GradientStops = ParseGradientStops();
              break;

            default:
              Debugger.Break();
              break;
          }
        }
      }
      MoveToNextElement();
      return brush;
    }
    void RealizeLinearGradientBrush(LinearGradientBrush brush, XForm xform)
    {
      XMatrix matrix = this.currentTransform;
      PdfShadingPattern pattern = new PdfShadingPattern(this.writer.Owner);
      pattern.Elements[PdfShadingPattern.Keys.PatternType] = new PdfInteger(2); // shading pattern

      // Setup shading
      PdfShading shading = new PdfShading(this.writer.Owner);
      PdfColorMode colorMode = PdfColorMode.Rgb; //this.document.Options.ColorMode;

      PdfDictionary function = BuildShadingFunction(brush.GradientStops, colorMode);
      function.Elements.SetString("/@", "This is the shading function of a LinearGradientBrush");
      shading.Elements[PdfShading.Keys.Function] = function;

      shading.Elements[PdfShading.Keys.ShadingType] = new PdfInteger(2); // Axial shading
      //if (colorMode != PdfColorMode.Cmyk)
      shading.Elements[PdfShading.Keys.ColorSpace] = new PdfName("/DeviceRGB");
      //else
      //shading.Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK");

      //double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
      double x1 = brush.StartPoint.X;
      double y1 = brush.StartPoint.Y;
      double x2 = brush.EndPoint.X;
      double y2 = brush.EndPoint.Y;

      shading.Elements[PdfShading.Keys.Coords] = new PdfLiteral("[{0:0.###} {1:0.###} {2:0.###} {3:0.###}]", x1, y1, x2, y2);

      // old: Elements[Keys.Background] = new PdfRawItem("[0 1 1]");
      // old: Elements[Keys.Domain] = 
      shading.Elements[PdfShading.Keys.Extend] = new PdfLiteral("[true true]");

      // Setup pattern
      pattern.Elements[PdfShadingPattern.Keys.Shading] = shading;
      pattern.Elements[PdfShadingPattern.Keys.Matrix] = PdfLiteral.FromMatrix(matrix); // new PdfLiteral("[" + PdfEncoders.ToString(matrix) + "]");

      string name = this.writer.Resources.AddPattern(pattern);
      this.writer.WriteLiteral("/Pattern cs\n", name);
      this.writer.WriteLiteral("{0} scn\n", name);

      double alpha = brush.Opacity * brush.GradientStops.GetAverageAlpha();
      if (alpha < 1 && this.writer.renderMode == RenderMode.Default)
      {
#if true
        PdfExtGState extGState = this.writer.Owner.ExtGStateTable.GetExtGStateNonStroke(alpha);
        string gs = this.writer.Resources.AddExtGState(extGState);
        this.writer.WriteLiteral("{0} gs\n", gs);
#else
#if true
        if (xform == null)
        {
          PdfExtGState extGState = this.writer.Owner.ExtGStateTable.GetExtGStateNonStroke(alpha);
          string gs = this.writer.Resources.AddExtGState(extGState);
          this.writer.WriteGraphicState(extGState);
        }
        else
        {
          //PdfFormXObject pdfForm = this.writer.Owner.FormTable.GetForm(form);
          PdfFormXObject pdfForm = xform.pdfForm;
          pdfForm.Elements.SetString("/@", "This is the Form XObject of the soft mask");

          string formName = this.writer.Resources.AddForm(pdfForm);

          PdfTransparencyGroupAttributes tgAttributes = new PdfTransparencyGroupAttributes(this.writer.Owner);
          //this.writer.Owner.Internals.AddObject(tgAttributes);
          tgAttributes.Elements.SetName(PdfTransparencyGroupAttributes.Keys.CS, "/DeviceRGB");

          // Set reference to transparency group attributes
          pdfForm.Elements.SetObject(PdfFormXObject.Keys.Group, tgAttributes);
          pdfForm.Elements[PdfFormXObject.Keys.Matrix] = new PdfLiteral("[1.001 0 0 1.001 0.001 0.001]");


          PdfSoftMask softmask = new PdfSoftMask(this.writer.Owner);
          this.writer.Owner.Internals.AddObject(softmask);
          softmask.Elements.SetString("/@", "This is the soft mask");
          softmask.Elements.SetName(PdfSoftMask.Keys.S, "/Luminosity");
          softmask.Elements.SetReference(PdfSoftMask.Keys.G, pdfForm);
          //pdfForm.Elements.SetName(PdfFormXObject.Keys.Type, "Group");
          //pdfForm.Elements.SetName(PdfFormXObject.Keys.ss.Ss.Type, "Group");

          PdfExtGState extGState = new PdfExtGState(this.writer.Owner);
          this.writer.Owner.Internals.AddObject(extGState);
          extGState.Elements.SetReference(PdfExtGState.Keys.SMask, softmask);
          this.writer.WriteGraphicState(extGState);
        }

#else
        XForm form = new XForm(this.writer.Owner, 220, 140);
        XGraphics formGfx = XGraphics.FromForm(form);

        // draw something
        //XSolidBrush xbrush = new XSolidBrush(XColor.FromArgb(128, 128, 128));
        XLinearGradientBrush xbrush = new XLinearGradientBrush(new XPoint(0, 0), new XPoint(220,0), XColors.White, XColors.Black);
        formGfx.DrawRectangle(xbrush, -10000, -10000, 20000, 20000);
        //formGfx.DrawString("Text, Graphics, Images, and Forms", new XFont("Verdana", 10, XFontStyle.Regular), XBrushes.Navy, 3, 0, XStringFormat.TopLeft);
        formGfx.Dispose();

        // Close form
        form.Finish();
        PdfFormXObject pdfForm = this.writer.Owner.FormTable.GetForm(form);
        string formName = this.writer.Resources.AddForm(pdfForm);

        //double x = 20, y = 20;
        //double cx = 1;
        //double cy = 1;

        //this.writer.AppendFormat("q {2:0.###} 0 0 -{3:0.###} {0:0.###} {4:0.###} cm 100 Tz {5} Do Q\n",
        //  x, y, cx, cy, y + 0, formName);

        //this.writer.AppendFormat("q {2:0.###} 0 0 -{3:0.###} {0:0.###} {4:0.###} cm 100 Tz {5} Do Q\n",
        //  x, y, cx, cy, y + 220/1.5, formName);

        PdfTransparencyGroupAttributes tgAttributes = new PdfTransparencyGroupAttributes(this.writer.Owner);
        this.writer.Owner.Internals.AddObject(tgAttributes);

        tgAttributes.Elements.SetName(PdfTransparencyGroupAttributes.Keys.CS, "/DeviceRGB");


        // Set reference to transparency group attributes
        pdfForm.Elements.SetReference(PdfFormXObject.Keys.Group, tgAttributes);


        PdfSoftMask softmask = new PdfSoftMask(this.writer.Owner);
        this.writer.Owner.Internals.AddObject(softmask);
        softmask.Elements.SetName(PdfSoftMask.Keys.S, "/Luminosity");
        softmask.Elements.SetReference(PdfSoftMask.Keys.G, pdfForm);
        //pdfForm.Elements.SetName(PdfFormXObject.Keys.Type, "Group");
        //pdfForm.Elements.SetName(PdfFormXObject.Keys.ss.Ss.Type, "Group");

        PdfExtGState extGState = new PdfExtGState(this.writer.Owner);
        this.writer.Owner.Internals.AddObject(extGState);
        extGState.Elements.SetReference(PdfExtGState.Keys.SMask, softmask);
        this.writer.WriteGraphicState(extGState);
#endif
#endif
      }
    }
    /// <summary>
    /// Builds a monochrome shading for a form XObject of a soft mask.
    /// </summary>
    PdfShading BuildShadingForSoftMask(LinearGradientBrush brush)
    {
      // Setup shading
      //<<
      //  /ShadingType 2
      //  /AntiAlias false
      //  /BBox [0 0 510.236 680.315]
      //  /ColorSpace /DeviceGray
      //  /Coords [5 5 153.492 -86.924]
      //  /Domain [0 1]
      //  /Extend [true true]
      //  /Function 18 0 R
      //>>
      PdfShading shading = Context.PdfDocument.Internals.CreateIndirectObject<PdfShading>();
#if DEBUG
      if (DevHelper.RenderComments)
        shading.Elements.SetString("/@comment", "This is the shading function of a soft mask");
#endif
      shading.Elements.SetInteger(PdfShading.Keys.ShadingType, 2); // Axial shading
      shading.Elements.SetBoolean(PdfShading.Keys.AntiAlias, false);
      // TODO: BBox full page
      //shading.Elements.SetValue(PdfShading.Keys.BBox, new PdfLiteral("[0 0 480 640]"));
      shading.Elements.SetName(PdfShading.Keys.ColorSpace, "/DeviceGray");

      double x1 = brush.StartPoint.X;
      double y1 = brush.StartPoint.Y;
      double x2 = brush.EndPoint.X;
      double y2 = brush.EndPoint.Y;
      shading.Elements.SetValue(PdfShading.Keys.Coords, new PdfLiteral("[{0:0.####} {1:0.####} {2:0.####} {3:0.####}]", x1, y1, x2, y2));

      shading.Elements.SetValue(PdfShading.Keys.Domain, new PdfLiteral("[0 1]"));
      shading.Elements.SetValue(PdfShading.Keys.Extend, new PdfLiteral("[true true]"));

      PdfColorMode colorMode = PdfColorMode.Rgb; //this.document.Options.ColorMode;
      PdfDictionary function = BuildShadingFunction(brush.GradientStops, true, colorMode);
#if true
      shading.Elements.SetValue(PdfShading.Keys.Function, function);
#else
      Context.PdfDocument.Internals.AddObject(function);
      shading.Elements.SetReference(PdfShading.Keys.Function, function);
#endif

      return shading;
    }
 /// <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;
 }
 /// <summary>
 /// Builds a pattern from a linear gradient brush.
 /// </summary>
 public static PdfShadingPattern BuildPatternFromLinearGradientBrush(DocumentRenderingContext context, LinearGradientBrush brush, XMatrix transform)
 {
   LinearShadingBuilder builder = new LinearShadingBuilder(context);
   PdfShadingPattern pattern = builder.BuildShadingPattern(brush, transform);
   return pattern;
 }
 /// <summary>
 /// Builds a shading from a linear gradient brush.
 /// </summary>
 public static PdfShading BuildShadingFromLinearGradientBrush(DocumentRenderingContext context, LinearGradientBrush brush)
 {
   LinearShadingBuilder builder = new LinearShadingBuilder(context);
   PdfShading shading = builder.BuildShading(brush);
   return shading;
 }
    /// <summary>
    /// Builds the soft mask.
    /// </summary>
    PdfSoftMask BuildSoftMask(LinearGradientBrush brush)
    {
      Debug.Assert(brush.GradientStops.HasTransparency);

      XRect viewBox = new XRect(0, 0, 360, 480); // HACK
      //XForm xform = new XForm(Context.PdfDocument, viewBox);

      PdfFormXObject form = Context.PdfDocument.Internals.CreateIndirectObject<PdfFormXObject>();
#if DEBUG
      if (DevHelper.RenderComments)
        form.Elements.SetString("/@comment", "This is the Form XObject of the soft mask");
#endif
      form.Elements.SetRectangle(PdfFormXObject.Keys.BBox, new PdfRectangle(viewBox));


      // Transparency group of mask form
      //<<
      //  /CS /DeviceGray
      //  /I false
      //  /K false
      //  /S /Transparency
      //  /Type /Group
      //>>
      PdfTransparencyGroupAttributes tgAttributes = Context.PdfDocument.Internals.CreateIndirectObject<PdfTransparencyGroupAttributes>();
      tgAttributes.Elements.SetName(PdfTransparencyGroupAttributes.Keys.CS, "/DeviceGray");
      tgAttributes.Elements.SetBoolean(PdfTransparencyGroupAttributes.Keys.I, false);
      tgAttributes.Elements.SetBoolean(PdfTransparencyGroupAttributes.Keys.K, false);

      // ExtGState of mask form
      //<<
      //  /AIS false
      //  /BM /Normal
      //  /ca 1
      //  /CA 1
      //  /op false
      //  /OP false
      //  /OPM 1
      //  /SA true
      //  /SMask /None
      //  /Type /ExtGState
      //>>
      PdfExtGState pdfStateMaskFrom = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>();
      pdfStateMaskFrom.SetDefault1();

      // Shading of mask form
      PdfShading shadingFrom = BuildShadingForSoftMask(brush);

      ////// Set reference to transparency group attributes
      ////pdfForm.Elements.SetObject(PdfFormXObject.Keys.Group, tgAttributes);
      ////pdfForm.Elements[PdfFormXObject.Keys.Matrix] = new PdfLiteral("[1.001 0 0 1.001 0.001 0.001]");

      // Soft mask
      //<<
      //  /G 21 0 R   % form
      //  /S /Luminosity
      //  /Type /Mask
      //>>
      PdfSoftMask softmask = Context.PdfDocument.Internals.CreateIndirectObject<PdfSoftMask>();  // new PdfSoftMask(this.writer.Owner);
      //extGState.Elements.SetReference(PdfExtGState.Keys.SMask, softmask);
      //this.writer.Owner.Internals.AddObject(softmask);
#if DEBUG
      if (DevHelper.RenderComments)
        softmask.Elements.SetString("/@comment", "This is the soft mask");
#endif
      softmask.Elements.SetName(PdfSoftMask.Keys.S, "/Luminosity");
      softmask.Elements.SetReference(PdfSoftMask.Keys.G, form);

      // Create content of mask form
      //<<
      //  /BBox [200.118 369.142 582.795 -141.094]
      //  /Group 16 0 R
      //  /Length 121
      //  /Matrix [1 0 0 1 0 0]
      //  /Resources
      //  <<
      //    /ExtGState
      //    <<
      //      /GS0 20 0 R
      //    >>
      //    /Shading
      //    <<
      //      /Sh0 19 0 R
      //    >>
      //  >>
      //  /Subtype /Form
      //>>
      //stream
      //  q
      //    200.118 369.142 382.677 -510.236 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
      form.Elements.SetReference(PdfFormXObject.Keys.Group, tgAttributes);
      PdfContentWriter writer = new PdfContentWriter(Context, form);
      writer.BeginContentRaw();
      // Acrobat 8 clips to bounding box, so we should do
      // why   0 480 360 -480 re ??
      //writer.WriteClip(bbox);
      //writer.WriteGraphicsState(extGState);
      writer.WriteLiteral("1 i 0 g\n");
      writer.WriteLiteral(writer.Resources.AddExtGState(pdfStateMaskFrom) + " 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(shadingFrom) + " sh EX\n");
      writer.EndContent();

      return softmask;
    }
    /// <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(LinearGradientBrush 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);

      // 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>
    /// Builds a PdfShadingPattern from the specified brush.
    /// </summary>
    PdfShadingPattern BuildShadingPattern(LinearGradientBrush brush, XMatrix transform)
    {
      //<<
      //  /Matrix [0.75 0 0 -0.75 8 470.4]
      //  /PatternType 2
      //  /Shading 22 0 R
      //  /Type /Pattern
      //>>
      XMatrix matrix = transform;
      PdfShadingPattern pattern = Context.PdfDocument.Internals.CreateIndirectObject<PdfShadingPattern>();
      pattern.Elements.SetInteger(PdfShadingPattern.Keys.PatternType, 2);  // Shading
      pattern.Elements.SetMatrix(PdfShadingPattern.Keys.Matrix, matrix);

      PdfShading shading = BuildShading(brush);
      Context.PdfDocument.Internals.AddObject(shading);
      pattern.Elements.SetReference(PdfShadingPattern.Keys.Shading, shading);

      return pattern;
    }