コード例 #1
0
ファイル: pixa.cpp プロジェクト: JasonNew/Lxcoff-Framework
void Java_com_example_ocr_Pixa_nativeMergeAndReplacePix(JNIEnv *env, jclass clazz,
                                                                         jint nativePixa,
                                                                         jint indexA, jint indexB) {
  PIXA *pixa = (PIXA *) nativePixa;

  l_int32 op;
  l_int32 x, y, w, h;
  l_int32 dx, dy, dw, dh;
  PIX *pixs, *pixd;
  BOX *boxA, *boxB, *boxd;

  boxA = pixaGetBox(pixa, indexA, L_CLONE);
  boxB = pixaGetBox(pixa, indexB, L_CLONE);
  boxd = boxBoundingRegion(boxA, boxB);

  boxGetGeometry(boxd, &x, &y, &w, &h);
  pixd = pixCreate(w, h, 1);

  op = PIX_SRC | PIX_DST;

  pixs = pixaGetPix(pixa, indexA, L_CLONE);
  boxGetGeometry(boxA, &dx, &dy, &dw, &dh);
  pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0);
  pixDestroy(&pixs);
  boxDestroy(&boxA);

  pixs = pixaGetPix(pixa, indexB, L_CLONE);
  boxGetGeometry(boxB, &dx, &dy, &dw, &dh);
  pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0);
  pixDestroy(&pixs);
  boxDestroy(&boxB);

  pixaReplacePix(pixa, indexA, pixd, boxd);

}
コード例 #2
0
/*!
 *  pixaInitFull()
 *
 *      Input:  pixa (typically empty)
 *              pix (to be replicated into the entire pixa ptr array)
 *              box (<optional> to be replicated into the entire boxa ptr array)
 *      Return: 0 if OK, 1 on error
 *
 *  Notes:
 *      (1) This initializes a pixa by filling up the entire pix ptr array
 *          with copies of @pix.  Any existing pix are destroyed.
 *          It also fills the boxa with copies of @box.
 *          After this oepration, the numbers of pix and boxes are equal to
 *          the number of allocated ptrs.
 *      (2) Note that we use pixaReplacePix() instead of pixaInsertPix().
 *          They both have the same effect when inserting into a NULL ptr
 *          in the pixa ptr array:
 *      (3) Example usage.  This function is useful to prepare for a
 *          random insertion (or replacement) of pix into a pixa.
 *          To randomly insert pix into a pixa, up to some index "max":
 *             Pixa *pixa = pixaCreate(max);
 *             Pix *pix = pixCreate(1, 1, 1);  // little memory
 *             Box *box = boxCreate(...);
 *             pixaInitFull(pixa, pix, box);
 *          An existing pixa with a smaller ptr array can also be reused:
 *             pixaExtendArrayToSize(pixa, max);
 *             Pix *pix = pixCreate(...);
 *             Box *box = boxCreate(...);
 *             pixaInitFull(pixa, pix, box);
 *          For these situations, the pix should be small and disposable.
 *          The initialization allows the pixa to always be properly
 *          filled, even if all pix (and boxes) are not later replaced.
 */
