A TypeScript based PDF generator library, made with React.

Overview

PDFME

pdfme is released under the MIT license. Unit Testing status Current npm package version. Downloads per month on npm. PRs welcome! Tweet

TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!

Features

Fast PDF Generator Easy PDF template design Simple JSON template
Works on node and browser. Use templates to generate PDF, Complex operations are not needed. Anyone can easily create templates with the designer. Templates are JSON data that is easy to understand and handle.

Introduction

pdfme was created to simplify the design and generation process of a PDF. It is especially useful for the following use cases:

  • Need to create a designed PDF with short code.
  • Need to integrate PDF editor features into an application.
  • Need to create a large number of PDFs without compromising performance

As an example, the author's service https://labelmake.jp/ can create more than 100 varieties of PDFs and generates more than 100,000 PDF files per month.

Installation

The operating requirements should be the node environment >=14. Please see the note at the end of this section for usage on Node.js <16.
There are two packages in pdfme, generator and UI.

The package for generating PDF can be installed with the following command.

npm i @pdfme/generator

The packages for using PDF designer, forms and viewers can be installed with the following commands.

npm i @pdfme/ui

The following type, function and classes are available in pdfme.

@pdfme/generator

@pdfme/ui

If your environment uses webpack, import the necessary items as shown below.

import { Template, generate } from '@pdfme/generator';
import { Template, Designer, Form, Viewer } from '@pdfme/ui';

All objects use Template, which will be briefly explained in the next section.

Template

The core of pdfme library are Templates.
Template Type can be imported by both @pdfme/generator or @pdfme/ui. Templates are used everywhere.

A template can be divided into two parts: a fixed part and a variable part.
We call them basePdf and schema. The following image is a good illustration of a template.

  • basePdf: PDF data for the fixed part of the PDF to be generated.
  • schemas: Definition data for the variable part of the PDF to be generated.

basePdf can be given a string(base64), ArrayBuffer, or Uint8Array.
A blank A4 PDF can be imported with BLANK_PDF. You can use it to check how it works.

schemas currently has the following types of data available

  • text
  • image
  • Various types of barcodes

Let's take a look at some specific data.
(If you are using TypeScript, you can import the Template type.)

Minimal Template

import { Template, BLANK_PDF } from '@pdfme/generator';
// import { Template, BLANK_PDF } from '@pdfme/ui'; <- Template types and BLANK_PDF can also be imported from @pdfme/ui.

const template: Template = {
  basePdf: BLANK_PDF,
  schemas: [
    {
      a: {
        type: 'text',
        position: { x: 0, y: 0 },
        width: 10,
        height: 10,
      },
      b: {
        type: 'text',
        position: { x: 10, y: 10 },
        width: 10,
        height: 10,
      },
      c: {
        type: 'text',
        position: { x: 20, y: 20 },
        width: 10,
        height: 10,
      },
    },
  ],
};

For more information, please refer to the API documentation of the Template type here.

You can create a template from Template Design page. Or, if you want to integrate the template creation feature into your application, check out the Designer section.

Generator

The PDF generator function, generate, takes 2 arguments of template and inputs for generate a PDF. It works both in Node.js and in the browser.

The code to generate a PDF file using the template created above is shown below.

import { Template, generate } from '@pdfme/generator';

const template: Template = {
  // skip... Check the Template section.
};
const inputs = [{ a: 'a1', b: 'b1', c: 'c1' }];

generate({ template, inputs }).then((pdf) => {
  console.log(pdf);

  // Browser
  // const blob = new Blob([pdf.buffer], { type: 'application/pdf' });
  // window.open(URL.createObjectURL(blob));

  // Node.js
  // fs.writeFileSync(path.join(__dirname, `test.pdf`), pdf);
});

You can create a PDF file like the below.

Also, each element in the inputs array corresponds to a page in the PDF, you can create a multi-page PDF file by providing multiple elements of inputs.

For more information, please refer to the API documentation of the generate function here.

UI

Designer

The Designer allows you to edit the Template schemas, making it easy for anyone to create Template json objects.

You can design your own template from Template Design page, or you can integrate the designer into your application.

Let's integrate the designer using the template created above as the default template.

import { Template, Designer } from '@pdfme/ui';

const domContainer = document.getElementById('container');
const template: Template = {
  // skip... Check the Template section.
};

const designer = new Designer({ domContainer, template });

The Designer class is instantiated as shown above, and the template designer is displayed in the domContainer.
You can edit the template as shown below. The operation is like Google Slides, etc., so you can use common keyboard shortcuts.

