1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-09-11 20:47:56 +03:00

Handle the appearance property

This commit is contained in:
Guillaume Ayoub 2023-01-23 12:38:12 +01:00
parent b2e4ea0f4d
commit ec1424d979
11 changed files with 78 additions and 39 deletions

View File

@ -743,3 +743,22 @@ All the ``flex-*``, ``align-*``, ``justify-*`` and ``order`` properties are
supported. The ``flex`` and ``flex-flow`` shorthands are supported too.
.. _CSS Flexible Box Layout Module Level 1: https://www.w3.org/TR/css-flexbox-1/
CSS Basic User Interface Module Level 3/4
+++++++++++++++++++++++++++++++++++++++++
The `CSS Basic User Interface Module Level 3/4`_ "enables authors to style user
interface related properties and values."
The ``outline-width``, ``outline-style``, ``outline-color`` properties and the
``outline`` shorthand are supported. The ``outline-offset`` property is **not**
supported.
The ``resize``, ``cursor``, ``caret-*`` and ``nav-*`` properties are **not**
supported.
The ``appearance`` property is supported. When set to ``auto``, it displays
form fields as PDF form fields (supported for text inputs, check boxes and
text areas only).
The ``accent-color`` property is **not** supported.

View File

@ -106,7 +106,9 @@ class HTML:
result, content_language=None)
self.etree_element = self.wrapper_element.etree_element
def _ua_stylesheets(self):
def _ua_stylesheets(self, forms=False):
if forms:
return [HTML5_UA_STYLESHEET, HTML5_UA_FORM_STYLESHEET]
return [HTML5_UA_STYLESHEET]
def _ua_counter_style(self):
@ -117,7 +119,7 @@ class HTML:
def render(self, stylesheets=None, presentational_hints=False,
optimize_size=('fonts',), font_config=None, counter_style=None,
image_cache=None):
image_cache=None, forms=False):
"""Lay out and paginate the document, but do not (yet) export it.
This returns a :class:`document.Document` object which provides
@ -137,12 +139,13 @@ class HTML:
:type counter_style: :class:`css.counters.CounterStyle`
:param counter_style: A dictionary storing ``@counter-style`` rules.
:param dict image_cache: A dictionary used to cache images.
:param bool forms: Whether PDF forms have to be included.
:returns: A :class:`document.Document` object.
"""
return Document._render(
self, stylesheets, presentational_hints,
optimize_size, font_config, counter_style, image_cache)
self, stylesheets, presentational_hints, optimize_size,
font_config, counter_style, image_cache, forms)
def write_pdf(self, target=None, stylesheets=None, zoom=1,
attachments=None, finisher=None, presentational_hints=False,
@ -187,7 +190,7 @@ class HTML:
:param bytes identifier: A bytestring used as PDF file identifier.
:param str variant: A PDF variant name.
:param str version: A PDF version number.
:param bool version: Whether PDF forms have to be included.
:param bool forms: Whether PDF forms have to be included.
:param bool custom_metadata: Whether custom HTML metadata should be
stored in the generated PDF.
:returns:
@ -199,10 +202,10 @@ class HTML:
return (
self.render(
stylesheets, presentational_hints, optimize_size, font_config,
counter_style, image_cache)
counter_style, image_cache, forms)
.write_pdf(
target, zoom, attachments, finisher, identifier, variant,
version, custom_metadata, forms))
version, custom_metadata))
class CSS:
@ -337,5 +340,6 @@ def _select_source(guess=None, filename=None, url=None, file_obj=None,
# Work around circular imports.
from .css import preprocess_stylesheet # noqa isort:skip
from .html import ( # noqa isort:skip
HTML5_UA_COUNTER_STYLE, HTML5_UA_STYLESHEET, HTML5_PH_STYLESHEET)
HTML5_UA_COUNTER_STYLE, HTML5_UA_STYLESHEET, HTML5_UA_FORM_STYLESHEET,
HTML5_PH_STYLESHEET)
from .document import Document, Page # noqa isort:skip

View File