l_int32
pixaInitFull(PIXA  *pixa,
             PIX   *pix,
             BOX   *box)
{
l_int32  i, n;
PIX     *pixt;

    PROCNAME("pixaInitFull");

    if (!pixa)
        return ERROR_INT("pixa not defined", procName, 1);
    if (!pix)
        return ERROR_INT("pix not defined", procName, 1);

    n = pixa->nalloc;
    pixa->n = n;
    for (i = 0; i < n; i++) {
        pixt = pixCopy(NULL, pix);
        pixaReplacePix(pixa, i, pixt, NULL);
    }
    if (box)
        boxaInitFull(pixa->boxa, box);

    return 0;
}
コード例 #3
0
ファイル: pixa.cpp プロジェクト: JasonNew/Lxcoff-Framework
void Java_com_example_ocr_Pixa_nativeReplacePix(JNIEnv *env, jclass clazz,
                                                                 jint nativePixa, jint index,
                                                                 jint nativePix, jint nativeBox) {
  PIXA *pixa = (PIXA *) nativePixa;
  PIX *pix = (PIX *) nativePix;
  BOX *box = (BOX *) nativeBox;

  pixaReplacePix(pixa, index, pix, box);
}
コード例 #4
0
ファイル: pixa.cpp プロジェクト: hanhailong/tess-two
void Java_com_googlecode_leptonica_android_Pixa_nativeReplacePix(JNIEnv *env, jclass clazz,
        jlong nativePixa, jint index,
        jlong nativePix, jlong nativeBox) {
    PIXA *pixa = (PIXA *) nativePixa;
    PIX *pix = (PIX *) nativePix;
    BOX *box = (BOX *) nativeBox;

    pixaReplacePix(pixa, index, pix, box);
}
コード例 #5
0
ファイル: pixafunc1.cpp プロジェクト: ONLYOFFICE/core
/*!
 *  pixaAddBorderGeneral()
 *
 *      Input:  pixad (can be null or equal to pixas)
 *              pixas (containing pix of all depths; colormap ok)
 *              left, right, top, bot  (number of pixels added)
 *              val   (value of added border pixels)
 *      Return: pixad (with border added to each pix), including on error
 *
 *  Notes:
 *      (1) For binary images:
 *             white:  val = 0
 *             black:  val = 1
 *          For grayscale images:
 *             white:  val = 2 ** d - 1
 *             black:  val = 0
 *          For rgb color images:
 *             white:  val = 0xffffff00
 *             black:  val = 0
 *          For colormapped images, use 'index' found this way:
 *             white: pixcmapGetRankIntensity(cmap, 1.0, &index);
 *             black: pixcmapGetRankIntensity(cmap, 0.0, &index);
 *      (2) For in-place replacement of each pix with a bordered version,
 *          use @pixad = @pixas.  To make a new pixa, use @pixad = NULL.
 *      (3) In both cases, the boxa has sides adjusted as if it were
 *          expanded by the border.
 */
PIXA *
pixaAddBorderGeneral(PIXA     *pixad,
                     PIXA     *pixas,
                     l_int32   left,
                     l_int32   right,
                     l_int32   top,
                     l_int32   bot,
                     l_uint32  val)
{
l_int32  i, n, nbox;
BOX     *box;
BOXA    *boxad;
PIX     *pixs, *pixd;

    PROCNAME("pixaAddBorderGeneral");

    if (!pixas)
        return (PIXA *)ERROR_PTR("pixas not defined", procName, pixad);
    if (left < 0 || right < 0 || top < 0 || bot < 0)
        return (PIXA *)ERROR_PTR("negative border added!", procName, pixad);
    if (pixad && (pixad != pixas))
        return (PIXA *)ERROR_PTR("pixad defined but != pixas", procName, pixad);

    n = pixaGetCount(pixas);
    if (!pixad)
        pixad = pixaCreate(n);
    for (i = 0; i < n; i++) {
        pixs = pixaGetPix(pixas, i, L_CLONE);
        pixd = pixAddBorderGeneral(pixs, left, right, top, bot, val);
        if (pixad == pixas)  /* replace */
            pixaReplacePix(pixad, i, pixd, NULL);
        else
            pixaAddPix(pixad, pixd, L_INSERT);
        pixDestroy(&pixs);
    }

    nbox = pixaGetBoxaCount(pixas);
    boxad = pixaGetBoxa(pixad, L_CLONE);
    for (i = 0; i < nbox; i++) {
        if ((box = pixaGetBox(pixas, i, L_COPY)) == NULL) {
            L_WARNING_INT("box %d not found", procName, i);
            break;
        }
        boxAdjustSides(box, box, -left, right, -top, bot);
        if (pixad == pixas)  /* replace */
            boxaReplaceBox(boxad, i, box);
        else
            boxaAddBox(boxad, box, L_INSERT);
    }
    boxaDestroy(&boxad);

    return pixad;
}
コード例 #6
0
/*!
 *  pixaGenerateFont()
 *
 *      Input:  pix (of 95 characters in 3 rows)
 *              fontsize (4, 6, 8, ... , 20, in pts at 300 ppi)
 *              &bl1 (<return> baseline of row 1)
 *              &bl2 (<return> baseline of row 2)
 *              &bl3 (<return> baseline of row 3)
 *      Return: pixa of font bitmaps for 95 characters, or null on error
 *
 *  Notes:
 *      (1) This does all the work.  See pixaGenerateFontFromFile()
 *          for an overview.
 *      (2) The pix is for one of the 9 fonts.  @fontsize is only
 *          used here for debugging.
 */
