1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMathTextFreeTypeTextRenderer.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkMathTextFreeTypeTextRenderer.h"
17 
18 #include "vtkFreeTypeTools.h"
19 #include "vtkMathTextUtilities.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkStdString.h"
22 #include "vtkUnicodeString.h"
23 #include "vtkTextProperty.h"
24 
25 //------------------------------------------------------------------------------
vtkObjectFactoryNewMacro(vtkMathTextFreeTypeTextRenderer)26 vtkObjectFactoryNewMacro(vtkMathTextFreeTypeTextRenderer)
27 
28 //------------------------------------------------------------------------------
29 void vtkMathTextFreeTypeTextRenderer::PrintSelf(ostream &os, vtkIndent indent)
30 {
31   this->Superclass::PrintSelf(os, indent);
32 
33   if (this->FreeTypeTools)
34   {
35     os << indent << "FreeTypeTools:" << endl;
36     this->FreeTypeTools->PrintSelf(os, indent.GetNextIndent());
37   }
38   else
39   {
40     os << indent << "FreeTypeTools: (nullptr)" << endl;
41   }
42 
43   if (this->MathTextUtilities)
44   {
45     os << indent << "MathTextUtilities:" << endl;
46     this->MathTextUtilities->PrintSelf(os, indent.GetNextIndent());
47   }
48   else
49   {
50     os << indent << "MathTextUtilities: (nullptr)" << endl;
51   }
52 }
53 
54 //------------------------------------------------------------------------------
FreeTypeIsSupported()55 bool vtkMathTextFreeTypeTextRenderer::FreeTypeIsSupported()
56 {
57   return this->FreeTypeTools != nullptr;
58 }
59 
60 //------------------------------------------------------------------------------
MathTextIsSupported()61 bool vtkMathTextFreeTypeTextRenderer::MathTextIsSupported()
62 {
63   return this->MathTextUtilities != nullptr &&
64          this->MathTextUtilities->IsAvailable();
65 }
66 
67 //------------------------------------------------------------------------------
GetBoundingBoxInternal(vtkTextProperty * tprop,const vtkStdString & str,int bbox[4],int dpi,int backend)68 bool vtkMathTextFreeTypeTextRenderer::GetBoundingBoxInternal(
69     vtkTextProperty *tprop, const vtkStdString &str, int bbox[4], int dpi,
70     int backend)
71 {
72   if (!bbox || !tprop)
73   {
74     vtkErrorMacro("No bounding box container and/or text property supplied!");
75     return false;
76   }
77 
78   memset(bbox, 0, 4 * sizeof(int));
79   if (str.empty())
80   {
81     return true;
82   }
83 
84   if (static_cast<Backend>(backend) == Default)
85   {
86     backend = this->DefaultBackend;
87   }
88 
89   if (static_cast<Backend>(backend) == Detect)
90   {
91     backend = static_cast<int>(this->DetectBackend(str));
92   }
93 
94   switch (static_cast<Backend>(backend))
95   {
96     case MathText:
97       if (this->MathTextIsSupported())
98       {
99         if (this->MathTextUtilities->GetBoundingBox(tprop, str.c_str(), dpi,
100                                                     bbox))
101         {
102           return true;
103         }
104       }
105       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
106       VTK_FALLTHROUGH;
107     case FreeType:
108     {
109       vtkStdString cleanString(str);
110       this->CleanUpFreeTypeEscapes(cleanString);
111       // Interpret string as UTF-8, use the UTF-16 GetBoundingBox overload:
112       return this->FreeTypeTools->GetBoundingBox(
113             tprop, vtkUnicodeString::from_utf8(cleanString), dpi, bbox);
114     }
115     case Default:
116     case UserBackend:
117     default:
118       vtkDebugMacro("Unrecognized backend requested: " << backend);
119       break;
120     case Detect:
121       vtkDebugMacro("Unhandled 'Detect' backend requested!");
122       break;
123   }
124   return false;
125 }
126 
127 //------------------------------------------------------------------------------
GetBoundingBoxInternal(vtkTextProperty * tprop,const vtkUnicodeString & str,int bbox[],int dpi,int backend)128 bool vtkMathTextFreeTypeTextRenderer::GetBoundingBoxInternal(
129     vtkTextProperty *tprop, const vtkUnicodeString &str, int bbox[], int dpi,
130     int backend)
131 {
132   if (!bbox || !tprop)
133   {
134     vtkErrorMacro("No bounding box container and/or text property supplied!");
135     return false;
136   }
137 
138   memset(bbox, 0, 4 * sizeof(int));
139   if (str.empty())
140   {
141     return true;
142   }
143 
144   if (static_cast<Backend>(backend) == Default)
145   {
146     backend = this->DefaultBackend;
147   }
148 
149   if (static_cast<Backend>(backend) == Detect)
150   {
151     backend = static_cast<int>(this->DetectBackend(str));
152   }
153 
154   switch (static_cast<Backend>(backend))
155   {
156     case MathText:
157       if (this->MathTextIsSupported())
158       {
159         vtkDebugMacro("Converting UTF16 to UTF8 for MathText rendering.");
160         if (this->MathTextUtilities->GetBoundingBox(tprop, str.utf8_str(), dpi,
161                                                     bbox))
162         {
163           return true;
164         }
165       }
166       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
167       VTK_FALLTHROUGH;
168     case FreeType:
169     {
170       vtkUnicodeString cleanString(str);
171       this->CleanUpFreeTypeEscapes(cleanString);
172       return this->FreeTypeTools->GetBoundingBox(tprop, cleanString, dpi, bbox);
173     }
174     case Default:
175     case UserBackend:
176     default:
177       vtkDebugMacro("Unrecognized backend requested: " << backend);
178       break;
179     case Detect:
180       vtkDebugMacro("Unhandled 'Detect' backend requested!");
181       break;
182   }
183   return false;
184 }
185 
186 //------------------------------------------------------------------------------
GetMetricsInternal(vtkTextProperty * tprop,const vtkStdString & str,vtkTextRenderer::Metrics & metrics,int dpi,int backend)187 bool vtkMathTextFreeTypeTextRenderer::GetMetricsInternal(
188     vtkTextProperty *tprop, const vtkStdString &str,
189     vtkTextRenderer::Metrics &metrics, int dpi, int backend)
190 {
191   if (!tprop)
192   {
193     vtkErrorMacro("No text property supplied!");
194     return false;
195   }
196 
197   metrics = Metrics();
198   if (str.empty())
199   {
200     return true;
201   }
202 
203   if (static_cast<Backend>(backend) == Default)
204   {
205     backend = this->DefaultBackend;
206   }
207 
208   if (static_cast<Backend>(backend) == Detect)
209   {
210     backend = static_cast<int>(this->DetectBackend(str));
211   }
212 
213   switch (static_cast<Backend>(backend))
214   {
215     case MathText:
216       if (this->MathTextIsSupported())
217       {
218         if (this->MathTextUtilities->GetMetrics(tprop, str.c_str(), dpi,
219                                                 metrics))
220         {
221           return true;
222         }
223       }
224       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
225       VTK_FALLTHROUGH;
226     case FreeType:
227     {
228       vtkStdString cleanString(str);
229       this->CleanUpFreeTypeEscapes(cleanString);
230       // Interpret string as UTF-8, use the UTF-16 GetBoundingBox overload:
231       return this->FreeTypeTools->GetMetrics(
232             tprop, vtkUnicodeString::from_utf8(cleanString), dpi, metrics);
233     }
234     case Default:
235     case UserBackend:
236     default:
237       vtkDebugMacro("Unrecognized backend requested: " << backend);
238       break;
239     case Detect:
240       vtkDebugMacro("Unhandled 'Detect' backend requested!");
241       break;
242   }
243   return false;
244 }
245 
246 //------------------------------------------------------------------------------
GetMetricsInternal(vtkTextProperty * tprop,const vtkUnicodeString & str,vtkTextRenderer::Metrics & metrics,int dpi,int backend)247 bool vtkMathTextFreeTypeTextRenderer::GetMetricsInternal(
248     vtkTextProperty *tprop, const vtkUnicodeString &str,
249     vtkTextRenderer::Metrics &metrics, int dpi, int backend)
250 {
251   if (!tprop)
252   {
253     vtkErrorMacro("No text property supplied!");
254     return false;
255   }
256 
257   metrics = Metrics();
258   if (str.empty())
259   {
260     return true;
261   }
262 
263   if (static_cast<Backend>(backend) == Default)
264   {
265     backend = this->DefaultBackend;
266   }
267 
268   if (static_cast<Backend>(backend) == Detect)
269   {
270     backend = static_cast<int>(this->DetectBackend(str));
271   }
272 
273   switch (static_cast<Backend>(backend))
274   {
275     case MathText:
276       if (this->MathTextIsSupported())
277       {
278         vtkDebugMacro("Converting UTF16 to UTF8 for MathText rendering.");
279         if (this->MathTextUtilities->GetMetrics(tprop, str.utf8_str(), dpi,
280                                                 metrics))
281         {
282           return true;
283         }
284       }
285       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
286       VTK_FALLTHROUGH;
287     case FreeType:
288     {
289       vtkUnicodeString cleanString(str);
290       this->CleanUpFreeTypeEscapes(cleanString);
291       return this->FreeTypeTools->GetMetrics(tprop, cleanString, dpi, metrics);
292     }
293     case Default:
294     case UserBackend:
295     default:
296       vtkDebugMacro("Unrecognized backend requested: " << backend);
297       break;
298     case Detect:
299       vtkDebugMacro("Unhandled 'Detect' backend requested!");
300       break;
301   }
302   return false;
303 }
304 
305 //------------------------------------------------------------------------------
RenderStringInternal(vtkTextProperty * tprop,const vtkStdString & str,vtkImageData * data,int textDims[2],int dpi,int backend)306 bool vtkMathTextFreeTypeTextRenderer::RenderStringInternal(
307     vtkTextProperty *tprop, const vtkStdString &str, vtkImageData *data,
308     int textDims[2], int dpi, int backend)
309 {
310   if (!data || !tprop)
311   {
312     vtkErrorMacro("No image container and/or text property supplied!");
313     return false;
314   }
315 
316   if (static_cast<Backend>(backend) == Default)
317   {
318     backend = this->DefaultBackend;
319   }
320 
321   if (static_cast<Backend>(backend) == Detect)
322   {
323     backend = static_cast<int>(this->DetectBackend(str));
324   }
325 
326   switch (static_cast<Backend>(backend))
327   {
328     case MathText:
329       if (this->MathTextIsSupported())
330       {
331         if (this->MathTextUtilities->RenderString(str.c_str(), data, tprop,
332                                                   dpi, textDims))
333         {
334           return true;
335         }
336       }
337       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
338       VTK_FALLTHROUGH;
339     case FreeType:
340     {
341       vtkStdString cleanString(str);
342       this->CleanUpFreeTypeEscapes(cleanString);
343       // Interpret string as UTF-8, use the UTF-16 RenderString overload:
344       return this->FreeTypeTools->RenderString(
345             tprop, vtkUnicodeString::from_utf8(cleanString), dpi, data,
346             textDims);
347     }
348     case Default:
349     case UserBackend:
350     default:
351       vtkDebugMacro("Unrecognized backend requested: " << backend);
352       break;
353     case Detect:
354       vtkDebugMacro("Unhandled 'Detect' backend requested!");
355       break;
356   }
357   return false;
358 }
359 
360 //------------------------------------------------------------------------------
RenderStringInternal(vtkTextProperty * tprop,const vtkUnicodeString & str,vtkImageData * data,int textDims[],int dpi,int backend)361 bool vtkMathTextFreeTypeTextRenderer::RenderStringInternal(
362     vtkTextProperty *tprop, const vtkUnicodeString &str, vtkImageData *data,
363     int textDims[], int dpi, int backend)
364 {
365   if (!data || !tprop)
366   {
367     vtkErrorMacro("No image container and/or text property supplied!");
368     return false;
369   }
370 
371   if (static_cast<Backend>(backend) == Default)
372   {
373     backend = this->DefaultBackend;
374   }
375 
376   if (static_cast<Backend>(backend) == Detect)
377   {
378     backend = static_cast<int>(this->DetectBackend(str));
379   }
380 
381   switch (static_cast<Backend>(backend))
382   {
383     case MathText:
384       if (this->MathTextIsSupported())
385       {
386         vtkDebugMacro("Converting UTF16 to UTF8 for MathText rendering.");
387         if (this->MathTextUtilities->RenderString(str.utf8_str(), data, tprop,
388                                                   dpi, textDims))
389         {
390           return true;
391         }
392       }
393       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
394       VTK_FALLTHROUGH;
395     case FreeType:
396     {
397       vtkUnicodeString cleanString(str);
398       this->CleanUpFreeTypeEscapes(cleanString);
399       return this->FreeTypeTools->RenderString(tprop, cleanString, dpi, data,
400                                                textDims);
401     }
402     case Default:
403     case UserBackend:
404     default:
405       vtkDebugMacro("Unrecognized backend requested: " << backend);
406       break;
407     case Detect:
408       vtkDebugMacro("Unhandled 'Detect' backend requested!");
409       break;
410   }
411   return false;
412 }
413 
414 //------------------------------------------------------------------------------
GetConstrainedFontSizeInternal(const vtkStdString & str,vtkTextProperty * tprop,int targetWidth,int targetHeight,int dpi,int backend)415 int vtkMathTextFreeTypeTextRenderer::GetConstrainedFontSizeInternal(
416     const vtkStdString &str, vtkTextProperty *tprop, int targetWidth,
417     int targetHeight, int dpi, int backend)
418 {
419   if (!tprop)
420   {
421     vtkErrorMacro("No text property supplied!");
422     return false;
423   }
424 
425   if (static_cast<Backend>(backend) == Default)
426   {
427     backend = this->DefaultBackend;
428   }
429 
430   if (static_cast<Backend>(backend) == Detect)
431   {
432     backend = static_cast<int>(this->DetectBackend(str));
433   }
434 
435   switch (static_cast<Backend>(backend))
436   {
437     case MathText:
438       if (this->MathTextIsSupported())
439       {
440         if (this->MathTextUtilities->GetConstrainedFontSize(str.c_str(), tprop,
441                                                             targetWidth,
442                                                             targetHeight,
443                                                             dpi) != -1)
444         {
445           return tprop->GetFontSize();
446         }
447       }
448       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
449       VTK_FALLTHROUGH;
450     case FreeType:
451     {
452       vtkStdString cleanString(str);
453       this->CleanUpFreeTypeEscapes(cleanString);
454       return this->FreeTypeTools->GetConstrainedFontSize(cleanString, tprop,
455                                                          dpi, targetWidth,
456                                                          targetHeight);
457     }
458     case Default:
459     case UserBackend:
460     default:
461       vtkDebugMacro("Unrecognized backend requested: " << backend);
462       break;
463     case Detect:
464       vtkDebugMacro("Unhandled 'Detect' backend requested!");
465       break;
466   }
467   return false;
468 }
469 
470 //------------------------------------------------------------------------------
GetConstrainedFontSizeInternal(const vtkUnicodeString & str,vtkTextProperty * tprop,int targetWidth,int targetHeight,int dpi,int backend)471 int vtkMathTextFreeTypeTextRenderer::GetConstrainedFontSizeInternal(
472     const vtkUnicodeString &str, vtkTextProperty *tprop, int targetWidth,
473     int targetHeight, int dpi, int backend)
474 {
475   if (!tprop)
476   {
477     vtkErrorMacro("No text property supplied!");
478     return false;
479   }
480 
481   if (static_cast<Backend>(backend) == Default)
482   {
483     backend = this->DefaultBackend;
484   }
485 
486   if (static_cast<Backend>(backend) == Detect)
487   {
488     backend = static_cast<int>(this->DetectBackend(str));
489   }
490 
491   switch (static_cast<Backend>(backend))
492   {
493     case MathText:
494       if (this->MathTextIsSupported())
495       {
496         vtkDebugMacro("Converting UTF16 to UTF8 for MathText rendering.");
497         if (this->MathTextUtilities->GetConstrainedFontSize(str.utf8_str(),
498                                                             tprop, targetWidth,
499                                                             targetHeight,
500                                                             dpi) != -1)
501         {
502           return tprop->GetFontSize();
503         }
504       }
505       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
506       VTK_FALLTHROUGH;
507     case FreeType:
508     {
509       vtkUnicodeString cleanString(str);
510       this->CleanUpFreeTypeEscapes(cleanString);
511       return this->FreeTypeTools->GetConstrainedFontSize(cleanString, tprop,
512                                                          dpi, targetWidth,
513                                                          targetHeight);
514     }
515     case Default:
516     case UserBackend:
517     default:
518       vtkDebugMacro("Unrecognized backend requested: " << backend);
519       break;
520     case Detect:
521       vtkDebugMacro("Unhandled 'Detect' backend requested!");
522       break;
523   }
524   return false;
525 }
526 
527 //------------------------------------------------------------------------------
StringToPathInternal(vtkTextProperty * tprop,const vtkStdString & str,vtkPath * path,int dpi,int backend)528 bool vtkMathTextFreeTypeTextRenderer::StringToPathInternal(
529     vtkTextProperty *tprop, const vtkStdString &str, vtkPath *path, int dpi,
530     int backend)
531 {
532   if (!path || !tprop)
533   {
534     vtkErrorMacro("No path container and/or text property supplied!");
535     return false;
536   }
537 
538   if (static_cast<Backend>(backend) == Default)
539   {
540     backend = this->DefaultBackend;
541   }
542 
543   if (static_cast<Backend>(backend) == Detect)
544   {
545     backend = static_cast<int>(this->DetectBackend(str));
546   }
547 
548   switch (static_cast<Backend>(backend))
549   {
550     case MathText:
551       if (this->MathTextIsSupported())
552       {
553         if (this->MathTextUtilities->StringToPath(str.c_str(), path, tprop,
554                                                   dpi))
555         {
556           return true;
557         }
558       }
559       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
560       VTK_FALLTHROUGH;
561     case FreeType:
562     {
563       vtkStdString cleanString(str);
564       this->CleanUpFreeTypeEscapes(cleanString);
565       return this->FreeTypeTools->StringToPath(tprop, str, dpi, path);
566     }
567     case Default:
568     case UserBackend:
569     default:
570       vtkDebugMacro("Unrecognized backend requested: " << backend);
571       break;
572     case Detect:
573       vtkDebugMacro("Unhandled 'Detect' backend requested!");
574       break;
575   }
576   return false;
577 }
578 
579 //------------------------------------------------------------------------------
StringToPathInternal(vtkTextProperty * tprop,const vtkUnicodeString & str,vtkPath * path,int dpi,int backend)580 bool vtkMathTextFreeTypeTextRenderer::StringToPathInternal(
581     vtkTextProperty *tprop, const vtkUnicodeString &str, vtkPath *path, int dpi,
582     int backend)
583 {
584   if (!path || !tprop)
585   {
586     vtkErrorMacro("No path container and/or text property supplied!");
587     return false;
588   }
589 
590   if (static_cast<Backend>(backend) == Default)
591   {
592     backend = this->DefaultBackend;
593   }
594 
595   if (static_cast<Backend>(backend) == Detect)
596   {
597     backend = static_cast<int>(this->DetectBackend(str));
598   }
599 
600   switch (static_cast<Backend>(backend))
601   {
602     case MathText:
603       if (this->MathTextIsSupported())
604       {
605         vtkDebugMacro("Converting UTF16 to UTF8 for MathText rendering.");
606         if (this->MathTextUtilities->StringToPath(str.utf8_str(), path, tprop,
607                                                   dpi))
608         {
609           return true;
610         }
611       }
612       vtkDebugMacro("MathText unavailable. Falling back to FreeType.");
613       VTK_FALLTHROUGH;
614     case FreeType:
615     {
616       vtkUnicodeString cleanString(str);
617       this->CleanUpFreeTypeEscapes(cleanString);
618       return this->FreeTypeTools->StringToPath(tprop, str, dpi, path);
619     }
620     case Default:
621     case UserBackend:
622     default:
623       vtkDebugMacro("Unrecognized backend requested: " << backend);
624       break;
625     case Detect:
626       vtkDebugMacro("Unhandled 'Detect' backend requested!");
627       break;
628   }
629   return false;
630 }
631 
632 //------------------------------------------------------------------------------
SetScaleToPowerOfTwoInternal(bool scale)633 void vtkMathTextFreeTypeTextRenderer::SetScaleToPowerOfTwoInternal(bool scale)
634 {
635   if (this->FreeTypeTools)
636   {
637     this->FreeTypeTools->SetScaleToPowerTwo(scale);
638   }
639   if (this->MathTextUtilities)
640   {
641     this->MathTextUtilities->SetScaleToPowerOfTwo(scale);
642   }
643 }
644 
645 //------------------------------------------------------------------------------
vtkMathTextFreeTypeTextRenderer()646 vtkMathTextFreeTypeTextRenderer::vtkMathTextFreeTypeTextRenderer()
647 {
648   this->FreeTypeTools = vtkFreeTypeTools::GetInstance();
649   this->MathTextUtilities = vtkMathTextUtilities::GetInstance();
650 }
651 
652 //------------------------------------------------------------------------------
653 vtkMathTextFreeTypeTextRenderer::~vtkMathTextFreeTypeTextRenderer() = default;
654