mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-12 17:52:13 +02:00
feat(#3453): Add frontend for Remove Header/Footer feature
- Implement form submission with options to remove header, footer, or both - Add margin mode selection with visual preview of selected header/footer bounds - Integrate PDF.js for live PDF view in margin mode - Create dedicated CSS for styling the feature page - Add localization keys for en-GB and en-US - Implement JavaScript to handle PDF rendering, preview overlays, and submission logic
This commit is contained in:
parent
b5bdf5b1c2
commit
61729d324c
@ -1004,6 +1004,23 @@ crop.submit=Submit
|
|||||||
remove-header-footer.title=Remove Header/Footer
|
remove-header-footer.title=Remove Header/Footer
|
||||||
remove-header-footer.header=Remove Header/Footer
|
remove-header-footer.header=Remove Header/Footer
|
||||||
remove-header-footer.submit=Remove
|
remove-header-footer.submit=Remove
|
||||||
|
remove-header-footer.removeHeader=Header
|
||||||
|
remove-header-footer.removeFooter=Footer
|
||||||
|
remove-header-footer.headerMargin=Header Margin
|
||||||
|
remove-header-footer.footerMargin=Footer Margin
|
||||||
|
remove-header-footer.pages=Select Pages
|
||||||
|
remove-header-footer.selectMode=Select Mode
|
||||||
|
remove-header-footer.view-more=View More
|
||||||
|
remove-header-footer.previousPage=Previous Page
|
||||||
|
remove-header-footer.nextPage=Next Page
|
||||||
|
remove-header-footer.zoomOut=Zoom Out
|
||||||
|
remove-header-footer.zoomIn=Zoom In
|
||||||
|
remove-header-footer.close=Close
|
||||||
|
remove-header-footer.margin=Margin
|
||||||
|
remove-header-footer.auto=Auto
|
||||||
|
remove-header-footer.manual=Manual
|
||||||
|
remove-header-footer.enterValue=Enter Value
|
||||||
|
|
||||||
|
|
||||||
#autoSplitPDF
|
#autoSplitPDF
|
||||||
autoSplitPDF.title=Auto Split PDF
|
autoSplitPDF.title=Auto Split PDF
|
||||||
|
@ -751,6 +751,10 @@ home.validateSignature.title=Validate PDF Signature
|
|||||||
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
home.validateSignature.desc=Verify digital signatures and certificates in PDF documents
|
||||||
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate
|
||||||
|
|
||||||
|
home.remove-header-footer.title=Remove Header/Footer
|
||||||
|
home.remove-header-footer.desc=Remove the headers and/or footers from a PDF document
|
||||||
|
remove-header-footer.tags=remove headers, remove footers, remove, header, footer
|
||||||
|
|
||||||
#replace-invert-color
|
#replace-invert-color
|
||||||
replace-color.title=Replace-Invert-Color
|
replace-color.title=Replace-Invert-Color
|
||||||
replace-color.header=Replace-Invert Color PDF
|
replace-color.header=Replace-Invert Color PDF
|
||||||
@ -996,6 +1000,26 @@ crop.title=Crop
|
|||||||
crop.header=Crop PDF
|
crop.header=Crop PDF
|
||||||
crop.submit=Submit
|
crop.submit=Submit
|
||||||
|
|
||||||
|
#remove-header-footer
|
||||||
|
remove-header-footer.title=Remove Header/Footer
|
||||||
|
remove-header-footer.header=Remove Header/Footer
|
||||||
|
remove-header-footer.submit=Remove
|
||||||
|
remove-header-footer.removeHeader=Header
|
||||||
|
remove-header-footer.removeFooter=Footer
|
||||||
|
remove-header-footer.headerMargin=Header Margin
|
||||||
|
remove-header-footer.footerMargin=Footer Margin
|
||||||
|
remove-header-footer.pages=Select Pages
|
||||||
|
remove-header-footer.selectMode=Select Mode
|
||||||
|
remove-header-footer.view-more=View More
|
||||||
|
remove-header-footer.previousPage=Previous Page
|
||||||
|
remove-header-footer.nextPage=Next Page
|
||||||
|
remove-header-footer.zoomOut=Zoom Out
|
||||||
|
remove-header-footer.zoomIn=Zoom In
|
||||||
|
remove-header-footer.close=Close
|
||||||
|
remove-header-footer.margin=Margin
|
||||||
|
remove-header-footer.auto=Auto
|
||||||
|
remove-header-footer.manual=Manual
|
||||||
|
remove-header-footer.enterValue=Enter Value
|
||||||
|
|
||||||
#autoSplitPDF
|
#autoSplitPDF
|
||||||
autoSplitPDF.title=Auto Split PDF
|
autoSplitPDF.title=Auto Split PDF
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
#previewContainer {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pdf-preview {
|
||||||
|
max-width: calc(100% - 30px);
|
||||||
|
max-height: calc(100% - 30px);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 35%;
|
||||||
|
left: 35%;
|
||||||
|
transform: translate(-25%, -33%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-line {
|
||||||
|
position: absolute;
|
||||||
|
height: 0;
|
||||||
|
border-top: 2px dashed red;
|
||||||
|
z-index: 10;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fullscreen overlay */
|
||||||
|
#pdfOverlay {
|
||||||
|
display: none; /* Make it hidden by default */
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(30, 30, 30, 0.514);
|
||||||
|
display: none;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 60px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height:100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
height: 4rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbarContainer {
|
||||||
|
height: 4rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagesView {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.pagesView canvas {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoomButton {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
color: white;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
border: 1px solid #ffffff00;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 20;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.253);
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zoomButton:hover {
|
||||||
|
background: rgba(0, 29, 41, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close-button {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: rgb(169 201 246);
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close-button .material-symbols-rounded {
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: rgb(58 94 134);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close-button:hover {
|
||||||
|
background: rgb(102, 102, 103);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close-button:hover .material-symbols-rounded {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1125px) {
|
||||||
|
#toolbarViewerRight {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,603 @@
|
|||||||
|
import * as pdfjsLib from '../../pdfjs-legacy/pdf.mjs';
|
||||||
|
import {PDFViewerApplication} from '../../pdfjs-legacy/js/viewer.mjs';
|
||||||
|
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdfjs-legacy/pdf.worker.mjs';
|
||||||
|
let loadedPdf = null;
|
||||||
|
let pdfFileUrl = null;
|
||||||
|
let pdfBlobUrl = null;
|
||||||
|
document.getElementById('removeHeaderFooterForm').addEventListener('submit', async function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const form = event.target;
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const responseContainer = document.getElementById('responseContainer');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(form.action, {
|
||||||
|
method: form.method,
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
const responseText = await response.text();
|
||||||
|
|
||||||
|
responseContainer.textContent = responseText;
|
||||||
|
responseContainer.className = 'alert alert-success';
|
||||||
|
} catch (error) {
|
||||||
|
responseContainer.textContent = 'An error occurred. Please try again.';
|
||||||
|
responseContainer.className = 'alert alert-danger';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
PDFViewerApplication.run();
|
||||||
|
const fileInput = document.getElementById('fileInput-input');
|
||||||
|
const pagesInput = document.getElementById("pageNumbers");
|
||||||
|
const previewContainer = document.getElementById('previewContainer');
|
||||||
|
const modeSelect = document.querySelector('select[name="mode"]');
|
||||||
|
|
||||||
|
const overlayHeaderCheckbox = document.getElementById('overlay-removeHeader');
|
||||||
|
const overlayFooterCheckbox = document.getElementById('overlay-removeFooter');
|
||||||
|
const overlayHeaderMargin = document.getElementById('overlay-headerMargin');
|
||||||
|
const overlayFooterMargin = document.getElementById('overlay-footerMargin');
|
||||||
|
const customOverlayHeaderWrapper = document.getElementById("overlay-headerCustomMarginWrapper");
|
||||||
|
const customOverlayFooterWrapper = document.getElementById("overlay-footerCustomMarginWrapper");
|
||||||
|
const customOverlayHeaderInput = document.getElementById("overlay-headerCustomMarginInput");
|
||||||
|
const customOverlayFooterInput = document.getElementById("overlay-footerCustomMarginInput");
|
||||||
|
|
||||||
|
const marginOptions = document.getElementById("margin-options");
|
||||||
|
const headerMarginColumn = document.getElementById("header-margin-column");
|
||||||
|
const footerMarginColumn = document.getElementById("footer-margin-column");
|
||||||
|
const mainHeaderCheckbox = document.getElementById("removeHeader");
|
||||||
|
const mainFooterCheckbox = document.getElementById("removeFooter");
|
||||||
|
const mainHeaderMargin = document.querySelector('select[name="headerMargin"]');
|
||||||
|
const mainFooterMargin = document.querySelector('select[name="footerMargin"]');
|
||||||
|
const customMainHeaderWrapper = document.getElementById("headerCustomMarginWrapper");
|
||||||
|
const customMainFooterWrapper = document.getElementById("footerCustomMarginWrapper");
|
||||||
|
const customMainHeaderInput = document.getElementById("headerCustomMarginInput");
|
||||||
|
const customMainFooterInput = document.getElementById("footerCustomMarginInput");
|
||||||
|
|
||||||
|
const viewerContainer = document.getElementById('viewerContainer');
|
||||||
|
const viewer = document.getElementById('viewer');
|
||||||
|
const pageNumberInput = document.getElementById('pageNumber');
|
||||||
|
const numPagesLabel = document.getElementById('numPages');
|
||||||
|
const scaleSelect = document.getElementById('scaleSelect');
|
||||||
|
const zoomInBtn = document.getElementById('zoomIn');
|
||||||
|
const zoomOutBtn = document.getElementById('zoomOut');
|
||||||
|
const nextBtn = document.getElementById('next');
|
||||||
|
const prevBtn = document.getElementById('previous');
|
||||||
|
const closeBtn = document.getElementById('closeOverlay');
|
||||||
|
const zoomButton = document.getElementById('zoomButton');
|
||||||
|
|
||||||
|
const pageContainer = document.getElementById('page-container');
|
||||||
|
const outerContainer = document.getElementById('outerContainer');
|
||||||
|
|
||||||
|
let pdfDoc = null;
|
||||||
|
let currentPage = 1;
|
||||||
|
let currentScale = 1.0;
|
||||||
|
|
||||||
|
// Sync margin controls between overlay and main form
|
||||||
|
function syncMarginControls(fromOverlay) {
|
||||||
|
|
||||||
|
const copyState = (source, target) => {
|
||||||
|
target.headerCheckbox.checked = source.headerCheckbox.checked;
|
||||||
|
target.footerCheckbox.checked = source.footerCheckbox.checked;
|
||||||
|
target.headerMargin.value = source.headerMargin.value;
|
||||||
|
target.footerMargin.value = source.footerMargin.value;
|
||||||
|
target.headerMarginColumn.style.display = source.headerMarginColumn.style.display;
|
||||||
|
target.footerMarginColumn.style.display = source.footerMarginColumn.style.display;
|
||||||
|
target.customHeaderWrapper.style.display = source.customHeaderWrapper.style.display;
|
||||||
|
target.customHeaderInput.value = source.customHeaderInput.value;
|
||||||
|
target.customFooterWrapper.style.display = source.customFooterWrapper.style.display;
|
||||||
|
target.customFooterInput.value = source.customFooterInput.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const overlay = {
|
||||||
|
headerCheckbox: overlayHeaderCheckbox,
|
||||||
|
footerCheckbox: overlayFooterCheckbox,
|
||||||
|
headerMargin: overlayHeaderMargin,
|
||||||
|
footerMargin: overlayFooterMargin,
|
||||||
|
headerMarginColumn: overlayHeaderMargin,
|
||||||
|
footerMarginColumn: overlayFooterMargin,
|
||||||
|
customHeaderWrapper: customOverlayHeaderWrapper,
|
||||||
|
customHeaderInput: customOverlayHeaderInput,
|
||||||
|
customFooterWrapper: customOverlayFooterWrapper,
|
||||||
|
customFooterInput: customOverlayFooterInput
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = {
|
||||||
|
headerCheckbox: mainHeaderCheckbox,
|
||||||
|
footerCheckbox: mainFooterCheckbox,
|
||||||
|
headerMargin: mainHeaderMargin,
|
||||||
|
footerMargin: mainFooterMargin,
|
||||||
|
headerMarginColumn: headerMarginColumn,
|
||||||
|
footerMarginColumn: footerMarginColumn,
|
||||||
|
customHeaderWrapper: customMainHeaderWrapper,
|
||||||
|
customHeaderInput: customMainHeaderInput,
|
||||||
|
customFooterWrapper: customMainFooterWrapper,
|
||||||
|
customFooterInput: customMainFooterInput
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fromOverlay) {
|
||||||
|
copyState(overlay, main);
|
||||||
|
} else {
|
||||||
|
copyState(main, overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOverlayMarginOptions() {
|
||||||
|
overlayHeaderMargin.style.display = overlayHeaderCheckbox.checked ? "block" : "none";
|
||||||
|
overlayFooterMargin.style.display = overlayFooterCheckbox.checked ? "block" : "none";
|
||||||
|
if(overlayHeaderMargin.value == "custom" && overlayHeaderCheckbox.checked){
|
||||||
|
customOverlayHeaderWrapper.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
customOverlayHeaderWrapper.style.display = "none";
|
||||||
|
}
|
||||||
|
if(overlayFooterMargin.value == "custom" && overlayFooterCheckbox.checked){
|
||||||
|
customOverlayFooterWrapper.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
customOverlayFooterWrapper.style.display = "none";
|
||||||
|
}
|
||||||
|
syncMarginControls(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMainMarginOptions() {
|
||||||
|
if (modeSelect.value === "margin") {
|
||||||
|
marginOptions.style.display = "flex";
|
||||||
|
headerMarginColumn.style.display = mainHeaderCheckbox.checked ? "block" : "none";
|
||||||
|
footerMarginColumn.style.display = mainFooterCheckbox.checked ? "block" : "none";
|
||||||
|
if(mainHeaderMargin.value == "custom"){
|
||||||
|
customMainHeaderWrapper.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
customMainHeaderWrapper.style.display = "none";
|
||||||
|
}
|
||||||
|
if(mainFooterMargin.value == "custom"){
|
||||||
|
customMainFooterWrapper.style.display = "block";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
customMainFooterWrapper.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedPdf) {
|
||||||
|
syncMarginControls(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
marginOptions.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize correctly on page load
|
||||||
|
toggleMainMarginOptions();
|
||||||
|
|
||||||
|
// Update when mode changes
|
||||||
|
modeSelect.addEventListener("change", toggleMainMarginOptions);
|
||||||
|
|
||||||
|
|
||||||
|
[
|
||||||
|
overlayHeaderCheckbox,
|
||||||
|
overlayFooterCheckbox,
|
||||||
|
overlayHeaderMargin,
|
||||||
|
overlayFooterMargin
|
||||||
|
].forEach(el => {
|
||||||
|
el.addEventListener('change', () => {
|
||||||
|
toggleOverlayMarginOptions();
|
||||||
|
renderAllPages();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener("change", async function () {
|
||||||
|
|
||||||
|
const existingPreview = document.getElementById("pdf-preview");
|
||||||
|
if (existingPreview) existingPreview.remove();
|
||||||
|
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
if (!file || file.type !== 'application/pdf') return;
|
||||||
|
|
||||||
|
|
||||||
|
if (pdfFileUrl) URL.revokeObjectURL(pdfFileUrl);
|
||||||
|
pdfFileUrl = URL.createObjectURL(file);
|
||||||
|
|
||||||
|
loadedPdf = await pdfjsLib.getDocument(pdfFileUrl).promise;
|
||||||
|
|
||||||
|
renderPreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
pagesInput.addEventListener("input", () => {
|
||||||
|
if (modeSelect.value == "margin" && loadedPdf) {
|
||||||
|
renderPreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modeSelect.addEventListener("change", () => {
|
||||||
|
const preview = document.getElementById("pdf-preview");
|
||||||
|
|
||||||
|
if (modeSelect.value == "margin" && loadedPdf){
|
||||||
|
document.querySelector("#editSection").style.display = "block";
|
||||||
|
renderPreview();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (preview) preview.remove();
|
||||||
|
document.querySelector("#editSection").style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
mainFooterCheckbox,
|
||||||
|
mainHeaderCheckbox,
|
||||||
|
mainFooterMargin,
|
||||||
|
mainHeaderMargin
|
||||||
|
].forEach(el => {
|
||||||
|
el.addEventListener("input", () => {
|
||||||
|
toggleMainMarginOptions();
|
||||||
|
if (modeSelect.value == "margin" && loadedPdf)
|
||||||
|
renderPreview();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
customMainHeaderInput,
|
||||||
|
customMainFooterInput
|
||||||
|
].forEach(el => {
|
||||||
|
el.addEventListener("input", () => {
|
||||||
|
if (checkValue(el)) {
|
||||||
|
toggleMainMarginOptions();
|
||||||
|
renderPreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
customOverlayHeaderInput,
|
||||||
|
customOverlayFooterInput
|
||||||
|
].forEach(el => {
|
||||||
|
el.addEventListener("input", () => {
|
||||||
|
if (checkValue(el)) {
|
||||||
|
toggleOverlayMarginOptions();
|
||||||
|
renderAllPages();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkValue(inputBlock) {
|
||||||
|
const value = parseInt(inputBlock.value, 10) || 0;
|
||||||
|
if (!isNaN(value) && value > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
if (modeSelect.value == "margin" && loadedPdf) {
|
||||||
|
renderPreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function drawMarginLine(ctx, y, width) {
|
||||||
|
if (y < 0 || y > ctx.canvas.height) return;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderPreview() {
|
||||||
|
if (modeSelect.value != "margin" || !loadedPdf) {
|
||||||
|
if (preview) preview.remove();
|
||||||
|
document.querySelector("#editSection").style.display = "none";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
document.querySelector("#editSection").style.display = "block";
|
||||||
|
}
|
||||||
|
document.querySelectorAll(".margin-line").forEach(e => e.remove());
|
||||||
|
|
||||||
|
|
||||||
|
const mainHeaderMargin = document.querySelector('select[name="headerMargin"]').value === 'custom'
|
||||||
|
? parseInt(document.getElementById('headerCustomMarginInput').value, 10) || 0
|
||||||
|
: parseInt(document.querySelector('select[name="headerMargin"]').value, 10) || 0;
|
||||||
|
const mainFooterMargin = document.querySelector('select[name="footerMargin"]').value === 'custom'
|
||||||
|
? parseInt(document.getElementById('footerCustomMarginInput').value, 10) || 0
|
||||||
|
: parseInt(document.querySelector('select[name="footerMargin"]').value, 10) || 0;
|
||||||
|
|
||||||
|
const existingPreview = document.getElementById("pdf-preview");
|
||||||
|
if (existingPreview) existingPreview.remove();
|
||||||
|
|
||||||
|
|
||||||
|
const pageInput = pagesInput.value.trim();
|
||||||
|
let firstPageNumber = 1;
|
||||||
|
|
||||||
|
if (pageInput) {
|
||||||
|
const pages = pageInput.split(',').flatMap(part => {
|
||||||
|
if (part.includes('-')) {
|
||||||
|
const [start, end] = part.split('-').map(Number);
|
||||||
|
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
||||||
|
}
|
||||||
|
return [Number(part)];
|
||||||
|
}).filter(n => !isNaN(n) && n > 0);
|
||||||
|
|
||||||
|
if (pages.length > 0) {
|
||||||
|
firstPageNumber = pages[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = await loadedPdf.getPage(firstPageNumber);
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
|
||||||
|
|
||||||
|
if (page.rotate == 90 || page.rotate == 270) {
|
||||||
|
canvas.width = page.view[3];
|
||||||
|
canvas.height = page.view[2];
|
||||||
|
} else {
|
||||||
|
canvas.width = page.view[2];
|
||||||
|
canvas.height = page.view[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
const renderContext = {
|
||||||
|
canvasContext: ctx,
|
||||||
|
viewport: page.getViewport({ scale: 1 }),
|
||||||
|
};
|
||||||
|
|
||||||
|
await page.render(renderContext).promise;
|
||||||
|
|
||||||
|
const scale = canvas.height / page.view[3]; // PDF pts to pixels
|
||||||
|
const headerY = mainHeaderMargin * scale;
|
||||||
|
const footerY = canvas.height - mainFooterMargin * scale;
|
||||||
|
|
||||||
|
|
||||||
|
ctx.strokeStyle = "red";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
|
||||||
|
if (mainHeaderCheckbox.checked) {
|
||||||
|
drawMarginLine(ctx, headerY, canvas.width);
|
||||||
|
}
|
||||||
|
if (mainFooterCheckbox.checked) {
|
||||||
|
drawMarginLine(ctx, footerY, canvas.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
const preview = document.createElement("img");
|
||||||
|
preview.id = "pdf-preview";
|
||||||
|
preview.alt = "preview";
|
||||||
|
preview.src = canvas.toDataURL();
|
||||||
|
preview.style.position = "absolute";
|
||||||
|
preview.style.top = "50%";
|
||||||
|
preview.style.left = "50%";
|
||||||
|
preview.style.transform = "translate(-50%, -50%)";
|
||||||
|
|
||||||
|
previewContainer.appendChild(preview);
|
||||||
|
|
||||||
|
URL.revokeObjectURL(pdfFileUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
function parsePagesInput(input, maxPage) {
|
||||||
|
if (!input) return Array.from({length: maxPage}, (_, i) => i + 1);
|
||||||
|
const parts = input.split(',');
|
||||||
|
const pages = new Set();
|
||||||
|
for (const part of parts) {
|
||||||
|
if (part.includes('-')) {
|
||||||
|
const [start, end] = part.split('-').map(Number);
|
||||||
|
if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start && end <= maxPage) {
|
||||||
|
for (let i = start; i <= end; i++) pages.add(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const n = Number(part);
|
||||||
|
if (!isNaN(n) && n > 0 && n <= maxPage) pages.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(pages).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAllPages() {
|
||||||
|
viewer.innerHTML = '';
|
||||||
|
|
||||||
|
const headerMargin = document.querySelector('select[name="headerMargin"]').value === 'custom'
|
||||||
|
? parseInt(document.getElementById('headerCustomMarginInput').value, 10) || 0
|
||||||
|
: parseInt(document.querySelector('select[name="headerMargin"]').value, 10) || 0;
|
||||||
|
const footerMargin = document.querySelector('select[name="footerMargin"]').value === 'custom'
|
||||||
|
? parseInt(document.getElementById('footerCustomMarginInput').value, 10) || 0
|
||||||
|
: parseInt(document.querySelector('select[name="footerMargin"]').value, 10) || 0;
|
||||||
|
|
||||||
|
const removeHeaderChecked = document.getElementById('removeHeader').checked;
|
||||||
|
const removeFooterChecked = document.getElementById('removeFooter').checked;
|
||||||
|
|
||||||
|
const pageInput = document.getElementById('pageNumbers').value.trim();
|
||||||
|
const pagesToShow = parsePagesInput(pageInput, pdfDoc.numPages);
|
||||||
|
|
||||||
|
const renderPage = (num, idx) => {
|
||||||
|
pdfDoc.getPage(num).then(page => {
|
||||||
|
const viewport = page.getViewport({ scale: currentScale });
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
canvas.height = viewport.height;
|
||||||
|
canvas.width = viewport.width;
|
||||||
|
canvas.style.display = 'block';
|
||||||
|
canvas.style.margin = '0 auto 16px auto';
|
||||||
|
viewer.appendChild(canvas);
|
||||||
|
page.render({ canvasContext: ctx, viewport: viewport }).promise.then(() => {
|
||||||
|
if (idx === 0) {
|
||||||
|
pageNumberInput.value = pagesToShow[0];
|
||||||
|
numPagesLabel.textContent = `/ ${pagesToShow.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scale = canvas.height / page.view[3];
|
||||||
|
const headerY = headerMargin * scale;
|
||||||
|
const footerY = canvas.height - footerMargin * scale;
|
||||||
|
ctx.strokeStyle = "red";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
if (removeHeaderChecked) {
|
||||||
|
drawMarginLine(ctx, headerY, canvas.width);
|
||||||
|
}
|
||||||
|
if (removeFooterChecked) {
|
||||||
|
drawMarginLine(ctx, footerY, canvas.width);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
pagesToShow.forEach((pageNum, idx) => renderPage(pageNum, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToPage(num) {
|
||||||
|
const canvases = viewer.querySelectorAll('canvas');
|
||||||
|
if (canvases[num - 1]) {
|
||||||
|
canvases[num - 1].scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomButton.addEventListener('click', async () => {
|
||||||
|
if (!fileInput.files[0]) return;
|
||||||
|
if (pdfBlobUrl) URL.revokeObjectURL(pdfBlobUrl);
|
||||||
|
pdfBlobUrl = URL.createObjectURL(fileInput.files[0]);
|
||||||
|
|
||||||
|
syncMarginControls(false);
|
||||||
|
outerContainer.style.display = 'block';
|
||||||
|
pageContainer.style.display = 'none';
|
||||||
|
|
||||||
|
pdfjsLib.getDocument(pdfBlobUrl).promise.then(pdf => {
|
||||||
|
pdfDoc = pdf;
|
||||||
|
currentPage = 1;
|
||||||
|
currentScale = 1.0;
|
||||||
|
renderAllPages();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
syncMarginControls(true);
|
||||||
|
outerContainer.style.display = 'none';
|
||||||
|
pageContainer.style.display = 'block';
|
||||||
|
viewer.innerHTML = '';
|
||||||
|
if (pdfBlobUrl) URL.revokeObjectURL(pdfBlobUrl);
|
||||||
|
pdfDoc = null;
|
||||||
|
renderPreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
nextBtn.addEventListener('click', () => {
|
||||||
|
if (currentPage < pdfDoc.numPages) {
|
||||||
|
currentPage++;
|
||||||
|
pageNumberInput.value = currentPage;
|
||||||
|
scrollToPage(currentPage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
prevBtn.addEventListener('click', () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage--;
|
||||||
|
pageNumberInput.value = currentPage;
|
||||||
|
scrollToPage(currentPage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scaleSelect.addEventListener('change', async e => {
|
||||||
|
let val = e.target.value;
|
||||||
|
if (val === 'page-fit' || val === 'page-width' || val === 'page-actual') {
|
||||||
|
const scale = await getPageScale(val);
|
||||||
|
onZoomChange(scale);
|
||||||
|
scaleSelect.value = val;
|
||||||
|
} else if (val === 'auto') {
|
||||||
|
onZoomChange(1.25);
|
||||||
|
scaleSelect.value = val;
|
||||||
|
} else {
|
||||||
|
onZoomChange(Number(val));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pageNumberInput.addEventListener('change', e => {
|
||||||
|
let num = parseInt(e.target.value, 10);
|
||||||
|
if (!isNaN(num) && num >= 1 && num <= pdfDoc.numPages) {
|
||||||
|
currentPage = num;
|
||||||
|
scrollToPage(currentPage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setScaleSelectValue(scale) {
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
for (const opt of scaleSelect.options) {
|
||||||
|
if (Number(opt.value) === scale) {
|
||||||
|
scaleSelect.value = opt.value;
|
||||||
|
const customOpt = scaleSelect.querySelector('#customScaleOption');
|
||||||
|
if (customOpt) {
|
||||||
|
customOpt.hidden = true;
|
||||||
|
customOpt.disabled = true;
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
|
||||||
|
let customOpt = scaleSelect.querySelector('#customScaleOption');
|
||||||
|
if (!customOpt) {
|
||||||
|
customOpt = document.createElement('option');
|
||||||
|
customOpt.id = 'customScaleOption';
|
||||||
|
scaleSelect.appendChild(customOpt);
|
||||||
|
}
|
||||||
|
customOpt.value = 'custom';
|
||||||
|
customOpt.textContent = `${Math.round(scale * 100)}%`;
|
||||||
|
customOpt.hidden = false;
|
||||||
|
customOpt.disabled = false;
|
||||||
|
scaleSelect.value = 'custom';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onZoomChange(newScale) {
|
||||||
|
currentScale = newScale;
|
||||||
|
setScaleSelectValue(currentScale);
|
||||||
|
renderAllPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPageScale(mode) {
|
||||||
|
|
||||||
|
if (!pdfDoc) return 1.0;
|
||||||
|
const firstCanvas = viewer.querySelector('canvas');
|
||||||
|
if (!firstCanvas) return 1.0;
|
||||||
|
const container = viewerContainer;
|
||||||
|
const pageIndex = 1;
|
||||||
|
return pdfDoc.getPage(pageIndex).then(page => {
|
||||||
|
const viewport = page.getViewport({ scale: 1.0 });
|
||||||
|
if (mode === 'page-fit') {
|
||||||
|
// Fit page height to container height
|
||||||
|
return container.clientHeight / viewport.height;
|
||||||
|
} else if (mode === 'page-width') {
|
||||||
|
// Fit page width to container width
|
||||||
|
return container.clientWidth / viewport.width;
|
||||||
|
} else if (mode === 'page-actual') {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the page number with the visible page number during scroll
|
||||||
|
viewerContainer.addEventListener('scroll', () => {
|
||||||
|
const canvases = viewer.querySelectorAll('canvas');
|
||||||
|
let closest = 0;
|
||||||
|
let minDiff = Infinity;
|
||||||
|
const containerTop = viewerContainer.scrollTop;
|
||||||
|
for (let i = 0; i < canvases.length; i++) {
|
||||||
|
const rect = canvases[i].getBoundingClientRect();
|
||||||
|
const diff = Math.abs(rect.top - viewerContainer.getBoundingClientRect().top);
|
||||||
|
if (diff < minDiff) {
|
||||||
|
minDiff = diff;
|
||||||
|
closest = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pdfDoc && pageNumberInput.value != (closest + 1)) {
|
||||||
|
pageNumberInput.value = closest + 1;
|
||||||
|
currentPage = closest + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
zoomInBtn.addEventListener('click', () => {
|
||||||
|
onZoomChange(currentScale + 0.1);
|
||||||
|
});
|
||||||
|
|
||||||
|
zoomOutBtn.addEventListener('click', () => {
|
||||||
|
onZoomChange(currentScale - 0.1);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -67,6 +67,7 @@
|
|||||||
<link rel="stylesheet" th:href="@{'/css/licenses.css'}" th:if="${currentPage == 'licenses'}">
|
<link rel="stylesheet" th:href="@{'/css/licenses.css'}" th:if="${currentPage == 'licenses'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/multi-tool.css'}" th:if="${currentPage == 'multi-tool'}">
|
<link rel="stylesheet" th:href="@{'/css/multi-tool.css'}" th:if="${currentPage == 'multi-tool'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/rotate-pdf.css'}" th:if="${currentPage == 'rotate-pdf'}">
|
<link rel="stylesheet" th:href="@{'/css/rotate-pdf.css'}" th:if="${currentPage == 'rotate-pdf'}">
|
||||||
|
<link rel="stylesheet" th:href="@{'/css/remove-header-footer.css'}" th:if="${currentPage == 'remove-header-footer'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/stamp.css'}" th:if="${currentPage == 'stamp'}">
|
<link rel="stylesheet" th:href="@{'/css/stamp.css'}" th:if="${currentPage == 'stamp'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/fileSelect.css'}" th:if="${currentPage != 'home'}">
|
<link rel="stylesheet" th:href="@{'/css/fileSelect.css'}" th:if="${currentPage != 'home'}">
|
||||||
<link rel="stylesheet" th:href="@{'/css/footer.css'}">
|
<link rel="stylesheet" th:href="@{'/css/footer.css'}">
|
||||||
|
@ -1,14 +1,32 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||||
<head>
|
<head>
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{remove-header-footer.title}, header=#{remove-header-footer.header})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{remove-header-footer.title}, header=#{remove-header-footer.header})}">
|
||||||
|
</th:block>
|
||||||
|
<!-- Bootstrap -->
|
||||||
|
<script th:src="@{'/js/thirdParty/popper.min.js'}"></script>
|
||||||
|
<script th:src="@{'/js/thirdParty/bootstrap.min.js'}"></script>
|
||||||
|
|
||||||
|
<script th:src="@{'/js/thirdParty/jquery.min.js'}"></script>
|
||||||
|
<script th:src="@{'/js/thirdParty/jquery.validate.min.js'}"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" th:href="@{'/css/theme/componentes.css'}">
|
||||||
|
<link rel="stylesheet" th:href="@{'/css/navbar.css'}">
|
||||||
|
<!-- This snippet is used in production (included from view-pdf.html) -->
|
||||||
|
<link rel="resource" type="application/l10n" th:href="@{'/pdfjs-legacy/locale/locale.json'}">
|
||||||
|
<script th:src="@{'/pdfjs-legacy/pdf.mjs'}" type="module"></script>
|
||||||
|
<link rel="stylesheet" th:href="@{'/pdfjs-legacy/css/viewer-redact.css'}">
|
||||||
|
|
||||||
|
<script th:src="@{'/pdfjs-legacy/js/viewer.mjs'}" type="module"></script>
|
||||||
|
|
||||||
|
<script type="module" th:src="@{'/js/pages/remove-header-footer.js'}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
|
<br><br>
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
|
||||||
<br><br>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 bg-card">
|
<div class="col-md-6 bg-card">
|
||||||
@ -16,55 +34,234 @@
|
|||||||
<span class="material-symbols-rounded tool-header-icon organize">toolbar</span>
|
<span class="material-symbols-rounded tool-header-icon organize">toolbar</span>
|
||||||
<span class="tool-header-text" th:text="#{remove-header-footer.header}"></span>
|
<span class="tool-header-text" th:text="#{remove-header-footer.header}"></span>
|
||||||
</div>
|
</div>
|
||||||
<br><br>
|
<form id="removeHeaderFooterForm" th:action="@{/api/v1/general/remove-header-footer}" method="post" enctype="multipart/form-data">
|
||||||
<span>remove_header_footer test</span>
|
<div
|
||||||
<form id="removeHeaderFooterForm" th:action="@{/api/v1/general/remove-header-footer}" method="post">
|
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label for="msgInput">Input text</label>
|
<div class="mb-3">
|
||||||
<input type="text" id="msgInput" name="message" class="form-control" placeholder="Enter your message" required>
|
<label for="pageNumbers" th:text="#{remove-header-footer.pages}"></label>
|
||||||
|
<input type="text" class="form-control" id="pageNumbers" name="pages" placeholder="1,3,5-10">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label th:text="#{remove-header-footer.selectMode}"></label>
|
||||||
|
<select class="form-control" name="mode">
|
||||||
|
<option value="margin" th:text="#{remove-header-footer.margin}"></option>
|
||||||
|
<option value="auto" th:text="#{remove-header-footer.auto}"></option>
|
||||||
|
<option value="manual"th:text="#{remove-header-footer.manual}"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="form-check" style="flex: 0 0 50%;">
|
||||||
|
<input type="checkbox" id="removeHeader" name="removeHeader">
|
||||||
|
<label for="removeHeader" th:text="#{remove-header-footer.removeHeader}"></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check" style="display: flex; align-items: center;">
|
||||||
|
<input type="checkbox" id="removeFooter" name="removeFooter">
|
||||||
|
<label for="removeFooter" th:text="#{remove-header-footer.removeFooter}"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="margin-options" class="row mb-3" style="display: none;">
|
||||||
|
<div class="col" id="header-margin-column" style="display: none;">
|
||||||
|
<label th:text="#{remove-header-footer.headerMargin}"></label>
|
||||||
|
<select class="form-control" name="headerMargin">
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
<option value="20">20 pt</option>
|
||||||
|
<option value="30">30 pt</option>
|
||||||
|
<option value="40" selected>40 pt (default)</option>
|
||||||
|
<option value="50">50 pt</option>
|
||||||
|
<option value="60">60 pt</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="custom-margin-wrapper" id="headerCustomMarginWrapper" style="display: none; margin-top: 8px;">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control custom-margin-input"
|
||||||
|
id="headerCustomMarginInput"
|
||||||
|
th:placeholder="#{remove-header-footer.enterValue}"
|
||||||
|
data-type="header"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col" id="footer-margin-column" style="display: none;">
|
||||||
|
<label th:text="#{remove-header-footer.footerMargin}"></label>
|
||||||
|
<select class="form-control" name="footerMargin">
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
<option value="20">20 pt</option>
|
||||||
|
<option value="30">30 pt</option>
|
||||||
|
<option value="40" selected>40 pt (default)</option>
|
||||||
|
<option value="50">50 pt</option>
|
||||||
|
<option value="60">60 pt</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="custom-margin-wrapper" id="footerCustomMarginWrapper" style="display: none; margin-top: 8px;">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control custom-margin-input"
|
||||||
|
id="footerCustomMarginInput"
|
||||||
|
th:placeholder="#{remove-header-footer.enterValue}"
|
||||||
|
data-type="footer"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="editSection" style="display: none">
|
||||||
|
<div id="previewContainer">
|
||||||
|
<!-- PDF preview will be rendered here -->
|
||||||
|
<button type="button" id="zoomButton" th:title="#{remove-header-footer.view-more}">
|
||||||
|
<span class="material-symbols-rounded">open_in_full</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="buttonContainer">
|
||||||
|
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{remove-header-footer.submit}"></button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{remove-header-footer.submit}"></button>
|
|
||||||
</form>
|
</form>
|
||||||
<div id="responseContainer" class="mt-3"></div>
|
<div id="responseContainer" class="mt-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
document.getElementById('removeHeaderFooterForm').addEventListener('submit', async function (event) {
|
|
||||||
event.preventDefault(); // Prevent the default form submission
|
|
||||||
|
|
||||||
const form = event.target;
|
|
||||||
const formData = new FormData(form);
|
|
||||||
const responseContainer = document.getElementById('responseContainer');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Send the form data to the server using fetch
|
|
||||||
const response = await fetch(form.action, {
|
|
||||||
method: form.method,
|
|
||||||
body: formData,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Parse the response as text
|
|
||||||
const responseText = await response.text();
|
|
||||||
|
|
||||||
// Display the response in the response container
|
|
||||||
responseContainer.textContent = responseText;
|
|
||||||
responseContainer.className = 'alert alert-success'; // Add a success style
|
|
||||||
} catch (error) {
|
|
||||||
// Handle errors and display an error message
|
|
||||||
responseContainer.textContent = 'An error occurred. Please try again.';
|
|
||||||
responseContainer.className = 'alert alert-danger'; // Add an error style
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
|
|
||||||
<!--
|
|
||||||
<script type="module" th:src="@{'/js/pages/crop.js'}"></script>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
|
<!--PDF Viewer-->
|
||||||
|
<div id="outerContainer" style="display: none;">
|
||||||
|
<div id="sidebarContainer"></div>
|
||||||
|
<div id="mainContainer">
|
||||||
|
<div class="toolbar" style="position:sticky;">
|
||||||
|
<div id="toolbarContainer" style="height:4rem;" >
|
||||||
|
<div id="toolbarViewer">
|
||||||
|
<div id="toolbarViewerLeft">
|
||||||
|
<div class="splitToolbarButton hiddenSmallView">
|
||||||
|
<button class="toolbarButton btn-secondary" th:title="#{remove-header-footer.previousPage}" id="previous"
|
||||||
|
tabindex="13" data-l10n-id="pdfjs-previous-button">
|
||||||
|
</button>
|
||||||
|
<div class="splitToolbarButtonSeparator"></div>
|
||||||
|
<button class="toolbarButton btn-secondary" th:title="#{remove-header-footer.nextPage}" id="next" tabindex="14"
|
||||||
|
data-l10n-id="pdfjs-next-button">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span class="loadingInput start">
|
||||||
|
<input type="number" id="pageNumber" class="toolbarField" value="1" min="1" tabindex="15"
|
||||||
|
data-l10n-id="pdfjs-page-input" autocomplete="off">
|
||||||
|
</span>
|
||||||
|
<span id="numPages" class="toolbarLabel"></span>
|
||||||
|
<img class="main-icon user-select-none" th:src="@{'/favicon.svg'}" alt="icon">
|
||||||
|
</div>
|
||||||
|
<div id="toolbarViewerMiddle" class="d-flex align-items-center justify-content-between w-100" style="max-width:900px; margin:0 auto;">
|
||||||
|
<div id="overlay-margin-options" class="d-flex align-items-center">
|
||||||
|
<div class="form-check" style="margin-bottom: 0px;" >
|
||||||
|
<input type="checkbox" id="overlay-removeHeader" style="margin-bottom: 4px; margin-left: 7px;" class="form-check-input">
|
||||||
|
<label for="overlay-removeHeader" class="form-check-label" style="padding-right: 2px;" th:text="#{remove-header-footer.removeHeader}"></label>
|
||||||
|
</div>
|
||||||
|
<span id="overlay-headerMargin-container">
|
||||||
|
<select class="form-select me-3" id="overlay-headerMargin" style="width:auto;">
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
<option value="20">20 pt</option>
|
||||||
|
<option value="30">30 pt</option>
|
||||||
|
<option value="40" selected>40 pt</option>
|
||||||
|
<option value="50">50 pt</option>
|
||||||
|
<option value="60">60 pt</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="custom-margin-wrapper" id="overlay-headerCustomMarginWrapper" style="display: none; width: 150px;">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control custom-margin-input"
|
||||||
|
id="overlay-headerCustomMarginInput"
|
||||||
|
th:placeholder="#{remove-header-footer.enterValue}"
|
||||||
|
data-type="header"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center flex-row">
|
||||||
|
<div class="splitToolbarButton mb-2 mb-md-0 me-md-2">
|
||||||
|
<button id="zoomOut" th:title="#{remove-header-footer.zoomOut}" class="toolbarButton btn-primary" tabindex="21"
|
||||||
|
data-l10n-id="pdfjs-zoom-out-button">
|
||||||
|
</button>
|
||||||
|
<div class="splitToolbarButtonSeparator"></div>
|
||||||
|
<button id="zoomIn" th:title="#{remove-header-footer.zoomIn}" class="toolbarButton btn-primary" tabindex="22"
|
||||||
|
data-l10n-id="pdfjs-zoom-in-button">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span id="scaleSelectContainer" class="dropdownToolbarButton">
|
||||||
|
<select id="scaleSelect" tabindex="23" data-l10n-id="pdfjs-zoom-select">
|
||||||
|
<option id="pageAutoOption" value="auto" selected="selected" data-l10n-id="pdfjs-page-scale-auto">
|
||||||
|
Automatic Zoom</option>
|
||||||
|
<option id="pageActualOption" value="page-actual" data-l10n-id="pdfjs-page-scale-actual">
|
||||||
|
Actual Size</option>
|
||||||
|
<option id="pageFitOption" value="page-fit" data-l10n-id="pdfjs-page-scale-fit">Page
|
||||||
|
Fit
|
||||||
|
</option>
|
||||||
|
<option id="pageWidthOption" value="page-width" data-l10n-id="pdfjs-page-scale-width">
|
||||||
|
Page Width</option>
|
||||||
|
<option value="0.5" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 50 }'>
|
||||||
|
50%</option>
|
||||||
|
<option value="0.75" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 75 }'>75%
|
||||||
|
</option>
|
||||||
|
<option value="1" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 100 }'>
|
||||||
|
100%</option>
|
||||||
|
<option value="1.25" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 125 }'>125%
|
||||||
|
</option>
|
||||||
|
<option value="1.5" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 150 }'>150%
|
||||||
|
</option>
|
||||||
|
<option value="2" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 200 }'>
|
||||||
|
200%</option>
|
||||||
|
<option value="3" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 300 }'>
|
||||||
|
300%</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="overlay-margin-options" class="d-flex align-items-center">
|
||||||
|
<div class="form-check" style="margin-bottom: 0px;" >
|
||||||
|
<input type="checkbox" id="overlay-removeFooter" class="form-check-input" style=" margin-bottom: 4px; margin-left: 7px;">
|
||||||
|
<label for="overlay-removeFooter" class="form-check-label" style="padding-right: 2px; " th:text="#{remove-header-footer.removeFooter}"></label>
|
||||||
|
</div>
|
||||||
|
<span id="overlay-footerMargin-container">
|
||||||
|
<select class="form-select" id="overlay-footerMargin" style="width:auto;">
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
<option value="20">20 pt</option>
|
||||||
|
<option value="30">30 pt</option>
|
||||||
|
<option value="40" selected>40 pt</option>
|
||||||
|
<option value="50">50 pt</option>
|
||||||
|
<option value="60">60 pt</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div class="custom-margin-wrapper" id="overlay-footerCustomMarginWrapper" style="display: none;max-width: 150px;">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control custom-margin-input"
|
||||||
|
id="overlay-footerCustomMarginInput"
|
||||||
|
th:placeholder="#{remove-header-footer.enterValue}"
|
||||||
|
data-type="footer"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="toolbarViewerRight">
|
||||||
|
<div id="splitToolbarButton">
|
||||||
|
<button id="closeOverlay" class="close-button me-2" aria-label="Close" th:title="#{remove-header-footer.close}">
|
||||||
|
<span class="material-symbols-rounded">close</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- Toolbar -->
|
||||||
|
</div>
|
||||||
|
<div id="viewerContainer" tabindex="0" class="overlay-content">
|
||||||
|
<div id="viewer" class="pdfViewer ṕagesView">
|
||||||
|
<!-- PDF will be rendered here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user