pdf-ctrl
v1.1.8
Published
Merge, rearrange pages, and download all-in-one PDF document
Maintainers
Readme
Merge and Reorganize PDF files
Here are the steps to run pdf-ctrl locally:
1. Install pdf-ctrl using npm package manager
npm i pdf-ctrl2. Add the following content to an index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF-Ctrl</title>
<link rel="stylesheet" href="dist/pdf-ctrl.min.css">
<script type="module" defer>
import PDFGrid from './dist/pdf-ctrl.min.js';
document.addEventListener('DOMContentLoaded', () => {
const openBtn = document.querySelector('#filePicker');
const reloadBtn = document.querySelector('#reload');
const downloadBtn = document.querySelector('#download');
const addBtn = document.querySelector('#addNewPage');
const removeBtn = document.querySelector('#mode');
const filePicker = document.createElement('input');
const grid = new PDFGrid('#main');
filePicker.accept = 'application/pdf';
filePicker.multiple = true;
filePicker.type = 'file';
reloadBtn.onclick = () => grid.reload();
filePicker.onchange = () => grid.render(filePicker.files);
addBtn.onclick = (e) => grid.addNewPage();
downloadBtn.onclick = (e) => grid.download();
openBtn.onclick = (e) => filePicker.showPicker();
removeBtn.onclick = (e) => {
if (removeBtn.value === 'Remove Pages') {
grid.toggleDeleteMode();
removeBtn.value = 'Remove Selected';
const cancelBtn = document.createElement('input');
cancelBtn.type = 'button';
cancelBtn.value = 'Cancel';
cancelBtn.classList = 'styled';
cancelBtn.onclick = (w) => {
grid.toggleDeleteMode();
removeBtn.value = 'Remove Pages';
cancelBtn.remove();
};
removeBtn.after(cancelBtn);
} else {
const deleteDialog = document.getElementById('delete-dialog');
deleteDialog.showModal();
deleteDialog.onclose = (w) => {
if (deleteDialog.returnValue === 'yes') {
grid.removePages();
}
}
}
}
});
</script>
<style>
.styled {
border: 0;
line-height: 2.5;
padding: 0 20px;
font-size: 1rem;
text-align: center;
color: white;
text-shadow: 1px 1px 1px black;
border-radius: 10px;
background-color: tomato;
background-image: linear-gradient(
to top left,
rgb(0 0 0 / 20%),
rgb(0 0 0 / 20%) 30%,
transparent
);
box-shadow:
inset 2px 2px 3px rgb(255 255 255 / 60%),
inset -2px -2px 3px rgb(0 0 0 / 60%);
}
.styled:hover {
font-weight: 300;
background-color: red;
}
.styled:active {
box-shadow:
inset -2px -2px 3px rgb(255 255 255 / 60%),
inset 2px 2px 3px rgb(0 0 0 / 60%);
}
#toolbar {
margin-bottom: 6px;
}
</style>
</head>
<body>
<div id="toolbar">
<input class="styled" type="button" value="Open" id="filePicker" />
<input class="styled" type="button" value="Reload" id="reload" />
<input class="styled" type="button" value="Add New Page" id="addNewPage" />
<input class="styled" type="button" value="Download" id="download" />
<input class="styled" type="button" value="Remove Pages" id="mode" />
</div>
<main id="main"></main>
<dialog id="delete-dialog">
<p>Remove seleted pages?</p>
<div style="display: flex; justify-content: center; align-items: center;gap: 4px;">
<button commandfor="delete-dialog" command="close" value="yes">Yes</button>
<button commandfor="delete-dialog" command="close" value="cancel">Cancel</button>
</div>
</dialog>
<footer>
<p>© 2026. Content Rights Reserved.</p>
</footer>
</body>
</html>3. Startup the http server
npx http-server --port 3000Or Simply Do This:
1. Clone it
git clone https://github.com/walber/pdf-ctrl.git2. Build it
npm run build3. Run it
npm run testHere's what you get:

