JIIT time table website

Home Projects JIIT time table website Export And Sharing Pdf And Png Export

PDF & PNG Export

Purpose

This document explains the PDF and PNG export functionality that allows users to download their weekly schedule as image files or PDF documents. The export system captures the rendered timeline view and converts it to downloadable formats using client-side libraries.

For information about sharing schedules via URL parameters, see Shareable URLs & Configuration Sharing. For syncing schedules to external calendar applications, see Google Calendar Integration.


Overview

The export system enables users to download their personalized timetables in two formats:

Format Use Case Library Used
PNG Quick sharing, social media, backgrounds html-to-image
PDF Printing, formal documentation, archival jsPDF + html-to-image

The export process involves three main components:

  1. Action Buttons (website/components/action-buttons.tsx) - UI triggers that initiate downloads
  2. Download Utilities (website/utils/download.ts) - Core export functions using image capture libraries
  3. Timeline Download Mode (website/app/timeline/page.tsx) - Special rendering mode optimized for capture

Sources: website/utils/download.ts1-150 website/components/action-buttons.tsx1-152


Export Pipeline Architecture

The following diagram illustrates the complete flow from user clicking a download button to receiving the file:

Architecture Diagram

Sources: website/components/action-buttons.tsx54-102 website/utils/download.ts1-150


Download Functions

downloadAsPng Function

The downloadAsPng function in website/utils/download.ts1-50 captures the target element as a PNG image using the html-to-image library.

Function Signature:

export const downloadAsPng = async (
  elementId: string,
  filename: string,
  callbacks?: {
    onProgress?: (msg: string) => void;
    onSuccess?: () => void;
    onError?: (err: Error) => void;
  }
): Promise<void>

Key Implementation Details:

Aspect Configuration Purpose
Quality quality: 1 (maximum) Lossless image capture
Background backgroundColor: '#131010' Match app dark theme
Pixel Ratio `window.devicePixelRatio
Transform scale(1) Prevent layout distortion

The function uses requestAnimationFrame to ensure the DOM is fully rendered before capture, synchronizing with the browser's rendering pipeline.

Sources: website/utils/download.ts1-50


downloadAsPdf Function

The downloadAsPdf function converts the captured PNG to a PDF document with A4 landscape orientation using jsPDF.

Function Signature:

export const downloadAsPdf = async (
  elementId: string,
  filename: string,
  callbacks?: {
    onProgress?: (msg: string) => void;
    onSuccess?: () => void;
    onError?: (err: Error) => void;
  }
): Promise<void>

PDF Configuration:

// A4 dimensions in mm (landscape)
const a4Width = 297;
const a4Height = 210;

const pdf = new jsPDF({
  orientation: 'landscape',
  unit: 'mm',
  format: [a4Width, a4Height],
});

The captured PNG image is first obtained using html-to-image.toPng(), then embedded into the PDF. The image is scaled proportionally to fit the A4 width while maintaining aspect ratio to prevent distortion.

Sources: website/utils/download.ts52-150


Timeline Download Mode

The Timeline page (website/app/timeline/page.tsx) supports a special "download mode" that optimizes the layout for capture.

Activation

Download mode is activated via URL query parameter:

const searchParams = useSearchParams();
const isDownloadMode = searchParams.get("download") === "1";

When a user clicks the PNG or PDF export button in ActionButtons, the component navigates to /timeline?download=1 website/components/action-buttons.tsx64 triggering this mode.

Layout Adjustments in Download Mode

When isDownloadMode is true, the Timeline component applies optimizations for clean image/PDF capture:

Modification Normal Mode Download Mode Purpose
Container Width Responsive Fixed wide layout Ensure full schedule visibility without scrolling
Display Width Auto Extended Accommodate all days in single view
Title Placement Outside grid Inside #schedule-display Include title in captured image
Welcome Banner Shown (dismissible) Hidden Clean export without UI overlays
Stats Section Visible Hidden Focus on schedule content only
Current Time Indicator Shown (animated) Hidden Avoid time-specific dynamic elements
Navigation Controls Visible Hidden Remove interactive UI from export

The #schedule-display element ID is specifically targeted by the download functions to capture the entire schedule grid including headers and all events.

Sources: website/components/action-buttons.tsx64 website/utils/download.ts1-150


Component Architecture

Architecture Diagram

Sources: website/components/action-buttons.tsx1-152 website/utils/download.ts1-150


Action Buttons Implementation

The ActionButtons component (website/components/action-buttons.tsx) manages the download workflow and user feedback.

State Management

const [loading, setLoading] = useState<null | "png" | "pdf">(null);
const [progressMsg, setProgressMsg] = useState("");

The loading state tracks which export is in progress, disabling buttons during operation website/components/action-buttons.tsx22-23

Download Handler

The handleDownload function orchestrates the export process:

Architecture Diagram

Key Steps:

  1. Navigation website/components/action-buttons.tsx64: Navigate to /timeline?download=1 using Next.js router to activate download mode
  2. Wait website/components/action-buttons.tsx65: 500ms delay ensures rendering completes
  3. Element Retrieval website/components/action-buttons.tsx67-68: Get #schedule-display DOM element
  4. Download Execution website/components/action-buttons.tsx78-101: Call appropriate download function with progress/success/error callbacks

Sources: website/components/action-buttons.tsx54-102


Display Schedule Selection

The component respects custom edits by using editedSchedule from UserContext when available:

const { editedSchedule } = useContext(UserContext);
const displaySchedule = editedSchedule || schedule;

This ensures that any user modifications made through the EditEventDialog (adding custom events, editing existing classes) are included in the exported PNG/PDF file website/components/action-buttons.tsx26

Sources: website/components/action-buttons.tsx19 website/components/action-buttons.tsx26


Image Capture Configuration

Quality Settings

Both export functions use identical image capture settings for consistency:

Parameter Value Purpose
quality 1 Maximum quality (no compression)
backgroundColor '#131010' Match app background color
pixelRatio `window.devicePixelRatio