@ -1096,7 +1096,7 @@ def preprocess_stylesheet(device_media_type, base_url, stylesheet_rules,
def get_all_computed_styles(html, user_stylesheets=None,
presentational_hints=False, font_config=None,
counter_style=None, page_rules=None,
target_collector=None):
target_collector=None, forms=False):
"""Compute all the computed styles of all elements in ``html`` document.
Do everything from finding author stylesheets to parsing and applying them.
@ -1112,7 +1112,7 @@ def get_all_computed_styles(html, user_stylesheets=None,
for style in html._ua_counter_style():
for key, value in style.items():
counter_style[key] = value
for sheet in (html._ua_stylesheets() or []):
for sheet in (html._ua_stylesheets(forms) or []):
sheets.append((sheet, 'user agent', None))
if presentational_hints:
for sheet in (html._ph_stylesheets() or []):

View File

@ -0,0 +1,5 @@
/* Default stylesheet for PDF forms */
button, input, select, textarea {
appearance: auto;
}

View File

@ -19,15 +19,12 @@ INITIAL_VALUES = {
'display': ('inline', 'flow'),
'empty_cells': 'show',
'float': 'none',
'height': 'auto',
'left': 'auto',
'line_height': 'normal',
'margin_top': Dimension(0, 'px'),
'margin_right': Dimension(0, 'px'),
'margin_bottom': Dimension(0, 'px'),
'margin_left': Dimension(0, 'px'),
'max_height': Dimension(inf, 'px'), # parsed value for 'none'
'max_width': Dimension(inf, 'px'),
'padding_top': Dimension(0, 'px'),
'padding_right': Dimension(0, 'px'),
'padding_bottom': Dimension(0, 'px'),
@ -39,7 +36,6 @@ INITIAL_VALUES = {
'unicode_bidi': 'normal',
'vertical_align': 'baseline',
'visibility': 'visible',
'width': 'auto',
'z_index': 'auto',
# Backgrounds and Borders 3 (CR): https://www.w3.org/TR/css-backgrounds-3/
@ -159,12 +155,20 @@ INITIAL_VALUES = {
'transform_origin': (Dimension(50, '%'), Dimension(50, '%')),
'transform': (), # computed value for 'none'
# User Interface 3 (REC): https://www.w3.org/TR/css-ui-3/
'box_sizing': 'content-box',
# User Interface 3/4 (REC/WD): https://www.w3.org/TR/css-ui-4/
'appearance': 'none',
'outline_color': 'currentColor', # invert is not supported
'outline_style': 'none',
'outline_width': 3, # computed value for 'medium'
'overflow_wrap': 'normal',
# Sizing 3 (WD): https://www.w3.org/TR/css-sizing-3/
'box_sizing': 'content-box',
'height': 'auto',
'max_height': Dimension(inf, 'px'), # parsed value for 'none'
'max_width': Dimension(inf, 'px'),
'min_height': 'auto',
'min_width': 'auto',
'width': 'auto',
# Flexible Box Layout Module 1 (CR): https://www.w3.org/TR/css-flexbox-1/
'align_content': 'stretch',
@ -176,8 +180,6 @@ INITIAL_VALUES = {
'flex_shrink': 1,
'flex_wrap': 'nowrap',
'justify_content': 'flex-start',
'min_height': 'auto',
'min_width': 'auto',
'order': 0,
# Text Decoration Module 3 (CR): https://www.w3.org/TR/css-text-decor-3/
@ -185,11 +187,12 @@ INITIAL_VALUES = {
'text_decoration_color': 'currentColor',
'text_decoration_style': 'solid',
# Overflow Module 3 (WD): https://www.w3.org/TR/css-overflow-3/
# Overflow Module 3/4 (WD): https://www.w3.org/TR/css-overflow-4/
'block_ellipsis': 'none',
'continue': 'auto',
'max_lines': 'none',
'overflow': 'visible',
'overflow_wrap': 'normal',
'text_overflow': 'clip',
# Lists Module 3 (WD): https://drafts.csswg.org/css-lists-3/

View File

@ -1592,3 +1592,12 @@ def transform(tokens):
else:
return
return tuple(transforms)
@property()
@single_token
def appearance(token):
"""``appearance`` property validation."""
keyword = get_keyword(token)
if keyword in ('none', 'auto'):
return keyword

View File

@ -171,10 +171,9 @@ class Document:
"""
@classmethod
def _build_layout_context(cls, html, stylesheets,
presentational_hints=False,
optimize_size=('fonts',), font_config=None,
counter_style=None, image_cache=None):
def _build_layout_context(cls, html, stylesheets, presentational_hints,
optimize_size, font_config, counter_style,
image_cache, forms):
if font_config is None:
font_config = FontConfiguration()
if counter_style is None:
@ -191,7 +190,7 @@ class Document:
user_stylesheets.append(css)
style_for = get_all_computed_styles(
html, user_stylesheets, presentational_hints, font_config,
counter_style, page_rules, target_collector)
counter_style, page_rules, target_collector, forms)
get_image_from_uri = functools.partial(
original_get_image_from_uri, cache=image_cache,
url_fetcher=html.url_fetcher, optimize_size=optimize_size)
@ -202,9 +201,8 @@ class Document:
return context
@classmethod
def _render(cls, html, stylesheets, presentational_hints=False,
optimize_size=('fonts',), font_config=None, counter_style=None,
image_cache=None):
def _render(cls, html, stylesheets, presentational_hints, optimize_size,
font_config, counter_style, image_cache, forms):
if font_config is None:
font_config = FontConfiguration()
@ -213,7 +211,7 @@ class Document:
context = cls._build_layout_context(
html, stylesheets, presentational_hints, optimize_size,
font_config, counter_style, image_cache)
font_config, counter_style, image_cache, forms)
root_box = build_formatting_structure(
html.etree_element, context.style_for, context.get_image_from_uri,
@ -319,7 +317,7 @@ class Document:
def write_pdf(self, target=None, zoom=1, attachments=None, finisher=None,
identifier=None, variant=None, version=None,
custom_metadata=False, forms=False):
custom_metadata=False):
"""Paint the pages in a PDF file, with metadata.
:type target:
@ -342,7 +340,6 @@ class Document:
:param bytes identifier: A bytestring used as PDF file identifier.
:param str variant: A PDF variant name.
:param str version: A PDF version number.
:param bool version: Whether PDF forms have to be included.
:param bool custom_metadata: A boolean defining whether custom HTML
metadata should be stored in the generated PDF.
:returns:
@ -353,7 +350,7 @@ class Document:
"""
pdf = generate_pdf(
self, target, zoom, attachments, self._optimize_size, identifier,
variant, version, custom_metadata, forms)
variant, version, custom_metadata)
if finisher:
finisher(self, pdf)

