<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Image to PDF Converter - Windows</title>
<style>
@import url('[Link]
family=Poppins:wght@600&display=swap');
:root {
--bg-color: #ffffff;
--primary-color: #111827;
--secondary-color: #6b7280;
--accent-color: #2563eb;
--card-bg: #f9fafb;
--shadow-color: rgba(0,0,0,0.05);
--border-radius: 0.75rem;
--transition-speed: 0.3s;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background: var(--bg-color);
color: var(--primary-color);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
position: sticky;
top: 0;
background: var(--bg-color);
border-bottom: 1px solid #e5e7eb;
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 10;
}
header h1 {
font-size: 1.75rem;
font-weight: 700;
margin: 0;
color: var(--primary-color);
}
main {
max-width: 800px;
margin: 2rem auto 4rem;
padding: 0 1rem;
flex-grow: 1;
}
.hero {
text-align: center;
margin-bottom: 3rem;
}
.hero h2 {
font-size: 2.75rem;
font-weight: 700;
margin-bottom: 0.5rem;
color: var(--primary-color);
}
.hero p {
font-weight: 500;
color: var(--secondary-color);
font-size: 1.125rem;
margin-top: 0;
}
.upload-section {
background: var(--card-bg);
padding: 2rem;
border-radius: var(--border-radius);
box-shadow: 0 4px 6px var(--shadow-color);
display: flex;
flex-direction: column;
gap: 1.5rem;
}
[Link] {
background-color: var(--accent-color);
color: white;
padding: 0.75rem 1.5rem;
font-weight: 600;
font-size: 1rem;
border: none;
border-radius: var(--border-radius);
cursor: pointer;
transition: background-color var(--transition-speed);
display: inline-block;
text-align: center;
user-select: none;
align-self: center;
}
[Link]:hover {
background-color: #1d4ed8;
}
input[type="file"] {
display: none;
}
#preview {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(100px,1fr));
gap: 1rem;
max-height: 300px;
overflow-y: auto;
padding: 0.25rem;
}
#preview img {
width: 100%;
height: 100px;
object-fit: cover;
border-radius: var(--border-radius);
box-shadow: 0 2px 4px var(--shadow-color);
transition: transform var(--transition-speed);
cursor: default;
}
#preview img:hover {
transform: scale(1.05);
}
.actions {
display: flex;
justify-content: center;
gap: 1rem;
}
[Link]-btn {
background-color: var(--accent-color);
color: white;
padding: 0.75rem 2rem;
font-weight: 700;
font-size: 1.125rem;
border: none;
border-radius: var(--border-radius);
cursor: pointer;
transition: background-color var(--transition-speed);
user-select: none;
}
[Link]-btn:disabled {
background-color: #a5b4fc;
cursor: not-allowed;
}
[Link]-btn:hover:not(:disabled) {
background-color: #1e40af;
}
footer {
text-align: center;
color: var(--secondary-color);
padding: 1rem 0;
font-size: 0.875rem;
}
/* Scrollbar styling for preview */
#preview::-webkit-scrollbar {
height: 8px;
}
#preview::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: var(--border-radius);
}
#preview::-webkit-scrollbar-track {
background-color: transparent;
}
</style>
</head>
<body>
<header>
<h1>Image to PDF Converter</h1>
</header>
<main>
<section class="hero" aria-label="Application introduction">
<h2>Convert your images into a PDF</h2>
<p>Select multiple images and combine them into a single downloadable PDF
file.</p>
</section>
<section class="upload-section" aria-label="Image selection and preview">
<label for="image-input" class="button" role="button" tabindex="0" aria-
controls="preview" aria-describedby="upload-instruction">
Select Images
</label>
<input type="file" id="image-input" accept="image/*" multiple aria-
describedby="upload-instruction" />
<div id="preview" aria-live="polite" aria-atomic="true" aria-
relevant="additions removals" aria-label="Selected image previews"></div>
<div class="actions">
<button id="convert-btn" class="convert-btn" disabled>Convert to PDF</button>
</div>
</section>
</main>
<footer>
© 2024 Image to PDF Converter
</footer>
<script src="[Link]
crossorigin="anonymous"></script>
<script>
(() => {
const { jsPDF } = [Link];
const imageInput = [Link]('image-input');
const preview = [Link]('preview');
const convertBtn = [Link]('convert-btn');
let images = [];
function resetPreview() {
[Link] = '';
images = [];
[Link] = true;
}
function addImagePreview(file) {
const imgElement = [Link]('img');
[Link] = [Link];
const reader = new FileReader();
[Link] = (e) => {
[Link] = [Link];
};
[Link](file);
[Link](imgElement);
[Link]({ file, dataUrl: null });
// We will assign dataUrl later when loading fully
}
// Read all files and get their DataURLs for PDF embedding
async function loadImages(files) {
const loadedImages = [];
for (const file of files) {
[Link](await new Promise((resolve, reject) => {
const reader = new FileReader();
[Link] = () => resolve({ file, dataUrl: [Link] });
[Link] = () => reject(new Error("Failed to read image: " +
[Link]));
[Link](file);
}));
}
return loadedImages;
}
[Link]('change', async (e) => {
resetPreview();
const files = [...[Link]];
if ([Link] === 0) return;
// Show previews immediately
[Link](addImagePreview);
// Load all images data URLs for pdf later
images = await loadImages(files);
[Link] = [Link] === 0;
});
function imgToPdfDimensions(imgWidth, imgHeight, pdfWidth, pdfHeight) {
// Calculate scaled dimensions keeping aspect ratio inside the pdf page
const widthRatio = pdfWidth / imgWidth;
const heightRatio = pdfHeight / imgHeight;
const scale = [Link](widthRatio, heightRatio);
return {
width: imgWidth * scale,
height: imgHeight * scale,
};
}
[Link]('click', async () => {
if ([Link] === 0) return;
[Link] = true;
[Link] = "Converting...";
// Create a pdf instance - standard A4 in points (210mm x 297mm approx)
const pdf = new jsPDF({
unit: 'pt',
format: 'a4',
compress: true,
});
const pdfWidth = [Link]();
const pdfHeight = [Link]();
for (let i = 0; i < [Link]; i++) {
const img = new Image();
[Link] = images[i].dataUrl;
// Wait for image to load to get dimensions
await new Promise((resolve) => {
[Link] = () => {
const { width, height } = imgToPdfDimensions([Link], [Link],
pdfWidth, pdfHeight);
// Center image in pdf page
const x = (pdfWidth - width) / 2;
const y = (pdfHeight - height) / 2;
[Link](img, 'JPEG', x, y, width, height);
if (i < [Link] - 1) [Link]();
resolve();
};
[Link] = () => {
alert("Failed to load an image for PDF, skipping.");
resolve();
};
});
}
[Link]('[Link]');
[Link] = "Convert to PDF";
[Link] = false;
});
})();
</script>
</body>
</html>