Skip to content

Commit

Permalink
Handle SVG image in an unified code
Browse files Browse the repository at this point in the history
  • Loading branch information
onizet committed Sep 23, 2024
1 parent 950bb3e commit cc63ee9
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 25 deletions.
7 changes: 1 addition & 6 deletions examples/Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static class Program
static async Task Main(string[] args)
{
const string filename = "test.docx";
string html = ResourceHelper.GetString("Resources.Document.html");
string html = ResourceHelper.GetString("Resources.AdvancedTable.html");
if (File.Exists(filename)) File.Delete(filename);

using (MemoryStream generatedDocument = new MemoryStream())
Expand All @@ -39,14 +39,9 @@ static async Task Main(string[] args)
}

HtmlConverter converter = new HtmlConverter(mainPart);
// HeaderPart headerPart = mainPart.AddNewPart<HeaderPart>();
//FooterPart footerPart = mainPart.AddNewPart<FooterPart>();
converter.RenderPreAsTable = true;
Body body = mainPart.Document.Body;

await converter.ParseHeader(@"<a href=""www.github.com"">
<img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="" alt=""Red dot"" /> Red Dot</a>");

await converter.ParseBody(html);
mainPart.Document.Save();

Expand Down
18 changes: 16 additions & 2 deletions src/Html2OpenXml/Expressions/Image/ImageExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
*/
using System;
using System.Threading;
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using AngleSharp.Svg.Dom;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using HtmlToOpenXml.IO;

Expand Down Expand Up @@ -57,14 +60,24 @@ class ImageExpression(IHtmlImageElement node) : ImageExpressionBase(node)
preferredSize.Height = imgNode.DisplayHeight;
}

var (imageObjId, drawingObjId) = IncrementDrawingObjId(context);

HtmlImageInfo? iinfo = context.ImageLoader.Download(src, CancellationToken.None)
.ConfigureAwait(false).GetAwaiter().GetResult();

if (iinfo == null)
return null;

if (iinfo.TypeInfo == ImagePartType.Svg)
{
var imagePart = context.HostingPart.GetPartById(iinfo.ImagePartId);
using var stream = imagePart.GetStream(System.IO.FileMode.Open);
using var sreader = new System.IO.StreamReader(stream);
imgNode.Insert(AdjacentPosition.AfterBegin, sreader.ReadToEnd());

var svgNode = imgNode.FindChild<ISvgSvgElement>();
if (svgNode is null) return null;
return SvgExpression.CreateSvgDrawing(context, svgNode, iinfo.ImagePartId, preferredSize);
}

