Coverage Report

Created: 2024-02-28 06:46

/src/leptonica/src/textops.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
28
/*!
29
 * \file textops.c
30
 * <pre>
31
 *
32
 *    Font layout
33
 *       PIX             *pixAddSingleTextblock()
34
 *       PIX             *pixAddTextlines()
35
 *       l_int32          pixSetTextblock()
36
 *       l_int32          pixSetTextline()
37
 *       PIXA            *pixaAddTextNumber()
38
 *       PIXA            *pixaAddTextlines()
39
 *       l_int32          pixaAddPixWithText()
40
 *
41
 *    Text size estimation and partitioning
42
 *       SARRAY          *bmfGetLineStrings()
43
 *       NUMA            *bmfGetWordWidths()
44
 *       l_int32          bmfGetStringWidth()
45
 *
46
 *    Text splitting
47
 *       SARRAY          *splitStringToParagraphs()
48
 *       static l_int32   stringAllWhitespace()
49
 *       static l_int32   stringLeadingWhitespace()
50
 *
51
 *    This is a simple utility to put text on images.  One font and style
52
 *    is provided, with a variety of pt sizes.  For example, to put a
53
 *    line of green 10 pt text on an image, with the beginning baseline
54
 *    at (50, 50):
55
 *        L_Bmf  *bmf = bmfCreate(NULL, 10);
56
 *        const char *textstr = "This is a funny cat";
57
 *        pixSetTextline(pixs, bmf, textstr, 0x00ff0000, 50, 50, NULL, NULL);
58
 *
59
 *    The simplest interfaces for adding text to an image are
60
 *    pixAddTextlines() and pixAddSingleTextblock().
61
 *    For example, to add the same text in red, centered, below the image:
62
 *        Pix *pixd = pixAddTextlines(pixs, bmf, textstr, 0xff000000,
63
 *                                    L_ADD_BELOW);  // red text
64
 *
65
 *    To add text to all pix in a pixa, generating a new pixa, use
66
 *    either an sarray to hold the strings for each pix, or use the
67
 *    strings in the text field of each pix; e.g.,
68
 *        Pixa *pixa2 = pixaAddTextlines(pixa1, bmf, sa, 0x0000ff00,
69
 *                                    L_ADD_LEFT);  // blue text
70
 *        Pixa *pixa2 = pixaAddTextlines(pixa1, bmf, NULL, 0x00ff0000,
71
 *                                    L_ADD_RIGHT);  // green text
72
 * </pre>
73
 */
74
75
#ifdef HAVE_CONFIG_H
76
#include <config_auto.h>
77
#endif  /* HAVE_CONFIG_H */
78
79
#include <string.h>
80
#include "allheaders.h"
81
82
static l_int32 stringAllWhitespace(char *textstr, l_int32 *pval);
83
static l_int32 stringLeadingWhitespace(char *textstr, l_int32 *pval);
84
85
86
/*---------------------------------------------------------------------*
87
 *                                 Font layout                         *
88
 *---------------------------------------------------------------------*/
89
/*!
90
 * \brief   pixAddSingleTextblock()
91
 *
92
 * \param[in]    pixs        input pix; colormap ok
93
 * \param[in]    bmf         bitmap font data
94
 * \param[in]    textstr     [optional] text string to be added
95
 * \param[in]    val         color to set the text
96
 * \param[in]    location    L_ADD_ABOVE, L_ADD_AT_TOP,
97
 *                           L_ADD_AT_BOT, L_ADD_BELOW
98
 * \param[out]   poverflow   [optional] 1 if text overflows allocated
99
 *                           region and is clipped; 0 otherwise
100
 * \return  pixd   new pix with rendered text, or either a copy,
101
 *                 or NULL on error
102
 *
103
 * <pre>
104
 * Notes:
105
 *      (1) This function paints a set of lines of text over an image.
106
 *          If %location is L_ADD_ABOVE or L_ADD_BELOW, the pix size
107
 *          is expanded with a border and rendered over the border.
108
 *      (2) %val is the pixel value to be painted through the font mask.
109
 *          It should be chosen to agree with the depth of pixs.
110
 *          If it is out of bounds, an intermediate value is chosen.
111
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
112
 *          hex representation of the red intensity, etc.
113
 *      (3) If textstr == NULL, use the text field in the pix.
114
 *      (4) If there is a colormap, this does the best it can to use
115
 *          the requested color, or something similar to it.
116
 *      (5) Typical usage is for labelling a pix with some text data.
117
 * </pre>
118
 */
119
PIX *
120
pixAddSingleTextblock(PIX         *pixs,
121
                      L_BMF       *bmf,
122
                      const char  *textstr,
123
                      l_uint32     val,
124
                      l_int32      location,
125
                      l_int32     *poverflow)
