Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "mupdf/html.h"
2
3
static const char *inherit_list[] = {
4
"color",
5
"direction",
6
"font-family",
7
"font-size",
8
"font-style",
9
"font-variant",
10
"font-weight",
11
"letter-spacing",
12
"line-height",
13
"list-style-image",
14
"list-style-position",
15
"list-style-type",
16
"orphans",
17
"quotes",
18
"text-align",
19
"text-indent",
20
"text-transform",
21
"visibility",
22
"white-space",
23
"widows",
24
"word-spacing",
25
};
26
27
static const char *border_width_kw[] = {
28
"medium",
29
"thick",
30
"thin",
31
};
32
33
static const char *border_style_kw[] = {
34
"dashed",
35
"dotted",
36
"double",
37
"groove",
38
"hidden",
39
"inset",
40
"none",
41
"outset",
42
"ridge",
43
"solid",
44
};
45
46
static const char *color_kw[] = {
47
"aqua",
48
"black",
49
"blue",
50
"fuchsia",
51
"gray",
52
"green",
53
"lime",
54
"maroon",
55
"navy",
56
"olive",
57
"orange",
58
"purple",
59
"red",
60
"silver",
61
"teal",
62
"transparent",
63
"white",
64
"yellow",
65
};
66
67
static int
68
keyword_in_list(const char *name, const char **list, int n)
69
{
70
int l = 0;
71
int r = n - 1;
72
while (l <= r)
73
{
74
int m = (l + r) >> 1;
75
int c = strcmp(name, list[m]);
76
if (c < 0)
77
r = m - 1;
78
else if (c > 0)
79
l = m + 1;
80
else
81
return 1;
82
}
83
return 0;
84
}
85
86
/*
87
* Compute specificity
88
*/
89
90
static int
91
count_condition_ids(fz_css_condition *cond)
92
{
93
int n = 0;
94
while (cond)
95
{
96
if (cond->type == '#')
97
n ++;
98
cond = cond->next;
99
}
100
return n;
101
}
102
103
static int
104
count_selector_ids(fz_css_selector *sel)
105
{
106
int n = count_condition_ids(sel->cond);
107
if (sel->left && sel->right)
108
{
109
n += count_selector_ids(sel->left);
110
n += count_selector_ids(sel->right);
111
}
112
return n;
113
}
114
115
static int
116
count_condition_atts(fz_css_condition *cond)
117
{
118
int n = 0;
119
while (cond)
120
{
121
if (cond->type != '#' && cond->type != ':')
122
n ++;
123
cond = cond->next;
124
}
125
return n;
126
}
127
128
static int
129
count_selector_atts(fz_css_selector *sel)
130
{
131
int n = count_condition_atts(sel->cond);
132
if (sel->left && sel->right)
133
{
134
n += count_selector_atts(sel->left);
135
n += count_selector_atts(sel->right);
136
}
137
return n;
138
}
139
140
static int
141
count_condition_names(fz_css_condition *cond)
142
{
143
int n = 0;
144
while (cond)
145
{
146
if (cond->type == ':')
147
n ++;
148
cond = cond->next;
149
}
150
return n;
151
}
152
153
static int
154
count_selector_names(fz_css_selector *sel)
155
{
156
int n = count_condition_names(sel->cond);
157
if (sel->left && sel->right)
158
{
159
n += count_selector_names(sel->left);
160
n += count_selector_names(sel->right);
161
}
162
else if (sel->name)
163
{
164
n ++;
165
}
166
return n;
167
}
168
169
#define INLINE_SPECIFICITY 1000
170
171
static int
172
selector_specificity(fz_css_selector *sel)
173
{
174
int b = count_selector_ids(sel);
175
int c = count_selector_atts(sel);
176
int d = count_selector_names(sel);
177
return b * 100 + c * 10 + d;
178
}
179
180
/*
181
* Selector matching
182
*/
183
184
static int
185
match_id_condition(fz_xml *node, const char *p)
186
{
187
const char *s = fz_xml_att(node, "id");
188
if (s && !strcmp(s, p))
189
return 1;
190
return 0;
191
}
192
193
static int
194
match_class_condition(fz_xml *node, const char *p)
195
{
196
const char *s = fz_xml_att(node, "class");
197
char buf[1024];
198
if (s) {
199
strcpy(buf, s);
200
s = strtok(buf, " ");
201
while (s) {
202
if (!strcmp(s, p))
203
return 1;
204
s = strtok(NULL, " ");
205
}
206
}
207
return 0;
208
}
209
210
static int
211
match_condition(fz_css_condition *cond, fz_xml *node)
212
{
213
if (!cond)
214
return 1;
215
216
switch (cond->type) {
217
default: return 0;
218
case ':': return 0; /* don't support pseudo-classes */
219
case '#': if (!match_id_condition(node, cond->val)) return 0; break;
220
case '.': if (!match_class_condition(node, cond->val)) return 0; break;
221
}
222
223
return match_condition(cond->next, node);
224
}
225
226
static int
227
match_selector(fz_css_selector *sel, fz_xml *node)
228
{
229
if (!node)
230
return 0;
231
232
if (sel->combine)
233
{
234
/* descendant */
235
if (sel->combine == ' ')
236
{
237
fz_xml *parent = fz_xml_up(node);
238
while (parent)
239
{
240
if (match_selector(sel->left, parent))
241
if (match_selector(sel->right, node))
242
return 1;
243
parent = fz_xml_up(parent);
244
}
245
return 0;
246
}
247
248
/* child */
249
if (sel->combine == '>')
250
{
251
fz_xml *parent = fz_xml_up(node);
252
if (!parent)
253
return 0;
254
if (!match_selector(sel->left, parent))
255
return 0;
256
if (!match_selector(sel->right, node))
257
return 0;
258
}
259
260
/* adjacent */
261
if (sel->combine == '+')
262
{
263
fz_xml *prev = fz_xml_prev(node);
264
while (prev && !fz_xml_tag(prev))
265
prev = fz_xml_prev(prev);
266
if (!prev)
267
return 0;
268
if (!fz_xml_tag(prev))
269
return 0;
270
if (!match_selector(sel->left, prev))
271
return 0;
272
if (!match_selector(sel->right, node))
273
return 0;
274
}
275
}
276
277
if (sel->name)
278
{
279
if (strcmp(sel->name, fz_xml_tag(node)))
280
return 0;
281
}
282
283
if (sel->cond)
284
{
285
if (!match_condition(sel->cond, node))
286
return 0;
287
}
288
289
return 1;
290
}
291
292
/*
293
* Annotating nodes with properties and expanding shorthand forms.
294
*/
295
296
static int
297
count_values(fz_css_value *value)
298
{
299
int n = 0;
300
while (value)
301
{
302
n++;
303
value = value->next;
304
}
305
return n;
306
}
307
308
static void add_property(fz_css_match *match, const char *name, fz_css_value *value, int spec);
309
310
static void
311
add_shorthand_trbl(fz_css_match *match, fz_css_value *value, int spec,
312
const char *name_t, const char *name_r, const char *name_b, const char *name_l)
313
{
314
int n = count_values(value);
315
316
if (n == 1)
317
{
318
add_property(match, name_t, value, spec);
319
add_property(match, name_r, value, spec);
320
add_property(match, name_b, value, spec);
321
add_property(match, name_l, value, spec);
322
}
323
324
if (n == 2)
325
{
326
fz_css_value *a = value;
327
fz_css_value *b = value->next;
328
329
add_property(match, name_t, a, spec);
330
add_property(match, name_r, b, spec);
331
add_property(match, name_b, a, spec);
332
add_property(match, name_l, b, spec);
333
}
334
335
if (n == 3)
336
{
337
fz_css_value *a = value;
338
fz_css_value *b = value->next;
339
fz_css_value *c = value->next->next;
340
341
add_property(match, name_t, a, spec);
342
add_property(match, name_r, b, spec);
343
add_property(match, name_b, c, spec);
344
add_property(match, name_l, b, spec);
345
}
346
347
if (n == 4)
348
{
349
fz_css_value *a = value;
350
fz_css_value *b = value->next;
351
fz_css_value *c = value->next->next;
352
fz_css_value *d = value->next->next->next;
353
354
add_property(match, name_t, a, spec);
355
add_property(match, name_r, b, spec);
356
add_property(match, name_b, c, spec);
357
add_property(match, name_l, d, spec);
358
}
359
}
360
361
static void
362
add_shorthand_margin(fz_css_match *match, fz_css_value *value, int spec)
363
{
364
add_shorthand_trbl(match, value, spec,
365
"margin-top", "margin-right", "margin-bottom", "margin-left");
366
}
367
368
static void
369
add_shorthand_padding(fz_css_match *match, fz_css_value *value, int spec)
370
{
371
add_shorthand_trbl(match, value, spec,
372
"padding-top", "padding-right", "padding-bottom", "padding-left");
373
}
374
375
static void
376
add_shorthand_border_width(fz_css_match *match, fz_css_value *value, int spec)
377
{
378
add_shorthand_trbl(match, value, spec,
379
"border-width-top", "border-width-right", "border-width-bottom", "border-width-left");
380
}
381
382
static void
383
add_shorthand_border(fz_css_match *match, fz_css_value *value, int spec)
384
{
385
while (value)
386
{
387
if (value->type == CSS_COLOR)
388
{
389
add_property(match, "border-color", value, spec);
390
}
391
else if (value->type == CSS_KEYWORD)
392
{
393
if (keyword_in_list(value->data, border_width_kw, nelem(border_width_kw)))
394
{
395
add_property(match, "border-width-top", value, spec);
396
add_property(match, "border-width-right", value, spec);
397
add_property(match, "border-width-bottom", value, spec);
398
add_property(match, "border-width-left", value, spec);
399
}
400
else if (keyword_in_list(value->data, border_style_kw, nelem(border_style_kw)))
401
{
402
add_property(match, "border-style", value, spec);
403
}
404
else if (keyword_in_list(value->data, color_kw, nelem(color_kw)))
405
{
406
add_property(match, "border-color", value, spec);
407
}
408
}
409
else
410
{
411
add_property(match, "border-width-top", value, spec);
412
add_property(match, "border-width-right", value, spec);
413
add_property(match, "border-width-bottom", value, spec);
414
add_property(match, "border-width-left", value, spec);
415
}
416
value = value->next;
417
}
418
}
419
420
static void
421
add_property(fz_css_match *match, const char *name, fz_css_value *value, int spec)
422
{
423
int i;
424
425
if (!strcmp(name, "margin"))
426
{
427
add_shorthand_margin(match, value, spec);
428
return;
429
}
430
if (!strcmp(name, "padding"))
431
{
432
add_shorthand_padding(match, value, spec);
433
return;
434
}
435
if (!strcmp(name, "border-width"))
436
{
437
add_shorthand_border_width(match, value, spec);
438
return;
439
}
440
if (!strcmp(name, "border"))
441
{
442
add_shorthand_border(match, value, spec);
443
return;
444
}
445
446
/* shorthand expansions: */
447
/* TODO: border-color */
448
/* TODO: border-style */
449
/* TODO: font */
450
/* TODO: list-style */
451
/* TODO: background */
452
453
for (i = 0; i < match->count; ++i)
454
{
455
if (!strcmp(match->prop[i].name, name))
456
{
457
if (match->prop[i].spec <= spec)
458
{
459
match->prop[i].value = value;
460
match->prop[i].spec = spec;
461
}
462
return;
463
}
464
}
465
466
if (match->count + 1 >= nelem(match->prop))
467
{
468
// fz_warn(ctx, "too many css properties");
469
return;
470
}
471
472
match->prop[match->count].name = name;
473
match->prop[match->count].value = value;
474
match->prop[match->count].spec = spec;
475
++match->count;
476
}
477
478
void
479
fz_match_css(fz_context *ctx, fz_css_match *match, fz_css_rule *css, fz_xml *node)
480
{
481
fz_css_rule *rule;
482
fz_css_selector *sel;
483
fz_css_property *prop, *head, *tail;
484
const char *s;
485
486
for (rule = css; rule; rule = rule->next)
487
{
488
sel = rule->selector;
489
while (sel)
490
{
491
if (match_selector(sel, node))
492
{
493
for (prop = rule->declaration; prop; prop = prop->next)
494
add_property(match, prop->name, prop->value, selector_specificity(sel));
495
break;
496
}
497
sel = sel->next;
498
}
499
}
500
501
s = fz_xml_att(node, "style");
502
if (s)
503
{
504
head = tail = prop = fz_parse_css_properties(ctx, s);
505
while (prop)
506
{
507
add_property(match, prop->name, prop->value, INLINE_SPECIFICITY);
508
tail = prop;
509
prop = prop->next;
510
}
511
if (tail)
512
tail->next = css->garbage;
513
css->garbage = head;
514
}
515
}
516
517
static fz_css_value *
518
value_from_raw_property(fz_css_match *match, const char *name)
519
{
520
int i;
521
for (i = 0; i < match->count; ++i)
522
if (!strcmp(match->prop[i].name, name))
523
return match->prop[i].value;
524
return NULL;
525
}
526
527
static fz_css_value *
528
value_from_property(fz_css_match *match, const char *name)
529
{
530
fz_css_value *value;
531
532
value = value_from_raw_property(match, name);
533
if (match->up)
534
{
535
if (value && !strcmp(value->data, "inherit"))
536
return value_from_property(match->up, name);
537
if (!value && keyword_in_list(name, inherit_list, nelem(inherit_list)))
538
return value_from_property(match->up, name);
539
}
540
return value;
541
}
542
543
static const char *
544
string_from_property(fz_css_match *match, const char *name, const char *initial)
545
{
546
fz_css_value *value;
547
value = value_from_property(match, name);
548
if (!value)
549
return initial;
550
return value->data;
551
}
552
553
static fz_css_number
554
make_number(float v, int u)
555
{
556
fz_css_number n;
557
n.value = v;
558
n.unit = u;
559
return n;
560
}
561
562
static fz_css_number
563
number_from_value(fz_css_value *value, float initial, int initial_unit)
564
{
565
char *p;
566
567
if (!value)
568
return make_number(initial, initial_unit);
569
570
if (value->type == CSS_PERCENT)
571
return make_number((float)fz_strtod(value->data, NULL), N_PERCENT);
572
573
if (value->type == CSS_NUMBER)
574
return make_number((float)fz_strtod(value->data, NULL), N_NUMBER);
575
576
if (value->type == CSS_LENGTH)
577
{
578
float x = (float)fz_strtod(value->data, &p);
579
580
if (p[0] == 'e' && p[1] == 'm')
581
return make_number(x, N_SCALE);
582
if (p[0] == 'e' && p[1] == 'x')
583
return make_number(x / 2, N_SCALE);
584
585
if (p[0] == 'i' && p[1] == 'n')
586
return make_number(x * 72, N_NUMBER);
587
if (p[0] == 'c' && p[1] == 'm')
588
return make_number(x * 7200 / 254, N_NUMBER);
589
if (p[0] == 'm' && p[1] == 'm')
590
return make_number(x * 720 / 254, N_NUMBER);
591
if (p[0] == 'p' && p[1] == 'c')
592
return make_number(x * 12, N_NUMBER);
593
594
if (p[0] == 'p' && p[1] == 't')
595
return make_number(x, N_NUMBER);
596
if (p[0] == 'p' && p[1] == 'x')
597
return make_number(x, N_NUMBER);
598
599
return make_number(x, N_NUMBER);
600
}
601
602
return make_number(initial, initial_unit);
603
}
604
605
static fz_css_number
606
number_from_property(fz_css_match *match, const char *property, float initial, int initial_unit)
607
{
608
return number_from_value(value_from_property(match, property), initial, initial_unit);
609
}
610
611
static fz_css_number
612
border_width_from_property(fz_css_match *match, const char *property)
613
{
614
fz_css_value *value = value_from_property(match, property);
615
if (value)
616
{
617
if (!strcmp(value->data, "thin"))
618
return make_number(1, N_NUMBER);
619
if (!strcmp(value->data, "medium"))
620
return make_number(2, N_NUMBER);
621
if (!strcmp(value->data, "thick"))
622
return make_number(4, N_NUMBER);
623
return number_from_value(value, 0, N_NUMBER);
624
}
625
return make_number(2, N_NUMBER); /* initial: 'medium' */
626
}
627
628
float
629
fz_from_css_number(fz_css_number number, float em, float width)
630
{
631
switch (number.unit) {
632
default:
633
case N_NUMBER: return number.value;
634
case N_SCALE: return number.value * em;
635
case N_PERCENT: return number.value * 0.01 * width;
636
}
637
}
638
639
float
640
fz_from_css_number_scale(fz_css_number number, float scale, float em, float width)
641
{
642
switch (number.unit) {
643
default:
644
case N_NUMBER: return number.value * scale;
645
case N_SCALE: return number.value * em;
646
case N_PERCENT: return number.value * 0.01 * width;
647
}
648
}
649
650
static fz_css_color
651
make_color(int r, int g, int b, int a)
652
{
653
fz_css_color c;
654
c.r = r;
655
c.g = g;
656
c.b = b;
657
c.a = a;
658
return c;
659
}
660
661
static int tohex(int c)
662
{
663
if (c - '0' < 10)
664
return c - '0';
665
return (c | 32) - 'a' + 10;
666
}
667
668
static fz_css_color
669
color_from_value(fz_css_value *value, fz_css_color initial)
670
{
671
if (!value)
672
return initial;
673
if (value->type == CSS_COLOR)
674
{
675
int r = tohex(value->data[0]) * 16 + tohex(value->data[1]);
676
int g = tohex(value->data[2]) * 16 + tohex(value->data[3]);
677
int b = tohex(value->data[4]) * 16 + tohex(value->data[5]);
678
return make_color(r, g, b, 255);
679
}
680
if (value->type == CSS_KEYWORD)
681
{
682
if (!strcmp(value->data, "transparent"))
683
return make_color(0, 0, 0, 0);
684
if (!strcmp(value->data, "maroon"))
685
return make_color(0x80, 0x00, 0x00, 255);
686
if (!strcmp(value->data, "red"))
687
return make_color(0xFF, 0x00, 0x00, 255);
688
if (!strcmp(value->data, "orange"))
689
return make_color(0xFF, 0xA5, 0x00, 255);
690
if (!strcmp(value->data, "yellow"))
691
return make_color(0xFF, 0xFF, 0x00, 255);
692
if (!strcmp(value->data, "olive"))
693
return make_color(0x80, 0x80, 0x00, 255);
694
if (!strcmp(value->data, "purple"))
695
return make_color(0x80, 0x00, 0x80, 255);
696
if (!strcmp(value->data, "fuchsia"))
697
return make_color(0xFF, 0x00, 0xFF, 255);
698
if (!strcmp(value->data, "white"))
699
return make_color(0xFF, 0xFF, 0xFF, 255);
700
if (!strcmp(value->data, "lime"))
701
return make_color(0x00, 0xFF, 0x00, 255);
702
if (!strcmp(value->data, "green"))
703
return make_color(0x00, 0x80, 0x00, 255);
704
if (!strcmp(value->data, "navy"))
705
return make_color(0x00, 0x00, 0x80, 255);
706
if (!strcmp(value->data, "blue"))
707
return make_color(0x00, 0x00, 0xFF, 255);
708
if (!strcmp(value->data, "aqua"))
709
return make_color(0x00, 0xFF, 0xFF, 255);
710
if (!strcmp(value->data, "teal"))
711
return make_color(0x00, 0x80, 0x80, 255);
712
if (!strcmp(value->data, "black"))
713
return make_color(0x00, 0x00, 0x00, 255);
714
if (!strcmp(value->data, "silver"))
715
return make_color(0xC0, 0xC0, 0xC0, 255);
716
if (!strcmp(value->data, "gray"))
717
return make_color(0x80, 0x80, 0x80, 255);
718
return make_color(0, 0, 0, 255);
719
}
720
return initial;
721
}
722
723
static fz_css_color
724
color_from_property(fz_css_match *match, const char *property, fz_css_color initial)
725
{
726
return color_from_value(value_from_property(match, property), initial);
727
}
728
729
int
730
fz_get_css_match_display(fz_css_match *match)
731
{
732
fz_css_value *value = value_from_property(match, "display");
733
if (value)
734
{
735
if (!strcmp(value->data, "none"))
736
return DIS_NONE;
737
if (!strcmp(value->data, "inline"))
738
return DIS_INLINE;
739
if (!strcmp(value->data, "block"))
740
return DIS_BLOCK;
741
if (!strcmp(value->data, "list-item"))
742
return DIS_LIST_ITEM;
743
}
744
return DIS_INLINE;
745
}
746
747
static int
748
white_space_from_property(fz_css_match *match)
749
{
750
fz_css_value *value = value_from_property(match, "white-space");
751
if (value)
752
{
753
if (!strcmp(value->data, "normal")) return WS_NORMAL;
754
if (!strcmp(value->data, "pre")) return WS_PRE;
755
if (!strcmp(value->data, "nowrap")) return WS_NOWRAP;
756
if (!strcmp(value->data, "pre-wrap")) return WS_PRE_WRAP;
757
if (!strcmp(value->data, "pre-line")) return WS_PRE_LINE;
758
}
759
return WS_NORMAL;
760
}
761
762
void
763
fz_default_css_style(fz_context *ctx, fz_css_style *style)
764
{
765
memset(style, 0, sizeof *style);
766
style->text_align = TA_LEFT;
767
style->vertical_align = VA_BASELINE;
768
style->white_space = WS_NORMAL;
769
style->font_size = make_number(1, N_SCALE);
770
}
771
772
void
773
fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style, fz_css_match *match)
774
{
775
fz_css_value *value;
776
777
fz_css_color black = { 0, 0, 0, 255 };
778
fz_css_color transparent = { 0, 0, 0, 0 };
779
780
fz_default_css_style(ctx, style);
781
782
style->white_space = white_space_from_property(match);
783
784
value = value_from_property(match, "text-align");
785
if (value)
786
{
787
if (!strcmp(value->data, "left"))
788
style->text_align = TA_LEFT;
789
if (!strcmp(value->data, "right"))
790
style->text_align = TA_RIGHT;
791
if (!strcmp(value->data, "center"))
792
style->text_align = TA_CENTER;
793
if (!strcmp(value->data, "justify"))
794
style->text_align = TA_JUSTIFY;
795
}
796
797
value = value_from_property(match, "vertical-align");
798
if (value)
799
{
800
if (!strcmp(value->data, "baseline"))
801
style->vertical_align = VA_BASELINE;
802
if (!strcmp(value->data, "sub"))
803
style->vertical_align = VA_SUB;
804
if (!strcmp(value->data, "super"))
805
style->vertical_align = VA_SUPER;
806
if (!strcmp(value->data, "top"))
807
style->vertical_align = VA_TOP;
808
if (!strcmp(value->data, "bottom"))
809
style->vertical_align = VA_BOTTOM;
810
}
811
812
value = value_from_property(match, "font-size");
813
if (value)
814
{
815
if (!strcmp(value->data, "xx-large")) style->font_size = make_number(1.73f, N_SCALE);
816
else if (!strcmp(value->data, "x-large")) style->font_size = make_number(1.44f, N_SCALE);
817
else if (!strcmp(value->data, "large")) style->font_size = make_number(1.2f, N_SCALE);
818
else if (!strcmp(value->data, "medium")) style->font_size = make_number(1.0f, N_SCALE);
819
else if (!strcmp(value->data, "small")) style->font_size = make_number(0.83f, N_SCALE);
820
else if (!strcmp(value->data, "x-small")) style->font_size = make_number(0.69f, N_SCALE);
821
else if (!strcmp(value->data, "xx-small")) style->font_size = make_number(0.69f, N_SCALE);
822
else if (!strcmp(value->data, "larger")) style->font_size = make_number(1.2f, N_SCALE);
823
else if (!strcmp(value->data, "smaller")) style->font_size = make_number(1/1.2f, N_SCALE);
824
else style->font_size = number_from_value(value, 12, N_NUMBER);
825
}
826
else
827
{
828
style->font_size = make_number(1, N_SCALE);
829
}
830
831
value = value_from_property(match, "border-style");
832
if (value)
833
{
834
if (!strcmp(value->data, "none"))
835
style->border_style = BS_NONE;
836
if (!strcmp(value->data, "hidden"))
837
style->border_style = BS_NONE;
838
if (!strcmp(value->data, "solid"))
839
style->border_style = BS_SOLID;
840
}
841
842
style->line_height = number_from_property(match, "line-height", 1.2f, N_SCALE);
843
844
style->text_indent = number_from_property(match, "text-indent", 0, N_NUMBER);
845
846
style->margin[0] = number_from_property(match, "margin-top", 0, N_NUMBER);
847
style->margin[1] = number_from_property(match, "margin-right", 0, N_NUMBER);
848
style->margin[2] = number_from_property(match, "margin-bottom", 0, N_NUMBER);
849
style->margin[3] = number_from_property(match, "margin-left", 0, N_NUMBER);
850
851
style->padding[0] = number_from_property(match, "padding-top", 0, N_NUMBER);
852
style->padding[1] = number_from_property(match, "padding-right", 0, N_NUMBER);
853
style->padding[2] = number_from_property(match, "padding-bottom", 0, N_NUMBER);
854
style->padding[3] = number_from_property(match, "padding-left", 0, N_NUMBER);
855
856
style->border_width[0] = border_width_from_property(match, "border-width-top");
857
style->border_width[1] = border_width_from_property(match, "border-width-right");
858
style->border_width[2] = border_width_from_property(match, "border-width-bottom");
859
style->border_width[3] = border_width_from_property(match, "border-width-left");
860
861
style->color = color_from_property(match, "color", black);
862
style->background_color = color_from_property(match, "background-color", transparent);
863
style->border_color = color_from_property(match, "border-color", style->color);
864
865
{
866
const char *font_family = string_from_property(match, "font-family", "serif");
867
const char *font_variant = string_from_property(match, "font-variant", "normal");
868
const char *font_style = string_from_property(match, "font-style", "normal");
869
const char *font_weight = string_from_property(match, "font-weight", "normal");
870
style->font = fz_load_html_font(ctx, set, font_family, font_variant, font_style, font_weight);
871
}
872
}
873
874
/*
875
* Pretty printing
876
*/
877
878
void
879
print_value(fz_css_value *val)
880
{
881
printf("%s", val->data);
882
if (val->args)
883
{
884
printf("(");
885
print_value(val->args);
886
printf(")");
887
}
888
if (val->next)
889
{
890
printf(" ");
891
print_value(val->next);
892
}
893
}
894
895
void
896
print_property(fz_css_property *prop)
897
{
898
printf("\t%s: ", prop->name);
899
print_value(prop->value);
900
printf(" !%d;\n", prop->spec);
901
}
902
903
void
904
print_condition(fz_css_condition *cond)
905
{
906
if (cond->type == '=')
907
printf("[%s=%s]", cond->key, cond->val);
908
else if (cond->type == '[')
909
printf("[%s]", cond->key);
910
else
911
printf("%c%s", cond->type, cond->val);
912
if (cond->next)
913
print_condition(cond->next);
914
}
915
916
void
917
print_selector(fz_css_selector *sel)
918
{
919
if (sel->combine)
920
{
921
putchar('(');
922
print_selector(sel->left);
923
if (sel->combine == ' ')
924
printf(" ");
925
else
926
printf(" %c ", sel->combine);
927
print_selector(sel->right);
928
putchar(')');
929
}
930
else if (sel->name)
931
printf("%s", sel->name);
932
else
933
printf("*");
934
if (sel->cond)
935
{
936
print_condition(sel->cond);
937
}
938
}
939
940
void
941
print_rule(fz_css_rule *rule)
942
{
943
fz_css_selector *sel;
944
fz_css_property *prop;
945
946
for (sel = rule->selector; sel; sel = sel->next)
947
{
948
print_selector(sel);
949
printf(" !%d", selector_specificity(sel));
950
if (sel->next)
951
printf(", ");
952
}
953
954
printf("\n{\n");
955
for (prop = rule->declaration; prop; prop = prop->next)
956
{
957
print_property(prop);
958
}
959
printf("}\n");
960
}
961
962
void
963
print_rules(fz_css_rule *rule)
964
{
965
while (rule)
966
{
967
print_rule(rule);
968
rule = rule->next;
969
}
970
}
971
972
void
973
print_style(fz_css_style *style)
974
{
975
printf("style {\n");
976
printf("\tfont-size = %g%c;\n", style->font_size.value, style->font_size.unit);
977
printf("\tfont = %s;\n", style->font->name);
978
printf("\tline-height = %g%c;\n", style->line_height.value, style->line_height.unit);
979
printf("\ttext-indent = %g%c;\n", style->text_indent.value, style->text_indent.unit);
980
printf("\ttext-align = %d;\n", style->text_align);
981
printf("\tvertical-align = %d;\n", style->vertical_align);
982
printf("\tmargin = %g%c %g%c %g%c %g%c;\n",
983
style->margin[0].value, style->margin[0].unit,
984
style->margin[1].value, style->margin[1].unit,
985
style->margin[2].value, style->margin[2].unit,
986
style->margin[3].value, style->margin[3].unit);
987
printf("\tpadding = %g%c %g%c %g%c %g%c;\n",
988
style->padding[0].value, style->padding[0].unit,
989
style->padding[1].value, style->padding[1].unit,
990
style->padding[2].value, style->padding[2].unit,
991
style->padding[3].value, style->padding[3].unit);
992
printf("}\n");
993
}
994
995