66 lines
3.2 KiB
Markdown
66 lines
3.2 KiB
Markdown
# jsPDF PDF Generation Pitfalls
|
|
|
|
Common issues when generating PDFs client-side with jsPDF, observed across ShopProQuote quote generation.
|
|
|
|
## 1. Fill Covering Text — Drawing Order
|
|
|
|
**Symptom:** Background boxes (rounded rects) appear as blank gray areas — text that was drawn afterward is invisible.
|
|
|
|
**Root cause:** Calling `doc.roundedRect(x, y, w, h, r, r, 'FD')` (fill + draw) AFTER text has been placed. In PDF, later operations paint on top. The fill covers the text.
|
|
|
|
**Fix:** Split into two operations:
|
|
1. Draw **fill-only** box BEFORE text: `doc.roundedRect(x, y, w, h, r, r, 'F')`
|
|
2. Draw **border-only** box AFTER text: `doc.roundedRect(x, y, w, h, r, r, 'D')`
|
|
|
|
Use a generous fill height initially (wider than content) and trim with the border pass using the actual computed height.
|
|
|
|
```
|
|
fill_height = generous_estimate // e.g. 80pt
|
|
doc.setFillColor(248, 248, 248)
|
|
doc.roundedRect(x, y, w, fill_height, 2, 2, 'F') // BEFORE text
|
|
|
|
// ... draw all text ...
|
|
|
|
actual_height = computed_from_content
|
|
doc.setDrawColor(220, 220, 220)
|
|
doc.roundedRect(x, y, w, actual_height, 2, 2, 'D') // AFTER text
|
|
```
|
|
|
|
## 2. Border Invisibility — Color Contrast
|
|
|
|
**Symptom:** Box border doesn't appear at all, even though `setDrawColor` and `setLineWidth` are called.
|
|
|
|
**Root cause:** Colors that are too close to white — e.g. `setDrawColor(230, 230, 230)` at `setLineWidth(0.5)` on a white page is invisible to the human eye, especially on screen. The fill at `(250, 250, 250)` vs white `(255, 255, 255)` is also nearly imperceptible.
|
|
|
|
**Fix:** Use colors with at least 30-35 points of contrast from background:
|
|
- Fill: `(248, 248, 248)` — visible light gray, not `(250, 250, 250)` or `(251, 251, 251)`
|
|
- Border: `(220, 220, 220)` at 0.5pt — visible but subtle, not `(230, 230, 230)`
|
|
- For print, these values are still very light and professional
|
|
|
|
**Diagnosis:** Check the PDF visually. If you can't clearly see the box boundary, the values are too close to white. Use PIL/pixel analysis to verify:
|
|
```python
|
|
from PIL import Image
|
|
img = Image.open('box.png')
|
|
# Check mid-region pixels — if they're > 248, the fill is invisible
|
|
```
|
|
|
|
## 3. Spacing After Box — `yPos` Drift
|
|
|
|
**Symptom:** Content after a background box overlaps with the box or starts inside it.
|
|
|
|
**Root cause:** After drawing text inside the box, `yPos` is at the last text line. But the box has bottom padding that extends below `yPos`. Adding a fixed margin (`yPos += 15`) doesn't account for the actual padding.
|
|
|
|
**Fix:** Track the box's computed bottom coordinate and position subsequent content relative to it:
|
|
```
|
|
boxBottomY = yPos_after_content + bottom_padding
|
|
nextY = boxBottomY + margin // start next section below box
|
|
```
|
|
|
|
Never use a fixed increment from the last text line — always compute from the box boundary.
|
|
|
|
## 4. Duplicate Variable Declarations in Scope
|
|
|
|
**Symptom:** A variable like `pdfApprovedServices` is declared with `const` in one place, then redeclared further down. Silent bug in JS (temporal dead zone or reference-before-declaration).
|
|
|
|
**Fix:** Declare variables once at the top of the scope. Use `let` for values that need to be computed after other operations. When moving code around, check for duplicate `const`/`let` declarations.
|