Cluedo Detective Notes

I made a pdf of Cluedo Detective Notes which you can download from here. Although it has 16 pages, you don’t need to print all of them. Just print out the number of pages you actually need.

In order to create this file I piggy backed of a private web app that I have been building which uses “PDF Kit”.

For reference the source code of the parts of PDF kit that do this are as follows:-

Firstly the main production text. This first function is loaded by an api server serving the request. params.position is a tweek to determing how many pages to print via a labeling system that we were piggy backing off to allow user selection. You can see how limit is calculated as it prints 4 detective notes per page. doc is the result of calling doc = new PDFModules.Label(res) where res is the http response

(function() {
  'use strict';

  module.exports = async function( params, doc) {
    doc.info.Title = 'Cleudo Labels ';
    doc.options.margins = { top: 28, bottom: 28, left: 28, right: 28 }
    let position = 0;
    const limit = (params.position + 1) * 4;
    for(let i= 0; i < limit; i++) {
      position = doc.labelContext('four', position);
      doc.font('Helvetica-Bold').fontSize(12);
      doc.text('D', 40, 10 ,{continued: true , baseline: 'alphabetic'}).fontSize(10).text('ETECTIVE',{continued:true})
        .fontSize(12).text(' N', {continued: true}).fontSize(10).text('OTES');

      doc.text('Suspected Persons',0,30 ).moveDown(0.8);
      doc.font('Helvetica').text('Col Mustard').text('Prof Plum').text('Rev Green').text('Mrs Peacock').text('Miss Scarlett').text('Mrs White');
      doc.moveDown(1.5).font('Helvetica-Bold').text('Probable Implements').moveDown(0.8);
      doc.font('Helvetica').text('Dagger').text('Candlestick').text('Revolver').text('Rope').text('Lead Piping').text('Spanner');
      doc.moveDown(1.5).font('Helvetica-Bold').text('Suspected Scene of Murder').moveDown(0.8);
      doc.font('Helvetica').text('Hall').text('Lounge').text('Dining Room').text('Kitchen').text('Ball Room').text('Conservatory');
      doc.text('Billiard Room').text('Library').text('Study');
      doc.labelContextReset();
    }
  };
})();

Then the control file that defined how the “labels” are laid out on the page

(function() {
  'use strict';

  module.exports = {
    four: {
      initialX: 28.35,
      initialY: 28.35,
      deltaX: 297.68,
      deltaY: 421.00,
      width: 221.74,
      columns: 2,
      rows: 2
    }
  };
})();

And then the bit of the main pdf control function that uses that control file to repeadely output the single detective note at the correct part of the page

(function() {
  'use strict';
  const label = require('../config/label');
  const PDFKit = require('pdfkit');

  const margins = { top: 36, bottom: 36, left: 72, right: 72 }

  class PDFDocument extends PDFKit {
    constructor(response, layout) {
      const options = {
        size: 'A4',
        layout: layout ?? 'landscape',
        info: info,
        margins: margins
      }
      super(options);
      this.pipe(response);
      this.font('Helvetica', 11); //default initial font
    }

  }
  class Label extends PDFDocument {
    constructor(response) {
      super(response, 'portrait') 
      this.labelDepth = false;  
    }
    labelContext(type, position) {
      let params = label[type];
      if (params === undefined) {
        throw new Error('Invalid Label Type');
      }
      if (this.labelDepth) {
        throw new Error('Label Context not clear');
      }
      this.labelDepth = true;
      let inPage = position % (params.rows * params.columns);
      if (position > 0 && inPage === 0) {
        this.addPage();
      }
      let column = inPage % params.columns;
      let row = Math.floor(inPage / params.columns);
      this.save();  // save context so we can restore it
      //eslint-disable-next-line max-len
      this.translate(params.initialX + (column * params.deltaX), params.initialY + (row * params.deltaY));
      return position + 1;
    }
    labelContextReset() {
      if (!this.labelDepth) {
        throw new Error('Label Context Not Set');
      }
      this.restore();
      this.labelDepth = false;
    }
  }

  module.exports = {
    PDFDocument: PDFDocument,
    Label: Label
  };
})();