126
0
{
127
0
char     *linestr;
128
0
l_int32   w, h, d, i, y, xstart, ystart, extra, spacer, rval, gval, bval;
129
0
l_int32   nlines, htext, ovf, overflow, offset, index;
130
0
l_uint32  textcolor;
131
0
PIX      *pixd;
132
0
PIXCMAP  *cmap, *cmapd;
133
0
SARRAY   *salines;
134
135
0
    if (poverflow) *poverflow = 0;
136
0
    if (!pixs)
137
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
138
0
    if (location != L_ADD_ABOVE && location != L_ADD_AT_TOP &&
139
0
        location != L_ADD_AT_BOT && location != L_ADD_BELOW)
140
0
        return (PIX *)ERROR_PTR("invalid location", __func__, NULL);
141
0
    if (!bmf) {
142
0
        L_ERROR("no bitmap fonts; returning a copy\n", __func__);
143
0
        return pixCopy(NULL, pixs);
144
0
    }
145
0
    if (!textstr)
146
0
        textstr = pixGetText(pixs);
147
0
    if (!textstr) {
148
0
        L_WARNING("no textstring defined; returning a copy\n", __func__);
149
0
        return pixCopy(NULL, pixs);
150
0
    }
151
152
        /* Make sure the "color" value for the text will work
153
         * for the pix.  If the pix is not colormapped and the
154
         * value is out of range, set it to mid-range. */
155
0
    pixGetDimensions(pixs, &w, &h, &d);
156
0
    cmap = pixGetColormap(pixs);
157
0
    if (d == 1 && val > 1)
158
0
        val = 1;
159
0
    else if (d == 2 && val > 3 && !cmap)
160
0
        val = 2;
161
0
    else if (d == 4 && val > 15 && !cmap)
162
0
        val = 8;
163
0
    else if (d == 8 && val > 0xff && !cmap)
164
0
        val = 128;
165
0
    else if (d == 16 && val > 0xffff)
166
0
        val = 0x8000;
167
0
    else if (d == 32 && val < 256)
168
0
        val = 0x80808000;
169
170
0
    xstart = (l_int32)(0.1 * w);
171
0
    salines = bmfGetLineStrings(bmf, textstr, w - 2 * xstart, 0, &htext);
172
0
    if (!salines)
173
0
        return (PIX *)ERROR_PTR("line string sa not made", __func__, NULL);
174
0
    nlines = sarrayGetCount(salines);
175
176
        /* Add white border if required */
177
0
    spacer = 10;  /* pixels away from image boundary or added border */
178
0
    if (location == L_ADD_ABOVE || location == L_ADD_BELOW) {
179
0
        extra = htext + 2 * spacer;
180
0
        pixd = pixCreate(w, h + extra, d);
181
0
        pixCopyColormap(pixd, pixs);
182
0
        pixCopyResolution(pixd, pixs);
183
0
        pixCopyText(pixd, pixs);
184
0
        pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
185
0
        if (location == L_ADD_ABOVE)
186
0
            pixRasterop(pixd, 0, extra, w, h, PIX_SRC, pixs, 0, 0);
187
0
        else  /* add below */
188
0
            pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0);
189
0
    } else {
190
0
        pixd = pixCopy(NULL, pixs);
191
0
    }
192
0
    cmapd = pixGetColormap(pixd);
193
194
        /* bmf->baselinetab[93] is the approximate distance from
195
         * the top of the tallest character to the baseline.  93 was chosen
196
         * at random, as all the baselines are essentially equal for
197
         * each character in a font. */
198
0
    offset = bmf->baselinetab[93];
199
0
    if (location == L_ADD_ABOVE || location == L_ADD_AT_TOP)
200
0
        ystart = offset + spacer;
201
0
    else if (location == L_ADD_AT_BOT)
202
0
        ystart = h - htext - spacer + offset;
203
0
    else   /* add below */
204
0
        ystart = h + offset + spacer;
205
206
        /* If cmapped, add the color if necessary to the cmap.  If the
207
         * cmap is full, use the nearest color to the requested color. */
208
0
    if (cmapd) {
209
0
        extractRGBValues(val, &rval, &gval, &bval);
210
0
        pixcmapAddNearestColor(cmapd, rval, gval, bval, &index);
211
0
        pixcmapGetColor(cmapd, index, &rval, &gval, &bval);
212
0
        composeRGBPixel(rval, gval, bval, &textcolor);
213
0
    } else {
214
0
        textcolor = val;
215
0
    }
216
217
        /* Keep track of overflow condition on line width */
218
0
    overflow = 0;
219
0
    for (i = 0, y = ystart; i < nlines; i++) {
220
0
        linestr = sarrayGetString(salines, i, L_NOCOPY);
221
0
        pixSetTextline(pixd, bmf, linestr, textcolor,
222
0
                       xstart, y, NULL, &ovf);
223
0
        y += bmf->lineheight + bmf->vertlinesep;
224
0
        if (ovf)
225
0
            overflow = 1;
226
0
    }
227
228
       /* Also consider vertical overflow where there is too much text to
229
        * fit inside the image: the cases L_ADD_AT_TOP and L_ADD_AT_BOT.
230
        *  The text requires a total of htext + 2 * spacer vertical pixels. */
231
0
    if (location == L_ADD_AT_TOP || location == L_ADD_AT_BOT) {
232
0
        if (h < htext + 2 * spacer)
233
0
            overflow = 1;
234
0
    }
235
0
    if (poverflow) *poverflow = overflow;
236
237
0
    sarrayDestroy(&salines);
238
0
    return pixd;
239
0
}
240
241
242
/*!
243
 * \brief   pixAddTextlines()
244
 *
245
 * \param[in]    pixs        input pix; colormap ok
246
 * \param[in]    bmf         bitmap font data
247
 * \param[in]    textstr     [optional] text string to be added
248
 * \param[in]    val         color to set the text
249
 * \param[in]    location    L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT
250
 * \return  pixd   new pix with rendered text, or either a copy,
251
 *                 or NULL on error
252
 *
253
 * <pre>
254
 * Notes:
255
 *      (1) This function expands an image as required to paint one or
256
 *          more lines of text adjacent to the image.  If %bmf == NULL,
257
 *          this returns a copy.  If above or below, the lines are
258
 *          centered with respect to the image; if left or right, they
259
 *          are left justified.
260
 *      (2) %val is the pixel value to be painted through the font mask.
261
 *          It should be chosen to agree with the depth of pixs.
262
 *          If it is out of bounds, an intermediate value is chosen.
263
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
264
 *          hex representation of the red intensity, etc.
265
 *      (3) If textstr == NULL, use the text field in the pix.  The
266
 *          text field contains one or most "lines" of text, where newlines
267
 *          are used as line separators.
268
 *      (4) If there is a colormap, this does the best it can to use
269
 *          the requested color, or something similar to it.
270
 *      (5) Typical usage is for labelling a pix with some text data.
271
 * </pre>
272
 */
273
PIX *
274
pixAddTextlines(PIX         *pixs,
275
                L_BMF       *bmf,
276
                const char  *textstr,
277
                l_uint32     val,
278
                l_int32      location)
