diff --git a/docs/tutorial/step1.html b/docs/tutorial/step1.html new file mode 100644 index 000000000..5b3a4c650 --- /dev/null +++ b/docs/tutorial/step1.html @@ -0,0 +1,936 @@ + + + + + + FreeType 2 Tutorial + + + + +

+ FreeType 2.0 Tutorial
+ Step 1 - simple glyph loading +

+ +

+ © 2000 David Turner + (david@freetype.org)
+ © 2000 The FreeType Development Team + (www.freetype.org) +

+ +
+ + +
+ +
+ +

+ Introduction +

+ +

This is the first section of the FreeType 2 tutorial. It will teach + you to do the following:

+ +
    +
  • initialise the library
  • +
  • open a font file by creating a new face object
  • +
  • select a character size in points or in pixels
  • +
  • load a single glyph image and convert it to a bitmap
  • +
  • render a very simple string of text
  • +
  • render a rotated string of text easily
  • +
+ +
+ +

+ 1. Header files +

+ +

To include the main FreeType header file, simply say

+ + +
+    #include <freetype/freetype.h>
+
+ +

in your application code. Note that other files are available in the + FreeType include directory, most of them being included by + "freetype.h". They will be described later in this + tutorial.

+ +
+ +

+ 2. Initialize the library +

+ +

Simply create a variable of type FT_Library named, for + example, library, and call the function + FT_Init_FreeType() as in

+ + +
+    #include <freetype/freetype.h>
+
+    FT_Library  library;
+
+    ...
+
+    {
+      ...
+      error = FT_Init_FreeType( &library );
+      if ( error )
+      {
+        ... an error occurred during library initialization ...
+      }
+    }
+
+ +

This function is in charge of the following:

+ +
    +
  • +

    Creating a new instance of the FreeType 2 library, and set + the handle library to it.

    +
  • +
  • +

    Load each modules that FreeType knows about in the library. + This means that by default, your new library object is able + to handle TrueType, Type 1, CID-keyed & OpenType/CFF fonts + gracefully.

    +
  • +
+ +

As you can see, the function returns an error code, like most others + in the FreeType API. An error code of 0 always means that + the operation was successful; otherwise, the value describes the error, + and library is set to NULL.

+ +
+ +

+ 3. Load a font face +

+ +

+ a. From a font file +

+ +

Create a new face object by calling FT_New_Face. + A face describes a given typeface and style. For example, + "Times New Roman Regular" and "Times New Roman Italic" correspond to + two different faces.

+ + +
+    FT_Library   library;   /* handle to library     */
+    FT_Face      face;      /* handle to face object */
+
+    error = FT_Init_FreeType( &library );
+    if ( error ) { ... }
+
+    error = FT_New_Face( library,
+                         "/usr/share/fonts/truetype/arial.ttf",
+                         0,
+                         &face );
+    if ( error == FT_Err_Unknown_File_Format )
+    {
+      ... the font file could be opened and read, but it appears
+      ... that its font format is unsupported
+    }
+    else if ( error )
+    {
+      ... another error code means that the font file could not
+      ... be opened or read, or simply that it is broken...
+    }
+
+ +

As you can certainly imagine, FT_New_Face opens a font + file, then tries to extract one face from it. Its parameters are

+ + + + + + + + + + + + + + + + + + +
+ library + +

handle to the FreeType library instance where the face object + is created

+
+ filepathname + +

the font file pathname (standard C string).

+
+ face_index + +

Certain font formats allow several font faces to be embedded + in a single file.

+ +

This index tells which face you want to load. An error will + be returned if its value is too large.

+ +

Index 0 always work though.

+
+ face + +

A pointer to the handle that will be set to describe + the new face object.

+ +

It is set to NULL in case of error.

+
+ +

To know how many faces a given font file contains, simply load its + first face (use face_index=0), then see the value of + face->num_faces which indicates how many faces are embedded + in the font file.

+ +

+ b. From memory +

+ +

In the case where you have already loaded the font file in memory, + you can similarly create a new face object for it by calling + FT_New_Memory_Face as in

