All files / src/plugins SvgToPdfExportPlugin.ts

20.69% Statements 12/58
0% Branches 0/20
10% Functions 1/10
20.69% Lines 12/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113  1x 1x 1x 1x 1x 1x   1x                                     1x 1x     1x                   1x           1x                                                                                                                                  
// We have to use a local file and let babel ignore it, until pdfkit is ported to ES6
var PDFDocument = require("../pdfkit.js");
var fs = require("fs");
let path = require("path");
let mkdirp = require("mkdirp");
import * as SVGtoPDF from "svg-to-pdfkit";
import * as _ from "lodash";
import { IAsyncArgdownPlugin, IAsyncRequestHandler } from "../IAsyncArgdownPlugin";
import { IArgdownRequest, IRequestHandler, ArgdownPluginError } from "@argdown/core";
import { ISvgResponse } from "./DotToSvgExportPlugin";
import { IFileRequest } from "../IFileRequest";
import { IFileNameProvider } from "./SaveAsFilePlugin";
 
export interface IPdfSettings {
  outputDir?: string;
}
export interface IPdfRequest {
  pdf?: IPdfSettings;
}
export interface ISvgToPdfSettings {
  outputDir?: string;
  format?: string;
  fileName?: string | IFileNameProvider;
}
export interface ISvgToPdfRequest extends IArgdownRequest {
  svgToPdf?: ISvgToPdfSettings;
}
export class SvgToPdfExportPlugin implements IAsyncArgdownPlugin {
  name = "SvgToPdfExportPlugin";
  defaults: ISvgToPdfSettings;
  constructor(config?: ISvgToPdfSettings) {
    this.defaults = _.defaultsDeep({}, config, {
      outputDir: "./pdf",
      format: "svg"
    });
  }
  getSettings(request: IArgdownRequest): ISvgToPdfSettings {
    const r = <ISvgToPdfRequest>request;
    r.svgToPdf = r.svgToPdf || {};
    return r.svgToPdf;
  }
  prepare: IRequestHandler = (request, response) => {
    if (!(<ISvgResponse>response).svg) {
      throw new ArgdownPluginError(this.name, "Missing svg field in response.");
    }
    _.defaults(this.getSettings(request), this.defaults);
  };
  runAsync: IAsyncRequestHandler = async (request, response) => {
    const settings = this.getSettings(request);
    let fileName = "default";
    let outputDir = settings.outputDir;
    const fileRequest = <IFileRequest>request;
    const pdfRequest = <IPdfRequest>request;
    if (fileRequest.outputDir) {
      fileName = this.getFileName(fileRequest.outputDir);
      outputDir = path.dirname(fileRequest.outputDir);
    } else if (pdfRequest.pdf && pdfRequest.pdf.outputDir) {
      fileName = this.getFileName(pdfRequest.pdf.outputDir);
      outputDir = path.dirname(pdfRequest.pdf.outputDir);
    } else if (_.isFunction(settings.fileName)) {
      fileName = settings.fileName.call(this, request, response);
    } else if (_.isString(settings.fileName)) {
      fileName = settings.fileName;
    } else if (fileRequest.inputPath) {
      fileName = this.getFileName(fileRequest.inputPath);
    }
    const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
    const filePath = absoluteOutputDir + "/" + fileName + ".pdf";
    await new Promise((resolve, reject) => {
      mkdirp(absoluteOutputDir, function(err: Error) {
        if (err) {
          reject(err);
        }
        resolve();
      });
    });
    const svgResponse = <ISvgResponse>response;
    const doc = new PDFDocument(settings);
    SVGtoPDF(doc, svgResponse.svg, 0, 0, settings);
    await this.savePdfToFile(doc, filePath);
  };
  // https://github.com/devongovett/pdfkit/issues/265
  async savePdfToFile(pdf: any, fileName: string) {
    return new Promise(resolve => {
      // To determine when the PDF has finished being written successfully
      // we need to confirm the following 2 conditions:
      //
      //   1. The write stream has been closed
      //   2. PDFDocument.end() was called syncronously without an error being thrown
 
      let pendingStepCount = 2;
 
      const stepFinished = () => {
        if (--pendingStepCount == 0) {
          resolve();
        }
      };
 
      const writeStream = fs.createWriteStream(fileName);
      writeStream.on("close", stepFinished);
      pdf.pipe(writeStream);
 
      pdf.end();
 
      stepFinished();
    });
  }
  getFileName(file: string): string {
    let extension = path.extname(file);
    return path.basename(file, extension);
  }
}