279
0
{
280
0
char     *str;
281
0
l_int32   i, w, h, d, rval, gval, bval, index;
282
0
l_int32   wline, wtext, htext, wadd, hadd, spacer, hbaseline, nlines;
283
0
l_uint32  textcolor;
284
0
PIX      *pixd;
285
0
PIXCMAP  *cmap, *cmapd;
286
0
SARRAY   *sa;
287
288
0
    if (!pixs)
289
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
290
0
    if (location != L_ADD_ABOVE && location != L_ADD_BELOW &&
291
0
        location != L_ADD_LEFT && location != L_ADD_RIGHT)
292
0
        return (PIX *)ERROR_PTR("invalid location", __func__, NULL);
293
0
    if (!bmf) {
294
0
        L_ERROR("no bitmap fonts; returning a copy\n", __func__);
295
0
        return pixCopy(NULL, pixs);
296
0
    }
297
0
    if (!textstr) {
298
0
        textstr = pixGetText(pixs);
299
0
        if (!textstr) {
300
0
            L_WARNING("no textstring defined; returning a copy\n", __func__);
301
0
            return pixCopy(NULL, pixs);
302
0
        }
303
0
    }
304
305
        /* Make sure the "color" value for the text will work
306
         * for the pix.  If the pix is not colormapped and the
307
         * value is out of range, set it to mid-range. */
308
0
    pixGetDimensions(pixs, &w, &h, &d);
309
0
    cmap = pixGetColormap(pixs);
310
0
    if (d == 1 && val > 1)
311
0
        val = 1;
312
0
    else if (d == 2 && val > 3 && !cmap)
313
0
        val = 2;
314
0
    else if (d == 4 && val > 15 && !cmap)
315
0
        val = 8;
316
0
    else if (d == 8 && val > 0xff && !cmap)
317
0
        val = 128;
318
0
    else if (d == 16 && val > 0xffff)
319
0
        val = 0x8000;
320
0
    else if (d == 32 && val < 256)
321
0
        val = 0x80808000;
322
323
        /* Get the text in each line */
324
0
    sa = sarrayCreateLinesFromString(textstr, 0);
325
0
    nlines = sarrayGetCount(sa);
326
327
        /* Get the necessary text size */
328
0
    wtext = 0;
329
0
    for (i = 0; i < nlines; i++) {
330
0
        str = sarrayGetString(sa, i, L_NOCOPY);
331
0
        bmfGetStringWidth(bmf, str, &wline);
332
0
        if (wline > wtext)
333
0
            wtext = wline;
334
0
    }
335
0
    hbaseline = bmf->baselinetab[93];
336
0
    htext = 1.5 * hbaseline * nlines;
337
338
        /* Add white border */
339
0
    spacer = 10;  /* pixels away from the added border */
340
0
    if (location == L_ADD_ABOVE || location == L_ADD_BELOW) {
341
0
        hadd = htext + 2 * spacer;
342
0
        pixd = pixCreate(w, h + hadd, d);
343
0
        pixCopyColormap(pixd, pixs);
344
0
        pixCopyResolution(pixd, pixs);
345
0
        pixCopyText(pixd, pixs);
346
0
        pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
347
0
        if (location == L_ADD_ABOVE)
348
0
            pixRasterop(pixd, 0, hadd, w, h, PIX_SRC, pixs, 0, 0);
349
0
        else  /* add below */
350
0
            pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0);
351
0
    } else {  /*  L_ADD_LEFT or L_ADD_RIGHT */
352
0
        wadd = wtext + 2 * spacer;
353
0
        pixd = pixCreate(w + wadd, h, d);
354
0
        pixCopyColormap(pixd, pixs);
355
0
        pixCopyResolution(pixd, pixs);
356
0
        pixCopyText(pixd, pixs);
357
0
        pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
358
0
        if (location == L_ADD_LEFT)
359
0
            pixRasterop(pixd, wadd, 0, w, h, PIX_SRC, pixs, 0, 0);
360
0
        else  /* add to right */
361
0
            pixRasterop(pixd, 0, 0, w, h, PIX_SRC, pixs, 0, 0);
362
0
    }
363
364
        /* If cmapped, add the color if necessary to the cmap.  If the
365
         * cmap is full, use the nearest color to the requested color. */
366
0
    cmapd = pixGetColormap(pixd);
367
0
    if (cmapd) {
368
0
        extractRGBValues(val, &rval, &gval, &bval);
369
0
        pixcmapAddNearestColor(cmapd, rval, gval, bval, &index);
370
0
        pixcmapGetColor(cmapd, index, &rval, &gval, &bval);
371
0
        composeRGBPixel(rval, gval, bval, &textcolor);
372
0
    } else {
373
0
        textcolor = val;
374
0
    }
375
376
        /* Add the text */
377
0
    for (i = 0; i < nlines; i++) {
378
0
        str = sarrayGetString(sa, i, L_NOCOPY);
379
0
        bmfGetStringWidth(bmf, str, &wtext);
380
0
        if (location == L_ADD_ABOVE)
381
0
            pixSetTextline(pixd, bmf, str, textcolor,
382
0
                           (w - wtext) / 2, spacer + hbaseline * (1 + 1.5 * i),
383
0
                           NULL, NULL);
384
0
        else if (location == L_ADD_BELOW)
385
0
            pixSetTextline(pixd, bmf, str, textcolor,
386
0
                           (w - wtext) / 2, h + spacer +
387
0
                           hbaseline * (1 + 1.5 * i), NULL, NULL);
388
0
        else if (location == L_ADD_LEFT)
389
0
            pixSetTextline(pixd, bmf, str, textcolor,
390
0
                           spacer, (h - htext) / 2 + hbaseline * (1 + 1.5 * i),
391
0
                           NULL, NULL);
392
0
        else  /* location == L_ADD_RIGHT */
393
0
            pixSetTextline(pixd, bmf, str, textcolor,
394
0
                           w + spacer, (h - htext) / 2 +
395
0
                           hbaseline * (1 + 1.5 * i), NULL, NULL);
396
0
    }
397
398
0
    sarrayDestroy(&sa);
399
0
    return pixd;
