home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / fontutils-0.6 / imageto / out-chars.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-23  |  20.9 KB  |  605 lines

  1. /* out-chars.c: try to extract the real characters from the image.
  2.  
  3. Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include "bb-outline.h"
  22. #include "gf.h"
  23. #include "report.h"
  24.  
  25. #include "bitmap2.h"
  26. #include "extract.h"
  27. #include "ifi.h"
  28. #include "image-header.h"
  29. #include "main.h"
  30. #include "out-strips.h"
  31.  
  32.  
  33. /* A list of the image rows on which the baselines occur.  The end of
  34.    the list is marked with an element -1.  (-baselines)  */
  35. int *baseline_list = NULL;
  36.  
  37. /* Find about this many characters in the image, then stop.  This is
  38.    useful only for testing, because converting the entire image takes a long
  39.    time.  So we might in truth output a few more characters than
  40.    specified here.  (-nchars)  */
  41. unsigned nchars_wanted = MAX_CHARCODE + 1;
  42.  
  43. /* If set, prints diagnostics about which boxes are and aren't cleaned. 
  44.    (-print-clean-info)  */ 
  45. boolean print_clean_info = false;
  46.  
  47. /* Says whether to print the row numbers of each character as we go, 
  48.    so that the user can decide how to adjust jumping baselines. 
  49.    (-print-guidelines)  */
  50. boolean print_guidelines = false;
  51.  
  52. /* Says which characters to output.  This is independent of the
  53.    ordering in the font file.  (-range) */
  54. int starting_char = 0;
  55. int ending_char = MAX_CHARCODE;
  56.  
  57. /* The design size of the font we're creating.  */
  58. static real design_size;
  59.  
  60. /* Where the baseline of the current row is in the image.  The first row
  61.    is #1, and so on.  We start over at row 1 at each image row.*/
  62. static unsigned row_baseline;
  63. static unsigned row_height;
  64.  
  65. /* How many boxes total the characters take up.  */
  66. static unsigned total_boxes_expected;
  67.  
  68.  
  69. static boolean bb_equal_p (bounding_box_type, bounding_box_type);
  70. static gf_char_type bitmap_to_gf_char
  71.   (bitmap_type, real, bounding_box_type, image_char_type);
  72. static void clean_bitmap (bitmap_type *, bb_list_type);
  73. static boolean do_image_line
  74.   (bitmap_type, unsigned *, unsigned *, real, image_char_list_type);
  75. static void image_to_bitmap_bbs (bb_list_type *);
  76. extern void offset_bb_list (bb_list_type *l, int offset);
  77. static unsigned output_chars
  78.   (bb_list_type, bitmap_type, real, image_char_list_type, unsigned);
  79.  
  80. /* Analyze the input image, outputting the characters we find.  */
  81.  
  82. void
  83. write_image_chars (image_header_type image_header, real ds)
  84. {
  85.   bitmap_type *image_bitmap;
  86.   unsigned nchars_done = 0;
  87.  
  88.   /* Read the image information.  This tells us to which character
  89.        each bounding box belongs, among other things.  */
  90.   image_char_list_type image_char_list = read_ifi_file (&total_boxes_expected);
  91.  
  92.   /* Remember this so we don't need to pass it through all the
  93.      intervening routines to the low-level output.  */
  94.   design_size = ds;
  95.   
  96.   /* The main loop also (and more commonly) exits when we've read the
  97.      entire image.  */ 
  98.   while (nchars_done < nchars_wanted)
  99.     {
  100.       unsigned *transitions;
  101.  
  102.       /* Read one line of characters in the image.  After this,
  103.          `image_bitmap' is, for example, `a...z', with blank columns at
  104.          the left and right.  Whatever is in the original image.  */
  105.       image_bitmap
  106.         = some_black_to_all_white_row (image_header.width, &transitions);
  107.       if (image_bitmap == NULL)
  108.         break; /* We've read the whole image file.  */
  109.  
  110.       if (baseline_list == NULL || *baseline_list == -1)
  111.         {
  112.          if (baseline_list != NULL)
  113.             WARNING ("imageto: Not enough baselines specified");
  114.           row_baseline = 0;
  115.         }
  116.       else
  117.         row_baseline = *baseline_list++;
  118.  
  119.       row_height = BITMAP_HEIGHT (*image_bitmap);
  120.  
  121.       /* If `do_image_line' fails, we're supposed to read the next
  122.          row in the image, and put it below the current line.  For
  123.          example, if a line has only an `!' on it, we will only get
  124.          the stem on the first call to `some_black_to_all_white_row'.  
  125.          We want to get the dot in there, too.  */
  126.       while (!do_image_line (*image_bitmap, transitions, &nchars_done,
  127.                              (real) image_header.hres, image_char_list))
  128.         {
  129.           bitmap_type *revised
  130.             = append_next_image_row (*image_bitmap, image_header.width,
  131.                                      &transitions);
  132.           if (revised == NULL)
  133.             {
  134.               WARNING ("imageto: Image ended in the midst of a character");
  135.               break;
  136.             }
  137.  
  138.           /* Tell the user the image row didn't end on a character.  */
  139.           REPORT ("+");
  140.  
  141.           free_bitmap (image_bitmap);
  142.           image_bitmap = revised;
  143.           if (baseline_list == NULL)
  144.             row_baseline = 0;
  145.  
  146.      row_height = BITMAP_HEIGHT (*image_bitmap);
  147.         }
  148.  
  149.       free_bitmap (image_bitmap);
  150.     }
  151. }
  152.  
  153. /* Analyze and output all of the bitmap IMAGE, which is one line of type
  154.    in the original.  The resolution of the image is H_RESOLUTION, and
  155.    we've read NCHARS characters so far.  We use IMAGE_CHAR_LIST and the
  156.    transition vector TRANSITIONS, which has in it how IMAGE breaks into
  157.    characters.
  158.    
  159.    We return false if we need to be given another image row.  */
  160.  
  161. #define NEXT_TRANSITION()  ({                        \
  162.   if (*transitions == BITMAP_WIDTH (image) + 1)                \
  163.     {                                    \
  164.       WARNING ("imageto: Expected more transitions");            \
  165.       break;                                \
  166.     }                                    \
  167.   *transitions++;                            \
  168. })
  169.  
  170. static boolean
  171. do_image_line (bitmap_type image, unsigned *transitions, unsigned *nchars, 
  172.                real h_resolution, image_char_list_type image_char_list)
  173. {
  174.   static unsigned box_count = 0;
  175.   bounding_box_type bb;
  176.   
  177.   /* We always use the entire image line vertically.  */
  178.   MIN_ROW (bb) = 0;
  179.   MAX_ROW (bb) = BITMAP_HEIGHT (image) - 1;
  180.   
  181.   /* `nchars_wanted' is an option to the program, defined at the top.  */
  182.   while (*nchars < nchars_wanted && *transitions != BITMAP_WIDTH (image) + 1)
  183.     {
  184.       int bb_offset = 0;
  185.       unsigned original_box_count = box_count;
  186.       bb_list_type boxes = bb_list_init ();
  187.       bitmap_type *char_bitmap = XTALLOC1 (bitmap_type);
  188.       bitmap_type *temp_bitmap = XTALLOC1 (bitmap_type);
  189.  
  190.       /* The first element of TRANSITIONS is white-to-black.
  191.          Thereafter, they alternate.  */
  192.       MIN_COL (bb) = NEXT_TRANSITION ();
  193.       MAX_COL (bb) = NEXT_TRANSITION ();
  194.       
  195.       /* After this, `char_bitmap' might be just an `a' (with blank
  196.          rows above and below, because `a' has neither a descender nor
  197.          an ascender), then `b', and so on.  In many specimens, some
  198.          characters overlap; for example, we might get `ij' as one
  199.          bitmap, instead of `i' and then `j'.  */
  200.       *char_bitmap = extract_subbitmap (image, bb);
  201.       *temp_bitmap = copy_bitmap (*char_bitmap);
  202.  
  203.       while (true)
  204.         {
  205.           bb_list_type temp_boxes;
  206.           bounding_box_type previous_bb;
  207.  
  208.           /* If we've read all the bounding boxes expected, we're done.  */
  209.           if (box_count == total_boxes_expected)
  210.             break;
  211.  
  212.           /* If we've read more than we expected to, we're in trouble.  */
  213.           if (box_count > total_boxes_expected)
  214.             {
  215.               WARNING2 ("imageto: Read box #%u but expected only %u",
  216.                         box_count, total_boxes_expected);
  217.               /* No point in giving this message more than once.  */
  218.               total_boxes_expected = INT_MAX;
  219.             }
  220.  
  221.           /* Find bounding boxes around all the shapes in `char_bitmap'.
  222.              Continuing the `ij' example, this would result in four
  223.              bounding boxes (one for each dot, one for the dotless `i',
  224.              and one for the dotless `j').  */
  225.           temp_boxes = find_outline_bbs (*temp_bitmap, false, 0, 0);
  226.  
  227.           /* The subimages we've created all start at column zero.  But
  228.              we want to put the images side-by-side, instead of
  229.              overlaying them.  So we change the bounding box numbers.  */
  230.           offset_bb_list (&temp_boxes, bb_offset);
  231.  
  232.           box_count += BB_LIST_LENGTH (temp_boxes);
  233.           bb_list_splice (&boxes, temp_boxes);
  234.  
  235.           /* If the white column was at the end of a character, exit the
  236.              loop to output what we've got..  */
  237.           if (box_at_char_boundary_p (image_char_list, *nchars,
  238.                                       box_count - original_box_count))
  239.             break;
  240.  
  241.           /* If we're at the end of the image row, tell our caller that
  242.              we had to quit in the middle of a character.  */
  243.       if (*transitions == BITMAP_WIDTH (image) + 1)
  244.             { /* Forget that we've seen this before.  */
  245.               box_count = original_box_count;
  246.               return false;
  247.             }
  248.  
  249.       previous_bb = bb;
  250.       MIN_COL (bb) = NEXT_TRANSITION ();
  251.           MAX_COL (bb) = NEXT_TRANSITION ();
  252.           
  253.       /* Leave in the white space between the character parts.  */
  254.       MIN_COL (bb) = MAX_COL (previous_bb);
  255.           
  256.       free_bitmap (temp_bitmap);
  257.           *temp_bitmap = extract_subbitmap (image, bb);
  258.           if (temp_bitmap == NULL)
  259.             {
  260.               WARNING1 ("imageto: Expected more bounding boxes for `%d'",
  261.                        IMAGE_CHARCODE (IMAGE_CHAR (image_char_list, *nchars)));
  262.               break;
  263.             }
  264.  
  265.           /* The boxes `temp_bitmap' should be just to the right of the
  266.              bitmap we've accumulated in `char_bitmap'.  This happens
  267.              above, next time through the loop.  */
  268.           bb_offset = BITMAP_WIDTH (*char_bitmap);
  269.  
  270.       /* When this happens, it usually means that the IFI file
  271.              didn't specify enough bounding boxes for some character,
  272.              and so things are out of sync.  */
  273.           if (BITMAP_HEIGHT (*char_bitmap) != BITMAP_HEIGHT (*temp_bitmap))
  274.             {
  275.               WARNING ("imageto: Line ended inside a character");
  276.               break;
  277.             }
  278.           bitmap_concat (char_bitmap, *temp_bitmap);
  279.         }  /* while (true) */
  280.         
  281.       free_bitmap (temp_bitmap);
  282.  
  283.       /* Convert the bits inside those bounding boxes into one (if not
  284.          overlapping with another) or more (if overlapping) characters
  285.      in the GF font.  */
  286.       *nchars += output_chars (boxes, *char_bitmap, h_resolution,
  287.                                image_char_list, *nchars);
  288.  
  289.       free_bitmap (char_bitmap);
  290.       bb_list_free (&boxes);
  291.     }
  292.   
  293.   return true;
  294. }
  295.  
  296. /* Move all the elements in BB_LIST to the right by OFFSET.  */
  297.  
  298. void
  299. offset_bb_list (bb_list_type *bb_list, int offset)
  300. {
  301.   unsigned this_bb;
  302.   
  303.   for (this_bb = 0; this_bb < BB_LIST_LENGTH (*bb_list); this_bb++)
  304.     {
  305.       bounding_box_type *bb = &BB_LIST_ELT (*bb_list, this_bb);
  306.       MIN_COL (*bb) += offset;
  307.       MAX_COL (*bb) += offset;
  308.     }
  309. }
  310.  
  311.  
  312. /* Return true if BB1 and BB2 are equal.  */
  313.  
  314. static boolean
  315. bb_equal_p (bounding_box_type bb1, bounding_box_type bb2)
  316. {
  317.   return
  318.     MIN_COL (bb1) == MIN_COL (bb2)
  319.     && MIN_ROW (bb1) == MIN_ROW (bb2)
  320.     && MAX_COL (bb1) == MAX_COL (bb2)
  321.     && MAX_ROW (bb1) == MAX_ROW (bb2);
  322. }
  323.  
  324. /* For each bounding box in the list BOXES, extract from IMAGE_BITMAP
  325.    and turn the resulting bitmap into a single character in the font. 
  326.    The information in IMAGE_CHAR_LIST maps bounding boxes to character codes;
  327.    consecutive bounding boxes may belong to the same character.  For
  328.    example, `i' will appear twice, once for the dot and once for the
  329.    stem.  We assume that all the bounding boxes for a given character
  330.    will appear in IMAGE_BITMAP.
  331.    
  332.    We return the number of characters (not bounding boxes) found,
  333.    including characters that were omitted.  */
  334.  
  335. /* Predicate to tell us if we want to actually write the character.  */
  336. #define OUTPUT_CHAR_P(code, image_char)                    \
  337.   ((code) >= starting_char && (code) <= ending_char            \
  338.    && !IMAGE_CHAR_OMIT (image_char))
  339.  
  340. static unsigned
  341. output_chars (bb_list_type boxes, bitmap_type image_bitmap, 
  342.           real h_resolution, image_char_list_type image_char_list,
  343.               unsigned current_char)
  344. {  
  345.   static unsigned char_count = 0;
  346.   int this_box; /* Because we might have to subtract when it's zero.  */
  347.   boolean done[BB_LIST_LENGTH (boxes)];
  348.  
  349.   /* Since we report (a lot) more information when `print_guidelines' is
  350.      true, we can fit fewer characters per line.  */
  351.   unsigned nchars_per_line = print_guidelines ? 1 : 11;
  352.  
  353.   unsigned nchars_written = 0;
  354.   
  355.   for (this_box = 0; this_box < BB_LIST_LENGTH (boxes); this_box++)
  356.     done[this_box] = false;
  357.  
  358.   for (this_box = 0; this_box < BB_LIST_LENGTH (boxes); this_box++)
  359.     {
  360.       bounding_box_type bb;
  361.       bitmap_type bitmap;
  362.       image_char_type c;
  363.       charcode_type charcode;
  364.       bb_list_type bb_list = bb_list_init ();
  365.       
  366.       /* `done[this_box]' will be set if we get to a bounding box that
  367.          has already been combined with a previous one, because of
  368.          an alternating combination.  Since we never go backwards, we
  369.          don't bother to set `done' for every box we look at.  */
  370.       if (done[this_box])
  371.         continue;
  372.         
  373.       c = IMAGE_CHAR (image_char_list, current_char++);
  374.       charcode = IMAGE_CHARCODE (c);
  375.       
  376.       REPORT ("[");
  377.  
  378.       /* Only bother to collect the character image if we're going to
  379.          output it; otherwise, it just wastes a lot of time and space.  */
  380.       if (OUTPUT_CHAR_P (charcode, c))
  381.         { /* A character consisting of zero bounding boxes is invisible;
  382.              e.g., a space.  We don't want to read any of the bitmap for
  383.              such a thing.  */
  384.           if (IMAGE_CHAR_BB_COUNT (c) == 0)
  385.             {
  386.               BITMAP_HEIGHT (bitmap) = 0;
  387.               BITMAP_WIDTH (bitmap) = 0;
  388.               BITMAP_BITS (bitmap) = NULL;
  389.               bb = (bounding_box_type) { 0, 0, 0, 0 };
  390.               
  391.               /* Since we're not eating up any bounding boxes,
  392.                  reconsider the current one.  */
  393.               this_box--;
  394.             }
  395.           else
  396.             {
  397.               bb = BB_LIST_ELT (boxes, this_box);
  398.               bitmap = extract_subbitmap (image_bitmap, bb);
  399.               bb_list_append (&bb_list, bb);
  400.             }
  401.         }
  402.  
  403.       while (IMAGE_CHAR_BB_COUNT (c)-- > 1)
  404.         {
  405.           unsigned combine_box;
  406.           
  407.           if (IMAGE_CHAR_BB_ALTERNATING (c))
  408.             {
  409.               /* Don't increment `this_box', since it is incremented at
  410.                  the end of the loop, and the next box is part of
  411.                  another character.  */
  412.               combine_box = this_box + 2;
  413.               /* Don't look at the second box again in the outer loop.  */
  414.               done[combine_box] = true;
  415.             }
  416.           else
  417.             combine_box = ++this_box;
  418.  
  419.           if (combine_box >= BB_LIST_LENGTH (boxes))
  420.             {
  421.               WARNING1 ("imageto: Not enough outlines for char %u",
  422.                         (unsigned) charcode);
  423.               break;
  424.             }
  425.  
  426.           if (OUTPUT_CHAR_P (charcode, c))
  427.             {
  428.               /* Get the shape to combine with `bitmap'.  */
  429.               bounding_box_type next_bb = BB_LIST_ELT (boxes, combine_box);
  430.               bitmap_type next_bitmap
  431.                 = extract_subbitmap (image_bitmap, next_bb);
  432.  
  433.               bb_list_append (&bb_list, next_bb);
  434.  
  435.               combine_images (&bitmap, next_bitmap, &bb, next_bb);
  436.               free_bitmap (&next_bitmap);
  437.             }
  438.         }
  439.       
  440.       if (OUTPUT_CHAR_P (charcode, c))
  441.         {
  442.           gf_char_type gf_char;
  443.           
  444.           if (BITMAP_BITS (bitmap) != NULL)
  445.             clean_bitmap (&bitmap, bb_list);
  446.  
  447.           gf_char = bitmap_to_gf_char (bitmap, h_resolution, bb, c);
  448.           gf_put_char (gf_char);
  449.  
  450.           /* This and the GF character's bitmap are the same, so we only
  451.              need to free one of the two.  */
  452.           if (BITMAP_BITS (bitmap) != NULL)
  453.             free_bitmap (&bitmap);
  454.         }
  455.       else
  456.         REPORT ("x"); /* We're ignoring this character.  */
  457.  
  458.       REPORT1 ("]%c", ++char_count % nchars_per_line ? ' ' : '\n');
  459.       nchars_written++;
  460.       
  461.       bb_list_free (&bb_list);
  462.     }
  463.   
  464.   return nchars_written;
  465. }
  466.  
  467. /* Remove bits of adjacent characters that may have crept into B because
  468.    of overlapping characters in the original image.  KNOWN_BOXES lists
  469.    all the known parts of B; if we find other bounding boxes in B, we
  470.    remove them.  */
  471.  
  472. static void
  473. clean_bitmap (bitmap_type *b, bb_list_type known_boxes)
  474. {
  475.   unsigned test;
  476.   bb_list_type test_boxes = find_outline_bbs (*b, false, BITMAP_WIDTH (*b), 0);
  477.  
  478.   if (print_clean_info)
  479.     REPORT2 ("Cleaning %ux%u bitmap:\n",
  480.              BITMAP_WIDTH (*b), BITMAP_HEIGHT (*b));
  481.  
  482.   /* Convert KNOWN_BOXES to the same coordinates as `test_boxes'.  */
  483.   image_to_bitmap_bbs (&known_boxes);
  484.   
  485.   for (test = 0; test < BB_LIST_LENGTH (test_boxes); test++)
  486.     {
  487.       unsigned known;
  488.       unsigned known_length = BB_LIST_LENGTH (known_boxes);
  489.       bounding_box_type test_bb = BB_LIST_ELT (test_boxes, test);
  490.  
  491.       if (print_clean_info)
  492.         REPORT4 ("  checking (%d,%d)-(%d,%d) ... ",
  493.                  MIN_COL (test_bb), MIN_ROW (test_bb),
  494.                  MAX_COL (test_bb), MAX_ROW (test_bb));
  495.       
  496.       /* If we want to keep `test_bb', it will be one of the elements of
  497.          BB_LIST.  Otherwise, it is a piece of an adjacent character,
  498.          and we should erase it.  */
  499.       for (known = 0; known < known_length && !bb_equal_p (test_bb,
  500.                                              BB_LIST_ELT (known_boxes, known));
  501.            known++)
  502.         ;
  503.       
  504.       if (known == known_length)
  505.         {
  506.           unsigned r;
  507.           int test_bb_width = BB_WIDTH (test_bb);
  508.           
  509.           assert (test_bb_width > 0);
  510.           
  511.           if (print_clean_info)
  512.             REPORT ("clearing.\n");
  513.  
  514.           for (r = MIN_ROW (test_bb); r <= MAX_ROW (test_bb); r++)
  515.             {
  516.               one_byte *row = BITMAP_ROW (*b, r);
  517.               memset (row + MIN_COL (test_bb), 0, test_bb_width);
  518.             }
  519.         }
  520.       else if (print_clean_info)
  521.         REPORT ("keeping.\n");
  522.     }
  523. }
  524.  
  525.  
  526. /* Translate the elements of BOXES to the origin, i.e., shift each down
  527.    by the minimum row and column.  We use this in `clean_bitmap' to
  528.    change bounding boxes in the coordinates of the entire image to the
  529.    coordinates of the single character we are cleaning.  */
  530.  
  531. static void
  532. image_to_bitmap_bbs (bb_list_type *boxes)
  533. {
  534.   unsigned b;
  535.   unsigned min_col = UINT_MAX;
  536.   unsigned min_row = UINT_MAX;
  537.   
  538.   /* First find the minimum row and column of all the bb's in BOXES.  */
  539.   for (b = 0; b < BB_LIST_LENGTH (*boxes); b++)
  540.     {
  541.       bounding_box_type bb = BB_LIST_ELT (*boxes, b);
  542.       
  543.       assert (MIN_COL (bb) >= 0 && MIN_ROW (bb) >= 0);
  544.       
  545.       MIN_EQUALS (min_col, MIN_COL (bb));
  546.       MIN_EQUALS (min_row, MIN_ROW (bb));
  547.     }
  548.  
  549.   /* Now translate all the bb's by those minimums.  */
  550.   for (b = 0; b < BB_LIST_LENGTH (*boxes); b++)
  551.     {
  552.       bounding_box_type *bb = &BB_LIST_ELT (*boxes, b);
  553.       
  554.       MIN_COL (*bb) -= min_col;
  555.       MAX_COL (*bb) -= min_col;
  556.       MIN_ROW (*bb) -= min_row;
  557.       MAX_ROW (*bb) -= min_row;
  558.     }
  559. }
  560.  
  561. /* Derive the information necessary to output the font character from
  562.    the bitmap B, and return it.  The resolution of the bitmap is given
  563.    in pixels per inch as H_RESOLUTION.  The bounding box BB encloses the
  564.    character in the image coordinates.  We use BB and the static
  565.    variables `row_baseline' and `row_height' to determine the
  566.    positioning of the GF character.  */
  567.  
  568. #define BB_TO_CARTESIAN(x)                         \
  569.   (row_height - 1 - (x) - row_baseline                     \
  570.    - IMAGE_CHAR_BASELINE_ADJUST (image_char))
  571.  
  572. static gf_char_type
  573. bitmap_to_gf_char (bitmap_type b, real h_resolution,
  574.                    bounding_box_type bb, image_char_type image_char)
  575. {
  576.   gf_char_type gf_char;
  577.   real width_in_points;
  578.   bounding_box_type cartesian_bb;  
  579.   charcode_type charcode = IMAGE_CHARCODE (image_char);
  580.   
  581.   MIN_ROW (cartesian_bb) = BB_TO_CARTESIAN (MAX_ROW (bb));
  582.   MAX_ROW (cartesian_bb) = BB_TO_CARTESIAN (MIN_ROW (bb));
  583.   
  584.   REPORT1 ("%u", charcode);
  585.   
  586.   GF_CHARCODE (gf_char) = charcode;
  587.   GF_BITMAP (gf_char) = b;
  588.   GF_CHAR_MIN_COL (gf_char) = IMAGE_CHAR_LSB (image_char);
  589.   GF_CHAR_MAX_COL (gf_char) = GF_CHAR_MIN_COL (gf_char) + BITMAP_WIDTH (b);
  590.   GF_CHAR_MIN_ROW (gf_char) = MIN_ROW (cartesian_bb);
  591.   GF_CHAR_MAX_ROW (gf_char) = MAX_ROW (cartesian_bb);
  592.  
  593.   GF_H_ESCAPEMENT (gf_char) = (GF_CHAR_MAX_COL (gf_char)
  594.                                + IMAGE_CHAR_RSB (image_char));
  595.  
  596.   width_in_points = GF_H_ESCAPEMENT (gf_char) * POINTS_PER_INCH / h_resolution;
  597.   GF_TFM_WIDTH (gf_char) = real_to_fix (width_in_points / design_size);
  598.  
  599.   if (print_guidelines)
  600.     REPORT3 (" (%s) %d/%d",  IMAGE_CHARNAME (image_char),
  601.          MIN_ROW (cartesian_bb), MAX_ROW (cartesian_bb));
  602.  
  603.   return gf_char;
  604. }
  605.