1 using System;
2 using System.Collections.Specialized;
3 using System.Drawing;
4 using System.Drawing.Imaging;
5 using System.IO;
6 using System.Runtime.InteropServices;
7 using System.Text;
8 using System.Windows.Forms;
9 namespace RichTextBoxEx.Controls
10 {
11 #region Public Enums
12 //可能的RTF颜色枚举
13 public enum RtfColor
14 {
15 Black, Maroon, Green, Olive, Navy, Purple, Teal, Gray, Silver,
16 Red, Lime, Yellow, Blue, Fuchsia, Aqua, White
17 }
18 #endregion
19 /// <summary>
20 /// This class adds the following functionality to RichTextBox:
21 ///
22 /// 1. Allows plain text to be inserted or appended programmatically to RTF
23 /// content.
24 /// 2. Allows the font, text color, and highlight color of plain text to be
25 /// specified when inserting or appending text as RTF.
26 /// 3. Allows images to be inserted programmatically, or with interaction from
27 /// the user.
28 /// </summary>
29 /// <remarks>
30 /// Many solutions to the problem of programmatically inserting images
31 /// into a RichTextBox use the clipboard or hard code the RTF for
32 /// the image in the program. This class is an attempt to make the process of
33 /// inserting images at runtime more flexible without the overhead of maintaining
34 /// the clipboard or the use of huge, cumbersome strings.
35 ///
36 /// RTF Specification v1.6 was used and is referred to many times in this document.
37 /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnrtfspec/html/rtfspec.asp
38 ///
39 /// For information about the RichEdit (Unmanaged RichTextBox) ...
40 /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp
41 /// </remarks>
42 public class ExRichTextBox : System.Windows.Forms.RichTextBox
43 {
44 #region My Enums
45 // Specifies the flags/options for the unmanaged call to the GDI+ method
46 // Metafile.EmfToWmfBits().
47 private enum EmfToWmfBitsFlags
48 {
49 // Use the default conversion
50 EmfToWmfBitsFlagsDefault = 0x00000000,
51 // Embedded the source of the EMF metafiel within the resulting WMF
52 // metafile
53 EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
54 // Place a 22-byte header in the resulting WMF file. The header is
55 // required for the metafile to be considered placeable.
56 EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
57 // Don't simulate clipping by using the XOR operator.
58 EmfToWmfBitsFlagsNoXORClip = 0x00000004
59 };
60 #endregion
61 #region My Structs
62 // Definitions for colors in an RTF document
63 private struct RtfColorDef
64 {
65 public const string Black = @"\red0\green0\blue0";
66 public const string Maroon = @"\red128\green0\blue0";
67 public const string Green = @"\red0\green128\blue0";
68 public const string Olive = @"\red128\green128\blue0";
69 public const string Navy = @"\red0\green0\blue128";
70 public const string Purple = @"\red128\green0\blue128";
71 public const string Teal = @"\red0\green128\blue128";
72 public const string Gray = @"\red128\green128\blue128";
73 public const string Silver = @"\red192\green192\blue192";
74 public const string Red = @"\red255\green0\blue0";
75 public const string Lime = @"\red0\green255\blue0";
76 public const string Yellow = @"\red255\green255\blue0";
77 public const string Blue = @"\red0\green0\blue255";
78 public const string Fuchsia = @"\red255\green0\blue255";
79 public const string Aqua = @"\red0\green255\blue255";
80 public const string White = @"\red255\green255\blue255";
81 }
82 // Control words for RTF font families
83 private struct RtfFontFamilyDef
84 {
85 public const string Unknown = @"\fnil";
86 public const string Roman = @"\froman";
87 public const string Swiss = @"\fswiss";
88 public const string Modern = @"\fmodern";
89 public const string Script = @"\fscript";
90 public const string Decor = @"\fdecor";
91 public const string Technical = @"\ftech";
92 public const string BiDirect = @"\fbidi";
93 }
94 #endregion
95 #region My Constants
96 // Not used in this application. Descriptions can be found with documentation
97 // of Windows GDI function SetMapMode
98 private const int MM_TEXT = 1;
99 private const int MM_LOMETRIC = 2;
100 private const int MM_HIMETRIC = 3;
101 private const int MM_LOENGLISH = 4;
102 private const int MM_HIENGLISH = 5;
103 private const int MM_TWIPS = 6;
104 // Ensures that the metafile maintains a 1:1 aspect ratio
105 private const int MM_ISOTROPIC = 7;
106 // Allows the x-coordinates and y-coordinates of the metafile to be adjusted
107 // independently
108 private const int MM_ANISOTROPIC = 8;
109 // Represents an unknown font family
110 private const string FF_UNKNOWN = "UNKNOWN";
111 // The number of hundredths of millimeters (0.01 mm) in an inch
112 // For more information, see GetImagePrefix() method.
113 private const int HMM_PER_INCH = 2540;
114 // The number of twips in an inch
115 // For more information, see GetImagePrefix() method.
116 private const int TWIPS_PER_INCH = 1440;
117 #endregion
118 #region My Privates
119 // The default text color
120 private RtfColor textColor;
121 // The default text background color
122 private RtfColor highlightColor;
123 // Dictionary that maps color enums to RTF color codes
124 private HybridDictionary rtfColor;
125 // Dictionary that mapas Framework font families to RTF font families
126 private HybridDictionary rtfFontFamily;
127 // The horizontal resolution at which the control is being displayed
128 private float xDpi;
129 // The vertical resolution at which the control is being displayed
130 private float yDpi;
131 #endregion
132 #region Elements required to create an RTF document
133 /* RTF HEADER
134 * ----------
135 *
136 * \rtf[N] - For text to be considered to be RTF, it must be enclosed in this tag.
137 * rtf1 is used because the RichTextBox conforms to RTF Specification
138 * version 1.
139 * \ansi - The character set.
140 * \ansicpg[N] - Specifies that unicode characters might be embedded. ansicpg1252
141 * is the default used by Windows.
142 * \deff[N] - The default font. \deff0 means the default font is the first font
143 * found.
144 * \deflang[N] - The default language. \deflang1033 specifies US English.
145 * */
146 private const string RTF_HEADER = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033";
147 /* RTF DOCUMENT AREA
148 * -----------------
149 *
150 * \viewkind[N] - The type of view or zoom level. \viewkind4 specifies normal view.
151 * \uc[N] - The number of bytes corresponding to a Unicode character.
152 * \pard - Resets to default paragraph properties
153 * \cf[N] - Foreground color. \cf1 refers to the color at index 1 in
154 * the color table
155 * \f[N] - Font number. \f0 refers to the font at index 0 in the font
156 * table.
157 * \fs[N] - Font size in half-points.
158 * */
159 private const string RTF_DOCUMENT_PRE = @"\viewkind4\uc1\pard\cf1\f0\fs20";
160 private const string RTF_DOCUMENT_POST = @"\cf0\fs17}";
161 private string RTF_IMAGE_POST = @"}";
162 #endregion
163 #region Accessors
164 // TODO: This can be ommitted along with RemoveBadCharacters
165 // Overrides the default implementation of RTF. This is done because the control
166 // was originally developed to run in an instant messenger that uses the
167 // Jabber XML-based protocol. The framework would throw an exception when the
168 // XML contained the null character, so I filtered out.
169 public new string Rtf
170 {
171 get { return RemoveBadChars(base.Rtf); }
172 set { base.Rtf = value; }
173 }
174 // The color of the text
175 public RtfColor TextColor
176 {
177 get { return textColor; }
178 set { textColor = value; }
179 }
180 // The color of the highlight
181 public RtfColor HiglightColor
182 {
183 get { return highlightColor; }
184 set { highlightColor = value; }
185 }
186 #endregion
187 #region Constructors
188 /// <summary>
189 /// Initializes the text colors, creates dictionaries for RTF colors and
190 /// font families, and stores the horizontal and vertical resolution of
191 /// the RichTextBox's graphics context.
192 /// </summary>
193 public ExRichTextBox()
194 : base()
195 {
196 // Initialize default text and background colors
197 textColor = RtfColor.Black;
198 highlightColor = RtfColor.White;
199 // Initialize the dictionary mapping color codes to definitions
200 rtfColor = new HybridDictionary();
201 rtfColor.Add(RtfColor.Aqua, RtfColorDef.Aqua);
202 rtfColor.Add(RtfColor.Black, RtfColorDef.Black);
203 rtfColor.Add(RtfColor.Blue, RtfColorDef.Blue);
204 rtfColor.Add(RtfColor.Fuchsia, RtfColorDef.Fuchsia);
205 rtfColor.Add(RtfColor.Gray, RtfColorDef.Gray);
206 rtfColor.Add(RtfColor.Green, RtfColorDef.Green);
207 rtfColor.Add(RtfColor.Lime, RtfColorDef.Lime);
208 rtfColor.Add(RtfColor.Maroon, RtfColorDef.Maroon);
209 rtfColor.Add(RtfColor.Navy, RtfColorDef.Navy);
210 rtfColor.Add(RtfColor.Olive, RtfColorDef.Olive);
211 rtfColor.Add(RtfColor.Purple, RtfColorDef.Purple);
212 rtfColor.Add(RtfColor.Red, RtfColorDef.Red);
213 rtfColor.Add(RtfColor.Silver, RtfColorDef.Silver);
214 rtfColor.Add(RtfColor.Teal, RtfColorDef.Teal);
215 rtfColor.Add(RtfColor.White, RtfColorDef.White);
216 rtfColor.Add(RtfColor.Yellow, RtfColorDef.Yellow);
217 // Initialize the dictionary mapping default Framework font families to
218 // RTF font families
219 rtfFontFamily = new HybridDictionary();
220 rtfFontFamily.Add(FontFamily.GenericMonospace.Name, RtfFontFamilyDef.Modern);
221 rtfFontFamily.Add(FontFamily.GenericSansSerif, RtfFontFamilyDef.Swiss);
222 rtfFontFamily.Add(FontFamily.GenericSerif, RtfFontFamilyDef.Roman);
223 rtfFontFamily.Add(FF_UNKNOWN, RtfFontFamilyDef.Unknown);
224 // Get the horizontal and vertical resolutions at which the object is
225 // being displayed
226 using (Graphics _graphics = this.CreateGraphics())
227 {
228 xDpi = _graphics.DpiX;
229 yDpi = _graphics.DpiY;
230 }
231 }
232 /// <summary>
233 /// Calls the default constructor then sets the text color.
234 /// </summary>
235 /// <param name="_textColor"></param>
236 public ExRichTextBox(RtfColor _textColor)
237 : this()
238 {
239 textColor = _textColor;
240 }
241 /// <summary>
242 /// Calls the default constructor then sets te text and highlight colors.
243 /// </summary>
244 /// <param name="_textColor"></param>
245 /// <param name="_highlightColor"></param>
246 public ExRichTextBox(RtfColor _textColor, RtfColor _highlightColor)
247 : this()
248 {
249 textColor = _textColor;
250 highlightColor = _highlightColor;
251 }
252 #endregion
253 #region Append RTF or Text to RichTextBox Contents
254 /// <summary>
255 /// Assumes the string passed as a paramter is valid RTF text and attempts
256 /// to append it as RTF to the content of the control.
257 /// </summary>
258 /// <param name="_rtf"></param>
259 public void AppendRtf(string _rtf)
260 {
261 // Move caret to the end of the text
262 this.Select(this.TextLength, 0);
263 // Since SelectedRtf is null, this will append the string to the
264 // end of the existing RTF
265 this.SelectedRtf = _rtf;
266 }
267 /// <summary>
268 /// Assumes that the string passed as a parameter is valid RTF text and
269 /// attempts to insert it as RTF into the content of the control.
270 /// </summary>
271 /// <remarks>
272 /// NOTE: The text is inserted wherever the caret is at the time of the call,
273 /// and if any text is selected, that text is replaced.
274 /// </remarks>
275 /// <param name="_rtf"></param>
276 public void InsertRtf(string _rtf)
277 {
278 this.SelectedRtf = _rtf;
279 }
280 /// <summary>
281 /// Appends the text using the current font, text, and highlight colors.
282 /// </summary>
283 /// <param name="_text"></param>
284 public void AppendTextAsRtf(string _text)
285 {
286 AppendTextAsRtf(_text, this.Font);
287 }
288
289 /// <summary>
290 /// Appends the text using the given font, and current text and highlight
291 /// colors.
292 /// </summary>
293 /// <param name="_text"></param>
294 /// <param name="_font"></param>
295 public void AppendTextAsRtf(string _text, Font _font)
296 {
297 AppendTextAsRtf(_text, _font, textColor);
298 }
299 /// <summary>
300 /// Appends the text using the given font and text color, and the current
301 /// highlight color.
302 /// </summary>
303 /// <param name="_text"></param>
304 /// <param name="_font"></param>
305 /// <param name="_color"></param>
306 public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor)
307 {
308 AppendTextAsRtf(_text, _font, _textColor, highlightColor);
309 }
310 /// <summary>
311 /// Appends the text using the given font, text, and highlight colors. Simply
312 /// moves the caret to the end of the RichTextBox's text and makes a call to
313 /// insert.
314 /// </summary>
315 /// <param name="_text"></param>
316 /// <param name="_font"></param>
317 /// <param name="_textColor"></param>
318 /// <param name="_backColor"></param>
319 public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor)
320 {
321 // Move carret to the end of the text
322 this.Select(this.TextLength, 0);
323 InsertTextAsRtf(_text, _font, _textColor, _backColor);
324 }
325 #endregion
326 #region Insert Plain Text
327 /// <summary>
328 /// Inserts the text using the current font, text, and highlight colors.
329 /// </summary>
330 /// <param name="_text"></param>
331 public void InsertTextAsRtf(string _text)
332 {
333 InsertTextAsRtf(_text, this.Font);
334 }
335
336 /// <summary>
337 /// Inserts the text using the given font, and current text and highlight
338 /// colors.
339 /// </summary>
340 /// <param name="_text"></param>
341 /// <param name="_font"></param>
342 public void InsertTextAsRtf(string _text, Font _font)
343 {
344 InsertTextAsRtf(_text, _font, textColor);
345 }
346 /// <summary>
347 /// Inserts the text using the given font and text color, and the current
348 /// highlight color.
349 /// </summary>
350 /// <param name="_text"></param>
351 /// <param name="_font"></param>
352 /// <param name="_color"></param>
353 public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor)
354 {
355 InsertTextAsRtf(_text, _font, _textColor, highlightColor);
356 }
357 /// <summary>
358 /// Inserts the text using the given font, text, and highlight colors. The
359 /// text is wrapped in RTF codes so that the specified formatting is kept.
360 /// You can only assign valid RTF to the RichTextBox.Rtf property, else
361 /// an exception is thrown. The RTF string should follow this format ...
362 ///
363 /// {\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{[FONTS]}{\colortbl ;[COLORS]}}
364 /// \viewkind4\uc1\pard\cf1\f0\fs20 [DOCUMENT AREA] }
365 ///
366 /// </summary>
367 /// <remarks>
368 /// NOTE: The text is inserted wherever the caret is at the time of the call,
369 /// and if any text is selected, that text is replaced.
370 /// </remarks>
371 /// <param name="_text"></param>
372 /// <param name="_font"></param>
373 /// <param name="_color"></param>
374 /// <param name="_color"></param>
375 public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor)
376 {
377 StringBuilder _rtf = new StringBuilder();
378 // Append the RTF header
379 _rtf.Append(RTF_HEADER);
380 // Create the font table from the font passed in and append it to the
381 // RTF string
382 _rtf.Append(GetFontTable(_font));
383 // Create the color table from the colors passed in and append it to the
384 // RTF string
385 _rtf.Append(GetColorTable(_textColor, _backColor));
386 // Create the document area from the text to be added as RTF and append
387 // it to the RTF string.
388 _rtf.Append(GetDocumentArea(_text, _font));
389 this.SelectedRtf = _rtf.ToString();
390 }
391 /// <summary>
392 /// Creates the Document Area of the RTF being inserted. The document area
393 /// (in this case) consists of the text being added as RTF and all the
394 /// formatting specified in the Font object passed in. This should have the
395 /// form ...
396 ///
397 /// \viewkind4\uc1\pard\cf1\f0\fs20 [DOCUMENT AREA] }
398 ///
399 /// </summary>
400 /// <param name="_text"></param>
401 /// <param name="_font"></param>
402 /// <returns>
403 /// The document area as a string.
404 /// </returns>
405 private string GetDocumentArea(string _text, Font _font)
406 {
407 StringBuilder _doc = new StringBuilder();
408 // Append the standard RTF document area control string
409 _doc.Append(RTF_DOCUMENT_PRE);
410 // Set the highlight color (the color behind the text) to the
411 // third color in the color table. See GetColorTable for more details.
412 _doc.Append(@"\highlight2");
413 // If the font is bold, attach corresponding tag
414 if (_font.Bold)
415 _doc.Append(@"\b");
416 // If the font is italic, attach corresponding tag
417 if (_font.Italic)
418 _doc.Append(@"\i");
419 // If the font is strikeout, attach corresponding tag
420 if (_font.Strikeout)
421 _doc.Append(@"\strike");
422 // If the font is underlined, attach corresponding tag
423 if (_font.Underline)
424 _doc.Append(@"\ul");
425 // Set the font to the first font in the font table.
426 // See GetFontTable for more details.
427 _doc.Append(@"\f0");
428 // Set the size of the font. In RTF, font size is measured in
429 // half-points, so the font size is twice the value obtained from
430 // Font.SizeInPoints
431 _doc.Append(@"\fs");
432 _doc.Append((int)Math.Round((2 * _font.SizeInPoints)));
433 // Apppend a space before starting actual text (for clarity)
434 _doc.Append(@" ");
435 // Append actual text, however, replace newlines with RTF \par.
436 // Any other special text should be handled here (e.g.) tabs, etc.
437 _doc.Append(_text.Replace("\n", @"\par "));
438 // RTF isn't strict when it comes to closing control words, but what the
439 // heck ...
440 // Remove the highlight
441 _doc.Append(@"\highlight0");
442 // If font is bold, close tag
443 if (_font.Bold)
444 _doc.Append(@"\b0");
445 // If font is italic, close tag
446 if (_font.Italic)
447 _doc.Append(@"\i0");
448 // If font is strikeout, close tag
449 if (_font.Strikeout)
450 _doc.Append(@"\strike0");
451 // If font is underlined, cloes tag
452 if (_font.Underline)
453 _doc.Append(@"\ulnone");
454 // Revert back to default font and size
455 _doc.Append(@"\f0");
456 _doc.Append(@"\fs20");
457 // Close the document area control string
458 _doc.Append(RTF_DOCUMENT_POST);
459 return _doc.ToString();
460 }
461 #endregion
462 #region Insert Image
463 /// <summary>
464 /// Inserts an image into the RichTextBox. The image is wrapped in a Windows
465 /// Format Metafile, because although Microsoft discourages the use of a WMF,
466 /// the RichTextBox (and even MS Word), wraps an image in a WMF before inserting
467 /// the image into a document. The WMF is attached in HEX format (a string of
468 /// HEX numbers).
469 ///
470 /// The RTF Specification v1.6 says that you should be able to insert bitmaps,
471 /// .jpegs, .gifs, .pngs, and Enhanced Metafiles (.emf) directly into an RTF
472 /// document without the WMF wrapper. This works fine with MS Word,
473 /// however, when you don't wrap images in a WMF, WordPad and
474 /// RichTextBoxes simply ignore them. Both use the riched20.dll or msfted.dll.
475 /// </summary>
476 /// <remarks>
477 /// NOTE: The image is inserted wherever the caret is at the time of the call,
478 /// and if any text is selected, that text is replaced.
479 /// </remarks>
480 /// <param name="_image"></param>
481 public void InsertImage(Image _image)
482 {
483 StringBuilder _rtf = new StringBuilder();
484 // Append the RTF header
485 _rtf.Append(RTF_HEADER);
486 // Create the font table using the RichTextBox's current font and append
487 // it to the RTF string
488 _rtf.Append(GetFontTable(this.Font));
489 // Create the image control string and append it to the RTF string
490 _rtf.Append(GetImagePrefix(_image));
491 // Create the Windows Metafile and append its bytes in HEX format
492 _rtf.Append(GetRtfImage(_image));
493 // Close the RTF image control string
494 _rtf.Append(RTF_IMAGE_POST);
495 this.SelectedRtf = _rtf.ToString();
496 }
497 /// <summary>
498 /// Creates the RTF control string that describes the image being inserted.
499 /// This description (in this case) specifies that the image is an
500 /// MM_ANISOTROPIC metafile, meaning that both X and Y axes can be scaled
501 /// independently. The control string also gives the images current dimensions,
502 /// and its target dimensions, so if you want to control the size of the
503 /// image being inserted, this would be the place to do it. The prefix should
504 /// have the form ...
505 ///
506 /// {\pict\wmetafile8\picw[A]\pich[B]\picwgoal[C]\pichgoal[D]
507 ///
508 /// where ...
509 ///
510 /// A = current width of the metafile in hundredths of millimeters (0.01mm)
511 /// = Image Width in Inches * Number of (0.01mm) per inch
512 /// = (Image Width in Pixels / Graphics Context's Horizontal Resolution) * 2540
513 /// = (Image Width in Pixels / Graphics.DpiX) * 2540
514 ///
515 /// B = current height of the metafile in hundredths of millimeters (0.01mm)
516 /// = Image Height in Inches * Number of (0.01mm) per inch
517 /// = (Image Height in Pixels / Graphics Context's Vertical Resolution) * 2540
518 /// = (Image Height in Pixels / Graphics.DpiX) * 2540
519 ///
520 /// C = target width of the metafile in twips
521 /// = Image Width in Inches * Number of twips per inch
522 /// = (Image Width in Pixels / Graphics Context's Horizontal Resolution) * 1440
523 /// = (Image Width in Pixels / Graphics.DpiX) * 1440
524 ///
525 /// D = target height of the metafile in twips
526 /// = Image Height in Inches * Number of twips per inch
527 /// = (Image Height in Pixels / Graphics Context's Horizontal Resolution) * 1440
528 /// = (Image Height in Pixels / Graphics.DpiX) * 1440
529 ///
530 /// </summary>
531 /// <remarks>
532 /// The Graphics Context's resolution is simply the current resolution at which
533 /// windows is being displayed. Normally it's 96 dpi, but instead of assuming
534 /// I just added the code.
535 ///
536 /// According to Ken Howe at pbdr.com, "Twips are screen-independent units
537 /// used to ensure that the placement and proportion of screen elements in
538 /// your screen application are the same on all display systems."
539 ///
540 /// Units Used
541 /// ----------
542 /// 1 Twip = 1/20 Point
543 /// 1 Point = 1/72 Inch
544 /// 1 Twip = 1/1440 Inch
545 ///
546 /// 1 Inch = 2.54 cm
547 /// 1 Inch = 25.4 mm
548 /// 1 Inch = 2540 (0.01)mm
549 /// </remarks>
550 /// <param name="_image"></param>
551 /// <returns></returns>
552 private string GetImagePrefix(Image _image)
553 {
554 StringBuilder _rtf = new StringBuilder();
555 // Calculate the current width of the image in (0.01)mm
556 int picw = (int)Math.Round((_image.Width / xDpi) * HMM_PER_INCH);
557 // Calculate the current height of the image in (0.01)mm
558 int pich = (int)Math.Round((_image.Height / yDpi) * HMM_PER_INCH);
559 // Calculate the target width of the image in twips
560 int picwgoal = (int)Math.Round((_image.Width / xDpi) * TWIPS_PER_INCH);
561 // Calculate the target height of the image in twips
562 int pichgoal = (int)Math.Round((_image.Height / yDpi) * TWIPS_PER_INCH);
563 // Append values to RTF string
564 _rtf.Append(@"{\pict\wmetafile8");
565 _rtf.Append(@"\picw");
566 _rtf.Append(picw);
567 _rtf.Append(@"\pich");
568 _rtf.Append(pich);
569 _rtf.Append(@"\picwgoal");
570 _rtf.Append(picwgoal);
571 _rtf.Append(@"\pichgoal");
572 _rtf.Append(pichgoal);
573 _rtf.Append(" ");
574 return _rtf.ToString();
575 }
576 /// <summary>
577 /// Use the EmfToWmfBits function in the GDI+ specification to convert a
578 /// Enhanced Metafile to a Windows Metafile
579 /// </summary>
580 /// <param name="_hEmf">
581 /// A handle to the Enhanced Metafile to be converted
582 /// </param>
583 /// <param name="_bufferSize">
584 /// The size of the buffer used to store the Windows Metafile bits returned
585 /// </param>
586 /// <param name="_buffer">
587 /// An array of bytes used to hold the Windows Metafile bits returned
588 /// </param>
589 /// <param name="_mappingMode">
590 /// The mapping mode of the image. This control uses MM_ANISOTROPIC.
591 /// </param>
592 /// <param name="_flags">
593 /// Flags used to specify the format of the Windows Metafile returned
594 /// </param>
595 [DllImportAttribute("gdiplus.dll")]
596 private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
597 byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
598
599 /// <summary>
600 /// Wraps the image in an Enhanced Metafile by drawing the image onto the
601 /// graphics context, then converts the Enhanced Metafile to a Windows
602 /// Metafile, and finally appends the bits of the Windows Metafile in HEX
603 /// to a string and returns the string.
604 /// </summary>
605 /// <param name="_image"></param>
606 /// <returns>
607 /// A string containing the bits of a Windows Metafile in HEX
608 /// </returns>
609 private string GetRtfImage(Image _image)
610 {
611 StringBuilder _rtf = null;
612 // Used to store the enhanced metafile
613 MemoryStream _stream = null;
614 // Used to create the metafile and draw the image
615 Graphics _graphics = null;
616 // The enhanced metafile
617 Metafile _metaFile = null;
618 // Handle to the device context used to create the metafile
619 IntPtr _hdc;
620 try
621 {
622 _rtf = new StringBuilder();
623 _stream = new MemoryStream();
624 // Get a graphics context from the RichTextBox
625 using (_graphics = this.CreateGraphics())
626 {
627 // Get the device context from the graphics context
628 _hdc = _graphics.GetHdc();
629 // Create a new Enhanced Metafile from the device context
630 _metaFile = new Metafile(_stream, _hdc);
631 // Release the device context
632 _graphics.ReleaseHdc(_hdc);
633 }
634 // Get a graphics context from the Enhanced Metafile
635 using (_graphics = Graphics.FromImage(_metaFile))
636 {
637 // Draw the image on the Enhanced Metafile
638 _graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width, _image.Height));
639 }
640 // Get the handle of the Enhanced Metafile
641 IntPtr _hEmf = _metaFile.GetHenhmetafile();
642 // A call to EmfToWmfBits with a null buffer return the size of the
643 // buffer need to store the WMF bits. Use this to get the buffer
644 // size.
645 uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
646 EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
647 // Create an array to hold the bits
648 byte[] _buffer = new byte[_bufferSize];
649 // A call to EmfToWmfBits with a valid buffer copies the bits into the
650 // buffer an returns the number of bits in the WMF.
651 uint _convertedSize = GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
652 EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
653 // Append the bits to the RTF string
654 for (int i = 0; i < _buffer.Length; ++i)
655 {
656 _rtf.Append(String.Format("{0:X2}", _buffer[i]));
657 }
658 return _rtf.ToString();
659 }
660 finally
661 {
662 if (_graphics != null)
663 _graphics.Dispose();
664 if (_metaFile != null)
665 _metaFile.Dispose();
666 if (_stream != null)
667 _stream.Close();
668 }
669 }
670 #endregion
671 #region RTF Helpers
672 /// <summary>
673 /// Creates a font table from a font object. When an Insert or Append
674 /// operation is performed a font is either specified or the default font
675 /// is used. In any case, on any Insert or Append, only one font is used,
676 /// thus the font table will always contain a single font. The font table
677 /// should have the form ...
678 ///
679 /// {\fonttbl{\f0\[FAMILY]\fcharset0 [FONT_NAME];}
680 /// </summary>
681 /// <param name="_font"></param>
682 /// <returns></returns>
683 private string GetFontTable(Font _font)
684 {
685 StringBuilder _fontTable = new StringBuilder();
686 // Append table control string
687 _fontTable.Append(@"{\fonttbl{\f0");
688 _fontTable.Append(@"\");
689 // If the font's family corresponds to an RTF family, append the
690 // RTF family name, else, append the RTF for unknown font family.
691 if (rtfFontFamily.Contains(_font.FontFamily.Name))
692 _fontTable.Append(rtfFontFamily[_font.FontFamily.Name]);
693 else
694 _fontTable.Append(rtfFontFamily[FF_UNKNOWN]);
695 // \fcharset specifies the character set of a font in the font table.
696 // 0 is for ANSI.
697 _fontTable.Append(@"\fcharset0 ");
698 // Append the name of the font
699 _fontTable.Append(_font.Name);
700 // Close control string
701 _fontTable.Append(@";}}");
702 return _fontTable.ToString();
703 }
704 /// <summary>
705 /// Creates a font table from the RtfColor structure. When an Insert or Append
706 /// operation is performed, _textColor and _backColor are either specified
707 /// or the default is used. In any case, on any Insert or Append, only three
708 /// colors are used. The default color of the RichTextBox (signified by a
709 /// semicolon (;) without a definition), is always the first color (index 0) in
710 /// the color table. The second color is always the text color, and the third
711 /// is always the highlight color (color behind the text). The color table
712 /// should have the form ...
713 ///
714 /// {\colortbl ;[TEXT_COLOR];[HIGHLIGHT_COLOR];}
715 ///
716 /// </summary>
717 /// <param name="_textColor"></param>
718 /// <param name="_backColor"></param>
719 /// <returns></returns>
720 private string GetColorTable(RtfColor _textColor, RtfColor _backColor)
721 {
722 StringBuilder _colorTable = new StringBuilder();
723 // Append color table control string and default font (;)
724 _colorTable.Append(@"{\colortbl ;");
725 // Append the text color
726 _colorTable.Append(rtfColor[_textColor]);
727 _colorTable.Append(@";");
728 // Append the highlight color
729 _colorTable.Append(rtfColor[_backColor]);
730 _colorTable.Append(@";}\n");
731 return _colorTable.ToString();
732 }
733 /// <summary>
734 /// Called by overrided RichTextBox.Rtf accessor.
735 /// Removes the null character from the RTF. This is residue from developing
736 /// the control for a specific instant messaging protocol and can be ommitted.
737 /// </summary>
738 /// <param name="_originalRtf"></param>
739 /// <returns>RTF without null character</returns>
740 private string RemoveBadChars(string _originalRtf)
741 {
742 return _originalRtf.Replace("\0", "");
743 }
744 #endregion
745 }
746 }