if (preferredSize.IsEmpty)
{
preferredSize = iinfo.Size;
Expand All @@ -78,6 +91,7 @@ class ImageExpression(IHtmlImageElement node) : ImageExpressionBase(node)
long widthInEmus = new Unit(UnitMetric.Pixel, preferredSize.Width).ValueInEmus;
long heightInEmus = new Unit(UnitMetric.Pixel, preferredSize.Height).ValueInEmus;

var (imageObjId, drawingObjId) = IncrementDrawingObjId(context);
var img = new Drawing(
new wp.Inline(
new wp.Extent() { Cx = widthInEmus, Cy = heightInEmus },
Expand Down
19 changes: 14 additions & 5 deletions src/Html2OpenXml/Expressions/Image/SvgExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,27 @@ sealed class SvgExpression(ISvgSvgElement node) : ImageExpressionBase(node)
using var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(svgNode.OuterHtml), writable: false);
imgPart.FeedData(stream);
var imagePartId = context.MainPart.GetIdOfPart(imgPart);
return CreateSvgDrawing(context, svgNode, imagePartId, Size.Empty);
}

Size preferredSize = Size.Empty;
internal static Drawing CreateSvgDrawing(ParsingContext context, ISvgSvgElement svgNode, string imagePartId, Size preferredSize)
{
var width = Unit.Parse(svgNode.GetAttribute("width"));
var height = Unit.Parse(svgNode.GetAttribute("height"));
long widthInEmus, heightInEmus;
if (width.IsValid && height.IsValid)
preferredSize = new Size(width.ValueInPx, height.ValueInPx);
{
widthInEmus = width.ValueInEmus;
heightInEmus = height.ValueInEmus;
}
else
{
widthInEmus = new Unit(UnitMetric.Pixel, preferredSize.Width).ValueInEmus;
heightInEmus = new Unit(UnitMetric.Pixel, preferredSize.Height).ValueInEmus;
}

var (imageObjId, drawingObjId) = IncrementDrawingObjId(context);

long widthInEmus = new Unit(UnitMetric.Pixel, preferredSize.Width).ValueInEmus;
long heightInEmus = new Unit(UnitMetric.Pixel, preferredSize.Height).ValueInEmus;

string? title = svgNode.QuerySelector("title")?.TextContent?.CollapseAndStrip() ?? "Picture " + imageObjId;
string? description = svgNode.QuerySelector("desc")?.TextContent?.CollapseAndStrip() ?? string.Empty;

Expand Down
20 changes: 11 additions & 9 deletions src/Html2OpenXml/IO/ImagePrefetcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader)
if (response?.Content == null)
return null;

HtmlImageInfo info = new HtmlImageInfo(src);
using (response)
{
// For requested url with no filename, we need to read the media mime type if provided
Expand All @@ -123,16 +122,19 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader)
}

var ipart = hostingPart.AddImagePart(type);
Size originalSize;
using (var outputStream = ipart.GetStream(FileMode.Create))
{
response.Content.CopyTo(outputStream);

outputStream.Seek(0L, SeekOrigin.Begin);
info.Size = GetImageSize(outputStream);
originalSize = GetImageSize(outputStream);
}

info.ImagePartId = hostingPart.GetIdOfPart(ipart);
return info;
return new HtmlImageInfo(src, hostingPart.GetIdOfPart(ipart)) {
TypeInfo = type,
Size = originalSize
};
}
}

Expand All @@ -143,20 +145,20 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader)
{
if (DataUri.TryCreate(src, out var dataUri))
{
Size size;
Size originalSize;
knownContentType.TryGetValue(dataUri!.Mime, out PartTypeInfo type);
var ipart = hostingPart.AddImagePart(type);
using (var outputStream = ipart.GetStream(FileMode.Create))
{
outputStream.Write(dataUri.Data, 0, dataUri.Data.Length);

outputStream.Seek(0L, SeekOrigin.Begin);
size = GetImageSize(outputStream);
originalSize = GetImageSize(outputStream);
}

return new HtmlImageInfo(src) {
ImagePartId = hostingPart.GetIdOfPart(ipart),
Size = size
return new HtmlImageInfo(src, hostingPart.GetIdOfPart(ipart)) {
TypeInfo = type,
Size = originalSize
};
}

Expand Down
11 changes: 8 additions & 3 deletions src/Html2OpenXml/Primitives/HtmlImageInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace HtmlToOpenXml;
/// <summary>
/// Represents an image and its metadata.
/// </summary>
sealed class HtmlImageInfo(string source)
sealed class HtmlImageInfo(string source, string partId)
{
/// <summary>
/// The URI identifying this cached image information.
Expand All @@ -26,12 +26,17 @@ sealed class HtmlImageInfo(string source)
/// <summary>
/// The Unique identifier of the ImagePart in the <see cref="MainDocumentPart"/>.
/// </summary>
public string? ImagePartId { get; set; }
public string ImagePartId { get; set; } = partId;

/// <summary>
/// Gets or sets the size of the image
/// Gets or sets the original size of the image.
/// </summary>
public Size Size { get; set; }

/// <summary>
/// Gets the content type of the image.
/// </summary>
public PartTypeInfo TypeInfo { get; set; }
}

/// <summary>
Expand Down

0 comments on commit cc63ee9

Please sign in to comment.