PdfTilingPattern BuildPattern(ImageBrush brush, XMatrix transform) { // Bounding box repects viewbox //XRect bbox = new XRect(brush.Viewbox.X, brush.Viewbox.Y, brush.Viewbox.Width, brush.Viewbox.Height); // double scalex = brush.Viewport.Width / brush.Viewbox.Width * 96 / pdfForm.DpiX; //BitmapImage src = new BitmapImage(); //BitmapSource cropbrush = BitmapSource.Create() //CroppedBitmap cropbrush = new CroppedBitmap((BitmapSource)brush.ImageSource, new System.Windows.Int32Rect((int)brush.Viewbox.X, (int)brush.Viewbox.Y, (int)brush.Viewbox.Width, (int)brush.Viewbox.Height)); //using (var fileStream = new System.IO.FileStream("c:\\fullimage.png", System.IO.FileMode.Create)) //{ // BitmapEncoder encoder = new PngBitmapEncoder(); // encoder.Frames.Add(BitmapFrame.Create((BitmapSource)brush.ImageSource)); // encoder.Save(fileStream); //} double scaledpix = ((BitmapSource)brush.ImageSource).DpiX; double scaledpiy = ((BitmapSource)brush.ImageSource).DpiY; if (brush.Viewbox.X > 0 || brush.Viewbox.Y > 0) { CroppedBitmap cropbrush = new CroppedBitmap((BitmapSource)brush.ImageSource, new System.Windows.Int32Rect((int)Math.Round((brush.Viewbox.X / 96) * scaledpix), (int)Math.Round((brush.Viewbox.Y / 96) * scaledpiy), (int)Math.Round((brush.Viewbox.Width / 96) * scaledpiy), (int)Math.Round((brush.Viewbox.Height / 96) * scaledpiy))); //cropbrush.DpiX = scaledpix; //cropbrush.DpiY = scaledpiy; brush.ImageSource = cropbrush; //brush.Viewport = new System.Windows.Rect(0, 0, brush.Viewport.Width, brush.Viewport.Height); //brush.Viewbox = new System.Windows.Rect(0, 0, ((Math.Round((brush.Viewbox.Width / 96) * scaledpiy)) / scaledpiy * 96) , brush.Viewbox.Height); brush.Viewbox = new System.Windows.Rect(0, 0, brush.Viewbox.Width, brush.Viewbox.Height); } //brush = new ImageBrush(cropbrush); //BitmapSource //BitmapSource. cropbrushbitmap = new BitmapSource(cropbrush); //using (var fileStream = new System.IO.FileStream("c:\\cropimage.png", System.IO.FileMode.Create)) //{ // BitmapEncoder encoder = new PngBitmapEncoder(); // encoder.Frames.Add(BitmapFrame.Create(cropbrush)); // encoder.Save(fileStream); //} //XRect bbox = new XRect(brush.Viewport.X - brush.Viewbox.X, brush.Viewport.Y - brush.Viewbox.Y, brush.Viewbox.Width, brush.Viewbox.Height); //XRect bbox = new XRect((brush.Viewbox.X / 96) * scaledpix, (brush.Viewbox.Y / 96) * scaledpiy, (brush.Viewbox.Width / 96) * scaledpix, (brush.Viewbox.Height / 96) * scaledpiy); //XRect bbox = new XRect(0, 0, (brush.Viewport.Width / 96) * scaledpix, (brush.Viewport.Height / 96) * scaledpiy); XRect bbox = new XRect(0, 0, brush.Viewport.Width, brush.Viewport.Height); #if true XMatrix matrix = transform; //this only needs to be a translate and to offset the viewbox viewport difference matrix.TranslatePrepend(brush.Viewport.X, brush.Viewport.Y); //matrix.TranslatePrepend((brush.Viewport.X - brush.Viewbox.X)+2, (brush.Viewport.Y - brush.Viewbox.Y)+2); if (brush.Transform != null) { matrix.Prepend(new XMatrix(brush.Transform.Value.M11, brush.Transform.Value.M12, brush.Transform.Value.M21, brush.Transform.Value.M22, brush.Transform.Value.OffsetX, brush.Transform.Value.OffsetY)); } #else double c = 1; XMatrix matrix = new XMatrix(1 * c, 0, 0, 1 * c, brush.Viewport.X * c, brush.Viewport.Y * c); // HACK: 480 XMatrix t = transform; //t.Invert(); t.Prepend(matrix); //t.TranslateAppend(brush.Viewport.X , brush.Viewport.Y); //matrix.Append(t); matrix = t; #endif double xStep = brush.Viewport.Width + 1; // (brush.Viewbox.Width / 96) * scaledpix; //* (brush.TileMode == TileMode.None ? 2 : 1); double yStep = brush.Viewport.Height + 1; // (brush.Viewbox.Height / 96) * scaledpiy; //* (brush.TileMode == TileMode.None ? 2 : 1); // PdfTilingPattern //<< // /BBox [0 0 240 120] // /Length 74 // /Matrix [0.75 0 0 -0.75 0 480] // /PaintType 1 // /PatternType 1 // /Resources // << // /ExtGState // << // /GS0 10 0 R // >> // /XObject // << // /Fm0 17 0 R // >> // >> // /TilingType 3 // /Type /Pattern // /XStep 480 // /YStep 640 //>> //stream // q // 0 0 240 120 re // W n // q // 2.3999939 0 0 1.1999969 0 0 cm // /GS0 gs // /Fm0 Do // Q //Q //endstream PdfTilingPattern pattern = Context.PdfDocument.Internals.CreateIndirectObject <PdfTilingPattern>(); pattern.Elements.SetInteger(PdfTilingPattern.Keys.PatternType, 1); // Tiling pattern.Elements.SetInteger(PdfTilingPattern.Keys.PaintType, 1); // Color pattern.Elements.SetInteger(PdfTilingPattern.Keys.TilingType, 3); // Constant spacing and faster tiling pattern.Elements.SetMatrix(PdfTilingPattern.Keys.Matrix, matrix); pattern.Elements.SetRectangle(PdfTilingPattern.Keys.BBox, new PdfRectangle(bbox)); pattern.Elements.SetReal(PdfTilingPattern.Keys.XStep, xStep); pattern.Elements.SetReal(PdfTilingPattern.Keys.YStep, yStep); // Set extended graphic state like Acrobat do PdfExtGState pdfExtGState = Context.PdfDocument.Internals.CreateIndirectObject <PdfExtGState>(); pdfExtGState.SetDefault1(); PdfFormXObject pdfForm = BuildForm(brush); //XRect viewBoxForm = new XRect(0, 0, 640, 480); PdfContentWriter writer = new PdfContentWriter(Context, pattern); writer.BeginContentRaw(); // Acrobat 8 clips to bounding box, so do we //writer.WriteClip(bbox); XMatrix transformation = new XMatrix(); //double dx = brush.Viewport.Width / brush.Viewbox.Width * 96 / pdfForm.DpiX; //double dy = brush.Viewport.Height / brush.Viewbox.Height * 96 / pdfForm.DpiY; double dx = brush.Viewport.Width / brush.Viewbox.Width * 96 / pdfForm.DpiX; double dy = brush.Viewport.Height / brush.Viewbox.Height * 96 / pdfForm.DpiY; transformation = new XMatrix(dx, 0, 0, dy, 0, 0); writer.WriteMatrix(transformation); writer.WriteGraphicsState(pdfExtGState); string name = writer.Resources.AddForm(pdfForm); writer.WriteLiteral(name + " Do\n"); writer.EndContent(); return(pattern); }
public void Multiply(XMatrix matrix) { Append(matrix); }
// -------------------------------------------------------------------------------------------- #region Append to PDF stream /// <summary> /// Appends one or up to five Bézier curves that interpolate the arc. /// </summary> void AppendPartialArc(double x, double y, double width, double height, double startAngle, double sweepAngle, PathStart pathStart, XMatrix matrix) { // Normalize the angles double α = startAngle; if (α < 0) α = α + (1 + Math.Floor((Math.Abs(α) / 360))) * 360; else if (α > 360) α = α - Math.Floor(α / 360) * 360; Debug.Assert(α >= 0 && α <= 360); double β = sweepAngle; if (β < -360) β = -360; else if (β > 360) β = 360; if (α == 0 && β < 0) α = 360; else if (α == 360 && β > 0) α = 0; // Is it possible that the arc is small starts and ends in same quadrant? bool smallAngle = Math.Abs(β) <= 90; β = α + β; if (β < 0) β = β + (1 + Math.Floor((Math.Abs(β) / 360))) * 360; bool clockwise = sweepAngle > 0; int startQuadrant = Quatrant(α, true, clockwise); int endQuadrant = Quatrant(β, false, clockwise); if (startQuadrant == endQuadrant && smallAngle) AppendPartialArcQuadrant(x, y, width, height, α, β, pathStart, matrix); else { int currentQuadrant = startQuadrant; bool firstLoop = true; do { if (currentQuadrant == startQuadrant && firstLoop) { double ξ = currentQuadrant * 90 + (clockwise ? 90 : 0); AppendPartialArcQuadrant(x, y, width, height, α, ξ, pathStart, matrix); } else if (currentQuadrant == endQuadrant) { double ξ = currentQuadrant * 90 + (clockwise ? 0 : 90); AppendPartialArcQuadrant(x, y, width, height, ξ, β, PathStart.Ignore1st, matrix); } else { double ξ1 = currentQuadrant * 90 + (clockwise ? 0 : 90); double ξ2 = currentQuadrant * 90 + (clockwise ? 90 : 0); AppendPartialArcQuadrant(x, y, width, height, ξ1, ξ2, PathStart.Ignore1st, matrix); } // Don't stop immediately if arc is greater than 270 degrees if (currentQuadrant == endQuadrant && smallAngle) break; else smallAngle = true; if (clockwise) currentQuadrant = currentQuadrant == 3 ? 0 : currentQuadrant + 1; else currentQuadrant = currentQuadrant == 0 ? 3 : currentQuadrant - 1; firstLoop = false; } while (true); } }
internal static XMatrix CreateSkewRadians(double skewX, double skewY) { XMatrix matrix = new XMatrix(); matrix.SetMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0, XMatrixTypes.Unknown); return matrix; }
internal static void TransformRect(ref XRect rect, ref XMatrix matrix) { if (!rect.IsEmpty) { XMatrixTypes type = matrix._type; if (type != XMatrixTypes.Identity) { if ((type & XMatrixTypes.Scaling) != XMatrixTypes.Identity) { rect.X *= matrix._m11; rect.Y *= matrix._m22; rect.Width *= matrix._m11; rect.Height *= matrix._m22; if (rect.Width < 0) { rect.X += rect.Width; rect.Width = -rect.Width; } if (rect.Height < 0) { rect.Y += rect.Height; rect.Height = -rect.Height; } } if ((type & XMatrixTypes.Translation) != XMatrixTypes.Identity) { rect.X += matrix._offsetX; rect.Y += matrix._offsetY; } if (type == XMatrixTypes.Unknown) { XPoint point1 = matrix.Transform(rect.TopLeft); XPoint point2 = matrix.Transform(rect.TopRight); XPoint point3 = matrix.Transform(rect.BottomRight); XPoint point4 = matrix.Transform(rect.BottomLeft); rect.X = Math.Min(Math.Min(point1.X, point2.X), Math.Min(point3.X, point4.X)); rect.Y = Math.Min(Math.Min(point1.Y, point2.Y), Math.Min(point3.Y, point4.Y)); rect.Width = Math.Max(Math.Max(point1.X, point2.X), Math.Max(point3.X, point4.X)) - rect.X; rect.Height = Math.Max(Math.Max(point1.Y, point2.Y), Math.Max(point3.Y, point4.Y)) - rect.Y; } } } }
public void AddTransform(XMatrix value, XMatrixOrder matrixOrder) { _gfxState.AddTransform(value, matrixOrder); }
internal static XMatrix CreateRotationRadians(double angle, double centerX, double centerY) { XMatrix matrix = new XMatrix(); double sin = Math.Sin(angle); double cos = Math.Cos(angle); double offsetX = (centerX * (1.0 - cos)) + (centerY * sin); double offsetY = (centerY * (1.0 - cos)) - (centerX * sin); matrix.SetMatrix(cos, sin, -sin, cos, offsetX, offsetY, XMatrixTypes.Unknown); return matrix; }
/// <summary> /// Builds a PdfTilingPattern pattern from a visual brush. /// </summary> public static PdfTilingPattern BuildFromVisualBrush(DocumentRenderingContext context, VisualBrush brush, XMatrix transform) { TilingPatternBuilder builder = new TilingPatternBuilder(context); PdfTilingPattern pdfPattern = builder.BuildPattern(brush, transform); return(pdfPattern); }
PdfTilingPattern BuildPattern(ImageBrush brush, XMatrix transform) { // Bounding box lays always at (0,0) XRect bbox = new XRect(0, 0, brush.Viewport.Width, brush.Viewport.Height); #if true XMatrix matrix = transform; matrix.Prepend(new XMatrix(1, 0, 0, 1, brush.Viewport.X, brush.Viewport.Y)); #else double c = 1; XMatrix matrix = new XMatrix(1 * c, 0, 0, 1 * c, brush.Viewport.X * c, brush.Viewport.Y * c); // HACK: 480 XMatrix t = transform; //t.Invert(); t.Prepend(matrix); //t.TranslateAppend(brush.Viewport.X , brush.Viewport.Y); //matrix.Append(t); matrix = t; #endif double xStep = brush.Viewport.Width; double yStep = brush.Viewport.Height; // PdfTilingPattern //<< // /BBox [0 0 240 120] // /Length 74 // /Matrix [0.75 0 0 -0.75 0 480] // /PaintType 1 // /PatternType 1 // /Resources // << // /ExtGState // << // /GS0 10 0 R // >> // /XObject // << // /Fm0 17 0 R // >> // >> // /TilingType 3 // /Type /Pattern // /XStep 480 // /YStep 640 //>> //stream // q // 0 0 240 120 re // W n // q // 2.3999939 0 0 1.1999969 0 0 cm // /GS0 gs // /Fm0 Do // Q //Q //endstream PdfTilingPattern pattern = Context.PdfDocument.Internals.CreateIndirectObject <PdfTilingPattern>(); pattern.Elements.SetInteger(PdfTilingPattern.Keys.PatternType, 1); // Tiling pattern.Elements.SetInteger(PdfTilingPattern.Keys.PaintType, 1); // Color pattern.Elements.SetInteger(PdfTilingPattern.Keys.TilingType, 3); // Constant spacing and faster tiling pattern.Elements.SetMatrix(PdfTilingPattern.Keys.Matrix, matrix); pattern.Elements.SetRectangle(PdfTilingPattern.Keys.BBox, new PdfRectangle(bbox)); pattern.Elements.SetReal(PdfTilingPattern.Keys.XStep, xStep); pattern.Elements.SetReal(PdfTilingPattern.Keys.YStep, yStep); // Set extended graphic state like Acrobat do PdfExtGState pdfExtGState = Context.PdfDocument.Internals.CreateIndirectObject <PdfExtGState>(); pdfExtGState.SetDefault1(); PdfFormXObject pdfForm = BuildForm(brush); //XRect viewBoxForm = new XRect(0, 0, 640, 480); PdfContentWriter writer = new PdfContentWriter(Context, pattern); writer.BeginContentRaw(); // Acrobat 8 clips to bounding box, so do we //writer.WriteClip(bbox); XMatrix transformation = new XMatrix(); double dx = brush.Viewport.Width / brush.Viewbox.Width * 96 / pdfForm.DpiX; double dy = brush.Viewport.Height / brush.Viewbox.Height * 96 / pdfForm.DpiY; transformation = new XMatrix(dx, 0, 0, dy, 0, 0); writer.WriteMatrix(transformation); writer.WriteGraphicsState(pdfExtGState); string name = writer.Resources.AddForm(pdfForm); writer.WriteLiteral(name + " Do\n"); writer.EndContent(); return(pattern); }
/// <summary> /// Builds a PdfFormXObject from the specified brush. /// </summary> PdfFormXObject BuildForm(ImageBrush brush) { //<< // /BBox [0 100 100 0] // /Length 65 // /Matrix [1 0 0 1 0 0] // /Resources // << // /ColorSpace // << // /CS0 15 0 R // >> // /ExtGState // << // /GS0 10 0 R // >> // /ProcSet [/PDF /ImageC /ImageI] // /XObject // << // /Im0 16 0 R // >> // >> // /Subtype /Form //>> //stream // q // 0 0 100 100 re // W n // q // /GS0 gs // 100 0 0 -100 0 100 cm // /Im0 Do // Q //Q //endstream PdfFormXObject pdfForm = Context.PdfDocument.Internals.CreateIndirectObject <PdfFormXObject>(); XPImage xpImage = ImageBuilder.FromImageBrush(Context, brush); XImage ximage = xpImage.XImage; ximage.Interpolate = false; double width = ximage.PixelWidth; double height = ximage.PixelHeight; pdfForm.DpiX = ximage.HorizontalResolution; pdfForm.DpiY = ximage.VerticalResolution; // view box in point // XRect box = new XRect(brush.Viewbox.X * 0.75, brush.Viewbox.Y * 0.75, brush.Viewbox.Width * 0.75, brush.Viewbox.Height * 0.75); XRect box = new XRect(0, 0, width, height); pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, new PdfRectangle(0, height, width, 0)); pdfForm.Elements.SetMatrix(PdfFormXObject.Keys.Matrix, new XMatrix()); PdfContentWriter writer = new PdfContentWriter(Context, pdfForm); Debug.Assert(ximage != null); //PdfFormXObject pdfForm = xform.PdfForm; pdfForm.Elements.SetMatrix(PdfFormXObject.Keys.Matrix, new XMatrix()); //formWriter.Size = brush.Viewport.Size; writer.BeginContentRaw(); string imageID = writer.Resources.AddImage(new PdfImage(Context.PdfDocument, ximage)); XMatrix matrix = new XMatrix(); //double scaleX = brush.Viewport.Width / brush.Viewbox.Width * 4 / 3 * ximage.PointWidth; //double scaleY = brush.Viewport.Height / brush.Viewbox.Height * 4 / 3 * ximage.PointHeight; //matrix.TranslatePrepend(-brush.Viewbox.X, -brush.Viewbox.Y); //matrix.ScalePrepend(scaleX, scaleY); //matrix.TranslatePrepend(brush.Viewport.X / scaleX, brush.Viewport.Y / scaleY); //double scaleX = 96 / ximage.HorizontalResolution; //double scaleY = 96 / ximage.VerticalResolution; //width *= scaleX; //height *= scaleY; matrix = new XMatrix(width, 0, 0, -height, 0, height); writer.WriteLiteral("q\n"); // TODO:WriteClip(path.Data); //formWriter.WriteLiteral("{0:0.###} 0 0 -{1:0.###} {2:0.###} {3:0.###} cm 100 Tz {4} Do Q\n", // matrix.M11, matrix.M22, matrix.OffsetX, matrix.OffsetY + brush.Viewport.Height, imageID); writer.WriteMatrix(matrix); writer.WriteLiteral(imageID + " Do Q\n"); #if DEBUG if (DevHelper.BorderPatterns) { writer.WriteLiteral("1 1 1 rg 0 0 m {0:0.###} 0 l {0:0.###} {1:0.###} l 0 {1:0.###} l h s\n", width, height); } #endif writer.EndContent(); return(pdfForm); }
/// <summary> /// Builds a PdfFormXObject from the specified brush. /// </summary> PdfFormXObject BuildForm(VisualBrush brush) { //<< // /BBox [0 100 100 0] // /Length 65 // /Matrix [1 0 0 1 0 0] // /Resources // << // /ColorSpace // << // /CS0 15 0 R // >> // /ExtGState // << // /GS0 10 0 R // >> // /ProcSet [/PDF /ImageC /ImageI] // /XObject // << // /Im0 16 0 R // >> // >> // /Subtype /Form //>> //stream // q // 0 0 100 100 re // W n // q // /GS0 gs // 100 0 0 -100 0 100 cm // /Im0 Do // Q //Q //endstream PdfFormXObject pdfForm = Context.PdfDocument.Internals.CreateIndirectObject <PdfFormXObject>(); double width = brush.Viewport.Width; double height = brush.Viewport.Height; pdfForm.DpiX = 96; pdfForm.DpiY = 96; // view box XRect box = new XRect(0, 0, width, height); pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, new PdfRectangle(0, height, width, 0)); pdfForm.Elements.SetMatrix(PdfFormXObject.Keys.Matrix, new XMatrix()); PdfContentWriter writer = new PdfContentWriter(Context, pdfForm); pdfForm.Elements.SetMatrix(PdfFormXObject.Keys.Matrix, new XMatrix()); writer.BeginContentRaw(); writer.WriteLiteral("-100 Tz\n"); XMatrix matrix = new XMatrix(width, 0, 0, -height, 0, height); writer.WriteLiteral("q\n"); //writer.WriteMatrix(matrix); writer.WriteVisual(brush.Visual); writer.WriteLiteral("Q\n"); #if DEBUG if (DevHelper.BorderPatterns) { writer.WriteLiteral("1 1 1 rg 0 0 m {0:0.###} 0 l {0:0.###} {1:0.###} l 0 {1:0.###} l h s\n", width, height); } #endif writer.EndContent(); 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 the soft mask. /// </summary> PdfSoftMask BuildSoftMask(RadialGradientBrush 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> /// Realizes the CTM. /// </summary> public void RealizeCtm() { //if (MustRealizeCtm) if (!UnrealizedCtm.IsIdentity) { Debug.Assert(!UnrealizedCtm.IsIdentity, "mrCtm is unnecessarily set."); const string format = Config.SignificantFigures7; double[] matrix = UnrealizedCtm.GetElements(); // Use up to six decimal digits to prevent round up problems. _renderer.AppendFormatArgs("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm\n", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); RealizedCtm.Prepend(UnrealizedCtm); UnrealizedCtm = new XMatrix(); EffectiveCtm = RealizedCtm; InverseEffectiveCtm = EffectiveCtm; InverseEffectiveCtm.Invert(); } }
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 } }
// -------------------------------------------------------------------------------------------- #region Realizing graphical state /// <summary> /// Initializes the default view transformation, i.e. the transformation from the user page /// space to the PDF page space. /// </summary> void BeginPage() { if (_gfxState.Level == GraphicsStackLevelInitial) { // TODO: Is PageOriging and PageScale (== Viewport) useful? Or just public DefaultViewMatrix (like Presentation Manager has had) // May be a BeginContainer(windows, viewport) is useful for userer that are not familar with maxtrix transformations. // Flip page horizontally and mirror text. // PDF uses a standard right-handed Cartesian coordinate system with the y axis directed up // and the rotation counterclockwise. Windows uses the opposite convertion with y axis // directed down and rotation clockwise. When I started with PDFsharp I flipped pages horizontally // and then mirrored text to compensate the effect that the fipping turns text upside down. // I found this technique during analysis of PDF documents generated with PDFlib. Unfortunately // this technique leads to several problems with programms that compose or view PDF documents // generated with PDFsharp. // In PDFsharp 1.4 I implement a revised technique that does not need text mirroring any more. DefaultViewMatrix = new XMatrix(); if (_gfx.PageDirection == XPageDirection.Downwards) { // Take TrimBox into account. PageHeightPt = Size.Height; XPoint trimOffset = new XPoint(); if (_page != null && _page.TrimMargins.AreSet) { PageHeightPt += _page.TrimMargins.Top.Point + _page.TrimMargins.Bottom.Point; trimOffset = new XPoint(_page.TrimMargins.Left.Point, _page.TrimMargins.Top.Point); } // Scale with page units. switch (_gfx.PageUnit) { case XGraphicsUnit.Point: // Factor is 1. // DefaultViewMatrix.ScalePrepend(XUnit.PointFactor); break; case XGraphicsUnit.Presentation: DefaultViewMatrix.ScalePrepend(XUnit.PresentationFactor); break; case XGraphicsUnit.Inch: DefaultViewMatrix.ScalePrepend(XUnit.InchFactor); break; case XGraphicsUnit.Millimeter: DefaultViewMatrix.ScalePrepend(XUnit.MillimeterFactor); break; case XGraphicsUnit.Centimeter: DefaultViewMatrix.ScalePrepend(XUnit.CentimeterFactor); break; } if (trimOffset != new XPoint()) { Debug.Assert(_gfx.PageUnit == XGraphicsUnit.Point, "With TrimMargins set the page units must be Point. Ohter cases nyi."); DefaultViewMatrix.TranslatePrepend(trimOffset.X, -trimOffset.Y); } // Save initial graphic state. SaveState(); // Set default page transformation, if any. if (!DefaultViewMatrix.IsIdentity) { Debug.Assert(_gfxState.RealizedCtm.IsIdentity); //_gfxState.RealizedCtm = DefaultViewMatrix; const string format = Config.SignificantFigures7; double[] cm = DefaultViewMatrix.GetElements(); AppendFormatArgs("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); } // Set page transformation //double[] cm = DefaultViewMatrix.GetElements(); //AppendFormat("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", // cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); } else { // Scale with page units. switch (_gfx.PageUnit) { case XGraphicsUnit.Point: // Factor is 1. // DefaultViewMatrix.ScalePrepend(XUnit.PointFactor); break; case XGraphicsUnit.Presentation: DefaultViewMatrix.ScalePrepend(XUnit.PresentationFactor); break; case XGraphicsUnit.Inch: DefaultViewMatrix.ScalePrepend(XUnit.InchFactor); break; case XGraphicsUnit.Millimeter: DefaultViewMatrix.ScalePrepend(XUnit.MillimeterFactor); break; case XGraphicsUnit.Centimeter: DefaultViewMatrix.ScalePrepend(XUnit.CentimeterFactor); break; } // Save initial graphic state. SaveState(); // Set page transformation. const string format = Config.SignificantFigures7; double[] cm = DefaultViewMatrix.GetElements(); AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); } } }
/// <summary> /// Appends a Bézier curve for an arc within a full quadrant. /// </summary> static void AppendPartialArcQuadrant(List <XPoint> points, double x, double y, double width, double height, double α, double β, PathStart pathStart, XMatrix matrix) { Debug.Assert(α >= 0 && α <= 360); Debug.Assert(β >= 0); if (β > 360) { β = β - Math.Floor(β / 360) * 360; } Debug.Assert(Math.Abs(α - β) <= 90); // Scanling factor double δx = width / 2; double δy = height / 2; // Center of ellipse double x0 = x + δx; double y0 = y + δy; // We have the following quarters: // | // 2 | 3 // ----+----- // 1 | 0 // | // If the angles lie in quarter 2 or 3, their values are subtracted by 180 and the // resulting curve is reflected at the center. This algorithm works as expected (simply tried out). // There may be a mathematically more elegant solution... bool reflect = false; if (α >= 180 && β >= 180) { α -= 180; β -= 180; reflect = true; } double cosα, cosβ, sinα, sinβ; if (width == height) { // Circular arc needs no correction. α = α * Calc.Deg2Rad; β = β * Calc.Deg2Rad; } else { // Elliptic arc needs the angles to be adjusted such that the scaling transformation is compensated. α = α * Calc.Deg2Rad; sinα = Math.Sin(α); if (Math.Abs(sinα) > 1E-10) { α = Calc.πHalf - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); } β = β * Calc.Deg2Rad; sinβ = Math.Sin(β); if (Math.Abs(sinβ) > 1E-10) { β = Calc.πHalf - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); } } double κ = 4 * (1 - Math.Cos((α - β) / 2)) / (3 * Math.Sin((β - α) / 2)); sinα = Math.Sin(α); cosα = Math.Cos(α); sinβ = Math.Sin(β); cosβ = Math.Cos(β); //XPoint pt1, pt2, pt3; if (!reflect) { // Calculation for quarter 0 and 1 switch (pathStart) { case PathStart.MoveTo1st: points.Add(matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα))); break; case PathStart.LineTo1st: points.Add(matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα))); break; case PathStart.Ignore1st: break; } points.Add(matrix.Transform(new XPoint(x0 + δx * (cosα - κ * sinα), y0 + δy * (sinα + κ * cosα)))); points.Add(matrix.Transform(new XPoint(x0 + δx * (cosβ + κ * sinβ), y0 + δy * (sinβ - κ * cosβ)))); points.Add(matrix.Transform(new XPoint(x0 + δx * cosβ, y0 + δy * sinβ))); } else { // Calculation for quarter 2 and 3 switch (pathStart) { case PathStart.MoveTo1st: points.Add(matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα))); break; case PathStart.LineTo1st: points.Add(matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα))); break; case PathStart.Ignore1st: break; } points.Add(matrix.Transform(new XPoint(x0 - δx * (cosα - κ * sinα), y0 - δy * (sinα + κ * cosα)))); points.Add(matrix.Transform(new XPoint(x0 - δx * (cosβ + κ * sinβ), y0 - δy * (sinβ - κ * cosβ)))); points.Add(matrix.Transform(new XPoint(x0 - δx * cosβ, y0 - δy * sinβ))); } }
/// <summary> /// Determines whether this matrix is equal to the specified matrix. /// </summary> public bool Equals(XMatrix value) { return Equals(this, value); }
/// <summary> /// Creates between 1 and 5 Béziers curves from parameters specified like in WPF. /// </summary> public static List <XPoint> BezierCurveFromArc(XPoint point1, XPoint point2, double rotationAngle, XSize size, bool isLargeArc, bool clockwise, PathStart pathStart) { #if DEBUG_ if (size == new XSize(115, 115)) { Debugger.Break(); } #endif // See also http://www.charlespetzold.com/blog/blog.xml from January 2, 2008 double δx = size.Width; double δy = size.Height; Debug.Assert(δx * δy > 0); double factor = δy / δx; bool isCounterclockwise = !clockwise; // Adjust for different radii and rotation angle XMatrix matrix = new XMatrix(); matrix.RotateAppend(-rotationAngle); matrix.ScaleAppend(δy / δx, 1); XPoint pt1 = matrix.Transform(point1); XPoint pt2 = matrix.Transform(point2); // Get info about chord that connects both points XPoint midPoint = new XPoint((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2); XVector vect = pt2 - pt1; double halfChord = vect.Length / 2; // Get vector from chord to center XVector vectRotated; // (comparing two Booleans here!) if (isLargeArc == isCounterclockwise) { vectRotated = new XVector(-vect.Y, vect.X); } else { vectRotated = new XVector(vect.Y, -vect.X); } vectRotated.Normalize(); // Distance from chord to center double centerDistance = Math.Sqrt(δy * δy - halfChord * halfChord); if (double.IsNaN(centerDistance)) { centerDistance = 0; } // Calculate center point XPoint center = midPoint + centerDistance * vectRotated; // Get angles from center to the two points double α = Math.Atan2(pt1.Y - center.Y, pt1.X - center.X); double β = Math.Atan2(pt2.Y - center.Y, pt2.X - center.X); // (another comparison of two Booleans!) if (isLargeArc == (Math.Abs(β - α) < Math.PI)) { if (α < β) { α += 2 * Math.PI; } else { β += 2 * Math.PI; } } // Invert matrix for final point calculation matrix.Invert(); double sweepAngle = β - α; // Let the algorithm of GDI+ DrawArc to Bézier curves do the rest of the job return(BezierCurveFromArc(center.X - δx * factor, center.Y - δy, 2 * δx * factor, 2 * δy, α / Calc.Deg2Rad, sweepAngle / Calc.Deg2Rad, pathStart, ref matrix)); }
internal static XMatrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY) { XMatrix matrix = new XMatrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, centerX - scaleX * centerX, centerY - scaleY * centerY, XMatrixTypes.Scaling | XMatrixTypes.Translation); return matrix; }
//+------------------------------------------------------------------------------------------------- // // Function: ArcToBezier // // Synopsis: Compute the Bezier approximation of an arc // // Notes: This utilitycomputes the Bezier approximation for an elliptical arc as it is defined // in the SVG arc spec. The ellipse from which the arc is carved is axis-aligned in its // own coordinates, and defined there by its x and y radii. The rotation angle defines // how the ellipse's axes are rotated relative to our x axis. The start and end points // define one of 4 possible arcs; the sweep and large-arc flags determine which one of // these arcs will be chosen. See SVG spec for details. // // Returning pieces = 0 indicates a line instead of an arc // pieces = -1 indicates that the arc degenerates to a point // //-------------------------------------------------------------------------------------------------- public static PointCollection ArcToBezier(double xStart, double yStart, double xRadius, double yRadius, double rotationAngle, bool isLargeArc, bool isClockwise, double xEnd, double yEnd, out int pieces) { double cosArcAngle, sinArcAngle, xCenter, yCenter, r, bezDist; XVector vecToBez1, vecToBez2; XMatrix matToEllipse; double fuzz2 = FUZZ * FUZZ; bool isZeroCenter = false; pieces = -1; // In the following, the line segment between between the arc's start and // end points is referred to as "the chord". // Transform 1: Shift the origin to the chord's midpoint double x = (xEnd - xStart) / 2; double y = (yEnd - yStart) / 2; double halfChord2 = x * x + y * y; // (half chord length)^2 // Degenerate case: single point if (halfChord2 < fuzz2) { // The chord degeneartes to a point, the arc will be ignored return(null); } // Degenerate case: straight line if (!AcceptRadius(halfChord2, fuzz2, ref xRadius) || !AcceptRadius(halfChord2, fuzz2, ref yRadius)) { // We have a zero radius, add a straight line segment instead of an arc pieces = 0; return(null); } if (xRadius == 0 || yRadius == 0) { // We have a zero radius, add a straight line segment instead of an arc pieces = 0; return(null); } // Transform 2: Rotate to the ellipse's coordinate system rotationAngle = -rotationAngle * Calc.Deg2Rad; double cos = Math.Cos(rotationAngle); double sin = Math.Sin(rotationAngle); r = x * cos - y * sin; y = x * sin + y * cos; x = r; // Transform 3: Scale so that the ellipse will become a unit circle x /= xRadius; y /= yRadius; // We get to the center of that circle along a verctor perpendicular to the chord // from the origin, which is the chord's midpoint. By Pythagoras, the length of that // vector is sqrt(1 - (half chord)^2). halfChord2 = x * x + y * y; // now in the circle coordinates if (halfChord2 > 1) { // The chord is longer than the circle's diameter; we scale the radii uniformly so // that the chord will be a diameter. The center will then be the chord's midpoint, // which is now the origin. r = Math.Sqrt(halfChord2); xRadius *= r; yRadius *= r; xCenter = yCenter = 0; isZeroCenter = true; // Adjust the unit-circle coordinates x and y x /= r; y /= r; } else { // The length of (-y,x) or (x,-y) is sqrt(rHalfChord2), and we want a vector // of length sqrt(1 - rHalfChord2), so we'll multiply it by: r = Math.Sqrt((1 - halfChord2) / halfChord2); //if (isLargeArc != (eSweepDirection == SweepDirection.Clockwise)) if (isLargeArc != isClockwise) // Going to the center from the origin=chord-midpoint { // in the direction of (-y, x) xCenter = -r * y; yCenter = r * x; } else { // in the direction of (y, -x) xCenter = r * y; yCenter = -r * x; } } // Transformation 4: shift the origin to the center of the circle, which then becomes // the unit circle. Since the chord's midpoint is the origin, the start point is (-x, -y) // and the endpoint is (x, y). XPoint ptStart = new XPoint(-x - xCenter, -y - yCenter); XPoint ptEnd = new XPoint(x - xCenter, y - yCenter); // Set up the matrix that will take us back to our coordinate system. This matrix is // the inverse of the combination of transformation 1 thru 4. matToEllipse = new XMatrix(cos * xRadius, -sin * xRadius, sin * yRadius, cos * yRadius, (xEnd + xStart) / 2, (yEnd + yStart) / 2); if (!isZeroCenter) { // Prepend the translation that will take the origin to the circle's center matToEllipse.OffsetX += (matToEllipse.M11 * xCenter + matToEllipse.M21 * yCenter); matToEllipse.OffsetY += (matToEllipse.M12 * xCenter + matToEllipse.M22 * yCenter); } // Get the sine & cosine of the angle that will generate the arc pieces GetArcAngle(ptStart, ptEnd, isLargeArc, isClockwise, out cosArcAngle, out sinArcAngle, out pieces); // Get the vector to the first Bezier control point bezDist = GetBezierDistance(cosArcAngle, 1); //if (eSweepDirection == SweepDirection.Counterclockwise) if (!isClockwise) { bezDist = -bezDist; } vecToBez1 = new XVector(-bezDist * ptStart.Y, bezDist * ptStart.X); PointCollection result = new PointCollection(); // Add the arc pieces, except for the last for (int idx = 1; idx < pieces; idx++) { // Get the arc piece's endpoint XPoint ptPieceEnd = new XPoint(ptStart.X * cosArcAngle - ptStart.Y * sinArcAngle, ptStart.X * sinArcAngle + ptStart.Y * cosArcAngle); vecToBez2 = new XVector(-bezDist * ptPieceEnd.Y, bezDist * ptPieceEnd.X); result.Add(matToEllipse.Transform(ptStart + vecToBez1)); result.Add(matToEllipse.Transform(ptPieceEnd - vecToBez2)); result.Add(matToEllipse.Transform(ptPieceEnd)); // Move on to the next arc ptStart = ptPieceEnd; vecToBez1 = vecToBez2; } // Last arc - we know the endpoint vecToBez2 = new XVector(-bezDist * ptEnd.Y, bezDist * ptEnd.X); result.Add(matToEllipse.Transform(ptStart + vecToBez1)); result.Add(matToEllipse.Transform(ptEnd - vecToBez2)); result.Add(new XPoint(xEnd, yEnd)); return(result); }
// Fast mutiplication taking matrix type into account. Reflectored from WPF. internal static void MultiplyMatrix(ref XMatrix matrix1, ref XMatrix matrix2) { XMatrixTypes type1 = matrix1._type; XMatrixTypes type2 = matrix2._type; if (type2 != XMatrixTypes.Identity) { if (type1 == XMatrixTypes.Identity) matrix1 = matrix2; else if (type2 == XMatrixTypes.Translation) { matrix1._offsetX += matrix2._offsetX; matrix1._offsetY += matrix2._offsetY; if (type1 != XMatrixTypes.Unknown) matrix1._type |= XMatrixTypes.Translation; } else if (type1 == XMatrixTypes.Translation) { double num = matrix1._offsetX; double num2 = matrix1._offsetY; matrix1 = matrix2; matrix1._offsetX = num * matrix2._m11 + num2 * matrix2._m21 + matrix2._offsetX; matrix1._offsetY = num * matrix2._m12 + num2 * matrix2._m22 + matrix2._offsetY; if (type2 == XMatrixTypes.Unknown) matrix1._type = XMatrixTypes.Unknown; else matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation; } else { switch ((((int)type1) << 4) | (int)type2) { case 0x22: matrix1._m11 *= matrix2._m11; matrix1._m22 *= matrix2._m22; return; case 0x23: matrix1._m11 *= matrix2._m11; matrix1._m22 *= matrix2._m22; matrix1._offsetX = matrix2._offsetX; matrix1._offsetY = matrix2._offsetY; matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation; return; case 0x24: case 0x34: case 0x42: case 0x43: case 0x44: matrix1 = new XMatrix( matrix1._m11 * matrix2._m11 + matrix1._m12 * matrix2._m21, matrix1._m11 * matrix2._m12 + matrix1._m12 * matrix2._m22, matrix1._m21 * matrix2._m11 + matrix1._m22 * matrix2._m21, matrix1._m21 * matrix2._m12 + matrix1._m22 * matrix2._m22, matrix1._offsetX * matrix2._m11 + matrix1._offsetY * matrix2._m21 + matrix2._offsetX, matrix1._offsetX * matrix2._m12 + matrix1._offsetY * matrix2._m22 + matrix2._offsetY); return; case 50: matrix1._m11 *= matrix2._m11; matrix1._m22 *= matrix2._m22; matrix1._offsetX *= matrix2._m11; matrix1._offsetY *= matrix2._m22; return; case 0x33: matrix1._m11 *= matrix2._m11; matrix1._m22 *= matrix2._m22; matrix1._offsetX = matrix2._m11 * matrix1._offsetX + matrix2._offsetX; matrix1._offsetY = matrix2._m22 * matrix1._offsetY + matrix2._offsetY; return; } } } }
/// <summary> /// Multiply the brush transformation matrix with the specified matrix. /// </summary> public void MultiplyTransform(XMatrix matrix) { _matrix.Prepend(matrix); }
/// <summary> /// Appends the specified matrix to this matrix. /// </summary> public void Append(XMatrix matrix) { this *= matrix; }
/// <summary> /// Multiply the brush transformation matrix with the specified matrix. /// </summary> public void MultiplyTransform(XMatrix matrix, XMatrixOrder order) { _matrix.Multiply(matrix, order); }
/// <summary> /// Multiplies this matrix with the specified matrix. /// </summary> public void Multiply(XMatrix matrix, XMatrixOrder order) { if (_type == XMatrixTypes.Identity) this = CreateIdentity(); // Must use properties, the fields can be invalid if the matrix is identity matrix. double t11 = M11; double t12 = M12; double t21 = M21; double t22 = M22; double tdx = OffsetX; double tdy = OffsetY; if (order == XMatrixOrder.Append) { _m11 = t11 * matrix.M11 + t12 * matrix.M21; _m12 = t11 * matrix.M12 + t12 * matrix.M22; _m21 = t21 * matrix.M11 + t22 * matrix.M21; _m22 = t21 * matrix.M12 + t22 * matrix.M22; _offsetX = tdx * matrix.M11 + tdy * matrix.M21 + matrix.OffsetX; _offsetY = tdx * matrix.M12 + tdy * matrix.M22 + matrix.OffsetY; } else { _m11 = t11 * matrix.M11 + t21 * matrix.M12; _m12 = t12 * matrix.M11 + t22 * matrix.M12; _m21 = t11 * matrix.M21 + t21 * matrix.M22; _m22 = t12 * matrix.M21 + t22 * matrix.M22; _offsetX = t11 * matrix.OffsetX + t21 * matrix.OffsetY + tdx; _offsetY = t12 * matrix.OffsetX + t22 * matrix.OffsetY + tdy; } DeriveMatrixType(); }
/// <summary> /// Resets the brush transformation matrix with identity matrix. /// </summary> public void ResetTransform() { _matrix = new XMatrix(); }
/// <summary> /// Modifies the current transformation matrix. /// </summary> public void MultiplyTransform(XMatrix matrix, XMatrixOrder order) { if (!matrix.IsIdentity) { this.MustRealizeCtm = true; this.unrealizedCtm.Multiply(matrix, order); } }
/// <summary> /// Draws the vertical Y axis. /// </summary> internal override void Draw() { AxisRendererInfo yari = ((ChartRendererInfo)this.rendererParms.RendererInfo).yAxisRendererInfo; double yMin = yari.MinimumScale; double yMax = yari.MaximumScale; double yMajorTick = yari.MajorTick; double yMinorTick = yari.MinorTick; XMatrix matrix = new XMatrix(); //XMatrix.Identity; matrix.TranslatePrepend(-yMin, -yari.Y); matrix.Scale(yari.InnerRect.Width / (yMax - yMin), 1, XMatrixOrder.Append); matrix.Translate(yari.X, yari.Y, XMatrixOrder.Append); // Draw axis. // First draw tick marks, second draw axis. double majorTickMarkStart = 0, majorTickMarkEnd = 0, minorTickMarkStart = 0, minorTickMarkEnd = 0; GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); XGraphics gfx = this.rendererParms.Graphics; LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); XPoint[] points = new XPoint[2]; if (yari.MinorTickMark != TickMarkType.None) { for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) { points[0].X = y; points[0].Y = minorTickMarkStart; points[1].X = y; points[1].Y = minorTickMarkEnd; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } XStringFormat xsf = new XStringFormat(); xsf.LineAlignment = XLineAlignment.Near; int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; for (int i = 0; i < countTickLabels; ++i) { double y = yMin + yMajorTick * i; string str = y.ToString(yari.TickLabelsFormat); XSize labelSize = gfx.MeasureString(str, yari.TickLabelsFont); if (yari.MajorTickMark != TickMarkType.None) { labelSize.Height += 1.5f * yari.MajorTickMarkWidth; points[0].X = y; points[0].Y = majorTickMarkStart; points[1].X = y; points[1].Y = majorTickMarkEnd; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } XPoint[] layoutText = new XPoint[1]; layoutText[0].X = y; layoutText[0].Y = yari.Y + 1.5 * yari.MajorTickMarkWidth; matrix.TransformPoints(layoutText); layoutText[0].X -= labelSize.Width / 2; // Center text vertically. gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0], xsf); } if (yari.LineFormat != null) { points[0].X = yMin; points[0].Y = yari.Y; points[1].X = yMax; points[1].Y = yari.Y; matrix.TransformPoints(points); if (yari.MajorTickMark != TickMarkType.None) { // yMax is at the upper side of the axis points[0].X -= yari.LineFormat.Width / 2; points[1].X += yari.LineFormat.Width / 2; } lineFormatRenderer.DrawLine(points[0], points[1]); } // Draw axis title if (yari.axisTitleRendererInfo != null) { RendererParameters parms = new RendererParameters(); parms.Graphics = gfx; parms.RendererInfo = yari; XRect rcTitle = yari.Rect; rcTitle.Height = yari.axisTitleRendererInfo.Height; rcTitle.Y += yari.Rect.Height - rcTitle.Height; yari.axisTitleRendererInfo.Rect = rcTitle; AxisTitleRenderer atr = new AxisTitleRenderer(parms); atr.Draw(); } }
///// <summary> ///// The world transform in PDF world space. ///// </summary> //public XMatrix EffectiveCtm //{ // get // { // //if (MustRealizeCtm) // if (!UnrealizedCtm.IsIdentity) // { // XMatrix matrix = RealizedCtm; // matrix.Prepend(UnrealizedCtm); // return matrix; // } // return RealizedCtm; // } // //set // //{ // // XMatrix matrix = realizedCtm; // // matrix.Invert(); // // matrix.Prepend(value); // // unrealizedCtm = matrix; // // MustRealizeCtm = !unrealizedCtm.IsIdentity; // //} //} public void AddTransform(XMatrix value, XMatrixOrder matrixOrder) { // TODO: User matrixOrder #if DEBUG if (matrixOrder == XMatrixOrder.Append) throw new NotImplementedException("XMatrixOrder.Append"); #endif XMatrix transform = value; if (_renderer.Gfx.PageDirection == XPageDirection.Downwards) { // Take chirality into account and // invert the direction of rotation. transform.M12 = -value.M12; transform.M21 = -value.M21; } UnrealizedCtm.Prepend(transform); WorldTransform.Prepend(value); }
/// <summary> /// Writes a Glyphs to the content stream. /// </summary> private void WriteGlyphs(Glyphs glyphs) { WriteSaveState("begin Glyphs", glyphs.Name); // Transform also affects clipping and opacity mask bool transformed = glyphs.RenderTransform != null; if (transformed) { WriteRenderTransform(glyphs.RenderTransform); } bool clipped = glyphs.Clip != null; if (clipped) { WriteClip(glyphs.Clip); } if (glyphs.Opacity < 1) { MultiplyOpacity(glyphs.Opacity); } if (glyphs.OpacityMask != null) { WriteOpacityMask(glyphs.OpacityMask); } XMatrix textMatrix = new XMatrix(); textMatrix.TranslatePrepend(glyphs.OriginX, glyphs.OriginY); glyphs.OriginX = glyphs.OriginY = 0; // HACK: do not change model double emSize = glyphs.FontRenderingEmSize; textMatrix.ScalePrepend(glyphs.FontRenderingEmSize); glyphs.FontRenderingEmSize = 1; // HACK: do not change model bool boldSimulation = (glyphs.StyleSimulations & StyleSimulations.BoldSimulation) == StyleSimulations.BoldSimulation; // just a draft... if (boldSimulation) { boldSimulation = true; // draw black stroke if it is not a solid color brush XColor color = XColor.FromArgb(0, 0, 0); if (glyphs.Fill is SolidColorBrush) { SolidColorBrush brush = glyphs.Fill as SolidColorBrush; color = brush.Color; } WriteLiteral(String.Format(CultureInfo.InvariantCulture, "{0:0.###} {1:0.###} {2:0.###} RG\n", color.R / 255.0, color.G / 255.0, color.B / 255.0)); WriteLiteral("{0:0.###} w\n", emSize / 50); } if ((glyphs.StyleSimulations & StyleSimulations.ItalicSimulation) == StyleSimulations.ItalicSimulation) { textMatrix.SkewPrepend(-20, 0); } XForm xform = null; XImage ximage = null; RealizeFill(glyphs.Fill, ref xform, ref ximage); RealizeFont(glyphs); if (boldSimulation) { WriteLiteral("2 Tr\n", 1); } double x = glyphs.OriginX; double y = glyphs.OriginY; //switch (format.Alignment) //{ // case XStringAlignment.Near: // // nothing to do // break; // case XStringAlignment.Center: // x += (rect.Width - width) / 2; // break; // case XStringAlignment.Far: // x += rect.Width - width; // break; //} PdfFont realizedFont = this.graphicsState.realizedFont; Debug.Assert(realizedFont != null); realizedFont.AddChars(glyphs.UnicodeString); OpenTypeDescriptor descriptor = realizedFont.FontDescriptor.descriptor; //if (bold && !descriptor.IsBoldFace) //{ // // TODO: emulate bold by thicker outline //} //if (italic && !descriptor.IsBoldFace) //{ // // TODO: emulate italic by shearing transformation //} #if true string s2 = ""; string s = glyphs.UnicodeString; if (!String.IsNullOrEmpty(s)) { int length = s.Length; for (int idx = 0; idx < length; idx++) { char ch = s[idx]; int glyphID = 0; if (descriptor.fontData.cmap.symbol) { glyphID = (int)ch + (descriptor.fontData.os2.usFirstCharIndex & 0xFF00); glyphID = descriptor.CharCodeToGlyphIndex((char)glyphID); } else { glyphID = descriptor.CharCodeToGlyphIndex(ch); } s2 += (char)glyphID; } } s = s2; #endif byte[] bytes = PdfEncoders.RawUnicodeEncoding.GetBytes(s); bytes = PdfEncoders.FormatStringLiteral(bytes, true, false, true, null); string text = PdfEncoders.RawEncoding.GetString(bytes); if (glyphs.IsSideways) { textMatrix.RotateAtPrepend(-90, new XPoint(x, y)); XPoint pos = new XPoint(x, y); AdjustTextMatrix(ref pos); //WriteTextTransform(textMatrix); WriteLiteral("{0} Tj\n", text); } else { #if true //if (glyphs.BidiLevel % 2 == 1) // WriteLiteral("-1 Tc\n"); if (!textMatrix.IsIdentity) { WriteTextTransform(textMatrix); } WriteGlyphsInternal(glyphs, null); #else XPoint pos = new XPoint(x, y); AdjustTextMatrix(ref pos); WriteLiteral("{0:0.###} {1:0.###} Td {2} Tj\n", pos.x, pos.y, text); //PdfEncoders.ToStringLiteral(s, PdfStringEncoding.RawEncoding, null)); #endif } WriteRestoreState("end Glyphs", glyphs.Name); }
public static XVector Multiply(XVector vector, XMatrix matrix) { return matrix.Transform(vector); }
/// <summary> /// Creates a literal from an XMatrix /// </summary> public static PdfLiteral FromMatrix(XMatrix matrix) { return(new PdfLiteral("[" + PdfEncoders.ToString(matrix) + "]")); }
// ----- DrawString --------------------------------------------------------------------------- public void DrawString(string s, XFont font, XBrush brush, XRect rect, XStringFormat format) { double x = rect.X; double y = rect.Y; double lineSpace = font.GetHeight(); double cyAscent = lineSpace * font.CellAscent / font.CellSpace; double cyDescent = lineSpace * font.CellDescent / font.CellSpace; double width = _gfx.MeasureString(s, font).Width; //bool bold = (font.Style & XFontStyle.Bold) != 0; //bool italic = (font.Style & XFontStyle.Italic) != 0; bool italicSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.ItalicSimulation) != 0; bool boldSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.BoldSimulation) != 0; bool strikeout = (font.Style & XFontStyle.Strikeout) != 0; bool underline = (font.Style & XFontStyle.Underline) != 0; Realize(font, brush, boldSimulation ? 2 : 0); switch (format.Alignment) { case XStringAlignment.Near: // nothing to do break; case XStringAlignment.Center: x += (rect.Width - width) / 2; break; case XStringAlignment.Far: x += rect.Width - width; break; } if (Gfx.PageDirection == XPageDirection.Downwards) { switch (format.LineAlignment) { case XLineAlignment.Near: y += cyAscent; break; case XLineAlignment.Center: // TODO: Use CapHeight. PDFlib also uses 3/4 of ascent y += (cyAscent * 3 / 4) / 2 + rect.Height / 2; break; case XLineAlignment.Far: y += -cyDescent + rect.Height; break; case XLineAlignment.BaseLine: // Nothing to do. break; } } else { switch (format.LineAlignment) { case XLineAlignment.Near: y += cyDescent; break; case XLineAlignment.Center: // TODO: Use CapHeight. PDFlib also uses 3/4 of ascent y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2; break; case XLineAlignment.Far: y += -cyAscent + rect.Height; break; case XLineAlignment.BaseLine: // Nothing to do. break; } } PdfFont realizedFont = _gfxState._realizedFont; Debug.Assert(realizedFont != null); realizedFont.AddChars(s); const string format2 = Config.SignificantFigures4; OpenTypeDescriptor descriptor = realizedFont.FontDescriptor._descriptor; string text = null; if (font.Unicode) { StringBuilder sb = new StringBuilder(); bool isSymbolFont = descriptor.FontFace.cmap.symbol; for (int idx = 0; idx < s.Length; idx++) { char ch = s[idx]; if (isSymbolFont) { // Remap ch for symbol fonts. ch = (char)(ch | (descriptor.FontFace.os2.usFirstCharIndex & 0xFF00)); // @@@ refactor } int glyphID = descriptor.CharCodeToGlyphIndex(ch); sb.Append((char)glyphID); } s = sb.ToString(); byte[] bytes = PdfEncoders.RawUnicodeEncoding.GetBytes(s); bytes = PdfEncoders.FormatStringLiteral(bytes, true, false, true, null); text = PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); } else { byte[] bytes = PdfEncoders.WinAnsiEncoding.GetBytes(s); text = PdfEncoders.ToStringLiteral(bytes, false, null); } // Map absolute position to PDF world space. XPoint pos = new XPoint(x, y); pos = WorldToView(pos); double verticalOffset = 0; if (boldSimulation) { // Adjust baseline in case of bold simulation??? // No, because this would change the center of the glyphs. //verticalOffset = font.Size * Const.BoldEmphasis / 2; } #if ITALIC_SIMULATION if (italicSimulation) { if (_gfxState.ItalicSimulationOn) { AdjustTdOffset(ref pos, verticalOffset, true); AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} Td\n{2} Tj\n", pos.X, pos.Y, text); } else { // Italic simulation is done by skewing characters 20° to the right. XMatrix m = new XMatrix(1, 0, Const.ItalicSkewAngleSinus, 1, pos.X, pos.Y); AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} {2:" + format2 + "} {3:" + format2 + "} {4:" + format2 + "} {5:" + format2 + "} Tm\n{6} Tj\n", m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY, text); _gfxState.ItalicSimulationOn = true; AdjustTdOffset(ref pos, verticalOffset, false); } } else { if (_gfxState.ItalicSimulationOn) { XMatrix m = new XMatrix(1, 0, 0, 1, pos.X, pos.Y); AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} {2:" + format2 + "} {3:" + format2 + "} {4:" + format2 + "} {5:" + format2 + "} Tm\n{6} Tj\n", m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY, text); _gfxState.ItalicSimulationOn = false; AdjustTdOffset(ref pos, verticalOffset, false); } else { AdjustTdOffset(ref pos, verticalOffset, false); AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} Td {2} Tj\n", pos.X, pos.Y, text); } } #else AdjustTextMatrix(ref pos); AppendFormat2("{0:" + format2 + "} {1:" + format2 + "} Td {2} Tj\n", pos.X, pos.Y, text); #endif if (underline) { double underlinePosition = lineSpace * realizedFont.FontDescriptor._descriptor.UnderlinePosition / font.CellSpace; double underlineThickness = lineSpace * realizedFont.FontDescriptor._descriptor.UnderlineThickness / font.CellSpace; //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness); double underlineRectY = Gfx.PageDirection == XPageDirection.Downwards ? y - underlinePosition : y + underlinePosition - underlineThickness; DrawRectangle(null, brush, x, underlineRectY, width, underlineThickness); } if (strikeout) { double strikeoutPosition = lineSpace * realizedFont.FontDescriptor._descriptor.StrikeoutPosition / font.CellSpace; double strikeoutSize = lineSpace * realizedFont.FontDescriptor._descriptor.StrikeoutSize / font.CellSpace; //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); double strikeoutRectY = Gfx.PageDirection == XPageDirection.Downwards ? y - strikeoutPosition : y + strikeoutPosition - strikeoutSize; DrawRectangle(null, brush, x, strikeoutRectY, width, strikeoutSize); } }
public static XVector Multiply(XVector vector, XMatrix matrix) { return(matrix.Transform(vector)); }
/// <summary> /// Determines whether the two matrices are equal. /// </summary> public static bool Equals(XMatrix matrix1, XMatrix matrix2) { if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity) return matrix1.IsIdentity == matrix2.IsIdentity; return matrix1.M11.Equals(matrix2.M11) && matrix1.M12.Equals(matrix2.M12) && matrix1.M21.Equals(matrix2.M21) && matrix1.M22.Equals(matrix2.M22) && matrix1.OffsetX.Equals(matrix2.OffsetX) && matrix1.OffsetY.Equals(matrix2.OffsetY); }
/// <summary> /// Draws the vertical Y axis. /// </summary> internal override void Draw() { AxisRendererInfo yari = ((ChartRendererInfo)_rendererParms.RendererInfo).yAxisRendererInfo; double yMin = yari.MinimumScale; double yMax = yari.MaximumScale; double yMajorTick = yari.MajorTick; double yMinorTick = yari.MinorTick; XMatrix matrix = new XMatrix(); matrix.TranslatePrepend(-yari.InnerRect.X, yMax); matrix.Scale(1, yari.InnerRect.Height / (yMax - yMin), XMatrixOrder.Append); matrix.ScalePrepend(1, -1); // mirror horizontal matrix.Translate(yari.InnerRect.X, yari.InnerRect.Y, XMatrixOrder.Append); // Draw axis. // First draw tick marks, second draw axis. double majorTickMarkStart = 0, majorTickMarkEnd = 0, minorTickMarkStart = 0, minorTickMarkEnd = 0; GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); XGraphics gfx = _rendererParms.Graphics; LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); LineFormatRenderer minorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MinorTickMarkLineFormat); LineFormatRenderer majorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MajorTickMarkLineFormat); XPoint[] points = new XPoint[2]; // Draw minor tick marks. if (yari.MinorTickMark != TickMarkType.None) { for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) { points[0].X = minorTickMarkStart; points[0].Y = y; points[1].X = minorTickMarkEnd; points[1].Y = y; matrix.TransformPoints(points); minorTickMarkLineFormat.DrawLine(points[0], points[1]); } } double lineSpace = yari.TickLabelsFont.GetHeight(); // old: yari.TickLabelsFont.GetHeight(gfx); int cellSpace = yari.TickLabelsFont.FontFamily.GetLineSpacing(yari.TickLabelsFont.Style); double xHeight = yari.TickLabelsFont.Metrics.XHeight; XSize labelSize = new XSize(0, 0); labelSize.Height = lineSpace * xHeight / cellSpace; int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; for (int i = 0; i < countTickLabels; ++i) { double y = yMin + yMajorTick * i; string str = y.ToString(yari.TickLabelsFormat); labelSize.Width = gfx.MeasureString(str, yari.TickLabelsFont).Width; // Draw major tick marks. if (yari.MajorTickMark != TickMarkType.None) { labelSize.Width += yari.MajorTickMarkWidth * 1.5; points[0].X = majorTickMarkStart; points[0].Y = y; points[1].X = majorTickMarkEnd; points[1].Y = y; matrix.TransformPoints(points); majorTickMarkLineFormat.DrawLine(points[0], points[1]); } else { labelSize.Width += SpaceBetweenLabelAndTickmark; } // Draw label text. XPoint[] layoutText = new XPoint[1]; layoutText[0].X = yari.InnerRect.X + yari.InnerRect.Width - labelSize.Width; layoutText[0].Y = y; matrix.TransformPoints(layoutText); layoutText[0].Y += labelSize.Height / 2; // Center text vertically. gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0]); } // Draw axis. if (yari.LineFormat != null && yari.LineFormat.Width > 0) { points[0].X = yari.InnerRect.X + yari.InnerRect.Width; points[0].Y = yMin; points[1].X = yari.InnerRect.X + yari.InnerRect.Width; points[1].Y = yMax; matrix.TransformPoints(points); if (yari.MajorTickMark != TickMarkType.None) { // yMax is at the upper side of the axis points[1].Y -= yari.LineFormat.Width / 2; points[0].Y += yari.LineFormat.Width / 2; } lineFormatRenderer.DrawLine(points[0], points[1]); } // Draw axis title if (yari._axisTitleRendererInfo != null && yari._axisTitleRendererInfo.AxisTitleText != "") { RendererParameters parms = new RendererParameters(); parms.Graphics = gfx; parms.RendererInfo = yari; double width = yari._axisTitleRendererInfo.Width; yari._axisTitleRendererInfo.Rect = yari.InnerRect; yari._axisTitleRendererInfo.Width = width; AxisTitleRenderer atr = new AxisTitleRenderer(parms); atr.Draw(); } }
internal static XMatrix CreateTranslation(double offsetX, double offsetY) { XMatrix matrix = new XMatrix(); matrix.SetMatrix(1, 0, 0, 1, offsetX, offsetY, XMatrixTypes.Translation); return matrix; }
public AbstractMatrix(float m11, float m12, float m21, float m22, float dx, float dy) { matrix = new XMatrix(m11, m12, m21, m22, dx, dy); }
internal static XMatrix CreateScaling(double scaleX, double scaleY) { XMatrix matrix = new XMatrix(); matrix.SetMatrix(scaleX, 0, 0, scaleY, 0, 0, XMatrixTypes.Scaling); return matrix; }
internal PdfFormXObject(PdfDocument thisDocument, PdfImportedObjectTable importedObjectTable, XPdfForm form) : base(thisDocument) { Elements.SetName(Keys.Type, "/XObject"); Elements.SetName(Keys.Subtype, "/Form"); if (form.IsTemplate) { Debug.Assert(importedObjectTable == null); // TODO more initialization here??? return; } Debug.Assert(importedObjectTable != null); Debug.Assert(ReferenceEquals(thisDocument, importedObjectTable.Owner)); XPdfForm pdfForm = form; // Get import page PdfPages importPages = importedObjectTable.ExternalDocument.Pages; if (pdfForm.PageNumber < 1 || pdfForm.PageNumber > importPages.Count) { PSSR.ImportPageNumberOutOfRange(pdfForm.PageNumber, importPages.Count, form._path); } PdfPage importPage = importPages[pdfForm.PageNumber - 1]; // Import resources PdfItem res = importPage.Elements["/Resources"]; if (res != null) // unlikely but possible { #if true // Get root object PdfObject root; if (res is PdfReference) { root = ((PdfReference)res).Value; } else { root = (PdfDictionary)res; } root = ImportClosure(importedObjectTable, thisDocument, root); // If the root was a direct object, make it indirect. if (root.Reference == null) { thisDocument._irefTable.Add(root); } Debug.Assert(root.Reference != null); Elements["/Resources"] = root.Reference; #else // Get transitive closure PdfObject[] resources = importPage.Owner.Internals.GetClosure(resourcesRoot); int count = resources.Length; #if DEBUG_ for (int idx = 0; idx < count; idx++) { Debug.Assert(resources[idx].XRef != null); Debug.Assert(resources[idx].XRef.Document != null); Debug.Assert(resources[idx].Document != null); if (resources[idx].ObjectID.ObjectNumber == 12) { GetType(); } } #endif // 1st step. Already imported objects are reused and new ones are cloned. for (int idx = 0; idx < count; idx++) { PdfObject obj = resources[idx]; if (importedObjectTable.Contains(obj.ObjectID)) { // external object was already imported PdfReference iref = importedObjectTable[obj.ObjectID]; Debug.Assert(iref != null); Debug.Assert(iref.Value != null); Debug.Assert(iref.Document == Owner); // replace external object by the already clone counterpart resources[idx] = iref.Value; } else { // External object was not imported ealier and must be cloned PdfObject clone = obj.Clone(); Debug.Assert(clone.Reference == null); clone.Document = Owner; if (obj.Reference != null) { // add it to this (the importer) document Owner.irefTable.Add(clone); Debug.Assert(clone.Reference != null); // save old object identifier importedObjectTable.Add(obj.ObjectID, clone.Reference); //Debug.WriteLine("Cloned: " + obj.ObjectID.ToString()); } else { // The root object (the /Resources value) is not an indirect object Debug.Assert(idx == 0); // add it to this (the importer) document Owner.irefTable.Add(clone); Debug.Assert(clone.Reference != null); } // replace external object by its clone resources[idx] = clone; } } #if DEBUG_ for (int idx = 0; idx < count; idx++) { Debug.Assert(resources[idx].XRef != null); Debug.Assert(resources[idx].XRef.Document != null); Debug.Assert(resources[idx].Document != null); if (resources[idx].ObjectID.ObjectNumber == 12) { GetType(); } } #endif // 2nd step. Fix up indirect references that still refers to the import document. for (int idx = 0; idx < count; idx++) { PdfObject obj = resources[idx]; Debug.Assert(obj.Owner != null); FixUpObject(importedObjectTable, importedObjectTable.Owner, obj); } // Set resources key to the root of the clones Elements["/Resources"] = resources[0].Reference; #endif } // Take /Rotate into account PdfRectangle rect = importPage.Elements.GetRectangle(PdfPage.Keys.MediaBox); int rotate = importPage.Elements.GetInteger(PdfPage.Keys.Rotate); //rotate = 0; if (rotate == 0) { // Set bounding box to media box Elements["/BBox"] = rect; } else { // TODO: Have to adjust bounding box? (I think not, but I'm not sure -> wait for problem) Elements["/BBox"] = rect; // Rotate the image such that it is upright XMatrix matrix = new XMatrix(); double width = rect.Width; double height = rect.Height; matrix.RotateAtPrepend(-rotate, new XPoint(width / 2, height / 2)); // Translate the image such that its center lies on the center of the rotated bounding box double offset = (height - width) / 2; if (height > width) { matrix.TranslatePrepend(offset, offset); } else { matrix.TranslatePrepend(-offset, -offset); } //string item = "[" + PdfEncoders.ToString(matrix) + "]"; //Elements[Keys.Matrix] = new PdfLiteral(item); Elements.SetMatrix(Keys.Matrix, matrix); } // Preserve filter because the content keeps unmodified PdfContent content = importPage.Contents.CreateSingleContent(); #if !DEBUG content.Compressed = true; #endif PdfItem filter = content.Elements["/Filter"]; if (filter != null) { Elements["/Filter"] = filter.Clone(); } // (no cloning needed because the bytes keep untouched) Stream = content.Stream; // new PdfStream(bytes, this); Elements.SetInteger("/Length", content.Stream.Value.Length); }
internal static XMatrix CreateSkewRadians(double skewX, double skewY, double centerX, double centerY) { XMatrix matrix = new XMatrix(); matrix.Append(CreateTranslation(-centerX, -centerY)); matrix.Append(new XMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0)); matrix.Append(CreateTranslation(centerX, centerY)); return matrix; }
/// <summary> /// Muliplies the spcified transformation with the current transformation and returns the new value; /// </summary> public XMatrix MultiplyTransform(XMatrix matrix) { return(this.graphicsState.MultiplyTransform(matrix)); }
static XMatrix CreateIdentity() { XMatrix matrix = new XMatrix(); matrix.SetMatrix(1, 0, 0, 1, 0, 0, XMatrixTypes.Identity); return matrix; }
void AddCapToPath(Path path, PathFigure figure, double length, double lineWidthHalf, LineCap lineCap, XMatrix matrix) { // sketch: // 1. create Transform that make a horizontal line with start in 0,0 // 2. create a Polygon with the shape of the line including its caps // 3. render the shape with the brush of the pen //PolyLineSegment seg; switch (lineCap) { case LineCap.Flat: matrix.Transform(new XPoint(length + lineWidthHalf, -lineWidthHalf)); break; case LineCap.Square: matrix.Transform(new XPoint(length + lineWidthHalf, -lineWidthHalf)); break; case LineCap.Round: break; case LineCap.Triangle: break; } }
internal static void PrependOffset(ref XMatrix matrix, double offsetX, double offsetY) { if (matrix._type == XMatrixTypes.Identity) { matrix = new XMatrix(1, 0, 0, 1, offsetX, offsetY); matrix._type = XMatrixTypes.Translation; } else { matrix._offsetX += (matrix._m11 * offsetX) + (matrix._m21 * offsetY); matrix._offsetY += (matrix._m12 * offsetX) + (matrix._m22 * offsetY); if (matrix._type != XMatrixTypes.Unknown) matrix._type |= XMatrixTypes.Translation; } }
/// <summary> /// Writes the specified text transformation matrix to the content stream. /// </summary> public void WriteTextTransform(XMatrix matrix) { Debug.Assert(this.streamMode == StreamMode.Text, "Must be in text mode when setting text matrix."); WriteLiteral("{0:0.####} {1:0.####} {2:0.####} {3:0.####} {4:0.####} {5:0.####} Tm\n", matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY); }
/// <summary> /// Multiplies two matrices. /// </summary> public static XMatrix Multiply(XMatrix trans1, XMatrix trans2) { MatrixHelper.MultiplyMatrix(ref trans1, ref trans2); return trans1; }
/// <summary> /// Multiplies a point with a matrix. /// </summary> public static XPoint Multiply(XPoint point, XMatrix matrix) { return(matrix.Transform(point)); }
/// <summary> /// Prepends the specified matrix to this matrix. /// </summary> public void Prepend(XMatrix matrix) { this = matrix * this; }
/// <summary> /// Draws the gridlines into the plot area. /// </summary> internal override void Draw() { ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; if (plotAreaRect.IsEmpty) { return; } AxisRendererInfo xari = cri.xAxisRendererInfo; AxisRendererInfo yari = cri.yAxisRendererInfo; double xMin = xari.MinimumScale; double xMax = xari.MaximumScale; double yMin = yari.MinimumScale; double yMax = yari.MaximumScale; double xMajorTick = xari.MajorTick; double yMajorTick = yari.MajorTick; double xMinorTick = xari.MinorTick; double yMinorTick = yari.MinorTick; XMatrix matrix = cri.plotAreaRendererInfo._matrix; LineFormatRenderer lineFormatRenderer; XGraphics gfx = _rendererParms.Graphics; XPoint[] points = new XPoint[2]; if (xari.MinorGridlinesLineFormat != null) { lineFormatRenderer = new LineFormatRenderer(gfx, xari.MinorGridlinesLineFormat); for (double x = xMin + xMinorTick; x < xMax; x += xMinorTick) { points[0].X = x; points[0].Y = yMin; points[1].X = x; points[1].Y = yMax; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } if (xari.MajorGridlinesLineFormat != null) { lineFormatRenderer = new LineFormatRenderer(gfx, xari.MajorGridlinesLineFormat); for (double x = xMin; x <= xMax; x += xMajorTick) { points[0].X = x; points[0].Y = yMin; points[1].X = x; points[1].Y = yMax; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } if (yari.MinorGridlinesLineFormat != null) { lineFormatRenderer = new LineFormatRenderer(gfx, yari.MinorGridlinesLineFormat); for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) { points[0].X = xMin; points[0].Y = y; points[1].X = xMax; points[1].Y = y; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } if (yari.MajorGridlinesLineFormat != null) { lineFormatRenderer = new LineFormatRenderer(gfx, yari.MajorGridlinesLineFormat); for (double y = yMin; y <= yMax; y += yMajorTick) { points[0].X = xMin; points[0].Y = y; points[1].X = xMax; points[1].Y = y; matrix.TransformPoints(points); lineFormatRenderer.DrawLine(points[0], points[1]); } } }
public void MultiplyPrepend(XMatrix matrix) { Prepend(matrix); }
public void MultiplyTransform(XMatrix m) { g.MultiplyTransform(m); }
/// <summary> /// Sets the clip path empty. Only possible if graphic state level has the same value as it has when /// the first time SetClip was invoked. /// </summary> public void ResetClip() { // No clip level means no clipping occurs and nothing is to do. if (this.clipLevel == 0) return; // Only at the clipLevel the clipping can be reset. if (this.clipLevel != this.gfxState.Level) throw new NotImplementedException("Cannot reset clip region in an inner graphic state level."); // Must be in graphical mode before popping the graphics state. BeginGraphic(); // Save InternalGraphicsState and transformation of the current graphical state. InternalGraphicsState state = this.gfxState.InternalState; XMatrix ctm = this.gfxState.Transform; // Empty clip path by switching back to the previous state. RestoreState(); SaveState(); // Save internal state this.gfxState.InternalState = state; // Restore CTM this.gfxState.Transform = ctm; }
/// <summary> /// Creates between 1 and 5 Béziers curves from parameters specified like in GDI+. /// </summary> public static List <XPoint> BezierCurveFromArc(double x, double y, double width, double height, double startAngle, double sweepAngle, PathStart pathStart, ref XMatrix matrix) { List <XPoint> points = new List <XPoint>(); // Normalize the angles. double α = startAngle; if (α < 0) { α = α + (1 + Math.Floor((Math.Abs(α) / 360))) * 360; } else if (α > 360) { α = α - Math.Floor(α / 360) * 360; } Debug.Assert(α >= 0 && α <= 360); double β = sweepAngle; if (β < -360) { β = -360; } else if (β > 360) { β = 360; } if (α == 0 && β < 0) { α = 360; } else if (α == 360 && β > 0) { α = 0; } // Is it possible that the arc is small starts and ends in same quadrant? bool smallAngle = Math.Abs(β) <= 90; β = α + β; if (β < 0) { β = β + (1 + Math.Floor((Math.Abs(β) / 360))) * 360; } bool clockwise = sweepAngle > 0; int startQuadrant = Quadrant(α, true, clockwise); int endQuadrant = Quadrant(β, false, clockwise); if (startQuadrant == endQuadrant && smallAngle) { AppendPartialArcQuadrant(points, x, y, width, height, α, β, pathStart, matrix); } else { int currentQuadrant = startQuadrant; bool firstLoop = true; do { if (currentQuadrant == startQuadrant && firstLoop) { double ξ = currentQuadrant * 90 + (clockwise ? 90 : 0); AppendPartialArcQuadrant(points, x, y, width, height, α, ξ, pathStart, matrix); } else if (currentQuadrant == endQuadrant) { double ξ = currentQuadrant * 90 + (clockwise ? 0 : 90); AppendPartialArcQuadrant(points, x, y, width, height, ξ, β, PathStart.Ignore1st, matrix); } else { double ξ1 = currentQuadrant * 90 + (clockwise ? 0 : 90); double ξ2 = currentQuadrant * 90 + (clockwise ? 90 : 0); AppendPartialArcQuadrant(points, x, y, width, height, ξ1, ξ2, PathStart.Ignore1st, matrix); } // Don't stop immediately if arc is greater than 270 degrees. if (currentQuadrant == endQuadrant && smallAngle) { break; } smallAngle = true; if (clockwise) { currentQuadrant = currentQuadrant == 3 ? 0 : currentQuadrant + 1; } else { currentQuadrant = currentQuadrant == 0 ? 3 : currentQuadrant - 1; } firstLoop = false; } while (true); } return(points); }
/// <summary> /// Appends a Bézier curve for an arc within a quadrant. /// </summary> void AppendPartialArcQuadrant(double x, double y, double width, double height, double α, double β, PathStart pathStart, XMatrix matrix) { Debug.Assert(α >= 0 && α <= 360); Debug.Assert(β >= 0); if (β > 360) β = β - Math.Floor(β / 360) * 360; Debug.Assert(Math.Abs(α - β) <= 90); // Scanling factor double δx = width / 2; double δy = height / 2; // Center of ellipse double x0 = x + δx; double y0 = y + δy; // We have the following quarters: // | // 2 | 3 // ----+----- // 1 | 0 // | // If the angles lie in quarter 2 or 3, their values are subtracted by 180 and the // resulting curve is reflected at the center. This algorithm works as expected (simply tried out). // There may be a mathematically more elegant solution... bool reflect = false; if (α >= 180 && β >= 180) { α -= 180; β -= 180; reflect = true; } double cosα, cosβ, sinα, sinβ; if (width == height) { // Circular arc needs no correction. α = α * Calc.Deg2Rad; β = β * Calc.Deg2Rad; } else { // Elliptic arc needs the angles to be adjusted such that the scaling transformation is compensated. α = α * Calc.Deg2Rad; sinα = Math.Sin(α); if (Math.Abs(sinα) > 1E-10) α = Calc.πHalf - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); β = β * Calc.Deg2Rad; sinβ = Math.Sin(β); if (Math.Abs(sinβ) > 1E-10) β = Calc.πHalf - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); } double κ = 4 * (1 - Math.Cos((α - β) / 2)) / (3 * Math.Sin((β - α) / 2)); sinα = Math.Sin(α); cosα = Math.Cos(α); sinβ = Math.Sin(β); cosβ = Math.Cos(β); XPoint pt1, pt2, pt3; if (!reflect) { // Calculation for quarter 0 and 1 switch (pathStart) { case PathStart.MoveTo1st: pt1 = matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα)); AppendFormat("{0:0.###} {1:0.###} m\n", pt1.x, pt1.y); break; case PathStart.LineTo1st: pt1 = matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα)); AppendFormat("{0:0.###} {1:0.###} l\n", pt1.x, pt1.y); break; case PathStart.Ignore1st: break; } pt1 = matrix.Transform(new XPoint(x0 + δx * (cosα - κ * sinα), y0 + δy * (sinα + κ * cosα))); pt2 = matrix.Transform(new XPoint(x0 + δx * (cosβ + κ * sinβ), y0 + δy * (sinβ - κ * cosβ))); pt3 = matrix.Transform(new XPoint(x0 + δx * cosβ, y0 + δy * sinβ)); AppendFormat("{0:0.###} {1:0.###} {2:0.###} {3:0.###} {4:0.###} {5:0.###} c\n", pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); } else { // Calculation for quarter 2 and 3 switch (pathStart) { case PathStart.MoveTo1st: pt1 = matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα)); AppendFormat("{0:0.###} {1:0.###} m\n", pt1.x, pt1.y); break; case PathStart.LineTo1st: pt1 = matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα)); AppendFormat("{0:0.###} {1:0.###} l\n", pt1.x, pt1.y); break; case PathStart.Ignore1st: break; } pt1 = matrix.Transform(new XPoint(x0 - δx * (cosα - κ * sinα), y0 - δy * (sinα + κ * cosα))); pt2 = matrix.Transform(new XPoint(x0 - δx * (cosβ + κ * sinβ), y0 - δy * (sinβ - κ * cosβ))); pt3 = matrix.Transform(new XPoint(x0 - δx * cosβ, y0 - δy * sinβ)); AppendFormat("{0:0.###} {1:0.###} {2:0.###} {3:0.###} {4:0.###} {5:0.###} c\n", pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); } }
/// <summary> /// Multiplies a point with a matrix. /// </summary> public static XPoint Multiply(XPoint point, XMatrix matrix) { return matrix.Transform(point); }
/// <summary> /// Realizes the CTM. /// </summary> public void RealizeCtm() { if (this.MustRealizeCtm) { Debug.Assert(!this.unrealizedCtm.IsIdentity, "mrCtm is unnecessarily set."); double[] matrix = this.unrealizedCtm.GetElements(); // Up to six decimal digits to prevent round up problems this.renderer.AppendFormat("{0:0.######} {1:0.######} {2:0.######} {3:0.######} {4:0.######} {5:0.######} cm\n", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); this.realizedCtm.Prepend(this.unrealizedCtm); this.unrealizedCtm = new XMatrix(); //XMatrix.Identity; this.MustRealizeCtm = false; } }
PdfTilingPattern BuildPattern(VisualBrush brush, XMatrix transform) { // Bounding box lays always at (0,0) XRect bbox = new XRect(0, 0, brush.Viewport.Width, brush.Viewport.Height); XMatrix matrix = transform; matrix.Prepend(new XMatrix(1, 0, 0, 1, brush.Viewport.X, brush.Viewport.Y)); if (brush.Transform != null) { matrix.Prepend(new XMatrix(brush.Transform.Value.M11, brush.Transform.Value.M12, brush.Transform.Value.M21, brush.Transform.Value.M22, brush.Transform.Value.OffsetX, brush.Transform.Value.OffsetY)); } // Set X Step beyond the viewport if tilemode is set to none since // there is no other easy way to turn off tiling - NPJ double xStep = brush.Viewport.Width * (brush.TileMode == TileMode.None ? 2 : 1); double yStep = brush.Viewport.Height * (brush.TileMode == TileMode.None ? 2 : 1); // PdfTilingPattern //<< // /BBox [0 0 240 120] // /Length 74 // /Matrix [0.75 0 0 -0.75 0 480] // /PaintType 1 // /PatternType 1 // /Resources // << // /ExtGState // << // /GS0 10 0 R // >> // /XObject // << // /Fm0 17 0 R // >> // >> // /TilingType 3 // /Type /Pattern // /XStep 480 // /YStep 640 //>> //stream // q // 0 0 240 120 re // W n // q // 2.3999939 0 0 1.1999969 0 0 cm // /GS0 gs // /Fm0 Do // Q //Q //endstream var pattern = new PdfTilingPattern(Context.PdfDocument); Context.PdfDocument.Internals.AddObject(pattern); pattern.Elements.SetInteger(PdfTilingPattern.Keys.PatternType, 1); // Tiling pattern.Elements.SetInteger(PdfTilingPattern.Keys.PaintType, 1); // Color pattern.Elements.SetInteger(PdfTilingPattern.Keys.TilingType, 3); // Constant spacing and faster tiling pattern.Elements.SetMatrix(PdfTilingPattern.Keys.Matrix, matrix); pattern.Elements.SetRectangle(PdfTilingPattern.Keys.BBox, new PdfRectangle(bbox)); pattern.Elements.SetReal(PdfTilingPattern.Keys.XStep, xStep); pattern.Elements.SetReal(PdfTilingPattern.Keys.YStep, yStep); // Set extended graphic state like Acrobat do var pdfExtGState = Context.PdfDocument.Internals.CreateIndirectObject <PdfExtGState>(); pdfExtGState.SetDefault1(); var pdfForm = BuildForm(brush); var writer = new PdfContentWriter(Context, pattern); writer.BeginContentRaw(); // Acrobat 8 clips to bounding box, so do we //writer.WriteClip(bbox); XMatrix transformation = new XMatrix(); double dx = brush.Viewport.Width / brush.Viewbox.Width * 96 / pdfForm.DpiX; double dy = brush.Viewport.Height / brush.Viewbox.Height * 96 / pdfForm.DpiY; transformation = new XMatrix(dx, 0, 0, dy, 0, 0); writer.WriteMatrix(transformation); writer.WriteGraphicsState(pdfExtGState); string name = writer.Resources.AddForm(pdfForm); writer.WriteLiteral(name + " Do\n"); writer.EndContent(); return(pattern); }