Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ env:

on:
push:
pull_request:

jobs:
build:
Expand Down
62 changes: 31 additions & 31 deletions OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<TargetFrameworks>net8.0</TargetFrameworks>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Codeuctivity.HtmlRenderer" Version="4.0.438" />
<PackageReference Include="Codeuctivity.SkiaSharpCompare" Version="3.0.165-PreRelease" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenXmlPowerTools\OpenXmlPowerTools.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<LangVersion>8.0</LangVersion>
<TargetFrameworks>net8.0</TargetFrameworks>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Codeuctivity.HtmlRenderer" Version="4.0.458" />
<PackageReference Include="Codeuctivity.SkiaSharpCompare" Version="3.0.165-PreRelease" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.1" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OpenXmlPowerTools\OpenXmlPowerTools.csproj" />
</ItemGroup>
</Project>
10 changes: 6 additions & 4 deletions OpenXmlPowerTools.Tests/PowerToolsBlockExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,22 @@ public void MustEndPowerToolsBlockToUseStronglyTypedClasses()
bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools"))));
part.PutXDocument();

// Get the part's content through the SDK. However, we will only see what we
// added through the SDK, not what we added through the PowerTools functionality.
// Get the part's content through the SDK.
// Note: In OpenXML SDK 3.3.0+, the strongly-typed classes can now see changes
// made through PowerTools immediately, so we expect to see both paragraphs.
body = part.Document.Body;
var paragraphs = body.Elements<Paragraph>().ToList();
Assert.Single(paragraphs);
Assert.Equal(2, paragraphs.Count);
Assert.Equal("Added through SDK", paragraphs[0].InnerText);
Assert.Equal("Added through PowerTools", paragraphs[1].InnerText);

// Now, let's end the PowerTools Block, which reloads the root element of this
// one part. Reloading those root elements this way is fine if you know exactly
// which parts had their content changed by the Open XML PowerTools.
wordDocument.EndPowerToolsBlock();

// Get the part's content through the SDK. Having reloaded the root element,
// we should now see both paragraphs.
// we should still see both paragraphs.
body = part.Document.Body;
paragraphs = body.Elements<Paragraph>().ToList();
Assert.Equal(2, paragraphs.Count);
Expand Down
12 changes: 12 additions & 0 deletions OpenXmlPowerTools/FontMetric/FontStyle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Codeuctivity.OpenXmlPowerTools.FontMetric
{
[Flags]
public enum FontStyle
{
Regular = 0,
Bold = 1,
Italic = 2
}
}
10 changes: 5 additions & 5 deletions OpenXmlPowerTools/HtmlToWmlConverterCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@
// then need to make sure that all of the cells below the caption have the border on the appropriate sides so that it looks as if the table
// has a border.

using Codeuctivity.OpenXmlPowerTools.FontMetric;
using DocumentFormat.OpenXml.Packaging;
using SixLabors.Fonts;
using SkiaSharp;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -1293,10 +1293,10 @@ private enum NextExpected
}

