259 lines
8.5 KiB
TypeScript
259 lines
8.5 KiB
TypeScript
import { state } from '../state.js';
|
|
import { getStandardPageName, convertPoints } from '../utils/helpers.js';
|
|
import { icons, createIcons } from 'lucide';
|
|
|
|
let analyzedPagesData: any = []; // Store raw data to avoid re-analyzing
|
|
|
|
function calculateAspectRatio(width: number, height: number): string {
|
|
const ratio = width / height;
|
|
return ratio.toFixed(3);
|
|
}
|
|
|
|
function calculateArea(width: number, height: number, unit: string): string {
|
|
const areaInPoints = width * height;
|
|
let convertedArea = 0;
|
|
let unitSuffix = '';
|
|
|
|
switch (unit) {
|
|
case 'in':
|
|
convertedArea = areaInPoints / (72 * 72); // 72 points per inch
|
|
unitSuffix = 'in²';
|
|
break;
|
|
case 'mm':
|
|
convertedArea = areaInPoints / (72 * 72) * (25.4 * 25.4); // Convert to mm²
|
|
unitSuffix = 'mm²';
|
|
break;
|
|
case 'px':
|
|
const pxPerPoint = 96 / 72;
|
|
convertedArea = areaInPoints * (pxPerPoint * pxPerPoint);
|
|
unitSuffix = 'px²';
|
|
break;
|
|
default: // 'pt'
|
|
convertedArea = areaInPoints;
|
|
unitSuffix = 'pt²';
|
|
break;
|
|
}
|
|
|
|
return `${convertedArea.toFixed(2)} ${unitSuffix}`;
|
|
}
|
|
|
|
|
|
function getSummaryStats() {
|
|
const totalPages = analyzedPagesData.length;
|
|
|
|
// Count unique page sizes
|
|
const uniqueSizes = new Map();
|
|
analyzedPagesData.forEach((pageData: any) => {
|
|
const key = `${pageData.width.toFixed(2)}x${pageData.height.toFixed(2)}`;
|
|
const label = `${pageData.standardSize} (${pageData.orientation})`;
|
|
uniqueSizes.set(key, {
|
|
count: (uniqueSizes.get(key)?.count || 0) + 1,
|
|
label: label,
|
|
width: pageData.width,
|
|
height: pageData.height
|
|
});
|
|
});
|
|
|
|
const hasMixedSizes = uniqueSizes.size > 1;
|
|
|
|
return {
|
|
totalPages,
|
|
uniqueSizesCount: uniqueSizes.size,
|
|
uniqueSizes: Array.from(uniqueSizes.values()),
|
|
hasMixedSizes
|
|
};
|
|
}
|
|
|
|
function renderSummary() {
|
|
const summaryContainer = document.getElementById('dimensions-summary');
|
|
if (!summaryContainer) return;
|
|
|
|
const stats = getSummaryStats();
|
|
|
|
let summaryHTML = `
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
<div class="bg-gray-900 border border-gray-700 rounded-lg p-4">
|
|
<p class="text-sm text-gray-400 mb-1">Total Pages</p>
|
|
<p class="text-2xl font-bold text-white">${stats.totalPages}</p>
|
|
</div>
|
|
<div class="bg-gray-900 border border-gray-700 rounded-lg p-4">
|
|
<p class="text-sm text-gray-400 mb-1">Unique Page Sizes</p>
|
|
<p class="text-2xl font-bold text-white">${stats.uniqueSizesCount}</p>
|
|
</div>
|
|
<div class="bg-gray-900 border border-gray-700 rounded-lg p-4">
|
|
<p class="text-sm text-gray-400 mb-1">Document Type</p>
|
|
<p class="text-2xl font-bold ${stats.hasMixedSizes ? 'text-yellow-400' : 'text-green-400'}">
|
|
${stats.hasMixedSizes ? 'Mixed Sizes' : 'Uniform'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
if (stats.hasMixedSizes) {
|
|
summaryHTML += `
|
|
<div class="bg-yellow-900/20 border border-yellow-500/30 rounded-lg p-4 mb-4">
|
|
<div class="flex items-start gap-3">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-yellow-400 mt-0.5 flex-shrink-0"></i>
|
|
<div>
|
|
<h4 class="text-yellow-200 font-semibold mb-2">Mixed Page Sizes Detected</h4>
|
|
<p class="text-sm text-gray-300 mb-3">This document contains pages with different dimensions:</p>
|
|
<ul class="space-y-1 text-sm text-gray-300">
|
|
${stats.uniqueSizes.map((size: any) => `
|
|
<li>• ${size.label}: ${size.count} page${size.count > 1 ? 's' : ''}</li>
|
|
`).join('')}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
summaryContainer.innerHTML = summaryHTML;
|
|
|
|
if (stats.hasMixedSizes) {
|
|
createIcons({ icons });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders the dimensions table based on the stored data and selected unit.
|
|
* @param {string} unit The unit to display dimensions in ('pt', 'in', 'mm', 'px').
|
|
*/
|
|
function renderTable(unit: any) {
|
|
const tableBody = document.getElementById('dimensions-table-body');
|
|
if (!tableBody) return;
|
|
|
|
tableBody.textContent = ''; // Clear the table body safely
|
|
|
|
analyzedPagesData.forEach((pageData) => {
|
|
const width = convertPoints(pageData.width, unit);
|
|
const height = convertPoints(pageData.height, unit);
|
|
const aspectRatio = calculateAspectRatio(pageData.width, pageData.height);
|
|
const area = calculateArea(pageData.width, pageData.height, unit);
|
|
|
|
const row = document.createElement('tr');
|
|
|
|
// Page number
|
|
const pageNumCell = document.createElement('td');
|
|
pageNumCell.className = 'px-4 py-3 text-white';
|
|
pageNumCell.textContent = pageData.pageNum;
|
|
|
|
// Dimensions
|
|
const dimensionsCell = document.createElement('td');
|
|
dimensionsCell.className = 'px-4 py-3 text-gray-300';
|
|
dimensionsCell.textContent = `${width} x ${height} ${unit}`;
|
|
|
|
// Standard size
|
|
const sizeCell = document.createElement('td');
|
|
sizeCell.className = 'px-4 py-3 text-gray-300';
|
|
sizeCell.textContent = pageData.standardSize;
|
|
|
|
// Orientation
|
|
const orientationCell = document.createElement('td');
|
|
orientationCell.className = 'px-4 py-3 text-gray-300';
|
|
orientationCell.textContent = pageData.orientation;
|
|
|
|
// Aspect Ratio
|
|
const aspectRatioCell = document.createElement('td');
|
|
aspectRatioCell.className = 'px-4 py-3 text-gray-300';
|
|
aspectRatioCell.textContent = aspectRatio;
|
|
|
|
// Area
|
|
const areaCell = document.createElement('td');
|
|
areaCell.className = 'px-4 py-3 text-gray-300';
|
|
areaCell.textContent = area;
|
|
|
|
// Rotation
|
|
const rotationCell = document.createElement('td');
|
|
rotationCell.className = 'px-4 py-3 text-gray-300';
|
|
rotationCell.textContent = `${pageData.rotation}°`;
|
|
|
|
row.append(pageNumCell, dimensionsCell, sizeCell, orientationCell, aspectRatioCell, areaCell, rotationCell);
|
|
tableBody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function exportToCSV() {
|
|
const unitsSelect = document.getElementById('units-select') as HTMLSelectElement;
|
|
const unit = unitsSelect?.value || 'pt';
|
|
|
|
const headers = ['Page #', `Width (${unit})`, `Height (${unit})`, 'Standard Size', 'Orientation', 'Aspect Ratio', `Area (${unit}²)`, 'Rotation'];
|
|
const csvRows = [headers.join(',')];
|
|
|
|
analyzedPagesData.forEach((pageData: any) => {
|
|
const width = convertPoints(pageData.width, unit);
|
|
const height = convertPoints(pageData.height, unit);
|
|
const aspectRatio = calculateAspectRatio(pageData.width, pageData.height);
|
|
const area = calculateArea(pageData.width, pageData.height, unit);
|
|
|
|
const row = [
|
|
pageData.pageNum,
|
|
width,
|
|
height,
|
|
pageData.standardSize,
|
|
pageData.orientation,
|
|
aspectRatio,
|
|
area,
|
|
`${pageData.rotation}°`
|
|
];
|
|
csvRows.push(row.join(','));
|
|
});
|
|
|
|
const csvContent = csvRows.join('\n');
|
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
const url = URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = 'page-dimensions.csv';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
/**
|
|
* Main function to analyze the PDF and display dimensions.
|
|
* This is called once after the file is loaded.
|
|
*/
|
|
export function analyzeAndDisplayDimensions() {
|
|
if (!state.pdfDoc) return;
|
|
|
|
analyzedPagesData = []; // Reset stored data
|
|
const pages = state.pdfDoc.getPages();
|
|
|
|
pages.forEach((page: any, index: any) => {
|
|
const { width, height } = page.getSize();
|
|
const rotation = page.getRotation().angle || 0;
|
|
|
|
analyzedPagesData.push({
|
|
pageNum: index + 1,
|
|
width, // Store raw width in points
|
|
height, // Store raw height in points
|
|
orientation: width > height ? 'Landscape' : 'Portrait',
|
|
standardSize: getStandardPageName(width, height),
|
|
rotation: rotation
|
|
});
|
|
});
|
|
|
|
const resultsContainer = document.getElementById('dimensions-results');
|
|
const unitsSelect = document.getElementById('units-select');
|
|
|
|
renderSummary();
|
|
|
|
// Initial render with default unit (points)
|
|
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
|
|
renderTable(unitsSelect.value);
|
|
|
|
resultsContainer.classList.remove('hidden');
|
|
|
|
unitsSelect.addEventListener('change', (e) => {
|
|
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'EventTarg... Remove this comment to see the full error message
|
|
renderTable(e.target.value);
|
|
});
|
|
|
|
const exportButton = document.getElementById('export-csv-btn');
|
|
if (exportButton) {
|
|
exportButton.addEventListener('click', exportToCSV);
|
|
}
|
|
}
|