Click here to Skip to main content
Click here to Skip to main content

RTF Document Constructor Library

By , 16 Aug 2010
 

Introduction/Background

Some time ago, I needed to construct Rich Text Format reports, and I decided to write my own library from scratch as I couldn't find any suitable solution on the web.

The main idea was to create a library that could be easily expanded, so that developers could add any feature from the RTF Specification that is not implemented by default. Look at the second part of this article for details.

Using the Code

The RtfDocument class has a constructor that takes a RtfCodepage enum value as parameter. I tested the library with Windows-1251 Cyrillic encoding, and I'm quite sure it works just as well with others. Unicode is supported too.

RtfDocument rtf = new RtfDocument();

We go on with adding fonts and colors to the specified tables. Later, we will use indexes to refer to them (that's certainly the simplest but not the best implementation). RtfDocument.DefaultFont defines the index of the font used for paragraphs with no FontIndex set explicitly.

rtf.FontTable.Add(new RtfFont("Calibri"));
rtf.FontTable.Add(new RtfFont("Constantia"));
rtf.ColorTable.AddRange(new RtfColor[] {
    new RtfColor(Color.Red),
    new RtfColor(0, 0, 255)
});

The contents of the document are paragraphs, formatted paragraphs, table rows, and tables. Let's create a header paragraph with centered 16pt text.

RtfFormattedParagraph header = 
  new RtfFormattedParagraph(new RtfParagraphFormatting(16, RtfTextAlign.Center));

Add some text, formatted text, and an empty paragraph to the header:

header.AppendText("Calibri");
header.AppendText(new RtfFormattedText(" Bold", RtfCharacterFormatting.Bold));
header.AppendParagraph();

Add another paragraph with a different formatting. We set FontIndex to 1 and IndentLeft to 6.05cm. Most of the indents and widths are set in twips. TwipConverter converts millimeters, centimeters, and points to twips, and vice versa.

RtfFormattedParagraph p = 
  new RtfFormattedParagraph(new RtfParagraphFormatting(12, RtfTextAlign.Left));
p.Formatting.FontIndex = 1;
p.Formatting.IndentLeft = TwipConverter.ToTwip(6.05F, MetricUnit.Centimeter);
p.AppendText("Constantia");
p.AppendText(new RtfFormattedText("Superscript", RtfCharacterFormatting.Superscript));

Here is an example demonstrating inline font size change. The font index -1 to be ignored.

p.AppendParagraph(new RtfFormattedText("Inline", -1, 8));
p.AppendText(new RtfFormattedText(" font size ", -1, 14));
p.AppendText(new RtfFormattedText("change", -1, 8));

Pictures are supported in different output formats. JPEG and PNG cannot be read by WordPad, so it's better to use WMF for compatibility. The conversion to WMF is done with P/Invoke calls, and the credits for this part go to David Bennett.

RtfImage picture = new RtfImage(Properties.Resources.lemon, RtfImageFormat.Wmf);
picture.ScaleX = 50;
picture.ScaleY = 50;

p.AppendParagraph(picture);

A hyperlink with common formatting:

RtfFormattedText linkText = 
  new RtfFormattedText("View article", RtfCharacterFormatting.Underline, 2);
p.AppendParagraph(new RtfHyperlink("RtfConstructor.aspx", linkText));

A centered table with 2 columns and 3 rows:

RtfTable t1 = new RtfTable(RtfTableAlign.Center, 2, 3);

The cells can be merged both horizontally and vertically:

t1.MergeCellsVertically(1, 0, 2);

Formatting to use within cells:

RtfParagraphFormatting LeftAligned12 = new RtfParagraphFormatting(12, RtfTextAlign.Left);
RtfParagraphFormatting Centered10 = new RtfParagraphFormatting(10, RtfTextAlign.Center);

The table cell class derives from formatted paragraph, and has some additional properties.

t1[0, 0].Definition.Style = 
  new RtfTableCellStyle(RtfBorderSetting.None, LeftAligned12, 
                        RtfTableCellVerticalAlign.Bottom);
t1[0, 0].AppendText("Bottom");

t1[1, 0].Definition.Style = 
  new RtfTableCellStyle(RtfBorderSetting.Left, Centered10, 
      RtfTableCellVerticalAlign.Center, 
      RtfTableCellTextFlow.BottomToTopLeftToRight);
t1[1, 1].Definition.Style = t1[1, 0].Definition.Style;
t1[1, 0].AppendText("Vertical");

We set TextColorIndex of the cell to 1, and add RtfFormattedText with different colors.

t1[0, 1].Formatting = new RtfParagraphFormatting(10, RtfTextAlign.Center);
t1[0, 1].Formatting.TextColorIndex = 1;
t1[0, 1].AppendText(new RtfFormattedText("Black", 0));
t1[0, 1].AppendText(" Red ");
t1[0, 1].AppendText(new RtfFormattedText("Blue", 2));

This part shows an example of bitwise operations on RtfCharacterFormatting:

t1[0, 2].AppendText("Normal");
t1[1, 2].AppendText(new RtfFormattedText("Italic", 
         RtfCharacterFormatting.Caps | RtfCharacterFormatting.Italic));
t1[1, 2].AppendParagraph("+");
t1[1, 2].AppendParagraph(new RtfFormattedText("Caps", 
         RtfCharacterFormatting.Caps | RtfCharacterFormatting.Italic));

Adding the contents to the document:

rtf.Contents.AddRange(new IRtfDocumentPart[] {
    header,
    t1,
    p,
});

When the document is complete, we use RtfWriter to convert it to RTF code:

RtfWriter rtfWriter = new RtfWriter();
TextWriter writer = new StreamWriter("test.rtf");
rtfWriter.Write(writer, rtf);

And voila, the resulting file as seen in Microsoft Word:

Screenshot

And that's the RTF code:

{\rtf1\ansi\ansicpg1252\deffont0\deflang1033
{\fonttbl {\f0\fnil\fcharset1\fprq0 Calibri;}{\f1\fnil\fcharset1\fprq0 Constantia;}}
{\colortbl ;\red255\green0\blue0;\red0\green0\blue255;}
\pard\plain\qc\fi0\li0\ri0\sl0\sb0\sa240\fs32 Calibri 
    {\b Bold}\par\trowd\trrh1134\trqc\clvertalb\cltxlrtb\cellx1701\clvmgf
\clvertalc\clbrdrl\brdrw10\brdrs\cltxbtlr\cellx2835\pard\
    intbl\plain\ql\fi0\li0\ri0\sl0\sb0\sa0\fs24 Bottom\cell\pard\intbl
\plain\qc\fi0\li0\ri0\sl0\sb0\sa0\fs20 Vertical\cell\row\
    trowd\trrh1134\trqc\clvertalc\cltxlrtb\cellx1701\clvmrg\clvertalc
\clbrdrl\brdrw10\brdrs\cltxbtlr\cellx2835\pard\intbl\plain\
    qc\fi0\li0\ri0\sl0\sb0\sa0\cf1\fs20{\cf0 Black } Red {\cf2 Blue}\cell
\pard\intbl\plain\qc\fi0\li0\ri0\sl0\sb0\sa0\fs20\cell\row\
    trowd\trrh1134\trqc\clvertalc\cltxlrtb\cellx1701\clvertalc\cltxlrtb
\cellx2835\pard\intbl\plain\qc\fi0\li0\ri0\sl0\sb0\sa0\fs20 Normal\
   cell\pard\intbl\plain\qc\fi0\li0\ri0\sl0\sb0\sa0\fs20{\i\caps I
talic}\par +\par{\i\caps Caps}\cell\row\pard\plain\ql\fi0\li3430\
   ri0\sl0\sb120\sa0\f1\fs24 Constantia {\super Superscript}\par{
\fs16 Inline}{\fs28  font size }{\fs16 change}\par{\pict\picscalex50\
   picscaley50\picw7938\pich11509\picwgoal4500\pichgoal6525
\wmetafile8 

...

}
\par{\field{\fldinst HYPERLINK "http://www.codeproject.com/KB/
       cs/RtfConstructor.aspx"}{\fldrslt{\cf2\cb1\ul View article}}}}

The code above has been wrapped to prevent scrolling.

Reflection Part

The conversion of RtfDocument to RTF code is done using Reflection.

Each class representing a control word has a specific attribute which is recognized by the RtfWriter. And that's what makes it easy to expand the library and add your own classes to support more control words. It is somehow similar to what you do when you add System.Xml.Serialization attributes to classes and members for serialization purposes. Except that you will have to study the RTF specification.

It comes as no surprise that the most used attribute is RtfControlWord. If its RtfControlWord.Name property is not set, RtfWriter uses the member name for the control word.

[RtfControlWord]
public int Red { get; set; }

Values of int members are appended to control words. RtfWriter ignores members marked with the RtfIndex attribute if their value is -1.

[RtfControlWord("cf"), RtfIndex]
public int TextColorIndex { get; set; }

The enums are special case, and they need the RtfEnumAsControlWord attribute as there are different ways they can be treated.

[RtfEnumAsControlWord(RtfEnumConversion.UseAttribute)]
public enum RtfTableAlign
{
    [RtfControlWord("trql")]
    Left,
    [RtfControlWord("trqc")]
    Center,
    [RtfControlWord("trqr")]
    Right
}

[RtfEnumAsControlWord(RtfEnumConversion.UseValue, Prefix = "fprq")]
public enum RtfFontPitch
{
    Default,     // as /fprq0
    Fixed,       // as /fprq1
    Variable     // as /fprq2
}

[RtfEnumAsControlWord(RtfEnumConversion.UseName)]
public enum RtfDocumentCharacterSet
{
    ANSI,        // as /ansi
    Mac,         // as /mac
    PC,          // as /pc
    PCa          // as /pca
}

Some members not marked with RtfControlWord must be included, and RtfInclude tells the RtfWriter to do that.

public class RtfTable
{
    [RtfInclude]
    public RtfTableRowCollection Rows
    {
        get { return _rows; }
    }
    //...
}

Some members should be included only if some conditions are met, and that's where the RtfInclude.ConditionMember property comes in handy.

[RtfControlWord("clbrdrt"), RtfInclude(ConditionMember = "IsTopBorderSet")]
public RtfBorder Top
{
    get { return top; }
}

public bool IsTopBorderSet
{
    get { return Top.Width > 0; }
}

A bool member marked with RtfControlWord is included only if its value is true.

[RtfControlWord("b")]
public bool Bold = false;

Some control words are paired, like /trowd to start a table row, and /row to end it. The RtfControlWordDenotingEnd attribute is used to define the second one.

[RtfControlWord("pard"), RtfControlWordDenotingEnd("cell")]
public class RtfTableCell {
    //...
}

Some members must be ignored, and RtfIgnore is used for that.

public class RtfTableCell
{
    [RtfIgnore]
    public RtfTable Table
    {
        get { return RowInternal.Table; }
    }
    
    //...
}

The RtfEnclosingBraces attribute and its RtfEnclosingBraces.ClosingSemicolon property speak for themselves.

[RtfControlWord("rtf1"), RtfEnclosingBraces]
public class RtfDocument {
    //...
}

[RtfControlWord("f", IsIndexed = true), 
 RtfEnclosingBraces(ClosingSemicolon = true)]
public class RtfFont {
    //...
}

Two attributes left unmentioned are RtfTextData to mark text, and RtfControlGroup which was added for some reason I don't remember. Only font and color tables are marked with it.

Points of Interest

Writing the RtfDocument with a lot of content takes a considerable time, but only once as the information about the types' attributes and members is stored in specific classes. Please take a look at the RtfDocumentInfo, RtfTypeInfo, and RtfAttributeInfo classes.

History

  • 16.08.10: Code revised, added support for tabs (not shown in the article).
  • 07.08.10: Added support for images, hyperlinks, inline color, and font formatting.
  • 05.08.10: Reflection Part added.
  • 02.08.10: Text revised, RTF code added.
  • 30.07.10: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dima Popov
Software Developer Energoservice
Russian Federation Russian Federation
Member
Dmitry lives in Arkhangelsk. He has developed C# applications for testing and configuring so-called IEDs since 2007.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDetecting When a New Page Has StartedmemberWes Copeland23 Apr '13 - 4:37 
In one of the RTF files I'm creating, I need to insert some text at the beginning of each new page. However, this wouldn't qualify as a "header" element, because there are slight differences in this text on each page, whereas I believe a header would be all the same.
 
Is there any way to do this with the library in its current form? Thanks again.
AnswerRe: Detecting When a New Page Has StartedmemberDima Popov23 Apr '13 - 6:16 
Anyway /header control word is not supported. It might be not that hard to implement, but it depends on the differences you mentioned. Could you give more details?
GeneralRe: Detecting When a New Page Has StartedmemberWes Copeland23 Apr '13 - 6:23 
I just had the thought that I can achieve what I'm trying to do using multiple tables with repeating table headers. I believe the RTF control word for this is \trhdr. I'll see if I can figure out how to get this implemented.
QuestionInsert Page BreakmemberWes Copeland16 Apr '13 - 6:55 
Hi. I'm really enjoying this library and it has been a great asset to me so far in the project I'm working on. One thing I need to do, however, is insert page breaks into the documents I'm generating.
 
I believe the RTF command sequence to do this is \sbkpage or \page, but given my limited knowledge of reflection I'm unsure of how to actually implement this control word into the library (also due to the fact that I don't believe any parameters are needed).
 
Any tips? Thanks for your help, and thanks again for this API.
AnswerRe: Insert Page BreakmemberDima Popov17 Apr '13 - 0:58 
Thanks for your feedback, it's always nice to know your code is helpful.
 
The /page control word can be inserted with the help of RtfPageBreak class (to be found in RtfSpecialCharacter.cs):
[RtfControlWord("page")]
public class RtfPageBreak : RtfDocumentContentBase { }

GeneralRe: Insert Page Break [modified]memberWes Copeland17 Apr '13 - 5:07 
Thanks for your response.
 
I've found the code you're mentioning, but I'm sure of how to actually use it in my project. I can create an RtfPageBreak object like this:
RtfPageBreak newpage = new RtfPageBreak();
But that doesn't actually do anything. Any further tips?
 
Also, as a follow-up question, I see that \line is a command word in the file you mentioned. How do I use this with a paragraph?
 
Edit: Nevermind, I figured it out. To those who are also wondering this, you do it with something like this:
TableIntroductionCont.AppendText(contentline);
RtfLineBreak linebreak = new RtfLineBreak();
TableIntroductionCont.Contents.Add(linebreak);


modified 17 Apr '13 - 13:58.

GeneralRe: Insert Page BreakmemberDima Popov18 Apr '13 - 1:51 
It's great that you've made it, as I didn't have time to provide a decent sample code and check it.
 
Not being able to use normal new line symbols is pain, I know, but I think it shouldn't be RtfWriter's responsibility to convert \n symbol to \line control word. I would suggest you writing a little class that searches and replaces \n symbols in a document with RtfLineBreak instances.
 
And please feel free to ask any questions.
GeneralRe: Insert Page BreakmemberWes Copeland18 Apr '13 - 2:12 
Thanks again for your reply. I wrote a small subroutine to do just that.
 
For those reading wondering how to do it, it's just a small addition to what I wrote above:
String[] splitter = Regex.Split(tableIntroductionContText, "\n");
foreach (string contentline in splitter)
{
    TableIntroductionCont.AppendText(contentline);
    RtfLineBreak linebreak = new RtfLineBreak();
    TableIntroductionCont.Contents.Add(linebreak);
}
Where tableIntroductionContText contains something like "This is\na test".
GeneralRe: Insert Page BreakmemberDima Popov18 Apr '13 - 2:39 
I would not use Regex here.
String[] splitter = tableIntroductionContText.Split('\n');

QuestionHow to specify length of cell while creating table.memberAbhinandan More28 Feb '13 - 3:13 
If I am creating table with 5 coloumn i want to set the length for each cell. how to set?
Please advice.
Thank you.
AnswerRe: How to specify length of cell while creating table.memberDima Popov28 Feb '13 - 4:25 
Could you please provide an image of what you're trying to achieve?
QuestionAdd Table with backround colormemberOliver Lange11 Jan '13 - 2:01 
Hej!
 
First of all - this is a very helpfull Library - congratualtions!
 

During playing around i came up with the idea to insert a table which have
a colored background. I can find the property backgroundcolorindex but it is
not clear for me, how to reference specify such an index.
 
Is it already possible to add a background color to a tabel cell - and if so, can
you please give me an hint how i should establish a color index table which can be
referenced with backgroundColorIndex ?
 
Thanks for your patience in advance
 
Oli
AnswerRe: Add Table with backround colormemberDima Popov11 Jan '13 - 2:57 
Hey!
I checked it, and yeah, it's quite confusing that TableCell.Formatting has BackgroundColorIndex property. In rtf it translates to cb, and this is not what you want - it's paragraph formatting, not that of a cell.
 
I've created a file in WordPad to see what control word stands for cell background:
{\rtf1
{\colortbl ;\red200\green200\blue200;}
\trowd
\clcbpat1\cellx1691 Yeah\cell\row\pard
}
All you have to do now is to add this property to TableCellStyle class:
[RtfControlWord("clcbpat"), RtfIndex]
public int BackgroundColorIndex { get; set; }
Please, note that index 0 is reserved for default color, and so index 1 corresponds to the first color in the color table.
 
Cheers,
Dima
GeneralRe: Add Table with backround colormemberOliver Lange13 Jan '13 - 19:51 
Hey Dima!
 
Thank's a lot for your fast reply!Thumbs Up | :thumbsup:
 
Perhaps you can give me a little bit more assistance? Big Grin | :-D
 
I've got the problem, that any reference onto the color table within my tabel doesn't seems
to work - even not the TextColorIndex. Here is, what I've done yet:
 
First: Iadded the new property within the RtfTableCellStyle.cs - Line 51ff ->
/// <summary>
    /// Represents table cell style.
    /// </summary>
    public class RtfTableCellStyle
    {
        private RtfTableCellVerticalAlign _verticalAlign = RtfTableCellVerticalAlign.Center;
        private RtfTableCellBorders _borders = new RtfTableCellBorders();
        private RtfTableCellTextFlow _textFlow = RtfTableCellTextFlow.LeftToRightTopToBottom;
        private RtfParagraphFormatting _defaultParagraphFormatting;
 
        /// <summary>
        /// Add BackgroundColor based on Paragraph Formatting Style
        /// </summary>
        [RtfControlWord("clcbpat"), RtfIndex]
        public int BackgroundColorIndex { get; set; }
 

 
Then i build a small applicatino doing something like this:
 
RtfDocument rtfdoc = new RtfDocument();
rtfdoc.FontTable.Add(new RtfFont("Arial"));
rtfdoc.ColorTable.AddRange(new RtfColor[] {
new RtfColor(Color.Red),
new RtfColor(0, 0, 255)
});

RtfTable t1 = new RtfTable(RtfTableAlign.Center, 1, 1);
 
t1[0, 0].Formatting = new RtfParagraphFormatting(10, RtfTextAlign.Center);
t1[0, 0].Formatting.TextColorIndex = 1;
t1[0, 0].Formatting.BackgroundColorIndex = 1;
t1[0, 0].AppendText(new RtfFormattedText("Black", 0));
t1[0, 0].AppendText(" Red ");
t1[0, 0].AppendText(new RtfFormattedText("Blue", 2));
rtfdoc.Contents.Add(t1);
 

Within the result RTF - the Word "Red" is shown in Black and not in red - which is
the color indexed color within the table. And also the background is not shown coloredConfused | :confused:
 
here is my result rtf:
{\rtf1\ansi\ansicpg1252\deffont0\deflang1033
{\fonttbl {\f0\fnil\fcharset1\fprq0 Arial;}}
{\colortbl ;\red255\green0\blue0;\red0\green0\blue255;}
\trowd\trrh0\trqc\clcbpat0\clvertalc\clbrdrt\brdrw10\brdrs\clbrdrl\brdrw10\brdrs\clbrdrb\brdrw10\brdrs\clbrdrr\brdrw10\brdrs
\cltxlrtb\cellx9797\pard\intbl\plain{\cf0 Black} Red {\cf2 Blue}\cell\row}
 
Any idea?!
Hope for a hint
 
cherio
Oliver
GeneralRe: Add Table with backround colormemberDima Popov13 Jan '13 - 21:14 
Hey Oliver,
 
Formatting of a cell is inhereted property of RtfFormattedParagraph parent class, and its BackgroundColorIndex doesn't affect the look of the table. Apparently, TextColorIndex doesn't work either.
 
So you don't set the table cell style background color index, and its value is zero:
{\rtf1\ansi\ansicpg1252\deffont0\deflang1033
{\fonttbl {\f0\fnil\fcharset1\fprq0 Arial;}}
{\colortbl ;\red255\green0\blue0;\red0\green0\blue255;}
\trowd\trrh0\trqc\clcbpat0\clvertalc\clbrdrt\brdrw10\brdrs\clbrdrl\brdrw10\brdrs\clbrdrb\brdrw10\brdrs\clbrdrr\brdrw10\brdrs
\cltxlrtb\cellx9797\pard\intbl\plain{\cf0 Black} Red {\cf2 Blue}\cell\row}
It can be changed this way:
t[0, 0].Definition.Style.BackgroundColorIndex = 1;
Sorry, it's confusing, I know.
GeneralRe: Add Table with backround colormemberOliver Lange13 Jan '13 - 21:43 
Sigh | :sigh:
oh oh - perhaps i made a mistake while adding the new property within the class?
 
My code:
RtfDocument rtfdoc = new RtfDocument();
rtfdoc.FontTable.Add(new RtfFont("Times New Roman"));
rtfdoc.ColorTable.Add(new RtfColor(Color.Red));
rtfdoc.ColorTable.Add(new RtfColor(0, 0, 255));
 
RtfTable t1 = new RtfTable(RtfTableAlign.Left, 1, 1);
t1[0, 0].Definition.Style.BackgroundColorIndex = 1;            
t1[0, 0].AppendText(" Red background ");
rtfdoc.Contents.Add(t1);
 
even don't result in a colored table cell Confused | :confused:
 
...any idea.... Blush | :O
GeneralRe: Add Table with backround color [modified]memberDima Popov13 Jan '13 - 22:08 
You made no mistake in adding the property, you just didn't use it Smile | :) You've made the exact modification I meant.
 
RtfLibrary\Rtf\Contents\Table\RtfTableCellStyle.cs:
public class RtfTableCellStyle
{
    private RtfTableCellVerticalAlign _verticalAlign = RtfTableCellVerticalAlign.Center;
    private RtfTableCellBorders _borders = new RtfTableCellBorders();
    private RtfTableCellTextFlow _textFlow = RtfTableCellTextFlow.LeftToRightTopToBottom;
    private RtfParagraphFormatting _defaultParagraphFormatting;
 
    [RtfControlWord("clcbpat"), RtfIndex]
    public int BackgroundColorIndex { get; set; }
 
...
 
But now I'm stuck, because I've copied your code, compiled, opened the generated file in WordPad, and it works as can be seen on screenshot.
 
Check if your rtf looks the same and has the control word needed for background color:
{\rtf1\ansi\ansicpg1252\deffont0\deflang1033
{\fonttbl {\f0\fnil\fcharset1\fprq0 Times New Roman;}}
{\colortbl ;\red255\green0\blue0;\red0\green0\blue255;}
\trowd\trrh0\trql\clcbpat1\clvertalc\clbrdrt\brdrw10\brdrs\clbrdrl\brdrw10\brdrs\clbrdrb\brdrw10\brdrs\clbrdrr\brdrw10\brdrs
\cltxlrtb\cellx9797\pard\intbl\plain  Red background \cell\row}
 
Does your editor where you view the generated file support cell background color?

modified 14 Jan '13 - 4:27.

GeneralRe: Add Table with backround colormemberOliver Lange14 Jan '13 - 0:21 
Sniff | :^) uuuppps
 
Sorry - i test it within a richt text box within ny c# project - and here in
the colors are not shown correctly!
 
When i opened the rtf File i.e. in word or word pad the colors are occuring correctly!
 
Thanks a lot for your patience - it works FINE!
 
best wishes - and may the source be with you Blush | :O
GeneralRe: Add Table with backround colormemberDima Popov14 Jan '13 - 0:56 
Yeah, RichTextBox doesn't support a lot of RTF Specification. I remember that there was some alternative control (free and maybe also open source) with better functionality, but I can't find it by googling. Maybe you'll have more luck in it.
 
Thanks for your feedback!
QuestionOpening the generated File via dialog box "Open, Save, Cancel"memberMember 95538035 Dec '12 - 23:08 
Hi
I want the generated rtf file to be opened after a dialog box.
        Response.ClearHeaders();
        Response.ContentType = "application/rtf";
        Response.AddHeader("content-disposition", "attachment;" + "filename=" + "testfileName.rtf");
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.Write(rtf.ToString());//what should go here
        //rtf.ToString();
        Response.End();

AnswerRe: Opening the generated File via dialog box "Open, Save, Cancel"memberDima Popov6 Dec '12 - 19:49 
I don't do much web-programming and never did it in .NET, so I can't be a good advisor. But try this:
Response.ContentType = "application/octet-stream";

GeneralRe: Opening the generated File via dialog box "Open, Save, Cancel"memberMember 95538036 Dec '12 - 22:02 
Hi
i wanted to know waht will go
Response.Write(rtf.ToString());//what should go here
in the code i posted earlier.
here rtf is a variable of( RtfDocument rtf = new RtfDocument(); )
 
Thanks in advance.
GeneralRe: Opening the generated File via dialog box "Open, Save, Cancel"memberDima Popov7 Dec '12 - 0:33 
That's quite a different question.
 
So, you want to get a string representation of RtfDocument. I'll give you a hint. You'll need to write rtf to MemoryStream and then read string from it.
 
Or you could modify my code. It would be simple as RtfWriter doesn't really use a stream the way it should. It creates StringBuilder, appends text to it and then writes the StringBuilder string to the stream. (The code is 3 years old, so I have no idea why I did it this way). You could modify RtftWriter so that it would return that string instead of writing it to the stream. And then you could use RtfWriter in RtfDocument.ToString() method so that you'll be able to use it the way you expected Response.Write(rtf.ToString()).
 
Then you could rename the RtfWriter class to something more appropriate, like RtfStringBuilder, and its Write method to Build or GetString.
QuestionHow to add RtfDocument to RichTextBoxmemberrwbta26 Oct '12 - 4:26 
After creating an RtfDocument, I would like to assign it to a winforms RichTextBox. How would I do that?
AnswerRe: How to add RtfDocument to RichTextBoxmemberDima Popov29 Oct '12 - 22:53 
First, you get a string generated by RtfWriter using MemoryStream, than you set RichTextBox.Rtf property.
QuestionRichTextBoxesmemberMember 950558611 Oct '12 - 7:03 
Can this be used with a RichTextBox to show content as an application adds to it progammattically?
AnswerRe: RichTextBoxesmemberDima Popov11 Oct '12 - 9:34 
Yes, it's possible. But it's not designed for real-time editing, and I'm afraid it would be too slow.
And I must warn you that System.Windows.Forms.RichTextBox (which comes with .NET) supports a much smaller subset of the RTF Specification than this library
QuestionSlow for large documentsmemberlallallalla7 Oct '12 - 23:09 
Hi
 
i have di produce large documents (lots of tables, 100 pages and more) and it's very slow.
I think it's a reflection problem.
Have you any suggest to speed up?
AnswerRe: Slow for large documentsmemberDima Popov8 Oct '12 - 3:50 
Well, I haven't tried creating documents that large, so the code isn't optimized for that. I can't say if it's possible to improve performance of RtfWriter to suit your needs.
 
The simplest thing to do is to modify your code, and create smaller documents Smile | :)
 
If that's not possible you could use profiler to analyze the performance, and then optimize the code.
SuggestionPaper size and landscape additionsmemberJWhattam2 Aug '12 - 11:24 
Hi Dima,
It's me again! Whilst using your library, I found that the paper size always defaulted to the US standard and the orientation to portrait. To get a specific paper size and change the orientation for Word I made the following changes in the RTFDocument class:
 
public class RtfDocument
      {
     ...
     private int _MarginTop = (int)TwipConverter.TwipsInInch;
     private int _MarginRight = (int)TwipConverter.TwipsInInch;
     private int _MarginLeft = (int)TwipConverter.TwipsInInch;
     private int _MarginBottom = (int)TwipConverter.TwipsInInch;
     private int _PageWidth = TwipConverter.ToTwip(8.3F, MetricUnit.Inch);
     private int _PageHeight = TwipConverter.ToTwip(11.7F, MetricUnit.Inch);
     private bool _IsLandscape = false;
 

     ... The following should be inserted after the ColorTable property
 

     /// <summary> Gets or sets whether the page layout is landscape (true) or not. </summary>
            public bool IsLandscape
            {
                  get { return _IsLandscape; }
                  set { _IsLandscape = value; }
            }
 
            /// <summary> Includes the \landscape control word when IsLandscape is true. </summary>
            [RtfControlWord("landscape"), RtfInclude(ConditionMember = "IsLandscape")]
            public string Landscape
            {
                  get { return ""; }
            }
 
            /// <summary> Gets or sets the page width for the document. </summary>
            /// <value> The page width, in twips; default is 8.3 inches in twips. </value>
            /// <remarks> Use page size's height when landscape mode selected. </remarks>
            [RtfControlWord("paperw")]
            public int PageWidth
            {
                  get { return _PageWidth; }
                  set { _PageWidth = value; }
            }
 
            /// <summary> Gets or sets the page height for the document. </summary>
            /// <value> The page width, in twips; default is 11.7 inches in twips. </value>
            /// <remarks> Use page size's width when landscape mode selected. </remarks>
            [RtfControlWord("paperh")]
            public int PageHeight
            {
                  get { return _PageHeight; }
                  set { _PageHeight = value; }
            }
 
            /// <summary> Gets or sets the top margin for the document. </summary>
            /// <value> The margin size, in twips; default is 1440 (one inch). </value>
            [RtfControlWord("margt")]
            public int MarginTop
            {
                  get { return _MarginTop; }
                  set { _MarginTop = value; }
            }
 
            /// <summary> Gets or sets the bottom margin for the document. </summary>
            /// <value> The margin size, in twips; default is 1440 (one inch). </value>
            [RtfControlWord("margb")]
            public int MarginBottom
            {
                  get { return _MarginBottom; }
                  set { _MarginBottom = value; }
            }
 
            /// <summary> Gets or sets the left margin for the document. </summary>
            /// <value> The margin size, in twips; default is 1440 (one inch). </value>
            [RtfControlWord("margl")]
            public int MarginLeft
            {
                  get { return _MarginLeft; }
                  set { _MarginLeft = value; }
            }
 
            /// <summary> Gets or sets the right margin for the document. </summary>
            /// <value> The margin size, in twips; default is 1440 (one inch). </value>
            [RtfControlWord("margr")]
            public int MarginRight
            {
                  get { return _MarginRight; }
                  set { _MarginRight = value; }
            }
 
Note that it includes the changes suugested by PSU Steve. Also note that the changes should be inserted after the color table property. When this is done, the following is inserted into the RTF document after the color table definition, assuming you have set IsLandscape to true:
...
{\colortbl ;\red0\green0\blue0;}
\landscape\paperw16834\paperh11909\margt1411\margb1411\margl1411\margr1411
 
I don't know if there is a more elegant way to insert the \landscape code, but the above seems to work. Note that the defaults selected for the paper size are based on the A4 dimensions since this is the default for Australia.
 
Hope others find this useful.
 
Regards,
John. Smile | :)
GeneralRe: Paper size and landscape additionsmemberDima Popov16 Aug '12 - 20:25 
Thanks for sharing your code! Smile | :)
 
JWhattam wrote:
if there is a more elegant way
I'd suggest removing unnecessary attribute RtfInclude(ConditionMember = "IsLandscape") and the property IsLandscape. Then make the Landscape property boolean itself (you could also rename it as IsLandscape, so that you would not need to change your code).
 
[RtfControlWord("landscape")]
public bool Landscape { get; set; }
 
It will give you the same result, as boolean properties are included in RTF if their value is true.
GeneralRe: Paper size and landscape additionsmemberJWhattam19 Aug '12 - 14:14 
Thanks Dima,
I didn't realize that for boolean properties they are only included if their value is true.
 
Regards and thanks again for sharing your library.
 
John.Cool | :cool:
QuestionImages in table cellsmemberJWhattam4 Apr '12 - 19:41 
Hi Dima,
I was wondering if it is possible to add images to table cells - I would like to create a two column table with simple icons in left column and associated text in the right column, the latter text may need to wrap across several lines. Is this possible?
 
Regards,
John W.
AnswerRe: Images in table cellsmemberDima Popov4 Apr '12 - 20:10 
Hi, John,
I think that's possible, you could try something like this:
 
RtfDocument rtf = new RtfDocument();
RtfTable t = new RtfTable(RtfTableAlign.Center, 2, 1);
RtfImage picture = new RtfImage(Properties.Resources.MyCat, RtfImageFormat.Wmf);
 
t[0, 0].AppendText(picture);
t[1, 0].AppendParagraph("My cat");
t[1, 0].AppendParagraph("Scratchy");
 
rtf.Contents.AddRange(new RtfDocumentContentBase[] { 
    t,
});

GeneralRe: Images in table cellsmemberJWhattam9 Apr '12 - 16:53 
Thanks Dima, I'll give it a go.
QuestionRe: Images in table cellsmemberJWhattam6 Jun '12 - 19:46 
Hi Dima,
Tried your suggestion and it works well - I'm appending the label using AppendText rather than AppendParagraph because when I use the latter, it inserts a line between current cell contents and the appended label paragraph. However, when I use AppendText I lose the ability to add space before and after to pad the separate legend lines. Is there any way I can use AppendParagraph without getting the extra line inserted for each cell's text?
 
Regards & Thanks for all your hard work. Big Grin | :-D
AnswerRe: Images in table cellsmemberDima Popov6 Jun '12 - 21:36 
Hi John,
 
I'm not quite sure that I have understood what you're trying to do, so please provide some code next time Smile | :) Here's an example of using AppendText with line breaks and simple space indentation.
RtfDocument rtf = new RtfDocument();
 
RtfTable table = new RtfTable(2, 1);
 
table[1, 0].AppendText("  Archaeopteryx");
table[1, 0].AppendText(new RtfLineBreak());
table[1, 0].AppendText("    A. lithographica");
 
rtf.Contents.Add(table);

QuestionRe: Images in table cellsmemberJWhattam7 Jun '12 - 12:16 
Sorry about the confusion,
I've looked into the library further and it appears you can only set the formatting for all cells in a table. If I use the following, I can set the left indent but it applies to all table cells:
Dim pTable as new RTFTable(RTFTableAlign.Left, 2, 3)
 
Dim pFormatting as new RTFParagraphFormatting(12)
pFormatting.IndentLeft = TwipConverter.ToTwip(0.5, MetricUnit.Centimeter)
 
pTable.DefaultCellStyle = new RTFTableCellStyle(RTFBorderSetting.None, pFormatting, RTFTableCellVerticalAlignment.Center, RTFTableCellTextFlow.LeftToRightTopToBottom)
 
Is there any way to set different cell styles for each of the table cells, eg have all cells in column 0 right aligned for example and have all cells in column 1 left aligned. Likewise control each cell's indent, space before, space after, etc.
 
Regards,
John. :(
BugRe: Images in table cellsmemberDima Popov7 Jun '12 - 21:21 
Now I got your point. The library allows you to have different styles and formatting in cells, and there's an example of it in the article.
 
RtfDocument rtf = new RtfDocument();
 
RtfTable table = new RtfTable(2, 2);
 
RtfParagraphFormatting indent1 = new RtfParagraphFormatting() { 
    IndentLeft = TwipConverter.ToTwip(1F, MetricUnit.Centimeter),
};
 
RtfParagraphFormatting indent2 = new RtfParagraphFormatting() {
    IndentLeft = TwipConverter.ToTwip(2F, MetricUnit.Centimeter),
};
 
table[1, 0].Definition.Style = new RtfTableCellStyle() {
    DefaultParagraphFormatting = indent1,
};
 
table[1, 1].Definition.Style = new RtfTableCellStyle() {
    DefaultParagraphFormatting = indent2,
};
 

table[1, 0].AppendText("Animalia");
table[1, 1].AppendText("Aves");
 
rtf.Contents.Add(table);
As I wrote this piece of code to demonstrate it, I found a bug in RtfTableCellDefinition class, which prevented RtfWriter to actually add formatting to the output document.
At line 145 in RtfTableCellDefinition.cs add the following:
_cell.IsFormattingIncluded = hasStyle && _cell.Formatting != null;
The Style property will look like that then:
[RtfInclude]
public RtfTableCellStyle Style
{
    get { return _style; }
    set
    {
        _style = value;
 
        if (hasStyle = _style != null)
        {
            _cell.Formatting = _style.DefaultParagraphFormatting;
        }
 
        _cell.IsFormattingIncluded = hasStyle && _cell.Formatting != null;
    }
}
It tells RtfWriter to include formatting when cell has a different style.
GeneralRe: Images in table cellsmemberJWhattam11 Jun '12 - 13:18 
Hi Dima,
Sorry about not seeing that in the example code - I was spending more time looking in the library and couldn't get the individual cell styles working, so I assumed it couldn't be done. Thanks again for all your help. My reports look exellenet thanks to your library.
 
Regards,
John.Thumbs Up | :thumbsup: Thumbs Up | :thumbsup: Thumbs Up | :thumbsup: Thumbs Up | :thumbsup: Thumbs Up | :thumbsup:
GeneralRe: Images in table cellsmemberDima Popov11 Jun '12 - 13:45 
Thanks for your feedback! Thumbs Up | :thumbsup:
GeneralMy vote of 5memberJWhattam20 Feb '12 - 12:39 
If I could give higher, I would!
GeneralRe: My vote of 5memberDima Popov15 Mar '12 - 22:18 
Thanks! Smile | :)
Questiongenerates large filesmemberFrankinstienCode14 Sep '11 - 11:27 
When appending a paragraph each and every one is blocked with the rtf codes, if text is of the same font, color and size why generate more rtf tags? Where would you set a look behind feature so that if nothing has changed then no new tags are inserted.
 
Frank
AnswerRe: generates large filesmemberDima Popov14 Sep '11 - 20:24 
The RtfParagraph class serves for this purpose. In rtf, it is represented by /par control word, and unlike /pard it doesn't affect formatting. The class has InheritedFormatting property marked with RtfIgnore attribute. I haven't checked it, but that's how it's supposed to work.
QuestionAppending RTFmemberAndreas Freeman1 Jul '11 - 16:58 
How do you append existing RTF text to this document class.
AnswerRe: Appending RTFmemberDima Popov1 Jul '11 - 20:35 
That is not supported. I suppose the problem is that you cannot append it as text, because the control characters will be escaped. You could create an attribute, apply it to a new class, and modify RtfWriter to write the contents of that class without escaping the control characters.
GeneralSetting marginsmemberPSU Steve7 Jan '11 - 4:41 
As I previously commented with my vote, I think this is awesome. That said, I'm using MS Word to work with the resulting RTF and needed to change the default page margins from 1-inch. While I realize that setting page margins doesn't seem to be part of the official RTF specification, some Google searching yielded "margl", "margr", "margt", and "margb" attributes that seem to set the margin sizes in twips. So, I added the following to the RtfDocument.cs file and it seems to be working just fine.
 
        /// <summary>Gets or sets the top margin for the document</summary>
        /// <value>The margin size, in twips; default is 1440 (one inch)</value>
        [RtfControlWord("margt")]
        public float MarginTop { 
            get { return _MarginTop; }
            set { _MarginTop = value; }
        }
        private float _MarginTop = TwipConverter.TwipsInInch;
  
        /// <summary>Gets or sets the bottom margin for the document</summary>
        /// <value>The margin size, in twips; default is 1440 (one inch)</value>
        [RtfControlWord("margb")]
        public float MarginBottom
        {
            get { return _MarginBottom; }
            set { _MarginBottom = value; }
        }
        private float _MarginBottom = TwipConverter.TwipsInInch;
  
        /// <summary>Gets or sets the left margin for the document</summary>
        /// <value>The margin size, in twips; default is 1440 (one inch)</value>
        [RtfControlWord("margl")]
        public float MarginLeft
        {
            get { return _MarginLeft; }
            set { _MarginLeft = value; }
        }
        private float _MarginLeft = TwipConverter.TwipsInInch;
  
        /// <summary>Gets or sets the right margin for the document</summary>
        /// <value>The margin size, in twips; default is 1440 (one inch)</value>
        [RtfControlWord("margr")]
        public float MarginRight
        {
            get { return _MarginRight; }
            set { _MarginRight = value; }
        }
        private float _MarginRight = TwipConverter.TwipsInInch;
 
BTW, there might be a better implementation of this -- I am NOT a c-sharp guy and I just delved into this code yesterday. But this seems to be a quick-and-dirty make to make margins work.
GeneralRe: Setting marginsmemberDima Popov10 Jan '11 - 3:31 
Thanks for voting and sharing your code! I suppose those properties must be int though.
GeneralMy vote of 5memberPSU Steve7 Jan '11 - 3:37 
This is way cool. Perfect for my reporting requirements. Many, many thanks!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 16 Aug 2010
Article Copyright 2010 by Dima Popov
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid