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