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 640400462..0ca986c80 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -482,15 +482,23 @@ def test_partial_pdf_custom_metadata(): assert b'value' in stdout -@pytest.mark.parametrize('html, field', ( - (b'', b'/Tx'), - (b'', b'/Btn'), - (b'', b'/Tx'), +@pytest.mark.parametrize('html, fields', ( + (b'', [b'/Tx']), + (b'', [b'/Btn']), + (b'', [b'/Tx']), + (b'', [b'/Ch', b'/Opt']), + # The selected values will be (b) and (c) in the PDF. + (b'' + , [b'/Ch', b'/Opt', b'[(b) (c)]']), )) -def test_pdf_inputs(html, field): +def test_pdf_inputs(html, fields): stdout = _run('--pdf-forms --uncompressed-pdf - -', html) assert b'AcroForm' in stdout - 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/layout/block.py b/weasyprint/layout/block.py index 2e1dc9958..fb39301f4 100644 --- a/weasyprint/layout/block.py +++ b/weasyprint/layout/block.py @@ -833,6 +833,18 @@ def block_container_layout(context, box, bottom_space, skip_stack, if next_page['page'] is None: next_page['page'] = new_box.page_values()[1] + if ( + box.element and + box.element.tag == 'select' and + box.element.attrib.get('multiple') is not None and + not box.element.attrib.get('height') and + not box.element.attrib.get('max-height') + ): + # Overriding the height when the select has the multiple attribute + # and no height attribute + options_number = len([child for child in box.element]) + new_box.height = min(box.height * 4, box.height * options_number) + return ( new_box, resume_at, next_page, adjoining_margins, collapsing_through, max_lines) diff --git a/weasyprint/pdf/anchors.py b/weasyprint/pdf/anchors.py index 269ed7b87..8105c200c 100644 --- a/weasyprint/pdf/anchors.py +++ b/weasyprint/pdf/anchors.py @@ -160,6 +160,75 @@ 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": + # Text, password, textarea, files, and unknown + 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) + multiple = element.attrib.get("multiple") is not None + options = [] + selected_values = [] + for option in element: + options.append( + pydyf.Array( + [f'({option.attrib.get("value")})', f'({option.text})'] + ) + ) + if option.attrib.get("selected") is not None: + selected_values.append( + pydyf.String(option.attrib.get("value")) + ) + + if multiple: + field = pydyf.Dictionary({ + 'DA': pydyf.String(b' '.join(field_stream.stream)), + 'F': 2 ** (3 - 1), # Print flag + 'FT': '/Ch', + 'Ff': 2097152, + 'I': pydyf.Array([3]), + 'M': "(D:20231104123906+00'00')", + 'MK': pydyf.Dictionary({ + 'BC': pydyf.Array([ + 0.627451, 0.627451, 0.643137 + ]), + 'R': 0, + }), + 'Opt': pydyf.Array(options), + 'P': page.reference, + 'Rect': pydyf.Array(rectangle), + 'Subtype': '/Widget', + 'T': pydyf.String(input_name), + 'Type': '/Annot', + 'V': pydyf.Array(selected_values) + if len(selected_values) > 1 + else pydyf.String(element.attrib.get('value', '')), + }) + else: + field = pydyf.Dictionary({ + 'DA': pydyf.String(b' '.join(field_stream.stream)), + 'F': 2 ** (3 - 1), # Print flag + 'FT': '/Ch', + 'Ff': 131072, + 'I': pydyf.Array([0]), + 'M': "(D:20231103110832+00'00')", + 'MK': pydyf.Dictionary({ + 'BC': pydyf.Array([ + 0.627451, 0.627451, 0.643137 + ]), + 'R': 0, + }), + 'Opt': pydyf.Array(options), + 'P': page.reference, + 'Rect': pydyf.Array(rectangle), + 'Subtype': '/Widget', + 'T': pydyf.String(input_name), + 'Type': '/Annot', + 'V': pydyf.String(element.attrib.get('value', '')), + }) else: # Text, password, textarea, files, and unknown font_description = get_font_description(style)