| |
| Conventions and Design in the FreeType 2 library |
| ------------------------------------------------ |
| |
| |
| Table of Contents |
| |
| Introduction |
| |
| I. Style and Formatting |
| |
| 1. Naming |
| 2. Declarations & Statements |
| 3. Blocks |
| 4. Macros |
| 5. Conventions |
| |
| II. Design conventions |
| |
| 1. Modularity and Components Layout |
| 2. Configuration and Debugging |
| |
| III. Usage conventions |
| |
| 1. Error handling |
| 2. Font File I/O |
| 3. Memory management |
| 4. Support for threaded environments |
| 5. Object Management |
| |
| |
| |
| Introduction |
| ============ |
| |
| This text introduces the many conventions used within the FreeType 2 |
| library code. Please read it before trying any modifications or |
| extensions of the source code. |
| |
| |
| |
| I. Style and Formatting |
| ======================= |
| |
| The following coding rules are extremely important to keep the |
| library's source code homogeneously. Keep in mind the following |
| points: |
| |
| - `Humans read source code, not machines' (Donald Knuth) |
| |
| The library source code should be as readable as possible, even |
| by non-C experts. With `readable', two things are meant: First, |
| the source code should be pleasant to the eye, with sufficient |
| whitespace and newlines, to not look like a boring stack of |
| characters stuck to each other. Second, the source should be |
| _expressive_ enough about its goals. This convention contains |
| rules that can help the source focus on its purpose, not on a |
| particular implementation. |
| |
| - `Paper is the _ultimate_ debugger' (David Turner :-) |
| |
| There is nothing like sheets of paper (and a large floor) to |
| help you understand the design of a library you're new to, or to |
| debug it. The formatting style presented here is targeted at |
| printing. For example, it is more than highly recommended to |
| never produce a source line that is wider than 78 columns. More |
| on this below. |
| |
| |
| 1. Naming |
| --------- |
| |
| a. Long and expressive labels |
| |
| Never hesitate to use long labels for your types, variables, |
| etc.! Except maybe for things like very trivial types, the |
| longest is the best, as it increases the source's |
| _expressiveness_. Never forget that the role of a label is to |
| express the `function' of the entity it represents, not its |
| implementation! |
| |
| NOTE: Hungarian notation is NOT expressive, as it sticks the |
| `type' of a variable to its name. A label like `usFoo' |
| rarely tells the use of the variable it represents. |
| |
| And the state of a variable (global, static, dynamic) |
| isn't helpful anymore. |
| |
| Conclusion: Avoid Hungarian Notation in FreeType 2. |
| |
| |
| When forging a name with several nouns |
| (e.g. `number-of-points'), use an uppercase letter for the first |
| letter of each word (except the first), like: |
| |
| numberOfPoints |
| |
| You are also welcome to introduce underscores `_' in your |
| labels, especially when sticking large nouns together, as it |
| `airs' the code greatly. E.g.: |
| |
| `numberOfPoints' or `number_Of_Points' |
| |
| `IncredibleFunction' or `Incredible_Function' |
| |
| And finally, always put a capital letter after an underscore, |
| except in variable labels that are all lowercase: |
| |
| `number_of_points' is OK for a variable (_all_ lowercase label) |
| |
| `incredible_function' is NOT for a function! |
| ^ ^ |
| |
| `Microsoft_windows' is a *shame*! |
| ^ ^ |
| |
| `Microsoft_Windows' isn't really better, but at least its a |
| ^ ^ correct function label within this |
| convention ;-) |
| |
| b. Data types |
| |
| Try to use C types to the very least! Rely on internally |
| defined equivalent types instead. For example, not all |
| compilers agree on the sign of `char'; the size of `int' is |
| platform-specific, etc. |
| |
| There are equivalents to the most common types in the |
| `fttypes.h' public header file, like `FT_Short', `FT_UShort', |
| etc. Using the internal types will guarantee that you won't |
| need to replace every occurence of `short' or whatever when |
| compiling on a weird platform or with a weird compiler, and |
| there are many more than you could think of... |
| |
| c. Functions |
| |
| The name of a function should always begin with a capital |
| letter, as lowercase first letters are reserved for variables. |
| The name of a function should be, again, _expressive_! Never |
| hesitate to put long function names in your code: It will make |
| the code much more readable. |
| |
| Expressiveness doesn't necessarily imply lengthiness though; for |
| instance, reading various data types from a file stream is |
| performed using the following functions defined in the |
| `ftstream.c' file of the `base' module: |
| |
| FT_Get_Char(), FT_Get_Short(), FT_Get_Long(), etc. |
| |
| Which is somewhat more readable than: |
| |
| cget, sget, usget, lget, etc. |
| |
| d. Variables |
| |
| Variable names (at least meant for the public interface) should |
| always begin with a lowercase letter. Lowercase first letters |
| are reserved for variables in this convention, as it has been |
| already explained above. You are still welcome to use long and |
| expressive variable names. |
| |
| Something like `numP' can express a number of pixels, porks, |
| pancakes, and much more... Something like `num_points' won't. |
| |
| Unfortunately (mostly due to the lazyness of the developers), |
| short variable names are still used in many parts of the |
| library. Volunteers are highly welcome to improve this... |
| |
| As a side note, a field name of a structure counts as a variable |
| name too. |
| |
| |
| 2. Declarations & Statements |
| ---------------------------- |
| |
| Try to align declarations and assignments in columns, if it proves |
| logically. For example (taken from `ttraster.c'): |
| |
| struct TProfile_ |
| { |
| FT_F26Dot6 X; /* current coordinate during sweep */ |
| PProfile link; /* link to next profile - various purpose */ |
| PLong offset; /* start of profile's data in render pool */ |
| Int flow; /* profile orientation: asc/descending */ |
| Long height; /* profile's height in scanlines */ |
| Long start; /* profile's starting scanline */ |
| |
| UShort countL; /* number of lines to step before this */ |
| /* profile becomes drawable */ |
| |
| PProfile next; /* next profile in same contour, used */ |
| /* during drop-out control */ |
| }; |
| |
| instead of |
| |
| struct TProfile_ |
| { |
| FT_F26Dot6 X; /* current coordinate during sweep */ |
| PProfile link; /* link to next profile - various purpose */ |
| PLong offset; /* start of profile's data in render pool */ |
| Int flow; /* profile orientation: asc/descending */ |
| Long height; /* profile's height in scanlines */ |
| Long start; /* profile's starting scanline */ |
| UShort countL; /* number of lines to step before this */ |
| /* profile becomes drawable */ |
| PProfile next; /* next profile in same contour, used */ |
| /* during drop-out control */ |
| }; |
| |
| This comes from the fact that you are more interested in the field |
| and its function than in its type. |
| |
| Or: |
| |
| x = i + 1; |
| y += j; |
| min = 100; |
| |
| instead of |
| |
| x=i+1; |
| y+=j; |
| min=100; |
| |
| And don't hesitate to separate blocks of declarations with |
| newlines to `distinguish' logical sections. |
| |
| E.g., taken from an old source file, in the declarations of the |
| CMap loader: |
| |
| long n, num_SH; |
| unsigned short u; |
| long off; |
| unsigned short l; |
| long num_Seg; |
| unsigned short* glArray; |
| long table_start; |
| int limit, i; |
| |
| TCMapDir cmap_dir; |
| TCMapDirEntry entry_; |
| PCMapTable Plcmt; |
| PCMap2SubHeader Plcmsub; |
| PCMap4 Plcm4; |
| PCMap4Segment segments; |
| |
| instead of |
| |
| long n, num_SH; |
| unsigned short u; |
| long off; |
| unsigned short l; |
| long num_Seg; |
| unsigned short *glArray; |
| long table_start; |
| int limit, i; |
| TCMapDir cmap_dir; |
| TCMapDirEntry entry_; |
| PCMapTable Plcmt; |
| PCMap2SubHeader Plcmsub; |
| PCMap4 Plcm4; |
| PCMap4Segment segments; |
| |
| |
| 3. Blocks |
| --------- |
| |
| Block separation is done with `{' and `}'. We do not use the K&R |
| convention which becomes only useful with an extensive use of |
| tabs. The `{' and its corresponding `}' should always be on the |
| same column. It makes it easier to separate a block from the rest |
| of the source, and it helps your _brain_ associate the accolades |
| easily (ask any Lisp programmer on the topic!). |
| |
| Use two spaces for the next indentation level. |
| |
| Never use tabs in FreeType 2 code; their widths may vary with |
| editors and systems. |
| |
| Example: |
| |
| if (condition_test) { |
| waow mamma; |
| I'm doing K&R format; |
| just like the Linux kernel; |
| } else { |
| This test failed poorly; |
| } |
| |
| should be rather formatted as |
| |
| if ( condition_test ) |
| { |
| This code isn't stuck to the condition; |
| read it on paper, you will find it more; |
| pleasant to the eye; |
| } |
| else |
| { |
| Of course, this is a matter of taste; |
| This is just the way it is in this convention; |
| and you should follow it to be homogenuous with; |
| the rest of the FreeType code; |
| } |
| |
| |
| 4. Macros |
| --------- |
| |
| Macros should be made of uppercase letters. If a macro label is |
| forged from several words, it is possible to only uppercasify the |
| first word, using an underscore to separate the nouns. This is |
| used in in some files for macros like |
| |
| GET_UShort(), USE_Stream(), etc. |
| |
| The role of macros used throughout the engine is explained later |
| in this document. |
| |
| |
| 5. Conventions |
| -------------- |
| |
| Currently, FreeType 2 source code uses the following formatting |
| rules: |
| |
| . The data type is separated with two spaces from the variable, |
| structure, or function name: |
| |
| const char foo; |
| |
| Usually, the `*' operator is concatenated to the data type: |
| |
| FT_Int* pointer; |
| |
| However, when declaring resp. defining an `output' parameter |
| (i.e. a pointer which will be assigned by the function), the |
| last `*' must be placed on the right in order to denote this, as |
| in: |
| |
| FT_New_Face( FT_Library library, |
| FT_Face *aface ); |
| |
| where the `*' is used to indicate that `aface' is returned. In |
| most cases, the name of such an output variable starts with `a' |
| or `an' (`aface' instead of `face', `anlru' instead of `lru', |
| etc.), following the English rules of the indefinite article. |
| |
| . As mentioned above, multiple declarations are vertically |
| aligned: |
| |
| FT_Short foo; |
| FT_Long bar; |
| FT_GlyphSlot slot; |
| |
| . Declarations are separated with two blank lines from the |
| following code. This intentionally disturbs the code flow to |
| make variable definitions more visible. |
| |
| { |
| char x, y; |
| |
| |
| x = 3; |
| y = 5; |
| } |
| |
| . An opening parenthesis follows a function directly without |
| space; after a built-in C keyword, one space is used: |
| |
| x = sin( y ); |
| y = sizeof ( long ); |
| |
| Except for casts, empty parameters, and the closing semicolon, |
| parentheses are surrounded with space: |
| |
| x = (char*)( foo + bar ); |
| y = rand(); |
| |
| . Binary operators are surrounded by spaces; unary operators have |
| no space after it: |
| |
| x = ( 3 + 4 ) / ( 7 - 2 ); |
| y = -( 3 + 4 ) * 7; |
| |
| . Array arguments are not surrounded by spaces: |
| |
| array[3] = array[1] + array[2]; |
| array[4] = array[1 + 3]; |
| |
| . Comma and semicolon have only space at the right side: |
| |
| if ( x = 0; x < y; x++, y-- ) |
| do_something(); |
| |
| Exception: |
| |
| for (;;) |
| { |
| ... |
| |
| . Don't use |
| |
| if ( x == y ) a = b; |
| |
| but |
| |
| if ( x == y ) |
| a = b; |
| |
| in general. |
| |
| . Preprocessor directives are never indented and always start in |
| the first column. |
| |
| . All function/structure/variable definitions start at column |
| three. |
| |
| . All full-line comments (except the header of a file) start at |
| column three (even comments for preprocessor directives). |
| |
| . Labels are sticking out two positions to the left: |
| |
| switch ( x ) |
| { |
| case 1: |
| do_something(); |
| break; |
| default: |
| do_nothing(); |
| break; |
| } |
| |
| |
| |
| II. Design Conventions |
| ====================== |
| |
| |
| 1. Modularity and Components Layout |
| ----------------------------------- |
| |
| The FreeType 2 engine has been designed with portability in mind. |
| This implies the ability to compile and run it on a great variety |
| of systems and weird environments, unlike many packages where the |
| word strictly means `runs on a bunch of Unix-like systems'. We |
| have thus decided to stick to the following restrictions: |
| |
| - The C version is written entirely in ANSI C. |
| |
| - The library, if compiled with gcc, doesn't produce any warning |
| with the `-ansi -pedantic' flags. Other compilers with better |
| checks may produce ANSI warnings -- please report. |
| |
| (NOTE: It can of course be compiled by an `average' C compiler, |
| and even by a C++ one.) |
| |
| - It only requires in its simplest form an ANSI libc to compile, |
| and no utilities other than a C preprocessor, compiler, and |
| linker. |
| |
| - It consists of modules, starting with a `base' module which |
| provides the API, some auxiliary modules used by the font |
| drivers, the font driver modules itself, and the rasterizer |
| modules. |
| |
| - The very low-level components can be easily replaced by |
| system-specific ones that do not rely on the standard libc. |
| These components deal mainly with i/o, memory, and mutex |
| operations. |
| |
| - A client application only needs to include one header file named |
| `freetype.h' to use the engine. Other public header files like |
| `ftglyph.h' or `ftimage.h' provide functional extensions. |
| |
| - All configuration options are gathered in two files, |
| `ftconfig.h' and `ftoption.h'. The former contains the |
| processor and OS specific configuration options, while the |
| latter treats options that may be enabled or disabled by the |
| user to enable and disable various features. |
| |
| |
| 2. Configuration and Debugging |
| ------------------------------ |
| |
| Configuration is covered by the `BUILD' documentation file. |
| |
| Debugging is controlled by two macros in `ftoption.h', |
| FT_DEBUG_LEVEL_ERROR and FT_DEBUG_LEVEL_TRACE; don't use them in |
| code to be released. Check the source code of the `ftview.c' |
| demonstration program (in the `ft2demos' package) how tracing can |
| be used and activated. |
| |
| |
| |
| III. Usage conventions |
| ====================== |
| |
| |
| 1. Error Handling |
| ----------------- |
| |
| Most functions directly return an error code. A return value of 0 |
| (FT_Err_Ok) means that no error occured, while a non-zero other |
| value indicates a failure of any kind. |
| |
| We use code like this in FreeType 2: |
| |
| if ( ( rc = Perform_Action_1( parms_of_1 ) ) || |
| ( rc = Perform_Action_2( parms_of_2 ) ) || |
| ( rc = Perform_Action_3( parms_of_3 ) ) ) |
| goto Fail; |
| |
| which is better but uses assignments within expressions, which are |
| always delicate to manipulate in C (the risk of writing `==' |
| exists, and would go unnoticed by a compiler). Moreover, the |
| assignments are a bit redundant and don't express much things |
| about the actions performed (they only speak of the error |
| management issue). |
| |
| That is why some macros have been defined for the most frequently |
| used functions. They relate to low-level routines that are called |
| very often (mainly i/o and memory handling functions). Each macro |
| produces an implicit assignment to a variable called `error' and |
| can be used instead as a simple function call. Example: |
| |
| if ( PERFORM_Action_1( parms_of_1 ) || |
| PERFORM_Action_2( parms_of_2 ) || |
| PERFORM_Action_3( parms_of_3 ) ) |
| goto Fail; |
| |
| with |
| |
| #define PERFORM_Action_1( parms_1 ) \ |
| ( error = Perform_Action_1( parms_1 ) ) |
| #define PERFORM_Action_2( parms_1 ) \ |
| ( error = Perform_Action_2( parms_1 ) ) |
| #define PERFORM_Action_3( parms_1 ) \ |
| ( error = Perform_Action_3( parms_1 ) ) |
| |
| defined in some header file. |
| |
| There, the developer only needs to define a local `error' variable |
| and use the macros directly in the code, without caring about the |
| actual error handling performed. Another advantage is that the |
| structure of source files remain very similar, even though the |
| error handling may be different. |
| |
| This convention is very close to the use of exceptions in |
| languages like C++, Pascal, Java, etc. where the developer |
| focuses on the actions to perform, and not on every little error |
| checking. |
| |
| |
| 2. Font File I/O |
| ---------------- |
| |
| a. Streams |
| |
| The engine uses `streams' to access the font files. A stream is |
| a structure containing information used to access files through |
| a system-specific i/o library. |
| |
| The default implementation of streams uses the ANSI libc i/o |
| functions. However, for the sake of embedding in light systems |
| and independence of a complete C library, it is possible to |
| re-implement the component for a specific system or OS, letting |
| it use system calls. |
| |
| b. Frames |
| |
| TrueType is tied to the big-endian format, which implies that |
| reading shorts or longs from the font file may need conversions |
| depending on the target processor. To be able to easily detect |
| read errors and allow simple conversion calls or macros, the |
| engine is able to access a font file using `frames'. |
| |
| A frame is simply a sequence of successive bytes taken from the |
| input file at the current position. A frame is pre-loaded into |
| memory by a call to the `ACCESS_Frame()' macro. |
| |
| It is then possible to read all sizes of data through the |
| `GET_xxx()' macros described above. |
| |
| When all important data is read, the frame can be released by a |
| call to `FORGET_Frame()'. |
| |
| The benefits of frames are various. Consider these two |
| approaches at extracting values: |
| |
| if ( ( error = Read_Short( &var1 ) ) || |
| ( error = Read_Long ( &var2 ) ) || |
| ( error = Read_Long ( &var3 ) ) || |
| ( error = Read_Short( &var4 ) ) ) |
| |
| return FAILURE; |
| |
| and |
| |
| /* Read the next 16 bytes */ |
| if ( ACCESS_Frame( 16L ) ) |
| return error; /* The Frame could not be read */ |
| |
| var1 = GET_Short(); /* extract values from the frame */ |
| var2 = GET_Long(); |
| var3 = GET_Long(); |
| var4 = GET_Short(); |
| |
| FORGET_Frame(); /* release the frame */ |
| |
| In the first case, there are four error assignments with four |
| checks of the file read. This unnecessarily increases the size |
| of the generated code. Moreover, you must be sure that `var1' |
| and `var4' are short variables, `var2' and `var3' long ones, if |
| you want to avoid bugs and/or compiler warnings. |
| |
| In the second case, you perform only one check for the read, and |
| exit immediately on failure. Then the values are extracted from |
| the frame, as the result of function calls. This means that you |
| can use automatic type conversion; there is no problem if |
| e.g. `var1' and `var4' are longs, unlike previously. |
| |
| Finally, frames are ideal when you are using memory-mapped |
| files, as the frame is not really `pre-loaded' and never uses |
| any `heap' space. |
| |
| IMPORTANT: You CANNOT nest several frame accesses. There is |
| only one frame available at a time for a specific |
| instance. |
| |
| It is also the programmer's responsibility to never |
| extract more data than was pre-loaded in the frame! |
| (But you usually know how many values you want to |
| extract from the file before doing so). |
| |
| |
| 3. Memory Management |
| -------------------- |
| |
| The library now has a component which uses an interface similar to |
| malloc()/free(). |
| |
| * FT_Alloc() |
| |
| To be used like malloc(), except that it returns an error code, |
| not an address. Its arguments are the size of the requested |
| block and the address of the target pointer to the `fresh' |
| block. An error code is returned in case of failure (and this |
| will also set the target pointer to NULL), 0 in case of success. |
| |
| FT_Alloc() internally calls the ft_alloc() function defined in |
| an FT_Memory object. All error checking is done by FT_Alloc() |
| itself so that ft_alloc() directly calls malloc(). |
| |
| * FT_Realloc() |
| |
| Similar to FT_Alloc(); it calls realloc() by default. |
| |
| * FT_Free() |
| |
| As you may have already guessed, FT_Free() is FT_Alloc()'s |
| counterpart. It takes as argument the _target pointer's |
| address_! You should _never_ pass the block's address directly, |
| i.e. the pointer, to FT_Free(). |
| |
| Similar to FT_Alloc(), FT_Free() does the necessary error |
| checking and calls free() by default. |
| |
| As the pointers addresses needed as arguments are typed `void**', |
| ftmemory.h provides some macros to help use the above functions |
| more easily, these are: |
| |
| MEM_Alloc() A version of FT_Alloc() that casts the argument |
| pointer to (void**). Similar functions are |
| MEM_Alloc_Array(), MEM_Realloc(), and |
| MEM_Realloc_Array() |
| |
| ALLOC() Same as MEM_Alloc(), but with an assignment to a |
| variable called `error'. See the section `error |
| handling' above for more info on this. Similar |
| functions are REALLOC(), ALLOC_ARRAY(), and |
| REALLOC_ARRAY(). |
| |
| FREE() A version of FT_Free() that casts the argument |
| pointer to (void**). |
| |
| MEM_Set() An alias for `memset()', which can be easily |
| changed to anything else if you wish to use a |
| different memory manager than the functions |
| provided by the ANSI libc. |
| |
| MEM_Copy() An alias of `memcpy()' or `bcopy()' used to move |
| blocks of memory. You may change it to something |
| different if necessary (e.g. not using libc). |
| |
| MEM_Move() An alias of `memmove().' Change its definition if |
| necessary. |
| |
| |
| 4. Support for threaded environments |
| ------------------------------------ |
| |
| Thread synchronisation has been dropped in FreeType 2. The |
| library is already re-entrant, and if you really need two threads |
| accessing the same FT_Library object, you should synchronize |
| access to it yourself with a simple mutex. |
| |
| |
| --- end of convntns.txt --- |