gspasov / svelte-pdf-simple

A simple svelte library for displaying pdfs that gives you full control
MIT License
53 stars 3 forks source link

svelte-pdf-simple

npm version npm downloads license

Simple svelte PDF Viewer, packed with features and functionalities. Fully cusomizable navigation controls and styles.

Functionality / Benefits

How to install

Add to your Svelte or SvelteKit project with the following lines:

npm install svelte-pdf-simple

or

yarn add svelte-pdf-simple

How to use

With path:

<script lang="ts">
  import { PdfViewer } from "svelte-pdf-simple";
  const pathToPdf = "./example.pdf";
</script>

<PdfViewer url={pathToPdf} />

With url:

<script lang="ts">
  import { PdfViewer } from "svelte-pdf-simple";
  const urlToPdf =
    "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf";
</script>

<PdfViewer url={urlToPdf} />

With Base64 encoded string:

<script lang="ts">
  import { PdfViewer } from "svelte-pdf-simple";
  const base64EncodedString =
    "JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwogIC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAvTWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0KPj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAgL1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9udAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2JqCgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJUCjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4gCjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAwMDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G";
</script>

<PdfViewer data={atob(base64EncodedString)} />

Full example with handling navigation, load state and potential password locked PDF

<script lang="ts">
  import { PdfViewer } from "svelte-pdf-simple";
  import type {
    PdfLoadFailureContent,
    PdfLoadSuccessContent,
    PdfPageContent,
  } from "svelte-pdf-simple";

  let pdfViewer: PdfViewer;
  let pageNumber = 0;
  let totalPages = 0;
  let isPdfLoaded = false;
  let password = "";

  function navigatePages(forward: boolean = false): void {
      forward 
          ? (pageNumber === totalPages ? pageNumber = 1 : pageNumber++)
          : (pageNumber === 1 ? pageNumber = totalPages : pageNumber--);
      pdfViewer.goToPage(pageNumber);
  }

  function handlePageChanged(event: CustomEvent<PdfPageContent>): void {
    pageNumber = event.detail.pageNumber;
  }

  function handleLoadedSuccess(event: CustomEvent<PdfLoadSuccessContent>) {
    totalPages = event.detail.totalPages;
    pageNumber = 1;
    isPdfLoaded = true;
  }

  function handleLoadFailure(event: CustomEvent<PdfLoadFailureContent>) {
    // Put your logic on how to handle PDF loading failure
  }
</script>

<main>
  {#if isPdfLoaded}
    <button on:click={_ => navigatePages(false)}>prev</button>
    <button on:click={_ => navigatePages(true)}>next</button>
    <span>{pageNumber}/{totalPages}</span>
  {/if}
  <PdfViewer
    bind:this={pdfViewer}
    props={{
      path: "./example.pdf",
      scale: 1.5,
      withAnnotations: true,
      withTextContent: true,
    }}
    style="border: 1px solid black; display: block; margin-top: 10px;"
    on:load_success={handleLoadedSuccess}
    on:load_failure={handleLoadFailure}
    on:page_changed={handlePageChanged}
  >
    <svelte:fragment slot="loading">Loading pdf..</svelte:fragment>
    <svelte:fragment slot="loading-failed">Well... something went wrong :(</svelte:fragment>
    <svelte:fragment slot="password-required">
      <p>This pdf is password protected. Please enter the password to view it.</p>
      <input type="password" bind:value={password} />
      <button on:click={() => pdfViewer.openWithPassword(password)}>unlock</button>
    </svelte:fragment>
  </PdfViewer>
</main>

Props

NB: You cannot setup a PdfViewer without specifying a source. Therefore you must specify source through either of the following properties data | url | path.

NB: It's possible to pass more than one input source. The precedence of the sources is the following (data > path > url)

Prop name Description Type Default value Required
url A url leading to the pdf (e.g. "https://my-domain.com/myPdf.pdf"). When loading PDF from a url there are some specifics to be considered. Read more. string | URL | undefined N/A No
path A path leading to the pdf (e.g. "./myPdf.pdf") string | undefined N/A No
data Provided when you want to specify a pdf either in binary form or a Base64 encoded pdf. When providing a Base64 encoded pdf use atob function to convert it to it's binary form when providing it in the data prop. string | number[] | TypedArray | undefined N/A No
password Specifies the password used to unlock protected pdf. string | undefined N/A No
httpHeaders Specifies headers needed when you are accessing a pdf from a url that requires authentication. Record<string, string> | undefined N/A No
scale Specifies how large you'll like your pdf to be displayed. number (decimal) 1.0 No
page On which page should the pdf open when it loads. number 1 No
rotation How much you'd like your pdf to be rotated. number 0 No
offsetX If specified, offsets the pdf on the X axis. number 0 No
offsetY If specified, offsets the pdf on the Y axis. number 0 No
rotation If specified, sets the rotation of the each page of the pdf. Degree 0 No
withAnnotations Specifies whether you'll like to attach annotations from the pages you load. boolean False No
withTextContent Specifies whether you'll like to attach the text content from the pages you load. boolean False No
additionalParams Additional pdfjs related parameters which you could add if necessary. AdditionalParameters | undefined N/A No

Styling

You can style the canvas in which the pdf is loaded by just passing style property to PdfViewer. $$restProps are passed down the to canvas element.

<PdfViewer 
  props={{path: "./myPdf.pdf"}} 
  style="border: 1px solid black; display: block; margin-top: 10px;"/>

Handling

Function name Description Accepted parameter type
goToPage/0 Changes PDF to specified page. N/A
resize/1 Resizes the PDF to the desired scale value. number (decimal)
rotate/1 Rotate the PDF to the desired degree. Degree
openWithPassword/1 Tries to open a locked PDF with the provided password. string

Dispatch Events

Event name Description Type
page_changed Notifies when PDF changes page. This event also holds page annotations and textContents if they were requested in the PdfViewer props PdfPageContent
load_success Notifies that PDF is successfully loaded. PdfLoadSuccess
load_failure Notifies that some Error has ocurred while loading the PDF. PdfLoadFailure
password_required Notifies that PDF is password protected and the user should provide a password to unlock the PDF. void
incorrect_password Notifies that the provided password to unlock the PDF is incorrect. void

Slots

Slot name Description
loading This slot is visible while the PDF is loading.
loading-failed This slot is visible if PDF has failed to load.
password_required This slot is visible if the PDF requires a password to be unlocked. This is a good spot to place an input form for specifying the password of the PDF

Demo example

To see an example implementation, clone the svelte-pdf-simple repo run the following commands:

$ git clone https://github.com/gspasov/svelte-pdf-simple.git
$ npm install
$ npm run dev

Using PDFs from links

Bare in mind that the pdf.js library enforces cors, therefore if you whish to supply a PDF which is coming from a link, the server serving this pdf should have appropriate CORS settings in the headers. For more information about this please follow this issue in pdf.js

For example, if you are hosting your pds in AWS S3, the following CORS setting work:

[
  {
    "AllowedHeaders": ["Authorization", "Range"],
    "AllowedMethods": ["GET"],
    "AllowedOrigins": ["*"], // supply custom origin
    "ExposeHeaders": ["Accept-Ranges", "Content-Range", "Content-Encoding", "Content-Length"]
  }
]

Contributing

If you would like to see some change/modification open an Issue or a PR. I'd be happy to look into it.

License

MIT