+ + +
+    FT_Library   library;   /* handle to library     */
+    FT_Face      face;      /* handle to face object */
+
+    error = FT_Init_FreeType( &library );
+    if ( error ) { ... }
+
+    error = FT_New_Memory_Face( library,
+                                buffer,    /* first byte in memory */
+                                size,      /* size in bytes        */
+                                0,         /* face_index           */
+                                &face );
+    if ( error ) { ... }
+
+ +

As you can see, FT_New_Memory_Face() simply takes a + pointer to the font file buffer and its size in bytes instead of a + file pathname. Other than that, it has exactly the same semantics as + FT_New_Face().

+ +

+ c. From other sources (compressed files, network, etc.) +

+ +

There are cases where using a file pathname or preloading the file + in memory is simply not enough. With FreeType 2, it is possible + to provide your own implementation of i/o routines.

+ +

This is done through the FT_Open_Face() function, which + can be used to open a new font face with a custom input stream, select + a specific driver for opening, or even pass extra parameters to the + font driver when creating the object. We advise you to refer to the + FreeType 2 reference manual in order to learn how to use it.

+ +

Note that providing a custom stream might also be used to access a + TrueType font embedded in a Postscript Type 42 wrapper.

+ +
+ +

+ 4. Accessing face content +

+ +

A face object models all information that globally describes + the face. Usually, this data can be accessed directly by dereferencing + a handle, like

+ + + + + + + + + + + + + + + + + + + + + + +
+ face->num_glyphs + +

Gives the number of glyphs available in the font face. + A glyph is simply a character image. It doesn't necessarily + correspond to a character code though.

+
+ face->flags + +

A 32-bit integer containing bit flags used to describe some + face properties. For example, the flag + FT_FACE_FLAG_SCALABLE is used to indicate that the face's + font format is scalable and that glyph images can be rendered for + all character pixel sizes. For more information on face flags, + please read the FreeType 2 API Reference.

+
+ face->units_per_EM + +

This field is only valid for scalable formats (it is set to 0 + otherwise). It indicates the number of font units covered by the + EM.

+
+ face->num_fixed_sizes + +

This field gives the number of embedded bitmap strikes + in the current face. A strike is simply a series of + glyph images for a given character pixel size. For example, a + font face could include strikes for pixel sizes 10, 12 + and 14. Note that even scalable font formats can have + embedded bitmap strikes!

+
+ face->fixed_sizes + +

this is a pointer to an array of FT_Bitmap_Size + elements. Each FT_Bitmap_Size indicates the horizontal + and vertical pixel sizes for each of the strikes that are + present in the face.

+
+ +

For a complete listing of all face properties and fields, please read + the FreeType 2 API Reference.

+ +


+ +

+ 5. Setting the current pixel size +

+ +

FreeType 2 uses "size objects" to model all + information related to a given character size for a given face. + For example, a size object will hold the value of certain metrics + like the ascender or text height, expressed in 1/64th of a pixel, + for a character size of 12 points.

+ +

When the FT_New_Face function is called (or one of its + cousins), it automatically creates a new size object for + the returned face. This size object is directly accessible as + face->size.

+ +

NOTA BENE: a single face object can deal with one or more size + objects at a time, however, this is something that few programmers + really need to do. We have thus have decided to simplify the API for + the most common use (i.e. one size per face), while keeping this + feature available through additional fuctions.

+ +

When a new face object is created, its size object defaults to the + character size of 10 pixels (both horizontally and vertically) for + scalable formats. For fixed-sizes formats, the size is more or less + undefined, which is why you must set it before trying to load a + glyph.

+ +

To do that, simply call FT_Set_Char_Size(). Here is an + example where the character size is set to 16pt for a 300x300 dpi + device:

+ + +
+    error = FT_Set_Char_Size(
+              face,    /* handle to face object           */
+              0,       /* char_width in 1/64th of points  */
+              16*64,   /* char_height in 1/64th of points */
+              300,     /* horizontal device resolution    */
+              300 );   /* vertical device resolution      */
+
+ +

You will notice that:

