Skip to content

Commit

Permalink
Support center image with margin auto #171
Browse files Browse the repository at this point in the history
  • Loading branch information
onizet committed Nov 4, 2024
1 parent e6ecabf commit 78f026c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 11 deletions.
39 changes: 30 additions & 9 deletions src/Html2OpenXml/Expressions/Image/ImageExpressionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +25,12 @@ namespace HtmlToOpenXml.Expressions;
/// </summary>
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;


/// <inheritdoc/>
public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
{
Expand All @@ -35,16 +40,17 @@ public override IEnumerable<OpenXmlElement> 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 };
Expand All @@ -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;
}
}

/// <summary>
Expand Down
9 changes: 9 additions & 0 deletions src/Html2OpenXml/Utilities/AngleSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,13 @@ public static string CollapseLineBreaks(this string str)

return new string(chars, 0, length);
}

/// <summary>
/// Determines whether the layout mode is inline vs block or flex.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInlineLayout(string? displayMode)
{
return displayMode?.StartsWith("inline", StringComparison.OrdinalIgnoreCase) == true;
}
}
2 changes: 0 additions & 2 deletions src/Html2OpenXml/Utilities/OpenXmlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
15 changes: 15 additions & 0 deletions test/HtmlToOpenXml.Tests/ImgTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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($@"<img style=""display: {displayMode}; margin-left: auto; margin-right: auto;""
src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="" width=""32"" height=""32"">");

Assert.That(elements, Has.Count.EqualTo(1));
Assert.That(elements[0], Is.TypeOf<Paragraph>());
AssertIsImg(mainPart, elements[0]);
return elements[0].GetFirstChild<ParagraphProperties>()?.
Justification?.Val?.Value == JustificationValues.Center;
}

private static (Drawing, ImagePart) AssertIsImg (OpenXmlPartContainer container, OpenXmlElement paragraph)
{
var run = paragraph.GetFirstChild<Run>();
Expand Down

2 comments on commit 78f026c

@lofcz
Copy link

@lofcz lofcz commented on 78f026c Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this work with just margin: auto; or margin: auto auto;?

@onizet
Copy link
Owner Author

@onizet onizet commented on 78f026c Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work for: margin: auto, margin: auto auto, margin-left: auto; margin-right: auto and margin: 10px auto or so.

Beware of the display mode to be set to: block or flex (anything not starting with "inline").
Confusion is that I miss an unit test case when display is omitted (by default, browser render image such as "inline-block")

Please sign in to comment.