Skip to content

Commit

Permalink
Clean and simplify PDF form generation
Browse files Browse the repository at this point in the history
  • Loading branch information
liZe committed Jul 21, 2024
1 parent 7b85115 commit e438d22
Showing 1 changed file with 43 additions and 66 deletions.
109 changes: 43 additions & 66 deletions weasyprint/pdf/anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
font_size = style['font_size'] * 0.75
field_stream = pydyf.Stream(compress=compress)
field_stream.set_color_rgb(*style['color'][:3])
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]:
Expand All @@ -149,7 +158,7 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
else:
character = '4' # Check character in Dingbats

# Create stream when input is checked
# Create stream when input is checked.
width = rectangle[2] - rectangle[0]
height = rectangle[1] - rectangle[3]
checked_stream = pydyf.Stream(extra={
Expand All @@ -162,7 +171,7 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
checked_stream.begin_text()
checked_stream.set_color_rgb(*style['color'][:3])
checked_stream.set_font_size('ZaDb', font_size)
# Center (let’s assume that Dingbat’s characters have a 0.75em 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)
Expand All @@ -171,22 +180,16 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
checked_stream.pop_state()
pdf.add_object(checked_stream)

checked = 'checked' in element.attrib
field_stream.set_font_size('ZaDb', font_size)

checked = 'checked' in element.attrib
key = len(group['Kids']) if input_type == 'radio' else 'on'
field = pydyf.Dictionary({
'Type': '/Annot',
'Subtype': '/Widget',
'Rect': pydyf.Array(rectangle),
'FT': '/Btn',
'F': 1 << (3 - 1), # Print flag
'P': page.reference,
'AS': f'/{key}' if checked else '/Off',
'AP': pydyf.Dictionary({'N': pydyf.Dictionary({
key: checked_stream.reference})}),
'MK': pydyf.Dictionary({'CA': pydyf.String(character)}),
'DA': pydyf.String(b' '.join(field_stream.stream)),
})
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
Expand All @@ -197,8 +200,8 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
else:
field['T'] = pydyf.String(input_name)
field['V'] = field['AS']

elif element.tag == 'select':
# Select fields
font_description = get_font_description(style)
font = pango.pango_font_map_load_font(
font_map, context, font_description)
Expand All @@ -215,17 +218,9 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
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',
})
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)
Expand All @@ -235,64 +230,46 @@ def add_forms(forms, matrix, pdf, page, resources, stream, font_map,
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
field = pydyf.Dictionary({
'Type': '/Annot',
'Subtype': '/Widget',
'Rect': pydyf.Array(rectangle),
'FT': '/Btn',
'T': pydyf.String(input_name),
'F': 1 << (3 - 1), # Print flag
'Ff': 1 << (17 - 1), # Push-button
'P': page.reference,
'DA': pydyf.String(b' '.join(field_stream.stream)),
'V': pydyf.String(form.attrib.get('value', '')),
'A': pydyf.Dictionary({
'Type': '/Action',
'S': '/SubmitForm',
'F': pydyf.String(form.attrib.get('action')),
'Fields': pydyf.Array((
field.reference for field in forms[form].values())),
'Flags': flags,
}),
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 unknown
# 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)
value = (
element.text if element.tag == 'textarea'
else element.attrib.get('value', ''))
field = pydyf.Dictionary({
'Type': '/Annot',
'Subtype': '/Widget',
'Rect': pydyf.Array(rectangle),
'FT': '/Tx',
'F': 1 << (3 - 1), # Print flag
'P': page.reference,
'T': pydyf.String(input_name),
'V': pydyf.String(value or ''),
'DA': pydyf.String(b' '.join(field_stream.stream)),
})
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)

maxlength = element.get('maxlength')
if maxlength and maxlength.isdigit():
field['MaxLen'] = element.get('maxlength')
if (max_length := element.get('maxlength', '')).isdigit():
field['MaxLen'] = max_length
pdf.add_object(field)

page['Annots'].append(field.reference)
Expand Down

0 comments on commit e438d22

Please sign in to comment.