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