0023015: TKOpenGl redesign regression in text printing
[occt.git] / src / OpenGl / OpenGl_Display_1.cxx
CommitLineData
2166f0fa
SK
1// File: OpenGl_Display_1.cxx
2// Created: 25 October 2011
3// Author: Sergey ZERCHANINOV
4// Copyright: OPEN CASCADE 2011
5
34a44cbd 6#include <OpenGl_tgl_all.hxx>
2166f0fa
SK
7#include <OpenGl_Display.hxx>
8
9#include <TCollection_AsciiString.hxx>
10#include <TCollection_HAsciiString.hxx>
11
12#include <OpenGl_FontMgr.hxx>
34a44cbd 13#include <OpenGl_PrinterContext.hxx>
2166f0fa
SK
14#include <OpenGl_AspectText.hxx>
15
16#ifdef HAVE_GL2PS
17#include <gl2ps.h>
18#endif
19
20/*-----------------------------------------------------------------------------*/
21/*
22* Prototypes variables statiques
23*/
24
25struct FontMapNode
26{
27 const char * EnumName;
28 const char * FontName;
29 OSD_FontAspect FontAspect;
30};
31
32static const FontMapNode myFontMap[] =
33{
34
35#ifdef WNT
36
37 { "Courier" , "Courier New" , OSD_FA_Regular },
38 { "Times-Roman" , "Times New Roman", OSD_FA_Regular },
39 { "Times-Bold" , "Times New Roman", OSD_FA_Bold },
40 { "Times-Italic" , "Times New Roman", OSD_FA_Italic },
41 { "Times-BoldItalic" , "Times New Roman", OSD_FA_BoldItalic },
42 { "ZapfChancery-MediumItalic", "Script" , OSD_FA_Regular },
43 { "Symbol" , "Symbol" , OSD_FA_Regular },
44 { "ZapfDingbats" , "WingDings" , OSD_FA_Regular },
45 { "Rock" , "Arial" , OSD_FA_Regular },
46 { "Iris" , "Lucida Console" , OSD_FA_Regular }
47
48#else //X11
49
50 { "Courier" , "Courier" , OSD_FA_Regular },
51 { "Times-Roman" , "Times" , OSD_FA_Regular },
52 { "Times-Bold" , "Times" , OSD_FA_Bold },
53 { "Times-Italic" , "Times" , OSD_FA_Italic },
54 { "Times-BoldItalic" , "Times" , OSD_FA_BoldItalic },
55 { "Arial" , "Helvetica" , OSD_FA_Regular },
56 { "ZapfChancery-MediumItalic", "-adobe-itc zapf chancery-medium-i-normal--*-*-*-*-*-*-iso8859-1" , OSD_FA_Regular },
57 { "Symbol" , "-adobe-symbol-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific" , OSD_FA_Regular },
58 { "ZapfDingbats" , "-adobe-itc zapf dingbats-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific" , OSD_FA_Regular },
59 { "Rock" , "-sgi-rock-medium-r-normal--*-*-*-*-p-*-iso8859-1" , OSD_FA_Regular },
60 { "Iris" , "--iris-medium-r-normal--*-*-*-*-m-*-iso8859-1" , OSD_FA_Regular }
61#endif
62
63};
64
65#define NUM_FONT_ENTRIES (sizeof(myFontMap)/sizeof(FontMapNode))
66
67/*-----------------------------------------------------------------------------*/
68
69/*
70* Constants
71*/
72
73#ifdef HAVE_GL2PS
74void OpenGl_Display::getGL2PSFontName (const char *src_font, char *ps_font)
75{
76 /*
77 Convert font name used for rendering to some "good" font names
78 that produce good vector text
79 */
80 static char const *family[] = {"Helvetica", "Courier", "Times"};
81 static char const *italic[] = {"Oblique", "Oblique", "Italic"};
82 static char const *base[] = {"", "", "-Roman"};
83
84 int font = 0;
85 int isBold = 0;
86 int isItalic = 0;
87
88 if( strstr( src_font, "Symbol" ) ){
89 sprintf( ps_font, "%s", "Symbol" );
90 return;
91 }
92
93 if( strstr( src_font, "ZapfDingbats" ) ){
94 sprintf( ps_font, "%s", "WingDings" );
95 return;
96 }
97
98 if ( strstr( src_font, "Courier" ) ){
99 font = 1;
100 }
101 else if ( strstr( src_font, "Times" ) ){
102 font = 2;
103 }
104
105 if ( strstr( src_font, "Bold" ) ){
106 isBold = 1;
107 }
108
109 if ( strstr( src_font, "Italic" ) || strstr( src_font, "Oblique" ) ){
110 isItalic = 1;
111 }
112
113 if ( isBold ){
114 sprintf( ps_font, "%s-%s", family[font], "Bold");
115 if ( isItalic ){
116 sprintf(ps_font, "%s%s", ps_font, italic[font]);
117 }
118 }
119 else if ( isItalic )
120 {
121 sprintf( ps_font, "%s-%s", family[font], italic[font] );
122 }
123 else
124 {
125 sprintf( ps_font, "%s%s", family[font], base[font] );
126 }
127}
128#endif
129
130/*-----------------------------------------------------------------------------*/
131
132/*
133* Fonctions publiques
134*/
135
136/*-----------------------------------------------------------------------------*/
137
138int OpenGl_Display::FindFont (const char* AFontName, const OSD_FontAspect AFontAspect,
139 const int ABestSize, const float AXScale, const float AYScale)
140{
141 if (!AFontName)
142 return -1;
143
144 if (ABestSize != -1)
145 myFontSize = ABestSize;
146
147 OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
148
149 Handle(TCollection_HAsciiString) family_name = new TCollection_HAsciiString(AFontName);
150 myFont = mgr->request_font( family_name, AFontAspect, myFontSize );
151
152 if( myFont == -1 )
153 {
154 //try to use font names mapping
155 FontMapNode newTempFont = myFontMap[0];
156 for ( int i = 0; i < NUM_FONT_ENTRIES; ++i )
157 {
158 if ( TCollection_AsciiString(myFontMap[i].EnumName).IsEqual( family_name->ToCString() ) )
159 {
160 newTempFont = myFontMap[i];
161 break;
162 }
163 }
164 family_name = new TCollection_HAsciiString(newTempFont.FontName);
165 myFont = mgr->request_font( family_name, newTempFont.FontAspect, myFontSize );
166 }
167
168 // Requested family name not found -> serach for any font family with given aspect and height
169 if ( myFont == -1 )
170 {
171 family_name = new TCollection_HAsciiString( "" );
172 myFont = mgr->request_font( family_name, AFontAspect, myFontSize );
173 }
174
175 // The last resort: trying to use ANY font available in the system
176 if ( myFont == -1 )
177 {
178 myFont = mgr->request_font( family_name, OSD_FA_Undefined, -1 );
179 }
180
181 if ( myFont != -1 )
182 mgr->setCurrentScale( AXScale, AYScale );
183
184 return myFont;
185}
186
187/*-----------------------------------------------------------------------------*/
188
189void OpenGl_Display::StringSize (const wchar_t *str, int &width, int &ascent, int &descent)
190{
191 ascent = 0;
192 descent = 0;
193 width = 0;
194 if (myFont != -1) {
195 OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
196 const FTFont* font = mgr->fontById( myFont );
197 if ( font ) {
198 width = int( mgr->computeWidth( myFont, str ) );
199 ascent = int( font->Ascender() );
200 descent = int( font->Descender() );
201 }
202 }
203}
204
f3f08423 205/*
206 Class : MultilineTextRenderer
207 Description : Class for constructing text and multi-line text
208*/
209
210class MultilineTextRenderer
211{
212 private:
213
214 Standard_Integer myLFNum; // Number of '\n' (Line Feed) '\x00\x0A'
215 Standard_Integer myCurrPos; // Position of the current substring
216 Standard_Integer myNewStrLen; // Length of the new string
217 Standard_Integer mySubstrNum; // Number of substrings
218 wchar_t *myNewStr; // New string
219 const wchar_t *myStrPtr; // Pointer to the original string
220
221 public:
222
223 // ----------------------------------------------
224 // Function: MultilineTextRenderer
225 // Purpose: Constructor with initialization
226 // ----------------------------------------------
227 MultilineTextRenderer ( const wchar_t *theStr,
228 GLdouble &theXdis,
229 GLdouble &theYdis,
230 const OpenGl_TextParam *theParam,
231 const FTFont *theFnt,
232 GLint theWidthFont,
233 GLint theAscentFont,
234 GLint theDescentFont ) :
235
236 myLFNum (0),
237 myCurrPos (0),
238 myNewStrLen (0),
239 mySubstrNum (0),
240 myNewStr (0),
241 myStrPtr (&theStr[0])
242 {
243 const Standard_Integer aStrLen = wcslen(theStr); // Length of the original string
244 Standard_Integer aNextCRChar = 0; // Character after '\r' (Carriage Return) '\x00\x0D'
245 Standard_Integer aHTNum = 0; // Number of '\t' (Horizontal Tabulation) '\x00\x09'
246 Standard_Integer aDumpNum = 0; // Number of '\a', '\b', '\v' and '\f'
247 Standard_Integer anAllSpaces = 0; // Number of spaces instead of all '\t' characters
248 Standard_Integer aMaxSubstrLen = 0; // Length of the largest substring in the new string
249
250 Standard_Integer aTimeVar = 0;
251
252 // Calculation index after last '\r' character
253 for ( Standard_Integer anIndex = 0; anIndex < aStrLen; ++anIndex )
254 {
255 if ( theStr[anIndex] == '\r' ) aNextCRChar = anIndex+1;
256 }
257
258 // Calculation numbers of the some control characters
259 for (Standard_Integer anIndex = aNextCRChar; anIndex < aStrLen; anIndex++)
260 {
261 ++aTimeVar;
262 switch ( theStr[anIndex] )
263 {
264 case '\n':
265 ++myLFNum;
266 aTimeVar = 0;
267 break;
268 case '\b':
269 case '\v':
270 case '\f':
271 case '\a':
272 ++aDumpNum;
273 --aTimeVar;
274 break;
275 case '\t':
276 ++aHTNum;
277 anAllSpaces += ( 8 - ( aTimeVar - 1 )%8 );
278 aTimeVar = 0;
279 break;
280 }
281 }
282
283 // Calculation length of the new string
284 myStrPtr += aNextCRChar;
285 myNewStrLen = aStrLen - aNextCRChar + anAllSpaces - aHTNum - aDumpNum;
286
287 myNewStr = new wchar_t[myNewStrLen + 1];
288 myNewStr[myNewStrLen]='\0';
289
290 CalcString (aStrLen, aMaxSubstrLen);
291 CalcHAlign (theXdis, theParam, theWidthFont, aStrLen, aMaxSubstrLen);
292 CalcVAlign (theYdis, theParam, theFnt, theAscentFont, theDescentFont);
293 }
294
295 // ----------------------------------------------
296 // Function: ~MultilineTextRenderer
297 // Purpose: Default destructor
298 // ----------------------------------------------
299 ~MultilineTextRenderer ()
300 {
301 delete[] myNewStr;
302 }
303
304 // ----------------------------------------------
305 // Function: Next
306 // Purpose: Calculate position of the next substring
307 // ----------------------------------------------
308 void Next ()
309 {
310 for ( Standard_Integer anIndex = 0; anIndex <= myNewStrLen; ++anIndex )
311 {
312 if ( myNewStr[myCurrPos + anIndex] == '\0' )
313 {
314 ++mySubstrNum;
315 myCurrPos += anIndex + 1;
316 break;
317 }
318 }
319 }
320
321 // ----------------------------------------------
322 // Function: More
323 // Purpose: Calculate position of the next substring
324 // ----------------------------------------------
325 Standard_Boolean More ()
326 {
327 if ( mySubstrNum <= myLFNum ) return Standard_True;
328 else return Standard_False;
329 }
330
331 // ----------------------------------------------
332 // Function: GetValue
333 // Purpose: Returns current substring
334 // ----------------------------------------------
335 wchar_t* GetValue ()
336 {
337 return ( myNewStr + myCurrPos );
338 }
339
340 private:
341
342 // ----------------------------------------------
343 // Function: CalcString
344 // Purpose: Calculate new string separated by '\0' without '\n', '\t', '\b', '\v', '\f', '\a'
345 // ----------------------------------------------
346 void CalcString ( const Standard_Integer theStrLen, Standard_Integer &theMaxSubstrLen )
347 {
348 Standard_Integer
349 aHelpIndex = 0,
350 aTimeVar = 0,
351 aSpacesNum = 0;
352
353 for ( Standard_Integer anIndex1 = 0, anIndex2 = 0;
354 (anIndex1 < theStrLen)&&(anIndex2 < myNewStrLen);
355 ++anIndex1, ++anIndex2 )
356 {
357 ++aTimeVar;
358
359 if ( *(myStrPtr + anIndex1) == '\n' ) aTimeVar = 0;
360
361 while ( (*(myStrPtr + anIndex1)=='\b')||(*(myStrPtr + anIndex1)=='\f')
362 ||(*(myStrPtr + anIndex1)=='\v')||(*(myStrPtr + anIndex1)=='\a') )
363 {
364 ++anIndex1;
365 }
366
367 if ( *(myStrPtr + anIndex1) == '\t' )
368 {
369 aSpacesNum = ( 8 - ( aTimeVar - 1 )%8 );
370
371 for ( aHelpIndex = 0; aHelpIndex < aSpacesNum; aHelpIndex++ )
372 {
373 myNewStr[anIndex2 + aHelpIndex] = ' ';
374 }
375 anIndex2 += aHelpIndex - 1;
376 aTimeVar = 0;
377 }
378 else
379 {
380 myNewStr[anIndex2] = *(myStrPtr + anIndex1);
381 }
382 }
383
384 // After this part of code we will have a string separated by '\0' characters
385 Standard_Integer aHelpLength = 0;
386
387 if( myNewStr )
388 {
389 for( Standard_Integer anIndex = 0; anIndex <= myNewStrLen; ++anIndex )
390 {
391 if ( myNewStr[anIndex] == '\n' )
392 {
393 myNewStr[anIndex] = '\0';
394 }
395
396 // Calculating length of the largest substring of the new string.
397 // It's needed for horizontal alignment
398 if ( myNewStr[anIndex] != '\0' )
399 {
400 ++aHelpLength;
401 }
402 else
403 {
404 if ( aHelpLength > theMaxSubstrLen ) theMaxSubstrLen = aHelpLength;
405
406 aHelpLength = 0;
407 }
408 }
409 }
410 }
411
412 // ----------------------------------------------
413 // Function: CalcVAlign
414 // Purpose: Calculate vertical alignment for text
415 // ----------------------------------------------
416 void CalcVAlign ( GLdouble &theYdis,
417 const OpenGl_TextParam *theParam,
418 const FTFont *theFnt,
419 GLint theAscentFont,
420 GLint theDescentFont )
421 {
422 switch (theParam->VAlign)
423 {
424 case Graphic3d_VTA_BOTTOM:
425 theYdis = (GLdouble)(myLFNum * theFnt->FaceSize());
426 break;
427 case Graphic3d_VTA_CENTER:
428 if ( (myLFNum%2) == 0 ) // An odd number of strings
429 {
430 theYdis = (GLdouble)((myLFNum/2.0) * theFnt->FaceSize()) + theDescentFont;
431 }
432 else // An even number of strings
433 {
434 theYdis = (GLdouble)((myLFNum - 1)/2.0 * theFnt->FaceSize()) - theDescentFont/2.0;
435 }
436 break;
437 case Graphic3d_VTA_TOP:
438 theYdis = -(GLdouble)theAscentFont - theDescentFont;
439 break;
440 default:
441 theYdis = (GLdouble)(myLFNum * theFnt->FaceSize());
442 break;
443 }
444 }
445
446 // ----------------------------------------------
447 // Function: CalcHAlign
448 // Purpose: Calculate horizontal alignment for text
449 // ----------------------------------------------
450 void CalcHAlign ( GLdouble &theXdis,
451 const OpenGl_TextParam *theParam,
452 GLint theWidthFont,
453 const Standard_Integer theStrLen,
454 Standard_Integer theMaxSubstrLen)
455 {
456 GLdouble aWidth = (GLdouble)(theMaxSubstrLen * theWidthFont)/theStrLen;
457
458 switch (theParam->HAlign)
459 {
460 case Graphic3d_HTA_LEFT:
461 theXdis = 0.;
462 break;
463 case Graphic3d_HTA_CENTER:
464 theXdis = -0.5 * (GLdouble)aWidth;
465 break;
466 case Graphic3d_HTA_RIGHT:
467 theXdis = -(GLdouble)aWidth;
468 break;
469 default:
470 theXdis = 0.;
471 break;
472 }
473 }
474};
475
2166f0fa
SK
476/*-----------------------------------------------------------------------------*/
477
478void OpenGl_Display::RenderText (const wchar_t* str, const int is2d, const float x, const float y, const float z,
479 const OpenGl_AspectText *aspect, const OpenGl_TextParam *param)
480{
481 OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
482 const FTFont* fnt = mgr->fontById( myFont );
483 if ( !fnt )
484 return;
485
486 // FTFont changes texture state when it renders and computes size for the text
487 glPushAttrib(GL_TEXTURE_BIT);
488
489 int widthFont, ascentFont, descentFont;
490 StringSize( str, widthFont, ascentFont, descentFont );
491
f3f08423 492 GLdouble xdis = 0., ydis = 0.;
2166f0fa
SK
493
494 float export_h = 1.;
495
f3f08423 496 MultilineTextRenderer aRenderer( str,
497 xdis,
498 ydis,
499 param,
500 fnt,
501 widthFont,
502 ascentFont,
503 descentFont );
504
2166f0fa
SK
505 glMatrixMode(GL_MODELVIEW);
506 glPushMatrix();
507 if (is2d)
508 {
509 glLoadIdentity();
510 glTranslatef(x, y, 0.f);
511 glRotatef( 180, 1, 0, 0 );
512 }
513 else
514 {
515 const GLdouble identityMatrix[4][4] =
516 {
517 {1.,0.,0.,0.},
518 {0.,1.,0.,0.},
519 {0.,0.,1.,0.},
520 {0.,0.,0.,1.}
521 };
522
523 GLdouble projMatrix[4][4], modelMatrix[4][4];
524 GLint viewport[4];
525
526 GLdouble wx, wy, wz;
527 GLdouble x1, y1, z1;
528 GLdouble x2, y2, z2;
529
530 glGetDoublev( GL_MODELVIEW_MATRIX, (GLdouble*)modelMatrix );
531 glGetDoublev( GL_PROJECTION_MATRIX, (GLdouble*)projMatrix );
532 glGetIntegerv( GL_VIEWPORT, (GLint*)viewport );
533
534 gluProject( x, y, z,
535 (GLdouble*)modelMatrix,
536 (GLdouble*)projMatrix,
537 (GLint*)viewport,
538 &wx, &wy, &wz );
539 glLoadIdentity();
540 gluUnProject( wx, wy, wz,
541 (GLdouble*)identityMatrix, (GLdouble*)projMatrix, (GLint*)viewport,
542 &x1, &y1 , &z1 );
543
544 GLdouble h = (GLdouble)fnt->FaceSize();
545
546 gluUnProject( wx, wy + h - 1., wz,
547 (GLdouble*)identityMatrix, (GLdouble*)projMatrix, (GLint*)viewport,
548 &x2, &y2, &z2 );
549
550 h = (y2-y1)/h;
551
552 glTranslated( x1, y1 , z1 );
553 glRotated(aspect->Angle(), 0, 0, 1);
554 glTranslated(xdis, ydis, 0);
555
556 if( !aspect->IsZoomable() )
557 {
34a44cbd 558#ifdef WNT
559 // if the context has assigned printer context, use it's parameters
560 OpenGl_PrinterContext* aPrinterContext =
561 OpenGl_PrinterContext::GetPrinterContext( GET_GL_CONTEXT() );
562 if( aPrinterContext )
563 {
564 // get printing scaling in x and y dimensions
565 GLfloat aTextScalex = 1, aTextScaley = 1;
566 aPrinterContext->GetScale( aTextScalex, aTextScaley );
567
568 // text should be scaled in all directions with same
569 // factor to save its proportions, so use height (y) scaling
570 // as it is better for keeping text/3d graphics proportions
571 glScalef( aTextScaley, aTextScaley, aTextScaley );
572 }
573#endif
2166f0fa
SK
574 glScaled( h, h, h );
575 }
576 else
577 {
f3f08423 578 export_h = (float)h;
2166f0fa
SK
579 }
580 }
581
582 GLint renderMode;
583 glGetIntegerv(GL_RENDER_MODE, &renderMode);
584 if ( renderMode == GL_FEEDBACK )
585 {
586#ifdef HAVE_GL2PS
c320e557 587 export_h = (float)fnt->FaceSize() / export_h;
f3f08423 588 for ( ; aRenderer.More(); aRenderer.Next() )
589 {
590 // x, y, z coordinates are used here as zero values, because position of the text
591 // and alignment is calculated in the code above using glTranslated methods
592 ExportText( aRenderer.GetValue(), is2d, 0, 0, 0, aspect, param, (short)export_h );
593 glTranslated(0, -(GLdouble)fnt->FaceSize(), 0);
594 }
2166f0fa
SK
595#endif
596 }
597 else
598 {
f3f08423 599 for ( ; aRenderer.More(); aRenderer.Next() )
600 {
601 mgr->render_text( myFont, aRenderer.GetValue(), is2d );
602 glTranslated(0, -(GLdouble)fnt->FaceSize(), 0);
603 }
2166f0fa 604 }
f3f08423 605 glPopMatrix();
2166f0fa
SK
606 glPopAttrib();
607}
608
609/*-----------------------------------------------------------------------------*/
610
2166f0fa 611void OpenGl_Display::ExportText (const wchar_t* text, const int is2d, const float x, const float y, const float z,
c320e557 612 const OpenGl_AspectText *aspect, const OpenGl_TextParam *param, const short height)
2166f0fa
SK
613{
614#ifdef HAVE_GL2PS
615
2166f0fa
SK
616 const char* fontname = aspect->Font();
617 float angle = aspect->Angle();
618
619 GLubyte zero = 0;
620 char ps_font[64];
621
622 getGL2PSFontName(fontname, ps_font);
623
624 if( is2d )
625 glRasterPos2f( x, y );
626 else
627 glRasterPos3f( x, y, z );
628
629 glBitmap( 1, 1, 0, 0, 0, 0, &zero );
630
631 //szv: workaround for gl2ps!
632 const int len = 4 * (wcslen(text) + 1); //szv: should be more than enough
633 char *astr = new char[len];
634 wcstombs(astr,text,len);
f3f08423 635
636 // Standard GL2PS's alignment isn't used, because it doesn't work correctly
637 // for all formats, therefore alignment is calculated manually relative
638 // to the bottom-left corner, which corresponds to the GL2PS_TEXT_BL value
639 gl2psTextOpt(astr, ps_font, height, GL2PS_TEXT_BL, angle);
2166f0fa
SK
640 delete[] astr;
641
642#endif
643}