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 }
时间: 2024-10-13 01:43:01