Comparing version 0.1.3 to 0.1.4
@@ -45,2 +45,4 @@ #!/usr/bin/env node | ||
separateSchemas: { key: '-separate-schemas', value: false, help: 'When multiple API files parsed, create separate schemas section for each.' }, | ||
style: { key: '-style', value: '', help: 'Path to style.json. See -export-style.' }, | ||
exportStyle: { key: '-export-style', value: false, help: 'Save default document style into style.json for editing.' }, | ||
help: { key: '-h', value: false, help: 'Show this help.' }, | ||
@@ -60,8 +62,25 @@ }; | ||
} | ||
if (args.help.value || argsParser.rest.length === 0) { | ||
if (args.help.value) { | ||
printUsageHelp(); | ||
return; | ||
} | ||
let style; | ||
if (args.style.value) { | ||
try { | ||
style = JSON.parse(fs_1.default.readFileSync(args.style.value, 'utf8')); | ||
} | ||
catch (e) { | ||
(0, logger_1.errorLog)(`Error in ${args.style.value}: ${e}`); | ||
return; | ||
} | ||
} | ||
const outputFile = args.output.value; | ||
const doc = new pdf_writer_1.PdfWriter(outputFile); | ||
const doc = new pdf_writer_1.PdfWriter(outputFile, style); | ||
if (args.exportStyle.value) { | ||
fs_1.default.writeFileSync('style.json', JSON.stringify(doc.style, null, 2)); | ||
} | ||
if (argsParser.rest.length === 0) { | ||
(0, logger_1.log)('No .json or .yaml files specified.\n'); | ||
return; | ||
} | ||
doc.addTitlePage(args.title.value, args.subtitle.value, (0, moment_1.default)().format('YYYY-MM-DD')); | ||
@@ -68,0 +87,0 @@ const errorMessages = []; |
@@ -19,3 +19,21 @@ "use strict"; | ||
class PdfWriter { | ||
constructor(outputFilePath) { | ||
constructor(outputFilePath, style) { | ||
// configurable PDF style | ||
this.style = { | ||
color: { | ||
main: '#333333', | ||
secondary: '#6B7B8E', | ||
types: '#8A3324', | ||
headers: '#2A4D69', | ||
subHeaders: '#4B86B4', | ||
}, | ||
font: { | ||
baseSize: 10, | ||
}, | ||
format: { | ||
indentStep: 12, | ||
horizontalMargin: 70, | ||
verticalMargin: 50 | ||
} | ||
}; | ||
this.currentSectionName = ''; | ||
@@ -32,21 +50,17 @@ this.pageNumber = 0; | ||
]; | ||
this.colorMain = 'black'; | ||
this.colorAccent = 'blue'; | ||
this.colorDisabled = 'grey'; | ||
this.baseFontSize = 10; | ||
this.paraGap = this.baseFontSize / 3; | ||
this.subHeaderGap = this.baseFontSize / 2; | ||
this.headerGap = this.baseFontSize + 4; | ||
this.indentStep = 12; | ||
this.margins = { | ||
horizontal: 70, | ||
vertical: 50 | ||
}; | ||
// calculated in applyStyle() | ||
this.paraGap = 0; | ||
this.subHeaderGap = 0; | ||
this.headerGap = 0; | ||
this.baseStyle = { | ||
font: EFont.NORM, | ||
fontSize: this.baseFontSize, | ||
fillColor: this.colorMain, | ||
leftMargin: this.margins.horizontal, | ||
fontSize: this.style.font.baseSize, | ||
fillColor: this.style.color.main, | ||
leftMargin: this.style.format.horizontalMargin, | ||
lineGap: 0, | ||
}; | ||
if (style) { | ||
this.style = style; // external style | ||
} | ||
this.applyStyle(); | ||
this.doc = new PDFDocument({ | ||
@@ -56,6 +70,6 @@ bufferPages: true, | ||
margins: { | ||
left: this.margins.horizontal, | ||
right: this.margins.horizontal, | ||
top: this.margins.vertical, | ||
bottom: this.margins.vertical | ||
left: this.style.format.horizontalMargin, | ||
right: this.style.format.horizontalMargin, | ||
top: this.style.format.verticalMargin, | ||
bottom: this.style.format.verticalMargin | ||
} | ||
@@ -84,3 +98,3 @@ }); | ||
if (date) { | ||
this.styledText(date, { font: EFont.NORM, fontSize: 12, fillColor: this.colorDisabled }, { align: 'center' }); | ||
this.styledText(date, { font: EFont.NORM, fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
} | ||
@@ -112,3 +126,3 @@ } | ||
const doc = this.doc; | ||
this.styledText(str, { font: EFont.BOLD, fontSize: this.baseFontSize + 4 - level * 2, lineGap: this.headerGap - level * 3 }, { destination: anchor }); | ||
this.styledText(str, { fillColor: this.style.color.headers, font: EFont.BOLD, fontSize: this.style.font.baseSize + 4 - level * 2, lineGap: this.headerGap - level * 3 }, { destination: anchor }); | ||
let newOutline; | ||
@@ -144,6 +158,6 @@ const outlinesLen = this.docOutlines.length; | ||
subHeader(str) { | ||
this.styledText(str, { font: EFont.BOLD, fontSize: this.baseFontSize, lineGap: this.subHeaderGap }); | ||
this.styledText(str, { fillColor: this.style.color.subHeaders, font: EFont.BOLD, fontSize: this.style.font.baseSize, lineGap: this.subHeaderGap }); | ||
} | ||
indentStart() { | ||
this.pushStyle({ leftMargin: this.indentStep }); | ||
this.pushStyle({ leftMargin: this.style.format.indentStep }); | ||
return this; | ||
@@ -164,3 +178,3 @@ } | ||
description(str, options) { | ||
this.styledText(str, { fillColor: this.colorDisabled }, options); | ||
this.styledText(str, { fillColor: this.style.color.secondary }, options); | ||
} | ||
@@ -182,11 +196,11 @@ dataFields(dataFields) { | ||
dataFields.forEach((field) => { | ||
var _a, _b, _c, _d; | ||
var _a, _b, _c, _d, _e; | ||
const fieldName = `${field.name}${((_a = field.required) !== null && _a !== void 0 ? _a : true) ? '' : '?'}`; | ||
const fieldType = (_b = field.type) === null || _b === void 0 ? void 0 : _b.text; | ||
const fieldType = ((_b = field.type) === null || _b === void 0 ? void 0 : _b.text) ? `${(_c = field.type) === null || _c === void 0 ? void 0 : _c.text};` : undefined; | ||
this.text(fieldName, { continued: fieldType ? true : false }); | ||
if (fieldType) { | ||
this.text(': ', { continued: true }); | ||
this.styledText(`${fieldType};`, { fillColor: this.colorAccent }, { | ||
goTo: (_c = field.type) === null || _c === void 0 ? void 0 : _c.anchor, | ||
underline: ((_d = field.type) === null || _d === void 0 ? void 0 : _d.anchor) ? true : false | ||
this.styledText(fieldType, { fillColor: this.style.color.types }, { | ||
goTo: (_d = field.type) === null || _d === void 0 ? void 0 : _d.anchor, | ||
underline: ((_e = field.type) === null || _e === void 0 ? void 0 : _e.anchor) ? true : false | ||
}); | ||
@@ -197,3 +211,6 @@ } | ||
let nameAndType = fieldName + (fieldType ? `: ${fieldType}` : ''); | ||
this.styledText(`// ${field.description}`, { fillColor: this.colorDisabled }, { x: origX + 12, indent: this.doc.widthOfString(nameAndType) }); | ||
this.styledText(` // ${field.description}`, { fillColor: this.style.color.secondary }, { | ||
x: origX + this.style.format.indentStep, | ||
indent: this.doc.widthOfString(nameAndType) - this.style.format.indentStep | ||
}); | ||
} | ||
@@ -210,3 +227,3 @@ this.doc.x = origX; | ||
this.text('Type: ', { continued: true }); | ||
this.styledText(typeName, { fillColor: this.colorAccent }); | ||
this.styledText(typeName, { fillColor: this.style.color.types }); | ||
} | ||
@@ -216,4 +233,4 @@ enumValues(values) { | ||
this.doc.moveUp(); | ||
const nextLineIndent = this.doc.x + this.indentStep; | ||
const indent = this.doc.widthOfString('Values: ') - this.indentStep; | ||
const nextLineIndent = this.doc.x + this.style.format.indentStep; | ||
const indent = this.doc.widthOfString('Values: ') - this.style.format.indentStep; | ||
values.forEach((value, index, array) => { | ||
@@ -239,3 +256,3 @@ const str = (index < array.length - 1) ? `${value}, ` : value; | ||
if (i > 0) { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.colorDisabled }, () => { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
if (this.pageHeaderNodes[i]) { | ||
@@ -252,2 +269,7 @@ this.text(this.pageHeaderNodes[i], { y: origTop / 2, align: 'right' }); | ||
} | ||
applyStyle() { | ||
this.paraGap = this.style.font.baseSize / 3; | ||
this.subHeaderGap = this.style.font.baseSize / 2; | ||
this.headerGap = this.style.font.baseSize + 4; | ||
} | ||
setStyle(style) { | ||
@@ -254,0 +276,0 @@ var _a; |
{ | ||
"name": "apibake", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "OpenAPI to PDF generator.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -7,14 +7,36 @@ # ApiBake | ||
npm install -g apibake | ||
``` | ||
npm install -g apibake | ||
apibake <openapi.json|.yaml|folder-name> [<file-or-folder2> <file-or-folder3> ...] [<options>] | ||
apibake <openapi.json|.yaml|folder-name> [<file-or-folder2> <file-or-folder3> ...] [<options>] | ||
``` | ||
**Options**: | ||
**Options:** | ||
-out <string>: Output file. | ||
-title <string>: Document title. | ||
-subtitle <string>: Document sub title. | ||
-separate-schemas: When multiple API files parsed create separate schemas section for each file. | ||
-h: Show this help. | ||
``` | ||
-out <string>: Output PDF file name. | ||
-title <string>: Document title. | ||
-subtitle <string>: Document sub title. | ||
-separate-schemas: When multiple API files parsed, create separate schemas section for each. | ||
-style <string>: Style to use. See -export-style. | ||
-export-style: Save document style into style.json for editing. | ||
-h: Show this help. | ||
``` | ||
**Examples:** | ||
Specify title and subtitle for your PDF: | ||
``` | ||
apibake openapi.json -title 'REST API Spec' -subtitle 'created by ApiBake' | ||
``` | ||
Combine several OpenAPI specs into one PDF: | ||
``` | ||
apibake api1.json api2.yaml -title 'REST API Spec' | ||
``` | ||
# MIT License | ||
@@ -21,0 +43,0 @@ |
@@ -19,2 +19,4 @@ #!/usr/bin/env node | ||
separateSchemas: <Arg>{ key: '-separate-schemas', value: false, help: 'When multiple API files parsed, create separate schemas section for each.' }, | ||
style: <Arg>{ key: '-style', value: '', help: 'Path to style.json. See -export-style.' }, | ||
exportStyle: <Arg>{ key: '-export-style', value: false, help: 'Save default document style into style.json for editing.' }, | ||
help: <Arg>{ key: '-h', value: false, help: 'Show this help.' }, | ||
@@ -38,3 +40,3 @@ } | ||
if (args.help.value || argsParser.rest.length === 0) { | ||
if (args.help.value) { | ||
printUsageHelp(); | ||
@@ -44,4 +46,24 @@ return; | ||
let style; | ||
if (args.style.value) { | ||
try { | ||
style = JSON.parse(fs.readFileSync(args.style.value as string, 'utf8')); | ||
} catch (e) { | ||
errorLog(`Error in ${args.style.value}: ${e}`); | ||
return; | ||
} | ||
} | ||
const outputFile = args.output.value as string; | ||
const doc = new PdfWriter(outputFile); | ||
const doc = new PdfWriter(outputFile, style); | ||
if (args.exportStyle.value) { | ||
fs.writeFileSync('style.json', JSON.stringify(doc.style, null, 2)); | ||
} | ||
if (argsParser.rest.length === 0) { | ||
log('No .json or .yaml files specified.\n'); | ||
return; | ||
} | ||
doc.addTitlePage( | ||
@@ -48,0 +70,0 @@ args.title.value as string, |
@@ -33,2 +33,22 @@ import fs from 'fs'; | ||
export class PdfWriter { | ||
// configurable PDF style | ||
style = { | ||
color: { | ||
main: '#333333', | ||
secondary: '#6B7B8E', | ||
types: '#8A3324', | ||
headers: '#2A4D69', | ||
subHeaders: '#4B86B4', | ||
}, | ||
font: { | ||
baseSize: 10, | ||
}, | ||
format: { | ||
indentStep: 12, | ||
horizontalMargin: 70, | ||
verticalMargin: 50 | ||
} | ||
} | ||
private doc; | ||
@@ -51,27 +71,21 @@ | ||
private colorMain = 'black'; | ||
private colorAccent = 'blue'; | ||
private colorDisabled = 'grey'; | ||
// calculated in applyStyle() | ||
private paraGap = 0; | ||
private subHeaderGap = 0; | ||
private headerGap = 0; | ||
private baseFontSize = 10; | ||
private paraGap = this.baseFontSize / 3; | ||
private subHeaderGap = this.baseFontSize / 2; | ||
private headerGap = this.baseFontSize + 4; | ||
private indentStep = 12; | ||
private margins = { | ||
horizontal: 70, | ||
vertical: 50 | ||
}; | ||
private baseStyle: TextStyle = { | ||
font: EFont.NORM, | ||
fontSize: this.baseFontSize, | ||
fillColor: this.colorMain, | ||
leftMargin: this.margins.horizontal, | ||
fontSize: this.style.font.baseSize, | ||
fillColor: this.style.color.main, | ||
leftMargin: this.style.format.horizontalMargin, | ||
lineGap: 0, | ||
}; | ||
constructor(outputFilePath: string, style?: any) { | ||
if (style) { | ||
this.style = style; // external style | ||
} | ||
this.applyStyle(); | ||
constructor(outputFilePath: string) { | ||
this.doc = new PDFDocument({ | ||
@@ -81,6 +95,6 @@ bufferPages: true, | ||
margins: { | ||
left: this.margins.horizontal, | ||
right: this.margins.horizontal, | ||
top: this.margins.vertical, | ||
bottom: this.margins.vertical | ||
left: this.style.format.horizontalMargin, | ||
right: this.style.format.horizontalMargin, | ||
top: this.style.format.verticalMargin, | ||
bottom: this.style.format.verticalMargin | ||
} | ||
@@ -114,3 +128,3 @@ }); | ||
if (date) { | ||
this.styledText(date, { font: EFont.NORM, fontSize: 12, fillColor: this.colorDisabled }, { align: 'center' }); | ||
this.styledText(date, { font: EFont.NORM, fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
} | ||
@@ -149,3 +163,3 @@ } | ||
this.styledText(str, | ||
{ font: EFont.BOLD, fontSize: this.baseFontSize + 4 - level * 2, lineGap: this.headerGap - level * 3 }, | ||
{ fillColor: this.style.color.headers, font: EFont.BOLD, fontSize: this.style.font.baseSize + 4 - level * 2, lineGap: this.headerGap - level * 3 }, | ||
{ destination: anchor } | ||
@@ -185,7 +199,7 @@ ); | ||
subHeader(str: string) { | ||
this.styledText(str, { font: EFont.BOLD, fontSize: this.baseFontSize, lineGap: this.subHeaderGap }); | ||
this.styledText(str, { fillColor: this.style.color.subHeaders, font: EFont.BOLD, fontSize: this.style.font.baseSize, lineGap: this.subHeaderGap }); | ||
} | ||
indentStart(): PdfWriter { | ||
this.pushStyle({ leftMargin: this.indentStep }); | ||
this.pushStyle({ leftMargin: this.style.format.indentStep }); | ||
return this; | ||
@@ -210,3 +224,3 @@ } | ||
description(str: string, options?: TextOptions) { | ||
this.styledText(str, { fillColor: this.colorDisabled }, options); | ||
this.styledText(str, { fillColor: this.style.color.secondary }, options); | ||
} | ||
@@ -233,3 +247,3 @@ | ||
const fieldName = `${field.name}${(field.required ?? true) ? '':'?'}` | ||
const fieldType = field.type?.text; | ||
const fieldType = field.type?.text ? `${field.type?.text};` : undefined; | ||
this.text(fieldName, { continued: fieldType ? true : false }); | ||
@@ -239,3 +253,3 @@ | ||
this.text(': ', { continued: true }); | ||
this.styledText(`${fieldType};`, { fillColor: this.colorAccent }, { | ||
this.styledText(fieldType, { fillColor: this.style.color.types }, { | ||
goTo: field.type?.anchor, | ||
@@ -249,3 +263,6 @@ underline: field.type?.anchor ? true : false | ||
let nameAndType = fieldName + (fieldType ? `: ${fieldType}` : ''); | ||
this.styledText(`// ${field.description}`, { fillColor: this.colorDisabled }, { x: origX + 12, indent: this.doc.widthOfString(nameAndType) }); | ||
this.styledText(` // ${field.description}`, { fillColor: this.style.color.secondary }, { | ||
x: origX + this.style.format.indentStep, | ||
indent: this.doc.widthOfString(nameAndType) - this.style.format.indentStep | ||
}); | ||
} | ||
@@ -264,3 +281,3 @@ this.doc.x = origX; | ||
this.text('Type: ', { continued: true }); | ||
this.styledText(typeName, { fillColor: this.colorAccent }); | ||
this.styledText(typeName, { fillColor: this.style.color.types }); | ||
} | ||
@@ -271,4 +288,4 @@ | ||
this.doc.moveUp(); | ||
const nextLineIndent = this.doc.x + this.indentStep; | ||
const indent = this.doc.widthOfString('Values: ') - this.indentStep; | ||
const nextLineIndent = this.doc.x + this.style.format.indentStep; | ||
const indent = this.doc.widthOfString('Values: ') - this.style.format.indentStep; | ||
@@ -299,3 +316,3 @@ values.forEach((value, index, array) => { | ||
if (i > 0) { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.colorDisabled }, () => { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
if (this.pageHeaderNodes[i]) { | ||
@@ -315,2 +332,8 @@ this.text(this.pageHeaderNodes[i], { y: origTop / 2, align: 'right' }); | ||
private applyStyle() { | ||
this.paraGap = this.style.font.baseSize / 3; | ||
this.subHeaderGap = this.style.font.baseSize / 2; | ||
this.headerGap = this.style.font.baseSize + 4; | ||
} | ||
private setStyle(style: TextStyle) { | ||
@@ -317,0 +340,0 @@ this.doc.font(this.fonts[style.font ?? 0]).fontSize(style.fontSize).fillColor(style.fillColor); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
66847
1688
62