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