After three PDF tools, the operator asked what to build next. Of the eight candidates I offered, they asked me to combine "merge and split PDF" with "photos to PDF." Both come down to taking pieces, laying them out as one list of pages, and binding them again in the order you want, so one screen was enough.
Copy, don't redraw
The PDF-to-image tool draws pages onto a canvas with pdf.js. Doing that here would turn text into pictures that can't be selected or searched, and make files several times larger. So pdf.js only draws the previews, and the output is built with pdf-lib (1.17.1), which copies each original page's content stream as it is.
My first version copied one page at a time, and I changed it. Every call to pdf-lib's copyPages copies the fonts and images the page uses all over again. Copying 23 pages from one file one by one can put the same Korean font in 23 times. Now the pages needed from each source file are gathered and copied in a single call, and the order is arranged afterwards. In testing, a 533 KB, 23-page PDF mixed with four photos, minus one page, came out at 478 KB, and the text extracted from page 3 matched the original page 3.
Photos lying on their side
Phone photos are stored the way the sensor sees them, with an EXIF orientation value saying how to stand them up. Put a JPG into a PDF as it is and PDF viewers ignore that value, so portrait shots fall over. I read the start of each JPG to find the orientation value, and only JPGs with value 1 (as stored) and PNGs go in byte for byte. Everything else is redrawn upright by the browser and saved as a quality-92 JPG. For a test I made a 1200×800 photo tagged with orientation 6 and confirmed it stood upright on a portrait A4 page in the output.
Encrypted PDFs
pdf-lib can't open encrypted PDFs, including ones with no open password that only restrict editing. pdf.js can open both, so when pdf-lib refuses a file, pdf.js draws its pages at 200 dpi and they go in as images. Those files are labeled "as images" next to their name so you know their text won't be searchable. I used pypdf to make one file with an open password and one with editing restrictions only. A wrong password was asked for again, and with the right one all five pages went in.
Dragging with a finger
With a mouse, dragging starts after 6 pixels of movement. The same rule on a phone would make a finger trying to scroll pick up a page instead. So on touch you hold for 0.28 seconds before a drag starts, and moving more than 10 pixels before that counts as scrolling. Once a drag starts, touchmove is blocked so the page doesn't scroll along with it. If several pages are selected, dragging one moves them all, and the list scrolls by itself near the edge of the screen.
A button with a borrowed name
In the last round of testing, the Cancel button in the password box was floating outside the box to the left, tilted. I had named the small picture that follows your finger during a drag "ghost," and the grey button style copied from the PDF tool was "btn ghost." The drag styles (fixed to the screen, tilted 3 degrees) landed on the button. I renamed the drag class to dragghost.
Result
I added one PDF and four photos (two JPGs, a PNG and a WebP), then rotated, removed, dragged with mouse and touch, moved with Alt+arrow, undid, and saved. All 26 pages came out in on-screen order, rotated pages carried their rotation, and photo pages had the right A4 orientation. Splitting by page produced 26 files, and the ranges "1-3, 9" produced two, of 3 pages and 1 page, in the ZIP.