static void measure_word(fz_context *ctx, fz_html_flow *node, float em) { const char *s; int c, g; float w; em = fz_from_css_number(node->style->font_size, em, em); node->x = 0; node->y = 0; node->h = fz_from_css_number_scale(node->style->line_height, em, em, em); w = 0; s = node->text; while (*s) { s += fz_chartorune(&c, s); g = fz_encode_character(ctx, node->style->font, c); if (g) { w += fz_advance_glyph(ctx, node->style->font, g) * em; } else { g = fz_encode_character(ctx, node->style->fallback, c); w += fz_advance_glyph(ctx, node->style->fallback, g) * em; } } node->w = w; node->em = em; }
static void htdoc_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em) { html_document *doc = (html_document*)doc_; if (doc->box) { doc->page_margin[T] = fz_from_css_number(doc->box->style.margin[T], em, em); doc->page_margin[B] = fz_from_css_number(doc->box->style.margin[B], em, em); doc->page_margin[L] = fz_from_css_number(doc->box->style.margin[L], em, em); doc->page_margin[R] = fz_from_css_number(doc->box->style.margin[R], em, em); } doc->page_w = w - doc->page_margin[L] - doc->page_margin[R]; doc->page_h = h - doc->page_margin[T] - doc->page_margin[B]; doc->em = em; fz_layout_html(ctx, doc->box, doc->page_w, doc->page_h, doc->em); }
static void epub_layout(fz_context *ctx, fz_document *doc_, float w, float h, float em) { epub_document *doc = (epub_document*)doc_; epub_chapter *ch; printf("epub: laying out chapters.\n"); for (ch = doc->spine; ch; ch = ch->next) { ch->em = em; ch->page_margin[T] = fz_from_css_number(ch->box->style.margin[T], em, em); ch->page_margin[B] = fz_from_css_number(ch->box->style.margin[B], em, em); ch->page_margin[L] = fz_from_css_number(ch->box->style.margin[L], em, em); ch->page_margin[R] = fz_from_css_number(ch->box->style.margin[R], em, em); ch->page_w = w - ch->page_margin[L] - ch->page_margin[R]; ch->page_h = h - ch->page_margin[T] - ch->page_margin[B]; fz_layout_html(ctx, ch->box, ch->page_w, ch->page_h, ch->em); } printf("epub: done.\n"); }
static float layout_block(fz_context *ctx, fz_html *box, fz_html *top, float em, float page_h, float vertical) { fz_html *child; int first; fz_css_style *style = &box->style; float *margin = box->margin; float *border = box->border; float *padding = box->padding; em = box->em = fz_from_css_number(style->font_size, em, em); margin[0] = fz_from_css_number(style->margin[0], em, top->w); margin[1] = fz_from_css_number(style->margin[1], em, top->w); margin[2] = fz_from_css_number(style->margin[2], em, top->w); margin[3] = fz_from_css_number(style->margin[3], em, top->w); padding[0] = fz_from_css_number(style->padding[0], em, top->w); padding[1] = fz_from_css_number(style->padding[1], em, top->w); padding[2] = fz_from_css_number(style->padding[2], em, top->w); padding[3] = fz_from_css_number(style->padding[3], em, top->w); border[0] = style->border_style[0] ? fz_from_css_number(style->border_width[0], em, top->w) : 0; border[1] = style->border_style[1] ? fz_from_css_number(style->border_width[1], em, top->w) : 0; border[2] = style->border_style[2] ? fz_from_css_number(style->border_width[2], em, top->w) : 0; border[3] = style->border_style[3] ? fz_from_css_number(style->border_width[3], em, top->w) : 0; box->x = top->x + margin[L] + border[L] + padding[L]; box->w = top->w - (margin[L] + margin[R] + border[L] + border[R] + padding[L] + padding[R]); if (margin[T] > vertical) margin[T] -= vertical; else margin[T] = 0; if (padding[T] == 0 && border[T] == 0) vertical += margin[T]; else vertical = 0; box->y = top->y + top->h + margin[T] + border[T] + padding[T]; box->h = 0; first = 1; for (child = box->down; child; child = child->next) { if (child->type == BOX_BLOCK) { vertical = layout_block(ctx, child, box, em, page_h, vertical); if (first) { /* move collapsed parent/child top margins to parent */ margin[T] += child->margin[T]; box->y += child->margin[T]; child->margin[T] = 0; first = 0; } box->h += child->h + child->padding[T] + child->padding[B] + child->border[T] + child->border[B] + child->margin[T] + child->margin[B]; } else if (child->type == BOX_BREAK) { box->h += fz_from_css_number_scale(style->line_height, em, em, em); vertical = 0; first = 0; } else if (child->type == BOX_FLOW) { layout_flow(ctx, child, box, em, page_h); if (child->h > 0) { box->h += child->h; vertical = 0; first = 0; } } } /* reserve space for the list mark */ if (box->list_item && box->h == 0) { box->h += fz_from_css_number_scale(style->line_height, em, em, em); vertical = 0; } if (box->h == 0) { if (margin[B] > vertical) margin[B] -= vertical; else margin[B] = 0; } else { box->h -= vertical; vertical = fz_max(margin[B], vertical); margin[B] = vertical; } return vertical; }
static void layout_flow(fz_context *ctx, fz_html *box, fz_html *top, float em, float page_h) { fz_html_flow *node, *line, *mark; float line_w; float indent; int align; int line_align; em = fz_from_css_number(box->style.font_size, em, em); indent = box->is_first_flow ? fz_from_css_number(top->style.text_indent, em, top->w) : 0; align = top->style.text_align; if (box->flow_dir == BIDI_RIGHT_TO_LEFT) { if (align == TA_LEFT) align = TA_RIGHT; else if (align == TA_RIGHT) align = TA_LEFT; } box->x = top->x; box->y = top->y + top->h; box->w = top->w; box->h = 0; if (!box->flow_head) return; for (node = box->flow_head; node; node = node->next) { if (node->type == FLOW_IMAGE) { float w = 0, h = 0; find_accumulated_margins(ctx, box, &w, &h); measure_image(ctx, node, top->w - w, page_h - h); } else { measure_word(ctx, node, em); } } /* start by skipping whitespace (and newline) at the beginning of tags */ node = box->flow_head; if (node->type == FLOW_BREAK) node = node->next; while (node && node->type == FLOW_GLUE) node = node->next; mark = NULL; line = node; line_w = indent; while (node) { switch (node->type) { case FLOW_WORD: break; case FLOW_IMAGE: /* TODO: break before/after image */ mark = node; break; case FLOW_GLUE: mark = node; break; case FLOW_BREAK: line_align = align == TA_JUSTIFY ? TA_LEFT : align; flush_line(ctx, box, page_h, top->w, line_align, indent, line, node); indent = 0; line = node->next; line_w = 0; mark = NULL; break; } if (mark && line_w + node->w > top->w) { flush_line(ctx, box, page_h, top->w, align, indent, line, mark); indent = 0; node = mark; while (node && node->type == FLOW_GLUE) node = node->next; line = node; line_w = 0; mark = NULL; } if (node) { line_w += node->w; node = node->next; } } if (line) { line_align = align == TA_JUSTIFY ? TA_LEFT : align; flush_line(ctx, box, page_h, top->w, line_align, indent, line, NULL); } }
static void layout_block(fz_context *ctx, fz_html *box, fz_html *top, float em, float top_collapse_margin, float page_h) { fz_html *child; float box_collapse_margin; int prev_br; float *margin = box->margin; float *border = box->border; float *padding = box->padding; em = fz_from_css_number(box->style.font_size, em, em); margin[0] = fz_from_css_number(box->style.margin[0], em, top->w); margin[1] = fz_from_css_number(box->style.margin[1], em, top->w); margin[2] = fz_from_css_number(box->style.margin[2], em, top->w); margin[3] = fz_from_css_number(box->style.margin[3], em, top->w); padding[0] = fz_from_css_number(box->style.padding[0], em, top->w); padding[1] = fz_from_css_number(box->style.padding[1], em, top->w); padding[2] = fz_from_css_number(box->style.padding[2], em, top->w); padding[3] = fz_from_css_number(box->style.padding[3], em, top->w); if (box->style.border_style) { border[0] = fz_from_css_number(box->style.border_width[0], em, top->w); border[1] = fz_from_css_number(box->style.border_width[1], em, top->w); border[2] = fz_from_css_number(box->style.border_width[2], em, top->w); border[3] = fz_from_css_number(box->style.border_width[3], em, top->w); } else border[0] = border[1] = border[2] = border[3] = 0; if (padding[T] == 0 && border[T] == 0) box_collapse_margin = margin[T]; else box_collapse_margin = 0; if (margin[T] > top_collapse_margin) margin[T] -= top_collapse_margin; else margin[T] = 0; box->x = top->x + margin[L] + border[L] + padding[L]; box->y = top->y + top->h + margin[T] + border[T] + padding[T]; box->w = top->w - (margin[L] + margin[R] + border[L] + border[R] + padding[L] + padding[R]); box->h = 0; prev_br = 0; for (child = box->down; child; child = child->next) { if (child->type == BOX_BLOCK) { layout_block(ctx, child, box, em, box_collapse_margin, page_h); box->h += child->h + child->padding[T] + child->padding[B] + child->border[T] + child->border[B] + child->margin[T] + child->margin[B]; box_collapse_margin = child->margin[B]; prev_br = 0; } else if (child->type == BOX_BREAK) { /* TODO: interaction with page breaks */ if (prev_br) box->h += fz_from_css_number_scale(box->style.line_height, em, em, em); prev_br = 1; } else if (child->type == BOX_FLOW) { layout_flow(ctx, child, box, em, page_h); if (child->h > 0) { box->h += child->h; box_collapse_margin = 0; prev_br = 0; } } } if (padding[B] == 0 && border[B] == 0) { if (margin[B] > 0) { box->h -= box_collapse_margin; if (margin[B] < box_collapse_margin) margin[B] = box_collapse_margin; } } }
static void layout_flow(fz_context *ctx, fz_html *box, fz_html *top, float em, float page_h) { fz_html_flow *node, *line_start, *word_start, *word_end, *line_end; float glue_w; float word_w; float line_w; float indent; float avail, line_h; float baseline; int align; em = fz_from_css_number(box->style.font_size, em, em); indent = box->is_first_flow ? fz_from_css_number(top->style.text_indent, em, top->w) : 0; align = top->style.text_align; box->x = top->x; box->y = top->y + top->h; box->w = top->w; box->h = 0; if (!box->flow_head) return; for (node = box->flow_head; node; node = node->next) if (node->type == FLOW_IMAGE) measure_image(ctx, node, top->w, page_h); else measure_word(ctx, node, em); line_start = find_next_word(box->flow_head, &glue_w); line_end = NULL; line_w = indent; word_w = 0; word_start = line_start; while (word_start) { word_end = find_next_glue(word_start, &word_w); if (line_w + word_w <= top->w) { line_w += word_w; glue_w = 0; line_end = word_end; word_start = find_next_word(word_end, &glue_w); word_w = glue_w; } else { avail = page_h - fmodf(box->y + box->h, page_h); line_h = measure_line(line_start, line_end, &baseline); if (line_h > avail) box->h += avail; layout_line(ctx, indent, top->w, line_w, align, line_start, line_end, box, baseline); box->h += line_h; word_start = find_next_word(line_end, &glue_w); line_start = word_start; line_end = NULL; indent = 0; line_w = 0; word_w = 0; } } /* don't justify the last line of a paragraph */ if (align == TA_JUSTIFY) align = TA_LEFT; if (line_start) { avail = page_h - fmodf(box->y + box->h, page_h); line_h = measure_line(line_start, line_end, &baseline); if (line_h > avail) box->h += avail; layout_line(ctx, indent, top->w, line_w, align, line_start, line_end, box, baseline); box->h += line_h; } }
static fz_css_color color_from_value(fz_css_value *value, fz_css_color initial) { if (!value) return initial; if (value->type == CSS_HASH) { int r, g, b; size_t n = strlen(value->data); if (n == 3) { r = tohex(value->data[0]) * 16 + tohex(value->data[0]); g = tohex(value->data[1]) * 16 + tohex(value->data[1]); b = tohex(value->data[2]) * 16 + tohex(value->data[2]); } else if (n == 6) { r = tohex(value->data[0]) * 16 + tohex(value->data[1]); g = tohex(value->data[2]) * 16 + tohex(value->data[3]); b = tohex(value->data[4]) * 16 + tohex(value->data[5]); } else { r = g = b = 0; } return make_color(r, g, b, 255); } if (value->type == '(' && !strcmp(value->data, "rgb")) { fz_css_value *vr, *vg, *vb; int r, g, b; vr = value->args; vg = vr && vr->next ? vr->next->next : NULL; /* skip the ',' nodes */ vb = vg && vg->next ? vg->next->next : NULL; /* skip the ',' nodes */ r = fz_from_css_number(number_from_value(vr, 0, N_NUMBER), 255, 255); g = fz_from_css_number(number_from_value(vg, 0, N_NUMBER), 255, 255); b = fz_from_css_number(number_from_value(vb, 0, N_NUMBER), 255, 255); return make_color(r, g, b, 255); } if (value->type == CSS_KEYWORD) { if (!strcmp(value->data, "transparent")) return make_color(0, 0, 0, 0); if (!strcmp(value->data, "maroon")) return make_color(0x80, 0x00, 0x00, 255); if (!strcmp(value->data, "red")) return make_color(0xFF, 0x00, 0x00, 255); if (!strcmp(value->data, "orange")) return make_color(0xFF, 0xA5, 0x00, 255); if (!strcmp(value->data, "yellow")) return make_color(0xFF, 0xFF, 0x00, 255); if (!strcmp(value->data, "olive")) return make_color(0x80, 0x80, 0x00, 255); if (!strcmp(value->data, "purple")) return make_color(0x80, 0x00, 0x80, 255); if (!strcmp(value->data, "fuchsia")) return make_color(0xFF, 0x00, 0xFF, 255); if (!strcmp(value->data, "white")) return make_color(0xFF, 0xFF, 0xFF, 255); if (!strcmp(value->data, "lime")) return make_color(0x00, 0xFF, 0x00, 255); if (!strcmp(value->data, "green")) return make_color(0x00, 0x80, 0x00, 255); if (!strcmp(value->data, "navy")) return make_color(0x00, 0x00, 0x80, 255); if (!strcmp(value->data, "blue")) return make_color(0x00, 0x00, 0xFF, 255); if (!strcmp(value->data, "aqua")) return make_color(0x00, 0xFF, 0xFF, 255); if (!strcmp(value->data, "teal")) return make_color(0x00, 0x80, 0x80, 255); if (!strcmp(value->data, "black")) return make_color(0x00, 0x00, 0x00, 255); if (!strcmp(value->data, "silver")) return make_color(0xC0, 0xC0, 0xC0, 255); if (!strcmp(value->data, "gray")) return make_color(0x80, 0x80, 0x80, 255); return make_color(0, 0, 0, 255); } return initial; }