mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 16:37:47 +03:00
Allow multiple and local values in src descriptor
This commit is contained in:
parent
fc5eb6c92a
commit
38a0a1a097
@ -52,19 +52,28 @@ def descriptor(descriptor_name=None, wants_base_url=False):
|
|||||||
|
|
||||||
|
|
||||||
@descriptor()
|
@descriptor()
|
||||||
def font_family(tokens):
|
def font_family(tokens, allow_spaces=False):
|
||||||
"""``font-family`` descriptor validation."""
|
"""``font-family`` descriptor validation."""
|
||||||
|
allowed_types = ['IDENT']
|
||||||
|
if allow_spaces:
|
||||||
|
allowed_types.append('S')
|
||||||
if len(tokens) == 1 and tokens[0].type == 'STRING':
|
if len(tokens) == 1 and tokens[0].type == 'STRING':
|
||||||
return tokens[0].value
|
return tokens[0].value
|
||||||
elif tokens and all(token.type == 'IDENT' 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)
|
return ' '.join(
|
||||||
|
token.value for token in tokens if token.type == 'IDENT')
|
||||||
|
|
||||||
|
|
||||||
@descriptor(wants_base_url=True)
|
@descriptor(wants_base_url=True)
|
||||||
@comma_separated_list
|
@comma_separated_list
|
||||||
def src(tokens, base_url):
|
def src(tokens, base_url):
|
||||||
"""``src`` descriptor validation."""
|
"""``src`` descriptor validation."""
|
||||||
|
if len(tokens) <= 2:
|
||||||
token = tokens.pop()
|
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.type == 'URI':
|
||||||
if token.value.startswith('#'):
|
if token.value.startswith('#'):
|
||||||
return 'internal', unquote(token.value[1:])
|
return 'internal', unquote(token.value[1:])
|
||||||
|
@ -15,6 +15,7 @@ from __future__ import division
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pyphen
|
import pyphen
|
||||||
@ -27,6 +28,8 @@ from .logger import LOGGER
|
|||||||
|
|
||||||
ffi = cffi.FFI()
|
ffi = cffi.FFI()
|
||||||
ffi.cdef('''
|
ffi.cdef('''
|
||||||
|
// Pango
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PANGO_STYLE_NORMAL,
|
PANGO_STYLE_NORMAL,
|
||||||
PANGO_STYLE_OBLIQUE,
|
PANGO_STYLE_OBLIQUE,
|
||||||
@ -203,6 +206,18 @@ ffi.cdef('''
|
|||||||
void pango_cairo_show_layout_line (cairo_t *cr, PangoLayoutLine *line);
|
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 unsigned char FcChar8;
|
||||||
typedef int FcBool;
|
typedef int FcBool;
|
||||||
typedef struct _FcConfig FcConfig;
|
typedef struct _FcConfig FcConfig;
|
||||||
@ -211,6 +226,15 @@ ffi.cdef('''
|
|||||||
FcConfig * FcConfigGetCurrent (void);
|
FcConfig * FcConfigGetCurrent (void);
|
||||||
FcBool FcConfigParseAndLoad
|
FcBool FcConfigParseAndLoad
|
||||||
(FcConfig *config, const FcChar8 *file, FcBool complain);
|
(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):
|
def add_font_face(rule_descriptors):
|
||||||
"""Add a font into the Fontconfig application."""
|
"""Add a font into the Fontconfig application."""
|
||||||
if not fontconfig:
|
if not fontconfig or (
|
||||||
LOGGER.warning('@font-face is currently supported only on Linux')
|
sys.platform.startswith('win') or
|
||||||
|
sys.platform.startswith('darwin')):
|
||||||
|
LOGGER.warning(
|
||||||
|
'@font-face is currently not supported on Windows and OSX')
|
||||||
return
|
return
|
||||||
|
for font_type, url in rule_descriptors['src']:
|
||||||
config = fontconfig.FcConfigGetCurrent()
|
config = fontconfig.FcConfigGetCurrent()
|
||||||
for font in rule_descriptors['src']:
|
if font_type in ('external', 'local'):
|
||||||
if font[0] == 'external':
|
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()
|
_, filename = tempfile.mkstemp()
|
||||||
with open(filename, 'wb') as fd:
|
with open(filename, 'wb') as fd:
|
||||||
fd.write(urlopen(font[1]).read())
|
fd.write(font)
|
||||||
else:
|
|
||||||
filename = font[1]
|
|
||||||
xml = '''<?xml version="1.0"?>
|
xml = '''<?xml version="1.0"?>
|
||||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||||
<fontconfig>
|
<fontconfig>
|
||||||
@ -1201,8 +1263,7 @@ def add_font_face(rule_descriptors):
|
|||||||
FONTCONFIG_WEIGHT_CONSTANTS[
|
FONTCONFIG_WEIGHT_CONSTANTS[
|
||||||
rule_descriptors.get('font_weight', 'normal')],
|
rule_descriptors.get('font_weight', 'normal')],
|
||||||
FONTCONFIG_STRETCH_CONSTANTS[
|
FONTCONFIG_STRETCH_CONSTANTS[
|
||||||
rule_descriptors.get('font_stretch', 'normal')],
|
rule_descriptors.get('font_stretch', 'normal')])
|
||||||
)
|
|
||||||
_, conf_filename = tempfile.mkstemp()
|
_, conf_filename = tempfile.mkstemp()
|
||||||
with open(conf_filename, 'wb') as fd:
|
with open(conf_filename, 'wb') as fd:
|
||||||
# TODO: encoding is OK for <test>, but what about <edit>s?
|
# TODO: encoding is OK for <test>, but what about <edit>s?
|
||||||
|
Loading…
Reference in New Issue
Block a user