PIXA *
pixaGenerateFont(PIX      *pixs,
                 l_int32   fontsize,
                 l_int32  *pbl0,
                 l_int32  *pbl1,
                 l_int32  *pbl2)
{
l_int32   i, j, nrows, nrowchars, nchars, h, yval;
l_int32   width, height;
l_int32   baseline[3];
l_int32  *tab = NULL;
BOX      *box, *box1, *box2;
BOXA     *boxar, *boxac, *boxacs;
PIX      *pix1, *pix2, *pixr, *pixrc, *pixc;
PIXA     *pixa;
l_int32   n, w, inrow, top;
l_int32  *ia;
NUMA     *na;

    PROCNAME("pixaGenerateFont");

    if (!pbl0 || !pbl1 || !pbl2)
        return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL);
    *pbl0 = *pbl1 = *pbl2 = 0;
    if (!pixs)
        return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL);

        /* Locate the 3 rows of characters */
    w = pixGetWidth(pixs);
    na = pixCountPixelsByRow(pixs, NULL);
    boxar = boxaCreate(0);
    n = numaGetCount(na);
    ia = numaGetIArray(na);
    inrow = 0;
    for (i = 0; i < n; i++) {
        if (!inrow && ia[i] > 0) {
            inrow = 1;
            top = i;
        } else if (inrow && ia[i] == 0) {
            inrow = 0;
            box = boxCreate(0, top, w, i - top);
            boxaAddBox(boxar, box, L_INSERT);
        }
    }
    FREE(ia);
    numaDestroy(&na);
    nrows = boxaGetCount(boxar);
#if  DEBUG_FONT_GEN
    L_INFO("For fontsize %s, have %d rows\n", procName, fontsize, nrows);
#endif  /* DEBUG_FONT_GEN */
    if (nrows != 3) {
        L_INFO("nrows = %d; skipping fontsize %d\n", procName, nrows, fontsize);
        return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL);
    }

        /* Grab the character images and baseline data */
#if DEBUG_BASELINE
    lept_rmdir("baseline");
    lept_mkdir("baseline");
