Implement font handling (including implementation of
o.e.swt.graphics.Font
, FontData
and
FontMetrics
classes). The test example of this step should
draw some text strings with various fonts on the SWT top window client
area using corresponding methods of o.e.swt.graphics.GC
class.
The GpiQueryFaceString()
function is intended to compose a
font facename (that includes textual representation of the font style)
given a font family name and flags describing the desired style (bold,
italic etc), by searching the installed fonts for the exact match (the
font that truely has the requested style, without any emulation). But this
function is buggy and sometimes returns NULL even if the desired font
actually exists in the system. The example is the Lucida Sans
Typewriter font family shipped with Java. If we have all 4 styles of
this family installed (Regular, Bold, Oblique and Bold Oblique) and try to
call this function for it with FWEIGHT_DONT_CARE
,
FWIDTH_DONT_CARE
and FTYPE_ITALIC
flags set
((i.e. we request any Lucida Sans Typewriter font that has the
FM_SEL_ITALIC
bit set in the
FONTMETRICS.fsSelection
field, ignoring
usWeightClass
and usWidthClass)
we get nothing
although the Lucida Sans Typewriter Oblique exists and
completely matches the given criteria.
Due to this bug, the only way to find a font given its family name and
style is the full scan of the list of all fonts installed in the system.
So we decided to cache the complete font list in some useful way upon the
first font request from the SWT user to speed up further font searches.
Hopefully, there is a function GpiQueryFontAction()
that can
tell us whether the font list has been changed since the last call or not,
which can be used to check the validity of the cache.
The font matching algorithm is close enough to the original algorithm
used by OS/2 to ensure that any font matched by our algorithm can be set
as the font for standard OS/2 window classes, but has some differences (in
particular, when there is no bitmap font of the requested size installed
in the system we select one with the closest size from the font family,
while OS/2 always selects the default font in that cases). See the
Device.matchFont()
methods for more detailed info about the
matching algorithm.
There are numerous bugs with calculating font metrics in GPI. For
example, various text drawing functions start using and returning wrong
character box and string extent coordinates when we set the text alignment
(GpiSetTextAlignment()
) other than default
(TA_NORMAL_HORIZ
, TA_NORMAL_VERT
). So we always
have to use this default alignment (instead of TA_TOP
for
example, which would be more useful in SWT).
Also GPI wrongly fills the background of the string for true type fonts
-- it makes it one pixel higher than the real font height
(lMaxBaselineExt
) and leaves unfilled gaps at the beginning
and at the end of the string. For this reason we always fill the
background ourselves instructing GPI not to fill it when drawing
strings.
Another problem is that lMaxBaselineExt
(as returned by
GpiQueryFontMetrics()
) is not always the sum of
lMaxAscender
and lMaxDescender
, sometimes this
sum is one pixel larger -- the example is the Times New Roman
TTF font drawn at 24 points (40 pixels). As a solution we calculate the
font descent value as lMaxBaselineExt - lMaxAscender
instead
of taking lMaxDescender
from returned font metrics.
Originally FATTRS
and FONTMETRICS
structures
have fields, szFamilyname
and szFacename
(FONTMETRICS
only) to store the font family and face name as
arrays of char
s (not as pointers to arrays). In Java we can
only define poiners (references) to arrays as field members. As opposed to
Windows version of SWT, where they defined similar fields as a sequence of
separate N byte fields (where N is the length of the original array), in
OS/2 we defined them as references to java byte arrays. It is much easier
to work with these arrays but is potentially dangerous because one can
accidentally replace these references with references to arrays of
different size -- it must not happen because the code assumes that these
arrays always have the length of OS.FACENAME
(32) bytes. When
the structure is instantiated these arrays are allocated automatically and
should not be reallocated later.
There are two ways to support unicode when working in OS/2 PM/GPI.
The first way is to convert unicode strings to single-byte strings
using Uni*
API (availabel since Warp 4 Fixpak 5) before
passing them to PM/GPI calls and provide the corresponding single-byte
codepage numbers when creating logical fonts and setting the message queue
codepage.
The second way is to work in true unicode mode -- this means to use the codepage IBM-1200 for font creation and the message queue and pass unicode strings to PM/GPI as is, i.e. as two-byte unicode strings.
It's obvious that the second way is more convenient than the first because it allows to draw characters from different languages simultaneously, using the same logical font (of course, provided that the font physically contains glyphs for all used character groups). It is also the only way to display such different characters in the window's titlebar. Unfortunately, the current implementation of this approach is quite buggy in OS/2. The following bugs are known:
0xFF00 - 0xFFEF
) although their glypths are actually
present in the font.When we use the first approach (single-byte strings with convertion) bugs pointed above do not appear. But this approach doesn't allow to draw characters from different languages using the same font (a separate font for every character group is required).
In SWT, the only way to deal with locales is to use the
FontData.setLocale()
method to create fonts from different
locales. As we understand this method makes sense only on systems that
lack the true unicode support (Win95?) and it corresponds to the first way
of working with unicode described above. For systems that support true
unicode it is useless and even produces worse results (WinXP).
Since OS/2 provides the true unicode support it seems to be more
logical to use it in SWT. But at the present time we decided not to do it
because of listed bugs. Currently the only alternative way is to use
single-byte approach and implement the setLocale()
method.
But we decided not to go this way also because of its conceptual
limitations and because we believe that bugs of true unicode mode will be
fixed in the near future by switching to the usage of the FreeType library to work with fonts
which is free of many-many font-related bugs seen in OS/2. So, currently,
only unicode characters that correspond to the system locale are displayed
correctly (unicode is converted to single-byte using
String.toBytes()
method) and there is no way to switch
locales (from the font usage point of view) dynamically in runtime.
Operation | Status | Remarks |
---|---|---|
Add FATTRS and FONTMETRICS classes and
their native getters/setters, FATTRS_* constants,
FM_TYPE_* , FM_DEFN_* , FM_SEL_* ,
FM_CAP_* constants |
Done [eli, dmik] | |
Implement FontMetrics class |
Done [eli, dmik] | |
Implement several missing methods in the GC
class |
Done [dmik] | See the updated SWT006 Step for more info |
Add OS.WinSetPresParam (...byte[]) ,
WinQueryPresParam() |
Done [dmik] | |
Add OS.GpiQueryFaceString() ,
FACENAMEDESC and FWEIGHT_* ,
FWIDTH_* , FTYPE_* constants...
remove!!! |
Done [dmik] | |
Add OS.GpiQueryFontAction() and QFA_*
constants; GpiQueryFonts() and QF_*
constants, GpiQueryFontMetrics() |
Done [dmik] | |
Add OS.GpiCreateLogFont() and FONT_*
constants, GpiSetCharSet() ,
GpiSetCharBox() |
Done [dmik] | |
Add OS.GpiCharStringAt() ,
GpiSetTextAlignment() , GpiQueryTextBox()
& TXTBOX_* constants |
Done [dmik] | |
Add OS.PrfQueryProfileSize() ,
PrfQueryProfileString() and HINI_*
constants |
Done [dmik] | to read the system font value from OS2.INI |
Add OS.WinEnableWindow() and implement
Control.setEnabled() |
Done [dmik] | |
Implement FontData and Font classes |
Done [dmik] | |
Imlement string methods in the GC class |
Done [dmik] | |
Add testcases SWT005_01 and
SWT005_02 |
Done [dmik] |