git-svn-id: https://pdfsharp.svn.codeplex.com/svn@39620 56d0cb2f-6006-4f69-a5a2-94740...
[pdfsharp:pdfsharp.git] / PDFsharp / code / PdfSharp / PdfSharp.Pdf.Internal / PdfEncoders.cs
1 #region PDFsharp - A .NET library for processing PDF\r
2 //\r
3 // Authors:\r
4 //   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)\r
5 //\r
6 // Copyright (c) 2005-2009 empira Software GmbH, Cologne (Germany)\r
7 //\r
8 // http://www.pdfsharp.com\r
9 // http://sourceforge.net/projects/pdfsharp\r
10 //\r
11 // Permission is hereby granted, free of charge, to any person obtaining a\r
12 // copy of this software and associated documentation files (the "Software"),\r
13 // to deal in the Software without restriction, including without limitation\r
14 // the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
15 // and/or sell copies of the Software, and to permit persons to whom the\r
16 // Software is furnished to do so, subject to the following conditions:\r
17 //\r
18 // The above copyright notice and this permission notice shall be included\r
19 // in all copies or substantial portions of the Software.\r
20 //\r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
24 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
27 // DEALINGS IN THE SOFTWARE.\r
28 #endregion\r
29 \r
30 using System;\r
31 using System.Diagnostics;\r
32 using System.Collections;\r
33 using System.Globalization;\r
34 using System.Text;\r
35 using PdfSharp.Drawing;\r
36 using System.IO;\r
37 using PdfSharp.Internal;\r
38 using PdfSharp.Pdf.Security;\r
39 \r
40 namespace PdfSharp.Pdf.Internal\r
41 {\r
42   /// <summary>\r
43   /// Groups a set of static encoding helper functions.\r
44   /// </summary>\r
45   internal static class PdfEncoders\r
46   {\r
47     // static char InvalidChar = '?';\r
48 \r
49     //[Obsolete]\r
50     //public static void WriteAnsi(Stream stream, string text)\r
51     //{\r
52     //  stream.Write(PdfEncoders.WinAnsiEncoding.GetBytes(text), 0, text.Length);\r
53     //}\r
54 \r
55     /// <summary>\r
56     /// Gets the raw encoding.\r
57     /// </summary>\r
58     public static Encoding RawEncoding\r
59     {\r
60       get\r
61       {\r
62         if (PdfEncoders.rawEncoding == null)\r
63           PdfEncoders.rawEncoding = new RawEncoding();\r
64         return PdfEncoders.rawEncoding;\r
65       }\r
66     }\r
67     static Encoding rawEncoding;\r
68 \r
69     /// <summary>\r
70     /// Gets the raw Unicode encoding.\r
71     /// </summary>\r
72     public static Encoding RawUnicodeEncoding\r
73     {\r
74       get\r
75       {\r
76         if (PdfEncoders.rawUnicodeEncoding == null)\r
77           PdfEncoders.rawUnicodeEncoding = new RawUnicodeEncoding();\r
78         return PdfEncoders.rawUnicodeEncoding;\r
79       }\r
80     }\r
81     static Encoding rawUnicodeEncoding;\r
82 \r
83     /// <summary>\r
84     /// Gets the Windows 1252 (ANSI) encoding.\r
85     /// </summary>\r
86     public static Encoding WinAnsiEncoding\r
87     {\r
88       get\r
89       {\r
90         if (PdfEncoders.winAnsiEncoding == null)\r
91           PdfEncoders.winAnsiEncoding =\r
92 #if !SILVERLIGHT\r
93             Encoding.GetEncoding(1252);\r
94 #else\r
95  Encoding.GetEncoding("utf-8"); // AGHACK\r
96 #endif\r
97         return PdfEncoders.winAnsiEncoding;\r
98       }\r
99     }\r
100     static Encoding winAnsiEncoding;\r
101 \r
102     /// <summary>\r
103     /// Gets the PDF DocEncoding encoding.\r
104     /// </summary>\r
105     public static Encoding DocEncoding\r
106     {\r
107       get\r
108       {\r
109         if (PdfEncoders.docEncoding == null)\r
110           PdfEncoders.docEncoding = new DocEncoding();\r
111         return PdfEncoders.docEncoding;\r
112       }\r
113     }\r
114     static Encoding docEncoding;\r
115 \r
116     /// <summary>\r
117     /// Gets the UNICODE little-endian encoding.\r
118     /// </summary>\r
119     public static Encoding UnicodeEncoding\r
120     {\r
121       get\r
122       {\r
123         if (PdfEncoders.unicodeEncoding == null)\r
124           PdfEncoders.unicodeEncoding = Encoding.Unicode;\r
125         return PdfEncoders.unicodeEncoding;\r
126       }\r
127     }\r
128     static Encoding unicodeEncoding;\r
129 \r
130     ///// <summary>\r
131     ///// Encodes a string from a byte array. Each character gets the code of the corresponding byte.\r
132     ///// </summary>\r
133     //public static string RawString(byte[] bytes, int offset, int length)\r
134     //{\r
135     //  char[] chars = new char[length];\r
136     //  for (int idx = offset, ch = 0; idx < offset +  length; idx++, ch++)\r
137     //    chars[ch] = (char)bytes[idx];\r
138     //  return new string(chars, 0, length);\r
139     //}\r
140     //\r
141     //public static string RawString(byte[] bytes)\r
142     //{\r
143     //  return RawString(bytes, 0, bytes.Length);\r
144     //}\r
145 \r
146 #if false\r
147     public static string EncodeAsLiteral(string text, bool unicode)\r
148     {\r
149       if (text == null || text == "")\r
150         return "<>";\r
151 \r
152       StringBuilder pdf = new StringBuilder("");\r
153       if (!unicode)\r
154       {\r
155         byte[] bytes = WinAnsiEncoding.GetBytes(text);\r
156         int count = bytes.Length;\r
157         pdf.Append("(");\r
158         for (int idx = 0; idx < count; idx++)\r
159         {\r
160           char ch = (char)bytes[idx];\r
161           if (ch < 32)\r
162           {\r
163             switch (ch)\r
164             {\r
165               case '\n':\r
166                 pdf.Append("\\n");\r
167                 break;\r
168 \r
169               case '\r':\r
170                 pdf.Append("\\r");\r
171                 break;\r
172 \r
173               case '\t':\r
174                 pdf.Append("\\t");\r
175                 break;\r
176 \r
177               case '\f':\r
178                 pdf.Append("\\f");\r
179                 break;\r
180 \r
181               default: \r
182                 pdf.Append(PdfEncoders.InvalidChar); // TODO\r
183                 break;\r
184             }\r
185           }\r
186           else\r
187           {\r
188             switch (ch)\r
189             {\r
190               case '(':\r
191                 pdf.Append("\\(");\r
192                 break;\r
193 \r
194               case ')':\r
195                 pdf.Append("\\)");\r
196                 break;\r
197 \r
198               case '\\':\r
199                 pdf.Append("\\\\");\r
200                 break;\r
201 \r
202               default:\r
203                 pdf.Append(ch);\r
204                 break;\r
205             }\r
206           }\r
207         }\r
208         pdf.Append(')');\r
209       }\r
210       else\r
211       {\r
212         pdf.Append("<");\r
213         byte[] bytes = UnicodeEncoding.GetBytes(text);\r
214         int count = bytes.Length;\r
215         for (int idx = 0; idx < count; idx += 2)\r
216         {\r
217           pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx + 1], bytes[idx]);\r
218           if (idx != 0 && (idx % 48) == 0)\r
219             pdf.Append("\n");\r
220         }\r
221         pdf.Append(">");\r
222       }\r
223       return pdf.ToString();\r
224     }\r
225 #endif\r
226 \r
227     //public static string EncodeAsLiteral(string text)\r
228     //{\r
229     //  return EncodeAsLiteral(text, false);\r
230     //}\r
231 \r
232     /// <summary>\r
233     /// Converts a raw string into a raw string literal, possibly encrypted.\r
234     /// </summary>\r
235     public static string ToStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler)\r
236     {\r
237       if (String.IsNullOrEmpty(text))\r
238         return "()";\r
239 \r
240       byte[] bytes;\r
241       switch (encoding)\r
242       {\r
243         case PdfStringEncoding.RawEncoding:\r
244           bytes = RawEncoding.GetBytes(text);\r
245           break;\r
246 \r
247         case PdfStringEncoding.WinAnsiEncoding:\r
248           bytes = WinAnsiEncoding.GetBytes(text);\r
249           break;\r
250 \r
251         case PdfStringEncoding.PDFDocEncoding:\r
252           bytes = DocEncoding.GetBytes(text);\r
253           break;\r
254 \r
255         case PdfStringEncoding.Unicode:\r
256           bytes = UnicodeEncoding.GetBytes(text);\r
257           break;\r
258 \r
259         default:\r
260           throw new NotImplementedException(encoding.ToString());\r
261       }\r
262       byte[] temp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, false, securityHandler);\r
263       return RawEncoding.GetString(temp, 0, temp.Length);\r
264     }\r
265 \r
266     /// <summary>\r
267     /// Converts a raw string into a raw string literal, possibly encrypted.\r
268     /// </summary>\r
269     public static string ToStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler)\r
270     {\r
271       if (bytes == null || bytes.Length == 0)\r
272         return "()";\r
273 \r
274       byte[] temp = FormatStringLiteral(bytes, unicode, true, false, securityHandler);\r
275       return RawEncoding.GetString(temp, 0, temp.Length);\r
276     }\r
277 \r
278     /// <summary>\r
279     /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.\r
280     /// </summary>\r
281     public static string ToHexStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler)\r
282     {\r
283       if (String.IsNullOrEmpty(text))\r
284         return "<>";\r
285 \r
286       byte[] bytes;\r
287       switch (encoding)\r
288       {\r
289         case PdfStringEncoding.RawEncoding:\r
290           bytes = RawEncoding.GetBytes(text);\r
291           break;\r
292 \r
293         case PdfStringEncoding.WinAnsiEncoding:\r
294           bytes = WinAnsiEncoding.GetBytes(text);\r
295           break;\r
296 \r
297         case PdfStringEncoding.PDFDocEncoding:\r
298           bytes = DocEncoding.GetBytes(text);\r
299           break;\r
300 \r
301         case PdfStringEncoding.Unicode:\r
302           bytes = UnicodeEncoding.GetBytes(text);\r
303           break;\r
304 \r
305         default:\r
306           throw new NotImplementedException(encoding.ToString());\r
307       }\r
308 \r
309       byte[] agTemp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, true, securityHandler);\r
310       return RawEncoding.GetString(agTemp, 0, agTemp.Length);\r
311     }\r
312 \r
313     /// <summary>\r
314     /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.\r
315     /// </summary>\r
316     public static string ToHexStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler)\r
317     {\r
318       if (bytes == null || bytes.Length == 0)\r
319         return "<>";\r
320 \r
321       byte[] agTemp = FormatStringLiteral(bytes, unicode, true, true, securityHandler);\r
322       return RawEncoding.GetString(agTemp, 0, agTemp.Length);\r
323     }\r
324 \r
325     /// <summary>\r
326     /// Converts the specified byte array into a byte array representing a string literal.\r
327     /// </summary>\r
328     /// <param name="bytes">The bytes of the string.</param>\r
329     /// <param name="unicode">Indicates whether one or two bytes are one character.</param>\r
330     /// <param name="prefix">Indicates whether to use Unicode prefix.</param>\r
331     /// <param name="hex">Indicates whether to create a hexadecimal string literal.</param>\r
332     /// <param name="securityHandler">Encrypts the bytes if specified.</param>\r
333     /// <returns>The PDF bytes.</returns>\r
334     public static byte[] FormatStringLiteral(byte[] bytes, bool unicode, bool prefix, bool hex, PdfStandardSecurityHandler securityHandler)\r
335     {\r
336       if (bytes == null || bytes.Length == 0)\r
337         return hex ? new byte[] { (byte)'<', (byte)'>' } : new byte[] { (byte)'(', (byte)')' };\r
338 \r
339       Debug.Assert(!unicode || bytes.Length % 2 == 0, "Odd number of bytes in Unicode string.");\r
340 \r
341       bool encrypted = false;\r
342       if (securityHandler != null)\r
343       {\r
344         bytes = (byte[])bytes.Clone();\r
345         bytes = securityHandler.EncryptBytes(bytes);\r
346         encrypted = true;\r
347       }\r
348 \r
349       int count = bytes.Length;\r
350       StringBuilder pdf = new StringBuilder();\r
351       if (!unicode)\r
352       {\r
353         if (!hex)\r
354         {\r
355           pdf.Append("(");\r
356           for (int idx = 0; idx < count; idx++)\r
357           {\r
358             char ch = (char)bytes[idx];\r
359             if (ch < 32)\r
360             {\r
361               switch (ch)\r
362               {\r
363                 case '\n':\r
364                   pdf.Append("\\n");\r
365                   break;\r
366 \r
367                 case '\r':\r
368                   pdf.Append("\\r");\r
369                   break;\r
370 \r
371                 case '\t':\r
372                   pdf.Append("\\t");\r
373                   break;\r
374 \r
375                 case '\b':\r
376                   pdf.Append("\\b");\r
377                   break;\r
378 \r
379                 // currups encrypted text                    \r
380                 //case '\f':\r
381                 //  pdf.Append("\\f");\r
382                 //  break;\r
383 \r
384                 default:\r
385                   // Don't escape chararchters less than 32 if the string is encrypted, because it is\r
386                   // unreadable anyway.\r
387                   encrypted = true;\r
388                   if (!encrypted)\r
389                   {\r
390                     pdf.Append("\\0");\r
391                     pdf.Append((char)(ch % 8 + '0'));\r
392                     pdf.Append((char)(ch / 8 + '0'));\r
393                   }\r
394                   else\r
395                     pdf.Append(ch);\r
396                   break;\r
397               }\r
398             }\r
399             else\r
400             {\r
401               switch (ch)\r
402               {\r
403                 case '(':\r
404                   pdf.Append("\\(");\r
405                   break;\r
406 \r
407                 case ')':\r
408                   pdf.Append("\\)");\r
409                   break;\r
410 \r
411                 case '\\':\r
412                   pdf.Append("\\\\");\r
413                   break;\r
414 \r
415                 default:\r
416                   pdf.Append(ch);\r
417                   break;\r
418               }\r
419             }\r
420           }\r
421           pdf.Append(')');\r
422         }\r
423         else\r
424         {\r
425           pdf.Append('<');\r
426           for (int idx = 0; idx < count; idx++)\r
427             pdf.AppendFormat("{0:X2}", bytes[idx]);\r
428           pdf.Append('>');\r
429         }\r
430       }\r
431       else\r
432       {\r
433       Hex:\r
434         if (hex)\r
435         {\r
436           if (prefix)\r
437             pdf.Append("<FEFF");\r
438           else\r
439             pdf.Append("<");\r
440           for (int idx = 0; idx < count; idx += 2)\r
441           {\r
442             pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx], bytes[idx + 1]);\r
443             if (idx != 0 && (idx % 48) == 0)\r
444               pdf.Append("\n");\r
445           }\r
446           pdf.Append(">");\r
447         }\r
448         else\r
449         {\r
450           // TODO non hex literals... not sure how to treat linefeeds, '(', '\' etc.\r
451           hex = true;\r
452           goto Hex;\r
453         }\r
454       }\r
455       return RawEncoding.GetBytes(pdf.ToString());\r
456     }\r
457 \r
458     /// <summary>\r
459     /// Converts WinAnsi to DocEncode characters. Incomplete, just maps \80 and some other characters.\r
460     /// </summary>\r
461     static byte[] docencode_______ = new byte[256]\r
462     {\r
463       // TODO: \r
464       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,\r
465       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,\r
466       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,\r
467       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,\r
468       0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,\r
469       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,\r
470       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,\r
471       0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,\r
472       0xA0, 0x7F, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,\r
473       0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x8A, 0x8C, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,\r
474       0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,\r
475       0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,\r
476       0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,\r
477       0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,\r
478       0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,\r
479       0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,\r
480     };\r
481 \r
482     //public static string DocEncode(string text, bool unicode)//, PdfStandardSecurityHandler securityHandler)\r
483     //{\r
484     //  if (text == null || text == "")\r
485     //    return "()";\r
486     //\r
487     //  int length = text.Length;\r
488     //  StringBuilder encoded = new StringBuilder(2 * length);\r
489     //  if (!unicode)\r
490     //  {\r
491     //    byte[] bytes = PdfEncoders.WinAnsiEncoding.GetBytes(text);\r
492     //    encoded.Append('(');\r
493     //    for (int idx = 0; idx < length; idx++)\r
494     //    {\r
495     //      char ch = (char)bytes[idx];\r
496     //      if (ch > 255)\r
497     //      {\r
498     //        //TODO unicode?\r
499     //        encoded.Append(PdfEncoders.InvalidChar);\r
500     //        //encoded.Append(ch);\r
501     //        continue;\r
502     //      }\r
503     //      ch = (char)docencode[(int)ch];\r
504     //      if (ch < 32)\r
505     //      {\r
506     //        switch (ch)\r
507     //        {\r
508     //          case '\n':\r
509     //            encoded.Append("\\n");\r
510     //            break;\r
511     //\r
512     //          case '\r':\r
513     //            encoded.Append("\\r");\r
514     //            break;\r
515     //\r
516     //          case '\t':\r
517     //            encoded.Append("\\t");\r
518     //            break;\r
519     //\r
520     //          case '\f':\r
521     //            encoded.Append("\\f");\r
522     //            break;\r
523     //\r
524     //          default: \r
525     //            encoded.Append(PdfEncoders.InvalidChar); // TODO\r
526     //            break;\r
527     //        }\r
528     //      }\r
529     //      else\r
530     //      {\r
531     //        switch (ch)\r
532     //        {\r
533     //          case '(':\r
534     //            encoded.Append("\\(");\r
535     //            break;\r
536     //\r
537     //          case ')':\r
538     //            encoded.Append("\\)");\r
539     //            break;\r
540     //\r
541     //          case '\\':\r
542     //            encoded.Append("\\\\");\r
543     //            break;\r
544     //\r
545     //          default:\r
546     //            encoded.Append(ch);\r
547     //            break;\r
548     //        }\r
549     //      }\r
550     //    }\r
551     //    encoded.Append(')');\r
552     //  }\r
553     //  else\r
554     //  {\r
555     //    encoded.Append("<FEFF");\r
556     //    for (int idx = 0; idx < length; idx++)\r
557     //      encoded.AppendFormat("{0:X4}", (int)text[idx]);\r
558     //    encoded.Append('>');\r
559     //  }\r
560     //  return encoded.ToString();\r
561     //}\r
562 \r
563     //public static string DocEncode(string text)\r
564     //{\r
565     //  return DocEncode(text, false);\r
566     //}\r
567 \r
568     ///// <summary>\r
569     ///// Encodes a hexadecimal doc-encoded string literal.\r
570     ///// </summary>\r
571     //public static string DocEncodeHex(string text, bool unicode)\r
572     //{\r
573     //  if (text == null || text == "")\r
574     //    return "<>";\r
575     //\r
576     //  int length = text.Length;\r
577     //  StringBuilder encoded = new StringBuilder(3 * length);\r
578     //  if (!unicode)\r
579     //  {\r
580     //    byte[] bytes = PdfEncoders.WinAnsiEncoding.GetBytes(text);\r
581     //    encoded.Append('<');\r
582     //    for (int idx = 0; idx < length; idx++)\r
583     //      encoded.AppendFormat("{0:X2}", docencode[bytes[idx]]);\r
584     //    encoded.Append('>');\r
585     //  }\r
586     //  else\r
587     //  {\r
588     //    encoded.Append("<FEFF");\r
589     //    for (int idx = 0; idx < length; idx++)\r
590     //    {\r
591     //      encoded.AppendFormat("{0:X4}", (int)text[idx]);\r
592     //    }\r
593     //    encoded.Append('>');\r
594     //  }\r
595     //  return encoded.ToString();\r
596     //}\r
597 \r
598     //public static string DocEncodeHex(string text)\r
599     //{\r
600     //  return DocEncodeHex(text, false);\r
601     //}\r
602 \r
603     /// <summary>\r
604     /// ...because I always forget CultureInfo.InvariantCulture and wonder why Acrobat\r
605     /// cannot understand my German decimal separator...\r
606     /// </summary>\r
607     public static string Format(string format, params object[] args)\r
608     {\r
609       return String.Format(CultureInfo.InvariantCulture, format, args);\r
610     }\r
611 \r
612     /// <summary>\r
613     /// Converts a float into a string with up to 3 decimal digits and a decimal point.\r
614     /// </summary>\r
615     public static string ToString(double val)\r
616     {\r
617       return val.ToString("0.###", CultureInfo.InvariantCulture);\r
618     }\r
619 \r
620     /// <summary>\r
621     /// Converts an XColor into a string with up to 3 decimal digits and a decimal point.\r
622     /// </summary>\r
623     public static string ToString(XColor color, PdfColorMode colorMode)\r
624     {\r
625       // If not defined let color decide\r
626       if (colorMode == PdfColorMode.Undefined)\r
627         colorMode = color.ColorSpace == XColorSpace.Cmyk ? PdfColorMode.Cmyk : PdfColorMode.Rgb;\r
628 \r
629       switch (colorMode)\r
630       {\r
631         case PdfColorMode.Cmyk:\r
632           return String.Format(CultureInfo.InvariantCulture, "{0:0.###} {1:0.###} {2:0.###} {3:0.###}",\r
633             color.C, color.M, color.Y, color.K);\r
634 \r
635         default:\r
636           return String.Format(CultureInfo.InvariantCulture, "{0:0.###} {1:0.###} {2:0.###}",\r
637             color.R / 255.0, color.G / 255.0, color.B / 255.0);\r
638       }\r
639     }\r
640 \r
641     /// <summary>\r
642     /// Converts an XMatrix into a string with up to 3 decimal digits and a decimal point.\r
643     /// </summary>\r
644     public static string ToString(XMatrix matrix)\r
645     {\r
646       return String.Format(CultureInfo.InvariantCulture,\r
647         "{0:0.###} {1:0.###} {2:0.###} {3:0.###} {4:0.###} {5:0.###}",\r
648         matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY);\r
649     }\r
650   }\r
651 }\r