400
0
}
401
402
403
/*!
404
 * \brief   pixSetTextblock()
405
 *
406
 * \param[in]    pixs          input image
407
 * \param[in]    bmf           bitmap font data
408
 * \param[in]    textstr       block text string to be set
409
 * \param[in]    val           color to set the text
410
 * \param[in]    x0            left edge for each line of text
411
 * \param[in]    y0            baseline location for the first text line
412
 * \param[in]    wtext         max width of each line of generated text
413
 * \param[in]    firstindent   indentation of first line, in x-widths
414
 * \param[out]   poverflow     [optional] 0 if text is contained in input pix;
415
 *                             1 if it is clipped
416
 * \return  0 if OK, 1 on error
417
 *
418
 * <pre>
419
 * Notes:
420
 *      (1) This function paints a set of lines of text over an image.
421
 *      (2) %val is the pixel value to be painted through the font mask.
422
 *          It should be chosen to agree with the depth of pixs.
423
 *          If it is out of bounds, an intermediate value is chosen.
424
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
425
 *          hex representation of the red intensity, etc.
426
 *          The last two hex digits are 00 (byte value 0), assigned to
427
 *          the A component.  Note that, as usual, RGBA proceeds from
428
 *          left to right in the order from MSB to LSB (see pix.h
429
 *          for details).
430
 *      (3) If there is a colormap, this does the best it can to use
431
 *          the requested color, or something similar to it.
432
 * </pre>
433
 */
434
l_ok
435
pixSetTextblock(PIX         *pixs,
436
                L_BMF       *bmf,
437
                const char  *textstr,
438
                l_uint32     val,
439
                l_int32      x0,
440
                l_int32      y0,
441
                l_int32      wtext,
442
                l_int32      firstindent,
443
                l_int32     *poverflow)
444
0
{
445
0
char     *linestr;
446
0
l_int32   d, h, i, w, x, y, nlines, htext, xwidth, wline, ovf, overflow;
447
0
SARRAY   *salines;
448
0
PIXCMAP  *cmap;
449
450
0
    if (!pixs)
451
0
        return ERROR_INT("pixs not defined", __func__, 1);
452
0
    if (!bmf)
453
0
        return ERROR_INT("bmf not defined", __func__, 1);
454
0
    if (!textstr)
455
0
        return ERROR_INT("textstr not defined", __func__, 1);
456
457
        /* Make sure the "color" value for the text will work
458
         * for the pix.  If the pix is not colormapped and the
459
         * value is out of range, set it to mid-range. */
460
0
    pixGetDimensions(pixs, &w, &h, &d);
461
0
    cmap = pixGetColormap(pixs);
462
0
    if (d == 1 && val > 1)
463
0
        val = 1;
464
0
    else if (d == 2 && val > 3 && !cmap)
465
0
        val = 2;
466
0
    else if (d == 4 && val > 15 && !cmap)
467
0
        val = 8;
468
0
    else if (d == 8 && val > 0xff && !cmap)
469
0
        val = 128;
470
0
    else if (d == 16 && val > 0xffff)
471
0
        val = 0x8000;
472
0
    else if (d == 32 && val < 256)
473
0
        val = 0x80808000;
474
475
0
    if (w < x0 + wtext) {
476
0
        L_WARNING("reducing width of textblock\n", __func__);
477
0
        wtext = w - x0 - w / 10;
478
0
        if (wtext <= 0)
479
0
            return ERROR_INT("wtext too small; no room for text", __func__, 1);
480
0
    }
481
482
0
    salines = bmfGetLineStrings(bmf, textstr, wtext, firstindent, &htext);
483
0
    if (!salines)
484
0
        return ERROR_INT("line string sa not made", __func__, 1);
485
0
    nlines = sarrayGetCount(salines);
486
0
    bmfGetWidth(bmf, 'x', &xwidth);
487
488
0
    y = y0;
489
0
    overflow = 0;
490
0
    for (i = 0; i < nlines; i++) {
491
0
        if (i == 0)
492
0
            x = x0 + firstindent * xwidth;
493
0
        else
494
0
            x = x0;
495
0
        linestr = sarrayGetString(salines, i, L_NOCOPY);
496
0
        pixSetTextline(pixs, bmf, linestr, val, x, y, &wline, &ovf);
497
0
        y += bmf->lineheight + bmf->vertlinesep;
498
0
        if (ovf)
499
0
            overflow = 1;
500
0
    }
501
502
       /* (y0 - baseline) is the top of the printed text.  Character
503
        * 93 was chosen at random, as all the baselines are essentially
504
        * equal for each character in a font. */
505
0
    if (h < y0 - bmf->baselinetab[93] + htext)
506
0
        overflow = 1;
507
0
    if (poverflow)
508
0
        *poverflow = overflow;
509
510
0
    sarrayDestroy(&salines);
511
0
    return 0;
512
0
}
513
514
515
/*!
516
 * \brief   pixSetTextline()
517
 *
518
 * \param[in]    pixs        input image
519
 * \param[in]    bmf         bitmap font data
520
 * \param[in]    textstr     text string to be set on the line
521
 * \param[in]    val         color to set the text
522
 * \param[in]    x0          left edge for first char
523
 * \param[in]    y0          baseline location for all text on line
524
 * \param[out]   pwidth      [optional] width of generated text
525
 * \param[out]   poverflow   [optional] 0 if text is contained in input pix;
526
 *                           1 if it is clipped
527
 * \return  0 if OK, 1 on error
528
 *
529
 * <pre>
530
 * Notes:
531
 *      (1) This function paints a line of text over an image.
532
 *      (2) %val is the pixel value to be painted through the font mask.
533
 *          It should be chosen to agree with the depth of pixs.
534
 *          If it is out of bounds, an intermediate value is chosen.
535
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
536
 *          hex representation of the red intensity, etc.
537
 *          The last two hex digits are 00 (byte value 0), assigned to
538
 *          the A component.  Note that, as usual, RGBA proceeds from
539
 *          left to right in the order from MSB to LSB (see pix.h
540
 *          for details).
541
 *      (3) If there is a colormap, this does the best it can to use
542
 *          the requested color, or something similar to it.
543
 * </pre>
544
 */
