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");
}
}
}