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