545
l_ok
546
pixSetTextline(PIX         *pixs,
547
               L_BMF       *bmf,
548
               const char  *textstr,
549
               l_uint32     val,
550
               l_int32      x0,
551
               l_int32      y0,
552
               l_int32     *pwidth,
553
               l_int32     *poverflow)
554
0
{
555
0
char      chr;
556
0
l_int32   d, i, x, w, nchar, baseline, index, rval, gval, bval;
557
0
l_uint32  textcolor;
558
0
PIX      *pix;
559
0
PIXCMAP  *cmap;
560
561
0
    if (!pixs)
562
0
        return ERROR_INT("pixs not defined", __func__, 1);
563
0
    if (!bmf)
564
0
        return ERROR_INT("bmf not defined", __func__, 1);
565
0
    if (!textstr)
566
0
        return ERROR_INT("teststr not defined", __func__, 1);
567
568
0
    d = pixGetDepth(pixs);
569
0
    cmap = pixGetColormap(pixs);
570
0
    if (d == 1 && val > 1)
571
0
        val = 1;
572
0
    else if (d == 2 && val > 3 && !cmap)
573
0
        val = 2;
574
0
    else if (d == 4 && val > 15 && !cmap)
575
0
        val = 8;
576
0
    else if (d == 8 && val > 0xff && !cmap)
577
0
        val = 128;
578
0
    else if (d == 16 && val > 0xffff)
579
0
        val = 0x8000;
580
0
    else if (d == 32 && val < 256)
581
0
        val = 0x80808000;
582
583
        /* If cmapped, add the color if necessary to the cmap.  If the
584
         * cmap is full, use the nearest color to the requested color. */
585
0
    if (cmap) {
586
0
        extractRGBValues(val, &rval, &gval, &bval);
587
0
        pixcmapAddNearestColor(cmap, rval, gval, bval, &index);
588
0
        pixcmapGetColor(cmap, index, &rval, &gval, &bval);
589
0
        composeRGBPixel(rval, gval, bval, &textcolor);
590
0
    } else
591
0
        textcolor = val;
592
593
0
    nchar = strlen(textstr);
594
0
    x = x0;
595
0
    for (i = 0; i < nchar; i++) {
596
0
        chr = textstr[i];
597
0
        if ((l_int32)chr == 10) continue;  /* NL */
598
0
        pix = bmfGetPix(bmf, chr);
599
0
        bmfGetBaseline(bmf, chr, &baseline);
600
0
        pixPaintThroughMask(pixs, pix, x, y0 - baseline, textcolor);
601
0
        w = pixGetWidth(pix);
602
0
        x += w + bmf->kernwidth;
603
0
        pixDestroy(&pix);
604
0
    }
605
606
0
    if (pwidth)
607
0
        *pwidth = x - bmf->kernwidth - x0;
608
0
    if (poverflow)
609
0
        *poverflow = (x > pixGetWidth(pixs) - 1) ? 1 : 0;
610
0
    return 0;
611
0
}
612
613
614
/*!
615
 * \brief   pixaAddTextNumber()
616
 *
617
 * \param[in]    pixas      input pixa; colormap ok
618
 * \param[in]    bmf        bitmap font data
619
 * \param[in]    na         [optional] number array; use 1 ... n if null
620
 * \param[in]    val        color to set the text
621
 * \param[in]    location   L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT
622
 * \return  pixad   new pixa with rendered numbers, or NULL on error
623
 *
624
 * <pre>
625
 * Notes:
626
 *      (1) Typical usage is for labelling each pix in a pixa with a number.
627
 *      (2) This function paints numbers external to each pix, in a position
628
 *          given by %location.  In all cases, the pix is expanded on
629
 *          on side and the number is painted over white in the added region.
630
 *      (3) %val is the pixel value to be painted through the font mask.
631
 *          It should be chosen to agree with the depth of pixs.
632
 *          If it is out of bounds, an intermediate value is chosen.
633
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
634
 *          hex representation of the red intensity, etc.
635
 *      (4) If na == NULL, number each pix sequentially, starting with 1.
636
 *      (5) If there is a colormap, this does the best it can to use
637
 *          the requested color, or something similar to it.
638
 * </pre>
639
 */
640
PIXA *
641
pixaAddTextNumber(PIXA     *pixas,
642
                  L_BMF    *bmf,
643
                  NUMA     *na,
644
                  l_uint32  val,
645
                  l_int32   location)
646
0
{
647
0
char     textstr[128];
648
0
l_int32  i, n, index;
649
0
PIX     *pix1, *pix2;
650
0
PIXA    *pixad;
651
652
0
    if (!pixas)
653
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
654
0
    if (!bmf)
655
0
        return (PIXA *)ERROR_PTR("bmf not defined", __func__, NULL);
656
0
    if (location != L_ADD_ABOVE && location != L_ADD_BELOW &&
657
0
        location != L_ADD_LEFT && location != L_ADD_RIGHT)
658
0
        return (PIXA *)ERROR_PTR("invalid location", __func__, NULL);
659
660
0
    n = pixaGetCount(pixas);
661
0
    pixad = pixaCreate(n);
662
0
    for (i = 0; i < n; i++) {
663
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
664
0
        if (na)
665
0
            numaGetIValue(na, i, &index);
666
0
        else
667
0
            index = i + 1;
668
0
        snprintf(textstr, sizeof(textstr), "%d", index);
669
0
        pix2 = pixAddTextlines(pix1, bmf, textstr, val, location);
670
0
        pixaAddPix(pixad, pix2, L_INSERT);
671
0
        pixDestroy(&pix1);
672
0
    }
673
674
0
    return pixad;
675
0
}
676
677
678
/*!
679
 * \brief   pixaAddTextlines()
680
 *
681
 * \param[in]    pixas      input pixa; colormap ok
682
 * \param[in]    bmf        bitmap font data
683
 * \param[in]    sa         [optional] sarray; use text embedded in
684
 *                          each pix if null
685
 * \param[in]    val        color to set the text
686
 * \param[in]    location   L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT
687
 * \return  pixad   new pixa with rendered text, or NULL on error
688
 *
689
 * <pre>
690
 * Notes:
691
 *      (1) This function adds one or more lines of text externally to
692
 *          each pix, in a position given by %location.  In all cases,
693
 *          the pix is expanded as necessary to accommodate the text.
694
 *      (2) %val is the pixel value to be painted through the font mask.
695
 *          It should be chosen to agree with the depth of pixs.
696
 *          If it is out of bounds, an intermediate value is chosen.
697
 *          For RGB, use hex notation: 0xRRGGBB00, where RR is the
698
 *          hex representation of the red intensity, etc.
699
 *      (3) If sa == NULL, use the text embedded in each pix.  In all
700
 *          cases, newlines in the text string are used to separate the
701
 *          lines of text that are added to the pix.
702
 *      (4) If sa has a smaller count than pixa, issue a warning
703
 *          and do not use any embedded text.
704
 *      (5) If there is a colormap, this does the best it can to use
705
 *          the requested color, or something similar to it.
706
 * </pre>
707
 */
