Skip to content

Latest commit

 

History

History
889 lines (651 loc) · 23.2 KB

File metadata and controls

889 lines (651 loc) · 23.2 KB

SimplePDFviewer Usage Guide

A lightweight, standalone JavaScript PDF viewer library that wraps Mozilla's pdf.js library with a modular course/chapter interface and responsive UI, making it easy to integrate into your web applications.

Table of Contents

  1. Quick Start
  2. Installation
  3. Core Concepts
  4. Basic Usage
  5. API Reference
  6. Course Data Format
  7. Customization
  8. Troubleshooting
  9. Examples
  10. Browser Support
  11. Performance Tips
  12. Limitations & Known Issues

Quick Start

Get running in 5 minutes:

Important: The library exports as PDFViewer, not SimplePDFviewer. Always use PDFViewer.init() to initialize the viewer.

  1. Add the script to your HTML:

    <div id="viewer" style="height: 100vh;"></div>
    
    <script src="https://cdn.jsdelivr.net/gh/BrunoRNS/SimplePDFviewer@latest/min/core.min.js"></script>
    <script>
        const course = {
            title: "My Course",
            modules: [{
                title: "Module 1",
                chapters: [{
                    title: "Chapter 1",
                    pdf: "path/to/pdf.pdf"
                }]
            }]
        };
    
        PDFViewer.init(document.getElementById('viewer'), course);
    </script>
  2. Style the container (set height and width):

    <style>
        #viewer { height: 100vh; width: 100%; }
    </style>

That's it! You now have a fully functional PDF viewer with navigation and chapter sidebar.

Installation

Option 1: CDN (Recommended for most use cases)

<script src="https://cdn.jsdelivr.net/gh/BrunoRNS/SimplePDFviewer@latest/min/core.min.js"></script>

Option 2: Local File

Download min/core.min.js from the repository and include it:

<script src="./core.min.js"></script>

Option 3: Development Version

For development, use the unminified version from src/:

<script src="./src/SimplePDFviewer.js"></script>

Core Concepts

SimplePDFviewer organizes PDFs into a hierarchical structure:

  • Course: The root container with a title and modules
  • Module: A collection of chapters (e.g., "Chapter 1", "Chapter 2")
  • Chapter: Individual PDF documents with titles and URLs

This structure allows you to organize related PDFs and navigate between them seamlessly.

Navigation Features

  • Sidebar: Lists all chapters for quick access
  • Previous/Next buttons: Navigate pages within a PDF or jump to the next/previous chapter
  • Keyboard navigation: Use arrow keys (←/→) to navigate
  • Responsive design: Sidebar collapses on mobile, toggleable with menu button
  • Auto-scaling: PDF pages automatically scale to fit the container
  • Automatic zoom reset: Zoom automatically resets to 100% when navigating between pages or chapters
  • Automatic scroll reset: Page scroll position automatically resets to top-left when navigating between pages or chapters

Basic Usage

1. Create Course Data

const course = {
  title: "Introduction to JavaScript",
  modules: [
    {
      title: "Fundamentals",
      chapters: [
        {
          title: "Variables & Types",
          pdf: "https://example.com/variables.pdf"
        },
        {
          title: "Functions",
          pdf: "https://example.com/functions.pdf"
        }
      ]
    },
    {
      title: "Advanced Topics",
      chapters: [
        {
          title: "Closures",
          pdf: "https://example.com/closures.pdf"
        }
      ]
    }
  ]
};

2. Initialize the Viewer

const viewer = PDFViewer.init(
  document.getElementById('viewer'),
  course,
  {
    onError: (error) => {
      console.error('PDF Error:', error);
    }
  }
);

3. Use Viewer Methods

// Navigate to next page
viewer.nextPage();

// Navigate to previous page
viewer.prevPage();

// Load a specific chapter
viewer.loadChapter(0, 1); // Module 0, Chapter 1

// Access current state
console.log(viewer.currentModule);    // 0
console.log(viewer.currentChapter);   // 0
console.log(viewer.currentPage);      // 1
console.log(viewer.pdfDoc);           // pdf.js document object

// Clean up when done
viewer.destroy();

API Reference

PDFViewer.init(container, course, options)

Initializes a PDF viewer instance.