#endif  /* DEBUG_BASELINE */
    tab = makePixelSumTab8();
    pixa = pixaCreate(95);
    for (i = 0; i < nrows; i++) {
        box = boxaGetBox(boxar, i, L_CLONE);
        pixr = pixClipRectangle(pixs, box, NULL);  /* row of chars */
        pixGetTextBaseline(pixr, tab, &yval);
        baseline[i] = yval;

#if DEBUG_BASELINE
        L_INFO("Baseline info: row %d, yval = %d, h = %d\n", procName,
               i, yval, pixGetHeight(pixr));
        pix1 = pixCopy(NULL, pixr);
        pixRenderLine(pix1, 0, yval, pixGetWidth(pix1), yval, 1,
                      L_FLIP_PIXELS);
        if (i == 0 )
            pixWrite("/tmp/baseline/row0.png", pix1, IFF_PNG);
        else if (i == 1)
            pixWrite("/tmp/baseline/row1.png", pix1, IFF_PNG);
        else
            pixWrite("/tmp/baseline/row2.png", pix1, IFF_PNG);
        pixDestroy(&pix1);
#endif  /* DEBUG_BASELINE */

        boxDestroy(&box);
        pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35);
        boxac = pixConnComp(pixrc, NULL, 8);
        boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL);
        if (i == 0) {  /* consolidate the two components of '"' */
            box1 = boxaGetBox(boxacs, 1, L_CLONE);
            box2 = boxaGetBox(boxacs, 2, L_CLONE);
            box1->w = box2->x + box2->w - box1->x;  /* increase width */
            boxDestroy(&box1);
            boxDestroy(&box2);
            boxaRemoveBox(boxacs, 2);
        }
        h = pixGetHeight(pixr);
        nrowchars = boxaGetCount(boxacs);
        for (j = 0; j < nrowchars; j++) {
            box = boxaGetBox(boxacs, j, L_COPY);
            if (box->w <= 2 && box->h == 1) {  /* skip 1x1, 2x1 components */
                boxDestroy(&box);
                continue;
            }
            box->y = 0;
            box->h = h - 1;
            pixc = pixClipRectangle(pixr, box, NULL);
            boxDestroy(&box);
            if (i == 0 && j == 0)  /* add a pix for the space; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            if (i == 2 && j == 0)  /* add a pix for the '\'; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            pixaAddPix(pixa, pixc, L_INSERT);
        }
        pixDestroy(&pixr);
        pixDestroy(&pixrc);
        boxaDestroy(&boxac);
        boxaDestroy(&boxacs);
    }
    FREE(tab);

    nchars = pixaGetCount(pixa);
    if (nchars != 95)
        return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL);

    *pbl0 = baseline[0];
    *pbl1 = baseline[1];
    *pbl2 = baseline[2];

        /* Fix the space character up; it should have no ON pixels,
         * and be about twice as wide as the '!' character.    */
    pix1 = pixaGetPix(pixa, 0, L_CLONE);
    width = 2 * pixGetWidth(pix1);
    height = pixGetHeight(pix1);
    pixDestroy(&pix1);
    pix1 = pixCreate(width, height, 1);
    pixaReplacePix(pixa, 0, pix1, NULL);

        /* Fix up the '\' character; use a LR flip of the '/' char */
    pix1 = pixaGetPix(pixa, 15, L_CLONE);
    pix2 = pixFlipLR(NULL, pix1);
    pixDestroy(&pix1);
    pixaReplacePix(pixa, 60, pix2, NULL);

#if DEBUG_CHARS
    pix1 = pixaDisplayTiled(pixa, 1500, 0, 10);
    pixDisplay(pix1, 100 * i, 200);
    pixDestroy(&pix1);
#endif  /* DEBUG_CHARS */

    boxaDestroy(&boxar);
    return pixa;
}
コード例 #7
0
ファイル: pixa2_reg.c プロジェクト: mehulsbhatt/MyOCRTEST
int main(int argc,
         char **argv) {
    const char *name;
    l_int32 i, n;
    BOX *box;
    PIX *pix0, *pix1, *pixd;
    PIXA *pixa;
    SARRAY *sa1, *sa2, *sa3, *sa4;
    L_REGPARAMS *rp;

    if (regTestSetup(argc, argv, &rp))
        return 1;

    /* ----------------  Find all the jpg and tif images --------------- */
    sa1 = getSortedPathnamesInDirectory(".", ".jpg", 0, 0);
    sa2 = getSortedPathnamesInDirectory(".", ".tif", 0, 0);
    sa3 = sarraySelectByRange(sa1, 0, 9);
    sa4 = sarraySelectByRange(sa2, 0, 9);
    sarrayConcatenate(sa3, sa4);
    n = sarrayGetCount(sa3);
    sarrayDestroy(&sa1);
    sarrayDestroy(&sa2);
    sarrayDestroy(&sa4);

    /* ---------------- Use replace to fill up a pixa -------------------*/
    pixa = pixaCreate(1);
    pixaExtendArrayToSize(pixa, n);
    if ((pix0 = pixRead("marge.jpg")) == NULL)
        rp->success = FALSE;
    pix1 = pixScaleToSize(pix0, 144, 108);  /* scale 0.25 */
    pixDestroy(&pix0);
    pixaInitFull(pixa, pix1, NULL);  /* fill it up */
    pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 25, 2);
    pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display);
    pixWrite("/tmp/regout/pix1.jpg", pixd, IFF_JFIF_JPEG);
    pixDestroy(&pix1);
    pixDestroy(&pixd);

    /* ---------------- And again with jpgs and tifs -------------------*/
    for (i = 0; i < n; i++) {
        name = sarrayGetString(sa3, i, L_NOCOPY);
        if ((pix0 = pixRead(name)) == NULL)
            rp->success = FALSE;
        pix1 = pixScaleToSize(pix0, 144, 108);
        pixaReplacePix(pixa, i, pix1, NULL);
        pixDestroy(&pix0);
    }
    pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 25, 2);
    pixDisplayWithTitle(pixd, 400, 100, NULL, rp->display);
    pixWrite("/tmp/regout/pix2.jpg", pixd, IFF_JFIF_JPEG);
    pixDestroy(&pixd);

    /* ---------------- And again, reversing the order ------------------*/
    box = boxCreate(0, 0, 0, 0);
    pixaInitFull(pixa, NULL, box);
    boxDestroy(&box);
    for (i = 0; i < n; i++) {
        name = sarrayGetString(sa3, i, L_NOCOPY);
        if ((pix0 = pixRead(name)) == NULL)
            rp->success = FALSE;
        pix1 = pixScaleToSize(pix0, 144, 108);
        pixaReplacePix(pixa, n - 1 - i, pix1, NULL);
        pixDestroy(&pix0);
    }
    pixd = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 25, 2);
    pixDisplayWithTitle(pixd, 700, 100, NULL, rp->display);
    pixWrite("/tmp/regout/pix3.jpg", pixd, IFF_JFIF_JPEG);
    pixDestroy(&pixd);
    sarrayDestroy(&sa3);

    pixaDestroy(&pixa);
    return regTestCleanup(rp);
}
コード例 #8
0
ファイル: imagefind.cpp プロジェクト: Appiah/tesseractstuff
// Finds image regions within the source pix (page image) and returns
// the image regions as a Boxa, Pixa pair, analgous to pixConnComp.
// The returned boxa, pixa may be NULL, meaning no images found.
// If not NULL, they must be destroyed by the caller.
void ImageFinder::FindImages(Pix* pix, Boxa** boxa, Pixa** pixa) {
  *boxa = NULL;
  *pixa = NULL;

#ifdef HAVE_LIBLEPT
  if (pixGetWidth(pix) < kMinImageFindSize ||
      pixGetHeight(pix) < kMinImageFindSize)
    return;  // Not worth looking at small images.
  // Reduce by factor 2.
  Pix *pixr = pixReduceRankBinaryCascade(pix, 1, 0, 0, 0);
  pixDisplayWrite(pixr, textord_tabfind_show_images);

  // Get the halftone mask directly from Leptonica.
  Pix *pixht2 = pixGenHalftoneMask(pixr, NULL, NULL,
                                   textord_tabfind_show_images);
  pixDestroy(&pixr);
  if (pixht2 == NULL)
    return;

  // Expand back up again.
  Pix *pixht = pixExpandReplicate(pixht2, 2);
  pixDisplayWrite(pixht, textord_tabfind_show_images);
  pixDestroy(&pixht2);

  // Fill to capture pixels near the mask edges that were missed
  Pix *pixt = pixSeedfillBinary(NULL, pixht, pix, 8);
  pixOr(pixht, pixht, pixt);
  pixDestroy(&pixt);

  // Eliminate lines and bars that may be joined to images.
  Pix* pixfinemask = pixReduceRankBinaryCascade(pixht, 1, 1, 3, 3);
  pixDilateBrick(pixfinemask, pixfinemask, 5, 5);
  pixDisplayWrite(pixfinemask, textord_tabfind_show_images);
  Pix* pixreduced = pixReduceRankBinaryCascade(pixht, 1, 1, 1, 1);
  Pix* pixreduced2 = pixReduceRankBinaryCascade(pixreduced, 3, 3, 3, 0);
  pixDestroy(&pixreduced);
  pixDilateBrick(pixreduced2, pixreduced2, 5, 5);
  Pix* pixcoarsemask = pixExpandReplicate(pixreduced2, 8);
  pixDestroy(&pixreduced2);
  pixDisplayWrite(pixcoarsemask, textord_tabfind_show_images);
  // Combine the coarse and fine image masks.
  pixAnd(pixcoarsemask, pixcoarsemask, pixfinemask);
  pixDestroy(&pixfinemask);
  // Dilate a bit to make sure we get everything.
  pixDilateBrick(pixcoarsemask, pixcoarsemask, 3, 3);
  Pix* pixmask = pixExpandReplicate(pixcoarsemask, 16);
  pixDestroy(&pixcoarsemask);
  pixDisplayWrite(pixmask, textord_tabfind_show_images);
  // And the image mask with the line and bar remover.
  pixAnd(pixht, pixht, pixmask);
  pixDestroy(&pixmask);
  pixDisplayWrite(pixht, textord_tabfind_show_images);
  // Find the individual image regions in the mask image.
  *boxa = pixConnComp(pixht, pixa, 8);
  pixDestroy(&pixht);
  // Rectangularize the individual images. If a sharp edge in vertical and/or
  // horizontal occupancy can be found, it indicates a probably rectangular
  // image with unwanted bits merged on, so clip to the approximate rectangle.
  int npixes = pixaGetCount(*pixa);
  for (int i = 0; i < npixes; ++i) {
    int x_start, x_end, y_start, y_end;
    Pix* img_pix = pixaGetPix(*pixa, i, L_CLONE);
    pixDisplayWrite(img_pix, textord_tabfind_show_images);
    if (pixNearlyRectangular(img_pix, kMinRectangularFraction,
                             kMaxRectangularFraction,
                             kMaxRectangularGradient,
                             &x_start, &y_start, &x_end, &y_end)) {
      // Add 1 to the size as a kludgy flag to indicate to the later stages
      // of processing that it is a clipped rectangular image .
      Pix* simple_pix = pixCreate(pixGetWidth(img_pix) + 1,
                                  pixGetHeight(img_pix), 1);
      pixDestroy(&img_pix);
      pixRasterop(simple_pix, x_start, y_start, x_end - x_start,
                  y_end - y_start, PIX_SET, NULL, 0, 0);
      // pixaReplacePix takes ownership of the simple_pix.
      pixaReplacePix(*pixa, i, simple_pix, NULL);
      img_pix = pixaGetPix(*pixa, i, L_CLONE);
    }
    // Subtract the pix from the correct location in the master image.
    l_int32 x, y, width, height;
    pixDisplayWrite(img_pix, textord_tabfind_show_images);
    boxaGetBoxGeometry(*boxa, i, &x, &y, &width, &height);
    pixRasterop(pix, x, y, width, height, PIX_NOT(PIX_SRC) & PIX_DST,
                img_pix, 0, 0);
    pixDestroy(&img_pix);
  }
#endif
}
コード例 #9
0
ファイル: bmf.c プロジェクト: ansgri/rsdt-students
/*!
 *  pixaGenerateFont()
 *
 *      Input:  dir (directory holding image of character set)
 *              size (4, 6, 8, ... , 20, in pts at 300 ppi)
 *              &bl1 (<return> baseline of row 1)
 *              &bl2 (<return> baseline of row 2)
 *              &bl3 (<return> baseline of row 3)
 *      Return: pixa of font bitmaps for 95 characters, or null on error
 *
 *  These font generation functions use 9 sets, each with bitmaps
 *  of 94 ascii characters, all in Palatino-Roman font.
 *  Each input bitmap has 3 rows of characters.  The range of
 *  ascii values in each row is as follows:
 *    row 0:  32-57   (32 is a space)
 *    row 1:  58-91   (92, '\', is not represented in this font)
 *    row 2:  93-126 
 *  We LR flip the '/' char to generate a bitmap for the missing
 *  '\' character, so that we have representations of all 95
 *  printable chars.
 *
 *  Computation of the bitmaps and baselines for a single
 *  font takes from 40 to 200 msec on a 2 GHz processor,
 *  depending on the size.  Use pixaGetFont() to read the
 *  generated character set directly from files that were
 *  produced in prog/genfonts.c using this function.
 */
