diff --git a/Examples/ApiExamples/ApiExamples/ApiExamples.csproj b/Examples/ApiExamples/ApiExamples/ApiExamples.csproj index 6fa5fd323..1ccca3b6e 100644 --- a/Examples/ApiExamples/ApiExamples/ApiExamples.csproj +++ b/Examples/ApiExamples/ApiExamples/ApiExamples.csproj @@ -125,9 +125,9 @@ - - - + + + diff --git a/Examples/ApiExamples/ApiExamples/ExXpsSaveOptions.cs b/Examples/ApiExamples/ApiExamples/ExXpsSaveOptions.cs index f96cf2fd9..17cd32dcd 100644 --- a/Examples/ApiExamples/ApiExamples/ExXpsSaveOptions.cs +++ b/Examples/ApiExamples/ApiExamples/ExXpsSaveOptions.cs @@ -170,9 +170,11 @@ public void XpsDigitalSignature() Document doc = new Document(MyDir + "Document.docx"); CertificateHolder certificateHolder = CertificateHolder.Create(MyDir + "morzal.pfx", "aw"); - DigitalSignatureDetails digitalSignatureDetails = new DigitalSignatureDetails( - certificateHolder, - new SignOptions() { Comments = "Some comments", SignTime = DateTime.Now }); + SignOptions options = new SignOptions(); + options.SignTime = DateTime.Now; + options.Comments = "Some comments"; + + DigitalSignatureDetails digitalSignatureDetails = new DigitalSignatureDetails(certificateHolder, options); XpsSaveOptions saveOptions = new XpsSaveOptions(); saveOptions.DigitalSignatureDetails = digitalSignatureDetails; diff --git a/Examples/ApiExamples/Runner.MAUI/Runner.MAUI.csproj b/Examples/ApiExamples/Runner.MAUI/Runner.MAUI.csproj index 7b30d22a7..f5e48b519 100644 --- a/Examples/ApiExamples/Runner.MAUI/Runner.MAUI.csproj +++ b/Examples/ApiExamples/Runner.MAUI/Runner.MAUI.csproj @@ -49,20 +49,20 @@ - - - - + + + + - + - + - + diff --git a/Examples/DocsExamples/Docker/Docker.csproj b/Examples/DocsExamples/Docker/Docker.csproj index 3bcedcee0..8a13643d7 100644 --- a/Examples/DocsExamples/Docker/Docker.csproj +++ b/Examples/DocsExamples/Docker/Docker.csproj @@ -6,7 +6,7 @@ - + diff --git a/Examples/DocsExamples/DocsExamples/DocsExamples.csproj b/Examples/DocsExamples/DocsExamples/DocsExamples.csproj index 2caba3a8f..243afc8b2 100644 --- a/Examples/DocsExamples/DocsExamples/DocsExamples.csproj +++ b/Examples/DocsExamples/DocsExamples/DocsExamples.csproj @@ -116,11 +116,11 @@ - - - - - + + + + + diff --git a/Examples/DocsExamples/DocsExamples/Mail Merge and Reporting/Base operations.cs b/Examples/DocsExamples/DocsExamples/Mail Merge and Reporting/Base operations.cs index 50d5ffe32..36dab756d 100644 --- a/Examples/DocsExamples/DocsExamples/Mail Merge and Reporting/Base operations.cs +++ b/Examples/DocsExamples/DocsExamples/Mail Merge and Reporting/Base operations.cs @@ -41,8 +41,6 @@ public void UseIfElseMustache() //ExStart:UseIfElseMustache //GistId:544788f602e697802e313a641cedb9b8 Document doc = new Document(MyDir + "Mail merge destinations - Mustache syntax.docx"); - doc.Sections.Clear(); - doc.Save(ArtifactsDir + "output.docx"); doc.MailMerge.UseNonMergeFields = true; doc.MailMerge.Execute(new[] { "GENDER" }, new object[] { "MALE" }); diff --git a/Examples/DocsExamples/DocsExamples/Programming with Documents/Working with Graphic Elements/CustomBarcodeGenerator.cs b/Examples/DocsExamples/DocsExamples/Programming with Documents/Working with Graphic Elements/CustomBarcodeGenerator.cs index eac134d7d..0afe2cb6f 100644 --- a/Examples/DocsExamples/DocsExamples/Programming with Documents/Working with Graphic Elements/CustomBarcodeGenerator.cs +++ b/Examples/DocsExamples/DocsExamples/Programming with Documents/Working with Graphic Elements/CustomBarcodeGenerator.cs @@ -1,240 +1,315 @@ +// Copyright (c) 2001-2024 Aspose Pty Ltd. All Rights Reserved. +// +// This file is part of Aspose.Words. The source code in this file +// is only intended as a supplement to the documentation, and is provided +// "as is", without warranty of any kind, either expressed or implied. +////////////////////////////////////////////////////////////////////////// + using System; -using System.Drawing; -using System.Globalization; -using Aspose.BarCode.Generation; +using System.IO; using Aspose.Words.Fields; -using BarcodeParameters = Aspose.Words.Fields.BarcodeParameters; -#if NET6_0 || __MOBILE__ -using Image = SkiaSharp.SKBitmap; -using Color = Aspose.Drawing.Color; +using Aspose.BarCode.Generation; +#if NET5_0_OR_GREATER +using SkiaSharp; +using Aspose.Drawing; +using Aspose.Drawing.Imaging; +#else +using System.Drawing; +using System.Drawing.Imaging; #endif -namespace DocsExamples.Programming_with_Documents.Working_with_Graphic_Elements +namespace DocsExamples { - //ExStart:CustomBarcodeGenerator - //GistId:00d34dba66626dbc0175b60bb3b71c8a - public class CustomBarcodeGenerator : DocsExamplesBase, IBarcodeGenerator + internal class CustomBarcodeGeneratorUtils { /// - /// Converts barcode image height from Word units to Aspose.BarCode units. + /// Converts a height value in twips to pixels using a default DPI of 96. /// - /// - /// - private static float ConvertSymbolHeight(string heightInTwipsString) + /// The height value in twips. + /// The default value to return if the conversion fails. + /// The height value in pixels. + public static double TwipsToPixels(string heightInTwips, double defVal) { - // Input value is in 1/1440 inches (twips). - int heightInTwips = TryParseInt(heightInTwipsString); - - if (heightInTwips == int.MinValue) - throw new Exception("Error! Incorrect height - " + heightInTwipsString + "."); - - // Convert to mm. - return (float) (heightInTwips * 25.4 / 1440); + return TwipsToPixels(heightInTwips, 96, defVal); } /// - /// Converts barcode image color from Word to Aspose.BarCode. + /// Converts a height value in twips to pixels based on the given resolution. /// - /// - /// - private static Color ConvertColor(string inputColor) + /// The height value in twips to be converted. + /// The resolution in pixels per inch. + /// The default value to be returned if the conversion fails. + /// The converted height value in pixels. + public static double TwipsToPixels(string heightInTwips, double resolution, double defVal) { - // Input should be from "0x000000" to "0xFFFFFF". - int color = TryParseHex(inputColor.Replace("0x", "")); - - if (color == int.MinValue) - throw new Exception("Error! Incorrect color - " + inputColor + "."); - - return Color.FromArgb(color >> 16, (color & 0xFF00) >> 8, color & 0xFF); - - // Backward conversion - - // return string.Format("0x{0,6:X6}", mControl.ForeColor.ToArgb() & 0xFFFFFF); + try + { + int lVal = int.Parse(heightInTwips); + return (lVal / 1440.0) * resolution; + } + catch + { + return defVal; + } } /// - /// Converts bar code scaling factor from percent to float. + /// Gets the rotation angle in degrees based on the given rotation angle string. /// - /// - /// - private static float ConvertScalingFactor(string scalingFactor) + /// The rotation angle string. + /// The default value to return if the rotation angle is not recognized. + /// The rotation angle in degrees. + public static float GetRotationAngle(string rotationAngle, float defVal) { - bool isParsed = false; - int percent = TryParseInt(scalingFactor); - - if (percent != int.MinValue && percent >= 10 && percent <= 10000) - isParsed = true; - - if (!isParsed) - throw new Exception("Error! Incorrect scaling factor - " + scalingFactor + "."); - - return percent / 100.0f; + switch (rotationAngle) + { + case "0": + return 0; + case "1": + return 270; + case "2": + return 180; + case "3": + return 90; + default: + return defVal; + } } /// - /// Implementation of the GetBarCodeImage() method for IBarCodeGenerator interface. + /// Converts a string representation of an error correction level to a QRErrorLevel enum value. /// - /// - /// - public Image GetBarcodeImage(BarcodeParameters parameters) + /// The string representation of the error correction level. + /// The default error correction level to return if the input is invalid. + /// The corresponding QRErrorLevel enum value. + public static QRErrorLevel GetQRCorrectionLevel(string errorCorrectionLevel, QRErrorLevel def) { - if (parameters.BarcodeType == null || parameters.BarcodeValue == null) - return null; - - BarcodeGenerator generator = new BarcodeGenerator(EncodeTypes.QR); - - string type = parameters.BarcodeType.ToUpper(); + switch (errorCorrectionLevel) + { + case "0": + return QRErrorLevel.LevelL; + case "1": + return QRErrorLevel.LevelM; + case "2": + return QRErrorLevel.LevelQ; + case "3": + return QRErrorLevel.LevelH; + default: + return def; + } + } - switch (type) + /// + /// Gets the barcode encode type based on the given encode type from Word. + /// + /// The encode type from Word. + /// The barcode encode type. + public static SymbologyEncodeType GetBarcodeEncodeType(string encodeTypeFromWord) + { + // https://support.microsoft.com/en-au/office/field-codes-displaybarcode-6d81eade-762d-4b44-ae81-f9d3d9e07be3 + switch (encodeTypeFromWord) { case "QR": - generator = new BarcodeGenerator(EncodeTypes.QR); - break; + return EncodeTypes.QR; case "CODE128": - generator = new BarcodeGenerator(EncodeTypes.Code128); - break; + return EncodeTypes.Code128; case "CODE39": - generator = new BarcodeGenerator(EncodeTypes.Code39Standard); - break; + return EncodeTypes.Code39; + case "JPPOST": + return EncodeTypes.RM4SCC; case "EAN8": - generator = new BarcodeGenerator(EncodeTypes.EAN8); - break; + case "JAN8": + return EncodeTypes.EAN8; case "EAN13": - generator = new BarcodeGenerator(EncodeTypes.EAN13); - break; + case "JAN13": + return EncodeTypes.EAN13; case "UPCA": - generator = new BarcodeGenerator(EncodeTypes.UPCA); - break; + return EncodeTypes.UPCA; case "UPCE": - generator = new BarcodeGenerator(EncodeTypes.UPCE); - break; - case "ITF14": - generator = new BarcodeGenerator(EncodeTypes.ITF14); - break; + return EncodeTypes.UPCE; case "CASE": - generator = new BarcodeGenerator(EncodeTypes.None); - break; + case "ITF14": + return EncodeTypes.ITF14; + case "NW7": + return EncodeTypes.Codabar; + default: + return EncodeTypes.None; } + } - if (generator.BarcodeType.Equals(EncodeTypes.None)) - return null; - - generator.CodeText = parameters.BarcodeValue; - - if (generator.BarcodeType.Equals(EncodeTypes.QR)) - generator.Parameters.Barcode.CodeTextParameters.TwoDDisplayText = parameters.BarcodeValue; - - if (parameters.ForegroundColor != null) - generator.Parameters.Barcode.BarColor = ConvertColor(parameters.ForegroundColor); - - if (parameters.BackgroundColor != null) - generator.Parameters.BackColor = ConvertColor(parameters.BackgroundColor); - - if (parameters.SymbolHeight != null) + /// + /// Converts a hexadecimal color string to a Color object. + /// + /// The hexadecimal color string to convert. + /// The default Color value to return if the conversion fails. + /// The Color object representing the converted color, or the default value if the conversion fails. + public static Color ConvertColor(string inputColor, Color defVal) + { + if (string.IsNullOrEmpty(inputColor)) return defVal; + try { - generator.Parameters.ImageHeight.Pixels = ConvertSymbolHeight(parameters.SymbolHeight); - generator.Parameters.AutoSizeMode = AutoSizeMode.None; + int color = Convert.ToInt32(inputColor, 16); + // Return Color.FromArgb((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF); + return Color.FromArgb(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF); } - - generator.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.None; - - if (parameters.DisplayText) - generator.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.Below; - - generator.Parameters.CaptionAbove.Text = ""; - - // Empiric scaling factor for converting Word barcode to Aspose.BarCode. - const float scale = 2.4f; - float xdim = 1.0f; - - if (generator.BarcodeType.Equals(EncodeTypes.QR)) + catch { - generator.Parameters.AutoSizeMode = AutoSizeMode.Nearest; - generator.Parameters.ImageWidth.Inches *= scale; - generator.Parameters.ImageHeight.Inches = generator.Parameters.ImageWidth.Inches; - xdim = generator.Parameters.ImageHeight.Inches / 25; - generator.Parameters.Barcode.XDimension.Inches = - generator.Parameters.Barcode.BarHeight.Inches = xdim; + return defVal; } + } - if (parameters.ScalingFactor != null) + /// + /// Calculates the scale factor based on the provided string representation. + /// + /// The string representation of the scale factor. + /// The default value to return if the scale factor cannot be parsed. + /// + /// The scale factor as a decimal value between 0 and 1, or the default value if the scale factor cannot be parsed. + /// + public static double ScaleFactor(string scaleFactor, double defVal) + { + try { - float scalingFactor = ConvertScalingFactor(parameters.ScalingFactor); - generator.Parameters.ImageHeight.Inches *= scalingFactor; - - if (generator.BarcodeType.Equals(EncodeTypes.QR)) - { - generator.Parameters.ImageWidth.Inches = generator.Parameters.ImageHeight.Inches; - generator.Parameters.Barcode.XDimension.Inches = - generator.Parameters.Barcode.BarHeight.Inches = xdim * scalingFactor; - } - - generator.Parameters.AutoSizeMode = AutoSizeMode.None; + int scale = int.Parse(scaleFactor); + return scale / 100.0; + } + catch + { + return defVal; } - -#if NET48 || JAVA - return generator.GenerateBarCodeImage(); - -#elif NET6_0 || __MOBILE__ - generator.GenerateBarCodeImage().Save(ArtifactsDir + "GetBarcodeImage.png"); - return Image.Decode(ArtifactsDir + "GetBarcodeImage.png"); -#endif } /// - /// Implementation of the GetOldBarcodeImage() method for IBarCodeGenerator interface. + /// Sets the position code style for a barcode generator. /// - /// - /// - public Image GetOldBarcodeImage(BarcodeParameters parameters) + /// The barcode generator. + /// The position code style to set. + /// The barcode value. + public static void SetPosCodeStyle(BarcodeGenerator gen, string posCodeStyle, string barcodeValue) { - if (parameters.PostalAddress == null) - return null; - - BarcodeGenerator generator = new BarcodeGenerator(EncodeTypes.Postnet) + switch (posCodeStyle) { - CodeText = parameters.PostalAddress - }; - -#if NET48 || JAVA - return generator.GenerateBarCodeImage(); -#elif NET6_0 || __MOBILE__ - generator.GenerateBarCodeImage().Save(ArtifactsDir + "OldBarcodeImage.png"); - return Image.Decode(ArtifactsDir + "OldBarcodeImage.png"); -#endif + // STD default and without changes. + case "SUP2": + gen.CodeText = barcodeValue.Substring(0, barcodeValue.Length - 2); + gen.Parameters.Barcode.Supplement.SupplementData = barcodeValue.Substring(barcodeValue.Length - 2, 2); + break; + case "SUP5": + gen.CodeText = barcodeValue.Substring(0, barcodeValue.Length - 5); + gen.Parameters.Barcode.Supplement.SupplementData = barcodeValue.Substring(barcodeValue.Length - 5, 5); + break; + case "CASE": + gen.Parameters.Border.Visible = true; + gen.Parameters.Border.Color = gen.Parameters.Barcode.BarColor; + gen.Parameters.Border.DashStyle = BorderDashStyle.Solid; + gen.Parameters.Border.Width.Pixels = gen.Parameters.Barcode.XDimension.Pixels * 5; + break; + } } + public const double DefaultQRXDimensionInPixels = 4.0; + public const double Default1DXDimensionInPixels = 1.0; + /// - /// Parses an integer using the invariant culture. Returns Int.MinValue if cannot parse. - /// - /// Allows leading sign. - /// Allows leading and trailing spaces. + /// Draws an error image with the specified exception message. /// - public static int TryParseInt(string s) + /// The exception containing the error message. + /// A Bitmap object representing the error image. + public static Bitmap DrawErrorImage(Exception error) { - return double.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out double temp) - ? CastDoubleToInt(temp) - : int.MinValue; + Bitmap bmp = new Bitmap(100, 100); + + using (Graphics grf = Graphics.FromImage(bmp)) +#if NET5_0_OR_GREATER + grf.DrawString(error.Message, new Aspose.Drawing.Font("Microsoft Sans Serif", 8f, FontStyle.Regular), Brushes.Red, new Rectangle(0, 0, bmp.Width, bmp.Height)); +#else + grf.DrawString(error.Message, new System.Drawing.Font("Microsoft Sans Serif", 8f, FontStyle.Regular), Brushes.Red, new Rectangle(0, 0, bmp.Width, bmp.Height)); +#endif + return bmp; } - /// - /// Casts a double to int32 in a way that uint32 are "correctly" casted too (they become negative numbers). - /// - public static int CastDoubleToInt(double value) +#if NET5_0_OR_GREATER + public static SKBitmap ConvertImageToWord(Bitmap bmp) { - long temp = (long) value; - return (int) temp; + MemoryStream ms = new MemoryStream(); + bmp.Save(ms, ImageFormat.Png); + ms.Position = 0; + + return SKBitmap.Decode(ms); + } +#else + public static Image ConvertImageToWord(Bitmap bmp) + { + return bmp; } +#endif + } - /// - /// Try parses a hex String into an integer value. - /// on error return int.MinValue - /// - public static int TryParseHex(string s) + internal class CustomBarcodeGenerator : IBarcodeGenerator + { +#if NET5_0_OR_GREATER + public SKBitmap GetBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters) +#else + public Image GetBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters) +#endif + { + try + { + BarcodeGenerator gen = new BarcodeGenerator(CustomBarcodeGeneratorUtils.GetBarcodeEncodeType(parameters.BarcodeType), parameters.BarcodeValue); + + // Set color. + gen.Parameters.Barcode.BarColor = CustomBarcodeGeneratorUtils.ConvertColor(parameters.ForegroundColor, gen.Parameters.Barcode.BarColor); + gen.Parameters.BackColor = CustomBarcodeGeneratorUtils.ConvertColor(parameters.BackgroundColor, gen.Parameters.BackColor); + + // Set display or hide text. + if (!parameters.DisplayText) + gen.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.None; + else + gen.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.Below; + + // Set QR Code error correction level.s + gen.Parameters.Barcode.QR.QrErrorLevel = QRErrorLevel.LevelH; + if (!string.IsNullOrEmpty(parameters.ErrorCorrectionLevel)) + gen.Parameters.Barcode.QR.QrErrorLevel = CustomBarcodeGeneratorUtils.GetQRCorrectionLevel(parameters.ErrorCorrectionLevel, gen.Parameters.Barcode.QR.QrErrorLevel); + + // Set rotation angle. + if (!string.IsNullOrEmpty(parameters.SymbolRotation)) + gen.Parameters.RotationAngle = CustomBarcodeGeneratorUtils.GetRotationAngle(parameters.SymbolRotation, gen.Parameters.RotationAngle); + + // Set scaling factor. + double scalingFactor = 1; + if (!string.IsNullOrEmpty(parameters.ScalingFactor)) + scalingFactor = CustomBarcodeGeneratorUtils.ScaleFactor(parameters.ScalingFactor, scalingFactor); + + // Set size. + if (gen.BarcodeType == EncodeTypes.QR) + gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.DefaultQRXDimensionInPixels * scalingFactor)); + else + gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.Default1DXDimensionInPixels * scalingFactor)); + + //Set height. + if (!string.IsNullOrEmpty(parameters.SymbolHeight)) + gen.Parameters.Barcode.BarHeight.Pixels = (float)Math.Max(5.0, Math.Round(CustomBarcodeGeneratorUtils.TwipsToPixels(parameters.SymbolHeight, gen.Parameters.Barcode.BarHeight.Pixels) * scalingFactor)); + + // Set style of a Point-of-Sale barcode. + if (!string.IsNullOrEmpty(parameters.PosCodeStyle)) + CustomBarcodeGeneratorUtils.SetPosCodeStyle(gen, parameters.PosCodeStyle, parameters.BarcodeValue); + + return CustomBarcodeGeneratorUtils.ConvertImageToWord(gen.GenerateBarCodeImage()); + } + catch (Exception e) + { + return CustomBarcodeGeneratorUtils.ConvertImageToWord(CustomBarcodeGeneratorUtils.DrawErrorImage(e)); + } + } + +#if NET5_0_OR_GREATER + public SKBitmap GetOldBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters) +#else + public Image GetOldBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters) +#endif { - return int.TryParse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int result) - ? result - : int.MinValue; + throw new NotImplementedException(); } } - //ExEnd:CustomBarcodeGenerator } diff --git a/Examples/DocsExamples/DocsExamples/Rendering and Printing/Rendering shapes.cs b/Examples/DocsExamples/DocsExamples/Rendering and Printing/Rendering shapes.cs index c4ca7480e..8199c5b9f 100644 --- a/Examples/DocsExamples/DocsExamples/Rendering and Printing/Rendering shapes.cs +++ b/Examples/DocsExamples/DocsExamples/Rendering and Printing/Rendering shapes.cs @@ -1,6 +1,4 @@ -#if NET48 || JAVA -using System.Drawing; -using System.Drawing.Imaging; +using System.Drawing; using Aspose.Words; using Aspose.Words.Drawing; using Aspose.Words.Rendering; @@ -8,6 +6,8 @@ using Aspose.Words.Tables; using NUnit.Framework; using System.IO; +using Aspose.Words.Layout; +using System.Drawing.Imaging; namespace DocsExamples.Rendering_and_Printing { @@ -17,7 +17,6 @@ internal class RenderingShapes : DocsExamplesBase public void RenderShapeAsEmf() { Document doc = new Document(MyDir + "Rendering.docx"); - // Retrieve the target shape from the document. Shape shape = (Shape) doc.GetChild(NodeType.Shape, 0, true); @@ -43,12 +42,10 @@ public void RenderShapeAsJpeg() //ExStart:RenderShapeAsJpeg //GistId:7fc867ac8ef1b729b6f70580fbc5b3f9 ShapeRenderer render = new ShapeRenderer(shape); - ImageSaveOptions imageOptions = new ImageSaveOptions(SaveFormat.Jpeg) { // Output the image in gray scale ImageColorMode = ImageColorMode.Grayscale, - // Reduce the brightness a bit (default is 0.5f) ImageBrightness = 0.45f }; @@ -58,8 +55,8 @@ public void RenderShapeAsJpeg() render.Save(stream, imageOptions); } //ExEnd:RenderShapeAsJpeg - } - + } +#if NET48 || JAVA [Test] //ExStart:RenderShapeToGraphics //GistId:7fc867ac8ef1b729b6f70580fbc5b3f9 @@ -68,12 +65,10 @@ public void RenderShapeToGraphics() Document doc = new Document(MyDir + "Rendering.docx"); Shape shape = (Shape) doc.GetChild(NodeType.Shape, 0, true); - ShapeRenderer render = shape.GetShapeRenderer(); // Find the size that the shape will be rendered to at the specified scale and resolution. Size shapeSizeInPixels = render.GetSizeInPixels(1.0f, 96.0f); - // Rotating the shape may result in clipping as the image canvas is too small. Find the longest side // and make sure that the graphics canvas is large enough to compensate for this. int maxSide = System.Math.Max(shapeSizeInPixels.Width, shapeSizeInPixels.Height); @@ -102,6 +97,29 @@ public void RenderShapeToGraphics() } //ExEnd:RenderShapeToGraphics + [Test] + public void FindShapeSizes() + { + Document doc = new Document(MyDir + "Rendering.docx"); + + Shape shape = (Shape)doc.GetChild(NodeType.Shape, 0, true); + + //ExStart:FindShapeSizes + //GistId:7fc867ac8ef1b729b6f70580fbc5b3f9 + Size shapeRenderedSize = shape.GetShapeRenderer().GetSizeInPixels(1.0f, 96.0f); + + using (Bitmap image = new Bitmap(shapeRenderedSize.Width, shapeRenderedSize.Height)) + { + using (Graphics graphics = Graphics.FromImage(image)) + { + // Render shape onto the graphics object using the RenderToScale + // or RenderToSize methods of ShapeRenderer class. + } + } + //ExEnd:FindShapeSizes + } +#endif + [Test] public void RenderCellToImage() { @@ -109,7 +127,8 @@ public void RenderCellToImage() //ExStart:RenderCellToImage Cell cell = (Cell)doc.GetChild(NodeType.Cell, 2, true); - RenderNode(cell, ArtifactsDir + "RenderShape.RenderCellToImage.png", null); + Document tmp = ConvertToImage(doc, cell); + tmp.Save(ArtifactsDir + "RenderShape.RenderCellToImage.png"); //ExEnd:RenderCellToImage } @@ -120,7 +139,8 @@ public void RenderRowToImage() //ExStart:RenderRowToImage Row row = (Row) doc.GetChild(NodeType.Row, 0, true); - RenderNode(row, ArtifactsDir + "RenderShape.RenderRowToImage.png", null); + Document tmp = ConvertToImage(doc, row); + tmp.Save(ArtifactsDir + "RenderShape.RenderRowToImage.png"); //ExEnd:RenderRowToImage } @@ -141,32 +161,11 @@ public void RenderParagraphToImage() PaperColor = Color.LightPink }; - RenderNode(textBoxShape.LastParagraph, ArtifactsDir + "RenderShape.RenderParagraphToImage.png", options); + Document tmp = ConvertToImage(doc, textBoxShape.LastParagraph); + tmp.Save(ArtifactsDir + "RenderShape.RenderParagraphToImage.png"); //ExEnd:RenderParagraphToImage } - [Test] - public void FindShapeSizes() - { - Document doc = new Document(MyDir + "Rendering.docx"); - - Shape shape = (Shape) doc.GetChild(NodeType.Shape, 0, true); - - //ExStart:FindShapeSizes - //GistId:7fc867ac8ef1b729b6f70580fbc5b3f9 - Size shapeRenderedSize = shape.GetShapeRenderer().GetSizeInPixels(1.0f, 96.0f); - - using (Bitmap image = new Bitmap(shapeRenderedSize.Width, shapeRenderedSize.Height)) - { - using (Graphics graphics = Graphics.FromImage(image)) - { - // Render shape onto the graphics object using the RenderToScale - // or RenderToSize methods of ShapeRenderer class. - } - } - //ExEnd:FindShapeSizes - } - [Test] public void RenderShapeImage() { @@ -180,114 +179,90 @@ public void RenderShapeImage() } /// - /// Renders any node in a document to the path specified using the image save options. + /// Renders any node in a document into an image. /// + /// The current document. /// The node to render. - /// The path to save the rendered image to. - /// The image options to use during rendering. This can be null. - public void RenderNode(Node node, string filePath, ImageSaveOptions imageOptions) + private static Document ConvertToImage(Document doc, CompositeNode node) { - if (imageOptions == null) - imageOptions = new ImageSaveOptions(FileFormatUtil.ExtensionToSaveFormat(Path.GetExtension(filePath))); - - // Store the paper color to be used on the final image and change to transparent. - // This will cause any content around the rendered node to be removed later on. - Color savePaperColor = imageOptions.PaperColor; - imageOptions.PaperColor = Color.Transparent; - - // There a bug which affects the cache of a cloned node. - // To avoid this, we clone the entire document, including all nodes, - // finding the matching node in the cloned document and rendering that instead. - Document doc = (Document) node.Document.Clone(true); - node = doc.GetChild(NodeType.Any, node.Document.GetChildNodes(NodeType.Any, true).IndexOf(node), true); - - // Create a temporary shape to store the target node in. This shape will be rendered to retrieve - // the rendered content of the node. - Shape shape = new Shape(doc, ShapeType.TextBox); - Section parentSection = (Section) node.GetAncestor(NodeType.Section); - - // Assume that the node cannot be larger than the page in size. - shape.Width = parentSection.PageSetup.PageWidth; - shape.Height = parentSection.PageSetup.PageHeight; - shape.FillColor = Color.Transparent; - - // Don't draw a surronding line on the shape. - shape.Stroked = false; - - // Move up through the DOM until we find a suitable node to insert into a Shape - // (a node with a parent can contain paragraphs, tables the same as a shape). Each parent node is cloned - // on the way up so even a descendant node passed to this method can be rendered. Since we are working - // with the actual nodes of the document we need to clone the target node into the temporary shape. - Node currentNode = node; - while (!(currentNode.ParentNode is InlineStory || currentNode.ParentNode is Story || - currentNode.ParentNode is ShapeBase)) - { - CompositeNode parent = (CompositeNode) currentNode.ParentNode.Clone(false); - currentNode = currentNode.ParentNode; - parent.AppendChild(node.Clone(true)); - node = parent; // Store this new node to be inserted into the shape. - } + Document tmp = CreateTemporaryDocument(doc, node); + AppendNodeContent(tmp, node); + AdjustDocumentLayout(tmp); + return tmp; + } - // We must add the shape to the document tree to have it rendered. - shape.AppendChild(node.Clone(true)); - parentSection.Body.FirstParagraph.AppendChild(shape); + /// + /// Creates a temporary document for further rendering. + /// + private static Document CreateTemporaryDocument(Document doc, CompositeNode node) + { + Document tmp = (Document)doc.Clone(false); + tmp.Sections.Add(tmp.ImportNode(node.GetAncestor(NodeType.Section), false, ImportFormatMode.UseDestinationStyles)); + tmp.FirstSection.AppendChild(new Body(tmp)); + tmp.FirstSection.PageSetup.TopMargin = 0; + tmp.FirstSection.PageSetup.BottomMargin = 0; - // Render the shape to stream so we can take advantage of the effects of the ImageSaveOptions class. - // Retrieve the rendered image and remove the shape from the document. - MemoryStream stream = new MemoryStream(); - ShapeRenderer renderer = shape.GetShapeRenderer(); - renderer.Save(stream, imageOptions); - shape.Remove(); + return tmp; + } - Rectangle crop = renderer.GetOpaqueBoundsInPixels(imageOptions.Scale, imageOptions.HorizontalResolution, - imageOptions.VerticalResolution); + /// + /// Adds a node to a temporary document. + /// + private static void AppendNodeContent(Document tmp, CompositeNode node) + { + if (node is HeaderFooter headerFooter) + foreach (Node hfNode in headerFooter.GetChildNodes(NodeType.Any, false)) + tmp.FirstSection.Body.AppendChild(tmp.ImportNode(hfNode, true, ImportFormatMode.UseDestinationStyles)); + else + AppendNonHeaderFooterContent(tmp, node); + } - using (Bitmap renderedImage = new Bitmap(stream)) + private static void AppendNonHeaderFooterContent(Document tmp, CompositeNode node) + { + Node parentNode = node.ParentNode; + while (!(parentNode is InlineStory || parentNode is Story || parentNode is ShapeBase)) { - Bitmap croppedImage = new Bitmap(crop.Width, crop.Height); - croppedImage.SetResolution(imageOptions.HorizontalResolution, imageOptions.VerticalResolution); - - // Create the final image with the proper background color. - using (Graphics g = Graphics.FromImage(croppedImage)) - { - g.Clear(savePaperColor); - g.DrawImage(renderedImage, new Rectangle(0, 0, croppedImage.Width, croppedImage.Height), crop.X, - crop.Y, crop.Width, crop.Height, GraphicsUnit.Pixel); + CompositeNode parent = (CompositeNode)parentNode.Clone(false); + parent.AppendChild(node.Clone(true)); + node = parent; - croppedImage.Save(filePath); - } + parentNode = parentNode.ParentNode; } + + tmp.FirstSection.Body.AppendChild(tmp.ImportNode(node, true, ImportFormatMode.UseDestinationStyles)); } /// - /// Finds the minimum bounding box around non-transparent pixels in a Bitmap. + /// Adjusts the layout of the document to fit the content area. /// - public Rectangle FindBoundingBoxAroundNode(Bitmap originalBitmap) + private static void AdjustDocumentLayout(Document tmp) { - Point min = new Point(int.MaxValue, int.MaxValue); - Point max = new Point(int.MinValue, int.MinValue); + LayoutEnumerator enumerator = new LayoutEnumerator(tmp); + RectangleF rect = RectangleF.Empty; + rect = CalculateVisibleRect(enumerator, rect); + + tmp.FirstSection.PageSetup.PageHeight = rect.Height; + tmp.UpdatePageLayout(); + } - for (int x = 0; x < originalBitmap.Width; ++x) + /// + /// Calculates the visible area of the content. + /// + private static RectangleF CalculateVisibleRect(LayoutEnumerator enumerator, RectangleF rect) + { + RectangleF result = rect; + do { - for (int y = 0; y < originalBitmap.Height; ++y) + if (enumerator.MoveFirstChild()) { - // Note that you can speed up this part of the algorithm using LockBits and unsafe code instead of GetPixel. - Color pixelColor = originalBitmap.GetPixel(x, y); - - // For each pixel that is not transparent, calculate the bounding box around it. - if (pixelColor.ToArgb() != Color.Empty.ToArgb()) - { - min.X = System.Math.Min(x, min.X); - min.Y = System.Math.Min(y, min.Y); - max.X = System.Math.Max(x, max.X); - max.Y = System.Math.Max(y, max.Y); - } + if (enumerator.Type == LayoutEntityType.Line || enumerator.Type == LayoutEntityType.Span) + result = result.IsEmpty ? enumerator.Rectangle : RectangleF.Union(result, enumerator.Rectangle); + result = CalculateVisibleRect(enumerator, result); + enumerator.MoveParent(); } - } + } while (enumerator.MoveNext()); - // Add one pixel to the width and height to avoid clipping. - return new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); + return result; } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Examples/DocsExamples/DocumentExplorer/DocumentExplorer.csproj b/Examples/DocsExamples/DocumentExplorer/DocumentExplorer.csproj index 531a4346f..b9b2290af 100644 --- a/Examples/DocsExamples/DocumentExplorer/DocumentExplorer.csproj +++ b/Examples/DocsExamples/DocumentExplorer/DocumentExplorer.csproj @@ -141,7 +141,7 @@ - 24.4.0 + 24.8.0