Marble uses a flat-to-gradient band in place of the marble photo and bundles Lustria (OFL). Both logo slots are opt-in. Quirk 13 documents a fontkit glyph-cache issue that the tests work around with warm-up renders; the engine fix follows separately.
174 lines
20 KiB
Markdown
174 lines
20 KiB
Markdown
# Template 2: Highlighter (`highlighter`)
|
|
|
|
Source: `templates/reference/2.pdf`. All coordinates are in points from the top-left of the reference page (595.5 x 842.25). Measured from the PDF content streams during design analysis.
|
|
|
|
## Style
|
|
|
|
Bold form-style invoice. Tight-tracked Poppins Bold 'Invoice', underlined fill-in fields, a signal-yellow header bar, a heavy 3pt black-framed grid table with indigo hairlines, a yellow 'total' cell, and a THANK YOU sign-off with icon-led contact lines.
|
|
|
|
## Fonts
|
|
|
|
All OFL; bundle as static TTF.
|
|
1) Poppins Bold ('Copyright 2020 The Poppins Project Authors', v4.004). upm 1000, hhea 1050 / -350 / lineGap 100, capHeight 705. Used for:
|
|
- 'Invoice': 45.68pt, letterSpacing -2.83pt (-0.062em), #1C2120
|
|
- Table heads 'Description / Qty / Price / Total': 13pt, tracking 0, #1C2120
|
|
- 'total :': 12pt, #000
|
|
- 'Bank Name : ...' and 'Account No : ...': 12pt, #1C2120, pitch 16.5 (lineHeight 1.375)
|
|
- 'THANK YOU': 25.01pt, #1C2120
|
|
2) Poppins Regular (capHeight 698). Used for the field labels 'Date:', 'Invoice number:', 'Billed to:' at 13pt with -0.37pt (-0.028em), and 'Payment Method:' at 13pt with tracking 0.
|
|
3) Montserrat Regular ('Copyright 2011 The Montserrat Project Authors', v7.200). upm 1000, hhea 968 / -251, capHeight 700. Used for the contact lines at 11.67pt, #000, pitch 22.5 (lineHeight 1.93).
|
|
The template has no item text (rows are empty); suggest Poppins Regular 11pt #1C2120.
|
|
Rupee sign: present in Montserrat (verified in the local TTF cmap). Poppins is likely to have it (ITF Devanagari family) but this is unverified, so use fontFamily ['Poppins','IBM Plex Sans'] for money.
|
|
|
|
## Colours
|
|
|
|
#1C2120 near-black ink: title, labels, header text, 3pt table frames, icons, THANK YOU.
|
|
#423869 deep indigo hairlines, 0.75pt: field underlines, table grid, the 4 lines at bottom right.
|
|
#FFE535 signal yellow: table header bar and the 'total :' cell.
|
|
#000000: contact text and the 'total :' label.
|
|
#FFFFFF: page and table body.
|
|
Expose #FFE535 as a swappable accent token.
|
|
|
|
## Layout
|
|
|
|
Coordinates are pt from the top-left; y is the baseline unless noted. Content box x 59.6 to 536.0 (about 60pt side margins).
|
|
1. Title 'Invoice' at x 60.3, baseline 98.0 (cap top about 65.8). The right half (x 352 to 536, y 50 to 114) is empty.
|
|
2. Field stack, labels at x 60.3-61.8 in Poppins Regular 13:
|
|
- 'Date:' baseline 132.2; underline y 137.4, x 97.6 to 266.0 (168.3 wide)
|
|
- 'Invoice number:' baseline 166.0; underline y 170.4, x 165.5 to 266.0 (100.5 wide)
|
|
- 'Billed to:' baseline 200.4; underline y 204.8, x 115.8 to 266.0 (150.1 wide)
|
|
Pitch 34. Each underline sits about 4.6pt below the baseline and starts 4-6pt after the label.
|
|
3. Right block at x 352.5, left-aligned: 'Payment Method:' baseline 162.9; 'Bank Name : Wealth Banking' 185.1; 'Account No : 123 4567 8901' 201.6.
|
|
4. Header bar: y 269.7 to 305.1 (h 35.4), x 59.6 to 536.0. The stroke is centred, so the outer edge runs 58.1 to 537.5. Fill #FFE535, 3pt #1C2120 frame, dividers 0.75 #423869 at x 245.5 / 309.4 / 412.2. Labels are centred on baseline 290.9: Qty centre 277.4, Price 360.8, Total 471.7, 'Description' 144.2.
|
|
5. Gap of 7.5.
|
|
6. Body: y 312.6 to 559.9 (h 247.3), same x, white, 3pt frame. Full-height dividers at x 245.5 / 309.4 / 412.2. Row rules at y 343.8, 375.3, 405.3, 436.8, 466.8, 498.3, 528.3, giving 8 rows of about 30.9.
|
|
7. Gap of 7.5, then the totals row at y 567.4 to 595.5 (h 28.1): a yellow cell at x 309.4 to 412.2 ('total :' baseline 585.2, x 333.8-368.8) plus a white value cell at x 411.0 to 535.3. Each cell has a 3pt frame.
|
|
8. Gap from 595 to 670.
|
|
9. Sign-off, left: 'THANK YOU' at x 61.8, baseline 695.0. Contact rows have an icon column at x 59.6-74.3 and text at x 81.9, baselines 732.6 / 755.1 / 777.6.
|
|
10. Right: 4 rules, 0.75 #423869, x about 323.7 to 536.0 (about 212 long), at y 694.0 / 723.3 / 752.5 / 781.5 (pitch about 29.2).
|
|
About 64pt of whitespace below the last baseline.
|
|
|
|
## Items table
|
|
|
|
Outer width 476.4 (x 59.6 to 536.0). Columns:
|
|
- Description 59.6-245.5 (185.9, 39.0%)
|
|
- Qty 245.5-309.4 (63.9, 13.4%)
|
|
- Price 309.4-412.2 (102.8, 21.6%)
|
|
- Total 412.2-536.0 (123.8, 26.0%)
|
|
Header: a separate bar, 7.5pt above the body. #FFE535 fill, 3pt #1C2120 frame, 35.4 tall, Poppins Bold 13 #1C2120 labels centred, internal dividers 0.75 #423869. 'Description' measures 144.2 at its centre, about 8pt left of the column centre; just centre it.
|
|
Body: white, 3pt #1C2120 outer frame, 0.75 #423869 grid lines both ways, 8 empty rows of about 30.9 (31.1 / 31.5 / 30.0 / 31.5 / 30.0 / 31.5 / 30.0 / 31.6). The reference file is a blank fill-in form.
|
|
Suggested cells: Poppins Regular 11 #1C2120. Description left with 9pt padding; Qty centred; Rate and Amount right-aligned with 9pt right padding (reference only centres the headers).
|
|
Option: 'pad to N empty rows' (default 8 on A4, 7 on Letter) keeps the form look when there are few items.
|
|
|
|
## Totals and payment
|
|
|
|
reference:
|
|
- One 'total :' row: a yellow label cell under the Price column and a white value cell under the Total column, 28.1pt tall, 3pt frames, 7.5pt below the body. The label is Poppins Bold 12 #000; the value cell is empty.
|
|
- Payment, top right: 'Payment Method:' (Regular 13), then 'Bank Name : ...' and 'Account No : ...' (Bold 12, label : value inline).
|
|
- Sign-off: 'THANK YOU' (Bold 25) with icon-led phone / email / address rows (Montserrat 11.67).
|
|
- 4 unlabelled ruled lines at bottom right, for notes or a signature.
|
|
- No subtotal or tax rows, no amount in words, no notes label, no signature caption.
|
|
Voiced version:
|
|
- Build the totals as a stack of the same 2-cell row (label cell at the Price column x, value cell at the Total column x), 24-28pt per row. Merge the frames: 3pt outside, 0.75 #423869 between rows.
|
|
- Rows: Subtotal, Discount (if > 0), CGST @x% + SGST @x% or IGST @x%, then Total. Total keeps the yellow label cell with a bold value; the value cell can also go yellow for emphasis.
|
|
- Amount in words to the left of the stack: x 60 to 300, Poppins Regular 10.5, top-aligned with the first totals row.
|
|
|
|
## Decoration and images
|
|
|
|
No raster images (41 KB, all vector). Everything can be rebuilt in react-pdf:
|
|
- Filled rects: header bar #FFE535 476.4 x 35.4; total label cell #FFE535 102.8 x 28.1.
|
|
- 3pt #1C2120 frames on the header bar, the body and both total cells. PDF strokes are centred on the edge (1.5pt outside), while react-pdf borders sit inside the box, so place those Views at the outer edge (x 58.1, width 479.4).
|
|
- 0.75pt #423869 hairlines: 3 field underlines, 3+3 column dividers, 7 row rules and the 4 bottom-right lines.
|
|
- 3 icons, fill #1C2120, drawn with <Svg><Path>:
|
|
- Phone: bbox x 59.6, y 721.9, 14.75 x 14.75. Native path (about 36 units, y-down), viewBox '0 0 36 36', drawn at 14.75pt:
|
|
'M34.06 26.407L30.564 22.911C28.634 20.981 25.504 20.981 23.575 22.911C22.856 23.629 22.408 24.514 22.224 25.439C16.459 24.361 10.852 18.777 10.503 13.786C11.45 13.61 12.357 13.159 13.089 12.426C15.019 10.496 15.019 7.366 13.089 5.436L9.594 1.94C7.664 0.01 4.534 0.01 2.604 1.94C-7.882 12.426 23.574 43.882 34.06 33.396C35.989 31.467 35.989 28.337 34.06 26.407Z'
|
|
- Mail: this is Material Icons 'mail' (Apache-2.0), viewBox 0 0 48 48. Drawn area 11.58 x 9.26 at x 62.5, y 746.9, so the 48-unit box is about 13.9pt:
|
|
'M40 8H8c-2.21 0-3.98 1.79-3.98 4L4 36c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4zm0 8L24 26 8 16v-4l16 10 16-10v4z'
|
|
- Pin: this is Material Icons 'place' (Apache-2.0), viewBox 0 0 48 48. Drawn area 8.46 x 12.09 at x 64.05, y 766.4, so the 48-unit box is about 14.5pt:
|
|
'M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z'
|
|
The icons' visual centres sit about 4pt above each text baseline (729.3 / 751.5 / 772.5).
|
|
Alternative: the Carbon icons Phone / Email / Location (Apache-2.0; @carbon/icons is already a dependency) would keep one icon set across the app.
|
|
|
|
## Mapping to Voiced fields
|
|
|
|
- 'Invoice': 'Invoice' or 'Tax Invoice'. 'Tax Invoice' is about 237pt at 45.68pt and fits the left half.
|
|
- Empty top right (x 352 to 536, y 50 to 114): vendor logo (3:1, fit 150x50, right-aligned) plus vendorName (Poppins Bold 13), with 'GSTIN ... / PAN ...' (Poppins Regular 10) under it.
|
|
- 'Date:': invoiceDate. Add a 'Due date:' field in the same underline style (pitch 34) for dueDate.
|
|
- 'Invoice number:': number.
|
|
- 'Billed to:': clientName on the line. Sub-lines underneath at x 60.3, Poppins Regular 11, pitch 15: clientAddress, 'GSTIN: ...', 'Place of supply: ...', 'PO: ...'.
|
|
- 'Payment Method:': 'Bank Name : bankName (branch)', 'Account Name : accountName', 'Account No : accountNo', 'IFSC : ifsc', 'UPI : ...' (planned).
|
|
- Table to items: Description / Qty (with unit such as 'hr' or 'session') / Price becomes Rate (a dash when mode is fixed) / Total becomes Amount.
|
|
- 'total :': the Subtotal / Discount / CGST / SGST or IGST / Total stack.
|
|
- 'THANK YOU': keep as a static string. The icon rows take vendorPhone / vendorEmail / vendorAddress.
|
|
- The 4 ruled lines:
|
|
- Lines 1-3: notes written on the lines (Poppins Regular 10.5, baseline about 3pt above each rule), or the amount in words if there are no notes.
|
|
- Line 4: the signature line. Put the signature image above it (140x40 max) with the caption 'Authorised signatory, <vendorName>'. Without a signature, write 'Computer-generated invoice; no signature required.' on that line.
|
|
Voiced fields with no reference slot, and where they go:
|
|
- Vendor name, logo, GSTIN and PAN: top right
|
|
- Place of supply: Billed-to sub-lines
|
|
- Tax rows: totals stack
|
|
- Amount in words: left of the totals
|
|
- Notes and signature: the ruled lines
|
|
- HSN/SAC (not in types.ts): a 52pt column between Description and Qty (Description shrinks to about 134pt), or a grey 9pt sub-line
|
|
- 'Generated with Voiced': a 7pt line under the sign-off
|
|
|
|
## Overflow risks
|
|
|
|
- The underlined fields have fixed widths: Invoice number 100.5pt (about 14 chars of Poppins 12, so 'INV/2026-27/0042' at about 105pt overflows), Billed to 150pt (about 22 chars), Date 168pt. Build each as a row of label plus a value View with flexGrow 1 and a bottom border, so the value wraps (the underline moves down) or shrinks to 11pt.
|
|
- Billed-to sub-lines: only 65pt (about 4 lines at 15pt) sits between the last underline (204.8) and the header bar (269.7). Longer addresses must push the table down, so use flow layout, not absolute positions.
|
|
- Payment block: 183.5pt wide at Bold 12, so long bank or branch names wrap. 5-6 lines at 16.5 pitch from 185 reach about 268, which is the header bar's top. Put the fields and the payment block in one flow row and start the table 30pt below the taller of the two.
|
|
- Rows: reference uses fixed 30.9pt rows with page-tall dividers. Use per-row cells with borderRight instead. The description column is 186pt, about 30 chars per line at 11pt, so rows will go multi-line.
|
|
- More than 8 items breaks onto a second page, and the 3pt frame must close on each page's fragment: rows carry 3pt left/right borders, the first row a 3pt top, the last row a 3pt bottom, and every row is wrap={false}.
|
|
- Totals: the 102.8pt label cell fits 'IGST @ 18%'; the 123.8pt value cell at Bold 12 fits up to about Rs 99,99,99,999.00 (with the rupee sign).
|
|
- Contacts: the address has about 228pt on one line (81.9 to 310, before the ruled lines at 323.7), about 38 chars at 11.67pt. Indian addresses wrap to 2-3 lines; at 22.5 pitch, two extra lines end around 823, inside the bottom margin. Use a 16pt pitch for wrapped continuation lines, and let the sign-off block flow instead of pinning it at 695.
|
|
- Notes longer than 3 lines overflow the ruled area. Generate the ruled lines from the wrapped line count, or cap the notes length.
|
|
|
|
## US Letter adaptation
|
|
|
|
- Keep the 60pt side margins (template default, user-overridable). Content width goes from 476.4 to 492 on Letter; set columns by percentage.
|
|
- Absorb the 50pt height loss: collapse the 75pt gap between the totals (595) and THANK YOU (670) to about 30pt, and the 64pt gap between the field stack and the header bar to about 40pt. With row padding on, pad to 7 empty rows on Letter.
|
|
- The sign-off block (THANK YOU, contacts, ruled lines) appears once, at the end. Anchor it to the bottom of the last page with a flexGrow spacer, not with fixed.
|
|
|
|
## react-pdf notes
|
|
|
|
- Page: <Page size={size} style={{ paddingTop: 50, paddingBottom: 60, paddingHorizontal: 60, fontFamily: 'Poppins', color: '#1C2120' }}>.
|
|
- Title: <Text style={{ fontWeight: 700, fontSize: 45.68, letterSpacing: -2.83, lineHeight: 1 }}>. Negative letterSpacing is fine.
|
|
- Field:
|
|
<View style={{ flexDirection: 'row', alignItems: 'flex-end', marginTop: 21 }}>
|
|
<Text style={{ fontSize: 13, letterSpacing: -0.37 }}>{label}</Text>
|
|
<View style={{ flexGrow: 1, flexShrink: 1, marginLeft: 4, borderBottomWidth: 0.75, borderBottomColor: '#423869', paddingBottom: 1 }}>
|
|
<Text style={{ fontSize: 12 }}>{value}</Text>
|
|
</View>
|
|
</View>
|
|
The field column is about 206 wide (60 to 266).
|
|
- Top row: flexDirection 'row', justifyContent 'space-between'. Left fields 206 wide; right payment/vendor column 183.5 wide.
|
|
- Header bar:
|
|
<View minPresenceAhead={62} style={{ flexDirection: 'row', height: 35.4, backgroundColor: '#FFE535', borderWidth: 3, borderColor: '#1C2120', marginBottom: 7.5 }}>
|
|
Cells use % widths with borderRightWidth 0.75, borderRightColor '#423869', justifyContent 'center'.
|
|
- Body rows:
|
|
<View wrap={false} style={{ flexDirection: 'row', minHeight: 30.9, borderLeftWidth: 3, borderRightWidth: 3, borderBottomWidth: 0.75, borderLeftColor: '#1C2120', borderRightColor: '#1C2120', borderBottomColor: '#423869' }}>
|
|
The first row adds borderTopWidth 3; the last row uses borderBottomWidth 3 with borderBottomColor '#1C2120'. Per-side border colours are supported.
|
|
- Totals: a column of 2-cell rows, lined up with the Price/Total columns by a left spacer equal to Description% + Qty% of the width.
|
|
- Icons: import { Svg, Path } from '@react-pdf/renderer'; then <Svg viewBox='0 0 48 48' style={{ width: 14, height: 14 }}><Path d={MAIL} fill='#1C2120' /></Svg>.
|
|
- Fonts: register Poppins 400/500/700 and Montserrat 400 from static TTF/WOFF. For money use fontFamily ['Poppins','IBM Plex Sans'].
|
|
|
|
## Similarity to other templates
|
|
|
|
Closest siblings are 7 and 11 (Poppins, a colour-filled header on a boxed grid table, boxed totals) and 8 (filled blue header plus an icon contact row). Only 2 has underlined fill-in fields, a detached header bar and 3pt frames. It is unrelated to the in-app template and to format.pdf/4.pdf. If 7 and 11 are built too, share one 'boxed grid table' component with accent colour and frame weight as parameters.
|
|
|
|
## Implementation notes
|
|
|
|
Built as `src/pdf/templates/highlighter/`: a standalone template (`tokens.ts` the type roles, palette and reference distances, `plan.ts` the pure planner, `Layout.tsx` the body, `highlighter.tsx` the definition) on the shared blocks and the Carbon icon paths (`ContactIcon`). Measured against `2.pdf` with `npm run templates:compare -- highlighter --rows=3` (images in `/tmp/invoice-c6b/`, left reference, right ours; `--numbers` prints the baselines). The reference page is a blank form, so the comparison sample here fills the fields and rows; every distance below was measured with the design's own content shape (no due date, no address, a two-line payment block).
|
|
|
|
**Matches the design (measured, A4):** the title baseline 98.0 at x 60.3 (98.0 / 60.3); the fields at 132.2 / 166.3 / 200.4 (132.2 / 166.0 / 200.4) with their label at x 60.3 and the value 5 pt after it on a 0.75 pt `#423869` underline ending at x 266; "Payment Method:" at 162.9 and the bold bank lines at 185.1 and the 16.5 pt pitch (162.9 / 185.1 / 201.6), x 352.5; the table bar 38.4 pt tall (frame included) with the Qty and Price headings centred on x 277.4 and 360.8 (277.4 / 360.8) and the Total heading 1.9 pt right of the design's (the design sets it 2.4 pt left of its column centre); a 4.5 pt gap under the bar, the body's own 3 pt frame, hairlines between the rows and columns; rows of 30.65 pt; the totals box one price-column wide plus one total column, its total label cell yellow; the sign-off and its ruled lines 212.3 pt wide ending 1.5 pt inside the frame's edge. Every colour is one of the design's four (a test scans the sources); the type is Poppins 400/700 and Montserrat 400 at the design's sizes.
|
|
|
|
**Intentional deviations, and why:**
|
|
- **Content box.** The 3 pt strokes are centred on x 59.6 and 536.0, so the frames reach 1.5 pt outside them: the content box is the frame's outer edge (58.1 to 537.5, 479.4 wide), and everything is measured from it. Margins are 58.1 / 57.8 pt at the sides, 50 pt at the top (the title's line box is the design's 64 pt zone) and 36 pt plus the footer reserve at the foot; the narrow and normal presets replace the margins with 36 and 54 pt, and the frame, the table columns and the totals box follow the content width.
|
|
- **Fields.** Date, Invoice number, a Due date field in the same style (only when the invoice has one), then Billed to with the client's name on its underline; the address, GSTIN, place of supply and PO number are 11 pt lines under it. A value that does not fit its underline wraps (the underline moves down) or, for an unbreakable token, shrinks and then splits at `@ . / -`. Pitch is a steady 34.1 pt (the design's is 33.8 / 34.4). The fields and the payment block sit side by side and the table starts under the taller one, at least 18 pt below it (the design leaves 64 pt, which Letter and a longer client block take back, down to 14 pt).
|
|
- **Top right.** The vendor's name (Poppins Bold 13), GSTIN and PAN, right-aligned in x 352 to 536, or the opt-in logo (off by default; 150 x 50, right-aligned, its top on y 50) above them; a wordmark replaces the name, a mark keeps it under the mark. The zone is 64 pt tall and grows with a logo, which moves the rest down. A cancelled invoice has a red CANCELLED line there and its sentence at the foot.
|
|
- **Payment block.** The account holder's name is added as its own line, and the bank lines read "Bank Name : ...", "Account No : ..." (then Branch and IFSC).
|
|
- **Table.** The design's rows are empty; ours carry the item: description left (9 pt in) with an HSN/SAC sub-line, qty centred, price and total right-aligned 9 pt inside their cells (the unit stays on the rate's line, "₹1,200.00/hr", while the column can take it). The form look pads with empty rows to 8 on A4 and 7 on Letter, but the pad rows give way first: they are dropped before the sign-off would have to move to a second page, and a long invoice has none. The 3 pt frame is closed at the foot after the last row only; where a table breaks onto a second page the page ends on the row's hairline (the frame's sides run to it), and the next page repeats the bar and the body's top rule.
|
|
- **Totals.** A stack of two-cell rows (label cell | value cell) lined up with the price and total columns: Subtotal, Discount, Taxable value, CGST / SGST / IGST ("CGST @ 9% :") with 0.75 pt rules between the rows and the 3 pt frame around them, and the "total :" row with the yellow label cell and a bold value. When a label needs more room than the price column has, the box grows to the left. The amount in words and the reverse-charge line are at its left, level with its first row.
|
|
- **Sign-off.** THANK YOU and the phone, e-mail and address with Carbon icons (the design's own Material paths are not used, so the app keeps one icon set); the phone and e-mail are one fitted line each (a wrapped phone number can break after a bracket), the address wraps. The ruled lines carry the notes, one wrapped line per ruled line (three lines at least; more than six lines and the notes flow as a block of their own before the sign-off), and the signature block (the image or "Computer-generated invoice; no signature is required.", the rule, the name and designation) takes the last one. The sign-off sits at the foot of the last page.
|
|
- **Elastic gaps** (Letter, and A4 when a one-page invoice overflows by less than the slack): fields to table bar (down to 14 pt), the row padding (down to 4.5 pt a side) and the totals to the sign-off (24 to 12 pt). The type is never scaled.
|