+ +
    +
  • +

    The character width and heights are specified in 1/64th of + points. A point is a physical distance, equaling 1/72th + of an inch, it's not a pixel..

    +

  • +
  • +

    The horizontal and vertical device resolutions are expressed in + dots-per-inch, or dpi. You can use 72 or + 96 dpi for display devices like the screen. The resolution + is used to compute the character pixel size from the character + point size.

    +
  • +
  • +

    A value of 0 for the character width means "same as + character height", a value of 0 for the character height + means "same as character width". Otherwise, it is possible + to specify different char widths and heights.

    +
  • +
  • +

    Using a value of 0 for the horizontal or vertical resolution means + 72 dpi, which is the default.

    +
  • +
  • +

    The first argument is a handle to a face object, not a size + object. That's normal, and must be seen as a convenience.

    +
  • +
+ +

This function computes the character pixel size that corresponds to + the character width and height and device resolutions. However, if you + want to specify the pixel sizes yourself, you can simply call + FT_Set_Pixel_Sizes(), as in

+ + +
+    error = FT_Set_Pixel_Sizes(
+              face,   /* handle to face object            */
+              0,      /* pixel_width                      */
+              16 );   /* pixel_height                     */
+
+ +

This example will set the character pixel sizes to 16x16 pixels. + As previously, a value of 0 for one of the dimensions means + "same as the other".

+ +

Note that both functions return an error code. Usually, an error + occurs with a fixed-size font format (like FNT or PCF) when trying to + set the pixel size to a value that is not listed in the + face->fixed_sizes array.

+ +
+ +

+ 6. Loading a glyph image +

+ +

+ a. Converting a character code into a glyph index +

+ +