The pixelRatio setting ensures sharp images on high-resolution displays (e.g., Retina screens) by using the device's native pixel density or defaulting to 2x for high-quality output.

Sources: website/utils/download.ts1-150


Error Handling

Both download functions implement try-catch error handling:

try {
  // Capture and download logic
  if (onSuccess) onSuccess();
} catch (err) {
  console.error('Error downloading PNG:', err);
  if (onError) onError(err instanceof Error ? err : new Error('Unknown error'));
}

Errors are:

  1. Logged to console for debugging purposes
  2. Passed to onError callback with proper Error typing
  3. Displayed to user via toast notifications website/components/action-buttons.tsx93-99

Sources: website/utils/download.ts1-150 website/components/action-buttons.tsx93-99


Toast Notification Integration

The export system provides real-time feedback through the useToast hook:

Progress Feedback

onProgress: (msg) => {
  setProgressMsg(msg);
  toast({
    title: msg,
    variant: "default",
  });
}

Progress Messages:

  • "Preparing image..." - Image capture initiated
  • "Saving image..." - PNG download started
  • "Preparing PDF..." - PDF generation initiated
  • "Saving PDF..." - PDF save started

Sources: website/components/action-buttons.tsx79-85 website/utils/download.ts1-150

Success Notification

onSuccess: () => {
  toast({
    title: `${type.toUpperCase()} ready!`,
    description: `Your schedule has been downloaded as a ${type.toUpperCase()}.`,
  });
  setLoading(null);
}

Sources: website/components/action-buttons.tsx86-92

Error Notification

onError: (err) => {
  toast({
    title: `Failed to download ${type.toUpperCase()}`,
    description: err.message,
    variant: "destructive",
  });
  setLoading(null);
}

Sources: website/components/action-buttons.tsx93-99


UI Components

Button States

The export buttons support three visual states:

State Condition Visual Indicator
Enabled loading === null Standard button styling
Loading (Self) loading === type Spinner icon (Loader2)
Disabled (Other) loading !== null Disabled state

Button Implementation:

<Button
  onClick={() => handleDownload(downloadAsPng, "png")}
  disabled={loading !== null}
  className="backdrop-blur-md bg-[#F0BB78]/30 border border-[#F0BB78]/20"
>
  {loading === "png" ? (
    <Loader2 className="w-4 h-4 mr-2 animate-spin" />
  ) : (
    <Download className="w-3 h-3 sm:w-4 sm:h-4 mr-2" />
  )}
  PNG
</Button>

Sources: website/components/action-buttons.tsx117-140


Download Mode Element Targeting

The export system specifically targets the #schedule-display element:

const element = document.getElementById("schedule-display");
if (!element) {
  toast({
    title: "Schedule not found!",
    description: "Please navigate to the timeline page first.",
    variant: "destructive",
  });
  setLoading(null);
  return;
}

This element is defined in the Timeline page and wraps the entire schedule grid, including:

  • Day headers (Monday through Saturday)
  • Time column with hourly slots
  • Event blocks with subject names, locations, and types
  • Title header (included in download mode only)

The download utilities receive this element ID as "schedule-display" website/components/action-buttons.tsx78

Sources: website/components/action-buttons.tsx67-76 website/components/action-buttons.tsx78


Library Dependencies

html-to-image

The html-to-image library provides the toPng function used for DOM-to-image conversion.

Import:

import { toPng } from 'html-to-image';

Key Features:

  • Converts DOM nodes to PNG images using canvas rendering
  • Supports custom dimensions, quality settings, and pixel ratios
  • Handles complex CSS styles and background colors
  • Returns base64-encoded data URLs

jsPDF

The jsPDF library generates PDF documents from the captured PNG image.

Import:

import jsPDF from 'jspdf';

Key Features:

  • Creates PDF documents programmatically
  • Supports landscape/portrait orientation
  • Embeds images with automatic scaling
  • Outputs directly to browser download

Sources: website/utils/download.ts1-150


Performance Considerations

Rendering Delay

The 500ms delay website/components/action-buttons.tsx65 after Next.js navigation ensures:

  1. Next.js completes the client-side route transition
  2. Timeline component renders with isDownloadMode=true
  3. Tailwind CSS styles are fully applied to the DOM
  4. Layout calculations and reflows stabilize
  5. All event blocks and grid elements are positioned correctly

Animation Frame Wait

Both download functions use requestAnimationFrame to synchronize with the browser's rendering pipeline:

await new Promise((resolve) => requestAnimationFrame(resolve));

This ensures the capture happens after the browser has completed layout and painting, resulting in accurate screenshots without visual glitches or missing elements.

Sources: website/components/action-buttons.tsx65 website/utils/download.ts1-150


File Naming

Both download functions accept a filename parameter and append the appropriate extension:

The default filename used is "schedule" src/components/action-buttons.tsx50 resulting in:

  • schedule.png
  • schedule.pdf

Sources: src/utils/download.ts38 src/utils/download.ts97 src/components/action-buttons.tsx50