| [9] | 1 | <?xml version="1.0" encoding="UTF-8"?> | 
|---|
|  | 2 | <?xml-stylesheet type="text/css" | 
|---|
|  | 3 | href="eclipseos2-xxe.css" | 
|---|
|  | 4 | ?> | 
|---|
|  | 5 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | 
|---|
|  | 6 | "xhtml1-strict.dtd"> | 
|---|
|  | 7 | <html> | 
|---|
|  | 8 | <head> | 
|---|
|  | 9 | <link href="eclipseos2.css" rel="stylesheet" type="text/css" /> | 
|---|
|  | 10 |  | 
|---|
|  | 11 | <title>Eclipse for OS/2 Transitional Project Notes</title> | 
|---|
|  | 12 | </head> | 
|---|
|  | 13 |  | 
|---|
|  | 14 | <body> | 
|---|
|  | 15 | <h1>SWT Step 5. Font handling</h1> | 
|---|
|  | 16 |  | 
|---|
|  | 17 | <h2>Objective</h2> | 
|---|
|  | 18 |  | 
|---|
|  | 19 | <p>Implement font handling (including implementation of | 
|---|
|  | 20 | <code>o.e.swt.graphics.Font</code>, <code>FontData</code> and | 
|---|
|  | 21 | <code>FontMetrics</code> classes). The test example of this step should | 
|---|
|  | 22 | draw some text strings with various fonts on the SWT top window client | 
|---|
|  | 23 | area using corresponding methods of <code>o.e.swt.graphics.GC</code> | 
|---|
|  | 24 | class.</p> | 
|---|
|  | 25 |  | 
|---|
|  | 26 | <h2>Task notes</h2> | 
|---|
|  | 27 |  | 
|---|
|  | 28 | <h3>Font matching</h3> | 
|---|
|  | 29 |  | 
|---|
|  | 30 | <p>The <code>GpiQueryFaceString()</code> function is intended to compose a | 
|---|
|  | 31 | font facename (that includes textual representation of the font style) | 
|---|
|  | 32 | given a font family name and flags describing the desired style (bold, | 
|---|
|  | 33 | italic etc), by searching the installed fonts for the exact match (the | 
|---|
|  | 34 | font that truely has the requested style, without any emulation). But this | 
|---|
|  | 35 | function is buggy and sometimes returns NULL even if the desired font | 
|---|
|  | 36 | actually exists in the system. The example is the <kbd>Lucida Sans | 
|---|
|  | 37 | Typewriter</kbd> font family shipped with Java. If we have all 4 styles of | 
|---|
|  | 38 | this family installed (Regular, Bold, Oblique and Bold Oblique) and try to | 
|---|
|  | 39 | call this function for it with <code>FWEIGHT_DONT_CARE</code>, | 
|---|
|  | 40 | <code>FWIDTH_DONT_CARE</code> and <code>FTYPE_ITALIC</code> flags set | 
|---|
|  | 41 | ((i.e. we request any <kbd>Lucida Sans Typewriter</kbd> font that has the | 
|---|
|  | 42 | <code>FM_SEL_ITALIC</code> bit set in the | 
|---|
|  | 43 | <code>FONTMETRICS.fsSelection</code> field, ignoring | 
|---|
|  | 44 | <code>usWeightClass</code> and <code>usWidthClass)</code> we get nothing | 
|---|
|  | 45 | although the <kbd>Lucida Sans Typewriter Oblique</kbd> exists and | 
|---|
|  | 46 | completely matches the given criteria.</p> | 
|---|
|  | 47 |  | 
|---|
|  | 48 | <p>Due to this bug, the only way to find a font given its family name and | 
|---|
|  | 49 | style is the full scan of the list of all fonts installed in the system. | 
|---|
|  | 50 | So we decided to cache the complete font list in some useful way upon the | 
|---|
|  | 51 | first font request from the SWT user to speed up further font searches. | 
|---|
|  | 52 | Hopefully, there is a function <code>GpiQueryFontAction()</code> that can | 
|---|
|  | 53 | tell us whether the font list has been changed since the last call or not, | 
|---|
|  | 54 | which can be used to check the validity of the cache.</p> | 
|---|
|  | 55 |  | 
|---|
|  | 56 | <p>The font matching algorithm is close enough to the original algorithm | 
|---|
|  | 57 | used by OS/2 to ensure that any font matched by our algorithm can be set | 
|---|
|  | 58 | as the font for standard OS/2 window classes, but has some differences (in | 
|---|
|  | 59 | particular, when there is no bitmap font of the requested size installed | 
|---|
|  | 60 | in the system we select one with the closest size from the font family, | 
|---|
|  | 61 | while OS/2 always selects the default font in that cases). See the | 
|---|
|  | 62 | <code>Device.matchFont()</code> methods for more detailed info about the | 
|---|
|  | 63 | matching algorithm.</p> | 
|---|
|  | 64 |  | 
|---|
|  | 65 | <h3>Font metrics</h3> | 
|---|
|  | 66 |  | 
|---|
|  | 67 | <p>There are numerous bugs with calculating font metrics in GPI. For | 
|---|
|  | 68 | example, various text drawing functions start using and returning wrong | 
|---|
|  | 69 | character box and string extent coordinates when we set the text alignment | 
|---|
|  | 70 | (<code>GpiSetTextAlignment()</code>) other than default | 
|---|
|  | 71 | (<code>TA_NORMAL_HORIZ</code>, <code>TA_NORMAL_VERT</code>). So we always | 
|---|
|  | 72 | have to use this default alignment (instead of <code>TA_TOP</code> for | 
|---|
|  | 73 | example, which would be more useful in SWT).</p> | 
|---|
|  | 74 |  | 
|---|
|  | 75 | <p>Also GPI wrongly fills the background of the string for true type fonts | 
|---|
|  | 76 | -- it makes it one pixel higher than the real font height | 
|---|
|  | 77 | (<code>lMaxBaselineExt</code>) and leaves unfilled gaps at the beginning | 
|---|
|  | 78 | and at the end of the string. For this reason we always fill the | 
|---|
|  | 79 | background ourselves instructing GPI not to fill it when drawing | 
|---|
|  | 80 | strings.</p> | 
|---|
|  | 81 |  | 
|---|
|  | 82 | <p>Another problem is that <code>lMaxBaselineExt</code> (as returned by | 
|---|
|  | 83 | <code>GpiQueryFontMetrics()</code>) is not always the sum of | 
|---|
|  | 84 | <code>lMaxAscender</code> and <code>lMaxDescender</code>, sometimes this | 
|---|
|  | 85 | sum is one pixel larger -- the example is the <kbd>Times New Roman</kbd> | 
|---|
|  | 86 | TTF font drawn at 24 points (40 pixels). As a solution we calculate the | 
|---|
|  | 87 | font descent value as <code>lMaxBaselineExt - lMaxAscender</code> instead | 
|---|
|  | 88 | of taking <code>lMaxDescender</code> from returned font metrics.</p> | 
|---|
|  | 89 |  | 
|---|
|  | 90 | <h3>FATTRS and FONTMETRICS sz* fields</h3> | 
|---|
|  | 91 |  | 
|---|
|  | 92 | <p>Originally <code>FATTRS</code> and <code>FONTMETRICS</code> structures | 
|---|
|  | 93 | have fields, <code>szFamilyname</code> and <code>szFacename</code> | 
|---|
|  | 94 | (<code>FONTMETRICS</code> only) to store the font family and face name as | 
|---|
|  | 95 | arrays of <code>char</code>s (not as pointers to arrays). In Java we can | 
|---|
|  | 96 | only define poiners (references) to arrays as field members. As opposed to | 
|---|
|  | 97 | Windows version of SWT, where they defined similar fields as a sequence of | 
|---|
|  | 98 | separate N byte fields (where N is the length of the original array), in | 
|---|
|  | 99 | OS/2 we defined them as references to java byte arrays. It is much easier | 
|---|
|  | 100 | to work with these arrays but is potentially dangerous because one can | 
|---|
|  | 101 | accidentally replace these references with references to arrays of | 
|---|
|  | 102 | different size -- it must not happen because the code assumes that these | 
|---|
|  | 103 | arrays always have the length of <code>OS.FACENAME</code> (32) bytes. When | 
|---|
|  | 104 | the structure is instantiated these arrays are allocated automatically and | 
|---|
|  | 105 | should not be reallocated later.</p> | 
|---|
|  | 106 |  | 
|---|
|  | 107 | <h3>Locales</h3> | 
|---|
|  | 108 |  | 
|---|
|  | 109 | <p>There are two ways to support unicode when working in OS/2 PM/GPI.</p> | 
|---|
|  | 110 |  | 
|---|
|  | 111 | <p>The first way is to convert unicode strings to single-byte strings | 
|---|
|  | 112 | using <code>Uni*</code> API (availabel since Warp 4 Fixpak 5) before | 
|---|
|  | 113 | passing them to PM/GPI calls and provide the corresponding single-byte | 
|---|
|  | 114 | codepage numbers when creating logical fonts and setting the message queue | 
|---|
|  | 115 | codepage.</p> | 
|---|
|  | 116 |  | 
|---|
|  | 117 | <p>The second way is to work in true unicode mode -- this means to use the | 
|---|
|  | 118 | codepage <kbd>IBM-1200</kbd> for font creation and the message queue and | 
|---|
|  | 119 | pass unicode strings to PM/GPI as is, i.e. as two-byte unicode | 
|---|
|  | 120 | strings.</p> | 
|---|
|  | 121 |  | 
|---|
|  | 122 | <p>It's obvious that the second way is more convenient than the first | 
|---|
|  | 123 | because it allows to draw characters from different languages | 
|---|
|  | 124 | simultaneously, using the same logical font (of course, provided that the | 
|---|
|  | 125 | font physically contains glyphs for all used character groups). It is also | 
|---|
|  | 126 | the only way to display such different characters in the window's | 
|---|
|  | 127 | titlebar. Unfortunately, the current implementation of this approach is | 
|---|
|  | 128 | quite buggy in OS/2. The following bugs are known:</p> | 
|---|
|  | 129 |  | 
|---|
|  | 130 | <ul> | 
|---|
|  | 131 | <li>For some bitmap fonts (<kbd>WarpSans</kbd> and <kbd>System | 
|---|
|  | 132 | Proportional</kbd>) GPI reports the wrong character width/extent for | 
|---|
|  | 133 | unicode characters that are absent in the font. GPI visualizes such | 
|---|
|  | 134 | missing characters with an empty rectangle character, but the reported | 
|---|
|  | 135 | width is two times smaller than its actual width. This causes some | 
|---|
|  | 136 | problems when formatting text and concatenating text strings in | 
|---|
|  | 137 | SWT.</li> | 
|---|
|  | 138 |  | 
|---|
|  | 139 | <li>There is a very strange bug appearing when we draw strings using | 
|---|
|  | 140 | <kbd>Courier New</kbd> and <kbd>Courier New Bold</kbd> TrueType fonts -- | 
|---|
|  | 141 | GPI places an extra space (equal to the average character width) between | 
|---|
|  | 142 | every character it draws which makes this fonts unusable in unicode | 
|---|
|  | 143 | mode.</li> | 
|---|
|  | 144 |  | 
|---|
|  | 145 | <li>For some bitmap fonts (<kbd>WarpSans</kbd>) GPI refuses to draw | 
|---|
|  | 146 | characters from some unicode groups (for example, japanese characters in | 
|---|
|  | 147 | range <code>0xFF00 - 0xFFEF</code>) although their glypths are actually | 
|---|
|  | 148 | present in the font.</li> | 
|---|
|  | 149 | </ul> | 
|---|
|  | 150 |  | 
|---|
|  | 151 | <p>When we use the first approach (single-byte strings with convertion) | 
|---|
|  | 152 | bugs pointed above do not appear. But this approach doesn't allow to draw | 
|---|
|  | 153 | characters from different languages using the same font (a separate font | 
|---|
|  | 154 | for every character group is required).</p> | 
|---|
|  | 155 |  | 
|---|
|  | 156 | <p>In SWT, the only way to deal with locales is to use the | 
|---|
|  | 157 | <code>FontData.setLocale()</code> method to create fonts from different | 
|---|
|  | 158 | locales. As we understand this method makes sense only on systems that | 
|---|
|  | 159 | lack the true unicode support (Win95?) and it corresponds to the first way | 
|---|
|  | 160 | of working with unicode described above. For systems that support true | 
|---|
|  | 161 | unicode it is useless and even produces worse results (WinXP).</p> | 
|---|
|  | 162 |  | 
|---|
|  | 163 | <p>Since OS/2 provides the true unicode support it seems to be more | 
|---|
|  | 164 | logical to use it in SWT. But at the present time we decided not to do it | 
|---|
|  | 165 | because of listed bugs. Currently the only alternative way is to use | 
|---|
|  | 166 | single-byte approach and implement the <code>setLocale()</code> method. | 
|---|
|  | 167 | But we decided not to go this way also because of its conceptual | 
|---|
|  | 168 | limitations and because we believe that bugs of true unicode mode will be | 
|---|
|  | 169 | fixed in the near future by switching to the usage of the <a | 
|---|
|  | 170 | href="http://www.freetype.org">FreeType</a> library to work with fonts | 
|---|
|  | 171 | which is free of many-many font-related bugs seen in OS/2. So, currently, | 
|---|
|  | 172 | only unicode characters that correspond to the system locale are displayed | 
|---|
|  | 173 | correctly (unicode is converted to single-byte using | 
|---|
|  | 174 | <code>String.toBytes()</code> method) and there is no way to switch | 
|---|
|  | 175 | locales (from the font usage point of view) dynamically in runtime.</p> | 
|---|
|  | 176 |  | 
|---|
|  | 177 | <h2>Step checklist</h2> | 
|---|
|  | 178 |  | 
|---|
|  | 179 | <table> | 
|---|
|  | 180 | <col width="40%" /> | 
|---|
|  | 181 |  | 
|---|
|  | 182 | <col /> | 
|---|
|  | 183 |  | 
|---|
|  | 184 | <col width="50%" /> | 
|---|
|  | 185 |  | 
|---|
|  | 186 | <thead> | 
|---|
|  | 187 | <tr> | 
|---|
|  | 188 | <th>Operation</th> | 
|---|
|  | 189 |  | 
|---|
|  | 190 | <th>Status</th> | 
|---|
|  | 191 |  | 
|---|
|  | 192 | <th>Remarks</th> | 
|---|
|  | 193 | </tr> | 
|---|
|  | 194 | </thead> | 
|---|
|  | 195 |  | 
|---|
|  | 196 | <tr> | 
|---|
|  | 197 | <td>Add <code>FATTRS</code> and <code>FONTMETRICS</code> classes and | 
|---|
|  | 198 | their native getters/setters, <code>FATTRS_*</code> constants, | 
|---|
|  | 199 | <code>FM_TYPE_*</code>, <code>FM_DEFN_*</code>, <code>FM_SEL_*</code>, | 
|---|
|  | 200 | <code>FM_CAP_*</code> constants</td> | 
|---|
|  | 201 |  | 
|---|
|  | 202 | <td>Done [eli, dmik]</td> | 
|---|
|  | 203 |  | 
|---|
|  | 204 | <td></td> | 
|---|
|  | 205 | </tr> | 
|---|
|  | 206 |  | 
|---|
|  | 207 | <tr> | 
|---|
|  | 208 | <td>Implement <code>FontMetrics</code> class</td> | 
|---|
|  | 209 |  | 
|---|
|  | 210 | <td>Done [eli, dmik]</td> | 
|---|
|  | 211 |  | 
|---|
|  | 212 | <td></td> | 
|---|
|  | 213 | </tr> | 
|---|
|  | 214 |  | 
|---|
|  | 215 | <tr> | 
|---|
|  | 216 | <td>Implement several missing methods in the <code>GC</code> | 
|---|
|  | 217 | class</td> | 
|---|
|  | 218 |  | 
|---|
|  | 219 | <td>Done [dmik]</td> | 
|---|
|  | 220 |  | 
|---|
|  | 221 | <td>See the updated <a href="swt006.html">SWT006 Step</a> for more | 
|---|
|  | 222 | info</td> | 
|---|
|  | 223 | </tr> | 
|---|
|  | 224 |  | 
|---|
|  | 225 | <tr> | 
|---|
|  | 226 | <td>Add <code>OS.WinSetPresParam (...byte[])</code>, | 
|---|
|  | 227 | <code>WinQueryPresParam()</code></td> | 
|---|
|  | 228 |  | 
|---|
|  | 229 | <td>Done [dmik]</td> | 
|---|
|  | 230 |  | 
|---|
|  | 231 | <td></td> | 
|---|
|  | 232 | </tr> | 
|---|
|  | 233 |  | 
|---|
|  | 234 | <tr> | 
|---|
|  | 235 | <td>Add <code>OS.GpiQueryFaceString()</code>, | 
|---|
|  | 236 | <code>FACENAMEDESC</code> and <code>FWEIGHT_*</code>, | 
|---|
|  | 237 | <code>FWIDTH_*</code>, <code>FTYPE_*</code> constants... | 
|---|
|  | 238 | remove!!!</td> | 
|---|
|  | 239 |  | 
|---|
|  | 240 | <td>Done [dmik]</td> | 
|---|
|  | 241 |  | 
|---|
|  | 242 | <td></td> | 
|---|
|  | 243 | </tr> | 
|---|
|  | 244 |  | 
|---|
|  | 245 | <tr> | 
|---|
|  | 246 | <td>Add <code>OS.GpiQueryFontAction()</code> and <code>QFA_*</code> | 
|---|
|  | 247 | constants; <code>GpiQueryFonts()</code> and <code>QF_*</code> | 
|---|
|  | 248 | constants, <code>GpiQueryFontMetrics()</code></td> | 
|---|
|  | 249 |  | 
|---|
|  | 250 | <td>Done [dmik]</td> | 
|---|
|  | 251 |  | 
|---|
|  | 252 | <td></td> | 
|---|
|  | 253 | </tr> | 
|---|
|  | 254 |  | 
|---|
|  | 255 | <tr> | 
|---|
|  | 256 | <td>Add <code>OS.GpiCreateLogFont()</code> and <code>FONT_*</code> | 
|---|
|  | 257 | constants, <code>GpiSetCharSet()</code>, | 
|---|
|  | 258 | <code>GpiSetCharBox()</code></td> | 
|---|
|  | 259 |  | 
|---|
|  | 260 | <td>Done [dmik]</td> | 
|---|
|  | 261 |  | 
|---|
|  | 262 | <td></td> | 
|---|
|  | 263 | </tr> | 
|---|
|  | 264 |  | 
|---|
|  | 265 | <tr> | 
|---|
|  | 266 | <td>Add <code>OS.GpiCharStringAt()</code>, | 
|---|
|  | 267 | <code>GpiSetTextAlignment()</code>, <code>GpiQueryTextBox()</code> | 
|---|
|  | 268 | & <code>TXTBOX_*</code> constants</td> | 
|---|
|  | 269 |  | 
|---|
|  | 270 | <td>Done [dmik]</td> | 
|---|
|  | 271 |  | 
|---|
|  | 272 | <td></td> | 
|---|
|  | 273 | </tr> | 
|---|
|  | 274 |  | 
|---|
|  | 275 | <tr> | 
|---|
|  | 276 | <td>Add <code>OS.PrfQueryProfileSize()</code>, | 
|---|
|  | 277 | <code>PrfQueryProfileString()</code> and <code>HINI_*</code> | 
|---|
|  | 278 | constants</td> | 
|---|
|  | 279 |  | 
|---|
|  | 280 | <td>Done [dmik]</td> | 
|---|
|  | 281 |  | 
|---|
|  | 282 | <td>to read the system font value from OS2.INI</td> | 
|---|
|  | 283 | </tr> | 
|---|
|  | 284 |  | 
|---|
|  | 285 | <tr> | 
|---|
|  | 286 | <td>Add <code>OS.WinEnableWindow()</code> and implement | 
|---|
|  | 287 | <code>Control.setEnabled()</code></td> | 
|---|
|  | 288 |  | 
|---|
|  | 289 | <td>Done [dmik]</td> | 
|---|
|  | 290 |  | 
|---|
|  | 291 | <td></td> | 
|---|
|  | 292 | </tr> | 
|---|
|  | 293 |  | 
|---|
|  | 294 | <tr> | 
|---|
|  | 295 | <td>Implement <code>FontData</code> and <code>Font</code> classes</td> | 
|---|
|  | 296 |  | 
|---|
|  | 297 | <td>Done [dmik]</td> | 
|---|
|  | 298 |  | 
|---|
|  | 299 | <td></td> | 
|---|
|  | 300 | </tr> | 
|---|
|  | 301 |  | 
|---|
|  | 302 | <tr> | 
|---|
|  | 303 | <td>Imlement string methods in the <code>GC</code> class</td> | 
|---|
|  | 304 |  | 
|---|
|  | 305 | <td>Done [dmik]</td> | 
|---|
|  | 306 |  | 
|---|
|  | 307 | <td></td> | 
|---|
|  | 308 | </tr> | 
|---|
|  | 309 |  | 
|---|
|  | 310 | <tr> | 
|---|
|  | 311 | <td>Add testcases <code>SWT005_01</code> and | 
|---|
|  | 312 | <code>SWT005_02</code></td> | 
|---|
|  | 313 |  | 
|---|
|  | 314 | <td>Done [dmik]</td> | 
|---|
|  | 315 |  | 
|---|
|  | 316 | <td></td> | 
|---|
|  | 317 | </tr> | 
|---|
|  | 318 | </table> | 
|---|
|  | 319 | </body> | 
|---|
|  | 320 | </html> | 
|---|