git-svn-id: https://pdfsharp.svn.codeplex.com/svn@39620 56d0cb2f-6006-4f69-a5a2-94740...
[pdfsharp:pdfsharp.git] / PDFsharp / code / PdfSharp / PdfSharp.Fonts.OpenType / OpenTypeDescriptor.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.Globalization;\r
33 using System.Runtime.InteropServices;\r
34 using System.Text;\r
35 #if GDI\r
36 using System.Drawing;\r
37 using System.Drawing.Drawing2D;\r
38 #endif\r
39 #if WPF\r
40 using System.Windows;\r
41 using System.Windows.Media;\r
42 #endif\r
43 using PdfSharp.Pdf.Internal;\r
44 using PdfSharp.Drawing;\r
45 \r
46 namespace PdfSharp.Fonts.OpenType\r
47 {\r
48   /// <summary>\r
49   /// The OpenType font descriptor.\r
50   /// </summary>\r
51   internal sealed class OpenTypeDescriptor : FontDescriptor\r
52   {\r
53     public OpenTypeDescriptor(XFont font, XPdfFontOptions options)\r
54     {\r
55       try\r
56       {\r
57         this.fontData = new FontData(font, options);\r
58         this.fontName = font.Name;\r
59         Initialize();\r
60       }\r
61       catch\r
62       {\r
63         throw;\r
64       }\r
65     }\r
66 \r
67     //#if WPF\r
68     //    public TrueTypeDescriptor(XFont font, XPdfFontOptions options)\r
69     //    {\r
70     //      try\r
71     //      {\r
72     //        this.fontData = new FontData(font, options);\r
73     //        this.fontName = font.Name;\r
74     //        Initialize();\r
75     //      }\r
76     //      catch (Exception ex)\r
77     //      {\r
78     //        throw ex;\r
79     //      }\r
80     //    }\r
81     //#endif\r
82 \r
83     //internal TrueTypeDescriptor(FontSelector selector)\r
84     //{\r
85     //  throw new NotImplementedException("TrueTypeDescriptor(FontSelector selector)");\r
86     //}\r
87 \r
88     internal OpenTypeDescriptor(XFont font)\r
89       : this(font, font.PdfOptions)\r
90     { }\r
91 \r
92     internal OpenTypeDescriptor(string idName, byte[] fontData)\r
93     {\r
94       try\r
95       {\r
96         this.fontData = new FontData(fontData);\r
97         // Try to get real name form name table\r
98         if (idName.Contains("XPS-Font-") && this.fontData.name != null && this.fontData.name.Name.Length != 0)\r
99         {\r
100           string tag = String.Empty;\r
101           if (idName.IndexOf('+') == 6)\r
102             tag = idName.Substring(0, 6);\r
103           idName = tag + "+" + this.fontData.name.Name;\r
104           if (this.fontData.name.Style.Length != 0)\r
105             idName += "," + this.fontData.name.Style;\r
106           idName = idName.Replace(" ", "");\r
107         }\r
108         this.fontName = idName;\r
109         Initialize();\r
110       }\r
111       catch (Exception ex)\r
112       {\r
113         throw ex;\r
114       }\r
115     }\r
116 \r
117     internal OpenTypeDescriptor(byte[] fontData)\r
118     {\r
119       try\r
120       {\r
121         this.fontData = new FontData(fontData);\r
122         // Try to get real name form name table\r
123         string name = this.fontData.name.Name;\r
124         if (this.fontData.name.Style.Length != 0)\r
125           name += "," + this.fontData.name.Style;\r
126         name = name.Replace(" ", "");\r
127         this.fontName = name;\r
128         Initialize();\r
129       }\r
130       catch\r
131       {\r
132         throw;\r
133       }\r
134     }\r
135 \r
136     internal FontData fontData;\r
137 \r
138     void Initialize()\r
139     {\r
140       //bool embeddingRestricted = this.fontData.os2.fsType == 0x0002;\r
141 \r
142       //this.fontName = image.n\r
143       this.italicAngle = this.fontData.post.italicAngle;\r
144 \r
145       this.xMin = this.fontData.head.xMin;\r
146       this.yMin = this.fontData.head.yMin;\r
147       this.xMax = this.fontData.head.xMax;\r
148       this.yMax = this.fontData.head.yMax;\r
149 \r
150       this.underlinePosition = this.fontData.post.underlinePosition;\r
151       this.underlineThickness = this.fontData.post.underlineThickness;\r
152       this.strikeoutPosition = this.fontData.os2.yStrikeoutPosition;\r
153       this.strikeoutSize = this.fontData.os2.yStrikeoutSize;\r
154 \r
155       // No documetation found how to get the set vertical stems width from the\r
156       // TrueType tables.\r
157       // The following formula comes from PDFlib Lite source code. Acrobat 5.0 sets\r
158       // /StemV to 0 always. I think the value doesn't matter.\r
159       //float weight = (float)(this.image.os2.usWeightClass / 65.0f);\r
160       //this.stemV = (int)(50 + weight * weight);  // MAGIC\r
161       this.stemV = 0;\r
162 \r
163       // PDFlib states that some Apple fonts miss the OS/2 table.\r
164       Debug.Assert(fontData.os2 != null, "TrueType font has no OS/2 table.");\r
165 \r
166       this.unitsPerEm = fontData.head.unitsPerEm;\r
167 \r
168       // PDFlib takes sTypoAscender and sTypoDescender from OS/2 tabel, but GDI+ uses usWinAscent and usWinDescent\r
169       if (fontData.os2.sTypoAscender != 0)\r
170         this.ascender = fontData.os2.usWinAscent;\r
171       else\r
172         this.ascender = fontData.hhea.ascender;\r
173       Debug.Assert(this.ascender > 0, "PDFsharp internal: Ascender should be greater than 0.");\r
174 \r
175       if (fontData.os2.sTypoDescender != 0)\r
176       {\r
177         this.descender = fontData.os2.usWinDescent;\r
178         Debug.Assert(this.descender > 0, "PDFsharp internal: Font with non positive ascender value found.");\r
179 #if true_\r
180         Debug.WriteLine(String.Format(CultureInfo.InvariantCulture,\r
181           "os2.usWinDescent={0}, hhea.descender={1}, os2.sTypoDescender={2}", fontData.os2.usWinDescent, fontData.hhea.descender, fontData.os2.sTypoDescender));\r
182 #endif\r
183         // Force sign from hhea.descender\r
184         // TODO:\r
185         this.descender = Math.Abs(this.descender) * Math.Sign(fontData.hhea.descender);\r
186       }\r
187       else\r
188         this.descender = fontData.hhea.descender;\r
189       Debug.Assert(this.descender < 0, "PDFsharp internal: Ascender should be less than 0.");\r
190 \r
191       this.leading = fontData.hhea.lineGap;\r
192 \r
193       // sCapHeight and sxHeight are only valid if version >= 2\r
194       if (fontData.os2.version >= 2 && fontData.os2.sCapHeight != 0)\r
195         this.capHeight = fontData.os2.sCapHeight;\r
196       else\r
197         this.capHeight = fontData.hhea.ascender;\r
198 \r
199       if (fontData.os2.version >= 2 && fontData.os2.sxHeight != 0)\r
200         this.xHeight = fontData.os2.sxHeight;\r
201       else\r
202         this.xHeight = (int)(0.66f * this.ascender);\r
203 \r
204       //this.flags = this.image.\r
205 \r
206       Encoding ansi = PdfEncoders.WinAnsiEncoding; // System.Text.Encoding.Default;\r
207       Encoding unicode = Encoding.Unicode;\r
208       byte[] bytes = new byte[256];\r
209 \r
210       bool symbol = this.fontData.cmap.symbol;\r
211       this.widths = new int[256];\r
212       for (int idx = 0; idx < 256; idx++)\r
213       {\r
214         bytes[idx] = (byte)idx;\r
215         // PDFlib handles some font flaws here...\r
216         // We wait for bug reports.\r
217 \r
218         char ch = (char)idx;\r
219         string s = ansi.GetString(bytes, idx, 1);\r
220         if (s.Length != 0)\r
221         {\r
222           if (s[0] != ch)\r
223             ch = s[0];\r
224         }\r
225 #if DEBUG\r
226         if (idx == 'S')\r
227           GetType();\r
228 #endif\r
229         int glyphIndex;\r
230         if (symbol)\r
231         {\r
232           glyphIndex = idx + (this.fontData.os2.usFirstCharIndex & 0xFF00);\r
233           glyphIndex = CharCodeToGlyphIndex((char)glyphIndex);\r
234         }\r
235         else\r
236         {\r
237           //Debug.Assert(idx + (this.fontData.os2.usFirstCharIndex & 0xFF00) == idx);\r
238           //glyphIndex = CharCodeToGlyphIndex((char)idx);\r
239           glyphIndex = CharCodeToGlyphIndex(ch);\r
240         }\r
241         this.widths[idx] = GlyphIndexToPdfWidth(glyphIndex);\r
242       }\r
243     }\r
244     public int[] widths;\r
245 \r
246     /// <summary>\r
247     /// Gets a value indicating whether this instance belongs to a bold font.\r
248     /// </summary>\r
249     public override bool IsBoldFace\r
250     {\r
251       get\r
252       {\r
253         // usWeightClass 700 is Bold\r
254         //Debug.Assert((this.fontData.os2.usWeightClass >= 700) == ((this.fontData.os2.fsSelection & (ushort)OS2Table.FontSelectionFlags.Bold) != 0));\r
255         return (this.fontData.os2.fsSelection & (ushort)OS2Table.FontSelectionFlags.Bold) != 0;\r
256       }\r
257     }\r
258 \r
259     /// <summary>\r
260     /// Gets a value indicating whether this instance belongs to an italic font.\r
261     /// </summary>\r
262     public override bool IsItalicFace\r
263     {\r
264       get { return (this.fontData.os2.fsSelection & (ushort)OS2Table.FontSelectionFlags.Italic) != 0; }\r
265     }\r
266 \r
267     internal int DesignUnitsToPdf(double value)\r
268     {\r
269       return (int)Math.Round(value * 1000.0 / this.fontData.head.unitsPerEm);\r
270     }\r
271 \r
272     /// <summary>\r
273     /// Maps a unicode to the index of the corresponding glyph.\r
274     /// See OpenType spec "cmap - Character To Glyph Index Mapping Table / Format 4: Segment mapping to delta values"\r
275     /// for details about this a little bit strange looking algorithm.\r
276     /// </summary>\r
277     public int CharCodeToGlyphIndex(char value)\r
278     {\r
279       try\r
280       {\r
281         CMap4 cmap = this.fontData.cmap.cmap4;\r
282         int segCount = cmap.segCountX2 / 2;\r
283         int seg;\r
284         for (seg = 0; seg < segCount; seg++)\r
285         {\r
286           if (value <= cmap.endCount[seg])\r
287             break;\r
288         }\r
289         Debug.Assert(seg < segCount);\r
290 \r
291         if (value < cmap.startCount[seg])\r
292           return 0;\r
293 \r
294         if (cmap.idRangeOffs[seg] == 0)\r
295           return (value + cmap.idDelta[seg]) & 0xFFFF;\r
296 \r
297         int idx = cmap.idRangeOffs[seg] / 2 + (value - cmap.startCount[seg]) - (segCount - seg);\r
298         Debug.Assert(idx >= 0 && idx < cmap.glyphCount);\r
299 \r
300         if (cmap.glyphIdArray[idx] == 0)\r
301           return 0;\r
302         \r
303         return (cmap.glyphIdArray[idx] + cmap.idDelta[seg]) & 0xFFFF;\r
304       }\r
305       catch\r
306       {\r
307         throw;\r
308       }\r
309     }\r
310 \r
311     /// <summary>\r
312     /// Converts the width of a glyph identified by its index to PDF design units.\r
313     /// </summary>\r
314     public int GlyphIndexToPdfWidth(int glyphIndex)\r
315     {\r
316       try\r
317       {\r
318         int numberOfHMetrics = this.fontData.hhea.numberOfHMetrics;\r
319         int unitsPerEm = this.fontData.head.unitsPerEm;\r
320 \r
321         // glyphIndex >= numberOfHMetrics means the font is mono-spaced and all glyphs have the same width\r
322         if (glyphIndex >= numberOfHMetrics)\r
323           glyphIndex = numberOfHMetrics - 1;\r
324 \r
325         int width = this.fontData.hmtx.metrics[glyphIndex].advanceWidth;\r
326 \r
327         // Sometimes the unitsPerEm is 1000, sometimes a power of 2.\r
328         if (unitsPerEm == 1000)\r
329           return width;\r
330         return width * 1000 / unitsPerEm; // normalize\r
331       }\r
332       catch (Exception ex)\r
333       {\r
334         throw ex;\r
335       }\r
336     }\r
337 \r
338     public int PdfWidthFromCharCode(char ch)\r
339     {\r
340       int idx = CharCodeToGlyphIndex(ch);\r
341       int width = GlyphIndexToPdfWidth(idx);\r
342       return width;\r
343     }\r
344 \r
345 #if DEBUG_\r
346     public static void Test()\r
347     {\r
348       Font font = new Font("Times New Roman", 10);\r
349       FontData image = new FontData(font);\r
350 \r
351 //      Font font = new Font("Isabelle", 12);\r
352 //      LOGFONT logFont = new LOGFONT();\r
353 //      font.ToLogFont(logFont);\r
354 //\r
355 //      IntPtr hfont = CreateFontIndirect(logFont);\r
356 ////      IntPtr hfont2 = font.ToHfont();\r
357 ////      System.Windows.Forms.MessageBox.Show(hfont2.ToString());\r
358 //\r
359 //      Graphics gfx = Graphics.FromHwnd(IntPtr.Zero);\r
360 //      IntPtr hdc = gfx.GetHdc();\r
361 //      IntPtr oldFont =  SelectObject(hdc, hfont);\r
362 //      int size = GetFontData(hdc, 0, 0, null, 0);\r
363 //\r
364 //      byte[] fontbits = new byte[size];\r
365 //      int xx = GetFontData(hdc, 0, 0, fontbits, size);\r
366 //      SelectObject(hdc, oldFont);\r
367 //      DeleteObject(hfont);\r
368 //      gfx.ReleaseHdc(hdc);\r
369 //\r
370 //      FontData image = new FontData(fontbits);\r
371 //      //image.Read();\r
372 //\r
373 //\r
374 //      //HandleRef\r
375 //\r
376 //      font.GetType();\r
377     }\r
378 #endif\r
379   }\r
380 }