16- Merge PDFs
17- Split PDF
18- PDF to Images
19- Images to PDF
20- Compress PDF
21- PDF Info
34### File Upload
35- Drag and drop support
36- Multiple file selection for merge/images-to-pdf
37- File type validation
38- File size display
43- **Rotate**: Angle selection and page targeting
44- **Protect**: Password input
45- **PDF to Images**: Format and quality selection
4647### Processing
19- **Response**: PDF file download or JSON error
2021### POST /api/pdf/pdf-to-images
22Convert PDF pages to images (info only - actual conversion requires additional libraries).
23- **Body**: FormData with `file0` and `options`
24- **Options**: `{ format?: 'png'|'jpg', dpi?: number }`
25- **Response**: JSON with conversion info
2627### POST /api/pdf/images-to-pdf
28Convert images to a PDF document.
29- **Body**: FormData with multiple image files
30- **Response**: PDF file download or JSON error
31
138});
139140// PDF to Images (simplified - returns info about conversion)
141app.post("/pdf-to-images", async (c) => {
142try {
143const { files, options } = await parseFormData(c.req.raw);
150const info = await getPDFInfo(pdfBytes);
151
152// Note: Actual image conversion would require additional libraries like pdf2pic
153// For now, we return information about what would be converted
154return c.json({
165});
166167// Images to PDF
168app.post("/images-to-pdf", async (c) => {
169try {
170const { files } = await parseFormData(c.req.raw);
171
172if (files.length === 0) {
173return c.json({ success: false, message: "At least one image file is required" });
174}
175177178for (const file of files) {
179const imageBytes = new Uint8Array(await file.arrayBuffer());
180let image;
181182if (file.type === 'image/png') {
183image = await pdfDoc.embedPng(imageBytes);
184} else if (file.type === 'image/jpeg' || file.type === 'image/jpg') {
185image = await pdfDoc.embedJpg(imageBytes);
186} else {
187continue; // Skip unsupported formats
189190const page = pdfDoc.addPage();
191const { width, height } = image.scale(1);
192
193// Scale image to fit page
194const pageWidth = page.getWidth();
195const pageHeight = page.getHeight();
199const scaledHeight = height * scale;
200
201page.drawImage(image, {
202x: (pageWidth - scaledWidth) / 2,
203y: (pageHeight - scaledHeight) / 2,
212headers: {
213'Content-Type': 'application/pdf',
214'Content-Disposition': 'attachment; filename="images.pdf"'
215}
216});
218return c.json({
219success: false,
220message: error instanceof Error ? error.message : "Failed to create PDF from images"
221});
222}
pdfFileUpload.tsx10 matches
16const fileInputRef = useRef<HTMLInputElement>(null);
1718const acceptedTypes = tool.type === 'images-to-pdf'
19? 'image/png,image/jpeg,image/jpg'
20: 'application/pdf';
2145const handleFiles = (files: File[]) => {
46const validFiles = files.filter(file => {
47if (tool.type === 'images-to-pdf') {
48return file.type.startsWith('image/');
49}
50return file.type === 'application/pdf';
189);
190191case 'pdf-to-images':
192return (
193<div className="space-y-4">
236ref={fileInputRef}
237type="file"
238multiple={tool.type === 'merge' || tool.type === 'images-to-pdf'}
239accept={acceptedTypes}
240onChange={handleFileSelect}
246<div>
247<p className="text-lg font-medium text-gray-900">
248{tool.type === 'images-to-pdf' ? 'Drop images here or click to browse' : 'Drop PDF files here or click to browse'}
249</p>
250<p className="text-sm text-gray-500 mt-1">
251{tool.type === 'merge' ? 'Select multiple PDF files to merge' :
252tool.type === 'images-to-pdf' ? 'Supports PNG, JPG, JPEG formats' :
253'Select a PDF file to process'}
254</p>
313<li>• Enter a password to protect the PDF</li>
314)}
315{tool.type === 'images-to-pdf' && (
316<li>• Upload PNG, JPG, or JPEG image files</li>
317)}
318<li>• Maximum file size: 50MB per file</li>
pdfToolSelector.tsx6 matches
21},
22{
23type: 'pdf-to-images',
24name: 'PDF to Images',
25description: 'Convert PDF pages to PNG or JPG images',
26icon: '🖼️'
27},
28{
29type: 'images-to-pdf',
30name: 'Images to PDF',
31description: 'Convert images to a PDF document',
32icon: '📸'
33},
pdfindex.html1 match
7<script src="https://cdn.twind.style" crossorigin></script>
8<script src="https://esm.town/v/std/catch"></script>
9<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📄</text></svg>">
10<style>
11.file-drop-zone {
23export interface PDFOperation {
4type: 'merge' | 'split' | 'pdf-to-images' | 'images-to-pdf' | 'compress' | 'info' | 'protect' | 'rotate';
5name: string;
6description: string;
7- **PDF Merge**: Combine multiple PDF files into one
8- **PDF Split**: Extract specific pages or split into separate files
9- **PDF to Images**: Convert PDF pages to PNG/JPG images
10- **Images to PDF**: Convert images to PDF format
11- **PDF Compression**: Reduce PDF file size
12- **PDF Info**: View PDF metadata and page count
37381. Select a PDF operation from the tool selector
392. Upload your PDF file(s) or images
403. Configure operation parameters
414. Process and download the result
sqliteExplorerAppREADME.md1 match
3View and interact with your Val Town SQLite data. It's based off Steve's excellent [SQLite Admin](https://www.val.town/v/stevekrouse/sqlite_admin?v=46) val, adding the ability to run SQLite queries directly in the interface. This new version has a revised UI and that's heavily inspired by [LibSQL Studio](https://github.com/invisal/libsql-studio) by [invisal](https://github.com/invisal). This is now more an SPA, with tables, queries and results showing up on the same page.
45
67## Install
testPondiversemain3 matches
3import getCreation from "./getCreation";
4import getCreationData from "./getCreationData";
5import getCreationImage from "./getCreationImage";
6import getCreations from "./getCreations";
7import updateTable from "./updateTable";
15case "/get-creation":
16return getCreation(req);
17case "/get-creation-image":
18return getCreationImage(req);
19case "/get-creation-data":
20return getCreationData(req);