89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
import { Document, Font, Page, StyleSheet, Text, View } from '@react-pdf/renderer';
|
|
|
|
import type { Paper } from '../domain/paperModel';
|
|
|
|
/**
|
|
* Register a Unicode TTF so Vietnamese diacritics render (react-pdf's built-in Helvetica does not
|
|
* cover them). The Roboto TTFs are SELF-HOSTED under public/fonts so PDF export also works in
|
|
* air-gapped production deploys (no CDN fetch at render time). If glyphs ever render as blanks,
|
|
* replace the files with a Noto-with-Vietnamese TTF — registration is the only place it is configured.
|
|
*/
|
|
Font.register({
|
|
family: 'Roboto',
|
|
fonts: [
|
|
{ src: '/fonts/roboto-regular.ttf' },
|
|
{ src: '/fonts/roboto-bold.ttf', fontWeight: 'bold' },
|
|
],
|
|
});
|
|
|
|
const styles = StyleSheet.create({
|
|
page: { fontFamily: 'Roboto', fontSize: 11, lineHeight: 1.5, padding: 48, color: '#1f2937' },
|
|
title: { fontSize: 18, fontWeight: 'bold', marginBottom: 4 },
|
|
titleEn: { fontSize: 12, color: '#6b7280', marginBottom: 12 },
|
|
meta: { fontSize: 9, color: '#6b7280', marginBottom: 16 },
|
|
h2: { fontSize: 12, fontWeight: 'bold', marginTop: 14, marginBottom: 4 },
|
|
body: { textAlign: 'justify' },
|
|
source: { marginBottom: 3 },
|
|
badge: { fontSize: 9, color: '#047857', marginTop: 16 },
|
|
});
|
|
|
|
export function PaperPdfDocument({ paper, projectTitle }: { paper: Paper; projectTitle?: string }) {
|
|
return (
|
|
<Document title={paper.title || 'Bài báo'} author={paper.approval?.approvedByName}>
|
|
<Page size="A4" style={styles.page}>
|
|
<Text style={styles.title}>{paper.title || '(Chưa đặt tiêu đề)'}</Text>
|
|
{paper.titleEn ? <Text style={styles.titleEn}>{paper.titleEn}</Text> : null}
|
|
|
|
<Text style={styles.meta}>
|
|
{projectTitle ? `Đề tài: ${projectTitle}` : ''}
|
|
{paper.publishedAt
|
|
? ` · Xuất bản: ${new Date(paper.publishedAt).toLocaleDateString('vi-VN')}`
|
|
: ' · Bản thảo'}
|
|
{paper.approval ? ` · Phê duyệt: ${paper.approval.approvedByName}` : ''}
|
|
</Text>
|
|
|
|
{paper.abstractVi ? (
|
|
<View>
|
|
<Text style={styles.h2}>Tóm tắt</Text>
|
|
<Text style={styles.body}>{paper.abstractVi}</Text>
|
|
</View>
|
|
) : null}
|
|
{paper.abstractEn ? (
|
|
<View>
|
|
<Text style={styles.h2}>Abstract</Text>
|
|
<Text style={styles.body}>{paper.abstractEn}</Text>
|
|
</View>
|
|
) : null}
|
|
|
|
{paper.body ? (
|
|
<View>
|
|
<Text style={styles.h2}>Nội dung</Text>
|
|
<Text style={styles.body}>{paper.body}</Text>
|
|
</View>
|
|
) : null}
|
|
|
|
{paper.sources.length > 0 ? (
|
|
<View>
|
|
<Text style={styles.h2}>Tài liệu tham khảo</Text>
|
|
{paper.sources.map((s, i) => (
|
|
<Text key={s.id} style={styles.source}>
|
|
[{i + 1}] {s.title}
|
|
{s.authors ? ` — ${s.authors}` : ''}
|
|
{s.doiOrUrl ? ` (${s.doiOrUrl})` : ''}
|
|
{s.verifiedReal ? ' ✓' : ''}
|
|
</Text>
|
|
))}
|
|
</View>
|
|
) : null}
|
|
|
|
{paper.status === 'published' && paper.approval ? (
|
|
<Text style={styles.badge}>
|
|
Đã phê duyệt & xuất bản bởi {paper.approval.approvedByName} —{' '}
|
|
{new Date(paper.approval.approvedAt).toLocaleString('vi-VN')}
|
|
</Text>
|
|
) : null}
|
|
</Page>
|
|
</Document>
|
|
);
|
|
}
|