Certificate
An official certificate with a security pattern, a raised seal and a QR code to check the original.
Report definition (JSON)
{
"name": "재직증명서",
"page": {
"size": "A4",
"margins": 20
},
"fingerprint": true,
"font": {
"family": "Nanum Gothic",
"size": 10.5
},
"watermark": [
{
"text": "SECOND KOREA 원본",
"repeat": true,
"opacity": 0.05,
"fontSize": 9,
"gap": 32,
"angle": -30
},
{
"text": "원 본",
"opacity": 0.06,
"fontSize": 120,
"color": "#2340f5"
}
],
"emboss": {
"text": "(주)세컨드코리아 대표이사 직인 ·",
"centerText": "인증",
"x": 128,
"y": 232,
"w": 36,
"depth": 0.35
},
"bands": {
"title": {
"height": 240,
"elements": [
{
"type": "text",
"x": 0,
"y": 0,
"w": 85,
"h": 7,
"text": "{docNo}",
"style": {
"fontSize": 9,
"color": "#555"
}
},
{
"type": "text",
"x": 0,
"y": 14,
"w": 170,
"h": 18,
"text": "재 직 증 명 서",
"style": {
"fontSize": 26,
"bold": true,
"align": "center",
"letterSpacing": 1.5
}
},
{
"type": "text",
"x": 0,
"y": 44,
"w": 10,
"h": 33,
"text": "인\n적\n사\n항",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333",
"lineHeight": 1.5
}
},
{
"type": "text",
"x": 10,
"y": 44,
"w": 30,
"h": 11,
"text": "성 명",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 44,
"w": 50,
"h": 11,
"text": "{person.name}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 90,
"y": 44,
"w": 30,
"h": 11,
"text": "생년월일",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 120,
"y": 44,
"w": 50,
"h": 11,
"text": "{format(person.birth, \"yyyy년 M월 d일\")}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 10,
"y": 55,
"w": 30,
"h": 22,
"text": "주 소",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 55,
"w": 130,
"h": 22,
"text": "{person.address}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 0,
"y": 77,
"w": 10,
"h": 33,
"text": "재\n직\n사\n항",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333",
"lineHeight": 1.5
}
},
{
"type": "text",
"x": 10,
"y": 77,
"w": 30,
"h": 11,
"text": "회 사 명",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 77,
"w": 130,
"h": 11,
"text": "{company.name}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 10,
"y": 88,
"w": 30,
"h": 11,
"text": "부 서",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 88,
"w": 50,
"h": 11,
"text": "{person.dept}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 90,
"y": 88,
"w": 30,
"h": 11,
"text": "직 위",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 120,
"y": 88,
"w": 50,
"h": 11,
"text": "{person.position}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 10,
"y": 99,
"w": 30,
"h": 11,
"text": "재직기간",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 99,
"w": 130,
"h": 11,
"text": "{format(person.joined, \"yyyy년 M월 d일\")} ~ 현재 ({floor(dateDiff(person.joined, issued) / 365)}년 {floor((dateDiff(person.joined, issued) % 365) / 30)}개월)",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 0,
"y": 110,
"w": 40,
"h": 11,
"text": "용 도",
"style": {
"background": "#f6f7f9",
"bold": true,
"align": "center",
"border": "0.25 solid #333"
}
},
{
"type": "text",
"x": 40,
"y": 110,
"w": 130,
"h": 11,
"text": "{purpose}",
"style": {
"border": "0.25 solid #333",
"padding": [
0.5,
2,
0.5,
2
]
}
},
{
"type": "text",
"x": 0,
"y": 136,
"w": 170,
"h": 10,
"text": "위와 같이 재직하고 있음을 증명합니다.",
"style": {
"align": "center",
"fontSize": 13
}
},
{
"type": "text",
"x": 0,
"y": 160,
"w": 170,
"h": 10,
"text": "{format(issued, \"yyyy년 M월 d일\")}",
"style": {
"align": "center",
"fontSize": 12
}
},
{
"type": "text",
"x": 0,
"y": 190,
"w": 170,
"h": 12,
"text": "{company.name} 대표이사 {company.ceo}",
"style": {
"align": "center",
"fontSize": 15,
"bold": true,
"letterSpacing": 0.6
}
},
{
"type": "image",
"x": 131,
"y": 184,
"w": 22,
"h": 22,
"src": "data:image/svg+xml;base64,… (1,866 chars)"
},
{
"type": "text",
"x": 0,
"y": 203,
"w": 170,
"h": 7,
"text": "{company.address}",
"style": {
"align": "center",
"fontSize": 9,
"color": "#555"
}
},
{
"type": "barcode",
"x": 0,
"y": 226,
"w": 22,
"h": 22,
"expr": "\"https://freereport.app/verify/\" + replace(docNo, \" \", \"\") + \"?h=\" + docHashShort",
"format": "qr",
"ecc": "Q"
},
{
"type": "text",
"x": 24,
"y": 230,
"w": 90,
"h": 14,
"text": "이 증명서는 위·변조 방지 처리가 되어 있습니다.\nQR 코드로 원본 여부를 확인할 수 있습니다.\n확인코드 {docHashShort}",
"style": {
"fontSize": 7.5,
"color": "#555",
"valign": "top"
}
}
]
}
}
}Data
Long lists show their first three items.
{
"docNo": "제 2026-0412 호",
"person": {
"name": "홍길동",
"birth": "1990-03-15",
"address": "서울특별시 중구 세종대로 110",
"dept": "개발팀",
"position": "책임연구원",
"joined": "2019-03-04"
},
"purpose": "금융기관 제출용",
"company": {
"name": "(주)세컨드코리아",
"ceo": "구명석",
"address": "경기도 파주시 와석순환로 507, 701호"
},
"issued": "2026-10-15"
}Code for this demo
import { certificateData } from '../../samples/data';
import { certificateReport } from '../../samples/reports';
import { mountViewer, textInput, tr, type DemoContext } from '../ui';
/** 재직증명서: a repeated security pattern, a large "원본" mark, a raised seal and a QR code. */
export const definition = certificateReport();
export const sampleData = certificateData;
export default function mount(ctx: DemoContext) {
const viewer = mountViewer(ctx, { definition, data: sampleData, zoom: 'page', fileName: '재직증명서' });
textInput(ctx, tr(ctx.lang, { en: 'Name', ko: '성명' }), certificateData.person.name, name => {
void viewer.setData({ ...certificateData, person: { ...certificateData.person, name } });
});
}