X11workbench Toolkit  1.0
draw_text.c
Go to the documentation of this file.
1 // _ _ _ //
3 // __| | _ __ __ _ __ __ | |_ ___ __ __| |_ ___ //
4 // / _` || '__|/ _` |\ \ /\ / / | __|/ _ \\ \/ /| __| / __| //
5 // | (_| || | | (_| | \ V V / | |_| __/ > < | |_ _| (__ //
6 // \__,_||_| \__,_| \_/\_/_____\__|\___|/_/\_\ \__|(_)\___| //
7 // |_____| //
8 // //
9 // text draw, multi-line and tabbed text //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17 
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  MIT-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  MIT-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <memory.h>
60 #include <string.h>
61 #include <strings.h>
62 
63 #include "window_helper.h" // this also includes 'font_helper.h'
64 
65 #include "draw_text.h"
66 #include "pixmap_helper.h" // for anti-alias functions, etc.
67 
68 
69 // function prototypes
70 
71 
94 static int InternalCalcIdealBounds(Display *pDisplay, WB_FONTC pFont, DT_WORDS *pWords, int iTabWidth, unsigned int iTabOrigin,
95  const WB_RECT *prcSource, WB_RECT *prcDest, int iAlignment, int iStartLine, int iEndLine);
96 
97 
98 static void InternalDebugDumpWords(DT_WORDS *pWords);
99 
100 
101 static void __internalDoAntiAlias(Display *pDisplay, Drawable dw, WBGC gc, int iX, int iY, int iWidth, int iHeight);
102 
103 
104 // *******************
105 // DRAW TEXT UTILITIES
106 // *******************
107 
108 // abstracting the 'draw text' process
109 // NOTE: this is like WBDrawString but you specify a font without assigning it to the WBGC
110 
111 void DTDrawString(Display *pDisplay, Drawable drawable, WB_FONTC pFont,
112  WBGC gc, int x, int y, const char *pString, int nLength)
113 {
114 char tbuf[1024];
115 char *pS;
116 WB_EXTENT ext;
117 XImage *pImage;
118 
119 //#if defined(X_HAVE_UTF8_STRING)
120 //#define DO_DRAW_STRING Xutf8DrawString
121 //#else // use 'multi-byte char' equivalent
122 //#define DO_DRAW_STRING XmbDrawString
123 //#endif // UTF8 vs multi-byte
124 
125 
126  // NOTE: invokes WB_DRAW_STRING macro, which (for X11) is either Xutf8DrawString() or XmbDrawString()
127  // depending on your system's configuration (more likely the first one)
128 
129  if(!pDisplay || !pString || drawable == None || !pFont || !gc || nLength <= 0)
130  {
131  if(pString && nLength > 0)
132  {
133  WB_ERROR_PRINT("ERROR: %s.%d returning error for \"%-.*s\"\n", __FUNCTION__, __LINE__, nLength, pString);
134  }
135  else
136  {
137  WB_ERROR_PRINT("ERROR: %s.%d returning error for %d,%p\n", __FUNCTION__, __LINE__, nLength, pString);
138  }
139 
140  return; // don't allow this. just don't.
141  }
142 
143 // TODO: do I allow this??
144 // if(nLength < 0) // negative length - use strlen
145 // {
146 // nLength = strlen(pString);
147 // }
148 
149  pImage = WBGetWindowImage(pDisplay, drawable);
150 
151  if(pImage) // cached image - use WBXDrawString to do the work
152  {
153  WBXDrawString(pImage, pFont, gc, x, y, pString, nLength);
154  // NOTE: does not update immediately, but after you 'end paint'
155 
156  return;
157  }
158 
159 
160  // do THIS part because 'pString' is 'const char *' and X*DrawString wants 'char *'
161  // and so 'pS' becomes a copy of the string, in writeable memory.
162 
163  if(WB_LIKELY(nLength <= sizeof(tbuf)))
164  {
165 use_the_stack_buffer:
166 
167  // use my temporary buffer for small strings [the most common thing]
168 
169  if(WB_LIKELY(nLength > 0))
170  {
171  memcpy(tbuf, pString, nLength);
172  }
173 
174  pS = &(tbuf[0]);
175  }
176  else
177  {
178  pS = WBAlloc(nLength);
179  if(!pS)
180  {
181  nLength = sizeof(tbuf); // shorten the string and do it anyway, but not all of it. desperate, yeah.
182  goto use_the_stack_buffer;
183  }
184 
185  memcpy(pS, pString, nLength);
186  }
187 
188 
189  // TODO: An alternate method of anti-aliasing
190  // make a font that is twice as big, render it on large-enough pixmap, and then
191  // shrink it down onto the display surface with a raster operation that combines
192  // pixels and (effectively) gives it an anti-aliasing effect.
193 
194 
195 #ifdef X11WORKBENCH_TOOLKIT_HAVE_XFT
196  if(pFont && pFont->pxftFont)
197  {
198 #warning TODO: IMPLEMENT THIS PART for Xft FONTS
199  }
200  else
201 #endif // X11WORKBENCH_TOOLKIT_HAVE_XFT
202  {
203  if(pFont->fsFont)
204  {
205  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
206  "%s.%d using WB_DRAW_STRING for \"%-.*s\" color=#%08lxH bkgnd=#%08lxH\n",
207  __FUNCTION__, __LINE__, nLength, pString,
209 
211  WB_DRAW_STRING(pDisplay, drawable, pFont->fsFont, gc->gc, x, y, pS, nLength); // for now, just do this
213 
215  {
216  WBTextExtent(pFont, pS, nLength, &ext);
217 
218  // NOTE: the x and y are for bottom left when the function was called, not top left (as with specifying a rect or geom)
219  __internalDoAntiAlias(pDisplay, drawable, gc, x, y - ext.height, ext.width, ext.height);
220  }
221  }
222  else
223  {
224  XGCValues gcv, gcv2;
225 
226  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
227  "%s.%d using XDrawString for \"%-.*s\" color=#%08lxH bkgnd=#%08lxH\n",
228  __FUNCTION__, __LINE__, nLength, pString,
230 
231  if(pFont->pFontStruct) // legacy font? switch gc to use this
232  {
233  memset(&gcv, 0, sizeof(gcv));
234  memset(&gcv2, 0, sizeof(gcv2));
235 
237  XGetGCValues(gc->display, gc->gc, GCFont, &gcv);
239 
240  memcpy(&gcv2, &gcv, sizeof(gcv));
241 
242  // if GC font is not this one, assign it to this one
243  if(gcv2.font != pFont->pFontStruct->fid)
244  {
245  gcv2.font = pFont->pFontStruct->fid; // new font ID
246 
248  XChangeGC(gc->display, gc->gc, GCFont, &gcv2);
250  }
251  }
252 
254  XDrawString(pDisplay, drawable, gc->gc, x, y, pS, nLength); // for now, just do this
256 
257  if(pFont->pFontStruct)
258  {
259  // put the font back
260 
262  XChangeGC(gc->display, gc->gc, GCFont, &gcv);
264  }
265  }
266  }
267 
268  // free up buffer if I allocated it
269  if(pS && pS != &(tbuf[0]) && pS != (char *)pString)
270  {
271  WBFree(pS);
272  }
273 }
274 
275 // determine ideal font size from desired text and geometry
276 
277 WB_FONT DTCalcIdealFont(Display *pDisplay, WB_FONTC pRefFont, const char *szText, WB_GEOM *geomBounds)
278 {
279  if(!pRefFont)
280  {
281  pRefFont = WBGetDefaultFont();
282  }
283 
284 
285 
286 
287  return NULL; // for now
288 }
289 
290 
291 #define INITIAL_DT_WORDS_COUNT 256
292 
293 static int CheckReAllocWords(DT_WORDS **ppWords)
294 {
295 DT_WORDS *pRval = *ppWords;
296 
297  if(pRval->nCount + 1 >= pRval->nMax)
298  {
299  int iNewLen = sizeof(*pRval)
300  + ((pRval->nMax & 0xffffff80) + 127) * sizeof(DT_WORD);
301 
302  void *p3 = WBReAlloc(pRval, iNewLen);
303 
304  if(!p3)
305  {
306  WB_ERROR_PRINT("%s - ERROR: not enough memory to allocate new struct (%d required)\n",
307  __FUNCTION__, iNewLen);
308 
309  return 0;
310  }
311 
312  pRval = (DT_WORDS *)p3;
313 //#ifdef HAVE_MALLOC_USABLE_SIZE
314  pRval->nMax = (WBAllocUsableSize(pRval) - sizeof(DT_WORDS))
315  / sizeof(DT_WORD)
316  + 1; // because DT_WORDS contains one DT_WORD
317 //#else // HAVE_MALLOC_USABLE_SIZE
318  pRval->nMax = (pRval->nMax & 0xffffff80) + 128;
319 //#endif // HAVE_MALLOC_USABLE_SIZE
320  }
321 
322  *ppWords = pRval;
323  return 1; // OK
324 }
325 
326 // NOTES ON UTF-8 AND INTERNATIONAL CHARACTERS
327 // notes from http://en.wikipedia.org/wiki/UTF-8
328 //
329 // # of bits highest Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
330 // -----------------------------------------------------------------------------
331 // 7 bits U+007F 0xxxxxxx
332 // 11 bits U+07FF 110xxxxx 10xxxxxx
333 // 16 bits U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
334 // 21 bits U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
335 // 26 bits U+3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
336 // 31 bits U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
337 //
338 // Because of THIS, it is STILL SAFE to look for <= ' ' for white space, and make some assumptions
339 // about 0-byte, \n, \r, \t, and other control characters (for now, anyway).
340 //
341 // RFC 3629 states "Implementations of the decoding algorithm MUST protect against decoding invalid sequences."
342 // invalid sequences could (in theory) cause burping to the point of security risk.
343 
344 // There are also some "print direction" override characters 202C 202D and 202E .
345 // http://www.unicodemap.org/details/0x202C/index.html (POP Directional Formatting)
346 // http://www.unicodemap.org/details/0x202D/index.html (LTR Override)
347 // http://www.unicodemap.org/details/0x202E/index.html (RTL Override)
348 //
349 // also http://www.unicodemap.org/details/0x200E/index.html (LTR mark)
350 // http://www.unicodemap.org/details/0x200E/index.html (RTL mark)
351 // http://www.unicodemap.org/details/0x202A/index.html (LTR embedding)
352 // http://www.unicodemap.org/details/0x202B/index.html (RTL embedding)
353 //
354 // and these:
355 // http://www.unicodemap.org/details/0x2028/index.html (line separator - alternate LF?)
356 // http://www.unicodemap.org/details/0x202A/index.html (paragraph separator - alternate to CTRL+L ?)
357 //
358 // There are also a handful of various width 'spacing' including NO WIDTH SPACE
359 // 206A through 206F apparently affect "shaping". 205F through 2069 apparently do nothing
360 //
361 // If I pay attention to these, it could put some serious strain on everything. If I don't, it's not really UTF-8
362 // However the focus of THIS project is "only implement what is necessary" (and forget the rest).
363 
364 
365 DT_WORDS * DTGetWordsFromText(Display *pDisplay, WB_FONTC pFont, const char *szText, int iAlignment)
366 {
367  const char *p1, *p2;
368 
369  DT_WORDS *pRval = WBAlloc(sizeof(DT_WORDS) + sizeof(DT_WORD) * (INITIAL_DT_WORDS_COUNT - 1));
370 
371  if(!pRval)
372  {
373  return NULL;
374  }
375 
376  pRval->nCount = 0;
377  pRval->szText = szText;
378 //#ifdef HAVE_MALLOC_USABLE_SIZE
379  pRval->nMax = (WBAllocUsableSize(pRval) - sizeof(DT_WORDS)) // calculate actual # of structs it contains
380  / sizeof(DT_WORD)
381  + 1; // because DT_WORDS contains one DT_WORD
382 //#else // HAVE_MALLOC_USABLE_SIZE
383  pRval->nMax = INITIAL_DT_WORDS_COUNT;
384 //#endif // HAVE_MALLOC_USABLE_SIZE
385 
386  if(!szText || !*szText)
387  {
388  return pRval;
389  }
390 
391  p1 = szText;
392  while(*p1)
393  {
394  // find non-white-space and keep 'tabs' on the count/type of white space as well
395  // adding entries to 'words' as needed
396 
397  while(*p1 && (unsigned char)*p1 <= ' ')
398  {
399  // TODO: check for high bit set aka UTF-8 multi-byte chars
400 
401  if(*p1 == '\t') // tab character
402  {
403  if(!CheckReAllocWords(&pRval))
404  {
405  WBFree(pRval);
406  return NULL; // ERROR
407  }
408 
409  pRval->aWords[pRval->nCount].pText = NULL;
410  pRval->aWords[pRval->nCount].nLength = 1;
411  pRval->aWords[pRval->nCount].iIsWhiteSpace = 0;
412  pRval->aWords[pRval->nCount].iIsLineFeed = 0;
413  pRval->aWords[pRval->nCount].iIsTab = 1;
414  pRval->aWords[pRval->nCount].iX = -1;
415  pRval->aWords[pRval->nCount].iY = -1;
416 
417  while(*(p1 + 1) == '\t')
418  {
419  p1++;
420  pRval->aWords[pRval->nCount].nLength++;
421  }
422 
423  // use white space string for width
424  pRval->aWords[pRval->nCount].iWidth = 0; // NA (calc on the fly)
425  pRval->aWords[pRval->nCount].iHeight = 0; // NA (calc on the fly)
426 
427  pRval->nCount++;
428  }
429  else if(*p1 == '\n' || *p1 == '\r')
430  {
431  if(!CheckReAllocWords(&pRval))
432  {
433  WBFree(pRval);
434  return NULL; // ERROR
435  }
436 
437  pRval->aWords[pRval->nCount].pText = NULL;
438  pRval->aWords[pRval->nCount].nLength = 1;
439 
440  if(iAlignment & DTAlignment_SINGLELINE) // treat line feed as white space
441  {
442  pRval->aWords[pRval->nCount].iIsWhiteSpace = 1;
443  pRval->aWords[pRval->nCount].iIsLineFeed = 0;
444  }
445  else
446  {
447  pRval->aWords[pRval->nCount].iIsWhiteSpace = 0;
448  pRval->aWords[pRval->nCount].iIsLineFeed = 1;
449  }
450 
451  pRval->aWords[pRval->nCount].iIsTab = 0;
452  pRval->aWords[pRval->nCount].iX = -1;
453  pRval->aWords[pRval->nCount].iY = -1;
454 
455  while(1)
456  {
457  if(*p1 == '\n' && p1[1] == '\r')
458  {
459  p1++;
460  }
461  else if(*p1 == '\r' && p1[1] == '\n')
462  {
463  p1++;
464  }
465 
466 // pRval->aWords[pRval->nCount].nLength++;
467 
468  if(p1[1] != '\r' && p1[1] != '\n')
469  {
470  break;
471  }
472 
473  p1++; // loop consecutive CR,LF
474  pRval->aWords[pRval->nCount].nLength++;
475  }
476 
477  // use white space string for width
478  pRval->aWords[pRval->nCount].iWidth = 0; // NA
479  pRval->aWords[pRval->nCount].iHeight = 0; // NA
480 
481  pRval->nCount++;
482  }
483  else if((unsigned char)*p1 <= ' ')
484  {
485  if(!CheckReAllocWords(&pRval))
486  {
487  WBFree(pRval);
488  return NULL; // ERROR
489  }
490 
491  pRval->aWords[pRval->nCount].pText = NULL;
492  pRval->aWords[pRval->nCount].nLength = 1;
493  pRval->aWords[pRval->nCount].iIsWhiteSpace = 1;
494  pRval->aWords[pRval->nCount].iIsLineFeed = 0;
495  pRval->aWords[pRval->nCount].iIsTab = 0;
496  pRval->aWords[pRval->nCount].iX = -1;
497  pRval->aWords[pRval->nCount].iY = -1;
498 
499  p2 = p1;
500 
501  while(*(p1 + 1) && (unsigned char)*(p1 + 1) <= ' ' && *(p1 + 1) != '\t' && *(p1 + 1) != '\r' && *(p1 + 1) != '\n')
502  {
503  p1++;
504  pRval->aWords[pRval->nCount].nLength++;
505  }
506 
507  // use white space string for width
508  pRval->aWords[pRval->nCount].iWidth = WBTextWidth(pFont, p2, pRval->aWords[pRval->nCount].nLength);
509  pRval->aWords[pRval->nCount].iHeight = WBFontHeight(pFont); // total height of font
510 
511  pRval->nCount++;
512  }
513 
514  p1++;
515  }
516 
517  if(*p1)
518  {
519  int iUnderscoreFlag = 0;
520 
521  // get length of word
522 
523  p2 = p1;
524  while(/* *p1 && */ (unsigned char)*p1 > ' ')
525  {
526  if(*p1 == '_')
527  {
528  iUnderscoreFlag++;
529  }
530 
531  p1++;
532  }
533 
534  if(!CheckReAllocWords(&pRval))
535  {
536  WBFree(pRval);
537  return NULL; // ERROR
538  }
539 
540  pRval->aWords[pRval->nCount].pText = p2;
541  pRval->aWords[pRval->nCount].nLength = p1 - p2;
542  pRval->aWords[pRval->nCount].iIsWhiteSpace = 0;
543  pRval->aWords[pRval->nCount].iIsTab = 0;
544  pRval->aWords[pRval->nCount].iIsLineFeed = 0;
545  pRval->aWords[pRval->nCount].iX = -1;
546  pRval->aWords[pRval->nCount].iY = -1;
547 
548  if(p1 > p2)
549  {
550  if(!(iAlignment & DTAlignment_UNDERSCORE) || !iUnderscoreFlag)
551  {
552  pRval->aWords[pRval->nCount].iWidth = WBTextWidth(pFont, p2, p1 - p2);
553  }
554  else
555  {
556  // fast and dirty, subtract "length of underscores" from text length
557  // assume kerning doesn't alter this at all
558 
559  // TODO: the hard way?
560 
561  pRval->aWords[pRval->nCount].iWidth = WBTextWidth(pFont, p2, p1 - p2)
562  - iUnderscoreFlag * WBTextWidth(pFont, "_", 1);
563  }
564  }
565  else
566  {
567  pRval->aWords[pRval->nCount].iWidth = 0;
568  }
569 
570  pRval->aWords[pRval->nCount].iHeight = WBFontHeight(pFont);//pFont->ascent + pFont->descent; // for now just do this
571 
572  pRval->nCount++;
573  }
574  }
575 
576  return pRval;
577 }
578 
579 int DTCalcIdealBounds(Display *pDisplay, WB_FONTC pFont, const char *szText, int iTabWidth, unsigned int iTabOrigin,
580  const WB_RECT *prcSource, WB_RECT *prcDest, int iAlignment)
581 {
582 int iRval;
583 DT_WORDS *pWords;
584 
585 
586  pWords = DTGetWordsFromText(pDisplay, pFont, szText, iAlignment);
587 
588  if(!pWords)
589  {
590  if(prcDest)
591  prcDest->left = prcDest->right = prcDest->top = prcDest->bottom = 0;
592 
593  return -1; // error
594  }
595 
596 // InternalDebugDumpWords(pWords);
597 
598  iRval = InternalCalcIdealBounds(pDisplay, pFont, pWords, iTabWidth, iTabOrigin, prcSource, prcDest, iAlignment, 0, -1);
599 
600  WBFree(pWords);
601 
602  return iRval;
603 }
604 
605 
606 static void DoHCenterWrappedLines(DT_WORDS *pWords, int iWidth)
607 {
608 int i1, i2, i3, iMax;
609 DT_WORD *pW;
610 
611 
612  // For a multi-line string, I need to make sure each line centers
613  // correctly. If the lines break, I allow them to break and wrap
614  // but each line that is COMPLETELY within the centered rectangle
615  // without wrapping MUST BE RE-CENTERED!!!
616 
617  i1 = 0; // this will be my 'word counter'
618 
619  do
620  {
621  i2 = i1; // start of line
622  iMax = 0;
623 
624  // step 1: find the end of the current line and calculate
625  // the max width of the line (including 'wrap')
626 
627  while(i1 < pWords->nCount)
628  {
629  pW = &(pWords->aWords[i1]);
630  i1++;
631 
632  if(pW->iIsLineFeed)
633  {
634  break; // i1 already incremented, so it will skip Mr. Line Feed on the next iteration
635  }
636 
637  if(!pW->iIsTab && !pW->iIsWhiteSpace)
638  {
639  i3 = pW->iX + pW->iWidth; // max extent
640 
641  if(i3 > iMax)
642  {
643  iMax = i3;
644  }
645  }
646  }
647 
648  // if 'iMax' is less than 'iWidth', add half the delta
649  // to all of the 'iX' values
650 
651  i3 = (iWidth - iMax) / 2;
652 
653  if(i3 > 0)
654  {
655  for(; i2 < i1; i2++)
656  {
657  pW = &(pWords->aWords[i2]);
658 
659  if(pW->iX >= 0) // only for the 'assigned' ones
660  {
661  pW->iX += i3;
662  }
663  }
664  }
665 
666  } while(i1 < pWords->nCount);
667 
668 
669 // InternalDebugDumpWords(pWords);
670 }
671 
672 
673 
675 // * parameters to this function
676 // *
677 // * pFont A pointer to an XFontStruct (NULL implies system default font)
678 // * pWords The DT_WORDS structure for the text
679 // * iTabWidth A positive integer in 'characters', or negative integer in pixels, indicating tab width
680 // * iTabOrgin An unsigned integer indicating the tab origin, using the same units as iTabWidth, corresponding to the first character.
681 // * prcSource A pointer to the 'source' bounding rectangle in which the text is intended to fit
682 // * prcDest A pointer to the 'destination' bounding rectangle, based on the actual text size
683 // * iAlignment The desired text alignment, one or more of the DTAlignment bit flags
684 // * iStartLine A zero-based index for the first line on which to start calculating things
685 // * iEndLine A zero-based index for the last line on which to start calculating things (-1 for 'all')
686 // * zero if the text will fit within prcSource, -1 error, or 1 to indicate that prcDest is larger than prcSource
687 
688 static int InternalCalcIdealBounds(Display *pDisplay, WB_FONTC pFont, DT_WORDS *pWords, int iTabWidth, unsigned int iTabOrigin,
689  const WB_RECT *prcSource, WB_RECT *prcDest, int iAlignment, int iStartLine, int iEndLine)
690 {
691 int iFontWidth, iFontHeight, iFontAscent, iFontDescent, iLineSpacing; // average font width/height and line spacing
692 int iHPos, iHPos0, iMaxLen, iLines;
693 int i1, i2, i3;
694 //const char *p1;
695 WB_RECT rctBounds, rctSource;
696 DT_WORD *pW, *pW2;
697 
698 
699  if(!pWords || (!prcSource && !prcDest))
700  {
701  WB_ERROR_PRINT("%s - returns ERROR (bad values pWords=%p, prcSource=%p, prcDest=%p)\n",
702  __FUNCTION__, pWords, prcSource, prcDest);
703  return -1;
704  }
705  else if(prcSource)
706  {
707  rctSource.left = prcSource->left;
708  rctSource.top = prcSource->top;
709  rctSource.right = prcSource->right;
710  rctSource.bottom = prcSource->bottom;
711  }
712  else
713  {
714  bzero(&rctSource, sizeof(rctSource));
715  }
716 
717  memcpy(&rctBounds, &rctSource, sizeof(rctBounds)); // make copy of it as 'rctSource' (for now)
718 
719  if(!pFont)
720  {
721  pFont = WBGetDefaultFont();
722  if(!pFont)
723  {
724  WB_ERROR_PRINT("%s - returns ERROR (no font)\n", __FUNCTION__);
725  return -1;
726  }
727  }
728 
729 // // TEMPORARY
730 // WB_ERROR_PRINT("%s - TEMPORARY: bounds rectangle is INITIALLY %d,%d,%d,%d\n",
731 // __FUNCTION__, rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom);
732 
733  // TODO: make use of iStartLine and iEndLine - for now "do all" every time so I can avoid a re-write
734 
735  // get a few things straight 'round here
736 
737  iFontWidth = WBFontAvgCharWidth(pFont);
738 
739  if(!iFontWidth)
740  {
741  iFontWidth = WBTextWidth(pFont, " ", 1); // width of a single space
742  }
743 
744  iFontAscent = WBFontAscent(pFont);
745  iFontDescent = WBFontDescent(pFont);
746  iFontHeight = iFontAscent + iFontDescent; // cache for performance
747 
748  // for now, line spacing equals 1/2 descent or 2 pixels
749  iLineSpacing = iFontDescent / 2;
750  if(iLineSpacing < 2)
751  {
752  iLineSpacing = 2;
753  }
754 
755  if(iTabWidth < 0)
756  {
757  iTabWidth = -iTabWidth; // in pixels
758  }
759  else
760  {
761  iTabOrigin *= iFontWidth;
762  iTabWidth *= iFontWidth; // convert chars to pixels
763  }
764 
765  // step 1: trial fit within the current rectangle
766 
767  iHPos0 = iHPos = rctSource.left;
768 
769  iMaxLen = 0;
770  iLines = 0;
771 
772  // initial text width and # of lines calculation.
773 
774  for(i1=0; i1 < pWords->nCount; i1++)
775  {
776  pW = &(pWords->aWords[i1]);
777 
778  if(pW->iIsWhiteSpace)
779  {
780  pW->iY = pW->iX = -1; // NA
781 
782  // before I start adding white space, see if there is
783  // any NON-WHITE SPACE following this entry...
784 
785  if(iAlignment & DTAlignment_ALLOW_TRAILING)
786  {
787  iHPos += pW->iWidth; // it's ok to use the TOTAL width of this white space
788  }
789  else
790  {
791  for(i2=i1 + 1; i2 < pWords->nCount; i2++)
792  {
793  pW2 = &(pWords->aWords[i2]);
794 
795  if(!pW2->iIsWhiteSpace && !pW2->iIsTab && !pW2->iIsLineFeed)
796  {
797  iHPos += pW->iWidth; // it's ok to use the width of this white space
798  break;
799  }
800  else if(pW2->iIsLineFeed)
801  {
802  break; // end of line (trailing white space will be ignored)
803  }
804  }
805  }
806  }
807  else if(pW->iIsTab)
808  {
809  pW->iY = pW->iX = -1; // NA
810 
811  // before I start adding white space for tabs, see if there is
812  // any NON-WHITE SPACE following this entry...
813 
814  if(iAlignment & DTAlignment_ALLOW_TRAILING)
815  {
816 do_the_tab:
817  if(iAlignment & (DTAlignment_HCENTER | DTAlignment_HJUSTIFY)) // no tabbing for center/justify
818  {
819  iHPos += pW->nLength * iFontWidth; // treat it as regular white space
820  }
821  else
822  {
823  i3 = (iHPos - iHPos0 + iTabOrigin) % iTabWidth; // remaining space to tab over
824 
825  if(!i3)
826  {
827  iHPos += iTabWidth; // tab out the entire distance
828  }
829  else
830  {
831  iHPos += i3; // tab out "the remainder"
832  }
833 
834  if(pW->nLength > 1) // check for multiple tabs
835  {
836  iHPos += iTabWidth * (pW->nLength - 1); // additional tabs
837  }
838  }
839  }
840  else
841  {
842  for(i2=i1 + 1; i2 < pWords->nCount; i2++)
843  {
844  pW2 = &(pWords->aWords[i2]);
845  pW2->iX = pW2->iY = -1; // because I'm skipping them
846 
847  if(!pW2->iIsWhiteSpace && !pW2->iIsTab && !pW2->iIsLineFeed)
848  {
849  goto do_the_tab; // cleaner code with label
850  }
851  else if(pW2->iIsLineFeed)
852  {
853  break; // end of line (trailing tabs will be ignored)
854  }
855  }
856 
857  // if I get here, only white space remains at the end of the DT_WORD array. Ignore it.
858  break;
859  }
860  }
861  else if(pW->iIsLineFeed)
862  {
863  pW->iY = pW->iX = -1; // NA
864 
865  if(iMaxLen < iHPos - iHPos0)
866  {
867  iMaxLen = iHPos - iHPos0;
868  }
869 
870  if(iHPos > iHPos0 && // there is actual text present on this line
871  (i1 + 1) == pWords->nCount) // the last entry? The text ends in one or more line feeds
872  {
873  // TODO: see if it's just a bunch of blank lines following the text
874  // and consider ignoring them ALL if it makes sense to do so
875 
876  iLines += pW->nLength - 1; // at the end, the final line feed doesn't "count" unless it's by itself
877  }
878  else
879  {
880  iLines += pW->nLength; // add ALL of the line feeds to the line count (more to come)
881  }
882 
883  iHPos = iHPos0;
884  }
885  else
886  {
887  if(!iLines) // this handles blank text, which will show up with "zero lines"
888  {
889  iLines++; // indicate at least one line of printable stuff
890  }
891 
892  pW->iX = iHPos - iHPos0; // relative X position
893  pW->iY = (iLines - 1) * iFontHeight + iFontAscent; // relative Y position for 'base of text'
894 
895  iHPos += pW->iWidth;
896  }
897 
898  if(iHPos > iMaxLen)
899  {
900  iMaxLen = iHPos;
901  }
902  }
903 
904  // Now I know the current (unmodified) text extents. Let's see what they SHOULD be
905 
906  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
907  "%s.%d - iHPos=%d, iMaxLen=%d, iLines=%d, iFontHeight=%d, *=%d\n", __FUNCTION__, __LINE__,
908  iHPos, iMaxLen, iLines, iFontHeight, (iLines * iFontHeight));
909 
910  rctBounds.left = rctSource.left;
911  rctBounds.top = rctSource.top;
912  rctBounds.right = /*rctBounds.left +*/ iMaxLen;
913  rctBounds.bottom = rctBounds.top + (iLines * iFontHeight);
914 
915  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
916  "%s.%d - rctSource %d,%d,%d,%d rctBounds: %d,%d,%d,%d\n", __FUNCTION__, __LINE__,
917  rctSource.left, rctSource.top, rctSource.right, rctSource.bottom,
918  rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom);
919 
920  if(iLines > 1)
921  {
922  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
923  "%s.%d - %d lines - font height %d, line spacing %d\n",
924  __FUNCTION__, __LINE__, iLines, iFontHeight, iLineSpacing);
925  rctBounds.bottom += (iLines - 1) * iLineSpacing; // inter-line spacing
926  }
927 
928 #ifndef NO_DEBUG
929 
930  if(rctBounds.top < rctSource.top || rctBounds.bottom > rctSource.bottom)
931  {
932  WB_ERROR_PRINT("WARNING: %s.%d - source rectangle too small, alignment flags may not work\n", __FUNCTION__, __LINE__);
933  WB_ERROR_PRINT(" %d,%d,%d,%d bounds vs source %d,%d,%d,%d\n",
934  rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom,
935  rctSource.left, rctSource.top, rctSource.right, rctSource.bottom);
936  WB_ERROR_PRINT(" Lines: %d Font ascent=%d, descent=%d, height=%d, line spacing %d\n",
937  iLines, iFontAscent, iFontDescent, iFontHeight, iLineSpacing);
938  }
939  else
940  {
941  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_DrawText,
942  "INFO: %s - source rectangle seems to fit\n", __FUNCTION__);
943  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_DrawText,
944  " %d,%d,%d,%d bounds vs source %d,%d,%d,%d\n",
945  rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom,
946  rctSource.left, rctSource.top, rctSource.right, rctSource.bottom);
947  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_DrawText,
948  " Lines: %d Font ascent=%d, descent=%d, height=%d, line spacing %d\n",
949  iLines, iFontAscent, iFontDescent, iFontHeight, iLineSpacing);
950  }
951 
952 #endif // NO_DEBUG
953 
954  if(rctBounds.right <= rctSource.right &&
955  rctBounds.bottom <= rctSource.bottom)
956  {
957 it_fits:
958  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_DrawText,
959  "%s.%d - 'it fits' bounds rectangle is %d,%d,%d,%d iAlighment=%d (%08xH)\n",
960  __FUNCTION__, __LINE__,
961  rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom,
962  iAlignment, iAlignment);
963 
964  // it looks like it can fit properly without any additional effort.
965  // based on the alignment flags, I will return now with the results.
966 
967  // TODO: check if any alignment flags might prevent me from returning now...
968 
969  if(prcDest) // caller wants the actual rectangle
970  {
971  if(iAlignment & DTAlignment_NO_SHRINK)
972  {
973  rctBounds.right = rctSource.right;
974  rctBounds.bottom = rctSource.bottom;
975  }
976  else
977  {
978  switch(iAlignment & DTAlignment_HMASK)
979  {
980  case DTAlignment_HJUSTIFY: // treat like 'center' for now
981  case DTAlignment_HCENTER:
982  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
983  "%s.%d - here I am, %d, %d, %d, %d\n", __FUNCTION__, __LINE__,
984  rctSource.left, rctSource.right, rctBounds.left, rctBounds.right);
985 
986  i1 = rctSource.right - rctBounds.right;
987  i1 -= (i1 >> 1); // so that it's balanced properly, do it THIS way
988  rctBounds.left += i1;
989  rctBounds.right += i1;
990 
991  // For a multi-line string, I need to make sure each line centers
992  // correctly. If the lines break, I allow them to break and wrap
993  // but each line that is COMPLETELY within the centered rectangle
994  // without wrapping MUST BE RE-CENTERED!!!
995 
996  DoHCenterWrappedLines(pWords, rctBounds.right - rctBounds.left);
997 
998  break;
999 
1000  case DTAlignment_HLEFT:
1001  break; // no changes
1002  case DTAlignment_HRIGHT:
1003  rctBounds.left += rctSource.right - rctBounds.right;
1004  rctBounds.right = rctSource.right;
1005  break;
1006  }
1007  }
1008 
1009  switch(iAlignment & DTAlignment_VMASK)
1010  {
1011  case DTAlignment_VTOP:
1012  break;
1013  case DTAlignment_VBOTTOM:
1014  rctBounds.top += rctSource.bottom - rctBounds.bottom;
1015  rctBounds.bottom = rctSource.bottom;
1016  break;
1017  case DTAlignment_VCENTER:
1018  i1 = ((rctSource.bottom - rctBounds.bottom + 1) >> 1) - 1; // favors being slightly closer to top
1019  rctBounds.top += i1;
1020  rctBounds.bottom += i1;
1021  break;
1022 
1023  case DTAlignment_VCENTERASCENT: // only works properly with single line
1024  i2 = iFontDescent >> 1; // 1/2 ascent is top + a / 2; 1/2 font is top + (a + d) / 2; diff is d / 2
1025  i1 = ((rctSource.bottom - rctBounds.bottom + 1) >> 1) - 1; // favors being slightly closer to top
1026  i1 -= i2;
1027  if(i1 > 0) // subtracting i2 from i1 didn't make it negative
1028  {
1029  rctBounds.top += i1;
1030  rctBounds.bottom += i1;
1031  }
1032  break;
1033 
1035  i2 = (iFontAscent - iFontDescent) >> 1; // ascent is top + a; 1/2 font is top + (a + d) / 2; diff is (a - d) / 2
1036  i1 = ((rctSource.bottom - rctBounds.bottom + 1) >> 1) - 1; // favors being slightly closer to top
1037  i1 += i2;
1038  if(i1 < rctSource.bottom) // adding i2 won't exceed 'bottom'
1039  {
1040  rctBounds.top += i1;
1041  rctBounds.bottom += i1;
1042  }
1043  else // adding i2 exceeds bottom, so just use bottom
1044  {
1045  rctBounds.top += rctSource.bottom - rctBounds.bottom;
1046  rctBounds.bottom = rctSource.bottom;
1047  }
1048  break;
1049 
1050  case DTAlignment_VTOPHALF:
1052  break; // reserved
1053  }
1054 
1055 // WB_ERROR_PRINT("%s - TEMPORARY: 'aligned' bounds rectangle is %d,%d,%d,%d\n",
1056 // __FUNCTION__, rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom);
1057 
1058  memcpy(prcDest, &rctBounds, sizeof(*prcDest));
1059  }
1060 
1061  return 0;
1062  }
1063 
1064  // the simplest solutions are usually the best. If the text extends past the horizontal
1065  // border but does not extend past the vertical border, attempt to wrap it to make it fit.
1066  // This is probably the most common solution.
1067 
1068  if(rctSource.right > 0 && // if prcSource is NULL, rctSource.right will be zero
1069  rctBounds.right > rctSource.right &&
1070  rctBounds.bottom <= rctSource.bottom &&
1071  !(iAlignment & DTAlignment_NO_WORD_WRAP)) // disable this if I'm bit allowing word wrap
1072  {
1073  iMaxLen = 0;
1074  iHPos0 = iHPos = rctSource.left;
1075 
1076  // TEMPORARY
1077 // WB_ERROR_PRINT("%s - TEMPORARY: 'needs text wrap' bounds rectangle is %d,%d,%d,%d\n",
1078 // __FUNCTION__, rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom);
1079 
1080 
1081 
1082  for(i1=0; i1 < pWords->nCount; i1++)
1083  {
1084  pW = &(pWords->aWords[i1]);
1085 
1086  if(pW->iIsWhiteSpace)
1087  {
1088  pW->iY = pW->iX = -1; // NA
1089 
1090  // before I start adding white space, see if there is
1091  // any NON-WHITE SPACE following this entry...
1092 
1093  if(iAlignment & DTAlignment_ALLOW_TRAILING)
1094  {
1095  iHPos += pW->iWidth; // it's ok to use the width of this white space
1096  }
1097  else
1098  {
1099  for(i2=i1 + 1; i2 < pWords->nCount; i2++)
1100  {
1101  pW2 = &(pWords->aWords[i2]);
1102 
1103  if(!pW2->iIsWhiteSpace && !pW2->iIsTab && !pW2->iIsLineFeed)
1104  {
1105  iHPos += pW->iWidth; // it's ok to use the width of this white space
1106  break;
1107  }
1108  else if(pW2->iIsLineFeed)
1109  {
1110  break; // end of line (trailing white space will be ignored)
1111  }
1112  }
1113  }
1114  }
1115  else if(pW->iIsTab)
1116  {
1117  pW->iY = pW->iX = -1; // NA
1118 
1119  // before I start adding white space for tabs, see if there is
1120  // any NON-WHITE SPACE following this entry...
1121 
1122  if(iAlignment & DTAlignment_ALLOW_TRAILING) // all bets are off when line-wrap happens
1123  {
1124 do_the_tab2:
1125  if(iAlignment & (DTAlignment_HCENTER | DTAlignment_HJUSTIFY)) // no tabbing for center/justify
1126  {
1127  iHPos += pW->nLength * iFontWidth; // treat it as regular white space
1128  }
1129  else
1130  {
1131  i3 = (iHPos - iHPos0 + iTabOrigin) % iTabWidth;
1132 
1133  if(!i3)
1134  {
1135  iHPos += iTabWidth; // tab out the entire distance
1136  }
1137  else
1138  {
1139  iHPos += i3; // tab out "the remainder"
1140  }
1141 
1142  if(pW->nLength > 1) // check for multiple tabs
1143  {
1144  iHPos += iTabWidth * (pW->nLength - 1); // additional tabs
1145  }
1146  }
1147 
1148  if(iHPos > rctSource.right) // tab goes past the end
1149  {
1150  // treat it like a line feed, but 'trim off' any additional white space first
1151 
1152  while((i1 + 1) < pWords->nCount)
1153  {
1154  pW2 = &(pWords->aWords[i1]);
1155  pW2->iX = pW2->iY = -1; // because I'm probably skipping them
1156 
1157  if(!pW2->iIsWhiteSpace && !pW2->iIsTab)
1158  {
1159  break; // if next item isn't white space, bust out of the loop
1160  }
1161  else if(pW2->iIsLineFeed)
1162  {
1163  i1++; // absorb THIS line feed since I'm doing one now
1164  break;
1165  }
1166 
1167  i1++; // keep skipping white space
1168  }
1169 
1170  // Do a line feed first (nearly the same as 'line feed' code, below)
1171 //do_a_line_feed2:
1172  if(iMaxLen < iHPos - iHPos0)
1173  {
1174  iMaxLen = iHPos - iHPos0;
1175  }
1176 
1177  if(iHPos > iHPos0 && // there is actual text present on this line
1178  (i1 + 1) == pWords->nCount) // the last entry? The text ends in one or more line feeds
1179  {
1180  // TODO: see if it's just a bunch of blank lines following the text
1181  // and consider ignoring them ALL if it makes sense to do so
1182 
1183  // at the end, the final line feed doesn't "count" unless it's by itself
1184  }
1185  else
1186  {
1187  iLines++; // only ONE line feed here...
1188  }
1189 
1190  iHPos = iHPos0;
1191  }
1192  }
1193  else
1194  {
1195  for(i2=i1 + 1; i2 < pWords->nCount; i2++)
1196  {
1197  pW2 = &(pWords->aWords[i2]);
1198  pW2->iX = pW2->iY = -1; // because I'm going to skip them
1199 
1200  if(!pW2->iIsWhiteSpace && !pW2->iIsTab && !pW2->iIsLineFeed)
1201  {
1202  goto do_the_tab2; // cleaner code with label
1203  }
1204  else if(pW2->iIsLineFeed)
1205  {
1206  i1 = i2 - 1; // skip all of the white space
1207  break; // this will cause the line feed to become "the next thing" and I'll end the line
1208  }
1209  }
1210 
1211  // if I get here, only white space remains at the end of the current 'line' (so ignore it)
1212  break;
1213  }
1214  }
1215  else if(pW->iIsLineFeed)
1216  {
1217  pW->iY = pW->iX = -1; // NA
1218 
1219  if(iMaxLen < iHPos - iHPos0)
1220  {
1221  iMaxLen = iHPos - iHPos0;
1222  }
1223 
1224  if(iHPos > iHPos0 && // there is actual text present on this line
1225  (i1 + 1) == pWords->nCount) // the last entry? The text ends in one or more line feeds
1226  {
1227  // TODO: see if it's just a bunch of blank lines following the text
1228  // and consider ignoring them ALL if it makes sense to do so
1229 
1230  iLines += pW->nLength - 1; // at the end, the final line feed doesn't "count" unless it's by itself
1231  }
1232  else
1233  {
1234  iLines += pW->nLength; // add ALL of the line feeds to the line count (more to come)
1235  }
1236 
1237  iHPos = iHPos0;
1238  }
1239  else
1240  {
1241  if(!iLines) // this handles blank text, which will show up with "zero lines"
1242  {
1243  iLines++; // indicate at least one line of printable stuff
1244 
1245  if(iHPos + pW->iWidth > rctSource.right) // word is "too wide"
1246  {
1247  goto will_not_fit; // generic bailout point, doesn't attempt to gerrymander the boundary rectangle
1248  }
1249  }
1250 
1251  pW->iX = iHPos - iHPos0; // relative X position
1252  pW->iY = (iLines - 1) * iFontHeight + iFontAscent; // relative Y position for 'base of text'
1253 
1254  if(iHPos + pW->iWidth > rctSource.right) // word is "too wide"
1255  {
1256  // Do a line feed first (nearly the same as 'line feed' code, above)
1257 
1258  if(iMaxLen < iHPos - iHPos0)
1259  {
1260  iMaxLen = iHPos - iHPos0;
1261  }
1262 
1263  if(iHPos > iHPos0 && // there is actual text present on this line
1264  (i1 + 1) == pWords->nCount) // the last entry? The text ends in one or more line feeds
1265  {
1266  // TODO: see if it's just a bunch of blank lines following the text
1267  // and consider ignoring them ALL if it makes sense to do so
1268 
1269  // at the end, the final line feed doesn't "count" unless it's by itself
1270  }
1271  else
1272  {
1273  iLines++; // only ONE line feed here...
1274  }
1275 
1276  iHPos = iHPos0;
1277  i1--; // "repeat this word"
1278  continue;
1279  }
1280 
1281  iHPos += pW->iWidth;
1282  }
1283  }
1284 
1285  if(iHPos > iMaxLen)
1286  {
1287  iMaxLen = iHPos;
1288  }
1289  }
1290 
1291  // re-calc boundary, see if it fits NOW
1292 
1293  rctBounds.left = rctSource.left;
1294  rctBounds.top = rctSource.top;
1295  rctBounds.right = rctBounds.left + iMaxLen;
1296  rctBounds.bottom = rctBounds.top + (iLines * iFontHeight);
1297 
1298  if(iLines > 1)
1299  {
1300  rctBounds.bottom += (iLines - 1) * iLineSpacing; // inter-line spacing
1301  }
1302 
1303  if(rctBounds.right <= rctSource.right &&
1304  rctBounds.bottom <= rctSource.bottom)
1305  {
1306  goto it_fits;
1307  }
1308 
1309 will_not_fit:
1310 
1311  WB_DEBUG_PRINT(DebugLevel_Heavy,
1312  "%s - 'it does not fit' bounds rectangle is %d,%d,%d,%d\n",
1313  __FUNCTION__, rctBounds.left, rctBounds.top, rctBounds.right, rctBounds.bottom);
1314 
1315  if(!(iAlignment & DTAlignment_PRINTING))
1316  {
1317  // At this point we know it's not going to fit in the specified rectangle. It will be
1318  // necessary to ESTIMATE the "ideal" rectangle for the text.
1319 
1320  // re-sizing the rectangle, ideal dimensions vs actual dimensions
1321 
1322  // since wrapped text looks SO much better when it maintains a reasonable height/width ratio,
1323  // try squeezing the text around to make this happen. The most reasonable first trial will
1324  // be to wrap the text at 3/4 iMaxLen, then 2/3 iMaxLen, then 1/2 iMaxLen, etc. until I end
1325  // up with a decent height/width ratio. To facilitate this I should throw out anything that
1326  // has a font pixel ratio of more than 16:1 (this would be appx 20 characters x 1 line) or
1327  // less than 4 : 1 (this would be appx 5 characters x 1 line). If I can't achieve either of
1328  // these goals, leave 'prcDest' as it is right now.
1329 
1330  // if rctSource is an empty rectangle, I'll need to create a set of dimensions that makes
1331  // good visual sense, based on the contents of the string.
1332 
1333  // First step: 'ideal' dimensions
1334 
1335 
1336  // Since the above didn't work, try half-split convergence
1337 
1338  // TODO: implement these things
1339  }
1340 
1341  if(prcDest)
1342  {
1343  memcpy(prcDest, &rctBounds, sizeof(*prcDest));
1344  }
1345 
1346  return 1; // for now (until I complete writing this)
1347 }
1348 
1349 
1350 //WB_DEFINE_PROFILE(anti_alias);
1351 //WB_DEFINE_PROFILE(anti_alias2);
1352 //WB_DEFINE_PROFILE(anti_alias3);
1353 //WB_DEFINE_PROFILE(anti_alias4);
1354 //WB_DEFINE_PROFILE(anti_alias5);
1355 
1356 static void __internalDoAntiAlias(Display *pDisplay, Drawable dw, WBGC gc, int iX, int iY, int iWidth, int iHeight)
1357 {
1358 XStandardColormap map;
1359 XImage *pImage;
1360 unsigned long lPixel;
1361 WB_GEOM geom;
1362 Window winRoot; // unused, but I still need it
1363 int iX0=0, iY0=0;
1364 unsigned int iWidth0=0, iHeight0=0, iBorder;
1365 unsigned int uiDepth = 0;
1366 Pixmap pxTemp;
1367 WBGC gc2;
1368 Region rgnClip;
1369 
1370 
1371 // WB_ENABLE_PROFILE(anti_alias, "overall");
1372 // WB_ENABLE_PROFILE(anti_alias2, "performing XCopyArea");
1373 // WB_ENABLE_PROFILE(anti_alias3, "performing WBSimpleAntiAliasImage");
1374 // WB_ENABLE_PROFILE(anti_alias4, "performing WBXGetImage");
1375 // WB_ENABLE_PROFILE(anti_alias5, "performing WBXPutImage");
1376 
1377  lPixel = WBGetGCFGColor(gc); // current foreground color
1378 
1379  pxTemp = None;
1380  rgnClip = None;
1381  gc2 = None;
1382 
1383  WBDefaultStandardColormap(pDisplay, &map);
1384 
1385  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1386  "%s.%d - geom %d,%d,%d,%d\n", __FUNCTION__, __LINE__, iX, iY, iWidth, iHeight);
1387 
1388  // use XGetGeometry to obtain the characteristics of the pixmap or window. iX and iY SHOULD be zero...
1390  XGetGeometry(pDisplay, dw, &winRoot, &iX0, &iY0, &iWidth0, &iHeight0, &iBorder, &uiDepth);
1392 
1393  geom.x = 0;
1394  geom.y = 0;
1395 
1396  if(iX >= iWidth0 + iX0 || iY >= iHeight0 + iY0)
1397  {
1398  WB_ERROR_PRINT("ERROR: %s.%d - drawable smaller than requested geom: %d,%d,%d,%d %d,%d,%d,%d\n",
1399  __FUNCTION__, __LINE__, iX0, iY0, iWidth0, iHeight0,
1400  iX, iY, iWidth, iHeight);
1401  return;
1402  }
1403 
1404 // WB_START_PROFILE(anti_alias);
1405 
1406  iWidth0 = (iWidth0 + iX0) - iX; // right - 'new left' = 'new width'
1407  iHeight0 = (iHeight0 + iY0) - iY; // adjust so that it matches the 'remaining space' from X,Y
1408 
1409  // to avoid errors from WBXGetImage, if the requested geometry is too big, reduce
1410  // the width/height to the actual width/height of the Drawable
1411 
1412  if(iWidth0 < iWidth)
1413  {
1414  geom.width = iWidth0;
1415  }
1416  else
1417  {
1418  geom.width = iWidth;
1419  }
1420 
1421  if(iHeight0 < iHeight)
1422  {
1423  geom.height = iHeight0;
1424  }
1425  else
1426  {
1427  geom.height = iHeight;
1428  }
1429 
1430  geom.x = 0;
1431  geom.y = 0;
1432 
1434  pxTemp = XCreatePixmap(pDisplay, dw, geom.width, geom.height,
1435  DefaultDepth(pDisplay, DefaultScreen(pDisplay)));
1437 
1438  if(pxTemp == None)
1439  {
1440  WB_ERROR_PRINT("ERROR: %s - unable to create temporary pixmap via XCreatePixmap()\n", __FUNCTION__);
1441  goto the_end;
1442  }
1443 
1444  rgnClip = WBGeomToRegion(&geom);
1445  if(rgnClip == None)
1446  {
1447  WB_ERROR_PRINT("ERROR: %s - unable to create temporary clipping region\n", __FUNCTION__);
1448  goto the_end;
1449  }
1450 
1451  gc2 = WBCopyDrawableGC(pDisplay, dw, gc); // everything the same except the clip region
1452  if(gc2 == None)
1453  {
1454  WB_ERROR_PRINT("ERROR: %s - unable to create temporary WBGC copy\n", __FUNCTION__);
1455 
1456  goto the_end;
1457  }
1458 
1459  WBSetRegion(gc2, rgnClip); // new clipping region
1460 
1461 // WB_START_PROFILE(anti_alias2);
1462 
1464  XCopyArea(pDisplay, dw, pxTemp, gc2->gc,
1465  iX, iY, geom.width, geom.height, 0, 0);
1467 
1468 // WB_STOP_PROFILE(anti_alias2);
1469 
1470 // WB_ERROR_PRINT("TEMPORARY: %s - geom %d,%d,%d,%d (%d,%d)\n",
1471 // __FUNCTION__, geom.x, geom.y, geom.width, geom.height, iWidth0, iHeight0);
1472 
1473 
1474  // create an XImage, and perform the operation on that
1475 
1476 // WB_START_PROFILE(anti_alias4);
1477  pImage = WBXGetImage(pDisplay, pxTemp, 0, 0, geom.width, geom.height, 0xffffffff, ZPixmap);
1478 // WB_STOP_PROFILE(anti_alias4);
1479 
1480  if(!pImage)
1481  {
1482  WB_ERROR_PRINT("ERROR: %s - unable to create image via WBXGetImage()\n", __FUNCTION__);
1483  }
1484  else
1485  {
1486 // WB_START_PROFILE(anti_alias3);
1487 
1488  WBSimpleAntiAliasImage(&map, pImage, lPixel, &geom);
1489 
1490 // WB_STOP_PROFILE(anti_alias3);
1491 
1492 // WB_START_PROFILE(anti_alias5);
1493 
1494  WBXPutImage(pDisplay, pxTemp, gc2, pImage, 0, 0, 0, 0, geom.width, geom.height);
1495 
1496 // WB_STOP_PROFILE(anti_alias5);
1497 
1499 // WB_START_PROFILE(anti_alias2);
1500  XCopyArea(pDisplay, pxTemp, dw, gc->gc, // this one uses 'gc' when copying BACK to 'dw'
1501  0, 0, geom.width, geom.height, iX, iY);
1502 // WB_STOP_PROFILE(anti_alias2);
1504 
1505  // I can destroy the image now
1506  WBXDestroyImage(pImage);
1507  }
1508 
1509 the_end:
1510 
1511 // WB_STOP_PROFILE(anti_alias);
1512 
1514  if(gc2 != NULL)
1515  {
1516  WBFreeGC(gc2);
1517  }
1518  if(pxTemp != None)
1519  {
1520  XFreePixmap(pDisplay, pxTemp);
1521  }
1522  if(rgnClip != None)
1523  {
1524  XDestroyRegion(rgnClip);
1525  }
1527 
1528 }
1529 
1530 
1531 void DTDrawSingleLineText(WB_FONTC pFont, const char *szText, Display *pDisplay, WBGC gc, Drawable dw,
1532  int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
1533 {
1534  DTDrawMultiLineText(pFont, szText, pDisplay, gc, dw, iTabWidth, iTabOrigin, prcBounds,
1536 }
1537 
1538 void DTDrawMultiLineText(WB_FONTC pFont, const char *szText, Display *pDisplay, WBGC gc, Drawable dw,
1539  int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
1540 {
1541 int i1, i2, i3, iH, iH2, iFontDescent; //, iW2, iFontWidth, iFontHeight;
1542 DT_WORDS *pWords;
1543 DT_WORD *pW;
1544 WB_RECT rcDest;
1545 XPoint xpt[3];
1546 XCharStruct xMaxBounds;
1547 
1548 
1549  if(!pFont)
1550  {
1551  pFont = WBGetDefaultFont();
1552  if(!pFont)
1553  {
1554  WB_ERROR_PRINT("%s - ERROR: WBGetDefaultFont returns NULL\n",
1555  __FUNCTION__);
1556 
1557  return; // bad
1558  }
1559  }
1560 
1561  if(iAlignment & DTAlignment_SINGLELINE)
1562  {
1563  iAlignment |= DTAlignment_NO_WORD_WRAP; // make sure
1564  }
1565 
1566 
1567  // get a few things straight 'round here
1568 
1569 // NOTE: iFontWidth and iFontHeight not being used; commented out because of linux gcc warnings
1570 // iFontWidth = WBFontSetAvgCharWidth(pDisplay, pFont); // was WBTextWidth(pFont, " ", 1); // width of a single space
1571 // iFontHeight = pFont->ascent + pFont->descent;
1572 
1573  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1574  "%s.%d call to DTGetWordsFromText\n", __FUNCTION__, __LINE__);
1575  pWords = DTGetWordsFromText(pDisplay, pFont, szText, iAlignment);
1576 
1577  if(!pWords)
1578  {
1579  WB_ERROR_PRINT("%s - ERROR: DTGetWordsFromText returns NULL\n",
1580  __FUNCTION__);
1581 
1582  return; // error
1583  }
1584 
1585  memcpy(&rcDest, prcBounds, sizeof(rcDest));
1586 
1587  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1588  "%s.%d bounds rectangle is INITIALLY %d,%d,%d,%d\n",
1589  __FUNCTION__, __LINE__, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom);
1590 
1591  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1592  "%s.%d call to InternalCalcIdealBounds\n", __FUNCTION__, __LINE__);
1593  if(InternalCalcIdealBounds(pDisplay, pFont, pWords, iTabWidth, iTabOrigin, prcBounds, &rcDest,
1594  iAlignment | DTAlignment_PRINTING, 0, -1)
1595  < 0)
1596  {
1597  InternalDebugDumpWords(pWords);
1598 
1599  WBFree(pWords);
1600 
1601  WB_ERROR_PRINT("%s - ERROR: InternalCalcIdealBounds returns error\n",
1602  __FUNCTION__);
1603 
1604  return; // bad (error)
1605  }
1606 
1607  iFontDescent = WBFontDescent(pFont);
1608  xMaxBounds = WBFontMaxBounds(pFont); // font's 'max_bounds' structure member, maximized for all of them
1609 
1610 // InternalDebugDumpWords(pWords);
1611 
1612  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1613  "%s.%d bounds rectangle is NOW %d,%d,%d,%d\n",
1614  __FUNCTION__, __LINE__, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom);
1615 
1616  // this function assumes I do not erase the background. TODO: flag for that?
1617 
1618  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1619  "%s.%d loop to display 'words'\n", __FUNCTION__, __LINE__);
1620  for(i1=0; i1 < pWords->nCount; i1++)
1621  {
1622  pW = &(pWords->aWords[i1]);
1623 
1624  if(pW->iX >= 0 && pW->iY >= 0 && pW->iWidth > 0)
1625  {
1626  if(!(iAlignment & DTAlignment_UNDERSCORE) ||
1627  !memchr(pW->pText, '_', pW->nLength))
1628  {
1629  // normal string drawing - no underscores to deal with
1630 
1631  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1632  "%s.%d DTDrawString at %d,%d %d chars of %s\n",
1633  __FUNCTION__, __LINE__, rcDest.left + pW->iX, rcDest.top + pW->iY, pW->nLength, pW->pText);
1634 
1635  // draw the entire string. 'pW->iY' is the BASE of the font
1636  DTDrawString(pDisplay, dw, pFont, gc, rcDest.left + pW->iX, rcDest.top + pW->iY,
1637  pW->pText, pW->nLength);
1638 
1639  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1640  "%s.%d normal display of \"%-.*s\"\n", __FUNCTION__, __LINE__, pW->nLength, pW->pText);
1641  }
1642  else
1643  {
1644  iH = rcDest.left + pW->iX;
1645  iH2 = -1; // horizontal position of NEXT underscore to display (corresponds to character index 'i3')
1646 
1647  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1648  "%s.%d loop to handle underscores\n", __FUNCTION__, __LINE__);
1649  for(i2=0, i3=0; i2 < pW->nLength; )
1650  {
1651  if(pW->pText[i2] == '_')
1652  {
1653  if((i2 + 1) >= pW->nLength)
1654  {
1655  break; // this will print the '_' normally
1656  }
1657  else if(i3 && i3 == i2) // a double-underscore
1658  {
1659  i2++;
1660  continue; // i'm going to print this as-is
1661  }
1662 
1663  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1664  "%s.%d DTDrawString at %d,%d %d chars of %s\n",
1665  __FUNCTION__, __LINE__, iH, rcDest.top + pW->iY, pW->nLength - i3, (char *)(pW->pText) + i3);
1666 
1667  // print i3 through i2 - 1. 'pW->iY' is the BASE of the font
1668  DTDrawString(pDisplay, dw, pFont, gc, iH, rcDest.top + pW->iY,
1669  (const char *)(pW->pText) + i3, i2 - i3);
1670 
1671  // draw the preceding underscore
1672  if(iH2 >= 0)
1673  {
1674  xpt[0].x=iH2;
1675  xpt[0].y=rcDest.top + pW->iY + iFontDescent + 1;// + pFont->max_bounds.ascent;
1676  xpt[1].x=iH + WBTextWidth(pFont, (char *)(pW->pText) + i3, 1); // width of character
1677  xpt[1].y=xpt[0].y;
1678 
1679  //BEGIN_XCALL_DEBUG_WRAPPER
1680  WBDrawLines(pDisplay, dw, gc, xpt, 2, CoordModeOrigin);
1681  //END_XCALL_DEBUG_WRAPPER
1682  }
1683 
1684  // advance the pointer
1685  iH += WBTextWidth(pFont, (char *)(pW->pText) + i3, i2 - i3);
1686 
1687  iH2 = iH; // next underscore's position
1688 
1689  i2++;
1690  i3 = i2; // the next text character
1691  }
1692  else
1693  {
1694  i2++;
1695  }
1696  }
1697 
1698  if(i3 < pW->nLength)
1699  {
1700  // draw the last part of the string. 'pW->iY' is the BASE of the font
1701  DTDrawString(pDisplay, dw, pFont, gc, iH, rcDest.top + pW->iY,
1702  (const char *)(pW->pText) + i3, pW->nLength - i3);
1703 
1704  // draw the preceding underscore
1705  if(iH >= 0)
1706  {
1707  xpt[0].x=iH;
1708  xpt[0].y=rcDest.top + pW->iY + xMaxBounds.descent + 1;// + pFont->max_bounds.ascent;
1709  xpt[1].x=iH + WBTextWidth(pFont, (char *)(pW->pText) + i3, 1); // width of character
1710  xpt[1].y=xpt[0].y;
1711 
1712  //BEGIN_XCALL_DEBUG_WRAPPER
1713  WBDrawLines(pDisplay, dw, gc, xpt, 2, CoordModeOrigin);
1714  //END_XCALL_DEBUG_WRAPPER
1715  }
1716  }
1717  }
1718 
1719  }
1720  }
1721 
1722  // now that I have drawn the text, anti-alias it if I set the anti-alias flag
1723 
1724  if(WBFontEnableAntiAlias() &&
1725  (iAlignment & DTAlignment_ANTIALIAS))
1726  {
1727  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_DrawText,
1728  "%s.%d call to __internalDoAntiAlias\n", __FUNCTION__, __LINE__);
1729  __internalDoAntiAlias(pDisplay, dw, gc,
1730  rcDest.left, rcDest.top,
1731  rcDest.right - rcDest.left,
1732  rcDest.bottom - rcDest.top);
1733  }
1734 
1735  WBFree(pWords);
1736 }
1737 
1738 
1739 // TODO: consider whether either of these is really necessary. each line could stand on its own,
1740 // and it would be more efficient. where it MIGHT help is a simple 'edit window' with limited
1741 // text length, which could be stored as a 'DT_WORDS'. Probably NOT practical.
1742 // (so perhaps these should be deprecated?)
1743 
1744 
1745 void DTPreRender(Display *pDisplay, WB_FONTC pFont, DT_WORDS *pWords, int iTabWidth, int iTabOrigin,
1746  WB_RECT *prcBounds, int iAlignment, int iStartLine, int iEndLine)
1747 {
1748 WB_RECT rcDest;
1749 
1750 
1751  if(!pDisplay)
1752  {
1753  pDisplay = WBGetDefaultDisplay();
1754  }
1755 
1756  if(!pFont)
1757  {
1758  pFont = WBGetDefaultFont();
1759  if(!pFont)
1760  {
1761  WB_ERROR_PRINT("%s - ERROR: WBGetDefaultFont returns NULL\n",
1762  __FUNCTION__);
1763 
1764  return; // bad
1765  }
1766  }
1767 
1768  if(iAlignment & DTAlignment_SINGLELINE)
1769  {
1770  iAlignment |= DTAlignment_NO_WORD_WRAP; // make sure
1771  }
1772 
1773  memcpy(&rcDest, prcBounds, sizeof(rcDest));
1774 
1775  if(InternalCalcIdealBounds(pDisplay, pFont, pWords, iTabWidth, iTabOrigin, prcBounds, &rcDest,
1776  iAlignment | DTAlignment_PRINTING, iStartLine, iEndLine)
1777  < 0)
1778  {
1779  InternalDebugDumpWords(pWords);
1780 
1781  WB_ERROR_PRINT("%s - ERROR: InternalCalcIdealBounds returns error\n",
1782  __FUNCTION__);
1783 
1784  return; // bad (error)
1785  }
1786 
1787 
1788  // TODO: implement
1789 }
1790 
1791 void DTRender(Display *pDisplay, WB_FONTC pFont, const DT_WORDS *pWords, WBGC gc, Drawable dw,
1792  int iHScrollBy, int iVScrollBy, const WB_RECT *prcBounds, const WB_RECT *prcViewport, int iAlignment)
1793 {
1794  // TODO: implement
1795 }
1796 
1797 
1798 
1799 
1800 
1801 static void InternalDebugDumpWords(DT_WORDS *pWords)
1802 {
1803  char tbuf[1024]; // for now max size of 'word'
1804  int i1, i2;
1805 
1806  WBDebugPrint("===================================================\n"
1807  "%s - DUMPING pWords array\n ", __FUNCTION__);
1808 
1809  for(i1=0; i1 < pWords->nCount; i1++)
1810  {
1811  DT_WORD *pW = &(pWords->aWords[i1]);
1812 
1813  if(pW->iIsLineFeed)
1814  {
1815  for(i2=0; i2 < pW->nLength; i2++)
1816  {
1817  WBDebugPrint("<LF>\n ");
1818  }
1819  }
1820  else if(pW->iIsTab)
1821  {
1822  for(i2=0; i2 < pW->nLength; i2++)
1823  {
1824  WBDebugPrint("<TAB>");
1825  }
1826  }
1827  else if(pW->iIsWhiteSpace)
1828  {
1829  for(i2=0; i2 < pW->nLength; i2++)
1830  {
1831  WBDebugPrint("<W>");
1832  }
1833  }
1834  else
1835  {
1836  i2 = pW->nLength > sizeof(tbuf) - 1 ? sizeof(tbuf) - 1: pW->nLength;
1837  memcpy(tbuf, pW->pText, i2);
1838  tbuf[i2] = 0;
1839  WBDebugPrint("%s", tbuf);
1840  }
1841  }
1842 
1843  WBDebugPrint("\n\n");
1844 
1845  // now do it again and indicate the length/width/height for each
1846 
1847  for(i1=0; i1 < pWords->nCount; i1++)
1848  {
1849  DT_WORD *pW = &(pWords->aWords[i1]);
1850 
1851  if(pW->iIsLineFeed)
1852  {
1853  WBDebugPrint(" LF: length=%d,width=%d,height=%d X=%d,Y=%d\n",
1854  pW->nLength, pW->iWidth, pW->iHeight, pW->iX, pW->iY);
1855  }
1856  else if(pW->iIsTab)
1857  {
1858  WBDebugPrint(" TAB: length=%d,width=%d,height=%d X=%d,Y=%d\n",
1859  pW->nLength, pW->iWidth, pW->iHeight, pW->iX, pW->iY);
1860  }
1861  else if(pW->iIsWhiteSpace)
1862  {
1863  WBDebugPrint(" WS: length=%d,width=%d,height=%d X=%d,Y=%d\n",
1864  pW->nLength, pW->iWidth, pW->iHeight, pW->iX, pW->iY);
1865  }
1866  else
1867  {
1868  i2 = pW->nLength > sizeof(tbuf) - 1 ? sizeof(tbuf) - 1: pW->nLength;
1869  memcpy(tbuf, pW->pText, i2);
1870  tbuf[i2] = 0;
1871  WBDebugPrint(" STR: length=%d,width=%d,height=%d X=%d,Y=%d \"%s\"\n",
1872  pW->nLength, pW->iWidth, pW->iHeight, pW->iX, pW->iY, tbuf);
1873  }
1874  }
1875 
1876  WBDebugPrint("\n===================================================\n");
1877 }
1878 
1879 
GC gc
the associated 'GC'
#define WB_LIKELY(x)
optimization for code branching when condition is 'likely'. use within conditionals
const char * szText
original text pointer
Definition: draw_text.h:214
int DTCalcIdealBounds(Display *pDisplay, WB_FONTC pFont, const char *szText, int iTabWidth, unsigned int iTabOrigin, const WB_RECT *prcSource, WB_RECT *prcDest, int iAlignment)
Calculate the ideal bounding rectangle for the specified text and font.
Definition: draw_text.c:579
static void InternalDebugDumpWords(DT_WORDS *pWords)
internal debug function to dump 'DT_WORDS' 'words' object
Definition: draw_text.c:1801
'window helper' main header file for the X11workbench Toolkit API
Display * display
the Display associated with the WBGC (NULL implies 'Default Display')
int iWidth
width of text in pixels (based on font size) (NA for tabs, LF, valid for white space)
Definition: draw_text.h:167
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:364
unsigned int width
the 'width' value of the extent.
int WBFontHeight(WB_FONTC pFont0)
Get the maximum character height from a WB_FONT.
Definition: font_helper.c:546
int WBFontAscent(WB_FONTC pFont0)
Get the maximum character ascent from a WB_FONT.
Definition: font_helper.c:495
void WBDefaultStandardColormap(Display *pDisplay, XStandardColormap *pMap)
returns a default XStandardColormap structure for the default screen of the specified display
center along the font baseline (single line only)
Definition: draw_text.h:88
Utilities for copying and drawing text, determining text extents, and so on.
internal flag, indicates that an underscore underlines the next character
Definition: draw_text.h:121
int nMax
max number of items in aWords (for memory allocation purposes)
Definition: draw_text.h:211
int WBFontAvgCharWidth(WB_FONTC pFont0)
Get the average character width for a font.
Definition: font_helper.c:343
XImage * WBGetWindowImage(Display *pDisplay, Window wID)
Obtain an XImage for the entire window.
internal flag, 'single line only' (no line breaks). Implies DTAlignment_NO_WORD_WRAP
Definition: draw_text.h:120
int nCount
number of items in aWords
Definition: draw_text.h:210
top-aligned text (using highest vertical ascent)
Definition: draw_text.h:84
unsigned int width
static __inline__ unsigned long WBGetForeground(WBGC hGC)
Get the (cached) foreground color for a WBGC.
int WBXPutImage(Display *pDisplay, Drawable dw, WBGC gc, XImage *pImage, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height)
Write contents of an XImage onto a Drawable.
WB_FONT DTCalcIdealFont(Display *pDisplay, WB_FONTC pRefFont, const char *szText, WB_GEOM *geomBounds)
Calculate the ideal font based on the text and rectangle.
Definition: draw_text.c:277
horizontally centered text. tabs are treated as white space
Definition: draw_text.h:78
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
internal wrapper struct for X11 'geometry' definition
struct tagDT_WORDS DT_WORDS
A collection of DT_WORD structures along with a pointer to the original text.
void DTDrawSingleLineText(WB_FONTC pFont, const char *szText, Display *pDisplay, WBGC gc, Drawable dw, int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
draw single-line text
Definition: draw_text.c:1531
void * WBReAlloc(void *pBuf, int nNewSize)
High performance memory sub-allocator 're-allocate'.
void DTPreRender(Display *pDisplay, WB_FONTC pFont, DT_WORDS *pWords, int iTabWidth, int iTabOrigin, WB_RECT *prcBounds, int iAlignment, int iStartLine, int iEndLine)
Pre-render a 'DT_WORDS' structure for subsequent display.
Definition: draw_text.c:1745
Structure defining a 'word' for rendering purposes.
Definition: draw_text.h:161
internal flag, applies simple anti-aliasing. See WBSimpleAntiAliasImage() and WBSimpleAntiAliasPixmap...
Definition: draw_text.h:119
int WBFontEnableAntiAlias(void)
returns non-zero value if certain fonts should be anti-aliased when rendered
Definition: font_helper.c:157
int WBDrawLines(Display *display, Drawable d, WBGC gc, XPoint *points, int npoints, int mode)
Wrapper for XDrawLine()
A collection of DT_WORD structures along with a pointer to the original text.
Definition: draw_text.h:206
center using entire text height (ascent + descent for single line)
Definition: draw_text.h:86
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
int WBSetRegion(WBGC hGC, Region rgnClip)
Assign clipping region, wrapper for XSetRegion()
center within the top half of the rectangle (single line only)
Definition: draw_text.h:89
int WBXDrawString(XImage *pImage, WB_FONTC pFont, WBGC hGC, int x, int y, const char *string, int length)
XImage version for XDrawString() or DTDrawString()
Do not shrink the bounding rectangle for ideal text dimension.
Definition: draw_text.h:110
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
center within the bottom half of the rectangle (single line only)
Definition: draw_text.h:90
XCharStruct WBFontMaxBounds(WB_FONTC pFont0)
Get a 'maximized' copy of 'max_bounds' (applicable to all font faces in the WB_FONT)
Definition: font_helper.c:592
int iIsTab
indicates that the text consists of tabs (nLength for number of tabs, pText is NULL)
Definition: draw_text.h:169
#define WB_DRAW_STRING
Definition: font_helper.h:101
int iIsWhiteSpace
indicates that it is 'white space' (regular or special character) (nLength, width,...
Definition: draw_text.h:171
int WBAllocUsableSize(void *pBuf)
High performance memory sub-allocator, similar to 'malloc_usable_size'.
static __inline__ unsigned long WBGetBackground(WBGC hGC)
Get the (cached) background color for a WBGC.
void DTDrawString(Display *pDisplay, Drawable drawable, WB_FONTC pFont, WBGC gc, int x, int y, const char *pString, int nLength)
Draw text in a platform-independent manner for UTF-8 or multi-byte text, equivalent to WBDrawString()...
Definition: draw_text.c:111
XImage * WBXGetImage(Display *pDisplay, Drawable dw, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format)
Read contents of a Drawable onto an XImage.
struct tagDT_WORD DT_WORD
Structure defining a 'word' for rendering purposes.
int iHeight
height of text in pixels (based on font size) (NA for tabs, LF, valid for white space)
Definition: draw_text.h:168
XFontSet fsFont
legacy 'XFontSet' for X11 raster fonts (UTF-8)
Definition: font_helper.h:168
void WBSimpleAntiAliasImage(const XStandardColormap *pMap, XImage *pImage, unsigned long lPixel, WB_GEOM *pGeom)
Simple anti-alias of an XImage using foreground pixel color.
right-justified text
Definition: draw_text.h:79
DT_WORD aWords[1]
The actual data (self-contained within the memory block)
Definition: draw_text.h:215
internal wrapper struct for 'extent' definition
int WBXDestroyImage(XImage *pImage)
Destroy an XImage - call this instead of XDestroyImage()
unsigned int height
XFontStruct * pFontStruct
legacy 'XFontStruct' for X11 raster fonts
Definition: font_helper.h:167
internal wrapper struct for 'rectangle' definition
horizontal alignment mask
Definition: draw_text.h:82
int WBFontDescent(WB_FONTC pFont0)
Get the maximum character descent from a WB_FONT.
Definition: font_helper.c:443
int iY
Relative 'Y' pixel position for the beginning of this element (when pre-rendered, else -1)
Definition: draw_text.h:173
int WBTextWidth(WB_FONTC pFont, const char *szText, int cbText)
Obtain the pixel width of specified text for a specified WB_FONT.
Definition: font_helper.c:747
void WBFreeGC(WBGC hGC)
Free resources for a WBGC, wrapper for XFreeGC()
static int InternalCalcIdealBounds(Display *pDisplay, WB_FONTC pFont, DT_WORDS *pWords, int iTabWidth, unsigned int iTabOrigin, const WB_RECT *prcSource, WB_RECT *prcDest, int iAlignment, int iStartLine, int iEndLine)
calculate ideal bounds rectangle for text given a font, tab spacing, tab origin, and alignment
Definition: draw_text.c:688
DT_WORDS * DTGetWordsFromText(Display *pDisplay, WB_FONTC pFont, const char *szText, int iAlignment)
Parse 'DT_WORDS' structure from single-line or multi-line text.
Definition: draw_text.c:365
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
unsigned long WBGetGCFGColor(WBGC gc)
returns the currently assigned foreground color for a WBGC
WBGC WBCopyDrawableGC(Display *pDisplay, Drawable dw, WBGC hGCOrig)
makes a copy of the specified WBGC for the desired 'Drawable'
int iIsLineFeed
indicates a line feed (CR, CRLF, LF, or LFCR) (nLength for number of line feeds, pText is NULL)
Definition: draw_text.h:170
Region WBGeomToRegion(const WB_GEOM *pGeom)
'Paint' helper, converts a WB_GEOM structure to a Region.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
void DTDrawMultiLineText(WB_FONTC pFont, const char *szText, Display *pDisplay, WBGC gc, Drawable dw, int iTabWidth, int iTabOrigin, const WB_RECT *prcBounds, int iAlignment)
draw multi-line text
Definition: draw_text.c:1538
center halfway up the 'ascent' portion (single line only)
Definition: draw_text.h:87
void DTRender(Display *pDisplay, WB_FONTC pFont, const DT_WORDS *pWords, WBGC gc, Drawable dw, int iHScrollBy, int iVScrollBy, const WB_RECT *prcBounds, const WB_RECT *prcViewport, int iAlignment)
Using pre-rendered 'DT_WORDS' structure, display.
Definition: draw_text.c:1791
An allocated structure containing XFontStruct, XFontInfo, and XftFont [as applicable] for a specified...
Definition: font_helper.h:152
left-justified text
Definition: draw_text.h:77
unsigned int height
the 'height' value of the extent.
internal wrapper struct for GC with local cache
int nLength
length of text element (in bytes)
Definition: draw_text.h:166
const char * pText
Definition: draw_text.h:163
bottom-aligned text (using lowest vertical descent)
Definition: draw_text.h:85
void WBTextExtent(WB_FONTC pFont, const char *szText, int cbText, WB_EXTENT *pExtent)
Obtain the pixel extent of specified text for a specified XFontSet.
Definition: font_helper.c:790
int iX
Relative 'X' pixel position for the beginning of this element (when pre-rendered, else -1)
Definition: draw_text.h:172
WB_FONTC WBGetDefaultFont(void)
Returns a pointer to the default font WB_FONT for the default display. This is a shared resource; do ...