// =========================================================================== //  ©2013-2026 WebSupergoo. All rights reserved. // //  This source code is for use exclusively with the ABCpdf product with //  which it is distributed, under the terms of the license for that //  product. Details can be found at // //    http://www.websupergoo.com/ // //  This copyright notice must not be deleted and must be reproduced alongside //  any sections of code extracted from this module. // =========================================================================== using System; using System.Collections.Generic; using System.Text; using System.Globalization; using System.Runtime.InteropServices; using WebSupergoo.ABCpdf14; namespace AdvancedGraphics {   /// <summary>Rectangle in double precision.</summary>   [StructLayout(LayoutKind.Auto)]   public struct PDFRect {     /// <summary>The entire plane.</summary>     public static PDFRect Infinity {       get {         return new PDFRect(double.NegativeInfinity, double.NegativeInfinity,           double.PositiveInfinity, double.PositiveInfinity);       }     }     public double X, Y, Width, Height;     public PDFRect(double x, double y, double width, double height) {       X = x;       Y = y;       Width = width;       Height = height;     }     public double Right {       get { return double.IsInfinity(Width)? Width: X+Width; }     }     public double Top {       get { return double.IsInfinity(Height)? Height: Y+Height; }     }     /// <summary>The string representation as used in PDF and XRect.String.</summary>     public override string ToString() {       const string format = PDFContent.mFloatFormat;       return string.Format(NumberFormatInfo.InvariantInfo,         "{0:" + format + "} {1:" + format         + "} {2:" + format + "} {3:" + format + "}",         X, Y, Right, Top);     }     public void Union(double x, double y, double width, double height) {       if(width>0) {         if(!(Width>0)) {           Width = width;           X = x;         } else if(x<X) {           Width += X-x;           X = x;           if(width>Width)             Width = width;         } else {           double right = Right;           double rectRight = double.IsInfinity(width)? width: x+width;           if(rectRight>right)             Width = rectRight-X;         }       }       if(height>0) {         if(!(Height>0)) {           Height = height;           Y = y;         } else if(y<Y) {           Height += Y-y;           Y = y;           if(height>Height)             Height = height;         } else {           double top = Top;           double rectTop = double.IsInfinity(height)? height: y+height;           if(rectTop>top)             Height = rectTop-Y;         }       }     }     public void Intersect(PDFRect rect) {       if(X<rect.X) {         Width -= rect.X-X;         X = rect.X;       }       if(rect.Width<double.PositiveInfinity         && (!(Width<double.PositiveInfinity) || X+Width>rect.X+rect.Width))         Width = rect.X+rect.Width-X;       if(Y<rect.Y) {         Height -= rect.Y-Y;         Y = rect.Y;       }       if(rect.Height<double.PositiveInfinity         && (!(Height<double.PositiveInfinity) || Y+Height>rect.Y+rect.Height))         Height = rect.Y+rect.Height-Y;     }     internal static void GetLineBounds(double x0, double y0,       double x1, double y1, out double outX, out double outY,       out double outWidth, out double outHeight)     {       if(x0<=x1) {         outWidth = x1-x0;         outX = x0;       } else {         outWidth = x0-x1;         outX = x1;       }       if(y0<=y1) {         outHeight = y1-y0;         outY = y0;       } else {         outHeight = y0-y1;         outY = y1;       }     }     internal static void GetBezierBounds(double start, double control0,       double control1, double end, out double outX, out double outWidth)     {       double midControl = (control0+control1)/2;       double quadControl0 = (start+control0)/2;       double quadControl1 = (quadControl0+midControl)/2;       quadControl0 = (start+quadControl0*3)/4;       double quadControl3 = (end+control1)/2;       double quadControl2 = (quadControl3+midControl)/2;       quadControl3 = (end+quadControl3*3)/4;       double mid = quadControl1+quadControl2;       quadControl1 = (quadControl1*6+mid)/8;       quadControl2 = (quadControl2*6+mid)/8;       double x;       if(start<=end)         x = start;       else {         x = end;         end = start;       }       if(quadControl0<x)         x = quadControl0;       else if(quadControl0>end)         end = quadControl0;       if(quadControl1<x)         x = quadControl1;       else if(quadControl1>end)         end = quadControl1;       if(quadControl2<x)         x = quadControl2;       else if(quadControl2>end)         end = quadControl2;       if(quadControl3<x)         x = quadControl3;       else if(quadControl3>end)         end = quadControl3;       outX = x;       outWidth = end-x;     }   }   /// <summary>Matrix in double precision.</summary>   [StructLayout(LayoutKind.Auto)]   public struct PDFMatrix {     public static PDFMatrix Identity { get { return new PDFMatrix(1, 0, 0, 1, 0, 0); } }     public double M00, M01, M10, M11, M20, M21;     public PDFMatrix(double m00, double m01, double m10, double m11,       double m20, double m21)     {       M00 = m00;       M01 = m01;       M10 = m10;       M11 = m11;       M20 = m20;       M21 = m21;     }     public PDFMatrix(PDFMatrix v, double m00, double m01, double m10, double m11,       double m20, double m21)     {       M00 = v.M00*m00+v.M10*m01;       M01 = v.M01*m00+v.M11*m01;       M10 = v.M00*m10+v.M10*m11;       M11 = v.M01*m10+v.M11*m11;       M20 = v.M00*m20+v.M10*m21+v.M20;       M21 = v.M01*m20+v.M11*m21+v.M21;     }     public PDFMatrix(ref PDFMatrix v, double m00, double m01, double m10, double m11,       double m20, double m21)     {       M00 = v.M00*m00+v.M10*m01;       M01 = v.M01*m00+v.M11*m01;       M10 = v.M00*m10+v.M10*m11;       M11 = v.M01*m10+v.M11*m11;       M20 = v.M00*m20+v.M10*m21+v.M20;       M21 = v.M01*m20+v.M11*m21+v.M21;     }     public PDFMatrix(PDFMatrix v0, PDFMatrix v1) : this(ref v0, ref v1) { }     public PDFMatrix(ref PDFMatrix v0, ref PDFMatrix v1) {       M00 = v0.M00*v1.M00+v0.M10*v1.M01;       M01 = v0.M01*v1.M00+v0.M11*v1.M01;       M10 = v0.M00*v1.M10+v0.M10*v1.M11;       M11 = v0.M01*v1.M10+v0.M11*v1.M11;       M20 = v0.M00*v1.M20+v0.M10*v1.M21+v0.M20;       M21 = v0.M01*v1.M20+v0.M11*v1.M21+v0.M21;     }     public double MultiplierXSquare { get { return M00*M00+M10*M10; } }     public double MultiplierYSquare { get { return M01*M01+M11*M11; } }     public void TransformPoint(double x, double y,       out double outX, out double outY)     {       outX = x*M00+y*M10+M20;       outY = x*M01+y*M11+M21;     }     public void Translate(double offsetX, double offsetY) {       TransformPoint(offsetX, offsetY, out M20, out M21);     }     public void Scale(double scaleX, double scaleY) {       M00 *= scaleX;       M01 *= scaleX;       M10 *= scaleY;       M11 *= scaleY;     }     public void GetRectBounds(double x, double y,       double width, double height, out double outX, out double outY,       out double outWidth, out double outHeight)     {       TransformPoint(x, y, out x, out y);       double widthX = width*M00;       double heightX = height*M10;       if(widthX>=heightX) {         if(widthX<0) {           widthX = -widthX-heightX;           x -= widthX;         } else if(heightX<0) {           widthX -= heightX;           x += heightX;         } else           widthX += heightX;       } else {         if(heightX<0) {           widthX = -widthX-heightX;           x -= widthX;         } else if(widthX<0) {           x += widthX;           widthX = heightX-widthX;         } else           widthX += heightX;       }       outX = x;       outWidth = widthX;       double heightY = height*M11;       double widthY = width*M01;       if(heightY>=widthY) {         if(heightY<0) {           heightY = -heightY-widthY;           y -= heightY;         } else if(widthY<0) {           heightY -= widthY;           y += widthY;         } else           heightY += widthY;       } else {         if(widthY<0) {           heightY = -heightY-widthY;           y -= heightY;         } else if(heightY<0) {           y += heightY;           heightY = widthY-heightY;         } else           heightY += widthY;       }       outY = y;       outHeight = heightY;     }     internal void GetBezierBounds(double startX, double startY,       double controlX0, double controlY0,       double controlX1, double controlY1, double endX, double endY,       out double outX, out double outY,       out double outWidth, out double outHeight)     {       // startX and startY are already transformed       //TransformPoint(startX, startY, out startX, out startY);       TransformPoint(controlX0, controlY0, out controlX0, out controlY0);       TransformPoint(controlX1, controlY1, out controlX1, out controlY1);       TransformPoint(endX, endY, out endX, out endY);       PDFRect.GetBezierBounds(startX, controlX0, controlX1, endX,         out outX, out outWidth);       PDFRect.GetBezierBounds(startY, controlY0, controlY1, endY,         out outY, out outHeight);     }   }   /// <summary>Form XObject ID and bounds.</summary>   [StructLayout(LayoutKind.Auto)]   public struct PDFFormXObjectID {     public int ID;     public PDFRect Bounds;     public PDFFormXObjectID(int id, PDFRect bounds) {       ID = id;       Bounds = bounds;     }   }   /// <summary>PDF content with bounds for the construction of Form XObjects.</summary>   public class PDFBoundedContent: PDFContent {     /// <summary>The graphics state for the calculation of the bounds.</summary>     protected class PDFGraphicsState {       public PDFRect ClipBounds;       public PDFMatrix Matrix;       public double LineWidth;       public double MiterLimit;       public LineJoin LineJoin;       public LineCap LineCap;       public int FontID;       public double FontSize;       public double CharSpacing;       public double WordSpacing;       public double HorizontalTextScale;       public double TextLeading;       public TextRenderingMode TextRender;       public double TextRise;       public PDFGraphicsState() {         ClipBounds = PDFRect.Infinity;         Matrix = PDFMatrix.Identity;         LineWidth = 1;         MiterLimit = 10;         LineJoin = PDFContent.LineJoin.Miter;         LineCap = PDFContent.LineCap.Butt;         HorizontalTextScale = 100;         TextRender = TextRenderingMode.FillText;       }       private PDFGraphicsState(PDFGraphicsState v) {         ClipBounds = v.ClipBounds;         Matrix = v.Matrix;         LineWidth = v.LineWidth;         MiterLimit = v.MiterLimit;         LineJoin = v.LineJoin;         LineCap = v.LineCap;         FontID = v.FontID;         FontSize = v.FontSize;         CharSpacing = v.CharSpacing;         WordSpacing = v.WordSpacing;         HorizontalTextScale = v.HorizontalTextScale;         TextLeading = v.TextLeading;         TextRender = v.TextRender;         TextRise = v.TextRise;       }       public PDFGraphicsState Clone() { return CloneState(); }       protected virtual PDFGraphicsState CloneState() {         return new PDFGraphicsState(this);       }     }     /// <summary>The Form XObject ID.</summary>     private int _xobjID;     /// <summary>The stack of graphics states.</summary>     private Stack<PDFGraphicsState> _stateStack;     /// <summary>The current graphics state.</summary>     private PDFGraphicsState _state;     /// <summary>The current drawing position.</summary>     private double _x, _y;     /// <summary>The bounds of the content.</summary>     private PDFRect _bounds;     /// <summary>The bounds of path being constructed.</summary>     private PDFRect _pathBounds;     /// <summary>The text line matrix.</summary>     private PDFMatrix _textMatrix;     /// <summary>The ID of the font whose information is loaded/cached.</summary>     private int _fontInfoID;     /// <summary>The font glyph bounding box.</summary>     private XRect _fontBBox;     /// <summary>Whether the font is multibyte.</summary>     private bool _fontIsMultibyte;     public PDFBoundedContent(Doc doc) : this(doc,       doc.AddObject("<</Type /XObject /Subtype /Form"       +" /Resources <</ProcSet[/PDF /Text /ImageB /ImageC /ImageI]>>>>stream\nendstream\n"))     { }     public PDFBoundedContent(Doc doc, int xobjID) : base(doc) {       _xobjID = xobjID;       _stateStack = new Stack<PDFGraphicsState>();       _state = new PDFGraphicsState();       _textMatrix = PDFMatrix.Identity;       _fontBBox = new XRect();     }     protected override int ResourcesObjectID { get { return _xobjID; } }     private void UnionBounds(PDFRect rect) {       rect.Intersect(_state.ClipBounds);       _bounds.Union(rect.X, rect.Y, rect.Width, rect.Height);     }     private void UnionPathBounds(double lineWidth) {       double x = _pathBounds.X;       double y = _pathBounds.Y;       if(lineWidth>0) {         lineWidth /= 2;         double halfWidthX = lineWidth*Math.Sqrt(_state.Matrix.MultiplierXSquare);         double halfWidthY = lineWidth*Math.Sqrt(_state.Matrix.MultiplierYSquare);         _pathBounds.X -= halfWidthX;         _pathBounds.Y -= halfWidthY;         _pathBounds.Width += 2*halfWidthX;         _pathBounds.Height += 2*halfWidthY;       }       UnionBounds(_pathBounds);       _pathBounds.X = x;       _pathBounds.Y = y;       _pathBounds.Width = 0;       _pathBounds.Height = 0;     }     public override void Move(double x, double y) {       base.Move(x, y);       _state.Matrix.TransformPoint(x, y, out _x, out _y);     }     public override void Line(double x, double y) {       base.Line(x, y);       double x0 = _x, y0 = _y;       _state.Matrix.TransformPoint(x, y, out _x, out _y);       PDFRect.GetLineBounds(x0, y0, _x, _y, out x0, out y0, out x, out y);       _pathBounds.Union(x0, y0, x, y);     }     public override void Rect(double x, double y, double w, double h) {       base.Rect(x, y, w, h);       _state.Matrix.TransformPoint(x, y, out _x, out _y);       _state.Matrix.GetRectBounds(x, y, w, h, out x, out y, out w, out h);       _pathBounds.Union(x, y, w, h);     }     public override void Bezier(double x1, double y1, double x2, double y2,       double x3, double y3)     {       base.Bezier(x1, y1, x2, y2, x3, y3);       double x0 = _x, y0 = _y;       _state.Matrix.TransformPoint(x3, y3, out _x, out _y);       _state.Matrix.GetBezierBounds(x0, y0, x1, y1, x2, y2, x3, y3,         out x0, out y0, out x1, out y1);       _pathBounds.Union(x0, y0, x1, y1);     }     public override void Stroke() {       base.Stroke();       UnionPathBounds(_state.LineWidth);     }     public override void Fill() {       base.Fill();       UnionPathBounds(0);     }     public override void FillEvenOddRule() {       base.FillEvenOddRule();       UnionPathBounds(0);     }     public override void Clip() {       base.Clip();       _state.ClipBounds.Intersect(_pathBounds);       _pathBounds.Width = 0;       _pathBounds.Height = 0;     }     public override void ClipEvenOddRule() {       base.ClipEvenOddRule();       _state.ClipBounds.Intersect(_pathBounds);       _pathBounds.Width = 0;       _pathBounds.Height = 0;     }     public override void SaveState() {       base.SaveState();       _stateStack.Push(_state.Clone());     }     public override void RestoreState() {       base.RestoreState();       _state = _stateStack.Pop();     }     public override void SetLineWidth(double v) {       base.SetLineWidth(v);       _state.LineWidth = Math.Abs(v);     }     public override void SetLineCap(int v) {       base.SetLineCap(v);       _state.LineCap = (LineCap)v;     }     public override void SetLineJoin(int v) {       base.SetLineJoin(v);       _state.LineJoin = (LineJoin)v;     }     public override void SetMiterLimit(double v) {       base.SetMiterLimit(v);       _state.MiterLimit = Math.Abs(v);     }     public override void Transform(double a, double b, double c, double d,       double h, double v)     {       base.Transform(a, b, c, d, h, v);       _state.Matrix = new PDFMatrix(ref _state.Matrix, a, b, c, d, h, v);     }     public override void AddContent(PDFContent theContent) {       throw new NotSupportedException("PDFBoundedContent does not support AddContent.");     }     public override void AddToDoc() {       throw new InvalidOperationException("PDFBoundedContent does not allow AddToDoc.");     }     /// <summary>Write the contents to the Form XObject.</summary>     public PDFFormXObjectID WriteToFormXObject() {       WriteAllGStates();       mDoc.SetInfo(_xobjID, "stream", mContents.ToString());       mDoc.SetInfo(_xobjID, "/BBox:Rect", _bounds.ToString());       ShowErrors();       return new PDFFormXObjectID(_xobjID, _bounds);     }     public override void DoImage(int imageID) {       base.DoImage(imageID);       PDFRect rect;       _state.Matrix.GetRectBounds(0, 0, 1, 1,         out rect.X, out rect.Y, out rect.Width, out rect.Height);       UnionBounds(rect);     }     public override void DoFormXObject(PDFFormXObjectID xobjID) {       base.DoFormXObject(xobjID);       PDFRect rect;       _state.Matrix.GetRectBounds(xobjID.Bounds.X, xobjID.Bounds.Y,         xobjID.Bounds.Width, xobjID.Bounds.Height,         out rect.X, out rect.Y, out rect.Width, out rect.Height);       UnionBounds(rect);     }     public override void BeginText() {       base.BeginText();       _textMatrix = PDFMatrix.Identity;     }     public override void SetTextRenderingMode(PDFContent.TextRenderingMode mode) {       base.SetTextRenderingMode(mode);       _state.TextRender = mode;     }     public override void SetCharacterSpacing(double tc) {       base.SetCharacterSpacing(tc);       _state.CharSpacing = tc;     }     public override void SetWordSpacing(double tw) {       base.SetWordSpacing(tw);       _state.WordSpacing = tw;     }     public override void SetHorizontalScaling(double th) {       base.SetHorizontalScaling(th);       _state.HorizontalTextScale = th;     }     public override void SetTextLeading(double tl) {       base.SetTextLeading(tl);       _state.TextLeading = tl;     }     public override void SetTextRise(double ts) {       base.SetTextRise(ts);       _state.TextRise = ts;     }     public override void ShowTextString(string text) {       base.ShowTextString(text);       if(text.Length<=0 || _state.FontID==0 || !(_state.FontSize>0))         return;       string s = mDoc.GetInfo(_state.FontID, "Widths "+text);       if(s.Length<=0)         return;       if(_fontInfoID!=_state.FontID) {         _fontIsMultibyte = mDoc.GetInfo(_state.FontID, "/Subtype:Name")=="Type0";         _fontBBox.String = mDoc.GetInfo(_state.FontID, "FontBBox");         _fontInfoID = _state.FontID;       }       long width = 0;       int i = 0, count = 0;       for(int j; (j = s.IndexOf(',', i))>=i; i = j+1, ++count) {         width += int.Parse(s.Substring(i, j-i), NumberFormatInfo.InvariantInfo);       }       i = int.Parse(s.Substring(i), NumberFormatInfo.InvariantInfo);       double right = width + Math.Max(_fontBBox.Right, i);       width += i;       ++count;       double scale = _state.FontSize/1000;       right *= scale;       double advance = width*scale;       int wordSpaceCount = 0;       if(!_fontIsMultibyte && _state.WordSpacing!=0) {         for(i = 0; i<count; ++i) {           if(text[i]==' ')             ++wordSpaceCount;         }       }       if(wordSpaceCount>0) {         double spacing = _state.WordSpacing*wordSpaceCount;         right += text[count-1]!=' '? spacing: spacing-_state.WordSpacing;         advance += spacing;       }       if(_state.CharSpacing!=0) {         double spacing = _state.CharSpacing*count;         if(count>1)           right += spacing-_state.CharSpacing;         advance += spacing;       }       PDFMatrix matrix = new PDFMatrix(ref _state.Matrix, ref _textMatrix);       if(_state.TextRise!=0)         matrix.Translate(0, _state.TextRise);       if(_state.HorizontalTextScale!=100) {         double hscale = _state.HorizontalTextScale/100;         matrix.Scale(hscale, 1);         advance *= hscale;       }       _textMatrix.Translate(advance, 0);       double left = _fontBBox.Left*scale;       PDFRect rect;       matrix.GetRectBounds(left, _fontBBox.Bottom*scale,         right-left, _fontBBox.Height*scale,         out rect.X, out rect.Y, out rect.Width, out rect.Height);       if(_state.LineWidth>0) {         switch(_state.TextRender) {         case TextRenderingMode.StrokeText:         case TextRenderingMode.FillThenStrokeText:         case TextRenderingMode.StrokeTextAndAddForClipping:         case TextRenderingMode.FillThenStrokeTextAndAddForClipping:           double halfWidth = _state.LineWidth/2;           double halfWidthX = halfWidth*Math.Sqrt(_state.Matrix.MultiplierXSquare);           double halfWidthY = halfWidth*Math.Sqrt(_state.Matrix.MultiplierYSquare);           rect.X -= halfWidthX;           rect.Y -= halfWidthY;           rect.Width += 2*halfWidthX;           rect.Height += 2*halfWidthY;           break;         }       }       UnionBounds(rect);     }     public override void SetFont(int fontObjID, double fontSize) {       base.SetFont(fontObjID, fontSize);       _state.FontID = fontObjID;       _state.FontSize = Math.Abs(fontSize);     }     public override void SetTextMatrix(double a, double b, double c, double d, double e, double f) {       base.SetTextMatrix(a, b, c, d, e, f);       _textMatrix.M00 = a;       _textMatrix.M01 = b;       _textMatrix.M10 = c;       _textMatrix.M11 = d;       _textMatrix.M20 = e;       _textMatrix.M21 = f;     }     public override void NextLine() {       base.NextLine();       if(_state.TextLeading!=0)         _textMatrix.Translate(0, -_state.TextLeading);     }     public override void TextMove(double tx, double ty) {       base.TextMove(tx, ty);       _textMatrix.Translate(tx, ty);     }   } }