diff --git a/docs/api_reference.rst b/docs/api_reference.rst
index 1e11c758e..377c21566 100644
--- a/docs/api_reference.rst
+++ b/docs/api_reference.rst
@@ -765,7 +765,7 @@ 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).
+form fields as PDF form fields (supported for text inputs, check boxes, text
+areas, and select only).
The ``accent-color`` property is **not** supported.
diff --git a/tests/test_api.py b/tests/test_api.py
index 588731ac8..16def4b69 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -487,12 +487,21 @@ def test_partial_pdf_custom_metadata():
(b'', [b'/Tx', b'/V ()']),
(b'', [b'/Btn']),
(b'', [b'/Tx', b'/V ()']),
+ (b'', [b'/Ch', b'/Opt']),
+ (b'', [b'/Ch', b'/Opt', b'/V (b)']),
+ (b'', [b'/Ch', b'/Opt', b'[(b) (c)]']),
))
def test_pdf_inputs(html, fields):
stdout = _run('--pdf-forms --uncompressed-pdf - -', html)
assert b'AcroForm' in stdout
- for field in fields:
- assert field in stdout
+ assert all(field in stdout for field in fields)
stdout = _run('--uncompressed-pdf - -', html)
assert b'AcroForm' not in stdout
diff --git a/weasyprint/css/html5_ua.css b/weasyprint/css/html5_ua.css
index 96443ef54..9bc5b0c9e 100644
--- a/weasyprint/css/html5_ua.css
+++ b/weasyprint/css/html5_ua.css
@@ -363,23 +363,29 @@ input[value=""]::before {
select {
background: lightgrey;
border-radius: 0.25em 0.25em;
- padding-right: 1.5em;
position: relative;
white-space: normal;
}
-select::before {
+select[multiple] {
+ height: 3.6em;
+}
+select:not([multiple])::before {
content: "˅";
position: absolute;
right: 0;
text-align: center;
width: 1.5em;
}
-option {
- display: none;
+select option {
+ padding-right: 1.5em;
white-space: nowrap;
}
+select:not([multiple]) option {
+ display: none;
+}
+select[multiple] option,
select:not(:has(option[selected])) option:first-of-type,
-option[selected]:not(option[selected] ~ option[selected]) {
+select option[selected]:not(option[selected] ~ option[selected]) {
display: block;
overflow: hidden;
}
diff --git a/weasyprint/pdf/anchors.py b/weasyprint/pdf/anchors.py
index 58e03d057..ac489957b 100644
--- a/weasyprint/pdf/anchors.py
+++ b/weasyprint/pdf/anchors.py
@@ -150,7 +150,7 @@ def add_inputs(inputs, matrix, pdf, page, resources, stream, font_map,
'Subtype': '/Widget',
'Rect': pydyf.Array(rectangle),
'FT': '/Btn',
- 'F': 2 ** (3 - 1), # Print flag
+ 'F': 1 << (3 - 1), # Print flag
'P': page.reference,
'T': pydyf.String(input_name),
'V': '/Yes' if checked else '/Off',
@@ -160,6 +160,43 @@ def add_inputs(inputs, matrix, pdf, page, resources, stream, font_map,
'AS': '/Yes' if checked else '/Off',
'DA': pydyf.String(b' '.join(field_stream.stream)),
})
+ elif element.tag == 'select':
+ # Select fields
+ font_description = get_font_description(style)
+ font = pango.pango_font_map_load_font(
+ font_map, context, font_description)
+ font = stream.add_font(font)
+ font.used_in_forms = True
+
+ field_stream.set_font_size(font.hash, font_size)
+ options = []
+ selected_values = []
+ for option in element:
+ value = pydyf.String(option.attrib.get('value', ''))
+ text = pydyf.String(option.text)
+ options.append(pydyf.Array([value, text]))
+ if 'selected' in option.attrib:
+ selected_values.append(value)
+
+ field = pydyf.Dictionary({
+ 'DA': pydyf.String(b' '.join(field_stream.stream)),
+ 'F': 1 << (3 - 1), # Print flag
+ 'FT': '/Ch',
+ 'Opt': pydyf.Array(options),
+ 'P': page.reference,
+ 'Rect': pydyf.Array(rectangle),
+ 'Subtype': '/Widget',
+ 'T': pydyf.String(input_name),
+ 'Type': '/Annot',
+ })
+ if 'multiple' in element.attrib:
+ field['Ff'] = 1 << (22 - 1)
+ field['V'] = pydyf.Array(selected_values)
+ else:
+ field['Ff'] = 1 << (18 - 1)
+ field['V'] = (
+ selected_values[-1] if selected_values
+ else pydyf.String(''))
else:
# Text, password, textarea, files, and unknown
font_description = get_font_description(style)
@@ -177,24 +214,18 @@ def add_inputs(inputs, matrix, pdf, page, resources, stream, font_map,
'Subtype': '/Widget',
'Rect': pydyf.Array(rectangle),
'FT': '/Tx',
- 'F': 2 ** (3 - 1), # Print flag
+ 'F': 1 << (3 - 1), # Print flag
'P': page.reference,
'T': pydyf.String(input_name),
- # Previously if the input had no value or the value was an
- # empty string, the V key was filled with a pydyf.String(None)
- # object. This caused the PDF input/textarea to be filled with
- # the string "None". Now if the input has no value or the
- # value is an empty string, the V key is filled with a
- # pydyf.String('') object.
'V': pydyf.String(value or ''),
'DA': pydyf.String(b' '.join(field_stream.stream)),
})
if element.tag == 'textarea':
- field['Ff'] = 2 ** (13 - 1)
+ field['Ff'] = 1 << (13 - 1)
elif input_type == 'password':
- field['Ff'] = 2 ** (14 - 1)
+ field['Ff'] = 1 << (14 - 1)
elif input_type == 'file':
- field['Ff'] = 2 ** (21 - 1)
+ field['Ff'] = 1 << (21 - 1)
pdf.add_object(field)
page['Annots'].append(field.reference)