From 0970a7f03f6c1a2fbd9b74a39c9d6f4f1a6083cb Mon Sep 17 00:00:00 2001 From: Eric Pattison Date: Wed, 18 Jul 2018 15:59:03 -0600 Subject: [PATCH] Adding support for PDFKit lists element types --- lib/elements/list.ts | 66 +++++++++++++++++++ tests/list-with-options/index.ts | 25 +++++++ tests/list-with-options/template.yml | 15 +++++ tests/list-with-variables/index.ts | 54 +++++++++++++++ .../list-with-variables/template-complex.yml | 7 ++ tests/list-with-variables/template-simple.yml | 7 ++ tests/list/index.ts | 20 ++++++ tests/list/template.yml | 8 +++ 8 files changed, 202 insertions(+) create mode 100644 lib/elements/list.ts create mode 100644 tests/list-with-options/index.ts create mode 100644 tests/list-with-options/template.yml create mode 100644 tests/list-with-variables/index.ts create mode 100644 tests/list-with-variables/template-complex.yml create mode 100644 tests/list-with-variables/template-simple.yml create mode 100644 tests/list/index.ts create mode 100644 tests/list/template.yml diff --git a/lib/elements/list.ts b/lib/elements/list.ts new file mode 100644 index 0000000..6737058 --- /dev/null +++ b/lib/elements/list.ts @@ -0,0 +1,66 @@ +/* + * This file is a part of yipt + * Copyright (C) 2017 Webnium (https://webnium.co.jp/) + * License under MIT license https://opensource.org/licenses/MIT + */ + +"use strict"; +import {Offset, PDFDocument, TemplateElement} from "../index"; +import Types, {bind} from "../evaluators"; +import {isNumber} from "util"; + +interface ListElement extends TemplateElement { + type: "list"; + items: any[]; + content: string; + options?: any; +} + +const OPTIONS = { + lineBreak: Types.boolean, + width: Types.number, + height: Types.number, + align: Types.enum(["justify", "left", "center", "right"]), + ellipsis: Types.string, + columns: Types.number, + columnGap: Types.number, + indent: Types.number, + paragraphGap: Types.number, + lineGap: Types.number, + wordSpacing: Types.number, + characterSpacing: Types.number, + fill: Types.boolean, + stroke: Types.boolean, + link: Types.string, + underline: Types.boolean, + strike: Types.boolean, + continued: Types.boolean, + features: Types.stringArray, + bulletRadius: Types.number, + textIndent: Types.number, + bulletIndent: Types.number, + listType: Types.string +}; + +export = async function (doc: PDFDocument, content: TemplateElement, vars: any, offset: Offset, _bindings: any) { + const element = content as ListElement; + const bindings = bind(doc, _bindings, offset); + const text = Types.array(element.content, vars); + + const options = Types.object(OPTIONS)(element.options, vars, bindings); + const top = Types.number(element.top, vars, bindings); + const left = Types.number(element.left, vars, bindings); + + if (isNumber(top) && isNumber(left)) { + doc.list(text, left + offset.left, top + offset.top, options); + } else if (isNumber(top)) { + doc.list(text, undefined, top + offset.top, options); + } else if (isNumber(left)) { + doc.list(text, left + offset.left, undefined, options); + } else if (options !== undefined) { + doc.list(text, options); + } else { + doc.list(text); + } + +}; diff --git a/tests/list-with-options/index.ts b/tests/list-with-options/index.ts new file mode 100644 index 0000000..fffe502 --- /dev/null +++ b/tests/list-with-options/index.ts @@ -0,0 +1,25 @@ +"use strict" + +import * as PDFKit from "pdfkit"; +import * as sinon from "sinon"; +import * as chai from "chai"; +import * as sinonChai from "sinon-chai"; +import yipt from "../../lib/index"; + +chai.should(); +chai.use(sinonChai); + +describe("list-with-options", () => { + it("should render pdf.", async () => { + const doc = new PDFKit({margin: 0}); + const list = sinon.spy(doc, "list"); + await yipt.render(doc, __dirname + "/template.yml"); + + list.should.have.been.calledWith(["item 1", "item 2", "item 3"], 20, 10, { + width: 200, + indent: 10, + bulletRadius: 2, + textIndent: 20 + }) + }) +}) \ No newline at end of file diff --git a/tests/list-with-options/template.yml b/tests/list-with-options/template.yml new file mode 100644 index 0000000..13bcb2d --- /dev/null +++ b/tests/list-with-options/template.yml @@ -0,0 +1,15 @@ +yipt: + version: "1.0" + content: + - type: list + top: 10 + left: 20 + content: + - "item 1" + - "item 2" + - "item 3" + options: + width: 200 + indent: 10 + bulletRadius: 2 + textIndent: 20 \ No newline at end of file diff --git a/tests/list-with-variables/index.ts b/tests/list-with-variables/index.ts new file mode 100644 index 0000000..8fb5b52 --- /dev/null +++ b/tests/list-with-variables/index.ts @@ -0,0 +1,54 @@ +"use strict"; + +import * as PDFKit from "pdfkit"; +import * as sinon from "sinon"; +import * as chai from "chai"; +import * as sinonChai from "sinon-chai"; +import yipt from "../../lib/index"; + +chai.should(); +chai.use(sinonChai); + +describe("list with variables", () => { + it("should render pdf.", async () => { + const doc = new PDFKit(); + const list = sinon.spy(doc, "list"); + const data = { + name: "data", + items: ["item 1", "item 2", "item 3"] + }; + + await yipt.render(doc, __dirname + "/template-simple.yml", data); + + list.should.have.been.calledWith(data.items); + }) +}) + +describe("List of objects", () => { + it("should render pdf.", async () => { + const doc = new PDFKit(); + const list = sinon.spy(doc, "list"); + const data = { + name: "data", + items: [{ + name: "item 1", + item: { + name: "fizz" + } + }, { + name: "item 2", + item: { + name: "buzz" + } + }, { + name: "item 3", + item: { + name: "fizzbuzz" + } + }] + } + + await yipt.render(doc, __dirname + "/template-complex.yml", data); + list.should.have.been.calledWith(["item 1 fizz", "item 2 buzz", "item 3 fizzbuzz"]) + }) +}) \ No newline at end of file diff --git a/tests/list-with-variables/template-complex.yml b/tests/list-with-variables/template-complex.yml new file mode 100644 index 0000000..8953c97 --- /dev/null +++ b/tests/list-with-variables/template-complex.yml @@ -0,0 +1,7 @@ +yipt: + version: "1.0" + content: + - type: text + content: "{name}" + - type: list + content: "[items].(name & ' ' & item.name)" \ No newline at end of file diff --git a/tests/list-with-variables/template-simple.yml b/tests/list-with-variables/template-simple.yml new file mode 100644 index 0000000..e80fe59 --- /dev/null +++ b/tests/list-with-variables/template-simple.yml @@ -0,0 +1,7 @@ +yipt: + version: "1.0" + content: + - type: text + content: "{name}" + - type: list + content: "{items}" \ No newline at end of file diff --git a/tests/list/index.ts b/tests/list/index.ts new file mode 100644 index 0000000..155f573 --- /dev/null +++ b/tests/list/index.ts @@ -0,0 +1,20 @@ +"use strict"; + +import * as PDFKit from "pdfkit"; +import * as sinon from "sinon"; +import * as chai from "chai"; +import * as sinonChai from "sinon-chai"; +import yipt from "../../lib/index"; + +chai.should(); +chai.use(sinonChai); + +describe("list", () => { + it("should render pdf.", async () => { + const doc = new PDFKit({margin:0}); + const list = sinon.spy(doc, "list"); + await yipt.render(doc, __dirname + "/template.yml"); + + list.getCall(0).should.have.been.calledWith(["Hello", "World", "!"]) + }) +}) \ No newline at end of file diff --git a/tests/list/template.yml b/tests/list/template.yml new file mode 100644 index 0000000..48142b4 --- /dev/null +++ b/tests/list/template.yml @@ -0,0 +1,8 @@ +yipt: + version: "1.0" + content: + - type: list + content: + - "Hello" + - "World" + - "!" \ No newline at end of file