Skip to content

Commit 6602f56

Browse files
authored
Merge pull request #41 from dotnet-campus/t/lindexi/Update
同步11月版本
2 parents d3acfb3 + 5542357 commit 6602f56

9 files changed

Lines changed: 360 additions & 23 deletions

File tree

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Compatibilities/Packaging/CompatiblePackage.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -697,8 +697,10 @@ internal void AddContentType(CompatiblePackage.PackUriHelper.ValidatedPartUri pa
697697
//partUri provided. Override takes precedence over the default entries
698698
if (_overrideDictionary != null)
699699
{
700-
if (_overrideDictionary.ContainsKey(partUri))
701-
return _overrideDictionary[partUri];
700+
if (_overrideDictionary.TryGetValue(partUri, out var result))
701+
{
702+
return result;
703+
}
702704
}
703705

704706
//Step 2: Check if there is a default entry corresponding to the
@@ -789,9 +791,11 @@ private void EnsureOverrideDictionary()
789791
{
790792
// The part Uris are stored in the Override Dictionary in their original form , but they are compared
791793
// in a normalized manner using the PartUriComparer
792-
if (_overrideDictionary == null)
793-
_overrideDictionary =
794-
new Dictionary<CompatiblePackage.PackUriHelper.ValidatedPartUri, CompatiblePackage.ContentType>(OverrideDictionaryInitialSize);
794+
_overrideDictionary ??= new Dictionary<CompatiblePackage.PackUriHelper.ValidatedPartUri, CompatiblePackage.ContentType>(
795+
OverrideDictionaryInitialSize,
796+
// 这里需要忽略字符串的大小写
797+
// 修复 https://github.com/dotnet/Open-XML-SDK/issues/1355
798+
new ValidatedPartUriIgnoreCaseEqualityComparer());
795799
}
796800

797801
private void ParseContentTypesFile(
@@ -1097,5 +1101,19 @@ private void ThrowIfXmlAttributeMissing(string attributeName, string? attributeV
10971101
private const string PartNameAttributeName = "PartName";
10981102
private const string TemporaryPartNameWithoutExtension = "/tempfiles/sample.";
10991103
}
1104+
1105+
class ValidatedPartUriIgnoreCaseEqualityComparer : IEqualityComparer<
1106+
CompatiblePackage.PackUriHelper.ValidatedPartUri>
1107+
{
1108+
public bool Equals(PackUriHelper.ValidatedPartUri x, PackUriHelper.ValidatedPartUri y)
1109+
{
1110+
return StringComparer.OrdinalIgnoreCase.Equals(x.NormalizedPartUriString, y.NormalizedPartUriString);
1111+
}
1112+
1113+
public int GetHashCode(PackUriHelper.ValidatedPartUri obj)
1114+
{
1115+
return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.NormalizedPartUriString);
1116+
}
1117+
}
11001118
}
11011119
}

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Compatibilities/Packaging/EmptyPackagePart.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ protected override string GetContentTypeCore()
5353
//Step 2: 通过路径进行特殊判断
5454
// 例如 Slide 几的页面路径
5555
var uri = partUri.OriginalString;
56-
if (Regex.IsMatch(uri, @"/ppt/slides/slide\d+.xml"))
56+
if (Regex.IsMatch(uri, @"/ppt/slides/slide\d+\.xml"))
5757
{
5858
// "/ppt/slides/slide0.xml"
5959
return new CompatiblePackage.ContentType(
@@ -84,6 +84,20 @@ protected override string GetContentTypeCore()
8484
return new CompatiblePackage.ContentType("application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml");
8585
}
8686

87+
if (Regex.IsMatch(uri, @"/ppt/diagrams/drawing\d+\.xml"))
88+
{
89+
// /ppt/diagrams/drawing0.xml
90+
// application/vnd.ms-office.drawingml.diagramDrawing+xml
91+
return new CompatiblePackage.ContentType("application/vnd.ms-office.drawingml.diagramDrawing+xml");
92+
}
93+
94+
if (Regex.IsMatch(uri, @"/tags/tag\d+\.xml"))
95+
{
96+
// /tags/tag0.xml
97+
// application/vnd.openxmlformats-officedocument.presentationml.tags+xml
98+
return new CompatiblePackage.ContentType("application/vnd.openxmlformats-officedocument.presentationml.tags+xml");
99+
}
100+
87101
//Step 3: Check if there is a default entry corresponding to the
88102
//extension of the partUri provided.
89103
string extension = partUri.PartUriExtension;

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/GeometryPath.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,19 @@ public readonly struct ShapePath
1313
/// 创建PPT的Geometry Path
1414
/// </summary>
1515
/// <param name="path">OpenXml Path字符串</param>
16-
/// <param name="fillMode">OpenXml的Path Fill Mode </param>
17-
/// <param name="isStroke">是否有轮廓</param>
18-
/// <param name="isExtrusionOk">指定使用 3D 拉伸可能在此路径</param>
16+
/// <param name="fillMode">OpenXml的Path Fill Mode:默认为Norm </param>
17+
/// <param name="isStroke">是否有轮廓:默认为True</param>
18+
/// <param name="isExtrusionOk">指定使用 3D 拉伸可能在此路径:默认为False</param>
1919
/// <param name="emuWidth">指定的宽度或在路径坐标系统中应在使用的最大的 x 坐标</param>
2020
/// <param name="emuHeight">指定框架的高度或在路径坐标系统中应在使用的最大的 y 坐标</param>
21-
public ShapePath(string path, PathFillModeValues fillMode = PathFillModeValues.Norm, bool isStroke = true, bool isExtrusionOk = false, double? emuWidth = null, double? emuHeight = null)
21+
/// <remarks>参考文档:Ecma Office Open XML Part 1 - Fundamentals And Markup Language Reference - 20.1.9.15 path (Shape Path)</remarks>
22+
public ShapePath(string path, PathFillModeValues? fillMode = PathFillModeValues.Norm, bool? isStroke = true, bool? isExtrusionOk = false, double? emuWidth = null, double? emuHeight = null)
2223
{
2324
Path = path;
24-
IsStroke = isStroke;
25-
FillMode = fillMode;
25+
IsStroke = isStroke ?? true;
26+
FillMode = fillMode ?? PathFillModeValues.Norm;
2627
IsFilled = fillMode is not PathFillModeValues.None;
27-
IsExtrusionOk = isExtrusionOk;
28+
IsExtrusionOk = isExtrusionOk ?? false;
2829
Width = emuWidth.HasValue ? new Emu(emuWidth.Value) : null;
2930
Height = emuHeight.HasValue ? new Emu(emuHeight.Value) : null;
3031
}

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/CommonElement/CommonElementTransformDataExtensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ internal static ITransformData CreateTransformData(this OpenXmlElement element,
119119
return transformData;
120120
}
121121

122+
var alternateContentTransform2D = element.GetFirstChild<ContentPart>()?.GetFirstChild<DocumentFormat.OpenXml.Office2010.PowerPoint.Transform2D>();
123+
if (alternateContentTransform2D is not null)
124+
{
125+
FillOffset(alternateContentTransform2D.Offset, transformData);
126+
FillExtents(alternateContentTransform2D.Extents, transformData);
127+
FillRotation(alternateContentTransform2D.Rotation, transformData);
128+
FillFlip(alternateContentTransform2D.HorizontalFlip, alternateContentTransform2D.VerticalFlip, transformData);
129+
return transformData;
130+
}
131+
132+
122133
return transformData;
123134

124135
void FillOffset(Offset? offset, TransformData transformData2)

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Primitive/Colors_/ColorHelper.cs

Lines changed: 147 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Globalization;
1+
using System.Diagnostics;
2+
using System.Globalization;
23

34
using DocumentFormat.OpenXml.Drawing;
45
using DocumentFormat.OpenXml.Flatten.Framework.Context;
@@ -295,14 +296,37 @@ public static class ColorHelper
295296
/// <returns></returns>
296297
public static Color? ToColor(this RgbColorModelHex color)
297298
{
298-
if (color.Val is not null)
299+
if (color.Val is null)
299300
{
300-
if (uint.TryParse(color.Val.Value, NumberStyles.HexNumber, null, out var result))
301-
{
302-
var solidColor = result.HexToColor();
303-
var modifiedColor = ColorTransform.AppendColorModify(solidColor, color.ChildElements);
304-
return modifiedColor;
305-
}
301+
return null;
302+
}
303+
304+
var solidColor = ToColor(color.Val.Value);
305+
if (solidColor is null)
306+
{
307+
return null;
308+
}
309+
var modifiedColor = ColorTransform.AppendColorModify(solidColor, color.ChildElements);
310+
return modifiedColor;
311+
312+
}
313+
314+
/// <summary>
315+
/// 将<see cref="string" />颜色值转换为<see cref="Color" />
316+
/// </summary>
317+
/// <param name="colorValue">颜色值:例如#E71224</param>
318+
/// <returns></returns>
319+
public static Color? ToColor(this string? colorValue)
320+
{
321+
if (string.IsNullOrEmpty(colorValue))
322+
{
323+
return null;
324+
}
325+
326+
var (success, a, r, g, b) = ConvertToColor(colorValue!);
327+
if (success)
328+
{
329+
return new(a, r, g, b);
306330
}
307331

308332
return null;
@@ -358,6 +382,121 @@ private static Color HexToColor(this uint rgb)
358382
return color;
359383
}
360384

385+
/// <summary>
386+
/// 将传入的颜色字符串转换为颜色输出
387+
/// </summary>
388+
/// <param name="hexColorText">颜色字符串,格式如 “#FFDFD991” 或 “#DFD991”等,规则和 WPF 的 XAML 颜色相同,其中 “#” 是可选的</param>
389+
/// <returns></returns>
390+
public static (bool success, byte a, byte r, byte g, byte b) ConvertToColor(string hexColorText)
391+
{
392+
#if NET6_0_OR_GREATER
393+
bool startWithPoundSign = hexColorText.StartsWith('#');
394+
#else
395+
bool startWithPoundSign = hexColorText.StartsWith("#");
396+
#endif
397+
var colorStringLength = hexColorText.Length;
398+
if (startWithPoundSign) colorStringLength -= 1;
399+
int currentOffset = startWithPoundSign ? 1 : 0;
400+
// 可以采用的格式如下
401+
// #FFDFD991 8 个字符 存在 Alpha 通道
402+
// #DFD991 6 个字符
403+
// #FD92 4 个字符 存在 Alpha 通道
404+
// #DAC 3 个字符
405+
if (colorStringLength == 8
406+
|| colorStringLength == 6
407+
|| colorStringLength == 4
408+
|| colorStringLength == 3)
409+
{
410+
bool success;
411+
byte result;
412+
byte a;
413+
414+
int readCount;
415+
// #DFD991 6 个字符
416+
// #FFDFD991 8 个字符 存在 Alpha 通道
417+
//if (colorStringLength == 8 || colorStringLength == 6)
418+
if (colorStringLength > 5)
419+
{
420+
readCount = 2;
421+
}
422+
else
423+
{
424+
readCount = 1;
425+
}
426+
427+
bool includeAlphaChannel = colorStringLength == 8 || colorStringLength == 4;
428+
429+
if (includeAlphaChannel)
430+
{
431+
(success, result) = HexCharToNumber(hexColorText, currentOffset, readCount);
432+
if (!success) return default;
433+
a = result;
434+
currentOffset += readCount;
435+
}
436+
else
437+
{
438+
a = 0xFF;
439+
}
440+
441+
(success, result) = HexCharToNumber(hexColorText, currentOffset, readCount);
442+
if (!success) return default;
443+
byte r = result;
444+
currentOffset += readCount;
445+
446+
(success, result) = HexCharToNumber(hexColorText, currentOffset, readCount);
447+
if (!success) return default;
448+
byte g = result;
449+
currentOffset += readCount;
450+
451+
(success, result) = HexCharToNumber(hexColorText, currentOffset, readCount);
452+
if (!success) return default;
453+
byte b = result;
454+
455+
return (true, a, r, g, b);
456+
}
457+
458+
return default;
459+
}
460+
461+
static (bool success, byte result) HexCharToNumber(string input, int offset, int readCount)
462+
{
463+
Debug.Assert(readCount == 1 || readCount == 2, "要求 readCount 只能是 1 或者 2 的值,这是框架限制,因此不做判断");
464+
465+
byte result = 0;
466+
467+
for (int i = 0; i < readCount; i++, offset++)
468+
{
469+
var c = input[offset];
470+
byte n;
471+
if (c >= '0' && c <= '9')
472+
{
473+
n = (byte) (c - '0');
474+
}
475+
else if (c >= 'a' && c <= 'f')
476+
{
477+
n = (byte) (c - 'a' + 10);
478+
}
479+
else if (c >= 'A' && c <= 'F')
480+
{
481+
n = (byte) (c - 'A' + 10);
482+
}
483+
else
484+
{
485+
return default;
486+
}
487+
488+
result *= 16;
489+
result += n;
490+
}
491+
492+
if (readCount == 1)
493+
{
494+
result = (byte) (result * 16 + result);
495+
}
496+
497+
return (true, result);
498+
}
499+
361500
//private static ColorBrush? ToColorBrush([CanBeNull] this Color? color)
362501
//{
363502
// if (color == null) return null;

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/CustomGeometryConverters/CustomGeometryConverter.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public CustomGeometryConverter(CustomGeometry customGeometry, ElementEmuSize emu
3838
/// <returns></returns>
3939
public SvgPath? Convert()
4040
{
41+
ConvertAdjustValueList();
4142
ConvertShapeGuideList();
4243
ConvertShapeTextRectangle();
4344
return ConvertPathList();
@@ -67,6 +68,25 @@ private void ConvertShapeTextRectangle()
6768
ShapeTextRectangle = new EmuShapeTextRectangle(left, top, right, bottom);
6869
}
6970

71+
private void ConvertAdjustValueList()
72+
{
73+
var adjustValueList = _customGeometry.AdjustValueList;
74+
if (adjustValueList is not null)
75+
{
76+
foreach (var shapeGuide in adjustValueList.Elements().OfType<ShapeGuide>())
77+
{
78+
var name = shapeGuide.Name?.Value;
79+
var formula = shapeGuide.Formula?.Value;
80+
81+
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(formula))
82+
{
83+
ShapeGeometryFormulaCalculator.Calculate(name!, formula!);
84+
}
85+
}
86+
}
87+
}
88+
89+
7090
private void ConvertShapeGuideList()
7191
{
7292
var shapeGuideList = _customGeometry.ShapeGuideList;
@@ -83,6 +103,7 @@ private void ConvertShapeGuideList()
83103
}
84104
}
85105
}
106+
86107
}
87108

