mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-08 17:51:20 +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.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.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
|
||||
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-color.title=Replace-Invert-Color
|
||||
replace-color.header=Replace-Invert Color PDF
|
||||
@ -996,6 +1000,26 @@ crop.title=Crop
|
||||
crop.header=Crop PDF
|
||||
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.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/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/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/fileSelect.css'}" th:if="${currentPage != 'home'}">
|
||||
<link rel="stylesheet" th:href="@{'/css/footer.css'}">
|
||||
|
@ -1,14 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<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>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<div id="page-container">
|
||||
<br><br>
|
||||
<div id="content-wrap">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
@ -16,55 +34,234 @@
|
||||
<span class="material-symbols-rounded tool-header-icon organize">toolbar</span>
|
||||
<span class="tool-header-text" th:text="#{remove-header-footer.header}"></span>
|
||||
</div>
|
||||
<br><br>
|
||||
<span>remove_header_footer test</span>
|
||||
<form id="removeHeaderFooterForm" th:action="@{/api/v1/general/remove-header-footer}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="msgInput">Input text</label>
|
||||
<input type="text" id="msgInput" name="message" class="form-control" placeholder="Enter your message" required>
|
||||
<form id="removeHeaderFooterForm" th:action="@{/api/v1/general/remove-header-footer}" method="post" enctype="multipart/form-data">
|
||||
<div
|
||||
th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<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>
|
||||
</form>
|
||||
<div id="responseContainer" class="mt-3"></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>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</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>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user