쿠폰
오려 쓰는 쿠폰을 한 장에 여러 벌 배치합니다. EAN-13 바코드, 배경색과 투명도, 그리고 구역 반복으로 벌을 늘리는 법을 배웁니다.
여기서 배우는 것
편집기로 만들기
- 사각형 하나로 쿠폰 한 장을 만들고, 그 안에 문구·바코드·유효기간을 넣습니다.
- 사각형의 반복 설정에 data.coupons 를 넣고 항목 이름을 cp 로 둡니다. 벌이 세로로 이어 붙어 한 장에 여러 개가 놓입니다.
- QR·바코드를 넣고 종류를 EAN-13 으로 고릅니다. 값은 숫자 12~13자리여야 합니다 — 그 밖의 값은 그리지 못합니다.
- 배경색을 옅게 깔고 큰 할인율을 투명도 낮춰 겹칩니다. 종이 값을 아끼려 잉크를 아끼는 것이 아니라, 읽을 것과 꾸밀 것을 구분하는 것입니다.
- 원을 하나 놓아 오리는 지점을 표시합니다. 도형은 색과 두께만 있으면 됩니다.
스키마 · API 로 만들기
- codeKind 를 "ean13" 으로 둡니다. props.text 에는 숫자만 넣으십시오 — 규격이 정한 자릿수를 벗어나면 그리지 못합니다.
- 반복 블록은 rect 의 props.repeat 입니다. 벌은 세로로 이어 붙고, 한 페이지를 넘으면 다음 페이지로 넘어갑니다.
- styles[].opacity 는 0~1 입니다. 노드가 아니라 스타일의 값이므로, 같은 스타일을 쓰는 노드가 함께 흐려집니다.
완성 문서
아래 JSON 이 위 두 순서가 만드는 바로 그 문서입니다. 저희 테스트가 이 문서를 검증하고 경고 없이 렌더되는지 확인하므로, 그대로 붙여 넣어 쓰셔도 됩니다.
{
"v": 1,
"unit": "px",
"page": {
"size": "a4",
"orientation": "portrait",
"margin": {
"t": 32,
"r": 32,
"b": 32,
"l": 32
},
"header": "",
"footer": ""
},
"fonts": [],
"styles": [
{
"id": "s_card",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#111111",
"bg": "#fff8e6",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 12,
"opacity": 1
},
{
"id": "s_off",
"fontFamily": "Liberation Sans",
"fontSize": 54,
"fontWeight": 700,
"italic": false,
"color": "#c9a227",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 0.35
},
{
"id": "s_h",
"fontFamily": "Liberation Sans",
"fontSize": 18,
"fontWeight": 700,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_l",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#555555",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_cap",
"fontFamily": "Liberation Sans",
"fontSize": 10,
"fontWeight": 400,
"italic": false,
"color": "#888888",
"bg": "transparent",
"align": "center",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
}
],
"nodes": [
{
"id": "n_card",
"parent": "",
"order": 0,
"type": "rect",
"x": 0,
"y": 0,
"w": 697,
"h": 190,
"show": "",
"styleId": "s_card",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#c9a227",
"fill": "#fff8e6",
"repeat": "data.coupons",
"as": "cp",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "One coupon"
},
{
"id": "n_logo",
"parent": "n_card",
"order": 1,
"type": "image",
"x": 20,
"y": 18,
"w": 100,
"h": 50,
"show": "",
"styleId": "s_l",
"props": {
"text": "",
"src": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAgNjAiPjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iNjAiIHJ4PSI4IiBmaWxsPSIjMWYyOTM3Ii8+PHRleHQgeD0iNjAiIHk9IjM4IiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIyMCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+TE9HTzwvdGV4dD48L3N2Zz4=",
"fit": "contain",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Logo"
},
{
"id": "n_off",
"parent": "n_card",
"order": 2,
"type": "label",
"x": 400,
"y": 20,
"w": 280,
"h": 70,
"show": "",
"styleId": "s_off",
"props": {
"text": "{{ cp.off }}%",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Discount"
},
{
"id": "n_h",
"parent": "n_card",
"order": 3,
"type": "label",
"x": 20,
"y": 82,
"w": 360,
"h": 24,
"show": "",
"styleId": "s_h",
"props": {
"text": "{{ cp.title }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Offer"
},
{
"id": "n_exp",
"parent": "n_card",
"order": 4,
"type": "label",
"x": 20,
"y": 108,
"w": 360,
"h": 18,
"show": "",
"styleId": "s_l",
"props": {
"text": "Valid until {{ cp.expires }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Expiry"
},
{
"id": "n_bar",
"parent": "n_card",
"order": 5,
"type": "code",
"x": 400,
"y": 100,
"w": 260,
"h": 62,
"show": "",
"styleId": "s_l",
"props": {
"text": "{{ cp.barcode }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "ean13",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Barcode"
},
{
"id": "n_cut",
"parent": "n_card",
"order": 6,
"type": "circle",
"x": 330,
"y": 178,
"w": 12,
"h": 12,
"show": "",
"styleId": "s_l",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#c9a227",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Tear point"
}
]
}요청 본문입니다. template 자리에 위 문서를, data 자리에 아래 데이터를 넣습니다.
{
"template": "(the document above)",
"data": {
"coupons": [
{
"title": "First order",
"off": 20,
"expires": "2026-12-31",
"barcode": "8801234567893"
},
{
"title": "Thanks for coming back",
"off": 10,
"expires": "2026-10-31",
"barcode": "8801234567909"
},
{
"title": "Refer a friend",
"off": 15,
"expires": "2026-11-30",
"barcode": "8801234567916"
}
]
}
}