2 Conventions and Design in the FreeType library
3 ----------------------------------------------
10 I. Style and Formatting
13 2. Declarations & Statements
17 II. Design conventions
19 1. Modularity and Components Layout
20 2. Configuration and Debugging
22 III. Usage conventions
26 3. Memory management (due to change soon)
27 4. Support for threaded environments
34 This text introduces the many conventions used within the FreeType
35 library code. Please read it before trying any modifications or
36 extensions of the source code.
40 I. Style and Formatting
41 =======================
43 The following coding rules are extremely important to keep the
44 library's source code homogeneously. Keep in mind the following
47 - `Humans read source code, not machines' (Donald Knuth)
49 The library source code should be as readable as possible, even
50 by non-C experts. With `readable', two things are meant: First,
51 the source code should be pleasant to the eye, with sufficient
52 whitespace and newlines, to not look like a boring stack of
53 characters stuck to each other. Second, the source should be
54 _expressive_ enough about its goals. This convention contains
55 rules that can help the source focus on its purpose, not on a
56 particular implementation.
58 - `Paper is the _ultimate_ debugger' (David Turner :-)
60 There is nothing like sheets of paper (and a large floor) to
61 help you understand the design of a library you're new to, or to
62 debug it. The formatting style presented here is targeted at
63 printing. For example, it is more than highly recommended to
64 never produce a source line that is wider than 78 columns. More
73 A unit of the library is called a `component'. Each component
74 has at least an interface, and often a body. The library comes
75 in two language flavors, C and Pascal (the latter severely out
76 of date unfortunately). A component in C is defined by two
77 files, one `.h' header and one `.c' body, while a Pascal
78 component is contained in a single `.pas' file.
80 All component source file names begin with the `tt' prefix, with
81 the exception of the `FreeType' component. For example, the
82 file component is implemented by the files `ttfile.h',
83 `ttfile.c', and `ttfile.pas'. Only lowercase letters should be
84 used, following the 8+3 naming convention to allow compilation
87 In the C version, a single component can have multiple bodies.
88 For example, `ttfile.c' provides stream i/o through standard
89 ANSI libc calls, while `ttfile2.c' implements the same thing
90 using a Unix memory-mapping API.
92 The FreeType component is an interface-only component.
94 b. Long and expressive labels
96 Never hesitate to use long labels for your types, variables,
97 etc.! Except maybe for things like very trivial types, the
98 longest is the best, as it increases the source's
99 _expressiveness_. Never forget that the role of a label is to
100 express the `function' of the entity it represents, not its
103 NOTE: Hungarian notation is NOT expressive, as it sticks the
104 `type' of a variable to its name. A label like `usFoo'
105 rarely tells the use of the variable it represents.
107 And the state of a variable (global, static, dynamic)
108 isn't helpful anymore.
110 Avoid Hungarian Notation like the *plague*!
113 When forging a name with several nouns
114 (e.g. `number-of-points'), use an uppercase letter for the first
115 letter of each word (except the first), like:
119 You are also welcomed to introduce underscores `_' in your
120 labels, especially when sticking large nouns together, as it
121 `airs' the code greatly. E.g.:
123 `numberOfPoints' or `number_Of_Points'
125 `IncredibleFunction' or `Incredible_Function'
127 And finally, always put a capital letter after an underscore,
128 except in variable labels that are all lowercase:
130 `number_of_points' is OK for a variable (_all_ lowercase label)
132 `incredible_function' is NOT for a function!
135 `Microsoft_windows' is a *shame*!
138 `Microsoft_Windows' isn't really better, but at least its a
139 ^ ^ correct function label within this
144 All types that are defined for use by FreeType client
145 applications are defined in the FreeType component. All types
146 defined there have a label beginning with `TT_'. Examples:
148 TT_Face, TT_F26Dot6, etc.
150 However, the library uses a lot more of internal types that are
151 defined in the Types, Tables, and Objs components (`tttypes' &
154 By convention, all internal types, except the simplest ones like
155 integers, have their name beginning with a capital `T', like in
156 'TFoo'. Note that the first letter of `foo' is also
157 capitalized. The corresponding pointer type uses a capital `P'
158 instead, i.e. (TFoo*) is simply named 'PFoo'. Examples:
160 typedef struct _TTableDir
162 TT_Fixed version; /* should be 0x10000 */
163 UShort numTables; /* Tables number */
165 UShort searchRange; /* These parameters are only used */
166 UShort entrySelector;/* for a dichotomy search in the */
167 UShort rangeShift; /* directory. We ignore them. */
170 typedef TTableDir* PTableDir;
172 Note that we _always_ define a typedef for structures. The
173 original struct label starts with `_T'.
175 This convention is a famous one from the Pascal world.
177 Try to use C or Pascal types to the very least! Rely on
178 internally defined equivalent types instead. For example, not
179 all compilers agree on the sign of `char'; the size of `int' is
180 platform-specific, etc.
182 There are equivalents to the most common types in the `Types'
183 components, like `Short', `UShort', etc. Using the internal
184 types will guarantee that you won't need to replace every
185 occurence of `short' or wathever when compiling on a weird
186 platform or with a weird compiler, and there are many more than
187 you could think of...
191 The name of a function should always begin with a capital
192 letter, as lowercase first letters are reserved for variables.
193 The name of a function should be, again, _expressive_! Never
194 hesitate to put long function names in your code: It will make
195 the code much more readable.
197 Expressiveness doesn't necessarily imply lengthiness though; for
198 instance, reading shorts from a file stream is performed using
199 the following functions defined in the `File' component:
201 Get_Byte, Get_Short, Get_UShort, Get_Long, etc.
203 Which is somewhat more readable than:
205 cget, sget, usget, lget, etc.
209 Variable names should always begin with a lowercase letter.
210 Lowercase first letters are reserved for variables in this
211 convention, as it has been already explained above. You're
212 still welcome to use long and expressive variable names.
214 Something like `numP' can express a number of pixels, porks,
215 pancakes, and much more... Something like `num_points' won't.
217 Today, we are still using short variable labels in some parts of
218 the library. We're working on removing them however...
220 As a side note, a field name of a structure counts as a variable
221 name too. There are exceptions to the first-lowercase-letter
222 rule, but these are only related to fields within the structure
223 defined by the TrueType specification (well, at least it
224 _should_ be that way).
227 2. Declarations & Statements
228 ----------------------------
232 Try to align declarations and assignments in columns, if it
233 proves logical. For example (taken from `ttraster.c'):
237 Int flow; /* Profile orientation : Asc/Descending */
238 Int height; /* profile's height in scanlines */
239 Int start; /* profile's start scanline */
240 ULong offset; /* offset of profile's data in render pool */
241 PProfile link; /* link to next profile */
242 Int index; /* index of profile's entry in trace table */
243 Int count_lines; /* count of lines having to be drawn */
244 Int start_line; /* lines to be rendered before this profile */
245 PTraceRec trace; /* pointer to profile's current trace table */
251 Int flow; /* Profile orientation : Asc/Descending */
252 Int height; /* profile's height in scanlines */
253 Int start; /* profile's start scanline */
254 ULong offset; /* offset of profile's data in render pool */
255 PProfile link; /* link to next profile */
256 Int index; /* index of profile's entry in trace table */
257 Int count_lines; /* count of lines having to be drawn */
258 Int start_line; /* lines to be rendered before this profile */
259 PTraceRec trace; /* pointer to profile's current trace table */
262 This comes from the fact that you're more interested by the
263 field and its function than by its type.
277 And don't hesitate to separate blocks of declarations with
278 newlines to `distinguish' logical sections.
280 E.g., taken from an old source file, in the declarations of the CMap
288 unsigned short* glArray;
293 TCMapDirEntry entry_;
295 PCMap2SubHeader Plcmsub;
297 PCMap4Segment segments;
306 unsigned short *glArray;
310 TCMapDirEntry entry_;
312 PCMap2SubHeader Plcmsub;
314 PCMap4Segment segments;
316 b. Aliases and the `with' clause
318 The Pascal language comes with a very handy `with' clause that
319 is often used when dealing with the fields of a same record.
320 The following Pascal source extract
322 with table[incredibly_long_index] do
326 z := wathever_the_hell;
329 is usually translated to:
331 table[incredibly_long_index].x = some_x;
332 table[incredibly_long_index].y = some_y;
333 table[incredibly_long_index].z = wathever_the_hell;
335 When a lot of fields are involved, it is usually helpful to
336 define an `alias' for the record, like in:
338 alias = table + incredibly_long_index;
342 alias->z = wathever_the_hell;
344 which gives cleaner source code, and eases the compiler's
347 Though the use of aliases is currently not fixed in the current
348 library source, it is useful to follow one of these rules:
350 - Avoid an alias with a stupid, or cryptic name, something like:
354 [lots of lines snipped]
357 tfr = weird_table + weird_index;
363 It doesn't really help to guess what 'tfr' stands for several
364 lines after its declaration, even if it's an extreme
365 contraction of one particular type.
367 Something like `cur_record' or `alias_cmap' is better. The
368 current source also uses a prefix of `Pl' for such aliases
369 (like Pointer to Local alias), but this use is _not_
370 encouraged. If you want to use prefixes, use `loc_', `cur_',
371 or `al_' at the very least, with a descriptive name following.
373 - Or simply use a local variable with a semi-expressive name:
376 THorizontalHeader hheader;
377 TVerticalHeader vheader;
380 hheader = instance->fontRes->horizontalHeader;
381 vheader = instance->fontRes->verticalHeader;
388 which is much better than
391 THorizontalHeader Plhhead;
392 TVerticalHeader Plvhead;
394 Plhhead = instance->fontRes->horizontalHeader;
395 Plvhead = instance->fontRes->verticalHeader;
406 Block separation is done with `{' and `}'. We do not use the K&R
407 convention which becomes only useful with an extensive use of
408 tabs. The `{' and its corresponding `}' should always be on the
409 same column. It makes it easier to separate a block from the rest
410 of the source, and it helps your _brain_ associates the accolades
411 easily (ask any Lisp programmer on the topic!).
413 Use two spaces for the next indentation level.
415 Never use tabs in your code, their widths may vary with editors
420 if (condition_test) {
422 I'm doing K&R format;
423 just like the Linux kernel;
425 This test failed poorly;
430 if ( condition_test )
432 This code isn't stuck to the condition;
433 read it on paper, you'll find it more;
438 Of course, this is a matter of taste;
439 That's just the way it is in this convention;
440 and you should follow it to be homogenous with;
441 the rest of the FreeType code;
450 Macros should be made of uppercase letters. When a macro label is
451 forged from several words, it is possible to only uppercasify the
452 first word, using an underscore to separate the nouns. This is
453 used in ttload.c, ttgload.c and ttfile.c with macros like
455 ACCESS_Frame, GET_UShort, CUR_Stream
457 The role of the macros used throughout the engine is explained
458 later in this document.
462 II. Design Conventions
463 ======================
466 1. Modularity and Components Layout
467 -----------------------------------
469 The FreeType engine has been designed with portability in mind.
470 This implies the ability to compile and run it on a great variety
471 of systems and weird environments, unlike many packages where the
472 word strictly means `runs on a bunch of Unix-like systems'. We
473 have thus decided to stick to the following restrictions:
475 - The C version is written in ANSI C. The Pascal version compiles
476 and run under Turbo Pascal 5.0 and compatible compilers.
478 - The library, if compiled with gcc, doesn't produce any warning
479 with the `-ansi -pedantic' flags. Other compilers with better
480 checks may produce ANSI warnings that we'd be happy to now
483 (NOTE: It can of course be compiled by an `average' C compiler,
484 and even by a C++ one.)
486 - It only requires in its simplest form an ANSI libc to compile,
487 and no utilities other than a C pre-processor, compiler, and
490 - It is written in a modular fashion. Each module is called a
491 `component' and is made of two files in the C version (an
492 interface with suffix `.h' and body with suffix `.c' ) and one
493 file in the Pascal one.
495 - The very low-level components can be easily replaced by
496 system-specific ones that do not rely on the standard libc.
497 These components deal mainly with i/o, memory, and mutex
500 - A client application must only include one interface file named
501 `freetype.h' resp. `freetype.pas' to use the engine. All other
502 components should never be used or accessed by client
503 applications, and their name always begin with a `tt' prefix:
505 ttmemory, ttobjs, ttinterp, ttapi, etc.
507 - All configuration options are gathered in two files. One
508 contains the processor and OS specific configuration options,
509 while the other treats options that may be enabled or disabled
510 by the developer to test specific features (like assertions,
515 These restrictions only apply to the core engine. The package
516 that comes with it contains several test programs sources that
517 are much less portable, even if they present a modular model
518 inspired from the engine's layout.
520 The components currently found in the `lib' directory are:
522 -------- high-level interface ----------------------------------
524 freetype.h High-level API, to be used by client applications.
526 ttapi.c Implementation of the api found in `freetype.h'.
528 -------- configuration -----------------------------------------
530 ttconfig.h Engine configuration options. These are commented
531 and switched by hand by the developer. See
532 section 2 below for more info.
534 ft-conf.h Included by ttconfig.h, this file isn't part of
535 the `lib' directory, but depends on the target
536 environment. See section 2 blow for more info.
538 -------- definitions -------------------------------------------
540 tttypes.h The engine's internal types definitions.
541 tttables.h The TrueType tables definitions, per se the Specs.
542 tttags.h The TrueType table tags definitions.
543 tterror.[ch] The error and debugging component.
545 ttdebug.[ch] Only used by the debugger, should not be linked
546 into a release build.
548 ttcalc.[ch] Math component used to perform some computations
549 with an intermediate 64-bit precision.
551 -------- replaceable components --------------------------------
553 ttmemory.[ch] Memory component. This version uses the ANSI libc
554 but can be replaced easily by your own version.
556 ttfile.[ch] Stream i/o component. This version uses the ANSI
557 libc but can be replaced easily by your own
558 version. Compiled only if file memory mapping
559 isn't available on your system.
561 ttfile2.[ch] Unix-specific file memory mapping version of the
562 file component. It won't compile on other
563 systems. Usually results in much faster file
564 access (about 2x on a SCSI P166 system)
566 ttmutex.[ch] Generic mutex component. This version is a dummy
567 and should only be used for a single-thread build.
568 You _need_ to replace this component's body with
569 your own implementation to be able to build a
570 threaded version of the engine.
572 -------- data management ---------------------------------------
574 ttengine.h The engine instance record definition, root of all
577 ttlists.[ch] Generic lists manager.
578 ttcache.[ch] Generic cache manager.
580 ttobjs.[ch] The engine's object definitions and
581 implementations module contains structures,
582 constructors, destructors and methods for the
585 face, instance, glyph, execution_context
587 ttload.[c] The TrueType tables loader.
589 ttgload.[ch] The glyph loader. A component in itself, due to
590 the task's complexity.
592 ttindex.[ch] The character mapping to glyph index conversion
593 routines. Implements functions defined in
596 ttinterp.[ch] The TrueType instructions interpreter. Probably
597 the nicest source in this engine. Apparently,
598 many have failed to produce a comparable one due
599 to the very poorly written specification! It took
600 David Turner three months of his spare time to get
601 it working correctly! :-)
603 ttraster.[ch] The engine's second best piece. This is the
604 scan-line converter. Performs gray-level
605 rendering (also known as font-smoothing) as well
609 2. Configuration and Debugging
610 ------------------------------
612 As stated above, configuration depends on two files:
614 The environment configuration file `ft-conf.h':
616 This file contains the definitions of many configuration options
617 that are processor and OS-dependent. On Unix systems, this file
618 is generated automatically by the `configure' script that comes
619 with the released package.
621 On other environments, it is located in one of the architecture
622 directories found in `arch' (e.g. `arch/os2/ft-conf.h').
624 The path to this file should be passed to the compiler when
625 compiling _each_ component. (typically with an -I option).
627 The engine configuration file `ttconfig.h':
629 This file contains many configuration options that the developer
630 can turn on or off to experiment with some `features' of the
631 engine that are not part of its `simplest' form. The options
634 Note that the makefiles are compiler-specific.
636 It is possible to enable the dumping of debugging information by
637 compiling the components with the various debug macros. Please
638 consult the file `ttconfig.h' for details.
640 If you want to port the engine to another environment, you will
643 - Write a new `ft-conf.h' file for it. Just copy one of those
644 available and change the flags accordingly (they're all
647 - Replace the memory, file, and mutex components with yours,
648 presenting the same interface and behaviour.
650 - Eventually add some code in ttapi.c to initialize
651 system-specific data with the engine.
655 III. Usage conventions
656 ======================
662 Error handling has been refined to allow reentrant builds of the
663 library, available only in the C version. We thus have now two
664 different conventions.
668 A global error variable is used to report errors when they are
669 detected. All functions return a boolean that indicates success
670 or failure of the call. If an error occurs within a given
671 function, the latter must set the error variable and return
672 `false' (which means failure).
674 It is then possible to make several calls in a single `if'
677 if not Perform_Action_1( parms_of_1 ) or
678 not Perform_Action_2( parms_of_2 ) or
679 not Perform_Action_3( parms_of_3 ) then goto Fail;
681 where execution will jump to the `Fail' label whenever an error
682 occurs in the sequence of actions invoked in the condition.
686 Global errors are forbidden in re-entrant builds. Each function
687 thus returns directly an error code. A return value of 0 means
688 that no error occured, while a non-zero other value indicates a
691 This convention is more constraining than the one used in the
692 Pascal source. The above Pascal statement should be translated
693 into the following C fragment:
695 rc = Perform_Action_1( parms_of_1 );
699 rc = Perform_Action_2( parms_of_2 );
703 rc = Perform_Action_3( parms_of_3 );
707 which, while being equivalent, isn't as pleasantly readable.
709 One `simple' way to match the original fragment would be to
712 if ( (rc = Perform_Action_1( parms_of_1 )) ||
713 (rc = Perform_Action_2( parms_of_2 )) ||
714 (rc = Perform_Action_3( parms_of_3 )) )
717 which is better but uses assignments within expressions, which
718 are always delicate to manipulate in C (the risk of writing `=='
719 exists, and would go unnoticed by a compiler). Moreover, the
720 assignments are a bit redundant and don't express much things
721 about the actions performed (they only speak of the error
724 That is why some macros have been defined for the most
725 frequently used functions. They relate to low-level routines
726 that are called very often (mainly i/o, mutex, and memory
727 handling functions). Each macro produces an implicit assignment
728 to a variable called `error' and can be used instead as a simple
729 function call. Example:
731 if ( PERFORM_Action_1( parms_of_1 ) ||
732 PERFORM_Action_2( parms_of_2 ) ||
733 PERFORM_Action_3( parms_of_3 ) )
738 #define PERFORM_Action_1( parms_1 ) \
739 ( error = Perform_Action_1( parms_1 ) )
740 #define PERFORM_Action_2( parms_1 ) \
741 ( error = Perform_Action_2( parms_1 ) )
742 #define PERFORM_Action_3( parms_1 ) \
743 ( error = Perform_Action_3( parms_1 ) )
745 defined at the beginning of the file.
747 There, the developer only needs to define a local `error'
748 variable and use the macros directly in the code, without caring
749 about the actual error handling performed. Examples of such a
750 usage can be found in `ttload.c' and `ttgload.c'. Moreover, the
751 structure of the source files remain very similar, even though
752 the error handling is very different.
754 This convention is very close to the use of exceptions in
755 languages like C++, Pascal, Java, etc. where the developer
756 focuses on the actions to perform, and not on every little error
765 The engine uses `streams' to access the font files. A stream is
766 a structure defined in the `File' component containing
767 information used to access files through a system-specific i/o
770 The current implementation of the File component uses the ANSI
771 libc i/o functions. However, for the sake of embedding in light
772 systems and independence of a complete libc, it is possible to
773 re-implement the component for a specific system or OS, letting
776 A stream is of type `TStream' defined in the `TTObjs' interface.
777 The type is `(void*)' but actually points to a structure defined
778 within the File component.
780 A stream is created, managed and closed through the interface of
781 the `File' component. Several implementations of the same
782 component can co-exist, each taking advantage of specific system
783 features (the file `ttfile2.c' uses memory-mapped files for
784 instance) as long as it respects the interface.
788 TrueType is tied to the big-endian format, which implies that
789 reading shorts or longs from the font file may need conversions
790 depending on the target processor. To be able to easily detect
791 read errors and allow simple conversion calls or macros, the
792 engine is able to access a font file using `frames'.
794 A frame is simply a sequence of successive bytes taken from the
795 input file at the current position. A frame is pre-loaded into
796 memory by a `TT_Access_Frame()' call of the `File' component.
798 It is then possible to read all sizes of data through the
799 `Get_xxx()' functions, like Get_Byte(), Get_Short(),
802 When all important data is read, the frame can be released by a
803 call to `TT_Forget_Frame()'.
805 The benefits of frames are various. Consider these two
806 approaches at extracting values:
808 if ( (error = Read_Short( &var1 )) ||
809 (error = Read_Long ( &var2 )) ||
810 (error = Read_Long ( &var3 )) ||
811 (error = Read_Short( &var4 )) )
817 /* Read the next 16 bytes */
818 if ( (error = TT_Access_Frame( 16L )) )
819 return error; /* The Frame could not be read */
821 var1 = Get_Short(); /* extract values from the frame */
826 TT_Forget_Frame(); /* release the frame */
828 In the first case, there are four error assignments with four
829 checks of the file read. This unnecessarily increases the size
830 of the generated code. Moreover, you must be sure that `var1'
831 and `var4' are short variables, `var2' and `var3' long ones, if
832 you want to avoid bugs and/or compiler warnings.
834 In the second case, you perform only one check for the read, and
835 exit immediately on failure. Then the values are extracted from
836 the frame, as the result of function calls. This means that you
837 can use automatic type conversion; there is no problem if
838 e.g. `var1' and `var4' are longs, unlike previously.
840 On big-endian machines, the `Get_xxx()' functions could also be
841 simple macros that merely peek the values directly from the
842 frame, which speeds up and simplifies the generated code!
844 And finally, frames are ideal when you are using memory-mapped
845 files, as the frame is not really `pre-loaded' and never uses
848 IMPORTANT: You CANNOT nest several frame accesses. There is
849 only one frame available at a time for a specific
852 It is also the programmer's responsibility to never
853 extract more data than was pre-loaded in the frame!
854 (But you usually know how many values you want to
855 extract from the file before doing so).
861 The library now uses a component which interface is similar to
862 malloc()/free(). It defines only two functions.
866 To be used like malloc(), except that it returns an error code,
867 not an address. Its arguments are the size of the requested
868 block and the address of the target pointer to the `fresh'
869 block. An error code is returned in case of failure (and this
870 will also set the target pointer to NULL), 0 in case of success.
872 Alloc() should always respect the following rules:
874 - Requesting a block of size 0 should set the target pointer to
875 NULL and return no error code (i.e., return 0).
877 - The returned block is always zeroed. This is an important
878 assumption of other parts of the library.
880 If you wish to replace the memory component with your own,
881 please respect this behaviour, or your engine won't work
886 As you may have already guessed, Free() is Alloc()'s
887 counterpart. It takes as argument the _target pointer's
888 address_! You should _never_ pass the block's address directly,
889 i.e. the pointer, to Free().
891 Free should always respect the following rules:
893 - Calling it with a NULL argument, or the address of a NULL
894 pointer is valid, and should return success.
896 - The pointer is always set to NULL after the block's
897 deallocation. This is also an important assumption of many
898 other parts of the library.
900 If you wish to replace the memory component with your own,
901 please respect this behaviour, or your engine won't work
904 As the pointers addresses needed as arguments are typed `void**',
905 the component's interface also provides in the C version some
906 macros to help use them more easily, these are:
908 MEM_Alloc A version of Alloc that casts the argument pointer
911 ALLOC Same as MEM_Alloc, but with an assignment to a
912 variable called `error'. See the section `error
913 handling' above for more info on this.
915 FREE A version of Free() that casts the argument
916 pointer to (void**). There is currently no error
917 handling by with this macro.
919 MEM_Set An alias for `memset()', which can be easily
920 changed to anything else if you wish to use a
921 different memory manager than the functions
922 provided by the ANSI libc.
924 MEM_Copy An alias of `memcpy()' or `bcopy()' used to move
925 blocks of memory. You may change it to something
926 different if you wish to use something else that
930 4. Support for threaded environments
931 ------------------------------------
933 Support for threaded environments have been added to the C
934 sources, and only to these. It is now theorically possible to
935 build three distinct versions of the library:
939 The default build. This one doesn't known about different
940 threads. Hence, no code is generated to perform coherent data
945 With this build, several threads can use the library at the
946 same time. However, some key components can only be used by
947 one single thread at a time, and use a mutex to synchronize
948 access to their functions. These are mainly the file, raster
949 and interpreter components.
953 A re-entrant version is able to perform certain actions in
954 parallel that a thread-safe one cannot. This includes
955 accessing file(s) in parallel, interpreting different
956 instruction streams in parallel, or even scan-line converting
957 distinct glyphs at the same time.
959 Note that most of the latest changes in the engine are making the
960 distinction between the thread-safe and re-entrant builds thinner
963 There is a `ttmutex' component that presents a generic interface
964 to mutex operations. It should be re-implemented for each
970 --- end of convntns.txt ---