Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
omega-slender authored Jun 18, 2024
1 parent 938044e commit 66eef49
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
Binary file added images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Affinity Thumbnail Viewer - View and Download Thumbnails</title>
<meta name="description" content="Affinity Thumbnail Viewer is a tool for viewing and downloading thumbnails from .afphoto and .afdesign files.">
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="images/icon.png" type="image/png">
</head>
<body>
<div class="container">
<h1>AFFINITY THUMBNAIL VIEWER</h1>
<input type="file" id="fileInput" accept=".afphoto,.afdesign">
<label for="fileInput">Choose a file</label>
<div id="output"></div>
<a id="downloadBtn" download="">Download Image</a>
<div class="credits">Page created by <strong><a href="https://linktr.ee/omega_slender" target="_blank" rel="noopener">Omega Slender</a></strong>, based on the work of <strong><a href="https://forum.affinity.serif.com/index.php?/topic/180457-afthumbs-extracting-png-thumbnails-from-afphoto-and-afdesign-files/" target="_blank" rel="noopener">v_kyr</a></strong></div>
</div>
<script src="script.js"></script>
</body>
</html>
70 changes: 70 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
document.getElementById('fileInput').addEventListener('change', () => {
const fileInput = document.getElementById('fileInput');
const output = document.getElementById('output');
const downloadBtn = document.getElementById('downloadBtn');

if (fileInput.files.length === 0) {
alert('Please select a file.');
return;
}

const file = fileInput.files[0];

if (!file.name.match(/\.(afphoto|afdesign)$/i)) {
alert('Please select a .afphoto or .afdesign file.');
return;
}

const reader = new FileReader();
reader.onload = function(event) {
const arrayBuffer = event.target.result;
const binaryData = new Uint8Array(arrayBuffer);

const images = extractPngs(binaryData);
if (images.length > 0) {
const outputImage = images.reduce((a, b) => a.byteLength < b.byteLength ? a : b);
const blob = new Blob([outputImage], { type: 'image/png' });
const url = URL.createObjectURL(blob);

const img = document.createElement('img');
img.src = url;
output.innerHTML = '';
output.appendChild(img);

downloadBtn.href = url;
downloadBtn.download = file.name.replace(/\.(afphoto|afdesign)$/i, '.png');
downloadBtn.style.display = 'inline-block';
} else {
output.textContent = 'No PNG images found in the file.';
downloadBtn.style.display = 'none';
}
};
reader.readAsArrayBuffer(file);
});

function extractPngs(binaryData) {
const images = [];
const numBytes = binaryData.length;

for (let i = 0; i < numBytes; i++) {
if (
binaryData[i] === 0x89 &&
binaryData[i + 1] === 0x50 &&
binaryData[i + 2] === 0x4E &&
binaryData[i + 3] === 0x47 &&
binaryData[i + 4] === 0x0D &&
binaryData[i + 5] === 0x0A &&
binaryData[i + 6] === 0x1A &&
binaryData[i + 7] === 0x0A
) {
const pngSize = binaryData[i + 8] |
(binaryData[i + 9] << 8) |
(binaryData[i + 10] << 16) |
(binaryData[i + 11] << 24);
const pngData = binaryData.slice(i, i + pngSize + 8);
images.push(pngData);
}
}

return images;
}
133 changes: 133 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
body {
background: linear-gradient(135deg, #FC466B, #3F5EFB);
font-family: 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
color: #E0E0E0;
}

.container {
background-color: #1E1E1E;
padding: 20px;
border-radius: 15px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
text-align: center;
max-width: 550px;
width: 100%;
}

h1 {
margin-bottom: 20px;
font-size: 28px;
color: #FFFFFF;
font-weight: bold;
}

input[type="file"] {
display: none;
}

label {
background-color: #3B3B3B;
color: #E0E0E0;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}

label:hover {
background-color: #575757;
}

#output {
margin-top: 20px;
}

#output img {
max-width: 1000px;
max-height: 350px;
max-width: 100%;
height: auto;
border-radius: 10px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
}

#downloadBtn {
background: linear-gradient(135deg, #4CAF50, #009688);
color: #ffffff;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
display: none;
margin-top: 10px;
text-decoration: none;
background-size: 200% 200%;
}

#downloadBtn:hover,
#downloadBtn:active {
background: linear-gradient(135deg, #54d058, #00baa8);
}

.credits {
font-size: 12px;
color: #E0E0E0;
margin-top: 20px;
}

.credits a {
color: #E0E0E0;
text-decoration: none;
transition: color 0.3s ease;
}

.credits a:hover {
color: #FFFFFF;
}

.credits strong {
font-weight: bold;
}

@media (max-width: 600px) {
.container {
padding: 20px;
border-radius: 10px;
}

h1 {
font-size: 20px;
margin-bottom: 15px;
}

label {
padding: 10px 15px;
}

#downloadBtn {
padding: 10px 15px;
}
}

@media (max-width: 400px) {
.container {
padding: 15px;
border-radius: 8px;
}

h1 {
font-size: 18px;
margin-bottom: 10px;
}

label {
padding: 8px 10px;
}

#downloadBtn {
padding: 8px 10px;
}
}

0 comments on commit 66eef49

Please sign in to comment.