// in theory, all unknown fonts are found by the above test, but if not...
FontFamily ff;
SKTypeface ff;
try
{
ff = SystemFonts.Families.Single(font => font.Name == fontName);
ff = SKTypeface.FromFamilyName(fontName);
}
catch (ArgumentException)
{
Expand Down Expand Up @@ -2039,9 +2039,9 @@ private static HashSet<string> KnownFamilies
if (_knownFamilies == null)
{
_knownFamilies = new HashSet<string>();
foreach (var fam in SystemFonts.Families)
foreach (var fam in SKFontManager.Default.FontFamilies)
{
_knownFamilies.Add(fam.Name);
_knownFamilies.Add(fam);
}
}
return _knownFamilies;
Expand Down
40 changes: 25 additions & 15 deletions OpenXmlPowerTools/MetricsGetter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using DocumentFormat.OpenXml.Packaging;
using Codeuctivity.OpenXmlPowerTools.FontMetric;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -106,47 +108,55 @@ public static XElement GetDocxMetrics(WmlDocument wmlDoc, MetricsGetterSettings
return metrics;
}

private static int _getTextWidth(SixLabors.Fonts.FontFamily ff, SixLabors.Fonts.FontStyle fs, decimal sz, string text)
private static int _getTextWidth(SKTypeface typeface, FontStyle fs, decimal sz, string text)
{
try
{
var font = new SixLabors.Fonts.Font(ff, (float)sz / 2f, fs);
var textOptions = new SixLabors.Fonts.TextOptions(font);
var size = SixLabors.Fonts.TextMeasurer.MeasureSize(text, textOptions);

return (int)size.Width;
var skFontStyle = SKFontStyle.Normal;
if (fs.HasFlag(FontStyle.Bold) && fs.HasFlag(FontStyle.Italic))
skFontStyle = new SKFontStyle(SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Italic);
else if (fs.HasFlag(FontStyle.Bold))
skFontStyle = new SKFontStyle(SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);
else if (fs.HasFlag(FontStyle.Italic))
skFontStyle = new SKFontStyle(SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Italic);

using var font = new SKFont(typeface, (float)sz / 2f);
using var paint = new SKPaint(font);
var bounds = new SKRect();
paint.MeasureText(text, ref bounds);
return (int)bounds.Width;
}
catch
{
return 0;
}
}

public static int GetTextWidth(SixLabors.Fonts.FontFamily ff, SixLabors.Fonts.FontStyle fs, decimal sz, string text)
public static int GetTextWidth(SKTypeface typeface, FontStyle fs, decimal sz, string text)
{
try
{
return _getTextWidth(ff, fs, sz, text);
return _getTextWidth(typeface, fs, sz, text);
}
catch (ArgumentException)
{
try
{
const SixLabors.Fonts.FontStyle fs2 = SixLabors.Fonts.FontStyle.Regular;
return _getTextWidth(ff, fs2, sz, text);
const FontStyle fs2 = FontStyle.Regular;
return _getTextWidth(typeface, fs2, sz, text);
}
catch (ArgumentException)
{
const SixLabors.Fonts.FontStyle fs2 = SixLabors.Fonts.FontStyle.Bold;
const FontStyle fs2 = FontStyle.Bold;
try
{
return _getTextWidth(ff, fs2, sz, text);
return _getTextWidth(typeface, fs2, sz, text);
}
catch (ArgumentException)
{
// if both regular and bold fail, then get metrics for Times New Roman the original FontStyle (in fs)
var ff2 = SixLabors.Fonts.SystemFonts.Families.Single(font => font.Name == "Times New Roman");
return _getTextWidth(ff2, fs, sz, text);
using var timesTypeface = SKTypeface.FromFamilyName("Times New Roman");
return _getTextWidth(timesTypeface ?? typeface, fs, sz, text);

Copilot AI Oct 10, 2025

Copy link

Choose a reason for hiding this comment

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

Potential null reference issue. If both timesTypeface and typeface are null, this will pass null to _getTextWidth which expects a non-null SKTypeface parameter.

Suggested change
return _getTextWidth(timesTypeface ?? typeface, fs, sz, text);
var tf = timesTypeface ?? typeface;
if (tf == null)
return 0;
return _getTextWidth(tf, fs, sz, text);

Copilot uses AI. Check for mistakes.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using DocumentFormat.OpenXml.Packaging;
using Codeuctivity.OpenXmlPowerTools.FontMetric;
using DocumentFormat.OpenXml.Packaging;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -2466,10 +2468,10 @@ private static HashSet<string> KnownFamilies
if (_knownFamilies == null)
{
_knownFamilies = new HashSet<string>();
var families = SixLabors.Fonts.SystemFonts.Families;
var families = SKFontManager.Default.FontFamilies;
foreach (var fam in families)
{
_knownFamilies.Add(fam.Name);
_knownFamilies.Add(fam);
}
}
return _knownFamilies;
Expand Down Expand Up @@ -2505,11 +2507,11 @@ private static int CalcWidthOfRunInTwips(XElement r)
}

// in theory, all unknown fonts are found by the above test, but if not...
SixLabors.Fonts.FontFamily ff;
SKTypeface ff;

try
{
ff = SixLabors.Fonts.SystemFonts.Families.Single(font => font.Name == fontName);
ff = SKTypeface.FromFamilyName(fontName);
}
catch (ArgumentException)
{
Expand All @@ -2518,15 +2520,15 @@ private static int CalcWidthOfRunInTwips(XElement r)
return 0;
}

var fs = SixLabors.Fonts.FontStyle.Regular;
var fs = FontStyle.Regular;
if (GetBoolProp(rPr, W.b) || GetBoolProp(rPr, W.bCs))
{
fs |= SixLabors.Fonts.FontStyle.Bold;
fs |= FontStyle.Bold;
}

if (GetBoolProp(rPr, W.i) || GetBoolProp(rPr, W.iCs))
{
fs |= SixLabors.Fonts.FontStyle.Italic;
fs |= FontStyle.Italic;
}

// Appended blank as a quick fix to accommodate &nbsp; that will get appended to some layout-critical runs such as list item numbers. In some cases, this might not be required or even wrong, so this must be revisited.
Expand Down Expand Up @@ -3618,4 +3620,4 @@ private static bool ConvertTrueFalseValueToBool(string? trueFalseValue)
return returnValue;
}
}
}
}
Loading
Loading