Usually, an application wants to load a glyph image based on its + character code, which is a unique value that defines the + character for a given encoding. For example, the character + code 65 represents the `A' in ASCII encoding.

+ +

A face object contains one or more tables, called + charmaps, that are used to convert character codes to glyph + indices. For example, most TrueType fonts contain two charmaps. One + is used to convert Unicode character codes to glyph indices, the other + is used to convert Apple Roman encoding into glyph indices. Such + fonts can then be used either on Windows (which uses Unicode) and + Macintosh (which uses Apple Roman, bwerk). Note also that a given + charmap might not map to all the glyphs present in the font.

+ +

By default, when a new face object is created, it lists all the + charmaps contained in the font face and selects the one that supports + Unicode character codes if it finds one. Otherwise, it tries to find + support for Latin-1, then ASCII.

+ +

We will describe later how to look for specific charmaps in a face. + For now, we will assume that the face contains at least a Unicode + charmap that was selected during FT_New_Face(). To convert a + Unicode character code to a font glyph index, we use + FT_Get_Char_Index() as in

+ + +
+    glyph_index = FT_Get_Char_Index( face, charcode );
+
+ +

This will look the glyph index corresponding to the given + charcode in the charmap that is currently selected for the + face. If charmap is selected, the function simply returns the + charcode.

+ +

Note that this is one of the rare FreeType functions that do not + return an error code. However, when a given character code has no + glyph image in the face, the value 0 is returned. By convention, + it always correspond to a special glyph image called the missing + glyph, which usually is represented as a box or a space.

+ +

+ b. Loading a glyph from the face +

+ +

Once you have a glyph index, you can load the corresponding glyph + image. The latter can be stored in various formats within the font file. + For fixed-size formats like FNT or PCF, each image is a bitmap. Scalable + formats like TrueType or Type 1 use vectorial shapes, named "outlines" + to describe each glyph. Some formats may have even more exotic ways + of representing glyph (e.g. MetaFont). Fortunately, FreeType 2 is + flexible enough to support any kind of glyph format through + a simple API.

+ +

The glyph image is always stored in a special object called a + glyph slot. As its name suggests, a glyph slot is simply a + container that is able to hold one glyph image at a time, be it a + bitmap, an outline, or something else. Each face object has a single + glyph slot object that can be accessed as + face->glyph.

+ +

Loading a glyph image into the slot is performed by calling + FT_Load_Glyph() as in

+ + +
+    error = FT_Load_Glyph( 
+              face,          /* handle to face object */
+              glyph_index,   /* glyph index           */
+              load_flags );  /* load flags, see below */
+
+ +

The load_flags value is a set of bit flags used to + indicate some special operations. The default value + FT_LOAD_DEFAULT is 0.

+ +

This function will try to load the corresponding glyph image + from the face. Basically, this means that:

+ +
    +
  • +

    If a bitmap is found for the corresponding glyph and pixel + size, it will be loaded into the slot (embedded bitmaps are always + favored over native image formats, because we assume that + they are higher-quality versions of the same glyph. This + can be ignored by using the FT_LOAD_NO_BITMAP flag)

    +
  • + +
  • +

    Otherwise, a native image for the glyph will be loaded. + It will also be scaled to the current pixel size, as + well as hinted for certain formats like TrueType and + Type1.

    +
  • +
+ +

The field glyph->format describe the format + used to store the glyph image in the slot. If it is not + ft_glyph_format_bitmap, one can immediately + convert it to a bitmap through FT_Render_Glyph, + as in:

+ + +
+   error = FT_Render_Glyph(
+                  face->glyph,      /* glyph slot  */
+                  render_mode );    /* render mode */
+      
+
+ +

The parameter render_mode is a set of bit flags used + to specify how to render the glyph image. Set it to 0 to render + a monochrome bitmap, or to ft_render_mode_antialias to + generate a high-quality (256 gray levels) anti-aliased bitmap + from the glyph image.

+ +

Once you have a bitmapped glyph image, you can access it directly + through glyph->bitmap (a simple bitmap descriptor), + and position it through glyph->bitmap_left and + glyph->bitmap_top.

+ +

Note that bitmap_left is the horizontal distance from the + current pen position to the left-most border of the glyph bitmap, + while bitmap_top is the vertical distance from the + pen position (on the baseline) to the top-most border of the + glyph bitmap. It is positive to indicate an upwards + distance.

+ +

The next section will detail the content of a glyph slot and + how to access specific glyph information (including metrics).

+ +

+ c. Using other charmaps +

+ +

As said before, when a new face object is created, it will look for + a Unicode, Latin-1, or ASCII charmap and select it. The currently + selected charmap is accessed via face->charmap. This + field is NULL when no charmap is selected, which typically happens + when you create a new FT_Face object from a font file that + doesn't contain an ASCII, Latin-1, or Unicode charmap (rare + stuff).

+ +

There are two ways to select a different charmap with FreeType 2. + The easiest is when the encoding you need already has a corresponding + enumeration defined in <freetype/freetype.h>, as + ft_encoding_big5. In this case, you can simply call + FT_Select_CharMap as in:

+ +
+    error = FT_Select_CharMap(
+                    face,                 /* target face object */
+                    ft_encoding_big5 );   /* encoding..         */
+      
+ +

Another way is to manually parse the list of charmaps for the + face, this is accessible through the fields + num_charmaps and charmaps + (notice the 's') of the face object. As you could expect, + the first is the number of charmaps in the face, while the + second is a table of pointers to the charmaps + embedded in the face.

+ +

Each charmap has a few visible fields used to describe it more + precisely. Mainly, one will look at + charmap->platform_id and + charmap->encoding_id that define a pair of + values that can be used to describe the charmap in a rather + generic way.

+ +

Each value pair corresponds to a given encoding. For example, + the pair (3,1) corresponds to Unicode. Their list is + defined in the TrueType specification but you can also use the + file <freetype/ftnameid.h> which defines several + helpful constants to deal with them..

+ +

To look for a specific encoding, you need to find a corresponding + value pair in the specification, then look for it in the charmaps + list. Don't forget that some encoding correspond to several + values pair (yes it's a real mess, but blame Apple and Microsoft + on such stupidity..). Here's some code to do it:

+ + +
+    FT_CharMap  found = 0;
+    FT_CharMap  charmap;
+    int         n;
+
+    for ( n = 0; n < face->num_charmaps; n++ )
+    {
+      charmap = face->charmaps[n];
+      if ( charmap->platform_id == my_platform_id &&
+           charmap->encoding_id == my_encoding_id )
+      {
+        found = charmap;
+        break;
+      }
+    }
+
+    if ( !found ) { ... }
+
+    /* now, select the charmap for the face object */
+    error = FT_Set_CharMap( face, found );
+    if ( error ) { ... }
+
+ +

Once a charmap has been selected, either through + FT_Select_CharMap or FT_Set_CharMap, + it is used by all subsequent calls to + FT_Get_Char_Index().

+ + +

+ d. Glyph Transforms: +

+ +

It is possible to specify an affine transformation to be applied + to glyph images when they're loaded. Of course, this will only + work for scalable (vectorial) font formats.

+ +

To do that, simply call FT_Set_Transform, as in:

+ +
+   error = FT_Set_Transform(
+                    face,       /* target face object    */
+                    &matrix,    /* pointer to 2x2 matrix */
+                    &delta );   /* pointer to 2d vector  */
+     
+ +

This function will set the current transform for a given face + object. Its second parameter is a pointer to a simple + FT_Matrix structure that describes a 2x2 affine matrix. + The third parameter is a pointer to a FT_Vector structure + that describe a simple 2d vector that is used to translate the + glyph image after the 2x2 transform.

+ +

Note that the matrix pointer can be set to NULL, (in which case + the identity transform will be used). Coefficients of the matrix + are otherwise in 16.16 fixed float units.

+ +

The vector pointer can also be set to NULL (in which case a delta + of (0,0) will be used). The vector coordinates are expressed in + 1/64th of a pixel (also known as 26.6 fixed floats).

+ +

NOTA BENE: The transform is applied every glyph that is loaded + through FT_Load_Glyph. Note that loading a glyph bitmap + with a non-trivial transform will produce an error..

+ +
+ +

+ 7. Simple Text Rendering: +

+ +

We will now present you with a very simple example used to render + a string of 8-bit Latin-1 text, assuming a face that contains a + Unicode charmap

+ +

The idea is to create a loop that will, on each iteration, load one + glyph image, convert it to an anti-aliased bitmap, draw it on the + target surface, then increment the current pen position

+ +

a. basic code :

+ +

The following code performs our simple text rendering with the + functions previously described.

+ +
+       FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+       int           pen_x, pen_y, n;
+
+       .. initialise library ..
+       .. create face object ..
+       .. set character size ..
+       
+       pen_x = 300;
+       pen_y = 200;
+       
+       for ( n = 0; n < num_chars; n++ )
+       {
+         FT_UInt  glyph_index;
+         
+         // retrieve glyph index from character code
+         glyph_index = FT_Get_Char_Index( face, text[n] );
+         
+         // load glyph image into the slot (erase previous one)
+         error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+         if (error) continue;  // ignore errors
+         
+         // convert to an anti-aliased bitmap
+         error = FT_Render_Glyph( face->glyph, ft_render_mode_antialias );
+         if (error) continue;
+         
+         // now, draw to our target surface
+         my_draw_bitmap( &slot->bitmap,
+                         pen_x + slot->bitmap_left,
+                         pen_y - slot->bitmap_top );
+                         
+         // increment pen position 
+         pen_x += slot->advance.x >> 6;
+         pen_y += slot->advance.y >> 6;   // unuseful for now..
+       }
+    
+ +

This code needs a few explanations:

+
    +
  • + we define a handle named slot that points to the + face object's glyph slot. (the type FT_GlyphSlot is + a pointer). That's a convenience to avoid using + face->glyph->XXX every time. +

  • + +
  • + we increment the pen position with the vector slot->advance, + which correspond to the glyph's advance width (also known + as its escapement). The advance vector is expressed in + 64/th of pixels, and is truncated to integer pixels on each + iteration.

    +

  • + +
  • + The function my_draw_bitmap is not part of FreeType, but + must be provided by the application to draw the bitmap to the target + surface. In this example, it takes a pointer to a FT_Bitmap descriptor + and the position of its top-left corner as arguments. +

  • + +
  • + The value of slot->bitmap_top is positive for an + upwards vertical distance. Assuming that the coordinates + taken by my_draw_bitmap use the opposite convention + (increasing Y corresponds to downwards scanlines), we substract + it to pen_y, instead of adding it.. +

  • + +
+ +

b. refined code:

+ +

The following code is a refined version of the example above. It + uses features and functions of FreeType 2 that have not yet been + introduced, and they'll be explained below:

+ +
+       FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+       FT_UInt       glyph_index;
+       int           pen_x, pen_y, n;
+
+       .. initialise library ..
+       .. create face object ..
+       .. set character size ..
+       
+       pen_x = 300;
+       pen_y = 200;
+       
+       for ( n = 0; n < num_chars; n++ )
+       {
+         // load glyph image into the slot (erase previous one)
+         error = FT_Load_Char( face, text[n], FT_LOAD_RENDER | FT_LOAD_ANTI_ALIAS );
+         if (error) continue;  // ignore errors
+         
+         // now, draw to our target surface
+         my_draw_bitmap( &slot->bitmap,
+                         pen_x + slot->bitmap_left,
+                         pen_y - slot->bitmap_top );
+                         
+         // increment pen position 
+         pen_x += slot->advance.x >> 6;
+       }
+    
+ +

We've reduced the size of our code, but it does exactly the same thing, + as:

+ +
    +
  • + We use the function FT_Load_Char instead of + FT_Load_Glyph. As you probably imagine, it's equivalent + to calling FT_Get_Char_Index then FT_Get_Load_Glyph. +

  • + +
  • + We do not use FT_LOAD_DEFAULT for the loading mode, but + the two bit flags FT_LOAD_RENDER and + FT_LOAD_ANTI_ALIAS. The first flag indicates that + the glyph image must be immediately converted to a bitmap, and + the second that it should be renderer anti-aliased. Of course, this + is also a shortcut that avoids calling FT_Render_Glyph + explicitely but is strictly equivalent.. +

  • +
+ +

c. more advanced rendering:

+ +

Let's try to render transformed text now (for example through a + rotation). We can do this using FT_Set_Transform. Here's + how to do it:

+ +
+       FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+       FT_Matrix     matrix;              // transformation matrix
+       FT_UInt       glyph_index;
+       FT_Vector     pen;                 // untransformed origin
+       int           pen_x, pen_y, n;
+
+       .. initialise library ..
+       .. create face object ..
+       .. set character size ..
+
+       // set up matrix
+       matrix.xx = (FT_Fixed)( cos(angle)*0x10000);
+       matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);
+       matrix.yx = (FT_Fixed)( sin(angle)*0x10000);
+       matrix.yy = (FT_Fixed)( cos(angle)*0x10000);
+              
+       // the pen position in 26.6 cartesian space coordinates
+       pen.x = 300 * 64;
+       pen.y = ( my_target_height - 200 ) * 64;
+       
+       for ( n = 0; n < num_chars; n++ )
+       {
+         // set transform
+         FT_Set_Transform( face, &matrix, &pen );
+         
+         // load glyph image into the slot (erase previous one)
+         error = FT_Load_Char( face, text[n], FT_LOAD_RENDER | FT_LOAD_ANTI_ALIAS );
+         if (error) continue;  // ignore errors
+         
+         // now, draw to our target surface (convert position)
+         my_draw_bitmap( &slot->bitmap,
+                         slot->bitmap_left,
+                         my_target_height - slot->bitmap_top );
+                         
+         // increment pen position 
+         pen.x += slot->advance.x;
+         pen.y += slot->advance.y;
+       }
+    
+ +

You'll notice that:

+ +
    +
  • + we now use a vector, of type FT_Vector to store the pen + position, with coordinates expressed as 1/64th of pixels, hence + a multiplication. The position is expressed in cartesian space. +

  • + +
  • + glyph images are always loaded, transformed and described in the + cartesian coordinate system in FreeType (which means that + increasing Y corresponds to upper scanlines), unlike the system + typically used for bitmaps (where the top-most scanline has + coordinate 0). We must thus convert between the two systems + when we define the pen position, and when we compute the top-left + position of the bitmap. +

  • + +
  • + we set the transform on each glyph, to indicate the rotation + matrix, as well as a delta that will move the transformed image + to the current pen position (in cartesian space, not bitmap space). +

  • + +
  • + the advance is always returned transformed, which is why it can + be directly added to the current pen position. Note that it is + not rounded this time. +

  • + +
+ +

It is important to note that, while this example is a bit more + complex than the previous one, it is strictly equivalent + for the case where the transform is the identity.. Hence it can + be used as a replacement (but a more powerful one).

+ +
+ +

+ Conclusion +

+ +

In this first section, you have learned the basics of FreeType 2, + as well as sufficient knowledge to know how to render rotated text. + Woww ! Congratulations..

+ +

The next section will dive into more details of the API in order + to let you access glyph metrics and images directly, as well as + how to deal with scaling, hinting, kerning, etc..

+ +

The third section will discuss issues like modules, caching and a + few other advanced topics like how to use multiple size objects + with a single face. +

+ +
+
+ + +