1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 08:27:22 +03:00

Allow multiple and local values in src descriptor

This commit is contained in:
Guillaume Ayoub 2016-10-01 20:38:33 +02:00
parent fc5eb6c92a
commit 38a0a1a097
2 changed files with 127 additions and 57 deletions

View File

@ -52,19 +52,28 @@ def descriptor(descriptor_name=None, wants_base_url=False):
@descriptor()
def font_family(tokens):
def font_family(tokens, allow_spaces=False):
"""``font-family`` descriptor validation."""
allowed_types = ['IDENT']
if allow_spaces:
allowed_types.append('S')
if len(tokens) == 1 and tokens[0].type == 'STRING':
return tokens[0].value
elif tokens and all(token.type == 'IDENT' for token in tokens):
return ' '.join(token.value for token in tokens)
if tokens and all(token.type in allowed_types for token in tokens):
return ' '.join(
token.value for token in tokens if token.type == 'IDENT')
@descriptor(wants_base_url=True)
@comma_separated_list
def src(tokens, base_url):
"""``src`` descriptor validation."""
if len(tokens) <= 2:
token = tokens.pop()
if token.type == 'FUNCTION' and token.function_name == 'format':
token = tokens.pop()
if token.type == 'FUNCTION' and token.function_name == 'local':
return 'local', font_family(token.content, allow_spaces=True)
if token.type == 'URI':
if token.value.startswith('#'):
return 'internal', unquote(token.value[1:])

View File

@ -15,6 +15,7 @@ from __future__ import division
import os
import re
import sys
import tempfile
import pyphen
@ -27,6 +28,8 @@ from .logger import LOGGER
ffi = cffi.FFI()
ffi.cdef('''
// Pango
typedef enum {
PANGO_STYLE_NORMAL,
PANGO_STYLE_OBLIQUE,
@ -203,6 +206,18 @@ ffi.cdef('''
void pango_cairo_show_layout_line (cairo_t *cr, PangoLayoutLine *line);
// FontConfig
typedef enum _FcResult {
FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId,
FcResultOutOfMemory
} FcResult;
typedef enum _FcMatchKind {
FcMatchPattern, FcMatchFont, FcMatchScan
} FcMatchKind;
typedef struct _FcConfig FcConfig;
typedef struct _FcPattern FcPattern;
typedef unsigned char FcChar8;
typedef int FcBool;
typedef struct _FcConfig FcConfig;
@ -211,6 +226,15 @@ ffi.cdef('''
FcConfig * FcConfigGetCurrent (void);
FcBool FcConfigParseAndLoad
(FcConfig *config, const FcChar8 *file, FcBool complain);
FcPattern * FcPatternCreate (void);
FcPattern * FcFontMatch (FcConfig *config, FcPattern *p);
FcBool FcPatternAddString
(FcPattern *p, const char *object, const FcChar8 *s);
FcBool FcConfigSubstitute
(FcConfig *config, FcPattern *p, FcMatchKind kind);
void FcDefaultSubstitute (FcPattern *pattern);
FcResult FcPatternGetString
(FcPattern *p, const char *object, int n, FcChar8 **s);
''')
@ -1164,17 +1188,55 @@ def show_first_line(context, pango_layout, hinting):
def add_font_face(rule_descriptors):
"""Add a font into the Fontconfig application."""
if not fontconfig:
LOGGER.warning('@font-face is currently supported only on Linux')
if not fontconfig or (
sys.platform.startswith('win') or
sys.platform.startswith('darwin')):
LOGGER.warning(
'@font-face is currently not supported on Windows and OSX')
return
for font_type, url in rule_descriptors['src']:
config = fontconfig.FcConfigGetCurrent()
for font in rule_descriptors['src']:
if font[0] == 'external':
if font_type in ('external', 'local'):
if font_type == 'local':
font_name = url.encode('utf-8')
pattern = fontconfig.FcPatternCreate()
fontconfig.FcConfigSubstitute(
config, pattern, fontconfig.FcMatchFont)
fontconfig.FcDefaultSubstitute(pattern)
fontconfig.FcPatternAddString(
pattern, b'fullname', font_name)
fontconfig.FcPatternAddString(
pattern, b'postscriptname', font_name)
family = ffi.new('FcChar8 **')
postscript = ffi.new('FcChar8 **')
matching_pattern = fontconfig.FcFontMatch(config, pattern)
# TODO: do many fonts have multiple family values?
fontconfig.FcPatternGetString(
matching_pattern, b'fullname', 0, family)
fontconfig.FcPatternGetString(
matching_pattern, b'postscriptname', 0, postscript)
family = ffi.string(family[0])
postscript = ffi.string(postscript[0])
if font_name.lower() in (family.lower(), postscript.lower()):
filename = ffi.new('FcChar8 **')
matching_pattern = fontconfig.FcFontMatch(
ffi.NULL, pattern)
fontconfig.FcPatternGetString(
matching_pattern, b'file', 0, filename)
url = u'file://' + ffi.string(filename[0]).decode('utf-8')
else:
LOGGER.warning(
'Failed to load local font "%s"',
font_name.decode('utf-8'))
continue
try:
font = urlopen(url).read()
except Exception as exc:
LOGGER.warning('Failed to load font at "%s" (%s)', url, exc)
continue
_, filename = tempfile.mkstemp()
with open(filename, 'wb') as fd:
fd.write(urlopen(font[1]).read())
else:
filename = font[1]
fd.write(font)
xml = '''<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
@ -1201,8 +1263,7 @@ def add_font_face(rule_descriptors):
FONTCONFIG_WEIGHT_CONSTANTS[
rule_descriptors.get('font_weight', 'normal')],
FONTCONFIG_STRETCH_CONSTANTS[
rule_descriptors.get('font_stretch', 'normal')],
)
rule_descriptors.get('font_stretch', 'normal')])
_, conf_filename = tempfile.mkstemp()
with open(conf_filename, 'wb') as fd:
# TODO: encoding is OK for <test>, but what about <edit>s?