using FastReport.Utils; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Text; using System.Windows.Forms; namespace FastReport.Export.Odf { /// /// Base class for any ODF exports. /// public partial class ODFExport : ExportBase { #region Constants private const float odfDivider = 37.82f; private const float odfMargDiv = 10f; private const float odfPageDiv = 10f; #endregion Constants /// /// Enum of OpenOffice formats. /// public enum OpenOfficeFormat { /// /// OpenOffice Spreadsheet format. /// Spreadsheet, /// /// OpenOffice Writer format. /// Writer } /// /// Standard of ODF format. /// public enum OdfStandard { /// /// ODF 1.0/1.1 /// None = 0, /// /// ODF 1.2 /// Odf1_2 = 1, /// /// XODF 1.0/1.1 /// Xodf1_1 = 2, /// /// XODF 1.2 /// Xodf1_2 = 3 } #region Private fields private string creator; private OpenOfficeFormat exportType; private bool firstPage; private ExportMatrix matrix; private float pageBottom; private bool pageBreaks; private float pageHeight; private bool pageLandscape; private float pageLeft; private float pageRight; private float pageTop; private float pageWidth; private bool wysiwyg; private OdfStandard odfCompliance = OdfStandard.None; private bool isXODT = false; private bool useTagText = false; private bool writeVersion = false; private bool extendedStyles = false; private bool writeRdf = false; private CultureInfo localization; private bool exportLocale; #endregion Private fields #region Properties /// /// Creator of the document /// public string Creator { get { return creator; } set { creator = value; } } /// /// Is XODT format /// public bool IsXOTD { get { return isXODT; } set { isXODT = value; } } /// /// Switch of page breaks /// public bool PageBreaks { get { return pageBreaks; } set { pageBreaks = value; } } /// /// Wysiwyg mode, set for better results /// public bool Wysiwyg { get { return wysiwyg; } set { wysiwyg = value; } } /// /// Gets or sets locale for all document. /// public CultureInfo Locale { get { return localization; } set { localization = value; } } /// /// Gets or sets a value indicating that locale export are enabled. /// public bool ExportLocale { get { return exportLocale; } set { exportLocale = value; } } internal OpenOfficeFormat ExportType { get { return exportType; } set { exportType = value; } } /// /// Gets or sets ODF Compliance standard. /// public OdfStandard OdfCompliance { get { return odfCompliance; } set { odfCompliance = value; switch (odfCompliance) { case OdfStandard.Xodf1_1: useTagText = true; isXODT = true; writeVersion = false; extendedStyles = false; writeRdf = false; break; case OdfStandard.None: writeVersion = false; extendedStyles = false; writeRdf = false; break; case OdfStandard.Xodf1_2: useTagText = true; isXODT = true; writeVersion = true; extendedStyles = true; writeRdf = true; break; case OdfStandard.Odf1_2: writeVersion = true; extendedStyles = true; writeRdf = true; break; } } } #endregion Properties #region Private Methods private string GetOdfVersion() { switch (OdfCompliance) { case OdfStandard.Xodf1_2: case OdfStandard.Odf1_2: return "1.2"; case OdfStandard.Xodf1_1: case OdfStandard.None: default: return "1.0"; } } private int ExportCell(Stream file, ZipArchive zip, ExportIEMObject obj, int dx, int dy, ref int picCount) { Write(file, String.Format(" 1 || dy > 1) { Write(file, String.Format("table:number-columns-spanned=\"{0}\" ", dx.ToString())); Write(file, String.Format("table:number-rows-spanned=\"{0}\" ", dy.ToString())); } WriteLine(file, ">"); if (obj.IsText) { // text ExportText(file, obj); } else if (obj.Width > 0) { // picture ExportPicture(file, zip, obj, ++picCount); } WriteLine(file, ""); return picCount; } private void ExportODF(Stream stream) { ZipArchive zip = new ZipArchive(); string ExportMime = exportType == OpenOfficeFormat.Spreadsheet ? "spreadsheet" : "text"; OdfCreateMime(zip, "mimetype", ExportMime); OdfMakeDocStyles(zip, "styles.xml"); #region Content.xml MemoryStream file = new MemoryStream(); WriteLine(file, ""); Write(file, ""); WriteLine(file, ""); OdfFontFaceDecals(file); OdfAutomaticStyles(file); // body WriteLine(file, ""); if (!useTagText) WriteLine(file, ""); else WriteLine(file, ""); TableBegin(file, 1); // rows int picCount = OdfRows(file, zip); TableEnd(file); if (!useTagText) WriteLine(file, ""); else WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); zip.AddStream("content.xml", file); #endregion Content.xml OdfCreateManifest(zip, "META-INF/manifest.xml", picCount, ExportMime); OdfCreateMeta(zip, "meta.xml", Creator); if (writeRdf) OdfCreateRDF(zip, "manifest.rdf"); zip.SaveToStream(Stream); zip.Clear(); } private void ExportPicture(Stream file, ZipArchive zip, ExportIEMObject obj, int picCount) { string picName = picCount.ToString() + ".png"; zip.AddStream("Pictures/Pic" + picName, obj.PictureStream); if (exportType == OpenOfficeFormat.Writer) Write(file, ""); Write(file, String.Format("", (picCount - 1).ToString(), picCount.ToString(), GetStringValue(obj.Width), GetStringValue(obj.Height) )); Write(file, String.Format("", picName )); Write(file, ""); if (exportType == OpenOfficeFormat.Writer) WriteLine(file, ""); } private void ExportText(Stream file, ExportIEMObject obj) { Write(file, ""); WriteLine(file, String.Format("{0}", ExportUtils.OdtString(obj.Text, obj.TextRenderType))); } private string GetBorderLineStyle(BorderLine line, string name) { return String.Format("fo:border-{0}=\"{1}cm {2} {3}\" ", name, GetStringValue(line.Width), OdfGetFrameName(line.Style), ExportUtils.HTMLColorCode(line.Color)); } private string GetStringValue(float value) { return ExportUtils.FloatToString(value / odfDivider); } private void HorizAlignStyle(Stream file, HorzAlign hAlign) { if (hAlign == HorzAlign.Left) Write(file, "fo:text-align=\"start\" "); else if (hAlign == HorzAlign.Center) Write(file, "fo:text-align=\"center\" "); else if (hAlign == HorzAlign.Right) Write(file, "fo:text-align=\"end\" "); else if (hAlign == HorzAlign.Justify) Write(file, "fo:text-align=\"justify\" "); } private void MarginStyle(Stream file, Padding padding) { if (padding.Left > 0) Write(file, String.Format("fo:margin-left=\"{0}cm\" ", GetStringValue(padding.Left))); if (padding.Right > 0) Write(file, String.Format("fo:margin-right=\"{0}cm\" ", GetStringValue(padding.Right))); if (padding.Top > 0) Write(file, String.Format("fo:margin-top=\"{0}cm\" ", GetStringValue(padding.Top))); if (padding.Bottom > 0) Write(file, String.Format("fo:margin-bottom=\"{0}cm\" ", GetStringValue(padding.Bottom))); } private void OdfAutomaticStyles(Stream file) { WriteLine(file, ""); OdfColumnStyles(file); OdfRowStyles(file); OdfStyles(file); if (exportType == OpenOfficeFormat.Writer) { WriterStyles(file); } WriteLine(file, ""); Write(file, ""); WriteLine(file, ""); WriteLine(file, ""); } private void OdfColumns(Stream file) { for (int x = 1; x < matrix.Width; x++) WriteLine(file, String.Format("", GetStringValue((matrix.XPosById(x) - matrix.XPosById(x - 1))) )); } private void OdfColumnStyles(Stream file) { List fList = new List(); for (int i = 1; i < matrix.Width; i++) { string s = GetStringValue((matrix.XPosById(i) - matrix.XPosById(i - 1))); if (fList.IndexOf(s) == -1) fList.Add(s); } fList.Sort(); for (int i = 0; i < fList.Count; i++) { WriteLine(file, String.Format( "", fList[i] )); WriteLine(file, String.Format( "", fList[i])); } } private void OdfCreateManifest(ZipArchive zip, string fileName, int PicCount, string MValue) { MemoryStream file = new MemoryStream(); WriteLine(file, ""); Write(file, ""); WriteLine(file, String.Format(" ", MValue)); WriteLine(file, " "); WriteLine(file, " "); if (writeRdf) WriteLine(file, " "); WriteLine(file, " "); for (int i = 1; i <= PicCount; i++) WriteLine(file, String.Format(" ", i.ToString())); WriteLine(file, ""); zip.AddStream(fileName, file); } private void OdfCreateMeta(ZipArchive zip, string fileName, string Creator) { StringBuilder sb = new StringBuilder(570); sb.AppendLine(""); sb.Append(""); sb.AppendLine(" "); sb.Append(" fast-report.com/Fast Report.NET/build:").Append(Config.Version).AppendLine(""); sb.Append(" ").Append(ExportUtils.XmlString(Creator, TextRenderType.Default)).AppendLine(""); sb.Append(" ").Append(SystemFake.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss")).AppendLine(""); sb.AppendLine(" "); sb.AppendLine(""); MemoryStream file = new MemoryStream(); Write(file, sb.ToString()); zip.AddStream(fileName, file); } private void OdfCreateRDF(ZipArchive zip, string fileName) { MemoryStream file = new MemoryStream(); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, " "); WriteLine(file, ""); zip.AddStream(fileName, file); } private void OdfCreateMime(ZipArchive zip, string fileName, string MValue) { MemoryStream file = new MemoryStream(); Write(file, String.Concat("application/vnd.oasis.opendocument.", MValue)); zip.AddStream(fileName, file); } private void OdfFontFaceDecals(Stream file) { List fList = new List(); for (int i = 0; i < matrix.StylesCount; i++) { ExportIEMStyle style = matrix.StyleById(i); if ((style.Font != null) && (fList.IndexOf(style.Font.Name) == -1)) fList.Add(style.Font.Name); } WriteLine(file, ""); fList.Sort(); for (int i = 0; i < fList.Count; i++) { WriteLine(file, String.Format( "", fList[i] )); } WriteLine(file, ""); } private string OdfGetFrameName(LineStyle style) { switch (style) { case LineStyle.Dash: case LineStyle.DashDot: case LineStyle.DashDotDot: case LineStyle.Dot: return "solid"; case LineStyle.Double: return "double"; default: return "solid"; } } private void OdfMakeDocStyles(ZipArchive zip, string fileName) { MemoryStream file = new MemoryStream(); WriteLine(file, ""); Write(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, String.Format("", ExportUtils.FloatToString(pageWidth / odfPageDiv), ExportUtils.FloatToString(pageHeight / odfPageDiv), ExportUtils.FloatToString(pageTop / odfMargDiv), ExportUtils.FloatToString(pageBottom / odfMargDiv), ExportUtils.FloatToString(pageLeft / odfMargDiv), ExportUtils.FloatToString(pageRight / odfMargDiv) )); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); zip.AddStream(fileName, file); } private string OdfMakeXmlHeader() { return " xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"" + " xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"" + " xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"" + " xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"" + " xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"" + " xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"" + " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" + " xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"" + " xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"" + " xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"" + " xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"" + " xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"" + " xmlns:math=\"http://www.w3.org/1998/Math/MathML\"" + " xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"" + " xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"" + " xmlns:dom=\"http://www.w3.org/2001/xml-events\"" + " xmlns:xforms=\"http://www.w3.org/2002/xforms\"" + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""; } private int OdfRows(Stream file, ZipArchive zip) { int picCount = 0; int page = 0; for (int y = 0; y < matrix.Height - 1; y++) { string pageB = String.Empty; if ((pageBreaks) && (matrix.YPosById(y) >= matrix.PageBreak(page))) { page++; pageB = "pb"; TableEnd(file); TableBegin(file, page + 1); } WriteLine(file, String.Format("", GetStringValue((matrix.YPosById(y + 1) - matrix.YPosById(y))), pageB)); for (int x = 0; x < matrix.Width - 1; x++) { int i = matrix.Cell(x, y); if (i != -1) { ExportIEMObject obj = matrix.ObjectById(i); if (obj.Counter == 0) { obj.Counter = 1; int fx, fy, dx, dy; matrix.ObjectPos(i, out fx, out fy, out dx, out dy); ExportCell(file, zip, obj, dx, dy, ref picCount); } else { Write(file, ""); } } else { Write(file, ""); else WriteLine(file, "/>"); } } WriteLine(file, ""); } return picCount; } private void OdfRowStyles(Stream file) { List> positions = new List>(); int page = 0; for (int i = 0; i < matrix.Height - 1; i++) { bool breakValue = false; if ((pageBreaks) && (matrix.YPosById(i) >= matrix.PageBreak(page))) { page++; breakValue = true; } KeyValuePair value = new KeyValuePair(matrix.YPosById(i + 1) - matrix.YPosById(i), breakValue); if (positions.IndexOf(value) == -1) { positions.Add(value); } } for (int i = 0; i < positions.Count; i++) { string rowPos = GetStringValue(positions[i].Key); string pageB = positions[i].Value ? "pb" : String.Empty; WriteLine(file, String.Format( "", rowPos, pageB)); string pageAuto = positions[i].Value ? "page" : "auto"; WriteLine(file, String.Format( "", pageAuto, rowPos)); WriteLine(file, ""); } WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); WriteLine(file, ""); } private void OdfStyles(Stream file) { for (int i = 0; i < matrix.StylesCount; i++) { ExportIEMStyle style = matrix.StyleById(i); WriteLine(file, String.Format("", i.ToString())); OdfTableCellStyles(file, style); if (exportType == OpenOfficeFormat.Spreadsheet) { ParagraphStyle(file, style); TextPropertiesStyle(file, style); } WriteLine(file, ""); } } private void OdfTableCellStyles(Stream file, ExportIEMStyle style) { Write(file, String.Format(" 0) { Write(file, String.Format("style:rotation-angle=\"{0}\" style:rotation-align=\"none\" ", (360 - style.Angle).ToString())); } if (style.VAlign == VertAlign.Center) Write(file, "style:vertical-align=\"middle\" "); if (style.VAlign == VertAlign.Top) Write(file, "style:vertical-align=\"top\" "); if (style.VAlign == VertAlign.Bottom) Write(file, "style:vertical-align=\"bottom\" "); if ((style.Border.Lines & BorderLines.Left) > 0) Write(file, GetBorderLineStyle(style.Border.LeftLine, "left")); if ((style.Border.Lines & BorderLines.Right) > 0) Write(file, GetBorderLineStyle(style.Border.RightLine, "right")); if ((style.Border.Lines & BorderLines.Top) > 0) Write(file, GetBorderLineStyle(style.Border.TopLine, "top")); if ((style.Border.Lines & BorderLines.Bottom) > 0) Write(file, GetBorderLineStyle(style.Border.BottomLine, "bottom")); Write(file, "/>"); } private void ParagraphStyle(Stream file, ExportIEMStyle style) { Write(file, ""); } private void TableBegin(Stream file, int tableNumber) { // table WriteLine(file, String.Format("", tableNumber)); // columns OdfColumns(file); } private void TableEnd(Stream file) { WriteLine(file, ""); } private void TextPropertiesStyle(Stream file, ExportIEMStyle style) { Write(file, String.Format(" 0) Write(file, " style:text-underline-style=\"solid\" " + "style:text-underline-width=\"auto\" " + "style:text-underline-color=\"font-color\" "); if ((style.Font.Style & FontStyle.Italic) > 0) Write(file, " style:font-style=\"italic\" "); if ((style.Font.Style & FontStyle.Bold) > 0) Write(file, " fo:font-weight=\"bold\" style:font-weight-asian=\"bold\" style:font-weight-complex=\"bold\""); WriteLine(file, String.Format(" fo:color=\"{0}\"/>", ExportUtils.HTMLColorCode(style.TextColor))); } private void Write(Stream stream, string value) { byte[] buf = Encoding.UTF8.GetBytes(value); stream.Write(buf, 0, buf.Length); } private void WriteLine(Stream stream, string value) { byte[] buf = Encoding.UTF8.GetBytes(value); stream.Write(buf, 0, buf.Length); stream.WriteByte(13); stream.WriteByte(10); } private void WriterStyles(Stream file) { for (int i = 0; i < matrix.StylesCount; i++) { ExportIEMStyle style = matrix.StyleById(i); WriteLine(file, String.Format("", i.ToString())); ParagraphStyle(file, style); TextPropertiesStyle(file, style); WriteLine(file, ""); } } #endregion Private Methods #region Protected Methods /// protected override void ExportBand(BandBase band) { base.ExportBand(band); matrix.AddBand(band, this); } /// protected override void ExportPageBegin(ReportPage page) { base.ExportPageBegin(page); matrix.AddPageBegin(page); } /// protected override void ExportPageEnd(ReportPage page) { matrix.AddPageEnd(page); if (firstPage) { pageBottom = page.BottomMargin; pageLeft = page.LeftMargin; pageRight = page.RightMargin; pageTop = page.TopMargin; pageWidth = ExportUtils.GetPageWidth(page); pageHeight = ExportUtils.GetPageHeight(page); pageLandscape = page.Landscape; firstPage = false; } } /// protected override void Finish() { matrix.Prepare(); ExportODF(Stream); } /// protected override void Start() { base.Start(); matrix = new ExportMatrix(); if (wysiwyg) matrix.Inaccuracy = 0.5f; else matrix.Inaccuracy = 10; matrix.RotatedAsImage = true; matrix.PlainRich = true; matrix.AreaFill = true; matrix.Report = Report; matrix.MaxCellHeight = 400; matrix.Images = true; matrix.ImageFormat = ImageFormat.Png; matrix.ShowProgress = ShowProgress; firstPage = true; } #endregion Protected Methods #region Public Constructors /// /// Initializes a new instance of the class. /// public ODFExport() { exportType = OpenOfficeFormat.Spreadsheet; ExportLocale = false; pageBreaks = true; wysiwyg = true; creator = "FastReport .NET"; } #endregion Public Constructors #region Public Methods /// public override void Serialize(FRWriter writer) { base.Serialize(writer); writer.WriteBool("Wysiwyg", Wysiwyg); writer.WriteBool("PageBreaks", PageBreaks); writer.WriteBool("ExportLocale", ExportLocale); writer.WriteValue("Locale", Locale); } #endregion Public Methods } /// /// Open Document Spreadsheet export (Open Office Calc). /// public class ODSExport : ODFExport { #region Public Constructors /// /// Initializes a new instance of the class. /// public ODSExport() { ExportType = OpenOfficeFormat.Spreadsheet; } #endregion Public Constructors #region Protected Methods /// protected override string GetFileFilter() { if (IsXOTD) return new MyRes("FileFilters").Get("XodsFile"); return new MyRes("FileFilters").Get("OdsFile"); } #endregion Protected Methods } /// /// Open Document Text export (Open Office Writer). /// public class ODTExport : ODFExport { #region Public Constructors /// /// Initializes a new instance of the class. /// public ODTExport() { ExportType = OpenOfficeFormat.Writer; } #endregion Public Constructors #region Protected Methods /// protected override string GetFileFilter() { if (IsXOTD) return new MyRes("FileFilters").Get("XodtFile"); return new MyRes("FileFilters").Get("OdtFile"); } #endregion Protected Methods } }