afcjk.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  afcjk.c                                                                */
00004 /*                                                                         */
00005 /*    Auto-fitter hinting routines for CJK script (body).                  */
00006 /*                                                                         */
00007 /*  Copyright 2006, 2007, 2008, 2009 by                                    */
00008 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00009 /*                                                                         */
00010 /*  This file is part of the FreeType project, and may only be used,       */
00011 /*  modified, and distributed under the terms of the FreeType project      */
00012 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00013 /*  this file you indicate that you have read the license and              */
00014 /*  understand and accept it fully.                                        */
00015 /*                                                                         */
00016 /***************************************************************************/
00017 
00018   /*
00019    *  The algorithm is based on akito's autohint patch, available here:
00020    *
00021    *  http://www.kde.gr.jp/~akito/patch/freetype2/
00022    *
00023    */
00024 
00025 #include "aftypes.h"
00026 #include "aflatin.h"
00027 
00028 
00029 #ifdef AF_CONFIG_OPTION_CJK
00030 
00031 #include "afcjk.h"
00032 #include "aferrors.h"
00033 
00034 
00035 #ifdef AF_USE_WARPER
00036 #include "afwarp.h"
00037 #endif
00038 
00039 
00040   /*************************************************************************/
00041   /*************************************************************************/
00042   /*****                                                               *****/
00043   /*****              C J K   G L O B A L   M E T R I C S              *****/
00044   /*****                                                               *****/
00045   /*************************************************************************/
00046   /*************************************************************************/
00047 
00048   FT_LOCAL_DEF( FT_Error )
00049   af_cjk_metrics_init( AF_LatinMetrics  metrics,
00050                        FT_Face          face )
00051   {
00052     FT_CharMap  oldmap = face->charmap;
00053 
00054 
00055     metrics->units_per_em = face->units_per_EM;
00056 
00057     /* TODO are there blues? */
00058 
00059     if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
00060       face->charmap = NULL;
00061     else
00062     {
00063       /* latin's version would suffice */
00064       af_latin_metrics_init_widths( metrics, face, 0x7530 );
00065       af_latin_metrics_check_digits( metrics, face );
00066     }
00067 
00068     FT_Set_Charmap( face, oldmap );
00069 
00070     return AF_Err_Ok;
00071   }
00072 
00073 
00074   static void
00075   af_cjk_metrics_scale_dim( AF_LatinMetrics  metrics,
00076                             AF_Scaler        scaler,
00077                             AF_Dimension     dim )
00078   {
00079     AF_LatinAxis  axis;
00080 
00081 
00082     axis = &metrics->axis[dim];
00083 
00084     if ( dim == AF_DIMENSION_HORZ )
00085     {
00086       axis->scale = scaler->x_scale;
00087       axis->delta = scaler->x_delta;
00088     }
00089     else
00090     {
00091       axis->scale = scaler->y_scale;
00092       axis->delta = scaler->y_delta;
00093     }
00094   }
00095 
00096 
00097   FT_LOCAL_DEF( void )
00098   af_cjk_metrics_scale( AF_LatinMetrics  metrics,
00099                         AF_Scaler        scaler )
00100   {
00101     metrics->root.scaler = *scaler;
00102 
00103     af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
00104     af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
00105   }
00106 
00107 
00108   /*************************************************************************/
00109   /*************************************************************************/
00110   /*****                                                               *****/
00111   /*****              C J K   G L Y P H   A N A L Y S I S              *****/
00112   /*****                                                               *****/
00113   /*************************************************************************/
00114   /*************************************************************************/
00115 
00116   static FT_Error
00117   af_cjk_hints_compute_segments( AF_GlyphHints  hints,
00118                                  AF_Dimension   dim )
00119   {
00120     AF_AxisHints  axis          = &hints->axis[dim];
00121     AF_Segment    segments      = axis->segments;
00122     AF_Segment    segment_limit = segments + axis->num_segments;
00123     FT_Error      error;
00124     AF_Segment    seg;
00125 
00126 
00127     error = af_latin_hints_compute_segments( hints, dim );
00128     if ( error )
00129       return error;
00130 
00131     /* a segment is round if it doesn't have successive */
00132     /* on-curve points.                                 */
00133     for ( seg = segments; seg < segment_limit; seg++ )
00134     {
00135       AF_Point  pt   = seg->first;
00136       AF_Point  last = seg->last;
00137       AF_Flags  f0   = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
00138       AF_Flags  f1;
00139 
00140 
00141       seg->flags &= ~AF_EDGE_ROUND;
00142 
00143       for ( ; pt != last; f0 = f1 )
00144       {
00145         pt = pt->next;
00146         f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
00147 
00148         if ( !f0 && !f1 )
00149           break;
00150 
00151         if ( pt == last )
00152           seg->flags |= AF_EDGE_ROUND;
00153       }
00154     }
00155 
00156     return AF_Err_Ok;
00157   }
00158 
00159 
00160   static void
00161   af_cjk_hints_link_segments( AF_GlyphHints  hints,
00162                               AF_Dimension   dim )
00163   {
00164     AF_AxisHints  axis          = &hints->axis[dim];
00165     AF_Segment    segments      = axis->segments;
00166     AF_Segment    segment_limit = segments + axis->num_segments;
00167     AF_Direction  major_dir     = axis->major_dir;
00168     AF_Segment    seg1, seg2;
00169     FT_Pos        len_threshold;
00170     FT_Pos        dist_threshold;
00171 
00172 
00173     len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
00174 
00175     dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
00176                                                   : hints->y_scale;
00177     dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
00178 
00179     /* now compare each segment to the others */
00180     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
00181     {
00182       /* the fake segments are for metrics hinting only */
00183       if ( seg1->first == seg1->last )
00184         continue;
00185 
00186       if ( seg1->dir != major_dir )
00187         continue;
00188 
00189       for ( seg2 = segments; seg2 < segment_limit; seg2++ )
00190         if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
00191         {
00192           FT_Pos  dist = seg2->pos - seg1->pos;
00193 
00194 
00195           if ( dist < 0 )
00196             continue;
00197 
00198           {
00199             FT_Pos  min = seg1->min_coord;
00200             FT_Pos  max = seg1->max_coord;
00201             FT_Pos  len;
00202 
00203 
00204             if ( min < seg2->min_coord )
00205               min = seg2->min_coord;
00206 
00207             if ( max > seg2->max_coord )
00208               max = seg2->max_coord;
00209 
00210             len = max - min;
00211             if ( len >= len_threshold )
00212             {
00213               if ( dist * 8 < seg1->score * 9                        &&
00214                    ( dist * 8 < seg1->score * 7 || seg1->len < len ) )
00215               {
00216                 seg1->score = dist;
00217                 seg1->len   = len;
00218                 seg1->link  = seg2;
00219               }
00220 
00221               if ( dist * 8 < seg2->score * 9                        &&
00222                    ( dist * 8 < seg2->score * 7 || seg2->len < len ) )
00223               {
00224                 seg2->score = dist;
00225                 seg2->len   = len;
00226                 seg2->link  = seg1;
00227               }
00228             }
00229           }
00230         }
00231     }
00232 
00233     /*
00234      *  now compute the `serif' segments
00235      *
00236      *  In Hanzi, some strokes are wider on one or both of the ends.
00237      *  We either identify the stems on the ends as serifs or remove
00238      *  the linkage, depending on the length of the stems.
00239      *
00240      */
00241 
00242     {
00243       AF_Segment  link1, link2;
00244 
00245 
00246       for ( seg1 = segments; seg1 < segment_limit; seg1++ )
00247       {
00248         link1 = seg1->link;
00249         if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
00250           continue;
00251 
00252         if ( seg1->score >= dist_threshold )
00253           continue;
00254 
00255         for ( seg2 = segments; seg2 < segment_limit; seg2++ )
00256         {
00257           if ( seg2->pos > seg1->pos || seg1 == seg2 )
00258             continue;
00259 
00260           link2 = seg2->link;
00261           if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
00262             continue;
00263 
00264           if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
00265             continue;
00266 
00267           if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
00268             continue;
00269 
00270           /* seg2 < seg1 < link1 < link2 */
00271 
00272           if ( seg1->len >= seg2->len * 3 )
00273           {
00274             AF_Segment  seg;
00275 
00276 
00277             for ( seg = segments; seg < segment_limit; seg++ )
00278             {
00279               AF_Segment  link = seg->link;
00280 
00281 
00282               if ( link == seg2 )
00283               {
00284                 seg->link  = 0;
00285                 seg->serif = link1;
00286               }
00287               else if ( link == link2 )
00288               {
00289                 seg->link  = 0;
00290                 seg->serif = seg1;
00291               }
00292             }
00293           }
00294           else
00295           {
00296             seg1->link = link1->link = 0;
00297 
00298             break;
00299           }
00300         }
00301       }
00302     }
00303 
00304     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
00305     {
00306       seg2 = seg1->link;
00307 
00308       if ( seg2 )
00309       {
00310         seg2->num_linked++;
00311         if ( seg2->link != seg1 )
00312         {
00313           seg1->link = 0;
00314 
00315           if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
00316             seg1->serif = seg2->link;
00317           else
00318             seg2->num_linked--;
00319         }
00320       }
00321     }
00322   }
00323 
00324 
00325   static FT_Error
00326   af_cjk_hints_compute_edges( AF_GlyphHints  hints,
00327                               AF_Dimension   dim )
00328   {
00329     AF_AxisHints  axis   = &hints->axis[dim];
00330     FT_Error      error  = AF_Err_Ok;
00331     FT_Memory     memory = hints->memory;
00332     AF_LatinAxis  laxis  = &((AF_LatinMetrics)hints->metrics)->axis[dim];
00333 
00334     AF_Segment    segments      = axis->segments;
00335     AF_Segment    segment_limit = segments + axis->num_segments;
00336     AF_Segment    seg;
00337 
00338     FT_Fixed      scale;
00339     FT_Pos        edge_distance_threshold;
00340 
00341 
00342     axis->num_edges = 0;
00343 
00344     scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
00345                                          : hints->y_scale;
00346 
00347     /*********************************************************************/
00348     /*                                                                   */
00349     /* We begin by generating a sorted table of edges for the current    */
00350     /* direction.  To do so, we simply scan each segment and try to find */
00351     /* an edge in our table that corresponds to its position.            */
00352     /*                                                                   */
00353     /* If no edge is found, we create and insert a new edge in the       */
00354     /* sorted table.  Otherwise, we simply add the segment to the edge's */
00355     /* list which is then processed in the second step to compute the    */
00356     /* edge's properties.                                                */
00357     /*                                                                   */
00358     /* Note that the edges table is sorted along the segment/edge        */
00359     /* position.                                                         */
00360     /*                                                                   */
00361     /*********************************************************************/
00362 
00363     edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
00364                                          scale );
00365     if ( edge_distance_threshold > 64 / 4 )
00366       edge_distance_threshold = FT_DivFix( 64 / 4, scale );
00367     else
00368       edge_distance_threshold = laxis->edge_distance_threshold;
00369 
00370     for ( seg = segments; seg < segment_limit; seg++ )
00371     {
00372       AF_Edge  found = 0;
00373       FT_Pos   best  = 0xFFFFU;
00374       FT_Int   ee;
00375 
00376 
00377       /* look for an edge corresponding to the segment */
00378       for ( ee = 0; ee < axis->num_edges; ee++ )
00379       {
00380         AF_Edge  edge = axis->edges + ee;
00381         FT_Pos   dist;
00382 
00383 
00384         if ( edge->dir != seg->dir )
00385           continue;
00386 
00387         dist = seg->pos - edge->fpos;
00388         if ( dist < 0 )
00389           dist = -dist;
00390 
00391         if ( dist < edge_distance_threshold && dist < best )
00392         {
00393           AF_Segment  link = seg->link;
00394 
00395 
00396           /* check whether all linked segments of the candidate edge */
00397           /* can make a single edge.                                 */
00398           if ( link )
00399           {
00400             AF_Segment  seg1 = edge->first;
00401             AF_Segment  link1;
00402             FT_Pos      dist2 = 0;
00403 
00404 
00405             do
00406             {
00407               link1 = seg1->link;
00408               if ( link1 )
00409               {
00410                 dist2 = AF_SEGMENT_DIST( link, link1 );
00411                 if ( dist2 >= edge_distance_threshold )
00412                   break;
00413               }
00414 
00415             } while ( ( seg1 = seg1->edge_next ) != edge->first );
00416 
00417             if ( dist2 >= edge_distance_threshold )
00418               continue;
00419           }
00420 
00421           best  = dist;
00422           found = edge;
00423         }
00424       }
00425 
00426       if ( !found )
00427       {
00428         AF_Edge  edge;
00429 
00430 
00431         /* insert a new edge in the list and */
00432         /* sort according to the position    */
00433         error = af_axis_hints_new_edge( axis, seg->pos,
00434                                         (AF_Direction)seg->dir,
00435                                         memory, &edge );
00436         if ( error )
00437           goto Exit;
00438 
00439         /* add the segment to the new edge's list */
00440         FT_ZERO( edge );
00441 
00442         edge->first    = seg;
00443         edge->last     = seg;
00444         edge->fpos     = seg->pos;
00445         edge->opos     = edge->pos = FT_MulFix( seg->pos, scale );
00446         seg->edge_next = seg;
00447         edge->dir      = seg->dir;
00448       }
00449       else
00450       {
00451         /* if an edge was found, simply add the segment to the edge's */
00452         /* list                                                       */
00453         seg->edge_next         = found->first;
00454         found->last->edge_next = seg;
00455         found->last            = seg;
00456       }
00457     }
00458 
00459     /*********************************************************************/
00460     /*                                                                   */
00461     /* Good, we now compute each edge's properties according to segments */
00462     /* found on its position.  Basically, these are as follows.          */
00463     /*                                                                   */
00464     /*  - edge's main direction                                          */
00465     /*  - stem edge, serif edge or both (which defaults to stem then)    */
00466     /*  - rounded edge, straight or both (which defaults to straight)    */
00467     /*  - link for edge                                                  */
00468     /*                                                                   */
00469     /*********************************************************************/
00470 
00471     /* first of all, set the `edge' field in each segment -- this is     */
00472     /* required in order to compute edge links                           */
00473     /*                                                                   */
00474     /* Note that removing this loop and setting the `edge' field of each */
00475     /* segment directly in the code above slows down execution speed for */
00476     /* some reasons on platforms like the Sun.                           */
00477 
00478     {
00479       AF_Edge  edges      = axis->edges;
00480       AF_Edge  edge_limit = edges + axis->num_edges;
00481       AF_Edge  edge;
00482 
00483 
00484       for ( edge = edges; edge < edge_limit; edge++ )
00485       {
00486         seg = edge->first;
00487         if ( seg )
00488           do
00489           {
00490             seg->edge = edge;
00491             seg       = seg->edge_next;
00492 
00493           } while ( seg != edge->first );
00494       }
00495 
00496       /* now compute each edge properties */
00497       for ( edge = edges; edge < edge_limit; edge++ )
00498       {
00499         FT_Int  is_round    = 0;  /* does it contain round segments?    */
00500         FT_Int  is_straight = 0;  /* does it contain straight segments? */
00501 
00502 
00503         seg = edge->first;
00504 
00505         do
00506         {
00507           FT_Bool  is_serif;
00508 
00509 
00510           /* check for roundness of segment */
00511           if ( seg->flags & AF_EDGE_ROUND )
00512             is_round++;
00513           else
00514             is_straight++;
00515 
00516           /* check for links -- if seg->serif is set, then seg->link must */
00517           /* be ignored                                                   */
00518           is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
00519 
00520           if ( seg->link || is_serif )
00521           {
00522             AF_Edge     edge2;
00523             AF_Segment  seg2;
00524 
00525 
00526             edge2 = edge->link;
00527             seg2  = seg->link;
00528 
00529             if ( is_serif )
00530             {
00531               seg2  = seg->serif;
00532               edge2 = edge->serif;
00533             }
00534 
00535             if ( edge2 )
00536             {
00537               FT_Pos  edge_delta;
00538               FT_Pos  seg_delta;
00539 
00540 
00541               edge_delta = edge->fpos - edge2->fpos;
00542               if ( edge_delta < 0 )
00543                 edge_delta = -edge_delta;
00544 
00545               seg_delta = AF_SEGMENT_DIST( seg, seg2 );
00546 
00547               if ( seg_delta < edge_delta )
00548                 edge2 = seg2->edge;
00549             }
00550             else
00551               edge2 = seg2->edge;
00552 
00553             if ( is_serif )
00554             {
00555               edge->serif   = edge2;
00556               edge2->flags |= AF_EDGE_SERIF;
00557             }
00558             else
00559               edge->link  = edge2;
00560           }
00561 
00562           seg = seg->edge_next;
00563 
00564         } while ( seg != edge->first );
00565 
00566         /* set the round/straight flags */
00567         edge->flags = AF_EDGE_NORMAL;
00568 
00569         if ( is_round > 0 && is_round >= is_straight )
00570           edge->flags |= AF_EDGE_ROUND;
00571 
00572         /* get rid of serifs if link is set                 */
00573         /* XXX: This gets rid of many unpleasant artefacts! */
00574         /*      Example: the `c' in cour.pfa at size 13     */
00575 
00576         if ( edge->serif && edge->link )
00577           edge->serif = 0;
00578       }
00579     }
00580 
00581   Exit:
00582     return error;
00583   }
00584 
00585 
00586   static FT_Error
00587   af_cjk_hints_detect_features( AF_GlyphHints  hints,
00588                                 AF_Dimension   dim )
00589   {
00590     FT_Error  error;
00591 
00592 
00593     error = af_cjk_hints_compute_segments( hints, dim );
00594     if ( !error )
00595     {
00596       af_cjk_hints_link_segments( hints, dim );
00597 
00598       error = af_cjk_hints_compute_edges( hints, dim );
00599     }
00600     return error;
00601   }
00602 
00603 
00604   FT_LOCAL_DEF( FT_Error )
00605   af_cjk_hints_init( AF_GlyphHints    hints,
00606                      AF_LatinMetrics  metrics )
00607   {
00608     FT_Render_Mode  mode;
00609     FT_UInt32       scaler_flags, other_flags;
00610 
00611 
00612     af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
00613 
00614     /*
00615      *  correct x_scale and y_scale when needed, since they may have
00616      *  been modified af_cjk_scale_dim above
00617      */
00618     hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
00619     hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
00620     hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
00621     hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
00622 
00623     /* compute flags depending on render mode, etc. */
00624     mode = metrics->root.scaler.render_mode;
00625 
00626 #ifdef AF_USE_WARPER
00627     if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
00628       metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
00629 #endif
00630 
00631     scaler_flags = hints->scaler_flags;
00632     other_flags  = 0;
00633 
00634     /*
00635      *  We snap the width of vertical stems for the monochrome and
00636      *  horizontal LCD rendering targets only.
00637      */
00638     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
00639       other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
00640 
00641     /*
00642      *  We snap the width of horizontal stems for the monochrome and
00643      *  vertical LCD rendering targets only.
00644      */
00645     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
00646       other_flags |= AF_LATIN_HINTS_VERT_SNAP;
00647 
00648     /*
00649      *  We adjust stems to full pixels only if we don't use the `light' mode.
00650      */
00651     if ( mode != FT_RENDER_MODE_LIGHT )
00652       other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
00653 
00654     if ( mode == FT_RENDER_MODE_MONO )
00655       other_flags |= AF_LATIN_HINTS_MONO;
00656 
00657     scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE;
00658 
00659     hints->scaler_flags = scaler_flags;
00660     hints->other_flags  = other_flags;
00661 
00662     return 0;
00663   }
00664 
00665 
00666   /*************************************************************************/
00667   /*************************************************************************/
00668   /*****                                                               *****/
00669   /*****          C J K   G L Y P H   G R I D - F I T T I N G          *****/
00670   /*****                                                               *****/
00671   /*************************************************************************/
00672   /*************************************************************************/
00673 
00674   /* snap a given width in scaled coordinates to one of the */
00675   /* current standard widths                                */
00676 
00677   static FT_Pos
00678   af_cjk_snap_width( AF_Width  widths,
00679                      FT_Int    count,
00680                      FT_Pos    width )
00681   {
00682     int     n;
00683     FT_Pos  best      = 64 + 32 + 2;
00684     FT_Pos  reference = width;
00685     FT_Pos  scaled;
00686 
00687 
00688     for ( n = 0; n < count; n++ )
00689     {
00690       FT_Pos  w;
00691       FT_Pos  dist;
00692 
00693 
00694       w = widths[n].cur;
00695       dist = width - w;
00696       if ( dist < 0 )
00697         dist = -dist;
00698       if ( dist < best )
00699       {
00700         best      = dist;
00701         reference = w;
00702       }
00703     }
00704 
00705     scaled = FT_PIX_ROUND( reference );
00706 
00707     if ( width >= reference )
00708     {
00709       if ( width < scaled + 48 )
00710         width = reference;
00711     }
00712     else
00713     {
00714       if ( width > scaled - 48 )
00715         width = reference;
00716     }
00717 
00718     return width;
00719   }
00720 
00721 
00722   /* compute the snapped width of a given stem */
00723 
00724   static FT_Pos
00725   af_cjk_compute_stem_width( AF_GlyphHints  hints,
00726                              AF_Dimension   dim,
00727                              FT_Pos         width,
00728                              AF_Edge_Flags  base_flags,
00729                              AF_Edge_Flags  stem_flags )
00730   {
00731     AF_LatinMetrics  metrics  = (AF_LatinMetrics) hints->metrics;
00732     AF_LatinAxis     axis     = & metrics->axis[dim];
00733     FT_Pos           dist     = width;
00734     FT_Int           sign     = 0;
00735     FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
00736 
00737     FT_UNUSED( base_flags );
00738     FT_UNUSED( stem_flags );
00739 
00740 
00741     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
00742       return width;
00743 
00744     if ( dist < 0 )
00745     {
00746       dist = -width;
00747       sign = 1;
00748     }
00749 
00750     if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
00751          ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
00752     {
00753       /* smooth hinting process: very lightly quantize the stem width */
00754 
00755       if ( axis->width_count > 0 )
00756       {
00757         if ( FT_ABS( dist - axis->widths[0].cur ) < 40 )
00758         {
00759           dist = axis->widths[0].cur;
00760           if ( dist < 48 )
00761             dist = 48;
00762 
00763           goto Done_Width;
00764         }
00765       }
00766 
00767       if ( dist < 54 )
00768         dist += ( 54 - dist ) / 2 ;
00769       else if ( dist < 3 * 64 )
00770       {
00771         FT_Pos  delta;
00772 
00773 
00774         delta  = dist & 63;
00775         dist  &= -64;
00776 
00777         if ( delta < 10 )
00778           dist += delta;
00779         else if ( delta < 22 )
00780           dist += 10;
00781         else if ( delta < 42 )
00782           dist += delta;
00783         else if ( delta < 54 )
00784           dist += 54;
00785         else
00786           dist += delta;
00787       }
00788     }
00789     else
00790     {
00791       /* strong hinting process: snap the stem width to integer pixels */
00792 
00793       dist = af_cjk_snap_width( axis->widths, axis->width_count, dist );
00794 
00795       if ( vertical )
00796       {
00797         /* in the case of vertical hinting, always round */
00798         /* the stem heights to integer pixels            */
00799 
00800         if ( dist >= 64 )
00801           dist = ( dist + 16 ) & ~63;
00802         else
00803           dist = 64;
00804       }
00805       else
00806       {
00807         if ( AF_LATIN_HINTS_DO_MONO( hints ) )
00808         {
00809           /* monochrome horizontal hinting: snap widths to integer pixels */
00810           /* with a different threshold                                   */
00811 
00812           if ( dist < 64 )
00813             dist = 64;
00814           else
00815             dist = ( dist + 32 ) & ~63;
00816         }
00817         else
00818         {
00819           /* for horizontal anti-aliased hinting, we adopt a more subtle */
00820           /* approach: we strengthen small stems, round stems whose size */
00821           /* is between 1 and 2 pixels to an integer, otherwise nothing  */
00822 
00823           if ( dist < 48 )
00824             dist = ( dist + 64 ) >> 1;
00825 
00826           else if ( dist < 128 )
00827             dist = ( dist + 22 ) & ~63;
00828           else
00829             /* round otherwise to prevent color fringes in LCD mode */
00830             dist = ( dist + 32 ) & ~63;
00831         }
00832       }
00833     }
00834 
00835   Done_Width:
00836     if ( sign )
00837       dist = -dist;
00838 
00839     return dist;
00840   }
00841 
00842 
00843   /* align one stem edge relative to the previous stem edge */
00844 
00845   static void
00846   af_cjk_align_linked_edge( AF_GlyphHints  hints,
00847                             AF_Dimension   dim,
00848                             AF_Edge        base_edge,
00849                             AF_Edge        stem_edge )
00850   {
00851     FT_Pos  dist = stem_edge->opos - base_edge->opos;
00852 
00853     FT_Pos  fitted_width = af_cjk_compute_stem_width(
00854                              hints, dim, dist,
00855                              (AF_Edge_Flags)base_edge->flags,
00856                              (AF_Edge_Flags)stem_edge->flags );
00857 
00858 
00859     stem_edge->pos = base_edge->pos + fitted_width;
00860   }
00861 
00862 
00863   static void
00864   af_cjk_align_serif_edge( AF_GlyphHints  hints,
00865                            AF_Edge        base,
00866                            AF_Edge        serif )
00867   {
00868     FT_UNUSED( hints );
00869 
00870     serif->pos = base->pos + ( serif->opos - base->opos );
00871   }
00872 
00873 
00874   /*************************************************************************/
00875   /*************************************************************************/
00876   /*************************************************************************/
00877   /****                                                                 ****/
00878   /****                    E D G E   H I N T I N G                      ****/
00879   /****                                                                 ****/
00880   /*************************************************************************/
00881   /*************************************************************************/
00882   /*************************************************************************/
00883 
00884 
00885 #define AF_LIGHT_MODE_MAX_HORZ_GAP    9
00886 #define AF_LIGHT_MODE_MAX_VERT_GAP   15
00887 #define AF_LIGHT_MODE_MAX_DELTA_ABS  14
00888 
00889 
00890   static FT_Pos
00891   af_hint_normal_stem( AF_GlyphHints  hints,
00892                        AF_Edge        edge,
00893                        AF_Edge        edge2,
00894                        FT_Pos         anchor,
00895                        AF_Dimension   dim )
00896   {
00897     FT_Pos  org_len, cur_len, org_center;
00898     FT_Pos  cur_pos1, cur_pos2;
00899     FT_Pos  d_off1, u_off1, d_off2, u_off2, delta;
00900     FT_Pos  offset;
00901     FT_Pos  threshold = 64;
00902 
00903 
00904     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
00905     {
00906       if ( ( edge->flags  & AF_EDGE_ROUND ) &&
00907            ( edge2->flags & AF_EDGE_ROUND ) )
00908       {
00909         if ( dim == AF_DIMENSION_VERT )
00910           threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP;
00911         else
00912           threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP;
00913       }
00914       else
00915       {
00916         if ( dim == AF_DIMENSION_VERT )
00917           threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3;
00918         else
00919           threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3;
00920       }
00921     }
00922 
00923     org_len    = edge2->opos - edge->opos;
00924     cur_len    = af_cjk_compute_stem_width( hints, dim, org_len,
00925                                             (AF_Edge_Flags)edge->flags,
00926                                             (AF_Edge_Flags)edge2->flags );
00927 
00928     org_center = ( edge->opos + edge2->opos ) / 2 + anchor;
00929     cur_pos1   = org_center - cur_len / 2;
00930     cur_pos2   = cur_pos1 + cur_len;
00931     d_off1     = cur_pos1 - FT_PIX_FLOOR( cur_pos1 );
00932     d_off2     = cur_pos2 - FT_PIX_FLOOR( cur_pos2 );
00933     u_off1     = 64 - d_off1;
00934     u_off2     = 64 - d_off2;
00935     delta      = 0;
00936 
00937 
00938     if ( d_off1 == 0 || d_off2 == 0 )
00939       goto Exit;
00940 
00941     if ( cur_len <= threshold )
00942     {
00943       if ( d_off2 < cur_len )
00944       {
00945         if ( u_off1 <= d_off2 )
00946           delta =  u_off1;
00947         else
00948           delta = -d_off2;
00949       }
00950 
00951       goto Exit;
00952     }
00953 
00954     if ( threshold < 64 )
00955     {
00956       if ( d_off1 >= threshold || u_off1 >= threshold ||
00957            d_off2 >= threshold || u_off2 >= threshold )
00958         goto Exit;
00959     }
00960 
00961     offset = cur_len % 64;
00962 
00963     if ( offset < 32 )
00964     {
00965       if ( u_off1 <= offset || d_off2 <= offset )
00966         goto Exit;
00967     }
00968     else
00969       offset = 64 - threshold;
00970 
00971     d_off1 = threshold - u_off1;
00972     u_off1 = u_off1    - offset;
00973     u_off2 = threshold - d_off2;
00974     d_off2 = d_off2    - offset;
00975 
00976     if ( d_off1 <= u_off1 )
00977       u_off1 = -d_off1;
00978 
00979     if ( d_off2 <= u_off2 )
00980       u_off2 = -d_off2;
00981 
00982     if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) )
00983       delta = u_off1;
00984     else
00985       delta = u_off2;
00986 
00987   Exit:
00988 
00989 #if 1
00990     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
00991     {
00992       if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS )
00993         delta = AF_LIGHT_MODE_MAX_DELTA_ABS;
00994       else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS )
00995         delta = -AF_LIGHT_MODE_MAX_DELTA_ABS;
00996     }
00997 #endif
00998 
00999     cur_pos1 += delta;
01000 
01001     if ( edge->opos < edge2->opos )
01002     {
01003       edge->pos  = cur_pos1;
01004       edge2->pos = cur_pos1 + cur_len;
01005     }
01006     else
01007     {
01008       edge->pos  = cur_pos1 + cur_len;
01009       edge2->pos = cur_pos1;
01010     }
01011 
01012     return delta;
01013   }
01014 
01015 
01016   static void
01017   af_cjk_hint_edges( AF_GlyphHints  hints,
01018                      AF_Dimension   dim )
01019   {
01020     AF_AxisHints  axis       = &hints->axis[dim];
01021     AF_Edge       edges      = axis->edges;
01022     AF_Edge       edge_limit = edges + axis->num_edges;
01023     FT_PtrDist    n_edges;
01024     AF_Edge       edge;
01025     AF_Edge       anchor   = 0;
01026     FT_Pos        delta    = 0;
01027     FT_Int        skipped  = 0;
01028 
01029 
01030     /* now we align all stem edges. */
01031     for ( edge = edges; edge < edge_limit; edge++ )
01032     {
01033       AF_Edge  edge2;
01034 
01035 
01036       if ( edge->flags & AF_EDGE_DONE )
01037         continue;
01038 
01039       /* skip all non-stem edges */
01040       edge2 = edge->link;
01041       if ( !edge2 )
01042       {
01043         skipped++;
01044         continue;
01045       }
01046 
01047       /* now align the stem */
01048 
01049       if ( edge2 < edge )
01050       {
01051         af_cjk_align_linked_edge( hints, dim, edge2, edge );
01052         edge->flags |= AF_EDGE_DONE;
01053         continue;
01054       }
01055 
01056       if ( dim != AF_DIMENSION_VERT && !anchor )
01057       {
01058 
01059 #if 0
01060         if ( fixedpitch )
01061         {
01062           AF_Edge     left  = edge;
01063           AF_Edge     right = edge_limit - 1;
01064           AF_EdgeRec  left1, left2, right1, right2;
01065           FT_Pos      target, center1, center2;
01066           FT_Pos      delta1, delta2, d1, d2;
01067 
01068 
01069           while ( right > left && !right->link )
01070             right--;
01071 
01072           left1  = *left;
01073           left2  = *left->link;
01074           right1 = *right->link;
01075           right2 = *right;
01076 
01077           delta  = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2;
01078           target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16;
01079 
01080           delta1  = delta;
01081           delta1 += af_hint_normal_stem( hints, left, left->link,
01082                                          delta1, 0 );
01083 
01084           if ( left->link != right )
01085             af_hint_normal_stem( hints, right->link, right, delta1, 0 );
01086 
01087           center1 = left->pos + ( right->pos - left->pos ) / 2;
01088 
01089           if ( center1 >= target )
01090             delta2 = delta - 32;
01091           else
01092             delta2 = delta + 32;
01093 
01094           delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 );
01095 
01096           if ( delta1 != delta2 )
01097           {
01098             if ( left->link != right )
01099               af_hint_normal_stem( hints, &right1, &right2, delta2, 0 );
01100 
01101             center2 = left1.pos + ( right2.pos - left1.pos ) / 2;
01102 
01103             d1 = center1 - target;
01104             d2 = center2 - target;
01105 
01106             if ( FT_ABS( d2 ) < FT_ABS( d1 ) )
01107             {
01108               left->pos       = left1.pos;
01109               left->link->pos = left2.pos;
01110 
01111               if ( left->link != right )
01112               {
01113                 right->link->pos = right1.pos;
01114                 right->pos       = right2.pos;
01115               }
01116 
01117               delta1 = delta2;
01118             }
01119           }
01120 
01121           delta               = delta1;
01122           right->link->flags |= AF_EDGE_DONE;
01123           right->flags       |= AF_EDGE_DONE;
01124         }
01125         else
01126 
01127 #endif /* 0 */
01128 
01129           delta = af_hint_normal_stem( hints, edge, edge2, 0,
01130                                        AF_DIMENSION_HORZ );
01131       }
01132       else
01133         af_hint_normal_stem( hints, edge, edge2, delta, dim );
01134 
01135 #if 0
01136       printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
01137                edge - edges, edge2 - edges,
01138                ( edge->pos - edge->opos ) / 64.0,
01139                ( edge2->pos - edge2->opos ) / 64.0 );
01140 #endif
01141 
01142       anchor = edge;
01143       edge->flags  |= AF_EDGE_DONE;
01144       edge2->flags |= AF_EDGE_DONE;
01145     }
01146 
01147     /* make sure that lowercase m's maintain their symmetry */
01148 
01149     /* In general, lowercase m's have six vertical edges if they are sans */
01150     /* serif, or twelve if they are with serifs.  This implementation is  */
01151     /* based on that assumption, and seems to work very well with most    */
01152     /* faces.  However, if for a certain face this assumption is not      */
01153     /* true, the m is just rendered like before.  In addition, any stem   */
01154     /* correction will only be applied to symmetrical glyphs (even if the */
01155     /* glyph is not an m), so the potential for unwanted distortion is    */
01156     /* relatively low.                                                    */
01157 
01158     /* We don't handle horizontal edges since we can't easily assure that */
01159     /* the third (lowest) stem aligns with the base line; it might end up */
01160     /* one pixel higher or lower.                                         */
01161 
01162     n_edges = edge_limit - edges;
01163     if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
01164     {
01165       AF_Edge  edge1, edge2, edge3;
01166       FT_Pos   dist1, dist2, span;
01167 
01168 
01169       if ( n_edges == 6 )
01170       {
01171         edge1 = edges;
01172         edge2 = edges + 2;
01173         edge3 = edges + 4;
01174       }
01175       else
01176       {
01177         edge1 = edges + 1;
01178         edge2 = edges + 5;
01179         edge3 = edges + 9;
01180       }
01181 
01182       dist1 = edge2->opos - edge1->opos;
01183       dist2 = edge3->opos - edge2->opos;
01184 
01185       span = dist1 - dist2;
01186       if ( span < 0 )
01187         span = -span;
01188 
01189       if ( edge1->link == edge1 + 1 &&
01190            edge2->link == edge2 + 1 &&
01191            edge3->link == edge3 + 1 && span < 8 )
01192       {
01193         delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
01194         edge3->pos -= delta;
01195         if ( edge3->link )
01196           edge3->link->pos -= delta;
01197 
01198         /* move the serifs along with the stem */
01199         if ( n_edges == 12 )
01200         {
01201           ( edges + 8 )->pos -= delta;
01202           ( edges + 11 )->pos -= delta;
01203         }
01204 
01205         edge3->flags |= AF_EDGE_DONE;
01206         if ( edge3->link )
01207           edge3->link->flags |= AF_EDGE_DONE;
01208       }
01209     }
01210 
01211     if ( !skipped )
01212       return;
01213 
01214     /*
01215      *  now hint the remaining edges (serifs and single) in order
01216      *  to complete our processing
01217      */
01218     for ( edge = edges; edge < edge_limit; edge++ )
01219     {
01220       if ( edge->flags & AF_EDGE_DONE )
01221         continue;
01222 
01223       if ( edge->serif )
01224       {
01225         af_cjk_align_serif_edge( hints, edge->serif, edge );
01226         edge->flags |= AF_EDGE_DONE;
01227         skipped--;
01228       }
01229     }
01230 
01231     if ( !skipped )
01232       return;
01233 
01234     for ( edge = edges; edge < edge_limit; edge++ )
01235     {
01236       AF_Edge  before, after;
01237 
01238 
01239       if ( edge->flags & AF_EDGE_DONE )
01240         continue;
01241 
01242       before = after = edge;
01243 
01244       while ( --before >= edges )
01245         if ( before->flags & AF_EDGE_DONE )
01246           break;
01247 
01248       while ( ++after < edge_limit )
01249         if ( after->flags & AF_EDGE_DONE )
01250           break;
01251 
01252       if ( before >= edges || after < edge_limit )
01253       {
01254         if ( before < edges )
01255           af_cjk_align_serif_edge( hints, after, edge );
01256         else if ( after >= edge_limit )
01257           af_cjk_align_serif_edge( hints, before, edge );
01258         else
01259         {
01260           if ( after->fpos == before->fpos )
01261             edge->pos = before->pos;
01262           else
01263             edge->pos = before->pos +
01264                         FT_MulDiv( edge->fpos - before->fpos,
01265                                    after->pos - before->pos,
01266                                    after->fpos - before->fpos );
01267         }
01268       }
01269     }
01270   }
01271 
01272 
01273   static void
01274   af_cjk_align_edge_points( AF_GlyphHints  hints,
01275                             AF_Dimension   dim )
01276   {
01277     AF_AxisHints  axis       = & hints->axis[dim];
01278     AF_Edge       edges      = axis->edges;
01279     AF_Edge       edge_limit = edges + axis->num_edges;
01280     AF_Edge       edge;
01281     FT_Bool       snapping;
01282 
01283 
01284     snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ             &&
01285                           AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) )  ||
01286                         ( dim == AF_DIMENSION_VERT             &&
01287                           AF_LATIN_HINTS_DO_VERT_SNAP( hints ) )  );
01288 
01289     for ( edge = edges; edge < edge_limit; edge++ )
01290     {
01291       /* move the points of each segment     */
01292       /* in each edge to the edge's position */
01293       AF_Segment  seg = edge->first;
01294 
01295 
01296       if ( snapping )
01297       {
01298         do
01299         {
01300           AF_Point  point = seg->first;
01301 
01302 
01303           for (;;)
01304           {
01305             if ( dim == AF_DIMENSION_HORZ )
01306             {
01307               point->x      = edge->pos;
01308               point->flags |= AF_FLAG_TOUCH_X;
01309             }
01310             else
01311             {
01312               point->y      = edge->pos;
01313               point->flags |= AF_FLAG_TOUCH_Y;
01314             }
01315 
01316             if ( point == seg->last )
01317               break;
01318 
01319             point = point->next;
01320           }
01321 
01322           seg = seg->edge_next;
01323 
01324         } while ( seg != edge->first );
01325       }
01326       else
01327       {
01328         FT_Pos  delta = edge->pos - edge->opos;
01329 
01330 
01331         do
01332         {
01333           AF_Point  point = seg->first;
01334 
01335 
01336           for (;;)
01337           {
01338             if ( dim == AF_DIMENSION_HORZ )
01339             {
01340               point->x     += delta;
01341               point->flags |= AF_FLAG_TOUCH_X;
01342             }
01343             else
01344             {
01345               point->y     += delta;
01346               point->flags |= AF_FLAG_TOUCH_Y;
01347             }
01348 
01349             if ( point == seg->last )
01350               break;
01351 
01352             point = point->next;
01353           }
01354 
01355           seg = seg->edge_next;
01356 
01357         } while ( seg != edge->first );
01358       }
01359     }
01360   }
01361 
01362 
01363   FT_LOCAL_DEF( FT_Error )
01364   af_cjk_hints_apply( AF_GlyphHints    hints,
01365                       FT_Outline*      outline,
01366                       AF_LatinMetrics  metrics )
01367   {
01368     FT_Error  error;
01369     int       dim;
01370 
01371     FT_UNUSED( metrics );
01372 
01373 
01374     error = af_glyph_hints_reload( hints, outline, 0 );
01375     if ( error )
01376       goto Exit;
01377 
01378     /* analyze glyph outline */
01379     if ( AF_HINTS_DO_HORIZONTAL( hints ) )
01380     {
01381       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
01382       if ( error )
01383         goto Exit;
01384     }
01385 
01386     if ( AF_HINTS_DO_VERTICAL( hints ) )
01387     {
01388       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
01389       if ( error )
01390         goto Exit;
01391     }
01392 
01393     /* grid-fit the outline */
01394     for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
01395     {
01396       if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
01397            ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
01398       {
01399 
01400 #ifdef AF_USE_WARPER
01401         if ( dim == AF_DIMENSION_HORZ                                  &&
01402              metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL )
01403         {
01404           AF_WarperRec  warper;
01405           FT_Fixed      scale;
01406           FT_Pos        delta;
01407 
01408 
01409           af_warper_compute( &warper, hints, dim, &scale, &delta );
01410           af_glyph_hints_scale_dim( hints, dim, scale, delta );
01411           continue;
01412         }
01413 #endif /* AF_USE_WARPER */
01414 
01415         af_cjk_hint_edges( hints, (AF_Dimension)dim );
01416         af_cjk_align_edge_points( hints, (AF_Dimension)dim );
01417         af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
01418         af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
01419       }
01420     }
01421 
01422 #if 0
01423     af_glyph_hints_dump_points( hints );
01424     af_glyph_hints_dump_segments( hints );
01425     af_glyph_hints_dump_edges( hints );
01426 #endif
01427 
01428     af_glyph_hints_save( hints, outline );
01429 
01430   Exit:
01431     return error;
01432   }
01433 
01434 
01435   /*************************************************************************/
01436   /*************************************************************************/
01437   /*****                                                               *****/
01438   /*****                C J K   S C R I P T   C L A S S                *****/
01439   /*****                                                               *****/
01440   /*************************************************************************/
01441   /*************************************************************************/
01442 
01443 
01444   static const AF_Script_UniRangeRec  af_cjk_uniranges[] =
01445   {
01446 #if 0
01447     AF_UNIRANGE_REC(  0x0100UL,  0xFFFFUL ),  /* why this? */
01448 #endif
01449     AF_UNIRANGE_REC(  0x2E80UL,  0x2EFFUL ),  /* CJK Radicals Supplement                 */
01450     AF_UNIRANGE_REC(  0x2F00UL,  0x2FDFUL ),  /* Kangxi Radicals                         */
01451     AF_UNIRANGE_REC(  0x3000UL,  0x303FUL ),  /* CJK Symbols and Punctuation             */
01452     AF_UNIRANGE_REC(  0x3040UL,  0x309FUL ),  /* Hiragana                                */
01453     AF_UNIRANGE_REC(  0x30A0UL,  0x30FFUL ),  /* Katakana                                */
01454     AF_UNIRANGE_REC(  0x3100UL,  0x312FUL ),  /* Bopomofo                                */
01455     AF_UNIRANGE_REC(  0x3130UL,  0x318FUL ),  /* Hangul Compatibility Jamo               */
01456     AF_UNIRANGE_REC(  0x31A0UL,  0x31BFUL ),  /* Bopomofo Extended                       */
01457     AF_UNIRANGE_REC(  0x31C0UL,  0x31EFUL ),  /* CJK Strokes                             */
01458     AF_UNIRANGE_REC(  0x31F0UL,  0x31FFUL ),  /* Katakana Phonetic Extensions            */
01459     AF_UNIRANGE_REC(  0x3200UL,  0x32FFUL ),  /* Enclosed CJK Letters and Months         */
01460     AF_UNIRANGE_REC(  0x3300UL,  0x33FFUL ),  /* CJK Compatibility                       */
01461     AF_UNIRANGE_REC(  0x3400UL,  0x4DBFUL ),  /* CJK Unified Ideographs Extension A      */
01462     AF_UNIRANGE_REC(  0x4DC0UL,  0x4DFFUL ),  /* Yijing Hexagram Symbols                 */
01463     AF_UNIRANGE_REC(  0x4E00UL,  0x9FFFUL ),  /* CJK Unified Ideographs                  */
01464     AF_UNIRANGE_REC(  0xF900UL,  0xFAFFUL ),  /* CJK Compatibility Ideographs            */
01465     AF_UNIRANGE_REC(  0xFE30UL,  0xFE4FUL ),  /* CJK Compatibility Forms                 */
01466     AF_UNIRANGE_REC(  0xFF00UL,  0xFFEFUL ),  /* Halfwidth and Fullwidth Forms           */
01467     AF_UNIRANGE_REC( 0x20000UL, 0x2A6DFUL ),  /* CJK Unified Ideographs Extension B      */
01468     AF_UNIRANGE_REC( 0x2F800UL, 0x2FA1FUL ),  /* CJK Compatibility Ideographs Supplement */
01469     AF_UNIRANGE_REC(       0UL,       0UL )
01470   };
01471 
01472 
01473   AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class,
01474     AF_SCRIPT_CJK,
01475     af_cjk_uniranges,
01476 
01477     sizeof( AF_LatinMetricsRec ),
01478 
01479     (AF_Script_InitMetricsFunc) af_cjk_metrics_init,
01480     (AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale,
01481     (AF_Script_DoneMetricsFunc) NULL,
01482 
01483     (AF_Script_InitHintsFunc)   af_cjk_hints_init,
01484     (AF_Script_ApplyHintsFunc)  af_cjk_hints_apply
01485   )
01486 
01487 #else /* !AF_CONFIG_OPTION_CJK */
01488 
01489   static const AF_Script_UniRangeRec  af_cjk_uniranges[] =
01490   {
01491     AF_UNIRANGE_REC( 0UL, 0UL )
01492   };
01493 
01494 
01495   AF_DEFINE_SCRIPT_CLASS(af_cjk_script_class,
01496     AF_SCRIPT_CJK,
01497     af_cjk_uniranges,
01498 
01499     sizeof( AF_LatinMetricsRec ),
01500 
01501     (AF_Script_InitMetricsFunc) NULL,
01502     (AF_Script_ScaleMetricsFunc)NULL,
01503     (AF_Script_DoneMetricsFunc) NULL,
01504 
01505     (AF_Script_InitHintsFunc)   NULL,
01506     (AF_Script_ApplyHintsFunc)  NULL
01507   )
01508 
01509 #endif /* !AF_CONFIG_OPTION_CJK */
01510 
01511 
01512 /* END */

Generated on Tue Jul 5 14:13:41 2011 for ROOT_528-00b_version by  doxygen 1.5.1