medical-form-printer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 wangchengshi-ship-it
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,548 @@
1
+ # medical-form-printer
2
+
3
+ [![npm version](https://img.shields.io/npm/v/medical-form-printer.svg)](https://www.npmjs.com/package/medical-form-printer)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js Version](https://img.shields.io/node/v/medical-form-printer.svg)](https://nodejs.org)
6
+
7
+ A schema-driven medical form print renderer that transforms structured form data into printable HTML and PDF documents. Designed for healthcare applications requiring professional document generation with support for complex layouts, smart pagination, and cross-environment consistency.
8
+
9
+ [δΈ­ζ–‡ζ–‡ζ‘£](./README.zh-CN.md)
10
+
11
+ ## Features
12
+
13
+ - πŸ–¨οΈ **Dual Environment** - Works seamlessly in both browser and Node.js
14
+ - πŸ“„ **Rich Section Types** - Info grids, data tables, checkbox grids, signature areas, notes, and more
15
+ - 🎨 **Theme Customization** - Fully customizable fonts, colors, spacing, and sizing
16
+ - πŸ“‘ **PDF Generation** - High-fidelity PDF output via Puppeteer (Node.js)
17
+ - πŸ”— **PDF Merging** - Combine multiple documents into a single PDF
18
+ - πŸ“ **Smart Pagination** - Automatic page breaks with header repetition and overflow handling
19
+ - πŸ”’ **CSS Isolation** - Embedded fonts and namespaced styles for consistent rendering
20
+ - πŸ”Œ **Extensible** - Register custom section renderers for specialized content
21
+ - πŸ“¦ **TypeScript First** - Full type definitions with comprehensive JSDoc documentation
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # npm
27
+ npm install medical-form-printer
28
+
29
+ # yarn
30
+ yarn add medical-form-printer
31
+
32
+ # pnpm
33
+ pnpm add medical-form-printer
34
+
35
+ # bun
36
+ bun add medical-form-printer
37
+ ```
38
+
39
+ For PDF generation in Node.js, install Puppeteer as a peer dependency:
40
+
41
+ ```bash
42
+ npm install puppeteer
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ### Browser Usage
48
+
49
+ ```typescript
50
+ import { renderToHtml } from 'medical-form-printer'
51
+
52
+ const printSchema = {
53
+ pageSize: 'A4',
54
+ orientation: 'portrait',
55
+ header: {
56
+ hospital: 'Sample Hospital',
57
+ department: 'Postpartum Care Center',
58
+ title: 'Patient Assessment Form',
59
+ },
60
+ sections: [
61
+ {
62
+ type: 'info-grid',
63
+ config: {
64
+ columns: 4,
65
+ rows: [
66
+ {
67
+ cells: [
68
+ { label: 'Name', field: 'name', type: 'text' },
69
+ { label: 'Age', field: 'age', type: 'number' },
70
+ { label: 'Date', field: 'admissionDate', type: 'date' },
71
+ { label: 'Room', field: 'roomNumber', type: 'text' },
72
+ ]
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ ],
78
+ footer: {
79
+ showPageNumber: true
80
+ }
81
+ }
82
+
83
+ const formData = {
84
+ name: 'Jane Doe',
85
+ age: 28,
86
+ admissionDate: '2024-01-15',
87
+ roomNumber: 'A-101'
88
+ }
89
+
90
+ // Render to HTML
91
+ const html = renderToHtml(printSchema, formData, {
92
+ watermark: 'Internal Use Only'
93
+ })
94
+
95
+ // Display in iframe or div
96
+ document.getElementById('preview').innerHTML = html
97
+ ```
98
+
99
+ ### Node.js Usage (PDF Generation)
100
+
101
+ ```typescript
102
+ import { renderToPdf, mergePdfs } from 'medical-form-printer/node'
103
+ import fs from 'fs'
104
+
105
+ // Generate single PDF
106
+ const pdfBuffer = await renderToPdf(printSchema, formData, {
107
+ watermark: 'Confidential'
108
+ })
109
+ fs.writeFileSync('assessment.pdf', pdfBuffer)
110
+
111
+ // Merge multiple forms into one PDF
112
+ const mergedPdf = await mergePdfs([
113
+ { schema: maternalSchema, data: maternalData },
114
+ { schema: newbornSchema, data: newbornData },
115
+ ])
116
+ fs.writeFileSync('complete-record.pdf', mergedPdf)
117
+ ```
118
+
119
+ ## API Reference
120
+
121
+ ### Core Rendering
122
+
123
+ #### `renderToHtml(schema, data, options?)`
124
+
125
+ Renders a print schema with form data to an HTML string.
126
+
127
+ ```typescript
128
+ import { renderToHtml } from 'medical-form-printer'
129
+
130
+ const html = renderToHtml(printSchema, formData, {
131
+ theme: customTheme,
132
+ watermark: 'Draft',
133
+ watermarkOpacity: 0.1
134
+ })
135
+ ```
136
+
137
+ **Parameters:**
138
+ - `schema: PrintSchema` - The print schema defining layout and sections
139
+ - `data: FormData` - The form data to render
140
+ - `options?: RenderOptions` - Optional rendering configuration
141
+
142
+ **Returns:** `string` - Complete HTML document
143
+
144
+ #### `renderToIsolatedHtml(schema, data, options?)`
145
+
146
+ Renders with CSS isolation for consistent cross-environment styling.
147
+
148
+ ```typescript
149
+ import { renderToIsolatedHtml } from 'medical-form-printer'
150
+
151
+ const html = renderToIsolatedHtml(printSchema, formData, {
152
+ watermark: 'Internal Use Only'
153
+ })
154
+ ```
155
+
156
+ All content is wrapped in an isolation container with:
157
+ - Namespaced CSS classes (prefixed with `mpr-`)
158
+ - Embedded Source Han Serif SC font
159
+ - Style containment for predictable rendering
160
+
161
+ #### `renderToIsolatedFragment(schema, data, options?)`
162
+
163
+ Renders an isolated HTML fragment for embedding in existing pages.
164
+
165
+ ```typescript
166
+ import { renderToIsolatedFragment } from 'medical-form-printer'
167
+
168
+ const fragment = renderToIsolatedFragment(printSchema, formData)
169
+ document.getElementById('preview').innerHTML = fragment
170
+ ```
171
+
172
+ ### PDF Generation (Node.js)
173
+
174
+ #### `renderToPdf(schema, data, options?)`
175
+
176
+ Generates a PDF buffer from a print schema.
177
+
178
+ ```typescript
179
+ import { renderToPdf } from 'medical-form-printer/node'
180
+
181
+ const pdfBuffer = await renderToPdf(printSchema, formData, {
182
+ watermark: 'Confidential',
183
+ pdfOptions: {
184
+ format: 'A4',
185
+ printBackground: true
186
+ }
187
+ })
188
+ ```
189
+
190
+ **Parameters:**
191
+ - `schema: PrintSchema` - The print schema
192
+ - `data: FormData` - The form data
193
+ - `options?: RenderOptions & { pdfOptions?: PdfOptions }` - Rendering and PDF options
194
+
195
+ **Returns:** `Promise<Buffer>` - PDF file buffer
196
+
197
+ #### `mergePdfs(documents, options?)`
198
+
199
+ Merges multiple documents into a single PDF.
200
+
201
+ ```typescript
202
+ import { mergePdfs } from 'medical-form-printer/node'
203
+
204
+ const mergedPdf = await mergePdfs([
205
+ { schema: schema1, data: data1 },
206
+ { schema: schema2, data: data2 },
207
+ ], {
208
+ watermark: 'Complete Record'
209
+ })
210
+ ```
211
+
212
+ ### Custom Section Renderers
213
+
214
+ #### `registerSectionRenderer(type, renderer)`
215
+
216
+ Registers a custom section renderer for specialized content.
217
+
218
+ ```typescript
219
+ import { registerSectionRenderer } from 'medical-form-printer'
220
+
221
+ registerSectionRenderer('vital-signs-chart', (config, data, options) => {
222
+ const values = data[config.dataField] || []
223
+ return `
224
+ <div class="vital-signs-chart">
225
+ <h3>${config.title}</h3>
226
+ <!-- Custom chart rendering -->
227
+ </div>
228
+ `
229
+ })
230
+ ```
231
+
232
+ #### `getSectionRenderer(type)`
233
+
234
+ Retrieves a registered section renderer.
235
+
236
+ ```typescript
237
+ import { getSectionRenderer } from 'medical-form-printer'
238
+
239
+ const renderer = getSectionRenderer('info-grid')
240
+ ```
241
+
242
+ ### Pagination
243
+
244
+ #### `renderPaginatedHtml(config)`
245
+
246
+ Renders multi-page content with smart pagination.
247
+
248
+ ```typescript
249
+ import {
250
+ renderPaginatedHtml,
251
+ calculatePageBreaks,
252
+ PAGE_A4
253
+ } from 'medical-form-printer'
254
+
255
+ const pageBreaks = calculatePageBreaks(measuredItems, {
256
+ pageHeight: PAGE_A4.height,
257
+ headerHeight: 60,
258
+ footerHeight: 40,
259
+ repeatTableHeaders: true
260
+ })
261
+
262
+ const html = renderPaginatedHtml({
263
+ schema: printSchema,
264
+ data: formData,
265
+ pageBreakResult: pageBreaks,
266
+ measuredItems: items,
267
+ config: {
268
+ isolated: true,
269
+ showHeaderOnEachPage: true,
270
+ continuationSuffix: '(continued)'
271
+ }
272
+ })
273
+ ```
274
+
275
+ #### Page Size Presets
276
+
277
+ ```typescript
278
+ import { PAGE_A4, PAGE_A5, PAGE_16K, PAGE_PRESETS } from 'medical-form-printer'
279
+
280
+ // PAGE_A4: { width: 210, height: 297 } (mm)
281
+ // PAGE_A5: { width: 148, height: 210 } (mm)
282
+ // PAGE_16K: { width: 185, height: 260 } (mm)
283
+ ```
284
+
285
+ #### Unit Conversion
286
+
287
+ ```typescript
288
+ import { mmToPx, pxToMm, mmToPt, ptToMm } from 'medical-form-printer'
289
+
290
+ const heightPx = mmToPx(297) // 297mm β†’ pixels
291
+ const heightMm = pxToMm(1123) // pixels β†’ mm
292
+ ```
293
+
294
+ ### Styling
295
+
296
+ #### `generateCss(theme?)`
297
+
298
+ Generates CSS styles for print rendering.
299
+
300
+ ```typescript
301
+ import { generateCss, defaultTheme } from 'medical-form-printer'
302
+
303
+ const css = generateCss(defaultTheme)
304
+ ```
305
+
306
+ #### `generateIsolatedCss(theme?)`
307
+
308
+ Generates isolated CSS with embedded fonts and namespaced classes.
309
+
310
+ ```typescript
311
+ import { generateIsolatedCss } from 'medical-form-printer'
312
+
313
+ const css = generateIsolatedCss()
314
+ // Includes @font-face, isolation container, and all component styles
315
+ ```
316
+
317
+ #### Theme Customization
318
+
319
+ ```typescript
320
+ import { renderToHtml, mergeTheme, defaultTheme } from 'medical-form-printer'
321
+
322
+ const customTheme = mergeTheme(defaultTheme, {
323
+ fonts: {
324
+ body: '"Microsoft YaHei", "PingFang SC", sans-serif',
325
+ heading: '"Microsoft YaHei", "PingFang SC", sans-serif'
326
+ },
327
+ colors: {
328
+ primary: '#1a1a1a',
329
+ border: '#333333',
330
+ background: '#ffffff'
331
+ },
332
+ fontSize: {
333
+ body: '10pt',
334
+ heading: '14pt',
335
+ small: '8pt'
336
+ },
337
+ spacing: {
338
+ section: '12pt',
339
+ cell: '4pt'
340
+ }
341
+ })
342
+
343
+ const html = renderToHtml(schema, data, { theme: customTheme })
344
+ ```
345
+
346
+ ### Formatters
347
+
348
+ ```typescript
349
+ import {
350
+ formatDate,
351
+ formatBoolean,
352
+ formatNumber,
353
+ formatValue,
354
+ isChecked
355
+ } from 'medical-form-printer'
356
+
357
+ formatDate('2024-01-15') // '2024-01-15'
358
+ formatDate('2024-01-15', { format: 'YYYYεΉ΄MM月DDζ—₯' }) // '2024εΉ΄01月15ζ—₯'
359
+ formatBoolean(true) // 'βœ“'
360
+ formatBoolean(false) // 'βœ—'
361
+ formatNumber(1234.5, { decimals: 2 }) // '1234.50'
362
+ isChecked('yes', ['yes', 'true']) // true
363
+ ```
364
+
365
+ ### HTML Builder Utilities
366
+
367
+ ```typescript
368
+ import {
369
+ HtmlBuilder,
370
+ h,
371
+ fragment,
372
+ when,
373
+ each,
374
+ escapeHtml
375
+ } from 'medical-form-printer'
376
+
377
+ // Fluent HTML building
378
+ const html = h('div', { class: 'container' },
379
+ h('h1', {}, 'Title'),
380
+ when(showContent, () => h('p', {}, 'Content')),
381
+ each(items, (item) => h('li', {}, item.name))
382
+ )
383
+
384
+ // Safe HTML escaping
385
+ const safe = escapeHtml('<script>alert("xss")</script>')
386
+ ```
387
+
388
+ ## PrintSchema Structure
389
+
390
+ ```typescript
391
+ interface PrintSchema {
392
+ pageSize: 'A4' | 'A5' | '16K'
393
+ orientation: 'portrait' | 'landscape'
394
+ header: {
395
+ hospital: string
396
+ department?: string
397
+ title: string
398
+ subtitle?: string
399
+ }
400
+ sections: PrintSection[]
401
+ footer?: {
402
+ showPageNumber?: boolean
403
+ pageNumberFormat?: string
404
+ notes?: string
405
+ }
406
+ }
407
+ ```
408
+
409
+ ## Section Types
410
+
411
+ | Type | Description | Use Case |
412
+ |------|-------------|----------|
413
+ | `info-grid` | Grid layout for key-value pairs | Patient demographics, basic info |
414
+ | `table` | Data table with columns | Nursing records, medication logs |
415
+ | `checkbox-grid` | Grid of checkbox options | Assessment checklists, symptoms |
416
+ | `signature-area` | Signature fields with labels | Approvals, acknowledgments |
417
+ | `notes` | Static text content | Instructions, disclaimers |
418
+ | `free-text` | Multi-line text input | Comments, observations |
419
+
420
+ ### Info Grid Section
421
+
422
+ ```typescript
423
+ {
424
+ type: 'info-grid',
425
+ config: {
426
+ columns: 4,
427
+ rows: [
428
+ {
429
+ cells: [
430
+ { label: 'Name', field: 'name', type: 'text' },
431
+ { label: 'Age', field: 'age', type: 'number', span: 1 },
432
+ { label: 'Date', field: 'date', type: 'date' },
433
+ { label: 'Status', field: 'status', type: 'checkbox', options: ['Active'] }
434
+ ]
435
+ }
436
+ ]
437
+ }
438
+ }
439
+ ```
440
+
441
+ ### Table Section
442
+
443
+ ```typescript
444
+ {
445
+ type: 'table',
446
+ title: 'Nursing Records',
447
+ config: {
448
+ dataField: 'nursingRecords',
449
+ columns: [
450
+ { header: 'Date', field: 'date', type: 'date', width: '15%' },
451
+ { header: 'Time', field: 'time', type: 'text', width: '10%' },
452
+ { header: 'Temperature', field: 'temperature', type: 'number', width: '15%' },
453
+ { header: 'Notes', field: 'notes', type: 'text' }
454
+ ]
455
+ }
456
+ }
457
+ ```
458
+
459
+ ### Checkbox Grid Section
460
+
461
+ ```typescript
462
+ {
463
+ type: 'checkbox-grid',
464
+ title: 'Symptoms Assessment',
465
+ config: {
466
+ field: 'symptoms',
467
+ columns: 4,
468
+ options: [
469
+ { value: 'fever', label: 'Fever' },
470
+ { value: 'headache', label: 'Headache' },
471
+ { value: 'fatigue', label: 'Fatigue' },
472
+ { value: 'nausea', label: 'Nausea' }
473
+ ]
474
+ }
475
+ }
476
+ ```
477
+
478
+ ### Signature Area Section
479
+
480
+ ```typescript
481
+ {
482
+ type: 'signature-area',
483
+ config: {
484
+ fields: [
485
+ { label: 'Patient Signature', field: 'patientSignature' },
486
+ { label: 'Nurse Signature', field: 'nurseSignature' },
487
+ { label: 'Date', field: 'signatureDate', type: 'date' }
488
+ ]
489
+ }
490
+ }
491
+ ```
492
+
493
+ ## CSS Isolation
494
+
495
+ For consistent rendering across different environments, use isolation mode:
496
+
497
+ ```typescript
498
+ import {
499
+ renderToIsolatedHtml,
500
+ CSS_NAMESPACE,
501
+ ISOLATION_ROOT_CLASS,
502
+ namespaceClass,
503
+ namespaceClasses
504
+ } from 'medical-form-printer'
505
+
506
+ // CSS_NAMESPACE = 'mpr'
507
+ // ISOLATION_ROOT_CLASS = 'mpr-root'
508
+
509
+ // Namespace utilities
510
+ namespaceClass('header') // 'mpr-header'
511
+ namespaceClasses(['header', 'footer']) // ['mpr-header', 'mpr-footer']
512
+ ```
513
+
514
+ Isolation mode provides:
515
+ - All classes prefixed with `mpr-` namespace
516
+ - Embedded Source Han Serif SC font (subset for CJK support)
517
+ - CSS containment (`contain: layout style`)
518
+ - Consistent rendering regardless of host page styles
519
+
520
+ ## Examples
521
+
522
+ See the [examples](./examples) directory for complete working examples:
523
+
524
+ - [Browser Example](./examples/browser) - Vanilla HTML/JS usage
525
+ - [Node.js Example](./examples/node) - PDF generation with file output
526
+
527
+ ## Storybook
528
+
529
+ Interactive component documentation is available via Storybook:
530
+
531
+ ```bash
532
+ npm run storybook
533
+ ```
534
+
535
+ ## Contributing
536
+
537
+ Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
538
+
539
+ ## License
540
+
541
+ [MIT](./LICENSE) Β© 2024
542
+
543
+ ## Links
544
+
545
+ - [GitHub Repository](https://github.com/wangchengshi-ship-it/medical-form-printer)
546
+ - [npm Package](https://www.npmjs.com/package/medical-form-printer)
547
+ - [Issue Tracker](https://github.com/wangchengshi-ship-it/medical-form-printer/issues)
548
+ - [Changelog](./CHANGELOG.md)