def add_forms(forms, matrix, pdf, page, resources, stream, font_map):
    """Include form inputs in PDF."""
    if not forms or not any(forms.values()):
        return

    if 'Annots' not in page:
        page['Annots'] = pydyf.Array()
    if 'AcroForm' not in pdf.catalog:
        pdf.catalog['AcroForm'] = pydyf.Dictionary({
            'Fields': pydyf.Array(),
            'DR': resources.reference,
            'NeedAppearances': 'true',
        })
    page_reference = page['Contents'].split()[0]
    context = ffi.gc(
        pango.pango_font_map_create_context(font_map),
        gobject.g_object_unref)
    inputs_with_forms = [
        (form, element, style, rectangle)
        for form, inputs in forms.items()
        for element, style, rectangle in inputs
    ]
    radio_groups = collections.defaultdict(dict)
    forms = collections.defaultdict(dict)
    for i, (form, element, style, rectangle) in enumerate(inputs_with_forms):
        rectangle = (
            *matrix.transform_point(*rectangle[:2]),
            *matrix.transform_point(*rectangle[2:]))

        input_type = element.attrib.get('type')
        input_value = element.attrib.get('value', 'Yes')
        default_name = f'unknown-{page_reference.decode()}-{i}'
        input_name = element.attrib.get('name', default_name)
        # TODO: where does this 0.75 scale come from?
        font_size = style['font_size'] * 0.75
        field_stream = stream.clone()
        field_stream.set_color(style['color'])
        field = pydyf.Dictionary({
            'Type': '/Annot',
            'Subtype': '/Widget',
            'Rect': pydyf.Array(rectangle),
            'P': page.reference,
            'F': 1 << (3 - 1),  # Print flag
            'T': pydyf.String(input_name),
        })
        if input_type in ('radio', 'checkbox'):
            if input_type == 'radio':
                if input_name not in radio_groups[form]:
                    radio_groups[form][input_name] = group = pydyf.Dictionary({
                        'FT': '/Btn',
                        'Ff': (1 << (15 - 1)) + (1 << (16 - 1)),  # NoToggle & Radio
                        'T': pydyf.String(input_name),
                        'V': '/Off',
                        'Kids': pydyf.Array(),
                        'Opt': pydyf.Array(),
                    })
                    pdf.add_object(group)
                    pdf.catalog['AcroForm']['Fields'].append(group.reference)
                group = radio_groups[form][input_name]
                font_size = style['font_size'] * 0.5
                character = 'l'  # Disc character in Dingbats
            else:
                character = '4'  # Check character in Dingbats

            # Create stream when input is checked.
            width = rectangle[2] - rectangle[0]
            height = rectangle[1] - rectangle[3]
            checked_stream = stream.clone(extra={
                'Resources': resources.reference,
                'Type': '/XObject',
                'Subtype': '/Form',
                'BBox': pydyf.Array((0, 0, width, height)),
            })
            checked_stream.push_state()
            checked_stream.begin_text()
            checked_stream.set_color(style['color'])
            checked_stream.set_font_size('ZaDb', font_size)
            # Center (assuming that Dingbat’s characters have a 0.75em size).
            x = (width - font_size * 0.75) / 2
            y = (height - font_size * 0.75) / 2
            checked_stream.move_text_to(x, y)
            checked_stream.show_text_string(character)
            checked_stream.end_text()
            checked_stream.pop_state()
            pdf.add_object(checked_stream)

            field_stream.set_font_size('ZaDb', font_size)

            checked = 'checked' in element.attrib
            key = len(group['Kids']) if input_type == 'radio' else 'on'
            appearance = pydyf.Dictionary({key: checked_stream.reference})
            field['FT'] = '/Btn'
            field['DA'] = pydyf.String(b' '.join(field_stream.stream))
            field['AS'] = f'/{key}' if checked else '/Off'
            field['AP'] = pydyf.Dictionary({'N': appearance})
            field['MK'] = pydyf.Dictionary({'CA': pydyf.String(character)})
            pdf.add_object(field)
            if input_type == 'radio':
                field['Parent'] = group.reference
                if checked:
                    group['V'] = f'/{key}'
                group['Kids'].append(field.reference)
                group['Opt'].append(pydyf.String(input_value))
            else:
                field['T'] = pydyf.String(input_name)
                field['V'] = field['AS']

        elif element.tag == 'select':
            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 or '')
                options.append(pydyf.Array([value, text]))
                if 'selected' in option.attrib:
                    selected_values.append(value)

            field['FT'] = '/Ch'
            field['DA'] = pydyf.String(b' '.join(field_stream.stream))
            field['Opt'] = pydyf.Array(options)
            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(''))
            pdf.add_object(field)

        elif input_type == 'submit' or element.tag == 'button':
            flags = 1 << (3 - 1)  # HTML form format
            if form.attrib.get('method', '').lower() != 'post':
                flags += 1 << (4 - 1)  # GET method
            fields = pydyf.Array((field.reference for field in forms[form].values()))
            field['FT'] = '/Btn'
            field['DA'] = pydyf.String(b' '.join(field_stream.stream))
            field['V'] = pydyf.String(form.attrib.get('value', ''))
            field['Ff'] = 1 << (17 - 1)  # Push-button
            field['A'] = pydyf.Dictionary({
                'Type': '/Action',
                'S': '/SubmitForm',
                'F': pydyf.String(form.attrib.get('action')),
                'Fields': fields,
                'Flags': flags,
            })
            pdf.add_object(field)

        else:
            # Text, password, textarea, files, and other unknown 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)
            field['FT'] = '/Tx'
            field['DA'] = pydyf.String(b' '.join(field_stream.stream))
            field['V'] = pydyf.String(element.attrib.get('value', ''))
            if element.tag == 'textarea':
                field['Ff'] = 1 << (13 - 1)
                field['V'] = pydyf.String(element.text or '')
            elif input_type == 'password':
                field['Ff'] = 1 << (14 - 1)
            elif input_type == 'file':
                field['Ff'] = 1 << (21 - 1)
            if (max_length := element.get('maxlength', '')).isdigit():
                field['MaxLen'] = max_length
            pdf.add_object(field)

        page['Annots'].append(field.reference)
        pdf.catalog['AcroForm']['Fields'].append(field.reference)
        if input_name not in forms:
            forms[form][input_name] = field
Include form inputs in PDF.
weasyprint/pdf/anchors.py