using System; using System.Collections.Generic; using System.IO; using System.IO.Packaging; using System.Linq; using System.Xml.Linq; namespace BetterEN5.Services.Conversion { public class EnbxWriter { private static readonly XNamespace EN = "http://schemas.seewo.com/easinote/2016/document"; private static readonly XNamespace DC = "http://purl.org/dc/elements/1.1/"; public void Export(SmfDocument doc, string enbxPath) { using var enbx = Package.Open(enbxPath, FileMode.Create, FileAccess.ReadWrite); Export(doc, enbx); } public void Export(SmfDocument doc, Package enbx) { WriteContentTypes(enbx); WritePackageRels(enbx); WriteMediaFiles(doc, enbx); var slideRelTargets = new List(); for (int i = 0; i < doc.Slides.Count; i++) { var slideFileName = $"slides/slide{i + 1}.xml"; slideRelTargets.Add(slideFileName); var slideDoc = BuildSlideXml(doc.Slides[i], doc, i + 1); var slideUri = PackUriHelper.CreatePartUri(new Uri($"/{slideFileName}", UriKind.Relative)); var slidePart = enbx.CreatePart(slideUri, "application/vnd.seewo.easinote.slide+xml"); using (var sw = new StreamWriter(slidePart.GetStream())) slideDoc.Save(sw); } var rootDoc = BuildDocumentXml(doc, slideRelTargets); var docUri = PackUriHelper.CreatePartUri(new Uri("/document.xml", UriKind.Relative)); var docPart = enbx.CreatePart(docUri, "application/vnd.seewo.easinote.document+xml"); using (var sw = new StreamWriter(docPart.GetStream())) rootDoc.Save(sw); } private XDocument BuildDocumentXml(SmfDocument doc, List slideTargets) { var docElem = new XElement(EN + "Document", new XAttribute(XNamespace.Xmlns + "en", EN.NamespaceName), new XAttribute(XNamespace.Xmlns + "dc", DC.NamespaceName)); var props = new XElement(EN + "Properties", new XElement(EN + "Title", doc.Properties.Title ?? ""), new XElement(EN + "Author", doc.Properties.Author ?? ""), new XElement(EN + "Created", doc.Properties.Created?.ToString("o") ?? ""), new XElement(EN + "Application", doc.Properties.Application ?? "Better-Seewo"), new XElement(EN + "Version", "5.2.4.9855"), new XElement(EN + "SlideWidth", doc.Properties.SlideWidth), new XElement(EN + "SlideHeight", doc.Properties.SlideHeight)); docElem.Add(props); var slidesElem = new XElement(EN + "Slides"); foreach (var target in slideTargets) { slidesElem.Add(new XElement(EN + "SlideRef", target)); } docElem.Add(slidesElem); return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), docElem); } private XDocument BuildSlideXml(SmfSlide slide, SmfDocument doc, int slideNum) { var slideElem = new XElement(EN + "Slide", new XAttribute("Id", slide.Id), new XAttribute("Width", doc.Properties.SlideWidth), new XAttribute("Height", doc.Properties.SlideHeight)); WriteBackground(slide, slideElem, doc, slideNum); var elementsElem = new XElement(EN + "Elements"); foreach (var element in slide.Elements) WriteElement(element, elementsElem, doc, slideNum); slideElem.Add(elementsElem); return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), slideElem); } private void WriteBackground(SmfSlide slide, XElement slideElem, SmfDocument doc, int slideNum) { var bg = slide.Background; if (bg.Type == SmfFillType.None) return; var bgElem = new XElement(EN + "Background"); switch (bg.Type) { case SmfFillType.Solid when bg.SolidColor != null: bgElem.Add(new XElement(EN + "SolidFill", new XElement(EN + "Color", bg.SolidColor.Value))); break; case SmfFillType.Gradient when bg.Gradient != null: var gradElem = new XElement(EN + "GradientFill", new XElement(EN + "Type", bg.Gradient.Type.ToString()), new XElement(EN + "Angle", bg.Gradient.Angle)); foreach (var stop in bg.Gradient.Stops) { gradElem.Add(new XElement(EN + "Stop", new XElement(EN + "Position", stop.Position), new XElement(EN + "Color", stop.Color.Value))); } bgElem.Add(gradElem); break; case SmfFillType.Image when bg.ImageRef != null: bgElem.Add(new XElement(EN + "ImageFill", new XElement(EN + "Source", bg.ImageRef))); break; } if (bgElem.HasElements) slideElem.Add(bgElem); } private void WriteElement(SmfElement element, XElement elementsElem, SmfDocument doc, int slideNum) { switch (element) { case SmfTextElement te: WriteTextElement(te, elementsElem); break; case SmfImageElement ie: WriteImageElement(ie, elementsElem); break; case SmfShapeElement se: WriteShapeElement(se, elementsElem); break; case SmfTableElement te: WriteTableElement(te, elementsElem); break; case SmfInkElement ie: WriteInkElement(ie, elementsElem); break; case SmfGroupElement ge: break; } } private void WriteTextElement(SmfTextElement elem, XElement parent) { var xElem = new XElement(EN + "TextElement", new XAttribute("Id", elem.Id)); AddBounds(xElem, elem.Bounds); if (elem.Rotation != 0) xElem.Add(new XElement(EN + "Rotation", elem.Rotation)); xElem.Add(new XElement(EN + "Locked", elem.Locked)); var rtElem = new XElement(EN + "RichText"); rtElem.Add(new XElement(EN + "LeftMargin", elem.RichText.LeftMargin)); rtElem.Add(new XElement(EN + "RightMargin", elem.RichText.RightMargin)); rtElem.Add(new XElement(EN + "TopMargin", elem.RichText.TopMargin)); rtElem.Add(new XElement(EN + "BottomMargin", elem.RichText.BottomMargin)); rtElem.Add(new XElement(EN + "VerticalAlignment", elem.RichText.VerticalAlignment.ToString())); rtElem.Add(new XElement(EN + "WordWrap", elem.RichText.WordWrap)); foreach (var para in elem.RichText.Paragraphs) { var pElem = new XElement(EN + "Paragraph"); if (para.Alignment != null) pElem.Add(new XElement(EN + "Alignment", para.Alignment.ToString())); if (para.SpaceBefore != null) pElem.Add(new XElement(EN + "SpaceBefore", para.SpaceBefore.Value)); if (para.SpaceAfter != null) pElem.Add(new XElement(EN + "SpaceAfter", para.SpaceAfter.Value)); if (para.LineSpacing != null) pElem.Add(new XElement(EN + "LineSpacing", para.LineSpacing.Value)); foreach (var run in para.Runs) { var rElem = new XElement(EN + "Run", new XElement(EN + "Text", run.Text)); if (run.Formatting != null) { var fElem = new XElement(EN + "Formatting"); if (run.Formatting.Size != null) fElem.Add(new XElement(EN + "Size", run.Formatting.Size.Value)); if (run.Formatting.Bold != null) fElem.Add(new XElement(EN + "Bold", run.Formatting.Bold.Value)); if (run.Formatting.Italic != null) fElem.Add(new XElement(EN + "Italic", run.Formatting.Italic.Value)); if (run.Formatting.Underline != null) fElem.Add(new XElement(EN + "Underline", run.Formatting.Underline.Value)); if (run.Formatting.Strikethrough != null) fElem.Add(new XElement(EN + "Strikethrough", run.Formatting.Strikethrough.Value)); if (run.Formatting.Color != null) fElem.Add(new XElement(EN + "Color", run.Formatting.Color.Value)); if (run.Formatting.Font != null) fElem.Add(new XElement(EN + "Font", run.Formatting.Font)); if (fElem.HasElements) rElem.Add(fElem); } pElem.Add(rElem); } if (pElem.HasElements) rtElem.Add(pElem); } xElem.Add(rtElem); parent.Add(xElem); } private void WriteImageElement(SmfImageElement elem, XElement parent) { var xElem = new XElement(EN + "ImageElement", new XAttribute("Id", elem.Id)); AddBounds(xElem, elem.Bounds); if (elem.Rotation != 0) xElem.Add(new XElement(EN + "Rotation", elem.Rotation)); xElem.Add(new XElement(EN + "Source", elem.Source)); if (elem.Crop != null) { xElem.Add(new XElement(EN + "Crop", new XElement(EN + "Left", elem.Crop.Left), new XElement(EN + "Right", elem.Crop.Right), new XElement(EN + "Top", elem.Crop.Top), new XElement(EN + "Bottom", elem.Crop.Bottom))); } parent.Add(xElem); } private void WriteShapeElement(SmfShapeElement elem, XElement parent) { var xElem = new XElement(EN + "ShapeElement", new XAttribute("Id", elem.Id)); AddBounds(xElem, elem.Bounds); if (elem.Rotation != 0) xElem.Add(new XElement(EN + "Rotation", elem.Rotation)); xElem.Add(new XElement(EN + "ShapeType", elem.ShapeType.ToString())); var fillElem = new XElement(EN + "Fill"); fillElem.Add(new XElement(EN + "Type", elem.Fill.Type.ToString())); if (elem.Fill.SolidColor != null) fillElem.Add(new XElement(EN + "Color", elem.Fill.SolidColor.Value)); if (fillElem.HasElements) xElem.Add(fillElem); if (elem.Stroke != null) { var sElem = new XElement(EN + "Stroke"); if (elem.Stroke.Color != null) sElem.Add(new XElement(EN + "Color", elem.Stroke.Color.Value)); sElem.Add(new XElement(EN + "Width", elem.Stroke.Width)); sElem.Add(new XElement(EN + "DashStyle", elem.Stroke.DashStyle.ToString())); xElem.Add(sElem); } if (elem.TextContent != null) { var rtElem = new XElement(EN + "TextContent"); foreach (var para in elem.TextContent.Paragraphs) { var pElem = new XElement(EN + "Paragraph"); foreach (var run in para.Runs) { pElem.Add(new XElement(EN + "Run", new XElement(EN + "Text", run.Text))); } if (pElem.HasElements) rtElem.Add(pElem); } xElem.Add(rtElem); } xElem.Add(new XElement(EN + "Locked", elem.Locked)); parent.Add(xElem); } private void WriteTableElement(SmfTableElement elem, XElement parent) { var xElem = new XElement(EN + "TableElement", new XAttribute("Id", elem.Id)); AddBounds(xElem, elem.Bounds); var colsElem = new XElement(EN + "Columns"); foreach (var w in elem.ColumnWidths) colsElem.Add(new XElement(EN + "Column", new XAttribute("Width", w))); xElem.Add(colsElem); var rowsElem = new XElement(EN + "Rows"); foreach (var row in elem.Rows) { var rElem = new XElement(EN + "Row", new XAttribute("Height", row.Height)); foreach (var cell in row.Cells) { var cElem = new XElement(EN + "Cell"); if (cell.RowSpan > 1) cElem.Add(new XAttribute("RowSpan", cell.RowSpan)); if (cell.ColSpan > 1) cElem.Add(new XAttribute("ColSpan", cell.ColSpan)); if (cell.Content != null) { var rtElem = new XElement(EN + "RichText"); foreach (var para in cell.Content.Paragraphs) { var pElem = new XElement(EN + "Paragraph"); foreach (var run in para.Runs) { pElem.Add(new XElement(EN + "Run", new XElement(EN + "Text", run.Text))); } if (pElem.HasElements) rtElem.Add(pElem); } if (rtElem.HasElements) cElem.Add(rtElem); } rElem.Add(cElem); } rowsElem.Add(rElem); } xElem.Add(rowsElem); parent.Add(xElem); } private void WriteInkElement(SmfInkElement elem, XElement parent) { var xElem = new XElement(EN + "InkElement", new XAttribute("Id", elem.Id)); foreach (var stroke in elem.Strokes) { var sElem = new XElement(EN + "Stroke"); sElem.Add(new XElement(EN + "Color", stroke.Color.Value)); sElem.Add(new XElement(EN + "Width", stroke.Width)); sElem.Add(new XElement(EN + "Highlighter", stroke.IsHighlighter)); sElem.Add(new XElement(EN + "Transparency", stroke.Transparency)); var ptsElem = new XElement(EN + "Points"); foreach (var pt in stroke.Points) { ptsElem.Add(new XElement(EN + "Point", new XAttribute("X", pt.X), new XAttribute("Y", pt.Y), new XAttribute("P", pt.Pressure))); } sElem.Add(ptsElem); xElem.Add(sElem); } parent.Add(xElem); } private void AddBounds(XElement parent, SmfBounds bounds) { parent.Add(new XElement(EN + "Bounds", new XElement(EN + "X", bounds.X), new XElement(EN + "Y", bounds.Y), new XElement(EN + "Width", bounds.Width), new XElement(EN + "Height", bounds.Height))); } private void WriteContentTypes(Package enbx) { var uri = PackUriHelper.CreatePartUri(new Uri("/[Content_Types].xml", UriKind.Relative)); var part = enbx.CreatePart(uri, "application/xml"); using (var sw = new StreamWriter(part.GetStream())) { sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); sw.Write(""); for (int i = 0; i < 100; i++) sw.Write($""); sw.Write(""); } } private void WritePackageRels(Package enbx) { var relsUri = PackUriHelper.CreatePartUri(new Uri("/_rels/.rels", UriKind.Relative)); var relsPart = enbx.CreatePart(relsUri, "application/vnd.openxmlformats-package.relationships+xml"); using (var sw = new StreamWriter(relsPart.GetStream())) { sw.Write(""); sw.Write(""); sw.Write(""); } } private void WriteMediaFiles(SmfDocument doc, Package enbx) { foreach (var (fileName, data) in doc.MediaFiles) { var mediaUri = PackUriHelper.CreatePartUri(new Uri($"/media/{fileName}", UriKind.Relative)); var ct = doc.MediaContentTypes.TryGetValue(fileName, out var contentType) ? contentType : "application/octet-stream"; var part = enbx.CreatePart(mediaUri, ct); using var stream = part.GetStream(); stream.Write(data, 0, data.Length); } } } }