Skip to content

Commit

Permalink
#20 updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
sinatayebati committed May 22, 2024
1 parent 05bf992 commit c475c51
Show file tree
Hide file tree
Showing 19 changed files with 128 additions and 559 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ jobs:
- name: Build Docker image
run: docker build -t vue-pdf-annotate .

- name: Deploy Docker container to GitHub Pages
- name: Run Docker container
run: |
docker run -d -p 5173:5173 vuetify-project
docker cp $(docker ps -ql):/app/dist ./dist
docker run -d -p 5000:80 vue-pdf-annotate
sleep 10 # wait for the container to start
docker cp $(docker ps -ql):/usr/share/nginx/html ./dist
docker stop $(docker ps -ql)
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
21 changes: 15 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use a Node.js base image
FROM node:18
# Stage 1: Build the application
FROM node:18 as build

# Set the working directory
WORKDIR /app
Expand All @@ -16,8 +16,17 @@ COPY . .
# Build the application
RUN npm run build

# Expose the port the app runs on
EXPOSE 5173
# Stage 2: Serve the application with Nginx
FROM nginx:alpine

# Command to run the app
CMD ["npm", "run", "dev"]
# Copy the build output to the Nginx html directory
COPY --from=build /app/dist /usr/share/nginx/html

# Copy the Nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf

# Expose the port
EXPOSE 80

# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
214 changes: 53 additions & 161 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Welcome to the PDF Annotator project! This project is built using Vue.js, PDF.js

