Comparing version 0.1.20 to 0.1.21
@@ -8,13 +8,11 @@ "use strict"; | ||
const fs_1 = __importDefault(require("fs")); | ||
const obj_utils_1 = require("./obj-utils"); | ||
const PDFDocument = require('pdfkit'); | ||
class Font { | ||
constructor(face, style) { | ||
this.face = face; | ||
this.style = style; | ||
} | ||
} | ||
; | ||
var EFont; | ||
(function (EFont) { | ||
EFont[EFont["NORM"] = 0] = "NORM"; | ||
EFont[EFont["BOLD"] = 1] = "BOLD"; | ||
EFont[EFont["ITALIC"] = 2] = "ITALIC"; | ||
EFont[EFont["BOLD_ITALIC"] = 3] = "BOLD_ITALIC"; | ||
EFont[EFont["MONOSPACED"] = 4] = "MONOSPACED"; | ||
})(EFont || (EFont = {})); | ||
; | ||
class PdfWriter { | ||
@@ -39,2 +37,12 @@ constructor(outputFilePath, style) { | ||
baseSize: 10, | ||
main: { | ||
norm: new Font('Helvetica'), | ||
bold: new Font('Helvetica-Bold'), | ||
italic: new Font('Helvetica-Oblique'), | ||
}, | ||
mono: { | ||
norm: new Font('Courier'), | ||
bold: new Font('Courier-Bold'), | ||
italic: new Font('Courier-Oblique') | ||
} | ||
}, | ||
@@ -52,13 +60,9 @@ format: { | ||
this.styleStack = []; | ||
this.fonts = [ | ||
'Helvetica', | ||
'Helvetica-Bold', | ||
'Helvetica-Oblique', | ||
'Helvetica-BoldOblique', | ||
'Courier-Bold' | ||
]; | ||
this.headerGap = 0.7; | ||
this.paraGap = 0.5; | ||
if (style) { | ||
this.style = (0, obj_utils_1.deepOverride)(this.style, style); | ||
} | ||
this.baseStyle = { | ||
font: EFont.NORM, | ||
font: this.style.font.main.norm, | ||
fontSize: this.style.font.baseSize, | ||
@@ -69,5 +73,2 @@ fillColor: this.style.color.main, | ||
}; | ||
if (style) { | ||
this.style = style; // external style | ||
} | ||
this.doc = new PDFDocument({ | ||
@@ -99,10 +100,10 @@ bufferPages: true, | ||
this.doc.y = this.doc.page.height * 0.3; | ||
this.text(title, { font: EFont.BOLD, fontSize: 20 }, { align: 'center' }); | ||
this.lineBreak(1); | ||
this.text(title, { font: this.style.font.main.bold, fontSize: 20 }, { align: 'center' }); | ||
if (subtitle) { | ||
this.text(subtitle, { font: EFont.NORM, fontSize: 14 }, { align: 'center' }); | ||
this.lineBreak(0.5); | ||
this.text(subtitle, { fontSize: 14 }, { align: 'center' }); | ||
} | ||
if (date) { | ||
this.text(date, { font: EFont.NORM, fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
this.lineBreak(3); | ||
this.text(date, { fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
} | ||
@@ -150,3 +151,3 @@ } | ||
header(level, str, anchor) { | ||
this.withStyle({ fillColor: this.style.color.headers, font: EFont.BOLD, fontSize: this.style.font.baseSize + 4 - level * 2 }, () => { | ||
this.withStyle({ fillColor: this.style.color.headers, font: this.style.font.main.bold, fontSize: this.style.font.baseSize + 4 - level * 2 }, () => { | ||
this.text(str, {}, { destination: anchor }); | ||
@@ -166,3 +167,3 @@ this.lineBreak(this.headerGap); | ||
}; | ||
this.withStyle({ font: EFont.BOLD, fontSize }, () => { | ||
this.withStyle({ font: this.style.font.main.bold, fontSize }, () => { | ||
var _a; | ||
@@ -217,3 +218,3 @@ const width = this.doc.widthOfString(method); | ||
subHeader(str) { | ||
this.withStyle({ fillColor: this.style.color.subHeaders, font: EFont.BOLD, fontSize: this.style.font.baseSize }, () => { | ||
this.withStyle({ fillColor: this.style.color.subHeaders, font: this.style.font.main.bold, fontSize: this.style.font.baseSize }, () => { | ||
this.text(str); | ||
@@ -273,5 +274,5 @@ this.lineBreak(this.headerGap); | ||
example(name, body) { | ||
this.text(`Example "${name}":`, { font: EFont.BOLD }); | ||
this.text(`Example "${name}":`, { font: this.style.font.main.bold }); | ||
this.lineBreak(this.paraGap); | ||
this.text(body, { fillColor: this.style.color.secondary, fontSize: this.style.font.baseSize - 2, font: EFont.MONOSPACED }); | ||
this.text(body, { fillColor: this.style.color.secondary, fontSize: this.style.font.baseSize - 2, font: this.style.font.mono.bold }); | ||
} | ||
@@ -307,3 +308,3 @@ enumValues(values) { | ||
if (i > 0) { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
this.withStyle({ fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
if (this.pageHeaderNodes[i]) { | ||
@@ -322,3 +323,3 @@ this.text(this.pageHeaderNodes[i], {}, { y: origTop / 2, align: 'right' }); | ||
var _a; | ||
this.doc.font(this.fonts[(_a = style.font) !== null && _a !== void 0 ? _a : 0]).fontSize(style.fontSize).fillColor(style.fillColor); | ||
this.doc.font(style.font.face, ((_a = style.font) === null || _a === void 0 ? void 0 : _a.style) || undefined).fontSize(style.fontSize).fillColor(style.fillColor); | ||
} | ||
@@ -325,0 +326,0 @@ resetStyle() { |
{ | ||
"name": "apibake", | ||
"version": "0.1.20", | ||
"version": "0.1.21", | ||
"description": "Convert OpenAPI spec to PDF.", | ||
@@ -32,3 +32,3 @@ "keywords": [ | ||
"new-ver": "npm version patch && tsc && git commit -am 'publish to npm' && git push origin", | ||
"dev": "ts-node ./src/index.ts test-data/v3.0 test-data/v3.1 test-data/private --title 'REST API Spec' --subtitle 'created by ApiBake'", | ||
"dev": "ts-node ./src/index.ts test-data/v3.0 test-data/v3.1 test-data/private --title 'REST API Spec' --subtitle 'created with ApiBake'", | ||
"test": "ts-node ./src/test.ts", | ||
@@ -35,0 +35,0 @@ "prod": "node ./dist/index.js" |
@@ -5,4 +5,6 @@ # ApiBake | ||
**Quick start:** | ||
## Quick Start | ||
Node.js 16+ required. | ||
``` | ||
@@ -26,3 +28,3 @@ npm install -g apibake | ||
**Examples:** | ||
## Examples | ||
@@ -32,3 +34,3 @@ Specify title and subtitle for your PDF: | ||
``` | ||
apibake openapi.json --title 'REST API Spec' --subtitle 'created by ApiBake' | ||
apibake openapi.json --title 'REST API Spec' --subtitle 'created with ApiBake' | ||
``` | ||
@@ -43,9 +45,86 @@ | ||
Custom config (colors, margins, font size): | ||
## Custom config: fonts, colors, page margins. | ||
To modify default apibake config - first export it into a file: | ||
``` | ||
apibake --export-config | ||
apibake api1.json --title 'REST API Spec' --config apibake-config.json | ||
``` | ||
Modify apibake-config.json and tell apibake to use it: | ||
``` | ||
apibake openapi.json --config apibake-config.json | ||
``` | ||
PDF default fonts can be specified by their names: | ||
- Courier | ||
- Courier-Bold | ||
- Courier-Oblique | ||
- Courier-BoldOblique | ||
- Helvetica | ||
- Helvetica-Bold | ||
- Helvetica-Oblique | ||
- Helvetica-BoldOblique | ||
- Symbol | ||
- Times-Roman | ||
- Times-Bold | ||
- Times-Italic | ||
- Times-BoldItalic | ||
- ZapfDingbats | ||
Alternatively, external font files can be specified. Supported font formats: TrueType (.ttf), OpenType (.otf), WOFF, WOFF2, TrueType Collection (.ttc), and Datafork TrueType (.dfont) fonts. | ||
Example: | ||
``` | ||
{ | ||
"font": { | ||
"baseSize": 10, | ||
"main": { | ||
"norm": { | ||
"face": "fonts/Roboto-Regular.ttf" | ||
}, | ||
"bold": { | ||
"face": "fonts/Roboto-Bold.ttf" | ||
}, | ||
"italic": { | ||
"face": "fonts/Roboto-Italic.ttf" | ||
} | ||
}, | ||
"mono": { | ||
"norm": { | ||
"face": "Courier" | ||
}, | ||
"bold": { | ||
"face": "Courier-Bold" | ||
}, | ||
"italic": { | ||
"face": "Courier-Oblique" | ||
} | ||
} | ||
}, | ||
... | ||
} | ||
``` | ||
Note: if font file is a collection (.ttc) - then font style must be also specified. Example: | ||
``` | ||
"font": { | ||
"main": { | ||
"norm": { | ||
"face": "fonts/Roboto.ttc", | ||
"style": "Roboto-Regular" | ||
}, | ||
"bold": { | ||
"face": "fonts/Roboto.ttc", | ||
"style": "Roboto-Bold" | ||
}, | ||
}, | ||
... | ||
} | ||
``` | ||
# MIT License | ||
@@ -52,0 +131,0 @@ |
import fs from 'fs'; | ||
import { DataField } from './openapi-parser'; | ||
import { deepOverride } from './obj-utils'; | ||
const PDFDocument = require('pdfkit'); | ||
interface TextStyle { | ||
font?: EFont; | ||
class Font { | ||
constructor( | ||
public face: string, | ||
public style?: string | ||
) {} | ||
} | ||
interface ITextStyle { | ||
font?: Font; | ||
fontSize?: number; | ||
@@ -13,3 +21,3 @@ fillColor?: string; | ||
interface TextOptions { | ||
interface ITextOptions { | ||
continued?: boolean; | ||
@@ -26,10 +34,2 @@ lineBreak?: boolean; | ||
enum EFont { | ||
NORM = 0, | ||
BOLD = 1, | ||
ITALIC = 2, | ||
BOLD_ITALIC = 3, | ||
MONOSPACED = 4 | ||
}; | ||
export class PdfWriter { | ||
@@ -45,3 +45,3 @@ | ||
subHeaders: '#4B86B4', | ||
getMethod: '#4A90E2', | ||
@@ -56,2 +56,12 @@ putMethod: '#6B8E23', | ||
baseSize: 10, | ||
main: { | ||
norm: new Font('Helvetica'), | ||
bold: new Font('Helvetica-Bold'), | ||
italic: new Font('Helvetica-Oblique'), | ||
}, | ||
mono: { | ||
norm: new Font('Courier'), | ||
bold: new Font('Courier-Bold'), | ||
italic: new Font('Courier-Oblique') | ||
} | ||
}, | ||
@@ -63,3 +73,3 @@ format: { | ||
} | ||
} | ||
}; | ||
@@ -74,28 +84,22 @@ private doc; | ||
private docOutlines: any[] = []; | ||
private styleStack: TextStyle[] = []; | ||
private styleStack: ITextStyle[] = []; | ||
private fonts = [ | ||
'Helvetica', | ||
'Helvetica-Bold', | ||
'Helvetica-Oblique', | ||
'Helvetica-BoldOblique', | ||
'Courier-Bold' | ||
]; | ||
private headerGap = 0.7; | ||
private paraGap = 0.5; | ||
private baseStyle: TextStyle = { | ||
font: EFont.NORM, | ||
fontSize: this.style.font.baseSize, | ||
fillColor: this.style.color.main, | ||
leftMargin: this.style.format.horizontalMargin, | ||
lineGap: 0, | ||
}; | ||
private baseStyle: ITextStyle; | ||
constructor(outputFilePath?: string, style?: any) { | ||
if (style) { | ||
this.style = style; // external style | ||
this.style = deepOverride(this.style, style); | ||
} | ||
this.baseStyle = { | ||
font: this.style.font.main.norm, | ||
fontSize: this.style.font.baseSize, | ||
fillColor: this.style.color.main, | ||
leftMargin: this.style.format.horizontalMargin, | ||
lineGap: 0, | ||
}; | ||
this.doc = new PDFDocument({ | ||
@@ -130,12 +134,12 @@ bufferPages: true, | ||
this.doc.y = this.doc.page.height * 0.3; | ||
this.text(title, { font: EFont.BOLD, fontSize: 20 }, { align: 'center' }); | ||
this.lineBreak(1); | ||
this.text(title, { font: this.style.font.main.bold, fontSize: 20 }, { align: 'center' }); | ||
if (subtitle) { | ||
this.text(subtitle, { font: EFont.NORM, fontSize: 14 }, { align: 'center' }); | ||
this.lineBreak(0.5); | ||
this.text(subtitle, { fontSize: 14 }, { align: 'center' }); | ||
} | ||
if (date) { | ||
this.text(date, { font: EFont.NORM, fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
this.lineBreak(3); | ||
this.text(date, { fontSize: 12, fillColor: this.style.color.secondary }, { align: 'center' }); | ||
} | ||
@@ -165,3 +169,3 @@ } | ||
text(str: string, style?: TextStyle, options?: TextOptions): PdfWriter { | ||
text(str: string, style?: ITextStyle, options?: ITextOptions): PdfWriter { | ||
if (style && Object.keys(style).length > 0) { | ||
@@ -175,3 +179,3 @@ this.withStyle(style, () => this.textImpl(str, options)); | ||
private textImpl(str: string, options?: TextOptions): PdfWriter { | ||
private textImpl(str: string, options?: ITextOptions): PdfWriter { | ||
const style = this.currentStyle(); | ||
@@ -190,3 +194,3 @@ const styledOpt = { lineGap: style.lineGap, ...options }; | ||
header(level: number, str: string, anchor?: string) { | ||
this.withStyle({ fillColor: this.style.color.headers, font: EFont.BOLD, fontSize: this.style.font.baseSize + 4 - level * 2 }, () => { | ||
this.withStyle({ fillColor: this.style.color.headers, font: this.style.font.main.bold, fontSize: this.style.font.baseSize + 4 - level * 2 }, () => { | ||
this.text(str, {}, { destination: anchor }); | ||
@@ -209,3 +213,3 @@ this.lineBreak(this.headerGap); | ||
this.withStyle({ font: EFont.BOLD, fontSize }, () => { | ||
this.withStyle({ font: this.style.font.main.bold, fontSize }, () => { | ||
const width = this.doc.widthOfString(method); | ||
@@ -264,3 +268,3 @@ const height = this.doc.heightOfString(method); | ||
subHeader(str: string) { | ||
this.withStyle({ fillColor: this.style.color.subHeaders, font: EFont.BOLD, fontSize: this.style.font.baseSize }, () => { | ||
this.withStyle({ fillColor: this.style.color.subHeaders, font: this.style.font.main.bold, fontSize: this.style.font.baseSize }, () => { | ||
this.text(str, ); | ||
@@ -328,5 +332,5 @@ this.lineBreak(this.headerGap); | ||
example(name: string, body: string) { | ||
this.text(`Example "${name}":`, { font: EFont.BOLD }); | ||
this.text(`Example "${name}":`, { font: this.style.font.main.bold }); | ||
this.lineBreak(this.paraGap); | ||
this.text(body, { fillColor: this.style.color.secondary, fontSize: this.style.font.baseSize - 2, font: EFont.MONOSPACED }); | ||
this.text(body, { fillColor: this.style.color.secondary, fontSize: this.style.font.baseSize - 2, font: this.style.font.mono.bold }); | ||
} | ||
@@ -367,3 +371,3 @@ | ||
if (i > 0) { | ||
this.withStyle({ font: EFont.NORM, fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
this.withStyle({ fontSize: 9, fillColor: this.style.color.secondary }, () => { | ||
if (this.pageHeaderNodes[i]) { | ||
@@ -383,4 +387,4 @@ this.text(this.pageHeaderNodes[i], {}, { y: origTop / 2, align: 'right' }); | ||
private setStyle(style: TextStyle) { | ||
this.doc.font(this.fonts[style.font ?? 0]).fontSize(style.fontSize).fillColor(style.fillColor); | ||
private setStyle(style: ITextStyle) { | ||
this.doc.font(style.font!.face, style.font?.style || undefined).fontSize(style.fontSize).fillColor(style.fillColor); | ||
} | ||
@@ -393,3 +397,3 @@ | ||
private withStyle(style: TextStyle, fn: (style: TextStyle) => void) { | ||
private withStyle(style: ITextStyle, fn: (style: ITextStyle) => void) { | ||
const newStyle = this.pushStyle(style); | ||
@@ -400,3 +404,3 @@ fn(newStyle); | ||
private pushStyle(style: TextStyle): TextStyle { | ||
private pushStyle(style: ITextStyle): ITextStyle { | ||
const mergedStyle = { ...this.currentStyle(), ...style }; | ||
@@ -410,3 +414,3 @@ mergedStyle.leftMargin = (this.currentStyle().leftMargin ?? 0) + (style.leftMargin ?? 0); // nested indent | ||
private popStyle(): TextStyle { | ||
private popStyle(): ITextStyle { | ||
this.styleStack.pop(); | ||
@@ -419,3 +423,3 @@ const prevStyle = this.currentStyle(); | ||
private currentStyle(): TextStyle { | ||
private currentStyle(): ITextStyle { | ||
return (this.styleStack.length > 0) | ||
@@ -422,0 +426,0 @@ ? this.styleStack[this.styleStack.length-1] |
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
76278
19
1869
147