using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.IO; using FastReport.Utils; using FastReport.RichTextParser; using System.Globalization; namespace FastReport.Export.RichText { /// /// Specifies the image format in RTF export. /// public enum RTFImageFormat { /// /// Specifies the .png format. /// Png, /// /// Specifies the .jpg format. /// Jpeg, /// /// Specifies the .emf format. /// Metafile } /// /// Represents the RTF export filter. /// public partial class RTFExport : ExportBase { #region Constants const float Xdivider = 15.05F; const float Ydivider1 = 14.8F; const float Ydivider2 = 14.8F; const float Ydivider3 = 14.7F; const float MargDivider = 56.904405f; // 3.793627f * 15 | PxInMm * TwipsInPX const float FONT_DIVIDER = 15F; const float IMAGE_DIVIDER = 25.3F; const int PIC_BUFF_SIZE = 512; #endregion #region Private fields private List colorTable; private bool pageBreaks; private List fontTable; private ExportMatrix matrix; private bool wysiwyg; private bool printOptimized; private string creator; private bool autoSize; private MyRes res; private RTFImageFormat imageFormat; private bool pictures; private string tempFile; private int jpegQuality; private float dpiFactor; private float yDiv; private Color textColor; private bool keepRichText; private CultureInfo localization; private bool exportLocale; #endregion #region Properties /// /// Gets or sets the quality of Jpeg images in RTF file. /// /// /// Default value is 90. This property will be used if you select Jpeg /// in the property. /// public int JpegQuality { get { return jpegQuality; } set { jpegQuality = value; } } /// /// Gets or sets the image format that will be used to save pictures in RTF file. /// /// /// Default value is Metafile. This format is better for exporting such objects as /// MSChartObject and ShapeObject. /// public RTFImageFormat ImageFormat { get { return imageFormat; } set { imageFormat = value; } } /// /// Gets or sets a value indicating that pictures are enabled. /// public bool Pictures { get { return pictures; } set { pictures = value; } } /// /// Gets or sets a value indicating that page breaks are enabled. /// public bool PageBreaks { get { return pageBreaks; } set { pageBreaks = value; } } /// /// Get or set a 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; } } /// /// Gets or sets a value that determines whether the wysiwyg mode should be used /// for better results. /// /// /// Default value is true. In wysiwyg mode, the resulting rtf file will look /// as close as possible to the prepared report. On the other side, it may have a lot /// of small rows/columns, which will make it less editable. If you set this property /// to false, the number of rows/columns in the resulting file will be decreased. /// You will get less wysiwyg, but more editable file. /// public bool Wysiwyg { get { return wysiwyg; } set { wysiwyg = value; } } /// /// Gets or sets the PrintOptimized. /// public bool PrintOptimized { get { return printOptimized; } set { printOptimized = value; } } /// /// Gets or sets the creator of the document. /// public string Creator { get { return creator; } set { creator = value; } } /// /// Gets or sets a value that determines whether the rows in the resulting table /// should calculate its height automatically. /// /// /// Default value for this property is false. In this mode, each row in the /// resulting table has fixed height to get maximum wysiwyg. If you set it to true, /// the height of resulting table will be calculated automatically by the Word processor. /// The document will be more editable, but less wysiwyg. /// public bool AutoSize { get { return autoSize; } set { autoSize = value; } } /// /// Gets or sets a value that determines whether the repot's RichObject will be /// translated as picture or joined to generated RTF. /// /// /// Default value for this property is false. In this mode, each RichObject /// will be embedded as a picture. This is default behavior. If you set it to true, /// the RichObject will be incorporated as a navive part of document. This is experimetal /// feature. /// public bool EmbedRichObject { get { return keepRichText; } set { keepRichText = value; } } #endregion #region Private Methods private string GetRTFBorders(ExportIEMStyle Style) { //// +debug //Style.Border.Lines = BorderLines.All; //Style.Border.Width = 1; //// -debug StringBuilder result = new StringBuilder(256); // top if ((Style.Border.Lines & BorderLines.Top) > 0) result.Append("\\clbrdrt"). Append(GetRTFLineStyle(Style.Border.TopLine.Style)). Append("\\brdrw"). Append(((int)Math.Round(Style.Border.TopLine.Width * 20)).ToString()). Append("\\brdrcf"). Append(GetRTFColorFromTable(GetRTFColor(Style.Border.TopLine.Color))); // left if ((Style.Border.Lines & BorderLines.Left) > 0) result.Append("\\clbrdrl"). Append(GetRTFLineStyle(Style.Border.LeftLine.Style)). Append("\\brdrw"). Append(((int)Math.Round(Style.Border.LeftLine.Width * 20)).ToString()). Append("\\brdrcf"). Append(GetRTFColorFromTable(GetRTFColor(Style.Border.LeftLine.Color))); // bottom if ((Style.Border.Lines & BorderLines.Bottom) > 0) result.Append("\\clbrdrb"). Append(GetRTFLineStyle(Style.Border.BottomLine.Style)). Append("\\brdrw"). Append(((int)Math.Round(Style.Border.BottomLine.Width * 20)).ToString()). Append("\\brdrcf"). Append(GetRTFColorFromTable(GetRTFColor(Style.Border.BottomLine.Color))); // right if ((Style.Border.Lines & BorderLines.Right) > 0) result.Append("\\clbrdrr"). Append(GetRTFLineStyle(Style.Border.RightLine.Style)). Append("\\brdrw"). Append(((int)Math.Round(Style.Border.RightLine.Width * 20)).ToString()). Append("\\brdrcf"). Append(GetRTFColorFromTable(GetRTFColor(Style.Border.RightLine.Color))); return result.ToString(); } private string GetRTFLineStyle(LineStyle lineStyle) { switch (lineStyle) { case LineStyle.Dash: return "\\brdrdash"; case LineStyle.DashDot: return "\\brdrdashd"; case LineStyle.DashDotDot: return "\\brdrdashdd"; case LineStyle.Dot: return "\\brdrdot"; case LineStyle.Double: return "\\brdrdb"; default: return "\\brdrs"; } } private string GetRTFColor(Color c) { StringBuilder result = new StringBuilder(64); result.Append("\\red").Append(Convert.ToString(c.R)). Append("\\green").Append(Convert.ToString(c.G)). Append("\\blue").Append(Convert.ToString(c.B)).Append(";"); return result.ToString(); } private string GetRTFFontStyle(FontStyle f) { StringBuilder result = new StringBuilder(8); if ((f & FontStyle.Italic) != 0) result.Append("\\i"); if ((f & FontStyle.Bold) != 0) result.Append("\\b"); if ((f & FontStyle.Underline) != 0) result.Append("\\ul"); return result.ToString(); } private string GetRTFColorFromTable(string f) { string Result; int i = colorTable.IndexOf(f); if (i != -1) Result = (i + 1).ToString(); else { colorTable.Add(f); Result = colorTable.Count.ToString(); } return Result; } private string GetRTFFontName(string f) { string Result; int i = fontTable.IndexOf(f); if (i != -1) Result = (i).ToString(); else { fontTable.Add(f); Result = (fontTable.Count - 1).ToString(); } return Result; } private string GetRTFHAlignment(HorzAlign HAlign) { switch (HAlign) { case HorzAlign.Right: return "\\qr"; case HorzAlign.Center: return "\\qc"; case HorzAlign.Justify: return "\\qj"; default: return "\\ql"; } } private string GetRTFVAlignment(VertAlign VAlign) { switch (VAlign) { case VertAlign.Top: return "\\clvertalt"; case VertAlign.Center: return "\\clvertalc"; default: return "\\clvertalb"; } } private string StrToRTFSlash(string Value) { StringBuilder Result = new StringBuilder(); for (int i = 0; i < Value.Length; i++) { if (Value[i] == '\\') Result.Append("\\\\"); else if (Value[i] == '{') Result.Append("\\{"); else if (Value[i] == '}') Result.Append("\\}"); else if ((Value[i] == '\r') && (i < (Value.Length - 1)) && (Value[i + 1] == '\n')) { Result.Append("\\line\r\n"); i++; } else Result.Append(Value[i]); } return Result.ToString(); } private string ParseHtmlTags(string s) { int Index = 0; int Begin = 0; int End = 0; string Tag; string Text; string result; string TagClose = ""; CurrentStyle current_style = new CurrentStyle(); CurrentStyle previos_style; current_style.Size = 10; current_style.Bold = false; current_style.Italic = false; current_style.Underline = false; current_style.Colour = Color.FromName("Black"); current_style.Strike = false; current_style.Sub = false; current_style.Sup = false; Stack style_stack = new Stack(); Begin = s.IndexOfAny(new char[1] { '<' }, Index); if (Begin == -1) result = s; else { result = ""; while (Begin != -1) { if (Begin != 0 && Index == 0) { if (Index == 0) { result += s.Substring(Index, Begin); } } End = s.IndexOfAny(new char[1] { '>' }, Begin + 1); if (End == -1) break; Tag = s.Substring(Begin + 1, End - Begin - 1); bool CloseTag = Tag.StartsWith("/"); if (CloseTag) Tag = Tag.Remove(0, 1); string[] items = Tag.Split(' '); Tag = items[0].ToUpper(); TagClose = ""; bool PutOnStack = true; if (!CloseTag) { current_style.LastTag = Tag; switch (Tag) { case "B": current_style.Bold = true; TagClose = "\\b "; break; case "I": current_style.Italic = true; TagClose = "\\i "; break; case "U": current_style.Underline = true; TagClose = "\\ul "; break; case "STRIKE": current_style.Strike = true; TagClose = "\\strike "; break; case "SUB": current_style.Sub = true; TagClose = "\\sub "; break; case "SUP": current_style.Sup = true; TagClose = "\\super "; break; case "BR": current_style.Sup = true; TagClose = "\\line "; break; case "FONT": { if (items.Length > 1) { string[] attrs = items[1].Split('='); if (attrs[0] == "color") { TagClose = "\\cf" + GetRTFColorFromTable(GetRTFColor(System.Drawing.ColorTranslator.FromHtml(attrs[1].Replace("\"", "")))) + " "; } } } /*current_style.Font = items[1];*/ // ParseFont(items[1], current_style, out current_style); break; default: TagClose = Tag; PutOnStack = false; break; } if (PutOnStack) style_stack.Push(current_style); } else { if (style_stack.Count > 0) { previos_style = style_stack.Pop(); #if false if (previos_style.LastTag != Tag) { throw new Exception("Unaligned HTML TAGS"); } #endif switch (Tag) { case "B": TagClose = "\\b0 "; break; case "I": TagClose = "\\i0 "; break; case "U": TagClose = "\\ul0 "; break; case "STRIKE": TagClose = "\\strike0 "; break; case "SUB": TagClose = "\\nosupersub "; break; case "SUP": TagClose = "\\nosupersub "; break; case "FONT": TagClose = "\\cf" + GetRTFColorFromTable(GetRTFColor((textColor))) + " "; /*current_style.Font = items[1];*/ // ParseFont(items[1], current_style, out current_style); break; default: throw new Exception("Unsupported HTML TAG"); } current_style = previos_style; } } Index = End + 1; Begin = s.IndexOfAny(new char[1] { '<' }, Index); if (Begin == -1) { Text = s.Substring(Index); } else { Text = s.Substring(Index, Begin - Index); } result += TagClose + Text; } } return result; } private string StrToRTFUnicodeEx(string Value, TextRenderType textRenderType) { Value = StrToRTFUnicode(StrToRTFSlash(Value)); switch (textRenderType) { case TextRenderType.HtmlParagraph: //TODO DETRAV RTF break; case TextRenderType.HtmlTags: Value = ParseHtmlTags(Value); break; } return Value; } private string StrToRTFUnicode(string Value) { StringBuilder Result = new StringBuilder(128); foreach (UInt16 c in Value) { if (c > 127) Result.Append("\\u").Append(c.ToString()).Append("\\'3f"); else Result.Append((char)c); } return Result.ToString(); } private void Prepare() { int i; ExportIEMObject Obj; for (int y = 0; y < matrix.Height; y++) for (int x = 0; x < matrix.Width; x++) { i = matrix.Cell(x, y); if (i != -1) { Obj = matrix.ObjectById(i); if (Obj.Counter != -1) { Obj.Counter = -1; if (Obj.Style != null) { GetRTFColorFromTable(GetRTFColor(Obj.Style.FillColor)); GetRTFColorFromTable(GetRTFColor(Obj.Style.Border.LeftLine.Color)); GetRTFColorFromTable(GetRTFColor(Obj.Style.Border.RightLine.Color)); GetRTFColorFromTable(GetRTFColor(Obj.Style.Border.TopLine.Color)); GetRTFColorFromTable(GetRTFColor(Obj.Style.Border.BottomLine.Color)); GetRTFColorFromTable(GetRTFColor(Obj.Style.TextColor)); GetRTFFontName(Obj.Style.Font.Name); } } } } } private string SetPageProp(int Page) { StringBuilder result = new StringBuilder(64); result.Append("\\pgwsxn"). Append(((int)Math.Round(matrix.PageWidth(Page) * MargDivider)).ToString()). Append("\\pghsxn"). Append(((int)Math.Round(matrix.PageHeight(Page) * MargDivider)).ToString()). Append("\\marglsxn"). Append(((int)Math.Round(matrix.PageLMargin(Page) * MargDivider)).ToString()). Append("\\margrsxn"). Append(((int)Math.Round(matrix.PageRMargin(Page) * MargDivider)).ToString()). Append("\\margtsxn"). Append(((int)Math.Round(matrix.PageTMargin(Page) * MargDivider)).ToString()). Append("\\margbsxn"). Append(((int)Math.Round(matrix.PageBMargin(Page) * MargDivider)).ToString()). Append(matrix.Landscape(Page) ? "\\lndscpsxn" : String.Empty); return result.ToString(); } private void Write(Stream stream, string str) { byte[] buff = Converter.StringToByteArray(str); stream.Write(buff, 0, buff.Length); } private void WriteLine(Stream stream, string str) { Write(stream, str); Write(stream, "\r\n"); } private Stream GetTempFileStream() { tempFile = Path.Combine(Config.GetTempFolder(), Path.GetRandomFileName()); return new FileStream(tempFile, FileMode.Create); } private void DeleteTempFile() { if (File.Exists(tempFile)) File.Delete(tempFile); } private string GetRTFString(ExportIEMObject obj, float drow) { ExportIEMStyle style = obj.Style; string text = obj.Text; TextRenderType textRenderType = obj.TextRenderType; StringBuilder CellsStream = new StringBuilder(); CellsStream.Append(GetRTFHAlignment(style.HAlign)); if (!String.IsNullOrEmpty(obj.URL)) { CellsStream.Append("{\\field{\\*\\fldinst HYPERLINK \"" + obj.URL + "\"}{\\fldrslt"); } else { CellsStream.Append("{"); } CellsStream.Append("\\f").Append(GetRTFFontName(style.Font.Name)); string s = StrToRTFUnicodeEx(ExportUtils.TruncReturns(text), textRenderType); double fh = style.Font.Height * dpiFactor * yDiv * 0.98; double lh = style.LineHeight * 8.0; if (s.Length > 0) { CellsStream.Append("\\fs").Append((Math.Round(style.Font.Size * 2)).ToString()); CellsStream.Append(GetRTFFontStyle(style.Font.Style)).Append("\\cf"); CellsStream.Append(GetRTFColorFromTable(GetRTFColor((style.TextColor)))); CellsStream.Append(style.RTL ? "\\rtlch" : String.Empty); CellsStream.Append("\\sb"). Append(((int)Math.Round(style.Padding.Top * yDiv)).ToString()). Append("\\sa"). Append(((int)Math.Round(style.Padding.Bottom * yDiv)).ToString()). Append("\\li"). Append(((int)Math.Round((style.Padding.Left) * Xdivider)).ToString()). Append("\\ri"). Append(((int)Math.Round((style.Padding.Right) * Xdivider)).ToString()). Append("\\sl-"). Append(((int)Math.Round((fh + lh))).ToString()). Append("\\slmult0 "); if (s.StartsWith("\t") && style.FirstTabOffset != 0) { // replace first tab symbol with \\fi s = "\\fi" + ((int)Math.Round((style.FirstTabOffset) * Xdivider)).ToString() + " " + s.Remove(0, 1); // convert multiline text to rtf paragraphs. \\fi can be applied to a paragraph only s = s.Replace("\\line\r\n\t", "\\par "); } CellsStream.Append(s); } else { int j = (int)(drow / FONT_DIVIDER); j = j > 20 ? 20 : j; CellsStream.Append("\\fs").Append(j.ToString()); } if (!String.IsNullOrEmpty(obj.URL)) { CellsStream.Append("}"); } CellsStream.Append("\\cell}"); return CellsStream.ToString(); } private string GetRTFMetafile(ExportIEMObject Obj) { byte[] picbuff = new Byte[PIC_BUFF_SIZE]; string scale = ((int)(100 / dpiFactor)).ToString(); StringBuilder CellsStream = new StringBuilder(256); if (!String.IsNullOrEmpty(Obj.URL)) { CellsStream.Append("{\\field{\\*\\fldinst HYPERLINK \"" + Obj.URL + "\"}{\\fldrslt"); } else { CellsStream.Append("{"); } Obj.PictureStream.Position = 0; if (pictures && Config.FullTrust && (imageFormat == RTFImageFormat.Metafile)) { scale = (int.Parse(scale) * dpiFactor).ToString(); CellsStream.Append("\\sb0\\li0\\sl0\\slmult0\\qc\\clvertalc {"); float dx = Obj.Width * 15; float dy = Obj.Height * 15; CellsStream.Append("\\pict\\picw").Append(dx.ToString()); CellsStream.Append("\\pich").Append(dy.ToString()); CellsStream.Append("\\picscalex").Append(scale); CellsStream.Append("\\picscaley").Append(scale); CellsStream.Append("\\picwGoal").Append(Convert.ToString(dx)); CellsStream.Append("\\pichGoal").Append(Convert.ToString(dy)); CellsStream.Append("\\emfblip\r\n"); int n; do { n = Obj.PictureStream.Read(picbuff, 0, PIC_BUFF_SIZE); for (int z = 0; z < n; z++) { CellsStream.Append(ExportUtils.XCONV[picbuff[z] >> 4]); CellsStream.Append(ExportUtils.XCONV[picbuff[z] & 0xF]); } CellsStream.Append("\r\n"); } while (n == PIC_BUFF_SIZE); CellsStream.Append("}"); } else if (pictures && (imageFormat != RTFImageFormat.Metafile)) { CellsStream.Append("\\sb0\\li0\\sl0\\slmult0\\qc\\clvertalc {"); float dx = (int)Obj.Width; float dy = (int)Obj.Height; CellsStream.Append("\\pict\\picw").Append(dx.ToString()); CellsStream.Append("\\pich").Append(dy.ToString()); CellsStream.Append("\\picscalex").Append(scale); CellsStream.Append("\\picscaley").Append(scale); CellsStream.Append("\\"); CellsStream.Append(imageFormat == RTFImageFormat.Jpeg ? "jpegblip\r\n" : "pngblip\r\n"); int n; do { n = Obj.PictureStream.Read(picbuff, 0, PIC_BUFF_SIZE); for (int z = 0; z < n; z++) { CellsStream.Append(ExportUtils.XCONV[picbuff[z] >> 4]); CellsStream.Append(ExportUtils.XCONV[picbuff[z] & 0xF]); } CellsStream.Append("\r\n"); } while (n == PIC_BUFF_SIZE); CellsStream.Append("}"); } if (!String.IsNullOrEmpty(Obj.URL)) { CellsStream.Append("}"); } CellsStream.Append("\\cell}\r\n"); return CellsStream.ToString(); } private string GetRTFHeader() { StringBuilder sb = new StringBuilder(); sb.Append("{\\rtf1\\ansi"); sb.Append("{\\fonttbl"); for (int i = 0; i < fontTable.Count; i++) sb.Append("{\\f" + Convert.ToString(i) + " " + fontTable[i] + "}"); sb.Append("}"); sb.Append("{\\colortbl;"); for (int i = 0; i < colorTable.Count; i++) sb.Append(colorTable[i]); sb.Append("}"); return sb.ToString(); } private string GetRTFMetaInfo() { StringBuilder buf = new StringBuilder(256); buf.Append("{\\info{\\title ").Append(StrToRTFUnicodeEx(Report.ReportInfo.Name, TextRenderType.Default)). Append("}{\\author ").Append(StrToRTFUnicodeEx(creator, TextRenderType.Default)). Append("}{\\creatim\\yr").Append(String.Format("{0:yyyy}", SystemFake.DateTime.Now)). Append("\\mo").Append(String.Format("{0:MM}", SystemFake.DateTime.Now)). Append("\\dy").Append(String.Format("{0:dd}", SystemFake.DateTime.Now)). Append("\\hr").Append(String.Format("{0:HH}", SystemFake.DateTime.Now)). Append("\\min").Append(String.Format("{0:mm}", SystemFake.DateTime.Now)).AppendLine("}}"); return buf.ToString(); } struct CurrentStyle { public int Size; public bool Bold; public bool Italic; public bool Underline; public bool Strike; public bool Sub; public bool Sup; public Color Colour; public string LastTag; }; #region Export RichObject to RTF private RichDocument rtf; private RunFormat current_format; private RichTextParser.ParagraphFormat.HorizontalAlign prev_align = RichTextParser.ParagraphFormat.HorizontalAlign.Left; private string HorizontalAlignCode(RichTextParser.ParagraphFormat.HorizontalAlign align) { switch (align) { case RichTextParser.ParagraphFormat.HorizontalAlign.Centered: return "\\qc"; case RichTextParser.ParagraphFormat.HorizontalAlign.Left: return "\\ql"; case RichTextParser.ParagraphFormat.HorizontalAlign.Right: return "\\qr"; case RichTextParser.ParagraphFormat.HorizontalAlign.Justified: return "\\qj"; case RichTextParser.ParagraphFormat.HorizontalAlign.Distributed: return "\\qd"; case RichTextParser.ParagraphFormat.HorizontalAlign.Kashida: return "\\qk"; case RichTextParser.ParagraphFormat.HorizontalAlign.Thai: return "\\qt"; default: return ""; } } private string VerticalAlignCode(Column.VertAlign valign) { switch (valign) { case Column.VertAlign.Top: return "\\clvertalt"; case Column.VertAlign.Center: return "\\clvertalc"; case Column.VertAlign.Bottom: return "\\clvertalb"; default: return ""; } } private void SavePargraph(StringBuilder s, RichTextParser.Paragraph par, bool InTable, float Left) { char lineBreakSumb = ' '; bool skipSpace = false; if (par.runs.Count == 0) { if (!InTable) s.Append("\\pard\\par\n"); else s.Append("\\pard\\intbl\\cell\n"); } else { if (InTable) s.Append("\\intbl\n"); if (par.format.align != prev_align) { s.AppendFormat("{0} ", HorizontalAlignCode(par.format.align)); prev_align = par.format.align; } // add tab stops with taking into consideration left padding of object if (par.format.tab_positions != null && par.format.tab_positions.Count > 0) { foreach (float tab in par.format.tab_positions) s.Append($"\\tx{Math.Round(tab + Left * Xdivider)}"); } foreach (Run r in par.runs) { RunFormat fmt = r.format; if (current_format.bold != fmt.bold) { s.Append(fmt.bold ? "\\b" : "\\b0"); current_format.bold = fmt.bold; } if (current_format.strike != fmt.strike) { s.Append(fmt.strike ? "\\strike" : "\\strike0"); current_format.strike = fmt.strike; } if (current_format.underline != fmt.underline) { s.Append(fmt.underline ? "\\ul" : "\\ulnone"); current_format.underline = fmt.underline; } if (current_format.italic != fmt.italic) { s.Append(fmt.italic ? "\\i" : "\\i0"); current_format.italic = fmt.italic; } if (current_format.color.ToArgb() != fmt.color.ToArgb()) { string color_def = GetRTFColor(fmt.color); int i = colorTable.IndexOf(color_def); if (i != -1) { s.AppendFormat("\\cf{0}", i + 1); } else { colorTable.Add(color_def); s.AppendFormat("\\cf{0}", colorTable.Count); } current_format.color = fmt.color; } if (current_format.font_size != fmt.font_size) { s.AppendFormat("\\fs{0}", fmt.font_size); current_format.font_size = fmt.font_size; } if (current_format.font_idx != fmt.font_idx) { string font_name = rtf.font_list[(int)fmt.font_idx].FontName; int i = fontTable.IndexOf(font_name); if (i != -1) { s.AppendFormat("\\f{0}", i); } else { fontTable.Add(font_name); s.AppendFormat("\\f{0}", (fontTable.Count - 1)); } current_format.font_idx = fmt.font_idx; } // avoid line break second symbol \r\n if ((r.text == "\r" || r.text == "\n") && (lineBreakSumb == r.text[0] || lineBreakSumb == ' ')) { s.Append("\\par "); lineBreakSumb = r.text[0]; skipSpace = true; } else if (r.text == "\t") { s.Append("\\tab "); skipSpace = true; } else { if (!skipSpace) s.Append(" "); s.Append(StrToRTFUnicode(r.text)); skipSpace = false; } } if (!InTable) s.Append("\\par\n"); else s.Append("\\cell\n"); } } private void SaveColumn(StringBuilder s, Column col) { // "clcbpat" - background color s.AppendFormat("{0}\\cellx{1}", VerticalAlignCode(col.valign), col.Width); } private void SaveRow(StringBuilder s, TableRow row, bool InTable) { s.AppendFormat("\\trgaph{0}\\trrh{1}\\trpaddl{2}\\trpaddr{3}", row.trgaph, row.height, row.default_pad_left, row.default_pad_right); foreach (RichObjectSequence seq in row.cells) { SaveSequence(s, seq, true, 0f); if (!InTable) s.AppendLine("\\cell"); else s.AppendLine("\\nestcell"); } if (!InTable) s.Append("\\row"); else s.Append("\\nestrow"); } private void SaveTable(StringBuilder s, RichTextParser.Table tbl, bool InTable) { s.Append("\\trowd"); foreach (Column col in tbl.columns) SaveColumn(s, col); foreach (TableRow row in tbl.rows) SaveRow(s, row, InTable); } private void SavePicture(StringBuilder CellsStream, Picture pic, bool InTable) { CellsStream.Append("\\sb0\\li0\\sl0\\slmult0\\qc\\clvertalc {"); float dx = (int)pic.width; float dy = (int)pic.height; CellsStream.Append("\\pict\\picw").Append(dx.ToString()); CellsStream.Append("\\pich").Append(dy.ToString()); CellsStream.Append("\\picscalex").Append(pic.scalex); CellsStream.Append("\\picscaley").Append(pic.scaley); System.Drawing.Imaging.ImageFormat format; if (imageFormat == RTFImageFormat.Jpeg) { CellsStream.Append("\\jpegblip\r\n"); format = System.Drawing.Imaging.ImageFormat.Jpeg; } else { CellsStream.Append("\\pngblip\r\n"); format = System.Drawing.Imaging.ImageFormat.Png; } int n; byte[] picbuff = new Byte[PIC_BUFF_SIZE]; MemoryStream pic_stream = new MemoryStream(); pic.image.Save(pic_stream, format); pic_stream.Seek(0, SeekOrigin.Begin); do { n = pic_stream.Read(picbuff, 0, PIC_BUFF_SIZE); for (int z = 0; z < n; z++) { CellsStream.Append(ExportUtils.XCONV[picbuff[z] >> 4]); CellsStream.Append(ExportUtils.XCONV[picbuff[z] & 0xF]); } CellsStream.Append("\r\n"); } while (n == PIC_BUFF_SIZE); CellsStream.Append("}"); } private void SaveSequence(StringBuilder s, RichObjectSequence seq, bool InTable, float Left) { for (int i = 0; i < seq.objects.Count; i++) { if (!InTable && i == seq.objects.Count - 1) InTable = true; switch (seq.objects[i].type) { case RichTextParser.RichObject.Type.Paragraph: SavePargraph(s, seq.objects[i].paragraph, InTable, Left); break; case RichTextParser.RichObject.Type.Picture: SavePicture(s, seq.objects[i].picture, InTable); break; case RichTextParser.RichObject.Type.Table: SaveTable(s, seq.objects[i].table, InTable); break; } } } private void TranslateEmbeddedRTF(ExportIEMObject Obj, StringBuilder CellsStream) { current_format.color = Color.FromArgb(0, 0, 0, 0); current_format.BColor = Color.FromArgb(0, 0, 0, 0); current_format.FillColor = Color.FromArgb(0, 0, 0, 0); current_format.bold = false; current_format.strike = false; current_format.italic = false; current_format.underline = false; current_format.font_size = 0; current_format.font_idx = 65536; current_format.script_type = RunFormat.ScriptType.PlainText; using (RTF_DocumentParser parser = new RTF_DocumentParser()) { parser.Load(Obj.Text); rtf = parser.Document; } foreach (Page page in rtf.pages) { CellsStream.AppendFormat(@"\margl{0}\margr{1}\margt{2}\margb{3}", 200, // page.margin_left, page.margin_right, 600, // page.margin_top, page.margin_bottom); CellsStream.Append($"\\li{Math.Round(Obj.Left * Xdivider)}"); SaveSequence(CellsStream, page.sequence, false, Obj.Left); } } private void AddWatermark(Stream stream) { Watermark watermark = Report.PreparedPages.GetPage(0).Watermark; if (watermark.Text != "") { int OLEColor = watermark.TextObject.TextColor.R + (watermark.TextObject.TextColor.G * 256) + (watermark.TextObject.TextColor.B * 256 * 256); double height = watermark.Font.SizeInPoints * 19; double width = 0; int rotation = 0; bool needSwapHeigtAndWidth = false; int countUpperLetter = 0; foreach (var c in watermark.Text) { if (char.IsUpper(c)) countUpperLetter++; } width = (watermark.Text.Length - countUpperLetter) * (height / 2.5) + countUpperLetter * (height / 2 * 1.28); switch (watermark.TextRotation) { case WatermarkTextRotation.Horizontal: { rotation = 0; break; } case WatermarkTextRotation.Vertical: { rotation = -5900000; needSwapHeigtAndWidth = true; break; } case WatermarkTextRotation.ForwardDiagonal: { if (matrix.Landscape(1)) rotation = -2400000; else { rotation = -3600000; needSwapHeigtAndWidth = true; } break; } case WatermarkTextRotation.BackwardDiagonal: { if (!matrix.Landscape(1)) { rotation = 3600000; needSwapHeigtAndWidth = true; } else rotation = 2250000; break; } } if (needSwapHeigtAndWidth) { double temp = height; height = width; width = temp; } Write(stream, @"\headery0{\header\pard\plain\itap0\s0\aspalpha\aspnum\adjustright\ltrpar\li0\lin0\ri0\rin0\ql\faauto\rtlch\afs24\ltrch\fs24{\rtlch\afs24\ltrch\fs2" + "\n" + @"{\shp{\*\shpinst\shplid2051\shpleft0\shpright" + width + @"\shptop0\shpbottom" + height + @"\shpfhdr1\shpz2\shpfblwtxt0" + "\n" + @"\shpbxmargin\shpbxignore\shpbymargin\shpbyignore\shpwr3\shpwrk0{\sp{\sn fLayoutInCell}" + "\n" + @"{\sv 1}" + "\n" + @"}" + "\n" + @"{\sp{\sn rotation}" + "\n" + @"{\sv " + rotation + @"}" + "\n" + @"}" + "\n" + @"{\sp{\sn fFlipH}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn fFlipV}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn gtextUNICODE}" + "\n" + @"{\sv " + StrToRTFUnicodeEx(ExportUtils.TruncReturns(watermark.Text), TextRenderType.Default) + @"}" + "\n" + @"}" + "\n" + @"{\sp{\sn gtextSize}" + "\n" + @"{\sv 123932160}" + "\n" + @"}" + "\n" + @"{\sp{\sn gtextFont}" + "\n" + @"{\sv " + watermark.Font.Name + @"}" + "\n" + @"}" + "\n" + @"{\sp{\sn fGtext}" + "\n" + @"{\sv 1}" + "\n" + @"}" + "\n" + @"{\sp{\sn fillColor}" + "\n" + @"{\sv " + OLEColor + @"}" + "\n" + @"}" + "\n" + @"{\sp{\sn fillOpacity}" + "\n" + @"{\sv 32768}" + "\n" + @"}" + "\n" + @"{\sp{\sn fFilled}" + "\n" + @"{\sv 1}" + "\n" + @"}" + "\n" + @"{\sp{\sn fLine}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn wzName}" + "\n" + @"{\sv PowerPlusWaterMarkObject100001}" + "\n" + @"}" + "\n" + @"{\sp{\sn posh}" + "\n" + @"{\sv 2}" + "\n" + @"}" + "\n" + @"{\sp{\sn posrelh}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn posv}" + "\n" + @"{\sv 2}" + "\n" + @"}" + "\n" + @"{\sp{\sn posrelv}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn shapeType}" + "\n" + @"{\sv 136}" + "\n" + @"}" + "\n" + @"}" + "\n" + @"{\shprslt\par\pard" + "\n" + @"\par}" + "\n" + @"}" + "\n" + @"}" + "\n" + @"{\rtlch\afs24\ltrch\fs24\par}" + "\n" + @"}" ); } else if (watermark.Image != null) { byte[] buffer; int pictureContrast = 60000; int pictureBrightness = -20000; using (var memStream = new MemoryStream()) { watermark.Image.Save(memStream, watermark.PictureObject.ImageFormat); buffer = memStream.ToArray(); } string hex = BitConverter.ToString(buffer, 0).Replace("-", string.Empty); string picture = @"{\sv{\pict\picscalex382\picscaley382\piccropl0\piccropr0\piccropt0\piccropb0"; float widthPic = (float)watermark.Image.Width / watermark.Image.HorizontalResolution; float heightPic = (float)watermark.Image.Height / watermark.Image.VerticalResolution; picture += @"\picw" + (int)(widthPic * 2540); picture += @"\pich" + (int)(heightPic * 2540); picture += @"\picwgoal" + (int)(widthPic * 1440); picture += @"\pichgoal" + (int)(heightPic * 1440); if (watermark.PictureObject.ImageFormat == System.Drawing.Imaging.ImageFormat.Jpeg) { picture += @"\jpegblip"; } else if (watermark.PictureObject.ImageFormat == System.Drawing.Imaging.ImageFormat.Png) picture += @"\pngblip"; int width; int height; switch (watermark.ImageSize) { case WatermarkImageSize.Zoom: float scaleX = matrix.PageWidth(0) / (watermark.Image.Width / 3.8f); float scaleY = matrix.PageHeight(0) / (watermark.Image.Height / 3.8f); width = (int)(watermark.Image.Width / 3.8f * Math.Min(scaleY, scaleX) * MargDivider); height = (int)(watermark.Image.Height / 3.8f * Math.Min(scaleY, scaleX) * MargDivider); break; case WatermarkImageSize.Stretch: width = (int)((matrix.PageWidth(0) - matrix.PageLMargin(0) - matrix.PageRMargin(0)) * MargDivider); height = (int)((matrix.PageHeight(0) - matrix.PageTMargin(0) - matrix.PageBMargin(0)) * MargDivider); break; default: width = (int)(watermark.Image.Width / 3.8f * MargDivider); height = (int)(watermark.Image.Height / 3.8f * MargDivider); break; } pictureContrast -= (int)(watermark.ImageTransparency * 100 * 600); pictureBrightness += (int)(watermark.ImageTransparency * 100 * 600); WriteLine(stream, @"\headery0{\header\pard\plain\itap0\s0\aspalpha\aspnum\adjustright\ltrpar\li0\lin0\ri0\rin0\ql\faauto\rtlch\afs24\ltrch\fs24{\rtlch\afs24\ltrch\fs2" + "\n" + @"{\shp{\*\shpinst\shpleft-0\shptop-0\shpright" + width + @"\shpbottom" + height + @"\shpfhdr1\shpbxcolumn\shpbxignore\shpbypara\shpbyignore\shpwr3\shpwrk0\shpfblwtxt1\shpz2\shplid2054{\sp{\sn shapeType}" + "\n" + @"{\sv 75}" + "\n" + @"}" + "\n" + @"{\sp{\sn fFlipH}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn fFlipV}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn pib}" + "\n" ); WriteLine(stream, picture + @"\bliptag-1263442625{\*\blipuid b4b1653f1c65f1c28791719870001760}"); WriteLine(stream, hex + "}"); WriteLine(stream, @"}" + "\n" + @"}" + "\n" + @"{\sp{\sn pibFlags}" + "\n" + @"\sv 2}" + "\n" + @"}" + "\n" + @"{\sp{\sn pictureContrast}" + "\n" + @"{\sv " + pictureContrast + "}" + "\n" + @"}" + "\n" + @"{\sp{\sn pictureBrightness}" + "\n" + @"{\sv " + pictureBrightness + "}" + "\n" + @"}" + "\n" + @"{\sp{\sn fLine}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn wzName}" + "\n" + @"{\sv WordPictureWatermark85221080}" + "\n" + @"}" + "\n" + @"{\sp{\sn posh}" + "\n" + @"{\sv 2}" + "\n" + @"}" + "\n" + @"{\sp{\sn posrelh}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn posv}" + "\n" + @"{\sv 2}" + "\n" + @"}" + "\n" + @"{\sp{\sn posrelv}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn dhgt}" + "\n" + @"{\sv 251660288}" + "\n" + @"}" + "\n" + @"{\sp{\sn fLayoutInCell}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"{\sp{\sn fBehindDocument}" + "\n" + @"{\sv 1}" + "\n" + @"}" + "\n" + @"{\sp{\sn fLayoutInCell}" + "\n" + @"{\sv 0}" + "\n" + @"}" + "\n" + @"}" + "\n" + @"{\shprslt" + "\n" + @"}" + "\n" + @"}" + "\n" + @"{\rtlch\afs24\ltrch\fs24\par}" + "\n" + @"}" ); } } #endregion private void ExportRTF(Stream stream) { int i, j, x, fx, fy, dx, dy, pbk; int dcol, drow, xoffs; ExportIEMObject Obj; Prepare(); //Write a header is below now pbk = 0; Write(stream, SetPageProp(pbk)); if (Report.PreparedPages.GetPage(0).Watermark.Enabled) AddWatermark(stream); if (ShowProgress) Config.ReportSettings.OnProgress(Report, res.Get("SavePage") + " " + (pbk + 1).ToString()); for (int y = 0; y < matrix.Height - 1; y++) { if (pageBreaks) if (pbk < matrix.PagesCount) if (matrix.PageBreak(pbk) <= matrix.YPosById(y)) { // WriteLine(stream, "\\pagebb\\sect"); WriteLine(stream, "\\pard\\sect"); pbk++; if (pbk < matrix.PagesCount) Write(stream, SetPageProp(pbk)); if (ShowProgress) Config.ReportSettings.OnProgress(Report, res.Get("SavePage") + " " + (pbk + 1).ToString()); } if (pbk == matrix.PagesCount - 1) yDiv = Ydivider3; else if (pbk > 0) yDiv = Ydivider1; else yDiv = Ydivider2; drow = (int)Math.Round((matrix.YPosById(y + 1) - matrix.YPosById(y)) * 14.90); StringBuilder buff = new StringBuilder(512); buff.Append(autoSize ? "\\trrh" : "\\trrh-" + (drow).ToString() + "\\trgaph15"); if (exportLocale) { if (localization != null) buff.Append($"\\lang{localization.LCID}"); else buff.Append($"\\lang{CultureInfo.GetCultureInfoByIetfLanguageTag(Res.LocaleName.Substring(0, 2)).LCID}"); } xoffs = (int)Math.Round(matrix.XPosById(0)); StringBuilder CellsStream = new StringBuilder(); for (x = 0; x <= matrix.Width - 2; x++) { i = matrix.Cell(x, y); if (i != -1) { Obj = matrix.ObjectById(i); matrix.ObjectPos(i, out fx, out fy, out dx, out dy); if (Obj.Counter == -1) { if (dy > 1) buff.Append("\\clvmgf"); if (Obj.Style != null) { if (Obj.Style.FillColor != Color.Transparent) buff.Append("\\clcbpat").Append(GetRTFColorFromTable(GetRTFColor(Obj.Style.FillColor))); buff.Append(GetRTFVAlignment(Obj.Style.VAlign)).Append(GetRTFBorders(Obj.Style)) .Append("\\cltxlrtb"); if (Obj.Style.Angle == 90) buff.Append(" \\cltxtbrl "); else if (Obj.Style.Angle == 270) buff.Append(" \\cltxbtlr "); } dcol = (int)Math.Round((Obj.Left + Obj.Width - xoffs) * Xdivider); buff.Append("\\cellx").Append(dcol.ToString()); if (Obj.IsText) { if (Obj.IsRichText) { // Write a rich text StringBuilder EmneddedRichStream = new StringBuilder(); TranslateEmbeddedRTF(Obj, EmneddedRichStream); string debug = EmneddedRichStream.ToString(); CellsStream.Append(EmneddedRichStream); } else { // Write a text textColor = Obj.Style.TextColor; CellsStream.AppendLine(GetRTFString(Obj, drow)); } } else { // Write a picture CellsStream.Append(GetRTFMetafile(Obj)); } Obj.Counter = y + 1; } else { if ((dy > 1) && (Obj.Counter != y + 1)) { buff.Append("\\clvmrg"). Append((Obj.Style != null) ? GetRTFBorders(Obj.Style) : String.Empty). Append("\\cltxlrtb"); dcol = (int)Math.Round((Obj.Left + Obj.Width - xoffs) * Xdivider); buff.Append("\\cellx").Append(dcol.ToString()); j = (int)(drow / FONT_DIVIDER); j = j > 20 ? 20 : j; CellsStream.Append("{\\fs").Append(j.ToString()); CellsStream.AppendLine("\\cell}"); Obj.Counter = y + 1; } } } } if (CellsStream.Length > 0) { WriteLine(stream, "\\trowd"); WriteLine(stream, buff.ToString()); WriteLine(stream, "\\pard\\intbl"); WriteLine(stream, CellsStream.ToString()); WriteLine(stream, "\\pard\\intbl{\\trowd"); WriteLine(stream, buff.ToString()); WriteLine(stream, "\\row}"); } } WriteLine(stream, "\\pard\\fs2\\par}"); //insert empty text with minimum size for avoiding creating a new page WriteLine(stream, "}"); #region Write a header byte[] b = new byte[stream.Length]; stream.Position = 0; stream.Read(b, 0, (int)stream.Length); stream.SetLength(0); WriteLine(stream, GetRTFHeader()); WriteLine(stream, GetRTFMetaInfo()); stream.Write(b, 0, b.Length); #endregion stream.Flush(); } #endregion #region Protected Methods /// protected override string GetFileFilter() { return new MyRes("FileFilters").Get("RtfFile"); } /// protected override void Start() { base.Start(); colorTable = new List(); fontTable = new List(); matrix = new ExportMatrix(); if (wysiwyg) matrix.Inaccuracy = 0.5f; else matrix.Inaccuracy = 10; matrix.RotatedAsImage = false; matrix.PlainRich = false; matrix.AreaFill = true; matrix.CropAreaFill = false; matrix.ShowProgress = ShowProgress; matrix.Report = Report; if (printOptimized) matrix.PrintOptimized = printOptimized; if (imageFormat != RTFImageFormat.Metafile) { matrix.FullTrust = false; matrix.ImageFormat = imageFormat == RTFImageFormat.Jpeg ? System.Drawing.Imaging.ImageFormat.Jpeg : System.Drawing.Imaging.ImageFormat.Png; matrix.JpegQuality = jpegQuality; } matrix.KeepRichText = keepRichText; } /// protected override void ExportPageBegin(ReportPage page) { base.ExportPageBegin(page); matrix.AddPageBegin(page); } /// protected override void ExportBand(BandBase band) { base.ExportBand(band); matrix.AddBand(band, this); } /// protected override void ExportPageEnd(ReportPage page) { matrix.AddPageEnd(page); } /// protected override void Finish() { matrix.Prepare(); ExportRTF(Stream); } #endregion /// public override void Serialize(FRWriter writer) { base.Serialize(writer); writer.WriteBool("Wysiwyg", Wysiwyg); writer.WriteBool("PageBreaks", PageBreaks); writer.WriteBool("Pictures", Pictures); writer.WriteValue("ImageFormat", ImageFormat); writer.WriteValue("KeeoRich", EmbedRichObject); writer.WriteValue("Locale", Locale); writer.WriteBool("ExportLocale", ExportLocale); } /// /// Initializes a new instance of the class. /// public RTFExport() { dpiFactor = 96f / DrawUtils.ScreenDpi; pageBreaks = true; wysiwyg = true; printOptimized = false; autoSize = false; pictures = true; imageFormat = RTFImageFormat.Metafile; jpegQuality = 90; ExportLocale = false; creator = "FastReport"; res = new MyRes("Export,Misc"); } } }