├── .gitattributes ├── .github └── workflows │ ├── NuGet-tag-publish.yml │ ├── dotnet-format.yml │ └── dotnetcore.yml ├── .gitignore ├── CHANGELOG.md ├── Directory.Build.props ├── DocumentFormat.OpenXml.Extensions.sln ├── LICENSE ├── README.md ├── README.zh-cn.md ├── THIRD-PARTY-NOTICES.TXT ├── build └── Version.props ├── demo └── DocumentFormat.OpenXml.Flatten.Demo │ ├── MAUI │ └── MauiPptxViewerCore │ │ ├── DocumentModel.cs │ │ ├── MauiPptxViewerCore.csproj │ │ ├── PptxDocument.cs │ │ ├── PptxSlide.cs │ │ ├── PptxViewer.cs │ │ └── Utils │ │ └── PathConverter.cs │ └── WPF │ ├── MauiForWpf │ ├── Fonts.cs │ ├── Matrix3x2Extensions.cs │ ├── MauiForWpf.csproj │ ├── README.md │ ├── XamlCanvas.cs │ ├── XamlCanvasSession.cs │ ├── XamlCanvasState.cs │ └── XamlGraphicsExtensions.cs │ └── OpenXmlFlattenMauiForWpfDemo │ ├── App.xaml │ ├── App.xaml.cs │ ├── AssemblyInfo.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── OpenXmlFlattenMauiForWpfDemo.csproj │ ├── PaintBoardUserControl.xaml │ ├── PaintBoardUserControl.xaml.cs │ └── TestFiles │ └── Shape Triangle.pptx ├── src ├── DocumentFormat.OpenXml.Flatten │ ├── .editorconfig │ ├── DocumentFormat.OpenXml.Flatten │ │ ├── Compatibilities │ │ │ └── Packaging │ │ │ │ ├── CompatiblePackage.cs │ │ │ │ ├── CompatiblePackageProvider.cs │ │ │ │ ├── ContentType.cs │ │ │ │ ├── EmptyPackagePart.cs │ │ │ │ ├── MimeUtility.cs │ │ │ │ ├── PackUriHelper.cs │ │ │ │ ├── PackageRelationship.cs │ │ │ │ ├── PackagingUtilities.cs │ │ │ │ ├── ZipPackagePart.cs │ │ │ │ ├── ZipStreamManager.cs │ │ │ │ └── ZipWrappingStream.cs │ │ ├── Contexts │ │ │ ├── CustomGeometryInfo.cs │ │ │ ├── EmuShapeTextRectangle.cs │ │ │ ├── GeometryPath.cs │ │ │ ├── SlideEmuSize.cs │ │ │ └── SvgPath.cs │ │ ├── ElementConverters │ │ │ ├── CommonElement │ │ │ │ ├── CommonElementTransformDataExtensions.cs │ │ │ │ ├── Context_ │ │ │ │ │ ├── ITransformData.cs │ │ │ │ │ └── TransformData.cs │ │ │ │ └── ExtentsToElementEmuSizeExtensions.cs │ │ │ ├── Primitive │ │ │ │ └── Colors_ │ │ │ │ │ ├── ARgbColor.cs │ │ │ │ │ ├── ARgbColorModelHex.cs │ │ │ │ │ ├── BrushFill.cs │ │ │ │ │ ├── BrushHelper.cs │ │ │ │ │ ├── ColorHelper.cs │ │ │ │ │ ├── ColorTransform.cs │ │ │ │ │ └── PresetColorMap.cs │ │ │ ├── Shape │ │ │ │ ├── CustomGeometryConverters │ │ │ │ │ └── CustomGeometryConverter.cs │ │ │ │ ├── FormulaCalculators │ │ │ │ │ ├── FormulaCalculateContext.cs │ │ │ │ │ ├── FormulaConvertContext.cs │ │ │ │ │ ├── FormulaConverter.cs │ │ │ │ │ ├── FormulaStringReader.cs │ │ │ │ │ ├── PresetGeometryCalculator.cs │ │ │ │ │ └── ShapeGeometryFormulaCalculator.cs │ │ │ │ ├── ShapeAdapt.cs │ │ │ │ ├── ShapeFlattenConverter.cs │ │ │ │ ├── ShapeGeometryConverters │ │ │ │ │ ├── AccentBorderCallout1Geometry.cs │ │ │ │ │ ├── AccentBorderCallout2Geometry.cs │ │ │ │ │ ├── AccentBorderCallout3Geometry.cs │ │ │ │ │ ├── AccentCallout1Geometry.cs │ │ │ │ │ ├── AccentCallout2Geometry.cs │ │ │ │ │ ├── AccentCallout3Geometry.cs │ │ │ │ │ ├── ActionButtonBackPreviousGeometry.cs │ │ │ │ │ ├── ActionButtonBeginningGeometry.cs │ │ │ │ │ ├── ActionButtonBlankGeometry.cs │ │ │ │ │ ├── ActionButtonDocumentGeometry.cs │ │ │ │ │ ├── ActionButtonEndGeometry.cs │ │ │ │ │ ├── ActionButtonForwardNextGeometry.cs │ │ │ │ │ ├── ActionButtonHelpGeometry.cs │ │ │ │ │ ├── ActionButtonHomeGeometry.cs │ │ │ │ │ ├── ActionButtonInformationGeometry.cs │ │ │ │ │ ├── ActionButtonMovieGeometry.cs │ │ │ │ │ ├── ActionButtonReturnGeometry.cs │ │ │ │ │ ├── ActionButtonSoundGeometry.cs │ │ │ │ │ ├── ArcGeometry.cs │ │ │ │ │ ├── BentArrowGeometry.cs │ │ │ │ │ ├── BentConnector2Geometry.cs │ │ │ │ │ ├── BentConnector3Geometry.cs │ │ │ │ │ ├── BentConnector4Geometry.cs │ │ │ │ │ ├── BentConnector5Geometry.cs │ │ │ │ │ ├── BentUpArrowGeometry.cs │ │ │ │ │ ├── BevelGeometry.cs │ │ │ │ │ ├── BlockArcGeometry.cs │ │ │ │ │ ├── BorderCallout1Geometry.cs │ │ │ │ │ ├── BorderCallout2Geometry.cs │ │ │ │ │ ├── BorderCallout3Geometry.cs │ │ │ │ │ ├── BracePairGeometry.cs │ │ │ │ │ ├── BracketPairGeometry.cs │ │ │ │ │ ├── Callout1Geometry.cs │ │ │ │ │ ├── Callout2Geometry.cs │ │ │ │ │ ├── Callout3Geometry.cs │ │ │ │ │ ├── CanGeometry.cs │ │ │ │ │ ├── ChevronGeometry.cs │ │ │ │ │ ├── ChordGeometry.cs │ │ │ │ │ ├── CircularArrowGeometry.cs │ │ │ │ │ ├── CloudCalloutGeometry.cs │ │ │ │ │ ├── CloudGeometry.cs │ │ │ │ │ ├── CornerGeometry.cs │ │ │ │ │ ├── CubeGeometry.cs │ │ │ │ │ ├── CurvedConnector2Geometry.cs │ │ │ │ │ ├── CurvedConnector3Geometry.cs │ │ │ │ │ ├── CurvedConnector4Geometry.cs │ │ │ │ │ ├── CurvedConnector5Geometry.cs │ │ │ │ │ ├── CurvedDownArrowGeometry.cs │ │ │ │ │ ├── CurvedLeftArrowGeometry.cs │ │ │ │ │ ├── CurvedRightArrowGeometry.cs │ │ │ │ │ ├── CurvedUpArrowGeometry.cs │ │ │ │ │ ├── DecagonGeometry.cs │ │ │ │ │ ├── DiagonalStripeGeometry.cs │ │ │ │ │ ├── DiamondGeometry.cs │ │ │ │ │ ├── DodecagonGeometry.cs │ │ │ │ │ ├── DonutGeometry.cs │ │ │ │ │ ├── DoubleWaveGeometry.cs │ │ │ │ │ ├── DownArrowCalloutGeometry.cs │ │ │ │ │ ├── DownArrowGeometry.cs │ │ │ │ │ ├── EllipseGeometry.cs │ │ │ │ │ ├── EllipseRibbon2Geometry.cs │ │ │ │ │ ├── EllipseRibbonGeometry.cs │ │ │ │ │ ├── FlowChartAlternateProcessGeometry.cs │ │ │ │ │ ├── FlowChartCollateGeometry.cs │ │ │ │ │ ├── FlowChartConnectorGeometry.cs │ │ │ │ │ ├── FlowChartDecisionGeometry.cs │ │ │ │ │ ├── FlowChartDelayGeometry.cs │ │ │ │ │ ├── FlowChartDisplayGeometry.cs │ │ │ │ │ ├── FlowChartDocumentGeometry.cs │ │ │ │ │ ├── FlowChartExtractGeometry.cs │ │ │ │ │ ├── FlowChartInputOutputGeometry.cs │ │ │ │ │ ├── FlowChartInternalStorageGeometry.cs │ │ │ │ │ ├── FlowChartMagneticDiskGeometry.cs │ │ │ │ │ ├── FlowChartMagneticDrumGeometry.cs │ │ │ │ │ ├── FlowChartMagneticTapeGeometry.cs │ │ │ │ │ ├── FlowChartManualInputGeometry.cs │ │ │ │ │ ├── FlowChartManualOperationGeometry.cs │ │ │ │ │ ├── FlowChartMergeGeometry.cs │ │ │ │ │ ├── FlowChartMultidocumentGeometry.cs │ │ │ │ │ ├── FlowChartOffpageConnectorGeometry.cs │ │ │ │ │ ├── FlowChartOnlineStorageGeometry.cs │ │ │ │ │ ├── FlowChartOrGeometry.cs │ │ │ │ │ ├── FlowChartPredefinedProcessGeometry.cs │ │ │ │ │ ├── FlowChartPreparationGeometry.cs │ │ │ │ │ ├── FlowChartProcessGeometry.cs │ │ │ │ │ ├── FlowChartPunchedCardGeometry.cs │ │ │ │ │ ├── FlowChartPunchedTapeGeometry.cs │ │ │ │ │ ├── FlowChartSortGeometry.cs │ │ │ │ │ ├── FlowChartSummingJunctionGeometry.cs │ │ │ │ │ ├── FlowChartTerminatorGeometry.cs │ │ │ │ │ ├── FoldedCornerGeometry.cs │ │ │ │ │ ├── FrameGeometry.cs │ │ │ │ │ ├── FunnelGeometry.cs │ │ │ │ │ ├── Gear6Geometry.cs │ │ │ │ │ ├── Gear9Geometry.cs │ │ │ │ │ ├── HalfFrameGeometry.cs │ │ │ │ │ ├── HeartGeometry.cs │ │ │ │ │ ├── HeptagonGeometry.cs │ │ │ │ │ ├── HexagonGeometry.cs │ │ │ │ │ ├── HomePlateGeometry.cs │ │ │ │ │ ├── HorizontalScrollGeometry.cs │ │ │ │ │ ├── IrregularSeal1Geometry.cs │ │ │ │ │ ├── IrregularSeal2Geometry.cs │ │ │ │ │ ├── LeftArrowCalloutGeometry.cs │ │ │ │ │ ├── LeftArrowGeometry.cs │ │ │ │ │ ├── LeftBraceGeometry.cs │ │ │ │ │ ├── LeftBracketGeometry.cs │ │ │ │ │ ├── LeftCircularArrowGeometry.cs │ │ │ │ │ ├── LeftRightArrowCalloutGeometry.cs │ │ │ │ │ ├── LeftRightArrowGeometry.cs │ │ │ │ │ ├── LeftRightRibbonGeometry.cs │ │ │ │ │ ├── LeftRightUpArrowGeometry.cs │ │ │ │ │ ├── LeftUpArrowGeometry.cs │ │ │ │ │ ├── LightningBoltGeometry.cs │ │ │ │ │ ├── LineGeometry.cs │ │ │ │ │ ├── MathDivideGeometry.cs │ │ │ │ │ ├── MathEqualGeometry.cs │ │ │ │ │ ├── MathMinusGeometry.cs │ │ │ │ │ ├── MathMultiplyGeometry.cs │ │ │ │ │ ├── MathNotEqualGeometry.cs │ │ │ │ │ ├── MathPlusGeometry.cs │ │ │ │ │ ├── MoonGeometry.cs │ │ │ │ │ ├── NoSmokingGeometry.cs │ │ │ │ │ ├── NonIsoscelesTrapezoidGeometry.cs │ │ │ │ │ ├── NotchedRightArrowGeometry.cs │ │ │ │ │ ├── OctagonGeometry.cs │ │ │ │ │ ├── ParallelogramGeometry.cs │ │ │ │ │ ├── PentagonGeometry.cs │ │ │ │ │ ├── PieGeometry.cs │ │ │ │ │ ├── PieWedgeGeometry.cs │ │ │ │ │ ├── PlaqueGeometry.cs │ │ │ │ │ ├── PlusGeometry.cs │ │ │ │ │ ├── QuadArrowCalloutGeometry.cs │ │ │ │ │ ├── QuadArrowGeometry.cs │ │ │ │ │ ├── RectangleGeometry.cs │ │ │ │ │ ├── Ribbon2Geometry.cs │ │ │ │ │ ├── RibbonGeometry.cs │ │ │ │ │ ├── RightArrowCalloutGeometry.cs │ │ │ │ │ ├── RightArrowGeometry.cs │ │ │ │ │ ├── RightBraceGeometry.cs │ │ │ │ │ ├── RightBracketGeometry.cs │ │ │ │ │ ├── RightTriangleGeometry.cs │ │ │ │ │ ├── Round1RectangleGeometry.cs │ │ │ │ │ ├── Round2DiagonalRectangleGeometry.cs │ │ │ │ │ ├── Round2SameRectangleGeometry.cs │ │ │ │ │ ├── RoundRectangleGeometry.cs │ │ │ │ │ ├── ShapeGeometryBase.cs │ │ │ │ │ ├── ShapeGeometryConverterHelper.cs │ │ │ │ │ ├── ShapeGeometryFormulaHelper.cs │ │ │ │ │ ├── SmileyFaceGeometry.cs │ │ │ │ │ ├── Snip1RectangleGeometry.cs │ │ │ │ │ ├── Snip2DiagonalRectangleGeometry.cs │ │ │ │ │ ├── Snip2SameRectangleGeometry.cs │ │ │ │ │ ├── SnipRoundRectangleGeometry.cs │ │ │ │ │ ├── Star10Geometry.cs │ │ │ │ │ ├── Star12Geometry.cs │ │ │ │ │ ├── Star16Geometry.cs │ │ │ │ │ ├── Star24Geometry.cs │ │ │ │ │ ├── Star32Geometry.cs │ │ │ │ │ ├── Star4Geometry.cs │ │ │ │ │ ├── Star5Geometry.cs │ │ │ │ │ ├── Star6Geometry.cs │ │ │ │ │ ├── Star7Geometry.cs │ │ │ │ │ ├── Star8Geometry.cs │ │ │ │ │ ├── StraightConnector1Geometry.cs │ │ │ │ │ ├── StripedRightArrowGeometry.cs │ │ │ │ │ ├── SunGeometry.cs │ │ │ │ │ ├── SwooshArrowGeometry.cs │ │ │ │ │ ├── TeardropGeometry.cs │ │ │ │ │ ├── TrapezoidGeometry.cs │ │ │ │ │ ├── TriangleGeometry.cs │ │ │ │ │ ├── UpArrowCalloutGeometry.cs │ │ │ │ │ ├── UpArrowGeometry.cs │ │ │ │ │ ├── UpDownArrowGeometry.cs │ │ │ │ │ ├── UturnArrowGeometry.cs │ │ │ │ │ ├── VerticalScrollGeometry.cs │ │ │ │ │ ├── WaveGeometry.cs │ │ │ │ │ ├── WedgeEllipseCalloutGeometry.cs │ │ │ │ │ ├── WedgeRectangleCalloutGeometry.cs │ │ │ │ │ └── WedgeRoundRectangleCalloutGeometry.cs │ │ │ │ ├── ShapeOutlineFlatten.cs │ │ │ │ ├── ShapePropertiesAdapt.cs │ │ │ │ ├── ShapeStyleAdapt.cs │ │ │ │ └── StyleMatrixReferenceHelper.cs │ │ │ ├── SmartArt │ │ │ │ └── SmartArtElementFlattenConverter.cs │ │ │ ├── Table │ │ │ │ └── TableStyleEntries │ │ │ │ │ ├── DarkStyle1Accent1_125E5076_3810_47DD_B79F_674D7AD40C01.cs │ │ │ │ │ ├── DarkStyle1Accent2_37CE84F3_28C3_443E_9E96_99CF82512B78.cs │ │ │ │ │ ├── DarkStyle1Accent3_D03447BB_5D67_496B_8E87_E561075AD55C.cs │ │ │ │ │ ├── DarkStyle1Accent4_E929F9F4_4A8F_4326_A1B4_22849713DDAB.cs │ │ │ │ │ ├── DarkStyle1Accent5_8FD4443E_F989_4FC4_A0C8_D5A2AF1F390B.cs │ │ │ │ │ ├── DarkStyle1Accent6_AF606853_7671_496A_8E4F_DF71F8EC918B.cs │ │ │ │ │ ├── DarkStyle1_E8034E78_7F5D_4C2E_B375_FC64B27BC917.cs │ │ │ │ │ ├── DarkStyle2Accent1AndAccent2_0660B408_B3CF_4A94_85FC_2B1E0A45F4A2.cs │ │ │ │ │ ├── DarkStyle2Accent3AndAccent4_91EBBBCC_DAD2_459C_BE2E_F6DE35CF9A28.cs │ │ │ │ │ ├── DarkStyle2Accent5AndAccent6_46F890A9_2807_4EBB_B81D_B2AA78EC7F39.cs │ │ │ │ │ ├── DarkStyle2_5202B0CA_FC54_4496_8BCA_5EF66A818D29.cs │ │ │ │ │ ├── LightStyle1Accent1_3B4B98B0_60AC_42C2_AFA5_B58CD77FA1E5.cs │ │ │ │ │ ├── LightStyle1Accent2_0E3FDE45_AF77_4B5C_9715_49D594BDF05E.cs │ │ │ │ │ ├── LightStyle1Accent3_C083E6E3_FA7D_4D7B_A595_EF9225AFEA82.cs │ │ │ │ │ ├── LightStyle1Accent4_D27102A9_8310_4765_A935_A1911B00CA55.cs │ │ │ │ │ ├── LightStyle1Accent5_5FD0F851_EC5A_4D38_B0AD_8093EC10F338.cs │ │ │ │ │ ├── LightStyle1Accent6_68D230F3_CF80_4859_8CE7_A43EE81993B5.cs │ │ │ │ │ ├── LightStyle1_9D7B26C5_4107_4FEC_AEDC_1716B250A1EF.cs │ │ │ │ │ ├── LightStyle2Accent1_69012ECD_51FC_41F1_AA8D_1B2483CD663E.cs │ │ │ │ │ ├── LightStyle2Accent2_72833802_FEF1_4C79_8D5D_14CF1EAF98D9.cs │ │ │ │ │ ├── LightStyle2Accent3_F2DE63D5_997A_4646_A377_4702673A728D.cs │ │ │ │ │ ├── LightStyle2Accent4_17292A2E_F333_43FB_9621_5CBBE7FDCDCB.cs │ │ │ │ │ ├── LightStyle2Accent5_5A111915_BE36_4E01_A7E5_04B1672EAD32.cs │ │ │ │ │ ├── LightStyle2Accent6_912C8C85_51F0_491E_9774_3900AFEF0FD7.cs │ │ │ │ │ ├── LightStyle2_7E9639D4_E3E2_4D34_9284_5A2195B3D0D7.cs │ │ │ │ │ ├── LightStyle3Accent1_BC89EF96_8CEA_46FF_86C4_4CE0E7609802.cs │ │ │ │ │ ├── LightStyle3Accent2_5DA37D80_6434_44D0_A028_1B22A696006F.cs │ │ │ │ │ ├── LightStyle3Accent3_8799B23B_EC83_4686_B30A_512413B5E67A.cs │ │ │ │ │ ├── LightStyle3Accent4_ED083AE6_46FA_4A59_8FB0_9F97EB10719F.cs │ │ │ │ │ ├── LightStyle3Accent5_BDBED569_4797_4DF1_A0F4_6AAB3CD982D8.cs │ │ │ │ │ ├── LightStyle3Accent6_E8B1032C_EA38_4F05_BA0D_38AFFFC7BED3.cs │ │ │ │ │ ├── LightStyle3_616DA210_FB5B_4158_B5E0_FEB733F419BA.cs │ │ │ │ │ ├── MediumStyle1Accent1_B301B821_A1FF_4177_AEE7_76D212191A09.cs │ │ │ │ │ ├── MediumStyle1Accent2_9DCAF9ED_07DC_4A11_8D7F_57B35C25682E.cs │ │ │ │ │ ├── MediumStyle1Accent3_1FECB4D8_DB02_4DC6_A0A2_4F2EBAE1DC90.cs │ │ │ │ │ ├── MediumStyle1Accent4_1E171933_4619_4E11_9A3F_F7608DF75F80.cs │ │ │ │ │ ├── MediumStyle1Accent5_FABFCF23_3B69_468F_B69F_88F6DE6A72F2.cs │ │ │ │ │ ├── MediumStyle1Accent6_10A1B5D5_9B99_4C35_A422_299274C87663.cs │ │ │ │ │ ├── MediumStyle1_793D81CF_94F2_401A_BA57_92F5A7B2D0C5.cs │ │ │ │ │ ├── MediumStyle2Accent1_5C22544A_7EE6_4342_B048_85BDC9FD1C3A.cs │ │ │ │ │ ├── MediumStyle2Accent2_21E4AEA4_8DFA_4A89_87EB_49C32662AFE0.cs │ │ │ │ │ ├── MediumStyle2Accent3_F5AB1C69_6EDB_4FF4_983F_18BD219EF322.cs │ │ │ │ │ ├── MediumStyle2Accent4_00A15C55_8517_42AA_B614_E9B94910E393.cs │ │ │ │ │ ├── MediumStyle2Accent5_7DF18680_E054_41AD_8BC1_D1AEF772440D.cs │ │ │ │ │ ├── MediumStyle2Accent6_93296810_A885_4BE3_A3E7_6D5BEEA58F35.cs │ │ │ │ │ ├── MediumStyle2_073A0DAA_6AF3_43AB_8588_CEC1D06C72B9.cs │ │ │ │ │ ├── MediumStyle3Accent1_6E25E649_3F16_4E02_A733_19D2CDBF48F0.cs │ │ │ │ │ ├── MediumStyle3Accent2_85BE263C_DBD7_4A20_BB59_AAB30ACAA65A.cs │ │ │ │ │ ├── MediumStyle3Accent3_EB344D84_9AFB_497E_A393_DC336BA19D2E.cs │ │ │ │ │ ├── MediumStyle3Accent4_EB9631B5_78F2_41C9_869B_9F39066F8104.cs │ │ │ │ │ ├── MediumStyle3Accent5_74C1A8A3_306A_4EB7_A6B1_4F7E0EB9C5D6.cs │ │ │ │ │ ├── MediumStyle3Accent6_2A488322_F2BA_4B5B_9748_0D474271808F.cs │ │ │ │ │ ├── MediumStyle3_8EC20E35_A176_4012_BC5E_935CFFF8708E.cs │ │ │ │ │ ├── MediumStyle4Accent1_69CF1AB2_1976_4502_BF36_3FF5EA218861.cs │ │ │ │ │ ├── MediumStyle4Accent2_8A107856_5554_42FB_B03E_39F5DBC370BA.cs │ │ │ │ │ ├── MediumStyle4Accent3_0505E3EF_67EA_436B_97B2_0124C06EBD24.cs │ │ │ │ │ ├── MediumStyle4Accent4_C4B1156A_380E_4F78_BDF5_A606A8083BF9.cs │ │ │ │ │ ├── MediumStyle4Accent5_22838BEF_8BB2_4498_84A7_C5851F593DF1.cs │ │ │ │ │ ├── MediumStyle4Accent6_16D9F66E_5EB9_4882_86FB_DCBF35E3C3E4.cs │ │ │ │ │ ├── MediumStyle4_D7AC3CCA_C797_4891_BE02_D94E43425B78.cs │ │ │ │ │ ├── NoStyleNoGrid_2D5ABB26_0587_4C30_8999_92F81FD0307C.cs │ │ │ │ │ ├── NoStyleTableGrid_5940675A_B579_460E_94D1_54222C63F5DA.cs │ │ │ │ │ ├── TableStyleEntryProvider.cs │ │ │ │ │ ├── ThemedStyle1Accent2_284E427A_3D55_4303_BF80_6455036E1DE7.cs │ │ │ │ │ ├── ThemedStyle1Accent3_69C7853C_536D_4A76_A0AE_DD22124D55A5.cs │ │ │ │ │ ├── ThemedStyle1Accent4_775DCB02_9BB8_47FD_8907_85C794F793BA.cs │ │ │ │ │ ├── ThemedStyle1Accent5_35758FB7_9AC5_4552_8A53_C91805E547FA.cs │ │ │ │ │ ├── ThemedStyle1Accent6_08FB837D_C827_4EFA_A057_4D05807E0F7C.cs │ │ │ │ │ ├── ThemedStyle2Accent1_D113A9D2_9D6B_4929_AA2D_F23B5EE8CBE7.cs │ │ │ │ │ ├── ThemedStyle2Accent2_18603FDC_E32A_4AB5_989C_0864C3EAD2B8.cs │ │ │ │ │ ├── ThemedStyle2Accent3_306799F8_075E_4A3A_A7F6_7FBC6576F1A4.cs │ │ │ │ │ ├── ThemedStyle2Accent4_E269D01E_BC32_4049_B463_5C60D7B0CCD2.cs │ │ │ │ │ ├── ThemedStyle2Accent5_327F97BB_C833_4FB7_BDE5_3F7075034690.cs │ │ │ │ │ └── ThemedStyle2Accent6_638B1855_1B75_4FBE_930C_398BA8C253C6.cs │ │ │ └── Text │ │ │ │ ├── TextBodyAdapt.cs │ │ │ │ └── TextBodyPropertiesExtensions.cs │ │ ├── ExcelTables │ │ │ ├── CellFlattenInfo.cs │ │ │ ├── CellFlattenStyle.cs │ │ │ ├── Contexts │ │ │ │ ├── ExcelColumnWidth.cs │ │ │ │ ├── ExcelPosition.cs │ │ │ │ ├── ExcelRange.cs │ │ │ │ └── ExcelTableConvertContext.cs │ │ │ ├── SparseCellTable.cs │ │ │ ├── SpreadsheetDocumentFlatten.cs │ │ │ ├── Utils │ │ │ │ ├── ExcelInfoHelper.cs │ │ │ │ └── SpreadsheetDocumentExtension.cs │ │ │ ├── WorksheetFlatten.cs │ │ │ └── WorksheetsFlatten.cs │ │ ├── Framework │ │ │ ├── Context │ │ │ │ ├── ElementContext.cs │ │ │ │ └── SlideContext.cs │ │ │ ├── Diagnostics │ │ │ │ └── CodeAnalysis.cs │ │ │ ├── ElementFlattenExtension.cs │ │ │ ├── ElementFlattenProvider.cs │ │ │ ├── FlattenObject.cs │ │ │ ├── OpenXmlCompositeElementFlattenProvider.cs │ │ │ └── OpenXmlElementFlattenConverterBase.cs │ │ ├── OpenMcdf │ │ │ ├── .editorconfig │ │ │ ├── OpenMcdf │ │ │ │ ├── CFException.cs │ │ │ │ ├── CFItem.cs │ │ │ │ ├── CFStorage.cs │ │ │ │ ├── CFStream.cs │ │ │ │ ├── CompoundFile.cs │ │ │ │ ├── DirectoryEntry.cs │ │ │ │ ├── Header.cs │ │ │ │ ├── IDirectoryEntry.cs │ │ │ │ ├── OpenMcdf.csproj │ │ │ │ ├── RBTree │ │ │ │ │ ├── Color.cs │ │ │ │ │ ├── IRBNode.cs │ │ │ │ │ ├── NodeOperation.cs │ │ │ │ │ ├── RBTree.cs │ │ │ │ │ ├── RBTreeDuplicatedItemException.cs │ │ │ │ │ └── RBTreeException.cs │ │ │ │ ├── Readonly │ │ │ │ │ ├── ByteArrayPool.cs │ │ │ │ │ ├── IByteArrayPool.cs │ │ │ │ │ ├── IStreamReader.cs │ │ │ │ │ ├── ReadonlyCompoundFile.cs │ │ │ │ │ ├── ReadonlyCompoundFileItem.cs │ │ │ │ │ ├── ReadonlyCompoundFileItemComparer.cs │ │ │ │ │ ├── ReadonlyCompoundFileStorage.cs │ │ │ │ │ ├── ReadonlyCompoundFileStream.cs │ │ │ │ │ ├── ReadonlyStreamViewForSectorList.cs │ │ │ │ │ ├── SectorList.cs │ │ │ │ │ └── StreamHelper.cs │ │ │ │ ├── Sector.cs │ │ │ │ ├── SectorCollection.cs │ │ │ │ ├── StreamRW.cs │ │ │ │ └── StreamView.cs │ │ │ └── README.md │ │ ├── OpenXmlElementFlatten.cs │ │ ├── Utils │ │ │ ├── MathHelper.cs │ │ │ ├── PlaceholderHelper.cs │ │ │ ├── RootElementExtensions.cs │ │ │ └── TextListStyleHelper.cs │ │ ├── dotnetCampus.DocumentFormat.OpenXml.Flatten.csproj │ │ └── dotnetCampus.DocumentFormat.OpenXml.Flatten.csproj.DotSettings │ └── README.md ├── dotnetCampus.OfficeDocumentZipper │ ├── App.xaml │ ├── App.xaml.cs │ ├── CalculatorPage.xaml │ ├── CalculatorPage.xaml.cs │ ├── Icon.png │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── dotnetCampus.OfficeDocumentZipper.csproj ├── dotnetCampus.OpenXMLUnitConverter │ ├── Angle.cs │ ├── EmuPoint.cs │ ├── EmuRectangle.cs │ ├── EmuSize.cs │ ├── MillisecondTime.cs │ ├── OpenXmlUnitConverter.cs │ ├── PixelPoint.cs │ ├── PixelSize.cs │ └── Properties │ │ └── AssemblyInfo.cs └── dotnetCampus.OpenXmlUnitConverter │ ├── Cm.cs │ ├── Degree.cs │ ├── Dxa.cs │ ├── Emu.cs │ ├── EmuPercentage.cs │ ├── HalfPoint.cs │ ├── Inch.cs │ ├── Legacy │ ├── Cm.cs │ ├── Degree.cs │ ├── Dxa.cs │ ├── Emu.cs │ ├── EmuPercentage.cs │ ├── HalfPoint.cs │ ├── Inch.cs │ ├── LegacyUnit.cs │ ├── Mm.cs │ ├── Percentage.cs │ ├── Pixel.cs │ ├── PixelPercentage.cs │ ├── Pound.cs │ ├── PoundHundredfold.cs │ ├── Pt.cs │ ├── PtHundredfold.cs │ └── UnitConverter.cs │ ├── Mm.cs │ ├── Percentage.cs │ ├── Pixel.cs │ ├── PixelPercentage.cs │ ├── Pound.cs │ ├── PoundHundredfold.cs │ ├── Pt.cs │ ├── PtHundredfold.cs │ ├── UnitConverter.cs │ └── dotnetCampus.OpenXmlUnitConverter.csproj └── tests ├── dotnetCampus.OpenXMLUnitConverter.Tests └── dotnetCampus.OpenXMLUnitConverter.Tests.csproj └── dotnetCampus.OpenXmlUnitConverter.Tests ├── AngleTest.cs ├── EmuPercentageTest.cs ├── EmuRectangleTest.cs ├── MillisecondTimeTest.cs ├── PercentageTest.cs ├── PixelPointTest.cs ├── PixelSizeTest.cs └── dotnetCampus.OpenXmlUnitConverter.Tests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/NuGet-tag-publish.yml: -------------------------------------------------------------------------------- 1 | name: publish nuget 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: | 20 | 3.1.x 21 | 5.0.x 22 | 6.0.x 23 | 24 | - name: Install dotnet tool 25 | run: dotnet tool install -g dotnetCampus.TagToVersion 26 | 27 | - name: Set tag to version 28 | run: dotnet TagToVersion -t ${{ github.ref }} 29 | 30 | - name: Build with dotnet 31 | run: dotnet build --configuration Release 32 | 33 | - name: Pack with dotnet 34 | run: dotnet pack --configuration Release --no-build 35 | 36 | - name: Install Nuget 37 | uses: nuget/setup-nuget@v1 38 | with: 39 | nuget-version: '5.x' 40 | 41 | - name: Add private GitHub registry to NuGet 42 | run: | 43 | nuget sources add -name github -Source https://nuget.pkg.github.com/dotnet-campus/index.json -Username dotnet-campus -Password ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Push generated package to GitHub registry 46 | run: | 47 | nuget push .\bin\Release\*.nupkg -Source github -SkipDuplicate 48 | nuget push .\bin\Release\*.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NugetKey }} -NoSymbols 49 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-format.yml: -------------------------------------------------------------------------------- 1 | name: Code format check 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | dotnet-format: 9 | runs-on: windows-latest 10 | steps: 11 | 12 | - name: Checkout repo 13 | uses: actions/checkout@v2 14 | with: 15 | ref: ${{ github.head_ref }} 16 | 17 | # 代码文件编码规范机器人,详细请看 [dotnet 在 GitHub 的 Action 上部署自动代码编码规范机器人](https://blog.lindexi.com/post/dotnet-%E5%9C%A8-GitHub-%E7%9A%84-Action-%E4%B8%8A%E9%83%A8%E7%BD%B2%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83%E6%9C%BA%E5%99%A8%E4%BA%BA.html) 18 | - name: Install dotnetCampus.EncodingNormalior 19 | run: dotnet tool update -g dotnetCampus.EncodingNormalior 20 | 21 | - name: Fix encoding 22 | run: EncodingNormalior -f . --TryFix true 23 | 24 | # 代码格式化机器人,详细请看 [dotnet 基于 dotnet format 的 GitHub Action 自动代码格式化机器人](https://blog.lindexi.com/post/dotnet-%E5%9F%BA%E4%BA%8E-dotnet-format-%E7%9A%84-GitHub-Action-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%9C%BA%E5%99%A8%E4%BA%BA.html ) 25 | - name: Install dotnet-format 26 | run: dotnet tool install -g dotnet-format 27 | 28 | - name: Run dotnet format 29 | run: dotnet format 30 | 31 | - name: Commit files 32 | # 下面将使用机器人的账号,你可以替换为你自己的账号 33 | run: | 34 | git config --local user.name "github-actions-dotnet-formatter[bot]" 35 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 36 | git commit -a -m 'Automated dotnet-format update' 37 | continue-on-error: true 38 | 39 | - name: Create Pull Request 40 | # if: steps.format.outputs.has-changes == 'true' # 如果有格式化,才继续 41 | uses: peter-evans/create-pull-request@v3 42 | with: 43 | title: '[Bot] Automated PR to fix formatting errors' 44 | body: | 45 | Automated PR to fix formatting errors 46 | committer: GitHub 47 | author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 48 | # 以下是给定代码审查者,需要设置仓库有权限的开发者 49 | assignees: lindexi,walterlv 50 | reviewers: lindexi,walterlv 51 | # 对应的上传分支 52 | branch: t/bot/fix-codeformatting -------------------------------------------------------------------------------- /.github/workflows/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v1 14 | with: 15 | dotnet-version: | 16 | 3.1.x 17 | 5.0.x 18 | 6.0.x 19 | - name: Build with dotnet 20 | run: dotnet build --configuration Release 21 | 22 | - name: Test 23 | run: dotnet test --configuration Release --no-build -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # dotnetCampus.OfficeDocumentZiper -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | latest 5 | $(MSBuildThisFileDirectory)bin\$(Configuration) 6 | dotnet campus(.NET 职业技术学院) 7 | 8 | dotnet-campus 9 | https://github.com/dotnet-campus/dotnetCampus.OfficeDocumentZipper 10 | https://github.com/dotnet-campus/dotnetCampus.OfficeDocumentZipper 11 | A dotnet tool to assist in editing Office document files 12 | 13 | git 14 | Copyright © 2020-$([System.DateTime]::Now.ToString(`yyyy`)) dotnet campus, All Rights Reserved. 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 dotnet campus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # DocumentFormat.OpenXml.Extensions 2 | 3 | [中文](README.zh-cn.md) | [English](README.md) 4 | 5 | OpenXML SDK 的扩展集,包括扩展库和辅助开发工具 6 | 7 | | Build | 8 | |--| 9 | |![](https://github.com/dotnet-campus/dotnetCampus.OfficeDocumentZipper/workflows/.NET%20Core/badge.svg)| 10 | 11 | # 工具 12 | 13 | ## dotnetCampus.OfficeDocumentZipper 14 | 15 | [解压缩文档为文件夹工具](https://blog.lindexi.com/post/dotnet-OpenXML-%E8%A7%A3%E5%8E%8B%E7%BC%A9%E6%96%87%E6%A1%A3%E4%B8%BA%E6%96%87%E4%BB%B6%E5%A4%B9%E5%B7%A5%E5%85%B7.html) 16 | 17 | 在开发过程中,需要不断解压缩 Office 文档,阅读或修改文档的内容,再次压缩回 Office 文档,这样的效率比较低。本工具提供一键解压自动格式化,一键组装Office文档且打开的功能 18 | 19 | ### 使用方法 20 | 21 | OfficeDocumentZipper 工具作为 dotnet tool 发布,可使用以下代码进行安装和启动 22 | 23 | ``` 24 | dotnet tool update -g dotnetCampus.OfficeDocumentZipper 25 | 26 | OfficeDocumentZipper 27 | ``` 28 | 29 | 建议将以上命令存放作为 bat 脚本,方便每次快速运行 30 | 31 | ### 功能 32 | 33 | - 解压 pptx docx xlsx 文件以及自动格式化文档内容 34 | - 压缩文件夹作为 pptx docx xlsx 文件 35 | - 转换 OpenXML 单位 36 | 37 | ![](https://user-images.githubusercontent.com/16054566/91013976-2b1c4580-e61b-11ea-8ef2-044ea79ef31b.png) 38 | 39 | # 库 40 | 41 | | Name | NuGet| 42 | |--|--| 43 | |dotnetCampus.OpenXMLUnitConverter|[![](https://img.shields.io/nuget/v/dotnetCampus.OpenXMLUnitConverter.svg)](https://www.nuget.org/packages/dotnetCampus.OpenXMLUnitConverter)| 44 | |dotnetCampus.OpenXMLUnitConverter.Source|[![](https://img.shields.io/nuget/v/dotnetCampus.OpenXMLUnitConverter.Source.svg)](https://www.nuget.org/packages/dotnetCampus.OpenXMLUnitConverter.Source)| 45 | 46 | 47 | ## dotnetCampus.OpenXMLUnitConverter 48 | 49 | 定义 OpenXML 的单位以及提供单位转换的功能 50 | 51 | ### 安装方法 52 | 53 | DLL 包: 54 | 55 | ```xml 56 | 57 | ``` 58 | 59 | [SouceYard](https://github.com/dotnet-campus/SourceYard) 源代码包: 60 | 61 | ```xml 62 | 63 | all 64 | runtime; build; native; contentfiles; analyzers 65 | 66 | ``` 67 | 68 | ### 使用方法 69 | 70 | 例子: 71 | 72 | ```csharp 73 | void Foo(DocumentFormat.OpenXml.Drawing.Point2DType point) 74 | { 75 | var x = new Emu(point.X); 76 | var pixelValue = x.ToPixel(); 77 | var cmValue = x.ToCm(); 78 | } 79 | ``` 80 | 81 | 详细请看 [Office Open XML 的测量单位](https://blog.lindexi.com/post/Office-Open-XML-%E7%9A%84%E6%B5%8B%E9%87%8F%E5%8D%95%E4%BD%8D.html ) 82 | 83 | ## DocumentFormat.OpenXml.Flatten 84 | 85 | 提供继承的属性拍平的功能的库,包含常用处理逻辑和计算逻辑 86 | 87 | ### 安装方法 88 | 89 | DLL 包: 90 | 91 | ```xml 92 | 93 | ``` 94 | 95 | [SouceYard](https://github.com/dotnet-campus/SourceYard) 源代码包: 96 | 97 | ```xml 98 | 99 | all 100 | runtime; build; native; contentfiles; analyzers 101 | 102 | ``` 103 | 104 | ### 使用方法 105 | 106 | 具体使用方法请参阅 [DocumentFormat.OpenXml.Flatten 使用文档](src/DocumentFormat.OpenXml.Flatten/README.md) 和[示例代码](demo) 107 | 108 | # 感谢 109 | 110 | - [OfficeDev/Open-XML-SDK: Open XML SDK by Microsoft](https://github.com/OfficeDev/Open-XML-SDK/ ) 111 | - [ironfede/openmcdf: Microsoft Compound File .net component - pure C# - NET Standard 2.0](https://github.com/ironfede/openmcdf ) 112 | 113 | # 开源社区 114 | 115 | 如果你希望参与贡献,欢迎 [Pull Request](https://github.com/dotnet-campus/DocumentFormat.OpenXml.Extensions/pulls),或给我们 [报告 Bug](https://github.com/dotnet-campus/DocumentFormat.OpenXml.Extensions/issues/new) 116 | 117 | # 授权协议 118 | 119 | [![](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](LICENSE) -------------------------------------------------------------------------------- /build/Version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1.0.0 4 | 5 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/MAUI/MauiPptxViewerCore/DocumentModel.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Packaging; 2 | using DocumentFormat.OpenXml.Presentation; 3 | 4 | namespace MauiPptxViewerCore; 5 | 6 | record DocumentModel(PresentationDocument PresentationDocument, Presentation Presentation) 7 | { 8 | public PresentationPart PresentationPart 9 | // 这里能拿到 Presentation 那就一定能拿到 PresentationPart 对象,一定不是空 10 | => Presentation.PresentationPart!; 11 | } -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/MAUI/MauiPptxViewerCore/MauiPptxViewerCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/MAUI/MauiPptxViewerCore/PptxDocument.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using DocumentFormat.OpenXml.Packaging; 3 | using DocumentFormat.OpenXml.Presentation; 4 | 5 | namespace MauiPptxViewerCore; 6 | 7 | class PptxDocument 8 | { 9 | public PptxDocument(DocumentModel documentModel) 10 | { 11 | DocumentModel = documentModel; 12 | 13 | var slideIdList = documentModel.Presentation.SlideIdList; 14 | 15 | if (slideIdList is null) 16 | { 17 | // 这个文档连一页都没有… 18 | throw new ArgumentException($"The Empty document. The number of slide is 0."); 19 | } 20 | 21 | var presentationPart = documentModel.PresentationPart; 22 | 23 | var pptxSlideList = new List(); 24 | 25 | // [dotnet OpenXML 幻灯片 PPTX 的 Slide Id 和页面序号的关系](https://blog.lindexi.com/post/dotnet-OpenXML-%E5%B9%BB%E7%81%AF%E7%89%87-PPTX-%E7%9A%84-Slide-Id-%E5%92%8C%E9%A1%B5%E9%9D%A2%E5%BA%8F%E5%8F%B7%E7%9A%84%E5%85%B3%E7%B3%BB.html) 26 | foreach (var slideId in slideIdList.ChildElements.OfType()) 27 | { 28 | // 获取页面内容 29 | Debug.Assert(slideId.RelationshipId != null, "slideId.RelationshipId != null"); 30 | SlidePart slidePart = (SlidePart)presentationPart.GetPartById(slideId.RelationshipId!); 31 | 32 | Debug.Assert(slideId.Id is not null, "slideId.Id != null"); 33 | var pptxSlide = new PptxSlide(slideId.Id!.Value, slidePart, documentModel); 34 | 35 | pptxSlideList.Add(pptxSlide); 36 | } 37 | 38 | SlideList = pptxSlideList; 39 | } 40 | 41 | public DocumentModel DocumentModel { get; } 42 | 43 | public List SlideList { get; } 44 | } -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/MAUI/MauiPptxViewerCore/PptxViewer.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Flatten.Compatibilities.Packaging; 2 | using DocumentFormat.OpenXml.Packaging; 3 | using DocumentFormat.OpenXml.Presentation; 4 | using Microsoft.Maui.Graphics; 5 | 6 | namespace MauiPptxViewerCore; 7 | 8 | public class PptxViewer 9 | { 10 | public PptxViewer(FileInfo pptxFile, ICanvas canvas) 11 | { 12 | PptxFile = pptxFile; 13 | Canvas = canvas; 14 | } 15 | 16 | public FileInfo PptxFile { get; } 17 | public ICanvas Canvas { get; } 18 | 19 | private Stream? _stream; // do not care about dispose it... This is a demo 20 | 21 | public void Open() 22 | { 23 | _stream = PptxFile.Open(FileMode.Open, FileAccess.ReadWrite); 24 | 25 | // 解析细节请参阅 [Office 使用 OpenXML SDK 解析文档博客目录](https://blog.lindexi.com/post/Office-%E4%BD%BF%E7%94%A8-OpenXML-SDK-%E8%A7%A3%E6%9E%90%E6%96%87%E6%A1%A3%E5%8D%9A%E5%AE%A2%E7%9B%AE%E5%BD%95.html ) 26 | var package = CompatiblePackageProvider.OpenPackage(_stream, FileMode.Open, FileAccess.ReadWrite); 27 | 28 | PresentationDocument presentationDocument = PresentationDocument.Open(package); 29 | 30 | Presentation presentation = presentationDocument.PresentationPart!.Presentation; 31 | 32 | var documentModel = new DocumentModel(presentationDocument, presentation); 33 | _pptxDocument = new PptxDocument(documentModel); 34 | 35 | var pptxSlide = _pptxDocument.SlideList.FirstOrDefault(); 36 | if (pptxSlide != null) 37 | { 38 | pptxSlide.Render(Canvas); 39 | } 40 | } 41 | 42 | private PptxDocument? _pptxDocument; 43 | } 44 | 45 | 46 | //public record PptxViewerBuilder(FileInfo PptxFile, ICanvas Canvas) 47 | //{ 48 | //} 49 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/MauiForWpf/Fonts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace Microsoft.Maui.Graphics.Xaml 5 | { 6 | static class Fonts 7 | { 8 | public readonly static FontService CurrentService = new FontService(); 9 | } 10 | 11 | class FontService 12 | { 13 | internal FakeFontStyle? GetFontStyleById(string fontName) 14 | { 15 | return null; 16 | } 17 | } 18 | 19 | class FakeFontStyle 20 | { 21 | public FontStyleType StyleType { get; internal set; } 22 | 23 | public Font FontFamily { get; internal set; } 24 | public int Weight { get; internal set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/MauiForWpf/MauiForWpf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows 5 | enable 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/MauiForWpf/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft.Maui.Graphics.Xaml 2 | 3 | Copy from: https://github.com/dotnet/Microsoft.Maui.Graphics 4 | 5 | I can not wait for Microsoft.Maui.Graphics.Xaml to be released -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/MauiForWpf/XamlCanvasSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.Maui.Graphics.Xaml 4 | { 5 | public class XamlCanvasSession : IDisposable 6 | { 7 | private XamlCanvas _canvas; 8 | 9 | public XamlCanvasSession(XamlCanvas canvas) 10 | { 11 | this._canvas = canvas; 12 | canvas.BeginDrawing(); 13 | } 14 | 15 | public XamlCanvas Canvas => _canvas; 16 | 17 | public void Dispose() 18 | { 19 | if (_canvas != null) 20 | { 21 | _canvas.EndDrawing(); 22 | _canvas = null; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace OpenXmlFlattenMauiForWpfDemo; 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Pptx File Path: 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace OpenXmlFlattenMauiForWpfDemo; 18 | 19 | /// 20 | /// Interaction logic for MainWindow.xaml 21 | /// 22 | public partial class MainWindow : Window 23 | { 24 | public MainWindow() 25 | { 26 | InitializeComponent(); 27 | 28 | Loaded += MainWindow_Loaded; 29 | } 30 | 31 | private void MainWindow_Loaded(object sender, RoutedEventArgs e) 32 | { 33 | var folder = System.IO.Path.GetDirectoryName(GetType().Assembly.Location)!; 34 | var testFile = System.IO.Path.Combine(folder, @"TestFiles\Shape Triangle.pptx"); 35 | 36 | Open(testFile); 37 | } 38 | 39 | private void OpenPptxFileButton_OnClick(object sender, RoutedEventArgs e) 40 | { 41 | var pptxFilePath = PptxFilePathTextBox.Text; 42 | 43 | Open(pptxFilePath); 44 | } 45 | 46 | private void Open(string pptxFilePath) 47 | { 48 | if (!string.IsNullOrEmpty(pptxFilePath) && File.Exists(pptxFilePath)) 49 | { 50 | PaintBoardUserControl.Open(new FileInfo(pptxFilePath)); 51 | 52 | PptxFilePathTextBox.Text = pptxFilePath; 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/OpenXmlFlattenMauiForWpfDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/PaintBoardUserControl.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/PaintBoardUserControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | using MauiPptxViewerCore; 17 | using Microsoft.Maui.Graphics; 18 | using Microsoft.Maui.Graphics.Xaml; 19 | 20 | namespace OpenXmlFlattenMauiForWpfDemo; 21 | 22 | /// 23 | /// PaintBoardUserControl.xaml 的交互逻辑 24 | /// 25 | public partial class PaintBoardUserControl : UserControl 26 | { 27 | public PaintBoardUserControl() 28 | { 29 | InitializeComponent(); 30 | 31 | var xamlCanvas = new XamlCanvas() 32 | { 33 | Canvas = MauiCanvas, 34 | }; 35 | _xamlCanvas = xamlCanvas; 36 | } 37 | 38 | public void Open(FileInfo pptxFile) 39 | { 40 | _pptxViewer = new PptxViewer(pptxFile, _xamlCanvas); 41 | _pptxViewer.Open(); 42 | } 43 | 44 | private PptxViewer? _pptxViewer; 45 | private readonly XamlCanvas _xamlCanvas; 46 | } 47 | -------------------------------------------------------------------------------- /demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/TestFiles/Shape Triangle.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet-campus/DocumentFormat.OpenXml.Extensions/8d1434603473618eed409bfa1d0c39fbf623e02b/demo/DocumentFormat.OpenXml.Flatten.Demo/WPF/OpenXmlFlattenMauiForWpfDemo/TestFiles/Shape Triangle.pptx -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Compatibilities/Packaging/CompatiblePackageProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Packaging; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.Compatibilities.Packaging; 5 | 6 | /// 7 | /// 带兼容处理的 Package 提供器 8 | /// 9 | public static class CompatiblePackageProvider 10 | { 11 | /// 12 | /// 从传入的 打开 对象 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | public static Package OpenPackage(Stream s, FileMode packageFileMode, 19 | FileAccess packageFileAccess) 20 | { 21 | return CompatiblePackage.OpenPackage(s, packageFileMode, packageFileAccess); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Compatibilities/Packaging/ZipWrappingStream.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.IO.Compression; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.Compatibilities.Packaging; 5 | 6 | partial class CompatiblePackage 7 | { 8 | internal sealed class ZipWrappingStream : Stream 9 | { 10 | private readonly Stream _baseStream; 11 | private readonly ZipArchiveEntry _zipArchiveEntry; 12 | private readonly FileMode _packageFileMode; 13 | private readonly FileAccess _packageFileAccess; 14 | private readonly bool _canRead; 15 | private readonly bool _canWrite; 16 | 17 | public ZipWrappingStream(ZipArchiveEntry zipArchiveEntry, Stream stream, FileMode packageFileMode, FileAccess packageFileAccess, bool canRead, bool canWrite) 18 | { 19 | _zipArchiveEntry = zipArchiveEntry; 20 | _baseStream = stream; 21 | _packageFileMode = packageFileMode; 22 | _packageFileAccess = packageFileAccess; 23 | _canRead = canRead; 24 | _canWrite = canWrite; 25 | } 26 | 27 | public override bool CanRead 28 | { 29 | get 30 | { 31 | if (_canRead == false) 32 | return false; 33 | return _baseStream.CanRead; 34 | } 35 | } 36 | 37 | public override bool CanWrite 38 | { 39 | get 40 | { 41 | if (_canWrite == false) 42 | return false; 43 | return _baseStream.CanWrite; 44 | } 45 | } 46 | 47 | public override void Write( 48 | byte[] buffer, 49 | int offset, 50 | int count 51 | ) 52 | { 53 | _baseStream.Write(buffer, offset, count); 54 | } 55 | 56 | public override int Read( 57 | byte[] buffer, 58 | int offset, 59 | int count 60 | ) 61 | { 62 | return _baseStream.Read(buffer, offset, count); 63 | } 64 | 65 | public override void SetLength( 66 | long value 67 | ) 68 | { 69 | _baseStream.SetLength(value); 70 | } 71 | 72 | public override long Seek( 73 | long offset, 74 | SeekOrigin origin 75 | ) 76 | { 77 | return _baseStream.Seek(offset, origin); 78 | } 79 | 80 | public override void Flush() 81 | { 82 | _baseStream.Flush(); 83 | } 84 | 85 | public override long Position 86 | { 87 | get 88 | { 89 | return _baseStream.Position; 90 | } 91 | set 92 | { 93 | _baseStream.Position = value; 94 | } 95 | } 96 | 97 | public override long Length 98 | { 99 | get 100 | { 101 | // ZipArchiveEntry's read stream doesn't provide a length since it's a raw DeflateStream 102 | // Return length from the archive entry. 103 | if (_packageFileAccess == FileAccess.Read) 104 | return _zipArchiveEntry.Length; 105 | else 106 | return _baseStream.Length; 107 | } 108 | } 109 | 110 | public override bool CanSeek 111 | { 112 | get { return _baseStream.CanSeek; } 113 | } 114 | 115 | protected override void Dispose(bool disposing) 116 | { 117 | if (disposing) 118 | _baseStream.Dispose(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/CustomGeometryInfo.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.Contexts 4 | { 5 | /// 6 | /// 记录自定义形状信息 7 | /// 8 | public readonly struct CustomGeometryInfo 9 | { 10 | /// 11 | /// 创建记录自定义形状信息 12 | /// 13 | /// 形状导航集合 14 | /// 调整点集合 15 | /// 路径集合 16 | /// 文本框范围 17 | public CustomGeometryInfo(ShapeGuideList? shapeGuideList, AdjustValueList? adjustValueList, PathList? pathList, Rectangle? rectangle) 18 | { 19 | ShapeGuideList = shapeGuideList; 20 | AdjustValueList = adjustValueList; 21 | PathList = pathList; 22 | Rectangle = rectangle; 23 | } 24 | 25 | /// 26 | /// 形状导航集合 27 | /// 28 | public ShapeGuideList? ShapeGuideList { get; } 29 | 30 | /// 31 | /// 调整点集合 32 | /// 33 | public AdjustValueList? AdjustValueList { get; } 34 | 35 | /// 36 | /// 路径集合 37 | /// 38 | public PathList? PathList { get; } 39 | 40 | /// 41 | /// 文本框范围 42 | /// 43 | public Rectangle? Rectangle { get; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/EmuShapeTextRectangle.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 2 | 3 | using dotnetCampus.OpenXmlUnitConverter; 4 | 5 | namespace DocumentFormat.OpenXml.Flatten.Contexts 6 | { 7 | /// 8 | /// 使用 Emu 单位的形状里的文本范围,对应 OpenXml Shape 的 a:rect 标签 9 | /// 10 | public readonly struct EmuShapeTextRectangle 11 | { 12 | /// 13 | /// 创建形状里的文本范围 14 | /// 15 | public EmuShapeTextRectangle(Emu left, Emu top, Emu right, Emu bottom) 16 | { 17 | var emuWidth = new Emu(right.Value - left.Value); 18 | var emuHeight = new Emu(bottom.Value - top.Value); 19 | 20 | OriginPoint = new EmuPoint(left, top); 21 | Size = new EmuSize(emuWidth, emuHeight); 22 | } 23 | 24 | /// 25 | /// 创建形状里的文本范围 26 | /// 27 | /// 形状文本框的大小 28 | /// 形状文本框的原点坐标 29 | public EmuShapeTextRectangle(EmuSize size, EmuPoint originPoint) 30 | { 31 | Size = size; 32 | OriginPoint = originPoint; 33 | } 34 | 35 | /// 36 | /// 形状文本框的坐标原点 37 | /// 38 | public EmuPoint OriginPoint { get; } 39 | 40 | /// 41 | /// Rectangle的大小 42 | /// 43 | public EmuSize Size { get; } 44 | } 45 | 46 | /// 47 | /// Emu单位的形状文本框的Margin 48 | /// 49 | public readonly struct EmuTextMargin 50 | { 51 | /// 52 | /// 创建EmuTextMargin 53 | /// 54 | /// emu单位的LeftInset 55 | /// emu单位的TopInset 56 | /// emu单位的RightInset 57 | /// emu单位的BottomInset 58 | public EmuTextMargin(double emuLeftInset, double emuTopInset, double emuRightInset, double emuBottomInset) 59 | { 60 | LeftInset = new Emu(emuLeftInset); 61 | TopInset = new Emu(emuTopInset); 62 | RightInset = new Emu(emuRightInset); 63 | BottomInset = new Emu(emuBottomInset); 64 | } 65 | 66 | /// 67 | /// 对应 a:bodyPr lIns 68 | /// 69 | public Emu LeftInset { get; } 70 | 71 | /// 72 | /// 对应 a:bodyPr rIns 73 | /// 74 | public Emu RightInset { get; } 75 | 76 | /// 77 | /// 对应 a:bodyPr bIns 78 | /// 79 | public Emu BottomInset { get; } 80 | 81 | /// 82 | /// 对应 a:bodyPr tIns 83 | /// 84 | public Emu TopInset { get; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/GeometryPath.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | using dotnetCampus.OpenXmlUnitConverter; 4 | 5 | namespace DocumentFormat.OpenXml.Flatten.Contexts 6 | { 7 | /// 8 | /// 对应PPT的Shape Path 9 | /// 10 | public readonly struct ShapePath 11 | { 12 | /// 13 | /// 创建PPT的Geometry Path 14 | /// 15 | /// OpenXml Path字符串 16 | /// OpenXml的Path Fill Mode:默认为Norm 17 | /// 是否有轮廓:默认为True 18 | /// 指定使用 3D 拉伸可能在此路径:默认为False 19 | /// 指定的宽度或在路径坐标系统中应在使用的最大的 x 坐标 20 | /// 指定框架的高度或在路径坐标系统中应在使用的最大的 y 坐标 21 | /// 参考文档:Ecma Office Open XML Part 1 - Fundamentals And Markup Language Reference - 20.1.9.15 path (Shape Path) 22 | public ShapePath(string path, PathFillModeValues? fillMode = PathFillModeValues.Norm, bool? isStroke = true, bool? isExtrusionOk = false, double? emuWidth = null, double? emuHeight = null) 23 | { 24 | Path = path; 25 | IsStroke = isStroke ?? true; 26 | FillMode = fillMode ?? PathFillModeValues.Norm; 27 | IsFilled = fillMode is not PathFillModeValues.None; 28 | IsExtrusionOk = isExtrusionOk ?? false; 29 | Width = emuWidth.HasValue ? new Emu(emuWidth.Value) : null; 30 | Height = emuHeight.HasValue ? new Emu(emuHeight.Value) : null; 31 | } 32 | 33 | /// 34 | /// 创建PPT的Geometry Path 35 | /// 36 | /// OpenXml Path字符串 37 | /// 指定的宽度或在路径坐标系统中应在使用的最大的 x 坐标 38 | /// 指定框架的高度或在路径坐标系统中应在使用的最大的 y 坐标 39 | public ShapePath(string path, double eumWidth, double eumHeight) : this(path, PathFillModeValues.Norm, emuWidth: eumWidth, emuHeight: eumHeight) 40 | { 41 | 42 | } 43 | 44 | /// 45 | /// 是否填充 46 | /// 47 | public bool IsFilled { get; } 48 | 49 | /// 50 | /// OpenXml 的 Path Stroke, 默认true 51 | /// 52 | public bool IsStroke { get; } 53 | 54 | /// 55 | /// OpenXml的Path Fill Mode 56 | /// 57 | public PathFillModeValues FillMode { get; } 58 | 59 | /// 60 | ///OpenXml Path字符串 61 | /// 62 | public string Path { get; } 63 | 64 | /// 65 | /// 指定使用 3D 拉伸可能在此路径,默认false或0 66 | /// 67 | public bool IsExtrusionOk { get; } 68 | 69 | /// 70 | /// 指定的宽度或在路径坐标系统中应在使用的最大的 x 坐标。默认是 0 的值 71 | /// 72 | public Emu? Width { get; } 73 | 74 | /// 75 | /// 指定框架的高度或在路径坐标系统中应在使用的最大的 y 坐标。默认是 0 的值 76 | /// 77 | public Emu? Height { get; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/SlideEmuSize.cs: -------------------------------------------------------------------------------- 1 | using dotnetCampus.OpenXmlUnitConverter; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.Contexts 4 | { 5 | /// 6 | /// 页面的使用 EMU 单位表示的 Size 尺寸 7 | /// 8 | public readonly struct SlideEmuSize 9 | { 10 | /// 11 | /// 创建使用 EMU 单位表示的 Size 尺寸 12 | /// 13 | /// 14 | /// 15 | public SlideEmuSize(Emu width, Emu height) 16 | { 17 | Width = width; 18 | Height = height; 19 | } 20 | 21 | /// 22 | /// 宽度 23 | /// 24 | public Emu Width { get; } 25 | /// 26 | /// 高度 27 | /// 28 | public Emu Height { get; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Contexts/SvgPath.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.Contexts 4 | { 5 | /// 6 | /// 提供包含Svg Path信息的类 7 | /// 8 | public class SvgPath 9 | { 10 | /// 11 | /// 创建 路径信息 12 | /// 13 | /// 几何形状类型 14 | /// Svg Path字符串 15 | /// 形状文本框 16 | /// Svg 多路径 17 | /// 自定义形状上下文信息 18 | public SvgPath(ShapeTypeValues? geometryShapeType, string? svgPathString, EmuShapeTextRectangle shapeTextRectangle, ShapePath[]? multiShapePaths, CustomGeometryInfo? customGeometryInfo = null) 19 | { 20 | ShapeType = geometryShapeType; 21 | SvgPathString = svgPathString; 22 | ShapeTextRectangle = shapeTextRectangle; 23 | MultiShapePaths = multiShapePaths; 24 | CustomGeometryInfo = customGeometryInfo; 25 | } 26 | 27 | /// 28 | /// 形状类型 29 | /// 30 | public ShapeTypeValues? ShapeType { get; } 31 | 32 | /// 33 | /// Svg Path字符串 34 | /// 35 | public string? SvgPathString { get; } 36 | 37 | /// 38 | /// 形状的文本框 39 | /// 40 | public EmuShapeTextRectangle ShapeTextRectangle { get; } 41 | 42 | /// 43 | ///Shape Path 多路径 44 | /// 45 | public ShapePath[]? MultiShapePaths { get; } 46 | 47 | /// 48 | /// 自定义形状上下文信息 49 | /// 50 | public CustomGeometryInfo? CustomGeometryInfo { get; } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/CommonElement/Context_/ITransformData.cs: -------------------------------------------------------------------------------- 1 | using dotnetCampus.OpenXmlUnitConverter; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement 4 | { 5 | /// 6 | /// 变换信息,包括坐标和尺寸旋转翻转等信息 7 | /// 8 | public interface ITransformData 9 | { 10 | /// 11 | /// X 坐标 12 | /// 13 | Emu OffsetX { get; } 14 | /// 15 | /// Y 坐标 16 | /// 17 | Emu OffsetY { get; } 18 | /// 19 | /// 宽度 20 | /// 21 | Emu Width { get; } 22 | /// 23 | /// 高度 24 | /// 25 | Emu Height { get; } 26 | /// 27 | /// 旋转 28 | /// 29 | Degree Rotation { get; } 30 | /// 31 | /// 是否水平翻转 32 | /// 33 | bool HorizontalFlip { get; } 34 | /// 35 | /// 是否垂直翻转 36 | /// 37 | bool VerticalFlip { get; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/CommonElement/Context_/TransformData.cs: -------------------------------------------------------------------------------- 1 | using dotnetCampus.OpenXmlUnitConverter; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement 4 | { 5 | class TransformData : 6 | // 继承 OpenXmlLeafElement 是为了做缓存,可以放在 OpenXML 元素 7 | OpenXmlLeafElement, 8 | ITransformData 9 | { 10 | public Emu OffsetX { get; set; } = new Emu(0); 11 | public Emu OffsetY { get; set; } = new Emu(0); 12 | public Emu Width { get; set; } = new Emu(0); 13 | public Emu Height { get; set; } = new Emu(0); 14 | public Degree Rotation { get; set; } = new Degree(0); 15 | public bool HorizontalFlip { get; set; } = false; 16 | public bool VerticalFlip { get; set; } = false; 17 | 18 | // 以下是继承 OpenXmlLeafElement 需要实现的属性 19 | public override string InnerXml { get => string.Empty; set { } } 20 | public override string Prefix => string.Empty; 21 | public override string InnerText { get => string.Empty; protected set { } } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Primitive/Colors_/ARgbColor.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.Primitive 4 | { 5 | /// 6 | /// 用 A R G B 表示的颜色 7 | /// 8 | public class ARgbColor 9 | { 10 | /// 11 | /// 创建 A R G B 颜色 12 | /// 13 | public ARgbColor() 14 | { 15 | } 16 | 17 | /// 18 | /// 创建 A R G B 颜色 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public ARgbColor(byte a, byte r, byte g, byte b) 25 | { 26 | A = a; 27 | R = r; 28 | G = g; 29 | B = b; 30 | } 31 | 32 | /// 33 | /// 表示透明色 34 | /// 35 | public byte A { set; get; } 36 | 37 | /// 38 | /// 表示红色 39 | /// 40 | public byte R { set; get; } 41 | 42 | /// 43 | /// 表示绿色 44 | /// 45 | public byte G { set; get; } 46 | 47 | /// 48 | /// 表示蓝色 49 | /// 50 | public byte B { set; get; } 51 | 52 | /// 53 | /// 转换为 类型 54 | /// 55 | /// 56 | public ARgbColorModelHex ToRgbColorModelHex() 57 | { 58 | var rgbColorModelHex = new ARgbColorModelHex(this); 59 | return rgbColorModelHex; 60 | } 61 | 62 | /// 63 | public override string ToString() 64 | { 65 | return $"{A:X2}{R:X2}{G:X2}{B:X2}"; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Primitive/Colors_/ARgbColorModelHex.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | using dotnetCampus.OpenXmlUnitConverter; 4 | 5 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.Primitive 6 | { 7 | /// 8 | /// 用 ARGB 表示的颜色,支持和 进行转换 9 | /// 10 | public class ARgbColorModelHex : RgbColorModelHex 11 | { 12 | /// 13 | /// 用 ARGB 表示的颜色 14 | /// 15 | /// 16 | public ARgbColorModelHex(in ARgbColor color) 17 | { 18 | var hexString = $"{color.R:X2}{color.G:X2}{color.B:X2}"; 19 | Val = new HexBinaryValue(hexString); 20 | if (color.A != 0xFF) 21 | { 22 | var percentage = new Percentage(color.A * 100000 / 0xFF); 23 | Alpha alpha = new Alpha 24 | { 25 | Val = percentage.IntValue 26 | }; 27 | AddChild(alpha); 28 | } 29 | 30 | Color = color; 31 | } 32 | 33 | /// 34 | /// 颜色 35 | /// 36 | public ARgbColor Color { get; } 37 | 38 | /// 39 | public override string ToString() => Color.ToString(); 40 | 41 | /// 42 | /// 在 OpenXML SDK 里面判断的是对象的类型,因此如果给 SolidFill 设置 RgbColorModelHex 属性为 ARgbColorModelHex 对象,那么将会在 SolidFill 的 RgbColorModelHex 拿到的是空值 43 | /// 44 | /// 45 | public RgbColorModelHex ToRgbColorModelHex() 46 | { 47 | return (RgbColorModelHex) Clone(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Primitive/Colors_/BrushFill.cs: -------------------------------------------------------------------------------- 1 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters 2 | { 3 | /// 4 | /// 颜色填充 5 | /// 6 | /// 这是因为在 OpenXML 里面,各个颜色填充没有共同的基类,为了转换之间有单位,因此特别定义此类型 7 | public class BrushFill 8 | { 9 | /// 10 | /// 创建颜色填充 11 | /// 12 | /// 13 | public BrushFill(OpenXmlElement fill) 14 | { 15 | _fill = fill; 16 | } 17 | 18 | /// 19 | /// 获取填充 20 | /// 21 | /// 22 | /// 23 | public T? GetFill() where T : OpenXmlElement 24 | { 25 | var value = _fill as T; 26 | return value; 27 | } 28 | 29 | /// 30 | /// 添加到 OpenXML 元素里面 31 | /// 32 | /// 33 | public void AddToElement(OpenXmlElement element) 34 | { 35 | if (_fill.Clone() is OpenXmlElement openXmlElement) 36 | { 37 | element.Append(new[] { openXmlElement }); 38 | } 39 | } 40 | 41 | private readonly OpenXmlElement _fill; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/FormulaCalculators/FormulaCalculateContext.cs: -------------------------------------------------------------------------------- 1 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.FormulaCalculators 2 | { 3 | /// 4 | /// 形状的几何公式计算上下文 5 | /// 6 | public struct FormulaCalculateContext 7 | { 8 | /// 9 | /// 形状的几何公式计算参数 10 | /// 11 | public double[] Arguments { set; get; } 12 | 13 | /// 14 | /// 表示第一个参数 15 | /// 16 | public double X => Arguments[0]; 17 | 18 | /// 19 | /// 表示第二个参数 20 | /// 21 | public double Y => Arguments[1]; 22 | 23 | /// 24 | /// 表示第三个参数 25 | /// 26 | public double Z => Arguments[2]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/FormulaCalculators/FormulaConvertContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.FormulaCalculators 4 | { 5 | /// 6 | /// 公式转换的上下文信息 7 | /// 8 | public struct FormulaConvertContext 9 | { 10 | /// 11 | /// 表达式名,对应 OpenXML 的 <a:gd name="T0" fmla="*/ 7 w 18" /> 中的 name 属性 12 | /// 13 | public string Name { set; get; } 14 | /// 15 | /// 输入的表达式,对应 OpenXML 的 <a:gd name="T0" fmla="*/ 7 w 18" /> 中的 fmla 属性 16 | /// 17 | public string InputText { set; get; } 18 | 19 | /// 20 | /// 变量池 21 | /// 22 | public Dictionary VariablePool { set; get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/FormulaCalculators/FormulaStringReader.cs: -------------------------------------------------------------------------------- 1 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.FormulaCalculators 2 | { 3 | /// 4 | /// 公式的字符串读取器 5 | /// 6 | public struct FormulaStringReader 7 | { 8 | /// 9 | /// 创建公式的字符串读取器 10 | /// 11 | /// 12 | /// 13 | public FormulaStringReader(string inputText, int currentOffset = 0) 14 | { 15 | _inputText = inputText; 16 | _currentOffset = currentOffset; 17 | } 18 | 19 | /// 20 | /// 读取下一个值 21 | /// 22 | /// 23 | public string? ReadNextValue() 24 | { 25 | if (_currentOffset >= _inputText.Length) 26 | { 27 | return null; 28 | } 29 | 30 | var i = _currentOffset; 31 | 32 | for (; i < _inputText.Length; i++) 33 | { 34 | if (_inputText[i] != ' ') 35 | { 36 | // 读取掉一些空格 37 | break; 38 | } 39 | } 40 | 41 | _currentOffset = i; 42 | 43 | for (; i < _inputText.Length; i++) 44 | { 45 | if (_inputText[i] == ' ') 46 | { 47 | break; 48 | } 49 | } 50 | 51 | var length = i - _currentOffset; 52 | if (length == 0) 53 | { 54 | return null; 55 | } 56 | 57 | var result = _inputText.Substring(_currentOffset, length); 58 | 59 | _currentOffset = i; 60 | 61 | return result; 62 | } 63 | 64 | private readonly string _inputText; 65 | private int _currentOffset; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/ActionButtonBlankGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | 6 | using dotnetCampus.OpenXmlUnitConverter; 7 | 8 | using static DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters.ShapeGeometryFormulaHelper; 9 | 10 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 11 | 12 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 13 | { 14 | /// 15 | /// 动作按钮: 空白 16 | /// 17 | public class ActionButtonBlankGeometry : ShapeGeometryBase 18 | { 19 | /// 20 | public override string? ToGeometryPathString(EmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | /// 26 | public override ShapePath[]? GetMultiShapePaths(EmuSize emuSize, AdjustValueList? adjusts = null) 27 | { 28 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 29 | 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | 48 | var shapePaths = new ShapePath[1]; 49 | 50 | // 51 | // 52 | // 53 | // 54 | var currentPoint = new EmuPoint(l, t); 55 | var stringPath = new StringBuilder(); 56 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 57 | // 58 | // 59 | // 60 | currentPoint = LineToToString(stringPath, r, t); 61 | // 62 | // 63 | // 64 | currentPoint = LineToToString(stringPath, r, b); 65 | // 66 | // 67 | // 68 | currentPoint = LineToToString(stringPath, l, b); 69 | // 70 | stringPath.Append("z "); 71 | shapePaths[0] = new ShapePath(stringPath.ToString()); 72 | 73 | 74 | // 75 | InitializeShapeTextRectangle(l, t, r, b); 76 | 77 | return shapePaths; 78 | } 79 | } 80 | 81 | 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/BentConnector2Geometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 连接符:肘形2 15 | /// 16 | public class BentConnector2Geometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string? ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | return null; 22 | } 23 | 24 | /// 25 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | 43 | 44 | var shapePaths = new ShapePath[1]; 45 | // 46 | // 47 | // 48 | // 49 | var currentPoint = new EmuPoint(l, t); 50 | var stringPath = new StringBuilder(); 51 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 52 | // 53 | // 54 | // 55 | currentPoint = LineToToString(stringPath, r, t); 56 | // 57 | // 58 | // 59 | currentPoint = LineToToString(stringPath, r, b); 60 | // 61 | 62 | shapePaths[0] = new ShapePath(stringPath.ToString(), PathFillModeValues.None); 63 | 64 | // 65 | InitializeShapeTextRectangle(l, t, r, b); 66 | 67 | return shapePaths; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/BentConnector3Geometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 连接符:肘形3 15 | /// 16 | public class BentConnector3Geometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string? ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | return null; 22 | } 23 | 24 | /// 25 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | // 29 | // 30 | // 31 | var customAdj1 = adjusts?.GetAdjustValue("adj1"); 32 | var adj1 = customAdj1 ?? 50000d; 33 | 34 | // 35 | // 36 | // 37 | var x1 = w * adj1 / 100000; 38 | 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | // 51 | // 52 | // 53 | // 54 | // 55 | 56 | var shapePaths = new ShapePath[1]; 57 | // 58 | // 59 | // 60 | // 61 | var currentPoint = new EmuPoint(l, t); 62 | var stringPath = new StringBuilder(); 63 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 64 | // 65 | // 66 | // 67 | currentPoint = LineToToString(stringPath, x1, t); 68 | // 69 | // 70 | // 71 | currentPoint = LineToToString(stringPath, x1, b); 72 | // 73 | // 74 | // 75 | currentPoint = LineToToString(stringPath, r, b); 76 | // 77 | shapePaths[0] = new ShapePath(stringPath.ToString(), PathFillModeValues.None); 78 | 79 | // 80 | InitializeShapeTextRectangle(l, t, r, b); 81 | 82 | return shapePaths; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/CurvedConnector2Geometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | 14 | /// 15 | /// 连接符:曲线2 16 | /// 17 | public class CurvedConnector2Geometry : ShapeGeometryBase 18 | { 19 | /// 20 | public override string? ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | /// 26 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 27 | { 28 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | 42 | 43 | var shapePaths = new ShapePath[1]; 44 | // 45 | // 46 | // 47 | // 48 | var currentPoint = new EmuPoint(l, t); 49 | var stringPath = new StringBuilder(); 50 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 51 | // 52 | // 53 | // 54 | // 55 | // 56 | currentPoint = CubicBezToString(stringPath, wd2, t, r, hd2, r, b); 57 | // 58 | shapePaths[0] = new ShapePath(stringPath.ToString(), PathFillModeValues.None); 59 | 60 | // 61 | InitializeShapeTextRectangle(l, t, r, b); 62 | 63 | return shapePaths; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/DiamondGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 菱形 15 | /// 16 | internal class DiamondGeometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, 22 | wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 23 | 24 | // 25 | // 26 | // 27 | // 28 | 29 | // 30 | var ir = w * 3 / 4; 31 | // 32 | var ib = h * 3 / 4; 33 | 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | // 51 | 52 | var currentPoint = new EmuPoint(l, vc); 53 | var stringPath = new StringBuilder(); 54 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 55 | // 56 | // 57 | // 58 | _ = LineToToString(stringPath, hc, t); 59 | // 60 | // 61 | // 62 | _ = LineToToString(stringPath, r, vc); 63 | // 64 | // 65 | // 66 | _ = LineToToString(stringPath, hc, b); 67 | 68 | stringPath.Append("z"); 69 | 70 | // 71 | InitializeShapeTextRectangle(wd4, hd4, ir, ib); 72 | 73 | return stringPath.ToString(); 74 | } 75 | 76 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 77 | { 78 | var shapePaths = new ShapePath[1]; 79 | shapePaths[0] = new ShapePath(ToGeometryPathString(emuSize, adjusts)); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/FlowChartExtractGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | 6 | using dotnetCampus.OpenXmlUnitConverter; 7 | 8 | using static DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters.ShapeGeometryFormulaHelper; 9 | 10 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 11 | 12 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 13 | { 14 | /// 15 | /// 流程图: 摘录 16 | /// 17 | public class FlowChartExtractGeometry : ShapeGeometryBase 18 | { 19 | 20 | public override string? ToGeometryPathString(EmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | public override ShapePath[]? GetMultiShapePaths(EmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | 29 | // 30 | // 31 | // 32 | 33 | // 34 | var x2 = w * 3 / 4; 35 | 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | 51 | 52 | //设置Emu转Pixel的精度为小数点4位,防止精度不够被转为0 53 | UnitPrecision = 4; 54 | var shapePaths = new ShapePath[1]; 55 | 56 | // 57 | var shapePathWidth = 2d; 58 | var shapePathHeight = 2d; 59 | // 60 | // 61 | // 62 | var currentPoint = new EmuPoint(0, 2); 63 | var stringPath = new StringBuilder(); 64 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 65 | // 66 | // 67 | // 68 | currentPoint = LineToToString(stringPath, 1, 0); 69 | // 70 | // 71 | // 72 | currentPoint = LineToToString(stringPath, 2, 2); 73 | // 74 | stringPath.Append("z "); 75 | shapePaths[0] = new ShapePath(stringPath.ToString(), emuWidth: shapePathWidth, emuHeight: shapePathHeight); 76 | 77 | 78 | // 79 | InitializeShapeTextRectangle(wd4, vc, x2, b); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/FlowChartManualInputGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | 6 | using dotnetCampus.OpenXmlUnitConverter; 7 | 8 | using static DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters.ShapeGeometryFormulaHelper; 9 | 10 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 11 | 12 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 13 | { 14 | /// 15 | /// 流程图: 手动输入 16 | /// 17 | public class FlowChartManualInputGeometry : ShapeGeometryBase 18 | { 19 | 20 | public override string? ToGeometryPathString(EmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | public override ShapePath[]? GetMultiShapePaths(EmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | 47 | 48 | //设置Emu转Pixel的精度为小数点4位,防止精度不够被转为0 49 | UnitPrecision = 4; 50 | var shapePaths = new ShapePath[1]; 51 | 52 | // 53 | var shapePathWidth = 5d; 54 | var shapePathHeight = 5d; 55 | // 56 | // 57 | // 58 | var currentPoint = new EmuPoint(0, 1); 59 | var stringPath = new StringBuilder(); 60 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 61 | // 62 | // 63 | // 64 | currentPoint = LineToToString(stringPath, 5, 0); 65 | // 66 | // 67 | // 68 | currentPoint = LineToToString(stringPath, 5, 5); 69 | // 70 | // 71 | // 72 | currentPoint = LineToToString(stringPath, 0, 5); 73 | // 74 | stringPath.Append("z "); 75 | shapePaths[0] = new ShapePath(stringPath.ToString(), emuWidth: shapePathWidth, emuHeight: shapePathHeight); 76 | 77 | 78 | // 79 | InitializeShapeTextRectangle(l, hd5, r, b); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/FlowChartMergeGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | 6 | using dotnetCampus.OpenXmlUnitConverter; 7 | 8 | using static DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters.ShapeGeometryFormulaHelper; 9 | 10 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 11 | 12 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 13 | { 14 | /// 15 | /// 流程图: 合并 16 | /// 17 | public class FlowChartMergeGeometry : ShapeGeometryBase 18 | { 19 | 20 | public override string? ToGeometryPathString(EmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | public override ShapePath[]? GetMultiShapePaths(EmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | 29 | // 30 | // 31 | // 32 | 33 | // 34 | var x2 = w * 3 / 4; 35 | 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | 51 | 52 | //设置Emu转Pixel的精度为小数点4位,防止精度不够被转为0 53 | UnitPrecision = 4; 54 | var shapePaths = new ShapePath[1]; 55 | 56 | // 57 | var shapePathWidth = 2d; 58 | var shapePathHeight = 2d; 59 | // 60 | // 61 | // 62 | var currentPoint = new EmuPoint(0, 0); 63 | var stringPath = new StringBuilder(); 64 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 65 | // 66 | // 67 | // 68 | currentPoint = LineToToString(stringPath, 2, 0); 69 | // 70 | // 71 | // 72 | currentPoint = LineToToString(stringPath, 1, 2); 73 | // 74 | stringPath.Append("z "); 75 | shapePaths[0] = new ShapePath(stringPath.ToString(), emuWidth: shapePathWidth, emuHeight: shapePathHeight); 76 | 77 | 78 | // 79 | InitializeShapeTextRectangle(wd4, t, x2, vc); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/FlowChartProcessGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | 6 | using dotnetCampus.OpenXmlUnitConverter; 7 | 8 | using static DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters.ShapeGeometryFormulaHelper; 9 | 10 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 11 | 12 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 13 | { 14 | /// 15 | /// 流程图: 过程 16 | /// 17 | public class FlowChartProcessGeometry : ShapeGeometryBase 18 | { 19 | 20 | public override string? ToGeometryPathString(EmuSize emuSize, AdjustValueList? adjusts = null) 21 | { 22 | return null; 23 | } 24 | 25 | public override ShapePath[]? GetMultiShapePaths(EmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | 47 | //设置Emu转Pixel的精度为小数点4位,防止精度不够被转为0 48 | UnitPrecision = 4; 49 | 50 | var shapePaths = new ShapePath[1]; 51 | 52 | // 53 | var shapePathWidth = 1d; 54 | var shapePathHeight = 1d; 55 | // 56 | // 57 | // 58 | var currentPoint = new EmuPoint(0, 0); 59 | var stringPath = new StringBuilder(); 60 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 61 | // 62 | // 63 | // 64 | currentPoint = LineToToString(stringPath, 1, 0); 65 | // 66 | // 67 | // 68 | currentPoint = LineToToString(stringPath, 1, 1); 69 | // 70 | // 71 | // 72 | currentPoint = LineToToString(stringPath, 0, 1); 73 | // 74 | stringPath.Append("z "); 75 | shapePaths[0] = new ShapePath(stringPath.ToString(), emuWidth: shapePathWidth, emuHeight: shapePathHeight); 76 | 77 | 78 | // 79 | InitializeShapeTextRectangle(l, t, r, b); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/LineGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 直线 15 | /// 16 | public class LineGeometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string? ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | return null; 22 | } 23 | 24 | /// 25 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 26 | { 27 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 28 | // 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | 39 | var shapePaths = new ShapePath[1]; 40 | // 41 | // 42 | // 43 | // 44 | var currentPoint = new EmuPoint(l, t); 45 | var stringPath = new StringBuilder(); 46 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 47 | // 48 | // 49 | // 50 | currentPoint = LineToToString(stringPath, r, b); 51 | // 52 | shapePaths[0] = new ShapePath(stringPath.ToString()); 53 | 54 | return shapePaths; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/RectangleGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 矩形 15 | /// 16 | internal class RectangleGeometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, 22 | wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 23 | 24 | // 25 | // 26 | // 27 | // 28 | // 29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | 42 | // 43 | // 44 | // 45 | var currentPoint = new EmuPoint(l, t); 46 | var stringPath = new StringBuilder(); 47 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 48 | // 49 | // 50 | // 51 | _ = LineToToString(stringPath, r, t); 52 | // 53 | // 54 | // 55 | _ = LineToToString(stringPath, r, b); 56 | // 57 | // 58 | // 59 | _ = LineToToString(stringPath, l, b); 60 | 61 | stringPath.Append("z"); 62 | 63 | // 64 | InitializeShapeTextRectangle(l, t, r, b); 65 | 66 | return stringPath.ToString(); 67 | } 68 | 69 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 70 | { 71 | var shapePaths = new ShapePath[1]; 72 | shapePaths[0] = new ShapePath(ToGeometryPathString(emuSize, adjusts)); 73 | 74 | return shapePaths; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/RightTriangleGeometry.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using DocumentFormat.OpenXml.Drawing; 4 | using DocumentFormat.OpenXml.Flatten.Contexts; 5 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 6 | 7 | using dotnetCampus.OpenXmlUnitConverter; 8 | 9 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 12 | { 13 | /// 14 | /// 直角三角形 15 | /// 16 | internal class RightTriangleGeometry : ShapeGeometryBase 17 | { 18 | /// 19 | public override string ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 20 | { 21 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, 22 | wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 23 | 24 | // 25 | // 26 | // 27 | // 28 | // 29 | 30 | // 31 | var it = h * 7 / 12; 32 | // 33 | var ir = w * 7 / 12; 34 | // 35 | var ib = h * 11 / 12; 36 | 37 | // 38 | // 39 | // 40 | // 41 | // 42 | // 43 | // 44 | // 45 | // 46 | // 47 | // 48 | // 49 | // 50 | // 51 | 52 | // 53 | // 54 | // 55 | var currentPoint = new EmuPoint(l, b); 56 | var stringPath = new StringBuilder(); 57 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 58 | // 59 | // 60 | // 61 | _ = LineToToString(stringPath, l, t); 62 | // 63 | // 64 | // 65 | _ = LineToToString(stringPath, r, b); 66 | 67 | stringPath.Append("z"); 68 | 69 | // 70 | var wd12 = w / 12; 71 | InitializeShapeTextRectangle(wd12, it, ir, ib); 72 | 73 | return stringPath.ToString(); 74 | } 75 | 76 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 77 | { 78 | var shapePaths = new ShapePath[1]; 79 | shapePaths[0] = new ShapePath(ToGeometryPathString(emuSize, adjusts)); 80 | 81 | return shapePaths; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeGeometryConverters/StraightConnector1Geometry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using DocumentFormat.OpenXml.Drawing; 8 | using DocumentFormat.OpenXml.Flatten.Contexts; 9 | using DocumentFormat.OpenXml.Flatten.ElementConverters.CommonElement; 10 | 11 | using dotnetCampus.OpenXmlUnitConverter; 12 | 13 | using ElementEmuSize = dotnetCampus.OpenXmlUnitConverter.EmuSize; 14 | 15 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.ShapeGeometryConverters 16 | { 17 | /// 18 | /// 直接箭头连接符 19 | /// 20 | public class StraightConnector1Geometry : ShapeGeometryBase 21 | { 22 | /// 23 | public override string? ToGeometryPathString(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 24 | { 25 | return null; 26 | } 27 | 28 | /// 29 | public override ShapePath[]? GetMultiShapePaths(ElementEmuSize emuSize, AdjustValueList? adjusts = null) 30 | { 31 | var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd6, cd8) = GetFormulaProperties(emuSize); 32 | // 33 | // 34 | // 35 | // 36 | // 37 | // 38 | // 39 | // 40 | // 41 | // 42 | 43 | var shapePaths = new ShapePath[1]; 44 | // 45 | // 46 | // 47 | // 48 | var currentPoint = new EmuPoint(l, t); 49 | var stringPath = new StringBuilder(); 50 | stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} "); 51 | // 52 | // 53 | // 54 | currentPoint = LineToToString(stringPath, r, b); 55 | // 56 | shapePaths[0] = new ShapePath(stringPath.ToString(), PathFillModeValues.None); 57 | 58 | return shapePaths; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapePropertiesAdapt.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters 4 | { 5 | /// 6 | /// 形状属性适配器 7 | /// 8 | public class ShapePropertiesAdapt 9 | { 10 | /// 11 | /// 形状属性 12 | /// 13 | public OpenXmlCompositeElement? ShapeProperties { get; } 14 | 15 | /// 16 | /// 创建形状属性适配器 17 | /// 18 | /// 19 | public ShapePropertiesAdapt(OpenXmlCompositeElement? shapeProperties) 20 | { 21 | ShapeProperties = shapeProperties; 22 | } 23 | 24 | /// 25 | /// 转换为形状属性适配器 26 | /// 27 | /// 28 | /// 29 | public static implicit operator ShapePropertiesAdapt(DocumentFormat.OpenXml.Presentation.ShapeProperties? shapeProperties) => 30 | new(shapeProperties); 31 | 32 | /// 33 | /// 转换为形状属性适配器 34 | /// 35 | /// 36 | /// 37 | public static implicit operator ShapePropertiesAdapt(DocumentFormat.OpenXml.Office.Drawing.ShapeProperties? shapeProperties) => 38 | new(shapeProperties); 39 | 40 | /// 41 | /// 获取Transform2D 42 | /// 43 | /// 44 | public Transform2D? GetTransform2D() 45 | { 46 | return ShapeProperties?.GetFirstChild(); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Shape/ShapeStyleAdapt.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters 4 | { 5 | /// 6 | /// 形状样式适配器 7 | /// 8 | public class ShapeStyleAdapt 9 | { 10 | /// 11 | /// 形状样式 12 | /// 13 | public OpenXmlCompositeElement? ShapeStyle { get; } 14 | 15 | /// 16 | /// 构建形状样式适配器 17 | /// 18 | /// 19 | public ShapeStyleAdapt(OpenXmlCompositeElement? shapeStyle) 20 | { 21 | ShapeStyle = shapeStyle; 22 | } 23 | 24 | /// 25 | /// 转换为形状样式适配器 26 | /// 27 | /// 28 | /// 29 | public static implicit operator ShapeStyleAdapt(DocumentFormat.OpenXml.Presentation.ShapeStyle? shapeStyle) => 30 | new(shapeStyle); 31 | 32 | /// 33 | /// 转换为形状样式适配器 34 | /// 35 | /// 36 | /// 37 | public static implicit operator ShapeStyleAdapt(DocumentFormat.OpenXml.Office.Drawing.ShapeStyle? shapeStyle) => 38 | new(shapeStyle); 39 | 40 | /// 41 | /// 获取FontReference 42 | /// 43 | /// 44 | public FontReference? GetFontReference() 45 | { 46 | return ShapeStyle?.GetFirstChild(); 47 | } 48 | 49 | /// 50 | /// 获取FontReference 51 | /// 52 | /// 53 | public LineReference? GetLineReference() 54 | { 55 | return ShapeStyle?.GetFirstChild(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Text/TextBodyAdapt.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using DocumentFormat.OpenXml.Drawing; 5 | 6 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.Text 7 | { 8 | 9 | /// 10 | /// 文本适配器 11 | /// 12 | /// 在 OpenXML 里面文本包含了 DocumentFormat.OpenXml.Presentation.TextBody 和 DocumentFormat.OpenXml.Drawing.TextBody 这两个类没有继承关系 13 | /// 但是有一些相同的属性,都可以作为文本 14 | public class TextBodyAdapt 15 | { 16 | /// 17 | /// 构建文本适配器 18 | /// 19 | /// 20 | public TextBodyAdapt(DocumentFormat.OpenXml.Presentation.TextBody textBody) 21 | { 22 | TextBody = textBody; 23 | 24 | BodyProperties = textBody.BodyProperties; 25 | ListStyle = textBody.ListStyle; 26 | } 27 | 28 | /// 29 | /// 构建文本适配器 30 | /// 31 | /// 32 | public TextBodyAdapt(DocumentFormat.OpenXml.Drawing.TextBody textBody) 33 | { 34 | TextBody = textBody; 35 | 36 | BodyProperties = textBody.BodyProperties; 37 | ListStyle = textBody.ListStyle; 38 | } 39 | 40 | /// 41 | /// 构建文本适配器 42 | /// 43 | /// 44 | public TextBodyAdapt(DocumentFormat.OpenXml.Office.Drawing.TextBody textBody) 45 | { 46 | TextBody = textBody; 47 | 48 | BodyProperties = textBody?.BodyProperties; 49 | ListStyle = textBody?.ListStyle; 50 | } 51 | 52 | /// 53 | /// 枚举子元素 54 | /// 55 | /// 56 | /// 57 | public IEnumerable Descendants() where T : OpenXmlElement => TextBody?.Descendants() ?? Enumerable.Empty(); 58 | 59 | /// 60 | /// 正文属性 61 | /// 62 | public BodyProperties? BodyProperties { get; } 63 | 64 | /// 65 | /// 文本列表样式 66 | /// 67 | public ListStyle? ListStyle { get; } 68 | 69 | private OpenXmlElement? TextBody { get; } 70 | 71 | /// 72 | /// 转为文本适配器 73 | /// 74 | /// 75 | public static implicit operator TextBodyAdapt?(DocumentFormat.OpenXml.Presentation.TextBody? textBody) 76 | => textBody is not null ? new TextBodyAdapt(textBody) : null; 77 | 78 | /// 79 | /// 转为文本适配器 80 | /// 81 | /// 82 | public static implicit operator TextBodyAdapt?(DocumentFormat.OpenXml.Drawing.TextBody? textBody) 83 | => textBody is not null ? new TextBodyAdapt(textBody) : null; 84 | 85 | /// 86 | /// 转为文本适配器 87 | /// 88 | /// 89 | public static implicit operator TextBodyAdapt?(DocumentFormat.OpenXml.Office.Drawing.TextBody? textBody) 90 | { 91 | return textBody is not null ? new TextBodyAdapt(textBody) : null; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ElementConverters/Text/TextBodyPropertiesExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using DocumentFormat.OpenXml.Flatten.Contexts; 8 | 9 | namespace DocumentFormat.OpenXml.Flatten.ElementConverters.Text 10 | { 11 | /// 12 | /// TextBody 的属性扩展方法 13 | /// 14 | public static class TextBodyPropertiesExtensions 15 | { 16 | /// 17 | /// 获取文本相对形状的边距,将会使用 lIns tIns rIns bIns 属性 18 | /// 19 | /// 20 | /// 21 | public static EmuTextMargin GetTextMargin(this DocumentFormat.OpenXml.Drawing.BodyProperties? bodyProperties) 22 | { 23 | if (bodyProperties is null) 24 | { 25 | return default; 26 | } 27 | // 可参考 Ecma-376 Ecma Office Open XML Part 1 - Fundamentals And Markup Language Reference 的 20.4.2.22 章内容 28 | // lIns tIns rIns bIns存在默认值 29 | var leftInset = bodyProperties.LeftInset ?? 91440d; 30 | var topInset = bodyProperties.TopInset ?? 45720d; 31 | var rightInset = bodyProperties.RightInset ?? 91440d; 32 | var bottomInset = bodyProperties.BottomInset ?? 45720d; 33 | 34 | var emuTextMargin = new EmuTextMargin(leftInset, topInset, rightInset, bottomInset); 35 | return emuTextMargin; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/CellFlattenStyle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts; 4 | 5 | using dotnetCampus.OpenXmlUnitConverter; 6 | 7 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables 8 | { 9 | /// 10 | /// 单元格样式 11 | /// 12 | public class CellFlattenStyle : IReadonlyCellFlattenStyle 13 | { 14 | /// 15 | /// 创建单元格样式 16 | /// 17 | public CellFlattenStyle() 18 | { 19 | } 20 | 21 | /// 22 | /// 创建单元格样式 23 | /// 24 | /// 25 | public CellFlattenStyle(IReadonlyCellFlattenStyle? reservedStyle) 26 | { 27 | ReservedStyle = reservedStyle; 28 | } 29 | 30 | /// 31 | public PixelSize Size 32 | { 33 | set => _size = value; 34 | get => _size ?? ReservedStyle?.Size ?? DefaultCellSize; 35 | } 36 | 37 | private PixelSize? _size; 38 | 39 | /// 40 | /// 默认 Excel 行为是 41 | /// - 列宽:72 pixel (8.38 Excel单位) 42 | /// - 行高:14.25 磅 43 | /// 44 | /// 列宽单位和像素计算方法:像素 =(列宽+0.62)* 8 45 | public static PixelSize DefaultCellSize => 46 | new PixelSize(new ExcelCellColumnWidth(8.38).ToPixel(), new Pixel(19)); 47 | 48 | 49 | /// 50 | public Pound FontSize 51 | { 52 | set => _fontSize = value; 53 | get => _fontSize ?? ReservedStyle?.FontSize ?? DefaultFontSize; 54 | } 55 | 56 | private Pound? _fontSize; 57 | 58 | /// 59 | /// 默认的字体是 11 磅的 60 | /// 61 | public static Pound DefaultFontSize => new Pound(11); 62 | 63 | 64 | /// 65 | public string FontName 66 | { 67 | set => _fontName = value; 68 | get => _fontName ?? ReservedStyle?.FontName ?? DefaultFontName; 69 | } 70 | 71 | private string? _fontName; 72 | 73 | /// 74 | /// 默认单元格字体名 75 | /// 76 | public const string DefaultFontName = "Arial"; 77 | 78 | /// 79 | public IReadonlyCellFlattenStyle BuildNewProperty(Action action) 80 | { 81 | // 这是参照文本库的设计,我也不知道这个设计是否在此是合理的 82 | var cellFlattenStyle = new CellFlattenStyle(this); 83 | action(cellFlattenStyle); 84 | return cellFlattenStyle; 85 | } 86 | 87 | private IReadonlyCellFlattenStyle? ReservedStyle { get; } 88 | } 89 | 90 | /// 91 | /// 单元格样式 92 | /// 93 | public interface IReadonlyCellFlattenStyle 94 | { 95 | /// 96 | /// 单元格的尺寸 97 | /// 98 | PixelSize Size { get; } 99 | 100 | /// 101 | /// 单元格默认的字号 102 | /// 103 | Pound FontSize { get; } 104 | 105 | /// 106 | /// 字体名 107 | /// 108 | string FontName { get; } 109 | 110 | /// 111 | /// 基于当前的构建出新的单元格样式 112 | /// 113 | /// 114 | /// 115 | IReadonlyCellFlattenStyle BuildNewProperty(Action action); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/Contexts/ExcelColumnWidth.cs: -------------------------------------------------------------------------------- 1 | using dotnetCampus.OpenXmlUnitConverter; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts 4 | { 5 | /// 6 | /// 表示单元格列宽单位 7 | /// 8 | /// 值和像素换算公式是 像素 =(列宽+0.62)* 8 9 | /// 此计算方法暂时没有找到文档 10 | public readonly struct ExcelCellColumnWidth 11 | { 12 | /// 13 | /// 表示单元格列宽单位 14 | /// 15 | /// 16 | public ExcelCellColumnWidth(double value) 17 | { 18 | Value = value; 19 | } 20 | 21 | private double Value { get; } 22 | 23 | /// 24 | /// 转换为使用像素单位 25 | /// 26 | /// 27 | public Pixel ToPixel() => new Pixel((Value + 0.62) * 8); 28 | 29 | /// 30 | public override string ToString() => ToPixel().ToString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/Contexts/ExcelRange.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts 4 | { 5 | /// 6 | /// 表示一定范围的 Excel 单元格,采用包含端点关系 7 | /// 8 | public readonly struct ExcelRange 9 | { 10 | /// 11 | /// 表示一定范围的 Excel 单元格 12 | /// 13 | public ExcelRange(ExcelPosition startPosition) : this(startPosition, startPosition) 14 | { 15 | } 16 | 17 | /// 18 | /// 表示一定范围的 Excel 单元格 19 | /// 20 | public ExcelRange(ExcelPosition startPosition, ExcelPosition endPosition) 21 | { 22 | StartPosition = startPosition; 23 | EndPosition = endPosition; 24 | } 25 | 26 | /// 27 | /// 起始的点,此点也被包含在范围内 28 | /// 29 | public ExcelPosition StartPosition { get; } 30 | 31 | /// 32 | /// 结束的点,此点也被包含在范围内 33 | /// 34 | public ExcelPosition EndPosition { get; } 35 | 36 | /// 37 | /// 表示整个表格的范围 38 | /// 39 | public static ExcelRange WholeRange => 40 | new ExcelRange(new ExcelPosition(0, 0), new ExcelPosition(int.MaxValue, int.MaxValue)); 41 | 42 | /// 43 | /// 包含的行数量 44 | /// 45 | public int RowCount => EndPosition.Row - StartPosition.Row + 1; 46 | 47 | /// 48 | /// 包含的列数量 49 | /// 50 | public int ColumnCount => EndPosition.Column - StartPosition.Column + 1; 51 | 52 | /// 53 | /// 判断给定的 是否在范围内 54 | /// 55 | /// [B1:F7] 包含 B2 也包含 B1 的值 56 | /// 57 | /// 58 | /// 59 | /// 60 | public bool Contain(ExcelPosition position) 61 | { 62 | // [B1:F7] 包含 B2 63 | return position.Row >= StartPosition.Row 64 | && position.Column >= StartPosition.Column 65 | && position.Row <= EndPosition.Row 66 | && position.Column <= EndPosition.Column; 67 | } 68 | 69 | /// 70 | public override string ToString() 71 | { 72 | return $"{StartPosition}:{EndPosition}"; 73 | } 74 | 75 | /// 76 | /// 尝试转换为表示一定范围的 Excel 单元格 77 | /// 78 | /// 79 | public static bool TryParse(string? excelRange, out ExcelRange range) 80 | { 81 | if (excelRange is null) 82 | { 83 | range = default; 84 | return false; 85 | } 86 | 87 | var match = Regex.Match(excelRange, @"([A-Z]+\d+):([A-Z]+\d+)"); 88 | 89 | if (match.Success) 90 | { 91 | // 如以下字符串 92 | // B1:F7 93 | // 在 Excel 的行是从 1 开始 94 | ExcelPosition? startPosition = ExcelPosition.ConvertFromString(match.Groups[1].Value); 95 | ExcelPosition? endPosition = ExcelPosition.ConvertFromString(match.Groups[2].Value); 96 | 97 | if (startPosition != null && endPosition != null) 98 | { 99 | range = new ExcelRange(startPosition.Value, endPosition.Value); 100 | return true; 101 | } 102 | } 103 | 104 | range = default; 105 | return false; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/Contexts/ExcelTableConvertContext.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Packaging; 2 | using DocumentFormat.OpenXml.Spreadsheet; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts 5 | { 6 | /// 7 | /// 转换的上下文 8 | /// 9 | public class ExcelTableConvertContext 10 | { 11 | /// 12 | /// 创建转换的上下文 13 | /// 14 | /// 15 | public ExcelTableConvertContext(SpreadsheetDocument spreadsheetDocument) 16 | { 17 | SpreadsheetDocument = spreadsheetDocument; 18 | } 19 | 20 | /// 21 | /// 文档 22 | /// 23 | public SpreadsheetDocument SpreadsheetDocument { get; } 24 | 25 | internal WorkbookPart? WorkbookPart => SpreadsheetDocument.WorkbookPart; 26 | internal Workbook? Workbook => WorkbookPart?.Workbook; 27 | internal Stylesheet? Stylesheet => WorkbookPart?.WorkbookStylesPart?.Stylesheet; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/SparseCellTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables 5 | { 6 | /// 7 | /// 稀疏的单元格表格 8 | /// 9 | public class SparseCellTable : IEnumerable 10 | { 11 | internal SparseCellTable(WorksheetFlatten currentWorksheet, List cellList) 12 | { 13 | CurrentWorksheet = currentWorksheet; 14 | _cellList = cellList; 15 | } 16 | 17 | /// 18 | /// 当前所在的工作表 19 | /// 20 | public WorksheetFlatten CurrentWorksheet { get; } 21 | 22 | private readonly List _cellList; 23 | 24 | /// 25 | public IEnumerator GetEnumerator() 26 | { 27 | return _cellList.GetEnumerator(); 28 | } 29 | 30 | /// 31 | IEnumerator IEnumerable.GetEnumerator() 32 | { 33 | return ((IEnumerable) _cellList).GetEnumerator(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/Utils/ExcelInfoHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | using DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts; 4 | using DocumentFormat.OpenXml.Spreadsheet; 5 | 6 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables.Utils 7 | { 8 | /// 9 | /// 获取 Excel 文档信息 10 | /// 11 | public static class ExcelInfoHelper 12 | { 13 | /// 14 | /// 获取作为 Ole 的范围 15 | /// 16 | /// 17 | /// 18 | public static ExcelRange? GetOleSize(this Workbook workbook) 19 | { 20 | var oleSize = workbook.GetFirstChild(); 21 | var oleSizeValue = oleSize?.Reference?.Value; 22 | if (oleSizeValue is null) 23 | { 24 | return null; 25 | } 26 | 27 | return StringToExcelRange(oleSizeValue); 28 | } 29 | 30 | internal static ExcelRange? StringToExcelRange(string? excelRange) 31 | { 32 | if (ExcelRange.TryParse(excelRange, out var range)) 33 | { 34 | return range; 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/Utils/SpreadsheetDocumentExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using DocumentFormat.OpenXml.Packaging; 8 | 9 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables.Utils 10 | { 11 | static class SpreadsheetDocumentExtension 12 | { 13 | public static SpreadsheetDocument GetSpreadsheetDocument(this WorksheetPart worksheetPart) => (SpreadsheetDocument) worksheetPart.OpenXmlPackage; 14 | 15 | public static WorkbookPart GetWorkbookPart(this WorksheetPart worksheetPart) => 16 | // 既然都能拿到 WorksheetPart 了,那么 WorkbookPart 一定不是空 17 | worksheetPart.GetSpreadsheetDocument().WorkbookPart!; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/ExcelTables/WorksheetsFlatten.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using DocumentFormat.OpenXml.Flatten.ExcelTables.Contexts; 6 | using DocumentFormat.OpenXml.Packaging; 7 | 8 | namespace DocumentFormat.OpenXml.Flatten.ExcelTables 9 | { 10 | /// 11 | /// 工作表集合 12 | /// 13 | public class WorksheetsFlatten : IReadOnlyList 14 | { 15 | /// 16 | /// 工作表集合 17 | /// 18 | /// 19 | /// 20 | internal WorksheetsFlatten(IEnumerable? worksheetParts, 21 | ExcelTableConvertContext context) 22 | { 23 | Context = context; 24 | if (worksheetParts is null) 25 | { 26 | InnerList = new WorksheetFlatten[0]; 27 | } 28 | else 29 | { 30 | InnerList = worksheetParts.Select(t => new WorksheetFlatten(t, Context)).ToList(); 31 | } 32 | } 33 | 34 | private IReadOnlyList InnerList { get; } 35 | private ExcelTableConvertContext Context { get; } 36 | 37 | /// 38 | public IEnumerator GetEnumerator() 39 | { 40 | return InnerList.GetEnumerator(); 41 | } 42 | 43 | IEnumerator IEnumerable.GetEnumerator() 44 | { 45 | return ((IEnumerable) InnerList).GetEnumerator(); 46 | } 47 | 48 | /// 49 | public int Count => InnerList.Count; 50 | 51 | /// 52 | public WorksheetFlatten this[int index] => InnerList[index]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Framework/Context/ElementContext.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Packaging; 2 | using DocumentFormat.OpenXml.Presentation; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.Framework.Context 5 | { 6 | /// 7 | /// 给每个元素独立的上下文信息 8 | /// 9 | /// 为什么定义结构体?原因是信息少,拷贝结构体性能损耗不多。而元素数量较多,创建类相对损耗比较多 10 | public readonly struct ElementContext 11 | { 12 | /// 13 | /// 创建给每个元素独立的上下文信息 14 | /// 15 | public ElementContext(OpenXmlElement originOpenXmlElement, SlideContext slideContext) 16 | { 17 | SlideContext = slideContext; 18 | OriginOpenXmlElement = originOpenXmlElement; 19 | 20 | GroupShape = originOpenXmlElement.Parent as GroupShape; 21 | } 22 | 23 | /// 24 | /// 页面上下文信息 25 | /// 26 | public SlideContext SlideContext { get; } 27 | 28 | /// 29 | /// 原始的 OpenXML 元素的对象。因为实际上传入的对象是被 Clone 一次的 30 | /// 31 | public OpenXmlElement OriginOpenXmlElement { get; } 32 | 33 | /// 34 | /// 如果元素放在组合内,那么此属性有值。否则是空 35 | /// 36 | public GroupShape? GroupShape { get; } 37 | 38 | /// 39 | /// 对应的元素最上层元素,如页面,如页面模版等 40 | /// 41 | public OpenXmlPartRootElement RootElement => SlideContext.RootElement; 42 | 43 | /// 44 | /// PPT文件 45 | /// 46 | public PresentationDocument Document => SlideContext.Document; 47 | 48 | /// 49 | /// 页面数据 50 | /// 这里不能直接认为元素就在slide上,还要考虑layout和master 51 | /// 52 | public OpenXmlPart GetCurrentPart(OpenXmlElement? _ = null) => SlideContext.GetCurrentPart(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Framework/Context/SlideContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | using DocumentFormat.OpenXml.Packaging; 6 | using DocumentFormat.OpenXml.Presentation; 7 | 8 | namespace DocumentFormat.OpenXml.Flatten.Framework.Context 9 | { 10 | /// 11 | /// 页面上下文 12 | /// 13 | public class SlideContext 14 | { 15 | /// 16 | /// 创建页面上下文 17 | /// 18 | /// 19 | /// 20 | public SlideContext(OpenXmlPartRootElement rootElement, PresentationDocument document) 21 | { 22 | RootElement = rootElement; 23 | Document = document; 24 | } 25 | 26 | /// 27 | /// 对应的元素最上层元素,如页面,如页面模版等 28 | /// 29 | public OpenXmlPartRootElement RootElement { get; } 30 | 31 | /// 32 | /// PPT文件 33 | /// 34 | public PresentationDocument Document { get; } 35 | 36 | /// 37 | /// 页面数据 38 | /// 这里不能直接认为元素就在slide上,还要考虑layout和master以及是否在主题上 39 | /// 40 | public OpenXmlPart GetCurrentPart(OpenXmlElement? element = null) 41 | { 42 | var openXmlPartRootElement = element?.Ancestors().FirstOrDefault(); 43 | if (openXmlPartRootElement is DocumentFormat.OpenXml.Drawing.Theme theme) 44 | { 45 | var themePart = theme.ThemePart; 46 | if (themePart != null) 47 | { 48 | return themePart; 49 | } 50 | } 51 | 52 | if (_currentPart != null) 53 | { 54 | return _currentPart; 55 | } 56 | 57 | if (RootElement is Slide slide) 58 | { 59 | _currentPart = slide.SlidePart; 60 | } 61 | else if (RootElement is SlideLayout slideLayout) 62 | { 63 | _currentPart = slideLayout.SlideLayoutPart; 64 | } 65 | else if (RootElement is SlideMaster slideMaster) 66 | { 67 | _currentPart = slideMaster.SlideMasterPart; 68 | } 69 | else 70 | { 71 | throw new ArgumentException($"{nameof(RootElement)} must be Slide or SlideLayout or SlideMaster"); 72 | } 73 | 74 | if (_currentPart is null) 75 | { 76 | #if DEBUG 77 | if (Debugger.IsAttached) 78 | { 79 | Debugger.Break(); 80 | } 81 | #endif 82 | throw new ArgumentException($"Can not find any part."); 83 | } 84 | 85 | return _currentPart; 86 | } 87 | 88 | private OpenXmlPart? _currentPart; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Framework/FlattenObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.Framework 5 | { 6 | /// 7 | /// 带继承的对象,可以继承属性的值 8 | /// 9 | /// 如果对象有设置属性,那么采用对象设置的属性 10 | /// 如果对象没有设置属性,尝试从 Reserved 属性读取继承的属性 11 | /// 如果没有 Reserved 属性,证明就是最底层的对象,采用传入的默认属性值 12 | public class FlattenObject 13 | { 14 | /// 15 | /// 创建带继承的对象 16 | /// 17 | /// 18 | public FlattenObject(FlattenObject? reserved = null) 19 | { 20 | Reserved = reserved; 21 | } 22 | 23 | private FlattenObject? Reserved { get; } 24 | private Dictionary ValueDictionary { get; } = new Dictionary(); 25 | 26 | /// 27 | /// 设置属性值 28 | /// 29 | /// 30 | /// 31 | protected void SetValue(object value, [CallerMemberName] string propertyName = null!) 32 | { 33 | ValueDictionary[propertyName] = value; 34 | } 35 | 36 | /// 37 | /// 获取属性值 38 | /// 39 | /// 40 | /// 41 | /// 42 | protected T? GetValue([CallerMemberName] string propertyName = null!) 43 | => GetValue(default!, propertyName); 44 | 45 | /// 46 | /// 获取属性值 47 | /// 48 | /// 49 | /// 50 | /// 51 | /// 52 | protected T GetValue(T defaultValue, [CallerMemberName] string propertyName = null!) 53 | { 54 | if (ValueDictionary.TryGetValue(propertyName, out var value)) 55 | { 56 | return (T) value; 57 | } 58 | else 59 | { 60 | if (Reserved is not null) 61 | { 62 | return Reserved.GetValue(defaultValue, propertyName); 63 | } 64 | else 65 | { 66 | return defaultValue; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Framework/OpenXmlCompositeElementFlattenProvider.cs: -------------------------------------------------------------------------------- 1 | namespace DocumentFormat.OpenXml.Flatten.Framework 2 | { 3 | class OpenXmlCompositeElementFlattenProvider 4 | : ElementFlattenProvider 5 | where TElement : OpenXmlCompositeElement 6 | { 7 | public OpenXmlCompositeElementFlattenProvider(params TElement?[] elementList) : base(elementList) 8 | { 9 | } 10 | 11 | public T GetOrCreateElement() where T : OpenXmlElement, new() 12 | { 13 | var result = Main.GetFirstChild(); 14 | 15 | if (result == null) 16 | { 17 | foreach (var element in ElementList) 18 | { 19 | if (element is null) continue; 20 | result = element.GetFirstChild(); 21 | if (result != null) 22 | { 23 | result = (T) result.Clone(); 24 | Main.AddChild(result); 25 | return result; 26 | } 27 | } 28 | } 29 | 30 | if (result is null) 31 | { 32 | return Main.GetOrCreateElement(); 33 | } 34 | 35 | return result; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Framework/OpenXmlElementFlattenConverterBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | using DocumentFormat.OpenXml.Flatten.Framework.Context; 5 | 6 | namespace DocumentFormat.OpenXml.Flatten.Framework 7 | { 8 | abstract class OpenXmlElementFlattenConverterBase : IOpenXmlElementFlattenConverter 9 | where T : OpenXmlElement 10 | { 11 | public bool IsMatch(OpenXmlElement element) => element is T; 12 | 13 | public OpenXmlElement Convert(OpenXmlElement element, ElementContext context) 14 | { 15 | return Convert((T) element, context); 16 | } 17 | 18 | protected abstract T Convert(T element, ElementContext context); 19 | } 20 | 21 | interface IOpenXmlElementFlattenConverter 22 | { 23 | bool IsMatch(OpenXmlElement element); 24 | OpenXmlElement Convert(OpenXmlElement element, ElementContext context); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | # C# files 4 | [*.cs] 5 | ## All files should be considered generated code. 6 | generated_code = true -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/IDirectoryEntry.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | /* This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 11 | * 12 | * The Original Code is OpenMCDF - Compound Document Format library. 13 | * 14 | * The Initial Developer of the Original Code is Federico Blaseotto.*/ 15 | 16 | 17 | using RedBlackTree; 18 | using System; 19 | 20 | 21 | namespace OpenMcdf 22 | { 23 | internal interface IDirectoryEntry : IComparable, IRBNode 24 | { 25 | int Child { get; set; } 26 | byte[] CreationDate { get; set; } 27 | byte[] EntryName { get; } 28 | string GetEntryName(); 29 | int LeftSibling { get; set; } 30 | byte[] ModifyDate { get; set; } 31 | string Name { get; } 32 | ushort NameLength { get; set; } 33 | void Read(System.IO.Stream stream, CFSVersion ver = CFSVersion.Ver_3); 34 | int RightSibling { get; set; } 35 | void SetEntryName(string entryName); 36 | int SID { get; set; } 37 | long Size { get; set; } 38 | int StartSetc { get; set; } 39 | int StateBits { get; set; } 40 | StgColor StgColor { get; set; } 41 | StgType StgType { get; set; } 42 | Guid StorageCLSID { get; set; } 43 | void Write(System.IO.Stream stream); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/RBTree/Color.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | namespace RedBlackTree 9 | { 10 | public enum Color { RED = 0, BLACK = 1 } 11 | } 12 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/RBTree/IRBNode.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | using System; 9 | 10 | namespace RedBlackTree 11 | { 12 | /// 13 | /// Red Black Node interface 14 | /// 15 | public interface IRBNode : IComparable 16 | { 17 | 18 | IRBNode Left 19 | { 20 | get; 21 | set; 22 | } 23 | 24 | IRBNode Right 25 | { 26 | get; 27 | set; 28 | } 29 | 30 | 31 | Color Color 32 | 33 | { get; set; } 34 | 35 | 36 | 37 | IRBNode Parent { get; set; } 38 | 39 | 40 | IRBNode Grandparent(); 41 | 42 | 43 | IRBNode Sibling(); 44 | // { 45 | //#if ASSERT 46 | // Debug.Assert(Parent != null); // Root node has no sibling 47 | //#endif 48 | // if (this == Parent.Left) 49 | // return Parent.Right; 50 | // else 51 | // return Parent.Left; 52 | // } 53 | 54 | IRBNode Uncle(); 55 | // { 56 | //#if ASSERT 57 | // Debug.Assert(Parent != null); // Root node has no uncle 58 | // Debug.Assert(Parent.Parent != null); // Children of root have no uncle 59 | //#endif 60 | // return Parent.Sibling(); 61 | // } 62 | // } 63 | 64 | void AssignValueTo(IRBNode other); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/RBTree/NodeOperation.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | namespace RedBlackTree 9 | { 10 | internal enum NodeOperation 11 | { 12 | LeftAssigned, RightAssigned, ColorAssigned, ParentAssigned, 13 | ValueAssigned 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/RBTree/RBTreeDuplicatedItemException.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | using System; 9 | 10 | namespace RedBlackTree 11 | { 12 | public class RBTreeDuplicatedItemException : RBTreeException 13 | { 14 | public RBTreeDuplicatedItemException(String msg) 15 | : base(msg) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/RBTree/RBTreeException.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是其他项目的源代码,不做更改,也不支持可空 2 | #pragma warning disable CS1591 // 这是其他项目的源代码,不做更改 3 | #pragma warning disable 618 // 这是其他项目的源代码,不做更改 4 | #pragma warning disable 1572 // 这是其他项目的源代码,不做更改 5 | #pragma warning disable 1573 6 | // ReSharper disable All 7 | 8 | using System; 9 | 10 | namespace RedBlackTree 11 | { 12 | public class RBTreeException : Exception 13 | { 14 | public RBTreeException(String msg) 15 | : base(msg) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/ByteArrayPool.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | // ReSharper disable once CheckNamespace 特别的命名空间 3 | 4 | namespace OpenMcdf 5 | { 6 | class ByteArrayPool : IByteArrayPool 7 | { 8 | public byte[] Rent(int minimumLength) 9 | { 10 | for (var i = 0; i < _bufferList.Length; i++) 11 | { 12 | var buffer = _bufferList[i]; 13 | 14 | if (buffer != null && buffer.Length >= minimumLength) 15 | { 16 | _bufferList[i] = null; 17 | return buffer; 18 | } 19 | } 20 | 21 | return new byte[minimumLength]; 22 | } 23 | 24 | public void Return(byte[]? byteList) 25 | { 26 | if (byteList == null || byteList.Length >= MaxBufferLength) 27 | { 28 | return; 29 | } 30 | 31 | for (var i = 0; i < _bufferList.Length; i++) 32 | { 33 | var buffer = _bufferList[i]; 34 | if (buffer is null || byteList.Length > buffer.Length) 35 | { 36 | buffer = byteList; 37 | _bufferList[i] = buffer; 38 | } 39 | } 40 | } 41 | 42 | private readonly byte[]?[] _bufferList = new byte[4][]; 43 | private const int MaxBufferLength = 8 * 1024; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/IByteArrayPool.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 特别的命名空间 2 | 3 | namespace OpenMcdf 4 | { 5 | /// 6 | /// 二进制数组缓存池 7 | /// 8 | public interface IByteArrayPool 9 | { 10 | /// 11 | /// 租借数组,将会返回大于或等于 长度的二进制数组,数组里面也许存在内容 12 | /// 13 | /// 14 | /// 15 | byte[] Rent(int minimumLength); 16 | 17 | /// 18 | /// 归还数组 19 | /// 20 | /// 21 | void Return(byte[] byteList); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/IStreamReader.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 特别的命名空间 2 | 3 | namespace OpenMcdf 4 | { 5 | internal interface IStreamReader 6 | { 7 | long Seek(long offset); 8 | byte ReadByte(); 9 | ushort ReadUInt16(); 10 | int ReadInt32(); 11 | uint ReadUInt32(); 12 | long ReadInt64(); 13 | ulong ReadUInt64(); 14 | byte[] ReadBytes(int count); 15 | byte[] ReadBytes(int count, out int readCount); 16 | void Close(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/ReadonlyCompoundFileItemComparer.cs: -------------------------------------------------------------------------------- 1 | #nullable disable // 这是迁移旧代码,不敢开可空 2 | // ReSharper disable CheckNamespace 特别的命名空间 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace OpenMcdf 7 | { 8 | internal class ReadonlyCompoundFileItemComparer : IComparer 9 | { 10 | public int Compare(ReadonlyCompoundFileItem x, ReadonlyCompoundFileItem y) 11 | { 12 | // X CompareTo Y : X > Y --> 1 ; X < Y --> -1 13 | return (x.DirEntry.CompareTo(y.DirEntry)); 14 | 15 | //Compare X < Y --> -1 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/SectorList.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 特别的命名空间 2 | 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace OpenMcdf 7 | { 8 | class SectorList 9 | { 10 | public SectorList(List idIndexList, Stream sourceStream, int sectorSize, SectorType type) 11 | { 12 | IdIndexList = idIndexList; 13 | SourceStream = sourceStream; 14 | SectorSize = sectorSize; 15 | Type = type; 16 | } 17 | 18 | public SectorList(Stream sourceStream, int sectorSize, SectorType type) 19 | { 20 | SourceStream = sourceStream; 21 | SectorSize = sectorSize; 22 | Type = type; 23 | 24 | IdIndexList = new List(); 25 | } 26 | 27 | public List IdIndexList { get; } 28 | 29 | public int Count => IdIndexList.Count; 30 | 31 | public Stream SourceStream { get; } 32 | 33 | public int SectorSize { get; } 34 | 35 | public SectorType Type { get; } 36 | 37 | public int Read(int sectorIndex, byte[] buffer, int position, int offset, int count) 38 | { 39 | var idIndex = IdIndexList[sectorIndex]; 40 | var sectorPosition = SectorSize + idIndex * SectorSize; 41 | 42 | var streamPosition = sectorPosition + position; 43 | SourceStream.Seek(streamPosition, SeekOrigin.Begin); 44 | return SourceStream.Read(buffer, offset, count); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/OpenMcdf/Readonly/StreamHelper.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable CheckNamespace 特别的命名空间 2 | 3 | using System; 4 | using System.IO; 5 | 6 | namespace OpenMcdf 7 | { 8 | static class StreamHelper 9 | { 10 | public static void CopyTo(this Stream sourceStream, Stream destinationStream, IByteArrayPool byteArrayPool, 11 | long position, long size) 12 | { 13 | sourceStream.Seek(position, SeekOrigin.Begin); 14 | const int defaultBufferLength = 4096; 15 | var bufferLength = (int) Math.Min(defaultBufferLength, size); 16 | var buffer = byteArrayPool.Rent(bufferLength); 17 | 18 | var readCount = 0; 19 | while (readCount < size) 20 | { 21 | var count = (int) Math.Min(size - readCount, bufferLength); 22 | var n = sourceStream.Read(buffer, 0, count); 23 | if (n == 0) 24 | { 25 | break; 26 | } 27 | readCount += n; 28 | 29 | destinationStream.Write(buffer, 0, n); 30 | } 31 | 32 | byteArrayPool.Return(buffer); 33 | } 34 | 35 | public static IStreamReader ToStreamReader(this Stream stream) 36 | { 37 | if (stream is IStreamReader reader) 38 | { 39 | return reader; 40 | } 41 | 42 | return new StreamRW(stream); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenMcdf/README.md: -------------------------------------------------------------------------------- 1 | # OpenMcdf 2 | 3 | 这是用来解析 OLE 文件的库,不依赖 Windows 平台的 Ole.dll 文件,完全由 C# 代码实现 4 | 5 | 开源地址: https://github.com/ironfede/openmcdf 6 | 7 | 本仓库用到了官方还没有合入的功能,参阅 [Add the readonly version by lindexi · Pull Request #87 · ironfede/openmcdf](https://github.com/ironfede/openmcdf/pull/87 ) 的更改,因此拷贝源代码到此仓库。请不要在本仓库更改 OpenMcdf 的代码。请到官方仓库更改 -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/OpenXmlElementFlatten.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using DocumentFormat.OpenXml.Flatten.ElementConverters; 4 | using DocumentFormat.OpenXml.Flatten.Framework; 5 | using DocumentFormat.OpenXml.Flatten.Framework.Context; 6 | 7 | namespace DocumentFormat.OpenXml.Flatten 8 | { 9 | /// 10 | /// 对 OpenXml 元素的属性拍平辅助类,属性拍平指的是从层层样式里面获取到属性的最终值 11 | /// 12 | public class OpenXmlElementFlatten 13 | { 14 | IReadOnlyList FlattenConverterList { get; } = 15 | new IOpenXmlElementFlattenConverter[] 16 | { 17 | new ShapeFlattenConverter(), 18 | new SmartArtElementFlattenConverter() 19 | }; 20 | 21 | /// 22 | /// 获取拍平属性后的 OpenXml 对象 23 | /// 24 | /// 25 | /// 26 | /// 是否需要复制新的 OpenXml 元素,不在原有的元素基础上修改属性值。默认值是在原有元素上修改值 27 | /// 28 | public OpenXmlElement GetFlattenElement(OpenXmlElement element, SlideContext context, bool shouldCloneOriginElement = false) 29 | { 30 | foreach (var shapeFlattenConverter in FlattenConverterList) 31 | { 32 | if (shapeFlattenConverter.IsMatch(element)) 33 | { 34 | var elementContext = new ElementContext(element, context); 35 | 36 | var convertingElement = element; 37 | if (shouldCloneOriginElement) 38 | { 39 | convertingElement = (OpenXmlElement) convertingElement.Clone(); 40 | } 41 | 42 | return shapeFlattenConverter.Convert(convertingElement, elementContext); 43 | } 44 | } 45 | 46 | return element; 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Utils/MathHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using DocumentFormat.OpenXml.Flatten.Contexts; 8 | 9 | using dotnetCampus.OpenXmlUnitConverter; 10 | 11 | namespace DocumentFormat.OpenXml.Flatten.Utils 12 | { 13 | static class MathHelper 14 | { 15 | public static bool NearlyEquals(EmuPoint a, EmuPoint b, double tolerance = 0.001) 16 | { 17 | return NearlyEquals(a.X.Value, b.X.Value, tolerance) && NearlyEquals(a.Y.Value, b.Y.Value, tolerance); 18 | } 19 | 20 | public static bool NearlyEquals(double a, double b, double tolerance = 0.001) 21 | { 22 | return System.Math.Abs(a - b) < tolerance; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/Utils/TextListStyleHelper.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml.Drawing; 2 | using DocumentFormat.OpenXml.Presentation; 3 | 4 | namespace DocumentFormat.OpenXml.Flatten.Utils 5 | { 6 | internal static class TextListStyleHelper 7 | { 8 | public static TextParagraphPropertiesType?[] 9 | ToTextParagraphPropertiesTypeList(this ListStyle textBodyListStyle) 10 | { 11 | return new TextParagraphPropertiesType?[] 12 | { 13 | textBodyListStyle.Level1ParagraphProperties, 14 | textBodyListStyle.Level2ParagraphProperties, 15 | textBodyListStyle.Level3ParagraphProperties, 16 | textBodyListStyle.Level4ParagraphProperties, 17 | textBodyListStyle.Level5ParagraphProperties, 18 | textBodyListStyle.Level6ParagraphProperties, 19 | textBodyListStyle.Level7ParagraphProperties, 20 | textBodyListStyle.Level8ParagraphProperties, 21 | textBodyListStyle.Level9ParagraphProperties 22 | }; 23 | } 24 | 25 | public static TextParagraphPropertiesType?[] 26 | ToTextParagraphPropertiesTypeList(this TextListStyleType textListStyleType) 27 | { 28 | return new TextParagraphPropertiesType?[] 29 | { 30 | textListStyleType.Level1ParagraphProperties, 31 | textListStyleType.Level2ParagraphProperties, 32 | textListStyleType.Level3ParagraphProperties, 33 | textListStyleType.Level4ParagraphProperties, 34 | textListStyleType.Level5ParagraphProperties, 35 | textListStyleType.Level6ParagraphProperties, 36 | textListStyleType.Level7ParagraphProperties, 37 | textListStyleType.Level8ParagraphProperties, 38 | textListStyleType.Level9ParagraphProperties 39 | }; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/dotnetCampus.DocumentFormat.OpenXml.Flatten.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;netstandard2.0;netcoreapp3.1;net6.0 5 | true 6 | 7 | true 8 | 9 | enable 10 | 11 | DocumentFormat.OpenXml.Flatten 12 | 13 | 提供继承的属性拍平的功能的库,包含常用处理逻辑和计算逻辑 14 | 15 | 16 | 17 | 18 | 19 | 20 | true 21 | 22 | 23 | true 24 | 25 | 26 | 27 | 28 | 29 | true 30 | 31 | 32 | 33 | true 34 | snupkg 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | all 51 | runtime; build; native; contentfiles; analyzers; buildtransitive 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/DocumentFormat.OpenXml.Flatten/dotnetCampus.DocumentFormat.OpenXml.Flatten.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True -------------------------------------------------------------------------------- /src/DocumentFormat.OpenXml.Flatten/README.md: -------------------------------------------------------------------------------- 1 | # DocumentFormat.OpenXml.Flatten 2 | 3 | 将 OpenXML 里面的 PPT 元素继承的属性拍平,让元素可以获取到最终的属性值。属性继承顺序是,先找元素本身,再找 SlideLayout 再找 SlideMaster 再找 Theme 如果依然找不到,就使用放在 App 里面的默认值 4 | 5 | | Build | 6 | |--| 7 | |![](https://github.com/dotnet-campus/dotnetCampus.OfficeDocumentZipper/workflows/.NET%20Core/badge.svg)| 8 | 9 | | Name | NuGet| 10 | |--|--| 11 | |dotnetCampus.DocumentFormat.OpenXml.Flatten|[![](https://img.shields.io/nuget/v/dotnetCampus.DocumentFormat.OpenXml.Flatten.svg)](https://www.nuget.org/packages/dotnetCampus.DocumentFormat.OpenXml.Flatten)| 12 | |dotnetCampus.DocumentFormat.OpenXml.Flatten.Source|[![](https://img.shields.io/nuget/v/dotnetCampus.DocumentFormat.OpenXml.Flatten.Source.svg)](https://www.nuget.org/packages/dotnetCampus.DocumentFormat.OpenXml.Flatten.Source)| 13 | 14 | ## 功能 15 | 16 | - 对形状元素的属性样式的继承处理 17 | - 提供预设形状和自定义形状的 Path 转为 WPF 的 [Mini-Language](https://docs.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/path-markup-syntax?view=netframeworkdesktop-4.8) 字符串,此字符串和 SVG 兼容 18 | - 提供 PPT 内嵌 OLE 或 XLSX 格式的表格的内容读取 -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace dotnetCampus.OfficeDocumentZipper 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet-campus/DocumentFormat.OpenXml.Extensions/8d1434603473618eed409bfa1d0c39fbf623e02b/src/dotnetCampus.OfficeDocumentZipper/Icon.png -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 36 | 38 | 40 | 41 | 43 | 45 | 47 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | https://github.com/dotnet-campus/dotnetCampus.OfficeDocumentZipper 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /src/dotnetCampus.OfficeDocumentZipper/dotnetCampus.OfficeDocumentZipper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | netcoreapp3.1 6 | true 7 | 8 | true 9 | 10 | OfficeDocumentZipper 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/EmuPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 采用 表示的点 7 | /// 8 | public readonly struct EmuPoint : IEquatable 9 | { 10 | /// 11 | /// 创建 表示的点 12 | /// 13 | /// 14 | /// 15 | public EmuPoint(Emu x, Emu y) 16 | { 17 | X = x; 18 | Y = y; 19 | } 20 | 21 | /// 22 | /// 创建 表示的点 23 | /// 24 | /// 用EMU单位的 X 坐标 25 | /// 用EMU单位的 Y 坐标 26 | public EmuPoint(double xEmu, double yEmu) : this(new Emu(xEmu), new Emu(yEmu)) 27 | { 28 | } 29 | 30 | /// 31 | /// 表示 X 坐标 32 | /// 33 | public Emu X { get; } 34 | 35 | /// 36 | /// 表示 Y 坐标 37 | /// 38 | public Emu Y { get; } 39 | 40 | /// 41 | public bool Equals(EmuPoint other) 42 | { 43 | return X.Equals(other.X) && Y.Equals(other.Y); 44 | } 45 | 46 | /// 47 | public override bool Equals(object? obj) 48 | { 49 | return obj is EmuPoint other && Equals(other); 50 | } 51 | 52 | /// 53 | public override int GetHashCode() 54 | { 55 | unchecked 56 | { 57 | return (X.GetHashCode() * 397) ^ Y.GetHashCode(); 58 | } 59 | } 60 | 61 | /// 62 | /// 判断相等 63 | /// 64 | /// 65 | /// 66 | /// 67 | public static bool operator ==(EmuPoint left, EmuPoint right) 68 | { 69 | return left.Equals(right); 70 | } 71 | 72 | /// 73 | /// 判断不相等 74 | /// 75 | /// 76 | /// 77 | /// 78 | public static bool operator !=(EmuPoint left, EmuPoint right) 79 | { 80 | return !left.Equals(right); 81 | } 82 | 83 | /// 84 | public override string ToString() => $"X={X} ;Y={Y}"; 85 | } 86 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/EmuRectangle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 4 | 5 | namespace dotnetCampus.OpenXmlUnitConverter 6 | { 7 | /// 8 | /// 采用 表示的矩形 9 | /// 10 | public readonly struct EmuRectangle : IEquatable 11 | { 12 | /// 13 | /// 创建使用 表示的矩形 14 | /// 15 | public EmuRectangle(Emu left, Emu top, Emu right, Emu bottom) 16 | { 17 | Left = left; 18 | Top = top; 19 | Right = right; 20 | Bottom = bottom; 21 | } 22 | 23 | /// 24 | /// 创建使用 表示的矩形 25 | /// 26 | public EmuRectangle(EmuPoint point, EmuSize size) 27 | { 28 | Left = point.X; 29 | Top = point.Y; 30 | Right = new Emu(point.X.Value + size.Width.Value); 31 | Bottom = new Emu(point.Y.Value + size.Height.Value); 32 | } 33 | 34 | public Emu Left { get; } 35 | public Emu Top { get; } 36 | 37 | public Emu Right { get; } 38 | public Emu Bottom { get; } 39 | 40 | public Emu Width => new Emu(Right.Value - Left.Value); 41 | public Emu Height => new Emu(Bottom.Value - Top.Value); 42 | 43 | public EmuPoint LeftTop => new EmuPoint(Left, Top); 44 | public EmuPoint RightBottom => new EmuPoint(Right, Bottom); 45 | public EmuSize Size => new EmuSize(Width, Height); 46 | 47 | public bool Equals(EmuRectangle other) 48 | { 49 | return Left.Equals(other.Left) 50 | && Top.Equals(other.Top) 51 | && Right.Equals(other.Right) 52 | && Bottom.Equals(other.Bottom); 53 | } 54 | 55 | public override bool Equals(object? obj) 56 | { 57 | return obj is EmuRectangle other && Equals(other); 58 | } 59 | 60 | public override int GetHashCode() 61 | { 62 | unchecked 63 | { 64 | var hashCode = Left.GetHashCode(); 65 | hashCode = (hashCode * 397) ^ Top.GetHashCode(); 66 | hashCode = (hashCode * 397) ^ Right.GetHashCode(); 67 | hashCode = (hashCode * 397) ^ Bottom.GetHashCode(); 68 | return hashCode; 69 | } 70 | } 71 | 72 | /// 73 | /// 判断相等 74 | /// 75 | public static bool operator ==(in EmuRectangle left, in EmuRectangle right) 76 | { 77 | return left.Equals(right); 78 | } 79 | 80 | /// 81 | /// 判断不相等 82 | /// 83 | public static bool operator !=(in EmuRectangle left, in EmuRectangle right) 84 | { 85 | return !left.Equals(right); 86 | } 87 | 88 | /// 89 | public override string ToString() => 90 | $"{LeftTop} ;{Size}"; 91 | } 92 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/EmuSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 采用 表示的尺寸 7 | /// 8 | public readonly struct EmuSize : IEquatable 9 | { 10 | /// 11 | /// 创建使用 EMU 单位表示的 Size 尺寸 12 | /// 13 | /// 14 | /// 15 | public EmuSize(Emu width, Emu height) 16 | { 17 | Width = width; 18 | Height = height; 19 | } 20 | 21 | /// 22 | /// 宽度 23 | /// 24 | public Emu Width { get; } 25 | 26 | /// 27 | /// 高度 28 | /// 29 | public Emu Height { get; } 30 | 31 | /// 32 | public bool Equals(EmuSize other) 33 | { 34 | return Width.Equals(other.Width) && Height.Equals(other.Height); 35 | } 36 | 37 | /// 38 | public override bool Equals(object? obj) 39 | { 40 | return obj is EmuSize other && Equals(other); 41 | } 42 | 43 | /// 44 | public override int GetHashCode() 45 | { 46 | unchecked 47 | { 48 | return (Width.GetHashCode() * 397) ^ Height.GetHashCode(); 49 | } 50 | } 51 | 52 | /// 53 | /// 判断相等 54 | /// 55 | public static bool operator ==(in EmuSize left, in EmuSize right) 56 | { 57 | return left.Equals(right); 58 | } 59 | 60 | /// 61 | /// 判断不相等 62 | /// 63 | public static bool operator !=(in EmuSize left, in EmuSize right) 64 | { 65 | return !left.Equals(right); 66 | } 67 | 68 | /// 69 | public override string ToString() => $"W={Width} ;H={Height}"; 70 | } 71 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/MillisecondTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using DocumentFormat.OpenXml; 4 | using DocumentFormat.OpenXml.Presentation; 5 | 6 | namespace dotnetCampus.OpenXmlUnitConverter 7 | { 8 | /// 9 | /// 定义ppt中的时间单位(毫秒) 10 | /// 11 | public readonly struct MillisecondTime 12 | { 13 | /// 14 | /// 创建 PPT 中的时间单位 15 | /// 16 | /// 17 | public MillisecondTime(StringValue? millisecond) 18 | { 19 | if (millisecond is null) 20 | { 21 | Milliseconds = long.MaxValue; 22 | IsIndefinite = true; 23 | } 24 | else if (long.TryParse(millisecond, out var value)) 25 | { 26 | Milliseconds = value; 27 | IsIndefinite = false; 28 | } 29 | else if (new IndefiniteTimeDeclarationValues(millisecond) == IndefiniteTimeDeclarationValues.Indefinite) 30 | { 31 | Milliseconds = long.MaxValue; 32 | IsIndefinite = true; 33 | } 34 | else 35 | { 36 | if (Debugger.IsAttached) 37 | { 38 | throw new ArgumentException($"输入参数不合法 MillisecondString={millisecond}", nameof(millisecond)); 39 | } 40 | else 41 | { 42 | Milliseconds = long.MaxValue; 43 | IsIndefinite = false; 44 | } 45 | } 46 | } 47 | 48 | private MillisecondTime(long milliseconds) 49 | { 50 | Milliseconds = milliseconds; 51 | IsIndefinite = false; 52 | } 53 | 54 | /// 55 | /// 是否未定义(相当于无穷,也相当于空值)具体按照业务决定 56 | /// 57 | public bool IsIndefinite { get; } 58 | 59 | /// 60 | /// 以毫秒形式表示 61 | /// 62 | public long Milliseconds { get; } 63 | 64 | /// 65 | /// 从 毫秒创建 66 | /// 67 | /// 68 | /// 69 | public static MillisecondTime FromMilliseconds(long milliseconds) 70 | { 71 | return new MillisecondTime(milliseconds); 72 | } 73 | 74 | /// 75 | /// 以ticks形式表示的时间 10000 Tick 是 1 毫秒 76 | /// 77 | public long ToTicks() => IsIndefinite 78 | ? throw new InvalidOperationException( 79 | $"The {nameof(MillisecondTime)} is indefinite. Can not convert to Tick.") 80 | : Milliseconds * TicksPerMillisecond; 81 | 82 | /// 83 | /// 返回使用 表示的时间 84 | /// 85 | public TimeSpan ToTimeSpan() => IsIndefinite 86 | ? throw new InvalidOperationException( 87 | $"The {nameof(MillisecondTime)} is indefinite. Can not convert to {nameof(TimeSpan)}.") 88 | : TimeSpan.FromMilliseconds(Milliseconds); 89 | 90 | private const int TicksPerMillisecond = 10000; 91 | 92 | /// 93 | public override string ToString() => $"Millisecond:{Milliseconds}"; 94 | } 95 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/OpenXmlUnitConverter.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 2 | 3 | using System; 4 | using DocumentFormat.OpenXml; 5 | 6 | namespace dotnetCampus.OpenXmlUnitConverter 7 | { 8 | public static class OpenXmlUnitConverter 9 | { 10 | public static Int64Value ToOpenXmlInt64Value(this Emu emu) 11 | { 12 | return new Int64Value((long)Math.Round(emu.Value)); 13 | } 14 | 15 | public static Int32Value ToOpenXmlInt32Value(this Emu emu) 16 | { 17 | return new Int32Value((int)Math.Round(emu.Value)); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/PixelPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 采用 表示的点 7 | /// 8 | public readonly struct PixelPoint : IEquatable 9 | { 10 | /// 11 | /// 创建 表示的点 12 | /// 13 | /// 14 | /// 15 | public PixelPoint(Pixel x, Pixel y) 16 | { 17 | X = x; 18 | Y = y; 19 | } 20 | 21 | /// 22 | /// 表示 X 坐标 23 | /// 24 | public Pixel X { get; } 25 | 26 | /// 27 | /// 表示 Y 坐标 28 | /// 29 | public Pixel Y { get; } 30 | 31 | /// 32 | public override string ToString() => $"X={X} ;Y={Y}"; 33 | 34 | /// 35 | public bool Equals(PixelPoint other) 36 | { 37 | return X.Equals(other.X) && Y.Equals(other.Y); 38 | } 39 | 40 | /// 41 | public override bool Equals(object? obj) 42 | { 43 | return obj is PixelPoint other && Equals(other); 44 | } 45 | 46 | /// 47 | public override int GetHashCode() 48 | { 49 | unchecked 50 | { 51 | return (X.GetHashCode() * 397) ^ Y.GetHashCode(); 52 | } 53 | } 54 | 55 | /// 56 | /// 判断相等 57 | /// 58 | public static bool operator ==(PixelPoint left, PixelPoint right) 59 | { 60 | return left.Equals(right); 61 | } 62 | 63 | /// 64 | /// 判断不相等 65 | /// 66 | public static bool operator !=(PixelPoint left, PixelPoint right) 67 | { 68 | return !left.Equals(right); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/PixelSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 像素尺寸 7 | /// 8 | public readonly struct PixelSize : IEquatable 9 | { 10 | /// 11 | /// 像素尺寸 12 | /// 13 | /// 14 | /// 15 | public PixelSize(Pixel width, Pixel height) 16 | { 17 | Width = width; 18 | Height = height; 19 | } 20 | 21 | /// 22 | /// 宽度 23 | /// 24 | public Pixel Width { get; } 25 | 26 | /// 27 | /// 高度 28 | /// 29 | public Pixel Height { get; } 30 | 31 | /// 32 | public bool Equals(PixelSize other) 33 | { 34 | return Width.Equals(other.Width) && Height.Equals(other.Height); 35 | } 36 | 37 | /// 38 | public override bool Equals(object? obj) 39 | { 40 | return obj is PixelSize other && Equals(other); 41 | } 42 | 43 | /// 44 | public override int GetHashCode() 45 | { 46 | unchecked 47 | { 48 | return (Width.GetHashCode() * 397) ^ Height.GetHashCode(); 49 | } 50 | } 51 | 52 | /// 53 | /// 判断相等 54 | /// 55 | public static bool operator ==(PixelSize left, PixelSize right) 56 | { 57 | return left.Equals(right); 58 | } 59 | 60 | /// 61 | /// 判断不相等 62 | /// 63 | public static bool operator !=(PixelSize left, PixelSize right) 64 | { 65 | return !left.Equals(right); 66 | } 67 | 68 | /// 69 | public override string ToString() => $"W={Width}; H={Height}"; 70 | } 71 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXMLUnitConverter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("dotnetCampus.OpenXMLUnitConverter.Tests")] -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Cm.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 厘米。 5 | /// 6 | /// 1 = 360000 7 | /// 1 = 2.54 8 | /// 9 | /// 10 | public readonly struct Cm 11 | { 12 | /// 13 | /// 创建 单位。 14 | /// 15 | /// 单位数值。 16 | public Cm(double value) 17 | { 18 | Value = value; 19 | } 20 | 21 | /// 22 | /// 获取 单位数值。 23 | /// 24 | public double Value { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Dxa.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 表示 DXA 单位,点的二十分之一。主要用于指定页面尺寸,边距,制表符等。 5 | /// 1 = 1/20 6 | /// 7 | public readonly struct Dxa 8 | { 9 | /// 10 | /// 创建 单位。 11 | /// 12 | /// 单位数值。 13 | public Dxa(double value) => Value = value; 14 | 15 | /// 16 | /// 获取 单位数值。 17 | /// 18 | public double Value { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Emu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 英制公制单位(English Metric Unit)。用于对接厘米()和英寸()的虚拟单位。 7 | /// 其特殊的数值设计,便于让你在转换百位以内的英寸和毫米、像素长度时,不会产生小数。 8 | /// 1 = 914400 9 | /// 10 | public readonly struct Emu : IEquatable 11 | { 12 | /// 13 | /// 创建 单位。 14 | /// 15 | /// 单位数值。 16 | public Emu(double value) 17 | { 18 | Value = value; 19 | } 20 | 21 | /// 22 | /// 获取 单位数值。 23 | /// 24 | public double Value { get; } 25 | 26 | /// 27 | /// 表示已初始化为零的 长度。 28 | /// 29 | public static Emu Zero => new Emu(0); 30 | 31 | /// 32 | public override string ToString() => $"EMU: {Value:0.00}"; 33 | 34 | /// 35 | public bool Equals(Emu other) 36 | { 37 | return Value.Equals(other.Value); 38 | } 39 | 40 | /// 41 | public override bool Equals(object? obj) 42 | { 43 | return obj is Emu other && Equals(other); 44 | } 45 | 46 | /// 47 | public override int GetHashCode() 48 | { 49 | return Value.GetHashCode(); 50 | } 51 | 52 | /// 53 | /// 判断相等 54 | /// 55 | /// 56 | /// 57 | /// 58 | public static bool operator ==(Emu left, Emu right) 59 | { 60 | return left.Equals(right); 61 | } 62 | 63 | /// 64 | /// 判断不相等 65 | /// 66 | /// 67 | /// 68 | /// 69 | public static bool operator !=(Emu left, Emu right) 70 | { 71 | return !left.Equals(right); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/EmuPercentage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DocumentFormat.OpenXml; 3 | 4 | namespace dotnetCampus.OpenXmlUnitConverter 5 | { 6 | /// 7 | /// 用 Emu 表示的百分数 8 | /// 9 | public readonly struct EmuPercentage 10 | { 11 | /// 12 | /// 用 Emu 表示的百分数 13 | /// 14 | /// 15 | public EmuPercentage(double value) 16 | { 17 | Value = value; 18 | } 19 | 20 | /// 21 | /// 用 Emu 表示的百分数 22 | /// 23 | /// 支持传入带百分号的内容 24 | public EmuPercentage(Int32Value int32Value) : this(int32Value.InnerText) 25 | { 26 | } 27 | 28 | internal EmuPercentage(string? percentageText) 29 | { 30 | if (!string.IsNullOrEmpty(percentageText)) 31 | { 32 | if (int.TryParse(percentageText, out var intValue)) 33 | { 34 | Value = intValue; 35 | return; 36 | } 37 | else 38 | { 39 | // 如果是带了百分比的 40 | if (percentageText!.Length > 1 && percentageText.EndsWith("%")) 41 | { 42 | #if NETCOREAPP3_1_OR_GREATER 43 | var percentageSpan = percentageText.AsSpan().Slice(0, percentageText.Length - 1); 44 | if (double.TryParse(percentageSpan, out var doubleValue)) 45 | #else 46 | var newPercentageText = percentageText.Substring(0, percentageText.Length - 1); 47 | if (double.TryParse(newPercentageText, out var doubleValue)) 48 | #endif 49 | { 50 | // 根据 OpenXml 规则,这里和像素百分比转换是 1000 倍 51 | var pixelPercentageValue = doubleValue * 1000; 52 | 53 | // 这里的百分比一定是 Pixel 的百分比级的,需要经过转换才能计算为 Emu 的百分比 54 | // 为了将 PixelPercentageValue 转换为 EmuPercentage 类型,需要经过换算逻辑 55 | var pixel = new Pixel(pixelPercentageValue); 56 | var emuPercentageValue = pixel.ToEmu(); 57 | 58 | Value = emuPercentageValue.Value; 59 | return; 60 | } 61 | } 62 | } 63 | } 64 | 65 | throw new ArgumentException($"Can not convert PercentageText={percentageText} to {nameof(EmuPercentage)} value."); 66 | } 67 | 68 | /// 69 | /// 用 Emu 表示的百分数 70 | /// 71 | public double Value { get; } 72 | } 73 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/HalfPoint.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 半点。用来表示字体大小。 5 | /// 1 = 1/2 6 | /// 7 | public readonly struct HalfPoint 8 | { 9 | /// 10 | /// 创建 单位。 11 | /// 12 | /// 单位数值。 13 | public HalfPoint(double value) 14 | { 15 | Value = value; 16 | } 17 | 18 | /// 19 | /// 获取 单位数值。 20 | /// 21 | public double Value { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Inch.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 英寸。 5 | /// 6 | /// 1 = 914400 7 | /// 1 = 2.54 8 | /// 9 | /// 10 | public readonly struct Inch 11 | { 12 | /// 13 | /// 创建 单位。 14 | /// 15 | /// 单位数值。 16 | public Inch(double value) 17 | { 18 | Value = value; 19 | } 20 | 21 | /// 22 | /// 获取 单位数值。 23 | /// 24 | public double Value { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Cm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Cm : LegacyUnit 8 | { 9 | public Cm(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator Cm(dotnetCampus.OpenXmlUnitConverter.Cm newUnit) 17 | { 18 | return new Cm(newUnit.Value); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Dxa.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Dxa : LegacyUnit 8 | { 9 | public Dxa(double value) => Value = value; 10 | 11 | public double Value { get; } 12 | 13 | public static implicit operator Dxa(dotnetCampus.OpenXmlUnitConverter.Dxa newUnit) 14 | { 15 | return new Dxa(newUnit.Value); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Emu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Emu : LegacyUnit 8 | { 9 | public Emu(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static readonly Emu Zero = new Emu(0); 17 | 18 | public static implicit operator Emu(dotnetCampus.OpenXmlUnitConverter.Emu newUnit) 19 | { 20 | return new Emu(newUnit.Value); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/EmuPercentage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class EmuPercentage : LegacyUnit 8 | { 9 | public EmuPercentage(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator EmuPercentage(dotnetCampus.OpenXmlUnitConverter.EmuPercentage newUnit) 17 | { 18 | return new EmuPercentage(newUnit.Value); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/HalfPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class HalfPoint : LegacyUnit 8 | { 9 | public HalfPoint(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator HalfPoint(dotnetCampus.OpenXmlUnitConverter.HalfPoint newUnit) 17 | { 18 | return new HalfPoint(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Inch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Inch : LegacyUnit 8 | { 9 | public Inch(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator Inch(dotnetCampus.OpenXmlUnitConverter.Inch newUnit) 17 | { 18 | return new Inch(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/LegacyUnit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace dotnetCampus.OpenXMLUnitConverter 9 | { 10 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("此类型仅供兼容代码使用,请勿直接调用。")] 11 | public abstract class LegacyUnit 12 | where T : struct 13 | where TLegacy : LegacyUnit 14 | { 15 | public static implicit operator T(LegacyUnit legacyUnit) 16 | { 17 | var legacyValue = legacyUnit.GetType().GetProperty("Value", typeof(double))!.GetValue(legacyUnit); 18 | return (T)typeof(T).GetConstructor(new[] { typeof(double) })!.Invoke(null, new object[] { legacyValue }); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Mm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Mm : LegacyUnit 8 | { 9 | public Mm(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator Mm(dotnetCampus.OpenXmlUnitConverter.Mm newUnit) 17 | { 18 | return new Mm(newUnit.Value); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Pixel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Pixel : LegacyUnit 8 | { 9 | public Pixel(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static readonly Pixel ZeroPixel = new Pixel(0); 17 | 18 | public static implicit operator Pixel(dotnetCampus.OpenXmlUnitConverter.Pixel newUnit) 19 | { 20 | return new Pixel(newUnit.Value); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/PixelPercentage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class PixelPercentage : Percentage 8 | { 9 | public PixelPercentage(int value) : base(value) 10 | { 11 | } 12 | 13 | public static implicit operator PixelPercentage(dotnetCampus.OpenXmlUnitConverter.PixelPercentage newUnit) 14 | { 15 | return new PixelPercentage(newUnit.IntValue); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Pound.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Pound : LegacyUnit 8 | { 9 | public Pound(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator Pound(dotnetCampus.OpenXmlUnitConverter.Pound newUnit) 17 | { 18 | return new Pound(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/PoundHundredfold.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class PoundHundredfold : LegacyUnit 8 | { 9 | public PoundHundredfold(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator PoundHundredfold(dotnetCampus.OpenXmlUnitConverter.PoundHundredfold newUnit) 17 | { 18 | return new PoundHundredfold(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/Pt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class Pt : LegacyUnit 8 | { 9 | public Pt(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator Pt(dotnetCampus.OpenXmlUnitConverter.Pt newUnit) 17 | { 18 | return new Pt(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Legacy/PtHundredfold.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace dotnetCampus.OpenXMLUnitConverter 5 | { 6 | [EditorBrowsable(EditorBrowsableState.Never), Obsolete("请使用 dotnetCampus.OpenXmlUnitConverter 命名空间下的同名类型。")] 7 | public class PtHundredfold : LegacyUnit 8 | { 9 | public PtHundredfold(double value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public double Value { get; } 15 | 16 | public static implicit operator PtHundredfold(dotnetCampus.OpenXmlUnitConverter.PtHundredfold newUnit) 17 | { 18 | return new PtHundredfold(newUnit.Value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Mm.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 毫米。 5 | /// 1 = 1/10 6 | /// 7 | public readonly struct Mm 8 | { 9 | /// 10 | /// 创建 单位。 11 | /// 12 | /// 单位数值。 13 | public Mm(double value) 14 | { 15 | Value = value; 16 | } 17 | 18 | /// 19 | /// 获取 单位数值。 20 | /// 21 | public double Value { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Pixel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 像素 7 | /// 8 | public readonly struct Pixel : IEquatable 9 | { 10 | /// 11 | /// 像素 12 | /// 13 | /// 14 | public Pixel(double value) 15 | { 16 | Value = value; 17 | } 18 | 19 | /// 20 | /// 像素 21 | /// 22 | public double Value { get; } 23 | 24 | /// 25 | /// 表示值是 0 的像素 26 | /// 27 | public static Pixel ZeroPixel => new Pixel(0); 28 | 29 | /// 30 | public override string ToString() => $"Pixel:{Value:0.00}"; 31 | 32 | /// 33 | public bool Equals(Pixel other) 34 | { 35 | return Value.Equals(other.Value); 36 | } 37 | 38 | /// 39 | public override bool Equals(object? obj) 40 | { 41 | return obj is Pixel other && Equals(other); 42 | } 43 | 44 | /// 45 | public override int GetHashCode() 46 | { 47 | return Value.GetHashCode(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/PixelPercentage.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 像素百分数 5 | /// 6 | public class PixelPercentage : Percentage 7 | { 8 | /// 9 | public PixelPercentage(int value) : base(value) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Pound.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnetCampus.OpenXmlUnitConverter 4 | { 5 | /// 6 | /// 磅 7 | /// 8 | public readonly struct Pound : IEquatable 9 | { 10 | /// 11 | /// 磅 12 | /// 13 | /// 14 | public Pound(double value) 15 | { 16 | Value = value; 17 | } 18 | 19 | /// 20 | /// 磅 21 | /// 22 | public double Value { get; } 23 | 24 | /// 25 | public override string ToString() => $"Pound:{Value:0.00}"; 26 | 27 | /// 28 | public bool Equals(Pound other) 29 | { 30 | return Value.Equals(other.Value); 31 | } 32 | 33 | /// 34 | public override bool Equals(object? obj) 35 | { 36 | return obj is Pound other && Equals(other); 37 | } 38 | 39 | /// 40 | public override int GetHashCode() 41 | { 42 | return Value.GetHashCode(); 43 | } 44 | 45 | /// 46 | /// 判断相等 47 | /// 48 | /// 49 | /// 50 | /// 51 | public static bool operator ==(Pound left, Pound right) 52 | { 53 | return left.Equals(right); 54 | } 55 | 56 | /// 57 | /// 判断不相等 58 | /// 59 | /// 60 | /// 61 | /// 62 | public static bool operator !=(Pound left, Pound right) 63 | { 64 | return !left.Equals(right); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/PoundHundredfold.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 100倍的磅 5 | /// 6 | public readonly struct PoundHundredfold 7 | { 8 | /// 9 | /// 100倍的磅 10 | /// 11 | /// 12 | public PoundHundredfold(double value) 13 | { 14 | Value = value; 15 | } 16 | 17 | /// 18 | /// 100倍的磅 19 | /// 20 | public double Value { get; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/Pt.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 点(Point)。 5 | /// 1 = 1/72 6 | /// 7 | public readonly struct Pt 8 | { 9 | /// 10 | /// 创建 单位。 11 | /// 12 | /// 单位数值。 13 | public Pt(double value) 14 | { 15 | Value = value; 16 | } 17 | 18 | /// 19 | /// 获取 单位数值。 20 | /// 21 | public double Value { get; } 22 | } 23 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/PtHundredfold.cs: -------------------------------------------------------------------------------- 1 | namespace dotnetCampus.OpenXmlUnitConverter 2 | { 3 | /// 4 | /// 100 倍的点 5 | /// 6 | public readonly struct PtHundredfold 7 | { 8 | /// 9 | /// 100 倍的点 10 | /// 11 | /// 12 | public PtHundredfold(double value) 13 | { 14 | Value = value; 15 | } 16 | 17 | /// 18 | /// 100 倍的点 19 | /// 20 | public double Value { get; } 21 | } 22 | } -------------------------------------------------------------------------------- /src/dotnetCampus.OpenXmlUnitConverter/dotnetCampus.OpenXmlUnitConverter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;net46;netstandard2.0;netcoreapp3.1;net5.0 5 | true 6 | enable 7 | 8 | 9 | 10 | 11 | true 12 | 13 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 14 | Defining units for OpenXml properties and the unit conversion function. 提供 OpenXml 计量单位和单位转换逻辑 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | true 38 | 39 | 40 | 41 | true 42 | snupkg 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXMLUnitConverter.Tests/dotnetCampus.OpenXMLUnitConverter.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/AngleTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DocumentFormat.OpenXml; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using MSTest.Extensions.Contracts; 5 | 6 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 7 | { 8 | [TestClass] 9 | public class AngleTest 10 | { 11 | [ContractTestCase] 12 | public void Calculate() 13 | { 14 | "传入两个相等的角度值,可以通过 == 判断相等".Test(() => 15 | { 16 | var a = Angle.Degree90; 17 | var b = new Angle(new Int32Value(5400000)); 18 | var c = Angle.FromRadiansValue(Math.PI / 2); 19 | 20 | Assert.AreEqual(true, a == b); 21 | Assert.AreEqual(true, a == c); 22 | Assert.AreEqual(true, b == c); 23 | 24 | Assert.AreEqual(false, a != b); 25 | Assert.AreEqual(false, a != c); 26 | Assert.AreEqual(false, b != c); 27 | }); 28 | 29 | "传入90度的角度和270度的角度,可以返回270度的角度更大".Test(() => 30 | { 31 | var a = Angle.Degree90; 32 | var b = Angle.Degree270; 33 | 34 | Assert.AreEqual(true, b > a); 35 | Assert.AreEqual(true, a < b); 36 | }); 37 | 38 | "使用负数表示的角度,拿到的是原先的负数角度值".Test(() => 39 | { 40 | var a = Angle.Degree180; 41 | var b = -a; 42 | Assert.AreEqual(a, b * -1); 43 | Assert.AreEqual(a, -b); 44 | }); 45 | 46 | "使用正数表示的角度,和原先角度值相同".Test(() => 47 | { 48 | var angle = Angle.Degree180; 49 | Assert.AreEqual(angle, +angle); 50 | }); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/EmuPercentageTest.cs: -------------------------------------------------------------------------------- 1 | using DocumentFormat.OpenXml; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using MSTest.Extensions.Contracts; 4 | 5 | namespace dotnetCampus.OpenXmlUnitConverter.Tests; 6 | 7 | [TestClass] 8 | public class EmuPercentageTest 9 | { 10 | [ContractTestCase] 11 | public void CreateEmuPercentage() 12 | { 13 | "传入带百分号的 Int32Value 值,可以成功转换为 EmuPercentage 对象".Test(() => 14 | { 15 | var int32Value = new Int32Value 16 | { 17 | InnerText = "10%" 18 | }; 19 | 20 | var emuPercentage = new EmuPercentage(int32Value); 21 | var pixelPercentage = emuPercentage.ToPixelPercentage(); 22 | Assert.AreEqual(10 * 1000, pixelPercentage.IntValue); 23 | }); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/EmuRectangleTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using MSTest.Extensions.Contracts; 3 | 4 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 5 | { 6 | [TestClass] 7 | public class EmuRectangleTest 8 | { 9 | [ContractTestCase] 10 | public void CreateEmuRectangle() 11 | { 12 | "给定矩形各个边角的值,可以计算出矩形的宽度和高度".Test(() => 13 | { 14 | // Arrange 15 | var emuRectangle = new EmuRectangle(new Emu(0), new Emu(0), new Emu(100), new Emu(100)); 16 | 17 | // Action 18 | var width = emuRectangle.Width; 19 | var height = emuRectangle.Height; 20 | 21 | // Assert 22 | Assert.AreEqual(100, width.Value); 23 | Assert.AreEqual(100, height.Value); 24 | }); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/MillisecondTimeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DocumentFormat.OpenXml; 3 | using DocumentFormat.OpenXml.Presentation; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using MSTest.Extensions.Contracts; 6 | 7 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 8 | { 9 | [TestClass] 10 | public class MillisecondTimeTest 11 | { 12 | [ContractTestCase] 13 | public void FromMilliseconds() 14 | { 15 | "提供从毫秒创建的方法,可以从给定的毫秒进行创建 MillisecondTime 结构体".Test(() => 16 | { 17 | var milliseconds = 300; 18 | var millisecondTime = MillisecondTime.FromMilliseconds(milliseconds); 19 | Assert.AreEqual(TimeSpan.FromMilliseconds(milliseconds), millisecondTime.ToTimeSpan()); 20 | }); 21 | } 22 | 23 | [ContractTestCase] 24 | public void ParseMillisecondTimeText() 25 | { 26 | "传入 null 的 OpenXML 字符串,可以说明未解析".Test(() => 27 | { 28 | StringValue stringValue = null!; 29 | var millisecondTime = new MillisecondTime(stringValue); 30 | Assert.AreEqual(true, millisecondTime.IsIndefinite); 31 | }); 32 | 33 | "传入未定义的 OpenXML 字符串,可以说明未解析".Test(() => 34 | { 35 | var indefinite = (IEnumValue)IndefiniteTimeDeclarationValues.Indefinite; 36 | var stringValue = new StringValue(indefinite.Value); 37 | var millisecondTime = new MillisecondTime(stringValue); 38 | Assert.AreEqual(true, millisecondTime.IsIndefinite); 39 | }); 40 | 41 | "传入数值表示的毫秒时间的 OpenXML 字符串,可以解析出毫秒时间".Test(() => 42 | { 43 | var n = 123; 44 | var stringValue = new StringValue(n.ToString()); 45 | var millisecondTime = new MillisecondTime(stringValue); 46 | Assert.AreEqual(n, millisecondTime.Milliseconds); 47 | Assert.AreEqual(1230000, millisecondTime.ToTicks()); 48 | Assert.AreEqual(TimeSpan.FromMilliseconds(n), millisecondTime.ToTimeSpan()); 49 | }); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/PercentageTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | using MSTest.Extensions.Contracts; 6 | 7 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 8 | { 9 | [TestClass] 10 | public class PercentageTest 11 | { 12 | [ContractTestCase] 13 | public void ParsePercentageText() 14 | { 15 | "传入带百分号的数值,可以转换为百分号".Test(() => 16 | { 17 | var percentageText = "100%"; 18 | var percentage = new Percentage(percentageText); 19 | Assert.AreEqual(100000, percentage.IntValue); 20 | }); 21 | 22 | "传入百分号带小数点的数值,能转换出 OpenXML 单位的整数".Test(() => 23 | { 24 | var percentageText = "99.999%"; 25 | var percentage = new Percentage(percentageText); 26 | Assert.AreEqual(99999, percentage.IntValue); 27 | }); 28 | } 29 | 30 | [ContractTestCase] 31 | public void TestCalculate() 32 | { 33 | "非零百分比除以自己等于 100%".Test((double value) => 34 | { 35 | var percentage = Percentage.FromDouble(value); 36 | 37 | var result = percentage / percentage; 38 | Assert.AreEqual(true, Math.Abs(result.DoubleValue - 1) < 0.000001); 39 | }).WithArguments(1.5, 2.3, 3.6, 100.5, 100000.123); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/PixelPointTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using MSTest.Extensions.Contracts; 3 | 4 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 5 | { 6 | [TestClass] 7 | public class PixelPointTest 8 | { 9 | [ContractTestCase] 10 | public void Convert() 11 | { 12 | "传入 Emu 表示的点,可以转换为像素表示的点".Test(() => 13 | { 14 | var emuPoint = new EmuPoint(new Emu(952500), new Emu(952500)); 15 | var pixelPoint = emuPoint.ToPixelPoint(); 16 | Assert.AreEqual(100, pixelPoint.X.Value); 17 | Assert.AreEqual(100, pixelPoint.Y.Value); 18 | 19 | Assert.AreEqual(emuPoint, pixelPoint.ToEmuPoint()); 20 | }); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/PixelSizeTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using MSTest.Extensions.Contracts; 3 | 4 | namespace dotnetCampus.OpenXmlUnitConverter.Tests 5 | { 6 | [TestClass] 7 | public class PixelSizeTest 8 | { 9 | [ContractTestCase] 10 | public void Convert() 11 | { 12 | "传入 EMU 表示的尺寸,可以转换为像素表示的尺寸,反向互转值相同".Test(() => 13 | { 14 | var emuSize = new EmuSize(new Emu(952500), new Emu(95250)); 15 | var pixelSize = emuSize.ToPixelSize(); 16 | 17 | Assert.AreEqual(100, pixelSize.Width.Value); 18 | Assert.AreEqual(10, pixelSize.Height.Value); 19 | 20 | Assert.AreEqual(emuSize, pixelSize.ToEmuSize()); 21 | }); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/dotnetCampus.OpenXmlUnitConverter.Tests/dotnetCampus.OpenXmlUnitConverter.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------