Parameters:

  • container (HTMLElement, required): The DOM element where the viewer will be rendered
  • course (Object, required): Course data object with structure detailed in Course Data Format
  • options (Object, optional): Configuration options
    • onError (Function): Callback function for error handling. Called with error object: { type, message, error }
    • colorTheme (String): Optional hex color for custom theme (e.g., #FF5722). Auto-adjusts if too bright/dark. Colors are auto-calculated for all UI elements.

Returns:

Instance object or null if initialization fails.

Example:

const viewer = PDFViewer.init(
  document.getElementById('viewer'),
  courseData,
  {
    onError: (err) => {
      console.error(`${err.type} error: ${err.message}`);
    }
  }
);

if (!viewer) {
  console.error('Failed to initialize viewer');
}

Instance Methods

nextPage()

Navigate to the next page. If at the end of a chapter, loads the next chapter.

viewer.nextPage();

prevPage()

Navigate to the previous page. If at the beginning of a chapter, loads the previous chapter.

viewer.prevPage();

loadChapter(moduleIndex, chapterIndex)

Load a specific chapter by module and chapter indices.

viewer.loadChapter(0, 2); // Load Chapter 2 from Module 0

if (viewer.pdfDoc) {
  console.log(`Loaded ${viewer.pdfDoc.numPages} pages`);
}

destroy()

Clean up the viewer instance. Removes event listeners and cancels rendering tasks.

viewer.destroy();

setZoom(value)

Set the zoom level for the current page (100-200%).

Parameters:

  • value (Number): Zoom level between 100 (100%) and 200 (200%)

Example:

// Zoom to 150%
viewer.setZoom(150);

// Current zoom level
console.log(viewer.zoom); // e.g., 150

Behavior:

  • Automatically clamps value to 100-200 range
  • Preserves scroll position proportionally when possible
  • Updates UI controls immediately
  • Page is re-rendered at the new zoom level

Note: Zoom automatically resets to 100% when navigating to a different page or chapter for better UX.

setTextSelectionEnabled(enabled)

Enable or disable text selection at runtime. Text selection is enabled by default and allows users to select and copy text from PDFs.

Parameters:

  • enabled (Boolean): true to enable text selection, false to disable

Example:

// Disable text selection
viewer.setTextSelectionEnabled(false);

// Re-enable text selection
viewer.setTextSelectionEnabled(true);

// Toggle based on user preference
document.getElementById('toggle-selection').onclick = () => {
    viewer.setTextSelectionEnabled(!viewer.enableTextSelection);
};

// Check current state
if (viewer.enableTextSelection) {
    console.log('Text selection is enabled');
}

Configuration at Initialization:

// Disable text selection from the start
const viewer = PDFViewer.init(container, course, {
    enableTextSelection: false
});

// Default behavior (enabled)
const viewer2 = PDFViewer.init(container, course);

How It Works:

  • Text selection uses a transparent text layer positioned on top of the PDF canvas
  • Users can highlight and copy text without affecting the visual PDF rendering
  • The text layer is automatically rendered when each page loads
  • Text selection can be toggled at any time without reloading the PDF

setTheme(hex)

Change the theme color after initialization. Automatically calculates all UI colors from the provided hex color.

Parameters:

  • hex (String): Hex color code (e.g., #FF5722, #F52)

Returns: true if successful, false if invalid format or lightness out of range

Example:

// Change to pink theme
if (viewer.setTheme('#E91E63')) {
  console.log('Theme changed successfully');
} else {
  console.error('Invalid color format');
}

// With preview functionality
document.getElementById('colorPicker').addEventListener('change', (e) => {
  viewer.setTheme(e.target.value);
});

Behavior:

  • Auto-adjusts very dark colors (< 15% lightness) to #2a2a2a minimum
  • Auto-adjusts very bright colors (> 85% lightness) to #e8e8e8 maximum
  • Maintains hue and saturation while adjusting lightness when needed
  • Re-injects CSS to update all UI colors immediately

Instance Properties

  • currentModule (number): Index of current module (0-based)
  • currentChapter (number): Index of current chapter (0-based)
  • currentPage (number): Current page number (1-based)
  • zoom (number): Current zoom level (100-200, default 100). Automatically resets to 100 when navigating between pages or chapters
  • enableTextSelection (boolean): Whether text selection is currently enabled (default: true)
  • pdfDoc (pdfjsLib.PDFDocument): The pdf.js document object (null before loading)
  • renderTask (pdfjsLib.RenderTask): Current render task (null if not rendering)
  • onError (Function): Error callback function
  • themeColors (Object): Current theme colors object with properties like primary, primaryHover, primaryActive, sidebarPrimary, etc.

Navigation Behavior

When navigating between pages or chapters using nextPage(), prevPage(), or loadChapter(), the viewer automatically applies the following state resets for optimal UX:

Automatic State Resets

  • Zoom: Resets to 100% to ensure the entire page is visible by default
  • Scroll Position: Resets to top-left (0, 0) so the new page starts at the beginning
  • UI State: Previous/Next buttons update based on current position in the course

Implementation Details

  • Scroll reset uses multiple simultaneous methods to ensure reliability across browsers
  • Scroll restoration and zoom restoration happen after page rendering completes
  • These behaviors are transparent to the user and cannot be disabled

Example: Custom Navigation with Reset Awareness

const viewer = PDFViewer.init(container, course);

// Listen for manual navigation
document.getElementById('next-btn').onclick = () => {
  viewer.nextPage(); // Auto-resets zoom and scroll
  console.log(`Zoom: ${viewer.zoom}%, Page: ${viewer.currentPage}`);
};

// Jump to a chapter - also triggers auto-reset
document.getElementById('jump-chapter').onclick = () => {
  viewer.loadChapter(1, 0); // Zoom and scroll auto-reset
};

Course Data Format

The course object must follow this structure:

{
  title: "string",           // Required: Course title
  modules: [                 // Required: Array of modules
    {
      title: "string",       // Required: Module title
      chapters: [            // Required: Array of chapters
        {
          title: "string",   // Required: Chapter title
          pdf: "string"      // Required: PDF URL (must be accessible/CORS-enabled)
        },
        // ... more chapters
      ]
    },
    // ... more modules
  ]
}

Validation Rules

  • title: Must be a non-empty string
  • modules: Must be a non-empty array
  • Each module must have a title and chapters array
  • Each chapter must have a title and pdf URL
  • All required fields are validated at initialization

Example with IDs (Optional)

You can add extra properties for your own tracking:

const course = {
  id: "course-001",
  title: "Advanced JavaScript",
  modules: [
    {
      id: "mod-101",
      title: "Core Concepts",
      chapters: [
        {
          id: "ch-1001",
          title: "Chapter 1",
          pdf: "https://example.com/ch1.pdf"
        }
      ]
    }
  ]
};

Customization

1. Styling with CSS

SimplePDFviewer injects default CSS classes. Override them with your own styles:

<style>
  /* Container and layout */
  .pdf-viewer-container { /* Main container */ }
  .pdf-viewer-main { /* Main content area */ }
  .pdf-viewer-sidebar { /* Left sidebar */ }
  .pdf-viewer-controls { /* Top control bar */ }
  .pdf-viewer-canvas-container { /* Canvas wrapper */ }

  /* Canvas and loading */
  .pdf-viewer-canvas { /* PDF canvas */ }
  .pdf-viewer-loading-overlay { /* Loading indicator */ }

  /* Chapters list */
  .pdf-viewer-chapter-item { /* Chapter in sidebar */ }
  .pdf-viewer-chapter-item:hover { /* Chapter hover state */ }
  .pdf-viewer-chapter-item.active { /* Active chapter */ }

  /* Buttons */
  .pdf-viewer-btn { /* All buttons */ }
  .pdf-viewer-btn:hover { /* Button hover */ }
  .pdf-viewer-btn:disabled { /* Disabled button */ }
  .pdf-viewer-toggle-btn { /* Menu toggle button */ }
</style>

2. Custom Colors (Legacy CSS Method)

.pdf-viewer-sidebar {
  background: #1a1a1a;  /* Dark sidebar */
  color: #ffffff;
}

.pdf-viewer-btn {
  background: #0066cc;  /* Custom blue */
}

.pdf-viewer-btn:hover {
  background: #0052a3;
}

.pdf-viewer-chapter-item.active {
  background: #0066cc;
}

2a. Theme Customization (NEW - Recommended for Global Color Changes)

Use the new colorTheme option to automatically generate all UI colors from a single hex color. Colors are intelligently calculated to maintain proper contrast and visual hierarchy.

During Initialization

const viewer = PDFViewer.init(
  document.getElementById('viewer'),
  course,
  {
    colorTheme: '#FF5722'  // Use Material Design Deep Orange
  }
);

Dynamic Theme Switching (After Initialization)

const viewer = PDFViewer.init(container, course);

// Change theme later
viewer.setTheme('#E91E63');   // Pink
viewer.setTheme('#4CAF50');   // Green
viewer.setTheme('#2196F3');   // Blue
viewer.setTheme('#FF9800');   // Orange

How It Works

  • Provides a single hex color as the primary theme color
  • Library automatically calculates:
    • Darker shades for hover and active states
    • Muted/darker sidebar color with reduced saturation
    • Maintains proper contrast for accessibility
    • Uses HSL color space for intelligent scaling

auto-adjustment for Edge Cases

The system automatically adjusts colors that are too bright or too dark:

viewer.setTheme('#000000');   // Auto-adjusted to #2a2a2a (minimum usable darkness)
viewer.setTheme('#FFFFFF');   // Auto-adjusted to #e8e8e8 (maximum usable brightness)
viewer.setTheme('#FF5722');   // Normal - accepted as-is

Returns: true if successful, false if invalid format

Color Format Validation

Accepted formats:

  • #RRGGBB (e.g., #FF5722)
  • #RGB (e.g., #F52)

Invalid formats (will be rejected):

  • rgb(255, 87, 34)
  • FF5722 (missing #)
  • #GGHHII (invalid hex)

Example: Theme Picker

// HTML
<button onclick="changeTheme('#FF5722')">Orange</button>
<button onclick="changeTheme('#E91E63')">Pink</button>
<input type="text" id="colorInput" placeholder="#FF5722">
<button onclick="applyCustomColor()">Apply</button>

// JavaScript
function applyCustomColor() {
  const color = document.getElementById('colorInput').value;
  if (viewer.setTheme(color)) {
    alert(`Theme changed to ${color}`);
  } else {
    alert(`Invalid color: ${color}`);
  }
}

3. Custom Container Size

<style>
  #viewer {
    height: 800px;
    width: 100%;
    border: 2px solid #ccc;
  }
</style>

4. Error Handling

const viewer = PDFViewer.init(container, course, {
  onError: (error) => {
    const { type, message, error: err } = error;

    switch(type) {
      case 'load':
        console.error('Failed to load PDF:', message);
        break;
      case 'render':
        console.error('Failed to render page:', message);
        break;
      default:
        console.error('Error:', message);
    }

    // You could show a user-friendly message
    if (type === 'load') {
      showNotification('Failed to load PDF. Please try again.');
    }
  }
});

Troubleshooting

1. PDF Fails to Load

Symptoms: Error alert after clicking a chapter, "Error loading PDF" message

Solutions:

  • Check CORS: The PDF URL must be CORS-enabled. If hosting on a different domain, configure your server:

    Access-Control-Allow-Origin: *
  • Verify URL: Ensure the PDF URL is correct and accessible:

    // Test in browser console
    fetch('your-pdf-url').then(r => console.log(r.status))
  • Browser Console: Check browser console for detailed error messages (F12 → Console tab)

2. Sidebar Not Showing on Mobile

Symptoms: Chapters sidebar is hidden on mobile devices

Expected behavior: The sidebar is hidden by default on mobile (<768px width) and can be toggled with the menu button (☰).

Solution: Use the hamburger menu button to toggle the sidebar, or increase your browser window width.

3. PDF.js Worker Not Loading

Symptoms: Console error about "pdf.worker.min.js"

Possible causes:

  • CDN is blocked or inaccessible
  • Script is hosted offline without CORS configuration

Solution:

  • Check internet connection
  • Check browser console for network errors
  • For offline use, download both pdf.min.js and pdf.worker.min.js and update URLs

4. Canvas Rendering Issues

Symptoms: Blank canvas, blurry text, or rendering errors

Solutions:

  • Verify PDF validity: The PDF might be corrupted. Test with a known-good PDF
  • Check container size: Ensure the container has dimensions (height/width)
  • Clear browser cache: Ctrl+Shift+Delete (or Cmd+Shift+Delete on Mac)

5. High CPU Usage

Symptoms: Browser feels slow, high CPU usage in Task Manager

Possible causes:

  • Rapidly resizing window
  • Large PDF with many pages

Solutions:

  • Debouncing is built in, but rendering triggers on resize. This is expected behavior for dynamic sizing.
  • Use smaller PDFs or convert large PDFs to multiple chapters

Examples

Example 1: Basic Local PDFs

Load PDFs from your local server:

const course = {
  title: "Training Materials",
  modules: [
    {
      title: "Week 1",
      chapters: [
        { title: "Monday", pdf: "/pdfs/monday.pdf" },
        { title: "Tuesday", pdf: "/pdfs/tuesday.pdf" },
        { title: "Wednesday", pdf: "/pdfs/wednesday.pdf" }
      ]
    },
    {
      title: "Week 2",
      chapters: [
        { title: "Thursday", pdf: "/pdfs/thursday.pdf" },
        { title: "Friday", pdf: "/pdfs/friday.pdf" }
      ]
    }
  ]
};

PDFViewer.init(document.getElementById('viewer'), course);

Example 2: Error Handling with User Feedback

const viewer = PDFViewer.init(container, course, {
  onError: (error) => {
    const message = error.type === 'load'
      ? 'Unable to load PDF. Check your internet connection.'
      : 'Failed to display PDF. Please try with another browser.';

    document.getElementById('error-message').textContent = message;
  }
});

Example 3: Remote PDFs with Dynamic Loading

// Load course data from API
fetch('/api/course')
  .then(res => res.json())
  .then(course => {
    const viewer = PDFViewer.init(
      document.getElementById('viewer'),
      course
    );
  });

Example 4: Multiple Viewers on Same Page

const course1 = { /* ... */ };
const course2 = { /* ... */ };

const viewer1 = PDFViewer.init(
  document.getElementById('viewer1'),
  course1
);

const viewer2 = PDFViewer.init(
  document.getElementById('viewer2'),
  course2
);

// Navigate both independently
viewer1.nextPage();
viewer2.loadChapter(0, 1);

Example 5: Manual Navigation Control

const viewer = PDFViewer.init(container, course);

// Custom button controls
document.getElementById('jump-to-chapter-2').onclick = () => {
  viewer.loadChapter(0, 2);
};

// Display current progress
document.getElementById('progress').textContent =
  `Module ${viewer.currentModule + 1}, ` +
  `Chapter ${viewer.currentChapter + 1}, ` +
  `Page ${viewer.currentPage} of ${viewer.pdfDoc?.numPages || '?'}`;

Browser Support

Browser Version Status
Chrome 45+ Full support
Firefox 40+ Full support
Safari 10+ Full support
Edge 12+ Full support
IE 11 Limited (no canvas HiDPI)
Mobile Chrome Latest 2 Full support
Mobile Safari Latest 2 Full support

Note: Uses ResizeObserver and other modern APIs. For older browsers, consider using polyfills.

Performance Tips

1. Optimize PDF Files

  • Compress PDFs: Use tools like ImageMagick or GhostScript to reduce file size
  • Remove unnecessary content: Delete unused fonts, images, and annotations
  • Split large documents: Break very large PDFs into multiple chapters

2. Caching

Print-to-PDF usually compresses well. For web-native PDFs, ensure your server sends proper cache headers:

Cache-Control: public, max-age=31536000

3. Content Delivery

  • Use a CDN to serve PDFs from servers closest to users
  • Compress responses with gzip

4. Initial Load

  • Don't preload all PDFs, load on-demand
  • Consider showing a preview or loading spinner

5. Memory Management

  • Call viewer.destroy() when done with a viewer instance
  • Close unused viewer instances
  • For multiple viewers, limit active instances

Limitations & Known Issues

Limitations

  1. No search functionality (use pdf.js directly if needed)
  2. No annotations (view-only)
  3. Performance degrades with very large PDFs (500+ pages per chapter)

Known Issues

  1. CORS requirement: PDFs must be from CORS-enabled servers
  2. Worker requirement: pdf.js worker must be accessible
  3. Mobile sidebar: Sidebar auto-closes after chapter selection for UX
  4. High-DPI rendering: Slightly higher memory usage on Retina/4K displays

Workarounds

For CSS-based zoom scaling (alternative to built-in controls):

.pdf-viewer-canvas-container {
  transform: scale(1.2);
  transform-origin: top center;
}

Note: Built-in zoom controls (100-200%) are now available and are recommended for better UX.

For search: Consider using the full pdf.js library or adding a search layer over the viewer.


Need Help?

License

MIT License - See LICENSE.txt for details

Credits


This project, SimplePDFviewer, is not affiliated with Mozilla, the developers of the pdf.js library that it uses. While the project is open-source and free to use, it is not officially endorsed or supported by Mozilla. If you have any issues or need help with the viewer, please refer to the GitHub issues page or the documentation provided in the repository.