708
PIXA *
709
pixaAddTextlines(PIXA     *pixas,
710
                 L_BMF    *bmf,
711
                 SARRAY   *sa,
712
                 l_uint32  val,
713
                 l_int32   location)
714
0
{
715
0
char    *textstr;
716
0
l_int32  i, n, nstr;
717
0
PIX     *pix1, *pix2;
718
0
PIXA    *pixad;
719
720
0
    if (!pixas)
721
0
        return (PIXA *)ERROR_PTR("pixas not defined", __func__, NULL);
722
0
    if (!bmf)
723
0
        return (PIXA *)ERROR_PTR("bmf not defined", __func__, NULL);
724
0
    if (location != L_ADD_ABOVE && location != L_ADD_BELOW &&
725
0
        location != L_ADD_LEFT && location != L_ADD_RIGHT)
726
0
        return (PIXA *)ERROR_PTR("invalid location", __func__, NULL);
727
728
0
    n = pixaGetCount(pixas);
729
0
    pixad = pixaCreate(n);
730
0
    nstr = (sa) ? sarrayGetCount(sa) : 0;
731
0
    if (nstr > 0 && nstr < n)
732
0
        L_WARNING("There are %d strings and %d pix\n", __func__, nstr, n);
733
0
    for (i = 0; i < n; i++) {
734
0
        pix1 = pixaGetPix(pixas, i, L_CLONE);
735
0
        if (i < nstr)
736
0
            textstr = sarrayGetString(sa, i, L_NOCOPY);
737
0
        else
738
0
            textstr = pixGetText(pix1);
739
0
        pix2 = pixAddTextlines(pix1, bmf, textstr, val, location);
740
0
        pixaAddPix(pixad, pix2, L_INSERT);
741
0
        pixDestroy(&pix1);
742
0
    }
743
744
0
    return pixad;
745
0
}
746
747
748
/*!
749
 * \brief   pixaAddPixWithText()
750
 *
751
 * \param[in]    pixa
752
 * \param[in]    pixs       any depth, colormap ok
753
 * \param[in]    reduction  integer subsampling factor
754
 * \param[in]    bmf        [optional] bitmap font data
755
 * \param[in]    textstr    [optional] text string to be added
756
 * \param[in]    val        color to set the text
757
 * \param[in]    location   L_ADD_ABOVE, L_ADD_BELOW, L_ADD_LEFT, L_ADD_RIGHT
758
 * \return  0 if OK, 1 on error.
759
 *
760
 * <pre>
761
 * Notes:
762
 *      (1) This function generates a new pix with added text, and adds
763
 *          it by insertion into the pixa.
764
 *      (2) If the input pixs is not cmapped and not 32 bpp, it is
765
 *          converted to 32 bpp rgb.  %val is a standard 32 bpp pixel,
766
 *          expressed as 0xrrggbb00.  If there is a colormap, this does
767
 *          the best it can to use the requested color, or something close.
768
 *      (3) if %bmf == NULL, generate an 8 pt font; this takes about 5 msec.
769
 *      (4) If %textstr == NULL, use the text field in the pix.
770
 *      (5) In general, the text string can be written in multiple lines;
771
 *          use newlines as the separators.
772
 *      (6) Typical usage is for debugging, where the pixa of labeled images
773
 *          is used to generate a pdf.  Suggest using 1.0 for scalefactor.
774
 * </pre>
775
 */
776
l_ok
777
pixaAddPixWithText(PIXA        *pixa,
778
                   PIX         *pixs,
779
                   l_int32      reduction,
780
                   L_BMF       *bmf,
781
                   const char  *textstr,
782
                   l_uint32     val,
783
                   l_int32      location)
784
0
{
785
0
l_int32   d;
786
0
L_BMF    *bmf8;
787
0
PIX      *pix1, *pix2, *pix3;
788
0
PIXCMAP  *cmap;
789
790
0
    if (!pixa)
791
0
        return ERROR_INT("pixa not defined", __func__, 1);
792
0
    if (!pixs)
793
0
        return ERROR_INT("pixs not defined", __func__, 1);
794
0
    if (location != L_ADD_ABOVE && location != L_ADD_BELOW &&
795
0
        location != L_ADD_LEFT && location != L_ADD_RIGHT)
796
0
        return ERROR_INT("invalid location", __func__, 1);
797
798
0
    if (!textstr) {
799
0
        textstr = pixGetText(pixs);
800
0
        if (!textstr) {
801
0
            L_WARNING("no textstring defined; inserting copy", __func__);
802
0
            pixaAddPix(pixa, pixs, L_COPY);
803
0
            return 0;
804
0
        }
805
0
    }
806
807
        /* Default font size is 8. */
808
0
    bmf8 = (bmf) ? bmf : bmfCreate(NULL, 8);
809
810
0
    if (reduction != 1)
811
0
        pix1 = pixScaleByIntSampling(pixs, reduction);
812
0
    else
813
0
        pix1 = pixClone(pixs);
814
815
        /* We want the text to be rendered in color.  This works
816
         * automatically if pixs is cmapped or 32 bpp rgb; otherwise,
817
         * we need to convert to rgb. */
818
0
    cmap = pixGetColormap(pix1);
819
0
    d = pixGetDepth(pix1);
820
0
    if (!cmap && d != 32)
821
0
        pix2 = pixConvertTo32(pix1);
822
0
    else
823
0
        pix2 = pixClone(pix1);
824
825
0
    pix3 = pixAddTextlines(pix2, bmf, textstr, val, location);
826
0
    pixDestroy(&pix1);
827
0
    pixDestroy(&pix2);
828
0
    if (!bmf) bmfDestroy(&bmf8);
829
0
    if (!pix3)
830
0
        return ERROR_INT("pix3 not made", __func__, 1);
831
832
0
    pixaAddPix(pixa, pix3, L_INSERT);
833
0
    return 0;
834
0
}
835
836
837
/*---------------------------------------------------------------------*
838
 *                   Text size estimation and partitioning             *
839
 *---------------------------------------------------------------------*/
