Files
Better-Seewo/Manager/Services/Conversion/EnbxWriter.cs

401 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Xml.Linq;
namespace BetterSeewo.Manager.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<string>();
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<string> 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("<?xml version=\"1.0\" encoding=\"utf-8\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">");
sw.Write("<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>");
sw.Write("<Default Extension=\"xml\" ContentType=\"application/xml\"/>");
sw.Write("<Default Extension=\"png\" ContentType=\"image/png\"/>");
sw.Write("<Default Extension=\"jpg\" ContentType=\"image/jpeg\"/>");
sw.Write("<Default Extension=\"jpeg\" ContentType=\"image/jpeg\"/>");
sw.Write("<Default Extension=\"gif\" ContentType=\"image/gif\"/>");
sw.Write("<Default Extension=\"bmp\" ContentType=\"image/bmp\"/>");
sw.Write("<Default Extension=\"mp4\" ContentType=\"video/mp4\"/>");
sw.Write("<Default Extension=\"mp3\" ContentType=\"audio/mpeg\"/>");
sw.Write("<Override PartName=\"/document.xml\" ContentType=\"application/vnd.seewo.easinote.document+xml\"/>");
for (int i = 0; i < 100; i++)
sw.Write($"<Override PartName=\"/slides/slide{i + 1}.xml\" ContentType=\"application/vnd.seewo.easinote.slide+xml\"/>");
sw.Write("</Types>");
}
}
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("<?xml version=\"1.0\" encoding=\"utf-8\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">");
sw.Write("<Relationship Id=\"rId1\" Type=\"http://schemas.seewo.com/easinote/relationships/document\" Target=\"document.xml\"/>");
sw.Write("</Relationships>");
}
}
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);
}
}
}
}