summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2016-01-22 12:00:55 +0100
committerTor Andersson <tor.andersson@artifex.com>2016-01-22 12:00:55 +0100
commitf501211e904d2c01e47b03e774ffc6f93faeae25 (patch)
treeaa13c9d28c17e3dbb73459ccf7c6a8ca6c3906e4
parentb31c7d1559125b5a0c596e52d632c70e48c506cf (diff)
downloadmupdf-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.h20
-rw-r--r--source/fitz/font.c2
-rw-r--r--source/html/css-apply.c119
-rw-r--r--source/html/css-parse.c21
-rw-r--r--source/html/html-font.c71
-rw-r--r--source/html/html-layout.c2
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);