GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: gx_rich_text_view_tag_enter.c Lines: 154 154 100.0 %
Date: 2024-12-05 08:52:37 Branches: 78 78 100.0 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 *
4
 * This program and the accompanying materials are made available under the
5
 * terms of the MIT License which is available at
6
 * https://opensource.org/licenses/MIT.
7
 *
8
 * SPDX-License-Identifier: MIT
9
 **************************************************************************/
10
11
12
/**************************************************************************/
13
/**************************************************************************/
14
/**                                                                       */
15
/** GUIX Component                                                        */
16
/**                                                                       */
17
/**   Rich Text View Management (Rich Text View)                          */
18
/**                                                                       */
19
/**************************************************************************/
20
21
#define GX_SOURCE_CODE
22
23
24
/* Include necessary system files.  */
25
26
#include "gx_api.h"
27
#include "gx_rich_text_view.h"
28
#include "gx_utility.h"
29
#include "gx_system.h"
30
31
/* Define rich text end tags. */
32
static GX_CONST GX_CHAR close_tag_bold[] = "</b>";
33
static GX_CONST GX_CHAR close_tag_italic[] = "</i>";
34
static GX_CONST GX_CHAR close_tag_underline[] = "</u>";
35
static GX_CONST GX_CHAR close_tag_font[] = "</f>";
36
static GX_CONST GX_CHAR close_tag_color[] = "</c>";
37
static GX_CONST GX_CHAR close_tag_hicolor[] = "</hc>";
38
static GX_CONST GX_CHAR close_tag_align[] = "</align>";
39
40
/* Define end tag string list. */
41
static GX_CONST GX_STRING gx_rich_text_end_tag_list[] =
42
{
43
    {close_tag_bold, sizeof(close_tag_bold) - 1},
44
    {close_tag_italic, sizeof(close_tag_italic) - 1},
45
    {close_tag_underline, sizeof(close_tag_underline) - 1},
46
    {close_tag_font, sizeof(close_tag_font) - 1},
47
    {close_tag_color, sizeof(close_tag_color) - 1},
48
    {close_tag_hicolor, sizeof(close_tag_hicolor) - 1},
49
    {close_tag_align, sizeof(close_tag_align) - 1}
50
};
51
52
/* Define tag index to access tag list. */
53
enum gx_rich_text_tag_index
54
{
55
    GX_RICH_TEXT_TAG_INDEX_BOLD = 0,
56
    GX_RICH_TEXT_TAG_INDEX_ITALIC,
57
    GX_RICH_TEXT_TAG_INDEX_UNDERLINE,
58
    GX_RICH_TEXT_TAG_INDEX_FONT,
59
    GX_RICH_TEXT_TAG_INDEX_COLOR,
60
    GX_RICH_TEXT_TAG_INDEX_HICOLOR,
61
    GX_RICH_TEXT_TAG_INDEX_ALIGN
62
};
63
64
/**************************************************************************/
65
/*                                                                        */
66
/*  FUNCTION                                               RELEASE        */
67
/*                                                                        */
68
/*    _gx_rich_text_resource_id_read                      PORTABLE C      */
69
/*                                                           6.1          */
70
/*  AUTHOR                                                                */
71
/*                                                                        */
72
/*    Kenneth Maxwell, Microsoft Corporation                              */
73
/*                                                                        */
74
/*  DESCRIPTION                                                           */
75
/*                                                                        */
76
/*    Internal helper function to read resource id.                       */
77
/*                                                                        */
78
/*  INPUT                                                                 */
79
/*                                                                        */
80
/*    text                                  Pointer to rich text          */
81
/*    start_index                           Start index of the resource   */
82
/*                                            text                        */
83
/*    length                                Length of the input string    */
84
/*    resource_id                           Retrieved resource id         */
85
/*                                                                        */
86
/*  OUTPUT                                                                */
87
/*                                                                        */
88
/*    status                                                              */
89
/*                                                                        */
90
/*  CALLS                                                                 */
91
/*                                                                        */
92
/*    None                                                                */
93
/*                                                                        */
94
/*  CALLED BY                                                             */
95
/*                                                                        */
96
/*    _gx_rich_text_view_tag_open                                         */
97
/*                                                                        */
98
/*  RELEASE HISTORY                                                       */
99
/*                                                                        */
100
/*    DATE              NAME                      DESCRIPTION             */
101
/*                                                                        */
102
/*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
103
/*                                                                        */
104
/**************************************************************************/
105
633
static UINT _gx_rich_text_resource_id_read(GX_STRING *text, UINT *resource_id)
106
{
107
633
UINT    status = GX_FAILURE;
108
633
UINT    id = 0;
109
CHAR    ch;
110
111
633
    if (text -> gx_string_length < 2)
112
    {
113
31
        return status;
114
    }
115
116
602
    ch = text -> gx_string_ptr[0];
117
118

602
    if ((ch > '9') || (ch < '0'))
119
    {
120
        /* Invalid id. */
121
118
        return status;
122
    }
123
124
1155
    while (text -> gx_string_length > 0)
125
    {
126
1152
        ch = text -> gx_string_ptr[0];
127
128
1152
        text -> gx_string_ptr++;
129
1152
        text -> gx_string_length--;
130
131

1152
        if ((ch <= '9') && (ch >= '0'))
132
        {
133
            /* Read id value. */
134
671
            id = id * 10 + (UINT)(ch - '0');
135
        }
136
        else
137
        {
138
481
            if (ch == '>')
139
            {
140
425
                status = GX_SUCCESS;
141
            }
142
481
            break;
143
        }
144
    }
145
146
484
    if (status == GX_SUCCESS)
147
    {
148
425
        *resource_id = id;
149
    }
150
151
484
    return status;
152
}
153
154
/**************************************************************************/
155
/*                                                                        */
156
/*  FUNCTION                                               RELEASE        */
157
/*                                                                        */
158
/*    _gx_rich_text_view_tag_open                         PORTABLE C      */
159
/*                                                           6.1          */
160
/*  AUTHOR                                                                */
161
/*                                                                        */
162
/*    Kenneth Maxwell, Microsoft Corporation                              */
163
/*                                                                        */
164
/*  DESCRIPTION                                                           */
165
/*                                                                        */
166
/*    This function detects rich text open tag from specified text        */
167
/*    position and updated rich text draw style accordion to tag type.    */
168
/*                                                                        */
169
/*  INPUT                                                                 */
170
/*                                                                        */
171
/*    view                                  Rich text view control block  */
172
/*    text                                  Text for processing           */
173
/*    format                                Current rich text format      */
174
/*    handled_bytes                         Bytes been processed          */
175
/*                                                                        */
176
/*  OUTPUT                                                                */
177
/*                                                                        */
178
/*    status                                                              */
179
/*                                                                        */
180
/*  CALLS                                                                 */
181
/*                                                                        */
182
/*    _gx_utility_string_compare            Test if two strings equal     */
183
/*                                                                        */
184
/*  CALLED BY                                                             */
185
/*                                                                        */
186
/*    _gx_rich_text_view_tag_enter                                        */
187
/*                                                                        */
188
/*  RELEASE HISTORY                                                       */
189
/*                                                                        */
190
/*    DATE              NAME                      DESCRIPTION             */
191
/*                                                                        */
192
/*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
193
/*                                                                        */
194
/**************************************************************************/
195
2086
static UINT _gx_rich_text_view_tag_open(GX_RICH_TEXT_VIEW *view, GX_CONST GX_STRING *text, GX_RICH_TEXT_FORMAT *format, GX_UBYTE *handled_bytes)
196
{
197
2086
UINT                 status = GX_FAILURE;
198
GX_STRING            string;
199
GX_STRING            tag_string;
200
GX_UBYTE             alignment;
201
2086
UINT                 resource_id = 0;
202
GX_RICH_TEXT_CONTEXT context;
203
2086
UINT                 tag_index = 0;
204
205
2086
    string = *text;
206
207
2086
    context.gx_rich_text_context_format = *format;
208
209


2086
    switch (string.gx_string_ptr[1])
210
    {
211
425
    case 'b':
212
        /* <b>: bold */
213
425
        if (string.gx_string_ptr[2] == '>')
214
        {
215
397
            string.gx_string_ptr += 3;
216
397
            string.gx_string_length -= 3;
217
218
397
            format -> gx_rich_text_flags |= GX_RICH_TEXT_BOLD;
219
397
            if (format -> gx_rich_text_flags & GX_RICH_TEXT_ITALIC)
220
            {
221
247
                format -> gx_rich_text_font_id = view -> gx_rich_text_view_fonts.gx_rich_text_fonts_bold_italic_id;
222
            }
223
            else
224
            {
225
150
                format -> gx_rich_text_font_id = view -> gx_rich_text_view_fonts.gx_rich_text_fonts_bold_id;
226
            }
227
397
            tag_index = GX_RICH_TEXT_TAG_INDEX_BOLD;
228
397
            status = GX_SUCCESS;
229
        }
230
425
        break;
231
232
392
    case 'i':
233
        /* <i>: italic */
234
392
        if (string.gx_string_ptr[2] == '>')
235
        {
236
364
            string.gx_string_ptr += 3;
237
364
            string.gx_string_length -= 3;
238
239
364
            format -> gx_rich_text_flags |= GX_RICH_TEXT_ITALIC;
240
364
            if (format -> gx_rich_text_flags & GX_RICH_TEXT_BOLD)
241
            {
242
230
                format -> gx_rich_text_font_id = view -> gx_rich_text_view_fonts.gx_rich_text_fonts_bold_italic_id;
243
            }
244
            else
245
            {
246
134
                format -> gx_rich_text_font_id = view -> gx_rich_text_view_fonts.gx_rich_text_fonts_italic_id;
247
            }
248
364
            tag_index = GX_RICH_TEXT_TAG_INDEX_ITALIC;
249
364
            status = GX_SUCCESS;
250
        }
251
392
        break;
252
253
89
    case 'u':
254
        /* <u>: underline*/
255
89
        if (string.gx_string_ptr[2] == '>')
256
        {
257
61
            string.gx_string_ptr += 3;
258
61
            string.gx_string_length -= 3;
259
260
61
            format -> gx_rich_text_flags |= GX_RICH_TEXT_UNDERLINE;
261
262
61
            tag_index = GX_RICH_TEXT_TAG_INDEX_UNDERLINE;
263
61
            status = GX_SUCCESS;
264
        }
265
89
        break;
266
267
303
    case 'f':
268
        /* <f font-id>: select font */
269
303
        if (string.gx_string_ptr[2] == ' ')
270
        {
271
275
            string.gx_string_ptr += 3;
272
275
            string.gx_string_length -= 3;
273
274
            /* font id should <= 0xff */
275
275
            status = _gx_rich_text_resource_id_read(&string, &resource_id);
276
277
275
            if (status != GX_SUCCESS)
278
            {
279
68
                return status;
280
            }
281
282
207
            tag_index = GX_RICH_TEXT_TAG_INDEX_FONT;
283
207
            format -> gx_rich_text_font_id = resource_id;
284
        }
285
235
        break;
286
287
222
    case 'c':
288
        /* <c color_id>: text color */
289
222
        if (string.gx_string_ptr[2] == ' ')
290
        {
291
194
            string.gx_string_ptr += 3;
292
194
            string.gx_string_length -= 3;
293
294
194
            status = _gx_rich_text_resource_id_read(&string,  &resource_id);
295
296
194
            if (status != GX_SUCCESS)
297
            {
298
56
                return status;
299
            }
300
301
138
            tag_index = GX_RICH_TEXT_TAG_INDEX_COLOR;
302
138
            format -> gx_rich_text_color = resource_id;
303
        }
304
166
        break;
305
306
248
    case 'h':
307
        /* <hc color_id>: highlight color */
308
248
        if (string.gx_string_length > 3)
309
        {
310

220
            if (string.gx_string_ptr[2] == 'c' && string.gx_string_ptr[3] == ' ')
311
            {
312
164
                string.gx_string_ptr += 4;
313
164
                string.gx_string_length -= 4;
314
164
                status = _gx_rich_text_resource_id_read(&string, &resource_id);
315
316
164
                if (status != GX_SUCCESS)
317
                {
318
84
                    return status;
319
                }
320
321
80
                tag_index = GX_RICH_TEXT_TAG_INDEX_HICOLOR;
322
80
                format -> gx_rich_text_highlight_color = resource_id;
323
            }
324
        }
325
164
        break;
326
327
351
    case 'a':
328
        /* <align align-value>: alignment */
329
351
        tag_string.gx_string_ptr = "lign ";
330
351
        tag_string.gx_string_length = sizeof("lign ") - 1;
331
332
351
        string.gx_string_ptr += 2;
333
351
        string.gx_string_length -= 2;
334
335
351
        if (tag_string.gx_string_length <= string.gx_string_length)
336
        {
337
323
            if (_gx_utility_string_compare(&string, &tag_string, tag_string.gx_string_length) == GX_TRUE)
338
            {
339
295
                string.gx_string_ptr += tag_string.gx_string_length;
340
295
                string.gx_string_length -= tag_string.gx_string_length;
341
342

295
                switch (string.gx_string_ptr[0])
343
                {
344
117
                case 'c':
345
117
                    tag_string.gx_string_ptr = "enter>";
346
117
                    tag_string.gx_string_length = sizeof("enter>") - 1;
347
117
                    alignment = GX_RICH_TEXT_CENTER;
348
117
                    break;
349
350
61
                case 'r':
351
61
                    tag_string.gx_string_ptr = "ight>";
352
61
                    tag_string.gx_string_length = sizeof("ight>") - 1;
353
61
                    alignment = GX_RICH_TEXT_RIGHT;
354
61
                    break;
355
356
89
                case 'l':
357
89
                    tag_string.gx_string_ptr = "eft>";
358
89
                    tag_string.gx_string_length = sizeof("eft>") - 1;
359
89
                    alignment = GX_RICH_TEXT_LEFT;
360
89
                    break;
361
362
28
                default:
363
28
                    return GX_FAILURE;
364
                }
365
366
267
                string.gx_string_ptr++;
367
267
                string.gx_string_length--;
368
369
267
                if (tag_string.gx_string_length <= string.gx_string_length)
370
                {
371
239
                    if (_gx_utility_string_compare(&string, &tag_string, tag_string.gx_string_length) == GX_TRUE)
372
                    {
373
211
                        format -> gx_rich_text_flags &= (GX_UBYTE)(~GX_RICH_TEXT_ALIGN_MASK);
374
211
                        format -> gx_rich_text_flags |= alignment;
375
211
                        string.gx_string_length -= tag_string.gx_string_length;
376
377
211
                        tag_index = GX_RICH_TEXT_TAG_INDEX_ALIGN;
378
211
                        status = GX_SUCCESS;
379
                    }
380
                }
381
            }
382
        }
383
323
        break;
384
385
56
    default:
386
56
        return GX_FAILURE;
387
    }
388
389
1794
    if (status == GX_SUCCESS)
390
    {
391
1458
        context.gx_rich_text_context_tag = &gx_rich_text_end_tag_list[tag_index];
392
393
        /* Push draw style to stack. */
394
1458
        status = _gx_rich_text_view_context_push(&context);
395
    }
396
397
1794
    if (status == GX_SUCCESS)
398
    {
399
1230
        *handled_bytes = (GX_UBYTE)(text -> gx_string_length - string.gx_string_length);
400
    }
401
402
1794
    return status;
403
}
404
405
406
/**************************************************************************/
407
/*                                                                        */
408
/*  FUNCTION                                               RELEASE        */
409
/*                                                                        */
410
/*    _gx_rich_text_view_tag_close                        PORTABLE C      */
411
/*                                                           6.1          */
412
/*  AUTHOR                                                                */
413
/*                                                                        */
414
/*    Kenneth Maxwell, Microsoft Corporation                              */
415
/*                                                                        */
416
/*  DESCRIPTION                                                           */
417
/*                                                                        */
418
/*    This function detects rich text close tag from specified text       */
419
/*    position and updated rich text draw style accordion to tag type.    */
420
/*                                                                        */
421
/*  INPUT                                                                 */
422
/*                                                                        */
423
/*    text                                  Text for processing           */
424
/*    format                                Current rich text format      */
425
/*    handled_bytes                         Bytes been processed          */
426
/*                                                                        */
427
/*  OUTPUT                                                                */
428
/*                                                                        */
429
/*    status                                                              */
430
/*                                                                        */
431
/*  CALLS                                                                 */
432
/*                                                                        */
433
/*    _gx_utility_string_compare            Test if two strings equal     */
434
/*                                                                        */
435
/*  CALLED BY                                                             */
436
/*                                                                        */
437
/*    _gx_rich_text_view_tag_enter                                        */
438
/*                                                                        */
439
/*  RELEASE HISTORY                                                       */
440
/*                                                                        */
441
/*    DATE              NAME                      DESCRIPTION             */
442
/*                                                                        */
443
/*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
444
/*                                                                        */
445
/**************************************************************************/
446
1095
static UINT _gx_rich_text_view_tag_close(GX_CONST GX_STRING *text, GX_RICH_TEXT_FORMAT *format, GX_UBYTE *handled_bytes)
447
{
448
1095
UINT                 status = GX_FAILURE;
449
GX_RICH_TEXT_CONTEXT context;
450
GX_CONST GX_STRING  *tag;
451
452
    /* Peek tag in top of the stack. */
453
1095
    status = _gx_rich_text_view_context_peek(&context);
454
455
1095
    if (status == GX_SUCCESS)
456
    {
457
1033
        tag = context.gx_rich_text_context_tag;
458
459
        /* Test if we meet the end tag in stack top. */
460
1033
        if (_gx_utility_string_compare(text, tag, tag -> gx_string_length) == GX_TRUE)
461
        {
462
            /* Yes, it's time to pop the style. */
463
1014
            _gx_rich_text_view_context_pop();
464
1014
            *format = context.gx_rich_text_context_format;
465
1014
            *handled_bytes = (GX_UBYTE)tag -> gx_string_length;
466
        }
467
        else
468
        {
469
19
            status = GX_FAILURE;
470
        }
471
    }
472
473
1095
    return status;
474
}
475
476
477
/**************************************************************************/
478
/*                                                                        */
479
/*  FUNCTION                                               RELEASE        */
480
/*                                                                        */
481
/*    _gx_rich_text_view_tag_enter                        PORTABLE C      */
482
/*                                                           6.1          */
483
/*  AUTHOR                                                                */
484
/*                                                                        */
485
/*    Kenneth Maxwell, Microsoft Corporation                              */
486
/*                                                                        */
487
/*  DESCRIPTION                                                           */
488
/*                                                                        */
489
/*    This function detects rich text tag from specified text position,   */
490
/*    and updated rich text draw style accordion to tag type.             */
491
/*                                                                        */
492
/*  INPUT                                                                 */
493
/*                                                                        */
494
/*    view                                  Rich text view control block  */
495
/*    text                                  Text for processing           */
496
/*    format                                Current rich text format      */
497
/*    handled_bytes                         Bytes been processed          */
498
/*                                                                        */
499
/*  OUTPUT                                                                */
500
/*                                                                        */
501
/*    status                                                              */
502
/*                                                                        */
503
/*  CALLS                                                                 */
504
/*                                                                        */
505
/*    _gx_rich_text_view_tag_open           Process open tag              */
506
/*    _gx_rich_text_view_tag_close          Prpcess close tag             */
507
/*                                                                        */
508
/*  CALLED BY                                                             */
509
/*                                                                        */
510
/*    GUIX Internal Code                                                  */
511
/*                                                                        */
512
/*  RELEASE HISTORY                                                       */
513
/*                                                                        */
514
/*    DATE              NAME                      DESCRIPTION             */
515
/*                                                                        */
516
/*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
517
/*                                                                        */
518
/**************************************************************************/
519
3230
UINT _gx_rich_text_view_tag_enter(GX_RICH_TEXT_VIEW *view, GX_CONST GX_STRING *text, GX_RICH_TEXT_FORMAT *format, GX_UBYTE *handled_bytes)
520
{
521
522
3230
    if (text -> gx_string_ptr[0] != '<')
523
    {
524
        /* Tag must start with '<'. */
525
1
        return GX_FAILURE;
526
    }
527
528
3229
    if (text -> gx_string_length < 3)
529
    {
530
        /* Tag contains at least 3 character: <tag-name>, <tag-name tag-value>, </tag-name>. */
531
29
        return GX_FAILURE;
532
    }
533
534
3200
    if (text -> gx_string_ptr[1] == '/')
535
    {
536
1114
        if (text -> gx_string_length < 4)
537
        {
538
            /* Close tag contains at least 4 character: </tag-name>. */
539
19
            return GX_FAILURE;
540
        }
541
542
1095
        return _gx_rich_text_view_tag_close(text, format, handled_bytes);
543
    }
544
    else
545
    {
546
2086
        return _gx_rich_text_view_tag_open(view, text, format, handled_bytes);
547
    }
548
}
549