FreeRDP
rdtk_font.c
1 
19 #include <rdtk/config.h>
20 
21 #include <errno.h>
22 
23 #include <winpr/config.h>
24 #include <winpr/wtypes.h>
25 #include <winpr/crt.h>
26 #include <winpr/assert.h>
27 #include <winpr/path.h>
28 #include <winpr/file.h>
29 #include <winpr/print.h>
30 
31 #include "rdtk_engine.h"
32 #include "rdtk_resources.h"
33 #include "rdtk_surface.h"
34 
35 #include "rdtk_font.h"
36 
37 #if defined(WINPR_WITH_PNG)
38 #define FILE_EXT "png"
39 #else
40 #define FILE_EXT "bmp"
41 #endif
42 
43 static int rdtk_font_draw_glyph(rdtkSurface* surface, int nXDst, int nYDst, rdtkFont* font,
44  rdtkGlyph* glyph)
45 {
46  WINPR_ASSERT(surface);
47  WINPR_ASSERT(font);
48  WINPR_ASSERT(glyph);
49 
50  nXDst += glyph->offsetX;
51  nYDst += glyph->offsetY;
52  const int nXSrc = glyph->rectX;
53  const int nYSrc = glyph->rectY;
54  const int nWidth = glyph->rectWidth;
55  const int nHeight = glyph->rectHeight;
56  const uint32_t nSrcStep = font->image->scanline;
57  const uint8_t* pSrcData = font->image->data;
58  uint8_t* pDstData = surface->data;
59  const uint32_t nDstStep = surface->scanline;
60 
61  for (int y = 0; y < nHeight; y++)
62  {
63  const uint8_t* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (nXSrc * 4)];
64  uint8_t* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
65 
66  for (int x = 0; x < nWidth; x++)
67  {
68  uint8_t B = pSrcPixel[0];
69  uint8_t G = pSrcPixel[1];
70  uint8_t R = pSrcPixel[2];
71  uint8_t A = pSrcPixel[3];
72  pSrcPixel += 4;
73 
74  if (1)
75  {
76  /* tint black */
77  R = 255 - R;
78  G = 255 - G;
79  B = 255 - B;
80  }
81 
82  if (A == 255)
83  {
84  pDstPixel[0] = B;
85  pDstPixel[1] = G;
86  pDstPixel[2] = R;
87  }
88  else
89  {
90  R = (R * A) / 255;
91  G = (G * A) / 255;
92  B = (B * A) / 255;
93  pDstPixel[0] = B + (pDstPixel[0] * (255 - A) + (255 / 2)) / 255;
94  pDstPixel[1] = G + (pDstPixel[1] * (255 - A) + (255 / 2)) / 255;
95  pDstPixel[2] = R + (pDstPixel[2] * (255 - A) + (255 / 2)) / 255;
96  }
97 
98  pDstPixel[3] = 0xFF;
99  pDstPixel += 4;
100  }
101  }
102 
103  return 1;
104 }
105 
106 int rdtk_font_draw_text(rdtkSurface* surface, uint16_t nXDst, uint16_t nYDst, rdtkFont* font,
107  const char* text)
108 {
109  WINPR_ASSERT(surface);
110  WINPR_ASSERT(font);
111  WINPR_ASSERT(text);
112 
113  const size_t length = strlen(text);
114  for (size_t index = 0; index < length; index++)
115  {
116  rdtkGlyph* glyph = &font->glyphs[text[index] - 32];
117  rdtk_font_draw_glyph(surface, nXDst, nYDst, font, glyph);
118  nXDst += (glyph->width + 1);
119  }
120 
121  return 1;
122 }
123 
124 int rdtk_font_text_draw_size(rdtkFont* font, uint16_t* width, uint16_t* height, const char* text)
125 {
126  WINPR_ASSERT(font);
127  WINPR_ASSERT(width);
128  WINPR_ASSERT(height);
129  WINPR_ASSERT(text);
130 
131  *width = 0;
132  *height = 0;
133  const size_t length = strlen(text);
134  for (size_t index = 0; index < length; index++)
135  {
136  const size_t glyphIndex = text[index] - 32;
137 
138  if (glyphIndex < font->glyphCount)
139  {
140  rdtkGlyph* glyph = &font->glyphs[glyphIndex];
141  *width += (glyph->width + 1);
142  }
143  }
144 
145  *height = font->height + 2;
146  return 1;
147 }
148 
149 static char* rdtk_font_load_descriptor_file(const char* filename, size_t* pSize)
150 {
151  WINPR_ASSERT(filename);
152  WINPR_ASSERT(pSize);
153 
154  union
155  {
156  size_t s;
157  INT64 i64;
158  } fileSize;
159  FILE* fp = winpr_fopen(filename, "r");
160 
161  if (!fp)
162  return NULL;
163 
164  if (_fseeki64(fp, 0, SEEK_END) != 0)
165  goto fail;
166  fileSize.i64 = _ftelli64(fp);
167  if (_fseeki64(fp, 0, SEEK_SET) != 0)
168  goto fail;
169 
170  if (fileSize.i64 < 1)
171  goto fail;
172 
173  uint8_t* buffer = (uint8_t*)malloc(fileSize.s + 2);
174 
175  if (!buffer)
176  goto fail;
177 
178  size_t readSize = fread(buffer, fileSize.s, 1, fp);
179  if (readSize == 0)
180  {
181  if (!ferror(fp))
182  readSize = fileSize.s;
183  }
184 
185  (void)fclose(fp);
186 
187  if (readSize < 1)
188  {
189  free(buffer);
190  return NULL;
191  }
192 
193  buffer[fileSize.s] = '\0';
194  buffer[fileSize.s + 1] = '\0';
195  *pSize = fileSize.s;
196  return (char*)buffer;
197 
198 fail:
199  (void)fclose(fp);
200  return NULL;
201 }
202 
203 static int rdtk_font_convert_descriptor_code_to_utf8(const char* str, uint8_t* utf8)
204 {
205  WINPR_ASSERT(str);
206  WINPR_ASSERT(utf8);
207 
208  const size_t len = strlen(str);
209  *((uint32_t*)utf8) = 0;
210 
211  if (len < 1)
212  return 1;
213 
214  if (len == 1)
215  {
216  if ((str[0] > 31) && (str[0] < 127))
217  {
218  utf8[0] = str[0];
219  }
220  }
221  else
222  {
223  if (str[0] == '&')
224  {
225  const char* acc = &str[1];
226 
227  if (strcmp(acc, "quot;") == 0)
228  utf8[0] = '"';
229  else if (strcmp(acc, "amp;") == 0)
230  utf8[0] = '&';
231  else if (strcmp(acc, "lt;") == 0)
232  utf8[0] = '<';
233  else if (strcmp(acc, "gt;") == 0)
234  utf8[0] = '>';
235  }
236  }
237 
238  return 1;
239 }
240 
241 static int rdtk_font_parse_descriptor_buffer(rdtkFont* font, uint8_t* buffer, size_t size)
242 {
243  WINPR_ASSERT(font);
244 
245  char* p = strstr((char*)buffer, "<?xml version=\"1.0\" encoding=\"utf-8\"?>");
246 
247  if (!p)
248  return -1;
249 
250  p += sizeof("<?xml version=\"1.0\" encoding=\"utf-8\"?>") - 1;
251  p = strstr(p, "<Font ");
252 
253  if (!p)
254  return -1;
255 
256  p += sizeof("<Font ") - 1;
257  /* find closing font tag */
258  char* end = strstr(p, "</Font>");
259 
260  if (!end)
261  return -1;
262 
263  /* parse font size */
264  p = strstr(p, "size=\"");
265 
266  if (!p)
267  return -1;
268 
269  p += sizeof("size=\"") - 1;
270  char* q = strchr(p, '"');
271 
272  if (!q)
273  return -1;
274 
275  *q = '\0';
276  errno = 0;
277  {
278  long val = strtol(p, NULL, 0);
279 
280  if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
281  return -1;
282 
283  font->size = (UINT32)val;
284  }
285  *q = '"';
286 
287  if (font->size <= 0)
288  return -1;
289 
290  p = q + 1;
291  /* parse font family */
292  p = strstr(p, "family=\"");
293 
294  if (!p)
295  return -1;
296 
297  p += sizeof("family=\"") - 1;
298  q = strchr(p, '"');
299 
300  if (!q)
301  return -1;
302 
303  *q = '\0';
304  font->family = _strdup(p);
305  *q = '"';
306 
307  if (!font->family)
308  return -1;
309 
310  p = q + 1;
311  /* parse font height */
312  p = strstr(p, "height=\"");
313 
314  if (!p)
315  return -1;
316 
317  p += sizeof("height=\"") - 1;
318  q = strchr(p, '"');
319 
320  if (!q)
321  return -1;
322 
323  *q = '\0';
324  errno = 0;
325  {
326  long val = strtol(p, NULL, 0);
327 
328  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
329  return -1;
330 
331  font->height = val;
332  }
333  *q = '"';
334 
335  if (font->height <= 0)
336  return -1;
337 
338  p = q + 1;
339  /* parse font style */
340  p = strstr(p, "style=\"");
341 
342  if (!p)
343  return -1;
344 
345  p += sizeof("style=\"") - 1;
346  q = strchr(p, '"');
347 
348  if (!q)
349  return -1;
350 
351  *q = '\0';
352  font->style = _strdup(p);
353  *q = '"';
354 
355  if (!font->style)
356  return -1;
357 
358  p = q + 1;
359  // printf("size: %d family: %s height: %d style: %s\n",
360  // font->size, font->family, font->height, font->style);
361  char* beg = p;
362  size_t count = 0;
363 
364  while (p < end)
365  {
366  p = strstr(p, "<Char ");
367 
368  if (!p)
369  return -1;
370 
371  p += sizeof("<Char ") - 1;
372  char* r = strstr(p, "/>");
373 
374  if (!r)
375  return -1;
376 
377  *r = '\0';
378  p = r + sizeof("/>");
379  *r = '/';
380  count++;
381  }
382 
383  if (count > UINT16_MAX)
384  return -1;
385 
386  font->glyphCount = (uint16_t)count;
387  font->glyphs = NULL;
388 
389  if (count > 0)
390  font->glyphs = (rdtkGlyph*)calloc(font->glyphCount, sizeof(rdtkGlyph));
391 
392  if (!font->glyphs)
393  return -1;
394 
395  p = beg;
396  size_t index = 0;
397 
398  while (p < end)
399  {
400  p = strstr(p, "<Char ");
401 
402  if (!p)
403  return -1;
404 
405  p += sizeof("<Char ") - 1;
406  char* r = strstr(p, "/>");
407 
408  if (!r)
409  return -1;
410 
411  *r = '\0';
412  /* start parsing glyph */
413  if (index >= font->glyphCount)
414  return -1;
415 
416  rdtkGlyph* glyph = &font->glyphs[index];
417  /* parse glyph width */
418  p = strstr(p, "width=\"");
419 
420  if (!p)
421  return -1;
422 
423  p += sizeof("width=\"") - 1;
424  q = strchr(p, '"');
425 
426  if (!q)
427  return -1;
428 
429  *q = '\0';
430  errno = 0;
431  {
432  long val = strtol(p, NULL, 0);
433 
434  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
435  return -1;
436 
437  glyph->width = (INT32)val;
438  }
439  *q = '"';
440 
441  if (glyph->width < 0)
442  return -1;
443 
444  p = q + 1;
445  /* parse glyph offset x,y */
446  p = strstr(p, "offset=\"");
447 
448  if (!p)
449  return -1;
450 
451  p += sizeof("offset=\"") - 1;
452  q = strchr(p, '"');
453 
454  if (!q)
455  return -1;
456 
457  char* tok[4] = { 0 };
458  *q = '\0';
459  tok[0] = p;
460  p = strchr(tok[0] + 1, ' ');
461 
462  if (!p)
463  return -1;
464 
465  *p = 0;
466  tok[1] = p + 1;
467  errno = 0;
468  {
469  long val = strtol(tok[0], NULL, 0);
470 
471  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
472  return -1;
473 
474  glyph->offsetX = (INT32)val;
475  }
476  {
477  long val = strtol(tok[1], NULL, 0);
478 
479  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
480  return -1;
481 
482  glyph->offsetY = (INT32)val;
483  }
484  *q = '"';
485  p = q + 1;
486  /* parse glyph rect x,y,w,h */
487  p = strstr(p, "rect=\"");
488 
489  if (!p)
490  return -1;
491 
492  p += sizeof("rect=\"") - 1;
493  q = strchr(p, '"');
494 
495  if (!q)
496  return -1;
497 
498  *q = '\0';
499  tok[0] = p;
500  p = strchr(tok[0] + 1, ' ');
501 
502  if (!p)
503  return -1;
504 
505  *p = 0;
506  tok[1] = p + 1;
507  p = strchr(tok[1] + 1, ' ');
508 
509  if (!p)
510  return -1;
511 
512  *p = 0;
513  tok[2] = p + 1;
514  p = strchr(tok[2] + 1, ' ');
515 
516  if (!p)
517  return -1;
518 
519  *p = 0;
520  tok[3] = p + 1;
521  errno = 0;
522  {
523  long val = strtol(tok[0], NULL, 0);
524 
525  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
526  return -1;
527 
528  glyph->rectX = (INT32)val;
529  }
530  {
531  long val = strtol(tok[1], NULL, 0);
532 
533  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
534  return -1;
535 
536  glyph->rectY = (INT32)val;
537  }
538  {
539  long val = strtol(tok[2], NULL, 0);
540 
541  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
542  return -1;
543 
544  glyph->rectWidth = (INT32)val;
545  }
546  {
547  long val = strtol(tok[3], NULL, 0);
548 
549  if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
550  return -1;
551 
552  glyph->rectHeight = (INT32)val;
553  }
554  *q = '"';
555  p = q + 1;
556  /* parse code */
557  p = strstr(p, "code=\"");
558 
559  if (!p)
560  return -1;
561 
562  p += sizeof("code=\"") - 1;
563  q = strchr(p, '"');
564 
565  if (!q)
566  return -1;
567 
568  *q = '\0';
569  rdtk_font_convert_descriptor_code_to_utf8(p, glyph->code);
570  *q = '"';
571  /* finish parsing glyph */
572  p = r + sizeof("/>");
573  *r = '/';
574  index++;
575  }
576 
577  return 1;
578 }
579 
580 static int rdtk_font_load_descriptor(rdtkFont* font, const char* filename)
581 {
582  size_t size = 0;
583 
584  WINPR_ASSERT(font);
585  char* buffer = rdtk_font_load_descriptor_file(filename, &size);
586 
587  if (!buffer)
588  return -1;
589 
590  return rdtk_font_parse_descriptor_buffer(font, (uint8_t*)buffer, size);
591 }
592 
593 rdtkFont* rdtk_font_new(rdtkEngine* engine, const char* path, const char* file)
594 {
595  size_t length = 0;
596  rdtkFont* font = NULL;
597  char* fontImageFile = NULL;
598  char* fontDescriptorFile = NULL;
599 
600  WINPR_ASSERT(engine);
601  WINPR_ASSERT(path);
602  WINPR_ASSERT(file);
603 
604  char* fontBaseFile = GetCombinedPath(path, file);
605  if (!fontBaseFile)
606  goto cleanup;
607 
608  winpr_asprintf(&fontImageFile, &length, "%s." FILE_EXT, fontBaseFile);
609  if (!fontImageFile)
610  goto cleanup;
611 
612  winpr_asprintf(&fontDescriptorFile, &length, "%s.xml", fontBaseFile);
613  if (!fontDescriptorFile)
614  goto cleanup;
615 
616  if (!winpr_PathFileExists(fontImageFile))
617  goto cleanup;
618 
619  if (!winpr_PathFileExists(fontDescriptorFile))
620  goto cleanup;
621 
622  font = (rdtkFont*)calloc(1, sizeof(rdtkFont));
623 
624  if (!font)
625  goto cleanup;
626 
627  font->engine = engine;
628  font->image = winpr_image_new();
629 
630  if (!font->image)
631  goto cleanup;
632 
633  const int status = winpr_image_read(font->image, fontImageFile);
634  if (status < 0)
635  goto cleanup;
636 
637  const int status2 = rdtk_font_load_descriptor(font, fontDescriptorFile);
638  if (status2 < 0)
639  goto cleanup;
640 
641  free(fontBaseFile);
642  free(fontImageFile);
643  free(fontDescriptorFile);
644  return font;
645 cleanup:
646  free(fontBaseFile);
647  free(fontImageFile);
648  free(fontDescriptorFile);
649 
650  rdtk_font_free(font);
651  return NULL;
652 }
653 
654 static rdtkFont* rdtk_embedded_font_new(rdtkEngine* engine, const uint8_t* imageData,
655  size_t imageSize, const uint8_t* descriptorData,
656  size_t descriptorSize)
657 {
658  size_t size = 0;
659  uint8_t* buffer = NULL;
660 
661  WINPR_ASSERT(engine);
662 
663  rdtkFont* font = (rdtkFont*)calloc(1, sizeof(rdtkFont));
664 
665  if (!font)
666  return NULL;
667 
668  font->engine = engine;
669  font->image = winpr_image_new();
670 
671  if (!font->image)
672  {
673  free(font);
674  return NULL;
675  }
676 
677  const int status = winpr_image_read_buffer(font->image, imageData, imageSize);
678  if (status < 0)
679  {
680  winpr_image_free(font->image, TRUE);
681  free(font);
682  return NULL;
683  }
684 
685  size = descriptorSize;
686  buffer = (uint8_t*)malloc(size);
687 
688  if (!buffer)
689  goto fail;
690 
691  CopyMemory(buffer, descriptorData, size);
692  const int status2 = rdtk_font_parse_descriptor_buffer(font, buffer, size);
693  free(buffer);
694 
695  if (status2 < 0)
696  goto fail;
697 
698  return font;
699 
700 fail:
701  rdtk_font_free(font);
702  return NULL;
703 }
704 
705 void rdtk_font_free(rdtkFont* font)
706 {
707  if (font)
708  {
709  free(font->family);
710  free(font->style);
711  winpr_image_free(font->image, TRUE);
712  free(font->glyphs);
713  free(font);
714  }
715 }
716 int rdtk_font_engine_init(rdtkEngine* engine)
717 {
718  WINPR_ASSERT(engine);
719  if (!engine->font)
720  {
721  const uint8_t* imageData = NULL;
722  const uint8_t* descriptorData = NULL;
723  const SSIZE_T imageSize =
724  rdtk_get_embedded_resource_file("source_serif_pro_regular_12." FILE_EXT, &imageData);
725  const SSIZE_T descriptorSize =
726  rdtk_get_embedded_resource_file("source_serif_pro_regular_12.xml", &descriptorData);
727 
728  if ((imageSize < 0) || (descriptorSize < 0))
729  return -1;
730 
731  engine->font = rdtk_embedded_font_new(engine, imageData, (size_t)imageSize, descriptorData,
732  (size_t)descriptorSize);
733  if (!engine->font)
734  return -1;
735  }
736 
737  return 1;
738 }
739 
740 int rdtk_font_engine_uninit(rdtkEngine* engine)
741 {
742  WINPR_ASSERT(engine);
743  if (engine->font)
744  {
745  rdtk_font_free(engine->font);
746  engine->font = NULL;
747  }
748 
749  return 1;
750 }