summaryrefslogtreecommitdiff
path: root/source/html
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2015-04-20 17:56:02 +0200
committerTor Andersson <tor.andersson@artifex.com>2015-04-20 17:56:02 +0200
commit1e09b18bfd55536c49899b73e116b46599412f56 (patch)
tree549d9133473ce3fb5ce0fa17e7a335953e1bd990 /source/html
parentd6eb03bf8603b89e181d036e76e555ff43a8fd8f (diff)
downloadmupdf-1e09b18bfd55536c49899b73e116b46599412f56.tar.xz
epub: list items with bullets and counters
Diffstat (limited to 'source/html')
-rw-r--r--source/html/css-apply.c103
-rw-r--r--source/html/html-layout.c72
2 files changed, 143 insertions, 32 deletions
diff --git a/source/html/css-apply.c b/source/html/css-apply.c
index 42e247e1..98818239 100644
--- a/source/html/css-apply.c
+++ b/source/html/css-apply.c
@@ -64,6 +64,29 @@ static const char *color_kw[] = {
"yellow",
};
+static const char *list_style_type_kw[] = {
+ "armenian",
+ "circle",
+ "decimal",
+ "decimal-leading-zero",
+ "disc",
+ "georgian",
+ "lower-alpha",
+ "lower-greek",
+ "lower-latin",
+ "lower-roman",
+ "none",
+ "square",
+ "upper-alpha",
+ "upper-latin",
+ "upper-roman",
+};
+
+static const char *list_style_position_kw[] = {
+ "inside",
+ "outside",
+};
+
static int
keyword_in_list(const char *name, const char **list, int n)
{
@@ -418,6 +441,26 @@ add_shorthand_border(fz_css_match *match, fz_css_value *value, int spec)
}
static void
+add_shorthand_list_style(fz_css_match *match, fz_css_value *value, int spec)
+{
+ while (value)
+ {
+ if (value->type == CSS_KEYWORD)
+ {
+ if (keyword_in_list(value->data, list_style_type_kw, nelem(list_style_type_kw)))
+ {
+ add_property(match, "list-style-type", value, spec);
+ }
+ else if (keyword_in_list(value->data, list_style_position_kw, nelem(list_style_position_kw)))
+ {
+ add_property(match, "list-style-position", value, spec);
+ }
+ }
+ value = value->next;
+ }
+}
+
+static void
add_property(fz_css_match *match, const char *name, fz_css_value *value, int spec)
{
int i;
@@ -442,6 +485,11 @@ add_property(fz_css_match *match, const char *name, fz_css_value *value, int spe
add_shorthand_border(match, value, spec);
return;
}
+ if (!strcmp(name, "list-style"))
+ {
+ add_shorthand_list_style(match, value, spec);
+ return;
+ }
/* shorthand expansions: */
/* TODO: border-color */
@@ -751,10 +799,10 @@ white_space_from_property(fz_css_match *match)
if (value)
{
if (!strcmp(value->data, "normal")) return WS_NORMAL;
- if (!strcmp(value->data, "pre")) return WS_PRE;
- if (!strcmp(value->data, "nowrap")) return WS_NOWRAP;
- if (!strcmp(value->data, "pre-wrap")) return WS_PRE_WRAP;
- if (!strcmp(value->data, "pre-line")) return WS_PRE_LINE;
+ else if (!strcmp(value->data, "pre")) return WS_PRE;
+ else if (!strcmp(value->data, "nowrap")) return WS_NOWRAP;
+ else if (!strcmp(value->data, "pre-wrap")) return WS_PRE_WRAP;
+ else if (!strcmp(value->data, "pre-line")) return WS_PRE_LINE;
}
return WS_NORMAL;
}
@@ -766,6 +814,7 @@ fz_default_css_style(fz_context *ctx, fz_css_style *style)
style->text_align = TA_LEFT;
style->vertical_align = VA_BASELINE;
style->white_space = WS_NORMAL;
+ style->list_style_type = LST_DISC;
style->font_size = make_number(1, N_SCALE);
}
@@ -784,29 +833,20 @@ fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style,
value = value_from_property(match, "text-align");
if (value)
{
- if (!strcmp(value->data, "left"))
- style->text_align = TA_LEFT;
- if (!strcmp(value->data, "right"))
- style->text_align = TA_RIGHT;
- if (!strcmp(value->data, "center"))
- style->text_align = TA_CENTER;
- if (!strcmp(value->data, "justify"))
- style->text_align = TA_JUSTIFY;
+ if (!strcmp(value->data, "left")) style->text_align = TA_LEFT;
+ else if (!strcmp(value->data, "right")) style->text_align = TA_RIGHT;
+ else if (!strcmp(value->data, "center")) style->text_align = TA_CENTER;
+ else if (!strcmp(value->data, "justify")) style->text_align = TA_JUSTIFY;
}
value = value_from_property(match, "vertical-align");
if (value)
{
- if (!strcmp(value->data, "baseline"))
- style->vertical_align = VA_BASELINE;
- if (!strcmp(value->data, "sub"))
- style->vertical_align = VA_SUB;
- if (!strcmp(value->data, "super"))
- style->vertical_align = VA_SUPER;
- if (!strcmp(value->data, "top"))
- style->vertical_align = VA_TOP;
- if (!strcmp(value->data, "bottom"))
- style->vertical_align = VA_BOTTOM;
+ if (!strcmp(value->data, "baseline")) style->vertical_align = VA_BASELINE;
+ else if (!strcmp(value->data, "sub")) style->vertical_align = VA_SUB;
+ else if (!strcmp(value->data, "super")) style->vertical_align = VA_SUPER;
+ else if (!strcmp(value->data, "top")) style->vertical_align = VA_TOP;
+ else if (!strcmp(value->data, "bottom")) style->vertical_align = VA_BOTTOM;
}
value = value_from_property(match, "font-size");
@@ -831,12 +871,19 @@ fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style,
value = value_from_property(match, "border-style");
if (value)
{
- if (!strcmp(value->data, "none"))
- style->border_style = BS_NONE;
- if (!strcmp(value->data, "hidden"))
- style->border_style = BS_NONE;
- if (!strcmp(value->data, "solid"))
- style->border_style = BS_SOLID;
+ if (!strcmp(value->data, "none")) style->border_style = BS_NONE;
+ else if (!strcmp(value->data, "hidden")) style->border_style = BS_NONE;
+ else if (!strcmp(value->data, "solid")) style->border_style = BS_SOLID;
+ }
+
+ value = value_from_property(match, "list-style-type");
+ if (value)
+ {
+ if (!strcmp(value->data, "none")) style->list_style_type = LST_NONE;
+ else if (!strcmp(value->data, "disc")) style->list_style_type = LST_DISC;
+ else if (!strcmp(value->data, "circle")) style->list_style_type = LST_CIRCLE;
+ else if (!strcmp(value->data, "square")) style->list_style_type = LST_SQUARE;
+ else if (!strcmp(value->data, "decimal")) style->list_style_type = LST_DECIMAL;
}
style->line_height = number_from_property(match, "line-height", 1.2f, N_SCALE);
diff --git a/source/html/html-layout.c b/source/html/html-layout.c
index 157c8685..7231205f 100644
--- a/source/html/html-layout.c
+++ b/source/html/html-layout.c
@@ -271,7 +271,7 @@ static void insert_inline_box(fz_context *ctx, fz_html *box, fz_html *top)
}
static void generate_boxes(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri,
- fz_xml *node, fz_html *top, fz_css_rule *rule, fz_css_match *up_match)
+ fz_xml *node, fz_html *top, fz_css_rule *rule, fz_css_match *up_match, int list_counter)
{
fz_css_match match;
fz_html *box;
@@ -321,6 +321,7 @@ static void generate_boxes(fz_context *ctx, fz_html_font_set *set, fz_archive *z
else if (display == DIS_LIST_ITEM)
{
top = insert_block_box(ctx, box, top);
+ box->list_item = ++list_counter;
}
else if (display == DIS_INLINE)
{
@@ -333,7 +334,12 @@ static void generate_boxes(fz_context *ctx, fz_html_font_set *set, fz_archive *z
}
if (fz_xml_down(node))
- generate_boxes(ctx, set, zip, base_uri, fz_xml_down(node), box, rule, &match);
+ {
+ int child_counter = list_counter;
+ if (!strcmp(tag, "ul") || !strcmp(tag, "ol"))
+ child_counter = 0;
+ generate_boxes(ctx, set, zip, base_uri, fz_xml_down(node), box, rule, &match, child_counter);
+ }
// TODO: remove empty flow boxes
}
@@ -602,7 +608,7 @@ static void layout_block(fz_context *ctx, fz_html *box, fz_html *top, float em,
float *border = box->border;
float *padding = box->padding;
- em = fz_from_css_number(box->style.font_size, em, em);
+ em = box->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);
@@ -754,6 +760,59 @@ static void draw_rect(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, flo
fz_drop_path(ctx, path);
}
+static void draw_list_mark(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *ctm, int n)
+{
+ fz_text *text;
+ fz_matrix trm;
+ float x, y, w, baseline;
+ float color[3];
+ const char *s, *p;
+ char buf[40];
+ int c, g;
+
+ fz_scale(&trm, box->em, -box->em);
+ text = fz_new_text(ctx, box->style.font, &trm, 0);
+ baseline = box->em * 0.8 + (box->h - box->em) / 2;
+
+ switch (box->style.list_style_type)
+ {
+ default:
+ case LST_DISC: p = "\342\227\217 "; break; /* U+25CF BLACK CIRCLE */
+ case LST_CIRCLE: p = "\342\227\213 "; break; /* U+25CB WHITE CIRCLE */
+ case LST_SQUARE: p = "\342\226\240 "; break; /* U+25A0 BLACK SQUARE */
+ case LST_DECIMAL: p = buf; fz_snprintf(buf, sizeof buf, "%d. ", n); break;
+ case LST_NONE: p = ""; break;
+ }
+
+ s = p;
+ w = 0;
+ while (*s)
+ {
+ s += fz_chartorune(&c, s);
+ g = fz_encode_character(ctx, box->style.font, c);
+ w += fz_advance_glyph(ctx, box->style.font, g) * box->em;
+ }
+
+ s = p;
+ x = box->x - box->padding[L] - box->border[L] - box->margin[L] - w;
+ y = box->y + baseline;
+ while (*s)
+ {
+ s += fz_chartorune(&c, s);
+ g = fz_encode_character(ctx, box->style.font, c);
+ fz_add_text(ctx, text, g, c, x, y);
+ x += fz_advance_glyph(ctx, box->style.font, g) * box->em;
+ }
+
+ color[0] = box->style.color.r / 255.0f;
+ color[1] = box->style.color.g / 255.0f;
+ color[2] = box->style.color.b / 255.0f;
+
+ fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1);
+
+ fz_drop_text(ctx, text);
+}
+
static void draw_block_box(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *ctm)
{
float x0, y0, x1, y1;
@@ -798,6 +857,11 @@ static void draw_block_box(fz_context *ctx, fz_html *box, float page_top, float
draw_rect(ctx, dev, ctm, color, x1, y0 - border[T], x1 + border[R], y1 + border[B]);
}
+ if (box->list_item)
+ {
+ draw_list_mark(ctx, box, page_top, page_bot, dev, ctm, box->list_item);
+ }
+
for (box = box->down; box; box = box->next)
{
switch (box->type)
@@ -927,7 +991,7 @@ fz_parse_html(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const cha
match.up = NULL;
match.count = 0;
- generate_boxes(ctx, set, zip, base_uri, xml, box, css, &match);
+ generate_boxes(ctx, set, zip, base_uri, xml, box, css, &match, 0);
fz_drop_css(ctx, css);
fz_drop_xml(ctx, xml);