diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2016-01-22 12:00:55 +0100 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2016-01-22 12:00:55 +0100 |
commit | f501211e904d2c01e47b03e774ffc6f93faeae25 (patch) | |
tree | aa13c9d28c17e3dbb73459ccf7c6a8ca6c3906e4 | |
parent | b31c7d1559125b5a0c596e52d632c70e48c506cf (diff) | |
download | mupdf-f501211e904d2c01e47b03e774ffc6f93faeae25.tar.xz |
epub: Implement @font-face rules.
Note: font->fallback is not reference counted here. The fallback
mechanism is probably going to have to change when we add text shaping.
-rw-r--r-- | include/mupdf/html.h | 20 | ||||
-rw-r--r-- | source/fitz/font.c | 2 | ||||
-rw-r--r-- | source/html/css-apply.c | 119 | ||||
-rw-r--r-- | source/html/css-parse.c | 21 | ||||
-rw-r--r-- | source/html/html-font.c | 71 | ||||
-rw-r--r-- | source/html/html-layout.c | 2 |
6 files changed, 216 insertions, 19 deletions
diff --git a/include/mupdf/html.h b/include/mupdf/html.h index 75d46313..48231281 100644 --- a/include/mupdf/html.h +++ b/include/mupdf/html.h @@ -3,6 +3,7 @@ #include "mupdf/fitz.h" +typedef struct fz_html_font_face_s fz_html_font_face; typedef struct fz_html_font_set_s fz_html_font_set; typedef struct fz_html_s fz_html; typedef struct fz_html_flow_s fz_html_flow; @@ -19,10 +20,21 @@ typedef struct fz_css_value_s fz_css_value; typedef struct fz_css_number_s fz_css_number; typedef struct fz_css_color_s fz_css_color; +struct fz_html_font_face_s +{ + char *family; + int is_bold; + int is_italic; + fz_font *font; + char *src; + fz_html_font_face *next; +}; + struct fz_html_font_set_s { fz_font *fonts[16]; fz_font *fallback; + fz_html_font_face *custom; }; enum @@ -239,11 +251,13 @@ float fz_from_css_number(fz_css_number, float em, float width); float fz_from_css_number_scale(fz_css_number number, float scale, float em, float width); fz_html_font_set *fz_new_html_font_set(fz_context *ctx); -fz_font *fz_load_html_font(fz_context *ctx, fz_html_font_set *set, - const char *family, const char *variant, const char *style, const char *weight); -fz_font *fz_load_html_fallback_font(fz_context *ctx, fz_html_font_set *set); +void fz_add_html_font_face(fz_context *ctx, fz_html_font_set *set, + const char *family, int is_bold, int is_italic, const char *src, fz_font *font); +fz_font *fz_load_html_font(fz_context *ctx, fz_html_font_set *set, const char *family, int is_bold, int is_italic); void fz_drop_html_font_set(fz_context *ctx, fz_html_font_set *htx); +void fz_add_css_font_faces(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css_rule *css); + fz_html *fz_parse_html(fz_context *ctx, fz_html_font_set *htx, fz_archive *zip, const char *base_uri, fz_buffer *buf, const char *user_css); void fz_layout_html(fz_context *ctx, fz_html *box, float w, float h, float em); void fz_draw_html(fz_context *ctx, fz_html *box, float page_top, float page_bot, fz_device *dev, const fz_matrix *ctm); diff --git a/source/fitz/font.c b/source/fitz/font.c index bdfee584..792e8d95 100644 --- a/source/fitz/font.c +++ b/source/fitz/font.c @@ -120,8 +120,6 @@ fz_drop_font(fz_context *ctx, fz_font *font) if (!fz_drop_imp(ctx, font, &font->refs)) return; - fz_drop_font(ctx, font->fallback); - if (font->t3lists) { free_resources(ctx, font); diff --git a/source/html/css-apply.c b/source/html/css-apply.c index 45fa7713..db588bf7 100644 --- a/source/html/css-apply.c +++ b/source/html/css-apply.c @@ -106,6 +106,18 @@ keyword_in_list(const char *name, const char **list, int n) return 0; } +static int +is_bold_from_font_weight(const char *weight) +{ + return !strcmp(weight, "bold") || !strcmp(weight, "bolder") || atoi(weight) > 400; +} + +static int +is_italic_from_font_style(const char *style) +{ + return !strcmp(style, "italic") || !strcmp(style, "oblique"); +} + /* * Compute specificity */ @@ -673,6 +685,93 @@ fz_match_css_at_page(fz_context *ctx, fz_css_match *match, fz_css_rule *css) sort_properties(match); /* speed up subsequent value_from_raw_property lookups */ } +void +fz_add_css_font_face(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css_property *declaration) +{ + fz_html_font_face *custom; + fz_css_property *prop; + fz_font *font = NULL; + fz_buffer *buf = NULL; + int is_bold, is_italic; + char path[2048]; + + const char *family = "serif"; + const char *weight = "normal"; + const char *style = "normal"; + const char *src = NULL; + + for (prop = declaration; prop; prop = prop->next) + { + if (!strcmp(prop->name, "font-family")) family = prop->value->data; + if (!strcmp(prop->name, "font-weight")) weight = prop->value->data; + if (!strcmp(prop->name, "font-style")) style = prop->value->data; + if (!strcmp(prop->name, "src")) src = prop->value->data; + } + + if (!src) + return; + + is_bold = is_bold_from_font_weight(weight); + is_italic = is_italic_from_font_style(style); + + fz_strlcpy(path, base_uri, sizeof path); + fz_strlcat(path, "/", sizeof path); + fz_strlcat(path, src, sizeof path); + fz_urldecode(path); + fz_cleanname(path); + + for (custom = set->custom; custom; custom = custom->next) + if (!strcmp(custom->src, path) && !strcmp(custom->family, family) && + custom->is_bold == is_bold && + custom->is_italic == is_italic) + return; /* already loaded */ + + printf("epub: @font-face: family='%s' b=%d i=%d src=%s\n", family, is_bold, is_italic, src); + + fz_var(buf); + fz_var(font); + + fz_try(ctx) + { + if (fz_has_archive_entry(ctx, zip, path)) + buf = fz_read_archive_entry(ctx, zip, path); + else + buf = fz_read_file(ctx, src); + font = fz_new_font_from_buffer(ctx, src, buf, 0, 0); + fz_add_html_font_face(ctx, set, family, is_bold, is_italic, path, font); + } + fz_always(ctx) + { + fz_drop_buffer(ctx, buf); + fz_drop_font(ctx, font); + } + fz_catch(ctx) + { + fz_warn(ctx, "cannot load font-face: %s", src); + } +} + +void +fz_add_css_font_faces(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const char *base_uri, fz_css_rule *css) +{ + fz_css_rule *rule; + fz_css_selector *sel; + + for (rule = css; rule; rule = rule->next) + { + sel = rule->selector; + while (sel) + { + if (sel->name && !strcmp(sel->name, "@font-face")) + { + fz_add_css_font_face(ctx, set, zip, base_uri, rule->declaration); + break; + } + sel = sel->next; + } + } +} + static fz_css_value * value_from_raw_property(fz_css_match *match, const char *name) { @@ -1156,11 +1255,23 @@ fz_apply_css_style(fz_context *ctx, fz_html_font_set *set, fz_css_style *style, style->border_width[3] = border_width_from_property(match, "border-left-width"); { - const char *font_family = string_from_property(match, "font-family", "serif"); - const char *font_variant = string_from_property(match, "font-variant", "normal"); - const char *font_style = string_from_property(match, "font-style", "normal"); const char *font_weight = string_from_property(match, "font-weight", "normal"); - style->font = fz_load_html_font(ctx, set, font_family, font_variant, font_style, font_weight); + const char *font_style = string_from_property(match, "font-style", "normal"); + int is_bold = is_bold_from_font_weight(font_weight); + int is_italic = is_italic_from_font_style(font_style); + value = value_from_property(match, "font-family"); + while (value) + { + if (strcmp(value->data, ",") != 0) + { + style->font = fz_load_html_font(ctx, set, value->data, is_bold, is_italic); + if (style->font) + break; + } + value = value->next; + } + if (!style->font) + style->font = fz_load_html_font(ctx, set, "serif", 0, 0); } } diff --git a/source/html/css-parse.c b/source/html/css-parse.c index f9d7595c..e395e933 100644 --- a/source/html/css-parse.c +++ b/source/html/css-parse.c @@ -879,6 +879,21 @@ static fz_css_rule *parse_at_page(struct lexbuf *buf) return fz_new_css_rule(buf->ctx, s, p); } +static fz_css_rule *parse_at_font_face(struct lexbuf *buf) +{ + fz_css_selector *s = NULL; + fz_css_property *p = NULL; + + white(buf); + expect(buf, '{'); + p = parse_declaration_list(buf); + expect(buf, '}'); + white(buf); + + s = fz_new_css_selector(buf->ctx, "@font-face"); + return fz_new_css_rule(buf->ctx, s, p); +} + static void parse_at_rule(struct lexbuf *buf) { expect(buf, CSS_KEYWORD); @@ -938,6 +953,12 @@ static fz_css_rule *parse_stylesheet(struct lexbuf *buf, fz_css_rule *chain) rule = *nextp = parse_at_page(buf); nextp = &rule->next; } + else if (buf->lookahead == CSS_KEYWORD && !strcmp(buf->string, "font-face")) + { + next(buf); + rule = *nextp = parse_at_font_face(buf); + nextp = &rule->next; + } else { parse_at_rule(buf); diff --git a/source/html/html-font.c b/source/html/html-font.c index 4b138162..d595b582 100644 --- a/source/html/html-font.c +++ b/source/html/html-font.c @@ -11,7 +11,7 @@ static const char *font_names[16] = "Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique", }; -fz_font * +static fz_font * fz_load_html_fallback_font(fz_context *ctx, fz_html_font_set *set) { if (!set->fallback) @@ -29,30 +29,67 @@ fz_load_html_fallback_font(fz_context *ctx, fz_html_font_set *set) } fz_font * -fz_load_html_font(fz_context *ctx, fz_html_font_set *set, - const char *family, const char *variant, const char *style, const char *weight) +fz_load_html_builtin_font(fz_context *ctx, fz_html_font_set *set, const char *family, int is_bold, int is_italic) { - unsigned char *data; - unsigned int size; - int is_mono = !strcmp(family, "monospace"); int is_sans = !strcmp(family, "sans-serif"); - int is_bold = !strcmp(weight, "bold") || !strcmp(weight, "bolder") || atoi(weight) > 400; - int is_italic = !strcmp(style, "italic") || !strcmp(style, "oblique"); - int idx = is_mono * 8 + is_sans * 4 + is_bold * 2 + is_italic; if (!set->fonts[idx]) { + unsigned char *data; + unsigned int size; + data = pdf_lookup_builtin_font(ctx, font_names[idx], &size); if (!data) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load html font: %s", font_names[idx]); set->fonts[idx] = fz_new_font_from_memory(ctx, font_names[idx], data, size, 0, 1); set->fonts[idx]->fallback = fz_load_html_fallback_font(ctx, set); } - return set->fonts[idx]; } +fz_font * +fz_load_html_font(fz_context *ctx, fz_html_font_set *set, const char *family, int is_bold, int is_italic) +{ + fz_html_font_face *custom; + + for (custom = set->custom; custom; custom = custom->next) + { + if (!strcmp(family, custom->family) && + is_bold == custom->is_bold && + is_italic == custom->is_italic) + { + return custom->font; + } + } + + if (!strcmp(family, "monospace") || + !strcmp(family, "sans-serif") || + !strcmp(family, "serif")) + return fz_load_html_builtin_font(ctx, set, family, is_bold, is_italic); + + return NULL; +} + +void +fz_add_html_font_face(fz_context *ctx, fz_html_font_set *set, + const char *family, int is_bold, int is_italic, const char *src, + fz_font *font) +{ + fz_html_font_face *custom; + + custom = fz_malloc_struct(ctx, fz_html_font_face); + custom->font = fz_keep_font(ctx, font); + custom->src = fz_strdup(ctx, src); + custom->family = fz_strdup(ctx, family); + custom->is_bold = is_bold; + custom->is_italic = is_italic; + custom->next = set->custom; + set->custom = custom; + + font->fallback = fz_load_html_builtin_font(ctx, set, family, is_bold, is_italic); +} + fz_html_font_set *fz_new_html_font_set(fz_context *ctx) { return fz_malloc_struct(ctx, fz_html_font_set); @@ -60,9 +97,23 @@ fz_html_font_set *fz_new_html_font_set(fz_context *ctx) void fz_drop_html_font_set(fz_context *ctx, fz_html_font_set *set) { + fz_html_font_face *font, *next; int i; + + font = set->custom; + while (font) + { + next = font->next; + fz_drop_font(ctx, font->font); + fz_free(ctx, font->src); + fz_free(ctx, font->family); + fz_free(ctx, font); + font = next; + } + for (i = 0; i < nelem(set->fonts); ++i) fz_drop_font(ctx, set->fonts[i]); fz_drop_font(ctx, set->fallback); + fz_free(ctx, set); } diff --git a/source/html/html-layout.c b/source/html/html-layout.c index 42222bdd..24edc567 100644 --- a/source/html/html-layout.c +++ b/source/html/html-layout.c @@ -1602,6 +1602,8 @@ fz_parse_html(fz_context *ctx, fz_html_font_set *set, fz_archive *zip, const cha // print_rules(css); + fz_add_css_font_faces(ctx, set, zip, base_uri, css); /* load @font-face fonts into font set */ + pool = fz_new_pool(ctx); box = new_box(ctx, pool); |