840
/*!
841
 * \brief   bmfGetLineStrings()
842
 *
843
 * \param[in]    bmf
844
 * \param[in]    textstr
845
 * \param[in]    maxw          max width of a text line in pixels
846
 * \param[in]    firstindent   indentation of first line, in x-widths
847
 * \param[out]   ph            height required to hold text bitmap
848
 * \return  sarray of text strings for each line, or NULL on error
849
 *
850
 * <pre>
851
 * Notes:
852
 *      (1) Divides the input text string into an array of text strings,
853
 *          each of which will fit within maxw bits of width.
854
 * </pre>
855
 */
856
SARRAY *
857
bmfGetLineStrings(L_BMF       *bmf,
858
                  const char  *textstr,
859
                  l_int32      maxw,
860
                  l_int32      firstindent,
861
                  l_int32     *ph)
862
0
{
863
0
char    *linestr;
864
0
l_int32  i, ifirst, sumw, newsum, w, nwords, nlines, len, xwidth;
865
0
NUMA    *na;
866
0
SARRAY  *sa, *sawords;
867
868
0
    if (!bmf)
869
0
        return (SARRAY *)ERROR_PTR("bmf not defined", __func__, NULL);
870
0
    if (!textstr)
871
0
        return (SARRAY *)ERROR_PTR("teststr not defined", __func__, NULL);
872
873
0
    if ((sawords = sarrayCreateWordsFromString(textstr)) == NULL)
874
0
        return (SARRAY *)ERROR_PTR("sawords not made", __func__, NULL);
875
876
0
    if ((na = bmfGetWordWidths(bmf, textstr, sawords)) == NULL) {
877
0
        sarrayDestroy(&sawords);
878
0
        return (SARRAY *)ERROR_PTR("na not made", __func__, NULL);
879
0
    }
880
0
    nwords = numaGetCount(na);
881
0
    if (nwords == 0) {
882
0
        sarrayDestroy(&sawords);
883
0
        numaDestroy(&na);
884
0
        return (SARRAY *)ERROR_PTR("no words in textstr", __func__, NULL);
885
0
    }
886
0
    bmfGetWidth(bmf, 'x', &xwidth);
887
888
0
    sa = sarrayCreate(0);
889
0
    ifirst = 0;
890
0
    numaGetIValue(na, 0, &w);
891
0
    sumw = firstindent * xwidth + w;
892
0
    for (i = 1; i < nwords; i++) {
893
0
        numaGetIValue(na, i, &w);
894
0
        newsum = sumw + bmf->spacewidth + w;
895
0
        if (newsum > maxw) {
896
0
            linestr = sarrayToStringRange(sawords, ifirst, i - ifirst, 2);
897
0
            if (!linestr)
898
0
                continue;
899
0
            len = strlen(linestr);
900
0
            if (len > 0)  /* it should always be */
901
0
                linestr[len - 1] = '\0';  /* remove the last space */
902
0
            sarrayAddString(sa, linestr, L_INSERT);
903
0
            ifirst = i;
904
0
            sumw = w;
905
0
        }
906
0
        else
907
0
            sumw += bmf->spacewidth + w;
908
0
    }
909
0
    linestr = sarrayToStringRange(sawords, ifirst, nwords - ifirst, 2);
910
0
    if (linestr)
911
0
        sarrayAddString(sa, linestr, L_INSERT);
912
0
    nlines = sarrayGetCount(sa);
913
0
    *ph = nlines * bmf->lineheight + (nlines - 1) * bmf->vertlinesep;
914
915
0
    sarrayDestroy(&sawords);
916
0
    numaDestroy(&na);
917
0
    return sa;
918
0
}
919
920
921
/*!
922
 * \brief   bmfGetWordWidths()
923
 *
924
 * \param[in]    bmf
925
 * \param[in]    textstr
926
 * \param[in]    sa        of individual words
927
 * \return  numa  of word lengths in pixels for the font represented
928
 *                by the bmf, or NULL on error
929
 */
930
NUMA *
931
bmfGetWordWidths(L_BMF       *bmf,
932
                 const char  *textstr,
933
                 SARRAY      *sa)
934
0
{
935
0
char    *wordstr;
936
0
l_int32  i, nwords, width;
937
0
NUMA    *na;
938
939
0
    if (!bmf)
940
0
        return (NUMA *)ERROR_PTR("bmf not defined", __func__, NULL);
941
0
    if (!textstr)
942
0
        return (NUMA *)ERROR_PTR("teststr not defined", __func__, NULL);
943
0
    if (!sa)
944
0
        return (NUMA *)ERROR_PTR("sa not defined", __func__, NULL);
945
946
0
    nwords = sarrayGetCount(sa);
947
0
    if ((na = numaCreate(nwords)) == NULL)
948
0
        return (NUMA *)ERROR_PTR("na not made", __func__, NULL);
949
950
0
    for (i = 0; i < nwords; i++) {
951
0
        wordstr = sarrayGetString(sa, i, L_NOCOPY);
952
0
        bmfGetStringWidth(bmf, wordstr, &width);
953
0
        numaAddNumber(na, width);
954
0
    }
955
956
0
    return na;
957
0
}
958
959
960
/*!
961
 * \brief   bmfGetStringWidth()
962
 *
963
 * \param[in]    bmf
964
 * \param[in]    textstr
965
 * \param[out]   pw        width of text string, in pixels for the
966
 *                         font represented by the bmf
967
 * \return  0 if OK, 1 on error
968
 */
