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:
|
if ft in _ALWAYS_SHOW_PRE:
|
||||||
return True
|
return True
|
||||||
if ft == 'rating' and str(v).isdigit() and int(v) > 0: 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' and v in ('yes','true'): return True
|
||||||
if ft == 'checkbox_group'and isinstance(v, list) and v: 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
|
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.
|
# Build one Table row with cells sized by colSpan.
|
||||||
# Fields with no value are skipped unless they are text/textarea/label —
|
# Fields with no value are skipped unless they are text/textarea/label —
|
||||||
# those always appear so free-text notes are never suppressed.
|
# those always appear so free-text notes are never suppressed.
|
||||||
cells = []
|
# current_col tracks horizontal position (1-based) so gaps between fields
|
||||||
col_widths = []
|
# 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:
|
for field in fields_in_row:
|
||||||
ftype = field.get('type', '')
|
ftype = field.get('type', '')
|
||||||
|
col = field.get('col', current_col)
|
||||||
col_span = field.get('colSpan', 1)
|
col_span = field.get('colSpan', 1)
|
||||||
cell_w = UNIT * col_span
|
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) ─────────────────────
|
# ── Inline label (free-standing text element) ─────────────────────
|
||||||
if ftype == 'label':
|
if ftype == 'label':
|
||||||
fs_map = {'small': 8, 'normal': 9, 'large': 11, 'x-large': 13}
|
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)
|
cells.append(p)
|
||||||
col_widths.append(cell_w)
|
col_widths.append(cell_w)
|
||||||
|
has_content = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
fid = str(field.get('id', ''))
|
fid = str(field.get('id', ''))
|
||||||
@@ -425,29 +461,10 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
|||||||
lbl = field.get('label', '')
|
lbl = field.get('label', '')
|
||||||
|
|
||||||
# ── Skip fields with no meaningful value ──────────────────────────
|
# ── 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):
|
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
|
continue
|
||||||
|
|
||||||
lbl_p = Paragraph(lbl, STYLES['FieldLabel'])
|
lbl_p = Paragraph(lbl, STYLES['FieldLabel'])
|
||||||
@@ -479,7 +496,9 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
|||||||
height=1.0 * inch,
|
height=1.0 * inch,
|
||||||
kind='proportional')
|
kind='proportional')
|
||||||
except Exception:
|
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
|
continue
|
||||||
|
|
||||||
elif ftype == 'signature':
|
elif ftype == 'signature':
|
||||||
@@ -536,9 +555,10 @@ def _form_fields_section(form_fields, form_data, static_folder):
|
|||||||
|
|
||||||
cells.append(box)
|
cells.append(box)
|
||||||
col_widths.append(cell_w)
|
col_widths.append(cell_w)
|
||||||
|
has_content = True
|
||||||
|
|
||||||
# Skip the entire row if nothing made it through the filter
|
# Skip the entire row if no field had meaningful content
|
||||||
if not cells:
|
if not has_content:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row_tbl = Table([cells], colWidths=col_widths, hAlign='LEFT')
|
row_tbl = Table([cells], colWidths=col_widths, hAlign='LEFT')
|
||||||
|
|||||||
Reference in New Issue
Block a user