PIXA *
pixaGenerateFont(const char  *dir,
                 l_int32      size,
                 l_int32     *pbl0,
                 l_int32     *pbl1,
                 l_int32     *pbl2)
{
char     *pathname;
l_int32   fileno;
l_int32   i, j, nrows, nrowchars, nchars, h, yval;
l_int32   width, height;
l_int32   baseline[3];
l_int32  *tab;
BOX      *box, *box1, *box2;
BOXA     *boxar, *boxac, *boxacs;
PIX      *pixs, *pixt1, *pixt2, *pixt3;
PIX      *pixr, *pixrc, *pixc;
PIXA     *pixa;

    PROCNAME("pixaGenerateFont");

    if (!pbl0 || !pbl1 || !pbl2)
        return (PIXA *)ERROR_PTR("&bl not all defined", procName, NULL);
    *pbl0 = *pbl1 = *pbl2 = 0;

    fileno = (size / 2) - 2;
    if (fileno < 0 || fileno > NFONTS)
        return (PIXA *)ERROR_PTR("font size invalid", procName, NULL);
    tab = makePixelSumTab8();
    pathname = genPathname(dir, inputfonts[fileno]);
    if ((pixs = pixRead(pathname)) == NULL)
        return (PIXA *)ERROR_PTR("pixs not all defined", procName, NULL);
    FREE(pathname);

    pixa = pixaCreate(95);
    pixt1 = pixMorphSequence(pixs, "c1.35 + c101.1", 0);
    boxar = pixConnComp(pixt1, NULL, 8);  /* one box for each row */
    pixDestroy(&pixt1);
    nrows = boxaGetCount(boxar);
#if  DEBUG_FONT_GEN
    fprintf(stderr, "For font %s, number of rows is %d\n",
            inputfonts[fileno], nrows);
#endif  /* DEBUG_FONT_GEN */
    if (nrows != 3) {
        L_INFO_INT2("nrows = %d; skipping font %d", procName, nrows, fileno);
        return (PIXA *)ERROR_PTR("3 rows not generated", procName, NULL);
    }
    for (i = 0; i < nrows; i++) {
        box = boxaGetBox(boxar, i, L_CLONE);
        pixr = pixClipRectangle(pixs, box, NULL);  /* row of chars */
        pixGetTextBaseline(pixr, tab, &yval);
        baseline[i] = yval;

#if DEBUG_BASELINE
      { PIX *pixbl;
        fprintf(stderr, "row %d, yval = %d, h = %d\n",
                i, yval, pixGetHeight(pixr));
        pixbl = pixCopy(NULL, pixr);
        pixRenderLine(pixbl, 0, yval, pixGetWidth(pixbl), yval, 1,
                      L_FLIP_PIXELS);
        if (i == 0 )
            pixWrite("junktl0", pixbl, IFF_PNG);
        else if (i == 1)
            pixWrite("junktl1", pixbl, IFF_PNG);
        else
            pixWrite("junktl2", pixbl, IFF_PNG);
        pixDestroy(&pixbl);
      }
#endif  /* DEBUG_BASELINE */

        boxDestroy(&box);
        pixrc = pixCloseSafeBrick(NULL, pixr, 1, 35);
        boxac = pixConnComp(pixrc, NULL, 8);
        boxacs = boxaSort(boxac, L_SORT_BY_X, L_SORT_INCREASING, NULL);
        if (i == 0) {  /* consolidate the two components of '"' */
            box1 = boxaGetBox(boxacs, 1, L_CLONE);
            box2 = boxaGetBox(boxacs, 2, L_CLONE);
            box1->w = box2->x + box2->w - box1->x;  /* increase width */
            boxDestroy(&box1);
            boxDestroy(&box2);
            boxaRemoveBox(boxacs, 2);
        }
        h = pixGetHeight(pixr);
        nrowchars = boxaGetCount(boxacs);
        for (j = 0; j < nrowchars; j++) {
            box = boxaGetBox(boxacs, j, L_COPY);
            if (box->w <= 2 && box->h == 1) {  /* skip 1x1, 2x1 components */
                boxDestroy(&box);
                continue;
            }
            box->y = 0;
            box->h = h - 1;
            pixc = pixClipRectangle(pixr, box, NULL);
            boxDestroy(&box);
            if (i == 0 && j == 0)  /* add a pix for the space; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            if (i == 2 && j == 0)  /* add a pix for the '\'; change later */
                pixaAddPix(pixa, pixc, L_COPY);
            pixaAddPix(pixa, pixc, L_INSERT);
        }
        pixDestroy(&pixr);
        pixDestroy(&pixrc);
        boxaDestroy(&boxac);
        boxaDestroy(&boxacs);
    }

    nchars = pixaGetCount(pixa);
    if (nchars != 95)
        return (PIXA *)ERROR_PTR("95 chars not generated", procName, NULL);

    *pbl0 = baseline[0];
    *pbl1 = baseline[1];
    *pbl2 = baseline[2];
        
        /* Fix the space character up; it should have no ON pixels,
         * and be about twice as wide as the '!' character.    */
    pixt2 = pixaGetPix(pixa, 0, L_CLONE);
    width = 2 * pixGetWidth(pixt2);
    height = pixGetHeight(pixt2);
    pixDestroy(&pixt2);
    pixt2 = pixCreate(width, height, 1);
    pixaReplacePix(pixa, 0, pixt2, NULL);

        /* Fix up the '\' character; use a LR flip of the '/' char */
    pixt2 = pixaGetPix(pixa, 15, L_CLONE);
    pixt3 = pixFlipLR(NULL, pixt2);
    pixDestroy(&pixt2);
    pixaReplacePix(pixa, 60, pixt3, NULL);
    
#if DEBUG_CHARS
  { PIX *pixd;
    pixd = pixaDisplayTiled(pixa, 1500, 0, 10);
    pixDisplay(pixd, 100 * i, 200);
    pixDestroy(&pixd);
  }
#endif  /* DEBUG_CHARS */

    pixDestroy(&pixs);
    boxaDestroy(&boxar);
    FREE(tab);

    return pixa;
}