View File

@ -310,7 +310,7 @@ class Box:
def is_input(self):
"""Return whether this box is a form input."""
# https://html.spec.whatwg.org/multipage/forms.html#category-submit
if self.element is not None:
if self.style['appearance'] == 'auto' and self.element is not None:
if self.element.tag in ('button', 'input', 'select', 'textarea'):
return not isinstance(self, (LineBox, TextBox))
return False

View File

@ -219,7 +219,7 @@ def element_to_box(element, style_for, get_image_from_uri, base_url,
if not counter_values[name]:
counter_values.pop(name)
box.children = children
box.children = children if style['appearance'] == 'none' else []
process_whitespace(box)
set_content_lists(
element, box, style, counter_values, target_collector, counter_style)

View File

@ -29,9 +29,12 @@ from .urls import get_url_attribute
HTML5_UA_COUNTER_STYLE = CounterStyle()
HTML5_UA = read_text(css, 'html5_ua.css')
HTML5_UA_FORM = read_text(css, 'html5_ua_form.css')
HTML5_PH = read_text(css, 'html5_ph.css')
HTML5_UA_STYLESHEET = CSS(
string=HTML5_UA, counter_style=HTML5_UA_COUNTER_STYLE)
HTML5_UA_FORM_STYLESHEET = CSS(
string=HTML5_UA_FORM, counter_style=HTML5_UA_COUNTER_STYLE)
HTML5_PH_STYLESHEET = CSS(string=HTML5_PH)
# https://html.spec.whatwg.org/multipage/#space-character

View File

@ -101,7 +101,7 @@ def _use_references(pdf, resources, images):
def generate_pdf(document, target, zoom, attachments, optimize_size,
identifier, variant, version, custom_metadata, forms):
identifier, variant, version, custom_metadata):
# 0.75 = 72 PDF point per inch / 96 CSS pixel per inch
scale = zoom * 0.75
@ -176,10 +176,9 @@ def generate_pdf(document, target, zoom, attachments, optimize_size,
add_links(links_and_anchors, matrix, pdf, pdf_page, pdf_names, mark)
add_annotations(
links_and_anchors[0], matrix, document, pdf, pdf_page, annot_files)
if forms:
add_inputs(
page.inputs, matrix, pdf, pdf_page, resources, stream,
document.font_config.font_map)
add_inputs(
page.inputs, matrix, pdf, pdf_page, resources, stream,
document.font_config.font_map)
page.paint(stream, scale=scale)
# Bleed