969
l_ok
970
bmfGetStringWidth(L_BMF       *bmf,
971
                  const char  *textstr,
972
                  l_int32     *pw)
973
0
{
974
0
char     chr;
975
0
l_int32  i, w, width, nchar;
976
977
0
    if (!bmf)
978
0
        return ERROR_INT("bmf not defined", __func__, 1);
979
0
    if (!textstr)
980
0
        return ERROR_INT("teststr not defined", __func__, 1);
981
0
    if (!pw)
982
0
        return ERROR_INT("&w not defined", __func__, 1);
983
984
0
    nchar = strlen(textstr);
985
0
    w = 0;
986
0
    for (i = 0; i < nchar; i++) {
987
0
        chr = textstr[i];
988
0
        bmfGetWidth(bmf, chr, &width);
989
0
        if (width != UNDEF)
990
0
            w += width + bmf->kernwidth;
991
0
    }
992
0
    w -= bmf->kernwidth;  /* remove last one */
993
994
0
    *pw = w;
995
0
    return 0;
996
0
}
997
998
999
1000
/*---------------------------------------------------------------------*
1001
 *                             Text splitting                          *
1002
 *---------------------------------------------------------------------*/
1003
/*!
1004
 * \brief   splitStringToParagraphs()
1005
 *
1006
 * \param[in]    textstr     text string
1007
 * \param[in]    splitflag   see enum in bmf.h; valid values in {1,2,3}
1008
 * \return  sarray  where each string is a paragraph of the input,
1009
 *                  or NULL on error.
1010
 */
1011
SARRAY *
1012
splitStringToParagraphs(char    *textstr,
1013
                        l_int32  splitflag)
1014
0
{
1015
0
char    *linestr, *parastring;
1016
0
l_int32  nlines, i, allwhite, leadwhite;
1017
0
SARRAY  *salines, *satemp, *saout;
1018
1019
0
    if (!textstr)
1020
0
        return (SARRAY *)ERROR_PTR("textstr not defined", __func__, NULL);
1021
1022
0
    if ((salines = sarrayCreateLinesFromString(textstr, 1)) == NULL)
1023
0
        return (SARRAY *)ERROR_PTR("salines not made", __func__, NULL);
1024
0
    nlines = sarrayGetCount(salines);
1025
0
    saout = sarrayCreate(0);
1026
0
    satemp = sarrayCreate(0);
1027
1028
0
    linestr = sarrayGetString(salines, 0, L_NOCOPY);
1029
0
    sarrayAddString(satemp, linestr, L_COPY);
1030
0
    for (i = 1; i < nlines; i++) {
1031
0
        linestr = sarrayGetString(salines, i, L_NOCOPY);
1032
0
        stringAllWhitespace(linestr, &allwhite);
1033
0
        stringLeadingWhitespace(linestr, &leadwhite);
1034
0
        if ((splitflag == SPLIT_ON_LEADING_WHITE && leadwhite) ||
1035
0
            (splitflag == SPLIT_ON_BLANK_LINE && allwhite) ||
1036
0
            (splitflag == SPLIT_ON_BOTH && (allwhite || leadwhite))) {
1037
0
            parastring = sarrayToString(satemp, 1);  /* add nl to each line */
1038
0
            sarrayAddString(saout, parastring, L_INSERT);
1039
0
            sarrayDestroy(&satemp);
1040
0
            satemp = sarrayCreate(0);
1041
0
        }
1042
0
        sarrayAddString(satemp, linestr, L_COPY);
1043
0
    }
1044
0
    parastring = sarrayToString(satemp, 1);  /* add nl to each line */
1045
0
    sarrayAddString(saout, parastring, L_INSERT);
1046
0
    sarrayDestroy(&satemp);
1047
0
    sarrayDestroy(&salines);
1048
0
    return saout;
1049
0
}
1050
1051
1052
/*!
1053
 * \brief   stringAllWhitespace()
1054
 *
1055
 * \param[in]    textstr   text string
1056
 * \param[out]   pval      1 if all whitespace; 0 otherwise
1057
 * \return  0 if OK, 1 on error
1058
 */
1059
static l_int32
1060
stringAllWhitespace(char     *textstr,
1061
                    l_int32  *pval)
1062
0
{
1063
0
l_int32  len, i;
1064
1065
0
    if (!textstr)
1066
0
        return ERROR_INT("textstr not defined", __func__, 1);
1067
0
    if (!pval)
1068
0
        return ERROR_INT("&va not defined", __func__, 1);
1069
1070
0
    len = strlen(textstr);
1071
0
    *pval = 1;
1072
0
    for (i = 0; i < len; i++) {
1073
0
        if (textstr[i] != ' ' && textstr[i] != '\t' && textstr[i] != '\n') {
1074
0
            *pval = 0;
1075
0
            return 0;
1076
0
        }
1077
0
    }
1078
0
    return 0;
1079
0
}
1080
1081
1082
/*!
1083
 * \brief   stringLeadingWhitespace()
1084
 *
1085
 * \param[in]    textstr   text string
1086
 * \param[out]   pval      1 if leading char is [space] or [tab]; 0 otherwise
1087
 * \return  0 if OK, 1 on error
1088
 */
1089
static l_int32
1090
stringLeadingWhitespace(char     *textstr,
1091
                        l_int32  *pval)
1092
0
{
1093
0
    if (!textstr)
1094
0
        return ERROR_INT("textstr not defined", __func__, 1);
1095
0
    if (!pval)
1096
0
        return ERROR_INT("&va not defined", __func__, 1);
1097
1098
0
    *pval = 0;
1099
0
    if (textstr[0] == ' ' || textstr[0] == '\t')
1100
0
        *pval = 1;
1101
1102
0
    return 0;
1103
0
}