Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "mupdf/html.h"
2
3
enum { T, R, B, L };
4
5
static const char *default_css =
6
"html,address,blockquote,body,dd,div,dl,dt,h1,h2,h3,h4,h5,h6,ol,p,ul,center,hr,pre{display:block}"
7
"span{display:inline}"
8
"li{display:list-item}"
9
"head{display:none}"
10
"body{margin:1em}"
11
"h1{font-size:2em;margin:.67em 0}"
12
"h2{font-size:1.5em;margin:.75em 0}"
13
"h3{font-size:1.17em;margin:.83em 0}"
14
"h4,p,blockquote,ul,ol,dl,dir,menu{margin:1.12em 0}"
15
"h5{font-size:.83em;margin:1.5em 0}"
16
"h6{font-size:.67em;margin:1.67em 0}"
17
"h1,h2,h3,h4,h5,h6,b,strong{font-weight:bold}"
18
"blockquote{margin-left:40px;margin-right:40px}"
19
"i,cite,em,var,address{font-style:italic}"
20
"pre,tt,code,kbd,samp{font-family:monospace}"
21
"pre{white-space:pre}"
22
"big{font-size:1.17em}"
23
"small,sub,sup{font-size:.83em}"
24
"sub{vertical-align:sub}"
25
"sup{vertical-align:super}"
26
"s,strike,del{text-decoration:line-through}"
27
"hr{border-width:thin;border-color:black;border-style:solid;margin:.5em 0}"
28
"ol,ul,dir,menu,dd{margin-left:40px}"
29
"ol{list-style-type:decimal}"
30
"ol ul,ul ol,ul ul,ol ol{margin-top:0;margin-bottom:0}"
31
"u,ins{text-decoration:underline}"
32
"center{text-align:center}"
33
"svg{display:none}"
34
"a{color:blue}"
35
;
36
37
static int iswhite(int c)
38
{
39
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
40
}
41
42
static void fz_drop_html_flow(fz_context *ctx, fz_html_flow *flow)
43
{
44
while (flow)
45
{
46
fz_html_flow *next = flow->next;
47
if (flow->type == FLOW_WORD)
48
fz_free(ctx, flow->text);
49
if (flow->type == FLOW_IMAGE)
50
fz_drop_image(ctx, flow->image);
51
fz_free(ctx, flow);
52
flow = next;
53
}
54
}
55
56
static fz_html_flow *add_flow(fz_context *ctx, fz_html *top, fz_css_style *style, int type)
57
{
58
fz_html_flow *flow = fz_malloc_struct(ctx, fz_html_flow);
59
flow->type = type;
60
flow->style = style;
61
*top->flow_tail = flow;
62
top->flow_tail = &flow->next;
63
return flow;
64
}
65
66
static void add_flow_space(fz_context *ctx, fz_html *top, fz_css_style *style)
67
{
68
fz_html_flow *flow;
69
70
/* delete space at the beginning of the line */
71
if (!top->flow_head)
72
return;
73
74
flow = add_flow(ctx, top, style, FLOW_GLUE);
75
flow->text = " ";
76
flow->broken_text = "";
77
}
78
79
static void add_flow_word(fz_context *ctx, fz_html *top, fz_css_style *style, const char *a, const char *b)
80
{
81
fz_html_flow *flow = add_flow(ctx, top, style, FLOW_WORD);
82
flow->text = fz_malloc(ctx, b - a + 1);
83
memcpy(flow->text, a, b - a);
84
flow->text[b - a] = 0;
85
}
86
87
static void add_flow_image(fz_context *ctx, fz_html *top, fz_css_style *style, fz_image *img)
88
{
89
fz_html_flow *flow = add_flow(ctx, top, style, FLOW_IMAGE);
90
flow->image = fz_keep_image(ctx, img);
91
}
92
93
static void generate_text(fz_context *ctx, fz_html *box, const char *text)
94
{
95
fz_html *flow = box;
96
while (flow->type != BOX_FLOW)
97
flow = flow->up;
98
99
while (*text)
100
{
101
if (iswhite(*text))
102
{
103
++text;
104
while (iswhite(*text))
105
++text;
106
add_flow_space(ctx, flow, &box->style);
107
}
108
if (*text)
109
{
110
const char *mark = text++;
111
while (*text && !iswhite(*text))
112
++text;
113
add_flow_word(ctx, flow, &box->style, mark, text);
114
}
115
}
116
}
117
118
static void generate_image(fz_context *ctx, fz_archive *zip, const char *base_uri, fz_html *box, const char *src)
119
{
120
fz_image *img;
121
fz_buffer *buf;
122
char path[2048];
123
124
fz_html *flow = box;
125
while (flow->type != BOX_FLOW)
126
flow = flow->up;
127
128
fz_strlcpy(path, base_uri, sizeof path);
129
fz_strlcat(path, "/", sizeof path);
130
fz_strlcat(path, src, sizeof path);
131
fz_cleanname(path);
132
133
fz_try(ctx)
134
{
135
buf = fz_read_archive_entry(ctx, zip, path);
136
img = fz_new_image_from_buffer(ctx, buf);
137
fz_drop_buffer(ctx, buf);
138
139
add_flow_image(ctx, flow, &box->style, img);
140
}
141
fz_catch(ctx)
142
{
143
const char *alt = "[image]";
144
fz_warn(ctx, "html: cannot add image src='%s'", src);
145
add_flow_word(ctx, flow, &box->style, alt, alt + 7);
146
}
147
}
148
149
static void init_box(fz_context *ctx, fz_html *box)
150
{
151
box->type = BOX_BLOCK;
152
box->x = box->y = 0;
153
box->w = box->h = 0;
154
155
box->up = NULL;
156
box->last = NULL;
157
box->down = NULL;
158
box->next = NULL;
159
160
box->flow_head = NULL;
161
box->flow_tail = &box->flow_head;
162
163
fz_default_css_style(ctx, &box->style);
164
}
165
166
void fz_drop_html(fz_context *ctx, fz_html *box)
167
{
168
while (box)
169
{
170
fz_html *next = box->next;
171
fz_drop_html_flow(ctx, box->flow_head);
172
fz_drop_html(ctx, box->down);
173
fz_free(ctx, box);
174
box = next;
175
}
176
}
177
178
static fz_html *new_box(fz_context *ctx)
179
{
180
fz_html *box = fz_malloc_struct(ctx, fz_html);
181
init_box(ctx, box);
182
return box;
183
}
184
185
static void insert_box(fz_context *ctx, fz_html *box, int type, fz_html *top)
186
{
187
box->type = type;
188
189
box->up = top;
190
191
if (top)
192
{
193
if (!top->last)
194
{
195
top->down = top->last = box;
196
}
197
else
198
{
199
top->last->next = box;
200
top->last = box;
201
}
202
}
203
}
204
205
static fz_html *insert_block_box(fz_context *ctx, fz_html *box, fz_html *top)
206
{
207
if (top->type == BOX_BLOCK)
208
{
209
insert_box(ctx, box, BOX_BLOCK, top);
210
}
211
else if (top->type == BOX_FLOW)
212
{
213
while (top->type != BOX_BLOCK)
214
top = top->up;
215
insert_box(ctx, box, BOX_BLOCK, top);
216
}
217
else if (top->type == BOX_INLINE)
218
{
219
while (top->type != BOX_BLOCK)
220
top = top->up;
221
insert_box(ctx, box, BOX_BLOCK, top);
222
}
223
return top;
224
}
225
226
static fz_html *insert_break_box(fz_context *ctx, fz_html *box, fz_html *top)
227
{
228
if (top->type == BOX_BLOCK)
229
{
230
insert_box(ctx, box, BOX_BREAK, top);
231
}
232
else if (top->type == BOX_FLOW)
233
{
234
while (top->type != BOX_BLOCK)
235
top = top->up;
236
insert_box(ctx, box, BOX_BREAK, top);
237
}
238
else if (top->type == BOX_INLINE)
239
{
240
while (top->type != BOX_BLOCK)
241
top = top->up;
242
insert_box(ctx, box, BOX_BREAK, top);
243
}
244
return top;
245
}
246
247
static void insert_inline_box(fz_context *ctx, fz_html *box, fz_html *top)
248
{
249
if (top->type == BOX_BLOCK)
250
{
251
if (top->last && top->last->type == BOX_FLOW)
252
{
253
insert_box(ctx, box, BOX_INLINE, top->last);
254
}
255
else
256
{
257
fz_html *flow = new_box(ctx);
258
flow->is_first_flow = !top->last;
259
insert_box(ctx, flow, BOX_FLOW, top);
260
insert_box(ctx, box, BOX_INLINE, flow);
261
}
262
}
263
else if (top->type == BOX_FLOW)
264
{
265
insert_box(ctx, box, BOX_INLINE, top);
266
}
267
else if (top->type == BOX_INLINE)
268
{
269
insert_box(ctx, box, BOX_INLINE, top);
270
}
271
}
272
273
static void generate_boxes(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri,
274
fz_xml *node, fz_html *top, fz_css_rule *rule, fz_css_match *up_match)
275
{
276
fz_css_match match;
277
fz_html *box;
278
const char *tag;
279
int display;
280
281
while (node)
282
{
283
match.up = up_match;
284
match.count = 0;
285
286
tag = fz_xml_tag(node);
287
if (tag)
288
{
289
fz_match_css(ctx, &match, rule, node);
290
291
display = fz_get_css_match_display(&match);
292
293
if (!strcmp(tag, "br"))
294
{
295
box = new_box(ctx);
296
fz_apply_css_style(ctx, set, &box->style, &match);
297
top = insert_break_box(ctx, box, top);
298
}
299
300
else if (!strcmp(tag, "img"))
301
{
302
const char *src = fz_xml_att(node, "src");
303
if (src)
304
{
305
box = new_box(ctx);
306
fz_apply_css_style(ctx, set, &box->style, &match);
307
insert_inline_box(ctx, box, top);
308
generate_image(ctx, zip, base_uri, box, src);
309
}
310
}
311
312
else if (display != DIS_NONE)
313
{
314
box = new_box(ctx);
315
fz_apply_css_style(ctx, set, &box->style, &match);
316
317
if (display == DIS_BLOCK)
318
{
319
top = insert_block_box(ctx, box, top);
320
}
321
else if (display == DIS_LIST_ITEM)
322
{
323
top = insert_block_box(ctx, box, top);
324
}
325
else if (display == DIS_INLINE)
326
{
327
insert_inline_box(ctx, box, top);
328
}
329
else
330
{
331
fz_warn(ctx, "unknown box display type");
332
insert_box(ctx, box, BOX_BLOCK, top);
333
}
334
335
if (fz_xml_down(node))
336
generate_boxes(ctx, set, zip, base_uri, fz_xml_down(node), box, rule, &match);
337
338
// TODO: remove empty flow boxes
339
}
340
}
341
else
342
{
343
if (top->type != BOX_INLINE)
344
{
345
box = new_box(ctx);
346
insert_inline_box(ctx, box, top);
347
box->style = top->style;
348
generate_text(ctx, box, fz_xml_text(node));
349
}
350
else
351
{
352
generate_text(ctx, top, fz_xml_text(node));
353
}
354
}
355
356
node = fz_xml_next(node);
357
}
358
}
359
360
static void measure_image(fz_context *ctx, fz_html_flow *node, float w, float h)
361
{
362
float xs = 1, ys = 1, s = 1;
363
node->x = 0;
364
node->y = 0;
365
if (node->image->w > w)
366
xs = w / node->image->w;
367
if (node->image->h > h)
368
ys = h / node->image->h;
369
s = fz_min(xs, ys);
370
node->w = node->image->w * s;
371
node->h = node->image->h * s;
372
}
373
374
static void measure_word(fz_context *ctx, fz_html_flow *node, float em)
375
{
376
const char *s;
377
int c, g;
378
float w;
379
380
em = fz_from_css_number(node->style->font_size, em, em);
381
node->x = 0;
382
node->y = 0;
383
node->h = fz_from_css_number_scale(node->style->line_height, em, em, em);
384
385
w = 0;
386
s = node->text;
387
while (*s)
388
{
389
s += fz_chartorune(&c, s);
390
g = fz_encode_character(ctx, node->style->font, c);
391
w += fz_advance_glyph(ctx, node->style->font, g) * em;
392
}
393
node->w = w;
394
node->em = em;
395
}
396
397
static float measure_line(fz_html_flow *node, fz_html_flow *end, float *baseline)
398
{
399
float max_a = 0, max_d = 0, h = 0;
400
while (node != end)
401
{
402
if (node->type == FLOW_IMAGE)
403
{
404
if (node->h > max_a)
405
max_a = node->h;
406
}
407
else
408
{
409
float a = node->em * 0.8;
410
float d = node->em * 0.2;
411
if (a > max_a) max_a = a;
412
if (d > max_d) max_d = d;
413
}
414
if (node->h > h) h = node->h;
415
if (max_a + max_d > h) h = max_a + max_d;
416
node = node->next;
417
}
418
*baseline = max_a + (h - max_a - max_d) / 2;
419
return h;
420
}
421
422
static void layout_line(fz_context *ctx, float indent, float page_w, float line_w, int align, fz_html_flow *node, fz_html_flow *end, fz_html *box, float baseline)
423
{
424
float x = box->x + indent;
425
float y = box->y + box->h;
426
float slop = page_w - line_w;
427
float justify = 0;
428
float va;
429
int n = 0;
430
431
if (align == TA_JUSTIFY)
432
{
433
fz_html_flow *it;
434
for (it = node; it != end; it = it->next)
435
if (it->type == FLOW_GLUE)
436
++n;
437
justify = slop / n;
438
}
439
else if (align == TA_RIGHT)
440
x += slop;
441
else if (align == TA_CENTER)
442
x += slop / 2;
443
444
while (node != end)
445
{
446
switch (node->style->vertical_align)
447
{
448
default:
449
case VA_BASELINE:
450
va = 0;
451
break;
452
case VA_SUB:
453
va = node->em * 0.2f;
454
break;
455
case VA_SUPER:
456
va = node->em * -0.3f;
457
break;
458
}
459
node->x = x;
460
if (node->type == FLOW_IMAGE)
461
node->y = y + baseline - node->h;
462
else
463
node->y = y + baseline + va;
464
x += node->w;
465
if (node->type == FLOW_GLUE)
466
x += justify;
467
node = node->next;
468
}
469
}
470
471
static fz_html_flow *find_next_glue(fz_html_flow *node, float *w)
472
{
473
while (node && node->type == FLOW_GLUE)
474
{
475
*w += node->w;
476
node = node->next;
477
}
478
while (node && node->type != FLOW_GLUE)
479
{
480
*w += node->w;
481
node = node->next;
482
}
483
return node;
484
}
485
486
static fz_html_flow *find_next_word(fz_html_flow *node, float *w)
487
{
488
while (node && node->type == FLOW_GLUE)
489
{
490
*w += node->w;
491
node = node->next;
492
}
493
return node;
494
}
495
496
static void find_accumulated_margins(fz_context *ctx, fz_html *box, float *w, float *h)
497
{
498
while (box)
499
{
500
/* TODO: take into account collapsed margins */
501
*h += box->margin[T] + box->padding[T] + box->border[T];
502
*h += box->margin[B] + box->padding[B] + box->border[B];
503
*w += box->margin[L] + box->padding[L] + box->border[L];
504
*w += box->margin[R] + box->padding[R] + box->border[R];
505
box = box->up;
506
}
507
}
508
509
static void layout_flow(fz_context *ctx, fz_html *box, fz_html *top, float em, float page_h)
510
{
511
fz_html_flow *node, *line_start, *word_start, *word_end, *line_end;
512
float glue_w;
513
float word_w;
514
float line_w;
515
float indent;
516
float avail, line_h;
517
float baseline;
518
int align;
519
520
em = fz_from_css_number(box->style.font_size, em, em);
521
indent = box->is_first_flow ? fz_from_css_number(top->style.text_indent, em, top->w) : 0;
522
align = top->style.text_align;
523
524
box->x = top->x;
525
box->y = top->y + top->h;
526
box->w = top->w;
527
box->h = 0;
528
529
if (!box->flow_head)
530
return;
531
532
for (node = box->flow_head; node; node = node->next)
533
{
534
if (node->type == FLOW_IMAGE)
535
{
536
float w = 0, h = 0;
537
find_accumulated_margins(ctx, box, &w, &h);
538
measure_image(ctx, node, top->w - w, page_h - h);
539
}
540
else
541
{
542
measure_word(ctx, node, em);
543
}
544
}
545
546
line_start = find_next_word(box->flow_head, &glue_w);
547
line_end = NULL;
548
549
line_w = indent;
550
word_w = 0;
551
word_start = line_start;
552
while (word_start)
553
{
554
word_end = find_next_glue(word_start, &word_w);
555
if (line_w + word_w <= top->w)
556
{
557
line_w += word_w;
558
glue_w = 0;
559
line_end = word_end;
560
word_start = find_next_word(word_end, &glue_w);
561
word_w = glue_w;
562
}
563
else
564
{
565
avail = page_h - fmodf(box->y + box->h, page_h);
566
line_h = measure_line(line_start, line_end, &baseline);
567
if (line_h > avail)
568
box->h += avail;
569
layout_line(ctx, indent, top->w, line_w, align, line_start, line_end, box, baseline);
570
box->h += line_h;
571
word_start = find_next_word(line_end, &glue_w);
572
line_start = word_start;
573
line_end = NULL;
574
indent = 0;
575
line_w = 0;
576
word_w = 0;
577
}
578
}
579
580
/* don't justify the last line of a paragraph */
581
if (align == TA_JUSTIFY)
582
align = TA_LEFT;
583
584
if (line_start)
585
{
586
avail = page_h - fmodf(box->y + box->h, page_h);
587
line_h = measure_line(line_start, line_end, &baseline);
588
if (line_h > avail)
589
box->h += avail;
590
layout_line(ctx, indent, top->w, line_w, align, line_start, line_end, box, baseline);
591
box->h += line_h;
592
}
593
}
594
595
static void layout_block(fz_context *ctx, fz_html *box, fz_html *top, float em, float top_collapse_margin, float page_h)
596
{
597
fz_html *child;
598
float box_collapse_margin;
599
int prev_br;
600
601
float *margin = box->margin;
602
float *border = box->border;
603
float *padding = box->padding;
604
605
em = fz_from_css_number(box->style.font_size, em, em);
606
607
margin[0] = fz_from_css_number(box->style.margin[0], em, top->w);
608
margin[1] = fz_from_css_number(box->style.margin[1], em, top->w);
609
margin[2] = fz_from_css_number(box->style.margin[2], em, top->w);
610
margin[3] = fz_from_css_number(box->style.margin[3], em, top->w);
611
612
padding[0] = fz_from_css_number(box->style.padding[0], em, top->w);
613
padding[1] = fz_from_css_number(box->style.padding[1], em, top->w);
614
padding[2] = fz_from_css_number(box->style.padding[2], em, top->w);
615
padding[3] = fz_from_css_number(box->style.padding[3], em, top->w);
616
617
if (box->style.border_style)
618
{
619
border[0] = fz_from_css_number(box->style.border_width[0], em, top->w);
620
border[1] = fz_from_css_number(box->style.border_width[1], em, top->w);
621
border[2] = fz_from_css_number(box->style.border_width[2], em, top->w);
622
border[3] = fz_from_css_number(box->style.border_width[3], em, top->w);
623
}
624
else
625
border[0] = border[1] = border[2] = border[3] = 0;
626
627
if (padding[T] == 0 && border[T] == 0)
628
box_collapse_margin = margin[T];
629
else
630
box_collapse_margin = 0;
631
632
if (margin[T] > top_collapse_margin)
633
margin[T] -= top_collapse_margin;
634
else
635
margin[T] = 0;
636
637
box->x = top->x + margin[L] + border[L] + padding[L];
638
box->y = top->y + top->h + margin[T] + border[T] + padding[T];
639
box->w = top->w - (margin[L] + margin[R] + border[L] + border[R] + padding[L] + padding[R]);
640
box->h = 0;
641
642
prev_br = 0;
643
for (child = box->down; child; child = child->next)
644
{
645
if (child->type == BOX_BLOCK)
646
{
647
layout_block(ctx, child, box, em, box_collapse_margin, page_h);
648
box->h += child->h +
649
child->padding[T] + child->padding[B] +
650
child->border[T] + child->border[B] +
651
child->margin[T] + child->margin[B];
652
box_collapse_margin = child->margin[B];
653
prev_br = 0;
654
}
655
else if (child->type == BOX_BREAK)
656
{
657
/* TODO: interaction with page breaks */
658
if (prev_br)
659
box->h += fz_from_css_number_scale(box->style.line_height, em, em, em);
660
prev_br = 1;
661
}
662
else if (child->type == BOX_FLOW)
663
{
664
layout_flow(ctx, child, box, em, page_h);
665
if (child->h > 0)
666
{
667
box->h += child->h;
668
box_collapse_margin = 0;
669
prev_br = 0;
670
}
671
}
672
}
673
674
if (padding[B] == 0 && border[B] == 0)
675
{
676
if (margin[B] > 0)
677
{
678
box->h -= box_collapse_margin;
679
if (margin[B] < box_collapse_margin)
680
margin[B] = box_collapse_margin;
681
}
682
}
683
}
684
685
static void draw_flow_box(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *ctm)
686
{
687
fz_html_flow *node;
688
fz_text *text;
689
fz_matrix trm;
690
const char *s;
691
float color[3];
692
float x, y;
693
int c, g;
694
695
for (node = box->flow_head; node; node = node->next)
696
{
697
if (node->type == FLOW_IMAGE)
698
{
699
if (node->y >= page_bot || node->y + node->h <= page_top)
700
continue;
701
}
702
else
703
{
704
if (node->y > page_bot || node->y < page_top)
705
continue;
706
}
707
708
if (node->type == FLOW_WORD)
709
{
710
fz_scale(&trm, node->em, -node->em);
711
text = fz_new_text(ctx, node->style->font, &trm, 0);
712
713
x = node->x;
714
y = node->y;
715
s = node->text;
716
while (*s)
717
{
718
s += fz_chartorune(&c, s);
719
g = fz_encode_character(ctx, node->style->font, c);
720
fz_add_text(ctx, text, g, c, x, y);
721
x += fz_advance_glyph(ctx, node->style->font, g) * node->em;
722
}
723
724
color[0] = node->style->color.r / 255.0f;
725
color[1] = node->style->color.g / 255.0f;
726
color[2] = node->style->color.b / 255.0f;
727
728
fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1);
729
730
fz_drop_text(ctx, text);
731
}
732
else if (node->type == FLOW_IMAGE)
733
{
734
fz_matrix local_ctm = *ctm;
735
fz_pre_translate(&local_ctm, node->x, node->y);
736
fz_pre_scale(&local_ctm, node->w, node->h);
737
fz_fill_image(ctx, dev, node->image, &local_ctm, 1);
738
}
739
}
740
}
741
742
static void draw_rect(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, float *rgba, float x0, float y0, float x1, float y1)
743
{
744
fz_path *path = fz_new_path(ctx);
745
746
fz_moveto(ctx, path, x0, y0);
747
fz_lineto(ctx, path, x1, y0);
748
fz_lineto(ctx, path, x1, y1);
749
fz_lineto(ctx, path, x0, y1);
750
fz_closepath(ctx, path);
751
752
fz_fill_path(ctx, dev, path, 0, ctm, fz_device_rgb(ctx), rgba, rgba[3]);
753
754
fz_drop_path(ctx, path);
755
}
756
757
static void draw_block_box(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *ctm)
758
{
759
float x0, y0, x1, y1;
760
float color[4];
761
762
// TODO: background fill
763
// TODO: border stroke
764
765
float *border = box->border;
766
float *padding = box->padding;
767
768
x0 = box->x - padding[L];
769
y0 = box->y - padding[T];
770
x1 = box->x + box->w + padding[R];
771
y1 = box->y + box->h + padding[B];
772
773
if (y0 > page_bot || y1 < page_top)
774
return;
775
776
if (box->style.background_color.a > 0)
777
{
778
color[0] = box->style.background_color.r / 255.0f;
779
color[1] = box->style.background_color.g / 255.0f;
780
color[2] = box->style.background_color.b / 255.0f;
781
color[3] = box->style.background_color.a / 255.0f;
782
draw_rect(ctx, dev, ctm, color, x0, y0, x1, y1);
783
}
784
785
if (box->style.border_color.a > 0)
786
{
787
color[0] = box->style.border_color.r / 255.0f;
788
color[1] = box->style.border_color.g / 255.0f;
789
color[2] = box->style.border_color.b / 255.0f;
790
color[3] = box->style.border_color.a / 255.0f;
791
if (border[T] > 0)
792
draw_rect(ctx, dev, ctm, color, x0 - border[L], y0 - border[T], x1 + border[R], y0);
793
if (border[B] > 0)
794
draw_rect(ctx, dev, ctm, color, x0 - border[L], y1, x1 + border[R], y1 + border[B]);
795
if (border[L] > 0)
796
draw_rect(ctx, dev, ctm, color, x0 - border[L], y0 - border[T], x0, y1 + border[B]);
797
if (border[R] > 0)
798
draw_rect(ctx, dev, ctm, color, x1, y0 - border[T], x1 + border[R], y1 + border[B]);
799
}
800
801
for (box = box->down; box; box = box->next)
802
{
803
switch (box->type)
804
{
805
case BOX_BLOCK: draw_block_box(ctx, box, page_top, page_bot, dev, ctm); break;
806
case BOX_FLOW: draw_flow_box(ctx, box, page_top, page_bot, dev, ctm); break;
807
}
808
}
809
}
810
811
void
812
fz_draw_html(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *inctm)
813
{
814
fz_matrix ctm = *inctm;
815
fz_pre_translate(&ctm, 0, -page_top);
816
draw_block_box(ctx, box, page_top, page_bot, dev, &ctm);
817
}
818
819
static char *concat_text(fz_context *ctx, fz_xml *root)
820
{
821
fz_xml *node;
822
int i = 0, n = 1;
823
char *s;
824
for (node = fz_xml_down(root); node; node = fz_xml_next(node))
825
{
826
const char *text = fz_xml_text(node);
827
n += text ? strlen(text) : 0;
828
}
829
s = fz_malloc(ctx, n);
830
for (node = fz_xml_down(root); node; node = fz_xml_next(node))
831
{
832
const char *text = fz_xml_text(node);
833
if (text)
834
{
835
n = strlen(text);
836
memcpy(s+i, text, n);
837
i += n;
838
}
839
}
840
s[i] = 0;
841
return s;
842
}
843
844
static fz_css_rule *
845
html_load_css(fz_context *ctx, fz_archive *zip, const char *base_uri, fz_css_rule *css, fz_xml *root)
846
{
847
fz_xml *node;
848
fz_buffer *buf;
849
char path[2048];
850
851
for (node = root; node; node = fz_xml_next(node))
852
{
853
const char *tag = fz_xml_tag(node);
854
if (tag && !strcmp(tag, "link"))
855
{
856
char *rel = fz_xml_att(node, "rel");
857
if (rel && !fz_strcasecmp(rel, "stylesheet"))
858
{
859
char *type = fz_xml_att(node, "type");
860
if ((type && !strcmp(type, "text/css")) || !type)
861
{
862
char *href = fz_xml_att(node, "href");
863
if (href)
864
{
865
fz_strlcpy(path, base_uri, sizeof path);
866
fz_strlcat(path, "/", sizeof path);
867
fz_strlcat(path, href, sizeof path);
868
fz_cleanname(path);
869
870
buf = fz_read_archive_entry(ctx, zip, path);
871
fz_write_buffer_byte(ctx, buf, 0);
872
fz_try(ctx)
873
css = fz_parse_css(ctx, css, (char*)buf->data, path);
874
fz_catch(ctx)
875
fz_warn(ctx, "ignoring stylesheet %s", path);
876
fz_drop_buffer(ctx, buf);
877
}
878
}
879
}
880
}
881
if (tag && !strcmp(tag, "style"))
882
{
883
char *s = concat_text(ctx, node);
884
fz_try(ctx)
885
css = fz_parse_css(ctx, css, s, "<style>");
886
fz_catch(ctx)
887
fz_warn(ctx, "ignoring inline stylesheet");
888
fz_free(ctx, s);
889
}
890
if (fz_xml_down(node))
891
css = html_load_css(ctx, zip, base_uri, css, fz_xml_down(node));
892
}
893
return css;
894
}
895
896
void
897
fz_layout_html(fz_context *ctx, fz_html *box, float w, float h, float em)
898
{
899
fz_html page_box;
900
901
init_box(ctx, &page_box);
902
page_box.w = w;
903
page_box.h = 0;
904
905
layout_block(ctx, box, &page_box, em, 0, h);
906
}
907
908
fz_html *
909
fz_parse_html(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_buffer *buf, const char *user_css)
910
{
911
fz_xml *xml;
912
fz_css_rule *css;
913
fz_css_match match;
914
fz_html *box;
915
916
xml = fz_parse_xml(ctx, buf->data, buf->len, 1);
917
918
css = fz_parse_css(ctx, NULL, default_css, "<default>");
919
if (user_css)
920
css = fz_parse_css(ctx, NULL, user_css, "<user>");
921
css = html_load_css(ctx, zip, base_uri, css, xml);
922
923
// print_rules(css);
924
925
box = new_box(ctx);
926
927
match.up = NULL;
928
match.count = 0;
929
930
generate_boxes(ctx, set, zip, base_uri, xml, box, css, &match);
931
932
fz_drop_css(ctx, css);
933
fz_drop_xml(ctx, xml);
934
935
return box;
936
}
937
938