05/26 Fix exported PDF form layout broken
This commit is contained in:
+47
-27
@@ -351,6 +351,7 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
if ft in _ALWAYS_SHOW_PRE:
|
||||
return True
|
||||
if ft == 'rating' and str(v).isdigit() and int(v) > 0: return True
|
||||
if ft == 'pass_fail' and v: return True
|
||||
if ft == 'checkbox' and v in ('yes','true'): return True
|
||||
if ft == 'checkbox_group'and isinstance(v, list) and v: return True
|
||||
if ft == 'image' and v and os.path.exists(os.path.join(static_folder, v)): return True
|
||||
@@ -398,14 +399,48 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
# Build one Table row with cells sized by colSpan.
|
||||
# Fields with no value are skipped unless they are text/textarea/label —
|
||||
# those always appear so free-text notes are never suppressed.
|
||||
cells = []
|
||||
col_widths = []
|
||||
# current_col tracks horizontal position (1-based) so gaps between fields
|
||||
# are filled with invisible placeholder cells that preserve column alignment.
|
||||
cells = []
|
||||
col_widths = []
|
||||
has_content = False
|
||||
current_col = 1
|
||||
|
||||
# Returns True if the field should be hidden from the PDF.
|
||||
def _skip(ftype, val):
|
||||
if ftype == 'rating':
|
||||
return not str(val).isdigit() or int(val) == 0
|
||||
if ftype == 'image':
|
||||
img_path = os.path.join(static_folder, val) if val else ''
|
||||
return not val or not os.path.exists(img_path)
|
||||
if ftype == 'signature':
|
||||
return not val or not str(val).startswith('data:')
|
||||
if ftype == 'checkbox':
|
||||
# stored as 'true'/'false' by _collect_form_responses
|
||||
return val not in ('yes', 'true')
|
||||
if ftype == 'pass_fail':
|
||||
return not val
|
||||
if ftype == 'checkbox_group':
|
||||
return not val or not isinstance(val, list) or len(val) == 0
|
||||
if ftype == 'table':
|
||||
return not val or not isinstance(val, list) or len(val) == 0
|
||||
# All remaining input types (text, textarea, number, date,
|
||||
# email, radio, select, and any future types)
|
||||
return not val
|
||||
|
||||
for field in fields_in_row:
|
||||
ftype = field.get('type', '')
|
||||
col = field.get('col', current_col)
|
||||
col_span = field.get('colSpan', 1)
|
||||
cell_w = UNIT * col_span
|
||||
|
||||
# Fill horizontal gap before this field with an invisible spacer
|
||||
if col > current_col:
|
||||
gap_w = UNIT * (col - current_col)
|
||||
cells.append(Paragraph('', STYLES['FieldValue']))
|
||||
col_widths.append(gap_w)
|
||||
current_col = col + col_span
|
||||
|
||||
# ── Inline label (free-standing text element) ─────────────────────
|
||||
if ftype == 'label':
|
||||
fs_map = {'small': 8, 'normal': 9, 'large': 11, 'x-large': 13}
|
||||
@@ -418,6 +453,7 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
)
|
||||
cells.append(p)
|
||||
col_widths.append(cell_w)
|
||||
has_content = True
|
||||
continue
|
||||
|
||||
fid = str(field.get('id', ''))
|
||||
@@ -425,29 +461,10 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
lbl = field.get('label', '')
|
||||
|
||||
# ── Skip fields with no meaningful value ──────────────────────────
|
||||
# Returns True if the field should be hidden from the PDF.
|
||||
def _skip(ftype, val):
|
||||
if ftype == 'rating':
|
||||
return not str(val).isdigit() or int(val) == 0
|
||||
if ftype == 'image':
|
||||
img_path = os.path.join(static_folder, val) if val else ''
|
||||
return not val or not os.path.exists(img_path)
|
||||
if ftype == 'signature':
|
||||
return not val or not str(val).startswith('data:')
|
||||
if ftype == 'checkbox':
|
||||
# stored as 'true'/'false' by _collect_form_responses
|
||||
return val not in ('yes', 'true')
|
||||
if ftype == 'pass_fail':
|
||||
return not val
|
||||
if ftype == 'checkbox_group':
|
||||
return not val or not isinstance(val, list) or len(val) == 0
|
||||
if ftype == 'table':
|
||||
return not val or not isinstance(val, list) or len(val) == 0
|
||||
# All remaining input types (text, textarea, number, date,
|
||||
# email, radio, select, and any future types)
|
||||
return not val
|
||||
|
||||
if _skip(ftype, val):
|
||||
# Keep a placeholder so subsequent fields stay in column position
|
||||
cells.append(Paragraph('', STYLES['FieldValue']))
|
||||
col_widths.append(cell_w)
|
||||
continue
|
||||
|
||||
lbl_p = Paragraph(lbl, STYLES['FieldLabel'])
|
||||
@@ -479,7 +496,9 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
height=1.0 * inch,
|
||||
kind='proportional')
|
||||
except Exception:
|
||||
# Could not load image — skip this cell entirely
|
||||
# Could not load image — insert placeholder to preserve column position
|
||||
cells.append(Paragraph('', STYLES['FieldValue']))
|
||||
col_widths.append(cell_w)
|
||||
continue
|
||||
|
||||
elif ftype == 'signature':
|
||||
@@ -536,9 +555,10 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
||||
|
||||
cells.append(box)
|
||||
col_widths.append(cell_w)
|
||||
has_content = True
|
||||
|
||||
# Skip the entire row if nothing made it through the filter
|
||||
if not cells:
|
||||
# Skip the entire row if no field had meaningful content
|
||||
if not has_content:
|
||||
continue
|
||||
|
||||
row_tbl = Table([cells], colWidths=col_widths, hAlign='LEFT')
|
||||
|
||||
Reference in New Issue
Block a user