The designer instance can be manipulated with the following methods.

  • saveTemplate
  • updateTemplate
  • getTemplate
  • onChangeTemplate
  • onSaveTemplate
  • destroy

For more information, please refer to the API documentation of the Designer class here.

Form

You can use templates to create forms and PDF viewers.

The Form creates a UI for the user to enter schemas based on the template.

import { Template, Form } from '@pdfme/ui';

const domContainer = document.getElementById('container');
const template: Template = {
  // skip...
};
// This is initial data.
const inputs = [{ a: 'a1', b: 'b1', c: 'c1' }];

const form = new Form({ domContainer, template, inputs });

The form instance has a method getInputs to get the user's input.

You can generate a PDF file based on the user's input by passing the data you get from getInputs as inputs to generate, as shown in the code below.

generate({ template, inputs: form.getInputs() }).then((pdf) => {
  const blob = new Blob([pdf.buffer], { type: 'application/pdf' });
  window.open(URL.createObjectURL(blob));
});

For more information, please refer to the API documentation of the Form class here.

Viewer

Viewing a PDF file in a mobile browser is a pain, because it doesn't display well in an iframe.

The Viewer is a byproduct of the Form development process, but it allows you to show your users a preview of the PDF file you will create.

Using the Viewer is basically the same as using the Form, except that user cannot edit it.

import { Template, Viewer } from '@pdfme/ui';

const domContainer = document.getElementById('container');
const template: Template = {
  // skip...
};
const inputs = [{ a: 'a1', b: 'b1', c: 'c1' }];

const viewer = new Viewer({ domContainer, template, inputs });

For more information, please refer to the API documentation of the Viewer class here.

Special Thanks

I definitely could not have created pdfme without these libraries. I am grateful to the developers of these libraries.

