diff --git a/src/Html2OpenXml/Expressions/Image/ImageExpressionBase.cs b/src/Html2OpenXml/Expressions/Image/ImageExpressionBase.cs index 3cf0422..0f77f42 100644 --- a/src/Html2OpenXml/Expressions/Image/ImageExpressionBase.cs +++ b/src/Html2OpenXml/Expressions/Image/ImageExpressionBase.cs @@ -12,7 +12,6 @@ using System.Collections.Generic; using System.Linq; using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using a = DocumentFormat.OpenXml.Drawing; @@ -26,6 +25,12 @@ namespace HtmlToOpenXml.Expressions; /// abstract class ImageExpressionBase(AngleSharp.Dom.IElement node) : HtmlDomExpression { + private readonly RunProperties runProperties = new(); + private readonly ParagraphProperties paraProperties = new(); + // some style attributes, such as borders, will convert this node to a framed container + private bool renderAsFramed; + + /// public override IEnumerable Interpret (ParsingContext context) { @@ -35,16 +40,17 @@ public override IEnumerable Interpret (ParsingContext context) return []; Run run = new(drawing); - Border border = ComposeStyles(); - if (border.Val?.Equals(BorderValues.None) == false) - { - run.RunProperties ??= new(); - run.RunProperties.Border = border; - } + ComposeStyles(); + + if (runProperties.HasChildren) + run.RunProperties = runProperties; + + if (renderAsFramed) + return [new Paragraph(paraProperties, run)]; return [run]; } - private Border ComposeStyles () + private void ComposeStyles () { var styleAttributes = node.GetStyles(); var border = new Border() { Val = BorderValues.None }; @@ -66,7 +72,22 @@ private Border ComposeStyles () border.Size = (uint) borderWidth.ValueInPx * 4; } } - return border; + + if (border.Val?.Equals(BorderValues.None) == false) + { + runProperties.Border = border; + } + + // if the layout is not inline and both left and right are auto, image appears centered + // https://developer.mozilla.org/en-US/docs/Web/CSS/margin-left + var margin = styleAttributes.GetMargin("margin"); + if (margin.Left.Type == UnitMetric.Auto + && margin.Right.Type == UnitMetric.Auto + && !AngleSharpExtensions.IsInlineLayout(styleAttributes["display"])) + { + paraProperties.Justification = new() { Val = JustificationValues.Center }; + renderAsFramed = true; + } } /// diff --git a/src/Html2OpenXml/Utilities/AngleSharpExtensions.cs b/src/Html2OpenXml/Utilities/AngleSharpExtensions.cs index 3d89624..bb397a2 100644 --- a/src/Html2OpenXml/Utilities/AngleSharpExtensions.cs +++ b/src/Html2OpenXml/Utilities/AngleSharpExtensions.cs @@ -153,4 +153,13 @@ public static string CollapseLineBreaks(this string str) return new string(chars, 0, length); } + + /// + /// Determines whether the layout mode is inline vs block or flex. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInlineLayout(string? displayMode) + { + return displayMode?.StartsWith("inline", StringComparison.OrdinalIgnoreCase) == true; + } } \ No newline at end of file diff --git a/src/Html2OpenXml/Utilities/OpenXmlExtensions.cs b/src/Html2OpenXml/Utilities/OpenXmlExtensions.cs index 5242f49..905ba21 100755 --- a/src/Html2OpenXml/Utilities/OpenXmlExtensions.cs +++ b/src/Html2OpenXml/Utilities/OpenXmlExtensions.cs @@ -9,11 +9,9 @@ * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A * PARTICULAR PURPOSE. */ -using System; using System.Runtime.CompilerServices; using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; -using DocumentFormat.OpenXml.Drawing.Wordprocessing; namespace HtmlToOpenXml; diff --git a/test/HtmlToOpenXml.Tests/ImgTests.cs b/test/HtmlToOpenXml.Tests/ImgTests.cs index 15e5596..f335991 100644 --- a/test/HtmlToOpenXml.Tests/ImgTests.cs +++ b/test/HtmlToOpenXml.Tests/ImgTests.cs @@ -219,6 +219,21 @@ public async Task ParseIntoDocumentPart_ReturnsImageParentedToPart (Type openXml AssertThatOpenXmlDocumentIsValid(); } + [TestCase("block", ExpectedResult = true)] + [TestCase("flex", ExpectedResult = true)] + [TestCase("inline", ExpectedResult = false)] + public bool CenterImg_ReturnsFramedImg(string displayMode) + { + var elements = converter.Parse($@""); + + Assert.That(elements, Has.Count.EqualTo(1)); + Assert.That(elements[0], Is.TypeOf()); + AssertIsImg(mainPart, elements[0]); + return elements[0].GetFirstChild()?. + Justification?.Val?.Value == JustificationValues.Center; + } + private static (Drawing, ImagePart) AssertIsImg (OpenXmlPartContainer container, OpenXmlElement paragraph) { var run = paragraph.GetFirstChild();