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