using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.IO; using System.Windows.Forms; using FastReport.Utils; namespace FastReport { /// /// Specifies the alignment of a image in the border. /// public enum ImageAlign { /// /// Specifies that image is not aligned in the layout rectangle. /// None, /// /// Specifies that image is aligned in the top-left of the layout rectangle. /// Top_Left, /// /// Specifies that image is aligned in the top-center of the layout rectangle. /// Top_Center, /// /// Specifies that image is aligned in the top-right of the layout rectangle. /// Top_Right, /// /// Specifies that image is aligned in the center-left of the layout rectangle. /// Center_Left, /// /// Specifies that image is aligned in the center-center of the layout rectangle. /// Center_Center, /// /// Specifies that image is aligned in the center-right of the layout rectangle. /// Center_Right, /// /// Specifies that image is aligned in the center-left of the layout rectangle. /// Bottom_Left, /// /// Specifies that image is aligned in the center-center of the layout rectangle. /// Bottom_Center, /// /// Specifies that image is aligned in the center-right of the layout rectangle. /// Bottom_Right, } /// /// the base class for all picture objects /// public abstract partial class PictureObjectBase : ReportComponentBase { #region Internal Fields #endregion Internal Fields #region Private Fields private int angle; private string dataColumn; private bool grayscale; private string imageLocation; private string imageSourceExpression; private float maxHeight; private float maxWidth; private Padding padding; private PictureBoxSizeMode saveSizeMode; private bool showErrorImage; private PictureBoxSizeMode sizeModeInternal; private ImageAlign imageAlign; #endregion Private Fields #region Public Properties /// /// Gets or sets the image rotation angle, in degrees. Possible values are 0, 90, 180, 270. /// [DefaultValue(0)] [Category("Appearance")] public int Angle { get { return angle; } set { angle = value; } } /// /// Gets or sets the data column name to get the image from. /// [Category("Data")] [Editor("FastReport.TypeEditors.DataColumnEditor, FastReport", typeof(UITypeEditor))] public string DataColumn { get { return dataColumn; } set { dataColumn = value; } } /// /// Gets or sets a value indicating that the image should be displayed in grayscale mode. /// [DefaultValue(false)] [Category("Appearance")] public virtual bool Grayscale { get { return grayscale; } set { grayscale = value; } } /// public override float Height { get { return base.Height; } set { if (MaxHeight != 0 && value > MaxHeight) value = MaxHeight; base.Height = value; } } /// /// Gets or sets the path for the image to display in the PictureObject. /// /// /// This property may contain the path to the image file as well as external URL. /// [Category("Data")] public string ImageLocation { get { return imageLocation; } set { if (!String.IsNullOrEmpty(Config.ReportSettings.ImageLocationRoot)) imageLocation = value.Replace(Config.ReportSettings.ImageLocationRoot, ""); else imageLocation = value; LoadImage(); ResetImageIndex(); } } /// /// Gets or sets the expression that determines the source for the image to display in the PictureObject. /// /// /// The result of the expression should be data column name or path to the image file. /// The data column name will be saved to the property. /// The path will be savetd to the property. /// [Category("Data")] [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] public string ImageSourceExpression { get { return imageSourceExpression; } set { imageSourceExpression = value; if (!String.IsNullOrEmpty(ImageSourceExpression) && Report != null) { string expression = ImageSourceExpression; if (ImageSourceExpression.StartsWith("[") && ImageSourceExpression.EndsWith("]")) { expression = ImageSourceExpression.Substring(1, ImageSourceExpression.Length - 2); } if (Data.DataHelper.IsValidColumn(Report.Dictionary, expression)) { DataColumn = expression; } if (Data.DataHelper.IsValidParameter(Report.Dictionary, expression)) { ImageLocation = Report.GetParameterValue(expression).ToString(); } } } } /// /// Gets a value indicating that the image stored in the databases column /// [Browsable(false)] public bool IsDataColumn { get { return !String.IsNullOrEmpty(DataColumn); } } /// /// Gets a value indicating that the image stored in the separate file /// [Browsable(false)] public bool IsFileLocation { get { if (String.IsNullOrEmpty(ImageLocation)) return false; Uri uri = CalculateUri(); return uri.IsFile; } } /// /// Gets a value indicating that the image stored in the Web /// [Browsable(false)] public bool IsWebLocation { get { if (String.IsNullOrEmpty(ImageLocation)) return false; Uri uri = CalculateUri(); return !uri.IsFile; } } /// /// Gets or sets the maximum height of a Picture object, in pixels. /// /// /// Use this property to restrict the object size if the property /// is set to AutoSize. /// [DefaultValue(0f)] [Category("Layout")] [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")] public float MaxHeight { get { return maxHeight; } set { maxHeight = value; } } /// /// Gets or sets the maximum width of a Picture object, in pixels. /// /// /// Use this property to restrict the object size if the property /// is set to AutoSize. /// [DefaultValue(0f)] [Category("Layout")] [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")] public float MaxWidth { get { return maxWidth; } set { maxWidth = value; } } /// /// Gets or sets padding within the PictureObject. /// [Category("Layout")] public Padding Padding { get { return padding; } set { padding = value; } } /// /// Gets or sets a value indicating whether the PictureObject should display /// the error indicator if there is no image in it. /// [DefaultValue(false)] [Category("Behavior")] public bool ShowErrorImage { get { return showErrorImage; } set { showErrorImage = value; } } /// /// Gets or sets a value that specifies how an image is positioned within a PictureObject. /// [DefaultValue(PictureBoxSizeMode.Zoom)] [Category("Behavior")] public virtual PictureBoxSizeMode SizeMode { get { return sizeModeInternal; } set { sizeModeInternal = value; UpdateAutoSize(); } } /// public override float Width { get { return base.Width; } set { if (MaxWidth != 0 && value > MaxWidth) value = MaxWidth; base.Width = value; } } /// /// Gets or sets the alignment of a image in the border. /// [DefaultValue(ImageAlign.None)] [Category("Appearance")] public ImageAlign ImageAlign { get { return imageAlign; } set { imageAlign = value; } } #endregion Public Properties #region Protected Properties /// /// Return base size of image, internal use only /// [Browsable(false)] protected abstract float ImageHeight { get; } /// /// Return base size of image, internal use only /// [Browsable(false)] protected abstract float ImageWidth { get; } #endregion Protected Properties #region Public Constructors /// public PictureObjectBase() { sizeModeInternal = PictureBoxSizeMode.Zoom; padding = new Padding(); imageLocation = ""; dataColumn = ""; imageSourceExpression = ""; } #endregion Public Constructors #region Public Methods /// public override void Assign(Base source) { base.Assign(source); PictureObjectBase src = source as PictureObjectBase; if (src != null) { imageLocation = src.ImageLocation; DataColumn = src.DataColumn; ImageSourceExpression = src.ImageSourceExpression; Padding = src.Padding; SizeMode = src.SizeMode; MaxWidth = src.MaxWidth; MaxHeight = src.MaxHeight; Angle = src.Angle; Grayscale = src.Grayscale; ShowErrorImage = src.ShowErrorImage; ImageAlign = src.ImageAlign; } } /// /// Calculates URI from ImageLocation /// /// public Uri CalculateUri() { try { return new Uri(ImageLocation); } catch (UriFormatException) { // TODO // the problem with linux???? PATH!!! string path; if (!String.IsNullOrEmpty(Config.ReportSettings.ImageLocationRoot)) path = Path.Combine(Config.ReportSettings.ImageLocationRoot, ImageLocation.Replace('/', '\\')); else path = Path.GetFullPath(ImageLocation); return new Uri(path); } } /// public override void Draw(FRPaintEventArgs e) { UpdateAutoSize(); base.Draw(e); DrawImage(e); DrawMarkers(e); Border.Draw(e, new RectangleF(AbsLeft, AbsTop, Width, Height)); DrawDesign(e); } public abstract void DrawImage(FRPaintEventArgs e); /// /// gets points for transform this image /// /// the box where to draw image /// image width /// image height /// scale horizontal /// scale vertical /// offset of left /// offset of top /// out start of vectors /// out end of frist vector /// out end of second vector public void GetImageAngleTransform(RectangleF drawRect, float imageWidth, float imageHeight, float scaleX, float scaleY, float offsetX, float offsetY, out PointF upperLeft, out PointF upperRight, out PointF lowerLeft) { RectangleF rect = drawRect; switch (SizeMode) { case PictureBoxSizeMode.Normal: case PictureBoxSizeMode.AutoSize: rect.Width = imageWidth * scaleX; rect.Height = imageHeight * scaleY; if (Angle == 90 || Angle == 180) rect.X -= rect.Width - drawRect.Width; if (Angle == 180) rect.Y -= rect.Height - drawRect.Height; break; case PictureBoxSizeMode.CenterImage: rect.Offset((Width - imageWidth) * scaleX / 2, (Height - imageHeight) * scaleY / 2); rect.Width = imageWidth * scaleX; rect.Height = imageHeight * scaleY; break; case PictureBoxSizeMode.StretchImage: break; case PictureBoxSizeMode.Zoom: /*float kx = drawRect.Width / imageWidth; float ky = drawRect.Height / imageHeight; if (kx < ky) rect.Height = imageHeight * kx; else rect.Width = imageWidth * ky; rect.Offset( (Width * e.ScaleX - rect.Width) / 2, (Height * e.ScaleY - rect.Height) / 2);*/ break; } float gridCompensationX = offsetX + rect.X; gridCompensationX = (int)gridCompensationX - gridCompensationX; float gridCompensationY = offsetY + rect.Y; gridCompensationY = (int)gridCompensationY - gridCompensationY; if (gridCompensationX < 0) gridCompensationX = 1 + gridCompensationX; if (gridCompensationY < 0) gridCompensationY = 1 + gridCompensationY; rect.Offset(gridCompensationX, gridCompensationY); upperLeft = new PointF(0, 0); upperRight = new PointF(rect.Width, 0); lowerLeft = new PointF(0, rect.Height); float angle = Angle; switch (SizeMode) { case PictureBoxSizeMode.Normal: { upperLeft = MovePointOnAngle(drawRect.Location, drawRect.Size, Angle); PointF ur = rotateVector(upperRight, angle); PointF ll = rotateVector(lowerLeft, angle); upperRight = PointF.Add(upperLeft, new SizeF(ur)); lowerLeft = PointF.Add(upperLeft, new SizeF(ll)); } break; case PictureBoxSizeMode.StretchImage: { upperLeft = MovePointOnAngle(drawRect.Location, drawRect.Size, Angle); upperRight = MovePointOnAngle( drawRect.Location, drawRect.Size, Angle + 90); lowerLeft = MovePointOnAngle( drawRect.Location, drawRect.Size, Angle + 270); } break; case PictureBoxSizeMode.CenterImage: { PointF rotatedVector; float w = rect.Left - (drawRect.Left + drawRect.Width / 2); float h = rect.Top - (drawRect.Top + drawRect.Height / 2); rotatedVector = rotateVector(new PointF(w, h), Angle); upperLeft = new PointF(rect.Left + rotatedVector.X - w, rect.Top + rotatedVector.Y - h); rotatedVector = rotateVector(new PointF(rect.Width, 0), Angle); upperRight = new PointF(upperLeft.X + rotatedVector.X, upperLeft.Y + rotatedVector.Y); rotatedVector = rotateVector(new PointF(0, rect.Height), Angle); lowerLeft = new PointF(upperLeft.X + rotatedVector.X, upperLeft.Y + rotatedVector.Y); } break; case PictureBoxSizeMode.AutoSize: case PictureBoxSizeMode.Zoom: { rect = new RectangleF(0, 0, imageWidth * 100f, imageHeight * 100f); PointF center = new PointF(drawRect.Left + drawRect.Width / 2, drawRect.Top + drawRect.Height / 2); PointF[] p = new PointF[4]; p[0] = new PointF(-rect.Width / 2, -rect.Height / 2); p[1] = new PointF(rect.Width / 2, -rect.Height / 2); p[2] = new PointF(rect.Width / 2, rect.Height / 2); p[3] = new PointF(-rect.Width / 2, rect.Height / 2); float scaleToMin = 1; for (int i = 0; i < 4; i++) p[i] = rotateVector(p[i], Angle); for (int i = 0; i < 4; i++) { if (p[i].X * scaleToMin < -drawRect.Width / 2) scaleToMin = -drawRect.Width / 2 / p[i].X; if (p[i].X * scaleToMin > drawRect.Width / 2) scaleToMin = drawRect.Width / 2 / p[i].X; if (p[i].Y * scaleToMin < -drawRect.Height / 2) scaleToMin = -drawRect.Height / 2 / p[i].Y; if (p[i].Y * scaleToMin > drawRect.Height / 2) scaleToMin = drawRect.Height / 2 / p[i].Y; } upperLeft = PointF.Add(center, new SizeF(p[0].X * scaleToMin, p[0].Y * scaleToMin)); upperRight = PointF.Add(center, new SizeF(p[1].X * scaleToMin, p[1].Y * scaleToMin)); lowerLeft = PointF.Add(center, new SizeF(p[3].X * scaleToMin, p[3].Y * scaleToMin)); } break; } if (ImageAlign != ImageAlign.None) UpdateAlign(drawRect, ref upperLeft, ref upperRight, ref lowerLeft); /*switch (Angle) { case 90: upperLeft = new PointF(rect.Right, rect.Top); upperRight = new PointF(rect.Right, rect.Bottom); lowerLeft = new PointF(rect.Left, rect.Top); break; case 180: upperLeft = new PointF(rect.Right, rect.Bottom); upperRight = new PointF(rect.Left, rect.Bottom); lowerLeft = new PointF(rect.Right, rect.Top); break; case 270: upperLeft = new PointF(rect.Left, rect.Bottom); upperRight = new PointF(rect.Left, rect.Top); lowerLeft = new PointF(rect.Right, rect.Bottom); break; default: upperLeft = new PointF(rect.Left, rect.Top); upperRight = new PointF(rect.Right, rect.Top); lowerLeft = new PointF(rect.Left, rect.Bottom); break; }*/ /* default: PointF rotatedVector; float w = rect.Left - (drawRect.Left + drawRect.Width / 2) ; float h = rect.Top - (drawRect.Top + drawRect.Height/2); rotatedVector = rotateVector(new PointF(w, h), Angle); upperLeft = new PointF(rect.Left + rotatedVector.X - w, rect.Top + rotatedVector.Y - h); rotatedVector = rotateVector(new PointF(rect.Width, 0), Angle); upperRight = new PointF(upperLeft.X + rotatedVector.X, upperLeft.Y + rotatedVector.Y); rotatedVector = rotateVector(new PointF(0, rect.Height), Angle); lowerLeft = new PointF(upperLeft.X + rotatedVector.X, upperLeft.Y + rotatedVector.Y); break; }*/ } private void UpdateAlign(RectangleF drawRect, ref PointF upperLeft, ref PointF upperRight, ref PointF lowerLeft) { PointF lowerRight = new PointF(upperRight.X + lowerLeft.X - upperLeft.X, upperRight.Y + lowerLeft.Y - upperLeft.Y); float top = Math.Min(Math.Min(upperLeft.Y, Math.Min(upperRight.Y, lowerLeft.Y)), lowerRight.Y); float botom = Math.Max( Math.Max(upperLeft.Y, Math.Max(upperRight.Y, lowerLeft.Y)), lowerRight.Y); float height = botom - top; float offsetY = drawRect.Y - top; float left = Math.Min(Math.Min(upperLeft.X, Math.Min(upperRight.X, lowerLeft.X)), lowerRight.X); float right = Math.Max(Math.Max(upperLeft.X, Math.Max(upperRight.X, lowerLeft.X)), lowerRight.X); float width = right - left; float offsetX = drawRect.X - left; switch (ImageAlign) { case ImageAlign.Top_Left: break; case ImageAlign.Top_Center: offsetX += (drawRect.Width - width) / 2; break; case ImageAlign.Top_Right: offsetX += drawRect.Width - width; break; case ImageAlign.Center_Left: offsetY += (drawRect.Height - height) / 2; break; case ImageAlign.Center_Center: offsetX += (drawRect.Width - width) / 2; offsetY += (drawRect.Height - height) / 2; break; case ImageAlign.Center_Right: offsetX += drawRect.Width - width; offsetY += (drawRect.Height - height) / 2; break; case ImageAlign.Bottom_Left: offsetY += drawRect.Height - height; break; case ImageAlign.Bottom_Center: offsetX += (drawRect.Width - width) / 2; offsetY += drawRect.Height - height; break; case ImageAlign.Bottom_Right: offsetX += drawRect.Width - width; offsetY += drawRect.Height - height; break; } upperLeft.X += offsetX; upperRight.X += offsetX; lowerLeft.X += offsetX; upperLeft.Y += offsetY; upperRight.Y += offsetY; lowerLeft.Y += offsetY; } /// /// Loads image /// public abstract void LoadImage(); /// /// Moves the point on specified angle /// /// /// /// /// public PointF MovePointOnAngle(PointF p, SizeF size, float fangle) { while (fangle >= 360) fangle -= 360; while (fangle < 0) fangle += 360; float x, y; if (fangle < 90) { x = fangle / 90f * size.Width; y = 0; } else if (fangle < 180) { x = size.Width; y = (fangle - 90f) / 90f * size.Height; } else if (fangle < 270) { x = size.Width - (fangle - 180f) / 90f * size.Width; y = size.Height; } else { x = 0; y = size.Height - (fangle - 270f) / 90f * size.Height; } return PointF.Add(p, new SizeF(x, y)); } /// public override void RestoreState() { base.RestoreState(); // avoid UpdateAutoSize call, use sizeModeInternal sizeModeInternal = saveSizeMode; } /// /// Rotates vector on specified angle /// /// /// /// public PointF rotateVector(PointF p, float fangle) { float angle = (float)(fangle / 180.0 * Math.PI); float ax = p.X; float ay = p.Y; float bx = ax * (float)Math.Cos(angle) - ay * (float)Math.Sin(angle); float by = ax * (float)Math.Sin(angle) + ay * (float)Math.Cos(angle); return new PointF(bx, by); } /// public override void SaveState() { base.SaveState(); saveSizeMode = SizeMode; } public override void Serialize(FRWriter writer) { PictureObjectBase c = writer.DiffObject as PictureObjectBase; base.Serialize(writer); if (writer.SerializeTo != SerializeTo.Preview && writer.SerializeTo != SerializeTo.SourcePages && ImageLocation != c.ImageLocation) writer.WriteStr("ImageLocation", ImageLocation); if (DataColumn != c.DataColumn) writer.WriteStr("DataColumn", DataColumn); if (ImageSourceExpression != c.ImageSourceExpression) writer.WriteStr("ImageSourceExpression", ImageSourceExpression); if (Padding != c.Padding) writer.WriteValue("Padding", Padding); if (SizeMode != c.SizeMode) writer.WriteValue("SizeMode", SizeMode); if (FloatDiff(MaxWidth, c.MaxWidth)) writer.WriteFloat("MaxWidth", MaxWidth); if (FloatDiff(MaxHeight, c.MaxHeight)) writer.WriteFloat("MaxHeight", MaxHeight); if (Angle != c.Angle) writer.WriteInt("Angle", Angle); if (Grayscale != c.Grayscale) writer.WriteBool("Grayscale", Grayscale); if (ShowErrorImage != c.ShowErrorImage) writer.WriteBool("ShowErrorImage", ShowErrorImage); if (ImageAlign != ImageAlign.None) writer.WriteValue("ImageAlign", ImageAlign); } #endregion Public Methods #region Internal Methods /// /// Draws not tiled image /// /// /// /// internal virtual void DrawImageInternal(FRPaintEventArgs e, RectangleF drawRect) { bool rotate = Angle == 90 || Angle == 270; float imageWidth = ImageWidth;//rotate ? Image.Height : Image.Width; float imageHeight = ImageHeight;//rotate ? Image.Width : Image.Height; PointF upperLeft; PointF upperRight; PointF lowerLeft; System.Drawing.Drawing2D.Matrix matrix = e.Graphics.Transform; GetImageAngleTransform(drawRect, imageWidth, imageHeight, e.ScaleX, e.ScaleY, matrix.OffsetX, matrix.OffsetY, out upperLeft, out upperRight, out lowerLeft); DrawImageInternal2(e.Graphics, upperLeft, upperRight, lowerLeft); } #endregion Internal Methods #region Protected Methods protected abstract void DrawImageInternal2(IGraphics graphics, PointF upperLeft, PointF upperRight, PointF lowerLeft); /// /// Reset index of image /// protected abstract void ResetImageIndex(); /// /// When auto size was updated, internal use only /// protected void UpdateAutoSize() { if (SizeMode == PictureBoxSizeMode.AutoSize) { if (ImageWidth == 0 || ImageHeight == 0) { if (IsRunning && !IsPrinting) { Width = 0; Height = 0; } } else { //bool rotate = Angle == 90 || Angle == 270; //Width = (rotate ? Image.Height : Image.Width) + Padding.Horizontal; //Height = (rotate ? Image.Width : Image.Height) + Padding.Vertical; PointF[] p = new PointF[4]; p[0] = new PointF(-ImageWidth / 2, -ImageHeight / 2); p[1] = new PointF(ImageWidth / 2, -ImageHeight / 2); p[2] = new PointF(ImageWidth / 2, ImageHeight / 2); p[3] = new PointF(-ImageWidth / 2, ImageHeight / 2); float minX = float.MaxValue; float maxX = float.MinValue; float minY = float.MaxValue; float maxY = float.MinValue; for (int i = 0; i < 4; i++) { p[i] = rotateVector(p[i], Angle); if (minX > p[i].X) minX = p[i].X; if (maxX < p[i].X) maxX = p[i].X; if (minY > p[i].Y) minY = p[i].Y; if (maxY < p[i].Y) maxY = p[i].Y; } Width = maxX - minX; Height = maxY - minY; // if width/height restrictions are set, use zoom mode to keep aspect ratio if (IsRunning && (MaxWidth != 0 || MaxHeight != 0)) SizeMode = PictureBoxSizeMode.Zoom; } } } #endregion Protected Methods /// public override string[] GetExpressions() { List expressions = new List(); expressions.AddRange(base.GetExpressions()); if (!String.IsNullOrEmpty(DataColumn)) expressions.Add(DataColumn); if (!String.IsNullOrEmpty(ImageSourceExpression)) { if (ImageSourceExpression.StartsWith("[") && ImageSourceExpression.EndsWith("]")) { expressions.Add(ImageSourceExpression.Substring(1, ImageSourceExpression.Length - 2)); } else { expressions.Add(ImageSourceExpression); } } return expressions.ToArray(); } } }