|resources:||Home Mailing List Installation Source Code Bugs Screenshots|
Project SILA Technical Design Document for Graphite 2
The original design for the SILA project was intended to allow rendering of Graphite fonts without supporting selection and cursor tracking.
The next stage in the SILA project was to add support for correctly displaying selections and cursors. The design therefore needed to be extended to allow for this.
There are 2 Mozilla modules which need to be modified to support Graphite: gfx and layout. The former is needed for the actually rendering, the latter to allow cursor tracking and selections to behave correctly with Graphite's glyph substitution and reordering.
SILA was initially based on the Graphite 1.0 API, however, it has now been modified to use the Graphite 2.0 API using the source code from Subversion. svn://scripts.sil.org/graphite/graphite/trunk
The Graphite library itself organises text in to TextSources, which are then rendered into a Segment of text containing information about how Unicode code points are mapped to specific glyphs and positions on screen. These Segments need to be accessible to allow cursor tracking to work correctly.
The Mozilla Gfx Module handles the platform specific rendering of Graphics and Fonts. It contains many different interfaces, but the most significant for using Graphite Fonts are nsIFontMetrics and nsIRenderingContext. nsIFontMetrics determines the specific rendering code used to display a particular font and it is here that most of the Graphite changes are. If a font is TrueType and has the Silf table, then a Graphite specific Font class will be used, otherwise Mozilla's default code will be used.
Changes were also necessary to nsRenderingContext to enable communication between the Layout module and Graphite. The nsIRenderingContext interface already contains a “FontID” parameter, so this was utilised to provide a means of referencing Graphite Segments from within the Layout module. However, in most cases the ID is not actually used in the existing Mozilla code, so the nsRenderingContext implementations were modified to pass the “FontID” to the nsIFontMetrics implementations.
Creating Segments using Graphite is expensive, but the line layout code in Layout makes a lot of calls to GetWidth or GetTextDimensions. This resulted in many different Segments being created and some of these were too small to contain the full context necessary to get an accurate measurement. A Segment Caching mechanmism was therefore introduced with associated code for keeping track of whether the current text had been cached. Most of this logic was platform independent, so a separate sila sub-module was created within the gfx module.
Sila specific Classes
A brief overview of the Sila classes is described below:
- A structure to pass information about the character array that is being measured or drawn between the nsIRenderingContext and the nsIFontMetrics implementation.
- A wrapper around a Segment and its associated TextSource. It has methods to measure and render the associated Segment and determine whether the text specified by a GrContextData structure is in the cache.
- This provides an implementation of the nsIGrSegWrapper interface, which is used by the layout module to interogate the segment for valid cursor positions, break points etc. It uses XPCOM to allow intermodule access.
- This keeps track of GrSegWrapper objects and associates them with FontIDs so they can be retrieved and manipulated from the layout module. It uses XPCOM to allow intermodule access.
- This creates or reuses GrSegCache objects as appropriate in a platform independent way. Having found a suitable GrSegCache object, it can be used for measuring or drawing.
- An implementation of TextSource, which can handle Mozilla's UTF-16 and UTF-32 text arrays. It also keeps track of selections positions.
- Used for Justification, it is almost identical to the Graphite example implementation.
- A mozilla specific factory for creating Platform specific Graphite SegmentPainters. This interface helps keep most of the SILA code platform independent.
Platform Specific Details
The sila modules is built statically and compiled directly into the gfx module. It is dynamically linked to the SilGraphite 2 library on the system.
The platform specific font changes are in the files nsRenderingContextGTK.cpp, nsFontMetricsXft.cpp and nsXftPainterFactory.cpp.
Currently printing on X is only possible using Xprint. Graphite printing only works if the Xprint code finds the Graphite font correctly. Frequently an alternative font is substituted, which means Graphite is not called. Due to a limitation in the way the Xprint module works, the Graphite font must have code points for all glyphs e.g. using the PUA for all glyph variants. Graphite lays out the glyphs by ID as normal and then a reverse look up is done to retrieve a code point that can be drawn using Xprint. This does not mean that the document itself needs to contain the PUA points, it is just a trick to allow the correct glyph to be drawn using only a character based X API.
The sila modules is built statically and compiled directly into the xprint module. It is dynamically linked to the SilGraphite 2 library on the system.
The platform specific font changes are in the files nsRenderingContextXlib.cpp, nsFontMetricsXlib.cpp and nsXpPainterFactory.cpp.
Graphite is built into a static library graphite.lib, which is then compiled directly into the sila module. The sila module is built as a separate module, which the windows gfx code interogates using XPCOM.
- Modifications to the normal Mozilla FontMetrics to call the sila module when a silf table is found in a TrueType font.
- Modifications to the normal Mozilla RenderingContext to pass the GrContextData through to the FontMetrics implementation.
- XPCOM interface to enable the windows gfx module to communicate with the sila module.
- Implementation of ISpecialTTFFont.
- Windows specific implementation of the Graphite Painter Factory. This is an implementation customized for Mozilla, not the one provided with Graphite.
There is no reason to prevent Graphite being added to other platforms, provided the Graphite library can be compiled for that platform. Normally you will need to:
- modify the nsIRenderingContext implementation to create GrContextData structures
- create an implementation of nsISegPainterFactory to do the actual painting in a platform specific way
- add code to the nsIFontMetrics implementation to create a platform specific gr::Font object and nsISegPainterFactory and pass them into GrSegCacheHandler to do the actual work.
The main changes are in the html/base/src/nsTextFrame class. This uses nsIRenderingContext and a combination of the FontID and NS_RENDERING_HINT_GRAPHITE to determine whether a Graphite font is in use. If present, the nsIGrSegManager interface is used to retreive an nsIGrSegWrapper implementation. This can then be queried for break points, and insertion points for cursor tracking, selections and line breaking.
Some changes were also necessary to nsTextTransformer for RTL text to prevent reordering and using Arabic Presentation forms when Graphite is present and doing its own substitution.
A new class GrLineBreaker was created as a wrapper around the existing LineBreaker implementation to allow the Graphite line break information to be processed when Graphite Segments are in use.
Minor changes were also necessary in layout/xul/base/src/nsTextBoxFrame.cpp and layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp to prevent elipsis from breaking in the middle of reordering/ligatures and in layout/base/src/nsBidiPresUtils.cpp to presever Control characters in non-bidi situations.
Author: Keith Stribley
Date: 8 Nov 2005