Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /***************************************************************************/
  2. /*                                                                         */
  3. /*  cf2ft.c                                                                */
  4. /*                                                                         */
  5. /*    FreeType Glue Component to Adobe's Interpreter (body).               */
  6. /*                                                                         */
  7. /*  Copyright 2013 Adobe Systems Incorporated.                             */
  8. /*                                                                         */
  9. /*  This software, and all works of authorship, whether in source or       */
  10. /*  object code form as indicated by the copyright notice(s) included      */
  11. /*  herein (collectively, the "Work") is made available, and may only be   */
  12. /*  used, modified, and distributed under the FreeType Project License,    */
  13. /*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
  14. /*  FreeType Project License, each contributor to the Work hereby grants   */
  15. /*  to any individual or legal entity exercising permissions granted by    */
  16. /*  the FreeType Project License and this section (hereafter, "You" or     */
  17. /*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
  18. /*  royalty-free, irrevocable (except as stated in this section) patent    */
  19. /*  license to make, have made, use, offer to sell, sell, import, and      */
  20. /*  otherwise transfer the Work, where such license applies only to those  */
  21. /*  patent claims licensable by such contributor that are necessarily      */
  22. /*  infringed by their contribution(s) alone or by combination of their    */
  23. /*  contribution(s) with the Work to which such contribution(s) was        */
  24. /*  submitted.  If You institute patent litigation against any entity      */
  25. /*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
  26. /*  the Work or a contribution incorporated within the Work constitutes    */
  27. /*  direct or contributory patent infringement, then any patent licenses   */
  28. /*  granted to You under this License for that Work shall terminate as of  */
  29. /*  the date such litigation is filed.                                     */
  30. /*                                                                         */
  31. /*  By using, modifying, or distributing the Work you indicate that you    */
  32. /*  have read and understood the terms and conditions of the               */
  33. /*  FreeType Project License as well as those provided in this section,    */
  34. /*  and you accept them fully.                                             */
  35. /*                                                                         */
  36. /***************************************************************************/
  37.  
  38.  
  39. #include "cf2ft.h"
  40. #include FT_INTERNAL_DEBUG_H
  41.  
  42. #include "cf2font.h"
  43. #include "cf2error.h"
  44.  
  45.  
  46. #define CF2_MAX_SIZE  cf2_intToFixed( 2000 )    /* max ppem */
  47.  
  48.  
  49.   /*
  50.    * This check should avoid most internal overflow cases.  Clients should
  51.    * generally respond to `Glyph_Too_Big' by getting a glyph outline
  52.    * at EM size, scaling it and filling it as a graphics operation.
  53.    *
  54.    */
  55.   static FT_Error
  56.   cf2_checkTransform( const CF2_Matrix*  transform,
  57.                       CF2_Int            unitsPerEm )
  58.   {
  59.     CF2_Fixed  maxScale;
  60.  
  61.  
  62.     FT_ASSERT( unitsPerEm > 0 );
  63.  
  64.     FT_ASSERT( transform->a > 0 && transform->d > 0 );
  65.     FT_ASSERT( transform->b == 0 && transform->c == 0 );
  66.     FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
  67.  
  68.     if ( unitsPerEm > 0x7FFF )
  69.       return FT_THROW( Glyph_Too_Big );
  70.  
  71.     maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
  72.  
  73.     if ( transform->a > maxScale || transform->d > maxScale )
  74.       return FT_THROW( Glyph_Too_Big );
  75.  
  76.     return FT_Err_Ok;
  77.   }
  78.  
  79.  
  80.   static void
  81.   cf2_setGlyphWidth( CF2_Outline  outline,
  82.                      CF2_Fixed    width )
  83.   {
  84.     CFF_Decoder*  decoder = outline->decoder;
  85.  
  86.  
  87.     FT_ASSERT( decoder );
  88.  
  89.     decoder->glyph_width = cf2_fixedToInt( width );
  90.   }
  91.  
  92.  
  93.   /* Clean up font instance. */
  94.   static void
  95.   cf2_free_instance( void*  ptr )
  96.   {
  97.     CF2_Font  font = (CF2_Font)ptr;
  98.  
  99.  
  100.     if ( font )
  101.     {
  102.       FT_Memory  memory = font->memory;
  103.  
  104.  
  105.       (void)memory;
  106.     }
  107.   }
  108.  
  109.  
  110.   /********************************************/
  111.   /*                                          */
  112.   /* functions for handling client outline;   */
  113.   /* FreeType uses coordinates in 26.6 format */
  114.   /*                                          */
  115.   /********************************************/
  116.  
  117.   static void
  118.   cf2_builder_moveTo( CF2_OutlineCallbacks      callbacks,
  119.                       const CF2_CallbackParams  params )
  120.   {
  121.     /* downcast the object pointer */
  122.     CF2_Outline   outline = (CF2_Outline)callbacks;
  123.     CFF_Builder*  builder;
  124.  
  125.     (void)params;        /* only used in debug mode */
  126.  
  127.  
  128.     FT_ASSERT( outline && outline->decoder );
  129.     FT_ASSERT( params->op == CF2_PathOpMoveTo );
  130.  
  131.     builder = &outline->decoder->builder;
  132.  
  133.     /* note: two successive moves simply close the contour twice */
  134.     cff_builder_close_contour( builder );
  135.     builder->path_begun = 0;
  136.   }
  137.  
  138.  
  139.   static void
  140.   cf2_builder_lineTo( CF2_OutlineCallbacks      callbacks,
  141.                       const CF2_CallbackParams  params )
  142.   {
  143.     /* downcast the object pointer */
  144.     CF2_Outline   outline = (CF2_Outline)callbacks;
  145.     CFF_Builder*  builder;
  146.  
  147.  
  148.     FT_ASSERT( outline && outline->decoder );
  149.     FT_ASSERT( params->op == CF2_PathOpLineTo );
  150.  
  151.     builder = &outline->decoder->builder;
  152.  
  153.     if ( !builder->path_begun )
  154.     {
  155.       /* record the move before the line; also check points and set */
  156.       /* `path_begun'                                               */
  157.       cff_builder_start_point( builder,
  158.                                params->pt0.x,
  159.                                params->pt0.y );
  160.     }
  161.  
  162.     /* `cff_builder_add_point1' includes a check_points call for one point */
  163.     cff_builder_add_point1( builder,
  164.                             params->pt1.x,
  165.                             params->pt1.y );
  166.   }
  167.  
  168.  
  169.   static void
  170.   cf2_builder_cubeTo( CF2_OutlineCallbacks      callbacks,
  171.                       const CF2_CallbackParams  params )
  172.   {
  173.     /* downcast the object pointer */
  174.     CF2_Outline   outline = (CF2_Outline)callbacks;
  175.     CFF_Builder*  builder;
  176.  
  177.  
  178.     FT_ASSERT( outline && outline->decoder );
  179.     FT_ASSERT( params->op == CF2_PathOpCubeTo );
  180.  
  181.     builder = &outline->decoder->builder;
  182.  
  183.     if ( !builder->path_begun )
  184.     {
  185.       /* record the move before the line; also check points and set */
  186.       /* `path_begun'                                               */
  187.       cff_builder_start_point( builder,
  188.                                params->pt0.x,
  189.                                params->pt0.y );
  190.     }
  191.  
  192.     /* prepare room for 3 points: 2 off-curve, 1 on-curve */
  193.     cff_check_points( builder, 3 );
  194.  
  195.     cff_builder_add_point( builder,
  196.                            params->pt1.x,
  197.                            params->pt1.y, 0 );
  198.     cff_builder_add_point( builder,
  199.                            params->pt2.x,
  200.                            params->pt2.y, 0 );
  201.     cff_builder_add_point( builder,
  202.                            params->pt3.x,
  203.                            params->pt3.y, 1 );
  204.   }
  205.  
  206.  
  207.   static void
  208.   cf2_outline_init( CF2_Outline  outline,
  209.                     FT_Memory    memory,
  210.                     FT_Error*    error )
  211.   {
  212.     FT_MEM_ZERO( outline, sizeof ( CF2_OutlineRec ) );
  213.  
  214.     outline->root.memory = memory;
  215.     outline->root.error  = error;
  216.  
  217.     outline->root.moveTo = cf2_builder_moveTo;
  218.     outline->root.lineTo = cf2_builder_lineTo;
  219.     outline->root.cubeTo = cf2_builder_cubeTo;
  220.   }
  221.  
  222.  
  223.   /* get scaling and hint flag from GlyphSlot */
  224.   static void
  225.   cf2_getScaleAndHintFlag( CFF_Decoder*  decoder,
  226.                            CF2_Fixed*    x_scale,
  227.                            CF2_Fixed*    y_scale,
  228.                            FT_Bool*      hinted,
  229.                            FT_Bool*      scaled )
  230.   {
  231.     FT_ASSERT( decoder && decoder->builder.glyph );
  232.  
  233.     /* note: FreeType scale includes a factor of 64 */
  234.     *hinted = decoder->builder.glyph->hint;
  235.     *scaled = decoder->builder.glyph->scaled;
  236.  
  237.     if ( *hinted )
  238.     {
  239.       *x_scale = FT_DivFix( decoder->builder.glyph->x_scale,
  240.                             cf2_intToFixed( 64 ) );
  241.       *y_scale = FT_DivFix( decoder->builder.glyph->y_scale,
  242.                             cf2_intToFixed( 64 ) );
  243.     }
  244.     else
  245.     {
  246.       /* for unhinted outlines, `cff_slot_load' does the scaling, */
  247.       /* thus render at `unity' scale                             */
  248.  
  249.       *x_scale = 0x0400;   /* 1/64 as 16.16 */
  250.       *y_scale = 0x0400;
  251.     }
  252.   }
  253.  
  254.  
  255.   /* get units per em from `FT_Face' */
  256.   /* TODO: should handle font matrix concatenation? */
  257.   static FT_UShort
  258.   cf2_getUnitsPerEm( CFF_Decoder*  decoder )
  259.   {
  260.     FT_ASSERT( decoder && decoder->builder.face );
  261.     FT_ASSERT( decoder->builder.face->root.units_per_EM );
  262.  
  263.     return decoder->builder.face->root.units_per_EM;
  264.   }
  265.  
  266.  
  267.   /* Main entry point: Render one glyph. */
  268.   FT_LOCAL_DEF( FT_Error )
  269.   cf2_decoder_parse_charstrings( CFF_Decoder*  decoder,
  270.                                  FT_Byte*      charstring_base,
  271.                                  FT_ULong      charstring_len )
  272.   {
  273.     FT_Memory  memory;
  274.     FT_Error   error = FT_Err_Ok;
  275.     CF2_Font   font;
  276.  
  277.  
  278.     FT_ASSERT( decoder && decoder->cff );
  279.  
  280.     memory = decoder->builder.memory;
  281.  
  282.     /* CF2 data is saved here across glyphs */
  283.     font = (CF2_Font)decoder->cff->cf2_instance.data;
  284.  
  285.     /* on first glyph, allocate instance structure */
  286.     if ( decoder->cff->cf2_instance.data == NULL )
  287.     {
  288.       decoder->cff->cf2_instance.finalizer =
  289.         (FT_Generic_Finalizer)cf2_free_instance;
  290.  
  291.       if ( FT_ALLOC( decoder->cff->cf2_instance.data,
  292.                      sizeof ( CF2_FontRec ) ) )
  293.         return FT_THROW( Out_Of_Memory );
  294.  
  295.       font = (CF2_Font)decoder->cff->cf2_instance.data;
  296.  
  297.       font->memory = memory;
  298.  
  299.       /* initialize a client outline, to be shared by each glyph rendered */
  300.       cf2_outline_init( &font->outline, font->memory, &font->error );
  301.     }
  302.  
  303.     /* save decoder; it is a stack variable and will be different on each */
  304.     /* call                                                               */
  305.     font->decoder         = decoder;
  306.     font->outline.decoder = decoder;
  307.  
  308.     {
  309.       /* build parameters for Adobe engine */
  310.  
  311.       CFF_Builder*  builder = &decoder->builder;
  312.       CFF_Driver    driver  = (CFF_Driver)FT_FACE_DRIVER( builder->face );
  313.  
  314.       /* local error */
  315.       FT_Error       error2 = FT_Err_Ok;
  316.       CF2_BufferRec  buf;
  317.       CF2_Matrix     transform;
  318.       CF2_F16Dot16   glyphWidth;
  319.  
  320.       FT_Bool  hinted;
  321.       FT_Bool  scaled;
  322.  
  323.  
  324.       /* FreeType has already looked up the GID; convert to         */
  325.       /* `RegionBuffer', assuming that the input has been validated */
  326.       FT_ASSERT( charstring_base + charstring_len >= charstring_base );
  327.  
  328.       FT_ZERO( &buf );
  329.       buf.start =
  330.       buf.ptr   = charstring_base;
  331.       buf.end   = charstring_base + charstring_len;
  332.  
  333.       FT_ZERO( &transform );
  334.  
  335.       cf2_getScaleAndHintFlag( decoder,
  336.                                &transform.a,
  337.                                &transform.d,
  338.                                &hinted,
  339.                                &scaled );
  340.  
  341.       font->renderingFlags = 0;
  342.       if ( hinted )
  343.         font->renderingFlags |= CF2_FlagsHinted;
  344.       if ( scaled && !driver->no_stem_darkening )
  345.         font->renderingFlags |= CF2_FlagsDarkened;
  346.  
  347.       /* now get an outline for this glyph;      */
  348.       /* also get units per em to validate scale */
  349.       font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
  350.  
  351.       error2 = cf2_checkTransform( &transform, font->unitsPerEm );
  352.       if ( error2 )
  353.         return error2;
  354.  
  355.       error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
  356.       if ( error2 )
  357.         return FT_ERR( Invalid_File_Format );
  358.  
  359.       cf2_setGlyphWidth( &font->outline, glyphWidth );
  360.  
  361.       return FT_Err_Ok;
  362.     }
  363.   }
  364.  
  365.  
  366.   /* get pointer to current FreeType subfont (based on current glyphID) */
  367.   FT_LOCAL_DEF( CFF_SubFont )
  368.   cf2_getSubfont( CFF_Decoder*  decoder )
  369.   {
  370.     FT_ASSERT( decoder && decoder->current_subfont );
  371.  
  372.     return decoder->current_subfont;
  373.   }
  374.  
  375.  
  376.   /* get `y_ppem' from `CFF_Size' */
  377.   FT_LOCAL_DEF( CF2_Fixed )
  378.   cf2_getPpemY( CFF_Decoder*  decoder )
  379.   {
  380.     FT_ASSERT( decoder                          &&
  381.                decoder->builder.face            &&
  382.                decoder->builder.face->root.size );
  383.     FT_ASSERT( decoder->builder.face->root.size->metrics.y_ppem );
  384.  
  385.     return cf2_intToFixed(
  386.              decoder->builder.face->root.size->metrics.y_ppem );
  387.   }
  388.  
  389.  
  390.   /* get standard stem widths for the current subfont; */
  391.   /* FreeType stores these as integer font units       */
  392.   /* (note: variable names seem swapped)               */
  393.   FT_LOCAL_DEF( CF2_Fixed )
  394.   cf2_getStdVW( CFF_Decoder*  decoder )
  395.   {
  396.     FT_ASSERT( decoder && decoder->current_subfont );
  397.  
  398.     return cf2_intToFixed(
  399.              decoder->current_subfont->private_dict.standard_height );
  400.   }
  401.  
  402.  
  403.   FT_LOCAL_DEF( CF2_Fixed )
  404.   cf2_getStdHW( CFF_Decoder*  decoder )
  405.   {
  406.     FT_ASSERT( decoder && decoder->current_subfont );
  407.  
  408.     return cf2_intToFixed(
  409.              decoder->current_subfont->private_dict.standard_width );
  410.   }
  411.  
  412.  
  413.   /* note: FreeType stores 1000 times the actual value for `BlueScale' */
  414.   FT_LOCAL_DEF( void )
  415.   cf2_getBlueMetrics( CFF_Decoder*  decoder,
  416.                       CF2_Fixed*    blueScale,
  417.                       CF2_Fixed*    blueShift,
  418.                       CF2_Fixed*    blueFuzz )
  419.   {
  420.     FT_ASSERT( decoder && decoder->current_subfont );
  421.  
  422.     *blueScale = FT_DivFix(
  423.                    decoder->current_subfont->private_dict.blue_scale,
  424.                    cf2_intToFixed( 1000 ) );
  425.     *blueShift = cf2_intToFixed(
  426.                    decoder->current_subfont->private_dict.blue_shift );
  427.     *blueFuzz  = cf2_intToFixed(
  428.                    decoder->current_subfont->private_dict.blue_fuzz );
  429.   }
  430.  
  431.  
  432.   /* get blue values counts and arrays; the FreeType parser has validated */
  433.   /* the counts and verified that each is an even number                  */
  434.   FT_LOCAL_DEF( void )
  435.   cf2_getBlueValues( CFF_Decoder*  decoder,
  436.                      size_t*       count,
  437.                      FT_Pos*      *data )
  438.   {
  439.     FT_ASSERT( decoder && decoder->current_subfont );
  440.  
  441.     *count = decoder->current_subfont->private_dict.num_blue_values;
  442.     *data  = (FT_Pos*)
  443.                &decoder->current_subfont->private_dict.blue_values;
  444.   }
  445.  
  446.  
  447.   FT_LOCAL_DEF( void )
  448.   cf2_getOtherBlues( CFF_Decoder*  decoder,
  449.                      size_t*       count,
  450.                      FT_Pos*      *data )
  451.   {
  452.     FT_ASSERT( decoder && decoder->current_subfont );
  453.  
  454.     *count = decoder->current_subfont->private_dict.num_other_blues;
  455.     *data  = (FT_Pos*)
  456.                &decoder->current_subfont->private_dict.other_blues;
  457.   }
  458.  
  459.  
  460.   FT_LOCAL_DEF( void )
  461.   cf2_getFamilyBlues( CFF_Decoder*  decoder,
  462.                       size_t*       count,
  463.                       FT_Pos*      *data )
  464.   {
  465.     FT_ASSERT( decoder && decoder->current_subfont );
  466.  
  467.     *count = decoder->current_subfont->private_dict.num_family_blues;
  468.     *data  = (FT_Pos*)
  469.                &decoder->current_subfont->private_dict.family_blues;
  470.   }
  471.  
  472.  
  473.   FT_LOCAL_DEF( void )
  474.   cf2_getFamilyOtherBlues( CFF_Decoder*  decoder,
  475.                            size_t*       count,
  476.                            FT_Pos*      *data )
  477.   {
  478.     FT_ASSERT( decoder && decoder->current_subfont );
  479.  
  480.     *count = decoder->current_subfont->private_dict.num_family_other_blues;
  481.     *data  = (FT_Pos*)
  482.                &decoder->current_subfont->private_dict.family_other_blues;
  483.   }
  484.  
  485.  
  486.   FT_LOCAL_DEF( CF2_Int )
  487.   cf2_getLanguageGroup( CFF_Decoder*  decoder )
  488.   {
  489.     FT_ASSERT( decoder && decoder->current_subfont );
  490.  
  491.     return decoder->current_subfont->private_dict.language_group;
  492.   }
  493.  
  494.  
  495.   /* convert unbiased subroutine index to `CF2_Buffer' and */
  496.   /* return 0 on success                                   */
  497.   FT_LOCAL_DEF( CF2_Int )
  498.   cf2_initGlobalRegionBuffer( CFF_Decoder*  decoder,
  499.                               CF2_UInt      idx,
  500.                               CF2_Buffer    buf )
  501.   {
  502.     FT_ASSERT( decoder && decoder->globals );
  503.  
  504.     FT_ZERO( buf );
  505.  
  506.     idx += decoder->globals_bias;
  507.     if ( idx >= decoder->num_globals )
  508.       return TRUE;     /* error */
  509.  
  510.     buf->start =
  511.     buf->ptr   = decoder->globals[idx];
  512.     buf->end   = decoder->globals[idx + 1];
  513.  
  514.     return FALSE;      /* success */
  515.   }
  516.  
  517.  
  518.   /* convert AdobeStandardEncoding code to CF2_Buffer; */
  519.   /* used for seac component                           */
  520.   FT_LOCAL_DEF( FT_Error )
  521.   cf2_getSeacComponent( CFF_Decoder*  decoder,
  522.                         CF2_UInt      code,
  523.                         CF2_Buffer    buf )
  524.   {
  525.     CF2_Int   gid;
  526.     FT_Byte*  charstring;
  527.     FT_ULong  len;
  528.     FT_Error  error;
  529.  
  530.  
  531.     FT_ASSERT( decoder );
  532.  
  533.     FT_ZERO( buf );
  534.  
  535.     gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
  536.     if ( gid < 0 )
  537.       return FT_THROW( Invalid_Glyph_Format );
  538.  
  539.     error = cff_get_glyph_data( decoder->builder.face,
  540.                                 gid,
  541.                                 &charstring,
  542.                                 &len );
  543.     /* TODO: for now, just pass the FreeType error through */
  544.     if ( error )
  545.       return error;
  546.  
  547.     /* assume input has been validated */
  548.     FT_ASSERT( charstring + len >= charstring );
  549.  
  550.     buf->start = charstring;
  551.     buf->end   = charstring + len;
  552.     buf->ptr   = buf->start;
  553.  
  554.     return FT_Err_Ok;
  555.   }
  556.  
  557.  
  558.   FT_LOCAL_DEF( void )
  559.   cf2_freeSeacComponent( CFF_Decoder*  decoder,
  560.                          CF2_Buffer    buf )
  561.   {
  562.     FT_ASSERT( decoder );
  563.  
  564.     cff_free_glyph_data( decoder->builder.face,
  565.                          (FT_Byte**)&buf->start,
  566.                          (FT_ULong)( buf->end - buf->start ) );
  567.   }
  568.  
  569.  
  570.   FT_LOCAL_DEF( CF2_Int )
  571.   cf2_initLocalRegionBuffer( CFF_Decoder*  decoder,
  572.                              CF2_UInt      idx,
  573.                              CF2_Buffer    buf )
  574.   {
  575.     FT_ASSERT( decoder && decoder->locals );
  576.  
  577.     FT_ZERO( buf );
  578.  
  579.     idx += decoder->locals_bias;
  580.     if ( idx >= decoder->num_locals )
  581.       return TRUE;     /* error */
  582.  
  583.     buf->start =
  584.     buf->ptr   = decoder->locals[idx];
  585.     buf->end   = decoder->locals[idx + 1];
  586.  
  587.     return FALSE;      /* success */
  588.   }
  589.  
  590.  
  591.   FT_LOCAL_DEF( CF2_Fixed )
  592.   cf2_getDefaultWidthX( CFF_Decoder*  decoder )
  593.   {
  594.     FT_ASSERT( decoder && decoder->current_subfont );
  595.  
  596.     return cf2_intToFixed(
  597.              decoder->current_subfont->private_dict.default_width );
  598.   }
  599.  
  600.  
  601.   FT_LOCAL_DEF( CF2_Fixed )
  602.   cf2_getNominalWidthX( CFF_Decoder*  decoder )
  603.   {
  604.     FT_ASSERT( decoder && decoder->current_subfont );
  605.  
  606.     return cf2_intToFixed(
  607.              decoder->current_subfont->private_dict.nominal_width );
  608.   }
  609.  
  610.  
  611.   FT_LOCAL_DEF( void )
  612.   cf2_outline_reset( CF2_Outline  outline )
  613.   {
  614.     CFF_Decoder*  decoder = outline->decoder;
  615.  
  616.  
  617.     FT_ASSERT( decoder );
  618.  
  619.     outline->root.windingMomentum = 0;
  620.  
  621.     FT_GlyphLoader_Rewind( decoder->builder.loader );
  622.   }
  623.  
  624.  
  625.   FT_LOCAL_DEF( void )
  626.   cf2_outline_close( CF2_Outline  outline )
  627.   {
  628.     CFF_Decoder*  decoder = outline->decoder;
  629.  
  630.  
  631.     FT_ASSERT( decoder );
  632.  
  633.     cff_builder_close_contour( &decoder->builder );
  634.  
  635.     FT_GlyphLoader_Add( decoder->builder.loader );
  636.   }
  637.  
  638.  
  639. /* END */
  640.