As someone who found the documentation of PDF.js insufficient and faced significant challenges in programmatically adding annotations to PDF documents using a dedicated backend and database, I decided to create this project. Working with the annotation layer of PDF.js was very challenging and almost impossible to accomplish this task. Therefore, I created this small project to showcase the easiest production-level solution ([pdfAnnotate](https://github.com/highkite/pdfAnnotate)) I found for programmatic annotation and demonstrate how you can easily integrate the prebuilt PDF.js viewer into your Vue project.

<p align="center">
<img src="src/assets/highlight.png"
width = 600px
>
</p>

### Table of Contents

1. [Getting Started](#getting-started)
Expand Down Expand Up @@ -66,65 +72,31 @@ The `pdfViewer.vue` component integrates the default/prebuilt PDF.js viewer usin

Here's how simple integration with iframe works:

1. **Vue Template Structure**
1. **Integration of PDFjs in Vue component via `iframe`**
```html
<template>
<div>
<iframe ref="pdfViewerIframe" id="pdfViewer" :src="viewerUrl" style="width: 100%; height: 100vh;"></iframe>
<iframe ref="pdfViewerIframe" id="pdfViewer" :src="viewerUrl"></iframe>
</div>
</template>
```

2. **Vue Script Setup**
```javascript
<script setup>
import { ref, onMounted } from 'vue';
import { piniaStore } from '../stores/piniaStore';
import { storeToRefs } from 'pinia';

const pdfViewerIframe = ref(null);
const viewerUrl = ref('/pdfjs-annotation/pdfjs-4.2.67-dist/web/viewer.html');

const pdfStore = piniaStore();
const { pdfUrl } = storeToRefs(pdfStore);

const loadPdf = async () => {
if (pdfUrl.value) {
const pdfData = await fetchPdfAsUint8Array(pdfUrl.value);
iframeLoader(pdfData);
} else {
console.error("No PDF available");
}
};

onMounted(() => {
loadPdf();
});

async function fetchPdfAsUint8Array(pdfPath) {
const response = await fetch(pdfPath);
const arrayBuffer = await response.arrayBuffer();
return new Uint8Array(arrayBuffer);
}

function iframeLoader(pdfData) {
const pdfViewerIframeElement = pdfViewerIframe.value;
if (!pdfViewerIframeElement) return;

pdfViewerIframeElement.onload = () => {
const viewerApp = pdfViewerIframeElement.contentWindow.PDFViewerApplication;
if (viewerApp && viewerApp.initialized) {
viewerApp.open({ data: pdfData });
} else {
console.error("Viewer is not ready or document not available");
}
};
pdfViewerIframeElement.src = viewerUrl.value;
}
</script>
```
2. **Break Down the Key Aspects**

- **Iframe Source**: set the iframe's `src` to the path of the PDF.js viewer HTML file.
- **Iframe ID**: assigned the iframe an ID of `pdfViewer` for easy interaction with its content.
- **PDF Loading**: `loadPDF` and `iframeLoader` functions handle fetching and preparing the PDF, which is then loaded into the iframe.
- **Below are the major API calls to pdfjs and their functions**:
- **`PDFViewerApplication`**: This is the main class in viewer.mjs that we interact with, controlling rendering and sub-functionalities.
- `PDFViewerApplication.open(file)`: Used to load the PDF. Pass a Uint8Array instead of a URL.
- Ensure to decode base64 encoded data before passing it -- not all browsers support atob or data URI schemes.
- **`value.contentWindow.PDFViewerApplication`**: Accesses the DOM content.
- **`PDFViewerApplication.pdfDocument`**: Initializes interaction with the DOM content.
- **`PDFViewerApplication.pdfDocument.getData()`**: Retrieves data from the DOM for operations like client-side annotation and rendering.
- **`PDFViewerApplication.initialized`**: Checks if the viewer is initialized and ready for rendering.
- **`PDFViewerApplication.eventBus`**: Enables the event bus of pdfjs.
- **`PDFViewerApplication.eventBus.on("pagechanging")`**: Listens for page changes in the PDF

The PDF.js viewer is seamlessly integrated into the Vue component using an `iframe`, making it easy to load and display PDF documents.

## Annotating PDFs with pdfAnnotate

Expand All @@ -138,11 +110,41 @@ Here's how you can use it:

The `pdfAnnotate` library supports several types of annotations, including:
- Highlight
<p align="center">
<img src="src/assets/higlight2.png"
width = 300px
>
</p>
- Squiggly
<p align="center">
<img src="src/assets/squiggly.png"
width = 300px
>
</p>
- Underline
<p align="center">
<img src="src/assets/underline.png"
width = 300px
>
</p>
- Strikeout
<p align="center">
<img src="src/assets/strike.png"
width = 300px
>
</p>
- Square
<p align="center">
<img src="src/assets/square.png"
width = 300px
>
</p>
- Oval
<p align="center">
<img src="src/assets/oval.png"
width = 300px
>
</p>
- and more

### Parameters Required
Expand All @@ -160,114 +162,4 @@ for details and the rest of the documentation, I urge you to read their entire d
* Note: one thing you need to be cautious is that pdfjs origin for a "rect" is top left, and pdfAnnotate origin is buttom left, so you need a simple conversion of "y" coordinates to create the list of your annotations.


### Script Setup

```javascript
<script setup>
import { ref } from 'vue';
import { piniaStore } from '../stores/piniaStore';
import { storeToRefs } from 'pinia';
import { AnnotationFactory } from 'annotpdf';

const pdfStore = piniaStore();
const { pdfUrl } = storeToRefs(pdfStore);

const annotationType = ref('highlight');
const annotationColor = ref({ r: 255, g: 255, b: 0 });

const setAnnotationType = (type, color) => {
annotationType.value = type;
annotationColor.value = color;
triggerHighlightAnnotations();
};

const triggerHighlightAnnotations = async () => {
const pdfViewerIframe = document.getElementById('pdfViewer');
const viewerApp = pdfViewerIframe.contentWindow.PDFViewerApplication;
if (viewerApp && viewerApp.pdfDocument) {
const data = await viewerApp.pdfDocument.getData();
const pdfFactory = new AnnotationFactory(data);
await highlightAnnotation(viewerApp, pdfFactory);
}
};

const triggerDeleteAnnotations = async () => {
const pdfViewerIframe = document.getElementById('pdfViewer');
const viewerApp = pdfViewerIframe.contentWindow.PDFViewerApplication;
if (viewerApp && viewerApp.pdfDocument) {
const data = await viewerApp.pdfDocument.getData();
const pdfFactory = new AnnotationFactory(data);
await deleteAnnotations(viewerApp, pdfFactory);
}
};

const highlightAnnotation = async (viewerApp, pdfFactory) => {
await deleteExistingAnnotations(viewerApp, pdfFactory);
const annotations = [];
const page = 0;
const contents = 'Trace-based Just-in-Time Type Specialization for Dynamic';
const author = 'Auto Generated';
const rect = [
530, // x1
792 - 94.99, // y1
80.56, // x2
792 - 77.06 // y2
];
annotations.push({
page,
rect,
contents,
author,
color: annotationColor.value,
opacity: 0.7,
});

annotations.forEach(annotation => {
if (annotationType.value === 'highlight') {
pdfFactory.createHighlightAnnotation(annotation);
} else if (annotationType.value === 'squiggly') {
pdfFactory.createSquigglyAnnotation(annotation);
} else if (annotationType.value === 'underline') {
pdfFactory.createUnderlineAnnotation(annotation);
} else if (annotationType.value === 'strike') {
pdfFactory.createStrikeOutAnnotation(annotation);
} else if (annotationType.value === 'square') {
pdfFactory.createSquareAnnotation(annotation);
} else if (annotationType.value === 'oval') {
pdfFactory.createCircleAnnotation(annotation);
}
});

const updatedPdfData = pdfFactory.write();
viewerApp.open({ data: updatedPdfData });
console.log('Added new annotations');
};

const deleteAnnotations = async (viewerApp, pdfFactory) => {
await deleteExistingAnnotations(viewerApp, pdfFactory);
};

const deleteExistingAnnotations = async (viewerApp, pdfFactory) => {
const existingAnnotations = await pdfFactory.getAnnotations();
const flattenedAnnotations = existingAnnotations.flat();
if (flattenedAnnotations.length > 0) {
const annotationIdsToDelete = flattenedAnnotations.map(annot => annot.id);
const deletePromises = annotationIdsToDelete.map(annotationId => pdfFactory.deleteAnnotation(annotationId));

// Delete all existing annotations
await Promise.all(deletePromises);
console.log('Deleted existing annotations');
const updatedPdfData = pdfFactory.write();
await viewerApp.open({ data: updatedPdfData });
}
};
</script>
```

By integrating `pdfAnnotate`, you can easily add programmatic annotations to your PDF documents. This library supports various annotation types, making it flexible and powerful for different use cases.

---



Thank you for using the PDF Annotator project! We hope this guide helps you get the most out of your PDF viewing and annotation capabilities. Happy coding!
19 changes: 2 additions & 17 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
<!-- <!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue PDFjs Annotation</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html> -->


<!DOCTYPE html>
<html lang="en">
<head>
Expand All @@ -25,11 +10,11 @@
integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w=="
crossorigin="anonymous"
/>
<title> Vue PDFjs Annotation </title>
<title>Vue PDFjs Annotation</title>
</head>

<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
</html>
36 changes: 36 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

server {
listen 80;

server_name localhost;

location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}

location ~* \.mjs$ {
add_header Content-Type application/javascript;
}

# Enable CORS headers
location /pdfjs-4.2.67-dist/ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Origin, Content-Type, Accept';
}

error_page 404 /index.html;
}
}
Binary file added public/compressed.tracemonkey-pldi-09.pdf
Binary file not shown.
Loading

0 comments on commit c475c51

Please sign in to comment.