Comments
  • Designer UI's size is buggy

    Designer UI's size is buggy

    I've noticed that many div inside the Designer component have their height and width computed in typescript instead of using CSS. I'm not sure why this is the case, their might be a good reason for that ?

    It causes many issues when trying to integrate the Designer inside an existing app, it overflows everywhere. I think it's because it doesn't react well to viewport size change. The core issue is that height and width are computed instead of using css flex etc...

    The problem is visible in the official codesandbox example integration with React https://codesandbox.io/embed/github/pdfme/pdfme-playground/tree/main/?module=%2Fsrc%2FDesigner.tsx

    image

    In the current state, it's almost impossible to integrate the Designer anywhere, which is a real shame ! 😢

    Many thanks for this lib by the way !

    opened by bcdrme 8
  • TypeError: e.charCodeAt is not a function

    TypeError: e.charCodeAt is not a function

    If I use node js 16 everything is ok, but if I switch to node js 14 there is this error:

    TypeError: e.charCodeAt is not a function
        at h (/project/node_modules/@pdfme/generator/dist/index.js:2:785405)
        at Object.loadFont (/project/node_modules/@pdfme/generator/dist/index.js:2:770307)
        at Object.r (/project/node_modules/@pdfme/generator/dist/index.js:2:785615)
        at Object.8887 (/project/node_modules/@pdfme/generator/dist/index.js:2:823962)
        at t (/project/node_modules/@pdfme/generator/dist/index.js:2:2196075)
        at /project/node_modules/@pdfme/generator/dist/index.js:2:3155012
        at /project/node_modules/@pdfme/generator/dist/index.js:2:3161603
        at /project/node_modules/@pdfme/generator/dist/index.js:2:3161609
        at /project/node_modules/@pdfme/generator/dist/index.js:2:84
        at Object.<anonymous> (/project/node_modules/@pdfme/generator/dist/index.js:2:223)
    
    opened by abdulgafur24 5
  • PDF generation error

    PDF generation error

    Hello, we have hit an issue today with generating pdfs with custom fonts.

    We use the latest @pdfme/generator and @pdfme/common (both v1.0.14). It appears it cannot locate the correct font to use for a text entry:

    webpack://pdfme/generator/src/helper.ts:296
            fontValue.widthOfTextAtSize(testString, size) + (testString.length - 1) * characterSpacing;
                      ^
    
    TypeError: Cannot read properties of undefined (reading 'widthOfTextAtSize')
        at testString (webpack://pdfme/generator/src/helper.ts:296:19)
        at getOverPosition (webpack://pdfme/generator/src/helper.ts:217:9)
        at getSplitPosition (webpack://pdfme/generator/src/helper.ts:230:19)
        at getSplittedLines (webpack://pdfme/generator/src/helper.ts:250:20)
        at null.<anonymous> (webpack://pdfme/generator/src/helper.ts:304:26)
        at Array.forEach (<anonymous>)
        at arg (webpack://pdfme/generator/src/helper.ts:293:30)
        at null.<anonymous> (webpack://pdfme/generator/src/helper.ts:404:5)
        at Generator.next (<anonymous>)
        at null.<anonymous> (webpack://pdfme/node_modules/@pdf-lib/fontkit/dist/fontkit.es.js:36550:1)
        at new Promise (<anonymous>)
        at null.aQ (webpack://pdfme/node_modules/@pdf-lib/fontkit/dist/fontkit.es.js:36550:1)
        at arg (webpack://pdfme/generator/src/helper.ts:399:7)
        at null.<anonymous> (webpack://pdfme/generator/src/generate.ts:69:15)
        at Generator.next (<anonymous>)
        at null.o (webpack://pdfme/generator/src/constants.ts:1:26)
    

    The font object passed to the generate function as logged in the line before passing it in:

    Creating pdf with font option {
      futuraBook: {
        data: <Buffer c6 ec ... many more bytes>,
        fallback: true,
        subset: false
      },
      futuraDemi: {
        data: <Buffer bf a9 ... many more bytes>,
        subset: false
      }
    }
    

    The schema has text entries with fontName set to "futuraDemi" and other text entries with no fontName set. I'm currently investigating the issue in the pdfme code, maybe it has something to do with the determination of the fallback font name?

    opened by mbiegert 4
  • ERROR Unhandled Promise Rejection

    ERROR Unhandled Promise Rejection

    Hi!

    When I run the lambda in my machine, through the serverless invoke local --function generate --path event_mock.json command, it generates correctly. when it is through the endpoint, the following error occurs:

    { "errorType": "Runtime.UnhandledPromiseRejection", "errorMessage": "Error: Invalid argument:\n--------------------------\nERROR POSITION: inputs.0.name\nERROR MESSAGE: Required\n--------------------------", "reason": { "errorType": "Error", "errorMessage": "Invalid argument:\n--------------------------\nERROR POSITION: inputs.0.name\nERROR MESSAGE: Required\n--------------------------", "stack": [ "Error: Invalid argument:", "--------------------------", "ERROR POSITION: inputs.0.name", "ERROR MESSAGE: Required", "--------------------------", " at Je (/var/task/node_modules/@pdfme/generator/dist/index.js:2:2194500)", " at _e (/var/task/node_modules/@pdfme/generator/dist/index.js:2:2194745)", " at /var/task/node_modules/@pdfme/generator/dist/index.js:2:3159977", " at Generator.next (<anonymous>)", " at /var/task/node_modules/@pdfme/generator/dist/index.js:2:3159891", " at new Promise (<anonymous>)", " at vQ (/var/task/node_modules/@pdfme/generator/dist/index.js:2:3159636)", " at wQ (/var/task/node_modules/@pdfme/generator/dist/index.js:2:3159915)", " at /var/task/handlers/GeneratePdfHandler.js:171:38", " at new Promise (<anonymous>)" ] }, "promise": {}, "stack": [ "Runtime.UnhandledPromiseRejection: Error: Invalid argument:", "--------------------------", "ERROR POSITION: inputs.0.name", "ERROR MESSAGE: Required", "--------------------------", " at process.<anonymous> (/var/runtime/index.js:35:15)", " at process.emit (events.js:314:20)", " at process.EventEmitter.emit (domain.js:483:12)", " at processPromiseRejections (internal/process/promises.js:209:33)", " at processTicksAndRejections (internal/process/task_queues.js:98:32)" ] }

    It's the same json in the req body and the event_mock.json... Any idea?

    opened by lucasoliveira08 3
  • How to add new schemas?

    How to add new schemas?

    Thank you for creating PDFMe! It's really cool!

    How can i add new schemas? Seems like the only way is downloading the sources and violating them. Is that the official way?

    opened by psociety 2
  • PDF generated with duplicate pages

    PDF generated with duplicate pages

    I am trying to export the PDF I created and it prints the pages 2 times

    This is my code in React with Typescript:

    import React, { useEffect, useRef } from 'react'
    import Templ from './Template'
    import { Form, Template } from '@pdfme/ui'
    import { generate } from '@pdfme/generator'
    import fileDownload from 'js-file-download'
    
    const ContractTemplate = ({data}) => {
    
      const designerRef = useRef<Form>()
    
      //Download PDF Template with the Inputs values <<<<============================= This is the Generator Method ❗❗
      const handleDownloadPdf = () => {
        generate({
          template:designerRef.current.getTemplate(),
          inputs:designerRef.current.getInputs()
        })
        .then((pdf) => {
          fileDownload(new Blob([pdf.buffer], { type: 'application/pdf' }), data.title+'.pdf')
        })
      }
    
      useEffect(()=>{
        if(data && data.template) {
          
          //Prepare Editor values
          const template = data.template as Template
          const domContainer = document.getElementById('pdf-editor')
    
          const inputs = template.schemas.map(schema => {
            const input = {}
            Object.keys(schema).forEach(key => {
              input[key] = schema[key].type==='text' ? '' : template.sampledata[0][key]
            })
            return input
          })
    
          //Generate PDFME Form
          designerRef.current = new Form({ domContainer, template, inputs })
        }
    
        //Destroy when exit
        return () => {
          if(designerRef.current)
            designerRef.current.destroy()
        }
      }, [])
    
      
      return <React.Fragment>
        <Templ {...{data, handleDownloadPdf}}/>
      </React.Fragment>
    }
    

    And this is the JSON data that I pass in the Generator Method : The basePdf var of the Template Object is a public URL of the Firebase server You can open the url to check

    {
       "template":{
          "sampledata":[
             {
                "field1":"tipe here"
             }
          ],
          "schemas":[
             {
                "field1":{
                   "lineHeight":1,
                   "height":32.13,
                   "characterSpacing":0,
                   "width":86.59,
                   "fontSize":30,
                   "position":{
                      "x":40.22,
                      "y":57.95
                   },
                   "alignment":"left",
                   "type":"text"
                }
             },
             {
                
             }
          ],
          "columns":[
             "field1"
          ],
          "basePdf":"https://firebasestorage.googleapis.com/v0/b/macaw-megalopolis.appspot.com/o/pdf_files%2Fpage__1.pdf?alt=media&token=c45eb42c-7a9c-410f-9801-212a7f89e806"
       },
       "inputs":[
          {
             "field1":"asdas"
          },
          {
             
          }
       ]
    }
    

    And this is the result:

    image

    What am I doing wrong? why does it throw me duplicate pages? It seems that it first uses the pages with edited values and then puts them back without the edited values.

    opened by EnderMaldonado 2
  • Custom css

    Custom css

    Hi, first of all I want to congratulate you for this initiative, there is a need for such a tool. And I want to ask you if it is possible to use fontWeight for dynamic text fields and maybe more css attributes? Thanks!

    opened by tudorels 2
  • Japanese characters can not be shown correctly in playground app

    Japanese characters can not be shown correctly in playground app

    Hi, I tried the playground app and change "Pet Name" to "犬の写真". After I click the "Generate PDF" button, Japanese characters are shown as □□□□.

    Do I miss something or is it need some settings to show Japanese characters?

    opened by scobin 2
  • All jest test suites failed after adding @pdfme to the project

    All jest test suites failed after adding @pdfme to the project

    Hello, I added this fantastic package into my project. Everything went smooth until I proceed to testing. When I ran jest tests, this error showed as a result of every suite. Any solution? image

    opened by zowebs 2
  • Barcode generation fails When running in node

    Barcode generation fails When running in node

    TypeError: ex.toBuffer is not a function
    

    The following files are likely to be related.

    packages/generator/src/helper.ts #createBarCode bwipjs.toBuffer

    bug @pdfme/generator 
    opened by hand-dot 2
  • checkbox as schema type

    checkbox as schema type

    How can we add a checkbox as the schema template type? and is it possible to just get the input values of the form in a json file so we can store the values in a database?

    opened by jinman 1
  • Layout is wrong when used with tailwind

    Layout is wrong when used with tailwind

    Describe the bug

    CleanShot 2022-12-21 at 12 24 15@2x

    select, input, button, and textarea's size are wrong.

    To fix this, we can use the below CSS style.

    .pdfme-designer select {
      font-size: 0.7rem;
      padding: 0;
      padding-left: 5px;
    }
    
    .pdfme-designer input {
      font-size: 0.8rem;
      padding: 2px;
      padding-left: 5px;
    }
    
    .pdfme-designer button {
      padding: 2px;
    }
    
    .pdfme-designer textarea {
      height: 50px !important;
      font-size: 0.75rem;
      line-height: 0.8rem;
      height: 65px;
    }
    

    To Reproduce

    Mount on TailwindCSS project.

    Expected behavior

    Same layout if we use TailwindCSS or not.

    Your Environment

    - pdfme package(@pdfme/generator or @pdfme/ui): @pdfme/ui
    - pdfme version: 1.0.17
    - Operating system: MacOS
    - Node.js version or Browser name & version: Chrome, Version 108.0.5359.124 (Official Build) (arm64)
    

    Your Error Log

    No Error log
    

    Additional context

    No response

    bug @pdfme/ui Designer @pdfme/ui Form @pdfme/ui Viewer 
    opened by hand-dot 0
  • How to add variable length data?

    How to add variable length data?

    Is there any way to create, for example, a table with a variable number of rows? I would like to pass in data coming from user input or from a database and I do not know the number of rows ahead of time. Could be dozens or hundreds.

    Are there any examples or codesandboxes that demonstrate such a thing?

    answered 
    opened by gregfenton 1
  • How do I add PDF Viewer to my React Application?

    How do I add PDF Viewer to my React Application?

    Hello, I recently was able to implement the Generator into my Application after some changes as I am not using TypeScript. I am having trouble implementing the Viewer however. I'll paste my code below, I am seriously lost I don't even know where to begin. Any attempts I have tried to make include my app getting a whitescreen and breaking, I suspect because I am not rendering the Viewer correctly.

    I have tried to go through the demo code however it is quite confusing for a beginner, it is hard to tell where anything is honestly.

    Any help or feedback is greatly appreciated.

    Code:

    import * as React from 'react';
    import { useState, useCallback, useEffect } from 'react';
    import debounce from 'lodash.debounce';
    import Card from '@mui/material/Card';
    import FormRow from '../../../components/FormRow';
    import CardContent from '@mui/material/CardContent';
    import Wrapper from '../../../assets/wrappers/InputForm';
    import { db } from '../../../firebase.config';
    import { addDoc, collection, doc, updateDoc } from 'firebase/firestore';
    import { getAuth } from '@firebase/auth';
    import { decode } from 'html-entities';
    import 'react-modal-video/scss/modal-video.scss';
    import '../AI-tools-css/ModalStyling.css';
    import { ImDownload } from 'react-icons/im';
    import { generate } from '@pdfme/generator';
    import { base } from './basicLessonPlanModel';
    
    const LessonPlannerV2 = () => {
      // PDF File States
    
      const [aimSection, setAimSection] = useState('');
      const [objectivesSection, setObjectivesSection] = useState('');
      const [materialsSection, setMaterialsSection] = useState('');
      const [anticipatorySection, setAnticipatorySection] = useState('');
      const [modeledSection, setModeledSection] = useState('');
      const [guidedSection, setGuidedSection] = useState('');
      const [independentPractice, setIndependentPractice] = useState('');
      const [struggleSection, setStruggleSection] = useState('');
      const [closureSection, setClosureSection] = useState('');
    
      // API Request & Response States
    
      const [completion, setCompletion] = useState({
        generatedText: '',
      });
      const [subject, setSubject] = useState('');
      const [gradeLevel, setGradeLevel] = useState('');
    
      // Loading State
      const [isLoading, setIsLoading] = useState(false);
    
      // add the documentHasChanged state hook
      const [documentHasChanged, setDocumentHasChanged] = useState(false);
    
      const debouncedTextChangeHandler = useCallback(
        debounce(handleEditorTextOnChange, 300),
        [completion]
      );
    
      async function fetchApi(subject, gradeLevel) {
        setIsLoading(true);
        const myHeaders = new Headers();
        myHeaders.append('Content-Type', 'application/json');
    
        const raw = JSON.stringify({
          subject,
          gradeLevel,
        });
    
        const requestOptions = {
          method: 'POST',
          headers: myHeaders,
          body: raw,
          redirect: 'follow',
        };
    
        fetch(
          `${window.location.origin}/api/v1/completions/lessonPlannerV2Completion`,
          requestOptions
        )
          .then(response => response.json())
          .then(result => {
            setIsLoading(false);
            console.log('lessonPlannerV2Completion ===', result);
            let textResult = decode(result.choices[0].text);
            setCompletion({
              generatedText: textResult,
            });
    
            // Aim
            var regex = /<aim>(.*?)<aim>/s;
            var matchAim = textResult.match(regex);
            console.log('Aim Match ====', matchAim[1]);
            setAimSection(matchAim[1]);
    
            // Objectives
            var regex = /<objectives>(.*?)<objectives>/s;
            var matchObjectives = textResult.match(regex);
            console.log('Objectives Match ====', matchObjectives[1]);
            setObjectivesSection(matchObjectives[1]);
    
            // Materials Needed
            var regex = /<materials>(.*?)<materials>/s;
            var matchMaterials = textResult.match(regex);
            console.log('Materials Match ====', matchMaterials[1]);
            setMaterialsSection(matchMaterials[1]);
    
            // Anticipatory Set
            var regex = /<anticipatory>(.*?)<anticipatory>/s;
            var matchAnticipatory = textResult.match(regex);
            //console.log('Objectives Match ====', matchAnticipatory[1]);
            setAnticipatorySection(matchAnticipatory[1]);
    
            // Modeled Practice
            var regex = /<modeled>(.*?)<modeled>/s;
            var matchModeled = textResult.match(regex);
            //console.log('Aim Match ====', matchModeled[1]);
            setModeledSection(matchModeled[1]);
    
            // Guided Practice
            var regex = /<guided>(.*?)<guided>/s;
            var matchGuided = textResult.match(regex);
            //console.log('Objectives Match ====', matchGuided[1]);
            setGuidedSection(matchGuided[1]);
    
            // Independent Practice
            var regex = /<independent>(.*?)<independent>/s;
            var matchIndependent = textResult.match(regex);
            // console.log('Aim Match ====', matchIndependent[1]);
            setIndependentPractice(matchIndependent[1]);
    
            // Struggle
            var regex = /<struggles>(.*?)<struggles>/s;
            var matchStruggle = textResult.match(regex);
            // console.log('Objectives Match ====', matchStruggle[1]);
            setStruggleSection(matchStruggle[1]);
    
            // Closure
            var regex = /<closure>(.*?)<closure>/s;
            var matchClosure = textResult.match(regex);
            // console.log('Aim Match ====', matchClosure[1]);
            setClosureSection(matchClosure[1]);
    
            setDocumentHasChanged(true);
          })
          .catch(error => console.log('error', error));
      }
    
      const handleSubmit = event => {
        event.preventDefault();
        if (!subject) {
          console.log('Please enter a subject');
          return;
        }
        fetchApi(subject, gradeLevel);
      };
    
      async function handleEditorTextOnChange(event, editor) {
        if (!completion.id) return console.log('No completion selected');
        const data = editor.getData();
        console.log('Saving data ...');
        const docRef = doc(db, 'completions', completion.id);
        await updateDoc(docRef, {
          generatedText: data,
        });
      }
    
      // PDF File Functions
    
      const template = {
        schemas: [
          {
            input1: {
              type: 'text',
              position: { x: 14.2, y: 27.78 },
              width: 168,
              height: 11.23,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1,
            },
            input2: {
              type: 'text',
              position: { x: 14.2, y: 50.38 },
              width: 168,
              height: 54.88,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1,
            },
            input3: {
              type: 'text',
              position: { x: 14.2, y: 119.22 },
              width: 168,
              height: 33.18,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1,
            },
            input4: {
              type: 'text',
              position: { x: 14.2, y: 166.26 },
              width: 168,
              height: 72.07,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            title: {
              type: 'text',
              position: { x: 58.7, y: 9 },
              width: 124.41,
              height: 7.17,
              alignment: 'left',
              fontSize: 16,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
          },
          {
            input5: {
              type: 'text',
              position: { x: 14.2, y: 36.09 },
              width: 168,
              height: 83.98,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            input6: {
              type: 'text',
              position: { x: 14.2, y: 137.78 },
              width: 168,
              height: 90.86,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            title: {
              type: 'text',
              position: { x: 58.7, y: 9 },
              width: 124.41,
              height: 7.17,
              alignment: 'left',
              fontSize: 16,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
          },
          {
            input7: {
              type: 'text',
              position: { x: 14.2, y: 29.16 },
              width: 168,
              height: 62.55,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            input8: {
              type: 'text',
              position: { x: 14.2, y: 116.42 },
              width: 168,
              height: 58.31,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            input9: {
              type: 'text',
              position: { x: 14.2, y: 188.92 },
              width: 168,
              height: 58.58,
              alignment: 'left',
              fontSize: 12,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
            title: {
              type: 'text',
              position: { x: 58.7, y: 9 },
              width: 124.41,
              height: 7.17,
              alignment: 'left',
              fontSize: 16,
              characterSpacing: 0,
              lineHeight: 1.1,
            },
          },
        ],
        basePdf: base,
      };
    
      const inputs = [
        {
          title: subject,
          input1: aimSection,
          input2: objectivesSection,
          input3: materialsSection,
          input4: anticipatorySection,
          input5: modeledSection,
          input6: guidedSection,
          input7: independentPractice,
          input8: struggleSection,
          input9: closureSection,
        },
      ];
    
      const handleDownload = async () => {
        // Generate the PDF file
        const pdf = await generate({
          template,
          inputs,
        });
    
        // Create a blob from the PDF data
        const blob = new Blob([pdf.buffer], { type: 'application/pdf' });
    
        // Create a link element to initiate the download
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = 'testing.pdf';
    
        // Append the link element to the document
        document.body.appendChild(link);
    
        // Click the link to initiate the download
        link.click();
    
        // Remove the link element from the document
        document.body.removeChild(link);
      };
    
      return (
        <Wrapper>
          <Card
            sx={{
              width: '100%',
              maxWidth: '100%',
              border: 'none',
              boxShadow: '0px 0px 2px rgba(0, 0, 0, 0.15)',
              borderRadius: '5px',
              height: '100%',
              '&:hover': {
                boxShadow: '0px 0px 10px rgba(0, 0, 0, 0.25)',
              },
            }}
            className="input-card"
          >
            <CardContent>
              <form onSubmit={handleSubmit}>
                <div className="form-center">
                  <div className="titleAndVideo">
                    <h4>Lesson Planner Version 2 ⚡</h4>
                    {/* <Model /> */}
                  </div>
                  <FormRow
                    type="text"
                    labelText="Topic or lesson to generate lesson for:"
                    name="subject"
                    value={subject}
                    handleChange={e => setSubject(e.target.value)}
                  />
                  <FormRow
                    type="text"
                    labelText="Grade Level:"
                    name="gradeLevel"
                    value={gradeLevel}
                    handleChange={e => setGradeLevel(e.target.value)}
                  />
                  <button
                    className="btn btn-block"
                    type="submit"
                    disabled={isLoading}
                  >
                    {isLoading ? 'Please Wait...' : 'Generate Lesson Plan'}
                  </button>
                </div>
              </form>
            </CardContent>
          </Card>
    
          <div className="downloadInfo">
            {<ImDownload className="downloadicon" />}
            Your lesson plan will automatically download once it is complete! 🚀
            <button onClick={handleDownload}>Download PDF</button>
          </div>
        </Wrapper>
      );
    };
    
    export default LessonPlannerV2;
    
    answered 
    opened by thefastlanesolution 1
  • Support adding new pages to template

    Support adding new pages to template

    Hi there! I love what you are doing. I was playing around your library and found out that you can't add new page dynamically in editing modes. It would be great to have that, thanks!

    idea 
    opened by asanoviskhak 5
  • Support set properties for PDF document.

    Support set properties for PDF document.

    Hi, the library is very impressive!

    I think that you should add on the Customize feature to set title, author, subject for document properties when generate pdf file. you can use methods in pdf-lib library, you can do it! refer: https://pdf-lib.js.org/docs/api/classes/pdfdocument#methods

    Thank you!

    opened by kymap057 0
Releases(1.0.0-beta.9)
  • 1.0.0-beta.9(Feb 21, 2022)

    What's Changed

    • Develop by @hand-dot in https://github.com/pdfme/pdfme/pull/1
    • move dir by @hand-dot in https://github.com/pdfme/pdfme/pull/2
    • remove save with page by @hand-dot in https://github.com/pdfme/pdfme/pull/3
    • minor fix by @hand-dot in https://github.com/pdfme/pdfme/pull/4
    • create storybook page by @hand-dot in https://github.com/pdfme/pdfme/pull/5
    • Develop by @hand-dot in https://github.com/pdfme/pdfme/pull/8
    • initial REVIEW for pdfme by @azure06 in https://github.com/pdfme/pdfme/pull/7
    • [@pdfme/ui] Fix Sidebar Bug by @hand-dot in https://github.com/pdfme/pdfme/pull/34
    • Improve documentation by @azure06 in https://github.com/pdfme/pdfme/pull/33
    • Add some function in sidebar by @hand-dot in https://github.com/pdfme/pdfme/pull/37
    • Develop by @hand-dot in https://github.com/pdfme/pdfme/pull/40

    New Contributors

    • @hand-dot made their first contribution in https://github.com/pdfme/pdfme/pull/1
    • @azure06 made their first contribution in https://github.com/pdfme/pdfme/pull/7

    Full Changelog: https://github.com/pdfme/pdfme/commits/1.0.0-beta.9

    Source code(tar.gz)
    Source code(zip)
Owner
pdfme
We aim to be the most user-friendly PDF library!
pdfme
A React component to view a PDF document

React PDF viewer A React component to view a PDF document. It's written in TypeScript, and powered by React hooks completely. // Core viewer import {

React PDF Viewer 1.4k Jan 9, 2023
A boiler code generator for electron with react or vue with taildwindcss in both JavaScript & TypeScript

A boiler code generator for electron with react or vue with taildwindcss in both JavaScript & TypeScript

Rajvir Singh 211 Dec 14, 2022
Simple generator of React code from Figma

Simple generator of React code from Figma

seya 276 Dec 23, 2022
Recurrence rules generator form built with React

React RRule Generator Recurrence rules generator form built with React This project is no longer maintained by me. Thank you for all your past contrib

Filip Duczymiński 95 Nov 6, 2022
Professional React app generator. Shipped with an exposed, unopinionated, highly-performant config

Professional React app generator. Shipped with an exposed, unopinionated, highly-performant config. Jest-SWC, Storybook, SWC, Typescript, Webpack 5.

null 35 Dec 20, 2022
Password Generator using Genetic Algorithm

Password Generator using Genetic Algorithm

Ahmad Anshorimuslim Syuhada 28 Nov 15, 2021
WYSIWG paper wallet generator

Hello Wallet Try it. Have you ever wanted to make a paper wallet but they all look like this? Using the latest graphic design technology it is possibl

Gavin 14 Nov 15, 2022
Github-profile-readme-maker - Best Profile Generator, Create your perfect GitHub Profile ReadMe in the best possible way

GPRM : GitHub Profile ReadMe Maker Features We got everything that you need ! Cr

Vishwa Gaurav 333 Dec 20, 2022
Bingo Letter - made with React, TypeScript and Tailwind CSS

This is my take on a childhood game my classmates and I used to play on paper. I thought it would be a fun project to automate because why not? It is made with React, TypeScript and Tailwind CSS. It is also responsive for smaller devices

Nutifafa Afi Attor 10 Nov 18, 2022
Lightweight auth library based on oidc-client for React single page applications (SPA). Support for hooks and higher-order components (HOC).

Lightweight auth library based on oidc-client for React single page applications (SPA). Support for hooks and higher-order components (HOC).

null 199 Jan 3, 2023
Ethereum's missing NFT swap library for web3 developers. Written in TypeScript. Powered by 0x.

NFT Swap SDK _The missing NFT swap SDK for Ethereum and EVM compatible chains, powered by the 0x protocol, written in TypeScript for web3 developers.

trader.xyz 191 Dec 25, 2022
An algebraic effects library for javascript and typescript using generators

Algebraify Algebraic Effects Are Here! (sort of) (If you're unfamiliar with algebraic effects, here's a great article: Algebraic Effects for the Rest

Jackie Edwards 72 Sep 19, 2022
A React library to show an administrative UI for the Mock Service Worker JS library

A React library to show an administrative UI for the Mock Service Worker JS library

Intesys 3 Nov 17, 2022
Calculator-in-react - Calculator made in React.JS using Hooks as useReducer

Calculator-in-react - Calculator made in React.JS using Hooks as useReducer

Leonardo Falcoski 3 Mar 9, 2022
Simple Light is a free landing page template built on top of TailwindCSS and fully coded in React. Made by

Simple Light is a free landing page template built on top of TailwindCSS and fully coded in React. Made by

Cruip 1.7k Dec 27, 2022
Personal portfolio website of theme Ubuntu 20.04, made using React.js & tailwind CSS

Personal portfolio website of theme Ubuntu 20.04, made using React.js & tailwind CSS

Vivek 2.7k Jan 9, 2023
Compares Discord libraries and their support of new API features. Made with React, Next.js, and Bulma.

Compares Discord libraries and their support of new API features. Made with React, Next.js, and Bulma.

Advaith 109 Dec 31, 2022
a simple dapp login interface made it with react-ts-chakra-ui and usedapp

a simple dapp login interface made it with react-ts-chakra-ui and usedapp

Sergio 1 May 5, 2022
Custom startpage made in React.js

Custom startpage made in React.js

Ryan Jin 21 Sep 29, 2022