88109
private SvgPath? ConvertPathList()
@@ -134,7 +155,13 @@ void TryClosePath()
134155
}
135156
}
136157

137-
svgPathList.Add(new ShapePath(stringPath.ToString()));
158+
var pathFillModeValues = path.Fill?.Value;
159+
var isStroke = path.Stroke?.Value;
160+
var width = path.Width?.Value;
161+
var height = path.Height?.Value;
162+
var isExtrusionOk = path.ExtrusionOk?.Value;
163+
164+
svgPathList.Add(new ShapePath(stringPath.ToString(), pathFillModeValues, isStroke, isExtrusionOk, width, height));
138165
stringPath.Clear();
139166
}
140167

src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/ShapeGeometryConverterHelper.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Reflection;
6+
using System.Text;
67
using System.Xml.Linq;
78

89
using DocumentFormat.OpenXml.Drawing;
@@ -268,11 +269,27 @@ public static class ShapeGeometryConverterHelper
268269
return default;
269270
}
270271

271-
var path = shapeGeometryBase.ToGeometryPathString(emuSize, adjustList);
272272
var multiGeometryPaths = shapeGeometryBase.GetMultiShapePaths(emuSize, adjustList);
273+
274+
var pathString = shapeGeometryBase.ToGeometryPathString(emuSize, adjustList);
275+
var path = string.IsNullOrWhiteSpace(pathString) ? GetSvgPath(multiGeometryPaths) : pathString;
273276
return new SvgPath(geometryShapeTypeValues, path, shapeGeometryBase.ShapeTextRectangle, multiGeometryPaths);
274277
}
275278

279+
private static string? GetSvgPath(ShapePath[]? shapePaths)
280+
{
281+
if (shapePaths is not null)
282+
{
283+
var sb = new StringBuilder();
284+
foreach (var shapePath in shapePaths)
285+
{
286+
sb.Append(shapePath.Path);
287+
}
288+
return sb.ToString();
289+
}
290+
return default;
291+
}
292+
276293
/// <summary>
277294
/// 根据调整点Name获取调整点的Value
278295
/// </summary>

0 commit comments

Comments
 (0)